Win 64bit GetThreadContext returns zeroe'd out registers, or 0x57 errorcode

4

I'm working on a Windows 7 64 bit machine (I have admin privs).

I'm using Python 2.7 (64-bit) with PyDev ctypes for Eclipse to try and read the values of registers in all threads associated with a particular PID (tried both PIDs of processes running in 64 and 32 bit modes), but when I do this, the values for the registers are all zeroe'd out. When I use Wow64GetThreadContext, the call fails with GetLastError returning 0x00000057 ('invalid parameters' according to MSDN)

I attach to the process successfully, enumerate the threads (via CreateToolhelp32Snapshot), find the threads that are owned by the process with the appropriate PID, and attempt to get the thread context. Here is my code for opening the thread and getting the thread context:

Opening a thread:

def open_thread(self, thread_id):
    h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, thread_id)

Getting context:

def get_thread_context(self, thread_id = None, h_thread = None):

    context = CONTEXT()
    context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS

    #alternatively, for 64
    context64 = WOW64_CONTEXT()
    context64.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS

    #Obtain a handle to the thread
    if h_thread is None:
        self.h_thread = self.open_thread(thread_id)

    kernel32.SuspendThread(self.h_thread)

    if kernel32.GetThreadContext(self.h_thread, byref(context)):
        kernel32.ResumeThread(self.h_thread)
        return context
    else:
        kernel32.ResumeThread(self.h_thread)
        return False

I call this code using:

debugger.attach(int(pid))

#debugger.run()

list = debugger.enumerate_threads()

for thread in list:
thread_context = debugger.get_thread_context(thread)

    if thread_context == False:
        print "[*] Thread context is false..."
    else:
        print "[*] Dumping registers for thread ID: 0x%08x" % thread
        print "[**] Eip: 0x%016x" % thread_context.Eip
        print "[**] Esp: 0x%016x" % thread_context.Esp
        print "[**] Ebp: 0x%016x" % thread_context.Ebp
        print "[**] Eax: 0x%016x" % thread_context.Eax
        print "[**] Ebx: 0x%016x" % thread_context.Ebx
        print "[**] Ecx: 0x%016x" % thread_context.Ecx
        print "[**] Edx: 0x%016x" % thread_context.Edx
        print "[*] End DUMP"

debugger.detach()

When I run this code using GetThreadContext with the CONTEXT structure, I get the context object back for each thread, but the register values are all zero.

