-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdiff.py
More file actions
226 lines (190 loc) · 6.81 KB
/
diff.py
File metadata and controls
226 lines (190 loc) · 6.81 KB
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
"""
Utils to diff two heap memory record allocations
2015-08-01: does not work. Is not used.
"""
import argparse
import logging
import sys
import os
from haystack import argparse_utils
from haystack.mappings import folder
from haystack.reverse.heuristics import reversers
from haystack.reverse import context
from haystack.reverse import config
from haystack.reverse import structure
__author__ = "Loic Jaquemet"
__copyright__ = "Copyright (C) 2012 Loic Jaquemet"
__email__ = "loic.jaquemet+python@gmail.com"
__license__ = "GPL"
__maintainer__ = "Loic Jaquemet"
__status__ = "Production"
log = logging.getLogger('diff')
def make(opts):
log.info('[+] Loading context of %s' % opts.dump1)
# '../../outputs/skype.1.a') # TODO
ctx = context.get_context(opts.dump1)
# refresh
if len(ctx.structures) != len(ctx.structures_addresses):
log.info(
'[+] Refreshing from %d allocators cached' %
(len(
ctx.structures)))
# FIXME, I think its now an heapwalker, not a reverser
mallocRev = reversers.MallocReverser()
ctx = mallocRev.reverse(ctx)
mallocRev.check_inuse(ctx)
log.info(
'[+] Final %d allocators from malloc blocs' %
(len(
ctx.structures)))
finder = ctx.get_memory_handler().get_heap_finder()
heap1 = finder.list_heap_walkers()[0]
log.info('[+] Loading _memory_handler of %s' % opts.dump2)
newmappings = folder.load(opts.dump2)
finder2 = newmappings.get_heap_finder()
heap2 = finder2.list_heap_walkers()[0]
log.info('[+] finding diff values with %s' % opts.dump2)
addrs = cmd_cmp(heap1, heap2, heap1.start)
# now compare with allocators addresses
structures = []
realloc = 0
log.info('[+] Looking at %d differences' % (len(addrs)))
st = []
# joined iteration, found structure affected
# use info from malloc : allocators.start + .size
addr_iter = iter(addrs)
structs_addr_iter = iter(ctx.malloc_addresses)
structs_size_iter = iter(ctx.malloc_sizes)
try:
addr = addr_iter.next()
st_addr = structs_addr_iter.next()
st_size = structs_size_iter.next()
cnt = 1
while True:
while (addr - st_addr) >= st_size: # find st containing offset
st_addr = structs_addr_iter.next()
st_size = structs_size_iter.next()
# check for gaps
if (addr - st_addr) < 0: # went to far - no struct overlapping
# addr is in between two struct - dump all addr stuck out of
# malloc_chunks
while (addr - st_addr) < 0:
addr = addr_iter.next()
pass
continue
#
# check if offset is really in st ( should be always if your not
# dumb/there no holes )
if 0 <= (addr - st_addr) < st_size:
# tag the structure as different
structures.append(ctx.structures[st_addr])
cnt += 1
else:
# (addr - st_addr) < 0 # impossible by previous while
# (addr - st_addr) >= st_size # then continur
continue
while (addr - st_addr) < st_size: # enumerate offsets in st range
addr = addr_iter.next()
cnt += 1
except StopIteration as e:
pass
addrs_found = cnt
log.info(
'[+] On %d diffs, found %d structs with different values. realloc: %d' %
(addrs_found, len(structures), realloc))
log.info('[+] Outputing to file (will be long-ish)')
print_diff_files(opts, context, newmappings, structures)
def print_diff_files(opts, context, newmappings, structures):
# print original struct in one file, diffed struct in the other
d1out = config.Config.getCacheFilename(
config.Config.DIFF_PY_HEADERS, '%s-%s' %
(opts.dump1, opts.dump1))
d2out = config.Config.getCacheFilename(
config.Config.DIFF_PY_HEADERS, '%s-%s' %
(opts.dump1, opts.dump2))
f1 = open(d1out, 'w')
f2 = open(d2out, 'w')
for st in structures:
st2 = structure.remap_load(context, st.vaddr, newmappings)
if st.bytes == st2.bytes:
print('identic bit field !!!')
return
# get the fields
# TODO FIXME , fix and leverage Field.getValue() to update from a changed mapping
# TODO, in toString(), pointer value should be in comment, to check for
# pointer change, when same pointed struct.
st.decodeFields()
#st.resolvePointers(ctx.structures_addresses, ctx.allocators)
# st._aggregateFields()
st2.reset() # clean previous state
st2.decodeFields()
#st2.resolvePointers(ctx.structures_addresses, ctx.allocators)
# st2._aggregateFields()
# write the files
f1.write(st.to_string())
f1.write('\n')
f2.write(st2.to_string())
f2.write('\n')
sys.stdout.write('.')
sys.stdout.flush()
print()
f1.close()
f2.close()
log.info('[+] diffed allocators dumped in %s %s' % (d1out, d2out))
def cmd_cmp(heap1, heap2, baseOffset):
# LINUX based system command cmp parsing
import subprocess
f1 = heap1._memdump.name
f2 = heap2._memdump.name
addrs = []
try:
res = subprocess.check_output(['cmp', f1, f2, '-l'])
except subprocess.CalledProcessError as e:
res = e.output
for line in res.split('\n'):
cols = line.split(' ')
try:
while cols[0] == '':
cols.pop(0)
except:
continue
addrs.append(int(cols.pop(0)) + baseOffset - 1) # starts with 1
return addrs
def argparser():
rootparser = argparse.ArgumentParser(
prog='haystack-reversers-diff',
description='Diff struct of the same instance.')
rootparser.add_argument(
'--debug',
action='store_true',
help='Debug mode on.')
rootparser.add_argument(
'dump1',
type=argparse_utils.readable,
action='store',
help='Dump file 1.')
rootparser.add_argument(
'dump2',
type=argparse_utils.readable,
action='store',
help='Dump file 2.')
rootparser.set_defaults(func=make)
return rootparser
def main(argv):
parser = argparser()
opts = parser.parse_args(argv)
level = logging.INFO
if opts.debug:
level = logging.DEBUG
flog = os.path.normpath('log')
logging.basicConfig(level=level, filename=flog, filemode='w')
logging.getLogger('diff').addHandler(
logging.StreamHandler(
stream=sys.stdout))
log.info('[+] output log to %s' % flog)
opts.func(opts)
if __name__ == '__main__':
main(sys.argv[1:])