May 12, 2022

关于逆向工程核心原理-隐藏进程

(简称逆向工程核心原理第三十三章)

先贴下技术图表

image-20220512115412740

实验目标与工作原理

隐藏进程?

隐藏进程(stealth process)在代码逆向分析领域中的专业术语为Rookit,指修改系统内核来隐藏进程、文件、注册表的一种技术,然而这章讲的不是这个Rookit。

在开始本章的隐藏进程之前,先了解下本章使用的技术

为了灵活使用目标进程的空间,使用了dll注入技术

API代码修改技术

钩取之前

  1. procexp调用API,直接CALL到了该API的起始位置
  2. API执行结束返回

image-20220512142845887

钩取之后

首先是把stealth.dll注入到了目标进程,钩取了ntdll.ZwQuerySystemInformation() API起始地址

  1. 正常调用API,运行到起始地址,而这时已经被钩取了起始地址
  2. 直接跳到了钩取设置的函数
  3. 于是脱钩,运行原本的API,API运行完返回到自己的设置的钩子函数
  4. 再进行自己想干的操作,再挂钩,
  5. 最后返回到主线程

使用API代码修改技术的唯一限制,要钩取的API代码长度要大于5个字节,然而这个限制是不存在的

(顾名思义,API代码修改是直接修改映射到目标进程内存空间的系统DLL的代码,进程的其他线程正在读某个函数时,尝试修改该代码会怎么样?这样会引发非法访问(Access Violation)异常!后面有解决的方法)

image-20220512143415877

进程隐藏!

相关API

由于进程是内核对象,所以(用户模式下的程序),只要通过相关API就能检测到他们,用户模式下检测进程的API如下2类

隐藏技术的问题

假设我们隐藏了一个进程,如果钩取运行中的ProcExp.exe或者taskmgr进程的ZwQuerySystemInformation() API,那么查找进程的就无法查找到我们隐藏的进程!

然而存在几个问题

除了这两个外还有n多的查看进程程序,如果想把某个进程隐藏,要钩取系统中运行的所有程序

钩取了一个taskmgr,我们又起了一个怎么办,新起的taskmgr并没有给钩取,所以任然能找到我们隐藏的进程

我们的隐藏程序需要对系统所有进程的ZwQuerySystemInformation() API,并且对后面将要启动所有进程也做相同的操作。

而这就是全局钩取的概念!由于需要对整个系统范围进程钩取,所有才用了全局(Global)这个词

(然而这个内容本章的后半部分和下一章)

练习 #1

PS: 这样并不能解决全局钩取的问题,运行HideProc.exe后,新建的进程不会自动钩取,因此这是一种不完全隐藏技术

实验环节

注进去的时候注意dll要绝对路径(bangzhu我半天

再查询一下dll可以发现注进去了

image-20220512154433617

开审源码

HideProc.cpp

原本的InjectDll.exe程序基础上添加向所有进程注入DLL的功能,可以认为是InjectDll.exe程序的加强版!

InjectAllProcess

把大于100的PID进程都注入了,关于CreateToolhelp32Snapshot还是第一次说,明明前几章都出现了?(难道是作者给留我们的自我思考查询时间

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
// Injection加强版!一节更比六节强
BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath)
{
DWORD dwPID = 0;
HANDLE hSnapShot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe;

// 该API可以获取系统中运行的所有进程的列表
pe.dwSize = sizeof( PROCESSENTRY32 );
hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

// 配合 Process32First 和 Process32Next 将获得的进程信息存放到 PROCESSENTRY32结构体变量pe,进而获取进程的PID
Process32First(hSnapShot, &pe);
do
{
dwPID = pe.th32ProcessID;

// 鉴于安全考虑不把PID少于100的系统进程进行DLL注入
if( dwPID < 100 )
continue;

if( nMode == INJECTION_MODE )
InjectDll(dwPID, szDllPath);
else
EjectDll(dwPID, szDllPath);
}
while( Process32Next(hSnapShot, &pe) );

CloseHandle(hSnapShot);

return TRUE;
}

其他的代码都是之前差不多的了,有一处好玩的是使用了stealth.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
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
#include "windows.h"
#include "stdio.h"
#include "tlhelp32.h"
#include "tchar.h"

typedef void (*PFN_SetProcName)(LPCTSTR szProcName);
enum {INJECTION_MODE = 0, EJECTION_MODE};

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;

if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
printf("OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}

if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;

// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}

if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}

return TRUE;
}

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
HANDLE hProcess, hThread;
LPVOID pRemoteBuf;
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;

if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
printf("OpenProcess(%d) failed!!!\n", dwPID);
return FALSE;
}

// 目标进程开内存空间
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
MEM_COMMIT, PAGE_READWRITE);

// 写进去
WriteProcessMemory(hProcess, pRemoteBuf,
(LPVOID)szDllPath, dwBufSize, NULL);

