feat: insert payload ok
This commit is contained in:
parent
7b240b0c11
commit
7d93440bd1
3
Makefile
3
Makefile
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
20
inc/woody.h
20
inc/woody.h
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue