一、前言
在写这篇博客的前,我考虑到实现这个功能需要完成的步骤应该不是很复杂,而且有很多相关的方法早已实现,但在学习过程后,我想将相关的注意点和细节整理在一起以备以后查阅。对于窗口程序和控制台程序来讲,他们的标题栏右上角都会存在一个关闭按钮,按照规则这个关闭按钮应该属于当前运行的进程,但是在最近的Windows上却不一定是这样(比如Win11的终端程序),他们的窗口进程和命令进程是隔离的,其中GUI窗口在UWP程序WindowsTerminal.exe中实现,命令则在当前进程的子进程conhost.exe下完成。而WindowsTerminal.exe完全是由services.exe运行的,我们的命令进程可以是其他任何父进程,比如常规的explorer.exe进程。这看上去像是专门为了窗口设计了一套外壳一样。再提升的管理员进程下创建时,则进行多个旁路,旁路的实现有所不同。这一套机制为通过修改代码实现禁用/拦截标题栏按钮造成了一定困难。
二、方法概述
2.1 关于 Detours 库
Detours 是 Microsoft 开源的一个钩子库,下载地址为:http://research.microsoft.com/en-us/projects/detours/,它具有两方面的功能:
1)拦截x86/x64机器上的任意的Win32 API函数。
2)插入任意的数据段到PE文件中,修改DLL文件的导入表。
2.2.1 如何使用挂钩例程
使用 DetoursFindFunction 函数,并制定第一个参数为 链接库友好名称,第二个参数为所要查找的导出函数名称,通过查找,我们可以得到该函数的入口地址。
然后,使用 DetoursAttach 函数在入口点之后写入 E9 JMP 到我们的钩子函数入口处,这样子我们就可以将函数的处理发送到我们的函数上,进而可以对参数进行过滤。
2.2.2 如何卸载钩子函数
使用 DetoursDetach 函数可以将函数入口处的机器码恢复原状,使得函数上下文脱钩。
2.3 研究窗口关闭消息
可以使用 APIMonitor v2 分析进程关闭按钮被点击后进程调用的 API 链,经过分析,窗口的关闭按钮被点击的时候,首先发送 SC_CLOSE 消息到窗口消息队列,然后发送 WM_CLOSE。
一般在处理 WM_CLOSE 的时候已经迟了,有些程序会在此之前处理 SC_CLOSE 并销毁成员函数以及变量,导致即使之后继续拦截 WM_CLOSE 也不会带来很好的效果。
这里我们通过 拦截通用处理函数 DefWindowProc 并过滤 SC_CLOSE 消息实现拦截窗口人为关闭操作,并弹出问询是否关闭的对话框,从而防止窗口被立即关闭。 (如果不是编译好的程序,可以直接在窗口的回调函数中直接处理这条消息)
MessageBoxIndirect 函数允许我们设置自定义的对话框图标,这里用 LoadIcon 加载系统默认图标,从而实现对话框的自定义。
实际测试过程中,发现在 Win10/11 上,新的终端控制台代替了传统的 Conhost 作为 Console 的外壳进程,所以一个控制台进程在以管理员权限运行时,是传统控制台,而普通用户模式则是终端窗口,这两个情况下的挂钩需要不一样的操作:(1)终端模式下Dll必须注入到 WindowsTerminal 中,conhost 模式下注入到子进程 conhost.exe 中;(2)终端模式下,窗口类名为 CASCADIA_HOSTING_WINDOW_CLASS ,conhost模式下类名为 ConsoleWindowClass。
三、编写代码
附:(完整代码)
1.以下为钩子函数的样例代码,生成动态链接库即可,测试时dll名称为terhonkcore.dll。
#include "pch.h"
#include "detours.h"
#include <tlhelp32.h>
#include <stdio.h>
#pragma comment(lib, "detours.lib")
#pragma data_seg("Shared")
bool IsUnload = FALSE;
int RunTimeStatus = 0;
int GlobalParentProcessId = 0;
#pragma data_seg()
#pragma comment(linker,"/section:Shared,rws")
#pragma comment(lib, "User32.lib")
PVOID g_pOldDefWindowProcW = NULL;
HMODULE hSyncCenter = NULL;
int CloseMsgShowMode = 0;// 关闭动作发生时提示窗口前一个状态的记录
bool IsOpenMode = true;
extern "C" __declspec(dllexport) int APIENTRY InstallHook();
extern "C" __declspec(dllexport) int APIENTRY UnInstallHook();
DWORD WINAPI BackgroundCheckSegment(LPVOID arg);
LRESULT WINAPI MyDefWindowProcW(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
typedef LRESULT(WINAPI* PfuncDefWindowProcW)(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
HANDLE hThread = NULL;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
DisableThreadLibraryCalls(hModule);
//SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
errno_t err = 0;
char fileName[100] = { 0 };
char processFullName[MAX_PATH] = { 0 };
char processName[0x40] = { 0 };
DWORD dwpid = 0;
char* tmp1 = NULL;
char* tmp2 = NULL;
dwpid = GetCurrentProcessId();
GetModuleFileNameA(NULL, processFullName, MAX_PATH); //进程完整路径
tmp1 = strrchr((char*)processFullName, '\\');
tmp2 = strrchr((char*)processFullName, '.');
memcpy(processName, tmp1 + 1, min(tmp2 - tmp1 - 1, 0x40)); //截取得进程名
if (!strcmp(processName, "WindowsTerminal") ||
!strcmp(processName, "conhost"))// 防止错误注入
{
hThread = CreateThread(NULL, 0,
BackgroundCheckSegment, 0, 0, NULL); // 创建线程
RunTimeStatus = InstallHook();
}
break;
}
case DLL_PROCESS_DETACH:
{
if (hThread != 0)
{
CloseHandle(hThread);
}
if(RunTimeStatus == 1)
RunTimeStatus = UnInstallHook();
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) int APIENTRY InstallHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
g_pOldDefWindowProcW = DetourFindFunction("user32.dll", "DefWindowProcW");
DetourAttach(&g_pOldDefWindowProcW, MyDefWindowProcW);
LONG ret = DetourTransactionCommit();
return ret == 1;
}
extern "C" __declspec(dllexport) int APIENTRY UnInstallHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&g_pOldDefWindowProcW, MyDefWindowProcW);
LONG ret = DetourTransactionCommit();
return ret == 2;
}
DWORD WINAPI BackgroundCheckSegment(LPVOID arg)
{
int nofoundCountedflag = 0;
bool IsNoFoundProc = true;
while (!IsUnload && nofoundCountedflag < 3)
{
if (GlobalParentProcessId == 0) {
nofoundCountedflag++;
break;
}
IsNoFoundProc = true;
HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe = { 0 };
pe.dwSize = sizeof(PROCESSENTRY32W);
if (Process32FirstW(hp, &pe)) {
do {
if (pe.th32ProcessID == GlobalParentProcessId) {
IsNoFoundProc = false;
break;
}
} while (Process32NextW(hp, &pe));
}
if (IsNoFoundProc == true)
nofoundCountedflag++;
CloseHandle(hp);
Sleep(1000);
}
UnInstallHook();// 执行自动清理
if (CloseMsgShowMode != 0)
{
HWND hForeWnd = NULL;
HWND Msgwnd = FindWindowA("#32770", "WindowsTerminal Runtime");
DWORD dwForeID;
DWORD dwCurID;
hForeWnd = GetForegroundWindow();
dwCurID = GetCurrentThreadId();
dwForeID = GetWindowThreadProcessId(hForeWnd, NULL);
AttachThreadInput(dwCurID, dwForeID, TRUE);
ShowWindow(Msgwnd, SW_SHOWNORMAL);
SetWindowPos(Msgwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
SetWindowPos(Msgwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
SetForegroundWindow(Msgwnd);
AttachThreadInput(dwCurID, dwForeID, FALSE);
Msgwnd = FindWindowExA(Msgwnd, NULL, "Button", NULL);// 第一个按钮,默认确认
//if(IsUnload == TRUE)// 区别正常关闭,与强制关闭
Msgwnd = GetNextWindow(Msgwnd, GW_HWNDNEXT);// 第二个按钮,默认为取消
if (Msgwnd != NULL) {
SendMessageW(Msgwnd, WM_LBUTTONDOWN, 0, 0);
SendMessageW(Msgwnd, WM_LBUTTONUP, 0, 0);
}
}
return 1;
}
extern "C" __declspec(dllexport) void SetStatusData(bool bEnable)
{
IsUnload = bEnable;
}
extern "C" __declspec(dllexport) void SetParentData(int dwParentProcessId)
{
GlobalParentProcessId = dwParentProcessId;
}
extern "C" __declspec(dllexport) void GetStatusData(int* bStatus, int index)
{
if (index == 1)
*bStatus = IsUnload;
else if (index == 2)
*bStatus = RunTimeStatus;
}
struct ThreadInfo
{
HWND hWnd;
UINT Msg;
WPARAM wParam;
LPARAM lParam;
};
DWORD WINAPI CreateMsgWindow(LPVOID arg)
{
ThreadInfo* tdinfo = (ThreadInfo*)arg;
CloseMsgShowMode = 2;
MSGBOXPARAMSA megboxparam{};
megboxparam.cbSize = sizeof(MSGBOXPARAMSA);
megboxparam.hwndOwner = NULL;
megboxparam.hInstance = hSyncCenter;
megboxparam.lpszText = "确定要关闭终端窗口吗?";
megboxparam.lpszIcon = MAKEINTRESOURCEA(1004);
megboxparam.dwStyle = MB_YESNO | MB_APPLMODAL | MB_USERICON;
megboxparam.lpszCaption = "WindowsTerminal Runtime";
megboxparam.dwContextHelpId = NULL;
megboxparam.lpfnMsgBoxCallback = NULL;
megboxparam.dwLanguageId = LANG_SYSTEM_DEFAULT;
MessageBeep(MB_ICONSTOP);
if (IDYES == MessageBoxIndirectA(&megboxparam))
{
CloseMsgShowMode = 0;
((PfuncDefWindowProcW)g_pOldDefWindowProcW)(
tdinfo->hWnd, tdinfo->Msg, tdinfo->wParam, tdinfo->lParam);
}
else CloseMsgShowMode = 0;
return 0;
}
LRESULT WINAPI MyDefWindowProcW(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
) {
if ((Msg == WM_SYSCOMMAND && wParam == SC_CLOSE)) {
TCHAR szBuf_title[MAX_PATH];
TCHAR szBuf_class[MAX_PATH];
int retBufferSize_r1 = GetWindowTextW(hWnd, szBuf_title, MAX_PATH);
int retBufferSize_r2 = GetClassNameW(hWnd, szBuf_class, MAX_PATH);
if (retBufferSize_r2 > 0)
{
if ((CloseMsgShowMode != 2) &&
(!wcscmp(szBuf_class, L"CASCADIA_HOSTING_WINDOW_CLASS")
|| !wcscmp(szBuf_class, L"ConsoleWindowClass"))
)// 独立页面
{
CloseMsgShowMode = 2;
hSyncCenter = LoadLibraryA("SyncCenter.dll");
if (hSyncCenter == nullptr)
{
if (IDYES == MessageBoxA(NULL, "确定要关闭终端窗口吗?",
"WindowsTerminal Runtime", MB_YESNO | MB_ICONERROR | MB_APPLMODAL))
{
CloseMsgShowMode = 0;
return ((PfuncDefWindowProcW)g_pOldDefWindowProcW)(
hWnd, Msg, wParam, lParam);
}
else { CloseMsgShowMode = 0; return 0x10086; }
}
HICON hIcon = LoadIconW(hSyncCenter, MAKEINTRESOURCE(1203));
ThreadInfo* tdinfo = new ThreadInfo;
tdinfo->hWnd = hWnd;
tdinfo->Msg = Msg;
tdinfo->wParam = wParam;
tdinfo->lParam = lParam;
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateMsgWindow,
tdinfo, 0, NULL);
HWND Msgwnd = NULL;
int Count = 0;
while (Msgwnd == NULL && Count < 99999999)
{
Count++;
Msgwnd = FindWindowA("#32770", "WindowsTerminal Runtime");
Sleep(10);
}
if (hIcon != NULL) {
ShowWindow(Msgwnd, SW_HIDE);
// 设置窗口的大小图标
// 大图标:按下alt+tab键切换窗口时对应的图标
// 小图标:就是窗口左上角对应的那个图标
PostMessageA(Msgwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
PostMessageA(Msgwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
ShowWindow(Msgwnd, SW_SHOW);
}
return 0x10086;// SiGN OK!
}
else {
if (CloseMsgShowMode == 2)
{
HWND hForeWnd = NULL;
HWND hWnd = FindWindowA("#32770", "WindowsTerminal Runtime");
DWORD dwForeID;
DWORD dwCurID;
hForeWnd = GetForegroundWindow();
dwCurID = GetCurrentThreadId();
dwForeID = GetWindowThreadProcessId(hForeWnd, NULL);
AttachThreadInput(dwCurID, dwForeID, TRUE);
ShowWindow(hWnd, SW_SHOWNORMAL);
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
MessageBeep(MB_ICONSTOP);
SetForegroundWindow(hWnd);
AttachThreadInput(dwCurID, dwForeID, FALSE);
}
return 0x10085;// 防止多次弹出窗口
}
}
}
return ((PfuncDefWindowProcW)g_pOldDefWindowProcW)(
hWnd, Msg, wParam, lParam);
}
2. 以下为测试程序的代码。
#include <iostream>
#include <conio.h>
#include <Windows.h>
#include <vector>
#include <tlhelp32.h>
#include <pathcch.h>
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "Pathcch.lib")
typedef VOID(NTAPI* pNtTestAlert)(VOID);
BOOL IsRunAsAdmin();
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId,
const wchar_t* pszDllFileName);
typedef void(__SetStatusData)(bool bEnable);
typedef void(__SetParentData)(int dwParentProcessId);
int main()
{
SetConsoleTitleW(L"TestConsole");
wchar_t szDllPath[MAX_PATH] = { 0 };
GetModuleFileNameW(NULL, szDllPath, MAX_PATH);
PathCchRemoveFileSpec(szDllPath, MAX_PATH);
wcscat_s(szDllPath, L"\\terhonkcore.dll");
SIZE_T dwDllPathLen = (wcslen(szDllPath) + 1) * sizeof(wchar_t);
printf("补丁文件路径: %ws\n", szDllPath);
HWND hTerminalwnd = NULL;
DWORD dwTerminalproc = 0;
HMODULE hNeroCore = NULL;
__SetStatusData* SetStatusData = nullptr;
__SetParentData* SetParentData = nullptr;
hNeroCore = LoadLibraryW(szDllPath);
if (hNeroCore != NULL)
{
SetParentData = (__SetParentData*)GetProcAddress(hNeroCore, "SetParentData");
SetStatusData = (__SetStatusData*)GetProcAddress(hNeroCore, "SetStatusData");
if (SetParentData == NULL || SetStatusData == NULL)
{
printf("初始化运行环境失败。\n");
return -1;
}
SetParentData(GetCurrentProcessId());
SetStatusData(FALSE);
}
else {
printf("初始化运行环境失败。\n");
return -1;
}
if (!IsRunAsAdmin())
{
std::vector<DWORD> wndList;
TCHAR szTitle[MAX_PATH] = { 0 };
TCHAR szClass[MAX_PATH] = { 0 };
hTerminalwnd = FindWindowW(L"CASCADIA_HOSTING_WINDOW_CLASS", L"TestConsole");
/**if (!hTerminalwnd)
{
// Err
printf("E1\n");
std::cin.get();
return 0;
}*/
int Count = 0;
while (hTerminalwnd == NULL && Count < 99999999)
{
Count++;
hTerminalwnd = FindWindowW(L"CASCADIA_HOSTING_WINDOW_CLASS", L"TestConsole");
Sleep(1);
}
GetWindowThreadProcessId(hTerminalwnd, &dwTerminalproc);
if (dwTerminalproc != NULL)
{
wndList.push_back(dwTerminalproc);
}
// 解决多开问题
HWND hWin32NextWnd = GetWindow(hTerminalwnd, GW_HWNDPREV);
while (hWin32NextWnd != NULL)
{
memset(szTitle, 0, sizeof(szTitle));
memset(szClass, 0, sizeof(szClass));
GetWindowTextW(hWin32NextWnd, szTitle,
sizeof(szTitle) / sizeof(TCHAR)); // 获取窗口名称
GetClassNameW(hWin32NextWnd, szClass,
sizeof(szClass) / sizeof(TCHAR)); // 窗口类
Sleep(1);
if (!wcscmp(szTitle, L"TestConsole")
&& !wcscmp(szClass, L"CASCADIA_HOSTING_WINDOW_CLASS"))
{
// 判断窗口标题不为空,并且窗口可见
//wndList.push_back(hWin32NextWnd); // 添加到临时list中
dwTerminalproc = 0;
GetWindowThreadProcessId(hWin32NextWnd, &dwTerminalproc);
if (dwTerminalproc != NULL)
{
wndList.push_back(dwTerminalproc);
}
}
hWin32NextWnd = GetWindow(hWin32NextWnd, GW_HWNDPREV);
}
/**
GetWindowThreadProcessId(hTerminalwnd, &dwTerminalproc);
if (!dwTerminalproc)
{
// Err
printf("E2\n");
std::cin.get();
return 0;
}
*/
for (int i = 0; i < wndList.size(); i++) {
ZwCreateThreadExInjectDll(wndList[i], szDllPath);
}
// 销毁过程
wndList.clear();
wndList.shrink_to_fit();
}
else
{
std::vector<DWORD> pids;
HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe = { 0 };
pe.dwSize = sizeof(PROCESSENTRY32W);
if (Process32FirstW(hp, &pe)) {
do {
if (pe.th32ParentProcessID == GetCurrentProcessId()) {
pids.push_back(pe.th32ProcessID);
}
} while (Process32NextW(hp, &pe));
}
CloseHandle(hp);
if (pids.size() == 0)
{
hTerminalwnd = FindWindowW(L"CASCADIA_HOSTING_WINDOW_CLASS", L"TestConsole");
int Count = 0;
while (hTerminalwnd == NULL && Count < 99999999)
{
Count++;
hTerminalwnd = FindWindowW(L"CASCADIA_HOSTING_WINDOW_CLASS", L"TestConsole");
Sleep(1);
}
if (hTerminalwnd == NULL)
{
// Err
printf("错误:窗口链接超时。\n");
std::cin.get();
return 0;
}
GetWindowThreadProcessId(hTerminalwnd, &dwTerminalproc);
if (!dwTerminalproc)
{
// Err
printf("错误:无法获取目标PID。\n");
std::cin.get();
return 0;
}
//printf("dwTerminalproc: %d\n", dwTerminalproc);
if (!ZwCreateThreadExInjectDll(dwTerminalproc, szDllPath))
printf("错误:创建进程保护失败。\n");
}
else {
for (int i = 0; i < pids.size(); i++) {
ZwCreateThreadExInjectDll(pids[i], szDllPath);
}
}
// 销毁过程
pids.clear();
pids.shrink_to_fit();
//ShowWindow(tpwnd, SW_SHOW);
}
char p[20]= { 0 };
printf("操作已完成。\n");
scanf_s("%s", &p, 20);
while (_stricmp(p, "continue"))
{
std::cin.ignore((std::numeric_limits< std::streamsize >::max)(), '\n');
printf("使用 continue 命令继续操作。\n");
scanf_s("%s", &p, 20);
}
printf("正在解除保护......\n");
SetParentData(0);
SetStatusData(TRUE);
printf("操作已完成。\n");
while (_stricmp(p, "exit"))
{
std::cin.ignore((std::numeric_limits< std::streamsize >::max)(), '\n');
printf("使用 exit 命令继续操作。\n");
scanf_s("%s", &p, 20);
}
return 0;
}
BOOL IsRunAsAdmin() //判断是否管理员模式
{
BOOL bIsElevated = FALSE;
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
struct {
DWORD TokenIsElevated;
}te{};
DWORD dwReturnLength = 0;
if (GetTokenInformation(hToken,
(_TOKEN_INFORMATION_CLASS)20,
&te, sizeof(te), &dwReturnLength))
{
if (dwReturnLength == sizeof(te))
bIsElevated = te.TokenIsElevated;
}
CloseHandle(hToken);
}
return bIsElevated;
}
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, const wchar_t* pszDllFileName)
{
size_t pathSize = (wcslen(pszDllFileName) + 1) * sizeof(wchar_t);
// 1.打开目标进程
HANDLE hProcess = OpenProcess(
PROCESS_ALL_ACCESS, // 打开权限
FALSE, // 是否继承
dwProcessId); // 进程PID
if (NULL == hProcess)
{
printf("错误:打开目标进程失败!\n");
return FALSE;
}
// 2.在目标进程中申请空间
LPVOID lpPathAddr = VirtualAllocEx(
hProcess, // 目标进程句柄
0, // 指定申请地址
pathSize, // 申请空间大小
MEM_RESERVE | MEM_COMMIT, // 内存的状态
PAGE_READWRITE); // 内存属性
if (NULL == lpPathAddr)
{
printf("错误:在目标进程中申请空间失败!\n");
CloseHandle(hProcess);
return FALSE;
}
// 3.在目标进程中写入Dll路径
if (FALSE == WriteProcessMemory(
hProcess, // 目标进程句柄
lpPathAddr, // 目标进程地址
pszDllFileName, // 写入的缓冲区
pathSize, // 缓冲区大小
NULL)) // 实际写入大小
{
printf("错误:目标进程中写入Dll路径失败!\n");
CloseHandle(hProcess);
return FALSE;
}
//4.加载ntdll.dll
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
if (NULL == hNtdll)
{
printf("错误:加载ntdll.dll失败!\n");
CloseHandle(hProcess);
return FALSE;
}
//5.获取LoadLibraryA的函数地址
//FARPROC可以自适应32位与64位
FARPROC pFuncProcAddr = GetProcAddress(GetModuleHandle(L"Kernel32.dll"),
"LoadLibraryW");
if (NULL == pFuncProcAddr)
{
printf("错误:获取LoadLibrary函数地址失败!\n");
CloseHandle(hProcess);
return FALSE;
}
//6.获取ZwCreateThreadEx函数地址,该函数在32位与64位下原型不同
//_WIN64用来判断编译环境 ,_WIN32用来判断是否是Windows系统
#ifdef _WIN64
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown
);
#else
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown
);
#endif
typedef_ZwCreateThreadEx ZwCreateThreadEx =
(typedef_ZwCreateThreadEx)GetProcAddress(hNtdll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx)
{
printf("错误:获取ZwCreateThreadEx函数地址失败!\n");
CloseHandle(hProcess);
return FALSE;
}
//7.在目标进程中创建远线程
HANDLE hRemoteThread = NULL;
DWORD dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL,
hProcess,
(LPTHREAD_START_ROUTINE)pFuncProcAddr, lpPathAddr, 0, 0, 0, 0, NULL);
if (NULL == hRemoteThread)
{
printf("错误:目标进程中创建线程失败!\n");
CloseHandle(hProcess);
return FALSE;
}
// 8.等待线程结束
WaitForSingleObject(hRemoteThread, -1);
// 9.清理环境
VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);
CloseHandle(hRemoteThread);
CloseHandle(hProcess);
FreeLibrary(hNtdll);
return TRUE;
}
四、测试结果
1. 第一种情况:直接运行控制台程序,此时出现终端窗口(win11)
![](https://i-blog.csdnimg.cn/blog_migrate/8d4bd16c349fd46c5bf0665b6d758c8f.png)
2.第二种情况:在已经提升为管理员的终端中运行程序
![](https://i-blog.csdnimg.cn/blog_migrate/9331ea648a8a78912bb2269ccea7a3b7.png)
3. 第三种情况:右键以管理员身份启动,或者在其他管理员进程中继承令牌权限
![](https://i-blog.csdnimg.cn/blog_migrate/718ad2a3f0e337becc0f3076e41d3b64.png)
后记
已知问题:终端模式下无法拦截通过关闭 PaneList (标题栏每个网格上的即时关闭按钮),正在分析微软公开的 Terminal 源代码,试图找到原因。
拦截 SC_CLOSE 并不能拦截通过编程直接发送 WM_CLOSE 来关闭窗口的情况,但是挂钩 WM_CLOSE 不能在 DefWindowProc 里面处理,那时候已经迟了,我们考虑挂钩消息处理函数 GetMessage 但会造成因函数执行时间检查失败而导致进程退出的情况,并且会影响进程效率,这还需要进一步分析。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_59075481/article/details/132949108