本文博客地址:http://blog.csdn.net/qq1084283172/article/details/75710411

一、前 言

在前面的博客中已经学习了作者crmulliner编写的,针对Android系统的跨进程 inline Hook的实现即Android native Hook框架adbi的实现。Android Hook框架adbi主要是针对的Android的native函数进行inline Hook操作,那么如果需要对Android系统中Java编写的函数进行Hook,又该怎么操作呢?作者crmulliner后面又实现了针对Android的java函数进行Hook的框架ddi。adbi Hook框架和ddi java Hook框架的实现流程都差不多,首先实现root权限下Android跨进程的so注入,在so库文件被加载注入到目标进程中时,调用该so库文件的构造函数有一次代码执行的机会,利用这一次代码执行的机会既可以进行针对Android系统底层native函数的inline Hook或者.got Hook等,也可以进行针对Android系统的java函数进行dalvik Hook或者art Hook。Android平台的所有跨进程Hook都是基于Android系统root权限下的so注入和一次代码执行机会来实现的,只要能实现Android的跨进程注入(这个注入既可以是shellcode代码片段的注入也可以是so库文件的注入)就可以在目标进程中做很多的事情。


这里简要说下Android平台针对java函数的dalvik Hook(暂时不讨论基于art模式下的java函数的Hook,后面有时间再讨论)。dalvik虚拟机模式下java函数的Hook主要是修改存储java函数的信息结构体Method。在dalvik虚拟机中,java函数的形式是以Method结构体来表现的,每一个java编写的类成员函数最终在dalvik虚拟机中以Method结构体的形式存在。基于dalvik虚拟机的java Hook通过修改java函数的Method结构体中的函数属性值access_flags将一个java层函数修改为native属性的函数,这样一个java层实现的函数被修改为native层实现的函数,我们就可以将自定义编写的native函数替换掉原来的java层函数实现,从而实现基于dalvik虚拟机的java Hook。

二、Android平台java Hook框架ddi实现dalvik 模式下Hook的步骤

1)在so库文件被加载注入到目标进程中时,调用该so库文件.init段的构造函数获取dalvik模式下执行Hook java函数代码的机会;


2) dalvik虚拟机模式下,动态加载”libdvm.so”库文件并获取该动态加载的”libdvm.so”库文件中java Hook实现需要的导出函数的指针和导出全局变量,例如获取导出函数dvmFindLoadedClass、dvmFindVirtualMethodHierByDescriptor、dvmFindDirectMethodByDescriptor、dvmUseJNIBridge等的函数指针;


3)调用上面步骤中提到的”libdvm.so”库文件中的导出函数 dvmFindLoadedClass 获取被java Hook的java目标函数所在的目标类,然后再调用导出函数 dvmFindVirtualMethodHierByDescriptor、dvmFindDirectMethodByDescriptor 在查找到的目标类中获取被java Hook的目标函数(java层实现的函数)的信息结构体Method;


4)查找到被java Hook的目标函数(java层实现的函数)的信息结构体Method以后,先保存该目标函数的信息结构体Method的原始值,用以后面对目标函数进行java Hook操作后的恢复还原,如果没有保存目标函数的原始Method信息结构体值的话,想在java Hook操作之后再调用原来的java层实现的目标函数的话就不可能了;


5)对将被java Hook的目标函数的信息结构体Method进行修改即修改目标函数信息结构体Method的成员变量 access_flags 的值,实现将一个java层实现的方法改为 native 层实现的本地方法,这样一个java函数就变成了可以被我们自定义替换的native函数;


6)java层实现的目标函数被修改为native属性的本地方法以后,还需要对该被java Hook的目标函数信息结构体Method的成员变量 insSize(函数传参寄存器的数量)、outsSize(局部变量使用寄存器数量)、registersSize(函数调用使用的寄存器的总数)、jniArgInfo、native_func(改为jni桥接函数,例如 dvmResolveNativeMethod)的值进行修正;


7)为被java Hook的目标函数设置新的函数实现,按照被java Hook的目标函数的原始java函数声明实现对应函数声明的native层的jni函数(java Hook的替换函数),然后修改该目标函数的信息结构体Method的成员变量 insns 的值为该native层实现的jni函数,到这里java层实现的目标函数就替换为我们自定义实现的native层的jni函数,从而实现dalvik虚拟机模式下的java Hook。

提示:ddi 框架的 java Hook 实现原理和前面的博客《Android进程so注入Hook java方法》中提到的 java Hook 实现原理是一样的,只是在细节处理上稍有不同,ddi 框架处理的更好,不需要再次进行jni函数的注册。

三、Android平台java Hook框架ddi实现dalvik 模式下Hook的代码分析

1. 在dalvik虚拟机中每一个方法都由一个称作Method的结构体来表示(包括JNI方法),ddi框架实现java Hook就是通过修改目标函数的信息结构体Method来实现的。下面来了解一下Method结构体构成:

  1. /*
  2. * A method. We create one of these for every method in every class
  3. * we load, so try to keep the size to a minimum.
  4. *
  5. * Much of this comes from and could be accessed in the data held in shared
  6. * memory. We hold it all together here for speed. Everything but the
  7. * pointers could be held in a shared table generated by the optimizer;
  8. * if we're willing to convert them to offsets and take the performance
  9. * hit (e.g. "meth->insns" becomes "baseAddr + meth->insnsOffset") we
  10. * could move everything but "nativeFunc".
  11. */
  12. // Android 4.4.4r1源码文件路径 /dalvik/vm/oo/Object.h
  13. struct Method {
  14. /* the class we are a part of */
  15. ClassObject* clazz;
  16. /* access flags; low 16 bits are defined by spec (could be u2?) */
  17. u4 accessFlags;
  18. /*
  19. * For concrete virtual methods, this is the offset of the method
  20. * in "vtable".
  21. *
  22. * For abstract methods in an interface class, this is the offset
  23. * of the method in "iftable[n]->methodIndexArray".
  24. */
  25. u2 methodIndex;
  26. /*
  27. * Method bounds; not needed for an abstract method.
  28. *
  29. * For a native method, we compute the size of the argument list, and
  30. * set "insSize" and "registerSize" equal to it.
  31. */
  32. u2 registersSize; /* ins + locals */
  33. u2 outsSize;
  34. u2 insSize;
  35. /* method name, e.g. "<init>" or "eatLunch" */
  36. const char* name;
  37. /*
  38. * Method prototype descriptor string (return and argument types).
  39. *
  40. * TODO: This currently must specify the DexFile as well as the proto_ids
  41. * index, because generated Proxy classes don't have a DexFile. We can
  42. * remove the DexFile* and reduce the size of this struct if we generate
  43. * a DEX for proxies.
  44. */
  45. DexProto prototype;
  46. /* short-form method descriptor string */
  47. const char* shorty;
  48. /*
  49. * The remaining items are not used for abstract or native methods.
  50. * (JNI is currently hijacking "insns" as a function pointer, set
  51. * after the first call. For internal-native this stays null.)
  52. */
  53. /* the actual code */
  54. const u2* insns; /* instructions, in memory-mapped .dex */
  55. /* JNI: cached argument and return-type hints */
  56. int jniArgInfo;
  57. /*
  58. * JNI: native method ptr; could be actual function or a JNI bridge. We
  59. * don't currently discriminate between DalvikBridgeFunc and
  60. * DalvikNativeFunc; the former takes an argument superset (i.e. two
  61. * extra args) which will be ignored. If necessary we can use
  62. * insns==NULL to detect JNI bridge vs. internal native.
  63. */
  64. DalvikBridgeFunc nativeFunc;
  65. /*
  66. * JNI: true if this static non-synchronized native method (that has no
  67. * reference arguments) needs a JNIEnv* and jclass/jobject. Libcore
  68. * uses this.
  69. */
  70. bool fastJni;
  71. /*
  72. * JNI: true if this method has no reference arguments. This lets the JNI
  73. * bridge avoid scanning the shorty for direct pointers that need to be
  74. * converted to local references.
  75. *
  76. * TODO: replace this with a list of indexes of the reference arguments.
  77. */
  78. bool noRef;
  79. /*
  80. * JNI: true if we should log entry and exit. This is the only way
  81. * developers can log the local references that are passed into their code.
  82. * Used for debugging JNI problems in third-party code.
  83. */
  84. bool shouldTrace;
  85. /*
  86. * Register map data, if available. This will point into the DEX file
  87. * if the data was computed during pre-verification, or into the
  88. * linear alloc area if not.
  89. */
  90. const RegisterMap* registerMap;
  91. /* set if method was called during method profiling */
  92. bool inProfile;
  93. };

