NCTF 2024 Reverse Writeup

NCTF 2024 Reverse Writeup

两只羊 Lv1

img

x1login

img

checkSecutity检测了root和debug,直接frida绕过

1
2
3
4
5
6
7
8
9
10
11
12
function anti_debug()
{
Java.perform(function() {
var MainActivity = Java.use("com.nctf.simplelogin.MainActivity");
var checkSecutity = MainActivity.checkSecutity
checkSecutity.implementation = function() {
console.log("entering checkSecutity")
//var res = this.checkSecutity();
//return res
}
})
}

程序多次调用了DecStr.get进行自解,而且动态加载了了一个dex文件,同样直接hook看结果

img

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function hook_get()
{
Java.perform(function() {

var DecStr = Java.use("com.nctf.simplelogin.DecStr");
var get = DecStr.get
get.implementation = function(arg) {
console.log("get => ", arg);
var res = this.get(arg);
console.log("res => ", res);
return res
}

var Secure = Java.use("com.nctf.simplelogin.Secure");
var loadDex = Secure.loadDex;
loadDex.implementation = function(arg1, arg2) {
console.log("load => ", arg2);
var res = this.loadDex(arg1, arg2);
console.log("dex => ", res);
return res
}
})

}

将dump出来的字节写入并用jeb打开

img

通过之前hook get方法可以直接拿到username = X1c@dM1n1$t

check_password是将username的md5结果和password传入libnative.so的doCheck函数进行校验

img

ida可以发现就是一个3des算法,key1和key2分别为username的md5前8个字节和后8个字节

然后考虑到可能会出现的魔改情况,这次利用3des的算法特性进行取巧解密

img

3des的加密流程为

des key1加密 => des key2解密 => des key1加密

那么解密的流程为

des key1解密 => des key2加密 => des key1解密

这样无论des的算法如何魔改,我们只需要把算法抠出来执行就好了

使用unicorn模拟执行框架

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
from unicorn import *
from unicorn.arm64_const import *
from elftools.elf.elffile import ELFFile
from pwn import*

# 读取 ELF 文件
elf_file = './libnative.so'

emu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
emu.mem_map(0, 0x100000, UC_PROT_ALL) # 分配内存

STACK_PTR = 0x7F0000

emu.mem_map(STACK_PTR - 0x1000, 0x10000, UC_PROT_ALL) # 分配内存
emu.reg_write(UC_ARM64_REG_SP, STACK_PTR)

with open(elf_file, 'rb') as f:
elf = ELFFile(f)

load_segments = [x for x in elf.iter_segments() if x.header.p_type == 'PT_LOAD']

for segment in load_segments:
prot = UC_PROT_ALL
#emu.mem_map(segment.header.p_vaddr, segment.header.p_memsz, prot)
emu.mem_write(segment.header.p_vaddr, segment.data())



def hook_addr(mu, address, size, user_data):
print(hex(address))


def call_enc(input, key):
enc_addr = 0x2644
#print(hex(u64(input)))
emu.reg_write(UC_ARM64_REG_X0, u64(input))
emu.reg_write(UC_ARM64_REG_X1, u64(key))

emu.emu_start(enc_addr, 0x2dbc)

res = emu.reg_read(UC_ARM64_REG_X0)
return p64(res)

def call_dec(input, key):
enc_addr = 0x2DC4
#print(hex(u64(input)))
emu.reg_write(UC_ARM64_REG_X0, u64(input))
emu.reg_write(UC_ARM64_REG_X1, u64(key))

emu.emu_start(enc_addr, 0x353C)

res = emu.reg_read(UC_ARM64_REG_X0)
return p64(res)
#print(hex(res))


cipher = [
0x40, 0x9E, 0xEC, 0x86, 0xB8, 0x84, 0xA5, 0x8B, 0x7E, 0x8A,
0x64, 0xE2, 0x1A, 0xD3, 0xB8, 0xBB, 0xDF, 0x4B, 0xFA, 0x12,
0x46, 0x45, 0x3E, 0x52, ]
cipher = bytes(cipher)
print("Starting emulation...")
for i in range(3):
# 开始仿真
cur_cipher = cipher[i * 8: i * 8 + 8]


