170917 逆向-WHCTF(BabyHack)
1625-5 王子昂 總結《2017年9月17日》 【連續第350天總結】
A. XCTF(武漢站)-Reverse
B.
EasyHook
剛開始看這個名字還以為要注入DLL啥的呢(:з」∠)忐忑不安地打開發現結構挺簡單:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // [email protected]
HANDLE FileHandle; // [email protected]
DWORD NumberOfBytesWritten; // [sp+4h] [bp-24h]@3
char Buffer; // [sp+8h] [bp-20h]@1
sub_401370(aPleaseInputFla);
scanf(a31s, &Buffer);
if ( strlen(&Buffer) == 19 ) // 輸入長度為19
{
sub_401220(); // 將Re_writeFile函式的地址覆蓋WriteFile
FileHandle = CreateFileA(FileName, 0x40000000u, 0, 0, 2u, 0x80u, 0);// 建立檔案
WriteFile(FileHandle, &Buffer, 0x13 u, &NumberOfBytesWritten, 0);// 執行WriteFile函式,實際上是Re_writeFile
sub_401240(&Buffer, &NumberOfBytesWritten); // 比較函式(偽)
if ( NumberOfBytesWritten == 1 )
sub_401370(aRightFlagIsYou); // 錯誤提示
else
sub_401370(aWrong);
system(aPause);
result = 0;
}
else
{
sub_401370(aWrong);
system(aPause);
result = 0 ;
}
return result;
}
關鍵就在sub_401220函式上
本來以為它是處理函式,但是開啟以後發現通過API取得了kernel32.dll中WriteFile的地址,之後就不太清晰了
OD動態除錯也可以發現,sub_401220前後並沒有使得buffer的值發生改變
單步除錯發現關鍵變化發生於call WriteFile
這明明是個API,怎麼會改變檔案內容呢
F7跟進去發現轉入了使用者模組的sub_401080
這下就清楚了,sub_401220取到了WriteFile的地址,然後用sub_401080覆蓋之;使得呼叫WriteFile的時候變為呼叫sub_401080
這個就是hook技術了
接下來分析sub_401080:呼叫了sub_401000處理input,然後就執行真正的WriteFile
而在sub_401000中終於是真正的處理演算法:
signed int __cdecl flag(int str, signed int length)
{
char v2; // [email protected]
char v3; // [email protected]
char v4; // [email protected]
int v5; // [email protected]
signed int result; // [email protected]
v2 = 0;
if ( length > 0 )
{
do
{
if ( v2 == 18 ) // 第19個字元異或0x13
{
*(_BYTE *)(str + 18) ^= 0x13u;
}
else
{
if ( v2 % 2 ) // 偶數字符(下標為奇數),str[n] = n ^ (str[n]-i)
v3 = *(_BYTE *)(v2 + str) - v2;
else
v3 = *(_BYTE *)(v2 + str + 2); // 奇數字符(下標為偶數),str[n] = n ^ str[n+2]
*(_BYTE *)(v2 + str) = v2 ^ v3;
}
++v2;
}
while ( v2 < length );
}
v4 = 0;
if ( length <= 0 )
{
LABEL_13:
result = 1;
}
else
{
v5 = 0;
while ( byte_40A030[v5] == *(_BYTE *)(v5 + str) )// 驗證處理過後的結果是否與byte_40a030相等
{
v5 = ++v4;
if ( v4 >= length )
goto LABEL_13;
}
result = 0;
}
return result;
}
最下面的地方迴圈比較,剛開始沒有注意,成功的話會返回1否則返回0
觀察了一下返回1的話會使得
*lpNumberOfBytesWritten = 1
演算法比較簡單,稍微繞一下就能寫出還原指令碼
而從main函式中可以看到,要提示正確,需要NumberOfBytesWritten == 1
這個變數則取決於sub_401240
開啟可以看到,是一個字串比較迴圈:
input的另一邊是“This_is_not_the_flag”
放入解密指令碼發現上字串的長度是20,與輸入不對應,做出來的字串也有些問題
比如說,解密後文本的第一個字元不會影響源字串,而最後一個字元則會決定最後一個和倒數第三個字元;
‘a’對應的倒數第三個字元是’b’,意味著沒有與其對應的原字串
檢查了好久演算法沒有出問題,花去了兩三個小時
本來在考慮是否要爆破通過前一個驗證,
後來突然想起來在sub_401000中最後還有一個驗證迴圈,雖然return不大對,不過死馬當活馬醫了
用IDC指令碼將值dump下來,進行解密
嘿,還真的生成了有意義的字串:
lag{Ho0k_w1th_Fun}
第一個字元很明顯是a,提交正確
雖然後面那個字串已經明白告訴This is not the flag了結果還是掉坑裡了OTZ
看起來lpNumberOfBytesWritten是NumberOfBytesWritten的指標,因此取值賦1也可以達到效果
如果不跳坑裡能拿900+Pt的,被拖了兩三個小時就只有500Pt啦,有點可惜~
練習不夠,對lp不夠敏感,還要慢慢學習
附上python加解密指令碼:
# origin pro
s = "1234567890123456789"
flag = ''
for i in range(18):
if(i%2):
flag += chr(i ^ (ord(s[i])-i))
else:
flag += chr(i ^ (ord(s[i+2])))
flag += chr(ord(s[18]) ^ 0x13)
print flag
# rev pro
# s = "This_is_not_the_flag" 被坑了許久的假字串
# s = "307234?69.9,9*9()6*" 1-9的驗證加密結果
s = "ajygkFm.\x7f_~-SV{8mLn" byte_40A030的真字串
flag = ['' for x in range(19)]
flag[18] = chr(ord(s[18]) ^ 0x13)
# s[18] = flag[18]
for i in range(18):
i=17-i
if((i)%2):
flag[i] = chr((ord(s[i]) ^ i) + i)
else:
if(i>1):
flag[i] = chr(ord(s[i-2]) ^ (i-2))
print ''.join(flag)
C. 明日計劃
ARP欺騙