pThreadProc = (LPTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(L"kernel32.dll"),
"LoadLibraryW");
// 远程执行!
hThread = CreateRemoteThread(hProcess, NULL, 0,
pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

CloseHandle(hThread);
CloseHandle(hProcess);

return TRUE;
}

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
BOOL bMore = FALSE, bFound = FALSE;
HANDLE hSnapshot, hProcess, hThread;
MODULEENTRY32 me = { sizeof(me) };
LPTHREAD_START_ROUTINE pThreadProc;

if( INVALID_HANDLE_VALUE ==
(hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) )
return FALSE;

bMore = Module32First(hSnapshot, &me);
for( ; bMore ; bMore = Module32Next(hSnapshot, &me) )
{
if( !_tcsicmp(me.szModule, szDllPath) ||
!_tcsicmp(me.szExePath, szDllPath) )
{
bFound = TRUE;
break;
}
}

if( !bFound )
{
CloseHandle(hSnapshot);
return FALSE;
}

if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
CloseHandle(hSnapshot);
return FALSE;
}

pThreadProc = (LPTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(L"kernel32.dll"),
"FreeLibrary");
hThread = CreateRemoteThread(hProcess, NULL, 0,
pThreadProc, me.modBaseAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hSnapshot);

return TRUE;
}

// Injection加强版!一节更比六节强
BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath)
{
DWORD dwPID = 0;
HANDLE hSnapShot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe;

// 该API可以获取系统中运行的所有进程的列表
pe.dwSize = sizeof( PROCESSENTRY32 );
hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

// 配合 Process32First 和 Process32Next 将获得的进程信息存放到 PROCESSENTRY32结构体变量pe,进而获取进程的PID
Process32First(hSnapShot, &pe);
do
{
dwPID = pe.th32ProcessID;

// 鉴于安全考虑不把PID少于100的系统进程进行DLL注入
if( dwPID < 100 )
continue;

if( nMode == INJECTION_MODE )
InjectDll(dwPID, szDllPath);
else
EjectDll(dwPID, szDllPath);
}
while( Process32Next(hSnapShot, &pe) );

CloseHandle(hSnapShot);

return TRUE;
}

int _tmain(int argc, TCHAR* argv[])
{
int nMode = INJECTION_MODE;
HMODULE hLib = NULL;
PFN_SetProcName SetProcName = NULL;

if( argc != 4 )
{
printf("\n Usage : HideProc.exe <-hide|-show> "\
"<process name> <dll path>\n\n");
return 1;
}

// change privilege
SetPrivilege(SE_DEBUG_NAME, TRUE);

// load library
hLib = LoadLibrary(argv[3]);

// 从我们的dll获取 SetProcName 函数地址
SetProcName = (PFN_SetProcName)GetProcAddress(hLib, "SetProcName");
// 将要隐藏进程名放到 g_szProcName
SetProcName(argv[2]);

// Inject(Eject) Dll to all process
if( !_tcsicmp(argv[1], L"-show") )
nMode = EJECTION_MODE;

InjectAllProcess(nMode, argv[3]);

// free library
FreeLibrary(hLib);

return 0;
}

stealth.cpp

dllexport

这个就是用于保存要隐藏的进程名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// global variable (in sharing memory)
#pragma comment(linker, "/SECTION:.SHARE,RWS")
#pragma data_seg(".SHARE")
TCHAR g_szProcName[MAX_PATH] = {0,};
#pragma data_seg()

// 导出函数噢
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void SetProcName(LPCTSTR szProcName)
{
_tcscpy_s(g_szProcName, szProcName);
}
#ifdef __cplusplus
}
#endif

DllMain

主函数还是一如既往的简单(突然想起第一次看WIN32程序是有多懵)

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
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
char szCurProc[MAX_PATH] = {0,};
char *p = NULL;

// 比较当前进程是不是HideProc,是的话就异常退出
GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
p = strrchr(szCurProc, '\\');
if( (p != NULL) && !_stricmp(p+1, "HideProc.exe") )
return TRUE;

switch( fdwReason )
{
// #2. API Hooking
case DLL_PROCESS_ATTACH :
hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,
(PROC)NewZwQuerySystemInformation, g_pOrgBytes);
break;

// #3. API Unhooking
case DLL_PROCESS_DETACH :
unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,
g_pOrgBytes);
break;
}

return TRUE;
}

hook_by_code

这边的一个点就是计算跳转地址,因为是偏移地址

跳转的原理:下一条指令地址(当前地址 + 5) + 操作数 = 目标地址(由于32位程序都是地址都是4个字节再加本身跳转指令如E9 EB,所以都是5个字节)

所以算操作数同理:目标地址 - 当前地址 - 5 = 操作数

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
BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
FARPROC pfnOrg;
DWORD dwOldProtect, dwAddress;
BYTE pBuf[5] = {0xE9, 0, };
PBYTE pByte;

// 获取要钩取的API地址
pfnOrg = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pfnOrg;

