DubheCTF re

VMT

有非常多的反调试,因此直接使用x32dbg+sharpOD过反调,中间发现一个类似Key的东西

image-20250702204954294

输入aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,运行到这key变了,且输出,好像发现我patch过程序了

image-20250702205140458

image-20250702205120231

key肯定是这俩中的一个

image-20250703154808291

这个有点像密文

image-20250703160007138

这个函数是个加密算法

image-20250703160411592

cyberchef试了几种加密算法发现是SM4直接出了

image-20250703161759165

DubheCTF{VMT_H00K_4ND_$3H_15_U53FUL}

1
2
3
4
5
from gmssl.sm4 import CryptSM4, SM4_DECRYPT, NoPadding

sm4_dec = CryptSM4(padding_mode=NoPadding)
sm4_dec.set_key(b'Pyu0Z8#bC5vqUFgt', SM4_DECRYPT)
print(sm4_dec.crypt_ecb(bytes.fromhex('6A61EF281A7473D6B1B431D0351F7E2242CFB9D6EC4E01EF656D6CF520F142821C7061EB843D5ABE378B394C4DC1298B')))

从其它佬的wp发现了个floss工具

image-20250703163723500

提取字符串,可以发现是SM4了

也可以不用插件,直接patch,这里就不展示patch了,问GPT就行,注意patch完别用IDA调试还会有问题,还有就是patch的时候注意栈平衡可能也会引发问题

有些反调试,不需要强行去掉,如果调试器支持硬件断点/线程恢复(如 x64dbg),影响不大。

patch完大概长这样:

image-20250703173515512

Destination

main看着很简单,调试看看,单步运行。发现会在程序进入main前的j__initterm退出,应该是个反调试

image-20250704114453162

image-20250704114408952

C++ 程序启动 在进入main()前 会 调用两次 _initterm, 在 main() 结束后 还会调用 两次 _initterm

  • 第一次 _initterm 初始化 C 环境(与多线程有关)
  • 第二次 _initterm 创建 C++ 全局变量, 如果是自定义类型, 会 调用 atexit() 传入 全局对象的析构函数, 注: atexit 内维护的是栈, 不是队列, 先传入的函数 会 后调用
  • main() 执行
  • 处理 atexit 中的函数
  • 第三次 _initterm do pre terminators
  • 第四次 _initterm do terminators

直接patch掉

=运行会发现,最后判断时输入会被加密,并且这里布过 __debugbreak () 函数会触发一个软件断点异常。

image-20250704115413126

跟进看看

image-20250704141827133

存在花指令

1
2
.text:0041411C                 add     dword ptr [esp+0], 5
.text:00414120 retn

下个断点调试看看,然后使用IDA trace,可以把运行过的高亮标起来

image-20250704153826703

接下来总结了几种方法:

调试时,发现了花指令和混淆代码

测试输入:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

image-20250704194445707

只有mov是真正要执行的命令,下面的为花指令

下面的jmp指令只是为了衔接程序其实并没有多大意义,混淆一下

下面那个也是混淆指令:

image-20250704194556015

基本上一大块只有一句是真正要执行的指令

法一:手动trace+自己(GPT)分析

1
2
3
4
5
6
004140D7       | push ebp                                      
00414AFE | mov ebp,esp
00416360 | sub esp,10C
004143A6 | push ebx
00415AFF | push esi
00414DE7 | push edi

….

比较费时间

法二:IDA手动摘出每一句patch

一个有效指令,然后提取出来机器码,最终将所有的机器码提取出来,再patch进去。再P+F5即可

也挺麻烦的

法三:IDC脚本去花

但是无法F5

官解的wp的去花脚本有点问题,

应该是这样才对

1
if(Byte(eip) == 0xE8 && Byte(eip+1) == 0x00 && Byte(eip+2) == 0x00 && Byte(eip+3) == 0x00 && Byte(eip+4) == 0x00)
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
80
81
82
83
84
85
86
87
88
89
#include <idc.idc>
static main()
{
auto last_eip;
auto eip = 0x4140D7;
auto offset;
auto sum = 1;
auto just_jump = 0;
while(Byte(eip) != 0xC3) //遇到retn指令停止
{
if(Byte(eip) == 0xE8 && Byte(eip+1) == 0x00 && Byte(eip+2) == 0x00 && Byte(eip+3) == 0x00 && Byte(eip+4) == 0x00)
{ //当遇到无效call花指令
PatchByte(eip + 0, 0x90);
PatchByte(eip + 1, 0x90);
PatchByte(eip + 2, 0x90);
PatchByte(eip + 3, 0x90);
PatchByte(eip + 4, 0x90);
PatchByte(eip + 5, 0x90);
PatchByte(eip + 6, 0x90);
PatchByte(eip + 7, 0x90);
PatchByte(eip + 8, 0x90);
PatchByte(eip + 9, 0x90);
//去除call和retn组成的花指令
eip = eip + 10;
continue;
}
else if(Byte(eip) == 0xE9 && just_jump == 0)
{ //当遇到长跳转jmp指令
offset = Byte(eip+1);
offset = Byte(eip+2) * 256 + offset;
offset = Byte(eip+3) * 256 * 256 + offset;
offset = Byte(eip+4) * 256 * 256 * 256 + offset;
eip = eip + offset;
eip = eip + 5;
eip = eip & 0xffffffff;
print(eip);
//根据opcode计算跳转地址
sum = sum + 1;
just_jump = 1;
continue;
}
else if(Byte(eip) == 0x0F && Byte(eip+1) == 0x84 && just_jump == 0)
{ //当遇到永恒跳转花指令
last_eip = eip;
offset = Byte(eip+2);
offset = Byte(eip+3) * 256 + offset;
offset = Byte(eip+4) * 256 * 256 + offset;
offset = Byte(eip+5) * 256 * 256 * 256 + offset;
eip = eip + offset;
eip = eip + 6;
eip = eip & 0xffffffff;
//根据opcode计算跳转地址
print(eip);
sum = sum + 1;
just_jump = 1;
PatchByte(last_eip, 0x90);
PatchByte(last_eip+1, 0xE9);
//将永恒跳转花指令替换为jmp
continue;
}
else if(Byte(eip) == 0xEB && just_jump == 0)
{ //当遇到短跳转jmp指令 jmp short
offset = Byte(eip+1);
eip = eip + offset;
eip = eip + 2;
// //根据opcode计算跳转地址
print(eip);
sum = sum + 1;
just_jump = 1;
continue;
}
else if(Byte(eip) == 0x74 && just_jump == 0)
{ //这里应该同理,但是翻了一下没找到这个对应花指令,去的差不多就行了,jz short
last_eip = eip;
offset = Byte(eip+1);
eip = eip + offset;
eip = eip + 2;
print(eip);
sum = sum + 1;
just_jump = 1;
PatchByte(last_eip, 0xEB);
continue;
}
eip = eip + 1;
just_jump = 0;
}
print(sum);
return 0;
}

image-20250707113307716

出现这样的问题,不知道怎么解决

法四:

x32dbgtrace:

单步trace把跟踪的结果导出为txt,写脚本过滤

1
2
3
4
5
6
with open('./1.txt','rb+') as f:
for line_1 in f:
if b"call" in line_1 or b"[esp" in line_1 or b"je" in line_1 or b"ret" in line_1 or b"jmp" in line_1 or b"jne" in line_1:
pass
else:
print(line_1)

