| EAX | Multipurpose. Return values from a function are usually stored in EAX. Low 16 bits are referenced as AX. AX can be further subdivided into AL (the low 8 bits), and AH (the upper 8 bits of AX). |
| EBX | Multipurpose. Low 16 bits are referenced as BX. BX can be further subdivided into BL (the low 8 bits), and BH (the upper 8 bits of BX). |
| ECX | Multipurpose. Often used as a counter, for example, to hold the number of loop iterations that should be performed. Low 16 bits are referenced as CX. CX can be further subdivided into CL (the low 8 bits), and CH (the upper 8 bits of CX). |
| EDX | Multipurpose. Low 16 bits are referenced as DX. DX can be further subdivided into DL (the low 8 bits), and DH (the upper 8 bits of DX). |
| ESI | Multipurpose. In certain operations that move or compare memory, ESI contains the source address. Low 16 bits are referenced as SI. |
| EDI | Multipurpose. In certain operations that move or compare memory, EDI contains the destination address. Low 16 bits are referenced as DI. |
| ESP | Stack pointer. Implicitly changed by PUSH, POP, CALL, and RET instructions. |
| EBP | Base pointer. Usually points to the current stack frame for a procedure. Procedure parameters are usually at positive offsets from EBP (for example, EBP+8). Local variables are usually at negative offsets (for example, EBP-16). Sometimes, optimizing compilers won't use a stack frame, and use EBP as a multipurpose register. |
| EFLAGS | Rarely directly referenced. Instead, instructions implicitly set or clear bitfields within the EFLAGS register to represent a certain state. For example, when the result of a mathematical operation is zero, the Zero flag is toggled on in the EFLAGS register. The conditional jump instructions make use of the EFLAGS register. |
| FS | 16-bit. Under Win32, the FS register points to a data structure with information pertaining to the current thread. FS is a segment register (segment registers are beyond the scope of this discussion). Intel CPUs have six segment registers, but the operating system sets them up and maintains them. Win32 compilers only need to explicitly refer to the FS segment register, which is used for things like structured exception handling and thread local storage. |
Figure 2 InstructionDemo.CPP
//==========================================
// Matt Pietrek
// Microsoft Systems Journal, February 1998
// Program: InstructionDemo.CPP
// FILE: InstructionDemo.CPP
//==========================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
// Force these functions inline (/O2 would normally do this
#pragma intrinsic( memset, strlen, strcmp )
__declspec(thread) int tlsVariable = 0; // Make a thread local variable
int g_myGlobalVariable; // Make a global variable
void MySubProcedure( void );
int main( int argc, char *argv[] )
{
char szBuffer[128];
char *pszString = "Hello";
unsigned long localUnsignedLong = 2;
unsigned char localUnsignedChar = 2;
long localSignedLong = 2;
char localSignedChar = 2;
int i;
g_myGlobalVariable = 0x12345678; // Assignment to global
localSignedLong = localSignedChar; // signed type promotion
// Conditional execution
if ( localUnsignedLong == 2 )
localSignedLong = 1;
else
localSignedLong = 2;
// Using TEST
if ( localUnsignedLong & 0x00040008 )
i = 3;
// AND'ing off bitfields
localUnsignedLong &= 0x01020304;
// OR'ing on bitfields
localSignedLong |= 0x05060708;
// LOOP code
for ( i = 0; i < 4; i++ )
localUnsignedLong += i;
// Procedure invocation
printf( "%u %u %08X %s", localUnsignedLong, argc, &argc, szBuffer );
// Using STOSD / STOSB
memset( szBuffer, 0, sizeof(szBuffer) );
// Using SCASB
i = strlen( szBuffer );
MySubProcedure( );
return 0;
}
void MySubProcedure( void )
{
tlsVariable = 2;
// Use of try/except code
__try
{
g_myGlobalVariable = 2;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
g_myGlobalVariable = 4;
}
}
Figure 3 InstructionDemo Mixed Source and Assembly
int main( int argc, char *argv[] )
{
401000: PUSH EBP
401001: MOV EBP,ESP
401003: SUB ESP,00000098
401009: PUSH EDI
char *pszString = "Hello";
40100A: MOV DWORD PTR [EBP-0000008C],00406030
unsigned long localUnsignedLong = 2;
401014: MOV DWORD PTR [EBP-00000088],00000002
unsigned char localUnsignedChar = 2;
40101E: MOV BYTE PTR [EBP-00000094],02
long localSignedLong = 2;
401025: MOV DWORD PTR [EBP-00000084],00000002
char localSignedChar = 2;
40102F: MOV BYTE PTR [EBP-00000098],02
g_myGlobalVariable = 0x12345678; // Assignment to global
401036: MOV DWORD PTR [004088E8],12345678
localSignedLong = localSignedChar; // signed type promotion
401040: MOVSX EAX,BYTE PTR [EBP-00000098]
401047: MOV DWORD PTR [EBP-00000084],EAX
// Conditional execution
if ( localUnsignedLong == 2 )
40104D: CMP DWORD PTR [EBP-00000088],02
401054: JNE 00401062
localSignedLong = 1;
401056: MOV DWORD PTR [EBP-00000084],00000001
else
401060: JMP 0040106C
localSignedLong = 2;
401062: MOV DWORD PTR [EBP-00000084],00000002
// Using TEST
if ( localUnsignedLong & 0x00040008 )
40106C: MOV ECX,DWORD PTR [EBP-00000088]
401072: AND ECX,00040008
401078: TEST ECX,ECX
40107A: JE 00401086
i = 3;
40107C: MOV DWORD PTR [EBP-00000090],00000003
// AND'ing off bitfields
localUnsignedLong &= 0x01020304;
401086: MOV EDX,DWORD PTR [EBP-00000088]
40108C: AND EDX,01020304
401092: MOV DWORD PTR [EBP-00000088],EDX
// OR'ing on bitfields
localSignedLong |= 0x05060708;
401098: MOV EAX,DWORD PTR [EBP-00000084]
40109E: OR EAX,05060708
4010A3: MOV DWORD PTR [EBP-00000084],EAX
// LOOP code
for ( i = 0; i < 4; i++ )
4010A9: MOV DWORD PTR [EBP-00000090],00000000
4010B3: JMP 004010C4
4010B5: MOV ECX,DWORD PTR [EBP-00000090]
4010BB: ADD ECX,01
4010BE: MOV DWORD PTR [EBP-00000090],ECX
4010C4: CMP DWORD PTR [EBP-00000090],04
4010CB: JNL 004010E1
localUnsignedLong += i;
4010CD: MOV EDX,DWORD PTR [EBP-00000088]
4010D3: ADD EDX,DWORD PTR [EBP-00000090]
4010D9: MOV DWORD PTR [EBP-00000088],EDX
4010DF: JMP 004010B5
// Procedure invocation
printf( "%u %u %08X %s", localUnsignedLong, argc, &argc, szBuffer );
4010E1: LEA EAX,[EBP-80]
4010E4: PUSH EAX
4010E5: LEA ECX,[EBP+08]
4010E8: PUSH ECX
4010E9: MOV EDX,DWORD PTR [EBP+08]
4010EC: PUSH EDX
4010ED: MOV EAX,DWORD PTR [EBP-00000088]
4010F3: PUSH EAX
4010F4: PUSH 00406038
4010F9: CALL 004011C0
4010FE: ADD ESP,14
// Using STOSD / STOSB
memset( szBuffer, 0, sizeof(szBuffer) );
401101: MOV ECX,00000020
401106: XOR EAX,EAX
401108: LEA EDI,[EBP-80]
40110B: REP STOSD
// Using SCASB
i = strlen( szBuffer );
40110D: LEA EDI,[EBP-80]
401110: OR ECX,FF
401113: XOR EAX,EAX
401115: REPNE SCASB
401117: NOT ECX
401119: ADD ECX,FF
40111C: MOV DWORD PTR [EBP-00000090],ECX
MySubProcedure( );
401122: CALL 0040112E
return 0;
401127: XOR EAX,EAX
}
401129: POP EDI
40112A: MOV ESP,EBP
40112C: POP EBP
40112D: RET
void MySubProcedure( void )
{
40112E: PUSH EBP
40112F: MOV EBP,ESP
401131: PUSH FF
401133: PUSH 00405058
401138: PUSH 004012F8
40113D: MOV EAX,FS:[00000000]
401143: PUSH EAX
401144: MOV DWORD PTR FS:[00000000],ESP
40114B: SUB ESP,08
40114E: PUSH EBX
40114F: PUSH ESI
401150: PUSH EDI
401151: MOV DWORD PTR [EBP-18],ESP
tlsVariable = 2;
401154: MOV EAX,[004088EC]
401159: MOV ECX,DWORD PTR FS:[0000002C]
401160: MOV EDX,DWORD PTR [ECX+EAX*4]
401163: MOV DWORD PTR [EDX+00000004],00000002
__try
{
40116D: MOV DWORD PTR [EBP-04],00000000
g_myGlobalVariable = 2;
401174: MOV DWORD PTR [004088E8],00000002
40117E: MOV DWORD PTR [EBP-04],FFFFFFFF
401185: JMP 004011A1
__except( EXCEPTION_EXECUTE_HANDLER )
401187: MOV EAX,00000001
40118C: RET
40118D: MOV ESP,DWORD PTR [EBP-18]
g_myGlobalVariable = 4;
401190: MOV DWORD PTR [004088E8],00000004
}
40119A: MOV DWORD PTR [EBP-04],FFFFFFFF
}
4011A1: MOV ECX,DWORD PTR [EBP-10]
4011A4: MOV DWORD PTR FS:[00000000],ECX
4011AB: POP EDI
4011AC: POP ESI
4011AD: POP EBX
4011AE: MOV ESP,EBP
4011B0: POP EBP
4011B1: RET