脱壳笔记-手工脱FSG压缩壳

壳:一段保护软件不被非法修改或反编译的程序,它会在附加在我们的代码之前,获得运行的权利,然后对程序进行修改。实现对软件的保护与压缩。

一、进行FSG压缩后的产生的效果

1、进行FSG压缩前后的效果图
脱壳笔记-手工脱FSG压缩壳
FSG压缩前为34KB,压缩后为9KB。此时不用怀疑,这两个程序都能都正常运行。

2、使用PEID查壳
脱壳笔记-手工脱FSG压缩壳
脱壳笔记-手工脱FSG压缩壳
可以看到,TestFSG使用的是FSG v2.0进行的压缩

3、使用OD对比查看模块间的调用(右键->查找->所有模块间的调用)
脱壳笔记-手工脱FSG压缩壳
脱壳笔记-手工脱FSG压缩壳
进行FSG的压缩之后,我们看不到模块之间的调用了

4、使用LoadPE查看TestFSG.exe的输入表
脱壳笔记-手工脱FSG压缩壳
此时输入表中只有两个API,它们就是FSG壳运行时所需要使用到的两个API。
FSG壳通过LoadLibrayA加载原始程序所需要使用的动态链接库,然后通过GetProcAddress获取原始程序所需要调用的API的地址,将原始的输入表还原到程序。
根据这样就实现了加载程序时对输入表的动态还原,所以我们第3点中看不到模块间的调用的原因。

既然FSG壳将我们的API进行了“隐藏”,那么我们要进行的脱壳就是要实现API的还原。

二、手工脱FSG壳

1、使用OD加载TestFSG
脱壳笔记-手工脱FSG压缩壳
此时程序先运行的是FSG这个壳的代码,这段代码主要用于将原始的代码、数据进行解压还原的操作。
解压完成后,程序就会跑到我们原始的程序上运行。
此时我们就可以将原始程序dump(转储)下来,再进行一些修复,就完成了FSG的手动脱壳。

2、使用F8单步运行,如果遇到call直接往下运行,如果遇到往前面运行的jmp语句,直接点击jmp的下一条语句,按F4(运行到指定位置)往下运行。直到跑到该汇编指令处:
脱壳笔记-手工脱FSG压缩壳
jnz语句用于最终判断FSG壳是否解压完成。如果没有解压完成,就跳转到004001D4的指令继续解压(循环解压);如果解压完成,继续往下执行0040001D1的jmp指令,使得程序跳转到原始程序的入口点运行。

3、在jmp处下断点(F2),F9运行到jmp处
脱壳笔记-手工脱FSG压缩壳
在jmp处F7(单步步入)执行后,就跳转到了我们原是程序的入口点
脱壳笔记-手工脱FSG压缩壳
此时并不能看到汇编代码,我们右键->分析->分析代码,此时就能显示了

4、使用LoadPE进行dump解压后的内存中的程序(注:如果无法在进程中找到使用OD打开的应用程序,使用管理员权限再运行下LoadPE)
脱壳笔记-手工脱FSG压缩壳
右键->完整转存(dump full)
转存后的可执行文件:dumped.exe
脱壳笔记-手工脱FSG压缩壳

双击dumped.exe,此时并不能正常运行!
脱壳笔记-手工脱FSG压缩壳
这是正常情况,因为壳通常都会修改IAT(关于IAT的理解)。于是乎,程序虽然经过FSG的解压,进入到了程序的入口点,但是FSG壳在还原的时候,并没有完全的还原IAT(导入函数地址表)。
甚至其它的一些壳不会进行还原IAT,它仅仅是实现了一个自己能够识别的IAT,然后再程序运行的过程中,它自身进行动态的还原。

5、使用ImpREC选择脱壳的程序查看输入表函数信息(注:如果没有找到脱壳程序的话,同样使用管理员权限运行即可)
脱壳笔记-手工脱FSG压缩壳
1)选择我们的脱壳进程TestFSG.exe(它这里自动转换成了小写testfsg.exe,不用介意)
2)填写我们OEP(程序入口点)的RVA值。
获取的方法一:手动计算,前面我们通过OD已经跳转到了OEP,地址为0040102D,00400000为基址,102D为OEP的RVA值。
获取的方法二:在OEP指令右键->用OllyDump脱壳调试进程->获取EIP作为OEP
脱壳笔记-手工脱FSG压缩壳
3)点击“自动查找IAT”,再点击“获取输入表”,此时就能获取到输入表的函数信息了
4)点击“转储到文件”转储到先前的dumped.exe文件,会新形成一个dumped_.exe文件
脱壳笔记-手工脱FSG压缩壳

到此,程序依旧不能运行,因为ImpREC仅仅帮我们修复了IAT中的一个dll,并没有完全的还原IAT

6、手动修复导入表(就是输入表)中的导入函数表信息(IAT Import address table)
1)在ImpREC获取一个导入函数的RVA值,在OD的内存地址窗口中跳转到RVA值
脱壳笔记-手工脱FSG压缩壳
此时取第一个函数的RVA值:00009000
前面我们知道程序加载到内存中的基址为:00400000
所以函数在内存中地址为:00400000+00009000=00409000

2)在内存中”Ctrl+G”跳转到00409000地址处,再”右键->长型->地址”,就可显示出调用的API名字
脱壳笔记-手工脱FSG压缩壳
首先我们得知道:每个Dll的导入函数地址表中根据0字节结尾!因为它们本身就是一个数组实现的。
而FSG虽然在应用程序加载的时候进行的动态还原,但是它并没有使得每个导入函数地址表中以0结尾,而我们要做的就是将不以0结尾了,使用二进制0填充

3)往下拉取到一个Dll的导入函数列表(IAT)末尾处
脱壳笔记-手工脱FSG压缩壳
使用0填充后效果
脱壳笔记-手工脱FSG压缩壳

4)然后将每个Dll的IAT末尾不为0的,都使用0字节填充,直到到导入表中的最后一个dll的IAT结束(通常为许多0字节结束的地方为结束位置,你也可以在结束的位置继续往下拉取,自行判断是否为结束)
脱壳笔记-手工脱FSG压缩壳
至此,对IAT的修复已经完成。
记录最后结束为止的RVA值:9170
减去起始位置的RVA值,得出IAT的大小:9170-9000=170

5)再使用ImpREC手动填写IAT的起始RVA(9000),以及IAT的大小170,再进行转储dumped.exe就OK了
脱壳笔记-手工脱FSG压缩壳
注:如果有显示无效的的输入表函数信息,直接右键->删除指针后再转储即可

本文难免有所错误,如有问题欢迎留言