Skip to content

Instantly share code, notes, and snippets.

@BobBurns
Last active September 21, 2017 04:30
Show Gist options
  • Save BobBurns/587d25da475494cb8a724392661459cb to your computer and use it in GitHub Desktop.
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
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)
}
}
#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