key1 = b'\x7d\x53\xec\xd3\x6a\x43\xd3\xd2'
key2 = b'\x37\xe7\xdd\x63\x3d\xcf\x84\x97'
v0 = call_dec(cur_cipher, key1)
v1 = call_enc(v0, key2)
res = call_dec(v1, key1)
print(res.decode(), end="")


img

NCTF{X1c@dM1n1$t_SafePWD~5y$x?YM+5U05Gm6=}

gogo

非常简单清晰的go vm,直接在每个运算handle打idapython log

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
base = 0x200000

def set_python_bpt(ea, cond):
''' Set conditional breakpoint with Python function

Usage:
set_python_bpt(0x08000688, 'view_regs()')
'''
idaapi.add_bpt(ea, 4, BPT_DEFAULT)
bpt = idaapi.bpt_t()
idaapi.get_bpt(ea, bpt)
bpt.elang = 'Python'
bpt.condition = cond
idaapi.update_bpt(bpt)

def hook_shl():
ebx = idc.get_reg_value("ebx")
cl = idc.get_reg_value("cl")
val1 = ebx
val2 = cl
print(f"{val1:x} << {val2:x} = {(val1 << val2)&0xFFFFFFFF:x}")

def hook_shr():
ebx = idc.get_reg_value("ebx")
cl = idc.get_reg_value("cl")
val1 = ebx
val2 = cl
print(f"{val1:x} >> {val2:x} = {(val1 >> val2)&0xFFFFFFFF:x}")

def hook_add():
edx = idc.get_reg_value("edx")
rax = idc.get_reg_value("rax")
rbx = idc.get_reg_value("rbx")
val1 = edx
val2 = idc.get_wide_dword(rax + rbx*4)
print(f"{val1:x} + {val2:x} = {(val1 + val2)&0xFFFFFFFF:x}")

def hook_xor():
edx = idc.get_reg_value("edx")
rax = idc.get_reg_value("rax")
rbx = idc.get_reg_value("rbx")
val1 = edx
val2 = idc.get_wide_dword(rax + rbx*4)
print(f"{val1:x} ^ {val2:x} = {(val1 ^ val2)&0xFFFFFFFF:x}")

def hook_sub():
edx = idc.get_reg_value("edx")
rax = idc.get_reg_value("rax")
rbx = idc.get_reg_value("rbx")
val1 = edx
val2 = idc.get_wide_dword(rax + rbx*4)
print(f"{val1:x} - {val2:x} = {(val1 - val2)&0xFFFFFFFF:x}")

def hook_mul():
edx = idc.get_reg_value("edx")
ebx = idc.get_reg_value("ebx")
val1 = edx
val2 = ebx
print(f"{val1:x} * {val2:x} = {(val1 * val2)&0xFFFFFFFF:x}")

def hook_and():
edx = idc.get_reg_value("edx")
rax = idc.get_reg_value("rax")
rbx = idc.get_reg_value("rbx")
val1 = edx
val2 = idc.get_wide_dword(rax + rbx*4)
print(f"{val1:x} & {val2:x} = {(val1 & val2)&0xFFFFFFFF:x}")

set_python_bpt(base + 0x0000000000A8860, 'hook_shr()')
set_python_bpt(base + 0x0000000000A8820, 'hook_shl()')
set_python_bpt(base + 0x00000000000A871D, 'hook_add()')
set_python_bpt(base + 0x00000000000A87DD, 'hook_xor()')
set_python_bpt(base + 0x00000000000A875D, 'hook_sub()')
set_python_bpt(base + 0x0000000000A87A0, 'hook_mul()')
set_python_bpt(base + 0x0000000000A889A, 'hook_and()')

将结果保存到record.txt,进行分析

算法将两个进行部分魔改的xxtea夹在了一起,所以并不好看,将其分离开来

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
9e37 << 10 = 9e370000
79b9 + 9e370000 = 9e3779b9
9e37 << 10 = 9e370000
79b9 + 9e370000 = 9e3779b9

# tmp1 = (f[4] >> 5) ^ (f[1] <<2)
48474645 << 2 = 211d1914
54535251 >> 5 = 2a29a92
211d1914 ^ 2a29a92 = 23bf8386