(1)clazz:当前方法所在的类;

(2)accessFlags:当前方法所具有的属性,例如:类访问属性、是否静态函数等,accessFlags属性值 的定义如下:

  1. /*
  2. * access flags and masks; the "standard" ones are all <= 0x4000
  3. *
  4. * Note: There are related declarations in vm/oo/Object.h in the ClassFlags
  5. * enum.
  6. */
  7. // Android 4.4.4r1 源码路径 /dalvik/libdex/DexFile.h
  8. enum {
  9. ACC_PUBLIC = 0x00000001, // class, field, method, ic
  10. ACC_PRIVATE = 0x00000002, // field, method, ic
  11. ACC_PROTECTED = 0x00000004, // field, method, ic
  12. ACC_STATIC = 0x00000008, // field, method, ic
  13. ACC_FINAL = 0x00000010, // class, field, method, ic
  14. ACC_SYNCHRONIZED = 0x00000020, // method (only allowed on natives)
  15. ACC_SUPER = 0x00000020, // class (not used in Dalvik)
  16. ACC_VOLATILE = 0x00000040, // field
  17. ACC_BRIDGE = 0x00000040, // method (1.5)
  18. ACC_TRANSIENT = 0x00000080, // field
  19. ACC_VARARGS = 0x00000080, // method (1.5)
  20. ACC_NATIVE = 0x00000100, // method
  21. ACC_INTERFACE = 0x00000200, // class, ic
  22. ACC_ABSTRACT = 0x00000400, // class, method, ic
  23. ACC_STRICT = 0x00000800, // method
  24. ACC_SYNTHETIC = 0x00001000, // field, method, ic
  25. ACC_ANNOTATION = 0x00002000, // class, ic (1.5)
  26. ACC_ENUM = 0x00004000, // class, field, ic (1.5)
  27. ACC_CONSTRUCTOR = 0x00010000, // method (Dalvik only)
  28. ACC_DECLARED_SYNCHRONIZED =
  29. 0x00020000, // method (Dalvik only)
  30. ACC_CLASS_MASK =
  31. (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT
  32. | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),
  33. ACC_INNER_CLASS_MASK =
  34. (ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC),
  35. ACC_FIELD_MASK =
  36. (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
  37. | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM),
  38. ACC_METHOD_MASK =
  39. (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
  40. | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE
  41. | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR
  42. | ACC_DECLARED_SYNCHRONIZED),
  43. };

上面的枚举变量中定义了java的类、类成员方法、类成员变量的单个函数属性值和组合方法并在右边的注释中给出了函数属性值的使用范围。类ClassObject、成员方法Method以及成员变量Field中 accessFlags 的具体含义和使用方法如下图所示:

上面的示意图来自作者Roland_Sun的博客《Android平台下Dalvik层hook框架ddi的研究》,作者的博客写的很不错,借用一下。

(3)methodIndex:对于已经实现了的虚函数来说,这个是该方法在类虚函数表(vtable)中的偏移; 对于类接口(interface)的抽象接口函数来说,这个是该方法在对应的接口表(假设这个方法是定义在类继承的第n+1个接口中,则表示iftable[n]->methodIndexArray)中的序号;如果只是类的普通函数,这个表示的是该函数在类成员函数列表中的序号;

(4)registersSize:当前方法被调用需要用到的总寄存器数量即 insSize(函数传参使用的寄存器数量)和 outsSize(函数中局部变量使用的寄存器数量)之和;

(5)outsSize:当前方法调用时,本地局部变量需要用到的寄存器数量;对于java实现的函数而言,由于Android系统java方法执行的 dalvik虚拟机 是基于寄存器设计的,在java函数调用时,本地局部变量的传递需要用到寄存器;而对于jni实现的native函数而言,native函数的执行不是有dalvik虚拟机来执行的并且native函数调用局部变量的申请和保存是基于堆栈来管理的,因此对于native函数来说,outsSize的值为0;

(6)insSize:当前方法在调用时,传递函数参数需要用的寄存器的数量;

(7)name:当前方法的名称字符串;

(8)prototype:当前方法的协议描述字符串也就当前方法的函数签名字符串(包括方法调用参数类型、顺序还有返回类型的描述);

(9)shorty:当前方法的短协议描述字符串,一个字符代表一种数据类型;

(10)insns:对于java方法来说,insns指向的是该java方法dalvik指令的内存存放地址(即insns指向的是dex文件被加载映射到内存中存放该java方法的dalvik指令的内存地址,dalvik虚拟机在执行代码指令的时,到insns内存地址处来取代码指令);对于普通native方法(开发自定义的jni函数)来说,insns指向的是该native层实现jni函数的调用地址;对于dalvik虚拟机自带的native函数(Internal Native)来说,insns的值为null;

(11)jniArgInfo:缓存了一些预先计算好的信息,从而不需要在方法调用的时再通过方法的参数和返回值进行实时计算,方便jni方法的调用,提高了调用的速度。如果第一位为1(即0x80000000),则dalvik虚拟机在执行时会忽略后面的所有信息,强制在方法调用时进行实时计算。意思就是在函数调用之前,进行一些函数调用需要的函数参数 和返回值的缓存处理,方面后面函数的调用,提高运行的效率;

(12)nativeFunc:对于dalvik虚拟机自带的内部native函数(Internal Native)来说,该变量指向的是该 Internal Native 方法的实际函数调用地址;对于普通native函数(开发者编写的jni函数)来说,该变量指向的是该native层jni函数调用需要的 jni桥接函数DalvikBridgeFunc 或者 DalvikNativeFunc 并且可以通过判断 insns==NULL 与否来判断该native函数是普通jni的native函数还是dalvik虚拟机自带的内部Internal Native 函数;

(13)fastJni:是否采用 fastJni模式 进行jni函数的调用标志,fastJni模式下直接进行jni函数的调用,省去了很多的跳转调用,高效,但是fastJni模式调用是有要求的,比如是静态,而且非synchronized函数;

(14)registerMap:表示当前方法在每一个GC安全点上,有哪些寄存器其存放的数值是指向某个对象的引用,它主要是给dalvik虚拟机做精确垃圾收集使用的,没有被引用对象的内存将被回收。

