湾区杯初赛WriteUp&复现
和X2c的nixware、Ky4niT3师傅参加了线上初赛,但是寄了。第一次组队参赛,还是太菜了。Re方向解了一道,然后和师傅们拼凑出了一道。基本就是签到题靠AI,难题没思路,见识还是太少了
WriteUp
hardtest
附件程序用IDA打开,shift+F12在字符串表通过关键字“flag:”可以找到main函数位置

以下是优化后的main函数:
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__int64 __fastcall main(int a1, char **a2, char **a3)
{
unsigned int seed; // eax
unsigned __int64 scale1; // rax
void *temp1; // rsp
unsigned __int64 scale2; // rax
void *temp2; // rsp
char temp; // al
_QWORD buf[2]; // [rsp+8h] [rbp-F0h] BYREF
__int64 i_2; // [rsp+18h] [rbp-E0h]
__int64 v12; // [rsp+20h] [rbp-D8h]
char input; // [rsp+29h] [rbp-CFh] BYREF
char judge; // [rsp+2Ah] [rbp-CEh]
char randNum; // [rsp+2Bh] [rbp-CDh]
int j; // [rsp+2Ch] [rbp-CCh]
int i; // [rsp+30h] [rbp-C8h]
int length; // [rsp+34h] [rbp-C4h]
__int64 v19; // [rsp+38h] [rbp-C0h]
__int64 Buf; // [rsp+40h] [rbp-B8h]
__int64 v21; // [rsp+48h] [rbp-B0h]
_QWORD *Enc; // [rsp+50h] [rbp-A8h]
char flag[104]; // [rsp+58h] [rbp-A0h] BYREF
unsigned __int64 v24; // [rsp+C0h] [rbp-38h]
v24 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
seed = time(0LL);
srand(seed);
randNum = rand() % 255 + 1;
printf("input your number(1-255): ");
if ( __isoc99_scanf("%d", &input) == 1 && randNum == input )
{
while ( getchar() != 10 )
;
printf("flag: ");
fgets(flag, 100, stdin); // 获取flag输入
flag[strcspn(flag, "\n")] = 0;
length = strlen(flag);
v19 = length - 1LL;
i_2 = length;
v12 = 0LL;
buf[0] = length;
buf[1] = 0LL;
scale1 = 16 * ((length + 15LL) / 0x10uLL); // 在动态分配栈上的内存
while ( buf != (buf - (scale1 & 0xFFFFFFFFFFFFF000LL)) )
;
temp1 = alloca(scale1 & 0xFFF);
if ( (scale1 & 0xFFF) != 0 )
*(&buf[-1] + (scale1 & 0xFFF)) = *(&buf[-1] + (scale1 & 0xFFF));
Buf = buf; // Buf指向分配的内存
encrypt1(flag, buf); // 对flag进行加密,加密结果实际上由Buf记录,buf是一个通用的缓存空间
v21 = length - 1LL;
scale2 = 16 * ((length + 15LL) / 0x10uLL); // 再次动态分配栈上的内存
while ( buf != (buf - (scale2 & 0xFFFFFFFFFFFFF000LL)) )
;
temp2 = alloca(scale2 & 0xFFF);
if ( (scale2 & 0xFFF) != 0 )
*(&buf[-1] + (scale2 & 0xFFF)) = *(&buf[-1] + (scale2 & 0xFFF));
Enc = buf; // Enc指向分配的内存
for ( i = 0; i < length; ++i )
{
temp = encrypt2(*(Buf + i)); // 对上一轮加密的结果再进行一次加密,加密结果由Enc记录
*(Enc + i) = temp;
}
judge = 1;
for ( j = 0; j < length; ++j )
{
if ( *(Enc + j) != byte_2120[j] ) // 判断加密结果是否正确
{
judge = 0;
break;
}
}
if ( judge )
puts("right");
return 0LL;
}
else
{
puts("error");
return 1LL;
}
}
__int64 __fastcall encrypt1(const char *flag, __int64 buf)
{
__int64 count; // rax
signed int i; // [rsp+14h] [rbp-1Ch]
signed int length; // [rsp+18h] [rbp-18h]
length = strlen(flag);
for ( i = 0; ; ++i )
{
count = i;
if ( i >= length )
break;
*(i + buf) = rol(flag[i], (i % 7) + 1); // return (flag[i] << ((i%7)+1)) | (flag[i] >> (8 - ((i%7)+1)))
}
return count;
}
__int64 __fastcall encrypt2(char Buf_i)
{
unsigned __int8 v1; // al
unsigned __int8 v3; // [rsp+15h] [rbp-3h]
v1 = rol(Buf_i ^ 0x5Au, 3); // return (a1^0x5Au << 3) | (a1^0x5Au >> (8 - 3));
v3 = sub_1313((16 * ((3 * (v1 >> 4)) & 0xF)) | (5 * (v1 & 0xF)) & 0xFu);// AI说是模幂运算函数,计算a1^(255) mod 257
return byte_2020[ror(v3, 2)]; // return (v3 >> 2) | (v3 << (8 - 2));此处建立了映射关系
}笔者加解密这一块太菜了,没有数学头脑,只能依靠AI解密了(因为涉及数论,笔者还没学过)
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
70def rol(b, n):
n %= 8
return ((b << n) | (b >> (8 - n))) & 0xFF
def ror(b, n):
n %= 8
return ((b >> n) | (b << (8 - n))) & 0xFF
def mod_inv(a, mod=257):
# 计算a在模257下的逆元,使用费马小定理
return pow(a, 255, mod)
byte_2020 = [
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
]
# 构建反向映射
rev_2020 = {}
for idx, val in enumerate(byte_2020):
rev_2020[val] = idx
enc_data = [
0x97, 0xD5, 0x60, 0x43, 0xB4, 0x10, 0x43, 0x73, 0x0F, 0xDA,
0x43, 0xCD, 0xD3, 0xE8, 0x73, 0x4A, 0x94, 0xC3, 0xCD, 0x71,
0xBD, 0xDC, 0x97, 0x1A
]
# 逆向encrypt2
enc1_out = []
for i, c in enumerate(enc_data):
idx = rev_2020[c] # 得到索引(即v3循环右移2位后的值)
v3 = rol(idx, 2) # 循环左移2位得到v3(原循环右移2位的逆)
# 如果v3为0?但原代码中a1不为0才计算,这里假设v3不为0
if v3 == 0:
x = 0
else:
x = mod_inv(v3, 257)
A = (x >> 4) & 0xF
B = x & 0xF
high = (11 * A) & 0xF
low = (13 * B) & 0xF
v1 = (high << 4) | low
tmp = ror(v1, 3) # 循环右移3位(原循环左移3位的逆)
enc1_out.append(tmp ^ 0x5A)
# 逆向encrypt1
flag = []
for i, c in enumerate(enc1_out):
n = (i % 7) + 1
flag_char = ror(c, n) # 循环右移n位(原循环左移n位的逆)
flag.append(flag_char)
# 转换为字符串
flag_str = ''.join(chr(b) for b in flag)
print(flag_str)
minigame
微信小程序逆向,第一次遇到这个类型的题
用wedecode解包附件,输出结果在wedecode-main/OUTPUT/default里

没有这方面的经验,让AI分析项目,发现utils/validator.wasm里有检验逻辑。wasm是一种二进制文件,可以转成wat阅读(类似汇编)。使用在线工具wasm2wat demo把wasm转成wat:
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(module
(type $t0 (func (param i32) (result i32)))
(type $t1 (func))
(func $c (export "c") (type $t0) (param $p0 i32) (result i32)
(local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(if $I6
(i32.ne
(block $B0 (result i32)
(block $B1
(block $B2
(br_if $B2
(i32.eqz
(i32.and
(local.tee $l3
(local.get $p0))
(i32.const 3))))
(drop
(br_if $B0
(i32.const 0)
(i32.eqz
(i32.load8_u
(local.get $p0)))))
(loop $L3
(br_if $B2
(i32.eqz
(i32.and
(local.tee $p0
(i32.add
(local.get $p0)
(i32.const 1)))
(i32.const 3))))
(br_if $L3
(i32.load8_u
(local.get $p0))))
(br $B1))
(loop $L4
(local.set $p0
(i32.add
(local.tee $l1
(local.get $p0))
(i32.const 4)))
(br_if $L4
(i32.eq
(i32.and
(i32.or
(i32.sub
(i32.const 16843008)
(local.tee $l4
(i32.load
(local.get $l1))))
(local.get $l4))
(i32.const -2139062144))
(i32.const -2139062144))))
(loop $L5
(local.set $l1
(i32.add
(local.tee $p0
(local.get $l1))
(i32.const 1)))
(br_if $L5
(i32.load8_u
(local.get $p0)))))
(i32.sub
(local.get $p0)
(local.get $l3)))
(i32.const 38))
(then
(return
(i32.const 0))))
(loop $L7
(block $B8
(local.set $l1
(i32.eq
(local.tee $p0
(i32.xor
(i32.load8_u offset=1024
(local.get $l2))
(i32.load8_s
(i32.add
(local.get $l2)
(local.get $l3)))))
(i32.const 153)))
(br_if $B8
(i32.ne
(local.get $p0)
(i32.const 153)))
(br_if $L7
(i32.ne
(local.tee $l2
(i32.add
(local.get $l2)
(i32.const 1)))
(i32.const 38)))))
(local.get $l1))
(func $b (export "b") (type $t1))
(memory $a (export "a") 258 258)
(data $d0 (i32.const 1024) "\ff\f5\f8\fe\e2\ff\f8\fc\a9\fb\ab\ae\fa\ad\ac\a8\fa\ae\ab\a1\a1\af\ae\f8\ac\af\ae\fc\a1\fa\a8\fb\fb\ad\fc\ac\aa\e4"))由AI分析知,逻辑主要是把输入的内容与密文异或,异或的结果要等于153。等价于将输入的内容和153异或要等于密文。解密就是拿153异或密文。exp:
1
2
3
4
5
6
7
8
9enc = [
0xff, 0xf5, 0xf8, 0xfe, 0xe2, 0xff, 0xf8, 0xfc, 0xa9, 0xfb,
0xab, 0xae, 0xfa, 0xad, 0xac, 0xa8, 0xfa, 0xae, 0xab, 0xa1,
0xa1, 0xaf, 0xae, 0xf8, 0xac, 0xaf, 0xae, 0xfc, 0xa1, 0xfa,
0xa8, 0xfb, 0xfb, 0xad, 0xfc, 0xac, 0xaa, 0xe4
]
flag = ''.join([chr(data ^ 153) for data in enc])
print(flag)
复现
strangapp
apk用JEB打开会发现MainActivity全是nop:

拖进GDA里看一下,会发现apk加了壳

所以第一步先脱壳
没辙了,题目环境卡的好死,配了一下午环境没配出能运行apk的,改天再试😡
湾区杯初赛WriteUp&复现