lunixbochs / patchkit

binary patching from Python
Other
631 stars 85 forks source link

Error when patching a arm binary #31

Closed Ver0n1ca closed 5 years ago

Ver0n1ca commented 5 years ago

The error message:

Traceback (most recent call last):
  File "mine-patch.py", line 35, in <module>
    patch = Patcher(binary)
  File "/home/ver0n1ca/Desktop/testpatch/mine/core/patcher.py", line 11, in __init__
    self.bin = Binary(binary)
  File "/home/ver0n1ca/Desktop/testpatch/mine/core/binary.py", line 29, in __init__
    if ph.isload:
  File "/home/ver0n1ca/Desktop/testpatch/mine/util/elffile.py", line 1434, in isload
    return PT[self.type].name == 'PT_LOAD'
  File "/home/ver0n1ca/Desktop/testpatch/mine/util/elffile.py", line 66, in __getitem__
    return self.bycode[key]
KeyError: 1879048193

The arm binary I use is provided. Please give me a clue, thanks! simple-arm.zip

lunixbochs commented 5 years ago

Is this still broken in the dyn branch?

Ver0n1ca commented 5 years ago

Another error occurred in dyn branch:

./patch simple-arm simple-arm test_patch_arm.py
Traceback (most recent call last):
  File "./patch", line 34, in <module>
    patch = Patcher(binary, verbose=options.verbose, cflags=options.cflags)
  File "/home/ver0n1ca/Desktop/patchkit-dyn/patchkit-dyn/core/patcher.py", line 11, in __init__
    self.bin = Binary(binary)
  File "/home/ver0n1ca/Desktop/patchkit-dyn/patchkit-dyn/core/binary.py", line 15, in __init__
    self.elf = elffile.open(fileobj=self.fileobj)
  File "/home/ver0n1ca/Desktop/patchkit-dyn/patchkit-dyn/util/elffile.py", line 685, in open
    return open(name=name, fileobj=fileobj, map=map, block=block)
  File "/home/ver0n1ca/Desktop/patchkit-dyn/patchkit-dyn/util/elffile.py", line 685, in open
    return open(name=name, fileobj=fileobj, map=map, block=block)
  File "/home/ver0n1ca/Desktop/patchkit-dyn/patchkit-dyn/util/elffile.py", line 666, in open
    ef.unpack_from(block)
  File "/home/ver0n1ca/Desktop/patchkit-dyn/patchkit-dyn/util/elffile.py", line 932, in unpack_from
    self._unpack_dyn()
  File "/home/ver0n1ca/Desktop/patchkit-dyn/patchkit-dyn/util/elffile.py", line 1055, in _unpack_dyn
    x.addend = self.codec.addr.unpack(self.read(x.off, self.addrCoding.size))[0]
AttributeError: 'ElfFile32l' object has no attribute 'addrCoding'

Seems like a small bug in elffile.py. I tried to make some modifies in master branch. I changed PT[self.type].name == 'PT_LOAD' into self.type == 'PT_LOAD' and made modifications in other similar situations and then I got:

./patch -v simple-arm test_patch_arm.py
[*] test_patch_arm.py
 [+] patch()
  [INJECT] @0xa7000-0xa700c
  68656c6c6f20776f726c640a
