├── LICENSE ├── README.md ├── args2.c ├── dyn_unmap_run.c ├── elfauxv.c ├── elfauxv_dynamic.c ├── env_test.c ├── example.c ├── hw.c ├── libstatic ├── crt.h ├── errno.c ├── file_size.c ├── libstatic.h ├── linux_close.c ├── linux_exit.c ├── linux_mmap.c ├── linux_mprotect.c ├── linux_munmap.c ├── linux_open.c ├── linux_read.c ├── linux_sbrk.c ├── linux_stat.c ├── linux_write.c ├── makefile ├── memcpy.c ├── print.c ├── string.c ├── strtoul.c └── to_hex.c ├── load_elf.c ├── makefile ├── map_file.c ├── margs.c ├── mycat.c ├── stack_fix.c ├── ulexec.c ├── ulexec.h └── unmap.c /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Bruce Ediger 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # userlandexec 2 | ## userland exec for Linux x86_64 3 | 4 | This code emulates an `exec()` system call. That is, it reads an ELF format file, 5 | and loads it into memory at the correct address. It then starts the newly-loaded 6 | executable to running. 7 | 8 | All this is usually done by the Linux kernel, so some bizarre things go on. 9 | For starters, the userland exec unmaps the currently-executing ELF file, 10 | so as to be able to put the new ELF file's contents in the right place 11 | in memory. 12 | 13 | This code works with 64-bit Linux ELF files, compiled with GCC and linked against glibc. 14 | Other C runtimes (Musl libc, for example) cause errors mysterious to me. 15 | 16 | ### Building 17 | 18 | Run `make` - that should compile `example` and `ulexec.so`. Once you've 19 | done that, you can try it out: 20 | 21 | `./example ./ulexec.so /usr/bin/cat /proc/self/maps` 22 | 23 | ### Fun 24 | 25 | `./example ./ulexec.so `./example ./ulexec.so `./example ./ulexec.so /usr/bin/cat /proc/self/maps` 26 | 27 | Yes! You can have it overlay itself with another copy of itself. 28 | -------------------------------------------------------------------------------- /args2.c: -------------------------------------------------------------------------------- 1 | /* $Id: args2.c,v 1.2 2014/02/12 17:48:03 bediger Exp $ */ 2 | #include 3 | int 4 | main(int ac, char **av) 5 | { 6 | int i; 7 | printf("argc at %p\n", &ac); 8 | printf("argv[0] at %p\n", &av[0]); 9 | printf("argc %d\n", ac); 10 | for (i = 0; i < ac; ++i) 11 | printf("%d: \"%s\"\n", i, av[i]); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /dyn_unmap_run.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | #define JMP_ADDR(x) asm("\tjmp *%0\n" :: "r" (x)) 12 | #define SET_STACK(x) asm("\tmovq %0, %%rsp\n" :: "r"(x)) 13 | 14 | void 15 | print_maps(void) 16 | { 17 | char rbuf[1024]; 18 | int fd, cc; 19 | 20 | fd = linux_open("/proc/self/maps", 0, 0); 21 | while (0 < (cc = linux_read(fd, rbuf, sizeof(rbuf)))) 22 | linux_write(1, rbuf, cc); 23 | linux_close(fd); 24 | } 25 | 26 | void c_main(int ac, char **av, char **env) 27 | { 28 | char *file_to_map = av[3]; 29 | char *file_to_unmap = av[2]; 30 | int how_to_map = 0; 31 | void *mapped; 32 | void *entry_point; 33 | unsigned long dummy; 34 | Elf64_Ehdr *elf_ehdr, *ldso_ehdr; 35 | struct saved_block *argvb, *envb, *elfauxvb; 36 | int trim_args; 37 | void *stack_bottom; 38 | unsigned long empty[32768]; 39 | 40 | empty[0] = 1; 41 | 42 | if (NULL == strstr(av[1], "dyn_unmap_run")) 43 | { 44 | file_to_map = av[1]; 45 | file_to_unmap = NULL; 46 | how_to_map = 1; 47 | trim_args = 1; 48 | } else { 49 | file_to_map = av[2]; 50 | file_to_unmap = av[0]; 51 | how_to_map = 0; 52 | trim_args = 2; 53 | } 54 | 55 | if (file_to_unmap) 56 | unmap(file_to_unmap); 57 | 58 | mapped = map_file(file_to_map, &dummy); 59 | elf_ehdr = (Elf64_Ehdr *)mapped; 60 | 61 | entry_point = load_elf(mapped, how_to_map, &elf_ehdr, &ldso_ehdr); 62 | 63 | linux_munmap(mapped, dummy); 64 | 65 | argvb = save_argv(ac - trim_args, &av[trim_args]); 66 | envb = save_argv(0, env); 67 | elfauxvb = save_elfauxv(env); 68 | 69 | stack_bottom = stack_setup(argvb, envb, elfauxvb, elf_ehdr, ldso_ehdr); 70 | 71 | SET_STACK(stack_bottom); 72 | JMP_ADDR(entry_point); 73 | } 74 | 75 | void 76 | error_msg(char *msg) 77 | { 78 | char buf[32]; 79 | print_string(1, msg); 80 | print_string(1, " "); 81 | to_decimal(errno, buf); 82 | print_string(1, buf); 83 | print_string(1, "\n"); 84 | } 85 | 86 | void 87 | print_address(char *phrase, void *address) 88 | { 89 | char buf[256]; 90 | to_hex((unsigned long)address, buf); 91 | print_string(1, phrase); 92 | print_string(1, " 0x"); 93 | print_string(1, buf); 94 | print_string(1, "\n"); 95 | } 96 | 97 | void 98 | unmap(char *progname) 99 | { 100 | char buf[1024], *p; 101 | char rbuf[2048]; 102 | int cc, fd; 103 | 104 | fd = linux_open("/proc/self/maps", 0, 0); 105 | 106 | p = &buf[0]; 107 | 108 | while (0 < (cc = linux_read(fd, rbuf, sizeof(rbuf)))) 109 | { 110 | int i; 111 | 112 | for (i = 0; i < cc; ++i) 113 | { 114 | int c = rbuf[i]; 115 | 116 | if ('\n' != c) 117 | *p++ = c; 118 | else { 119 | *p = '\0'; 120 | /* When a line from /proc/self/maps shows up as having been 121 | * mapped in from this running program, ld.so or libc, unmap it. 122 | * This will keep the exec'd program's address space a lot 123 | * cleaner. But even a 32-bit address space can hold 2 copies 124 | * of glibc without ill effects, so you don't really have to 125 | * munmap() anything other than the program calling ul_exec() */ 126 | if (strstr(buf, progname) || strstr(buf, "libdl") || strstr(buf, "/usr/lib/ld-") 127 | || strstr(buf, "/lib64/ld-") || strstr(buf, "libc")) 128 | { 129 | char *u; 130 | char *first, *second; 131 | unsigned long low, high; 132 | 133 | u = strchr(buf, ' '); 134 | *u = '\0'; 135 | 136 | first = buf; 137 | 138 | second = strchr(first, '-'); 139 | *second = '\0'; 140 | ++second; 141 | 142 | low = strtoul(first, NULL, 0x10); 143 | high = strtoul(second, NULL, 0x10); 144 | 145 | linux_munmap((void *)low, high-low); 146 | } 147 | 148 | p = &buf[0]; 149 | } 150 | } 151 | } 152 | 153 | linux_close(fd); 154 | } 155 | 156 | /* 157 | void * 158 | memcpy(void *dest, const void *src, unsigned long n) 159 | { 160 | unsigned long i; 161 | unsigned char *d = (unsigned char *)dest; 162 | unsigned char *s = (unsigned char *)src; 163 | 164 | for (i = 0; i < n; ++i) 165 | d[i] = s[i]; 166 | 167 | return dest; 168 | } 169 | */ 170 | -------------------------------------------------------------------------------- /elfauxv.c: -------------------------------------------------------------------------------- 1 | /* $Id: elfauxv.c,v 1.2 2014/02/12 17:48:03 bediger Exp $ */ 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | typedef unsigned long uint64_t; 8 | #define BUFSIZ 1024 9 | char *printable_aux_type(uint64_t a_val); 10 | 11 | void 12 | print_data( 13 | int i, void *auxvp, 14 | int a_type, unsigned long a_val 15 | ) 16 | { 17 | print_long(1, (unsigned long)i); 18 | print_string(1, " "); 19 | print_hex(1, (unsigned long)auxvp); 20 | print_string(1, " a_type "); 21 | print_long(1, (unsigned long)a_type); 22 | print_string(1, ", "); 23 | print_string(1, printable_aux_type(a_type)); 24 | print_string(1, ": a_un.a_val "); 25 | print_hex(1, a_val); 26 | print_string(1, "\n"); 27 | 28 | /* 29 | printf("%d %p a_type %lu, %s: a_un.a_val 0x%lx\n", 30 | i, auxvp, 31 | auxvp->a_type, printable_aux_type(auxvp->a_type), 32 | auxvp->a_un.a_val); 33 | */ 34 | } 35 | 36 | char * 37 | printable_aux_type(uint64_t a_val) 38 | { 39 | char *r = "Unknown"; 40 | switch (a_val) 41 | { 42 | case AT_NULL: r = "End of vector"; break; 43 | case AT_IGNORE: r = "Entry should be ignored"; break; 44 | case AT_EXECFD: r = "File descriptor of program"; break; 45 | case AT_PHDR: r = "Program headers for program"; break; 46 | case AT_PHENT: r = "Size of program header entry"; break; 47 | case AT_PHNUM: r = "Number of program headers"; break; 48 | case AT_PAGESZ: r = "System page size"; break; 49 | case AT_BASE: r = "Base address of interpreter"; break; 50 | case AT_FLAGS: r = "Flags"; break; 51 | case AT_ENTRY: r = "Entry point of program"; break; 52 | case AT_NOTELF: r = "Program is not ELF"; break; 53 | case AT_UID: r = "Real uid"; break; 54 | case AT_EUID: r = "Effective uid"; break; 55 | case AT_GID: r = "Real gid"; break; 56 | case AT_EGID: r = "Effective gid"; break; 57 | case AT_CLKTCK: r = "Frequency of times()"; break; 58 | case AT_PLATFORM: r = "String identifying platform. "; break; 59 | case AT_HWCAP: r = "Machine dependent hints about processor capabilities. "; break; 60 | case AT_FPUCW: r = "Used FPU control word. "; break; 61 | case AT_DCACHEBSIZE: r = "Data cache block size. "; break; 62 | case AT_ICACHEBSIZE: r = "Instruction cache block size. "; break; 63 | case AT_UCACHEBSIZE: r = "Unified cache block size. "; break; 64 | case AT_IGNOREPPC: r = "Entry should be ignored. "; break; 65 | case AT_SECURE: r = "Boolean, was exec setuid-like? "; break; 66 | /* The following ifdefs exist because apparently glibc starting 67 | * defining new auxillary types */ 68 | #ifdef AT_BASE_PLATFORM 69 | case AT_BASE_PLATFORM: r= "String identifying real platforms."; break; 70 | #endif 71 | #ifdef AT_RANDOM 72 | case AT_RANDOM: r = "Address of 16 random bytes. "; break; 73 | #endif 74 | #ifdef AT_EXECFN 75 | case AT_EXECFN: r = "Filename of executable. "; break; 76 | #endif 77 | case AT_SYSINFO: r = "Address of VDSO"; break; 78 | case AT_SYSINFO_EHDR: r = "AT_SYSINFO_EHDR"; break; 79 | #ifdef AT_L1I_CACHESHAPE 80 | case AT_L1I_CACHESHAPE: r = "AT_L1I_CACHESHAPE"; break; 81 | case AT_L1D_CACHESHAPE: r = "AT_L1D_CACHESHAPE"; break; 82 | case AT_L2_CACHESHAPE: r = "AT_L2_CACHESHAPE"; break; 83 | case AT_L3_CACHESHAPE: r = "AT_L3_CACHESHAPE"; break; 84 | #endif 85 | } 86 | return r; 87 | } 88 | 89 | void 90 | print_maps(void) 91 | { 92 | char rbuf[BUFSIZ]; 93 | int fd, cc; 94 | 95 | fd = linux_open("/proc/self/maps", 0, 0); 96 | while (0 < (cc = linux_read(fd, rbuf, sizeof(rbuf)))) 97 | linux_write(1, rbuf, cc); 98 | linux_close(fd); 99 | } 100 | 101 | void 102 | c_main(int ac, char **av, char **envp) 103 | { 104 | int i; 105 | unsigned long *p; 106 | Elf64_auxv_t *auxvp; 107 | 108 | print_maps(); 109 | 110 | print_string(1, "ac "); 111 | print_long(1, ac); 112 | print_string(1, "\n"); 113 | 114 | print_string(1, "av[0] holds "); 115 | print_hex(1, (unsigned long)av[0]); 116 | print_string(1, "\n"); 117 | 118 | print_string(1, "envp[0] holds "); 119 | print_hex(1, (unsigned long)envp[0]); 120 | print_string(1, "\n"); 121 | 122 | p = (unsigned long *)&envp[0]; 123 | 124 | while (*p != 0) 125 | ++p; 126 | 127 | print_string(1, "First NULL 8-byte word at "); 128 | print_hex(1, (unsigned long)p); 129 | print_string(1, "\n"); 130 | 131 | 132 | for (i = 0, 133 | auxvp = (Elf64_auxv_t *)(p + 1); 134 | auxvp->a_type != AT_NULL; 135 | ++auxvp, ++i 136 | ) { 137 | char *str; 138 | 139 | print_data(i, auxvp, auxvp->a_type, auxvp->a_un.a_val); 140 | 141 | switch (auxvp->a_type) 142 | { 143 | case AT_PLATFORM: 144 | #ifdef AT_EXECFN 145 | case AT_EXECFN: 146 | #endif 147 | str = (char *)auxvp->a_un.a_val; 148 | print_string(1, "\t\""); 149 | print_string(1, str); 150 | print_string(1, "\"\n"); 151 | break; 152 | } 153 | } 154 | 155 | linux_exit(0); 156 | } 157 | -------------------------------------------------------------------------------- /elfauxv_dynamic.c: -------------------------------------------------------------------------------- 1 | /* $Id: elfauxv_dynamic.c,v 1.2 2014/02/12 17:48:03 bediger Exp $ */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | char * 8 | printable_aux_type(uint64_t a_val) 9 | { 10 | char *r = "Unknown"; 11 | switch (a_val) 12 | { 13 | case AT_NULL: r = "End of vector"; break; 14 | case AT_IGNORE: r = "Entry should be ignored"; break; 15 | case AT_EXECFD: r = "File descriptor of program"; break; 16 | case AT_PHDR: r = "Program headers for program"; break; 17 | case AT_PHENT: r = "Size of program header entry"; break; 18 | case AT_PHNUM: r = "Number of program headers"; break; 19 | case AT_PAGESZ: r = "System page size"; break; 20 | case AT_BASE: r = "Base address of interpreter"; break; 21 | case AT_FLAGS: r = "Flags"; break; 22 | case AT_ENTRY: r = "Entry point of program"; break; 23 | case AT_NOTELF: r = "Program is not ELF"; break; 24 | case AT_UID: r = "Real uid"; break; 25 | case AT_EUID: r = "Effective uid"; break; 26 | case AT_GID: r = "Real gid"; break; 27 | case AT_EGID: r = "Effective gid"; break; 28 | case AT_CLKTCK: r = "Frequency of times()"; break; 29 | case AT_PLATFORM: r = "String identifying platform. "; break; 30 | case AT_HWCAP: r = "Machine dependent hints about processor capabilities. "; break; 31 | case AT_FPUCW: r = "Used FPU control word. "; break; 32 | case AT_DCACHEBSIZE: r = "Data cache block size. "; break; 33 | case AT_ICACHEBSIZE: r = "Instruction cache block size. "; break; 34 | case AT_UCACHEBSIZE: r = "Unified cache block size. "; break; 35 | case AT_IGNOREPPC: r = "Entry should be ignored. "; break; 36 | case AT_SECURE: r = "Boolean, was exec setuid-like? "; break; 37 | /* The following ifdefs exist because apparently glibc starting 38 | * defining new auxillary types */ 39 | #ifdef AT_BASE_PLATFORM 40 | case AT_BASE_PLATFORM: r= "String identifying real platforms."; break; 41 | #endif 42 | #ifdef AT_RANDOM 43 | case AT_RANDOM: r = "Address of 16 random bytes. "; break; 44 | #endif 45 | #ifdef AT_EXECFN 46 | case AT_EXECFN: r = "Filename of executable. "; break; 47 | #endif 48 | case AT_SYSINFO: r = "Address of VDSO"; break; 49 | case AT_SYSINFO_EHDR: r = "AT_SYSINFO_EHDR"; break; 50 | #ifdef AT_L1I_CACHESHAPE 51 | case AT_L1I_CACHESHAPE: r = "AT_L1I_CACHESHAPE"; break; 52 | case AT_L1D_CACHESHAPE: r = "AT_L1D_CACHESHAPE"; break; 53 | case AT_L2_CACHESHAPE: r = "AT_L2_CACHESHAPE"; break; 54 | case AT_L3_CACHESHAPE: r = "AT_L3_CACHESHAPE"; break; 55 | #endif 56 | } 57 | return r; 58 | } 59 | 60 | void 61 | print_maps(void) 62 | { 63 | char fname[BUFSIZ]; 64 | char buf[BUFSIZ]; 65 | int cc; 66 | FILE *fin; 67 | 68 | snprintf(fname, sizeof(fname), "/proc/%d/maps", getpid()); 69 | fin = fopen(fname, "r"); 70 | 71 | while (0 < (cc = fread(buf, 1, sizeof(buf), fin))) 72 | fwrite(buf, 1, cc, stdout); 73 | 74 | fclose(fin); 75 | } 76 | 77 | int 78 | main(int ac, char **av, char **envp) 79 | { 80 | int i; 81 | unsigned long *p; 82 | Elf64_auxv_t *auxvp; 83 | 84 | print_maps(); 85 | 86 | printf("ac %d\n", ac); 87 | printf("av[0] holds %p\n", av[0]); 88 | printf("envp[0] holds %p\n", envp[0]); 89 | 90 | p = (unsigned long *)&envp[0]; 91 | 92 | while (*p != 0) 93 | ++p; 94 | 95 | printf("First NULL 8-byte word at %p\n", (void *) p); 96 | 97 | 98 | for (i = 0, 99 | auxvp = (Elf64_auxv_t *)(p + 1); 100 | auxvp->a_type != AT_NULL; 101 | ++auxvp, ++i 102 | ) { 103 | char *str; 104 | 105 | printf("%d %p a_type %lu, %s: a_un.a_val 0x%lx\n", i, auxvp, 106 | auxvp->a_type, printable_aux_type(auxvp->a_type), 107 | auxvp->a_un.a_val); 108 | 109 | switch (auxvp->a_type) 110 | { 111 | case AT_PLATFORM: 112 | #ifdef AT_EXECFN 113 | case AT_EXECFN: 114 | #endif 115 | str = (char *)auxvp->a_un.a_val; 116 | printf("\t\"%s\"\n", str); 117 | break; 118 | } 119 | } 120 | 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /env_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int ac, char **av, char **env); 4 | 5 | /* 6 | * This is the real entry point. It passes the initial stack into 7 | * the C entry point. 8 | * 9 | * %rdi - holds value of argc 10 | * %rsi - holds value of argv, a pointer 11 | * %rdx - holds value of env, a pointer 12 | * 13 | * env = argv + 8*(argc+1) 14 | */ 15 | asm ( 16 | ".text\n" 17 | ".global _start\n" 18 | ".type _start,@function\n" 19 | "_start:\n\t" 20 | "movq (%rsp),%rdi\n\t" 21 | "movq %rsp,%rsi\n\t" 22 | "addq $8,%rsi\n\t" 23 | "movq %rdi, %rax\n\t" 24 | "addq $1,%rax\n\t" 25 | "movq $8,%rdx\n\n" 26 | "mul %rdx\n\t" 27 | "addq %rsi,%rax\n\t" 28 | "movq %rax, %rdx\n\t" 29 | "jmp c_main" 30 | ); 31 | 32 | void 33 | c_main(int ac, char **av, char **env) 34 | { 35 | int r = main(ac, av, env); 36 | linux_exit(r); 37 | } 38 | 39 | int 40 | main(int ac, char **av, char **env) 41 | { 42 | char buffer[32]; 43 | int i; 44 | /* 45 | if (env == NULL) 46 | linux_exit(1); 47 | */ 48 | 49 | print_string(1, "argc holds "); 50 | print_long(1, ac); 51 | print_string(1, "\n"); 52 | 53 | print_string(1, "argv holds "); 54 | to_hex((unsigned long)av, buffer); 55 | print_string(1, buffer); 56 | print_string(1, "\n"); 57 | 58 | print_string(1, "env holds "); 59 | to_hex((unsigned long)env, buffer); 60 | print_string(1, buffer); 61 | print_string(1, "\n"); 62 | 63 | print_string(1, "\nEnvironment:\n\t"); 64 | for (i = 0; env[i]; ++i) 65 | { 66 | print_string(1, env[i]); 67 | print_string(1, "\n\t"); 68 | } 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | /* $Id: example.c,v 1.1 2014/02/13 02:58:26 bediger Exp $ */ 2 | #include /* BUFSIZ, snprintf(), fopen(), fread(), etc */ 3 | #include 4 | #include 5 | #include 6 | #include /* strtoul() */ 7 | 8 | int 9 | main(int ac, char **av, char **env) 10 | { 11 | void (*ul_exec)(int, char **, char **); 12 | void *libhdl; 13 | char *execd; 14 | 15 | execd = av[1]; 16 | 17 | /* The flags passed to dlopen() actually make a difference. 18 | * RTLD_NOW - "all undefined symbols in the library are resolved before 19 | * dlopen() returns". 20 | * RTLD_DEEPBIND - "a self-contained library will use its own symbols in 21 | * preference to global symbols with the same name". 22 | * 23 | * Since ul_exec() does an munmap() on libc.so and libdl.so, we don't 24 | * want the ul_exec() code to reference anything in either library. 25 | */ 26 | 27 | libhdl = dlopen(execd, RTLD_NOW|RTLD_DEEPBIND); 28 | if (!libhdl) 29 | { 30 | fprintf(stderr, "Problem with loading SO: %s\n", 31 | dlerror()); 32 | exit(1); 33 | } 34 | 35 | ul_exec = (void (*)(int, char **, char **))dlsym(libhdl, "ulexec"); 36 | 37 | if (!ul_exec) 38 | fprintf(stderr, "Problem loading symbol: %s\n", dlerror()); 39 | else 40 | ul_exec(ac, av, env); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /hw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void c_main(int ac, char **av, char **env) 5 | { 6 | print_string(1, "Hello, world!\n"); 7 | linux_exit(3); 8 | } 9 | -------------------------------------------------------------------------------- /libstatic/crt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is the real entry point. It passes the initial stack into 3 | * the C entry point. 4 | * 5 | * %rdi - holds value of argc 6 | * %rsi - holds value of argv, a pointer 7 | * %rdx - holds value of env, a pointer 8 | * 9 | * env = argv + 8*(argc+1) 10 | */ 11 | asm ( 12 | ".text\n" 13 | ".global _start\n" 14 | ".type _start,@function\n" 15 | "_start:\n\t" 16 | "movq (%rsp),%rdi\n\t" 17 | "movq %rsp,%rsi\n\t" 18 | "addq $8,%rsi\n\t" 19 | "movq %rdi, %rax\n\t" 20 | "addq $1,%rax\n\t" 21 | "movq $8,%rdx\n\n" 22 | "mul %rdx\n\t" 23 | "addq %rsi,%rax\n\t" 24 | "movq %rax, %rdx\n\t" 25 | "jmp c_main" 26 | ); 27 | -------------------------------------------------------------------------------- /libstatic/errno.c: -------------------------------------------------------------------------------- 1 | long errno = 0; 2 | -------------------------------------------------------------------------------- /libstatic/file_size.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | unsigned long 4 | file_size(char *filename) 5 | { 6 | char sbuf[144]; 7 | unsigned long ret; 8 | 9 | if (0 > (long)(ret = linux_stat(filename, (void *)&sbuf))) 10 | { 11 | print_string(2, "stat problem: "); 12 | print_long(2, errno); 13 | print_string(2, "\n"); 14 | } else { 15 | ret = *(unsigned long *)(sbuf+48); 16 | } 17 | 18 | return ret; 19 | } 20 | -------------------------------------------------------------------------------- /libstatic/libstatic.h: -------------------------------------------------------------------------------- 1 | extern long errno; 2 | 3 | #define NULL 0 4 | 5 | int linux_write(int fd, const void *data, unsigned long len); 6 | int linux_read(int fd, char *buffer, unsigned long bufferlen); 7 | int linux_open(const char *pathname, unsigned long flags, unsigned long mode); 8 | int linux_close(int fd); 9 | int linux_stat(const char *filename, void *buf); 10 | void linux_exit(int code); 11 | int print_long(int fd, unsigned long i); 12 | int print_hex(int fd, unsigned long i); 13 | int print_string(int fd, char *s); 14 | int to_decimal(unsigned long x, char *p); 15 | int to_hex(unsigned long x, char *p); 16 | void *linux_mmap(void *start, unsigned long length, int prot, int flags, int fd, unsigned long offset); 17 | unsigned long int strtoul(const char *nptr, char **endptr, int base); 18 | int linux_munmap(void *start, unsigned long length); 19 | int linux_mprotect(void *addr, unsigned long len, int prot); 20 | unsigned long file_size(char *filename); 21 | int linux_getpagesize(void); 22 | void linux_brk(unsigned long addr); 23 | 24 | 25 | unsigned long strlen(const char *s); 26 | char *strchr(const char *s, int c); 27 | char *strrchr(const char *s, int c); 28 | char *strstr(const char *str, char *substr); 29 | void *memcpy(void *dest, const void *src, unsigned long n); 30 | char *strcat(char *dest, const char *src); 31 | 32 | #define PGSZ 0x1000 33 | -------------------------------------------------------------------------------- /libstatic/linux_close.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | linux_close(int fd) 6 | { 7 | 8 | long ret; 9 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_close), 10 | "D" (fd): 11 | "cc", "memory", "rcx", 12 | "r8", "r9", "r10", "r11" ); 13 | if (ret < 0) 14 | { 15 | errno = -ret; 16 | ret = -1; 17 | } 18 | return (int)ret; 19 | } 20 | -------------------------------------------------------------------------------- /libstatic/linux_exit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void linux_exit(int code) 5 | { 6 | asm volatile ("syscall" : : "a" (__NR_exit), "D" (code)); 7 | } 8 | -------------------------------------------------------------------------------- /libstatic/linux_mmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | void * 3 | linux_mmap( 4 | void *start, 5 | unsigned long length, 6 | int prot, 7 | int flags, 8 | int fd, 9 | unsigned long offset 10 | ) 11 | */ 12 | asm ( 13 | ".text\n" 14 | ".global linux_mmap\n" 15 | ".type linux_mmap,@function\n" 16 | "linux_mmap:\n\t" 17 | "mov %rcx,%r10\n\t" 18 | "mov $0x9,%eax\n\t" 19 | "syscall \n\t" 20 | "cmp $0xfffffffffffff001,%rax\n\t" 21 | "jae .Lx1\n\t" 22 | ".Lx2:\n\t" 23 | "retq \n\t" 24 | ".Lx1:\n\t" 25 | "mov 2623486(%rip),%rcx\n\t" 26 | "xor %edx,%edx\n\t" 27 | "sub %rax,%rdx\n\t" 28 | "mov %edx,%fs:(%rcx)\n\t" 29 | "or $0xffffffffffffffff,%rax\n\t" 30 | "jmp .Lx2\n" 31 | ); 32 | -------------------------------------------------------------------------------- /libstatic/linux_mprotect.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | linux_mprotect(void *addr, unsigned long len, int prot) 6 | { 7 | 8 | long ret; 9 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_mprotect), 10 | "D" (addr), "S" (len), "d" (prot) : 11 | "cc", "memory", "rcx", 12 | "r8", "r9", "r10", "r11" ); 13 | if (ret < 0) 14 | { 15 | errno = -ret; 16 | ret = -1; 17 | } 18 | return (int) ret; 19 | } 20 | -------------------------------------------------------------------------------- /libstatic/linux_munmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int 4 | linux_munmap(void *start, unsigned long length) 5 | { 6 | 7 | long ret; 8 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_munmap), 9 | "D" (start), "S" (length) : 10 | "cc", "memory", "rcx", 11 | "r8", "r9", "r10", "r11" ); 12 | if (ret < 0) 13 | { 14 | errno = -ret; 15 | ret = -1; 16 | } 17 | return (int)ret; 18 | } 19 | -------------------------------------------------------------------------------- /libstatic/linux_open.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | linux_open(const char *pathname, unsigned long flags, unsigned long mode) 6 | { 7 | 8 | long ret; 9 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_open), 10 | "D" (pathname), "S" (flags), "d" (mode) : 11 | "cc", "memory", "rcx", 12 | "r8", "r9", "r10", "r11" ); 13 | if (ret < 0) 14 | { 15 | errno = -ret; 16 | ret = -1; 17 | } 18 | return (int) ret; 19 | } 20 | -------------------------------------------------------------------------------- /libstatic/linux_read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int 4 | linux_read(int fd, char *buffer, unsigned long bufferlen) 5 | { 6 | 7 | long ret; 8 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_read), 9 | "D" (fd), "S" (buffer), "d" (bufferlen) : 10 | "cc", "memory", "rcx", 11 | "r8", "r9", "r10", "r11" ); 12 | if (ret < 0) 13 | { 14 | errno = -ret; 15 | ret = -1; 16 | } 17 | return (int)ret; 18 | } 19 | -------------------------------------------------------------------------------- /libstatic/linux_sbrk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void linux_brk(unsigned long addr) 5 | { 6 | asm volatile ("syscall" : : "a" (__NR_brk), "D" (addr)); 7 | } 8 | -------------------------------------------------------------------------------- /libstatic/linux_stat.c: -------------------------------------------------------------------------------- 1 | #include 2 | // int stat(const char *path, struct stat *buf); 3 | 4 | int 5 | linux_stat(const char *path, void *buf) 6 | { 7 | long ret; 8 | asm volatile ("syscall" : 9 | "=a" (ret) : 10 | "a" (4), "D" (path), "S" (buf) : 11 | "memory" 12 | ); 13 | if (ret < 0) 14 | { 15 | errno = -ret; 16 | ret = -1; 17 | } 18 | return (int)ret; 19 | } 20 | -------------------------------------------------------------------------------- /libstatic/linux_write.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | linux_write(int fd, const void *data, unsigned long len) 6 | { 7 | 8 | long ret; 9 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_write), 10 | "D" (fd), "S" (data), "d" (len) : 11 | "cc", "memory", "rcx", 12 | "r8", "r9", "r10", "r11" ); 13 | if (ret < 0) 14 | { 15 | errno = -ret; 16 | ret = -1; 17 | } 18 | return (int)ret; 19 | } 20 | -------------------------------------------------------------------------------- /libstatic/makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -fPIC -nostdlib -I. 3 | 4 | OBJS = linux_close.o linux_exit.o linux_open.o linux_read.o linux_write.o \ 5 | linux_mmap.o linux_munmap.o linux_stat.o memcpy.o \ 6 | print.o errno.o strtoul.o file_size.o string.o linux_mprotect.o \ 7 | linux_sbrk.o 8 | 9 | all: libstatic.a 10 | 11 | libstatic.a: $(OBJS) 12 | ar rcs libstatic.a $(OBJS) 13 | 14 | linux_close.o: linux_close.c libstatic.h 15 | linux_exit.o: linux_exit.c libstatic.h 16 | linux_open.o: linux_open.c libstatic.h 17 | linux_read.o: linux_read.c libstatic.h 18 | linux_write.o: linux_write.c libstatic.h 19 | linux_stat.o: linux_stat.c libstatic.h 20 | linux_sbrk.o: linux_sbrk.c libstatic.h 21 | linux_mmap.o: linux_mmap.c 22 | linux_mprotect.o: linux_mprotect.c libstatic.h 23 | linux_munmap.o: linux_munmap.c libstatic.h 24 | print.o: print.c libstatic.h 25 | errno.o: errno.c libstatic.h 26 | strtoul.o: strtoul.c libstatic.h 27 | file_size.o: file_size.c libstatic.h 28 | string.o: string.c libstatic.h 29 | memcpy.o: memcpy.c libstatic.h 30 | 31 | clean: 32 | -rm -rf *.o *.a *core 33 | -------------------------------------------------------------------------------- /libstatic/memcpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | void * 3 | memcpy(void *dest, const void *src, unsigned long n) 4 | { 5 | unsigned long i; 6 | unsigned char *d = (unsigned char *)dest; 7 | unsigned char *s = (unsigned char *)src; 8 | 9 | for (i = 0; i < n; ++i) 10 | d[i] = s[i]; 11 | 12 | return dest; 13 | } 14 | -------------------------------------------------------------------------------- /libstatic/print.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | print_long(int fd, unsigned long i) 5 | { 6 | char i_buff[32]; 7 | int l, r; 8 | to_decimal(i, i_buff); 9 | for (l = 0; i_buff[l]; ++l); 10 | r = linux_write(fd, i_buff, l); 11 | return r; 12 | } 13 | 14 | int 15 | print_hex(int fd, unsigned long i) 16 | { 17 | char i_buff[64]; 18 | int l, r; 19 | to_hex(i, i_buff); 20 | for (l = 0; i_buff[l]; ++l); 21 | r = linux_write(fd, i_buff, l); 22 | return r; 23 | } 24 | 25 | int 26 | print_string(int fd, char *s) 27 | { 28 | int i, r; 29 | for (i = 0; s[i]; ++i); 30 | r = linux_write(fd, s, i); 31 | return r; 32 | } 33 | 34 | int 35 | to_decimal(unsigned long x, char *p) 36 | { 37 | int count = 0; 38 | 39 | if (x == 0) 40 | *p++ ='0'; 41 | else { 42 | unsigned long q, r, b; 43 | int f = 0; 44 | b = 10000000000000000000U; 45 | 46 | do { 47 | q = x/b; 48 | if (q || f) 49 | { 50 | *p++ = ('0' + q); 51 | ++count; 52 | f = 1; 53 | x = x%b; 54 | } 55 | b /= 10; 56 | } while (b > 0); 57 | 58 | } 59 | 60 | *p = '\0'; 61 | 62 | return count; 63 | } 64 | 65 | int 66 | to_hex(unsigned long n, char *p) 67 | { 68 | int i; 69 | int count = 0; 70 | for (i = 0; i < 16; ++i) 71 | { 72 | char x = ((n >> 60) & 0xf); 73 | if (x < (char)10) 74 | *p++ = x + '0'; 75 | else 76 | *p++ = (x - 10) + 'a'; 77 | ++count; 78 | n <<= 4; 79 | } 80 | *p = '\0'; 81 | return count; 82 | } 83 | -------------------------------------------------------------------------------- /libstatic/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | unsigned long 4 | strlen(const char *s) 5 | { 6 | unsigned long r = 0; 7 | for (; s && *s; ++s, ++r); 8 | return r; 9 | } 10 | 11 | char *strchr(const char *s, int c) 12 | { 13 | char *r = NULL; 14 | 15 | for (; s && *s; ++s) 16 | { 17 | if (*s == c) 18 | { 19 | r = (char *)s; 20 | break; 21 | } 22 | } 23 | return r; 24 | } 25 | 26 | char *strrchr(const char *s, int c) 27 | { 28 | char *r = NULL; 29 | char *p; 30 | 31 | p = &s[strlen(s)-1]; 32 | 33 | for (; p != s; --p) 34 | { 35 | if (*p == c) 36 | { 37 | r = (char *)p; 38 | break; 39 | } 40 | } 41 | return r; 42 | } 43 | 44 | char * 45 | strcat(char *dest, const char *src) 46 | { 47 | if (dest && src) 48 | { 49 | char *p = dest; 50 | while (*p) 51 | ++p; 52 | 53 | for (; *src; ++p, ++src) 54 | *p = *src; 55 | } 56 | 57 | return dest; 58 | } 59 | char *strstr(const char *str, char *substr) 60 | { 61 | char *r = NULL; 62 | int substrl = strlen(substr); 63 | int strl = strlen(str); 64 | 65 | if (substrl < strl) 66 | { 67 | int i; 68 | 69 | for (i = 0; i <= strl - substrl; ++i) 70 | { 71 | char *p = (char *)&str[i]; 72 | int j; 73 | 74 | for (j = 0; j < substrl; ++j) 75 | { 76 | if (p[j] != substr[j]) 77 | break; 78 | } 79 | 80 | if (j == substrl) 81 | { 82 | r = p; 83 | break; 84 | } 85 | } 86 | } else if (substrl == strl) { 87 | int i; 88 | char *p = (char *)&str[0]; 89 | for (i = 0; i < substrl; ++i) 90 | { 91 | if (p[i] != substr[i]) 92 | break; 93 | } 94 | if (i == substrl) 95 | r = p; 96 | } 97 | 98 | return r; 99 | } 100 | -------------------------------------------------------------------------------- /libstatic/strtoul.c: -------------------------------------------------------------------------------- 1 | #include 2 | unsigned long int 3 | strtoul(const char *nptr, char **endptr, int base) 4 | { 5 | unsigned long ret = 0; 6 | int i; 7 | 8 | for (i = 0; nptr[i]; ++i) 9 | { 10 | char digit = nptr[i]; 11 | unsigned int value; 12 | if (digit <= '9') 13 | { 14 | value = '0'; 15 | } else if (digit <= 'Z') { 16 | value = 'A' - 10; 17 | } else if (digit <= 'z') { 18 | value = 'a' - 10; 19 | } 20 | ret *= base; 21 | ret += (digit - value); 22 | if (endptr) *endptr = &(nptr[i]); 23 | } 24 | 25 | return ret; 26 | } 27 | -------------------------------------------------------------------------------- /libstatic/to_hex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void 5 | to_hex(char *p, unsigned long n) 6 | { 7 | int i; 8 | for (i = 0; i < 16; ++i) 9 | { 10 | char x = ((n >> 60) & 0xf); 11 | if (x < (char)10) 12 | *p++ = x + '0'; 13 | else 14 | *p++ = (x - 10) + 'a'; 15 | n <<= 4; 16 | } 17 | *p = '\0'; 18 | } 19 | 20 | int 21 | main(int ac, char **av) 22 | { 23 | char buffer[256]; 24 | unsigned long x = strtoul(av[1], NULL, 0x10); 25 | 26 | to_hex(buffer, x); 27 | 28 | printf("0x%lx converts to: \"%s\"\n", x, buffer); 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /load_elf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) 8 | #define ALIGN(k, v) (((k)+((v)-1))&(~((v)-1))) 9 | #define ALIGNDOWN(k, v) ((unsigned long)(k)&(~((unsigned long)(v)-1))) 10 | 11 | void *memcopy(void *dest, const void *src, unsigned long n); 12 | 13 | void * 14 | load_elf(char *mapped, int anywhere, Elf64_Ehdr **elf_ehdr, Elf64_Ehdr **ldso_ehdr) 15 | { 16 | Elf64_Ehdr *hdr; 17 | Elf64_Phdr *pdr, *interp = NULL; 18 | int i; 19 | void *text_segment = NULL; 20 | void *entry_point = NULL; 21 | unsigned long initial_vaddr = 0; 22 | unsigned long brk_addr = 0; 23 | char buf[128]; 24 | unsigned int mapflags = MAP_PRIVATE|MAP_ANONYMOUS; 25 | 26 | if (!anywhere) 27 | mapflags |= MAP_FIXED; 28 | 29 | /* Just addresses in mapped-in file. */ 30 | hdr = (Elf64_Ehdr *)mapped; 31 | pdr = (Elf64_Phdr *)(mapped + hdr->e_phoff); 32 | 33 | entry_point = (void *)hdr->e_entry; 34 | 35 | for (i = 0; i < hdr->e_phnum; ++i, ++pdr) 36 | { 37 | unsigned int protflags = 0; 38 | unsigned long map_addr = 0, rounded_len, k; 39 | unsigned long unaligned_map_addr = 0; 40 | void *segment; 41 | 42 | if (pdr->p_type == 0x03) /* PT_INTERP */ 43 | { 44 | interp = pdr; 45 | continue; 46 | } 47 | 48 | if (pdr->p_type != PT_LOAD) /* Segment not "loadable" */ 49 | continue; 50 | 51 | if (text_segment != 0 && anywhere) 52 | { 53 | unaligned_map_addr 54 | = (unsigned long)text_segment 55 | + ((unsigned long)pdr->p_vaddr - (unsigned long)initial_vaddr) 56 | ; 57 | map_addr = ALIGNDOWN((unsigned long)unaligned_map_addr, 0x1000); 58 | mapflags |= MAP_FIXED; 59 | } else if (!anywhere) { 60 | map_addr = ALIGNDOWN(pdr->p_vaddr, 0x1000); 61 | } else { 62 | map_addr = 0UL; 63 | } 64 | 65 | if (!anywhere && initial_vaddr == 0) 66 | initial_vaddr = pdr->p_vaddr; 67 | 68 | /* mmap() freaks out if you give it a non-multiple of pagesize */ 69 | rounded_len = (unsigned long)pdr->p_memsz + ((unsigned long)pdr->p_vaddr % 0x1000); 70 | rounded_len = ROUNDUP(rounded_len, 0x1000); 71 | 72 | segment = linux_mmap( 73 | (void *)map_addr, 74 | rounded_len, 75 | PROT_WRITE, mapflags, -1, 0 76 | ); 77 | 78 | if (segment == (void *) -1) 79 | { 80 | print_string(1, "Failed to mmap() "); 81 | to_hex(pdr->p_memsz, buf); 82 | print_string(1, buf); 83 | print_string(1, " bytes at 0x"); 84 | to_hex(map_addr, buf); 85 | print_string(1, buf); 86 | print_string(1, "\n"); 87 | linux_exit(3); 88 | } 89 | 90 | memcopy( 91 | !anywhere? (void *)pdr->p_vaddr: 92 | (void *)((unsigned long)segment + ((unsigned long)pdr->p_vaddr % 0x1000)), 93 | mapped + pdr->p_offset, 94 | pdr->p_filesz 95 | ); 96 | 97 | if (!text_segment) 98 | { 99 | *elf_ehdr = segment; 100 | text_segment = segment; 101 | initial_vaddr = pdr->p_vaddr; 102 | if (anywhere) 103 | entry_point = (void *)((unsigned long)entry_point 104 | - (unsigned long)pdr->p_vaddr 105 | + (unsigned long)text_segment); 106 | } 107 | 108 | 109 | if (pdr->p_flags & PF_R) 110 | protflags |= PROT_READ; 111 | if (pdr->p_flags & PF_W) 112 | protflags |= PROT_WRITE; 113 | if (pdr->p_flags & PF_X) 114 | protflags |= PROT_EXEC; 115 | 116 | linux_mprotect(segment, rounded_len, protflags); 117 | 118 | k = pdr->p_vaddr + pdr->p_memsz; 119 | if (k > brk_addr) brk_addr = k; 120 | } 121 | 122 | if (interp) 123 | { 124 | Elf64_Ehdr *junk_ehdr = NULL; 125 | unsigned long sz_dummy; 126 | entry_point = load_elf(map_file(&(((char *)mapped)[interp->p_offset]), &sz_dummy), 1, ldso_ehdr, &junk_ehdr); 127 | } 128 | 129 | if (!anywhere) 130 | linux_brk(ROUNDUP(brk_addr, 0x1000)); 131 | 132 | return (void *)entry_point; 133 | } 134 | 135 | void * 136 | memcopy(void *dest, const void *src, unsigned long n) 137 | { 138 | unsigned long i; 139 | unsigned char *d = (unsigned char *)dest; 140 | unsigned char *s = (unsigned char *)src; 141 | 142 | /* 143 | char buf[64]; 144 | 145 | print_string(1, "load_elf() copying 0x"); 146 | to_hex(n, buf); 147 | print_string(1, buf); 148 | print_string(1, " bytes from 0x"); 149 | to_hex((unsigned long)src, buf); 150 | print_string(1, buf); 151 | print_string(1, " to 0x"); 152 | to_hex((unsigned long)dest, buf); 153 | print_string(1, buf); 154 | print_string(1, "\n"); 155 | linux_read(0, buf, 1); 156 | */ 157 | 158 | for (i = 0; i < n; ++i) 159 | d[i] = s[i]; 160 | 161 | return dest; 162 | } 163 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -I. -g -Wall -Wextra -std=gnu99 -nostdlib -fPIC 3 | 4 | all: dyn_unmap_run hw env_test margs args2 elfauxv elfauxv_dynamic \ 5 | example ulexec.so mycat 6 | 7 | mycat: mycat.c libstatic/libstatic.h libstatic/crt.h libstatic/libstatic.a 8 | gcc -I. -g -Wall -std=gnu99 -nostdlib -c mycat.c 9 | gcc -I. -g -std=gnu99 -nostdlib \ 10 | mycat.o -o mycat -Llibstatic -lstatic 11 | 12 | dyn_unmap_run: dyn_unmap_run.c load_elf.o map_file.o stack_fix.o ulexec.h libstatic/libstatic.h libstatic/crt.h libstatic/libstatic.a 13 | gcc -I. -g -Wall -std=gnu99 -nostdlib -fPIC -c dyn_unmap_run.c 14 | gcc -I. -g -std=gnu99 -nostdlib \ 15 | dyn_unmap_run.o load_elf.o map_file.o stack_fix.o -o dyn_unmap_run -Llibstatic -lstatic 16 | 17 | ulexec.so: ulexec.c load_elf.o map_file.o stack_fix.o ulexec.h unmap.o \ 18 | libstatic/libstatic.h libstatic/crt.h libstatic/libstatic.a 19 | gcc -I. -g -Wall -std=gnu99 -nostdlib -fPIC -c ulexec.c 20 | gcc -fPIC -shared -I. -g -std=gnu99 -nostdlib \ 21 | ulexec.o load_elf.o map_file.o unmap.o stack_fix.o -o ulexec.so -Llibstatic -lstatic 22 | 23 | env_test: env_test.o libstatic/libstatic.a 24 | gcc -g -I. -std=gnu99 -nostdlib -fPIC \ 25 | env_test.o -o env_test \ 26 | -Llibstatic -lstatic 27 | chmod ugo-x env_test 28 | 29 | places: places.o libstatic/libstatic.a 30 | gcc -g -I. -std=gnu99 -nostdlib -fPIC \ 31 | places.o -o places \ 32 | -Llibstatic -lstatic 33 | chmod ugo-x places 34 | 35 | margs: margs.o libstatic/libstatic.a 36 | gcc -g -I. -std=gnu99 -nostdlib -fPIC \ 37 | margs.o -o margs \ 38 | -Llibstatic -lstatic 39 | chmod ugo-x margs 40 | 41 | hw: hw.c libstatic/libstatic.a libstatic/crt.h 42 | gcc -I. -g -Wall -std=gnu99 -nostdlib -fPIC -c hw.c 43 | gcc -I. -g -std=gnu99 -nostdlib \ 44 | hw.o -o hw -Llibstatic -lstatic 45 | chmod ugo-x hw 46 | 47 | raw: raw.c libstatic/libstatic.a 48 | gcc -I. -g -Wall -std=gnu99 -nostdlib -fPIC -c raw.c 49 | gcc -I. -g -std=gnu99 -nostdlib raw.o -o raw -Llibstatic -lstatic 50 | 51 | globaltest: globaltest.c libstatic/libstatic.a libstatic/crt.h 52 | gcc -I. -g -Wall -std=gnu99 -nostdlib -fPIC -c globaltest.c 53 | gcc -I. -g -std=gnu99 -nostdlib \ 54 | globaltest.o -o globaltest -Llibstatic -lstatic 55 | chmod ugo-x globaltest 56 | 57 | global2: global2.c libstatic/libstatic.a libstatic/crt.h 58 | gcc -I. -g -Wall -std=gnu99 -nostdlib -fPIC -c global2.c 59 | gcc -I. -g -std=gnu99 -nostdlib \ 60 | global2.o -o global2 -Llibstatic -lstatic 61 | chmod ugo-x global2 62 | 63 | elfauxv: elfauxv.o libstatic/libstatic.a 64 | gcc -I. -g -std=gnu99 -nostdlib \ 65 | elfauxv.o -o elfauxv -Llibstatic -lstatic 66 | chmod ugo-x elfauxv 67 | 68 | args2: args2.c 69 | gcc -I. -g -Wall -Wextra -o args2 args2.c 70 | chmod ugo-x args2 71 | 72 | example: example.c 73 | gcc -I. -g -Wall -Wextra -o example example.c -ldl 74 | 75 | elfauxv_dynamic: elfauxv_dynamic.c 76 | gcc -I. -g -Wall -Wextra -o elfauxv_dynamic elfauxv_dynamic.c 77 | chmod ugo-x elfauxv_dynamic 78 | 79 | libstatic/libstatic.a: 80 | cd libstatic; make 81 | 82 | load_elf.o: load_elf.c ulexec.h libstatic/libstatic.h 83 | map_file.o: map_file.c ulexec.h libstatic/libstatic.h 84 | unmap.o: unmap.c ulexec.h libstatic/libstatic.h 85 | places.o: places.c ulexec.h libstatic/libstatic.h 86 | elfauxv.o: elfauxv.c ulexec.h libstatic/libstatic.h 87 | stack_fix.o: stack_fix.c ulexec.h libstatic/libstatic.h 88 | 89 | clean: 90 | -rm -rf *.o *.a *core 91 | -rm -rf margs hw args2 env_test dyn_unmap_run static_dyn_load_run 92 | -rm -rf elfauxv elfauxv_dynamic hw2 example ulexec.so 93 | cd libstatic; make clean 94 | -------------------------------------------------------------------------------- /map_file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | void * 10 | map_file(char *file_to_map, unsigned long *sz) 11 | { 12 | struct stat sb; 13 | void *mapped; 14 | 15 | if (0 > linux_stat(file_to_map, &sb)) 16 | { 17 | error_msg("map_file stat() failed "); 18 | linux_exit(1); 19 | } 20 | 21 | *sz = sb.st_size; 22 | 23 | mapped = linux_mmap(NULL, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 24 | 25 | if (mapped == (void *)-1) 26 | { 27 | error_msg("map_file mmap() failed "); 28 | linux_exit(1); 29 | } 30 | 31 | copy_in(file_to_map, mapped); 32 | 33 | return mapped; 34 | } 35 | 36 | void 37 | copy_in(char *filename, void *address) 38 | { 39 | int fd, cc; 40 | off_t offset = 0; 41 | char buf[1024]; 42 | 43 | if (0 > (fd = linux_open(filename, 0, 0))) 44 | { 45 | error_msg("opening dynamically-loaded file failed"); 46 | linux_exit(2); 47 | } 48 | 49 | while (0 < (cc = linux_read(fd, buf, sizeof(buf)))) 50 | { 51 | memcpy((address + offset), buf, cc); 52 | offset += cc; 53 | } 54 | 55 | linux_close(fd); 56 | } 57 | -------------------------------------------------------------------------------- /margs.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | void c_main(int ac, char **av, char **env) 6 | { 7 | int i; 8 | 9 | for (i = 0; i < ac; ++i) 10 | { 11 | print_long(1, (unsigned long)i); 12 | print_string(1, " "); 13 | print_string(1, "\""); 14 | print_string(1, av[i]); 15 | print_string(1, "\""); 16 | print_string(1, "\r\n"); 17 | } 18 | 19 | linux_exit(13); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /mycat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define BUFSIZ 1024 4 | void 5 | c_main(int ac, char **av, char **envp) 6 | { 7 | int fd = linux_open(av[1], 0, 0); 8 | int cc; 9 | char buf[BUFSIZ]; 10 | while (0 < (cc = linux_read(fd, buf, sizeof(buf)))) 11 | linux_write(1, buf, cc); 12 | linux_close(fd); 13 | linux_exit(0); 14 | } 15 | -------------------------------------------------------------------------------- /stack_fix.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define ALLOCATE(size) \ 8 | linux_mmap(0, (size), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) 9 | 10 | /* call with argc as positive value for main's argv, 11 | * call with argc == 0 for env. */ 12 | struct saved_block * 13 | save_argv(int argc, char **argv) 14 | { 15 | struct saved_block *r = NULL; 16 | int i, len; 17 | char *str; 18 | 19 | if (argc > 0) 20 | for (i = 0, len = 0; i < argc; ++i) 21 | len += strlen(argv[i]) + 1; 22 | else { 23 | argc = 0; 24 | char **p = argv; 25 | while (*p) 26 | { 27 | len += strlen(*p) + 1; 28 | ++p; /* move past ASCII Nul */ 29 | ++argc; 30 | } 31 | } 32 | 33 | r = ALLOCATE(sizeof(*r)); 34 | r->size = len; 35 | r->cnt = argc; 36 | r->block = ALLOCATE(len); 37 | 38 | /* Do it this way because the values of argv[] may not actually 39 | * exist as contiguous strings. We will make them contiguous. */ 40 | for (i = 0, str = r->block; i < argc; i++) 41 | { 42 | int j; 43 | for (j = 0; argv[i][j]; ++j) 44 | str[j] = argv[i][j]; 45 | str[j] = '\0'; 46 | str += (j + 1); 47 | } 48 | 49 | return r; 50 | } 51 | 52 | void 53 | release_args(struct saved_block *args) 54 | { 55 | linux_munmap((void *)args->block, args->size); 56 | linux_munmap((void *)args, sizeof(*args)); 57 | } 58 | 59 | 60 | struct saved_block * 61 | save_elfauxv(char **envp) 62 | { 63 | struct saved_block *r; 64 | unsigned long *p; 65 | int cnt; 66 | Elf64_auxv_t *q; 67 | 68 | p = (unsigned long *)envp; 69 | while (*p != 0) 70 | ++p; 71 | 72 | ++p; /* skip null word after env */ 73 | 74 | for (cnt = 0, q = (Elf64_auxv_t *)p; q->a_type != AT_NULL; ++q) 75 | ++cnt; 76 | 77 | ++cnt; /* The AT_NULL final entry */ 78 | 79 | r = ALLOCATE(sizeof(*r)); 80 | r->size = sizeof(*q) * cnt; 81 | r->cnt = cnt; 82 | r->block = ALLOCATE(r->size); 83 | memcpy((void *)r->block, (void *)p, r->size); 84 | 85 | return r; 86 | } 87 | 88 | /* Returns value for %rsp, the new "bottom of the stack */ 89 | void * 90 | stack_setup( 91 | struct saved_block *args, 92 | struct saved_block *envp, 93 | struct saved_block *auxvp, 94 | Elf64_Ehdr *ehdr, 95 | Elf64_Ehdr *ldso 96 | ) 97 | { 98 | Elf64_auxv_t *aux, *excfn = NULL; 99 | char **av, **ev; 100 | char *addr, *str, *rsp; 101 | unsigned long *ptr; 102 | int i, j; 103 | char newstack[16384]; 104 | 105 | /* Align new stack. */ 106 | rsp = (char *)ALIGN(((unsigned long)&newstack[150]), 16); 107 | 108 | /* 109 | * After returning from 110 | * stack_setup(), don't do anything that uses the call stack: that 111 | * will roach this newly-constructed stack. 112 | */ 113 | 114 | ptr = (unsigned long *)rsp; 115 | 116 | *ptr++ = args->cnt; /* set argc */ 117 | av = (char **)ptr; 118 | ptr += args->cnt; /* skip over argv[] */ 119 | *ptr++ = 0; 120 | 121 | ev = (char **)ptr; 122 | ptr += envp->cnt; /* skip over envp[] */ 123 | *ptr++ = 0; 124 | 125 | aux = (Elf64_auxv_t *)ptr; 126 | 127 | ptr = (unsigned long *)ROUNDUP((unsigned long)ptr + auxvp->size, sizeof(unsigned long)); 128 | 129 | /* copy ELF auxilliary vector table */ 130 | addr = (char *)aux; 131 | for (j = 0; j < auxvp->size; ++j) 132 | addr[j] = auxvp->block[j]; 133 | 134 | /* Fix up a few entries: kernel will have set up the AUXV 135 | * for the user-land exec program, mapped in at a low address. 136 | * need to fix up a few AUXV entries for the "real" program. */ 137 | for (i = 0; i < auxvp->cnt; ++i) 138 | { 139 | switch (aux[i].a_type) 140 | { 141 | case AT_PHDR: aux[i].a_un.a_val = (unsigned long)((char *)ehdr + ehdr->e_phoff); break; 142 | case AT_PHNUM: aux[i].a_un.a_val = ehdr->e_phnum; break; 143 | case AT_BASE: aux[i].a_un.a_val = (unsigned long)ldso; break; 144 | case AT_ENTRY: aux[i].a_un.a_val = (unsigned long)ehdr->e_entry; break; 145 | #ifdef AT_EXECFN 146 | case AT_EXECFN: excfn = &(aux[i]); break; 147 | #endif 148 | } 149 | } 150 | 151 | *ptr++ = 0; 152 | 153 | /* Copy argv strings onto stack */ 154 | addr = (char *)ptr; 155 | str = args->block; 156 | 157 | for (i = 0; i < args->cnt; ++i) 158 | { 159 | av[i] = addr; 160 | for (j = 0; *str; ++j) 161 | *addr++ = *str++; 162 | *addr++ = *str++; /* ASCII Nul */ 163 | } 164 | 165 | ptr = (unsigned long *)ROUNDUP((unsigned long)addr, sizeof(unsigned long)); 166 | *ptr = 0; 167 | 168 | /* Copy envp strings onto stack */ 169 | addr = (char *)ptr; 170 | str = envp->block; 171 | 172 | for (i = 0; i < envp->cnt; ++i) 173 | { 174 | ev[i] = addr; 175 | for (j = 0; *str; ++j) 176 | *addr++ = *str++; 177 | *addr++ = *str++; /* ASCII Nul */ 178 | } 179 | 180 | ptr = (unsigned long *)ROUNDUP((unsigned long)addr, sizeof(unsigned long)); 181 | *ptr = 0; 182 | 183 | /* Executable name at top of stack */ 184 | if (excfn) 185 | { 186 | addr = (char *)ptr; 187 | str = args->block; 188 | excfn->a_un.a_val = (unsigned long)addr; 189 | for (j = 0; *str; ++j) 190 | *addr++ = *str++; 191 | *addr++ = *str++; /* ASCII Nul */ 192 | 193 | ptr = (unsigned long *)ROUNDUP((unsigned long)addr, sizeof(unsigned long)); 194 | } 195 | 196 | release_args(args); 197 | release_args(envp); 198 | release_args(auxvp); 199 | 200 | return ((void *)rsp); 201 | } 202 | -------------------------------------------------------------------------------- /ulexec.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #define JMP_ADDR(x) asm("\tjmp *%0\n" :: "r" (x)) 10 | #define SET_STACK(x) asm("\tmovq %0, %%rsp\n" :: "r"(x)) 11 | 12 | void 13 | print_maps(void) 14 | { 15 | char rbuf[1024]; 16 | int fd, cc; 17 | 18 | fd = linux_open("/proc/self/maps", 0, 0); 19 | while (0 < (cc = linux_read(fd, rbuf, sizeof(rbuf)))) 20 | linux_write(1, rbuf, cc); 21 | linux_close(fd); 22 | } 23 | 24 | void 25 | ulexec(int ac, char **av, char **env) 26 | { 27 | char *file_to_map; 28 | char *file_to_unmap; 29 | int how_to_map = 0; 30 | void *mapped; 31 | void *entry_point; 32 | Elf64_Ehdr *elf_ehdr, *ldso_ehdr; 33 | Elf64_Phdr *phdr; 34 | struct saved_block *argvb, *envb, *elfauxvb; 35 | int trim_args, i; 36 | void *stack_bottom; 37 | unsigned long mapped_sz; 38 | 39 | file_to_map = av[2]; 40 | file_to_unmap = av[0]; 41 | how_to_map = 0; 42 | trim_args = 2; 43 | 44 | 45 | if (file_to_unmap) 46 | { 47 | char *s = strrchr(file_to_unmap, '/'); 48 | if (s) 49 | file_to_unmap = s; 50 | unmap(file_to_unmap); 51 | } 52 | 53 | mapped = map_file(file_to_map, &mapped_sz); 54 | elf_ehdr = (Elf64_Ehdr *)mapped; 55 | 56 | phdr = (Elf64_Phdr *)((unsigned long)elf_ehdr + elf_ehdr->e_phoff); 57 | 58 | for (i = 0; i < elf_ehdr->e_phnum; ++i) 59 | if (phdr[i].p_type == PT_LOAD && phdr[i].p_vaddr == 0) 60 | { 61 | how_to_map = 1; /* map it anywhere, like ld.so, or PIC code. */ 62 | break; 63 | } 64 | 65 | entry_point = load_elf(mapped, how_to_map, &elf_ehdr, &ldso_ehdr); 66 | 67 | linux_munmap(mapped, mapped_sz); 68 | 69 | argvb = save_argv(ac - trim_args, &av[trim_args]); 70 | envb = save_argv(0, env); 71 | elfauxvb = save_elfauxv(env); 72 | 73 | stack_bottom = stack_setup(argvb, envb, elfauxvb, elf_ehdr, ldso_ehdr); 74 | 75 | SET_STACK(stack_bottom); 76 | JMP_ADDR(entry_point); 77 | } 78 | 79 | void 80 | error_msg(char *msg) 81 | { 82 | char buf[32]; 83 | print_string(1, msg); 84 | print_string(1, " "); 85 | to_decimal(errno, buf); 86 | print_string(1, buf); 87 | print_string(1, "\n"); 88 | } 89 | 90 | void 91 | print_address(char *phrase, void *address) 92 | { 93 | char buf[256]; 94 | to_hex((unsigned long)address, buf); 95 | print_string(1, phrase); 96 | print_string(1, " 0x"); 97 | print_string(1, buf); 98 | print_string(1, "\n"); 99 | } 100 | 101 | 102 | /* 103 | void * 104 | memcpy(void *dest, const void *src, unsigned long n) 105 | { 106 | unsigned long i; 107 | unsigned char *d = (unsigned char *)dest; 108 | unsigned char *s = (unsigned char *)src; 109 | 110 | for (i = 0; i < n; ++i) 111 | d[i] = s[i]; 112 | 113 | return dest; 114 | } 115 | */ 116 | -------------------------------------------------------------------------------- /ulexec.h: -------------------------------------------------------------------------------- 1 | void *map_file(char *filename, unsigned long *sz); 2 | void error_msg(char *msg); 3 | void print_address(char *phrase, void *address); 4 | void *load_elf(char *mapped, int anywhere, Elf64_Ehdr **elf, Elf64_Ehdr **ldso); 5 | void unmap(char *progname); 6 | void copy_in(char *progname, void *address); 7 | 8 | #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) 9 | #define ALIGN(k, v) (((k)+((v)-1))&(~((v)-1))) 10 | #define ALIGNDOWN(k, v) ((unsigned long)(k)&(~((unsigned long)(v)-1))) 11 | 12 | struct saved_block { 13 | int size; 14 | int cnt; 15 | char *block; 16 | }; 17 | 18 | void release_args(struct saved_block *args); 19 | struct saved_block *save_elfauxv(char **envp); 20 | struct saved_block *save_argv(int argc, char **argv); 21 | void *stack_setup( 22 | struct saved_block *args, 23 | struct saved_block *envp, 24 | struct saved_block *auxvp, 25 | Elf64_Ehdr *ehdr, 26 | Elf64_Ehdr *ldso 27 | ); 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /unmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void 4 | unmap(char *progname) 5 | { 6 | char buf[1024], *p; 7 | char rbuf[2048]; 8 | int cc, fd; 9 | 10 | fd = linux_open("/proc/self/maps", 0, 0); 11 | 12 | p = &buf[0]; 13 | 14 | while (0 < (cc = linux_read(fd, rbuf, sizeof(rbuf)))) 15 | { 16 | int i; 17 | 18 | for (i = 0; i < cc; ++i) 19 | { 20 | int c = rbuf[i]; 21 | 22 | if ('\n' != c) 23 | *p++ = c; 24 | else { 25 | *p = '\0'; 26 | /* When a line from /proc/self/maps shows up as having been 27 | * mapped in from this running program, ld.so or libc, unmap it. 28 | * This will keep the exec'd program's address space a lot 29 | * cleaner. But even a 32-bit address space can hold 2 copies 30 | * of glibc without ill effects, so you don't really have to 31 | * munmap() anything other than the program calling ul_exec() */ 32 | if (strstr(buf, progname) || strstr(buf, "libdl") || strstr(buf, "/usr/lib/ld-") 33 | || strstr(buf, "/lib64/ld-") || strstr(buf, "libc")) 34 | { 35 | char *u; 36 | char *first, *second; 37 | unsigned long low, high; 38 | 39 | u = strchr(buf, ' '); 40 | *u = '\0'; 41 | 42 | first = buf; 43 | 44 | second = strchr(first, '-'); 45 | *second = '\0'; 46 | ++second; 47 | 48 | low = strtoul(first, NULL, 0x10); 49 | high = strtoul(second, NULL, 0x10); 50 | 51 | linux_munmap((void *)low, high-low); 52 | } 53 | 54 | p = &buf[0]; 55 | } 56 | } 57 | } 58 | 59 | linux_close(fd); 60 | 61 | } 62 | --------------------------------------------------------------------------------