2. ddi框架中dalvik模式下java Hook需要用到的信息结构体 dalvik_hook_t ,该结构体主要的作用是进行被java Hook的java目标函数的信息保存和一些java Hook参数的设置和缓存预留。

  1. // 被dalvik Hook的目标函数dalvik Hook相关的信息结构体
  2. struct dalvik_hook_t
  3. {
  4. // 被dalvik Hook的目标函数所在类的名称
  5. char clname[256];
  6. // 被dalvik Hook的目标函数所在类的协议名称字符串,例如:"java/lang/StringBuffer"
  7. char clnamep[256];
  8. // 被dalvik Hook的目标函数的名称
  9. char method_name[256];
  10. // 被dalvik Hook的目标函数的签名
  11. char method_sig[256];
  12. // 被dalvik Hook的目标函数的信息结构体Method
  13. Method *method;
  14. // 记录被dalvik Hook的目标函数是否是静态函数
  15. int sm; // static method
  16. // original values, saved before patching
  17. // 保存被dalvik Hook的目标函数原始的寄存器数量
  18. int iss; // 目标函数(java层)传参使用的寄存器数量
  19. int rss; // 目标函数(java层)使用的总的寄存器数量
  20. int oss; // 目标函数(java层)局部变量使用的寄存器数量
  21. // 保存被dalvik Hook的目标函数原始的函数属性值
  22. int access_flags;
  23. // 保存被dalvik Hook的目标函数原始的dalvik代码指令的首地址
  24. void *insns; // dalvik code
  25. // native values
  26. // 记录java层目标函数被dalvik Hook后(改为native函数)的寄存器数量
  27. int n_iss; // == n_rss,改为native函数后函数传参使用的寄存器数量
  28. int n_rss; // num argument (+ 1, if non-static method),改为native函数后使用的总的寄存器数量
  29. int n_oss; // 0,改为native函数后局部变量使用的寄存器的数量,由于native函数使用堆栈传参,因此为0
  30. // java层目标函数被dalvik Hook后(改为native函数)需要的自定义替换函数native_func
  31. void *native_func;
  32. // java层目标函数被dalvik Hook后(改为native函数)的函数属性值0x0100
  33. int af; // access flags modifier
  34. // 是否记录被dalvik Hook的java层目标函数的jclass和jmethodID值
  35. int resolvm;
  36. // for the call
  37. // 记录被dalvik Hook的java层目标函数的jclass值
  38. jclass cls;
  39. // 记录被dalvik Hook的java层目标函数的mid
  40. jmethodID mid;
  41. // debug stuff
  42. int dump; // call dvmDumpClass() while patching(是否打印被dalvik Hook类的日志开关)
  43. int debug_me; // print debug while operating on this method(是否打印调试log日志的开关)
  44. };

3. 上面提到的结构体 Method 和 dalvik_hook_t 在后面的代码分析中还会提到,比较重要,需要好好的理解一下。现在开始详细分析一下 ddi框架 的实现流程,ddi框架 Hook的实现也是基于root权限下Android跨进程so库文件的注入实现的,在目标进程加载注入的so库文件时,调用.init段构造函数实现dalvik虚拟机模式下的java Hook,ddi Hook框架的so库文件注入工具也是使用 hijack,ddi Hook框架是在修改adbi Hook框架源码的基础上实现的。

  1. // 在android系统libc.so库中的epoll_wait函数被inline Hook后的自定义Hook函数my_epoll_wait中,进行针对dalvik的Hook操作
  2. static int my_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
  3. {
  4. // 被inline Hook的epoll_wait函数原型的声明
  5. int (*orig_epoll_wait)(int epfd, struct epoll_event *events, int maxevents, int timeout);
  6. // 获取epoll_wait函数的原始调用地址
  7. orig_epoll_wait = (void*)eph.orig;
  8. // 恢复被Hook的epoll_wait函数的调用
  9. hook_precall(&eph);
  10. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  11. // 下面是新增的针对Android系统的dalvik进行Hook操作的代码
  12. // 动态加载"libdvm.so"库文件并获取该加载的动态库中dalvik Hook实现需要的导出函数的调用地址和导出全局变量
  13. // dalvik Hook实现需要的导出函数的调用地址和导出全局变量的信息保存在结构体变量dexstuff_t d 中
  14. dexstuff_resolv_dvm(&d);
  15. // insert hooks
  16. do_patch();
  17. // call dump class (demo)
  18. dalvik_dump_class(&d, "Ljava/lang/String;");
  19. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  20. // 对原始的epoll_wait函数进行调用
  21. int res = orig_epoll_wait(epfd, events, maxevents, timeout);
  22. return res;
  23. }
  24. // 设置自定义代码被执行的入口点
  25. void __attribute__ ((constructor)) my_init(void);
  26. // so库文件被加载时,首先执行构造函数my_init,在执行so的构造函数时进行java的Hook
  27. void my_init(void)
  28. {
  29. log("libstrmon: started\n")
  30. // set to 1 to turn on, this will be noisy(调试开关)
  31. debug = 1;
  32. // set log function for libbase (very important!)(设置libbase的打印log日志的函数)
  33. set_logfunction(my_log2);
  34. // set log function for libdalvikhook (very important!)(设置libdalvikhook的log日志打印的函数)
  35. dalvikhook_set_logfunction(my_log2);
  36. // adbi中提到的inline Hook实现的Hook函数
  37. hook(&eph, getpid(), "libc.", "epoll_wait", my_epoll_wait, 0);
  38. }

提示:ddi Hook框架的作者偷了个懒直接将dalvik模式下的java Hook的处理代码放在了inline Hook替换epoll_wait函数的Hook自定义函数my_epoll_wait中,其实可以直接将dalvik模式下的java Hook的处理代码放在被注入加载的so库文件.init段的 构造函数my_init中 ,在so库文件被加载注入到目标进程中时调用该构造函数实现dalvik模式下的java Hook目标java函数。

4. ddi Hook框架的主要代码实现流程如下:

  1. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. // 下面是新增的针对Android系统的dalvik进行Hook操作的代码
  3. // 1.动态加载"libdvm.so"库文件并获取该加载的动态库中dalvik Hook实现需要的导出函数的调用地址和导出全局变量
  4. // dalvik Hook实现需要的导出函数的调用地址和导出全局变量的信息保存在结构体变量dexstuff_t d 中
  5. dexstuff_resolv_dvm(&d);
  6. // insert hooks
  7. // 2.对指定的java层目标函数进行dalvik Hook操作
  8. do_patch();
  9. // call dump class (demo)
  10. dalvik_dump_class(&d, "Ljava/lang/String;");
  11. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