# tmp3 = (f[1] >> 3) ^ (f[4] << 4)
48474645 >> 3 = 908e8c8

54535251 << 4 = 45352510
908e8c8 ^ 45352510 = 4c3dcdd8

# tmp4 = (f[4] >> 5) ^ (f[1] <<2) + (f[1] >> 3) ^ (f[4] << 4)
23bf8386 + 4c3dcdd8 = 6ffd515e

# e = (sum >> 2) & 3;
9e3779b9 >> 2 = 278dde6e
278dde6e & 3 = 2

# (p&3)^e
2 ^ 0 = 2

# (sum ^ f[1])
9e3779b9 ^ 48474645 = d6703ffc

# (key[(p&3)^e] ^ z)
54535251 ^ a78c0b4f = f3df591e

# (sum^y) + (key[(p&3)^e] ^ z))
d6703ffc + f3df591e = ca4f991a

# MX = ((f[4] >> 5) ^ (f[1] <<2) + (f[1] >> 3) ^ (f[4] << 4)) ^ (sum ^ f[1]) + (key[(p&3)^e] ^ f[4]))
6ffd515e ^ ca4f991a = a5b2c844

# z = v[0] += MX
a5b2c844 + 44434241 = e9f60a85

# tmp2 = (f[6] >> 2) ^ (f[9] << 5)
62615a59 >> 2 = 18985696
6e6d6c6b << 5 = cdad8d60
18985696 ^ cdad8d60 = d535dbf6

# tmp5 = (f[6] << 3) ^ (f[9] >> 4)
62615a59 << 3 = 130ad2c8
6e6d6c6b >> 4 = 6e6d6c6
130ad2c8 ^ 6e6d6c6 = 15ec040e

# tmp6 = tmp2 + tmp5
# tmp6 = (f[6] >> 2) ^ (f[9] << 5) + (f[6] << 3) ^ (f[9] >> 4)
d535dbf6 + 15ec040e = eb21e004

a78c << 10 = a78c0000
b4f + a78c0000 = a78c0b4f
9f1c << 10 = 9f1c0000

# e = (sum >> 2) & 3;
2 & 3 = 2
9e3779b9 >> 2 = 278dde6e
278dde6e & 3 = 2
2 * 4 = 8
2 ^ 0 = 2
8 + 20 = 28
2 & 3 = 2

#get key[0]
f72e + 9f1c0000 = 9f1cf72e
2 * 4 = 8

8 + 20 = 28

# (sum ^ f[6])
9e3779b9 ^ 62615a59 = fc5623e0

# (key[(p&3)^e] ^ f[9])
6e6d6c6b ^ 9f1cf72e = f1719b45

#(sum^y) + (key[(p&3)^e] ^ f[9])
fc5623e0 + f1719b45 = edc7bf25
6e63 << 10 = 6e630000

# MZ2 = ((f[6] >> 2) ^ (f[9] << 5) + (f[6] << 3) ^ (f[9] >> 4)) ^ (sum^y) + (key[(p&3)^e] ^ f[9])
eb21e004 ^ edc7bf25 = 6e65f21
7466 + 6e630000 = 6e637466
3230 << 10 = 32300000

3234 + 32300000 = 32303234

# z = f[5] + MZ2
6e65f21 + 58575655 = 5f3db576

exp1

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>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX1 (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t * key)
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;

sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
//printf("%x \n", (p&3)^e);
//printf("%x \n", MX1);
z = v[p] += MX1;
//printf("%x ", z);

}
y = v[0];
z = v[n-1] += MX1;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
//printf("%d\n",rounds);
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX1;
}
z = v[n-1];
y = v[0] -= MX1;
sum -= DELTA;
}
while (--rounds);
}
}

unsigned char cipher[]={ 0x5D, 0x45, 0xD5, 0xB9, 0x8C, 0x95, 0x9C, 0x38, 0x3B, 0xB1,
0x3E, 0x1E, 0x5F, 0xC8, 0xE8, 0xBB, 0x64, 0x38, 0x48, 0x69};

