WaniCTF complex  Writeup Using Ghidra

Ghidraを勉強した記録として残しておく。
stack strings の問題だけど byte だけではなく，qword, dword, word も登場する。

complexの入手先

https://github.com/wani-hackase/wanictf2020-writeup/tree/master/reversing/complex/file

表層解析で得られる文字列 Incorrect を検索
main関数だ

```c
undefined8 main(void)
{
  int iVar1;
  size_t sVar2;
  char local_78 [48];
  char local_48 [5];
  char acStack67 [37];
  char acStack30 [14];
  int local_10;
  int local_c;
  
  printf("input flag : ");
  __isoc99_scanf(&DAT_00101dc2,local_48);
  sVar2 = strlen(local_48);
  if (((sVar2 != 0x2b) || (iVar1 = strncmp(local_48,"FLAG{",5), iVar1 != 0)) ||
     (iVar1 = strcmp(acStack30,"}"), iVar1 != 0)) {
    puts("Incorrect");
    return 1;
  }
  strncpy(local_78,acStack67,0x25);
  local_c = 0;
  do {
    if (0x13 < local_c) {
      return 0;
    }
    local_10 = check(local_c,local_78,local_78);
    if (local_10 != 0) {
      if (local_10 == 1) {
        puts("Incorrect");
        return 1;
      }
      if (local_10 == 2) {
        printf("Correct! Flag is %s\n",local_48);
        return 0;
      }
    }
    local_c = local_c + 1;
  } while( true );
}
```

check関数のリターンが 2 だと Correct! になるようだ。

check関数

```c
void check(undefined4 param_1,undefined8 param_2)
{
  switch(param_1) {
  case 0:
    check_0(param_2);
    break;
  case 1:
    check_1(param_2);
    break;
  case 2:
    check_2(param_2);
    break;
  case 3:
    check_3(param_2);
    break;
  case 4:
    check_4(param_2);
    break;
  case 5:
    check_5(param_2);
    break;
  case 6:
    check_6(param_2);
    break;
  case 7:
    check_7(param_2);
    break;
  case 8:
    check_8(param_2);
    break;
  case 9:
    check_9(param_2);
    break;
  case 10:
    check_10(param_2);
    break;
  case 0xb:
    check_11(param_2);
    break;
  case 0xc:
    check_12(param_2);
    break;
  case 0xd:
    check_13(param_2);
    break;
  case 0xe:
    check_14(param_2);
    break;
  case 0xf:
    check_15(param_2);
    break;
  case 0x10:
    check_16(param_2);
    break;
  case 0x11:
    check_17(param_2);
    break;
  case 0x12:
    check_18(param_2);
    break;
  case 0x13:
    check_19(param_2);
  }
  return;
}
````

心を折りにきてるな。負けるもんか。（正直，心折れそう）
一つずつ落ち着いてみていく。
check_13関数だけ 2 を返してる

```c
undefined8 check_13(long param_1)

{
  undefined8 local_68;
  undefined8 local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined4 local_48;
  undefined local_44;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  undefined4 local_18;
  undefined local_14;
  int local_c;
  
  local_38 = 0x3131393431333637;
  local_30 = 0x3435313837393235;
  local_28 = 0x3635313836343636;
  local_20 = 0x3834303131353334;
  local_18 = 0x34323435;
  local_14 = 0x37;
  local_68 = 0x6e44564d6e575f53;
  local_60 = 0x576a48545b585747;
  local_58 = 0x535d45675d57535e;
  local_50 = 0x675a42444550416b;
  local_48 = 0x415e5543;
  local_44 = 0x52;
  local_c = 0;
  while( true ) {
    if (0x24 < local_c) {
      return 2;
    }
    if (((int)*(char *)((long)&local_38 + (long)local_c) ^ (uint)*(byte *)(param_1 + local_c)) !=
        (int)*(char *)((long)&local_68 + (long)local_c)) break;
    local_c = local_c + 1;
  }
  return 1;
}
```
local_38のstack strings

```c
  local_38 = 0x3131393431333637;
  local_30 = 0x3435313837393235;
  local_28 = 0x3635313836343636;
  local_20 = 0x3834303131353334;
  local_18 = 0x34323435;
  local_14 = 0x37;
```

と，local_68のstack strings 

```c
  local_68 = 0x6e44564d6e575f53;
  local_60 = 0x576a48545b585747;
  local_58 = 0x535d45675d57535e;
  local_50 = 0x675a42444550416b;
  local_48 = 0x415e5543;
  local_44 = 0x52;
