前段时间发现了一个Legend的hook框架,实际使用之后发现并不支持4.x系统,会崩溃.

这个问题需要记录一下,我用hook的方法替代原来的方法后调用hook类的getMethods之类的方法会引起崩溃,崩溃日志如下.

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
04-26 15:01:55.258 10961-10961/? E/dalvikvm: Unable to find method Lcom/legend/demo/App;.onCreate (Landroid/app/Application;)V in DEX file!
04-26 15:01:55.258 10961-10961/? E/dalvikvm: VM aborting
04-26 15:01:55.258 10961-10961/? A/libc: Fatal signal 6 (SIGABRT) at 0x00002ad1 (code=-6), thread 10961 (com.legend.demo)
04-26 15:01:55.308 368-368/? I/DEBUG: Build fingerprint: 'htc/htccn_chs_2/htc_a51dtul:4.4.4/KTU84P/484493.10:user/release-keys'
04-26 15:01:55.308 368-368/? I/DEBUG: Revision: '0'
04-26 15:01:55.308 368-368/? I/DEBUG: pid: 10961, tid: 10961, name: com.legend.demo >>> com.legend.demo <<<
04-26 15:01:55.308 368-368/? I/DEBUG: debuggerd: checkTellHTCSettings
04-26 15:01:55.308 368-368/? I/DEBUG: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
04-26 15:01:55.308 1102-1213/? V/HtcNativeCrashUtil: Read id1=-1243796867 id2=-217320719, they are start flags. This is HTC header
04-26 15:01:55.388 368-368/? I/DEBUG: r0 00000000 r1 00002ad1 r2 00000006 r3 00000000
04-26 15:01:55.388 368-368/? I/DEBUG: r4 00000006 r5 00000016 r6 00002ad1 r7 0000010c
04-26 15:01:55.388 368-368/? I/DEBUG: r8 00000013 r9 5c89ca10 sl 57662f54 fp 00000000
04-26 15:01:55.388 368-368/? I/DEBUG: ip 41a882c8 sp be950098 lr 4004cd25 pc 4005bd28 cpsr 000f0010
04-26 15:01:55.388 368-368/? I/DEBUG: d0 74726f6261204d56 d1 6f6874656d20646e
04-26 15:01:55.388 368-368/? I/DEBUG: d2 0000008400000075 d3 5c894de80000006c
04-26 15:01:55.388 368-368/? I/DEBUG: d4 0063006900660069 d5 006f006900740061
04-26 15:01:55.388 368-368/? I/DEBUG: d6 006e0061004d006e d7 0072006500670061
04-26 15:01:55.388 368-368/? I/DEBUG: d8 0000000000000000 d9 0000000000000000
04-26 15:01:55.388 368-368/? I/DEBUG: d10 0000000000000000 d11 0000000000000000
04-26 15:01:55.388 368-368/? I/DEBUG: d12 0000000000000000 d13 0000000000000000
04-26 15:01:55.388 368-368/? I/DEBUG: d14 0000000000000000 d15 0000000000000000
04-26 15:01:55.388 368-368/? I/DEBUG: d16 010000000cffffff d17 08ffffffff000000
04-26 15:01:55.388 368-368/? I/DEBUG: d18 00630069006e0055 d19 004c00650064006f
04-26 15:01:55.388 368-368/? I/DEBUG: d20 0000000100000001 d21 3ff0000000000000
04-26 15:01:55.388 368-368/? I/DEBUG: d22 3efa019fabb79d95 d23 3f2a019f8723aeaa
04-26 15:01:55.388 368-368/? I/DEBUG: d24 3f56c16c16c76a94 d25 3f81111111185da8
04-26 15:01:55.388 368-368/? I/DEBUG: d26 3fa555555555551c d27 3fc55555555554db
04-26 15:01:55.388 368-368/? I/DEBUG: d28 3fe0000000000000 d29 0000000000000001
04-26 15:01:55.388 368-368/? I/DEBUG: d30 fff0000000000000 d31 4000000000000000
04-26 15:01:55.388 368-368/? I/DEBUG: scr 68000010
04-26 15:01:55.388 368-368/? I/DEBUG: backtrace:
04-26 15:01:55.388 368-368/? I/DEBUG: #00 pc 00022d28 /system/lib/libc.so (tgkill+12)
04-26 15:01:55.388 368-368/? I/DEBUG: #01 pc 00013d21 /system/lib/libc.so (pthread_kill+48)
04-26 15:01:55.388 368-368/? I/DEBUG: #02 pc 00013f35 /system/lib/libc.so (raise+10)
04-26 15:01:55.388 368-368/? I/DEBUG: #03 pc 00012ac3 /system/lib/libc.so
04-26 15:01:55.388 368-368/? I/DEBUG: #04 pc 000225d4 /system/lib/libc.so (abort+4)
04-26 15:01:55.388 368-368/? I/DEBUG: #05 pc 0004e623 /system/lib/libdvm.so (dvmAbort+110)
04-26 15:01:55.388 368-368/? I/DEBUG: #06 pc 0007df8b /system/lib/libdvm.so (dvmGetMethodIdx(Method const*)+194)
04-26 15:01:55.388 368-368/? I/DEBUG: #07 pc 0007f93f /system/lib/libdvm.so (dvmCreateReflectMethodObject(Method const*)+226)
04-26 15:01:55.388 368-368/? I/DEBUG: #08 pc 0007fb45 /system/lib/libdvm.so (dvmGetDeclaredMethods(ClassObject*, bool)+292)
04-26 15:01:55.388 368-368/? I/DEBUG: #09 pc 0007601f /system/lib/libdvm.so
04-26 15:01:55.388 368-368/? I/DEBUG: #10 pc 0002a0a0 /system/lib/libdvm.so
04-26 15:01:55.388 368-368/? I/DEBUG: #11 pc 000329a4 /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)
04-26 15:01:55.388 368-368/? I/DEBUG: #12 pc 0002fec0 /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+232)
04-26 15:01:55.388 368-368/? I/DEBUG: #13 pc 0006dabd /system/lib/libdvm.so (dvmInvokeMethod(Object*, Method const*, ArrayObject*, ArrayObject*, ClassObject*, bool)+392)
04-26 15:01:55.388 368-368/? I/DEBUG: #14 pc 000777c1 /system/lib/libdvm.so
04-26 15:01:55.388 368-368/? I/DEBUG: #15 pc 0002a0a0 /system/lib/libdvm.so
04-26 15:01:55.388 368-368/? I/DEBUG: #16 pc 000329a4 /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)
04-26 15:01:55.388 368-368/? I/DEBUG: #17 pc 0002fec0 /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+232)
04-26 15:01:55.388 368-368/? I/DEBUG: #18 pc 0006d78d /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list)+336)
04-26 15:01:55.388 368-368/? I/DEBUG: #19 pc 00052def /system/lib/libdvm.so
04-26 15:01:55.388 368-368/? I/DEBUG: #20 pc 0004fbdb /system/lib/libandroid_runtime.so
04-26 15:01:55.388 368-368/? I/DEBUG: #21 pc 00050d9d /system/lib/libandroid_runtime.so (android::AndroidRuntime::start(char const*, char const*)+472)
04-26 15:01:55.388 368-368/? I/DEBUG: #22 pc 000010bb /system/bin/app_process
04-26 15:01:55.388 368-368/? I/DEBUG: #23 pc 0000e53b /system/lib/libc.so (__libc_init+50)
04-26 15:01:55.388 368-368/? I/DEBUG: #24 pc 00000db8 /system/bin/app_process

