[TOC]

权限

权限管理

image-1

UAC机制

管理方式

在管理员账户下,运行一个程序,也是低权限,只有选择管理员方式运行,才会分配高权限。

image-2

调整UAC

方法1:gpedit

image-3

方法2 :设置

image-4

使用案例

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
BOOL CMy01UACDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();


// 给按钮加小盾牌
// 3. 判断具体的权限状况


// 1. 获得本进程的令牌
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return false;
// 2. 获取提升类型
TOKEN_ELEVATION_TYPE ElevationType = TokenElevationTypeDefault;
BOOL bIsAdmin = false;
DWORD dwSize = 0;
if (GetTokenInformation(hToken, TokenElevationType, &ElevationType,
sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) {
// 2.1 创建管理员组的对应SID
BYTE adminSID[SECURITY_MAX_SID_SIZE];
dwSize = sizeof(adminSID);
CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize);
// 2.2 判断当前进程运行用户角色是否为管理员
if (ElevationType == TokenElevationTypeLimited) {
// a. 获取连接令牌的句柄
HANDLE hUnfilteredToken = NULL;
GetTokenInformation(hToken, TokenLinkedToken, (PVOID)&hUnfilteredToken,
sizeof(HANDLE), &dwSize);
// b. 检查这个原始的令牌是否包含管理员的SID
if (!CheckTokenMembership(hUnfilteredToken, &adminSID, &bIsAdmin))
return false;
CloseHandle(hUnfilteredToken);
}
else {
bIsAdmin = IsUserAnAdmin();
}
CloseHandle(hToken);
}
BOOL bFullToken = false;
switch (ElevationType) {
case TokenElevationTypeDefault: /* 默认的用户或UAC被禁用 */
if (IsUserAnAdmin()) bFullToken = true; // 默认用户有管理员权限
else bFullToken = false;// 默认用户不是管理员组
break;
case TokenElevationTypeFull: /* 已经成功提高进程权限 */
if (IsUserAnAdmin()) bFullToken = true; //当前以管理员权限运行
else bFullToken = false;//当前未以管理员权限运行
break;
case TokenElevationTypeLimited: /* 进程在以有限的权限运行 */
if (bIsAdmin) bFullToken = false;//用户有管理员权限,但进程权限有限
else bFullToken = false;//用户不是管理员组,且进程权限有限
}
// 4. 根据权限的不同控制按钮的显示
if (!bFullToken)
Button_SetElevationRequiredState(::GetDlgItem(m_hWnd, IDC_BUTTON1),!bFullToken);
else
::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON1), SW_HIDE);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}


void CMy01UACDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
//以UAC的方式,重新启动程序
// 1. 获得本进程的令牌

// 1. 隐藏当前窗口
ShowWindow(SW_HIDE);
// 2. 获取当前程序路径
WCHAR szApplication[MAX_PATH] = { 0 };
DWORD cchLength = _countof(szApplication);
QueryFullProcessImageName(GetCurrentProcess(), 0,
szApplication, &cchLength);
// 3. 以管理员权限重新打开进程
SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) };
sei.lpVerb = L"runas"; // 请求提升权限
sei.lpFile = szApplication; // 可执行文件路径
sei.lpParameters = NULL; // 不需要参数
sei.nShow = SW_SHOWNORMAL; // 正常显示窗口
if (ShellExecuteEx(&sei))
exit(0);
else
ShowWindow( SW_SHOWNORMAL);
}


void CMy01UACDlg::OnBnClickedButton2()
{
EnableDebugPrivilege(TRUE);
}

BOOL CMy01UACDlg::EnableDebugPrivilege(BOOL fEnable) { //提升为调试权限
BOOL fOk = FALSE; HANDLE hToken;
// 以修改权限的方式,打开进程的令牌
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES,
&hToken)) {
// 令牌权限结构体
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
//获得LUID
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); //修改权限
fOk = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return(fOk);
}

内存管理

Windows系统使用虚拟内存的好处是什么

a) 无论物理内存实际有多大,每一个进程都有4GB的虚拟地址空间。
b) 每一个进程在虚拟地址空间的使用上都是相似的,低2GB是用户空间,高2GB是系统空间,低2GB的用户代码空间的代码无法访问高2GB系统空间。
c) 在进程中使用的全部都是虚拟地址,具体虚拟地址到物理地址的转换由操作系统内核完成,故而你无法在自己的进程中访问到其他进程的内存,虽然大家的地址长得如此类似。
d) 一个进程的虚拟空间只有使用一部分与物理内存有映射关系,并且windows尽量保证对于不同进程的同一份数据,在物理内存中只有一份,分别映射到多个进程中。从而节约内存。
e) 当各个进程所使用的内存数量超出物理内存的时候,操作系统还能够将物理内存中暂时用不到的数据交换到硬盘中。

堆的管理

API 说明
HeapCreate 在进程中创建一个堆,返回一个堆句柄
GetProcessHeap 获取当前进程中的一个堆,返回一个句柄
GetProcessHeaps 获取进程中的所用堆,堆的数量和堆的各个句柄
HeapAlloc 从指定的堆上分配块
HeapReAlloc 重新分配内存,改变已经分配好的堆内存块大小
GetSystemInfo 获取系统信息
HeapSize 获取指定堆的大小
HeapFree 释放HeapAlloc和HeapReAlloc申请的内存
HeapDestroy 销毁由HeapCreate创建的堆
CreateToolhelp32Snapshot 可以分别创建进程、线程、进程模块、进程堆的快照
Heap32First 用来首次调用,获得第一个堆对象的信息
Heap32Next 以后的调用由他来完成,不断的获取堆对象信息