Exception thrown by patch: /home/ver0n1ca/Desktop/patchkit/test_patch_arm.py patch
Traceback (most recent call last):
  File "/home/ver0n1ca/Desktop/patchkit/core/patcher.py", line 69, in patch
    func(patchset)
  File "/home/ver0n1ca/Desktop/patchkit/test_patch_arm.py", line 13, in patch
    ''' % (size,hello))
  File "/home/ver0n1ca/Desktop/patchkit/core/context.py", line 313, in inject
    ret = self.asm(self.arch.ret())
  File "/home/ver0n1ca/Desktop/patchkit/core/arch.py", line 34, in ret
    raise NotImplementedError
NotImplementedError

I guess it is because the ret() for arm is not implemented? So I started implementing those functions for arm. It's a lot of pain since I know nothing about arm assemble.

Please let me know if my solution is OK. Thanks!

lunixbochs commented 5 years ago

That PT_TYPE change in master is incorrect, since dyn has a totally rewritten enum system

Ver0n1ca commented 5 years ago

Oh, I see... But what should I do to fix the error in dyn?

lunixbochs commented 5 years ago

Try a git pull

lunixbochs commented 5 years ago

The other error on master is because the assembly helpers for arm hooking haven’t been implemented. You can use patch and inject to manually create a trampoline.

  1. Disassemble at the address you want to hook
  2. Copy enough instructions out to make room for a jump.
  3. Put those instructions in a pt.inject() allocation followed by a jump back to immediately after where you grabbed them.
  4. Inject your hook, which is your custom code followed by a jump to the trampoline address.
  5. Patch the hooked address to jump to your injected code.

If you ask very nicely I can implement this as the main hooking method in a way that works with arm.

Ver0n1ca commented 5 years ago

Hi, after git pull the dyn branch, there's another error:

./patch simple-arm test_patch_arm.py 
[*] test_patch_arm.py
 [+] patch()
  [INJECT] @0xa7000-0xa700c
Exception thrown by patch: /home/ver0n1ca/Desktop/patchkit-dyn_new/test_patch_arm.py patch
Traceback (most recent call last):
  File "/home/ver0n1ca/Desktop/patchkit-dyn_new/core/patcher.py", line 69, in patch
    func(patchset)
  File "/home/ver0n1ca/Desktop/patchkit-dyn_new/test_patch_arm.py", line 14, in patch
    ''' % (size,hello))
  File "/home/ver0n1ca/Desktop/patchkit-dyn_new/core/context.py", line 307, in inject
    raw, typ = self._compile(addr, **kwargs)
  File "/home/ver0n1ca/Desktop/patchkit-dyn_new/core/context.py", line 275, in _compile
    raw = self.asm(asm, addr=addr)
  File "/home/ver0n1ca/Desktop/patchkit-dyn_new/core/context.py", line 136, in asm
    return self.arch.asm(asm, addr=addr, att_syntax=att_syntax)
  File "/home/ver0n1ca/Desktop/patchkit-dyn_new/core/arch.py", line 21, in asm
    self.ks.syntax = saved
  File "/usr/local/lib/python2.7/dist-packages/keystone/keystone.py", line 182, in syntax
    raise KsError(status)
KsError: Invalid option (KS_ERR_OPT_INVALID)
Memory maps:
0x10000-0x90000
0x96f6c-0xa6f6c
0xa7000-0xa8000
0x8a7000-0x8a8000
0x16a7000-0x16a8000
0x24a7000-0x24a8000

I tried to read ks.cpp and found:

    switch(type) {
        case KS_OPT_SYNTAX:
            if (ks->arch != KS_ARCH_X86)
                return KS_ERR_OPT_INVALID;

So I guess ks.syntax cannot be used for arm? Thanks!

Ver0n1ca commented 5 years ago

If you have time, it would be very very nice of you to help me with arm hooking! :D

lunixbochs commented 5 years ago

pushed some stuff

lunixbochs commented 5 years ago

This arm32 patch worked for me on latest dyn (make sure to set the hook addr to something reasonable)

def patch(pt):
    hello, size = pt.inject(raw='hello world\n', size=True)
    addr = pt.inject(asm=r'''
    push {r0, r1, r2, r7}
    mov r7, 0x4
    mov r0, 0x1
    mov r1, %#x
    mov r2, %#x
    swi #0
    pop {r0, r1, r2, r7}
    bx lr
    ''' % (hello, size))
    pt.hook(0x1043c, addr)
Ver0n1ca commented 5 years ago

It works for me perfectly! Thank you so much :D

lunixbochs commented 5 years ago

If this project is helpful for you, consider supporting my Patreon: https://www.patreon.com/lunixbochs

Ver0n1ca commented 5 years ago

Sure, I'll see what I can do :)

Ver0n1ca commented 5 years ago

Hi,

When I use the dyn branch to patch binaries I found that if the evicted instructions contains short jump will lead to some problem since it cannot jump to the right address. Could you please give me a clue on how to fix it?

Thanks a lot!

lunixbochs commented 5 years ago

Good question, this should be a new issue. You can typically fix this by lifting and reassembling the instructions instead of just moving their bytes as hook does now.

You can do a sort of manual hook like this:

src = pt.dis(addr)
hook = pt.inject(asm='''
do something
ret
''')
trampoline = pt.inject(asm=src)
pt.inject(jmp=hook)
pt.patch(addr, call=trampoline)
Ver0n1ca commented 5 years ago

Hi,

I solved the short jump according to your suggestion and I also solved another similar one:D Thanks a lot!