pythonpython-3.xwinapictypes

Ctypes- Can't get PEB Address using NtQueryInformationProcess


I am using ctypes and trying to get the PEB address using the NtQueryInformationProcess function.

The return value is 0, meaning that the function was completed successfully. But the PROCESS_BASIC_INFORMATION structure (given as the third argument) doesn't contain the PEB address.

class PROCESS_BASIC_INFORMATION(Structure):
    _fields_ = [
        ("Reserved1", c_void_p),
        ("PebBaseAddress", DWORD),
        ("Reserved2", c_void_p * 2),
        ("UniqueProcessId", DWORD),
        ("Reserved3", c_void_p)]

ntdll = WinDLL('ntdll')
NTSTATUS = LONG
ntdll.argtypes = [HANDLE, DWORD, c_void_p, DWORD, PDWORD]
ntdll.restype = NTSTATUS
processInformation = PROCESS_BASIC_INFORMATION()
processInformationLength = sizeof(PROCESS_BASIC_INFORMATION)
result = ntdll.NtQueryInformationProcess(hProcess, 0, processInformation, processInformationLength, byref(DWORD(0)))

Consider the fact that the return value is 0, what can be the problem?


Solution

  • Listing [Python.Docs]: ctypes - A foreign function library for Python.

    1. You defined argtypes and restype for the wrong object (ntdll). You should define them for ntdll.NtQueryInformationProcess. That is Undefined Behavior. Check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for a common pitfall when working with CTypes (calling functions).
      While we are here, it's obvious that you like DWORD. Even if in this case it makes no difference, keep function signatures consistent with the ones from C:

      ntdll.NtQueryInformationProcess.argtypes = (HANDLE, c_int, c_void_p, ULONG, PULONG)
      ntdll.NtQueryInformationProcess.restype = NTSTATUS
      
    2. According to [MS.Learn]: NtQueryInformationProcess function (winternl.h) (emphasis is mine):

      ProcessInformation

      A pointer to a buffer supplied by the calling application into which the function writes the requested information.

      So, your call should look like (pass processInformation by reference):

       result = ntdll.NtQueryInformationProcess(hProcess, 0, byref(processInformation), processInformationLength, byref(DWORD(0)))
      
    3. According to the same page, your structure definition is incorrect. It should be:

       class PROCESS_BASIC_INFORMATION(Structure):
           _fields_ = (
               ("Reserved1", c_void_p),
               ("PebBaseAddress", c_void_p),
               ("Reserved2", c_void_p * 2),
               ("UniqueProcessId", c_void_p),
               ("Reserved3", c_void_p),
           )
      

      Your version (on 064bit) is 8 bytes shorter (because of (the 2) size differences between DWORD and pointer), resulting in a too short buffer being passed to the function, which is Undefined Behavior (it can lead to crashes)