简介

这是一个可以直接解释执行从ida pro里面提取出来的x86汇编代码的虚拟机。

非常精简,整体架构上不能跟那些成熟的虚拟机相比,主要目标是够用、能用、轻量就行,如果觉得代码架构设计的不是很好的话,也不用过于吐槽哈。。

虽然我还有写过两个比较成熟的虚拟机项目(jvm和avm),虽然架构上比这个更完善,更容易扩展,功能也更强大

但是毕竟是给公司写的,没法拿出来分享。。

背景

先说说,为什么要写这个东西。。

之前有段时间,我在用ida逆向分析某些程序的算法,并且要把它提取出来将其跨平台运行,这个时候我首先考虑到是ida的F5插件

毕竟这个可以直接反成c/c++代码,还是很强大的,基本上98%的x86汇编代码,我在通过f5还原成c/c++代码后,都能正常运行。

原本我以为可以万事大吉了,不过就在当我沾沾自喜的时候,发现其中某个汇编函数的c代码,死活就是运行不正常,输出结果不对。

而且那个函数偏偏代码量出奇的大,光c代码就有上万行,而且里面还对数据结构和明文都做了变换和加密,要是慢慢调试的话,得痛苦死。。哎。。

没办法,只好另想出路,既然ida还原c有时候不一定完全准确,但是其汇编代码的准确度还是可以保证的,并且从ida中提取的汇编代码 基本上,不用怎么改,就能编译通过,因此,我先验证了下直接编译汇编代码,运行看看结果对不对。。

结果跟我想的一样,是ok的。。那么问题来了。。

既然汇编运行结果正常,那怎么把它整成跨平台运行呢,直接从编译后x86的指令集进行模拟?工作量有点大,得不偿失。。

有没有取巧些办法呢?当然有,那就是直接解析和运行源码级的x86汇编代码,相当于写个轻量级的精简版x86的脚本虚拟机,来把它运行起来。。

听上去,貌似更麻烦了,其实由于这里只要能够跑通部分需要的汇编指令就行了,因此写个精简版的还是很方便,不需要多少工作量

我前前后后,也就花了一个礼拜就搞定了,非常精简,当然也不完善(也没必要哈,不能跟那些大部头相比)

我的目标就是够用就行,因此我写的差不多厚,就尝试去加载之前有问题的汇编代码,如果发现有指令没实现,那就去实现它,直到跑通为主。。

最后测试结果:

可以正常跑通那个十几万行的汇编代码,并且在arm下运行的性能还算ok,至少满足我的个人需求了。。: )

特性

  • 跨平台运行支持,可以在windows、linux、macosx以及android, ios上运行x86的汇编代码。。
  • 支持常用x86汇编指令(例如,逻辑操作,跳转,循环,调用,压栈等指令)
  • 支持函数间跳转,以及第三方api调用
  • 支持参数传入,以及运行结束后,返回值的获取
  • 虚拟机的运行粒度为单个函数,函数间的跳转可以通过多个虚拟机实例来完成(轻量的,性能影响不大)
  • 支持线程安全
  • 暂时不支持arm64,只能在32位下运行(有兴趣的同学可以自行修改)

例子

我们先从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

在 macosx 上编译

$ sudo brew install xmake
$ xmake f -a i386
$ xmake

在 linux 上编译

$ git clone https://github.com/waruqi/xmake.git
$ cd xmake
$ sudo ./install
$
$ cd vm86
$ xmake f -a i386
$ xmake

在 windows 上编译

下载 https://github.com/waruqi/xmake/archive/master.zip

解压运行里面的 install.bat 安装xmake后进行编译:

$ xmake

编译android版本

$ 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/

