Skip to content

Commit 8503381

Browse files
committed
♻️ Small code refactoring
1 parent caa6a62 commit 8503381

File tree

5 files changed

+69
-57
lines changed

5 files changed

+69
-57
lines changed

__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from binaryninja.enums import MessageBoxButtonSet, MessageBoxIcon, MessageBoxButtonResult
44

55
from .bnhelpers import BNHelpers
6-
from .delphi_analyser import ClassFinder, DelphiVMT
6+
from .delphi_analyser import DelphiAnalyzer, DelphiVMT
77

88

99
class AnalyzeDelphiVmtsTask(BackgroundTaskThread):
@@ -17,8 +17,8 @@ def __init__(self, bv: BinaryView, tag_type: Tag, delphi_version: int):
1717
def run(self):
1818
self._bv.begin_undo_actions()
1919

20-
finder = ClassFinder(self._bv, self._delphi_version)
21-
finder.update_analysis_and_wait(self.analyze_callback)
20+
analyzer = DelphiAnalyzer(self._bv, self._delphi_version)
21+
analyzer.update_analysis_and_wait(self.analyze_callback)
2222

2323
self._bv.commit_undo_actions()
2424
self._bv.update_analysis()
@@ -41,8 +41,8 @@ def analyze_callback(self, delphi_vmt: DelphiVMT):
4141
if function.name.startswith('sub_') or function.name == 'vmt' + delphi_vmt.class_name:
4242
self._bv.remove_user_function(function)
4343

44-
# TODO: Clean that later (define a property for VMT tables)
45-
for table_addr in delphi_vmt._get_vmt_tables_addr():
44+
# Same here
45+
for table_addr in delphi_vmt.table_list.keys():
4646
for function in self._bv.get_functions_containing(table_addr):
4747
if function.name.startswith('sub_'):
4848
self._bv.remove_user_function(function)

delphi_analyser.py

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,25 @@ def __init__(self, bv: BinaryView, delphi_version: int, address: int):
3030
self._class_name = ''
3131
self._instance_size = 0
3232
self._parent_vmt = 0
33+
self._table_list: Mapping[int, str] = {}
3334
self._virtual_methods: Mapping[int, str] = {}
3435

3536
if not self._check_self_ptr():
3637
return
3738

38-
if not self._parse_name():
39+
if not self._resolve_name():
3940
return
4041

41-
if not self._parse_instance_size():
42+
if not self._resolve_instance_size():
4243
return
4344

44-
if not self._parse_parent_vmt():
45+
if not self._resolve_parent_vmt():
4546
return
4647

47-
if not self._parse_virtual_methods():
48+
if not self._resolve_table_list():
49+
return
50+
51+
if not self._resolve_virtual_methods():
4852
return
4953

5054
self._is_valid = True
@@ -57,7 +61,9 @@ def __repr__(self):
5761
def __str__(self):
5862
if not self._is_valid:
5963
return f'<InvalidVmt address=0x{self._vmt_address:08X}>'
60-
return f'<{self._class_name} start=0x{self.start:08X} instance_size=0x{self._instance_size:X}>'
64+
65+
return (f'<{self._class_name} start=0x{self.start:08X} '
66+
f'instance_size=0x{self._instance_size:X}>')
6167

6268

6369
## Properties
@@ -82,6 +88,10 @@ def instance_size(self) -> int:
8288
def parent_vmt(self) -> int:
8389
return self._parent_vmt
8490

91+
@property
92+
def table_list(self) -> Mapping[int, str]:
93+
return self._table_list
94+
8595
@property
8696
def virtual_methods(self) -> Mapping[int, str]:
8797
return self._virtual_methods
@@ -107,23 +117,23 @@ def br_offset(self) -> int:
107117
## Public API
108118

109119
def seek_to_code(self, address: int) -> bool:
110-
if not self._isValidCodeAdr(address):
120+
if not self._is_valid_code_addr(address):
111121
return False
112122

113123
self._br.seek(address)
114124
return True
115125

116126

117127
def seek_to_code_offset(self, offset: int) -> bool:
118-
if not self._isValidCodeAdr(self._code_section.start + offset):
128+
if not self._is_valid_code_addr(self._code_section.start + offset):
119129
return False
120130

121131
self._br.seek(self._code_section.start + offset)
122132
return True
123133

124134

125135
def seek_to_vmt_offset(self, offset: int) -> bool:
126-
if not self._isValidCodeAdr(self._vmt_address + offset):
136+
if not self._is_valid_code_addr(self._vmt_address + offset):
127137
return False
128138

129139
self._br.seek(self._vmt_address + offset)
@@ -148,7 +158,7 @@ def _check_self_ptr(self) -> bool:
148158
return self_ptr == self._vmt_address
149159