// 检测是否已经钩取
if( pByte[0] == 0xE9 )
return FALSE;

// 为了修改5个字节,先向内存开写权限
VirtualProtect((LPVOID)pfnOrg, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

// 先备份5个字节
memcpy(pOrgBytes, pfnOrg, 5);

// 计算jmp地址
// => XXXX = pfnNew - pfnOrg - 5
dwAddress = (DWORD)pfnNew - (DWORD)pfnOrg - 5;
memcpy(&pBuf[1], &dwAddress, 4);

// 开钩!
memcpy(pfnOrg, pBuf, 5);

// 恢复内存属性
VirtualProtect((LPVOID)pfnOrg, 5, dwOldProtect, &dwOldProtect);

return TRUE;
}

unhook_by_code

脱钩的方式非常简单

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
BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect;
PBYTE pByte;

// 获取API地址
pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;

// 若已经脱钩就直接返回
if( pByte[0] != 0xE9 )
return FALSE;

// 开写属性
VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

// 脱钩!
memcpy(pFunc, pOrgBytes, 5);

// 恢复内存属性
VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

return TRUE;
}

NewZwQuerySystemInformation

(这个结构体没看太懂,先记录着)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
BYTE Reserved1[48];
PVOID Reserved2[3];
HANDLE UniqueProcessId;
PVOID Reserved3;
ULONG HandleCount;
BYTE Reserved4[4];
PVOID Reserved5[11];
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION;

NTSTATUS WINAPI ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);

配合我的注释已然是很清楚,有趣的是在链表中删除节点的方法

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
NTSTATUS WINAPI NewZwQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength)
{
NTSTATUS status;
FARPROC pFunc;
PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
char szProcName[MAX_PATH] = {0,};

// 开始之前先脱钩
unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, g_pOrgBytes);

// 调用原始API
pFunc = GetProcAddress(GetModuleHandleA(DEF_NTDLL),
DEF_ZWQUERYSYSTEMINFORMATION);
status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
(SystemInformationClass, SystemInformation,
SystemInformationLength, ReturnLength);

// 状态不对就开溜
if( status != STATUS_SUCCESS )
goto __NTQUERYSYSTEMINFORMATION_END;

// 仅针对SystemProcessInformation类型操作
if( SystemInformationClass == SystemProcessInformation )
{
// SYSTEM_PROCESS_INFORMATION 类型转换
// pCur 是指向单向链表的头
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

while ( TRUE )
{
// 比较进程名称
// g_szProcName = 要隐藏的进程名
// (=> SetProcName() 里设置)
if ( pCur->Reserved2[1] != NULL )
{
if ( !_tcsicmp((PWSTR)pCur->Reserved2[1], g_szProcName) )
{
// 从链表中删除隐藏进程的节点
// 两个情况,一个是下一个节点已经结束了
if ( pCur -> NextEntryOffset == 0 )
// 就把上个节点的子项(也就是到当前节点偏移)设置为0,于是就隐藏了该节点
pPrev->NextEntryOffset = 0;
else
// 另一个情况,还有下个节点
// 就把上个节点的偏移(通过该节点的偏移)再加上当前节点到下一个节点的偏移值,就形成了跳过了当前节点,直接到下个节点
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur;
}

// 到结尾就退出
if(pCur->NextEntryOffset == 0)
break;

// 链表的下一项
pCur = (PSYSTEM_PROCESS_INFORMATION)
((ULONG)pCur + pCur->NextEntryOffset);
}
}

__NTQUERYSYSTEMINFORMATION_END:

// 函数终止前,再次执行API钩取操作,为下次调用准备
hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,
(PROC)NewZwQuerySystemInformation, g_pOrgBytes);

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
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
#include "windows.h"
#include "tchar.h"

#define STATUS_SUCCESS (0x00000000L)

typedef LONG NTSTATUS;

typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
BYTE Reserved1[48];
PVOID Reserved2[3];
HANDLE UniqueProcessId;
PVOID Reserved3;
ULONG HandleCount;
BYTE Reserved4[4];
PVOID Reserved5[11];
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)
(SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);

#define DEF_NTDLL ("ntdll.dll")
#define DEF_ZWQUERYSYSTEMINFORMATION ("ZwQuerySystemInformation")


// global variable (in sharing memory)
#pragma comment(linker, "/SECTION:.SHARE,RWS")
#pragma data_seg(".SHARE")
TCHAR g_szProcName[MAX_PATH] = {0,};
#pragma data_seg()

// global variable
BYTE g_pOrgBytes[5] = {0,};


BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
FARPROC pfnOrg;
DWORD dwOldProtect, dwAddress;
BYTE pBuf[5] = {0xE9, 0, };
PBYTE pByte;

// 获取要钩取的API地址
pfnOrg = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pfnOrg;

// 检测是否已经钩取
if( pByte[0] == 0xE9 )
return FALSE;

