unicorn

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)如下

image.png

VS打開unicorn.sln項(xiàng)目文件章喉,解決方案自動(dòng)載入這些

image.png

如果都需要的話,直接編譯就好了身坐,只需要其中幾種秸脱,則右鍵解決方案->屬性->配置屬性 如下

image.png

生成選項(xiàng)中勾選你需要的支持項(xiàng)即可

項(xiàng)目編譯屬性為:

  1. 使用多字節(jié)字符集
  2. 不使用預(yù)編譯頭
  3. 附加選項(xiàng) /wd4018 /wd4244 /wd4267
  4. 預(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)在此處


image.png

事實(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)目的主目錄下

image.png

在VS解決方案中吮便,頭文件添加現(xiàn)有項(xiàng)unicorn.h,資源文件中添加unicorn.lib幢踏,重新生成解決方案

image.png

接下來測(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é)果如下

image.png

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;
}

輸出:

image.png

得到版本號(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;
}

輸出:

image.png

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;
}

輸出

image.png

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;
}

輸出

image.png

架構(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;
}

輸出

image.png

無錯(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;
}

輸出

image.png

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, &regid, (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;
}

輸出

image.png

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, &regid, &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;
}

輸出

image.png

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;
}

輸出

image.png

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;
}

輸出

image.png

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;
}

輸出

image.png

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;
}

輸出

image.png

對(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, &region, &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;
}

輸出

image.png

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;
}

輸出

image.png

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屿讽,隨后出現(xiàn)的幾起案子昭灵,更是在濱河造成了極大的恐慌,老刑警劉巖伐谈,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烂完,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡衩婚,警方通過查閱死者的電腦和手機(jī)窜护,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來非春,“玉大人柱徙,你說我怎么就攤上這事缓屠。” “怎么了护侮?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵敌完,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我羊初,道長(zhǎng)滨溉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任长赞,我火速辦了婚禮晦攒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘得哆。我一直安慰自己脯颜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布贩据。 她就那樣靜靜地躺著栋操,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饱亮。 梳的紋絲不亂的頭發(fā)上矾芙,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音近上,去河邊找鬼剔宪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛壹无,可吹牛的內(nèi)容都是我干的歼跟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼格遭,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼哈街!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拒迅,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤骚秦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后璧微,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體作箍,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年前硫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胞得。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屹电,死狀恐怖阶剑,靈堂內(nèi)的尸體忽然破棺而出跃巡,到底是詐尸還是另有隱情,我是刑警寧澤牧愁,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布素邪,位于F島的核電站,受9級(jí)特大地震影響猪半,放射性物質(zhì)發(fā)生泄漏兔朦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一磨确、第九天 我趴在偏房一處隱蔽的房頂上張望沽甥。 院中可真熱鬧,春花似錦乏奥、人聲如沸安接。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至歇式,卻和暖如春驶悟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背材失。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工痕鳍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人龙巨。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓笼呆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親旨别。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诗赌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 參考鏈接 https://bbs.pediy.com/thread-224330.htmhttp://eterna...
    SamiraG閱讀 1,614評(píng)論 0 0
  • 一、目標(biāo)[http://91fans.com.cn/post/disollvmone/#_%E4%B8%80%E7...
    fenfei331閱讀 1,492評(píng)論 1 3
  • 1秸弛、安裝python依賴:centos里面是-devel铭若,如果在ubuntu下安裝則要改成-dev, yum -y...
    珍惜Any閱讀 1,418評(píng)論 0 0
  • 推薦指數(shù): 6.0 書籍主旨關(guān)鍵詞:特權(quán)递览、焦點(diǎn)叼屠、注意力、語言聯(lián)想绞铃、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析镜雨,社會(huì)...
    Jenaral閱讀 5,720評(píng)論 0 5
  • 昨天,在回家的路上儿捧,坐在車?yán)镉圃沼圃盏乜粗摹度龉衬墓适隆芳晕耄冶焕锩娴膬?nèi)容深深吸引住了挑宠,盡管上學(xué)時(shí)...
    夜闌曉語閱讀 3,785評(píng)論 2 9