主要是从攻防世界中的easyhook中学习,感觉好神奇.
参考了以下博客:
先贴源码:
sub_401370(aPleaseInputFla);
scanf("%31s", input_flag);
if ( strlen(input_flag) == 19 )
{
sub_401220(); //未知名函数,一开始当然不会注意
v4 = CreateFileA(FileName, 0x40000000u, 0, 0, 2u, 0x80u, 0); //创建文件函数
WriteFile(v4, input_flag, 19u, &NumberOfBytesWritten, 0); //写入函数,这些系统函数通常不是重点,如果影响理解代码的话直接查API即可, WriteFile(句柄, 写入字符串, 写入字节, 指向写入直接的指针, 0)
sub_401240(input_flag, &NumberOfBytesWritten);
if ( NumberOfBytesWritten == 1 ) //判断函数
sub_401370(aRightFlagIsYou);
else
sub_401370(aWrong);
system(Command);
result = 0;
}
else
{
sub_401370(aWrong);
system(Command);
result = 0;
}
return result;
}
然后要注意sub_401220():
int sub_401220()
{
HMODULE v0; // eax
DWORD v2; // eax
v2 = GetCurrentProcessId(); //系统函数,获取进程ID,就是当前程序的ID
hProcess = OpenProcess(0x1F0FFFu, 0, v2); //系统函数,返回现有进程对象的句柄。
v0 = LoadLibraryA(LibFileName); //系统函数,将指定的可执行模块映射到调用进程的地址空间,这里LibFileName双击跟踪存入的是kernel32.dll模块,就是导入了它
*(_DWORD *)WriteFile_0 = GetProcAddress(v0, ProcName); //系统函数,返回指定的导出动态链接库(DLL)函数的地址。 ProcName存放的是WriteFile函数名,也就是导入WriteFile函数。
lpAddress = *(LPVOID *)WriteFile_0; //获取WriteFile函数地址
if ( !*(_DWORD *)WriteFile_0 ) //无用函数,因为kernel32.dll模块的WriteFile函数一定存在
return sub_401370(&unk_40A044); //sub_401370函数类型puts函数,是通过主函数分析得到的,&unk_40A044处的字符串是获取原API入口地址出错,不用管他。
unk_40C9B4 = *(_DWORD *)lpAddress; //这里获取WriteFile函数的地址
*((_BYTE *)&unk_40C9B4 + 4) = *((_BYTE *)lpAddress + 4); //这里地址后四位也保持一样,不知道有什么用
aHook1 = 0xE9; //这里连同第二句是我犯下的第二个错误:这里转十六进制不是E9,是JMP的机器码指令,而不一开始并没有机器码的相关知识。
aHook2 = (char *)sub_401080 - (char *)lpAddress - 5; //这里是一个偏移地址,而且还是满足HOOK的连续地址。之所以这样写是因为汇编语言JMP address被编译后会变成机器指令码,E9 偏移地址,偏移地址=目标地址-当前地址-5(jmp和其后四位地址共占5个字节)。所以前面直接用E9,这里直接用偏移地址就省去编译生成机器码那一步。这也是HOOK的原型。
return sub_4010D0(); 返回一个函数,继续跟踪,因为前面并没有什么修改程序的行为
}
主要看下面,可以看到这里搞了jmp,而且注意aHook1和aHook2(这里是我修改过的名字)是连续的,也就是说,可以看成jmp sub_401080.
接下来看sub_4010D0():
BOOL sub_4010D0()
{
DWORD v1; // [esp+4h] [ebp-8h] BYREF
DWORD flOldProtect; // [esp+8h] [ebp-4h] BYREF
v1 = 0;
VirtualProtectEx(hProcess, lpAddress, 5u, 4u, &flOldProtect);
WriteProcessMemory(hProcess, lpAddress, &aHook1, 5u, 0);//修改
return VirtualProtectEx(hProcess, lpAddress, 5u, flOldProtect, &v1);
}
//VirtualProtectEx(进程,修改地址,修改区域大小,[修改其中权限,1,2,4对应执行,写,读],一个保存修改前权限的变量 ),所以这三行作用是对lpAddress所存储的地址处进行了5字节的权限修改操作,先改成读写再往此处写入以前面E9 偏移量地址处的5个字节(即上面的跳板指令),最后恢复权限,完成修改。
这就是hook,劫持程序流.
那么现在WriteFile被改写成了jmp也就是跳到sub_401080,我们看看它:
int __stdcall sub_401080(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped)
{
int v5; // ebx
v5 = sub_401000(lpBuffer, nNumberOfBytesToWrite); //加密函数
sub_401140(); //这个是把WriteFile恢复的函数
WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
if ( v5 )
*lpNumberOfBytesWritten = 1;
return 0;
}
sub_401000:
int __cdecl sub_401000(int input_flag, int _19)
{
char i; // al
char v3; // bl
char v4; // cl
int v5; // eax
for ( i = 0; i < _19; ++i )
{
if ( i == 18 )
{
*(_BYTE *)(input_flag + 18) ^= 19u; // 这里犯下第五个错误,写脚本时这种单独的if语句应该直接在外面单独成行,不然就要多写几个elif语句了,界面就会很乱!
}
else
{
if ( i % 2 )
v3 = *(_BYTE *)(i + input_flag) - i;
else
v3 = *(_BYTE *)(i + input_flag + 2);
*(_BYTE *)(i + input_flag) = i ^ v3;
}
}
v4 = 0;
if ( _19 <= 0 ) // 不会小于等于,所以不会在这里返回
return 1;
v5 = 0;
while ( aAjygkfm[v5] == *(_BYTE *)(v5 + input_flag) ) //这里犯下第四个错误,前面的加密逻辑中我们没有可以参照的字符串,因为flag是推出来的,而真正的字符串在后面,所以逆向逻辑时首先要找到非输入的字符串才行!这里双击跟踪 aAjygkfm等于ajygkFm.\x7f_~-SV{8mLn
{
v5 = ++v4;
if ( v4 >= _19 )
return 1;
}
return 0;
}
写脚本,可得flag为:"-lag{Ho0k_w1th_Fun}"
标签:函数,flag,地址,更新,hook,WriteFile,初探,input,sub From: https://www.cnblogs.com/T0fV404/p/18638633