使用 unicorn 和 capstone 库来模拟 ARM Thumb 指令的执行,并实现多个钩子(hook)来监控代码执行、系统调用和内存读写操作(二)
import unicorn
import capstone
import binascii
def printArm32Regs(mu):
for i in range(66,78):
print("R%d,value:%x"%(i-66,mu.reg_read(i)))
#typedef void (*uc_cb_hookcode_t)(uc_engine *uc, uint64_t address, uint32_t size, void *user_data);
def hook_code(mu,address,size,user_data):
code=mu.mem_read(address,size)
CP=capstone.Cs(capstone.CS_ARCH_ARM,capstone.CS_MODE_THUMB)
for i in CP.disasm(code,0,len(code)):
print("[addr:%x]:%s %s\n"%(address,i.mnemonic,i.op_str))
print("-----------------------------")
printArm32Regs(mu)
print("-----------------------------")
return
#typedef void (*uc_cb_hookintr_t)(uc_engine *uc, uint32_t intno, void *user_data);
def hook_syscall(mu,intno,user_data):
print("syscall num:0x%d is called!!"%intno)
if intno==2:
print("exit syscall is called!!")
print("-----------------------------")
printArm32Regs(mu)
print("-----------------------------")
return
#void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type,uint64_t address, int size, int64_t value, void *user_data);
def hook_mem_write_unmapped(mu,type,address,size,value,user_data):
if type==unicorn.UC_MEM_WRITE_UNMAPPED:
print("UC_HOOK_MEM_WRITE_UNMAPPED addr:0x%x,size:%d,value:0x%x"%(address,size,value))
mu.mem_map(0x0,0x1000)
print("hook_mem type:%d addr:0x%x,size:%d,value:0x%x" % (type, address, size, value))
return True
def hook_mem(mu,type,address,size,value,user_data):
if type==unicorn.UC_MEM_WRITE:
print("write addr:0x%x,size:%d,value:0x%x"%(address,size,value))
if type==unicorn.UC_MEM_READ:
print("read addr:0x%x,size:%d,value:0x%x"%(address,size,value))
print("hook_mem type:%d addr:0x%x,size:%d,value:0x%x"%(type,address,size,value))
return
def testthumb():
# text:00008ACA 0A 46 MOV R2, R1 ; Rd = Op2
# .text:00008ACC 03 46 MOV R3, R0 ; Rd = Op2
#.text:00008B04 04 92 STR R2, [SP,#0x40+var_30] ; Store to Memory
CODE=b'\x0a\x46\x03\x46\x04\x92\x4F\xF0\x0B\x07\x00\xdf'
CP=capstone.Cs(capstone.CS_ARCH_ARM,capstone.CS_MODE_THUMB)
for i in CP.disasm(CODE,0,len(CODE)):
print("[addr:%x]:%s %s\n"%(i.address,i.mnemonic,i.op_str))
mu=unicorn.Uc(unicorn.UC_ARCH_ARM,unicorn.UC_MODE_THUMB)
ADDRESS=0x1000
SIZE=1024
mu.mem_map(ADDRESS,SIZE)
mu.mem_write(ADDRESS,CODE)
bytes=mu.mem_read(ADDRESS,10)
print("ADDRESS:%x,content:%s"%(ADDRESS,binascii.b2a_hex(bytes)))
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0,0x100)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1,0x200)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R2,0x300)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R3,0x400)
hook_code1=mu.hook_add(unicorn.UC_HOOK_CODE, hook_code)
#mu.hook_del(hook_code1)
'''/*
Callback function for hooking memory (READ, WRITE & FETCH)
@type: this memory is being READ, or WRITE
@address: address where the code is being executed
@size: size of data being read or written
@value: value of data being written to memory, or irrelevant if type = READ.
@user_data: user data passed to tracing APIs
*/
typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type,
uint64_t address, int size, int64_t value, void *user_data);'''
#mu.hook_add(unicorn.UC_HOOK_MEM_WRITE,hook_mem)
mu.hook_add(unicorn.UC_HOOK_MEM_WRITE,hook_mem)
mu.hook_add(unicorn.UC_HOOK_INTR,hook_syscall)
mu.hook_add(unicorn.UC_HOOK_MEM_WRITE_UNMAPPED,hook_mem_write_unmapped)
try:
mu.emu_start(ADDRESS+1,ADDRESS+len(CODE))
print("emulat over")
printArm32Regs(mu)
bytes=mu.mem_read(0x10,4)
print("mem:0x%x,value:%s"%(0x10,binascii.b2a_hex(bytes)))
except unicorn.UcError as e:
print(e)
#def hook_add(self, htype, callback, user_data=None, begin=1, end=0, arg1=0):
return
if __name__ == '__main__':
testthumb()
运行结果图:
一:代码概述
使用 unicorn
和 capstone
库来模拟 ARM Thumb 指令的执行,并实现了多个钩子(hook)来监控代码执行、系统调用和内存读写操作。主要功能是加载一段机器码,执行这些指令,并在执行过程中打印出相关信息。
1.1代码结构
1. 导入库
import unicorn
import capstone
import binascii
unicorn
: 用于 CPU 模拟的库。capstone
: 用于反汇编机器码的库。binascii
: 用于处理二进制和 ASCII 之间的转换。
2. 打印寄存器值的函数
def printArm32Regs(mu):
for i in range(66, 78):
print("R%d,value:%x" % (i - 66, mu.reg_read(i)))
- 该函数接受一个
unicorn
模拟器实例mu
作为参数。 - 它循环读取 R0 到 R12(在 ARM 中,寄存器编号从 0 开始,R0 对应 66,R1 对应 67,以此类推),并打印出每个寄存器的值。
3. 钩子函数
-
代码钩子
def hook_code(mu, address, size, user_data): code = mu.mem_read(address, size) CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(code, 0, len(code)): print("[addr:%x]:%s %s\n" % (address, i.mnemonic, i.op_str)) print("-----------------------------") printArm32Regs(mu) print("-----------------------------") return
- 该函数在每次执行指令时被调用,读取当前执行的指令并反汇编,打印出指令的地址、助记符和操作数,以及当前寄存器的值。
-
系统调用钩子
def hook_syscall(mu, intno, user_data): print("syscall num:0x%d is called!!" % intno) if intno == 2: print("exit syscall is called!!") print("-----------------------------") printArm32Regs(mu) print("-----------------------------") return
- 该函数在系统调用发生时被调用,打印出系统调用的编号,并在特定的系统调用(如 exit)时提供额外信息。
-
未映射内存写入钩子
def hook_mem_write_unmapped(mu, type, address, size, value, user_data): if type == unicorn.UC_MEM_WRITE_UNMAPPED: print("UC_HOOK_MEM_WRITE_UNMAPPED addr:0x%x,size:%d,value:0x%x" % (address, size, value)) mu.mem_map(0x0, 0x1000) # 映射未映射的内存 print("hook_mem type:%d addr:0x%x,size:%d,value:0x%x" % (type, address, size, value)) return True
- 该函数在尝试写入未映射内存时被调用,打印出相关信息并映射一段内存。
-
内存读写钩子
def hook_mem(mu, type, address, size, value, user_data): if type == unicorn.UC_MEM_WRITE: print("write addr:0x%x,size:%d,value:0x%x" % (address, size, value)) if type == unicorn.UC_MEM_READ: print("read addr:0x%x,size:%d,value:0x%x" % (address, size, value)) print("hook_mem type:%d addr:0x%x,size:%d,value:0x%x" % (type, address, size, value)) return
- 该函数在内存读写操作时被调用,打印出读写的地址、大小和数值。
4. 主测试函数
def testthumb():
CODE = b'\x0a\x46\x03\x46\x04\x92\x4F\xF0\x0B\x07\x00\xdf'
CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB)
for i in CP.disasm(CODE, 0, len(CODE)):
print("[addr:%x]:%s %s\n" % (i.address, i.mnemonic, i.op_str))
mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB)
ADDRESS = 0x1000
SIZE = 1024
mu.mem_map(ADDRESS, SIZE)
mu.mem_write(ADDRESS, CODE)
bytes = mu.mem_read(ADDRESS, 10)
print("ADDRESS:%x,content:%s" % (ADDRESS, binascii.b2a_hex(bytes)))
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, 0x100)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0x200)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R2, 0x300)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R3, 0x400)
- 该函数定义了要执行的机器码
CODE
,并使用capstone
反汇编它。 - 创建一个 Unicorn 模拟器实例,映射内存并写入机器码。
- 初始化寄存器 R0 到 R3 的值。
5. 添加钩子
hook_code1 = mu.hook_add(unicorn.UC_HOOK_CODE, hook_code)
mu.hook_add(unicorn.UC_HOOK_MEM_WRITE, hook_mem)
mu.hook_add(unicorn.UC_HOOK_INTR, hook_syscall)
mu.hook_add(unicorn.UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_write_unmapped)
- 添加多个钩子,以便在执行代码、内存读写和系统调用时调用相应的钩子函数。
6. 执行指令
try:
mu.emu_start(ADDRESS + 1, ADDRESS + len(CODE))
print("emulat over")
printArm32Regs(mu)
bytes = mu.mem_read(0x10, 4)
print("mem:0x%x,value:%s" % (0x10, binascii.b2a_hex(bytes)))
except unicorn.UcError as e:
print(e)
- 启动模拟器,从
ADDRESS + 1
开始执行,直到ADDRESS + len(CODE)
。 - 执行完成后,打印寄存器的值和内存地址
0x10
的内容。
1.2总括
代码实现了一个简单的 ARM Thumb 指令模拟器,能够反汇编和执行指令,并通过钩子监控执行过程中的各种事件。展示了如何使用 unicorn
和 capstone
库进行低级别的系统模拟和调试。大家阔以学习借鉴一下。
二:钩子详解
下面是对你代码中添加的钩子的详细解释,包括每个钩子的功能、触发条件和使用场景。
1. 代码钩子 (hook_code
)
def hook_code(mu, address, size, user_data):
code = mu.mem_read(address, size)
CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB)
for i in CP.disasm(code, 0, len(code)):
print("[addr:%x]:%s %s\n" % (address, i.mnemonic, i.op_str))
print("-----------------------------")
printArm32Regs(mu)
print("-----------------------------")
return
功能
- 目的: 在每次执行指令时被调用,打印当前执行的指令及其相关信息。
- 操作:
- 从模拟器的内存中读取当前执行的指令。
- 使用
capstone
反汇编这些指令,并打印出指令的地址、助记符和操作数。 - 打印当前寄存器的值,以便观察指令执行前后的状态。
触发条件
- 每当模拟器执行一条指令时,都会触发这个钩子。
使用场景
- 适用于调试和分析代码执行过程,帮助开发者理解指令的执行顺序和寄存器状态变化。
2. 系统调用钩子 (hook_syscall
)
def hook_syscall(mu, intno, user_data):
print("syscall num:0x%d is called!!" % intno)
if intno == 2:
print("exit syscall is called!!")
print("-----------------------------")
printArm32Regs(mu)
print("-----------------------------")
return
功能
- 目的: 在系统调用发生时被调用,打印系统调用的编号和相关信息。
- 操作:
- 打印被调用的系统调用编号。
- 如果系统调用编号为 2(通常表示 exit),则打印特定信息。
- 打印当前寄存器的值。
触发条件
- 当模拟器执行系统调用指令时触发。
使用场景
- 适用于监控和调试系统调用的执行,帮助开发者理解程序与操作系统之间的交互。
3. 未映射内存写入钩子 (hook_mem_write_unmapped
)
def hook_mem_write_unmapped(mu, type, address, size, value, user_data):
if type == unicorn.UC_MEM_WRITE_UNMAPPED:
print("UC_HOOK_MEM_WRITE_UNMAPPED addr:0x%x,size:%d,value:0x%x" % (address, size, value))
mu.mem_map(0x0, 0x1000) # 映射未映射的内存
print("hook_mem type:%d addr:0x%x,size:%d,value:0x%x" % (type, address, size, value))
return True
功能
- 目的: 处理对未映射内存的写入操作,打印相关信息并映射内存。
- 操作:
- 检查写入操作是否为未映射内存。
- 如果是,打印地址、大小和写入的值,并映射一段内存(例如,0x0 地址的 0x1000 字节)。
- 打印所有内存操作的类型、地址、大小和数值。
触发条件
- 当模拟器尝试写入未映射的内存地址时触发。
使用场景
- 适用于处理和调试内存访问错误,确保程序在访问内存时不会崩溃。
4. 内存读写钩子 (hook_mem
)
def hook_mem(mu, type, address, size, value, user_data):
if type == unicorn.UC_MEM_WRITE:
print("write addr:0x%x,size:%d,value:0x%x" % (address, size, value))
if type == unicorn.UC_MEM_READ:
print("read addr:0x%x,size:%d,value:0x%x" % (address, size, value))
print("hook_mem type:%d addr:0x%x,size:%d,value:0x%x" % (type, address, size, value))
return
功能
- 目的: 监控内存的读写操作,打印相关信息。
- 操作:
- 根据操作类型(读或写)打印相应的信息,包括地址、大小和数值。
- 打印所有内存操作的类型、地址、大小和数值。
触发条件
- 当模拟器执行内存读或写操作时触发。
使用场景
- 适用于调试内存访问,帮助开发者理解程序如何与内存交互。
5.总结
这些钩子提供了对模拟器执行过程的深入监控,允许开发者在指令执行、系统调用和内存操作时获取详细信息。这对于调试、分析和理解程序的行为非常有帮助。通过这些钩子,开发者可以实时观察寄存器和内存的状态变化,从而更好地理解程序的执行流程。
原文地址:https://blog.csdn.net/SXXYNHHXX/article/details/143747850
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!