前几天,参加了2022网鼎杯青龙组的比赛,第一道逆向考察点是upx脱壳,然而由于好久没有做过upx相关的题目了,有些生疏了,修了半天环境竟然都没有发现原来是特征值被篡改了,这提醒我,是时候该回头总结一下upx类题目了
0x00 前言
前几天,参加了2022网鼎杯青龙组的比赛,第一道逆向考察点是upx脱壳,然而由于好久没有做过upx相关的题目了,有些生疏了,修了半天环境竟然都没有发现原来是特征值被篡改了,这提醒我,是时候该回头总结一下upx类题目了
0x01 upx ctf normal题型
能用upx -d直接脱掉的题目就不说了
目前比赛中流行的upx类题目解法主要包括两种,第一种、工具受阻直接采取手工脱壳的方法(压缩壳甚至可以直接带壳跑,不会损坏文件内容)第二种针对篡改特征值类防脱壳,可以使用二进制文本工具修复特征值。此外,我们再研究以下upx各种防脱壳的办法(以备以后出题 huohai 大家(雾))
0x02 修复特征值类
[2022网鼎杯青龙组]fakeshell
拿到exe,查壳发现UPX壳 使用upx -d脱壳失败。当时比赛想带壳跑,但是一直提示缺少两个dll文件(后来发现是vs2010没装全的原因)但是其实这个提示误导了我,导致我没有看到很明显的upx特征值被修改了
010 editor打开源文件,发现两处被篡改的特征值 将其修复为UPX0,UPX1
修复后就可以成功用upx -d脱壳了
ida分析,发现了两个处理:
很简单的逻辑加密,写脚本得到flag即可
a=
[0x4B,0x48,0x79,0x13,0x45,0x30,0x5C,0x49,0x5A,0x79,0x13,0x70,0x6D,0x78,0x13,0x6F,0x48,0x5D,0x64,0x64]
flag=""
for i in range(len(a)):
a[i]=(a[i]^0x50)-10
for i in range(len(a)):
flag+=chr(a[i]^0x66)
print(flag)
[HGAME2022 WEEK2] upx magic
再来趁热打铁回顾一道 2022 年初HGAME 的一道同类型的题目,,不过这题是改变了upx加壳后的magic 因此直接查壳会显示没有加壳,比网鼎杯这道题更加高明。
程序放入010editor中打开 搜索upx,我们可以看到原本的特征值upx!被篡改成了upx? 因此我们把每一处修复回来就可以了(55 50 58 35 改为 55 50 58 21)
成功脱壳
ida打开分析,发现是crc16加密
写脚本爆破flag
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
int main()
{
int cipher[] = {0x8d68, 0x9d49, 0x2a12, 0xab1a, 0xcbdc, 0xb92b, 0x2e32,
0x9f59, 0xddcd, 0x9d49, 0xa90a, 0xe70, 0xf5cf, 0xa50, 0x5af5, 0xff9f, 0x9f59,
0xbd0b, 0x58e5, 0x3823, 0xbf1b, 0x78a7, 0xab1a, 0x48c4, 0xa90a, 0x2c22, 0x9f59,
0x5cc5, 0x5ed5, 0x78a7, 0x2672, 0x5695};
for(int k=0;k<32;k++)
{
for(int i=0x2e;i<0x7f;i++)
{
unsigned int crc =i;
crc<<=8;
for(int j=0;j<8;j++)
{
if((crc & 0x8000)!=0)
{
crc<<=1;
crc=crc^0x1021;
}
else
{
crc<<=1;
}
}
if(cipher[k] == (crc&0xffff))
{
printf("%c",i);
break;
}
}
}
system("pause");
return 0;
}
0x03 手动脱壳
这里介绍最常用的两种方法,足以应对ctf题目。采用的样例文件来自吾爱破解网站
方法一:单步跟踪法
接着用od载入程序,载入后进行调试。这里介绍
f8(快捷键) 为单步执行。单步跟踪的脱壳原则是允许向下的跳转不执行向上的跳转。
那当我们遇到向上的跳转时怎么办呢。有两种办法:
第一种,选中跳转命令下一行,右击->断点->运行到选定位置
第二种 选中跳转命令下一行,右击->断点->切换 运行。再点切换取消断点
一般采用第一种方法(快捷键f4)
向下的跳转如下图:红色线连接的箭头向下,这时候我们直接单步执行(f8)即可
向上的跳转如下图,根据我们之前说的脱壳原则,不允许步入向上的跳转,选中该语句下一行 运行到此处即可(f4)
就这样按部就班的进行动调即可,但是还有一些特殊情况需要规避,例如下图
按照常理,面对向上的跳转,我们应该f4进跳转语句的下一行,但是我们发现下一行仍然是一个跳转语句,所以我们选择运行到再下一行也就是mov命令行。
遇到的下一个特殊情况如下图所示,jmp命令的下一行为call命令,采用同样的方法,执行到再下一行popad行
继续单步,步入到如下命令 目前的地址范围为0057开头,而该语句直接跳转到0047开头,如此大范围的跳转,一般预示着动调的结束,我们将要进入真正的入口点(OEP)
f8单步跳转进入真正的入口点
右击该语句,使用olldump脱壳,
方法二:ESP定律法
名词定义:
esp:一个寄存器
esp定律:ESP定律又称堆栈平衡定律,是应用频率最高的脱壳方法之一 ,不论是新手还是老手都经常用到。
原理:堆栈平衡
由于在程序自解密或者自解压过程中, 多数壳会先将当前寄存器状态压栈, 如使用pushad
, 而在解压结束后, 会将之前的寄存器值出栈, 如使用popad
. 因此在寄存器出栈时, 往往程序代码被恢复, 此时硬件断点触发(这就是我们要下硬件断点的原因),然后在程序当前位置, 只需要一些单步操作, 就会到达正确的OEP位置.
操作步骤
在pushad处(程序打开处f8单步执行。发现只有esp的值发生变化)右击esp->在数据窗口中跟随。在esp地址处下硬件访问断点。
单步运行后也可以使用commend命令寻址下断点
dd XXXXX(esp地址) hr XXXXX
之后点击下图圈出部分运行停止到jmp语句。打开调试,删除硬件断点后,稍作跟进即可找到程序真正的oep
找到真正入口点之后就可以愉快的脱壳了,该方法适用于几乎全部的压缩壳以及早期的加密壳
dump程序
在找到程序 OEP 后, 我们需要将程序 dump 出来, 并重建IAT
. IAT
全名是Import Address Table
, 表项指向函数实际地址.
比如如下, 我们找到了 OEP, 到达了程序的真正入口点. 我们这时就需要将程序 dump 出来. 我们右键, 选择"用OllyDump脱壳调试进程"
主要就是看看入口点地址
有没有选对. 然后取消勾选重建输入表
.
尝试来运行一下脱壳出的文件dump1,发现运行错误,这是由于脱壳过程中程序的IAT出现了问题,接下来我们来重建IAT
打开ImportREC
, 选择一个正在运行的进程原版.exe
(原版.exe
是我在 OD 中正在调试的进程, OD 中的EIP
正处在OEP
位置, 在用Ollydump
之后不要关闭这个进程哦.) Ollydbg 里我们知道程序目前在的入口点是004022CA2, 而镜像基址是
00400000`
因此我们这里需要填写OEP
是00022CA2
点击自动获取iat接下来可能会弹出提示框 "发现可能是原 IAT 地址"点击确定再点击获取输入表
IAT表修复完成后,最后点击转储到文件完成iat表的修复
UPX 防脱壳进阶技术研究
目前ctf 中大多数upx题型还是处在不能脱壳但是可以识别upx的阶段,接下来我们学习一些方法让查壳程序无法识别出upx壳的方法。
去掉UPX特征码
查阅资料我们了解到upx的特征码是如下内容
特征码1:60 BE ?? ?? ?? 00 8D BE ?? ?? ?? FF
特征码2:60 BE ?? ?? ?? ?? 8D BE ?? ?? ?? ?? 57 EB 0B 90 8A 06 46 88 07 47 01 DB 75 ?? 8B 1E 83 ?? ?? 11 DB 72 ?? B8 01 00 00 00 01 DB 75
特征码3:55 FF 96 ?? ?? ?? ?? 09 C0 74 07 89 03 83 C3 04 EB ?? FF 96 ?? ?? ?? ?? 8B AE ?? ?? ?? ?? 8D BE 00 F0 FF FF BB 00 10 00 00 50 54 6A 04 53 57 FF D5 8D 87 ?? ?? 00 00 80 20 7F 80 60 28 7F 58 50 54 50 53 57 FF D5 58 61 8D 44 24 80 6A 00 39 C4 75 FA 83 EC 80
随便找了一个upx加密的文件 放到od中跑起来,来研究如何对抗这三段特征码
对抗特征码1
010editor 找到对应位置 添加混淆代码
60 pushad
90 nop
BE 00004100 mov esi,packed.00410000
8DBE 0010FFFF lea edi,dword ptr d[esi+FFFF1000]
57 push edi
EB 01 jmp short packed.00418121
90 nop
8A06mov al,byte ptr ds:[esi]
可以看到我们混淆特征码1之后查壳程序已经验证不出upx壳了,但是还是能看到特征值的,无法完全混淆。
对抗特征码2
修改前:
0041813172 ED jb short packed.00418120
00418133B8 01000000 mov eax,1
0041813801DBadd ebx,ebx
修改后:
0041829539C4cmp esp,eax
00418297 ^ 0F85 F6FFFFFF jnz packed.00418293
0041829D83EC 80 sub esp,-80
004182A0 - E9 7EDDFEFF jmp packed.00406023
对抗特征码3:
修改前:
0041829539C4cmp esp,eax
00418297 ^ 75 FA jnz short packed.00418293
0041829983EC 80 sub esp,-80
0041829C - E9 82DDFEFF jmp packed.00406023
修改后:
0041829539C4cmp esp,eax
00418297 ^ 0F85 F6FFFFFF jnz packed.00418293
0041829D83EC 80 sub esp,-80
004182A0 - E9 7EDDFEFF jmp packed.00406023
其实就是把jnz short XXX改成jnz long XXX
加垃圾区段
发现之前的混淆并不能使得exeinfo完全识别不出来 exeinfo牛逼 那我们再学习一些别的办法,比如再在文件末尾添加512个00的脏数据
PE头移动
原来0xec、后来的0x54、0x55处是PE头大小,0x3c是PE头起始位置,所有地址反着写.
修改后
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ?
00000010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ? @
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000030 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 @
00000040 50 45 00 00 4C 01 03 00 9A DA 22 4D 00 00 00 00 PE L 氌"M
00000050 00 00 00 00 78 01 0F 01 0B 01 06 00 00 90 00 00 x?
00000060 00 20 00 00 00 F0 00 00 10 81 01 00 00 00 01 00? ?
00000070 00 90 01 00 00 00 40 00 00 10 00 00 00 02 00 00? @
00000080 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00
00000090 00 B0 01 00 00 10 00 00 00 00 00 00 02 00 00 00?
000000A0 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00
000000B0 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00
000000C0 D0 A5 01 00 04 02 00 00 00 90 01 00 D0 15 00 00 啸 ? ?
000000D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000001A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000001B0 00 00 00 00 00 00 00 00
0x04 后记
目前能够总结到的关于upx壳的内容就这么多,ctf中脱壳也不作为逆向的主要得分点,但是还是要尽量掌握全面常见的方式方法,系统梳理,比赛的时候才不容易失误,这次比赛的经历也算是给我长了一个教训,知己知彼才能百战不殆
0x05 参考链接
- 本文作者: 绿冰壶
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/1868
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!