基本使用

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
void Test()
{
//我们不创建堆,进程也会有默认的堆
HANDLE hHeap = GetProcessHeap(); // 获取默认堆
SYSTEM_INFO si; //系统信息
GetSystemInfo(&si); // 获取系统信息
//在堆上分配3个页面大小的内存
LPVOID lpMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, si.dwPageSize * 3);
HeapFree(hHeap, 0, lpMem);
//默认堆,不应该去销毁它
//HeapDestroy( hHeap );
}



int main()
{
Test();
//1 创建一个堆
HANDLE hHeap = HeapCreate(0, 0, 0);
SYSTEM_INFO si; //系统信息
GetSystemInfo(&si); // 获取系统信息,主要是为了获取内存分页大小
//在堆上分配3个页面大小的内存
LPVOID lpMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, si.dwPageSize * 3);
//。。。。。
//释放堆内存
HeapFree(hHeap, 0, lpMem);
//销毁堆,这个堆,没有任何需要使用的内存了,就销毁
HeapDestroy(hHeap);

}

使用场景:

一般情况下,咱么都是使用malloc或者new。如果有以下场景可以尝试使用堆,假如咱们的程序需要大量的去申请小块的内存,管理这些地址,就会比较麻烦,此时就可以使用自己创建的堆,在用完了内存之后,去直接销毁堆,此时内存就自动释放了,也就免去了挨个去释放的麻烦。

遍历堆

和遍历进程,线程一致的,使用快照去遍历即可。

虚拟内存管理函数

作用 函数名 说明
分配 VirtualAlloc 分配或预定一块虚拟内存
VirtualAllocEx 可以在其他进程分配或预定一块虚拟内存
释放 VirtualFree 将一块虚拟内存释放
VirtualFreeEX 可以将释放其他进程的内存
锁定与解锁 VirtualLook 可以将内存锁定,不能交换数据到硬盘
VirtualUnlook 为内存解锁
修改保护属性 VirtualProtect 修改一块虚拟内存的属性
VirtualProtectEx 可以修改其他内存的属性
读写其他进程内存 ReadProcessMemory 读写远程进程的内存数据
WriteProcessMemory 将数据写入远程进程内存
查询内存状态 VirtualQuery 查询内存状态
VirtualQueryEx 可以查询其他内存状态

安全属性

属性 描述
PAGE_NOACCESS 0x01 不可访问
PAGE_READONLY 0x02 只读
PAGE_READWRITE 0x04 可读可写
PAGE_WRITECOPY 0x08 可写可读
PAGE_EXECUTE 0x10 可执行
PAGE_EXECUTE_READ 0x20 可读可执行
PAGE_EXECUTE_READWRITE 0x40 可读可写可执行
PAGE_EXECUTE_WRITECOPY 0x80 可执行,写时复制
PAGE_GUARD 0x100
PAGE_NOCACHE 0x200
PAGE_WRITECOMBINE 0x400
PAGE_GRAPHICS_NOACCESS 0x0800
PAGE_GRAPHICS_READONLY 0x1000
PAGE_GRAPHICS_READWRITE 0x2000
PAGE_GRAPHICS_EXECUTE 0x4000
PAGE_GRAPHICS_EXECUTE_READ 0x8000
PAGE_GRAPHICS_EXECUTE_READWRITE 0x10000
PAGE_GRAPHICS_COHERENT 0x20000
PAGE_ENCLAVE_THREAD_CONTROL 0x80000000
PAGE_REVERT_TO_FILE_MAP 0x80000000
PAGE_TARGETS_NO_UPDATE 0x40000000
PAGE_TARGETS_INVALID 0x40000000
PAGE_ENCLAVE_UNVALIDATED 0x20000000
PAGE_ENCLAVE_DECOMMIT 0x10000000

申请和释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
//1 申请
LPVOID lpvResult = VirtualAlloc(
NULL,
4097, // 4KB * 1
MEM_RESERVE | MEM_COMMIT, // 预定并调拨内存
PAGE_READWRITE); // 可读写
//使用
wcscpy_s((LPWSTR)lpvResult, wcslen(L"Hello!")+2, L"World!");
MessageBox(NULL, (LPWSTR)lpvResult, NULL, MB_OK);
//释放
VirtualFree(lpvResult, 1, MEM_RELEASE);
}

修改保护属性

1
2
3
4
5
6
7
8
9
10
int main()
{
char* p = (char*)"hello World";
DWORD dwNewProtect = PAGE_READWRITE;
DWORD dwOldProtect = 0;
VirtualProtect(p, 1, dwNewProtect, &dwOldProtect);
p[0] = 'm';
p[1] = 'k';
p[2] = 'm';
}

文件映射

可以将文件直接映射进内存的一种技术。

API 说明
GetSystemInfo 获取系统信信息,用于确定分配粒度
CreateFileMapping 创建一个mapping对象
OpenFileMapping 打开已命名的mapping对象(可跨进程)
UnmapViewOfFile 取消文件映射
MapViewOfFile 将mapping对象的文件映射到内存
FlushViewOfFile 将映射到内存的文件写回到硬盘

有两个作用:

1 操作文件比较方便

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
int main()
{
//1. 打开文件,得到一个文件句柄
HANDLE hFile = CreateFile(
L"D:\\test\\123.txt",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0
);
//2. 创建一个文件映射对象,和文件句柄关联如果,要超过文件大小的话
// 创建的时候,就需要写一个大小。
//
HANDLE hFileMapping =
CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 100, 0);
//3. 将文件映射到内存
char *p = (char*)MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0,0, 0);

