@@ -14,6 +14,24 @@ import com.android.tools.smali.dexlib2.iface.reference.TypeReference
14
14
import java.util.EnumSet
15
15
import kotlin.collections.forEach
16
16
17
+ /* *
18
+ * Matches method [Instruction] objects, similar to how [Fingerprint] matches entire fingerprints.
19
+ *
20
+ * The most basic filters match only opcodes and nothing more,
21
+ * and more precise filters can match:
22
+ * - Field references (get/put opcodes) by name/type.
23
+ * - Method calls (invoke_* opcodes) by name/parameter/return type.
24
+ * - Object instantiation for specific class types.
25
+ * - Literal const values.
26
+ *
27
+ * Variable space is allowed between each filter.
28
+ *
29
+ * By default [OpcodeFilter] and [OpcodesFilter] use a default [maxInstructionsBefore] of zero,
30
+ * meaning the opcode must be immediately after the previous filter.
31
+ *
32
+ * All other filters use a default [maxInstructionsBefore] of [METHOD_MAX_INSTRUCTIONS] meaning
33
+ * they can match anywhere after the previous filter.
34
+ */
17
35
abstract class InstructionFilter (
18
36
/* *
19
37
* Maximum number of non matching method instructions that can appear before this filter.
@@ -23,6 +41,12 @@ abstract class InstructionFilter(
23
41
val maxInstructionsBefore : Int = METHOD_MAX_INSTRUCTIONS
24
42
) {
25
43
44
+ /* *
45
+ * If this filter matches the method instruction.
46
+ *
47
+ * method index can be ignored unless a filter has an unusual reason,
48
+ * such as checking for the last index of a method.
49
+ */
26
50
abstract fun matches (
27
51
method : Method ,
28
52
instruction : Instruction ,
@@ -32,6 +56,7 @@ abstract class InstructionFilter(
32
56
companion object {
33
57
/* *
34
58
* Maximum number of instructions allowed in a Java method.
59
+ * Indicates to allow a match anywhere after the previous filter.
35
60
*/
36
61
const val METHOD_MAX_INSTRUCTIONS = 65535
37
62
}
@@ -54,6 +79,7 @@ class AnyFilter(
54
79
}
55
80
}
56
81
82
+
57
83
/* *
58
84
* Single opcode.
59
85
*/
@@ -71,6 +97,12 @@ class OpcodeFilter(
71
97
}
72
98
73
99
companion object {
100
+ /* *
101
+ * First opcode can match anywhere in a method, but all
102
+ * subsequent opcodes must match after the previous opcode.
103
+ *
104
+ * A value of `null` indicates to match any opcode.
105
+ */
74
106
fun listOfOpcodes (opcodes : Collection <Opcode ?>): List <InstructionFilter > {
75
107
var list = ArrayList <InstructionFilter >(opcodes.size)
76
108
@@ -91,21 +123,19 @@ class OpcodeFilter(
91
123
}
92
124
}
93
125
126
+
94
127
/* *
95
128
* Matches multiple opcodes.
96
129
* If using only a single opcode instead use [OpcodeFilter].
97
130
*/
98
- open class OpcodesFilter (
99
- /* *
100
- * Value of null will match any opcode.
101
- */
131
+ open class OpcodesFilter private constructor(
102
132
val opcodes : EnumSet <Opcode >? ,
103
- maxInstructionsBefore : Int = METHOD_MAX_INSTRUCTIONS ,
133
+ maxInstructionsBefore : Int ,
104
134
) : InstructionFilter(maxInstructionsBefore) {
105
135
106
136
constructor (
107
137
/* *
108
- * Value of null will match any opcode.
138
+ * Value of ` null` will match any opcode.
109
139
*/
110
140
opcodes: List <Opcode >? ,
111
141
maxInstructionsBefore: Int = METHOD_MAX_INSTRUCTIONS
@@ -119,18 +149,31 @@ open class OpcodesFilter(
119
149
if (opcodes == null ) {
120
150
return true // Match anything.
121
151
}
122
- return opcodes.contains(instruction.opcode) == true
152
+ return opcodes.contains(instruction.opcode)
123
153
}
124
154
}
125
155
156
+
157
+ /* *
158
+ * Literal value, such as:
159
+ * `const v1, 0x7f080318`
160
+ *
161
+ * that can be matched using:
162
+ * `LiteralFilter(0x7f080318)`
163
+ * or
164
+ * `LiteralFilter(2131231512)`
165
+ *
166
+ * Use a lambda if the literal is not known at the declaration of this object such as:
167
+ * `LiteralFilter({ OtherClass.findLiteralToUse() })`
168
+ */
126
169
class LiteralFilter (
127
170
var literal : () -> Long ,
128
171
opcodes : List <Opcode >? = null ,
129
172
maxInstructionsBefore : Int = METHOD_MAX_INSTRUCTIONS ,
130
173
) : OpcodesFilter(opcodes, maxInstructionsBefore) {
131
174
132
175
/* *
133
- * Constant long literal.
176
+ * Integer/Long literal.
134
177
*/
135
178
constructor (
136
179
literal : Long ,
@@ -160,6 +203,19 @@ class LiteralFilter(
160
203
}
161
204
}
162
205
206
+ /* *
207
+ * Filters opcode method calls.
208
+ *
209
+ * `Null` parameters matches anything.
210
+ *
211
+ * By default any type of method call matches.
212
+ * Specify opcodes if a specific type of method call is desired (such as only static calls).
213
+ *
214
+ * Fingerprints can be used to find obfuscated class/method names to filter with,
215
+ * such as:
216
+ * `MethodFilter(definingClass = { fingerprint.originalClassDef.type },
217
+ * methodName = { fingerprint.originalMethod.name })`
218
+ */
163
219
class MethodFilter (
164
220
/* *
165
221
* Defining class of the method call. Matches using endsWith().
@@ -310,40 +366,42 @@ class MethodFilter (
310
366
private val regex = Regex (""" ^(L[^;]+;)->([^(\s]+)\(([^)]*)\)(.+)$""" )
311
367
312
368
/* *
313
- * Returns a filter for a JVM-style method signature. e.g.:
369
+ * Returns a filter for a copy pasted JVM-style method signature. e.g.:
314
370
* Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;
371
+ *
372
+ * Does not support obfuscated method names or parameter/return types.
315
373
*/
316
374
fun parseJvmMethodCall (
317
375
methodSignature : String ,
318
376
) = parseJvmMethodCall(methodSignature, null , METHOD_MAX_INSTRUCTIONS )
319
377
320
378
/* *
321
- * Returns a filter for a JVM-style method signature. e.g.:
379
+ * Returns a filter for a copy pasted JVM-style method signature. e.g.:
322
380
* Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;
323
381
*
324
- * Does not support obfuscated method names or parameter/return types
382
+ * Does not support obfuscated method names or parameter/return types.
325
383
*/
326
384
fun parseJvmMethodCall (
327
385
methodSignature : String ,
328
386
maxInstructionsBefore : Int
329
387
) = parseJvmMethodCall(methodSignature, null , maxInstructionsBefore)
330
388
331
389
/* *
332
- * Returns a filter for a JVM-style method signature. e.g.:
390
+ * Returns a filter for a copy pasted JVM-style method signature. e.g.:
333
391
* Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;
334
392
*
335
- * Does not support obfuscated method names or parameter/return types
393
+ * Does not support obfuscated method names or parameter/return types.
336
394
*/
337
395
fun parseJvmMethodCall (
338
396
methodSignature : String ,
339
397
opcodes : List <Opcode >? ,
340
398
) = parseJvmMethodCall(methodSignature, opcodes, METHOD_MAX_INSTRUCTIONS )
341
399
342
400
/* *
343
- * Returns a filter for a JVM-style method signature. e.g.:
401
+ * Returns a filter for a copy pasted JVM-style method signature. e.g.:
344
402
* Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;
345
403
*
346
- * Does not support obfuscated method names or parameter/return types
404
+ * Does not support obfuscated method names or parameter/return types.
347
405
*/
348
406
fun parseJvmMethodCall (
349
407
methodSignature : String ,
@@ -415,14 +473,23 @@ class MethodFilter (
415
473
}
416
474
}
417
475
476
+ /* *
477
+ * Matches a field call, such as:
478
+ * `iget-object v0, p0, Lahhh;->g:Landroid/view/View;`
479
+ *
480
+ * Using filter:
481
+ * `FieldFilter(type ="Landroid/view/View;", opcode = Opcode.IGET_OBJECT)`
482
+ *
483
+ * If the field is a call to the defining class, use `this` as the declaring class:
484
+ * `FieldFilter(definingClass = "this", type ="Landroid/view/View;", opcode = Opcode.IGET_OBJECT)`
485
+ */
418
486
class FieldFilter (
419
487
/* *
420
488
* Defining class of the field call. Matches using endsWith().
421
489
*
422
490
* For calls to a method in the same class, use 'this' as the defining class.
423
491
* Note: 'this' does not work for fields found in superclasses.
424
492
*/
425
-
426
493
val definingClass : (() -> String )? = null ,
427
494
/* *
428
495
* Name of the field. Must be a full match of the field name.
0 commit comments