DJBCTF

整体上Re有点偏杂,最后拿了个杯子,哈哈,,

Re

A-Maze-In

迷宫题,但是有点特别,就是每个步骤对应的地图不一样。。

image-20210126111337271

提取出4个地图后,开始手走了一下,要命。。想到写一个dfs来搜索,这个应该是可行的,但我没有注意好走过的路径标记导致一直陷入了循环,找不解。。

后面从逆向的角度看,这个虽然正着走麻烦,但是从终点走到起点是每一步都确定的,走那一步看它反方向是不是为1即可。

因为每一步确定还是比较好走的:LLDRRDLLLDRDLDDDRRULURRULURRDDDLDR

赛后对我之前写的dfs,发现主要是每次往回走导致一直陷入一个死胡同,就是走了L然后走R,那每次走之前加了个判断试试。。果然,秒出。。

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <stdio.h>

unsigned char byte_404018[] =
{
0, 1, 0, 1, 0, 1, 1, 1, 0, 0,
1, 1, 1, 0, 1, 1, 0, 1, 1, 0,
0, 1, 0, 1, 0, 1, 1, 0, 0, 1,
0, 0, 1, 0, 0, 0, 1, 0, 0, 1,
0, 0, 1, 1, 0, 1, 1, 0, 1, 1,
0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
1, 1, 0, 0, 0, 1, 0, 1, 0, 0,
1, 1, 0, 0, 1, 1, 1, 0, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0, 1, 0,
0, 1, 1, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 0, 1, 0, 1, 0, 0,
1, 1, 0, 1, 1, 0, 1, 1, 0, 0,
0, 1, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 1, 0, 0, 1, 1,
0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
1, 1, 0, 0, 0, 1, 0, 1, 0, 0,
1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
1, 1, 0, 0, 0, 1, 0, 1, 1, 0,
1, 0, 1, 1, 0, 0, 1, 0, 0, 1,
0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 1, 1, 0, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1, 0, 0,
1, 1, 1, 0, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1, 0, 0,
1, 1, 1, 0, 1, 0
};
char flag[100];

int dfs(int i, int v5, int v4, char ch)
{
int j = 0, k = 0;

if(i > 33 )
{
if(v4 == 4 && v5 == 7)
{
printf("yes: %s\n", flag);
getchar();
return 0;
}

}
if (ch != 'L' && (v5 >= 0 && v4 >= 0) && (byte_404018+3)[32 * v5 + 4 * v4] == 1 )
{
flag[i] = 'R';
dfs(i+1, v5, v4+1, 'R');
}

if (ch != 'R' && (v5 >= 0 && v4 >= 0) && (byte_404018+2)[32 * v5 + 4 * v4] == 1 )
{
flag[i] = 'L';
dfs(i+1, v5, v4-1, 'L');
}

if (ch != 'U' && (v5 >= 0 && v4 >= 0) && (byte_404018+1)[32 * v5 + 4 * v4] == 1 )
{
flag[i] = 'D';

dfs(i+1, v5+1, v4, 'D');
}

if (ch != 'D' && (v5 >= 0 && v4 >= 0) && byte_404018[32 * v5 + 4 * v4] == 1 )
{
flag[i] = 'U';
dfs(i+1, v5-1, v4, 'U');
}
}

int main(void)
{
dfs(0, 0, 3, '1');

return 0;
}

Matara Okina

本来没怎么做过安卓,从这个题学了不少,比赛期间大多数时间都去学安卓了,虽然对解决本题用不上,但由此感觉安卓还是挺好玩的。

开始拿到题目对下面就有点疑惑:

image-20210125114031206

知道是取出secret参数的值经过异或运算后和一个ans字符串比较一下。那就先求出来看看:Android_scheme_is_FUN

开始以为这就是flag,结果不是,仔细看到是把这个字符串传入一个native层的check的函数的,通过一些运算显示出来。。其次看到scheme,加上之前的uri,突然明白了。。再到Mainifest看看,果然:

image-20210125114541846

就类似我们点击一个链接打开app一样,自己写一个网页链接让手机点一下就可以了。

1
<a href="sh0w://p4th/70/1nput?secret=Android_scheme_is_FUN">app</a>

UnrealFlag

对于这个题,其实就是一个解密一个游戏中加密的数据,挺实战的,比赛时没做,也是信息搜索能力没到位,知道要解密pak文件但是google,baidu了半天都没找到一个合适的文章。这个文章赛后看到的,很清晰且全:Reverse Engineering AES Keys From Unreal Engine 4 Projects

前期准备就是下载要解密文件对应游戏使用的虚幻引擎版本,这里4.26。