//4. 读取或者写入
strcpy_s(p, 100,"15pb hello 15pb hello");

//5. 文件映射的写入,会自动刷,也可以手动刷
FlushViewOfFile(p, 0);
//6. 关闭句柄
CloseHandle(hFileMapping);
CloseHandle(hFile);

}

2 文件映射是一个内核对象,可以在多进程中去访问,所以文件映射也是一种进程间通讯机制。

进程间通讯

通讯A端

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
// 06_进程间通讯.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>
int main()
{
//1 创建文件映射
HANDLE hFileMapping =
CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL, PAGE_READWRITE,
0,
100,
L"hello 15pb"//起了名字,别的进程才能打开它
);
//2 映射内存
char* pBuf = (char*)MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 16);

pBuf[0] = 1;

//3 时刻检测,第一个字节是否被修改了
while (pBuf[0] == 1)
{

}
//4 如果被修改了,那么就结束
printf("数据已经被更改");
CloseHandle(hFileMapping);
}

通讯B端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <Windows.h>
int main()
{
// 1. 打开文件Mapping
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,
FALSE, L"hello 15pb");
//2. 映射到内存
char* pBuf = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS,
0, 0, 0);
//3. 修改数据
pBuf[0] = 0;
//4. 通讯完毕,退出
printf("数据已经更改完毕了");
CloseHandle(hMapFile);
}

虚拟内存的遍历

1
2
3
4
5
6
SIZE_T WINAPI VirtualQueryEx(
_In_ HANDLE hProcess, // 进程句柄
_In_opt_ LPCVOID lpAddress, // 查询地址
_Out_ PMEMORY_BASIC_INFORMATION lpBuffer,
_In_ SIZE_T dwLength // 传出结构体的大小
);
1
2
3
4
5
6
7
8
9
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress; // 将参数向下取整到页面大小
PVOID AllocationBase; // 区域地址,此区域包含传入地址
DWORD AllocationProtect; // 此区域在预定时的保护属性
SIZE_T RegionSize; // 区域的大小
DWORD State; // 区域的页面状态[注1]
DWORD Protect; // 页面保护属性
DWORD Type; // 页面类型[注2]
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

页面的状态

状态 说明
空闲的(Free) 0x00010000 进程不能访问这种页面,此页面还没有被分配。
保留的(reserve) 0x00002000 这个页面被预定了。但是还未与物理内存映射,因此这里也是不能访问的。
提交的(commit) 0x00001000 内存已经被分配了,并且也与物理存储器映射了,进程已经可以访问这里

页面类型

映射方式 描述
private 进程私有内存,不被其他进程所共享, 一般是堆,栈
mapped 从别的进程内存映射而来
image 从程序的PE映像映射而来,一般是映像的区段.

遍历代码

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
#include <Windows.h>
#include <stdio.h>
#include <TlHelp32.h>
#include <string>
using std::wstring;
void EnumProcess();
void EnumVirtualMemroy(HANDLE Process);
MEMORY_BASIC_INFORMATION VirtualMemroy;
int main()
{
EnumProcess();

return 0;
}

//遍历进程
void EnumProcess()
{
wstring buffer;

// 2. 创建一个快照用于遍历进程,参数2可以留空
HANDLE Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

// 3. 创建一个用于保存进程信息的结构体
PROCESSENTRY32 ProcessInfo = { sizeof(PROCESSENTRY32) };

// 4. 尝试遍历第一个进程的信息,成功就继续,失败就跳过
if (Process32First(Snapshot, &ProcessInfo))
{
do {
// 5.5.2 通过指定的权限获取进程句柄
HANDLE Process = OpenProcess(PROCESS_QUERY_INFORMATION,FALSE, ProcessInfo.th32ProcessID);
if (Process&& Process != INVALID_HANDLE_VALUE)
{
// 5.5.3 通过API查询到进程对应的Exe的路径,获取不到是因为权限不够
//QueryFullProcessImageName(Process, 0, ImagePath, &PathSize);
printf("%ls\n", ProcessInfo.szExeFile);
EnumVirtualMemroy(Process);
// 5.5.4 关闭句柄并打印数据
CloseHandle(Process);
}

// 6. 尝试遍历进程快照内的下一个进程
} while (Process32Next(Snapshot, &ProcessInfo));
}

}
//虚拟内存的遍历
void EnumVirtualMemroy(HANDLE Process)
{
int dwLength = sizeof(MEMORY_BASIC_INFORMATION);
LPVOID Base = NULL;
while (Base<(LPVOID)0x7fffffff)
{
VirtualQueryEx(Process, Base, &VirtualMemroy, dwLength);

Base = (LPVOID)((DWORD)Base + VirtualMemroy.RegionSize);

printf("\t类型:%d\t属性:%d\t状态:%d\t大小:%d\n",
VirtualMemroy.Type,
VirtualMemroy.AllocationProtect,
VirtualMemroy.State,
VirtualMemroy.RegionSize
);
}
}

Dll注入

注:职业黑客的必修课

重点内容)

1 什么是DLL注入??为什么要DLL注入??

在一个本来不需要加载此DLL的进程中,强行的使其加载此DLL文件。那么这个就叫做DLL注入技术。

我们在向一个进程中注入我们自己的DLL,相当于在对方的进程中加入了我们自己的代码,可以修改对方程序的功能。

游戏辅助,输入法,对于软件行为的拦截。

2 DLL注入的方式有哪些??

远程线程注入

消息钩子注入

注册表注入

APC注入

