├── .gitignore ├── LICENSE ├── README.md ├── jni ├── Android.mk └── Application.mk ├── main.cc └── memdump.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Android build files 2 | libs/ 3 | obj/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # memdump 2 | Yet another tool for dump mapped memory regions of process, designed to work with Android. 3 | ``` 4 | Usage: memdump [-a/--all] [-d ] [-h/--help] 5 | -h/--help: Print this help. 6 | -a/--all: Dump all memory region. 7 | -d : Output directory for dumped files. 8 | ``` 9 | [![LICENSE](http://www.wtfpl.net/wp-content/uploads/2012/12/wtfpl-badge-2.png)](http://www.wtfpl.net/txt/copying/) 10 | -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := memdump 6 | LOCAL_SRC_FILES := ../main.cc 7 | LOCAL_CPPFLAGS := -O2 -Wall -std=c++11 8 | 9 | include $(BUILD_EXECUTABLE) 10 | -------------------------------------------------------------------------------- /jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a x86 arm64-v8a 2 | APP_PLATFORM := android-14 3 | APP_STL := c++_static 4 | -------------------------------------------------------------------------------- /main.cc: -------------------------------------------------------------------------------- 1 | #include "memdump.h" 2 | 3 | void print_usage(const char* kProg) 4 | { 5 | fprintf(stderr, "Usage: %s [-a/--all] [-d ] [-h/--help]\n", kProg); 6 | fprintf(stderr, "\t-h/--help: Print this help.\n"); 7 | fprintf(stderr, "\t-a/--all: Dump all memory region.\n"); 8 | fprintf(stderr, "\t-d : Output directory for dump files.\n"); 9 | } 10 | 11 | void dump_region(const uintptr_t start, const uintptr_t end, const std::string &outdir, const int pid, const bool ismem) 12 | { 13 | const std::string prefix = ismem ? "/mem_" : "/map_"; 14 | 15 | std::stringstream outpath; 16 | outpath << std::hex << outdir << prefix << start << "_" << end << ".bin"; 17 | 18 | fprintf(stderr, "memdump: Output file %s\n", outpath.str().c_str()); 19 | 20 | struct stat st = {0}; 21 | if (stat(outdir.c_str(), &st) == -1) 22 | mkdir(outdir.c_str(), 0655); 23 | 24 | std::ofstream outfile(outpath.str()); 25 | if (!outfile) { 26 | fprintf(stderr, "memdump: Error opening output file.\n"); 27 | exit(3); 28 | } 29 | 30 | std::ifstream procmem("/proc/" + std::to_string(pid) + "/mem"); 31 | if (!procmem) { 32 | fprintf(stderr, "memdump: Error opening /proc/%d/mem.\n", pid); 33 | exit(3); 34 | } 35 | 36 | procmem.seekg(start); 37 | 38 | std::istreambuf_iterator infile_it(procmem); 39 | std::ostreambuf_iterator outfile_it(outfile); 40 | 41 | std::copy_n(infile_it, end - start, outfile_it); 42 | 43 | outfile.close(); 44 | } 45 | 46 | void dump_all(const std::vector &maps, const std::string &outdir, const int pid) 47 | { 48 | for (auto& region : maps) { 49 | dump_region(region.start, region.end, outdir, pid, region.path.empty()); 50 | } 51 | } 52 | 53 | void parse_maps(const int pid, std::vector &maps) 54 | { 55 | std::ifstream proc_maps("/proc/" + std::to_string(pid) + "/maps"); 56 | 57 | if (!proc_maps) { 58 | fprintf(stderr, "memdump: Error opening /proc/%d/maps\n", pid); 59 | exit(3); 60 | } 61 | 62 | std::string line; 63 | while (std::getline(proc_maps, line)) { 64 | maps_lines parse(line); 65 | maps.push_back(parse); 66 | } 67 | 68 | proc_maps.close(); 69 | } 70 | 71 | int main(int argc, char* argv[]) 72 | { 73 | // TODO: Saner argument parser. eta : never 74 | if (argc < 2) { 75 | print_usage(argv[0]); 76 | return 1; 77 | } 78 | 79 | InputParser input(argc, argv); 80 | if (input.cmdOptionExists("-h") || input.cmdOptionExists("--help")) { 81 | print_usage(argv[0]); 82 | return 0; 83 | } 84 | 85 | const int kPid = std::strtol(argv[1], NULL, 10); 86 | bool all = false; 87 | 88 | std::string outdir = input.getCmdOption("-d"); 89 | if (outdir.empty()) { 90 | outdir = '.'; 91 | } else { 92 | if (outdir.back() == '/') 93 | outdir.erase(outdir.end() - 1); 94 | if (outdir.front() != '/') 95 | outdir.insert(0, "./"); 96 | } 97 | 98 | if (input.cmdOptionExists("--all") || input.cmdOptionExists("-a")) 99 | all = true; 100 | 101 | int status; 102 | ptrace(PTRACE_ATTACH, kPid, nullptr, 0); 103 | waitpid(kPid, &status, WSTOPPED); 104 | 105 | std::vector maps; 106 | parse_maps(kPid, maps); 107 | 108 | if (!all) { 109 | for (size_t i = 0; i < maps.size(); ++i) 110 | fprintf(stdout, "[%zu] %s\n", i, maps[i].str().c_str()); 111 | 112 | decltype(maps.size()) region; 113 | fprintf(stdout, "Select Region: "); 114 | // TODO: Handle when input select out of list. High priority. 115 | scanf("%zu", ®ion); 116 | // assert(region < maps.size()); 117 | 118 | dump_region(maps[region].start, maps[region].end, outdir, kPid, maps[region].path.empty()); 119 | } else { 120 | dump_all(maps, outdir, kPid); 121 | } 122 | 123 | ptrace(PTRACE_DETACH, kPid, nullptr, 0); 124 | fprintf(stderr, "memdump: Done!\n"); 125 | 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /memdump.h: -------------------------------------------------------------------------------- 1 | #ifndef MAPS_H_ 2 | #define MAPS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | struct maps_lines 20 | { 21 | std::string range; 22 | uintptr_t start; 23 | uintptr_t end; 24 | std::string permissions; 25 | uintptr_t offset; 26 | std::string device; 27 | uintptr_t inode; 28 | std::string path; 29 | 30 | maps_lines(std::string line) 31 | { 32 | std::stringstream lines(line); 33 | lines >> range >> permissions >> offset >> device >> inode >> path; 34 | 35 | // split range 36 | std::size_t delim = range.find('-'); 37 | end = std::strtoul(range.substr(delim + 1).c_str(), NULL, 16); 38 | start = std::strtoul(range.substr(0, delim).c_str(), NULL, 16); 39 | } 40 | 41 | std::string str() 42 | { 43 | return std::to_string(start) + "-" + std::to_string(end) + " " + 44 | permissions + " " + path; 45 | } 46 | }; 47 | 48 | // https://stackoverflow.com/a/868894 49 | class InputParser{ 50 | public: 51 | InputParser (int &argc, char **argv){ 52 | for (int i=1; i < argc; ++i) 53 | this->tokens.push_back(std::string(argv[i])); 54 | } 55 | /// @author iain 56 | const std::string& getCmdOption(const std::string &option) const{ 57 | std::vector::const_iterator itr; 58 | itr = std::find(this->tokens.begin(), this->tokens.end(), option); 59 | if (itr != this->tokens.end() && ++itr != this->tokens.end()){ 60 | return *itr; 61 | } 62 | static const std::string empty_string(""); 63 | return empty_string; 64 | } 65 | /// @author iain 66 | bool cmdOptionExists(const std::string &option) const{ 67 | return std::find(this->tokens.begin(), this->tokens.end(), option) 68 | != this->tokens.end(); 69 | } 70 | private: 71 | std::vector tokens; 72 | }; 73 | 74 | #endif 75 | --------------------------------------------------------------------------------