文档章节

分享个用 c 写的 x86 汇编指令脚本虚拟机

f
 feifei_fei
发布于 2016/08/13 00:04
字数 1710
阅读 41
收藏 0
点赞 0
评论 0

这是一个可以直接解释执行从 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

原文出处:http://www.tboox.org/cn/2016/07/26/x86-script-instruction-virtual-machine/

 

本文转载自:http://www.v2ex.com/t/297674#reply7

共有 人打赏支持
f
粉丝 0
博文 1
码字总数 0
作品 0
呼和浩特
汇编指令脚本虚拟机--vm86

x86汇编指令脚本虚拟机 简介: 这是一个可以直接解释执行从ida pro里面提取出来的x86汇编代码的虚拟机。 非常精简,整体架构上不能跟那些成熟的虚拟机相比,主要目标是够用、能用、轻量就行。...

ruki ⋅ 2016/07/27 ⋅ 2

基于虚拟机的软件保护技术

本文并不打算对vmprotect或其它某款软件安全套件进行深入讨论,而着眼于研究基于虚拟机的软件保护技术的起源、思想和实现。 现有软件保护技术概述 传统的软件保护技术,根据针对对象不同,可...

linuxprobe ⋅ 2016/07/18 ⋅ 0

C/C++ 编译器--AsmJit

AsmJit 是一个完整的 JIT(Just-In-Time,运行时刻)的针对 C++ 语言的汇编器,可以生成兼容 x86 和 x64 架构的原生代码,不仅支持整个 x86/x64 的指令集(包括传统的 MMX 和最新的 AVX2 指令集...

匿名 ⋅ 2009/06/27 ⋅ 1

tinyvm不是java虚拟机

这不是java虚拟机,只是一个虚拟了一部分x86汇编指令的虚拟机(解释执行),并且实现也非常简单,寻址方式只有简单的寄存器寻址。

大卷卷 ⋅ 2011/12/16 ⋅ 0

纯 C# 实现 Hook 功能

原文出处:zhao.yj 发布一个自己写的用于Hook .Net方法的类库,代码量不大,完全的C#代码实现,是一个比较有趣的功能,分享出来希望能和大家共同探讨 安装:Install-Package DotNetDetour 源...

zhao.yj ⋅ 2016/06/03 ⋅ 0

计算机硬件及操作系统基础学习笔记

计算机体系结构:运算器+控制器=CPU中央处理单元 运算器:就是一个加法器,只能做二进制计算,算数和逻辑运算,如加减乘除,移位 控制器:控制协调运算器和存储器交流,接受控制指令,指示运...

love8023icando ⋅ 2016/03/30 ⋅ 0

嵌入在 Python 的 x86-64 汇编器--PeachPy

PeachPy 是一个用于编写高性能汇编内核的 Python 框架,可在汇编中编写模块。 它自动化了一些细节,并允许使用 Python 生成重复的汇编代码序列。 PeachPy 旨在简化编写优化的汇编内核,同时保...

匿名 ⋅ 2016/12/05 ⋅ 0

Cosmos的基石:IL2CPU编译器--C#开源操作系统学习系列三

本文的代码包以cosmos-12304.zip为例(从这个包开始,COSMOS的内核算是有了个基本的雏形,就像是一颗大树在出芽前会先长出庞大的根系,现在就要破土长出第一颗芽了) IL2CPU之于COSMOS就相当...

李恒哲 ⋅ 2011/02/23 ⋅ 0

X86汇编学习小结

本文起源:想看看C/C++中的Strict Aliasing,可是找到的资料几乎都用C/C++对应的汇编代码来解释一些概念。看不懂这些汇编,只好先简单整理一点汇编相关的资料了。 找几个编译器,找个最最简单...

晨曦之光 ⋅ 2012/05/08 ⋅ 0

Qemu 简述

Qemu 架构 Qemu 是纯软件实现的虚拟化模拟器,几乎可以模拟任何硬件设备,我们最熟悉的就是能够模拟一台能够独立运行操作系统的虚拟机,虚拟机认为自己和硬件打交道,但其实是和 Qemu 模拟出...

chambai ⋅ 2017/11/19 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

从方法论到零售客户实践 解码阿里巴巴数据中台——2018上海云栖大会

摘要: 一、数据中台之道 6月8日,上海云栖大会进入了第二天的议程,数据中台专场论坛座无虚席,数据中台总架构师邓中华女士向在场的观众介绍了数据中台的衍生发展之道。 基于OneID、OneData...

阿里云云栖社区 ⋅ 18分钟前 ⋅ 0

Ubuntu部署django问题汇总

使用Anaconda3的Python3.6的pip安装UWSGI报错 原因是gcc版本不兼容,安装4.7并修改gccsudo apt-get install gcc-4.7sudo mv /usr/bin/gcc /usr/bin/gcc.baksudo ln -s /usr/bin/gcc-4.......

wuyaSama ⋅ 21分钟前 ⋅ 0

从方法论到零售客户实践 解码阿里巴巴数据中台——2018上海云栖大会

摘要: 一、数据中台之道 6月8日,上海云栖大会进入了第二天的议程,数据中台专场论坛座无虚席,数据中台总架构师邓中华女士向在场的观众介绍了数据中台的衍生发展之道。 基于OneID、OneData...

猫耳m ⋅ 21分钟前 ⋅ 0

Docker减肥小记

如果经常使用 docker,你会发现 docker 占用的资源膨胀很快,其中最明显也最容易被察 如何快速的清理 docker 占用的系统资源,具体点说就是删除那些无用的镜像、容器、网络和数据卷… 1、查看...

寰宇01 ⋅ 32分钟前 ⋅ 0

微信小程序中如何使用WebSocket实现长连接(含完整源码)

本文由腾讯云技术团队原创,感谢作者的分享。 1、前言 微信小程序提供了一套在微信上运行小程序的解决方案,有比较完整的框架、组件以及 API,在这个平台上面的想象空间很大。腾讯云研究了一...

JackJiang- ⋅ 40分钟前 ⋅ 0

定制库到Maven本地资源库

1.如果只有定制库的JAR文件 下载链接如下:pdf.jar 2.使用命令转换成Maven本地资源 mvn install:install-file -Dfile=/Users/manager/Downloads/clj-pdf-2.2.33.jar -DgroupId=clj-pdf -Dar......

年少爱追梦 ⋅ 44分钟前 ⋅ 0

高仿springmvc之xuchen-mvc

package org.mvc.framework.servlet; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.......

徐志 ⋅ 46分钟前 ⋅ 0

关于自定义URLStreamHandler的一次踩坑

关于自定义URLStreamHandler的一次踩坑 20180625 lambo init 说明 一般自定义实现url的协议解析.方案为实现URLStreamHandler.实现其 openConnection 就可以了, 如果我们执行 new URL("xx://...

林小宝 ⋅ 47分钟前 ⋅ 0

【SM2证书】利用BC的X509v3CertificateBuilder组装X509国密证书

演示证书文件 链接: https://pan.baidu.com/s/1ijHNnMQJj7jzW-jXEVd6Gg 密码: vfva 所需jar包 <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on --> <dependenc......

小帅帅丶 ⋅ 48分钟前 ⋅ 0

用Calendar 实现 计算 一段时间的毫秒值

Calendar c=Calendar.getInstance();c.add(Calendar.MONTH, -1);int lastMonthMaxDay=c.getActualMaximum(Calendar.DAY_OF_MONTH);c.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH)......

岸芷汀兰 ⋅ 52分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部