首页
社区
课程
招聘
[原创]从零手写 ARM64 VMP
发表于: 11小时前 310

[原创]从零手写 ARM64 VMP

11小时前
310

VMP(Virtual Machine Protection,虚拟机保护)简单说就是把.so中的关键代码转换成自定义的虚拟指令,交给程序内置的虚拟机执行,以此来增加逆向分析的难度。这里的VMP泛指代码虚拟化保护。

程序在正常情况下的执行顺序是,ARM64原生指令-> CPU直接执行。

经过虚拟化保护后,将关键代码转换成自定义字节码,再将字节码交由内置的解释器进行读取并且执行,结果保持不变。

简单来说就是将原本走直线的路径改成了多绕几圈。

实现后的解释器效果可以参考下图(图片来自某加固厂商)。

image

本文的目标就是自己手搓一个可以在安卓APP上运行的mini版本,以此来提升对虚拟化保护的理解。

下面这个就是将要受到保护的函数,可以很清晰的看出整体的逻辑非常的简单,对a、b两个参数进行加减乘除运算后将结果交由​print_value函数打印日志。

image

运行后的结果长这样

image

target函数经由编译器编译后的汇编大概长这样。

image

但是现在我想过要换一种组织方式去实现。

简单来说据就是我希望x=a+b不再只是一个固定写在target里的C++操作,而是可以通过一份数据表达出来。

假设说我要给另外一段程序发送一条计算要求,让他完成这句

这条要求至少需要说明:做什么操作(加法)、第一个数从哪里取(从a取)、第二个数从哪里取(从b取)、计算结果存放在哪里(放到x)。

如果说不知道这些的话,执行者就不知道该算什么、用什么算、算完存哪里。

当然也可以直接在数据里写a、b、x。设计完后指令长这样:

但是需要再去做一个按名字查找的表保存值

执行器读取这条指令时:

根据 ​"a"​ 查出 ​10;

根据 ​"b"​ 查出 ​4;

相加得到 ​14;

把名字 ​"x"​ 对应的值更新为 ​14。

这条路线是成立的 。 只是需要处理字符串,以及名字对应哪个值的查找。

所以说不论是用名字还是数字,都得自己规定:执行器去哪里取这些值 。

既然已经知道执行器需要拿到a、b,还需要一个地方保存结果x。

一个很普通的办法就是数组:

然后做一个约定

再把入参放进去:

此时,例如调用target(10, 4)

数组就是:

于是这里不需要再实现字符串查找,直接用数组下标就行。

现在已经有了int values[3] 。执行器会用这些位置保存输入和中间结果。如果说规定:

就可以把:​values[2] = values[0] + values[1];

写成一种方便阅读的表示:​R2 = R0 + R1

这里的虚拟寄存器并非是每一个虚拟机都是我这种的实现,也可以使用前面提到的按照名字保存值的表,操作数栈等其他存储方式。

原来由 a、b、x 承担的存储工作,现在可以由三个数组元素承担。但是它还没有实现由数据描述计算步骤。

因为这里values[2] = values[0] + values[1],仍然把加法和操作的位置都写死在了 C++ 代码里。

image

现在就是先把values[2] = values[0] + values[1]这里的三个下标拿出来。

把他们保存成一份数据

这里的顺序是按照下面约定排序的

所以​{2, 0, 1},意思就是:

这里还要区分两个数组:

代码长这样

现在实现的是由数据决定操作数来自哪里,以及结果写到哪里。还是没有做到由数据决定做说明运算,因为

这里的 + 任然写死了。如果说下一步要减法,不可能再去写一个只会减法的执行器,这里需要在说明数据里面再去放一个字段,交代清楚执行器这次执行到底是加还是减。

还是最朴素的方式,约定

这两个数字没有特殊含义。

不是 CPU 规定 1 必须是加法,也不是 C++ 规定的。换成:

同样可以。只要写数据的人和读取数据的执行器遵守同一份约定就行 。

给原来的格式增加一个字段

原来是这样的约定:

现在新增字段后站这样:

于是unsigned char instruction[] = {1, 2, 0, 1}; 就表示values[2] = values[0] + values[1];

而 unsigned char instruction[] = {2, 2, 0, 1}; 就表示values[2] = values[0] - values[1];

现在有了操作码,就可以写成这样了

但是还需要去解决怎么根据不同的操作码做不同的事情

现在操作码是int op = instruction[0];那么只需要最普通的if判断就能实现

但是用1、2这种名称来记录对应关系不太行,可以起个别名。

现在就可以写成这样了

image

现在就拥有一条完整的、能够被执行器理解的指令(操作码,结果位置,左边来源,右边来源)

但是目前还是只能执行一条,在原函数中 需要先加、再减、再乘、再除。这就产生了一个新的问题,如果把多条指令接在统一数组里面,执行器怎么知道当前读到哪条了,以及下一条从哪里开始?

在前面我们只能描述int x = a + b;,现在可以新增一点,操作码在前面以及定义好了

第一步表示为

第二步表示为

接下来,一个直接的想法就是:把两条指令依次放进同一个数组。

这里从​instruction改名为code,是因为他现在保存了一组指令,不再只是一条。

程序运行的时候知道前四个元素是一条指令,是因为我们做了每条指令固定占据四个元素。

这里的每个元素的类型是unsigned char,占一个C++字节,因此也可以说每条指令占据了四个字节。

这里先不用循环,用笨办法写一下看看。

目前执行一条指令时,我们这样读取:

如果要读第二条,很容易想到:

这里就有规律了,第一条的起点是0,第二条的起点是4,如果有第三条那么他的起点就是8,以此类推。

因为每条指令占据四个字节,所以每执行完一条,下一条的起点就向后移动四个字节。

我们不需要为每一条指令复制一套读取代码,只需要让读取起点发生变化。

比如说把当前读取起点保存成一个变量

offset就是偏移量。在这里,他表示当前这条指令的开头,距离code数组开头有多少个字节。

现在读取指令可以改成

当offset = 0;时,读的是

当offset = 4;时,读的是

读取代码没有变,但它能通过起点变化读取不同的指令。

所以我们可以把它改名为:

此pc非彼pc,在当前设计中,它只是字节码数组中的一个偏移量。之所以叫虚拟PC是因为在当前这套规则当中,他是负责跟踪虚拟指令的位置。

所以现在的执行顺序变成了

对应

因此

可以通过std::size_t code_size = sizeof(code);得到整个数组的字节数,也就是8。来做终止条件

只要没有走到数组末尾,就继续处理下一条指令。

image

运行结果:

image

原函数前两步是:

第一步目前已经会表达,但是第二步是一个常量,根据前面定义的规则,没办法对这个常量进行计算。

要解决也很简单,为这个常量准备一个存储位置,然后从这个存储位置取值,就可以参与运算了。

最简单的办法就是直接values[3] = 2,然后

但是我们之前的目标是尽量让具体计算步骤由指令数据描述。那么往一个位置放入常量也可以由指令描述。

这里增加一种操作:

例如:把数值2写入位置3。

可以为这个指令起名叫做​LOADI。数值直接写在指令里面,不需要根据位置编号再去别的地方读取。

新指令可以规定为

例如

表示

因为给LOADI的第三个字段规定的含义是直接数值,而不是来源位置。

数据里的一个字节到底表示位置还是数值,是由指令格式决定的,不是数字自己决定的。

最后一个放0是因为每条指令固定占据四个字节,而LOADI只需要三个字段,那么就需要多放一个作为填充。

在之前这里只有三个有效位置,现在想要使用位置3,就必须新增一个元素,最开始的约定也需要改变。

需要先给新操作分配一个编号

然后增加一种判断即可。但是需要先把变量名调整一下


冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

最后于 11小时前 被北袅编辑 ,原因:
收藏
点赞 ・19
打赏
分享
最新回复 (8)
雪    币: 701
活跃值: (750)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2 楼
666
11小时前
0
雪    币: 9532
活跃值: (8192)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3 楼
感谢分享
11小时前
0
雪    币: 411
活跃值: (1091)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4 楼
666
10小时前
0
雪    币: 4812
活跃值: (8322)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5 楼
感谢分享
10小时前
0
雪    币: 106
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6 楼
看着不烧心
10小时前
0
雪    币: 3657
活跃值: (3408)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
7 楼
温泉划水鱼 看着不烧心
先把规矩立住
9小时前
0
雪    币: 857
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8 楼
狠狠学习好吧
9小时前
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9 楼
666
8小时前
0
游客
登录 | 注册 方可回帖
返回