认识一下Unicorn

一:初步认识

Unicorn 是一个轻量级的、跨平台的 CPU 模拟器框架,旨在为开发者提供一个简单而强大的工具,用于模拟和执行机器代码。它是基于 QEMU 的一部分,专注于提供高性能的 CPU 模拟,支持多种架构和模式。下面我们对 Unicorn 进行简单介绍,包括其特点、架构、使用场景和基本用法。

1. 特点

  • 多架构支持: Unicorn 支持多种 CPU 架构,包括但不限于 x86、ARM、AArch64、MIPS、PowerPC 和 SPARC。这使得它适用于广泛的应用场景。

  • 高性能: Unicorn 设计为高效的模拟器,能够快速执行机器代码,适合需要高性能的应用,如动态分析和逆向工程。

  • 易于使用: Unicorn 提供了简单的 API,开发者可以轻松地集成到自己的项目中。它支持多种编程语言的绑定,包括 C、Python、Ruby 和 Go。

  • 灵活的内存管理: Unicorn 允许开发者自定义内存映射,可以模拟不同的内存布局和访问权限。

  • 钩子机制: Unicorn 提供了钩子(hook)机制,允许开发者在特定事件(如指令执行、内存读写、系统调用等)发生时插入自定义代码,以便进行监控和调试。

2. 架构

Unicorn 的架构主要由以下几个部分组成:

  • 核心模拟器: 负责执行机器代码,管理 CPU 状态和内存。

  • 内存管理: 提供灵活的内存映射和访问控制,允许开发者定义可读、可写和可执行的内存区域。

  • 钩子系统: 允许开发者在特定事件发生时插入自定义处理逻辑。

  • 多架构支持: 每种架构都有其特定的实现,确保模拟的准确性和性能。

3. 使用场景

Unicorn 适用于多种场景,包括但不限于:

  • 动态分析: 在安全研究中,Unicorn 可以用于动态分析恶意软件,帮助研究人员理解其行为。

  • 逆向工程: 开发者可以使用 Unicorn 模拟执行二进制文件,以便分析其逻辑和功能。

  • 漏洞研究: Unicorn 可以帮助研究人员模拟漏洞利用过程,测试和验证漏洞的影响。

  • 教育和培训: Unicorn 是学习计算机体系结构和操作系统的一个很好的工具,学生可以通过模拟器理解底层原理。

4. 基本用法

以下是使用 Unicorn 的基本步骤,展示如何设置和运行一个简单的模拟:

安装 Unicorn

在使用 Unicorn 之前,需要先安装它。可以通过源代码编译或使用包管理器(如 pip)安装 Python 绑定。

# 使用 pip 安装 Python 绑定
pip install unicorn

示例代码

以下是一个简单的示例,展示如何使用 Unicorn 模拟执行一段 ARM Thumb 机器码:

import unicorn
import unicorn.arm_const# 定义要执行的机器码
CODE = b'\x0a\x46\x03\x46\x04\x92\x4F\xF0\x0B\x07\x00\xdf'  # 示例 ARM Thumb 代码# 创建 Unicorn 模拟器实例
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)# 初始化寄存器
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, 0x100)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0x200)# 执行指令
mu.emu_start(ADDRESS, ADDRESS + len(CODE))# 打印寄存器值
print("R0: 0x%x" % mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0))
print("R1: 0x%x" % mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1))

代码解释

  1. 导入库: 导入 Unicorn 库和 ARM 常量。
  2. 定义机器码: 定义要执行的 ARM Thumb 机器码。
  3. 创建模拟器实例: 创建一个 Unicorn 模拟器实例,指定架构和模式。
  4. 映射内存: 映射一段内存区域以存放机器码。
  5. 写入机器码: 将机器码写入映射的内存区域。
  6. 初始化寄存器: 设置寄存器的初始值。
  7. 执行指令: 启动模拟器,从指定地址开始执行指令。
  8. 打印寄存器值: 执行完成后,打印寄存器的值。

二:寄存器常量命名规则

在使用 Unicorn 模拟器时,寄存器常量的命名规则通常遵循特定的约定,以便于开发者理解和使用。以下是一些常见的寄存器常量命名规则和示例,主要以 ARM 和 x86 架构为例。

1. ARM 架构寄存器常量

在 Unicorn 中,ARM 寄存器常量通常以 UC_ARM_REG_ 前缀开头,后面跟随寄存器的名称。

  • 通用寄存器:

    • UC_ARM_REG_R0UC_ARM_REG_R15: 表示 ARM 的通用寄存器 R0 到 R15。
  • 程序计数器:

    • UC_ARM_REG_PC: 表示程序计数器(Program Counter),通常是 R15。
  • 堆栈指针:

    • UC_ARM_REG_SP: 表示堆栈指针(Stack Pointer),通常是 R13。
  • 链接寄存器:

    • UC_ARM_REG_LR: 表示链接寄存器(Link Register),通常是 R14。
  • 状态寄存器:

    • UC_ARM_REG_CPSR: 表示当前程序状态寄存器(Current Program Status Register)。

