Skip to content

Commit a54c961

Browse files
committed
1. 完成方法调用时Hook:包括before,after,replace三种时机,static,非static两种类型,void非void两种方法,共12中不同状态的Hook
2. 添加before:static,非static,void非void共4中情况下的测试用例 待完成:方法定义时的12种Hook方式,还需要添加更多测试用例
1 parent 58be77f commit a54c961

32 files changed

+846
-31
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ $RECYCLE.BIN/
200200
.AppleDouble
201201
.LSOverride
202202

203-
# Icon must end with two \r
203+
# Icon must end with two \res
204204
Icon
205205

206206

replugin-gradle/README.md

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Gradle Method Hook 说明
2+
3+
## 说明:
4+
* `info`:MethodInfo,originMethod的方法信息:className,methodName,methodDesc. 为更灵活的修改提供条件。
5+
* `o`: 原方法调用者对象; `Origin`:静态方法类型
6+
* `A`,`B`: 参数类型
7+
* `R`: 方法返回类型
8+
* `V`: 方法返回类型为`void`
9+
10+
## 使用范围
11+
12+
* 先支持Java
13+
* 之后再更直观的支持Kotlin. 当然在没有直观支持kotlin之前,也可以通过java的方式通过稍微复杂的配置实现kotlin的支持.需要对编译之后的kotlin与java的区别做一些了解,尤其object,componnet object,@JVMStatic 等的原理
14+
15+
## 1. 在方法定义处Hook
16+
17+
* 原方法:`o.f(a,b):R`
18+
19+
### 1.1 before:在方法开始处hook
20+
21+
```
22+
原方法非静态:
23+
o.f(a,b):R {
24+
H.f(info,o:O,a:A,b:B):V
25+
....
26+
return ...
27+
}
28+
29+
原方法静态:
30+
31+
Origin.s(a,b):R {
32+
H.f(info,a:A,b:B):V
33+
--
34+
return ...
35+
}
36+
```
37+
38+
### 1.2 after:在方法结尾处hook. 将原结果传入,返回hook之后的结果
39+
40+
```
41+
原方法非静态:
42+
o.f(a,b):R {
43+
....
44+
return H.f(info,o:O,a:A,b:B,res:R):R
45+
}
46+
47+
原方法静态:
48+
49+
Origin.s(a,b):R {
50+
val res = .. // 先执行原有逻辑,将结果作为参数产地给替换者方法
51+
return H.f(info,o:O,a:A,b:B,res:R):R
52+
}
53+
```
54+
55+
### 1.3 replace:不执行方法体内容,直接hook,返回hook结果。
56+
57+
```
58+
原方法非静态:
59+
o.f(a,b):R {
60+
return H.f(info,o:O,a:A,b:B):R
61+
}
62+
63+
原方法静态:
64+
65+
Origin.s(a,b):R {
66+
return H.f(info,a:A,b:B):R
67+
}
68+
69+
```
70+
71+
## 2. 在方法调用处Hook
72+
73+
* 原方法调用处:
74+
75+
```
76+
{
77+
o.f(a,b):R
78+
}
79+
80+
```
81+
82+
### 1.1 before:方法执行之前hook
83+
84+
* 可以在同一个原方法上定义多个beforeMethod
85+
86+
```
87+
原方法非静态:
88+
{
89+
H.f(info,o:O,a:A,b:B):V
90+
o.f(a,b):R
91+
}
92+
93+
原方法静态:
94+
95+
{
96+
H.f(info,a:A,b:B):R
97+
Origin.f(a,b):R
98+
}
99+
100+
```
101+
102+
### 1.2 after:方法执行之后Hook,并将参数和结果同时传递给替换者。
103+
104+
* 每个原方法只能定义一个afterMethod
105+
106+
```
107+
原方法非静态:
108+
{
109+
val res = o.f(a,b):R
110+
H.f(info,o:O,a:A,b:B,res:R):R
111+
}
112+
113+
原方法静态:
114+
{
115+
val res = Origin.f(a,b):R
116+
H.f(info,a:A,b:B,res:R):R
117+
}
118+
```
119+
120+
### 1.3 replace:直接替换
121+
122+
* 每个原方法只能定义一个replaceMethod
123+
124+
```
125+
原方法非静态:
126+
{
127+
H.f(info,o:O,a:A,b:B):R
128+
}
129+
130+
原方法静态:
131+
132+
{
133+
H.f(info,a:A,b:B):R
134+
}
135+
136+
```

