0%

西湖论剑MZ

题目大致三个步骤

长度检测,置换,对比sha1值。

SHA1那里很容易可以从chatgpt那里得出。

最关键的就是要搞清楚中间的置换逻辑。至于为什么有一个sha1检测,我首先想到的就是多解,也就是根据那个逻辑能得出多种组合,而这里只取其中一个。(当时出题就是没有考虑多解,后来经提醒想到可以用哈希值来固定答案)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
for ( i = 0; i < 48; ++i )
{
v6 = input[i];
v5 = off_B69000[2 * v6];
if ( v6 - 5 == v5 ) // 需要满足input[i]-5==cpdata[2*input[i]]
{
v9[i] = ~(v6 + 1);
}
else
{
if ( v6 + 5 != v5 ) // 需要满足input[i]+5 == cpdata[2*input[i]]
{
printf("Wrong flag\n", v4);
exit(0);
}
v9[i] = ~(v6 - 1);
}
off_B69000 = (int *)off_B69000[2 * v6 + 1];
}

off_B69000是一个指针数组,input需要满足的条件是input[i] -/+ 5 == off_B69000[2*input[i]],而且每次操作之后都会由off_B69000 = (int *)off_B69000[2 * v6 + 1];改变地址,只能采取暴破的方法。

在输入之前有一个初始化的函数

他按照值、地址、值、地址的顺序赋值了5000组,这些数据就是比较数据。在数组的位置右键,点击arry然后填写数组大小即可将其解析,再将其dump出来即可。

暴破脚本:

该脚本的要点在于偏移地址的计算和递归的运用。

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
#include<stdio.h>
int index=0;
char flag[48];
int fun(int base);
void printStr(char*str,int maxlen);
int offset=0;
unsigned int arr[10000] = {…………}

int main()
{
fun(0);
return 0;
}

void printStr(char*str,int maxlen)
{
int strlen=0;
while(flag[strlen]!='\0'&&strlen<maxlen)
{
printf("%c",flag[strlen]);
strlen++;
}
printf("\n");
}

int fun(int index)
{
if(index==48)
{
if(flag[8]!='s'||flag[15]!='e'||flag[18]!='5'||flag[23]!='e'||flag[33]!='t'||flag[38]!='n'||flag[45]!='t')//这里是依次追加上的
{
return 1;
}
printStr(flag,48);
return 1;
}
for(int chr=33;chr<127;chr++) //利用可见字符进行暴破
{
if((chr+5==arr[chr*2+offset])||(chr-5==arr[chr*2+offset]))
{
int orgoffset=offset; //保存原始偏移
offset=arr[chr*2+1+offset]; //记录下一次偏移
int result=offset;

result-=0x299078; //实际偏移 299078
offset=(result/4); //因为dump的数据是int类型,四字节大小,所以将得到的相对偏移除4
flag[index]=chr; //将符合的结果放入flag数组

fun(index+1);
offset=orgoffset; //还原初始偏移
}
}

}
//Somet1mes_ch0ice_i5_more_import@nt_tHan_effort~!

参考:

2024年西湖论剑逆向Reverse题目MZ分析_哔哩哔哩_bilibili

【CTF&RE】2024西湖论剑–MZ_哔哩哔哩_bilibili