用户态枚举进程的几种方法

    技术2022-05-11  70

    最近在准备一个进程查看(当然不只进程查看功能了)的工具,总结了在用户态下查找进程的几种方法。

    当然,如果想要真正做到进程查看,还是要进入核心态中,因为在用户态是查不到什么东西的,但是可以用来和

    核心态结果进行比较找出隐藏进程。(内核级病毒、木马在Ring0中可以很容易的做到隐藏而是用户态程序检测不到)。

    //write by jingzhongrong

    1、利用ToolHelp API

    首先创建一个系统快照,然后通过对系统快照的访问完成进程的枚举

    获取系统快照使用 CreateToolhelp32Snapshot 函数

    函数原型声明如下:

    HANDLE WINAPI CreateToolhelp32Snapshot(  DWORD dwFlags,  DWORD th32ProcessID);

    将dwFlags设置为TH32CS_SNAPPROCESS用于获取进程快照。

    函数调用成功后会返回一个快照的句柄,便可以使用Process32First、Process32Next进行枚举了              

    函数原型声明如下:

    BOOL WINAPI Process32First(  HANDLE hSnapshot,  LPPROCESSENTRY32 lppe);

    BOOL WINAPI Process32Next( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );

    下面是相关代码:

     

    #include <windows.h> #include  <tlhelp32.h> #include  <stdio.h> void  useToolHelp(){    HANDLE procSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0 );    if(procSnap ==  INVALID_HANDLE_VALUE)    {        printf("CreateToolhelp32Snapshot failed, %d " ,GetLastError());        return ;    }     //    PROCESSENTRY32 procEntry = { 0  };    procEntry.dwSize = sizeof (PROCESSENTRY32);    BOOL bRet = Process32First(procSnap,& procEntry);    while (bRet)    {        wprintf(L"PID: %d (%s) " ,procEntry.th32ProcessID, procEntry.szExeFile);                                                           bRet = Process32Next(procSnap,& procEntry);    }    CloseHandle(procSnap);}void  main(){    useToolHelp();    getchar();}

    用此方法可以在进程ID和进程名称间进行转换,即通过进程名称获得进程ID,通过进程ID获取进程名称。

     

    2、通过psapi.dll提供的函数

    通过psapi.dll提供的EnumProcesses、EnumProcessModules实现

    函数原型声明如下:

    BOOL EnumProcesses( DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned ); BOOL EnumProcessModules( HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded ); 相关代码如下:(参考MSDN) #include  < windows.h > #include  < stdio.h > #include  < tchar.h > #include  " psapi.h " #pragma  comment(lib,"psapi.lib") void  PrintProcessNameAndID(DWORD processID){    TCHAR szProcessName[MAX_PATH]  =  _T( " <unknown> " );  HANDLE hProcess  =  OpenProcess(PROCESS_ALL_ACCESS /*  | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ */ ,FALSE,processID);          // Process name.    if (NULL != hProcess)  {        HMODULE hMod;    DWORD cbNeeded;     if (EnumProcessModules(hProcess, & hMod, sizeof (hMod),  & cbNeeded))             {            GetModuleBaseName(hProcess,hMod,szProcessName, sizeof (szProcessName) / sizeof (TCHAR));                            }  }    wprintf(_T( " PID: %d (%s) " ),processID,szProcessName);  CloseHandle(hProcess);} void  main( ){    DWORD aProcesses[ 1024 ], cbNeeded, cProcesses;  unsigned  int  i;   if ( ! EnumProcesses(aProcesses, sizeof (aProcesses), & cbNeeded))         return ;  cProcesses  =  cbNeeded / sizeof (DWORD);   for (i = 0 ;i < cProcesses;i ++ )        PrintProcessNameAndID(aProcesses[i]);    getchar();}

    此方法由于需要进行OpenProcess操作,所以需要一定的权限,当权限不够时,有些进程将不能被打开。

    下面给出提升权限的相关代码:

    void  RaisePrivilege(){    HANDLE hToken;    TOKEN_PRIVILEGES tp;    tp.PrivilegeCount = 1 ;    tp.Privileges[0].Attributes =  SE_PRIVILEGE_ENABLED;    if(OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,& hToken))    {        if(LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0 ].Luid))        {            AdjustTokenPrivileges(hToken,FALSE,&tp,NULL,NULL,0 );        }    }    if (hToken)        CloseHandle(hToken);}

    3、通过ntdll.dll提供的Native API

    使用Native API 中的ZwQuerySystemInformation的SystemProcessesAndThreadsInformation系统调用枚举进程

    由于该函数没有被导出,所以首先定义要使用到的结构和常量

     

    typedef DWORD (WINAPI * ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);typedef struct  _SYSTEM_PROCESS_INFORMATION{    DWORD NextEntryDelta;    DWORD ThreadCount;    DWORD Reserved1[6 ];    FILETIME ftCreateTime;    FILETIME ftUserTime;    FILETIME ftKernelTime;    UNICODE_STRING ProcessName;    DWORD BasePriority;    DWORD ProcessId;    DWORD InheritedFromProcessId;    DWORD HandleCount;    DWORD Reserved2[2 ];    DWORD VmCounters;    DWORD dCommitCharge;    PVOID ThreadInfos[1 ];}SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;#define SystemProcessesAndThreadsInformation 5

    然后动态加载ntdll.dll,获得函数的地址。

    便可以进行进程的枚举

    相关代码如下:

     

    #include <windows.h> #include  <ntsecapi.h> #include  <stdio.h> typedef DWORD (WINAPI  * ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);typedef struct  _SYSTEM_PROCESS_INFORMATION{    DWORD NextEntryDelta;    DWORD ThreadCount;    DWORD Reserved1[6 ];    FILETIME ftCreateTime;    FILETIME ftUserTime;    FILETIME ftKernelTime;    UNICODE_STRING ProcessName;    DWORD BasePriority;    DWORD ProcessId;    DWORD InheritedFromProcessId;    DWORD HandleCount;    DWORD Reserved2[2 ];    DWORD VmCounters;    DWORD dCommitCharge;    PVOID ThreadInfos[1 ];}SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;#define SystemProcessesAndThreadsInformation 5 void  main(){    HMODULE hNtDll = GetModuleHandle(L"ntdll.dll" );    if(! hNtDll)        return ;    ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll,"ZwQuerySystemInformation" );    ULONG cbBuffer = 0x10000 ;    LPVOID pBuffer =  NULL;    pBuffer =  malloc(cbBuffer);    if(pBuffer ==  NULL)        return ;    ZwQuerySystemInformation(SystemProcessesAndThreadsInformation,pBuffer,cbBuffer,NULL);    PSYSTEM_PROCESS_INFORMATION pInfo =  (PSYSTEM_PROCESS_INFORMATION)pBuffer;    for (;;)    {        wprintf(L"PID: %d (%ls) ",pInfo->ProcessId,pInfo-> ProcessName.Buffer);        if(pInfo->NextEntryDelta == 0 )            break ;        pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo-> NextEntryDelta);    }    free(pBuffer);    getchar();}

    代码在VS2005 release + XP SP2调试通过

    4、通过进程打开的句柄来枚举进程

    同样使用ZwQuerySystemInformation函数,使用SystemHandleInformation系统调用

    相关代码如下:

     

    #include <windows.h> #include  <ntsecapi.h> #include  <ntstatus.h> #include  <stdio.h> typedef NTSTATUS (WINAPI  * ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);typedef struct  _SYSTEM_HANDLE_INFORMATION {    ULONG ProcessId;    UCHAR ObjectTypeNumber;    UCHAR Flags;    USHORT Handle;    PVOID Object;    ACCESS_MASK GrantedAccess;}SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;typedef struct  _SYSTEM_HANDLE_INFORMATION_EX {    ULONG NumberOfHandles;    SYSTEM_HANDLE_INFORMATION Information[1 ];}SYSTEM_HANDLE_INFORMATION_EX, * PSYSTEM_HANDLE_INFORMATION_EX;#define SystemHandleInformation 0x10   //16 void  main(){    HMODULE hNtDll = LoadLibrary(L"ntdll.dll" );    if(! hNtDll)        return ;    ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll,"ZwQuerySystemInformation" );    ULONG cbBuffer = 0x4000 ;    LPVOID pBuffer =  NULL;    NTSTATUS s;    do     {        pBuffer  =  malloc(cbBuffer);        if(pBuffer ==  NULL)            return ;        memset(pBuffer,0 ,cbBuffer);        s =  ZwQuerySystemInformation(SystemHandleInformation,pBuffer,cbBuffer,NULL);        if(s ==  STATUS_INFO_LENGTH_MISMATCH)        {            free(pBuffer);            cbBuffer = cbBuffer * 2 ;        }    }while(s ==  STATUS_INFO_LENGTH_MISMATCH);    PSYSTEM_HANDLE_INFORMATION_EX pInfo =  (PSYSTEM_HANDLE_INFORMATION_EX)pBuffer;    ULONG OldPID = 0 ;    for(DWORD i = 0;i<pInfo->NumberOfHandles;i++ )    {        if(OldPID != pInfo-> Information[i].ProcessId)        {            OldPID = pInfo-> Information[i].ProcessId;            wprintf(L"PID: %d " ,OldPID);        }    }    free(pBuffer);    FreeLibrary(hNtDll);    getchar();}

    另外,在进行进程“隐藏”工作的时候,此处的句柄是一件容易被忽略的地方,因此需要注意隐藏由程序打开的相关句柄,由于系统中句柄数量经常变换,所以没有什么必要修改其中的NumberOfHandles域,因为如果修改此处的值,则需要不停对句柄的变化进行维护,开销比较大。

     

    在用户态下的进程枚举已经变得不可靠,因为一个内核级的Rootkit很容易就能够更改这些函数的返回结果,因此进程的可靠枚举应在内核态中实现,可以通过编写驱动来实现


    最新回复(0)