当前位置 博文首页 > weixin_34391445的博客: 【超详细】遍历Windows进程

    weixin_34391445的博客: 【超详细】遍历Windows进程

    作者:[db:作者] 时间:2021-06-14 12:41

    摘要

    遍历系统进程常用的有三种方式

    • 头文件<Tlhelp32.h>,先使用CreateToolhelp32Snapshot创建系统快照,即拷贝一份系统所有进程的信息到指定的结构体PROCESSENTRY32中,然后使用Process32FirstProcess32Next获得每一个进程的详细信息。需要注意的是编译成32位程序只能获取32位进程的详细信息,编译成64位则能获取所有进程的详细信息
    • 头文件<psapi.h>,先使用EnumProcesses获取系统所有的进程PID,然后用EnumProcessModules遍历每个进程中的模块,最后用GetModuleFileNameEx获取模块的完整路径或者用GetModuleBaseName获取模块的名称。也需要注意程序编译的位数与进程的位数
    • 无需头文件,使用NativeAPI接口NtQuerySystemInformation,由于是未公开函数所以需要自定义函数原型。留个坑以后填

    CreateToolhelp32Snapshot

    完整思路
    (1).使用CreateToolhelp32Snapshot以及传参TH32CS_SNAPPROCESS创建系统进程快照
    (2).使用Process32First获取第一个进程的信息存入到PROCESSENTRY32结构体中
    (3).再用Process32Next遍历所有进程
    (4).最后用CloseHandle关闭句柄

    注意事项
    程序很简单,需要注意的是编码问题,包括控制台输出的编码以及程序本身的编码,windows.h头文件也定义许多保证编码兼容性的变量,比如TEXT() _T TCHAR LPTSTR...

    • 注意控制台中文输出需要添加编译指令-fexec-charset=gbk
    • 宽字符的使用一般是wchar_t *ws = L"你好"; wprintf(L"%s\n",ws)
    • TCHAR遇到宽字符就是Unicode编码,否则就是char类型,TEXT() _T() _tprintf等函数同理
    • 使用这些保证兼容性的函数可能需要的头文件有<locale.h><tchar.h>,可能需要添加代码setlocale(LC_ALL,"chs");

    完整Demo

    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <Tlhelp32.h>
    
    int main(int argc, char *argv[])
    {
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(pe32);
        HANDLE hSnapshot_proc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (hSnapshot_proc != INVALID_HANDLE_VALUE)
        {
            BOOL check = Process32First(hSnapshot_proc, &pe32);
            while (check)
            {
                printf("进程PID = %d 进程名 = %s\n", pe32.th32ProcessID, pe32.szExeFile);
                check = Process32Next(hSnapshot_proc, &pe32);
            }
        }
        CloseHandle(hSnapshot_proc);
        system("pause");
        return 0;
    }

    编译命令gcc -fexec-charset=gbk proc1.c -o proc1

    运行截图

    clipboard.png

    EnumProcesses

    完整思路
    (1).使用EnumProcesses获取所有进程PID
    (2).获取当前进程的Debug权限
    (3).使用OpenProcess打开进程,依次获取所有进程的进程句柄
    (4).根据进程句柄以及EnumProcessModules遍历进程的所有模块
    (5).使用GetModuleFileNameEx获取进程的完整路径
    (6).使用CloseHandle关闭句柄以及取消Debug权限

    注意事项
    使用EnumProcesses需要注意四个坑,先说明一下我是用的MinGW编译器,用 VScode 写完代码自己调 gcc 编译!

    • 注意导入头文件的顺序,<psapi.h>一定要在<windows.h>之后
    • 注意编译指令顺序,-lpsapi一定要在文件名proc3.c之后
    • 注意编译成32位只能获取32位进程的信息,编译成64位都能获取
    • 注意使用OpenProcess前一定要获取Debug权限

    完整Demo

    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <psapi.h>
    
    BOOL SetProcessPrivilege(char *lpName, BOOL opt);
    int main(int argc, char *argv[])
    {
        DWORD Proc_pid[1024], Retn_bytes, Proc_count, Retn_bytes2;
        unsigned int i;
        HMODULE hMod[1024];
        HANDLE hProcess;
        char szModName[MAX_PATH];
        if (EnumProcesses(Proc_pid, sizeof(Proc_pid), &Retn_bytes))
        {
            Proc_count = Retn_bytes / sizeof(DWORD);
            SetProcessPrivilege("SeDebugPrivilege", 1);
            for (i = 0; i < Proc_count; i++)
            {
                hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Proc_pid[i]);
                if (hProcess != NULL)
                {
                    EnumProcessModules(hProcess, hMod, sizeof(hMod), &Retn_bytes2);
                    GetModuleFileNameEx(hProcess, hMod[0], szModName, sizeof(szModName));
                    printf("PID=%d Path=%s\n", Proc_pid[i], szModName);
                }
                CloseHandle(hProcess);
            }
            SetProcessPrivilege("SeDebugPrivilege", 0);
        }
        system("pause");
        return 0;
    }
    
    BOOL SetProcessPrivilege(char *lpName, BOOL opt)
    {
        HANDLE tokenhandle;
        TOKEN_PRIVILEGES NewState;
    
        if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tokenhandle))
        {
            LookupPrivilegeValue(NULL, lpName, &NewState.Privileges[0].Luid);
            NewState.PrivilegeCount = 1;
            NewState.Privileges[0].Attributes = opt != 0 ? 2 : 0;
            AdjustTokenPrivileges(tokenhandle, FALSE, &NewState, sizeof(NewState), NULL, NULL);
            CloseHandle(tokenhandle);
            return 1;
        }
        else
        {
            return 0;
        }
    }

    编译命令gcc proc3.c -lpsapi -o proc3

    运行截图

    clipboard.png

    END