吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9789|回复: 18
收起左侧

[原创] FSG的分析以及输入表的手动处理

[复制链接]
ximo 发表于 2009-9-10 19:23
本帖最后由 ximo 于 2009-9-10 20:54 编辑

好久不脱壳了,都快生疏了,于是拣个软柿子捏捏,顺便复习下PE格式。
壳很简单,FSG,大部分人接触脱壳破解几个星期估计都会脱。
做个详细的分析,并纯手动修复输入表。高手就飘过吧。。。
哎,高手都玩TMD,NP了,我这种层次只能捏压缩壳玩,实在惭愧。

一、压入壳的参数
01000154 >  8725 58050201        xchg dword ptr ds:[1020558],esp
0100015A    61                   popad
0100015B    94                   xchg eax,esp
开头的3句式把[1020558]的内容压入了堆栈,等效于:
01000154 > /E9 A70B0000          jmp np_fsg2.01000D00
01000159   |90                   nop

01000D00    B8 5C050201          mov eax,np_fsg2.0102055C
01000D05    B9 007D0000          mov ecx,7D00
01000D0A    BA 80000000          mov edx,80
01000D0F    BB 5C050201          mov ebx,np_fsg2.0102055C
01000D14    BC C4FF0600          mov esp,6FFC4
01000D19    BD 04760001          mov ebp,np_fsg2.01007604
01000D1E    BE ECBD0101          mov esi,np_fsg2.0101BDEC
01000D23    BF 00100001          mov edi,np_fsg2.01001000
01000D28  ^ E9 2FF4FFFF          jmp np_fsg2.0100015C
01000D2D    90                   nop

B8 5C 05 02 01 B9 00 7D 00 00 BA 80 00 00 00 BB 5C 05 02 01 BC C4 FF 06 00 BD 04 76 00 01 BE EC
BD 01 01 BF 00 10 00 01 E9 2F F4 FF FF 90
不过FSG用了xchg语句,更为巧妙和简捷。
观察下[1020558]里的内容:
0102053C   01001000  np_fsg2.01001000-----------EDI------------------------代码段的起始RVA
01020540   0101BDEC  np_fsg2.0101BDEC-----------ESI------------------------保存加密数据的起始地址
01020544   01007604  np_fsg2.01007604-----------EBP------------------------输入表RVA
01020548   00000000-----------------------------ESP------------------------0102053C+4*8=0102055C
0102054C   0102055C  np_fsg2.0102055C-----------EBX------------------------FSG自己的结构
01020550   00000080-----------------------------EDX------------------------dl=80h
01020554   00007D00-----------------------------ECX------------------------加密数据的长度(大小)
01020558   0006FFC4-----------------------------EAX------------------------保存原始的ESP      
FSG自身结构:
0102055C   010001E8  np_fsg2.010001E8--------------------------------------解密函数(aplib)
01020560   010001DC  np_fsg2.010001DC--------------------------------------解密函数(aplib)
01020564   010001DE  np_fsg2.010001DE--------------------------------------解密函数(aplib)
01020568   0100739D  np_fsg2.0100739D--------------------------------------未加壳程序的入口地址(OEP)
0102056C > 7C801D77  kernel32.LoadLibraryA
01020570 > 7C80ADA0  kernel32.GetProcAddress

其中所代表的各个值有后面的调试可以知道。

