GetThreadContext winapi call with python / ctypes

0

I'm currently working on a Python/Winapi project.
I want to call Get- and SetThreadContext with Python and ctypes.
My setup is Windows10 64bit and Python 64bit. The Apis should work for both, 32bit and 64bit processes.

It does work with 32bit processes by calling Wow64GetThreadContext with a WOW64Context.
However with 64bit processes and Get/SetThreadContex it does not work.

GetLastError does show 87 (The parameter is incorrect.)
Probably the implementation of my Context64 is wrong but I couldn't find a mistake.
I tried it also with the Implementation from https://github.com/MarioVilas/winappdbg
Both implementations show a size of 1232 with ctypes.sizeof(ctx).

My current implementation of CONTEXT for x64:

WORD        = ctypes.c_ushort
BYTE        = ctypes.c_ubyte
DWORD64     = ctypes.c_ulonglong

CONTEXT_AMD64           = 0x00100000
CONTEXT_CONTROL         = (CONTEXT_AMD64 | 0x00000001)
CONTEXT_INTEGER         = (CONTEXT_AMD64 | 0x00000002)
CONTEXT_SEGMENTS        = (CONTEXT_AMD64 | 0x00000004)
CONTEXT_FLOATING_POINT  = (CONTEXT_AMD64 | 0x00000008)
CONTEXT_DEBUG_REGISTERS = (CONTEXT_AMD64 | 0x00000010)
CONTEXT_MMX_REGISTERS   = CONTEXT_FLOATING_POINT
CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT)
CONTEXT_ALL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS |
                CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS)
CONTEXT_EXCEPTION_ACTIVE    = 0x0000000008000000
CONTEXT_SERVICE_ACTIVE      = 0x0000000010000000
CONTEXT_EXCEPTION_REQUEST   = 0x0000000040000000
CONTEXT_EXCEPTION_REPORTING = 0x0000000080000000


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

class XMM_SAVE_AREA32(ctypes.Structure):
    _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(ctypes.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(ctypes.Union):
    _fields_=[
              ("FltSave", XMM_SAVE_AREA32),
              ("DummyStruct", DUMMYSTRUCTNAME)
              ]

class CONTEXT64(ctypes.Structure):
    _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)
]

My code implementation:

KERNEL32 = ctypes.windll.kernel32
ctx = constants.CONTEXT64()
ctx.ContextFlags = constants.CONTEXT_FULL
_GetThreadContext = KERNEL32.GetThreadContext
_GetThreadContext.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
_GetThreadContext.restype = ctypes.c_uint
_SetThreadContext = KERNEL32.SetThreadContext
_SetThreadContext.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
_SetThreadContext.restype = ctypes.c_uint

if KERNEL32.SuspendThread(thandle) == -1:
   print("[!] Failed to suspend thread, exit...")
   print("[!] Last error code: " + str(KERNEL32.GetLastError()))
   sys.exit(0)
if not _GetThreadContext(thandle, ctypes.byref(ctx)):
   print("[!] Failed to get thread context, exit...")
   print("[!] Last error code: " + str(KERNEL32.GetLastError()))
   sys.exit(0)

Notes: I use notepad for testing as the target process (also tried with puttyx64)
thandle is tested and does work with other calls and suspend.
thandle does have the following permissions (0x0400 | 0x0010 | 0x0020 | 0x0008 | 0x0002 | 0x0040 | 0x0800).
No Python errors

What am I doing wrong here?

Thank you

EDIT: Minimal (not)working example (notepad.exe must be running)

import ctypes
import time
import sys

WORD = ctypes.c_ushort
BYTE = ctypes.c_ubyte
DWORD64 = ctypes.c_ulonglong
DWORD = ctypes.c_uint
ULONG = ctypes.c_ulong
LONG = ctypes.c_long
CHAR = ctypes.c_char

CONTEXT_AMD64           = 0x00100000
CONTEXT_CONTROL         = (CONTEXT_AMD64 | 0x00000001)
CONTEXT_INTEGER         = (CONTEXT_AMD64 | 0x00000002)
CONTEXT_SEGMENTS        = (CONTEXT_AMD64 | 0x00000004)
CONTEXT_FLOATING_POINT  = (CONTEXT_AMD64 | 0x00000008)
CONTEXT_DEBUG_REGISTERS = (CONTEXT_AMD64 | 0x00000010)
CONTEXT_MMX_REGISTERS   = CONTEXT_FLOATING_POINT
CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT)
CONTEXT_ALL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS |
                CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS)
