Ghidraの勉強になると思って,日立CTFのBinary_絶対に通らないif文の静的解析にチャレンジした結果,うまくいったので共有する。
問題へのリンク
絶対に通らないif文
int main(int argc, char **argv)
{
if (1 == 2)
{
show_flag();
}
return 0;
}
逆アセンブル結果
0040B1C6: C7 45 FC 01 00 00 00 mov dword ptr [ebp-4],1
0040B1CD: C7 45 F8 02 00 00 00 mov dword ptr [ebp-8],2
0040B1D4: 8B 45 FC mov eax,dword ptr [ebp-4]
0040B1D7: 3B 45 F8 cmp eax,dword ptr [ebp-8]
0040B1DA: 75 05 JNZ LAB_0040B1E1
0040B1DC: e8 6f e3 CALL FUN_00409550
動的解析でフラグを得るならば,バイナリエディタで
C7 45 FC 01 00 00 00
を検索して
C7 45 FC 02 00 00 00
に変更すれば if (2 == 2) になってフラグが表示される。
ここからがGhidraによる静的解析
FUN_00409550のデコンパイル結果
void FUN_00409550(void)
{
undefined4 extraout_EDX;
int iVar1;
int local_438;
byte local_430 [4];
undefined local_42c;
undefined local_42b;
undefined local_42a;
undefined local_429;
undefined local_428;
undefined local_427;
undefined local_426;
undefined local_425;
undefined local_424;
undefined local_423;
undefined local_422;
undefined local_421;
undefined local_420;
undefined local_41f;
undefined local_41e;
undefined local_41d;
undefined local_41c;
undefined local_41b;
undefined local_41a;
undefined local_419;
undefined local_418;
undefined local_417;
undefined local_416;
undefined local_415;
undefined local_414;
undefined local_413;
undefined local_412;
undefined local_411;
undefined4 local_410;
byte local_40c [1024];
uint local_c;
int local_8;
local_c = DAT_0040e004 ^ (uint)&stack0xfffffffc;
local_40c[0] = 0x18;
local_40c[1] = 0x13;
local_40c[2] = 0x5e;
local_40c[3] = 0x76;
local_40c[4] = 0x34;
(中略)
local_40c[1019] = 0x35;
local_40c[1020] = 0x7a;
local_40c[1021] = 0x40;
local_40c[1022] = 0x9b;
local_40c[1023] = 0x36;
local_430[0] = 0x53;
local_430[1] = 0xb9;
local_430[2] = 0xf;
local_430[3] = 0xa4;
local_42c = 0xb4;
(中略)
local_415 = 0x89;
local_414 = 0x6a;
local_413 = 0x47;
local_412 = 0x8e;
local_411 = 0x74;
local_410 = 0x3ff;
local_8 = 0;
local_438 = 0x3ff;
while (-1 < local_438) {
local_8 = local_438 % 0x20;
local_430[local_8] = local_430[local_8] ^ local_40c[local_438];
local_438 = local_438 + -1;
}
iVar1 = 0;
while (iVar1 < 0x20) {
FID_conflict:_wprintf("%c",(uint)local_430[iVar1]);
iVar1 = iVar1 + 1;
}
FID_conflict:_wprintf("\n");
FUN_00401000(local_c ^ (uint)&stack0xfffffffc,extraout_EDX,(char)iVar1);
return;
}
変数の型を修正して、デコンパイル誤りを補正する
byte local_430 [4];
↓
byte flag [32];
変数名を変更してわかりやすく
int local_438; → int i;
int local_8; → int j;
byte local_40c [1024]; → byte key [1024];
void FUN_00409550(void)
{
undefined4 extraout_EDX;
int iVar1;
int i;
byte flag [32];
byte key [1024];
uint local_c;
int j;
local_c = DAT_0040e004 ^ (uint)&stack0xfffffffc;
key[0] = 0x18;
key[1] = 0x13;
key[2] = 0x5e;
key[3] = 0x76;
key[4] = 0x34;
(中略)
key[1019] = 0x35;
key[1020] = 0x7a;
key[1021] = 0x40;
key[1022] = 0x9b;
key[1023] = 0x36;
flag[0] = 0x53;
flag[1] = 0xb9;
flag[2] = 0xf;
flag[3] = 0xa4;
flag[4] = 0xb4;
(中略)
flag[27] = 0x89;
flag[28] = 0x6a;
flag[29] = 0x47;
flag[30] = 0x8e;
flag[31] = 0x74;
j = 0;
i = 0x3ff;
while (-1 < i) {
j = i % 0x20;
flag[j] = flag[j] ^ key[i];
i = i + -1;
}
iVar1 = 0;
while (iVar1 < 0x20) {
FID_conflict:_wprintf("%c",(uint)flag[iVar1]);
iVar1 = iVar1 + 1;
}
FID_conflict:_wprintf("\n");
FUN_00401000(local_c ^ (uint)&stack0xfffffffc,extraout_EDX,(char)iVar1);
return;
}
keyが1024バイト
flagが32バイト
flag1バイトにつき32回xorする
ソルバー ( Ghidra Script )
# step 1 extract key
key=[]
inst = getInstructionAt(toAddr(0x00409563))
i = 0
while i < 0x400:
key.append(inst.getOpObjects(1)[0].getValue())
inst = inst.getNext()
i = i + 1
#print(key)
# step 2 extract flag
flag=[]
inst = getInstructionAt(toAddr(0x0040affb))
i = 0
while i < 0x20:
flag.append(inst.getOpObjects(1)[0].getValue())
inst = inst.getNext()
i = i + 1
#print(flag)
# step 3 xor
j = 0
i = 0x3ff
while -1 < i:
j = i % 0x20
flag[j] = flag[j] ^ key[i]
i = i - 1
#print(flag)
print(''.join(map(chr,flag)))