├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── arch ├── amd64 │ ├── assemble_intel.c │ ├── display_amd64.c │ ├── dump_amd64.c │ ├── elf_amd64.c │ ├── include │ │ ├── arch.h │ │ ├── display.h │ │ ├── dump_state.h │ │ ├── elf_gen.h │ │ └── ptrace_arch.h │ └── ptrace_amd64.c ├── armv7 │ ├── assemble_armv7.c │ ├── display_armv7.c │ ├── dump_armv7.c │ ├── elf_armv7.c │ ├── include │ │ ├── arch.h │ │ ├── display.h │ │ ├── dump_state.h │ │ ├── elf_gen.h │ │ └── ptrace_arch.h │ └── ptrace_armv7.c ├── armv8 │ ├── assemble_armv8.c │ ├── display_armv8.c │ ├── dump_armv8.c │ ├── elf_armv8.c │ ├── include │ │ ├── arch.h │ │ ├── display.h │ │ ├── dump_state.h │ │ ├── elf_gen.h │ │ └── ptrace_arch.h │ └── ptrace_armv8.c └── x86 │ ├── assemble_intel.c │ ├── display_x86.c │ ├── dump_x86.c │ ├── elf_x86.c │ ├── include │ ├── arch.h │ ├── display.h │ ├── dump_state.h │ ├── elf_gen.h │ └── ptrace_arch.h │ └── ptrace_x86.c ├── azure-pipelines.yml ├── common.c ├── exedir.c ├── include ├── assemble.h ├── common.h ├── exedir.h ├── pipe.h ├── printfmt.h ├── ptrace.h └── ui.h ├── pipe.c ├── ptrace.c ├── rappel.c ├── t ├── Makefile ├── mov-amd64.sh ├── mov-armv8.sh └── mov-x86.sh └── ui.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | # gdb 35 | .gdb_history 36 | peda-session-* 37 | 38 | # bin 39 | bin/* 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 yrp. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. All advertising materials mentioning features or use of this software must 14 | display the following acknowledgement: 15 | "¯\_(ツ)_/¯" 16 | 17 | 4. Neither the name of the copyright holder nor the names of its contributors 18 | may be used to endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR 22 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 23 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 24 | EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 30 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ARCH ?= $(shell uname -m) 2 | 3 | ifeq ($(ARCH), i386) 4 | ARCH = x86 5 | else ifeq ($(ARCH), i686) 6 | ARCH = x86 7 | else ifeq ($(ARCH), x86_64) 8 | ARCH = amd64 9 | else ifeq ($(ARCH), armv7l) 10 | ARCH = armv7 11 | else ifeq ($(ARCH), aarch64) 12 | ARCH = armv8 13 | endif 14 | 15 | CFLAGS_ARCH =-Ddisplay=display_$(ARCH) -Dgen_elf=gen_elf_$(ARCH) -Dptrace_reset=ptrace_reset_$(ARCH) -Ddump_state=dump_state_$(ARCH) \ 16 | -Dptrace_reset=ptrace_reset_$(ARCH) -Dptrace_collect_regs=ptrace_collect_regs_$(ARCH) 17 | 18 | CFLAGS_amd64 = -Dassemble=assemble_intel \ 19 | -DREGFMT=REGFMT64 -DARCH_INIT_PROC_INFO=AMD64_INIT_PROC_INFO 20 | CFLAGS_x86 = -Dassemble=assemble_intel \ 21 | -DREGFMT=REGFMT32 -DARCH_INIT_PROC_INFO=X86_INIT_PROC_INFO \ 22 | -m32 23 | CFLAGS_armv7 = -Dassemble=assemble_armv7 \ 24 | -DREGFMT=REGFMT32 -DARCH_INIT_PROC_INFO=ARMV7_INIT_PROC_INFO 25 | CFLAGS_armv8 = -Dassemble=assemble_armv8 \ 26 | -DREGFMT=REGFMT64 -DARCH_INIT_PROC_INFO=ARMV8_INIT_PROC_INFO 27 | 28 | CFLAGS = -std=c11 -Wall -pedantic -Wno-gnu-empty-initializer $(CFLAGS_ARCH) $(CFLAGS_$(ARCH)) -O2 -fPIE -D_FORTIFY_SOURCE=2 29 | LDFLAGS = 30 | INC = -Iinclude/ -Iarch/$(ARCH)/include 31 | LIBS = -ledit 32 | 33 | print-% : ; @echo $* = $($*) 34 | 35 | SRC = rappel.c exedir.c common.c ptrace.c ui.c pipe.c 36 | SRC_ARCH = $(shell find arch/$(ARCH) -name "*.c") 37 | 38 | ALL_SRC = $(SRC) $(SRC_ARCH) 39 | 40 | OBJ = $(patsubst %.c, obj/%.o, $(ALL_SRC)) 41 | 42 | TARGET = bin/rappel 43 | 44 | .PHONY: clean 45 | 46 | all: $(TARGET) 47 | @echo Done. 48 | 49 | debug: CFLAGS += -g 50 | debug: $(TARGET) 51 | 52 | bin: 53 | mkdir -p bin 54 | 55 | $(TARGET): $(OBJ) | bin 56 | $(CC) $(CFLAGS) -o $@ $(OBJ) $(LDFLAGS) $(LIBS) 57 | 58 | obj: 59 | mkdir -p obj 60 | mkdir -p obj/arch/$(ARCH) 61 | 62 | obj/%.o: %.c | obj 63 | $(CC) $(CFLAGS) $(INC) -c $< -o $@ 64 | 65 | .PHONY: test 66 | test: $(TARGET) 67 | $(MAKE) -C t $(ARCH) 68 | 69 | clean: 70 | $(RM) -r ./obj 71 | $(RM) -r ./bin 72 | 73 | uninstall: 74 | $(RM) -rf ~/.rappel 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rappel 2 | 3 | [![Build Status](https://dev.azure.com/yrp604/rappel/_apis/build/status/yrp604.rappel?branchName=master)](https://dev.azure.com/yrp604/rappel/_build/latest?definitionId=1&branchName=master) 4 | 5 | Rappel is a pretty janky assembly REPL. It works by creating a shell ELF, starting it under ptrace, then continiously rewriting/running the `.text` section, while showing the register states. It's maybe half done right now, and supports Linux x86, amd64, armv7 (no thumb), and armv8 at the moment. 6 | 7 | * If you're looking for a Windows version, please see [@zerosum0x0](https://twitter.com/zerosum0x0)'s [WinREPL](https://github.com/zerosum0x0-archive/archive/raw/main/WinREPL-master.zip) (archived) 8 | * If you're looking for a macOS version, please see [@tyilol](https://twitter.com/tyilol)'s [asm_repl](https://github.com/Tyilo/asm_repl) 9 | * If you're looking for a hacked together with gdb and Python version, please see amtal's [rappel.py](https://gist.github.com/amtal/c457176af7f8770e0ad519aadc86013c/) 10 | 11 | ## Install 12 | 13 | The only dependencies are `libedit` and an assembler (`nasm` on x86/amd64, `as` on ARM) , which on Debian can be installed with the `libedit-dev` and `nasm`/`binutils` packages. Please note, as `rappel` requires the ability to write to executable memory via `ptrace`, the program is broken under `PAX_MPROTECT` on grsec kernels (see [#2](https://github.com/yrp604/rappel/issues/2)). 14 | 15 | ``` 16 | $ CC=clang make 17 | ``` 18 | 19 | It should work fine with `gcc`, albeit with a few more warnings. 20 | 21 | By default rappel is compiled with your native architecture. If you're on amd64 and want to target x86 you can do this with 22 | 23 | ``` 24 | $ ARCH=x86 CC=clang make 25 | ``` 26 | 27 | In theory you can also compile an armv7 binary this way, but I really doubt it will work. For rappel to function, the architecture of the main rappel binary must match that of the process it creates, and the host must be able to run binaries of this architecture. 28 | 29 | ## Running 30 | 31 | Rappel has two modes it can operate in. A pipe mode for one off things, a la 32 | 33 | ``` 34 | $ echo "inc eax" | bin/rappel 35 | rax=0000000000000001 rbx=0000000000000000 rcx=0000000000000000 36 | rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000 37 | rip=0000000000400004 rsp=00007ffc73019c20 rbp=0000000000000000 38 | r8=0000000000000000 r9=0000000000000000 r10=0000000000000000 39 | r11=0000000000000000 r12=0000000000000000 r13=0000000000000000 40 | r14=0000000000000000 r15=0000000000000000 41 | [cf:0, zf:0, of:0, sf:0, pf:0, af:0, df:0] 42 | cs=0033 ss=002b ds=0000 es=0000 fs=0000 gs=0000 efl=00000202 43 | $ 44 | ``` 45 | 46 | Or an interactive mode: 47 | 48 | ``` 49 | $ bin/rappel 50 | rax=0000000000000000 rbx=0000000000000000 rcx=0000000000000000 51 | rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000 52 | rip=0000000000400001 rsp=00007ffdedb264a0 rbp=0000000000000000 53 | r8=0000000000000000 r9=0000000000000000 r10=0000000000000000 54 | r11=0000000000000000 r12=0000000000000000 r13=0000000000000000 55 | r14=0000000000000000 r15=0000000000000000 56 | [cf:0, zf:0, of:0, sf:0, pf:0, af:0, df:0] 57 | cs=0033 ss=002b ds=0000 es=0000 fs=0000 gs=0000 efl=00000202 58 | > inc rax 59 | rax=0000000000000001 rbx=0000000000000000 rcx=0000000000000000 60 | rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000 61 | rip=0000000000400004 rsp=00007ffdedb264a0 rbp=0000000000000000 62 | r8=0000000000000000 r9=0000000000000000 r10=0000000000000000 63 | r11=0000000000000000 r12=0000000000000000 r13=0000000000000000 64 | r14=0000000000000000 r15=0000000000000000 65 | [cf:0, zf:0, of:0, sf:0, pf:0, af:0, df:0] 66 | cs=0033 ss=002b ds=0000 es=0000 fs=0000 gs=0000 efl=00000202 67 | > push rax 68 | rax=0000000000000001 rbx=0000000000000000 rcx=0000000000000000 69 | rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000 70 | rip=0000000000400002 rsp=00007ffdedb26498 rbp=0000000000000000 71 | r8=0000000000000000 r9=0000000000000000 r10=0000000000000000 72 | r11=0000000000000000 r12=0000000000000000 r13=0000000000000000 73 | r14=0000000000000000 r15=0000000000000000 74 | [cf:0, zf:0, of:0, sf:0, pf:0, af:0, df:0] 75 | cs=0033 ss=002b ds=0000 es=0000 fs=0000 gs=0000 efl=00000202 76 | > pop rbx 77 | rax=0000000000000001 rbx=0000000000000001 rcx=0000000000000000 78 | rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000 79 | rip=0000000000400002 rsp=00007ffdedb264a0 rbp=0000000000000000 80 | r8=0000000000000000 r9=0000000000000000 r10=0000000000000000 81 | r11=0000000000000000 r12=0000000000000000 r13=0000000000000000 82 | r14=0000000000000000 r15=0000000000000000 83 | [cf:0, zf:0, of:0, sf:0, pf:0, af:0, df:0] 84 | cs=0033 ss=002b ds=0000 es=0000 fs=0000 gs=0000 efl=00000202 85 | > cmp rax, rbx 86 | rax=0000000000000001 rbx=0000000000000001 rcx=0000000000000000 87 | rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000 88 | rip=0000000000400004 rsp=00007ffdedb264a0 rbp=0000000000000000 89 | r8=0000000000000000 r9=0000000000000000 r10=0000000000000000 90 | r11=0000000000000000 r12=0000000000000000 r13=0000000000000000 91 | r14=0000000000000000 r15=0000000000000000 92 | [cf:0, zf:1, of:0, sf:0, pf:1, af:0, df:0] 93 | cs=0033 ss=002b ds=0000 es=0000 fs=0000 gs=0000 efl=00000246 94 | > ^D 95 | $ 96 | ``` 97 | 98 | x86 looks like: 99 | ``` 100 | $ echo "nop" | bin/rappel 101 | eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000 102 | eip=00400002 esp=ffc67240 ebp=00000000 [cf:0, zf:0, of:0, sf:0, pf:0, af:0, df:0] 103 | cs=0023 ss=002b ds=002b es=002b fs=0000 gs=0000 efl=00000202 104 | $ 105 | ``` 106 | 107 | ARMv7 looks like: 108 | ``` 109 | $ echo "nop" | bin/rappel 110 | R0 :0x00000000 R1 :0x00000000 R2 :0x00000000 R3 :0x00000000 111 | R4 :0x00000000 R5 :0x00000000 R6 :0x00000000 R7 :0x00000000 112 | R8 :0x00000000 R9 :0x00000000 R10:0x00000000 113 | FP :0x00000000 IP :0x00000000 114 | SP :0xbe927f30 LR :0x00000000 PC :0x00400004 115 | APSR:0x00000010 116 | $ 117 | ``` 118 | 119 | ARMv8 looks like: 120 | ``` 121 | $ echo "nop" | bin/rappel 122 | X0: 0x0000000000000000 X1: 0x0000000000000000 X2: 0x0000000000000000 X3: 0x0000000000000000 123 | X4: 0x0000000000000000 X5: 0x0000000000000000 X6: 0x0000000000000000 X7: 0x0000000000000000 124 | X8: 0x0000000000000000 X9: 0x0000000000000000 X10: 0x0000000000000000 X11: 0x0000000000000000 125 | X12: 0x0000000000000000 X13: 0x0000000000000000 X14: 0x0000000000000000 X15: 0x0000000000000000 126 | X16: 0x0000000000000000 X17: 0x0000000000000000 X18: 0x0000000000000000 X19: 0x0000000000000000 127 | X20: 0x0000000000000000 X21: 0x0000000000000000 X22: 0x0000000000000000 X23: 0x0000000000000000 128 | X24: 0x0000000000000000 X25: 0x0000000000000000 X26: 0x0000000000000000 X27: 0x0000000000000000 129 | X28: 0x0000000000000000 X29: 0x0000000000000000 X30: 0x0000000000000000 130 | PC: 0x0000000000400004 SP: 0x0000007fedb9be40 PS: 0x0000000000000000 131 | ``` 132 | 133 | ## Notes 134 | Someone asked about xmm registers. If you pass `-x` it will dump out quite a bit of info. 135 | 136 | ``` 137 | GP Regs: 138 | rax=0000000000000000 rbx=0000000000000000 rcx=0000000000000000 139 | rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000 140 | rip=0000000000400001 rsp=00007ffca03d9370 rbp=0000000000000000 141 | r8=0000000000000000 r9=0000000000000000 r10=0000000000000000 142 | r11=0000000000000000 r12=0000000000000000 r13=0000000000000000 143 | r14=0000000000000000 r15=0000000000000000 144 | [cf:0, zf:0, of:0, sf:0, pf:0, af:0, df:0] 145 | cs=0033 ss=002b ds=0000 es=0000 fs=0000 gs=0000 efl=00000202 146 | FP Regs: 147 | rip: 0000000000000000 rdp: 0000000000000000 mxcsr: 00001f80 mxcsr_mask:0000ffff 148 | cwd: 037f swd: 0000 ftw: 0000 fop: 0000 149 | st_space: 150 | 0x00: 00000000 00000000 00000000 00000000 151 | 0x10: 00000000 00000000 00000000 00000000 152 | 0x20: 00000000 00000000 00000000 00000000 153 | 0x30: 00000000 00000000 00000000 00000000 154 | 0x40: 00000000 00000000 00000000 00000000 155 | 0x50: 00000000 00000000 00000000 00000000 156 | 0x60: 00000000 00000000 00000000 00000000 157 | 0x70: 00000000 00000000 00000000 00000000 158 | xmm_space: 159 | 0x00: 00000000 00000000 00000000 00000000 160 | 0x10: 00000000 00000000 00000000 00000000 161 | 0x20: 00000000 00000000 00000000 00000000 162 | 0x30: 00000000 00000000 00000000 00000000 163 | 0x40: 00000000 00000000 00000000 00000000 164 | 0x50: 00000000 00000000 00000000 00000000 165 | 0x60: 00000000 00000000 00000000 00000000 166 | 0x70: 00000000 00000000 00000000 00000000 167 | 0x80: 00000000 00000000 00000000 00000000 168 | 0x90: 00000000 00000000 00000000 00000000 169 | 0xa0: 00000000 00000000 00000000 00000000 170 | 0xb0: 00000000 00000000 00000000 00000000 171 | 0xc0: 00000000 00000000 00000000 00000000 172 | 0xd0: 00000000 00000000 00000000 00000000 173 | 0xe0: 00000000 00000000 00000000 00000000 174 | 0xf0: 00000000 00000000 00000000 00000000 175 | ``` 176 | 177 | There are some other regsets the kernel exports via ptrace(), but they're dependent on kernel version, and I didn't want to try to detect and adjust at runtime. If you want them, you should just need to add the storage in `proc_info_t`, edit `ptrace_collect_regs_()`, then add the display in the relevant `display` function. 178 | 179 | Right now supported platforms are determined by what hardware I own. Adding a new architecture shouldn't be too difficult, as most of the code can be adapted from existing archs. 180 | 181 | ## Docs 182 | 183 | You can get pretty much all the documentation with either `-h` from the command line, or `.help` from the interactive bit. 184 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | * move all arch specific stuff into their own folder tree 2 | * a mode where `rdtsc` is inserted before and after every instruction, and timing info retrieved 3 | * the ability to map arbitrary memory in the child process. I.e. map a file into the address space, or just declare chunks of memory as mapped 4 | * thumb support 5 | * ability to specify the entry point 6 | * move generated binaries to memfd or shm so as to remove filesystem dep 7 | * .showstack command 8 | * split .show into a family of commands? 9 | -------------------------------------------------------------------------------- /arch/amd64/assemble_intel.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include "common.h" 13 | #include "arch.h" 14 | 15 | #include "assemble.h" 16 | 17 | extern struct options_t options; 18 | 19 | static 20 | void _child( 21 | const int out[2], 22 | const int err[2], 23 | int t) 24 | { 25 | char path[PATH_MAX] = {0}; 26 | snprintf(path, sizeof(path), "/dev/fd/%d", t); 27 | 28 | REQUIRE (dup2(out[1], STDOUT_FILENO) != -1); 29 | REQUIRE (close(out[0]) == 0); 30 | REQUIRE (close(out[1]) == 0); 31 | 32 | REQUIRE (dup2(err[1], STDERR_FILENO) != -1); 33 | REQUIRE (close(err[0]) == 0); 34 | REQUIRE (close(err[1]) == 0); 35 | 36 | execlp("nasm", "nasm", "-o", "/proc/self/fd/1", path, (char *)NULL); 37 | perror("execlp"); 38 | exit(EXIT_FAILURE); 39 | } 40 | 41 | const 42 | size_t assemble( 43 | uint8_t *const bytecode, 44 | const size_t bytecode_sz, 45 | const char *const assembly, 46 | const size_t asm_sz) 47 | { 48 | char path[PATH_MAX]; 49 | snprintf(path, sizeof(path), "/tmp/rappel-input.XXXXXX"); 50 | 51 | const int t = mkstemp(path); 52 | 53 | if (t == -1) { 54 | perror("mkstemp"); 55 | exit(EXIT_FAILURE); 56 | } 57 | 58 | REQUIRE (unlink(path) == 0); 59 | 60 | dprintf(t, BITSTR); 61 | dprintf(t, "section .text vstart=%ld\n", options.start); 62 | 63 | write_data(t, (uint8_t *)assembly, asm_sz); 64 | 65 | int out[2] = { -1, -1}; 66 | REQUIRE (pipe(out) == 0); 67 | 68 | int err[2] = { -1, -1}; 69 | REQUIRE (pipe(err) == 0); 70 | 71 | const pid_t asm_pid = fork(); 72 | 73 | if (asm_pid < 0) { 74 | perror("fork"); 75 | exit(EXIT_FAILURE); 76 | } else if (asm_pid == 0) { 77 | _child(out, err, t); 78 | // Not reached 79 | abort(); 80 | } 81 | 82 | REQUIRE (close(out[1]) == 0); 83 | REQUIRE (close(err[1]) == 0); 84 | 85 | verbose_printf("nasm is pid %d\n", asm_pid); 86 | 87 | mem_assign(bytecode, bytecode_sz, TRAP, TRAP_SZ); 88 | 89 | const size_t sz = read_data(out[0], bytecode, bytecode_sz); 90 | 91 | REQUIRE(close(out[0]) == 0); 92 | 93 | if (sz >= bytecode_sz) { 94 | fprintf(stderr, "Too much bytecode to handle, exiting...\n"); 95 | exit(EXIT_FAILURE); 96 | } 97 | 98 | char err_text[0x1000] = { 0 }; 99 | 100 | const size_t err_sz = read_data(err[0], (uint8_t *) err_text, sizeof(err_text)); 101 | 102 | REQUIRE(close(err[0]) == 0); 103 | 104 | int status = -1; 105 | REQUIRE (waitpid(asm_pid, &status, 0) != -1); 106 | 107 | if (WIFSIGNALED(status)) 108 | fprintf(stderr, "nasm exited with signal %d.\n", WTERMSIG(status)); 109 | else if (WIFEXITED(status) && WEXITSTATUS(status)) 110 | fprintf(stderr, "nasm exited %d.\n", WEXITSTATUS(status)); 111 | 112 | REQUIRE (close(t) == 0); 113 | 114 | if (err_sz) { 115 | fprintf(stderr, "%s", err_text); 116 | return 0; 117 | } 118 | 119 | return sz; 120 | } 121 | -------------------------------------------------------------------------------- /arch/amd64/display_amd64.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include "arch.h" 5 | 6 | #include "display.h" 7 | #include "printfmt.h" 8 | 9 | extern struct options_t options; 10 | 11 | void display_amd64( 12 | const struct proc_info_t *const info) 13 | { 14 | const struct user_regs_struct_amd64 *regs = &info->regs_struct; 15 | const struct user_fpregs_struct_amd64 *fpregs = &info->fpregs_struct; 16 | 17 | const struct user_regs_struct_amd64 *old_regs = &info->old_regs_struct; 18 | const struct user_fpregs_struct_amd64 *old_fpregs = &info->old_fpregs_struct; 19 | 20 | if (options.allregs) printf("GP Regs:\n"); 21 | 22 | PRINTREG64("rax=", rax, regs, old_regs, " "); 23 | PRINTREG64("rbx=", rbx, regs, old_regs, " "); 24 | PRINTREG64("rcx=", rcx, regs, old_regs, "\n"); 25 | 26 | PRINTREG64("rdx=", rdx, regs, old_regs, " "); 27 | PRINTREG64("rsi=", rsi, regs, old_regs, " "); 28 | PRINTREG64("rdi=", rdi, regs, old_regs, "\n"); 29 | 30 | PRINTREG64("rip=", rip, regs, old_regs, " "); 31 | PRINTREG64("rsp=", rsp, regs, old_regs, " "); 32 | PRINTREG64("rbp=", rbp, regs, old_regs, "\n"); 33 | 34 | PRINTREG64(" r8=", r8 , regs, old_regs, " "); 35 | PRINTREG64(" r9=", r9 , regs, old_regs, " "); 36 | PRINTREG64("r10=", r10, regs, old_regs, "\n"); 37 | 38 | PRINTREG64("r11=", r11, regs, old_regs, " "); 39 | PRINTREG64("r12=", r12, regs, old_regs, " "); 40 | PRINTREG64("r13=", r13, regs, old_regs, "\n"); 41 | 42 | PRINTREG64("r14=", r14, regs, old_regs, " "); 43 | PRINTREG64("r15=", r15, regs, old_regs, "\n"); 44 | 45 | const uint8_t of = (regs->rflags & 0x800) >> 11; 46 | const uint8_t old_of = (old_regs->rflags & 0x800) >> 11; 47 | 48 | const uint8_t df = (regs->rflags & 0x400) >> 10; 49 | const uint8_t old_df = (old_regs->rflags & 0x400) >> 10; 50 | 51 | const uint8_t sf = (regs->rflags & 0x80) >> 7; 52 | const uint8_t old_sf = (regs->rflags & 0x80) >> 7; 53 | 54 | const uint8_t zf = (regs->rflags & 0x40) >> 6; 55 | const uint8_t old_zf = (old_regs->rflags & 0x40) >> 6; 56 | 57 | const uint8_t af = (regs->rflags & 0x10) >> 4; 58 | const uint8_t old_af = (old_regs->rflags & 0x10) >> 4; 59 | 60 | const uint8_t pf = (regs->rflags & 4) >> 2; 61 | const uint8_t old_pf = (old_regs->rflags & 4) >> 2; 62 | 63 | const uint8_t cf = regs->rflags & 1; 64 | const uint8_t old_cf = old_regs->rflags & 1; 65 | 66 | printf("["); 67 | PRINTBIT("cf=", cf, old_cf, ", "); 68 | PRINTBIT("zf=", zf, old_zf, ", "); 69 | PRINTBIT("of=", of, old_of, ", "); 70 | PRINTBIT("sf=", sf, old_sf, ", "); 71 | PRINTBIT("pf=", pf, old_pf, ", "); 72 | PRINTBIT("af=", af, old_af, ", "); 73 | PRINTBIT("df=", df, old_df, ""); 74 | printf("]\n"); 75 | 76 | PRINTREG16("cs=", cs, regs, old_regs, " "); 77 | PRINTREG16("ss=", ss, regs, old_regs, " "); 78 | PRINTREG16("ds=", ds, regs, old_regs, " "); 79 | 80 | PRINTREG16("es=", es, regs, old_regs, " "); 81 | PRINTREG16("fs=", fs, regs, old_regs, " "); 82 | PRINTREG16("gs=", gs, regs, old_regs, " "); 83 | PRINTREG32("efl=", rflags, regs, old_regs, "\n"); 84 | 85 | if (options.allregs) { 86 | printf("FP Regs:\n"); 87 | PRINTREG64("rip=", rip, fpregs, old_fpregs, "\t"); 88 | PRINTREG64("rdp=", rdp, fpregs, old_fpregs, "\t"); 89 | PRINTREG32("mxcsr=", mxcsr, fpregs, old_fpregs, "\t"); 90 | PRINTREG32("mxcsr_mask=", mxcr_mask, fpregs, old_fpregs, "\n"); 91 | 92 | PRINTREG16("cwd=", cwd, fpregs, old_fpregs, "\t"); 93 | PRINTREG16("swd=", swd, fpregs, old_fpregs, "\t"); 94 | PRINTREG16("ftw=", ftw, fpregs, old_fpregs, "\t"); 95 | PRINTREG16("fop=", fop, fpregs, old_fpregs, "\n"); 96 | 97 | printf("st_space:\n"); 98 | for (uint32_t i = 0; i < 32/4; ++i) { 99 | printf("0x%02x:\t", i * 0x10); 100 | for (uint32_t j = i*4; j < i*4 + 4; ++j) { 101 | DUMPREG32(st_space[j], fpregs, old_fpregs); 102 | printf("\t"); 103 | } 104 | printf("\n"); 105 | } 106 | 107 | printf("xmm_space:\n"); 108 | for (uint32_t i = 0; i < 64/4; ++i) { 109 | printf("0x%02x:\t", i * 0x10); 110 | for (uint32_t j = i*4; j < i*4 + 4; ++j) { 111 | DUMPREG32(xmm_space[j], fpregs, old_fpregs); 112 | printf("\t"); 113 | } 114 | printf("\n"); 115 | } 116 | } 117 | // 118 | // 5 is sigtrap, which is expected, -1 is initial value 119 | if (info->sig != 5 && info->sig != -1) { 120 | printf("Process died with signal=%d\n", info->sig); 121 | printf("Exited=%ld\n", info->exit_code); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /arch/amd64/dump_amd64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | #include "arch.h" 6 | 7 | void dump_state_amd64( 8 | const struct proc_info_t *const info) 9 | { 10 | const struct user_regs_struct_amd64 *regs = &info->regs_struct; 11 | const struct user_fpregs_struct_amd64 *fpregs = &info->fpregs_struct; 12 | 13 | write_data(STDOUT_FILENO, (uint8_t *)regs, sizeof(struct user_regs_struct_amd64)); 14 | write_data(STDOUT_FILENO, (uint8_t *)fpregs, sizeof(struct user_fpregs_struct_amd64)); 15 | } 16 | -------------------------------------------------------------------------------- /arch/amd64/elf_amd64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "common.h" 8 | #include "arch.h" 9 | 10 | #include "elf_gen.h" 11 | 12 | const size_t gen_elf_amd64( 13 | uint8_t **out, 14 | const unsigned long start, 15 | const uint8_t *const code, 16 | const size_t code_sz) 17 | { 18 | /* We give the elf header and phdr an entire page, because the elf loader can 19 | * only map the file at PAGE_SIZE offsets. So our file will look like this 20 | * for an invocation with some code and 2 data segments. 21 | * 22 | * +----------+ 23 | * | 1st page | 24 | * | ehdr | 25 | * | phdr | 26 | * | shdr | 27 | * | shdr | 28 | * |----------| 29 | * | 2nd page | 30 | * | code | 31 | * |----------| 32 | * | 3rd page | 33 | * | data 1 | 34 | * |----------| 35 | * | 4th page | 36 | * | data 2 | 37 | * +----------+ 38 | * 39 | * TODO add data section, section headers 40 | */ 41 | 42 | const size_t pg_align_dist = start - (start & ~0xffff); 43 | const size_t pad_sz = ((code_sz + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) - code_sz; 44 | const size_t sz = PAGE_SIZE + pg_align_dist + code_sz + pad_sz; 45 | 46 | uint8_t *const e = xmalloc(sz); 47 | mem_assign(e, sz, TRAP, TRAP_SZ); 48 | 49 | Elf64_Ehdr *const ehdr = (Elf64_Ehdr *) e; 50 | 51 | ehdr->e_ident[0] = ELFMAG0; 52 | ehdr->e_ident[1] = ELFMAG1; 53 | ehdr->e_ident[2] = ELFMAG2; 54 | ehdr->e_ident[3] = ELFMAG3; 55 | ehdr->e_ident[4] = ELFCLASS64; 56 | ehdr->e_ident[5] = ELFDATA2LSB; 57 | ehdr->e_ident[6] = EV_CURRENT; 58 | ehdr->e_ident[7] = ELFOSABI_NONE; 59 | ehdr->e_ident[9] = 0; 60 | // Padding 61 | ehdr->e_type = ET_EXEC; 62 | ehdr->e_machine = EM_X86_64; 63 | ehdr->e_version = EV_CURRENT; 64 | ehdr->e_entry = start; 65 | ehdr->e_phoff = sizeof(Elf64_Ehdr); 66 | ehdr->e_shoff = 0; // XXX 67 | ehdr->e_flags = 0; 68 | ehdr->e_ehsize = sizeof(Elf64_Ehdr); 69 | ehdr->e_phentsize = sizeof(Elf64_Phdr); 70 | ehdr->e_phnum = 1; 71 | ehdr->e_shentsize = 0; 72 | ehdr->e_shnum = 0; 73 | ehdr->e_shstrndx = 0; 74 | 75 | Elf64_Phdr *const phdr = (Elf64_Phdr *) ((uint8_t *) e + sizeof(Elf64_Ehdr)); 76 | 77 | phdr->p_type = PT_LOAD; 78 | phdr->p_flags = PF_X | PF_R; 79 | phdr->p_offset = PAGE_SIZE; 80 | phdr->p_vaddr = start - pg_align_dist; 81 | phdr->p_paddr = 0; 82 | phdr->p_filesz = code_sz + pg_align_dist; 83 | phdr->p_memsz = code_sz + pg_align_dist; 84 | phdr->p_align = 0x4; 85 | 86 | uint8_t *const data = (uint8_t *) e + PAGE_SIZE + pg_align_dist; 87 | memcpy(data, code, code_sz); 88 | 89 | *out = e; 90 | 91 | return sz; 92 | } 93 | -------------------------------------------------------------------------------- /arch/amd64/include/arch.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TRAP 0xcc // int3 4 | #define TRAP_SZ 1 5 | 6 | #define BITSTR "[bits 64]\n" 7 | 8 | #define AMD64_INIT_PROC_INFO(i) \ 9 | do {\ 10 | (i).regs = (struct iovec) { .iov_base = &(i).regs_struct, .iov_len = sizeof((i).regs_struct) }; \ 11 | (i).fpregs = (struct iovec) { .iov_base = &(i).fpregs_struct, .iov_len = sizeof((i).fpregs_struct) }; \ 12 | } while (0) 13 | 14 | struct user_fpregs_struct_amd64 15 | { 16 | unsigned short int cwd; 17 | unsigned short int swd; 18 | unsigned short int ftw; 19 | unsigned short int fop; 20 | unsigned long int rip; 21 | unsigned long int rdp; 22 | unsigned int mxcsr; 23 | unsigned int mxcr_mask; 24 | unsigned int st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ 25 | unsigned int xmm_space[64]; /* 16*16 bytes for each XMM-reg = 256 bytes */ 26 | unsigned int padding[24]; 27 | }; 28 | 29 | struct user_regs_struct_amd64 30 | { 31 | unsigned long int r15; 32 | unsigned long int r14; 33 | unsigned long int r13; 34 | unsigned long int r12; 35 | unsigned long int rbp; 36 | unsigned long int rbx; 37 | unsigned long int r11; 38 | unsigned long int r10; 39 | unsigned long int r9; 40 | unsigned long int r8; 41 | unsigned long int rax; 42 | unsigned long int rcx; 43 | unsigned long int rdx; 44 | unsigned long int rsi; 45 | unsigned long int rdi; 46 | unsigned long int orig_rax; 47 | unsigned long int rip; 48 | unsigned long int cs; 49 | unsigned long int rflags; 50 | unsigned long int rsp; 51 | unsigned long int ss; 52 | unsigned long int fs_base; 53 | unsigned long int gs_base; 54 | unsigned long int ds; 55 | unsigned long int es; 56 | unsigned long int fs; 57 | unsigned long int gs; 58 | }; 59 | 60 | struct proc_info_t { 61 | pid_t pid; 62 | 63 | struct user_regs_struct_amd64 regs_struct; 64 | struct user_regs_struct_amd64 old_regs_struct; 65 | struct iovec regs; 66 | 67 | struct user_fpregs_struct_amd64 fpregs_struct; 68 | struct user_fpregs_struct_amd64 old_fpregs_struct; 69 | struct iovec fpregs; 70 | 71 | int sig; 72 | long exit_code; 73 | }; 74 | 75 | #define PAGE_SHIFT 12 76 | #define PAGE_SIZE (1UL << PAGE_SHIFT) 77 | -------------------------------------------------------------------------------- /arch/amd64/include/display.h: -------------------------------------------------------------------------------- 1 | void display_amd64( 2 | const struct proc_info_t *const); 3 | -------------------------------------------------------------------------------- /arch/amd64/include/dump_state.h: -------------------------------------------------------------------------------- 1 | void dump_state_amd64( 2 | const struct proc_info_t *const); 3 | -------------------------------------------------------------------------------- /arch/amd64/include/elf_gen.h: -------------------------------------------------------------------------------- 1 | const 2 | size_t gen_elf_amd64( 3 | uint8_t **, 4 | const unsigned long, 5 | const uint8_t *const, 6 | const size_t); 7 | -------------------------------------------------------------------------------- /arch/amd64/include/ptrace_arch.h: -------------------------------------------------------------------------------- 1 | void ptrace_collect_regs_amd64( 2 | const pid_t, 3 | struct proc_info_t *const); 4 | 5 | void ptrace_reset_amd64( 6 | const pid_t, 7 | const unsigned long); 8 | -------------------------------------------------------------------------------- /arch/amd64/ptrace_amd64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | #include "arch.h" 6 | 7 | void ptrace_collect_regs_amd64( 8 | const pid_t child_pid, 9 | struct proc_info_t *const info) 10 | { 11 | info->pid = child_pid; 12 | 13 | info->old_regs_struct = info->regs_struct; 14 | 15 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &info->regs) == 0); 16 | 17 | info->old_fpregs_struct = info->fpregs_struct; 18 | 19 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRFPREG, &info->fpregs) == 0); 20 | 21 | info->sig = -1; 22 | info->exit_code = -1; 23 | } 24 | 25 | void ptrace_reset_amd64( 26 | const pid_t child_pid, 27 | const unsigned long start) 28 | { 29 | struct user_regs_struct_amd64 regs_struct = {0}; 30 | struct iovec regs = {.iov_base = ®s_struct, .iov_len = sizeof(regs_struct) }; 31 | 32 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, ®s) == 0); 33 | 34 | regs_struct.rip = start; 35 | 36 | REQUIRE (ptrace(PTRACE_SETREGSET, child_pid, NT_PRSTATUS, ®s) == 0); 37 | } 38 | -------------------------------------------------------------------------------- /arch/armv7/assemble_armv7.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include "common.h" 13 | #include "arch.h" 14 | 15 | #include "assemble.h" 16 | 17 | extern struct options_t options; 18 | 19 | static 20 | void _child_assemble( 21 | const int fildes[2], 22 | int t) 23 | { 24 | char path[PATH_MAX] = {0}; 25 | snprintf(path, sizeof(path), "/dev/fd/%d", t); 26 | 27 | REQUIRE (dup2(fildes[0], STDIN_FILENO) != -1); 28 | 29 | REQUIRE (close(fildes[0]) == 0); 30 | REQUIRE (close(fildes[1]) == 0); 31 | 32 | execlp("as", "as", "-mfpu=neon", "-o", path, "-", (char *)NULL); 33 | perror("execlp"); 34 | exit(EXIT_FAILURE); 35 | } 36 | 37 | static 38 | void _child_objcopy( 39 | const int filedes[2], 40 | int t) 41 | { 42 | char path[PATH_MAX] = {0}; 43 | snprintf(path, sizeof(path), "/dev/fd/%d", t); 44 | 45 | REQUIRE (dup2(filedes[1], STDOUT_FILENO) != -1); 46 | 47 | REQUIRE (close(filedes[0]) == 0); 48 | REQUIRE (close(filedes[1]) == 0); 49 | 50 | execlp("objcopy", "objcopy", "-O", "binary", path, "/dev/stdout", (char *)NULL); 51 | perror("execlp"); 52 | exit(EXIT_FAILURE); 53 | } 54 | 55 | const 56 | size_t assemble_armv7( 57 | uint8_t *const bytecode, 58 | const size_t bytecode_sz, 59 | const char *const assembly, 60 | const size_t asm_sz) 61 | { 62 | size_t sz = 0; 63 | 64 | char path[PATH_MAX]; 65 | snprintf(path, sizeof(path), "/tmp/rappel-output.XXXXXX"); 66 | 67 | const int t = mkstemp(path); 68 | 69 | if (t == -1) { 70 | perror("mkstemp"); 71 | exit(EXIT_FAILURE); 72 | } 73 | 74 | REQUIRE (unlink(path) == 0); 75 | 76 | int fildes[2] = {-1, -1}; 77 | REQUIRE (pipe(fildes) == 0); 78 | 79 | const pid_t asm_pid = fork(); 80 | 81 | if (asm_pid < 0) { 82 | perror("fork"); 83 | exit(EXIT_FAILURE); 84 | } else if (asm_pid == 0) { 85 | _child_assemble(fildes, t); 86 | // Not reached 87 | abort(); 88 | } 89 | 90 | verbose_printf("as is pid %d\n", asm_pid); 91 | 92 | REQUIRE (close(fildes[0]) == 0); 93 | 94 | write_data(fildes[1], (uint8_t *)assembly, asm_sz); 95 | 96 | REQUIRE (close(fildes[1]) == 0); 97 | 98 | int asm_status = -1; 99 | REQUIRE (waitpid(asm_pid, &asm_status, 0) != -1); 100 | 101 | if (WIFSIGNALED(asm_status)) { 102 | fprintf(stderr, "as exited with signal %d.\n", WTERMSIG(asm_status)); 103 | } else if (WIFEXITED(asm_status) && WEXITSTATUS(asm_status)) { 104 | fprintf(stderr, "as exited %d.\n", WEXITSTATUS(asm_status)); 105 | goto exit; 106 | } 107 | 108 | REQUIRE (lseek(t, SEEK_SET, 0) != -1); 109 | 110 | int results[2] = {-1, -1}; 111 | REQUIRE (pipe(results) == 0); 112 | 113 | const pid_t objcopy_pid = fork(); 114 | 115 | if (objcopy_pid < 0) { 116 | perror("fork"); 117 | exit(EXIT_FAILURE); 118 | } else if (objcopy_pid == 0) { 119 | _child_objcopy(results, t); 120 | // Not reached 121 | abort(); 122 | } 123 | 124 | verbose_printf("objcopy is pid %d\n", objcopy_pid); 125 | 126 | REQUIRE (close(results[1]) == 0); 127 | 128 | mem_assign(bytecode, bytecode_sz, TRAP, TRAP_SZ); 129 | 130 | sz = read_data(results[0], bytecode, bytecode_sz); 131 | 132 | REQUIRE (close(results[0]) == 0); 133 | 134 | if (sz >= bytecode_sz) { 135 | fprintf(stderr, "Too much bytecode to handle, exiting...\n"); 136 | exit(EXIT_FAILURE); 137 | } 138 | 139 | int objcopy_status = -1; 140 | REQUIRE (waitpid(objcopy_pid, &objcopy_status, 0) != -1); 141 | 142 | if (WIFEXITED(objcopy_status) && WIFSIGNALED(objcopy_status)) 143 | fprintf(stderr, "objcopy exited with signal %d.\n", WTERMSIG(objcopy_status)); 144 | else if (WIFEXITED(objcopy_status) && WEXITSTATUS(objcopy_status)) 145 | fprintf(stderr, "objcopy exited %d.\n", WEXITSTATUS(objcopy_status)); 146 | 147 | exit: 148 | REQUIRE (close(t) == 0); 149 | 150 | return sz; 151 | } 152 | -------------------------------------------------------------------------------- /arch/armv7/display_armv7.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include "arch.h" 5 | 6 | #include "display.h" 7 | #include "printfmt.h" 8 | 9 | extern struct options_t options; 10 | 11 | void display_armv7( 12 | const struct proc_info_t *const info) 13 | { 14 | const struct user_regs_armv7 *regs = &info->regs_struct; 15 | const struct user_fpregs_armv7 *fpregs = &info->fpregs_struct; 16 | const struct user_vfpregs_armv7 *vfpregs = &info->vfpregs_struct; 17 | 18 | const struct user_regs_armv7 *old_regs = &info->old_regs_struct; 19 | const struct user_fpregs_armv7 *old_fpregs = &info->old_fpregs_struct; 20 | const struct user_vfpregs_armv7 *old_vfpregs = &info->old_vfpregs_struct; 21 | 22 | 23 | if (options.allregs) printf("GP Regs:\n"); 24 | 25 | PRINTREG32("R0: ", uregs[0], regs, old_regs, "\t"); 26 | PRINTREG32("R1: ", uregs[1], regs, old_regs, "\t"); 27 | PRINTREG32("R2: ", uregs[2], regs, old_regs, "\t"); 28 | PRINTREG32("R3: ", uregs[3], regs, old_regs, "\n"); 29 | 30 | PRINTREG32("R4: ", uregs[4], regs, old_regs, "\t"); 31 | PRINTREG32("R5: ", uregs[5], regs, old_regs, "\t"); 32 | PRINTREG32("R6: ", uregs[6], regs, old_regs, "\t"); 33 | PRINTREG32("R7: ", uregs[7], regs, old_regs, "\n"); 34 | 35 | PRINTREG32("R8: ", uregs[8], regs, old_regs, "\t"); 36 | PRINTREG32("R9: ", uregs[9], regs, old_regs, "\t"); 37 | PRINTREG32("R10: ", uregs[10], regs, old_regs, "\n"); 38 | 39 | PRINTREG32("FP: ", uregs[11], regs, old_regs, "\t"); 40 | PRINTREG32("IP: ", uregs[12], regs, old_regs, "\n"); 41 | 42 | PRINTREG32("PC: ", uregs[15], regs, old_regs, "\t"); 43 | PRINTREG32("SP: ", uregs[13], regs, old_regs, "\t"); 44 | PRINTREG32("LR: ", uregs[14], regs, old_regs, "\n"); 45 | PRINTREG32("APSR:", uregs[16], regs, old_regs, "\n"); 46 | 47 | if (options.allregs) { 48 | printf("FP Regs:\n"); 49 | PRINTREG32("fpsr: ", fpsr, fpregs, old_fpregs, "\t"); 50 | PRINTREG32("fpcr: ", fpcr, fpregs, old_fpregs, "\t"); 51 | PRINTREG32("init_flag: ", init_flag, fpregs, old_fpregs, "\n"); 52 | 53 | printf("ftype:"); 54 | for (uint32_t i = 0; i < 8; ++i) { 55 | PRINTREG8("", ftype[i], fpregs, old_fpregs, " "); 56 | } 57 | printf("\n"); 58 | 59 | for (uint32_t i = 0; i < 8; ++i) { 60 | printf("fpreg %u: ", i); 61 | PRINTBIT("sign1 ", fpregs->fpregs[i].sign1, old_fpregs->fpregs[i].sign1, " "); 62 | PRINTBIT("sign2 ", fpregs->fpregs[i].sign2, old_fpregs->fpregs[i].sign2, " "); 63 | PRINTREG16("exponent ", fpregs[i].exponent, fpregs, old_fpregs, " "); 64 | PRINTREG16("mantissa1 ", fpregs[i].mantissa1, fpregs, old_fpregs, " "); 65 | PRINTREG16("mantissa0 ", fpregs[i].mantissa0, fpregs, old_fpregs, "\n"); 66 | } 67 | 68 | printf("VFP Regs:\n"); 69 | for (uint32_t i = 0; i < 32 / 2; ++i) { 70 | printf("0x%02x:\t", i * 0x10); 71 | for (uint32_t j = i*2; j < i*2 + 2; ++j) { 72 | DUMPREG64(vfpregs[j], vfpregs, old_vfpregs); 73 | printf("\t"); 74 | } 75 | printf("\n"); 76 | } 77 | } 78 | 79 | // 5 is sigtrap, which is expected, -1 is initial value 80 | if (info->sig != 5 && info->sig != -1) { 81 | printf("Process died with signal: %d\n", info->sig); 82 | printf("Exited: %ld\n", info->exit_code); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /arch/armv7/dump_armv7.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | #include "arch.h" 6 | 7 | void dump_state_armv7( 8 | const struct proc_info_t *const info) 9 | { 10 | const struct user_regs_armv7 *regs = &info->regs_struct; 11 | const struct user_fpregs_armv7 *fpregs = &info->fpregs_struct; 12 | 13 | write_data(STDOUT_FILENO, (uint8_t *)regs, sizeof(struct user_regs_armv7)); 14 | write_data(STDOUT_FILENO, (uint8_t *)fpregs, sizeof(struct user_fpregs_armv7)); 15 | } 16 | -------------------------------------------------------------------------------- /arch/armv7/elf_armv7.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common.h" 6 | #include "arch.h" 7 | 8 | #include "elf_gen.h" 9 | 10 | const size_t gen_elf_armv7( 11 | uint8_t **out, 12 | const unsigned long start, 13 | const uint8_t *const code, 14 | const size_t code_sz) 15 | { 16 | /* We give the elf header and phdr an entire page, because the elf loader can 17 | * only map the file at PAGE_SIZE offsets. So our file will look like this 18 | * for an invocation with some code and 2 data segments. 19 | * 20 | * +----------+ 21 | * | 1st page | 22 | * | ehdr | 23 | * | phdr | 24 | * | shdr | 25 | * | shdr | 26 | * |----------| 27 | * | 2nd page | 28 | * | code | 29 | * |----------| 30 | * | 3rd page | 31 | * | data 1 | 32 | * |----------| 33 | * | 4th page | 34 | * | data 2 | 35 | * +----------+ 36 | * 37 | * TODO add data section, section headers 38 | */ 39 | 40 | const size_t pg_align_dist = start - (start & ~0xffff); 41 | const size_t pad_sz = ((code_sz + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) - code_sz; 42 | const size_t sz = PAGE_SIZE + pg_align_dist + code_sz + pad_sz; 43 | 44 | uint8_t *const e = xmalloc(sz); 45 | mem_assign(e, sz, TRAP, TRAP_SZ); 46 | 47 | Elf32_Ehdr *const ehdr = (Elf32_Ehdr *) e; 48 | 49 | ehdr->e_ident[0] = ELFMAG0; 50 | ehdr->e_ident[1] = ELFMAG1; 51 | ehdr->e_ident[2] = ELFMAG2; 52 | ehdr->e_ident[3] = ELFMAG3; 53 | ehdr->e_ident[4] = ELFCLASS32; 54 | ehdr->e_ident[5] = ELFDATA2LSB; 55 | ehdr->e_ident[6] = EV_CURRENT; 56 | ehdr->e_ident[7] = ELFOSABI_NONE; 57 | ehdr->e_ident[9] = 0; 58 | // Padding 59 | ehdr->e_type = ET_EXEC; 60 | ehdr->e_machine = EM_ARM; 61 | ehdr->e_version = EV_CURRENT; 62 | ehdr->e_entry = start; 63 | ehdr->e_phoff = sizeof(Elf32_Ehdr); 64 | ehdr->e_shoff = 0; // XXX 65 | ehdr->e_flags = 0; 66 | ehdr->e_ehsize = sizeof(Elf32_Ehdr); 67 | ehdr->e_phentsize = sizeof(Elf32_Phdr); 68 | ehdr->e_phnum = 1; 69 | ehdr->e_shentsize = 0; 70 | ehdr->e_shnum = 0; 71 | ehdr->e_shstrndx = 0; 72 | 73 | Elf32_Phdr *const phdr = (Elf32_Phdr *) ((uint8_t *) e + sizeof(Elf32_Ehdr)); 74 | 75 | phdr->p_type = PT_LOAD; 76 | phdr->p_flags = PF_X | PF_R; 77 | phdr->p_offset = PAGE_SIZE; 78 | phdr->p_vaddr = start - pg_align_dist; 79 | phdr->p_paddr = 0; 80 | phdr->p_filesz = code_sz + pg_align_dist; 81 | phdr->p_memsz = code_sz + pg_align_dist; 82 | phdr->p_align = 0x4; 83 | 84 | uint8_t *const data = (uint8_t *) e + PAGE_SIZE + pg_align_dist; 85 | memcpy(data, code, code_sz); 86 | 87 | *out = e; 88 | 89 | return sz; 90 | } 91 | -------------------------------------------------------------------------------- /arch/armv7/include/arch.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TRAP 0xe1200070 4 | #define TRAP_SZ 4 5 | 6 | #define ARMV7_INIT_PROC_INFO(i) \ 7 | do {\ 8 | (i).regs = (struct iovec) { .iov_base = &(i).regs_struct, .iov_len = sizeof((i).regs_struct) }; \ 9 | (i).fpregs = (struct iovec) { .iov_base = &(i).fpregs_struct, .iov_len = sizeof((i).fpregs_struct) }; \ 10 | (i).vfpregs = (struct iovec) { .iov_base = &(i).vfpregs_struct, .iov_len = sizeof((i).vfpregs_struct) }; \ 11 | } while (0) 12 | 13 | struct user_regs_armv7 14 | { 15 | unsigned long int uregs[18]; 16 | }; 17 | 18 | struct user_fpregs_armv7 19 | { 20 | struct fp_reg 21 | { 22 | unsigned int sign1:1; 23 | unsigned int unused:15; 24 | unsigned int sign2:1; 25 | unsigned int exponent:14; 26 | unsigned int j:1; 27 | unsigned int mantissa1:31; 28 | unsigned int mantissa0:32; 29 | } fpregs[8]; 30 | unsigned int fpsr:32; 31 | unsigned int fpcr:32; 32 | unsigned char ftype[8]; 33 | unsigned int init_flag; 34 | }; 35 | 36 | // As far as I can tell, this isn't exported from the kernel in any userland 37 | // include. This is basically how its defined in arch/arm/include/asm/fpstate.h 38 | // as 'struct vfp_hard_struct' 39 | struct user_vfpregs_armv7 40 | { 41 | uint64_t vfpregs[32]; 42 | }; 43 | 44 | struct proc_info_t { 45 | pid_t pid; 46 | 47 | struct user_regs_armv7 regs_struct; 48 | struct user_regs_armv7 old_regs_struct; 49 | struct iovec regs; 50 | 51 | struct user_fpregs_armv7 fpregs_struct; 52 | struct user_fpregs_armv7 old_fpregs_struct; 53 | struct iovec fpregs; 54 | 55 | struct user_vfpregs_armv7 vfpregs_struct; 56 | struct user_vfpregs_armv7 old_vfpregs_struct; 57 | struct iovec vfpregs; 58 | 59 | int sig; 60 | long exit_code; 61 | }; 62 | 63 | #define PAGE_SHIFT 12 64 | #define PAGE_SIZE (1UL << PAGE_SHIFT) 65 | -------------------------------------------------------------------------------- /arch/armv7/include/display.h: -------------------------------------------------------------------------------- 1 | void display_armv7( 2 | const struct proc_info_t *const); 3 | -------------------------------------------------------------------------------- /arch/armv7/include/dump_state.h: -------------------------------------------------------------------------------- 1 | void dump_state_armv7( 2 | const struct proc_info_t *const); 3 | -------------------------------------------------------------------------------- /arch/armv7/include/elf_gen.h: -------------------------------------------------------------------------------- 1 | const 2 | size_t gen_elf_armv7( 3 | uint8_t **, 4 | const unsigned long, 5 | const uint8_t *const, 6 | const size_t); 7 | -------------------------------------------------------------------------------- /arch/armv7/include/ptrace_arch.h: -------------------------------------------------------------------------------- 1 | void ptrace_collect_regs_armv7( 2 | const pid_t, 3 | struct proc_info_t *const); 4 | 5 | void ptrace_reset_armv7( 6 | const pid_t, 7 | const unsigned long); 8 | -------------------------------------------------------------------------------- /arch/armv7/ptrace_armv7.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | #include "arch.h" 6 | 7 | void ptrace_collect_regs_armv7( 8 | const pid_t child_pid, 9 | struct proc_info_t *const info) 10 | { 11 | info->pid = child_pid; 12 | 13 | info->old_regs_struct = info->regs_struct; 14 | 15 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &info->regs) == 0); 16 | 17 | info->old_fpregs_struct = info->fpregs_struct; 18 | 19 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRFPREG, &info->fpregs) == 0); 20 | 21 | info->old_vfpregs_struct = info->vfpregs_struct; 22 | 23 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_ARM_VFP, &info->vfpregs) == 0); 24 | 25 | info->sig = -1; 26 | info->exit_code = -1; 27 | } 28 | 29 | void ptrace_reset_armv7( 30 | const pid_t child_pid, 31 | const unsigned long start) 32 | { 33 | struct user_regs_armv7 regs_struct = {}; 34 | struct iovec regs = {.iov_base = ®s_struct, .iov_len = sizeof(regs_struct) }; 35 | 36 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, ®s) == 0); 37 | 38 | regs_struct.uregs[15] = start; 39 | 40 | REQUIRE (ptrace(PTRACE_SETREGSET, child_pid, NT_PRSTATUS, ®s) == 0); 41 | } 42 | -------------------------------------------------------------------------------- /arch/armv8/assemble_armv8.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include "common.h" 13 | #include "arch.h" 14 | 15 | #include "assemble.h" 16 | 17 | extern struct options_t options; 18 | 19 | static 20 | void _child_assemble( 21 | const int fildes[2], 22 | int t) 23 | { 24 | char path[PATH_MAX] = {0}; 25 | snprintf(path, sizeof(path), "/dev/fd/%d", t); 26 | 27 | REQUIRE (dup2(fildes[0], STDIN_FILENO) != -1); 28 | 29 | REQUIRE (close(fildes[0]) == 0); 30 | REQUIRE (close(fildes[1]) == 0); 31 | 32 | execlp("as", "as", "-o", path, "-", (char *)NULL); 33 | perror("execlp"); 34 | exit(EXIT_FAILURE); 35 | } 36 | 37 | static 38 | void _child_objcopy( 39 | const int filedes[2], 40 | int t) 41 | { 42 | char path[PATH_MAX] = {0}; 43 | snprintf(path, sizeof(path), "/dev/fd/%d", t); 44 | 45 | REQUIRE (dup2(filedes[1], STDOUT_FILENO) != -1); 46 | 47 | REQUIRE (close(filedes[0]) == 0); 48 | REQUIRE (close(filedes[1]) == 0); 49 | 50 | execlp("objcopy", "objcopy", "-O", "binary", path, "/dev/stdout", (char *)NULL); 51 | perror("execlp"); 52 | exit(EXIT_FAILURE); 53 | } 54 | 55 | const 56 | size_t assemble_armv8( 57 | uint8_t *const bytecode, 58 | const size_t bytecode_sz, 59 | const char *const assembly, 60 | const size_t asm_sz) 61 | { 62 | size_t sz = 0; 63 | 64 | char path[PATH_MAX]; 65 | snprintf(path, sizeof(path), "/tmp/rappel-output.XXXXXX"); 66 | 67 | const int t = mkstemp(path); 68 | 69 | if (t == -1) { 70 | perror("mkstemp"); 71 | exit(EXIT_FAILURE); 72 | } 73 | 74 | REQUIRE (unlink(path) == 0); 75 | 76 | int fildes[2] = {-1, -1}; 77 | REQUIRE (pipe(fildes) == 0); 78 | 79 | const pid_t asm_pid = fork(); 80 | 81 | if (asm_pid < 0) { 82 | perror("fork"); 83 | exit(EXIT_FAILURE); 84 | } else if (asm_pid == 0) { 85 | _child_assemble(fildes, t); 86 | // Not reached 87 | abort(); 88 | } 89 | 90 | verbose_printf("as is pid %d\n", asm_pid); 91 | 92 | REQUIRE (close(fildes[0]) == 0); 93 | 94 | write_data(fildes[1], (uint8_t *)assembly, asm_sz); 95 | 96 | REQUIRE (close(fildes[1]) == 0); 97 | 98 | int asm_status = -1; 99 | REQUIRE (waitpid(asm_pid, &asm_status, 0) != -1); 100 | 101 | if (WIFSIGNALED(asm_status)) { 102 | fprintf(stderr, "as exited with signal %d.\n", WTERMSIG(asm_status)); 103 | } else if (WIFEXITED(asm_status) && WEXITSTATUS(asm_status)) { 104 | fprintf(stderr, "as exited %d.\n", WEXITSTATUS(asm_status)); 105 | goto exit; 106 | } 107 | 108 | REQUIRE (lseek(t, SEEK_SET, 0) != -1); 109 | 110 | int results[2] = {-1, -1}; 111 | REQUIRE (pipe(results) == 0); 112 | 113 | const pid_t objcopy_pid = fork(); 114 | 115 | if (objcopy_pid < 0) { 116 | perror("fork"); 117 | exit(EXIT_FAILURE); 118 | } else if (objcopy_pid == 0) { 119 | _child_objcopy(results, t); 120 | // Not reached 121 | abort(); 122 | } 123 | 124 | verbose_printf("objcopy is pid %d\n", objcopy_pid); 125 | 126 | REQUIRE (close(results[1]) == 0); 127 | 128 | mem_assign(bytecode, bytecode_sz, TRAP, TRAP_SZ); 129 | 130 | sz = read_data(results[0], bytecode, bytecode_sz); 131 | 132 | REQUIRE(close(results[0]) == 0); 133 | 134 | if (sz >= bytecode_sz) { 135 | fprintf(stderr, "Too much bytecode to handle, exiting...\n"); 136 | exit(EXIT_FAILURE); 137 | } 138 | 139 | int objcopy_status = -1; 140 | REQUIRE (waitpid(objcopy_pid, &objcopy_status, 0) != -1); 141 | 142 | if (WIFEXITED(objcopy_status) && WIFSIGNALED(objcopy_status)) 143 | fprintf(stderr, "objcopy exited with signal %d.\n", WTERMSIG(objcopy_status)); 144 | else if (WIFEXITED(objcopy_status) && WEXITSTATUS(objcopy_status)) 145 | fprintf(stderr, "objcopy exited %d.\n", WEXITSTATUS(objcopy_status)); 146 | 147 | exit: 148 | REQUIRE (close(t) == 0); 149 | 150 | return sz; 151 | } 152 | -------------------------------------------------------------------------------- /arch/armv8/display_armv8.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include "arch.h" 5 | 6 | #include "display.h" 7 | #include "printfmt.h" 8 | 9 | extern struct options_t options; 10 | 11 | void display_armv8( 12 | const struct proc_info_t *const info) 13 | { 14 | const struct user_regs_armv8 *uregs = &info->regs_struct; 15 | const struct user_fpregs_armv8 *fpregs = &info->fpregs_struct; 16 | 17 | const struct user_regs_armv8 *old_uregs = &info->old_regs_struct; 18 | const struct user_fpregs_armv8 *old_fpregs = &info->old_fpregs_struct; 19 | 20 | if (options.allregs) printf("GP Regs:\n"); 21 | 22 | PRINTREG64(" x0=", regs[0], uregs, old_uregs, "\t"); 23 | PRINTREG64(" x1=", regs[1], uregs, old_uregs, "\t"); 24 | PRINTREG64(" x2=", regs[2], uregs, old_uregs, "\t"); 25 | PRINTREG64(" x3=", regs[3], uregs, old_uregs, "\n"); 26 | 27 | PRINTREG64(" x4=", regs[4], uregs, old_uregs, "\t"); 28 | PRINTREG64(" x5=", regs[5], uregs, old_uregs, "\t"); 29 | PRINTREG64(" x6=", regs[6], uregs, old_uregs, "\t"); 30 | PRINTREG64(" x7=", regs[7], uregs, old_uregs, "\n"); 31 | 32 | PRINTREG64(" x8=", regs[8], uregs, old_uregs, "\t"); 33 | PRINTREG64(" x9=", regs[9], uregs, old_uregs, "\t"); 34 | PRINTREG64("x10=", regs[10], uregs, old_uregs, "\t"); 35 | PRINTREG64("x11=", regs[11], uregs, old_uregs, "\n"); 36 | 37 | PRINTREG64("x12=", regs[12], uregs, old_uregs, "\t"); 38 | PRINTREG64("x13=", regs[13], uregs, old_uregs, "\t"); 39 | PRINTREG64("x14=", regs[14], uregs, old_uregs, "\t"); 40 | PRINTREG64("x15=", regs[15], uregs, old_uregs, "\n"); 41 | 42 | PRINTREG64("x16=", regs[16], uregs, old_uregs, "\t"); 43 | PRINTREG64("x17=", regs[17], uregs, old_uregs, "\t"); 44 | PRINTREG64("x18=", regs[18], uregs, old_uregs, "\t"); 45 | PRINTREG64("x19=", regs[19], uregs, old_uregs, "\n"); 46 | 47 | PRINTREG64("x20=", regs[20], uregs, old_uregs, "\t"); 48 | PRINTREG64("x21=", regs[21], uregs, old_uregs, "\t"); 49 | PRINTREG64("x22=", regs[22], uregs, old_uregs, "\t"); 50 | PRINTREG64("x23=", regs[23], uregs, old_uregs, "\n"); 51 | 52 | PRINTREG64("x24=", regs[24], uregs, old_uregs, "\t"); 53 | PRINTREG64("x25=", regs[25], uregs, old_uregs, "\t"); 54 | PRINTREG64("x26=", regs[26], uregs, old_uregs, "\t"); 55 | PRINTREG64("x27=", regs[27], uregs, old_uregs, "\n"); 56 | 57 | PRINTREG64("x28=", regs[28], uregs, old_uregs, "\t"); 58 | PRINTREG64(" fp=", regs[29], uregs, old_uregs, "\t"); 59 | PRINTREG64(" lr=", regs[30], uregs, old_uregs, "\t"); 60 | PRINTREG64(" sp=", sp, uregs, old_uregs, "\n"); 61 | 62 | PRINTREG64(" pc=", pc, uregs, old_uregs, "\t"); 63 | PRINTREG64("psr=", pstate, uregs, old_uregs, "\n"); 64 | 65 | if (options.allregs) { 66 | printf("FP Regs:\n"); 67 | } 68 | 69 | // 5 is sigtrap, which is expected, -1 is initial value 70 | if (info->sig != 5 && info->sig != -1) { 71 | printf("Process died with signal: %d\n", info->sig); 72 | printf("Exited: %ld\n", info->exit_code); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /arch/armv8/dump_armv8.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | #include "arch.h" 6 | 7 | void dump_state_armv8( 8 | const struct proc_info_t *const info) 9 | { 10 | const struct user_regs_armv8 *regs = &info->regs_struct; 11 | const struct user_fpregs_armv8 *fpregs = &info->fpregs_struct; 12 | 13 | write_data(STDOUT_FILENO, (uint8_t *)regs, sizeof(struct user_regs_armv8)); 14 | write_data(STDOUT_FILENO, (uint8_t *)fpregs, sizeof(struct user_fpregs_armv8)); 15 | } 16 | -------------------------------------------------------------------------------- /arch/armv8/elf_armv8.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "common.h" 8 | #include "arch.h" 9 | 10 | #include "elf_gen.h" 11 | 12 | const size_t gen_elf_armv8( 13 | uint8_t **out, 14 | const unsigned long start, 15 | const uint8_t *const code, 16 | const size_t code_sz) 17 | { 18 | /* We give the elf header and phdr an entire page, because the elf loader can 19 | * only map the file at PAGE_SIZE offsets. So our file will look like this 20 | * for an invocation with some code and 2 data segments. 21 | * 22 | * +----------+ 23 | * | 1st page | 24 | * | ehdr | 25 | * | phdr | 26 | * | shdr | 27 | * | shdr | 28 | * |----------| 29 | * | 2nd page | 30 | * | code | 31 | * |----------| 32 | * | 3rd page | 33 | * | data 1 | 34 | * |----------| 35 | * | 4th page | 36 | * | data 2 | 37 | * +----------+ 38 | * 39 | * TODO add data section, section headers 40 | */ 41 | 42 | const size_t pg_align_dist = start - (start & ~0xffff); 43 | const size_t pad_sz = ((code_sz + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) - code_sz; 44 | const size_t sz = PAGE_SIZE + pg_align_dist + code_sz + pad_sz; 45 | 46 | uint8_t *const e = xmalloc(sz); 47 | mem_assign(e, sz, TRAP, TRAP_SZ); 48 | 49 | Elf64_Ehdr *const ehdr = (Elf64_Ehdr *) e; 50 | 51 | ehdr->e_ident[0] = ELFMAG0; 52 | ehdr->e_ident[1] = ELFMAG1; 53 | ehdr->e_ident[2] = ELFMAG2; 54 | ehdr->e_ident[3] = ELFMAG3; 55 | ehdr->e_ident[4] = ELFCLASS64; 56 | ehdr->e_ident[5] = ELFDATA2LSB; 57 | ehdr->e_ident[6] = EV_CURRENT; 58 | ehdr->e_ident[7] = ELFOSABI_NONE; 59 | ehdr->e_ident[9] = 0; 60 | // Padding 61 | ehdr->e_type = ET_EXEC; 62 | ehdr->e_machine = EM_AARCH64; 63 | ehdr->e_version = EV_CURRENT; 64 | ehdr->e_entry = start; 65 | ehdr->e_phoff = sizeof(Elf64_Ehdr); 66 | ehdr->e_shoff = 0; // XXX 67 | ehdr->e_flags = 0; 68 | ehdr->e_ehsize = sizeof(Elf64_Ehdr); 69 | ehdr->e_phentsize = sizeof(Elf64_Phdr); 70 | ehdr->e_phnum = 1; 71 | ehdr->e_shentsize = 0; 72 | ehdr->e_shnum = 0; 73 | ehdr->e_shstrndx = 0; 74 | 75 | Elf64_Phdr *const phdr = (Elf64_Phdr *) ((uint8_t *) e + sizeof(Elf64_Ehdr)); 76 | 77 | phdr->p_type = PT_LOAD; 78 | phdr->p_flags = PF_X | PF_R; 79 | phdr->p_offset = PAGE_SIZE; 80 | phdr->p_vaddr = start - pg_align_dist; 81 | phdr->p_paddr = 0; 82 | phdr->p_filesz = code_sz + pg_align_dist; 83 | phdr->p_memsz = code_sz + pg_align_dist; 84 | phdr->p_align = 0x4; 85 | 86 | uint8_t *const data = (uint8_t *) e + PAGE_SIZE + pg_align_dist; 87 | memcpy(data, code, code_sz); 88 | 89 | *out = e; 90 | 91 | return sz; 92 | } 93 | -------------------------------------------------------------------------------- /arch/armv8/include/arch.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define TRAP 0xd4200020 5 | #define TRAP_SZ 4 6 | 7 | #define ARMV8_INIT_PROC_INFO(i) \ 8 | do {\ 9 | (i).regs = (struct iovec) { .iov_base = &(i).regs_struct, .iov_len = sizeof((i).regs_struct) }; \ 10 | (i).fpregs = (struct iovec) { .iov_base = &(i).fpregs_struct, .iov_len = sizeof((i).fpregs_struct) }; \ 11 | } while (0) 12 | 13 | struct user_regs_armv8 14 | { 15 | uint64_t regs[31]; 16 | uint64_t sp; 17 | uint64_t pc; 18 | uint64_t pstate; 19 | }; 20 | 21 | struct user_fpregs_armv8 22 | { 23 | __int128_t vregs[32]; 24 | uint32_t fpsr; 25 | uint32_t fpcr; 26 | uint32_t __reserved[2]; 27 | }; 28 | 29 | // TODO NT_ARM_TLS regset 30 | 31 | struct proc_info_t { 32 | pid_t pid; 33 | 34 | struct user_regs_armv8 regs_struct; 35 | struct user_regs_armv8 old_regs_struct; 36 | struct iovec regs; 37 | 38 | struct user_fpregs_armv8 fpregs_struct; 39 | struct user_fpregs_armv8 old_fpregs_struct; 40 | struct iovec fpregs; 41 | 42 | int sig; 43 | long exit_code; 44 | }; 45 | 46 | #define PAGE_SHIFT 12 47 | #define PAGE_SIZE (1UL << PAGE_SHIFT) 48 | -------------------------------------------------------------------------------- /arch/armv8/include/display.h: -------------------------------------------------------------------------------- 1 | void display_armv8( 2 | const struct proc_info_t *const); 3 | -------------------------------------------------------------------------------- /arch/armv8/include/dump_state.h: -------------------------------------------------------------------------------- 1 | void dump_state_armv8( 2 | const struct proc_info_t *const); 3 | -------------------------------------------------------------------------------- /arch/armv8/include/elf_gen.h: -------------------------------------------------------------------------------- 1 | const 2 | size_t gen_elf_armv8( 3 | uint8_t **, 4 | const unsigned long, 5 | const uint8_t *const, 6 | const size_t); 7 | -------------------------------------------------------------------------------- /arch/armv8/include/ptrace_arch.h: -------------------------------------------------------------------------------- 1 | void ptrace_collect_regs_armv8( 2 | const pid_t, 3 | struct proc_info_t *const); 4 | 5 | void ptrace_reset_armv8( 6 | const pid_t, 7 | const unsigned long); 8 | -------------------------------------------------------------------------------- /arch/armv8/ptrace_armv8.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | #include "arch.h" 6 | 7 | void ptrace_collect_regs_armv8( 8 | const pid_t child_pid, 9 | struct proc_info_t *const info) 10 | { 11 | info->pid = child_pid; 12 | 13 | info->old_regs_struct = info->regs_struct; 14 | 15 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &info->regs) == 0); 16 | 17 | info->old_fpregs_struct = info->fpregs_struct; 18 | 19 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRFPREG, &info->fpregs) == 0); 20 | 21 | info->sig = -1; 22 | info->exit_code = -1; 23 | } 24 | 25 | void ptrace_reset_armv8( 26 | const pid_t child_pid, 27 | const unsigned long start) 28 | { 29 | struct user_regs_armv8 regs_struct = {}; 30 | struct iovec regs = {.iov_base = ®s_struct, .iov_len = sizeof(regs_struct) }; 31 | 32 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, ®s) == 0); 33 | 34 | regs_struct.pc = start; 35 | 36 | REQUIRE (ptrace(PTRACE_SETREGSET, child_pid, NT_PRSTATUS, ®s) == 0); 37 | } 38 | -------------------------------------------------------------------------------- /arch/x86/assemble_intel.c: -------------------------------------------------------------------------------- 1 | ../amd64/assemble_intel.c -------------------------------------------------------------------------------- /arch/x86/display_x86.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include "arch.h" 5 | 6 | #include "display.h" 7 | #include "printfmt.h" 8 | 9 | extern struct options_t options; 10 | 11 | void display_x86( 12 | const struct proc_info_t *const info) 13 | { 14 | const struct user_regs_struct_x86 *regs = &info->regs_struct; 15 | const struct user_fpregs_struct_x86 *fpregs = &info->fpregs_struct; 16 | const struct user_fpxregs_struct_x86 *fpxregs = &info->fpxregs_struct; 17 | 18 | const struct user_regs_struct_x86 *old_regs = &info->old_regs_struct; 19 | const struct user_fpregs_struct_x86 *old_fpregs = &info->old_fpregs_struct; 20 | const struct user_fpxregs_struct_x86 *old_fpxregs = &info->old_fpxregs_struct; 21 | 22 | if (options.allregs) printf("GP Regs:\n"); 23 | 24 | PRINTREG32("eax=", eax, regs, old_regs, " "); 25 | PRINTREG32("ebx=", ebx, regs, old_regs, " "); 26 | PRINTREG32("ecx=", ecx, regs, old_regs, " "); 27 | PRINTREG32("edx=", edx, regs, old_regs, " "); 28 | PRINTREG32("esi=", esi, regs, old_regs, " "); 29 | PRINTREG32("edi=", edi, regs, old_regs, "\n"); 30 | 31 | PRINTREG32("eip=", eip, regs, old_regs, " "); 32 | PRINTREG32("esp=", esp, regs, old_regs, " "); 33 | PRINTREG32("ebp=", ebp, regs, old_regs, " "); 34 | 35 | const uint8_t of = (regs->eflags & 0x800) >> 11; 36 | const uint8_t old_of = (old_regs->eflags & 0x800) >> 11; 37 | 38 | const uint8_t df = (regs->eflags & 0x400) >> 10; 39 | const uint8_t old_df = (old_regs->eflags & 0x400) >> 10; 40 | 41 | const uint8_t sf = (regs->eflags & 0x80) >> 7; 42 | const uint8_t old_sf = (regs->eflags & 0x80) >> 7; 43 | 44 | const uint8_t zf = (regs->eflags & 0x40) >> 6; 45 | const uint8_t old_zf = (old_regs->eflags & 0x40) >> 6; 46 | 47 | const uint8_t af = (regs->eflags & 0x10) >> 4; 48 | const uint8_t old_af = (old_regs->eflags & 0x10) >> 4; 49 | 50 | const uint8_t pf = (regs->eflags & 4) >> 2; 51 | const uint8_t old_pf = (old_regs->eflags & 4) >> 2; 52 | 53 | const uint8_t cf = regs->eflags & 1; 54 | const uint8_t old_cf = old_regs->eflags & 1; 55 | 56 | printf("["); 57 | PRINTBIT("cf:", cf, old_cf, ", "); 58 | PRINTBIT("zf:", zf, old_zf, ", "); 59 | PRINTBIT("of:", of, old_of, ", "); 60 | PRINTBIT("sf:", sf, old_sf, ", "); 61 | PRINTBIT("pf:", pf, old_pf, ", "); 62 | PRINTBIT("af:", af, old_af, ", "); 63 | PRINTBIT("df:", df, old_df, ""); 64 | printf("]\n"); 65 | 66 | PRINTREG16("cs=", xcs, regs, old_regs, " "); 67 | PRINTREG16("ss=", xss, regs, old_regs, " "); 68 | PRINTREG16("ds=", xds, regs, old_regs, " "); 69 | 70 | PRINTREG16("es=", xss, regs, old_regs, " "); 71 | PRINTREG16("fs=", xfs, regs, old_regs, " "); 72 | PRINTREG16("gs=", xgs, regs, old_regs, " "); 73 | 74 | PRINTREG32("efl=", eflags, regs, old_regs, "\n"); 75 | 76 | 77 | if (options.allregs) { 78 | printf("FP Regs:\n"); 79 | PRINTREG32("cwd: ", cwd, fpregs, old_fpregs, "\t"); 80 | PRINTREG32("swd: ", swd, fpregs, old_fpregs, "\t"); 81 | PRINTREG32("twd: ", twd, fpregs, old_fpregs, "\t"); 82 | PRINTREG32("fip: ", fip, fpregs, old_fpregs, "\n"); 83 | 84 | PRINTREG32("fcs: ", fcs, fpregs, old_fpregs, "\t"); 85 | PRINTREG32("foo: ", foo, fpregs, old_fpregs, "\t"); 86 | PRINTREG32("fos: ", fos, fpregs, old_fpregs, "\n"); 87 | 88 | printf("st_space:\n"); 89 | for (uint32_t i = 0; i < 20/4; ++i) { 90 | printf("0x%02x:\t", i * 0x10); 91 | for (uint32_t j = i*4; j < i*4 + 4; ++j) { 92 | DUMPREG32(st_space[j], fpregs, old_fpregs); 93 | printf("\t"); 94 | } 95 | printf("\n"); 96 | } 97 | 98 | fpregs = NULL; 99 | 100 | printf("FPX Regs:\n"); 101 | PRINTREG32("cwd: ", cwd, fpxregs, old_fpxregs, "\t"); 102 | PRINTREG32("swd: ", swd, fpxregs, old_fpxregs, "\t"); 103 | PRINTREG32("twd: ", twd, fpxregs, old_fpxregs, "\t"); 104 | PRINTREG32("fop: ", fop, fpxregs, old_fpxregs, "\n"); 105 | 106 | PRINTREG32("fip: ", fip, fpxregs, old_fpxregs, "\t"); 107 | PRINTREG32("fcs: ", fcs, fpxregs, old_fpxregs, "\t"); 108 | PRINTREG32("foo: ", foo, fpxregs, old_fpxregs, "\t"); 109 | PRINTREG32("fos: ", fos, fpxregs, old_fpxregs, "\n"); 110 | 111 | PRINTREG32("mxcsr: ", mxcsr, fpxregs, old_fpxregs, "\n"); 112 | 113 | printf("st_space:\n"); 114 | for (uint32_t i = 0; i < 32/4; ++i) { 115 | printf("0x%02x:\t", i * 0x10); 116 | for (uint32_t j = i*4; j < i*4 + 4; ++j) { 117 | DUMPREG32(st_space[j], fpxregs, old_fpxregs); 118 | printf("\t"); 119 | } 120 | printf("\n"); 121 | } 122 | printf("xmm_space:\n"); 123 | for (uint32_t i = 0; i < 32/4; ++i) { 124 | printf("0x%02x:\t", i * 0x10); 125 | for (uint32_t j = i*4; j < i*4 + 4; ++j) { 126 | DUMPREG32(st_space[j], fpxregs, old_fpxregs); 127 | printf("\t"); 128 | } 129 | printf("\n"); 130 | } 131 | } 132 | 133 | // 5 is sigtrap, which is expected, -1 is initial value 134 | if (info->sig != 5 && info->sig != -1) { 135 | printf("Process died with signal: %d\n", info->sig); 136 | printf("Exited: %ld\n", info->exit_code); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /arch/x86/dump_x86.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | #include "arch.h" 6 | 7 | void dump_state_x86( 8 | const struct proc_info_t *const info) 9 | { 10 | const struct user_regs_struct_x86 *regs = &info->regs_struct; 11 | const struct user_fpregs_struct_x86 *fpregs = &info->fpregs_struct; 12 | const struct user_fpxregs_struct_x86 *fpxregs = &info->fpxregs_struct; 13 | 14 | write_data(STDOUT_FILENO, (uint8_t *)regs, sizeof(struct user_regs_struct_x86)); 15 | write_data(STDOUT_FILENO, (uint8_t *)fpregs, sizeof(struct user_fpregs_struct_x86)); 16 | write_data(STDOUT_FILENO, (uint8_t *)fpxregs, sizeof(struct user_fpxregs_struct_x86)); 17 | } 18 | -------------------------------------------------------------------------------- /arch/x86/elf_x86.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "common.h" 8 | #include "arch.h" 9 | 10 | #include "elf_gen.h" 11 | 12 | const size_t gen_elf_x86( 13 | uint8_t **out, 14 | const unsigned long start, 15 | const uint8_t *const code, 16 | const size_t code_sz) 17 | { 18 | /* We give the elf header and phdr an entire page, because the elf loader can 19 | * only map the file at PAGE_SIZE offsets. So our file will look like this 20 | * for an invocation with some code and 2 data segments. 21 | * 22 | * +----------+ 23 | * | 1st page | 24 | * | ehdr | 25 | * | phdr | 26 | * | shdr | 27 | * | shdr | 28 | * |----------| 29 | * | 2nd page | 30 | * | code | 31 | * |----------| 32 | * | 3rd page | 33 | * | data 1 | 34 | * |----------| 35 | * | 4th page | 36 | * | data 2 | 37 | * +----------+ 38 | * 39 | * TODO add data section, section headers 40 | */ 41 | 42 | const size_t pg_align_dist = start - (start & ~0xffff); 43 | const size_t pad_sz = ((code_sz + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) - code_sz; 44 | const size_t sz = PAGE_SIZE + pg_align_dist + code_sz + pad_sz; 45 | 46 | uint8_t *const e = xmalloc(sz); 47 | mem_assign(e, sz, TRAP, TRAP_SZ); 48 | 49 | Elf32_Ehdr *const ehdr = (Elf32_Ehdr *) e; 50 | 51 | ehdr->e_ident[0] = ELFMAG0; 52 | ehdr->e_ident[1] = ELFMAG1; 53 | ehdr->e_ident[2] = ELFMAG2; 54 | ehdr->e_ident[3] = ELFMAG3; 55 | ehdr->e_ident[4] = ELFCLASS32; 56 | ehdr->e_ident[5] = ELFDATA2LSB; 57 | ehdr->e_ident[6] = EV_CURRENT; 58 | ehdr->e_ident[7] = ELFOSABI_NONE; 59 | ehdr->e_ident[9] = 0; 60 | // Padding 61 | ehdr->e_type = ET_EXEC; 62 | ehdr->e_machine = EM_386; 63 | ehdr->e_version = EV_CURRENT; 64 | ehdr->e_entry = start; 65 | ehdr->e_phoff = sizeof(Elf32_Ehdr); 66 | ehdr->e_shoff = 0; // XXX 67 | ehdr->e_flags = 0; 68 | ehdr->e_ehsize = sizeof(Elf32_Ehdr); 69 | ehdr->e_phentsize = sizeof(Elf32_Phdr); 70 | ehdr->e_phnum = 1; 71 | ehdr->e_shentsize = 0; 72 | ehdr->e_shnum = 0; 73 | ehdr->e_shstrndx = 0; 74 | 75 | Elf32_Phdr *const phdr = (Elf32_Phdr *) ((uint8_t *) e + sizeof(Elf32_Ehdr)); 76 | 77 | phdr->p_type = PT_LOAD; 78 | phdr->p_flags = PF_X | PF_R; 79 | phdr->p_offset = PAGE_SIZE; 80 | phdr->p_vaddr = start - pg_align_dist; 81 | phdr->p_paddr = 0; 82 | phdr->p_filesz = code_sz + pg_align_dist; 83 | phdr->p_memsz = code_sz + pg_align_dist; 84 | phdr->p_align = 0x4; 85 | 86 | uint8_t *const data = (uint8_t *) e + PAGE_SIZE + pg_align_dist; 87 | memcpy(data, code, code_sz); 88 | 89 | *out = e; 90 | 91 | return sz; 92 | } 93 | -------------------------------------------------------------------------------- /arch/x86/include/arch.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TRAP 0xcc // int3 4 | #define TRAP_SZ 1 5 | 6 | #define BITSTR "[bits 32]\n" 7 | 8 | #define X86_INIT_PROC_INFO(i) \ 9 | do {\ 10 | (i).regs = (struct iovec) { .iov_base = &(i).regs_struct, .iov_len = sizeof((i).regs_struct) }; \ 11 | (i).fpregs = (struct iovec) { .iov_base = &(i).fpregs_struct, .iov_len = sizeof((i).fpregs_struct) }; \ 12 | (i).fpxregs = (struct iovec) { .iov_base = &(i).fpxregs_struct, .iov_len = sizeof((i).fpxregs_struct) }; \ 13 | } while (0) 14 | 15 | struct user_fpregs_struct_x86 16 | { 17 | long int cwd; 18 | long int swd; 19 | long int twd; 20 | long int fip; 21 | long int fcs; 22 | long int foo; 23 | long int fos; 24 | long int st_space [20]; 25 | }; 26 | 27 | struct user_fpxregs_struct_x86 28 | { 29 | unsigned short int cwd; 30 | unsigned short int swd; 31 | unsigned short int twd; 32 | unsigned short int fop; 33 | long int fip; 34 | long int fcs; 35 | long int foo; 36 | long int fos; 37 | long int mxcsr; 38 | long int reserved; 39 | long int st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ 40 | long int xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ 41 | long int padding[56]; 42 | }; 43 | 44 | struct user_regs_struct_x86 45 | { 46 | long int ebx; 47 | long int ecx; 48 | long int edx; 49 | long int esi; 50 | long int edi; 51 | long int ebp; 52 | long int eax; 53 | long int xds; 54 | long int xes; 55 | long int xfs; 56 | long int xgs; 57 | long int orig_eax; 58 | long int eip; 59 | long int xcs; 60 | long int eflags; 61 | long int esp; 62 | long int xss; 63 | }; 64 | 65 | 66 | struct proc_info_t { 67 | pid_t pid; 68 | 69 | struct user_regs_struct_x86 regs_struct; 70 | struct user_regs_struct_x86 old_regs_struct; 71 | struct iovec regs; 72 | 73 | struct user_fpregs_struct_x86 fpregs_struct; 74 | struct user_fpregs_struct_x86 old_fpregs_struct; 75 | struct iovec fpregs; 76 | 77 | struct user_fpxregs_struct_x86 fpxregs_struct; 78 | struct user_fpxregs_struct_x86 old_fpxregs_struct; 79 | struct iovec fpxregs; 80 | 81 | int sig; 82 | long exit_code; 83 | }; 84 | 85 | #define PAGE_SHIFT 12 86 | #define PAGE_SIZE (1UL << PAGE_SHIFT) 87 | -------------------------------------------------------------------------------- /arch/x86/include/display.h: -------------------------------------------------------------------------------- 1 | void display_x86( 2 | const struct proc_info_t *const); 3 | -------------------------------------------------------------------------------- /arch/x86/include/dump_state.h: -------------------------------------------------------------------------------- 1 | void dump_state_x86( 2 | const struct proc_info_t *const); 3 | -------------------------------------------------------------------------------- /arch/x86/include/elf_gen.h: -------------------------------------------------------------------------------- 1 | const 2 | size_t gen_elf_x86( 3 | uint8_t **, 4 | const unsigned long, 5 | const uint8_t *const, 6 | const size_t); 7 | -------------------------------------------------------------------------------- /arch/x86/include/ptrace_arch.h: -------------------------------------------------------------------------------- 1 | void ptrace_collect_regs_x86( 2 | const pid_t, 3 | struct proc_info_t *const); 4 | 5 | void ptrace_reset_x86( 6 | const pid_t, 7 | const unsigned long); 8 | -------------------------------------------------------------------------------- /arch/x86/ptrace_x86.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | #include "arch.h" 6 | 7 | void ptrace_collect_regs_x86( 8 | const pid_t child_pid, 9 | struct proc_info_t *const info) 10 | { 11 | info->pid = child_pid; 12 | 13 | info->old_regs_struct = info->regs_struct; 14 | 15 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &info->regs) == 0); 16 | 17 | info->old_fpregs_struct = info->fpregs_struct; 18 | 19 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRFPREG, &info->fpregs) == 0); 20 | 21 | info->old_fpxregs_struct = info->fpxregs_struct; 22 | 23 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRXFPREG, &info->fpxregs) == 0); 24 | 25 | info->sig = -1; 26 | info->exit_code = -1; 27 | } 28 | 29 | void ptrace_reset_x86( 30 | const pid_t child_pid, 31 | const unsigned long start) 32 | { 33 | struct user_regs_struct_x86 regs_struct = {}; 34 | struct iovec regs = {.iov_base = ®s_struct, .iov_len = sizeof(regs_struct) }; 35 | 36 | REQUIRE (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, ®s) == 0); 37 | 38 | regs_struct.eip = start; 39 | 40 | REQUIRE (ptrace(PTRACE_SETREGSET, child_pid, NT_PRSTATUS, ®s) == 0); 41 | } 42 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | 4 | pr: 5 | - master 6 | 7 | jobs: 8 | - job: x64 9 | pool: 10 | vmImage: 'ubuntu-latest' 11 | steps: 12 | - bash: | 13 | set -ex 14 | docker run -v $PWD:/t amd64/debian:unstable-slim /bin/bash -c "set -ex; apt-get update; apt-get install -y libedit-dev nasm make gcc; cd /t; CC=gcc make test" 15 | - script: 16 | sudo make clean 17 | - bash: | 18 | set -ex 19 | docker run -v $PWD:/t amd64/debian:unstable-slim /bin/bash -c "set -ex; apt-get update; apt-get install -y libedit-dev nasm make clang; cd /t; CC=clang make test" 20 | 21 | - job: arm64 22 | pool: 23 | vmImage: 'ubuntu-latest' 24 | steps: 25 | - bash: | 26 | set -ex 27 | docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 28 | - bash: | 29 | set -ex 30 | docker run --rm -v $PWD:/t arm64v8/debian:unstable-slim /bin/bash -c "set -ex; apt-get update; apt-get install -y libedit-dev nasm make gcc; cd /t; CC=gcc make" 31 | - script: 32 | sudo make clean 33 | - bash: | 34 | set -ex 35 | docker run --rm -v $PWD:/t arm64v8/debian:unstable-slim /bin/bash -c "set -ex; apt-get update; apt-get install -y libedit-dev nasm make clang; cd /t; CC=clang make" 36 | 37 | - job: armv7l 38 | pool: 39 | vmImage: 'ubuntu-latest' 40 | steps: 41 | - bash: | 42 | set -ex 43 | docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 44 | - bash: | 45 | set -ex 46 | docker run --rm -v $PWD:/t arm32v7/debian:unstable-slim /bin/bash -c "set -ex; apt-get update; apt-get install -y libedit-dev nasm make gcc; cd /t; CC=gcc make" 47 | - script: 48 | sudo make clean 49 | - bash: | 50 | set -ex 51 | docker run --rm -v $PWD:/t arm32v7/debian:unstable-slim /bin/bash -c "set -ex; apt-get update; apt-get install -y libedit-dev nasm make clang; cd /t; CC=clang make" 52 | 53 | - job: i386 54 | pool: 55 | vmImage: 'ubuntu-latest' 56 | steps: 57 | - bash: | 58 | set -ex 59 | docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 60 | - bash: | 61 | set -ex 62 | docker run --rm -v $PWD:/t i386/debian:unstable-slim /bin/bash -c "set -ex; apt-get update; apt-get install -y libedit-dev nasm make gcc; cd /t; ARCH=x86 CC=gcc make test" 63 | - script: 64 | sudo make clean 65 | - bash: | 66 | set -ex 67 | docker run --rm -v $PWD:/t i386/debian:unstable-slim /bin/bash -c "set -ex; apt-get update; apt-get install -y libedit-dev nasm make clang; cd /t; ARCH=x86 CC=clang make test" 68 | -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common.h" 9 | 10 | extern struct options_t options; 11 | 12 | void mem_assign( 13 | uint8_t *buf, 14 | const size_t buf_sz, 15 | const uint64_t val, 16 | const size_t val_sz) 17 | { 18 | if (val_sz != 1 && val_sz != 2 && val_sz != 4 && val_sz != 8) { 19 | fprintf(stderr, "%s: val_sz must be 1, 2, 4, or 8\n", __func__); 20 | exit(EXIT_FAILURE); 21 | } 22 | 23 | if ((buf_sz % val_sz) != 0) { 24 | fprintf(stderr, "%s: buf_sz (%zu) must be multiple of val_sz (%zu)\n", __func__, buf_sz, val_sz); 25 | exit(EXIT_FAILURE); 26 | } 27 | 28 | if (val_sz == 1) 29 | memset(buf, val, buf_sz); 30 | else 31 | for (uint64_t i = 0; i < buf_sz; i += val_sz) 32 | memcpy(buf + i, &val, val_sz); 33 | } 34 | 35 | void* xmalloc( 36 | const size_t n) 37 | { 38 | void *ptr = malloc(n); 39 | 40 | if (ptr) return ptr; 41 | 42 | perror("malloc"); 43 | abort(); 44 | } 45 | 46 | void* xrealloc( 47 | void *ptr, 48 | const size_t n) 49 | { 50 | void *p = realloc(ptr, n); 51 | 52 | if (p) return p; 53 | 54 | perror("realloc"); 55 | abort(); 56 | } 57 | 58 | const 59 | size_t read_data( 60 | const int fd, 61 | uint8_t *const buf, 62 | const size_t buf_sz) 63 | { 64 | size_t len = 0; 65 | while (len < buf_sz) { 66 | const ssize_t ret = read(fd, buf + len, buf_sz - len); 67 | 68 | if (ret < 0) { 69 | perror("read"); 70 | exit(EXIT_FAILURE); 71 | } 72 | if (ret == 0) break; 73 | 74 | len += ret; 75 | } 76 | 77 | return len; 78 | } 79 | 80 | void write_data( 81 | const int fd, 82 | const uint8_t *const buf, 83 | const size_t sz) 84 | { 85 | size_t written; 86 | 87 | for (written = 0; written < sz;) { 88 | const ssize_t ret = write(fd, buf + written, sz - written); 89 | 90 | if (ret < 0) { 91 | perror("write"); 92 | exit(EXIT_FAILURE); 93 | } 94 | 95 | if (ret == 0) break; 96 | 97 | written += ret; 98 | } 99 | } 100 | 101 | // Should this be a macro? 102 | void verbose_printf( 103 | const char *const fmt, 104 | ...) 105 | { 106 | if (!options.verbose) return; 107 | 108 | va_list args; 109 | va_start(args, fmt); 110 | vprintf(fmt, args); 111 | va_end(args); 112 | } 113 | 114 | void verbose_dump( 115 | const uint8_t *const buf, 116 | const size_t sz, 117 | const unsigned long long base) 118 | { 119 | if (!options.verbose) return; 120 | 121 | dump(buf, sz, base); 122 | } 123 | 124 | void dump( 125 | const uint8_t *const buf, 126 | const size_t sz, 127 | const unsigned long base) 128 | { 129 | for (size_t i = 0; i < sz; i += 0x10) { 130 | if (base != -1) printf(REGFMT ": ", base + i); 131 | 132 | for (size_t j = i; j < (i + 0x10); j++) { 133 | if (j < sz) 134 | printf("%02x ", buf[j]); 135 | else 136 | printf(" "); 137 | } 138 | 139 | printf("\t"); 140 | 141 | for (size_t j = i; j < (i + 0x10) && j < sz; j++) { 142 | if (j < sz) { 143 | if (buf[j] > 0x1f && buf[j] < 0x7f) 144 | printf("%c", buf[j]); 145 | else 146 | printf("."); 147 | } 148 | } 149 | printf("\n"); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /exedir.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include "common.h" 16 | #include "exedir.h" 17 | 18 | #define RAPPEL_DIR "rappel" 19 | 20 | extern struct options_t options; 21 | 22 | static const 23 | int _reopen_ro( 24 | const int h, 25 | const char *const path) 26 | { 27 | REQUIRE (close(h) == 0); 28 | 29 | const int ro_h = open(path, O_RDONLY | O_CLOEXEC); 30 | 31 | REQUIRE (ro_h >= 0); 32 | 33 | return ro_h; 34 | } 35 | 36 | static 37 | void _clean_rappel_dir(void) 38 | { 39 | char path[PATH_MAX] = { 0 }; 40 | int ret = snprintf(path, sizeof(path), "%s/exe", options.rappel_dir); 41 | if (ret < 0) { 42 | fprintf(stderr, "Path exceeds max path length: %s/exe", options.rappel_dir); 43 | exit(EXIT_FAILURE); 44 | } 45 | 46 | DIR *exedir = opendir(path); 47 | 48 | REQUIRE (exedir != NULL); 49 | 50 | struct dirent *f; 51 | while ((f = readdir(exedir))) { 52 | if (!strcmp(f->d_name, ".") || !strcmp(f->d_name, "..")) 53 | continue; 54 | 55 | if (!strcmp(f->d_name, "history")) 56 | continue; 57 | 58 | ret = snprintf(path, sizeof(path), "%s/exe/%s", options.rappel_dir, f->d_name); 59 | if (ret < 0) { 60 | fprintf(stderr, "Path exceeds max path length: %s/exe/%s", options.rappel_dir, f->d_name); 61 | exit(EXIT_FAILURE); 62 | } 63 | 64 | if (unlink(path) == -1) 65 | fprintf(stderr, "Cannot unlink %s: %s\n", f->d_name, strerror(errno)); 66 | } 67 | 68 | REQUIRE (closedir(exedir) == 0); 69 | } 70 | 71 | void init_rappel_dir(void) 72 | { 73 | const char *home = getenv("HOME"); 74 | const char *xdg_data_home = getenv("XDG_DATA_HOME"); 75 | 76 | if (!home) { 77 | fprintf(stderr, "HOME not set"); 78 | exit(EXIT_FAILURE); 79 | } 80 | 81 | if (!xdg_data_home) { 82 | snprintf( 83 | options.rappel_dir, 84 | sizeof(options.rappel_dir), 85 | "%s/.%s", 86 | home, 87 | RAPPEL_DIR 88 | ); 89 | } else { 90 | snprintf( 91 | options.rappel_dir, 92 | sizeof(options.rappel_dir), 93 | "%s/%s", 94 | xdg_data_home, 95 | RAPPEL_DIR 96 | ); 97 | } 98 | 99 | if (mkdir(options.rappel_dir, 0755) == -1) 100 | REQUIRE (errno == EEXIST); 101 | 102 | char path[PATH_MAX] = { 0 }; 103 | int ret = snprintf(path, sizeof(path), "%s/exe", options.rappel_dir); 104 | if (ret < 0) { 105 | fprintf(stderr, "Path exceeds max path length: %s/exe", options.rappel_dir); 106 | exit(EXIT_FAILURE); 107 | } 108 | 109 | if (mkdir(path, 0755) == -1) 110 | REQUIRE (errno == EEXIST); 111 | 112 | _clean_rappel_dir(); 113 | } 114 | 115 | const 116 | int write_exe( 117 | const uint8_t *data, 118 | const size_t data_sz, 119 | const char *name) 120 | { 121 | if (name) 122 | return write_named_file(data, data_sz, name); 123 | else 124 | return write_tmp_file(data, data_sz); 125 | } 126 | 127 | const 128 | int write_named_file( 129 | const uint8_t *data, 130 | const size_t data_sz, 131 | const char *name) 132 | { 133 | const int h = open(name, O_WRONLY | O_CREAT, 134 | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 135 | 136 | REQUIRE (h >= 0); 137 | 138 | write_data(h, data, data_sz); 139 | 140 | return _reopen_ro(h, name); 141 | } 142 | 143 | const 144 | int write_tmp_file( 145 | const uint8_t *data, 146 | const size_t data_sz) 147 | { 148 | char path[PATH_MAX] = { 0 }; 149 | 150 | int ret = snprintf(path, sizeof(path), "%s/exe/rappel-exe.XXXXXX", options.rappel_dir); 151 | if (ret < 0) { 152 | fprintf(stderr, "Path exceeds max path length: %s/exe/rappel-exe.XXXXXX", options.rappel_dir); 153 | exit(EXIT_FAILURE); 154 | } 155 | 156 | const int h = mkstemp(path); 157 | 158 | REQUIRE (h >= 0); 159 | 160 | write_data(h, data, data_sz); 161 | 162 | REQUIRE (fchmod(h, S_IXUSR | S_IRUSR | S_IWUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0); 163 | 164 | const int h_ro = _reopen_ro(h, path); 165 | 166 | return h_ro; 167 | } 168 | -------------------------------------------------------------------------------- /include/assemble.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const 4 | size_t assemble_intel( 5 | uint8_t *const, 6 | const size_t, 7 | const char *const, 8 | const size_t); 9 | 10 | const 11 | size_t assemble_arm( 12 | uint8_t *const, 13 | const size_t, 14 | const char *const, 15 | const size_t); 16 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #define REQUIRE(x) \ 9 | do {\ 10 | if (!(x)) { \ 11 | perror(#x); \ 12 | exit(EXIT_FAILURE); \ 13 | }\ 14 | } while (0) 15 | 16 | // Round up to the nearest multiple 17 | // // https://gist.github.com/aslakhellesoy/1134482 18 | #define ROUNDUP(n, m) n >= 0 ? ((n + m - 1) / m) * m : (n / m) * m; 19 | 20 | 21 | struct options_t { 22 | unsigned long start; 23 | int raw; 24 | int verbose; 25 | int allregs; 26 | int passsig; 27 | int dump; 28 | const char *savefile; 29 | char rappel_dir[PATH_MAX]; 30 | }; 31 | 32 | #define REGFMT64 "%016" PRIx64 33 | #define REGFMT32 "%08" PRIx32 34 | #define REGFMT16 "%04" PRIx16 35 | #define REGFMT8 "%02" PRIx8 36 | 37 | void mem_assign( 38 | uint8_t *, 39 | const size_t, 40 | const uint64_t, 41 | const size_t); 42 | 43 | void* xmalloc( 44 | size_t); 45 | 46 | void* xrealloc( 47 | void *, 48 | size_t); 49 | 50 | const 51 | size_t read_data( 52 | const int, 53 | uint8_t *const, 54 | const size_t); 55 | 56 | void write_data( 57 | const int, 58 | const uint8_t *const, 59 | const size_t); 60 | 61 | __attribute__ ((format (printf, 1, 2))) 62 | void verbose_printf( 63 | const char *const, 64 | ...); 65 | 66 | void verbose_dump( 67 | const uint8_t *const, 68 | const size_t, 69 | const unsigned long long); 70 | 71 | void dump( 72 | const uint8_t *const, 73 | const size_t, 74 | const unsigned long); 75 | -------------------------------------------------------------------------------- /include/exedir.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void init_rappel_dir(void); 4 | 5 | const 6 | int write_exe( 7 | const uint8_t *, 8 | const size_t, 9 | const char *); 10 | 11 | const 12 | int write_tmp_file( 13 | const uint8_t *, 14 | const size_t); 15 | 16 | const 17 | int write_named_file( 18 | const uint8_t *, 19 | const size_t, 20 | const char *); 21 | -------------------------------------------------------------------------------- /include/pipe.h: -------------------------------------------------------------------------------- 1 | void pipe_mode(void); 2 | -------------------------------------------------------------------------------- /include/printfmt.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // This needs to be modified to handle expressions 4 | 5 | #define RED "\x1b[0;31m" 6 | #define RST "\x1b[0m" 7 | 8 | #define DUMPREG64(x, y, z) \ 9 | do {\ 10 | if (y->x == z->x) \ 11 | printf(REGFMT64, y->x); \ 12 | else \ 13 | printf(RED REGFMT64 RST, y->x); \ 14 | } while (0) 15 | 16 | #define PRINTREG64(h, x, y, z, t) \ 17 | do {\ 18 | printf("%s", h); \ 19 | DUMPREG64(x, y, z); \ 20 | printf("%s", t);\ 21 | } while (0) 22 | 23 | #define DUMPREG32(x, y, z) \ 24 | do {\ 25 | if (y->x == z->x) \ 26 | printf(REGFMT32, (uint32_t)y->x); \ 27 | else \ 28 | printf(RED REGFMT32 RST, (uint32_t)y->x); \ 29 | } while (0) 30 | 31 | #define PRINTREG32(h, x, y, z, t) \ 32 | do {\ 33 | printf("%s", h); \ 34 | DUMPREG32(x, y, z); \ 35 | printf("%s", t);\ 36 | } while (0) 37 | 38 | #define DUMPREG16(x, y, z) \ 39 | do {\ 40 | if (y->x == z->x) \ 41 | printf(REGFMT16, (uint16_t)y->x); \ 42 | else \ 43 | printf(RED REGFMT16 RST, (uint16_t)y->x); \ 44 | } while (0) 45 | 46 | #define PRINTREG16(h, x, y, z, t) \ 47 | do {\ 48 | printf("%s", h); \ 49 | DUMPREG16(x, y, z); \ 50 | printf("%s", t);\ 51 | } while (0) 52 | 53 | #define DUMPREG8(x, y, z) \ 54 | do {\ 55 | if (y->x == z->x) \ 56 | printf(REGFMT8, y->x); \ 57 | else \ 58 | printf(RED REGFMT8 RST, y->x); \ 59 | } while (0) 60 | 61 | #define PRINTREG8(h, x, y, z,t) \ 62 | do {\ 63 | printf("%s", h); \ 64 | DUMPREG8(x, y ,z); \ 65 | printf("%s", t);\ 66 | } while (0) 67 | 68 | #define PRINTBIT(name, y, z, t) \ 69 | do {\ 70 | if (y == z) \ 71 | printf("%s%d", name, y); \ 72 | else \ 73 | printf(RED "%s%d" RST, name, y); \ 74 | printf("%s", t); \ 75 | } while (0) 76 | -------------------------------------------------------------------------------- /include/ptrace.h: -------------------------------------------------------------------------------- 1 | const 2 | int ptrace_write( 3 | const pid_t, 4 | const void *const, 5 | const uint8_t *const, 6 | size_t); 7 | 8 | const 9 | int ptrace_read( 10 | const pid_t, 11 | const void *const, 12 | void *const, 13 | const size_t); 14 | 15 | void ptrace_child( 16 | const int); 17 | 18 | void ptrace_launch( 19 | const pid_t); 20 | 21 | const 22 | int ptrace_reap( 23 | const pid_t, 24 | struct proc_info_t *const); 25 | 26 | void ptrace_cont(const pid_t, 27 | struct proc_info_t *const); 28 | 29 | void ptrace_detatch( 30 | const pid_t child_pid, 31 | struct proc_info_t *const); 32 | -------------------------------------------------------------------------------- /include/ui.h: -------------------------------------------------------------------------------- 1 | static 2 | const uint8_t hex_hashmap[] = 3 | { 4 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 5 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 6 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 7 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 8 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // !"#$%&' 9 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ()*+,-./ 10 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567 11 | 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 89:;<=>? 12 | 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, // @ABCDEFG 13 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // HIJKLMNO 14 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // PQRSTUVW 15 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // XYZ[\]^_ 16 | 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, // `abcdefg 17 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // hijklmno 18 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // pqrstuvw 19 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // xyz{|}~. 20 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 21 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 22 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 23 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 24 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 25 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 26 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 27 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 28 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 29 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 30 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 31 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 32 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 33 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 34 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ........ 35 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // ........ 36 | }; 37 | 38 | void interact( 39 | const char *const); 40 | -------------------------------------------------------------------------------- /pipe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "assemble.h" 9 | #include "common.h" 10 | #include "arch.h" 11 | #include "display.h" 12 | #include "dump_state.h" 13 | #include "elf_gen.h" 14 | #include "exedir.h" 15 | #include "ptrace.h" 16 | 17 | #include "pipe.h" 18 | 19 | #define STDIN_BUF_SZ 64000000 // 64mb 20 | #define BYTECODE_BUF_SZ 64000000 // 64mb 21 | 22 | extern struct options_t options; 23 | 24 | static const 25 | int _is_ascii( 26 | const uint8_t *const d, 27 | const size_t sz) 28 | { 29 | for (size_t i = 0; i < sz; i++) 30 | if ((d[i] < 0x20 || d[i] > 0x7f) && d[i] != '\n') 31 | return 0; 32 | 33 | return 1; 34 | } 35 | 36 | static 37 | void _semi_to_linebreak( 38 | char *const data, 39 | const size_t data_sz) 40 | { 41 | for (size_t i = 0; i < data_sz; i++) 42 | if (data[i] == ';') 43 | data[i] = '\n'; 44 | } 45 | 46 | static const 47 | size_t _read_bytecode( 48 | uint8_t *const data, 49 | const size_t data_sz) 50 | { 51 | uint8_t *const raw = xmalloc(STDIN_BUF_SZ); 52 | 53 | const size_t raw_sz = read_data(STDIN_FILENO, raw, STDIN_BUF_SZ); 54 | 55 | 56 | // If we've read in 64mb of data, we're just going to assume there's more 57 | // we're not reading 58 | if (raw_sz >= STDIN_BUF_SZ) { 59 | fprintf(stderr, "Too much bytecode to handle, exiting...\n"); 60 | exit(EXIT_FAILURE); 61 | } 62 | 63 | if (!_is_ascii(raw, raw_sz) || options.raw) { 64 | if (raw_sz > data_sz) { 65 | fprintf(stderr, "Too much bytecode to copy, exiting...\n"); 66 | exit(EXIT_FAILURE); 67 | } else { 68 | memcpy(data, raw, raw_sz); 69 | 70 | free(raw); 71 | 72 | return raw_sz; 73 | } 74 | } else { 75 | _semi_to_linebreak((char *)raw, raw_sz); 76 | 77 | const size_t asm_sz = assemble(data, data_sz, (char *)raw, raw_sz); 78 | 79 | free(raw); 80 | 81 | return asm_sz; 82 | } 83 | } 84 | 85 | void pipe_mode(void) 86 | { 87 | uint8_t *elf; 88 | uint8_t *const data = xmalloc(BYTECODE_BUF_SZ); 89 | 90 | const size_t data_sz = _read_bytecode(data, BYTECODE_BUF_SZ); 91 | 92 | if (data_sz >= BYTECODE_BUF_SZ) { 93 | fprintf(stderr, "Too much assembled bytecode to handle, exiting...\n"); 94 | exit(EXIT_FAILURE); 95 | } 96 | 97 | if (!data_sz) { 98 | fprintf(stderr, "No bytecode from assembler, exiting...\n"); 99 | exit(EXIT_FAILURE); 100 | } 101 | 102 | verbose_printf("Got assembly:\n"); 103 | verbose_dump(data, data_sz, -1); 104 | 105 | const size_t elf_sz = gen_elf(&elf, options.start, data, data_sz); 106 | 107 | free(data); 108 | 109 | const int exe_fd = write_exe(elf, elf_sz, options.savefile); 110 | 111 | free(elf); 112 | 113 | const pid_t tracee = fork(); 114 | 115 | if (tracee < 0) { 116 | perror("fork"); 117 | exit(EXIT_FAILURE); 118 | } else if (tracee == 0) { 119 | ptrace_child(exe_fd); 120 | abort(); 121 | } else { 122 | close(exe_fd); 123 | ptrace_launch(tracee); 124 | 125 | struct proc_info_t info = {0}; 126 | ARCH_INIT_PROC_INFO(info); 127 | 128 | ptrace_cont(tracee, &info); 129 | 130 | ptrace_reap(tracee, &info); 131 | ptrace_detatch(tracee, &info); 132 | if (options.dump) 133 | dump_state(&info); 134 | else 135 | display(&info); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /ptrace.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include // NT_PRSTATUS 15 | 16 | #include "common.h" 17 | #include "arch.h" 18 | #include "ptrace.h" 19 | 20 | #include "ptrace_arch.h" 21 | 22 | extern struct options_t options; 23 | 24 | static 25 | void _exited_collect_regs( 26 | const pid_t child_pid, 27 | struct proc_info_t *const info) 28 | { 29 | ptrace_collect_regs(child_pid, info); 30 | 31 | siginfo_t si; 32 | REQUIRE (ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &si) == 0); 33 | 34 | info->sig = si.si_signo; 35 | 36 | REQUIRE (ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &info->exit_code) == 0); 37 | } 38 | 39 | const 40 | int ptrace_write( 41 | const pid_t child_pid, 42 | const void *const base, 43 | const uint8_t *const data, 44 | const size_t data_sz) 45 | { 46 | int ret = 0; 47 | 48 | for (unsigned ii = 0; ii < data_sz; ii += sizeof(long)) { 49 | const uintptr_t addr = (uintptr_t)base + ii; 50 | unsigned long val = 0; 51 | 52 | if (ii + sizeof(long) < data_sz) { 53 | val = *(unsigned long *)(data + ii); 54 | } else { 55 | if (ptrace_read(child_pid, (const void *const) addr, &val, sizeof(val))) 56 | ret = -1; 57 | 58 | memcpy(&val, data + ii, data_sz - ii); 59 | } 60 | 61 | verbose_printf("ptrace_write: " REGFMT " = " REGFMT "\n", addr, val); 62 | 63 | if (ptrace(PTRACE_POKETEXT, child_pid, addr, val) == -1) { 64 | ret = -1; 65 | fprintf(stderr, "ptrace() - failed to write value " REGFMT " to " REGFMT "\n", val, addr); 66 | } 67 | } 68 | 69 | return ret; 70 | } 71 | 72 | const 73 | int ptrace_read( 74 | const pid_t child_pid, 75 | const void *const base, 76 | void *const out, 77 | const size_t out_sz) 78 | { 79 | int ret = 0; 80 | 81 | const size_t alloc_sz = ROUNDUP(out_sz, sizeof(long)); 82 | 83 | unsigned long *const copy = xmalloc(alloc_sz); 84 | 85 | for (unsigned ii = 0; ii < alloc_sz / sizeof(long); ++ii) { 86 | const uintptr_t addr = (uintptr_t)base + ii * sizeof(long); 87 | 88 | verbose_printf("ptrace_read: " REGFMT "\n", addr); 89 | 90 | errno = 0; 91 | copy[ii] = ptrace(PTRACE_PEEKDATA, child_pid, addr, 0); 92 | 93 | if (errno) { 94 | ret = -1; 95 | fprintf(stderr, "ptrace() - failed to read value at " REGFMT "\n", addr); 96 | } 97 | } 98 | 99 | memcpy(out, copy, out_sz); 100 | 101 | free(copy); 102 | 103 | return ret; 104 | } 105 | 106 | void ptrace_child( 107 | const int exe_fd) 108 | { 109 | char *const av[] = { NULL }; 110 | char *const ep[] = { NULL }; 111 | 112 | REQUIRE (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == 0); 113 | 114 | fexecve(exe_fd, av, ep); 115 | 116 | perror("fexecve"); 117 | exit(EXIT_FAILURE); 118 | } 119 | 120 | void ptrace_launch( 121 | const pid_t child_pid) 122 | { 123 | int status; 124 | REQUIRE (waitpid(child_pid, &status, 0) != -1); 125 | 126 | // Doesn't exist on my armv8 board, so kill it for now... 127 | //REQUIRE (ptrace(PTRACE_SETOPTIONS, child_pid, NULL, PTRACE_O_EXITKILL) == 0); 128 | 129 | REQUIRE (ptrace(PTRACE_SETOPTIONS, child_pid, NULL, PTRACE_O_TRACEEXIT) == 0); 130 | } 131 | 132 | void ptrace_cont( 133 | const pid_t child_pid, 134 | struct proc_info_t *const info) 135 | { 136 | ptrace_collect_regs(child_pid, info); 137 | 138 | REQUIRE (ptrace(PTRACE_CONT, child_pid, NULL, NULL) == 0); 139 | } 140 | 141 | const 142 | int ptrace_reap( 143 | const pid_t child_pid, 144 | struct proc_info_t *const info) 145 | { 146 | // If shellcode forks, this will have to be revisited. 147 | int status; 148 | 149 | REQUIRE (waitpid(child_pid, &status, 0) != -1); 150 | 151 | if (WIFEXITED(status)) { 152 | printf("pid %d exited: %d\n", child_pid, WEXITSTATUS(status)); 153 | return 1; 154 | } if (WIFSIGNALED(status)) { 155 | printf("pid %d exited on signal %d\n", child_pid, WTERMSIG(status)); 156 | return 1; 157 | } 158 | 159 | // We've exited 160 | if (status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))) { 161 | _exited_collect_regs(child_pid, info); 162 | return 1; 163 | } 164 | 165 | ptrace_collect_regs(child_pid, info); 166 | 167 | if (status>>8 == SIGTRAP) 168 | return 0; 169 | 170 | // Otherwise pass the signal on to the child process 171 | printf("pid %d got signal %d, %s.\n", 172 | child_pid, 173 | WSTOPSIG(status), 174 | (options.passsig) ? "delivering" : "not delivering"); 175 | 176 | if (options.passsig) 177 | REQUIRE (ptrace(PTRACE_CONT, child_pid, 0, WSTOPSIG(status)) == 0); 178 | 179 | return 0; 180 | } 181 | 182 | void ptrace_detatch( 183 | const pid_t child_pid, 184 | struct proc_info_t *const info) 185 | { 186 | REQUIRE (ptrace(PTRACE_DETACH, child_pid, NULL, NULL) == 0); 187 | 188 | int status; 189 | REQUIRE (waitpid(child_pid, &status, 0) != -1); 190 | 191 | if (WIFEXITED(status)) 192 | info->exit_code = WEXITSTATUS(status); 193 | if (WIFSIGNALED(status)) 194 | info->sig = WTERMSIG(status); 195 | } 196 | -------------------------------------------------------------------------------- /rappel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "common.h" 8 | #include "exedir.h" 9 | #include "pipe.h" 10 | #include "ui.h" 11 | 12 | // Defaults 13 | struct options_t options = { 14 | .start = 0x400000, 15 | .verbose = 0, 16 | .raw = 0, 17 | .allregs = 0, 18 | .savefile = NULL, 19 | .rappel_dir = { 0 } 20 | }; 21 | 22 | static 23 | void _usage( 24 | const char *argv0) 25 | { 26 | fprintf(stderr, "Usage: %s [options]\n" 27 | "\t-h\t\tDisplay this help\n" 28 | "\t-r\t\tTreat stdin as raw bytecode (useful for ascii shellcode)\n" 29 | "\t-p\t\tPass signals to child process (will allow child to kill itself via SIGSEGV, others)\n" 30 | "\t-s \tSave generated exe to \n" 31 | "\t-x\t\tDisplay all registers (FP)\n" 32 | "\t-v\t\tIncrease verbosity\n" 33 | , argv0); 34 | 35 | exit(EXIT_FAILURE); 36 | } 37 | 38 | static 39 | void _parse_opts( 40 | int argc, 41 | char **argv) { 42 | int c; 43 | 44 | while ((c = getopt(argc, argv, "s:dhrpvx")) != -1) 45 | switch (c) { 46 | case 'h': 47 | _usage(argv[0]); 48 | break; 49 | case 'd': 50 | ++options.dump; 51 | break; 52 | case 'r': 53 | ++options.raw; 54 | break; 55 | case 's': 56 | options.savefile = optarg; 57 | break; 58 | case 'p': 59 | ++options.passsig; 60 | break; 61 | case 'v': 62 | ++options.verbose; 63 | break; 64 | case 'x': 65 | ++options.allregs; 66 | break; 67 | default: 68 | exit(EXIT_FAILURE); 69 | } 70 | } 71 | 72 | int main(int argc, char **argv) { 73 | _parse_opts(argc, argv); 74 | 75 | init_rappel_dir(); 76 | 77 | if (isatty(STDIN_FILENO)) 78 | interact(argv[0]); 79 | else 80 | pipe_mode(); 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /t/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | 3 | .PHONY: amd64 4 | amd64: 5 | ./mov-amd64.sh 6 | 7 | .PHONY: x86 8 | x86: 9 | ./mov-x86.sh 10 | 11 | armv8: 12 | ./mov-armv8.sh 13 | -------------------------------------------------------------------------------- /t/mov-amd64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | echo "mov rax, 0x4141" | ../bin/rappel | grep -q "0000000000004141" 4 | -------------------------------------------------------------------------------- /t/mov-armv8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | echo "mov x0, #0x4141" | ../bin/rappel | grep -q "0000000000004141" 4 | -------------------------------------------------------------------------------- /t/mov-x86.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | echo "mov eax, 0x4141" | ../bin/rappel | grep -q "00004141" 4 | -------------------------------------------------------------------------------- /ui.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "assemble.h" 13 | #include "common.h" 14 | #include "arch.h" 15 | #include "display.h" 16 | #include "exedir.h" 17 | #include "elf_gen.h" 18 | #include "ptrace.h" 19 | #include "ptrace_arch.h" 20 | 21 | #include "ui.h" 22 | 23 | extern struct options_t options; 24 | extern int exiting; 25 | 26 | static int in_block; 27 | 28 | static 29 | char const* _prompt( 30 | EditLine *const e) 31 | { 32 | if (in_block) 33 | return "_> "; 34 | else 35 | return "> "; 36 | } 37 | 38 | static 39 | void _help(void) 40 | { 41 | printf("Commands:\n"); 42 | printf(".quit - quit\n"); 43 | printf(".help - display this help\n"); 44 | printf(".info - display registers\n"); 45 | printf(".begin - start a block, input will not be assembled/run until '.end'\n"); 46 | printf(".end - assemble and run the prior block\n"); 47 | printf(".showmap - shortcut for cat /proc//maps\n"); 48 | printf(".allregs - toggle all register display\n"); 49 | printf(".read
[amount] - read bytes of data from address using ptrace [16]\n"); 50 | printf(".write
- write data starting at address using ptrace\n"); 51 | } 52 | 53 | static 54 | void _ui_read( 55 | const pid_t child_pid, 56 | const char *line) 57 | { 58 | char *dupline = strdup(line); 59 | 60 | if (!dupline) { 61 | perror("strdup"); 62 | return; 63 | } 64 | 65 | char *saveptr; 66 | 67 | const char *dotread = strtok_r(dupline, " ", &saveptr); 68 | 69 | if (!dotread || strcasecmp(dotread, ".read")) 70 | goto bail; 71 | 72 | const char *addr_str = strtok_r(NULL, " ", &saveptr); 73 | 74 | if (!addr_str) 75 | goto bail; 76 | 77 | errno = 0; 78 | const unsigned long addr = strtoul(addr_str, NULL, 0x10); 79 | 80 | if (addr == ULONG_MAX && errno) { 81 | perror("strtoul"); 82 | goto bail; 83 | } 84 | 85 | const char *sz_str = strtok_r(NULL, " ", &saveptr); 86 | 87 | unsigned long sz = 0x10; 88 | 89 | if (sz_str && strlen(sz_str)) { 90 | errno = 0; 91 | sz = strtoul(sz_str, NULL, 0); 92 | 93 | if (sz == ULONG_MAX && errno) { 94 | perror("strtoul"); 95 | goto bail; 96 | } 97 | } 98 | 99 | uint8_t *buf = xmalloc(sz); 100 | 101 | if (!ptrace_read(child_pid, (void *)addr, buf, sz)) 102 | dump(buf, sz, addr); 103 | 104 | free(buf); 105 | 106 | bail: 107 | free(dupline); 108 | } 109 | 110 | static 111 | void _ui_write( 112 | const pid_t child_pid, 113 | const char *line) 114 | { 115 | char *dupline = strdup(line); 116 | 117 | if (!dupline) { 118 | perror("strdup"); 119 | return; 120 | } 121 | 122 | char *saveptr; 123 | 124 | const char *dotread = strtok_r(dupline, " ", &saveptr); 125 | 126 | if (!dotread || strcasecmp(dotread, ".write")) 127 | goto bail; 128 | 129 | const char *addr_str = strtok_r(NULL, " ", &saveptr); 130 | 131 | if (!addr_str) 132 | goto bail; 133 | 134 | errno = 0; 135 | const unsigned long addr = strtoul(addr_str, NULL, 0x10); 136 | 137 | if (addr == ULONG_MAX && errno) { 138 | perror("strtoul"); 139 | goto bail; 140 | } 141 | 142 | const char *val_str = strtok_r(NULL, " ", &saveptr); 143 | 144 | if (!val_str) goto bail; 145 | 146 | char *p = strchr(val_str, '\n'); 147 | if (p) *p = 0; 148 | 149 | const size_t val_len = strlen(val_str); 150 | 151 | if (val_len % 2) { 152 | printf("Memory write values should be hex encoded, even length strings\n"); 153 | goto bail; 154 | } 155 | 156 | const size_t sz = val_len / 2; 157 | 158 | uint8_t *buf = xmalloc(sz); 159 | memset(buf, 0, sz); 160 | 161 | for (size_t ii = 0; ii < val_len; ii += 2) { 162 | uint8_t a = hex_hashmap[(uint8_t)val_str[ii + 0]]; 163 | uint8_t b = hex_hashmap[(uint8_t)val_str[ii + 1]]; 164 | 165 | if (a == 0xff || b == 0xff) { 166 | printf("Memory write values should be hex encoded, even length strings\n"); 167 | } 168 | 169 | buf[ii / 2] = a << 4 | b; 170 | } 171 | 172 | ptrace_write(child_pid, (void *)addr, buf, sz); 173 | 174 | free(buf); 175 | 176 | bail: 177 | free(dupline); 178 | } 179 | 180 | static const 181 | pid_t _gen_child(void) { 182 | uint8_t buf[PAGE_SIZE]; 183 | mem_assign(buf, PAGE_SIZE, TRAP, TRAP_SZ); 184 | 185 | uint8_t *elf; 186 | const size_t elf_sz = gen_elf(&elf, options.start, (uint8_t *)buf, PAGE_SIZE); 187 | 188 | const int exe_fd = write_exe(elf, elf_sz, options.savefile); 189 | 190 | free(elf); 191 | 192 | const pid_t tracee = fork(); 193 | 194 | if (tracee < 0) { 195 | perror("fork"); 196 | exit(EXIT_FAILURE); 197 | } else if (tracee == 0) { 198 | ptrace_child(exe_fd); 199 | abort(); 200 | } 201 | 202 | // Parent 203 | close(exe_fd); 204 | 205 | return tracee; 206 | } 207 | 208 | void interact( 209 | const char *const argv_0) 210 | { 211 | EditLine *const el = el_init(argv_0, stdin, stdout, stderr); 212 | el_set(el, EL_PROMPT, &_prompt); 213 | el_set(el, EL_EDITOR, "emacs"); 214 | 215 | History *const hist = history_init(); 216 | if (!hist) { 217 | fprintf(stderr, "Could not initalize history\n"); 218 | exit(EXIT_FAILURE); 219 | } 220 | 221 | HistEvent ev; 222 | history(hist, &ev, H_SETSIZE, 100); 223 | 224 | char hist_path[PATH_MAX] = { 0 }; 225 | int ret = snprintf(hist_path, sizeof(hist_path), "%s/history", options.rappel_dir); 226 | if (ret < 0) { 227 | fprintf(stderr, "Path exceeds max path length: %s/history", options.rappel_dir); 228 | exit(EXIT_FAILURE); 229 | } 230 | 231 | history(hist, &ev, H_LOAD, hist_path); 232 | 233 | el_set(el, EL_HIST, history, hist); 234 | 235 | const pid_t child_pid = _gen_child(); 236 | 237 | verbose_printf("child process is %d\n", child_pid); 238 | 239 | if (options.verbose) _help(); 240 | 241 | char buf[PAGE_SIZE] = { 0 }; 242 | size_t buf_sz = 0; 243 | int end = 0, child_died = 0; 244 | 245 | struct proc_info_t info = { 0 }; 246 | ARCH_INIT_PROC_INFO(info); 247 | 248 | ptrace_launch(child_pid); 249 | ptrace_cont(child_pid, &info); 250 | ptrace_reap(child_pid, &info); 251 | 252 | display(&info); 253 | 254 | for (;;) { 255 | int count; 256 | const char *const line = el_gets(el, &count); 257 | 258 | if (count == -1) { 259 | perror("el_gets"); 260 | exit(EXIT_FAILURE); 261 | } 262 | 263 | // count is 0 == ^d 264 | if (!count || strcasestr(line, ".quit") || strcasestr(line, ".exit")) break; 265 | 266 | // We have input, add it to our history 267 | history(hist, &ev, H_ENTER, line); 268 | 269 | // If we start with a ., we have a command 270 | if (line[0] == '.') { 271 | if (strcasestr(line, "help")) { 272 | _help(); 273 | continue; 274 | } 275 | 276 | if (strcasestr(line, "info")) { 277 | display(&info); 278 | continue; 279 | } 280 | 281 | if (strcasestr(line, "showmap")) { 282 | char cmd[PATH_MAX] = { 0 }; 283 | snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", child_pid); 284 | 285 | if (system(cmd)) 286 | fprintf(stderr, "sh: %s failed\n", cmd); 287 | 288 | continue; 289 | } 290 | 291 | 292 | if (strcasestr(line, "read")) { 293 | _ui_read(child_pid, line); 294 | continue; 295 | } 296 | 297 | if (strcasestr(line, "write")) { 298 | _ui_write(child_pid, line); 299 | continue; 300 | } 301 | 302 | if (strcasestr(line, "allregs")) { 303 | if (strcasestr(line, "on")) 304 | options.allregs = 1; 305 | if (strcasestr(line, "off")) 306 | options.allregs = 0; 307 | continue; 308 | } 309 | 310 | if (strcasestr(line, "begin")) { 311 | in_block = 1; 312 | continue; 313 | } 314 | 315 | // Note the lack of continue. Need to fall through... 316 | if (strcasestr(line, "end")) { 317 | in_block = 0; 318 | end = 1; 319 | } 320 | } 321 | 322 | if (buf_sz + count > sizeof(buf)) { 323 | printf("Buffer full (max: 0x%zx), please use '.end'\n", sizeof(buf)); 324 | continue; 325 | } 326 | 327 | // Since we fell through, we want to avoid adding .end to our buffer 328 | if (!end) { 329 | memcpy(buf + buf_sz, line, count); 330 | buf_sz += count; 331 | } 332 | 333 | if (!in_block) { 334 | verbose_printf("Trying to assemble (%zu):\n%s", buf_sz, buf); 335 | 336 | uint8_t bytecode[PAGE_SIZE]; 337 | const size_t bytecode_sz = assemble(bytecode, sizeof(bytecode), buf, buf_sz); 338 | 339 | verbose_printf("Got asm (%zu):\n", bytecode_sz); 340 | verbose_dump(bytecode, bytecode_sz, -1); 341 | 342 | if (!bytecode_sz) { 343 | fprintf(stderr, "assembled to 0 length bytecode:\n%s", buf); 344 | } 345 | 346 | memset(buf, 0, sizeof(buf)); 347 | buf_sz = 0; 348 | end = 0; 349 | 350 | if (!bytecode_sz) { 351 | continue; 352 | } 353 | 354 | // round up to nearest ptr_sz + size of at least one trap 355 | const size_t code_buf_sz = ROUNDUP(bytecode_sz + TRAP_SZ, sizeof(long)); 356 | uint8_t *code_buf = xmalloc(code_buf_sz); 357 | mem_assign((uint8_t *)code_buf, code_buf_sz, TRAP, TRAP_SZ); 358 | memcpy(code_buf, bytecode, bytecode_sz); 359 | 360 | ptrace_write(child_pid, (void *)options.start, code_buf, code_buf_sz); 361 | 362 | free(code_buf); 363 | 364 | ptrace_reset(child_pid, options.start); 365 | 366 | ptrace_cont(child_pid, &info); 367 | 368 | if (ptrace_reap(child_pid, &info)) { 369 | child_died = 1; 370 | break; 371 | } 372 | 373 | display(&info); 374 | } 375 | } 376 | 377 | if (!child_died) 378 | ptrace_detatch(child_pid, &info); 379 | 380 | printf("\n"); 381 | 382 | // we close this one with a file pointer so we can truncate the file 383 | FILE *hist_save = fopen(hist_path, "wb"); 384 | REQUIRE (hist_save != NULL); 385 | 386 | history(hist, &ev, H_SAVE_FP, hist_save); 387 | 388 | REQUIRE (fclose(hist_save) == 0); 389 | 390 | history_end(hist); 391 | el_end(el); 392 | } 393 | --------------------------------------------------------------------------------