BYUCTF 2023 writeup

# Chain

> You know sometimes you just run out of ideas for challenge descriptions...


## ghidraによる解析
main()が見つからなかったので，Decompileを見ながらメイン処理っぽい関数をSymbol  TreeのFunctionsから探す．

それっぽい関数（FUN_00010610）が見つかる．
![Chain_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2857396/b39ba1a9-0fa7-32a6-31ff-28d4787b38a8.png)

<details>
    <summary>FUN_000106a0 Decompileソースコード</summary>

~~~
undefined4 FUN_000106a0(void)

{
  size_t sVar1;
  int iVar2;
  int local_1c4;
  uint local_1c0 [45];
  byte abStack_10c [256];
  int local_c;
  
  local_c = 0;
  memset(local_1c0,0,0xb4);
  local_1c0[0] = 0xc2;
  local_1c0[1] = 0x9c;
  local_1c0[2] = 0x65;
  local_1c0[3] = 0x83;
  local_1c0[4] = 0x95;
  local_1c0[5] = 0x66;
  local_1c0[6] = 0xfa;
  local_1c0[7] = 0x15;
  local_1c0[8] = 0x5e;
  local_1c0[9] = 0x58;
  local_1c0[10] = 0x2f;
  local_1c0[11] = 0x23;
  local_1c0[12] = 0xac;
  local_1c0[13] = 0x4f;
  local_1c0[14] = 0xa1;
  local_1c0[15] = 0x4c;
  local_1c0[16] = 0x7d;
  local_1c0[17] = 0x1e;
  local_1c0[18] = 0x69;
  local_1c0[19] = 0x80;
  local_1c0[20] = 0x8c;
  local_1c0[21] = 0x4a;
  local_1c0[22] = 0x26;
  local_1c0[23] = 0x5b;
  local_1c0[24] = 0x5f;
  local_1c0[25] = 0x91;
  local_1c0[26] = 0x30;
  local_1c0[27] = 0xcf;
  local_1c0[28] = 0xc0;
  local_1c0[29] = 0x4d;
  local_1c0[30] = 0x97;
  local_1c0[31] = 0x9b;
  local_1c0[32] = 0xba;
  local_1c0[33] = 0x20;
  local_1c0[34] = 0x77;
  local_1c0[35] = 0x4c;
  local_1c0[36] = 0xf5;
  local_1c0[37] = 0xef;
  local_1c0[38] = 0x97;
  local_1c0[39] = 0x96;
  local_1c0[40] = 0x31;
  local_1c0[41] = 0x30;
  local_1c0[42] = 0x8c;
  local_1c0[43] = 0xe2;
  puts("Password? ");
  fgets((char *)abStack_10c,0xff,stdin);
  sVar1 = strlen((char *)abStack_10c);
  if (sVar1 == 0x2d) {
    for (local_1c4 = 0; local_1c4 < 0x2c; local_1c4 = local_1c4 + 1) {
      if ((uint)((byte)FUN_000105ac[*(int *)(&DAT_00021040 + local_1c4 * 4)] ^
                abStack_10c[local_1c4]) != (local_1c0[local_1c4] & 0xff)) {
        iVar2 = printf("Wrong!");
        goto LAB_00010914;
      }
    }
    iVar2 = puts("Correct!");
  }
  else {
    iVar2 = puts("Wrong!");
  }
LAB_00010914:
  if (local_c != 0) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail(iVar2,local_c,0);
  }
  return 0;
}
~~~
</details>

入力`abStack_10c`がforループ中のif条件を満たし続ければ"Correct!"を表示するので，正しい入力自体がflagになりそう．
* `if(sVar1 == 0x2d)` 
    入力長は45文字
* `local_1c4 = 0; local_1c4 < 0x2c; local_1c4 = local_1c4 + 1`
    ループ変数`local_1c4`をインクリメント
* `if((uint)((byte)FUN_000105ac[*(int *)(&DAT_00021040 + local_1c4 * 4)] ^ abStack_10c[local_1c4]) != (local_1c0[local_1c4] & 0xff))`
    変数を整理すると，
    * `FUN_000105ac` <span style="color:red;">関数</span>
    * `DAT_00021040` 配列 &emsp; #別箇所で定義
        アセンブラコードの一部
        ![Chain_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2857396/4f342243-db15-c313-bf51-987759f2cd9a.png)
    * `local_1c0` 配列（`FUN_00010610` の最初の方で初期化）
    
    入力とXORする値について，
    * `(&DAT_00021040 + local_1c4 * 4)`
        配列`DAT_00021040`の先頭アドレスにループ変数`local_1c4*4`（配列がint型配列のため*4）を加算
        →出力はポインタ
    * `(int *)(&DAT_00021040 + local_1c4 * 4)`
        上記で出たポインタをint型ポインタ(int *)にキャスト
    * `*(int *)(&DAT_00021040 + local_1c4 * 4)`
        上記で出たポインタが指すアドレスに格納されている値を取得
    * `(byte)FUN_000105ac[*(int *)(&DAT_00021040 + local_1c4 * 4)]`
        関数に[]?
        結論を述べると，`関数FUN_000105acのアドレス＋配列DAT_00021040のlocal_1c4番目の要素の値`が指すアドレスから1バイト分読みとった値になる．
        読み取ってくる値は関数FUN_000105acに記載されているアセンブラ命令に対応するマシン語の一部．
    
    <br>

    `(byte)FUN_000105ac[*(int *)(&DAT_00021040 + local_1c4 * 4)]`に該当するアセンブリコードの詳細は以下<br>