// 为了修改5个字节,先向内存开写权限
VirtualProtect((LPVOID)pfnOrg, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

// 先备份5个字节
memcpy(pOrgBytes, pfnOrg, 5);

// 计算jmp地址
// => XXXX = pfnNew - pfnOrg - 5
dwAddress = (DWORD)pfnNew - (DWORD)pfnOrg - 5;
memcpy(&pBuf[1], &dwAddress, 4);

// 开钩!
memcpy(pfnOrg, pBuf, 5);

// 恢复内存属性
VirtualProtect((LPVOID)pfnOrg, 5, dwOldProtect, &dwOldProtect);

return TRUE;
}


BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect;
PBYTE pByte;

// 获取API地址
pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;

// 若已经脱钩就直接返回
if( pByte[0] != 0xE9 )
return FALSE;

// 开写属性
VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

// 脱钩!
memcpy(pFunc, pOrgBytes, 5);

// 恢复内存属性
VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

return TRUE;
}


NTSTATUS WINAPI NewZwQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength)
{
NTSTATUS status;
FARPROC pFunc;
PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
char szProcName[MAX_PATH] = {0,};

// 开始之前先脱钩
unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, g_pOrgBytes);

// 调用原始API
pFunc = GetProcAddress(GetModuleHandleA(DEF_NTDLL),
DEF_ZWQUERYSYSTEMINFORMATION);
status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
(SystemInformationClass, SystemInformation,
SystemInformationLength, ReturnLength);

// 状态不对就开溜
if( status != STATUS_SUCCESS )
goto __NTQUERYSYSTEMINFORMATION_END;

// 仅针对SystemProcessInformation类型操作
if( SystemInformationClass == SystemProcessInformation )
{
// SYSTEM_PROCESS_INFORMATION 类型转换
// pCur 是指向单向链表的头
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

while ( TRUE )
{
// 比较进程名称
// g_szProcName = 要隐藏的进程名
// (=> SetProcName() 里设置)
if ( pCur->Reserved2[1] != NULL )
{
if ( !_tcsicmp((PWSTR)pCur->Reserved2[1], g_szProcName) )
{
// 从链表中删除隐藏进程的节点
// 两个情况,一个是下一个节点已经结束了
if ( pCur -> NextEntryOffset == 0 )
// 就把上个节点的子项(也就是到当前节点偏移)设置为0,于是就隐藏了该节点
pPrev->NextEntryOffset = 0;
else
// 另一个情况,还有下个节点
// 就把上个节点的偏移(通过该节点的偏移)再加上当前节点到下一个节点的偏移值,就形成了跳过了当前节点,直接到下个节点
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur;
}

// 到结尾就退出
if(pCur->NextEntryOffset == 0)
break;

// 链表的下一项
pCur = (PSYSTEM_PROCESS_INFORMATION)
((ULONG)pCur + pCur->NextEntryOffset);
}
}

__NTQUERYSYSTEMINFORMATION_END:

// 函数终止前,再次执行API钩取操作,为下次调用准备
hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,
(PROC)NewZwQuerySystemInformation, g_pOrgBytes);

return status;
}


BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
char szCurProc[MAX_PATH] = {0,};
char *p = NULL;

// 比较当前进程是不是HideProc,是的话就异常退出
GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
p = strrchr(szCurProc, '\\');
if( (p != NULL) && !_stricmp(p+1, "HideProc.exe") )
return TRUE;

switch( fdwReason )
{
// #2. API Hooking
case DLL_PROCESS_ATTACH :
hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,
(PROC)NewZwQuerySystemInformation, g_pOrgBytes);
break;

// #3. API Unhooking
case DLL_PROCESS_DETACH :
unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,
g_pOrgBytes);
break;
}

return TRUE;
}

// 导出函数噢
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void SetProcName(LPCTSTR szProcName)
{
_tcscpy_s(g_szProcName, szProcName);
}
#ifdef __cplusplus
}
#endif

全局钩子

首先阐述一下全局API钩取的概念及具体实现方法,所针对的进程

而前面的例子是没有将来要运行的所有进程,若重新运行taskmgr之类就无法隐藏我们的进程

Kernel32.CreateProcess()

每次创建进程就会调用此API,但钩取该API得考虑几个因素

更底层原则 Ntdll.ZwResumeThread()

由此命名为 更底层原则!ZwResumeThread() 函数在进程创建后、主线程运行前被调用执行(在CreateProcess() 内部调用)

PS: 该API尚未被公开,所以随着系统升级可能就改变了,所以如下例子没用

练习 #2

首先要把stealth2.dll文件复制到 C:/Windows/System32 文件夹下

实验环节

经过反复操作得出以下几点

image-20220513103923218

开审源码

HideProc2.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
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
#include "windows.h"
#include "stdio.h"
#include "tlhelp32.h"
#include "tchar.h"

enum {INJECTION_MODE = 0, EJECTION_MODE};

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;

if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
printf("OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}

if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;

// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}

if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}

