windows原理05


dll注入技术

​ DLL注入的初始动力源自于程序员对其他第三方应用程序进行功能扩展的愿望,现在的DLL注入仍然是构成系统复杂功能,或对应用程序实现复杂操作的基础支撑技术。通过CreateRemoteThread()进行DLL注入的操作就是众多DLL注入技术的一种

​ 目前公开的DLL注入技巧:

  1. 注册表注入
  2. ComRes注入
  3. APC注入
  4. 消息钩子注入
  5. 远线程注入
  6. 依赖可信进程注入
  7. 劫持进程创建注入
  8. 输入法注入
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
远程线程注入

DWORD dwPid = 11344;//目标进程PID
char* pDllPath=(char*)"D:\\Demo\\Injection\\Debug\\Dll1.dll";//要注入的DLL

//打开进程获取句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, dwPid);
if (hProcess == INVALID_HANDLE_VALUE)
{
printf("句柄打开失败\n");
return 0;
}
//在目标进程中申请足够大的内存,存储DLL的全路径
LPVOID pszDllName = VirtualAllocEx(hProcess,
NULL, strlen(pDllPath),
MEM_COMMIT, PAGE_READWRITE);
if (pszDllName == NULL)
{
printf("内存申请失败\n");
CloseHandle(hProcess);
return 0;
}
//把DLL全路径写入到目标内存中
DWORD dwWrite;
WriteProcessMemory(hProcess,
pszDllName, pDllPath,
strlen(pDllPath), &dwWrite);
//创建远程线程
HANDLE hThread = CreateRemoteThread(
hProcess, //远程进程句柄
NULL, //安全属性
0, //栈大小
(LPTHREAD_START_ROUTINE)LoadLibraryA,//线程处理函数
pszDllName,//传入参数
NULL, //默认创建后的状态
NULL); //线程ID
if (hThread == NULL)
{
printf("线程创建失败\n");
CloseHandle(hProcess);
return 0;
}
//等待线程结束返回
WaitForSingleObject(hThread, -1);
//获取线程退出码,即LoadLibrary的返回值,即dll的首地址
DWORD dwExitCode;
GetExitCodeThread(hThread, &dwExitCode);
HMODULE hMod = (HMODULE)dwExitCode;
//释放空间
if (VirtualFreeEx(hProcess, pszDllName, 0, MEM_DECOMMIT))
{
CloseHandle(hProcess);
return 0;
}
CloseHandle(hProcess);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Dll1.dll

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(0, L"HOOK!", 0, 0);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

windows下的Hook技术

​ Hook是程序设计中最为灵活多变的技巧之一,在Windows下,Hook有两种含义:

  • 系统提供的消息Hook基址
  • 自定义的Hook编程技巧

​ 其中,由系统消息提供的消息钩子基址是由一系列的API提供的一种服务,这个系列的API可以完成对大多数应用程序关键节点的Hook操作,为此,Windows为每种Hook类型维护了一个钩子链表,我们可以通过一个系统API来完成对整个系统中所有符合此机制的关键点Hook。

​ 另一种自定义的Hook编程技巧则是基于特定系统结构、文件结构、汇编语言的一种高级技术。

系统消息Hook

​ Windows操作系统是以事件驱动的。事件被包装成了消息发送给窗口,比如点击菜单,按钮,移动窗口,按下键盘,正常消息:

  • 当按下键盘,产生一个消息,按键消息加入到系统消息队列
  • 操作系统从消息队列中取出消息,添加到相应的应用程序的消息队列中
  • 应用程序使用消息泵从自身的消息队列中取出消息WM_KEYDOWN,调用消息处理函数

​ 我们可以在系统消息队列到程序消息队列之间添加消息钩子,从而使得在系统消息队列消息可以发给应用程序之前捕获到消息。

​ 可以多次添加钩子,从而形成一个钩子链,可以依次调用函数。

​ 消息钩子是windows操作系统提供的机制,即spy++截获窗口消息的功能就是基于这样的机制。

1
2
3
4
5
6
7
main()

HMODULE hMod = LoadLibraryA("D:\\Demo\\Injection\\Debug\\Dll1.dll");
while (true)
{
Sleep(200);
}
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
dllmain.cpp(Dll1.dll)

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

HHOOK g_hook;

LRESULT CALLBACK KeyboardProc(
_In_ int code,
_In_ WPARAM wParam,
_In_ 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);
// 将其打印出来
CHAR szInfo[10] = { 0 };
sprintf_s(szInfo, _countof(szInfo), "Hook_%c", wKeyCode);
OutputDebugStringA(szInfo);
return 0;
}
}
return CallNextHookEx(g_hook, code, wParam, lParam);
}

