电脑疯子技术论坛|电脑极客社区

微信扫一扫 分享朋友圈

已有 1072 人浏览分享

一病毒样本的解密+分析。【样本已放出】

 关闭 [复制链接]
1072 3
这里把对krap样本解密和分析过程与大家一块交流一下。

说一下流程,否则容易乱

1 解密-(pack部分)--> 2二次解密(pack部分)--->3内存中解密(pack部分)--->4替换自身模块基址映像(pack部分)---->5注入explorer.exe(病毒自身任务完成,被删除。注入explorer也仅是为了再次注入svchost,完成后,explorer中的线程退出)----> 6 注入svchost(在这个傀儡进程里面干活是这个病毒真正的目的,下载download)


// --------开始了 -----------------------

1绕过虚拟机的解密头

    Packed.Win32.Krap 开头是个略有变化的解密头部,变化的是解密最开始绕过虚拟机的技巧。   
   
    004010AB    55              push ebp
    004010AC    8D4D E8         lea ecx,dword ptr ss:[ebp-18]
    004010AF    8365 E8 00      and dword ptr ss:[ebp-18],0
    004010B3    51              push ecx
    004010B4    51              push ecx
    004010B5    6A 00           push 0
    004010B7    FF15 18314000   call dword ptr ds:[<&COMCTL32.LBItemFromPt>; COMCTL32.LBItemFromPt
    004010BD    FF15 F8304000   call dword ptr ds:[<&WS2_32.WSAGetLastError>; WS2_32.WSAGetLastError
    004010C3    3D 78050000     cmp eax,578             // 验证网络函数的LastError返回值
.--004010C8    0F85 15010000   jnz __krap_.004011E3    // 没有仿真该处直接被引到退出流程
|   
|   ........
|   
|  //退出进程
.->004011E3    FF15 04314000   call dword ptr ds:[<&KERNEL32.TerminateP>; kernel32.TerminateProcess
    004011E9    8BE5            mov esp,ebp
    004011EB    5D              pop ebp
    004011EC    C3              retn

    在不同的被打包的程序中,call LBItemFromPt 这处调用不断变化。例如另一个变种(Win32.LdPinch.arqy)
使用了GetEffectiveClientRect这个函数。
   
    WSAGetLastError比较有意思的地方是,如果上一次的调用是不同的API时,会有不同的返回值结果。
比如调用VirtualAlloc返回值是0x57,调用GetFileVerSionInfoSizeA返回值是0x714。AVer的WSAGetLastError如果
是简单函数仿真的话将被绕过。

    接着是解密前的配置工作,简单说一下思路。
    就是把这个要解密的次数,大小等信息先解密出来。以后的解密工作是根据这些配置进行的。
    我逆出来的解密配置结构大致如下,解密配置和解密后面的数据都是用的一个算法,单字节解密。
   
    typedef struct _decrypt_info
    {
       unsinged int  encrypt_buff_off; 被加密数据的偏移
       unsinged int  encrypt_buff_len; 被加密数据的大小
    }decrypt_info;

    typedef struct _virus_config_info
    {
       unsigned int alloc_mem_len1;  分配解密内存长度1
       unsigned int alloc_mem_len2;  分配解密内存长度2
       int          decryp_count  ;  需要解密的次数
       int          unknown;
       decrypt_info di[1]         ;  解密需要的信息,需要解密多少次,就有decrypt_info[decryp_count]长度
    }virus_config_info;   
   
    几个重要的数据
    ebp-0x0c  ---  记录最后key的偏移
    ebp-0x10  ---  控制解密的次数,
    ebp-0x28  ---  key的首地址
   
    ebp-0x1c  ---  累加解密长度
    ebp-0x18  ---  本次解密长度
   
   
    004010CE    29FF            sub edi,edi
    004010D0    897D FC         mov dword ptr ss:[ebp-4],edi
    004010D3    6A 00           push 0
    004010D5    9D              popfd
    004010D6    81D7 0F734000   adc edi,__krap_.0040730F        ; key 首地址+偏移
    004010DC    037D E8         add edi,dword ptr ss:[ebp-18]
    004010DF    897D F4         mov dword ptr ss:[ebp-C],edi    ; 记录最后key的偏移
    004010E2    89E9            mov ecx,ebp
    004010E4    BE A8010000     mov esi,1A8
    004010E9    F7DE            neg esi
    004010EB    01F1            add ecx,esi
    004010ED    894D F0         mov dword ptr ss:[ebp-10],ecx   ; 配置段的首地址
    004010F0    BE 0E734000     mov esi,__krap_.0040730E
    004010F5    8975 D8         mov dword ptr ss:[ebp-28],esi   ; key偏移减1
    004010F8    8B4D F0         mov ecx,dword ptr ss:[ebp-10]
    004010FB    83E9 F0         sub ecx,-10
    004010FE    894D E4         mov dword ptr ss:[ebp-1C],ecx
    00401101    816D FC 2800000>sub dword ptr ss:[ebp-4],28
    00401108    F755 FC         not dword ptr ss:[ebp-4]
    0040110B    8345 FC 01      add dword ptr ss:[ebp-4],1      ; 得到第一次在栈中解密的长度
    0040110F    FF75 D8         push dword ptr ss:[ebp-28]
    00401112    FF75 FC         push dword ptr ss:[ebp-4]
    00401115    FF75 F4         push dword ptr ss:[ebp-C]
    00401118    8DBD 58FEFFFF   lea edi,dword ptr ss:[ebp-1A8]
    0040111E    57              push edi
   
    // 这里对栈中数据进行解密,就是解密配置信息
    0040111F    E8 DCFEFFFF     call __krap_.00401000
   
    这个版本的Krap是分3段进行解密。
   
    解密前的堆栈:
    0012FE18  FA034B1C
    0012FE1C  805B639F
    0012FE20  00000001
    0012FE24  E56C6946
    0012FE28  8128E5B0
    0012FE2C  8128E560
    0012FE30  00000000
    0012FE34  FF29F178
    0012FE38  FF29F178
    0012FE3C  FA034B34
    0012FE40  805B056B
   
    解密后的堆栈:   
    0012FE18  000013F0 ------>开始加密的代码长度       --、——————长度相加,刚好是解密总长度
    0012FE1C  000026C0 ------>文件内部加密的PE文件长度 --/ ------>多重加密,第一次解密后仍然是加密的
    0012FE20  00000003 ------>控制解密次数
    0012FE24  00001A00 ------>解密及获得API地址后,分配内存的长度
    0012FE28  00004000 ------>加密数据的起始偏移,在data节中
    0012FE2C  0000330E ------>第一次解密的长度
    0012FE30  00002000 ------>加密数据的起始偏移,在rsrc节中
    0012FE34  00000096 ------>第二次解密的长度
    0012FE38  00003248 ------>加密数据的起始偏移,在rdata节中
    0012FE3C  0000070C ------>第三次解密的长度
   
    解密的函数比较简单,我也不知道是个啥加密算法,不多说了配合F5看一些的情况吧。
   
    int __stdcall decode_buff(char *alloc_buf/*解密后的数据buff*/, char *encode_buf/*加密数据*/, int size, char *key)
    {
      int result;  
      int v5;   
      char v6;
      int _ch;  
      signed int eax_ret;  
      unsigned int index;  
      signed int v10;  
      signed int v11;

      eax_ret = 0;
      index = 0;
      v10 = -1;
      while ( index < size )
      {
        v6 = ~((unsigned __int8)~(unsigned __int8)*(_DWORD *)&encode_buf[index] | (unsigned __int8)~(_BYTE)v10);
        _ch = *(_DWORD *)&key[eax_ret];
        v11 = -1;
        result = (int)&alloc_buf[index];
        alloc_buf[index] = 0;
        alloc_buf[index] -= ~(_BYTE)_ch + 1 + v6;
        alloc_buf[index] = -alloc_buf[index];
        v5 = eax_ret++ + 1;
        if ( eax_ret == 1 )
          eax_ret = 0;
        v11 = v5;
        if ( (unsigned int)eax_ret > 0x488000 )
          break;
        index -= v10;
      }
      return result;
    }

    接着分配内存进行第一次解密,同时将解密过程中的信息存放的堆栈中,
   
    $-1AC    > 00080000  -------->存放当前解密次数,用于和配置中的次数相比较
    ...
    $-48     > 00080000
    $-44     > 00000000
    $-40     > 00001FE0
    $-3C     > FF20B000
    $-38     > E1C60400
    $-34     > 00000001
    $-30     > 00000000
    $-2C     > 00000408
    $-28     > 0040730E   ------->加密的数据区
    $-24     > 00003AB0   ------->分配的内存长度
    $-20     > 00400000   ------->硬编码写入映像的基址例如0x400000
    $-1C     > 0012FE28   ------->存放的配置信息+0X10偏移的指针
    $-18     > 00000000   ------->解密长度的总计
    $-14     > 80616CDB   ------->分配内存的偏移0x1960处,是解密后运行的入口点
    $-10     > 0012FE18   ------->存放的配置信息
    $-C      > 0040730F   
    $-8      > FFFFFFFE   ------->存放分配的内存的首地址
    $-4      > 00000028
    $ ==>    > 0012FFF0
   
    //第一次解密   
    00401124    8B5D F0         mov ebx,dword ptr ss:[ebp-10]
    00401127    8B5B 04         mov ebx,dword ptr ds:[ebx+4]    ; 从配置中取一次解密长度0x26c0
    0040112A    8B75 F0         mov esi,dword ptr ss:[ebp-10]
    0040112D    031E            add ebx,dword ptr ds:[esi]      ; 累加0x13f0
    0040112F    895D DC         mov dword ptr ss:[ebp-24],ebx
    00401132    BB 40000000     mov ebx,40
    00401137    53              push ebx                        ; 页属性,可执行,可读写
    00401138    68 00300000     push 3000
    0040113D    FF75 DC         push dword ptr ss:[ebp-24]
    00401140    6A 00           push 0
    00401142    FF15 00314000   call dword ptr ds:[<&KERNEL32.VirtualAlloc>]  ; kernel32.VirtualAlloc
   
    // ...
    // 累加解密次数,和配置中的次数进行比
   
    0040117E    8B79 08         mov edi,dword ptr ds:[ecx+8]     ; 取配置中的比较次数
    00401181    39FE            cmp esi,edi                      ; 比较解密次数
    00401183    73 2C           jnb short __krap_.004011B1
    00401185    FF75 D8         push dword ptr ss:[ebp-28]       ; 密钥的首地址
    00401188    8B4D E4         mov ecx,dword ptr ss:[ebp-1C]
    0040118B    FF71 04         push dword ptr ds:[ecx+4]        ; 解密的长度
    0040118E    8B45 E4         mov eax,dword ptr ss:[ebp-1C]
    00401191    8B5D E0         mov ebx,dword ptr ss:[ebp-20]    ; 基址0x400000
    00401194    0318            add ebx,dword ptr ds:[eax]
    00401196    53              push ebx                         ; 加密的数据
    00401197    8B75 F8         mov esi,dword ptr ss:[ebp-8]
    0040119A    0375 E8         add esi,dword ptr ss:[ebp-18]
    0040119D    56              push esi
    0040119E    E8 5DFEFFFF     call <__krap_.decode_buff>
    004011A3    8B4D E4         mov ecx,dword ptr ss:[ebp-1C]     ; ecx -> decrypt_info * 指针
    004011A6    8B45 E8         mov eax,dword ptr ss:[ebp-18]
    004011A9    0341 04         add eax,dword ptr ds:[ecx+4]
    004011AC    8945 E8         mov dword ptr ss:[ebp-18],eax     ; 累加解密的长度
    004011AF  ^ EB AE           jmp short __krap_.0040115F
   
    // 等于3次后解密完毕
   
    004011BF    894D EC         mov dword ptr ss:[ebp-14],ecx     ; 解密后运行的入口点
    004011C2    8B8D 64FEFFFF   mov ecx,dword ptr ss:[ebp-19C]
    004011C8    51              push ecx
    004011C9    8B5D F0         mov ebx,dword ptr ss:[ebp-10]
    004011CC    8B45 F8         mov eax,dword ptr ss:[ebp-8]
    004011CF    0343 04         add eax,dword ptr ds:[ebx+4]
    004011D2    50              push eax
    004011D3    8B45 EC         mov eax,dword ptr ss:[ebp-14]
    004011D6    50              push eax
    004011D7    68 000000B8     push B8000000
    004011DC    FF75 EC         push dword ptr ss:[ebp-14]       // 压入解密后的入口点
    004011DF    C2 0400         retn 4                           // 跳向新的入口 00921690

评论 3

bek bek  管理员  发表于 2010-11-22 13:48:02 | 显示全部楼层
2  内存中的二次解密
  
    第一次解密后跳到这里:
   
    00921690    55              push ebp
    00921691    8BEC            mov ebp,esp
    00921693    81EC 68020000   sub esp,268
   
    //... 无效的东西,nop掉

    009216A1    50              push eax                                                            
    009216A2    8BC5            mov eax,ebp
    009216A4    83C0 18         add eax,18
    009216A7    8985 C8FEFFFF   mov dword ptr ss:[ebp-138],eax
    009216AD    58              pop eax
    009216AE    E8 FDFEFFFF     call 009215B0     // 获得kernel32.dll的基址
    009216B3    8985 E8FDFFFF   mov dword ptr ss:[ebp-218],eax  
    009216B9    E8 22FFFFFF     call 009215E0     // 获得ntdll.dll的基址
   
    // 单字节赋值,乱序的写入栈中需要的模块名称及函数名称,用来获得以后要导出的API
   
    009216CD    C685 2CFEFFFF 6B    mov byte ptr ss:[ebp-1D4],6B
    009216D4    C685 2DFEFFFF 65    mov byte ptr ss:[ebp-1D3],65
    009216DB    C685 2EFEFFFF 72    mov byte ptr ss:[ebp-1D2],72
    009216E2    C685 2FFEFFFF 6E    mov byte ptr ss:[ebp-1D1],6E
    009216E9    C685 30FEFFFF 65    mov byte ptr ss:[ebp-1D0],65
    009216F0    C685 31FEFFFF 6C    mov byte ptr ss:[ebp-1CF],6C
    009216F7    C685 32FEFFFF 33    mov byte ptr ss:[ebp-1CE],33
    009216FE    C685 33FEFFFF 32    mov byte ptr ss:[ebp-1CD],32
    00921705    C685 34FEFFFF 00    mov byte ptr ss:[ebp-1CC],0   
    ...
   
    // 栈中数据
   
    0012FC04  6B 65 72 6E 65 6C 33 32 00 00 00 00 4C 6F 63 61  kernel32....Loca
    0012FC14  6C 41 6C 6C 6F 63 00 01 56 69 72 74 75 61 6C 41  lAlloc.VirtualA
    0012FC24  6C 6C 6F 63 00                                   lloc.

    0012FD15                                               4C  L
    0012FD25  6F 63 61 6C 46 72 65 65 00                       ocalFree.

    0012FCA8  56 69 72 74 75 61 6C 46 72 65 65 00              VirtualFree.

    0012FD10  47 65 74 50 72 6F 63 41 64 64 72 65 73 73 00 7C  GetProcAddress.|

    0012FDB8  56 69 72 74 75 61 6C 50 72 6F 74 65 63 74 00 00  VirtualProtect..

    0012FCB8  55 6E 6D 61 70 56 69 65 77 4F 66 46 69 6C 65 00  UnmapViewOfFile.
    ...
   
    // 在将这些排列不连续的字符串指针,加入到一个表中留着后面使用
   
    $-A8     > 0012FC10  ASCII "LocalAlloc"
    $-A4     > 0012FD24  ASCII "LocalFree"
    $-A0     > 0012FC1C  ASCII "VirtualAlloc"
    $-9C     > 0012FCA8  ASCII "VirtualFree"
    $-98     > 0012FD10  ASCII "GetProcAddress"
    $-94     > 0012FDB8  返回到 0012FDB8
    $-90     > 0012FCB8  ASCII "UnmapViewOfFile"
    $-8C     > 0012FC2C  ASCII "LoadLibraryA"
    $-88     > 0012FCC8  ASCII "GetModuleHandleA"
    $-84     > 0012FBEC  ASCII "LoadResource"
    $-80     > 0012FCDC  ASCII "LockResource"
    $-7C     > 0012FDC8  ASCII "FindResourceA"
    $-78     > 0012FBD4  ASCII "GetProcessHeap"
    $-74     > 0012FCEC  ASCII "RtlAllocateHeap"
   
    // 然后定位到自身捆绑的一个PE文件,我们这里为了区别,叫它pe_bound,这是个被UPX压缩的文件,也是加密的。
   
    在内存中用那个pe_bound文件修改自身,起到内存免杀作用。
    首先,按照pe_bound的section格式,继续拷贝每一个节到自身进程空间。
      
    // 获得需要的API的地址
   
    00921CA8    E8 B3E7FFFF     call 00920460                              //GetModuleHandleA 的API地址
    00921CAD    8B8D CCFEFFFF   mov ecx,dword ptr ss:[ebp-134]
    00921CB3    8941 30         mov dword ptr ds:[ecx+30],eax
    00921CB6    8D95 2CFEFFFF   lea edx,dword ptr ss:[ebp-1D4]
    00921CBC    52              push edx
    00921CBD    8B85 CCFEFFFF   mov eax,dword ptr ss:[ebp-134]
    00921CC3    8B48 30         mov ecx,dword ptr ds:[eax+30]
    00921CC6    FFD1            call ecx                                  // 获得自身kernle32基址
    00921CC8    8985 E8FDFFFF   mov dword ptr ss:[ebp-218],eax                  
    00921CCE    8B95 58FFFFFF   mov edx,dword ptr ss:[ebp-A8]
    00921CD4    52              push edx
    00921CD5    8B85 E8FDFFFF   mov eax,dword ptr ss:[ebp-218]
    00921CDB    50              push eax                                 //获得 (kernel32.LocalAlloc)
    00921CDC    E8 7FE7FFFF     call 00920460
    00921CE1    8B8D CCFEFFFF   mov ecx,dword ptr ss:[ebp-134]
    00921CE7    8901            mov dword ptr ds:[ecx],eax
    00921CE9    8B95 5CFFFFFF   mov edx,dword ptr ss:[ebp-A4]
    00921CEF    52              push edx
    00921CF0    8B85 E8FDFFFF   mov eax,dword ptr ss:[ebp-218]
    00921CF6    50              push eax                                 //获得 (kernel32.LocalFree)
    //...
   
    // 再分配一块内存
    00921E2D    6A 04           push 4
    00921E2F    68 00300000     push 3000
    00921E34    8B55 0C         mov edx,dword ptr ss:[ebp+C]
    00921E37    52              push edx
    00921E38    6A 00           push 0
    00921E3A    8B85 CCFEFFFF   mov eax,dword ptr ss:[ebp-134]
    00921E40    8B48 08         mov ecx,dword ptr ds:[eax+8]
    00921E43    FFD1            call ecx                       

    //具体信息
    0012FB5C   00921E45  /CALL 到 VirtualAlloc 来自 00921E43
    0012FB60   00000000  |Address = NULL
    0012FB64   00001A00  |Size = 1A00 (6656.)
    0012FB68   00003000  |AllocationType = MEM_COMMIT|MEM_RESERVE
    0012FB6C   00000004  \Protect = PAGE_READWRITE

    00921E45    8985 C4FEFFFF   mov dword ptr ss:[ebp-13C],eax
    00921E4B    8B95 C4FEFFFF   mov edx,dword ptr ss:[ebp-13C]
    00921E51    52              push edx
    00921E52    8B45 08         mov eax,dword ptr ss:[ebp+8]
    00921E55    50              push eax
    00921E56    E8 25E2FFFF     call 00920080          ---- 第二次解密数据到930000这块内存,这块就是用来放解密的那个pe_bound
   
    这个文件本身是非连续加密的,加密的算法简单,跳过若干个字节进行解密,看一些解密后的情况。

bek bek  管理员  发表于 2010-11-22 14:00:39 | 显示全部楼层
1楼和2楼放出部分解密过程

下面直接放出整个解密过程文本和病毒样本

有段时间没有来VIP发帖了。感觉有点对不住大家。





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
757338903 757338903  VIP荣誉会员  发表于 2010-11-22 14:31:08 | 显示全部楼层
先下载下来,研究研究~~嘿嘿,,bek老师注意休息啊!

您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

关注

23

粉丝

2901

主题
精彩推荐
热门资讯
网友晒图
图文推荐

Powered by Pcgho! X3.4

© 2008-2022 Pcgho Inc.