April 19, 2022

STARCTF2022-NaCI

0x00 日常查壳

无壳64位

image-20220419152650981

0x01 分析主程序

我们shift + F12通过字符串查找引用,直接找到这个主函数

image-20220419153100353

F5加密函数!

然而这个函数的F5给淦烂了,只能动调去理解这个程序,然而!我摸透了这其中的符文!

image-20220419154430259

主要是两个点去修复,顺便去个无用指令

1. 奇怪的call

每个call都是这样实现的,再去看看retn

image-20220419153452942

2. 奇怪的retn

可以发现每次jmp rdi就是一种回跳,而这不就是retn吗

PS:上面的call和retn不是同一组call+retn

image-20220419153623495

所以源程序的特性

  1. 把要回来的地址暂存入内存,再jmp到指定地址,同时jmp后还有段花指令
  2. 当要跳回时,重新把地址放到rdi,再jmp跳回去

所以我们可以改成

  1. 把jmp上面的所有操作去掉,直接改成call(push + jmp)

  2. call后面的花全部去掉

  3. 再把jmp rdi改成retn(pop + jmp),这时候直接用的是栈顶的地址其实就是回去的地址

以此为想法写个idapython

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
start = 0x807FEC0
end = 0x8080AD1

address = [0 for i in range(5)]
callTarget = ["lea", "lea", "mov", "jmp"]
retnTarget = ["lea", "mov", "and", "lea", "jmp"]


def nop(s, e):
while (s < e):
patch_byte(s, 0x90)
s += 1

def turnCall(s, e, h):
# nop掉call之前的值
nop(s, e)
patch_byte(e, 0xE8)
# 把后面的花指令去掉 重新计算去花长度
huaStart = next_head(e)
huaEnd = h
nop(huaStart, huaEnd)

def turnRetn(s, e):
nop(s, e)
# 注意原来是jmp xxx
# 所以前面nop掉一个 后面改成retn
patch_byte(e, 0x90)
patch_byte(e + 1, 0xC3)

p = start
while p < end:
address[0] = p
address[1] = next_head(p)
address[2] = next_head(address[1])
address[3] = next_head(address[2])
address[4] = next_head(address[3])

for i in range(0, 4):
if print_insn_mnem(address[i]) != callTarget[i]:
break
else:
turnCall(address[0], address[3], get_operand_value(address[1], 1))
p = next_head(next_head(address[3]))
continue

for i in range(0, 5):
if print_insn_mnem(address[i]) != retnTarget[i]:
break
else:
turnRetn(address[0], address[4])
p = next_head(next_head(address[4]))
continue

p = next_head(p)

shift + F2打开idapython窗口,选择python,run一下

image-20220419154550063

patch完保存一下,让ida重新解析

image-20220419154355090

保存好再次打开这个文件,进入本函数,F5加密函数!

image-20220419154720870

0x02 分析加密函数

可以通过创建结构体让程序很好理解,在我们不确定这些子项的具体函数,可以先用ida默认的

image-20220419155017422

简单审计一下即可知道大概意思

image-20220419155448997

异或加密

我们的输入分别小端序放入,经过一个可以逆的异或计算

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
__int64 __fastcall XOR(__int64 input)
{
__int64 v1; // rbx
__int64 v2; // r13
__int64 v3; // r15
_QWORD *v4; // r15
struct_v5 *v5; // r15
int v6; // ebx
int v7; // ebx

v4 = (v3 - 8);
*v4 = v1;
v5 = (v2 + (v4 - 56));
v5->input = input;
v5->xorKey = sub_8080360();
v5->input1 = *(v2 + v5->input);
v5->highFour = Big(HIDWORD(v5->input1)); // 高四位小端序放入
v5->lowFour = Big(v5->input1); // 低四位小端序放入
for ( v5->count = 0; v5->count <= 43; ++v5->count )
{
v5->orgLowFour = v5->lowFour;
v6 = ROL(v5->lowFour, 1);
v7 = ROL(v5->lowFour, 8) & v6;
v5->lowFour = v5->highFour ^ v7 ^ ROL(v5->lowFour, 2) ^ *(v2 + 4 * v5->count + v5->xorKey);
v5->highFour = v5->orgLowFour;
}
v5->input1 = 0LL;
v5->input1 = ((v5->input1 | v5->lowFour) << 32) | v5->highFour;// 低高互换
return v5->input1;
}

key可以通过动调直接获取,再注意一下结尾的高低32位互换即可

image-20220419160146104

魔改XTEA

