Virus.Win32.Virlock.b分析

0x00 样本说明

分析样本是被0b500d25f645c0b25532c1e3c9741667的样本感染得到。感染前的文件是Tcpview.exe,一款windows网络连接查看工具。

感染前后文件对比图示如下:

感染前

感染后

文件大小

106496 字节

818688 字节

文件md5

4B6B70F4A199CF3EAC1554B08804BC4F

041D85ECABF5F2E8366C5E0FDCB705F3

文件图标

   

分析样本的报毒情况如下:

杀软类型

病毒名

nod32

a variant of Win32/Virlock.J virus

kav

Virus.Win32.PolyRansom.f

0x01 入口解密部分

该部分代码实际功能是将密文代码进行解密,而后对解密后的代码进行调用。详细的情况如下:

入口地址00401000下的代码是一些冗余代码,而后从地址0040101C下的jmp指令跳转到地址:sub_4C324E,该地址下是一段解密代码.代码被解密后保存到设定好的地址0x4C8000下,然后通过retn指令返回到解密后的代码处0x4C8000开始执行。解密代码具体情况如下:

密文起始地址

0x4BFA4E

解密后数据存放地址

0x4C8000

解密算法

xor

解密key

0x7F

解密长度

0x380

0x02 虚拟机执行代码

该部分代码实际功能和0x01部分的代码功能相同,也是将密文代码进行解密,而后对解密后的代码进行调用。详细情况如下:

从地址0x4C8000处开始,是一段功能为解密的代码,只不过该部分代码,解密方式并不再通过简单的xor指令进行解密。而是采用了通过一个小的虚拟机来执行一系列的功能代码。被虚拟机执行的代码功能是对指定地址为起始的代码进行解密,解密方式为xor。而后对解密后的代码进行调用。

虚拟机在执行代码的过程中主要包含三个关键地址,分别是执行的指令序列表地址;功能指令结构地址;虚拟机指令执行框架代码地址。三个地址下的数据都被加密,并采用不同的密钥。但采用的加密方式都是简单的xor加密。

1、  指令执行序列编号密文存储结构地址

每一个一个指令序列的长度是word,具体解密信息如下表所示。

密文起始地址

0x4C3E4E

密文结束地址

0x4C4248

解密算法

xor

解密key

0xCBF8

         通过上面的表格可以看出,该地址下指令数量共有(0x4C4248-0x4C3E4E)/2=0x1FD(509)条。但由于代码在执行的时候会有同一条指令的多次执行,实际执行指令数量远大于这个数值,经过分析实际执行的指令条目是1693245条。具体如何重复执行同一条指令可以参考指令功能代码存储部分中的相关说明。完整的解密后的指令执行序列编号表参见附件“指令执行序列编号表.txt”。下面是指令执行序列编号表起始部分。

0000

0100

0200

0300

0400

0500

0600

0700

0800

D500

0900

0A00

D600

0B00

D500

……

由于计算机数据存储采用大尾模式,上面编号,实际为:0000,0001,0002,0003,0004,0005,0006,0007,0008,00d5,0009,000a,00d6,000b,00d5……。

2、  虚拟机中执行的指令执行环境恢复保存代码密文地址

该部分代码下文称之为指令调度框架,实际其为一个小虚拟机的指令执行环境。具体代码解密情况如下图所示。

密文起始地址

0x4C524E

明文起始地址

0x4C8600

解密算法

xor

解密密钥

0xED

解密长度

0x6E

解密后明文调度框架代码如下:

004C8600    896C24 08       mov     dword ptr [esp+8], ebp

004C8604    8BEC            mov     ebp, esp

004C8606    90              nop

004C8607    837D 0C 00      cmp     dword ptr [ebp+C], 0

004C860B    74 03           je      short 004C8610

004C860D    8B65 0C         mov     esp, dword ptr [ebp+C]

004C8610    90              nop

004C8611    81C5 792740B6   add     ebp, B6402779

