首先ida64打开,shift+f12查看字符串,发现有you found me字段,跟进查看调用了它的函数。
发现是sub_4009C6()函数,分析
__int64 sub_4009C6()
{
__int64 result; // rax
int i; // [rsp+Ch] [rbp-114h]
__int64 v2; // [rsp+10h] [rbp-110h]
__int64 v3; // [rsp+18h] [rbp-108h]
__int64 v4; // [rsp+20h] [rbp-100h]
__int64 v5; // [rsp+28h] [rbp-F8h]
__int64 v6; // [rsp+30h] [rbp-F0h]
__int64 v7; // [rsp+38h] [rbp-E8h]
__int64 v8; // [rsp+40h] [rbp-E0h]
__int64 v9; // [rsp+48h] [rbp-D8h]
__int64 v10; // [rsp+50h] [rbp-D0h]
__int64 v11; // [rsp+58h] [rbp-C8h]
char v12[13]; // [rsp+60h] [rbp-C0h] BYREF
char v13[4]; // [rsp+6Dh] [rbp-B3h] BYREF
char v14[19]; // [rsp+71h] [rbp-AFh] BYREF
char v15[32]; // [rsp+90h] [rbp-90h] BYREF
int v16; // [rsp+B0h] [rbp-70h]
char v17; // [rsp+B4h] [rbp-6Ch]
char v18[72]; // [rsp+C0h] [rbp-60h] BYREF
unsigned __int64 v19; // [rsp+108h] [rbp-18h]
v19 = __readfsqword(0x28u);
qmemcpy(v12, "Iodl>Qnb(ocy", 12);
v12[12] = 127;
qmemcpy(v13, "y.i", 3);
v13[3] = 127;
qmemcpy(v14, "d`3w}wek9{iy=~yL@EC", sizeof(v14));
memset(v15, 0, sizeof(v15));
v16 = 0;
v17 = 0;
sub_4406E0(0LL, v15, 37LL);
v17 = 0;
if ( sub_424BA0(v15) == 36 )
{
for ( i = 0; i < (unsigned __int64)sub_424BA0(v15); ++i )
{
if ( (unsigned __int8)(v15[i] ^ i) != v12[i] )
{
result = 4294967294LL;
goto LABEL_13;
}
}
sub_410CC0("continue!");
memset(v18, 0, 65);
sub_4406E0(0LL, v18, 64LL);
v18[39] = 0;
if ( sub_424BA0(v18) == 39 )
{
v2 = sub_400E44(v18);
v3 = sub_400E44(v2);
v4 = sub_400E44(v3);
v5 = sub_400E44(v4);
v6 = sub_400E44(v5);
v7 = sub_400E44(v6);
v8 = sub_400E44(v7);
v9 = sub_400E44(v8);
v10 = sub_400E44(v9);
v11 = sub_400E44(v10);
if ( !(unsigned int)sub_400360(v11, off_6CC090) )
{
sub_410CC0("You found me!!!");
sub_410CC0("bye bye~");
}
result = 0LL;
}
else
{
result = 4294967293LL;
}
}
else
{
result = 0xFFFFFFFFLL;
}
LABEL_13:
if ( __readfsqword(0x28u) != v19 )
sub_444020();
return result;
}
首先该函数对v12,v13,v14,v15进行了初始化操作,然后sub_4406E0是一个相当于gets的函数,将用户输入的字符串保存到v15中
接下来可以看出sub_424BA0函数是一个相当于strlen的函数,判断输入的字符串长度是否等于36,如果是再进行下一步操作,后续的you found me 输出刚好是在这第一个if条件里面,所以首先flag长度为36,具体是什么再继续进行分析。
下面for循环里面的if条件是一个关键,如果v15里面有一个字符异或i之后和v12里对应的字符不同,就会进行goto 操作,然后结束这个函数。
if ( sub_424BA0(v15) == 36 )
{
for ( i = 0; i < (unsigned __int64)sub_424BA0(v15); ++i )
{
if ( (unsigned __int8)(v15[i] ^ i) != v12[i] )
{
result = 4294967294LL;
goto LABEL_13;
}
}
LABEL_13:
if ( __readfsqword(0x28u) != v19 )
sub_444020();
flag要满足的条件之2就是,v15[i] ^ i == v12[i], v12已经给出
但是具体 i 的范围是0~strlen(v15), 也就是说 0<= i < 36,而v12长度只有13,但是注意到这三个数组是连续存放的,所以再访问的时候应该也是连续访问,所以实际上就是三个数组合在一起成为目标数组。
char v12[13]; // [rsp+60h] [rbp-C0h] BYREF
char v13[4]; // [rsp+6Dh] [rbp-B3h] BYREF
char v14[19]; // [rsp+71h] [rbp-AFh] BYREF
按照逻辑,可以编写python脚本:
hint = []
str1 = "Iodl>Qnb(ocy"
for i in range(len(str1)):
hint.append(ord(str1[i]))
hint.append(127)
str2 = "y.i"
for i in range(len(str2)):
hint.append(ord(str2[i]))
hint.append(127)
str3 = "d`3w}wek9{iy=~yL@EC"
for i in range(len(str3)):
hint.append(ord(str3[i]))
str = ""
for i in range(len(hint)):
str += chr(hint[i] ^ i)
print(str)
结果是
提示说flag的前四位是flag。。。。。
关于v12,v13,v14三个数组是连续的, ida+Ubuntu动态调试证明一下
首先先在调用数组前下断点
进程停留在这里
可以看出这里是连续执行了36次mov操作,将存储在v12,v13,v14中的所有字符都放在了连续的地址中
那么在循环异或的时候,当i大于了v12的数组长度时,实际上取到的值就是v13的元素,类推。
而后是sub_400E44函数被调用了十次,双击查看发现函数的功能是base64编码。
也就是v18被base64编码了10次,最终的结果储存在v11中
然后是if判断,调用了sub_400360函数,是一个类似于strcmp的函数,如果off_6CC090和v11的所有元素都相等,就输出you found me
也就是找到了flag。
然后就是写脚本解码
import base64
lis = "Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZadGVHdFRNVTVYVW01T2FGSnRVbGhhVjNoaFZWWmtWMXBFVWxSTmJFcElWbTAxVDJGV1NuTlhia0pXWWxob1dGUnJXbXRXTVZaeVdrWm9hVlpyV1hwV1IzaGhXVmRHVjFOdVVsWmlhMHBZV1ZSR1lWZEdVbFZTYlhSWFRWWndNRlZ0TVc5VWJGcFZWbXR3VjJKSFVYZFdha1pXWlZaT2NtRkhhRk5pVjJoWVYxZDBhMVV3TlhOalJscFlZbGhTY1ZsclduZGxiR1J5VmxSR1ZXSlZjRWhaTUZKaFZqSktWVkZZYUZkV1JWcFlWV3BHYTFkWFRrZFRiV3hvVFVoQ1dsWXhaRFJpTWtsM1RVaG9hbEpYYUhOVmJUVkRZekZhY1ZKcmRGTk5Wa3A2VjJ0U1ExWlhTbFpqUldoYVRVWndkbFpxUmtwbGJVWklZVVprYUdFeGNHOVhXSEJIWkRGS2RGSnJhR2hTYXpWdlZGVm9RMlJzV25STldHUlZUVlpXTlZadE5VOVdiVXBJVld4c1dtSllUWGhXTUZwell6RmFkRkpzVWxOaVNFSktWa1phVTFFeFduUlRhMlJxVWxad1YxWnRlRXRXTVZaSFVsUnNVVlZVTURrPQ=="
for i in range(10):
lis = base64.b64decode(lis)
print(lis)
最后得到的结果是一个网址。。。。
看来是被坑了,这里面应该没有想要的flag
参考WP,发现程序在正常执行后要执行在这里的sub_400D35函数:
跟进查看:
unsigned __int64 sub_400D35() { unsigned __int64 result; // rax unsigned int v1; // [rsp+Ch] [rbp-24h] int i; // [rsp+10h] [rbp-20h] int j; // [rsp+14h] [rbp-1Ch] unsigned int v4; // [rsp+24h] [rbp-Ch] unsigned __int64 v5; // [rsp+28h] [rbp-8h] v5 = __readfsqword(0x28u); v1 = sub_43FD20(0LL) - qword_6CEE38; for ( i = 0; i <= 1233; ++i ) { sub_40F790(v1); sub_40FE60(); sub_40FE60(); v1 = sub_40FE60() ^ 0x98765432; } v4 = v1; if ( ((unsigned __int8)v1 ^ byte_6CC0A0[0]) == 102 && (HIBYTE(v4) ^ (unsigned __int8)byte_6CC0A3) == 103 ) { for ( j = 0; j <= 24; ++j ) sub_410E90((unsigned __int8)(byte_6CC0A0[j] ^ *((_BYTE *)&v4 + j % 4))); } result = __readfsqword(0x28u) ^ v5; if ( result ) sub_444020(); return result; }
关键代码区域是第二十行那里的if和for循环,第一个if判断, 可以根据flag和已知数组反向解出v1
后边的for循环可以根据已知数组和v4解出flag
这就是已知数组:
v1 = '' enc1 = 'flag' flag = '' str = [0x40,0x35,0x20,0x56,0x5D,0x18,0x22,0x45,0x17,0x2F,0x24,0x6E,0x62,0x3C,0x27,0x54,0x48,0x6C,0x24,0x6E,0x72,0x3C,0x32,0x45,0x5B] for i in range(4): v1 += chr(str[i] ^ ord(enc1[i])) print (v1) for i in range(len(str)): flag += chr(str[i] ^ ord(v1[i % 4])) print(flag)
str里是已知数组对应的ASCII码16进制数,变量v1中存放的就是它的地址,然后将其赋值给v4后再在for循环中进行引用。
(&v4 + j % 4也就是v4[j % 4])参考C语言运算符优先级, % > + > &
最后的输出是
也就是说最终结果为
flag{Act1ve_Defen5e_Test}