return TRUE;
}

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
HANDLE hProcess, hThread;
LPVOID pRemoteBuf;
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;

if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
printf("OpenProcess(%d) failed!!!\n", dwPID);
return FALSE;
}

pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
MEM_COMMIT, PAGE_READWRITE);

WriteProcessMemory(hProcess, pRemoteBuf,
(LPVOID)szDllPath, dwBufSize, NULL);

pThreadProc = (LPTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(L"kernel32.dll"),
"LoadLibraryW");
hThread = CreateRemoteThread(hProcess, NULL, 0,
pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

CloseHandle(hThread);
CloseHandle(hProcess);

return TRUE;
}

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
BOOL bMore = FALSE, bFound = FALSE;
HANDLE hSnapshot, hProcess, hThread;
MODULEENTRY32 me = { sizeof(me) };
LPTHREAD_START_ROUTINE pThreadProc;

if( INVALID_HANDLE_VALUE ==
(hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) )
return FALSE;

bMore = Module32First(hSnapshot, &me);
for( ; bMore ; bMore = Module32Next(hSnapshot, &me) )
{
if( !_tcsicmp(me.szModule, szDllPath) ||
!_tcsicmp(me.szExePath, szDllPath) )
{
bFound = TRUE;
break;
}
}

if( !bFound )
{
CloseHandle(hSnapshot);
return FALSE;
}

if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
printf("OpenProcess(%d) failed!!!\n", dwPID);
CloseHandle(hSnapshot);
return FALSE;
}

pThreadProc = (LPTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(L"kernel32.dll"),
"FreeLibrary");
hThread = CreateRemoteThread(hProcess, NULL, 0,
pThreadProc, me.modBaseAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hSnapshot);

return TRUE;
}

BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath)
{
DWORD dwPID = 0;
HANDLE hSnapShot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe;

// Get the snapshot of the system
pe.dwSize = sizeof( PROCESSENTRY32 );
hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

// find process
Process32First(hSnapShot, &pe);
do
{
dwPID = pe.th32ProcessID;

if( dwPID < 100 )
continue;

// 传入的是进程ID和dll路径
if( nMode == INJECTION_MODE )
InjectDll(dwPID, szDllPath);
else
EjectDll(dwPID, szDllPath);
} while( Process32Next(hSnapShot, &pe) );

CloseHandle(hSnapShot);

return TRUE;
}

int _tmain(int argc, TCHAR* argv[])
{
int nMode = INJECTION_MODE;

if( argc != 3 )
{
printf("\n Usage : HideProc2.exe <-hide|-show> <dll path>\n\n");
return 1;
}

// change privilege
SetPrivilege(SE_DEBUG_NAME, TRUE);

// Inject(Eject) Dll to all process
if( !_tcsicmp(argv[1], L"-show") )
nMode = EJECTION_MODE;

InjectAllProcess(nMode, argv[2]);

return 0;
}

stealth2.cpp

DllMain

与stealth.cpp不同是还钩取了CreateProcessA/W两个API,达成全局钩取

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
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
char szCurProc[MAX_PATH] = {0,};
char *p = NULL;

// 不会注入到HideProc2.exe进程
GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
p = strrchr(szCurProc, '\\');
if( (p != NULL) && !_stricmp(p+1, "HideProc2.exe") )
return TRUE;

// change privilege
SetPrivilege(SE_DEBUG_NAME, TRUE);

switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
// hook
hook_by_code("kernel32.dll", "CreateProcessA",
(PROC)NewCreateProcessA, g_pOrgCPA);
hook_by_code("kernel32.dll", "CreateProcessW",
(PROC)NewCreateProcessW, g_pOrgCPW);
hook_by_code("ntdll.dll", "ZwQuerySystemInformation",
(PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);
break;

case DLL_PROCESS_DETACH :
// unhook
unhook_by_code("kernel32.dll", "CreateProcessA",
g_pOrgCPA);
unhook_by_code("kernel32.dll", "CreateProcessW",
g_pOrgCPW);
unhook_by_code("ntdll.dll", "ZwQuerySystemInformation",
g_pOrgZwQSI);
break;
}

return TRUE;
}

NewCreateProcessA

和挂那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
BOOL WINAPI NewCreateProcessA(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL bRet;
FARPROC pFunc;

// unhook
unhook_by_code("kernel32.dll", "CreateProcessA", g_pOrgCPA);

// 获取原来的API地址
pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);

// 向生成子进程注入stealth2.dll
if( bRet )
InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

// hook
hook_by_code("kernel32.dll", "CreateProcessA",
(PROC)NewCreateProcessA, g_pOrgCPA);

return bRet;
}

于是就是stealth2.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
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
#include "windows.h"
#include "stdio.h"
#include "tchar.h"

#define STR_MODULE_NAME (L"stealth2.dll")
#define STR_HIDE_PROCESS_NAME (L"notepad.exe")
#define STATUS_SUCCESS (0x00000000L)

