Unicorn-CPU模拟框架数据类型及API分析与示例(三) 0x2 API分析 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 枚举的其他错误类型
源码实现
uc_err uc_reg_write (uc_engine *uc, int regid, const void *value) { return uc_reg_write_batch(uc, ®id, (void *const *)&value, 1 ); } uc_err uc_reg_write_batch (uc_engine *uc, int *ids, void *const *vals, int count) { int ret = UC_ERR_OK; if (uc->reg_write) ret = uc->reg_write(uc, (unsigned int *)ids, vals, count); 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实例创建成功" << 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实例关闭成功" << endl ; return 0 ; }
输出
uc_reg_read uc_err uc_reg_read (uc_engine *uc, int regid, void *value) ;
读取寄存器的值
@uc: uc_open()返回的句柄 @regid: 将被读取的寄存器ID @value: 指向保存寄存器值的指针 @return 成功则返回UC_ERR_OK , 否则返回 uc_err 枚举的其他错误类型
源码实现
uc_err uc_reg_read (uc_engine *uc, int regid, void *value) { return uc_reg_read_batch(uc, ®id, &value, 1 ); } uc_err uc_reg_read_batch (uc_engine *uc, int *ids, void **vals, int count) { if (uc->reg_read) uc->reg_read(uc, (unsigned int *)ids, vals, count); else return -1 ; return UC_ERR_OK; }
使用示例:
#include <iostream> #include "unicorn/unicorn.h" using namespace std ;int main () { uc_engine* uc; uc_err err; err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); if (err != UC_ERR_OK) { printf ("Failed on uc_open() with error returned: %u\n" , err); return -1 ; } if (!err) cout << "uc实例创建成功" << 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实例关闭成功" << endl ; return 0 ; }
输出
uc_reg_write_batch uc_err uc_reg_write_batch (uc_engine *uc, int *regs, void *const *vals, int count) ;
同时将多个值写入多个寄存器
@uc: uc_open()返回的句柄 @regid: 存储将被写入的多个寄存器ID的数组 @value: 指向保存多个值的数组的指针 @count: *regs 和 *vals 数组的长度 @return 成功则返回UC_ERR_OK , 否则返回 uc_err 枚举的其他错误类型
源码实现
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; 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" ); uint64_t var[7 ] = { 0 }; for (int i = 0 ; i < 7 ; i++) { cout << syscall_abi[i] << " " ; printf ("%" PRIu64, vals[i]); cout << endl ; } return 0 ; }
输出
uc_reg_read_batch uc_err uc_reg_read_batch (uc_engine *uc, int *regs, void **vals, int count) ;
同时读取多个寄存器的值。
@uc: uc_open()返回的句柄 @regid: 存储将被读取的多个寄存器ID的数组 @value: 指向保存多个值的数组的指针 @count: *regs 和 *vals 数组的长度 @return 成功则返回UC_ERR_OK , 否则返回 uc_err 枚举的其他错误类型
源码实现
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 ) ;
在内存中写入一段字节码。
@uc: uc_open() 返回的句柄 @address: 写入字节的起始地址 @bytes: 指向一个包含要写入内存的数据的指针 @size: 要写入的内存大小。 注意: @bytes 必须足够大以包含 @size 字节。 @return 成功则返回UC_ERR_OK , 否则返回 uc_err 枚举的其他错误类型
源码实现
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; while (count < size ) { MemoryRegion *mr = memory_mapping(uc, address); if (mr) { uint32_t operms = mr->perms; if (!(operms & UC_PROT_WRITE)) 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)) 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" #define ADDRESS 0x1000 int main () { uc_engine* uc; uc_err err; err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); if (err != UC_ERR_OK) { printf ("Failed on uc_open() with error returned: %u\n" , err); return -1 ; } uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024 , UC_PROT_ALL); if (uc_mem_write(uc, ADDRESS, X86_CODE32, sizeof (X86_CODE32) - 1 )) { printf ("Failed to write emulation code to memory, quit!\n" ); return -1 ; } uint32_t code; if (uc_mem_read(uc,ADDRESS,&code, sizeof (code))) { printf ("Failed to read emulation code to memory, quit!\n" ); return -1 ; } cout << hex << code << endl ; err = uc_close(uc); if (err != UC_ERR_OK) { printf ("Failed on uc_close() with error returned: %u\n" , err); return -1 ; } return 0 ; }
输出
uc_mem_read uc_err uc_mem_read (uc_engine *uc, uint64_t address, void *bytes, size_t size ) ;
从内存中读取字节。
@uc: uc_open() 返回的句柄 @address: 读取字节的起始地址 @bytes: 指向一个包含要读取内存的数据的指针 @size: 要读取的内存大小。 注意: @bytes 必须足够大以包含 @size 字节。 @return 成功则返回UC_ERR_OK , 否则返回 uc_err 枚举的其他错误类型
源码实现
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; 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) ;
在指定的时间内模拟机器码。
@uc: uc_open() 返回的句柄 @begin: 开始模拟的地址 @until: 模拟停止的地址 (当到达该地址时) @timeout: 模拟代码的持续时间(以微秒计)。当这个值为0时,将在无限时间内模拟代码,直到代码完成。 @count: 要模拟的指令数。当这个值为0时,将模拟所有可用的代码,直到代码完成 @return 成功则返回UC_ERR_OK , 否则返回 uc_err 枚举的其他错误类型
源码实现
uc_err uc_emu_start (uc_engine* uc, uint64_t begin , uint64_t until, uint64_t timeout, size_t count) { 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 = 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: uc_reg_write(uc, UC_MIPS_REG_PC, &begin ); break ; #endif #ifdef UNICORN_HAS_SPARC case UC_ARCH_SPARC: uc_reg_write(uc, UC_SPARC_REG_PC, &begin ); break ; #endif } uc->stop_request = false ; uc->emu_count = count; if (count <= 0 && uc->count_hook != 0 ) { uc_hook_del(uc, uc->count_hook); uc->count_hook = 0 ; } if (count > 0 && uc->count_hook == 0 ) { uc_err err; uc->hook_insert = 1 ; err = uc_hook_add(uc, &uc->count_hook, UC_HOOK_CODE, hook_count_cb, NULL , 1 , 0 ); uc->hook_insert = 0 ; if (err != UC_ERR_OK) { return err; } } uc->addr_end = until; if (timeout) enable_emu_timer(uc, timeout * 1000 ); if (uc->vm_start(uc)) { return UC_ERR_RESOURCE; } uc->emulation_done = true ; if (timeout) { 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" #define ADDRESS 0x1000 int main () { uc_engine* uc; uc_err err; int r_eax = 0x111 ; err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); if (err != UC_ERR_OK) { printf ("Failed on uc_open() with error returned: %u\n" , err); return -1 ; } uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024 , UC_PROT_ALL); if (uc_mem_write(uc, ADDRESS, X86_CODE32, sizeof (X86_CODE32) - 1 )) { printf ("Failed to write emulation code to memory, quit!\n" ); return -1 ; } uc_reg_write(uc, UC_X86_REG_EAX, &r_eax); printf (">>> before EAX = 0x%x\n" , r_eax); err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof (X86_CODE32) - 1 , 0 , 0 ); if (err) { printf ("Failed on uc_emu_start() with error returned %u: %s\n" , err, uc_strerror(err)); } uc_reg_read(uc, UC_X86_REG_EAX, &r_eax); printf (">>> after EAX = 0x%x\n" , r_eax); err = uc_close(uc); if (err != UC_ERR_OK) { printf ("Failed on uc_close() with error returned: %u\n" , err); return -1 ; } return 0 ; }
输出
uc_emu_stop uc_err uc_emu_stop (uc_engine *uc) ;
停止模拟
通常是从通过 tracing API注册的回调函数中调用。
@uc: uc_open() 返回的句柄 @return 成功则返回UC_ERR_OK , 否则返回 uc_err 枚举的其他错误类型
源码实现
uc_err uc_emu_stop (uc_engine *uc) { if (uc->emulation_done) return UC_ERR_OK; uc->stop_request = true ; if (uc->current_cpu) { cpu_exit(uc->current_cpu); } return UC_ERR_OK; }
使用示例: