使用 Winsta API 结束进程句柄

最近发现对于一些进程我们使用 taskkill 并不能终止进程(不是不能而是为了安全被拦截了),而使用 tskill.exe(win10之前系统有)可以直接终止进程,不需要管理员权限打开进程就可以直接结束进程,而且速度比taskkill 快得多。

于是我们拷贝了一份并利用 IDA 逆向分析了一下它是如何运作的,发现主要调用 WinStationTerminateProcess 来终止进程,查找进程我们改用 NtQuerySystemInformation 来完成,算是一种结束进程的方法吧,TerminateProcess 也可以终止进程。这里为了用 WinStationTerminateProcess 实现,遂简单写了一下实现代码。

函数定义:

BOOLEAN (WINAPI* WinStationTerminateProcess)(
    _In_opt_ HANDLE     hServer,
    _In_ ULONG              ProcessId,
    _In_ ULONG              ExitCode
);

我们发现只要提供进程的 PID 就可以结束进程,避免了调用 OpenProcess,第一个参数指定目标工作站,如果为 NULL,表示搜索所有工作站。

完整代码:

#include <iostream>
#include <windows.h>
#include <shellscalingapi.h>

#pragma comment(lib, "Shcore.lib")

typedef HANDLE (WINAPI* MyWinStationOpenServerW)(
    _In_ PWSTR 	ServerName
);

typedef BOOLEAN (WINAPI* MyWinStationTerminateProcess)(
    _In_opt_ HANDLE 	hServer,
    _In_ ULONG 	ProcessId,
    _In_ ULONG 	ExitCode
);

DWORD WinstaEnumerateProcess(PWSTR wsTargetProcess);
BOOL WinstaEnumerateSutdownProcess(PWSTR hServer, PWSTR wsTargetProcess, ULONG ExitCode);


PVOID fpWinStationOpenServerW = NULL;
PVOID fpWinStationTerminateProcess = NULL;
HANDLE hServerSign = NULL;


#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#define STATUS_SUCCESS ((NTSTATUS) 0x00000000)
#define SystemProcessInformation    5 // 功能号
#define NTAPI    __stdcall

typedef LONG   NTSTATUS;
typedef LONG    KPRIORITY;