typedef LONG NTSTATUS;

typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
BYTE Reserved1[52];
PVOID Reserved2[3];
HANDLE UniqueProcessId;
PVOID Reserved3;
ULONG HandleCount;
BYTE Reserved4[4];
PVOID Reserved5[11];
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);

typedef BOOL (WINAPI *PFCREATEPROCESSA)(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);

typedef BOOL (WINAPI *PFCREATEPROCESSW)(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);

BYTE g_pOrgCPA[5] = {0,};
BYTE g_pOrgCPW[5] = {0,};
BYTE g_pOrgZwQSI[5] = {0,};

BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect, dwAddress;
BYTE pBuf[5] = {0xE9, 0, };
PBYTE pByte;

pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if( pByte[0] == 0xE9 )
return FALSE;

VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

memcpy(pOrgBytes, pFunc, 5);

dwAddress = (DWORD)pfnNew - (DWORD)pFunc - 5;
memcpy(&pBuf[1], &dwAddress, 4);

memcpy(pFunc, pBuf, 5);

VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

return TRUE;
}

BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect;
PBYTE pByte;

pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if( pByte[0] != 0xE9 )
return FALSE;

VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

memcpy(pFunc, pOrgBytes, 5);

VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

return TRUE;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;

if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
printf("OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}

if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;

// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}

if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}

return TRUE;
}

BOOL InjectDll2(HANDLE hProcess, LPCTSTR szDllName)
{
HANDLE hThread;
LPVOID pRemoteBuf;
DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR);
FARPROC pThreadProc;

pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
MEM_COMMIT, PAGE_READWRITE);
if( pRemoteBuf == NULL )
return FALSE;

WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName,
dwBufSize, NULL);

pThreadProc = GetProcAddress(GetModuleHandleA("kernel32.dll"),
"LoadLibraryW");
hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)pThreadProc,
pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

CloseHandle(hThread);

return TRUE;
}

NTSTATUS WINAPI NewZwQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength)
{
NTSTATUS status;
FARPROC pFunc;
PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
char szProcName[MAX_PATH] = {0,};

unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI);

pFunc = GetProcAddress(GetModuleHandleA("ntdll.dll"),
"ZwQuerySystemInformation");
status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
(SystemInformationClass, SystemInformation,
SystemInformationLength, ReturnLength);

if( status != STATUS_SUCCESS )
goto __NTQUERYSYSTEMINFORMATION_END;

if( SystemInformationClass == SystemProcessInformation )
{
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

while(TRUE)
{
if(pCur->Reserved2[1] != NULL)
{
if(!_tcsicmp((PWSTR)pCur->Reserved2[1], STR_HIDE_PROCESS_NAME))
{
if(pCur->NextEntryOffset == 0)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur;
}

if(pCur->NextEntryOffset == 0)
break;

pCur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pCur + pCur->NextEntryOffset);
}
}

__NTQUERYSYSTEMINFORMATION_END:

hook_by_code("ntdll.dll", "ZwQuerySystemInformation",
(PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);

return status;
}

BOOL WINAPI NewCreateProcessA(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL bRet;
FARPROC pFunc;

// unhook
unhook_by_code("kernel32.dll", "CreateProcessA", g_pOrgCPA);

// 获取原来的API地址
pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);

// 向生成子进程注入stealth2.dll
if( bRet )
InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

// hook
hook_by_code("kernel32.dll", "CreateProcessA",
(PROC)NewCreateProcessA, g_pOrgCPA);

return bRet;
}

BOOL WINAPI NewCreateProcessW(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL bRet;
FARPROC pFunc;

// unhook
unhook_by_code("kernel32.dll", "CreateProcessW", g_pOrgCPW);

pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessW");
bRet = ((PFCREATEPROCESSW)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);

if( bRet )
InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

// hook
hook_by_code("kernel32.dll", "CreateProcessW",
(PROC)NewCreateProcessW, g_pOrgCPW);

return bRet;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
char szCurProc[MAX_PATH] = {0,};
char *p = NULL;

// 不会注入到HideProc2.exe进程
GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
p = strrchr(szCurProc, '\\');
if( (p != NULL) && !_stricmp(p+1, "HideProc2.exe") )
return TRUE;

// change privilege
SetPrivilege(SE_DEBUG_NAME, TRUE);

switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
// hook
hook_by_code("kernel32.dll", "CreateProcessA",
(PROC)NewCreateProcessA, g_pOrgCPA);
hook_by_code("kernel32.dll", "CreateProcessW",
(PROC)NewCreateProcessW, g_pOrgCPW);
hook_by_code("ntdll.dll", "ZwQuerySystemInformation",
(PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);
break;

case DLL_PROCESS_DETACH :
// unhook
unhook_by_code("kernel32.dll", "CreateProcessA",
g_pOrgCPA);
unhook_by_code("kernel32.dll", "CreateProcessW",
g_pOrgCPW);
unhook_by_code("ntdll.dll", "ZwQuerySystemInformation",
g_pOrgZwQSI);
break;
}

return TRUE;
}

