一种快速提取恶意软件解密逻辑代码的方法

安全
通过本文,我们了解到,可以直接利用汇编中已有的解压缩例程,将其放在Visual Studio中编译成DLL,最后再使用Python来调用。由于我们仅仅需要调用该例程来传递恶意软件的数据,因此并不需要再在C或者Python中重新调用接口。

前言

在平时的恶意软件分析和逆向工作中,我们往往需要对某些类型的加密算法或者解压缩算法进行逆向。而这一逆向工作,可能会需要好几个小时、好几天、好几个月,甚至是好几年才能完成。在我们分析的过程中,常常需要弄明白恶意软件所使用的数据Blob是什么。

[[218895]]

要回答这个问题,本身就是一件有挑战性的工作,我通常并没有那么多的时间来对一些加密的程序做完全彻底的逆向。我一般只需要弄明白这个数据是恶意软件用来做什么的配置文件,甚至有的时候,我根本不知道这些数据是什么。尽管很不愿意接受这样的结果,但却是时常发生的。

目前,有几种方法可以解密恶意软件,并解压其中的数据。我们可以运行恶意软件并转储内存段、在调试器中对其进行调试、在解密/解压缩的部分放置Hook从而dump出其返回值、进行静态分析等等。虽然这些方法都很不错,但无疑要花费大量的时间。

如果我们有几个需要解密或解压缩的数据Blob,那么该怎么办呢?如果可以直接从恶意软件的解密/解压缩部分中得到其汇编代码,那便可以将其放在一个编译器中(比如Visual Studio),将其编译成动态链接库(DLL),然后再使用我们熟悉的脚本语言(比如Python)对其进行调用。

本文将重点讲解可以实现这一点的技术方法。在分析恶意软件Reaver的过程中,Unit 42安全小组发布了一个API调用及字符串的数据库查找工具,地址为:

https://github.com/pan-unit42/public_tools/tree/master/Reaver_Decompression

分析过程

我们以针对Reaver恶意软件家族的分析为例,尝试确定其使用的压缩算法,并确定是否可以在不运行恶意软件的前提下,从中逆向出其使用的算法。请注意,这里的前提是不运行恶意软件。

在我对该恶意软件的分析过程中,发现它似乎使用了一个修改过的Lempel-Ziv-Welch(LZW)压缩算法。我们所分析的Reaver恶意软件样本中,解压缩算法位于地址0x100010B2,其汇编代码大约有200行。解压缩例程如下所示:

  1. ; void __thiscall decompress(_DWORD *this, int nstream, int outputint zero, int zero2, int zero3) 
  2. decompress      proc near               ; CODE XREF: decompressingData+5A↓p 
  3. nstream         = dword ptr  8 
  4. output          = dword ptr  0Ch 
  5. zero            = dword ptr  10h 
  6. zero2           = dword ptr  14h 
  7. zero3           = dword ptr  18h 
  8.                  push    ebp 
  9.                  mov     ebp, esp 
  10.                  push    ebx 
  11.                  push    esi 
  12.                  push    edi 
  13.                  mov     esi, ecx 
  14.                  push    16512           ; unsigned int 
  15.                  call    Malloc 
  16.                  pop     ecx 
  17.                  mov     edi, eax 
  18.                  mov     ecx, 1020h 
  19.                  xor     eax, eax 
  20.                  mov     [esi], edi 
  21.                  xor     ebx, ebx 
  22.                  rep stosd 

为了简洁起见,我们没有展示该函数的全部代码。需要注意的地方是:

该函数调用约定(Calling Convention)是__thiscall(说明是C++);

该函数使用了5个参数;

该函数从恶意软件中调用一次(通过在IDA Pro中标识的交叉引用数量来看到的)。

下面是该函数调用部分的代码:

  1. xor     eax, eax 
  2. mov     ecx, [ebp+v6] 
  3. push    eax 
  4. push    eax 
  5. push    eax 
  6. movzx   eax, word ptr [ebx+24] 
  7. push    dword ptr [edx] ; output 
  8. lea     eax, [eax+ebx+26] 
  9. push    eax 
  10. call    decompress 

对调用解压缩函数的分析如下:

会清除EAX寄存器,因此EAX为0;

指向对象的指针存储在ECX(Thiscall)中;

EAX的三次push说明了解压缩例程的第3、4、5个参数始终为0;

第2个参数是指向目标缓冲区的指针;

第1个参数是指向压缩数据的指针。