5. 在对目标java函数进行java Hook之前,调用dexstuff_resolv_dvm函数,先 dlopen 加载动态库文件libdvm.so,调用 dlsym 函数获取libdvm.so库文件中java Hook需要的导出函数dvm_dalvik_system_DexFile、dvm_java_lang_Class、dvmStringFromCStr、dvmFindLoadedClass、dvmFindVirtualMethodHierByDescriptor、dvmFindDirectMethodByDescriptor、dvmUseJNIBridge、dvmGetCurrentJNIMethod等的调用地址(VA);dvm_dalvik_system_DexFile函数用于dex文件的加载和指定类的加载,dvmStringFromCStr将C语言格式的字符串转换成dalvik虚拟机能使用的字符串,dvmFindLoadedClass进行目标类的查找,dvmFindVirtualMethodHierByDescriptor和dvmFindDirectMethodByDescriptor进行指定类成员函数的查找,dvmUseJNIBridge函数实现修改Method结构体的成员变量native_func为jni调用的桥接跳转函数DalvikBridgeFunc 或者 DalvikNativeFunc以及设置Method结构体的成员变量insns指向替换被java Hook的目标函数的native层自定义实现jni函数的调用地址。

  1. // 获取加载的动态库中导出函数的调用地址
  2. static void* mydlsym(void *hand, const char *name)
  3. {
  4. void* ret = dlsym(hand, name);
  5. log("%s = 0x%x\n", name, ret)
  6. return ret;
  7. }
  8. // 动态加载"libdvm.so"库文件并获取该加载的动态库中dalvik Hook实现需要的导出函数的调用地址和导出全局变量
  9. void dexstuff_resolv_dvm(struct dexstuff_t *d)
  10. {
  11. // 动态加载"libdvm.so"库文件并保存文件句柄
  12. d->dvm_hand = dlopen("libdvm.so", RTLD_NOW);
  13. log("dvm_hand = 0x%x\n", d->dvm_hand)
  14. // 获取加载的文件句柄成功的情况
  15. if (d->dvm_hand) {
  16. // 获取加载的"libdvm.so"库文件中导出函数dvm_dalvik_system_DexFile的调用地址
  17. d->dvm_dalvik_system_DexFile = (DalvikNativeMethod*) mydlsym(d->dvm_hand, "dvm_dalvik_system_DexFile");
  18. // 获取加载的"libdvm.so"库文件中导出函数dvm_java_lang_Class的调用地址
  19. d->dvm_java_lang_Class = (DalvikNativeMethod*) mydlsym(d->dvm_hand, "dvm_java_lang_Class");
  20. // 获取加载的"libdvm.so"库文件中导出函数dvmThreadSelf的调用地址
  21. d->dvmThreadSelf_fnPtr = mydlsym(d->dvm_hand, "_Z13dvmThreadSelfv");
  22. if (!d->dvmThreadSelf_fnPtr)
  23. d->dvmThreadSelf_fnPtr = mydlsym(d->dvm_hand, "dvmThreadSelf");
  24. // 获取加载的"libdvm.so"库文件中导出函数dvmStringFromCStr的调用地址
  25. d->dvmStringFromCStr_fnPtr = mydlsym(d->dvm_hand, "_Z32dvmCreateStringFromCstrAndLengthPKcj");
  26. // 获取加载的"libdvm.so"库文件中导出函数dvmGetSystemClassLoader的调用地址
  27. d->dvmGetSystemClassLoader_fnPtr = mydlsym(d->dvm_hand, "_Z23dvmGetSystemClassLoaderv");
  28. // 获取加载的"libdvm.so"库文件中导出函数dvmIsClassInitialized的调用地址
  29. d->dvmIsClassInitialized_fnPtr = mydlsym(d->dvm_hand, "_Z21dvmIsClassInitializedPK11ClassObject");
  30. // 获取加载的"libdvm.so"库文件中导出函数dvmInitClass的调用地址
  31. d->dvmInitClass_fnPtr = mydlsym(d->dvm_hand, "dvmInitClass");
  32. // 获取加载的"libdvm.so"库文件中导出函数dvmFindVirtualMethodHierByDescriptor的调用地址
  33. d->dvmFindVirtualMethodHierByDescriptor_fnPtr = mydlsym(d->dvm_hand, "_Z36dvmFindVirtualMethodHierByDescriptorPK11ClassObjectPKcS3_");
  34. if (!d->dvmFindVirtualMethodHierByDescriptor_fnPtr)
  35. d->dvmFindVirtualMethodHierByDescriptor_fnPtr = mydlsym(d->dvm_hand, "dvmFindVirtualMethodHierByDescriptor");
  36. // 获取加载的"libdvm.so"库文件中导出函数dvmFindDirectMethodByDescriptor的调用地址
  37. d->dvmFindDirectMethodByDescriptor_fnPtr = mydlsym(d->dvm_hand, "_Z31dvmFindDirectMethodByDescriptorPK11ClassObjectPKcS3_");
  38. if (!d->dvmFindDirectMethodByDescriptor_fnPtr)
  39. d->dvmFindDirectMethodByDescriptor_fnPtr = mydlsym(d->dvm_hand, "dvmFindDirectMethodByDescriptor");
  40. // 获取加载的"libdvm.so"库文件中导出函数dvmIsStaticMethod的调用地址
  41. d->dvmIsStaticMethod_fnPtr = mydlsym(d->dvm_hand, "_Z17dvmIsStaticMethodPK6Method");
  42. // 获取加载的"libdvm.so"库文件中导出函数dvmAllocObject的调用地址
  43. d->dvmAllocObject_fnPtr = mydlsym(d->dvm_hand, "dvmAllocObject");
  44. // 获取加载的"libdvm.so"库文件中导出函数dvmCallMethodV的调用地址
  45. d->dvmCallMethodV_fnPtr = mydlsym(d->dvm_hand, "_Z14dvmCallMethodVP6ThreadPK6MethodP6ObjectbP6JValueSt9__va_list");
  46. // 获取加载的"libdvm.so"库文件中导出函数dvmCallMethodA的调用地址
  47. d->dvmCallMethodA_fnPtr = mydlsym(d->dvm_hand, "_Z14dvmCallMethodAP6ThreadPK6MethodP6ObjectbP6JValuePK6jvalue");
  48. // 获取加载的"libdvm.so"库文件中导出函数dvmAddToReferenceTable的调用地址
  49. d->dvmAddToReferenceTable_fnPtr = mydlsym(d->dvm_hand, "_Z22dvmAddToReferenceTableP14ReferenceTableP6Object");
  50. // 获取加载的"libdvm.so"库文件中导出函数dvmSetNativeFunc的调用地址
  51. d->dvmSetNativeFunc_fnPtr = mydlsym(d->dvm_hand, "_Z16dvmSetNativeFuncP6MethodPFvPKjP6JValuePKS_P6ThreadEPKt");
  52. // 获取加载的"libdvm.so"库文件中导出函数dvmUseJNIBridge的调用地址
  53. d->dvmUseJNIBridge_fnPtr = mydlsym(d->dvm_hand, "_Z15dvmUseJNIBridgeP6MethodPv");
  54. if (!d->dvmUseJNIBridge_fnPtr)
  55. d->dvmUseJNIBridge_fnPtr = mydlsym(d->dvm_hand, "dvmUseJNIBridge");
  56. // 获取加载的"libdvm.so"库文件中导出函数dvmDecodeIndirectRef的调用地址
  57. d->dvmDecodeIndirectRef_fnPtr = mydlsym(d->dvm_hand, "_Z20dvmDecodeIndirectRefP6ThreadP8_jobject");
  58. // 获取加载的"libdvm.so"库文件中导出函数dvmLinearSetReadWrite的调用地址
  59. d->dvmLinearSetReadWrite_fnPtr = mydlsym(d->dvm_hand, "_Z21dvmLinearSetReadWriteP6ObjectPv");
  60. // 获取加载的"libdvm.so"库文件中导出函数dvmGetCurrentJNIMethod的调用地址
  61. d->dvmGetCurrentJNIMethod_fnPtr = mydlsym(d->dvm_hand, "_Z22dvmGetCurrentJNIMethodv");
  62. // 获取加载的"libdvm.so"库文件中导出函数dvmFindInstanceField的调用地址
  63. d->dvmFindInstanceField_fnPtr = mydlsym(d->dvm_hand, "_Z20dvmFindInstanceFieldPK11ClassObjectPKcS3_");
  64. //d->dvmCallJNIMethod_fnPtr = mydlsym(d->dvm_hand, "_Z21dvmCheckCallJNIMethodPKjP6JValuePK6MethodP6Thread");
  65. // 获取加载的"libdvm.so"库文件中导出函数dvmCallJNIMethod的调用地址
  66. d->dvmCallJNIMethod_fnPtr = mydlsym(d->dvm_hand, "_Z16dvmCallJNIMethodPKjP6JValuePK6MethodP6Thread");
  67. // 获取加载的"libdvm.so"库文件中导出函数dvmDumpAllClasses的调用地址
  68. d->dvmDumpAllClasses_fnPtr = mydlsym(d->dvm_hand, "_Z17dvmDumpAllClassesi");
  69. // 获取加载的"libdvm.so"库文件中导出函数dvmDumpClass的调用地址
  70. d->dvmDumpClass_fnPtr = mydlsym(d->dvm_hand, "_Z12dvmDumpClassPK11ClassObjecti");
  71. // 获取加载的"libdvm.so"库文件中导出函数dvmFindLoadedClass的调用地址
  72. d->dvmFindLoadedClass_fnPtr = mydlsym(d->dvm_hand, "_Z18dvmFindLoadedClassPKc");
  73. if (!d->dvmFindLoadedClass_fnPtr)
  74. d->dvmFindLoadedClass_fnPtr = mydlsym(d->dvm_hand, "dvmFindLoadedClass");
  75. // 获取加载的"libdvm.so"库文件中导出函数dvmHashTableLock的调用地址
  76. d->dvmHashTableLock_fnPtr = mydlsym(d->dvm_hand, "_Z16dvmHashTableLockP9HashTable");
  77. // 获取加载的"libdvm.so"库文件中导出函数dvmHashTableUnlock的调用地址
  78. d->dvmHashTableUnlock_fnPtr = mydlsym(d->dvm_hand, "_Z18dvmHashTableUnlockP9HashTable");
  79. // 获取加载的"libdvm.so"库文件中导出函数dvmHashForeach的调用地址
  80. d->dvmHashForeach_fnPtr = mydlsym(d->dvm_hand, "_Z14dvmHashForeachP9HashTablePFiPvS1_ES1_");
  81. // 获取加载的"libdvm.so"库文件中导出函数dvmInstanceof的调用地址
  82. d->dvmInstanceof_fnPtr = mydlsym(d->dvm_hand, "_Z13dvmInstanceofPK11ClassObjectS1_");
  83. // 获取加载的"libdvm.so"库文件中导出全部变量gDvm的调用地址
  84. d->gDvm = mydlsym(d->dvm_hand, "gDvm");
  85. }
  86. }

6. 调用 do_patch函数 实现对指定的java目标函数进行java Hook处理,do_patch函数调用主要有 dalvik_hook_setup函数 和 dalvik_hook函数组成,dalvik_hook_setup函数负责dalvik Hook的预处理,dalvik_hook函数实现对java目标函数的dalvik Hook操作。

  1. // 对指定的java层目标函数进行dalvik Hook操作
  2. void do_patch()
  3. {
  4. log("do_patch()\n")
  5. // 对StringBuffer.toString()进行dalvik Hook操作
  6. dalvik_hook_setup(&sb1, "Ljava/lang/StringBuffer;", "toString", "()Ljava/lang/String;", 1, sb1_tostring);
  7. dalvik_hook(&d, &sb1);
  8. dalvik_hook_setup(&sb20, "Ljava/lang/StringBuilder;", "toString", "()Ljava/lang/String;", 1, sb20_tostring);
  9. dalvik_hook(&d, &sb20);
  10. dalvik_hook_setup(&sb2, "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I", 2, sb2_compareto);
  11. dalvik_hook(&d, &sb2);
  12. dalvik_hook_setup(&sb3, "Ljava/lang/String;", "compareToIgnoreCase", "(Ljava/lang/String;)I", 2, sb3_comparetocase);
  13. dalvik_hook(&d, &sb3);
  14. dalvik_hook_setup(&sb13, "Ljava/lang/String;", "equalsIgnoreCase", "(Ljava/lang/String;)Z", 2, sb13_equalsIgnoreCase);
  15. dalvik_hook(&d, &sb13);
  16. dalvik_hook_setup(&sb6, "Ljava/lang/String;", "contains", "(Ljava/lang/CharSequence;)Z", 2, sb6_contains);
  17. dalvik_hook(&d, &sb6);
  18. dalvik_hook_setup(&sb14, "Ljava/lang/String;", "contentEquals", "(Ljava/lang/StringBuffer;)Z", 2, sb14_contentEquals);
  19. dalvik_hook(&d, &sb14);
  20. dalvik_hook_setup(&sb7, "Ljava/lang/String;", "indexOf", "(Ljava/lang/String;I)I", 3, sb7_indexof);
  21. dalvik_hook(&d, &sb7);
  22. dalvik_hook_setup(&sb11, "Ljava/lang/StringBuffer;", "indexOf", "(Ljava/lang/String;I)I", 3, sb11_indexof);
  23. dalvik_hook(&d, &sb11);
  24. dalvik_hook_setup(&sb9, "Ljava/lang/String;", "endsWith", "(Ljava/lang/String;)Z", 2, sb9_endswith);
  25. dalvik_hook(&d, &sb9);
  26. dalvik_hook_setup(&sb10, "Ljava/lang/String;", "startsWith", "(Ljava/lang/String;I)Z", 3, sb10_startswith);
  27. dalvik_hook(&d, &sb10);
  28. dalvik_hook_setup(&sb8, "Ljava/lang/String;", "matches", "(Ljava/lang/String;)Z", 2, sb8_matches);
  29. dalvik_hook(&d, &sb8);
  30. dalvik_hook_setup(&sb5, "Ljava/lang/Class;", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", 3, sb5_getmethod);
  31. dalvik_hook(&d, &sb5);
  32. }

7. 调用dalvik_hook_setup函数保存被java Hook的目标函数所在类的名称和协议字符串,被java Hook的目标函数的名称和函数签名,预先计算、保存java目标函数被修改为native函数之后寄存器的数量值、替换java目标函数的自定义native函数的调用地址以及一些java Hook中标识的初始化设置。

  1. // 源码文件 dalvik_hook.c
  2. /*
  3. * dalvik_hook_t用于记录被dalvik Hook的java类成员方法的有关信息
  4. * cls为被dalvik Hook的java类成员方法所在的类的签名
  5. * meth为被dalvik Hook的java类成员方法的名称
  6. * sig为被dalvik Hook的java类成员方法的函数签名
  7. * ns为java成员方法被修改为native层实现的方法后调用,传参需要的寄存器的个数
  8. * func为被dalvik Hook的java类成员方法的替换自定义Hook函数
  9. * 调用实例:dalvik_hook_setup(&sb1, "Ljava/lang/StringBuffer;", "toString", "()Ljava/lang/String;", 1, sb1_tostring);
  10. *
  11. * 注意:
  12. * 1.Android的dalvik虚拟机是基于寄存器的,因此java语言实现的函数在执行的时候需要预先计算出函数调用时传递参数需要的寄存器数量insSize以及函数内部
  13. * 局部变量要用到的寄存器的数量outsSize,需要的寄存器总数量为registersSize=outsSize+insSize。
  14. * 2.java的类成员方法中非静态的成员方法的第一个函数参数是指向本身的对象指针然后是其他的传入参数,静态成员方法不存在这样的问题。
  15. * 3.当java层的类成员方法被修改为native属性的成员方法后,由于native属性方法的局部变量的内存申请是通过堆栈来完成的,因此在计算该方法的寄存器数量时,
  16. * 局部变量用到的寄存器的数量outsSize为0,并且总的寄存器数量与传参寄存器的数量相同即registersSize=insSize。
  17. *
  18. */
  19. // 记录java层目标函数被dalvik Hook操作相关需要的结构体信息
  20. int dalvik_hook_setup(struct dalvik_hook_t *h, char *cls, char *meth, char *sig, int ns, void *func)
  21. {
  22. if (!h)
  23. return 0;
  24. // 保存被dalvik Hook的类成员方法所在的类的名称
  25. strcpy(h->clname, cls);
  26. // 保存被dalvik Hook的类成员方法所在的类的协议名称,如:"java/lang/StringBuffer"
  27. strncpy(h->clnamep, cls+1, strlen(cls)-2);
  28. // 保存被dalvik Hook的类成员方法的名称
  29. strcpy(h->method_name, meth);
  30. // 保存被dalvik Hook的类成员方法的函数签名
  31. strcpy(h->method_sig, sig);
  32. // 提示:下面的部分变量值主要用于该java层的类成员方法meth被修改为native层后寄存器记录变量值的修改
  33. // dalvik Hook的实现其实就是将java层实现的类成员函数修改为native层实现的自定义Hook函数
  34. // 保存被dalvik Hook后java层类成员方法的寄存器数量
  35. // 被dalvik Hook后该native层的函数传参需要的寄存器数量
  36. h->n_iss = ns;
  37. // 被dalvik Hook后该native层的函数需要的总的寄存器的数量
  38. h->n_rss = ns;
  39. // 被dalvik Hook后该native层的函数局部变量需要的寄存器的数量
  40. h->n_oss = 0;
  41. // 被dalvik Hook后该native层的函数被替换的自定义Hook函数
  42. h->native_func = func;
  43. // 记录被dalvik Hook的函数是否是静态成员方法的标志,默认为非静态成员方法即0
  44. h->sm = 0; // set by hand if needed
  45. // java类成员函数的类型属性标志accessFlags,其中accessFlags=0x0100表示该函数为native层实现的函数
  46. h->af = 0x0100; // native, modify by hand if needed
  47. // 记录是否需要保存查找到,将被dalvik Hook的目标函数所在类的指针和方法结构体指针的标志,默认为0-需要
  48. h->resolvm = 0; // don't resolve method on-the-fly, change by hand if needed
  49. // debug调试打印Log日志的开关,默认为0-不打印日志
  50. h->debug_me = 0;
  51. return 1;
  52. }

8. 调用dalvik_hook函数实现对java目标函数的dalvik Hook,先调用libdvm.so库文件的 导出函数dvmFindLoadedClass 查找到被java Hook的java目标函数所在的目标类,再调用libdvm.so库文件的 导出函数dvmFindVirtualMethodHierByDescriptor 或者 dvmFindDirectMethodByDescriptor 查找到被java Hook的目标函数的信息结构体 Method,在修改该目标函数的信息结构体 Method之前先 保存备份 该被java Hook目标函数的所在类、原始方法信息结构体Method等信息,然后修改该被java Hook的目标函数的信息结构体 Method 的成员变量 access_flags 的值将一个java函数改为native属性jni实现的函数,修正目标函数被改后函数调用需要的寄存器数量(registersSize == insSize,outsSize=0),修正目标函数被改后函数调用需要的jniArgInfo的值为0x80000000,修正目标函数被改后函数调用 insns 和 native_func 的值,这两个变量是通过调用libdvm.so库文件中的 导出函数dvmUseJNIBridge 来修改的,作用就是将 nativeFunc域 改成指向一个JNI桥跳转函数(dvmCallJNIMethod )并将 insns域 改成替换被java Hook目标函数的自定义native属性jni函数的调用地址,dvmUseJNIBridge函数这么调用相当于 对被修改后的目标函数(native属性)进行了 RegisterNatives 操作。

  1. // 修改java层目标函数为native属性的native实现的jni函数并修正该函数正确调用相关的Method结构体的信息
  2. void* dalvik_hook(struct dexstuff_t *dex, struct dalvik_hook_t *h)
  3. {
  4. if (h->debug_me)
  5. log("dalvik_hook: class %s\n", h->clname)
  6. // 调用dalvik虚拟机的函数dvmFindLoadedClass获取被dalvik hook的目标函数所在类的指针
  7. void *target_cls = dex->dvmFindLoadedClass_fnPtr(h->clname);
  8. if (h->debug_me)
  9. log("class = 0x%x\n", target_cls)
  10. // print class in logcat
  11. if (h->dump && dex && target_cls){
  12. // 打印找到被dalvik hook的目标函数所在类的名称信息
  13. dex->dvmDumpClass_fnPtr(target_cls, (void*)1);
  14. }
  15. // 判断被dalvik hook的目标函数所在类的指针是否为null并打印日志
  16. if (!target_cls) {
  17. if (h->debug_me)
  18. log("target_cls == 0\n")
  19. return (void*)0;
  20. }
  21. // 在被dalvik hook的目标函数所在类的 非静态成员函数 中查找将被dalvik hook的java层目标函数
  22. h->method = dex->dvmFindVirtualMethodHierByDescriptor_fnPtr(target_cls, h->method_name, h->method_sig);
  23. // 如果查找失败
  24. if (h->method == 0) {
  25. // 在被dalvik hook的目标函数所在类的 静态成员函数 中查找将被dalvik hook的java层目标函数
  26. h->method = dex->dvmFindDirectMethodByDescriptor_fnPtr(target_cls, h->method_name, h->method_sig);
  27. }
  28. // constrcutor workaround, see "dalvik_prepare" below
  29. // 保存查找到的,将被dalvik hook的java层目标函数所在类指针和方法结构体指针的信息,用以dalvik Hook后的还原
  30. if (!h->resolvm) {
  31. // 保存查找到将被dalvik hook的java层目标函数所在类的指针
  32. h->cls = target_cls;
  33. // 保存查找到将被dalvik hook的java层目标函数的方法体结构指针
  34. h->mid = (void*)h->method;
  35. }
  36. // 打印查找到被dalvik hook的java层目标函数的信息
  37. if (h->debug_me)
  38. log("%s(%s) = 0x%x\n", h->method_name, h->method_sig, h->method)
  39. // 进行目标函数被dalvik Hook操作的修改
  40. if (h->method) {
  41. /*
  42. * dalvik虚拟机中方法结构体Method的提示:
  43. * -----------------------------------------------------------------------------------------------------------
  44. * 1.method->insns:
  45. * 如果这个方法不是Native层实现即java层实现的函数,则这里存放了指向该方法具体的Dalvik指令的指针
  46. * (这个变量指向的是实际加载到内存中的Dalvik指令代码,而不是在Dex文件中的);
  47. * 如果这个方法是一个Dalvik虚拟机自带的Native函数(即Internal Native函数),则这个变量会是Null。
  48. * 如果这个方法是一个普通的Native函数即jni实现的自定义函数,则这里存放了指向jni实际函数机器码的首地址;
  49. * -----------------------------------------------------------------------------------------------------------
  50. * 2.method->jniArgInfo:
  51. * 这个变量记录了一些预先计算好的函数参数信息,从而不需要在函数调用的时候再通过方法的参数和返回值实时计算了,
  52. * 方便了JNI的调用,提高了调用的速度。如果第一位为1(即0x80000000),则Dalvik虚拟机会忽略后面的所有信息,强制在调用时实时计算;
  53. * -----------------------------------------------------------------------------------------------------------
  54. * 3.method->nativeFunc:
  55. * 如果这个方法是一个Dalvik虚拟机自带的Native函数(Internal Native)的话,则这里存放了指向JNI实际函数机器码的首地址。
  56. * 如果这个方法是一个普通的Native函数,则这里将指向一个中间的跳转JNI桥(Bridge)代码;
  57. * -----------------------------------------------------------------------------------------------------------
  58. * 4.通过method->accessFlags可以判断一个方法是不是Native的(和0x00100相与),如果是Native方法的话,就直接执行nativeFunc所指向的本地代码,
  59. * 如果不是Native方法的话,就执行insns所指向的Dalvik代码。
  60. */
  61. // 保存被dalvik Hook的java层目标函数的dalvik字节码指针method->insns
  62. h->insns = h->method->insns;
  63. if (h->debug_me) {
  64. // 打印被dalvik Hook操作的java层目标函数的原始信息
  65. log("nativeFunc %x\n", h->method->nativeFunc)
  66. log("insSize = 0x%x registersSize = 0x%x outsSize = 0x%x\n", h->method->insSize, h->method->registersSize, h->method->outsSize)
  67. }
  68. // 保存被dalvik Hook的java层目标函数的寄存器数量信息,用以后面dalvik Hook的恢复还原
  69. // 被dalvik Hook的java层目标函数的传参寄存器的个数
  70. h->iss = h->method->insSize;
  71. // 保存被dalvik Hook的java层目标函数的局部变量使用的寄存器个数
  72. h->oss = h->method->outsSize;
  73. // 保存被dalvik Hook的java层目标函数的总寄存器个数
  74. h->rss = h->method->registersSize;
  75. // 修改被dalvik Hook的java层目标函数的寄存器个数为该目标函数为native属性时的正确个数
  76. h->method->insSize = h->n_iss;
  77. h->method->registersSize = h->n_rss;
  78. h->method->outsSize = h->n_oss;
  79. if (h->debug_me) {
  80. log("shorty %s\n", h->method->shorty)
  81. log("name %s\n", h->method->name)
  82. log("arginfo %x\n", h->method->jniArgInfo)
  83. }
  84. // 修改被dalvik Hook的目标函数的jni参数为运行时实时计算
  85. // 原本函数可能并不是Native的,现在被偷偷改成了Native的,所以肯定不能使用这个域进行优化
  86. h->method->jniArgInfo = 0x80000000; // <--- also important
  87. if (h->debug_me) {
  88. log("noref %c\n", h->method->noRef)
  89. log("access %x\n", h->method->a)
  90. }
  91. // 保存被dalvik Hook的java层目标函数的原始函数属性标志值
  92. h->access_flags = h->method->a;
  93. // 修改被dalvik Hook的java层目标函数的为native层实现的jni函数
  94. h->method->a = h->method->a | h->af; // make method native
  95. if (h->debug_me)
  96. log("access %x\n", h->method->a)
  97. // 调用libdvm.so中的dvmUseJNIBridge函数来实现将method->nativeFunc域改成指向一个JNI桥跳转函数地址(dvmCallJNIMethod)
  98. // 并将method->insns域改成指向真正的jni函数代码即我们自定义实现的dalvik Hook函数代码首地址处
  99. dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);
  100. if (h->debug_me)
  101. log("patched %s to: 0x%x\n", h->method_name, h->native_func)
  102. // 到这里,java层目标函数的dalvik Hook实现完成
  103. return (void*)1;
  104. } else {
  105. // 查找被dalvik hook的java层目标函数失败的情况
  106. if (h->debug_me)
  107. log("could NOT patch %s\n", h->method_name)
  108. }
  109. return (void*)0;
  110. }

