Quantcast
Channel: Nikolay Igotti
Viewing all articles
Browse latest Browse all 30

memory debugger for linux

$
0
0
Sometimes it's required for an application to catch all memory accesses to particular location, for example for debugging purposes. It could be done using mprotect call, but there's another, specific for x86, but very effective way -- using x86 debugging registers. Usually, debuggers, when setting watchpoints, use this kind of API, but sometimes condition you're trying to check is too complex for debugger to be really helpful, or happens too frequently, so that you have to wait literally days for condition to happen.
#include<signal.h>
#include<syscall.h>
#include<unistd.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>

#include<unistd.h>
#include<linux/stat.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<sys/ptrace.h>
#include<sys/types.h>
#include<sys/wait.h>enum {
  USER_DR0 = 63*sizeof(void*),
  USER_DR1 = 64*sizeof(void*),
  USER_DR2 = 65*sizeof(void*),
  USER_DR3 = 66*sizeof(void*),
  USER_DR4 = 67*sizeof(void*),
  USER_DR5 = 68*sizeof(void*),
  USER_DR6 = 69*sizeof(void*),
  USER_DR7 = 70*sizeof(void*),
};typedefenum {
  BREAK_ON_EXEC  = 0x00,
  BREAK_ON_WRITE = 0x01,
  BREAK_ON_RW    = 0x03,
} BreakFlags;typedefenum {
  LEN_1 = 0x00,
  LEN_2 = 0x01,
  LEN_4 = 0x03,
} DataLength;typedefstruct {int        dr0_local:1;int        dr0_global:1;int        dr1_local:1;int        dr1_global:1;int        dr2_local:1;int        dr2_global:1;int        dr3_local:1;int        dr3_global:1;int        exact_local:1;int        exact_global:1;int        reserved:6;
  BreakFlags dr0_break:2;
  DataLength dr0_len:2;
  BreakFlags dr1_break:2;
  DataLength dr1_len:2;
  BreakFlags dr2_break:2;
  DataLength dr2_len:2;
  BreakFlags dr3_break:2;
  DataLength dr3_len:2;
} DR7;voidaddwatchpoint(pid_t pid, void* address, void (*handler)(int, siginfo_t, void*))
{
  pid_t child;struct sigaction trap_action;

  sigaction(SIGTRAP, NULL, &trap_action);
  trap_action.sa_handler = (void (*)(int))handler;
  trap_action.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER;
  sigaction(SIGTRAP, &trap_action, NULL);if ((child=fork()) == 0) {// setup debugging registers    uintptr_t dr0 = (uintptr_t)address;
    DR7 dr7 = {0};
    dr7.dr0_local = 1;
    dr7.dr0_break = BREAK_ON_WRITE;
    dr7.dr0_len   = LEN_4;if (ptrace(PTRACE_ATTACH, pid, NULL, NULL)) {		
      perror("attach");return;
    }while (ptrace(PTRACE_POKEUSER, pid, USER_DR0, dr0));

    printf("watchpoint for pid: %u, DR0: %x, DR7: %x\n", 
           pid, dr0, *(uintptr_t*)(&dr7));   if (ptrace(PTRACE_POKEUSER, pid, USER_DR7, dr7)) {
      perror("poke dr7");
      ptrace(PTRACE_DETACH, pid, NULL, NULL);return;
    }if (ptrace(PTRACE_POKEUSER, pid, USER_DR6, 0)) {
      perror("poke dr6");
      ptrace(PTRACE_DETACH, pid, NULL, NULL);return;
    }    if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) {
      perror("detach");
    }return;
  }

  waitpid(child, NULL, 0);
}

volatileint var = 0;voidtrap(int sig, siginfo_t info, void* uc) { if (var == 50) {
    printf("caught: %d, run gdb - %d\n", var, getpid());while (1) {}
  }
}

pid_t gettid() {return syscall(__NR_gettid);
}intmain(int argc, char * argv[])
{int i;

  addwatchpoint(gettid(), (void*)&var, trap);for (i=0; i<100; i++) {
    var++;
  }return 0;  
}

Viewing all articles
Browse latest Browse all 30

Trending Articles