2. x86 架构寄存器常量

在 Unicorn 中,x86 寄存器常量通常以 UC_X86_REG_ 前缀开头,后面跟随寄存器的名称。

  • 通用寄存器:

    • UC_X86_REG_EAX: 表示扩展累加寄存器 EAX。
    • UC_X86_REG_EBX: 表示扩展基寄存器 EBX。
    • UC_X86_REG_ECX: 表示扩展计数寄存器 ECX。
    • UC_X86_REG_EDX: 表示扩展数据寄存器 EDX。
  • 指针寄存器:

    • UC_X86_REG_ESP: 表示扩展堆栈指针寄存器 ESP。
    • UC_X86_REG_EBP: 表示扩展基指针寄存器 EBP。
  • 程序计数器:

    • UC_X86_REG_EIP: 表示扩展指令指针寄存器 EIP。
  • 状态寄存器:

    • UC_X86_REG_EFLAGS: 表示扩展标志寄存器 EFLAGS。

3. MIPS 架构寄存器常量

在 Unicorn 中,MIPS 寄存器常量通常以 UC_MIPS_REG_ 前缀开头,后面跟随寄存器的名称。

  • 通用寄存器:

    • UC_MIPS_REG_R0: 表示 MIPS 的 R0 寄存器。
    • UC_MIPS_REG_R1: 表示 MIPS 的 R1 寄存器。
    • UC_MIPS_REG_R31: 表示 MIPS 的 R31 寄存器(通常是返回地址寄存器)。
  • 程序计数器:

    • UC_MIPS_REG_PC: 表示程序计数器(Program Counter)。

4. 总结

寄存器常量的命名规则在不同的架构中略有不同,但通常遵循以下原则:

  • 前缀: 使用特定的前缀(如 UC_ARM_REG_UC_X86_REG_UC_MIPS_REG_)来标识寄存器的架构。
  • 寄存器名称: 使用寄存器的名称或编号,通常以大写字母表示。
  • 一致性: 保持命名的一致性,以便于开发者理解和使用。
    简化概况:
    UC_ + 指令集 + REG + 大写寄存器名
    UC_ARMREG + 大写寄存器名 (UC_ARM_REG_R0)
    UC_X86REG + 大写寄存器名 (UC_X86_REG_EAX)

三:unicorn中的异常处理类

在 Unicorn 模拟器中,异常处理类主要用于处理模拟过程中可能出现的错误和异常情况。Unicorn 是一个轻量级的多架构 CPU 模拟器,广泛用于安全研究、逆向工程和动态分析。虽然 Unicorn 本身并没有专门的异常处理类,但它提供了一些机制和方法来处理在模拟过程中可能发生的错误。

1. Unicorn 的异常处理机制

Unicorn 的异常处理主要通过返回值和错误码来实现。

  • 返回值: Unicorn 的许多 API 函数在发生错误时会返回特定的错误码。开发者需要检查这些返回值,以确定是否发生了错误。
  • 错误码: Unicorn 定义了一系列错误码,表示不同类型的错误,例如内存访问错误、无效的指令等。

2. 常见的错误码

以下是一些 Unicorn 中常见的错误码及其含义:

  • UC_ERR_OK: 表示没有错误。
  • UC_ERR_MEM_UNMAPPED: 表示访问了未映射的内存。
  • UC_ERR_INSN_INVALID: 表示无效的指令。
  • UC_ERR_MODE: 表示模式不匹配(例如,尝试在不支持的模式下执行指令)。

3. 使用示例

以下是一个使用 Unicorn 的示例,展示如何处理可能的异常情况:

import unicorn
from unicorn import *
from unicorn.x86_const import *# 模拟器初始化
mu = Uc(UC_ARCH_X86, UC_MODE_64)# 分配内存
ADDRESS = 0x1000
mu.mem_map(ADDRESS, 2 * 1024)  # 映射 2KB 内存# 写入代码
CODE = b"\x90\x90"  # NOP 指令
mu.mem_write(ADDRESS, CODE)# 设置程序计数器
mu.reg_write(UC_X86_REG_RIP, ADDRESS)try:# 开始模拟mu.emu_start(ADDRESS, ADDRESS + len(CODE))
except UcError as e:print("Unicorn error: {}".format(e))# 检查返回值
if mu.errno != UC_ERR_OK:print("Error occurred: {}".format(mu.errno))

4. 自定义异常处理

虽然 Unicorn 本身没有专门的异常处理类,但开发者可以创建自定义异常类来处理 Unicorn 模拟中的错误。可以帮助更好地管理错误并提供更清晰的错误信息。

示例:

