17. Internals

Because some horrible hacks of PythonForWindows are hidden and I wanted to talk about it.

17.1. remotectypes.py

Performing parsing of PEB / PE in remote process may be painful and i didn’t want to have two versions of all my parsing code.

So I made a wrapper around ctypes that is able to do two things:

  • Transform a 32bits ctypes structure into a 64bits one and reverse

This is done by replacing the c_void_p/c_char_p by DWORD or QWORD and rewriting a wrapper around the ctypes POINTER and other stuff. It might not works for every structure by i didn’t have any problem for now.

  • Read the memory in another process

For this one I rewrote a class that use the standard ctypes structure offset-size calculation, extracts those information when asked for a field and read it from the target process. We just need to take care of special cases: POINTER / ARRAY / STRING / ..

We also need to be carreful about the inheritance, we need to inherit from “hidden” ctypes classes to keep the magic working.

This module exports the following API:

windows.remotectypes.transform_type_to_remote32bits(ftype)[source]
windows.remotectypes.transform_type_to_remote64bits(ftype)[source]

Both functions return a class that represent the structure in a remote process. The class.__init__ accept two arguments:

  • base_addr: the address of the object in the remote process
  • target: an object with a method read_memory (so a windows.winobject.WinProcess in our case)

Example WinProcess.peb:

def peb(self):
    if windows.current_process.bitness == 32 and self.bitness == 64:
        return RemotePEB64(self.peb_addr, self)
    if windows.current_process.bitness == 64 and self.bitness == 32:
        return RemotePEB32(self.peb_addr, self)
    return RemotePEB(self.peb_addr, self)

I am pretty sure that this code does NOT handle all the cases, so it might break some day.

17.2. syswow64.py – Crossing the heaven gate

One of my goal with PythonForWindows is to have some abstraction of the bitness of the processes. It means being able to work on a 32bits Python or a 64bits Python.

In the case of a 32bits python on a 64bits system (SysWow64) it’s not trivial to perform operation on other 64bits processes. For example directly calling CreateRemoteThread() will not work.

To be able to perform those operation we must be able to execute code in the 64bits part of our SysWow64 process.

For that we need to jump to the 64bits segment of our process, execute some code then return. To do so, we need to use some far jump / far ret with the segments selector 0x23 (CS_32bits) and 0x33 (CS_64bits).

The generation of this is quite ugly in my case. This code is in:

windows.syswow64.execute_64bits_code_from_syswow()[source]

Once we are able to execute some code in the 64bits part we need to create the code that will call our API (in NTDLL). To do that, I rely on the type information already present in the function of windows.winproxy. With these information we are able to know

  • The name of the API
  • The number of arguments

Then I generate the correct x64 stub (using windows.native_exec.simple_x64) with the function:

windows.syswow64.generate_syswow64_call()[source]

One problem I encountered is that our function must be able to pass values of 64bits, so passing arguments by register is not possible.

For now I allocate a buffer where a python wrapper copy the parameters and the x64 stub retrieves them from here.

(It might be possible to do something by creating a WINCFUNC with only ULONG64 parameters).

windows.syswow64.try_generate_stub_target()[source]

The final result is a Python function like the one in windows.winproxy

  • It copies the arguments in the buffer
  • Jumps on the 32->64 stub
  • X64 bits code retrieves the arguments in the buffer and setup the registers and the stack for the call
  • Calls the API
  • Returns to 32bits mode.
class windows.syswow64.Syswow64ApiProxy[source]

Existing function are:

windows.syswow64.NtCreateThreadEx_32_to_64()[source]
windows.syswow64.NtQueryInformationProcess_32_to_64()[source]
windows.syswow64.NtQueryInformationThread_32_to_64()[source]
windows.syswow64.NtQueryVirtualMemory_32_to_64()[source]
windows.syswow64.NtGetContextThread_32_to_64()[source]
windows.syswow64.NtSetContextThread_32_to_64()[source]
windows.syswow64.LdrLoadDll_32_to_64()[source]