{"id":13531333,"url":"https://github.com/tboox/vm86","last_synced_at":"2025-04-05T14:08:18.923Z","repository":{"id":38359536,"uuid":"64183833","full_name":"tboox/vm86","owner":"tboox","description":"🍔 A x86 Script Instruction Virtual Machine","archived":false,"fork":false,"pushed_at":"2021-11-02T02:36:13.000Z","size":16547,"stargazers_count":529,"open_issues_count":1,"forks_count":105,"subscribers_count":30,"default_branch":"master","last_synced_at":"2025-03-29T13:09:53.243Z","etag":null,"topics":["assembly","i386","ida","vm"],"latest_commit_sha":null,"homepage":"https://tboox.org","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tboox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-26T02:37:48.000Z","updated_at":"2025-03-26T17:10:29.000Z","dependencies_parsed_at":"2022-07-12T17:27:38.114Z","dependency_job_id":null,"html_url":"https://github.com/tboox/vm86","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tboox%2Fvm86","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tboox%2Fvm86/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tboox%2Fvm86/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tboox%2Fvm86/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tboox","download_url":"https://codeload.github.com/tboox/vm86/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247345854,"owners_count":20924102,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assembly","i386","ida","vm"],"created_at":"2024-08-01T07:01:02.140Z","updated_at":"2025-04-05T14:08:18.903Z","avatar_url":"https://github.com/tboox.png","language":"C","funding_links":[],"categories":["Projects","C"],"sub_categories":[],"readme":"\u003cp\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003evm86\u003c/h1\u003e\n\n  \u003cdiv\u003e\n    \u003ca href=\"https://github.com/tboox/vm86/blob/master/LICENSE.md\"\u003e\n      \u003cimg src=\"https://img.shields.io/github/license/tboox/vm86.svg?colorB=f48041\u0026style=flat-square\" alt=\"license\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://www.reddit.com/r/tboox/\"\u003e\n      \u003cimg src=\"https://img.shields.io/badge/chat-on%20reddit-ff3f34.svg?style=flat-square\" alt=\"Reddit\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://gitter.im/tboox/tboox?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge\"\u003e\n      \u003cimg src=\"https://img.shields.io/gitter/room/tboox/tboox.svg?style=flat-square\u0026colorB=96c312\" alt=\"Gitter\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://t.me/tbooxorg\"\u003e\n      \u003cimg src=\"https://img.shields.io/badge/chat-on%20telegram-blue.svg?style=flat-square\" alt=\"Telegram\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://jq.qq.com/?_wv=1027\u0026k=5hpwWFv\"\u003e\n      \u003cimg src=\"https://img.shields.io/badge/chat-on%20QQ-ff69b4.svg?style=flat-square\" alt=\"QQ\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://tboox.org/donation/\"\u003e\n      \u003cimg src=\"https://img.shields.io/badge/donate-us-orange.svg?style=flat-square\" alt=\"Donate\" /\u003e\n    \u003c/a\u003e\n  \u003c/div\u003e\n\n  \u003cp\u003eThe x86 Script Instruction Virtual Machine\u003c/p\u003e\n\u003c/div\u003e\n\u003c/p\u003e\n\n\n## Introduction\n\nThis is a very simple and lightweight x86 virtual machine which can load and run the assembly code from ida pro directly.\n \n## Features\n\n* Supports cross-platform and it's able to run the x86 assembly code on linux, windows, maxosx, android and ios ...\n* Supports the frequently-used x86 assembly instruction (.e.g logical operations, goto, loop, call, stack operations ...)\n* Supports call the third-party library interfaces. (.e.g libc ..)\n* We can pass arguments and get the return results after running.\n* Supports thread-safe.\n* Does not support arm64 and only for 32-bits architecture \n\n## Example\n\nWe get one assemble code from ida pro first and this code will call the libc api: `printf`\n\n```asm\nsub_hello\tproc near \narg_0\t\t= dword\tptr  8 \n.data \n        format db \\\"hello: %x\\\", 0ah, 0dh, 0 \n \noff_5A74B0\tdd offset loc_6B2B50\t; DATA XREF: sub_589100+1832\u0018r \n\t\tdd offset loc_58A945\t; jump table for switch\tstatement \n \n.code \n        ; hi\n        push\tebp ;hello \n\t\tmov\tebp, esp \n \n    loc_6B2B50:\t\t\t\t; CODE XREF: sub_6B2B40+8\u0018j\n        push    eax \n\t\tmov\teax, [ebp+arg_0] \n        push eax \n        mov eax, offset format \n        push eax \n        call printf \n        add esp, 4 \n        pop eax \n        \n        mov ecx, 1\n        jmp ds:off_5A74B0[ecx*4]\n \nloc_58A945:\n        push    eax \n\t\tmov\teax, [ebp+arg_0] \n        push eax \n        mov eax, offset format \n        push eax \n        call printf \n        add esp, 4 \n        pop eax \n        \n  end:\n        mov\tesp, ebp \n\t\tpop\tebp \n        retn \nsub_hello    endp \n```\n\nAnd we call it in c language first.\n\n```c\nsub_hello(31415926);\n```\n\nThe output results:\n\n```\nhello: 31415926\nhello: 31415926\n```\n\nNextly, we attempt to load this asm code using our x86 virtual machine.\n\n```c\nstatic tb_void_t vm86_demo_proc_exec_hello(tb_uint32_t value)\n{\n    // the code\n    static tb_char_t const s_code_sub_hello[] = \n    {\n\"sub_hello\tproc near \\n\\\narg_0\t\t= dword\tptr  8 \\n\\\n.data \\n\\\n        format db \\\"hello: %x\\\", 0ah, 0dh, 0 \\n\\\n \\n\\\noff_5A74B0\tdd offset loc_6B2B50\t; DATA XREF: sub_589100+1832\u0018r \\n\\\n\t\tdd offset loc_58A945\t; jump table for switch\tstatement \\n\\\n \\n\\\n.code \\n\\\n        ; hi\\n\\\n        push\tebp ;hello \\n\\\n\t\tmov\tebp, esp \\n\\\n \\n\\\n    loc_6B2B50:\t\t\t\t; CODE XREF: sub_6B2B40+8\u0018j\\n\\\n        push    eax \\n\\\n\t\tmov\teax, [ebp+arg_0] \\n\\\n        push eax \\n\\\n        mov eax, offset format \\n\\\n        push eax \\n\\\n        call printf \\n\\\n        add esp, 4 \\n\\\n        pop eax \\n\\\n        \\n\\\n        mov ecx, 1\\n\\\n        jmp ds:off_5A74B0[ecx*4]\\n\\\n \\n\\\nloc_58A945:\\n\\\n        push    eax \\n\\\n\t\tmov\teax, [ebp+arg_0] \\n\\\n        push eax \\n\\\n        mov eax, offset format \\n\\\n        push eax \\n\\\n        call printf \\n\\\n        add esp, 4 \\n\\\n        pop eax \\n\\\n        \\n\\\n  end:\\n\\\n        mov\tesp, ebp \\n\\\n\t\tpop\tebp \\n\\\n        retn \\n\\\nsub_hello    endp \\n\\\n    \"\n    };\n\n    // the machine\n    vm86_machine_ref_t machine = vm86_machine();\n    if (machine)\n    {\n        // the lock\n        tb_spinlock_ref_t lock = vm86_machine_lock(machine);\n\n        // enter\n        tb_spinlock_enter(lock);\n\n        // the stack\n        vm86_stack_ref_t stack = vm86_machine_stack(machine);\n\n        // compile proc\n        vm86_proc_ref_t proc = vm86_text_compile(vm86_machine_text(machine), s_code_sub_hello, sizeof(s_code_sub_hello));\n        if (proc)\n        {\n            // add function\n            vm86_machine_function_set(machine, \"printf\", vm86_demo_proc_func_printf);\n\n            // init arguments\n            vm86_stack_push(stack, value);\n\n            // done proc\n            vm86_proc_done(proc);\n\n            // restore stack\n            vm86_stack_pop(stack, tb_null);\n\n            // trace\n            tb_trace_i(\"sub_hello(%x)\", value);\n        }\n\n        // leave\n        tb_spinlock_leave(lock);\n    } \n}\n\nint main(int argc, char** argv)\n{\n    // call this function: sub_hello(0x31415926)\n    vm86_demo_proc_exec_hello(0x31415926);    \n}\n```\n\nThe output results:\n\n```\nhello: 31415926\nhello: 31415926\n```\n\n## Compilation\n\nPlease install [xmake](http://www.xmake.io) first!\n\n### Compile project on macosx \n\n```bash\n$ sudo brew install xmake\n$ xmake f -a i386\n$ xmake\n```\n\n### Compile project on linux \n\n```bash\n$ git clone https://github.com/waruqi/xmake.git\n$ cd xmake\n$ sudo ./install\n$\n$ cd vm86\n$ xmake f -a i386\n$ xmake\n```\n\n### Compile project on windows \n\nDownloads https://github.com/waruqi/xmake/archive/master.zip first.\n\nExtracts it and run install.bat\n\nLastly, we start compiling vm86 project.\n\n```bash\n$ xmake\n```\n\n### Compile project for android \n\n```bash\n$ cd vm86\n$ xmake f -p android --ndk=/xxx/ndk\n$ xmake\n```\n\n## Running\n\n```bash\n$ xmake r demo\n```\n\n## Ida scripts\n\nThe script files: `export_function.idc` and `export_data.idc` in the project directory (idc) \ncan help us to export the given assembly function and data from the ida pro.\n\n#### Contacts\n\n- Email:        \n    - waruqi@gmail.com\n- Website: \t    \n\t- [tboox.org](http://www.tboox.org)\n\t- [tboox.net](http://www.tboox.net)\n\n\n# x86汇编指令脚本虚拟机\n\n## 简介\n\n这是一个可以直接解释执行从ida pro里面提取出来的x86汇编代码的虚拟机。\n\n非常精简，整体架构上不能跟那些成熟的虚拟机相比，主要目标是够用、能用、轻量就行，如果觉得代码架构设计的不是很好的话，也不用过于吐槽哈。。\n\n虽然我还有写过两个比较成熟的虚拟机项目（jvm和avm），虽然架构上比这个更完善，更容易扩展，功能也更强大\n\n但是毕竟是给公司写的，没法拿出来分享。。\n\n## 背景\n\n先说说，为什么要写这个东西。。\n\n之前有段时间，我在用ida逆向分析某些程序的算法，并且要把它提取出来将其跨平台运行，这个时候我首先考虑到是ida的F5插件\n\n毕竟这个可以直接反成c/c++代码，还是很强大的，基本上98%的x86汇编代码，我在通过f5还原成c/c++代码后，都能正常运行。\n\n原本我以为可以万事大吉了，不过就在当我沾沾自喜的时候，发现其中某个汇编函数的c代码，死活就是运行不正常，输出结果不对。\n\n而且那个函数偏偏代码量出奇的大，光c代码就有上万行，而且里面还对数据结构和明文都做了变换和加密，要是慢慢调试的话，得痛苦死。。哎。。\n\n\n没办法，只好另想出路，既然ida还原c有时候不一定完全准确，但是其汇编代码的准确度还是可以保证的，并且从ida中提取的汇编代码\n基本上，不用怎么改，就能编译通过，因此，我先验证了下直接编译汇编代码，运行看看结果对不对。。\n\n结果跟我想的一样，是ok的。。那么问题来了。。\n\n既然汇编运行结果正常，那怎么把它整成跨平台运行呢，直接从编译后x86的指令集进行模拟？工作量有点大，得不偿失。。\n\n有没有取巧些办法呢？当然有，那就是直接解析和运行源码级的x86汇编代码，相当于写个轻量级的精简版x86的脚本虚拟机，来把它运行起来。。\n\n听上去，貌似更麻烦了，其实由于这里只要能够跑通部分需要的汇编指令就行了，因此写个精简版的还是很方便，不需要多少工作量\n\n我前前后后，也就花了一个礼拜就搞定了，非常精简，当然也不完善（也没必要哈，不能跟那些大部头相比）\n\n我的目标就是够用就行，因此我写的差不多厚，就尝试去加载之前有问题的汇编代码，如果发现有指令没实现，那就去实现它，直到跑通为主。。\n\n最后测试结果：\n\n可以正常跑通那个十几万行的汇编代码，并且在arm下运行的性能还算ok，至少满足我的个人需求了。。: )\n\n## 特性\n\n* 跨平台运行支持，可以在windows、linux、macosx以及android, ios上运行x86的汇编代码。。\n* 支持常用x86汇编指令（例如，逻辑操作，跳转，循环，调用，压栈等指令）\n* 支持函数间跳转，以及第三方api调用\n* 支持参数传入，以及运行结束后，返回值的获取\n* 虚拟机的运行粒度为单个函数，函数间的跳转可以通过多个虚拟机实例来完成（轻量的，性能影响不大）\n* 支持线程安全\n* 暂时不支持arm64，只能在32位下运行（有兴趣的同学可以自行修改）\n\n## 例子\n\n我们先从ida中提取一段汇编代码，这段汇编主要是`printf`库函数打印外部传入的数值\n\n```asm\nsub_hello\tproc near \narg_0\t\t= dword\tptr  8 \n.data \n        format db \\\"hello: %x\\\", 0ah, 0dh, 0 \n \noff_5A74B0\tdd offset loc_6B2B50\t; DATA XREF: sub_589100+1832\u0018r \n\t\tdd offset loc_58A945\t; jump table for switch\tstatement \n \n.code \n        ; hi\n        push\tebp ;hello \n\t\tmov\tebp, esp \n \n    loc_6B2B50:\t\t\t\t; CODE XREF: sub_6B2B40+8\u0018j\n        push    eax \n\t\tmov\teax, [ebp+arg_0] \n        push eax \n        mov eax, offset format \n        push eax \n        call printf \n        add esp, 4 \n        pop eax \n        \n        mov ecx, 1\n        jmp ds:off_5A74B0[ecx*4]\n \nloc_58A945:\n        push    eax \n\t\tmov\teax, [ebp+arg_0] \n        push eax \n        mov eax, offset format \n        push eax \n        call printf \n        add esp, 4 \n        pop eax \n        \n  end:\n        mov\tesp, ebp \n\t\tpop\tebp \n        retn \nsub_hello    endp \n```\n\n如果用c来调用的话，就是\n\n```c\nsub_hello(31415926);\n```\n\n输出结果：\n\n```\nhello: 31415926\nhello: 31415926\n```\n\n接下来我们把这段汇编直接放到我们的虚拟机里面执行：\n\n```c\nstatic tb_void_t vm86_demo_proc_exec_hello(tb_uint32_t value)\n{\n    // 上述汇编代码的字符串表示\n    static tb_char_t const s_code_sub_hello[] = \n    {\n\"sub_hello\tproc near \\n\\\narg_0\t\t= dword\tptr  8 \\n\\\n.data \\n\\\n        format db \\\"hello: %x\\\", 0ah, 0dh, 0 \\n\\\n \\n\\\noff_5A74B0\tdd offset loc_6B2B50\t; DATA XREF: sub_589100+1832\u0018r \\n\\\n\t\tdd offset loc_58A945\t; jump table for switch\tstatement \\n\\\n \\n\\\n.code \\n\\\n        ; hi\\n\\\n        push\tebp ;hello \\n\\\n\t\tmov\tebp, esp \\n\\\n \\n\\\n    loc_6B2B50:\t\t\t\t; CODE XREF: sub_6B2B40+8\u0018j\\n\\\n        push    eax \\n\\\n\t\tmov\teax, [ebp+arg_0] \\n\\\n        push eax \\n\\\n        mov eax, offset format \\n\\\n        push eax \\n\\\n        call printf \\n\\\n        add esp, 4 \\n\\\n        pop eax \\n\\\n        \\n\\\n        mov ecx, 1\\n\\\n        jmp ds:off_5A74B0[ecx*4]\\n\\\n \\n\\\nloc_58A945:\\n\\\n        push    eax \\n\\\n\t\tmov\teax, [ebp+arg_0] \\n\\\n        push eax \\n\\\n        mov eax, offset format \\n\\\n        push eax \\n\\\n        call printf \\n\\\n        add esp, 4 \\n\\\n        pop eax \\n\\\n        \\n\\\n  end:\\n\\\n        mov\tesp, ebp \\n\\\n\t\tpop\tebp \\n\\\n        retn \\n\\\nsub_hello    endp \\n\\\n    \"\n    };\n\n    // 定义一个虚拟机\n    vm86_machine_ref_t machine = vm86_machine();\n    if (machine)\n    {\n        // 锁定虚拟机，保证线程安全（这个根据需要，可选）\n        tb_spinlock_ref_t lock = vm86_machine_lock(machine);\n        tb_spinlock_enter(lock);\n\n        // 获取虚拟机的堆栈\n        vm86_stack_ref_t stack = vm86_machine_stack(machine);\n\n        // 编译上面的汇编代码，并生成一个过程对象的引用\n        vm86_proc_ref_t proc = vm86_text_compile(vm86_machine_text(machine), s_code_sub_hello, sizeof(s_code_sub_hello));\n        if (proc)\n        {\n            // 添加汇编里面需要调用到的外部库函数\n            vm86_machine_function_set(machine, \"printf\", vm86_demo_proc_func_printf);\n\n            // 初始化调用参数\n            vm86_stack_push(stack, value);\n\n            // 执行这个汇编代码\n            vm86_proc_done(proc);\n\n            // 恢复堆栈，获取返回值（这里是void的，传null就行了）\n            vm86_stack_pop(stack, tb_null);\n        }\n\n        // 解锁虚拟机\n        tb_spinlock_leave(lock);\n    } \n}\n\nint main(int argc, char** argv)\n{\n    // 执行这个汇编函数：sub_hello(0x31415926)\n    vm86_demo_proc_exec_hello(0x31415926);    \n}\n```\n\n\n如果ok，那么输出结果当然也是：\n\n```\nhello: 31415926\nhello: 31415926\n```\n\n## 编译\n\n需要先安装[xmake](https://xmake.io)，请到xmake官方文档库查看各个平台的安装流程。\n\n### 在 macosx 上编译\n\n注：新系统已经默认不支持i386编译，缺少相关系统库\n\n```bash\n$ xmake f -a i386\n$ xmake\n```\n\n### 在 linux 上编译\n\n```bash\n$ xmake f -a i386\n$ xmake\n```\n\n### 在 windows 上编译\n\n```bash\n$ xmake\n```\n\n### 编译android版本\n\n```bash\n$ xmake f -p android --ndk=/xxx/ndk\n$ xmake\n```\n\n## 运行\n\n运行测试程序：\n\n```bash\n$ xmake r demo\n```\n\n## 后话\n\n最后，在项目的idc目录下，有两个脚本工具：`export_function.idc` 和 `export_data.idc` 可以用来辅助我们从ida中导出指定的汇编函数和数据\n\n## 联系方式\n\n- Email:        \n    - waruqi@gmail.com\n- Website: \t    \n\t- [tboox.org](https://tboox.org)\n- QQ(group):    \n    - 343118190(full), 662147501\n- Telegram(group):\n    - https://t.me/tbooxorg\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftboox%2Fvm86","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftboox%2Fvm86","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftboox%2Fvm86/lists"}