[rev] decompile me (zer0pts CTF 2023) writeup

---

- Source: zer0ptz CTF 2023
- Author: [ptr-yudai]()

---

バイナリファイルが与えられる。逆アセンブル結果はこれ。

```c
void main(void)

{
  int iVar1;
  undefined4 extraout_var;
  long lVar2;
  undefined8 *puVar3;
  undefined8 local_208 [16];
  undefined local_188 [384];
  
  puVar3 = local_208;
  for (lVar2 = 0x200; lVar2 != 0; lVar2 = lVar2 + -1) {
    *(undefined *)puVar3 = 0;
    puVar3 = (undefined8 *)((long)puVar3 + 1);
  }
  local_208[0] = 0x80;
  write(1,"FLAG: ",0xe);
  read(0,local_208,0x80);
  RC4_setkey(&key,sbox);
  RC4_encrypt(local_208,local_188,sbox);
  iVar1 = memcmp(local_188,&enc,0x80);
  if (CONCAT44(extraout_var,iVar1) == 0) {
    puts("Correct!");
  }
  else {
    puts("Wrong...");
  }
  return;
}
```

ユーザー入力をRC4で暗号化し、`enc`と照合して結果を返す。よって`enc`を`key`で復号化すればflagが得られそうに見えるが、そうはいかない。

処理を追っていく。`memcmp`が呼び出し規約の異なる独自実装となっていて、関数内部では引数`__s1`の代わりに`R14`を使用している。よく見ると`__s2`も引数ではなく変数`dat`を参照している。

```as
                             int  __stdcall  memcmp (void *  __s1 , void *  __s2 , size_t  _
             int               EAX:4          <RETURN>
             void *            RDI:8          __s1
             void *            RSI:8          __s2
             size_t            RDX:8          __n
                             memcmp                                          XREF[1]:     main:001012bd (c)   
        0010115b 31  c0           XOR        EAX ,EAX
        0010115d 31  c9           XOR        ECX ,ECX
        0010115f 48  8d  35       LEA        __s2 ,[dat ]                                      = 4c0733dc85c4cf78
                 1a  2e  00  00
                             lx                                              XREF[1]:     00101173 (j)   
        00101166 41  8a  1c  0e    MOV        BL,byte ptr [R14  + RCX *0x1 ]
        0010116a 32  1c  0e       XOR        BL,byte ptr [__s2  + RCX *0x1 ]=>dat               = 4c0733dc85c4cf78
        0010116d 08  d8           OR         AL,BL
        0010116f ff  c1           INC        ECX
        00101171 39  d1           CMP        ECX ,__n
        00101173 72  f1           JC         lx
        00101175 c3              RET
```

では`R14`はどこから来たのか。`RC4_setkey`と`RC4_encrypt`を見てみる。

```as
                             undefined  RC4_setkey ()
             undefined         AL:1           <RETURN>
                             RC4_setkey                                      XREF[1]:     main:0010128d (c)   
        00101176 31  c9           XOR        ECX ,ECX
        00101178 31  d2           XOR        EDX ,EDX
                             .loop_init_sbox                                 XREF[1]:     00101187 (j)   
        0010117a 41  88  4c       MOV        byte ptr [R13  + RCX *0x1 ],CL
                 0d  00
        0010117f ff  c1           INC        ECX
        00101181 81  f9  00       CMP        ECX ,0x100
                 01  00  00
        00101187 72  f1           JC         .loop_init_sbox
        00101189 31  c9           XOR        ECX ,ECX
        0010118b 31  db           XOR        EBX ,EBX
                             .loop_key_schedule                              XREF[1]:     001011c4 (j)   
        0010118d 41  0f  b6       MOVZX      EAX ,byte ptr [R13  + RCX *0x1 ]
                 44  0d  00
        00101193 01  c2           ADD        EDX ,EAX
        00101195 41  0f  b6       MOVZX      EAX ,byte ptr [R12  + RBX *0x1 ]
                 04  1c
        0010119a 01  c2           ADD        EDX ,EAX
        0010119c 0f  b6  d2       MOVZX      EDX ,DL
        0010119f 41  8a  7c       MOV        DIL ,byte ptr [R13  + RDX *0x1 ]
                 15  00
        001011a4 41  8a  74       MOV        SIL ,byte ptr [R13  + RCX *0x1 ]
                 0d  00
        001011a9 41  88  74       MOV        byte ptr [R13  + RDX *0x1 ],SIL
                 15  00
        001011ae 41  88  7c       MOV        byte ptr [R13  + RCX *0x1 ],DIL
                 0d  00
        001011b3 ff  c3           INC        EBX
        001011b5 83  fb  08       CMP        EBX ,0x8
        001011b8 72  02           JC         .skip_mod_keylen
        001011ba 31  db           XOR        EBX ,EBX
                             .skip_mod_keylen                                XREF[1]:     001011b8 (j)   
        001011bc ff  c1           INC        ECX
        001011be 81  f9  00       CMP        ECX ,0x100
                 01  00  00
        001011c4 72  c7           JC         .loop_key_schedule
        001011c6 c3              RET
```

