Skip to content

Instantly share code, notes, and snippets.

@Keno
Created December 24, 2019 21:26
Show Gist options
  • Save Keno/e0231a5f6f8e255eff4842931d555325 to your computer and use it in GitHub Desktop.
Save Keno/e0231a5f6f8e255eff4842931d555325 to your computer and use it in GitHub Desktop.
watchpoint.c
#include "util.h"
#define ARRAY_SIZE(a) \
((sizeof(a) / sizeof(*(a))) / \
!(sizeof(a) % sizeof(*(a))))
struct DebugControl {
uintptr_t dr0_local : 1;
uintptr_t dr0_global : 1;
uintptr_t dr1_local : 1;
uintptr_t dr1_global : 1;
uintptr_t dr2_local : 1;
uintptr_t dr2_global : 1;
uintptr_t dr3_local : 1;
uintptr_t dr3_global : 1;
uintptr_t ignored : 8;
uintptr_t dr0_type : 2;
uintptr_t dr0_len : 2;
uintptr_t dr1_type : 2;
uintptr_t dr1_len : 2;
uintptr_t dr2_type : 2;
uintptr_t dr2_len : 2;
uintptr_t dr3_type : 2;
uintptr_t dr3_len : 2;
};
struct PackedDebugControl {
union {
struct DebugControl ctrl;
uintptr_t val;
};
};
struct test_instance {
size_t start_offset;
size_t break_offset;
size_t n;
};
static void breakpoint(void)
{
__asm__("int $3");
}
static void do_memset(void *start, size_t n)
{
uintptr_t a = 0;
__asm__("rep stosb\n\t" ::"a"(a), "c"(n), "D"(start));
}
static void cont_wait_stop(pid_t child) {
int status;
test_assert(0 == ptrace(PTRACE_CONT, child, NULL, NULL));
test_assert(child == waitpid(child, &status, 0));
test_assert(status == ((SIGTRAP << 8) | 0x7f));
}
static void advance_rip(pid_t child) {
struct user_regs_struct regs;
struct iovec iov;
pid_t wret;
iov.iov_base = &regs;
iov.iov_len = sizeof(regs);
test_assert(0 == ptrace(PTRACE_GETREGSET, child, (void*)NT_PRSTATUS, &iov));
regs.rip += 1;
test_assert(0 == ptrace(PTRACE_SETREGSET, child, (void*)NT_PRSTATUS, &iov));
}
int main(void) {
pid_t child;
int status;
int pipe_fds[2];
struct test_instance *program;
size_t nprograms;
// Setup program
nprograms = 0x2020;
program = malloc(nprograms * sizeof(struct test_instance));
for (int i = 0; i < nprograms; ++i) {
program[i].start_offset = 0;
program[i].break_offset = i;
program[i].n = 0x2020;
}
test_assert(0 == pipe(pipe_fds));
size_t max_size = 0;
for (int i = 0; i < nprograms; ++i) {
if (program[i].n > max_size)
max_size = program[i].n;
}
void *ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (0 == (child = fork())) {
char ch;
read(pipe_fds[0], &ch, 1);
for (int i = 0; i < nprograms; ++i) {
breakpoint();
do_memset(ptr+program[i].start_offset, program[i].n);
}
return 77;
}
test_assert(0 == ptrace(PTRACE_ATTACH, child, NULL, NULL));
test_assert(child == waitpid(child, &status, 0));
test_assert(status == ((SIGSTOP << 8) | 0x7f));
test_assert(1 == write(pipe_fds[1], "x", 1));
atomic_printf("N\tOvershoot\n");
for (int i = 0; i < nprograms; ++i) {
// Wait till we're stopped at the breakpoint
cont_wait_stop(child);
advance_rip(child);
// Setup watchpoint
test_assert(0 == ptrace(PTRACE_POKEUSER, child,
(void*)offsetof(struct user, u_debugreg[0]),
(void*)ptr + program[i].break_offset));
struct PackedDebugControl ctrl;
assert(sizeof(struct DebugControl) == sizeof(uintptr_t));
memset(&ctrl, 0, sizeof(struct PackedDebugControl));
ctrl.ctrl.dr0_local = 1;
ctrl.ctrl.dr0_type = 0b01;
//ctrl.ctrl.dr0_len = 0;
test_assert(0 == ptrace(PTRACE_POKEUSER, child,
(void*)offsetof(struct user, u_debugreg[7]),
(void*)ctrl.val));
// Continue to watchpoint
test_assert(0 == ptrace(PTRACE_CONT, child, NULL, NULL));
test_assert(child == waitpid(child, &status, 0));
// Read rdi
struct user_regs_struct regs;
struct iovec iov;
pid_t wret;
iov.iov_base = &regs;
iov.iov_len = sizeof(regs);
test_assert(0 == ptrace(PTRACE_GETREGSET, child, (void*)NT_PRSTATUS, &iov));
atomic_printf("%ld\t%lld\n", program[i].break_offset, regs.rdi-program[i].break_offset-(uintptr_t)ptr);
// Clear watchpoint
test_assert(0 == ptrace(PTRACE_POKEUSER, child,
(void*)offsetof(struct user, u_debugreg[7]),
(void*)0));
}
test_assert(0 == ptrace(PTRACE_DETACH, child, NULL, NULL));
test_assert(child == waitpid(child, &status, 0));
test_assert(WIFEXITED(status));
test_assert(WEXITSTATUS(status) == 77);
atomic_puts("EXIT-SUCCESS");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment