进程查看

    技术2022-05-20  32

    最近在准备一个进程查看(当然不只进程查看功能了)的工具,总结了在用户态下查找进程的几种方法。当然,如果想要真正做到进程查看,还是要进入核心态中,因为在用户态是查不到什么东西的,但是可以用来和核心态结果进行比较找出隐藏进程。(内核级病毒、木马在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)