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)))
もっと美しくコードを書ければいいのに。。。