My OS is Windows 7 64-bit and my processor is an Intel Core i7-4700MQ running on the x64 architecture. My program is 32-bit.

Recently, I read an article here describing how it is possible and legal in C to write the main function simply as a constant array of integers, characters, floats, doubles, etc. The article went step-by-step in the process of writing assembly in linux and then transforming it into an array of constant integers in order to print "Hello world!\n\0". I had never seen such a program before, so I decided it would be cool to make one that works on Windows.

In my program, I display an ANSI style MessageBox with the title "Made by CaptainObvious!\n\0" and the the message "C and assembly are for real men\n\0". I have the working inline assembly code in my C program shown below :

int main( )
        "sub $0x10, %esp;\n"
        "movl $0x0, 0x0(%esp);\n"
        "lea message, %eax;\n"
        "movl %eax, 0x4(%esp);\n"
        "lea title, %eax;\n"
        "movl %eax, 0x8(%esp);\n"
        "movl $0x00000040, 0xc(%esp);\n"
        "call _MessageBoxA@16;\n"
        "movl $0, %eax;\n"

        "message: .ascii \"C and assembly are for real men.\\n\\0\";"
        "title: .ascii \"Made by CaptainObvious!\\n\\0\";"

    return 0;

Also to ensure that I could implement the technique used by the article, I wrote a simple array of opcodes that functions to return the value of 10 immediately after execution. The test was successfull, and that array is shown below :

const char main[] = { 0x55, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0xe8, 0, 0, 0, 0, 0xb8, 10, 0, 0, 0, 0xc9, 0xc3 };

Yet when I tried to convert my more complex program to a constant array of integers, my program failed to execute with SIGILL (Illegal Instruction). Here is the relavent disassembly output from gdb :

0x00403040  and    $0xffffff89,%ebp
0x00403043  push   %ebp
0x00403044  add    %ch,%al
0x00403046  lock in $0x83,%al ; <--- SIGILL
; ...

Upon seeing the disassembly output from gdb, I suspected that my array of integers were wrong, but although I caught a few mistakes, it still throws the exact same SIGILL exception shown above. Here is what the current integer array is :

const unsigned int main[] =
    0x5589e583, 0xe4f0e800, 0x00000083, 0xec10c704, 0x24000000,
    0x008d053d, 0x00000089, 0x4424048d, 0x055f0000, 0x00894424,
    0x08c74424, 0x0c400000, 0x00e80000, 0x0000b800, 0x000000c9,
    0xc3432061, 0x6e642061, 0x7373656d, 0x626c7920, 0x61726520,
    0x666f7220, 0x7265616c, 0x206d656e, 0x2e0a004d, 0x61646520,
    0x62792043, 0x68726973, 0x204f2754, 0x6f6f6c65, 0x210a00b8,
    0x00000000, 0xc9c39090


Currently to get the hexadecimal opcodes, I dump the .text section of my program using the objdump.exe program that comes with the MinGW package. Here is the output of my program upon dumping the .text section :

Disassembly of section .text:

00000000 <_main>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 e4 f0                and    $0xfffffff0,%esp
   6:   e8 00 00 00 00          call   b <_main+0xb>
   b:   83 ec 10                sub    $0x10,%esp
   e:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  15:   8d 05 3d 00 00 00       lea    0x3d,%eax
  1b:   89 44 24 04             mov    %eax,0x4(%esp)
  1f:   8d 05 5f 00 00 00       lea    0x5f,%eax
  25:   89 44 24 08             mov    %eax,0x8(%esp)
  29:   c7 44 24 0c 40 00 00    movl   $0x40,0xc(%esp)
  30:   00
  31:   e8 00 00 00 00          call   36 <_main+0x36>
  36:   b8 00 00 00 00          mov    $0x0,%eax
  3b:   c9                      leave
  3c:   c3                      ret

0000003d <message>:
  3d:   43                      inc    %ebx
  3e:   20 61 6e                and    %ah,0x6e(%ecx)
  41:   64 20 61 73             and    %ah,%fs:0x73(%ecx)
  45:   73 65                   jae    ac <title+0x4d>
  47:   6d                      insl   (%dx),%es:(%edi)
  48:   62 6c 79 20             bound  %ebp,0x20(%ecx,%edi,2)
  4c:   61                      popa
  4d:   72 65                   jb     b4 <title+0x55>
  4f:   20 66 6f                and    %ah,0x6f(%esi)
  52:   72 20                   jb     74 <title+0x15>
  54:   72 65                   jb     bb <title+0x5c>
  56:   61                      popa
  57:   6c                      insb   (%dx),%es:(%edi)
  58:   20 6d 65                and    %ch,0x65(%ebp)
  5b:   6e                      outsb  %ds:(%esi),(%dx)
  5c:   2e 0a 00                or     %cs:(%eax),%al

