写了一个小工具,可以通过Hook Winlogon进程主模块的导入表、延迟导入表来拦截对于User32!ExitWindowsEx函数的调用
整个步骤如下:
1、启动一个进程,注入DLL到Winlogon进程
(1) 因为winlogon进程是system级别进程,所以要让注入进程已管理员方式启动,并使能SeDebugPrivilege权限,这样才可以注入system进程。
(2) 通过进程枚举获取到winlogon进程的PID,这个过程还可以判断session会话,因为同一时刻,系统中可能存在多个winlogon进程。
(3) 打开此进程,可以使用PROCESS_ALL_ACCESS标志。
(4) 在目标进程winlogon进程中申请内存,并写入需要注入的DLL对应的路径名称,写入绝对路径名称。
(5) 通过远线程注入,启动函数为LoadLibraryA(W),传入参数为目标进程中需要注入DLL的路径地址。
2、在DLL的入口函数DllMain中HOOk 导入表,延迟导入表
(1) 通过尝试,在windows 7 x86中,winlogon进程是在导入表中引用了User32模块的ExitWindowsEx函数。
(2) 而在 Windows 10 1809 x64中,winlogon进程是在延迟导入表中引用了User32模块的ExitWindowsEx函数。所以直接对这两个表都进行修复即可
3、Hook函数指向自己写的函数,在自己写的函数中编写功能代码
(1) 通过尝试,Winlogon进程会调用两次ExitWindowsEx函数,调用时使用的标志数据uFlags是不一样的,第一次调用ExitWindowsEx时会向桌面进程发送一些用户注销或者关机消息,用户进程可以在此时处理以便需要保存的内容。所有进程都处理完成后,第一次调用的ExitWindowsEx函数就返回TRUE了,否则返回FALSE。
(2) 如果第一次调用ExitWindowsEx成功,那么就说明桌面进程都同意关机或者注销了,会第二次调用ExitWindowsEx函数,需要意识到第二次调用前,桌面进程都被销毁了,此时如果要显示一个程序窗口,我们需要在桌面"WinSta0\winlogon"上进行显示,指定STARTUPINFOA(W)的lpDesktop指向此字符串。
备注:在windows 7上存在会话隔离,服务进程默认处于会话0(Session 0)下,所以窗口正常情况下都看不见,Windows启动后的第一个Winlogon进程为会话1,Winlogon下创建了工作站WinSta0,在此工作站下创建了桌面"WinSta0\winlogon",登录对话框就是显示在这个桌面上的,当用户成功登录后,显示的桌面为"WinSta0\Default",也就是说一般情况下我们看到的的程序都运行在WinSta0\Default桌面上,Winlogon进程还创建了桌面"WinSta0\Disconnect"。可以通过Process Explorer工具来查看某个进程中的句柄,可以看到类似的桌面都打开了WindowsStation对象,对应的名称为\Sessions\1\Windows\WindowStations\WinSta0 不同的会话对应的会话ID不同,所以WindowsStation对象对应的Name为\Sessions\XXX\Windows\WindowStations\WinSta0。SysinternalsSuite套件中提供了一个工具叫做Desktops.exe,这个工具除了当前默认的桌面外,还可以模拟出3个桌面,查看此进程可以看到共对应四个Desktop对象,分别为:【\Default】、【\Sysinternals Desktop 1】、【\Sysinternals Desktop 2】、【\Sysinternals Desktop 3】,通过Alt 1、2、3、4即可在四个桌面之间快速进行切换。
通过上面的知识,我们可以选择在Winlogon进程第二次调用ExitWindowsEx前,进行拦截,可以弹窗提示告知用户即将关机,或者显示其它消息。因为第一次ExitWindowsEx返回TRUE才会进行第二次调用,表明用户进程都已知晓,如果有未保存文件,此时用户都已知晓并处理过了。
当第一次调用ExitWindowsEx成功后,此时Windows桌面已经进入到了上面所说的WinSta0\winlogon下,所以调用CreateProcessAsUserA(W)时需要指定对应的桌面。
4、需要考虑到我们的DLL是运行在system进程中的,而提示时需要启动一个新的进程,最好不要让这个进程拥有过高权限,所以我们使用CreateProcessAsUserA(W)来创建进程。
通过尝试,在第二次调用ExitWindowsEx前,我们先打开当前进程,因为之后需要复制token,再修改token进程等级。但是在打开当前进程时程序执行失败,所以我们选择在DllMain函数中创建好一个Token进行保存,在调用CreateProcessAsUserA(W)使用这个token就可以了。
注入进程代码如下:
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
|
#include <Windows.h>
#include <iostream>
#include <Psapi.h>
#include <Tlhelp32.h>
#include <sddl.h>
#include <Shlwapi.h>
using namespace std;
#pragma comment (lib,"advapi32.lib")
#pragma comment (lib,"Shlwapi.lib")
VOID InjectToWinLogon()
{
PROCESSENTRY32 entry;
HANDLE snapshot
=
NULL, proc
=
NULL;
entry.dwSize
=
sizeof(PROCESSENTRY32);
snapshot
=
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
INT
pid
=
-
1
;
if
(Process32First(snapshot, &entry))
{
while
(Process32Next(snapshot, &entry))
{
if
(wcscmp(entry.szExeFile, L
"winlogon.exe"
)
=
=
0
)
{
pid
=
entry.th32ProcessID;
break
;
}
}
}
CloseHandle(snapshot);
if
(pid <
0
)
{
/
/
puts(
"[-] Could not find winlogon.exe"
);
return
;
}
proc
=
OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if
(proc
=
=
NULL)
{
DWORD error
=
GetLastError();
puts(
"[-] Failed to open process."
);
printf(
"error %d\n"
, error);
return
;
}
TCHAR buffDll[MAX_PATH]
=
{
0
};
GetModuleFileName(NULL, buffDll, _countof(buffDll));
PathRemoveFileSpec(buffDll);
_tcscat_s(buffDll, _countof(buffDll), L
"\\DllHookExitWindowsEx.dll"
);
LPVOID
buffer
=
VirtualAllocEx(proc, NULL, sizeof(buffDll), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if
(
buffer
=
=
NULL)
{
printf(
"[-] Failed to allocate remote memory"
);
}
if
(!WriteProcessMemory(proc,
buffer
, buffDll, sizeof(buffDll),
0
))
{
puts(
"[-] Failed to write to remote memory"
);
return
;
}
LPTHREAD_START_ROUTINE start
=
(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L
"Kernel32.dll"
),
"LoadLibraryW"
);
HANDLE hthread
=
CreateRemoteThread(proc,
0
,
0
, (LPTHREAD_START_ROUTINE)start,
buffer
,
0
,
0
);
DWORD error
=
GetLastError();
if
(hthread
=
=
INVALID_HANDLE_VALUE)
{
puts(
"[-] Failed to create remote thread"
);
return
;
}
}
void EnableSeDebugPrivilegePrivilege()
{
LUID luid;
HANDLE currentProc
=
OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId());
if
(currentProc)
{
HANDLE TokenHandle
=
NULL;
BOOL
hProcessToken
=
OpenProcessToken(currentProc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle);
if
(hProcessToken)
{
BOOL
checkToken
=
LookupPrivilegeValue(NULL, L
"SeDebugPrivilege"
, &luid);
if
(!checkToken)
{
/
/
std::cout <<
"[+] Current process token already includes SeDebugPrivilege\n"
<< std::endl;
}
else
{
TOKEN_PRIVILEGES tokenPrivs;
tokenPrivs.PrivilegeCount
=
1
;
tokenPrivs.Privileges[
0
].Luid
=
luid;
tokenPrivs.Privileges[
0
].Attributes
=
SE_PRIVILEGE_ENABLED;
BOOL
adjustToken
=
AdjustTokenPrivileges(TokenHandle, FALSE, &tokenPrivs, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL);
if
(adjustToken !
=
0
)
{
/
/
std::cout <<
"[+] Added SeDebugPrivilege to the current process token"
<< std::endl;
}
}
CloseHandle(TokenHandle);
}
}
CloseHandle(currentProc);
}
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
/
/
开启权限 使之可以注入到syetem进程
EnableSeDebugPrivilegePrivilege();
/
/
注入dll
InjectToWinLogon();
getchar();
return
0
;
}
|
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
|
#include <Windows.h>
#include <Psapi.h>
#include <Tlhelp32.h>
#include "warningUser.h"
#include <Shlwapi.h>
#include <stdlib.h>
#include <tchar.h>
#pragma comment (lib,"Shlwapi.lib")
LPVOID _copyNtShutdownSystem
=
NULL;
LPVOID _ExitWindowsExAddTwoByte
=
NULL;
HMODULE _gloDllModule
=
NULL;
#pragma warning(disable:4996)
/
*
__declspec(naked)
*
/
void MyExitWindowsEx()
{
/
*
__asm
{
call testMsgBox;
jmp _ExitWindowsExAddTwoByte
}
*
/
}
typedef
BOOL
(WINAPI
*
FuncExitWindowsEx)(_In_ UINT uFlags, _In_ DWORD dwReason);
FuncExitWindowsEx _OldExitWindowsEx
=
NULL;
HANDLE gloCreateProcessHandle
=
NULL;
BOOL
WINAPI IATHookExitWindowsEx(_In_ UINT uFlags, _In_ DWORD dwReason)
{
BOOL
bRet
=
FALSE;
static
BOOL
bNeedWarning
=
FALSE;
/
/
__asm
int
3
/
/
DebugBreak();
/
*
if
(uFlags &
0x200000
)
/
/
win7 x86可以通过这句来判断是否是第二次调用 通过调试获得的 需要测试
{
}
*
/
if
(bNeedWarning)
{
TCHAR wszProcessName[MAX_PATH]
=
{
0
};
GetModuleFileName(_gloDllModule, wszProcessName, _countof(wszProcessName));
PathRemoveFileSpec(wszProcessName);
_tcscat_s(wszProcessName, _countof(wszProcessName), L
"\\LogOffWillRun.exe"
);
useTokenCreateProcess(gloCreateProcessHandle, wszProcessName);
}
bRet
=
_OldExitWindowsEx(uFlags, dwReason);
if
(bRet)
{
bNeedWarning
=
TRUE;
}
return
bRet;
}
/
/
这是 win7 x86上的 Iniline Hook
void hook_ExitWindowsEx()
{
HMODULE hUser32
=
GetModuleHandle(L
"user32.dll"
);
char
*
pOldExitWindowsEx
=
(char
*
)GetProcAddress(hUser32,
"ExitWindowsEx"
);
char
*
pOldAddr
=
pOldExitWindowsEx;
/
/
00540000
8bff
mov edi, edi
int
iLengthCopy
=
7
;
if
(NULL !
=
pOldAddr)
{
_copyNtShutdownSystem
=
VirtualAlloc(
0
,
1024
, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
char
*
pNewAddr
=
(char
*
)_copyNtShutdownSystem;
char
*
pnop
=
pOldAddr
-
5
;
/
/
有
5
个字节的NOP
char aa
=
*
pOldAddr;
char bb
=
*
(pOldAddr
+
1
);
if
((char)
0x8b
=
=
*
pOldAddr && (char)
0xff
=
=
*
(pOldAddr
+
1
))
{
DWORD oldshutdownProtect
=
0
;
if
(VirtualProtect(pOldAddr
-
5
, iLengthCopy, PAGE_EXECUTE_READWRITE, &oldshutdownProtect))
{
/
/
*
pOldNtShutdownSyetem
=
(char)
0xe9
;
/
/
jmp
*
pOldExitWindowsEx
=
(char)
0xeB
;
/
/
jmp 短跳转
*
(UCHAR
*
)(pOldExitWindowsEx
+
1
)
=
(USHORT)(
-
0x7
);
/
/
addr
*
pnop
=
(char)
0xe9
;
/
/
jmp
*
(
int
*
)(pnop
+
1
)
=
(
int
)MyExitWindowsEx
-
(
int
)(pnop
+
5
);
/
/
addr
_ExitWindowsExAddTwoByte
=
pOldExitWindowsEx
+
2
;
VirtualProtect(pOldAddr
-
5
, iLengthCopy, oldshutdownProtect, NULL);
}
}
}
return
;
}
BYTE
*
getNtHdrs(BYTE
*
pe_buffer)
{
if
(pe_buffer
=
=
NULL)
return
NULL;
IMAGE_DOS_HEADER
*
idh
=
(IMAGE_DOS_HEADER
*
)pe_buffer;
if
(idh
-
>e_magic !
=
IMAGE_DOS_SIGNATURE) {
return
NULL;
}
const
LONG
kMaxOffset
=
1024
;
LONG
pe_offset
=
idh
-
>e_lfanew;
if
(pe_offset > kMaxOffset)
return
NULL;
IMAGE_NT_HEADERS32
*
inh
=
(IMAGE_NT_HEADERS32
*
)((BYTE
*
)pe_buffer
+
pe_offset);
if
(inh
-
>Signature !
=
IMAGE_NT_SIGNATURE)
return
NULL;
return
(BYTE
*
)inh;
}
IMAGE_DATA_DIRECTORY
*
getPeDir(PVOID pe_buffer, size_t dir_id)
{
if
(dir_id >
=
IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
return
NULL;
BYTE
*
nt_headers
=
getNtHdrs((BYTE
*
)pe_buffer);
if
(nt_headers
=
=
NULL)
return
NULL;
IMAGE_DATA_DIRECTORY
*
peDir
=
NULL;
IMAGE_NT_HEADERS
*
nt_header
=
(IMAGE_NT_HEADERS
*
)nt_headers;
peDir
=
&(nt_header
-
>OptionalHeader.DataDirectory[dir_id]);
if
(peDir
-
>VirtualAddress
=
=
NULL) {
return
NULL;
}
return
peDir;
}
bool
FixDelayIATHook(PVOID modulePtr)
{
IMAGE_DATA_DIRECTORY
*
importsDir
=
getPeDir(modulePtr, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
if
(importsDir
=
=
NULL)
return
false;
size_t maxSize
=
importsDir
-
>Size;
size_t impAddr
=
importsDir
-
>VirtualAddress;
IMAGE_DELAYLOAD_DESCRIPTOR
*
lib_desc
=
NULL;
size_t parsedSize
=
0
;
bool
bFound
=
TRUE;
size_t addrExitWindowsEx
=
(size_t)GetProcAddress(GetModuleHandle(L
"User32"
),
"ExitWindowsEx"
);
for
(; parsedSize < maxSize; parsedSize
+
=
sizeof(IMAGE_DELAYLOAD_DESCRIPTOR)) {
lib_desc
=
(IMAGE_DELAYLOAD_DESCRIPTOR
*
)(impAddr
+
parsedSize
+
(ULONG_PTR)modulePtr);
if
(lib_desc
-
>ImportAddressTableRVA
=
=
NULL && lib_desc
-
>ImportNameTableRVA
=
=
NULL)
break
;
LPSTR lib_name
=
(LPSTR)((ULONGLONG)modulePtr
+
lib_desc
-
>DllNameRVA);
size_t call_via
=
lib_desc
-
>ImportAddressTableRVA;
size_t thunk_addr
=
lib_desc
-
>ImportNameTableRVA;
if
(thunk_addr
=
=
NULL) thunk_addr
=
lib_desc
-
>ImportAddressTableRVA;
size_t offsetField
=
0
;
size_t offsetThunk
=
0
;
for
(;; offsetField
+
=
sizeof(IMAGE_THUNK_DATA), offsetThunk
+
=
sizeof(IMAGE_THUNK_DATA))
{
IMAGE_THUNK_DATA
*
fieldThunk
=
(IMAGE_THUNK_DATA
*
)(size_t(modulePtr)
+
offsetField
+
call_via);
IMAGE_THUNK_DATA
*
orginThunk
=
(IMAGE_THUNK_DATA
*
)(size_t(modulePtr)
+
offsetThunk
+
thunk_addr);
if
(
0
=
=
fieldThunk
-
>u1.Function &&
0
=
=
orginThunk
-
>u1.Function)
{
break
;
}
PIMAGE_IMPORT_BY_NAME by_name
=
NULL;
LPSTR func_name
=
NULL;
size_t addrOld
=
NULL;
if
(orginThunk
-
>u1.Ordinal & IMAGE_ORDINAL_FLAG32 || orginThunk
-
>u1.Ordinal & IMAGE_ORDINAL_FLAG64)
/
/
check
if
using ordinal (both x86 && x64)
{
addrOld
=
(size_t)GetProcAddress(LoadLibraryA(lib_name), (char
*
)(orginThunk
-
>u1.Ordinal &
0xFFFF
));
/
/
通过序号也可以获取到 获取低两个字节 也可以获取到函数地址
/
/
printf(
" [V] API %x at %x\n"
, orginThunk
-
>u1.Ordinal, addr);
/
/
fieldThunk
-
>u1.Function
=
addr;
continue
;
}
else
{
by_name
=
(PIMAGE_IMPORT_BY_NAME)(size_t(modulePtr)
+
orginThunk
-
>u1.AddressOfData);
func_name
=
(LPSTR)by_name
-
>Name;
addrOld
=
(size_t)GetProcAddress(LoadLibraryA(lib_name), func_name);
}
/
/
printf(
" [V] API %s at %x\n"
, func_name, addr);
OutputDebugStringA(
"\r\n"
);
OutputDebugStringA(func_name);
/
/
HOOK
if
(strcmpi(func_name,
"ExitWindowsEx"
)
=
=
0
)
{
/
/
DebugBreak();
DWORD dOldProtect
=
0
;
size_t
*
pFuncAddr
=
(size_t
*
)&fieldThunk
-
>u1.Function;
if
(VirtualProtect(pFuncAddr, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dOldProtect))
{
fieldThunk
-
>u1.Function
=
(size_t)IATHookExitWindowsEx;
VirtualProtect(pFuncAddr, sizeof(size_t), dOldProtect, &dOldProtect);
_OldExitWindowsEx
=
(FuncExitWindowsEx)addrExitWindowsEx;
bFound
=
true;
return
bFound;
}
break
;
}
}
}
return
true;
}
bool
FixIATHook(PVOID modulePtr)
{
IMAGE_DATA_DIRECTORY
*
importsDir
=
getPeDir(modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
if
(importsDir
=
=
NULL)
return
false;
size_t maxSize
=
importsDir
-
>Size;
size_t impAddr
=
importsDir
-
>VirtualAddress;
IMAGE_IMPORT_DESCRIPTOR
*
lib_desc
=
NULL;
size_t parsedSize
=
0
;
bool
bFound
=
TRUE;
size_t addrExitWindowsEx
=
(size_t)GetProcAddress(GetModuleHandle(L
"User32"
),
"ExitWindowsEx"
);
for
(; parsedSize < maxSize; parsedSize
+
=
sizeof(IMAGE_IMPORT_DESCRIPTOR)) {
lib_desc
=
(IMAGE_IMPORT_DESCRIPTOR
*
)(impAddr
+
parsedSize
+
(ULONG_PTR)modulePtr);
if
(lib_desc
-
>OriginalFirstThunk
=
=
NULL && lib_desc
-
>FirstThunk
=
=
NULL)
break
;
LPSTR lib_name
=
(LPSTR)((size_t)modulePtr
+
lib_desc
-
>Name);
size_t call_via
=
lib_desc
-
>FirstThunk;
size_t thunk_addr
=
lib_desc
-
>OriginalFirstThunk;
if
(thunk_addr
=
=
NULL)
thunk_addr
=
lib_desc
-
>FirstThunk;
size_t offsetField
=
0
;
size_t offsetThunk
=
0
;
for
(;; offsetField
+
=
sizeof(IMAGE_THUNK_DATA), offsetThunk
+
=
sizeof(IMAGE_THUNK_DATA))
{
IMAGE_THUNK_DATA
*
fieldThunk
=
(IMAGE_THUNK_DATA
*
)(size_t(modulePtr)
+
offsetField
+
call_via);
IMAGE_THUNK_DATA
*
orginThunk
=
(IMAGE_THUNK_DATA
*
)(size_t(modulePtr)
+
offsetThunk
+
thunk_addr);
if
(
0
=
=
fieldThunk
-
>u1.Function &&
0
=
=
orginThunk
-
>u1.Function)
{
break
;
}
PIMAGE_IMPORT_BY_NAME by_name
=
NULL;
LPSTR func_name
=
NULL;
size_t addrOld
=
NULL;
if
(orginThunk
-
>u1.Ordinal & IMAGE_ORDINAL_FLAG32 || orginThunk
-
>u1.Ordinal & IMAGE_ORDINAL_FLAG64)
/
/
check
if
using ordinal (both x86 && x64)
{
addrOld
=
(size_t)GetProcAddress(LoadLibraryA(lib_name), (char
*
)(orginThunk
-
>u1.Ordinal &
0xFFFF
));
/
/
通过序号?
/
/
printf(
" [V] API %x at %x\n"
, orginThunk
-
>u1.Ordinal, addr);
/
/
fieldThunk
-
>u1.Function
=
addr;
/
/
DebugBreak();
continue
;
}
else
{
by_name
=
(PIMAGE_IMPORT_BY_NAME)(size_t(modulePtr)
+
orginThunk
-
>u1.AddressOfData);
func_name
=
(LPSTR)by_name
-
>Name;
addrOld
=
(size_t)GetProcAddress(LoadLibraryA(lib_name), func_name);
}
/
/
printf(
" [V] API %s at %x\n"
, func_name, addr);
OutputDebugStringA(
"\r\n"
);
OutputDebugStringA(func_name);
/
/
HOOK
if
(strcmpi(func_name,
"ExitWindowsEx"
)
=
=
0
)
{
/
/
DebugBreak();
DWORD dOldProtect
=
0
;
size_t
*
pFuncAddr
=
(size_t
*
)&fieldThunk
-
>u1.Function;
if
(VirtualProtect(pFuncAddr, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dOldProtect))
{
fieldThunk
-
>u1.Function
=
(size_t)IATHookExitWindowsEx;
VirtualProtect(pFuncAddr, sizeof(size_t), dOldProtect, &dOldProtect);
_OldExitWindowsEx
=
(FuncExitWindowsEx)addrExitWindowsEx;
bFound
=
true;
return
bFound;
}
}
}
}
return
true;
}
BOOL
APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
/
/
DebugBreak();
_gloDllModule
=
hModule;
gloCreateProcessHandle
=
getMediumProcessToken();
HMODULE exeModule
=
GetModuleHandle(NULL);
FixIATHook(exeModule);
FixDelayIATHook(exeModule);
break
;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
{
if
(gloCreateProcessHandle !
=
NULL)
{
CloseHandle(gloCreateProcessHandle);
gloCreateProcessHandle
=
NULL;
}
}
break
;
}
return
TRUE;
}
|
其中引用了warningUser.h文件内容如下:
1
2
|
HANDLE getMediumProcessToken();
void useTokenCreateProcess(HANDLE hToken, TCHAR
*
szProcessName);
|
源文件warningUser.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
|
#include "warningUser.h"
#include <Windows.h>
#include <sddl.h>
#include <tchar.h>
#include <Shlwapi.h>
#include <stdlib.h>
#pragma comment(lib, "Advapi32.lib")
#pragma comment (lib,"Shlwapi.lib")
HANDLE getMediumProcessToken()
{
WCHAR
*
wszIntegritySid
=
L
"S-1-16-8192"
;
/
/
CreateIntegritySidProcess(L
"S-1-16-4096"
);
/
/
low权限进程
/
/
CreateIntegritySidProcess(L
"S-1-16-8192"
);
/
/
medium权限进程
/
/
CreateIntegritySidProcess(L
"S-1-16-12288"
);
/
/
high权限进程
/
/
CreateIntegritySidProcess(L
"S-1-16-16384"
);
/
/
system权限进程
HANDLE mediumToken
=
NULL;
HANDLE hToken
=
NULL;
HANDLE hNewToken
=
NULL;
PSID pIntegritySid
=
NULL;
TOKEN_MANDATORY_LABEL TIL
=
{
0
};
__try
{
if
(FALSE
=
=
OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &hToken))
{
__leave;
}
if
(FALSE
=
=
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL,
SecurityImpersonation, TokenPrimary, &hNewToken))
{
__leave;
}
if
(FALSE
=
=
ConvertStringSidToSid(wszIntegritySid, &pIntegritySid))
{
__leave;
}
TIL.Label.Attributes
=
SE_GROUP_INTEGRITY;
TIL.Label.Sid
=
pIntegritySid;
/
/
Set
the process integrity level
if
(FALSE
=
=
SetTokenInformation(hNewToken, TokenIntegrityLevel, &TIL,
sizeof(TOKEN_MANDATORY_LABEL)
+
GetLengthSid(pIntegritySid)))
{
__leave;
}
mediumToken
=
hNewToken;
}
__finally
{
if
(NULL !
=
pIntegritySid)
{
LocalFree(pIntegritySid);
pIntegritySid
=
NULL;
}
if
(NULL !
=
hToken)
{
CloseHandle(hToken);
hToken
=
NULL;
}
}
return
mediumToken;
}
void useTokenCreateProcess(HANDLE hToken, TCHAR
*
szProcessName)
{
/
/
LogOffWillRun
/
/
WCHAR wszProcessName[MAX_PATH]
=
L
"C:\\Windows\\System32\\CMD.exe"
;
PROCESS_INFORMATION ProcInfo
=
{
0
};
STARTUPINFO StartupInfo
=
{
0
};
StartupInfo.cb
=
sizeof(STARTUPINFO);
/
/
si.dwXSize
=
120
;
/
/
StartupInfo.lpDesktop
=
L
"WinSta0\\Default"
;
StartupInfo.lpDesktop
=
L
"WinSta0\\winlogon"
;
StartupInfo.dwFlags
=
STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow
=
SW_SHOWNORMAL;
BOOL
bRet
=
CreateProcessAsUser(hToken, NULL,
szProcessName, NULL, NULL, FALSE,
0
, NULL, NULL, &StartupInfo, &ProcInfo);
if
(bRet)
{
WaitForSingleObject(ProcInfo.hProcess, INFINITE);
}
if
(ProcInfo.hProcess)
{
CloseHandle(ProcInfo.hProcess);
ProcInfo.hProcess
=
NULL;
}
if
(ProcInfo.hThread)
{
CloseHandle(ProcInfo.hThread);
ProcInfo.hThread
=
NULL;
}
}
|
在提示进程LogOffWillRun中,代码很简单,进行提示,如下
1
2
3
4
5
6
7
8
|
#include <Windows.h>
#pragma comment(lib, "User32.lib")
int
_tmain(
int
argc, _TCHAR
*
argv[])
{
MessageBox(GetConsoleWindow(), _TEXT(
"调用了ExitWindowsEx进行关机或者注销"
), _TEXT(
"提示"
), MB_OK);
return
0
;
}
|
代码基本到此结束,运行注入DLL的进程时需要以管理员权限运行。
winlogon调用两次ExitWindowsEx后,wininit进程会去调用ntdll!NtShutDownSystem进程进行关机。在win 10 1809 x64上,发现wininit会进行第三次调用ExitWindowsEx函数。本来想远线程注入wininit进程,但是失败,错误代码为8,含义为内存资源不足,无法处理此命令。
还有一个想法是Hook wininit进程,具体如下:
1、打开wininit进程
2、远程修改导入表,Hook NtShutDownSystem
(1) 可以通过在本进程中调用LoadLibraryExA(W)展开wininit的PE文件,定位到NtShutDownSystem函数对应的地址,通过此地址与内存中的wininit映像计算一个偏移。
(2)枚举wininit进程模块,获取主进程基地址,然后通过上一步的偏移量来修改导入表或者延迟导入表。
(3)修改地址为一段shellcode的起始地址,并保留原来wininit中导入表的内容
a、远程申请一段空间,可读可写可执行,这段空间包括两部分内容,第一部分是数据,第二部分是代码。
b、数据是原来导入表的内容,也可以写入其它数据。
c、代码可以使用PIC_Bindshell项目,然后自己编写C语言函数,编写一段shellcode,引用之前的数据(可以通过call pop重定位或者生成shellcode后,通过每次修改shellcode硬编码来引用数据)。
3、修改导入表后,Hook函数指针指向我们自己的shellcode(如果数据与代码在连续内存,记得加上偏移,使指针指向shellcode起始地址),当wininit调用NtShutDownSystem时会先调用我们自己的函数,使用PIC_Bindshell编写代码可以加载我们自己的动态库,调用动态库导出函数来启动一个进程提示用户关机了。
备注:winlogon进程调用两次ExitWindowsEx后程序就退出了,如果我们选择注销当前用户,当前winlogon结束,会启动新的winlogon,此时没有我们注入的动态库,即可以将注入器写成服务进程,后台枚举winlogon进程,注入DLL。或者HOOk Wininit进程。也可以在桌面进程中监听窗口消息WM_QUERYENDSESSION。或者在服务进程中通过RegisterServiceCtrlHandlerA(W)注册一个回调函数,在回调函数中处理SERVICE_CONTROL_SHUTDOWN请求。
2022-12-28添加:在Hook的ExitWindowsEx函数中也可以通过MessageBox来提示用户,如下:
1
2
3
4
|
if
(bNeedWarning)
{
MessageBox(NULL, _TEXT(
"弹框提示"
), _TEXT(
"提示"
), MB_OK);
}
|
效果如下:
如果是通过启动一个新进程的方式,优点是更加灵活,自己编写新进程,不需要修改DLL。
更多【拦截Windows关机消息】相关视频教程:www.yxfzedu.com