输入法注入

远程线程注入

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>

#define DLLPATH L"DLL地址"
int main()
{
//通过ID获得目标句柄
HANDLE hHandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,9740);
//先获取字符串长度
int nLength = wcslen(DLLPATH);
//在目标进程中申请一块空间
LPVOID address = VirtualAllocEx(hHandle,0, nLength*2+2,MEM_COMMIT,PAGE_READWRITE);
//将DLL名字写入到目标进程之前还需要一个参数是返回的实际写入大小
DWORD dwConst = 0;
//将DLL名字写入到目标进程空间
WriteProcessMemory(hHandle, address,DLLPATH, nLength * 2 + 2, &dwConst);
//创建远程线程
HANDLE hRhread = CreateRemoteThread(hHandle,0,0,(LPTHREAD_START_ROUTINE)LoadLibrary, address,0,0);
//等待线程执行完成
WaitForSingleObject(hRhread,-1);
//销毁在目标进程申请的空间
VirtualFreeEx(hHandle, address, nLength * 2 + 2,MEM_COMMIT);
//线程计数-1
CloseHandle(hRhread);
return 0;
}

4 关于64位的注入

32位的dll正常情况只能注入到32位程序中,注入程序也应该是32位,64位的dll正常情况只能注入到64位的程序中,注入程序也应该是64位。

消息钩子注入

什么是Hook,这个概念应该从何说起???

Hook:是钩子的意思

Hook技术主要指的是拦截程序原有的信息,数据,代码,

1 使得你有机会对拦截到的信息数据做处理。然后再交给原来的程序去使用,从而能够截获到程序的关键信息。可以查看,也可以修改。

2 能够修改程序的部分功能。

2 Hook是怎么分类的??

在windows系统下,有两类Hook:

2.1 windows消息Hook。windows提供的能够让程序员截获到所有窗口程序消息的机制。

消息Hook也是我们的一种Dll注入手段。

2.2 自定义Hook 非常普遍的Hook方式,也是我们通常意义所说的Hook。

2.2.1 修改程序的代码,使得其能够执行到Hook者提供的“善意代码”中。 inline-Hook

2.2.2 修改存储函数地址的变量,当程序从变量中获取函数地址并调用的时候,就会调用到Hook者提供的“善意代码”了。

IAT-Hook

IDT-Hook

SYSENTR-Hook

3 windows消息钩子的实现的原理以及代码

3.1 windows消息钩子的实现的原理

SetWindowsHookEx这个函数,能够实现的功能是截获 1 系统中所有的窗口程序的消息或者 2 某一个线程的窗口消息。

截获到了消息,必然是需要执行自己的代码,自己的代码需要放置在一个dll中,然后消息钩子设置成功之后,会将dll注入到目标进程,从而使得自己的回调函数能够在对方的进程中执行。

额外的知识点:窗口程序的消息是被某一个线程获取到的,哪一个线程创建了窗口,哪一个线程就能够获得此窗口的消息。此线程在创建完窗口之后,就变成了GUI线程。

1
2
3
4
5
6
HHOOK SetWindowsHookExA(
int idHook, // 要截获的是哪种类型的消息
HOOKPROC lpfn, //截获到消息之后,调用的回调函数
HINSTANCE hmod, //回调函数所在的模块,这个模块需要是一个dll。
DWORD dwThreadId //填0 截获系统中所有的窗口的消息 填线程ID 那就仅截获此线程的窗口消息
);
1
2
3
4
//当钩子使用完毕之后,卸载钩子
BOOL WINAPI UnhookWindowsHookEx(
_In_ HHOOK hhk //填充返回的句柄
);

在钩子的消息拦截函数的最后,应该调用这个函数,因为程序可能会有多个钩子,新添加的在最上面,为了不影响其他钩子的功能,需要调用这个函数。

1
2
3
4
5
6
7
8
WINUSERAPI
LRESULT
WINAPI
CallNextHookEx(
_In_opt_ HHOOK hhk, //钩子的句柄
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam);

消息钩子的代码

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
#define _countof(a) sizeof(a)/sizeof(a[0])
HHOOK g_Hook = 0;
//拦截消息的回调函数
LRESULT CALLBACK KeyboardProc(
int code, // 消息类型
WPARAM wParam, // 虚拟码
LPARAM lParam) { // 按键信息
// 判断是否wParam与lParam都有键盘消息,是的话则执行打印操作
if (code == HC_ACTION) {
// 将256个虚拟键的状态拷贝到指定的缓冲区中,如果成功则继续
BYTE KeyState[256] = { 0 };
if (GetKeyboardState(KeyState)) {
// 得到第16–23位,键盘虚拟码
LONG KeyInfo = lParam;
UINT keyCode = (KeyInfo >> 16) & 0x00ff;
WCHAR wKeyCode = 0;
ToAscii((UINT)wParam, keyCode, KeyState, (LPWORD)&wKeyCode, 0);
// 将其打印出来
WCHAR szInfo[512] = { 0 };
swprintf_s(szInfo, _countof(szInfo), L"Hook_%c", (char)wKeyCode);
OutputDebugString(szInfo);
return 0;
}
}
return CallNextHookEx(g_Hook, code, wParam, lParam);
}
//开启Hook的函数
void OnHook()
{
HMODULE hModule = GetModuleHandle(L"MessageHookdll.dll");
g_Hook = SetWindowsHookEx(
WH_KEYBOARD,
KeyboardProc,
hModule,
NULL
);
}
//关闭Hook的函数
void UnHook()
{
if (g_Hook!=0)
{
UnhookWindowsHookEx(g_Hook);
g_Hook = 0;
}
}

