欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

BPE32 多态引擎剖析

程序员文章站 2022-04-12 20:25:13
autor: nEINEIe-mail: neineit@gmail.comdate:   2008-11-10 一 BPE32简介二 技术细节分析2.0 -- 整体流程...

autor: nEINEI
e-mail: neineit@gmail.com
date:   2008-11-10

一 BPE32简介
二 技术细节分析
2.0 -- 整体流程设计
2.1 -- 随机数设计
2.2 -- 代码加密方案
2.3 -- 对抗VM的SEH设计
2.4 -- 随机数生成与寄存器选择
2.5 -- 垃圾指令生成方式
2.6 -- 解密器设计
2.7 -- 重建指令流程
三 代码解析
四 检测方案


一 BPE32简介:
BPE32(Bennys Polymorphic Engine for Win32)多态引擎是由Bennys /29A在#4期发布的一个病毒多态引擎,之后在病毒编写如(如Win32.Vulcano)及壳的编写(如ASProtect)当中都得到了应 用,BPE32是一个很不错的多态引擎,这里将从设计的角度分析该引擎。

按照Bennys的描述,BPE32引擎有如下特点:
1 可以通过创建SHE来干扰一些AV
2 随机使用寄存器做代码的混淆
3 同一功能代码,由不同的指令动态生成
4 功能代码具有空间随机分布
5 具有仿真CALL 及 jmp 指令
6 在代码之间插入垃圾指令(也包括使用未公开的SALC指令)

我们先看一下BPE32的调用时的输入参数,
ESI  -- 指向待加密的virus数据。
EDI  -- 指向一块内存数据,用来存放由BPE32生成的解密器及加密数据。
ECX  -- 加解密数据的计数,加解密时按照4byte的方式操作,数据由公式(_end - start +3)/4 获得。
EBP  -- 做重定位时使用。
输出参数,
EAX  -- 解码器加上加密数据后的大小,不对齐,精确到1byte而不是一个DWORD
调用很方式简单,例如:
mov esi, vir_body ; 病毒体
mov edi, pmem     ; 内存空间
mov ecx, 6c0h     ; 解密计数
call BPE32
这样调用后pmem里面就是一个重建的病毒代码了。
下面将对具体技术细节做分析。
二 技术细节分析
2.0 流程设计:
调用BPE32后,将在内存中产生如下方式的3部分功能代码,结构如下:
/-------    +--------------------+
|    |    call  decryptor |  --------->@1
|    +--------------------+
|    |                      |
|    | encryptvirus body  |  --------->@2
|    |                    |
------>|--------------------+
|                    |
|    decryptor       |
|                    |  --------->@3
+--------------------+
@1 是经过计算构造好的一个call调用,因为调用的具体位置要有@2部分决定。
@2 是一个经过加密后的病毒体。
@3 是一个解密器,用于对@2部分进行解密,该部分是经过代码混淆变换的。
这样每次感染其它文件后,重新生成的代码将不再有固定的特征部分,这将使得特征扫描机制失效。

2.1 随机数设计:
BPE32的随机数部分设计的很简单,利用了RDTCS指令来产生一个随机数字,通过栈中参数X,产生一个0 ~ X-1 之间的数字,当参数是0时,则直接返回产生的该数字。
random    proc
push edx
RDTCS
xor edx, edx        ;nulify EDX, we need only EAX
cmp [esp+8], edx        ;is parameter==0 ?
je r_out            ;yeah, do not truncate result
div dword ptr [esp+8]    ;divide it
xchg eax, edx        ;remainder as result
r_out:    pop edx        ;restore EDX
ret Pshd                  ;quit procedure and destroy pushed parameter
random    endp

2.2 代码加密方案:
BPE32采用的加密方案是,先产生两个随机数,一个作为密钥B_key(不变的数字),一个作为增量密钥I_key(每次运算后相加),每次使得B_key + I_key,然后 xor 待加密数据一个DWORD,实现如下:

push 0                     ;产生一个无索引范围的随机数
call random
xchg edx, eax
mov [ebp + xor_key - mgdelta], edx    ;存储基密钥
push 0                     ;产生一个无索引范围的随机数
call random
xchg ebx, eax
mov [ebp + key_inc - mgdelta], ebx    ;存储增量密钥
x_loop:    lodsd                 ;加密virus body
xor eax, edx
stosd
add edx, ebx
loop x_loop

2.3 对抗VM的SEH设计:
对于上面小节中提到的 @3部分,其实是由如下部分组成的,decryptor如下图:
+------------------+ <--------
|  seh handler     |          |
+------------------+          |
|  deleta geter    |          |
+------------------+          |
|   decryption     |          |
+------------------+          |
|  loop decryptor  | ---------/
+------------------+
seh handler    -- 安装一个seh处理过程。
deleta geter   -- 因为@3部分是由垃圾指令随机填充的,所以每循环一次后需要进行一次重定位。
decryption     -- 解密部分,同样由垃圾指令所包围。
loop decryptor -- 跳向seh handler。
对于SEH BPE32会产生如下形式的代码:
start:
call end_seh_fn
/---->mov esp, [esp+8]       ;--> 相当于push  seh->handler
|     jmp seh_rs
|     end_seh_fn:
|     sub edx, edx
|     push dword ptr fs:[edx] ;--> 相当于push  seh->prev
|     mov fs:[edx], esp
-----inc byte ptr [edx]      ;--> 该处引发异常,跳向上面语句
jmp start
seh_rs:    xor edi, edi
pop dword ptr fs:[edi]
pop edi
这样对于使用vm技术的aver,如果没有做好seh仿真,将导致仿真失败,无法完成检测,而jmp start 这条语句也很重要,有些aver会实现指令预取的功能(具体可参考《对抗启发式代码仿真检测技术分析》一文),这样会另aver陷入无休止的仿真循环当 中。

2.4 随机数生成与寄存器选择
BPE32 通过get_reg 函数产生一个随机的寄存器索引,即产生0 ~ 7 之间的整数,不使用EAX(0),ESP(100b) ,在调用的外部也会判断是否使用了的EBP(101b),实现如下:
get_reg proc
push 8       ; 产生一个随机的 0 ~ 7 之间的整数
call random
test eax, eax
je get_reg   ;不能使用eax
cmp al, 100b ;不能使用esp
je get_reg
ret
get_reg endp
2.5 垃圾指令生成方式:
BPE32 通过调用rjunk 函数来产生垃圾指令,这部分可以产生1byte,2 byte,3byte,5byte垃圾指令,及jmp call类的仿真指令(无疑这类指令的加入使得junk看起来更像真实的指令),同时为了使junk的产生更加随机化,BPE32做了一个简单映射关系。
0=5, 1=1+2, 2=2+1, 3=1, 4=2, 5=3, 6=none, 7=dummy jump and call
左侧索引(eax,随机数) = 右侧(垃圾指令字节数),也就是rjunk先产生一个0 ~ 7 的随机数,根据这个索引映射的列表,进行垃圾指令的生成,例如:
eax 是 0时,产生5byte junk
eax 是 1时,产生1byte junk 和 2byte junk
eax 是 2时,则先产生2byte junk,再产生1byte junk
eax 是 7时,产生jmp或call
按照以上的映射关系,依次类推的产生垃圾指令,下面以1byte junk的代码来说如何产生垃圾代码。
1 byte junk 示例:
esi -- > 待加密数据
edi -- > 内存buff
产生1byte junk指令到内存buff
j1:
call junx1      ;one byte junk instruction
nop
dec eax
SALC
inc eax
clc
cwde
stc
cld