Winesap's Blog

TMCTF 2017 Rev400

| Comments

好像開始會一點 Windows 技能了... 學習中

逆向題,x64 PE+、VMProtect 還有一些 anti-debug。每隔幾秒會顯示一個HEX字元,總共41216個,理論上跑得夠快的話丟著跑完就可以了。從頭幾個字元當 header 來看應該會得到一個 7z 檔。

所以先來寫一個用來抓字的工具。它顯示字元的方式是直接把游標移到按鈕上,然後顏色應該是因為 hover 的關係所會變。簡單做法就是底下這樣,可以抓到現在指的字元,再 GetParent()^2 抓個主視窗標題就可以看是第幾個了:

#include <stdafx.h>
#include <Windows.h>
int main() {
    while (1) {
        POINT pt;
        GetCursorPos(&pt);
        HWND hwnd = WindowFromPoint(pt);
        CHAR str[100];
        GetWindowTextA(hwnd, str, 100);
        printf("%s\n", str);
    }
}

接下來把它加速就可以了。由於等待時視窗會卡住 (不是 Timer),CPU 也沒有燒起來 (不是 spin-loop),應該是用到 Sleep 類的 WinAPI。由於某種 anti-debug 導致 break-point 失效的樣子,但看不出它是怎麼做到的,所以要測試的地方直接 patch 加個無窮迴圈讓它停下來好了。我用 IDA remote x64 debugger,attach 上去後跑 python script:

import ida_bytes

def patch(addr, buf):
    ida_bytes.patch_many_bytes(addr, buf.decode('hex'))
    
# ntdll_NtDelayExecution -> movabs rax,0x7ffd4d19e900; jmp rax

patch(0x7FFD4F245A20, '48b800e9194dfd7f0000ffe0')

這裡直接 patch ntdll_NtDelayExecution,跳到另一段也是 patch 上去的 code 跑。內建的 ida_idp.AssembleLine 好像不吃 x64 的樣子,所以後來直接轉 HEX 後貼過來,或許可以試著把 pwntools 裝進去。


DelayExecution 第二個參數 (rdx) 是指向一個 uint64 值,是負值代表要 delay 幾 ns。Patch 好之後 continue,就會發現它跑得飛快了。

但跑一陣子後會跳 "You are too fast",猜測是用了某個時間相關的函式,判斷單位時間內不能跑太多個。試過 GetSystem/LocalTimeWM_TIMER後,發現它是定期用 GetTickCount 來取時間,所以 patch kernel32_GetTickCount 讓回傳值多個 N 倍就行,這樣就可以一路跑到底了。

不過實際測過後發現跑太快了會掉包,應該放慢一點就可以了。但比賽時間不太夠得快點跑完,所以最後 patch 了 user32_SetCursorPos,把座標 (rcx, rdx) 塞進用 kernel32_VirtualAlloc 開出來的 buffer 裡。因為它按鈕的橫向排列順序是依序 rotate 而已,所以只要 group 完 X 座標後就可以直接算出對應該的字元。

7z 解出來後是 stub.exe,這個就是很簡單的 reverse 了。16 位密碼每位是獨立的,直接暴出來就行:

如果早點做有時間的話,放著慢慢跑完比較有趣 XD

Comments

comments powered by Disqus