-
-
[分享][原创]IAT劫持在加壳中的运用
-
发表于:
2017-12-23 23:16
4310
-
来看雪有一段时间了,看过一些前辈们的文章,受益良多。今天也准备分享一点干货,以示回馈。也算是在看雪的第一篇文章吧。
关于加脱壳、PE、IAT的基本概念这里就不重复了,有需要的可以看看其他高手分析的文章。
程序的一大特点就是具有确定性。只要两套程序在某个时间点具有相同状态,在同样的外部输入下,它们将会得到同样的运行结果。而这就是加壳脱壳的基本前提。我们知道,脱壳的基本流程是:dump、寻找oep、修复IAT(严格来说是IT)。理论上讲,在oep处脱壳后修复了IT的程序,与带壳达到oep的程序基本具有相同的状态,因而能够向后正常运行。但注意是基本相同,也就是说一旦存在一点不同,脱壳就是不彻底的,还需要做额外的修补。
我们来仔细回顾一下脱壳的过程。假定在第n次运行带壳程序到oep时脱壳,我们将此时的程序状态命名为State[n]。以后第m次启动脱壳后的程序到达oep的状态命名为State[m]。思考:State[n]与State[m]是否都能正常运行?它们有什么区别?
State[n]:第n次运行原始程序时在oep的程序状态,壳程序参与了初始化,一定能正常运行。
State[m]:第n次dump的静态程序状态+第m次动态加载到达oep之后的状态,壳程序未参与初始化,可能无法正常运行。
区别1:壳程序是否参与了oep之前的初始化。
区别2:不同次启动时的各个dll的基地址不同。
为什么说State[m]可能无法正常运行呢?假定其中包含的State[n]的部分静态信息是需要被动态修复的,那么它就无法运行。而这个就可以被某些高级加壳程序所利用。举个例子:
壳程序在到达oep之前,劫持对IAT中某dll API函数dllX!API_X的jmp [RVA_IAT_DLLX_APIX]到壳程序段的私有程序funcKe。但是在funcKe中重新生成要jmp的目标地址ptrTarget。此时ptrTarget1=
[RVA_IAT_DLLX_APIX]
。在此时简单脱壳的话,
jmp [RVA_IAT_DLLX_APIX] 将永远被保持为被劫持状态,并且指向固定的虚拟地址ptrTarget1。脱壳之后,当重新运行时,dllX的基地址发生了变化,由于壳程序不再参与初始化,原来的ptrTarget1不再有效了。当程序调用
dllX!API_X时,就会进入funcKe,然后使用未修复的ptrTarget1,程序崩溃......
我第一次遇到这个case是在手动为某流行游戏脱壳的时候发现的。出于商业忌讳,名字我就不说了,但是我相信懂的人有共鸣。当我脱壳后运行游戏时直接crash,起初还怀疑是否脱壳的过程操作不当。后来调试一下脱壳后的程序,发现部分jmp [RVA_IAT]被劫持到了壳程序段才恍然大悟。那段壳程序由几百行混淆汇编代码构成,当时为了确定问题,不得不从最后一行反向阅读汇编代码,分析栈数据的来源。壳程序的一种典型做法是:
原始IAT中转 jmp [RVA_IAT]----->call funcKe
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课