Monday, January 7, 2013

Get-PEB – A Tool to Dump the Process Environment Block (PEB) of Any Process

Recently, I made the mistake of volunteering to undertake the creation of a process environment block parsing tool in PowerShell. Several painstaking days of work later, Get-PEB was created. Get-PEB is a self-contained script that will retrieve and parse the PEB of an arbitrary process, independent of Windows OS version (well, XP and above) and architecture – i.e. it will retrieve the PEB of 32-bit, 64-bit, and Wow64 processes.

What is the process environment block? It is a structure that is formed during process initialization that contains data pertinent to the execution of a process and is closely associated with the EPROCESS data structure in the kernel. The structure isn’t fully documented by Microsoft but fortunately, the symbols for the PEB and its embedded structures are made available via `dt nt!_PEB` in Windbg. Additionally, there is a wealth of open source documentation.

The process environment block is also heavily used by shellcode to resolve dll function addresses without needing to call GetProcAddress.

Running Get-PEB is pretty simple. You can either give it a process ID via the ‘-Id’ parameter or you can just pipe the output of Get-Process (ps) to it via the pipeline. For example, let’s say I’m interested in retrieving the PEB from a notepad.exe process:

One of the techniques used by shellcode to get the addresses of loaded modules in memory is to walk the InLoadOrderModuleList doubly linked list to obtain the base address of kernel32.dll and ntdll.dll. Ntdll and kernel32 are almost always the second and third entries in this list. I didn’t bother to parse every possible substructure contained within the PEB in Get-PEB but I did make sure to parse the InLoadOrderModuleList, InMemoryOrderModuleList, and InInitializationOrderModuleList linked lists which point to a series of LDR_DATA_TABLE_ENTRY structures.

So, if I wanted to view the InLoadOrderModuleList field in notepad.exe, I simply type the following:

This script was not trivial to produce. I faced a number of challenges during its creation:

Problem: The definition of the PEB structure has evolved over time since Windows XP and I wanted to reflect these changes dynamically based upon the version of Windows running.

Solution: Reflection was suited perfectly for this task since its intended use is the creation of assemblies, modules, and types (structures are a subset of types in .NET) on the fly. I wrote some logic that retrieves the NTDDI representation of the Windows version and built up the structure of the PEB dynamically based upon the well documented difference in ReactOS.

Problem: Parsing memory structure pointed to by memory addresses in the virtual address space of another process proved problematic.

Solution: I developed a helper function - Get-StructFromMemory to address this issue. It is basically calls ReadProcessMemory in kernel32, copies data from the other process into local virtual memory and calls [Runtime.InteropServices.Marshal]::PtrToStructure.

Problem: Making it all look pretty.

Solution: ps1xml files are designed to format the output of objects. The challenge is that since the PEB structure is created dynamically, I have to account for every possible type that it might emit. The included Get-PEB.format.ps1xml accounts for all these possibilities.

I hope you enjoy Get-PEB. As usual, I encourage you to ask questions, report bugs, propose improvements. Hopefully, time permitting, I’ll parse additional substructures in the PEB. The next one on the list for me is the _RTL_USER_PROCESS_PARAMETERS structure pointed to by the ProcessParameters field.

13 comments:

When and if you do add the _RTL_USER_PROCESS_PARAMETERS structure, that will be of great value and significance because, at last, one can obtain another process' current working directory. And having looked all over the web for a way to do so in PS has convinced me that you are closer to accomplishing that in PS than anyone else.

Even without you telling me what the issue was, I assume you were getting the following error message: 'Cannot get the PEB of a 64-bit process from a Wow64 process. Use 64-bit PowerShell and try again.' Right?

Using ProcessExplorer or Task Manager I get the PID of process I'm in interested in, say, 11796.Then, in PS I do:$PEB = Get-PEB -Id 11796$PEB.ProcessParameters.CurrentDirectorygives me "C:\Windows\" for any process.

Hmm. Considering I'm getting conflicting information in Get-PEB vs. WinDbg, it would appear as though my script has a bug. I'll dig into it and hopefully have a solution shortly. Thanks for bringing that to my attention.

Hey Paul. I figured it out. Before reading ahead, run Get-PEB from 32-bit PowerShell and observe the difference.

The misrepresented CurrentDirectory is caused by the fact that Get-PEB, if run from 64-bit PowerShell retrieves the 64-bit PEB of a 32-bit process. To fix this peculiarity, I'll have to determine the bitness of the process and get its corresponding PEB. I can't explain why C:\Windows is the most common CurrentDirectory in this case, but I can tell you with certainty that the issue is dual PEBs in a wow64 process.

You're right! I just finished trying it inside a 32- and 64-bit PS and, in the 32-bit PS, I obtained the correct CurrentDirectory. Good find!

In determining the bitness of the process, here's a small bit of Python code from the psutil module that may help you: // get the address of ProcessParameters#ifdef _WIN64 if (!ReadProcessMemory(processHandle, (PCHAR)pebAddress + 32, &rtlUserProcParamsAddress, sizeof(PVOID), NULL))#else if (!ReadProcessMemory(processHandle, (PCHAR)pebAddress + 0x10, &rtlUserProcParamsAddress, sizeof(PVOID), NULL))#endif { CloseHandle(processHandle);