unicorn 使用介绍

本篇介绍

unicorn是一个轻量级,多平台,多架构的CPU模拟器框架。使用Unicorn可以模拟执行执行,并且也支持指令级别的hook。本篇看下unicorn的用法。

模拟指令介绍

代码如下:

#include <iostream>

#include <unicorn/unicorn.h>

// code to be emulated
#define X86_CODE32 "\x41\x4a" // INC ecx; DEC edx

// memory address where emulation starts
#define ADDRESS 0x1000000

int main(int argc, char **argv, char **envp)
{
  uc_engine *uc;
  uc_err err;
  int r_ecx = 0x1234;     // ECX register
  int r_edx = 0x7890;     // EDX register

  std::cout<<"Emulate i386 code"<<std::endl;

  // Initialize emulator in X86-32bit mode
  err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
  if (err != UC_ERR_OK) {
    std::cout<<"Failed on uc_open() with error returned:"<<err<<std::endl;
    return -1;
  }

  // map 2MB memory for this emulation
  uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);

  // write machine code to be emulated to memory
  if (uc_mem_write(uc, ADDRESS, X86_CODE32, sizeof(X86_CODE32) - 1)) {
    std::cout<<"Failed to write emulation code to memory, quit!"<<err<<std::endl;
    return -1;
  }
  
  std::cout<<"Before Emulation done"<<std::endl;
  std::cout<<">>> ECX = "<<std::hex << r_ecx << std::endl;
  std::cout<<">>> EDX = "<<std::hex<< r_edx<< std::endl;

  // initialize machine registers
  uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
  uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);

  // emulate code in infinite time & unlimited instructions
  err=uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0);
  if (err) {
    std::cout<<"Failed on uc_emu_start() with error returned"<< err << uc_strerror(err) << std::endl;
  }

  // now print out some registers
  std::cout<<"Emulation done. Below is the CPU context"<<std::endl;

  uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx);
  uc_reg_read(uc, UC_X86_REG_EDX, &r_edx);
  std::cout<<">>> ECX = "<<std::hex << r_ecx << std::endl;
  std::cout<<">>> EDX = "<<std::hex<< r_edx<< std::endl;

  uc_close(uc);

  return 0;
}

执行结果如下:

Emulate i386 code
Before Emulation done
>>> ECX = 1234
>>> EDX = 7890
Emulation done. Below is the CPU context
>>> ECX = 1235
>>> EDX = 788f

从上面的例子总结如下:

uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc);

负责创建新的 Unicorn实例。

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

为模拟映射一块内存,地址首地址和长度都需要是0x1000的整倍数,在模拟过程中,左右的CPU操作都只能访问此内存。

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

在内存中写入一段字节码。

uc_err uc_reg_write(uc_engine *uc, int regid, const void *value);

将值写入寄存器。

uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t
timeout, size_t count);

在指定的时间内模拟机器码。

uc_err uc_reg_read(uc_engine *uc, int regid, void *value);

读取寄存器的值。

hook指令介绍

unicorn也支持多种类型的hook,类型如下:

typedef enum uc_hook_type {
    // Hook all interrupt/syscall events
    UC_HOOK_INTR = 1 << 0,
    // Hook a particular instruction - only a very small subset of instructions
    // supported here
    UC_HOOK_INSN = 1 << 1,
    // Hook a range of code
    UC_HOOK_CODE = 1 << 2,
    // Hook basic blocks
    UC_HOOK_BLOCK = 1 << 3,
    // Hook for memory read on unmapped memory
    UC_HOOK_MEM_READ_UNMAPPED = 1 << 4,
    // Hook for invalid memory write events
    UC_HOOK_MEM_WRITE_UNMAPPED = 1 << 5,
    // Hook for invalid memory fetch for execution events
    UC_HOOK_MEM_FETCH_UNMAPPED = 1 << 6,
    // Hook for memory read on read-protected memory
    UC_HOOK_MEM_READ_PROT = 1 << 7,
    // Hook for memory write on write-protected memory
    UC_HOOK_MEM_WRITE_PROT = 1 << 8,
    // Hook for memory fetch on non-executable memory
    UC_HOOK_MEM_FETCH_PROT = 1 << 9,
    // Hook memory read events.
    UC_HOOK_MEM_READ = 1 << 10,
    // Hook memory write events.
    UC_HOOK_MEM_WRITE = 1 << 11,
    // Hook memory fetch for execution events
    UC_HOOK_MEM_FETCH = 1 << 12,
    // Hook memory read events, but only successful access.
    // The callback will be triggered after successful read.
    UC_HOOK_MEM_READ_AFTER = 1 << 13,
    // Hook invalid instructions exceptions.
    UC_HOOK_INSN_INVALID = 1 << 14,
    // Hook on new edge generation. Could be useful in program analysis.
    //
    // NOTE: This is different from UC_HOOK_BLOCK in 2 ways:
    //       1. The hook is called before executing code.
    //       2. The hook is only called when generation is triggered.
    UC_HOOK_EDGE_GENERATED = 1 << 15,
    // Hook on specific tcg op code. The usage of this hook is similar to
    // UC_HOOK_INSN.
    UC_HOOK_TCG_OPCODE = 1 << 16,
} uc_hook_type;