二、解压缩
0100015C    55                   push ebp
0100015D    A4                   movs byte ptr es:[edi],byte ptr ds:[esi]
0100015E    B6 80                mov dh,80
01000160    FF13                 call dword ptr ds:[ebx]
01000162  ^ 73 F9                jnb short np_fsg2.0100015D
01000164    33C9                 xor ecx,ecx
01000166    FF13                 call dword ptr ds:[ebx]
01000168    73 16                jnb short np_fsg2.01000180
0100016A    33C0                 xor eax,eax
0100016C    FF13                 call dword ptr ds:[ebx]
0100016E    73 1F                jnb short np_fsg2.0100018F
01000170    B6 80                mov dh,80
01000172    41                   inc ecx
01000173    B0 10                mov al,10
01000175    FF13                 call dword ptr ds:[ebx]
01000177    12C0                 adc al,al
01000179  ^ 73 FA                jnb short np_fsg2.01000175
0100017B    75 3A                jnz short np_fsg2.010001B7
0100017D    AA                   stos byte ptr es:[edi]
0100017E  ^ EB E0                jmp short np_fsg2.01000160
01000180    FF53 08              call dword ptr ds:[ebx+8]
01000183    02F6                 add dh,dh
01000185    83D9 01              sbb ecx,1
01000188    75 0E                jnz short np_fsg2.01000198
0100018A    FF53 04              call dword ptr ds:[ebx+4]
0100018D    EB 24                jmp short np_fsg2.010001B3
0100018F    AC                   lods byte ptr ds:[esi]
01000190    D1E8                 shr eax,1
01000192    74 2D                je short np_fsg2.010001C1
01000194    13C9                 adc ecx,ecx
01000196    EB 18                jmp short np_fsg2.010001B0
01000198    91                   xchg eax,ecx
01000199    48                   dec eax
0100019A    C1E0 08              shl eax,8
0100019D    AC                   lods byte ptr ds:[esi]
0100019E    FF53 04              call dword ptr ds:[ebx+4]
010001A1    3B43 F8              cmp eax,dword ptr ds:[ebx-8]
010001A4    73 0A                jnb short np_fsg2.010001B0
010001A6    80FC 05              cmp ah,5
010001A9    73 06                jnb short np_fsg2.010001B1
010001AB    83F8 7F              cmp eax,7F
010001AE    77 02                ja short np_fsg2.010001B2
010001B0    41                   inc ecx
010001B1    41                   inc ecx
010001B2    95                   xchg eax,ebp
010001B3    8BC5                 mov eax,ebp
010001B5    B6 00                mov dh,0
010001B7    56                   push esi
010001B8    8BF7                 mov esi,edi
010001BA    2BF0                 sub esi,eax
010001BC    F3:A4                rep movs byte ptr es:[edi],byte ptr ds:[esi]
010001BE    5E                   pop esi
010001BF  ^ EB 9F                jmp short np_fsg2.01000160                  

很明显这是个解压缩函数,所使用的压缩引擎为apLib。
这段是原来的.code段,也就是01001000处,长度为7D00的数据给解压缩了。
而压缩后的数据保存的地址为0101BDEC中。
0100015D    A4                   movs byte ptr es:[edi],byte ptr ds:[esi]

ds:[esi]=[0101BDEC]=A3
es:[edi]=[01001000]=00

由此可知:
0102053C   01001000  np_fsg2.01001000-----------EDI------------------------代码段的起始RVA
01020540   0101BDEC  np_fsg2.0101BDEC-----------ESI------------------------保存加密数据的起始地址
这2句结构的意思。

aplib压缩引擎是开源的,就不具体深究了。有兴趣的可以去参考它的源码。

三、输入表的处理:
010001C1    5E                   pop esi                                      ;解压数据完毕,并把输入表的RVA传给esi
010001C2    AD                   lods dword ptr ds:[esi]                      ;取出IID.FirstThunk
010001C3    97                   xchg eax,edi
010001C4    AD                   lods dword ptr ds:[esi]                      ;取出dll的name放eax
010001C5    50                   push eax                                     ;把DLL的name压栈
010001C6    FF53 10              call dword ptr ds:[ebx+10]                   ;eax=hDll=LoadLibraryA("comdlg32.dll"),以第一个DLL为例
010001C9    95                   xchg eax,ebp
010001CA    8B07                 mov eax,dword ptr ds:[edi]
010001CC    40                   inc eax                                      ;IAT存放在了[edi]-1的地方,手动修复的时候要用
010001CD  ^ 78 F3                js short np_fsg2.010001C2                    ;一个dll内的输入表处理完就跳,去进行下一个DLL处理
010001CF    75 03                jnz short np_fsg2.010001D4                   ;处理完所有的,则不跳
010001D1    FF63 0C              jmp dword ptr ds:[ebx+C]                     ;跳向OEP
010001D4    50                   push eax                                     ;压入hDll
010001D5    55                   push ebp                                     ;压入要处理的API名称
010001D6    FF53 14              call dword ptr ds:[ebx+14]                   ;eax=GetProcAddress(hDll,"PageSetupDlgW")以第一个函数为例
010001D9    AB                   stos dword ptr es:[edi]                      ;填充IAT
010001DA  ^ EB EE                jmp short np_fsg2.010001CA                   ;循环处理

