内容纲要

脱壳

PUSHAD:压入32位寄存器,其入栈顺序是:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI.

OEP:(Original Entry Point),程序的入口点。软件加壳一般隐藏了程序真实的OEP(或者用了假的OEP), 我们需要寻找程序真正的OEP,才可以完成脱壳。

IAT:(Import Address Table),导入地址表。由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL中。当PE文件被装入内存的时候,Windows装载器才将DLL 装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成。其中导入地址表就指示函数实际地址。 多数加壳软件在运行时会重建导入地址表,因此获取加壳程序正确的导入地址表也是手动脱壳操作中的一个关键问题。

脱壳方法

1 单步跟踪法

单步跟踪法的原理就是通过Ollydbg的单步(F8)、单步进入(F7)和运行到(F4)功能,完整走过程序的自脱壳过程,跳过一些循环恢复代码的片段,并用单步进入确保程序不会略过OEP。这样可以在软件自动脱壳模块运行完毕后,到达OEP,并dump程序。

2 ESP定律法

ESP定律法是脱壳的利器,是应用频率最高的脱壳方法之一。

ESP定律的原理在于程序中堆栈平衡的合理利用。由于在程序自解密或者自解压过程中,不少壳会先将当前寄存器内容压栈,如使用pushad,在解压结束后,会将之前的寄存器值出栈,如使用popad。因此在寄存器出栈时,往往程序代码被自动恢复,此时硬件断点触发。然后在程序当前位置,只需要少许单步跟踪,就很容易到达正确的OEP位置。

3 内存镜像法(二次断点法)

内存镜像法是在加壳程序被加载时,通过OD的ALT+M快捷键,进入到程序虚拟内存区段。然后通过加两次内存一次性断点,到达程序正确OEP的位置。

内存镜像法的原理在于对于程序资源段和代码段下断点,一般程序自解压或者自解密时,会首先访问资源段获取所需资源,然后在自动脱壳完成后,转回程序代码段。这时候下内存一次性断点,程序就会停在OEP处。

4 一步到达OEP

所谓的一步到达OEP的脱壳方法,是根据所脱壳的特征,寻找其距离OEP最近的一处汇编指令,然后下int3断点,在程序走到OEP的时候dump程序。 如一些压缩壳往往popad指令距离OEP或者Magic Jump特别近,因此使用Ollydbg的搜索功能,可以搜索壳的特征汇编代码,达到一步断点到达OEP的效果。

5 最后一次异常法

最后一次异常法的原理是,程序在自解压或自解密过程中,可能会触发无数次的异常。如果能定位到最后一次程序异常的位置,可能就会很接近自动脱壳完成位置。现在最后一次异常法脱壳可以利用Ollydbg的异常计数器插件,先记录异常数目,然后重新载入,自动停在最后一次异常处。

6 模拟跟踪法

模拟跟踪法的原理就是使用Ollydbg下条件断点,SFX相当于是一个自解压段,在自解压段结束时(eip的值转到代码段时),已经距离OEP很近,但是这种跟踪方法会比较耗时。

7“SFX”法

“SFX”法利用了Ollydbg自带的OEP寻找功能,可以选择直接让程序停在OD找到的OEP处,此时自解压已经完成,可以直接dump程序。

实例

oj_Evil.exe——ESP定律

看到pushad在栈下硬件访问断点,f9,找到popad,在发生跳转后dump出文件

image-20210427175841429

image-20210427184830313

攻防世界_easyre-153——手动脱upx

拖入ida

image-20210509025122993

sub_8048294函数;

image-20210509025147285

看到了int 80,get pid

image-20210509025811048

汇编

image-20210509032815608

image-20210509032414686

ebx:

从第一个int 80得到eax=[pid]

将eax赋给ebp+var78

ebp+var4c=/proc/

将eax=[pid]入栈(var46),同时赋给ebp+var68,得到/proc/[pid]

加/,得到/proc/[pid]/

ecx->68->eax->46,exe赋给ecx,得到/proc/[pid]/exe

ebx=/proc/[pid]/exe的地址

这里和我之前学栈溢出的知识点串起来了

image-20210509032531249

系统调用号

而proc/[pid]/exe是程序进程的绝对地址

开始动调

image-20210513080505792

去查找字符串

image-20210513075322296

去目录下看看

image-20210513075421352

image-20210513081343991

然后这个时候的fd文件里只有012

接着F8

image-20210513081457319

image-20210513081532247

当执行完两个循环后,发现

image-20210513081606739

多了一个AAAAAA的文件,而且权限为写+执行

一直动调会发现经过一次判断跳到

image-20210513083134494

查看上一步

image-20210513083215662

esi+4

image-20210513083230951

猜测是运行upx壳

所以修改eip进入该判断内,判断的是v38==! (一个!和三个空格)且off==0

image-20210513083520196

执行if(sys_close*2)先关闭两个文件

image-20210513084021170

执行sys_open(file)打开file也就是之前创建的AAAA

image-20210513084141152

可以看到这个时候AAAA的权限变成r+x,而这时会发现我们的动调文件里有,复制出来

image-20210513085507985

接着执行到unlink

image-20210513084850750

同时AAAA也不在动调文件了

运行之前复制的文件

image-20210513085705339

用ida打开

image-20210513085813396

int __cdecl lol(_BYTE *a1)
{
  char v2; // [esp+15h] [ebp-13h]
  char v3; // [esp+16h] [ebp-12h]
  char v4; // [esp+17h] [ebp-11h]
  char v5; // [esp+18h] [ebp-10h]
  char v6; // [esp+19h] [ebp-Fh]
  char v7; // [esp+1Ah] [ebp-Eh]
  char v8; // [esp+1Bh] [ebp-Dh]

  v2 = 2 * a1[1];
  v3 = a1[4] + a1[5];
  v4 = a1[8] + a1[9];
  v5 = 2 * a1[12];
  v6 = a1[18] + a1[17];
  v7 = a1[10] + a1[21];
  v8 = a1[9] + a1[25];
  return printf("flag_is_not_here");
}
//RCTF{rhelheg}

写脚本,得到flag

附上大佬的wp