而压缩的数据如下:

  1. 08 00 A5 04 01 12 03 06  8C 18 36 7A 04 21 62 25   ..¥.....Œ.6z.!b% 
  2. 08 94 24 33 64 B8 20 C3  86 4D 03 05 02 09 1A 8C   .”$3d¸ Ã†M.....Œ 
  3. 71 A3 C7 91 32 74 AA CC  29 23 C7 49 98 36 65 82   q£Ç‘2tªÌ)#ÇI˜6e‚ 
  4. 5C CC 58 F0 20 8E 1E 52  CA 9C 19 C2 E6 CD C8 25   ÌXð Ž.RÊœ.ÂæÍÈ% 
  5. 65 F2 AC 1C D8 32 46 0E  98 32 9F C0 29 E3 06 67   eò¬.Ø2F.˜2ŸÀ)ã.g 
  6. 9E 22 78 54 62 E4 69 50  06 0C A0 33 E5 94 09 43   ž"xTbäiP.. 3å”.C 
  7. A7 8C 51 A4 4A 59 36 8D  01 75 0A 48 2B 61 D8 D4   §ŒQ¤JY6..u.H+aØÔ 
  8. 29 83 75 A7 46 18 32 64  40 25 52 86 0D C8 32 60   )ƒu§F.2d@%R†.È2` 
  9. C5 A6 34 DB 52 C6 0C 85  64 D4 D4 99 43 87 CA 9B   Å¦4ÛRÆ.…dÔÔ™C‡Ê› 
  10. 35 44 A1 C8 49 63 27 8D  DB 33 65 E6 D0 6D 4A A3   5D¡ÈIc'.Û3eæÐmJ£ 
  11. 07 93 37 7F EB C0 11 4C  D8 B0 4C B8 61 C7 66 65   .“7.ëÀ.LØ°L¸aÇfe 
  12. 8A B6 46 0F A1 81 E5 BC  19 93 78 8E 5F C0 6E 16   Š¶F.¡.å¼.“xŽ_Àn. 
  13. A3 4D 38 85 4E 18 39 74  BC CA 29 4C 7A F3 59 19   £M8…N.9t¼Ê)LzóY. 

为了简洁起见,在这里也不展示压缩数据的全部内容,其完整大小是45115字节。

第1-7字节(08 00 A5 04 01 12 03)是压缩例程的一个“魔法值”,我们在所有Reaver变种中都发现了这个头部。

在掌握了上述这些之后,我们就可以将注意力集中在解压缩例程的工作机制上。

请大家注意:在这里,我们可以监视调用或转储目标缓冲区内容后所得到的返回结果,其中会包含解压缩的数据,但是如果选择这种方法,就需要我们在调试器中运行代码。而我们的前提是不运行恶意软件样本。

创建DLL

在掌握了一定信息后,我们开始创建一个DLL。我们可以使用Visual Studio,或者任何能处理编译程序集(NASM/MASM)的编译器。创建一个新的空DLL项目,并添加一个新的头文件。

举例来说,我创建了一个头文件,如下所示:

  1. #pragma once 
  2. #ifndef _DEFINE_LZWDecompress_DLL 
  3. #define _DEFINE_LZWDecompress_DLL 
  4.   
  5. #ifdef __cplusplus   
  6. extern "C" { 
  7. #endif   
  8. __declspec(dllexport) BOOL  Decompress(char *src, char *dst); 
  9. #ifdef __cplusplus   
  10. #endif  
  11. BOOL Decompress(char *src, char *dst); 
  12. #endif 

上述代码会创建一个名为“Decompress”的文件,并且能接收两个参数。我们在这里之所以仅使用了两个参数,原因在于其他三个参数始终为0,所以无需定义他们。该函数的返回类型为布尔型。

针对源文件(.cpp或.c),需要从IDA Pro或其他调试器中获得汇编代码,再将其添加到源文件中。以下是我修复后的源文件代码:

  1. #include <windows.h> 
  2. #include <stdio.h> 
  3. #include "TestDLL.h" 
  4. BOOL Decompress(char *src, char *dst) 
  5.     //Use calloc vs malloc.  Temp buffer is for the dictionary  
  6.     void *pTmpbuff; 
  7.     pTmpbuff = (int*) calloc(0x4080u, sizeof(unsigned int)); 
  8.     if (src && dst) 
  9.     { 
  10.         __asm 
  11.         { 
  12.             xor ebx, ebx;  //Need to clear ebx register  
  13.             SUB ESP, 0x40; //Need to subtract stack, so we don’t overwrite some Ctypes return data 
  14.             MOV ESI, ESP; 
  15.             PUSH EAX; 
  16.             POP EDI;        //Our Temp Buffer 
  17.             PUSH[EBP + 8];      //Source Buffer 
  18.             POP EAX; 
  19.             PUSH[EBP + 0xC];  //Destination Buffer 
  20.             POP EDX; 
  21.             LEA ECX, DWORD PTR DS : [EAX + 1]; //Where we start.  Get the 1st DWORD of the compressed data appears to be magic value 
  22.             MOV DWORD PTR DS : [ESI], EDI;//Temp buffer address 
  23.             MOV DWORD PTR DS : [ESI + 0x1C], EDX;//Destination address 
  24.             MOV DWORD PTR DS : [ESI + 0x18], ECX;//Compressed Data 
  25.             MOV BYTE PTR DS : [ESI + 0x20], BL;//0 
  26.             MOV CL, BYTE PTR DS : [EAX];//08 
  27.             PUSH 1; 
  28.             POP EAX; 
  29.             MOV BYTE PTR DS : [ESI + 0x22], CL; 
  30.             SHL EAX, CL; 
  31.             MOV DWORD PTR DS : [ESI + 0x30], EBX; 
  32.             MOV WORD PTR DS : [ESI + 8], AX; 
  33.             INC EAX; 
  34.             MOV WORD PTR DS : [ESI + 0xA], AX; 
  35.             MOV EAX, DWORD PTR SS : [EBP + 0x10]; 
  36.             MOV DWORD PTR DS : [ESI + 0x2C], EAX; 
  37.             LEA EAX, DWORD PTR DS : [EAX * 8 + 0x1F]; 
  38.             SHR EAX, 5; 
  39.             SHL EAX, 2; 
  40.             CMP BYTE PTR SS : [EBP + 0x18], BL; 
  41.             MOV DWORD PTR DS : [ESI + 0x38], EAX; 
  42.             SETE AL; 
  43.             DEC EAX; 
  44.             AND AL, 1; 
  45.             ADD EAX, 0x0FF; 
  46.             CMP AL, BL; 
  47.             MOV BYTE PTR DS : [ESI + 0xC], AL; 
  48.             JNZ SHORT check3; 
  49.             MOV EAX, DWORD PTR SS : [EBP + 0x14]; 
  50.             MOV DWORD PTR DS : [ESI + 0x14], EDX; 
  51.             MOV DWORD PTR DS : [ESI + 0x28], EAX; 
  52.             MOV DWORD PTR DS : [ESI + 0x34], EBX; 
  53. check3: 
  54.             MOV ECX, ESI; 
  55.             CALL check4; 
  56. check26: 
  57.             MOV ECX, ESI; 
  58.             CALL check10; 
  59.             MOV EDI, EAX; 
  60.             CMP DI, WORD PTR DS : [ESI + 0xA]; 
  61.             JE Finished; 
  62.             CMP DI, WORD PTR DS : [ESI + 8]; 
  63.             JNZ SHORT check22; 
  64.             MOV ECX, ESI; 
  65.             CALL check4; 
  66. check24: 
  67.             MOV ECX, ESI; 
  68.             CALL check10; 
  69.             MOV EDI, EAX 
  70.             CMP DI, WORD PTR DS : [ESI + 8] 
  71.             JNZ SHORT check23; 
  72.             JMP SHORT check24; 
  73. check22: 
  74.             CMP DI, WORD PTR DS : [ESI + 0X24] 
  75.             JNB SHORT check25; 
  76.             PUSH EDI 
  77.             JMP SHORT check27; 
  78. check25: 
  79.             PUSH EBX; 
  80. check27: 
  81.             MOV ECX, ESI; 
  82.             CALL check28; 
  83.             MOVZX AX, AL; 
  84.             PUSH EAX; 
  85.             PUSH EBX; 
  86.             MOV ECX, ESI; 
  87.             CALL check31; 
  88.             PUSH EDI; 
  89.             MOV ECX, ESI; 
  90.             CALL check35; 
  91.             MOV EBX, EDI; 
  92.             JMP SHORT check26; 
  93. check10: 
  94.             MOVZX EAX, BYTE PTR DS : [ECX + 0x20]; 
  95.             PUSH EBX; 
  96.             PUSH ESI; 
  97.             PUSH EDI; 
  98.             MOVZX EDI, BYTE PTR DS : [ECX + 0x23]; 
  99.             ADD EAX, EDI; 
  100.             CMP EAX, 8; 
  101.             JA SHORT Check6; 
  102.             MOV EDX, DWORD PTR DS : [ECX + 0x18]; 
  103.             MOVZX ESI, BYTE PTR DS : [EDX]; 
  104.             JMP SHORT Check8; 
  105.         Check6: 
  106.             MOV EDX, DWORD PTR DS : [ECX + 0x18]; 
  107.             CMP EAX, 0x10; 
  108.             JA SHORT Check7; 
  109.             MOVZX ESI, WORD PTR DS : [EDX]; 
  110.             JMP SHORT Check8; 
  111.         Check7: 
  112.             MOVZX ESI, BYTE PTR DS : [EDX + 2]; 
  113.             MOVZX EBX, WORD PTR DS : [EDX]; 
  114.             SHL ESI, 0X10; 
  115.             OR ESI, EBX; 
  116.         Check8: 
  117.             MOV EBX, EAX; 
  118.             PUSH 0x20; 
  119.             SHR EBX, 3; 
  120.             ADD EBX, EDX; 
  121.             MOV DL, AL; 
  122.             AND DL, 7; 
  123.             MOV DWORD PTR DS : [ECX + 0X18], EBX; 
  124.             MOV BYTE PTR DS : [ECX + 0X20], DL; 
  125.             POP ECX; 
  126.             SUB ECX, EAX; 
  127.             MOV EAX, ESI; 
  128.             PUSH 0x20; 
  129.             SHL EAX, CL; 
  130.             POP ECX; 
  131.             SUB ECX, EDI; 
  132.             POP EDI; 
  133.             POP ESI; 
  134.             POP EBX; 
  135.             SHR EAX, CL; 
  136.             RETN; 
  137.         check28: 
  138.             MOV EAX, DWORD PTR DS : [ECX]; 
  139.             MOV EDX, DWORD PTR SS : [ESP + 4]; 
  140.         check30: 
  141.             MOVZX ECX, DX; 
  142.             MOV CX, WORD PTR DS : [EAX + ECX * 4]; 
  143.             CMP CX, 0x0FFFF; 
  144.             JE SHORT check29; 
  145.             MOV EDX, ECX; 
  146.             JMP SHORT check30; 
  147.         check29: 
  148.             MOVZX ECX, DX; 
  149.             MOV AL, BYTE PTR DS : [EAX + ECX * 4 + 2]; 
  150.             RETN 4; 
  151.         check31: 
  152.             MOVZX EDX, WORD PTR DS : [ECX + 0x24]; 
  153.             LEA EAX, DWORD PTR DS : [ECX + 0x24]; 
  154.             PUSH ESI; 
  155.             MOV ESI, DWORD PTR DS : [ECX]; 
  156.             PUSH EDI; 
  157.             MOV DI, WORD PTR SS : [ESP + 0xC]; 
  158.             MOV WORD PTR DS : [ESI + EDX * 4], DI; 
  159.             MOV ESI, DWORD PTR DS : [ECX]; 
  160.             MOVZX EDX, WORD PTR DS : [EAX]; 
  161.             MOV DI, WORD PTR SS : [ESP + 0x10]; 
  162.             MOV WORD PTR DS : [ESI + EDX * 4 + 2], DI; 
  163.             INC WORD PTR DS : [EAX]; 
  164.             MOV AX, WORD PTR DS : [EAX]; 
  165.             POP EDI; 
  166.             CMP AX, 8; 
  167.             POP ESI; 
  168.             JE SHORT check32; 
  169.             CMP AX, 0x10; 
  170.             JE SHORT check32; 
  171.             CMP AX, 0x20; 
  172.             JE SHORT check32; 
  173.             CMP AX, 0x40; 
  174.             JE SHORT check32; 
  175.             CMP AX, 0x80; 
  176.             JE SHORT check32; 
  177.             CMP AX, 0x100; 
  178.             JE SHORT check32; 
  179.             CMP AX, 0x200; 
  180.             JE SHORT check32; 
  181.             CMP AX, 0x400; 
  182.             JE SHORT check32; 
  183.             CMP AX, 0x800; 
  184.             JNZ SHORT check33; 
  185.         check32: 
  186.             INC BYTE PTR DS : [ECX + 0x23]; 
  187.         check33: 
  188.             RETN 8; 
  189.         check4: 
  190.             MOV EDX, ECX; 
  191.             PUSH EDI; 
  192.             MOV ECX, 0x1000; 
  193.             OR EAX, 0xFFFFFFFF; 
  194.             MOV EDI, DWORD PTR DS : [EDX] 
  195.             REP STOS DWORD PTR ES : [EDI]; 
  196.             XOR EAX, EAX; 
  197.             POP EDI; 
  198.             CMP WORD PTR DS : [EDX + 8], AX; 
  199.             JBE SHORT check1; 
  200.             PUSH ESI; 
  201.             MOV ESI, DWORD PTR DS : [EDX]; 
  202.         check2: 
  203.             MOVZX ECX, AX; 
  204.             MOV WORD PTR DS : [ESI + ECX * 4 + 2], AX; 
  205.             INC EAX; 
  206.             CMP AX, WORD PTR DS : [EDX + 8]; 
  207.             JB SHORT check2; 
  208.             POP ESI; 
  209.         check1: 
  210.             MOV AX, WORD PTR DS : [EDX + 0xA]; 
  211.             INC AX; 
  212.             MOV WORD PTR DS : [EDX + 0x24], AX; 
  213.             MOV AL, BYTE PTR DS : [EDX + 0x22]; 
  214.             INC AL; 
  215.             MOV BYTE PTR DS : [EDX + 0x23], AL; 
  216.             RETN; 
  217.         check23: 
  218.             PUSH EDI; 
  219.             MOV ECX, ESI; 
  220.             CALL check35; 
  221.             MOV EBX, EDI; 
  222.             JMP SHORT check26; 
  223.         check35: 
  224.             PUSH EBP; 
  225.             MOV EBP, ESP; 
  226.             PUSH ESI; 
  227.             PUSH EDI; 
  228.             MOV ESI, ECX; 
  229.             NOP; 
  230.             MOV AX, WORD PTR SS : [EBP + 8]; 
  231.             CMP AX, WORD PTR DS : [ESI + 8]; 
  232.             JNB SHORT check36; 
  233.             NOP; 
  234.             MOV ECX, DWORD PTR DS : [ESI]; 
  235.             MOV EDX, DWORD PTR DS : [ESI + 0x1C]; 
  236.             MOV EDI, DWORD PTR DS : [ESI + 0x30]; 
  237.             MOVZX EAX, AX; 
  238.             MOV AL, BYTE PTR DS : [ECX + EAX * 4 + 2]; 
  239.             MOV BYTE PTR DS : [EDX + EDI], AL; 
  240.             INC DWORD PTR DS : [ESI + 0x30]; 
  241.             NOP; 
  242.             MOV EAX, DWORD PTR DS : [ESI + 0x30]; 
  243.             CMP EAX, DWORD PTR DS : [ESI + 0x2C]; 
  244.             JNZ SHORT FuncRetn; 
  245.             MOV ECX, ESI; 
  246.             CALL check37; 
  247.             NOP; 
  248.             JMP SHORT FuncRetn; 
  249. check36: 
  250.             MOVZX EDI, AX; 
  251.             MOV EAX, DWORD PTR DS : [ESI]; 
  252.             MOV ECX, ESI; 
  253.             SHL EDI, 2; 
  254.             MOV AX, WORD PTR DS : [EDI + EAX]; 
  255.             PUSH EAX; 
  256.             CALL check35; 
  257.             NOP; 
  258.             MOV EAX, DWORD PTR DS : [ESI]; 
  259.             MOV ECX, ESI; 
  260.             MOV AX, WORD PTR DS : [EDI + EAX + 2]; 
  261.             PUSH EAX; 
  262.             CALL check35; 
  263.             NOP; 
  264.             NOP; 
  265.             POP EDI; 
  266.             POP ESI; 
  267.             POP EBP; 
  268.             RETN 4; 
  269.         check38: 
  270.             MOVZX EDX, AL; 
  271.             MOVZX EDX, BYTE PTR DS : [EDX + ECX + 0xD]; 
  272.             ADD DWORD PTR DS : [ECX + 0x34], EDX; 
  273.             MOV EDX, DWORD PTR DS : [ECX + 0x34]; 
  274.             CMP EDX, DWORD PTR DS : [ECX + 0x28]; 
  275.             JB SHORT FuncRetrn2; 
  276.             INC AL; 
  277.             CMP AL, 4; 
  278.             MOV BYTE PTR DS : [ECX + 0xC], AL; 
  279.             JNB SHORT Frtn; 
  280.             MOVZX EAX, AL; 
  281.             MOVZX EAX, BYTE PTR DS : [EAX + ECX + 0xD]; 
  282.             SHR EAX, 1; 
  283.             MOV DWORD PTR DS : [ECX + 0x34], EAX; 
  284.             FuncRetrn2: 
  285.             MOV EAX, DWORD PTR DS : [ECX + 0x38]; 
  286.             MOV EDX, DWORD PTR DS : [ECX + 0x14]; 
  287.             IMUL EAX, DWORD PTR DS : [ECX + 0x34]; 
  288.             SUB EDX, EAX; 
  289.             MOV DWORD PTR DS : [ECX + 0x1C], EDX; 
  290.             Frtn: 
  291.             RETN; 
  292. FuncRetn: 
  293.             NOP; 
  294.             POP EDI; 
  295.             POP ESI; 
  296.             POP EBP; 
  297.             RETN 4; 
  298.         check37: 
  299.             MOV AL, BYTE PTR DS : [ECX + 0xC]; 
  300.             AND DWORD PTR DS : [ECX + 0x30], 0; 
  301.             CMP AL, 0x0FF; 
  302.             JNZ SHORT check38; 
  303.             MOV EAX, DWORD PTR DS : [ECX + 0x38]; 
  304.             SUB DWORD PTR DS : [ECX + 0x1C], EAX; 
  305.             RETN; 
  306.         Finished: 
  307.             MOV ESP,EBP; 
  308.             POP EBP; 
  309.             //Debug VS Release build have different stack sizes.  The following is needed for the return parameters and CTYPES 
  310. #ifdef _DEBUG 
  311.             ADD ESI, 0x120; 
  312. #else 
  313.             ADD ESI, 0x58; //Need for Pythnon CTypes return parameters! 
  314. #endif 
  315.             RETN; 
  316.         } 
  317.     } 
  318.     return TRUE

通过IDA Pro或者例如Immunity Debugger这样的反汇编程序来获取汇编代码并不难,但是在获得之后,还需要我们进行一些处理。特别需要注意的一个地方就是在代码块中进行的函数调用。在汇编中,每一次调用过程都需要一个名称(标签),并且所有的代码需要按照调用顺序正确地排列,否则将产生意外的结果,或者是直接崩溃。因此,我们在复制每个函数的汇编代码时都需要非常谨慎。在刚刚的例子中,为了方便快速,我直接使用了“check”来表示函数名称或者跳转的位置。

由于LZW使用索引将数据编码到字典中,解压例程所做的第一件事,就是分配内存中的16512字节(0x4080)的缓冲区来创建字典。在汇编中,它使用C++ API malloc分配缓冲区,并将缓冲区设置为NULL(这是malloc的工作方式)。有一种更简单有效的方法,是使用calloc函数,在减少指令数量的前提下实现缓冲区的分配。

我们首先在C++中进行编码,然后再Visual Studio中使用__asm关键字内嵌汇编语言。在__asm内的代码块就是我们放置汇编指令并进行必要调整的位置:

将EBX设置为0;

从栈中减去64字节(0x40),以防止我们覆盖任何栈的数据;

将栈指针保存到ESI中;

EDI指向我们通过calloc创建的字典缓冲区;

EAX指向我们的源数据;

EDX指向我们的目标缓冲区。

为了满足解压缩算法的要求,我们手工添加了下面的9行代码,其余代码直接从Immunity Debugger中复制即可:

  1. xor ebx, ebx;          //Need to clear ebx register  
  2. SUB ESP, 0x40;    //Need to subtract stack, so we don’t overwrite some Ctypes return data 
  3. MOV ESI, ESP; 
  4. PUSH EAX; 
  5. POP EDI;    //Our Temp Buffer 
  6. PUSH[EBP + 8];  //Source Buffer 
  7. POP EAX; 
  8. PUSH[EBP + 0xC];  //Destination Buffer 
  9. POP EDX; 

此时,我们需要做的就是更新汇编调用,跳转到有意义的名称,并按正确的顺序来排列它们。现在代码应该可以编译并运行了。但当例程结束后,我们必须手动恢复栈,从而让Python ctypes返回到正确的调用方。我们添加了以下代码:

  1. Finished: 
  2. MOV ESP,EBP; 
  3. POP EBP; 
  4. //Debug VS Release build have different stack sizes.  The following is needed for the return parameters and CTYPES 
  5. #ifdef _DEBUG 
  6. ADD ESI, 0x120; 
  7. #else 
  8. ADD ESI, 0x58; //Need for CTypes return parameters!!!! 
  9. #endif 
  10. RETN; 

在这里,我们尝试恢复堆栈指针寄存器(SP)和基址指针寄存器(BP),并将0x120或0x58添加到ESI,具体要取决于VS的版本是测试版还是正式版。

调用DLL

至此,我们就有了一个DLL,可以开始调用它,并通过Python和ctypes来传递它的数据。下面这个Python脚本的作用就是利用这个DLL,来解密Reaver的数据:

  1. #------------------------------------------------------------------------------- 
  2. Name:        LzwDecompression 
  3. # Purpose: 
  4. # Author:      Mike Harbison Unit 42 
  5. # Created:     11/11/2017 
  6. #------------------------------------------------------------------------------- 
  7. from ctypes import * 
  8. import sys 
  9. import os.path 
  10. import argparse 
  11. import re,struct 
  12. import subprocess, random 
  13. # MAP types to ctypes 
  14. LPBYTE = POINTER(c_ubyte) 
  15. LPCSTR = LPCTSTR = c_char_p 
  16. BOOL = c_bool 
  17. if os.name != 'nt'
  18.     print ("Script can only be run from Windows"
  19.     sys.exit("Sorry Windows only"
  20. def assert_success(success): 
  21.     if not success: 
  22.         raise AssertionError(FormatError()) 
  23. def LzwDecompress(hdll,data): 
  24.     inbuf = create_string_buffer(data) 
  25.     outbuf= create_string_buffer(len(data)) 
  26.     success = hdll.Decompress(inbuf,outbuf) 
  27.     assert_success(success) 
  28.     return outbuf.raw 
  29. def CabExtract(match,pargs,data): 
  30.     offset = match.start() 
  31.     CabHeaderMagicValue = offset + 124 
  32.     CabSizeStart = offset + 132 
  33.     CabFileNameStart = offset + 184 
  34.     CabFileNameEnd = data[CabFileNameStart:].find(''
  35.     CabName = data[CabFileNameStart:CabFileNameStart+CabFileNameEnd] 
  36.     CabSize = struct.unpack("L",data[CabSizeStart:CabSizeStart+4])[0] 
  37.     CabData = data[CabHeaderMagicValue:CabHeaderMagicValue+CabSize] 
  38.     FileName=pargs.input_file 
  39.     #Add magic value 
  40.     Cab="4D534346".decode('hex')+CabData[4:] 
  41.     print "Found our CAB Data at file offset-->{}".format(offset) 
  42.     CabDir=os.path.splitext(FileName)[0] 
  43.     if not os.path.exists(CabDir): 
  44.         os.makedirs(CabDir) 
  45.     else
  46.         CabDir+='_'+str(random.randint(1111,9999)) 
  47.         os.makedirs(CabDir) 
  48.     CabFile=os.path.basename(FileName).split('.')[0]+".cab" 
  49.     with open(CabDir+"\"+CabFile,"wb") as fp: 
  50.         fp.write(Cab) 
  51.     print "Wrote CAB File-->%s"%CabDir+"\"+CabFile 
  52.     print "Expanding CAB File %s"%CabName 
  53.     args = [" -r ",CabDir + "\" + CabFile,' ',CabDir] 
  54.     result=subprocess.Popen("expand "+"".join(args), stdout=subprocess.PIPE) 
  55.     result.wait() 
  56.     if "Expanding Files Complete" not in result.stdout.read(): 
  57.         print "Error Expanding CAB file" 
  58.         sys.exit(1) 
  59.     ExpandedFile = CabDir + "\" + CabName 
  60.     if not os.path.isfile(ExpandedFile): 
  61.         print "Did not find our expanded file %s"%CabName 
  62.         sys.exit(1) 
  63.     print "Check directory %s for expanded file %s"%(CabDir,CabName) 
  64.     return ExpandedFile 
  65. def DecompressRoutine(pargs,hlzw,data): 
  66.     LzwCompPattern = "x08x00xA5x04x01x12x03" 
  67.     regex = re.compile(LzwCompPattern) 
  68.     for match in regex.finditer(data): 
  69.         offset=match.start() 
  70.         print "Found our compression header at file offset-->{}".format(offset) 
  71.         Deflated=LzwDecompress(hlzw,data[offset:]) 
  72.         if Deflated: 
  73.             with open(pargs.out_file, "wb"as wp: 
  74.                 wp.write(Deflated) 
  75.             print "Wrote decompressed stream to file-->%s"%(pargs.out_file) 
  76.             return True 
  77.     return False 
  78. def Start(pargs,hlzw,data): 
  79.     CabCompPattern = bytearray("46444944657374726F790000464449436F7079004644494973436162696E657400000000464449437265617465000000636162696E65742E646C6C004D6963726F736F6674"
  80.     #Check For CAB file magic value first 
  81.     found = False 
  82.     regex = re.compile(CabCompPattern.decode('hex')) 
  83.     for match in regex.finditer(data): 
  84.         found = True 
  85.         ExpandedFile=CabExtract(match,pargs,data) 
  86.         if ExpandedFile: 
  87.             with open(ExpandedFile,"rb"as fp: 
  88.                 ExpandedData=fp.read() 
  89.                 DecompressRoutine(pargs,hlzw,ExpandedData) 
  90.             return True 
  91.     if not found: 
  92.         result=DecompressRoutine(pargs,hlzw,data) 
  93.         if result: 
  94.             return True 
  95.         else
  96.             return False 
  97. def main(): 
  98.     parser=argparse.ArgumentParser() 
  99.     parser.add_argument("-i"'--infile' , dest='input_file',help="Input file to process",required=True
  100.     parser.add_argument("-o"'--outfile', dest='out_file',help="Optional Output file name",required=False
  101.     results = parser.parse_args() 
  102.     if not results.out_file: 
  103.         results.out_file=results.input_file + "_dec.txt" 
  104.     lzwdll="LzwDecompress.dll" 
  105.     lzwdllpath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + lzwdll 
  106.     if os.path.isfile(lzwdllpath): 
  107.         lzw = windll.LoadLibrary(lzwdllpath) 
  108.         lzw.Decompress.argtypes=(LPCSTR,LPCSTR) 
  109.         lzw.Decompress.restypes=BOOL 
  110.     else
  111.         print ("Missing LzwDecompress.DLL"
  112.         sys.exit(1) 
  113.     with open(results.input_file,"rb"as fp: 
  114.         FileData=fp.read() 
  115.         Success=Start(results,lzw,FileData) 
  116.         if not Success: 
  117.             print("Did not find CAB or Compression routine in file %s")%(results.input_file) 
  118. if __name__ == '__main__'
  119.     main() 

为适应Reaver的多个变种,我们不久前更新了这个Python脚本。新的Reaver变种使用了微软的CAB包作为第一层压缩。该脚本执行以下操作:

1. 加载我们的DLL LzwDecompress.dll。

2. 尝试定位到修改后的LZW头部或Microsoft CAB的签名值。

3. 对于LZW解压缩例程,创建的两个字符串缓冲区作为指向缓冲区的指针。源缓冲区是指向需要解压缩的数据的指针,目标缓冲区是我们存储解压缩后数据的位置。

4. 调用Decompress,并将其传递给我们的两个参数。

5. 将数据写入文件。

 下面是脚本运行截图:

下面的示例是使用LZW来解压缩一个旧版本的Reaver恶意软件例程。解压的数据将写入到文本文件中,如下所示:

  1. RA@10001=ole32.dll 
  2. RA@10002=CoCreateGuid 
  3. RA@10003=Shlwapi.dll 
  4. RA@10004=SHDeleteKeyA 
  5. RA@10005=wininet.dll 
  6. RA@10006=InternetOpenA 
  7. RA@10007=InternetOpenUrlA 
  8. RA@10008=InternetCloseHandle 
  9. RA@10009=HttpQueryInfoA 
  10. RA@10010=InternetReadFile 
  11. [TRUNCATED] 
  12. RA@10276=image/jpeg 
  13. RA@10277=netsvcs 
  14. RA@10282=Global%sEvt 
  15. RA@10283=temp%sk.~tmp 
  16. RA@10284=Global%skey 
  17. RA@10285=%08x%s 
  18. RA@10286=%s 
  19. RA@10287=%s*.* 
  20. RA@10288=%s%s 
  21. RA@10289=CMD.EXE 
  22. RA@10290=%s= 
  23. RA@10311=%sctr.dll 
  24. RA@10312=uc.dat 
  25. RA@10313=ChangeServiceConfig2A 
  26. RA@10314=QueryServiceConfig2A 

下面是新版本Reaver恶意软件的例子,它使用Microsoft CAB添加了一层压缩:

在这里,脚本成功将文件解压缩,并读取解压缩后的文件,最终找到了解压缩例程的魔法值,并将解压数据写入文本文件中。

总结

通过本文,我们了解到,可以直接利用汇编中已有的解压缩例程,将其放在Visual Studio中编译成DLL,最后再使用Python来调用。由于我们仅仅需要调用该例程来传递恶意软件的数据,因此并不需要再在C或者Python中重新调用接口。

上述方法的实现,需要我们对于汇编语言、栈以及例程中所需的寄存器有足够了解。一旦掌握了这些知识,该方法就很容易实现,并且可以用于任何函数之中。

责任编辑:武晓燕 来源: paloaltonetworks.com
相关推荐

2015-07-28 13:36:04

2011-07-04 17:53:48

快速测试

2021-03-31 09:17:46

Android恶意软件攻击

2013-12-19 09:23:15

2010-11-15 11:15:55

2022-12-26 08:00:00

2017-07-24 10:15:32

2010-02-22 13:07:21

2011-03-15 14:16:27

2020-12-02 10:21:48

恶意软件黑客网络攻击

2012-08-21 10:11:42

2021-08-28 05:01:41

勒索软件方法

2021-09-05 11:58:26

勒索软件恢复云恢复勒索软件

2022-06-20 12:05:19

网络威胁恶意软件黑客

2016-04-18 13:41:10

软件IC网

2013-12-04 13:34:44

2015-03-13 15:24:49

开源软件代码Github重定义

2022-09-22 08:18:28

JavaAssistJboss开源

2018-12-14 14:30:12

安全检测布式系测试

2019-12-20 16:34:32

网络安全软件电脑
点赞
收藏

51CTO技术栈公众号