前言
本文主要说明新年快乐的解题过程和相关思路
新年快乐
将下载得到的新年快乐.exe
放到exeinfoPE中查看,发现其加了UPX的壳
那么这里尝试手动脱壳,将exe拖入OllyICE中,发现其停留在了0040E2F0
的位置,对应的汇编指令是pushad,那么这里我就根据esp堆栈平衡来寻找OEP
按快捷键F7前进一步,发现此时只有ESP和EIP的内容有变化
这里就选中ESP的内容0060FF58
,右键选择数据窗口中跟随
接着在左下角的数据窗口中可以看见首行的地址为0060FF58
,选中任意的内容,右键设置一个硬件访问断点
可以在菜单栏的调试中查看已设置的硬件断点
接着按快捷键F9直接去到硬件断点的位置,可以看到停在了004E486
,对应的汇编代码为lea eax, dword ptr [esp-80]
,并且发现上一条汇编指令为popad
那么根据ESP堆栈平衡,大概可以猜出OEP的地址为下面第一条JMP指令所指定的地址,即00401280
,那么就在其上一行设置一个断点(快捷键F2),并按F9运行到断点处
再F7往下运行,跳到00401280
,并且使用OllyDump插件进行程序入口点的修改,点击Dump转储为一个PE文件
将转储的文件中IDA打开,找到main函数,发现是字符串比较,那么flag的值应该就是flag{HappyNewYear!}
相关知识点
1.壳
壳可以理解为对原始的软件所加的一层包装,大致可以将其分为两类:压缩壳和加密壳
压缩壳:对原始软件进行了压缩,在添加压缩壳后软件的体积会减小,在软件运行的时候再进行解压
加密壳:对原始软件进行了加密,能够起到保护的作用,在软件运行的时候再进行解密
无论是哪种壳,在运行时都是先运行壳相关的代码,将原始软件解压缩或者解密后,再跳到软件的入口点执行
2.ESP堆栈平衡原理
我对于ESP堆栈平衡原理的理解是,在程序运行时,对于某个函数的调用,对应汇编指令为call
,在调用前需要将函数的返回地址先压入栈中,此时ESP会改变,那么当这个函数执行完成,即将返回的时候,对应的汇编指令为ret
,会返回到函数调用前所压入的那个返回地址,此时ESP也会改变,并且其值与函数调用前的值相同,这个就称作ESP堆栈平衡。
那么加壳程序的运行应该也是类似,在程序装载在内存后,壳相关代码运行前,寄存器会有一些初始值,那么在壳代码进行解压缩或者解密的时候,可能会改变某些寄存器的值,那么这时就需要用到PUSHAD
,其作用是按照 EAX、ECX、EDX、EBX、ESP(执行 PUSHAD 之前的值)、EBP、ESI 和 EDI 的顺序,将所有 32 位通用寄存器压入堆栈,那么在PUSHAD后,ESP的值会改变,而与之对应的POPAD
就是将上述寄存器的值弹出堆栈,ESP也会改变,并且与PUSHAD之前的值相同。所以,在看见POPAD指令的时候,说明壳相关代码应该是执行完成了,那距离原程序的入口就不远了。
3.硬件断点
硬件断点分为硬件访问断点、硬件写入断点、硬件执行断点,主要使用调试寄存器
其中DR0-DR3 负责存储硬件断点的内存地址,所以最多只能同时使用 4 个硬件断点
那么在本题中,我设置硬件断点的位置为0060FF58
,由于栈空间是往低地址增长的,当回到0060FF58
这个地址的时候,一般都是壳相关代码执行完,准备执行原程序代码的时候。在执行POPAD前,ESP的值为0060FF58
,执行POPAD的时候会修改ESP的值,会访问到0060FF58
这个地址,所以最终停在了POPAD命令的下一行