这题的逆向部分比较容易,细节略过,只给出结果。
要求用户输入flag,长度为27字符,程序将其与9个1一起排成6*6的矩阵:
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . 1 1 1
1 1 1 1 1 1
注意,这里的.表示用户的输入,而1表示数字1不是字符’1’。
假设这个6*6矩阵为A,程序计算矩阵A*Trans(A),判断与给定的矩阵是否相等(Trans表示转置)。
从内存中copy之后得到原矩阵为(后面记为B):
第一列 | 第二列 | 第三列 | 第四列 | 第五列 | 第六列 |
---|---|---|---|---|---|
73767 | 62102 | 48910 | 55372 | 37336 | 663 |
62102 | 55344 | 41766 | 45072 | 30247 | 560 |
48910 | 41766 | 36843 | 34717 | 28867 | 445 |
55372 | 45072 | 34717 | 45069 | 28239 | 503 |
37336 | 30247 | 28867 | 28239 | 39900 | 348 |
663 | 560 | 445 | 503 | 348 | 6 |
该矩阵第i行
第j列
的值 == A的第i行
与第j行
分别相乘,然后相加。
本来是不可能直接解回去的,但是我们有很多限制条件:
- A的前27个数都是介于32~127的整数。
- 不是36个元素都不知道,我们已经知道后面9个是1。
- 第1行的元素恰好是”whctf{“。
第三条靠猜,但是验证起来非常容易:
>>> b="whctf{"
>>> l=list()
>>> for i in b:
l.append(ord(i))
>>> l
[119, 104, 99, 116, 102, 123]
>>> sum(l)
663
>>> sum([i*i for i in l])
73767
接下来选取第5行
作为突破口。第5行应该是这样的形式:
. . } 1 1 1
利用B(5,1)、B(5,6)的值可以轻易推出第5行的内容为:
// B(5,1)表示矩阵B的第5行第1列的元素,下同
t h } 1 1 1
剩下几行没有想到比较好的思路,决定采用逐行爆破。建议用C语言提升效率。
比如第二行的爆破代码如下:
#include<stdio.h>
#define ROW1 62102
#define ROW5 30247
#define ROW6 560
#define ROWSELF 55344
int buf[6];
char out[10]={0};
int p[]={119, 104, 99, 116, 102, 123}; //"whctf{"
int q[]={116,104,125,1,1,1}; //第5行内容
void test()
{
int sum=0;
int i;
for(i=0;i<6;i++)
sum+=buf[i]*buf[i];
if(sum!=ROWSELF) //判断平方和是否与B(2,2)相等
return;
sum=0;
for(i=0;i<6;i++)
sum+=p[i]*buf[i];
if(sum!=ROW1) //判断与第1行相乘相加是否与B(2,1)相等
return;
sum=0;
for(i=0;i<6;i++)
sum+=q[i]*buf[i];
if(sum!=ROW5) //判断与第5行相乘相加是否与B(2,5)相等
return;
for(i=0;i<6;i++)
out[i]=buf[i];
printf("%s\n",out); //通过上面检验条件的可以输出了
}
void burp(int m)
{
int k,i;
if(m==5)
{
k=ROW6; //利用与第6行相乘相加的值与B(2,6)相等
for(i=0;i<5;i++) //可以求出第2行各个元素的和
k-=buf[i]; //因此实际的burp相当于是5次方而不是6次方
buf[5]=k; //从而节约不少时间
if(k<0x7f&&k>=0x20)
test();
}
else
{
for(i=0x20;i<0x7f;i++)
{
buf[m]=i;
burp(m+1);
}
}
}
int main()
{
burp(0);
}
每行都写了份代码,本来是打算发给队友并行计算,后来发现修改代码的过程中就差不多能跑完上一份代码了。
最后把2、3、4行跑出来,拼起来得到flag:whctf{Y0u_ar3_g00d_a7_m4th}