打完 CISCN 2021
无事,想起群里之前说到的 DRM
,于是和队友一起尝试了一下(
预处理
由于各种 #xxx
完全没有缩进,因此使用 clang-format
对预处理器进行格式化处理:
clang-format --style='{IndentPPDirectives: AfterHash}' cpp.c
缩进后的代码虽然算不上好读,但至少能够分析了:
观察
从 #if __INCLUDE_LEVEL__ > 12
一行开始,接下来就是大段的 #if S == xx
。在每个 #if
之后紧跟的又是 #unset S
和 #define
新 S
,对应的是 S++
。每个 S
对应一条不同的指令。
简单观察一条指令,不难发现其每次都会对 0-7
进行 define
。比如上面截图里就 define
了 R0-R7
。因此不难想到一个 R
其实是由 8 位构成的,即一个字节。因此之后的整理也就基本基于这个思路了。
分析
分析后的指令对照如下所示:
#if __INCLUDE_LEVEL__ > 12
# if S == 0
# define S 24
# endif
# if S == 1
//# S++
//# R = !R
# endif
# if S == 2
//# S++
//# Z = 0b00000001
# endif
# if S == 3
//# S++
//# R = R + Z
# endif
# if S == 4
//# S++
//# R = R + Z
# endif
# if S == 5
//# S++
//# if R == 0 {
//# S = 38
//# }
# endif
# if S == 6
//# S++
//# R = R + Z
# endif
# if S == 7
//# S++
//# if R == 0 {
//# S = 59
//# }
# endif
# if S == 8
//# S++
//# R = R + Z
# endif
# if S == 9
//# S++
//# if R == 0 {
//# S = 59
//# }
# endif
# if S == 10
//# S++
# error "BUG"
# endif
# if S == 11
//# S = -1
# endif
# if S == 12
//# S++
//# X = 0b00000001
# endif
# if S == 13
//# S++
//# Y = 0
# endif
# if S == 14
//# S++
//# if X == 0 {
//# S = 22
//# }
# endif
# if S == 15
//# S++
//# Z = X
# endif
# if S == 16
//# S++
//# Z = Z & B
# endif
# if S == 17
//# S++
//# if Z == 0 {
//# S = 19
//# }
# endif
# if S == 18
//# S++
//# Y = Y + A
# endif
# if S == 19
//# S++
//# X = X + X
# endif
# if S == 20
//# S++
//# A = A + A
# endif
# if S == 21
//# S = 14
# endif
# if S == 22
//# S++
//# A = Y
# endif
# if S == 23
//# S = 1
# endif
# if S == 24
//# S++
//# I = 0
# endif
# if S == 25
//# S++
//# M = 0
# endif
# if S == 26
//# S++
//# N = 0b00000001
# endif
# if S == 27
//# S++
//# P = 0
# endif
# if S == 28
//# S++
//# Q = 0
# endif
# if S == 29
//# S++
//# B = 0b11100101
# endif
# if S == 30
//# S++
//# B = B + I
# endif
# if S == 31
//# S++
//# if B == 0 {
//# S = 56
//# }
# endif
# if S == 32
//# S++
//# B = 0b10000000
# endif
# if S == 33
//# S++
//# B = B + I
# endif
# if S == 34
//# S++
//# l = B
//# A = [l]
# endif
# if S == 35
//# S++
//# l = I
//# B = [l]
# endif
# if S == 36
//# S++
//# R = 0b00000001
# endif
# if S == 37
//# S = 12
# endif
# if S == 38
//# S++
//# O = M
# endif
# if S == 39
//# S++
//# O = O + N
# endif
# if S == 40
//# S++
//# M = N
# endif
# if S == 41
//# S++
//# N = O
# endif
# if S == 42
//# S++
//# A = A + M
# endif
# if S == 43
//# S++
//# B = 0b00100000
# endif
# if S == 44
//# S++
//# B = B + I
# endif
# if S == 45
//# S++
//# l = B
//# C = [l]
# endif
# if S == 46
//# S++
//# A = A ^ C
# endif
# if S == 47
//# S++
//# P = P + A
# endif
# if S == 48
//# S++
//# B = 0b01000000
# endif
# if S == 49
//# S++
//# B = B + I
# endif
# if S == 50
//# S++
//# l = B
//# A = [l]
# endif
# if S == 51
//# S++
//# A = P ^ A
# endif
# if S == 52
//# S++
//# Q = Q | A
# endif
# if S == 53
//# S++
//# A = 0b00000001
# endif
# if S == 54
//# S++
//# I = A + I
# endif
# if S == 55
//# S = 29
# endif
# if S == 56
//# S++
//# if Q == 0 {
//# S = 58
//# }
# endif
# if S == 57
//# S++
# error "INVALID_FLAG"
# endif
# if S == 58
//# S = -1
# endif
#else
# if S != -1
# include "cpp.c"
# endif
# if S != -1
# include "cpp.c"
# endif
#endif
整理后得到如下流程:
_1:
if (R == 1)
R = !R
RET
}
if (R == 2)
R = !R
JMP _59 (return)
}
if (R == 3)
R = !R
JMP _59 (return)
}
unreachable!()
_11:
return
############################################
// for (X = 1, Y = 0; X != 0; X *= 2, A *= 2)
_12:
X = 1
Y = 0
while(X != 0) {
Z = X & B
if (Z != 0) {
Y = Y + A
}
X *= 2 // 8 times
A *= 2
}
//for (X = 1; X <= 8; X++) {
// if (B & (1 << X)) {
// Y += A * (1 << X)
// }
//}
############################################
_22:
A = Y
JMP _1
_main:
I = 0
M = 0
N = 1
P = 0
Q = 0
_29:
B = 229 + I
if (B == 0) {
if (Q == 0) {
return
} else {
invalid flag
}
}
########################
A = FLAG[I]
B = [I]
R = 1
CALL _12
// A = A * [I]
########################
O = M + N
M = N
N = O
// M: 1 1 2 3 5 8 13 21 34 55 89 144 233 121 98 219 61 24 85 109 194 47 241 32 17 49 66
// A += fib(I)
A = A + M
B = I + 32
C = [B]
A = A ^ C
// P += (FLAG[I]*[I] + fib(I)) ^ [32+I]
P = P + A
B = I + 64
A = [B]
// P == [64+I]
A = A ^ P
Q = Q | A
I++
JMP _29
最后得到脚本:
function to_byte(n) {
return n & 0xff
}
const first = [
0b10111011,
0b01010101,
0b10101011,
0b11000101,
0b10111001,
0b10011101,
0b11001001,
0b01101001,
0b10111011,
0b00110111,
0b11011001,
0b11001101,
0b00100001,
0b10110011,
0b11001111,
0b11001111,
0b10011111,
0b00001001,
0b10110101,
0b00111101,
0b11101011,
0b01111111,
0b01010111,
0b10100001,
0b11101011,
0b10000111,
0b01100111,
];
const second = [
0b00001000,
0b01100100,
0b01100100,
0b00110101,
0b10010001,
0b01100100,
0b11100111,
0b10100000,
0b00000110,
0b10101010,
0b11011101,
0b01110101,
0b00010111,
0b10011101,
0b01101101,
0b01011100,
0b01011110,
0b00011001,
0b11111101,
0b11101001,
0b00001100,
0b11111001,
0b10110100,
0b10000011,
0b10000110,
0b00100010,
0b01000010,
]
const third = [
0b11111010,
0b01111011,
0b00011011,
0b10111010,
0b00011110,
0b10110100,
0b10110011,
0b01011000,
0b11000110,
0b11110011,
0b10001100,
0b10010000,
0b00111011,
0b10111010,
0b00011001,
0b01101110,
0b11001110,
0b11011111,
0b11110001,
0b00100101,
0b10001101,
0b01000000,
0b10000000,
0b01110000,
0b11100000,
0b01001101,
0b00011100,
]
const possible = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_'.split('').map(c => c.charCodeAt(0))
const fib = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55,
89, 144, 233, 377, 610, 987, 1597, 2584, 4181,
6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418].map(to_byte)
function guess(i) {
return possible.filter(c => {
let P = i === 0 ? 0 : third[i - 1]
P += (to_byte(c * first[i]) + fib[i]) ^ second[i]
P = to_byte(P)
return P === third[i]
}).map(c => String.fromCharCode(c))
}
[...Array(27).keys()].map(i => guess(i)).map(a => a[0]).join('')
flag
为 CTF{pr3pr0cess0r_pr0fe5sor}
: