FSG 1.33 -> dulek/xt大体思路:

1. FSG首先会初始化ESI(需要解压数据的地址)和EDI(解压后数据存放的地址);
2. 00404B9B    74 2F           je      short 00404BCC
    该处代码每实现一次都会都会完成一段数据的解密工作,每次解压完成后可以看一下内存映像,看看解压后的效果;
3. 然后再次初始化ESI和EDI,进行下一段数据的解密;


1.  首先,我们利用PeiD查看一下区段信息,内容如下:

 这里需要注意:
第一个区段的文件偏移和文件大小都为零,相对虚拟地址为1000,大小为21000,所以当用OD载入内存的时候,内存地址401000---421FFF(第一区段的内存空间)区域内,所有的数据都是0,而这毫无以为是没有意义的。在这里我们就会猜想到,外壳程序肯定会向这个内存区域填充数据,而数据的来源就应该是第二个区段的数据,这只是我们的假设,后面我们会利用代码来验证我们的假设。
2.  利用OD载入我们的程序,在数据窗口中我们查看一下各个区段的数据:
第一个区段(401000421FFF)的内容都为0:

第二个区段(422000---430FFF)为一些无规则的数据,这就是需要解压的数据:

3.  看完了内存数据的分配,下面让我们主要看一下我们的代码:
00430764 >  BE A4014000     mov     esi, 004001A4      ; 这是个定值,用于提取数据
00430769    AD              lods    dword ptr [esi]     ; 提取400198,用于定位后面的子程序调用
0043076A    93              xchg    eax, ebx           ; 并保存到EBX
0043076B    AD              lods    dword ptr [esi]     ; 提取401000(第一个区段的虚拟地址)
0043076C    97              xchg    eax, edi           ; 并保存到EDI
0043076D    AD              lods    dword ptr [esi]   ; 提取422CE4(需要解压的数据的地址,位于第二区段中)
0043076E    56              push    esi             ; 保存ESI=4001B0
0043076F    96              xchg    eax, esi
00430770    B2 80           mov     dl, 80
00430772    A4              movs    byte ptr es:[edi], byte ptr [esi]         ; 向第一区段解压数据
下图:利用4001A4来定位要提取的数据:

4.  初始化好ESI(需要解压的数据的地址)和ESI(存放数据的地址),就开始复制数据了,代码如下:
00430772    A4              movs    byte ptr es:[edi], byte ptr [esi]  ; 向第一区段解压数据
00430773    B6 80           mov     dh, 80
00430775    FF13            call    dword ptr [ebx]  ; EBX=400198,该调用主要是进行跳转的判断,小菜不懂算法,哈哈
00430777  ^ 73 F9           jnb     short 00430772
00430779    33C9            xor     ecx, ecx
0043077B    FF13            call    dword ptr [ebx]
0043077D    73 16           jnb     short 00430795
0043077F    33C0            xor     eax, eax
00430781    FF13            call    dword ptr [ebx]
00430783    73 1F           jnb     short 004307A4
00430785    B6 80           mov     dh, 80
00430787    41              inc     ecx
00430788    B0 10           mov     al, 10
0043078A    FF13            call    dword ptr [ebx]
0043078C    12C0            adc     al, al
0043078E  ^ 73 FA           jnb     short 0043078A
00430790    75 3C           jnz     short 004307CE
00430792    AA              stos    byte ptr es:[edi]
00430793  ^ EB E0           jmp     short 00430775
00430795    FF53 08         call    dword ptr [ebx+8]
00430798    02F6            add     dh, dh
0043079A    83D9 01         sbb     ecx, 1
0043079D    75 0E           jnz     short 004307AD
0043079F    FF53 04         call    dword ptr [ebx+4]
004307A2    EB 26           jmp     short 004307CA
004307A4    AC              lods    byte ptr [esi]
004307A5    D1E8            shr     eax, 1
004307A7    74 2F           je      short 004307D8  ;当该处跳转实现的时候,就完成了一段数据的解压工作,然后需要重新赋值ESI和EDI,进行下一断数据的解压;
004307A9    13C9            adc     ecx, ecx
004307AB    EB 1A           jmp     short 004307C7
004307AD    91              xchg    eax, ecx
004307AE    48              dec     eax
004307AF    C1E0 08         shl     eax, 8
004307B2    AC              lods    byte ptr [esi]
004307B3    FF53 04         call    dword ptr [ebx+4]
004307B6    3D 007D0000     cmp     eax, 7D00
004307BB    73 0A           jnb     short 004307C7
004307BD    80FC 05         cmp     ah, 5
004307C0    73 06           jnb     short 004307C8
004307C2    83F8 7F         cmp     eax, 7F
004307C5    77 02           ja      short 004307C9
004307C7    41              inc     ecx
004307C8    41              inc     ecx
004307C9    95              xchg    eax, ebp
004307CA    8BC5            mov     eax, ebp
004307CC    B6 00           mov     dh, 0
004307CE    56              push    esi    ; (422CEE)(422CFD)(422CFE)
004307CF    8BF7            mov     esi, edi
004307D1    2BF0            sub     esi, eax
004307D3    F3:A4           rep     movs byte ptr es:[edi], byte ptr [esi]
004307D5    5E              pop     esi
004307D6  ^ EB 9D           jmp     short 00430775
004307D8    8BD6            mov     edx, esi   ;运行到该处时,需要重新计算ESI和EDI,此时可以打开内存镜像,查看一下解压后的数据;
004307DA    5E              pop     esi
004307DB    AD              lods    dword ptr [esi]
004307DC    48              dec     eax
5.  重新计算ESI和EDI的方法:


