calling MessageBoxA the 'hard way'


Not really a new concept by any stretch but I wanted to do a short writeup actually demonstrating using the PEB to dynamically resolve function addresses. The following code example leverages some of the POC work by Dino, Skape, 0ptyx, Skywing and LSD into a heavily commented example.

The sequence of events in example below is the following:

[*] Walk PEB structure to obtain the base address of kernel32.dll/kernelbase.dll
[*] Resolve the address of LoadLibrary so that User32.dll can be loaded
[*] Call LoadLibrary
[*] Resolve the address of GetModuleHandle to get User32.dll address
[*] Call GetModuleHandle
[*] Get the address of MessageBoxA using GetProcAddress
[*] Call MessageBoxA
[*] Call ExitProcess

The call to "DwmEnableComposition" is needed under Vista and newer Windows to disable the desktop composite manager. The program will crash unless this is done. The composite manager is automatically re-enabled upon program exit.

compatibility settings

Compile it with Dev-C++

Link to source code: message-box-fun.c
/* Calling MessageBoxA the hard way 

   Creds to Dino, Skape, 0ptyx and Skywing for the 
   find_kernel32 and hashing routines
   http://www.hick.org/~mmiller/shellcode/win32/generic.c   
   
   The code is a bit bigger than it could be due to the need 
   to disable the composite manager under windows Vista, 7, 2008
   before calling MessageBoxA.
   
   The reason for using LoadLibraryExA vs LoadLibrary is because LoadLibraryExA
   is found in kernelbase.dll on Windows versions Vista and newer. So a bit of 
   a hack for portability reasons. 

   - dijital1
*/
#include <windows.h>

