打完 CISCN 2021
无事,想起群里之前说到的 DRM
,于是和队友一起尝试了一下(
ToC
预处理
由于各种 #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 = 1Y = 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 = YJMP _1
_main:I = 0M = 0N = 1P = 0Q = 0
_29:B = 229 + Iif (B == 0) { if (Q == 0) { return } else { invalid flag }}
########################A = FLAG[I]B = [I]R = 1
CALL _12// A = A * [I]########################
O = M + NM = NN = 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 + 32C = [B]
A = A ^ C// P += (FLAG[I]*[I] + fib(I)) ^ [32+I]P = P + A
B = I + 64A = [B]
// P == [64+I]A = A ^ PQ = Q | AI++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}
: