C/C++ Windows API——枚举进程、结束进程及提升权限

// EnumProcessDemo.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 
#include //CreateToolhelp32Snapshot
#include //EnumProcesses

bool GetPrivileges()
{
    // 取得当前进程的[Token](标识)句柄
    HANDLE hToken;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
        if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
            return true;
        }
        else {
            return false;
        }
    }
    // 取得调试的[LUID](本地唯一的标识符)值
    TOKEN_PRIVILEGES tokenPrivilege = { 0 };
    TOKEN_PRIVILEGES oldPrivilege = { 0 };
    DWORD dwPrivilegeSize;
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tokenPrivilege.Privileges[0].Luid)) {
        CloseHandle(hToken);
        return false;
    }

    // 设置特权数组的元素个数
    tokenPrivilege.PrivilegeCount = 1;

    // 设置[LUID]的属性值
    tokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    // 为当前进程取得DEBUG权限
    if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPrivilege, sizeof(TOKEN_PRIVILEGES), &oldPrivilege, &dwPrivilegeSize)) {
        CloseHandle(hToken);
        return false;
    }
    //程序结束时要记得释放句柄
    //CloseHandle(hToken);
    return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
    BOOL ret;
    //ret = GetPrivileges();
    //printf("GetPrivileges -> %d\n", ret);

    /*
    //可以获得包括系统进程在内的所有存在PID的进程(唯一一个无PID的“系统中断”进程获取不到)
    HANDLE WINAPI CreateToolhelp32Snapshot(
    DWORD dwFlags,      //用来指定“快照”中需要返回的对象
    DWORD th32ProcessID //指定将要快照的进程ID。如果该参数为0表示快照当前进程。该参数只有在设置了TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE后才有效,在其他情况下该参数被忽略,所有的进程都会被快照。
    );
    // The th32ProcessID argument is only used if TH32CS_SNAPHEAPLIST or
    // TH32CS_SNAPMODULE is specified. th32ProcessID == 0 means the current
    // process.
    //
    // NOTE that all of the snapshots are global except for the heap and module
    //      lists which are process specific. To enumerate the heap or module
    //      state for all WIN32 processes call with TH32CS_SNAPALL and the
    //      current process. Then for each process in the TH32CS_SNAPPROCESS
    //      list that isn't the current process, do a call with just
    //      TH32CS_SNAPHEAPLIST and/or TH32CS_SNAPMODULE.
    //
    // dwFlags
    //
    #define TH32CS_SNAPHEAPLIST 0x00000001  //在快照中包含在th32ProcessID中指定的进程的所有的堆
    #define TH32CS_SNAPPROCESS  0x00000002  //在快照中包含系统中所有的进程
    #define TH32CS_SNAPTHREAD   0x00000004  //在快照中包含系统中所有的线程
    #define TH32CS_SNAPMODULE   0x00000008  //在快照中包含在th32ProcessID中指定的进程的所有的模块
    #define TH32CS_SNAPMODULE32 0x00000010  //在快照中包含系统中所有的模块
    #define TH32CS_SNAPALL      (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)  //在快照中包含系统中所有的进程和线程
    #define TH32CS_INHERIT      0x80000000  //声明快照句柄是可继承的

    调用成功,返回快照的句柄,调用失败,返回INVALID_HANDLE_VALUE
    */
    DWORD dwFlags = TH32CS_SNAPPROCESS;
    DWORD th32ProcessID = 0;
    HANDLE handle = CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
    if (handle == INVALID_HANDLE_VALUE) {
        printf("CreateToolhelp32Snapshot -> INVALID_HANDLE_VALUE -> fail(%ld)\n", GetLastError());
    }
    else {
        printf("CreateToolhelp32Snapshot -> ret=%p -> succ\n", handle);

        /*
        typedef struct tagPROCESSENTRY32W
        {
        DWORD   dwSize;
        DWORD   cntUsage;               //此进程的引用计数(deprecated,always zero)
        DWORD   th32ProcessID;          // this processID,就是任务管理器里面的进程的PID
        ULONG_PTR th32DefaultHeapID;    //进程默认堆(deprecated,always zero)
        DWORD   th32ModuleID;           // associated exe,进程模块ID(deprecated,always zero)
        DWORD   cntThreads;             //此进程开启的线程数
        DWORD   th32ParentProcessID;    // this process's parent process
        LONG    pcPriClassBase;         // Base priority of process's threads,线程优先权
        DWORD   dwFlags;                //(deprecated,always zero)
        WCHAR   szExeFile[MAX_PATH];    // Path,进程可执行程序名。但是,如果被调用进程是一个64位程序,您必须调用QueryFullProcessImageName函数去获取64位进程的可执行文件完整路径名。
        } PROCESSENTRY32W;
        */
        PROCESSENTRY32 processEntry32 = { 0 };
        processEntry32.dwSize = sizeof(processEntry32);

        int processCnt = 0;
        //遍历进程快照,轮流显示每个进程的信息
        BOOL hasNext = Process32First(handle, &processEntry32);
        while (hasNext) {
            processCnt++;
            _tprintf(_T("Process32Next -> th32ProcessID=%ld, cntThreads=%ld, th32ParentProcessID=%ld, pcPriClassBase=%ld, szExeFile=%s\n"), processEntry32.th32ProcessID, processEntry32.cntThreads, processEntry32.th32ParentProcessID, processEntry32.pcPriClassBase, processEntry32.szExeFile);
            hasNext = Process32Next(handle, &processEntry32);
        }
        printf("CreateToolhelp32Snapshot count=%d\n", processCnt);

        CloseHandle(handle);
    }

    /*
    BOOL WINAPI EnumProcesses (
    _Out_writes_bytes_(cb) DWORD * lpidProcess, //Pointer to an array that receives the list of process identifiers.接收进程标识符的数组.
    _In_ DWORD cb,                              //Size of the pProcessIds array, in bytes.
    _Out_ LPDWORD lpcbNeeded                    //Number of bytes returned in the pProcessIds array.数组返回的字节数
    );
    成功返回非零数,失败返回零,可以使用函数 GetLastError获取错误信息.

    下面算式可以得出返回了多少进程:
    nReturned = cbNeeded / sizeof(DWORD)。

    定义个比较大的数组来接收进程IDs,是一个比较好的选择.虽然文档将返回的 DWORD 命名为“pBytesRrturned”,实际上是没有办法知道到底要传多大的数组的。EnumProcesses()根本不会在 pBytesRrturned 中返回一个大于 cb 参数传递的数组值。结果,唯一确保 EnumProcesses()函数成功的方法是分配一个 DWORD 数组,并且,如果返回的 cbNeeded 等于 cb,分配一个较大的数组,并不停地尝试直到 cbNeeded 小于 cb
    */
    const DWORD PID_CB = 1024;
    DWORD pidProcesss[PID_CB];
    DWORD pcbNeeded;
    ret = EnumProcesses(pidProcesss, PID_CB, &pcbNeeded);
    if (!ret) {
        //printf("EnumProcesses -> fail(%ld)\n", GetLastError());
    }
    else
    {
        //printf("EnumProcesses -> pcbNeeded=%ld -> succ\n", pcbNeeded);

        DWORD processCnt = pcbNeeded / sizeof(DWORD);
        printf("processCnt = %ld\n", processCnt);

        for (DWORD i = 0; i < processCnt; i++) {
            /*
            WINBASEAPI HANDLE WINAPI OpenProcess(
                _In_ DWORD dwDesiredAccess,
                _In_ BOOL bInheritHandle,
                _In_ DWORD dwProcessId
            );

            #define PROCESS_TERMINATE                  (0x0001) //允许函数TerminateProcess 使用此句柄关闭进程.
            #define PROCESS_CREATE_THREAD              (0x0002)
            #define PROCESS_SET_SESSIONID              (0x0004)
            #define PROCESS_VM_OPERATION               (0x0008) //允许函数VirtualProtectEx使用此句柄修改进程的虚拟内存.
            #define PROCESS_VM_READ                    (0x0010)
            #define PROCESS_VM_WRITE                   (0x0020)
            #define PROCESS_DUP_HANDLE                 (0x0040)
            #define PROCESS_CREATE_PROCESS             (0x0080)
            #define PROCESS_SET_QUOTA                  (0x0100)
            #define PROCESS_SET_INFORMATION            (0x0200)
            #define PROCESS_QUERY_INFORMATION          (0x0400)
            #define PROCESS_SUSPEND_RESUME             (0x0800)
            #define PROCESS_QUERY_LIMITED_INFORMATION  (0x1000)
            #define PROCESS_SET_LIMITED_INFORMATION    (0x2000)
            #if (NTDDI_VERSION >= NTDDI_VISTA)
            #define PROCESS_ALL_ACCESS        (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF)
            #else
            #define PROCESS_ALL_ACCESS        (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)
            #endif
            */  
            DWORD dwDesiredAccess = PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
            //DWORD dwDesiredAccess = PROCESS_ALL_ACCESS;
            BOOL bInheritHandle = FALSE;
            DWORD dwProcessId = pidProcesss[i];
            //OpenProcess后返回的句柄在最后要记得关闭
            handle = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
            if (handle == NULL || handle == INVALID_HANDLE_VALUE) {
                //PID等于0的错误码是87(参数错误);5是(拒绝访问);6是句柄无效;299是仅完成部分的 ReadProcessMemoty 或 WriteProcessMemory 请求。
                //只有3个进程open不了,提升权限也无效("System Idle Process(pid=0)""System(pid=4)""audiodg.exe(pid=336)")
                printf("OpenProcess dwProcessId=%ld -> handle=%p(NULL or INVALID_HANDLE_VALUE) -> fail(%ld)\n", dwProcessId, handle, GetLastError());
            }
            else {
                printf("OpenProcess dwProcessId=%ld -> handle=%p -> succ\n", dwProcessId, handle);
                /*
                BOOL WINAPI EnumProcessModulesEx(
                    _In_  HANDLE  hProcess,
                    _Out_ HMODULE *lphModule,
                    _In_  DWORD   cb,
                    _Out_ LPDWORD lpcbNeeded,
                    _In_  DWORD   dwFilterFlag
                );

                dwFilterFlag可以是下列值
                #define LIST_MODULES_DEFAULT 0x0  // This is the default one app would get without any flag.
                #define LIST_MODULES_32BIT   0x01  // list 32bit modules in the target process.
                #define LIST_MODULES_64BIT   0x02  // list all 64bit modules. 32bit exe will be stripped off.
                // list all the modules
                #define LIST_MODULES_ALL   (LIST_MODULES_32BIT | LIST_MODULES_64BIT)
                */

                const DWORD hModuleCb = 1024;
                HMODULE hModules[hModuleCb];
                DWORD cbNeeded;
                DWORD dwFilterFlag = LIST_MODULES_ALL;
                ret = EnumProcessModulesEx(handle, hModules, hModuleCb, &cbNeeded, dwFilterFlag);
                if (!ret) {
                    /*
                    问题描述
                    错误码为299(仅完成部分的 ReadProcessMemoty 或 WriteProcessMemory 请求)
                    如果在WOW64上运行的32位应用程序调用本函数,那它只能枚举32位进程中的模块。对于64位的进程,本函数将执行失败,错误代码是ERROR_PARTIAL_COPY (299)。
                    解决方案:
                    将生成的程序改为64位
                    */
                    printf("EnumProcessModulesEx dwProcessId=%ld, handle=%p -> ret=%d -> fail(%d)\n", dwProcessId, handle, ret, GetLastError());
                }
                else
                {
                    //printf("EnumProcessModulesEx dwProcessId=%ld, handle=%p -> ret=%d -> succ\n", dwProcessId, handle, ret);

                    DWORD moduleCnt = cbNeeded / sizeof(HMODULE);
                    printf("moduleCnt = %ld\n", moduleCnt);

                    for (DWORD j = 0; j/*
                        返回进程名包含路径
                        _Success_(return != 0)
                        _Ret_range_(1, nSize)
                        DWORD WINAPI GetModuleFileNameExW(
                            _In_opt_ HANDLE hProcess,
                            _In_opt_ HMODULE hModule,
                            _When_(return < nSize, _Out_writes_to_(nSize, return + 1))
                            _When_(return == nSize, _Out_writes_all_(nSize))
                            LPWSTR lpFilename,
                            _In_ DWORD nSize
                        );
                        */
                        TCHAR szModuleName[MAX_PATH] = { 0 };
                        DWORD nSize = MAX_PATH;
                        DWORD dwRet = GetModuleFileNameEx(handle, hModules[j], szModuleName, nSize);
                        if (dwRet == 0) {
                            //printf("GetModuleFileNameEx dwProcessId=%ld, handle=%p, hModules=%p-> fail(%d)\n", dwProcessId, handle, (unsigned int)hModules[j], GetLastError());
                        }
                        else {
                            //_tprintf(_T("GetModuleFileNameEx dwProcessId=%ld, handle=%p, hModules=%p -> ret=%ld, szModuleName=%s -> succ\n"), dwProcessId, handle, (unsigned int)hModules[j], dwRet, szModuleName);
                        }

                        /*
                        返回进程名不包含路径
                        DWORD WINAPI GetModuleBaseNameW(
                            _In_ HANDLE hProcess,
                            _In_opt_ HMODULE hModule,
                            _Out_writes_(nSize) LPTSTR lpBaseName,
                            _In_ DWORD nSize
                        );
                        */
                        TCHAR lpBaseName[MAX_PATH] = { 0 };
                        dwRet = GetModuleBaseName(handle, hModules[j], lpBaseName, nSize);
                        if (dwRet == 0) {
                            //printf("GetModuleBaseName handle=%p, hModule=%p -> fail(%d)\n", handle, (unsigned int)hModules[j], GetLastError());
                        }
                        else {
                            //_tprintf(_T("GetModuleBaseName dwProcessId=%ld, handle=%p, hModule=%p -> ret=%ld, lpBaseName=%s -> succ\n"), dwProcessId, handle, (unsigned int)hModules[j], dwRet, lpBaseName);
                            //strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。
                            if (_tcsstr(lpBaseName, _T("notepad.exe"))) {
                                //需要以terminal的权限打开进程句柄才能关闭进程,否则会失败
                                HANDLE terminalHandle = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
                                if(handle !=NULL && handle != INVALID_HANDLE_VALUE) {
                                    /*
                                    WINBASEAPI BOOL WINAPI TerminateProcess(
                                        _In_ HANDLE hProcess,
                                        _In_ UINT uExitCode
                                        );
                                    */
                                    ret = TerminateProcess(terminalHandle, 0);
                                    printf("TerminateProcess dwProcessId=%ld -> ret=%d\n", dwProcessId, ret);
                                    CloseHandle(terminalHandle);
                                }
                                else {
                                    printf("OpenProcess dwProcessId=%ld -> %d\n", dwProcessId, ret);
                                }
                            }
                        }
                    }
                }
                CloseHandle(handle);
            }
        }
    }

    system("pause");
    return 0;
}

C/C++ Windows API——枚举进程、结束进程及提升权限_第1张图片

C/C++ Windows API——枚举进程、结束进程及提升权限_第2张图片

遇到的问题:

(1)EnumProcessModulesEx 遇到有些进程会出错,错误码为299。

参考:http://blog.csdn.net/anycell/article/details/3505864

原因:PEB的地址被设置在PebBaseAddress中。但是64位进程的PEB头地址是保存在64位长度的地址中的(比如上面说道的系统进程Services.exe),而32位进程的PEB头地址只有32位长度。运行在64位系统上32位应用程序是如何将64位的PEB地址转换成32位地址的呢?如果64位PEB地址的高32位为0,则转换不会出现任何问题。但如果高32位也包含地址信息,那么WOW64(Windows 32-bit on Windows 64-bit,windows 64位系统上兼容32位应用程序的技术,作为由32位向64位程序的过渡方案)只是简单的将低32位的PEB地址赋给32位应用程序中的PebBaseAddress变量,当然就会发生错误了!于是Windows就会出发error 299并返回失败。

解决办法:将编译的程序由32位改为64位即可。

(2)有3个进程open不了,提升权限也无效(“System Idle Process(pid=0)”、”System(pid=4)”、”audiodg.exe(pid=336)”)

暂无解决办法

你可能感兴趣的:(#,Windows,API)