Created
May 23, 2020 05:18
-
-
Save Keno/cde691b26e32373307fb7449ad305739 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <signal.h> | |
#include <asm/ptrace.h> | |
#include <sys/ptrace.h> | |
#include <sys/syscall.h> | |
#include <sys/wait.h> | |
#include <sys/uio.h> | |
#include <linux/elf.h> | |
#define __asm_syscall(...) do { \ | |
__asm__ __volatile__ ( "svc 0" \ | |
: "=r"(x0) : __VA_ARGS__ : "memory", "cc"); \ | |
return x0; \ | |
} while (0) | |
#define PTRACE_SYSEMU 31 | |
static inline long __syscall3(long n, long a, long b, long c) | |
{ | |
register long x8 __asm__("x8") = n; | |
register long x0 __asm__("x0") = a; | |
register long x1 __asm__("x1") = b; | |
register long x2 __asm__("x2") = c; | |
__asm_syscall("r"(x8), "0"(x0), "r"(x1), "r"(x2)); | |
} | |
char hello[] = "Hello World\n"; | |
volatile int values[8] = {0, 1, 2, 3, 4, 5, 6, 7}; | |
void do_child(int with_ptrace) { | |
if (with_ptrace) { | |
pid_t child = getpid(); | |
ptrace(PTRACE_TRACEME, 0, 0, 0); | |
__syscall3(SYS_tgkill, child, child, SIGSTOP); | |
} | |
int v1 = values[0]; | |
int v2 = values[1]; | |
int v3 = values[2]; | |
int v4 = values[3]; | |
int v5 = values[4]; | |
int v6 = values[5]; | |
__syscall3(SYS_write, 0, (long)hello, sizeof(hello)); | |
assert(v1 == values[0]); | |
assert(v2 == values[1]); | |
assert(v3 == values[2]); | |
assert(v4 == values[3]); | |
assert(v5 == values[4]); | |
assert(v6 == values[5]); | |
exit(0); | |
} | |
int main(void) { | |
pid_t child; | |
int status; | |
// First without the ptracer attached | |
if (0 == (child = fork())) { | |
do_child(0); | |
} | |
/* Wait until the tracee stops */ | |
assert(child == waitpid(child, &status, 0)); | |
assert(WIFEXITED(status) && WEXITSTATUS(status) == 0); | |
// Next with the ptracer attached | |
if (0 == (child = fork())) { | |
do_child(1); | |
} | |
/* Wait until the tracee stops */ | |
assert(child == waitpid(child, &status, 0)); | |
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP); | |
/* Get out of the SIGSTOP */ | |
assert(0 == ptrace(PTRACE_SINGLESTEP, child, 0, 0)); | |
/* Wait until the tracee stops */ | |
assert(child == waitpid(child, &status, 0)); | |
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP); | |
/* Continue to entry of the syscall */ | |
assert(0 == ptrace(PTRACE_SYSEMU, child, 0, 0)); | |
/* Wait until the tracee stops */ | |
assert(child == waitpid(child, &status, 0)); | |
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP); | |
/* Continue to exit of the syscall */ | |
assert(0 == ptrace(PTRACE_SYSCALL, child, 0, 0)); | |
/* Wait until the tracee stops */ | |
assert(child == waitpid(child, &status, 0)); | |
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP); | |
// Alright, save the registers we had here, we're gonna do | |
// some schenanigans. | |
struct user_pt_regs regs, regs2; | |
memset(®s, 0, sizeof(regs)); | |
memset(®s2, 0, sizeof(regs)); | |
struct iovec iov = { .iov_base = ®s, .iov_len = sizeof(regs) }; | |
assert(0 == ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov)); | |
// Save these registers, so we can restore them later | |
memcpy(®s2, ®s, sizeof(regs)); | |
// All right, now puppeteer the tracee to do our bidding. | |
regs.regs[1] += 6; | |
regs.regs[2] -= 6; | |
regs.pc -= 4; | |
assert(0 == ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov)); | |
// Run the syscall | |
assert(0 == ptrace(PTRACE_SINGLESTEP, child, 0, 0)); | |
/* Wait until the tracee stops */ | |
assert(child == waitpid(child, &status, 0)); | |
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP); | |
// Alright, we've had our fun, reset the registers | |
memcpy(®s, ®s2, sizeof(regs)); | |
// Emulate a successful system call of the full size | |
regs.regs[0] = regs.regs[2]; | |
assert(0 == ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov)); | |
assert(0 == ptrace(PTRACE_CONT, child, 0, 0)); | |
/* Wait until the tracee exits */ | |
assert(child == waitpid(child, &status, 0)); | |
assert(WIFEXITED(status) && WEXITSTATUS(status) == 0); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment