題目下載:
鏈接:https://pan.baidu.com/s/1ArbrvREhcbzVPFsHGoo2yA
提取碼:3khv
IDA載入,尋找main函數
sub_401000()函數內部是一個反調試
而nullsub_1無法打開
查看main函數的彙編代碼,發現有未定義的函數
繼續下滑發現有關鍵代碼
在函數頭摁下快捷鍵P,將其定義爲一個函數,摁下F5鍵發現還是無法F5
找到IDA設置,打開stack pointer
發現函數的最後,retn的棧小於0了,導致不能F5
先在pop 或者call那裏,將棧指針調整(快捷鍵Alt+k),使得retn的棧指針大於等於0
如果出現下圖情況(一會出現一會不出現,,玄學問題)
- 點進這個地址,讓IDA自動分析一下,退出來就能F5
- 這道題,此call剛開始未定義,然後輸出“Successful”的代碼在此call的上面,可以判斷此call並不是關鍵代碼,因此將其取消定義(右鍵→undefine)
void __usercall hanshu1(int a1@<ebp>)
{
int v1; // ecx
*(a1 - 132) = 0;
*(a1 - 40) = 89;
*(a1 - 39) = 110;
*(a1 - 38) = 123;
*(a1 - 37) = 107;
*(a1 - 36) = 89;
*(a1 - 35) = 32;
*(a1 - 34) = 119;
*(a1 - 33) = 106;
*(a1 - 32) = 90;
*(a1 - 31) = 91;
*(a1 - 30) = 77;
*(a1 - 29) = 111;
*(a1 - 28) = 91;
*(a1 - 27) = 67;
*(a1 - 26) = 90;
*(a1 - 25) = 42;
*(a1 - 24) = 90;
*(a1 - 23) = 67;
*(a1 - 22) = 119;
*(a1 - 21) = 101;
*(a1 - 20) = 86;
*(a1 - 19) = 110;
*(a1 - 18) = 85;
*(a1 - 17) = 67;
*(a1 - 16) = 89;
*(a1 - 15) = 91;
*(a1 - 14) = 73;
*(a1 - 13) = 121;
*(a1 - 12) = 89;
*(a1 - 11) = 91;
*(a1 - 10) = 42;
*(a1 - 9) = 41;
*(a1 - 8) = 3;
printf(Format);
*(a1 - 76) = 0;
*(a1 - 75) = 0;
*(a1 - 71) = 0;
*(a1 - 67) = 0;
*(a1 - 63) = 0;
*(a1 - 59) = 0;
*(a1 - 55) = 0;
*(a1 - 51) = 0;
*(a1 - 47) = 0;
scanf(aS, a1 - 76); // a1-76 爲用戶的輸入
*(a1 - 128) = a1 - 76; // a1-128 是一個指針,指向用戶輸入
*(a1 - 144) = *(a1 - 128) + 1; // a1-144存儲用戶輸入第二個字符
do
*(a1 - 113) = *(*(a1 - 128))++;
while ( *(a1 - 113) );
*(a1 - 140) = *(a1 - 128) - *(a1 - 144);
*(a1 - 148) = hanshu2((a1 - 76), *(a1 - 140), (a1 - 136));// a1-140 爲編碼之前的長度
// a1-136 爲base64編碼後的長度
for ( *(a1 - 124) = 0; *(a1 - 124) < 33; ++*(a1 - 124) )// a1-124 爲計數器 <33 33位
*(a1 + *(a1 - 124) - 112) = *(*(a1 - 124) + *(a1 - 148)) ^ 3;// base64編碼後與3異或
// 賦給a1-112
for ( *(a1 - 120) = 0; *(a1 - 120) < *(a1 - 136); ++*(a1 - 120) )// a1-120爲計數器,循環次數爲base64編碼後的長度
{
if ( *(a1 + *(a1 - 120) - 112) != *(a1 + *(a1 - 120) - 40) )// base64編碼後,再與3異或,然後與a1-40比較
{
printf(asc_40302C);
exit(0);
}
if ( *(a1 + *(a1 - 120) - 112) == *(a1 + *(a1 - 120) - 40) )
++*(a1 - 132); // 如果不相等,a1-132就不加,不加的話,下面判斷就會失敗
}
if ( *(a1 - 132) == *(a1 - 136) )
printf(aSuccessful);
v1 = a1 ^ *(a1 - 4);
JUMPOUT(loc_40121B);
}
可以發現他把所有的變量都用指針來表示了,,,
定義了一個字符串,(從a1-40到a1-8)
這是最後與變換後的flag比較的字符串
hanshu2()是一個base64加密
可以參考Base16,Base32,Base64編碼詳細學習
查看其字符表,發現是一個自定義字符表的base64加密
其中&
=&
是一個轉義字符串
轉義字符串(Escape String),即字符實體(Character Entity)分成三部分:第一部分是一個&符號,英文叫ampersand;第二部分是實體(Entity)名字或者是#加上實體(Entity)編號;第三部分是一個分號。
總體流程爲,程序將用戶的輸入進行base64加密,然後與3異或,最後與存儲在棧上的字符串進行比對
寫腳本
因爲text長度爲33,而base64將原字符串長度變爲4/3,所以密文長度要能被4整除
所以密文的長度應該爲36,需要加上三個"="
import base64
text = [89,110,123,107,89,32,119,106,90,91,77,111,91,67,90,42,90,67,119,101,86,110,85,67,89,91,73,121,89,91,42,41,3]
for i in range(len(text)):
text[i] ^= 3
text[i] = chr(text[i])
text = ''.join(text)
text += "==="
print(base64.b64decode(text.translate(str.maketrans("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz)!@#$%^&*(+/","ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")).encode('utf-8')))
最後得出flag爲flag{base_f4ck_Reverse}