[rev] nimrev (CakeCTF 2022) writeup

---

- Source: CakeCTF 2022
- Author: [ptr-yudai](https://x.com/ptrYudai)

---

Nim言語のバイナリが与えられる。

Ghidraに投げてmain関数から順に処理を追っていくと、`NimMainModule`という関数が見つかる。
```c
void NimMainModule(void)

{
  char cVar1;
  undefined8 uVar2;
  undefined8 *puVar3;
  undefined8 uVar4;
  long in_FS_OFFSET;
  code *local_28;
  undefined8 local_20;
  undefined8 local_18;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  nimZeroMem(&local_18,8);
  uVar2 = readLine_systemZio_271(stdin);
  puVar3 = (undefined8 *)newSeq(NTIseqLcharT__lBgZ7a89beZGYPl8PiANMTA_,0x18);
  *(undefined *)(puVar3 + 2) = 0xbc;
  *(undefined *)((long)puVar3 + 0x11) = 0x9e;
  *(undefined *)((long)puVar3 + 0x12) = 0x94;
  *(undefined *)((long)puVar3 + 0x13) = 0x9a;
  *(undefined *)((long)puVar3 + 0x14) = 0xbc;
  *(undefined *)((long)puVar3 + 0x15) = 0xab;
  *(undefined *)((long)puVar3 + 0x16) = 0xb9;
  *(undefined *)((long)puVar3 + 0x17) = 0x84;
  *(undefined *)(puVar3 + 3) = 0x8c;
  *(undefined *)((long)puVar3 + 0x19) = 0xcf;
  *(undefined *)((long)puVar3 + 0x1a) = 0x92;
  *(undefined *)((long)puVar3 + 0x1b) = 0xcc;
  *(undefined *)((long)puVar3 + 0x1c) = 0x8b;
  *(undefined *)((long)puVar3 + 0x1d) = 0xce;
  *(undefined *)((long)puVar3 + 0x1e) = 0x92;
  *(undefined *)((long)puVar3 + 0x1f) = 0xcc;
  *(undefined *)(puVar3 + 4) = 0x8c;
  *(undefined *)((long)puVar3 + 0x21) = 0xa0;
  *(undefined *)((long)puVar3 + 0x22) = 0x91;
  *(undefined *)((long)puVar3 + 0x23) = 0xcf;
  *(undefined *)((long)puVar3 + 0x24) = 0x8b;
  *(undefined *)((long)puVar3 + 0x25) = 0xa0;
  *(undefined *)((long)puVar3 + 0x26) = 0xbc;
  *(undefined *)((long)puVar3 + 0x27) = 0x82;
  nimZeroMem(&local_28,0x10);
  local_28 = colonanonymous__main_7;
  local_20 = 0;
  if (puVar3 == (undefined8 *)0x0) {
    uVar4 = 0;
  }
  else {
    uVar4 = *puVar3;
  }
  puVar3 = (undefined8 *)map_main_11(puVar3 + 2,uVar4,colonanonymous__main_7,0);
  if (puVar3 == (undefined8 *)0x0) {
    uVar4 = 0;
  }
  else {
    uVar4 = *puVar3;
  }
  uVar4 = join_main_42(puVar3 + 2,uVar4,0);
  cVar1 = eqStrings(uVar2,uVar4);
  if (cVar1 == '\x01') {
    local_18 = copyString(&TM__V45tF8B8NBcxFcjfe7lhBw_4);
  }
  else {
    local_18 = copyString(&TM__V45tF8B8NBcxFcjfe7lhBw_5);
  }
  echoBinSafe(&local_18,1);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}
```

`puVar3`に何らかの値を格納し、`map_main_11`と`join_main_42`で何らかの処理を行い、標準入力と比較しているように見える。

`map_main_11`を見てみる。
```c
long map_main_11(long param_1,long param_2,code *param_3,long param_4)

{
  undefined uVar1;
  long lVar2;
  long local_28;
  
  lVar2 = newSeq(NTIseqLcharT__lBgZ7a89beZGYPl8PiANMTA_,param_2);
  for (local_28 = 0; local_28 < param_2; local_28 = local_28 + 1) {
    if (param_4 == 0) {
      uVar1 = (*param_3)((int)*(char *)(param_1 + local_28));
    }
    else {
      uVar1 = (*param_3)((int)*(char *)(param_1 + local_28),param_4);
    }
    *(undefined *)(local_28 + lVar2 + 0x10) = uVar1;
  }
  return lVar2;
}
```

`param_4`が0なので、関数ポインタ`param_3`の処理を配列全てに対して行っているだけっぽい。`NimMainModule`に戻って`param_3`の値を確認すると、`colonanonymous__main_7`という関数が渡されていた。

`colonanonymous__main_7`の中身はこれ。NOTを取っているだけ。
```c
byte colonanonymous__main_7(byte param_1)

{
  return ~param_1;
}
```

一応`join_main_42`も見る。配列を結合して文字列に変換しているだけのような雰囲気。
```c
undefined8 join_main_42(long param_1,long param_2,undefined8 *param_3)

{
  char cVar1;
  undefined8 *puVar2;
  undefined8 uVar3;
  undefined8 local_28;
  undefined8 local_20;
  
  local_28 = 0;
  for (local_20 = 0; local_20 < param_2; local_20 = local_20 + 1) {
    cVar1 = *(char *)(param_1 + local_20);
    if (0 < local_20) {
      if (param_3 == (undefined8 *)0x0) {
        uVar3 = 0;
      }
      else {
        uVar3 = *param_3;
      }
      local_28 = resizeString(local_28,uVar3);
      appendString(local_28,param_3);
    }
    puVar2 = (undefined8 *)nimCharToStr((int)cVar1);
    if (puVar2 == (undefined8 *)0x0) {
      uVar3 = 0;
    }
    else {
      uVar3 = *puVar2;
    }
    local_28 = resizeString(local_28,uVar3);
    appendString(local_28,puVar2);
  }
  return local_28;
}
```

よって、`puVar3`のNOTを求めてやれば良さそう。

solverを書く。
単に`~`で演算すると負数になってしまうので`0xff`との積を取る必要がある。
```py
raw = [
  0xbc, 0x9e, 0x94, 0x9a, 0xbc, 0xab, 0xb9, 0x84,
  0x8c, 0xcf, 0x92, 0xcc, 0x8b, 0xce, 0x92, 0xcc,
  0x8c, 0xa0, 0x91, 0xcf, 0x8b, 0xa0, 0xbc, 0x82
]

data = []
for i in range(len(raw)):
    data.append(~raw[i] & 0xff)

print(''.join([chr(x) for x in data]))
```

flagが得られた。
`CakeCTF{s0m3t1m3s_n0t_C}`