Transfer from http://www.woodmann.com/fravia/iceman1.htm
Win32 - INSIDE Debug API ------------------------ (Things you need to know: the mysterious context structure) by Iceman, 20 march 1998 I'm very proud that my previous work "Tweaking with memory in Windows 95" was goodenough to open a new section, " HCU's PAPERS", at Reverser's.That's very stimulating, so I'mback! I hope that will be many other contributors to this ! section.Let's bring light in theshadows BTW, Reverser, I really like the picture for your new section.I'm with you boys, nowand forever And one more thing:! for now one I will send you my essays in .htm format ! .No moreplain text files in this document I will focus on Debug API functions.I think that it is an interesting chapter, who worth a closer lock Note:. in order to use those functions in Windows NT your user must have debug privileges access Right granted.i don't Know for Sure But It Seem That Nt Does Not Grantthis for Default To Administratrs. I Have Started to Work On Part Two of "Tweaking with Memory in Windows 95" THISTIME I Want TO Present a vxd aproach.it's nice to write self-modify code using the linkertrick (make code section w) .but wouldne
t be nicer to relay only on VxD callsto tweak with memory leaving that damned section write protected and without ANY high-levelcalls to functions like VirtualProtect? The target will be this time the virtual memory manger itself.Anyone out there who could HELP ?. The document has the following structure: Chapter1: Functions and structures Chapter2: Debug events Chapter3: Creating or attaching a process for being debugged Chapter4: The main loop:.... WaitForDebugEvent - ContinueDebugEvent Chapter5.Handling debug events Chapter6: GetThreadContext & Set ThreadContext. (Advanced Stuff) 6.1 Thread Contexts Explained 6.2 Injecting Code in Another Process. Chapter7: notes ================================ ============================================================================================================================================================================================================= ============= Chapter1: Functions and Structures ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - a Good Win32 API Reference for Future Reference Is Ok.i Don't Explain Here AllThe Parameters Those Functions Take, Nor All Structures.But For Now, Let's See The API: C ontinueDebugEvent DebugActiveProcess DebugBreak FatalExit FlushInstructionCache GetThreadContext GetThreadSelectorEntry IsDebuggerPresent OutputDebugString ReadProcessMemory IsDebuggerPresent SetThreadContext WaitForDebugEvent WriteProcessMemory As we can see, two old friends: WriteProcessMemory &
ReadProcessMemory.We all knowthem very well, so let's go further. FlushInstructionCache, IsDebuggerPresent, OutputDebugString? Self-explanatory. DebugActiveProcess ------------------ This function allows a debugger to attach to an active process BOOL DebugActiveProcess (DWORD dwProcessId); Parameters:. DWORD dwProcessId: PID of process to attach WaitForDebugEvent ----------------- This function allow the debugger to wait until a debug event heapens in target process BOOL WaitForDebugEvent (LPDEBUG_EVENT lpDebugEvent, DWORD dwMilliseconds); Parameters:.... LPDEBUG_EVENT lpDebugEvent: Pointer to a DEBUG_EVENT type structure This struct will receive info about the debug event trapped DWORD dwMilliseconds: Number of ms to wait.Could be INFINITE. IT IS INFINITE WAITFORDEBUGEVENT DOES NOT RETURN Until A Debug Event Occurs. ContinueDebugevent ------------------ this function allow the debugger to resume a thread thread th at previously raised a debug event BOOL ContinueDebugEvent (DWORD dwProcessId, DWORD dwThreadId, DWORD dwContinueStatus); Parameters:. DWORD dwProcessId: PID of process beeing debugged DWORD dwThreadId: TID of thread to be resumed DWORD dwContinueStatus: DWORD that specify how the thread will continue .Two values defined:.. DBG_CONTINUE & DBG_EXCEPTION_NOT_HANDLED Debug Break ---------- This function causes a breakpoint in current process VOID DebugBreak (VOID); FatalExit --------- This function force the Exit of Caller Process, Transferring Execution To Debugger Void Fatalexit (Int EXITCODE);
Parameters: int exitcode: EXIT code GetthreadContext & set thread context ------------------------------------- Those functions are used to retrieve & set the context of a thread.See chapter 6. BOOL GetThreadContext (hANDLE hThread, lPCONTEXT lpContext); BOOL SetThreadContext (hANDLE hThread, CONST cONTEXT * lpContext); Parameters: hANDLE hThread: A handle to thread whose context is being read or set lPCONTEXT lpContext:.. Pointer to a cONTEXT structure to receive / set context info GetThreadSelectorEntry ---------------------- The GetThreadSelectorEntry function retrieves a descriptor table entry for the specified selector and thread BOOL GetThreadSelectorEntry (hANDLE hThread, DWORD dwSelector, LPLDT_ENTRY lpSelectorEntry); Parameters:.. hANDLE hThread: A handle to thread containing specified selector DWORD dwSelector: Selector Number LPLDT_EN Try lpselectorentry pointer to a structure That Receive descriptor table. ======================================= ============================================================================================================================================================================================================= =======
Chapter2: Debug Events. ---------------------- From Debug API Functions Point of View A Debug Events IS An Object Used to CommunicateWith The Debugger.when a Debug Event occurs in target process the OS inform the debugger aboutthis.The debugger use WaitForDebugEvent to retrieve info about the event that occurred in targetprocess (See chapter 5) .Following debug events exists: 1.CREATE_PROCESS_DEBUG_EVENT & EXIT_PROCESS_DEBUG_EVENT raised every time than a newprocess is created / destroyd by the process being debugged. 2.CREATE_THREAD_DEBUG_EVENT & EXIT_CREATE_THREAD_DEBUG_EVENT raised whenever a newthread object is created / destroyed by the process being debugged. 3.LOAD_DLL_DEBUG_EVENT & UNLOAD_DLL_DEBUG_EVENT generated whenever the target loads / unloads a dll. 4.OUTPUT_DEBUG_STRING_EVENT generated than target calls OutputDebugString 5.Exception_Debug_event generated gen an exception Occurs in target process.thisinclude breakpoint instructions such int 3, DivideOverflow ..... 6.rip_debug_ . EVENT generated when a RIP exception occurs WaitForDebugEvent receives the debug event and returns information about the eventin a DEBUG_EVENT structure.This structure is defined as below in WIN32 AP: typedef struct _DEBUG_EVENT {DWORD dwDebugEventCode; DWORD dwProcessId; DWORD dwThreadId; union {EXCEPTION_DEBUG_INFO Exception ; CREATE_THREAD_DEBUG_INFO CreateThread; CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; EXIT_THREAD_DEBUG_INFO ExitThread; EXIT_PROCESS_DEBUG_INFO ExitProcess; LOAD_DLL_DEBUG_INFO LoadDll; UNLOAD_DLL_DEBUG_INFO UnloadDll; OUTPUT_DEBUG_STRING_INFO DebugString; RIP_INFO RipInfo;} u;
} DEBUG_EVENT; The member dwDebugEventCode contains a value indicating which kind of debug eventswas ocureed.The dwProcessId member contain the PID of process in which the debug event occurred.The union u member is a classic C / C union.It is reflected in a structure whose type is determined by DWORD dwDebugEventCode.This structure contains extended information about theevent that ocurred.I do not list all of them here because it's pointless. Also note that a CREATE_PROCESS_DEBUG_EVENT is generated than a debugger attach to a target process. === ============================================================================================================================================================================================================= ==================================================== Chapter3: CREATINGOR Attaching A Process For Being Debugged ----------------------------------------------- ------------- In this short chapter I present you how to create a process for being debugged, orhow to attach to an already running process 3.1:. Creating a process for being debugged Use CreateProcess to create The process being debugged.call this function with dwCreationFlags parameter with one of following values DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS.If target process is created with DEBUG_PROCESS creation flag than the debugger will receive events from all processes crated by target process.If dwCreationFlags =
DEBUG_ONLY_THIS_PROCESS than the debugger will receive debug events only from target process ignoring child processes.As usually you can use PROCESS_INFORMATION structure to ret rive handles to both the created process and it's primary thread as well as the PID an TID (for primary thread). 3.2 : Attaching to an already running process Use DebugActiveProcess function.If this function returns successfully you are attached to target as if you called CreateProcess with DEBUG_ONLY_THIS_PROCESS flag Note that in WindowsNT DebugActiveProcess can easily fail if we try to attach to a process that was created with. a security descriptor that denies requested access.In WIN95the only thing you have to worry is to pas a valid PID to DebugActiveProcess.That's it man! NT has better security. Attaching to a process is an elegant method but sometimes the loader method is theonly solution .It's up to you what method to use.for A Simple Game Trainer It's Ok to AttachBut if you real want to Do Cool Things ..., better use the loader method.it gives You FullControl over the target process and it's threads. ======================================================================================================================================================================================== ============================================================================================================================================================================================================= =====================
Chapter4: The Main Loop: Waitfordebugevent - Continuedebugevent ----------------------------------------- --------------------- A Minimum Skeleton for Using Debug API Function Is Easy To Implement.all You Have Todo Is To Create a Process for Being Debugged and The Implement Code ? to watch for debug events.I call the part responsible with watching debug events "The Main Loop" .Why Because is verysimple to implement as a While loop.The functions you have to use for this are WaitForDebugEvent -. ContinueDebugEvent As we have seen before WaitForDebugEvent waits for acertain amount of time for a debug event to occur in target process.If a debug event does notoccur in this time the function times-out and return FALSE. If a debug events occurs thanthis function return TRUE, fill a DEBUG_EVENT type structure with info about event type and freeze the thread in witch the debug event ocurred.The programmer is responsible to performevent type checking and take appropriate meassures.After the specific code for handling debugevent is executed we have to use ContinueDebugEvent to resume thread execution and wait forother events to occure.Another thing to worry: the only thread witch is allowed to callWaitForDebugEvent is the thread who created or attached to target process.So let's see somecode : PROCESS_INFORMATION pi; STARTUP_INFO si; DEBUG_EVENT devent; if (CreateProcess (0, "target.exe", 0, 0, FALSE, DEBUG_ONLY_THIS_PROCESS, 0, 0, & si, & pi)) while (TRUE) {{if (WaitForDebugEvent (& devent , 150) // Wait 150 ms for debug event {switch (devent.dwdebugeventcode) {copy crete_process_debug_event: // your handler here
case EXIT_PROCESS_DEBUG_EVENT: // your handler here break; case EXCEPTION_DEBUG_EVENT: // your handler here break;} ContinueDebugEvent (devent.dwProcessId, devent.dwThreadId, DBG_CONTINUE);} else {// other operations}}} // while end here else {MessageBox (0, "Unexpected Load Error", "Fatal Error", MB_OK;} ============================== ============================================================================================================================================================================================================= ================= Chapter5.Handling Debug Events ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --- In previous example we can see that how we can trap debug events and take appropriateactions using case / switch C / C statements.Each debug event has a personal handler whogets executed when corresponding debug event occurs.More information about the debug eventcan be found in union u member of DEBUG_EVENT.As a example let's the structure correspondingto EXCEPTION_DEBUG_EVENT.I choose this because encountering breakpoints and tracing through code generates an exception debug event.See a AP I reference for other events. Typedef struct _exception_debug_info {exception_record exceptionRecord; DWORD DWFIRSTCHANCE;} Exception_Debug_info;
In this case we have to retrieve data we need from another structure, member of EXCEPTION_DEBUG_INFO structure.This is EXCEPTION_RECORD structure in which, finally, we can find all data we need about trapped exception.Let's see: typedef struct _EXCEPTION_RECORD {DWORD ExceptionCode; DWORD ExceptionFlags; struct _exception_record * ExceptionRecord; pvoid exceptionaddress; dword numberparameters; dword exceptioninformation [eXception_maximum_parameters];
DWORD ExceptionCode: Specifyes the type of exception ExceptionFlags: 0 if exception is a continuable exception EXCEPTION_NONCONTINUABLE if exception is not continuable ExceptionRecord:. Pointer to an associated EXCEPTION_RECORD structure PVOID ExceptionAddress: Pointer to the address where exception occurred DWORD NumberParameters: Number of parameters defined in ExceptionInformation ExceptionInformation: Additional 32 bit array.For most exception is undefined Using the information from those structures we can find all we need.We can retrievethe thread there exception occurred, type of exception, if we can continue execution or not, the address where. exception occurred and others. Note that trying to continue a EXCEPTION_NONCONTINUABLE exception type willgenerate a EXCEPTION_NONCONTINUABLE_EXCEPTION exception. Currently used exceptions are EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP.Thefirst exception is raised on a breakpoint hit, the seconds signalizes that trace trapsignals that one instruction has been executed. Using a similar mechanism you can gather information about threads, dll's usedby running process and other things. ==================== ============================================================================================================================================================================================================= ===============================Chapter6: GetthreadContext &
SET ThreadContext (Advanced Stuff) ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------- 6.1 Thread contexts explained --------------------------- I really enjoyed writing this chapter.All others are things easy to figure out.Don'tscare it not really difficult to understand what's going on in this chapter.Before to presentthose two functions and their use I want to remember some basic things about processes and threads. in WIN32 philosophy a process is a object who has an private address space, code, data, and a primary thread.Each process has at the very beginning only one thread.From theprimary thread we can later create other threads which run in the same address space.Contraryto the popularly belief a process does NOT execute any kind of code.The threads are the objectswho executes the code.The thread objects share the same address space and resources but theyhave individual contexts.What means that? Windows95 and WindowsNT are Multitasking and Multithread Operating SYST ems.The OS seems to run all threads in the same time, but this is not true. Every individual thread is scheduled for execution for a short time, and the theOS save the thread state in a structure called CONTEXT structure and goes for the next thread.The information saved in this structure represents the thread context and is formed by: - threads machine registers (CPU registers) - the kernel stack and the user stack address - thread environment block address The the OS encounter again our thread it restores it's. Context Info from AssociatedStructures and Resume Execution Like Nothing Happened. OK, So Let '
s see the CONTEXT structure.Unfortunately seems that Microsoft does notinclude info about this structure API help files. The structure is documented at minimumin winnt.h header file in Watcom compilers (can be elsewhere in others.Keep looking) .Keep in mind that this structure is hardware dependent so expect different implementations for x86, Alpha ... typedef struct _CONTEXT {DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; FLOATING_SAVE_AREA FloatSave; DWORD 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;} CONTEXT; typedef struct _FLOATING_SAVE_AREA {DWORD ControlWord DWORDTUSWORD; DWORD TAGWORD; DWORD ERROROFFSET; DWORD ERRORSELECTOR; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea [SIZE_OF_80387_REGISTERS]; DWORD Cr0NpxState;} FLOATING_SAVE_AREA; typedef FLOATING_SAVE_AREA * PFLOATING_SAVE_AREA; DWORD ContextFlags: the folowing values are defined: CONTEXT_CONTROL // SS: SP, CS: IP, FLAGS, BP CONTEXT_INTEGER // AX, BX, CX, DX, SI, DI CONTEXT_SEGMENTS // DS, ES, FS, GS CONTEXT_FLOATING_POINT // 387 state CONTEXT_DEBUG_REGISTERS // DB 0-3,6,7 CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER> CONTEXT_SEGMENTS) Watch out CONTEXT_FULL does not include Context_debug_registers and context_floating_point.you must specify the independently.it's huge and ugly, isn '
t it? Ok we know now how a CONTEX structure is looking.Now let's see what can we do with this monster.First let's talk a little about GetThreadContext & SetThreadContext. The function GetThreadContext is used to get a thread context. BOOL GetThreadContext (HANDLE hThread, lPCONTEXT lpContext);.. hThread is the handle of thread whose context is to be retrieved lPCONTEXT lpContext is a pointer to a cONTEXT structure PRIOR tO USE GetThreadContext you MUST initialize ContextFlags member withthe appropriate flag.Use This to set the amount of info . to retrieve.For example ifyou specify CONTEXT_CONTROL value for ContextFlags only SS: SP, CS: IP, FLAGS, BPwill be saved The function SetThreadContext is used to set the thread context BOOL SetThreadContext (HANDLE hThread, CONST cONTEXT * lpContext); Like. Before hthread is a handle to destination thread and const context * lpContext is a pointer to a context structure.the Amount of Information Restore determi Ned by contextflags member. You May Want to consider a thread contextwhile the thread is running.i consider this "One Way Ticket to the Hell"
.UseSuspendThread to stop a running thread.Later after you set the context youcan use ResumeThread function.Warning: using ResumeThread does not guaranteethat the target thread will indeed resume executions.Why Every thread have anthread suspend count.When the thread is running the counter? is 0.Every time when we use SuspendThread this counter is incremented by one.So we call SuspendThread.The counter will be updated to 1.But W_95 and W_NT are multithread enviromentsso another thread can call too SuspendThread on our thread.Now the counter is 2.Calling ResumeThread once will have only one effect.The counter is again 1.Thread execution is not resumed until the thread suspend count is 0. (ResumeThread function decrements the suspend counter) .So how can we be sure that the thread resumed execution SIMPLE.EXAMINE The Return Value. If it's 0 the the Thread Was Not Suspended.if It's1 The Thread Was Suspended But Resumed Execution.if It's Greater Than 1 The Thread Suspend Counter Was Decrem ENTED But The Thread Was Not ResMed.a Value of 0xfffffffff Means ThatResuMethread Faled. 6.2 Injecting Code in Another Process. --------------------------- ----------- Now let's take a deep breath.We are almost through.As usually I want to presentyou an interesting trick.Let's inject some code into another process address space.We knowhow, but first let's talk About a little impediment.we needour brand new code.a Virtualalalk, was not provided in Win95 Api.it Seeththis One Along with IT '
s companion VirtualFreeEx exists under NT.If our code is very little we can use the space provided by our compilers: The MS-DOS stub of the PE files, copyright strings or even unnecessary data strings (I dont care too much if in HelpAbout the program says that it was developed by "! ^ $ ## @ * ^ $ f76"). Another method is tosave a code page of target process, overwrite with new code, execute new code, restorecode page.Let's see this step by step . 1. Use CreateProcess to create a process for being debugged 2. Build the "Main Loop" WaitForDebugEvent -... ContinueDebugEvent 3. Stop the target thread Use SuspendThread 4. Use VirtualProtectEx to set a read-write permission to target page 5. Use ReadProcessMemory to save the target page. 6. Use GetThreadContext to save the thread context. 7. Use WriteProcessMemory to write new code page. 8 Make sure that the last instruction in the new code is a INT 3.We need this to take control When out finished.the int 3 Will be trapped by ur little debugger-like application EXCEPTION_DEBUG_EVENT.Make sure that is a EXCEPTION_BREAKPOINT and has occurred at the address there our INT 3 resides. 9. Make a temporary copy of saved CONTEXT structure. 10. Set the new eip in the temporary CONTEXT structure (You now what IS EIP, DIDN '
t you? 11.Resume execution of the thread.Watch it executing our new code.When INT 3 gets executed our little loader will trap it.The target thread is stopped. 12.Restore the original code page using WriteProcessMemory. 13.Restore the protection attributes on target page. 14.Use SetThreadContext to set thread context from the first cONTEXT structure. 15.Resume thread. If we need that our resides in target process address space at the same time with theoriginal code, and our code is BIG we have to commit some memory in the target process addressspace.The code to call VirtuallAlloc is very small, so use previous method to call VirtualAllocin the context of the target process.This will commit memory in target's address space andreturn a pointer to it.Several kb So don't be a fool and startto commit cosmic values like 10 mb.i wonder if there is another method to ustement a virtualalocex under win95.i keep looking. yorn if VirtualAllocEx is implementedin Memphis (future Windows 98) ?. If you ever need to convert a segment relative address in linear virtual address youcan use GetThreadSelectorEntry Final words:. !! WATCH !! the stack.DON'T mess IT.If you do YOU WILL be sorry. ======================================================================================================================================================================== =============================================================================================================================================================================================================