/* ** Debugger with ptrace() ** ** (c) Rainer Schnitker 1994 */ #include #include #include #include #include #include #include "ansi.h" #ifdef __GO32__ #include #include #include #include #include #else #include #include #include #include #include #endif #include "syms.h" #include "unassmbl.h" #include "breakp.h" #include "dpmi.h" #include "ansi.h" extern int get_input(char *, int, int); static int pid; static int uaddr; static char **org_env; static int sigtrap; struct reg_names { char *name; int size; int ofs; }; extern struct reg_names regs[]; typedef enum { Unknown, CONT, STEP, NEXT, LIST, DUMP, DISP, SHELL, BREAK_SET, BREAK_LIST, BREAK_CLEAR, BREAK_ENABLE, BREAK_DISABLE, REGS, SET, WHERE, FIND, NPX, PROCESS, USER, SEL, HELP, QUIT } deb_commands; typedef struct { char *cp; deb_commands t; } item; static item cmds[] = { "go", CONT, "g", CONT, "cont", CONT, "c", CONT, "step", STEP, "s", STEP, "next", NEXT, "n", NEXT, "list", LIST, "l", LIST, "dump", DUMP, "d", DUMP, "disp", DISP, "reg", REGS, "r", REGS, "where", WHERE, "w", WHERE, "find", FIND, "f", FIND, "bp", BREAK_SET, "bc", BREAK_CLEAR, "bd", BREAK_DISABLE, "be", BREAK_ENABLE, "bl", BREAK_LIST, "set", SET, "process", PROCESS, "user", USER, "shell", SHELL, "sel", SEL, "npx", NPX, "help", HELP, "h", HELP, "?", HELP, "quit", QUIT, "q", QUIT, 0, 0 }; char *sig_text[] = { NULL, "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGEMT", "SIGFPE", "SIGKILL", "SIGBUS", "SIGSEGV", "SIGSYS", "SIGPIPE", "SIGALRM", "SIGTERM", "16" "17" "SIGCLD", "19" "20" "SIGBREAK"}; static ansi_flag=0; void ansi(int fg) { if (!ansi_flag) return; printf("\033[%d;%dm", (fg & A_bold) ? 1 : 0, 30+(fg&7)); } void read_memory(unsigned long addr, BYTE *buf, int len) { unsigned long long pc, r, i; for (pc = addr, i = 0; pc < addr + len; pc++, i++) { errno = 0; r = ptrace(PTRACE_PEEKDATA, pid, pc, 0); if (errno != 0) break; else buf[i] = (BYTE) r&0xff; } } int ptrace_get_register(int off) { int r; errno = 0; r = ptrace(PTRACE_PEEKUSER, pid, uaddr + off, 0); if (r == -1 && errno) perror("ptrace"); return r; } int ptrace_put_register(int off, long value) { int r; errno = 0; r = ptrace(PTRACE_POKEUSER, pid, uaddr + off, value); if (r == -1 && errno) perror("ptrace"); return r; } static void show_regs(void) { static int seg_no[6] = { CS, DS, ES, SS, FS, GS }; static char * seg_name[6] = { "cs", "ds", "es", "ss", "fs", "gs" }; int i, r, j = 1; for (i = 0; i <= 9; ++i) { errno = 0; r = ptrace_get_register(regs[i].ofs * 4); if (errno != 0) { perror("ptrace"); return; } if (++j == 4) { j = 0; putchar('\n'); } printf("%s=%.8x ", regs[i].name + 1, r); } putchar('\n'); for (i = 0; i <= 5; ++i) { errno = 0; r = ptrace_get_register(seg_no[i] * 4); if (errno != 0) { perror("ptrace"); return; } printf("%s=%.4x ", seg_name[i], r & 0xFFFF); } putchar('\n'); } static int do_run(int cmd) { int s, t, p, r; s = ptrace(cmd, pid, 0, 0); if (s < 0) { perror("ptrace"); return 0; } p = wait(&t); if (p == -1) perror("wait"); if ((t & 0377) != 0177) { printf("Program terminated (%d)\n", (t >> 8) & 0xff); exit(0); } else { if ((t >> 8) == SIGTRAP) sigtrap = 1; else { printf("process %p stopped by signal %s\n", p, sig_text[t >> 8]); sigtrap = 0; } } errno = 0; r = ptrace_get_register(EIP * 4); if (errno != 0) { perror("ptrace"); return 0; } return r; } static int run(int cmd) { int i, r, t; if (cmd == PTRACE_STEP) return do_run(PTRACE_STEP); /* RESUME, if breakpoint at eip, do one step */ r = ptrace_get_register(EIP * 4); for (i = 0; i < MAX_BP; ++i) if (bp[i].addr == r && bp[i].status == BP_ENABLE) break; if ((i < MAX_BP) && (ptrace(PTRACE_PEEKTEXT, pid, r, 0) & 0xFF) != 0xCD) do_run(PTRACE_STEP); insert_breakpoints(pid); r = do_run(PTRACE_RESUME); remove_breakpoints(pid); /* correct eip, take back 1 step */ if (r) { r -= sigtrap; ptrace(PTRACE_POKEUSER, pid, uaddr + EIP * 4, r); } return r; } static void dump(unsigned long n) { static unsigned long dump_addr; int i, j; BYTE dump_buf[16]; if (n) dump_addr = n; if (dump_addr == 0) return; for (i = 0; i < 8; ++i) { printf("%08lx: ", dump_addr); for (j = 0; j < 16; j++) { dump_buf[j] = (BYTE) ptrace(PTRACE_PEEKDATA, pid, dump_addr++, 0); printf("%02X ", dump_buf[j] & 0xFF); } putchar(' '); for (j = 0; j < 16; ++j) { char c = dump_buf[j]; if (isalnum(c) || ispunct(c)) putchar(c); else putchar('.'); } putchar('\n'); } } static void command_loop(void) { static unsigned long next_inst; static unsigned long this_inst; static int cmd; static char prompt[] = "cmd:"; unsigned long temp, list_inst; int i, j, len; char input[40]; char *argv[10]; char *name; putchar('\n'); show_regs(); list_inst = this_inst = ptrace_get_register(EIP * 4); next_inst = unassemble(this_inst, 1); while (1) { ansi(A_white); printf("%s", prompt); fflush(stdout); len = get_input(input, 40, 0); ansi(A_yellow); /* make strings */ j = 0; for (i = 0; input[i] != 0; i++) if (input[i] == ' ') { input[i] = 0; if (input[i + 1] == ' ') continue; if (input[i + 1] == 0) continue; argv[j++] = input + i + 1; } argv[j] = 0; if (*input == 0) { /* only return */ for (i = sizeof(prompt); i != 0; i--) { putchar('\b'), putchar(' '), putchar('\b'); fflush(stdout); argv[0] = NULL; } } else { /* new input */ putchar('\n'); fflush(stdout); list_inst = this_inst; for (i = 0; cmds[i].cp; i++) if (strcmp(cmds[i].cp, input) == 0) { cmd = cmds[i].t; i = -1; break; } if (i != -1 && *input) cmd = Unknown; } switch (cmd) { case HELP: printf("go g - go or continue execution\n"); printf("cont c - continue execution\n"); printf("step s - step through instruction\n"); printf("next n - step to next instruction\n"); printf("list l - list instructions at \n"); printf("dump d - dump memory at \n"); printf("disp v - display symbol v\n"); printf("reg r - show/set registers\n"); printf("where w - display list of active functions\n"); printf("find f - find a symbol/location (wildcard)\n"); printf("bp v - set breakpoint at v\n"); printf("bx no - x=List/Clear/Dis-/Enable breakpoint no\n"); printf("set m v - memory to value; size=byte/word\n"); printf("process - show process data\n"); printf("sel s - show n selectors start with s\n"); printf("shell argv - execute other program\n"); printf("quit q - terminate debugger\n"); printf("help ? - this text\n"); break; case CONT: if (argv[0]) { temp = syms_name2val(argv[0]); if (undefined_symbol) break; i = set_bp(temp); run(PTRACE_RESUME); delete_bp(i); } else run(PTRACE_RESUME); break; case STEP: if ((ptrace(PTRACE_PEEKTEXT, pid, this_inst, 0) & 0xFF) == 0xCD) goto Next; /* not INT instr */ run(PTRACE_STEP); break; case NEXT: if (last_unassemble_unconditional || last_unassemble_jump) run(PTRACE_STEP); else { Next: i = set_bp(next_inst); run(PTRACE_RESUME); delete_bp(i); } break; case LIST: if (argv[0]) { /* weitere eingabe */ temp = syms_name2val(argv[0]); if (!undefined_symbol) list_inst = temp; else list_inst = this_inst; } for (i = 1; i <= 10; i++) list_inst = unassemble(list_inst, 0); break; case DUMP: if (argv[0] == NULL) { dump(0); break; } else temp = syms_name2val(argv[0]); if (!undefined_symbol) { printf("%s: adr 0x%08x val 0x%08lx\n", argv[0], temp, ptrace(PTRACE_PEEKDATA, pid, temp, 0)); dump(temp); } break; case DISP: if (!argv[0]) break; temp = syms_name2val(argv[0]); if (!undefined_symbol) printf("%s: adr 0x%08x val 0x%08lx\n", argv[0], temp, ptrace(PTRACE_PEEKDATA, pid, temp, 0)); break; case BREAK_SET: if (argv[0]) { temp = syms_name2val(argv[0]); if (undefined_symbol) break; i = set_bp(temp); if (i>0) printf("breakpoint %d set\n",i); } break; case BREAK_CLEAR: if (argv[0]) { sscanf(argv[0], "%d", &i); delete_bp(i); } break; case BREAK_ENABLE: if (argv[0]) { sscanf(argv[0], "%d", &i); enable_bp(i); } break; case BREAK_DISABLE: if (argv[0]) { sscanf(argv[0], "%d", &i); disable_bp(i); } break; case BREAK_LIST: printf("Breakpoint list:\n"); for (i = 0; i < MAX_BP; i++) if (bp[i].status) { name = syms_val2name(bp[i].addr, &temp); printf("bp %d at %08X ", i, bp[i].addr); if (name) printf("%s", name); if (temp) printf("+%lX ", temp); if (bp[i].status == BP_DISABLE) printf(" - disabled"); printf("\n"); } break; case REGS: if (argv[0] == NULL) { show_regs(); break; } if (argv[1] == NULL) break; temp = syms_name2val(argv[1]); if (undefined_symbol) break; j = 0; for (i = 0; regs[i].name; i++) if (strcmp(regs[i].name, argv[0]) == 0) { int r = ptrace_get_register(regs[i].ofs * 4); j=1; switch (regs[i].size) { case 0: r = (r & 0xffff00ff) | ((temp & 0xff) << 8); break; case 1: * (char *) & r = * (char *) & temp; break; case 2: * (short *) & r = * (short *) & temp; break; case 4: r = temp; break; } ptrace_put_register(regs[i].ofs * 4, r); break; } if (!j) puts("register not found"); break; case SHELL: if (argv[0] == NULL) break; if (spawnvpe(P_WAIT, argv[0], argv, org_env) < 0) printf("error spawn %s\n", argv[0]); break; case FIND: if (argv[0] == NULL) break; if (strpbrk(argv[0], "*?") != NULL) { syms_listwild(argv[0]); break; } temp = syms_name2val(argv[0]); if (!undefined_symbol) { name = syms_val2name(this_inst, &temp); printf("0x%08lx %s", this_inst, name); if (temp) printf("+%lx", temp); name = syms_val2line(this_inst, &i, 0); if (name) printf(", line %d in file %s", i, name); putchar('\n'); } break; case SET: if (argv[0] == NULL || argv[1] == NULL) break; if (*(argv[0]) == '%') { puts("Use the command 'reg' or 'r'"); break; } else { DWORD pokeat; int size; temp = syms_name2val(argv[1]); if (undefined_symbol) break; pokeat = syms_name2val(argv[0]); if (undefined_symbol) break; if (argv[2]) { if (*(argv[2]) == 'b') size = 1; else if (*(argv[2]) == 'w') size = 2; } else size=0; if (size) { /* value */ DWORD oldv = ptrace(PTRACE_PEEKTEXT, pid, pokeat, 0); if (size = 1) * (char *) & temp = * (char *) & temp; else * (short *) & temp = * (short *) & temp; } ptrace(PTRACE_POKETEXT, pid, pokeat, temp); } break; case PROCESS: printf("pid %d ppid %d\n\n", getpid(), getppid()); PrintFreeMemInfo(); break; case SEL: if (argv[0] == NULL) break; if (argv[1] == NULL) i = 1; else i = atoi(argv[1]); temp = syms_name2val(argv[0]); if (undefined_symbol) break; show_descriptor(temp, i); break; case QUIT: ansi(A_white); return; case Unknown: printf("unknown command, try (H)elp or ?\n"); break; } /* switch */ if (cmd == CONT || cmd == NEXT || cmd == STEP) { list_inst = this_inst = ptrace_get_register(EIP * 4); next_inst = unassemble(this_inst, 1); } } /* while */ } int main(int argc, char **argv, char **envp) { static char debug_file[260]; struct user u; char *term; term = getenv("TERM"); if (term != NULL && strncmp(term, "ansi", 4) == 0) { ansi_flag = 1; } org_env = envp; if (argv[1]) strcpy(debug_file, argv[1]); for (;;) { if (access(debug_file, 0) < 0) strcat(debug_file, ".exe"); pid = spawnvpe(P_DEBUG | P_WINDOWED, debug_file, argv + 1, envp); if (pid < 0) { perror("Debugger spawnve"); printf("New Filename:"); gets(debug_file); } else break; } printf("child PID %d\n", pid); errno = 0; uaddr = ptrace(PTRACE_PEEKUSER, pid, (char *) &u.u_ar0 - (char *) &u, 0); if (errno != 0) perror("ptrace"); uaddr -= 0xe0000000; printf("debugging file %s\n", debug_file); fflush(stdout); syms_init(debug_file); command_loop(); return (0); }