在上面的分析中基本将ddi Hook框架下dalvik模式的java Hook的关键操作和修改说的很清楚了,但是有些细节的地方没有细说,具体的再回头看看前面提到的 Method结构体 的组成,到这一步基于dalvik虚拟机的java Hook就实现了。

9. dalvik虚拟机模式下,目标java函数被java Hook的步骤已经完成,下面来分析下 替换被java Hook目标函数 的native方法(自定义实现的jni函数)的实现。以被java Hook的 目标函数StringBuffer.toString() 为例子,替换java目标函数toString的native函数为 jni函数sb1_tostring 如下所示,为了调用被java Hook的目标函数toString的原始方法,先调用 dalvik_prepare 函数 取消对目标函数toString的dalvik Hook,然后通过反射调用原始的java层目标函数toString,然后再次调用 dalvik_postcall函数 对java层实现的目标函数进行再次 dalvik Hook 操作。

  1. // patches
  2. static void* sb1_tostring(JNIEnv *env, jobject obj)
  3. {
  4. // 恢复还原被dalvik Hook的java层目标函数
  5. dalvik_prepare(&d, &sb1, env);
  6. // 调用被java Hook目标函数的原始函数
  7. void *res = (*env)->CallObjectMethod(env, obj, sb1.mid);
  8. // 再次对java层实现的目标函数进行dalvik Hook操作
  9. dalvik_postcall(&d, &sb1);
  10. // 进行字符串的转换
  11. const char *s = (*env)->GetStringUTFChars(env, res, 0);
  12. if (s) {
  13. log("sb1.toString() = %s\n", s)
  14. (*env)->ReleaseStringUTFChars(env, res, s);
  15. }
  16. return res;
  17. }

