@@ -14,6 +14,24 @@ import com.android.tools.smali.dexlib2.iface.reference.TypeReference
1414import java.util.EnumSet
1515import kotlin.collections.forEach
1616
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+ */
1735abstract class InstructionFilter (
1836 /* *
1937 * Maximum number of non matching method instructions that can appear before this filter.
@@ -23,6 +41,12 @@ abstract class InstructionFilter(
2341 val maxInstructionsBefore : Int = METHOD_MAX_INSTRUCTIONS
2442) {
2543
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+ */
2650 abstract fun matches (
2751 method : Method ,
2852 instruction : Instruction ,
@@ -32,6 +56,7 @@ abstract class InstructionFilter(
3256 companion object {
3357 /* *
3458 * Maximum number of instructions allowed in a Java method.
59+ * Indicates to allow a match anywhere after the previous filter.
3560 */
3661 const val METHOD_MAX_INSTRUCTIONS = 65535
3762 }
@@ -54,6 +79,7 @@ class AnyFilter(
5479 }
5580}
5681
82+
5783/* *
5884 * Single opcode.
5985 */
@@ -71,6 +97,12 @@ class OpcodeFilter(
7197 }
7298
7399 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+ */
74106 fun listOfOpcodes (opcodes : Collection <Opcode ?>): List <InstructionFilter > {
75107 var list = ArrayList <InstructionFilter >(opcodes.size)
76108
@@ -91,21 +123,19 @@ class OpcodeFilter(
91123 }
92124}
93125
126+
94127/* *
95128 * Matches multiple opcodes.
96129 * If using only a single opcode instead use [OpcodeFilter].
97130 */
98- open class OpcodesFilter (
99- /* *
100- * Value of null will match any opcode.
101- */
131+ open class OpcodesFilter private constructor(
102132 val opcodes : EnumSet <Opcode >? ,
103- maxInstructionsBefore : Int = METHOD_MAX_INSTRUCTIONS ,
133+ maxInstructionsBefore : Int ,
104134) : InstructionFilter(maxInstructionsBefore) {
105135
106136 constructor (
107137 /* *
108- * Value of null will match any opcode.
138+ * Value of ` null` will match any opcode.
109139 */
110140 opcodes: List <Opcode >? ,
111141 maxInstructionsBefore: Int = METHOD_MAX_INSTRUCTIONS
@@ -119,18 +149,31 @@ open class OpcodesFilter(
119149 if (opcodes == null ) {
120150 return true // Match anything.
121151 }
122- return opcodes.contains(instruction.opcode) == true
152+ return opcodes.contains(instruction.opcode)
123153 }
124154}
125155
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+ */
126169class LiteralFilter (
127170 var literal : () -> Long ,
128171 opcodes : List <Opcode >? = null ,
129172 maxInstructionsBefore : Int = METHOD_MAX_INSTRUCTIONS ,
130173) : OpcodesFilter(opcodes, maxInstructionsBefore) {
131174
132175 /* *
133- * Constant long literal.
176+ * Integer/Long literal.
134177 */
135178 constructor (
136179 literal : Long ,
@@ -160,6 +203,19 @@ class LiteralFilter(
160203 }
161204}
162205
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+ */
163219class MethodFilter (
164220 /* *
165221 * Defining class of the method call. Matches using endsWith().
@@ -310,40 +366,42 @@ class MethodFilter (
310366 private val regex = Regex (""" ^(L[^;]+;)->([^(\s]+)\(([^)]*)\)(.+)$""" )
311367
312368 /* *
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.:
314370 * 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.
315373 */
316374 fun parseJvmMethodCall (
317375 methodSignature : String ,
318376 ) = parseJvmMethodCall(methodSignature, null , METHOD_MAX_INSTRUCTIONS )
319377
320378 /* *
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.:
322380 * Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;
323381 *
324- * Does not support obfuscated method names or parameter/return types
382+ * Does not support obfuscated method names or parameter/return types.
325383 */
326384 fun parseJvmMethodCall (
327385 methodSignature : String ,
328386 maxInstructionsBefore : Int
329387 ) = parseJvmMethodCall(methodSignature, null , maxInstructionsBefore)
330388
331389 /* *
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.:
333391 * Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;
334392 *
335- * Does not support obfuscated method names or parameter/return types
393+ * Does not support obfuscated method names or parameter/return types.
336394 */
337395 fun parseJvmMethodCall (
338396 methodSignature : String ,
339397 opcodes : List <Opcode >? ,
340398 ) = parseJvmMethodCall(methodSignature, opcodes, METHOD_MAX_INSTRUCTIONS )
341399
342400 /* *
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.:
344402 * Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;
345403 *
346- * Does not support obfuscated method names or parameter/return types
404+ * Does not support obfuscated method names or parameter/return types.
347405 */
348406 fun parseJvmMethodCall (
349407 methodSignature : String ,
@@ -415,14 +473,23 @@ class MethodFilter (
415473 }
416474}
417475
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+ */
418486class FieldFilter (
419487 /* *
420488 * Defining class of the field call. Matches using endsWith().
421489 *
422490 * For calls to a method in the same class, use 'this' as the defining class.
423491 * Note: 'this' does not work for fields found in superclasses.
424492 */
425-
426493 val definingClass : (() -> String )? = null ,
427494 /* *
428495 * Name of the field. Must be a full match of the field name.
0 commit comments