/* * FuncTrace. Copyright (c) 2005, Trent Waddington. All rights reserved. * * Outputs a function level trace of any unstripped target program. * */ #include #include #include #include #include #include struct func_data { std::string name; unsigned int undercc; }; std::map funcs; struct return_data { unsigned int proc; unsigned int undercc; }; std::map > returns; void addBreakpoints(int pid, char *fname) { FILE *f; char *buf = (char*)malloc(strlen(fname) + strlen(fname) + 1000); strcpy(buf, fname); strcat(buf, ".map"); f = fopen(buf, "r"); if (f == NULL) { fprintf(stderr, "cannot open map file, trying to create..\n"); sprintf(buf, "nm %s > %s.map", fname, fname); system(buf); strcpy(buf, fname); strcat(buf, ".map"); f = fopen(buf, "r"); if (f == NULL) { fprintf(stderr, "failed to create map file.\n"); exit(1); } } free(buf); //printf("adding breakpoints..\n"); while (!feof(f)) { char line[1024], *p; fgets(line, 1024, f); p = strchr(line, '\n'); if (p) *p = 0; p = strchr(line, ' '); if (p == NULL) continue; p++; if (*p == 'T') { unsigned int addr; if (sscanf(line, "%x", &addr) == 1 && funcs.find(addr) == funcs.end()) { struct func_data d; d.name = p + 2; d.undercc = ptrace(PTRACE_PEEKTEXT, pid, (void*)addr, 0); if (*(unsigned char*)&d.undercc != 0xcc) { unsigned int undercc = d.undercc; *(unsigned char*)&undercc = 0xcc; ptrace(PTRACE_POKETEXT, pid, (void*)addr, (void*)undercc); funcs[addr] = d; //printf("added %s at addr %x undercc=%x\n", d.name.c_str(), addr, d.undercc); } } } } fclose(f); } int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "usage: functrace \n"); return 1; } int pid; if ((pid=fork()) == 0) { ptrace(PTRACE_TRACEME, 0, 0, 0); execl(argv[1], argv[1]); return 0; } int first = 1; int indent = 0; unsigned int stepfrom = 0; while(1) { int status; waitpid(pid, &status, 0); if (WIFEXITED(status)) break; if (first) { first = 0; addBreakpoints(pid, argv[1]); } else if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { struct user_regs_struct regs; ptrace(PTRACE_GETREGS, pid, 0, ®s); unsigned int bytes = ptrace(PTRACE_PEEKTEXT, pid, (void*)(regs.eip - 1), 0); if (*(unsigned char*)&bytes == 0xcc) { regs.eip--; ptrace(PTRACE_SETREGS, pid, 0, ®s); if (funcs.find(regs.eip) == funcs.end()) { if (returns.find(regs.eip) != returns.end()) { if (returns[regs.eip].find(regs.esp-4) == returns[regs.eip].end()) { fprintf(stderr, "unknown return to %x with esp %x\n", regs.eip, regs.esp); return 1; } indent--; for (int i = 0; i < indent; i++) fprintf(stderr, " "); fprintf(stderr, "<- %s\n", funcs[returns[regs.eip][regs.esp-4].proc].name.c_str()); ptrace(PTRACE_POKETEXT, pid, (void*)regs.eip, (void*)returns[regs.eip][regs.esp-4].undercc); } else { fprintf(stderr, "unknown breakpoint at %x\n", regs.eip); return 1; } } else { for (int i = 0; i < indent; i++) fprintf(stderr, " "); fprintf(stderr, "-> %s\n", funcs[regs.eip].name.c_str()); indent++; ptrace(PTRACE_POKETEXT, pid, (void*)regs.eip, (void*)funcs[regs.eip].undercc); unsigned int retaddr = ptrace(PTRACE_PEEKTEXT, pid, (void*)regs.esp, 0); struct return_data rd; rd.proc = regs.eip; rd.undercc = ptrace(PTRACE_PEEKTEXT, pid, (void*)retaddr, 0); returns[retaddr][regs.esp] = rd; bytes = rd.undercc; *(unsigned char*)&bytes = 0xcc; ptrace(PTRACE_POKETEXT, pid, (void*)retaddr, (void*)bytes); //printf("adding breakpoint at return address %x with esp %x\n", retaddr, regs.esp); ptrace(PTRACE_SINGLESTEP, pid, 0, 0); stepfrom = regs.eip; continue; } } else if (stepfrom == 0) { fprintf(stderr, "unexpected single step\n"); return 1; } else { unsigned int undercc = funcs[stepfrom].undercc; *(unsigned char*)&undercc = 0xcc; ptrace(PTRACE_POKETEXT, pid, (void*)stepfrom, (void*)undercc); stepfrom = 0; } } else if (WIFSTOPPED(status)) { fprintf(stderr, "stopped on unhandled signal %i\n", WSTOPSIG(status)); return 1; } ptrace(PTRACE_CONT, pid, 0, 0); } return 0; }