commit 93d66e8a25ae8021cd598f03a75575b613452f0a Author: hippoz <10706925-hippoz@users.noreply.gitlab.com> Date: Tue Jul 11 20:49:24 2023 +0300 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b4f3cb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +builddir/ \ No newline at end of file diff --git a/a.out b/a.out new file mode 100644 index 0000000..30df6b5 Binary files /dev/null and b/a.out differ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..e47c9a1 --- /dev/null +++ b/meson.build @@ -0,0 +1,12 @@ +project('layer9', 'c') + +add_global_arguments('-pie', language : 'c') + +executable( + 'layer9', + './src/mach.c', + './src/start.s', + './src/syscall.c', + './src/main.c', + dependencies : [] +) diff --git a/myfile b/myfile new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/myfile @@ -0,0 +1 @@ +hello diff --git a/src/mach.c b/src/mach.c new file mode 100644 index 0000000..43ea62d --- /dev/null +++ b/src/mach.c @@ -0,0 +1,24 @@ +#include "mach.h" + +const char *exec_magic_str(Exec *exec) { + switch (exec->magic) { + case A_MAGIC: return "68020"; + case I_MAGIC: return "intel 386"; + case J_MAGIC: return "intel 960 (retired)"; + case K_MAGIC: return "sparc"; + case V_MAGIC: return "mips 3000 BE"; + case X_MAGIC: return "att dsp 3210 (retired)"; + case M_MAGIC: return "mips 4000 BE"; + case D_MAGIC: return "amd 29000 (retired)"; + case E_MAGIC: return "arm"; + case Q_MAGIC: return "powerpc"; + case N_MAGIC: return "mips 4000 LE"; + case L_MAGIC: return "dec alpha (retired)"; + case P_MAGIC: return "mips 3000 LE"; + case U_MAGIC: return "sparc64"; + case S_MAGIC: return "amd64"; + case T_MAGIC: return "powerpc64"; + case R_MAGIC: return "arm64"; + default: return "[unsupported]"; + } +} diff --git a/src/mach.h b/src/mach.h new file mode 100644 index 0000000..2bbf1d1 --- /dev/null +++ b/src/mach.h @@ -0,0 +1,44 @@ +#ifndef _MACH_H +#define _MACH_H + +#include + +typedef struct Exec { + int32_t magic; /* magic number */ + int32_t text; /* size of text segment */ + int32_t data; /* size of initialized data */ + int32_t bss; /* size of uninitialized data */ + int32_t syms; /* size of symbol table */ + int32_t entry32; /* entry point */ + int32_t spsz; /* size of pc/sp offset table */ + int32_t pcsz; /* size of pc/line number table */ + int64_t entry; /* entry point for HDR_MAGIC */ +} __attribute__((__packed__)) Exec; + +const static int exec_field_count = sizeof(Exec) / sizeof(int32_t); + +#define HDR_MAGIC 0x00008000 /* header expansion */ + +#define _MAGIC(f, b) ((f)|((((4*(b))+0)*(b))+7)) +#define A_MAGIC _MAGIC(0, 8) /* 68020 */ +#define I_MAGIC _MAGIC(0, 11) /* intel 386 */ +#define J_MAGIC _MAGIC(0, 12) /* intel 960 (retired) */ +#define K_MAGIC _MAGIC(0, 13) /* sparc */ +#define V_MAGIC _MAGIC(0, 16) /* mips 3000 BE */ +#define X_MAGIC _MAGIC(0, 17) /* att dsp 3210 (retired) */ +#define M_MAGIC _MAGIC(0, 18) /* mips 4000 BE */ +#define D_MAGIC _MAGIC(0, 19) /* amd 29000 (retired) */ +#define E_MAGIC _MAGIC(0, 20) /* arm */ +#define Q_MAGIC _MAGIC(0, 21) /* powerpc */ +#define N_MAGIC _MAGIC(0, 22) /* mips 4000 LE */ +#define L_MAGIC _MAGIC(0, 23) /* dec alpha (retired) */ +#define P_MAGIC _MAGIC(0, 24) /* mips 3000 LE */ +#define U_MAGIC _MAGIC(0, 25) /* sparc64 */ +#define S_MAGIC _MAGIC(HDR_MAGIC, 26) /* amd64 */ +#define T_MAGIC _MAGIC(HDR_MAGIC, 27) /* powerpc64 */ +#define R_MAGIC _MAGIC(HDR_MAGIC, 28) /* arm64 */ + + +const char *exec_magic_str(Exec *exec); + +#endif \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d5372c4 --- /dev/null +++ b/src/main.c @@ -0,0 +1,152 @@ +#include "syscall.h" +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mach.h" +#include "tos.h" +#include "sys.h" + +#define ALIGN(v, a) (((v) + (a) - 1) & ~((a) - 1)) +#define DONE_ERR(M_err, M_ret) do { fprintf(stderr, (M_err)); defer_status = (M_ret); goto done; } while(0) + +static char sud_intercept = SYSCALL_DISPATCH_FILTER_ALLOW; + + +extern void start(uintptr_t entry, Tos* tos, int argc, char **argv); +void enter(uintptr_t entry, int argc, char **argv) +{ + Tos tos; + tos.pid = getpid(); + printf("jump to entry 0x%"PRIxPTR"\n", entry); + sud_intercept = SYSCALL_DISPATCH_FILTER_BLOCK; + start(entry, &tos, argc, argv); +} + +void handle_sigsys(int sig, siginfo_t *info, void *ptr) +{ + ucontext_t *uc = ptr; + mcontext_t *mc = &uc->uc_mcontext; + greg_t *gregs = mc->gregs; + + int64_t ret = -1; + int64_t *sp = (int64_t *)gregs[REG_RSP]; + int64_t syscall = gregs[REG_RBP]; + + printf("syscall %ld\n", syscall); + + switch (syscall) { + case OPEN: + ret = sys$open((char *)sp[1], (int)sp[2]); + break; + } + + gregs[REG_RAX] = ret; +} + +int findlibc(struct dl_phdr_info *info, size_t size, void *ptr) +{ + const Elf64_Phdr *p; + uintptr_t offset, length, libc; + + libc = (uintptr_t)read; /* arbitrary function used to identify libc text segment */ + for (p = info->dlpi_phdr; p < info->dlpi_phdr + info->dlpi_phnum; ++p) { + if (p->p_type != PT_LOAD || !(p->p_flags & PF_X)) + continue; + offset = info->dlpi_addr + p->p_vaddr; + length = p->p_memsz; + if (offset < libc && libc - offset < length) { + if (prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, offset, length, &sud_intercept) != 0) { + perror("prctl PR_SET_SYSCALL_USER_DISPATCH"); + exit(1); + } + return 1; + } + } + return 0; +} + +static char *program_argv[] = { + "out", + "../myfile", + NULL +}; + +int main() +{ + int defer_status = 0; + void *file_segments = NULL; + FILE *f = NULL; + Exec header = {0}; + size_t text_addr = 0x200000; + size_t data_addr; + + f = fopen("../a.out", "rb"); + + if (fread(&header, 1, sizeof(header), f) != sizeof(header)) { + DONE_ERR("failed to read file header\n", 1); + } + + // header fields are big-endian + int32_t *buf = (int32_t*)&header; + for (int i = 0; i < exec_field_count; i++) { + buf[i] = ntohl(buf[i]); + } + + printf("load %s binary with entry 0x%"PRIxPTR"\n", exec_magic_str(&header), (uintptr_t)header.entry); + + data_addr = ALIGN(text_addr + header.text, 0x200000); + + size_t segments_size = sizeof(Exec) + header.text + header.data; + file_segments = malloc(segments_size); + if (!file_segments) { + defer_status = 1; + goto done; + } + if (fread(file_segments, 1, segments_size, f) != segments_size) { + DONE_ERR("failed to read file header and segments\n", 1); + } + + void *text_segment = mmap((void*)text_addr, header.text, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (!text_segment) { + DONE_ERR("failed to map text segment into memory\n", 1); + } + printf("mapped text segment at 0x%"PRIxPTR"\n", (uintptr_t)text_segment); + + void *data_segment = mmap((void*)data_addr, header.data + header.bss, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (!text_segment) { + DONE_ERR("failed to map data segment into memory\n", 1); + } + printf("mapped data segment at 0x%"PRIxPTR"\n", (uintptr_t)data_segment); + + memcpy(text_segment, file_segments, header.text); + memcpy(data_segment, file_segments + sizeof(Exec) + header.text, header.data); + + if (dl_iterate_phdr(findlibc, NULL) != 1) { + DONE_ERR("failed to find libc and enable SUD\n", 1); + } + + struct sigaction sa = { + .sa_sigaction = handle_sigsys, + .sa_flags = SA_SIGINFO + }; + sigaction(SIGSYS, &sa, NULL); + + enter(header.entry32, 2, program_argv); +done: + free(file_segments); + fclose(f); + return defer_status; +} \ No newline at end of file diff --git a/src/start.s b/src/start.s new file mode 100644 index 0000000..2ec14cf --- /dev/null +++ b/src/start.s @@ -0,0 +1,25 @@ +/* Thanks: https://git.sr.ht/~mcf/nine/tree/main/item/start.s */ + +/* SPDX-License-Identifier: Unlicense */ +.text +.globl start +start: + mov %rdi, %rbp /* entry */ + mov %rsi, %rax /* _tos */ + mov %rdx, %rbx /* argc */ + mov %rcx, %rsi /* argv */ + + /* push argv onto stack */ + mov %rbx, %rcx + add $1, %rcx + sal $3, %rcx + sub %rcx, %rsp + mov %rsp, %rdi + rep movsb + + /* push argc onto stack */ + push %rbx + + jmp *%rbp + +.section .note.GNU-stack,"",@progbits diff --git a/src/sys.h b/src/sys.h new file mode 100644 index 0000000..8b7f97d --- /dev/null +++ b/src/sys.h @@ -0,0 +1,52 @@ +#define SYSR1 0 +#define _ERRSTR 1 +#define BIND 2 +#define CHDIR 3 +#define CLOSE 4 +#define DUP 5 +#define ALARM 6 +#define EXEC 7 +#define EXITS 8 +#define _FSESSION 9 +#define FAUTH 10 +#define _FSTAT 11 +#define SEGBRK 12 +#define _MOUNT 13 +#define OPEN 14 +#define _READ 15 +#define OSEEK 16 +#define SLEEP 17 +#define _STAT 18 +#define RFORK 19 +#define _WRITE 20 +#define PIPE 21 +#define CREATE 22 +#define FD2PATH 23 +#define BRK_ 24 +#define REMOVE 25 +#define _WSTAT 26 +#define _FWSTAT 27 +#define NOTIFY 28 +#define NOTED 29 +#define SEGATTACH 30 +#define SEGDETACH 31 +#define SEGFREE 32 +#define SEGFLUSH 33 +#define RENDEZVOUS 34 +#define UNMOUNT 35 +#define _WAIT 36 +#define SEMACQUIRE 37 +#define SEMRELEASE 38 +#define SEEK 39 +#define FVERSION 40 +#define ERRSTR 41 +#define STAT 42 +#define FSTAT 43 +#define WSTAT 44 +#define FWSTAT 45 +#define MOUNT 46 +#define AWAIT 47 +#define PREAD 50 +#define PWRITE 51 +#define TSEMACQUIRE 52 +#define _NSEC 53 diff --git a/src/syscall.c b/src/syscall.c new file mode 100644 index 0000000..1809b22 --- /dev/null +++ b/src/syscall.c @@ -0,0 +1,28 @@ +#define _GNU_SOURCE +#include +#include +#include +#include "syscall.h" + +static int mode_open(char *name, int mode, int perm, int flag) +{ + switch (mode & 3) { + case 0: flag |= O_RDONLY; break; + case 1: flag |= O_WRONLY; break; + case 2: flag |= O_RDWR; break; + case 3: flag |= O_PATH; break; + } + if (mode & 16) + flag |= O_TRUNC; + if (mode & 32) + flag |= O_CLOEXEC; + return open(name, flag, perm); +} + + +int sys$open(char *path, int mode) +{ + printf("sys$open(0x%"PRIxPTR", %d)\n", (uintptr_t)path, mode); + + return mode_open(path, mode, 0, 0); +} diff --git a/src/syscall.h b/src/syscall.h new file mode 100644 index 0000000..188cc81 --- /dev/null +++ b/src/syscall.h @@ -0,0 +1,3 @@ +#pragma once + +int sys$open(char *path, int mode); diff --git a/src/tos.h b/src/tos.h new file mode 100644 index 0000000..023f2be --- /dev/null +++ b/src/tos.h @@ -0,0 +1,26 @@ +/* Thanks: https://git.sr.ht/~mcf/nine/tree/main/item/tos.h */ + +#include + +typedef struct Tos Tos; +typedef struct Plink Plink; + +struct Tos { + struct /* Per process profiling */ + { + Plink *pp; /* known to be 0(ptr) */ + Plink *next; /* known to be 4(ptr) */ + Plink *last; + Plink *first; + uint32_t pid; + uint32_t what; + } prof; + uint64_t cyclefreq; /* cycle clock frequency if there is one, 0 otherwise */ + int64_t kcycles; /* cycles spent in kernel */ + int64_t pcycles; /* cycles spent in process (kernel + user) */ + uint32_t pid; /* might as well put the pid here */ + uint32_t clock; + /* top of stack is here */ +}; + +extern Tos *_tos;