//导入函数:安装钩子,当触发Hook消息WH_KEYBOARD后,目标进程加载dll
BOOL InstallHook(HMODULE hModule){
g_hook = SetWindowsHookEx(WH_KEYBOARD,
KeyboardProc, hModule, 0);
if (g_hook == NULL)
return FALSE;
return TRUE;
}

//导出函数:卸载钩子
BOOL UnInsatllHook() {
if (g_hook == NULL)
return FALSE;
return UnhookWindowsHookEx(g_hook);
}

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
InstallHook(hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
UnInsatllHook();
break;
}
return TRUE;
}

使用DebugView查看输出结果

image-20191116162651900

能够设置的钩子类型

宏值 含义
WH_MSGFILTER 截获用户与空间交互的消息
WH_KEYBOARD 截获键盘消息
WH_GETMESSAGE 截获从消息队列送出的消息
WH_CBT 截获系统的基本消息,譬如:窗口的创建、激活、关闭、最大最小化、移动等等
WH_MOUSE 截获鼠标消息
WH_CALLWNDPROCRET 截获目标窗口处理完毕的消息

自定义钩子

​ 钩子的主要含义其实就是改变程序原有的执行流程,让程序执行我们自己的代码。我们也可以通过修改程序代码的方式来实现这一点

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
//注入代码
void InjectCode(char* pCode, DWORD dwSize, DWORD dwPid)
{
//1.打开进程获取句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess)
{
printf("句柄打开失败\n");
return;
}
//2. 再目标进程中申请足够大的内存,存储DLL的全路径
LPVOID lpBuf = VirtualAllocEx(hProcess, NULL,
strlen(pCode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

if (!lpBuf)
{
printf("内存申请失败\n");
CloseHandle(hProcess);
return;
}

//3.把DLL全路径写入进去
DWORD dwWrite;
WriteProcessMemory(hProcess, lpBuf,
pCode, dwSize, &dwWrite);
//4.创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL,
(LPTHREAD_START_ROUTINE)lpBuf, NULL, NULL, NULL);

//5.等待线程结束
WaitForSingleObject(hThread, -1);

//6.释放内存
VirtualFreeEx(hProcess, lpBuf, 0, MEM_RELEASE);
CloseHandle(hProcess);
}

//构造shellcode
DWORD dwAddr;
__asm
{
push 0;
push 0;
push 0;
push 0;
mov eax, 0x99999999;
call eax;
}
//x64dbg二进制shellcode形式复制出来的
char pCode[] = "\x6A\x00\x6A\x00\x6A\x00\x6A\x00\xB8\x99\x99\x99\x99\xFF\xD0";
DWORD dwAddr;
scanf_s("%x", &dwAddr);
*(int*)(pCode + 9) = dwAddr;

DWORD dwPid;
scanf_s("%d", &dwPid);
InjectCode(pCode, sizeof(pCode),dwPid);

printf("MessageBoxW:0x%08X\n", (DWORD)MessageBoxW);

内联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
49
50
51
52
53
54
55
56
57
#include <Windows.h>
#include <tchar.h>

char g_OldCode[5] = {};
char g_NewCode[5] = { 0xE9 };//jmp

int WINAPI MyMessageBox(HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);

void OnHook() {
//保存原函数被修改部分的代码
memcpy(g_OldCode, MessageBox, 5);
//计算偏移
DWORD dwOffset =
(DWORD)MyMessageBox - (DWORD)MessageBox - 5;
//写入新的偏移
memcpy(g_NewCode + 1, &dwOffset, 4);
//修改内存属性(代码段不可写)
DWORD dwOld;
VirtualProtect(MessageBox, 5, PAGE_READWRITE, &dwOld);
memcpy(MessageBox, g_NewCode, 5);
VirtualProtect(MessageBox, 5, dwOld, &dwOld);
}

void UnHook() {
DWORD dwOld;
VirtualProtect(MessageBox,
5, PAGE_EXECUTE_READWRITE, &dwOld);
memcpy(MessageBox, g_OldCode, 5);
VirtualProtect(MessageBox,
5, dwOld, &dwOld);
}

int WINAPI MyMessageBox(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType) {
lpText = _T("HOOK!");

//卸载钩子
UnHook();
//调用MessageBox
int nReturn = MessageBox(hWnd, lpText, lpCaption, uType);
return nReturn;
}

int main()
{
//调用正常的MessageBox
MessageBox(0, 0, 0, 0);
OnHook();
//调用我的MessageBox
MessageBox(0, 0, 0, 0);
}

也可以把代码注入到其他进程中

image-20191116185153487

IAT 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
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
// dllmain.cpp : 定义 DLL 应用程序的入口点。
// Dll1
#include "stdafx.h"

#include <Windows.h>
#include <tchar.h>

DWORD g_IATAddr;
DWORD g_FunOldAddr;

int WINAPI MyMessageBox(HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);

void OnHook(const char* pFunName, const char* pDllName) {
//遍历导入表(INT)
//参数为NULL返回创建进程的文件的句柄
HMODULE hMod = GetModuleHandle(NULL);
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)
(pDos->e_lfanew + (DWORD)pDos);

DWORD dwImportRVA = pNt->OptionalHeader.DataDirectory[1].VirtualAddress;

PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)
(dwImportRVA + (DWORD)hMod);