热补丁

前面提到过一句,反复进行的脱钩挂钩不仅会造成整体性能低下,更严重的是多线程环境下还会产生运行时错误,这是因为脱钩/挂钩操作要对前五个字节进行修改(覆盖)引起的。

《Windows核心编程》一书指出,运行代码修改技术钩取API会对系统安全造成威胁

使用热补丁(Hot Patch)技术比修改5个字节的方法更稳定

首先看看常用API的起始代码部分

image-20220515171347149

会发现头七个字节都是无用指令(解惑我当初看这些API的迷惑了)

Q: 所以微软为什么要这些无用代码?

A: 原因就是方便打热补丁

Q: 所以我们该如何打热补丁进行钩取?

A: 二级跳转

如何二级跳转,就是在函数入口设置一个跳到五个NOP的头地址,再五个NOP的设置一个跳到HOOK函数即可

热补丁由API钩取组成,在进程处于运行状态时临时更改进程内存中的库文件(重启系统时,修改的目标库文件会被完全取代)

感觉这句意思就是重启后那些钩取的API会自动恢复?

image-20220515172039782

A. 所以说我们可以不用再进行脱钩挂钩函数,原来我们执行脱钩挂钩函数的原因是还要调用原函数

B. 而这些可以打热补丁的函数,头7个字节无意义,所以我们调用(原API地址 + 2)就等同于调用原本函数

这正是热补丁的优点

练习 #3

实验环节

先把stealth3放到system32里,和上次bug差不多,不过能做

image-20220515173131859

开审源码

stealth3.cpp

hook_by_hotpatch

这边就是直接硬编码到API上就好,也不需要进行反复挂钩脱钩操作

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
BOOL hook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew)
{
FARPROC pFunc;
DWORD dwOldProtect, dwAddress;
BYTE pBuf[5] = { 0xE9, 0, };
BYTE pBuf2[2] = { 0xEB, 0xF9 };
PBYTE pByte;

// 获取的该API的地址(首指令也就是mov edi, edi)
pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if( pByte[0] == 0xEB )
return FALSE;

VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);

// 1. NOP (0x90)
// 算出新地址也就是 5条nop后的下条指令地址也就是当前mov edi, edi的地址 于是就是要跳转的地址 减去 当前地址,就是Nop处要设置的跳转大小
dwAddress = (DWORD)pfnNew - (DWORD)pFunc;
memcpy(&pBuf[1], &dwAddress, 4);
memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5);

// 2. MOV EDI, EDI (0x8BFF)
// 往上跳7也就是-7,F9是-7的补码形式
memcpy(pFunc, pBuf2, 2);

VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, dwOldProtect, &dwOldProtect);

return TRUE;
}

NewCreateProcessA

这边也不需要进行脱钩挂钩操作,直接原API地址 + 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
32
33
34
BOOL WINAPI NewCreateProcessA(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL bRet;
FARPROC pFunc;

pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
pFunc = (FARPROC)((DWORD)pFunc + 2);
bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);

if( bRet )
InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

return bRet;
}

于是就是整个stealth3.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
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#include "windows.h"
#include "stdio.h"
#include "tchar.h"

#define STR_MODULE_NAME (L"stealth3.dll")
#define STR_HIDE_PROCESS_NAME (L"notepad.exe")
#define STATUS_SUCCESS (0x00000000L)

typedef LONG NTSTATUS;

typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
BYTE Reserved1[52];
PVOID Reserved2[3];
HANDLE UniqueProcessId;
PVOID Reserved3;
ULONG HandleCount;
BYTE Reserved4[4];
PVOID Reserved5[11];
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);

typedef BOOL (WINAPI *PFCREATEPROCESSA)(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);

typedef BOOL (WINAPI *PFCREATEPROCESSW)(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);

BYTE g_pOrgZwQSI[5] = {0,};

BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect, dwAddress;
BYTE pBuf[5] = {0xE9, 0, };
PBYTE pByte;

pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if( pByte[0] == 0xE9 )
return FALSE;

VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

memcpy(pOrgBytes, pFunc, 5);

dwAddress = (DWORD)pfnNew - (DWORD)pFunc - 5;
memcpy(&pBuf[1], &dwAddress, 4);

memcpy(pFunc, pBuf, 5);

VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

return TRUE;
}

BOOL hook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew)
{
FARPROC pFunc;
DWORD dwOldProtect, dwAddress;
BYTE pBuf[5] = { 0xE9, 0, };
BYTE pBuf2[2] = { 0xEB, 0xF9 };
PBYTE pByte;

// 获取的该API的地址(首指令也就是mov edi, edi)
pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if( pByte[0] == 0xEB )
return FALSE;

VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);

