-
Notifications
You must be signed in to change notification settings - Fork 128
/
vtableAddress.py
162 lines (146 loc) · 6.32 KB
/
vtableAddress.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
from __future__ import print_function
import idc
import idautils
import ida_frame
import ida_struct
import idaapi
import sys, os
idaapi.require("AddBP")
REGISTERS = ['eax', 'ebx', 'ecx', 'edx', 'rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'r8', 'r9', 'r10', 'r15', 'X0', 'X1', 'X2', 'X3', 'X4',
'X5', 'X6', 'X7', 'X8', 'X9', 'X10', 'X11', 'X12', 'X13', 'X14', 'X15', 'X16', 'X17', 'X18', 'X19', 'X20',
'X21', 'X22', 'X23', 'X24', 'X25', 'X26', 'X27', 'X28', 'X29', 'X30', 'X31']
def get_processor_architecture():
arch = "Intel"
info = idaapi.get_inf_structure()
if info.procName == "ARM":
arch = "ARM"
if info.is_64bit():
return arch, True
elif info.is_32bit():
return arch, False
else:
return "Error", False
def get_local_var_value_64(loc_var_name):
frame = ida_frame.get_frame(idc.here())
loc_var = ida_struct.get_member_by_name(frame, loc_var_name)
loc_var_start = loc_var.soff
loc_var_ea = loc_var_start + idc.get_reg_value("RSP")
loc_var_value = idc.read_dbg_qword(loc_var_ea) # in case the variable is 32bit, just use get_wide_dword() instead
return loc_var_value
def get_arch_dct():
arch, is_64 = get_processor_architecture()
if arch != "Error" or (arch == "ARM" and not is_64):
dct_arch = {}
if arch == "ARM":
dct_arch["opcode"] = "LDR"
dct_arch["separator"] = ","
dct_arch["val_offset"] = 2
if arch == "Intel":
dct_arch["opcode"] = "mov"
dct_arch["separator"] = "+"
dct_arch["val_offset"] = 1
return dct_arch
else:
print("Error, Architecture is not supported. Supported architectures are Intel x64/x32 and Arm x64")
return -1
def get_con2_var_or_num(i_cnt, cur_addr):
"""
:param i_cnt: the register of the virtual call
:param cur_addr: the current address in the memory
:return: "success" string and the address of the vtable's location. if it fails it sends the reason and -1
"""
start_addr = idc.get_func_attr(cur_addr, idc.FUNCATTR_START)
virt_call_addr = cur_addr
cur_addr = idc.prev_head(cur_addr)
dct_arch = get_arch_dct()
if dct_arch == -1:
return 'Wrong Architechture', "-1", cur_addr
while cur_addr >= start_addr:
if idc.print_insn_mnem(cur_addr)[:3] == dct_arch["opcode"] and idc.print_operand(cur_addr, 0) == i_cnt: # TODO lea ?
opnd2 = idc.print_operand(cur_addr, 1)
place = opnd2.find(dct_arch["separator"])
if place != -1: # if the function is not the first in the vtable
register = opnd2[opnd2.find('[') + 1: place]
if opnd2.find('*') == -1:
offset = opnd2[place + dct_arch["val_offset"]: opnd2.find(']')]
else:
offset = "*"
return register, offset, cur_addr
else:
offset = "0"
if opnd2.find(']') != -1:
register = opnd2[opnd2.find('[') + 1: opnd2.find(']')]
else:
register = opnd2
return register, offset, cur_addr
elif idc.print_insn_mnem(cur_addr)[:4] == "call":
intr_func_name = idc.print_operand(cur_addr, 0)
# In case the code has CFG -> ignores the function call before the virtual calls
if "guard_check_icall_fptr" not in intr_func_name:
if "nullsub" not in intr_func_name:
# intr_func_name = idc.Demangle(intr_func_name, idc.GetLongPrm(idc.INF_SHORT_DN))
print("Warning! At address 0x%08x: The vtable assignment might be in another function (Maybe %s),"
" could not place BP." % (virt_call_addr, intr_func_name))
cur_addr = start_addr
cur_addr = idc.prev_head(cur_addr)
return "out of the function", "-1", cur_addr
return '', 0, cur_addr
def get_bp_condition(start_addr, register_vtable, offset):
arch, is_64 = get_processor_architecture()
file_name = "BPCond.py"
if arch == "Intel":
if is_64:
file_name = "BPCond64.py"
else:
file_name = "BPCondAarch64.py"
condition_file = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), file_name)
if arch != "Error" or (arch == "ARM" and not is_64):
with open(condition_file, 'r') as f1:
bp_cond_text = f1.read()
bp_cond_text = bp_cond_text.replace("<<<start_addr>>>", str(start_addr))
bp_cond_text = bp_cond_text.replace("<<<register_vtable>>>", register_vtable)
bp_cond_text = bp_cond_text.replace("<<<offset>>>", offset)
return bp_cond_text
return "# Error in BP condition"
def write_vtable2file(start_addr):
"""
:param start_addr: The start address of the virtual call
:return: The break point condition and the break point address
"""
raw_opnd = idc.print_operand(start_addr, 0)
if raw_opnd in REGISTERS:
reg = raw_opnd
else:
for reg in REGISTERS:
if raw_opnd.find(reg) != -1:
break
opnd = get_con2_var_or_num(reg, start_addr)
reg_vtable = opnd[0]
offset = opnd[1]
bp_address = opnd[2]
set_bp = True
cond = ""
# TODO check the get_con2 return variables!!@
try:
# TODO If a structure was already assigned to the BP (not by Virtuailor), before running the code the code will\
# assume it was examined by the user, the BP will not be set
arch_dct = get_arch_dct()
plus_indx = raw_opnd.find(arch_dct["separator"])
if plus_indx != -1:
call_offset = raw_opnd[plus_indx + 1:raw_opnd.find(']')]
# if the offset is in hex
if call_offset.find('h') != -1:
call_offset = int(call_offset[:call_offset.find('h')], 16)
if offset.find('h') != -1:
offset = str(int(offset[:offset.find('h')], 16))
elif offset.find('0x') != -1:
offset = str(int(offset[offset.find('0x') + 2:], 16))
except ValueError:
# A offset structure was set, the old offset will be deleted
set_bp = False
finally:
if set_bp:
# start_addr = start_addr - idc.SegStart(start_addr)
if reg_vtable in REGISTERS:
cond = get_bp_condition(start_addr, reg_vtable, offset)
return cond, bp_address