Refer to the article: zhuanlan.zhihu.com/p/307382854 zhuanlan.zhihu.com/p/43609710
Demo project: github.com/TokenYc/Lin… Demo has some built-in detectors for detection.
Lint is used to detect static code and resources and find places in them that do not conform to predefined rules.
Lint can be used to detect user privacy methods in the SDK. The biggest pitfall with custom Lint is that it can fail to implement Lint because of the different dependency versions in the project. Therefore, if you write your own version number, you should use the demo version number, and then change the version according to the project when the test works properly. Include:
- Lint – API version
CompileOnly com. Android. "tools. Lint: lint - API: 27.1.3"Copy the code
- build tool
The classpath "com. Android. Tools. Build: gradle: 4.1.3." "Copy the code
- Yes, you read that right.
Implementation 'androidx. Appcompat: appcompat: 1.2.0'Copy the code
End result: You can see which SDKS call privacy-related methods.
Custom Lint
If you’re new to Lint, it’s recommended to follow the steps before you understand them.
1. Create a Java Module
Add it in build.gradle of the Java Module
Dependencies {compileOnly "com. Android. Tools. Lint: lint - API: 27.1.3"}Copy the code
2. Create Detector and Issue
First, a Detector, which stands for a Detector. Inherit Detector and then implement ClassScanner. Just put the files in the Java Module you created. Why ClassScanner? There are actually several scanners available, but so far only one Scanner has been tested that can scan three SDKS. Directly to the code, here to detect the call to obtain IMEI method as an example (part of the method internal code has been omitted) :
package com.qianfanyun.lintlib.detector import com.android.tools.lint.detector.api.* import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.AbstractInsnNode import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.MethodInsnNode import org.objectweb.asm.tree.MethodNode class StealImeiDetector : Detector (), ClassScanner {/ * * * returns the ASM instructions * / override of the Detector for fun getApplicableAsmNodeTypes () : IntArray? {// We are interested in instructions related to method calls, Return intArrayOf(AbstractinsnNode. METHOD_INSN)} /** * Override fun checkInstruction(context: ClassContext, classNode: classNode, method: MethodNode, instruction: override fun checkInstruction(context: ClassContext, classNode: classNode, method: MethodNode, instruction: AbstractInsnNode) { } }Copy the code
Two methods are overridden:
- GetApplicableAsmNodeTypes said this approach, the scanner to scan what instructions. So what we’re going to return is Method, which is a Method instruction.
- The checkInstruction method provides a description of some methods to facilitate filtering.
Once you understand the overall structure, add the logic:
package com.qianfanyun.lintlib.detector import com.android.tools.lint.detector.api.* import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.AbstractInsnNode import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.MethodInsnNode import org.objectweb.asm.tree.MethodNode /** * @date on 2019-12-23 14:31 * @author ArcherYc * @mail [email protected] */ class StealImeiDetector : Detector (), ClassScanner {/ * * * returns the ASM instructions * / override of the Detector for fun getApplicableAsmNodeTypes () : IntArray? {// We are interested in instructions related to method calls, Return intArrayOf(AbstractinsnNode. METHOD_INSN)} /** * Override fun checkInstruction(context: ClassContext, classNode: classNode, method: MethodNode, instruction: override fun checkInstruction(context: ClassContext, classNode: classNode, method: MethodNode, instruction: AbstractInsnNode) { if (instruction.opcode ! = Opcodes.INVOKEVIRTUAL) { return } val callerMethodSig = classNode.name + "." + method.name + method.desc val MethodInsn = instruction as MethodInsnNode Call any methods in NfcAdapter will report abnormal if (methodInsn. Name = = "getDeviceId && methodInsn. The owner = =" android/telephony/TelephonyManager ") {val message = "SDK $callerMethodSig calls the" + "${methodInsn. Owner. SubstringAfterLast ('/')}. ${methodInsn. Name}, Be careful!" context.report(ISSUE, method, methodInsn, context.getLocation(methodInsn), message) } } }Copy the code
The logical code is easier to understand, comparing the name of the method with the class of the method to find the method we want to detect. Lint is then added to its detection report using the report method.
Then Issue. The Detector represents a Detector, and Issure represents a problem, which can be interpreted as a description of the Detector. Typically placed directly in the Detector as a static member. Create the method is relatively fixed, the specific parameter represents what, run again to know.
package com.qianfanyun.lintlib.detector import com.android.tools.lint.detector.api.* import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.AbstractInsnNode import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.MethodInsnNode import org.objectweb.asm.tree.MethodNode /** * @date on 2019-12-23 14:31 * @author ArcherYc * @mail [email protected] */ class StealImeiDetector : Detector(), ClassScanner {companion object {val ISSUE = issue.create ("StealImei",// problem Id "",// simple description of problem, Will be overridden by description of incoming report interface "",// detailed description of the problem category. CORRECTNESS,// Problem type 6,// Problem severity, 0~10, Serious Severity. The greater the Severity ERROR, the problem of / / / / the Detector and the Scope of the corresponding relation between Implementation (StealImeiDetector: : class. Java, Scope. ALL_CLASSES_AND_LIBRARIES))} / * * * returns the ASM instructions * / override of the Detector for fun getApplicableAsmNodeTypes () : IntArray? {// We are interested in instructions related to method calls, Return intArrayOf(AbstractinsnNode. METHOD_INSN)} /** * Override fun checkInstruction(context: ClassContext, classNode: classNode, method: MethodNode, instruction: override fun checkInstruction(context: ClassContext, classNode: classNode, method: MethodNode, instruction: AbstractInsnNode) { if (instruction.opcode ! = Opcodes.INVOKEVIRTUAL) { return } val callerMethodSig = classNode.name + "." + method.name + method.desc val MethodInsn = instruction as MethodInsnNode Call any methods in NfcAdapter will report abnormal if (methodInsn. Name = = "getDeviceId && methodInsn. The owner = =" android/telephony/TelephonyManager ") {val message = "SDK $callerMethodSig calls the" + "${methodInsn. Owner. SubstringAfterLast ('/')}. ${methodInsn. Name}, Be careful!" context.report(ISSUE, method, methodInsn, context.getLocation(methodInsn), message) } } }Copy the code
At this point, the most important part is done.
3. Create a Registry
(1) You need to register an Issue with Lint through a Registry. The code is very simple, the issues that need to be registered can be directly loaded into the list. API returns the fixed CURRENT_API.
package com.qianfanyun.lintlib
import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.detector.api.CURRENT_API
import com.android.tools.lint.detector.api.Issue
import com.qianfanyun.lintlib.detector.*
import java.util.*
class DangerIssueRegistry : IssueRegistry() {
override val issues: List<Issue>
get() {
return Arrays.asList(
StealImeiDetector.ISSUE,
)
}
override val api: Int
get() = CURRENT_API
}
Copy the code
(2) Add it to build.gradle in the Java Module
jar {
manifest {
attributes("Lint-Registry-v2": "com.qianfanyun.lintlib.DangerIssueRegistry")
}
}
Copy the code
4. Build. Gradle in your app
Build. Gradle in your app
dependencies {
lintChecks project(':lintlib')
}
Copy the code
5. Perform
Execute on the command line
gradle app:lintDebug
Copy the code
If you add Varint to your project, you need to add the corresponding name between lintDebug. The corresponding command becomes
gradle app:lintxxxxDebug
Copy the code
After execution, if all goes well, two links appear, one in HTML format and one in XML format.Click to check it out. HTML can be viewed in a browser. Something like thatIn this way, you can find code in third-party SDKS that involves privacy policies.