Micro Unicorn-Engine API Documentation
Warning: This is an unofficial API document by kabeor, If there are any mistakes, welcome to ask.
注意: 這是由kabeor制作的非官方API參考文檔裳凸,如有錯(cuò)誤歡迎提出捺癞,覺得不錯(cuò)可以給個(gè)star鼓勵(lì)我
之前對(duì)Capstone反匯編引擎的API分析文檔已經(jīng)被官方收錄 https://github.com/kabeor/Micro-Capstone-Engine-API-Documentation 非区,在實(shí)現(xiàn)自己想要做出的調(diào)試器的路上过牙,又遇到了與Capstone同作者的國(guó)外大佬aquynh的另一個(gè)著名項(xiàng)目Unicorn实柠,不巧的是丙曙,詳盡的API文檔仍然較少,更多的是大篇幅的代碼翅萤,因此決定繼續(xù)分析Unicorn框架恐疲,包括數(shù)據(jù)類型,已開放API及其實(shí)現(xiàn)套么。
Unicorn是一個(gè)輕量級(jí), 多平臺(tái), 多架構(gòu)的CPU模擬器框架培己,基于qemu開發(fā),它可以代替CPU模擬代碼的執(zhí)行胚泌,常用于惡意代碼分析省咨,F(xiàn)uzz等,該項(xiàng)目被用于Radare2逆向分析框架玷室,GEF(gdb的pwn分析插件)零蓉,Pwndbg,Angr符號(hào)執(zhí)行框架等多個(gè)著名項(xiàng)目穷缤。接下來我也將通過閱讀源碼和代碼實(shí)際調(diào)用來寫一個(gè)簡(jiǎn)單的非官方版本的API手冊(cè)敌蜂。
Blog: kabeor.cn
0x0 開發(fā)準(zhǔn)備
Unicorn官網(wǎng): http://www.unicorn-engine.org
自行編譯lib和dll方法
源碼: https://github.com/unicorn-engine/unicorn/archive/master.zip
下載后解壓
文件結(jié)構(gòu)如下:
. <- 主要引擎core engine + README + 編譯文檔COMPILE.TXT 等
├── arch <- 各語言反編譯支持的代碼實(shí)現(xiàn)
├── bindings <- 中間件
│ ├── dotnet <- .Net 中間件 + 測(cè)試代碼
│ ├── go <- go 中間件 + 測(cè)試代碼
│ ├── haskell <- Haskell 中間件 + 測(cè)試代碼
│ ├── java <- Java 中間件 + 測(cè)試代碼
│ ├── pascal <- Pascal 中間件 + 測(cè)試代碼
│ ├── python <- Python 中間件 + 測(cè)試代碼
│ ├── ruby <- Ruby 中間件 + 測(cè)試代碼
│ └── vb6 <- VB6 中間件 + 測(cè)試代碼
├── docs <- 文檔,主要是Unicorn的實(shí)現(xiàn)思路
├── include <- C頭文件
├── msvc <- Microsoft Visual Studio 支持(Windows)
├── qemu <- qemu框架源碼
├── samples <- Unicorn使用示例
└── tests <- C語言測(cè)試用例
下面演示W(wǎng)indows10使用Visual Studio2019編譯
打開msvc文件夾津肛,內(nèi)部結(jié)構(gòu)如下
VS打開unicorn.sln項(xiàng)目文件章喉,解決方案自動(dòng)載入這些
如果都需要的話,直接編譯就好了身坐,只需要其中幾種秸脱,則右鍵解決方案->屬性->配置屬性 如下
生成選項(xiàng)中勾選你需要的支持項(xiàng)即可
項(xiàng)目編譯屬性為:
- 使用多字節(jié)字符集
- 不使用預(yù)編譯頭
- 附加選項(xiàng) /wd4018 /wd4244 /wd4267
- 預(yù)處理器定義中添加
_CRT_SECURE_NO_WARNINGS
編譯后會(huì)在當(dāng)前文件夾Debug目錄下生成unicorn.lib靜態(tài)編譯庫和unicorn.dll動(dòng)態(tài)庫這樣就可以開始使用Unicorn進(jìn)行開發(fā)了
編譯到最后一項(xiàng)可能會(huì)報(bào)錯(cuò)系統(tǒng)找不到指定的路徑,查看makefile發(fā)現(xiàn)問題出現(xiàn)在此處
事實(shí)上只不過是不能將生成的lib和dll復(fù)制到新建的文件夾而已部蛇,只需要到生成目錄去找即可摊唇。
官方目前提供的最新已編譯版本為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將影響后面開發(fā)的位數(shù)
引擎調(diào)用測(cè)試
新建一個(gè)VS項(xiàng)目,將..\unicorn-master\include\unicorn中的頭文件以及編譯好的lib和dll文件全部拷貝到新建項(xiàng)目的主目錄下
在VS解決方案中吮便,頭文件添加現(xiàn)有項(xiàng)unicorn.h,資源文件中添加unicorn.lib幢踏,重新生成解決方案
接下來測(cè)試我們生成的unicorn框架
主文件代碼如下
#include <iostream>
#include "unicorn/unicorn.h"
// 要模擬的指令
#define X86_CODE32 "\x41\x4a" // INC ecx; DEC edx
// 起始地址
#define ADDRESS 0x1000000
int main()
{
uc_engine* uc;
uc_err err;
int r_ecx = 0x1234; // ECX 寄存器
int r_edx = 0x7890; // EDX 寄存器
printf("Emulate i386 code\n");
// X86-32bit 模式初始化模擬
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;
}
// 給模擬器申請(qǐng) 2MB 內(nèi)存
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// 將要模擬的指令寫入內(nèi)存
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;
}
運(yùn)行結(jié)果如下
ecx+1和edx-1成功模擬髓需。
0x1 數(shù)據(jù)類型分析
uc_arch
架構(gòu)選擇
typedef enum uc_arch {
UC_ARCH_ARM = 1, // ARM 架構(gòu) (包括 Thumb, Thumb-2)
UC_ARCH_ARM64, // ARM-64, 也稱 AArch64
UC_ARCH_MIPS, // Mips 架構(gòu)
UC_ARCH_X86, // X86 架構(gòu) (包括 x86 & x86-64)
UC_ARCH_PPC, // PowerPC 架構(gòu) (暫不支持)
UC_ARCH_SPARC, // Sparc 架構(gòu)
UC_ARCH_M68K, // M68K 架構(gòu)
UC_ARCH_MAX,
} uc_arch;
uc_mode
模式選擇
typedef enum uc_mode {
UC_MODE_LITTLE_ENDIAN = 0, // 小端序模式 (默認(rèn))
UC_MODE_BIG_ENDIAN = 1 << 30, // 大端序模式
// arm / arm64
UC_MODE_ARM = 0, // ARM 模式
UC_MODE_THUMB = 1 << 4, // THUMB 模式 (包括 Thumb-2)
UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M 系列 (暫不支持)
UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (暫不支持)
// arm (32bit) cpu 類型
UC_MODE_ARM926 = 1 << 7, // ARM926 CPU 類型
UC_MODE_ARM946 = 1 << 8, // ARM946 CPU 類型
UC_MODE_ARM1176 = 1 << 9, // ARM1176 CPU 類型
// mips
UC_MODE_MICRO = 1 << 4, // MicroMips 模式 (暫不支持)
UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (暫不支持)
UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (暫不支持)
UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA
UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA
// x86 / x64
UC_MODE_16 = 1 << 1, // 16-bit 模式
UC_MODE_32 = 1 << 2, // 32-bit 模式
UC_MODE_64 = 1 << 3, // 64-bit 模式
// ppc
UC_MODE_PPC32 = 1 << 2, // 32-bit 模式 (暫不支持)
UC_MODE_PPC64 = 1 << 3, // 64-bit 模式 (暫不支持)
UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions 模式 (暫不支持)
// sparc
UC_MODE_SPARC32 = 1 << 2, // 32-bit 模式
UC_MODE_SPARC64 = 1 << 3, // 64-bit 模式
UC_MODE_V9 = 1 << 4, // SparcV9 模式 (暫不支持)
// m68k
} uc_mode;
uc_err
錯(cuò)誤類型,是uc_errno()的返回值
typedef enum uc_err {
UC_ERR_OK = 0, // 無錯(cuò)誤
UC_ERR_NOMEM, // 內(nèi)存不足: uc_open(), uc_emulate()
UC_ERR_ARCH, // 不支持的架構(gòu): uc_open()
UC_ERR_HANDLE, // 不可用句柄
UC_ERR_MODE, // 不可用/不支持架構(gòu): uc_open()
UC_ERR_VERSION, // 不支持版本 (中間件)
UC_ERR_READ_UNMAPPED, // 由于在未映射的內(nèi)存上讀取而退出模擬: uc_emu_start()
UC_ERR_WRITE_UNMAPPED, // 由于在未映射的內(nèi)存上寫入而退出模擬: uc_emu_start()
UC_ERR_FETCH_UNMAPPED, // 由于在未映射的內(nèi)存中獲取數(shù)據(jù)而退出模擬: uc_emu_start()
UC_ERR_HOOK, // 無效的hook類型: uc_hook_add()
UC_ERR_INSN_INVALID, // 由于指令無效而退出模擬: uc_emu_start()
UC_ERR_MAP, // 無效的內(nèi)存映射: uc_mem_map()
UC_ERR_WRITE_PROT, // 由于UC_MEM_WRITE_PROT沖突而停止模擬: uc_emu_start()
UC_ERR_READ_PROT, // 由于UC_MEM_READ_PROT沖突而停止模擬: uc_emu_start()
UC_ERR_FETCH_PROT, // 由于UC_MEM_FETCH_PROT沖突而停止模擬: uc_emu_start()
UC_ERR_ARG, // 提供給uc_xxx函數(shù)的無效參數(shù)
UC_ERR_READ_UNALIGNED, // 未對(duì)齊讀取
UC_ERR_WRITE_UNALIGNED, // 未對(duì)齊寫入
UC_ERR_FETCH_UNALIGNED, // 未對(duì)齊的提取
UC_ERR_HOOK_EXIST, // 此事件的鉤子已經(jīng)存在
UC_ERR_RESOURCE, // 資源不足: uc_emu_start()
UC_ERR_EXCEPTION, // 未處理的CPU異常
UC_ERR_TIMEOUT // 模擬超時(shí)
} uc_err;
uc_mem_type
UC_HOOK_MEM_*的所有內(nèi)存訪問類型
typedef enum uc_mem_type {
UC_MEM_READ = 16, // 內(nèi)存從..讀取
UC_MEM_WRITE, // 內(nèi)存寫入到..
UC_MEM_FETCH, // 內(nèi)存被獲取
UC_MEM_READ_UNMAPPED, // 未映射內(nèi)存從..讀取
UC_MEM_WRITE_UNMAPPED, // 未映射內(nèi)存寫入到..
UC_MEM_FETCH_UNMAPPED, // 未映射內(nèi)存被獲取
UC_MEM_WRITE_PROT, // 內(nèi)存寫保護(hù)房蝉,但是已映射
UC_MEM_READ_PROT, // 內(nèi)存讀保護(hù)僚匆,但是已映射
UC_MEM_FETCH_PROT, // 內(nèi)存不可執(zhí)行,但是已映射
UC_MEM_READ_AFTER, // 內(nèi)存從 (成功訪問的地址) 讀入
} uc_mem_type;
uc_hook_type
uc_hook_add()的所有hook類型參數(shù)
typedef enum uc_hook_type {
// Hook 所有中斷/syscall 事件
UC_HOOK_INTR = 1 << 0,
// Hook 一條特定的指令 - 只支持非常小的指令子集
UC_HOOK_INSN = 1 << 1,
// Hook 一段代碼
UC_HOOK_CODE = 1 << 2,
// Hook 基本塊
UC_HOOK_BLOCK = 1 << 3,
// 用于在未映射的內(nèi)存上讀取內(nèi)存的Hook
UC_HOOK_MEM_READ_UNMAPPED = 1 << 4,
// Hook 無效的內(nèi)存寫事件
UC_HOOK_MEM_WRITE_UNMAPPED = 1 << 5,
// Hook 執(zhí)行事件的無效內(nèi)存
UC_HOOK_MEM_FETCH_UNMAPPED = 1 << 6,
// Hook 讀保護(hù)的內(nèi)存
UC_HOOK_MEM_READ_PROT = 1 << 7,
// Hook 寫保護(hù)的內(nèi)存
UC_HOOK_MEM_WRITE_PROT = 1 << 8,
// Hook 不可執(zhí)行內(nèi)存上的內(nèi)存
UC_HOOK_MEM_FETCH_PROT = 1 << 9,
// Hook 內(nèi)存讀取事件
UC_HOOK_MEM_READ = 1 << 10,
// Hook 內(nèi)存寫入事件
UC_HOOK_MEM_WRITE = 1 << 11,
// Hook 內(nèi)存獲取執(zhí)行事件
UC_HOOK_MEM_FETCH = 1 << 12,
// Hook 內(nèi)存讀取事件搭幻,只允許能成功訪問的地址
// 成功讀取后將觸發(fā)回調(diào)
UC_HOOK_MEM_READ_AFTER = 1 << 13,
// Hook 無效指令異常
UC_HOOK_INSN_INVALID = 1 << 14,
} uc_hook_type;
宏定義Hook類型
// Hook 所有未映射內(nèi)存訪問的事件
#define UC_HOOK_MEM_UNMAPPED (UC_HOOK_MEM_READ_UNMAPPED + UC_HOOK_MEM_WRITE_UNMAPPED + UC_HOOK_MEM_FETCH_UNMAPPED)
// Hook 所有對(duì)受保護(hù)內(nèi)存的非法訪問事件
#define UC_HOOK_MEM_PROT (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_FETCH_PROT)
// Hook 所有非法讀取存儲(chǔ)器的事件
#define UC_HOOK_MEM_READ_INVALID (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_READ_UNMAPPED)
// Hook 所有非法寫入存儲(chǔ)器的事件
#define UC_HOOK_MEM_WRITE_INVALID (UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_WRITE_UNMAPPED)
// Hook 所有非法獲取內(nèi)存的事件
#define UC_HOOK_MEM_FETCH_INVALID (UC_HOOK_MEM_FETCH_PROT + UC_HOOK_MEM_FETCH_UNMAPPED)
// Hook 所有非法的內(nèi)存訪問事件
#define UC_HOOK_MEM_INVALID (UC_HOOK_MEM_UNMAPPED + UC_HOOK_MEM_PROT)
// Hook 所有有效內(nèi)存訪問的事件
// 注意: UC_HOOK_MEM_READ 在 UC_HOOK_MEM_READ_PROT 和 UC_HOOK_MEM_READ_UNMAPPED 之前觸發(fā) ,
// 因此這個(gè)Hook可能會(huì)觸發(fā)一些無效的讀取咧擂。
#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()映射內(nèi)存區(qū)域
使用uc_mem_regions()檢索該內(nèi)存區(qū)域的列表
typedef struct uc_mem_region {
uint64_t begin; // 區(qū)域起始地址 (包括)
uint64_t end; // 區(qū)域結(jié)束地址 (包括)
uint32_t perms; // 區(qū)域的內(nèi)存權(quán)限
} uc_mem_region;
uc_query_type
uc_query()的所有查詢類型參數(shù)
typedef enum uc_query_type {
// 動(dòng)態(tài)查詢當(dāng)前硬件模式
UC_QUERY_MODE = 1,
UC_QUERY_PAGE_SIZE,
UC_QUERY_ARCH,
} uc_query_type;
uc_context
與uc_context_*()一起使用,管理CPU上下文的不透明存儲(chǔ)
struct uc_context;
typedef struct uc_context uc_context;
uc_prot
新映射區(qū)域的權(quán)限
typedef enum uc_prot {
UC_PROT_NONE = 0, //無
UC_PROT_READ = 1, //讀取
UC_PROT_WRITE = 2, //寫入
UC_PROT_EXEC = 4, //可執(zhí)行
UC_PROT_ALL = 7, //所有權(quán)限
} uc_prot;
0x2 API分析
uc_version
unsigned int uc_version(unsigned int *major, unsigned int *minor);
用于返回Unicorn API主次版本信息
@major: API主版本號(hào)
@minor: API次版本號(hào)
@return 16進(jìn)制數(shù)檀蹋,計(jì)算方式 (major << 8 | minor)
提示: 該返回值可以和宏UC_MAKE_VERSION比較
源碼實(shí)現(xiàn)
unsigned int uc_version(unsigned int *major, unsigned int *minor)
{
if (major != NULL && minor != NULL) {
*major = UC_API_MAJOR; //宏
*minor = UC_API_MINOR; //宏
}
return (UC_API_MAJOR << 8) + UC_API_MINOR; //(major << 8 | minor)
}
編譯后不可更改松申,不接受自定義版本
使用示例:
#include <iostream>
#include "unicorn/unicorn.h"
using namespace std;
int main()
{
unsigned int version;
version = uc_version(NULL,NULL);
cout << hex << version << endl;
return 0;
}
輸出:
得到版本號(hào)1.0.0
uc_arch_supported
bool uc_arch_supported(uc_arch arch);
確定Unicorn是否支持當(dāng)前架構(gòu)
@arch: 架構(gòu)類型 (UC_ARCH_*)
@return 如果支持返回True
源碼實(shí)現(xiàn)
bool uc_arch_supported(uc_arch arch)
{
switch (arch) {
#ifdef UNICORN_HAS_ARM
case UC_ARCH_ARM: return true;
#endif
#ifdef UNICORN_HAS_ARM64
case UC_ARCH_ARM64: return true;
#endif
#ifdef UNICORN_HAS_M68K
case UC_ARCH_M68K: return true;
#endif
#ifdef UNICORN_HAS_MIPS
case UC_ARCH_MIPS: return true;
#endif
#ifdef UNICORN_HAS_PPC
case UC_ARCH_PPC: return true;
#endif
#ifdef UNICORN_HAS_SPARC
case UC_ARCH_SPARC: return true;
#endif
#ifdef UNICORN_HAS_X86
case UC_ARCH_X86: return true;
#endif
/* 無效或禁用架構(gòu) */
default: return false;
}
}
使用示例:
#include <iostream>
#include "unicorn/unicorn.h"
using namespace std;
int main()
{
cout << "是否支持UC_ARCH_X86架構(gòu):" << uc_arch_supported(UC_ARCH_X86) << endl;
return 0;
}
輸出:
uc_open
uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc);
創(chuàng)建新的Unicorn實(shí)例
@arch: 架構(gòu)類型 (UC_ARCH_*)
@mode: 硬件模式. 由 UC_MODE_* 組合
@uc: 指向 uc_engine 的指針, 返回時(shí)更新
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result)
{
struct uc_struct *uc;
if (arch < UC_ARCH_MAX) {
uc = calloc(1, sizeof(*uc)); //申請(qǐng)內(nèi)存
if (!uc) {
// 內(nèi)存不足
return UC_ERR_NOMEM;
}
uc->errnum = UC_ERR_OK;
uc->arch = arch;
uc->mode = mode;
// 初始化
// uc->ram_list = { .blocks = QTAILQ_HEAD_INITIALIZER(ram_list.blocks) };
uc->ram_list.blocks.tqh_first = NULL;
uc->ram_list.blocks.tqh_last = &(uc->ram_list.blocks.tqh_first);
uc->memory_listeners.tqh_first = NULL;
uc->memory_listeners.tqh_last = &uc->memory_listeners.tqh_first;
uc->address_spaces.tqh_first = NULL;
uc->address_spaces.tqh_last = &uc->address_spaces.tqh_first;
switch(arch) { // 根據(jù)架構(gòu)進(jìn)行預(yù)處理
default:
break;
#ifdef UNICORN_HAS_M68K
case UC_ARCH_M68K:
if ((mode & ~UC_MODE_M68K_MASK) ||
!(mode & UC_MODE_BIG_ENDIAN)) {
free(uc);
return UC_ERR_MODE;
}
uc->init_arch = m68k_uc_init;
break;
#endif
#ifdef UNICORN_HAS_X86
case UC_ARCH_X86:
if ((mode & ~UC_MODE_X86_MASK) ||
(mode & UC_MODE_BIG_ENDIAN) ||
!(mode & (UC_MODE_16|UC_MODE_32|UC_MODE_64))) {
free(uc);
return UC_ERR_MODE;
}
uc->init_arch = x86_uc_init;
break;
#endif
#ifdef UNICORN_HAS_ARM
case UC_ARCH_ARM:
if ((mode & ~UC_MODE_ARM_MASK)) {
free(uc);
return UC_ERR_MODE;
}
if (mode & UC_MODE_BIG_ENDIAN) {
uc->init_arch = armeb_uc_init;
} else {
uc->init_arch = arm_uc_init;
}
if (mode & UC_MODE_THUMB)
uc->thumb = 1;
break;
#endif
#ifdef UNICORN_HAS_ARM64
case UC_ARCH_ARM64:
if (mode & ~UC_MODE_ARM_MASK) {
free(uc);
return UC_ERR_MODE;
}
if (mode & UC_MODE_BIG_ENDIAN) {
uc->init_arch = arm64eb_uc_init;
} else {
uc->init_arch = arm64_uc_init;
}
break;
#endif
#if defined(UNICORN_HAS_MIPS) || defined(UNICORN_HAS_MIPSEL) || defined(UNICORN_HAS_MIPS64) || defined(UNICORN_HAS_MIPS64EL)
case UC_ARCH_MIPS:
if ((mode & ~UC_MODE_MIPS_MASK) ||
!(mode & (UC_MODE_MIPS32|UC_MODE_MIPS64))) {
free(uc);
return UC_ERR_MODE;
}
if (mode & UC_MODE_BIG_ENDIAN) {
#ifdef UNICORN_HAS_MIPS
if (mode & UC_MODE_MIPS32)
uc->init_arch = mips_uc_init;
#endif
#ifdef UNICORN_HAS_MIPS64
if (mode & UC_MODE_MIPS64)
uc->init_arch = mips64_uc_init;
#endif
} else { // 小端序
#ifdef UNICORN_HAS_MIPSEL
if (mode & UC_MODE_MIPS32)
uc->init_arch = mipsel_uc_init;
#endif
#ifdef UNICORN_HAS_MIPS64EL
if (mode & UC_MODE_MIPS64)
uc->init_arch = mips64el_uc_init;
#endif
}
break;
#endif
#ifdef UNICORN_HAS_SPARC
case UC_ARCH_SPARC:
if ((mode & ~UC_MODE_SPARC_MASK) ||
!(mode & UC_MODE_BIG_ENDIAN) ||
!(mode & (UC_MODE_SPARC32|UC_MODE_SPARC64))) {
free(uc);
return UC_ERR_MODE;
}
if (mode & UC_MODE_SPARC64)
uc->init_arch = sparc64_uc_init;
else
uc->init_arch = sparc_uc_init;
break;
#endif
}
if (uc->init_arch == NULL) {
return UC_ERR_ARCH;
}
if (machine_initialize(uc))
return UC_ERR_RESOURCE;
*result = uc;
if (uc->reg_reset)
uc->reg_reset(uc);
return UC_ERR_OK;
} else {
return UC_ERR_ARCH;
}
}
注意: uc_open會(huì)申請(qǐng)堆內(nèi)存,使用完必須用uc_close釋放俯逾,否則會(huì)發(fā)生泄露
使用示例:
#include <iostream>
#include "unicorn/unicorn.h"
using namespace std;
int main()
{
uc_engine* uc;
uc_err err;
//// 初始化 X86-32bit 模式模擬器
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;
}
if (!err)
cout << "uc引擎創(chuàng)建成功" << endl;
//// 關(guān)閉uc
err = uc_close(uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_close() with error returned: %u\n", err);
return -1;
}
if (!err)
cout << "uc引擎關(guān)閉成功" << endl;
return 0;
}
輸出
uc_close
uc_err uc_close(uc_engine *uc);
關(guān)閉一個(gè)uc實(shí)例贸桶,將釋放內(nèi)存。關(guān)閉后無法恢復(fù)桌肴。
@uc: 指向由 uc_open() 返回的指針
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_close(uc_engine *uc)
{
int i;
struct list_item *cur;
struct hook *hook;
// 清理內(nèi)部數(shù)據(jù)
if (uc->release)
uc->release(uc->tcg_ctx);
g_free(uc->tcg_ctx);
// 清理 CPU.
g_free(uc->cpu->tcg_as_listener);
g_free(uc->cpu->thread);
// 清理所有 objects.
OBJECT(uc->machine_state->accelerator)->ref = 1;
OBJECT(uc->machine_state)->ref = 1;
OBJECT(uc->owner)->ref = 1;
OBJECT(uc->root)->ref = 1;
object_unref(uc, OBJECT(uc->machine_state->accelerator));
object_unref(uc, OBJECT(uc->machine_state));
object_unref(uc, OBJECT(uc->cpu));
object_unref(uc, OBJECT(&uc->io_mem_notdirty));
object_unref(uc, OBJECT(&uc->io_mem_unassigned));
object_unref(uc, OBJECT(&uc->io_mem_rom));
object_unref(uc, OBJECT(uc->root));
// 釋放內(nèi)存
g_free(uc->system_memory);
// 釋放相關(guān)線程
if (uc->qemu_thread_data)
g_free(uc->qemu_thread_data);
// 釋放其他數(shù)據(jù)
free(uc->l1_map);
if (uc->bounce.buffer) {
free(uc->bounce.buffer);
}
g_hash_table_foreach(uc->type_table, free_table, uc);
g_hash_table_destroy(uc->type_table);
for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
free(uc->ram_list.dirty_memory[i]);
}
// 釋放hook和hook列表
for (i = 0; i < UC_HOOK_MAX; i++) {
cur = uc->hook[i].head;
// hook 可存在于多個(gè)列表皇筛,可通過計(jì)數(shù)獲取釋放的時(shí)間
while (cur) {
hook = (struct hook *)cur->data;
if (--hook->refs == 0) {
free(hook);
}
cur = cur->next;
}
list_clear(&uc->hook[i]);
}
free(uc->mapped_blocks);
// 最后釋放uc自身
memset(uc, 0, sizeof(*uc));
free(uc);
return UC_ERR_OK;
}
使用實(shí)例同uc_open()
uc_query
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result);
查詢引擎的內(nèi)部狀態(tài)
@uc: uc_open() 返回的句柄
@type: uc_query_type 中枚舉的類型
@result: 保存被查詢的內(nèi)部狀態(tài)的指針
@return: 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result)
{
if (type == UC_QUERY_PAGE_SIZE) {
*result = uc->target_page_size;
return UC_ERR_OK;
}
if (type == UC_QUERY_ARCH) {
*result = uc->arch;
return UC_ERR_OK;
}
switch(uc->arch) {
#ifdef UNICORN_HAS_ARM
case UC_ARCH_ARM:
return uc->query(uc, type, result);
#endif
default:
return UC_ERR_ARG;
}
return UC_ERR_OK;
}
使用示例:
#include <iostream>
#include "unicorn/unicorn.h"
using namespace std;
int main()
{
uc_engine* uc;
uc_err err;
//// Initialize emulator in X86-32bit mode
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;
}
if (!err)
cout << "uc實(shí)例創(chuàng)建成功" << endl;
size_t result[] = {0};
err = uc_query(uc, UC_QUERY_ARCH, result); // 查詢架構(gòu)
if (!err)
cout << "查詢成功: " << *result << endl;
err = uc_close(uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_close() with error returned: %u\n", err);
return -1;
}
if (!err)
cout << "uc實(shí)例關(guān)閉成功" << endl;
return 0;
}
輸出
架構(gòu)查詢結(jié)果為4,對(duì)應(yīng)的正是UC_ARCH_X86
uc_errno
uc_err uc_errno(uc_engine *uc);
當(dāng)某個(gè)API函數(shù)失敗時(shí)坠七,報(bào)告最后的錯(cuò)誤號(hào)水醋,一旦被訪問,uc_errno可能不會(huì)保留原來的值彪置。
@uc: uc_open() 返回的句柄
@return: 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_errno(uc_engine *uc)
{
return uc->errnum;
}
使用示例:
#include <iostream>
#include "unicorn/unicorn.h"
using namespace std;
int main()
{
uc_engine* uc;
uc_err err;
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;
}
if (!err)
cout << "uc實(shí)例創(chuàng)建成功" << endl;
err = uc_errno(uc);
cout << "錯(cuò)誤號(hào): " << err << endl;
err = uc_close(uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_close() with error returned: %u\n", err);
return -1;
}
if (!err)
cout << "uc實(shí)例關(guān)閉成功" << endl;
return 0;
}
輸出
無錯(cuò)誤拄踪,輸出錯(cuò)誤號(hào)為0
uc_strerror
const char *uc_strerror(uc_err code);
返回給定錯(cuò)誤號(hào)的解釋
@code: 錯(cuò)誤號(hào)
@return: 指向給定錯(cuò)誤號(hào)的解釋的字符串指針
源碼實(shí)現(xiàn)
const char *uc_strerror(uc_err code)
{
switch(code) {
default:
return "Unknown error code";
case UC_ERR_OK:
return "OK (UC_ERR_OK)";
case UC_ERR_NOMEM:
return "No memory available or memory not present (UC_ERR_NOMEM)";
case UC_ERR_ARCH:
return "Invalid/unsupported architecture (UC_ERR_ARCH)";
case UC_ERR_HANDLE:
return "Invalid handle (UC_ERR_HANDLE)";
case UC_ERR_MODE:
return "Invalid mode (UC_ERR_MODE)";
case UC_ERR_VERSION:
return "Different API version between core & binding (UC_ERR_VERSION)";
case UC_ERR_READ_UNMAPPED:
return "Invalid memory read (UC_ERR_READ_UNMAPPED)";
case UC_ERR_WRITE_UNMAPPED:
return "Invalid memory write (UC_ERR_WRITE_UNMAPPED)";
case UC_ERR_FETCH_UNMAPPED:
return "Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)";
case UC_ERR_HOOK:
return "Invalid hook type (UC_ERR_HOOK)";
case UC_ERR_INSN_INVALID:
return "Invalid instruction (UC_ERR_INSN_INVALID)";
case UC_ERR_MAP:
return "Invalid memory mapping (UC_ERR_MAP)";
case UC_ERR_WRITE_PROT:
return "Write to write-protected memory (UC_ERR_WRITE_PROT)";
case UC_ERR_READ_PROT:
return "Read from non-readable memory (UC_ERR_READ_PROT)";
case UC_ERR_FETCH_PROT:
return "Fetch from non-executable memory (UC_ERR_FETCH_PROT)";
case UC_ERR_ARG:
return "Invalid argument (UC_ERR_ARG)";
case UC_ERR_READ_UNALIGNED:
return "Read from unaligned memory (UC_ERR_READ_UNALIGNED)";
case UC_ERR_WRITE_UNALIGNED:
return "Write to unaligned memory (UC_ERR_WRITE_UNALIGNED)";
case UC_ERR_FETCH_UNALIGNED:
return "Fetch from unaligned memory (UC_ERR_FETCH_UNALIGNED)";
case UC_ERR_RESOURCE:
return "Insufficient resource (UC_ERR_RESOURCE)";
case UC_ERR_EXCEPTION:
return "Unhandled CPU exception (UC_ERR_EXCEPTION)";
case UC_ERR_TIMEOUT:
return "Emulation timed out (UC_ERR_TIMEOUT)";
}
}
使用示例:
#include <iostream>
#include "unicorn/unicorn.h"
using namespace std;
int main()
{
uc_engine* uc;
uc_err err;
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;
}
if (!err)
cout << "uc實(shí)例創(chuàng)建成功" << endl;
err = uc_errno(uc);
cout << "錯(cuò)誤號(hào): " << err << " 錯(cuò)誤描述: " << uc_strerror(err) <<endl;
err = uc_close(uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_close() with error returned: %u\n", err);
return -1;
}
if (!err)
cout << "uc實(shí)例關(guān)閉成功" << endl;
return 0;
}
輸出
uc_reg_write
uc_err uc_reg_write(uc_engine *uc, int regid, const void *value);
將值寫入寄存器
@uc: uc_open()返回的句柄
@regid: 將被修改的寄存器ID
@value: 指向寄存器將被修改成的值的指針
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_reg_write(uc_engine *uc, int regid, const void *value)
{
return uc_reg_write_batch(uc, ®id, (void *const *)&value, 1);
}
uc_err uc_reg_write_batch(uc_engine *uc, int *ids, void *const *vals, int count)
{
int ret = UC_ERR_OK;
if (uc->reg_write)
ret = uc->reg_write(uc, (unsigned int *)ids, vals, count); //結(jié)構(gòu)體中寫入
else
return UC_ERR_EXCEPTION;
return ret;
}
使用示例:
#include <iostream>
#include "unicorn/unicorn.h"
using namespace std;
int main()
{
uc_engine* uc;
uc_err err;
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;
}
if (!err)
cout << "uc實(shí)例創(chuàng)建成功" << endl;
int r_eax = 0x12;
err = uc_reg_write(uc, UC_X86_REG_ECX, &r_eax);
if (!err)
cout << "寫入成功: " << r_eax << endl;
err = uc_close(uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_close() with error returned: %u\n", err);
return -1;
}
if (!err)
cout << "uc實(shí)例關(guān)閉成功" << endl;
return 0;
}
輸出
uc_reg_read
uc_err uc_reg_read(uc_engine *uc, int regid, void *value);
讀取寄存器的值
@uc: uc_open()返回的句柄
@regid: 將被讀取的寄存器ID
@value: 指向保存寄存器值的指針
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_reg_read(uc_engine *uc, int regid, void *value)
{
return uc_reg_read_batch(uc, ®id, &value, 1);
}
uc_err uc_reg_read_batch(uc_engine *uc, int *ids, void **vals, int count)
{
if (uc->reg_read)
uc->reg_read(uc, (unsigned int *)ids, vals, count);
else
return -1;
return UC_ERR_OK;
}
使用示例:
#include <iostream>
#include "unicorn/unicorn.h"
using namespace std;
int main()
{
uc_engine* uc;
uc_err err;
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;
}
if (!err)
cout << "uc實(shí)例創(chuàng)建成功" << endl;
int r_eax = 0x12;
err = uc_reg_write(uc, UC_X86_REG_ECX, &r_eax);
if (!err)
cout << "寫入成功: " << r_eax << endl;
int recv_eax;
err = uc_reg_read(uc, UC_X86_REG_ECX, &recv_eax);
if (!err)
cout << "讀取成功: " << recv_eax << endl;
err = uc_close(uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_close() with error returned: %u\n", err);
return -1;
}
if (!err)
cout << "uc實(shí)例關(guān)閉成功" << endl;
return 0;
}
輸出
uc_reg_write_batch
uc_err uc_reg_write_batch(uc_engine *uc, int *regs, void *const *vals, int count);
同時(shí)將多個(gè)值寫入多個(gè)寄存器
@uc: uc_open()返回的句柄
@regid: 存儲(chǔ)將被寫入的多個(gè)寄存器ID的數(shù)組
@value: 指向保存多個(gè)值的數(shù)組的指針
@count: *regs 和 *vals 數(shù)組的長(zhǎng)度
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_reg_write_batch(uc_engine *uc, int *ids, void *const *vals, int count)
{
int ret = UC_ERR_OK;
if (uc->reg_write)
ret = uc->reg_write(uc, (unsigned int *)ids, vals, count);
else
return UC_ERR_EXCEPTION;
return ret;
}
使用示例:
#include <iostream>
#include <string>
#include "unicorn/unicorn.h"
using namespace std;
int syscall_abi[] = {
UC_X86_REG_RAX, UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX,
UC_X86_REG_R10, UC_X86_REG_R8, UC_X86_REG_R9
};
uint64_t vals[7] = { 200, 10, 11, 12, 13, 14, 15 };
void* ptrs[7];
int main()
{
int i;
uc_err err;
uc_engine* uc;
// set up register pointers
for (i = 0; i < 7; i++) {
ptrs[i] = &vals[i];
}
if ((err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc))) {
uc_perror("uc_open", err);
return 1;
}
// reg_write_batch
printf("reg_write_batch({200, 10, 11, 12, 13, 14, 15})\n");
if ((err = uc_reg_write_batch(uc, syscall_abi, ptrs, 7))) {
uc_perror("uc_reg_write_batch", err);
return 1;
}
// reg_read_batch
memset(vals, 0, sizeof(vals));
if ((err = uc_reg_read_batch(uc, syscall_abi, ptrs, 7))) {
uc_perror("uc_reg_read_batch", err);
return 1;
}
printf("reg_read_batch = {");
for (i = 0; i < 7; i++) {
if (i != 0) printf(", ");
printf("%" PRIu64, vals[i]);
}
printf("}\n");
uint64_t var[7] = { 0 };
for (int i = 0; i < 7; i++)
{
cout << syscall_abi[i] << " ";
printf("%" PRIu64, vals[i]);
cout << endl;
}
return 0;
}
輸出
uc_reg_read_batch
uc_err uc_reg_read_batch(uc_engine *uc, int *regs, void **vals, int count);
同時(shí)讀取多個(gè)寄存器的值。
@uc: uc_open()返回的句柄
@regid: 存儲(chǔ)將被讀取的多個(gè)寄存器ID的數(shù)組
@value: 指向保存多個(gè)值的數(shù)組的指針
@count: *regs 和 *vals 數(shù)組的長(zhǎng)度
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_reg_read_batch(uc_engine *uc, int *ids, void **vals, int count)
{
if (uc->reg_read)
uc->reg_read(uc, (unsigned int *)ids, vals, count);
else
return -1;
return UC_ERR_OK;
}
使用示例同uc_reg_write_batch()悉稠。
uc_mem_write
uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t size);
在內(nèi)存中寫入一段字節(jié)碼宫蛆。
@uc: uc_open() 返回的句柄
@address: 寫入字節(jié)的起始地址
@bytes: 指向一個(gè)包含要寫入內(nèi)存的數(shù)據(jù)的指針
@size: 要寫入的內(nèi)存大小。
注意: @bytes 必須足夠大以包含 @size 字節(jié)的猛。
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *_bytes, size_t size)
{
size_t count = 0, len;
const uint8_t *bytes = _bytes;
if (uc->mem_redirect) {
address = uc->mem_redirect(address);
}
if (!check_mem_area(uc, address, size))
return UC_ERR_WRITE_UNMAPPED;
// 內(nèi)存區(qū)域可以重疊相鄰的內(nèi)存塊
while(count < size) {
MemoryRegion *mr = memory_mapping(uc, address);
if (mr) {
uint32_t operms = mr->perms;
if (!(operms & UC_PROT_WRITE)) // 沒有寫保護(hù)
// 標(biāo)記為可寫
uc->readonly_mem(mr, false);
len = (size_t)MIN(size - count, mr->end - address);
if (uc->write_mem(&uc->as, address, bytes, len) == false)
break;
if (!(operms & UC_PROT_WRITE)) // 沒有寫保護(hù)
// 設(shè)置寫保護(hù)
uc->readonly_mem(mr, true);
count += len;
address += len;
bytes += len;
} else // 此地址尚未被映射
break;
}
if (count == size)
return UC_ERR_OK;
else
return UC_ERR_WRITE_UNMAPPED;
}
使用示例:
#include <iostream>
#include <string>
#include "unicorn/unicorn.h"
using namespace std;
#define X86_CODE32 "\x41\x4a" // INC ecx; DEC edx
#define ADDRESS 0x1000
int main()
{
uc_engine* uc;
uc_err err;
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;
}
uint32_t code;
if(uc_mem_read(uc,ADDRESS,&code, sizeof(code))) {
printf("Failed to read emulation code to memory, quit!\n");
return -1;
}
cout << hex << code << endl;
err = uc_close(uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_close() with error returned: %u\n", err);
return -1;
}
return 0;
}
輸出
uc_mem_read
uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size);
從內(nèi)存中讀取字節(jié)艺智。
@uc: uc_open() 返回的句柄
@address: 讀取字節(jié)的起始地址
@bytes: 指向一個(gè)包含要讀取內(nèi)存的數(shù)據(jù)的指針
@size: 要讀取的內(nèi)存大小。
注意: @bytes 必須足夠大以包含 @size 字節(jié)补憾。
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *_bytes, size_t size)
{
size_t count = 0, len;
uint8_t *bytes = _bytes;
if (uc->mem_redirect) {
address = uc->mem_redirect(address);
}
if (!check_mem_area(uc, address, size))
return UC_ERR_READ_UNMAPPED;
// 內(nèi)存區(qū)域可以重疊相鄰的內(nèi)存塊
while(count < size) {
MemoryRegion *mr = memory_mapping(uc, address);
if (mr) {
len = (size_t)MIN(size - count, mr->end - address);
if (uc->read_mem(&uc->as, address, bytes, len) == false)
break;
count += len;
address += len;
bytes += len;
} else // 此地址尚未被映射
break;
}
if (count == size)
return UC_ERR_OK;
else
return UC_ERR_READ_UNMAPPED;
}
使用示例同uc_mem_write()
uc_emu_start
uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count);
在指定的時(shí)間內(nèi)模擬機(jī)器碼报亩。
@uc: uc_open() 返回的句柄
@begin: 開始模擬的地址
@until: 模擬停止的地址 (當(dāng)?shù)竭_(dá)該地址時(shí))
@timeout: 模擬代碼的持續(xù)時(shí)間(以微秒計(jì))。當(dāng)這個(gè)值為0時(shí)岂却,將在無限時(shí)間內(nèi)模擬代碼忿薇,直到代碼完成裙椭。
@count: 要模擬的指令數(shù)。當(dāng)這個(gè)值為0時(shí)署浩,將模擬所有可用的代碼揉燃,直到代碼完成
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count)
{
// 重制計(jì)數(shù)器
uc->emu_counter = 0;
uc->invalid_error = UC_ERR_OK;
uc->block_full = false;
uc->emulation_done = false;
uc->timed_out = false;
switch(uc->arch) {
default:
break;
#ifdef UNICORN_HAS_M68K
case UC_ARCH_M68K:
uc_reg_write(uc, UC_M68K_REG_PC, &begin);
break;
#endif
#ifdef UNICORN_HAS_X86
case UC_ARCH_X86:
switch(uc->mode) {
default:
break;
case UC_MODE_16: {
uint64_t ip;
uint16_t cs;
uc_reg_read(uc, UC_X86_REG_CS, &cs);
// 抵消后面增加的 IP 和 CS
ip = begin - cs*16;
uc_reg_write(uc, UC_X86_REG_IP, &ip);
break;
}
case UC_MODE_32:
uc_reg_write(uc, UC_X86_REG_EIP, &begin);
break;
case UC_MODE_64:
uc_reg_write(uc, UC_X86_REG_RIP, &begin);
break;
}
break;
#endif
#ifdef UNICORN_HAS_ARM
case UC_ARCH_ARM:
uc_reg_write(uc, UC_ARM_REG_R15, &begin);
break;
#endif
#ifdef UNICORN_HAS_ARM64
case UC_ARCH_ARM64:
uc_reg_write(uc, UC_ARM64_REG_PC, &begin);
break;
#endif
#ifdef UNICORN_HAS_MIPS
case UC_ARCH_MIPS:
// TODO: MIPS32/MIPS64/BIGENDIAN etc
uc_reg_write(uc, UC_MIPS_REG_PC, &begin);
break;
#endif
#ifdef UNICORN_HAS_SPARC
case UC_ARCH_SPARC:
// TODO: Sparc/Sparc64
uc_reg_write(uc, UC_SPARC_REG_PC, &begin);
break;
#endif
}
uc->stop_request = false;
uc->emu_count = count;
// 如果不需要計(jì)數(shù),則移除計(jì)數(shù)掛鉤hook
if (count <= 0 && uc->count_hook != 0) {
uc_hook_del(uc, uc->count_hook);
uc->count_hook = 0;
}
// 設(shè)置計(jì)數(shù)hook記錄指令數(shù)
if (count > 0 && uc->count_hook == 0) {
uc_err err;
// 對(duì)計(jì)數(shù)指令的回調(diào)必須在所有其他操作之前運(yùn)行筋栋,因此必須在hook列表的開頭插入hook炊汤,而不是附加hook
uc->hook_insert = 1;
err = uc_hook_add(uc, &uc->count_hook, UC_HOOK_CODE, hook_count_cb, NULL, 1, 0);
// 恢復(fù)到 uc_hook_add()
uc->hook_insert = 0;
if (err != UC_ERR_OK) {
return err;
}
}
uc->addr_end = until;
if (timeout)
enable_emu_timer(uc, timeout * 1000); // microseconds -> nanoseconds
if (uc->vm_start(uc)) {
return UC_ERR_RESOURCE;
}
// 模擬完成
uc->emulation_done = true;
if (timeout) {
// 等待超時(shí)
qemu_thread_join(&uc->timer);
}
if(uc->timed_out)
return UC_ERR_TIMEOUT;
return uc->invalid_error;
}
使用示例:
#include <iostream>
#include <string>
#include "unicorn/unicorn.h"
using namespace std;
#define X86_CODE32 "\x33\xC0" // xor eax, eax
#define ADDRESS 0x1000
int main()
{
uc_engine* uc;
uc_err err;
int r_eax = 0x111;
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_EAX, &r_eax);
printf(">>> before EAX = 0x%x\n", r_eax);
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));
}
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
printf(">>> after EAX = 0x%x\n", r_eax);
err = uc_close(uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_close() with error returned: %u\n", err);
return -1;
}
return 0;
}
輸出
uc_emu_stop
uc_err uc_emu_stop(uc_engine *uc);
停止模擬
通常是從通過 tracing API注冊(cè)的回調(diào)函數(shù)中調(diào)用。
@uc: uc_open() 返回的句柄
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_emu_stop(uc_engine *uc)
{
if (uc->emulation_done)
return UC_ERR_OK;
uc->stop_request = true;
if (uc->current_cpu) {
// 退出當(dāng)前線程
cpu_exit(uc->current_cpu);
}
return UC_ERR_OK;
}
使用示例:
uc_emu_stop(uc);
uc_hook_add
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
void *user_data, uint64_t begin, uint64_t end, ...);
注冊(cè)hook事件的回調(diào)弊攘,當(dāng)hook事件被觸發(fā)將會(huì)進(jìn)行回調(diào)抢腐。
@uc: uc_open() 返回的句柄
@hh: 注冊(cè)hook得到的句柄. uc_hook_del() 中使用
@type: hook 類型
@callback: 當(dāng)指令被命中時(shí)要運(yùn)行的回調(diào)
@user_data: 用戶自定義數(shù)據(jù). 將被傳遞給回調(diào)函數(shù)的最后一個(gè)參數(shù) @user_data
@begin: 回調(diào)生效區(qū)域的起始地址(包括)
@end: 回調(diào)生效區(qū)域的結(jié)束地址(包括)
注意 1: 只有回調(diào)的地址在[@begin, @end]中才會(huì)調(diào)用回調(diào)
注意 2: 如果 @begin > @end, 每當(dāng)觸發(fā)此hook類型時(shí)都會(huì)調(diào)用回調(diào)
@...: 變量參數(shù) (取決于 @type)
注意: 如果 @type = UC_HOOK_INSN, 這里是指令I(lǐng)D (如: UC_X86_INS_OUT)
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
void *user_data, uint64_t begin, uint64_t end, ...)
{
int ret = UC_ERR_OK;
int i = 0;
struct hook *hook = calloc(1, sizeof(struct hook));
if (hook == NULL) {
return UC_ERR_NOMEM;
}
hook->begin = begin;
hook->end = end;
hook->type = type;
hook->callback = callback;
hook->user_data = user_data;
hook->refs = 0;
*hh = (uc_hook)hook;
// UC_HOOK_INSN 有一個(gè)額外參數(shù):指令I(lǐng)D
if (type & UC_HOOK_INSN) {
va_list valist;
va_start(valist, end);
hook->insn = va_arg(valist, int);
va_end(valist);
if (uc->insn_hook_validate) {
if (! uc->insn_hook_validate(hook->insn)) {
free(hook);
return UC_ERR_HOOK;
}
}
if (uc->hook_insert) {
if (list_insert(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) {
free(hook);
return UC_ERR_NOMEM;
}
} else {
if (list_append(&uc->hook[UC_HOOK_INSN_IDX], hook) == NULL) {
free(hook);
return UC_ERR_NOMEM;
}
}
hook->refs++;
return UC_ERR_OK;
}
while ((type >> i) > 0) {
if ((type >> i) & 1) {
if (i < UC_HOOK_MAX) {
if (uc->hook_insert) {
if (list_insert(&uc->hook[i], hook) == NULL) {
if (hook->refs == 0) {
free(hook);
}
return UC_ERR_NOMEM;
}
} else {
if (list_append(&uc->hook[i], hook) == NULL) {
if (hook->refs == 0) {
free(hook);
}
return UC_ERR_NOMEM;
}
}
hook->refs++;
}
}
i++;
}
if (hook->refs == 0) {
free(hook);
}
return ret;
}
使用示例:
#include <iostream>
#include <string>
#include "unicorn/unicorn.h"
using namespace std;
int syscall_abi[] = {
UC_X86_REG_RAX, UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX,
UC_X86_REG_R10, UC_X86_REG_R8, UC_X86_REG_R9
};
uint64_t vals[7] = { 200, 10, 11, 12, 13, 14, 15 };
void* ptrs[7];
void uc_perror(const char* func, uc_err err)
{
fprintf(stderr, "Error in %s(): %s\n", func, uc_strerror(err));
}
#define BASE 0x10000
// mov rax, 100; mov rdi, 1; mov rsi, 2; mov rdx, 3; mov r10, 4; mov r8, 5; mov r9, 6; syscall
#define CODE "\x48\xc7\xc0\x64\x00\x00\x00\x48\xc7\xc7\x01\x00\x00\x00\x48\xc7\xc6\x02\x00\x00\x00\x48\xc7\xc2\x03\x00\x00\x00\x49\xc7\xc2\x04\x00\x00\x00\x49\xc7\xc0\x05\x00\x00\x00\x49\xc7\xc1\x06\x00\x00\x00\x0f\x05"
void hook_syscall(uc_engine* uc, void* user_data)
{
int i;
uc_reg_read_batch(uc, syscall_abi, ptrs, 7);
printf("syscall: {");
for (i = 0; i < 7; i++) {
if (i != 0) printf(", ");
printf("%" PRIu64, vals[i]);
}
printf("}\n");
}
void hook_code(uc_engine* uc, uint64_t addr, uint32_t size, void* user_data)
{
printf("HOOK_CODE: 0x%" PRIx64 ", 0x%x\n", addr, size);
}
int main()
{
int i;
uc_hook sys_hook;
uc_err err;
uc_engine* uc;
for (i = 0; i < 7; i++) {
ptrs[i] = &vals[i];
}
if ((err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc))) {
uc_perror("uc_open", err);
return 1;
}
printf("reg_write_batch({200, 10, 11, 12, 13, 14, 15})\n");
if ((err = uc_reg_write_batch(uc, syscall_abi, ptrs, 7))) {
uc_perror("uc_reg_write_batch", err);
return 1;
}
memset(vals, 0, sizeof(vals));
if ((err = uc_reg_read_batch(uc, syscall_abi, ptrs, 7))) {
uc_perror("uc_reg_read_batch", err);
return 1;
}
printf("reg_read_batch = {");
for (i = 0; i < 7; i++) {
if (i != 0) printf(", ");
printf("%" PRIu64, vals[i]);
}
printf("}\n");
// syscall
printf("\n");
printf("running syscall shellcode\n");
if ((err = uc_hook_add(uc, &sys_hook, UC_HOOK_CODE, hook_syscall, NULL, 1, 0))) {
uc_perror("uc_hook_add", err);
return 1;
}
if ((err = uc_mem_map(uc, BASE, 0x1000, UC_PROT_ALL))) {
uc_perror("uc_mem_map", err);
return 1;
}
if ((err = uc_mem_write(uc, BASE, CODE, sizeof(CODE) - 1))) {
uc_perror("uc_mem_write", err);
return 1;
}
if ((err = uc_emu_start(uc, BASE, BASE + sizeof(CODE) - 1, 0, 0))) {
uc_perror("uc_emu_start", err);
return 1;
}
return 0;
}
輸出
對(duì)每條指令都進(jìn)行hook
uc_hook_del
uc_err uc_hook_del(uc_engine *uc, uc_hook hh);
刪除一個(gè)已注冊(cè)的hook事件
@uc: uc_open() 返回的句柄
@hh: uc_hook_add() 返回的句柄
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_hook_del(uc_engine *uc, uc_hook hh)
{
int i;
struct hook *hook = (struct hook *)hh;
for (i = 0; i < UC_HOOK_MAX; i++) {
if (list_remove(&uc->hook[i], (void *)hook)) {
if (--hook->refs == 0) {
free(hook);
break;
}
}
}
return UC_ERR_OK;
}
使用示例:
if ((err = uc_hook_add(uc, &sys_hook, UC_HOOK_CODE, hook_syscall, NULL, 1, 0))) {
uc_perror("uc_hook_add", err);
return 1;
}
if ((err = uc_hook_del(uc, &sys_hook))) {
uc_perror("uc_hook_del", err);
return 1;
}
uc_mem_map
uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
為模擬映射一塊內(nèi)存。
@uc: uc_open() 返回的句柄
@address: 要映射到的新內(nèi)存區(qū)域的起始地址襟交。這個(gè)地址必須與4KB對(duì)齊迈倍,否則將返回UC_ERR_ARG錯(cuò)誤。
@size: 要映射到的新內(nèi)存區(qū)域的大小捣域。這個(gè)大小必須是4KB的倍數(shù)啼染,否則將返回UC_ERR_ARG錯(cuò)誤。
@perms: 新映射區(qū)域的權(quán)限竟宋。參數(shù)必須是UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC或這些的組合提完,否則返回UC_ERR_ARG錯(cuò)誤。
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms)
{
uc_err res;
if (uc->mem_redirect) {
address = uc->mem_redirect(address);
}
res = mem_map_check(uc, address, size, perms); //內(nèi)存安全檢查
if (res)
return res;
return mem_map(uc, address, size, perms, uc->memory_map(uc, address, size, perms));
}
使用示例同uc_hook_add丘侠。
uc_mem_map_ptr
uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr);
在模擬中映射現(xiàn)有的主機(jī)內(nèi)存徒欣。
@uc: uc_open() 返回的句柄
@address: 要映射到的新內(nèi)存區(qū)域的起始地址。這個(gè)地址必須與4KB對(duì)齊蜗字,否則將返回UC_ERR_ARG錯(cuò)誤打肝。
@size: 要映射到的新內(nèi)存區(qū)域的大小。這個(gè)大小必須是4KB的倍數(shù)挪捕,否則將返回UC_ERR_ARG錯(cuò)誤粗梭。
@perms: 新映射區(qū)域的權(quán)限。參數(shù)必須是UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC或這些的組合级零,否則返回UC_ERR_ARG錯(cuò)誤断医。
@ptr: 指向支持新映射內(nèi)存的主機(jī)內(nèi)存的指針。映射的主機(jī)內(nèi)存的大小應(yīng)該與size的大小相同或更大奏纪,并且至少使用PROT_READ | PROT_WRITE進(jìn)行映射鉴嗤,否則不定義映射。
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr)
{
uc_err res;
if (ptr == NULL)
return UC_ERR_ARG;
if (uc->mem_redirect) {
address = uc->mem_redirect(address);
}
res = mem_map_check(uc, address, size, perms); //內(nèi)存安全檢查
if (res)
return res;
return mem_map(uc, address, size, UC_PROT_ALL, uc->memory_map_ptr(uc, address, size, perms, ptr));
}
使用示例同uc_mem_map
uc_mem_unmap
uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size);
取消對(duì)模擬內(nèi)存區(qū)域的映射
@uc: uc_open() 返回的句柄
@address: 要映射到的新內(nèi)存區(qū)域的起始地址序调。這個(gè)地址必須與4KB對(duì)齊醉锅,否則將返回UC_ERR_ARG錯(cuò)誤。
@size: 要映射到的新內(nèi)存區(qū)域的大小发绢。這個(gè)大小必須是4KB的倍數(shù)硬耍,否則將返回UC_ERR_ARG錯(cuò)誤垄琐。
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size)
{
MemoryRegion *mr;
uint64_t addr;
size_t count, len;
if (size == 0)
// 沒有要取消映射的區(qū)域
return UC_ERR_OK;
// 地址必須對(duì)齊到 uc->target_page_size
if ((address & uc->target_page_align) != 0)
return UC_ERR_ARG;
// 大小必須是 uc->target_page_size 的倍數(shù)
if ((size & uc->target_page_align) != 0)
return UC_ERR_ARG;
if (uc->mem_redirect) {
address = uc->mem_redirect(address);
}
// 檢查用戶請(qǐng)求的整個(gè)塊是否被映射
if (!check_mem_area(uc, address, size))
return UC_ERR_NOMEM;
// 如果這個(gè)區(qū)域跨越了相鄰的區(qū)域,可能需要分割區(qū)域
addr = address;
count = 0;
while(count < size) {
mr = memory_mapping(uc, addr);
len = (size_t)MIN(size - count, mr->end - addr);
if (!split_region(uc, mr, addr, len, true))
return UC_ERR_NOMEM;
// 取消映射
mr = memory_mapping(uc, addr);
if (mr != NULL)
uc->memory_unmap(uc, mr);
count += len;
addr += len;
}
return UC_ERR_OK;
}
使用示例:
if ((err = uc_mem_map(uc, BASE, 0x1000, UC_PROT_ALL))) {
uc_perror("uc_mem_map", err);
return 1;
}
if ((err = uc_mem_unmap(uc, BASE, 0x1000))) {
uc_perror("uc_mem_unmap", err);
return 1;
}
uc_mem_protect
uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
設(shè)置模擬內(nèi)存的權(quán)限
@uc: uc_open() 返回的句柄
@address: 要映射到的新內(nèi)存區(qū)域的起始地址经柴。這個(gè)地址必須與4KB對(duì)齊狸窘,否則將返回UC_ERR_ARG錯(cuò)誤。
@size: 要映射到的新內(nèi)存區(qū)域的大小坯认。這個(gè)大小必須是4KB的倍數(shù)朦前,否則將返回UC_ERR_ARG錯(cuò)誤。
@perms: 映射區(qū)域的新權(quán)限鹃操。參數(shù)必須是UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC或這些的組合,否則返回UC_ERR_ARG錯(cuò)誤春哨。
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size, uint32_t perms)
{
MemoryRegion *mr;
uint64_t addr = address;
size_t count, len;
bool remove_exec = false;
if (size == 0)
// trivial case, no change
return UC_ERR_OK;
// address must be aligned to uc->target_page_size
if ((address & uc->target_page_align) != 0)
return UC_ERR_ARG;
// size must be multiple of uc->target_page_size
if ((size & uc->target_page_align) != 0)
return UC_ERR_ARG;
// check for only valid permissions
if ((perms & ~UC_PROT_ALL) != 0)
return UC_ERR_ARG;
if (uc->mem_redirect) {
address = uc->mem_redirect(address);
}
// check that user's entire requested block is mapped
if (!check_mem_area(uc, address, size))
return UC_ERR_NOMEM;
// Now we know entire region is mapped, so change permissions
// We may need to split regions if this area spans adjacent regions
addr = address;
count = 0;
while(count < size) {
mr = memory_mapping(uc, addr);
len = (size_t)MIN(size - count, mr->end - addr);
if (!split_region(uc, mr, addr, len, false))
return UC_ERR_NOMEM;
mr = memory_mapping(uc, addr);
// will this remove EXEC permission?
if (((mr->perms & UC_PROT_EXEC) != 0) && ((perms & UC_PROT_EXEC) == 0))
remove_exec = true;
mr->perms = perms;
uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0);
count += len;
addr += len;
}
// if EXEC permission is removed, then quit TB and continue at the same place
if (remove_exec) {
uc->quit_request = true;
uc_emu_stop(uc);
}
return UC_ERR_OK;
}
使用示例:
if ((err = uc_mem_protect(uc, BASE, 0x1000, UC_PROT_ALL))) { //可讀可寫可執(zhí)行
uc_perror("uc_mem_protect", err);
return 1;
}
uc_mem_regions
uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count);
檢索由 uc_mem_map() 和 uc_mem_map_ptr() 映射的內(nèi)存的信息荆隘。
這個(gè)API為@regions分配內(nèi)存,用戶之后必須通過free()釋放這些內(nèi)存來避免內(nèi)存泄漏赴背。
@uc: uc_open() 返回的句柄
@regions: 指向 uc_mem_region 結(jié)構(gòu)體的數(shù)組的指針. 由Unicorn申請(qǐng)椰拒,必須通過uc_free()釋放這些內(nèi)存
@count: 指向@regions中包含的uc_mem_region結(jié)構(gòu)體的數(shù)量的指針
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼分析
uint32_t uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count)
{
uint32_t i;
uc_mem_region *r = NULL;
*count = uc->mapped_block_count;
if (*count) {
r = g_malloc0(*count * sizeof(uc_mem_region));
if (r == NULL) {
// 內(nèi)存不足
return UC_ERR_NOMEM;
}
}
for (i = 0; i < *count; i++) {
r[i].begin = uc->mapped_blocks[i]->addr;
r[i].end = uc->mapped_blocks[i]->end - 1;
r[i].perms = uc->mapped_blocks[i]->perms;
}
*regions = r;
return UC_ERR_OK;
}
使用示例:
#include <iostream>
#include <string>
#include "unicorn/unicorn.h"
using namespace std;
int main()
{
uc_err err;
uc_engine* uc;
if ((err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc))) {
uc_perror("uc_open", err);
return 1;
}
if ((err = uc_mem_map(uc, BASE, 0x1000, UC_PROT_ALL))) {
uc_perror("uc_mem_map", err);
return 1;
}
uc_mem_region *region;
uint32_t count;
if ((err = uc_mem_regions(uc, ®ion, &count))) {
uc_perror("uc_mem_regions", err);
return 1;
}
cout << "起始地址: 0x" << hex << region->begin << " 結(jié)束地址: 0x" << hex << region->end << " 內(nèi)存權(quán)限: " <<region->perms << " 已申請(qǐng)內(nèi)存塊數(shù): " << count << endl;
if ((err = uc_free(region))) { ////注意釋放內(nèi)存
uc_perror("uc_free", err);
return 1;
}
return 0;
}
輸出
uc_free
uc_err uc_free(void *mem);
釋放由 uc_context_alloc 和 uc_mem_regions 申請(qǐng)的內(nèi)存
@mem: 由uc_context_alloc (返回 *context), 或由 uc_mem_regions (返回 *regions)申請(qǐng)的內(nèi)存
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_free(void *mem)
{
g_free(mem);
return UC_ERR_OK;
}
void g_free(gpointer ptr)
{
free(ptr);
}
使用示例同uc_mem_regions
uc_context_alloc
uc_err uc_context_alloc(uc_engine *uc, uc_context **context);
分配一個(gè)可以與uc_context_{save,restore}一起使用的區(qū)域來執(zhí)行CPU上下文的快速保存/回滾,包括寄存器和內(nèi)部元數(shù)據(jù)凰荚。上下文不能在具有不同架構(gòu)或模式的引擎實(shí)例之間共享燃观。
@uc: uc_open() 返回的句柄
@context: 指向uc_engine*的指針。當(dāng)這個(gè)函數(shù)成功返回時(shí)便瑟,將使用指向新上下文的指針更新它缆毁。之后必須使用uc_free()釋放這些分配的內(nèi)存。
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_context_alloc(uc_engine *uc, uc_context **context)
{
struct uc_context **_context = context;
size_t size = cpu_context_size(uc->arch, uc->mode);
*_context = malloc(size + sizeof(uc_context));
if (*_context) {
(*_context)->size = size;
return UC_ERR_OK;
} else {
return UC_ERR_NOMEM;
}
}
使用示例
#include <iostream>
#include <string>
#include "unicorn/unicorn.h"
using namespace std;
#define ADDRESS 0x1000
#define X86_CODE32_INC "\x40" // INC eax
int main()
{
uc_engine* uc;
uc_context* context;
uc_err err;
int r_eax = 0x1; // EAX 寄存器
printf("===================================\n");
printf("Save/restore CPU context in opaque blob\n");
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
if (err) {
printf("Failed on uc_open() with error returned: %u\n", err);
return 0;
}
uc_mem_map(uc, ADDRESS, 8 * 1024, UC_PROT_ALL);
if (uc_mem_write(uc, ADDRESS, X86_CODE32_INC, sizeof(X86_CODE32_INC) - 1)) {
printf("Failed to write emulation code to memory, quit!\n");
return 0;
}
// 初始化寄存器
uc_reg_write(uc, UC_X86_REG_EAX, &r_eax);
printf(">>> Running emulation for the first time\n");
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INC) - 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_EAX, &r_eax);
printf(">>> EAX = 0x%x\n", r_eax);
// 申請(qǐng)并保存 CPU 上下文
printf(">>> Saving CPU context\n");
err = uc_context_alloc(uc, &context);
if (err) {
printf("Failed on uc_context_alloc() with error returned: %u\n", err);
return 0;
}
err = uc_context_save(uc, context);
if (err) {
printf("Failed on uc_context_save() with error returned: %u\n", err);
return 0;
}
printf(">>> Running emulation for the second time\n");
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INC) - 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_EAX, &r_eax);
printf(">>> EAX = 0x%x\n", r_eax);
// 恢復(fù) CPU 上下文
err = uc_context_restore(uc, context);
if (err) {
printf("Failed on uc_context_restore() with error returned: %u\n", err);
return 0;
}
printf(">>> CPU context restored. Below is the CPU context\n");
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
printf(">>> EAX = 0x%x\n", r_eax);
// 釋放 CPU 上下文
err = uc_free(context);
if (err) {
printf("Failed on uc_free() with error returned: %u\n", err);
return 0;
}
uc_close(uc);
return 0;
}
輸出
uc_context_save
uc_err uc_context_save(uc_engine *uc, uc_context *context);
保存當(dāng)前CPU上下文
@uc: uc_open() 返回的句柄
@context: uc_context_alloc() 返回的句柄
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_context_save(uc_engine *uc, uc_context *context)
{
struct uc_context *_context = context;
memcpy(_context->data, uc->cpu->env_ptr, _context->size);
return UC_ERR_OK;
}
使用示例同uc_context_alloc()
uc_context_restore
uc_err uc_context_restore(uc_engine *uc, uc_context *context);
恢復(fù)已保存的CPU上下文
@uc: uc_open() 返回的句柄
@context: uc_context_alloc() 返回并且已使用 uc_context_save 保存的句柄
@return 成功則返回UC_ERR_OK , 否則返回 uc_err 枚舉的其他錯(cuò)誤類型
源碼實(shí)現(xiàn)
uc_err uc_context_restore(uc_engine *uc, uc_context *context)
{
struct uc_context *_context = context;
memcpy(uc->cpu->env_ptr, _context->data, _context->size);
return UC_ERR_OK;
}
使用示例同uc_context_alloc()
uc_context_size
size_t uc_context_size(uc_engine *uc);
返回存儲(chǔ)cpu上下文所需的大小到涂〖箍颍可以用來分配一個(gè)緩沖區(qū)來包含cpu上下文,并直接調(diào)用uc_context_save践啄。
@uc: uc_open() 返回的句柄
@return 存儲(chǔ)cpu上下文所需的大小浇雹,類型為 size_t.
源碼實(shí)現(xiàn)
size_t uc_context_size(uc_engine *uc)
{
return cpu_context_size(uc->arch, uc->mode);
}
static size_t cpu_context_size(uc_arch arch, uc_mode mode)
{
switch (arch) {
#ifdef UNICORN_HAS_M68K
case UC_ARCH_M68K: return M68K_REGS_STORAGE_SIZE;
#endif
#ifdef UNICORN_HAS_X86
case UC_ARCH_X86: return X86_REGS_STORAGE_SIZE;
#endif
#ifdef UNICORN_HAS_ARM
case UC_ARCH_ARM: return mode & UC_MODE_BIG_ENDIAN ? ARM_REGS_STORAGE_SIZE_armeb : ARM_REGS_STORAGE_SIZE_arm;
#endif
#ifdef UNICORN_HAS_ARM64
case UC_ARCH_ARM64: return mode & UC_MODE_BIG_ENDIAN ? ARM64_REGS_STORAGE_SIZE_aarch64eb : ARM64_REGS_STORAGE_SIZE_aarch64;
#endif
#ifdef UNICORN_HAS_MIPS
case UC_ARCH_MIPS:
if (mode & UC_MODE_MIPS64) {
if (mode & UC_MODE_BIG_ENDIAN) {
return MIPS64_REGS_STORAGE_SIZE_mips64;
} else {
return MIPS64_REGS_STORAGE_SIZE_mips64el;
}
} else {
if (mode & UC_MODE_BIG_ENDIAN) {
return MIPS_REGS_STORAGE_SIZE_mips;
} else {
return MIPS_REGS_STORAGE_SIZE_mipsel;
}
}
#endif
#ifdef UNICORN_HAS_SPARC
case UC_ARCH_SPARC: return mode & UC_MODE_SPARC64 ? SPARC64_REGS_STORAGE_SIZE : SPARC_REGS_STORAGE_SIZE;
#endif
default: return 0;
}
}
使用示例同uc_context_alloc()