I've written some code of enumeration process before. I'm reviewing the code I learned before these two days. I see the CreateToolhelp32Snapshot function. By the way, let's see its specific implementation.
The process of enumerating process information seems easy, just some Windows API functions:
- CreateToolhelp32Snapshot
- Process32First
- Process32Next
By using these three functions, the enumeration of current system processes can be realized. However, the specific implementation of these three functions remains to be studied.
Start with CreateToolhelp32Snapshot:
It passes two parameters. The first parameter is a macro, which indicates the type of snapshot we need to take. Here we enumerate the process information, and naturally pass th32cs ﹣ snapprocess. The second parameter is the process ID. we usually pass in 0. What happens after passing in 0? The default is the current process.
if(th32ProcessID == 0) { th32ProcessID = GetCurrentProcessId(); }
Next, we will apply for a memory space in the current process, then call NtQuerySystemInformation to process information query, and return the query information in the parameter list.
for(;;) { (*ProcThrdInfoSize) += 0x10000; //Apply for memory at the current processor, read and write, and submit physical memory Status = NtAllocateVirtualMemory(NtCurrentProcess(), ProcThrdInfo, 0, ProcThrdInfoSize, MEM_COMMIT, PAGE_READWRITE); if(!NT_SUCCESS(Status)) { break; } //Query process information and return it as a parameter Status = NtQuerySystemInformation(SystemProcessInformation, *ProcThrdInfo, *ProcThrdInfoSize, NULL); if(Status == STATUS_INFO_LENGTH_MISMATCH) { //If the length is not enough, release the memory, enter the for loop and add another page to reapply NtFreeVirtualMemory(NtCurrentProcess(), ProcThrdInfo, ProcThrdInfoSize, MEM_RELEASE); *ProcThrdInfo = NULL; } else { break; } }
Next, create a Section object and map the virtual memory. Initialize the returned process information to the memory where the Section object is located and return the Section handle
... //Create Section object, readable and writable, memory commit Status = NtCreateSection(&hSection, SECTION_ALL_ACCESS, &ObjectAttributes, &SSize, PAGE_READWRITE, SEC_COMMIT, NULL); ... //Map in the current process, return the mapped address and assign it to a PTH32SNAPSHOT pointer Status = NtMapViewOfSection(hSection, NtCurrentProcess(), (PVOID*)&Snapshot, 0, 0, &SOffset, &ViewSize, ViewShare, 0, PAGE_READWRITE); ...... //Put the previously queried process information into the mapped address ProcessListEntry = (LPPROCESSENTRY32W)OffsetToPtr(Snapshot, DataOffset); ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)ProcThrdInfo; ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)((ULONG_PTR)ProcessInfo + ProcOffset); ProcessListEntry->dwSize = sizeof(PROCESSENTRY32W); ProcessListEntry->cntUsage = 0; /* no longer used */ ProcessListEntry->th32ProcessID = (ULONG_PTR)ProcessInfo->UniqueProcessId; ProcessListEntry->th32DefaultHeapID = 0; /* no longer used */ ProcessListEntry->th32ModuleID = 0; /* no longer used */ ProcessListEntry->cntThreads = ProcessInfo->NumberOfThreads; ProcessListEntry->th32ParentProcessID = (ULONG_PTR)ProcessInfo->InheritedFromUniqueProcessId; ....//Above are some assignment statements //Finish the data we want to write, and finish the mapping Status = NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)Snapshot); if(NT_SUCCESS(Status)) { //Returns a handle to the Section object for later use *SectionHandle = hSection; }
At this point, the code has been basically implemented, and then some resources that are no longer needed will be recycled.
The implementation of Process32First and Process32Next are basically the same:
//Internally map the handle of the section object and read out the contents in memory Status = NtMapViewOfSection(hSnapshot, NtCurrentProcess(), (PVOID*)&Snapshot, 0, 0, &SOffset, &ViewSize, ViewShare, 0, PAGE_READWRITE); //Analyze the contents if(Snapshot->ProcessListCount > 0) { LPPROCESSENTRY32W Entries = (LPPROCESSENTRY32W)OffsetToPtr(Snapshot, Snapshot->ProcessListOffset); //Copy structure content Snapshot->ProcessListIndex = 1; RtlCopyMemory(lppe, &Entries[0], sizeof(PROCESSENTRY32W)); Ret = TRUE; } //Free memory map NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)Snapshot);
Process32Next just uses the index to start traversal and copy the information of each process. I won't elaborate here. The most important three functions are implemented as a whole. I believe that based on this, I can understand the meaning of snapshot better.
With simple test code:
/* typedef struct tagPROCESSENTRY32 { DWORD dwSize; //The number of bytes of this structure must be initialized to sizeof(PROCESSENTRY32) before calling Process32First DWORD cntUsage; //0 It's been a long time DWORD th32ProcessID; //Current process Id ULONG_PTR th32DefaultHeapID; //0 It's been a long time DWORD th32ModuleID; //0 It's been a long time DWORD cntThreads; //Number of execution threads started by the process DWORD th32ParentProcessID; //Parent process Id LONG pcPriClassBase; //Basic priority of process creation thread DWORD dwFlags; //0 Reserved value not used TCHAR szExeFile[MAX_PATH]; //Process name } PROCESSENTRY32; */ void _tmain(int argc, _TCHAR **argv, _TCHAR **envp) { _tsetlocale(LC_ALL, _T("chs")); TCHAR* v1 = (TCHAR*)(_T("Process enumeration:")); _tprintf(_T("%s\n"), v1); KtEnumSystemProcess(); _tprintf(_T("Input AnyKey to Exit\r\n")); getchar(); } VOID KtEnumSystemProcess() { HANDLE SnapHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (SnapHandle == INVALID_HANDLE_VALUE) { return; } //PROCESSENTRY32 must be initialized like this PROCESSENTRY32 ProcessEntry = { sizeof(PROCESSENTRY32) }; BOOL IsOk = Process32First(SnapHandle, &ProcessEntry); //NtMapViewOfSection maps the Section object to virtual memory again //Read //Basically consistent with Process32First, extract the process list in PTH32SNAPSHOT one by one through index for (; IsOk; IsOk = Process32Next(SnapHandle, &ProcessEntry)) { _tprintf(_T("ProcessName:%s "), ProcessEntry.szExeFile); _tprintf(_T("ThreadPriority:%d "), ProcessEntry.pcPriClassBase); _tprintf(_T("ProcessID:%d \r\n"), ProcessEntry.th32ProcessID); _tprintf(_T("ParentID:%d \r\n"), ProcessEntry.th32ParentProcessID); } }
One disadvantage of snapshot function is that it can't update the process in the system in real time. If I close a process after taking a snapshot, that is, after the handle of the Section object returns, it will still enumerate the process that I closed. Similarly, when I start a process after taking a snapshot, I will not give the process information.
Note: the source code in the process of explaining the code comes from the open source code react OS
Today's exhortation: the darkness before dawn is as thick as the night.