-
-
变形的初步方案--两篇变形文章的读后感
-
发表于:
2006-8-22 08:06
12558
-
变形的初步方案
--两篇变形文章的读后感
Mental Driller的“Metamorphism in practice or "How I made MetaPHOR and what I've learnt"”一文(以下简称M文)所述变形引擎,全部代码虽然经过变形,但毕竟已经是机器语言了,有些行为如果处理不好,可能引起怀疑。如果直接使用宏语言编写程序并且携带,只把其中一部分模块翻译成可直接执行的机器语言,也许更好。宏命令可以相当于一条到三条指令组合,也可以只是个标记;可能有一个或几个参数,也可能没有。并且因此可以省略把机器语言反汇编成伪汇编语言这一部分,自定义的宏语言程序比较容易控制。这个想法一部分来自Navrhar的“Assembly Language or HLL”一文。
宏程序包括入口模块、汇编模块、变形模块、传播模块、X模块。可以放在PE文件的一个只读不执行的数据节。宏程序由宏命令组成,宏命令的基本内容有宏代号、参数个数、参数列表、和连接位置(指向下一条命令,其作用与M文中伪指令的Pointer不同)。其中翻译成机器语言的部分(入口模块和汇编模块)附加到PE文件的可执行不可写入的代码节(利用空白区或者新节)。
入口模块取得控制权,并做些准备工作,需要的时候它启动汇编模块翻译出其他模块并执行。例如,汇编模块从数据节读宏程序,先翻译变形模块。然后执行变形模块把宏程序变形,这样,新一代的数据节就和上一代的不同,再从中翻译出的机器语言代码也就变形了。
宏程序变形主要分两步:置换和收缩/展开。
置换:把宏程序所有命令打乱重新随机排列,命令中的连接位置字段内容必须随之修改,以保持连续性,并跟踪程序入口位置。收缩和展开:一条宏命令用几条宏命令替换、执行相同任务,或者几条宏命令合并用一条宏命令替换。这与宏语言的设计有密切关系,可以参考M文的指令对和三联组,只不过改成宏代号之间的对应。
变形模块的收缩/展开部分隐含地使用一个宏替换表,展开比较容易做,收缩需要判断在逻辑上连续的几个宏代号是不是可以用一个宏代号替换。注意,不同之处:在M文中收缩/展开是一对互补操作,在这里一般情况下是随机决定把一个宏展开成几个宏、或者和后面的宏一起、判断能不能合并(如果后面的宏是引用或跳转目标就不能合并)。相同之处:能递归,所以要设定代码大小上下限,不要让若干代以后宏程序变得太大或太小,接近下限就多倾向于展开一些宏,接近上限就多倾向于收缩一些宏,达到界限就要反向操作或者只使用一条等效的宏代替(如XOR EBX,EBX用SUB EBX,EBX,因为宏可以根据参数不同改变寄存器,所以可用的替换比较多)。应该防止万一收缩了不是原来展开的宏,造成很多宏不能收缩,因此要设计好宏替换表。
[注:“隐含地使用表”就是根据它编程,但不储存表。不过如果显式地使用表,编码更简单,可以通过分散存放、混洗、加密等等把表变形。]
递归性与宏的设计有关,如M文的例子,PUSH Reg <--> MOV Mem,Reg/PUSH Mem,而其中MOV Mem,Reg <--> PUSH Reg/POP Mem,又递归使用到PUSH Reg。这里一条指令或一个指令对都可以作为一个宏。
变形以后,汇编模块就可以从新的宏程序翻译了。汇编模块的翻译部分也隐含地使用一个宏命令表,其中每个宏代号对应着一个指令格式串(1至3条机器指令,已填好操作符和部分操作数,留空的操作数将用宏命令的参数填入),如M文中的MakeMOVMemReg()和MakePUSHMem()等等。
按宏命令的排列顺序(已置换)取下一条命令翻译,根据连接位置字段用无条件跳转连接。跳转指令可以用JMP xxxx或CMP EAX,EAX/JZ xxxx或PUSH xxxx/RETN或PUSH EAX/XOR EAX,EAX/POP EAX/JNC xxxx,等等;跳转地址xxxx需要计算,实现方法至少有两个,用最大长度存放各指令组,再根据连接位置就可以计算出xxxx,或者,翻译命令时用临时表记录其地址,最后按表修改跳转地址,在M文中对此有更多描述。如果有时又按连接位置取下一条命令翻译(此时不用插入跳转),这样可使得代码整体架构改变,而实际算法没有改变。
以上只是初步设想,很多问题不可能涉及,下一步详细设计时合理地设计宏是关键,然后编码首先要保证汇编模块能正确翻译,之后才变形。
月中人 原创于2006.8.22.
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课