SECCON Beginners CTF 2020 Mask Writeup Using Ghidra

Ghidraを勉強した記録として残しておく。

表層解析で得られる文字列 Wrong を検索
普通にmain関数内

main関数のデコンパイル結果（当初）

```c
undefined8 main(int param_1,long param_2)
{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  int local_e0;
  byte local_d8 [64];
  byte local_98 [64];
  byte local_58 [72];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  if (param_1 == 1) {
    puts("Usage: ./mask [FLAG]");
  }
  else {
    strcpy((char *)local_d8,*(char **)(param_2 + 8));
    sVar2 = strlen((char *)local_d8);
    iVar1 = (int)sVar2;
    puts("Putting on masks...");
    local_e0 = 0;
    while (local_e0 < iVar1) {
      local_98[local_e0] = local_d8[local_e0] & 0x75;
      local_58[local_e0] = local_d8[local_e0] & 0xeb;
      local_e0 = local_e0 + 1;
    }
    local_98[iVar1] = 0;
    local_58[iVar1] = 0;
    puts((char *)local_98);
    puts((char *)local_58);
    iVar1 = strcmp((char *)local_98,"atd4`qdedtUpetepqeUdaaeUeaqau");
    if ((iVar1 == 0) &&
       (iVar1 = strcmp((char *)local_58,"c`b bk`kj`KbababcaKbacaKiacki"), iVar1 == 0)) {
      puts("Correct! Submit your FLAG.");
    }
    else {
      puts("Wrong FLAG. Try again.");
    }
  }
  if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}
```

変数の型を変更してデコンパイル結果の誤りを正し，わかりやすい変数名に変更

  int local_e0;
　　↓
  int i;

  byte local_d8 [64];
　　↓
  byte input_value [64];

  byte local_98 [64];
　　↓
  byte str1 [64];

  byte local_58 [72];
　　↓
  byte str2 [64];


```c
undefined8 main(int param_1,long param_2)
{
  int size;
  size_t sVar1;
  long in_FS_OFFSET;
  int i;
  byte input_value [64];
  byte str1 [64];
  byte str2 [64];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  if (param_1 == 1) {
    puts("Usage: ./mask [FLAG]");
  }
  else {
    strcpy((char *)input_value,*(char **)(param_2 + 8));
    sVar1 = strlen((char *)input_value);
    size = (int)sVar1;
    puts("Putting on masks...");
    i = 0;
    while (i < size) {
      str1[i] = input_value[i] & 0x75;
      str2[i] = input_value[i] & 0xeb;
      i = i + 1;
    }
    str1[size] = 0;
    str2[size] = 0;
    puts((char *)str1);
    puts((char *)str2);
    size = strcmp((char *)str1,"atd4`qdedtUpetepqeUdaaeUeaqau");
    if ((size == 0) && (size = strcmp((char *)str2,"c`b bk`kj`KbababcaKbacaKiacki"), size == 0)) {
      puts("Correct! Submit your FLAG.");
    }
    else {
      puts("Wrong FLAG. Try again.");
    }
  }
  if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}
```

ちょっと気になる点

```c
    size = strcmp((char *)str1,"atd4`qdedtUpetepqeUdaaeUeaqau");
    if ((size == 0) && (size = strcmp((char *)str2,"c`b bk`kj`KbababcaKbacaKiacki"), size == 0)) {
      puts("Correct! Submit your FLAG.");
```

ここで使われている変数は size というより ret が正解。
Ghidraには同じレジスタを同じ変数にする癖がある。
Split Out As New Variable で変数名を区別してみたが，

```c
    ret = strcmp((char *)str1,"atd4`qdedtUpetepqeUdaaeUeaqau");
    if ((ret == 0) && (size = strcmp((char *)str2,"c`b bk`kj`KbababcaKbacaKiacki"), size == 0)) {
      puts("Correct! Submit your FLAG.");
```

のように２カ所 size として残ってしまった。
Split Out As New Variableは気まぐれ。

ソルバー ( Ghidra Script )

```python
ans=[]
mask_len = 29
str1 = getBytes(toAddr(0x0010202d),mask_len)
str2 = getBytes(toAddr(0x0010204b),mask_len)

#print(str1)
#print(str2)

i = 0
while i < mask_len:
    # Brute force
    j = 0
    while j < 256:
        if ( j & 0x75 == str1[i] ) & ( j & 0xeb == str2[i] ):
             ans.append(j)
             break
        j = j + 1
    i = i + 1

#print(ans)
print(''.join(map(chr,ans)))
```