Last active
September 21, 2017 04:30
-
-
Save BobBurns/587d25da475494cb8a724392661459cb to your computer and use it in GitHub Desktop.
trying to trace golang program with ptrace from Learning Linux Binary Analysis by Elfmaster Ryan O'Neill
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"time" | |
) | |
func print_hello(str string) { | |
fmt.Println("hello", str) | |
} | |
func main() { | |
for i:= 0; i < 20; i++ { | |
print_hello("bla") | |
time.Sleep(10 * time.Second) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <errno.h> | |
#include <signal.h> | |
#include <elf.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <sys/user.h> | |
#include <sys/stat.h> | |
#include <sys/ptrace.h> | |
#include <sys/mman.h> | |
typedef struct handle { | |
Elf64_Ehdr *ehdr; | |
Elf64_Phdr *phdr; | |
Elf64_Shdr *shdr; | |
uint8_t *mem; | |
char *symname; | |
Elf64_Addr symaddr; | |
struct user_regs_struct pt_reg; | |
char *exec; | |
} handle_t; | |
Elf64_Addr lookup_symbol(handle_t *, const char *); | |
int main(int argc, char **argv, char **envp) | |
{ | |
int fd; | |
handle_t h; | |
struct stat st; | |
long long trap, orig; | |
int status, pid, clone_pid; | |
char * args[2]; | |
if (argc < 3) | |
{ | |
printf("Usage: %s <program> <function>\n", argv[0]); | |
exit(0); | |
} | |
if ((h.exec = strdup(argv[1])) == NULL) | |
{ | |
perror("strdup"); | |
exit(-1); | |
} | |
args[0] = h.exec; | |
args[1] = NULL; | |
if ((h.symname = strdup(argv[2])) == NULL) | |
{ | |
perror("strdup"); | |
exit(-1); | |
} | |
if ((fd = open(argv[1], O_RDONLY)) == -1) | |
{ | |
perror("open"); | |
exit(-1); | |
} | |
if (fstat(fd, &st) < 0) | |
{ | |
perror("fstat"); | |
exit(-1); | |
} | |
h.mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); | |
if (h.mem == MAP_FAILED) | |
{ | |
perror("mmap"); | |
exit(-1); | |
} | |
h.ehdr = (Elf64_Ehdr *)h.mem; | |
h.phdr = (Elf64_Phdr *)(h.mem + h.ehdr->e_phoff); | |
h.shdr = (Elf64_Shdr *)(h.mem + h.ehdr->e_shoff); | |
if (h.mem[0] != 0x7f || strncmp((char *)&h.mem[1], "ELF", 3)) | |
{ | |
printf("0x%x %x %x %x \n", h.mem[0], h.mem[1], h.mem[2], h.mem[3]); | |
printf("%s is not an ELF file\n", h.exec); | |
exit(-1); | |
} | |
if (h.ehdr->e_type != ET_EXEC) | |
{ | |
printf("%s is not an ELF executable\n", h.exec); | |
exit(-1); | |
} | |
if (h.ehdr->e_shstrndx == 0 || h.ehdr->e_shoff == 0 || | |
h.ehdr->e_shnum == 0) | |
{ | |
printf("section header table not found\n"); | |
exit(-1); | |
} | |
if ((h.symaddr = lookup_symbol(&h, h.symname)) == 0) | |
{ | |
printf("Unable to find symbol: %s not found in executable\n", h.symname); | |
exit(-1); | |
} | |
close(fd); | |
if ((pid = fork()) < 0) | |
{ | |
perror("fork"); | |
exit(-1); | |
} | |
if (pid == 0) | |
{ | |
if (ptrace(PTRACE_TRACEME, pid, NULL, NULL) < 0) | |
{ | |
perror("PTRACE_TRACEME"); | |
exit(-1); | |
} | |
execve(h.exec, args, envp); | |
exit(0); | |
} | |
#define TRACE_FLAGS (PTRACE_O_EXITKILL | PTRACE_O_TRACECLONE \ | |
| PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | \ | |
PTRACE_O_TRACESYSGOOD) | |
waitpid(pid, &status, __WALL); | |
//wait(&status); | |
/* setting TRACE_FLAGS hangs */ | |
/* PTRACE_O_TRACEFORK doesnt hang but doesnt trace thread */ | |
if (ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE) < 0) | |
{ | |
perror("set options failed."); | |
exit(-1); | |
} | |
printf("Beginning analysis of pid; %d at %lx\n", pid, h.symaddr); | |
if ((orig = ptrace(PTRACE_PEEKTEXT, pid, (void *)h.symaddr, 0)) == -1) | |
{ | |
printf("symaddr %lx %lx\n", h.symaddr, &h.symaddr); | |
printf("orig: %llx", orig); | |
perror("PTRACE_PEEKTEXT"); | |
exit(-1); | |
} | |
/* int3 */ | |
trap = (orig & ~0xff) | 0xcc; | |
if (ptrace(PTRACE_POKETEXT, pid, h.symaddr, trap) < 0) { | |
perror("PTRACE_POKETEXT"); | |
exit(-1); | |
} | |
trace: | |
if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) | |
{ | |
perror("PTRACE_CONT"); | |
exit(-1); | |
} | |
waitpid(pid, &status, __WALL); | |
/* check here for clone */ | |
//wait(&status); | |
switch (status >> 8) | |
{ | |
case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)): | |
case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): | |
case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)): | |
printf("got clone.\n"); | |
if (ptrace(PTRACE_GETEVENTMSG, pid, NULL, &clone_pid) < 0) | |
{ | |
perror("PTRACE_GETEVENTMSG"); | |
exit(-1); | |
} | |
/* this hangs */ | |
pid = clone_pid; | |
printf("new pid: %d", pid); | |
waitpid(pid, &status, __WALL); | |
goto trace; | |
} | |
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) | |
{ | |
if (ptrace(PTRACE_GETREGS, pid, NULL, &h.pt_reg) < 0) | |
{ | |
perror("PTRACE_GETREGS"); | |
exit(-1); | |
} | |
printf("\nExecutable %s (pid: %d) has hit breakpoint 0x%lx\n", | |
h.exec, pid, h.symaddr); | |
printf("%%rcx: %llx\n%%rdx: %llx\n%%rbx: %llx\n" | |
"%%rax: %llx\n%%rdi: %llx\n%%rsi: %llx\n" | |
"%%r8: %llx\n%%r9: %llx\n%%r10: %llx\n" | |
"%%r11: %llx\n%%r12: %llx\n%%r13: %llx\n" | |
"%%r14: %llx\n%%r15: %llx\n%%rsp: %llx\n", | |
h.pt_reg.rcx, h.pt_reg.rdx, h.pt_reg.rbx, | |
h.pt_reg.rax, h.pt_reg.rdi, h.pt_reg.rsi, | |
h.pt_reg.r8, h.pt_reg.r9, h.pt_reg.r10, | |
h.pt_reg.r11, h.pt_reg.r12, h.pt_reg.r13, | |
h.pt_reg.r14, h.pt_reg.r15, h.pt_reg.rsp); | |
printf("\nhit any key to continue: "); | |
/* I'm not sure why getchar does weird things */ | |
/* when running go */ | |
// getchar(); | |
//sleep(1); | |
/* put code back to original */ | |
if (ptrace(PTRACE_POKETEXT, pid, h.symaddr, orig) < 0) | |
{ | |
perror("PTRACE_POKETEXT"); | |
exit(-1); | |
} | |
h.pt_reg.rip = h.pt_reg.rip - 1; | |
if (ptrace(PTRACE_SETREGS, pid, NULL, &h.pt_reg) < 0) | |
{ | |
perror("PTRACE_SETREGS"); | |
exit(-1); | |
} | |
if (ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0) | |
{ | |
perror("PTRACE_SINGLESTEP"); | |
exit(-1); | |
} | |
waitpid(pid, &status, __WALL); | |
// wait(NULL); | |
if (ptrace(PTRACE_POKETEXT, pid, h.symaddr, trap) < 0) | |
{ | |
perror("PTRACE_POKETEXT"); | |
exit(-1); | |
} | |
printf("ok\n "); | |
goto trace; | |
} | |
if (WIFEXITED(status)) | |
printf("Completed tracing pid: %d\n", pid); | |
exit(0); | |
} | |
Elf64_Addr lookup_symbol(handle_t *h, const char *symname) | |
{ | |
int i, j; | |
char *strtab; | |
Elf64_Sym *symtab; | |
for (i = 0; i < h->ehdr->e_shnum; i++) | |
{ | |
if (h->shdr[i].sh_type == SHT_SYMTAB) | |
{ | |
strtab = (char *)&h->mem[h->shdr[h->shdr[i].sh_link].sh_offset]; | |
symtab = (Elf64_Sym *)&h->mem[h->shdr[i].sh_offset]; | |
for (j = 0; j < h->shdr[i].sh_size/sizeof(Elf64_Sym); j++) | |
{ | |
if (strcmp(&strtab[symtab->st_name], symname) == 0) | |
return (symtab->st_value); | |
symtab++; | |
} | |
} | |
} | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment