unicorn的hook相关(c语言)

在上一篇文章中我们介绍了python版的hook相关,这次我们就来介绍一下c语言版的,先看一个例子
在看这个例子之前,我们要介绍两个函数,分别是uc_reg_write_batch和uc_reg_read_batch,在此之前,我们已经介绍了uc_reg_write是用来修改寄存器内容的和uc_reg_read是用来读取寄存器内容的,那么万一我们要修改很多呢,总不能一个一个修改吧,所以这个就提供了一次性修改和读取多个寄存器的函数

uc_reg_write_batch和uc_reg_read_batch

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 };
err=uc_reg_write_batch(uc,syscall_abi,vals,7);
//总共有四个参数,第一个还是uc类,第二个就是我们要修改的寄存器常量,不过相比uc_reg_write函数这个变成了数组地址,这个数组放的就是寄存器常量,第三个参数就是我们要修改的值,这个也是数组地址,和第二个参数一一对应修改,第四个就是要修改的个数,这边是7,也就是说我们要修改7个寄存器的值,对应数组下标应该是[0,7)左开右闭区间
err=uc_reg_read_batch(uc,syscall_abi,vals,7);
//这个和write很像,只不过是吧第三个参数意义改了一下,变成了查询到的信息存放在这个数组里

例子

#include <stdio.h>
#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"
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_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;
    }
    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;
}

hook_add函数

对于前面文章看过的阅读这个应该问题不大,接下来我们来介绍一下hook_add函数

uc_hook sys_hook;
err=uc_hook_add(uc, &sys_hook,UC_HOOK_INTR,hook_syscall,NULL,1,0);
//这个有七个参数,第一个参数是uc类,第二个参数是回调路径,这个在下一个函数uc_hook_del中会有用到,第三个参数是hook类型,通过修改这个参数我们可以让不同时候来调用这个hook,第四个参数就是hook函数,我们每次调用hook的时候要执行的函数,第五个参数是hook函数里的user_data,也就是用户数据,第六个参数是代码起始地址,第七个参数是代码终止地址,从起始地址开始到终止地址的代码都会进行hook判断,然后根据hook类型来判断是否调用hook函数,如果起始地址大于终止地址,也就像这个样例一样,那么就会把整个范围认为是整个unicorn运行的代码范围

hook函数

接下来介绍一下我们自己写的hook函数

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);
}
//这个函数一共有四个参数,第一个是uc类,第二个是触发调用hook函数的指令的地址,第三个参数对应的就是这个指令的大小,第四个参数就是用户数据,也就是hook_add提到的可以传一些数据过来

hook_del函数

既然可以添加hook,那肯定可以删除hook,这时候就要用到我们的hook_del函数

err=uc_hook_del(uc,sys_hook);
//这个有两个参数,第一个参数是uc类,第二个参数是回调路径,也就是hook_add的第二个参数

例子

#include <stdio.h>
#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_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;
}

hook类型

不同hook类型,调用hook的时机也不同,像上面的例子就是每个指令执行之前调用hook函数,接下来我们来看看所有的hook类型

// All type of hooks for uc_hook_add() API.
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,
} uc_hook_type;
posted @ 2022-07-07 11:17  予柒  阅读(396)  评论(0编辑  收藏  举报
返回顶端
Live2D /*修改地一:waifu.css*/
/*修改地二:waifu.css*/
/*修改地三:live2d.js*/ /*修改地四:waifu-tips.js*/