消息钩子使用了调试函数 OutputDebugString(szInfo);输出按键码,但我们看不到对方程序的调试信息,可以通过下图程序查看

image-5

自定义钩子:

自定义钩子两大类:内联钩子 修改存储函数地址变量的钩子

4.1 什么是内联钩子 inline-Hook

任何位置,都可以修改为jmp,使其执行到此处时,能够跳转到我们自己的代码去执行:

1 被修改的指令,是否是有用的,如果是有用的,那么你就需要在你自己的代码中,将有用的指令写一遍,使其在你代码中能够执行。

2 jmp指令一般是5个字节,所以我们选取的指令最好也是5个字节,如果不是5个字节,那么会发生指令截断,跳转回来的时候,就需要考虑跳转到完整的指令后去执行程序本身的代码。

3 jmp指令OPCODE的操作数怎么求得,也是Hook的关键知识点

jmp指令OPCODE的操作数 = 要跳转的目标地址-hook点所在的地址-5

DLL测试代码

InLine_HOOK

.h文件

1
2
3
4
#pragma once
void OnHook();
void OffHook();
void InitHook();

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

unsigned char g_NewCode[5] = { 0xE9 };
unsigned char g_OldCode[5] = { };
void OffHook();
void OnHook();
int WINAPI MyMessageBoxW(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType)
{
lpText = (wchar_t*)L"你被打劫了^_^";
DWORD dwReturnCode = 0;
OffHook();
dwReturnCode = MessageBoxW(hWnd, lpText, lpCaption, uType);
OnHook();
return dwReturnCode;
}
void InitHook()
{
//1 得到Hook的指令
DWORD dwOffset = (DWORD)MyMessageBoxW - (DWORD)MessageBoxW - 5;
//*(PDWORD)(code + 1) = dwOffset;
memcpy(g_NewCode + 1, &dwOffset, 4);
//2 得到原始的指令
memcpy(g_OldCode, MessageBoxW, 5);
}

void OnHook()
{

//1 修改Hook点的属性
DWORD dwOldProtect = 0;
VirtualProtect(MessageBoxW, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//2 修改指令
memcpy(MessageBoxW, g_NewCode,5);
//3 属性还原
VirtualProtect(MessageBoxW, 5, dwOldProtect, &dwOldProtect);
}


void OffHook()
{
//1 改属性
DWORD dwOldProtect = 0;
VirtualProtect(MessageBoxW, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//2 改指令
memcpy(MessageBoxW, g_OldCode, 5);
//3 属性还原
VirtualProtect(MessageBoxW, 5, dwOldProtect, &dwOldProtect);
}

dllmain.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
#include "framework.h"
#include "HookMessageBox.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
InitHook();
OnHook();
}
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
{
OffHook();
}
break;
}
return TRUE;
}

IAT_HOOK

.h文件

1
2
3
4
5
6
7
8
#pragma once
#include <windows.h>
void IatHook(char* lpModuleAddress, DWORD FunAddress, const char* szTarFunName, const char* szTarDllName = NULL);
int WINAPI MyMessageBoxW(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType);

.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
#include "IATHookMessageBoxW.h"
#include <Windows.h>
typedef int (WINAPI* MESSAGEBOX)(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType);
MESSAGEBOX g_OldMessageBox;

void IatHook(char* lpModuleAddress, DWORD FunAddress, const char* szTarFunName, const char* szTarDllName)
{
//1 获取到导入表结构
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpModuleAddress;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpModuleAddress);
//1 获取到导入表的数据目录结构
PIMAGE_DATA_DIRECTORY dwImportDir = &pNt->OptionalHeader.DataDirectory[1];
//1 获取到导入表结构
PIMAGE_IMPORT_DESCRIPTOR pImportTable =
(PIMAGE_IMPORT_DESCRIPTOR)(lpModuleAddress + dwImportDir->VirtualAddress);
//2 解析导入表
//2 开始解析
while (pImportTable->Name != 0)
{

char* pDllName = (pImportTable->Name + lpModuleAddress);
char* pbuf = new char[strlen(pDllName) + 1]{ 0 };
strcpy_s(pbuf, strlen(pDllName) + 1, pDllName);
_strlwr_s(pbuf, strlen(pDllName) + 1);
if (szTarDllName != NULL)
{
if (strcmp(szTarDllName, pbuf) == 0)
{
//得到目标函数的地址
HMODULE hModule = GetModuleHandleA(pDllName);
DWORD dwTarAddress = (DWORD)GetProcAddress(hModule, szTarFunName);
//在IAT中寻找这个函数地址
PIMAGE_THUNK_DATA32 pIat = (PIMAGE_THUNK_DATA32)
(pImportTable->FirstThunk + lpModuleAddress);
while (pIat->u1.Function!=0)
{
//找到了之后开始Hook,IAT也是不可写的
if (pIat->u1.Function == dwTarAddress)
{

//保存了一下MessageBox的地址。
g_OldMessageBox = (MESSAGEBOX)pIat->u1.Function;
//1 修改Hook点的属性
DWORD dwOldProtect = 0;
VirtualProtect(&pIat->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
pIat->u1.Function = FunAddress;
VirtualProtect(&pIat->u1.Function, 4, dwOldProtect, &dwOldProtect);
}
pIat++;
}
}
}
pImportTable++;
}
}
int WINAPI MyMessageBoxW(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType)
{
lpText = (wchar_t*)L"你被打劫了^_^";
DWORD dwReturnCode = 0;
dwReturnCode = g_OldMessageBox(hWnd, lpText, lpCaption, uType);
return dwReturnCode;
}

