[原创]HOOK的原理分析与easy_hook逆向题分析
2023-5-26 15:6:11 Author: bbs.pediy.com(查看原文) 阅读量:15 收藏

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

HOOK是一种截取信息、更改程序执行流向、添加新功能的强大技术。通过在执行真正的目标函数之前执行实现插入的代码,获取程序执行过程的决定权。
二次开发或补丁 信息截获 安全防护 HOOK使用最多的战场,各类安全软件都会在应用层和内核中安装hook、系统回调、过滤驱动等。从而对系统中所有的进/线程、窗口、文件、网络操作等进行检查、过滤和拦截,在x86系统中,流行在KiFastCallEntry中进行HOOK,以达到间接hook所有系统调用的目的,同时在应用层中通常也会有一些安全模块,通过安装一些应用层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技术时需要注意安全性和稳定性问题。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

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);

}

(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函数的弹窗显示出来
图片描述

步骤一,拉进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
图片描述


文章来源: https://bbs.pediy.com/thread-277375.htm
如有侵权请联系:admin#unsafe.sh