攻防世界 RE csaw2013reversing2

攻防世界官网: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注入分析便能直接调用该函数,而不需要我们费力的去分离函数。