好久没写笔记了,因为好久没有研究技术了,最近一年来工作做了转型后,比较忙,一直没时间去研究自己喜欢的Android技术,刚好前段时间有个项目需要涉及到一些Android安全分析的,之前有段时间倒是研究过Substrate的Hook框架,但是由于当时C++完全不懂,看代码像看天书,而Xposed的框架当时只是安装后测试了一下别人写好的一些模块(比如抢红包模块),刚好趁这次机会,与同事一同学习一下Xposed的框架,闲话不多说,开整!
老早之前写过Xposed框架的安装,那时候的Android Studio还用的1.x版本,对于C/C++的集成可以说非常差,有兴趣的可以参看我之前写的Xposed框架安装笔记:
而如今N久没用AS,发现已更新至2.x 版本,而且谷歌官方宣布对C/C++的集成愈加完善,于是果断安装最新版本的AS(2.3.3),在新建项目时,即可直接勾选C/C++支持,如下图所示:
1.AS会自动下载并安装C/C++编译所需的NDK环境,接着下载Xposed的api JAR包,我下载的是api-82.jar,将其放入项目的libs文件夹中,然后bulid-library,接着在测试机上安装Xposed的安装包,因为我的测试机是Android 4.4.4系统,因此我安装的是xposed 2.7版本(http://dl-xda.xposed.info/modules/de.robv.android.xposed.installer_v33_36570c.apk)。
2.修改AndroidMainfest.xml文件,在application中添加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!-- 是否是xposed模块,xposed根据这个来判断是否是模块 --> <meta-data android:name="xposedmodule" android:value="true" /> <!-- 模块描述,显示在xposed模块列表那里第二行 --> <meta-data android:name="xposeddescription" android:value="测试Xposed模块" /> <!-- 最低xposed版本号(lib文件名可知) --> <meta-data android:name="xposedminversion" android:value="54" /> |
3.新建一个assets文件夹,记得放在main目录下,在assets文件夹下创建一个文件,名叫xposed_init,写入你要创建的类名,我的是“com.example.kuhn.xposed.Module”
4.接着修改app目录下的build.gradle 文件,将dependencies 中的fileTree和files前面的compile改为“provided”,如下图:
1 2 3 4 5 6 7 8 9 |
dependencies { provided fileTree(include: ['*.jar'], dir: 'libs') androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support.constraint:constraint-layout:1.0.2' testCompile 'junit:junit:4.12' provided files('libs/api-82.jar') } |
5.然后创建Module类,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
package com.example.kuhn.xposed; /** * Created by kuhn on 2017/10/13. */ import java.lang.reflect.Field; import dalvik.system.DexFile; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; public class Module implements IXposedHookLoadPackage { // native方法在libnativelib.so库文件中实现 public native void dumpdex(int cookie); // 内部类 class dumpThread implements Runnable { int cookide; public dumpThread(int cookide){ // 保存dex文件的mCookie值 this.cookide = cookide; } @Override public void run() { try { // 休眠5s 时间足够壳修复dex Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 从内存中dump出解密后的内存dex文件 dumpdex(cookide); } } @Override public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { // 判断是否是要Hook的包名(xxx.yyy.zzz为需要脱壳的apk的包名) if (lpparam.packageName.equals("xxx.yyy.zzz")){ XposedBridge.log("Loaded App:" + lpparam.packageName); // 加载动态库文件libnativelib.so System.load("/data/data/com.example.kuhn.xposed/lib/libnative-lib.so"); //System.loadLibrary("native-lib"); // 对类dalvik.system.DexFile的方法loadDex进行java Hook操作 // 获取到需要脱壳apk解密dex文件加载后返回的mCookie值 // 根据mCookie值进行内存dex文件的dump操作 loadhooklib(lpparam); } } private void loadhooklib(XC_LoadPackage.LoadPackageParam lpparam) { // 对类dalvik.system.DexFile的方法loadDex进行dalvik模式下的java Hook操作 // /libcore/dalvik/src/main/java/dalvik/system/DexFile.java // static public DexFile loadDex(String sourcePathName, String outputPathName, int flags) // http://androidxref.com/4.4.4_r1/xref/libcore/dalvik/src/main/java/dalvik/system/DexFile.java#141 XposedHelpers.findAndHookMethod(DexFile.class.getName(), lpparam.classLoader, "loadDex", String.class, String.class, int.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { if (!param.hasThrowable()) { int falg = (Integer) param.args[2]; // 加载的dex文件的路径 String sourcePathName = (String) param.args[0]; // dex被优化后的odex文件的存放路径 String outputPathName = (String) param.args[1]; XposedBridge.log("sourcePathName:" + sourcePathName + " outputPathName:" + outputPathName + " flag" + falg); // 获取dex文件被loadDex后返回的DexFile文件对象 Object object = param.getResult(); if (object instanceof DexFile) { // 通过类反射获取DexFile类的私有成员mCookie的调用Field Field field = ((DexFile) object).getClass().getDeclaredField("mCookie"); // 设置有权限 field.setAccessible(true); // 获取到DexFile类的私有成员mCookie的值 int cookie = field.getInt(object); // 恢复权限 field.setAccessible(false); System.out.println("cookie:" + String.format("%x", cookie)); // 创建线程对需要脱壳的apk进程进行内存dex的dump操作 Thread thread = new Thread(new Module.dumpThread(cookie)); // 启动线程 thread.start(); } } } }); } } |
6.在cpp文件中创建dumpdex.cpp文件,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
#include <jni.h> #include <stdio.h> #include <unistd.h> //#include <string> #include <malloc.h> #include <android/log.h> #include "Object.h" //using std::string; char* getExternalStorageDirectory(JNIEnv* env); void printinfo(const char* tag, const char* fmt, ...); char* jstringTostring(JNIEnv* env, jstring str) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("utf-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray)env->CallObjectMethod(str, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*)malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; } // 通过jni函数反射调用java方法获取到设备的scard卡的文件路径 char* getExternalStorageDirectory(JNIEnv* env) { jclass Environment = env->FindClass("android/os/Environment"); if (Environment != NULL) { //Messageprint::printinfo("util", "Environment class have found"); jmethodID getExternalStorageDirectoryID = env->GetStaticMethodID(Environment, "getExternalStorageDirectory", "()Ljava/io/File;"); if (getExternalStorageDirectoryID != NULL) { jobject fileobject = env->CallStaticObjectMethod(Environment, getExternalStorageDirectoryID); jclass Fileclass = env->FindClass("java/io/File"); jmethodID getAbsolutePathId = env->GetMethodID(Fileclass, "getAbsolutePath", "()Ljava/lang/String;"); jstring jstringPath = (jstring)env->CallObjectMethod(fileobject, getAbsolutePathId); char* StorageDirectoryPath = jstringTostring(env, jstringPath); return StorageDirectoryPath; } } return NULL; } // 使用了stl的库函数 #ifdef __cplusplus extern "C" { #endif // 调用native层实现的jni方法dumpdex JNIEXPORT void JNICALL Java_com_example_kuhn_xposed_Module_dumpdex(JNIEnv *env, jobject instance, jint cookie) { DexOrJar* pDexOrJar = (DexOrJar*)cookie; DvmDex* pDvmDex; //打印dex文件的内存加载路径 printf("jni", pDexOrJar->fileName); // 判断当前mCookie值是否是dex文件的 if (pDexOrJar->isDex) { // 得到内存加载的odex文件的信息结构体 pDvmDex = pDexOrJar->pRawDexFile->pDvmDex; } else { pDvmDex = pDexOrJar->pJarFile->pDvmDex; } // 获取到描述内存加载的odex文件信息的结构体DexFile DexFile* dexFile = pDvmDex->pDexFile; // 得到内存加载的odex文件的基地址(起始地址) MemMapping mapping = pDvmDex->memMap; printinfo("jni","MemMapping:addr:%x length:%x baseAddr:%x baseLength:%x", mapping.addr, mapping.length, mapping.baseAddr, mapping.baseLength); // 通过jni函数反射调用java方法获取到设备的scard卡的文件路径 char* path = getExternalStorageDirectory(env); char szBufferDexPath[128]; memset(szBufferDexPath, 0, sizeof(szBufferDexPath)); memcpy(szBufferDexPath, path, strlen(path)); // 拼接字符串得到dump的dex文件的路径 strcat(szBufferDexPath, "/xxxx.dex"); printinfo("dump dex path: %s", szBufferDexPath); // F_OK = 0 if (!access(szBufferDexPath, F_OK)) { // 删除已经存在的文件 remove(szBufferDexPath); } // 创建新文件保存dump的dex文件 FILE* file = fopen(szBufferDexPath, "wb+"); // 保存三倍dex文件长度(比较暴力,可以参考dexhunter的实现代码进行优化) fwrite(mapping.addr,mapping.length*3,1,file); // 关闭文件 fclose(file); } #ifdef __cplusplus } #endif // 打印Log日志信息 void printinfo(const char* tag, const char* fmt, ...) { va_list ap; char buf[1024]; va_start(ap, fmt); vsnprintf(buf, 1024, fmt, ap); va_end(ap); __android_log_write(ANDROID_LOG_INFO, tag, buf); } |
7.创建Object.h文件,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 |
// // Created by hekun on 2017/9/28. // #ifndef HELPTOOLCLIENT_OBJECT_H #define HELPTOOLCLIENT_OBJECT_H #include <stddef.h> //#include <cstdint> #include <pthread.h> typedef uint8_t u1; typedef uint16_t u2; typedef uint32_t u4; typedef uint64_t u8; typedef int8_t s1; typedef int16_t s2; typedef int32_t s4; typedef int64_t s8; /* fwd decl */ struct DataObject; struct InitiatingLoaderList; struct ClassObject; struct StringObject; struct ArrayObject; struct Method; struct ExceptionEntry; struct LineNumEntry; struct StaticField; struct InstField; struct Field; struct RegisterMap; struct Object; union JValue { u1 z; s1 b; u2 c; s2 s; s4 i; s8 j; float f; double d; Object* l; }; typedef void (*DalvikBridgeFunc)(const u4* args, JValue* pResult, const Method* method, struct Thread* self); enum AccessFlags { ACC_MIRANDA = 0x8000, // method (internal to VM) JAVA_FLAGS_MASK = 0xffff, // bits set from Java sources (low 16) }; typedef void (*DalvikNativeFunc)(const u4* args, JValue* pResult); enum ClassFlags { CLASS_ISFINALIZABLE = (1 << 31), // class/ancestor overrides finalize() CLASS_ISARRAY = (1 << 30), // class is a "[*" CLASS_ISOBJECTARRAY = (1 << 29), // class is a "[L*" or "[[*" CLASS_ISCLASS = (1 << 28), // class is *the* class Class CLASS_ISREFERENCE = (1 << 27), // class is a soft/weak/phantom ref // only ISREFERENCE is set --> soft CLASS_ISWEAKREFERENCE = (1 << 26), // class is a weak reference CLASS_ISFINALIZERREFERENCE = (1 << 25), // class is a finalizer reference CLASS_ISPHANTOMREFERENCE = (1 << 24), // class is a phantom reference CLASS_MULTIPLE_DEFS = (1 << 23), // DEX verifier: defs in multiple DEXs /* unlike the others, these can be present in the optimized DEX file */ CLASS_ISOPTIMIZED = (1 << 17), // class may contain opt instrs CLASS_ISPREVERIFIED = (1 << 16), // class has been pre-verified }; #define EXPECTED_FILE_FLAGS \ (ACC_CLASS_MASK | CLASS_ISPREVERIFIED | CLASS_ISOPTIMIZED) #define SET_CLASS_FLAG(clazz, flag) \ do { (clazz)->accessFlags |= (flag); } while (0) #define CLEAR_CLASS_FLAG(clazz, flag) \ do { (clazz)->accessFlags &= ~(flag); } while (0) #define IS_CLASS_FLAG_SET(clazz, flag) \ (((clazz)->accessFlags & (flag)) != 0) #define GET_CLASS_FLAG_GROUP(clazz, flags) \ ((u4)((clazz)->accessFlags & (flags))) enum MethodFlags { METHOD_ISWRITABLE = (1 << 31), // the method's code is writable }; #define SET_METHOD_FLAG(method, flag) \ do { (method)->accessFlags |= (flag); } while (0) #define CLEAR_METHOD_FLAG(method, flag) \ do { (method)->accessFlags &= ~(flag); } while (0) #define IS_METHOD_FLAG_SET(method, flag) \ (((method)->accessFlags & (flag)) != 0) #define GET_METHOD_FLAG_GROUP(method, flags) \ ((u4)((method)->accessFlags & (flags))) enum ClassStatus { CLASS_ERROR = -1, CLASS_NOTREADY = 0, CLASS_IDX = 1, /* loaded, DEX idx in super or ifaces */ CLASS_LOADED = 2, /* DEX idx values resolved */ CLASS_RESOLVED = 3, /* part of linking */ CLASS_VERIFYING = 4, /* in the process of being verified */ CLASS_VERIFIED = 5, /* logically part of linking; done pre-init */ CLASS_INITIALIZING = 6, /* class init in progress */ CLASS_INITIALIZED = 7, /* ready to go */ }; #define CLASS_WALK_SUPER ((unsigned int)(3)) #define CLASS_SMALLEST_OFFSET (sizeof(struct Object)) #define CLASS_BITS_PER_WORD (sizeof(unsigned long int) * 8) #define CLASS_OFFSET_ALIGNMENT 4 #define CLASS_HIGH_BIT ((unsigned int)1 << (CLASS_BITS_PER_WORD - 1)) #define _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) \ (((unsigned int)(byteOffset) - CLASS_SMALLEST_OFFSET) / \ CLASS_OFFSET_ALIGNMENT) #define CLASS_CAN_ENCODE_OFFSET(byteOffset) \ (_CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) < CLASS_BITS_PER_WORD) #define CLASS_BIT_FROM_OFFSET(byteOffset) \ (CLASS_HIGH_BIT >> _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset)) #define CLASS_OFFSET_FROM_CLZ(rshift) \ (((int)(rshift) * CLASS_OFFSET_ALIGNMENT) + CLASS_SMALLEST_OFFSET) struct InterfaceEntry { ClassObject* clazz; int* methodIndexArray; }; struct Object { ClassObject* clazz; u4 lock; }; #define DVM_OBJECT_INIT(obj, clazz_) \ dvmSetFieldObject(obj, OFFSETOF_MEMBER(Object, clazz), clazz_) struct DataObject : Object { u4 instanceData[1]; }; struct StringObject : Object { u4 instanceData[1]; int length() const; int utfLength() const; ArrayObject* array() const; const u2* chars() const; }; struct ArrayObject : Object { u4 length; u8 contents[1]; }; struct InitiatingLoaderList { Object** initiatingLoaders; int initiatingLoaderCount; }; struct Field { ClassObject* clazz; /* class in which the field is declared */ const char* name; const char* signature; /* e.g. "I", "[C", "Landroid/os/Debug;" */ u4 accessFlags; }; struct StaticField : Field { JValue value; /* initially set from DEX for primitives */ }; struct InstField : Field { int byteOffset; }; #define CLASS_FIELD_SLOTS 4 enum PrimitiveType { PRIM_NOT = 0, /* value is a reference type, not a primitive type */ PRIM_VOID = 1, PRIM_BOOLEAN = 2, PRIM_BYTE = 3, PRIM_SHORT = 4, PRIM_CHAR = 5, PRIM_INT = 6, PRIM_LONG = 7, PRIM_FLOAT = 8, PRIM_DOUBLE = 9, }; // java类的描述结构体 struct ClassObject : Object { u4 instanceData[CLASS_FIELD_SLOTS]; const char* descriptor; char* descriptorAlloc; u4 accessFlags; u4 serialNumber; void* pDvmDex; ClassStatus status; ClassObject* verifyErrorClass; u4 initThreadId; size_t objectSize; ClassObject* elementClass; int arrayDim; PrimitiveType primitiveType; ClassObject* super; Object* classLoader; InitiatingLoaderList initiatingLoaderList; int interfaceCount; ClassObject** interfaces; int directMethodCount; Method* directMethods; int virtualMethodCount; Method* virtualMethods; int vtableCount; Method** vtable; int iftableCount; InterfaceEntry* iftable; int ifviPoolCount; int* ifviPool; int ifieldCount; int ifieldRefCount; // number of fields that are object refs InstField* ifields; u4 refOffsets; /* source file name, if known */ const char* sourceFile; int sfieldCount; StaticField sfields[]; /* MUST be last item */ }; struct DexProto { const void * dexFile; /* file the idx refers to */ u4 protoIdx; /* index into proto_ids table of dexFile */ }; struct Method { /* the class we are a part of */ ClassObject* clazz; u4 accessFlags; u2 methodIndex; u2 registersSize; /* ins + locals */ u2 outsSize; u2 insSize; /* method name, e.g. "<init>" or "eatLunch" */ const char* name; DexProto prototype; const char* shorty; const u2* insns; /* instructions, in memory-mapped .dex */ int jniArgInfo; DalvikBridgeFunc nativeFunc; bool fastJni; bool noRef; bool shouldTrace; const RegisterMap* registerMap; /* set if method was called during method profiling */ bool inProfile; }; enum { ACC_PUBLIC = 0x00000001, // class, field, method, ic ACC_PRIVATE = 0x00000002, // field, method, ic ACC_PROTECTED = 0x00000004, // field, method, ic ACC_STATIC = 0x00000008, // field, method, ic ACC_FINAL = 0x00000010, // class, field, method, ic ACC_SYNCHRONIZED = 0x00000020, // method (only allowed on natives) ACC_SUPER = 0x00000020, // class (not used in Dalvik) ACC_VOLATILE = 0x00000040, // field ACC_BRIDGE = 0x00000040, // method (1.5) ACC_TRANSIENT = 0x00000080, // field ACC_VARARGS = 0x00000080, // method (1.5) ACC_NATIVE = 0x00000100, // method ACC_INTERFACE = 0x00000200, // class, ic ACC_ABSTRACT = 0x00000400, // class, method, ic ACC_STRICT = 0x00000800, // method ACC_SYNTHETIC = 0x00001000, // field, method, ic ACC_ANNOTATION = 0x00002000, // class, ic (1.5) ACC_ENUM = 0x00004000, // class, field, ic (1.5) ACC_CONSTRUCTOR = 0x00010000, // method (Dalvik only) ACC_DECLARED_SYNCHRONIZED = 0x00020000, // method (Dalvik only) ACC_CLASS_MASK = (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM), ACC_INNER_CLASS_MASK = (ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC), ACC_FIELD_MASK = (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM), ACC_METHOD_MASK = (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR | ACC_DECLARED_SYNCHRONIZED), }; bool dvmIsPublicMethod(const Method* method) { return (method->accessFlags & ACC_PUBLIC) != 0; } bool dvmIsPrivateMethod(const Method* method) { return (method->accessFlags & ACC_PRIVATE) != 0; } bool dvmIsStaticMethod(const Method* method) { return (method->accessFlags & ACC_STATIC) != 0; } bool dvmIsSynchronizedMethod(const Method* method) { return (method->accessFlags & ACC_SYNCHRONIZED) != 0; } bool dvmIsDeclaredSynchronizedMethod(const Method* method) { return (method->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0; } bool dvmIsFinalMethod(const Method* method) { return (method->accessFlags & ACC_FINAL) != 0; } bool dvmIsNativeMethod(const Method* method) { return (method->accessFlags & ACC_NATIVE) != 0; } bool dvmIsAbstractMethod(const Method* method) { return (method->accessFlags & ACC_ABSTRACT) != 0; } bool dvmIsSyntheticMethod(const Method* method) { return (method->accessFlags & ACC_SYNTHETIC) != 0; } bool dvmIsMirandaMethod(const Method* method) { return (method->accessFlags & ACC_MIRANDA) != 0; } bool dvmIsConstructorMethod(const Method* method) { return *method->name == '<'; } /* Dalvik puts private, static, and constructors into non-virtual table */ bool dvmIsDirectMethod(const Method* method) { return dvmIsPrivateMethod(method) || dvmIsStaticMethod(method) || dvmIsConstructorMethod(method); } /* Get whether the given method has associated bytecode. This is the * case for methods which are neither native nor abstract. */ bool dvmIsBytecodeMethod(const Method* method) { return (method->accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) == 0; } bool dvmIsProtectedField(const Field* field) { return (field->accessFlags & ACC_PROTECTED) != 0; } bool dvmIsStaticField(const Field* field) { return (field->accessFlags & ACC_STATIC) != 0; } bool dvmIsFinalField(const Field* field) { return (field->accessFlags & ACC_FINAL) != 0; } bool dvmIsVolatileField(const Field* field) { return (field->accessFlags & ACC_VOLATILE) != 0; } bool dvmIsInterfaceClass(const ClassObject* clazz) { return (clazz->accessFlags & ACC_INTERFACE) != 0; } bool dvmIsPublicClass(const ClassObject* clazz) { return (clazz->accessFlags & ACC_PUBLIC) != 0; } bool dvmIsFinalClass(const ClassObject* clazz) { return (clazz->accessFlags & ACC_FINAL) != 0; } bool dvmIsAbstractClass(const ClassObject* clazz) { return (clazz->accessFlags & ACC_ABSTRACT) != 0; } bool dvmIsAnnotationClass(const ClassObject* clazz) { return (clazz->accessFlags & ACC_ANNOTATION) != 0; } bool dvmIsPrimitiveClass(const ClassObject* clazz) { return clazz->primitiveType != PRIM_NOT; } /* linked, here meaning prepared and resolved */ bool dvmIsClassLinked(const ClassObject* clazz) { return clazz->status >= CLASS_RESOLVED; } /* has class been verified? */ bool dvmIsClassVerified(const ClassObject* clazz) { return clazz->status >= CLASS_VERIFIED; } bool dvmIsClassInitialized(const ClassObject* clazz) { return (clazz->status == CLASS_INITIALIZED); } /* annotation constants */ enum { kDexVisibilityBuild = 0x00, /* annotation visibility */ kDexVisibilityRuntime = 0x01, kDexVisibilitySystem = 0x02, kDexAnnotationByte = 0x00, kDexAnnotationShort = 0x02, kDexAnnotationChar = 0x03, kDexAnnotationInt = 0x04, kDexAnnotationLong = 0x06, kDexAnnotationFloat = 0x10, kDexAnnotationDouble = 0x11, kDexAnnotationString = 0x17, kDexAnnotationType = 0x18, kDexAnnotationField = 0x19, kDexAnnotationMethod = 0x1a, kDexAnnotationEnum = 0x1b, kDexAnnotationArray = 0x1c, kDexAnnotationAnnotation = 0x1d, kDexAnnotationNull = 0x1e, kDexAnnotationBoolean = 0x1f, kDexAnnotationValueTypeMask = 0x1f, /* low 5 bits */ kDexAnnotationValueArgShift = 5, }; /* map item type codes */ enum { kDexTypeHeaderItem = 0x0000, kDexTypeStringIdItem = 0x0001, kDexTypeTypeIdItem = 0x0002, kDexTypeProtoIdItem = 0x0003, kDexTypeFieldIdItem = 0x0004, kDexTypeMethodIdItem = 0x0005, kDexTypeClassDefItem = 0x0006, kDexTypeMapList = 0x1000, kDexTypeTypeList = 0x1001, kDexTypeAnnotationSetRefList = 0x1002, kDexTypeAnnotationSetItem = 0x1003, kDexTypeClassDataItem = 0x2000, kDexTypeCodeItem = 0x2001, kDexTypeStringDataItem = 0x2002, kDexTypeDebugInfoItem = 0x2003, kDexTypeAnnotationItem = 0x2004, kDexTypeEncodedArrayItem = 0x2005, kDexTypeAnnotationsDirectoryItem = 0x2006, }; /* auxillary data section chunk codes */ enum { kDexChunkClassLookup = 0x434c4b50, /* CLKP */ kDexChunkRegisterMaps = 0x524d4150, /* RMAP */ kDexChunkEnd = 0x41454e44, /* AEND */ }; /* debug info opcodes and constants */ enum { DBG_END_SEQUENCE = 0x00, DBG_ADVANCE_PC = 0x01, DBG_ADVANCE_LINE = 0x02, DBG_START_LOCAL = 0x03, DBG_START_LOCAL_EXTENDED = 0x04, DBG_END_LOCAL = 0x05, DBG_RESTART_LOCAL = 0x06, DBG_SET_PROLOGUE_END = 0x07, DBG_SET_EPILOGUE_BEGIN = 0x08, DBG_SET_FILE = 0x09, DBG_FIRST_SPECIAL = 0x0a, DBG_LINE_BASE = -4, DBG_LINE_RANGE = 15, }; enum { kSHA1DigestLen = 20, kSHA1DigestOutputLen = kSHA1DigestLen * 2 + 1 }; /* * Direct-mapped "header_item" struct. */ struct DexHeader { u1 magic[8]; /* includes version number */ u4 checksum; /* adler32 checksum */ u1 signature[kSHA1DigestLen]; /* SHA-1 hash */ u4 fileSize; /* length of entire file */ u4 headerSize; /* offset to start of next section */ u4 endianTag; u4 linkSize; u4 linkOff; u4 mapOff; u4 stringIdsSize; u4 stringIdsOff; u4 typeIdsSize; u4 typeIdsOff; u4 protoIdsSize; u4 protoIdsOff; u4 fieldIdsSize; u4 fieldIdsOff; u4 methodIdsSize; u4 methodIdsOff; u4 classDefsSize; u4 classDefsOff; u4 dataSize; u4 dataOff; }; /* * Direct-mapped "map_item". */ struct DexMapItem { u2 type; /* type code (see kDexType* above) */ u2 unused; u4 size; /* count of items of the indicated type */ u4 offset; /* file offset to the start of data */ }; /* * Direct-mapped "map_list". */ struct DexMapList { u4 size; /* #of entries in list */ DexMapItem list[1]; /* entries */ }; /* * Direct-mapped "string_id_item". */ struct DexStringId { u4 stringDataOff; /* file offset to string_data_item */ }; /* * Direct-mapped "type_id_item". */ struct DexTypeId { u4 descriptorIdx; /* index into stringIds list for type descriptor */ }; /* * Direct-mapped "field_id_item". */ struct DexFieldId { u2 classIdx; /* index into typeIds list for defining class */ u2 typeIdx; /* index into typeIds for field type */ u4 nameIdx; /* index into stringIds for field name */ }; /* * Direct-mapped "method_id_item". */ struct DexMethodId { u2 classIdx; /* index into typeIds list for defining class */ u2 protoIdx; /* index into protoIds for method prototype */ u4 nameIdx; /* index into stringIds for method name */ }; /* * Direct-mapped "proto_id_item". */ struct DexProtoId { u4 shortyIdx; /* index into stringIds for shorty descriptor */ u4 returnTypeIdx; /* index into typeIds list for return type */ u4 parametersOff; /* file offset to type_list for parameter types */ }; /* * Direct-mapped "class_def_item". */ struct DexClassDef { u4 classIdx; /* index into typeIds for this class */ u4 accessFlags; u4 superclassIdx; /* index into typeIds for superclass */ u4 interfacesOff; /* file offset to DexTypeList */ u4 sourceFileIdx; /* index into stringIds for source file name */ u4 annotationsOff; /* file offset to annotations_directory_item */ u4 classDataOff; /* file offset to class_data_item */ u4 staticValuesOff; /* file offset to DexEncodedArray */ }; /* * Direct-mapped "type_item". */ struct DexTypeItem { u2 typeIdx; /* index into typeIds */ }; /* * Direct-mapped "type_list". */ struct DexTypeList { u4 size; /* #of entries in list */ DexTypeItem list[1]; /* entries */ }; typedef struct DexMapId { u2 type; /*Section type*/ u2 unused; /*unused*/ u4 size; /* section size*/ u4 offset; /* section offset */ } DexMapId; /* * Direct-mapped "code_item". * * The "catches" table is used when throwing an exception, * "debugInfo" is used when displaying an exception stack trace or * debugging. An offset of zero indicates that there are no entries. */ struct DexCode { u2 registersSize; u2 insSize; u2 outsSize; u2 triesSize; u4 debugInfoOff; /* file offset to debug info stream */ u4 insnsSize; /* size of the insns array, in u2 units */ u2 insns[1]; /* followed by optional u2 padding */ /* followed by try_item[triesSize] */ /* followed by uleb128 handlersSize */ /* followed by catch_handler_item[handlersSize] */ }; /* * Direct-mapped "try_item". */ struct DexTry { u4 startAddr; /* start address, in 16-bit code units */ u2 insnCount; /* instruction count, in 16-bit code units */ u2 handlerOff; /* offset in encoded handler data to handlers */ }; /* * Link table. Currently undefined. */ struct DexLink { u1 bleargh; }; /* * Direct-mapped "annotations_directory_item". */ struct DexAnnotationsDirectoryItem { u4 classAnnotationsOff; /* offset to DexAnnotationSetItem */ u4 fieldsSize; /* count of DexFieldAnnotationsItem */ u4 methodsSize; /* count of DexMethodAnnotationsItem */ u4 parametersSize; /* count of DexParameterAnnotationsItem */ /* followed by DexFieldAnnotationsItem[fieldsSize] */ /* followed by DexMethodAnnotationsItem[methodsSize] */ /* followed by DexParameterAnnotationsItem[parametersSize] */ }; /* * Direct-mapped "field_annotations_item". */ struct DexFieldAnnotationsItem { u4 fieldIdx; u4 annotationsOff; /* offset to DexAnnotationSetItem */ }; /* * Direct-mapped "method_annotations_item". */ struct DexMethodAnnotationsItem { u4 methodIdx; u4 annotationsOff; /* offset to DexAnnotationSetItem */ }; /* * Direct-mapped "parameter_annotations_item". */ struct DexParameterAnnotationsItem { u4 methodIdx; u4 annotationsOff; /* offset to DexAnotationSetRefList */ }; /* * Direct-mapped "annotation_set_ref_item". */ struct DexAnnotationSetRefItem { u4 annotationsOff; /* offset to DexAnnotationSetItem */ }; /* * Direct-mapped "annotation_set_ref_list". */ struct DexAnnotationSetRefList { u4 size; DexAnnotationSetRefItem list[1]; }; /* * Direct-mapped "annotation_set_item". */ struct DexAnnotationSetItem { u4 size; u4 entries[1]; /* offset to DexAnnotationItem */ }; /* * Direct-mapped "annotation_item". * * NOTE: this structure is byte-aligned. */ struct DexAnnotationItem { u1 visibility; u1 annotation[1]; /* data in encoded_annotation format */ }; /* * Direct-mapped "encoded_array". * * NOTE: this structure is byte-aligned. */ struct DexEncodedArray { u1 array[1]; /* data in encoded_array format */ }; /* * Lookup table for classes. It provides a mapping from class name to * class definition. Used by dexFindClass(). * * We calculate this at DEX optimization time and embed it in the file so we * don't need the same hash table in every VM. This is slightly slower than * a hash table with direct pointers to the items, but because it's shared * there's less of a penalty for using a fairly sparse table. */ struct DexClassLookup { int size; // total size, including "size" int numEntries; // size of table[]; always power of 2 struct { u4 classDescriptorHash; // class descriptor hash code int classDescriptorOffset; // in bytes, from start of DEX int classDefOffset; // in bytes, from start of DEX } table[1]; }; /* * Header added by DEX optimization pass. Values are always written in * local byte and structure padding. The first field (magic + version) * is guaranteed to be present and directly readable for all expected * compiler configurations; the rest is version-dependent. * * Try to keep this simple and fixed-size. */ struct DexOptHeader { u1 magic[8]; /* includes version number */ u4 dexOffset; /* file offset of DEX header */ u4 dexLength; u4 depsOffset; /* offset of optimized DEX dependency table */ u4 depsLength; u4 optOffset; /* file offset of optimized data tables */ u4 optLength; u4 flags; /* some info flags */ u4 checksum; /* adler32 checksum covering deps/opt */ /* pad for 64-bit alignment if necessary */ }; #define DEX_OPT_FLAG_BIG (1<<1) /* swapped to big-endian */ #define DEX_INTERFACE_CACHE_SIZE 128 /* must be power of 2 */ /* * Structure representing a DEX file. * * Code should regard DexFile as opaque, using the API calls provided here * to access specific structures. */ struct DexFile { /* directly-mapped "opt" header */ const DexOptHeader* pOptHeader; /* pointers to directly-mapped structs and arrays in base DEX */ const DexHeader* pHeader; const DexStringId* pStringIds; const DexTypeId* pTypeIds; const DexFieldId* pFieldIds; const DexMethodId* pMethodIds; const DexProtoId* pProtoIds; const DexClassDef* pClassDefs; const DexLink* pLinkData; /* * These are mapped out of the "auxillary" section, and may not be * included in the file. */ const DexClassLookup* pClassLookup; const void* pRegisterMapPool; // RegisterMapClassPool /* points to start of DEX file data */ const u1* baseAddr; /* track memory overhead for auxillary structures */ int overhead; /* additional app-specific data structures associated with the DEX */ //void* auxData; }; struct MemMapping { void* addr; /* start of data */ size_t length; /* length of data */ void* baseAddr; /* page-aligned base address */ size_t baseLength; /* length of mapping */ }; struct DvmDex { /* pointer to the DexFile we're associated with */ DexFile* pDexFile; /* clone of pDexFile->pHeader (it's used frequently enough) */ const DexHeader* pHeader; /* interned strings; parallel to "stringIds" */ struct StringObject** pResStrings; /* resolved classes; parallel to "typeIds" */ struct ClassObject** pResClasses; /* resolved methods; parallel to "methodIds" */ struct Method** pResMethods; /* resolved instance fields; parallel to "fieldIds" */ /* (this holds both InstField and StaticField) */ struct Field** pResFields; /* interface method lookup cache */ struct AtomicCache* pInterfaceCache; /* shared memory region with file contents */ bool isMappedReadOnly; MemMapping memMap; jobject dex_object; /* lock ensuring mutual exclusion during updates */ pthread_mutex_t modLock; }; struct JarFile { u4* Nocare[9]; char* cacheFileName; DvmDex* pDvmDex; }; struct RawDexFile { char* cacheFileName; struct DvmDex* pDvmDex; //DvmDex* }; struct DexOrJar { char* fileName; bool isDex; bool okayToFree; RawDexFile* pRawDexFile; JarFile* pJarFile; u1* pDexMemory; // malloc()ed memory, if any }; #endif //HELPTOOLCLIENT_OBJECT_H |
8.修改CMakeList.txt文件,加入dumpdex.cpp文件,如下图:
9.编译apk,安装至测试机,然后Xposed中在模块中勾选“Xposed”模块,软重启后,就会看到相应的提示了,如下图:
10.在sdcard的根目录下即会出现dump出来的dex文件。
11.使用JEB打开dex:
额外笔记:
1.Android加固实现一般是基于DexClassLoader的加载流程来实现的,DexClassLoader在整个java层的实现流程如下图:
2.从DexClassLoader到openDexFileNative函数的整个流程下来,DexClassLoader的java层实现全部完成,openDexFileNative之后是由native层函数实现,dex文件的加固是基于DexClassLoader的加载流程而来的,dex文件优化为odex文件后加载到apk进程的内存中返回是mCookie值,这个mCookie值就是dex文件加载到内存之后的镜像描述结构体指针DexOrJar* ,DexOrJar结构体的具体代码可参见:
http://androidxref.com/4.4.4_r1/xref/dalvik/vm/native/dalvik_system_DexFile.cpp#DexOrJar
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* * Internal struct for managing DexFile. */ struct DexOrJar { // 描述的dex文件或者jar文件的路径 char* fileName; // 是否是dex文件的标识 bool isDex; bool okayToFree; // 描述dex文件内存加载镜像odex文件的结构体 RawDexFile* pRawDexFile; // 描述内存加载后的jar文件的结构体 JarFile* pJarFile; u1* pDexMemory; // malloc()ed memory, if any }; |
3.openDexFileNative在native层对应的实现函数是 Dalvik_dalvik_system_DexFile_openDexFileNative,代码具体参见:
http://androidxref.com/4.4.4_r1/xref/dalvik/vm/native/dalvik_system_DexFile.cpp#151
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 对应函数的注册结构体 const DalvikNativeMethod dvm_dalvik_system_DexFile[] = { { "openDexFileNative", "(Ljava/lang/String;Ljava/lang/String;I)I", Dalvik_dalvik_system_DexFile_openDexFileNative }, { "openDexFile", "([B)I", Dalvik_dalvik_system_DexFile_openDexFile_bytearray }, { "closeDexFile", "(I)V", Dalvik_dalvik_system_DexFile_closeDexFile }, { "defineClassNative", "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;", Dalvik_dalvik_system_DexFile_defineClassNative }, { "getClassNameList", "(I)[Ljava/lang/String;", Dalvik_dalvik_system_DexFile_getClassNameList }, { "isDexOptNeeded", "(Ljava/lang/String;)Z", Dalvik_dalvik_system_DexFile_isDexOptNeeded }, { NULL, NULL, NULL }, }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
/* * private static int openDexFileNative(String sourceName, String outputName, * int flags) throws IOException * * Open a DEX file, returning a pointer to our internal data structure. * * "sourceName" should point to the "source" jar or DEX file. * * If "outputName" is NULL, the DEX code will automatically find the * "optimized" version in the cache directory, creating it if necessary. * If it's non-NULL, the specified file will be used instead. * * TODO: at present we will happily open the same file more than once. * To optimize this away we could search for existing entries in the hash * table and refCount them. Requires atomic ops or adding "synchronized" * to the non-native code that calls here. * * TODO: should be using "long" for a pointer. */ static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args, JValue* pResult) { StringObject* sourceNameObj = (StringObject*) args[0]; StringObject* outputNameObj = (StringObject*) args[1]; DexOrJar* pDexOrJar = NULL; JarFile* pJarFile; RawDexFile* pRawDexFile; char* sourceName; char* outputName; if (sourceNameObj == NULL) { dvmThrowNullPointerException("sourceName == null"); RETURN_VOID(); } sourceName = dvmCreateCstrFromString(sourceNameObj); if (outputNameObj != NULL) outputName = dvmCreateCstrFromString(outputNameObj); else outputName = NULL; /* * We have to deal with the possibility that somebody might try to * open one of our bootstrap class DEX files. The set of dependencies * will be different, and hence the results of optimization might be * different, which means we'd actually need to have two versions of * the optimized DEX: one that only knows about part of the boot class * path, and one that knows about everything in it. The latter might * optimize field/method accesses based on a class that appeared later * in the class path. * * We can't let the user-defined class loader open it and start using * the classes, since the optimized form of the code skips some of * the method and field resolution that we would ordinarily do, and * we'd have the wrong semantics. * * We have to reject attempts to manually open a DEX file from the boot * class path. The easiest way to do this is by filename, which works * out because variations in name (e.g. "/system/framework/./ext.jar") * result in us hitting a different dalvik-cache entry. It's also fine * if the caller specifies their own output file. */ if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) { ALOGW("Refusing to reopen boot DEX '%s'", sourceName); dvmThrowIOException( "Re-opening BOOTCLASSPATH DEX files is not allowed"); free(sourceName); free(outputName); RETURN_VOID(); } /* * Try to open it directly as a DEX if the name ends with ".dex". * If that fails (or isn't tried in the first place), try it as a * Zip with a "classes.dex" inside. */ if (hasDexExtension(sourceName) && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) { ALOGV("Opening DEX file '%s' (DEX)", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = true; pDexOrJar->pRawDexFile = pRawDexFile; pDexOrJar->pDexMemory = NULL; } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) { ALOGV("Opening DEX file '%s' (Jar)", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = false; pDexOrJar->pJarFile = pJarFile; pDexOrJar->pDexMemory = NULL; } else { ALOGV("Unable to open DEX file '%s'", sourceName); dvmThrowIOException("unable to open DEX file"); } if (pDexOrJar != NULL) { pDexOrJar->fileName = sourceName; addToDexFileTable(pDexOrJar); } else { free(sourceName); } free(outputName); RETURN_PTR(pDexOrJar); } |
4.基于Xposed Hook开发脱壳工具其实就是使用Xposed Hook框架Hook掉 DexClassLoader加载dex文件流程中类dalvik.system.DexFile的方法loadDex函数,等到loadDex函数返回时拿到dex文件内存加载后的mCookie值,然后内存dump出mCookie值描述的dex文件的值。dalvik.system.DexFile的loadDex函数的实现如下:
http://androidxref.com/4.4.4_r1/xref/libcore/dalvik/src/main/java/dalvik/system/DexFile.java#141
PS:因为dump的方法比较暴力,故dump出来的dex可以通过手动优化一下(如dd大法,dd if=in.dat of=out.dat count=1 bs=6941960),后面可以考虑将dump这块的代码改为dexhunter的代码进行结合,相信会更加强大。
另外,如果要复习dex文件格式,可参见我以前写的这篇文章:
参考文章:
http://blog.csdn.net/QQ1084283172/article/details/77966109