150160

151-
def _parse_name(self) -> bool:
161+
def _resolve_name(self) -> bool:
152162
class_name_addr = self._get_class_name_addr()
153163

154164
if class_name_addr is None:
@@ -172,23 +182,23 @@ def _parse_name(self) -> bool:
172182
return True
173183

174184

175-
def _parse_instance_size(self) -> bool:
185+
def _resolve_instance_size(self) -> bool:
176186
if not self.seek_to_vmt_offset(self._vmt_offsets.cVmtInstanceSize):
177187
return False
178188

179189
self._instance_size = self._br.read32()
180190
return True
181191

182192

183-
def _parse_parent_vmt(self) -> bool:
193+
def _resolve_parent_vmt(self) -> bool:
184194
if not self.seek_to_vmt_offset(self._vmt_offsets.cVmtParent):
185195
return False
186196

187197
self._parent_vmt = self._br.read32()
188198
return True
189199

190200

191-
def _parse_virtual_methods(self) -> bool:
201+
def _resolve_virtual_methods(self) -> bool:
192202
class_name_addr = self._get_class_name_addr()
193203

194204
if class_name_addr is None:
@@ -197,7 +207,7 @@ def _parse_virtual_methods(self) -> bool:
197207
address_size = self._bv.address_size
198208
offsets = self.vmt_offsets.__dict__.items()
199209
offset_map = {y:x for x, y in offsets}
200-
tables_addr = self._get_vmt_tables_addr()
210+
tables_addr = self._table_list.keys()
201211

202212
if not self.seek_to_vmt_offset(self._vmt_offsets.cVmtParent + address_size):
203213
return False
@@ -208,7 +218,7 @@ def _parse_virtual_methods(self) -> bool:
208218
if field_value == 0:
209219
continue
210220