class UnicornException(Exception):"""自定义异常类,用于处理 Unicorn 模拟中的错误"""def __init__(self, message, error_code):super().__init__(message)self.error_code = error_code# 使用自定义异常处理
try:mu.emu_start(ADDRESS, ADDRESS + len(CODE))
except UcError as e:raise UnicornException("Unicorn error occurred", e.errno)# 捕获自定义异常
try:# 可能引发异常的代码mu.emu_start(ADDRESS, ADDRESS + len(CODE))
except UnicornException as ue:print("Caught custom exception: {} with error code: {}".format(ue, ue.error_code))

四:Unicorn中的接口(包括python接口)

1.内存相关

在 Unicorn 模拟器中,内存管理是一个重要的功能,涉及到内存的映射、读取和写入。以下是对 uc_mem_mapuc_mem_readuc_mem_write 函数的介绍。

1. uc_mem_map

函数原型:

uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perm);

用途:
uc_mem_map 用于在模拟器中映射一段内存。映射的内存区域可以用于存储代码、数据等。

参数:

  • uc_engine *uc: 指向 Unicorn 引擎的指针。
  • uint64_t address: 要映射的内存起始地址。
  • size_t size: 要映射的内存大小(以字节为单位)。
  • uint32_t perm: 内存权限标志,通常是以下组合:
    • UC_MEM_WRITE: 可写
    • UC_MEM_READ: 可读
    • UC_MEM_EXEC: 可执行

示例:

uc_err err;
err = uc_mem_map(uc, 0x1000, 0x2000, UC_MEM_READ | UC_MEM_WRITE | UC_MEM_EXEC);
if (err != UC_ERR_OK) {printf("Failed to map memory: %s\n", uc_strerror(err));
}

2. uc_mem_read

函数原型:

uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *buf, size_t size);

用途:
uc_mem_read 用于从模拟器的内存中读取数据。

参数:

  • uc_engine *uc: 指向 Unicorn 引擎的指针。
  • uint64_t address: 要读取的内存地址。
  • void *buf: 指向存储读取数据的缓冲区。
  • size_t size: 要读取的字节数。

示例:

uint8_t buffer[4];
uc_err err;
err = uc_mem_read(uc, 0x1000, buffer, sizeof(buffer));
if (err != UC_ERR_OK) {printf("Failed to read memory: %s\n", uc_strerror(err));
} else {printf("Read data: %02x %02x %02x %02x\n", buffer[0], buffer[1], buffer[2], buffer[3]);
}

3. uc_mem_write

函数原型:

uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *buf, size_t size);

用途:
uc_mem_write 用于向模拟器的内存中写入数据。

参数:

  • uc_engine *uc: 指向 Unicorn 引擎的指针。
  • uint64_t address: 要写入的内存地址。
  • const void *buf: 指向要写入数据的缓冲区。
  • size_t size: 要写入的字节数。

示例:

uint8_t data[4] = {0x90, 0x90, 0x90, 0x90}; // NOP 指令
uc_err err;
err = uc_mem_write(uc, 0x1000, data, sizeof(data));
if (err != UC_ERR_OK) {printf("Failed to write memory: %s\n", uc_strerror(err));
}

4. 总结

  • uc_mem_map: 用于在 Unicorn 模拟器中映射一段内存,设置内存的权限。
  • uc_mem_read: 用于从映射的内存中读取数据。
  • uc_mem_write: 用于向映射的内存中写入数据。

python中的接口

在 Unicorn 模拟器的 Python 接口中,mu.mem_mapmu.mem_readmu.mem_write 是用于内存管理的关键函数。

1. mu.mem_map

函数原型:

mu.mem_map(address, size, perm)

用途:
mu.mem_map 用于在模拟器中映射一段内存。映射的内存区域可以用于存储代码、数据等。

参数:

  • address: 要映射的内存起始地址(整数)。
  • size: 要映射的内存大小(以字节为单位,整数)。
  • perm: 内存权限标志,可以是以下组合(可省略):
    • UC_MEM_READ: 可读
    • UC_MEM_WRITE: 可写
    • UC_MEM_EXEC: 可执行

示例:

from unicorn import Uc, UC_ARCH_X86, UC_MODE_64, UC_MEM_READ, UC_MEM_WRITE, UC_MEM_EXEC# 初始化 Unicorn 模拟器
mu = Uc(UC_ARCH_X86, UC_MODE_64)# 映射一段内存
ADDRESS = 0x1000
SIZE = 0x2000  # 8KB
mu.mem_map(ADDRESS, SIZE, UC_MEM_READ | UC_MEM_WRITE | UC_MEM_EXEC)

2. mu.mem_read

函数原型:

mu.mem_read(address, size)

用途:
mu.mem_read 用于从模拟器的内存中读取数据。

参数:

  • address: 要读取的内存地址(整数)。
  • size: 要读取的字节数(整数)。

返回值:
返回读取的数据,类型为字节串(bytes)。

示例:

# 从映射的内存中读取数据
data = mu.mem_read(ADDRESS, 4)  # 读取4字节
print(f"Read data: {data.hex()}")