dllmain.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "framework.h"
#include "IATHookMessageBoxW.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
LPVOID ExeBase = GetModuleHandle(0);
IatHook((char*)ExeBase,(DWORD)MyMessageBoxW,"MessageBoxW","user32.dll");
}

break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

Process_Hook

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
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <TlHelp32.h>

// 通过进程名获取进程id
DWORD GetPid(const wchar_t* filePath);
// 获取helloworld程序pid
DWORD g_dwPid = GetPid(L"FileCleaner2.0.exe");
// 旧的函数地址
DWORD* g_pOldAddr = (DWORD*)OpenProcess;
// 旧的函数数据
char g_oldCode[5] = {};

void OnHook();
void UnHook();

// 编写自己的OpenProcess函数
HANDLE WINAPI MyOpenProcess(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwProcessId
)
{
// 比对pid是否与helloworld程序相等
if (dwProcessId == g_dwPid)
{
MessageBox(0,L"进程被保护",L"提示",0);
}
else
{
// 如果不相等,重新调用原来的OpenProcess函数
// 先恢复原来函数数据
UnHook();
HANDLE hProcess = OpenProcess(
dwDesiredAccess, bInheritHandle, dwProcessId);
// 调用完之后重新hook
OnHook();
return hProcess;
}
}

BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
OnHook();
MessageBox(NULL, L"注入成功", L"提示", NULL);
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

void OnHook()
{
// 1.先保存OpenProcess原来的数据
memcpy(g_oldCode, g_pOldAddr, 5);

// 2.设置新的指令,jmp
char opcode[5] = { 0xE9 };
// 2.1 计算偏移并赋值
*(DWORD*)(opcode + 1) = (DWORD)MyOpenProcess - (DWORD)OpenProcess - 5;

// 3.修改保护属性
DWORD dwOldProtect = 0;
VirtualProtect(g_pOldAddr, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

// 4.写入跳转指令
memcpy(g_pOldAddr, opcode, 5);

// 5.还原保护属性
VirtualProtect(g_pOldAddr, 5, dwOldProtect, &dwOldProtect);
}

void UnHook()
{
// 1.修改保护属性
DWORD dwOldProtect = 0;
VirtualProtect(g_pOldAddr, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 2.还原旧的指令数据
memcpy(g_pOldAddr, g_oldCode, 5);
// 3.还原保护属性
VirtualProtect(g_pOldAddr, 5, dwOldProtect, &dwOldProtect);
}

DWORD GetPid(const wchar_t* szExeName)
{
// 创建进程快照
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// 判断快照句柄是否有效
if (hSnapshot != INVALID_HANDLE_VALUE)
{
//定义进程结构体,第一个元素必须赋值为结构体大小
PROCESSENTRY32 stcPe32 = { sizeof(PROCESSENTRY32) };
//查找第一个进程
Process32First(hSnapshot, &stcPe32);
do
{ // 如果进程名一致,返回进程id
if (!wcscmp(stcPe32.szExeFile, szExeName))
return stcPe32.th32ProcessID;

//查找下一个进程
} while (Process32Next(hSnapshot, &stcPe32));
//关闭快照句柄
CloseHandle(hSnapshot);
}
return -1;
}

测试文件

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
// 注入.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <windows.h>
#include <TlHelp32.h>

#define DLLPATH "C:\\Users\\hugan\\Documents\\15PB\\代码注入与拦截\\OD注入\\x64\\Debug\\InlineHook.dll"
//#define DLLPATH "D:\\041\\test\\Debug\\IatHook.dll"

// 通过进程名获取进程id
DWORD GetPid(const wchar_t* szExeName);

int main()
{
// 1.获取目标进程句柄
// 1.1 获取指定进程id
DWORD dwPid = GetPid(L"Taskmgr.exe");
// 1.2 打开进程(注入OD需要以管理员身份运行vs,否则无法打开进程)
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
// 1.3 检测进程句柄
if (hProcess == INVALID_HANDLE_VALUE)
return -1;

// 2.1 计算dll路径长度
DWORD dwSize = strlen(DLLPATH) + 1;
// 2.在目标进程申请一段空间
LPVOID lpAddr = VirtualAllocEx(
hProcess, 0, dwSize, MEM_COMMIT, PAGE_READWRITE);

// 3.1 写入之前可以先测试加载一下看能否成功
/*HMODULE hMod = LoadLibraryA(DLLPATH);
MessageBoxW(0, 0, 0, 0);*/

// 3.将dll名字写入到目标进程空间中
// 注意:路径与函数必须匹配,A版函数配A版字符串。如果用W版,写入的时候,长度要乘以2,
// 因为wcslen求出的是字符串长度而不是字节数
if (!WriteProcessMemory(hProcess, lpAddr, DLLPATH, dwSize, NULL))
return -1;

// 4.创建远程线程
HANDLE hRtThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)LoadLibraryA,lpAddr, NULL, NULL);

// 5.等待执行结果
WaitForSingleObject(hRtThread, -1);

// 6.获取线程退出码
DWORD dwExitCode = 0;
GetExitCodeThread(hRtThread, &dwExitCode);
// 6.1 退出码也就是LoadLibrary的返回值(如果执行成功,就是dll的模块句柄)
HMODULE hModule = (HMODULE)dwExitCode;

// 7.释放空间
if (!VirtualFreeEx(hProcess, lpAddr, dwSize, MEM_DECOMMIT))
return -1;
CloseHandle(hProcess);

return 0;
}