replugin-gradle/src/main/kotlin/com/qihoo360/replugin/config/BaseExtension.kt

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.qihoo360.replugin.config
22

3+
import groovy.lang.Closure
4+
import org.gradle.api.NamedDomainObjectContainer
35
import java.io.File
46

57
/**
@@ -13,6 +15,7 @@ open class BaseExtension {
1315
var applicationId: String? = null
1416
open var libPackages: Set<String>? = setOf()
1517
open var targetClasses = setOf<String>()
18+
1619
fun isTargetClass(desc: String): Boolean {
1720
return targetClasses.contains(
1821
if (desc.contains(".")) desc.replace(
@@ -21,4 +24,11 @@ open class BaseExtension {
2124
) else desc
2225
)
2326
}
27+
28+
lateinit var hookMethods: NamedDomainObjectContainer<TargetMethod>
29+
fun hookMethods(closure: Closure<TargetMethod>) {
30+
this.hookMethods.configure(closure)
31+
}
32+
33+
val defaultHookMethod: Set<TargetMethod> = setOf()
2434
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.qihoo360.replugin.config
2+
3+
/**
4+
* author:gaoguanling
5+
* date:2021/11/15
6+
* time:19:32
7+
8+
* link:
9+
*/
10+
class HookMethodContainer(targetMethods: Set<TargetMethod>) {
11+
12+
private val methodConfig: Map<String, Map<TargetMethod.HookType, List<TargetMethod>>> = targetMethods
13+
.groupBy { getMethodKey(it) }
14+
.map { (methodKey, list) ->
15+
Pair(methodKey, list.groupBy { TargetMethod.getHookTypeByValue(it.hookType) })
16+
}.associate { (first, second) ->
17+
Pair(first, second)
18+
}
19+
20+
fun getMethodConfig(className: String, methodName: String, methodDesc: String): Map<TargetMethod.HookType, List<TargetMethod>>? {
21+
return methodConfig[getMethodKey(className, methodName, methodDesc)]
22+
}
23+
24+
fun isEmpty(): Boolean {
25+
return methodConfig.isEmpty()
26+
}
27+
28+
companion object {
29+
fun getInstance(extension: BaseExtension): HookMethodContainer {
30+
return HookMethodContainer(extension.defaultHookMethod.toSet().plus(extension.hookMethods.toSet()))
31+
}
32+
33+
fun getMethodKey(targetMethod: TargetMethod): String {
34+
return getMethodKey(targetMethod.className, targetMethod.methodName, targetMethod.methodDesc)
35+
}
36+
37+
fun getMethodKey(className: String, methodName: String, methodDesc: String): String {
38+
return "$className:$methodName$methodDesc"
39+
}
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.qihoo360.replugin.config
2+
3+
/**
4+
* author:gaoguanling
5+
* date:2021/11/18
6+
* time:00:10
7+
8+
* link:
9+
*/
10+
data class MethodInfoDesc(val owner: String, val name: String, val desc: String, val isInterface: Boolean)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.qihoo360.replugin.config
2+
3+
/**
4+
* author:gaoguanling
5+
* date:2021/11/15
6+
* time:17:53
7+
8+
* link:
9+
*/
10+
open class TargetMethod(var name: String) {
11+
var className: String = "com/qihoo360/replugin/base/method/Origin"
12+
var methodName: String = "f"
13+
var methodDesc: String = "(Lcom/qihoo360/replugin/base/method/A;Lcom/qihoo360/replugin/base/method/B;)Lcom/qihoo360/replugin/base/method/R"
14+
var targetClassName: String = "com/qihoo360/replugin/base/method/Target"
15+
var targetMethodName: String = "f"
16+
var targetMethodDesc: String = "(Lcom/qihoo360/replugin/base/method/A;Lcom/qihoo360/replugin/base/method/B;)Lcom/qihoo360/replugin/base/method/R"
17+
var hookType: Int = HookType.DEFINE_NORMAL_BEFORE_METHOD.value
18+
19+
constructor(
20+
name: String,
21+
className: String, methodName: String, methodDesc: String,
22+
targetClassName: String, targetMethodName: String, targetMethodDesc: String,
23+
hookType: Int
24+
) : this(name) {
25+
this.className = className
26+
this.methodName = methodName
27+
this.methodDesc = methodDesc
28+
this.targetClassName = targetClassName
29+
this.targetMethodName = targetMethodName
30+
this.targetMethodDesc = targetMethodDesc
31+
this.hookType = hookType
32+
}
33+
34+
enum class HookType(val value: Int) {
35+
DEFINE_NORMAL_BEFORE_METHOD(0x01),
36+
DEFINE_NORMAL_AFTER_METHOD(0x02),
37+
DEFINE_NORMAL_REPLACE_METHOD(0x03),
38+
DEFINE_STATIC_BEFORE_METHOD(0x04),
39+
DEFINE_STATIC_AFTER_METHOD(0x05),
40+
DEFINE_STATIC_REPLACE_METHOD(0x06),
41+
CALL_NORMAL_BEFORE_METHOD(0x07),
42+
CALL_NORMAL_AFTER_METHOD(0x08),
43+
CALL_NORMAL_REPLACE_METHOD(0x09),
44+
CALL_STATIC_BEFORE_METHOD(0x0a),
45+
CALL_STATIC_AFTER_METHOD(0x0b),
46+
CALL_STATIC_REPLACE_METHOD(0x0c)
47+
}
48+
49+
companion object {
50+
fun getHookTypeByValue(value: Int): HookType {
51+
return when (value) {
52+
0x01 ->
53+
HookType.DEFINE_NORMAL_BEFORE_METHOD
54+
0x02 ->
55+
HookType.DEFINE_NORMAL_AFTER_METHOD
56+
0x03 ->
57+
HookType.DEFINE_NORMAL_REPLACE_METHOD
58+
0x04 ->
59+
HookType.DEFINE_STATIC_BEFORE_METHOD
60+
0x05 ->
61+
HookType.DEFINE_STATIC_AFTER_METHOD
62+
0x06 ->
63+
HookType.DEFINE_STATIC_REPLACE_METHOD
64+
0x07 ->
65+
HookType.CALL_NORMAL_BEFORE_METHOD
66+
0x08 ->
67+
HookType.CALL_NORMAL_AFTER_METHOD
68+
0x09 ->
69+
HookType.CALL_NORMAL_REPLACE_METHOD
70+
0x0a ->
71+
HookType.CALL_STATIC_BEFORE_METHOD
72+
0x0b ->
73+
HookType.CALL_STATIC_AFTER_METHOD
74+
0x0c ->
75+
HookType.CALL_STATIC_REPLACE_METHOD
76+
else ->
77+
HookType.DEFINE_NORMAL_BEFORE_METHOD
78+
}
79+
}
80+
}
81+
82+
override fun toString(): String {
83+
return "TargetMethod(name='$name', className='$className', methodName='$methodName', methodDesc='$methodDesc', targetClassName='$targetClassName', targetMethodName='$targetMethodName', targetMethodDesc='$targetMethodDesc', hookType=$hookType)"
84+
}
85+
}

replugin-gradle/src/main/kotlin/com/qihoo360/replugin/plugin/PluginPlugin.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.qihoo360.replugin.AbstractPlugin
55
import com.qihoo360.replugin.Constants
66
import com.qihoo360.replugin.config.PluginExtension
77
import com.qihoo360.replugin.config.TargetClass
8+
import com.qihoo360.replugin.config.TargetMethod
89
import com.qihoo360.replugin.transform.AbstractTransform
910
import org.gradle.api.Project
1011

@@ -24,10 +25,11 @@ open class PluginPlugin : AbstractPlugin<PluginExtension>() {
2425
extension = project.extensions.getByName(Constants.PLUGIN_CONFIG) as PluginExtension
2526
if (extension == null)
2627
throw Exception("请在build.gradle 文件中配置 repluginPluginConfig!!")
27-
else{
28+
else {
2829
extension!!.applicationId = android.defaultConfig.applicationId
2930
extension!!.excludedClasses = project.container(TargetClass::class.java)
3031
extension!!.skipClasses = project.container(TargetClass::class.java)
32+
extension!!.hookMethods = project.container(TargetMethod::class.java)
3133
}
3234
}
3335

replugin-gradle/src/main/kotlin/com/qihoo360/replugin/transform/ClassReWriter.kt

+8-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package com.qihoo360.replugin.transform
22

33
import com.qihoo360.replugin.Log
44
import com.qihoo360.replugin.config.BaseExtension
5+
import com.qihoo360.replugin.transform.bean.InstrumentationContext
56
import com.qihoo360.replugin.transform.bean.TransformClassInfo
67
import com.qihoo360.replugin.transform.visitor.*
78
import org.objectweb.asm.ClassReader
89
import org.objectweb.asm.ClassWriter
9-
import org.objectweb.asm.util.CheckClassAdapter
1010

1111

1212
/**
@@ -35,15 +35,18 @@ object ClassReWriter {
3535
Log.detail(tag, "Skip: ${classInfo.name}")
3636
return null
3737
}
38-
val verifierVisitor = CheckClassAdapter(classWriter)
39-
val activityVisitor = ActivityClassVisitor(verifierVisitor, context)
38+
39+
// val verifierVisitor = CheckClassAdapter(classWriter)
40+
// val activityVisitor = ActivityClassVisitor(verifierVisitor, context)
41+
val activityVisitor = ActivityClassVisitor(classWriter, context)
4042
val broadCastVisitor = LocalBroadcastClassVisitor(activityVisitor, context)
4143
val providerVisitor = ProviderClassClassVisitor(broadCastVisitor, context)
4244
val identifierClassVisitor = IdentifierClassVisitor(providerVisitor, context)
43-
val constantClassVisitor = ConstantClassVisitor(identifierClassVisitor,context)
45+
val constantClassVisitor = ConstantClassVisitor(identifierClassVisitor, context)
46+
val finalVisitor = if (context.hookMethodConfig.isEmpty()) constantClassVisitor else MethodHookClassVisitor(constantClassVisitor, context)
4447

4548
classReader.accept(
46-
constantClassVisitor,
49+
finalVisitor,
4750
ClassReader.SKIP_FRAMES or ClassReader.EXPAND_FRAMES
4851
)
4952
return if (context.classModified) {

replugin-gradle/src/main/kotlin/com/qihoo360/replugin/transform/visitor/InstrumentationContext.kt replugin-gradle/src/main/kotlin/com/qihoo360/replugin/transform/bean/InstrumentationContext.kt

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
package com.qihoo360.replugin.transform.visitor
1+
package com.qihoo360.replugin.transform.bean
22

33
import com.qihoo360.replugin.Log
44
import com.qihoo360.replugin.config.BaseExtension
5-
import com.qihoo360.replugin.transform.bean.TransformClassInfo
5+
import com.qihoo360.replugin.config.HookMethodContainer
66
import java.util.*
77

88
/**
@@ -20,11 +20,12 @@ class InstrumentationContext(
2020
var superClassName: String? = null
2121
var classModified: Boolean = false
2222
var skipClass: Boolean = false
23-
23+
val hookMethodConfig = HookMethodContainer.getInstance(extension)
24+
2425
fun addSkippedMethod(name: String, desc: String) {
2526
Log.i(
2627
"InstrumentationContext",
27-
"Will skip all tracing in method " + classInfo.name + "#" + name + ":" + desc + " as requested"
28+
"Will skip all tracing in method ${classInfo.name}#$name:$desc"
2829
)
2930
skippedMethods[classInfo.name + "#" + name] = desc
3031
}

0 commit comments

Comments
 (0)