/* set the thread x86 registers */ void set_thread_context( struct thread *thread, const context_t *context, unsigned int flags ) { int pid = get_ptrace_tid(thread); struct dbreg dbregs; /* all other regs are handled on the client side */ assert( flags == SERVER_CTX_DEBUG_REGISTERS ); if (!suspend_for_ptrace( thread )) return; #ifdef DBREG_DRX /* needed for FreeBSD, the structure fields have changed under 5.x */ DBREG_DRX((&dbregs), 0) = context->debug.i386_regs.dr0; DBREG_DRX((&dbregs), 1) = context->debug.i386_regs.dr1; DBREG_DRX((&dbregs), 2) = context->debug.i386_regs.dr2; DBREG_DRX((&dbregs), 3) = context->debug.i386_regs.dr3; DBREG_DRX((&dbregs), 4) = 0; DBREG_DRX((&dbregs), 5) = 0; DBREG_DRX((&dbregs), 6) = context->debug.i386_regs.dr6; DBREG_DRX((&dbregs), 7) = context->debug.i386_regs.dr7; #else dbregs.dr0 = context->debug.i386_regs.dr0; dbregs.dr1 = context->debug.i386_regs.dr1; dbregs.dr2 = context->debug.i386_regs.dr2; dbregs.dr3 = context->debug.i386_regs.dr3; dbregs.dr4 = 0; dbregs.dr5 = 0; dbregs.dr6 = context->debug.i386_regs.dr6; dbregs.dr7 = context->debug.i386_regs.dr7; #endif if (ptrace( PTRACE_SETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1) file_set_error(); else if (thread->context) thread->context->debug.i386_regs = context->debug.i386_regs; /* update the cached values */ resume_after_ptrace( thread ); }
/* retrieve an LDT selector entry */ void get_selector_entry( struct thread *thread, int entry, unsigned int *base, unsigned int *limit, unsigned char *flags ) { if (!thread->process->ldt_copy) { set_error( STATUS_ACCESS_DENIED ); return; } if (entry >= 8192) { set_error( STATUS_ACCESS_VIOLATION ); return; } if (suspend_for_ptrace( thread )) { unsigned char flags_buf[sizeof(long)]; long *addr = (long *)(unsigned long)thread->process->ldt_copy + entry; if (read_thread_long( thread, addr, (long *)base ) == -1) goto done; if (read_thread_long( thread, addr + 8192, (long *)limit ) == -1) goto done; addr = (long *)(unsigned long)thread->process->ldt_copy + 2*8192 + (entry / sizeof(long)); if (read_thread_long( thread, addr, (long *)flags_buf ) == -1) goto done; *flags = flags_buf[entry % sizeof(long)]; done: resume_after_ptrace( thread ); } }
/* retrieve the thread x86 registers */ void get_thread_context( struct thread *thread, context_t *context, unsigned int flags ) { int pid = get_ptrace_tid(thread); struct dbreg dbregs; /* all other regs are handled on the client side */ assert( flags == SERVER_CTX_DEBUG_REGISTERS ); if (!suspend_for_ptrace( thread )) return; if (ptrace( PTRACE_GETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1) file_set_error(); else { #ifdef DBREG_DRX /* needed for FreeBSD, the structure fields have changed under 5.x */ context->debug.i386_regs.dr0 = DBREG_DRX((&dbregs), 0); context->debug.i386_regs.dr1 = DBREG_DRX((&dbregs), 1); context->debug.i386_regs.dr2 = DBREG_DRX((&dbregs), 2); context->debug.i386_regs.dr3 = DBREG_DRX((&dbregs), 3); context->debug.i386_regs.dr6 = DBREG_DRX((&dbregs), 6); context->debug.i386_regs.dr7 = DBREG_DRX((&dbregs), 7); #else context->debug.i386_regs.dr0 = dbregs.dr0; context->debug.i386_regs.dr1 = dbregs.dr1; context->debug.i386_regs.dr2 = dbregs.dr2; context->debug.i386_regs.dr3 = dbregs.dr3; context->debug.i386_regs.dr6 = dbregs.dr6; context->debug.i386_regs.dr7 = dbregs.dr7; #endif context->flags |= SERVER_CTX_DEBUG_REGISTERS; } resume_after_ptrace( thread ); }
/* set the thread x86 registers */ void set_thread_context( struct thread *thread, const context_t *context, unsigned int flags ) { int pid = get_ptrace_tid( thread ); /* all other regs are handled on the client side */ assert( flags == SERVER_CTX_DEBUG_REGISTERS ); if (!suspend_for_ptrace( thread )) return; switch (context->cpu) { case CPU_x86: /* Linux 2.6.33+ does DR0-DR3 alignment validation, so it has to know LEN bits first */ if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.i386_regs.dr7 & 0xffff0000 ) == -1) goto error; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(0), context->debug.i386_regs.dr0 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr0 = context->debug.i386_regs.dr0; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(1), context->debug.i386_regs.dr1 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr1 = context->debug.i386_regs.dr1; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(2), context->debug.i386_regs.dr2 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr2 = context->debug.i386_regs.dr2; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(3), context->debug.i386_regs.dr3 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr3 = context->debug.i386_regs.dr3; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(6), context->debug.i386_regs.dr6 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr6 = context->debug.i386_regs.dr6; /* Linux 2.6.33+ needs enable bits set briefly to update value returned by PEEKUSER later */ ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.i386_regs.dr7 | 0x55 ); if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.i386_regs.dr7 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr7 = context->debug.i386_regs.dr7; break; case CPU_x86_64: if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.x86_64_regs.dr7 & 0xffff0000 ) == -1) goto error; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(0), context->debug.x86_64_regs.dr0 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr0 = context->debug.x86_64_regs.dr0; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(1), context->debug.x86_64_regs.dr1 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr1 = context->debug.x86_64_regs.dr1; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(2), context->debug.x86_64_regs.dr2 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr2 = context->debug.x86_64_regs.dr2; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(3), context->debug.x86_64_regs.dr3 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr3 = context->debug.x86_64_regs.dr3; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(6), context->debug.x86_64_regs.dr6 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr6 = context->debug.x86_64_regs.dr6; ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.x86_64_regs.dr7 | 0x55 ); if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.x86_64_regs.dr7 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr7 = context->debug.x86_64_regs.dr7; break; default: set_error( STATUS_INVALID_PARAMETER ); } resume_after_ptrace( thread ); return; error: file_set_error(); resume_after_ptrace( thread ); }
/* set the thread x86 registers */ void set_thread_context( struct thread *thread, const context_t *context, unsigned int flags ) { int pid = get_ptrace_tid( thread ); /* all other regs are handled on the client side */ assert( flags == SERVER_CTX_DEBUG_REGISTERS ); if (!suspend_for_ptrace( thread )) return; switch (context->cpu) { case CPU_x86: if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(0), context->debug.i386_regs.dr0 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr0 = context->debug.i386_regs.dr0; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(1), context->debug.i386_regs.dr1 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr1 = context->debug.i386_regs.dr1; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(2), context->debug.i386_regs.dr2 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr2 = context->debug.i386_regs.dr2; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(3), context->debug.i386_regs.dr3 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr3 = context->debug.i386_regs.dr3; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(6), context->debug.i386_regs.dr6 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr6 = context->debug.i386_regs.dr6; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.i386_regs.dr7 ) == -1) goto error; if (thread->context) thread->context->debug.i386_regs.dr7 = context->debug.i386_regs.dr7; break; case CPU_x86_64: if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(0), context->debug.x86_64_regs.dr0 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr0 = context->debug.x86_64_regs.dr0; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(1), context->debug.x86_64_regs.dr1 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr1 = context->debug.x86_64_regs.dr1; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(2), context->debug.x86_64_regs.dr2 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr2 = context->debug.x86_64_regs.dr2; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(3), context->debug.x86_64_regs.dr3 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr3 = context->debug.x86_64_regs.dr3; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(6), context->debug.x86_64_regs.dr6 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr6 = context->debug.x86_64_regs.dr6; if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->debug.x86_64_regs.dr7 ) == -1) goto error; if (thread->context) thread->context->debug.x86_64_regs.dr7 = context->debug.x86_64_regs.dr7; break; default: set_error( STATUS_INVALID_PARAMETER ); } resume_after_ptrace( thread ); return; error: file_set_error(); resume_after_ptrace( thread ); }
/* retrieve the thread x86 registers */ void get_thread_context( struct thread *thread, context_t *context, unsigned int flags ) { int i, pid = get_ptrace_tid(thread); long data[8]; /* all other regs are handled on the client side */ assert( flags == SERVER_CTX_DEBUG_REGISTERS ); if (!suspend_for_ptrace( thread )) return; for (i = 0; i < 8; i++) { if (i == 4 || i == 5) continue; errno = 0; data[i] = ptrace( PTRACE_PEEKUSER, pid, DR_OFFSET(i), 0 ); if ((data[i] == -1) && errno) { file_set_error(); goto done; } } switch (context->cpu) { case CPU_x86: context->debug.i386_regs.dr0 = data[0]; context->debug.i386_regs.dr1 = data[1]; context->debug.i386_regs.dr2 = data[2]; context->debug.i386_regs.dr3 = data[3]; context->debug.i386_regs.dr6 = data[6]; context->debug.i386_regs.dr7 = data[7]; break; case CPU_x86_64: context->debug.x86_64_regs.dr0 = data[0]; context->debug.x86_64_regs.dr1 = data[1]; context->debug.x86_64_regs.dr2 = data[2]; context->debug.x86_64_regs.dr3 = data[3]; context->debug.x86_64_regs.dr6 = data[6]; context->debug.x86_64_regs.dr7 = data[7]; break; default: set_error( STATUS_INVALID_PARAMETER ); goto done; } context->flags |= SERVER_CTX_DEBUG_REGISTERS; done: resume_after_ptrace( thread ); }
/* write data to a process memory space */ int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, const char *src ) { struct thread *thread = get_ptrace_thread( process ); int ret = 0; long data = 0; data_size_t len; long *addr; unsigned long first_mask, first_offset, last_mask, last_offset; if (!thread) return 0; if ((unsigned long)ptr != ptr) { set_error( STATUS_ACCESS_DENIED ); return 0; } /* compute the mask for the first long */ first_mask = ~0; first_offset = ptr % sizeof(long); memset( &first_mask, 0, first_offset ); /* compute the mask for the last long */ last_offset = (size + first_offset) % sizeof(long); if (!last_offset) last_offset = sizeof(long); last_mask = 0; memset( &last_mask, 0xff, last_offset ); addr = (long *)(unsigned long)(ptr - first_offset); len = (size + first_offset + sizeof(long) - 1) / sizeof(long); if (suspend_for_ptrace( thread )) { if (!check_process_write_access( thread, addr, len )) { set_error( STATUS_ACCESS_DENIED ); goto done; } if (len > 3) { char procmem[24]; int fd; sprintf( procmem, "/proc/%u/mem", process->unix_pid ); if ((fd = open( procmem, O_WRONLY )) != -1) { ssize_t r = pwrite( fd, src, size, ptr ); close( fd ); if (r == size) { ret = 1; goto done; } } } /* first word is special */ if (len > 1) { memcpy( (char *)&data + first_offset, src, sizeof(long) - first_offset ); src += sizeof(long) - first_offset; if (write_thread_long( thread, addr++, data, first_mask ) == -1) goto done; first_offset = 0; len--; } else last_mask &= first_mask; while (len > 1) { memcpy( &data, src, sizeof(long) ); src += sizeof(long); if (write_thread_long( thread, addr++, data, ~0ul ) == -1) goto done; len--; } /* last word is special too */ memcpy( (char *)&data + first_offset, src, last_offset - first_offset ); if (write_thread_long( thread, addr, data, last_mask ) == -1) goto done; ret = 1; done: resume_after_ptrace( thread ); } return ret; }
/* read data from a process memory space */ int read_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, char *dest ) { struct thread *thread = get_ptrace_thread( process ); unsigned int first_offset, last_offset, len; long data, *addr; if (!thread) return 0; if ((unsigned long)ptr != ptr) { set_error( STATUS_ACCESS_DENIED ); return 0; } first_offset = ptr % sizeof(long); last_offset = (size + first_offset) % sizeof(long); if (!last_offset) last_offset = sizeof(long); addr = (long *)(unsigned long)(ptr - first_offset); len = (size + first_offset + sizeof(long) - 1) / sizeof(long); if (suspend_for_ptrace( thread )) { if (len > 3) /* /proc/pid/mem should be faster for large sizes */ { char procmem[24]; int fd; sprintf( procmem, "/proc/%u/mem", process->unix_pid ); if ((fd = open( procmem, O_RDONLY )) != -1) { ssize_t ret = pread( fd, dest, size, ptr ); close( fd ); if (ret == size) { len = 0; goto done; } } } if (len > 1) { if (read_thread_long( thread, addr++, &data ) == -1) goto done; memcpy( dest, (char *)&data + first_offset, sizeof(long) - first_offset ); dest += sizeof(long) - first_offset; first_offset = 0; len--; } while (len > 1) { if (read_thread_long( thread, addr++, &data ) == -1) goto done; memcpy( dest, &data, sizeof(long) ); dest += sizeof(long); len--; } if (read_thread_long( thread, addr++, &data ) == -1) goto done; memcpy( dest, (char *)&data + first_offset, last_offset - first_offset ); len--; done: resume_after_ptrace( thread ); } return !len; }