findkey write_up

没想到给小明神秘盒子的论坛高手居然是本次ctf的策划人之一,在比赛的时候,神秘盒子再一次出现,小明下意识的再次重复之前的操作,结果显而易见得失败了,赶紧帮小明解答这个加了点佐料的盒子。 注意:得到的 flag 请包上 flag{} 提交

最近闲来事贼多,做一题逆向暂且表示自己是个re手(文明六超好玩)

findkey1

打开一看,这不是微软的模板嘛,file选中可以下滑找到exit,help选中下滑有一个about,打开都没什么东西。怪不得叫做findkey。此时题目可能做什么我稍微清楚了点,不过由于我开发的少,所以中间还是有不少波折。

这时候打开ida看看。从WinMain()入手。

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
  CHAR Buffer[100]; // [esp+4Ch] [ebp-84h] BYREF
  HACCEL hAccTable; // [esp+B0h] [ebp-20h]
  struct tagMSG Msg; // [esp+B4h] [ebp-1Ch] BYREF

  memset(&byte_428C54, 0, 0x100u);
  dword_428C50 = 0;
  LoadStringA(hInstance, 0x67u, WindowName, 100);
  LoadStringA(hInstance, 0x6Du, ClassName, 100);
  set(hInstance); //set something
  if ( !sub_40100F(hInstance, nShowCmd) )
    return 0;
  LoadStringA(::hInstance, 0x6Eu, Buffer, 100);
  hAccTable = LoadAcceleratorsA(hInstance, (LPCSTR)0x6D);
  while ( GetMessageA(&Msg, 0, 0, 0) )
  {
    if ( !TranslateAcceleratorA(Msg.hwnd, hAccTable, &Msg) )
    {
      TranslateMessage(&Msg);
      DispatchMessageA(&Msg);
    }
  }
  return Msg.wParam;
}

可以看出这里没有我们需要的东西,基本框架在这,我们应该找找WndProc,寻找程序在哪做手脚的。

跟进set( hInstance ),

ATOM __cdecl sub_4011D0(HINSTANCE hInstance)
{
  WNDCLASSEXA v2; // [esp+4Ch] [ebp-30h] BYREF

  v2.cbSize = 48;
  v2.style = 3;
  v2.lpfnWndProc = (WNDPROC)&sub_401014;
  v2.cbClsExtra = 0;
  v2.cbWndExtra = 0;
  v2.hInstance = hInstance;
  v2.hIcon = LoadIconA(hInstance, (LPCSTR)0x6B);
  v2.hCursor = LoadCursorA(0, (LPCSTR)0x7F00);
  v2.hbrBackground = (HBRUSH)6;
  v2.lpszMenuName = (LPCSTR)109;
  v2.lpszClassName = ClassName;
  v2.hIconSm = LoadIconA(hInstance, (LPCSTR)0x6C);
  return RegisterClassExA(&v2);
}

这里的sub_401014其实就定义了WndProc,而我找到这个还花了不少时间。跟进。

void sub_401014()
{
  JUMPOUT(0x401640);//这里0x401640标红了,说明这里没有解析到,
}

跟进一看,原来没有构造出函数,然后我就选中了一段区域自己构造了一个WndProc。

  if ( times == 16 )
  {
    strcpy(String, "ctf");
    v22 = 0;
    v23 = 0;
    SetWindowTextA(hWndParent, String);
    strcpy(Text, "Are you kidding me?");
    MessageBoxA(hWndParent, Text, Buffer, 0);
  }
  ++times;
}

由于这个地方看上去简单,我就先观察了一下,这里会跳出一个Messagebox,那我试试。根据微软的Msg的定义,0x208为WM_MBUTTON,然后这里在times变为16时执行了里面内容,否则就是++times。那我鼠标中键疯狂点看看。

findkey2

哈哈,Are you kidding me ? definitely yes.

不过很遗憾这个和题目没啥关系。

if ( v11 == 0x205 )
{
  if ( strlen((const char *)String1) > 6 )
    ExitProcess(0);
  if ( strlen((const char *)String1) )
  {
    memset(v20, 0, sizeof(v20));
    v6 = strlen((const char *)String1);
    memcpy(v20, String1, v6);
    v7 = strlen((const char *)String1);
    sub_40101E(String1, v7, (LPSTR)String1);
    strcpy(Str, "0kk`d1a`55k222k2a776jbfgd`06cjjb");
    memset(v17, 0, sizeof(v17));
    v18 = 0;
    v19 = 0;
    strcpy(v13, "SS");
    *(_DWORD *)&v13[3] = 0;
    v14 = 0;
    v15 = 0;
    v8 = strlen(Str);
    sub_401005(v13, (int)Str, v8);
    if ( _strcmpi((const char *)String1, Str) )
    {
      SetWindowTextA(hWndParent, "flag{}");
      MessageBoxA(hWndParent, "Are you kidding me?", "^_^", 0);
      ExitProcess(0);
    }
    memcpy(v12, &unk_423030, 0x32u);
    v9 = strlen(v12);
    sub_401005(v20, (int)v12, v9);
    MessageBoxA(hWndParent, v12, 0, 0x32u);
  }
  ++times;
}

在这里发现了它其实是对String1作为一个输入,原始的输入到了v20,通过MD5加密后保存到String1和Str比较,而Str和String1没什么关系,幸好,不然F(x)==G(Y,X)的函数更加复杂,也许以后我可以做一个这种的

**********************************************************************MARK IT*****************************************************************
MD5 result
v18
0019F98C  63 38 38 33 37 62 32 33 66 66 38 61 61 61 38 61  c8837b23ff8aaa8a  
0019F99C  32 64 64 65 39 31 35 34 37 33 63 65 30 39 39 31  2dde915473ce0991  


v13
need to make xor
0019F880  57 5E 52 54 49 5F 01 6D 69 46 02 6E 5F 02 6C 57  W^RTI_.miF.n_.lW  
0019F890  5B 54 4C 00 00 00 00 00 53 53 00 00 30 6B 6B 60  [TL.....SS..0kk`  



“”
C6DCE185903ADADFFF0673FB1FC7AEB6
“abcdefg”
7AC66C0F148DE9519B8BD264312C4D64

由此我发现了MD5加密后的值,即Str,这里面用win Api 做到的hash加密也很值得学习。

然后就是暴力破解MD5(只有6位),这里我用了hashcat,是一个好东西,我写的脚本,直接歇菜

#0r4nge的垃圾脚本
#非常抱歉,俺这脚本跑不出来2333,就挂在这以后改改,多线程等等,字典太长也很麻烦害。
import hashlib

#dic = "abcdefghijklmnokqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
dic = "abcdefghijklmnokqrstuvwxyz"
dic += '\0x00'
length = len(dic)

for a in range(length):
    for b in range(length):
        for c in range(length):
            for d in range(length):
                for e in range(length):
                    for f in range(length):
                        m = dic[a]+dic[b]+dic[c]+dic[d]+dic[e]+dic[f]
                        flag=hashlib.md5()
                        flag.update(m.encode("utf8"))
                        md5=flag.hexdigest()
                        if md5=='c8837b23ff8aaa8a2dde915473ce0991':
                            print(m)
                            print(md5)
                            print("SUCCESSFUL")
这里的hash加密函数其实可以借鉴一波的,我就mark了
int __cdecl sub_4013A0(BYTE *LP____STRING1, DWORD dwDataLen, LPSTR LP____STRING2)
{
  int result; // eax
  DWORD i; // [esp+4Ch] [ebp-24h]
  CHAR String2; // [esp+50h] [ebp-20h]
  BYTE v6[16]; // [esp+54h] [ebp-1Ch]
  DWORD pdwDataLen; // [esp+64h] [ebp-Ch]
  HCRYPTHASH phHash; // [esp+68h] [ebp-8h]
  HCRYPTPROV phProv; // [esp+6Ch] [ebp-4h]

  if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
    return 0;
  if ( CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash) )          //0x8003 MD5
  {
    if ( CryptHashData(phHash, LP____STRING1, dwDataLen, 0) )
    {
      CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);  //2u  HP_HASHVAL
      *LP____STRING2 = 0;
      for ( i = 0; i < pdwDataLen; ++i )
      {
        wsprintfA(&String2, "%02X", v6[i]);
        lstrcatA(LP____STRING2, &String2);
      }
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      result = 1;
    }
    else
    {
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      result = 0;
    }
  }
  else
  {
    CryptReleaseContext(phProv, 0);
    result = 0;
  }
  return result;
}