From 7d93440bd109123a0841cee2a64a0e9c9e72f5dd Mon Sep 17 00:00:00 2001 From: gbrochar Date: Fri, 30 Aug 2024 15:10:07 +0200 Subject: [PATCH] feat: insert payload ok --- Makefile | 3 + README.md | 2 +- assets/debug_mode.s | 26 ++++++ assets/rsa.s | 149 +++++++++++++++++++++++++++++++++ gen_payload.sh | 1 + inc/woody.h | 20 +++++ src/check_ident.c | 37 +++++++++ src/error.c | 11 +++ src/fetch.c | 8 ++ src/main.c | 2 +- src/woody_woodpacker.c | 184 ++++++++++++++++++++++++++++++++++++++++- 11 files changed, 438 insertions(+), 5 deletions(-) create mode 100644 assets/debug_mode.s create mode 100644 assets/rsa.s create mode 100755 gen_payload.sh create mode 100644 src/check_ident.c create mode 100644 src/error.c create mode 100644 src/fetch.c diff --git a/Makefile b/Makefile index 7e5c4c7..f2a1044 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,9 @@ NAME = woody_woodpacker SRC_FILE = main.c \ woody_woodpacker.c \ + error.c \ + fetch.c \ + check_ident.c \ OBJ_FILE = $(SRC_FILE:.c=.o) diff --git a/README.md b/README.md index 2103ac0..8185194 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # Woody Woodpacker -Woody Woodpacker is a simple ELF packer that will encrypt the text section of an ELF program and add a payload that will write "....WOODY...." and decypher the text section before executing it normally. +Woody Woodpacker is a simple ELF packer that will encrypt the text section of an ELF program and add a payload that will write "....WOODY...." and decrypt the text section before executing it normally. diff --git a/assets/debug_mode.s b/assets/debug_mode.s new file mode 100644 index 0000000..a27c883 --- /dev/null +++ b/assets/debug_mode.s @@ -0,0 +1,26 @@ +bits 64 +global _start + +_start: + push rbp + push rsp + push rax + push rdx + push rsi + push rdi + + mov rdi, 1 + lea rsi, [rel msg] + mov rdx, 14 + mov rax, 1 + syscall + + pop rdi + pop rsi + pop rdx + pop rax + pop rsp + pop rbp + + jmp 0xdadadada + msg db "....WOODY....",10 diff --git a/assets/rsa.s b/assets/rsa.s new file mode 100644 index 0000000..74f57cf --- /dev/null +++ b/assets/rsa.s @@ -0,0 +1,149 @@ +bits 64 +global _start + +_start: + push rbp + push rsp + push rbx + push r12 + push r13 + push r14 + push r15 + + push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + + mov rdi, 1 + lea rsi, [rel msg] + mov rbx, rsi + sub rbx, qword [rel text_section] ;text_section address because of this and that + mov r8, qword [rel section_size] ;text_section size + shr r8, 2 + inc r8 + mov r9, 0 ;increment register + mov r10, 0 ;increment register + xor r10, r10 + xor r13, r13 + mov r13d, dword [rel private_key] + xor r12, r12 + mov r12d, dword [rel private_key + 4] + ;shr r12, 32 + push r13 ; push rsa.d + push r12 ; push rsa.n + jmp decrypt_loop + + ; rbx is adress of text(encrypted) section + ; r8 is section size + ; r9 is index + ; rax is cypher that needs to be converted to message + ; dword [rsp + 16] is rsa.d + ; dword [rsp + 8] is rsa.n + ; qword [rsp] is cypher backup + decrypt_once: + mov r11, 0x100000000 + sq_mul_bit_index: + shr r11, 1 + mov r12, r11 + and r12, qword [rsp + 16] + jz sq_mul_bit_index + sq_mul_loop: + ; check if pow is zero + shr r11, 1 + cmp r11, 0 + je decrypt_loop2 + ; square ... + mul rax, + ; modulo n ... + mov r13, qword [rsp + 8] + xor rdx, rdx + div r13 + mov rax, rdx + ; ... and multiply + mov r12, r11 + and r12, qword [rsp + 16] + cmp r12, 0 + je sq_mul_loop + mov r13, qword [rsp] + mul r13 + ; modulo n ... + mov r13, qword [rsp + 8] + xor rdx, rdx + div r13 + mov rax, rdx + ; end of loop + jmp sq_mul_loop + + decrypt_loop: + cmp r8, r10 + je end_decrypt + xor rax, rax + mov eax, dword [rbx + r9] + push rax + ;push r10 + jmp decrypt_once + decrypt_loop2: + sub rax, 42 ; remove 42 of result (avoid 0 values) + sub rax, r10 ; remove index of result (caesar like cypher so 0/42 values are differents) + ; unpadding and write back here + mov dword [rbx + r9], 0 + mov r15, r10 + shr r15, 5 + shl r15, 2 + mov rcx, r10 + shl rcx, 59 + shr rcx, 59 + inc rcx + shl rax, cl + mov r14, r9 + sub r14, r15 + add [rbx + r14], eax + shr rax, 32 + cmp r9, 0 + je first_block_skip + add [rbx + r14 - 4], eax + + first_block_skip: + ; unpadding and write back here + pop rax + add r9, 4 + inc r10 + jmp decrypt_loop + + end_decrypt: + mov rdx, 14 + mov rax, 1 + syscall + + pop r12 ; pop rsa.n + pop r12 ; pop rsa.d + + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + pop rsp + pop rbp + + jmp 0xdadadada ; this needs to be just before that + msg db "....WOODY....",10 ; that needs to be just after this + text_section dq 0xbabababababababa + section_size dq 0xcacacacacacacaca + private_key dq 0xabcdefabcdefabcd diff --git a/gen_payload.sh b/gen_payload.sh new file mode 100755 index 0000000..cad6810 --- /dev/null +++ b/gen_payload.sh @@ -0,0 +1 @@ +nasm -f elf64 -o payload.o $1 && ld -o a.out payload.o && nasm -f bin -o payload $1 && hexdump -v -e '"\\\x\" 1/1 "%02x"' payload && rm -rf payload.o payload a.out diff --git a/inc/woody.h b/inc/woody.h index 4db4815..cdc5919 100644 --- a/inc/woody.h +++ b/inc/woody.h @@ -2,7 +2,27 @@ # define WOODY_H # include "ft_printf.h" +# include +# include +# include +# include + +# define RET_ERR 1 +# define RET_OK 0 + +typedef struct s_map { + void *data; + off_t size; +} t_map; int woody_woodpacker(char *path); + +void *fetch(t_map map, size_t offset, size_t len); + +int wdy_error(char *str); +int wdy_perror(char *path); + +int check_ident(t_map file); + #endif diff --git a/src/check_ident.c b/src/check_ident.c new file mode 100644 index 0000000..271fa95 --- /dev/null +++ b/src/check_ident.c @@ -0,0 +1,37 @@ +#include "woody.h" + +int ident_error(void) { + return wdy_error("file format not recognized (invalid ELF Ident)"); +} + +int is_valid_elf_magic_number(char *ident) { + return !ft_strncmp(ident, ELFMAG, SELFMAG); +} + +int is_valid_arch(char arch) { + return arch == ELFCLASS64 || arch == ELFCLASS32; +} + +int is_valid_data_format(unsigned char data_format) { + return data_format == ELFDATA2MSB || data_format == ELFDATA2LSB; +} + +int is_valid_version(unsigned char version) { + return version == EV_CURRENT; +} + +int check_ident(t_map file) { + char *ident = (char *)fetch(file, 0, EI_NIDENT); + if (!ident) { + return ident_error(); + } + + if (!is_valid_elf_magic_number(ident) + || !is_valid_arch(ident[EI_CLASS]) + || !is_valid_data_format(ident[EI_DATA]) + || !is_valid_version(ident[EI_VERSION])) { + return ident_error(); + } + + return RET_OK; +} diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..cb434bf --- /dev/null +++ b/src/error.c @@ -0,0 +1,11 @@ +#include "woody.h" + +int wdy_error(char *str) { + ft_printf("error: %s\n", str); + return RET_ERR; +} + +int wdy_perror(char *path) { + perror(path); + return RET_ERR; +} diff --git a/src/fetch.c b/src/fetch.c new file mode 100644 index 0000000..6e50882 --- /dev/null +++ b/src/fetch.c @@ -0,0 +1,8 @@ +#include "woody.h" + +void *fetch(t_map map, size_t offset, size_t len) { + if (offset + len <= (size_t)map.size) { + return map.data + offset; + } + return NULL; +} diff --git a/src/main.c b/src/main.c index 5da71ea..d95d228 100644 --- a/src/main.c +++ b/src/main.c @@ -6,5 +6,5 @@ int main(int ac, char **av) { } else { ft_printf("usage: %s [filename]\n", av[0]); } - return 0; + return RET_OK; } diff --git a/src/woody_woodpacker.c b/src/woody_woodpacker.c index 254d0fa..7de7058 100644 --- a/src/woody_woodpacker.c +++ b/src/woody_woodpacker.c @@ -1,6 +1,184 @@ -#include "ft_printf.h" +#include "woody.h" + +int wdy_get_file(char *path, t_map *file) { + int fd = open(path, O_RDONLY); + if (fd == -1) { + return wdy_perror(path); + } + + file->size = lseek(fd, 0, SEEK_END); + if (file->size == -1) { + close(fd); + return wdy_perror(path); + } + + file->data = mmap( + NULL, + file->size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE, + fd, + 0); + close(fd); + if (file->data == MAP_FAILED) { + return wdy_perror(path); + } + + return RET_OK; +} + +int pack_elf32(t_map file) { + (void)file; + return wdy_error("ELF needs to be in 64 bits format"); +} + +int get_load_segment64( + Elf64_Ehdr elf_header, + Elf64_Phdr *program_headers, + Elf64_Phdr **load_segment) { + for (int i = 0; i < elf_header.e_phnum; i++) { + Elf64_Phdr *p_hdr = &program_headers[i]; + if (p_hdr->p_type == PT_LOAD + && p_hdr->p_flags & PF_X + && p_hdr->p_vaddr <= elf_header.e_entry + && p_hdr->p_vaddr + p_hdr->p_filesz > elf_header.e_entry) { + *load_segment = p_hdr; + return RET_OK; + } + } + return RET_ERR; +} + +// TODO refacto +t_map get_code_cave(t_map file, Elf64_Phdr load_segment) { + size_t page_size = load_segment.p_align; + size_t len = load_segment.p_filesz + page_size - load_segment.p_filesz % page_size; + size_t offset = load_segment.p_offset; + unsigned char *seg = fetch(file, offset, len); + if (!seg) { + printf("fallback to weird code cave finder\n"); + seg = fetch(file, offset, file.size - offset); + len = file.size - offset; + if (!seg) { + ft_printf("unreachable !!!\n"); + } + } + + size_t longest = 0; + size_t longest_i = 0; + for (size_t i = 0; i < len; i++) { + if (seg[i] == 0) { + size_t j = 0; + while (i + j < len && seg[i + j] == 0) { + j++; + } + + if (j > longest) { + longest_i = i; + longest = j; + } + i += j; + + } + } + + t_map code_cave; + code_cave.data = fetch(file, longest_i + offset, longest); + code_cave.size = longest; + if (!code_cave.data) { + printf("unreachable !!!\n"); + } + return code_cave; +} + +int pack_elf64(t_map file) { + Elf64_Ehdr *elf_header = (Elf64_Ehdr *)fetch(file, 0, sizeof(Elf64_Ehdr)); + if (!elf_header) { + return wdy_error("cannot fetch elf header"); + } + + size_t phdrs_len = elf_header->e_phnum * elf_header->e_phentsize; + Elf64_Phdr *program_headers = fetch(file, elf_header->e_phoff, phdrs_len); + if (!program_headers) { + return wdy_error("cannot fetch program headers table"); + } + + Elf64_Phdr *load_segment; + if (get_load_segment64(*elf_header, program_headers, &load_segment) == RET_ERR) { + return wdy_error("cannot get load segment"); + } + + // TODO the whole section below till the end of the function needs + // cleaning and refactoring, better naming also. + + // This should go in a separate function regarding encryption if encryption has been requested. + /* + size_t encryption_block_size = 31; // 32 bits and padding... + t_map to_encrypt; + + to_encrypt.data = fetch(file, load_segment.p_offset, 0); + // size is a multiple of 31... + to_encrypt.size = load_segment.p_filesz + encryption_block_size - load_segment.p_filesz % encryption_block_size; + */ + + t_map code_cave = get_code_cave(file, *load_segment); + + size_t payload_len = 55; + unsigned char *payload = malloc(payload_len * sizeof(unsigned char)); + ft_memcpy(payload, "\x55\x54\x50\x52\x56\x57\xbf\x01\x00\x00\x00\x48\x8d\x35\x17\x00\x00\x00\xba\x0e\x00\x00\x00\xb8\x01\x00\x00\x00\x0f\x05\x5f\x5e\x5a\x58\x5c\x5d\xe9\xb1\xda\xda\xda\x2e\x2e\x2e\x2e\x57\x4f\x4f\x44\x59\x2e\x2e\x2e\x2e\x0a", payload_len); + // todo measure this, probably wc/4 in shell script output + hardcoded + // 55 bytes ^^^ + + // same, probably will hardcode it in functions like "get_debug_payload_info" + // or "get_rsa_payload_info" etc etc... + size_t jump_i = 36; + + // This should fallback to compression algorithm, or smaller payload (eg rsa->xor) + if (payload_len > (size_t)code_cave.size) { + printf("code cave size: %ld (0x%lx) bytes\n", code_cave.size, code_cave.size); + return wdy_error("payload length exceed code cave size"); + } + + // e_entry because relative to this (where we gonna go) + // code cave start because thats the start of the code + // jump_i because thats the index of jump from code cave start + // 5 because jump_i is 5 bytes long operation (1 byte opcode + 4 byte operand) + int jump_value = elf_header->e_entry - (code_cave.data - file.data) - jump_i - 5; + + ft_memcpy(payload + jump_i + 1, &jump_value, 4); + + elf_header->e_entry = code_cave.data - file.data; + //load_segment->p_filesz += payload_len; + load_segment->p_memsz += payload_len; + load_segment->p_flags |= PF_W | PF_R; + ft_memcpy(code_cave.data, payload, payload_len); + + int fd = open("woody", O_WRONLY | O_CREAT, 0755); + if (fd == -1) { + return wdy_perror("woody"); + } + write(fd, file.data, file.size); + + return RET_OK; +} + +int pack_elf(t_map file) { + // at this point arch is known to be either ELFCLASS32 or ELFCLASS64 + unsigned char arch = ((unsigned char *)file.data)[4]; + if (arch == ELFCLASS64) { + return pack_elf64(file); + } + return pack_elf32(file); +} int woody_woodpacker(char *path) { - ft_printf("%s\n", path); - return 0; + t_map file; + + if (wdy_get_file(path, &file) == RET_ERR) { + return RET_ERR; + } + if (check_ident(file) == RET_ERR) { + return RET_ERR; + } + return pack_elf(file); }