看一个例子如下:

#include <string.h>

#include "unicorn/unicorn.h"

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_code(uc_engine *uc, uint64_t address, uint32_t size,
                      void *user_data)
{
    printf("HOOK_CODE: 0x%" PRIx64 ", 0x%x\n", address, 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 shellcode\n");
    if ((err = uc_hook_add(uc, &sys_hook, UC_HOOK_CODE, hook_code, 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;
    }
        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");
    return 0; 
}

对应的输出如下:

reg_write_batch({200, 10, 11, 12, 13, 14, 15})
reg_read_batch = {200, 10, 11, 12, 13, 14, 15}

running shellcode
HOOK_CODE: 0x10000, 0x7
HOOK_CODE: 0x10007, 0x7
HOOK_CODE: 0x1000e, 0x7
HOOK_CODE: 0x10015, 0x7
HOOK_CODE: 0x1001c, 0x7
HOOK_CODE: 0x10023, 0x7
HOOK_CODE: 0x1002a, 0x7
HOOK_CODE: 0x10031, 0x2
reg_read_batch = {100, 1, 2, 3, 4, 5, 6}

此时每个指令执行时,都会被hook到。
看下如下函数:

uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
        void *user_data, uint64_t begin, uint64_t end, ...);

注册hook事件的回调,当hook事件被触发将会进行回调。

unicorn与Android

再看一个unicorn调试Android的例子,看看如何在unicorn中模拟arm环境,执行Android上的so。首先写一个helloworld。

#include <stdio.h>

int main() {
  printf("Hello World\n");
  return 0;
}

使用交叉编译成android上的bin文件。查看file信息:

a.bin: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /system/bin/linker64, not stripped

我目前在macos上,肯定是执行不了这个bin,那接下来就需要让unicorn加载该代码,也就是把整个so都读到内存中。这儿需要考虑到代码里调用了printf这样libc的函数,unicorn里是访问不到的,那就需要跳过该地址,具体操作就是遇到执行printf的指令,也就是lr,那就直接修改pc为下一条指令。

这儿会有一个小问题,让unicorn从哪儿开始执行呢?肯定不是从so的0地址开始执行,entry_point算是一个地址,可是我们更希望是从main开始执行,那如何做到呢?其实很简单,找一个反编译软件看下就可以了,我这边查到的结果如下:


WeChatWorkScreenshot_e2f3fe94-2922-4412-9cf8-0977ff8ae8e2.png

这样偏移就有了。接下来为了让指令显示更清晰下,就顺便反编译了下,这儿借助capstone就做到了。最后的执行结果如下:

code is 0xd10083ff
0x0:    sub sp, sp, #0x20
    op_count: 3
        operands[0].type: REG = sp
        operands[0].access: WRITE
        operands[1].type: REG = sp
        operands[1].access: READ
        operands[2].type: IMM = 0x20
        operands[2].access: READ
    Registers read: sp
    Registers modified: sp

HOOK_CODE: 0x65c, 0x4
code is 0xa9017bfd
0x0:    stp x29, x30, [sp, #0x10]
    op_count: 3
        operands[0].type: REG = fp
        operands[0].access: READ
        operands[1].type: REG = lr
        operands[1].access: READ
        operands[2].type: MEM
            operands[2].mem.base: REG = sp
            operands[2].mem.disp: 0x10
        operands[2].access: READ | WRITE
    Registers read: fp lr sp

HOOK_CODE: 0x660, 0x4
code is 0x910043fd
0x0:    add x29, sp, #0x10
    op_count: 3
        operands[0].type: REG = fp
        operands[0].access: WRITE
        operands[1].type: REG = sp
        operands[1].access: READ
        operands[2].type: IMM = 0x10
        operands[2].access: READ
    Registers read: sp
    Registers modified: fp

HOOK_CODE: 0x664, 0x4
code is 0x2a1f03e8
0x0:    mov w8, wzr
    op_count: 2
        operands[0].type: REG = w8
        operands[0].access: WRITE
        operands[1].type: REG = wzr
        operands[1].access: READ
    Registers read: wzr
    Registers modified: w8

HOOK_CODE: 0x668, 0x4
code is 0xb9000be8
0x0:    str w8, [sp, #8]
    op_count: 2
        operands[0].type: REG = w8
        operands[0].access: READ
        operands[1].type: MEM
            operands[1].mem.base: REG = sp
            operands[1].mem.disp: 0x8
        operands[1].access: READ | WRITE
    Registers read: w8 sp

HOOK_CODE: 0x66c, 0x4
code is 0xb81fc3bf
0x0:    stur    wzr, [x29, #-4]
    op_count: 2
        operands[0].type: REG = wzr
        operands[0].access: READ
        operands[1].type: MEM
            operands[1].mem.base: REG = fp
            operands[1].mem.disp: 0xfffffffc
        operands[1].access: READ | WRITE
    Registers read: wzr fp

HOOK_CODE: 0x670, 0x4
code is 0xf0ffffe0
0x0:    adrp    x0, #0xfffffffffffff000
    op_count: 2
        operands[0].type: REG = x0
        operands[0].access: WRITE
        operands[1].type: IMM = 0xfffffffffffff000
        operands[1].access: READ
    Registers modified: x0

HOOK_CODE: 0x674, 0x4
code is 0x91142000
0x0:    add x0, x0, #0x508
    op_count: 3
        operands[0].type: REG = x0
        operands[0].access: WRITE
        operands[1].type: REG = x0
        operands[1].access: READ
        operands[2].type: IMM = 0x508
        operands[2].access: READ
    Registers read: x0
    Registers modified: x0

HOOK_CODE: 0x678, 0x4
code is 0x94000015
0x0:    bl  #0x54
    op_count: 1
        operands[0].type: IMM = 0x54
        operands[0].access: READ
    Registers modified: lr

HOOK_CODE: 0x67c, 0x4
skip it
code is 0xb9400be0
0x0:    ldr w0, [sp, #8]
    op_count: 2
        operands[0].type: REG = w0
        operands[0].access: WRITE
        operands[1].type: MEM
            operands[1].mem.base: REG = sp
            operands[1].mem.disp: 0x8
        operands[1].access: READ
    Registers read: sp
    Registers modified: w0

HOOK_CODE: 0x680, 0x4
code is 0xa9417bfd
0x0:    ldp x29, x30, [sp, #0x10]
    op_count: 3
        operands[0].type: REG = fp
        operands[0].access: WRITE
        operands[1].type: REG = lr
        operands[1].access: WRITE
        operands[2].type: MEM
            operands[2].mem.base: REG = sp
            operands[2].mem.disp: 0x10
        operands[2].access: READ
    Registers read: sp
    Registers modified: fp lr

HOOK_CODE: 0x684, 0x4
code is 0x910083ff
0x0:    add sp, sp, #0x20
    op_count: 3
        operands[0].type: REG = sp
        operands[0].access: WRITE
        operands[1].type: REG = sp
        operands[1].access: READ
        operands[2].type: IMM = 0x20
        operands[2].access: READ
    Registers read: sp
    Registers modified: sp

HOOK_CODE: 0x688, 0x4

最后看下完整代码:

#include <string.h>
#include <stdlib.h>

#include "unicorn/unicorn.h"
#include <capstone/platform.h>
#include <capstone/capstone.h>

#define BASE 0x0

static csh handle;

uint64_t skip_list[] = {0x67c,};

void uc_perror(const char* func, uc_err err)
{
    fprintf(stderr, "Error in %s(): %s\n", func, uc_strerror(err));
}


static void print_string_hex(const char *comment, unsigned char *str, size_t len)
{
    unsigned char *c;

    printf("%s", comment);
    for (c = str; c < str + len; c++) {
        printf("0x%02x ", *c & 0xff);
    }

    printf("\n");
}

static void print_insn_detail(cs_insn *ins)
{
    cs_arm64 *arm64;
    int i;
    cs_regs regs_read, regs_write;
    unsigned char regs_read_count, regs_write_count;
    unsigned char access;

    // detail can be NULL if SKIPDATA option is turned ON
    if (ins->detail == NULL)
        return;

    arm64 = &(ins->detail->arm64);
    if (arm64->op_count)
        printf("\top_count: %u\n", arm64->op_count);

    for (i = 0; i < arm64->op_count; i++) {
        cs_arm64_op *op = &(arm64->operands[i]);
        switch(op->type) {
            default:
                break;
            case ARM64_OP_REG:
                printf("\t\toperands[%u].type: REG = %s\n", i, cs_reg_name(handle, op->reg));
                break;
            case ARM64_OP_IMM:
                printf("\t\toperands[%u].type: IMM = 0x%" PRIx64 "\n", i, op->imm);
                break;
            case ARM64_OP_FP:
#if defined(_KERNEL_MODE)
                // Issue #681: Windows kernel does not support formatting float point
                printf("\t\toperands[%u].type: FP = <float_point_unsupported>\n", i);
#else
                printf("\t\toperands[%u].type: FP = %f\n", i, op->fp);
#endif
                break;
            case ARM64_OP_MEM:
                printf("\t\toperands[%u].type: MEM\n", i);
                if (op->mem.base != ARM64_REG_INVALID)
                    printf("\t\t\toperands[%u].mem.base: REG = %s\n", i, cs_reg_name(handle, op->mem.base));
                if (op->mem.index != ARM64_REG_INVALID)
                    printf("\t\t\toperands[%u].mem.index: REG = %s\n", i, cs_reg_name(handle, op->mem.index));
                if (op->mem.disp != 0)
                    printf("\t\t\toperands[%u].mem.disp: 0x%x\n", i, op->mem.disp);

                break;
            case ARM64_OP_CIMM:
                printf("\t\toperands[%u].type: C-IMM = %u\n", i, (int)op->imm);
                break;
            case ARM64_OP_REG_MRS:
                printf("\t\toperands[%u].type: REG_MRS = 0x%x\n", i, op->reg);
                break;
            case ARM64_OP_REG_MSR:
                printf("\t\toperands[%u].type: REG_MSR = 0x%x\n", i, op->reg);
                break;
            case ARM64_OP_PSTATE:
                printf("\t\toperands[%u].type: PSTATE = 0x%x\n", i, op->pstate);
                break;
            case ARM64_OP_SYS:
                printf("\t\toperands[%u].type: SYS = 0x%x\n", i, op->sys);
                break;
            case ARM64_OP_PREFETCH:
                printf("\t\toperands[%u].type: PREFETCH = 0x%x\n", i, op->prefetch);
                break;
            case ARM64_OP_BARRIER:
                printf("\t\toperands[%u].type: BARRIER = 0x%x\n", i, op->barrier);
                break;
        }

        access = op->access;
        switch(access) {
            default:
                break;
            case CS_AC_READ:
                printf("\t\toperands[%u].access: READ\n", i);
                break;
            case CS_AC_WRITE:
                printf("\t\toperands[%u].access: WRITE\n", i);
                break;
            case CS_AC_READ | CS_AC_WRITE:
                printf("\t\toperands[%u].access: READ | WRITE\n", i);
                break;
        }

        if (op->shift.type != ARM64_SFT_INVALID &&
                op->shift.value)
            printf("\t\t\tShift: type = %u, value = %u\n",
                    op->shift.type, op->shift.value);

        if (op->ext != ARM64_EXT_INVALID)
            printf("\t\t\tExt: %u\n", op->ext);

        if (op->vas != ARM64_VAS_INVALID)
            printf("\t\t\tVector Arrangement Specifier: 0x%x\n", op->vas);

        if (op->vector_index != -1)
            printf("\t\t\tVector Index: %u\n", op->vector_index);
    }

    if (arm64->update_flags)
        printf("\tUpdate-flags: True\n");

    if (arm64->writeback)
        printf("\tWrite-back: %s\n", arm64->post_index ? "Post" : "Pre");

    if (arm64->cc)
        printf("\tCode-condition: %u\n", arm64->cc);

    // Print out all registers accessed by this instruction (either implicit or explicit)
    if (!cs_regs_access(handle, ins,
                regs_read, &regs_read_count,
                regs_write, &regs_write_count)) {
        if (regs_read_count) {
            printf("\tRegisters read:");
            for(i = 0; i < regs_read_count; i++) {
                printf(" %s", cs_reg_name(handle, regs_read[i]));
            }
            printf("\n");
        }

        if (regs_write_count) {
            printf("\tRegisters modified:");
            for(i = 0; i < regs_write_count; i++) {
                printf(" %s", cs_reg_name(handle, regs_write[i]));
            }
            printf("\n");
        }
    }

    printf("\n");
}

void hook_code(uc_engine *uc, uint64_t address, uint32_t size,
                      void *user_data)
{

    uint32_t code;
    if(uc_mem_read(uc,address, &code, sizeof(code))) {
        printf("Failed to read emulation code to memory, quit!\n");
        return;
    }
    printf("code is 0x%x\n", code);

    cs_insn *insn;
    int count = cs_disasm(handle, (const uint8_t*)(&code), size, 0x00, 0, &insn);
    if (count) {
            size_t j;

            for (j = 0; j < count; j++) {
                printf("0x%" PRIx64 ":\t%s\t%s\n", insn[j].address, insn[j].mnemonic, insn[j].op_str);
                print_insn_detail(&insn[j]);
            }
            //printf("0x%" PRIx64 ":\n", insn[j-1].address + insn[j-1].size);

            // free memory allocated by cs_disasm()
            cs_free(insn, count);
        } else {
            printf("ERROR: Failed to disasm given code!\n");
    }

    printf("HOOK_CODE: 0x%" PRIx64 ", 0x%x\n", address, size);

    uint64_t next_addr = address + size;
    for (int i = 0; i < sizeof(skip_list); i++) {
        if (address == skip_list[i]) {
            printf("skip it\n");
            uc_reg_write(uc, UC_ARM64_REG_PC, &next_addr);
        }
    }
}

bool init_capstone() {
    cs_err err = cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &handle);
    if (err) {
        printf("Failed on cs_open() with error returned: %u\n", err);
        return false;
            
    }
    cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);
    return true;
}

int main() {
    int i;
    uc_hook sys_hook;
    uc_err err;
    uc_engine* uc;
    char *data = NULL;
  
    if ((err = uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc))) {
        uc_perror("uc_open", err);
        return 1;
    }

    if (!init_capstone()) {
        goto end;
    }
    
    FILE *ptr = fopen("./a.bin", "rb");
    if (ptr == NULL) {
        printf("open fail\n");
        return 1;
    }
    fseek(ptr, 0l, SEEK_END);
    int file_size = ftell(ptr);
    fseek(ptr, 0l, SEEK_SET);
    if (file_size <= 0) {
         printf("invalid file size\n");
         goto end;
    }

    data = (char *)malloc(file_size);
    if (data == NULL) {
        printf("malloc fail\n");
        goto end;
    }

    int read_size = fread(data, 1, file_size, ptr);
    if (read_size != file_size) {
        printf("read fail\n");
        goto end;
    }

    if ((err = uc_mem_map(uc, BASE, 8 * 0x1000, UC_PROT_ALL))) {
        uc_perror("uc_mem_map", err);
        return 1;
    }


    if ((err = uc_mem_write(uc, BASE, data, read_size))) {
        uc_perror("uc_mem_write", err);
        return 1;
    }

    uint64_t stack_top = BASE + read_size + 0x1000 -0x8;
    uc_reg_write(uc, UC_ARM64_REG_SP, &stack_top);

    uint64_t start_addr = BASE + 0x65c;
    uint64_t end_addr = BASE + 0x68c;

    if ((err = uc_hook_add(uc, &sys_hook, UC_HOOK_CODE, hook_code, NULL, start_addr, end_addr))) {
        uc_perror("uc_hook_add", err);
        return 1;
    }

    if ((err = uc_emu_start(uc, start_addr, end_addr, 0, 0))) {
        uc_perror("uc_emu_start", err);
        return 1;
    }

end:
    if (ptr != NULL) {
        fclose(ptr);
        ptr = NULL;
    }
    if (data != NULL) {
        free(data);
        data = NULL;
    }
    return 0; 
}

这样就大概介绍了下unicorn的使用,还是很有趣的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容