```as
                             undefined  RC4_encrypt ()
             undefined         AL:1           <RETURN>
                             RC4_encrypt                                     XREF[1]:     main:001012a4 (c)   
        001011c7 31  c9           XOR        ECX ,ECX
        001011c9 31  d2           XOR        EDX ,EDX
        001011cb 31  db           XOR        EBX ,EBX
                             .loop_encrypt                                   XREF[1]:     00101218 (j)   
        001011cd ff  c2           INC        EDX
        001011cf 0f  b6  d2       MOVZX      EDX ,DL
        001011d2 41  0f  b6       MOVZX      EAX ,byte ptr [R13  + RDX *0x1 ]
                 44  15  00
        001011d8 01  c3           ADD        EBX ,EAX
        001011da 0f  b6  db       MOVZX      EBX ,BL
        001011dd 41  8a  7c       MOV        DIL ,byte ptr [R13  + RDX *0x1 ]
                 15  00
        001011e2 41  8a  74       MOV        SIL ,byte ptr [R13  + RBX *0x1 ]
                 1d  00
        001011e7 41  88  74       MOV        byte ptr [R13  + RDX *0x1 ],SIL
                 15  00
        001011ec 41  88  7c       MOV        byte ptr [R13  + RBX *0x1 ],DIL
                 1d  00
        001011f1 41  0f  b6       MOVZX      EAX ,byte ptr [R13  + RDX *0x1 ]
                 44  15  00
        001011f7 41  0f  b6       MOVZX      EDI ,byte ptr [R13  + RBX *0x1 ]
                 7c  1d  00
        001011fd 01  f8           ADD        EAX ,EDI
        00101202 41  0f  b6       MOVZX      EAX ,byte ptr [R13  + RAX *0x1 ]
                 44  05  00
        00101208 41  32  04  0f    XOR        AL,byte ptr [R15  + RCX *0x1 ]
        0010120c 41  88  04  0e    MOV        byte ptr [R14  + RCX *0x1 ],AL
        00101210 ff  c1           INC        ECX
        00101212 81  f9  80       CMP        ECX ,0x80
                 00  00  00
        00101218 72  b3           JC         .loop_encrypt
        0010121a c3              RET
```

これらの関数も呼び出し規約が異なる。おそらく`R12`が鍵、`R13`がS-Box、`R14`が暗号化後の値、`R15`がユーザー入力に対応している。

main関数を見ていると`R12`に変数`val`をセットしている処理があった。
```as
        0010123b 4c  8d  25       LEA        R12 ,[val ]                                       = 1145141919810931
                 36  2d  00  00
```

よって鍵が`val`、暗号化後の比較対象が`dat`だと分かるので、これらをバイナリの中から探して復号する。

solverを書く。RC4は暗号化と復号化の処理が同じになる。
```py
val = bytes([
    0x31, 0x09, 0x81, 0x19, 0x19, 0x14, 0x45, 0x11
])

dat = bytes([
    0x78, 0xcf, 0xc4, 0x85, 0xdc, 0x33, 0x07, 0x4c,
    0x93, 0x35, 0xfb, 0x7c, 0x10, 0x8e, 0xbe, 0x93,
    0x28, 0xe6, 0x2e, 0x75, 0xda, 0x5e, 0x85, 0xc5,
    0x91, 0x15, 0x75, 0x89, 0x48, 0x0e, 0x29, 0xa4,
    0xf9, 0xa6, 0x3a, 0x6e, 0x1f, 0x84, 0xf7, 0x42,
    0xb0, 0x93, 0x31, 0xf0, 0x68, 0xc0, 0x43, 0x38,
    0x07, 0x32, 0x09, 0x57, 0xda, 0x32, 0x44, 0xcf,
    0xcd, 0x8f, 0xe5, 0xbf, 0xe3, 0xd6, 0xbb, 0x59,
    0x9a, 0x6a, 0x84, 0x85, 0xd3, 0x22, 0xa9, 0x8e,
    0xb5, 0xea, 0xbd, 0x57, 0xde, 0xb1, 0x6c, 0x93,
    0xe4, 0x74, 0x70, 0xac, 0x1a, 0x03, 0xd9, 0x16,
    0x9f, 0xbc, 0x97, 0xfb, 0x85, 0xd9, 0xa6, 0x9e,
    0xd4, 0xd6, 0x02, 0x59, 0xd5, 0x28, 0xb3, 0x93,
    0x16, 0xb6, 0xc4, 0x78, 0xc4, 0xa2, 0x12, 0xd2,
    0xef, 0xb1, 0x54, 0x18, 0xfd, 0x76, 0x51, 0xa3,
    0x5e, 0x57, 0xb8, 0x58, 0x4b, 0x1e, 0xe2, 0x41
])

# RC4 encrypt by copilot :)
def rc4(key, data):
    S = list(range(256))
    j = 0
    out = bytearray()
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    i = j = 0
    for b in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        out.append(b ^ S[(S[i] + S[j]) % 256])
    return bytes(out)

flag = rc4(val, dat)
print(flag)
```

flagが得られた。
`zer0pts{d0n'7_4lw4y5_7ru57_d3c0mp1l3r}`