May 10, 2023

hook技术学习

更新ing

inline hook

核心思路就是修改函数里的代码

hook库函数思路

  1. 调用GetModuleHandle获取模块基址
  2. 调用GetProcAddress获取要hook的库函数基址
  3. 调用VirtualProtect修改内存属性
  4. 计算重定位跳转地址并jmp
  5. 计算跳转回来的地址

练习

x86

自己写一个程序,向文件中写入字符串:

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
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>

int __cdecl _tmain(int argc, TCHAR* argv[])
{
HANDLE hFile;
TCHAR s[] = _T("./t.txt");
char w[] = "hhhhhhh";
int i = 0;
DWORD dwBytesToWrite = (DWORD)strlen(w);
DWORD dwBytesWritten = 0;
hFile = CreateFile(s, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile == INVALID_HANDLE_VALUE)
{
puts("open file error");
return 0;
}
getchar();
//while(TRUE)
WriteFile(hFile, w, dwBytesToWrite, &dwBytesWritten, NULL);

return 0;
}
函数绕过

编写一个DLL,将WriteFile函数hook掉,让程序运行结束后t.txt中没有内容

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
#include "pch.h"
#include <Windows.h>
#include <stdio.h>

DWORD jump = 0;


bool APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{
HMODULE hwnd = GetModuleHandle(TEXT("kernelbase.dll"));
DWORD base = (DWORD)GetProcAddress(hwnd, "WriteFile");
DWORD oldProtect = 0;

if (VirtualProtect((LPVOID)base, 5, PAGE_EXECUTE_READWRITE, &oldProtect))
{
jump = 136;
__asm {
mov eax, base
mov byte ptr[eax], 0xe9
inc eax
mov ebx, jump
mov dword ptr[eax], ebx
}
VirtualProtect((LPVOID)base, 5, oldProtect, &oldProtect);
}
return true;
}

使用DLL注入工具注入DLL,运行后发现t.txt中并未写入字符串,且程序正常退出(呃呃呃呃呃呃呃试了好久x),测试成功。

注意一下jmp的时候应该填地址还是偏移。
此处记录一下硬编码(被坑N年系列):

1
2
3
4
5
6
0xFF15 CALL 后面的四字节是存放地址的地址
0xE8 CALL 后面四个字节是地址
0xFF25 JMP 后面的四字节是存放地址的地址
0xE9 JMP 后面四个字节是偏移
0x68 PUSH 后面的四个字节入栈
0x6A PUSH 后面的一个字节入栈
更改参数

根据想要更改的参数所在位置(栈或寄存器上)写汇编就彳亍
这里我更改写入的字符串,这个参数在栈上。编写以下代码:

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
#include "pch.h"
#include <Windows.h>
#include <stdio.h>

DWORD jump = 0;
const char* string = "gggggg" ;
DWORD s = (DWORD)string;
__declspec(naked) bool _stdcall Transfer(HWND hwnd, LPCSTR lpString) {

__asm {
mov eax, esp
add eax, 8
mov ebx, s
mov dword ptr[eax], ebx
push 0x18
push 0x75c11e38
mov ebx, jump
jmp ebx
}
}

bool APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{
HMODULE hwnd = GetModuleHandle(TEXT("kernelbase.dll"));
DWORD base = (DWORD)GetProcAddress(hwnd, "WriteFile");
DWORD oldProtect = 0;

if (VirtualProtect((LPVOID)base, 7, PAGE_EXECUTE_READWRITE, &oldProtect))
{
DWORD value = (DWORD)Transfer - base - 5;
jump = base + 7;
__asm {
mov eax, base
mov byte ptr[eax], 0xe9
inc eax
mov ebx, value
mov dword ptr[eax], ebx
}
VirtualProtect((LPVOID)base, 7, oldProtect, &oldProtect);
}
return true;
}

因为jump指令覆盖了两条指令,而这两条指令的长度为7,所以在我自定义的函数中先执行被更改的指令,然后更改我需要的数据(栈上的数据),最后再跳转到这两条指令之后的指令正常执行。
注意直接写mov dword ptr[eax], s会报错,需要一个寄存器中转。

哦,所以你还不滚去学汇编语法?
😭😭😭

听说DLL注入杀毒软件会报毒,因为杀软会把一切不是系统DLL的dll划分为不可信的病毒DLL,但是👴8想在自己电脑上下杀软,于是👴用sandbox浅试了一下

step1
先去下了360,但是运行的时候给👴疯狂报错Windows cannot access the specified device, path, or file
👴试了微软提供的各种解决方案都8彳亍,于是👴放弃了

🌶️🐔360!

step2
又去下了金山毒霸,一次性成功

🌶️🐔360给👴爪巴!

step3
运行金山毒霸,打开程序,注入DLL,然后无事发生

👴:?

step4
试了一下全盘扫描,扫了十几分钟(也可能没有,反正扫了好久),依然无事发生

👴:?
👴:🌶️🐔金山给👴爪巴!

算了,扫不出来拉倒,自己看看得了

x64

x64无法编译内嵌汇编代码 芥末🌶️🐔 所以只能手动把汇编硬编码
。。。找不到64位DLL注入的工具,就8写DLL了😶直接开勾(

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
49
50
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>

#define HOOK_LEN 0xC
void HookWriteFile();
BYTE Ori_Code[HOOK_LEN] = { 0x00 };
BYTE HookCode[HOOK_LEN] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
/*
MOV RAX, 0x9090909090909090
JMP RAX
*/
DWORD dwBytesWritten = 0;
static int (WINAPI* OldWriteFile)(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) = WriteFile;
int WINAPI MyWriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
memcpy(OldWriteFile, &Ori_Code, sizeof(HookCode)); // 恢复hook原始代码
int ret = OldWriteFile(hFile, "gggggg", 6, &dwBytesWritten, NULL); // 调用原函数
HookWriteFile(); // 继续hook
return ret;
}

VOID HookWriteFile()
{
DWORD OldProtect;
if (VirtualProtect(OldWriteFile, HOOK_LEN, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy(Ori_Code, OldWriteFile, HOOK_LEN); // 拷贝原始机器码指令
*(PINT64)(HookCode + 2) = (INT64)&MyWriteFile; // 填充90为指定跳转地址
}
memcpy(OldWriteFile, &HookCode, sizeof(HookCode)); // 拷贝Hook机器指令
}
int main()
{
HookWriteFile();

HANDLE hFile;
TCHAR s[] = _T("./t.txt");

hFile = CreateFile(s, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile == INVALID_HANDLE_VALUE)
{
puts("open file error");
return 0;
}

WriteFile(hFile, "hhhhhh", 6, &dwBytesWritten, NULL);
return 0;
}

没有static int (WINAPI* OldWriteFile)(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) = WriteFile;这一句,直接用VirtualProtect改权限的话也是不行的,估计是因为DLL的页还没有换进去所以发生页错误了……
还有我刚开始尝试直接把汇编硬编码写进数据,然后用VirtualProtect赋予执行权限,但不知道为什么不行。。。暂且不知道是什么原因。。。
总而言之就是笨人的所有尝试均以失败告终
于是我屈服了,只能直接硬改DLL,然后在外部用函数调用,改参数重新执行 (没有x86那样自己写汇编想改什么就改什么的感觉真不爽
简而言之先把库函数开头改成跳转,然后在跳转函数里把库函数开头改回来,再在跳转函数里重新调用库函数(这时候就可以自己随便定义参数了)

在dr3神的帮助下,👴知道了process hacker可以注入64位的DLL
但是👴已经懒得写了,所以就这样吧

参考链接:
https://www.cnblogs.com/LyShark/p/11692436.html
https://www.cnblogs.com/ibinary/p/11334793.html

关于本文

本文作者 云之君, 许可由 CC BY-NC 4.0.