0000005f <title>:
  5f:   4d                      dec    %ebp
  60:   61                      popa
  61:   64 65 20 62 79          fs and %ah,%fs:%gs:0x79(%edx)
  66:   20 43 61                and    %al,0x61(%ebx)
  69:   70 74                   jo     df <title+0x80>
  6b:   61                      popa
  6c:   69 6e 4f 62 76 69 6f    imul   $0x6f697662,0x4f(%esi),%ebp
  73:   75 73                   jne    e8 <title+0x89>
  75:   21 0a                   and    %ecx,(%edx)
  77:   00 b8 00 00 00 00       add    %bh,0x0(%eax)
  7d:   c9                      leave
  7e:   c3                      ret
  7f:   90                      nop

This brings me back to my question, how do I convert assembly to a constant array of integer opcodes for my main function?

2 Answers 2


You are executing code on a little-endian machine. This means that this code:

   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 e4 f0                and    $0xfffffff0,%esp
   6:   e8 00 00 00 00          call   b <_main+0xb>

Would be expressed as 32-bit unsigned hex integers as:

  0x83e58955  0x00e8f0e4 ...

What you have is this:

const unsigned int main[] =
    0x5589e583, 0xe4f0e800, ...

Do you see the difference?

  • I see the difference, but now it seems I have the problem of the MessageBox function not being called... Jan 29, 2015 at 4:10
  • As the other answer correctly describes, this is likely due to fixups done when loading the program. Read about relocation (and GuntramBlohm's answer of course) as a start at understanding how this works in practice.
    – Edward
    Jan 29, 2015 at 20:08

In addition to what Edward said about little endianness, if you check the objdump assembly, you'll see the e8 00 00 00 00 instructions at 06 and 31, which both translate to "call the next instruction" or "call the adress just behind myself". This is because the offset part - 00000000 - is a dummy. The compiler doesn't know the real function address at runtime, so it justs emits a 00000000 for the address, and creates relocation entries in a different section of the object file. When the program is run, the loader will resolve these relocation entries to the real ones. (*)

But if you just write the hex bytes in your program, there will be no relocation entries, so the program will really just call (jump to) the "next" address just behind the current one. Which has the interesting side effect that the called code gets executed, returns to where it was called, and just gets executed again. The intended function will not get called. You should probably try and single step your program with a debugger to see what's happening.

Comparing the assembly with your C program, you'll see that the first call (at 6) doesn't directly correspond to anything in your original program. This is quite likely setting up the stack canary, which you don't really need for your example. I'd just replace these 5 bytes with nops (0x90).

The second call, which is at 0x31, is the call to MessageBoxA. We need to fix that to call the real MessageBoxA function. Unfortunately, that's much harder than you'd think, these days. In older version of windows, the DLL that provides the MessageBoxA function (user.dll) always got loaded at the same address, so you could just find out which one it was, and call that address. But, since your technique is exactly what's used by malware in buffer overflow attacks, ASLR was invented - each time your program gets run, user.dll gets a different base address, which means MessageBoxA is at a different address, so you don't know what to call.

Defeating ASLR ist not easy (it's not intended to be), and clearly out of scope for this question.

But, in order to not disappoint you too much, and provide you with a working example, you could put your "byte code" into a function, and use main to patch that function:

int main(void) {
    int func[]={ .... };   // your original byte code here
    int *patchpos=(int *)((char *)func+0x32); // this is the address part of the call at 0x31;
    int patchoffs=((char *)MessageBoxA)-((char *)patchpos)+4; // calculate the relative offset of MessageBoxA from the instruction behind patchpos
    *patchpos=patchoffs;   // patch the call
    (*(int (*)())func)();    // call func

Disclaimer: I'm not able to test it right now, maybe i got one of the offsets wrong. Single step it in a debugger if it doesn't work.

That way, the compiler will still emit a relocation entry for MessageBoxA, and the C code can determine the real function address. If you want to get rid of that, and keep the byte array in main, you'll have to patch the compiled .exe to create a relocation entry (hard because you need to know a lot about the PE format), or determine the address at runtime (hard because of ASLR).

(*) I'm oversimplifying a bit here and omitting the trampoline jump in the .plt section. But this answer is already long enough, and i'm not sure that windows handles this in the same way as linux, anyway.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.