-
Notifications
You must be signed in to change notification settings - Fork 3
/
findcrypt.py
166 lines (127 loc) · 5.27 KB
/
findcrypt.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# Search crypto constants in algorithm.
#@author er28-0652
#@category Search
#@keybinding
#@menupath
#@toolbar
import functools
import struct
import const
from ghidra.program.model.symbol import SourceType
from ghidra.program.model.listing import CodeUnit
# ghidra api
def find(find_bytes, min_addr=None):
min_addr = min_addr or currentProgram.getMinAddress()
return currentProgram.getMemory().findBytes(min_addr, find_bytes, None, True, monitor)
def create_label(addr, label_name, source=SourceType.USER_DEFINED):
sym_table = currentProgram.getSymbolTable()
sym_table.createLabel(addr, label_name, source)
def get_instructions_from(addr=None):
return currentProgram.getListing().getInstructions(addr, True)
def get_all_instructions():
return currentProgram.getListing().getInstructions(True)
def get_instruction_at(addr):
return getInstructionAt(addr)
def get_memory_address_ranges():
return currentProgram.getMemory().getAddressRanges()
def has_scalar_operand(inst, idx=1):
return inst.getScalar(idx) is not None
def set_eol_comment(addr, text):
code_unit = currentProgram.getListing().getCodeUnitAt(addr)
code_unit.setComment(CodeUnit.EOL_COMMENT, text)
def get_function_containing(addr):
return getFunctionContaining(addr)
def get_instructions_in_func(func):
inst = get_instruction_at(func.getEntryPoint())
while inst and getFunctionContaining(inst.getAddress()) == func:
yield inst
inst = inst.getNext()
# partial funcs
pack_longlong = functools.partial(struct.pack, '<Q')
pack_long = functools.partial(struct.pack, '<L')
# global value
# generate scalar on operand and its address pairs
SCALAR_ADDR_PAIRS = {inst.getScalar(1).getValue(): inst.getAddress() for inst in filter(has_scalar_operand, get_all_instructions())}
class NonSparseConst:
BYTE = 'B'
LONG = 'L'
LONGLONG = 'Q'
def __init__(self, const):
self._const = const
self.algorithm = const['algorithm']
self.name = const['name']
self.size = const['size']
self.array = const['array']
self._byte_array = None
def handle_byte(self):
return self.array
def handle_long(self):
return ''.join(map(pack_long, self.array))
def handle_longlong(self):
return ''.join(map(pack_longlong, self.array))
def to_bytes(self):
handler = {
self.BYTE: self.handle_byte,
self.LONG: self.handle_long,
self.LONGLONG: self.handle_longlong
# if there'll be another types, add handler here
}.get(self.size)
if handler is None:
raise ValueError('{} is not supported'.format(self.size))
return bytes(bytearray(handler()))
@property
def byte_array(self):
if self._byte_array is None:
self._byte_array = self.to_bytes()
return self._byte_array
class SparseConst:
def __init__(self, const):
self._const = const
self.algorithm = const['algorithm']
self.name = const['name']
self.array = const['array']
class OperandConst:
def __init__(self, const):
self._const = const
self.algorithm = const['algorithm']
self.name = const['name']
self.value = const['value']
def find_crypt_non_sparse_consts():
print('[*] processing non-sparse consts')
for nsc in map(NonSparseConst, const.non_sparse_consts):
found = find(nsc.byte_array)
if found:
print(' [+] found {name} for {alg} at {addr}'.format(name=nsc.name, alg=nsc.algorithm, addr=found))
create_label(found, nsc.name)
def find_crypt_sparse_consts():
print('[*] processing sparse consts')
for sc in map(SparseConst, const.sparse_consts):
# get address of first const matched one in operands
found_addr = SCALAR_ADDR_PAIRS.get(sc.array[0])
if found_addr:
# check the rest of consts, maybe it should be in the same function
# it is noted that it will be failed if the constants are not used in function (like shellcode).
maybe_crypto_func = get_function_containing(found_addr)
insts = get_instructions_in_func(maybe_crypto_func)
# get all scalars in same function
insts_with_scalars = filter(has_scalar_operand, insts)
scalars = [inst.getScalar(1).getValue() for inst in insts_with_scalars]
# check all values in consts array are contained in scalars in same function
if all([c in scalars for c in sc.array]):
# if all consts are contained
# add comment at the first found const's address
print(' [+] found {name} for {alg} at {addr}'.format(name=sc.name, alg=sc.algorithm, addr=found_addr))
create_label(found_addr, sc.name)
def find_crypt_operand_consts():
print('[*] processing operand consts')
for oc in map(OperandConst, const.operand_consts):
found_addr = SCALAR_ADDR_PAIRS.get(oc.value)
if found_addr:
print(' [+] found {name} for {alg} at {addr}'.format(name=oc.name, alg=oc.algorithm, addr=found_addr))
set_eol_comment(found_addr, oc.name)
def main():
find_crypt_non_sparse_consts()
find_crypt_sparse_consts()
find_crypt_operand_consts()
if __name__ == '__main__':
main()