Skip to content

Commit d5f175d

Browse files
committed
tweak output, do PC-relative hook patching
1 parent ddaffac commit d5f175d

File tree

2 files changed

+58
-34
lines changed

2 files changed

+58
-34
lines changed

core/arch.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ def __init__(self):
1111
def asm(self, asm, addr=0, att_syntax=False):
1212
if not asm:
1313
return ''
14+
# asm start label for use with relative offsets
15+
asm = '_PKST_:;' + asm
16+
1417
saved = self.ks.syntax
1518
if att_syntax:
1619
self.ks.syntax = KS_OPT_SYNTAX_ATT
@@ -43,21 +46,29 @@ def jmp(self, dst): return 'jmp %s;' % dst
4346
def ret(self): return 'ret;'
4447
def nop(self): return 'nop;'
4548

49+
# memcpy should be pc-relative
50+
# dst and src are offsets from the _PKST_ label
4651
def memcpy(self, dst, src, size):
4752
return '''
4853
push edi
4954
push esi
5055
push ecx
56+
57+
call ref
58+
ref: pop edi
59+
sub edi, ref - _PKST_
60+
mov esi, edi
61+
62+
add edi, 0x%x
63+
add esi, 0x%x
5164
mov ecx, 0x%x
52-
mov edi, 0x%x
53-
mov esi, 0x%x
5465
5566
rep movsb
5667
5768
pop ecx
5869
pop esi
5970
pop edi
60-
''' % (size, dst, src)
71+
''' % (dst, src, size)
6172

6273
class x86_64(x86):
6374
_cs = CS_ARCH_X86, CS_MODE_64
@@ -68,16 +79,17 @@ def memcpy(self, dst, src, size):
6879
push rdi
6980
push rsi
7081
push rcx
82+
83+
lea rdi, [rip - _PKST_ + %d]
84+
lea rsi, [rip - _PKST_ + %d]
7185
mov rcx, 0x%x
72-
mov rdi, 0x%x
73-
mov rsi, 0x%x
7486
7587
rep movsb
7688
7789
pop rcx
7890
pop rsi
7991
pop rdi
80-
''' % (size, dst, src)
92+
''' % (dst, src, size)
8193

8294
class arm(Arch):
8395
_cs = CS_ARCH_ARM, CS_MODE_ARM

core/context.py

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from util.elffile import EM
1010
from util.patch.dis import irdis
1111

12+
def pfcol(s):
13+
return '[\033[1m\033[32m%s\033[0m] ' % s
14+
1215
class Context(object):
1316
def __init__(self, binary, verbose=False):
1417
self.binary = binary
@@ -40,7 +43,7 @@ def entry(self):
4043

4144
@entry.setter
4245
def entry(self, val):
43-
self.info('[MOVE ENTRY POINT] -> 0x%x' % val)
46+
self.info(pfcol('MOVE ENTRY POINT') + '-> 0x%x' % val)
4447
self.elf.entry = val
4548

4649
def funcs(self, marked=False):
@@ -86,11 +89,11 @@ def info(self, *args, **kwargs):
8689

8790
# TODO: show warn/error at the end, and colorize
8891
def warn(self, *args, **kwargs):
89-
kwargs['prefix'] = '\033[33m[WARN]\033[39m'
92+
kwargs['prefix'] = '\033[1m\033[33m[WARN]\033[0m'
9093
self.info(*args, **kwargs)
9194

9295
def error(self, *args, **kwargs):
93-
kwargs['prefix'] = '\033[31m[ERR]\033[39m'
96+
kwargs['prefix'] = '\033[1m\033[31m[ERR]\033[0m'
9497
self.info(*args, **kwargs)
9598

9699
def debug(self, *args, **kwargs):
@@ -155,22 +158,22 @@ def search(self, data):
155158
idx = segment.data.index(data)
156159
if idx >= 0:
157160
addr = segment.addr + idx
158-
self.debug('[SEARCH] "%s" found at 0x%x' % (tmp, addr))
161+
self.debug(pfcol('SEARCH') + '"%s" found at 0x%x' % (tmp, addr))
159162
return addr
160163
except ValueError:
161164
pass
162-
self.error('[SEARCH] "%s" not found.' % tmp)
165+
self.error(pfcol('SEARCH') + '"%s" not found.' % tmp)
163166

164-
def hook(self, src, dst, first=False):
167+
def hook(self, src, dst, first=False, noentry=False):
165168
# hooking the entry point is a special, more efficient case
166-
if src == self.entry:
169+
if src == self.entry and not noentry:
167170
if first:
168171
self.binary.entry_hooks.insert(0, dst)
169172
else:
170173
self.binary.entry_hooks.append(dst)
171-
self.debug('[HOOK] ENTRY -> 0x%x' % dst)
174+
self.debug(pfcol('HOOK') + 'ENTRY -> 0x%x' % dst)
172175
return
173-
self.debug('[HOOK] @0x%x -> 0x%x' % (src, dst))
176+
self.debug(pfcol('HOOK') + '@0x%x -> 0x%x' % (src, dst))
174177
self.make_writable(src)
175178

176179
alloc = self.binary.next_alloc()
@@ -207,37 +210,34 @@ def hook(self, src, dst, first=False):
207210
jmpevict = str(self.elf.read(jmpoff, len(emptyjmp)))
208211

209212
stage0 = evicted + jmpevict
210-
stage1_addr = self.inject(raw=stage0, internal=True)
211-
stage2_addr = self.inject(raw=stage0, internal=True)
213+
# TODO: self.alloc()?
214+
stage1_addr = self.binary.alloc(len(stage0), target='patch')
215+
stage2_addr = self.binary.alloc(len(stage0), target='patch')
212216

217+
# memcpy needs to be pc-relative
218+
base = self.binary.next_alloc()
213219
hook1 = self.inject(asm=';'.join((
214220
self.arch.call(dst),
215-
self.arch.memcpy(src, stage2_addr, len(stage0)),
221+
self.arch.memcpy(src - base, stage2_addr - base, len(stage0)),
216222
self.arch.jmp(src),
217223
)), internal=True)
224+
base = self.binary.next_alloc()
218225
hook2 = self.inject(asm=';'.join((
219-
self.arch.memcpy(src, stage1_addr, len(stage0)),
226+
self.arch.memcpy(src - base, stage1_addr - base, len(stage0)),
220227
self.arch.jmp(jmpoff),
221228
)), internal=True)
222229

223230
# we need to overwrite both stages because we didn't know the hook addrs at the time
224231
stage1 = self.asm(';'.join(
225232
(self.arch.jmp(hook1),) + (self.arch.nop(),) * (len(evicted) - len(emptyjmp)),
226233
), addr=src) + jmpevict
227-
self.elf.write(stage1_addr, stage1)
234+
self.patch(stage1_addr, raw=stage1, is_asm=True, internal=True, desc='hook stage 1')
228235
stage2 = evicted + self.asm(self.arch.jmp(hook2), addr=jmpoff)
229-
self.elf.write(stage2_addr, stage2)
230-
231-
'''
232-
print 'stage 1', binascii.hexlify(stage1)
233-
print self.pdis(self.arch.dis(stage1, src))
234-
print 'stage 2', binascii.hexlify(stage2)
235-
print self.pdis(self.arch.dis(stage2, src))
236-
'''
236+
self.patch(stage2_addr, raw=stage2, is_asm=True, internal=True, desc='hook stage 2')
237237

238238
# TODO: act more like mobile substrate wrt orig calling?
239239
# that is, make calling orig optional
240-
self.patch(src, raw=stage1)
240+
self.patch(src, raw=stage1, is_asm=True, internal=True, desc='hook entry point')
241241

242242
def _lint(self, addr, raw, typ, is_asm=False):
243243
if typ == 'asm' or is_asm:
@@ -273,6 +273,10 @@ def inject(self, **kwargs):
273273
mark_func = kwargs.get('mark_func', False)
274274
return_size = kwargs.get('size', False)
275275
target = kwargs.get('target', 'patch')
276+
desc = kwargs.get('desc', '')
277+
if desc:
278+
desc = ' | "%s"' % desc
279+
276280
addr = self.binary.next_alloc(target)
277281
c = kwargs.get('c')
278282
if c:
@@ -289,7 +293,7 @@ def inject(self, **kwargs):
289293
if raw[-len(ret):] != ret and not internal:
290294
self.warn('Injected asm does not return!')
291295

292-
self.info('[INJECT] @0x%x-0x%x' % (addr, addr + len(raw)))
296+
self.info(pfcol('INJECT') + '@0x%x-0x%x%s' % (addr, addr + len(raw), desc))
293297
if not kwargs.get('silent'):
294298
if typ == 'asm' or is_asm:
295299
self.debug(dis=self.arch.dis(raw, addr=addr))
@@ -307,22 +311,30 @@ def inject(self, **kwargs):
307311

308312
def patch(self, addr, **kwargs):
309313
raw, typ = self._compile(addr, **kwargs)
314+
desc = kwargs.get('desc', '')
315+
if desc:
316+
desc = ' | "%s"' % desc
310317

311-
self.info('[PATCH] @0x%x-0x%x' % (addr, addr + len(raw)))
318+
self.info(pfcol('PATCH') + '@0x%x-0x%x%s' % (addr, addr + len(raw), desc))
312319
if len(raw) == 0:
313320
self.warn('Empty patch.')
314321
return
315322

316323
if typ == 'asm' or kwargs.get('is_asm'):
317324
size = len(''.join([str(i.bytes) for i in self.dis(addr, len(raw))]))
318-
if size != len(raw):
325+
if size != len(raw) and not kwargs.get('internal'):
319326
self.warn('Assembly patch is not aligned with underlying instructions.')
320327

321328
self._lint(addr, raw, typ, is_asm=kwargs.get('is_asm'))
322329
if not kwargs.get('silent'):
323330
if typ == 'asm' or kwargs.get('is_asm'):
324-
for line in self.pdis(self.dis(addr, len(raw))).split('\n'):
325-
self.debug('- %s' % line)
331+
# collapse nulls
332+
old = self.elf.read(addr, len(raw))
333+
if old == '\0' * len(raw):
334+
self.debug('- %s' % ('00' * len(raw)))
335+
else:
336+
for line in self.pdis(self.dis(addr, len(raw))).split('\n'):
337+
self.debug('- %s' % line)
326338
for line in self.pdis(self.arch.dis(raw, addr=addr)).split('\n'):
327339
self.debug('+ %s' % line)
328340
else:

0 commit comments

Comments
 (0)