ReversingはReversingで解こう (ctf4b 2021)

競技終了後、とある方のWriteupを読んでいるとrev問を全て静的解析で解いていて、そういう解き方でも解いてみたいな、と感じたのでやってみることにしました。

mopisec.hatenablog.com

only_read

最初から静的解析して解いていたのでスキップ。

children

main関数を整理するとhogeという関数が呼ばれていくことがわかるので、処理に読んでいきます。(その先のvvv関数についても解析を進めました。)
エンコードされているデータのバイト列を抽出して、デコード関数を手元で実装することで解くことができます。

enc_flag1 = [0x63,0x75,0x64,0x37,0x66,0x7e,0x76,0x37,0x7f,0x6c]
enc_flag2 = [0x72,0x67,0x77,0x32,0x5b,0x71,0x74,0x33,0x7b,0x60]
enc_flag3 = [0x6e,0x66,0x5d,0x77,0x34,0x35,0x37,0x32,0x57,0x38]
enc_flag4 = [0x35,0x5e,0x77,0x70,0x61,0x63,0x73,0x36,0x75,0x00]

def decode_flag(encoded):
    res = ""
    i = 0
    while i < 10:
        res += chr(encoded[i] ^ i)
        i += 1
    return res

flag = ""
flag += decode_flag(enc_flag1)
flag += decode_flag(enc_flag2)
flag += decode_flag(enc_flag3)
flag += decode_flag(enc_flag4)
print(flag)

以上のスクリプトを実行すると

> python .\childrensolver.py
ctf4b{p0werfu1_tr4sing_t0015_15_usefu1}

Flagが出力されます。

please_not_trace_me

静的解析する過程で rc4 という関数が見つかるので、RC4暗号が使われているのだろうと予想できます。 generate_key 関数から順番に解析を進めていくことにしました。

Ghidraによるデコンパイル結果:

void generate_key(char *param_1)
{
  bool bVar1;
  
  bVar1 = false;
  do {
    while (!bVar1) {
      _2_stringEncoder(0,encodeStrings_litStr0);
      strcpy(param_1,encodeStrings_litStr0);
      bVar1 = true;
    }
  } while (!bVar1);
  return;
}

_2_stringEncoder 関数から得た文字列を使用しているようです。

void _2_stringEncoder(int param_1,undefined *param_2)
{
  if (param_1 == 0) {
    *param_2 = 0x6e;
    param_2[1] = 0x69;
    param_2[2] = 99;
    param_2[3] = 0x6b;
    param_2[4] = 0x65;
    param_2[5] = 0x6c;
    param_2[6] = 0x6f;
    param_2[7] = 100;
    param_2[8] = 0x65;
    param_2[9] = 0x6f;
    param_2[10] = 0x6e;
    param_2[0xb] = 0;
  }
  return;
}

以上の内容をもとにソルバを書くと、以下のようなものになります。

# From http://inaz2.hatenablog.com/entry/2013/11/30/233649
def KSA(key):
    # key から256マスの変換テーブル S を作る
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + ord(key[i % len(key)])) % 256
        S[i], S[j] = S[j], S[i]
    return S

encrypted = b'\x80\x97\x85\xd7\x81\x98\x87\xd2\x87\xbc\x9a\xd3\x96\xbc\x87\xd0\x80\x91\x9a\x93\x97\xbc\x91\x80\xd7\xdc\x9e'
key = ''.join([chr(kchar) for kchar in [0x6e,0x69,99,0x6b,0x65,0x6c,0x6f,100,0x65,0x6f,0x6e]])

S = KSA(key)
flag = ''
for i in range(len(encrypted)):
    flag += chr(encrypted[i] ^ S[S[0] * 2]) # param_1[local_14c] ^ local_118[(int)(uint)(byte)(local_118[0] * '\x02')

print(flag)

これを実行することで、Flagが得られます。

> python .\pntmsolver.py
ctf4b{d1d_y0u_d3crypt_rc4?}

be_angry

angrで解くことが想定されている問題だった問題です。複雑すぎて、現在のスキルでは静的解析することができませんでした。

firmware

最初から静的解析していたのでスキップ。

感想

Reversingはやっぱり面白いな、と思いました!