PMD工具(Ptrace-based Memory Dump)源代码(注释版)
来源:互联网 发布:云计算运维工程师待遇 编辑:程序博客网 时间:2024/05/16 10:43
1. PMD工具
PMD工具是基于ptrace的Android手机内存dump工具,其能够获取Android手机指定进程的内存,并生成相应的map文件和mem文件。
Usage: pid out_dir [-s <image> <offset>]
PMD工具的编译与使用见我的另一篇博客:
http://blog.csdn.net/rzwinters/article/details/75517107
2. Android.mk与Application.mk
- Android.mk
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := pmdLOCAL_SRC_FILES := pmd.cLOCAL_CPPFLAGS := -O3 -Wall -fPIELOCAL_LDLIBS := -O3 -llog -fPIE -pieLOCAL_C_INCLUDES := /home/richard/android-ndk-r15b/platforms/android-19/arch-arm/usr/include/include $(BUILD_EXECUTABLE)
- Application.mk
APP_ABI := armeabiAPP_PLATFORM := android-19
3. 源代码(注释版)
#define _LARGEFILE64_SOURCE //for using function lseek64#include <unistd.h>#include <stdlib.h>#include <sys/types.h>#include <sys/ptrace.h>#include <sys/prctl.h>#include <sys/wait.h>#include <linux/user.h>#include <dirent.h>#include <string.h>#include <signal.h>#include <errno.h>#include <stdio.h>#include <fcntl.h>#include <sys/stat.h>#define DROID_VERSION#ifdef DROID_VERSION# include <android/log.h>#endif//#define LOG_ON //for LOG#undef LOG_ON/* Variadic macros for printing log and err info */#ifdef DROID_VERSION# define PMD_LOG(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "[pmd]", "%s: "fmt, __func__, ##__VA_ARGS__)#else# define PMD_LOG(fmt, ...) fprintf(stderr, "[ ] "__func__ fmt, ##__VA_ARGS__)#endif#define PMD_ERR(prefix, ...) PMD_LOG(prefix": %s\n", ##__VA_ARGS__, strerror(errno))#define n_ARRAY(array) (sizeof(array)/sizeof(array[0]))/* Segments need to be skipped when dumping memory */const char *skip_segments[] = { "/dev/kgsl-3d0"};/* Segements must be injected when dumping memory */const char *must_injects[] = { "/dev/ashmem"};static inline int is_in_string_list (const char* s, const char* strings[], unsigned n_strings){ unsigned i; /* Check if s is in the strings */ for(i = 0; i < n_strings; i++) { if(strcmp(strings[i], s) == 0) return 1; } return 0;}#define is_in_skip_segments(s) is_in_string_list(s, skip_segments, n_ARRAY(skip_segments))#define is_in_must_inject(s) is_in_string_list(s, must_injects, n_ARRAY(must_injects))static inline size_tget_tids(pid_t **const listptr, size_t *const sizeptr, const pid_t pid){ char dirname[64]; DIR *dir; pid_t *list; size_t size, used = 0; list = *listptr = NULL; size = *sizeptr = 0; /* Open the task dir of the target process in proc filesystem */ sprintf(dirname, "/proc/%d/task/", (int)pid); dir = opendir(dirname); if (!dir) { PMD_ERR("open task dir"); return 0; } while (1) { struct dirent *ent; int value; char dummy; /* Read the entries in the task dir */ ent = readdir(dir); if (!ent) break; /* Parse TIDs. Ignore non-numeric entries. */ if (sscanf(ent->d_name, "%d%c", &value, &dummy) != 1) continue; /* Ignore obviously invalid entries. */ if (value < 1) continue; /* Make sure there is room for another TID. */ if (used >= size) { size = (used | 127) + 128; list = realloc(list, size * sizeof list[0]); //expansion *listptr = list; //write back *sizeptr = size; //write back } /* Add to list. */ list[used++] = (pid_t)value; } closedir(dir); return used; //return, write back}static inline voidcheck_new_tids(pid_t **const listptr, size_t *const usedptr, size_t *const sizeptr, const pid_t pid, const unsigned char let_run) { int used = *usedptr; int max = *sizeptr; pid_t *list = *listptr; /* Get current existing TIDs in the target process */ pid_t *new_list; int new_max; int new_used = get_tids(&new_list , &new_max, pid); /* Find new threads of the target process */ int i, j; for(i = 0; i < new_used; i++) { unsigned char found = 0; if(new_list[i] == pid) continue; //avoid PTRACEing the main process for twice for(j = 0; j < used; j++) { if(list[j] == new_list[i]) { found = 1; break; } } /* PTRACE the new thread */ if(!found) { PMD_LOG("New Thread %d", new_list[i]); if(ptrace(PTRACE_ATTACH, new_list[i], NULL, NULL) == -1) PMD_ERR("ptrace thread %d", new_list[i]); /* Continue the new thread */ if(let_run) ptrace(PTRACE_CONT, new_list[i], NULL, NULL); } } /* Write back new list */ free(list); *listptr = new_list; *usedptr = new_used; *sizeptr = new_max;}static inline pid_t wait_and_check_threads(pid_t pid, int* status, pid_t**const tids, size_t*const n_tids, size_t*const max_tids, unsigned char let_run){//#define DEBUG_WAIT //for DEBUG#ifdef DEBUG_WAIT int cnt = 0;#endif pid_t stopped = 0; int stat = 0; errno = 0; while(stopped == 0) { /* Check and PTRACE new threads in the target process */ check_new_tids(tids, n_tids, max_tids, pid, let_run); /* Check if any thread is stopped */ stopped = waitpid((pid_t)-1, &stat, __WALL|WNOHANG);#ifdef DEBUG_WAIT if(cnt++ == 10000) { PMD_ERR("In Wait %d %d", stopped, stat); cnt = 0; }#endif }#ifdef DEBUG_WAIT PMD_ERR("returning %d %d", stopped, stat);#endif if(status != NULL) *status = stat; //return status return stopped; //return the pid of the stopped thread}int getdata(pid_t child, long addr, char *str, int len){ char *laddr; int i, j; union u { long val; char chars[sizeof(long)]; }data; i = 0; j = len / sizeof(long); laddr = str; /* Read data from the memory of target process, starting from addr, length of len */ /* Each loop read data of sizeof(long) bytes */ while(i < j) { errno = 0; data.val = ptrace(PTRACE_PEEKDATA, child, (void*)(addr + i * 4), NULL); if(errno != 0) { PMD_ERR("read 0x%lx", (addr + i * 4)); return 1; } memcpy(laddr, data.chars, sizeof(long)); //save data ++i; laddr += sizeof(long); //prepare for the next loop } /* Read remaining data */ j = len % sizeof(long); if(j != 0) { errno = 0; data.val = ptrace(PTRACE_PEEKDATA, child, (void*)(addr + i * 4), NULL); if(errno != 0) { PMD_ERR("read 0x%lx", (addr + i * 4)); return 1; } memcpy(laddr, data.chars, j); //save remaining data } return 0;}int putdata(pid_t child, long addr, const char *str, int len){ const char *laddr; int i, j; union u { long val; char chars[sizeof(long)]; }data; i = 0; j = len / sizeof(long); laddr = str; /* Write data into the memory of target process, starting from addr, length of len */ /* Each loop write data of sizeof(long) bytes */ while(i < j) { memcpy(data.chars, laddr, sizeof(long)); if(ptrace(PTRACE_POKEDATA, child, (void*)(addr + i * 4), (void*)data.val) == -1) //write data return 1; ++i; laddr += sizeof(long); //prepare for the next loop } /* Write remaining data */ j = len % sizeof(long); if(j != 0) { memcpy(data.chars, laddr, j); if(ptrace(PTRACE_POKEDATA, child, (void*)(addr + i * 4), (void*)data.val) == -1) //write remaining data return 1; } return 0;}#define BUF_SZ 8192 // bytesunsigned long buf[BUF_SZ/sizeof(unsigned long)];static inline int dump_region_inject(int out_fd, pid_t pid, off64_t start, off64_t end, pid_t**const tids, size_t*const n_tids, size_t*const max_tids){ int ret = 0; struct pt_regs in_regs, out_regs, save_regs; /* Save all the current reg values */ ptrace(PTRACE_GETREGS, pid, NULL, &save_regs); ptrace(PTRACE_GETREGS, pid, NULL, &in_regs); /* Each loop dump maximum data of BUF_SZ bytes */ while(start != end) { int read_sz = ((end - start) > BUF_SZ ? BUF_SZ : (end - start)); int i; /* Each loop read data of sizeof(unsigned long) bytes from the memory of target process */ for(i = 0; i < read_sz / sizeof(unsigned long); i++) { /* Modify the value of register R3 to start */ in_regs.ARM_r3 = (unsigned long)start; /* Set the modified reg values back */ ptrace(PTRACE_SETREGS, pid, NULL, &in_regs); /* Resume all threads */ /* Execute the shell code to load the data at start to register R2, and then stop */ ptrace(PTRACE_CONT, pid, NULL, NULL); // execute shell int status; pid_t p; /* Wait the main process to stop */ do { p = wait(&status); //p = wait_and_check_threads(pid, &status, tids, n_tids, max_tids, 0); } while(p != pid); /* Check if the main process is stopped by signal SIGTRAP */ if(!(WIFSTOPPED(status)) || WSTOPSIG(status) != SIGTRAP) { PMD_LOG("Wait returned %d: status %s", p, strsignal(WSTOPSIG(status))); ret = 1; goto out; } /* Save all the current reg values */ ptrace(PTRACE_GETREGS, pid, NULL, &out_regs);#ifdef LOG_ON /* Check if PC skipped */ if(out_regs.ARM_pc != in_regs.ARM_pc + 4) { PMD_LOG("PC Skip? %lx %lx", out_regs.ARM_pc, in_regs.ARM_pc); ret = 1; goto out; }#endif buf[i] = out_regs.ARM_r2; //put the loaded data in register R2 into buf start += sizeof(unsigned long); //prepare for the next loop } /* Write the loaded data in buf into the output file */ if(write(out_fd, buf, read_sz) != read_sz) { PMD_ERR("write 0x%llx %d", lseek64(out_fd, 0, SEEK_CUR), read_sz); ret = 2; goto out; } }out: /* Set the saved reg values back */ ptrace(PTRACE_SETREGS, pid, NULL, &save_regs); return ret;}static inline int dump_region_ptrace(pid_t child, int out_fd, off64_t start, off64_t end){ /* Each loop dump maximum data of BUF_SZ bytes */ while(start != end) { register int read_sz = ((end - start) > BUF_SZ ? BUF_SZ : (end - start)); /* Read data from the memory of target process, starting from start, length of read_sz */ if(getdata(child, start, (char*)buf, read_sz) != 0) { PMD_ERR("getdata 0x%llx %d", start, read_sz); return 1; } /* Write data into the output file */ if(write(out_fd, buf, read_sz) != read_sz) { PMD_ERR("write 0x%llx %d", lseek64(out_fd, 0, SEEK_CUR), read_sz); return 2; } start += read_sz; //prepare for the next loop } return 0;}static inline int dump_region_fd(int out_fd, int in_fd, off64_t start, off64_t end){ /* Modify current file offset to start */ if(lseek64(in_fd, start, SEEK_SET) != start) { PMD_ERR("seek"); return -1; } /* Each loop dump maximum data of BUF_SZ bytes */ while(start != end) { register int read_sz = ((end - start) > BUF_SZ ? BUF_SZ : (end - start)); /* Read data from the input file, starting from start, length of read_sz */ if(read(in_fd, buf, read_sz) != read_sz) { PMD_ERR("read 0x%llx %d", lseek64(in_fd, 0, SEEK_CUR), read_sz); return 1; } /* Write data into the output file */ if(write(out_fd, buf, read_sz) != read_sz) { PMD_ERR("write 0x%llx %d", lseek64(out_fd, 0, SEEK_CUR), read_sz); return 2; } start += read_sz; //prepare for the next loop }#undef BUF_SZ return 0;}#define ARM_BP_STR "\xf0\x01\xf0\xe7"#define THUMB_BP_STR "\xbe\xbe\xbe\xbe"static inline void dump_mem(pid_t pid, int mem, int mem_file, FILE* maps, FILE* map_file, pid_t**const tids, size_t*const n_tids, size_t*const max_tids){ char buf[BUFSIZ + 1]; char name[BUFSIZ + 1]; off64_t start, end, pos;#ifdef LOG_ON int regions = 0;#endif/* Define the shell code (ldr instruction + BP instruction) */ #define ARM_LINUX_BP const char shell[] = "\x00\x20\x93\xe5" // ldr r2, [r3]#ifdef ARM_LINUX_BP ARM_BP_STR; // ARM breakpoint#endif#ifdef THUMB_BP THUMB_BP_STR; // Thumb breakpoint#endif char save[sizeof(shell)]; /* Insert shell code */ /* Save all the current reg values */ struct pt_regs regs; ptrace(PTRACE_GETREGS, pid, NULL, ®s); /* Save old instructions at PC */ if(getdata(pid, regs.ARM_pc, save, sizeof(save)) != 0) return; /* Write shell code instructions to PC */ if(putdata(pid, regs.ARM_pc, shell, sizeof(shell)) != 0) return; /* Traverse every segment in the maps file */ while(fgets(buf, BUFSIZ, maps)) { name[0] = '\0'; sscanf(buf, "%llx-%llx %*s %*x %*x:%*x %*u %s\n", &start, &end, name); /* Check if the segment needs to be skipped */ if(is_in_skip_segments(name)) continue; /* Get the current file offset in mem_file */ pos = lseek64(mem_file, 0, SEEK_CUR); /* Check if the segment must be injected */ if(is_in_must_inject(name)) { /* Dump memory using PTRACE */ if(dump_region_ptrace(pid, mem_file, start, end)) { PMD_ERR("dump ptrace: %llx->%llx %s", start, end, name); continue; } } // first try to read from proc/mem (WAY MUCH FASTER) /* Dump memory using mem file */ else if(dump_region_fd(mem_file, mem, start, end)) { PMD_LOG("injecting for: %llx->%llx %s", start, end, name); // if that doesn't work, then try inject /* Dump memory by injecting shell code */ if(dump_region_inject(mem_file, pid, start, end, tids, n_tids, max_tids)) { PMD_ERR("dump: %llx->%llx %s", start, end, name); continue; } } /* Write into map_file */ fprintf(map_file, "%llx->%llx->%llx %s\n", start, end, pos, name);#ifdef LOG_ON regions++; PMD_LOG("dump [%d] = %s = %llx", regions, buf, pos);#endif } /* Set the saved reg values back */ ptrace(PTRACE_SETREGS, pid, NULL, ®s); /* Write saved old instructions back to PC */ if(putdata(pid, regs.ARM_pc, save, sizeof(save)) != 0) return;#ifdef LOG_ON PMD_LOG("Done: dumped %d regions.\n", regions);#else PMD_LOG("Done.\n");#endif}uintptr_t find_start_of_map(FILE* maps, char* image){ char buf[BUFSIZ + 1]; char name[BUFSIZ + 1]; char perms[5]; uintptr_t start; /* Find the image (code segment) in the maps file of target process */ while(fgets(buf, BUFSIZ, maps)) { name[0] = '\0'; sscanf(buf, "%x-%*x %s %*x %*x:%*x %*u %s\n", &start, perms, name); /* match the name and the perms (code segment) */ if(strstr(name, image) != NULL && strstr(perms, "r-x") == perms) return start; //return the start address of the image (code segment) } return 0;}void hold_for_break(pid_t pid, pid_t**const tids, size_t*const n_tids, size_t*const max_tids, FILE* maps, char* image, uintptr_t offset){ /* Find the start address of the image (code segment) in the memory of target process */ uintptr_t addr = find_start_of_map(maps, image); rewind(maps); //rewind the ptr of maps file if(addr == 0) { PMD_LOG("ERROR! No matching map for %s", image); return; } addr+=offset; //locate the address /* Define the breakpoint instruction (ARM and Thumb)*/ char bp[] =#ifdef ARM_LINUX_BP ARM_BP_STR; // ARM breakpoint#endif#ifdef THUMB_BP THUMB_BP_STR; // Thumb breakpoint#endif char save[sizeof(bp)]; char check[sizeof(bp)]; /* Insert the BP instruction into memory at addr */ /* Save the old instruction at addr */ if(getdata(pid, addr, save, 4) != 0) return; /* Write the BP instruction to addr */ if(putdata(pid, addr, bp, 4) != 0) return; /* Check if the BP instruction has been inserted */ if(getdata(pid, addr, check, 4) != 0) return; if(memcmp(bp, check, 4) != 0) { PMD_ERR("NOT SETTING BP!!!!!!!!!"); return; } PMD_LOG("Put BP on 0x%x", addr); PMD_LOG("Now holding for BP...."); kill(pid, SIGCONT); //resume all threads unsigned char hold = 1; pid_t stopped = 0; struct pt_regs regs; while(hold) { int status = 0; /* Check new threads and wait a thread to stop */ stopped = wait_and_check_threads(pid, &status, tids, n_tids, max_tids, 1); if(stopped == -1) { PMD_ERR("wait?"); return; } ptrace(PTRACE_GETREGS, stopped, NULL, ®s); //get current reg values of the stopped thread /* Check if the thread is stopped by signal SIGTRAP */ /* OR */ /* Check if the thread is stopped at the inserted BP instruction */ if((WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) || (addr - 32 <= regs.ARM_pc && regs.ARM_pc <= addr + 32)) //the thread hit the inserted BP instruction { hold = 0;#ifdef LOG_ON PMD_LOG("Hit BP %d: pc 0x%lx status %s", stopped, regs.ARM_pc, strsignal(WSTOPSIG(status)));#else PMD_LOG("Hit our BP!");#endif } else //classify and print the stopped info of the thread { int sig = 0; if (WIFEXITED(status)) { //exit#ifdef LOG_ON PMD_LOG("Wait Ret: Exited %d: pc 0x%lx status %d", stopped, regs.ARM_pc, WEXITSTATUS(status));#endif } else if (WIFSIGNALED(status)) { //killed by signal#ifdef LOG_ON PMD_LOG("Wait Ret: Killed by signal %d: pc 0x%lx status %s", stopped, regs.ARM_pc, strsignal(WTERMSIG(status)));#endif } else if (WIFSTOPPED(status)) { //stopped by signal#ifdef LOG_ON PMD_LOG("Wait Ret: Stopped by signal %d: pc 0x%lx status %s", stopped, regs.ARM_pc, strsignal(WSTOPSIG(status)));#endif } ptrace(PTRACE_CONT, stopped, NULL, (void*)(sig)); //resume the stopped thread, ignore the signal } } /* Change the PC and set back the modified reg values of the stopped thread in order to resume running */ regs.ARM_pc = addr; ptrace(PTRACE_SETREGS, stopped, NULL, ®s); /* Write the saved old instruction back at addr */ putdata(pid, addr, save, 4); kill(pid, SIGSTOP); // stop other threads /* Wait until the main process stopped */ do { stopped = wait_and_check_threads(pid, NULL, tids, n_tids, max_tids, 0); } while (stopped != pid);}int main(int argc, char *argv[]){ FILE *maps = NULL, *map_file = NULL; int mem = -1, mem_file = -1; int error = EXIT_SUCCESS; pid_t pid; pid_t *tid = 0; size_t tids = 0; size_t tids_max = 0; int t; char path[BUFSIZ]; /* Validate input arguments */ if(argc < 3 || argc > 6) { PMD_LOG("usage: %s pid out_dir [-s <image> <offset>]\n", argv[0]); return EXIT_FAILURE; } /* Get the PID of the target process */ pid = strtol(argv[1], NULL, 10); /* Create the output map_file */ snprintf(path, sizeof(path), "%s/%d.map", argv[2], pid); map_file = fopen(path, "w"); if(!map_file) { PMD_ERR("%s", path); error = EXIT_FAILURE; goto clean_up; } /* Create the output mem_file */ snprintf(path, sizeof(path), "%s/%d.mem", argv[2], pid); mem_file = open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if(mem_file < 0) { PMD_ERR("%s", path); error = EXIT_FAILURE; goto clean_up; } /* PTRACE the target process */ if(ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) { PMD_ERR("ptrace"); error = EXIT_FAILURE; goto clean_up; } /* Get current existing TIDs in the target process */ check_new_tids(&tid, &tids, &tids_max, pid, 0); /* Open the maps file of the target process in proc filesystem */ snprintf(path, sizeof(path), "/proc/%d/maps", pid); maps = fopen(path, "r"); if(!maps) { PMD_ERR("%s", path); error = EXIT_FAILURE; goto clean_up; } /* Open the mem file of the target process in proc filesystem */ snprintf(path, sizeof(path), "/proc/%d/mem", pid); mem = open(path, O_RDONLY); if(mem < 0) { PMD_ERR("%s", path); error = EXIT_FAILURE; goto clean_up; } /* Deal with the added arguments: [-s <image> <offset>] */ /* GUESS USAGE: Run the process to the given address (image address + offset) before dumping memory */ if(argc > 3) { hold_for_break(pid, &tid, &tids, &tids_max, maps, argv[4], strtoll(argv[5], NULL, 0)); } /* Dump the memory of the target process */ dump_mem(pid, mem, mem_file, maps, map_file, &tid, &tids, &tids_max);clean_up: /* Detach the main process and all the threads */ ptrace(PTRACE_DETACH, pid, NULL, NULL); for (t = 0; t < tids; t++) { if(tid[t] != pid) ptrace(PTRACE_DETACH, tid[t], NULL, NULL); } kill(pid, SIGCONT); //resume the main process /* Close all the open files */ if(mem >= 0) close(mem); if(mem_file >= 0) close(mem_file); if(maps) fclose(maps); if(map_file) fclose(map_file); return error;}
阅读全文
0 0
- PMD工具(Ptrace-based Memory Dump)源代码(注释版)
- java源代码分析工具PMD 3.1发布
- PMD Java静态源代码分析工具
- memory std::allocator源代码及注释.
- dump memory
- checkstyle pmd findbugs工具比较(转)
- ptrace源代码分析
- 《使用MAT(Memory Analyzer Tool)工具分析dump文件》
- 测试工具PMD使用
- PMD代码检查工具
- PMD logoJava代码检查工具 PMD
- 代码检测工具(四)静态代码检查工具-PMD
- Software Based Memory Testing
- Memory dump and bug
- Dump memory using gdb
- MTK Memory dump
- Dump Linux Memory
- OutOfMemoryError dump memory
- 图-迪杰斯特拉(dijkstra)算法
- 蚂蚁分类信息系统5.8 短信通道2 互亿无线配置使用说明
- Excel在统计分析中的应用—第六章—概率分布及概率分布图-Part3-离散型概率分布(负二项分布函数NEGBINOM.DIST()的应用)
- java语言的各种输入情况
- RHEL 7.2 下安装Tomcat并设置开机自启
- PMD工具(Ptrace-based Memory Dump)源代码(注释版)
- 问题 : 最简单的计算机
- Maven系列三@三生命周期和Maven聚合
- 欢迎使用CSDN-markdown编辑器
- 1002
- 学习SpringMVC——从HelloWorld开始
- H5面试题---&&的使用
- Windows下免安装版Tomcat的配置
- POJ 3617 Best Cow Line——贪心