├── README.md ├── 例子.cpp └── Android-Memory-Debug.hpp /README.md: -------------------------------------------------------------------------------- 1 | # Android-MemoryDebug-Tools 2 | 3 | Android application memory debugging tool written in c++ 4 | -------------------------------------------------------------------------------- /例子.cpp: -------------------------------------------------------------------------------- 1 | #include "Android-Memory-Debug.hpp" 2 | 3 | int main() 4 | { 5 | 6 | MemoryDebug md; 7 | md.setPackageName("tv.danmaku.bili"); 8 | 9 | 10 | 11 | md.searchMem < int >(2234, DWORD, Mem_Ca); 12 | md.SearchOffest < int >(2234, 0); 13 | md.SearchOffest < int >(46, 0x4); 14 | md.SearchOffest < int >(120, -0x4); 15 | md.Editoffest < int >(9999, 0x8); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /Android-Memory-Debug.hpp: -------------------------------------------------------------------------------- 1 | // 大部分代码来自于 http://t.csdn.cn/bQCd5 2 | // 我在其基础上对搜索函数进行了优化,并封装了一些常用函数 3 | // 支持编译共享库和二进制 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | // 支持的搜索类型 37 | enum 38 | { 39 | DWORD, 40 | FLOAT, 41 | BYTE, 42 | WORD, 43 | QWORD, 44 | XOR, 45 | DOUBLE, 46 | }; 47 | 48 | // 支持的内存范围(请参考GG修改器内存范围) 49 | enum 50 | { 51 | Mem_Auto, // 所有内存页 52 | Mem_A, 53 | Mem_Ca, 54 | Mem_Cd, 55 | Mem_Cb, 56 | Mem_Jh, 57 | Mem_J, 58 | Mem_S, 59 | Mem_V, 60 | Mem_Xa, 61 | Mem_Xs, 62 | Mem_As, 63 | Mem_B, 64 | Mem_O, 65 | }; 66 | 67 | struct MemPage 68 | { 69 | long start; 70 | long end; 71 | char flags[8]; 72 | char name[128]; 73 | void *buf = NULL; 74 | }; 75 | 76 | struct AddressData 77 | { 78 | std::vector < long >addrs; 79 | }; 80 | 81 | // 根据类型判断类型所占字节大小 82 | size_t judgSize(int type) 83 | { 84 | switch (type) 85 | { 86 | case DWORD: 87 | case FLOAT: 88 | case XOR: 89 | return 4; 90 | case BYTE: 91 | return sizeof(char); 92 | case WORD: 93 | return sizeof(short); 94 | case QWORD: 95 | return sizeof(long); 96 | case DOUBLE: 97 | return sizeof(double); 98 | } 99 | return 4; 100 | } 101 | 102 | int memContrast(char *str, char *mem_flags) 103 | { 104 | 105 | // A内存的判断并不完善,有能力的可以自己优化 106 | if (strstr(mem_flags, "rw") != NULL && strlen(str) == 0) 107 | return Mem_A; 108 | if ((strstr(str, "/data/app/") != NULL && strstr(mem_flags, "r-xp")) 109 | || (strstr(mem_flags, "r-xp") != nullptr && strlen(str) == 0)) 110 | return Mem_Xa; 111 | if (strstr(str, "/dev/ashmem/") != NULL) 112 | return Mem_As; 113 | if (strstr(str, "/system/fonts/") != NULL) 114 | return Mem_B; 115 | if (strstr(str, "/system/framework/") != NULL) 116 | return Mem_Xs; 117 | if (strcmp(str, "[anon:libc_malloc]") == 0) 118 | return Mem_Ca; 119 | if (strstr(str, ":bss") != NULL) 120 | return Mem_Cb; 121 | if (strstr(str, "/data/data/") != NULL) 122 | return Mem_Cd; 123 | if (strstr(str, "[anon:dalvik") != NULL) 124 | return Mem_J; 125 | if (strcmp(str, "[stack]") == 0) 126 | return Mem_S; 127 | if (strcmp(str, "/dev/kgsl-3d0") == 0) 128 | return Mem_V; 129 | return Mem_O; 130 | } 131 | 132 | 133 | 134 | class MemoryDebug 135 | { 136 | private: 137 | pid_t pid = 0; // 调试应用的PID 138 | std::vector < long >res; 139 | 140 | std::vector < long >ofstmp; 141 | public: 142 | // 设置调试的应用包名,返回PID 143 | int setPackageName(const char *name); 144 | // 获取模块的基址,@name:模块名,@index:模块在内存中的内存页位置(第几位,从1开始,默认1) 145 | long getModuleBase(const char *name, int index = 1); 146 | // 获取模块的BSS基址 147 | long getBssModuleBase(const char *name); 148 | // 读内存的基础函数 149 | size_t preadv(long address, void *buffer, size_t size); 150 | // 写内存的基础函数 151 | size_t pwritev(long address, void *buffer, size_t size); 152 | template < class T > void Editoffest(T num, int offset); 153 | 154 | template < class T > void searchoffest(T num, int offset); 155 | 156 | template < class T > void SearchOffest(T num, int offset); 157 | // 传lambda进去 158 | template < typename F > int searchCall(int type, F && call); 159 | template < class T > void EditXa(long address, T newValue); 160 | // 搜索字符串 161 | template < typename T > 162 | AddressData searchString(const std::string & str, int mem, bool debug = false); 163 | // 仿memorytools函数 164 | template < class T > void searchMem(T value, int type, int mem); 165 | 166 | template < class T > AddressData searchPointer(std::vector < long >address, long offset, 167 | int type, int mem, bool debug = false); 168 | 169 | // 根据值搜索内存,并返回相应地址 170 | template < class T > AddressData search(T value, int type, int mem, bool debug = false); 171 | // 修改内存地址值,返回-1,修改失败,返回1,修改成功 172 | template < class T > int edit(T value, long address, int type, bool debug = false); 173 | 174 | // 多包名读取 175 | int getPidByPackageNames(std::vector < std::string > packageNames); 176 | // 读取一个DWORD(int)数值 177 | int ReadDword(long address); 178 | // 读取一个int指针地址数值 179 | long ReadDword64(long address); 180 | // 读取一个float类型数值 181 | float ReadFloat(long address); 182 | // 读取一个long类型数值 183 | long ReadLong(long address); 184 | // 32位指针跳转 185 | long int ReadPointer(long address); 186 | 187 | std::vector < long >getvec(); 188 | }; 189 | 190 | 191 | int MemoryDebug::getPidByPackageNames(std::vector < std::string > packageNames) 192 | { 193 | DIR *dir; 194 | FILE *fp; 195 | char filename[32]; 196 | char cmdline[256]; 197 | struct dirent *entry; 198 | int ptid = -1; 199 | 200 | dir = opendir("/proc"); 201 | while ((entry = readdir(dir)) != NULL) 202 | { 203 | ptid = atoi(entry->d_name); 204 | if (ptid != 0) 205 | { 206 | sprintf(filename, "/proc/%d/cmdline", ptid); 207 | fp = fopen(filename, "r"); 208 | if (fp) 209 | { 210 | fgets(cmdline, sizeof(cmdline), fp); 211 | fclose(fp); 212 | for (auto packageName:packageNames) 213 | { 214 | if (strcmp(packageName.c_str(), cmdline) == 0) 215 | { 216 | return ptid; 217 | } 218 | } 219 | } 220 | } 221 | } 222 | closedir(dir); 223 | return -1; 224 | } 225 | 226 | 227 | 228 | int MemoryDebug::setPackageName(const char *name) 229 | { 230 | int id = -1; 231 | DIR *dir; 232 | FILE *fp; 233 | char filename[32]; 234 | char cmdline[256]; 235 | struct dirent *entry; 236 | dir = opendir("/proc"); 237 | while ((entry = readdir(dir)) != NULL) 238 | { 239 | id = atoi(entry->d_name); 240 | if (id != 0) 241 | { 242 | sprintf(filename, "/proc/%d/cmdline", id); 243 | fp = fopen(filename, "r"); 244 | if (fp) 245 | { 246 | fgets(cmdline, sizeof(cmdline), fp); 247 | fclose(fp); 248 | if (strcmp(name, cmdline) == 0) 249 | { 250 | pid = id; 251 | return id; 252 | } 253 | } 254 | } 255 | } 256 | closedir(dir); 257 | return -1; 258 | } 259 | 260 | 261 | std::vector < long >MemoryDebug::getvec() 262 | { 263 | return res; 264 | } 265 | 266 | 267 | std::vector < uint8_t > stringToByteArray(const std::string & input) 268 | { 269 | std::vector < uint8_t > result; 270 | std::istringstream stream(input); 271 | char c; 272 | while (stream >> c) 273 | { 274 | result.push_back(c); 275 | } 276 | return result; 277 | } 278 | 279 | 280 | long MemoryDebug::getModuleBase(const char *name, int index) 281 | { 282 | int i = 0; 283 | long start = 0, end = 0; 284 | char line[1024] = { 0 }; 285 | char fname[128]; 286 | sprintf(fname, "/proc/%d/maps", pid); 287 | FILE *p = fopen(fname, "r"); 288 | if (p) 289 | { 290 | while (fgets(line, sizeof(line), p)) 291 | { 292 | if (strstr(line, name) != NULL) 293 | { 294 | i++; 295 | if (i == index) 296 | { 297 | sscanf(line, "%lx-%lx", &start, &end); 298 | break; 299 | } 300 | } 301 | } 302 | fclose(p); 303 | } 304 | return start; 305 | } 306 | 307 | long MemoryDebug::getBssModuleBase(const char *name) 308 | { 309 | FILE *fp; 310 | int cnt = 0; 311 | long start; 312 | char tmp[256]; 313 | fp = NULL; 314 | char line[1024]; 315 | char fname[128]; 316 | sprintf(fname, "/proc/%d/maps", pid); 317 | fp = fopen(fname, "r"); 318 | while (!feof(fp)) 319 | { 320 | fgets(tmp, 256, fp); 321 | if (cnt == 1) 322 | { 323 | if (strstr(tmp, "[anon:.bss]") != NULL) 324 | { 325 | sscanf(tmp, "%lx-%*lx", &start); 326 | break; 327 | } 328 | else 329 | { 330 | cnt = 0; 331 | } 332 | } 333 | if (strstr(tmp, name) != NULL) 334 | { 335 | cnt = 1; 336 | } 337 | } 338 | return start; 339 | } 340 | 341 | size_t MemoryDebug::pwritev(long address, void *buffer, size_t size) 342 | { 343 | struct iovec iov_WriteBuffer, iov_WriteOffset; 344 | iov_WriteBuffer.iov_base = buffer; 345 | iov_WriteBuffer.iov_len = size; 346 | iov_WriteOffset.iov_base = (void *)address; 347 | iov_WriteOffset.iov_len = size; 348 | return syscall(SYS_process_vm_writev, pid, &iov_WriteBuffer, 1, &iov_WriteOffset, 1, 0); 349 | } 350 | 351 | size_t MemoryDebug::preadv(long address, void *buffer, size_t size) 352 | { 353 | struct iovec iov_ReadBuffer, iov_ReadOffset; 354 | iov_ReadBuffer.iov_base = buffer; 355 | iov_ReadBuffer.iov_len = size; 356 | iov_ReadOffset.iov_base = (void *)address; 357 | iov_ReadOffset.iov_len = size; 358 | return syscall(SYS_process_vm_readv, pid, &iov_ReadBuffer, 1, &iov_ReadOffset, 1, 0); 359 | } 360 | 361 | 362 | 363 | 364 | template < class T > void MemoryDebug::searchMem(T value, int type, int mem) 365 | { 366 | size_t size = judgSize(type); 367 | MemPage *mp = nullptr; 368 | AddressData ad; 369 | char path[128] = ""; 370 | char *line = nullptr; 371 | size_t mapsize = 0; 372 | sprintf(path, "/proc/%d/maps", pid); 373 | auto handle = fopen(path, "r"); 374 | while (getline(&line, &mapsize, handle) > 0) 375 | { 376 | mp = (MemPage *) calloc(1, sizeof(MemPage)); 377 | sscanf(line, "%p-%p %s %*p %*p:%*p %*p %[^\n]%s", &mp->start, &mp->end, 378 | mp->flags, mp->name); 379 | if ((memContrast(mp->name, mp->flags) == mem || mem == Mem_Auto) 380 | && strstr(mp->flags, "r") != nullptr) 381 | { 382 | std::unique_ptr < T[] > buf(new T[mp->end - mp->start]); 383 | preadv(mp->start, buf.get(), mp->end - mp->start); 384 | T *p = std::find(buf.get(), buf.get() + (mp->end - mp->start) / size, value); 385 | while (p != buf.get() + (mp->end - mp->start) / size) 386 | { 387 | res.push_back(mp->start + (p - buf.get()) * size); 388 | p = std::find(p + 1, buf.get() + (mp->end - mp->start) / size, value); 389 | } 390 | free(mp); 391 | } 392 | } 393 | 394 | fclose(handle); 395 | } 396 | 397 | using namespace std; 398 | template < class T > void MemoryDebug::SearchOffest(T num, int offset) 399 | { 400 | if (res.size() != 0 && ofstmp.size() == 0) 401 | { 402 | for (auto addr:res) 403 | { 404 | T local_value = 0; 405 | preadv(addr + offset, &local_value, sizeof(T)); 406 | if (local_value == num) 407 | { 408 | ofstmp.push_back(addr); 409 | } 410 | } 411 | res.clear(); 412 | } 413 | else 414 | { 415 | 416 | for (auto addr:ofstmp) 417 | { 418 | T local_value = 0; 419 | preadv(addr + offset, &local_value, sizeof(T)); 420 | if (local_value == num) 421 | { 422 | res.push_back(addr); 423 | } 424 | } 425 | ofstmp.clear(); 426 | } 427 | } 428 | 429 | template < class T > void MemoryDebug::Editoffest(T num, int offset) 430 | { 431 | if (res.size() != 0 && ofstmp.size() == 0) 432 | { 433 | for (auto addr:res) 434 | { 435 | pwritev(addr + offset, &num, sizeof(T)); 436 | } 437 | } 438 | else 439 | { 440 | for (auto addr:ofstmp) 441 | { 442 | pwritev(addr + offset, &num, sizeof(T)); 443 | } 444 | } 445 | } 446 | 447 | 448 | 449 | 450 | template < class T > AddressData MemoryDebug::search(T value, int type, int mem, bool debug) 451 | { 452 | size_t size = judgSize(type); 453 | MemPage *mp = nullptr; 454 | AddressData ad; 455 | char path[128] = ""; 456 | char *line = nullptr; 457 | size_t mapsize = 0; 458 | sprintf(path, "/proc/%d/maps", pid); 459 | auto handle = fopen(path, "r"); 460 | while (getline(&line, &mapsize, handle) > 0) 461 | { 462 | mp = (MemPage *) calloc(1, sizeof(MemPage)); 463 | sscanf(line, "%p-%p %s %*p %*p:%*p %*p %[^\n]%s", &mp->start, &mp->end, 464 | mp->flags, mp->name); 465 | if ((memContrast(mp->name, mp->flags) == mem || mem == Mem_Auto) 466 | && strstr(mp->flags, "r") != nullptr) 467 | { 468 | std::unique_ptr < T[] > buf(new T[mp->end - mp->start]); 469 | preadv(mp->start, buf.get(), mp->end - mp->start); 470 | T *p = std::find(buf.get(), buf.get() + (mp->end - mp->start) / size, value); 471 | while (p != buf.get() + (mp->end - mp->start) / size) 472 | { 473 | ad.addrs.push_back(mp->start + (p - buf.get()) * size); 474 | p = std::find(p + 1, buf.get() + (mp->end - mp->start) / size, value); 475 | } 476 | free(mp); 477 | } 478 | } 479 | 480 | fclose(handle); 481 | return ad; 482 | } 483 | 484 | // 对单个地址的搜索速度还不错 485 | template < class T > AddressData MemoryDebug::searchPointer(std::vector < long >addrs, long offset, 486 | int type, int mem, bool debug) 487 | { 488 | size_t size = judgSize(type); 489 | MemPage *mp = nullptr; 490 | AddressData ad; 491 | char path[128] = ""; 492 | char *line = nullptr; 493 | size_t mapsize = 0; 494 | sprintf(path, "/proc/%d/maps", pid); 495 | auto handle = fopen(path, "r"); 496 | while (getline(&line, &mapsize, handle) > 0) 497 | { 498 | mp = (MemPage *) calloc(1, sizeof(MemPage)); 499 | sscanf(line, "%p-%p %s %*p %*p:%*p %*p %[^\n]%s", &mp->start, &mp->end, mp->flags, 500 | mp->name); 501 | if ((memContrast(mp->name, mp->flags) == mem || memContrast(mp->name, mp->flags) == Mem_Ca 502 | || mem == Mem_Auto) && strstr(mp->flags, "r") != nullptr) 503 | 504 | 505 | { 506 | if (addrs[0] - mp->start <= 0xFFFFFFFF) 507 | { 508 | 509 | 510 | std::vector < long >addrList(addrs.begin(), addrs.end()); 511 | size_t numAddrs = addrList.size(); 512 | std::unique_ptr < T[] > buf(new T[mp->end - mp->start]); 513 | preadv(mp->start, buf.get(), mp->end - mp->start); 514 | T *p = buf.get(); 515 | if (numAddrs > 10) 516 | { 517 | std::unordered_set < long >addrSet(addrs.begin(), addrs.end()); 518 | while (p != buf.get() + (mp->end - mp->start) / size) 519 | { 520 | if (addrSet.find(*p) != addrSet.end()) 521 | { 522 | ad.addrs.push_back(mp->start + (p - buf.get()) * size); 523 | } 524 | p++; 525 | } 526 | } 527 | else 528 | { 529 | while (p != buf.get() + (mp->end - mp->start) / size) 530 | { 531 | for (size_t i = 0; i < numAddrs; i++) 532 | { 533 | if (*p == addrList[i]) 534 | { 535 | ad.addrs.push_back(mp->start + (p - buf.get()) * size); 536 | break; 537 | } 538 | } 539 | p++; 540 | } 541 | } 542 | } 543 | 544 | 545 | 546 | 547 | 548 | free(mp); 549 | } 550 | } 551 | 552 | fclose(handle); 553 | return ad; 554 | } 555 | 556 | 557 | 558 | 559 | 560 | template < typename F > int MemoryDebug::searchCall(int mem, F && call) 561 | { 562 | int ret = 0; 563 | MemPage *mp = nullptr; 564 | char path[128] = ""; 565 | char *line = nullptr; 566 | size_t mapsize = 0; 567 | sprintf(path, "/proc/%d/maps", pid); 568 | auto handle = fopen(path, "r"); 569 | while (getline(&line, &mapsize, handle) > 0) 570 | { 571 | mp = (MemPage *) calloc(1, sizeof(MemPage)); 572 | sscanf(line, "%p-%p %s %*p %*p:%*p %*p %[^\n]%s", &mp->start, &mp->end, mp->flags, 573 | mp->name); 574 | if ((memContrast(mp->name, mp->flags) == mem || mem == Mem_Auto) 575 | && strstr(mp->flags, "r") != nullptr) 576 | { 577 | int sz = mp->end - mp->start; 578 | std::unique_ptr < int[] > buf(new int[sz]); 579 | preadv(mp->start, buf.get(), sz); 580 | call(mp->start, sz, buf.get(), res); 581 | } 582 | free(mp); 583 | } 584 | fclose(handle); 585 | return ret; 586 | } 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | template < typename T > 595 | AddressData MemoryDebug::searchString(const std::string & str, int mem, bool debug) 596 | { 597 | MemPage *mp = nullptr; 598 | AddressData ad; 599 | char path[128] = ""; 600 | char *line = nullptr; 601 | size_t mapsize = 0; 602 | sprintf(path, "/proc/%d/maps", pid); 603 | auto handle = fopen(path, "r"); 604 | 605 | std::vector < T > bytes; 606 | for (char c:str) 607 | { 608 | bytes.push_back(c); 609 | } 610 | 611 | 612 | while (getline(&line, &mapsize, handle) > 0) 613 | { 614 | mp = (MemPage *) calloc(1, sizeof(MemPage)); 615 | sscanf(line, "%p-%p %s %*p %*p:%*p %*p %[^\n]%s", &mp->start, &mp->end, mp->flags, 616 | mp->name); 617 | if ((memContrast(mp->name, mp->flags) == mem || mem == Mem_Auto) 618 | && strstr(mp->flags, "r") != nullptr) 619 | { 620 | std::unique_ptr < T[] > buf(new T[mp->end - mp->start]); 621 | preadv(mp->start, buf.get(), mp->end - mp->start); 622 | for (size_t i = 0; i < mp->end - mp->start; ++i) 623 | { 624 | bool match = true; 625 | for (size_t j = 0; j < bytes.size(); ++j) 626 | { 627 | if (buf[i + j] != bytes[j]) 628 | { 629 | match = false; 630 | break; 631 | } 632 | } 633 | if (match) 634 | { 635 | ad.addrs.push_back(mp->start + i); 636 | } 637 | } 638 | free(mp); 639 | } 640 | } 641 | fclose(handle); 642 | return ad; 643 | } 644 | 645 | 646 | template < class T > int MemoryDebug::edit(T value, long address, int type, bool debug) 647 | { 648 | if (-1 == pwritev(address, &value, judgSize(type))) 649 | { 650 | if (debug) 651 | printf("修改失败-> addr:%p\n", address); 652 | return -1; 653 | } 654 | else 655 | { 656 | if (debug) 657 | printf("修改成功-> addr:%p\n", address); 658 | return 1; 659 | } 660 | return -1; 661 | } 662 | 663 | template < class T > void MemoryDebug::EditXa(long address, T newValue) 664 | { 665 | char lj[64]; 666 | int handle; 667 | sprintf(lj, "/proc/%d/mem", pid); 668 | handle = open(lj, O_RDWR); 669 | lseek(handle, 0, SEEK_SET); 670 | pwrite64(handle, &newValue, sizeof(T), address); 671 | close(handle); 672 | } 673 | 674 | long MemoryDebug::ReadDword64(long address) 675 | { 676 | long local_ptr = 0; 677 | preadv(address, &local_ptr, 8); 678 | return local_ptr; 679 | } 680 | 681 | long int MemoryDebug::ReadPointer(long address) 682 | { 683 | long int local_value = 0; 684 | preadv(address, &local_value, 4); 685 | return local_value; 686 | } 687 | 688 | int MemoryDebug::ReadDword(long address) 689 | { 690 | int local_value = 0; 691 | preadv(address, &local_value, sizeof(char)); 692 | return local_value; 693 | } 694 | 695 | 696 | 697 | 698 | float MemoryDebug::ReadFloat(long address) 699 | { 700 | float local_value = 0; 701 | preadv(address, &local_value, 4); 702 | return local_value; 703 | } 704 | 705 | long MemoryDebug::ReadLong(long address) 706 | { 707 | long local_value = 0; 708 | preadv(address, &local_value, 8); 709 | return local_value; 710 | } 711 | 712 | void getRoot(char **argv) 713 | { 714 | char shellml[64]; 715 | sprintf(shellml, "su -c %s", *argv); 716 | if (getuid() != 0) 717 | { 718 | system(shellml); 719 | exit(1); 720 | } 721 | } 722 | --------------------------------------------------------------------------------