├── LICENSE ├── Makefile ├── README.md ├── loader_amd64.s ├── loader_i386.s ├── mkshellcode.py ├── shellcode_amd64.c ├── shellcode_i386.c └── test.c /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Felipe Andres Manzano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: test32 test64 shellcode.bin 2 | 3 | %.o : %.c 4 | gcc -m32 -march=i386 -fno-stack-protector -fno-common -Os -fomit-frame-pointer -fno-PIC -c -static $< 5 | 6 | %.bin : %.o 7 | python mkshellcode.py $< $@ 8 | test32: test.c 9 | gcc -m32 test.c -o test32 10 | 11 | test64: test.c 12 | gcc -m64 test.c -o test64 13 | 14 | clean: 15 | rm -f *.o shellcode.bin test32 test64 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mkShellcode 2 | =========== 3 | 4 | This rearranges an ELF object file so it can be used as shellcode. 5 | 6 | http://blog.binamuse.com/2013/01/about-shellcodes-in-c.html 7 | -------------------------------------------------------------------------------- /loader_amd64.s: -------------------------------------------------------------------------------- 1 | .section .text 2 | #esp shall point to writeable memory 3 | jmp labelA 4 | dummy: 5 | jmp begin 6 | labelA: 7 | call dummy 8 | begin: 9 | #read eip into esi 10 | popq %rsi 11 | subq $(begin-relocs), %rsi 12 | #leal (%esi), %esi 13 | 14 | #esi points to relocs 15 | movq (%rsi), %rcx #Number of relocations 16 | leaq 16(%rsi,%rcx,8), %rdi #Start of .text [SIZE|RELOC1|RELOC2|....|RELOCN][START][CODE] 17 | andq %rcx,%rcx 18 | jz done 19 | fix_reloc: 20 | movq (%rsi,%rcx,8), %rax 21 | addq %rdi, (%rdi,%rax,1) 22 | dec %rcx 23 | jne fix_reloc 24 | done: 25 | addq -8(%rdi), %rdi 26 | jmpq %rdi 27 | relocs: 28 | 29 | -------------------------------------------------------------------------------- /loader_i386.s: -------------------------------------------------------------------------------- 1 | .section .text 2 | #esp shall point to writeable memory 3 | jmp labelA 4 | dummy: 5 | jmp begin 6 | labelA: 7 | call dummy 8 | begin: 9 | #read eip into esi 10 | popl %esi 11 | subl $(begin-relocs), %esi 12 | #leal (%esi), %esi 13 | 14 | #esi points to relocs 15 | movl (%esi), %ecx #Number of relocations 16 | leal 8(%esi,%ecx,4), %edi #Start of .text [SIZE|RELOC1|RELOC2|....|RELOCN][START][CODE] 17 | andl %ecx,%ecx 18 | jz done 19 | fix_reloc: 20 | movl (%esi,%ecx,4), %eax 21 | addl %edi, (%edi,%eax,1) 22 | dec %ecx 23 | jne fix_reloc 24 | done: 25 | addl -4(%edi), %edi 26 | jmp *%edi 27 | relocs: 28 | 29 | -------------------------------------------------------------------------------- /mkshellcode.py: -------------------------------------------------------------------------------- 1 | from StringIO import StringIO 2 | from elftools.elf.elffile import ELFFile,RelocationSection,SymbolTableSection 3 | from elftools.elf.enums import ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64 4 | import sys,struct,subprocess 5 | def run(cmd): 6 | return subprocess.check_output(cmd.split(' ')) 7 | 8 | def find_relocations_for_section(elf, section_name): 9 | """ Given a section, find the relocation section for it in the ELF 10 | file. Return a RelocationSection object, or None if none was 11 | found. 12 | """ 13 | rels = elf.get_section_by_name(b'.rel' + section_name) 14 | if rels is None: 15 | rels = elf.get_section_by_name(b'.rela' + section_name) 16 | return rels 17 | 18 | #the user shellcode 19 | filename = sys.argv[1] 20 | 21 | #read the ELF object file 22 | elf = ELFFile(file(filename)) 23 | 24 | #64 bit not implemented! 25 | print('[II] Object %s is a %s_%s elf' % (filename, elf.get_machine_arch(), elf.elfclass)) 26 | assert elf.elfclass == 32 and elf.get_machine_arch() == 'x86' 27 | 28 | #assemble and load loader .text section 29 | run('as -32 loader_x86.s -o loader.o') #32 bit only 30 | loader = ELFFile(file('loader.o')).get_section_by_name('.text').data() 31 | print "[II] %s loader is %d bytes long"%(elf.get_machine_arch(),len(loader)) 32 | 33 | 34 | #list of elf sections 35 | print "[II] Elf has %d sections."% elf.num_sections() 36 | 37 | #Select the interesting sections... 38 | selected_sections = [".text",".data", ".bss"] 39 | for section in elf.iter_sections(): 40 | if section.name.startswith(".rodata"): 41 | selected_sections.append(section.name) 42 | 43 | print "[II] Selected sections are: ", " ".join(selected_sections) 44 | 45 | #Precalculate the offsets and packs the shellcode 46 | #[text][data][rodata1][rodata2][rodata3] 47 | offsets = {} 48 | shellcode = StringIO('') 49 | for section_name in selected_sections: 50 | offsets[section_name] = shellcode.len 51 | try: 52 | data = elf.get_section_by_name(section_name).data() 53 | print "[II] Section %s is %d bytes offset %d"%(section_name,len(data),offsets[section_name]) 54 | except: 55 | data = '' 56 | print '[WW] No %s section'%section_name 57 | shellcode.write(data) 58 | 59 | print "[II] Total packed data size %d" % shellcode.len 60 | 61 | #FIX RELOCS! 62 | relocs = [] 63 | for section_name in selected_sections: 64 | reloc_section = find_relocations_for_section(elf, section_name) 65 | if reloc_section is None: 66 | continue 67 | symtab = elf.get_section(reloc_section['sh_link']) 68 | for reloc in reloc_section.iter_relocations(): 69 | assert elf.get_machine_arch() == 'x86' and not reloc.is_RELA() 70 | reloc_base = offsets[section_name] 71 | reloc_offset = reloc['r_offset'] 72 | reloc_type = reloc['r_info_type'] 73 | target_symbol = symtab.get_symbol(reloc['r_info_sym']) 74 | target_name = elf.get_section(target_symbol['st_shndx']).name 75 | target_base = offsets[target_name] 76 | target_offset = target_symbol['st_value'] 77 | 78 | shellcode.seek(reloc_base+reloc_offset) 79 | value = struct.unpack("", target_name, target_base,target_offset, value 81 | if reloc_type == ENUM_RELOC_TYPE_i386['R_386_32']: 82 | value = target_base + target_offset + value 83 | relocs.append(reloc_base+reloc_offset) 84 | print "[II] Offset ",reloc_base+reloc_offset, "added to reloc list" 85 | elif reloc_type == ENUM_RELOC_TYPE_i386['R_386_PC32']: #relative reference to text 86 | value = (target_base + target_offset) - (reloc_base + reloc_offset) + value 87 | else: 88 | assert reloc_type == ENUM_RELOC_TYPE_i386['R_386_NONE'] 89 | shellcode.seek(reloc_base + reloc_offset) 90 | shellcode.write(struct.pack(" 2 | 3 | 4 | char message[] = "HOLA MUNDO!(shellcode)\n"; 5 | 6 | 7 | /* Linux takes system call arguments in registers: 8 | syscall number %eax call-clobbered 9 | arg 1 %ebx call-saved 10 | arg 2 %ecx call-clobbered 11 | arg 3 %edx call-clobbered 12 | arg 4 %esi call-saved 13 | arg 5 %edi call-saved 14 | arg 6 %ebp call-saved 15 | */ 16 | 17 | #if __x86_64__ 18 | /* 64-bit */ 19 | long syscall(long syscall_number, long arg1, long arg2, long arg3, long arg4, long arg5, long arg6){ 20 | long ret; 21 | asm volatile ( 22 | "movq %1, %%rax\n\t" 23 | "movq %2, %%rdi\n\t" 24 | "movq %3, %%rsi\n\t" 25 | "movq %4, %%rdx\n\t" 26 | "movq %5, %%rcx\n\t" 27 | "movq %6, %%r8\n\t" 28 | "movq %7, %%r9\n\t" 29 | "syscall" 30 | : "=a"(ret) 31 | : "g"(syscall_number), "g"(arg1), "g"(arg2), "g"(arg3), "g"(arg4), "g"(arg5), "g"(arg6) ); 32 | return ret; 33 | } 34 | 35 | 36 | #else 37 | int syscall(int syscall_number, ... ){ 38 | int ret; 39 | asm volatile ( 40 | "movl %1, %%eax\n\t" 41 | "movl %2, %%ebx\n\t" 42 | "movl %3, %%ecx\n\t" 43 | "movl %4, %%edx\n\t" 44 | "movl %5, %%edi\n\t" 45 | "movl %6, %%esi\n\t" 46 | "movl %7, %%ebp\n\t" 47 | "int $0x80" 48 | : "=a"(ret) 49 | : "g"(syscall_number), "g"(*(&syscall_number+1)), "g"(*(&syscall_number+2)), "g"(*(&syscall_number+3)), "g"(*(&syscall_number+4)), "g"(*(&syscall_number+5)), "g"(*(&syscall_number+6)) 50 | : "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp" 51 | ); 52 | return ret; 53 | } 54 | #endif 55 | 56 | int write(long fd, void* buffer, unsigned long size){ 57 | return syscall(1L, fd, buffer, size,0,0,0); 58 | } 59 | int ex1t(int errorlevel){ 60 | return syscall(60, errorlevel,0,0,0,0,0); 61 | } 62 | 63 | 64 | int shellcode(){ 65 | write(1,message,2300); 66 | ex1t(0); 67 | //write(0,"CHAU\n",5); 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /shellcode_i386.c: -------------------------------------------------------------------------------- 1 | char message[] = "HOLA MUNDO!(shellcode)\n"; 2 | 3 | 4 | /* Linux takes system call arguments in registers: 5 | syscall number %eax call-clobbered 6 | arg 1 %ebx call-saved 7 | arg 2 %ecx call-clobbered 8 | arg 3 %edx call-clobbered 9 | arg 4 %esi call-saved 10 | arg 5 %edi call-saved 11 | arg 6 %ebp call-saved 12 | */ 13 | int syscall(int syscall_number, ... ){ 14 | int ret; 15 | asm volatile ( 16 | "movl %1, %%eax\n\t" 17 | "movl %2, %%ebx\n\t" 18 | "movl %3, %%ecx\n\t" 19 | "movl %4, %%edx\n\t" 20 | "movl %5, %%edi\n\t" 21 | "movl %6, %%esi\n\t" 22 | "movl %7, %%ebp\n\t" 23 | "int $0x80" 24 | : "=a"(ret) 25 | : "g"(syscall_number), "g"(*(&syscall_number+1)), "g"(*(&syscall_number+2)), "g"(*(&syscall_number+3)), "g"(*(&syscall_number+4)), "g"(*(&syscall_number+5)), "g"(*(&syscall_number+6)) 26 | : "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp" 27 | ); 28 | return ret; 29 | } 30 | 31 | int write(int fd, void* buffer, unsigned int size){ 32 | return syscall(4, fd, buffer, size, 0,0,0); 33 | } 34 | 35 | 36 | int shellcode(){ 37 | write(1,message,23); 38 | //write(0,"CHAU\n",5); 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* Shellcode testbed 10 | * 11 | * man mmap: 12 | * mmap() creates a new mapping in the virtual address space of the call- 13 | * ing process. The starting address for the new mapping is specified 14 | * in addr. The length argument specifies the length of the mapping. 15 | * 16 | * */ 17 | int 18 | main (int argc, char *argv[]) 19 | { 20 | void *p; 21 | int fd; 22 | off_t size; 23 | 24 | if (argc != 2) 25 | { 26 | printf ("Usage:\n\t%s shellcode.bin\n", argv[0]); 27 | exit (-1); 28 | } 29 | 30 | //Open tha file 31 | fd= open (argv[1], O_RDONLY); 32 | assert ( fd != -1); 33 | //Read the size 34 | size = lseek (fd, 0, SEEK_END); 35 | lseek (fd, 0, SEEK_SET); 36 | assert ( size != 0); 37 | //Allocates a virtual memory map RWX 38 | p = mmap (NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE, 39 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 40 | printf ("Memory@%p\n", p); 41 | 42 | //Reads content of the "binary" file into mem 43 | assert(size == read (fd, p, size)); 44 | 45 | //Close the file so the shellcode inherits only 0,1,2 46 | close (fd); 47 | 48 | //Call the first instruction in mem 49 | printf ("Passing control to the shellcode...\n"); 50 | ((void (*)()) p) (); 51 | printf ("The shellcode has returned to main!\n"); 52 | exit (-1); 53 | } 54 | --------------------------------------------------------------------------------