其实利用该图来获得EDI(解压数据的存放地址,按顺序依次给EDI赋值),ESI从422CE4依次增长。
6.  解压的大体流程: 
第一段需要解压的数据:把区域4220CE4---42E3A0的数据解压到401000;
第二段需要解压的数据:把区域42E3A0----42F24B的数据解压到415000;(用来存放IAT)
第三段需要解压的数据:把区域42F24B----42F787的数据解压到41A000;
第四段需要解压的数据:把区域42F787-----43000C的数据解压到41F000;
第五次需要解压的数据:把区域43000C-----430764的数据解压到4019DB ;(解压出来的数据是函数名)
7. 当函数名解压出来之后,就要来获得各个函数的地址,然后填充IAT啦:
  下图就是解压出来的数据:


然后我们看一下IAT的填充代码:
004307E7  ^\EB 87           jmp     short 00430770
004307E9    AD              lods    dword ptr [esi]  ; 获得LoadLibraryA的地址
004307EA    93              xchg    eax, ebx         ; 保存到EBX
004307EB    5E              pop     esi
004307EC    46              inc     esi
004307ED    AD              lods    dword ptr [esi]  ; 得到IAT的虚拟地址
004307EE    97              xchg    eax, edi       ; 保存到EDI
004307EF    56              push    esi
004307F0    FF13            call    dword ptr [ebx]  ; 利用LoadLibraryA加载KERNEL32.DLL
004307F2    95              xchg    eax, ebp   ;模块基址保存到EBP
004307F3    AC              lods    byte ptr [esi]   ; 获得函数名
004307F4    84C0            test    al, al
004307F6  ^ 75 FB           jnz     short 004307F3
004307F8    FE0E            dec     byte ptr [esi]     ; 减一
004307FA  ^ 74 F0           je      short 004307EC    ; 如果等于零,则处理另一个模块的函数,不等于零则还是本模块
004307FC    79 05           jns     short 00430803
004307FE    46              inc     esi
004307FF    AD              lods    dword ptr [esi]
00430800    50              push    eax
00430801    EB 09           jmp     short 0043080C
00430803    FE0E            dec     byte ptr [esi]    ; 再减一,获得函数名
00430805  - 0F84 5B1EFDFF   je      00402666  ;等于0时候处理完成了所有的函数,跳到OEP
0043080B    56              push    esi       ; 函数名地址入栈
0043080C    55              push    ebp     ; 模块基址入栈
0043080D    FF53 04         call    dword ptr [ebx+4]  ; 调用GetProcAddress
00430810    AB              stos    dword ptr es:[edi]  ; 将地址填充到IAT
00430811  ^ EB E0           jmp     short 004307F3   ; 处理下一个函数
IAT填充的思路:
要想填充IAT,我们就要活得IAT的地址,该地址保存在每个模块名的前面的四个字节中,将该值给EDI,就可以利用stos,保存函数地址了。
函数名的查找过程:扫描每个字节,然后进行减一操作,若为零,则当前模块的函数处理完毕,需要处理下一个模块,如果不等于零,则再次减一,此次若等于零,则表示所有的函数处理完成了,直接跳到OEP就可以了,若不等于零,则处理本模块的下一个函数。