Skip to content

Instantly share code, notes, and snippets.

@Keno
Created May 23, 2020 05:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Keno/cde691b26e32373307fb7449ad305739 to your computer and use it in GitHub Desktop.
Save Keno/cde691b26e32373307fb7449ad305739 to your computer and use it in GitHub Desktop.
#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(&regs, 0, sizeof(regs));
memset(&regs2, 0, sizeof(regs));
struct iovec iov = { .iov_base = &regs, .iov_len = sizeof(regs) };
assert(0 == ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov));
// Save these registers, so we can restore them later
memcpy(&regs2, &regs, 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(&regs, &regs2, 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