注意,写个解密脚本即出

  1. 轮数变了
  2. delta数变了
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
__int64 __fastcall XTEA(int count, __int64 a2)
{
__int64 v2; // r13
myst *v3; // r15
__int64 result; // rax

v3[-1].t1 = count; // 轮数为传入的轮数,分别是2 4 8 16
*&v3[-2].v0 = a2;
*&v3[-1].key = key; // key可以直接拿
v3[-1].v0 = *(v2 + *&v3[-2].v0);
v3[-1].v1 = *(v2 + *&v3[-2].v0 + 4);
v3[-1].sum = 0;
v3[-1].delta = 0x10325476; // delta数变了
for ( v3[-1].t9 = 0; v3[-1].t9 < v3[-1].t1; ++v3[-1].t9 )
{
v3[-1].v0 += (((v3[-1].v1 >> 5) ^ (16 * v3[-1].v1)) + v3[-1].v1) ^ (*(v2 + 4 * (v3[-1].sum & 3) + *&v3[-1].key)
+ v3[-1].sum);
v3[-1].sum += v3[-1].delta;
v3[-1].v1 += (((v3[-1].v0 >> 5) ^ (16 * v3[-1].v0)) + v3[-1].v0) ^ (*(v2
+ 4 * ((v3[-1].sum >> 11) & 3)
+ *&v3[-1].key)
+ v3[-1].sum);
}
*(v2 + *&v3[-2].v0) = v3[-1].v0;
result = v3[-1].v1;
*(v2 + *&v3[-2].v0 + 4) = result;
return result;
}

0x03 GetFlag

直接从Check函数第一个参数拿到密文

image-20220419161345315

EXP

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

#define SHL(x, n) ( ((x) & 0xFFFFFFFF) << n )
#define ROTL(x, n) ( SHL((x), n) | ((x) >> (32 - n)) )

unsigned int xorKey[44] = {
0x04050607, 0x00010203, 0x0C0D0E0F, 0x08090A0B, 0xCD3FE81B, 0xD7C45477, 0x9F3E9236, 0x0107F187,
0xF993CB81, 0xBF74166C, 0xDA198427, 0x1A05ABFF, 0x9307E5E4, 0xCB8B0E45, 0x306DF7F5, 0xAD300197,
0xAA86B056, 0x449263BA, 0x3FA4401B, 0x1E41F917, 0xC6CB1E7D, 0x18EB0D7A, 0xD4EC4800, 0xB486F92B,
0x8737F9F3, 0x765E3D25, 0xDB3D3537, 0xEE44552B, 0x11D0C94C, 0x9B605BCB, 0x903B98B3, 0x24C2EEA3,
0x896E10A2, 0x2247F0C0, 0xB84E5CAA, 0x8D2C04F0, 0x3BC7842C, 0x1A50D606, 0x49A1917C, 0x7E1CB50C,
0xFC27B826, 0x5FDDDFBC, 0xDE0FC404, 0xB2B30907
};

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], delta = 0x10325476, sum=delta*num_rounds;
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[(sum & 3)]);
}
v[0]=v0; v[1]=v1;
}

void XorRol(uint32_t v[2])
{
uint32_t encLow = v[1];
uint32_t encHigh = v[0];
uint32_t orgLow, orgHigh, v6, v7, v8;
int i;

for ( i = 43; i >= 0; i-- )
{
orgLow = encHigh;
v6 = ROTL(orgLow, 1);
v7 = ROTL(orgLow, 8) & v6;
v8 = v7 ^ ROTL(orgLow, 2);
orgHigh = encLow ^ xorKey[i] ^ v8;

encHigh = orgHigh;
encLow = orgLow;
}
v[0] = orgLow; v[1] = orgHigh;
}

int main()
{
uint32_t v[] = { 0xFDF5C266, 0x7A328286, 0xCE944004, 0x5DE08ADC, 0xA6E4BD0A, 0x16CAADDC, 0x13CD6F0C, 0x1A75D936 };
uint32_t k[4] = { 0x03020100, 0x07060504, 0x0B0A0908, 0x0F0E0D0C };
int i, j;
uint32_t teaData[8];

// uint32_t testData[] = { 0xD4C2E7AE, 0xD2E28713 };
// XorRol(testData);
// printf("0x%X, 0x%X, ", testData[0], testData[1]);

for ( i = 0; i <= 3; i++ )
{
decipher(1 << (i + 1), v + i * 2, k);
printf("0x%X, 0x%X, ", v[i * 2], v[i * 2 + 1]);
teaData[i * 2] = v[i * 2];
teaData[i * 2 + 1] = v[i * 2 + 1];
}

puts("\n");

for ( i = 0; i <= 3; i++ )
{
XorRol(teaData + i * 2);
// printf("0x%X, 0x%X, ", teaData[i * 2], teaData[i * 2 + 1]);
}

puts("\n");

unsigned char * t = (unsigned char *)&teaData;
for ( i = 0; i < 32; i += 4 )
printf("%c%c%c%c", t[i + 3], t[i + 2], t[i + 1], t[i]);

return 0;
}

GetFlag!

image-20220419161304529

DASCTF X SU
🍬
HFCTF2022
🍪

About this Post

This post is written by P.Z, licensed under CC BY-NC 4.0.