004C8617    81C5 17DBBF49   add     ebp, 49BFDB17

004C861D    81C4 0E2740B6   add     esp, B640270E

004C8623    81C4 E2D9BF49   add     esp, 49BFD9E2

004C8629    5E              pop     esi

004C862A    90              nop

004C862B    5F              pop     edi

004C862C    5A              pop     edx

004C862D    90              nop

004C862E    59              pop     ecx

004C862F    5B              pop     ebx

004C8630    90              nop

004C8631    58              pop     eax

004C8632    90              nop

004C8633    9D              popfd

004C8634    90              nop

004C8635    90              nop

004C8636    90              nop

004C8637    90              nop

004C8638    90              nop

004C8639    90              nop

004C863A    90              nop

004C863B    90              nop

004C863C    90              nop

004C863D    90              nop

004C863E    9C              pushfd

004C863F    50              push    eax

004C8640    90              nop

004C8641    53              push    ebx

004C8642    51              push    ecx

004C8643    90              nop

004C8644    52              push    edx

004C8645    90              nop

004C8646    57              push    edi

004C8647    56              push    esi

004C8648    90              nop

004C8649    81EC FB3040B6   sub     esp, B64030FB

004C864F    81EC F5CFBF49   sub     esp, 49BFCFF5

004C8655    81ED 8B3040B6   sub     ebp, B640308B

004C865B    81ED 05D2BF49   sub     ebp, 49BFD205

004C8661    8965 0C         mov     dword ptr [ebp+C], esp

004C8664    90              nop

004C8665    8BE5            mov     esp, ebp

004C8667    8B6C24 08       mov     ebp, dword ptr [esp+8]

004C866B    90              nop

004C866C    C3              retn

004C866D    90              nop

指令调度框架代码中,分为3个部分。包括环境初始化部分,指令代码部分和环境保存部分。环境初始化部分和环境保存部分主要是对指令代码执行环境的栈恢复和栈还原,其中初始化部分中对第一条指令执行,未进行esp恢复。指令代码部分是上图中的红色部分[地址0x004C8634处],预留的地址长度是单条汇编指令最大长度0xA。

3、  指令功能代码存储地址

该部分代码或可称之为指令handle,是一个由指令结构体组成的数组。具体的解密信息如下表。

密文起始地址

0x4C164E

密文数据结构

ins_struct{db flag; db length; db[length] data; }

解密算法

xor

解密密钥

0x9C

所有的指令功能代码,是按照上面描述的指令结构,按照编号从大到小顺序排列。即密文起始地址下实际是一个结构体:ins_struct{n=509}。完整的解密后的指令结构体明文表参见附件“指令编号和功能代码数据关系表.txt”;完整的解密后指令序号和汇编指令代码对应关系参见附件“指令序号汇编指令对照表.TXT”。部分解密后数据如下表:

指令序号

指令结构体数据

汇编指令代码

00

00 03 83 EC 1C

sub     esp, 1C

01

00 08 C7 44 24 14 00 00 00 00

mov     dword ptr [esp+14], 0

02

00 08 C7 44 24 18 00 00 00 00

mov     dword ptr [esp+18], 0

03

00 07 C7 04 24 00 00 00 00

mov     dword ptr [esp], 0

04

00 08 C7 44 24 04 00 00 00 00

mov     dword ptr [esp+4], 0

05

00 08 C7 44 24 08 00 00 00 00

mov     dword ptr [esp+8], 0

06

00 08 C7 44 24 0C 00 00 00 00

mov     dword ptr [esp+C], 0

07

00 04 8B 44 24 14

mov     eax, dword ptr [esp+14]

08

00 05 83 7C 24 14 00

cmp     dword ptr [esp+14], 0

09

00 05 BB 00 00 00 00

mov     ebx, 0

0A

00 03 83 FB 01

cmp     ebx, 1

0B

00 05 83 7C 24 14 01

cmp     dword ptr [esp+14], 1

