├── README ├── eepy ├── eepy.c └── eepy.h ├── main.c └── makefile /README: -------------------------------------------------------------------------------- 1 | eepy - sleep obfuscation via rop 2 | 3 | [features] 4 | randomized RC4 key on each iteration 5 | PT_LOAD encryption 6 | full heap encryption 7 | -------------------------------------------------------------------------------- /eepy/eepy.c: -------------------------------------------------------------------------------- 1 | /* @file eepy.c 2 | * @author 3 | * @brief eepy sleep obfuscator 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* doesn't have this for some reason? */ 17 | #if __SIZEOF_POINTER__ == 8 18 | #define ElfW(type) Elf64_##type 19 | #else 20 | #define ElfW(type) Elf32_##type 21 | #endif 22 | 23 | #define ROP_CALL_2(_rop_ctx, val1, val2, fn_ptr) \ 24 | __push((uintptr)fn_ptr); \ 25 | __push((uintptr)val2); \ 26 | __push((uintptr)_rop_ctx->pop_rsi); \ 27 | __push((uintptr)val1); \ 28 | __push((uintptr)_rop_ctx->pop_rdi) 29 | 30 | #define ROP_CALL_3(_rop_ctx, val1, val2, val3, fn_ptr) \ 31 | __push((uintptr)fn_ptr); \ 32 | __push((uintptr)0); \ 33 | __push((uintptr)0); \ 34 | __push((uintptr)val3); \ 35 | __push((uintptr)_rop_ctx->pop_rdx_rcx_rbx); \ 36 | __push((uintptr)val2); \ 37 | __push((uintptr)_rop_ctx->pop_rsi); \ 38 | __push((uintptr)val1); \ 39 | __push((uintptr)_rop_ctx->pop_rdi) 40 | 41 | #define ROP_CALL_4(_rop_ctx, val1, val2, val3, val4, fn_ptr) \ 42 | __push((uintptr)fn_ptr); \ 43 | __push((uintptr)0); \ 44 | __push((uintptr)val4); \ 45 | __push((uintptr)val3); \ 46 | __push((uintptr)_rop_ctx->pop_rdx_rcx_rbx); \ 47 | __push((uintptr)val2); \ 48 | __push((uintptr)_rop_ctx->pop_rsi); \ 49 | __push((uintptr)val1); \ 50 | __push((uintptr)_rop_ctx->pop_rdi) 51 | 52 | /* @brief pushes a value onto the stack 53 | * @param val: value to push 54 | * @retval none 55 | * @note this routine needs to be inlined at all times 56 | */ 57 | static inline __attribute__((always_inline)) void __push(uintptr val) { 58 | __asm__ volatile( 59 | "push %0\n" 60 | : 61 | : "r" (val) 62 | : "memory" 63 | ); 64 | } 65 | 66 | /* @brief inline return function to start rop chain 67 | * @retval none 68 | */ 69 | static inline __attribute__((always_inline)) void __ret(void) { 70 | __asm__ volatile( 71 | "ret\n" 72 | ); 73 | } 74 | 75 | struct mem_range_t { 76 | void *start; // base addr 77 | uintptr size; // size in bytes 78 | int prot; 79 | }; 80 | 81 | static int eepy_get_libc(uintptr *base, uintptr *size) { 82 | if (!base | !size) { 83 | return(0); 84 | } 85 | FILE *maps = fopen("/proc/self/maps", "r"); 86 | if (!maps) { 87 | perror("fopen"); 88 | return(0); 89 | } 90 | uintptr l_start = 0; 91 | uintptr l_size = 0; 92 | 93 | char line[256]; 94 | while (fgets(line, sizeof(line), maps)) { 95 | if (strstr(line, "libc") && strstr(line, "/lib")) { 96 | uintptr start, end; 97 | char perms[5]; 98 | if (sscanf(line, "%lx-%lx %4s", &start, &end, perms) == 3) { 99 | if (strcmp(perms, "r-xp") == 0) { 100 | l_size = end - start; 101 | l_start = start; 102 | break; 103 | } 104 | } 105 | } 106 | } 107 | 108 | fclose(maps); 109 | if (!l_start || !l_size) { 110 | return(0); 111 | } 112 | *base = l_start; 113 | *size = l_size; 114 | return(1); 115 | } 116 | 117 | static int eepy_get_gadgets(struct eepy_ctx *ctx, uintptr libc_base, 118 | uintptr libc_size) { 119 | if (!ctx || !libc_base || !libc_size) { 120 | return(0); 121 | } 122 | 123 | void *pop_rdi = NULL; 124 | void *pop_rsi = NULL; 125 | void *pop_rdx_rcx_rbx = NULL; 126 | void *pop_rax = NULL; 127 | void *syscall = NULL; 128 | void *jmp_rax = NULL; 129 | 130 | unsigned char *start = (unsigned char*)libc_base; 131 | for (uintptr i = 0; i < libc_size; i++) { 132 | if (start[i] == 0xC3) { 133 | /* pop rdx; pop rcx; pop rbx; ret */ 134 | if (i >= 3 && pop_rdx_rcx_rbx == NULL && 135 | start[i - 1] == 0x5B && 136 | start[i - 2] == 0x59 && 137 | start[i - 3] == 0x5A) { 138 | pop_rdx_rcx_rbx = &start[i - 3]; 139 | } 140 | /* pop rdi; ret */ 141 | if (i >= 1 && pop_rdi == NULL && 142 | start[i - 1] == 0x5F) { 143 | pop_rdi = &start[i - 1]; 144 | } 145 | /* pop rsi; ret */ 146 | if (i >= 1 && pop_rsi == NULL && 147 | start[i - 1] == 0x5E) { 148 | pop_rsi = &start[i - 1]; 149 | } 150 | /* pop rax; ret */ 151 | if (i >= 1 && pop_rax == NULL && 152 | start[i - 1] == 0x58) { 153 | pop_rax = &start[i - 1]; 154 | } 155 | } 156 | /* jmp rax */ 157 | if (i >= 1 && jmp_rax == NULL && 158 | start[i - 0] == 0xE0 && 159 | start[i - 1] == 0xFF) { 160 | jmp_rax = &start[i - 1]; 161 | } 162 | /* syscall */ 163 | if (i >= 1 && syscall == NULL && 164 | start[i - 0] == 0x05 && 165 | start[i - 1] == 0x0F) { 166 | syscall = &start[i - 1]; 167 | } 168 | } 169 | 170 | if (!pop_rdi || !pop_rsi || !pop_rdx_rcx_rbx || !pop_rax || !syscall || 171 | !jmp_rax) { 172 | return(0); 173 | } 174 | ctx->pop_rsi = (uintptr)pop_rsi; 175 | ctx->pop_rdi = (uintptr)pop_rdi; 176 | ctx->pop_rdx_rcx_rbx = (uintptr)pop_rdx_rcx_rbx; 177 | return(1); 178 | } 179 | 180 | static int eepy_get_base(struct eepy_ctx *ctx) { 181 | ElfW(Phdr) *phdr = (ElfW(Phdr) *)getauxval(AT_PHDR); 182 | if (!phdr) { 183 | return(0); 184 | } 185 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)((uintptr)phdr - phdr->p_offset); 186 | ctx->prog_base = (void*)ehdr; 187 | return(1); 188 | } 189 | 190 | /* @brief initializes an eepy context 191 | * @param ctx - eepy context 192 | * @retval 1 on success, 0 otherwise 193 | */ 194 | int eepy_init(struct eepy_ctx *ctx) { 195 | uintptr libc_base, libc_size; 196 | if (!eepy_get_libc(&libc_base, &libc_size)) { 197 | return(0); 198 | } 199 | if (!eepy_get_gadgets(ctx, libc_base, libc_size)) { 200 | return(0); 201 | } 202 | if (!eepy_get_base(ctx)) { 203 | return(0); 204 | } 205 | return(1); 206 | } 207 | 208 | static struct mem_range_t *eepy_get_ranges(void *img_base, int *num_ranges) { 209 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr)*)img_base; 210 | if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || 211 | ehdr->e_ident[EI_MAG1] != ELFMAG1 || 212 | ehdr->e_ident[EI_MAG2] != ELFMAG2 || 213 | ehdr->e_ident[EI_MAG3] != ELFMAG3) { 214 | printf("ps_gadget_get_ranges: invalid elf header\n"); 215 | return(NULL); 216 | } 217 | 218 | int num_seg = 0, j = 0; 219 | struct mem_range_t *ranges; 220 | ElfW(Phdr) *phdr = (ElfW(Phdr)*)((char*)img_base + ehdr->e_phoff); 221 | 222 | for (int i = 0; i < ehdr->e_phnum; i++) { 223 | ElfW(Phdr) *seg = &phdr[i]; 224 | if (seg->p_type != PT_LOAD) { 225 | continue; 226 | } 227 | num_seg++; 228 | } 229 | ranges = malloc(sizeof(struct mem_range_t) * num_seg); 230 | 231 | for (int i = 0; i < ehdr->e_phnum; i++) { 232 | ElfW(Phdr) *seg = &phdr[i]; 233 | if (seg->p_type != PT_LOAD) { 234 | continue; 235 | } 236 | 237 | int prot = 0; 238 | if (seg->p_flags & PF_R) prot |= PROT_READ; 239 | if (seg->p_flags & PF_W) prot |= PROT_WRITE; 240 | if (seg->p_flags & PF_X) prot |= PROT_EXEC; 241 | 242 | ranges[j].start = img_base + (seg->p_vaddr & ~(getpagesize() - 1)); 243 | ranges[j].size = (seg->p_memsz + getpagesize() - 1) 244 | & ~(getpagesize() - 1); 245 | ranges[j].prot = prot; 246 | j++; 247 | } 248 | *num_ranges = num_seg; 249 | return(ranges); 250 | } 251 | 252 | static void eepy_get_heap_range(struct mem_range_t *heap_range) { 253 | if (!heap_range) { 254 | return; 255 | } 256 | 257 | FILE *maps = fopen("/proc/self/maps", "r"); 258 | uintptr l_start = 0, l_size = 0; 259 | char line[256]; 260 | 261 | while (fgets(line, sizeof(line), maps)) { 262 | if (strstr(line, "[heap]")) { 263 | uintptr start, end; 264 | if (sscanf(line, "%lx-%lx", &start, &end) == 2) { 265 | l_start = start; 266 | l_size = end - start; 267 | } 268 | } 269 | } 270 | 271 | if (!l_start || !l_size) { 272 | return; 273 | } 274 | heap_range->start = (void*)l_start; 275 | heap_range->size = l_size; 276 | } 277 | 278 | /* @brief sets up and executes the rop chain 279 | * @param ctx - eepy context 280 | * @param sleep - sleep time in seconds 281 | * @retval none 282 | */ 283 | void bedtime(struct eepy_ctx *ctx, u32 sleep) { 284 | if (!ctx || !sleep) { 285 | return; 286 | } 287 | 288 | int num_ranges = 0; 289 | struct mem_range_t *mem_ranges = eepy_get_ranges(ctx->prog_base, 290 | &num_ranges); 291 | struct mem_range_t heap_range = {0}; 292 | eepy_get_heap_range(&heap_range); 293 | 294 | RC4_KEY rc4_key; 295 | unsigned char key_bytes[16]; 296 | RAND_bytes(key_bytes, 16); 297 | 298 | struct timespec timespec_t; 299 | timespec_t.tv_sec = sleep; 300 | timespec_t.tv_nsec = 0; 301 | 302 | void *ret_addr = &&ps_chain_ret; 303 | __push((uintptr)ret_addr); // final return address 304 | 305 | /* mprotect regions back to original protection */ 306 | for (int i = 0; i < num_ranges; i++) { 307 | ROP_CALL_3(ctx, mem_ranges[i].start, mem_ranges[i].size, 308 | mem_ranges[i].prot, mprotect); 309 | } 310 | 311 | /* decrypt heap */ 312 | ROP_CALL_4(ctx, &rc4_key, heap_range.size, heap_range.start, 313 | heap_range.start, RC4); 314 | 315 | /* set up arc4 key */ 316 | ROP_CALL_3(ctx, &rc4_key, 16, key_bytes, RC4_set_key); 317 | 318 | /* decrypt regions */ 319 | for (int i = 0; i < num_ranges; i++) { 320 | ROP_CALL_4(ctx, &rc4_key, mem_ranges[i].size, mem_ranges[i].start, 321 | mem_ranges[i].start, RC4); 322 | } 323 | 324 | /* set up arc4 key */ 325 | ROP_CALL_3(ctx, &rc4_key, 16, key_bytes, RC4_set_key); 326 | 327 | /* nanosleep */ 328 | ROP_CALL_2(ctx, ×pec_t, NULL, nanosleep); 329 | 330 | /* encrypt regions */ 331 | for (int i = 0; i < num_ranges; i++) { 332 | ROP_CALL_4(ctx, &rc4_key, mem_ranges[i].size, mem_ranges[i].start, 333 | mem_ranges[i].start, RC4); 334 | } 335 | 336 | /* set up arc4 key */ 337 | ROP_CALL_3(ctx, &rc4_key, 16, key_bytes, RC4_set_key); 338 | 339 | /* encrypt heap */ 340 | ROP_CALL_4(ctx, &rc4_key, heap_range.size, heap_range.start, 341 | heap_range.start, RC4); 342 | 343 | /* set up arc4 key */ 344 | ROP_CALL_3(ctx, &rc4_key, 16, key_bytes, RC4_set_key); 345 | 346 | /* mprotect regions to rw */ 347 | for (int i = 0; i < num_ranges; i++) { 348 | ROP_CALL_3(ctx, mem_ranges[i].start, mem_ranges[i].size, 349 | (PROT_READ | PROT_WRITE), mprotect); 350 | } 351 | 352 | free(mem_ranges); 353 | /* start rop chain - execution continues at [ps_chain_ret] */ 354 | __ret(); 355 | 356 | ps_chain_ret: 357 | return; 358 | } 359 | -------------------------------------------------------------------------------- /eepy/eepy.h: -------------------------------------------------------------------------------- 1 | #ifndef __EEPY_H__ 2 | #define __EEPY_H__ 3 | 4 | /* @file eepy.h 5 | * @author 6 | * @brief eepy sleep obfuscator 7 | */ 8 | 9 | typedef unsigned int u32; 10 | typedef unsigned long uintptr; 11 | 12 | struct eepy_ctx { 13 | void *prog_base; 14 | uintptr pop_rdi; 15 | uintptr pop_rsi; 16 | uintptr pop_rdx; 17 | uintptr pop_rax; 18 | uintptr pop_rdx_rcx_rbx; 19 | }; 20 | 21 | int eepy_init(struct eepy_ctx *ctx); 22 | void bedtime(struct eepy_ctx *ctx, u32 sleep); 23 | 24 | #endif /* __EEPY_H__ */ 25 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void subroutine(void) { 6 | printf("subroutine called\n"); 7 | } 8 | 9 | int main(void) { 10 | printf("pid: %d\n", getpid()); 11 | 12 | struct eepy_ctx ctx = {0}; 13 | if (!eepy_init(&ctx)) { 14 | printf("failed to initialize eepy\n"); 15 | return(0); 16 | } 17 | 18 | while (1) { 19 | subroutine(); // do work 20 | bedtime(&ctx, 5); // sleep 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Wextra -Wno-deprecated-declarations -Wno-unused-parameter 3 | CFLAGS += -I. -I./eepy 4 | LDFLAGS = -lssl -lcrypto 5 | SRC = main.c eepy/eepy.c 6 | OBJ = $(SRC:.c=.o) 7 | EXEC = demo 8 | 9 | $(EXEC): $(OBJ) 10 | $(CC) -o $(EXEC) $(OBJ) $(LDFLAGS) 11 | 12 | %.o: %.c 13 | $(CC) $(CFLAGS) -c $< -o $@ 14 | 15 | clean: 16 | rm -f $(OBJ) $(EXEC) 17 | --------------------------------------------------------------------------------