写了一个小工具,可以通过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就可以了。
注入进程代码如下:
被注入的DLL代码如下:
其中引用了warningUser.h文件内容如下:
源文件warningUser.cpp文件内容为:
在提示进程LogOffWillRun中,代码很简单,进行提示,如下
代码基本到此结束,运行注入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来提示用户,如下:
效果如下:
如果是通过启动一个新进程的方式,优点是更加灵活,自己编写新进程,不需要修改DLL。
using namespace std;
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
;
}
using namespace std;
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
;
}
LPVOID _copyNtShutdownSystem
=
NULL;
LPVOID _ExitWindowsExAddTwoByte
=
NULL;
HMODULE _gloDllModule
=
NULL;
/
*
__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;
}
LPVOID _copyNtShutdownSystem
=
NULL;
LPVOID _ExitWindowsExAddTwoByte
=
NULL;
HMODULE _gloDllModule
=
NULL;
/
*
__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;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-12-28 13:47
被0346954编辑
,原因: