daily-alpacahack-twilight

  • ~2.62K 字
  1. 1. daily alpacahack twilight
    1. 1.1. はじめに
    2. 1.2. 問題
    3. 1.3. 解説
      1. 1.3.1. デコンパイル
        1. 1.3.1.1. 関数a
        2. 1.3.1.2. 関数b
        3. 1.3.1.3. 再度mainへ
      2. 1.3.2. 具体的には…?
        1. 1.3.2.1. 逆算してみよう
  2. 2. 最後に

daily alpacahack twilight

はじめに

みなさん、Daily alpacahack楽しんでますか?

簡単な問題も難しい問題も含めて様々なものが用意されているので、是非楽しんで、新しいことを学んでいってください。ただ、苦しい時もあるかもしれませんがCTFはTry Harderの精神があるので、それもまた一興で楽しんでください。

問題

今回はreversingのtwilightという問題を解いていきます。

解説

添付してあるのは実行ファイルと出力されたであろうファイルです。

実行ファイルに関してはx86-64のアーキテクチャのようです。お手元にあるデコンパイラでデコンパイルして中を解析してみましょうという問題です。(私はidaで解析していきます。)

image-20251216195237539

デコンパイル

デコンパイルした結果です。読みやすいように変数名を変えます。

image-20251218162946827

変数名は、カーソルを合わせて右クリック→Rename lvarから行うことができます。(ショートカットはNです)

ここでは、以下を変更しています。

  • v6をFUNCTIONS
  • sをINPUT

image-20251218163048788

様々な処理がありますが、重要なのは以下の点です。

  1. 11行目にあるscanfで32文字の入力を受け取り、INPUT変数に格納します。
  2. 12行目~13行目でFUNCTIONSに「a」、「b」の関数を格納しています。
  3. 14行目~18行目のfor文でループ処理をしています。(この時ループ回数をiとしています。)
  4. 16行目で、INPUTのi番目とループ回数iを引数としてFUNCTIONSのi%2番目を実行しています。
  5. 16、17行目で、FUNCTIONSのi%2番目を実行した結果をv3に格納して16進数で表示しています。

image-20251218163245154

それでは、各関数を見ていきましょう

関数a

この関数はa1,a2の引数をとって足し算しているように見えます。

なので、読みやすくするために関数名の書き換えを実施します。(idaはカーソルを関数名に合わせてnで書き換えられます。)

書き換え前

image-20251216200231689

書き換え後

image-20251216200435477

関数b

この関数はa1,a2の引数をとって排他的論理和しているように見えます。

なので、読みやすくするために関数名の書き換えを実施します。(idaはカーソルを関数名に合わせてnで書き換えられます。)

書き換え前

image-20251218163423649

書き換え後

image-20251218163434832

再度mainへ

再度mainへ戻り(idaでは、funcionタブにあるmainやESCキーを押すことで戻れます)デコンパイル結果を見てみるとFUNCTIONSの関数名が先ほど書き換えた内容になり少しは読みやすくなっていると思います。

image-20251218163459346

具体的には…?

ここから1ループだけ具体的な内容と共に追っていきます。

for文のiは0から始まるため、i=0として考えます。そうすると、16行目は以下のように見なすことができます。

v3 = FUNCTIONS[0](INPUT[0],0);

更に、FUNCTIONS[0]というのは12行目にあるようにadd関数なので書き換えることができます。

v3 = add(INPUT[0],0);

このことから出力の最初にある値は、flagの0番目とループ回数の0が足された値になることがわかります。この時、出力v3ループ回数が既知であることからINPUT[0]は逆算可能になることがわかります。

逆算してみよう

先の内容から、v3ループ回数が既知の状態でINPUT[i]を求めるような逆関数を考えればよさそうです。

以下がPythonで関数と逆関数を書いた結果です。一つ注意として、XORの逆関数はXORそのものなため新しく定義をしていません。

1
2
3
4
5
6
7
8
9
10
11
def add(a1, a2):
return a1 + a2

# add逆関数
def sub(a1, v3):
return v3 - a1

# xorの逆関数はxor
def xor(a1, a2):
return a1 ^ a2

これらを使って、for文も含めてデコンパイル結果をPythonで表現すると、以下になります。

1
2
3
4
5
6
7

INPUT = "A"*32 # 本当はここにflagが入ります

FUNCTIONS = [add, xor]

for i in range(len(INPUT)):
print(hex(FUNCTIONS[i%2](INPUT[i], i)))

逆算するPythonコードは以下です。

1
2
3
4
5
6
7
8
9
10
11
12
13
OUT = [0x41, 0x6D, 0x72, 0x62, 0x67, 0x64, 0x81, 0x46, 0x74, 0x79, 0x6B, 0x68, 0x6D, 0x45, 0x6F, 0x6C, 0x7B, 0x4E, 0x7B, 0x7D, 0x73, 0x42, 0x85, 0x79, 0x7C, 0x7C, 0x8C, 0x77, 0x7D, 0x73, 0x82, 0x62]

INPUT = []

# FUNCTIONS = [add, xor]
INV_FUNCTIONS = [sub, xor]

for i in range(len(OUT)):
v3 = INV_FUNCTIONS[i % 2](i, OUT[i])
INPUT.append(v3)

print(bytes(INPUT))
# b'Alpaca{AlpacaHack_in_Wonderland}'

最後に

この問題に対する解き方は様々な方法があると思います。今回は、これからのrevでよく使うであろうデコンパイラで解析してみようという問題にしました。(デコンパイラ使わなくてもできると思いますが…)

この小さいバイナリーを使って命名や各関数のチェックをする方法を学んでいただければ今後に生かすことができると考えていますので、是非デコンパイラを使って解いてみてください。ご参加いただき、ありがとうございました。