目录

Hook

常用hook方式

JMP指令型hook

RET指令型hook

Detours库hook

JMP与CALL 公式

目标地址 = 当前指令地址 +指令长度 + 偏移量

偏移量 = 目标地址 - 当前指令地址 - 指令长度

1
2
3
4
5
776101EC	E9	C9950200
// 776101EC为当前地址
// E9 为JMP
// C9950200 为偏移 逆序
// 776397BA = 776101EC + 5 + 000295C9 

跨进程hook

1
2
3
4
5
6
//跨进程hook 动态打补丁
OpenProcess	//打开进程 获取进程权限的句柄
WriteProcessMemory	//跨进程写入进程
VirtualAllocEx	//跨进程分配内存
VirtualProtectEx	//修改内存页面属性
JMP 跳转指令公式	目标偏移 = 目标地址 - 5 - 当前指令地址	//5是指令长度 (近跳为2)
 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
#include <iostream>
#include <Windows.h>

BYTE gbuf_0040100A[] = { 0xE9, 0x77, 0x08, 0x00, 0x00, 0x90 };
BYTE gbuf_00401886[] = {
	0x53, 0x51, 0x8B, 0xD8, 0x8B, 0x45, 0x08, 0x8B, 0x4D, 0x0C, 0x0F, 0xAF, 0xC1, 0x8B, 0xCB, 0x03,
	0xC8, 0x90, 0x89, 0x4D, 0xFC, 0x59, 0x5B, 0x8B, 0x4D, 0xFC, 0xE9, 0x6B, 0xF7, 0xFF, 0xFF, 0x00 };

int main()
{
    std::cout << "Hello World!\n";
	//获取目标进程句柄
	DWORD pid = 0, oldProtect1 = 0, oldProtect2=0;
	printf("请手动输入进程pid");
	scanf_s("%d", pid);
	HANDLE hp = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);

	//更改页面属性为 PAGE_EXECUTE_READWRITE
	VirtualProtectEx(hp, (PVOID)0x0040100A, 32, PAGE_EXECUTE_READWRITE, &oldProtect1);
	VirtualProtectEx(hp, (PVOID)0x00401886, 32, PAGE_EXECUTE_READWRITE, &oldProtect2);

	//写入hook代码
	//memcpy_s((BYTE*)0x0040100A,6, gbuf_0040100A,6);//本地hook
	//memcpy_s((BYTE*)0x00401886,32, gbuf_00401886, 32);
	WriteProcessMemory(hp, (BYTE*)0x0040100A, gbuf_0040100A, 6,&pid);
	WriteProcessMemory(hp, (BYTE*)0x00401886, gbuf_00401886, 32, &pid);

	//恢复原页面属性
	VirtualProtectEx(hp, (PVOID)0x0040100A, 32, oldProtect1, &oldProtect1);
	VirtualProtectEx(hp, (PVOID)0x00401886, 32, oldProtect2, &oldProtect2);
}
1
2
3
4
//DLL HOOK补丁	//更完美一些的可以考虑 劫持一个DLL
VirtualProtect	//修改内存页面属性
指针操作 写入代码即可
写完可以考虑恢复内存页面属性

DLL版 动态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
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <stdio.h>
#include <Windows.h>
int myAdd(int a, int b)
{
	int num = a + b + a * b;
	printf("%d+%d+%d*%d=%d \r\n", a, b, a, b, num);
	return num;
}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
	{
		//写入4字节 偏移 call 12345678 偏移量 = 目标地址 - 当前指令地址 - 指令长度 
		//0006121C E8 57442E12  //call 12345678
		DWORD 目标地址 = (DWORD)myAdd;
		DWORD 偏移 = 目标地址 - 5 - 0x0006121C;

		//向0x0006121D 4字节偏移
		//更改页面属性为 PAGE_EXECUTE_READWRITE
		DWORD oldProtect1 = 0;
		BOOL br = VirtualProtect((PVOID)0x0006121D, 8, PAGE_EXECUTE_READWRITE, &oldProtect1);
		//8 为4字节

		//写入偏移
		DWORD *poffset = (DWORD*)0x0006121D;
		*poffset = 偏移;
		poffset[0] = 偏移;

		//恢复原页面属性
		br = VirtualProtect((PVOID)0x0006121D, 8, oldProtect1, &oldProtect1);
		break;
	}
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

