我正在使用更新的 Kali 并将其编译为 64 位
学习 Linux 二进制分析,第 57 页上的“基于 ptrace 的简单调试器”。
包含要调试的源代码和测试文件。
问题是从共享对象文件“test”访问 lookup_symbol 的返回值。
关于给定的程序:我唯一更改的是检查 ET_EXEC 的错误检查,我将其更改为 ET_DYN,以便我可以尝试跟踪共享对象文件,这在我为打印函数导入 stdio.h 时似乎不可避免. 另外,更改了检查 ELF 文件的部分,使用了:
h.mem[0] != 0x7f || strcmp((char *)&h.mem 1 , "ELF" 但我不认为第二部分是必要的,除非我在 strcmp 中使用 "ELF\002\001\001",否则我的电脑会报错。
父程序的工作在lookup_symbol 函数中完成,该函数在字符串表中搜索符号名称并返回所需符号的符号表st_value(虚拟地址)。
在可执行文件和共享目标文件中,st_value 保存一个虚拟地址。为了使这些文件的符号对动态链接器更有用,段偏移量(文件解释)让位于与段号无关的虚拟地址(内存解释)。
在 'test' 中搜索print_string:
./tracer ./test print_string
目标是在每次 print_string 调用时中断并在该点打印寄存器,然后可以按任意键继续执行。
运行源代码时,lookup_symbol 函数在我的 comp 上返回 0x6b0,作为 symtab->st_value 的值并将其分配给 h.symaddr。
0x6b0是符号表中print_string的虚拟地址(value of),通过检查readelf确认
58: 00000000000006b0 27 FUNC 全局默认值 14 print_string
书中的原文直接在以下函数中使用了 0x6b0:
如果 ((orig = ptrace(PTRACE_PEEKTEXT, pid, h.symaddr, NULL)) < 0)
当它尝试使用 h.symaddr=0x6b0 时,此函数在我的计算机上失败并出现错误:
Beginning analysis of pid: 2462 at 6b0
PTRACE_PEEKER: Input/output error
hello 1
hello 2
问题不在于返回的是虚拟地址,因为如上所述,可执行文件和共享目标文件都在符号表-> st_value 中包含虚拟地址。我不知道为什么,但是这个 ptracing 父程序如何处理共享对象和可执行文件是有区别的。
这是测试文件:
#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;
}
这是源文件:
#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;
}