……

……

……

D5

01 04 04 00 00 00

向后跳转4条指令

D6

01 04 4B 00 00 00

向后跳转0x4b条指令

D7

01 04 4A 00 00 00

向后跳转0x4a条指令

……

……

……

FE

01 04 EA FF FF FF

向前跳转0xea条指令

FF

01 04 F8 FF FF FF

向前跳转0x8条指令

结构中的flag域是一个指令标示符,结构中的data数据部分是一个公用体。Flag标示符用来标识data数据部分的数据类型。当flag值为0时,结构中的data数据部分是指令代码,将代码解密后写入到指令调度框架中执行;如果flag的值为1,是下一条指令编号相对当前编号的偏移值,即不执行代码而是跳转到下定位后的编号代码进行执行。需要注意的是,当flag为1时,只有当指令调度框架中的zf标志寄存器被置0时,才有实际跳转意义,否则按无效指令执行,直接执行下一条代码。详细汇编代码参考下图:

.data:004C802B                 cmp     [esp+arg_4], 0

.data:004C8030                 nop

.data:004C8031                 jz      loc_4C8196

;判断指令调度框架中保存的esp值是否为0,即判断是否为第一条指令,由于第一条指令部的flag为0,该条件无法满足,永远不会跳转。

.data:004C8037                 jmp     loc_4C825F

.data:004C825F                 mov     eax, esp

.data:004C8261                 nop

.data:004C8262                 sub     eax, [esp+arg_4]

.data:004C8266                 sub     eax, 4

.data:004C8269                 nop

.data:004C826A                 jmp     loc_4C8191

.data:004C8191                 mov     ecx, 1

.data:004C8196                 add     esp, 104h

.data:004C819C                 jmp     loc_4C803C

.data:004C803C                 cmp     ecx, 1

.data:004C803F                 jnz     loc_4C8047

.data:004C8045                 sub     esp, eax

.data:004C8047                 popf

.data:004C8048                 nop

.data:004C8049                 jnz     loc_4C8081

; 弹出指令调度框架中的标志寄存器,判断zf寄存器的值。如果zf_flag=0,解密计算跳转偏移数据,否则直接给跳转偏移值设置为0,而后执行下一条指令。跳转偏移值存放于ebx中。

.data:004C804F                 jmp     loc_4C8078

.data:004C8078                 and     ebx, 0

.data:004C807B                 nop

.data:004C807C                 jmp     loc_4C8155

; 此跳转直接跳过指令跳转功能的代码。跳转到4c8155

.data:004C8081                 mov     ebx, [edi+2]

.data:004C8084                 nop

.data:004C8085                 mov     dl, 9Ch

.data:004C8087                 xor     bl, dl

.data:004C8089                 nop

.data:004C808A                 jmp     loc_4C8213

.data:004C8213                 rol     ebx, 8

.data:004C8216                 nop

.data:004C8217                 xor     bl, dl

.data:004C8219                 rol     ebx, 8

.data:004C821C                 xor     bl, dl

.data:004C821E                 rol     ebx, 8

.data:004C8221                 nop

.data:004C8222                 jmp     loc_4C814C

.data:004C814C                 xor     bl, dl

.data:004C814E                 nop

.data:004C814F                 rol     ebx, 8

.data:004C8152                 nop

.data:004C8153                 shl     ebx, 1

; 从004C8081地址到此处,功能是解密指令结构中的data数据,并乘2,用以作跳转偏移。

;解密出来的偏移量是指令个数,每个指令长度是2byte,所以地址偏移需要乘2

.data:004C8155                 sub     esp, 108h

.data:004C815B                 nop

.data:004C815C                 jmp     loc_4C81A1

……

完整的指令执行序列顺序表参见附件“条件断点_log.txt”。该文件是通过od加载分析文件后设置条件记录断点,产生的记录日志。条件断点的设置方式是在地址0x004C80D4下设置记录条件“eax==ecx”,输出eax的值;在地址0x004C80B0下设置记录条件“eax==eax”,输出eax的值。

