直接上伪代码
__int64 sub_140001290()
{
bool v0; // si
__int64 v1; // rax
__int64 v2; // r8
unsigned __int8 *v3; // rax
unsigned __int8 *v4; // rbx
int v5; // er10
__int64 v6; // r11
_BYTE *v7; // r9
void **v8; // r8
__int64 v9; // rdi
__int64 v10; // r15
__int64 v11; // r12
__int64 v12; // rbp
signed int v13; // ecx
unsigned __int8 *v14; // rdx
__int64 v15; // rdi
__int64 *v16; // r14
__int64 v17; // rbp
__int64 v18; // r13
__int64 *v19; // rdi
__int64 v20; // r12
__int64 v21; // r15
__int64 v22; // rbp
__int64 v23; // rdx
__int64 v24; // rbp
__int64 v25; // rbp
__int64 v26; // r10
__int64 v27; // rdi
__int64 v28; // r8
bool v29; // dl
__int64 v30; // rax
void *v31; // rdx
__int64 v32; // rax
__int64 v33; // rax
_BYTE *v34; // rcx
__int64 v36; // [rsp+20h] [rbp-68h]
void *Memory; // [rsp+30h] [rbp-58h]
unsigned __int64 v37; // [rsp+40h] [rbp-48h]
unsigned __int64 v39; // [rsp+48h] [rbp-40h]
v0 = 0;
v37 = 0i64;
v39 = 15i64;
LOBYTE(Memory) = 0;
v1 = sub_1400019C0(std::cout, "I'm a first timer of Logic algebra , how about you?");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v1, sub_140001B90);
sub_1400019C0(std::cout, "Let's start our game,Please input your flag:");
sub_140001DE0(std::cin, &Memory, v2);
std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, sub_140001B90);
if ( v37 - 5 > 0x19 )
{
v33 = sub_1400019C0(std::cout, "Wrong input ,no GXY{} in input words");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v33, sub_140001B90);
goto LABEL_45;
}
v3 = (unsigned __int8 *)sub_1400024C8(0x20ui64);
v4 = v3;
if ( v3 )
{
*(_QWORD *)v3 = 0i64;
*((_QWORD *)v3 + 1) = 0i64;
*((_QWORD *)v3 + 2) = 0i64;
*((_QWORD *)v3 + 3) = 0i64;
}
else
{
v4 = 0i64;
}
v5 = 0;
if ( v37 > 0 )
{
v6 = 0i64;
do
{
v7 = &Memory;
if ( v39 >= 0x10 )
v7 = Memory;
v8 = &Dst;
if ( (unsigned __int64)qword_140006060 >= 0x10 )
v8 = (void **)Dst;
v4[v6] = v7[v6] ^ *((_BYTE *)v8 + v5++ % 27);
++v6;
}
while ( v5 < v37 );
}
v9 = 0i64;
v10 = 0i64;
v11 = 0i64;
v12 = 0i64;
if ( (signed int)v37 > 30 )
goto LABEL_28;
v13 = 0;
if ( (signed int)v37 <= 0 )
goto LABEL_28; // 输入长度在0到30之间
v14 = v4;
do
{
v15 = *v14 + v9;
++v13;
++v14;
switch ( v13 )
{
case 8:
v12 = v15;
goto LABEL_24;
case 16:
v11 = v15;
goto LABEL_24;
case 24:
v10 = v15;
LABEL_24:
v15 = 0i64;
break;
case 32:
sub_1400019C0(std::cout, "ERRO,out of range");
exit(1);
break;
}
v9 = v15 << 8;
}
while ( v13 < (signed int)v37 ); // 将v4以8个字节为单位分为v12,v11,v10,v9四部分
if ( v12 )
{
v16 = (__int64 *)sub_1400024C8(0x20ui64);
*v16 = v12;
v16[1] = v11;
v16[2] = v10;
v16[3] = v9;
goto LABEL_29;
}
LABEL_28:
v16 = 0i64;
LABEL_29:
v36 = v16[2];
v17 = v16[1];
v18 = *v16;
v19 = (__int64 *)sub_14000223C(0x20ui64);
if ( IsDebuggerPresent() )
{
sub_1400019C0(std::cout, "Hi , DO not debug me !");
Sleep(0x7D0u);
exit(0);
}
v20 = v17 & v18;
*v19 = v17 & v18;
v21 = v36 & ~v18;
v19[1] = v21;
v22 = ~v17;
v23 = v36 & v22;
v19[2] = v36 & v22;
v24 = v18 & v22;
v19[3] = v24; // 这里很多变量,但是可以发现v20==v19,v19[1]==v21......,简化了很多
if ( v21 != 1176889593874i64 ) // 约束条件,v21 == 1176889593874i64一般都是成立的
{
v19[1] = 0i64;
v21 = 0i64;
}
v25 = v21 | v20 | v23 | v24;
v26 = v16[1];
v27 = v16[2];
v28 = v23 & *v16 | v27 & (v20 | v26 & ~*v16 | ~(v26 | *v16));
v29 = 0;
if ( v28 == 577031497978884115i64 ) // v28 == 577031497978884115i64成立
v29 = v25 == 4483974544037412639i64; // v25 == 4483974544037412639i64成立
if ( (v25 ^ v16[3]) == 4483974543195470111i64 )
v0 = v29;
if ( (v21 | v20 | v26 & v27) != (~*v16 & v27 | 0xC00020130082C0Ci64) || v0 != 1 )
{
sub_1400019C0(std::cout, "Wrong answer!try again");
j_j_free(v4);
}
else
{
v30 = sub_1400019C0(std::cout, "Congratulations!flag is GXY{");
v31 = &Memory;
if ( v39 >= 0x10 )
v31 = Memory;
v32 = sub_140001FD0(v30, v31, v37);
sub_1400019C0(v32, "}");
j_j_free(v4);
}
LABEL_45:
if ( v39 >= 0x10 )
{
v34 = Memory;
if ( v39 + 1 >= 0x1000 )
{
v34 = (_BYTE *)*((_QWORD *)Memory - 1);
if ( (unsigned __int64)((_BYTE *)Memory - v34 - 8) > 0x1F )
invalid_parameter_noinfo_noreturn();
}
j_j_free(v34);
}
return 0i64;
}
困扰我们的是v37,Dst
(v38和qword_140006060这种跳转一般没有意义)
在动态调试相关位置下断点,观察变量
多次输入,多次观测
发现v37是输入长度,v38在输入长度较大时为1f,较小为f
同时得到Dst = i_will_check_is_debug_or_not
v37是输入长度,那么v37到底是什么时候被改变的呢?
注意主函数的变量声明
我们进入sub_140001DE0(std::cin, &Memory, v2);
__int64 __fastcall sub_140001DE0(__int64 a1, _QWORD *a2, __int64 a3)
{
_QWORD *v3; // rbx
__int64 v4; // rdi
unsigned int v5; // esi
char v6; // r15
__int64 v7; // r12
__int64 v8; // rcx
__int64 v9; // rax
__int64 v10; // rdx
__int64 v11; // r8
__int64 v12; // r9
__int64 v13; // r13
void (__fastcall ***v14)(_QWORD, signed __int64); // rax
_BYTE *v15; // rax
__int64 v16; // rcx
signed __int64 v17; // r14
int v18; // eax
char v19; // r8
unsigned __int64 v20; // rcx
unsigned __int64 v21; // rdx
_QWORD *v22; // rax
__int64 v23; // rcx
char v25; // [rsp+40h] [rbp-38h]
__int64 v26; // [rsp+48h] [rbp-30h]
v3 = a2; // 注意这里,把memory地址给了v3
v4 = a1;
v5 = 0;
v6 = 0;
v7 = a1;
v8 = *(_QWORD *)(*(signed int *)(*(_QWORD *)a1 + 4i64) + a1 + 72);
if ( v8 )
(*(void (__cdecl **)(__int64, _QWORD *, __int64))(*(_QWORD *)v8 + 8i64))(v8, a2, a3);
if ( (unsigned __int8)std::basic_istream<char,std::char_traits<char>>::_Ipfx(v4, 0i64) )
{
v9 = std::ios_base::getloc(v4 + *(signed int *)(*(_QWORD *)v4 + 4i64), &v25);
v13 = sub_1400018C0(v9);
if ( v26 )
{
v14 = (void (__fastcall ***)(_QWORD, signed __int64))(*(__int64 (__cdecl **)(__int64, __int64, __int64, __int64))(*(_QWORD *)v26 + 16i64))(
v26,
v10,
v11,
v12);
if ( v14 )
(**v14)(v14, 1i64);
}
v3[2] = 0i64; // 而v3[2]就是 v3的地址+偏移地址0x10
// 就是主函数rsp+30h + 10h = rsp + 40h
// 即是v37地址
v15 = v3;
if ( v3[3] >= 0x10ui64 )
v15 = (_BYTE *)*v3;
*v15 = 0;
v16 = *(signed int *)(*(_QWORD *)v4 + 4i64);
v17 = *(_QWORD *)(v16 + v4 + 40);
if ( v17 <= 0 || (unsigned __int64)v17 >= 0x7FFFFFFFFFFFFFFFi64 )
v17 = 0x7FFFFFFFFFFFFFFFi64;
v18 = std::basic_streambuf<char,std::char_traits<char>>::sgetc(*(_QWORD *)(v16 + v4 + 72));
while ( v17 )
{
if ( v18 == -1 )
{
v5 = 1;
break;
}
v19 = v18;
if ( *(_BYTE *)(*(_QWORD *)(v13 + 24) + 2i64 * (unsigned __int8)v18) & 0x48 )
break;
v20 = v3[2];
v21 = v3[3];
if ( v20 >= v21 )
{
sub_140001BD0(v3);
}
else
{
v3[2] = v20 + 1;
v22 = v3;
if ( v21 >= 0x10 )
v22 = (_QWORD *)*v3;
*((_BYTE *)v22 + v20) = v19;
*((_BYTE *)v22 + v20 + 1) = 0;
}
v6 = 1;
--v17;
v18 = std::basic_streambuf<char,std::char_traits<char>>::snextc(*(_QWORD *)(*(signed int *)(*(_QWORD *)v4 + 4i64)
+ v4
+ 72));
}
}
*(_QWORD *)(*(signed int *)(*(_QWORD *)v4 + 4i64) + v4 + 40) = 0i64;
if ( !v6 )
v5 |= 2u;
std::basic_ios<char,std::char_traits<char>>::setstate(v4 + *(signed int *)(*(_QWORD *)v4 + 4i64), v5, 0i64);
v23 = *(_QWORD *)(*(signed int *)(*(_QWORD *)v7 + 4i64) + v7 + 72);
if ( v23 )
(*(void (**)(void))(*(_QWORD *)v23 + 16i64))();
return v4;
}
ida变量数据是线性的,传入一个参数,其周围数据也可以被改变
最后上官方wp,题目后面给了v11做已知条件(
from z3 import *
xor_str="i_will_check_is_debug_or_not"
s = Solver()
x = BitVec('x',64)
y = BitVec('y',64)
z = BitVec('z',64)
l = BitVec('l',64)
s.add(y==0xd44335b301b2c3e)
s.add((z&(~x))==0x11204161012)
s.add((z&~y) & x | z & ((x&y) | y & (~x) | ~(y | x))==0x8020717153E3013)
s.add(((z&(~x)) | (x&y) | (z&(~y)) | (x&(~y))) == 0x3E3A4717373E7F1F)
s.add((((z&(~x)) | (x&y) | (z&(~y)) | (x&(~y))) ^ l) == 0x3E3A4717050F791F)
s.add(((z&(~x)) | (x&y) | y & z) == (((~x)& z)|0xC00020130082C0C))
if s.check() ==sat:
m = s.model()
print(m)
flag=""
ls=[]
ls.append(hex(int(str(m[x]),10))[2:].rjust(16,"0"))
ls.append(hex(int(str(m[y]),10))[2:].rjust(16,"0"))
ls.append(hex(int(str(m[z]),10))[2:].rjust(16,"0"))
ls.append(hex(int(str(m[l]),10))[2:-2])
print(ls)
sum=0
for i in ls:
for j in range(0,len(i),2):
xx=i[j]+i[j+1]
flag+=chr(int(xx,16)^ord(xor_str[(sum)%27]))
sum+=1
print(flag)