This is the result of the exploration into the world of mystery, the world of obfuscated coding. Actually some months back, there was a C code to print “Hello World” but uses no function by name main. It just had a char array, main.
The main idea is to write the very first C code without using the main as a function but as an array. Is it possible? This is what the analysis about. Why is the x86? The title gives a very strong hint that the code is highly architecture dependent. An obfuscated code of one of this kind could be found in the IOCCC here (mullender).
Here comes the attempt to code “Hello World!” in C without using main as a function but in disguise as an int array.
asm(".text\n"); int main[] = { 0x90E58955, 0x8310EC83, 0x9090E4F0, 0x48F445C7, 0xC76C6C65, 0x206FF845, 0x45C76F57, 0x646C72FC, 0x0045C721, 0x0000000A, 0x50F4458D }; asm("call printf\n" ".long 0x0000B890\n.long 0xC3C90000\n");
This is not randomly coded, ofcourse a random code could segfault and lets see the steps for building such array oriented code with their strict limitations. This code is highly architecture specific and would work for i686 Machines. I’vent tested in other architectures but could tell without trying that it would certainly fail upto 95% in other architectures.
Here goes the first C Code for a traditional “Hello World!” program, “hello.c”.
#include <stdio.h> int main(int argc, char *argv[]) { printf("Hello World!\n"); return 0; }
A simple cute looking code above has worn a mysterical mask in the above section.
If you are using GNU/Linux, you would’ve been familiar with -S flag the gcc has. It is used to dump the assembly code of the C program. Here is the assembly dump for the above traditional code.
To get the assembly dump of "hello.c" the following command can be used.
$ gcc -S hello.c
Which dumps the below code in the file, "hello.s"
.file "hello.c" .section .rodata .LC0: .string "Hello World!\n" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $24, %esp andl $-16, %esp movl $0, %eax subl %eax, %esp movl $.LC0, (%esp) call printf movl $0, %eax leave ret .size main, .-main .section .note.GNU-stack,"",@progbits .ident "GCC: (GNU) 3.3.5 (Debian 1:3.3.5-13)"
At the first sight, its a lot actually to see all those symbols above. Still a mystery what the above assembly means, except for few things here and there. We could see the “Hello World!\n” string, then our main function and certain things are very clear. The main part comes where the code is a core x86 assembly with all those % all over that needs a clarity.
The x86 programming manuals could explain much more about the sections. As far as our problem at hand, we can assume the sections are the different blocks where the code/data of the program sits. The .text is called the text section of the code where the executable chunck of code sits. The sections above the .text in the above assembly dump is in the data (.rodata section) area where the code will not be executed under certain architectures.
So, to make our code executable, we have got to place the code on the .text area to be on the safer side, though certain architectures would support the execution of code from the data segment.
We observe .LOC label or the location immediate to the .rodata section has the static string “Hello World!\n” that we had given. Here is another small program created to re-initiate our aim of making the desired code.
int main[] = { 1, 2, 3, 4, 5 };
Again, making an assembly dump shows the following code. A little surprising, we’ve found how our code should be
... .globl main .data .align 4 .type main, @object .size main, 20 main: .long 1 .long 2 .long 3 .long 4 .long 5 ...
Why did we do the above? The main reason is that this is how our final program should be, an int array. Moreover the above has revealed us how the main is organized in case if we declare it as an array now. It is considered as just another object and its declared in the data area. As far now, its fine. The curiosity never stops. The above assembly is compiled and the executable is created. The linker doesnot report an error since for the linker to operate smoothly, it needs some global pointer by the name main. Obviously, the code segfaulted :(
There exists partially/completely a reverse engineering or a decoding/deciphering or whatever we could call, in every hack. Ours is no different. Switching back to the assembly dump, “hello.s”, we compile that to produce the executable. We then use the objdump to extract the symbols and the opcodes defined in the executable. Actually if you have the x86 Mnemonics and Hex mapping for those opcodes, there is no need to do this but in case of unavailability, the below procedure could give you a good insight about the contents of the executable.
The objdump is used this way to dump all symbols of all sections from the ''a.out'' created.
$ objdump -D ./a.out > hello.dump
Here goes the main section for the above assembly code.... 08048384 <main>: 8048384: 55 push %ebp 8048385: 89 e5 mov %esp,%ebp 8048387: 83 ec 08 sub $0x8,%esp 804838a: 83 e4 f0 and $0xfffffff0,%esp 804838d: b8 00 00 00 00 mov $0x0,%eax 8048392: 29 c4 sub %eax,%esp 8048394: c7 04 24 c4 84 04 08 movl $0x80484c4,(%esp) 804839b: e8 10 ff ff ff call 80482b0 <_init+0x38> 80483a0: b8 00 00 00 00 mov $0x0,%eax 80483a5: c9 leave 80483a6: c3 ret 80483a7: 90 nop ...
The numbers in the first column might be a little different, its just the logical address of the each statement. We are mainly interested in the second column of HEX digits. Those HEX digits corresponds to the assembly instructions that could be found straight at the end of the line.
Something rings a bell, can we use these HEX directly in the assembly code “hello.s” and see if it works? Here it goes, our hands on the assembly file. Just the four instructions after the main: section are replaced. To remind, .long takes a 4-byte value, .word a 2-byte and .byte, as the name says.
... .globl main .type main, @function main: /* pushl %ebp */ .byte 0x55 /* mov %esp, %ebp */ .word 0xE589 /* sub $0x8, %esp and $-16, %esp */ .long 0xF008EC83 .word 0xE483 ...
The above HEX codes are pasted from right to left due to endianness. Before that, the curiosity never leaves and the code is compiled and executed. We find... BINGO! BINGO!! the “Hello World!”. This gives a clarity over the code in the a.out and that in the “hello.s”. Though it has to be this way, we never know what linker does exactly right? Now it has become clear that it does nothing more than fixing up the block with the continuous values corresponding to the opcode instruction/parameter data/address.
The curiosity has not stopped, it has only risen more. Now comes an interesting part, revisiting the assembly dump at the call section, we see:
call printf
But we haven’t defined printf in the file. So, its obvious that the linker does the job of linking the opcodes for the printf from the libc. Also the corresponding objdump of the a.out shows:
804839b: e8 10 ff ff ff call 80482b0 <_init+0x38>
OH! The call calls the printf and the address need not be constant, it might vary depending on the position where it loads, which is known to linker alone. Just a greedy wish to encode the above HEX as .long as we did above didnot prove good. It just SEGFAULTED. Here we have few problems to be resolved. Even the objdump of this executable could reveal that the address for call in the original objdump is not the same as with the recently dumped values. Interesting,
What kind of code we need? The code has to print “Hello World!” or any string for that matter, provided we should not frame main as function but as an array.
What is the hurdle in coding that? There are actually few issues to be resolved before achieving the above code. Lets see one by one.
call doesnot work? If it is because of the address assignment by the linker, then, we have no way of calling printf, is that so?
Lets start our enquiry on the original assembly dump, “hello.s”. This is needed since we’ll have to convert every other opcodes to long so that we make main as array. Lets ignore the directives like .file and .indent which are just meta information. We’ll jump directly into the main: block.
Here is the top three lines of the code.
pushl %ebp movl %esp, %ebp subl $24, %esp
What does the above lines do? This requires few fundamental learning of how a program gets executed. We require an initial address where the program intends to store a few pointers/data and also to refer itself and things like that. So, inorder to facilitate that we had two such pointers, %ebp and %esp. The first one is known as the base pointer which acts as a base for the stack that we need and the %esp is the stack pointer which points to the top of the stack.
The first two statements initiates the above two pointers and the third statement allocates 24 Bytes of memory in the stack. We need to subtract since in the x86 architecture the stack grows down. Hence a higher address is subtracted.
The next statement makes the stack pointer to align in the next available 4-byte address for access and alignment reasons.
andl $-16, %esp
Remember that $-16 is converted to unsigned int and hence the value $0xfffffff0 in the objdump. Again, one more thing to be remembered is that writing in the %ebp will store that data as data and anything written to %esp is taken as an address.
The main problem now at hand is storage of data. We cannot just add .long 0xDEADBEEF as some random data in the main: section now. The reason is that the code in that section are executed and if you insert the above long value, it would be taken as a opcode instruction and would be evaluated whose results could not be predicted before hand. Only an objdump could help. Hence we need a way to store the string since we wanted everything to be intact in the main.
movl is an instruction which could be used to write long data into %ebp. Here is code to store the string “Hello World!\n\0”. The “\0” is needed since we are coding the string directly and there is no “C” interface code to detect the string literal to append ‘\0’.
movl $0x6C6C6548, -12(%ebp) movl $0x6F57206F, -8(%ebp) movl $0x21646C72, -4(%ebp) movl $0x0000000A, (%ebp)
The above code has a lot to be explained. The HEX numbers are nothing but the asciified string glued to be a long. The -x(%ebp) is noteworthy. The x is offset from the base pointer. Again here the data is from right to left. If you can figure out, the ‘H’ has 0×48, ‘e’ has 0×65, ‘l’ has 0x6C as ascii values and so on. Since we are accessing the memory as a stack, the initial data has to be stored at the far off address and the last one at the %ebp and since the stack grows down, the offset is subtracted. The ‘.long’ has 4-byte value and hence the offset values are multiples of 4.
Now that we have stored data in the %ebp are we done? Not at all. We’ll have to move the address in the -12(%ebp) into the %esp. Why should this be done? Because the next thing that we’ll do is to call the printf and we’ll have to know what it does before we conclude this. The printf looks for the pointer which defines the format string and that the arguments for the printf function are fetched from the stack, we need the pointer into the stack.
How do we fetch the actual address of the %ebp or any other pointers for instance? The leal comes into help. It is the “Load Effective Address Long” instruction which loads the effective address of the source into the destination. Before storing into the %esp, we have a small problem. From the explanations for %esp we find that the we cannot load the pointer directly into the %esp. It would cause a segfault. If you are curious, try it. So, here comes the help, the register %eax which can be used to hold the address and then it can be pushed into the stack. This should do. The code, here it goes. The pushl pushes the long address into the stack.
leal -12(%ebp), %eax pushl %eax
Now that we are ready to call the printf, lets put our code together and run it.
... main: push %ebp mov %esp, %ebp nop subl $0x10, %esp andl $-16, %esp movl $0x6C6C6548, -12(%ebp) movl $0x6F57206F, -8(%ebp) movl $0x21646C72, -4(%ebp) movl $0x0000000A, (%ebp) leal -12(%ebp), %eax pushl %eax call printf mov $0x0, %eax leave ret ...
BINGO! BINGO!!! It prints “Hello World!”. So, we are on the right track. Now that it only takes time to read back the objdump of the a.out created from this and readback the HEX values and make the long values for the main array. But, wait. We have still not addressed the main part of the code. What are we planning to do for the call stuff? We dont know the address where printf gets loaded. Also, the tricky part is, we cannot include that in an array in C program, isn’t? It is useless to invoke asm(x) call from within an array.
What, what could be better solution for this problem??? There came as a flash, why dont we jump? How does this help? This way, we make a jump in the main and jump to somewhere outside main area where we have access to asm(x). Remember we have no other go other than to include the asm in the code otherwise we have to insert the proper assembly to drive the data into the output section. Im still a naive at assembly and restricting myself to call the pre-defined printf let alone the other methods for future.
So, our action plan is this:
MAIN | JUMP <HELPER> | RET | NOP | HELPER | CALL PRINTF
But experimenting with the above gives another insight, we cannot define helper other than having a function but having defined helper as function, we have yet another drawback. Once a jump is made, since its a function, the assembler creates a new stack for it and hence our stack becomes local to main and the stack for the helper doesnot have the data. OOPS! A bad method definitely. There is just a small tweak to the above method. We know after jump we will only have a finite number of instructions and from the objdump of original code we find we are just 7 bytes away. BINGO! This gives us the solution. Here is what we do:
MAIN | JUMP <7 Bytes> | RET | NOP | 7: CALL PRINTF | RET
Finally here the design lies. We jump after 7 bytes and we call the printf and return to the caller and again we return back. And its done
As a naive, since i didn’t know how to jump 7 bytes from the current point, i had to take a round-about trip. Here is the code with the helper to fetch the proper jump instruction:
... leal -12(%ebp), %eax pushl %eax jmp helper mov $0x0, %eax leave ret nop helper: call printf leave ret ...
The jmp jumps to helper and everything else is as per our design. Here goes the objdump of this.
80483ad: 50 push %eax 80483ae: e9 07 00 00 00 jmp 80483ba <helper> 80483b3: b8 00 00 00 00 mov $0x0,%eax
So, here it goes. The needed opcode in HEX. We are almost done. Now here is the code that is converted into long in assembly.
All the above design to call the printf is not actually required. Looking at the object dump and realizing that the asm call can be made globally in the C code, makes us abandon the above design and define main till the call statement and insert a line of asm block that has the call to printf and returns.
NOTE: If you wanted to do something else, say, store some other data and then wanted to print that too, then you’ll have to fallback to the above design.
The final assembly code, modified from “hello.s” as the base script is rewritten with the above changes. But converted to .long data.
.text .globl main main: .long 0x90E58955 .long 0x8310EC83 .long 0x9090E4F0 .long 0x48F445C7 .long 0xC76C6C65 .long 0x206FF845 .long 0x45C76F57 .long 0x646C72FC .long 0x0045C721 .long 0x0000000A .long 0x50F4458D call printf .long 0x0000B890 .long 0xC3C90000
Now the construction of the C code comes into play. I suppose its lot more easier to do than the above since the whole long values are available. The last two lines are tweaked in the C code with the asm statement as a fix.
Thats the end of one conquest to write the code which doesnot have main as a function but rather as a data.
There is one more observation made on the object dump. We can find that sometimes the above code would segfault. Why is that at times it does. The object dump again reveals the reason why. The following line is observed as a last line in the “main” section of the object dump.
00 00 add %al,(%eax)
What does the above line do? And why should this code be inserted even if there is no such instruction in the assembly file?
A slight deeper observation reveals that the code should be alighned with the 16 byte offset. In case if you had observed the object dump of the traditional “hello world”, you could’ve observed that the main section ends at the address XXXXXXXf. Hence the above observation. To make things work properly, the NOPs should be inserted at the last so that the code is stable.
The above observation is not exactly true. Here goes the section of assembly dump in the system where the code is executed. The GCC footprint is GCC 3.4.2 20041017 (Red Hat 3.4.2-6.fc3).
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)"
The .note.GNU-stack section causes the SEGFAULT i believe. To test that the line is removed and the assembly is compiled, and the code works correctly. Why should the above line cause a SEGFAULT? Is there any way to prevent the above?? Yes, ofcourse, there exists an answer for that. Here it goes.
http://www.redhat.com/archives/fedora-devel-list/2003-November/msg00838.html
A nice mailing list solution providing answer to our questions. And finally, we have the compilation flags for our C-Code.
gcc -Wl,-z,execstack final.c
And BINGO! The code is executed as expected.