-
Notifications
You must be signed in to change notification settings - Fork 2
/
instructionComparator.py
152 lines (124 loc) · 5.89 KB
/
instructionComparator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import binaryninja as binja
def compare_instructions(src_instr: binja.HighLevelILInstruction, dst_instr: binja.HighLevelILInstruction) -> bool:
if src_instr.operation != dst_instr.operation:
return False
operation = src_instr.operation
if operation == binja.HighLevelILOperation.HLIL_CALL:
return compare_calls(src_instr, dst_instr)
if (operation == binja.HighLevelILOperation.HLIL_ASSIGN) or (operation == binja.HighLevelILOperation.HLIL_VAR_INIT):
src_var, src_val = src_instr.operands
dst_var, dst_val = dst_instr.operands
# left hand side of assignment operation can be variable, field, etc.
if type(src_var) == type(dst_var):
if type(src_var) == binja.Variable:
if src_var.type != dst_var.type:
return False
elif type(src_var) == binja.highlevelil.HighLevelILInstruction:
if src_var.operation != dst_var.operation:
return False
else:
return False
if src_val.operation != dst_val.operation:
return False
elif operation == binja.HighLevelILOperation.HLIL_CALL:
return compare_calls(src_instr, dst_instr)
# TODO: check other arithemetic operations (ie. DIV, MOD, etc.)
if (operation == binja.HighLevelILOperation.HLIL_ADD) or \
(operation == binja.HighLevelILOperation.HLIL_SUB) or \
(operation == binja.HighLevelILOperation.HLIL_MUL):
return compare_arithmetic(src_instr, dst_instr)
# ignore branch targets, comparisions should only be based on the condition
elif (operation == binja.HighLevelILOperation.HLIL_WHILE) or (operation == binja.HighLevelILOperation.HLIL_IF):
src_condition = src_instr.operands[0]
dst_condition = dst_instr.operands[0]
if len(src_condition.operands) != len(dst_condition.operands):
return False
for i in range(len(src_condition.operands)):
src_operand = src_condition.operands[i]
dst_operand = dst_condition.operands[i]
if (type(src_operand) != binja.HighLevelILInstruction) or (type(dst_operand) != binja.HighLevelILInstruction):
continue
if (src_operand.operation == binja.HighLevelILOperation.HLIL_STRUCT_FIELD or
src_operand.operation == binja.HighLevelILOperation.HLIL_VAR) and \
(dst_operand.operation == binja.HighLevelILOperation.HLIL_STRUCT_FIELD or
dst_operand.operation == binja.HighLevelILOperation.HLIL_VAR):
continue
if src_operand != dst_operand:
return False
return True
# probably nothing address specific
return src_instr == dst_instr
def compare_derefs(src_instr: binja.HighLevelILInstruction, dst_instr: binja.HighLevelILInstruction) -> bool:
src_pointer = src_instr.src
dst_pointer = dst_instr.src
if src_pointer.operation != dst_pointer.operation:
return False
operation = src_pointer.operation
# TODO: extract strings/constants
if operation == binja.HighLevelILOperation.HLIL_CONST_PTR:
pass
elif operation == binja.HighLevelILOperation.HLIL_VAR:
return src_pointer.var.type == dst_pointer.var.type
elif (operation == binja.HighLevelILOperation.HLIL_ADD) or \
(operation == binja.HighLevelILOperation.HLIL_SUB) or \
(operation == binja.HighLevelILOperation.HLIL_MUL):
return compare_arithmetic(src_pointer, dst_pointer)
else:
print('[!] unexpected pointer type {} at {}'.format(operation, hex(src_instr.address)))
return False
def compare_arithmetic(src_instr: binja.HighLevelILInstruction, dst_instr: binja.HighLevelILInstruction) -> bool:
print(src_instr)
print(dst_instr)
num1_src, num2_src = src_instr.operands
num1_dst, num2_dst = dst_instr.operands
if (num1_src.operation != num2_src.operation) or (num1_dst.operation != num2_dst.operation):
return False
# TODO: check for floats as well
# extract numeric constants
if num1_src.operation == binja.HighLevelILOperation.HLIL_CONST:
val1 = num1_src.constant
val2 = num2_src.constant
if val1 != val2:
return False
if num1_dst.operation == binja.HighLevelILOperation.HLIL_CONST:
val1 = num1_dst.constant
val2 = num2_dst.constant
if val1 != val2:
return False
# compare variable refrences
if num1_src.operation == binja.HighLevelILOperation.HLIL_CONST_PTR:
return compare_derefs(src_instr, dst_instr)
if num1_dst.operation == binja.HighLevelILOperation.HLIL_CONST_PTR:
return compare_derefs(src_instr, dst_instr)
return True
def compare_calls(src_instr: binja.HighLevelILInstruction, dst_instr: binja.HighLevelILInstruction) -> bool:
src_function = src_instr.operands[0]
dst_function = dst_instr.operands[0]
# TODO: verify the function being called is the same
src_args = src_instr.operands[1]
dst_args = dst_instr.operands[1]
if len(src_args) != len(dst_args):
return False
for i in range(len(src_args)):
src_arg = src_args[i]
dst_arg = dst_args[i]
if (type(src_arg) == binja.HighLevelILInstruction) and (type(dst_arg) == binja.HighLevelILInstruction):
if src_arg.operation != dst_arg.operation:
return False
# ignore contant pointers, as their addresses will vary
if src_arg.operation == binja.HighLevelILOperation.HLIL_CONST_PTR:
# check if the pointer is a string, and if so compare string values between instructions
src_bv = src_instr.il_basic_block.view
dst_bv = dst_instr.il_basic_block.view
src_string_at = src_bv.get_ascii_string_at(src_bv.start + src_arg.value.value)
dst_string_at = dst_bv.get_ascii_string_at(dst_bv.start + dst_arg.value.value)
if (src_string_at is not None) and (dst_string_at is not None):
if src_string_at.value != dst_string_at.value:
return False
elif src_arg.operation == binja.HighLevelILOperation.HLIL_CONST:
if src_arg.value != dst_arg.value:
return False
elif (type(src_arg) == binja.Variable) and (type(dst_arg) == binja.Variable):
if src_arg.type != dst_arg.type:
return False
return True