// 1. NOP (0x90)
// 算出新地址也就是 5条nop后的下条指令地址也就是当前mov edi, edi的地址 于是就是要跳转的地址 减去 当前地址,就是Nop处要设置的跳转大小
dwAddress = (DWORD)pfnNew - (DWORD)pFunc;
memcpy(&pBuf[1], &dwAddress, 4);
memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5);

// 2. MOV EDI, EDI (0x8BFF)
// 往上跳7也就是-7,F9是-7的补码形式
memcpy(pFunc, pBuf2, 2);

VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, dwOldProtect, &dwOldProtect);

return TRUE;
}

BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect;
PBYTE pByte;

pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if( pByte[0] != 0xE9 )
return FALSE;

VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

memcpy(pFunc, pOrgBytes, 5);

VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

return TRUE;
}

BOOL unhook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName)
{
FARPROC pFunc;
DWORD dwOldProtect;
PBYTE pByte;
BYTE pBuf[5] = { 0x90, 0x90, 0x90, 0x90, 0x90 };
BYTE pBuf2[2] = { 0x8B, 0xFF };


pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if( pByte[0] != 0xEB )
return FALSE;

VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

// 1. NOP (0x90)
memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5);

// 2. MOV EDI, EDI (0x8BFF)
memcpy(pFunc, pBuf2, 2);

VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

return TRUE;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;

if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
printf("OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}

if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;

// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}

if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}

return TRUE;
}

BOOL InjectDll2(HANDLE hProcess, LPCTSTR szDllName)
{
HANDLE hThread;
LPVOID pRemoteBuf;
DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR);
FARPROC pThreadProc;

pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
MEM_COMMIT, PAGE_READWRITE);
if( pRemoteBuf == NULL )
return FALSE;

WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName,
dwBufSize, NULL);

pThreadProc = GetProcAddress(GetModuleHandleA("kernel32.dll"),
"LoadLibraryW");
hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)pThreadProc,
pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

CloseHandle(hThread);

return TRUE;
}

NTSTATUS WINAPI NewZwQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength)
{
NTSTATUS status;
FARPROC pFunc;
PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
char szProcName[MAX_PATH] = {0,};

unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI);

pFunc = GetProcAddress(GetModuleHandleA("ntdll.dll"),
"ZwQuerySystemInformation");
status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
(SystemInformationClass, SystemInformation,
SystemInformationLength, ReturnLength);

if( status != STATUS_SUCCESS )
goto __NTQUERYSYSTEMINFORMATION_END;

if( SystemInformationClass == SystemProcessInformation )
{
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

while(TRUE)
{
if(pCur->Reserved2[1] != NULL)
{
if(!_tcsicmp((PWSTR)pCur->Reserved2[1], STR_HIDE_PROCESS_NAME))
{
if(pCur->NextEntryOffset == 0)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur;
}

if(pCur->NextEntryOffset == 0)
break;

pCur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pCur + pCur->NextEntryOffset);
}
}

__NTQUERYSYSTEMINFORMATION_END:

hook_by_code("ntdll.dll", "ZwQuerySystemInformation",
(PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);

return status;
}

BOOL WINAPI NewCreateProcessA(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL bRet;
FARPROC pFunc;

pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
pFunc = (FARPROC)((DWORD)pFunc + 2);
bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);

if( bRet )
InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

return bRet;
}

BOOL WINAPI NewCreateProcessW(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL bRet;
FARPROC pFunc;

pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessW");
pFunc = (FARPROC)((DWORD)pFunc + 2);
bRet = ((PFCREATEPROCESSW)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
if( bRet )
InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

return bRet;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
char szCurProc[MAX_PATH] = {0,};
char *p = NULL;

GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
p = strrchr(szCurProc, '\\');
if( (p != NULL) && !_stricmp(p+1, "HideProc2.exe") )
return TRUE;

// change privilege
SetPrivilege(SE_DEBUG_NAME, TRUE);

switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
// hook
hook_by_hotpatch("kernel32.dll", "CreateProcessA",
(PROC)NewCreateProcessA);
hook_by_hotpatch("kernel32.dll", "CreateProcessW",
(PROC)NewCreateProcessW);
hook_by_code("ntdll.dll", "ZwQuerySystemInformation",
(PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);
break;

case DLL_PROCESS_DETACH :
// unhook
unhook_by_hotpatch("kernel32.dll", "CreateProcessA");
unhook_by_hotpatch("kernel32.dll", "CreateProcessW");
unhook_by_code("ntdll.dll", "ZwQuerySystemInformation",
g_pOrgZwQSI);
break;
}

return TRUE;
}

代码修改钩取技术需要注意的的问题

终于做完了这篇的笔记…好久好久好久没写这么长的文章了,上次感觉好像还是unicorn那题,呼呼解决一个大石头。–5.15

DASCTF X SU
🍬
HFCTF2022
🍪

About this Post

This post is written by P.Z, licensed under CC BY-NC 4.0.