项目介绍
1  | rust复制代码allprojects {  | 
1  | arduino复制代码dependencies {  | 
1  | scss复制代码{  | 
文件格式
| 名字 | 用途 | 
|---|---|
| ELF Header | ELF 头部信息,记录该 ELF 文件类型为 Core,指令集类型等信息,由数据结构 ElfN_Ehdr N=32,64组成,对应的信息可用 readelf -h 查看 | 
| Program Headers | 记录该 Core 文件所有的段信息,包含每一个段在文件内的偏移,以及对应的虚拟地址偏移,段大小等信息,每一个段为数据结构 ElfN_Phdr 组成,对应的信息可用 readelf -l 查看 | 
| PT_NOTE | 记录该程序的线程状态信息,线程 TID、寄存器信息等,辅助调试信息 AUXV,对应的信息可用 readelf -n 查看 | 
| PT_LOAD | 与程序的 /proc/self/maps 一一对应,记录的是程序运行时虚拟内存空间,存放内存元数据 | 
| AUXV | 记录着执行程序的 PHDR 地址(AT_PHDR)ElfN_Phdr 数据大小(AT_PHENT),以及执行程序共有多少个段(AT_PHENT),这三个数据是调试器所需要的,用于找到 link_map 信息。链接地址(AT_BASE Android 平台则是 /system/bin/linker 地址),此处信息与 /proc/self/auxv 一一对应 | 
工作原理
核心技术依赖 Linux 写时复制机制用于父子进程内存拷贝,以及 ptrace 系统调用,它可用于挂入到目标进程上,并且可访问进程内存、寄存器等信息。程序需访问的文件节点:  
| 节点 | 项目用途 | 
|---|---|
/proc/<PID>/maps | 
程序虚拟内存空间,用于解析生成 Core 文件的 Program Headers | 
/proc/<PID>/auxv | 
程序辅助调试信息,用于解析生成 Note 段的 AUXV 部分 | 
/proc/<PID/task/* | 
程序的线程信息,用于解析生成 Note 段的 Register 信息,以及暂停线程 | 
/proc/<PID/mem | 
用于 COPY2 模式直接复制父进程内存 | 
PTRACE API介绍
API官方文档使用:man7.org/linux/man-p…
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
| 参数类型 | 描述 | 
|---|---|
| PTRACE_ATTACH | 挂入到目标PID进程上, 使目标PID进程成为调用进程的tracee,并可向tracee进程发送SIGSTOP信号来终止进程,不必等调用结束再来终止tracee,可使用waitpid(2)来等tracee结束 | 
| PTRACE_TRACEME | 用于父子进程之间,表示该进程可被父进程跟踪 | 
| PTRACE_PEEKTEXTPTRACE_PEEKDATAPTRACE_PEEKUSER | 允许跟踪进程读取被跟踪进程的虚拟内存地址 | 
| PTRACE_POKETEXTPTRACE_POKEDATAPTRACE_POKEUSER | 允许跟踪进程修改被跟踪进程的虚拟内存地址 | 
| PTRACE_GETREGSET | 允许跟踪进程读取被跟踪进程的寄存器信息 | 
| PTRACE_SETREGSET | 允许跟踪进程修改被跟踪进程的寄存器信息 | 
| PTRACE_CONT | 重启已经被终止的被跟踪进程 | 
| PTRACE_DETACH | 会重启终止的被跟踪进程,并解除跟踪 | 
| PTRACE_SYSCALL | 重启被跟踪进程,在下一个系统调用开始/退出时终止该进程。如strace工具 | 
| PTRACE_SINGLESTEP | 重启进程,并且在下一条指令运行结束后切换到终止状态。如单步调试 | 
| PTRACE_GETSIGINFO | 获取引起进程停止的信号信息,可获取siginfo_t结构信息进行修改,通过SETSIGINFO回传 | 
| PTRACE_SETSIGINFO | |
| PTRACE_SETOPTIONS | 
暂停线程工作
使用 PTRACE_ATTACH 命令即可将目标线程暂停下来,线程进入 T 状态,在内核态上函数将会停留在 ptrace_stop 函数上等待,直到跟踪进程退出或接收到 PTRACE_CONT 或 PTRACE_DETACH 命令恢复状态。
1  | C++复制代码void OpencoreImpl::StopAllThread(pid_t pid)  | 
获取线程寄存器
使用 PTRACE_GETREGSET 命令获取进程各个线程的寄存器信息,实际上内核返回的是线程内核态的上下文信息,对应的内核数据结构为 pt_regs。
1  | C++复制代码...  | 
PTRACE 读取内存
使用 ptrace 读取内存缺点在于非常的慢,原因是每次 syscall 只能访问 4、8 字节,取决于目标操作系统是 32 位还是 64 位,每一段内存的转储过程中都需要进行反复遍历,受限与 ptrace 读取能力,要想完整的保存 Android 应用程序的内存,短则 5 min 起步,长则 20 min 左右。
1  | C++复制代码...  | 
父子进程内存拷贝
在 Linux 系统中,调用 fork 系统调用创建子进程时,并不会把父进程所有占用的内存页复制一份,而是与父进程共用相同的内存页,而当子进程或者父进程对内存页进行修改时才会进行复制 —— 这就是著名的 写时复制 机制。  
基于这个机制我们可以直接拷贝子进程虚拟内存空间,尽管子进程后边有修改过内存数据,但与父进程相识度非常的高,大多数情况下可以满足我们对本次内存分析的需求,并且相比纯 ptrace 读取内存能节省大量的抓取时间。
直接内存拷贝
子进程访问 /proc/<PID>/mem 节点获取父进程所有可访问的虚拟内存,性能最高的模式。
1  | scss复制代码...  | 
本文转载自: 掘金