unicorn icon indicating copy to clipboard operation
unicorn copied to clipboard

Existence of HOOK_CODE changes behavior of HOOK_BLOCK

Open paulkermann opened this issue 1 year ago • 3 comments

I have a unicorn instance hooking functions via the HOOK_BLOCK callback. In that callback I alter the stack and the IP. This method worked fine for me in versions up until version 2.0.rc7 (rc6 and 1.0.3 worked fine). When I changed the callback from HOOK_BLOCK to HOOK_CODE (in the newer version) the code worked fine. The weird thing is that if I do something like this

temp_hook = uc.hook_add(UC_HOOK_CODE, temp_func)
uc.hook_del(temp_hook)
block_hook = uc.hook_add(UC_HOOK_BLOCK, block_callback)

then the code works as it was before. Even before doing emu_start it seems to matter somehow.

paulkermann avatar Jul 21 '22 16:07 paulkermann

I have the exact same issue. My arm64 code is jumping to a block that is hooked with HOOK_BLOCK, I then handle it in my own code and set the X0 and PC so the arm64 code resumes. This used to work fine with Unicorn 1. Now, when I run the same code it fails to work properly and keeps calling the block, creating an infinite loop.

Then, I added a HOOK_CODE block in an attempt to debug it. To my confusion it started working after I added the debug hook.

I will attempt to create a small repro.

AeonLucid avatar Jul 23 '22 09:07 AeonLucid

Here is a repro of the issue.

from unicorn import *
from unicorn.arm64_const import *

# MOV x0, #1234
# MOV x1, #2345
# MOV x2, #4331
# LDR x9, =0x2000
# BLR x9
# MOV x8, x2
code_1 = '409A80D2212581D2621D82D2090084D220013FD6E80302AA'
addr_1 = 0x1000

# MOV x2, #9999
# RET
code_2 = 'E2E184D2C0035FD6'
addr_2 = 0x2000


def hook_code(uc_, address, size, user_data):
    print('Called hook_code')
    pass


def hook_block(uc_, address, size, user_data):
    print('Called hook_block')
    uc_.reg_write(UC_ARM64_REG_X2, 1337)
    uc_.reg_write(UC_ARM64_REG_PC, uc_.reg_read(UC_ARM64_REG_LR))


uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
uc.mem_map(addr_1, 0x1000)
uc.mem_map(addr_2, 0x2000)
uc.mem_write(addr_1, bytes.fromhex(code_1))
uc.mem_write(addr_2, bytes.fromhex(code_2))
# uc.hook_del(uc.hook_add(UC_HOOK_CODE, hook_code))
uc.hook_add(UC_HOOK_BLOCK, hook_block, user_data=None, begin=addr_2, end=addr_2 + 4)
uc.emu_start(addr_1, until=addr_1 + len(code_1) // 2)

print('x2 = 1337 (EXPECTED)')
print('x2 = %d (ACTUAL)' % uc.reg_read(UC_ARM64_REG_X2))

This code will infinite loop with the Unicorn 2.0.0 tag. If you uncomment uc.hook_del(uc.hook_add(UC_HOOK_CODE, hook_code)) it will work.

AeonLucid avatar Jul 23 '22 10:07 AeonLucid

Reverting commit https://github.com/unicorn-engine/unicorn/commit/b7bc13650c56ebaad47264c7c7cf5a5a72e25fd4 fixes the issue.

AeonLucid avatar Jul 23 '22 10:07 AeonLucid

Fixed in c4a0813

wtdcode avatar Aug 31 '22 15:08 wtdcode

@wtdcode The commit linked is a test case, where is the actual fix commited?

AeonLucid avatar Aug 31 '22 15:08 AeonLucid

092014a6cc85ee94b42646eed2db4a98012e1e31

wtdcode avatar Aug 31 '22 15:08 wtdcode