L3Hctf复现

ez_android

先将程序拖进jadx中分析

找到MainActivity

通过mainactivity找到TauriActivity

再通过TauriActivity找到WryActivity

1752638685032

发现主要逻辑都在native层里面

把so文件放进ida中分析

随便填点东西在程序里

输出了Wrong answer

1752638522730

再根据这个字符串
在so层里面找

1752645622731

找到了greet函数

1752645713404

这里采用正向爆破的方法

代码如下

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
#include<stdio.h>
#include <memory.h>
#include<stdint.h>
void jiami(uint8_t *a){
uint64_t v10;
uint8_t v11;
unsigned char aDghpc2lzywtleq[]="dGhpc2lzYWtleQ";
for (int i = 0LL; i != 27; ++i )
{
v10 = i - 14;
if ( i < 0xE )
v10 = i;
v11 = aDghpc2lzywtleq[(((2 * i) | 1) - 14 * ((147 * ((2 * i) | 1u)) >> 11))] + (a[i] ^ aDghpc2lzywtleq[v10]);
a[i] = aDghpc2lzywtleq[(i + 4) % 0xEu] ^ ((v11 << (aDghpc2lzywtleq[(i + 3) % 0xEu] & 7)) | (v11 >> (-aDghpc2lzywtleq[(i + 3) % 0xEu] & 7)));
}
}
int check(uint8_t *a,uint8_t *b){
int count=0;
for(int i=0;i<28;i++){
if(a[i]==b[i])
count++;
else
break;
}
return count;
}
int main(){
uint8_t flag[28];
uint8_t v19[11];
uint8_t enc[28];
uint8_t tmp[28];
memcpy(&v19[8], "O2*", 3);
*(uint64_t *)v19 = 0xFC020A4C0E2C7290;
*(uint64_t *)enc = 0xA409663A025150C;
*((uint64_t *)enc+1) = 0x1FE106294065165C;
*((uint64_t *)enc+2) = 0xFC020A4C0E2C7290;
*(uint64_t *)(enc+19) = *(uint64_t *)&v19[3];
memset(flag,0,28);
for(int i=0;i<28;i++){
for(int j=0;j<128;j++){
memset(tmp,0,28);
memcpy(tmp,flag,28);
tmp[i]=j;
jiami(tmp);
if(check(tmp,enc)>=i+1)
{
flag[i]=j;
printf("%s\n",flag);
break;
}
}
}
return 0;
}

学到的东西:

表达式 指针运算的步长 地址偏移量 实际访问地址
*((uint64_t *)enc + 2) sizeof(uint64_t)(8 字节) 16 字节 enc + 16
*(uint64_t *)(enc + 2) enc 的原始类型(通常是 1 字节) 2 字节 enc + 2

终焉之门

1752665985141

打开界面如上

直接拖进ida中进行分析
直接搜索关键词

1752666042227

找到关键函数

1752666095588

1752666134705

主要逻辑不太看的懂

丢给ai
说是

该程序实质是一个结合图形API的密码验证器,核心验证逻辑依赖于GPU计算,需进一步分析着色器代码才能获取有效Flag。

而这两个函数都和着色器有关系
1752666411598

跟进去看看

1752666501074

一眼看上去像是乱码一样

估计是进行了某种加密,阻碍静态分析的

但程序在运行时如果需要执行这段代码

肯定需要进行解密

所以试着通过动调来查看

1752666782628

程序执行过这两个函数后,点进去查看

image-20250716195131149

1752666681169

1752666712176

1752666733541

发现是VM

通过分析可得

1752667100533

提取出opcode以及主要的程序逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
dword_7FF7C69E30E0[168] = {
0x00000002, 0x00000000, 0x00000002, 0x00000001, 0x00000002, 0x00000000, 0x0000000E, 0x00000000,
0x00000002, 0x00000010, 0x00000008, 0x00000000, 0x00000002, 0x00000002, 0x00000002, 0x00000001,
0x0000000E, 0x00000000, 0x00000002, 0x00000011, 0x00000008, 0x00000000, 0x00000002, 0x00000003,
0x00000002, 0x00000002, 0x0000000E, 0x00000000, 0x00000002, 0x00000012, 0x00000007, 0x00000000,
0x00000002, 0x00000004, 0x00000002, 0x00000003, 0x0000000E, 0x00000000, 0x00000002, 0x00000013,
0x00000007, 0x00000000, 0x00000002, 0x00000005, 0x00000002, 0x00000004, 0x0000000E, 0x00000000,
0x00000002, 0x00000014, 0x00000008, 0x00000000, 0x00000002, 0x00000006, 0x00000002, 0x00000005,
0x0000000E, 0x00000000, 0x00000002, 0x00000015, 0x00000007, 0x00000000, 0x00000002, 0x00000007,
0x00000002, 0x00000006, 0x0000000E, 0x00000000, 0x00000002, 0x00000016, 0x00000007, 0x00000000,
0x00000002, 0x00000008, 0x00000002, 0x00000007, 0x0000000E, 0x00000000, 0x00000002, 0x00000017,
0x00000007, 0x00000000, 0x00000002, 0x00000009, 0x00000002, 0x00000008, 0x0000000E, 0x00000000,
0x00000002, 0x00000018, 0x00000007, 0x00000000, 0x00000002, 0x0000000A, 0x00000002, 0x00000009,
0x0000000E, 0x00000000, 0x00000002, 0x00000019, 0x00000007, 0x00000000, 0x00000002, 0x0000000B,
0x00000002, 0x0000000A, 0x0000000E, 0x00000000, 0x00000002, 0x0000001A, 0x00000007, 0x00000000,
0x00000002, 0x0000000C, 0x00000002, 0x0000000B, 0x0000000E, 0x00000000, 0x00000002, 0x0000001B,
0x00000008, 0x00000000, 0x00000002, 0x0000000D, 0x00000002, 0x0000000C, 0x0000000E, 0x00000000,
0x00000002, 0x0000001C, 0x00000008, 0x00000000, 0x00000002, 0x0000000E, 0x00000002, 0x0000000D,
0x0000000E, 0x00000000, 0x00000002, 0x0000001D, 0x00000007, 0x00000000, 0x00000002, 0x0000000F,
0x00000002, 0x0000000E, 0x0000000E, 0x00000000, 0x00000002, 0x0000001E, 0x00000008, 0x00000000,
0x00000010, 0x00000000, 0x00000002, 0x00000010, 0x00000002, 0x00000011, 0x0000000F, 0x00000000,
0x00000012, 0x00000054, 0x00000002, 0x0000001F, 0x00000001, 0x00000000, 0x00000003, 0x00000001
};
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
void main() {
if (gl_GlobalInvocationID.x > 0) return;

uint ip = 0;
int sp = 0;
verdict = -233;

while (ip < uint(MaxInstructionCount)) {
int opcode = opcodes[int(ip)];
int arg = opcodes[int(ip)+1];

switch (opcode) {
case 2:
stack_data[sp++] = co_consts[arg];
break;

case 7:
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = a + b;
break;

case 8:
int a = stack_data[--sp];
int b = stack_data[--sp];
stack_data[sp++] = a - b;
break;

case 14:
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = a ^ b;
break;

case 15:
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = int(a == b);
break;

case 16:
bool ok = true;
for (int i = 0; i < 16; i++) {
if (stack_data[i] != (cipher[i] - 20)) {
ok = false;
break;
}
}
verdict = ok ? 1 : -1;
return;

case 18:
int c = stack_data[--sp];
if (c == 0) ip = uint(arg);
break;

default:
verdict = 500;
return;
}

ip += 2;
}

verdict = 501;
}

根据opcode推测flag就是先经过一次异或再进行一次sub或者add

模拟压栈和出栈的规律

最后和cipher-20的数据进行比较

代码如下

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 cipher[]={0xF3, 0x82, 0x06, 0x1FD,
0x150, 0x38, 0xB2, 0xDE,
0x15A, 0x197, 0x9C, 0x1D7,
0x6E, 0x28, 0x146, 0x97};
unsigned int co_consts[] = { 0xB0, 0xC8,
0xFA, 0x86, 0x6E, 0x8F, 0xAF, 0xBF, 0xC9, 0x64, 0xD7, 0xC3, 0xE3, 0xEF, 0x87, 0x00};
unsigned int flag[16];
for(int i=0;i<16;i++){
cipher[i]-=20;
}
int a[]={1,0,0,1,1,0,1,1,1,1,1,1,0,0,1,0};
flag[0]=cipher[0];
for(int i=1;i<16;i++){
if(a[i]==0)
flag[i]=(co_consts[i-1]-cipher[i])^flag[i-1];
//cipher[i]=co_consts[i-1]-(flag[i]^flag[i-1])
else
flag[i]=(cipher[i]-co_consts[i-1])^flag[i-1];
//cipher[i]=co_consts[i-1]+(flag[i]^flag[i-1])
}
for(int i=0;i<16;i++)
printf("%x",flag[i]);
return 0;
}