当时比赛的时候只做了程序分析,还得是mmr大爹和se大爹最后AK了电子取证还出了个pwn+web直接起飞了。

逆向只有一道题,纯坐牢呜呜呜。:<

LOADER

64bit程序,无壳,IDApro打开以后主函数只有几行

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
DWORD flOldProtect; // [rsp+30h] [rbp+8h] BYREF

VirtualProtect(&unk_7FF6741C3040, 0x34166ui64, 0x40u, &flOldProtect);
sub_7FF6741C1040(&unk_7FF6741C3040);
return 0;
}

查询MSDN可知,VirtualProtect函数的作用是修改任何进程的访问保护,语法为

1
2
3
4
5
6
BOOL VirtualProtect(
[in] LPVOID lpAddress, // 要更改其访问保护属性的页面的区域的起始页的地址。
[in] SIZE_T dwSize, // 要更改其访问保护属性的区域的大小(以字节为单位)
[in] DWORD flNewProtect, // 内存保护选项。此参数可以是内存保护常量之一。
[out] PDWORD lpflOldProtect // 指向一个变量的指针,该变量接收指定页区域中第一页的上一个访问保护值。
);

再来看主函数的调用,显然是把一段内存中起始地址为0x7FF6741C3040,大小为0x34166的进程设置为可访问,并且下面再调用该进程。

由PE头信息可知,MZ开头的一般是一个新的可执行文件,这里多次尝试dump该文件均失败。。。

于是只能硬上动调,由于程序运行的时候会出现plz input your flag(format: flag{decimal number})的输入提示,于是想到找到该提示的输出语句,并且在scanf处卡死便可以找到主函数逻辑。

于是一路F8找到跑飞的地方,然后重新启用动调在该处F7步入接着F8按死,经过多次重复操作以后找到了主函数。

其中在sub_7FF624926220处跑飞,判断输入语句在该函数内。

接着往下看

1658126884786

判断flag为长度42位,开头为flag的字符串,这几行的作用就是判断其框架和长度,我们可以根据这个构造测试输入,这里拿flag{731926347612678468721647812683469788}举例子(瞎几把打的)。

label_15就是格式错误退出程序的地方。

再往下,把中间的36位字符串拿出来,取出前面的18位转化成整形,存在input_1里面

1658127037225

注意:这个input_1里面存的是一个地址,打开该地址后,由于这是一个int128位整形,所以需要按四次D把数据转化成dq类型,可以看出正好是前18位的内容

1658127103157

1658127299578

还得是SG👴!

后面也有同样的操作

1658127483505

接下来就是判断flag是否符合条件的部分了

1
2
3
4
5
6
7
8
9
10
11
sub_7FF624934380((__int64)&data_1, 10i64, &xmmword_7FF62494F190);// data1为72057594037927936
sub_7FF624934380((__int64)&data_2, 10i64, &xmmword_7FF62494F1A0);// data2为1152921504606846976
v30 = _mm_loadu_si128((const __m128i *)&xmmword_7FF62494F190);
v29 = _mm_loadu_si128((const __m128i *)&input_1);
if ( !(unsigned __int8)sub_7FF6249350C0(&v30, &v29) )// 比较大小,这里sub_7FF6249350C0相当于一个<号
goto LABEL_15;
v30 = _mm_loadu_si128((const __m128i *)&input_1);
v29 = _mm_loadu_si128((const __m128i *)&xmmword_7FF62494F1A0);
if ( !(unsigned __int8)sub_7FF6249350C0(&v30, &v29) )// 比较大小,这里sub_7FF6249350C0相当于一个<号
goto LABEL_15;

首先我们找到data_1和data_2的值,分别为72057594037927936和1152921504606846976,运行到sub_7FF624934380时,发现是一个赋值函数,把数据分别赋值给xmmword_7FF62494F190xmmword_7FF62494F1A0,之后又赋值给v30v29注意两次比大小的顺序是不一样的

于是得出

之后的代码分析如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
v31 = 0i64;
v32 = 0i64;
v30 = (__m128i)input_1;
v29 = (__m128i)input_1; // v29,v39赋值为input_1
multify(&v30, &v29, (__int64)&v31); // 经过尝试发现是一个大整数乘法函数,答案存在v31中
input1_mul_input1 = v31;
if ( v31 )
*(_QWORD *)(v31 - 16) += 8i64;
if ( (_QWORD)ans1 )
{
v23 = ans1 - 16;
v24 = *(_QWORD *)(ans1 - 16) - 8i64;
*(_QWORD *)(ans1 - 16) = v24;
if ( v24 <= 7 )
sub_7FF624929580(off_7FF62493A350 + 3, v23);
}
*(_QWORD *)&ans1 = input1_mul_input1; // 再赋值给ans1
v33 = 0ui64;
BYTE8(ans1) = v32;
v30 = (__m128i)input_2;
v29 = (__m128i)input_2;
multify(&v30, &v29, (__int64)&v33); // v33为input_2 * input_2
input2_mul_input2 = _mm_load_si128(&v33);
v34 = 0i64;
v35 = 0i64;
v30 = input2_mul_input2;
multify2(&v30, 11i64, &v34);
v26 = v34; // v26为input2 * input2 * 11
if ( v34 )
*(_QWORD *)(v34 - 16) += 8i64;
if ( (_QWORD)ans2 )
{
v27 = ans2 - 16;
v28 = *(_QWORD *)(ans2 - 16) - 8i64;
*(_QWORD *)(ans2 - 16) = v28;
if ( v28 <= 7 )
sub_7FF624929580(off_7FF62493A350 + 3, v27);
}
*(_QWORD *)&ans2 = v26;
v30 = _mm_loadu_si128((const __m128i *)&ans1);
BYTE8(ans2) = v35;
v29 = _mm_loadu_si128((const __m128i *)&ans2);
subtraction(&v30, &v29, &ans3); // 这是一个做减法的函数,此时v30和v29分别为ans1,ans2
// 那么ans3存的就是input1 * input1 - input2 * input2 * 11
sub_7FF624934380((__int64)&unk_7FF624939D50, 10i64, &answer); //这个answer的值经过查数据就是9
v30 = _mm_loadu_si128((const __m128i *)&ans3);
v29 = _mm_loadu_si128((const __m128i *)&answer);// 最终答案为9
if ( sub_7FF624935540(&v30, &v29) ) // 判断是否相等
{
qword_7FF62493D6A0 = 1i64; // 返回1,成功
}
else
{
LABEL_15:
if ( qword_7FF62493D6A0 != 1 )
return sub_7FF6249256F0((__int64)&unk_7FF624939D00, 1i64);
}
return sub_7FF6249256F0((__int64)&unk_7FF624939D28, 1i64);
}

于是把两段代码合起来我们可以得到一个大整数方程和不等式:

input_1input_2分别为36位flag的前18位和后18位。

crypto✌说这个叫pell方程(佩尔方程),可以用脚本,也可以用wolframalpha在线解方程

1658128895053

由于x和y的长度加起来少了一位,应该是在y前面加个0,于是构造flag{118936021352508390035860559716724409}

1658129421337

纯纯废物了这下QWQ。