可以得到:

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
push ebp                    
push ebp
mov ebp,esp
sub esp,10C
push ebx
push esi
push edi
mov dword ptr ss:[ebp-8],32
mov dword ptr ss:[ebp-44],0
mov eax,4
imul ecx,eax,B
mov edx,dword ptr ds:[ecx+4234
mov dword ptr ss:[ebp-38],edx
mov eax,dword ptr ss:[ebp-44]
sub eax,5B4B9F9E
mov dword ptr ss:[ebp-44],eax
mov eax,dword ptr ss:[ebp-44]
shr eax,2
and eax,3
mov dword ptr ss:[ebp-20],eax
mov dword ptr ss:[ebp-14],0
cmp dword ptr ss:[ebp-14],B
jae destination.416302
mov eax,dword ptr ss:[ebp-14]
mov ecx,dword ptr ds:[eax*4+42
mov dword ptr ss:[ebp-2C],ecx
mov eax,dword ptr ss:[ebp-38]
shr eax,5
mov ecx,dword ptr ss:[ebp-2C]
shl ecx,2
xor eax,ecx
mov edx,dword ptr ss:[ebp-2C]
shr edx,3
mov ecx,dword ptr ss:[ebp-38]
shl ecx,4
xor edx,ecx
add eax,edx
mov edx,dword ptr ss:[ebp-44]
xor edx,dword ptr ss:[ebp-2C]
mov ecx,dword ptr ss:[ebp-14]
and ecx,3
xor ecx,dword ptr ss:[ebp-20]
mov ecx,dword ptr ds:[ecx*4+42
xor ecx,dword ptr ss:[ebp-38]
add edx,ecx
xor eax,edx
mov edx,dword ptr ss:[ebp-14]
mov ecx,dword ptr ds:[edx*4+42
add ecx,eax
mov dword ptr ss:[ebp-10C],ecx
mov edx,dword ptr ss:[ebp-14]
mov eax,dword ptr ss:[ebp-10C]
mov dword ptr ds:[edx*4+4234A8
mov ecx,dword ptr ss:[ebp-10C]
mov dword ptr ss:[ebp-38],ecx
mov eax,dword ptr ss:[ebp-14]
add eax,1
mov dword ptr ss:[ebp-14],eax
cmp dword ptr ss:[ebp-14],B
jae destination.416302

可以判断这是一个xxtea算法,轮次为11,delta为0x5B4B9F9E

结合其中几个地址:00415B41 | mov esi,dword ptr ds:[esi*4+42309C] |

可以猜到这可能是key

我们动调可以发现push ebp经过两次,说明加密了两次

让GPT还原下代码:

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
#include <stdint.h>

void encrypt(uint32_t* v, int B, uint32_t* key_table) {
uint32_t rounds = 32;
uint32_t delta = 0;
uint32_t acc = v[B - 1]; // ebp-38 初始值为最后一个元素
uint32_t sum = 0;

while (rounds--) {
sum -= 0x5B4B9F9E; // 每轮更新 delta
uint32_t key_index = ((sum >> 2) & 3); // ebp-20
for (int i = 0; i < B; i++) {
uint32_t y = v[(i + 1) % B];
uint32_t z = v[i];

// 这里是混合逻辑,模拟汇编中的变换:
uint32_t temp1 = (acc >> 5) ^ (z << 2);
uint32_t temp2 = (z >> 3) ^ (acc << 4);
uint32_t temp3 = ((sum ^ z) + (key_table[(i & 3) ^ key_index] ^ acc));

uint32_t val = z + (temp1 + temp2) ^ temp3;
v[i] = val;
acc = val;
}
}
}

直接写解密代码

image-20250708145806665

解出来一堆乱码

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
#include <stdio.h>
#include <stdint.h>

#define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)))
#define DELTA 0x5B4B9F9E

void xxtea_decrypt(uint32_t* v, int n, uint32_t const key[4]) {
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) {
rounds = 0x32;
sum = 0 - rounds * DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p = 11; p > 0; p--) {
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum += DELTA;
} while (--rounds);
}
}

int main() {
uint32_t v[12] = { 0x9e549543,0x5e7cb348,0xd9a84a2f,0x85eb99de,0xb6825884,0xc4f74ea1,0x22b1828a,0x290d7296,0x198ee473,0x9655b529,0x38ac196a,0x192b6236 };
uint32_t key[4] = { 0x6B0E7A6B, 0x0D13011EE, 0x0A7E12C6D, 0x0C199ACA6 };

xxtea_decrypt(v, 12, key);
xxtea_decrypt(v, 12, key);

unsigned char* flag = (unsigned char*)v;
for (int i = 0; i < 12; i++)
{
printf("%c%c%c%c", *((char*)&v[i] + 0), *((char*)&v[i] + 1), *((char*)&v[i] + 2), *((char*)&v[i] + 3));
}

return 0;
}

猜测密文可能有点问题,交叉引用了下

image-20250708150041012

发现

image-20250708150055040

连GPT都看不懂做了什么,跟我说无法逆向

image-20250708151047096

得知这里运用了天堂之门的的技术,通过修改 CS 寄存器实现 32 位到 64 位代码的切换,用于反调试(无法跟进)

天堂之门 (WoW64 技术) 总结及 CTF 中的分析 - CTF 对抗 - 看雪 - 安全社区 | 安全招聘 | kanxue.com

[原创] 天堂之门 (Heaven’s Gate) C 语言实现 - 软件逆向 - 看雪 - 安全社区 | 安全招聘 | kanxue.com

我们可以用 x32dbg 再打开程序

参考[DubheCTF 2024]Destination – Arnold’s Blog

image-20250708151342400

难怪运行时候总是显示:

image-20250708152118455

调用了这个dll

我们只需要将opcode dump出来然后patch到64位程序中,再去生成函数即可还原

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
code = [0x55, 0x8B, 0xEC, 0x81, 0xEC, 0xC0, 0x0, 0x0, 0x0,
0x53, 0x56, 0x57, 0x8B, 0xFD, 0x33, 0xC9, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC,
0xF3, 0xAB, 0x57, 0x56, 0x41, 0x56, 0x41, 0x57, 0x48, 0x31, 0xFF,
0x8B, 0x34, 0xBD, 0xA8, 0x34, 0x42, 0x0, 0x4D, 0x31, 0xF6, 0x49, 0x89, 0xF7,
0x49, 0xC1, 0xEF, 0x1F, 0x49, 0x83, 0xFF, 0x1, 0x75, 0xA,
0xD1, 0xE6, 0x81, 0xF6, 0x2F, 0x97, 0xA6, 0x84, 0xEB, 0x2, 0xD1, 0xE6,
0x49, 0xFF, 0xC6, 0x49, 0x83, 0xFE, 0x20, 0x75, 0xDE,
0x89, 0x34, 0xBD, 0xA8, 0x34, 0x42, 0x0,
0x48, 0xFF, 0xC7, 0x48, 0x83, 0xFF, 0xC, 0x75, 0xC4,
0x41, 0x5F, 0x41, 0x5E, 0x5E, 0x5F, 0x6A, 0x23, 0x68, 0xF3, 0x79, 0x41, 0x0,
0x48, 0xCB,
0x5F, 0x5E, 0x5B, 0x81, 0xC4, 0xC0, 0x0, 0x0, 0x0,
0x3B, 0xEC, 0xE8, 0x3B, 0xD3, 0xFF, 0xFF, 0x8B, 0xE5, 0x5D, 0xC3]

# 转换为以空格分隔的十六进制字符串
hex_string = ' '.join(f'{b:02X}' for b in code)

print(hex_string)

Online x86 and x64 Intel Instruction Assembler

image-20250708153103014

再去IDA试试

image-20250708154423913

其实就是

1
2
3
4
5
6
7
// 每个 32-bit 数据(12 个)执行如下循环:
for (i = 0; i < 32; ++i) {
if (v >> 31 == 1) // 如果最高位是 1(符号位)
v = (v << 1) ^ 0x84A6972F;
else
v = v << 1;
}

像一个CRC,首先判断符号位,如果为1,先左移一位,在异或。如果不为1,则直接左移一位。这里的异或数据末尾为1,所以和左移一位后(最低位为0)的数据异或后末尾为1。直接左移的末尾为0。可以根据此将32位还原

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
#include<stdio.h>

int main()
{
unsigned int cpdata[12] = { 0xA790FAD6, 0xE8C8A277, 0xCF0384FA, 0x2E6C7FD7, 0x6D33968B, 0x5B57C227, 0x653CA65E, 0x85C6F1FC, 0xE1F32577, 0xD4D7AE76, 0x3FAF6DC4, 0x0D599D8C };

for (int i = 0; i < 12; i++) {
unsigned int tmp = cpdata[i];
for (int j = 0; j < 32; j++){
if (tmp & 0x1 == 1) //末位为1,80000000 即是1000 0000 0000 0000 0000 0000 0000 0000
{
tmp = ((tmp ^ 0x84A6972F) >> 1) | 0x80000000;
}
else{
tmp = tmp >> 1;
}
}
cpdata[i] = tmp;
}

for (int i = 0; i < 12; i++){
printf("0x%x,", cpdata[i]);
}
}

//0x9e549543,0x5e7cb348,0xd9a84a2f,0x85eb99de,0xb6825884,0xc4f74ea1,0x22b1828a,0x290d7296,0x198ee473,0x9655b529,0x38ac196a,0x192b6236

最终代码:

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
#include <stdio.h>
#include <stdint.h>

#define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)))
#define DELTA 0x5B4B9F9E

void xxtea_decrypt(uint32_t* v, int n, uint32_t const key[4]) {
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) {
rounds = 0x32;
sum = 0 - rounds * DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p = 11; p > 0; p--) {
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum += DELTA;
} while (--rounds);
}
}

int main() {
uint32_t v[12] = { 0x9e549543,0x5e7cb348,0xd9a84a2f,0x85eb99de,0xb6825884,0xc4f74ea1,0x22b1828a,0x290d7296,0x198ee473,0x9655b529,0x38ac196a,0x192b6236 };
uint32_t key[4] = { 0x6B0E7A6B, 0x0D13011EE, 0x0A7E12C6D, 0x0C199ACA6 };

xxtea_decrypt(v, 12, key);
xxtea_decrypt(v, 12, key);

unsigned char* flag = (unsigned char*)v;
for (int i = 0; i < 12; i++)
{
printf("%c%c%c%c", *((char*)&v[i] + 0), *((char*)&v[i] + 1), *((char*)&v[i] + 2), *((char*)&v[i] + 3));
}

return 0;
}

DubheCTF{82e1e3f8-85fe469f-8499dd48-466a9d60}

ezVK

Vulkan 是一种现代、高性能的图形和计算 API,由 Khronos Group 开发。它被设计为跨平台的解决方案,适用于高效利用 GPU 的应用程序,例如游戏、虚拟现实、3D 渲染和科学计算。

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
int __fastcall main_0(int argc, const char **argv, const char **envp)
{
v3 = &v9;
// 将 v9 开始的内存初始化为 0xCCCCCCCC,用于调试初始化(152 次迭代,应该是清理某个栈缓冲区)
for ( i = 152LL; i; --i )
{
*(_DWORD *)v3 = 0xCCCCCCCC;
v3 += 4;
}

// 检查是否处于调试状态(JMC 模式)
j___CheckForDebuggerJustMyCode(&unk_1400280AD, argv, envp);

// 打印提示信息,提示用户输入 Flag
print_message("Input Your Flag:");

// 读取用户输入的字符串到 g_userInputFlag 中
read_input("%s", g_userInputFlag);

// 获取用户输入的字符串长度
v10 = j_strlen(g_userInputFlag);

// 如果输入长度为 42 个字符
if ( j_strlen(g_userInputFlag) == 42 )
{
v11 = 0;
v12 = 0;

// 每 4 字节一组,将输入字符串转换为 DWORD 数组存储在 g_inputFlagAsDwords 中
while ( v11 < v10 )
{
g_inputFlagAsDwords[v12] = *(_DWORD *)&g_userInputFlag[v11];
v11 += 4;
++v12;
}

// 初始化 Vulkan 上下文
init_vulkan_context(v13, 3LL);

v19[0] = 0;
// 从资源中加载名为 "ezVK" 的 Shader(着色器)
v20 = load_shader_from_resource("ezVK", v19);

// 准备创建 Shader Module 的结构体 buf,并设置参数
memset(buf, 0, 0x28uLL);
LODWORD(buf[0]) = 16;
buf[3] = v19[0];
buf[4] = v20;

// 创建 Vulkan 的 Shader Module(着色器模块)
ShaderModule = j_vkCreateShaderModule(v14, buf, 0LL, v16);
check_vulkan_result("vkCreateShaderModule", ShaderModule);

// 创建 Vulkan Pipeline(图形管线)
create_vulkan_pipeline(v14, v16[0], v16[1], v17);

// 计算输入的 DWORD 数量 + 1
j_1 = v10 / 4 + 1;
LODWORD(src[2]) = j_1;

// 创建 Vulkan Buffer(用于数据传输的缓冲区)
create_vulkan_buffer(v14, v18, src);

v24[0] = 0LL;
// 将 Vulkan 的 Buffer 映射到内存中,以写入数据
v7 = j_vkMapMemory(v14, src[0], 0LL, 4LL * SLODWORD(src[2]), 0, v24);
check_vulkan_result("vkMapMemory", v7);

// 将转换后的 DWORD 输入写入 Vulkan Buffer
j_memcpy(v24[0], g_inputFlagAsDwords, 4LL * SLODWORD(src[2]));

// 解除内存映射
j_vkUnmapMemory(v14, src[0]);

// 将 src 的 buffer 描述复制给 src_1,用于提交队列
qmemcpy(src_1, src, 0x18uLL);

// 提交任务到 Vulkan 队列(用于执行 Shader)
submit_to_vulkan_queue(v14, v15, 0LL, src_1);

// 执行 Compute Shader 进行运算处理
dispatch_vulkan_compute_shader(v13, (unsigned int)(j_1 / 2 + 1));

// 再次将 Vulkan 的 Buffer 映射到内存中,以读取计算结果
v8 = j_vkMapMemory(v14, src[0], 0LL, 4LL * SLODWORD(src[2]), 0, v24);
check_vulkan_result("vkMapMemory", v8);

v25 = v24[0]; // 指向 Shader 输出的结果
j_vkUnmapMemory(v14, src[0]); // 解除映射

// 释放 Vulkan 使用的资源
j_vkFreeMemory(v14, src[0], 0LL);
j_vkDestroyBuffer(v14, src[1], 0LL);
cleanup_vulkan_resources(v13);

// 遍历比较输出结果与程序中预设的加密 Flag 数据
for ( j = 0; j < j_1; ++j )
{
if ( g_encryptedFlag[j] != v25[j] )
goto LABEL_5; // 一旦有一项不匹配,跳转输出错误
}

// 所有项都匹配,输出成功信息
print_message("You Are GENIUS!!!\n");
return 0;
}
else
{
LABEL_5:
// 输出错误信息
print_message("Wrong.\n");
return 0;
}
}

CTF 比赛常用 GPU 做验证计算,增加逆向难度。可以考虑对 Vulkan shader 部分进行提取和分析,或用 vulkan SDK 做运行态 hook/debug。

Shader(着色器)是一种在图形处理单元(GPU)上运行的程序,它负责对图形进行渲染处理,或者执行并行计算任务。简单来说,它是一段专门为 GPU 编写的代码,用来告诉 GPU 如何处理输入数据并输出图像或计算结果

需要反编译Shader

SPIR-V(Standard Portable Intermediate Representation - Vulkan)是由 Khronos Group 制定的一种中间语言,它是专门为 GPU 编程设计的,用于表示 Shader 和计算内核程序

就像 CPU 的汇编语言一样,SPIR-V 是 GPU 的“字节码”或“汇编语言”。

找到了GitHub - KhronosGroup/SPIRV-Cross:SPIRV-Cross 是一个实用的工具和库,用于对 SPIR-V 执行反射并将 SPIR-V 反汇编回高级语言。这个工具

resorce hacker导出shader

image-20250709203749231

image-20250718203614828

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
#version 450
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

const uint _80[5] = uint[](1214346853u, 558265710u, 559376756u, 1747010677u, 1651008801u);

layout(binding = 0, std430) buffer V
{
uint v[];
} _23;

void main()
{
uint cnt = gl_GlobalInvocationID.x * 2u;
uint sum = 0u;
uint l = _23.v[cnt];
uint r = _23.v[cnt + 1u];
for (int i = 1; i <= 40; i++)
{
l += ((((((~(r << uint(3))) & (r >> uint(5))) | ((r << uint(3)) & (~(r >> uint(5))))) ^ (~r)) & ((r << uint(3)) ^ (r >> uint(5)))) ^ ((~((~(sum + _80[sum & 4u])) | (~((r >> uint(3)) & (r << uint(2)))))) & (l | (~l))));
sum += 1932555628u;
r += ((((((~(l << uint(3))) & (l >> uint(5))) | ((l << uint(3)) & (~(l >> uint(5))))) ^ (~l)) & ((l << uint(3)) ^ (l >> uint(5)))) ^ ((~((~(sum + _80[(sum >> uint(11)) & 4u])) | (~((l >> uint(3)) & (l << uint(2)))))) & (r | (~r))));
}
_23.v[cnt] = l;
_23.v[cnt + 1u] = 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
#include <iostream>
int main()
{
unsigned int l, r;
unsigned int sum = 0;

const unsigned int _80[5] = { 1214346853, 558265710, 559376756, 1747010677, 1651008801 };

unsigned int data[12] = {
0x185B72AF, 0x0631D2C6, 0xDE8B33CC, 0x31EBCD9F, 0x05DB8B33, 0x0A8D77D0, 0x865C6111, 0xBF032335,
0x722228A5, 0xAD833A57, 0xB7C3456F ,0};

l = data[0], r = data[1];
for (int i = 0; i < 5; i++)
{
l = data[2 * i], r = data[2 * i + 1];
sum = 1932555628 * 40;
for (int i = 1; i <= 40; i++)
{
r -= ((((((~(l << 3)) & (l >> 5)) | ((l << 3) & (~(l >> 5)))) ^ (~l)) & ((l << 3) ^ (l >> 5))) ^ ((~((~(sum + _80[(sum >> 11) & 4u])) | (~((l >> 3) & (l << 2))))) & (r | (~r))));
sum -= 1932555628;
l -= ((((((~(r << 3)) & (r >> 5)) | ((r << 3) & (~(r >> 5)))) ^ (~r)) & ((r << 3) ^ (r >> 5))) ^ ((~((~(sum + _80[sum & 4u])) | (~((r >> 3) & (r << 2))))) & (l | (~l))));
}
printf("%c%c%c%c", l & 0xff, (l >> 8) & 0xff, (l >> 16) & 0xff, (l >> 24) & 0xff);
printf("%c%c%c%c", r & 0xff, (r >> 8) & 0xff, (r >> 16) & 0xff, (r >> 24) & 0xff);
}

for (int j = 0; j < 0xffff; j++)
{
l = j, r = 0;
unsigned int sum = 0;
for (int i = 1; i <= 40; i++)
{
l += ((((((~(r << 3)) & (r >> 5)) | ((r << 3) & (~(r >> 5)))) ^ (~r)) & ((r << 3) ^ (r >> 5))) ^ ((~((~(sum + _80[sum & 4u])) | (~((r >> 3) & (r << 2))))) & (l | (~l))));
sum += 1932555628u;
r += ((((((~(l << 3)) & (l >> 5)) | ((l << 3) & (~(l >> 5)))) ^ (~l)) & ((l << 3) ^ (l >> 5))) ^ ((~((~(sum + _80[(sum >> 11) & 4u])) | (~((l >> 3) & (l << 2))))) & (r | (~r))));
}
if (l == 0xB7C3456F)
{
printf("%c%c", j & 0xff, (j >> 8) & 0xff);
return 0;
}
}
}

最后一个字节为0,被遮掩了,需要爆破,注意长度为42也就是我们只需要爆破两字节即可,但是最后一位肯定是}其实我们只需爆破一个字节

l的变化与r有关,因此我们只需要爆破最后与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
#include <stdint.h>
#include <stdio.h>

void decrypt(uint32_t v[2]) {
uint32_t sum = 40*1932555628u;
uint32_t l = v[0];
uint32_t r = v[1];
uint32_t _80[5] = {1214346853u, 558265710u, 559376756u, 1747010677u, 1651008801u};
for (size_t i = 0; i < 40; i++)
{
r -= ((((((~(l << 3)) & (l >> 5)) | ((l << 3) & (~(l >> 5)))) ^ (~l)) & ((l << 3) ^ (l >> 5))) ^ ((~((~(sum + _80[(sum >> 11) & 4u])) | (~((l >> 3) & (l << 2))))) & (r | (~r))));
sum -= 1932555628u;
l -= ((((((~(r << 3)) & (r >> 5)) | ((r << 3) & (~(r >> 5)))) ^ (~r)) & ((r << 3) ^ (r >> 5))) ^ ((~((~(sum + _80[sum & 4u])) | (~((r >> 3) & (r << 2))))) & (l | (~l))));
}
v[0] = l;
v[1] = r;
}

void encrypt(uint32_t v[2]) {
uint32_t sum = 0;
uint32_t l = v[0];
uint32_t r = v[1];
uint32_t _80[5] = {1214346853u, 558265710u, 559376756u, 1747010677u, 1651008801u};
for (size_t i = 0; i < 40; i++)
{
l += ((((((~(r << 3)) & (r >> 5)) | ((r << 3) & (~(r >> 5)))) ^ (~r)) & ((r << 3) ^ (r >> 5))) ^ ((~((~(sum + _80[sum & 4u])) | (~((r >> 3) & (r << 2))))) & (l | (~l))));
sum += 1932555628u;
r += ((((((~(l << 3)) & (l >> 5)) | ((l << 3) & (~(l >> 5)))) ^ (~l)) & ((l << 3) ^ (l >> 5))) ^ ((~((~(sum + _80[(sum >> 11) & 4u])) | (~((l >> 3) & (l << 2))))) & (r | (~r))));
}
v[0] = l;
v[1] = r;
}

int main() {
uint32_t enc[12] = {0x185B72AF, 0x0631D2C6, 0xDE8B33CC, 0x31EBCD9F, 0x05DB8B33, 0x0A8D77D0, 0x865C6111, 0xBF032335, 0x722228A5, 0xAD833A57, 0xB7C3456F, 0};
for (size_t i = 0; i < 10; i+=2)
{
decrypt(&enc[i]);
}
char table[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%%&\'()*+,-./:;<=>?@[\\]^_`{|}~";
for (size_t i = 0; i < 96; i++)
{
char temp[8] = {0};
temp[0] = table[i];
temp[1] = '}';
encrypt((uint32_t*)temp);
if (*(uint32_t*)temp == enc[10]) {
printf("Found: %c\n", table[i]);
}
}
printf("%s\n", enc);
}

其它方法动调,也可以得到shader.bin

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
import idaapi

# Function to dump memory range to a file
def dump_memory(start_addr, size, file_path):
# Open the file for writing in binary mode
with open(file_path, "wb") as f:
# Iterate over each byte in the range and write it to the file
for i in range(size):
# Read a byte from the current address
byte = idaapi.get_byte(start_addr + i)
# Write the byte to the file
f.write(byte.to_bytes(1, 'little'))

print(f"Dumped memory range from {hex(start_addr)} to {hex(start_addr + size)} into {file_path}")

# Example usage:
# Replace 'start_addr' with the starting address of the memory range you want to dump
# Replace 'size' with the size of the memory range in bytes
# Replace 'output_file_path' with the path to the file where you want to write the memory dump
start_addr = 0x7FF6063AA1D0 # Replace with the actual start address
size = 0xDB4 # Replace with the actual size
output_file_path = "E:\\memory_dump.bin" # Replace with your desired file path

# Call the function to perform the memory dump
dump_memory(start_addr, size, output_file_path)

fffffragment

参考了星盟的wp,奈何后半段代码写的有点抽象,我优化了下DubheCTF2024 Writeup - 星盟安全团队

image-20250728114127653

这个调用…

找了下没有so层,只能这样分析了

当前是做了混淆,putString每次都会打印出东西来

跟进去

image-20250728145958770

发现有两个选择,共同点是putString后会到下一个class

整体思路是一路追踪字符串在Bundle中的传递和拼接,还原出最终的明文数据

我们发现无论分支怎么走都在package p013II111;

搜索Wrong和congraluate,eh0的上一个是q70,也就是说当到了这样一条线肯定是失败了,其实下面的脚本光q70就够了,因为q70的上游是有多个,eh0的上游只有q70

image-20250728155646769

image-20250728155727684

jadx导出工程,这里是要到Congratulations这里,2个按钮分为向左向右走,最终走到Congratulations这里,手搓函数太多了不太可能

写一个脚本追踪一下路径

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
import os
import re

# 指定要遍历的目录路径
directory = "./java/p013II111"
good_name = 'ch0.java'
bad_name = ['eh0.java', 'q70.java']
# C0116Oo0o0 sub_0.java

def read_java(name):
java_file_path = name
try:
with open(java_file_path, 'rb') as file:
return file.read()

except FileNotFoundError:
print("File not found.")
except IOError:
print("Error reading the file.")


def get_next_file_name(context):
pattern = r", (\w+)\.class"
x = re.findall(pattern.encode(), context)
# print(x)
b = [(i + b'.java').decode() for i in x]
a_ = b
for i in range(len(b)):
for j in range(len(b)):
if b[i] == b[j] and i != j:
a_ = []
return a_


def get_next_road(name, path):
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith(".java"):
# 如果文件是以 .java 结尾的,则打印文件路径
# print(file)
if file == name:
x = (os.path.join(root, file).replace('\\', '/'))
data = read_java(x)
name1 = get_next_file_name(data)
for i in name1:
if good_name == i:
print('found')
path.append(i)
print(path)
elif i in bad_name:
pass
else:
get_next_road(i, path + [i])

path = []
path.append('MainActivity.java')
path.append('C0116Oo0o0.java')
# print(path)

# path = 'MainActivity.java C0116Oo0o0.java'

get_next_road('C0116Oo0o0.java', path)

找到这样一条路径

1
2
found
['MainActivity.java', 'C0116Oo0o0.java', 'C1073y6.java', 'C0822re.java', 'C1082yf.java', 'C0334fh.java', 'C0604mi.java', 'C0901tj.java', 'C0154al.java', 'C0413hm.java', 'C0683on.java', 'C0118O0ooO.java', 'C0102O0ooO.java', 'C0696o0Ooo.java', 'C0390h.java', 'C0660o0.java', 'C0957v1.java', 'C0209c3.java', 'C0468j4.java', 'C0776q5.java', 'C1036x6.java', 'C0399h8.java', 'C0669o9.java', 'C0966va.java', 'C0218cc.java', 'C0477jd.java', 'C0294ee.java', 'C0405he.java', 'C0515ke.java', 'C0637ne.java', 'C0785qe.java', 'C1007we.java', 'C1118ze.java', 'C0221cf.java', 'C0332ff.java', 'C0443if.java', 'C0553lf.java', 'C0675of.java', 'C0823rf.java', 'C0934uf.java', 'C1045xf.java', 'C0259dg.java', 'C0370gg.java', 'C0480jg.java', 'C0602mg.java', 'C0750pg.java', 'C0861sg.java', 'C0972vg.java', 'C1083yg.java', 'C0187bh.java', 'C0297eh.java', 'C0518kh.java', 'C0640nh.java', 'C0788qh.java', 'C0899th.java', 'C1010wh.java', 'C1121zh.java', 'C0224ci.java', 'C0335fi.java', 'C0446ii.java', 'C0556li.java', 'C0826ri.java', 'C0937ui.java', 'C1048xi.java', 'C0152aj.java', 'C0262dj.java', 'C0373gj.java', 'C0483jj.java', 'C0605mj.java', 'C0753pj.java', 'C0864sj.java', 'C1086yj.java', 'C0190bk.java', 'C0300ek.java', 'C0411hk.java', 'C0521kk.java', 'C0643nk.java', 'C0791qk.java', 'C0902tk.java', 'C1013wk.java', 'C1124zk.java', 'C0338fl.java', 'C0449il.java', 'C0559ll.java', 'C0681ol.java', 'C0829rl.java', 'C0940ul.java', 'C1051xl.java', 'C0155am.java', 'C0265dm.java', 'C0376gm.java', 'C0608mm.java', 'C0756pm.java', 'C0867sm.java', 'C0978vm.java', 'C1089ym.java', 'C0193bn.java', 'C0303en.java', 'C0414hn.java', 'C0524kn.java', 'C0646nn.java', 'C0109OoOOO.java', 'C0117O0OOo.java', 'C0126OoOoo.java', 'C0086Il1.java', 'C0698oo00O.java', 'C0714ooO00.java', 'C0728oOO00.java', 'C0699o00OO.java', 'C0107Ooo0O.java', 'C0080I11I.java', 'C0083I11.java', 'C0128Ooo00.java', 'C0108O0O0o.java', 'C0103OOoOo.java', 'C0088Il111.java', 'C0082II1l.java', 'C0131O0OoO.java', 'C0705oOo0o.java', 'C0719oOo0O.java', 'C0702ooOoo.java', 'C0079IlIl.java', 'C0579l1lI.java', 'C1140I111.java', 'C0710oooo0.java', 'C0581lIII.java', 'C0717oo0o0.java', 'C0726oOooo.java', 'C0119O0000.java', 'C0715o0O0o.java', 'C0122OO00O.java', 'C0580lll.java', 'OooOo.java', 'll1I.java', 'oO000.java', 'C3043oOo0o.java', 'oOOO0.java', 'C3042oooOo.java', 'C0132a.java', 'C0242d.java', 'C0353g.java', 'C0585m.java', 'C0733p.java', 'C0844s.java', 'C0955v.java', 'C1066y.java', 'C0170b0.java', 'C0280e0.java', 'C0391h0.java', 'C0501k0.java', 'C0623n0.java', 'C0882t0.java', 'C0993w0.java', 'C1104z0.java', 'C0207c1.java', 'C0318f1.java', 'C0429i1.java', 'C0539l1.java', 'C0661o1.java', 'C0809r1.java', 'C0920u1.java', 'C0135a2.java', 'C0245d2.java', 'C0356g2.java', 'C0466j2.java', 'C0588m2.java', 'C0736p2.java', 'C0847s2.java', 'C0958v2.java', 'C1069y2.java', 'C0173b3.java', 'C0394h3.java', 'C0504k3.java', 'C0626n3.java', 'C0774q3.java', 'C0885t3.java', 'C0996w3.java', 'C1107z3.java', 'C0210c4.java', 'C0321f4.java', 'C0432i4.java', 'C0664o4.java', 'C0812r4.java', 'C0923u4.java', 'C1034x4.java', 'C0138a5.java', 'C0248d5.java', 'C0359g5.java', 'C0469j5.java', 'C0591m5.java', 'C0739p5.java', 'C0961v5.java', 'C1072y5.java', 'C0176b6.java', 'C0286e6.java', 'C0397h6.java', 'C0507k6.java', 'C0629n6.java', 'C0777q6.java', 'C0888t6.java', 'C0999w6.java', 'C0324f7.java', 'C0435i7.java', 'C0545l7.java', 'C0667o7.java', 'C0815r7.java', 'C0926u7.java', 'C1037x7.java', 'C0141a8.java', 'C0251d8.java', 'C0362g8.java', 'C0594m8.java', 'C0742p8.java', 'C0853s8.java', 'C0964v8.java', 'C1075y8.java', 'C0179b9.java', 'C0289e9.java', 'C0400h9.java', 'C0510k9.java', 'C0632n9.java', 'C0891t9.java', 'C1002w9.java', 'C1113z9.java', 'C0216ca.java', 'C0327fa.java', 'C0438ia.java', 'C0548la.java', 'C0670oa.java', 'C0818ra.java', 'C0929ua.java', 'C0144ab.java', 'C0254db.java', 'C0365gb.java', 'C0475jb.java', 'C0597mb.java', 'C0745pb.java', 'C0856sb.java', 'C0967vb.java', 'C1078yb.java', 'C0182bc.java', 'C0403hc.java', 'C0513kc.java', 'C0635nc.java', 'C0783qc.java', 'C0894tc.java', 'C1005wc.java', 'C1116zc.java', 'C0219cd.java', 'C0330fd.java', 'C0441id.java', 'C0673od.java', 'C0821rd.java', 'C0932ud.java', 'C1043xd.java', 'C0147ae.java', 'C0257de.java', 'ch0.java']

接下来我们把putString搞出来

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
80
81
import os
import re

# 指定要遍历的目录路径
directory = "./java/p013II111"
call_list = ['MainActivity.java', 'C0116Oo0o0.java', 'C1073y6.java', 'C0822re.java', 'C1082yf.java', 'C0334fh.java', 'C0604mi.java', 'C0901tj.java', 'C0154al.java', 'C0413hm.java', 'C0683on.java', 'C0118O0ooO.java', 'C0102O0ooO.java', 'C0696o0Ooo.java', 'C0390h.java', 'C0660o0.java', 'C0957v1.java', 'C0209c3.java', 'C0468j4.java', 'C0776q5.java', 'C1036x6.java', 'C0399h8.java', 'C0669o9.java', 'C0966va.java', 'C0218cc.java', 'C0477jd.java', 'C0294ee.java', 'C0405he.java', 'C0515ke.java', 'C0637ne.java', 'C0785qe.java', 'C1007we.java', 'C1118ze.java', 'C0221cf.java', 'C0332ff.java', 'C0443if.java', 'C0553lf.java', 'C0675of.java', 'C0823rf.java', 'C0934uf.java', 'C1045xf.java', 'C0259dg.java', 'C0370gg.java', 'C0480jg.java', 'C0602mg.java', 'C0750pg.java', 'C0861sg.java', 'C0972vg.java', 'C1083yg.java', 'C0187bh.java', 'C0297eh.java', 'C0518kh.java', 'C0640nh.java', 'C0788qh.java', 'C0899th.java', 'C1010wh.java', 'C1121zh.java', 'C0224ci.java', 'C0335fi.java', 'C0446ii.java', 'C0556li.java', 'C0826ri.java', 'C0937ui.java', 'C1048xi.java', 'C0152aj.java', 'C0262dj.java', 'C0373gj.java', 'C0483jj.java', 'C0605mj.java', 'C0753pj.java', 'C0864sj.java', 'C1086yj.java', 'C0190bk.java', 'C0300ek.java', 'C0411hk.java', 'C0521kk.java', 'C0643nk.java', 'C0791qk.java', 'C0902tk.java', 'C1013wk.java', 'C1124zk.java', 'C0338fl.java', 'C0449il.java', 'C0559ll.java', 'C0681ol.java', 'C0829rl.java', 'C0940ul.java', 'C1051xl.java', 'C0155am.java', 'C0265dm.java', 'C0376gm.java', 'C0608mm.java', 'C0756pm.java', 'C0867sm.java', 'C0978vm.java', 'C1089ym.java', 'C0193bn.java', 'C0303en.java', 'C0414hn.java', 'C0524kn.java', 'C0646nn.java', 'C0109OoOOO.java', 'C0117O0OOo.java', 'C0126OoOoo.java', 'C0086Il1.java', 'C0698oo00O.java', 'C0714ooO00.java', 'C0728oOO00.java', 'C0699o00OO.java', 'C0107Ooo0O.java', 'C0080I11I.java', 'C0083I11.java', 'C0128Ooo00.java', 'C0108O0O0o.java', 'C0103OOoOo.java', 'C0088Il111.java', 'C0082II1l.java', 'C0131O0OoO.java', 'C0705oOo0o.java', 'C0719oOo0O.java', 'C0702ooOoo.java', 'C0079IlIl.java', 'C0579l1lI.java', 'C1140I111.java', 'C0710oooo0.java', 'C0581lIII.java', 'C0717oo0o0.java', 'C0726oOooo.java', 'C0119O0000.java', 'C0715o0O0o.java', 'C0122OO00O.java', 'C0580lll.java', 'OooOo.java', 'll1I.java', 'oO000.java', 'C3043oOo0o.java', 'oOOO0.java', 'C3042oooOo.java', 'C0132a.java', 'C0242d.java', 'C0353g.java', 'C0585m.java', 'C0733p.java', 'C0844s.java', 'C0955v.java', 'C1066y.java', 'C0170b0.java', 'C0280e0.java', 'C0391h0.java', 'C0501k0.java', 'C0623n0.java', 'C0882t0.java', 'C0993w0.java', 'C1104z0.java', 'C0207c1.java', 'C0318f1.java', 'C0429i1.java', 'C0539l1.java', 'C0661o1.java', 'C0809r1.java', 'C0920u1.java', 'C0135a2.java', 'C0245d2.java', 'C0356g2.java', 'C0466j2.java', 'C0588m2.java', 'C0736p2.java', 'C0847s2.java', 'C0958v2.java', 'C1069y2.java', 'C0173b3.java', 'C0394h3.java', 'C0504k3.java', 'C0626n3.java', 'C0774q3.java', 'C0885t3.java', 'C0996w3.java', 'C1107z3.java', 'C0210c4.java', 'C0321f4.java', 'C0432i4.java', 'C0664o4.java', 'C0812r4.java', 'C0923u4.java', 'C1034x4.java', 'C0138a5.java', 'C0248d5.java', 'C0359g5.java', 'C0469j5.java', 'C0591m5.java', 'C0739p5.java', 'C0961v5.java', 'C1072y5.java', 'C0176b6.java', 'C0286e6.java', 'C0397h6.java', 'C0507k6.java', 'C0629n6.java', 'C0777q6.java', 'C0888t6.java', 'C0999w6.java', 'C0324f7.java', 'C0435i7.java', 'C0545l7.java', 'C0667o7.java', 'C0815r7.java', 'C0926u7.java', 'C1037x7.java', 'C0141a8.java', 'C0251d8.java', 'C0362g8.java', 'C0594m8.java', 'C0742p8.java', 'C0853s8.java', 'C0964v8.java', 'C1075y8.java', 'C0179b9.java', 'C0289e9.java', 'C0400h9.java', 'C0510k9.java', 'C0632n9.java', 'C0891t9.java', 'C1002w9.java', 'C1113z9.java', 'C0216ca.java', 'C0327fa.java', 'C0438ia.java', 'C0548la.java', 'C0670oa.java', 'C0818ra.java', 'C0929ua.java', 'C0144ab.java', 'C0254db.java', 'C0365gb.java', 'C0475jb.java', 'C0597mb.java', 'C0745pb.java', 'C0856sb.java', 'C0967vb.java', 'C1078yb.java', 'C0182bc.java', 'C0403hc.java', 'C0513kc.java', 'C0635nc.java', 'C0783qc.java', 'C0894tc.java', 'C1005wc.java', 'C1116zc.java', 'C0219cd.java', 'C0330fd.java', 'C0441id.java', 'C0673od.java', 'C0821rd.java', 'C0932ud.java', 'C1043xd.java', 'C0147ae.java', 'C0257de.java', 'ch0.java']

table = ['Bundle', 'package', 'import', 'View', '/* rena', 'null) {', 'view', 'iew', 'ew) {',"null","m6087OoOoo()"]

def read_java(name):
java_file_path = name
try:
with open(java_file_path, 'rb') as file:
return file.read()

except FileNotFoundError:
print("File not found.")
except IOError:
print("Error reading the file.")
abc = 1
def get_next_file_name(context, next_name, pos):
global abc
pattern = next_name
# print(next_name)
match = re.search(pattern.encode(), context)
start_pos = match.start()
content = context[max(start_pos - 650, 0):start_pos - 100].decode('utf-8')
if abc == 1:
print(content)
abc = 0
lines = content.split('\n')
java_header = '''
package out_java;

import out_java.C00591l1l;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
'''
class_name = f"f{pos}"
method_name = f"f{pos}"
java_code = f"{java_header}\nclass {class_name} {{\n"
java_code += f" public static String {method_name}(String str2) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {{\n"

for line in lines:
x = 1
for i in table:
if i in line:
x = 0
if x == 1:
if 'putString' in line:
if "str3 + str2" in line:
x = " str3 = str3 + str2;"
elif "str2 + str3" in line:
x = " str3 = str2 + str3;"
java_code += x + '\n'
else:
java_code += line + '\n'
java_code += (f' return str3;\n')
java_code += (' }\n}\n')
java_file_name = f'./out_java/f{pos}.java'
# print(java_file)
with open(java_file_name, 'w+') as f:
f.write(java_code)

def read_encode(name, path, pos):
path = path.replace('java', 'class')
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith(".java"):
if file == name:
x = (os.path.join(root, file).replace('\\', '/'))
data = read_java(x)
get_next_file_name(data, path, pos)

for i in range(1, 257):
read_encode(call_list[i], call_list[i + 1], i)

导入工程,然后手修一下错误就行

1
2
for i in range(1,257):
print(f'str=f{i}.f{i}(str);')
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
package out_java;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class Main {
public static void main(String[] args) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
String str = "";
str=f1.f1(str);
str=f2.f2(str);
str=f3.f3(str);
str=f4.f4(str);
str=f5.f5(str);
str=f6.f6(str);
str=f7.f7(str);
str=f8.f8(str);
str=f9.f9(str);
str=f10.f10(str);
str=f11.f11(str);
str=f12.f12(str);
str=f13.f13(str);
str=f14.f14(str);
str=f15.f15(str);
str=f16.f16(str);
str=f17.f17(str);
str=f18.f18(str);
str=f19.f19(str);
str=f20.f20(str);
str=f21.f21(str);
str=f22.f22(str);
str=f23.f23(str);
str=f24.f24(str);
str=f25.f25(str);
str=f26.f26(str);
str=f27.f27(str);
str=f28.f28(str);
str=f29.f29(str);
str=f30.f30(str);
str=f31.f31(str);
str=f32.f32(str);
str=f33.f33(str);
str=f34.f34(str);
str=f35.f35(str);
str=f36.f36(str);
str=f37.f37(str);
str=f38.f38(str);
str=f39.f39(str);
str=f40.f40(str);
str=f41.f41(str);
str=f42.f42(str);
str=f43.f43(str);
str=f44.f44(str);
str=f45.f45(str);
str=f46.f46(str);
str=f47.f47(str);
str=f48.f48(str);
str=f49.f49(str);
str=f50.f50(str);
str=f51.f51(str);
str=f52.f52(str);
str=f53.f53(str);
str=f54.f54(str);
str=f55.f55(str);
str=f56.f56(str);
str=f57.f57(str);
str=f58.f58(str);
str=f59.f59(str);
str=f60.f60(str);
str=f61.f61(str);
str=f62.f62(str);
str=f63.f63(str);
str=f64.f64(str);
str=f65.f65(str);
str=f66.f66(str);
str=f67.f67(str);
str=f68.f68(str);
str=f69.f69(str);
str=f70.f70(str);
str=f71.f71(str);
str=f72.f72(str);
str=f73.f73(str);
str=f74.f74(str);
str=f75.f75(str);
str=f76.f76(str);
str=f77.f77(str);
str=f78.f78(str);
str=f79.f79(str);
str=f80.f80(str);
str=f81.f81(str);
str=f82.f82(str);
str=f83.f83(str);
str=f84.f84(str);
str=f85.f85(str);
str=f86.f86(str);
str=f87.f87(str);
str=f88.f88(str);
str=f89.f89(str);
str=f90.f90(str);
str=f91.f91(str);
str=f92.f92(str);
str=f93.f93(str);
str=f94.f94(str);
str=f95.f95(str);
str=f96.f96(str);
str=f97.f97(str);
str=f98.f98(str);
str=f99.f99(str);
str=f100.f100(str);
str=f101.f101(str);
str=f102.f102(str);
str=f103.f103(str);
str=f104.f104(str);
str=f105.f105(str);
str=f106.f106(str);
str=f107.f107(str);
str=f108.f108(str);
str=f109.f109(str);
str=f110.f110(str);
str=f111.f111(str);
str=f112.f112(str);
str=f113.f113(str);
str=f114.f114(str);
str=f115.f115(str);
str=f116.f116(str);
str=f117.f117(str);
str=f118.f118(str);
str=f119.f119(str);
str=f120.f120(str);
str=f121.f121(str);
str=f122.f122(str);
str=f123.f123(str);
str=f124.f124(str);
str=f125.f125(str);
str=f126.f126(str);
str=f127.f127(str);
str=f128.f128(str);
str=f129.f129(str);
str=f130.f130(str);
str=f131.f131(str);
str=f132.f132(str);
str=f133.f133(str);
str=f134.f134(str);
str=f135.f135(str);
str=f136.f136(str);
str=f137.f137(str);
str=f138.f138(str);
str=f139.f139(str);
str=f140.f140(str);
str=f141.f141(str);
str=f142.f142(str);
str=f143.f143(str);
str=f144.f144(str);
str=f145.f145(str);
str=f146.f146(str);
str=f147.f147(str);
str=f148.f148(str);
str=f149.f149(str);
str=f150.f150(str);
str=f151.f151(str);
str=f152.f152(str);
str=f153.f153(str);
str=f154.f154(str);
str=f155.f155(str);
str=f156.f156(str);
str=f157.f157(str);
str=f158.f158(str);
str=f159.f159(str);
str=f160.f160(str);
str=f161.f161(str);
str=f162.f162(str);
str=f163.f163(str);
str=f164.f164(str);
str=f165.f165(str);
str=f166.f166(str);
str=f167.f167(str);
str=f168.f168(str);
str=f169.f169(str);
str=f170.f170(str);
str=f171.f171(str);
str=f172.f172(str);
str=f173.f173(str);
str=f174.f174(str);
str=f175.f175(str);
str=f176.f176(str);
str=f177.f177(str);
str=f178.f178(str);
str=f179.f179(str);
str=f180.f180(str);
str=f181.f181(str);
str=f182.f182(str);
str=f183.f183(str);
str=f184.f184(str);
str=f185.f185(str);
str=f186.f186(str);
str=f187.f187(str);
str=f188.f188(str);
str=f189.f189(str);
str=f190.f190(str);
str=f191.f191(str);
str=f192.f192(str);
str=f193.f193(str);
str=f194.f194(str);
str=f195.f195(str);
str=f196.f196(str);
str=f197.f197(str);
str=f198.f198(str);
str=f199.f199(str);
str=f200.f200(str);
str=f201.f201(str);
str=f202.f202(str);
str=f203.f203(str);
str=f204.f204(str);
str=f205.f205(str);
str=f206.f206(str);
str=f207.f207(str);
str=f208.f208(str);
str=f209.f209(str);
str=f210.f210(str);
str=f211.f211(str);
str=f212.f212(str);
str=f213.f213(str);
str=f214.f214(str);
str=f215.f215(str);
str=f216.f216(str);
str=f217.f217(str);
str=f218.f218(str);
str=f219.f219(str);
str=f220.f220(str);
str=f221.f221(str);
str=f222.f222(str);
str=f223.f223(str);
str=f224.f224(str);
str=f225.f225(str);
str=f226.f226(str);
str=f227.f227(str);
str=f228.f228(str);
str=f229.f229(str);
str=f230.f230(str);
str=f231.f231(str);
str=f232.f232(str);
str=f233.f233(str);
str=f234.f234(str);
str=f235.f235(str);
str=f236.f236(str);
str=f237.f237(str);
str=f238.f238(str);
str=f239.f239(str);
str=f240.f240(str);
str=f241.f241(str);
str=f242.f242(str);
str=f243.f243(str);
str=f244.f244(str);
str=f245.f245(str);
str=f246.f246(str);
str=f247.f247(str);
str=f248.f248(str);
str=f249.f249(str);
str=f250.f250(str);
str=f251.f251(str);
str=f252.f252(str);
str=f253.f253(str);
str=f254.f254(str);
str=f255.f255(str);
str=f256.f256(str);
System.out.println(str);
System.out.println();
}
}
1
71_FITz728fUFH3VM4mtnQCyf7mnFUkjUcpJER982SfiR_zAhsVTzUNh4mdEvoyqYMyoMt0WXSa2UX2SUNrm4YLz5k_DegEeO7RuK_SOfAwetgoXc4iGvpd9BYix9JpCAuuK7CXDppzvWHvWnw_5AmjRl_O4JXfqJ27Pwg8nHCawmSfna5KxHexQtmvqxsGetjFQ7eYueBCBd_7hsmQXrYolPhI39Q6FRrSHUSKs8AiLxdd3lqFfTl8Qvvpwt0aO7FTiNN0T3olLllHy026HCczgMc8Dt4HEZZ1zUjZ5AoeP5XkIts9H6Tda_cBKNKnaahj_FTs0FKqI0rUaClVgfvKDetQxDT7WOSONEQpqwU8MjCOG58GEv5dJevHnRnqFVAXn_dkXO3nwK7QVI6KuUoxge0csUHy3lor2V5pXWTv3goKrngMNCiUerF9V_NXoTWAWdZr_pHaAOPhQgmeyj1a08HRaaQdhyfiGa8IFV8wCJ_RVL2Bco2Ztu6AbyeQJjPDHbmjLQzJ0lCTI0bFO5f0F6xOaGDqsDqRDmWIBkVCrzRCxQNMxWQdylrwAtjlIo9wcs2_XqdO5_35nfjSuL_mY1lxhkrmWlHYFllXzNJBO8dxmlCQTOubH1F8snqTNvdU36b2aWKYPGRnMlboNEiX8xkht7qR3vSW_INOt_TpxiYvE5xdYUJk9armmk1CtpEYGqUx3sz2k6M3zB6qNNTW4cLsAJAkykhznpehsoBEvyIDP5hMJDSgf6utz

最后sha1一下

1
2
3
4
5
6
7
8
import hashlib

data = """71_FITz728fUFH3VM4mtnQCyf7mnFUkjUcpJER982SfiR_zAhsVTzUNh4mdEvoyqYMyoMt0WXSa2UX2SUNrm4YLz5k_DegEeO7RuK_SOfAwetgoXc4iGvpd9BYix9JpCAuuK7CXDppzvWHvWnw_5AmjRl_O4JXfqJ27Pwg8nHCawmSfna5KxHexQtmvqxsGetjFQ7eYueBCBd_7hsmQXrYolPhI39Q6FRrSHUSKs8AiLxdd3lqFfTl8Qvvpwt0aO7FTiNN0T3olLllHy026HCczgMc8Dt4HEZZ1zUjZ5AoeP5XkIts9H6Tda_cBKNKnaahj_FTs0FKqI0rUaClVgfvKDetQxDT7WOSONEQpqwU8MjCOG58GEv5dJevHnRnqFVAXn_dkXO3nwK7QVI6KuUoxge0csUHy3lor2V5pXWTv3goKrngMNCiUerF9V_NXoTWAWdZr_pHaAOPhQgmeyj1a08HRaaQdhyfiGa8IFV8wCJ_RVL2Bco2Ztu6AbyeQJjPDHbmjLQzJ0lCTI0bFO5f0F6xOaGDqsDqRDmWIBkVCrzRCxQNMxWQdylrwAtjlIo9wcs2_XqdO5_35nfjSuL_mY1lxhkrmWlHYFllXzNJBO8dxmlCQTOubH1F8snqTNvdU36b2aWKYPGRnMlboNEiX8xkht7qR3vSW_INOt_TpxiYvE5xdYUJk9armmk1CtpEYGqUx3sz2k6M3zB6qNNTW4cLsAJAkykhznpehsoBEvyIDP5hMJDSgf6utz"""

# 编码为 UTF-8 并计算 SHA1 摘要
sha1_hash = hashlib.sha1(data.encode("utf-8")).hexdigest()

print("flag:DubheCTF{" + sha1_hash + "}")

flag:DubheCTF{20c21afe96f05b02430a017a550bfce5addb6fe2}

还有其他方法:JEB脚本提取

此脚本适用于jeb3,python2,修改麻烦所以没有试

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
# ?description=
# ?shortcut=

import hashlib
import inspect
import re

from com.pnfsoftware.jeb.client.api import IClientContext, IGraphicalClientContext, IScript
from com.pnfsoftware.jeb.core import IRuntimeProject
from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit, IJavaMethod, IJavaStatement
from com.pnfsoftware.jeb.core.units.code.android import IDexUnit, IDexDecompilerUnit
from com.pnfsoftware.jeb.core.units.code.android.dex import IDexClass
from com.pnfsoftware.jeb.core.actions import ActionContext, ActionXrefsData, Actions

class TraceClassCrossRef(IScript):
def run(self, ctx):
# type: (IScript, IGraphicalClientContext) -> None
if not isinstance(ctx, IGraphicalClientContext):
return

proj = ctx.getMainProject() # type: IRuntimeProject
javaSourceUnit = ctx.getFocusedUnit() # type: IJavaSourceUnit
if not isinstance(javaSourceUnit, IJavaSourceUnit):
return

id = javaSourceUnit.getDexItem().getItemId()

self.cnt = 0
self.flag = ''

data = ActionXrefsData()
dexUnit = proj.findUnit(IDexUnit) # type: IDexUnit
javaSourceUnit = proj.findUnit(IJavaSourceUnit) # type: IJavaSourceUnit
self.traceBackRef(javaSourceUnit, dexUnit, data, id)

print()
print("Flag (reconstructed):", self.flag)

def traceBackRef(self, javaSourceUnit, dexUnit, data, id):
# type: (IJavaSourceUnit, IDexUnit, ActionXrefsData, int) -> None
if javaSourceUnit.prepareExecution(ActionContext(javaSourceUnit, Actions.QUERY_XREFS, id), data):
for xref_addr in data.getAddresses():
decompiler = javaSourceUnit.getDecompiler() # type: IDexDecompilerUnit
className = xref_addr.split('-')[0]
methodName = xref_addr.split('+')[0]

dexClass = dexUnit.getClass(className) # type: IDexClass
javaMethod = decompiler.getMethod(methodName, False) # type: IJavaMethod

if javaMethod is None:
decompiler.decompileMethod(methodName)
javaMethod = decompiler.getMethod(methodName, False)

id = dexClass.getItemId()
self.flag = self.getSliceInMethod(javaMethod) + self.flag
print(self.flag)

if "MainActivity" in xref_addr:
return
self.cnt += 1
self.traceBackRef(javaSourceUnit, dexUnit, data, id)
return

def getSliceInMethod(self, javaMethod):
# type: (IJavaMethod) -> str
javaStatements = javaMethod.getStatements() # type: List[IJavaStatement]
print("Number of statements:", len(javaStatements))
for javaStatement in javaStatements:
lineText = javaStatement.toString()
match = re.search(r'.*?putString.*?"(.*?)".*?', lineText)
if match:
return match.group(1)
print('No string found in method:', javaMethod)
return ''

手撕方案(这个太狠了😂)

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
ch0
C0257de 1
C0147ae 1
C1043xd 2
C0932ud 1
C0821rd 2
C0673od 2
C0441id 1
C0330fd 2
C0219cd 2
C1116zc 1
C1005wc 1
C0894tc 1
C0783qc 1
C0635nc 1
C0513kc 1
C0403hc 2
C0182bc 1
C1078yb 1
C0967vb 1
C0856sb 1
C0745pb 1
C0597mb 1
C0475jb 2
C0365gb 2
C0254db 1
C0144ab 1
C0929ua 2
C0818ra 2
C0670oa 2
C0548la 2
C0438ia 1
C0327fa 1
C0216ca 2
C1113z9 1
C1002w9 1
C0891t9 2
C0632n9 2
C0510k9 1
C0400h9 2
C0289e9 1
C0179b9 1
C1075y8 1
C0964v8 1
C0853s8 2
C0742p8 2
C0594m8 2
C0362g8 1
C0251d8 2
C0141a8 1
C1037x7 1
C0926u7 1
C0815r7 1
C0667o7 1
C0545l7 2
C0435i7 2
C0324f7 1
C0999w6 1
C0888t6 2
C0777q6 1
C0629n6 1
C0507k6 1
C0397h6 1
C0286e6 1
C0176b6 2
C1072y5 1
C0961v5 1
C0739p5 1
C0591m5 2
C0469j5 2
C0359g5 2
C0248d5 1
C0138a5 2
C1034x4 1
C0923u4 2
C0812r4 2
C0664o4 1
C0432i4 1
C0321f4 1
C0210c4 2
C1107z3 2
C0996w3 1
C0885t3 2
C0774q3 2
C0626n3 2
C0504k3 1
C0394h3 2
C0173b3 2
C1069y2 1
C0958v2 2
C0847s2 1
C0736p2 1
C0588m2 2
C0466j2 1
C0356g2 1
C0245d2 1
C0135a2 2
C0920u1 2
C0809r1 1
C0661o1 1
C0539l1 2
C0429i1 1
C0318f1 2
C0207c1 2
C1104z0 2
C0993w0 1
C0882t0 1
C0623n0 1
C0501k0 1
C0391h0 1
C0280e0 2
C0170b0 2
C1066y 2
C0955v 2
C0844s 1
C0733p 1
C0585m 1
C0353g 2
C0242d 1
C0132a 2
C3042oooOo 1
oOOO0 1
C3043oOo0o 2
oO000 2
ll1I 2
OooOo 1
C0580lll 1
C0122OO00O 1
C0715o0O0o 1
C0119O0000 1
C0726oOooo 2
C0717oo0o0 1
C0581lIII 1
C0710oooo0 1
C1140I111 1
C0579l1lI 2
C0079IlIl 2
C0702ooOoo 2
C0719oOo0O 2
C0705oOo0o 1
C0131O0OoO 2
C0082II1l 1
C0088Il111 1
C0103OOoOo 2
C0108O0O0o 1
C0128Ooo00 2
C0083I11 1
C0080I11I 1
C0107Ooo0O 1
C0699o00OO 2
C0728oOO00 1
C0714ooO00 2
C0698oo00O 1
C0086Il1 1
C0126OoOoo 1
C0117O0OOo 1
C0109OoOOO 1
C0646nn 1
C0524kn 1
C0414hn 2
C0303en 2
C0193bn 2
C1089yn 1
C0978vn 1
C0867sm 1
C0756pm 1
C0608mm 1
C0376gm 1
C0265dm 1
C0155am 2
C1051xl 2
C0940ul 1
C0829rl 1
C0681ol 1
C0559ll 1
C0449il 1
C0338fl 1
C1124zk 1
C1013wk 2
C0902tk 2
C0791qk 1
C0643nk 1
C0521kk 1
C0411hk 2
C0300ek 2
C0190bk 2
C1086yj 2
C0864sj 1
C0753pj 1
C0605mj 2
C0483jj 2
C0373gj 2
C0262dj 1
C0152aj 2
C1048xi 2
C0937ui 1
C0826ri 2
C0556li 1
C0446ii 2
C0335fi 2
C0224ci 1
C1121zh 2
C1010wh 1
C0899th 1
C0788qh 2
C0640nh 2
C0518kh 2
C0297eh 2
C0187bh 2
C1083yg 1
C0972vg 2
C0861sg 1
C0750pg 1
C0602mg 1
C0480jg 2
C0370gg 1
C0259dg 2
C1045xf 1
C0934uf 1
C0823rf 2
C0675of 1
C0553lf 1
C0443if 1
C0332ff 1
C0221cf 2
C1118ze 1
C1007we 2
C0785qe 1
C0637ne 2
C0515ke 2
C0405he 2
C0294ee 1
C0477jd 1
C0218cc 2
C0966va 1
C0669o9 2
C0399h8 1
C1036x6 2
C0776q5 1
C0468j4 2
C0209c3 2
C0957v1 1
C0660o0 1
C0390h 1
C0696o0Ooo 2
C0102O0ooO 2
C0118O0ooO 2
C0683on 2
C0413hm 2
C0154al 1
C0901tj 2
C0604mi 1
C0334fh 2
C1082yf 1
C0642re 1
C0893y6 1
Calculation 2

moon

详细见:DubheCTF-Destination&Moon | Clovershrub

个人不是很懂密码

sub_140001780是高精度快速幂,sub_140001000是高精度乘法,sub_140001260是高精度除法,xmmword_140007848是一个大质数,Src是做除法时生成的余数。最后可以得到程序在干这么个事:

没看懂这里的数字为啥是反的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
prime=1537131588382913092665966115381275601741897676956736016043055688971156548045659189201332511868437849089
#s=1501105066780188567056607534552026954826071950153062515667046571260895066450839051954426281121521337
s2=750552533390094283528303767276013477413035975076531257833523285630447533225419525977213140560760669
# t=10
#n=3
b=535687859422589012977141675826129236269925277717894712488225104574807190354008307256942078237560832125
x = [0 for _ in range(14)]
ans=885929268745208437773737031796868079277836491798608104263226417228443603006843430266722004055919359990

a = input()
a_1 = pow(a,prime-2,prime)
x[9] = pow(a,s_2,prime)
for i in range(8,-1,-1):
tmp = pow(a_1*x[i+1]*x[i+1],pow(2,i),prime)
if tmp = 1:
j=0
elif tmp =prime-1:
j=1
else:
print('fail')
break
x[i] = (x[i+1]*(pow(b,j*pow(2,8-i),prime)))%prime

print(x[0]==ans)

交给gemini-pro求解

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
import itertools
import hashlib

prime=1537131588382913092665966115381275601741897676956736016043055688971156548045659189201332511868437849089
s2=750552533390094283528303767276013477413035975076531257833523285630447533225419525977213140560760669
b=535687859422589012977141675826129236269925277717894712488225104574807190354008307256942078237560832125
ans=885929268745208437773737031796868079277836491798608104263226417228443603006843430266722004055919359990

b_inv = pow(b, -1, prime)
s2_inv = pow(s2, -1, prime - 1)
num_j = 9

for j_num in range(2**num_j):
j_sequence = []
temp_j_num = j_num
for _ in range(num_j):
j_sequence.insert(0, temp_j_num % 2)
temp_j_num //= 2

xc = [0] * 10
xc[0] = ans

for i in range(num_j):
j_i = j_sequence[8-i]
xc[i+1] = (xc[i] * pow(b_inv, j_i * pow(2, 8-i), prime)) % prime

candidate_a = pow(xc[9], s2_inv, prime)

if candidate_a == 0:
continue

a_inv = pow(candidate_a, -1, prime)

x_forward = [0] * 10
x_forward[9] = pow(candidate_a, s2, prime)

if x_forward[9] != xc[9]:
continue

valid_sequence = True
for i in range(num_j - 1, -1, -1):
tmp = pow(a_inv * x_forward[i+1] * x_forward[i+1], pow(2, i), prime)

generated_j = -1
if tmp == 1:
generated_j = 0
elif tmp == prime - 1:
generated_j = 1
else:
valid_sequence = False
break

assumed_j = j_sequence[8-i]
if generated_j != assumed_j:
valid_sequence = False
break

x_forward[i] = (x_forward[i+1] * pow(b, generated_j * pow(2, 8-i), prime)) % prime

if valid_sequence:
if x_forward[0] == ans:
print(f"Found candidate_a: {candidate_a}")
num_bytes = str(candidate_a).encode('utf-8')
md5_hash = hashlib.md5(num_bytes).hexdigest()
print(f"Flag: DubheCTF{{{md5_hash}}}")
break
1
2
Found candidate_a: 9282518933443361561931517206943216160800270539664959226902371097049976661617888730966203839253721281
Flag: DubheCTF{c58a378bec96b92d8e19b2e7dbddc4ff}