211-
if not self._isValidCodeAdr(field_value):
221+
if not self._is_valid_code_addr(field_value):
212222
prev_offset = self._br.offset - address_size
213223
raise RuntimeError(f'Invalid code address deteted at 0x{prev_offset:08X} '
214224
'({self.class_name})\n If you think it\'s a bug, please open an issue on '
@@ -227,7 +237,31 @@ def _parse_virtual_methods(self) -> bool:
227237
return True
228238

229239

230-
def _isValidCodeAdr(self, addy: int, allow_null=False) -> bool:
240+
def _resolve_table_list(self) -> bool:
241+
if not self.seek_to_vmt_offset(self.vmt_offsets.cVmtIntfTable):
242+
return False
243+
244+
offsets = self._vmt_offsets.__dict__.items()
245+
offset_map = {y:x[4:] for x, y in offsets}
246+
247+
stop_at = self._vmt_address + self._vmt_offsets.cVmtClassName
248+
249+
while self._br.offset != stop_at:
250+
prev_br_offset = self._br.offset
251+
address = self._br.read32()
252+
253+
if address < 1:
254+
continue
255+
256+
if not self._is_valid_code_addr(address):
257+
raise RuntimeError('Invalid table address detected')
258+
259+
self._table_list[address] = offset_map[prev_br_offset - self._vmt_address]
260+
261+
return True
262+
263+
264+
def _is_valid_code_addr(self, addy: int, allow_null=False) -> bool:
231265
if addy == 0:
232266
return allow_null
233267
return addy >= self._code_section.start and addy < self._code_section.end
@@ -239,34 +273,13 @@ def _get_class_name_addr(self) -> Union[None, int]:
239273

240274
class_name_addr = self._br.read32()
241275

242-
if not self._isValidCodeAdr(class_name_addr):
276+
if not self._is_valid_code_addr(class_name_addr):
243277
return None
244278

245279
return class_name_addr
246280

247281

248-
def _get_vmt_tables_addr(self) -> Union[None, List[int]]:
249-
if not self.seek_to_vmt_offset(self.vmt_offsets.cVmtIntfTable):
250-
return
251-
252-
result = []
253-
stop_at = self._vmt_address + self.vmt_offsets.cVmtClassName
254-
255-
while self._br.offset != stop_at:
256-
address = self._br.read32()
257-
258-
if address < 1:
259-
continue
260-
261-
if not self._isValidCodeAdr(address):
262-
raise RuntimeError('Invalid table address detected')
263-
264-
result.append(address)
265-
266-
return result
267-
268-
269-
class ClassFinder(object):
282+
class DelphiAnalyzer(object):
270283
'''
271284
TODO: Doc
272285
'''
@@ -286,7 +299,6 @@ def __init__(self, bv: BinaryView, delphi_version: int):
286299
def delphi_version(self) -> int:
287300
return self._delphi_version
288301

289-
290302
@property
291303
def vmt_list(self) -> List[DelphiVMT]:
292304
return self._vmt_list

examples/create_vmt_structs.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import sys
66
from os import path
77

8-
# from delphi_ninja.delphi_analyser import ClassFinder, DelphiVMT
8+
# from delphi_ninja.delphi_analyser import DelphiAnalyzer, DelphiVMT
99
# from delphi_ninja.bnlogger import BNLogger
1010
# from delphi_ninja.bnhelpers import BNHelpers
1111

@@ -14,7 +14,7 @@
1414
module_parent = path.dirname(module_dir)
1515
sys.path.insert(0, module_parent)
1616
delphi_ninja = importlib.import_module(module_name)
17-
ClassFinder = delphi_ninja.delphi_analyser.ClassFinder
17+
DelphiAnalyzer = delphi_ninja.delphi_analyser.DelphiAnalyzer
1818
DelphiVMT = delphi_ninja.delphi_analyser.DelphiVMT
1919
BNLogger = delphi_ninja.bnlogger.BNLogger
2020
BNHelpers = delphi_ninja.bnhelpers.BNHelpers
@@ -44,8 +44,8 @@ def main(target: str, delphi_version: int):
4444
BNLogger.log('-----------------------------')
4545
BNLogger.log('Searching for VMT...')
4646

47-
finder = ClassFinder(bv, delphi_version)
48-
finder.update_analysis_and_wait(lambda vmt: analyze_callback(vmt, bv))
47+
analyzer = DelphiAnalyzer(bv, delphi_version)
48+
analyzer.update_analysis_and_wait(lambda vmt: analyze_callback(vmt, bv))
4949

5050
bv.update_analysis_and_wait()
5151

examples/list_vmts.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
from os import path
77

88
# from delphi_ninja.bnlogger import BNLogger
9-
# from delphi_ninja.delphi_analyser import ClassFinder
9+
# from delphi_ninja.delphi_analyser import DelphiAnalyzer
1010

1111
module_dir = path.dirname(path.dirname(path.abspath(__file__)))
1212
module_name = path.basename(module_dir)
1313
module_parent = path.dirname(module_dir)
1414
sys.path.insert(0, module_parent)
1515
delphi_ninja = importlib.import_module(module_name)
1616
BNLogger = delphi_ninja.bnlogger.BNLogger
17-
ClassFinder = delphi_ninja.delphi_analyser.ClassFinder
17+
DelphiAnalyzer = delphi_ninja.delphi_analyser.DelphiAnalyzer
1818

1919

2020
def main(target: str, delphi_version: int):
@@ -36,8 +36,8 @@ def main(target: str, delphi_version: int):
3636
BNLogger.log('-----------------------------')
3737
BNLogger.log('Searching for VMT...')
3838

39-
finder = ClassFinder(bv, delphi_version)
40-
finder.update_analysis_and_wait(lambda vmt: BNLogger.log(vmt))
39+
analyzer = DelphiAnalyzer(bv, delphi_version)
40+
analyzer.update_analysis_and_wait(lambda vmt: BNLogger.log(vmt))
4141

4242

4343
if __name__ == '__main__':

examples/vmt_visualizer.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from os import path
99

1010
# from delphi_ninja.bnlogger import BNLogger
11-
# from delphi_ninja.delphi_analyser import ClassFinder, DelphiVMT
11+
# from delphi_ninja.delphi_analyser import DelphiAnalyzer, DelphiVMT
1212

1313
module_dir = path.dirname(path.dirname(path.abspath(__file__)))
1414
module_name = path.basename(module_dir)
@@ -17,7 +17,7 @@
1717
delphi_ninja = importlib.import_module(module_name)
1818

1919
BNLogger = delphi_ninja.bnlogger.BNLogger
20-
ClassFinder = delphi_ninja.delphi_analyser.ClassFinder
20+
DelphiAnalyzer = delphi_ninja.delphi_analyser.DelphiAnalyzer
2121
DelphiVMT = delphi_ninja.delphi_analyser.DelphiVMT
2222

2323

@@ -58,11 +58,11 @@ def main(target: str, delphi_version: int):
5858
BNLogger.log('File loaded')
5959
BNLogger.log('Searching for VMT...')
6060

61-
finder = ClassFinder(bv, delphi_version)
62-
finder.update_analysis_and_wait()
61+
analyzer = DelphiAnalyzer(bv, delphi_version)
62+
analyzer.update_analysis_and_wait()
6363

6464
BNLogger.log('Creating Graph...')
65-
vmt_map = {vmt.start:vmt for vmt in finder.vmt_list}
65+
vmt_map = {vmt.start:vmt for vmt in analyzer.vmt_list}
6666
create_graph(vmt_map)
6767

6868

0 commit comments

Comments
 (0)