x86汇编指令脚本虚拟机的更多相关文章

  1. 开始逆向objc基础准备(一)简单认识一下arm32,以及与x86汇编指令类比

    ARM32体系中有31或33个通用寄存器,没有特定的某种态下有r0-r15一共16个寄存器,快速中断态下有另一组r8-r12备份寄存器,在用户态和系统态之外其它态下都各自有一组r13-r14备份寄存器 ...

  2. x86汇编指令详解

    80x86指令系统 80x86指令系统,指令按功能可分为以下七个部分. (1) 数据传送指令. (2) 算术运算指令. (3) 逻辑运算指令. (4) 串操作指令. (5) 控制转移指令. (6) 处 ...

  3. x86汇编指令具体解释

    80x86指令系统 80x86指令系统,指令按功能可分为下面七个部分. (1) 数据传送指令. (2) 算术运算指令. (3) 逻辑运算指令. (4) 串操作指令. (5) 控制转移指令. (6) 处 ...

  4. 为什么X86汇编中的mov指令不支持内存到内存的寻址?

    在X86汇编中,MOV [0012H], [0016H]这种指令是不允许的,至少得有一个操作数是寄存器.当然,这种问题在用高级语言的时候看不到,感觉好像基本上都是从内存到内存啊,为毛到了汇编就不行了? ...

  5. 【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

    作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42408137 转载请著名出处 本博客相关文档下载 :  -- AR ...

  6. 寄存器理解 及 X86汇编入门

    本文整理自多材料源,感谢原址分享,请查看末尾Url I, 汇编语言分类: 汇编语言和CPU息息相关,但是不能把汇编语言完全等同于CPU的机器指令.不同架构的CPU指令并不相同,如x86,powerpc ...

  7. x86汇编反编译到c语言之——(1)表达式求值及赋值语句

    一. 反编译一种可能的实现方式 我们的目的是将多种平台的汇编如x86,ARM,6502反编译为c语言,所以实现时先将多种汇编转化为 特定虚拟机汇编语言,然后只需要将虚拟机汇编语言反编译为c语言.其中多 ...

  8. x86汇编反编译到c语言之——(2)if语句

    一. 测试的C语句及编译后的x86汇编代码 int a; int b; int main(void) { int c; if (c) a = 4; else b = 5; return 0; } 1 ...

  9. ARM汇编指令调试方法

    学习ARM汇编时,少不了对ARM汇编指令的调试.作为支持多语言的调试器,gdb自然是较好的选择.调试器工作时,一般通过修改代码段的内容构造trap软中断指令,实现程序的暂停和程序执行状态的监控.为了在 ...

随机推荐

  1. 使用Apache FtpServer搭建FTP服务器 [FlashFXP]

    <server xmlns="http://mina.apache.org/ftpserver/spring/v1" xmlns:xsi="http://www.w ...

  2. php面试题7(1、unset变量是删除栈变量,并不删除堆变量)(2、php爬虫特别简单: 可以file_get_contents和直接fopen)

    php面试题7(1.unset变量是删除栈变量,并不删除堆变量)(2.php爬虫特别简单: 可以file_get_contents和直接fopen) 一.总结 1.unset变量是删除栈变量,并不删除 ...

  3. Web全栈工程师修养

    全栈工程师现在是个很热的话题,如何定义全栈工程师?在著名的问答网站Quora上有人提出了这个问题,其中一个获得了高票的回答是: 全栈工程师是指,一个能处理数据库.服务器.系统工程和客户端的所有工作的工 ...

  4. tplink-如何远程WEB管理路由器?

    http://service.tp-link.com.cn/detail_article_185.html 如何远程WEB管理路由器? 新版tplink怎么远程Web管理? https://www.1 ...

  5. 在SSMS查询分析器中显示行号

    有网友问及,看到Insus.NET帮他解决问题分享的截屏时,发现代码中有显示行号.而他的没有. Step1: Go to Tools > Options Step2: In the Option ...

  6. MySQL搜索:WHERE

    MySQL指定搜索条件进行搜索能够使用where条件. 在SELECT语句中.数据依据WHERE子语句中指定的条件进行过滤,WHERE子语句在表名之后给出. product表例如以下: a 查找价格等 ...

  7. iOS过场动画调研笔记

    前言 因项目须要,近期一段时间都在调研iOS的过场动画.对于我来说这是一个之前没有太涉及的领域,所以有必要把调研的过程和自己的一些理解纪录下来 为什么要自己定义过场动画? 假设大家有关注Materia ...

  8. Android Material Design 5.0 PickerDialog

    5.0系统下的时间选择器效果图: 该项目兼容到3.0下面所以用第三方开源项目:actionbarsherlock,动画效果兼容:nineoldandroids-2.4.0.jar,格式转换器:joda ...

  9. L脚本语言实现文件加解密

    L脚本语言中能够对内存对象进行AES加解密.我们能够非常easy地实现文件加解密 #scp #定义一个秘钥字符串 定义:字符串,str1,abcdefg 打开:文件,file1,c:\1.txt 打开 ...

  10. Java实现的并发任务处理实例

    本文实例讲述了Java实现的并发任务处理方法.分享给大家供大家参考,具体如下: public void init() { super.init(); this.ioThreadPool = new T ...