填充IAT的过程很简单,详细见上面的注释。
处理完所有的IAT,就用010001D1    FF63 0C              jmp dword ptr ds:[ebx+C]  跳向了OEP。
由于ebx中保存的是FSG自身的结构,查上面的结构可知,[ebx+C]指向的值即为OEP。

至此,整个流程就基本分析清楚了。

由上面的分析,可以得到2种快捷的脱壳方法:

法1.查找跳向OEP的特征代码jmp dword ptr ds:[ebx+C],也就是一般脚本里的FF 63 0C,在这里下好断点,中断后F7,就到OEP了。此时dump,IR修复OK。

法2.由于
01000154 >  8725 58050201        xchg dword ptr ds:[1020558],esp
0100015A    61                   popad
执行完这2条语句后,堆栈中的内容为FSG自己的结构,而其自身结构的第四个值即为原始的OEP值,因此直接在OEP处下硬件断点,断下后即可脱壳了
故一般所谓的快速方法为:
1.载如OD,F8两次,然后再堆栈处,从上往下数4行,或者找LoadLibraryA前1行,在这个地址处下硬件断点,F9,中断后,即可。

四、纯手动修复IAT
首先在010001C1    5E                   pop esi    处dump程序,因为此时数据已经全部解压完毕,而IAT还没有填充。
下面就开始手动处理:
1.新建IID表:
找个空白的位置,用于新建IID表,我以9860为例。
由于用于修复的文件是从内存中dump出来的,因此不存在RVA和offset的区别,任何1个地址都是相同的。
先来看下IID的结构:
IID的结构如下:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    };
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
由5个DWORD组成

可知,他有5个DWORD值组成,具体如下:
1.OriginalFirstThunk
2.TimeDateStamp
3.ForwarderChain
4.Name
5.FirstThunk

下面就来开始填充。以第一个DLL为例:
由FSG结构中输入表的RVA得,下面的结构:
01007604  010012C4  np_fsg2.010012C4-------------------------------------IID->FirstThunk
01007608  01007AAC  ASCII "comdlg32.dll"---------------------------------IID->Name
0100760C  01001174  np_fsg2.01001174
01007610  01007AFA  ASCII "SHELL32.dll"
01007614  010012B4  np_fsg2.010012B4
01007618  01007B3A  ASCII "WINSPOOL.DRV"
0100761C  01001020  np_fsg2.01001020
01007620  01007B5E  ASCII "COMCTL32.dll"
01007624  010012EC  np_fsg2.010012EC
01007628  01007C76  ASCII "msvcrt.dll"
0100762C  01001000  np_fsg2.01001000
01007630  01007D08  ASCII "ADVAPI32.dll"
01007634  0100108C  np_fsg2.0100108C
01007638  010080EC  ASCII "KERNEL32.dll"
0100763C  01001028  np_fsg2.01001028
01007640  0100825E  ASCII "GDI32.dll"
01007644  01001188  np_fsg2.01001188
01007648  0100873C  ASCII "USER32.dll"
上述结构中,各个分支都存放着2个值:
1.dll的名称
2.起始FirstThunk的值

