0%

手脱 Upx 壳

记录学习手脱 Upx 壳的过程,分 PE 篇和 ELF 篇。

手动脱壳的原理

在加壳的程序执行时,壳会先于原程序执行,将原程序恢复。通过动态调试的方式将程序运行到壳执行结束的时刻,此时内存中已有恢复了的原程序,将这部分内存复制一份,就得到了脱完壳的程序。

PE 篇

例题:BUUCTF 新年快乐

需要用到工具 x32dbg,Exeinfo PE。

先用 Exeinfo 查壳,发现是 upx。

然后使用 x32dbg 打开文件:

根据堆栈平衡原理脱壳。

堆栈平衡原理:

upx 壳运行时,会先将所有寄存器入栈(pushad),根据栈先进后出的特性,这些寄存器会在壳运行结束处附近出栈,所以我们只需找到程序开始的 pushad 指令,执行完该指令后对 esp 指向的内存地址打一个断点,就能快速找到 upx 壳的结束部分。

x32dbg 自动发现了 pushad,可以在断点栏看到:

运行程序,直到程序在 pushad 处停下。

执行 pushad,然后转到对 esp 指向的地址打断点:

运行,果然在 popad 执行后停下了:

到这里壳基本执行完了,下一个无条件跳转就会跳到原程序的入口。所以我们一路步过到那条 jmp,然后 dump 内存。

得到的程序已能用 IDA 正常反编译。

ELF 篇

例题:[GUET-CTF2019]re

elf 文件无法在 windows 下运行,故需要用 IDA 进行远程调试。

elf 文件同样可以用 Exeinfo 查壳。

懒得写了,这个讲得挺好的:运行时压缩壳 - IDA动调ELF脱壳

贴个关键脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <idc.idc>
#define PT_LOAD 1
#define PT_DYNAMIC 2
static main(void)
{
auto ImageBase,StartImg,EndImg;
auto e_phoff;
auto e_phnum,p_offset;
auto i,dumpfile;

// 基地址和起始地址,这里根据实际修改,一般不用改
ImageBase=0x400000;
StartImg=0x400000;
EndImg=0x0;

// 检查是不是elf文件
if (Dword(ImageBase)==0x7f454c46 || Dword(ImageBase)==0x464c457f )
{
if(dumpfile=fopen("D:\\dumpfile","wb"))//更改路径
{
// 计算程序头表的偏移和条目数量
e_phoff=ImageBase+Qword(ImageBase+0x20);
Message("e_phoff = 0x%x\n", e_phoff);
e_phnum=Word(ImageBase+0x38);
Message("e_phnum = 0x%x\n", e_phnum);

// 遍历程序头表中的每个条目
for(i=0;i<e_phnum;i++)
{
// 检查程序头条目的类型是否为加载段或动态加载信息段
if (Dword(e_phoff)==PT_LOAD || Dword(e_phoff)==PT_DYNAMIC)
{
// 段在文件中的偏移
p_offset=Qword(e_phoff+0x8);
// 段的起始地址
StartImg=Qword(e_phoff+0x10);
// 段的结束地址
EndImg=StartImg+Qword(e_phoff+0x28);
Message("start = 0x%x, end = 0x%x, offset = 0x%x\n", StartImg, EndImg, p_offset);
// 将段的内容保存到文件
dump(dumpfile,StartImg,EndImg,p_offset);
Message("dump segment %d ok.\n",i);
}
// 更新程序头表的偏移
e_phoff=e_phoff+0x38;
}
// 指向程序头表的偏移位置
fseek(dumpfile,0x3c,0);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
// 存储着程序头表中每个条目的大小
fseek(dumpfile,0x28,0);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fputc(0x00,dumpfile);
fclose(dumpfile);
} else Message("dump err.");
}
}
static dump(dumpfile,startimg,endimg,offset)
{
auto i;
auto size;
size=endimg-startimg;
fseek(dumpfile,offset,0);
for ( i=0; i < size; i=i+1 )
fputc(Byte(startimg+i),dumpfile);
}