这是一个能够直接解释执行从ida pro里面提取出来的x86汇编代码的虚拟机。linux
很是精简,总体架构上不能跟那些成熟的虚拟机相比,主要目标是够用、能用、轻量就行,若是以为代码架构设计的不是很好的话,也不用过于吐槽哈。。android
虽然我还有写过两个比较成熟的虚拟机项目(jvm和avm),虽然架构上比这个更完善,更容易扩展,功能也更强大ios
可是毕竟是给公司写的,无法拿出来分享。。c++
先说说,为何要写这个东西。。git
以前有段时间,我在用ida逆向分析某些程序的算法,而且要把它提取出来将其跨平台运行,这个时候我首先考虑到是ida的F5插件github
毕竟这个能够直接反成c/c++代码,仍是很强大的,基本上98%的x86汇编代码,我在经过f5还原成c/c++代码后,都能正常运行。算法
本来我觉得能够万事大吉了,不过就在当我沾沾自喜的时候,发现其中某个汇编函数的c代码,死活就是运行不正常,输出结果不对。macos
并且那个函数恰恰代码量出奇的大,光c代码就有上万行,并且里面还对数据结构和明文都作了变换和加密,要是慢慢调试的话,得痛苦死。。哎。。windows
没办法,只好另想出路,既然ida还原c有时候不必定彻底准确,可是其汇编代码的准确度仍是能够保证的,而且从ida中提取的汇编代码 基本上,不用怎么改,就能编译经过,所以,我先验证了下直接编译汇编代码,运行看看结果对不对。。api
结果跟我想的同样,是ok的。。那么问题来了。。
既然汇编运行结果正常,那怎么把它整成跨平台运行呢,直接从编译后x86的指令集进行模拟?工做量有点大,得不偿失。。
有没有取巧些办法呢?固然有,那就是直接解析和运行源码级的x86汇编代码,至关于写个轻量级的精简版x86的脚本虚拟机,来把它运行起来。。
听上去,貌似更麻烦了,其实因为这里只要可以跑通部分须要的汇编指令就好了,所以写个精简版的仍是很方便,不须要多少工做量
我前先后后,也就花了一个礼拜就搞定了,很是精简,固然也不完善(也不必哈,不能跟那些大部头相比)
个人目标就是够用就行,所以我写的差很少厚,就尝试去加载以前有问题的汇编代码,若是发现有指令没实现,那就去实现它,直到跑通为主。。
最后测试结果:
能够正常跑通那个十几万行的汇编代码,而且在arm下运行的性能还算ok,至少知足个人我的需求了。。: )
咱们先从ida中提取一段汇编代码,这段汇编主要是printf
库函数打印外部传入的数值
sub_hello proc near
arg_0 = dword ptr 8
.data
format db \"hello: %x\", 0ah, 0dh, 0
off_5A74B0 dd offset loc_6B2B50 ; DATA XREF: sub_589100+1832
dd offset loc_58A945 ; jump table for switch statement
.code
; hi
push ebp ;hello
mov ebp, esp
loc_6B2B50: ; CODE XREF: sub_6B2B40+8
push eax
mov eax, [ebp+arg_0]
push eax
mov eax, offset format
push eax
call printf
add esp, 4
pop eax
mov ecx, 1
jmp ds:off_5A74B0[ecx*4]
loc_58A945:
push eax
mov eax, [ebp+arg_0]
push eax
mov eax, offset format
push eax
call printf
add esp, 4
pop eax
end:
mov esp, ebp
pop ebp
retn
sub_hello endp
若是用c来调用的话,就是
sub_hello(31415926);
输出结果:
hello: 31415926
hello: 31415926
接下来咱们把这段汇编直接放到咱们的虚拟机里面执行:
static tb_void_t vm86_demo_proc_exec_hello(tb_uint32_t value) { // 上述汇编代码的字符串表示 static tb_char_t const s_code_sub_hello[] = { "sub_hello proc near \n\ arg_0 = dword ptr 8 \n\ .data \n\ format db \"hello: %x\", 0ah, 0dh, 0 \n\ \n\ off_5A74B0 dd offset loc_6B2B50 ; DATA XREF: sub_589100+1832 \n\ dd offset loc_58A945 ; jump table for switch statement \n\ \n\ .code \n\ ; hi\n\ push ebp ;hello \n\ mov ebp, esp \n\ \n\ loc_6B2B50: ; CODE XREF: sub_6B2B40+8\n\ push eax \n\ mov eax, [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\ \n\ loc_58A945:\n\ push eax \n\ mov eax, [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 esp, ebp \n\ pop ebp \n\ retn \n\ sub_hello endp \n\ " }; // 定义一个虚拟机 vm86_machine_ref_t machine = vm86_machine(); if (machine) { // 锁定虚拟机,保证线程安全(这个根据须要,可选) tb_spinlock_ref_t lock = vm86_machine_lock(machine); tb_spinlock_enter(lock); // 获取虚拟机的堆栈 vm86_stack_ref_t stack = vm86_machine_stack(machine); // 编译上面的汇编代码,并生成一个过程对象的引用 vm86_proc_ref_t proc = vm86_text_compile(vm86_machine_text(machine), s_code_sub_hello, sizeof(s_code_sub_hello)); if (proc) { // 添加汇编里面须要调用到的外部库函数 vm86_machine_function_set(machine, "printf", vm86_demo_proc_func_printf); // 初始化调用参数 vm86_stack_push(stack, value); // 执行这个汇编代码 vm86_proc_done(proc); // 恢复堆栈,获取返回值(这里是void的,传null就好了) vm86_stack_pop(stack, tb_null); } // 解锁虚拟机 tb_spinlock_leave(lock); } } int main(int argc, char** argv) { // 执行这个汇编函数:sub_hello(0x31415926) vm86_demo_proc_exec_hello(0x31415926); }
若是ok,那么输出结果固然也是:
hello: 31415926
hello: 31415926
须要先安装xmake
$ sudo brew install xmake $ xmake f -a i386 $ xmake
$ git clone https://github.com/waruqi/xmake.git $ cd xmake $ sudo ./install $ $ cd vm86 $ xmake f -a i386 $ xmake
下载 https://github.com/waruqi/xmake/archive/master.zip
解压运行里面的 install.bat 安装xmake后进行编译:
$ xmake
$ cd vm86 $ xmake f -p android --ndk=/xxx/ndk $ xmake
运行测试程序:
$ xmake r demo
最后,在项目的idc目录下,有两个脚本工具:export_function.idc
和 export_data.idc
能够用来辅助咱们从ida中导出指定的汇编函数和数据
http://www.tboox.org/cn/2016/07/26/x86-script-instruction-virtual-machine/