一道SCTF的逆向題,做了很久很久才做起,唉…
IDA打開,用shift+f12查找出現的字符串,再通過x交叉引用定位到主函數
根據僞代碼,大概是判斷我們輸入的一個長度小於等於6的字符串,跟進判斷函數看一看
看到32位的a-f+0-9的字符串,盲猜哈希,又因爲輸入的長度小於等於6,肯定能爆破出來,於是丟到cmd5網站上,解出來“sycsyc”
成功繞過第一個判斷以後,下面的代碼大致意思是查找.reioc段並且改爲可寫,然後用剛剛輸入的字符串進行異或,這裏用IDCPython腳本處理一下
可以看到處理完後.reioc段變成了這個樣子(先u將數據轉爲未定義,再p轉爲函數)
第一個call是printf讓你輸入flag,第二個call比較關鍵。先讀取輸入的字符,要求長度爲30,然後找到.ebata段並對其進行動態patch
具體的patch方法是,取輸入的前5個字符,進行異或
再寫腳本處理一下。前5個字符到底是什麼呢?盲猜是4個字母+1個大括號,因此有“flag{”、“sctf{”、“SCTF{”、“FLAG{”四種可能的開頭方式(以我心目中的可能性降序排序),因此全部試一遍 比賽官網上面寫了flag格式
老樣子,將被動態patch的數據轉爲函數,即可在f5裏看到sub_*開頭的函數啦
這裏只要合理的用y來修改一下變量類型,就能更好的看清楚算法流程啦~
我們用c++寫一下爆破腳本:
#include <iostream>
using namespace std;
int sub_401A70(int *a1, char *Str) {
int v5[30];
int v6 = 0;
int v10 = 0;
int v11 = 0;
int v12[30] = { 128,85,126,45,209,9,37,171,60,86,149,196,54,19,237,114,36,147,178,200,69,236,22,107,103,29,249,163,150,217 };
for (int i = 0; i < strlen(Str); ++i)
v5[i] = Str[i];
for (int j = 0; j < strlen(Str); ++j) {
v11 = (v11 + 1) % 256;
v10 = (a1[v11] + v10) % 256;
a1[v11] = a1[v10] & ~a1[v11] | a1[v11] & ~a1[v10];
a1[v10] = a1[v10] & ~a1[v11] | a1[v11] & ~a1[v10];
a1[v11] = a1[v10] & ~a1[v11] | a1[v11] & ~a1[v10];
v6 = (a1[v10] + a1[v11]) % 256;
v5[j] ^= a1[v6];
}
for (int k = 0; k < 30; ++k)
if (v5[k] != v12[k]) {
return k + 1;
}
return 0;
}
int sub_404000(char *Str) {
int v3[300];
int v5 = 0;
int v9[300];
char Dst[40];
char v11[9] = "syclover";
memset(Dst, 0, 0x28u);
for (int i = 0; i < strlen(Str); ++i)
Dst[i] = Str[i];
for (int j = 0; j < 256; ++j) {
v9[j] = j;
v3[j] = v11[j % 8];
}
for (int k = 0; k < 256; ++k) {
v5 = (v3[k] + v9[k] + v5) % 256;
int tmp = v9[k];
v9[k] = v9[v5];
v9[v5] = tmp;
}
return sub_401A70(v9, Dst);
}
int main()
{
char flag[] = "SCTF{111111111111111111111111}";
for (int i = 5; i < 29; i++)
flag[i] = 1;
for (int i = 5; sub_404000(flag);)
if (sub_404000(flag) == i + 1)
flag[i]++;
else
i++;
cout << flag;
}
成功爆破出flag!
SCTF{zzz~(|3[___]_rc4_5o_e4sy}