typedef struct _UNICODE_STRING
{
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _CLIENT_ID
{
    DWORD        UniqueProcess;
    DWORD        UniqueThread;
} CLIENT_ID, * PCLIENT_ID;


typedef struct _VM_COUNTERS
{
    SIZE_T        PeakVirtualSize;
    SIZE_T        VirtualSize;
    ULONG         PageFaultCount;
    SIZE_T        PeakWorkingSetSize;
    SIZE_T        WorkingSetSize;
    SIZE_T        QuotaPeakPagedPoolUsage;
    SIZE_T        QuotaPagedPoolUsage;
    SIZE_T        QuotaPeakNonPagedPoolUsage;
    SIZE_T        QuotaNonPagedPoolUsage;
    SIZE_T        PagefileUsage;
    SIZE_T        PeakPagefileUsage;
} VM_COUNTERS;

typedef enum _THREAD_STATE
{
    StateInitialized,
    StateReady,
    StateRunning,
    StateStandby,
    StateTerminated,
    StateWait,
    StateTransition,
    StateUnknown
} THREAD_STATE;

typedef enum _KWAIT_REASON
{
    Executive,
    FreePage,
    PageIn,
    PoolAllocation,
    DelayExecution,
    Suspended,
    UserRequest,
    WrExecutive,
    WrFreePage,
    WrPageIn,
    WrPoolAllocation,
    WrDelayExecution,
    WrSuspended,
    WrUserRequest,
    WrEventPair,
    WrQueue,
    WrLpcReceive,
    WrLpcReply,
    WrVirtualMemory,
    WrPageOut,
    WrRendezvous,
    Spare2,
    Spare3,
    Spare4,
    Spare5,
    Spare6,
    WrKernel
} KWAIT_REASON;

typedef struct _SYSTEM_THREAD_INFORMATION_X64
{
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER CreateTime;
    ULONG WaitTime;
    PVOID StartAddress;
    CLIENT_ID ClientId;
    KPRIORITY Priority;
    KPRIORITY BasePriority;
    ULONG ContextSwitchCount;
    THREAD_STATE ThreadState;
    KWAIT_REASON WaitReason;
    ULONG Reserved;
} SYSTEM_THREAD_INFORMATION_X64, * PSYSTEM_THREAD_INFORMATION_X64;



typedef struct _SYSTEM_PROCESS_INFORMATION
{
    ULONG            NextEntryDelta; // 指向下一个结构体的指针
    ULONG            ThreadCount; // 本进程的总线程数
    ULONG            Reserved1[6]; // 保留
    LARGE_INTEGER    CreateTime; // 进程的创建时间
    LARGE_INTEGER    UserTime; // 在用户层的使用时间
    LARGE_INTEGER    KernelTime; // 在内核层的使用时间
    UNICODE_STRING   ProcessName; // 进程名
    KPRIORITY        BasePriority; // 
    ULONG            ProcessId; // 进程ID
    ULONG            InheritedFromProcessId;
    ULONG            HandleCount; // 进程的句柄总数
    ULONG            Reserved2[2]; // 保留
    ULONG_PTR        PageDirectoryBase;
    VM_COUNTERS      VmCounters;
    SIZE_T           PrivatePageCount;
    IO_COUNTERS      IoCounters;
    SYSTEM_THREAD_INFORMATION_X64 Threads[1]; // 子线程信息数组
}SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;

typedef DWORD(WINAPI* MyNtQuerySystemInformation)(
    UINT, PVOID, DWORD, PDWORD);

PVOID fpNtQuerySystemInformation = NULL;


int wmain(int argc, wchar_t* argv[])
{
    SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);

    if (argc != 2)
    {
        return MessageBoxW(NULL,
            L"参数错误",
            L"应用程序错误",
            MB_OK | MB_APPLMODAL | MB_ICONERROR);
    }
    WCHAR* wsTargetProcessName = argv[1];
    PWSTR hServer = new WCHAR[45];

    HMODULE hWinsta = LoadLibraryW(L"winsta.dll");
    if (!hWinsta)
        return MessageBoxW(NULL,  
            L"加载 winsta.dll 动态链接库失败。", 
            L"应用程序错误",
            MB_OK | MB_APPLMODAL | MB_ICONERROR);

    HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
    if (!hNtdll)
        return MessageBoxW(NULL,
            L"加载 ntdll.dll 动态链接库失败。",
            L"应用程序错误",
            MB_OK | MB_APPLMODAL | MB_ICONERROR);
    
    SetLastError(0);
    fpNtQuerySystemInformation = (MyNtQuerySystemInformation)GetProcAddress(
        hNtdll,"NtQuerySystemInformation");
    if (fpNtQuerySystemInformation == NULL || GetLastError() != 0)
        return MessageBoxW(NULL,
            L"无法定位函数 NtQuerySystemInformation 入口点位于动态链接库 ntdll.dll 上。",
            L"应用程序错误",
            MB_OK | MB_APPLMODAL | MB_ICONERROR);

    SetLastError(0);
    fpWinStationOpenServerW =
        (MyWinStationOpenServerW)
        GetProcAddress(hWinsta, "WinStationOpenServerW");
    if (fpWinStationOpenServerW == NULL || GetLastError() != 0)
        return MessageBoxW(NULL, 
            L"无法定位函数 WinStationOpenServerW 入口点位于动态链接库 winsta.dll 上。",
            L"应用程序错误",
            MB_OK | MB_APPLMODAL | MB_ICONERROR);

    SetLastError(0);
    fpWinStationTerminateProcess =
        (MyWinStationTerminateProcess)
        GetProcAddress(hWinsta, "WinStationTerminateProcess");
    if (fpWinStationTerminateProcess == NULL || GetLastError() != 0)
        return MessageBoxW(NULL,
            L"无法定位函数 WinStationTerminateProcess 入口点位于动态链接库 winsta.dll 上。",
            L"应用程序错误", 
            MB_OK | MB_APPLMODAL | MB_ICONERROR);

    wcscpy_s(hServer, 45 * sizeof(WCHAR), L"(null)");

    if (!WinstaEnumerateSutdownProcess(hServer, wsTargetProcessName, 0))
    {
        return MessageBoxW(NULL,
            L"调用 WinstaEnumerateSutdownProcess 时失败。",
            L"应用程序错误",
            MB_OK | MB_APPLMODAL | MB_ICONERROR);
    }
    else {
        printf("操作成功完成!\n");
    }

    //system("pause");
    return 0;
}


BOOL WinstaEnumerateSutdownProcess(
    PWSTR hServer, 
    PWSTR wsTargetProcess, 
    ULONG ExitCode)
{
    BOOLEAN Ret = (unsigned)0;
    DWORD dwProcessId = 0;
    BOOLEAN ret = (unsigned)0;
    if(!wcscmp(hServer, L"(null)"))
        hServerSign = NULL;
    else 
        hServerSign = 
        ((MyWinStationOpenServerW)fpWinStationOpenServerW)
        (hServer);

    
    if (wsTargetProcess == NULL)
    {
        printf("错误:内存不足。\n");
        return 0;
    }
           
    dwProcessId = WinstaEnumerateProcess(wsTargetProcess);

    if (dwProcessId == 0)
    {
        printf("错误:找不到进程名\"%ws\"。\n", wsTargetProcess);
        return FALSE;
    }
    SetLastError(0);
    ret = ((MyWinStationTerminateProcess)fpWinStationTerminateProcess)
        (hServerSign, dwProcessId, ExitCode);

    if (ret == (unsigned)0)
    {
        DWORD Lasterror = GetLastError();
        if (Lasterror == 5)
        {
            printf("错误:结束进程 \"%ws\" 时失败。\n原因:拒绝访问[%d]。\n",
                wsTargetProcess, Lasterror);
            MessageBoxW(NULL,
                L"调用 WinStationTerminateProcess 时失败。",
                L"应用程序错误",
                MB_OK | MB_APPLMODAL | MB_ICONERROR);
        }
        else {
            printf("错误:结束进程 \"%ws\" 时失败。\n原因:[%d]。\n",
                wsTargetProcess, Lasterror);
            MessageBoxW(NULL,
                L"调用 WinStationTerminateProcess 时失败。",
                L"应用程序错误",
                MB_OK | MB_APPLMODAL | MB_ICONERROR);
        }
        return FALSE;// TODO: GetLastError();FormatMessage() Ntdll;MessageBox()
    }
    else return TRUE;
}