int main()
{
uint32_t *v = (uint32_t*)cipher;

uint32_t k[4]= {0x6e637466,0x62ef0ed,0xa78c0b4f,0x32303234};
int n= 5;
btea(v, -n, k);
unsigned char a;

for(int i = 0; i < 5; i++) {
//printf("%x ", v[i]);
}
for(int i=0;i<32;i++)
{
printf("%c", cipher[i]);
}

//NCTF{H4rd_VM_with_Go
return 0;
}

exp2

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
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX1 (((z<<5^y>>2) + (y<<3^z>>4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t * key)
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;

sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
//printf("%x \n", (p&3)^e);
//printf("%x \n", MX1);
z = v[p] += MX1;
//printf("%x ", z);

}
y = v[0];
z = v[n-1] += MX1;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
//printf("%d\n",rounds);
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX1;
}
z = v[n-1];
y = v[0] -= MX1;
sum -= DELTA;
}
while (--rounds);
}
}

unsigned char cipher[]={ 0xDE, 0x81, 0xD8, 0xAD, 0xC2, 0xC4, 0xA6, 0x32, 0x1C, 0xAB,
0x61, 0x3E, 0xCB, 0xFF, 0xEF, 0xF1, 0x27, 0x30, 0x7A, 0x16};

int main()
{
uint32_t *v = (uint32_t*)cipher;

uint32_t k[4]= {0x32303234,0xd6eb12c3,0x9f1cf72e,0x4e435446};
int n= 5;
btea(v, -n, k);
unsigned char a;

for(int i = 0; i < 5; i++) {
//printf("%x ", v[i]);
}
for(int i=0;i<20;i++)
{
printf("%c", cipher[i]);
}
//r0ut1n3_5fc4b0be7ad}
return 0;
}

NCTF{H4rd_VM_with_Gor0ut1n3_5fc4b0be7ad}

ezDos

ida打开

img

疑似是rc4变种,但没太管具体细节,直接用dosbox debug动调到0xFA处拿到xor值(是真难用啊这玩意)

https://www.cnblogs.com/BDAMBXA/p/17303042.html

按t是单步执行,g cs:地址可以类似下断点的效果

img

当时好像提取错了一个字节,忘了是哪个了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
using namespace std;
unsigned char xor_key[] = {
0x32, 0x7D, 0x59, 0x7a, 0xF3, 0x0d, 0xb3, 0x7B, 0x64, 0xbc, 0xeb, 0x28, 0xc4, 0xa4, 0x50,
0x30, 0xa0, 0xed, 0x27, 0x6a, 0xe3, 0x76, 0x69, 0x0c, 0xda, 0x28, 0xf8, 0x08, 0xba, 0xa6,
0x17, 0x3e, 0x12, 0x59, 0x45, 0x06, 0x4e, 0xf1
};
//NCTF{Y0u_ar3_Assemb1y_M4st3r_5d0b497e}
unsigned char cipher[] = { 0x7C, 0x3E, 0x0D, 0x3C, 0x88, 0x54, 0x83, 0x0E, 0x3B,
0xB8, 0x99, 0x1B, 0x9B, 0xE5, 0x23, 0x43, 0xC5, 0x80, 0x45,
0x5B, 0x9A, 0x29, 0x24, 0x38, 0xA9, 0x5C, 0xCB, 0x7A, 0xE5,
0x93, 0x73, 0x0E, 0x70, 0x6D, 0x7C, 0x31, 0x2B, 0x8C};
int main()
{
unsigned char a;
for(int i = 0; i < sizeof(xor_key); i++) {
a = cipher[i] ^ xor_key[i];
printf("%x ", a);
}
}

NCTF{Y0u_ar3_Assemb1y_M4st3r_5d0b497e}

SafeProgram

tls的对key和sbox进行了变换

img

crc校验反调试直接nop绕过

img

主函数就是sm4标准算法

img

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
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
//sm4.h
#pragma once
#ifndef _SM4_H_
#define _SM4_H_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define u8 unsigned char
#define u32 unsigned long

void four_uCh2uLong(u8* in, u32* out); //四字节转换成u32

void uLong2four_uCh(u32 in, u8* out); //u32转换成四字节

unsigned long move(u32 data, int length); //左移,保留丢弃位放置尾部

unsigned long func_key(u32 input); //先使用Sbox进行非线性变化,再将线性变换L置换为L'

