一道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}