0

I have some C++ code executing shellcode on a Windows 7 SP1 (32-bit) virtual machine. The shellcode is meant to run the Windows ShellExecute function.

ShellExecute(0,"open","cmd",NULL,0,SW_MAXIMIZE);

Here is the shellcode. In it, the CALLed address is reversed (because x86 is little-endian). However, the PUSHed strings ('cmd' and 'open') are not. Why?

#include <windows.h>
char code[] =
"\x68\x63\x6d\x64\x00"  // PUSH "cmd" - string already terminated
"\x8B\xDC"              // MOV EBX, ESP:
                        // puts the pointer to the text "cmd" into ebx
"\x6A\x00"              // PUSH the string terminator for 'open'
"\x68\x6f\x70\x65\x6e"  // PUSH "open" onto the stack
"\x8B\xCC"              // MOV ECX, ESP:
                        // puts the pointer to the text "open" into ecx
"\x6A\x03"              // PUSH 3: Push the last argument
"\x33\xC0"              // xor eax, eax: zero out eax
"\x50"                  // PUSH EAX: push second to last argument - 0
"\x50"                  // PUSH EAX: push third to last argument - 0
"\x53"                  // PUSH EBX: push pointer to string 'cmd'       <---- not reversed
"\x51"                  // PUSH ECX: push pointer to string 'open'      <---- not reversed
"\x50"                  // PUSH EAX: push the first argument - 0
"\xB8\xc0\x87\x8e\x76"  // MOV EAX,0x768e87c0: move ShellExecuteA       <---- reversed
                        // address into EAX
"\xff\xD0"              // CALL EAX: call the function ShellExecuteA
;                       // Terminates the C instruction

int main(int argc, char **argv) 
{ 
  LoadLibraryA("Shell32.dll");  // Load shell32.dll library
  int (*func)(); 
  func = (int (*)()) code; 
  (int)(*func)(); 
}

CALL vs. PUSH?

Is it because of the different operations, CALL and PUSH? That doesn't seem to be the base because in this post (albeit on a Linux machine), the PUSHed strings are reversed.

Windows vs. Linux?

But in this article (on a Windows machine), the PUSHed strings are not reversed (like in my shellcode). So is it an OS thing? If so, that's still confusing because endianness is tied to architecture, and I assume all these examples are running on an x86 machine.

Nadim Hussami
  • 349
  • 2
  • 14
  • Because x86 *machine code* is little-endian. So the store done by `push` puts the bytes in the same order they were in the `imm32`. Strings are byte-streams, so you want them in memory in printing order. – Peter Cordes Aug 24 '22 at 14:01
  • Also somewhat related: [How are dw and dd different from db directives for strings?](https://stackoverflow.com/q/38860174) re: your link where you show asm source using `push 0x68732f6e` instead of `push "hs/n"`. That's writing the 32-bit value represented (in little-endian) by the desired sequence of ASCII bytes. – Peter Cordes Aug 24 '22 at 14:11

0 Answers0