Permalink
Browse files

fixed stability issues by injecting into VDSO

  • Loading branch information...
1 parent 718b658 commit de281611cfdaf0b393f0d4d46715146110dfac09 @pforemski pforemski committed Feb 29, 2012
Showing with 122 additions and 55 deletions.
  1. +6 −7 doc/tracedump.1.markdown
  2. +54 −22 inject.c
  3. +2 −1 pid.c
  4. +3 −3 port.c
  5. +39 −16 ptrace.c
  6. +2 −0 ptrace.h
  7. +12 −6 tracedump.c
  8. +4 −0 tracedump.h
@@ -52,18 +52,17 @@ separated with spaces), or a command to execute with execvp(3).
## LIMITATIONS
- * currently works on x86-32 Linux hosts only
* IP packets past the first fragment will not be captured
- * maximum number of monitored ports is limited to less than 300 ports, due to limits on the
- BPF filter attached to the sniffing socket
+ * some applications (e.g. chromium-browser) cant be started from tracedump, but attaching to
+ existing process by PID works
## BUGS
- * sometimes the traced process segfaults - more testing needed
- * cant start chromium-browser within tracedump, but attaching works
+ * currently works on x86-32 Linux hosts only
+ * maximum number of monitored ports is limited to less than 300 ports, due to limits on the
+ BPF filter attached to the sniffing socket
* there is a low probability of loosing TCP packets if the time distance between a particular
- bind() system call and a connect() or listen() call is greater than 60 seconds
- * probably much more :)
+ bind() system call and a connect() or listen() call is greater than 10 seconds
## SEE ALSO
View
@@ -13,11 +13,47 @@
#include "tracedump.h"
+static void _prepare(struct pid *sp)
+{
+ FILE *fp;
+ char buf[128];
+ unsigned char code[4] = { 0xcd, 0x80, 0, 0 }; // int 0x80
+
+ if (sp->vdso_addr)
+ return;
+
+ /* find VDSO address */
+ snprintf(buf, sizeof buf, "/proc/%d/maps", sp->pid);
+ fp = fopen(buf, "r");
+ while (fgets(buf, sizeof buf, fp)) {
+ if (strlen(buf) < 49 + 6)
+ continue;
+
+ /* found it? */
+ if (strncmp(buf + 49, "[vdso]", 6) == 0) {
+ /* parse address */
+ buf[8] = 0;
+ sp->vdso_addr = strtoul(buf, NULL, 16);
+ break;
+ }
+ }
+ fclose(fp);
+
+ if (!sp->vdso_addr)
+ dbg(0, "pid %d: no [vdso] memory region\n", sp->pid);
+
+#ifdef VDSO_PIGGYBACK
+ /* on x86-32 the INT 0x80 is already there :) */
+ sp->vdso_addr += 0x406;
+#else
+ /* inject our code */
+ dbg(3, "pid %d: installing code at 0x%x\n", sp->pid, sp->vdso_addr);
+ ptrace_write(sp, sp->vdso_addr, code, sizeof code);
+#endif
+}
+
int32_t inject_socketcall(struct tracedump *td, struct pid *sp, uint32_t sc_code, ...)
{
- /* int 0x80, int3 */
- unsigned char code[4] = { 0xcd, 0x80, 0xcc, 0 };
- char backup[4];
struct user_regs_struct regs, regs2;
int ss_vals, ss_mem, ss;
va_list vl;
@@ -28,8 +64,6 @@ int32_t inject_socketcall(struct tracedump *td, struct pid *sp, uint32_t sc_code
uint32_t *stack32;
int i, j;
- dbg(4, "inject_socketcall(pid=%d, code=%d)\n", sp->pid, sc_code);
-
/*
* get the required amount of stack space
*/
@@ -58,7 +92,6 @@ int32_t inject_socketcall(struct tracedump *td, struct pid *sp, uint32_t sc_code
*/
ptrace_getregs(sp, &regs);
memcpy(&regs2, &regs, sizeof regs);
- ptrace_read(sp, regs.eip, backup, sizeof backup);
/*
* write the stack
@@ -93,13 +126,16 @@ int32_t inject_socketcall(struct tracedump *td, struct pid *sp, uint32_t sc_code
/*
* write the code and run
*/
- regs2.eax = 102; // socketcall
+ _prepare(sp);
+
+ regs2.eax = 102; // socketcall
regs2.ebx = sc_code;
regs2.ecx = regs.esp - ss;
+ regs2.eip = sp->vdso_addr; // gateway to int3
- ptrace_write(sp, regs.eip, code, sizeof code);
ptrace_setregs(sp, &regs2);
- ptrace_cont(sp, 0, true); // sync call, wait for it
+ ptrace_cont_syscall(sp, 0, true); // enter...
+ ptrace_cont_syscall(sp, 0, true); // ...and exit
/*
* read back
@@ -124,9 +160,7 @@ int32_t inject_socketcall(struct tracedump *td, struct pid *sp, uint32_t sc_code
va_end(vl);
/* restore */
- ptrace_write(sp, regs.eip, backup, sizeof backup);
ptrace_setregs(sp, &regs);
-
mmatic_free(stack);
return regs2.eax;
@@ -152,25 +186,23 @@ void inject_escape_socketcall(struct tracedump *td, struct pid *sp)
void inject_restore_socketcall(struct tracedump *td, struct pid *sp)
{
- /* int 0x80, int3 */
- unsigned char code[4] = { 0xcd, 0x80, 0xcc, 0 };
- char backup[4];
struct user_regs_struct regs2;
- /* backup */
- ptrace_read(sp, sp->regs.eip, backup, 4);
+ /* prepare */
+ _prepare(sp);
+ memcpy(&regs2, &sp->regs, sizeof regs2);
+ regs2.eax = sp->regs.orig_eax;
+ regs2.eip = sp->vdso_addr;
/* exec */
- sp->regs.eax = sp->regs.orig_eax;
- ptrace_setregs(sp, &sp->regs);
- ptrace_write(sp, sp->regs.eip, code, 4);
- ptrace_cont(sp, 0, true);
+ ptrace_setregs(sp, &regs2);
+ ptrace_cont_syscall(sp, 0, true);
+ ptrace_cont_syscall(sp, 0, true);
- /* read the return code */
+ /* rewrite the return code */
ptrace_getregs(sp, &regs2);
sp->regs.eax = regs2.eax;
/* restore */
ptrace_setregs(sp, &sp->regs);
- ptrace_write(sp, sp->regs.eip, backup, 4);
}
View
@@ -18,9 +18,10 @@ struct pid *pid_get(struct tracedump *td, pid_t pid)
if (!sp) {
dbg(7, "new pid %d\n", pid);
sp = mmatic_zalloc(td->mm, sizeof *sp);
+ thash_uint_set(td->pids, pid, sp);
+
sp->td = td;
sp->pid = pid;
- thash_uint_set(td->pids, pid, sp);
}
return sp;
View
@@ -22,7 +22,7 @@ static void *gc_thread(void *arg)
pthread_sigmask(SIG_SETMASK, &ss, NULL);
while (1) {
- sleep(60);
+ sleep(3);
/* read list of active tcp/udp ports */
tcp = port_list(td, true);
@@ -42,9 +42,9 @@ static void *gc_thread(void *arg)
/* TCP */
thash_reset(td->tcp_ports);
while ((sp = thash_uint_iter(td->tcp_ports, &port))) {
- /* skip ports "younger" than 60 secs
+ /* skip ports "younger" than 10 secs
* workaround that autobound TCP ports are not visible in procfs - Linux bug? */
- if (pjf_timediff(&now, &sp->since)/1000000 < 60)
+ if (pjf_timediff(&now, &sp->since)/1000000 < 10)
continue;
if (!PORT_ISSET(tcp, port)) {
View
@@ -33,7 +33,6 @@ static long _run_ptrace(enum __ptrace_request request, struct pid *sp,
}
dbg(1, "%s(req %d, pid %d): %s\n", func, request, pid, strerror(errno));
- dbg(1, "pid %d state: %s", pid_state(pid));
} else {
dbg(5, "%s(req %d, pid %d)\n", func, request, pid);
}
@@ -80,16 +79,26 @@ int ptrace_attach_pid(struct pid *sp, void (*cb)(struct pid *sp))
int ptrace_attach_child(struct pid *sp, void (*cb)(struct pid *sp))
{
- int status;
+ int rc, status;
+ FILE *fp;
+ char buf[128];
- ptrace_wait(sp, &status);
- if (WIFSTOPPED(status) &&
+ rc = waitpid(sp->pid, &status, __WALL);
+ if (rc == sp->pid && WIFSTOPPED(status) &&
(WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTRAP)) {
- dbg(2, "attached to PID %d\n", sp->pid);
+ /* read PID name */
+ snprintf(buf, sizeof buf, "/proc/%d/cmdline", sp->pid);
+ fp = fopen(buf, "r");
+ fgets(sp->cmdline, sizeof sp->cmdline, fp);
+ fclose(fp);
+ dbg(1, "attached to PID %d (%s)\n", sp->pid, sp->cmdline);
+
+ /* set ptrace options */
run_ptrace(PTRACE_SETOPTIONS, sp, NULL,
- PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE);
+ PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACESYSGOOD);
+ /* callback */
if (cb)
cb(sp);
@@ -124,11 +133,20 @@ int ptrace_wait(struct pid *sp, int *st)
if (WIFEXITED(status))
dbg(2, "wait(%d): process exited with status %d\n", pid, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
- dbg(1, "wait(%d): process terminated with signal %d\n", pid, WTERMSIG(status));
- else if (WIFSTOPPED(status) &&
- WSTOPSIG(status) != SIGSTOP &&
- WSTOPSIG(status) != SIGTRAP)
- dbg(2, "wait(%d): process received signal %d\n", pid, WSTOPSIG(status));
+ dbg(2, "wait(%d): process terminated with signal %d\n", pid, WTERMSIG(status));
+ else if (WIFSTOPPED(status)) {
+ switch (WSTOPSIG(status)) {
+ case SIGSTOP:
+ case SIGTRAP:
+ case SIGTRAPS:
+ break;
+ case SIGSEGV:
+ dbg(1, "wait(%d): process received SIGSEGV - segmentation fault\n", pid);
+ break;
+ default:
+ dbg(2, "wait(%d): process received signal %d\n", pid, WSTOPSIG(status));
+ }
+ }
if (st)
*st = status;
@@ -150,7 +168,7 @@ static inline void _ptrace_cont(bool syscall, struct pid *sp, unsigned long sig,
break;
sig = WSTOPSIG(status);
- if (sig == SIGSTOP || sig == SIGTRAP)
+ if (sig == SIGTRAP || sig == SIGTRAPS)
break;
}
}
@@ -178,10 +196,15 @@ void ptrace_detach(struct pid *sp, unsigned long sig)
ptrace_wait(sp, &status);
if (WIFSTOPPED(status)) {
- if (WSTOPSIG(status) == SIGTRAP || WSTOPSIG(status) == SIGSTOP)
- run_ptrace(PTRACE_DETACH, sp, NULL, (void *) SIGCONT);
- else
- run_ptrace(PTRACE_DETACH, sp, NULL, (void *) WSTOPSIG(status));
+ switch (WSTOPSIG(status)) {
+ case SIGSTOP:
+ case SIGTRAP:
+ case SIGTRAPS:
+ run_ptrace(PTRACE_DETACH, sp, NULL, (void *) SIGCONT);
+ break;
+ default:
+ run_ptrace(PTRACE_DETACH, sp, NULL, (void *) WSTOPSIG(status));
+ }
} else {
run_ptrace(PTRACE_DETACH, sp, NULL, (void *) 0);
}
View
@@ -11,6 +11,8 @@
#include <stdint.h>
#include <sys/ptrace.h>
+#define SIGTRAPS (SIGTRAP | 0x80)
+
/** Attach to process pid
* @param cb call cb before continuing
* @retval 0 success
View
@@ -211,6 +211,7 @@ int main(int argc, char *argv[])
struct user_regs_struct regs;
int status;
int stopped_pid;
+ int stopsig;
int i;
int attached;
struct sigaction sa;
@@ -289,25 +290,30 @@ int main(int argc, char *argv[])
/* wait for syscall from any pid */
stopped_pid = ptrace_wait(NULL, &status);
- /* TODO?: filter out our threads :) */
-
if (stopped_pid == -1) {
break;
} else if (WIFEXITED(status) || WIFSIGNALED(status)) {
pid_del(td, stopped_pid);
continue;
+ } else if (!WIFSTOPPED(status)) {
+ goto next_syscall;
}
/* fetch pid info */
sp = pid_get(td, stopped_pid);
/* handle signal passing */
- if (WIFSTOPPED(status)) {
- if (WSTOPSIG(status) != SIGTRAP && WSTOPSIG(status) != SIGSTOP) {
+ stopsig = WSTOPSIG(status);
+ switch (stopsig) {
+ case SIGTRAPS:
+ break;
+ case SIGTRAP:
+ case SIGSTOP:
+ goto next_syscall;
+ default:
/* pass the signal to child */
- ptrace_cont_syscall(sp, WSTOPSIG(status), false);
+ ptrace_cont_syscall(sp, stopsig, false);
continue;
- }
}
/* get regs, skip syscalls other than socketcall */
View
@@ -29,6 +29,8 @@
#define TRACEDUMP_VERSION "0.5"
+//#define VDSO_PIGGYBACK
+
struct tracedump;
struct pid;
struct sock;
@@ -71,12 +73,14 @@ struct tracedump {
struct pid {
struct tracedump *td; /**< path to the root data structure */
int pid; /**< process ID */
+ char cmdline[128]; /**< process cmdline */
bool in_socketcall; /**< true if in syscall 102 and its bind(), sendto() or connect() */
int code; /**< socketcall code */
struct sock *ss; /**< cache */
struct user_regs_struct regs; /**< regs backup */
+ uint32_t vdso_addr; /**< VDSO address (linux-gate.so.1) */
};
/** Represents a socket */

0 comments on commit de28161

Please sign in to comment.