reverse答案
培训中例题的答案
第一题
32位exe程序,c++编写的,没有加壳
运行一下看看
程序需要输入serial
直接查看main函数,f5
查看伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
signed int v3; // ebx
char v4; // al
int result; // eax
int v6; // [esp+0h] [ebp-70h]
int v7; // [esp+0h] [ebp-70h]
char v8; // [esp+12h] [ebp-5Eh]
char v9[20]; // [esp+14h] [ebp-5Ch]
char v10; // [esp+28h] [ebp-48h]
__int16 v11; // [esp+48h] [ebp-28h]
char v12; // [esp+4Ah] [ebp-26h]
char v13; // [esp+4Ch] [ebp-24h]
strcpy(&v13, "437261636b4d654a757374466f7246756e");//把字符串"437261636b4d654a757374466f7246756e"复制到v13
while ( 1 )
{
memset(&v10, 0, 0x20u);
v11 = 0;
v12 = 0;
sub_40134B(aPleaseInputYou, v6);//这里调用函数sub_40134B,输出aPleaseInputYou里的字符:"please input your serial:"
scanf(aS, v9);//获取输入字符,给v9
if ( strlen(v9) > 0x11 )
break;//v9长度大于17就退掉
v3 = 0;//设置v3为计数器
do
{
v4 = v9[v3];
if ( !v4 )
break;//v4为0x00时退掉
sprintf(&v8, asc_408044, v4);//asc_408044为"%x" sprintf(&v8,"%x",v4)把v4的值的16进制发到v8
strcat(&v10, &v8);//连接字符串v10与v8并把得到的字符串赋值给v10
++v3;//计数器增加
}
while ( v3 < 17 );
if ( !strcmp(&v10, &v13) )
sub_40134B(aSuccess, v7);//strcmp(&v10, &v13) 在v10的字符串与v13的相等时返回0
else
sub_40134B(aWrong, v7);
}
sub_40134B(aWrong, v7);
result = stru_408090._cnt-- - 1;
if ( stru_408090._cnt < 0 )
return _filbuf(&stru_408090);
++stru_408090._ptr;
return result;
}
程序基本逻辑是将输入的值v9转为16进制后与v13作比较,相同则success
v13=437261636b4d654a757374466f7246756e
接下来只要把v13从16进制转为字符串即可
第二题
和上一道题一样
直接查看main函数伪代码
这里我们先shift + f12
查看一下字符串
发现有一行字符串是flag get
双击进入汇编语言看一下
可以发现又出现了和上一道题一样的大串的数据
这里我们呢直接使用ida自带的工具查看一下这串数据是什么
可以看出来ida已经自动识别出了这段数据具体的内容是什么
将两段数据恢复为可以识别的形态
可以发现数据恢复了,但是顺序有问题
这里由于使用的是大端序(主机使用大端序,网络使用小端序),所以字符串是反着的
这里我们呢只要将第一段数据右键转为数据
这里可以发现数据格式已经从xmmword格式转为db格式了
然后右键选择我所标注的选项,就可以将数据自动修复为正序的数据
第三题
exeinfo检查和上面两道题一样
ida打开
这里发现程序调用的函数是出奇的多,逻辑也是很复杂。当面对这种情况是,就需要使用动态调试的方法
这里我们使用x32dbg进行调试
这里我们选择搜索--所有模块--字符串
这里我们呢直接搜索flag
可以看到很明显的48B0F0:"done!!! the flag is "
在这段语句下面紧接着调用了call函数,猜测这段call指令应该是输出了flag
下面我们根据程序界面的关键字,就是需要输入n的地方查看一下
可以看到输入后就有call函数来判断,那么我们只要使得这些判断和判断后进行赋值的语句失效,那么我们是否可以直接让程序输出flag
直接将输入后的语句全部nop填充掉,看看程序
程序已经因为流程被打断,直接输出了flag
第四题
可以看出不一样了,程序不是一个常规的PE程序,而是一个elf格式的文件,这个格式是Linux执行文件
这里我们使用动态分析的方式对程序进行分析
虽说是动态分析,但是依然要借助ida
先看main函数
main函数一共调用了四个函数,一个个看
setlocale
banner
prompt_authentication
authenticate
通览一遍,感觉最重要的函数是authenticate
记下来我们详细看一下authenticate
先分别看一下其中的unk_8048B44
和unk_8048BA4
unk_8048B44
.rodata:08048B44 unk_8048B44 db 53h ; S ; DATA XREF: authenticate+78↑o
.rodata:08048B45 db 0
.rodata:08048B46 db 0
.rodata:08048B47 db 0
.rodata:08048B48 db 75h ; u
.rodata:08048B49 db 0
.rodata:08048B4A db 0
.rodata:08048B4B db 0
.rodata:08048B4C db 63h ; c
.rodata:08048B4D db 0
.rodata:08048B4E db 0
.rodata:08048B4F db 0
.rodata:08048B50 db 63h ; c
.rodata:08048B51 db 0
.rodata:08048B52 db 0
.rodata:08048B53 db 0
.rodata:08048B54 db 65h ; e
.rodata:08048B55 db 0
.rodata:08048B56 db 0
.rodata:08048B57 db 0
.rodata:08048B58 db 73h ; s
.rodata:08048B59 db 0
.rodata:08048B5A db 0
.rodata:08048B5B db 0
.rodata:08048B5C db 73h ; s
.rodata:08048B5D db 0
.rodata:08048B5E db 0
.rodata:08048B5F db 0
.rodata:08048B60 db 21h ; !
success
unk_8048BA4
.rodata:08048BA4 unk_8048BA4 db 41h ; A ; DATA XREF: authenticate:loc_804878F↑o
.rodata:08048BA5 db 0
.rodata:08048BA6 db 0
.rodata:08048BA7 db 0
.rodata:08048BA8 db 63h ; c
.rodata:08048BA9 db 0
.rodata:08048BAA db 0
.rodata:08048BAB db 0
.rodata:08048BAC db 63h ; c
.rodata:08048BAD db 0
.rodata:08048BAE db 0
.rodata:08048BAF db 0
.rodata:08048BB0 db 65h ; e
.rodata:08048BB1 db 0
.rodata:08048BB2 db 0
.rodata:08048BB3 db 0
.rodata:08048BB4 db 73h ; s
.rodata:08048BB5 db 0
.rodata:08048BB6 db 0
.rodata:08048BB7 db 0
.rodata:08048BB8 db 73h ; s
.rodata:08048BB9 db 0
.rodata:08048BBA db 0
.rodata:08048BBB db 0
.rodata:08048BBC db 20h
.rodata:08048BBD db 0
.rodata:08048BBE db 0
.rodata:08048BBF db 0
.rodata:08048BC0 db 64h ; d
.rodata:08048BC1 db 0
.rodata:08048BC2 db 0
.rodata:08048BC3 db 0
.rodata:08048BC4 db 65h ; e
.rodata:08048BC5 db 0
.rodata:08048BC6 db 0
.rodata:08048BC7 db 0
.rodata:08048BC8 db 6Eh ; n
.rodata:08048BC9 db 0
.rodata:08048BCA db 0
.rodata:08048BCB db 0
.rodata:08048BCC db 69h ; i
.rodata:08048BCD db 0
.rodata:08048BCE db 0
.rodata:08048BCF db 0
.rodata:08048BD0 db 65h ; e
.rodata:08048BD1 db 0
.rodata:08048BD2 db 0
.rodata:08048BD3 db 0
.rodata:08048BD4 db 64h ; d
.rodata:08048BD5 db 0
.rodata:08048BD6 db 0
.rodata:08048BD7 db 0
.rodata:08048BD8 db 21h ; !
.rodata:08048BD9 db 0
.rodata:08048BDA db 0
.rodata:08048BDB db 0
.rodata:08048BDC db 0Ah
.rodata:08048BDD db 0
accessdenied
一个是成功,一个是访问被拒,很显然,这个就是输出我们flag是否正确的函数
接下来我们向上看
s2 = decrypt(&s, &dword_8048A90);
if ( fgetws(ws, 0x2000, stdin) )
{
ws[wcslen(ws) - 1] = 0;
if ( !wcscmp(ws, s2) )
wprintf(&unk_8048B44);
else
wprintf(&unk_8048BA4);
}
free(s2);
}
判断s2和ws是否匹配
匹配输出success
,反之输出accessdenied
而s2是经过decrypt
函数加密过的,我们跟进一下decrypt
函数
可以大致看出函数的加密流程,但是加密流程较为复杂(我看不懂)所以在这考虑使用动态分析的方式,直接读取寄存器中加密过后的s2数值
接下来我们需要找到s2加密后储存在那个寄存器中
而这就需要去authenticate
函数的汇编代码里面找
可以看到其中有一处是通过call汇编指令,将数据送入了decrypt
函数,出来后的数据被直接存放在eax
中
现在我们的目的就是让程序执行decrypt
函数,然后查看eax
寄存器中的值
先给decrypt
函数打个断点
接下来程序运行到打断点的位置停下来
这时候程序在刚碰到decrypt
函数的时候就停了下来,所以我们需要让他步进这段函数
使用指令n
可以看到gdb输出的信息,其中说明了程序已经执行完了decrypt
函数
接下来我们直接查看eax寄存器中的值
6--显示6行数据
s--字符型
w--字节形式