通过日志可以发现系统在寻找方法的时候通过MethodID在全局搜索找到了原来的Method,虽然这个Method的name并没有被我们替换,但是这个Method的classObject已经被我们替换(这是一个引用关系,需要调用这个classObject的静态方法,所以不能保留原来Method的classObject)所以会导致对比的时候出现错误,导致如下异常,具体代码在Annotation.cpp
dvmGetMethodIdx方法中,通过compareMethodStr对比class name, method name, method signature 这几项,从代码中看用的是二分搜索法,那么说明这个id是按照从小到大排列的.
注意alibaba的AndFix用的也是这个技术,但是它没有出现这个问题,因为它的方法签名和之前的方法是完全一致的.

现在可以祭出下面这张结构图了:

1461656394434.jpgenter description here

就是这个methods_ids

Dalvik METHOD结构

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
typedef struct Method {
struct ClassObject *clazz;
u4 accessFlags;
u2 methodIndex;
u2 registersSize; /* ins + locals */
u2 outsSize;
u2 insSize;
/* method name, e.g. "<init>" or "eatLunch" */
const char* name;
/*
* Method prototype descriptor string (return and argument types).
*
* TODO: This currently must specify the DexFile as well as the proto_ids
* index, because generated Proxy classes don't have a DexFile. We can
* remove the DexFile* and reduce the size of this struct if we generate
* a DEX for proxies.
*/
DexProto prototype;
/* short-form method descriptor string */
const char* shorty;
/*
* The remaining items are not used for abstract or native methods.
* (JNI is currently hijacking "insns" as a function pointer, set
* after the first call. For internal-native this stays null.)
*/
/* the actual code */
u2* insns;
/* cached JNI argument and return-type hints */
int jniArgInfo;
/*
* Native method ptr; could be actual function or a JNI bridge. We
* don't currently discriminate between DalvikBridgeFunc and
* DalvikNativeFunc; the former takes an argument superset (i.e. two
* extra args) which will be ignored. If necessary we can use
* insns==NULL to detect JNI bridge vs. internal native.
*/
DalvikBridgeFunc nativeFunc;
#ifdef WITH_PROFILER
bool inProfile;
#endif
#ifdef WITH_DEBUGGER
short debugBreakpointCount;
#endif
bool fastJni;
/*
* JNI: true if this method has no reference arguments. This lets the JNI
* bridge avoid scanning the shorty for direct pointers that need to be
* converted to local references.
*
* TODO: replace this with a list of indexes of the reference arguments.
*/
bool noRef;
} Method;

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
//麻烦的地方是,这是个逆向过程,Method结构里并没有存储在DexFile里所对应的Method的ID是多少,但是通过MethodID可以找到对应的类,所以才会出现从header里搜索这个id的过程,暂时没搞懂Method为啥不存储methodID,怀疑可能跟JIT有关系
u4 dvmGetMethodIdx(const Method* method)
{
if (method->clazz->pDvmDex == NULL) return 0;
DexFile* pDexFile = method->clazz->pDvmDex->pDexFile;
u4 hi = pDexFile->pHeader->methodIdsSize -1;
u4 lo = 0;
u4 cur;
while (hi >= lo) {
int cmp;
cur = (lo + hi) / 2;
cmp = compareMethodStr(pDexFile, cur, method);
if (cmp < 0) {
lo = cur + 1;
} else if (cmp > 0) {
hi = cur - 1;
} else {
break;
}
}
if (hi < lo) {
/* this should be impossible -- the method came out of this DEX */
char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
ALOGE("Unable to find method %s.%s %s in DEX file!",
method->clazz->descriptor, method->name, desc);
free(desc);
dvmAbort();
}
return cur;
}
/*
* Compare the attributes (class name, method name, method signature) of
* the specified method to "method".
*/
static int compareMethodStr(DexFile* pDexFile, u4 methodIdx,
const Method* method)//这个method就是被hook的方法
{
const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx);//根据遍历的id取出这个方法的相关信息,这个方法的签名是原来的签名,但是这个方法对应的clazz被我们替换了.这个MethodId
const char* str = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);//从信息里取出类名
int result = strcmp(str, method->clazz->descriptor);//比较类名,这里会一直是false,我们可以替换clazz的descriptor,但是我们不能这么做.破坏了类原有的信息.
if (result == 0) {
str = dexStringById(pDexFile, pMethodId->nameIdx);//假如类名一致
result = strcmp(str, method->name);
if (result == 0) {
DexProto proto;
dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
result = dexProtoCompare(&proto, &method->prototype);
}
}
return result;
}

上述源码中涉及到的几个重要系统源码:

java_lang_Method.cpp

java_lang_Class.cpp

Method.java

DexFile.h