Putting the Context in Context

[Previous] [Next]

By now, you should understand the important role that the context structure plays in thread scheduling. The context structure allows the system to remember a thread's state so that the thread can pick up where it left off the next time it has a CPU to run on.

You might be surprised to learn that such a low-level data structure is completely documented in the Platform SDK. However, if you look up the CONTEXT structure in the documentation, all you'll see is this:

"A CONTEXT structure contains processor-specific register data. The system uses CONTEXT structures to perform various internal operations. Currently, there are CONTEXT structures defined for Intel, MIPS, Alpha, and PowerPC processors. Refer to the header file WinNT.h for definitions of these structures."

The documentation does not show you the structure's members and does not describe the members in any way whatsoever because the members depend on which CPU Windows 2000 is running on. In fact, of all the data structures Windows defines, the CONTEXT structure is the only data structure that is CPU-specific.

So what's in the CONTEXT structure? Well, it contains a data member for each register on the host CPU. On an x86 machine, the members are Eax, Ebx, Ecx, Edx, and so on. For the Alpha processor, the members are IntV0, IntT0, IntT1, IntS0, IntRa, IntZero, and so on. The code fragment below shows the complete CONTEXT structure for an x86 CPU.

 typedef struct _CONTEXT { // // The flags values within this flag control the contents of // a CONTEXT record. // // If the context record is used as an input parameter, then // for each portion of the context record controlled by a flag // whose value is set, it is assumed that that portion of the // context record contains valid context. If the context record // is being used to modify a threads context, then only that // portion of the threads context will be modified. // // If the context record is used as an IN OUT parameter to capture // the context of a thread, then only those portions of the thread's // context corresponding to set flags will be returned. // // The context record is never used as an OUT only parameter. // DWORD ContextFlags; // // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT // included in CONTEXT_FULL. // DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_FLOATING_POINT. // FLOATING_SAVE_AREA FloatSave; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_SEGMENTS. // DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_INTEGER. // DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_CONTROL. // DWORD Ebp; DWORD Eip; DWORD SegCs; // MUST BE SANITIZED DWORD EFlags; // MUST BE SANITIZED DWORD Esp; DWORD SegSs; // // This section is specified/returned if the ContextFlags word // contains the flag CONTEXT_EXTENDED_REGISTERS. // The format and contexts are processor specific // BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; } CONTEXT; 

A CONTEXT structure has several sections. CONTEXT_CONTROL contains the control registers of the CPU, such as the instruction pointer, stack pointer, flags, and function return address. (Unlike the x86 processor, which pushes a function's return address on the stack when it makes a call, the Alpha CPU places a function's return address in a register when it makes a call.) CONTEXT_INTEGER identifies the CPU's integer registers; CONTEXT_FLOATING_POINT identifies the CPU's floating-point registers; CONTEXT_SEGMENTS identifies the CPU's segment registers (x86 only); CONTEXT_DEBUG_REGISTERS identifies the CPU's debug registers (x86 only); and CONTEXT_ EXTENDED_REGISTERS identifies the CPU's extended registers (x86 only).

Windows actually lets you look inside a thread's kernel object and grab its current set of CPU registers. To do this, you simply call GetThreadContext:

 BOOL GetThreadContext( HANDLE hThread, PCONTEXT pContext); 

To call this function, just allocate a CONTEXT structure, initialize some flags (the structure's ContextFlags member) indicating which registers you want to get back, and pass the address of the structure to GetThreadContext. The function then fills in the members you've requested.

You should call SuspendThread before calling GetThreadContext; otherwise, the thread might be scheduled and the thread's context might be different from what you get back. A thread actually has two contexts: user mode and kernel mode. GetThreadContext can return only the user-mode context of a thread. If you call SuspendThread to stop a thread but that thread is currently executing in kernel mode, its user-mode context is stable even though SuspendThread hasn't actually suspended the thread yet. But the thread cannot execute any more user-mode code until it is resumed, so you can safely consider the thread suspended and GetThreadContext will work.

The CONTEXT structure's ContextFlags member does not correspond to any CPU registers. This member exists in all CONTEXT structure definitions regardless of the CPU architecture. The ContextFlags member indicates to the GetThreadContext function which registers you want to retrieve. For example, if you want to get the control registers for a thread, you can write something like this:

 // Create a CONTEXT structure. CONTEXT Context; // Tell the system that we are interested in only the // control registers. Context.ContextFlags = CONTEXT_CONTROL; // Tell the system to get the registers associated with a thread. GetThreadContext(hThread, &Context); // The control register members in the CONTEXT structure // reflect the thread's control registers. The other members // are undefined. 

Notice that you must first initialize the ContextFlags member in the CONTEXT structure before calling GetThreadContext. If you want to get a thread's control and integer registers, you should initialize ContextFlags as follows:

 // Tell the system that we are interested // in the control and integer registers. Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; 

Here is the identifier you can use to get all of the thread's important registers (that is, the ones Microsoft deems to be most commonly used):

 // Tell the system we are interested in the important registers. Context.ContextFlags = CONTEXT_FULL; 

CONTEXT_FULL is defined in WinNT.h as shown in the following table.

CPU Type Definition of CONTEXT_FULL
x86 CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS
Alpha CONTEXT_CONTROL | CONTEXT_FLOATING_POINT | CONTEXT_INTEGER

When GetThreadContext returns, you can easily examine any of the thread's register values, but remember that this means writing CPU-dependent code. The following table lists the instruction pointer and stack pointer members of a CONTEXT structure according to the CPU type.

CPU Type Instruction Pointer Stack Pointer
x86 CONTEXT.Eip CONTEXT.Esp
Alpha CONTEXT.Fir CONTEXT.IntSp

It's amazing how much power Windows offers the developer! But, if you think that's cool, you're gonna love this: Windows lets you change the members in the CONTEXT structure and then place the new register values back into the thread's kernel object by calling SetThreadContext:

 BOOL SetThreadContext( HANDLE hThread, CONST CONTEXT *pContext); 

Again, the thread whose context you're changing should be suspended first or the results will be unpredictable.

Before calling SetThreadContext, you must initialize the ContextFlags member of CONTEXT again, as shown here:

 CONTEXT Context; // Stop the thread from running. SuspendThread(hThread); // Get the thread's context registers. Context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread, &Context); // Make the instruction pointer point to the address of your choice. // Here I've arbitrarily set the address instruction pointer to // 0x00010000. #if defined(_ALPHA_) Context.Fir = 0x00010000; #elif defined(_X86_) Context.Eip = 0x00010000; #else #error Module contains CPU-specific code; modify and recompile. #endif // Set the thread's registers to reflect the changed values. // It's not really necessary to reset the ControlFlags member // because it was set earlier. Context.ControlFlags = CONTEXT_CONTROL; SetThreadContext(hThread, &Context); // Resuming the thread will cause it to begin execution // at address 0x00010000. ResumeThread(hThread); 

This will probably cause an access violation in the remote thread; the unhandled exception message box will be presented to the user, and the remote process will be terminated. That's right—the remote process will be terminated, not your process. You will have successfully crashed another process while yours continues to execute just fine!

The GetThreadContext and SetThreadContext functions give you a lot of control over threads, but you should use them with caution. In fact, few applications ever call these functions at all. The functions were added to help debuggers and other tools. But any application can call them.

I'll talk about the CONTEXT structure more in Chapter 24.



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net