static char *title __asm__( "title" ) = ".: ciphermonk research :.";
static char *caption __asm__( "caption" ) = "Calling MessageBoxA the 'hard way'";
static char *libname __asm__( "libname" ) = "User32.dll";
static char *winmgr __asm__( "winmgr" ) = "dwmapi.dll";
static char *msgbox __asm__( "msgbox" ) = "MessageBoxA";
static char *dwmcmp __asm__( "dwmcmp" ) = "DwmEnableComposition";


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{    

// intel style assembly language
asm(".intel_syntax noprefix\n");

asm (
   "call find_kernel32\n"  // find base address of kernel32.dll
   "mov ebp,eax\n"         // save the base of kernel32.dll
   "mov ebx,0x27E03A9D\n"  // Hash of LoadLibraryExA 
   "call find_function\n"  // resolve API address
   "mov ecx, 0x10\n"       // Set LOAD_IGNORE_CODE_AUTHZ_LEVEL 
   "push ecx\n"
   "xor ebx, ebx\n"        // Required NULL for second arg to LoadLibraryA
   "push ebx\n"
   "mov ebx, libname\n"    // String containing "User32.dll"
   "push ebx\n"  
   "call eax\n"            // Call LoadLibraryA
    
      /* Windows Vista, 7 and Server 2008 Specific Code */
   
   "call find_kernel32\n"  // find base address of kernel32.dll
   "mov ebp,eax\n"         // save the base of kernel32.dll
   "mov ebx,0x27E03A9D\n"  // Hash of LoadLibraryExA 
   "call find_function\n"  // resolve API address
   "mov ecx, 0x10\n"       // Set LOAD_IGNORE_CODE_AUTHZ_LEVEL
   "push ecx\n"
   "xor ebx, ebx\n"        // Required NULL for second arg to LoadLibraryA
   "push ebx\n"
   "mov ebx, winmgr\n"     // String containing "Dwmapi.dll"
   "push ebx\n"  
   "call eax\n"            // Call LoadLibraryA
   
   "test eax, eax \n"      // Did the call to load dwmapi.dll fail? We must be
   "jz legacy_win\n"       // on pre Vista windows then. Skip the following code
   
   "push eax\n"            // Save base address of Dwmapi.dll
   
   /* Get the Address of DwmEnableComposition */
   "call find_kernel32\n"  // find base address of kernel32.dll
   "mov ebp,eax\n"         // save the base of kernel32.dll
   "mov ebx,0xE553E06F\n"  // Hash of GetProcAddress
   "call find_function\n"  // Resolve Address of GetProcAddress
   "pop ecx\n"             // Restore the Saved address of Dwmapi.dll
   "mov ebx, dwmcmp\n"     // String containing "DwmEnableComposition"
   "push ebx\n"
   "push ecx\n"            // Handle for Dwmapi.dll
   "call eax\n"            // Call GetProcAddress
   
   /* EAX now contains the address of DwmEnableComposition */
   
   /* Disable the Composite Manager */
   "xor ebx, ebx\n"         // Set the DWM_EC_DISABLECOMPOSITION flag
   "push ebx\n"             
   "call eax\n"             // Call DwmEnableComposition with disable flag set
    
"legacy_win:\n"   
    /* Resolve and call GetModuleHandle  */
   "call find_kernel32\n"  // find base address of kernel32.dll
   "mov ebp,eax\n"         // save the base of kernel32.dll
   "mov ebx, 0x48269992\n" // Hash of GetModuleHandle
   "call find_function\n"  // Resolve Address of GetModuleHandle
   "mov ebx, libname\n"    // String containing "User32.dll"
   "push ebx\n"
   "call eax\n"            // Call GetModuleHandle
   
   "push eax\n"            // Save base address of User32.dll
   
   /* Get the Address of MessageBoxA */
   "call find_kernel32\n"  // find base address of kernel32.dll
   "mov ebp,eax\n"         // save the base of kernel32.dll
   "mov ebx,0xE553E06F\n"  // Hash of GetProcAddress
   "call find_function\n"  // Resolve Address of GetProcAddress
   "pop ecx\n"             // Restore the Saved address of User32.dll
   "mov ebx, msgbox\n"     // String containing "MessageBoxA"
   "push ebx\n"
   "push ecx\n"            // Handle for User32.dll
   "call eax\n"            // Call GetProcAddress
  
    /* EAX now contains the address of MessageBoxA */
  
    /* Call MessageBoxA  */
    "xor edx, edx\n"       // Set Message Box behavior
    "push edx\n"
    "mov edx, title\n"     // Set Message Box title
    "push edx\n"
    "mov edx, caption\n"   // Set the Message
    "push edx\n"
    "xor edx, edx\n"       // This Window has no owner
    "push edx\n"
    "call eax\n"           // Call MessageBoxA
    
    
   /* Call ExitProcess  */
   "call find_kernel32\n"   // find base address of kernel32.dll
   "mov ebp,eax\n"          // save the base of kernel32.dll
   "mov ebx, 0xC3F39F16\n"  // Hash of ExitProcess
   "call find_function\n"   // Resolve the Address of ExitProcess
   "xor ebx, ebx\n"         // Set the Return Value
   "push ebx\n"
   "call eax\n"             // Call ExitProcess
  
 /* Assembly Functions Start Here */
    
"find_kernel32:\n"
    "push esi\n"               // Save the ESI register
    "xor eax,eax\n"            // Zero EAX register
    "mov eax, [fs:eax+0x30]\n" // Store the address of PEB in eax
    "mov eax, [eax + 0x0c]\n"  // Get PROCCESS_MODULE_INFO pointer
                               // LOADER data contains DLL list 
    "mov esi, [eax + 0x1c]\n"  // Grab address for NTDLL 
    "lodsd\n"                  // Load second Entry - kernel32.dll under XP
                                                 // - kernelbase.dll under Vista 
    "mov eax, [eax + 0x8]\n"   // Get the base address of kernel32.dll
    "pop esi\n"                // restore ESI register
    "ret\n"                       
);

  /* Funtion that walks through the array of pointers to the function names in
  kernel32, hashes each name and compares the hash until it finds a match
  */

asm(
"find_function:\n" 
    "xor ecx,ecx\n"            // ECX maintains the a counter of the number
                               // of functions checked. Used to later index
                               // to the correct offset for the target
                               // function's entry point
    "mov edi,[ebp+0x3c]\n"     // Offset to value in PE header that's used
    "mov edi,[ebp+edi+0x78]\n" // in the calculation to find the export table

    "add edi,ebp\n"            // Add base kernel32 base address to RVA

"next_function_pointer:\n"         
    "mov edx,[edi+0x20]\n"     // RVA of array of pointers to func names
    "add edx,ebp\n"            // Add base address to RVA to get actual
                               // address of array
    "mov esi,[edx+ecx*4]\n"    // Obtain RVA of function name in array 
    "add esi,ebp\n"            // Add base address to RVA to get actual
                               // address of function name
    "xor eax,eax\n"            // Zero EAX before use in the next sequence
    "cdq\n"                    // Zero EDX - saves a byte ;-)
"hash_next_byte:\n"
    "lodsb\n"                  // Load byte in si into al
    "ror edx,0xd\n"            // Rotate EDX bits 13 positions to the right 
    "add edx,eax\n"            // Add EAX to EDX storing hash in EDX
    "test al,al\n"             // Test for end or function name
    "jnz hash_next_byte\n"     // Loop until all bytes have been hashed
    "inc ecx\n"                // Increment ECX to increment index into array 
    "cmp edx,ebx\n"            // Compare EDX hash against EBX hash
       "jnz  next_function_pointer\n"
    "dec ecx\n"                // Found a match. Decrement ECX by one so that 
                                   // calculation for the function entry point
                                   // will be correct.
    "mov ebx,[edi+0x24]\n"     // RVA of function entry point
    "add ebx,ebp\n"            // Add base address to RVA to get actual entry 
                                    // point address
    "mov cx,[ebx+ecx*2]\n"     // Calculations to walk export directory to 
    "mov ebx,[edi+0x1c]\n"     // Obtain function addresss
    "add ebx,ebp\n"            // http://win32assembly.online.fr/pe-tut7.html
    "mov eax,[ebx+ecx*4]\n"    // for more detail
    "add eax,ebp\n"            // Add Base address to RVA to get the function
                               // entry point
    "ret\n" 
  );

}