DWORD GetPid(const wchar_t* szExeName)
{
// 创建进程快照
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// 判断快照句柄是否有效
if (hSnapshot != INVALID_HANDLE_VALUE)
{
//定义进程结构体,第一个元素必须赋值为结构体大小
PROCESSENTRY32 stcPe32 = { sizeof(PROCESSENTRY32) };
//查找第一个进程
Process32First(hSnapshot, &stcPe32);
do
{ // 如果进程名一致,返回进程id
if (!wcscmp(stcPe32.szExeFile, szExeName))
return stcPe32.th32ProcessID;

//查找下一个进程
} while (Process32Next(hSnapshot, &stcPe32));
//关闭快照句柄
CloseHandle(hSnapshot);
}
return -1;
}

CAPIHook函数封装

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#ifndef __APIHOOK_H__
#define __APIHOOK_H__
#include <windows.h>
class CAPIHook

{

public:
CAPIHook(LPSTR pszModName, LPSTR pszFuncName,
PROC pfnHook, BOOL bExcludeAPIHookMod=TRUE);
virtual ~CAPIHook();
operator PROC() {return m_pfnOrig;}
private:

LPSTR m_pszModName; //导出要HOOK函数的模块的名字
LPSTR m_pszFuncName; //要HOOK的函数的名字
PROC m_pfnOrig; //原API函数地址
PROC m_pfnHook; //HOOK后函数的地址
BOOL m_bExcludeAPIHookMod; //是否将HOOK API的模块本身排除在外
private:
static void ReplaceIATEntryInAllMods(LPSTR pszExportMod, PROC pfnCurrent,PROC pfnNew, BOOL bExcludeAPIHookMod);
static void ReplaceIATEntryInOneMod(LPSTR pszExportMod, PROC pfnCurrent,PROC pfnNew, HMODULE hModCaller);

//下面的代码用来解决其他模块动态加载DLL的问题
private:

//这两个指针用来将所有的CAPIHook对象连在一起
static CAPIHook *sm_pHeader;
CAPIHook *m_pNext;

private:

//当一个新的DLL被加载时,调用此函数
static void WINAPI HookNewlyLoadedModule(HMODULE hModule, DWORD dwFlags);
//用来跟踪当前进程加载新的DLL
static HMODULE WINAPI LoadLibraryA(PCSTR pszModulePath);
static HMODULE WINAPI LoadLibraryW(PCWSTR pszModulePath);
static HMODULE WINAPI LoadLibraryExA(PCSTR pszModulePath, HANDLE hFile, DWORD dwFlags);
static HMODULE WINAPI LoadLibraryExW(PCWSTR pszModulePath, HANDLE hFile, DWORD dwFlags);
//如果请求已HOOK的API函数,则返回用户自定义函数的地址
static FARPROC WINAPI GetProcAddress(HMODULE hModule, PCSTR pszProcName);

private:
//自动对这些函数进行挂钩
static CAPIHook sm_LoadLibraryA;
static CAPIHook sm_LoadLibraryW;
static CAPIHook sm_LoadLibraryExA;
static CAPIHook sm_LoadLibraryExW;
static CAPIHook sm_GetProcAddress;
};

#endif
========================APIHook.cpp========================

#include "APIHook.h"
#include "Tlhelp32.h"
#include <ImageHlp.h> //为了调用ImageDirectoryEntryToData函数
#pragma comment(lib, "ImageHlp")

//CAPIHook对象链表的头指针
CAPIHook *CAPIHook::sm_pHeader = NULL;
CAPIHook::CAPIHook(LPSTR pszModName, LPSTR pszFuncName,PROC pfnHook, BOOL bExcludeAPIHookMod/* =TRUE */)
{
//保存这个Hook函数的信息
m_bExcludeAPIHookMod = bExcludeAPIHookMod;
m_pszModName = pszModName;
m_pszFuncName = pszFuncName;
m_pfnHook = pfnHook;
m_pfnOrig = ::GetProcAddress(::GetModuleHandle(pszModName), pszFuncName);

//将此对象添加到链表中
m_pNext = sm_pHeader;
sm_pHeader = this;
//在所有当前已加载的模块中HOOK这个函数
ReplaceIATEntryInAllMods(m_pszModName, m_pfnOrig, m_pfnHook, bExcludeAPIHookMod);
}

CAPIHook::~CAPIHook()
{
//取消对所有模块中函数的HOOK
ReplaceIATEntryInAllMods(m_pszModName, m_pfnHook, m_pfnOrig, m_bExcludeAPIHookMod);
CAPIHook *p = sm_pHeader;

//从链表中移除此对象
if(p == this)
{
sm_pHeader = p->m_pNext;
}
else
{
while(p != NULL)
{
if(p->m_pNext == this)
{
p->m_pNext = this->m_pNext;
break;
}
p = p->m_pNext;
}
}
}

void CAPIHook::ReplaceIATEntryInOneMod(LPSTR pszExportMod, PROC pfnCurrent, PROC pfnNew, HMODULE hModCaller)
{
//取得模块的导入表(import descriptor)的首地址
//ImageDirectoryEntryToData函数可以返回导入表地址
ULONG ulSize;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc =(PIMAGE_IMPORT_DESCRIPTOR)::ImageDirectoryEntryToData(hModCaller, TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);

if(pImportDesc == NULL) //这个模块没有导入表项
return;

//查找包含pszExportMod模块中函数导入信息的导入表项
while(pImportDesc->Name != 0)
{
LPSTR pszMod = (LPSTR)((DWORD)hModCaller + pImportDesc->Name);
if (lstrcmpi(pszMod, pszExportMod) == 0)
{
break;
}
pImportDesc++;
}
if (pImportDesc->Name == 0) //hModCaller模块没有从pszExportMod模块导入任何函数
{
return;
}

//取得调用者的导入地址表(import address table, IAT)
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)(pImportDesc->FirstThunk + (DWORD)hModCaller);