I have tried replacing GetThreadContext with Wow64GetThreadContext (and respectively, SuspendThread with Wow64SuspendThread), but when I do this, the call fails with error 'invalid parameters'. The arguments I give to Wow64GetThreadContext are the same as those I give to GetThreadContext, other than the name of the variables in the code I provided (this is because when I looked at their definitions in WinNT.h, they were equivalent (unless I missed something). I've defined these structures the following way:

class WOW64_CONTEXT(Structure):
_fields_ = [

    ("ContextFlags", DWORD),
    ("Dr0", DWORD),
    ("Dr1", DWORD),
    ("Dr2", DWORD),
    ("Dr3", DWORD),
    ("Dr6", DWORD),
    ("Dr7", DWORD),
    ("FloatSave", WOW64_FLOATING_SAVE_AREA),
    ("SegGs", DWORD),
    ("SegFs", DWORD),
    ("SegEs", DWORD),
    ("SegDs", DWORD),
    ("Edi", DWORD),
    ("Esi", DWORD),
    ("Ebx", DWORD),
    ("Edx", DWORD),
    ("Ecx", DWORD),
    ("Eax", DWORD),
    ("Ebp", DWORD),
    ("Eip", DWORD),
    ("SegCs", DWORD),
    ("EFlags", DWORD),
    ("Esp", DWORD),
    ("SegSs", DWORD),
    ("ExtendedRegisters", BYTE * 512),
]

class WOW64_FLOATING_SAVE_AREA(Structure):
_fields_ = [

    ("ControlWord", DWORD),
    ("StatusWord", DWORD),
    ("TagWord", DWORD),
    ("ErrorOffset", DWORD),
    ("ErrorSelector", DWORD),
    ("DataOffset", DWORD),
    ("DataSelector", DWORD),
    ("RegisterArea", BYTE * 80),
    ("Cr0NpxState", DWORD),
]

class CONTEXT(Structure):
_fields_ = [

    ("ContextFlags", DWORD),
    ("Dr0", DWORD),
    ("Dr1", DWORD),
    ("Dr2", DWORD),
    ("Dr3", DWORD),
    ("Dr6", DWORD),
    ("Dr7", DWORD),
    ("FloatSave", FLOATING_SAVE_AREA),
    ("SegGs", DWORD),
    ("SegFs", DWORD),
    ("SegEs", DWORD),
    ("SegDs", DWORD),
    ("Edi", DWORD),
    ("Esi", DWORD),
    ("Ebx", DWORD),
    ("Edx", DWORD),
    ("Ecx", DWORD),
    ("Eax", DWORD),
    ("Ebp", DWORD),
    ("Eip", DWORD),
    ("SegCs", DWORD),
    ("EFlags", DWORD),
    ("Esp", DWORD),
    ("SegSs", DWORD),
    ("ExtendedRegisters", BYTE * 512),
]

class FLOATING_SAVE_AREA(Structure):
_fields_ = [

    ("ControlWord", DWORD),
    ("StatusWord", DWORD),
    ("TagWord", DWORD),
    ("ErrorOffset", DWORD),
    ("ErrorSelector", DWORD),
    ("DataOffset", DWORD),
    ("DataSelector", DWORD),
    ("RegisterArea", BYTE * 80),
    ("Cr0NpxState", DWORD),
]

I've done a fair amount of googling on this issue, and have tried the following to no avail:

  • According to a comment on the MSDN: CONTEXT_FULL should be CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT to be used with Win64 properly.

  • I've tried renaming the registers inside my CONTEXT and WOW_64CONTEXT structures by replacing the 'E' in the register names with 'R' (Eax -> Rax, etc.)

Has anyone else used Python with ctypes to successfully get the context of a 64-bit thread on Windows?

python
windows
64-bit
pydev
threadcontext
asked on Stack Overflow Jul 6, 2013 by bl4ckmes4

3 Answers

5

You're main problem is that WOW64 is actually the 32 bit context, not the 64 bit. You need to implement a 64 bit structure, similar to this:

class CONTEXT64(Structure):
_pack_ = 16
_fields_ = [
    ("P1Home", DWORD64),
    ("P2Home", DWORD64),
    ("P3Home", DWORD64),
    ("P4Home", DWORD64),
    ("P5Home", DWORD64),
    ("P6Home", DWORD64),

    ("ContextFlags", DWORD),
    ("MxCsr", DWORD),

    ("SegCs", WORD),
    ("SegDs", WORD),
    ("SegEs", WORD),
    ("SegFs", WORD),
    ("SegGs", WORD),
    ("SegSs", WORD),
    ("EFlags", DWORD),

    ("Dr0", DWORD64),
    ("Dr1", DWORD64),
    ("Dr2", DWORD64),
    ("Dr3", DWORD64),
    ("Dr6", DWORD64),
    ("Dr7", DWORD64),

    ("Rax", DWORD64),
    ("Rcx", DWORD64),
    ("Rdx", DWORD64),
    ("Rbx", DWORD64),
    ("Rsp", DWORD64),
    ("Rbp", DWORD64),
    ("Rsi", DWORD64),
    ("Rdi", DWORD64),
    ("R8", DWORD64),
    ("R9", DWORD64),
    ("R10", DWORD64),
    ("R11", DWORD64),
    ("R12", DWORD64),
    ("R13", DWORD64),
    ("R14", DWORD64),
    ("R15", DWORD64),
    ("Rip", DWORD64),

    ("DebugControl", DWORD64),
    ("LastBranchToRip", DWORD64),
    ("LastBranchFromRip", DWORD64),
    ("LastExceptionToRip", DWORD64),
    ("LastExceptionFromRip", DWORD64),

    ("DUMMYUNIONNAME", DUMMYUNIONNAME),

    ("VectorRegister", M128A * 26),
    ("VectorControl", DWORD64)
]

Note: This definition is located in WinNT.h -- if you have VC++ installed, it will be in the /include directory where it's installed.

Once who have this structure built, you use this instead of the CONTEXT / WOW64 CONTEXT you have built. You'll also have to obviously change the registers to RAX, etc.

(Note: there are 4 other things you have to implement in Python ctypes as well: DWORD64, M128A, DUMMYUNIONNAME, DUMMYSTRUCTNAME, and XMM_SAVE_AREA32. For brevity, I've excluded them, but you can find their definitions in the following locations to build them yourself:

DWORD64: is just a c_ulonglong

DUMMYUNIONNAME, DUMMYSTRUCTNAME: In WinNT.h in the _CONTEXT struct

M128A: http://winappdbg.sourceforge.net/doc/v1.3/winappdbg.win32.defines.M128A-class.html

XMM_SAVE_AREA32: http://winappdbg.sourceforge.net/doc/v1.3/winappdbg.win32.context_amd64.XMM_SAVE_AREA32-class.html

answered on Stack Overflow Sep 4, 2013 by Justin
3

I too had this problem, i now have this working, i assume this is from the Gray Hat python book, after much googling it appeared that Wow64GetThreadContext is used for retrieving 32 bit thread contexts on a 64 bit system, i used the original GetThreadContext function but i passed it a Wow64Context structure defined as below:

class M128A(Structure):
    _fields_ = [
            ("Low", DWORD64),
            ("High", DWORD64)
            ] 

class XMM_SAVE_AREA32(Structure):
    _pack_ = 1 
    _fields_ = [  
                ('ControlWord', WORD), 
                ('StatusWord', WORD), 
                ('TagWord', BYTE), 
                ('Reserved1', BYTE), 
                ('ErrorOpcode', WORD), 
                ('ErrorOffset', DWORD), 
                ('ErrorSelector', WORD), 
                ('Reserved2', WORD), 
                ('DataOffset', DWORD), 
                ('DataSelector', WORD), 
                ('Reserved3', WORD), 
                ('MxCsr', DWORD), 
                ('MxCsr_Mask', DWORD), 
                ('FloatRegisters', M128A * 8), 
                ('XmmRegisters', M128A * 16), 
                ('Reserved4', BYTE * 96)
                ] 

class DUMMYSTRUCTNAME(Structure):
    _fields_=[
              ("Header", M128A * 2),
              ("Legacy", M128A * 8),
              ("Xmm0", M128A),
              ("Xmm1", M128A),
              ("Xmm2", M128A),
              ("Xmm3", M128A),
              ("Xmm4", M128A),
              ("Xmm5", M128A),
              ("Xmm6", M128A),
              ("Xmm7", M128A),
              ("Xmm8", M128A),
              ("Xmm9", M128A),
              ("Xmm10", M128A),
              ("Xmm11", M128A),
              ("Xmm12", M128A),
              ("Xmm13", M128A),
              ("Xmm14", M128A),
              ("Xmm15", M128A)
              ]


class DUMMYUNIONNAME(Union):
    _fields_=[
              ("FltSave", XMM_SAVE_AREA32),
              ("DummyStruct", DUMMYSTRUCTNAME)
              ]

class CONTEXT64(Structure):
    _pack_ = 16
    _fields_ = [
                ("P1Home", DWORD64),
                ("P2Home", DWORD64),
                ("P3Home", DWORD64),
                ("P4Home", DWORD64),
                ("P5Home", DWORD64),
                ("P6Home", DWORD64),
                ("ContextFlags", DWORD),
                ("MxCsr", DWORD),
                ("SegCs", WORD),
                ("SegDs", WORD),
                ("SegEs", WORD),
                ("SegFs", WORD),
                ("SegGs", WORD),
                ("SegSs", WORD),
                ("EFlags", DWORD),
                ("Dr0", DWORD64),
                ("Dr1", DWORD64),
                ("Dr2", DWORD64),
                ("Dr3", DWORD64),
                ("Dr6", DWORD64),
                ("Dr7", DWORD64),
                ("Rax", DWORD64),
                ("Rcx", DWORD64),
                ("Rdx", DWORD64),
                ("Rbx", DWORD64),
                ("Rsp", DWORD64),
                ("Rbp", DWORD64),
                ("Rsi", DWORD64),
                ("Rdi", DWORD64),
                ("R8", DWORD64),
                ("R9", DWORD64),
                ("R10", DWORD64),
                ("R11", DWORD64),
                ("R12", DWORD64),
                ("R13", DWORD64),
                ("R14", DWORD64),
                ("R15", DWORD64),
                ("Rip", DWORD64),
                ("DebugControl", DWORD64),
                ("LastBranchToRip", DWORD64),
                ("LastBranchFromRip", DWORD64),
                ("LastExceptionToRip", DWORD64),
                ("LastExceptionFromRip", DWORD64),
                ("DUMMYUNIONNAME", DUMMYUNIONNAME),
                ("VectorRegister", M128A * 26),
                ("VectorControl", DWORD64)
]

of course i haven't checked yet as to whether the values being returned in the registers are correct or just garbage yet, but the fact that values are there instead of 0x00000000 or error 0x57 is reassuring.

Accessing the registers is still done through thread_context.Rip etc, not eip

answered on Stack Overflow Sep 15, 2013 by ali2992
0

Check the value of your ContextFlags constants. The value I got from python module win32con misses the architecture dependent bit. This is an extract from my WinNT.h (from Windows SDK Server2003SP1):

#define CONTEXT_AMD64   0x100000

// end_wx86

#define CONTEXT_CONTROL (CONTEXT_AMD64 | 0x1L)
#define CONTEXT_INTEGER (CONTEXT_AMD64 | 0x2L)
#define CONTEXT_SEGMENTS (CONTEXT_AMD64 | 0x4L)
#define CONTEXT_FLOATING_POINT  (CONTEXT_AMD64 | 0x8L)
#define CONTEXT_DEBUG_REGISTERS (CONTEXT_AMD64 | 0x10L)

#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT)

#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS)

[...]

#define CONTEXT_i386    0x00010000    // this assumes that i386 and
#define CONTEXT_i486    0x00010000    // i486 have identical context records

// end_wx86

#define CONTEXT_CONTROL         (CONTEXT_i386 | 0x00000001L) // SS:SP, CS:IP, FLAGS, BP
#define CONTEXT_INTEGER         (CONTEXT_i386 | 0x00000002L) // AX, BX, CX, DX, SI, DI
#define CONTEXT_SEGMENTS        (CONTEXT_i386 | 0x00000004L) // DS, ES, FS, GS
#define CONTEXT_FLOATING_POINT  (CONTEXT_i386 | 0x00000008L) // 387 state
#define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386 | 0x00000010L) // DB 0-3,6,7
#define CONTEXT_EXTENDED_REGISTERS  (CONTEXT_i386 | 0x00000020L) // cpu specific extensions

#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER |\
                      CONTEXT_SEGMENTS)

#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS)

If it's not working for you, check the WinNT.h for your windows version.

answered on Stack Overflow Sep 22, 2013 by user2804197

User contributions licensed under CC BY-SA 3.0