DWORD WinstaEnumerateProcess(PWSTR wsTargetProcess)
{
    NTSTATUS status = STATUS_SUCCESS;
    DWORD   dwSize = 0xFFFFF;
    
    DWORD dwNameSize = (DWORD)wcslen(wsTargetProcess) + 1;
    if (dwNameSize <= 0) return FALSE;

    WCHAR* wsTargetName = new WCHAR[dwNameSize];
    WCHAR* wsCurrentName = new WCHAR[MAX_PATH];
    
    PSYSTEM_PROCESS_INFORMATION pInfo = { 0 };
    LPTSTR pBuf = NULL;
    

    pBuf = new TCHAR[dwSize];
    if (NULL == pBuf)
    {
        return FALSE;
    }
    pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuf;

    status = ((MyNtQuerySystemInformation)fpNtQuerySystemInformation)
        (
            SystemProcessInformation,
            pInfo,
            dwSize,
            &dwSize
            );
    if (!NT_SUCCESS(status)) {/*如果函数执行失败*/
        return FALSE;
    }
    // 遍历结构体,找到对应的进程
    while (pInfo->NextEntryDelta)
    {
        if (0 != pInfo->InheritedFromProcessId)
        {
            //printf("进程名: %wZ, PID: %d\n", &(pInfo->ProcessName), pInfo->ProcessId);
            PWSTR curpoint = pInfo->ProcessName.Buffer;
            wcscpy_s(wsCurrentName, MAX_PATH * sizeof(WCHAR), curpoint);
            wcscpy_s(wsTargetName, MAX_PATH * sizeof(WCHAR), wsTargetProcess);

            if (!_wcsicmp(curpoint, wsTargetProcess))
            {
                
                return pInfo->InheritedFromProcessId;
            }
        }
        pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);
    }

    if (NULL != pBuf)
    {
        delete[] pBuf;
        pBuf = NULL;
    }
    
    return FALSE;
        
}

效果如图:

 可以在不提权的情况下结束管理员权限的进程。

后记

Winsta 有好几个有意思的函数,比如可以用其中的函数结束工作站强制注销或关机等等。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Windows下,一个服务是没有桌面交互权限的,因此无法直接打开带界面的进程。但是,你可以通过使用CreateProcessAsUser()函数来创建一个新的进程,并将其运行在指定用户的会话中,该用户的会话必须已经登录。下面是一个简单的示例: ```c++ #include <Windows.h> #include <WtsApi32.h> #include <UserEnv.h> #pragma comment(lib, "WtsApi32.lib") #pragma comment(lib, "UserEnv.lib") void runProcessAsUser(LPCWSTR username, LPCWSTR password, LPCWSTR domain, LPCWSTR application) { HANDLE token; DWORD session_id = WTSGetActiveConsoleSessionId(); WTSQueryUserToken(session_id, &token); LPVOID environment; CreateEnvironmentBlock(&environment, token, FALSE); PROCESS_INFORMATION process_info; STARTUPINFO startup_info; ZeroMemory(&startup_info, sizeof(startup_info)); startup_info.cb = sizeof(startup_info); startup_info.lpDesktop = L"winsta0\\default"; startup_info.lpEnvironment = environment; startup_info.dwFlags |= STARTF_USESHOWWINDOW; startup_info.wShowWindow = SW_SHOW; CreateProcessAsUser(token, application, NULL, NULL, NULL, FALSE, 0, environment, NULL, &startup_info, &process_info); CloseHandle(process_info.hProcess); CloseHandle(process_info.hThread); DestroyEnvironmentBlock(environment); CloseHandle(token); } int main() { runProcessAsUser(L"username", L"password", L"domain", L"path/to/application.exe"); return 0; } ``` 注意,这个示例仅适用于当前用户会话已经登录的情况。如果要在服务启动时启动带界面的进程,你需要在服务启动时等待用户登录,并在用户登录后再使用上述代码来打开进程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

涟幽516

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

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

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

打赏作者

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

抵扣说明:

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

余额充值