unsigned long func_data(u32 input); //先使用Sbox进行非线性变化,再进行线性变换L

void print_hex(u8* data, int len); //无符号字符数组转16进制打印

void encode_fun(u8 len, u8* key, u8* input, u8* output); //加密函数

void decode_fun(u8 len, u8* key, u8* input, u8* output); //解密函数

/******************************定义系统参数FK的取值****************************************/
const u32 TBL_SYS_PARAMS[4] = {
0xa3b1bac6,
0x56aa3350,
0x677d9197,
0xb27022dc
};

/******************************定义固定参数CK的取值****************************************/
const u32 TBL_FIX_PARAMS[32] = {

0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,
0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
0x10171e25,0x2c333a41,0x484f565d,0x646b7279
};

/******************************SBox参数列表****************************************/
const u8 TBL_SBOX[256] = {

0xD1, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6,
0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05, 0x2B, 0x67, 0x9A, 0x76,
0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86,
0x06, 0x99, 0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A,
0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62, 0xE4, 0xB3,
0x17, 0xA9, 0x1C, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA,
0x75, 0x8F, 0x3F, 0xA6, 0x47, 0x07, 0xA7, 0x4F, 0xF3, 0x73,
0x71, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0xD6, 0xA8,
0x68, 0x6B, 0x81, 0xB2, 0xFC, 0x64, 0xDA, 0x8B, 0xF8, 0xEB,
0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35, 0x1E, 0x24, 0x0E, 0x78,
0x63, 0x58, 0x9F, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21,
0xC9, 0x87, 0xD4, 0x00, 0x46, 0x57, 0x5E, 0xD3, 0x27, 0x52,
0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E, 0xEA, 0xBF,
0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE,
0xF9, 0x61, 0x15, 0xA1, 0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34,
0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29,
0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F, 0xD5, 0xDB, 0x37, 0x45,
0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C,
0x5B, 0x51, 0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F,
0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8, 0x0A, 0xC1,
0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12,
0xB8, 0xE5, 0xB4, 0xB0, 0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96,
0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE,
0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
};

#endif
//main.c

#include "sm4.h"

//4字节无符号数组转无符号long型
void four_uCh2uLong(u8* in, u32* out)
{
int i = 0;
*out = 0;
for (i = 0; i < 4; i++)
*out = ((u32)in[i] << (24 - i * 8)) ^ *out;
}

//无符号long型转4字节无符号数组
void uLong2four_uCh(u32 in, u8* out)
{
int i = 0;
//从32位unsigned long的高位开始取
for (i = 0; i < 4; i++)
*(out + i) = (u32)(in >> (24 - i * 8));
}

//左移,保留丢弃位放置尾部
u32 move(u32 data, int length)
{
u32 result = 0;
result = (data << length) ^ (data >> (32 - length));

return result;
}

//秘钥处理函数,先使用Sbox进行非线性变化,再将线性变换L置换为L'
u32 func_key(u32 input)
{
int i = 0;
u32 ulTmp = 0;
u8 ucIndexList[4] = { 0 };
u8 ucSboxValueList[4] = { 0 };
uLong2four_uCh(input, ucIndexList);
for (i = 0; i < 4; i++)
{
ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
}
four_uCh2uLong(ucSboxValueList, &ulTmp);
ulTmp = ulTmp ^ move(ulTmp, 13) ^ move(ulTmp, 23);

return ulTmp;
}

//加解密数据处理函数,先使用Sbox进行非线性变化,再进行线性变换L
u32 func_data(u32 input)
{
int i = 0;
u32 ulTmp = 0;
u8 ucIndexList[4] = { 0 };
u8 ucSboxValueList[4] = { 0 };
uLong2four_uCh(input, ucIndexList);
for (i = 0; i < 4; i++)
{
ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
}
four_uCh2uLong(ucSboxValueList, &ulTmp);
ulTmp = ulTmp ^ move(ulTmp, 2) ^ move(ulTmp, 10) ^ move(ulTmp, 18) ^ move(ulTmp, 24);

return ulTmp;
}

