基础步骤,拉进IDA中按下f5得到main函数的反汇编代码
<主要流程>:判断输入flag长度是否为19,一个不明函数401220,接着两个API调用(查了一下就是创建文件然后写入内容,这里是将输入的flag也就是buffer中的内容写入文件),又是一个不明函数401240;判断NumberOfBytesWritten的值,为1则flag正确,可以发现这个参数再writefile和401240函数中都有调用到,初步猜测flag在这俩函数内(后续结果证明我是错的)。
双击进入401240函数内,反汇编伪代码如下:
虽然他已经跟我讲“This_is_not_the_flag”了,但我看到底下的混淆操作就误以为是要把这个字符串进行变换,看了一会观察到这里要求的字符串长度是20,而前面是19,莎莎比比的我就考虑了函数压栈的顺序以及栈空间的利用,但是a2的值是0x13,没办法匹配上,纠结了很久还是没做出来。所以其实这是一个<干扰函数>淦啊
<flag被变换了>在这个函数里,可以发现我们输入的flag被变换了,回到刚才main函数的伪代码,通过设立断点发现总是在WriteFile函数执行完就被变换,可是静态下进入函数的汇编代码里查看,并无修改flag的举动,有蹊跷!此时还有一个函数没看,那就是401220!!!
双击进入401220函数:
看到了一大堆有关进程ID和地址的函数,结合题目所给的HOOK提示(一定要注意题目给的提示!!!),<HOOK>真相只有一个,那就是401220函数获取了WriteFile函数的地址,修改了执行代码,变换了flag,因为可以看见writefile函数创建并写入信息的txt,所以hook函数执行完自己的代码后也执行了真正的writefile函数。
前9行调用了好多API,功能就是获取进程ID(v2)、句柄(gProcess)和WriteFile函数地址(writeFile_0);后面几行则是一大堆赋值操作:
lpAddress和unk_40C9B4拿到了writefile函数地址
byte_40C9BC赋值为 -23,十六进制是E9
40C9BD的赋值操作是401080这个函数的地址减去writefile函数地址再减去5,熟悉汇编代码的大佬应该一眼就能看出这是E9跳转指令后面跟随的跳转地址的计算方法,E9 xxxx,xxxx = 目的地址 - 当前地址 - 指令长度
在这之后调用了4010D0函数,双击进入:
又是API,VirtualProtectEx函数的意思是,将hProcess进程句柄里ipAddress(现在是writefile函数地址)开始的5个字节的保护模式由floldProtect改为4,这样应该就能方便writeProcessMemory函数修改从lpAdress(现在是writefile函数地址)开始的5个字节为40C9BC的内容;最后return又调用了前一个函数,恢复了保护模式。
这里发现了一个盲点,40C9BC只有一个字节,却需要写入五个字节,还差四个字节刚好就是跟在后面的40C9BD,到这里就很清晰啦,writefile函数地址开始的指令被修改成无条件跳转指令,跳转到了401080函数。
进入401080函数一探究竟!
又内嵌了函数,一个一个来吧~
可以看到当v5为1时,lpNumberOfBytesWritten置为1,此时main函数才会输出 成功 信息。v5是401000函数的返回值,他的第一个参数就是我们输入的flag!快双击他!
终于啊,这才应该是真正的加密函数!8-22行对输入的flag进行处理,27-31行判断处理完的flag是否与一个字符串常量匹配,只有完全匹配时才返回1。根据这个可以敲下解密代码:
#include <iostream>
#include <string.h>
using namespace std;
int main(){
char s[19] = {0x61,0x6A,0x79,0x67,0x6B,0x46,0x6D,0x2E,0x7F,0x5F,0x7E,0x2D,0x53,0x56,0x7B,0x38,0x6D,0x4C,0x6E};
char flag[19];
char v3;
for(char i = 0; i < 0x13; i++)
{
if(i == 18)
{
flag[i] = s[i] ^ 0x13;
}
else
{
v3 = s[i] ^ i;
if(i%2)
flag[i] = v3 + i;
else
flag[i+2] = v3;
}
cout << flag[i];
}
}
运行结果为也不知道第一个字符出了什么问题,改为 f 就是正确的flag啦。到这里题目就算成功做出了,但接下来程序还没执行完,还有函数没看完,继续看吧。
对输入flag加密后程序执行了401140函数,让我看看!
哦吼,看似跟之前的4010D0函数很像,实际上算是个逆过程,unk_40C9B4存储着write file的地址,结果就是把writefile函数恢复成原来的样子。
接下来,由于writefile函数之前被hook了,并没有执行,所以需要再执行一遍。
总结:这道题的精髓就在于hook函数修改了writefile函数的执行路径,跳转到了加密函数上;当执行writefile函数时,我们输入的flag就被加密了(会让不明所以的小白我以为是writefile函数内对flag进行加密),之后加密函数还会恢复writefile的执行路径,真正的执行其实际功能。