while (pImport->Name)
{
char* pDll = (char*)(pImport->Name + (DWORD)hMod);
if (!_stricmp(pDll, pDllName))//忽略大小写
{
//遍历INT
IMAGE_THUNK_DATA* pINT = (IMAGE_THUNK_DATA*)
(pImport->OriginalFirstThunk + (DWORD)hMod);
IMAGE_THUNK_DATA* pIAT = (IMAGE_THUNK_DATA*)
(pImport->FirstThunk + (DWORD)hMod);
while (pINT->u1.AddressOfData)
{
if (!IMAGE_SNAP_BY_ORDINAL(pINT->u1.AddressOfData))
{
PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)
(pINT->u1.AddressOfData + (DWORD)hMod);
if (!_stricmp((char*)pName->Name, pFunName))
{
//函数地址
g_FunOldAddr = pIAT->u1.Function;
g_IATAddr = (DWORD)pIAT;

//修改IAT表中的地址
DWORD dwOld;
VirtualProtect(pIAT, 4, PAGE_READWRITE, &dwOld);
*(DWORD*)pIAT = (DWORD)MyMessageBox;
VirtualProtect(pIAT, 4, dwOld, &dwOld);
return;
}
}
//下一个函数
pINT++;
pIAT++;
}
}
//下一个模块
pImport++;
}
}

void UnHook() {
DWORD dwOld;
VirtualProtect((LPVOID)g_IATAddr, 4, PAGE_READWRITE, &dwOld);
*(DWORD*)g_IATAddr = g_FunOldAddr;
VirtualProtect((LPVOID)g_IATAddr, 4, dwOld, &dwOld);
}

int WINAPI MyMessageBox(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType) {
lpText = _T("HOOK!");

//卸载钩子
UnHook();
//调用MessageBox
int nReturn = MessageBox(hWnd, lpText, lpCaption, uType);
OnHook("MessageBoxW", "user32.dll");
return nReturn;
}


BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
OnHook("MessageBoxW", "user32.dll");
//case DLL_THREAD_ATTACH:
//case DLL_THREAD_DETACH:
//case DLL_PROCESS_DETACH:
// UnHook();
break;
}
return TRUE;
}
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
// Injection.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <windows.h>
#include <stdio.h>

void Inject(char* pDllPath, DWORD dwPid)
{
//1.打开进程获取句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess)
{
printf("句柄打开失败\n");
return;
}
//2. 再目标进程中申请足够大的内存,存储DLL的全路径
LPVOID lpBuf = VirtualAllocEx(hProcess, NULL,
strlen(pDllPath), MEM_COMMIT, PAGE_READWRITE);

if (!lpBuf)
{
printf("内存申请失败\n");
CloseHandle(hProcess);
return;
}

//3.把DLL全路径写入进去
DWORD dwWrite;
WriteProcessMemory(hProcess, lpBuf,
pDllPath, strlen(pDllPath), &dwWrite);
//4.创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL,
(LPTHREAD_START_ROUTINE)LoadLibraryA, lpBuf, NULL, NULL);

//5.等待线程结束
WaitForSingleObject(hThread, -1);

//6.释放内存
VirtualFreeEx(hProcess, lpBuf, 0, MEM_RELEASE);
CloseHandle(hProcess);

}

int main()
{
DWORD dwPid = 5668;//目标进程PID
char* pDllPath = (char*)"D:\\Demo\\Injection\\Debug\\Dll1.dll";//要注入的DLL

Inject(pDllPath, dwPid);
system("pause");
}

image-20191117162702982