HOOK的原理分析与easy_hook逆向题分析
2023-6-1 18:0:5 Author: 看雪学苑(查看原文) 阅读量:56 收藏

内容

(1)API函数分析:选取特定的API函数,通过逆向工程技术分析其二进制代码结构和执行流程,了解其参数传递方式和返回值处理方法。
(2)HOOK技术实现:介绍HOOK技术的基本原理和实现方法。
(3)OD的动态分析、IDA的静态分析工具的使用。
(4)基于攻防世界逆向题easy hook的静态分析。

简单介绍一下hook

HOOK是一种截取信息、更改程序执行流向、添加新功能的强大技术。通过在执行真正的目标函数之前执行实现插入的代码,获取程序执行过程的决定权。

二次开发或补丁信息截获安全防护 HOOK使用最多的战场,各类安全软件都会在应用层和内核中安装hook、系统回调、过滤驱动等。从而对系统中所有的进/线程、窗口、文件、网络操作等进行检查、过滤和拦截,在x86系统中,流行在KiFastCallEntry中进行HOOK,以达到间接hook所有系统调用的目的,同时在应用层中通常也会有一些安全模块,通过安装一些应用层HOOK来配合操作。

API HOOK

API HOOK是一种HOOK技术,它的原理是通过修改目标程序中的API调用来实现对程序的拦截和修改。在Windows操作系统中,每个API都有一个唯一的标识符,称为函数地址或入口地址。通过修改目标程序中API调用的入口地址,可以将程序的流程控制转移到HOOK函数中,从而实现对程序的拦截和修改。


具体来说,API HOOK的实现分为以下几个步骤:

(1)获取目标程序中API的入口地址;
(2)保存原始的API入口地址,以便在HOOK函数中调用;
(3)修改目标程序中API的入口地址,将其指向HOOK函数;
(4)在HOOK函数中实现自定义的功能或修改;

如果需要,可以在HOOK函数中调用原始的API函数。

通过API HOOK技术,可以实现对目标程序中的API进行拦截和修改,比如监控系统调用、实现自定义的功能、加强安全性等。但是,使用API HOOK技术也有一定的风险和副作用,如果使用不当可能会导致系统不稳定或者安全漏洞。因此,在使用API HOOK技术时需要注意安全性和稳定性问题。

HOOK 流程

HOOK 本函数MessageBoxA的源代码(能运行)