其中日志中的eax的值是执行指令序号,如果指令序号大于0xd5,该指令是跳转指令,只是用来记录,不是具体执行代码。完整的代码功能是将数据解密到0x401400地址处,其间会有对api函数调用。由于这部分代码篇幅过长,未作整理。

通过条件记录断点的记录结果,被执行的最后一条指令是call edi,指令编号是d4。其调用的地址是0x00401400。如要跳过虚拟机执行代码部分直接到对解密后代码的调用,可以通过直接在地址0x004C80B0下设置条件断点:“eax==0xd4”。断住后,执行到CALL地址内部的call edi处,即可跳转到解密后的代码地址0x00401400处。

0x03 病毒功能代码

病毒的所有功能代码执行前都是加密的,需要先进行解密然后再执行,且所有的病毒功能代码的对自身函数的调用,也都是加密的,也需要先解密进行执行。具体可参见下图。

 

加/解密的过程采用的算法是一个对称加密算法。都是先调用一个函数(fun_calc)计算密钥,然后再调用一个函数(fun_decode)对数据进行解/加密。由于计算密钥使用的数据和解/加密函数的地址都是硬编码写在函数内部的,所以不同功能函数的fun_calc、fun_decode函数虽然算法相同,但并不是调用的同一段函数。

从00401000处开始所有代码执行都是通过上面图示的情况进行执行的。代码量较大,不能静态分析,只能动态逐函数解密后分析。给全面分析带来较大的困难,下面的图示只对还原感染前文件相关的代码层次作了介绍。

loc_00401400

         sub_00401522

         sub_004014C8

         //decode_00401576

         loc_00401576[virus_fun]

                   sub_00401674

                            sub_00401737

                            sub_004017C8

                            //decode_004017E5

                            loc_004017E5[virus_fun]//decode_00401914 + 792

                            //decode_data contain fun_00401914 and fun_00401C61

                            sub_00401737

                            sub_004017C8

                   sub_00401914

                            sub_004019A7

                            sub_00401981

                            //decode_00401A7E

                            loc_00401A7E[virus_fun]//get_3functions_addresses

                            sub_004019A7

                            sub_00401981

                   sub_00401674

                            sub_00401737

                            sub_004017C8

                            //decode_004017E5

                            loc_004017E5[virus_fun]//decode_ds:[004018C8]=004A4A30

                            //decode_data is original_file data

                            sub_00401737

                            sub_004017C8

                   sub_00401C61

                            sub_00401D60

                            sub_00401CD7

                            //decode_00401DAD

                            loc_00401DAD[virus_fun]

                            //call two functions, get_system_version

                            sub_00401D60

                            sub_00401CD7

                   sub_00401674

                            sub_00401737

                            sub_004017C8

                            //decode_004020B6

                            loc_004017E5[virus_fun]//decode_004020B6 + 73

                            sub_00401737

                            sub_004017C8

         sub_00401522

         sub_004014C8

         loc_004020B6[virus_fun]

                   sub_004021C2

                   sub_0040219C

                   //decode_00402204

                   loc_00402204[virus_fun]

上图中的所有包含地址的sub_xxxxxxxx标号,都代表一个call调用;所有包含地址的loc_xxxxxxxx标号,都代表一段病毒功能代码的调用。sub_xxxxxxxx调用主要是对loc_xxxxxxxx代码的解密,而loc_xxxxxxxx代码主要是关键的代码执行。

从上图中可以发现,当执行到loc_004017E5代码结束时,已经完成了对原始文件数据的解密。在这里直接读出数据即可完成样本的修复。

后续所有的功能函数分析,待以后继续添加。

附件地址:http://files.cnblogs.com/files/Mikhail/%E9%99%84%E4%BB%B6.rar

原文地址:https://www.cnblogs.com/Mikhail/p/5197830.html