10. Hook技术原理与进程隐藏实现

1 hook

Hook翻译过来为‘挂钩’、‘钩子’,是一种非常形象的叫法,它的原理并不难懂:在合适的地点挂上一个钩子,等待目标进入。

将其比喻为现实中的例子,可以想象有一条河流,河流自上而下流动可以看作程序的执行过程,而Hook就是在河流的某个地方布上一张大网。

河流想要流下去,那就必须要通过这张网,而我们在其通过的这一瞬间完成一系列巧妙的事情,比如修改其原本的运行流程、变量的值等等。

这只是一种形象的比喻,实际上Hook远比河流中的一张网更加强大。

事实上一个简单的C++程序就能演示这一过程:

#include<iostream>
using namespace std;

void fun(int t) {
	cout << t << endl;
}
int main()
{
	int a = 10;
	fun(a);
}

上面的程序就演示了一个最基本的运行逻辑,代码从main函数开始,向fun函数传入变量a,然后进入函数中打印这个a的值。

此时如果有一张网,也就是Hook代码,插在了函数调用前,去修改了a的值:

int a=10;
{ //假设有一段hook代码插入在了这里
    a=20;
}
fun(a);

那么此时fun函数打印出来的值自然也会跟着变化。

虽然看起来似乎很简单,但这确实就是Hook的底层原理,唯一的难点只是我们应该如何去插入这段我们自己写的代码而已。

一个更具实际意义的简单例子就是C++中的虚函数表,这在前面的逆向分析技术中也已经提到过。

由于它是动态查表来寻找到要调用的函数地址,所以如果此时你写一个与某个虚函数签名一致的函数,并将其地址复制到那张虚函数表的相应位置覆盖掉原本的函数地址,那么程序自然就会来执行你写的这个函数。

如果放在汇编语言中,其实就更简单了,因为汇编语言执行流程其实就是从上向下:

1
2
3

想要hook,直接在其必经之路上,执行一个jmp之类的跳转指令,让其跳转到自己的代码中去执行,执行完后,再让其跳转回来:

1
2
jmp 0x100000 #假设跳转到0x100000处
3 

.... 0x100000
#执行一系列自定义的代码
jmp 3  #然后跳转回去继续执行

此时,只需要把我们想要让其执行的代码复制到0x100000处,就自然会被执行,执行完后再让其跳转回去,也不影响程序的正常工作。

使用到的函数大致就是win api读写、分配内存的函数,过程大致如下:

  1. 在本进程中通过OpenProcress函数打开想要Hook的程序
  2. 使用VirutalAllocEx在目标进程中分配一块空间。
  3. WriteProcessMemory函数在分配的空间中写上我们想要让其执行的代码,以及在哪里进行跳转等等
  4. 一般还需要用ReadProcessMemory函数读取、保留其原本位置的数据,用于后面的恢复
  5. 可能还需要用VirutalProtectEx函数更改目标内存的权限,使其允许我们读写目标进程的内存。

但这是一个比较繁琐的过程,操作的数据也都是二进制,代码中也就是十六进制的数据。

作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux