Skip to content

Commit 5610763

Browse files
authored
Merge pull request #1 from AleksRychkov/redesign
Redesign
2 parents 8c1ae10 + 5c21b50 commit 5610763

File tree

101 files changed

+2848
-1840
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+2848
-1840
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
.externalNativeBuild
1010
.cxx
1111
local.properties
12+
/perfetto/trace_file.perfetto-trace

README.md

+87-117
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,23 @@ plugins {
2121
### Quick start
2222

2323
Create `methodhook_activity.conf` file to instruct plugin what and where to inject calls. You can
24-
place it adjacent to
25-
application's build.gradle[.kts] file
24+
place it adjacent to application's build.gradle[.kts] file
2625

2726
```conf
2827
activity {
28+
type = "default"
29+
package = "*"
2930
superClass = "android.app.Activity"
30-
methods = [
31-
"onCreate"
32-
]
33-
packageId = "org.example"
34-
beginMethodWith = "org.example.MethodHook.start"
35-
endMethodWith = "org.example.MethodHook.end"
31+
interfaces = []
32+
class = "*"
33+
methods = [ "onCreate" ]
34+
enter = "org.example.MethodHook.enter"
35+
exit = "org.example.MethodHook.exit"
3636
}
3737
```
3838

3939
Add plugin's configuration to an Android application's `build.gradle[.kts]` file. In `addConfig`
40-
method specify a path
41-
to
42-
a created `methodhook_activity.conf` file previously
40+
method specify a path to a created `methodhook_activity.conf` file previously
4341

4442
```gradle
4543
// build.gradle.kts
@@ -66,21 +64,22 @@ package org.example
6664

6765
object MethodHook {
6866
@JvmStatic
69-
fun start(runtimeClazz: String, clazz: String, method: String) {
70-
println("MethodHook::start::$runtimeClazz::$clazz::$method")
67+
fun enter(clazz: String, method: String, descriptor: String) {
68+
println("MethodHook::enter::$clazz.$method.$descriptor")
7169
}
7270

7371
@JvmStatic
74-
fun end(runtimeClazz: String, clazz: String, method: String) {
75-
println("MethodHook::end::$runtimeClazz::$clazz::$method")
72+
fun exit(clazz: String, method: String, descriptor: String) {
73+
println("MethodHook::exit::$clazz.$method.$descriptor")
7674
}
7775
}
7876
```
7977

8078
Build project's `debug` buildType.
81-
Now, `onCreate(savedInstanceState: Bundle?)` method of all activities defined in `org.example`
82-
package should be
83-
instructed with `MethodHook::start` and `MethodHook::end` calls.
79+
Now, `onCreate(savedInstanceState: Bundle?)` method of all activities should be
80+
instructed with `MethodHook::enter` and `MethodHook::exit` calls.
81+
82+
![!methodhook example](./doc/methodhook_example.png)
8483

8584
### Example
8685

@@ -107,53 +106,72 @@ You can define multiple configs inside one file. Start a config with a name
107106

108107
```config
109108
activity {
110-
superClass = "android.app.Activity"
111-
methods = [
112-
"onCreate"
113-
]
114-
packageId = "org.example"
115-
beginMethodWith = "org.example.MethodHook.start"
116-
endMethodWith = "org.example.MethodHook.end"
109+
...
117110
}
118111
frgament {
119-
exactClass = "org.example.SomeFragment"
120-
methods = [
121-
"onResume"
122-
]
123-
beginMethodWith = "org.example.MethodHook.start"
124-
endMethodWith = "org.example.MethodHook.end"
112+
...
125113
}
126114
```
127115

128-
Each config supports following options:
129-
130-
* `superClass`: Specifies the parent class (__canonical name__) for which the plugin should target
131-
methods in its
132-
subclasses, e.g. `android.app.Activity`. Must not be defined along with `exactClass`.
133-
* `exactClass`: Specifies the exact class (__canonical name__) for which the plugin should target
134-
methods,
135-
`e.g. org.example.SomeClass`. Must not be defined along with `superClass`.
136-
* `methods`: Specifies an array of methods (identified by their names) where the plugin will
137-
inject additional code, e.g. `onCreate`.
138-
* `beginMethodWith`: Specifies a reference to a method to be injected at the beginning of instructed
139-
methods,
140-
e.g. `org.example.MethodHook.start`.
141-
* `endMethodWith`: Specifies a reference to a method to be injected at the end of instructed
142-
methods,
143-
e.g. `org.example.MethodHook.end`.
144-
* `packageId`: Specifies the package name that identifies the group of classes where the plugin will
145-
target methods for
146-
injection, e.g. `org.example`.
147-
Not required, if not defined, the config will be applied to all files in your project, including
148-
third-party libraries
149-
150-
> [!IMPORTANT]
151-
> * Each config must have either `superClass` or `exactClass` option, but not both.
152-
> * Do not duplicate configs with same `superClass` or `exactClass` option.
153-
> * Each config must have `methods` option.
154-
> * Each config must have at least one of the options (or both): `beginMethodWith`, `endMethodWith`.
155-
> * Bear in mind that, a class, targeted by the plugin, must be associated with only one config.
156-
Otherwise, you will get an error.
116+
#### Config file parameters
117+
118+
* `type`: Specifies type of instrumentation to apply.
119+
* **Required**
120+
* Variants:
121+
* __default__ - instrument methods with `enter` and `exit` static method calls, with following arguments:
122+
`className: String`, `methodName: String`, `methodDescriptor: String`.
123+
* __trace__ - instrument methods with `android.os.Trace.beginSection` and `android.os.Trace.endSection` calls.
124+
* __descriptor__ - instrument methods with `enter` and `exit` static method calls. `enter` method must have same
125+
list of arguments as instrumented method, `exit` must have single nullable argument of `android.lang.Object`(
126+
`kotlin.Any`) type.
127+
128+
* `package`: Specifies the package name that identifies the group of classes where the plugin will target methods for
129+
injection.
130+
* **Required**
131+
* Variants:
132+
* /* - any package.
133+
* __package__, e.g. `"org.example"`
134+
135+
136+
* `superClass`: Specifies the parent class (__canonical name__) for which the plugin should target methods in its
137+
subclasses.
138+
* **Required**
139+
* Variants:
140+
* \* - any parent class or none.
141+
* __canonical name__, e.g. `"android.app.Activity"`.
142+
143+
* `interfaces`: Specifies a list of interfaces (by __canonical name__). If a class implements any of the specified
144+
interfaces, its methods will be instrumented.
145+
* **Required**
146+
* Variants:
147+
* \* - any interface or none.
148+
* __canonical name__, e.g. `[ "java.util.RandomAccess" ]`.
149+
150+
* `class`: Specifies the exact class (__canonical name__) for which the plugin should target methods.
151+
* **Required**
152+
* Variants:
153+
* \* - any class.
154+
* __canonical name__, e.g. `"org.example.SomeClass" `.
155+
156+
* `methods`: Specifies an array of methods (identified by their names) where the plugin will inject additional code.
157+
* **Required**
158+
* Variants:
159+
* \* - all methods including constructor.
160+
* __method name__ - exact method name to be instrumented, e.g. ` [ "onCreate" ]`.
161+
162+
* `descriptor`: Specifies instrumented method descriptor.
163+
* **Not required**
164+
* Variants:
165+
* __method descriptor__, e.g. `"(Landroid/content/Context;)V"`.
166+
167+
* `enter`: Specifies a reference to a method to be injected at the beginning of instructed methods.
168+
* **Not required**
169+
* Variants:
170+
* __static method reference__, e.g. `org.example.MethodHook.enter`
171+
* `exit`: Specifies a reference to a method to be injected at the end of instructed methods.
172+
* **Not required**
173+
* Variants:
174+
* __static method reference__, e.g. `org.example.MethodHook.exit`
157175

158176
#### Plugin configuration
159177

@@ -190,67 +208,19 @@ androidMethodHook {
190208

191209
The `androidMethodHook` configuration supports next options:
192210

193-
* `forceLogging`: Enables info logs of the plugin. Same as if executing gradle command with `--info`
194-
flag,
195-
e.g. `./gradlew assembleDebug --info`. Default value is `false`.
196-
* `configs`: Creates plugin's config for specific build variant. The name of config must be the same
197-
as name of build
198-
variant, e.g. buildType: `debug`, or if you have productFlavor named `demo`: `demoDebug`. You can
199-
have separate sets
211+
* `forceLogging`: Enables info logs of the plugin. Same as if executing gradle command with `--info` flag, e.g.
212+
`./gradlew assembleDebug --info`. Default value is `false`.
213+
214+
* `configs`: Creates plugin's config for specific build variant. The name of config must be the same as name of build
215+
variant, e.g. buildType: `debug`, or if you have productFlavor named `demo`: `demoDebug`. You can have separate sets
200216
of config for different build variants.
201217
* `addConfig`: Adds relative path to a config file.
202218

203-
#### Inject method calls
204-
205-
Plugin allows to inject a single method call to the beginning or the end of a target method.
206-
Injected method must be `public static void` and have three `String` arguments.
207-
Create a separate method to be injected at the beginning and end of the target method
208-
209-
```kotlin
210-
@file:Suppress("UNUSED_PARAMETER")
211-
212-
package org.example
213-
214-
object MethodHook {
215-
@JvmStatic
216-
fun start(runtimeClazz: String, clazz: String, method: String) {
217-
// some logic to be executed at the beginning of a [method]
218-
}
219-
220-
@JvmStatic
221-
fun end(runtimeClazz: String, clazz: String, method: String) {
222-
// some logic to be executed at the end of a [method]
223-
}
224-
}
225-
```
226-
227-
```java
228-
package org.example;
229-
230-
public class MethodHook {
231-
public static void start(String runtimeClazz, String clazz, String method) {
232-
// some logic to be executed at the beginning of a [method]
233-
}
219+
## References
234220

235-
public static void end(String runtimeClazz, String clazz, String method) {
236-
// some logic to be executed at the end of a [method]
237-
}
238-
}
239-
```
240-
241-
* first argument is a runtime class, `this.getClass().getName()`, e.g. `org.example.MainActivity`.
242-
* second argument is an actual class where method was called, e.g. `org.example.AbstractActivity`.
243-
* third argument is a method name, arguments, return type, e.g. `onCreate(Bundle)->void`.
244-
245-
Specify created methods in config
246-
247-
```conf
248-
activity {
249-
250-
beginMethodWith = "org.example.MethodHook.start"
251-
endMethodWith = "org.example.MethodHook.end"
252-
}
253-
```
221+
* [Using the ASM framework to implement common Java bytecode transformation patterns](https://lsieun.github.io/assets/pdf/asm-transformations.pdf)
222+
* [ASM framework - Eugene Kuleshov](https://wiki.jvmlangsummit.com/pdf/23_Kuleshov_asm.pdf)
223+
* [ASM guide](https://asm.ow2.io/asm4-guide.pdf)
254224

255225
## License
256226

doc/methodhook_example.png

457 KB
Loading

examples/android-gradle-kts/build.gradle.kts

+8-3
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,21 @@ android {
3636

3737
dependencies {
3838
implementation(libs.androidx.appcompat)
39+
40+
implementation(libs.okhttp)
3941
}
4042

4143
androidMethodHook {
4244
forceLogging = true
4345
forceClassTransform = true
4446
configs {
4547
create("debug") {
46-
addConfig("./methodhook/methodhook_activity.conf")
47-
addConfig("./methodhook/methodhook_fragment.conf")
48-
addConfig("./methodhook/methodhook_service.conf")
48+
addConfig("./methodhook/descriptor.conf")
49+
addConfig("./methodhook/default.conf")
50+
addConfig("./methodhook/auto_trace.conf")
51+
52+
addConfig("./methodhook/okhttp.conf")
53+
addConfig("./methodhook/frame_counter.conf")
4954
}
5055
}
5156
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
autoTrace {
2+
type = "trace"
3+
package = "io.github.aleksrychkov.example"
4+
superClass = "*"
5+
interfaces = [ "io.github.aleksrychkov.example.AutoTrace" ]
6+
class = "*"
7+
methods = []
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
descriptorFoo {
2+
type = "default"
3+
package = "io.github.aleksrychkov.example.sandbox"
4+
superClass = "*"
5+
interfaces = []
6+
class = "io.github.aleksrychkov.example.sandbox.DefaultPlayground"
7+
methods = [ "test" ]
8+
enter = "io.github.aleksrychkov.methodhook.DefaultHook.enter"
9+
exit = "io.github.aleksrychkov.methodhook.DefaultHook.exit"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
descriptorFoo {
2+
type = "descriptor"
3+
package = "io.github.aleksrychkov.example.sandbox"
4+
superClass = "*"
5+
interfaces = []
6+
class = "io.github.aleksrychkov.example.sandbox.DescriptorPlayground"
7+
methods = [ "foo" ]
8+
descriptor = "(Ljava/lang/String;I)Ljava/lang/String;"
9+
enter = "io.github.aleksrychkov.methodhook.DescriptorHook.enterFoo"
10+
exit = "io.github.aleksrychkov.methodhook.DescriptorHook.exitFoo"
11+
}
12+
descriptorLong {
13+
type = "descriptor"
14+
package = "io.github.aleksrychkov.example.sandbox"
15+
superClass = "*"
16+
interfaces = []
17+
class = "io.github.aleksrychkov.example.sandbox.DescriptorPlayground"
18+
methods = [ "llong" ]
19+
descriptor = "()J"
20+
enter = "io.github.aleksrychkov.methodhook.DescriptorHook.enterLong"
21+
exit = "io.github.aleksrychkov.methodhook.DescriptorHook.exitLong"
22+
}
23+
descriptorAny {
24+
type = "descriptor"
25+
package = "io.github.aleksrychkov.example.sandbox"
26+
superClass = "*"
27+
interfaces = []
28+
class = "io.github.aleksrychkov.example.sandbox.DescriptorPlayground"
29+
methods = [ "aany" ]
30+
descriptor = "(Ljava/lang/Object;)Ljava/lang/Object;"
31+
enter = "io.github.aleksrychkov.methodhook.DescriptorHook.enterAny"
32+
exit = "io.github.aleksrychkov.methodhook.DescriptorHook.exitAny"
33+
}
34+
descriptorInt {
35+
type = "descriptor"
36+
package = "io.github.aleksrychkov.example.sandbox"
37+
superClass = "*"
38+
interfaces = []
39+
class = "io.github.aleksrychkov.example.sandbox.DescriptorPlayground"
40+
methods = [ "iint" ]
41+
descriptor = "(I)I"
42+
enter = "io.github.aleksrychkov.methodhook.DescriptorHook.enterInt"
43+
exit = "io.github.aleksrychkov.methodhook.DescriptorHook.exitInt"
44+
}
45+
descriptorVoid {
46+
type = "descriptor"
47+
package = "io.github.aleksrychkov.example.sandbox"
48+
superClass = "*"
49+
interfaces = []
50+
class = "io.github.aleksrychkov.example.sandbox.DescriptorPlayground"
51+
methods = [ "vvoid" ]
52+
descriptor = "()V"
53+
enter = "io.github.aleksrychkov.methodhook.DescriptorHook.enterVoid"
54+
exit = "io.github.aleksrychkov.methodhook.DescriptorHook.exitVoid"
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
frameCounterContext {
2+
type = "descriptor"
3+
package = "androidx.appcompat.app"
4+
superClass = "*"
5+
interfaces = []
6+
class = "androidx.appcompat.app.AppCompatActivity"
7+
methods = [ "attachBaseContext" ]
8+
descriptor = "(Landroid/content/Context;)V"
9+
enter = "io.github.aleksrychkov.methodhook.FrameCounterHook.attachBaseContext"
10+
}

0 commit comments

Comments
 (0)