首页 网络安全 安全学院 查看内容

BPE32 多态引擎剖析

2009-1-1 12:30 805 0

摘要: 创建时间:2008-11-11文章属性:原创文章提交:nEINEI (neineit_at_gmail.com)BPE32 多态引擎剖析autor: nEINEIe-mail: neineit@gma...
关键词: nbsp 指令 一个 寄存器 随机数 代码 密钥 加密 BPE32 随机

创建时间:2008-11-11文章属性:原创文章提交:nEINEI (neineit_at_gmail.com)BPE32 多态引擎剖析autor: nEINEIe-mail: [email protected]:   2008-11-10一 BPE32简介二 技术细节分析   2.0 -- 整体流程设计   2.1 -- 随机数设计   2.2 -- 代码加密方案   2.3 -- 对抗VM的SEH设计   2.4 -- 随机数生成与寄存器选择   2.5 -- 垃圾指令生成方式   2.6 -- 解密器设计   2.7 -- 重建指令流程三 代码解析四 检测方案一 BPE32简介:   BPE32(Benny's Polymorphic Engine for Win32)多态引擎是由Benny's /29A在#4期发布的一个病毒多态引擎,之后在病毒编写如(如Win32.Vulcano)及壳的编写(如ASProtect)当中都得到了应用,BPE32是一个很不错的多态引擎,这里将从设计的角度分析该引擎。   按照Benny's的描述,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 resultdiv dword ptr [esp+8]    ;divide itxchg eax, edx        ;remainder as resultr_out:    pop edx        ;restore EDX  ret Pshd                  ;quit procedure and destroy pushed parameterrandom    endp2.2 代码加密方案:  BPE32采用的加密方案是,先产生两个随机数,一个作为密钥B_key(不变的数字),一个作为增量密钥I_key(每次运算后相加),每次使得B_key + I_key,然后 xor 待加密数据一个DWORD,实现如下:push 0                     ;产生一个无索引范围的随机数call random         xchg edx, eaxmov [ebp + xor_key - mgdelta], edx    ;存储基密钥push 0                     ;产生一个无索引范围的随机数call randomxchg ebx, eaxmov [ebp + key_inc - mgdelta], ebx    ;存储增量密钥x_loop:    lodsd                 ;加密virus bodyxor eax, edxstosdadd edx, ebxloop x_loop2.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 startseh_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 procpush 8       ; 产生一个随机的 0 ~ 7 之间的整数call randomtest eax, eaxje get_reg   ;不能使用eaxcmp al, 100b ;不能使用espje get_regretget_reg endp2.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指令到内存buffj1:    call junx1      ;one byte junk instruction    nop    dec eax    SALC    inc eax    clc    cwde    stc    cld  junx1:  pop esi                ; 1 byte junk 指令首地址,即指向nop指令    push 8    call random    add esi, eax    ; 随机定位一条    movsb           ;加入edi指向的内存当中    ret   其它的junk产生方式仅比 1byte junk复杂一些而已,故不再赘述,BPE32还有一个重要的功能指令产生函数,make_xor,make_xor2,make_xor主要是将指定的寄存器(由bl寄存器中的内容指定)清0,make_xor可以产生随机的,xor,Rx,Rx / sub Rx,Rx/ mov Rx,0 这样的等效指令,make_xor2则产生一个指定寄存器xor某一数值的指令,例如:    mov bh,2    call make_xor2    mov eax,01234567h    stosd    以上则产生一条xor edx,01234567h这样的指令,以上两个函数功能简单,故不再做过多说明。  2.6 解密器设计  因为BPE32可以随机的使用寄存器,故这里用Rx来表示任意一个可用的寄存器,每条语句的Rx并不一定代表同一个寄存器。解密器的设计是BPE32多态的重点,我这里先将主要的功能代码表示出来。     mov  Rx,src      --------  I1  获得待解密的地址,放入Rx中     mov  Rx, cnt      --------  I2  获得要解密的此时,放入Rx中/--->xor  Rx,Rx       --------  I3  解密|    add  Rx,4        --------  I4  待解密的地址加4|    add  Rx,1        --------  I5  计数器加1|    add  Rx,Rx       --------  I6  基密钥 + 增量密钥|    jcxz xxx          --------  I7  测试解密是否完成,完成后跳出循环\--- jmp  xxx          --------  I8  继续解密   BPE32围绕 I1 ~ I8 ,通过随机寄存器、插入垃圾指令、变换指令顺序、同等指令替换等手段产生来产生数据不同但功能相同的解密代码,下面我将列举一个去除垃圾指令的BPE32产生的解密代码。0040202B    E8 00000000     CALL T-BPE32.00402030          ;构造的一个call00402030    8B3C24          MOV EDI,DWORD PTR SS:[ESP]00402033    58              POP EAX00402034    81EF 30204000   SUB EDI,T-BPE32.00402030       ;构造一个重定位; I1 的一种生成方式,F7973BCB为随机产生的一个密钥,xor后,ecx 指向了最初call调用后地址,即待解密数据首地址0040203A    68 CB3B97F7     PUSH F7973BCB;构造一个随机加密的密钥使得ecx指向最初的一个call调用0040203F    59   
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部