[Google CTF 2021] CPP WriteUp

打完 CISCN 2021 无事,想起群里之前说到的 DRM,于是和队友一起尝试了一下(

预处理

由于各种 #xxx 完全没有缩进,因此使用 clang-format 对预处理器进行格式化处理:

clang-format --style='{IndentPPDirectives: AfterHash}' cpp.c

缩进后的代码虽然算不上好读,但至少能够分析了:

观察

#if __INCLUDE_LEVEL__ > 12 一行开始,接下来就是大段的 #if S == xx。在每个 #if 之后紧跟的又是 #unset S#defineS,对应的是 S++。每个 S 对应一条不同的指令。

简单观察一条指令,不难发现其每次都会对 0-7 进行 define。比如上面截图里就 defineR0-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('')

flagCTF{pr3pr0cess0r_pr0fe5sor}

暂无评论

发送评论 编辑评论


上一篇
下一篇