```
を xor すれば flag みたいです。

本当？
main関数に戻ってみる

```c
  int iVar1;
  size_t sVar2;
  char local_78 [48];
  char local_48 [5];
  char acStack67 [37];
  char acStack30 [14];
  int local_10;
  int local_c;
  
  printf("input flag : ");
  __isoc99_scanf(&DAT_00101dc2,local_48);
  sVar2 = strlen(local_48);
  if (((sVar2 != 0x2b) || (iVar1 = strncmp(local_48,"FLAG{",5), iVar1 != 0)) ||
     (iVar1 = strcmp(acStack30,"}"), iVar1 != 0)) {
    puts("Incorrect");
    return 1;
  }
  strncpy(local_78,acStack67,0x25);
  local_c = 0;
  do {
    if (0x13 < local_c) {
      return 0;
    }
    local_10 = check(local_c,local_78,local_78);
```

check関数に渡してる local_78 は，acStack67のコピーだけど。。。
acStack67への代入はどこにも無い。

xorしてる変数の長さは同じはず。

char local_48 [5];
　　　↓
char local_48 [48];

と、型を変更して，デコンパイル誤りを正す。

```c
  int iVar1;
  size_t sVar2;
  char local_78 [48];
  char local_48 [48];
  int local_c;
  
  printf("input flag : ");
  __isoc99_scanf(&DAT_00101dc2,local_48);
  sVar2 = strlen(local_48);
  if (((sVar2 != 0x2b) || (iVar1 = strncmp(local_48,"FLAG{",5), iVar1 != 0)) ||
     (iVar1 = strcmp(local_48 + 0x2a,"}"), iVar1 != 0)) {
    puts("Incorrect");
    return 1;
  }
  strncpy(local_78,local_48 + 5,0x25);
```
check関数（check_13関数）に渡すのは，"FLAG{"の 5バイトを除いた 0x25バイトですね。

stack strings のところの逆アセンブルはこんな感じ

```
        0010139c 48 b8 37 ..     MOV        RAX,0x3131393431333637
        001013a6 48 ba 35 ..     MOV        RDX,0x3435313837393235
        001013b0 48 89 45 d0     MOV        qword ptr [RBP + local_38],RAX
        001013b4 48 89 55 d8     MOV        qword ptr [RBP + local_30],RDX
        001013b8 48 b8 36 ..     MOV        RAX,0x3635313836343636
        001013c2 48 ba 34 ..     MOV        RDX,0x3834303131353334
        001013cc 48 89 45 e0     MOV        qword ptr [RBP + local_28],RAX
        001013d0 48 89 55 e8     MOV        qword ptr [RBP + local_20],RDX
        001013d4 c7 45 f0 ..     MOV        dword ptr [RBP + local_18],0x34323435
        001013db c6 45 f4 37     MOV        byte ptr [RBP + local_14],0x37
        001013df 48 b8 53 ..     MOV        RAX,0x6e44564d6e575f53
        001013e9 48 ba 47 ..     MOV        RDX,0x576a48545b585747
        001013f3 48 89 45 a0     MOV        qword ptr [RBP + local_68],RAX
        001013f7 48 89 55 a8     MOV        qword ptr [RBP + local_60],RDX
        001013fb 48 b8 5e ..     MOV        RAX,0x535d45675d57535e
        00101405 48 ba 6b ..     MOV        RDX,0x675a42444550416b
        0010140f 48 89 45 b0     MOV        qword ptr [RBP + local_58],RAX
        00101413 48 89 55 b8     MOV        qword ptr [RBP + local_50],RDX
        00101417 c7 45 c0 ..     MOV        dword ptr [RBP + local_48],0x415e5543
        0010141e c6 45 c4 52     MOV        byte ptr [RBP + local_44],0x52
```

ソルバー ( Ghidra Script ) を作ってみる

```python
def qword_parse(value):
    return([(value & 0x00000000000000ff) / 0x1,(value & 0x000000000000ff00) / 0x100,(value & 0x0000000000ff0000) / 0x10000,(value & 0x00000000ff000000) / 0x1000000,(value & 0x000000ff00000000) / 0x100000000,(value & 0x0000ff0000000000) / 0x10000000000,(value & 0x00ff000000000000) / 0x1000000000000,(value & 0xff00000000000000) / 0x100000000000000])

def dword_parse(value):
    return([(value & 0x000000ff) / 0x1,(value & 0x0000ff00) / 0x100,(value & 0x00ff0000) / 0x10000,(value & 0xff000000) / 0x1000000])

def word_parse(value):
    return([(value & 0x00ff) / 0x1,(value & 0xff00) / 0x100])

# step 1 local_38 extract
local_38 = []

addr = toAddr(0x0010139c)
inst = getInstructionAt(addr)
local_38.extend(qword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x001013a6)
inst = getInstructionAt(addr)
local_38.extend(qword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x001013b8)
inst = getInstructionAt(addr)
local_38.extend(qword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x001013c2)
inst = getInstructionAt(addr)
local_38.extend(qword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x001013d4)
inst = getInstructionAt(addr)
local_38.extend(dword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x001013db)
inst = getInstructionAt(addr)
local_38.append(inst.getOpObjects(1)[0].getValue())

#print(local_38)
#print(''.join(map(chr,local_38)))

# step 2 local_68 extract
local_68 = []

addr = toAddr(0x001013df)
inst = getInstructionAt(addr)
local_68.extend(qword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x001013e9)
inst = getInstructionAt(addr)
local_68.extend(qword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x001013fb)
inst = getInstructionAt(addr)
local_68.extend(qword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x00101405)
inst = getInstructionAt(addr)
local_68.extend(qword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x00101417)
inst = getInstructionAt(addr)
local_68.extend(dword_parse(inst.getOpObjects(1)[0].getValue()))

addr = toAddr(0x0010141e)
inst = getInstructionAt(addr)
local_68.append(inst.getOpObjects(1)[0].getValue())

#print(local_68)
#print(''.join(map(chr,local_68)))

# step 3 xor
ans = []
i = 0
while i < 0x25:
    ans.append(local_38[i] ^ local_68[i])
    i = i + 1

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

もっと美しくコードを書ければいいのに。。。