Pages

Sunday, December 5, 2010

"Hello, World!" first shellcode - Part 1

Simple C program to print "Hello, World!". Compiled with gcc version 3.3.6 on Ubuntu 7.04

#include <stdio.h>

char SHELLCODE[]="\xeb\x13\x59\x31\xc0\xb0\x04\x31\xdb\x43\x31\xd2"
                 "\xb2\x0f\xcd\x80\xb0\x01\x4b\xcd\x80\xe8\xe8\xff"
                 "\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72"
                 "\x6c\x64\x21\x0a\x0d";

typedef void (*func_ptr)();

int main() {
    func_ptr f = (func_ptr)SHELLCODE;
    f();
}
To compile the source:
$ gcc my_shellcode.c
$ ./a.out 
Hello, world!
The char array represents position-independent instructions, that, when injected into a process, will be executed. But where did that came from?  Here's an .asm source to generate position-independent code that prints something:
BITS 32             ; tell nasm this is 32-bit code

jmp short one       ; jump down to a call at the end

two:
; ssize_t write(int fd, const void *buf, size_t count);
  pop ecx           ; pop the return address (string ptr) into ecx
  xor eax, eax      ; zero out full 32-bits of eax register
  mov al, 4         ; write syscall #4 to the low byte of eax
  xor ebx, ebx      ; zero out ebx
  inc ebx           ; increment ebx to 1, STDOUT file descriptor
  xor edx, edx
  mov dl, 15        ; length of the string
  int 0x80          ; do syscall: write(1, string, 14)

; void _exit(int status);
  mov al, 1        ; exit syscall #1, the top 3 bytes are still zeroed
  dec ebx          ; decrement ebx back down to 0 for status = 0   
  int 0x80         ; do syscall:  exit(0)

one:
  call two   ; call back upwards to avoid null bytes
  db "Hello, world!", 0x0a, 0x0d  ; with newline and carriage return bytes

Some things to pay attention to:
  • the jmp technique with 2 labels (the call in the second label will jump back, will avoid \x00 bytes)
  • xor eax, eax, to zero a register (xor instruction does not modify the flags registry, so it's better than something like for example sub eax, eax that also clears eax.)
  • inc, dec,  instructions that take less machine bytes than mov, so the size of the shellcode  is reduced
  • system calls number can be found in /usr/include/asm-i386/unistd.h (found out with find command ls /usr/include/ -name "*.h" |xargs grep execve , for example)
The asm source cannot be linked into an elf executable. But it can be compiled into machine instructions:
$ nasm shell.asm
$ hexdump -C shell
$ ndisasm shell
The output shows the actual machine code, interpreted as instructions. 
To be continued..