UPX逆向分析
UPX原理
UPX是一种流行的开源可执行文件压缩工具,它可以减小可执行文件的体积,并且可以提高程序的启动速度。UPX实现可执行文件的压缩是通过加壳技术实现的。
UPX的加壳原理大致如下:
1. UPX首先读取可执行文件的头部信息,包括文件格式、程序头、节表等信息。
2. 然后,UPX在可执行文件中添加一个新的“壳”程序,通常是一个简单的解压缩程序。这个壳程序会被添加到文件的头部或尾部,具体位置取决于可执行文件格式和UPX的配置。
3. 接下来,UPX将原始可执行文件的内容压缩,并将压缩后的数据添加到壳程序的后面。
4. 最后,UPX修改可执行文件的程序头和节表等其他信息,使其指向壳程序的入口点和压缩数据的位置。
当用户运行经过UPX加壳的可执行文件时,
会先执行壳程序,壳程序会将压缩的数据解压缩并加载到内存中,
然后跳转到原始可执行文件的入口点开始执行。
由于压缩数据的存在,
可执行文件的体积得到了缩小,从而减少了磁盘空间的使用和网络传输的时间,
同时也提高了程序的启动速度。
总之,UPX的加壳原理是在原始可执行文件中添加一个解压缩壳程序,并将原始程序的内容压缩后添加到壳程序后面,从而实现可执行文件的压缩和解压缩。
版本:upx-3.96-win64 可以去我库里找找 附带IDA分析 UPX
虽然 有源码 但是想练一练逆向 所以就有了这篇文章
PE分析
居然自己还套一个壳,改个名 脱了upx -d a.exe
动态静态分析
带参数的动调 file->改变命令行 输入参数
IDA跑 看到入口main函数 FCG还是挺长的 工作量估计有点大
通过字符串查找packer函数
开始运行后,首先会打印“Ultimate Packer for eXecutables“,然后就开始根据你的命令参数执行函数
字符列表找到字符串,交叉引用 有三个函数进行了调用
一个一个看一下
第一个sub_417930
没有参数 输出 使用手册
第二个sub_417DC0
输出一些upx的项目说明
第三个sub_469900
输出upx 文件的状态
这三个函数都是通过sub_41DE80
这里主要是去分析他的upx 加壳和脱壳(解压缩) 所以着重分析sub_469900
sub_469900
一个个函数跟进简单分析结果如下
分为三个部分:
UPX程序部分
checkupx函数
打印部分
sub_417870函数 打印 Ultimate Packer for eXecutables ...
sub_4669E0函数 打印 File size Ratio Format Name ...
检验参数部分
判断是要脱壳还是加壳 还是其他操作
packer--sub_469340(v8, Destination); 里面包括加壳,脱壳以及多参数运行
根据参数输出结果
switch...case
2 输出Unpacked
3 输出Tested
4 输出分割符
5 ret
先分析 加壳–sub_469340(v8, Destination);
sub_469340
进入函数以后 先检验 命令行的第二个参数指向的文件
通过stat库函数获取文件属性
是否是一个非目录文件 (v16.st_mode & 0xF000) == 0x4000
是否是一个有效文件 (v16.st_mode & 0xF000) != 0x8000
文件是否为空 v16.st_size <= 0
文件是否过小 size<511
文件是否过大 size>0x30000000
文件是否可写(有没有写保护)
后面就是创建类了 里面的成员 动态得到大概
之后就又来到了参数判断
v22是临时文件类 创建了一个临时文件 xxx.upx
Destinationa 是一个嵌套类 里面有 参数类和文件类(xxx.exe)
v10 = *off_4D6100;
if ( *off_4D6100 == 1 ) // 这里保存参数个数 argc-1
{
sub_45D960(Destinationa, v22); // 加壳 只有一个参数 所以就是进行加壳操作
}
else
{
switch ( v10 )
{
case 2:
sub_45D9A0(Destinationa, v22); //这里进去看了一下 又和脱壳相关
break;
//后面都是多参运行 不是很关心 主要还是看加壳 脱壳
case 3:
sub_45D9E0(Destinationa);
break;
case 4:
sub_45DA10(Destinationa);
break;
case 5:
sub_45DA40(Destinationa);
break;
default:
sub_4D5118("invalid command");
}
}
终于找到了dopack函数了 – sub_45D960
dopack – sub_457BA0函数
1 | __int64 __fastcall sub_45D960(_QWORD *a1, __int64 a2) |
里面又调用了两个函数
第一个函数 最后调用了sub_45C2D0 里面有很多的if…else 分类 pack
第二个函数则是对返回的类操作了一下调用了里面的方法 感觉应该是 用作检查 更新的
这是我粗浅的判断 实际上 sub_45C2D0 这个函数使用来获取加壳文件的类型
而sub_457BA0才是dopack函数
直接来到
sub_457BA0 OD也跟进来
1 | __int64 __fastcall sub_457BA0(_QWORD *a1, __int64 a2) |
从IDA可以看出Pack函数是 间接调用
是虚函数 从OD来在IDA里找到这个虚函数
这里分成4个部分 对照源码分析中的Pack1、pack2、packe3、pack4
这里通过他的字符串做简单的识别
pack1
生成文件头DOS头+upx节表
pack2
压缩数据并添加到file中
pack3
添加loader函数 在UPX运行时解压缩程序
pack4
添加壳的头部
这里添加的内容如下(这是Linux系统的UPX源码):
1 | void PackUnix::writePackHeader(OutputFile *fo) |
这里主要是和脱壳相关,所以这里删除啥的没有影响,但是这是UPX的一个特征码 这里就是脱壳机关心的东西,通过删除这些东西,可以做到初步免查壳,exeinfo_PE就看不到UPX的版本信息了,但是仍能识别UPX
在运行的时候会生成两个临时文件 分别是xxx.upx和xxx.000
分别是 DOS头和DOS块 – xxx.upx
以及 添加了压缩数据 – xxx.000 (没有导入表)
总结一下UPX加壳流程
pack()函数为加壳函数,
分别调用pack1()进行头部的处理,对头部的section信息进行擦出,
pack2()对数据段进行压缩,
pack3()修改LOAD段,
pack4()则添加最后的壳的头部,并修改壳的section段。
绕过UPX查壳
一般市面上的查壳软件都是通过特征码进行识别的,虽然效率很高,但是 只要通过去除混淆特征码,那么就可以实现绕过查杀的目的。
特征码
就像PE文件都有MZ头一样,(MZ就算是PE文件的特征码)UPX也有自己的特征码
包括
- UPX节表名
1 | 通过学习PE文件结构我们可以知道, |
UPX头
1
这里是UPX头部,是通过Pack4()函数添加的,相关信息,各个字段所代表的属性 在上面已经列出
1
2
3ExeinfoPE就是通过这读取到的UPX版本信息,记录的都是和UPX和文件的基本信息,包括文件大小,压缩等级...
替换这些信息也不会对PE文件的运行造成影响
但ExeinfoPE获取到的UPX版本信息就会有错误,比如将版本字段改为6666但是仍然还有UPX壳的信息
来看下一个特征码
psuhad … 机器码(60 BE …)
这里的特征码是pushad的特征码,用作upx的loader
exeinfoPE就是通过查特征码表查的
改了这里以后 exeinfoPE还有是有疑似UPX的提示
还有什么捏 不到了
点一下Scan/t 提示 “3个节的结构 似乎是UPX”(口翻一下)
这里试一下再添加一个节
添加一个空节
1
2
3
4
5
6
7
8
9
10
11
12
13
14<1>判断节表区域是否有空闲区域添加一个节表.APEX
<2>在节表中新增一个成员
复制其中一个 比较省事 这里复制了upx1节
<3>修改PE头部中节的数量
标准PE头 3-》4
<4>修改SizeOfImage的大小
可选PE头 原有+内存偏移
<5>在原有的数据最后,新增一个节的数据(内存对齐的整数倍)
直接00
<6>修正新增节表的属性
//可以便增加边修改
需要修改的节表属性
VirtualAddress(在内存中的偏移) = 上一个节表的VirtualAddress+上一个节表的(Misc在内存中的大小(内存对齐整数倍))
PointerToRawData(在文件中的偏移)可以看到显示了4个节,他就不知道是啥保护了,但是还有提示了有壳 试一下把入口改了
在新增节的位置添加跳转代码 并修改OEP–》0x2f000
虽然还有是提示有壳保护 但是 点击Scan/t他分析不出来是UPX了
后面还有能咋弄捏?不到了QAQ
这里由于妹有改什么特别的东西直接ESP定律就能手脱捏
虽然PE文件还是有点乱(毕竟多了一个节) 但是 IDA雀氏能分析了
手动加壳
再套一层 把upx的loader代码加密了 这应该就看不出了叭
还是新增一个节 用来保存解密upx的代码 同时修改OEP
这里和上面的一样
加密oep代码
010Editor进行异或0x10叭
在apex节添加解密代码
1 | 0042F000 | 60 | pushad |保存一下现场 |
程序能跑再看一下 同样识别不出来UPX了
所以这也是一种绕过方法