3. mu.mem_write

函数原型:

mu.mem_write(address, data)

用途:
mu.mem_write 用于向模拟器的内存中写入数据。

参数:

  • address: 要写入的内存地址(整数)。
  • data: 要写入的数据,类型为字节串(bytes)。

示例:

# 向映射的内存中写入数据
data_to_write = b'\x90\x90\x90\x90'  # NOP 指令
mu.mem_write(ADDRESS, data_to_write)
print("Data written to memory.")

4. 总结

  • mu.mem_map: 用于在 Unicorn 模拟器中映射一段内存,设置内存的权限。
  • mu.mem_read: 用于从映射的内存中读取数据,返回读取的字节串。
  • mu.mem_write: 用于向映射的内存中写入数据,接受字节串作为输入。

2.寄存器相关

在 Unicorn 模拟器中,uc_reg_readuc_reg_write 是用于读取和写入寄存器的函数,而在 Python 接口中,mu.reg_readmu.reg_write 提供了相同的功能。

1. uc_reg_read (C API)

函数原型:

uc_err uc_reg_read(uc_engine *uc, int reg, uint64_t *value);

用途:
uc_reg_read 用于从模拟器中读取指定寄存器的值。

参数:

  • uc_engine *uc: 指向 Unicorn 引擎的指针。
  • int reg: 要读取的寄存器的标识符(例如,UC_X86_REG_EAX)。
  • uint64_t *value: 指向存储寄存器值的变量的指针。

返回值:
返回错误代码,成功时为 UC_ERR_OK

示例:

uint64_t eax_value;
uc_err err = uc_reg_read(uc, UC_X86_REG_EAX, &eax_value);
if (err != UC_ERR_OK) {printf("Failed to read EAX register: %s\n", uc_strerror(err));
} else {printf("EAX: %llu\n", eax_value);
}

2. uc_reg_write (C API)

函数原型:

uc_err uc_reg_write(uc_engine *uc, int reg, uint64_t value);

用途:
uc_reg_write 用于向模拟器中写入指定寄存器的值。

参数:

  • uc_engine *uc: 指向 Unicorn 引擎的指针。
  • int reg: 要写入的寄存器的标识符。
  • uint64_t value: 要写入的值。

返回值:
返回错误代码,成功时为 UC_ERR_OK

示例:

uc_err err = uc_reg_write(uc, UC_X86_REG_EAX, 0x12345678);
if (err != UC_ERR_OK) {printf("Failed to write EAX register: %s\n", uc_strerror(err));
}

3. mu.reg_read (Python API)

函数原型:

value = mu.reg_read(reg)

用途:
mu.reg_read 用于从模拟器中读取指定寄存器的值。

参数:

  • reg: 要读取的寄存器的标识符(例如,UC_X86_REG_EAX)。

返回值:
返回寄存器的值(整数)。

示例:

from unicorn import Uc, UC_ARCH_X86, UC_MODE_64, UC_X86_REG_EAX# 初始化 Unicorn 模拟器
mu = Uc(UC_ARCH_X86, UC_MODE_64)# 读取 EAX 寄存器的值
eax_value = mu.reg_read(UC_X86_REG_EAX)
print(f"EAX: {eax_value}")

4. mu.reg_write (Python API)

函数原型:

mu.reg_write(reg, value)

用途:
mu.reg_write 用于向模拟器中写入指定寄存器的值。

参数:

  • reg: 要写入的寄存器的标识符。
  • value: 要写入的值(整数)。

示例:

# 向 EAX 寄存器写入值
mu.reg_write(UC_X86_REG_EAX, 0x12345678)
print("EAX register updated.")

5. 总结

  • uc_reg_read: C API,用于读取指定寄存器的值。
  • uc_reg_write: C API,用于向指定寄存器写入值。
  • mu.reg_read: Python API,用于读取指定寄存器的值,返回整数。
  • mu.reg_write: Python API,用于向指定寄存器写入值,接受整数作为输入。

3.指令执行类

在 Unicorn 模拟器中,指令执行类的钩子(hooks)允许开发者监控和控制指令的执行过程。这些钩子可以帮助开发者实现调试、分析和控制程序的执行流。以下是对指令执行钩子的详细介绍,包括它们的用途、参数、Python 中的对应类和示例。

1. UC_HOOK_INTR

  • 用途: 在处理器接收到中断时触发。可以用于监控中断的发生和处理。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • intno: 中断号,表示触发的中断类型。
    • user_data: 用户自定义数据,可以在添加钩子时传递。
  • Python 示例:
    def hook_intr(uc, intno, user_data):print(f"Interrupt received: {intno}")mu.hook_add(UC_HOOK_INTR, hook_intr)
    

2. UC_HOOK_INSN

  • 用途: 在每条指令执行之前触发。可以用于监控指令的执行,进行调试或分析。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • address: 当前执行的指令地址。
    • size: 指令的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_insn(uc, address, size, user_data):print(f"Instruction executed at address: 0x{address:x}, size: {size}")mu.hook_add(UC_HOOK_INSN, hook_insn)
    

