前言
最近在准备CTF比赛,刷到一道非常经典的逆向题:攻防世界——csaw2013reversing2。这道题十分完美的用上了静态分析和动态分析的方法。在这里,我先总结一些啥是动态分析,然后再用动态分析与上个月的静态分析来解一道CTF逆向题。关于静态分析: chapter1:逆向工程之学习——静态分析
动态分析
什么是动态分析?
指程序运行的同时跟踪其行为的技术。其实类似于我们在一些IDE中进行代码调试时所用的“Debug”。
动态分析常用工具:OllyDbg
使用方法很简单:直接打开软件,把目标exe文件拖入即可。
我们关键是来看一下:OllyDbg的四个大黑框都是啥。我们主要把注意力放在左上角的框(反编译代码和二进制代码)和右上角的框(寄存器)。左上角的框就相当于你在IDE中需要进行Debug的一些代码,按F2设置断点,按F9执行程序/运行到断点处,F8单步调试。你可以看到图中00401388代表的是这个程序的物理地址,然后你会发现00401388是灰色背景,但是下面的都是黑色背景。那是因为程序正运行到此。
那么右上角的框,代表了一些变量值,这些值都存储再寄存器中(EAX , ECX …),下面还有一些标志位(C , P , A , Z),这些都可以在程序调试中,手动修改,目的是:实现一些违背源程序本意的操作。 比如:得到 Flag
但是,汇编代码冗长,你想快速地找到你想设置断点的程序位置并不容易。所以得结合上次的静态分析技术(忘了可以看我上一篇博客,不看也没关系,等下会在解题部分进行演示)。
如何调试
只要你会用主流的IDE比如Pycharm,VS,IDE,MATLAB 等。学会OllyDbg也不是难事!!!
其实我们只需要遵循三步走:
-
第一步设置断点并开始调试:F2 – > F9
-
第二步单步调试:F8
-
第三步观察寄存器,栈区域,必要时修改寄存器
总之就和你平时调试IDE一样简单!
开始解题
打开运行的话就是一段乱码。然后程序结束。下面开始进行正常的解题分析步骤
开始静态分析
- Step1:我们先把这个程序丢到PE中看一看是几位的,加没加壳!重点还是看加没加壳
- Step2:丢进IDA 进行静态分析,我们直接找Main函数
- Step3:按F5 对main 函数的汇编代码进行反汇编
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // ecx@1
LPVOID lpMem; // [sp+8h] [bp-Ch]@1
HANDLE hHeap; // [sp+10h] [bp-4h]@1
hHeap = HeapCreate(0x40000u, 0, 0);
lpMem = HeapAlloc(hHeap, 8u, MaxCount + 1);
memcpy_s(lpMem, MaxCount, &dword_409B10, MaxCount);
//注意下面的这个if 语句
if ( sub_40102A() || IsDebuggerPresent() )
{
__debugbreak();
sub_401000(v3 + 4, (int)lpMem);
ExitProcess(0xFFFFFFFF);
}
MessageBoxA(0, (LPCSTR)lpMem + 1, "Flag", 2u);
HeapFree(hHeap, 0, lpMem);
HeapDestroy(hHeap);
ExitProcess(0);
}
浏览完代码,初步断定,IpMem 中存储了Flag,但不太会走入if 里面的逻辑。
注意下这个sub_40102A() || IsDebuggerPresent()
sub_410102A 直接返回一个0 , IsDebuggerPresent() 如果你不是调试代码那么也是返回0.
所以,If中的逻辑很可以是对IPMem的一变换——得到Flag,所以正常的思路就是触发这个If内部逻辑。
但是这个IF里有个坑,就是你进去之后你就会立马结束Debug,这样就没法执行下面的变换操作了,所以这里正常的思路就是进入动态调试阶段,并且不要执行debugbreak()函数,这个是可以在OllyDbg中做到的。
但是这个IF还有个坑,就是他没有显示的函数了。也就是说,即使你求出了FLAG,也没法给你显示出来。
这个显示问题容易解决,因为如果你直接运行这个EXE程序,就会直接显示出一个乱码,所以我们只需要在最后让他跳到一个显示函数中即可。
IsDebuggerPresent()有两条路,红色代表NO逻辑,绿色代表Yes逻辑。绿色逻辑中含有MessageBoxA,这就是显示函数。所以常规思路就是让IsDebuggerPresent跳入loc_4010B9(物理地址:004010B9)。
好分析至此:基本上确定了解题思路:
1. 进入IF逻辑
2. 跳过debugbreak()
3. 显示IPMem内的信息
开始动态分析
- Step1:设置IsDebuggerPresent()为断点,运行至断点处
- Step:2 修改EAX寄存器的值为1,强制进入IF逻辑。(不知道为什么,我在调试中,isDebuggerPresent()返回的也是0)
- Step:3 修改判断逻辑,不要让他进入debuggerbreak()
修改je为Jne。观察左下角可以发现现在显示“跳转未实现”。相当于从“是”逻辑跳转改为“否”逻辑跳转。 - Step4:将00401B9A int3指令处修改为NOP。这里有个跳转,但是我们需要取消它。所以直接改为NOP。
- Step5: 调用完Call 之后,相当于进入了对IPMem的变换。但是后面就有个jmp,按照原程序的逻辑,这个jmp应该就对应着:ExitProcess(0xFFFFFFFF)。我们只需要让他跳转到MessageBoxA即可。上面已经分析除了他的地址(loc_4010B9(物理地址:004010B9)。)所以修改jmp指令为 004010B9
最终的Flag: {reversing_is_not_that_hard!}
总结
1.静态分析看整体
2.动态分析看细节
3.静动结合出奇迹