0

I'm new in c++ programming and I'm coding a tool to enumerate information of all process running on windows OS.

After researching, googling, I have found a useful library Ntdll.lib and header winternl.h that helping me gathering information about process by using NtQuerySystemInformation() function. Everything work fine, I have call that function and retrieved the array of structure SYSTEM_PROCESS_INFORMATION that contains information about the process entry, here is my piece of code:

DWORD dwRet;
DWORD dwSize = 0;
NTSTATUS Status = STATUS_INFO_LENGTH_MISMATCH;
    
    while (true)
    {
        // Check if pointer p is not NULL then free it
        if (p != NULL) { VirtualFree(p, 0, MEM_RELEASE); }
        
        p = (PSYSTEM_PROCESS_INFORMATION)VirtualAlloc(NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

        // Query system to get the process list information
        Status = NtQuerySystemInformation(SystemProcessInformation, (PVOID)p, (ULONG)dwSize, &dwRet);

        if (Status == STATUS_SUCCESS)                       
        {   
            cout << "Query process information successfully!!" << endl;
            break;          
        }
        else if (Status != STATUS_INFO_LENGTH_MISMATCH)     
        {   
            VirtualFree(p, 0, MEM_RELEASE);
            p = NULL;
            cout << "NtQuerySystemInformation failed with error code: " << Status << endl;
            return FALSE;
        }
        
        // Add more 16kb to buffer in case there is more process opened during this loop
        dwSize = dwRet + (2 << 14);
    }

The problem appears when I was looking for thread details of processes, in particular, I don't know how to get the array of structure SYSTEM_THREAD_INFORMATION with NtQuerySystemInformation() function.

I have read the docs here: https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation and it says that each SYSTEM_PROCESS_INFORMATION structure returned by NtQuerySystemInformation() has 1 or more SYSTEM_THREAD_INFORMATION structure followed in memory but I don't know how to interact with them. Anyone has an idea for my problem? I'm just a newbie in c++ programming and I'm studying in user mode code so sorry if my question is silly or not worth asking.

Botje
  • 26,269
  • 3
  • 31
  • 41
  • That's an *internal* method that's not meant to be used by user code. It's not needed. The rest of the code is extremely dangerous and that pointer arithmetic is *guaranteed* to cause errors. You didn't explain what process information you want but whatever it is, there are high-level APIs for this already. For example the [Process Status API](https://docs.microsoft.com/en-us/windows/win32/psapi/process-status-helper) helps in diagnostics. There's [GetProcessInformation](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocessinformation) too – Panagiotis Kanavos Aug 22 '22 at 11:56
  • Processes and threads are different things anyway. One process can have many threads, but handles are owned by the *process*, not the threads. Only threads get scheduled for execution though, not processes. What information are you looking for? – Panagiotis Kanavos Aug 22 '22 at 11:58
  • 2
    @PanagiotisKanavos - this method can be used of course. and need. unclear what mean "dangerous" and if not make errors by self - will be no errors – RbMm Aug 22 '22 at 13:13
  • @RbMm you missed the`[NtQuerySystemInformation may be altered or unavailable in future versions of Windows. Applications should use the alternate functions listed in this topic.]` warning at the top of the method, or that it's defined in `winternl.h`. Methods starting with `Nt` are *not* for general usage. As for `unclear what you mean by errors`, `VirtualFree` is a low-level call meant to be called by `new` and pointer arithmetic like `dwSize = dwRet + (2 << 14)` can *easily* result in memory overruns – Panagiotis Kanavos Aug 22 '22 at 13:23
  • There's even a [Is ok to use undocumented SYSTEM_PROCESS_INFORMATION?](https://stackoverflow.com/questions/24677043/is-ok-to-use-undocumented-system-process-information) question – Panagiotis Kanavos Aug 22 '22 at 13:27
  • Often times the Nt/Zw functions are the only options in kernel mode programming, and they are documented nowdays after all. `VirtualFree` is an official documented Windows API intended to be called by developers directly - in C you even have to since there's no `new`. And pointer arithmetic itself is also a very legitimate and important technique that isn't dangerous if done properly (although the example in OP's code looks a bit weird indeed). – Louis Bernard Aug 22 '22 at 13:30
  • @PanagiotisKanavos *may be altered or unavailable in future versions of* - this is lie. i read this already more than 20+ years – RbMm Aug 22 '22 at 13:37
  • @PanagiotisKanavos - `and pointer arithmetic like dwSize = dwRet + (2 << 14) can easily result in memory overruns` - this at all senseless. we simply increase buffer size for next allocation. all what you say about new, virtualalloc - unrelated here at all – RbMm Aug 22 '22 at 13:39
  • [*SYSTEM_PROCESS_INFORMATION is **immediately followed** in memory by one or more SYSTEM_THREAD_INFORMATION structures*](https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation#system_process_information). @EdwardDaemon Any updates? – YangXiaoPo-MSFT Aug 23 '22 at 02:31
  • @Panagiotis Kanavos Process Status API is a high level code running in user mode like you said, it's good to get process information with **Openprocess()** to get the handle and then go futher, but the problem is the **Openprocess()** func will fail to all system process (Protected Process Light) like idle, csrss.exe, registry,... that's why i choose **Ntqueysysteminformation()** because it working with system right so it can enum all process included PPL. – Edward Daemon Aug 23 '22 at 09:38
  • @Panagiotis Kanavos i think it's not dangerous at all, it has functions working on kernel level and memory allocation but i think it's up to you, the way you use it right or wrong. That is just my personal opinion, feel free mate. – Edward Daemon Aug 23 '22 at 09:45
  • @YangXiaoPo-MSFT i'm trying the way that dude below shows me – Edward Daemon Aug 23 '22 at 09:47
  • @EdwardDaemon If an answer has helped you, please accept it. – WBuck Aug 24 '22 at 12:51

2 Answers2

0

Starting from your PSYSTEM_PROCESS_INFORMATION p:

while (p) {
  PSYSTEM_THREAD_INFORMATION pt = (PSYSTEM_THREAD_INFORMATION)(p + 1);
  for (int t = 0; t < p->NumberOfThreads; t++) {
    std::cout << "Start address of thread " << t << " is " << std::hex << pt->StartAddress << std::dec << std::endl;
    pt++; // Adds sizeof(SYSTEM_THREAD_INFORMATION) to the address in pt
  }
  if (p->NextEntryOffset) {
    p = PSYSTEM_PROCESS_INFORMATION((void *)p + NextEntryOffset);
  } else {
    p = nullptr;
  }
}
Botje
  • 26,269
  • 3
  • 31
  • 41
  • 3
    'void *': unknown size so expression `(void *)p + sizeof(*p)` never correct. `PSYSTEM_THREAD_INFORMATION pt = (PSYSTEM_THREAD_INFORMATION)(p + 1);` – RbMm Aug 22 '22 at 13:10
  • Makes you wonder, who would possibly up-vote a proposed answer containing code that doesn't even compile? – IInspectable Aug 22 '22 at 17:50
  • I'll try it in my code, seems like a good idea, didn't think about it – Edward Daemon Aug 23 '22 at 09:48
  • 1
    @IInspectable Sadly, [gcc allows it as a nonstandard extension](https://stackoverflow.com/questions/13113301/how-void-pointer-arithmetic-is-happening-in-gcc). – Raymond Chen Aug 31 '22 at 14:25
0

Thanks to Botje and RbMm and all of you for helping me solving this problem.

I just add the part that Botje show me and correct the line

PSYSTEM_THREAD_INFORMATION pt = (PSYSTEM_THREAD_INFORMATION)(p + 1);

then the problem is solved and my code works smoothly.

Thanks again, this post is closed