<details open>
    <summary>アセンブリコード詳細</summary>

![Chain_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2857396/9ff7c1ec-c6d7-a0bb-5f4a-59e652d9f44a.png)

[レジスタ]はレジスタが指すアドレスに格納されている値を取得．
~~~
ldr r3,[PTR_PTR_FUN_0001094c]
~~~
`PTR_PTR_FUN_0001094c`は`FUN_000105ac`（イメージできなかった関数）を指すポインタのポインタ．
[]より，`r3` ← `FUN_000105acを指すアドレスが`ロードされる．

~~~
ldr r3,[r3,#0x0]
~~~
1行目を踏まえると，`r3` ← `FUN_000105acの先頭アドレス`

~~~
ldr r1,[PTR_DAT_00010950]
~~~
`PTR_DAT_00010950`は`DAT_00021040`（別箇所で定義されていた配列）を指すポインタ．
よって，`r1` ← `DAT_00021040の先頭アドレス`

~~~
ldr r2,[r11,#local_1c4]
~~~
`[r11,#local_1c4]`には事前に0が格納されているため，`r2` ← `0`．

~~~
ldr r2,[r1,r2,lsl #offset DAT_00021040]
~~~
`[r1, r2, lsl #offset DAT_00021040]`は`r1+(r2<<#offset)`のアドレスに格納されている値をロードしてくる．
`r1=DAT_00021040の先頭アドレス, r2=0`より，ここでは配列`DAT_00021040`の最初の要素（0x0E）になる．

~~~
add r3,r3,r2
ldrb r3,[r3,#0x0]
~~~
`r3=関数FUN_000105acのアドレス, r2=0x0E`より，`関数がマップされている先頭アドレス＋0x0E`のアドレスから1バイトロードしている．（ここがDecompileの関数名[]の処理と推測）
FUN_000105ac付近のアセンブリコードは以下
![Chain_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2857396/f477e88e-fe60-d309-17e5-80b0b25eb85d.png)
`[000105ac+0x0E=000105ba]`の値は0xa0（画像赤枠）
`0xa0`と`local_1c0[local_1c4]`のXORを計算すると（1ループ目と仮定して`local_1c4=0`），`0xa0 ^ 0xc2 = 0x62`
asciiコードだと`b`になり，byuctfの頭文字．
</details>

## 解答用ソースコード

<details>
  <summary>ソースコード</summary>

~~~
#!/usr/bin/env python3

local_1c0 = [0xc2, 0x9c, 0x65, 0x83, 0x95, 0x66, 0xfa, 0x15, 0x5e, 0x58, 0x2f, 0x23, 0xac, 0x4f, 0xa1, 0x4c, 0x7d, 0x1e, 0x69, 0x80, 0x8c, 0x4a, 0x26, 0x5b, 0x5f, 0x91, 0x30, 0xcf, 0xc0, 0x4d, 0x97, 0x9b, 0xba, 0x20, 0x77, 0x4c, 0xf5, 0xef, 0x97, 0x96, 0x31, 0x30, 0x8c, 0xe2]
  # 配列 local_1c0
DAT_00021040 = [0x0E, 0x03, 0x1C, 0x13, 0x17, 0x21, 0x12, 0x04, 0x27, 0x09, 0x0D, 0x22, 0x1E, 0x15, 0x0B, 0x24, 0x1D, 0x0A, 0x18, 0x2B, 0x19, 0x00, 0x1B, 0x2A, 0x08, 0x1F, 0x20, 0x25, 0x02, 0x1A, 0x0C, 0x29, 0x07, 0x05, 0x11, 0x28, 0x14, 0x16, 0x23, 0x0F, 0x01, 0x10, 0x2C, 0x06, 0x26]
  # 配列 DAT_00021040（4バイト単位で1バイト分しか値が格納されていないため，非ゼロの値を抽出）
FUN_000105ac = [0x24, 0x00, 0x9f, 0xe5, 0x24, 0x10, 0x9f, 0xe5, 0x00, 0x30, 0x41, 0xe0, 0xa3, 0x1f, 0xa0, 0xe1, 0x43, 0x11, 0x81, 0xe0, 0xc1, 0x10, 0xb0, 0xe1, 0x1e, 0xff, 0x2f, 0x01, 0x10, 0x30, 0x9f, 0xe5, 0x00, 0x00, 0x53, 0xe3, 0x1e, 0xff, 0x2f, 0x01, 0x13, 0xff, 0x2f, 0xe1, 0xf8, 0x10, 0x02, 0x00, 0xf8, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00]
  # 関数 FUN_000105acの先頭から格納されているマシン語16進表記を適当な分とってくる．

for i in range(44):
    print(chr(FUN_000105ac[DAT_00021040[i]]^local_1c0[i]), end="")

print("")
~~~
</details>

## flag
> byuctf{1_h0p3_ARM_wasn't_t00_b4d_0f_4_tw1st}