slide: https://drive.google.com/drive/folders/1ZC6mdxIzcLsnGJ7j8S4emGsiY_LRNlKt?usp=sharing
lecture: https://youtu.be/4-hgyiCV3ZA
main
跳到 main
xrefs
.pdata
typedef struct _RUNTIME_FUNCTION {
ULONG BeginAddress;
ULONG EndAddress;
ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
來到 main
的 UnwindData
指的地方
_UNWIND_INFO
先做好 SCOPE_RECORD
然後放到 main
的 UnwindData
的下面
回到 main_0
main_0
看到 2 個處以 0 的地方
那邊會有 exception
分別標上 exception1
exception2
可以發現對應到上面 _UNWIND_INFO
的SCOPE_RECORD
// win32.h
#define EXCEPTION_EXECUTE_HANDLER 1
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_CONTINUE_EXECUTION -1
由以上資料
我們知道 exception
發生後會跳到那邊了
來看看 exception1
發生後會跳到的位置
exception1
jumpTarget
大概就是進行下面這樣的操作
for (int i = 0; i < 38; i++)
{
flag_inp[i] ^= key + i;
}
至於 key
的值我們可以往上看
於 main
unwine_info
的 _C_specific_handler
我們了解到了 key
會是 rax
rax
的值則會是 0xBE
實際跑起來也確實如此
接著來看 exception2
的 jumpTarget
exception2
continue_exec
做的事情大概像這樣
for (int i = 0; i < 38; i++)
{
flag_inp[i] += key + i;
}
其中 key
會是 0xEF
之後就是正常的執行後面 IDA
有成功反編譯出來的的迴圈
所以之後把 flag_enc
拉出來解密即可
Exception/sol.cpp
for (int i = 0; i < 38; i++)
{
unsigned char key = 0xEF;
flag_enc[i] -= key + i;
}
for (int i = 0; i < 38; i++)
{
unsigned char key = 0xBE;
flag_enc[i] ^= key + i;
}
// output flag
for (int i = 0; i < 38; i++)
{
cout << flag_enc[i];
}
// output: FLAG{__C_specific_handler_is_hooked:O}
從 IDA
可以看到城市一開始會有三次的 TLS Callback
TlsCalllback1
這個做的是把 key
進行 funcs
的操作
TlsCalllback2
這個做的事把 funcs[3]
的東西往左 rotate
main
由於上面的 TlsCallback
所以 main
裏面用到的 funcs
key
都會跟一開始的不同
只要把 TlsCallback
操作後的 funcs
key
拉出來然後就可以解密了
TLS Callback/sol.cpp
for (int i = 0; i < 58; i++)
{
flag_enc[i] -= key[i & 3];
(funcs[i % 3u])(flag_enc[i]);
}
// output
for (int i = 0; i < 58; i++)
{
cout << (unsigned char)flag_enc[i];
}
// output: FLAG{The_first_TLS_callback_function_is_called_two_times!}
main
由 :9,10
可以知道 flag
長度可能為 39
進入 sub_401604
sub_401604
看到了一些 GetProcAddress
整理一下
之後把 sub_401604
命名為 getProcAddresses
回到 main
進入 sub_401884
sub_401884
進入 sub_401550
sub_401550
已經知道這是把一個 exe
解密了
把這個 exe
透過動態分析直接取出解密後的樣子
把函式取名為 decrypt_exe
回到上一層
sub_401884
修一修可以看出
:21
取得了自己這個執行檔
的位置
:22
把自己這個執行檔
跑起來 第6個參數 4
表示 CREATE_SUSPENDED
之後用 exe
把 自己這個執行檔
記憶體中的東西換掉
所以到了 :53
會恢復執行的東西變成 exe
回到 main
進入 sub_401723
sub_401723
exe
sub_401723
exe
可以看到 sub_401723:28
會先把 flag_inp
傳給 exe:19
然後 exe:21
會對 flag_inp
做一些事 (sub_401550
)
然後 exe:24
會把 flag_inp
回傳給 sub_401723:32
來看看 sub_401550
對 flag_inp
做了什麼事
sub_401550
很明顯的
這裡做的事加密 flag_inp
回到 main
進入 sub_401C37
sub_401C37
是很一般的 check_flag
所以這裡只要把 flag_enc
拉出來
把上面的加密倒過來做就會得到 flag
Meow/sol.cpp
這邊的部分我先把 encrypt
的部分複製貼上
然後 copilot
會幫忙寫好 decrypt
然後把 check_flag
的部分複製貼上
然後 copilot
會幫忙寫好 output_flag
這邊只要呼叫 output_flag
出來的就會是答案
int main ()
{
output_flag();
// output: FLAG{pr0c355_h0ll0w1ng_4nd_n4m3d_p1p35}
}
順帶議題
這題的 key
是 meow
的 base64
看到有被包殼
先解殼
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2022
UPX 4.0.0 Markus Oberhumer, Laszlo Molnar & John Reiser Oct 28th 2022
File size Ratio Format Name
-------------------- ------ ----------- -----------
39424 <- 16896 42.86% win64/pe dropper_43741eb13c4a767e.exe
Unpacked 1 file.
main
動態分析發現跑道這會跳到其他的地方
來看看是什麼地方
原來跑去 kernel32.sleep(0x9AEC800)
了
所以這裡才會跑這麼久
把他 patch 成 sleep(1)
往下走到 00007FF60FE92DC5
然後觀察一下參數
結果 flag
就在裡面 5: [rsp+20]
那就直接把 flag
拿走咯 ~~
5: [rsp+20] 000001D60364D2C0 000001D60364D2C0 "FLAG{H3r3_U_G0_iT_Is_UR_flAg}"