hook代码
Unicorn开始运行后,就可以通过代码的hook,查看具体的要执行的指令情况。
举例
官网代码
比如 官网测试代码 中的:
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
...
# tracing one instruction at ADDRESS with customized callback
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
其中的,此处的:
- hook代码的钩子参数叫:
UC_HOOK_CODE
- hook代码的函数名:默认的,典型的,都叫做:
hook_code
- hook_code内部的逻辑:此处只是简单的,打印出
- address: 当前PC的地址 -> 表示代码执行到哪里了
- size: 当前指令的字节大小
而此处只是用于演示,所以没有加更多复杂的逻辑。
下面介绍更加实例的,复杂的例子:
自己实例
以 模拟akd函数symbol2575 为例,其中的代码的hook部分是:
import re
from unicorn import *
from unicorn.arm64_const import *
from unicorn.arm_const import *
# only for debug
gNoUse = 0
#-------------------- Code --------------------
# memory address where emulation starts
CODE_ADDRESS = 0x10000
logging.info("CODE_ADDRESS=0x%X", CODE_ADDRESS)
# code size: 4MB
CODE_SIZE = 4 * 1024 * 1024
logging.info("CODE_SIZE=0x%X", CODE_SIZE)
CODE_ADDRESS_END = (CODE_ADDRESS + CODE_SIZE) # 0x00410000
logging.info("CODE_ADDRESS_END=0x%X", CODE_ADDRESS_END)
CODE_ADDRESS_REAL_END = CODE_ADDRESS + gCodeSizeReal
logging.info("CODE_ADDRESS_REAL_END=0x%X", CODE_ADDRESS_REAL_END)
# CODE_ADDRESS_REAL_LAST_LINE = CODE_ADDRESS_REAL_END - 4
# logging.info("CODE_ADDRESS_REAL_LAST_LINE=0x%X", CODE_ADDRESS_REAL_LAST_LINE)
...
# callback for tracing instructions
def hook_code(mu, address, size, user_data):
...
if pc == 0x12138:
spValue = mu.mem_read(sp)
logging.info("\tspValue=0x%X", spValue)
gNoUse = 1
if pc == 0x1213C:
gNoUse = 1
if pc == 0x118B4:
gNoUse = 1
if pc == 0x118B8:
gNoUse = 1
...
# tracing one instruction with customized callback
# mu.hook_add(UC_HOOK_CODE, hook_code, begin=CODE_ADDRESS, end=CODE_ADDRESS)
# mu.hook_add(UC_HOOK_CODE, hook_code, begin=CODE_ADDRESS, end=CODE_ADDRESS_REAL_END)
# mu.hook_add(UC_HOOK_CODE, hook_code, begin=CODE_ADDRESS, end=EMULATE_MALLOC_CODE_END)
mu.hook_add(UC_HOOK_CODE, hook_code, begin=CODE_ADDRESS, end=CODE_ADDRESS_END)
代码看起来,很多,很复杂。我们拆开来一点点解释:
添加代码hook
添加代码的hook,和官网示例中类似,都是:
mu.hook_add(UC_HOOK_CODE, hook_code, begin=CODE_ADDRESS, end=CODE_ADDRESS_END)
其中:
UC_HOOK_CODE
:是要添加的hook类型,此处表示要去hook的是code代码hook_code
:当执行到代码时,去调用到的(钩子)函数,即我们此处用于调试代码的函数begin=CODE_ADDRESS, end=CODE_ADDRESS_END
:表示触发hook的代码的范围,此处值得说一下这个细节:begin=CODE_ADDRESS
:表示对于最初的代码的起始地址,这个没啥特殊的end=CODE_ADDRESS_END
:这个值得重点解释一下- 第一层优化:官网示例代码的优化
- 对于官网示例,默认是:
end=ADDRESS
==代码起始地址 ->其最终的效果是:只触发了第一行的代码,其余代码都没触发此处的函数hook_code
- 所以我们此处要优化改进为:把范围放大到,真正的我们代码的范围
- 所以之前用了代码:
mu.hook_add(UC_HOOK_CODE, hook_code, begin=CODE_ADDRESS, end=CODE_ADDRESS_REAL_END)
,表示末尾处用的是end=CODE_ADDRESS_REAL_END
- 对于官网示例,默认是:
- 第二层优化:包含其他非当前函数的代码
- 不过此处真正的代码的范围,按理说应该是
end=CODE_ADDRESS_REAL_END
,但是此处没有采用,原因是:- 先贴出相关值
gCodeSizeReal=9416 == 0x24C8 CODE_ADDRESS=0x10000 CODE_SIZE=0x400000 CODE_ADDRESS_END=0x410000 CODE_ADDRESS_REAL_END=0x124C8
- 先贴出相关值
- 如果用
end=CODE_ADDRESS_REAL_END
==0x124C8
,则此处,的确是对于当前要模拟的函数的代码,是正常hook,都可以触发到hook_code
了,但是,后续对于, - 除了
CODE_ADDRESS
~CODE_ADDRESS_REAL_END
==0x10000
~0x124C8
之外, - 在
CODE_ADDRESS
~CODE_ADDRESS_END
==0x10000
~0x410000
之内, - 还有些额外的代码,是此处特殊的,要模拟的第三方函数,比如
malloc
等,也想要触发调试函数hook_code
- 如果设置了
end=CODE_ADDRESS_REAL_END
,则函数本身之外的其他额外代码,就无法触发hook_code
,导致无法调试内部细节了
- 不过此处真正的代码的范围,按理说应该是
- 第一层优化:官网示例代码的优化
其他细节
hook_code
中其他部分的代码,也很多,分别实现了各自的目的和效果,包括:
其他一些细节,还有:
- 当特定PC时,查看读取内存的值
if pc == 0x12138: spValue = mu.mem_read(sp) logging.info("\tspValue=0x%X", spValue)
- 当特定PC时,暂停运行,用于辅助调试,查看其他的值
if pc == 0x1213C: gNoUse = 1