10. dalvik_prepare 函数的实现如下,dalvik_prepare 函数的作用是 恢复还原被dalvik Hook的java层目标函数的信息结构体Method的成员变量 access_flags、insSize、registersSize、outsSize、jniArgInfo、insns 的原始值,从而实现被java Hook的目标函数dalvik Hook的取消和原始函数调用的恢复还原。

  1. // 恢复还原被dalvik Hook的java层目标函数
  2. int dalvik_prepare(struct dexstuff_t *dex, struct dalvik_hook_t *h, JNIEnv *env)
  3. {
  4. // this seems to crash when hooking "constructors"
  5. // 判断需要被恢复还原的dalvik Hook目标函数是否存在
  6. if (h->resolvm) {
  7. // 查找指定的目标类
  8. h->cls = (*env)->FindClass(env, h->clnamep);
  9. if (h->debug_me)
  10. log("cls = 0x%x\n", h->cls)
  11. if (!h->cls)
  12. return 0;
  13. // 查找指定的目标函数
  14. if (h->sm)
  15. h->mid = (*env)->GetStaticMethodID(env, h->cls, h->method_name, h->method_sig);
  16. else
  17. h->mid = (*env)->GetMethodID(env, h->cls, h->method_name, h->method_sig);
  18. if (h->debug_me)
  19. log("mid = 0x%x\n", h-> mid)
  20. if (!h->mid)
  21. return 0;
  22. }
  23. // 恢复被dalvik Hook的java层目标函数的原始寄存器数量
  24. h->method->insSize = h->iss;
  25. h->method->registersSize = h->rss;
  26. h->method->outsSize = h->oss;
  27. // 恢复被dalvik Hook的java层目标函数的原始函数类型属性
  28. h->method->a = h->access_flags;
  29. h->method->jniArgInfo = 0;
  30. // 恢复被dalvik Hook的java层目标函数的dalvik字节码指针
  31. h->method->insns = h->insns;
  32. return 1;
  33. }

11. dalvik_postcall函数的实现如下,dalvik_postcall函数的作用是通过再一次修改java目标函数的信息结构体Method的成员变量 access_flags、insSize、registersSize、outsSize、jniArgInfo、insns 、native_func 的值将一个java函数修改为native属性的自定义jni函数,从而实现对java目标函数的再次 dalvik Hook操作。

  1. // 再次对java层实现的目标函数进行dalvik Hook操作
  2. void dalvik_postcall(struct dexstuff_t *dex, struct dalvik_hook_t *h)
  3. {
  4. // 修改被dalvik Hook的java层目标函数的寄存器数量值
  5. h->method->insSize = h->n_iss;
  6. h->method->registersSize = h->n_rss;
  7. h->method->outsSize = h->n_oss;
  8. //log("shorty %s\n", h->method->shorty)
  9. //log("name %s\n", h->method->name)
  10. //log("arginfo %x\n", h->method->jniArgInfo)
  11. // 修改被dalvik Hook的java层目标函数的参数的计算方法为运行时实时计算
  12. h->method->jniArgInfo = 0x80000000;
  13. //log("noref %c\n", h->method->noRef)
  14. //log("access %x\n", h->method->a)
  15. // 修改被dalvik Hook的java层目标函数的类型属性为native
  16. h->access_flags = h->method->a;
  17. h->method->a = h->method->a | h->af;
  18. //log("access %x\n", h->method->a)
  19. // 修改被dalvik Hook的目标函数的insns为我们自定义的dalvik Hook函数的代码首地址
  20. // 修改nativeFunc为函数正确调用需要的jni跳转桥代码的地址
  21. dex->dvmUseJNIBridge_fnPtr(h->method, h->native_func);
  22. if (h->debug_me)
  23. log("patched BACK %s to: 0x%x\n", h->method_name, h->native_func)
  24. }

