4

I have a 64 bit process that needs to read the 32bit PEB of a Wow64 process.

I'm able to get it with NtQueryInformationProcess, but I realized that Wow64 processes have two PEBs (64 and 32 bit) and NtQueryInformationProcess returns the PEB corresponding to the bitness of the caller (64bit in my case), as @Anders commented in this solution:

How to get the Process Environment Block (PEB) from extern process?

That's my scenario: I'm trying to get the 32bit PEB of a Wow64 process, from inside a x64 process. Any suggestions that involve changing that scenario are useless. I'm also aware that this kind of solution is not recommended for production and that's not my intention.

Any ideas?

Thanks in advance.

Community
  • 1
  • 1
karliwson
  • 3,365
  • 1
  • 24
  • 46

2 Answers2

6

If you read the NtQueryInterformationProcess() documentation on MSDN, there is a comment that says:

It appears that when querying a process running under wow64 in (at least) windows Vista the PebBaseAddress returned is actually for the 64-bit modules loaded under wow64. From some initial investigations I've done it appears that the PEB which pertains to 32-bit modules can be found by taking the PebBaseAddress and subtracting one page (0x1000) from its value. I have minimally confirmed this hypothesis by inspecting the process's TIB's and following their PEB pointers back to an address which, so far, has always shown to be -0x1000 from the PebBaseAddress value returned by this function.

Update: I just found this code that states the above will not work from Windows 8 onwards, but does provide an alternative solution:

#define TEB32OFFSET 0x2000

void interceptNtDll32(HANDLE hProcess)
{
    THREAD_BASIC_INFORMATION tbi;
    NTSTATUS ntrv;
    TEB32 teb32;
    void *teb32addr;
    PEB_LDR_DATA32 ldrData;
    PEB32 peb32;
    LIST_ENTRY32 *pMark = NULL;
    LDR_DATA_TABLE_ENTRY32 ldrDataTblEntry;
    size_t bytes_read;
    HANDLE hThread = getThreadHandle(hProcess);

    /* Used to be able to get 32 bit PEB from PEB64 with 0x1000 offset but
       Windows 8 changed that so we do it indirectly from the TEB */
    if(!hThread)
        return;

    /* Get thread basic information to get 64 bit TEB */
    ntrv = NtQueryInformationThread(hThread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL);
    if(ntrv != 0){
        goto out;
    }

    /* Use magic to find 32 bit TEB */
    teb32addr = (char *)tbi.TebBaseAddress + TEB32OFFSET; // Magic...
    ntrv = NtReadVirtualMemory(hProcess, teb32addr, &teb32, sizeof(teb32), NULL);
    if(ntrv != 0 || teb32.NtTib.Self != (DWORD)teb32addr){  // Verify magic...
        goto out;
    }

    /* TEB32 has address for 32 bit PEB.*/
    ntrv = NtReadVirtualMemory(hProcess, (void *)teb32.ProcessEnvironmentBlock, &peb32, sizeof(peb32), NULL);
    if(ntrv != 0){
        goto out;
    }

    ...
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • That's what I found too: https://github.com/nihilus/ScyllaHide/blob/master/InjectorCLI/RemotePebHider.cpp I just tested in Windows 10 and confirmed that the first one (PEB64 - 0x1000) does not work. I´m trying the code you mentioned. Thanks. – karliwson Jan 12 '16 at 06:18
2

You can use NtQueryInformationProcess

see https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess#ulong_ptr

set the ProcessInformationClass to ProcessWow64Information

set a pointer to recive the ProcessInformation's value

When called the api, the ProcessInformation's value will be pebBaseAddr of the wow64 process if it's not zero

Akkuman
  • 31
  • 2