填充如下:
1.OriginalFirstThunk的值可以填充为00000000,我这里填充的是FirstThunk的值
2.TimeDateStamp的值可以填充为00000000,我这里填充的是FFFFFFFF
3.ForwarderChain的值可以填充为00000000,我这里填充的是FFFFFFFF
4.关键数据,这里的值是指向dll的name的,从上面的表可知,第一个dll是comdlg32.dll,而其地址为01007AAC,减去基址得00007AAC,因此这里填AC7A 0000
5.关键数据,这里的值是FirstThunk,由上表得,地址为010012C4,减去基址整理得:C412 0000
这里你可以自己找个空白的地址,然后自己填充IAT表,然后把修改FirstThunk的值,这里我直接使用的原始的输入表信息

下面的几个按照上面的方法进行填充
完全填充后如下:
1.jpg
2.填充IAT
以第一个DLL:comdlg32.dll的第一个函数为例子:
先来看下OD中的数据
OD中的数据:
010012C4  01007A7B  np_fsg2.01007A7B
010012C8  01007A5F  np_fsg2.01007A5F
010012CC  01007A9F  np_fsg2.01007A9F
010012D0  01007A51  np_fsg2.01007A51
010012D4  01007A41  np_fsg2.01007A41
010012D8  01007A8B  np_fsg2.01007A8B
010012DC  01007A6B  np_fsg2.01007A6B
010012E0  01007A15  np_fsg2.01007A15
010012E4  01007A2D  np_fsg2.01007A2D

分析下:
IID->FirstThunk=12C4,
看下12C4的值:
[010012C4]=01007A7B
翻到7A7B看下数据:
01007A7B  00 50 61 67 65 53 65 74 75 70 44 6C 67 57 00 00  .PageSetupDlgW..
不过7A7B并没有直接指向函数的头部,而是在前1个字节,这也是上面壳在填充输入表的时候,要inc eax的原因。
很明显是指向API函数的
不过由于windows在自己装载PE的时候,是允许按序号转载的,所以在每个函数前,都会预留2个字节,给存放序号用。
故我们的在填充FirstThunk的时候,需要把原始的数据再减去1(因为本来就已经向前1个字节了)
因此,修改后的数据为:01007A7B
再减去基址01000000,整理后得:7A7A 0000

当FirstThunk所指向的值为000000000的时候,改dll填充完毕。
因此我们要把遇到的FFFFFF7F都修改为00000000

后面的都是按照这个原则进行修改,就是把原来的值-01000001
完全修改后:
2.jpg
这样输入表就处理好了。

最后的工作就是修复输入表的RVA和 size,以及改写OEP的值了

OEP的值=739D
输入表的RVA=9860
size=(01001340-01001000)/4-8=C8,当然这个值可以改写的稍大点。

全处理完后,保存,OK了。
文件正常运行。脱壳到此结束。

整理一下:

1.运行到010001C1,dump文件
2.按上面的过程手动处理输入表
3.修改输入表RVA,size,以及OEP的值
4.保存,完工

基本上把这个压缩壳剖析完了,可能讲的又点啰嗦了。不过觉得该讲的也都讲述清楚了。
希望新手朋友看完有所收获吧。

作者:ximo[LCG]

试炼.rar

81.19 KB, 下载次数: 37, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 1威望 +1 收起 理由
wgz001 + 1 膜拜

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

西氏 发表于 2009-9-10 19:26
学习了。原来时分析过程。
zapline 发表于 2009-9-10 19:50
zzage 发表于 2009-9-10 20:02
wgz001 发表于 2009-9-10 20:04
先膜拜  后学习   [s:41]
小糊涂虫 发表于 2009-9-10 21:23
跟着超人看系列脱壳教程
zapline 发表于 2009-9-10 19:50

我也是一样看系列教程的~~~~~~`
8568309 发表于 2009-9-10 22:08
跟随前人的脚步学习
CHHSun 发表于 2009-9-12 16:02
专门来膜拜的{:299_843:}
255411 发表于 2009-9-13 07:53
老大 好久没脱壳  依然还是那么强悍。肯定是在偷着研究高科技
Hmily 发表于 2009-9-16 16:30
徐教授发帖了~强势插入惨无人道的围观~
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 10:18

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表