四、dalvik模式下java Hook框架ddi的编译和运行

java Hook框架ddi的源码下载地址:https://github.com/crmulliner/ddi

1. java Hook框架ddi源码的结构如下图所示,ddi框架的源码中提供了 2个dalvik虚拟机模式下 java Hook的例子,具体的实现都差不多,只是在java Hook替换函数(native 属性自定义的jni函数)的实现上稍有不同。

2. ddi框架的源码编译步骤如下:

  1. #!/bin/sh
  2. cd hijack/jni
  3. ndk-build
  4. cd ../..
  5. cd dalvikhook/jni
  6. ndk-build
  7. cd ../..
  8. cd examples
  9. cd strmon/jni
  10. ndk-build
  11. cd ../..

3. ddi框架源码编译后的运行步骤如下:

  1. # 拷贝需要的动态加载库文件到Android系统的/system/lib路径下
  2. cd dalvikhook
  3. cd jni
  4. cd libs
  5. adb pull /system/lib/libdl.so
  6. adb pull /system/lib/libdvm.so
  7. adb shell chmod 0777 /system/lib/libdl.so
  8. adb shell chmod 0777 /system/lib/libdvm.so
  9. #拷贝进程注入工具hijack到Android系统的/data/local/tmp路径下
  10. adb push adbi/hijack/libs/armeabi/hijack /data/local/tmp/
  11. adb shell chmod 0777 /data/local/tmp/hijack
  12. # 拷贝java Hook动态库文件libstrmon.so到Android系统的/data/local/tmp路径下
  13. adb push examples/strmon/libs/armeabi/libstrmon.so /data/local/tmp
  14. adb shell chmod 0777 /data/local/tmp/libstrmon.so
  15. # 进行dalvik模式下的java Hook操作(Run strmon)
  16. adb shell
  17. su
  18. cd /data/local/tmp
  19. # GET PID from com.android.contacts
  20. ps | grep com.android.contacts
  21. # 设置log日志的打印
  22. >/data/local/tmp/strmon.log
  23. chmod 777 /data/local/tmp/strmon.log
  24. # 对进程com.android.contacts进行java Hook操作
  25. ./hijack -d -p PID -l /data/local/tmp/libstrmon.so
  26. # 获取打印的log
  27. cat strmon.log

4.ddi框架源码的编译和运行也可以参考官方的文档说明:https://github.com/crmulliner/ddi/blob/master/README.md

参考链接:

深入理解Android之Java虚拟机Dalvik

Dalvik虚拟机JNI方法的注册过程分析

Android平台下Dalvik层hook框架ddi的研究

Android平台dalvik模式下java Hook框架ddi的分析(1)的更多相关文章

  1. Android平台dalvik模式下java Hook框架ddi的分析(2)--dex文件的注入和调用

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/77942585 前面的博客<Android平台dalvik模式下java Ho ...

  2. 基于dalvik模式下的Xposed Hook开发的某加固脱壳工具

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/77966109 这段时间好好的学习了一下Android加固相关的知识和流程也大致把A ...

  3. Dalvik模式下基于Android运行时类加载的函数dexFindClass脱壳

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78003184 前段时间在看雪论坛发现了<发现一个安卓万能脱壳方法>这篇 ...

  4. Dalvik模式下在Android so库文件.init段、.init_array段构造函数上下断点

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78244766 在前面的博客<在Android so文件的.init..ini ...

  5. Android平台免Root无侵入AOP框架Dexposed使用详解

    Dexposed是基于久负盛名的开源Xposed框架实现的一个Android平台上功能强大的无侵入式运行时AOP框架. Dexposed的AOP实现是完全非侵入式的,没有使用任何注解处理器,编织器或者 ...

  6. Android Hook框架adbi的分析(2)--- inline Hook的实现

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/74452308 一. Android Hook框架adbi源码中inline Hoo ...

  7. Dalvik模式下System.loadLibrary函数的执行流程分析

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78212010 Android逆向分析的过程中免不了碰到Android so被加固的 ...

  8. java fork-join框架应用和分析

    http://shmilyaw-hotmail-com.iteye.com/blog/1897636 java fork-join框架应用和分析 博客分类: concurrency multithre ...

  9. DEBUG模式下, 内存中的变量地址分析

    测试函数的模板实现 /// @file my_template.h /// @brief 测试数据类型用的模板实现 #ifndef MY_TEMPLATE_H_2016_0123_1226 #defi ...

随机推荐

  1. Java实现文件的读写

    需求:实现基本的读写 package com.sbx.io; import java.io.File; import java.io.FileReader; import java.io.FileWr ...

  2. 订单和产品的多对多表关系在crudapi系统零代码实现

    表关系管理 在上一篇序列号管理中,产品和销售订单都是孤立的单表,本文通过crudapi中表关系(relation)管理将多个表连接起来,形成一个整体. 概要 关系类型 表与表之间的关系(relatio ...

  3. Prometheus + Spring Boot 应用监控

    1.  Prometheus是什么 Prometheus是一个具有活跃生态系统的开源系统监控和告警工具包.一言以蔽之,它是一套开源监控解决方案. Prometheus主要特性: 多维数据模型,其中包含 ...

  4. 使用wireshark 抓取 http https tcp ip 协议进行学习

    使用wireshark 抓取 http https tcp ip 协议进行学习 前言 本节使用wireshark工具抓包学习tcp ip http 协议 1. tcp 1.1 tcp三次握手在wire ...

  5. 我与FreeBSD 的故事之二

    那些人的丑恶嘴脸使我发笑,我愈发远离所谓的社区与论坛.电视剧<武林外传>说的好:有人的地方就有江湖,江湖从未走远,从未改变.社区中的冲突很少是技术层面的,按照老话说睿智的人很少发表自己的见 ...

  6. @WebFilter("")配置servlet访问出现404的原因

    配置 servlet 一共有两种方式 直接在web.xml中配置name 和 url-parttern 使用注解配置servlet 使用注解的方式配置servlet是在servlet3.0之后新增的特 ...

  7. pytest+jenkins+allure 生成测试报告发送邮件

    前言第一部分:Pycharm for Gitee1. pycharm安装gitee插件2. gitee关联本地Git快速设置- 如果你知道该怎么操作,直接使用下面的地址简易的命令行入门教程:3. Gi ...

  8. .net 程序员的centos命令总结

    1,ssh相关 在初始化一台云服务器的时候,第一件事情就是去把该关的门都关上,首先第一关就是禁用root登录,禁用密码登录,顺便改一下远程登录端口,让登录都通过ssh密钥对来进行,阿里云里有密钥对管理 ...

  9. js 获取树结构的节点深度

    需求:获取树结构的节点深度. 实现util.js: // 获取节点深度 参数为树结构array function getMaxFloor(treeData){ let deep = 0; functi ...

  10. python inspect库

    一.介绍 inspect模块用于收集python对象的信息,可以获取类或函数的参数的信息,源码,解析堆栈,对对象进行类型检查等等. inspect模块主要提供了四种用处: 对是否是模块.框架.函数进行 ...