项目介绍
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复制代码... |
本文转载自: 掘金