Experiment 1: Textless Hello world A while ago I read an article about creating really small ELF executables. The author made me realize how 'liberal' Linux is when it comes to executable format. And so I got to thinking... What if I could create an executable without a text segment at all? I'm sure it would assemble, but it wouldn't actually do anything... So what if the instructions are read from somewhere else? Like that data segment? I got it in my mind somehow to ty and make a C program where main isn't a function. I got to some poking around the limits of this setup using assembler. Since I'm not such an assembler geek, this was fun. Since the symbols are translated by the assembler, and I'll be feeding the C compiler pre-assembled machine code. And since there's no way to tell where any bss or data object, or even main itself, is located, I would not be able to use any symbol in the code. No goto, no offset, no jumps. If there are no segments to store data in, how will I print anything to the screen? write() takes a pointer. I was saved by reminding myself that the stack pointer is, in fact, a pointer just like any other. First, I pushed several 4-byte strings, translated to hex, onto the stack, followed by a 12-byte write of the stack pointer. This worked. Duh. Well, now that it's printing something, without using anything from the data segment, the next step is to move the instructions /to/ the data segment. I located the text segment with a hex editor by finding some known opcodes, and copied it manually. Later, I used gdb's disassembler feature, and after that I was reminded of objdump -d. unsigned char main[] = { 0xBB, 4, 0, 0, 0, 0xB9, 1, 0, 0, 0, ... }; This is what it looked like, more or less. Compiling was next, so I grit my teeth an tried: $ gcc test.c -c No warnings? Nothing? And it spit out an object file too... Wow! $ gcc test.o -o test No warnings again... Well then: $ ./test Hello world! Wow, it worked! Cool! Next I converted the main to unsigned int main[], which bit me in the butt at first because I forgot about endianness for a minute. The result just looked cool. Next I tried a something a little different... Shortening the executable by using Gas' precision, and using pusha to push all string segments at once. This worked. I then quickly wrote a tool in C to convert disassembley data from objdump to an unsigned int main[] = {}. And now I can make similar executables in a matter of seconds. Yay! And take a look. The program as compiled by as: pieter@wings:~/projects/obfu/asm$ size ./notext_s.o text data bss dec hex filename 39 0 0 39 27 ./notext_s.o 39 bytes worth of instructions in the text segment. Pretty good. Here's what gcc spit out when compiling unsigned int main[]: pieter@wings:~/projects/obfu/asm$ size ./notext_c.o text data bss dec hex filename 0 40 0 40 28 ./notext_c.o Ooh, look! No text! And 40 (39 rounded to the next integer boundary) bytes worth of data, and no text! And guess what? It fooled the linker, too! Works like a charm! Note that this program compiles fine any way you put it: $ gcc -W -Wall -O3 -ansi -pedantic notext_c.c -o notext_c notext_c.c:1: warning: `main' is usually a function $ main usually a function... Psh... What do they know! :P