MFC DLL 动态HOOK与UNHOOK

显示到编辑窗口中

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/image-20211209221525688.png

hook.cpp

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include "pch.h"
#include "HOOK.h"
#include <stdio.h>
#include <Windows.h>
#include "CPAGE1.h"
extern CPAGE1 pg1;
DWORD baseAdress1 = 0x009C121C;
DWORD baseAdress2 = baseAdress1 + 1;

int myAdd(int a, int b)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	DWORD tid = GetCurrentThreadId();
	printf("MyAdd所在线程tid=%d \r\n", tid);
	CString strtmp;
	strtmp.Format("a=%d,b=%d \r\n", a, b);
	pg1.m_edt_str += strtmp; //注意多线程冲突
	
	pg1.SendMessage(WM_USER + 2, 222, 333);	//变量同步到窗口 需要重写WindowProc
	int num = a + b + a * b;
	printf("%d+%d+%d*%d=%d \r\n", a, b, a, b, num);
	return num;
}

void hook()
{
	//写入4字节 偏移 call 12345678 偏移量 = 目标地址 - 当前指令地址 - 指令长度 
	//0084121C E8 57442E12  //call 12345678
	DWORD 目标地址 = (DWORD)myAdd;
	DWORD 偏移 = 目标地址 - 5 - baseAdress1;

	//向0x0084121D 4字节偏移
	//更改页面属性为 PAGE_EXECUTE_READWRITE
	DWORD oldProtect1 = 0;
	BOOL br = VirtualProtect((PVOID)baseAdress2, 8, PAGE_EXECUTE_READWRITE, &oldProtect1);
	//8 为4字节

	//写入偏移
	DWORD *poffset = (DWORD*)baseAdress2;
	*poffset = 偏移;
	poffset[0] = 偏移;

	//恢复原页面属性
	br = VirtualProtect((PVOID)baseAdress2, 8, oldProtect1, &oldProtect1);

	return;
}
void unhook()
{

	//向0x0084121D 4字节偏移
	//更改页面属性为 PAGE_EXECUTE_READWRITE
	DWORD oldProtect1 = 0;
	BOOL br = VirtualProtect((PVOID)baseAdress2, 8, PAGE_EXECUTE_READWRITE, &oldProtect1);
	//8 为4字节

	//写入偏移
	DWORD *poffset = (DWORD*)baseAdress2;
	*poffset = 0xFFFFFF5F;

	//恢复原页面属性
	br = VirtualProtect((PVOID)baseAdress2, 8, oldProtect1, &oldProtect1);
	return;

}

hook.h

1
2
3
4
5
6
7
#pragma once
class HOOK
{
};

void hook();
void unhook();

主程序源文件 MFC_HOOK_DLL.cpp 关键部分

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 唯一的 CMFCHOOKDLLApp 对象

CMFCHOOKDLLApp theApp;

#include "CPAGE1.h"
CPAGE1 pg1;
// CMFCHOOKDLLApp 初始化
DWORD WINAPI showDlg(PVOID arglist)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	DWORD tid = GetCurrentThreadId();
	printf("showDlg所在线程tid=%d \r\n", tid);
	pg1.DoModal();	//显示窗口
	return 1;
}
BOOL CMFCHOOKDLLApp::InitInstance()
{
	CWinApp::InitInstance();
	//写HOOK 显示窗口
	::CreateThread(0, 0, showDlg, 0, 0, 0);
	return TRUE;
}

Dlg源文件 CPAGE1.cpp 关键部分

 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
// CPAGE1 消息处理程序

#include "HOOK.h"
void CPAGE1::OnBnClickedButton1Hook()
{
	hook();
	UpdateData(FALSE);	//变量同步到窗口
	// TODO: 在此添加控件通知处理程序代码
}


void CPAGE1::OnBnClickedButton2Unhook()
{
	unhook();
	UpdateData(FALSE);	//变量同步到窗口
	// TODO: 在此添加控件通知处理程序代码
}

