精华内容
下载资源
问答
  • <div><p>This currently fails because of #499 </p><p>该提问来源于开源项目:unicorn-engine/unicorn</p></div>
  • <div><p>That should prevent name conflicts with the unicorn webserver gem</p><p>该提问来源于开源项目:unicorn-engine/unicorn</p></div>
  • unicorn.cr:Unicorn Engine的水晶绑定
  • 0x10 Unicorn Unicorn 是一个轻量级的多平台多架构的 CPU 仿真框架。作为一款著名的开源CPU 模拟框架,很多二进制逆向分析软件都用到了 Unicorn ,或者使用到了它的思想。比如 Radare2、Pwndbg、gdb-gef。Unicorn 在...

    0x10 Unicorn

    Unicorn 是一个轻量级的多平台多架构的 CPU 仿真框架。作为一款著名的开源CPU 模拟框架,很多二进制逆向分析软件都用到了 Unicorn ,或者使用到了它的思想。比如 Radare2、Pwndbg、gdb-gef。Unicorn 在 QEMU 的基础上,增加了许多新特性,以及对 CPU 仿真更好的支持 1

    • 多架构支持:Arm, Arm64 (Armv8), M68K, Mips, Sparc, & X86 (include X86_64)
    • 轻量级的 API
    • 纯 C 语言实现,并支持 Pharo,Crystal,Clojure,Visual Basic,Perl,Rust,Haskell,Ruby,Python,Java,Go,.NET,Delphi / Pascal 和 MSVC 的编译
    • 原生支持 Windows 和类 Unix 系统,如 Mac OSX, Linux, *BSD & Solaris
    • 使用 JIT(Just-In-Time,即时编译技术)提高性能
    • 支持各种级别的细粒度分析
    • 线程安全
    • 根据免费软件许可 GPLv2 分发

    原作者在 2015 年的 BlackHat 上,发表了相关议题, BlackHat USA 2015 slides 提供更多的信息,有兴趣的读者可以观看一波。


    0x20 基于 C/C++ 使用 Unicorn 进行开发

    0x21 编译生成库文件

    项目地址:https://github.com/unicorn-engine/unicorn,以下是该项目的详细结构 2

       .                   <- 主要引擎core engine + README + 编译文档COMPILE.TXT 等
    ├── arch            <- 各语言反编译支持的代码实现
    │   ├── AArch64     <- ARM64 (aka ARMv8) 引擎
    │   ├── ARM         <- ARM 引擎
    │   ├── EVM         <- Ethereum 引擎
    │   ├── M680X       <- M680X 引擎
    │   ├── M68K        <- M68K 引擎
    │   ├── Mips        <- Mips 引擎
    │   ├── PowerPC     <- PowerPC 引擎
    │   ├── Sparc       <- Sparc 引擎
    │   ├── SystemZ     <- SystemZ 引擎
    │   ├── TMS320C64x  <- TMS320C64x 引擎
    │   ├── X86         <- X86 引擎
    │   └── XCore       <- XCore 引擎
    ├── bindings        <- 中间件
    │   ├── java        <- Java 中间件 + 测试代码
    │   ├── ocaml       <- Ocaml 中间件 + 测试代码
    │   └── python      <- Python 中间件 + 测试代码
    ├── contrib         <- 社区代码
    ├── cstool          <- Cstool 检测工具源码
    ├── docs            <- 文档,主要是capstone的实现思路
    ├── include         <- C头文件
    ├── msvc            <- Microsoft Visual Studio 支持(Windows)
    ├── packages        <- Linux/OSX/BSD包
    ├── windows         <- Windows 支持(Windows内核驱动编译)
    ├── suite           <- Capstone开发测试工具
    ├── tests           <- C语言测试用例
    └── xcode           <- Xcode 支持 (MacOSX 编译)
    

    由于项目原生支持 Windows,本次使用 Win10+Microsoft Visual Studio 2015 进行编译。使用 VS 2015 直接打开 msvc 目录下的 unicorn.sln,即可自动载入项目。右击 解决方案 -> 属性,可以弹出如下属性页,从而选择自己想编译的项目。
    在这里插入图片描述
    当然,你也可以直接在项目右下角的 属性 视图里面进行快捷设置
    在这里插入图片描述
    在项目的编译属性中,设置如下,其余设置默认即可

    • 不使用预编译头
    • 附加选项 /wd4018 /wd4244 /wd4267

    编译完成之后,会在项目文件夹的编译器目录下(如果你使用的是 Win32 编译器,那么就是 Win32)的 Debug 目录中生成相应的链接库(unicorn.lib 静态链接库 + unicorn.dll 动态链接库)
    在这里插入图片描述
    这两个库以及项目的头文件,就是我们开发需要的东西了。


    0x22 新建项目调用引擎

    新建一个VS工程(Win32 项目 或者 控制台项目)。将…\unicorn-master\include\unicorn中的头文件以及编译好的 lib 和 dll 文件全部拷贝到新建项目的主目录下

    #include<iostream>
    #include "include\unicorn\unicorn.h"
    
    #define X86_CODE "\x41\x4a"		// 要模拟的指令
    #define ADDRESS 0x2000000		// 起始地址
    
    using namespace std;
    
    int main()
    {
    	uc_engine * uc;
    	uc_err err;
    	int r_ecx = 0x1234;
    	int r_edx = 0x5678;
    
    	cout << "Emulate i386 code" << endl;
    
    	/*x86模式初始化*/
    	err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
    	if (err != UC_ERR_OK)
    	{
    		cout << "Something error in uc_open() %u" << err << endl;
    		return -1;
    	}
    
    	/*申请模拟器内存大小*/
    	uc_mem_map(uc, ADDRESS, 4 * 1024 * 1024, UC_PROT_ALL);		// 4MB
    
    	/*要模拟的指令写入寄存器*/
    	if (uc_mem_write(uc, ADDRESS, X86_CODE, sizeof(X86_CODE) - 1))
    	{
    		cout << "Failed to write emulation code to memory, abort" << endl;
    		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_CODE) - 1, 0, 0);
    	if (err)
    	{
    		printf("Something wrong in uc_emu_start(), error code: %u %s\n", err, uc_strerror(err));
    		return -1;
    	}
    
    	/*打印寄存器内容*/
    	printf("Emulation done. Blew 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;
    
    }
    

    解决方案资源管理器 头文件添加现有项 unicorn.h,资源文件中添加 unicorn.lib,重新生成解决方案。编译并运行。如下图所示,成功的执行指令 ecx + 1 edx - 1。如果报错,请参考 0x3 Q&A
    在这里插入图片描述

    0x23 关键代码解析

    • 第4行:我们要模拟的原始二进制代码。此示例中的代码处于十六进制模式,代表两个X86指令“ INC ecx ”和“ DEC edx ” 3
    • 第11行:声明一个指向uc_engine类型的句柄的指针。该句柄将在Unicorn的每个API中使用
    • 第12行:声明数据类型为uc_err的变量,以防 Unicor API返回错误。
    • 第53行:调用uc_close函数完成仿真

    其余代码请看下面的函数解析

    0x24 API 解析: unicorn.h 关键函数

    uc_engineuc_struct 的别名,这里第一行代码,是定义 uc 为指向 unicorn engine 的指针。
    uc_err 是错误类型,是 uc_errno() 的返回值

    typedef enum uc_err {
        UC_ERR_OK = 0,   // 无错误
        UC_ERR_NOMEM,      // 内存不足: uc_open(), uc_emulate()
        UC_ERR_ARCH,     // 不支持的架构: uc_open()
        UC_ERR_HANDLE,   // 不可用句柄
        UC_ERR_MODE,     // 不可用/不支持架构: uc_open()
        UC_ERR_VERSION,  // 不支持版本 (中间件)
        UC_ERR_READ_UNMAPPED, // 由于在未映射的内存上读取而退出模拟: uc_emu_start()
        UC_ERR_WRITE_UNMAPPED, // 由于在未映射的内存上写入而退出模拟: uc_emu_start()
        UC_ERR_FETCH_UNMAPPED, // 由于在未映射的内存中获取数据而退出模拟: uc_emu_start()
        UC_ERR_HOOK,    // 无效的hook类型: uc_hook_add()
        UC_ERR_INSN_INVALID, // 由于指令无效而退出模拟: uc_emu_start()
        UC_ERR_MAP, // 无效的内存映射: 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函数的无效参数
        UC_ERR_READ_UNALIGNED,  // 未对齐读取
        UC_ERR_WRITE_UNALIGNED,  // 未对齐写入
        UC_ERR_FETCH_UNALIGNED,  // 未对齐的提取
        UC_ERR_HOOK_EXIST,  // 此事件的钩子已经存在
        UC_ERR_RESOURCE,    // 资源不足: uc_emu_start()
        UC_ERR_EXCEPTION, // 未处理的CPU异常
        UC_ERR_TIMEOUT // 模拟超时
    } uc_err;
    

    uc_open() 用于创建 unicorn 实例

    uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc);
    
    @arch: 架构类型 (UC_ARCH_*)
    @mode: 硬件模式. 由 UC_MODE_* 组合
    @uc: 指向 uc_engine 的指针, 返回时更新
    @return 成功则返回UC_ERR_OK , 否则返回 uc_err 枚举的其他错误类型
    

    uc_mem_map() 为模拟器映射一块内存

    uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
    
    @uc: uc_open() 返回的句柄
    @address: 要映射到的新内存区域的起始地址。这个地址必须与4KB对齐,否则将返回UC_ERR_ARG错误。
    @size: 要映射到的新内存区域的大小。这个大小必须是4KB的倍数,否则将返回UC_ERR_ARG错误。
    @perms: 新映射区域的权限。参数必须是UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC或这些的组合,否则返回UC_ERR_ARG错误。
    @return 成功则返回UC_ERR_OK , 否则返回 uc_err 枚举的其他错误类型
    

    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_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_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 枚举的其他错误类型
    

    0x30 Q&A

    0x31 使用了不安全函数

    在这里插入图片描述
    在预处理器中,添加相应的定义即可
    在这里插入图片描述
    或者在程序中添加以下任意一行代码

    #define _CRT_SECURE_NO_DEPRECATE;
    #define _CRT_SECURE_NO_WARNINGS;
    #pragma warning(disable:4996);
    

    重新编译,并运行。

    0x32 error LNK2019: 无法解析的外部符号 _main

    在这里插入图片描述
    打开项目属性页,修改 调试器 >系统 > 子系统 ,切换成控制台(如果原来是控制台,则切换为窗口)
    在这里插入图片描述

    0x32 无法查找或打开 PDB 文件

    在这里插入图片描述
    【工具】->【选项】->【调试】->【常规]】勾选“启用源服务器支持” 4
    在这里插入图片描述
    【工具】->【选项】->【调试】->【符号】,勾选“Microsoft符号服务器”
    在这里插入图片描述

    0x40 基于 Python 调用 Unicorn Engine

    使用 Python 调用 Unicorn 相对来说,要简单许多。当然首先得安装相应的包

    pip install unicorn -i https://pypi.mirrors.ustc.edu.cn/simple/
    
    from __future__ import print_function
    from unicorn import *
    from unicorn.x86_const import *
    
    # code to be emulated
    X86_CODE32 = b"\x41\x4a" # INC ecx; DEC edx
    
    # memory address where emulation starts
    ADDRESS = 0x1000000
     
    print("Emulate i386 code")
    try:
        # Initialize emulator in X86-32bit mode
        mu = Uc(UC_ARCH_X86, UC_MODE_32)
    
        # map 2MB memory for this emulation
        mu.mem_map(ADDRESS, 2 * 1024 * 1024)
    
        # write machine code to be emulated to memory
        mu.mem_write(ADDRESS, X86_CODE32)
    
        # initialize machine registers
        mu.reg_write(UC_X86_REG_ECX, 0x1234)
        mu.reg_write(UC_X86_REG_EDX, 0x5678)
    
        # emulate code in infinite time & unlimited instructions
        mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32))
    
        # now print out some registers
        print("Emulation done. Below is the CPU context")
    
        r_ecx = mu.reg_read(UC_X86_REG_ECX)
        r_edx = mu.reg_read(UC_X86_REG_EDX)
        print(">>> ECX = 0x%x" %r_ecx)
        print(">>> EDX = 0x%x" %r_edx)
    
    except UcError as e:
        print("ERROR: %s" % e)
    

    输出结果
    在这里插入图片描述

    • 第2〜3行:在使用Unicorn之前,导入unicorn模块。此示例还使用了一些X86寄存器常量,因此也需要unicorn.x86_const
    • 第6行:要模拟的原始二进制代码。此示例中的代码处于十六进制模式,代表两个X86指令“ INC ecx ”和“ DEC edx ”
    • 第9行:将在其中模拟上面的代码的虚拟地址
    • 第14行:使用类Uc初始化Unicorn 。此类接受2个参数:硬件体系结构和硬件模式。在此示例中,我们要模拟X86体系结构的32位代码。mu接受返回值
    • 第17行:mem_map在第9行声明的地址处映射2MB的内存用于此仿真。在此过程中,所有CPU操作都只能访问该内存。该内存使用默认权限READ,WRITE和EXECUTE映射
    • 第20行:将要模拟的代码写入到我们上面刚刚映射的内存中。mem_write方法采用2个参数:要写入的地址和要写入内存的代码
    • 23〜24行:使用reg_write方法设置ECX和EDX寄存器的值
    • 第27行:使用方法emu_start启动仿真。该API包含4个参数:仿真代码的地址,仿真停止的地址(紧随X86_CODE32的最后一个字节之后),要仿真的时间以及要仿真的指令数。如果像本例一样忽略最后两个参数,Unicorn将在无限的时间和无限数量的指令中模拟代码
    • 第32〜35行:打印出寄存器ECX和EDX的值。我们使用reg_read函数读取寄存器的值

    0x50 总结

    Unicorn Engine 用于仿真各种架构的 CPU 指令集,在 QEMU 的基础上,扩展了很多新特性和新功能,本文初步介绍了其简单的用法,利用 C 和 Python 调用了其 API,模拟了 x86 指令集,如果用其实际开发一个项目,你将会体会到 Unicorn 的独特魅力。在这里,是希望通过简短的介绍,让大家能够从源码以及用法的角度上,对 Unicorn 有一个更加全面的了解,为后续 Afl-Unicorn 进行 Fuzz 测试,或者编写自己的模拟器,提供一种思想上的理念。附录中的参考网址,都是值得学习的平台,尤其感谢 kabeor制作的非官方 API 参考文档。


    1. http://www.unicorn-engine.org/ ↩︎

    2. https://github.com/kabeor/Micro-Unicorn-Engine-API-Documentation/blob/master/Micro%20Unicorn-Engine%20API%20Documentation.md ↩︎

    3. http://www.unicorn-engine.org/docs/tutorial.html ↩︎

    4. https://blog.csdn.net/qq_38410428/article/details/102720550 ↩︎

    展开全文
  • <div><p>It is not possible to read inferior memory within GDB, inside a Unicorn callback. <p>Does Unicorn do anything that would explain breakage of GDB'...unicorn-engine/unicorn</p></div>
  • http://eternal.red/2018/unicorn-engine-tutorial/

    如果你对 Unicorn Engine 还不是很了解的话,请点击我在之前发过的博客:Tutorial for Unicorn:Unicorn Engine 的开发和使用。有时候,我们可能不需要模拟整个系统或者整个二进制,这时候 Unicorn 作为一款优秀的模拟器,就发挥了它的作用。但是 Unicorn 并不支持系统调用,因此,需要手动映射内存,并将数据写入其中,从自定义的开始地址加载相应汇编代码。

    0x10 源码

    本次,先介绍一道简单的自定义题目。比如说,我们想要改写如下代码生成的二进制。这段代码意思很简单,a = 1 != 5,b = "spiderman" != “batman”,很显然,result 输出的结果一直是 0。最终的目标就是,在没有源码的情况下,让二进制输出 1。

    #include <stdio.h>
    
    int strcmp(char *a, char *b)
    {
        //get length
        int len = 0;
        char *ptr = a;
        while(*ptr)
        {
            ptr++;
            len++;
        }
        
        //comparestrings
        for(int i=0; i<=len; i++)
        {
            if (a[i]!=b[i])
                return 1;
        }
        
        return 0;
    }
    
    __attribute__((stdcall))
    int  super_function(int a, char *b)
    {
        if (a==5 && !strcmp(b, "batman"))
        {
            return 1;
        }
        return 0;
    }
    
    int main()
    {
        int result = super_function(1, "spiderman");
        printf("%d\n", result);
    } 
    

    编译生成二进制文件,这里 -m32 是说编译成 32 位程序,当然也可以不加该参数

    gcc test.c -m32 -o test
    

    运行程序,结果符合预期
    在这里插入图片描述

    0x20 Unicorn 模拟程序

    0x21 模拟原程序

    引入库文件,第一行代码是引入 unicorn 库,第二行则是根据要模拟的不同架构,选择不同的库,本次是要模拟 x86 架构,因此引入 unicorn.x86_const

    from unicorn import *
    from unicorn.x86_const import *
    

    初始化 unicorn engine 类,生成 unicorn 实例对象 mu

    mu = Uc(UC_ARCH_X86, UC_MODE_32)
    

    设计内存分布,调用 mem_map 方法来映射内存,这里假设我们要在将代码放在 0x000000 处执行,将栈的位置和大小也确定下来(tips:正常来说,对于 x86 4GB 内存空间,程序加载的起始地址是 0x08048000,这里为了让调试地址与 IDA 反汇编一一对应,直接写 0x00000,换成其他地址也是可以的

    BASE = 0x000000
    STACK_ADDR = 0X100000
    STACK_SIZE = 1024 * 1024 # 1MB
    
    mu.mem_map(BASE, 1024 * 1024)
    mu.mem_map(STACK_ADDR, STACK_SIZE)
    

    现在,我们需要像加载程序一样在我们的基地址加载二进制文件。然后我们需要设置 RSP 指向堆栈的末尾,接触过汇编的人应该都知道,这也是正常函数执行的开始过程,sub sp 用来设置栈的大小。

    mu.mem_write(BASE, read("./test"))
    mu.reg_write(UC_X86_REG_ESP, STACK_ADDR + STACK_SIZE - 1)
    

    选择需要模拟执行的汇编代码,即要确定开始和结束的地址,将其加载进已经指定好的内存空间
    在这里插入图片描述

    mu.emu_start(0x1240, 0x127B)
    

    运行,可以追踪到详细的错误汇编代码
    在这里插入图片描述
    定位 0x1247,猜测可能是由于 ecx-4 处的内存地址并没有被映射,绕过即可

    .text:00001247                 push    dword ptr [ecx-4]
    

    怎么绕过呢?绕过其实就是将 eip 寄存器存放的地址指向另外一条指令(eip 存放下一条即将执行指令的地址)

    instructions_skip_list = [0x1247]
    
    def hook_code(mu, address, size, user_data):
    	print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' % (address, size))
    
    	if address in instructions_skip_list:
    		mu.reg_write(UC_X86_REG_EIP, address + size)
    

    到此,我们完整的运行了指定的代码片段。但是这并不足够,因为程序还没有输出,而 printf 作为 libc 的库函数,我们并没有加载到内存中去,所以没办法模拟,那么就需要使用 unicorn 将结果显示出来。

    0x22 修改运行逻辑

    对于本例来说,很好解决,因为在调用 printf 之前,要打印的值也就是参数值,已经存放在相关寄存器或者变量 a 中。

    再次查看汇编代码,发现,0x00001266 处调用 super_function,那么调用这个函数之后,返回值就会存放在 eax 寄存中,只要该代码运行之后,将 eax 的值打印出来就行了。

    def hook_code(mu, address, size, user_data):
    	# print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' % (address, size))
    
    	if address == 0x0000126B:
    		print("result = %x" % mu.reg_read(UC_X86_REG_EAX))
    

    运行会打印出:result = 0,现在真正的问题就在于怎么修改程序让结果为 1 了(为了加大难度,不要再上面的代码基础上,直接加一条指令 mu.reg_write(UC_X86_REG_EAX, 1))。我们是要更改一下传入的参数,让判断逻辑成立。
    在这里插入图片描述
    要修改的汇编代码如下
    在这里插入图片描述
    可以直接使用 IDA 修改汇编指令,但是这不符合我们的初衷,我们是要使用 unicorn 来修改运行过程中,寄存器的值,从而达到修改程序逻辑的目的。

    在栈中,写入一个字符串,即 batman, 替换 0x125D 处的代码。而 push 1 没有用到寄存器,怎么修改呢?这个时候就需要对栈比较了解了。 在本例中,函数是从栈中取参数,而栈又是靠 esp 寄存器存放的栈指针进行定位

    mu.mem_write(STACK_ADDR, "batman\x00")
    reg_esp = STACK_ADDR + STACK_SIZE / 2
    mu.reg_write(UC_X86_REG_ESP, reg_esp)
    mu.mem_write(reg_esp + 4, p32(5))
    mu.mem_write(reg_esp + 8, p32(STACK_ADDR))
    

    0x30 总结

    使用 unicorn 模拟执行二进制代码的过程就像上面所述,使用其提供的 api,就可以完成很多操作。具体代码的执行,可能因为不同的环境,所有差别,但是总体的思想是一致的,先初始化 unicorn 引擎,然后分配内存,将目标程序加载进我们设置的内存,接着 hook 函数,排错;最后调用 emu_start 即可开始。期间,可以使用相应的函数,修改寄存器的值,进而改变程序执行的逻辑。

    0x40 附录

    0x21 节完整代码

    from unicorn import *
    from unicorn.x86_const import *
    import struct
    
    def read(name):
    	with open(name) as fp:
    		return fp.read()
    
    instructions_skip_list = [0x1247]
    
    def hook_code(mu, address, size, user_data):
    	# print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' % (address, size))
    
    	if address in instructions_skip_list:
    		mu.reg_write(UC_X86_REG_EIP, address + size)
    
    	if address == 0x0000126B:
    		print("result = %x" % mu.reg_read(UC_X86_REG_EAX))
    		
    
    def main():
    	# 初始化 unicorn
    	mu = Uc(UC_ARCH_X86, UC_MODE_32)
    	
    	# 内存布局
    	BASE = 0x000000
    	STACK_ADDR = 0X100000
    	STACK_SIZE = 1024 * 1024 # 1MB
    	
    	mu.mem_map(BASE, 1024 * 1024)
    	mu.mem_map(STACK_ADDR, STACK_SIZE)
    	
    	# 加载程序
    	mu.mem_write(BASE, read("./test"))
    	mu.reg_write(UC_X86_REG_ESP, STACK_ADDR + STACK_SIZE - 1)
    
    	mu.hook_add(UC_HOOK_CODE, hook_code)
    	mu.emu_start(0x1240, 0x127B)
    
    if __name__ == '__main__':
    	main()
    
    

    0x22 节完整代码

    from unicorn import *
    from unicorn.x86_const import *
    import struct
    
    BASE = 0x08048000
    STACK_ADDR = 0X100000
    STACK_SIZE = 1024 * 1024 # 1MB
    instructions_skip_list = [BASE + 0x1247]
    
    def read(name):
    	with open(name) as fp:
    		return fp.read()
    
    def p32(num):
    	return struct.pack("I", num)
    
    def hook_code(mu, address, size, user_data):
    	print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' % (address, size))
    
    	if address in instructions_skip_list:
    		mu.reg_write(UC_X86_REG_EIP, address + size)
    
    	if address == BASE + 0x0000126B:
    		print("result = %x" % mu.reg_read(UC_X86_REG_EAX))
    		
    
    def main():
    	mu = Uc(UC_ARCH_X86, UC_MODE_32)
    
    	mu.mem_map(BASE, 1024 * 1024)
    	mu.mem_map(STACK_ADDR, STACK_SIZE)
    
    	mu.mem_write(BASE, read("./test"))
    	#mu.reg_write(UC_X86_REG_ESP, STACK_ADDR + STACK_SIZE - 1)
    	
    	mu.mem_write(STACK_ADDR, "batman\x00")
    	reg_esp = STACK_ADDR + STACK_SIZE / 2
    	mu.reg_write(UC_X86_REG_ESP, reg_esp)
    	mu.mem_write(reg_esp + 4, p32(5))
    	mu.mem_write(reg_esp + 8, p32(STACK_ADDR))
    
    	mu.hook_add(UC_HOOK_CODE, hook_code)
    	mu.emu_start(BASE + 0x1240, BASE + 0x127B)
    
    if __name__ == '__main__':
    	main()
    
    

    参考文章
    https://blog.csdn.net/song_lee/article/details/104439329
    http://eternal.red/2018/unicorn-engine-tutorial/

    展开全文
  • <div><p>ENG-24 Renamed unicorn <code>engine</code> to <code>model_runner</code> and created a directory hierarchy for backend code such that <code>model_runner</code> will be imported from <code>...
  • Ubuntu server 14.04 交叉编译Unicorn-engine 编译的过程基本上按照的是unicorn/COMPILE-WINDOWS.md描述的进行编译的,不过还是改了一些地方。在Ubuntu 14.04 server上交叉编译Unicorn给windows使用。 第一步就是...

    Ubuntu server 14.04 交叉编译Unicorn-engine

        编译的过程基本上按照的是unicorn/COMPILE-WINDOWS.md描述的进行编译的,不过还是改了一些地方。在Ubuntu 14.04 server上交叉编译Unicorn给windows使用。

        第一步就是按照Mingw64了,Mingw64既支持编译windows 32位的程序,也支持编译windows 64位的程序。Sudo apt-get install mingw-w64

        按照官方文档的描述,下面直接安装Mingw-glib2,

    sudo dpkg –i –force-depends mingw64-x86-glib2_2.31.0_all.deb

        然后就尝试./make.sh cross-win32进行编译。发现并不能成功,提示缺少其他依赖。缺少什么就安装什么,像pkg-config,libtool,zlib等,前两个好安装,直接apt-get install就能安装,但是第三个有问题了,利用sudo apt-get install zlib1g-dev 安装了zlib后,在本地尝试编译了:

    #include <zlib.h>

    int main(void) { zlibVersion(); return 0; }

    并且可以成功运行,但是编译unicorn的时候,还是提示找不到zlib.h,这个时候意识到我现在是交叉编译,需要windows版本的zlib,所以就下载源码,对zlib进行交叉编译。在StackOverflow上找到了一个解决方案,成功交叉编译zlib。

    http://stackoverflow.com/questions/21322707/zlib-header-not-found-when-cross-compiling-with-mingw

     

        继续编译Unicorn,发现还是报错,"/usr/bin/i686-w64-mingw32-ld: cannot find -lglib-2.0",自己不是已经安装了mingw64-x86-glib2_2.31.0_all.deb,为啥还提示找不到libglib-2.0?我直接find / -name libglib*,发现系统上是有libglib-2.0的库的,

        我直接把这两个路径加到了make.sh的库搜索路径里,如下:

        继续编译Unicorn,成功编译!!!!

     

    参考:

    http://stackoverflow.com/questions/21322707/zlib-header-not-found-when-cross-compiling-with-mingw

    http://www.devinprogress.org/2014/02/how-to-cross-compile-libcurl-on-linux/

    https://wiki.openttd.org/Cross-compiling_for_Windows#Compiling_zlib

    http://linux.m2osw.com/cross_compiling_zlib

    http://blog.csdn.net/npy_lp/article/details/6991704

     

    转载于:https://www.cnblogs.com/wangaohui/p/5512296.html

    展开全文
  • 使用Unicorn-engine 续1

    2016-05-24 16:39:00
     在windows上,我主要是用python进行分析程序,因此我最初安装的是官网上的unicorn-0.9-python2.7-win32.exe。至于我为什么要用32位的版本,因为64位的版本不能在IDAPython里使用,就像下面:  后来,我干脆...

      续上次,在ubuntu server 14.04交叉编译好后,下一步就是在windows上使用了。

      在windows上,我主要是用python进行分析程序,因此我最初安装的是官网上的 unicorn-0.9-python2.7-win32.exe。至于我为什么要用32位的版本,因为64位的版本不能在IDAPython里使用,就像下面:

      后来,我干脆统一了IDA是32位的程序,python是32位的,unicorn也是32位的,这样的话在IDAPython里可以使用unicorn了。

      安装unicorn-0.9-python2.7-win32.exe会在site-packages目录下存在以下dll:

      他们的依赖关系如下(用dependency walker看的):

      在python2.7/Lib/site-packages/unicorn.py里也看到定义了这些动态链接库的加载顺序:

      下面我来讲下官网上的unicorn-0.9-python2.7-win32.exe有什么bug,导致我去自己编译源码。

      问题是这样的:

    Example 1:

    Error:

    Python>uc = Uc(UC_ARCH_X86, UC_MODE_64)

    Python>uc.mem_map(0x19000c000,0x1000)

    Python>uc.mem_write(0x19000c000, '\x00')

    Traceback (most recent call last):

    File "<string>", line 1, in <module>

    File "C:\Python27\lib\site-packages\unicorn\unicorn.py", line 232, in mem_write

    raise UcError(status)

    unicorn.unicorn.UcError: Invalid memory write (UC_ERR_WRITE_UNMAPPED)

    Correct:

    Python>uc.mem_map(0x9000c000,0x1000)

    Python>uc.mem_write(0x9000c000, '\x00')

    Python>

      我使用的是UC_MODE_64,但是发现mem_map的时候地址只能在32位内,大于32位的地址将会在往地址写内容时报错。在测试时发现,如果指令的地址在32位以上,也不能模拟执行,那这个UC_MODE_64就感觉没什么用了。

      我在Github上发现,有人已经提出这个问题了,而且给出了对源代码的修改方案,并进行了pull request。链接:https://github.com/unicorn-engine/unicorn/issues/523 代码还没有被合并到主分支里,所以如果直接从github上下载的话,还是有问题的,所以要自己手动的去修改。

      编译完成后,按照github上的说明,我主要使用的是unicorn python bindings,在bings/python目录下,运行python setup.py install进行安装。

      同时将编译完成的unicorn.dll,/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libglib-2.0-0.dll,/usr/lib/gcc/x86_64-w64-mingw32/4.8/libgcc_s_sjlj-1.dll,/usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll拷贝到Lib/site-packages/unicorn目录下就可以了。他们的依赖关系是这样的:

      因此,我们修改下unicorn.py里定义的动态链接库加载顺序。

      到此,完工,可以完美使用了,并且解决了bug。附成功截图(在写入时没有报错):

    转载于:https://www.cnblogs.com/wangaohui/p/5523920.html

    展开全文
  • 最近在研究用Unicorn Engine调用SO时,发现网上的资料很少,并且没有一个完整的调用实例。所以我把自己做的发出来跟大家分享,共同学习进步。 下面开始: 一、我们的目的 以上一串字符串中vf字段为标红部分的...
  • <div><p>Hi, I'm struggling make it work. I successfully compiled the engine and tested it with samples. Using the make.sh with: <em>UNICORN_QEMU_FLAGS=...unicorn-engine/unicorn</p></div>
  • 1.第一步添加包含目录 2.添加资源文件 3.就可以愉快的写代码了 转载于:https://www.cnblogs.com/Reserved/p/8526510.html

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 677
精华内容 270
关键字:

engineunicorn