文档编号: S001-F011
原作者: Rajaat
译者: 冲天剑([ptg])
审校: arjat[ptg]
发布时间:
2006-07-21
原文: http://vx.netlux.org/lib/static/vdat/tugenpol.htm
关键词: 多态变形
简介:
在病毒扫描器中使用操作码模拟器解密非常多态化的病毒已不是新鲜想法了。更好的扫描器可以处理带有复杂译码器的病毒,它用一个通用的译码器从病毒上脱去保护壳,从而可以看清病毒的结构。
迄今为止,每个多态变形引擎都可以生成特定类型的译码器。这些译码器的一般行为可能会有差异,而普通的加密器则没有。
作为病毒编写者的我们,必须适应不断创新的编程技术。要让病毒得到进一步的进化,就要将编程普通化。普通化的程序设计可用于所有与病毒相关的手法中。
背景:
每个多态变形引擎基本上都是基于某些将以随机顺序使用的规则,垃圾指令以及一些可以组成不同操作码的指令。但是,如果你把一段代码输入给多态变形引擎,这个引擎如何生成这段代码的变形呢?真正的多态变形引擎将把每一个东西多态化,而不管它做什么。看下面的例子:
mov ax, 3D02h
int
21h
如果你把这段代码输入给上述的、普通的变形机,你可能会得到如下的结果:
sub ax, ax
add ax, 3000h
cmp ax, 78AFh
je NEVER_JUMPS
xor ax, 0D02h
NEVER_JUMPS:
int
21h
或者如果你想生成一个译码器,可以把下面的代码段输入给变形机:
mov si, offset ENCRYPTED_VIRUS
mov al, 32h
mov cx, VIRUS_LENGTH
DECRYPT_VIRUS:
xor byte ptr [si], al
add al, 4
inc si
loop DECRYPT_VIRUS
你可能会得到如下的结果:
mov si, offset ENCRYPTED_VIRUS + 145h
add si, -145h
mov al, 33h
dec ax
mov cx, VIRUS_LENGTH xor 1234h
xor cx, 1234h
DECRYPT_VIRUS:
xchg si, di
xor byte ptr [di], al
xchg si, di
inc al
inc al
inc al
add al, 1
sub si, -1
dec cx
jnz DECRYPT_VIRUS
你可能已经注意到了,这个变形机仅仅把随机指令放在那些它能组成更紧密指令的地方,它并不交换操作码。因为变形机并不知道哪些指令被准许交换,哪些不准,所以,这应当被单独的模块生成。为了生成译码器,你必须调用两个引擎,如下图所示:
.----------------. .-----------.
(1) >>-| 代码编码器 |---->>----| 变形机 |->> (3)
'----------------' (2) '-----------'
(1) 输入参数用以生成译码器
(2) 普通的译码器
(3) 多态化的普通译码器
你想把代码编码器和变形机弄得多复杂都可以。在它最简单的实现里,变形机有少许缺点:因为它一次只变形一个操作码,随机操作码不会和来自下一个操作数的随机操作码混合。这样做的优点是,除了多态变形结构可少许预测外,你并不需要跟踪寄存器。但这样的变形机会有一些问题:
● 重新计算分支转移指令的操作码
● 如何处理作为指针使用的寄存器
● 如何让一个病毒完全变异?你必须保留原始的元病毒代码,否则它将变形已经变形的病毒。
● 变形过程中如何把数据和代码分开。
解决jump和寄存器处理操作码的方法是,构建类似于可执行文件中使用的重定位表。如果你想变形一段程序时,你可以输入给变形机应当被调整的转移的范围。当调用引擎时,你可以把它放在栈上,或者设置一些指向预定义表的指针,你尽可以挑自己喜欢的方法。只不过你要确保可以重定位8位和16位、直接或非直接的地址。
你也可以生成一个范围表,它必须不被变形,对它的引用应该可以更新(指针寄存器)。
重定位/排除/解析(resolve)表(RER表)大致如下所示:
enum RER_types {
RERT_nil, // 0 - RER链尾
RERT_8rel, // 1 - 8位相对地址(条件跳转)
RERT_16rel, // 2 - 16位相对地址(过程调用)
RERT_16dir // 3 - 16位直接地址(指针&跳转)
}
struct RER_table {
RER_type : unsigned char;
RER_address : unsigned int ;
}
在用这个表对你想变形的代码做过变形之后,如果你还想对当前的结果再次变形,则不要忘了用新地址更新这个表。如果编程的风格比较好的话,你可以多次调用变形,每次它将变形指定的例程。
稍微难一点的是包括压缩被前一次变形生成的例程的可能性。对做相同事情的代码来说,扩展以及往里增加指令比把它们压缩成更紧凑的指令要难一些,因为你需要知道怎样才能把少许指令优化成更少的指令或简短的变体。可能会存在一些简单的算法,比如说把add <reg>, 1改成inc <reg>,更复杂的可以参考下面的内容:
未优化的代码 优化后的代码
cmc jc ERROR
jnc ERROR
cmc
xor ax, ax mov ax, 3D02h
add ax, 3D02h
把压缩器模块分开可能会更好,这样一来,你可以尽可能的用压缩器把病毒优化成最简短的形式,接下来再调用变形引擎把它放大。这两个步骤都可以使用同样的指令表和RER表。你还可以增加一个随意调用压缩器和变形器的重叠模块,从而在扩展病毒其它部分的同时可以优化其中的一部分。
因此,各模块之间的结构如下所示:
.------.
|编码器|(1)
'---v--'
.------.
|变异器|(2)
'---v--'
.------.(3)
.-----|变形器|-----.
.------. '------' .------.
(4)|压缩器| |扩展器|(5)
'------' '---v--'
.----------.
|加入花指令|(6)
'---v------'
.------------.
|加入垃圾数据|(7)
'---v--------'
.------------.
|随机数生成器|(8)
'------------'
(1) 以随机顺序排列指令
(2) 为必须完成的操作选用其它的寄存器
(3) 随机调用压缩器或扩展器来变形代码
(4) 通过优化它知道的指令来压缩代码
(5) 通过生成具有同样功能的多条随机操作码来扩展代码
(6) 增加任意的无用指令
(7) 增加简单的垃圾数据(随机数)
(8) 随机数生成器,必须能生成慢速随机数和常规随机数(用做慢速和常规的多态变形)
Rajaat 于1998年11月29日