3. UC_HOOK_CODE

  • 用途: 在执行代码块时触发。可以用于监控代码块的执行情况。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • address: 代码块的起始地址。
    • size: 代码块的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_code(uc, address, size, user_data):print(f"Code block executed at address: 0x{address:x}, size: {size}")mu.hook_add(UC_HOOK_CODE, hook_code)
    

4. UC_HOOK_BLOCK

  • 用途: 在执行代码块(基本块)时触发。可以用于监控基本块的执行情况,通常用于分析控制流。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • address: 基本块的起始地址。
    • size: 基本块的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_block(uc, address, size, user_data):print(f"Basic block executed at address: 0x{address:x}, size: {size}")mu.hook_add(UC_HOOK_BLOCK, hook_block)
    

5.mu.hook_add 方法

方法原型:

mu.hook_add(hook_type, callback, begin=0, end=0)

参数:

  • hook_type: 指定钩子的类型,可以是以下之一:

    • UC_HOOK_INTR: 中断钩子。
    • UC_HOOK_INSN: 指令钩子。
    • UC_HOOK_CODE: 代码块钩子。
    • UC_HOOK_BLOCK: 基本块钩子。
  • callback: 当钩子触发时调用的回调函数。该函数应接受相应的参数,具体取决于钩子的类型。

  • begin: 可选参数,指定钩子监控的起始地址(适用于 UC_HOOK_CODEUC_HOOK_BLOCK)。

  • end: 可选参数,指定钩子监控的结束地址(适用于 UC_HOOK_CODEUC_HOOK_BLOCK)。

例如:mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
1. 添加指令钩子
from unicorn import Uc, UC_ARCH_X86, UC_MODE_64, UC_HOOK_INSN, UC_X86_REG_EAX# 初始化 Unicorn 模拟器
mu = Uc(UC_ARCH_X86, UC_MODE_64)# 定义指令钩子的回调函数
def hook_insn(uc, address, size, user_data):print(f"Instruction executed at address: 0x{address:x}, size: {size}")# 添加指令钩子
mu.hook_add(UC_HOOK_INSN, hook_insn)# 这里可以添加代码以执行模拟
2. 添加代码块钩子
# 定义代码块钩子的回调函数
def hook_code(uc, address, size, user_data):print(f"Code executed at address: 0x{address:x}, size: {size}")# 添加代码块钩子,监控特定地址范围
mu.hook_add(UC_HOOK_CODE, hook_code, begin=0x1000, end=0x2000)
3. 添加中断钩子
# 定义中断钩子的回调函数
def hook_intr(uc, intr_idx, user_data):print(f"Interrupt occurred: {intr_idx}")# 添加中断钩子
mu.hook_add(UC_HOOK_INTR, hook_intr)
4. 添加基本块钩子
# 定义基本块钩子的回调函数
def hook_block(uc, address, user_data):print(f"Basic block executed at address: 0x{address:x}")# 添加基本块钩子
mu.hook_add(UC_HOOK_BLOCK, hook_block)

4.内存访问类

在 Unicorn 模拟器中,内存访问钩子(hooks)允许开发者监控和控制内存的读写操作。每个钩子都有特定的用途和参数。

1. UC_HOOK_MEM_READ

  • 用途: 在内存读取操作时触发。可以用于监控何时读取了特定内存地址的内容。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • access: 访问类型(通常为 UC_MEM_READ)。
    • address: 被读取的内存地址。
    • size: 读取的字节数。
    • user_data: 用户自定义数据,可以在添加钩子时传递。
  • Python 示例:
    def hook_mem_read(uc, access, address, size, user_data):print(f"Memory read at address: 0x{address:x}, size: {size}")mu.hook_add(UC_HOOK_MEM_READ, hook_mem_read)
    

2. UC_HOOK_MEM_WRITE

  • 用途: 在内存写入操作时触发。可以用于监控何时向特定内存地址写入数据。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • access: 访问类型(通常为 UC_MEM_WRITE)。
    • address: 被写入的内存地址。
    • size: 写入的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_mem_write(uc, access, address, size, user_data):print(f"Memory write at address: 0x{address:x}, size: {size}")mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write)
    

3. UC_HOOK_MEM_FETCH

  • 用途: 在内存取指令时触发。用于监控指令从内存中获取的情况。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • access: 访问类型(通常为 UC_MEM_FETCH)。
    • address: 被取指令的内存地址。
    • size: 取指令的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_mem_fetch(uc, access, address, size, user_data):print(f"Memory fetch at address: 0x{address:x}, size: {size}")mu.hook_add(UC_HOOK_MEM_FETCH, hook_mem_fetch)
    