CONTEXT_EXCEPTION_ACTIVE    = 0x0000000008000000
CONTEXT_SERVICE_ACTIVE      = 0x0000000010000000
CONTEXT_EXCEPTION_REQUEST   = 0x0000000040000000
CONTEXT_EXCEPTION_REPORTING = 0x0000000080000000


class PROCESSENTRY32(ctypes.Structure):
    _fields_ = [
        ('dwSize',              DWORD),
        ('cntUsage',            DWORD),
        ('th32ProcessID',       DWORD),
        ('th32DefaultHeapID',   ctypes.POINTER(ULONG)),
        ('th32ModuleID',        DWORD),
        ('cntThreads',          DWORD),
        ('th32ParentProcessID', DWORD),
        ('pcPriClassBase',      LONG),
        ('dwFlags',             DWORD),
        ('szExeFile',           CHAR * 260)
    ]

class THREADENTRY32(ctypes.Structure):
    _fields_ = [
        ('dwSize',              DWORD),
        ('cntUsage',            DWORD),
        ('th32ThreadID',        DWORD),
        ('th32OwnerProcessID',  DWORD),
        ('tpBasePri',           DWORD),
        ('tpDeltaPri',          DWORD),
        ('dwFlags',             DWORD)
    ]


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

class XMM_SAVE_AREA32(ctypes.Structure):
    _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(ctypes.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(ctypes.Union):
    _fields_=[
              ("FltSave", XMM_SAVE_AREA32),
              ("DummyStruct", DUMMYSTRUCTNAME)
              ]

class CONTEXT64(ctypes.Structure):
    _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)
]

KERNEL32 = ctypes.WinDLL('kernel32', use_last_error=True)
PSAPI = ctypes.WinDLL('psapi', use_last_error=True)
name = "notepad.exe"

hSnapshot = KERNEL32.CreateToolhelp32Snapshot(0x00000002 | 0x00000004, 0)
pe32 = PROCESSENTRY32()
pe32.dwSize = ctypes.sizeof(PROCESSENTRY32)
processes = []
if KERNEL32.Process32First(hSnapshot, ctypes.byref(pe32)):
    while True:
        processes.append({'pid': pe32.th32ProcessID, 'name': pe32.szExeFile})
        if not KERNEL32.Process32Next(hSnapshot, ctypes.byref(pe32)):
            break

for n in processes:
    if n['name'] == bytes(name, "utf-8"):
        pid = n['pid']
if not pid:
    sys.exit(0)

te32 = THREADENTRY32()
te32.dwSize = ctypes.sizeof(THREADENTRY32)
threads = []
if KERNEL32.Thread32First(hSnapshot, ctypes.byref(te32)):
    while True:
        if te32.th32OwnerProcessID == pid:
            threads.append(te32.th32ThreadID)
        if not KERNEL32.Thread32Next(hSnapshot, ctypes.byref(te32)):
            break

access = (0x0400 | 0x0010 | 0x0020 | 0x0008 | 0x0002 | 0x0040 | 0x0800)
thandle = KERNEL32.OpenThread(access, 0, threads[0])
ctx = CONTEXT64()
ctx.ContextFlags = CONTEXT_FULL
_GetThreadContext = KERNEL32.GetThreadContext
_GetThreadContext.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
_GetThreadContext.restype = ctypes.c_uint
_SetThreadContext = KERNEL32.SetThreadContext
_SetThreadContext.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
_SetThreadContext.restype = ctypes.c_uint
if KERNEL32.SuspendThread(thandle) == -1:
    print("[!] Failed to suspend thread, exit...")
    print("[!] Last error code: " + str(ctypes.get_last_error()))
    sys.exit(0)
if not _GetThreadContext(thandle, ctypes.byref(ctx)):
    print("[!] Failed to get thread context, exit...")
    print("[!] Last error code: " + str(ctypes.get_last_error()))
    sys.exit(0)
if not _SetThreadContext(thandle, ctypes.byref(ctx)):
    print("[!] Failed to set thread context, exit...")
    print("[!] Last error code: " + str(ctypes.get_last_error()))
    sys.exit(0)
python
windows
winapi
64-bit
ctypes
asked on Stack Overflow Jul 23, 2020 by Sauseee • edited Jul 24, 2020 by Sauseee

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0