首页
社区
课程
招聘
[原创]UnicornVM-全平台全架构正式发布
发表于: 2020-2-29 18:50 12346

[原创]UnicornVM-全平台全架构正式发布

2020-2-29 18:50
12346

这个月初,我们发布了适用于Android armeabi-v7a的UnicornVM,月中陆陆续续发布了适用于Android arm64-v8a、iOS arm64的版本。这三个版本原计划是3、4、5月逐步发布的,但是特殊时期我们需要响应国家号召居家不出门不添乱,所以咱们只有老实居家使出洪荒之力加油搞开发写代码了。结果就是,我们整整提前了2个月完成UnicornVM的测试和发布,可喜可贺。

今天,我们修复了arm64版本若干崩溃的问题,也达到了比较好的稳定性,所以arm32/arm64两个架构、Android/iOS两个平台目前都处于稳定版本的状态。

从年前1月份提出UnicornVM这个新产品概念到现在已经有2个月的时间了,可能很多朋友还不是很了解这个框架到底有什么作用,可以拿来干嘛?那么,今天这篇文章,我们将详细介绍一下它的结构和用法。

首先从字面UnicornVM上理解,拆成两部分Unicorn、VM。其中Unicorn是由越南大佬Nguyen Anh Quynh开发的虚拟CPU框架,底层基于Qemu,而VM是指Virtual Machine也即虚拟机。虚拟机一般有两种解释,一种是指系统虚拟机,比如VMWare、VirtualBox,一种是指软件虚拟机,比如Python解释器,此处我们的UnicornVM指第二种情况。那么UnicornVM与Unicorn区别在哪里呢?

我们都知道,有意义的程序要正常运行起来,需要代码、数据两部分。把代码、数据这两者统一起来看其实它们都是内存,即便是存放在文件系统里面的数据也是需要加载到内存里面才能访问。但是由于Unicorn是基于Qemu开发而来的,因此它自带虚拟内存管理。所以我们想要正常让它模拟执行函数代码时,还需要手动处理内存映射的问题,这为我们在某些场景使用带来了各种不方便。
所以,我们可以直接让Unicorn与程序的运行内存交互,而抛弃它的Qemu虚拟内存吗?这其实就是UnicornVM解决的核心问题。你只需要丢一个合法的函数地址给它,然后它就可以达到像直接执行这个函数一样的效果。同时,由于这是一个虚拟机架构,所以我们可以在指令级别控制这个函数的执行过程,你可以理解成我们把arm/arm64指令集脚本化了,而UnicornVM就是这个脚本化系统的解释器,就像Python解释器、JavaScript解释器执行它们各自的字节码一样。目前,UnicornVM框架导出了2个API以及若干数据结构,如下:

// run function 'fn' on our VCPU with 'ctx'
// return value is x0
VCAPI long vc_run_interp(const void *fn, const vc_context_t *ctx);

// make a wrapper for function 'fn' with 'usrctx','callback'
// return value is a new function pointer which will run under our VCPU
// you can replace this pointer to target's function pointer
// like C++-Vtable/Script-Native-Bridge
// if return null, you should check errno
VCAPI const void *vc_make_callee(const void *fn, void *usrctx,
                                 fn_vc_callback_t callback);
vc_run_interp表示直接将函数跑在虚拟机解释器里面,第一个参数fn代表要执行的函数地址,第二个参数ctx是执行这个函数时候由调用者提供的上下文,包括寄存器参数、回调函数等信息。
vc_make_callee表示对指定的函数生成一个包装函数,而这个包装函数就跑在虚拟机解释器里面,第一个参数fn代表要执行的函数地址,第二个参数usrctx是调用者自定义的上下文,第三个参数callback是解释器的回调函数。
我们重点看一下解释器回调相关的结构,它的定义如下:
// callback prototype
typedef vc_callback_return_t (*fn_vc_callback_t)(vc_callback_args_t *args);

// callback return type
typedef enum vc_callback_return_t {
  cbret_continue,    // let interp continue
  cbret_processed,   // already processed by callback implementation
  cbret_recursive,   // interp this function recursively
  cbret_directcall,  // call this function directly
} vc_callback_return_t;