//加密函数(可以加密任意长度数据,16字节为一次循环,不足部分补0凑齐16字节的整数倍)
//len:数据长度(任意长度数据) key:密钥(16字节) input:输入的原始数据 output:加密后输出数据
void encode_fun(u8 len, u8* key, u8* input, u8* output)
{
int i = 0, j = 0;
u8* p = (u8*)malloc(50); //定义一个50字节缓存区
u32 ulKeyTmpList[4] = { 0 }; //存储密钥的u32数据
u32 ulKeyList[36] = { 0 }; //用于密钥扩展算法与系统参数FK运算后的结果存储
u32 ulDataList[36] = { 0 }; //用于存放加密数据

/***************************开始生成子秘钥********************************************/
four_uCh2uLong(key, &(ulKeyTmpList[0]));
four_uCh2uLong(key + 4, &(ulKeyTmpList[1]));
four_uCh2uLong(key + 8, &(ulKeyTmpList[2]));
four_uCh2uLong(key + 12, &(ulKeyTmpList[3]));

ulKeyList[0] = ulKeyTmpList[0] ^ TBL_SYS_PARAMS[0];
ulKeyList[1] = ulKeyTmpList[1] ^ TBL_SYS_PARAMS[1];
ulKeyList[2] = ulKeyTmpList[2] ^ TBL_SYS_PARAMS[2];
ulKeyList[3] = ulKeyTmpList[3] ^ TBL_SYS_PARAMS[3];

for (i = 0; i < 32; i++) //32次循环迭代运算
{
//5-36为32个子秘钥
ulKeyList[i + 4] = ulKeyList[i] ^ func_key(ulKeyList[i + 1] ^ ulKeyList[i + 2] ^ ulKeyList[i + 3] ^ TBL_FIX_PARAMS[i]);
}
/***********************************生成32轮32位长子秘钥结束**********************************/

for (i = 0; i < len; i++) //将输入数据存放在p缓存区
*(p + i) = *(input + i);
for (i = 0; i < 16 - len % 16; i++)//将不足16位补0凑齐16的整数倍
*(p + len + i) = 0;

for (j = 0; j < len / 16 + ((len % 16) ? 1 : 0); j++) //进行循环加密,并将加密后数据保存(可以看出此处是以16字节为一次加密,进行循环,即若16字节则进行一次,17字节补0至32字节后进行加密两次,以此类推)
{
/*开始处理加密数据*/
four_uCh2uLong(p + 16 * j, &(ulDataList[0]));
four_uCh2uLong(p + 16 * j + 4, &(ulDataList[1]));
four_uCh2uLong(p + 16 * j + 8, &(ulDataList[2]));
four_uCh2uLong(p + 16 * j + 12, &(ulDataList[3]));
//加密
for (i = 0; i < 32; i++)
{
ulDataList[i + 4] = ulDataList[i] ^ func_data(ulDataList[i + 1] ^ ulDataList[i + 2] ^ ulDataList[i + 3] ^ ulKeyList[i + 4]);
}
/*将加密后数据输出*/
uLong2four_uCh(ulDataList[35], output + 16 * j);
uLong2four_uCh(ulDataList[34], output + 16 * j + 4);
uLong2four_uCh(ulDataList[33], output + 16 * j + 8);
uLong2four_uCh(ulDataList[32], output + 16 * j + 12);
}
free(p);
}

