- Source: CakeCTF 2022
- Author: ptr-yudai
Nim言語のバイナリが与えられる。
Ghidraに投げてmain関数から順に処理を追っていくと、NimMainModule
という関数が見つかる。
void NimMainModule(void)
{
char cVar1;
undefined8 uVar2;
undefined8 *puVar3;
undefined8 uVar4;
long in_FS_OFFSET;
code *local_28;
undefined8 local_20;
undefined8 local_18;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
nimZeroMem(&local_18,8);
uVar2 = readLine_systemZio_271(stdin);
puVar3 = (undefined8 *)newSeq(NTIseqLcharT__lBgZ7a89beZGYPl8PiANMTA_,0x18);
*(undefined *)(puVar3 + 2) = 0xbc;
*(undefined *)((long)puVar3 + 0x11) = 0x9e;
*(undefined *)((long)puVar3 + 0x12) = 0x94;
*(undefined *)((long)puVar3 + 0x13) = 0x9a;
*(undefined *)((long)puVar3 + 0x14) = 0xbc;
*(undefined *)((long)puVar3 + 0x15) = 0xab;
*(undefined *)((long)puVar3 + 0x16) = 0xb9;
*(undefined *)((long)puVar3 + 0x17) = 0x84;
*(undefined *)(puVar3 + 3) = 0x8c;
*(undefined *)((long)puVar3 + 0x19) = 0xcf;
*(undefined *)((long)puVar3 + 0x1a) = 0x92;
*(undefined *)((long)puVar3 + 0x1b) = 0xcc;
*(undefined *)((long)puVar3 + 0x1c) = 0x8b;
*(undefined *)((long)puVar3 + 0x1d) = 0xce;
*(undefined *)((long)puVar3 + 0x1e) = 0x92;
*(undefined *)((long)puVar3 + 0x1f) = 0xcc;
*(undefined *)(puVar3 + 4) = 0x8c;
*(undefined *)((long)puVar3 + 0x21) = 0xa0;
*(undefined *)((long)puVar3 + 0x22) = 0x91;
*(undefined *)((long)puVar3 + 0x23) = 0xcf;
*(undefined *)((long)puVar3 + 0x24) = 0x8b;
*(undefined *)((long)puVar3 + 0x25) = 0xa0;
*(undefined *)((long)puVar3 + 0x26) = 0xbc;
*(undefined *)((long)puVar3 + 0x27) = 0x82;
nimZeroMem(&local_28,0x10);
local_28 = colonanonymous__main_7;
local_20 = 0;
if (puVar3 == (undefined8 *)0x0) {
uVar4 = 0;
}
else {
uVar4 = *puVar3;
}
puVar3 = (undefined8 *)map_main_11(puVar3 + 2,uVar4,colonanonymous__main_7,0);
if (puVar3 == (undefined8 *)0x0) {
uVar4 = 0;
}
else {
uVar4 = *puVar3;
}
uVar4 = join_main_42(puVar3 + 2,uVar4,0);
cVar1 = eqStrings(uVar2,uVar4);
if (cVar1 == '\x01') {
local_18 = copyString(&TM__V45tF8B8NBcxFcjfe7lhBw_4);
}
else {
local_18 = copyString(&TM__V45tF8B8NBcxFcjfe7lhBw_5);
}
echoBinSafe(&local_18,1);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
puVar3
に何らかの値を格納し、map_main_11
とjoin_main_42
で何らかの処理を行い、標準入力と比較しているように見える。
map_main_11
を見てみる。
long map_main_11(long param_1,long param_2,code *param_3,long param_4)
{
undefined uVar1;
long lVar2;
long local_28;
lVar2 = newSeq(NTIseqLcharT__lBgZ7a89beZGYPl8PiANMTA_,param_2);
for (local_28 = 0; local_28 < param_2; local_28 = local_28 + 1) {
if (param_4 == 0) {
uVar1 = (*param_3)((int)*(char *)(param_1 + local_28));
}
else {
uVar1 = (*param_3)((int)*(char *)(param_1 + local_28),param_4);
}
*(undefined *)(local_28 + lVar2 + 0x10) = uVar1;
}
return lVar2;
}
param_4
が0なので、関数ポインタparam_3
の処理を配列全てに対して行っているだけっぽい。NimMainModule
に戻ってparam_3
の値を確認すると、colonanonymous__main_7
という関数が渡されていた。
colonanonymous__main_7
の中身はこれ。NOTを取っているだけ。
byte colonanonymous__main_7(byte param_1)
{
return ~param_1;
}
一応join_main_42
も見る。配列を結合して文字列に変換しているだけのような雰囲気。
undefined8 join_main_42(long param_1,long param_2,undefined8 *param_3)
{
char cVar1;
undefined8 *puVar2;
undefined8 uVar3;
undefined8 local_28;
undefined8 local_20;
local_28 = 0;
for (local_20 = 0; local_20 < param_2; local_20 = local_20 + 1) {
cVar1 = *(char *)(param_1 + local_20);
if (0 < local_20) {
if (param_3 == (undefined8 *)0x0) {
uVar3 = 0;
}
else {
uVar3 = *param_3;
}
local_28 = resizeString(local_28,uVar3);
appendString(local_28,param_3);
}
puVar2 = (undefined8 *)nimCharToStr((int)cVar1);
if (puVar2 == (undefined8 *)0x0) {
uVar3 = 0;
}
else {
uVar3 = *puVar2;
}
local_28 = resizeString(local_28,uVar3);
appendString(local_28,puVar2);
}
return local_28;
}
よって、puVar3
のNOTを求めてやれば良さそう。
solverを書く。
単に~
で演算すると負数になってしまうので0xff
との積を取る必要がある。
raw = [
0xbc, 0x9e, 0x94, 0x9a, 0xbc, 0xab, 0xb9, 0x84,
0x8c, 0xcf, 0x92, 0xcc, 0x8b, 0xce, 0x92, 0xcc,
0x8c, 0xa0, 0x91, 0xcf, 0x8b, 0xa0, 0xbc, 0x82
]
data = []
for i in range(len(raw)):
data.append(~raw[i] & 0xff)
print(''.join([chr(x) for x in data]))
flagが得られた。
CakeCTF{s0m3t1m3s_n0t_C}