然后找到要解密文件使用的key,最后使用下载引擎中提供的Urealpak.exe解密即可。

开始找key:

找到安装引擎目录的:D:\Epic Games\UE_4.26\Engine\Source\Runtime\PakFile\Private\IPlatformFilePak.cpp文件,查看代码,虽然这个引擎版本可能会有一定的区别,但都是DecryptData下手:第三个参数即是。

image-20210126140813667)当为了在调试的时候好定位到上面这个函数,我们一般的方法是找到调用它的地方,直到有错误字符串信息出现(方便我们定位)。找引用的时候可能出现很多函数调用它的情况,但一般以LoadIndex()函数为最上层的函数,至于原因,上面文章有提到:

image-20210126140646329

一直向上回溯找到了第一次出现字符串的地方,x64搜索看看,有的。

image-20210126142130832

然后在x64中看的时候,源代码与其反汇编的结果并不是一一对应的,有可能一个函数是内联函数和反汇编结果是对一个函数进行了展开。这里就是:

首先看定位到字符串那一部分的源代码:

image-20210126151157572

然后x64中的情况:

image-20210126151408840

所以这里直接跟进我下断点的函数,然后从源代码可知,最后可从第三个参数获取key的函数在最后一个,但我们在x64中看到的是编译后的结果,一般编译器都会在初始和末尾添加一些检查函数的,这里大概猜一下,先在倒数第二个函数处下断然后看r8寄存器的值,如果不行继续换别的就是,反正范围就这么几个函数。

成功得到key,从hex转化为base64就好了。

image-20210126152347350

最后:UnrealPak.exe accepts a “crypto.json” parameter. You will want to create this file somewhere and add the following,即创建一个和下面类似的文件,把找到的key写入指定位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"$types": {
"UnrealBuildTool.EncryptionAndSigning+CryptoSettings, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null": "1",
"UnrealBuildTool.EncryptionAndSigning+EncryptionKey, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null": "2"
},
"$type": "1",
"EncryptionKey": {
"$type": "2",
"Name": null,
"Guid": null,
"Key": "uZFnisGm9AFdQ2hGBMRKm38+LAShgkbEMJOn8bv/arc="
},
"SigningKey": null,
"bEnablePakSigning": false,
"bEnablePakIndexEncryption": true,
"bEnablePakIniEncryption": true,
"bEnablePakUAssetEncryption": false,
"bEnablePakFullAssetEncryption": false,
"bDataCryptoRequired": true,
"SecondaryEncryptionKeys": []
}

接下来用指定格式的command解密:

1
"D:\Epic Games"\UE_4.26\Engine\Binaries\Win64\Unrealpak.exe FindFlag-WindowsNoEditor.pak -Extract C:\Users\11480\Desktop\sss\ -cryptokeys=C:\Users\11480\Desktop\sss\crypto.json

一般解密游戏中的数据就是各种模型,flag也应该也是一个类似的模型文件,使用umodel打文件flag.uasset,再转化成png保存就好了。

image-20210126160135944

anniu

开始就知道要让灰色按钮可用,找了enablewindow函数,但是不行,然后这个是易语言写的程序和以往做的有点不一样。。找按钮事件又没找到?。。奇怪。

最后直接OD从开始跟踪程序创建窗口的过程。其实就是很多个子窗口叠加的。从创建flag按钮的creatwindowsex找到了端倪,我才知道可以创建窗口的时候让控件不可用。。

其中的WS_DISABLED从VS查到了是0x08000000,所以我把整个值改为0x44012F00即可。

image-20210125115444155

warmup

就一个数独游戏,开始判断是不是每列每行的数都各不相同,然后判断每4*4的单元是不是每个数各不相同,典型的数独特征。

直接找个在线求解一下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main(void)
{
char flag[] = {8, 7, 6, 13, 10, 9, 15, 8, 9, 7, 5, 5, 6, 1, 8, 12, 9,
14, 16, 12, 2, 6, 6, 3, 7, 10, 4, 15, 5, 7, 8, 9, 8, 2,
1, 3, 7, 14, 3, 7, 12, 11, 1, 4, 13, 2, 8, 6};
int i = 0;

for(i = 0; i < 48; i++)
{
if(flag[i]-1 >= 10)
putchar(flag[i]-1+87);
else
putchar(flag[i]-1+48);
}
}

e

应该类似主程序装载一个文件,然后关键函数都在那个.so文件,所以动态跟踪就好了,很简单,找到输入的地方后下个断点,一会就来到的一个比较函数,虽然去了符号,但还是能猜到就是类似strcmp()函数,输入和目标字符比较。

-------------本文结束感谢您的阅读-------------