//pg1.SendMessage(WM_USER + 2, 222, 333);
LRESULT CPAGE1::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	// TODO: 在此添加专用代码和/或调用基类
	if (message==WM_USER+2 && wParam ==222 && lParam ==333)
	{
		UpdateData(FALSE);	//更新变量到窗口
		DWORD tid = GetCurrentThreadId();
		printf("WindowProc所在线程tid=%d \r\n", tid);
	}
	return CDialogEx::WindowProc(message, wParam, lParam);
}

CPAGE1.h

 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
#pragma once


// CPAGE1 对话框

class CPAGE1 : public CDialogEx
{
	DECLARE_DYNAMIC(CPAGE1)

public:
	CPAGE1(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CPAGE1();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG1 };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedButton1Hook();
	afx_msg void OnBnClickedButton2Unhook();
	afx_msg void OnEnChangeEdit1();
	// EDIT1 m_edt_str
	CString m_edt_str;
	virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
};

动态hook游戏组包

hook.cpp

 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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include "pch.h"
#include "HOOK.h"
#include <stdio.h>
#include <Windows.h>
#include "CPAGE1.h"
extern CPAGE1 pg1;
DWORD baseAdress1 = 0x009C121C;
DWORD baseAdress2 = baseAdress1 + 1;

int myAdd(int a, int b)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	DWORD tid = GetCurrentThreadId();
	printf("MyAdd所在线程tid=%d \r\n", tid);
	CString strtmp;
	strtmp.Format("a=%d,b=%d \r\n", a, b);
	pg1.m_edt_str += strtmp; //注意多线程冲突
	
	pg1.SendMessage(WM_USER + 2, 222, 333);	//变量同步到窗口 需要重写WindowProc
	int num = a + b + a * b;
	printf("%d+%d+%d*%d=%d \r\n", a, b, a, b, num);
	return num;
}

void 取数据函数()
{

}


UINT_PTR send_off5 = ((UINT_PTR)(LoadLibraryA("Gane.exe"))) + 0x351E75;
//裸汇编
__declspec(naked)void hookSend()
{
	//取出想要的数据
	//还原指令
	//跳转到Game.exe+351E75
	__asm
	{
		call 取数据函数
		//还原代码
		push ebp
		mov ebp, esp
		push - 1
		jmp send_off5
	}
}

BYTE jmpCode[5] = { 0xE9,0,0,0,0 };	//偏移=目标地址-5-当前地址



void hook()
{
	//写入4字节 偏移 call 12345678 偏移量 = 目标地址 - 当前指令地址 - 指令长度 
	//0084121C E8 57442E12  //call 12345678
	DWORD HOOK地址 = send_off5 - 5;	//当前指令地址
	DWORD 目标地址 = (DWORD)hookSend;
	DWORD 偏移 = 目标地址 - 5 - HOOK地址;
	//组装jmpCode
	DWORD*pJMP = (DWORD*)(jmpCode + 1);
	pJMP[0] = 偏移;

	//向0x0084121D 4字节偏移
	//更改页面属性为 PAGE_EXECUTE_READWRITE
	DWORD oldProtect1 = 0;
	BOOL br = VirtualProtect((PVOID)HOOK地址, 8, PAGE_EXECUTE_READWRITE, &oldProtect1);
	//8 为4字节

	//写入5字节jmpCode
	memcpy((char*)HOOK地址, jmpCode, 5);

	//恢复原页面属性
	br = VirtualProtect((PVOID)baseAdress2, 8, oldProtect1, &oldProtect1);

	return;
}
void unhook()
{

	//向0x0084121D 4字节偏移
	//更改页面属性为 PAGE_EXECUTE_READWRITE
	DWORD oldProtect1 = 0;
	BOOL br = VirtualProtect((PVOID)baseAdress2, 8, PAGE_EXECUTE_READWRITE, &oldProtect1);
	//8 为4字节

	//写入偏移
	DWORD *poffset = (DWORD*)baseAdress2;
	*poffset = 0xFFFFFF5F;

	//恢复原页面属性
	br = VirtualProtect((PVOID)baseAdress2, 8, oldProtect1, &oldProtect1);
	return;

}