注意
本文最后更新于 2024-02-12,文中内容可能已过时。
听说BugKu上线了,从昨天下午做到了今天下午,正好一天时间,最后是做了87题,实在是做不动了,冲分就暂时告一段落,前面的题目还是比较水的。这里讲解一下花了我大概一个小时才做出来的题目,bingo。
题目是一个解锁包,解压得到一张图片。
刚开始的时候没看分类,以为是MISC题,然后找了半天隐写内容,都没做找到,但是这个文件这么大,肯定是有问题。
然后又仔细找了一下,结果发现了这样一段内容,
看上去内容好像是EXE程序中才会有的,于是看了一下分类,好家伙,原来是re题。
百度了一下png文件尾
PNG (png),
文件头:89504E47 文件尾:AE 42 60 82
搜索 AE 42 60 82
这不就是熟悉的MZ文件头吗,用010 Editor提取出这一段内容。
并重命名为bingo.exe
结果报错了,随便找了一个exe文件,比对文件内容是否确实,发生少了PE文件头标识。
补上这一段内容。
数据补上后直接打开运行,发现可以显示黑框,但是运行后直接退出,于是打开IDA分析一下
ida打开后似乎认不出来文件的其他内容,动态调试后发现这一段内容会出现异常的情况。
1
2
3
4
5
6
7
8
9
| pusha
mov ecx, 3E000h
mov ebx, 1000h
mov ebx, 400000h
add ebx, edx
xor byte ptr [ebx], 22h
inc ebx
popa
jmp loc_408BE0
|
原因是edx的内容也是400xxxh,相加之后到了800xxx,超出了范围。
这里不知道是作者预期还是怎么的,反正应该是这个解密函数出现了问题。
但是看到这里应该就是一个xor解密(xor 0x22),所以直接在外部解密吧。
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
| #define MAXKEY 5000
#define MAXFILE 1000
#include <cstdio>
#include <cstring>
using namespace std;
int main()
{
char xor_key[MAXKEY], file_dir[MAXFILE];
char* buf;
//printf("xor key: ");
//scanf("%s", xor_key);
xor_key[0] = 0x22;
xor_key[1] = 0;
printf("file: ");
scanf("%s", file_dir);
FILE* fp = fopen(file_dir, "rb");
strcat(file_dir, ".xor");
FILE* fpw = fopen(file_dir, "wb+");
if (fp && fpw)
{
fseek(fp, 0, SEEK_END);
size_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
buf = new char[size];
fread(buf, sizeof(char), size, fp);
for (size_t i = 0, keySize = strlen(xor_key); i < size; i++)
buf[i] ^= xor_key[i % keySize];
fwrite(buf, sizeof(char), size, fpw);
}
if (fp) fclose(fp);
if (fpw) fclose(fpw);
return 0;
}
|
解密文件后,得到文件bingo.exe.xor,观察文件信息,发现实际上只有**.text**段进行了异或加密,其他内容都没有加密。
从文件中大量的0x22内容也可以看出来(因为0x00 ^ 0x22 = 0x22)
从0xCC就可以知道,应该是解密对了,因为0xCC对应的是INT3断点,也就是当段未初始化的时候,vs debug模式下会赋值的内容。vs中的烫烫烫也是这么来的。
替换.text段的内容。
替换后得到的exe程序,直接运行当然还是不可以的,但是可以放到ida中解析各个函数了。
但是没有任何的符号信息,难以阅读。所以我还是打算调整程序让其可以正常运行。
打开ida后,定位到start处
直接用Keypatch(ida插件)进行修改,让其直接跳到程序真正的入口点(jmp sub_408BE0)。
修改后进行保存
发现程序以及可以成功运行。
重新载入后发现,接下来发现程序的符号信息就有了。
可以看出,程序对输入内容进行加密后与程序中off_443DC0(zaciWjV!Xm[_XSqeThmegndq)进行比对。
这里的加密方法就是对你输入值(c)进行平方,然后再加上一个参数(b),最后解出来a。
满足关系式: a^2 + b^2 = c^2。
本来以为直接解密就好了,没想到这里还有一个函数_strrev(v6);
他的作用是把字符串信息倒置,所以最后显示的顺序也会变换。
由于这里sqrt还有个精度问题,我这里就不进行逆运算了,也就是
c = sqrt(a^2 + b^2)
直接编写程序爆破c的内容。
解密程序
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
| #include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;
int main()
{
char s[] = "zaciWjV!Xm[_XSqeThmegndq";
char e[] = " ";
char* v6 = (char*)operator new(strlen(s) + 1);
memset(v6, 0, strlen(s) + 1);
for (int i = 0; i < strlen(s); i++)
{
v6[i] = 'a' + i;
_strrev(v6);
}
for (int i = 0; i < strlen(s); i++) e[v6[i] - 'a'] = s[i];
printf("%s\n", v6);
printf("%s\n", e);
int a2 = 0x34;
for (int i = 0; i < strlen(s); ++i)
{
for (char t = 1; t < 0xFF; t++)
{
int v2 = (signed __int64)pow((double)a2, 2.0);
signed int v3 = (unsigned __int64)(signed __int64)pow((double)t, 2.0);
v3 -= v2;
v6[i] = (signed __int64)(sqrt((double)v3) + 0.5);
if (v6[i] == e[i])
{
printf("%c", t);
break;
}
}
--a2;
}
return 0;
}
|
运行后可以得到:
flag{woc_6p_tql_moshifu},看到这个flag之后还是觉得挺开心的。