feat: insert payload ok

This commit is contained in:
gbrochar 2024-08-30 15:10:07 +02:00
parent 7b240b0c11
commit 7d93440bd1
11 changed files with 438 additions and 5 deletions

View File

@ -2,6 +2,9 @@ NAME = woody_woodpacker
SRC_FILE = main.c \ SRC_FILE = main.c \
woody_woodpacker.c \ woody_woodpacker.c \
error.c \
fetch.c \
check_ident.c \
OBJ_FILE = $(SRC_FILE:.c=.o) OBJ_FILE = $(SRC_FILE:.c=.o)

View File

@ -1,3 +1,3 @@
# Woody Woodpacker # 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.

26
assets/debug_mode.s Normal file
View File

@ -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

149
assets/rsa.s Normal file
View File

@ -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

1
gen_payload.sh Executable file
View File

@ -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

View File

@ -2,7 +2,27 @@
# define WOODY_H # define WOODY_H
# include "ft_printf.h" # include "ft_printf.h"
# include <stdio.h>
# include <fcntl.h>
# include <sys/mman.h>
# include <elf.h>
# define RET_ERR 1
# define RET_OK 0
typedef struct s_map {
void *data;
off_t size;
} t_map;
int woody_woodpacker(char *path); 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 #endif

37
src/check_ident.c Normal file
View File

@ -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;
}

11
src/error.c Normal file
View File

@ -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;
}

8
src/fetch.c Normal file
View File

@ -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;
}

View File

@ -6,5 +6,5 @@ int main(int ac, char **av) {
} else { } else {
ft_printf("usage: %s [filename]\n", av[0]); ft_printf("usage: %s [filename]\n", av[0]);
} }
return 0; return RET_OK;
} }

View File

@ -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) { int woody_woodpacker(char *path) {
ft_printf("%s\n", path); t_map file;
return 0;
if (wdy_get_file(path, &file) == RET_ERR) {
return RET_ERR;
}
if (check_ident(file) == RET_ERR) {
return RET_ERR;
}
return pack_elf(file);
} }