二进制逆向题目解析
兴趣是最好的老师
看到这个文件,我们应该先打开,发现是错误的,所以用010Editor打开文件,打开之后,
我们先看文件头,发现PE头发生了错误,我们要把它改为50 45 00 00,然后再打开,就会出现让你输入flag,随意输入几个字符,它会出现错误“wrong”
这样我们就需要用ida打开,寻找flag:
用ida打开文件,是流程图浏览模式:
使用Shift+12调出字符串表,寻找有效字符串,我们已经知道当我们输入flag时,程序会跳出“wrong”,所以我们就应该寻找有效字符“wrong”,
点击进去,可以进入汇编代码区,会看到wrong字符,:
双击红色框内,可以看到对应函数对应的流程图:
F5可以进入汇编代码段:
进入代码段,我们可以看到一串代码:
__int64 main_0()
{
int v0; // edx
__int64 v1; // ST00_8
int v3; // [esp+0h] [ebp-1A0h]
const char **v4; // [esp+4h] [ebp-19Ch]
const char **v5; // [esp+8h] [ebp-198h]
int v6; // [esp+Ch] [ebp-194h]
int i; // [esp+D4h] [ebp-CCh]
int v8; // [esp+E0h] [ebp-C0h]
int v9; // [esp+ECh] [ebp-B4h]
int v10; // [esp+F0h] [ebp-B0h]
int v11; // [esp+F4h] [ebp-ACh]
int v12; // [esp+F8h] [ebp-A8h]
int v13; // [esp+FCh] [ebp-A4h]
int v14; // [esp+100h] [ebp-A0h]
int v15; // [esp+104h] [ebp-9Ch]
int v16; // [esp+108h] [ebp-98h]
int v17; // [esp+10Ch] [ebp-94h]
int v18; // [esp+110h] [ebp-90h]
int v19; // [esp+114h] [ebp-8Ch]
int v20; // [esp+118h] [ebp-88h]
int v21; // [esp+11Ch] [ebp-84h]
int v22; // [esp+120h] [ebp-80h]
int v23; // [esp+124h] [ebp-7Ch]
int v24; // [esp+128h] [ebp-78h]
int v25; // [esp+12Ch] [ebp-74h]
int v26; // [esp+130h] [ebp-70h]
int v27; // [esp+134h] [ebp-6Ch]
int v28; // [esp+138h] [ebp-68h]
int v29; // [esp+13Ch] [ebp-64h]
int v30; // [esp+140h] [ebp-60h]
char v31; // [esp+14Fh] [ebp-51h]
char v32[17]; // [esp+178h] [ebp-28h]
char v33; // [esp+189h] [ebp-17h]
char v34; // [esp+18Ah] [ebp-16h]
char v35; // [esp+18Bh] [ebp-15h]
char v36; // [esp+18Ch] [ebp-14h]
char v37; // [esp+18Dh] [ebp-13h]
v31 = 0;
v9 = 1;
v10 = 4;
v11 = 14;
v12 = 10;
v13 = 5;
v14 = 36;
v15 = 23;
v16 = 42;
v17 = 13;
v18 = 19;
v19 = 28;
v20 = 13;
v21 = 27;
v22 = 39;
v23 = 48;
v24 = 41;
v25 = 42;
v26 = 26;
v27 = 20;
v28 = 59;
v29 = 4;
v30 = 0;
printf("please enter flag:");
while ( 1 )
{
v6 = getch();
v32[v31] = v6;
if ( !(_BYTE)v6 || v32[v31] == 13 )
break;
if ( v32[v31] == 8 )
{
printf("\b\b");
--v31;
}
else
{
printf("%c", v32[v31++]);
}
}
v8 = 0;
for ( i = 0; i < 17; ++i )
{
if ( v32[i] != byte_415768[*(&v9 + i)] )
v8 = 1;
}
if ( v33 != 49 || v34 != 48 || v35 != 50 || v36 != 52 || v37 != 125 )
v8 = 1;
v32[v31] = 0;
printf("\r\n");
if ( v8 )
{
printf("wrong\n");
main(v3, v4, v5);
}
else
{
printf("success\n");
}
system("pause");
HIDWORD(v1) = v0;
LODWORD(v1) = 0;
return v1;
}
我们仔细观察这段代码,寻找flag,由我们运行的程序知,我们输入错误的flag,会出现“wrong”,那么我们查看代码,找到怎样会输出wrong,我们向下看:
if ( v8 )
{
printf("wrong\n");
main(v3, v4, v5);
}
else
{
printf("success\n");
}
如果(v8),则输出wrong,否则,输出success,我们不想输出wrong,肯定要使程序输出success,flag正确,那么v8就要等于0,我们在向上看,怎么令v8等于0:
v8 = 0;
for ( i = 0; i < 17; ++i )
{
if ( v32[i] != byte_415768[*(&v9 + i)] )
v8 = 1;
}
if ( v33 != 49 || v34 != 48 || v35 != 50 || v36 != 52 || v37 != 125 )
v8 = 1;
我们看使v8=1的下面一个if:
if ( v33 != 49 || v34 != 48 || v35 != 50 || v36 != 52 || v37 != 125 )
v8 = 1;
使v8=0,要使if不成立,则v33=49,v34=48,v35=50,v36=52,v37=125,我们又看这五个变量的定义,它们是字符,所以要把它们改成字符,即v33 = ‘1’ ,v34 = ‘0’,v35 = ‘2’,v36 = ‘4’,v37= ‘}’,再观察它们的地址,所以我们可知,flag的最后五个字符为:“1024}”
在向上看这段代码,
for ( i = 0; i < 17; ++i )
{
if ( v32[i] != byte_415768[*(&v9 + i)] )
v8 = 1;
}
for循环中if成立,则v8=1,要使v8=0,则for循环中if不成立,所以,v32[i] = byte_415768[*(&v9 + i)],我们把byte_415768,发现它是一串字符串,如图:
把73h化为字符,就会得到这样这样一串字符串“sKfxEeft}f{gyrYgthtyhifsjei53UUrrr_t2cdsef66246087138\0087138”
[*(&v9 + i)]这里是把v9数组作为下标来取byte_415768中的值,我们再看代码,
int v9; // [esp+ECh] [ebp-B4h]
int v10; // [esp+F0h] [ebp-B0h]
int v11; // [esp+F4h] [ebp-ACh]
int v12; // [esp+F8h] [ebp-A8h]
int v13; // [esp+FCh] [ebp-A4h]
int v14; // [esp+100h] [ebp-A0h]
int v15; // [esp+104h] [ebp-9Ch]
int v16; // [esp+108h] [ebp-98h]
int v17; // [esp+10Ch] [ebp-94h]
int v18; // [esp+110h] [ebp-90h]
int v19; // [esp+114h] [ebp-8Ch]
int v20; // [esp+118h] [ebp-88h]
int v21; // [esp+11Ch] [ebp-84h]
int v22; // [esp+120h] [ebp-80h]
int v23; // [esp+124h] [ebp-7Ch]
int v24; // [esp+128h] [ebp-78h]
int v25; // [esp+12Ch] [ebp-74h]
int v26; // [esp+130h] [ebp-70h]
int v27; // [esp+134h] [ebp-6Ch]
int v28; // [esp+138h] [ebp-68h]
int v29; // [esp+13Ch] [ebp-64h]
int v30; // [esp+140h] [ebp-60h]
我们会发现这些定义的v9到v30他们的地址是连在一起的,把v9到v30作为一个数组,byte_415768[*(&v9 + i)]
这里是把v9数组作为下标来取byte_415768中的值,但是走到这我们还是不知道要输入什么,所以继续向上看:
while ( 1 )
{
v6 = getch();
v32[v31] = v6;
if ( !(_BYTE)v6 || v32[v31] == 13 )
break;
if ( v32[v31] == 8 )
{
printf("\b\b");
--v31;
}
else
{
printf("%c", v32[v31++]);
}
}
这串代码其实没什么意思,要想运行下面的程序,就需要跳过这个循环,所以就必须:!(_BYTE)v6 || v32[v31] == 13
所以,v6使我们程序输入的东西,即flag,而v6和v32相等,所以我们可以写python程序:
a='sKfxEeft}f{gyrYgthtyhifsjei53UUrrr_t2cdsef66246087138\0087138'
b=[1,4,14,10,5,36,23,42,13,19,28,13,27,39,48,41,42,26,20,59,4,0]
for i in range(17):
print(a[b[i]],end='')
print('1024}')
把得到的flag输入程序即可得到success。
注意:这种反编译题,它出来的代码不是真正的c++代码,但是不影响我们做题,所以我们做题时,可以倒着推理,怎样才能获得我们要得到的东西,找到就可以了。
链接:【EXE PE】了解pe段–PE结构详解
链接: C/C++中*和&的用法