4. UC_HOOK_MEM_READ_AFTER

  • 用途: 在内存读取操作之后触发。可以用于在读取操作完成后执行某些逻辑。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • access: 访问类型(通常为 UC_MEM_READ)。
    • address: 被读取的内存地址。
    • size: 读取的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_mem_read_after(uc, access, address, size, user_data):print(f"Memory read completed at address: 0x{address:x}, size: {size}")mu.hook_add(UC_HOOK_MEM_READ_AFTER, hook_mem_read_after)
    

5. UC_HOOK_MEM_PROT

  • 用途: 在内存保护操作时触发。用于监控内存保护的设置和更改。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • access: 访问类型(通常为 UC_MEM_PROT)。
    • address: 被保护的内存地址。
    • size: 保护的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_mem_prot(uc, access, address, size, user_data):print(f"Memory protection change at address: 0x{address:x}, size: {size}")mu.hook_add(UC_HOOK_MEM_PROT, hook_mem_prot)
    

6. UC_HOOK_MEM_FETCH_INVALID

  • 用途: 在尝试从无效内存地址获取指令时触发。用于处理无效内存访问的情况。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • access: 访问类型(通常为 UC_MEM_FETCH_INVALID)。
    • address: 尝试获取指令的无效内存地址。
    • size: 取指令的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_mem_fetch_invalid(uc, access, address, size, user_data):print(f"Invalid memory fetch attempt at address: 0x{address:x}")mu.hook_add(UC_HOOK_MEM_FETCH_INVALID, hook_mem_fetch_invalid)
    

7. UC_HOOK_MEM_INVALID

  • 用途: 在访问无效内存地址时触发。用于处理无效内存访问的情况。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • access: 访问类型(通常为 UC_MEM_INVALID)。
    • address: 被访问的无效内存地址。
    • size: 访问的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_mem_invalid(uc, access, address, size, user_data):print(f"Invalid memory access at address: 0x{address:x}")mu.hook_add(UC_HOOK_MEM_INVALID, hook_mem_invalid)
    

8. UC_HOOK_MEM_VALID

  • 用途: 在访问有效内存地址时触发。用于确认内存访问的有效性。
  • 参数:
    • uc: 当前的 Unicorn 模拟器实例。
    • access: 访问类型(通常为 UC_MEM_VALID)。
    • address: 被访问的有效内存地址。
    • size: 访问的字节数。
    • user_data: 用户自定义数据。
  • Python 示例:
    def hook_mem_valid(uc, access, address, size, user_data):print(f"Valid memory access at address: 0x{address:x}")mu.hook_add(UC_HOOK_MEM_VALID, hook_mem_valid)
    

五:完结,看一下完整的代码叭

from unicorn import *
from unicorn.arm_const import *
ARM_CODE   = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0"
# mov r0, #0x37;
# sub r1, r2, r3
# Test ARM# callback for tracing instructions
def hook_code(uc, address, size, user_data):print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))def test_arm():print("Emulate ARM code")try:# Initialize emulator in ARM modemu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)# map 2MB memory for this emulationADDRESS = 0x10000mu.mem_map(ADDRESS, 2 * 0x10000)mu.mem_write(ADDRESS, ARM_CODE)mu.reg_write(UC_ARM_REG_R0, 0x1234)mu.reg_write(UC_ARM_REG_R2, 0x6789)mu.reg_write(UC_ARM_REG_R3, 0x3333)mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)# emulate machine code in infinite timemu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))r0 = mu.reg_read(UC_ARM_REG_R0)r1 = mu.reg_read(UC_ARM_REG_R1)print(">>> R0 = 0x%x" % r0)print(">>> R1 = 0x%x" % r1)except UcError as e:print("ERROR: %s" % e)

代码解释,看看都能不能看懂了

代码解释

  1. 导入库:

    from unicorn import *
    from unicorn.arm_const import *
    

    这两行代码导入了 Unicorn 模拟器的核心库和 ARM 架构的常量。

  2. 定义 ARM 代码:

    ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0"
    

    这里定义了一段 ARM 机器码,包含两个指令:

    • mov r0, #0x37:将立即数 0x37 移动到寄存器 r0
    • sub r1, r2, r3:将 r2r3 的值相减,并将结果存储到 r1
  3. 钩子函数:

    def hook_code(uc, address, size, user_data):print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
    

    这个回调函数在每次执行指令时被调用,打印出当前指令的地址和大小。

  4. 主测试函数:

    def test_arm():print("Emulate ARM code")try:# Initialize emulator in ARM modemu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
    

    这里初始化了一个 Unicorn 模拟器实例,设置为 ARM 架构和 Thumb 模式。

  5. 内存映射和写入:

            ADDRESS = 0x10000mu.mem_map(ADDRESS, 2 * 0x10000)mu.mem_write(ADDRESS, ARM_CODE)
    

    映射了 2MB 的内存,并将 ARM 代码写入指定的内存地址。

  6. 寄存器初始化:

            mu.reg_write(UC_ARM_REG_R0, 0x1234)mu.reg_write(UC_ARM_REG_R2, 0x6789)mu.reg_write(UC_ARM_REG_R3, 0x3333)
    

    初始化寄存器 r0, r2, 和 r3 的值。

  7. 添加钩子:

            mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
    

    添加代码钩子,以便在执行代码时调用 hook_code 函数。

  8. 开始模拟:

            mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))
    

    开始模拟执行 ARM 代码。

  9. 读取寄存器值:

            r0 = mu.reg_read(UC_ARM_REG_R0)r1 = mu.reg_read(UC_ARM_REG_R1)print(">>> R0 = 0x%x" % r0)print(">>> R1 = 0x%x" % r1)
    

    模拟结束后,读取并打印寄存器 r0r1 的值。

  10. 异常处理:

       except UcError as e:print("ERROR: %s" % e)
    

代码修改

为了确保代码能够正确运行,以下是一些必要的修改:

  1. 钩子范围: 在添加钩子时,beginend 参数应设置为代码的起始和结束地址。
  2. 寄存器值: 在执行 sub 指令之前,确保 r2r3 的值已正确设置。

更新后的代码

from unicorn import *
from unicorn.arm_const import *ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0"  # mov r0, #0x37; sub r1, r2, r3# callback for tracing instructions
def hook_code(uc, address, size, user_data):print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))def test_arm():print("Emulate ARM code")try:# Initialize emulator in ARM modemu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)# map 2MB memory for this emulationADDRESS = 0x10000mu.mem_map(ADDRESS, 2 * 0x10000)mu.mem_write(ADDRESS, ARM_CODE)# Initialize registersmu.reg_write(UC_ARM_REG_R0, 0x1234)  # r0 will be set to 0x37mu.reg_write(UC_ARM_REG_R2, 0x6789)  # r2 value for subtractionmu.reg_write(UC_ARM_REG_R3, 0x3333)  # r3 value for subtraction# Add hook for the code rangemu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS + len(ARM_CODE))# emulate machine codemu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))# Read back the values of registersr0 = mu.reg_read(UC_ARM_REG_R0)r1 = mu.reg_read(UC_ARM_REG_R1)print(">>> R0 = 0x%x" % r0)print(">>> R1 = 0x%x" % r1)except UcError as e:print("ERROR: %s" % e)# Run the test
test_arm()

运行结果

运行此代码后,将看到每条指令的跟踪输出,以及最终寄存器 r0r1 的值。r0 应该是 0x37,而 r1 将是 0x6789 - 0x3333 的结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/471154.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C 语言 【模拟实现内存库函数】

1、memcpy memcpy函数是C/C语言中的一个用于内存复制的函数,声明在 string.h 中(C是 cstring)。其原型是: void * memcpy ( void * destination, const void * source, size_t num ); 其中,destination表示的是要拷贝…

【大数据学习 | flume】flume的概述与组件的介绍

1. flume概述 Flume是cloudera(CDH版本的hadoop) 开发的一个分布式、可靠、高可用的海量日志收集系统。它将各个服务器中的数据收集起来并送到指定的地方去,比如说送到HDFS、Hbase,简单来说flume就是收集日志的。 Flume两个版本区别: ​ 1&…

01:(手撸HAL+CubeMX)时钟篇

(手撸HALCubeMX)时钟篇 1、对SystemInit函数的分析2、使用HSI将总线时钟配置为最高频率3、使用HSE将总线时钟配置为最高频率4、使用Cube配置时钟树的参数5、对HAL_Init函数分析6、对系统定时器中断服务函数分析 有关时钟树和上电/复位的基础知识请参考“…

大数据新视界 -- 大数据大厂之 Impala 存储格式转换:从原理到实践,开启大数据性能优化星际之旅(下)(20/30)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Spring Boot实现文件上传与OSS集成:从基础到应用

目录 前言1. 文件上传的基础实现1.1 前端文件上传请求1.2 后端文件接收与保存 2. 集成第三方OSS服务2.1 准备工作2.2 编写OSS集成代码2.3 修改Controller实现文件上传至OSS 3. 文件上传的扩展:多文件上传与权限控制结语 前言 随着互联网应用的快速发展,…

微服务各组件整合

nacos 第一步&#xff0c;引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency> 第二步&#xff0c;增加配置 spring:application:name: …

机器学习总结

机器学习按照模型类型分为监督学习模型&#xff0c;无监督学习模型和概率模型三大类&#xff1a; 下图是机器学习笔记思维导图&#xff0c;&#xff1a; 一.什么是机器学习 从本质上讲&#xff0c;可以认为机器学习就是在数据中寻找一种合适的函数来描述输入与输出之间的关系。…

WEB攻防-通用漏洞SQL注入sqlmapOracleMongodbDB2等

