-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathschema-diff.py
141 lines (96 loc) · 3.32 KB
/
schema-diff.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
"""
Command line tool to diff client opcode schemas generated by the IDA script and remap opcodes from one binary to another.
"""
import json
import sys
import requests
import CppHeaderParser
#### config/settings/garbage
fucked_distance = 0xffffffff
max_size_diff = 10
#### end config shit
if len(sys.argv) != 3:
print('missing args: [old exe schema] [new exe schema]')
sys.exit(1)
with open(sys.argv[1]) as f:
old_schema = json.load(f)
with open(sys.argv[2]) as f:
new_schema = json.load(f)
# print revs
print('old client rev: %s' % old_schema['clean_rev'])
print('new client rev: %s' % new_schema['clean_rev'])
# fetch name hinting file for old rev
if old_schema['ipcs_file']:
print('have ipcs_file in client schema, downloading symbols: %s' % old_schema['ipcs_file'])
ipcs_data = requests.get(old_schema['ipcs_file'])
if ipcs_data.status_code == 200:
header = CppHeaderParser.CppHeader(ipcs_data.text, argType="string")
opcodes_found = []
# newlines for the autism
print()
def get_opcode_by_val(enum_name, opcode):
for enum in header.enums:
if enum['name'] == enum_name:
# find enum value
for val in enum['values']:
if val['value'] == opcode:
return val['name']
return "Unknown"
def find_close_numeric(objs, dest, getter):
closest = fucked_distance
closest_obj = None
for obj in objs:
val = getter(obj)
num = abs(val - dest)
if num < closest:
closest = num
closest_obj = obj
return (closest, closest_obj)
def find_in(objs, expr):
for obj in objs:
if expr(obj):
return obj
return None
def get_opcodes_str(opcodes):
return ', '.join([hex(o) for o in opcodes])
def add_match_case(cases, case):
# check if case already exists
for c in cases:
if c['rel_ea'] == case['rel_ea']:
return
cases.append(case)
for k, case in enumerate(old_schema['cases']):
old_opcodes = case['opcodes']
print('finding opcode(s): %s' % get_opcodes_str(old_opcodes))
matched_handlers = []
# see if we can get a match for the relative ea first
dist, dist_match = find_close_numeric(new_schema['cases'], case['rel_ea'], lambda obj : obj['rel_ea'])
if dist == fucked_distance:
print(' got fucked distance, what?')
continue
order_match = new_schema['cases'][k]
size_diff = abs(dist_match['size'] - case['size'])
#print(' os: %d ns: %d d: %d' % (dist_match['size'], case['size'], size_diff))
# see if the rva matches for the cases found by the distance and order
if dist_match['rel_ea'] == order_match['rel_ea'] and size_diff < max_size_diff:
print(' got order match, size diff: %d < %d' % (size_diff, max_size_diff))
# check if calls count match in found match & og code
if len(case['func']['calls']) == len(order_match['func']['calls']):
print(' has nested callcount match')
add_match_case(matched_handlers, order_match)
matched = len(matched_handlers)
if matched == 1:
# holy shit
opcodes_found.append((old_opcodes, matched_handlers[0]['opcodes']))
elif matched > 1:
print(' found %d matching handlers' % matched)
#break
# dump found shit
print()
for k, v in enumerate(opcodes_found):
old, new = v
print('branch %d' % k)
old = ', '.join(['%s (%s)' % (hex(o), get_opcode_by_val('ServerZoneIpcType', o)) for o in old])
print(' - old: %s' % old)
print(' - new: %s' % get_opcodes_str(new))
print('found %d/%d opcode branches!' % (len(opcodes_found), len(old_schema['cases'])))