I'm using updated Kali and compiling this for 64- bitWorking through Learning Linux Binary Analysis,'simple ptrace-based debugger' on page 57.Source code and testfile to debug included.Problem is accessing the return value of lookup_symbol from the shared object file 'test'.Regarding the given program:The only thing I've changed is the error check that checks for ET_EXEC, I changed it to ET_DYN so that I can try to trace shared object files, which seem unavoidable when I import stdio.h for the print function. Also, changed the part that checks for ELF files, used use: h.mem[0] != 0x7f || strcmp((char *)&h.mem1, ELF  But I don't think the second part is necessary, and my computer spits out an error unless I use ELF\002\001\001 in the strcmp. The work of the parent program is done in the lookup_symbol function which searches for the symbol name in the string table and returns the symbol table st_value (the virtual address) of the desired symbol.From TIS ELF Specification In executable and shared object files, st_value holds a virtual  address. To make these files' symbols more useful for the dynamic  linker, the section offset (file interpretation) gives way to a  virtual address (memory interpretation) for which the section number  is irrelevant.To search for print_string in 'test':./tracer ./test print_stringThe goal is to break at each print_string call and print the registers at that point, then any key can be pressed to continue execution.When running the source code the lookup_symbol function returns 0x6b0 on my comp for the value of symtab->st_value and assigns it to h.symaddr.0x6b0 is the virtual address (value of) of print_string in the symbol table, confirmed by checking readelf 58: 00000000000006b0    27 FUNC    GLOBAL DEFAULT   14 print_stringThe original from the book uses 0x6b0 directly in the following function:if ((orig = ptrace(PTRACE_PEEKTEXT, pid, h.symaddr, NULL)) < 0)This function fails with an error on my computer when it attempts to use h.symaddr=0x6b0:Beginning analysis of pid: 2462 at 6b0PTRACE_PEEKER: Input/output errorhello 1hello 2The problem is not that it's a virtual address being returned, because as quoted above, both executables and shared object files contain virtual addresses in symbol_table->st_value. I'm not sure why but there is a difference in how this ptracing parent program treats shared objects and executables.This is the test file:#include <stdio.h>void print_string(char * str){    printf(%s\n, str); }int main(int argc, char ** argv){    print_string(hello 1);    print_string(hello 2);    return 0;}This is the source file:    #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/user.h>#include <sys/stat.h>#include <sys/ptrace.h>#include <sys/mman.h>#include <sys/wait.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 trap, orig;    int status, 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)) < 0)    {        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)    {        printf(%s is not an ELF file\n, h.exec);        exit(-1);    }    if (h.ehdr->e_type != ET_DYN)    {        printf(%s is not an ELF dynamic\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);    }    wait(&status);    printf(Beginning analysis of pid: %d at %lx\n, pid, h.symaddr);    if ((orig = ptrace(PTRACE_PEEKTEXT, pid, h.symaddr, NULL)) < 0)    {        perror(PTRACE_PEEKER);        exit(-1);    }    trap = (orig & ~0xff | 0xcc);    printf(Made it here);    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);    }    wait(&status);    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%%);    printf(\nHit any key to continue: );    getchar();    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);    }    wait(NULL);    if (ptrace(PTRACE_POKETEXT, pid, h.symaddr, trap) < 0)    {        perror(PTRACE_POKETEXT);        exit(-1);    }    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)                        //printf(symtab->st_value is 0x%lx\n, symtab->st_value);                          return (symtab->st_value);                    symtab++;                }            }        }        return 0;    }
ptrace TRACE_PEEKER: Input/output error accessing virtual address contents when traced file is a shared object file
elf
The problem is that when I compile test.c with gcc, gcc creates a shared object file. This is the case with even the simplest of programs:    //simplereturn.c    int main(void)    {        return 22;    }from readelf:Type:                              DYN (Shared object file)When I try to parse this with tracer, previously I had changed the error-checking section that ensured that the file that I'm parsing with ptrace is ET_EXEC (an executable file). I changed it so it checked if the traced file was ET_DYN (shared object file). Doing this allowed the error-check to pass for the simple C programs that I wanted to trace, but had unexpected (at least for me) consequences. When lookup_symbol was returning it was in fact returning a virtual address (0x6b0), which agrees with the symbol table of the readelf, and the function address in objdump -D. Can even check it in gdb. However, interestingly, a break at print_string is placed at 0x6bc, while the start of the print_string function is at 0x6b0. Probably not irrelevant.After a lot of research and struggle, the problem was an easy fix. I created an executable instead of a shared object file. The requisite as far as I can tell is that it uses no external libraries or functions, or I guess probably that it's self-contained. Either way, gcc won't make them.;test.asmsection .text    global _start_start:    mov rsi, hello1    call print_string    mov rsi, hello2    call print_string    xor rdi,rdi    mov rax, 60    syscallprint_string:    mov rax, 1    mov rdi, 1    mov rdx, 6    syscall    retsection .data    hello1 db Hello1    hello2 db Hello2Compiled with nasm -g -f elf64 test.asm && ld -m elf_x86_64 -o testasm testasm.oNow readelf -a testasm confirms that it's an executable ELF Header:  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00   Class:                             ELF64  Data:                              2's complement, little endian  Version:                           1 (current)  OS/ABI:                            UNIX - System V  ABI Version:                       0 Type:                              EXEC (Executable file) //EXEC NOW  Machine:                           Advanced Micro Devices X86-64  Version:                           0x1  Entry point address:               0x4000b0  Start of program headers:          64 (bytes into file)  Start of section headers:          616 (bytes into file)  Flags:                             0x0  Size of this header:               64 (bytes)  Size of program headers:           56 (bytes)  Number of program headers:         2  Size of section headers:           64 (bytes)  Number of section headers:         6  Section header string table index: 5And I switched the error checking in the original back:#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/user.h>#include <sys/stat.h>#include <sys/ptrace.h>#include <sys/mman.h>#include <sys/wait.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 trap, orig;    int status, 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)) < 0)    {        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) || strcmp((char *)&h.mem[1], ELF\002\001\001))    {        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);    }    wait(&status);    printf(Beginning analysis of pid: %d at %lx\n, pid, h.symaddr);    if ((orig = ptrace(PTRACE_PEEKTEXT, pid, h.symaddr, (void *)0)) < 0)    {        perror(PTRACE_PEEKER);        exit(-1);    }    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);    }    wait(&status);    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%%);    printf(\nHit any key to continue: );    getchar();    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);    }    wait(NULL);    if (ptrace(PTRACE_POKETEXT, pid, h.symaddr, trap) < 0)    {        perror(PTRACE_POKETEXT);        exit(-1);    }    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;    }And now when run it it works$ ./tracer ./testasm print_stringBeginning analysis of pid: 5378 at 4000d8Executable ./testasm (pid: 5378) has hit breakpoint 0x4000d8%rcx: 564ed7b07050%rdx: 7fb9525a9760%rbx: 7fffffc2%rax: 1%rdi: 3e%rsi: 1958ac0%r8: 7ffcd2b98848%r9: 7ffcd2b98828%r10: 3d2bea1a8%%r11: 564ed7b07010%r12: 0%r13: 7fb9527d2728%%r14: 57f00000001%r15: 801%rsp: 203bec%Hit any key to continue: Hello1Executable ./testasm (pid: 5378) has hit breakpoint 0x4000d8%rcx: 564ed7b07050%rdx: 7fb9525a9760%rbx: 7fffffc2%rax: 1%rdi: 3e%rsi: 1958ac0%r8: 7ffcd2b98848%r9: 7ffcd2b98828%r10: 3d2bea1a8%%r11: 564ed7b07010%r12: 0%r13: 7fb9527d2728%%r14: 57f00000001%r15: 801%rsp: 203bec%Hit any key to continue: Hello2Completed tracing pid: 5378checking gdb and readelfLookup symbol returns value at 0x4000d8, as the symbol table from readelf -a testasm says it should:Symbol table '.symtab' contains 11 entries:   Num:    Value          Size Type    Bind   Vis      Ndx Name     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND      1: 00000000004000b0     0 SECTION LOCAL  DEFAULT    1      2: 00000000006000ec     0 SECTION LOCAL  DEFAULT    2      3: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.asm     4: 00000000004000d8     0 NOTYPE  LOCAL  DEFAULT    1 print_string     5: 00000000006000ec     0 NOTYPE  LOCAL  DEFAULT    2 hello1     6: 00000000006000f2     0 NOTYPE  LOCAL  DEFAULT    2 hello2     7: 00000000004000b0     0 NOTYPE  GLOBAL DEFAULT    1 _start     8: 00000000006000f8     0 NOTYPE  GLOBAL DEFAULT    2 __bss_start     9: 00000000006000f8     0 NOTYPE  GLOBAL DEFAULT    2 _edata    10: 00000000006000f8     0 NOTYPE  GLOBAL DEFAULT    2 _endSo the differences between tracing an executable or shared object file in this case were what broke it.