SQL注入课程体系&#xff1a; 1、数据库注入-access mysql mssql oracle mongodb postgresql 2、数据类型注入-数字型 字符型 搜索型 加密型&#xff08;base64 json等&#xff09; 3、提交方式注入-get post cookie http头等 4、查询方式注入-查询 增加 删除 更新 堆叠等 …

三、损失函数

损失函数 前言一、分类问题的损失函数1.1 二分类损失函数1.1.1 数学定义1.1.2 函数解释&#xff1a;1.1.3 性质1.1.4 计算演示1.1.5 代码演示 1.2 多分类损失函数1.1.1 数学定义1.1.2 性质与特点1.1.3 计算演示1.1.4 代码演示 二、回归问题的损失函数2.1 MAE损失2.2 MSE损失2.3…

PNG图片批量压缩exe工具+功能纯净+不改变原始尺寸

小编最近有一篇png图片要批量压缩&#xff0c;大小都在5MB之上&#xff0c;在网上找了半天要么就是有广告&#xff0c;要么就是有毒&#xff0c;要么就是功能复杂&#xff0c;整的我心烦意乱。 于是我自己用python写了一个纯净工具&#xff0c;只能压缩png图片&#xff0c;没任…

测试工程师简历「精选篇」

【#测试工程师简历#】一份专业且引人注目的测试工程师简历&#xff0c;无疑是你敲开理想职位大门的金钥匙。那么&#xff0c;如何撰写一份既体现技术水平又彰显个人特色的简历呢&#xff1f;以下是幻主简历网整理的测试工程师简历「程序员篇」&#xff0c;欢迎大家阅读收藏&…

git下载慢下载不了?Git国内国外下载地址镜像,git安装视频教程

git安装下载的视频教程在这 3分钟完成git下载和安装&#xff0c;git国内外下载地址镜像&#xff0c;Windows为例_哔哩哔哩_bilibili 一、Git安装包国内和国外下载地址镜像 1.1国外官方下载地址 打开Git的官方网站&#xff1a;Git官网下载页面。在页面上选择对应的系统&…

专题十八_动态规划_斐波那契数列模型_路径问题_算法专题详细总结

目录 动态规划 动态规范五步走&#xff1a; 1. 第 N 个泰波那契数&#xff08;easy&#xff09; 解析&#xff1a; 1.状态表达式&#xff1a; 2.状态转移方程&#xff1a; 3.初始化&#xff1a; 4.填表顺序&#xff1a; 5.返回值 编写代码&#xff1a; 总结&#xff…

阿里云centos7.9服务器磁盘挂载,切换服务路径

项目背景 1、项目使用的服务器为阿里云centos7.9&#xff0c;默认的磁盘为vda&#xff0c;文件系统挂载在这个磁盘上&#xff0c;项目上使用的文件夹为/home/hnst/uploadPath 2、vda使用率已达到91% 3、现购置一块新的磁盘为vdb&#xff0c;大小为2T 目的 切换服务所使用的…

STM32问题集

这里写目录标题 一、烧录1、 Can not connect to target!【ST-LINK烧录】 一、烧录 1、 Can not connect to target!【ST-LINK烧录】 烧录突然 If the target is in low power mode, please enable “Debug in Low Power mode” option from Target->settings menu 然后就&…

Scala学习记录,case class,迭代器

case class case class创建的对象的属性是不可改的 创建对象&#xff0c;可以不用写new 自动重写&#xff1a;toString, equals, hashCode, copy 自动重写方法&#xff1a;toString,equals,hashCode,copy 小习一下 1.case class 的定义语法是什么 基本形式&#xff1a;case …

成都睿明智科技有限公司解锁抖音电商新玩法

在这个短视频风起云涌的时代&#xff0c;抖音电商以其独特的魅力迅速崛起&#xff0c;成为众多商家争夺的流量高地。而在这片充满机遇与挑战的蓝海中&#xff0c;成都睿明智科技有限公司犹如一颗璀璨的新星&#xff0c;以其专业的抖音电商服务&#xff0c;助力无数品牌实现从零…

阅读2020-2023年《国外军用无人机装备技术发展综述》笔记_技术趋势

目录 文献基本信息 序言 1 发展概况 2 重点技术发展 2.1 人工智能技术 2.1.1 应用深化 2.1.2 作战效能提升 2.2 航空技术 2.2.1螺旋桨设计创新 2.2.2 发射回收技术进步 2.3 其他相关技术 2.3.1 远程控制技术探 2.3.2 云地控制平台应用 3 装备系统进展 3.1 无人作…

LeetCode 86.分隔链表

题目&#xff1a; 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 思路&#xff1a; 代码&#xff1a; /*** Definiti…

SystemVerilog学习笔记(六):控制流

条件语句 条件语句用于检查块中的语句是否被执行。条件语句创建语句块。如果给出的表达式是 true&#xff0c;执行块中的语句集&#xff0c;如果表达式为 false&#xff0c;则 else 块语句将最后执行。 序号条件语句1.if2.if-else3.if-else ladder4.unique if5.unique0 if6.p…