#include <stdio.h>
#include<Windows.h>
typedef int (WINAPI* lpMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
BYTE olddata32[5] = { 0 };
void hook();
void Unhook();
//回调函数 当调用hook时,hook执行完成后调用该函数后再进入main函数
int WINAPI MyMessageboxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
Unhook();
lpMessageBoxA messagebox = (lpMessageBoxA)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
int ret = messagebox(0, "inlinehook", "tip3", 0);
hook();//函数释放前再次HOOK,为了拦截下次调用
return ret;
}
void hook()
{
//获取messagebox的基地址
DWORD messagebox = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
BYTE data[5] = { 0xe9, };
DWORD offset = (DWORD)MyMessageboxA - messagebox - 5; //计算jmp跳转的偏移量
//保存前五个字节的数据
memcpy(olddata32, (const void *)messagebox, 5);
//把偏移量与JMP指令拼接
memcpy(&data[1], &offset, 4);
DWORD oldProtect = 0;
//更改页面属性 将内存改为可读可写可执行
VirtualProtect((LPVOID)messagebox, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy((void*)messagebox, data, 5);
//还原属性
VirtualProtect((LPVOID)messagebox, 5, oldProtect, &oldProtect);
}
void Unhook()
{

DWORD messagebox = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
DWORD oldProtect = 0;
VirtualProtect((LPVOID)messagebox, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy((void*)messagebox, olddata32, sizeof(olddata32));
VirtualProtect((LPVOID)messagebox, 5, oldProtect, &oldProtect);

}
void main()
{
MessageBoxA(0, "hello", "tip1", 0);
hook();
MessageBoxA(0, "hello", "tip2", 0);
}

结果分析

Hook前




Hook后,我们的弹窗本该是hello的但是hook后,程序流程被我们修改了。


过程分析

(1)首先我们先给MessageBoxA API下软件断点

这里我提供三种方法:

方法一: Ctrl + F 快捷键输入MessageBoxA,跳到一个地址,下软件断点。




方法二:在Command后面红色标注的命令框里输入:BP MessageBoxA。




方法三:利用吾爱破解里面的OD插件给MessageBoxA下断点。




(2)下好断点后程序运行后跳到了760D34B0地址




我们可以看到760D34B0就是我们BP MessageBoxA下断点后的地址,我们可以记住这个地址,因为我们HOOK主要修改的就是该地址,下面我们从汇编代码分析该函数流程。

760D34B0 > 8BFF mov edi,edi

它只是一个占位符,方便在调试和优化代码时进行修改。


760D34B2 55 push ebp
760D34B3 8BEC mov ebp,esp

通过这两条指令,函数就可以在堆栈中为局部变量分配存储空间,并在函数执行过程中保存和恢复现场。这样做的好处是可以避免局部变量和其他函数之间的冲突,同时也可以提高函数的可读性和可维护性。


760D34B5 6A FF push -0x1
760D34B7 6A 00 push 0x0

通常被用于函数的参数传递,将参数压入堆栈中,以便被调用函数在堆栈中获取。


760D34B9 FF75 14 push dword ptr ss:[ebp+0x14]
760D34BC FF75 10 push dword ptr ss:[ebp+0x10] ;
760D34BF FF75 0C push dword ptr ss:[ebp+0xC] ;
760D34C2 FF75 08 push dword ptr ss:[ebp+0x8]
这四个push是将MessageBoxA的四个参数压入堆栈中去。

760D34C5 E8 D6010000 call user32.MessageBoxTimeoutA
该函数是显示MessageBoxA的弹窗函数。

(3)执行函数后我们继续运行,执行该函数后OD显示如图所示:



00C4295C 6A 00 push 0x0
00C4295E 68 187CC400 push hookmess.00C47C18 ; ASCII "tip2"
00C42963 68 107CC400 push hookmess.00C47C10 ; ASCII "hello"
00C42968 6A 00 push 0x0
这四个push是hook前的函数流程,将该四个push压入堆栈后,在运行到光标的call中跳入到前面的MessageBoxA函数流程。

(4)hook后我们F7进入该函数




我们可以看到760D34B0-760D34B7地址的汇编指令被我们修改了
Hook前的执行指令:
760D34B0 > 8BFF mov edi,edi
760D34B2 55 push ebp
760D34B3 8BEC mov ebp,esp
Hook后执行过程的指令被我们修改成了jmp 00C4110E
00C4110E地址就是我们修改MessageBoxA函数的地址

(5)HOOK函数的逆向分析如下:

这个跳转是VC编译器的特性可以不用管。



00C41730是HOOK函数的起始地址。



这下面的四个push就是将我们修改MessageBoxA也就是MyMessageBoxA的参数压入堆栈,然后通过下面的call调用。


最后执行以前的函数流程,将修改后的MessageBoxA函数的弹窗显示出来。


攻防世界逆向题Easy Hook分析过程

查壳

如下图所示,我们可以知道该程序是32位的PE文件。

IDA静态分析

步骤一,拉进IDA中按下f5得到main函数的反汇编代码。




步骤二:分析伪代码,如下图是我整理的伪代码注释,我们分析完毕后可以知道flag的长度为19,sub_401220()函数就是hook函数,hook的是WriteFile函数。加密过程也在该函数里面。




步骤三:我们运行程序输入1234567890123456789,可以在程序运行目录下找到一个文件,我们用文本的形式打开该文件可以得到下面的结果,并不是我们的输入。



步骤四:进入sub_401220()函数分析,这个函数逻辑清晰地找到了库函数 WriteFile 的地址,向 byte_40C9BC 中载入了 JUMP 指令的字节码 E9,计算了 sub_401080 距离 WriteFile 的第五个字节的距离。我们分析先sub_4010D0()函数。


步骤五:进入sub_4010D0()函数分析,我们分析该函数就可以确定是hook了,我们回到 sub_401220()函数,可以知道sub_401080就是整个程序的关键了。



步骤六:进入sub_401080()函数分析,该函数就是我们HOOK的返回值,通过sub_401080()函数重新定义了一个WriteFile函数,修改了参数里面的值。我们先分析sub_401140()函数有什么用。



步骤七:进入sub_401140()函数分析,在 sub_401000() 函数加密完 lpBuffer 后,sub_401140又把 WriteFile 函数还原,继续写操作。




步骤八:进入sub_401000()函数分析,该函数就是我们加密的函数,flag由此解密,简单的异或,第一个判断i=18时,flag[i] = a1[i] ^ 0x13; i 不等于18时,第二个判断,if (i % 2),满足条件flag[i] = (a1[i] ^ i) + i,不满足条件flag[i+2] = (res[i] ^ i)。至此IDA的分析就完成了,接下来就是写解密脚本了。




最后写解密脚本,得到flag。


#include <stdio.h>
unsigned char res[] = {
0x61, 0x6A, 0x79, 0x67, 0x6B, 0x46, 0x6D, 0x2E,
0x7F, 0x5F, 0x7E, 0x2D, 0x53, 0x56, 0x7B, 0x38,
0x6D, 0x4C, 0x6E, 0x00};
char flag[19];
int main()
{for (int i = 18; i >= 0; i--){
if (i == 18)
flag[i] = res[i] ^ 0x13;
else{
if (i % 2){
flag[i] = (res[i] ^ i) + i;
}else{
flag[i+2] = (res[i] ^ i);
} } }
for (int i = 1; i < 19; i++)
printf("%c", flag[i]);}

看雪ID:孤岛林潭

https://bbs.kanxue.com/user-home-943939.htm

*本文为看雪论坛优秀文章,由 孤岛林潭 原创,转载请注明来自看雪社区

# 往期推荐

1、无限硬件中断的代码实现

2、x32TLS回调函数实验

3、metasploit浅析

4、FastBin Attack:House of spirit attack

5、SOCIALNETWORK打靶记录

6、Fart 源码攻略笔记

球分享

球点赞

球在看


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458505874&idx=1&sn=98b4f7f00077a794aa32be7202e4daaa&chksm=b18ee21886f96b0ea5df955e4a726225e6d22849288d9c9696a0301bbccbdf9eb0e177e3b99d#rd
如有侵权请联系:admin#unsafe.sh