API 钩子获取窗口的关闭消息

一、前言

在写这篇博客的前,我考虑到实现这个功能需要完成的步骤应该不是很复杂,而且有很多相关的方法早已实现,但在学习过程后,我想将相关的注意点和细节整理在一起以备以后查阅。对于窗口程序和控制台程序来讲,他们的标题栏右上角都会存在一个关闭按钮,按照规则这个关闭按钮应该属于当前运行的进程,但是在最近的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)

在终端中运行的效果

2.第二种情况:在已经提升为管理员的终端中运行程序

在提升的终端中运行的效果

3. 第三种情况:右键以管理员身份启动,或者在其他管理员进程中继承令牌权限

在提升的进程中运行的效果

后记

已知问题:终端模式下无法拦截通过关闭 PaneList (标题栏每个网格上的即时关闭按钮),正在分析微软公开的 Terminal 源代码,试图找到原因。

拦截 SC_CLOSE 并不能拦截通过编程直接发送 WM_CLOSE 来关闭窗口的情况,但是挂钩 WM_CLOSE 不能在 DefWindowProc 里面处理,那时候已经迟了,我们考虑挂钩消息处理函数 GetMessage 但会造成因函数执行时间检查失败而导致进程退出的情况,并且会影响进程效率,这还需要进一步分析。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_59075481/article/details/132949108

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涟幽516

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值