// opcode type for callback args
typedef enum vc_optype_t {
  vcop_read,    // memory read
  vcop_write,   // memory write
  vcop_call,    // function call
  vcop_return,  // function return
  vcop_svc,     // arch syscall
} vc_optype_t;

// callback args
typedef struct vc_callback_args_t {
  // your own context passed for vc_run_interp/vc_make_callee
  const void *usrctx;
  // arm64 execution context
  vc_arm64ctx_t *arm64ctx;
  // current opcode
  vc_optype_t op;
  union {
    // for vcop_read/vcop_write
    struct {
      const void *src;
      void *dst;
      int byte;
    } rw;
    // for vcop_call
    struct {
      const void *callee;
    } call;
    // for vcop_return
    struct {
      const void *hitaddr;  // which address hit return
    } ret;
    // for vcop_svc
    struct {
      int sysno;  // syscall number
    } svc;
  } info;
} vc_callback_args_t;
它的作用是虚拟机解释器在执行到内存读写、函数调用/返回、系统调用等指令时,调用由用户指定的这个回调,从而达到用户可控的目的。回调函数的返回值用于告诉虚拟机解释器该如何处理本条指令,比如递归调用、忽略、默认处理等,如果调用者已经处理了这条指令,则可以返回cbret_processed。我们看一下sample里面的代码片段,如下:
// run interpretee directly
int vrun_print_message(const char *reason, const char **argv, FILE *cblogfp, fn_vc_callback_t cb) {
  vc_context_t ctx;
  memset(&ctx, 0, sizeof(ctx));
  ctx.usrctx = cblogfp;
  ctx.callback = cb;
  ctx.regctx.r[0].p = reason;
  ctx.regctx.r[1].p = (void *)argv;
  return (int)(long)vc_run_interp((void *)print_message, &ctx);
}
这是sample里面的例子代码,将函数print_message直接跑在解释器下面。
// run interpretee with a wrapper
int wrapper_print_message(const char *reason, const char **argv, FILE *cblogfp,
                          fn_vc_callback_t cb) {
  const void *fnptr = vc_make_callee((void *)print_message, cblogfp, cb);
  return ((int (*)(const char *, const char **))fnptr)(reason, argv);
}
这是sample里面的例子代码,将函数print_message生成一个代理函数指针,然后直接调用这个指针,代理函数将会执行在虚拟机解释器里面。
好了,看到这里,我想朋友们已经学会并理解了UnicornVM的核心理念和功能,如果你在使用它的过程中有任何疑问、建议、问题,欢迎反馈,么么哒。

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
免费 6
支持
分享
最新回复 (32)
雪    币: 78
活跃值: (1219)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
2
不好意思,只是看你那张图有
最后于 2020-2-29 22:39 被淡淡的荧光编辑 ,原因:
2020-2-29 20:25
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
3
淡淡的荧光 老哥是不是对vmp有什么误解,vmp是对汇编指令进行虚拟化,你这是软件模拟硬件,汇编指令并没有变化,当然可以对汇编指令进行加密,再仿真引擎进行解密,但这只能算是指令加密吧。另外软件模拟硬件对性能影响挺 ...
是的,我对vmp误解非常深,深到文章一颗字都没有敢提vmp。
2020-2-29 21:50
1
雪    币: 997
活跃值: (420)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
4
2020-3-1 00:20
0
雪    币: 222
活跃值: (729)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
强大,可以处理系统api吗?
2020-3-1 00:22
0
雪    币: 357
活跃值: (3138)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
6
产品很多,高产
2020-3-1 04:26
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
7
兴兴 强大[em_71],可以处理系统api吗?
可以,处理对象是函数,不分是否是系统API。
2020-3-1 09:44
0
雪    币: 2075
活跃值: (4447)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
你一个人开发的?一堆仓库我给看晕了,不过你能讲讲你的模拟器相比市面上其他的有什么区别吗。
2020-3-2 09:40
0
雪    币: 878
活跃值: (496)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
LidaDbg是跑在真机上还是虚拟机上的?
有个设想是, 用IDA直接调试真机, 不过代码由Unicorn解释执行
2020-3-2 09:52
0
雪    币: 2141
活跃值: (7200)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
10
wocao,一个人搞这么多项目?这么快?
2020-3-2 10:03
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
11
爱吃菠菜 wocao,一个人搞这么多项目?这么快?
没有没有,是个小团队。
2020-3-2 10:27
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
12
rrrfff LidaDbg是跑在真机上还是虚拟机上的? 有个设想是, 用IDA直接调试真机, 不过代码由Unicorn解释执行
LidaDbg基于lldb/lldb-server,真机和虚拟机都支持。
2020-3-2 10:29
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
13
小黄鸭爱学习 你一个人开发的?一堆仓库我给看晕了,不过你能讲讲你的模拟器相比市面上其他的有什么区别吗。
一个小团队。我们基于anbox,anbox基于lxc,也即我们的模拟器基于Linux容器技术,资源复用和多开就很厉害了。同时支持Win10 WSL2,不需要单独VBox虚拟机。以及脚本框架Fridobot、PantaEngine执行引擎都是为这个模拟器配套的,但是现在还没有做整合。
2020-3-2 10:33
0
雪    币: 22
活跃值: (30)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
这个工作和棒,发布在哪了? 好久没到看雪上溜达了
2020-3-3 12:42
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
15
supermilg 这个工作和棒,发布在哪了? 好久没到看雪上溜达了
https://gitee.com/geekneo/VirtualCode
2020-3-3 15:26
0
雪    币: 3937
活跃值: (3327)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
16
为啥不用github
2020-3-3 16:32
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
17
StriveMario 为啥不用github
我也想用啊,奈何速度实在太慢。
2020-3-3 19:44
0
雪    币: 1157
活跃值: (847)
能力值: ( LV8,RANK:150 )
在线值:
发帖
回帖
粉丝
18
注入目标进程,直接在需要模拟的那个函数头部hook一下,然后在fake函数中启动unicorn, 映射一下内存,接着还是用玩unicorn的那些步骤,是不是也能达到同样的效果?

因为我看了下,跟直接用unicorn的最大区别就是可以在原程序内存中直接执行,省去了一些手动恶心的事情
2020-3-7 23:22
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
19
freakish 注入目标进程,直接在需要模拟的那个函数头部hook一下,然后在fake函数中启动unicorn, 映射一下内存,接着还是用玩unicorn的那些步骤,是不是也能达到同样的效果? 因为我看了下,跟 ...
很明显答案是:No。
比如,ldr x3, [x17, x8]这条指令,你如何映射内存? 
2020-3-8 08:51
0
雪    币: 2075
活跃值: (4447)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
你可以理解成我们把arm/arm64指令集脚本化了,而UnicornVM就是这个脚本化系统的解释器。这句话才是这个架构的本质所在吧。相对与之前直接使用unicorn仿真arm 调用 so。不知道你这个框架能做到哪些简便之处。
2020-3-9 09:51
0
雪    币: 1157
活跃值: (847)
能力值: ( LV8,RANK:150 )
在线值:
发帖
回帖
粉丝
21
GeekNeo 很明显答案是:No。 比如,ldr x3, [x17, x8]这条指令,你如何映射内存?
我没有测试过这条指令具体映射的时候出现的问题, 如果有问题,那应该是属于unicorn 引擎本身需要加强的地方
2020-3-9 12:27
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
22
小黄鸭爱学习 你可以理解成我们把arm/arm64指令集脚本化了,而UnicornVM就是这个脚本化系统的解释器。这句话才是这个架构的本质所在吧。相对与之前直接使用unicorn仿真arm 调用 so。不知道你这个 ...
https://mp.weixin.qq.com/s/0OL8073Zo_EStTCMnCZAjA
这里面有阐述。
2020-3-9 17:46
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
23
freakish 我没有测试过这条指令具体映射的时候出现的问题, 如果有问题,那应该是属于unicorn 引擎本身需要加强的地方
这种动态计算的内存地址,除非用非常复杂的内存映射,否则处理不了。
2020-3-9 17:47
0
雪    币: 3100
活跃值: (10726)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
24
完全不懂,你们是怎么让自己变成大佬的?
2020-3-9 18:56
0
雪    币: 1662
活跃值: (3569)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
25
xinpoo 完全不懂,你们是怎么让自己变成大佬的?
专业专注、持之以恒,你可以的。
2020-3-10 10:12
0
游客
登录 | 注册 方可回帖
返回
//