charryyu_ych 发表于 2017-12-25 16:11

IAT劫持在加壳中的运用

本帖最后由 charryyu_ych 于 2017-12-25 16:21 编辑

    来吾爱有一段时间了,看过一些前辈们的文章,受益良多。今天也准备分享一点干货,以示回馈。
    关于加脱壳、PE、IAT的基本概念这里就不重复了,有需要的可以看看其他高手分析的文章。

    程序的一大特点就是具有确定性。只要两套程序在某个时间点具有相同状态,在同样的外部输入下,它们将会得到同样的运行结果。而这就是加壳脱壳的基本前提。我们知道,脱壳的基本流程是:dump、寻找oep、修复IAT(严格来说是IT)。理论上讲,在oep处脱壳后修复了IT的程序,与带壳达到oep的程序基本具有相同的状态,因而能够向后正常运行。但注意是基本相同,也就是说一旦存在一点不同,脱壳就是不彻底的,还需要做额外的修补。

    我们来仔细回顾一下脱壳的过程。假定在第n次运行带壳程序到oep时脱壳,我们将此时的程序状态命名为State。以后第m次启动脱壳后的程序到达oep的状态命名为State。思考:State与State是否都能正常运行?它们有什么区别?
      State:第n次运行原始程序时在oep的程序状态,壳程序参与了初始化,一定能正常运行。
      State:第n次dump的静态程序状态+第m次动态加载到达oep之后的状态,壳程序未参与初始化,可能无法正常运行。
      区别1:壳程序是否参与了oep之前的初始化。
      区别2:不同次启动时的各个dll的基地址不同。
    为什么说State可能无法正常运行呢?假定其中包含的State的部分静态信息是需要被动态修复的,那么它就无法运行。而这个就可以被某些高级加壳程序所利用。举个例子:
    壳程序在到达oep之前,劫持对IAT中某dll API函数dllX!API_X的jmp 到壳程序段的私有程序funcKe。但是在funcKe中重新生成要jmp的目标地址ptrTarget。此时ptrTarget1= 。在此时简单脱壳的话, jmp 将永远被保持为被劫持状态,并且指向固定的虚拟地址ptrTarget1。脱壳之后,当重新运行时,dllX的基地址发生了变化,由于壳程序不再参与初始化,原来的ptrTarget1不再有效了。当程序调用 dllX!API_X时,就会进入funcKe,然后使用未修复的ptrTarget1,程序崩溃......


    我第一次遇到这个case是在手动为某流行游戏脱壳的时候发现的。出于商业忌讳,名字我就不说了,但是我相信懂的人有共鸣。当我脱壳后运行游戏时直接crash,起初还怀疑是否脱壳的过程操作不当。后来调试一下脱壳后的程序,发现部分jmp 被劫持到了壳程序段才恍然大悟。那段壳程序由几百行混淆汇编代码构成,当时为了确定问题,不得不从最后一行反向阅读汇编代码,分析栈数据的来源。壳程序的一种典型做法是:
      原始IAT中转 jmp ----->call funcKe
      funcKe:
      address         call address+1
      address+1       pop eax         //利用栈上的ret拿到了call eip+1这句代码所处的虚拟地址address
         .........
      address+x       sub eax,n      //经过中间运算得到API地址
                               push eax         //将API地址放到栈顶
                               ret                   //利用ret指令实现对API的调用。
   
    领会了这个要领之后,对抗思路也就很清晰了。先按照常规流程进行脱壳,脱壳之后再进行相应处理。
      处理方式1:恢复被劫持代码。将中转jmp的call funcKe恢复为jmp 或者在funcKe的首地址处理也可以。
      处理方式2:dll固定基地址技术。记住脱壳时的每个exe、dll的基地址,只要保证每次程序启动时仍然使用相同的基地址即可。本文只是讲壳相关的,该技术不在此论述。

    很高兴能与大家一起学习交流,写得不好的地方还望大家见谅,欢迎批评指正。

Poner 发表于 2017-12-25 16:53

转载的嘛?貌似看过有一模一样的

charryyu_ych 发表于 2017-12-25 18:41

Poner 发表于 2017-12-25 16:53
转载的嘛?貌似看过有一模一样的

在看雪上发过,自己亲手写的。

newchange452pj 发表于 2017-12-25 23:15

谢谢分享

whatdos 发表于 2017-12-26 11:03

谢谢分享

Sound 发表于 2017-12-26 14:14

不错 通俗易懂

charryyu_ych 发表于 2017-12-26 17:51

谢谢大家的支持。
补充一下,大多数情况下脱壳后就可以进行静态分析,但仍需要动态调试的配合,因此需要让它能正常运行。如果为了方便,修复少数几个IAT劫持就可以让程序跑起来了。这是因为一般而言,部分主要的dll加载地址基本都是在固定位置的。但是要注意一点:kernel32.dll、user32.dll、gdi32.dll等主要的dll跟系统的启动有关。因此如果要偷懒,就尽量完成分析之后再重启电脑。否则你会发现有一大堆的kernel32 API需要修复,到那时比起修复的工作量,还不如重新脱壳。

xiaohong 发表于 2018-1-20 08:28

谢谢分享{:1_918:}

欧琪茵 发表于 2018-2-7 13:16

谢谢分享!
页: [1]
查看完整版本: IAT劫持在加壳中的运用