攻防世界官网:https://adworld.xctf.org.cn/ 。
源程序、IDA分析文件、hook.dll下载: http://file.eonew.cn/ctf/re/csaw2013reversing2.zip 。
一道简单的逆向题,虽然有一个坑,可以通过dll注入来获取flag。
目录
观察
用IDA反汇编查看,程序还是很简单的。
主程序
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // ecx
CHAR *lpMem; // [esp+8h] [ebp-Ch]
HANDLE hHeap; // [esp+10h] [ebp-4h]
hHeap = HeapCreate(0x40000u, 0, 0);
lpMem = (CHAR *)HeapAlloc(hHeap, 8u, MaxCount + 1);
memcpy_s(lpMem, MaxCount, &unk_409B10, MaxCount);
if ( sub_40102A() || IsDebuggerPresent() )
{
__debugbreak();
sub_401000(v3 + 4, (int)lpMem);
ExitProcess(0xFFFFFFFF);
}
MessageBoxA(0, lpMem + 1, "Flag", 2u);
HeapFree(hHeap, 0, lpMem);
HeapDestroy(hHeap);
ExitProcess(0);
}
直接运行发现得到的是一堆乱码,以为是编码不正确,所以就用IDA将0x409B10
的提取出来,
Python>get_bytes(0x409B10, 36).encode('hex')
bbcca0bcdcd1beb8cdcfbeaed2c4ab82d2d993b3d4de93a9d3cbb882d3cbbeb99ad7ccdd
最后发现什么编码都不管用,查看了官方的writeup后才知道原来是加密过了,这个是真的有点坑,加密函数就是sub_401000
,这里我不用官方的做法,而是进行dll注入
。当然把这段代码提取出来进行解密也是没有问题的。
sub_401000
void __fastcall sub_401000(int a1, int a2)
{
int v2; // esi
unsigned int v3; // eax
unsigned int v4; // ecx
unsigned int v5; // eax
v2 = dword_409B38;
v3 = a2 + 1 + strlen((const char *)(a2 + 1)) + 1;
v4 = 0;
v5 = ((v3 - (a2 + 2)) >> 2) + 1;
if ( v5 )
{
do
*(_DWORD *)(a2 + 4 * v4++) ^= v2;
while ( v4 < v5 );
}
}
要特别注意一点,__fastcall:从左开始不大于4字节的参数放入CPU的ECX和EDX寄存器,其余参数从右向左入栈。
所以编写hook的时候要注意一下
hook.c
#include <windows.h>
#include <stdio.h>
typedef void (* FUNC)();
unsigned char bin[] = {
0xbb, 0xcc, 0xa0, 0xbc, 0xdc, 0xd1, 0xbe, 0xb8, 0xcd, 0xcf, 0xbe, 0xae,
0xd2, 0xc4, 0xab, 0x82, 0xd2, 0xd9, 0x93, 0xb3, 0xd4, 0xde, 0x93, 0xa9,
0xd3, 0xcb, 0xb8, 0x82, 0xd3, 0xcb, 0xbe, 0xb9, 0x9a, 0xd7, 0xcc, 0xdd,
0x00
};
unsigned int bin_len = 37;
void hook()
{
FUNC ptr_func;
int i;
// 获得基地址
uintptr_t image_base = (uintptr_t)GetModuleHandle(NULL);
ptr_func = (FUNC)(image_base + 0x1000);
// 传参
_asm{
lea edx, bin
}
// 利用解密函数进行解密
ptr_func();
fwrite(bin, 1, bin_len, stdout);
puts("");
}
BOOL WINAPI DllMain(
_In_ HINSTANCE hinstDLL, // 指向自身的句柄
_In_ DWORD fdwReason, // 调用原因
_In_ LPVOID lpvReserved // 隐式加载和显式加载
)
{
puts("hook start");
hook();
return TRUE;
}
不知道为什么,编译器不能正确编译__fastcall的函数,所以这里我直接嵌入汇编进行实现。
运行实例
D:\test>csaw2013reversing2.exe
hook start
flag{reversing_is_not_that_hard!}
总结
dll注入还是很有必要的,特别是遇到一些难以分离的函数的话,dll注入分析便能直接调用该函数,而不需要我们费力的去分离函数。