Common Assembly-Language Constructs

[Previous] [Next]

Up to this point, I've just been covering basic assembly-language instructions. Now I want to start looking at various assembly-language constructs that you'll encounter and explain how you identify them and translate them into higher level operations.

FS Register Access

In Win32 operating systems, the FS register is special because the pointer to the thread information block (TIB) is stored in it. The TIB is also called the thread environment block (TEB). The TIB holds all the thread-specific data so that the operating system can keep your thread access straight. This thread-specific data includes all the structured exception handling (SEH) chains, thread local storage, and other information needed internally. For more information about SEH, see Chapter 9. For an example of thread local storage, see the MemStress discussion in Chapter 15.

The TIB is stored in a special memory segment, and when the operating system needs to access the TIB, it converts the FS register plus an offset into a normal linear address. When you see an instruction accessing the FS register, one of the following operations is underway: an SEH frame is being created or destroyed, the TIB is being accessed, or thread local storage is being accessed.

Creating or Destroying an SEH Frame

The first instructions after setting up the stack frame are often something like the following code, which is standard code to start a __try block. The first node in the chain of SEH handlers is at offset 0 in the TIB. In the disassembly below, the compiler is pushing a data value and a pointer to a function on the stack. That function is __except_handler3. The first MOV instruction is accessing the TIB; the offset of 0 indicates that a node is being added to the top of the exception chain. The last two instructions indicate where the code moves the actual node to the chain.

PUSH 004060d0 PUSH 004014a0 MOV EAX , FS:[00000000] PUSH EAX MOV DWORD PTR FS:[0] , ESP

Although this example is nice and clean, the compiler doesn't always produce such tidy code. Sometimes it spreads the SEH frame creation throughout the code. Depending on the code generation and optimization flags, the compiler moves instructions around to take better advantage of the CPU's pipelining. The following disassembly example, in which KERNEL32.DLL symbols are loaded, shows the start of the Microsoft Windows NT 4 IsBadReadPtr function.

MOV EAX , FS:[00000000h] PUSH EBP MOV EBP , ESP PUSH 0FFh PUSH 77F3D1E8h PUSH _except_handler3 PUSH EAX MOV EAX , [BaseStaticServerData] MOV DWORD PTR FS:[00000000h] , ESP

Destroying an SEH frame is much more mundane than creating one, as the following code shows. The key item to remember is that any access of FS:[0] means SEH.

MOV ECX , DWORD PTR [EBP-10h] MOV DWORD PTR FS:[0] , ECX

Accessing the TIB

The value at FS:[18] is the linear address of the TIB structure. In the following code, the Windows 2000 implementation of GetCurrentThreadId gets the linear address of the TIB, and at offset 0x24, it gets the actual thread ID.

GetCurrentThreadId: MOV EAX , FS:[00000018h] MOV EAX , DWORD PTR [EAX+024h] RET

Accessing Thread Local Storage

Thread local storage is a Win32 mechanism that allows you to have variables that are global, but each thread has its own instance of the global variables. Offset 0x2C in the TIB structure is the pointer to the thread local storage array. The following disassembly shows how to access the thread local storage pointer.

MOV ECX , DWORD PTR FS:[2Ch] MOV EDX , DWORD PTR [ECX+EAX*4]

Structure and Class References

Because so much of Windows development is structures and classes, I want to spend some time going over how you access that memory. Although structures and classes are convenient to deal with in high-level languages, at the assembly-language level they really don't exist. In high-level languages, a structure and a class are just shorthand ways to specify offsets into a blob of memory. If you're dealing with Visual Basic code, a Visual Basic form is identical to a class at the assembly-language level.

For the most part, the compilers lay out memory for your structures and classes just as you specify. Occasionally, the compiler will pad fields to keep them on natural memory boundaries, which for x86 CPUs is 4 or 8 bytes.

Structure and class references are denoted by a register and a memory offset. In MyStruct below, the comments to the right of each member show the offset from the beginning of the structure for each member. After the MyStruct definition, I show various ways of accessing the structure fields.

typedef struct tag_MyStruct { DWORD dwFirst ; // 0-byte offset char szBuff[ 256 ] ; // 4-byte offset int iVal ; // 260-byte offset } MyStruct , * PMyStruct ; void FillStruct ( PMyStruct pSt ) { char szName[] = "Pam\n" ; __asm { MOV EAX , pSt // Place pSt into EAX. Below, I'm using the // direct offsets in the assembly language to show // what they look like in a disassembly. The // inline assembler allows you to use the normal // <struct>.<field> references. // C code : pSt->dwFirst = 23 ; MOV DWORD PTR [EAX] , 17h // C code: pSt->iVal = 0x33 ; MOV DWORD PTR [EAX + 0104h] , 0x33 // C code: strcpy ( pSt->szBuff , szName ) ; LEA ECX , szName // Push szName on the stack. PUSH ECX LEA ECX , [EAX + 4] // Get to the szBuff field. PUSH ECX CALL strcpy ADD ESP , 8 // strcpy is a __cdecl function. // C code: pSt->szBuff[ 1 ] = 'A' ; MOV BYTE PTR [EAX + 5] , 41h // C code: printf ( pSt->szBuff ) ; MOV EAX , pSt // Get pSt back. EAX was destroyed // on the call to strcpy. LEA ECX , [EAX + 4] PUSH ECX CALL DWORD PTR [printf] ADD ESP , 4 // printf is a __cdecl function. } }



Debugging Applications
Debugging Applications for MicrosoftВ® .NET and Microsoft WindowsВ® (Pro-Developer)
ISBN: 0735615365
EAN: 2147483647
Year: 2000
Pages: 122
Authors: John Robbins

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