Unicorn-CPU模拟框架数据类型及API分析与示例(一)
之前对Capstone反汇编引擎的API分析文档(https://xz.aliyun.com/t/5753)已经被[官方](http://www.capstone-engine.org/documentation.html)收录 https://github.com/kabeor/Micro-Capstone-Engine-API-Documentation ,在实现自己想要做出的调试器的路上,又遇到了与Capstone同作者的国外大佬aquynh的另一个著名项目Unicorn,不巧的是,详尽的API文档仍然较少,更多的是大篇幅的代码,因此决定继续分析Unicorn框架,包括数据类型,已开放API及其实现。
Unicorn是一个轻量级, 多平台, 多架构的CPU模拟器框架,基于qemu开发,它可以代替CPU模拟代码的执行,常用于恶意代码分析,Fuzz等,该项目被用于Radare2逆向分析框架,GEF(gdb的pwn分析插件),Pwndbg,Angr符号执行框架等多个著名项目。接下来我也将通过阅读源码和代码实际调用来写一个简单的非官方版本的API手册。
个人博客: kabeor.cn
0x0 开发准备
Unicorn官网: http://www.unicorn-engine.org
自行编译lib和dll方法
源码: https://github.com/unicorn-engine/unicorn/archive/master.zip
下载后解压
文件结构如下:
. <- 主要引擎core engine + README + 编译文档COMPILE.TXT 等
├── arch <- 各语言反编译支持的代码实现
├── bindings <- 中间件
│ ├── dotnet <- .Net 中间件 + 测试代码
│ ├── go <- go 中间件 + 测试代码
│ ├── haskell <- Haskell 中间件 + 测试代码
│ ├── java <- Java 中间件 + 测试代码
│ ├── pascal <- Pascal 中间件 + 测试代码
│ ├── python <- Python 中间件 + 测试代码
│ ├── ruby <- Ruby 中间件 + 测试代码
│ └── vb6 <- VB6 中间件 + 测试代码
├── docs <- 文档,主要是Unicorn的实现思路
├── include <- C头文件
├── msvc <- Microsoft Visual Studio 支持(Windows)
├── qemu <- qemu框架源码
├── samples <- Unicorn使用示例
└── tests <- C语言测试用例
下面演示Windows10使用Visual Studio2019编译
打开msvc文件夹,内部结构如下
VS打开unicorn.sln项目文件,解决方案自动载入这些
如果都需要的话,直接编译就好了,只需要其中几种,则右键解决方案->属性->配置属性 如下
生成选项中勾选你需要的支持项即可
项目编译属性为:
- 使用多字节字符集
- 不使用预编译头
- 附加选项 /wd4018 /wd4244 /wd4267
- 预处理器定义中添加
_CRT_SECURE_NO_WARNINGS
编译后会在当前文件夹Debug目录下生成unicorn.lib静态编译库和unicorn.dll动态库这样就可以开始使用Unicorn进行开发了
编译到最后一项可能会报错系统找不到指定的路径,查看makefile发现问题出现在此处
事实上只不过是不能将生成的lib和dll复制到新建的文件夹而已,只需要到生成目录去找即可。
官方目前提供的最新已编译版本为1.0.1版本,比较老,建议自己编辑最新版本源码,以获得更多可用API。
Win32:https://github.com/unicorn-engine/unicorn/releases/download/1.0.1/unicorn-1.0.1-win32.zip
Win64:https://github.com/unicorn-engine/unicorn/releases/download/1.0.1/unicorn-1.0.1-win64.zip
注意: 选x32或x64将影响后面开发的位数
引擎调用测试
新建一个VS项目,将..\unicorn-master\include\unicorn中的头文件以及编译好的lib和dll文件全部拷贝到新建项目的主目录下
在VS解决方案中,头文件添加现有项unicorn.h,资源文件中添加unicorn.lib,重新生成解决方案
接下来测试我们生成的unicorn框架
主文件代码如下
#include <iostream> #include "unicorn/unicorn.h"
#define X86_CODE32 "\x41\x4a"
#define ADDRESS 0x1000000
int main() { uc_engine* uc; uc_err err; int r_ecx = 0x1234; int r_edx = 0x7890;
printf("Emulate i386 code\n");
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); if (err != UC_ERR_OK) { printf("Failed on uc_open() with error returned: %u\n", err); return -1; }
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
if (uc_mem_write(uc, ADDRESS, X86_CODE32, sizeof(X86_CODE32) - 1)) { printf("Failed to write emulation code to memory, quit!\n"); return -1; }
uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
printf(">>> ECX = 0x%x\n", r_ecx); printf(">>> EDX = 0x%x\n", r_edx);
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0); if (err) { printf("Failed on uc_emu_start() with error returned %u: %s\n", err, uc_strerror(err)); }
printf("Emulation done. Below is the CPU context\n");
uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx); uc_reg_read(uc, UC_X86_REG_EDX, &r_edx); printf(">>> ECX = 0x%x\n", r_ecx); printf(">>> EDX = 0x%x\n", r_edx);
uc_close(uc);
return 0; }
|
运行结果如下
ecx+1和edx-1成功模拟。
0x1 数据类型分析
uc_arch
架构选择
typedef enum uc_arch { UC_ARCH_ARM = 1, UC_ARCH_ARM64, UC_ARCH_MIPS, UC_ARCH_X86, UC_ARCH_PPC, UC_ARCH_SPARC, UC_ARCH_M68K, UC_ARCH_MAX, } uc_arch;
|
uc_mode
模式选择
typedef enum uc_mode { UC_MODE_LITTLE_ENDIAN = 0, UC_MODE_BIG_ENDIAN = 1 << 30,
UC_MODE_ARM = 0, UC_MODE_THUMB = 1 << 4, UC_MODE_MCLASS = 1 << 5, UC_MODE_V8 = 1 << 6,
UC_MODE_ARM926 = 1 << 7, UC_MODE_ARM946 = 1 << 8, UC_MODE_ARM1176 = 1 << 9,
UC_MODE_MICRO = 1 << 4, UC_MODE_MIPS3 = 1 << 5, UC_MODE_MIPS32R6 = 1 << 6, UC_MODE_MIPS32 = 1 << 2, UC_MODE_MIPS64 = 1 << 3,
UC_MODE_16 = 1 << 1, UC_MODE_32 = 1 << 2, UC_MODE_64 = 1 << 3,
UC_MODE_PPC32 = 1 << 2, UC_MODE_PPC64 = 1 << 3, UC_MODE_QPX = 1 << 4,
UC_MODE_SPARC32 = 1 << 2, UC_MODE_SPARC64 = 1 << 3, UC_MODE_V9 = 1 << 4,
} uc_mode;
|
uc_err
错误类型,是uc_errno()的返回值
typedef enum uc_err { UC_ERR_OK = 0, UC_ERR_NOMEM, UC_ERR_ARCH, UC_ERR_HANDLE, UC_ERR_MODE, UC_ERR_VERSION, UC_ERR_READ_UNMAPPED, UC_ERR_WRITE_UNMAPPED, UC_ERR_FETCH_UNMAPPED, UC_ERR_HOOK, UC_ERR_INSN_INVALID, UC_ERR_MAP, UC_ERR_WRITE_PROT, UC_ERR_READ_PROT, UC_ERR_FETCH_PROT, UC_ERR_ARG, UC_ERR_READ_UNALIGNED, UC_ERR_WRITE_UNALIGNED, UC_ERR_FETCH_UNALIGNED, UC_ERR_HOOK_EXIST, UC_ERR_RESOURCE, UC_ERR_EXCEPTION, UC_ERR_TIMEOUT } uc_err;
|
uc_mem_type
UC_HOOK_MEM_*的所有内存访问类型
typedef enum uc_mem_type { UC_MEM_READ = 16, UC_MEM_WRITE, UC_MEM_FETCH, UC_MEM_READ_UNMAPPED, UC_MEM_WRITE_UNMAPPED, UC_MEM_FETCH_UNMAPPED, UC_MEM_WRITE_PROT, UC_MEM_READ_PROT, UC_MEM_FETCH_PROT, UC_MEM_READ_AFTER, } uc_mem_type;
|
uc_hook_type
uc_hook_add()的所有hook类型参数
typedef enum uc_hook_type { UC_HOOK_INTR = 1 << 0, UC_HOOK_INSN = 1 << 1, UC_HOOK_CODE = 1 << 2, UC_HOOK_BLOCK = 1 << 3, UC_HOOK_MEM_READ_UNMAPPED = 1 << 4, UC_HOOK_MEM_WRITE_UNMAPPED = 1 << 5, UC_HOOK_MEM_FETCH_UNMAPPED = 1 << 6, UC_HOOK_MEM_READ_PROT = 1 << 7, UC_HOOK_MEM_WRITE_PROT = 1 << 8, UC_HOOK_MEM_FETCH_PROT = 1 << 9, UC_HOOK_MEM_READ = 1 << 10, UC_HOOK_MEM_WRITE = 1 << 11, UC_HOOK_MEM_FETCH = 1 << 12, UC_HOOK_MEM_READ_AFTER = 1 << 13, UC_HOOK_INSN_INVALID = 1 << 14, } uc_hook_type;
|
宏定义Hook类型
#define UC_HOOK_MEM_UNMAPPED (UC_HOOK_MEM_READ_UNMAPPED + UC_HOOK_MEM_WRITE_UNMAPPED + UC_HOOK_MEM_FETCH_UNMAPPED)
#define UC_HOOK_MEM_PROT (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_FETCH_PROT)
#define UC_HOOK_MEM_READ_INVALID (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_READ_UNMAPPED)
#define UC_HOOK_MEM_WRITE_INVALID (UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_WRITE_UNMAPPED)
#define UC_HOOK_MEM_FETCH_INVALID (UC_HOOK_MEM_FETCH_PROT + UC_HOOK_MEM_FETCH_UNMAPPED)
#define UC_HOOK_MEM_INVALID (UC_HOOK_MEM_UNMAPPED + UC_HOOK_MEM_PROT)
#define UC_HOOK_MEM_VALID (UC_HOOK_MEM_READ + UC_HOOK_MEM_WRITE + UC_HOOK_MEM_FETCH)
|
uc_mem_region
由uc_mem_map()和uc_mem_map_ptr()映射内存区域
使用uc_mem_regions()检索该内存区域的列表
typedef struct uc_mem_region { uint64_t begin; uint64_t end; uint32_t perms; } uc_mem_region;
|
uc_query_type
uc_query()的所有查询类型参数
typedef enum uc_query_type { UC_QUERY_MODE = 1, UC_QUERY_PAGE_SIZE, UC_QUERY_ARCH, } uc_query_type;
|
uc_context
与uc_context_*()一起使用,管理CPU上下文的不透明存储
struct uc_context; typedef struct uc_context uc_context;
|
uc_prot
新映射区域的权限
typedef enum uc_prot { UC_PROT_NONE = 0, UC_PROT_READ = 1, UC_PROT_WRITE = 2, UC_PROT_EXEC = 4, UC_PROT_ALL = 7, } uc_prot;
|