import ctypes, sys, io from ctypes import wintypes, byref, sizeof, c_char sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') k = ctypes.windll.kernel32 DEBUG_ONLY_THIS_PROCESS = 0x00000002 DBG_CONTINUE = 0x00010002 DBG_EXCEPTION_NOT_HANDLED = 0x80010001 EXCEPTION_BREAKPOINT = 0x80000003 EXCEPTION_ACCESS_VIOLATION = 0xC0000005 CREATE_PROCESS_DEBUG_EVENT = 3 EXCEPTION_DEBUG_EVENT = 1 LOAD_DLL_DEBUG_EVENT = 6 CREATE_THREAD_DEBUG_EVENT = 2 CONTEXT_FULL = 0x00010007 # WOW64 x86 CONTEXT_FULL (CONTROL|INTEGER|SEGMENTS) BP_OPEN = 0x00439380 # stream open method entry (thiscall, ecx=this, path at [this+4]) class STARTUPINFO(ctypes.Structure): _fields_=[("cb",wintypes.DWORD),("lpReserved",wintypes.LPWSTR),("lpDesktop",wintypes.LPWSTR), ("lpTitle",wintypes.LPWSTR),("dwX",wintypes.DWORD),("dwY",wintypes.DWORD), ("dwXSize",wintypes.DWORD),("dwYSize",wintypes.DWORD),("dwXCountChars",wintypes.DWORD), ("dwYCountChars",wintypes.DWORD),("dwFillAttribute",wintypes.DWORD),("dwFlags",wintypes.DWORD), ("wShowWindow",wintypes.WORD),("cbReserved2",wintypes.WORD),("lpReserved2",ctypes.c_void_p), ("hStdInput",wintypes.HANDLE),("hStdOutput",wintypes.HANDLE),("hStdError",wintypes.HANDLE)] class PROCESS_INFORMATION(ctypes.Structure): _fields_=[("hProcess",wintypes.HANDLE),("hThread",wintypes.HANDLE), ("dwProcessId",wintypes.DWORD),("dwThreadId",wintypes.DWORD)] class EXCEPTION_RECORD(ctypes.Structure): _fields_=[("ExceptionCode",wintypes.DWORD),("ExceptionFlags",wintypes.DWORD), ("ExceptionRecord",ctypes.c_void_p),("ExceptionAddress",ctypes.c_void_p), ("NumberParameters",wintypes.DWORD),("ExceptionInformation",ctypes.c_void_p*15)] class EXCEPTION_DEBUG_INFO(ctypes.Structure): _fields_=[("ExceptionRecord",EXCEPTION_RECORD),("dwFirstChance",wintypes.DWORD)] class DEBUG_EVENT(ctypes.Structure): class _U(ctypes.Union): _fields_=[("Exception",EXCEPTION_DEBUG_INFO),("raw",c_char*160)] _fields_=[("dwDebugEventCode",wintypes.DWORD),("dwProcessId",wintypes.DWORD), ("dwThreadId",wintypes.DWORD),("u",_U)] # WOW64_CONTEXT x86 class WOW64_FLOATING_SAVE_AREA(ctypes.Structure): _fields_=[("ControlWord",wintypes.DWORD),("StatusWord",wintypes.DWORD),("TagWord",wintypes.DWORD), ("ErrorOffset",wintypes.DWORD),("ErrorSelector",wintypes.DWORD),("DataOffset",wintypes.DWORD), ("DataSelector",wintypes.DWORD),("RegisterArea",c_char*80),("Cr0NpxState",wintypes.DWORD)] class WOW64_CONTEXT(ctypes.Structure): _fields_=[("ContextFlags",wintypes.DWORD),("Dr0",wintypes.DWORD),("Dr1",wintypes.DWORD), ("Dr2",wintypes.DWORD),("Dr3",wintypes.DWORD),("Dr6",wintypes.DWORD),("Dr7",wintypes.DWORD), ("FloatSave",WOW64_FLOATING_SAVE_AREA),("SegGs",wintypes.DWORD),("SegFs",wintypes.DWORD), ("SegEs",wintypes.DWORD),("SegDs",wintypes.DWORD),("Edi",wintypes.DWORD),("Esi",wintypes.DWORD), ("Ebx",wintypes.DWORD),("Edx",wintypes.DWORD),("Ecx",wintypes.DWORD),("Eax",wintypes.DWORD), ("Ebp",wintypes.DWORD),("Eip",wintypes.DWORD),("SegCs",wintypes.DWORD),("EFlags",wintypes.DWORD), ("Esp",wintypes.DWORD),("SegSs",wintypes.DWORD),("ExtendedRegisters",c_char*512)] app = b"Z:\\Development\\devl\\Havoc\\HAVOC_NOCD.EXE" cwd = b"Z:\\Development\\devl\\Havoc" si=STARTUPINFO(); si.cb=sizeof(si) pi=PROCESS_INFORMATION() ok=k.CreateProcessA(app, None, None, None, False, DEBUG_ONLY_THIS_PROCESS, None, cwd, byref(si), byref(pi)) if not ok: print("CreateProcess failed", k.GetLastError()); sys.exit(1) print("launched pid", pi.dwProcessId) hProc=None; orig=None; threads={} def rpm(addr,n): buf=(c_char*n)(); rd=ctypes.c_size_t(0) k.ReadProcessMemory(hProc, ctypes.c_void_p(addr), buf, n, byref(rd)) return bytes(buf[:rd.value]) def wpm(addr,data): wr=ctypes.c_size_t(0) return k.WriteProcessMemory(hProc, ctypes.c_void_p(addr), data, len(data), byref(wr)) EXIT_PROCESS_DEBUG_EVENT=5; OUTPUT_DEBUG_STRING_EVENT=8 import time evt=DEBUG_EVENT(); hit=0; done=False; dllcount=0 t0=time.time() while not done: if time.time()-t0 > 30: print("=== 30s budget: dumping thread states ===") for tid,hThr in threads.items(): ctx=WOW64_CONTEXT(); ctx.ContextFlags=CONTEXT_FULL if k.Wow64GetThreadContext(hThr, byref(ctx)): b=rpm(ctx.Eip,8) print(" tid %d EIP=0x%08X bytes=%s"%(tid, ctx.Eip, b.hex())) k.TerminateProcess(pi.hProcess,0); break if not k.WaitForDebugEvent(byref(evt), 3000): continue code=evt.dwDebugEventCode; cont=DBG_CONTINUE if code==CREATE_PROCESS_DEBUG_EVENT: hProc=pi.hProcess threads[evt.dwThreadId]=pi.hThread orig=rpm(BP_OPEN,1) okw=wpm(BP_OPEN, b"\xCC") chk=rpm(BP_OPEN,1) print("set BP at 0x%X (orig=%s wpm_ok=%s now=%s)"%(BP_OPEN, orig.hex(), bool(okw), chk.hex())) elif code==EXIT_PROCESS_DEBUG_EVENT: print("PROCESS EXITED code=0x%X"%(evt.u.Exception.ExceptionRecord.ExceptionCode)); done=True elif code==LOAD_DLL_DEBUG_EVENT: dllcount+=1 elif code==CREATE_THREAD_DEBUG_EVENT: # track new thread handle: reopen by tid h=k.OpenThread(0x1FFFFF, False, evt.dwThreadId) if h: threads[evt.dwThreadId]=h elif code==EXCEPTION_DEBUG_EVENT: er=evt.u.Exception.ExceptionRecord exc=er.ExceptionCode & 0xffffffff addr=er.ExceptionAddress or 0 if exc==EXCEPTION_BREAKPOINT and addr==BP_OPEN: hThr=threads.get(evt.dwThreadId) ctx=WOW64_CONTEXT(); ctx.ContextFlags=CONTEXT_FULL k.Wow64GetThreadContext(hThr, byref(ctx)) this=ctx.Ecx pathptr=this+4 raw=rpm(pathptr,160) s=raw.split(b"\x00")[0] hit+=1 print("BP hit #%d: this=0x%08X path@0x%08X = %r"%(hit,this,pathptr,s.decode('latin1'))) # restore and step back so it re-executes original wpm(BP_OPEN, orig) ctx.Eip=BP_OPEN ctx.ContextFlags=CONTEXT_FULL k.Wow64SetThreadContext(hThr, byref(ctx)) if hit>=3: print("captured enough; terminating child") k.TerminateProcess(hProc,0); done=True elif exc==EXCEPTION_ACCESS_VIOLATION: hThr=threads.get(evt.dwThreadId) ctx=WOW64_CONTEXT(); ctx.ContextFlags=CONTEXT_FULL k.Wow64GetThreadContext(hThr, byref(ctx)) print("ACCESS_VIOLATION eip=0x%08X eax=0x%08X esi=0x%08X ecx=0x%08X (firstchance=%d)"%( ctx.Eip, ctx.Eax, ctx.Esi, ctx.Ecx, evt.u.Exception.dwFirstChance)) k.TerminateProcess(hProc,0); done=True elif exc==EXCEPTION_BREAKPOINT: pass # initial ntdll bp else: cont=DBG_EXCEPTION_NOT_HANDLED k.ContinueDebugEvent(evt.dwProcessId, evt.dwThreadId, cont) print("debugger done")