//查找我们要HOOK的函数,将它的地址用新函数的地址替换掉
while (pThunk->u1.Function)
{
//lpAddr指向的内存保存了函数的地址
PDWORD lpAddr = (PDWORD)&(pThunk->u1.Function);
if (*lpAddr == (DWORD)pfnCurrent)
{
//修改页的保护属性
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
::VirtualQuery(lpAddr, &mbi, sizeof(mbi));
::VirtualProtect(lpAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);


//修改内存地址,相当于"lpAddr = (DWORD)pfnNew;"
::WriteProcessMemory(::GetCurrentProcess(), lpAddr, &pfnNew, sizeof(DWORD), NULL);
::VirtualProtect(lpAddr, sizeof(DWORD), dwOldProtect, 0);
break;
}
pThunk++;
}
}



void CAPIHook::ReplaceIATEntryInAllMods(LPSTR pszExportMod, PROC pfnCurrent, PROC pfnNew, BOOL bExcludeAPIHookMod)
{
//取得当前模块的句柄
HMODULE hModThis = NULL;
if(bExcludeAPIHookMod)
{
MEMORY_BASIC_INFORMATION mbi;
if(::VirtualQuery(ReplaceIATEntryInAllMods, &mbi, sizeof(mbi)) != 0)
hModThis = (HMODULE)mbi.AllocationBase;
}

//取得本进程的模块列表
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ::GetCurrentProcessId());


//遍历所有模块,分别对它们调用ReplaceIATEntryInOnMod函数,修改导入地址表
MODULEENTRY32 me = {sizeof(MODULEENTRY32)};
BOOL bOK = ::Module32First(hSnap, &me);
while (bOK)
{
//注意,我们不HOOK当前模块的函数
if (me.hModule != hModThis)
{
ReplaceIATEntryInOneMod(pszExportMod, pfnCurrent, pfnNew, me.hModule);
}

bOK = ::Module32Next(hSnap, &me);
}
::CloseHandle(hSnap);
}



//挂钩LoadLibrary和GetProcAddress函数,以便在这些函数被调用以后,挂钩的函数也能够被正确的处理
CAPIHook CAPIHook::sm_LoadLibraryA("Kernel32.dll", "LoadLibraryA", (PROC)CAPIHook::LoadLibraryA, TRUE);
CAPIHook CAPIHook::sm_LoadLibraryW("Kernel32.dll", "LoadLibraryW", (PROC)CAPIHook::LoadLibraryW, TRUE);
CAPIHook CAPIHook::sm_LoadLibraryExA("Kernel32.dll", "LoadLibraryExA",(PROC)CAPIHook::LoadLibraryExA, TRUE);
CAPIHook CAPIHook::sm_LoadLibraryExW("Kernel32.dll", "LoadLibraryExW",(PROC)CAPIHook::LoadLibraryExW, TRUE);
CAPIHook CAPIHook::sm_GetProcAddress("Kernel32.dll", "GetProcAddress", (PROC)CAPIHook::GetProcAddress, TRUE);

void WINAPI CAPIHook::HookNewlyLoadedModule(HMODULE hModule, DWORD dwFlags)
{
//如果一个新的模块被加载,挂钩各CAPIHook对象要求的API函数
if((hModule != NULL) && ((dwFlags&LOAD_LIBRARY_AS_DATAFILE) == 0))
{
CAPIHook *p = sm_pHeader;
while (p != NULL)
{
ReplaceIATEntryInOneMod(p->m_pszModName, p->m_pfnOrig, p->m_pfnHook, hModule);
p = p->m_pNext;
}
}
}



HMODULE WINAPI CAPIHook::LoadLibraryA(PCSTR pszModulePath)
{
HMODULE hModule = ::LoadLibraryA(pszModulePath);
HookNewlyLoadedModule(hModule, 0);
return hModule;
}



HMODULE WINAPI CAPIHook::LoadLibraryW(PCWSTR pszModulePath)
{
HMODULE hModule = ::LoadLibraryW(pszModulePath);
HookNewlyLoadedModule(hModule, 0);
return hModule;
}



HMODULE WINAPI CAPIHook::LoadLibraryExA(PCSTR pszModulePath, HANDLE hFile, DWORD dwFlags)
{
HMODULE hModule = ::LoadLibraryExA(pszModulePath, hFile, dwFlags);
HookNewlyLoadedModule(hModule, dwFlags);
return hModule;
}



HMODULE WINAPI CAPIHook::LoadLibraryExW(PCWSTR pszModulePath, HANDLE hFile, DWORD dwFlags)
{
HMODULE hModule = ::LoadLibraryExW(pszModulePath, hFile, dwFlags);
HookNewlyLoadedModule(hModule, dwFlags);
return hModule;
}



FARPROC WINAPI CAPIHook::GetProcAddress(HMODULE hModule, PCSTR pszProcName)
{
//得到函数的真实地址
FARPROC pfn = ::GetProcAddress(hModule, pszProcName);
//看它是否我们要HOOK的函数
CAPIHook *p = sm_pHeader;
while(p != NULL)
{
if (p->m_pfnOrig == pfn)
{
pfn = p->m_pfnHook;
break;
}
p = p->m_pNext;
}
return pfn;
}