0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

WaniCTF complex Writeup Using Ghidra

0
Last updated at Posted at 2021-03-06

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

complexの入手先

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

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関数

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 を返してる

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

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

と,local_68のstack strings

  local_68 = 0x6e44564d6e575f53;
  local_60 = 0x576a48545b585747;
  local_58 = 0x535d45675d57535e;
  local_50 = 0x675a42444550416b;
  local_48 = 0x415e5543;
  local_44 = 0x52;

を xor すれば flag みたいです。

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

  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];

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

  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 ) を作ってみる

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)))

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

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?