自学内容网 自学内容网

使用 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()

运行结果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一:代码概述

使用 unicorncapstone 库来模拟 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 指令模拟器,能够反汇编和执行指令,并通过钩子监控执行过程中的各种事件。展示了如何使用 unicorncapstone 库进行低级别的系统模拟和调试。大家阔以学习借鉴一下。

二:钩子详解

下面是对你代码中添加的钩子的详细解释,包括每个钩子的功能、触发条件和使用场景。

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)!