//解密函数(与加密函数基本一致,只是秘钥使用的顺序不同,即把钥匙反着用就是解密)
//len:数据长度 key:密钥 input:输入的加密后数据 output:输出的解密后数据
void decode_fun(u8 len, u8* key, u8* input, u8* output)
{
int i = 0, j = 0;
u32 ulKeyTmpList[4] = { 0 };//存储密钥的u32数据
u32 ulKeyList[36] = { 0 }; //用于密钥扩展算法与系统参数FK运算后的结果存储
u32 ulDataList[36] = { 0 }; //用于存放加密数据

/*开始生成子秘钥*/
four_uCh2uLong(key, &(ulKeyTmpList[0]));
four_uCh2uLong(key + 4, &(ulKeyTmpList[1]));
four_uCh2uLong(key + 8, &(ulKeyTmpList[2]));
four_uCh2uLong(key + 12, &(ulKeyTmpList[3]));

ulKeyList[0] = ulKeyTmpList[0] ^ TBL_SYS_PARAMS[0];
ulKeyList[1] = ulKeyTmpList[1] ^ TBL_SYS_PARAMS[1];
ulKeyList[2] = ulKeyTmpList[2] ^ TBL_SYS_PARAMS[2];
ulKeyList[3] = ulKeyTmpList[3] ^ TBL_SYS_PARAMS[3];

for (i = 0; i < 32; i++) //32次循环迭代运算
{
//5-36为32个子秘钥
ulKeyList[i + 4] = ulKeyList[i] ^ func_key(ulKeyList[i + 1] ^ ulKeyList[i + 2] ^ ulKeyList[i + 3] ^ TBL_FIX_PARAMS[i]);
}
/*生成32轮32位长子秘钥结束*/

for (j = 0; j < len / 16; j++) //进行循环加密,并将加密后数据保存
{
/*开始处理解密数据*/
four_uCh2uLong(input + 16 * j, &(ulDataList[0]));
four_uCh2uLong(input + 16 * j + 4, &(ulDataList[1]));
four_uCh2uLong(input + 16 * j + 8, &(ulDataList[2]));
four_uCh2uLong(input + 16 * j + 12, &(ulDataList[3]));

//解密
for (i = 0; i < 32; i++)
{
ulDataList[i + 4] = ulDataList[i] ^ func_data(ulDataList[i + 1] ^ ulDataList[i + 2] ^ ulDataList[i + 3] ^ ulKeyList[35 - i]);//与加密唯一不同的就是轮密钥的使用顺序
}
/*将解密后数据输出*/
uLong2four_uCh(ulDataList[35], output + 16 * j);
uLong2four_uCh(ulDataList[34], output + 16 * j + 4);
uLong2four_uCh(ulDataList[33], output + 16 * j + 8);
uLong2four_uCh(ulDataList[32], output + 16 * j + 12);
}
}

//无符号字符数组转16进制打印
void print_hex(u8* data, int len)
{
int i = 0;
char alTmp[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
for (i = 0; i < len; i++)
{
printf("%c", alTmp[data[i] / 16]);
printf("%c", alTmp[data[i] % 16]);
putchar(' ');
}
putchar('\n');
}
/*在主函数中实现任意字节加密与解密,并且结果正确*/
int main(void)
{
u8 i, len;
u8 encode_Result[50] = {
0xFB, 0x97, 0x3C, 0x3B, 0xF1, 0x99, 0x12, 0xDF, 0x13, 0x30,
0xF7, 0xD8, 0x7F, 0xEB, 0xA0, 0x6C, 0x14, 0x5B, 0xA6, 0x2A,
0xA8, 0x05, 0xA5, 0xF3, 0x76, 0xBE, 0xC9, 0x01, 0xF9, 0x36,
0x7B, 0x46
}; //定义加密输出缓存区
u8 decode_Result[50] = { 0 }; //定义解密输出缓存区
u8 key[16] = "NCTF24nctfNCTF24"; //定义16字节的密钥
//u8 Data_plain[18] = { 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,0x01,0x23 };//定义18字节的原始输入数据(测试用)
//u8 Data_plain[32] = { 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10 };//定义32字节的原始输入数据(测试用)
u8 Data_plain[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef";//定义16字节的原始输入数据(测试用)
len = 32;

//encode_fun(sizeof(Data_plain), key, Data_plain, encode_Result); //数据加密
printf("加密后数据是:\n");

len = 32;
for (i = 0; i < len; i++)
printf("%x ", *(encode_Result + i));
/*注意:此处解密函数的输入数据长度应为扩展后的数据长度,即必为16的倍数*/
decode_fun(len, key, encode_Result, decode_Result); //数据解密
printf("解密后数据是:\n");
for (i = 0; i < len; i++)
printf("%c", *(decode_Result + i));

system("pause");
return 0;
}

NCTF{58cb925e0cd823c0d0b54fd06b820b7e}

  • 标题: NCTF 2024 Reverse Writeup
  • 作者: 两只羊
  • 创建于 : 2025-03-02 16:03:14
  • 更新于 : 2025-05-09 19:06:46
  • 链接: https://twogoat.github.io/2025/03/02/NCTF-2024-Reverse-Writeup/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
NCTF 2024 Reverse Writeup