├── LICENSE ├── PermissionInfo_Pixel3.txt ├── README.md ├── lib ├── soot-4.2.1-jar-with-dependencies.jar ├── soot-infoflow-2.9.0.jar ├── soot-infoflow-android-2.10.0.jar └── trove4j-3.0.1.jar ├── pom.xml ├── sinks ├── CF_SINK_INFO.txt ├── DF_SINK_INFO.txt └── sink.txt └── src ├── .DS_Store ├── .gitignore └── main └── java ├── analyze ├── AnalyzeDatabase.java ├── AnalyzeNIO.java ├── AnalyzeService.java ├── FileUseAnalyze.java ├── Main.java └── MainAnalyze.java ├── cfg ├── CfgFactory.java └── Path.java ├── cg └── CallGraphUtils.java ├── component ├── EntryPointAnalyze.java ├── FragementCreater.java └── ResolveManifest.java ├── constant ├── ApkAndJavaConstants.java ├── CollectionsUsageDefinition.java ├── EntryPointsDefinition.java ├── FileUsageDefinition.java ├── IPCPointDefinition.java └── StrawPointsDefinition.java ├── dataflow ├── AbstractDataFlow.java ├── AccessPathTag.java ├── Analyze.java ├── BackwardDataFlow.java ├── CallSite.java ├── DBInfo.java ├── DataFlowEngine.java ├── DatabaseUseDetector.java ├── Event.java ├── EventQueue.java ├── ExportedConfigFileDetector.java ├── ExportedFileChecker.java ├── FileInfo.java ├── FileLoaderDetector.java ├── FileNameConstructor.java ├── FileType.java ├── FileUseDetector.java ├── ForwardDataFlow.java ├── Icc.java ├── Point.java ├── RuleChecker.java ├── SensitiveActionChecker.java ├── SpInfo.java ├── StrawDetector.java └── TaintWrapper.java ├── timeout └── TimeOutTask.java └── util ├── DirTraversal.java ├── Log.java ├── SootInit.java ├── StringConvertUtil.java └── StringUtil.java /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CLDroid 2 | 3 | CLDroid provides an end-to-end solution to statically detect cross-layer threats in Android apps (implemented based on Soot framework). 4 | 5 | Given an Android app, CLDroid first identifies the data pools (e.g., shared preference files and databases) that may be injected by external apps through exported components. Second, CLDroid employs data identifier-based analysis to track the data flow of data items that traverse through the target data pool. Third, CLDroid learns app-specific data use semantics and universally assesses their security risks from the perspectives of two attack vectors (i.e., data loading and data consuming). 6 | 7 | For more details, welcome to follow our paper: 8 | 9 | ``` 10 | Keke Lian, Lei Zhang, Guangliang Yang, Shuo Mao, Xinjie Wang, Yuan Zhang, and Min Yang. 2024. Component 11 | Security Ten Years Later: An Empirical Study of Cross-Layer Threats in Real-World Mobile Applications. Proc. 12 | ACM Softw. Eng. 1, FSE, Article 4 (July 2024) 13 | ``` -------------------------------------------------------------------------------- /lib/soot-4.2.1-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LianKee/CLDroid/3f37222c4dfc248b8992e06bbf9f1ecb6a653cd7/lib/soot-4.2.1-jar-with-dependencies.jar -------------------------------------------------------------------------------- /lib/soot-infoflow-2.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LianKee/CLDroid/3f37222c4dfc248b8992e06bbf9f1ecb6a653cd7/lib/soot-infoflow-2.9.0.jar -------------------------------------------------------------------------------- /lib/soot-infoflow-android-2.10.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LianKee/CLDroid/3f37222c4dfc248b8992e06bbf9f1ecb6a653cd7/lib/soot-infoflow-android-2.10.0.jar -------------------------------------------------------------------------------- /lib/trove4j-3.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LianKee/CLDroid/3f37222c4dfc248b8992e06bbf9f1ecb6a653cd7/lib/trove4j-3.0.1.jar -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | CLDroid 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-jar-plugin 15 | 2.4 16 | 17 | 18 | 19 | true 20 | analyze.Main 21 | 22 | 23 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-compiler-plugin 28 | 29 | 1.8 30 | 1.8 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | junit 41 | junit 42 | 4.13.2 43 | 44 | 45 | com.alibaba 46 | fastjson 47 | 1.2.28 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LianKee/CLDroid/3f37222c4dfc248b8992e06bbf9f1ecb6a653cd7/src/.DS_Store -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/java/analyze/AnalyzeDatabase.java: -------------------------------------------------------------------------------- 1 | package analyze; 2 | 3 | import cg.CallGraphUtils; 4 | import component.EntryPointAnalyze; 5 | import component.ResolveManifest; 6 | import constant.ApkAndJavaConstants; 7 | import constant.FileUsageDefinition; 8 | import dataflow.Analyze; 9 | import dataflow.CallSite; 10 | import dataflow.DatabaseUseDetector; 11 | import dataflow.Point; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import soot.*; 15 | import soot.jimple.*; 16 | import soot.jimple.infoflow.InfoflowConfiguration; 17 | import soot.jimple.infoflow.android.manifest.binary.AbstractBinaryAndroidComponent; 18 | import soot.jimple.toolkits.callgraph.CallGraph; 19 | import soot.jimple.toolkits.callgraph.Edge; 20 | import soot.jimple.toolkits.callgraph.ReachableMethods; 21 | import soot.jimple.toolkits.callgraph.TransitiveTargets; 22 | import soot.util.queue.QueueReader; 23 | import util.DirTraversal; 24 | import util.Log; 25 | import util.SootInit; 26 | 27 | import java.io.File; 28 | import java.io.IOException; 29 | import java.util.*; 30 | 31 | 32 | public class AnalyzeDatabase { 33 | 34 | public static final Logger logger = LoggerFactory.getLogger(AnalyzeDatabase.class); 35 | 36 | 37 | //直接使用数据库提供的API进行查询的 38 | public static final String DATA_BASE_QUERY_0 = "android.database.Cursor query(java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String)"; 39 | public static final String DATA_BASE_QUERY_1 = "android.database.Cursor query(java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String)"; 40 | public static final String DATA_BASE_QUERY_2 = "android.database.Cursor query(boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String)"; 41 | public static final String DATA_BASE_QUERY_3 = "android.database.Cursor query(boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String,android.os.CancellationSignal)"; 42 | public static final String DATA_BASE_QUERY_4 = "android.database.Cursor rawQuery(java.lang.String,java.lang.String[])"; 43 | public static final String DATA_BASE_QUERY_5 = "android.database.Cursor rawQuery(java.lang.String,java.lang.String[],android.os.CancellationSignal)"; 44 | public static final String DATA_BASE_QUERY_6 = "android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,java.lang.String,java.lang.String[],java.lang.String)"; 45 | public static final String DATA_BASE_QUERY_7 = "android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,java.lang.String,java.lang.String[],java.lang.String,android.os.CancellationSignal)"; 46 | public static final String DATA_BASE_QUERY_8 = "android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String)"; 47 | public static final String DATA_BASE_QUERY_9 = "android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String,android.os.CancellationSignal)"; 48 | public static final String DATA_BASE_QUERY_10 = "void execSQL(java.lang.String)"; 49 | public static final String DATA_BASE_QUERY_11 = "void execSQL(java.lang.String,java.lang.Object[])"; 50 | 51 | //使用ContentResolver提供的API进行查询的 52 | public static final String CONTENT_RESOLVER_QUERY_0 = "android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)"; 53 | public static final String CONTENT_RESOLVER_QUERY_1 = "android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,android.os.CancellationSignal)"; 54 | public static final String CONTENT_RESOLVER_QUERY_2 = "android.database.Cursor query(android.net.Uri,java.lang.String[],android.os.Bundle,android.os.CancellationSignal)"; 55 | 56 | public static final HashSet QUERY_API_SET = new HashSet<>(); 57 | 58 | static { 59 | QUERY_API_SET.add(DATA_BASE_QUERY_0); 60 | QUERY_API_SET.add(DATA_BASE_QUERY_1); 61 | QUERY_API_SET.add(DATA_BASE_QUERY_2); 62 | QUERY_API_SET.add(DATA_BASE_QUERY_3); 63 | QUERY_API_SET.add(DATA_BASE_QUERY_4); 64 | QUERY_API_SET.add(DATA_BASE_QUERY_5); 65 | QUERY_API_SET.add(DATA_BASE_QUERY_6); 66 | QUERY_API_SET.add(DATA_BASE_QUERY_7); 67 | QUERY_API_SET.add(DATA_BASE_QUERY_8); 68 | QUERY_API_SET.add(DATA_BASE_QUERY_9); 69 | QUERY_API_SET.add(DATA_BASE_QUERY_10); 70 | QUERY_API_SET.add(DATA_BASE_QUERY_11); 71 | 72 | QUERY_API_SET.add(CONTENT_RESOLVER_QUERY_0); 73 | QUERY_API_SET.add(CONTENT_RESOLVER_QUERY_1); 74 | QUERY_API_SET.add(CONTENT_RESOLVER_QUERY_2); 75 | } 76 | 77 | public static final DatabaseUseDetector detector = new DatabaseUseDetector(); 78 | 79 | 80 | 81 | 82 | public static void main(String[] args) throws IOException { 83 | String androidJar = "/home/ms/appAnalysis/AndroidHome"; 84 | // String androidJar = "/home/ms/appAnalysis/AndroidHome"; 85 | // String androidJar = ApkAndJavaConstants.androidJarPath; 86 | String apkDir = "/home/ms/targetApp"; 87 | // String apkDir = System.getProperty("user.dir") + File.separator + "App_test"; 88 | // String apkDir = "D:\\App\\AppIO\\app\\build\\intermediates\\apk\\debug"; 89 | String logName="./log/targetApp.txt"; 90 | // String logName="./social_data_use.txt"; 91 | Log.openLog(logName, false); 92 | 93 | new DirTraversal() { 94 | @Override 95 | public void work(File apkFile) throws Exception { 96 | if (!apkFile.getName().endsWith(".apk")) 97 | return; 98 | AnalyzeDatabase.logger.info("begin detect App:{}", apkFile); 99 | Log.write(Log.Mode.APP, apkFile.getName()); 100 | SootInit.initSootForAndroid(apkFile.getPath(), androidJar); 101 | 102 | HashSet components = ResolveManifest.getAllComponents(apkFile.getPath()); 103 | HashMap> component2MapEntryPoint = 104 | EntryPointAnalyze.getComponent2MapEntryPoint(components); 105 | 106 | List entry=new ArrayList<>(); 107 | for (String component : component2MapEntryPoint.keySet()) { 108 | HashSet entrypoints = component2MapEntryPoint.get(component); 109 | for(SootMethod method:entrypoints) { 110 | if(method==null) 111 | continue; 112 | entry.add(method); 113 | } 114 | } 115 | AnalyzeDatabase.logger.info("add all clinit method as entrypoint "); 116 | entry.addAll(EntryPointAnalyze.getClinitMethod()); 117 | AnalyzeDatabase.logger.info("entry point size: {}",entry.size()); 118 | AnalyzeDatabase.logger.info("build cg ..."); 119 | long start = System.nanoTime(); 120 | CallGraphUtils.buildCGbyCHA(entry); 121 | CallGraph callGraph = Scene.v().getCallGraph(); 122 | ReachableMethods reachableMethods = Scene.v().getReachableMethods(); 123 | long stop = System.nanoTime(); 124 | AnalyzeDatabase.logger.info("build cg coasts :{} seconds",(stop-start)/1E9); 125 | QueueReader listener = reachableMethods.listener(); 126 | HashSet riskyMethod=new HashSet<>(); 127 | while (listener.hasNext()){ 128 | SootMethod method = listener.next().method(); 129 | if(QUERY_API_SET.contains(method.getSubSignature())|| FileUsageDefinition.getSharedPreferencesWriteAPIList().contains(method.getSubSignature())|| 130 | FileUsageDefinition.getSharedPreferencesReadAPIList().contains(method.getSignature())){ 131 | //筛选出可能导致数据库泄漏的方法 132 | Iterator edgeIterator = callGraph.edgesInto(method); 133 | while (edgeIterator.hasNext()){ 134 | Edge next = edgeIterator.next(); 135 | riskyMethod.add(next.src()); 136 | } 137 | } 138 | } 139 | AnalyzeDatabase.logger.info("detect {} methods directly call the risky method in call graph",riskyMethod.size()); 140 | HashSet entrySubSignature=new HashSet<>(); 141 | for(SootMethod method:entry){ 142 | entrySubSignature.add(method.getSubSignature()); 143 | } 144 | HashMap> result=new HashMap<>(); 145 | detector.setMAX_DEPTH(2); 146 | int count=0; 147 | for(SootMethod method:riskyMethod){ 148 | HashSet> paths=new HashSet<>(); 149 | CallGraphUtils.findEntryMethod(method,entrySubSignature,"SubSignature",new ArrayList<>(),1,0,paths,false); 150 | for(List path:paths){ 151 | AnalyzeDatabase.logger.info("call stack depth: {}",path.size()); 152 | Collections.reverse(path); 153 | HashSet points = doAnalyze(method, path); 154 | if(points!=null){ 155 | if(!result.containsKey(method.getSignature())) 156 | result.put(method.getSignature(),new HashSet<>()); 157 | result.get(method.getSignature()).addAll(points); 158 | } 159 | count++; 160 | } 161 | } 162 | for(String entrypoint:result.keySet()){ 163 | writeAnalyzeResult(result.get(entrypoint),entrypoint); 164 | } 165 | AnalyzeDatabase.logger.info("total {} called!",count); 166 | } 167 | }.traverse(new File(apkDir)); 168 | Log.closeLog(); 169 | } 170 | 171 | public static HashSet doAnalyze(SootMethod method, List callStack) { 172 | if (method == null) 173 | return null; 174 | AnalyzeDatabase.logger.info("detect entrypoint {}", method.getSignature()); 175 | detector.setEntryPoint(method.getSignature()); 176 | detector.inter_forward(method, 0, callStack); 177 | return detector.getAnalysisResultOfEntry(method.getSignature()); 178 | } 179 | 180 | public static void writeAnalyzeResult(HashSet analysisResult, String entryPoint) { 181 | if (analysisResult == null) 182 | return; 183 | for (Point p : analysisResult) 184 | Log.write(Log.Mode.DATA_BASE_SINK, entryPoint, p.unit, p.method, p.type, p.otherMsg); 185 | } 186 | 187 | 188 | public static boolean isSystemClass(String clsName) { 189 | if (clsName.startsWith("java.") || clsName.startsWith("javax.")) 190 | return true; 191 | if (clsName.startsWith("android.") || clsName.startsWith("androidx.") || clsName.startsWith("com.google.") || clsName.startsWith("com.android.")) 192 | return true; 193 | if (clsName.startsWith("jdk")) 194 | return true; 195 | if (clsName.startsWith("sun.")) 196 | return true; 197 | if (clsName.startsWith("org.omg") || clsName.startsWith("org.w3c.dom")) 198 | return true; 199 | return false; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/main/java/analyze/AnalyzeNIO.java: -------------------------------------------------------------------------------- 1 | package analyze; 2 | 3 | import component.EntryPointAnalyze; 4 | import component.ResolveManifest; 5 | import constant.ApkAndJavaConstants; 6 | import dataflow.Point; 7 | import dataflow.StrawDetector; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import soot.*; 11 | import soot.jimple.AssignStmt; 12 | import soot.jimple.DefinitionStmt; 13 | import soot.jimple.InvokeExpr; 14 | import soot.jimple.InvokeStmt; 15 | import soot.jimple.infoflow.InfoflowConfiguration; 16 | import soot.jimple.infoflow.android.InfoflowAndroidConfiguration; 17 | import soot.jimple.infoflow.android.SetupApplication; 18 | import soot.jimple.infoflow.android.manifest.binary.AbstractBinaryAndroidComponent; 19 | import soot.jimple.toolkits.callgraph.CallGraph; 20 | import soot.jimple.toolkits.callgraph.Edge; 21 | import soot.jimple.toolkits.callgraph.ReachableMethods; 22 | import soot.util.queue.QueueReader; 23 | import util.DirTraversal; 24 | import util.Log; 25 | import util.SootInit; 26 | 27 | import java.io.*; 28 | import java.util.ArrayList; 29 | import java.util.HashMap; 30 | import java.util.HashSet; 31 | import java.util.Iterator; 32 | 33 | 34 | public class AnalyzeNIO { 35 | 36 | public static final Logger logger = LoggerFactory.getLogger(AnalyzeNIO.class); 37 | 38 | 39 | public static final String SERVER_SOCKET_API = ""; 40 | public static final String SERVER_SOCKET_CHANNEL_API_0 = ""; 41 | public static final String SERVER_SOCKET_CHANNEL_API_1 = ""; 42 | public static final String SERVER_SOCKET_CHANNEL_API_2 = ""; 43 | 44 | 45 | 46 | public static final String UDS_API=""; 47 | public static final String SERVER_API=""; 48 | public static final HashSet NIO_API_List = new HashSet<>(); 49 | 50 | 51 | public static final String DATA_READ_IN_PARAM_0=""; 52 | public static final String DATA_READ_IN_PARAM_1=""; 53 | public static final String DATA_READ_IN_PARAM_2=""; 54 | public static final String DATA_READ_IN_PARAM_3=""; 55 | public static final String DATA_READ_IN_PARAM_4=""; 56 | public static final String DATA_READ_IN_PARAM_5=""; 57 | public static final String DATA_READ_IN_PARAM_6=""; 58 | public static final String DATA_READ_IN_PARAM_7=""; 59 | public static final String DATA_READ_IN_PARAM_8=""; 60 | public static final String DATA_READ_IN_PARAM_9=""; 61 | public static final String DATA_READ_IN_PARAM_10=""; 62 | public static final String DATA_READ_IN_PARAM_11=""; 63 | public static final String DATA_READ_IN_PARAM_12=""; 64 | public static final String DATA_READ_IN_PARAM_13=""; 65 | public static final String DATA_READ_IN_PARAM_14=""; 66 | public static final String DATA_READ_IN_PARAM_15=""; 67 | public static final String DATA_READ_IN_PARAM_16=""; 68 | 69 | public static final HashSet DATA_READ_API=new HashSet<>(); 70 | 71 | static { 72 | NIO_API_List.add(UDS_API); 73 | NIO_API_List.add(SERVER_API); 74 | 75 | DATA_READ_API.add(DATA_READ_IN_PARAM_0); 76 | DATA_READ_API.add(DATA_READ_IN_PARAM_1); 77 | DATA_READ_API.add(DATA_READ_IN_PARAM_2); 78 | DATA_READ_API.add(DATA_READ_IN_PARAM_3); 79 | DATA_READ_API.add(DATA_READ_IN_PARAM_4); 80 | DATA_READ_API.add(DATA_READ_IN_PARAM_5); 81 | DATA_READ_API.add(DATA_READ_IN_PARAM_6); 82 | DATA_READ_API.add(DATA_READ_IN_PARAM_7); 83 | DATA_READ_API.add(DATA_READ_IN_PARAM_8); 84 | DATA_READ_API.add(DATA_READ_IN_PARAM_9); 85 | DATA_READ_API.add(DATA_READ_IN_PARAM_10); 86 | DATA_READ_API.add(DATA_READ_IN_PARAM_11); 87 | DATA_READ_API.add(DATA_READ_IN_PARAM_12); 88 | DATA_READ_API.add(DATA_READ_IN_PARAM_13); 89 | DATA_READ_API.add(DATA_READ_IN_PARAM_14); 90 | DATA_READ_API.add(DATA_READ_IN_PARAM_15); 91 | DATA_READ_API.add(DATA_READ_IN_PARAM_16); 92 | 93 | } 94 | 95 | public static final StrawDetector detector=new StrawDetector(); 96 | 97 | public static void main(String[] args) throws IOException { 98 | String androidJar = "/home/ms/appAnalysis/AndroidHome"; 99 | // String androidJar = ApkAndJavaConstants.androidJarPath; 100 | String apkDir = "/home/shared/download_apks/social"; 101 | // String apkDir = System.getProperty("user.dir")+File.separator+"detectedApp"; 102 | // String apkDir = "D:\\App\\AppIO\\app\\build\\intermediates\\apk\\debug"; 103 | 104 | // 105 | Log.openLog("/home/ms/appAnalysis/log/NIO.txt", false); 106 | // Log.openLog("./NIO.txt", false); 107 | 108 | detector.setTaintWrapper(DATA_READ_API); 109 | new DirTraversal() { 110 | @Override 111 | public void work(File apkFile) throws Exception { 112 | if (!apkFile.getName().endsWith(".apk")) 113 | return; 114 | AnalyzeNIO.logger.info("begin detect App:{}", apkFile); 115 | Log.write(Log.Mode.APP, apkFile.getName()); 116 | 117 | SootInit.initSootForAndroid(apkFile.getPath(), androidJar); 118 | boolean flag = false; 119 | for (String apiSignature : NIO_API_List) { 120 | try { 121 | SootMethod method = Scene.v().getMethod(apiSignature); 122 | flag=true; 123 | } catch (Exception e) { 124 | } 125 | } 126 | if (!flag) 127 | return; 128 | for (SootClass cls : Scene.v().getApplicationClasses()) { 129 | if(isSystemClass(cls.getName())) 130 | continue; 131 | for (SootMethod m : cls.getMethods()) { 132 | try { 133 | if (m.isJavaLibraryMethod()) 134 | continue; 135 | if (m.isAbstract()) 136 | continue; 137 | if (m.isPhantom()) 138 | continue; 139 | if (m.isNative()) 140 | continue; 141 | for (Unit u : m.retrieveActiveBody().getUnits()) { 142 | if (!(u instanceof AssignStmt)) 143 | continue; 144 | AssignStmt assignStmt = (AssignStmt) u; 145 | if (!(assignStmt.containsInvokeExpr())) 146 | continue; 147 | String signature = assignStmt.getInvokeExpr().getMethod().getSignature(); 148 | if (!NIO_API_List.contains(signature)) 149 | continue; 150 | doAnalysis(m, u); 151 | } 152 | } catch (Exception e) { 153 | } 154 | } 155 | } 156 | } 157 | }.traverse(new File(apkDir)); 158 | Log.closeLog(); 159 | } 160 | 161 | public static void doAnalysis(SootMethod caller,Unit callSite){ 162 | if(!(callSite instanceof DefinitionStmt)) 163 | return; 164 | DefinitionStmt definitionStmt = (DefinitionStmt) callSite; 165 | ValueBox valueBox = definitionStmt.getDefBoxes().get(0); 166 | //寻找数据流中的是数据流动 167 | detector.setEntryPoint(caller.getSignature()); 168 | AnalyzeNIO.logger.info("detect NIO in {}",caller.getSignature()); 169 | detector.run(caller,callSite,valueBox,0,0,new ArrayList<>()); 170 | HashSet result = detector.getAnalysisResultOfEntry(caller.getSignature()); 171 | if(result!=null) 172 | writeAnalyzeResult(result,caller.getSignature()); 173 | } 174 | 175 | public static void writeAnalyzeResult(HashSet analysisResult, String entryPoint) { 176 | if (analysisResult == null) 177 | return; 178 | for (Point p : analysisResult) 179 | Log.write(Log.Mode.SINK, entryPoint, p.unit, p.method, p.type); 180 | } 181 | 182 | public static boolean isSystemClass(String clsName){ 183 | if(clsName.startsWith("java.")||clsName.startsWith("javax.")) 184 | return true; 185 | if(clsName.startsWith("android.")||clsName.startsWith("androidx.")||clsName.startsWith("com.google.")||clsName.startsWith("com.android.")) 186 | return true; 187 | if(clsName.startsWith("jdk")) 188 | return true; 189 | if(clsName.startsWith("sun.")) 190 | return true; 191 | if(clsName.startsWith("org.omg")||clsName.startsWith("org.w3c.dom")) 192 | return true; 193 | return false; 194 | } 195 | 196 | 197 | 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/analyze/AnalyzeService.java: -------------------------------------------------------------------------------- 1 | package analyze; 2 | 3 | import dataflow.Point; 4 | import dataflow.StrawDetector; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import soot.*; 8 | import soot.options.Options; 9 | import util.Log; 10 | 11 | import java.io.BufferedReader; 12 | import java.io.FileReader; 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.HashMap; 16 | import java.util.HashSet; 17 | 18 | 19 | public class AnalyzeService { 20 | 21 | public static final Logger logger = LoggerFactory.getLogger(AnalyzeService.class); 22 | 23 | public static final String service_path = "/home/lkk/AndroidStudy_StaticAnalysis/DeviceData/Android11_Pixel3XL/InputData/Jimple"; 24 | public static final String entry_point_path = "/home/ms/appAnalysis/entrypoint.txt"; 25 | 26 | private static final StrawDetector detector = new StrawDetector(); 27 | 28 | private static String logFileName = "serviceLog.txt"; 29 | 30 | public static void initSoot() { 31 | 32 | G.reset(); 33 | 34 | 35 | 36 | Options.v().set_allow_phantom_refs(true); 37 | Options.v().set_whole_program(true); 38 | Options.v().set_src_prec(Options.src_prec_jimple); 39 | Options.v().set_process_dir(Collections.singletonList(service_path)); 40 | Options.v().set_output_format(Options.output_format_none); 41 | ArrayList excludeList = new ArrayList<>(); 42 | excludeList.add("java.*"); 43 | excludeList.add("sun.*"); 44 | Options.v().set_exclude(excludeList); 45 | Options.v().set_drop_bodies_after_load(false); 46 | Options.v().set_no_bodies_for_excluded(true); 47 | Scene.v().loadNecessaryClasses(); 48 | } 49 | 50 | public static void main(String[] args) { 51 | initSoot(); 52 | logger.info("detector begin running ..."); 53 | HashMap> entryFromText = getEntryFromText();//获取所有的系统服务 54 | Log.openLog(logFileName, true); 55 | ArrayList entrypointList = new ArrayList<>(); 56 | 57 | for (String service : entryFromText.keySet()) { 58 | for (String interfaceName : entryFromText.get(service)) { 59 | try { 60 | SootMethod method = Scene.v().getMethod(interfaceName); 61 | entrypointList.add(method); 62 | } catch (Exception e) { 63 | logger.info("the method don't exist"); 64 | } 65 | } 66 | } 67 | 68 | logger.info("begin building call graph ..."); 69 | Scene.v().setEntryPoints(entrypointList); 70 | long start = System.nanoTime(); 71 | PackManager.v().getPack("cg").apply(); 72 | long end = System.nanoTime(); 73 | logger.info("building call graph for {} entry points costs {} seconds", entrypointList.size(), (end - start) / 1e9); 74 | 75 | 76 | for (String service : entryFromText.keySet()) { 77 | 78 | logger.info("detect service {}", service); 79 | Log.write(Log.Mode.SERVICE, service); 80 | for (String interfaceName : entryFromText.get(service)) { 81 | SootMethod method = null; 82 | try { 83 | method = Scene.v().getMethod(interfaceName); 84 | Log.write(Log.Mode.INTERFACE, interfaceName); 85 | for (int i = 0; i < method.getParameterCount(); i++) { 86 | Type type = method.getParameterTypes().get(i); 87 | if(!isPrimativeType(type.toString())) { 88 | logger.info("detect interface {}", interfaceName); 89 | detector.setEntryPoint(interfaceName); 90 | detector.run(method, null, null, i, 0, new ArrayList<>()); 91 | } 92 | } 93 | HashSet analysisResultOfEntry = detector.getAnalysisResultOfEntry(interfaceName); 94 | writeAnalyzeResult(analysisResultOfEntry,interfaceName); 95 | } catch (Exception e) { 96 | logger.info("the method don't exist"); 97 | } 98 | } 99 | } 100 | Log.closeLog(); 101 | } 102 | 103 | public static HashMap> getEntryFromText() { 104 | HashMap> res = new HashMap<>(); 105 | 106 | try { 107 | FileReader fileReader = new FileReader(entry_point_path); 108 | BufferedReader reader = new BufferedReader(fileReader); 109 | String line = null; 110 | String service = null; 111 | HashSet serviceInterface = new HashSet<>(); 112 | while ((line = reader.readLine()) != null) { 113 | if (line.contains("系统服务")) { 114 | if (service != null) { 115 | res.put(service, (HashSet) serviceInterface.clone()); 116 | serviceInterface.clear(); 117 | } 118 | service = line.substring(6); 119 | } else { 120 | serviceInterface.add(line.substring(6)); 121 | } 122 | } 123 | } catch (Exception e) { 124 | e.printStackTrace(); 125 | } 126 | return res; 127 | } 128 | 129 | public static void writeAnalyzeResult(HashSet analysisResult, String entryPoint) { 130 | if (analysisResult == null) 131 | return; 132 | for (Point p : analysisResult) 133 | Log.write(Log.Mode.SINK, entryPoint, p.unit, p.method, p.type); 134 | } 135 | 136 | public static boolean isPrimativeType(String type){ 137 | if(type.equals("int")||type.equals("long")||type.equals("float")||type.equals("double")||type.equals("boolean")) 138 | return true; 139 | return false; 140 | } 141 | 142 | 143 | 144 | 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/analyze/FileUseAnalyze.java: -------------------------------------------------------------------------------- 1 | package analyze; 2 | 3 | import component.EntryPointAnalyze; 4 | import component.ResolveManifest; 5 | import constant.ApkAndJavaConstants; 6 | import dataflow.FileUseDetector; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import soot.Scene; 10 | import soot.SootMethod; 11 | import soot.Unit; 12 | import soot.jimple.infoflow.android.manifest.ProcessManifest; 13 | import soot.jimple.infoflow.android.manifest.binary.AbstractBinaryAndroidComponent; 14 | import util.DirTraversal; 15 | import util.Log; 16 | import util.SootInit; 17 | 18 | import java.io.File; 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.HashSet; 22 | 23 | 24 | public class FileUseAnalyze { 25 | 26 | // private static String apkPath = ApkAndJavaConstants.apkDir; 27 | private static String apkPath = System.getProperty("user.dir")+File.separator+"targetApp"; 28 | // private static String apkPath ="/home/shared/download_apks"; 29 | // private static String androidJar = ApkAndJavaConstants.androidJarPath; 30 | private static String androidJar = "/home/ms/appAnalysis/AndroidHome"; 31 | 32 | public static final Logger logger= LoggerFactory.getLogger(FileUseAnalyze.class); 33 | 34 | public static final FileUseDetector detector=new FileUseDetector(); 35 | 36 | public static final String logRootPath="/home/ms/appAnalysis/log/appUseFileInfo"; 37 | 38 | 39 | public static void main(String[] args) { 40 | // apkPath=args[0]; 41 | // androidJar=args[1]; 42 | File apkDir=new File(apkPath); 43 | Log.openLog("fileNameRebuild.txt",false); 44 | new DirTraversal(){ 45 | @Override 46 | public void work(File apkFile) throws Exception { 47 | if(!apkFile.getName().endsWith(".apk")) 48 | return; 49 | FileUseAnalyze.logger.info("开始检测App:{}",apkFile); 50 | Log.write(Log.Mode.APP,apkFile.getName()); 51 | HashSet components = ResolveManifest.getAllComponents(apkFile.getPath()); 52 | SootInit.initSootForAndroid(apkFile.getPath(), androidJar); 53 | HashMap> component2MapEntryPoint = 54 | EntryPointAnalyze.getComponent2MapEntryPoint(components); 55 | Scene.v().getOrMakeFastHierarchy(); 56 | String packageName = ResolveManifest.getPackageName(apkFile); 57 | detector.setPackageName(packageName); 58 | for (String component : component2MapEntryPoint.keySet()) { 59 | FileUseAnalyze.logger.info("开始检测Component:{}",component); 60 | HashSet entrypoints = component2MapEntryPoint.get(component); 61 | for(SootMethod method:entrypoints) { 62 | if(method==null) 63 | continue; 64 | FileUseAnalyze.logger.info("开始检测入entry point:{}",method.getSignature()); 65 | doAnalyze(method); 66 | } 67 | 68 | } 69 | FileUseAnalyze.logger.info("结束了"); 70 | detector.writeLog(); 71 | Scene.v().releaseFastHierarchy(); 72 | } 73 | }.traverse(apkDir); 74 | Log.closeLog(); 75 | } 76 | 77 | public void analyze(String apkPath){ 78 | if(!apkPath.endsWith(".apk")) 79 | return; 80 | File apkFile = new File(apkPath); 81 | FileUseAnalyze.logger.info("开始检测App:{}",apkFile); 82 | HashSet components = ResolveManifest.getAllComponents(apkPath); 83 | SootInit.initSootForAndroid(apkFile.getPath(), androidJar); 84 | HashMap> component2MapEntryPoint = 85 | EntryPointAnalyze.getComponent2MapEntryPoint(components); 86 | Scene.v().getOrMakeFastHierarchy(); 87 | String packageName = ResolveManifest.getPackageName(apkFile); 88 | detector.setPackageName(packageName); 89 | Log.openLog(String.format("%s/%s.txt",logRootPath,packageName),false); 90 | Log.write(Log.Mode.APP,apkFile.getName()); 91 | for (String component : component2MapEntryPoint.keySet()) { 92 | // FileUseAnalyze.logger.info("开始检测Component:{}",component); 93 | HashSet entrypoints = component2MapEntryPoint.get(component); 94 | for(SootMethod method:entrypoints) { 95 | if(method==null) 96 | continue; 97 | // FileUseAnalyze.logger.info("开始检测入entry point:{}",method.getSignature()); 98 | doAnalyze(method); 99 | } 100 | 101 | 102 | 103 | } 104 | // FileUseAnalyze.logger.info("结束了"); 105 | detector.writeLog(); 106 | Scene.v().releaseFastHierarchy(); 107 | Log.closeLog(); 108 | } 109 | 110 | public static void doAnalyze(SootMethod method){ 111 | if(method==null) 112 | return; 113 | detector.inter_forward(method,0,new ArrayList<>()); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/analyze/Main.java: -------------------------------------------------------------------------------- 1 | package analyze; 2 | 3 | import cg.CallGraphUtils; 4 | import component.EntryPointAnalyze; 5 | import component.ResolveManifest; 6 | import constant.ApkAndJavaConstants; 7 | import constant.EntryPointsDefinition; 8 | import constant.StrawPointsDefinition; 9 | import dataflow.Point; 10 | import dataflow.StrawDetector; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.xmlpull.v1.XmlPullParserException; 14 | import soot.*; 15 | import soot.jimple.AssignStmt; 16 | import soot.jimple.infoflow.android.manifest.binary.AbstractBinaryAndroidComponent; 17 | import util.DirTraversal; 18 | import util.Log; 19 | import util.SootInit; 20 | 21 | import java.io.*; 22 | import java.util.*; 23 | 24 | 25 | public class Main { 26 | 27 | private static String apkDir = ApkAndJavaConstants.apkDir; 28 | private static String androidJar = ApkAndJavaConstants.androidJarPath; 29 | private static final Logger logger = LoggerFactory.getLogger(Main.class); 30 | 31 | private static final List lifeCycleMethodList = EntryPointsDefinition.getAllLifeCycleMethodList(); 32 | 33 | private static final String GET_INTENT = "android.content.Intent getIntent()"; 34 | 35 | private static final StrawDetector analyzer=new StrawDetector(); 36 | 37 | private static String logFileName = "Log.txt"; 38 | 39 | private static String permissionInfoPath="./PermissionInfo.txt"; 40 | 41 | private static int max = 100000; 42 | 43 | private static HashSet filterAppSet=new HashSet<>(); 44 | 45 | private static String appFilterFile="/home/ms/appAnalysis/filterApp.txt"; 46 | 47 | 48 | public static void main(String[] args) throws IOException, XmlPullParserException { 49 | 50 | // apkDir = args[0]; 51 | apkDir = System.getProperty("user.dir")+File.separator+"App_test"; 52 | // androidJar = args[1]; 53 | androidJar = ApkAndJavaConstants.androidJarPath; 54 | // logFileName = args[2]; 55 | logFileName = "./detectData"; 56 | // max = Integer.parseInt(args[3]); 57 | max = 100; 58 | // permissionInfoPath=args[4]; 59 | permissionInfoPath=System.getProperty("user.dir")+File.separator+"PermissionInfo.txt"; 60 | 61 | logger.info("the straw detector begin running ..."); 62 | 63 | // appFilterFile=args[5]; 64 | appFilterFile="./filterApp.txt"; 65 | 66 | 67 | Log.openLog(logFileName, false); 68 | File apkDir = new File(Main.apkDir); 69 | 70 | HashSet detectedAppInfo = getDetectedAppInfo(logFileName); 71 | logger.info("retrieve permission info ... "); 72 | HashMap permissionInfo = getDevicePermissionInfo(new File(permissionInfoPath)); 73 | logger.info("init filter app set ..."); 74 | initFilterApp(); 75 | 76 | final int[] counter = {0}; 77 | 78 | new DirTraversal(){ 79 | @Override 80 | public void work(File apkFile) throws Exception { 81 | 82 | if (counter[0] > max) 83 | return; 84 | if(detectedAppInfo.contains(apkFile.getName())) 85 | return; 86 | if(!apkFile.getName().endsWith(".apk")) 87 | return; 88 | if(!filterAppSet.isEmpty()&&!filterAppSet.contains(apkFile.getName().split(".apk")[0].trim())) 89 | return; 90 | 91 | counter[0]++; 92 | 93 | Log.write(Log.Mode.APP, apkFile.getName()); 94 | String versionName = ResolveManifest.getVersionName(apkFile); 95 | Log.write(Log.Mode.VERSION,versionName); 96 | Log.write(Log.Mode.PATH,apkFile.getPath()); 97 | 98 | Main.logger.info("straw detector begin detect {}", apkFile.getName()); 99 | 100 | Main.logger.info("straw detector begin detecting all exported components "); 101 | HashSet components = ResolveManifest.getReachableComponents(apkFile.getPath(),permissionInfo,new HashSet<>()); 102 | SootInit.initSootForAndroid(apkFile.getPath(), androidJar); 103 | Main.logger.info("begin detecting app: {} ,there are {} exported components in it", apkFile.getName(), components.size()); 104 | HashMap> component2MapEntryPoint = 105 | EntryPointAnalyze.getComponent2MapEntryPoint(components); 106 | // List entryPoints=new ArrayList<>(); 107 | // for (String component : component2MapEntryPoint.keySet()) { 108 | // HashSet entrypoints = component2MapEntryPoint.get(component); 109 | // for(SootMethod m:entrypoints){ 110 | // if(m==null) 111 | // continue; 112 | // entryPoints.add(m); 113 | // } 114 | // } 115 | // Scene.v().setEntryPoints(entryPoints); 116 | // try { 117 | // Main.logger.info("begin build cg ..."); 118 | // PackManager.v().getPack("cg").apply(); 119 | // }catch (Exception e){ 120 | // return; 121 | // } 122 | Main.logger.info("begin data flow analyze"); 123 | for (String component : component2MapEntryPoint.keySet()) { 124 | HashSet entrypoints = component2MapEntryPoint.get(component); 125 | Main.logger.info("begin detect component: {} there are {} entry points that may be exploited", component, entrypoints.size()); 126 | doAnalyze(entrypoints, component); 127 | } 128 | } 129 | }.traverse(apkDir); 130 | Log.closeLog(); 131 | 132 | } 133 | 134 | private static void doAnalyze(HashSet entrypoints, String component) { 135 | 136 | boolean flag = false; 137 | 138 | for (SootMethod entryPoint : entrypoints) { 139 | if (entryPoint == null) 140 | continue; 141 | analyzer.setEntryPoint(entryPoint.getSignature()); 142 | logger.info("begin detecting {}", entryPoint.getSignature()); 143 | if (lifeCycleMethodList.contains(entryPoint.getSubSignature())) { 144 | int index = 0; 145 | List parameterTypes = entryPoint.getParameterTypes(); 146 | for (; index < parameterTypes.size(); index++) { 147 | if (parameterTypes.get(index).toString().equals("android.content.Intent") || parameterTypes.get(index).toString().equals("android.content.ContentValues")) 148 | break; 149 | } 150 | if (index != parameterTypes.size()) { 151 | logger.info("run data flow analyze ...."); 152 | analyzer.run(entryPoint,null,null,index,0,new ArrayList<>()); 153 | } 154 | // 分析代码中是否包含getIntent调用 155 | HashSet invokeUnits = getGetIntentInvokeUnit(entryPoint); 156 | for (Unit invokeUnit : invokeUnits) { 157 | logger.info("run data flow analyze ..."); 158 | analyzer.run(entryPoint,invokeUnit,invokeUnit.getDefBoxes().get(0),0,0,new ArrayList<>() ); 159 | } 160 | 161 | } else { 162 | logger.info("this is an onBind service {}", entryPoint.getSignature()); 163 | int parameterCount = entryPoint.getParameterCount(); 164 | for (int index = 0; index < parameterCount; index++) { 165 | logger.info("run data flow analyze ...."); 166 | analyzer.run(entryPoint,null,null,index,0,new ArrayList<>()); 167 | } 168 | } 169 | HashSet analysisResultOfEntry = analyzer.getAnalysisResultOfEntry(entryPoint.getSignature()); 170 | if(analysisResultOfEntry!=null){ 171 | writeAnalyzeResult(analysisResultOfEntry,entryPoint.getSignature()); 172 | flag=true; 173 | } 174 | } 175 | if (flag) { 176 | Log.write(Log.Mode.COMPONENT, component); 177 | } 178 | 179 | } 180 | 181 | public static void writeAnalyzeResult(HashSet analysisResult, String entryPoint) { 182 | if (analysisResult == null) 183 | return; 184 | for (Point p : analysisResult) 185 | Log.write(Log.Mode.SINK, entryPoint, p.unit, p.method, p.type,p.otherMsg); 186 | } 187 | 188 | private static boolean reachableAnalyze(SootMethod method) { 189 | 190 | boolean entry_usable = false; 191 | if (!lifeCycleMethodList.contains(method.getSubSignature())) { 192 | //如果是bounded Service 193 | entry_usable = true; 194 | } else { 195 | for (Type paramType : method.getParameterTypes()) { 196 | if (paramType.toString().equals("android.content.Intent") || paramType.toString().equals("android.content.ContentValues")) { 197 | entry_usable = true; 198 | break; 199 | } 200 | } 201 | if (!entry_usable) { 202 | if (CallGraphUtils.isMethodReachable2Target(method, "android.content.Intent getIntent()")) 203 | entry_usable = true; 204 | } 205 | 206 | } 207 | if (entry_usable) { 208 | if (CallGraphUtils.isMethodReachable2Target(method, StrawPointsDefinition.getAllInsertMethodList()))//这个方法并不通用 209 | return true; 210 | logger.info("no straw point is found in the call chain"); 211 | return false; 212 | 213 | } else { 214 | logger.info("no getIntent is called and no parameter is usable"); 215 | return false; 216 | } 217 | } 218 | 219 | public static HashSet getGetIntentInvokeUnit(SootMethod method) { 220 | HashSet res = new HashSet<>(); 221 | for (Unit unit : method.retrieveActiveBody().getUnits()) { 222 | if (unit instanceof AssignStmt) { 223 | AssignStmt assignStmt = (AssignStmt) unit; 224 | if (assignStmt.containsInvokeExpr()) { 225 | String subSignature = assignStmt.getInvokeExpr().getMethod().getSubSignature(); 226 | if (subSignature.equals(GET_INTENT)) { 227 | res.add(unit); 228 | } 229 | } 230 | } 231 | } 232 | return res; 233 | } 234 | 235 | public static HashSet getDetectedAppInfo(String fileName){ 236 | HashSet res=new HashSet<>(); 237 | try { 238 | FileReader fileReader = new FileReader(fileName); 239 | BufferedReader reader = new BufferedReader(fileReader); 240 | String line=null; 241 | while ((line=reader.readLine())!=null){ 242 | if(line.contains("APP:")){ 243 | String[] split = line.split(":"); 244 | res.add(split[1].substring(1)); 245 | } 246 | } 247 | }catch (Exception e){ 248 | e.printStackTrace(); 249 | } 250 | return res; 251 | } 252 | 253 | public static HashMap getDevicePermissionInfo(File permissionFile){ 254 | HashMap res=new HashMap<>(); 255 | try { 256 | FileInputStream in = new FileInputStream(permissionFile); 257 | BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 258 | String line=null; 259 | String permission=null; 260 | String protectLevel=null; 261 | while ((line=reader.readLine())!=null){ 262 | if(line.contains("permission:")){ 263 | permission=line.split(":")[1]; 264 | } 265 | if(line.contains("protectionLevel:")){ 266 | protectLevel=line.split(":")[1]; 267 | if(protectLevel.contains("normal")) { 268 | res.put(permission, 0); 269 | }else if(protectLevel.contains("dangerous")){ 270 | res.put(permission,1); 271 | }else if(protectLevel.contains("signature")) { 272 | res.put(permission, 2); 273 | }else { 274 | res.put(permission,3); 275 | } 276 | } 277 | } 278 | return res; 279 | }catch (Exception e){ 280 | return res; 281 | } 282 | } 283 | 284 | public static void initFilterApp(){ 285 | try { 286 | File file = new File(appFilterFile); 287 | FileReader reader = new FileReader(file); 288 | BufferedReader bufferedReader = new BufferedReader(reader); 289 | String line=null; 290 | while ((line=bufferedReader.readLine())!=null){ 291 | filterAppSet.add(line.trim()); 292 | } 293 | }catch (Exception e){ 294 | e.printStackTrace(); 295 | } 296 | } 297 | 298 | 299 | } 300 | -------------------------------------------------------------------------------- /src/main/java/analyze/MainAnalyze.java: -------------------------------------------------------------------------------- 1 | package analyze; 2 | 3 | import constant.ApkAndJavaConstants; 4 | import dataflow.ExportedConfigFileDetector; 5 | import org.xmlpull.v1.XmlPullParserException; 6 | import soot.Scene; 7 | import soot.SootClass; 8 | import soot.jimple.infoflow.android.axml.AXmlNode; 9 | import soot.jimple.infoflow.android.manifest.IAndroidComponent; 10 | import soot.jimple.infoflow.android.manifest.ProcessManifest; 11 | import soot.jimple.infoflow.android.manifest.binary.BinaryManifestActivity; 12 | import soot.jimple.infoflow.android.manifest.binary.BinaryManifestBroadcastReceiver; 13 | import soot.jimple.infoflow.android.manifest.binary.BinaryManifestContentProvider; 14 | import soot.jimple.infoflow.android.manifest.binary.BinaryManifestService; 15 | import util.SootInit; 16 | 17 | import java.io.*; 18 | import java.util.HashMap; 19 | import java.util.HashSet; 20 | import java.util.List; 21 | import java.util.Set; 22 | 23 | 24 | public class MainAnalyze { 25 | 26 | 27 | public static String apkDir=""; 28 | 29 | public static String androidJar=""; 30 | 31 | public static String logFileName=""; 32 | 33 | public static String permissionInfoFile=""; 34 | 35 | public static String filterAppInfo=""; 36 | 37 | public static String sinkInfo=""; 38 | 39 | public static String dataSavedDir=""; 40 | 41 | public static String systemActionFile=""; 42 | 43 | public static void main(String[] args) throws Exception { 44 | 45 | // apkDir=System.getProperty("user.dir")+ File.separator+"app"; 46 | apkDir="/home/shared/download_apks"; 47 | 48 | // androidJar= ApkAndJavaConstants.androidJarPath; 49 | androidJar= "/home/ms/appAnalysis/AndroidHome"; 50 | // logFileName="Log.txt"; 51 | logFileName="log7.txt"; 52 | 53 | // logFileName="myLog.txt"; 54 | // 55 | // permissionInfoFile=System.getProperty("user.dir")+ File.separator+"PermissionInfo.txt"; 56 | permissionInfoFile="/home/ms/ICFinder/PermissionInfo_Pixel3.txt"; 57 | 58 | // filterAppInfo="./filterApp.txt"; 59 | // sinkInfo=System.getProperty("user.dir")+File.separator+"sinks"+File.separator+"sink.txt"; 60 | sinkInfo="/home/ms/ICFinder/sinks/sink.txt"; 61 | 62 | dataSavedDir=System.getProperty("user.dir")+File.separator+"analyze_result"; 63 | 64 | // systemActionFile=System.getProperty("user.dir")+File.separator+"SystemProtectedBroadcast_Pixel3.txt"; 65 | systemActionFile="/home/ms/ICFinder/SystemProtectedBroadcast_Pixel3.txt"; 66 | 67 | // 68 | 69 | ExportedConfigFileDetector detector = new ExportedConfigFileDetector(apkDir, androidJar, logFileName, permissionInfoFile,systemActionFile,sinkInfo,dataSavedDir); 70 | detector.cal_exportComponentNumber(); 71 | // detector.analyze(); 72 | // detector.setTimeout(10000); 73 | // detector.start(); 74 | 75 | } 76 | 77 | 78 | public static void runAnalyze(String apkPath){ 79 | 80 | // apkDir="/home/shared/download_apks"; 81 | apkDir="/home/ms/target"; 82 | androidJar= "/home/ms/appAnalysis/AndroidHome"; 83 | logFileName="log7.txt"; 84 | permissionInfoFile="/home/ms/ICFinder/PermissionInfo_Pixel3.txt"; 85 | filterAppInfo="./filterApp.txt"; 86 | sinkInfo="/home/ms/ICFinder/sinks/sink.txt"; 87 | dataSavedDir="/home/ms/ICFinder/analyze_result/data7"; 88 | systemActionFile="/home/ms/ICFinder/SystemProtectedBroadcast_Pixel3XL.txt"; 89 | 90 | ExportedConfigFileDetector detector = new ExportedConfigFileDetector(apkDir, androidJar, logFileName, permissionInfoFile, systemActionFile,sinkInfo,dataSavedDir); 91 | detector.run(apkPath); 92 | } 93 | 94 | public static void analyze(String apkPath){ 95 | androidJar= "/home/ms/appAnalysis/AndroidHome"; 96 | SootInit.initSootForAndroid(apkPath,androidJar); 97 | System.out.println("开始分析:"+apkPath); 98 | 99 | boolean flag=false; 100 | for(SootClass cls:Scene.v().getClasses()) 101 | if(cls.getName().contains("com.alibaba.fastjson")){ 102 | flag=true; 103 | break; 104 | } 105 | if(flag){ 106 | try { 107 | FileWriter fileWriter = new FileWriter("/home/ms/ICFinder/fastJosn.txt",true); 108 | BufferedWriter writer = new BufferedWriter(fileWriter); 109 | writer.write(apkPath+'\n'); 110 | writer.close(); 111 | }catch (Exception e){ 112 | e.printStackTrace(); 113 | } 114 | 115 | } 116 | } 117 | 118 | //获取设备中的权限信息 119 | public static HashMap getDevicePermissionInfo(File permissionFile) { 120 | HashMap res = new HashMap<>(); 121 | try { 122 | FileInputStream in = new FileInputStream(permissionFile); 123 | BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 124 | String line = null; 125 | String permission = null; 126 | String protectLevel = null; 127 | while ((line = reader.readLine()) != null) { 128 | if (line.contains("permission:")) { 129 | permission = line.split(":")[1]; 130 | } 131 | if (line.contains("protectionLevel:")) { 132 | protectLevel = line.split(":")[1]; 133 | if (protectLevel.contains("normal")) { 134 | res.put(permission, 0); 135 | } else if (protectLevel.contains("dangerous")) { 136 | res.put(permission, 1); 137 | } else if (protectLevel.contains("signature")) { 138 | res.put(permission, 2); 139 | } else { 140 | res.put(permission, 3); 141 | } 142 | } 143 | } 144 | return res; 145 | } catch (Exception e) { 146 | return res; 147 | } 148 | } 149 | 150 | 151 | // 对于给定应用的指定组件统计他们是不是存在权限申明了permission但是permission是normal的级别 152 | 153 | 154 | public static void analyze(String apkPath, HashSet components) throws XmlPullParserException, IOException { 155 | //我们需要获得设备的权限列表 156 | permissionInfoFile="/home/ms/ICFinder/PermissionInfo_Pixel3.txt"; 157 | System.out.println("开始分析应用:"+apkPath); 158 | HashMap devicePermissionInfo = getDevicePermissionInfo(new File(permissionInfoFile)); 159 | 160 | ProcessManifest manifest = new ProcessManifest(apkPath); 161 | HashSet res=new HashSet<>(); 162 | 163 | //获取应用种自定义权限和他保护等级的映射 164 | for(AXmlNode permission:manifest.getManifest().getChildrenWithTag("permission")) { 165 | if(permission.getAttribute("protectionLevel") == null) devicePermissionInfo.put(permission.getAttribute("name").getValue().toString(), 0); 166 | else { 167 | devicePermissionInfo.put(permission.getAttribute("name").getValue().toString(),(Integer) permission.getAttribute("protectionLevel").getValue()); 168 | } 169 | } 170 | 171 | //开始遍历组件 172 | //判断这个组件是不是我们要找的组件,如果是我们看是否申明了权限,如果申明了,看他的权限是不不是normal级别的 173 | for(BinaryManifestService service: manifest.getServices()){ 174 | AXmlNode aXmlNode=service.getAXmlNode(); 175 | String componentName = aXmlNode.getAttribute("name").getValue().toString(); 176 | if(!components.contains(componentName)) 177 | continue; 178 | System.out.println("检测到组件"); 179 | if(!aXmlNode.hasAttribute("permission")) 180 | continue; 181 | String permission = aXmlNode.getAttribute("permission").getValue().toString(); 182 | System.out.println("权限是:"+permission); 183 | if(devicePermissionInfo.get(permission)==0){ 184 | //如果应用中的权限未定义或者该权限的级别只为normal,那么记录下该应用以及组件 185 | res.add(componentName); 186 | } 187 | } 188 | for(BinaryManifestActivity activity: manifest.getActivities()){ 189 | AXmlNode aXmlNode=activity.getAXmlNode(); 190 | String componentName = aXmlNode.getAttribute("name").getValue().toString(); 191 | if(!components.contains(componentName)) 192 | continue; 193 | System.out.println("检测到组件"); 194 | if(!aXmlNode.hasAttribute("permission")) 195 | continue; 196 | String permission = aXmlNode.getAttribute("permission").getValue().toString(); 197 | System.out.println("权限是:"+permission); 198 | if(devicePermissionInfo.get(permission)==0){ 199 | //如果应用中的权限未定义或者该权限的级别只为normal,那么记录下该应用以及组件 200 | res.add(componentName); 201 | } 202 | } 203 | for(BinaryManifestBroadcastReceiver receiver: manifest.getBroadcastReceivers()){ 204 | AXmlNode aXmlNode=receiver.getAXmlNode(); 205 | String componentName = aXmlNode.getAttribute("name").getValue().toString(); 206 | if(!components.contains(componentName)) 207 | continue; 208 | System.out.println("检测到组件"); 209 | if(!aXmlNode.hasAttribute("permission")) 210 | continue; 211 | String permission = aXmlNode.getAttribute("permission").getValue().toString(); 212 | System.out.println("权限是:"+permission); 213 | 214 | if(devicePermissionInfo.get(permission)==0){ 215 | //如果应用中的权限未定义或者该权限的级别只为normal,那么记录下该应用以及组件 216 | res.add(componentName); 217 | } 218 | } 219 | for(BinaryManifestContentProvider provider: manifest.getContentProviders()){ 220 | AXmlNode aXmlNode=provider.getAXmlNode(); 221 | //provider这里我们只关心它的write permission 222 | String componentName = aXmlNode.getAttribute("name").getValue().toString(); 223 | if(!components.contains(componentName)) 224 | continue; 225 | System.out.println("检测到组件"); 226 | 227 | if(!aXmlNode.hasAttribute("writePermission")) 228 | continue; 229 | String permission = aXmlNode.getAttribute("writePermission").getValue().toString(); 230 | System.out.println("权限是:"+permission); 231 | 232 | if(devicePermissionInfo.get(permission)==0){ 233 | //如果应用中的权限未定义或者该权限的级别只为normal,那么记录下该应用以及组件 234 | res.add(componentName); 235 | } 236 | } 237 | 238 | //我们根据分析结果进行统计 239 | if(res.isEmpty()) 240 | return; 241 | System.out.println("检测到使用权限过低的组件"); 242 | try{ 243 | BufferedWriter writer = new BufferedWriter(new FileWriter("/home/ms/ICFinder/weakPermissionAppAndConponents", true)); 244 | writer.write("App:"+apkPath+'\n'); 245 | for(String component : res){ 246 | writer.write("COMPONENT:"+component+'\n'); 247 | } 248 | writer.close(); 249 | 250 | }catch (Exception e){ 251 | e.printStackTrace(); 252 | } 253 | 254 | } 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | } 263 | -------------------------------------------------------------------------------- /src/main/java/cfg/CfgFactory.java: -------------------------------------------------------------------------------- 1 | package cfg; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import soot.Body; 6 | import soot.SootMethod; 7 | import soot.Unit; 8 | import soot.toolkits.graph.BriefUnitGraph; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.HashSet; 13 | import java.util.List; 14 | 15 | 16 | public class CfgFactory { 17 | 18 | private static final Logger logger = LoggerFactory.getLogger(CfgFactory.class); 19 | 20 | private static CfgFactory cfgFactory = null; 21 | private HashMap methodMapCfg = new HashMap<>(); 22 | 23 | private HashMap> methodMapPath=new HashMap<>(); 24 | 25 | private CfgFactory() { 26 | } 27 | 28 | public static CfgFactory getInstance() { 29 | if (cfgFactory == null) 30 | cfgFactory = new CfgFactory(); 31 | return cfgFactory; 32 | } 33 | 34 | 35 | public BriefUnitGraph getCFG(SootMethod sootMethod) { 36 | if(methodMapCfg.keySet().contains(sootMethod)) 37 | return methodMapCfg.get(sootMethod); 38 | if(!sootMethod.isConcrete()) 39 | return null; 40 | Body body = sootMethod.retrieveActiveBody(); 41 | if(body==null){ 42 | logger.warn("can't get the body of {}",sootMethod.getSignature()); 43 | return null; 44 | } 45 | BriefUnitGraph graph = new BriefUnitGraph(body); 46 | methodMapCfg.put(sootMethod,graph); 47 | return graph; 48 | } 49 | 50 | 51 | 52 | public void buildPaths(SootMethod method,Unit unit,BriefUnitGraph graph,HashSet visit,Path path){ 53 | 54 | Path newPath = new Path(); 55 | newPath.addAll(path); 56 | newPath.add(unit); 57 | 58 | HashSet newVisit=new HashSet<>(); 59 | newVisit.addAll(visit); 60 | newVisit.add(unit); 61 | 62 | boolean f=true; 63 | for(Unit succor:graph.getSuccsOf(unit)){ 64 | if(!newVisit.contains(succor)){ 65 | f=false; 66 | buildPaths(method,succor,graph,newVisit,newPath); 67 | } 68 | } 69 | 70 | if(f){ 71 | if(!methodMapPath.containsKey(method)) 72 | methodMapPath.put(method,new HashSet<>()); 73 | methodMapPath.get(method).add(newPath); 74 | } 75 | 76 | } 77 | 78 | public HashSet getPaths(SootMethod method){ 79 | if(!methodMapPath.containsKey(method)){ 80 | BriefUnitGraph cfg = getCFG(method); 81 | if(cfg==null) 82 | return null; 83 | for(Unit head:cfg.getHeads()){ 84 | buildPaths(method,head,cfg,new HashSet<>(),new Path()); 85 | } 86 | } 87 | return methodMapPath.get(method); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/cfg/Path.java: -------------------------------------------------------------------------------- 1 | package cfg; 2 | 3 | import soot.Unit; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | 9 | public class Path { 10 | 11 | public List list=new ArrayList<>(); 12 | 13 | public void add(Unit unit){ 14 | list.add(unit); 15 | } 16 | 17 | public void addAll(Path path){ 18 | list.addAll(path.list); 19 | } 20 | 21 | public boolean contains(Unit unit){ 22 | return list.contains(unit); 23 | } 24 | 25 | public Unit get(int index){ 26 | return list.get(index); 27 | } 28 | 29 | 30 | 31 | public int indexOf(Unit unit){ 32 | return list.indexOf(unit); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/component/FragementCreater.java: -------------------------------------------------------------------------------- 1 | package component; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import soot.Scene; 6 | import soot.SootClass; 7 | import soot.SootMethod; 8 | 9 | import java.util.Arrays; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | 13 | 14 | public class FragementCreater { 15 | public static final Logger logger= LoggerFactory.getLogger(FragementCreater.class); 16 | 17 | public static final String FRAGMENT="androidx.fragment.app.Fragment"; 18 | public static final String FRAGMENT_1="android.support.v4.app.Fragment"; 19 | public static final String DIALOG_FRAGMENT="androidx.fragment.app.DialogFragment"; 20 | public static final String LIST_FRAGMENT="androidx.fragment.app.ListFragment"; 21 | 22 | 23 | public static final String FRAGMENT_ONCREATE = "void onCreate(android.os.Bundle)"; 24 | public static final String FRAGMENT_ONATTACH = "void onAttach(android.app.Activity)"; 25 | public static final String FRAGMENT_ONCREATEVIEW = "android.view.View onCreateView(android.view.LayoutInflater,android.view.ViewGroup,android.os.Bundle)"; 26 | public static final String FRAGMENT_ONVIEWCREATED = "void onViewCreated(android.view.View,android.os.Bundle)"; 27 | public static final String FRAGMENT_ONSTART = "void onStart()"; 28 | public static final String FRAGMENT_ONACTIVITYCREATED = "void onActivityCreated(android.os.Bundle)"; 29 | public static final String FRAGMENT_ONVIEWSTATERESTORED = "void onViewStateRestored(android.app.Activity)"; 30 | public static final String FRAGMENT_ONRESUME = "void onResume()"; 31 | public static final String FRAGMENT_ONPAUSE = "void onPause()"; 32 | public static final String FRAGMENT_ONSTOP = "void onStop()"; 33 | public static final String FRAGMENT_ONDESTROYVIEW = "void onDestroyView()"; 34 | public static final String FRAGMENT_ONDESTROY = "void onDestroy()"; 35 | public static final String FRAGMENT_ONDETACH = "void onDetach()"; 36 | public static final String FRAGMENT_ONSAVEINSTANCESTATE = "void onSaveInstanceState(android.os.Bundle)"; 37 | 38 | public static final String[] fragmentLifeCycleMethod={FRAGMENT_ONCREATE,FRAGMENT_ONATTACH,FRAGMENT_ONCREATEVIEW,FRAGMENT_ONVIEWCREATED,FRAGMENT_ONSTART, 39 | FRAGMENT_ONACTIVITYCREATED,FRAGMENT_ONSAVEINSTANCESTATE,FRAGMENT_ONVIEWSTATERESTORED,FRAGMENT_ONRESUME,FRAGMENT_ONPAUSE,FRAGMENT_ONSTOP,FRAGMENT_ONDESTROYVIEW, 40 | FRAGMENT_ONDESTROY,FRAGMENT_ONDETACH}; 41 | 42 | public static final List fragmentLifeCycleMethodList= Arrays.asList(fragmentLifeCycleMethod); 43 | 44 | public static final String[] fragmentClasses={FRAGMENT,FRAGMENT_1,DIALOG_FRAGMENT,LIST_FRAGMENT}; 45 | 46 | public static HashSet getAllFragmentInApp(){ 47 | HashSet res=new HashSet<>(); 48 | //我们遍历应用中的类,看这些类是不是 49 | for(SootClass cls:Scene.v().getClasses()){ 50 | if(cls.isAbstract()||cls.isInterface()) 51 | continue; 52 | if(!cls.isApplicationClass()) 53 | continue; 54 | if(cls.isEnum()) 55 | continue; 56 | if(cls.isLibraryClass()) 57 | continue; 58 | for(String fragmentClas:fragmentClasses) { 59 | //判断是不是Fragment几个类的子类,如果是子类我们应该寻找它的生命周期函数 60 | if (isSubClass(cls, fragmentClas)){ 61 | logger.info("[Fragment Class]: {}",cls.getName()); 62 | for(String subSignature:fragmentLifeCycleMethodList){ 63 | SootMethod method = findMethodBySubSignature(cls, subSignature); 64 | if(method==null) 65 | continue; 66 | res.add(method); 67 | } 68 | } 69 | } 70 | } 71 | return res; 72 | } 73 | 74 | public static SootMethod findMethodBySubSignature(SootClass currentClass, String subsignature) { 75 | if (currentClass.getName().startsWith("android.") || currentClass.getName().startsWith("androidx")) 76 | return null; 77 | 78 | SootMethod m = currentClass.getMethodUnsafe(subsignature); 79 | 80 | if (m != null) { 81 | return m; 82 | } 83 | if (currentClass.hasSuperclass()) { 84 | return findMethodBySubSignature(currentClass.getSuperclass(), subsignature); 85 | } 86 | return null; 87 | } 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | public static boolean isSubClass(SootClass currentCls,String cls){ 100 | if(!currentCls.hasSuperclass()) 101 | return false; 102 | SootClass superclass = currentCls.getSuperclass(); 103 | if(superclass.getName().equals(cls)) 104 | return true; 105 | return isSubClass(superclass,cls); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/constant/ApkAndJavaConstants.java: -------------------------------------------------------------------------------- 1 | package constant; 2 | 3 | import java.io.File; 4 | 5 | 6 | public class ApkAndJavaConstants { 7 | 8 | public static final String apkDir = System.getProperty("user.dir") + File.separator + "app"; 9 | public static final String androidJarPath = System.getenv("ANDROID_HOME") + File.separator + "platforms"; 10 | 11 | 12 | public static final String programPath = System.getProperty("user.dir") + File.separator + "program"; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/constant/CollectionsUsageDefinition.java: -------------------------------------------------------------------------------- 1 | package constant; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | 7 | public class CollectionsUsageDefinition { 8 | 9 | //和集合相关的api 10 | 11 | //方法特别多,这里只关注他们的subSignature 12 | 13 | public static final String ADD_0="boolean add(java.lang.Object)"; 14 | public static final String ADD_1="void add(int,java.lang.Object)"; 15 | public static final String ADD_ALL="boolean addAll(java.util.Collection)"; 16 | public static final String REMOVE_0="java.lang.Object remove(int)"; 17 | public static final String REMOVE_1="boolean remove(java.lang.Object)"; 18 | public static final String REMOVE_2="void remove()"; 19 | public static final String REMOVE_ALL="boolean removeAll(java.util.Collection)"; 20 | public static final String CLEAR="void clear()"; 21 | public static final String PUT="java.lang.Object put(java.lang.Object,java.lang.Object)"; 22 | public static final String PUT_ALL="java.lang.Object remove(java.lang.Object)"; 23 | 24 | public static final String[] collectionApi={ 25 | ADD_0,ADD_1,ADD_ALL,REMOVE_0,REMOVE_1,REMOVE_2,REMOVE_ALL,CLEAR,PUT,PUT_ALL 26 | }; 27 | 28 | public static final List collectionApiList= Arrays.asList(collectionApi); 29 | 30 | public static List getCollectionApiList(){ 31 | return collectionApiList; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/constant/EntryPointsDefinition.java: -------------------------------------------------------------------------------- 1 | package constant; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | 8 | public class EntryPointsDefinition { 9 | 10 | 11 | public static final String ACTIVITY_ONCREATE = "void onCreate(android.os.Bundle)"; 12 | public static final String ACTIVITY_ONSTART = "void onStart()"; 13 | public static final String ACTIVITY_ONRESTOREINSTANCESTATE = "void onRestoreInstanceState(android.os.Bundle)"; 14 | public static final String ACTIVITY_ONPOSTCREATE = "void onPostCreate(android.os.Bundle)"; 15 | public static final String ACTIVITY_ONRESUME = "void onResume()"; 16 | public static final String ACTIVITY_ONPOSTRESUME = "void onPostResume()"; 17 | public static final String ACTIVITY_ONCREATEDESCRIPTION = "java.lang.CharSequence onCreateDescription()"; 18 | public static final String ACTIVITY_ONSAVEINSTANCESTATE = "void onSaveInstanceState(android.os.Bundle)"; 19 | public static final String ACTIVITY_ONPAUSE = "void onPause()"; 20 | public static final String ACTIVITY_ONSTOP = "void onStop()"; 21 | public static final String ACTIVITY_ONRESTART = "void onRestart()"; 22 | public static final String ACTIVITY_ONDESTROY = "void onDestroy()"; 23 | public static final String ACTIVITY_ONATTACHFRAGMENT = "void onAttachFragment(android.app.Fragment)"; 24 | 25 | 26 | public static final String SERVICE_ONCREATE = "void onCreate()"; 27 | public static final String SERVICE_ONSTART1 = "void onStart(android.content.Intent,int)"; 28 | public static final String SERVICE_ONSTART2 = "int onStartCommand(android.content.Intent,int,int)"; 29 | public static final String SERVICE_ONBIND = "android.os.IBinder onBind(android.content.Intent)"; 30 | public static final String SERVICE_ONREBIND = "void onRebind(android.content.Intent)"; 31 | public static final String SERVICE_ONUNBIND = "boolean onUnbind(android.content.Intent)"; 32 | public static final String SERVICE_ONDESTROY = "void onDestroy()"; 33 | public static final String SERVICE_ONHANDLEINTENT="void onHandleIntent(android.content.Intent)"; 34 | 35 | public static final String BROADCAST_ONRECEIVE = "void onReceive(android.content.Context,android.content.Intent)"; 36 | 37 | public static final String CONTENTPROVIDER_ONCREATE = "boolean onCreate()"; 38 | public static final String CONTENTPROVIDER_INSERT = "android.net.Uri insert(android.net.Uri,android.content.ContentValues)"; 39 | public static final String CONTENTPROVIDER_QUERY = 40 | "android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)"; 41 | public static final String CONTENTPROVIDER_UPDATE = "int update(android.net.Uri,android.content.ContentValues,java.lang.String,java.lang.String[])"; 42 | public static final String CONTENTPROVIDER_DELETE = "int delete(android.net.Uri,java.lang.String,java.lang.String[])"; 43 | public static final String CONTENTPROVIDER_GETTYPE = "java.lang.String getType(android.net.Uri)"; 44 | 45 | 46 | public static final String APPLICATION_ONCREATE = "void onCreate()"; 47 | public static final String APPLICATION_ONTERMINATE = "void onTerminate()"; 48 | 49 | public static List allLifeCycleMethodList = null; 50 | 51 | private static final String[] activityMethods = {ACTIVITY_ONCREATE, ACTIVITY_ONDESTROY, ACTIVITY_ONPAUSE, 52 | ACTIVITY_ONRESTART, ACTIVITY_ONRESUME, ACTIVITY_ONSTART, ACTIVITY_ONSTOP, ACTIVITY_ONSAVEINSTANCESTATE, 53 | ACTIVITY_ONRESTOREINSTANCESTATE, ACTIVITY_ONCREATEDESCRIPTION, ACTIVITY_ONPOSTCREATE, ACTIVITY_ONPOSTRESUME, 54 | ACTIVITY_ONATTACHFRAGMENT}; 55 | private static final List activityMethodList = Arrays.asList(activityMethods); 56 | 57 | private static final String[] serviceMethods = {SERVICE_ONCREATE, SERVICE_ONDESTROY, SERVICE_ONSTART1, 58 | SERVICE_ONSTART2, SERVICE_ONBIND, SERVICE_ONREBIND, SERVICE_ONUNBIND,SERVICE_ONHANDLEINTENT}; 59 | private static final List serviceMethodList = Arrays.asList(serviceMethods); 60 | 61 | private static final String[] broadcastMethods = {BROADCAST_ONRECEIVE}; 62 | private static final List broadcastMethodList = Arrays.asList(broadcastMethods); 63 | 64 | private static final String[] contentproviderMethods = {CONTENTPROVIDER_ONCREATE, CONTENTPROVIDER_DELETE, 65 | CONTENTPROVIDER_GETTYPE, CONTENTPROVIDER_INSERT, CONTENTPROVIDER_QUERY, CONTENTPROVIDER_UPDATE}; 66 | private static final List contentProviderMethodList = Arrays.asList(contentproviderMethods); 67 | 68 | private static final String[] applicationMethods={APPLICATION_ONCREATE,APPLICATION_ONTERMINATE}; 69 | 70 | private static final List applicationMethodList =Arrays.asList(applicationMethods); 71 | 72 | public static List getActivityLifecycleMethods() { 73 | return activityMethodList; 74 | } 75 | 76 | public static List getServiceLifecycleMethods() { 77 | return serviceMethodList; 78 | } 79 | 80 | public static List getBroadcastLifecycleMethods() { 81 | return broadcastMethodList; 82 | } 83 | 84 | public static List getContentproviderLifecycleMethods() { 85 | return contentProviderMethodList; 86 | } 87 | 88 | public static List getAppliactionMethods(){ 89 | return applicationMethodList; 90 | } 91 | 92 | public static List getAllLifeCycleMethodList() { 93 | 94 | if (allLifeCycleMethodList == null) { 95 | allLifeCycleMethodList = new ArrayList<>(); 96 | allLifeCycleMethodList.addAll(activityMethodList); 97 | allLifeCycleMethodList.addAll(serviceMethodList); 98 | allLifeCycleMethodList.addAll(broadcastMethodList); 99 | allLifeCycleMethodList.addAll(contentProviderMethodList); 100 | } 101 | 102 | return allLifeCycleMethodList; 103 | } 104 | 105 | 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/constant/FileUsageDefinition.java: -------------------------------------------------------------------------------- 1 | package constant; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | 8 | 9 | public class FileUsageDefinition { 10 | 11 | 12 | //SharedPreferences的载入操作 13 | public static final String SHARED_PREFERENCES_0 = "android.content.SharedPreferences getSharedPreferences(java.lang.String,int)"; 14 | public static final String SHARED_PREFERENCES_1 = "android.content.SharedPreferences getPreferences(int)"; 15 | public static final String SHARED_PREFERENCES_2 = "android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context)"; 16 | 17 | //SharedPreferences的读 18 | public static final String SHARED_PREFERENCE_REDA_0 = ""; 19 | public static final String SHARED_PREFERENCE_REDA_1 = ""; 20 | public static final String SHARED_PREFERENCE_REDA_2 = ""; 21 | public static final String SHARED_PREFERENCE_REDA_3 = ""; 22 | public static final String SHARED_PREFERENCE_REDA_4 = ""; 23 | public static final String SHARED_PREFERENCE_REDA_5 = ""; 24 | public static final String SHARED_PREFERENCE_REDA_6 = ""; 25 | //SharedPreference的写 26 | public static final String SHARED_PREFERENCE_WRITE_0 = "android.content.SharedPreferences$Editor putInt(java.lang.String,int)"; 27 | public static final String SHARED_PREFERENCE_WRITE_1 = "android.content.SharedPreferences$Editor putBoolean(java.lang.String,boolean)"; 28 | public static final String SHARED_PREFERENCE_WRITE_2 = "android.content.SharedPreferences$Editor putLong(java.lang.String,long)"; 29 | public static final String SHARED_PREFERENCE_WRITE_3 = "android.content.SharedPreferences$Editor putFloat(java.lang.String,float)"; 30 | public static final String SHARED_PREFERENCE_WRITE_4 = "android.content.SharedPreferences$Editor putString(java.lang.String,java.lang.String)"; 31 | public static final String SHARED_PREFERENCE_WRITE_5 = "android.content.SharedPreferences$Editor putStringSet(java.lang.String,java.util.Set)"; 32 | 33 | //数据库对象的创建方式 34 | public static final String DATA_BASE_0 = "android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String,int,android.database.sqlite.SQLiteDatabase$CursorFactory,android.database.DatabaseErrorHandler)"; 35 | public static final String DATA_BASE_1 = "android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String,int,android.database.sqlite.SQLiteDatabase$CursorFactory)"; 36 | public static final String DATA_BASE_2 = "android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.io.File,android.database.sqlite.SQLiteDatabase$CursorFactory)"; 37 | public static final String DATA_BASE_3 = "android.database.sqlite.SQLiteDatabase getReadableDatabase()"; 38 | public static final String DATA_BASE_4 = "android.database.sqlite.SQLiteDatabase getWritableDatabase()"; 39 | public static final String DATA_BASE_5 = "android.database.sqlite.SQLiteDatabase openDatabase(java.io.File,android.database.sqlite.SQLiteDatabase$OpenParams)"; 40 | public static final String DATA_BASE_6 = "android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String,android.database.sqlite.SQLiteDatabase$CursorFactory,int)"; 41 | public static final String DATA_BASE_7 = "android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String,android.database.sqlite.SQLiteDatabase$CursorFactory,int,android.database.DatabaseErrorHandler)"; 42 | 43 | //数据库的查询 44 | public static final String DATA_BASE_QUERY_0 = "android.database.Cursor query(java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String)"; 45 | public static final String DATA_BASE_QUERY_1 = "android.database.Cursor query(java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String)"; 46 | public static final String DATA_BASE_QUERY_2 = "android.database.Cursor query(boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String)"; 47 | public static final String DATA_BASE_QUERY_3 = "android.database.Cursor query(boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String,android.os.CancellationSignal)"; 48 | public static final String DATA_BASE_QUERY_4 = "android.database.Cursor rawQuery(java.lang.String,java.lang.String[])"; 49 | public static final String DATA_BASE_QUERY_5 = "android.database.Cursor rawQuery(java.lang.String,java.lang.String[],android.os.CancellationSignal)"; 50 | public static final String DATA_BASE_QUERY_6 = "android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,java.lang.String,java.lang.String[],java.lang.String)"; 51 | public static final String DATA_BASE_QUERY_7 = "android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,java.lang.String,java.lang.String[],java.lang.String,android.os.CancellationSignal)"; 52 | public static final String DATA_BASE_QUERY_8 = "android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String)"; 53 | public static final String DATA_BASE_QUERY_9 = "android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String,android.os.CancellationSignal)"; 54 | public static final String DATA_BASE_QUERY_10 = "void execSQL(java.lang.String)"; 55 | public static final String DATA_BASE_QUERY_11 = "void execSQL(java.lang.String,java.lang.Object[])"; 56 | 57 | //ContentResolver查询 58 | public static final String CONTENT_RESOLVER_QUERY_0 = "android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)"; 59 | public static final String CONTENT_RESOLVER_QUERY_1 = "android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,android.os.CancellationSignal)"; 60 | // public static final String CONTENT_RESOLVER_LOAD_THUMBNAIL = "android.graphics.Bitmap loadThumbnail(android.net.Uri,android.util.Size,android.os.CancellationSignal)"; 61 | // public static final String CONTENT_RESOLVER_OPEN_ASSET_FILE = "android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri,java.lang.String,android.os.CancellationSignal)"; 62 | // public static final String CONTENT_RESOLVER_OPEN_FILE = "android.os.ParcelFileDescriptor openFile(android.net.Uri,java.lang.String,android.os.CancellationSignal)"; 63 | // public static final String CONTENT_RESOLVER_OPEN_INPUT_STREAM = "java.io.InputStream openInputStream(android.net.Uri)"; 64 | 65 | //文件加载构造方法,文件创建 66 | public static final String FILE_LOAD_0 = "(java.lang.String)>"; 67 | public static final String FILE_LOAD_1 = "(java.lang.String,java.lang.String)>"; 68 | public static final String FILE_LOAD_2 = "(java.io.File,java.lang.String)>"; 69 | public static final String FILE_LOAD_3 = "(java.net.URI)>"; 70 | //输入流构造方法,这些输入流的参数为file或者file_name,这些API可以直接判定文件名称 71 | public static final String FILE_INPUT_STREAM_0 = "(java.io.File)>"; 72 | public static final String FILE_INPUT_STREAM_1 = "(java.lang.String)>"; 73 | public static final String FILE_INPUT_STREAM_2 = "(java.lang.String)>"; 74 | public static final String FILE_INPUT_STREAM_3 = "(java.io.File)>"; 75 | //输出流构造方法,这些输出流的参数为file或者file_name,这些API可以直接判定文件名称 76 | public static final String FILE_OUTPUT_STREAM_0 = "(java.lang.String)>"; 77 | public static final String FILE_OUTPUT_STREAM_1 = "(java.lang.String,boolean)>"; 78 | public static final String FILE_OUTPUT_STREAM_2 = "(java.io.File)>"; 79 | public static final String FILE_OUTPUT_STREAM_3 = "(java.io.File,boolean)>"; 80 | public static final String FILE_OUTPUT_STREAM_4 = "(java.lang.String)>"; 81 | public static final String FILE_OUTPUT_STREAM_5 = "(java.lang.String,boolean)>"; 82 | public static final String FILE_OUTPUT_STREAM_6 = "(java.io.File)>"; 83 | public static final String FILE_OUTPUT_STREAM_7 = "(java.io.File,boolean)>"; 84 | public static final String FILE_OUTPUT_STREAM_8 = "(java.io.OutputStream)>"; 85 | public static final String FILE_OUTPUT_STREAM_9 = "(java.io.OutputStream,java.lang.String)>"; 86 | public static final String FILE_OUTPUT_STREAM_10 = "(java.io.Writer)>"; 87 | public static final String FILE_OUTPUT_STREAM_11 = "(java.io.Writer,int)>"; 88 | public static final String FILE_OUTPUT_STREAM_12 = "(java.io.OutputStream)>"; 89 | public static final String FILE_OUTPUT_STREAM_13 = "(java.io.OutputStream)>"; 90 | public static final String FILE_OUTPUT_STREAM_14 = "(java.io.OutputStream,int)>"; 91 | public static final String FILE_OUTPUT_STREAM_15 = "(java.io.OutputStream)>"; 92 | 93 | //Uri创建API 94 | public static final String URI_CREATE_0 = ""; 95 | public static final String URI_CREATE_1 = ""; 96 | public static final String URI_CREATE_2 = ""; 97 | public static final String URI_CREATE_3 = ""; 98 | 99 | public static final String[] sharedPreferencesLoadAPI = {SHARED_PREFERENCES_0, SHARED_PREFERENCES_1, SHARED_PREFERENCES_2}; 100 | 101 | public static final List sharedPreferencesLoadAPIList = Arrays.asList(sharedPreferencesLoadAPI); 102 | 103 | public static final String[] sharedPreferencesReadAPI = {SHARED_PREFERENCE_REDA_0, SHARED_PREFERENCE_REDA_1, SHARED_PREFERENCE_REDA_2, SHARED_PREFERENCE_REDA_3, 104 | SHARED_PREFERENCE_REDA_4, SHARED_PREFERENCE_REDA_5, SHARED_PREFERENCE_REDA_6}; 105 | 106 | public static final List sharedPreferencesReadAPIList = Arrays.asList(sharedPreferencesReadAPI); 107 | 108 | public static final String[] sharedPreferencesWriteAPI = {SHARED_PREFERENCE_WRITE_0, SHARED_PREFERENCE_WRITE_1, SHARED_PREFERENCE_WRITE_2, SHARED_PREFERENCE_WRITE_3, 109 | SHARED_PREFERENCE_WRITE_3, SHARED_PREFERENCE_WRITE_4, SHARED_PREFERENCE_WRITE_5}; 110 | 111 | public static final List sharedPreferencesWriteAPIList = Arrays.asList(sharedPreferencesWriteAPI); 112 | 113 | public static final String[] dataBaseOpenOrCreateAPI = {DATA_BASE_0, DATA_BASE_1, DATA_BASE_2, DATA_BASE_3, DATA_BASE_4, DATA_BASE_5, DATA_BASE_6, DATA_BASE_7}; 114 | 115 | public static final List dataBaseOpenOrCreateAPIList = Arrays.asList(dataBaseOpenOrCreateAPI); 116 | 117 | public static final String[] dataBaseQueryAPI = {DATA_BASE_QUERY_0, DATA_BASE_QUERY_1, DATA_BASE_QUERY_2, DATA_BASE_QUERY_3, DATA_BASE_QUERY_4, DATA_BASE_QUERY_5, 118 | DATA_BASE_QUERY_6, DATA_BASE_QUERY_7, DATA_BASE_QUERY_8, DATA_BASE_QUERY_9}; 119 | 120 | public static final List dataBaseQueryAPIList = Arrays.asList(dataBaseQueryAPI); 121 | 122 | public static final String[] contentResolverQueryAPI = {CONTENT_RESOLVER_QUERY_0, CONTENT_RESOLVER_QUERY_1, 123 | // CONTENT_RESOLVER_LOAD_THUMBNAIL, CONTENT_RESOLVER_OPEN_ASSET_FILE, 124 | // CONTENT_RESOLVER_OPEN_FILE, CONTENT_RESOLVER_OPEN_INPUT_STREAM 125 | }; 126 | 127 | public static final List contentResolverQueryAPIList = Arrays.asList(contentResolverQueryAPI); 128 | 129 | public static final String[] inputStreamConstructor = {FILE_INPUT_STREAM_0, FILE_INPUT_STREAM_1, FILE_INPUT_STREAM_2, FILE_INPUT_STREAM_3}; 130 | 131 | public static final List inputStreamConstructorList = Arrays.asList(inputStreamConstructor); 132 | 133 | public static final String[] outputStreamConstructor = {FILE_OUTPUT_STREAM_0, FILE_OUTPUT_STREAM_1, FILE_OUTPUT_STREAM_2, FILE_OUTPUT_STREAM_3, FILE_OUTPUT_STREAM_4, FILE_OUTPUT_STREAM_5, 134 | FILE_OUTPUT_STREAM_5, FILE_OUTPUT_STREAM_6, FILE_OUTPUT_STREAM_7, FILE_OUTPUT_STREAM_8, FILE_OUTPUT_STREAM_9, FILE_OUTPUT_STREAM_10, 135 | FILE_OUTPUT_STREAM_11, FILE_OUTPUT_STREAM_12, FILE_OUTPUT_STREAM_12, FILE_OUTPUT_STREAM_13, FILE_OUTPUT_STREAM_14, FILE_OUTPUT_STREAM_15}; 136 | 137 | public static final List outputStreamConstructorList = Arrays.asList(outputStreamConstructor); 138 | 139 | public static final String[] uriCreateAPI = {URI_CREATE_0, URI_CREATE_1, URI_CREATE_2, URI_CREATE_3}; 140 | 141 | public static final List uriCreateAPIList = Arrays.asList(uriCreateAPI); 142 | 143 | public static final String[] javaIOAPI = { 144 | FILE_LOAD_0, 145 | FILE_LOAD_1, 146 | FILE_LOAD_2, 147 | FILE_LOAD_3, 148 | FILE_INPUT_STREAM_0, 149 | FILE_INPUT_STREAM_1, 150 | FILE_INPUT_STREAM_2, 151 | FILE_INPUT_STREAM_3, 152 | FILE_OUTPUT_STREAM_0, 153 | FILE_OUTPUT_STREAM_1, 154 | FILE_OUTPUT_STREAM_2, 155 | FILE_OUTPUT_STREAM_3, 156 | FILE_OUTPUT_STREAM_4, 157 | FILE_OUTPUT_STREAM_5, 158 | FILE_OUTPUT_STREAM_6, 159 | FILE_OUTPUT_STREAM_7, 160 | FILE_OUTPUT_STREAM_8, 161 | FILE_OUTPUT_STREAM_9, 162 | FILE_OUTPUT_STREAM_10, 163 | FILE_OUTPUT_STREAM_11, 164 | FILE_OUTPUT_STREAM_12, 165 | FILE_OUTPUT_STREAM_13, 166 | FILE_OUTPUT_STREAM_14, 167 | FILE_OUTPUT_STREAM_15 168 | }; 169 | 170 | 171 | 172 | private static List javaIOAPIList = Arrays.asList(javaIOAPI); 173 | 174 | public static final String[] fileLoadApis={FILE_LOAD_0,FILE_LOAD_1,FILE_LOAD_2,FILE_LOAD_3}; 175 | 176 | private static List fileLoadAPIList=Arrays.asList(fileLoadApis); 177 | 178 | 179 | 180 | 181 | public static List getSharedPreferencesLoadAPIList() { 182 | return sharedPreferencesLoadAPIList; 183 | } 184 | 185 | public static List getSharedPreferencesReadAPIList() { 186 | return sharedPreferencesReadAPIList; 187 | } 188 | 189 | public static List getSharedPreferencesWriteAPIList() { 190 | return sharedPreferencesWriteAPIList; 191 | } 192 | 193 | public static List getDataBaseOpenOrCreateAPIList() { 194 | return dataBaseOpenOrCreateAPIList; 195 | } 196 | 197 | public static List getDataBaseQueryAPIList() { 198 | return dataBaseQueryAPIList; 199 | } 200 | 201 | public static List getContentResolverQueryAPIList() { 202 | return contentResolverQueryAPIList; 203 | } 204 | 205 | public static List getInputStreamConstructorList() { 206 | return inputStreamConstructorList; 207 | } 208 | 209 | public static List getOutputStreamConstructorList() { 210 | return outputStreamConstructorList; 211 | } 212 | 213 | public static List getUriCreateAPIList() { 214 | return uriCreateAPIList; 215 | } 216 | 217 | public static List getJavaIOAPIList(){ 218 | return javaIOAPIList; 219 | } 220 | 221 | public static List getFileLoadAPIList(){ return fileLoadAPIList;} 222 | 223 | 224 | 225 | 226 | } -------------------------------------------------------------------------------- /src/main/java/constant/IPCPointDefinition.java: -------------------------------------------------------------------------------- 1 | package constant; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | 7 | public class IPCPointDefinition { 8 | 9 | 10 | //通过以下方法调用第三方应用或者自身应用的组件 11 | public static final String IPC_SERVICE="android.content.ComponentName startService(android.content.Intent)"; 12 | public static final String IPC_FOREGROUND_SERVICE="android.content.ComponentName startForegroundService(android.content.Intent)"; 13 | 14 | public static final String IPC_RECEIVER="void sendBroadcast(android.content.Intent)"; 15 | public static final String IPC_ORDER_RECEIVER="void sendBroadcast(android.content.Intent,java.lang.String)"; 16 | 17 | public static final String IPC_PROVIDER_BULK_INSERT="int bulkInsert(android.net.Uri,android.content.ContentValues[])"; 18 | public static final String IPC_PROVIDER_INSERT_0="android.net.Uri insert(android.net.Uri,android.content.ContentValues)"; 19 | public static final String IPC_PROVIDER_INSERT_1="android.net.Uri insert(android.net.Uri,android.content.ContentValues,android.os.Bundle)"; 20 | public static final String IPC_PROVIDER_UPDATE_0="int update(android.net.Uri,android.content.ContentValues[],java.lang.String,java.lang.String[])"; 21 | public static final String IPC_PROVIDER_UPDATA_1="int update(android.net.Uri,android.content.ContentValues[],android.os.Bundle)"; 22 | 23 | 24 | public static final String[] ipc_method={IPC_SERVICE,IPC_FOREGROUND_SERVICE,IPC_RECEIVER,IPC_ORDER_RECEIVER,IPC_PROVIDER_BULK_INSERT,IPC_PROVIDER_INSERT_0,IPC_PROVIDER_INSERT_1,IPC_PROVIDER_UPDATE_0, 25 | IPC_PROVIDER_UPDATA_1 26 | }; 27 | 28 | public static final List ipcMethodList= Arrays.asList(ipc_method); 29 | 30 | public static List getIpcMethodList(){ 31 | return ipcMethodList; 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/constant/StrawPointsDefinition.java: -------------------------------------------------------------------------------- 1 | package constant; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | 8 | public class StrawPointsDefinition { 9 | 10 | public static final String COLLECTIONS_STRAWPOINT_REGEX = "(<(java|android)\\.util\\..*(boolean|void) add.*>)|(<(java|android)\\.util\\..*(java.lang.Object|void) put.*>)|()"; 11 | 12 | public static final String FILE_WRITE_0=""; 13 | public static final String FILE_WRITE_1=""; 14 | public static final String FILE_WRITE_2=""; 15 | public static final String FILE_WRITE_3=""; 16 | public static final String FILE_WRITE_4=""; 17 | public static final String FILE_WRITE_5=""; 18 | public static final String FILE_WRITE_6=""; 19 | public static final String FILE_WRITE_7=""; 20 | public static final String FILE_WRITE_8=""; 21 | public static final String FILE_WRITE_9=""; 22 | public static final String FILE_WRITE_10=""; 23 | public static final String FILE_WRITE_11=""; 24 | public static final String FILE_WRITE_12=""; 25 | public static final String FILE_WRITE_13=""; 26 | public static final String FILE_WRITE_14=""; 27 | public static final String FILE_WRITE_15=""; 28 | public static final String FILE_WRITE_16=""; 29 | public static final String FILE_WRITE_17=""; 30 | public static final String FILE_WRITE_18=""; 31 | public static final String FILE_WRITE_19=""; 32 | public static final String FILE_WRITE_20=""; 33 | public static final String FILE_WRITE_21=""; 34 | public static final String FILE_WRITE_22=""; 35 | public static final String FILE_WRITE_23=""; 36 | public static final String FILE_WRITE_24=""; 37 | public static final String FILE_WRITE_25=""; 38 | public static final String FILE_WRITE_26=""; 39 | public static final String FILE_WRITE_27=""; 40 | public static final String FILE_WRITE_28=""; 41 | 42 | public static final String SHAREDPREFERENCES_PUT_STRING=""; 43 | public static final String SHAREDPREFERENCES_PUT_BOOLEAN=""; 44 | public static final String SHAREDPREFERENCES_PUT_STRINGSET=""; 45 | public static final String SHAREDPREFERENCES_PUT_FLOAT=""; 46 | public static final String SHAREDPREFERENCES_PUT_LONG=""; 47 | public static final String SHARED_PREFERENCE_PUT_INT = ""; 48 | 49 | 50 | public static final String DATABASE_INSERT=""; 51 | public static final String DATABASE_INSERTORTHROW=""; 52 | public static final String DATABASE_INSERTWITHONCONFLICT=""; 53 | public static final String DATABASE_EXECSQL=""; 54 | public static final String DATABASE_UPDATE_0=""; 55 | public static final String DATABASE_UPDATE_1=""; 56 | // public static final String DATABASE_DELETE=""; 57 | 58 | public static final String MEDIA_BULK_INSERT=""; 59 | public static final String MEDIA_INSERT_0=""; 60 | public static final String MEDIA_INSERT_1=""; 61 | 62 | public static final String IPC_SERVICE=""; 63 | public static final String IPC_FOREGROUND_SERVICE=""; 64 | public static final String IPC_RECEIVER=""; 65 | public static final String IPC_ORDER_RECEIVER=""; 66 | public static final String IPC_PROVIDER_BULK_INSERT=""; 67 | public static final String IPC_PROVIDER_INSERT_0=""; 68 | public static final String IPC_PROVIDER_INSERT_1=""; 69 | public static final String IPC_PROVIDER_UPDATE_0=""; 70 | public static final String IPC_PROVIDER_UPDATA_1=""; 71 | 72 | private static final String[] fileWriteMethod={FILE_WRITE_0,FILE_WRITE_1,FILE_WRITE_2,FILE_WRITE_3,FILE_WRITE_4,FILE_WRITE_5, 73 | FILE_WRITE_6,FILE_WRITE_7,FILE_WRITE_8,FILE_WRITE_9,FILE_WRITE_10,FILE_WRITE_11,FILE_WRITE_12,FILE_WRITE_13,FILE_WRITE_14,FILE_WRITE_15, 74 | FILE_WRITE_16,FILE_WRITE_17,FILE_WRITE_18,FILE_WRITE_19,FILE_WRITE_20,FILE_WRITE_21,FILE_WRITE_22,FILE_WRITE_23,FILE_WRITE_24,FILE_WRITE_25, 75 | FILE_WRITE_26,FILE_WRITE_27,FILE_WRITE_28}; 76 | private static final List fileWriteMethodList=Arrays.asList(fileWriteMethod); 77 | 78 | private static final String[] sharedPreferencesWriteMethod={SHAREDPREFERENCES_PUT_STRING,SHAREDPREFERENCES_PUT_STRINGSET,SHAREDPREFERENCES_PUT_BOOLEAN,SHAREDPREFERENCES_PUT_FLOAT, 79 | SHAREDPREFERENCES_PUT_LONG,SHARED_PREFERENCE_PUT_INT}; 80 | private static final List sharedPreferencesWriteMethodList=Arrays.asList(sharedPreferencesWriteMethod); 81 | 82 | private static final String[] databaseInsertMethod={DATABASE_INSERT,DATABASE_INSERTORTHROW,DATABASE_INSERTWITHONCONFLICT,DATABASE_EXECSQL,DATABASE_UPDATE_0,DATABASE_UPDATE_1 83 | ,IPC_PROVIDER_BULK_INSERT,IPC_PROVIDER_INSERT_0,IPC_PROVIDER_UPDATA_1,IPC_PROVIDER_UPDATE_0,IPC_PROVIDER_INSERT_1}; 84 | private static final List databaseInsertMethodList=Arrays.asList(databaseInsertMethod); 85 | 86 | private static final String[] mediaInsertMethod={MEDIA_BULK_INSERT,MEDIA_INSERT_0,MEDIA_INSERT_1}; 87 | private static final List mediaInsertMethodList=Arrays.asList(mediaInsertMethod); 88 | 89 | private static final String[] databaseModifiedMethods={DATABASE_INSERT,DATABASE_UPDATE_1,DATABASE_UPDATE_0,DATABASE_INSERTORTHROW,DATABASE_INSERTWITHONCONFLICT}; 90 | private static final List databaseModifiedMethodsList=Arrays.asList(databaseModifiedMethods); 91 | 92 | public static List allInsertMethodList=null; 93 | 94 | /* 95 | ======================================================================================================= 96 | */ 97 | public static List getFileWriteMethodList(){ 98 | return fileWriteMethodList; 99 | } 100 | 101 | public static List getSharedPreferencesWriteMethodList(){ 102 | return sharedPreferencesWriteMethodList; 103 | } 104 | 105 | public static List getDatabaseInsertMethodList(){ 106 | return databaseInsertMethodList; 107 | } 108 | 109 | public static List getMediaInsertMethodList(){ 110 | return mediaInsertMethodList; 111 | } 112 | 113 | public static List getAllInsertMethodList(){ 114 | if(allInsertMethodList==null){ 115 | allInsertMethodList=new ArrayList<>(); 116 | allInsertMethodList.addAll(fileWriteMethodList); 117 | allInsertMethodList.addAll(sharedPreferencesWriteMethodList); 118 | allInsertMethodList.addAll(databaseInsertMethodList); 119 | allInsertMethodList.addAll(mediaInsertMethodList); 120 | } 121 | return allInsertMethodList; 122 | } 123 | 124 | public static List getDatabaseModifeiedMethods(){ 125 | return databaseModifiedMethodsList; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/dataflow/AbstractDataFlow.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import cg.CallGraphUtils; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import soot.*; 7 | import soot.jimple.*; 8 | import soot.jimple.toolkits.callgraph.Edge; 9 | import soot.toolkits.graph.BriefUnitGraph; 10 | 11 | import java.util.*; 12 | 13 | 14 | public abstract class AbstractDataFlow implements Analyze { 15 | 16 | public static final Logger logger = LoggerFactory.getLogger(AbstractDataFlow.class); 17 | 18 | protected int MAX_DEPTH = 15; 19 | 20 | protected Analyze analyze = null; 21 | 22 | public void run() { 23 | 24 | } 25 | 26 | public void setMAX_DEPTH(int depth){ 27 | this.MAX_DEPTH=depth; 28 | } 29 | 30 | public static boolean isValueUsedInUnit(Unit unit, Value value) { 31 | List usedValue = new ArrayList<>(); 32 | for (ValueBox useBox : unit.getUseBoxes()) { 33 | usedValue.add(useBox.getValue().toString()); 34 | } 35 | return usedValue.contains(value.toString()); 36 | } 37 | 38 | public static boolean isValueDefinedInUnit(Unit unit, Value value) { 39 | List definedValue = new ArrayList<>(); 40 | for (ValueBox defBox : unit.getDefBoxes()) { 41 | definedValue.add(defBox.getValue().toString()); 42 | } 43 | return definedValue.contains(value.toString()); 44 | } 45 | 46 | 47 | public static Unit getParmaAssignUnit(BriefUnitGraph graph, int paramIndex) { 48 | Queue queue = new LinkedList<>(graph.getHeads()); 49 | HashSet visit = new HashSet<>(); 50 | while (!queue.isEmpty()) { 51 | Unit poll = queue.poll(); 52 | visit.add(poll); 53 | if (poll.toString().contains("@parameter" + paramIndex)) { 54 | return poll; 55 | } 56 | for (Unit succor : graph.getSuccsOf(poll)) { 57 | if (!visit.contains(succor)) 58 | queue.add(succor); 59 | } 60 | } 61 | return null; 62 | } 63 | 64 | public static boolean isSystemClass(SootClass sootClass) { 65 | String name = sootClass.getName(); 66 | //如果不是分析android系统,注意要让android的库设置为系统类 67 | if (name.startsWith("java") || name.startsWith("javax")||name.startsWith("android.")||name.startsWith("androidx.")||name.startsWith("com.android")||name.startsWith("sun.")) { 68 | return true; 69 | } 70 | // 71 | // if(name.startsWith("java.")||name.startsWith("javax.")) 72 | // return true; 73 | return false; 74 | 75 | } 76 | 77 | 78 | public static boolean isMethodCalled(List callStack, SootMethod method) { 79 | for (CallSite callSite : callStack) { 80 | if (callSite.caller.getSignature().equals(method.getSignature())) 81 | return true; 82 | } 83 | return false; 84 | } 85 | 86 | 87 | //寻找变量的直接赋值语句 88 | public Unit findDirectDefUnit(ValueBox valueBox, Unit u, SootMethod m) { 89 | if (!m.isConcrete()) 90 | return null; 91 | BriefUnitGraph cfg = new BriefUnitGraph(m.retrieveActiveBody()); 92 | Queue queue = new LinkedList<>(); 93 | queue.addAll(cfg.getPredsOf(u)); 94 | HashSet visit = new HashSet<>(); 95 | while (!queue.isEmpty()) { 96 | Unit poll = queue.poll(); 97 | visit.add(poll); 98 | if (isValueDefinedInUnit(poll, valueBox.getValue())) { 99 | return poll; 100 | } 101 | for (Unit pre : cfg.getPredsOf(poll)) { 102 | if (!visit.contains(pre)) 103 | queue.add(pre); 104 | } 105 | } 106 | return null; 107 | } 108 | 109 | public static InvokeExpr getInvokeExpr(Unit u){ 110 | 111 | if(u instanceof InvokeStmt){ 112 | return ((InvokeStmt)u).getInvokeExpr(); 113 | } 114 | 115 | if(u instanceof AssignStmt){ 116 | AssignStmt assignStmt = (AssignStmt) u; 117 | if(assignStmt.containsInvokeExpr()) 118 | return assignStmt.getInvokeExpr(); 119 | } 120 | return null; 121 | } 122 | 123 | public static HashSet getMethodFromCG(Unit u) { 124 | 125 | 126 | // HashSet res = new HashSet<>(); 127 | // 128 | // InvokeExpr m = getInvokeExpr(u); 129 | // if(m==null) 130 | // return res; 131 | // if(!Scene.v().hasCallGraph()) 132 | // throw new RuntimeException("No CallGraph in Scence"); 133 | // Iterator edgeIterator = Scene.v().getCallGraph().edgesOutOf(u); 134 | // while (edgeIterator.hasNext()){ 135 | // Edge next = edgeIterator.next(); 136 | // if(next.tgt().getSubSignature().equals(m.getMethod().getSubSignature())) 137 | // res.add(next.tgt()); 138 | // } 139 | return CallGraphUtils.getMethod(u); 140 | } 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/dataflow/AccessPathTag.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import soot.tagkit.AttributeValueException; 4 | import soot.tagkit.Tag; 5 | 6 | 7 | //access path长度为0,表示当前变量就是污染,当前变量可以是局部变量,this,实例字段或者是静态字段 8 | //一个taint,如果它的access path为0,表示该引用所指的对象是污染的 9 | //如果access path不为0,表示这个引用所指的对象是污染的,比如引用是taint,access path是.x.y.z,那么taint.x.y.z是污染的 10 | public class AccessPathTag implements Tag { 11 | 12 | // private byte[] accessPath = new byte[100];//表示当前的域名,规定最大长度为5,如果有本tag并且它特curIndex为0,表示就是本变量被污染了 13 | //规定任何污染的变量都需要带本属性 14 | 15 | private int fieldLength = 0; 16 | 17 | private String fieldChain = ""; 18 | int accessPathIndex=0; 19 | 20 | @Override 21 | public String getName() { 22 | return "AccessPath"; 23 | } 24 | 25 | public AccessPathTag(Tag tag) { 26 | // accessPath = tag.getValue().clone(); 27 | AccessPathTag accessPathTag = (AccessPathTag) tag; 28 | fieldChain = accessPathTag.fieldChain; 29 | fieldLength=accessPathTag.fieldLength; 30 | } 31 | 32 | public AccessPathTag() { 33 | 34 | } 35 | 36 | // public AccessPathTag(String acc) 37 | 38 | @Override 39 | public byte[] getValue() throws AttributeValueException { 40 | return null; 41 | } 42 | 43 | public boolean appendAccessPath(String fieldName) { 44 | if (fieldLength == 5) {//如果当前的access path超过规定的最大长度,我们认为这个比如r.a.b.c.d.f,此时我们认为r是没有被污染的 45 | return false; 46 | } else { 47 | fieldChain = fieldName + "." + fieldChain; 48 | // System.out.println("升"); 49 | // System.out.println(fieldChain); 50 | // int cur = 0; 51 | // for (byte c : fieldChain.getBytes()) { 52 | // accessPath[cur] = c; 53 | // cur++; 54 | // } 55 | fieldLength += 1; 56 | return true; 57 | } 58 | } 59 | 60 | public boolean removeAccessPath() { 61 | if (fieldLength == 0) 62 | return true; 63 | // System.out.println("降"); 64 | byte[] bytes = fieldChain.getBytes(); 65 | for (int i = 0; i < fieldChain.length(); i++) { 66 | if ((char) bytes[i] == '.') { 67 | fieldChain = fieldChain.substring(i+1); 68 | // accessPath = fieldChain.getBytes(); 69 | return true; 70 | } 71 | } 72 | return false; 73 | 74 | } 75 | 76 | public void setValue(byte[] value) { 77 | // accessPath = value.clone(); 78 | } 79 | 80 | public boolean match(String fieldName) { 81 | if (fieldChain.isEmpty()) 82 | return true; 83 | //查看filed是否匹配 84 | // System.out.println(fieldChain); 85 | String[] split = fieldChain.split("/."); 86 | // System.out.println("匹配字段"); 87 | // System.out.println(split[0]); 88 | // System.out.println(fieldName); 89 | return split[0].replace(".","").equals(fieldName); 90 | } 91 | 92 | public int getFieldLength() { 93 | return fieldLength; 94 | } 95 | 96 | public String getFieldChain(){ 97 | return fieldChain; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/dataflow/Analyze.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | 4 | import soot.SootMethod; 5 | import soot.Unit; 6 | import soot.ValueBox; 7 | 8 | import java.util.HashSet; 9 | import java.util.List; 10 | 11 | 12 | public interface Analyze { 13 | boolean caseAnalyze(Unit unit, SootMethod method, List callStack, HashSet res, ValueBox taintValueBox); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dataflow/BackwardDataFlow.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import cfg.CfgFactory; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import soot.*; 7 | import soot.jimple.*; 8 | import soot.toolkits.graph.BriefUnitGraph; 9 | import util.StringUtil; 10 | 11 | import java.util.ArrayList; 12 | import java.util.HashSet; 13 | import java.util.List; 14 | 15 | 16 | public class BackwardDataFlow extends AbstractDataFlow { 17 | 18 | private static final Logger logger= LoggerFactory.getLogger(BackwardDataFlow.class); 19 | 20 | private boolean flag=true; 21 | 22 | public boolean getFlag(){ 23 | return flag; 24 | } 25 | 26 | public void startBackward(){ 27 | this.flag=true; 28 | } 29 | 30 | 31 | public BackwardDataFlow(Analyze analyze){ 32 | this.analyze=analyze; 33 | } 34 | 35 | @Override 36 | public boolean caseAnalyze(Unit unit, SootMethod method, List callStack,HashSet res,ValueBox taintValueBox) { 37 | return analyze.caseAnalyze(unit,method,callStack,res,null); 38 | } 39 | 40 | public void run(SootMethod method,Unit endUnit,ValueBox valueBox,int depth,List callStack){ 41 | inter_backward_dataflow(method,endUnit,valueBox,depth,callStack); 42 | } 43 | 44 | 45 | public void inter_backward_dataflow(SootMethod method, Unit endUnit, ValueBox endValueBox, int depth, List callStack){ 46 | 47 | if(depth>MAX_DEPTH) 48 | return; 49 | if(isMethodCalled(callStack,method)) 50 | return; 51 | 52 | CfgFactory cfgFactory = CfgFactory.getInstance(); 53 | BriefUnitGraph cfg = cfgFactory.getCFG(method); 54 | if (cfg == null) { 55 | logger.warn("the cfg is null"); 56 | return; 57 | } 58 | 59 | EventQueue waitForProcessEvent=new EventQueue(); 60 | if(endUnit!=null&&endValueBox!=null) { 61 | Event event = new Event(endUnit, endValueBox); 62 | waitForProcessEvent.add(event); 63 | }else { 64 | for (Unit tailUnit : cfg.getTails()) { 65 | if (tailUnit instanceof ReturnStmt) { 66 | ReturnStmt returnStmt = (ReturnStmt) tailUnit; 67 | if (!returnStmt.getOp().toString().equals("null")) { 68 | Event event = new Event(tailUnit, returnStmt.getOpBox()); 69 | waitForProcessEvent.add(event); 70 | } 71 | } 72 | } 73 | } 74 | 75 | HashSet processedEvent=new HashSet<>(); 76 | while (!waitForProcessEvent.isEmpty()&&flag){ 77 | Event event = waitForProcessEvent.poll(); 78 | processedEvent.add(event); 79 | Unit unit = event.unit; 80 | ValueBox valueBox = event.valueBox; 81 | for(Unit pred:cfg.getPredsOf(unit)){ 82 | if(isValueDefinedInUnit(pred,valueBox.getValue())){ 83 | //todo:对于字段的,对于new不做处理,交给使用者 84 | if (caseAnalyze(pred,method,callStack,null,null)) { 85 | //如果符合条件,将标志位设为false,同时关闭查找 86 | flag = false; 87 | } 88 | if(pred instanceof AssignStmt){ 89 | AssignStmt assignStmt = (AssignStmt) pred; 90 | if(assignStmt.containsInvokeExpr()){ 91 | SootClass declaringClass = assignStmt.getInvokeExpr().getMethod().getDeclaringClass(); 92 | if(!isSystemClass(declaringClass)){ 93 | List new_callStack = new ArrayList<>(callStack); 94 | new_callStack.add(new CallSite(method,pred,-1)); 95 | for(SootMethod calleeMethod:getMethodFromCG(pred)) { 96 | inter_backward_dataflow(calleeMethod, null, null, depth + 1, new_callStack); 97 | } 98 | } 99 | } 100 | else if(!assignStmt.containsFieldRef()&&!assignStmt.containsArrayRef()){ 101 | if(!(assignStmt.getLeftOp() instanceof NewExpr)){ 102 | //如果只是简单的赋值 103 | Event new_event = new Event(pred, assignStmt.getUseBoxes().get(0)); 104 | if(processedEvent.contains(new_event)) 105 | processedEvent.add(new_event); 106 | } 107 | } 108 | }else if((pred instanceof IdentityStmt)&&pred.toString().contains("@param")) { 109 | //todo:如果是参数,我们应该去上一层中进行后向的数据流分析 110 | int parameterOrder = StringUtil.getParameterOrder(pred.toString()); 111 | //找寻本方法的调用语句 112 | CallSite preCallSite = getPreCallSite(callStack); 113 | if (preCallSite != null) { 114 | Unit invokeUnit = preCallSite.invokeUnit; 115 | ValueBox arg = null; 116 | if (invokeUnit instanceof AssignStmt) { 117 | arg = ((AssignStmt) invokeUnit).getInvokeExpr().getArgBox(parameterOrder); 118 | } else { 119 | arg = ((InvokeStmt) invokeUnit).getInvokeExpr().getArgBox(parameterOrder); 120 | } 121 | if (arg != null) 122 | inter_backward_dataflow(preCallSite.caller, preCallSite.invokeUnit, arg, depth--, copyAndRemoveLast(callStack)); 123 | }else { 124 | logger.info("the object from the param of the first method called"); 125 | } 126 | } 127 | }else { 128 | Event old_event = new Event(pred, valueBox); 129 | if(!processedEvent.contains(old_event)){ 130 | waitForProcessEvent.add(old_event); 131 | } 132 | } 133 | } 134 | } 135 | 136 | 137 | } 138 | public CallSite getPreCallSite(List callStack) { 139 | if(callStack.size()==0) 140 | return null; 141 | return callStack.get(callStack.size() - 1); 142 | 143 | } 144 | 145 | public List copyAndRemoveLast(List callStack){ 146 | List new_callStack=new ArrayList<>(callStack); 147 | new_callStack.remove(callStack.size()-1); 148 | return new_callStack; 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/dataflow/CallSite.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import soot.SootMethod; 4 | import soot.Unit; 5 | 6 | import java.util.Objects; 7 | 8 | 9 | public class CallSite { 10 | public SootMethod caller; 11 | public Unit invokeUnit; 12 | public int paramIndex=-1; 13 | 14 | public CallSite(SootMethod caller, Unit invokeUnit, int paramIndex) { 15 | this.caller = caller; 16 | this.invokeUnit = invokeUnit; 17 | this.paramIndex=paramIndex; 18 | } 19 | 20 | @Override 21 | public boolean equals(Object obj) { 22 | CallSite callSite = (CallSite) obj; 23 | return this.invokeUnit.toString().equals(callSite.invokeUnit.toString()) && this.caller.getSignature().equals(callSite.caller.getSignature()); 24 | } 25 | 26 | 27 | @Override 28 | public int hashCode() { 29 | return Objects.hash(caller, invokeUnit); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "CallSite{" + 35 | "caller=" + caller + 36 | ", invokeUnit=" + invokeUnit + 37 | '}'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dataflow/DBInfo.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import util.StringUtil; 6 | 7 | import java.awt.*; 8 | import java.util.HashSet; 9 | import java.util.Objects; 10 | 11 | 12 | public class DBInfo { 13 | //数据库信息描述 14 | 15 | public static final Logger logger= LoggerFactory.getLogger(DBInfo.class); 16 | 17 | 18 | public String dbName; 19 | public String tableName; 20 | public String tableInfo; 21 | public String uri; 22 | public boolean insert; 23 | 24 | public DBInfo(String dbName,String tableName,String tableInfo,String uri,boolean insert){ 25 | this.dbName=dbName; 26 | this.tableName=tableName; 27 | //需要对table info进行处理 28 | this.tableInfo=getStandardTableInfo(tableInfo,tableName); 29 | 30 | this.uri=uri; 31 | this.insert=insert; 32 | } 33 | 34 | private String getStandardTableInfo(String tableInfo,String tableName){ 35 | //我们需要对tableinfo的信息进行处理 36 | if(tableInfo=="") 37 | return ""; 38 | String reg="\\{.*\\}"; 39 | String string = StringUtil.findString(tableInfo, reg); 40 | if(string==null) 41 | return ""; 42 | 43 | String replace = string.replace("{", ""); 44 | String s = replace.replace("}", ""); 45 | String[] split = s.split("MS"); 46 | HashSet tableInfos=new HashSet<>(); 47 | for(String table_format:split) { 48 | if(tableName==""){ 49 | tableInfos.add(table_format); 50 | }else { 51 | if(table_format.contains(tableName)) 52 | tableInfos.add(table_format); 53 | } 54 | } 55 | return tableInfos.toString(); 56 | 57 | 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | return Objects.hash(dbName,tableName,tableInfo,uri); 63 | } 64 | 65 | @Override 66 | public boolean equals(Object obj) { 67 | if(!(obj instanceof DBInfo)) 68 | return false; 69 | DBInfo dbInfo = (DBInfo) obj; 70 | return dbInfo.tableName.equals(this.dbName)&&dbInfo.tableName.equals(this.tableName)&&dbInfo.tableInfo.equals(this.tableInfo)&& 71 | dbInfo.uri.equals(this.uri); 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | String msg="DB_NAME: "+dbName+'\t'+"TABLE_NAME: "+tableName+'\t'+"TABLE_INFO: "+tableInfo+"\t"+"URI: "+uri; 77 | return msg; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/dataflow/DatabaseUseDetector.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import constant.FileUsageDefinition; 4 | import constant.StrawPointsDefinition; 5 | import fj.P; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import soot.*; 9 | import soot.jimple.AssignStmt; 10 | import soot.jimple.DefinitionStmt; 11 | import soot.jimple.InvokeExpr; 12 | import soot.jimple.InvokeStmt; 13 | import soot.jimple.toolkits.callgraph.Edge; 14 | import soot.jimple.toolkits.callgraph.TransitiveTargets; 15 | import soot.toolkits.graph.BriefUnitGraph; 16 | import util.StringUtil; 17 | 18 | import java.util.*; 19 | 20 | 21 | public class DatabaseUseDetector extends AbstractDataFlow { 22 | 23 | public static final Logger logger = LoggerFactory.getLogger(DatabaseUseDetector.class); 24 | 25 | //直接使用数据库提供的API进行查询的 26 | public static final String DATA_BASE_QUERY_0 = "android.database.Cursor query(java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String)"; 27 | public static final String DATA_BASE_QUERY_1 = "android.database.Cursor query(java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String)"; 28 | public static final String DATA_BASE_QUERY_2 = "android.database.Cursor query(boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String)"; 29 | public static final String DATA_BASE_QUERY_3 = "android.database.Cursor query(boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String,android.os.CancellationSignal)"; 30 | public static final String DATA_BASE_QUERY_4 = "android.database.Cursor rawQuery(java.lang.String,java.lang.String[])"; 31 | public static final String DATA_BASE_QUERY_5 = "android.database.Cursor rawQuery(java.lang.String,java.lang.String[],android.os.CancellationSignal)"; 32 | public static final String DATA_BASE_QUERY_6 = "android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,java.lang.String,java.lang.String[],java.lang.String)"; 33 | public static final String DATA_BASE_QUERY_7 = "android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,java.lang.String,java.lang.String[],java.lang.String,android.os.CancellationSignal)"; 34 | public static final String DATA_BASE_QUERY_8 = "android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String)"; 35 | public static final String DATA_BASE_QUERY_9 = "android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase$CursorFactory,boolean,java.lang.String,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,java.lang.String,java.lang.String,java.lang.String,android.os.CancellationSignal)"; 36 | public static final String DATA_BASE_QUERY_10 = "void execSQL(java.lang.String)"; 37 | public static final String DATA_BASE_QUERY_11 = "void execSQL(java.lang.String,java.lang.Object[])"; 38 | 39 | //使用ContentResolver提供的API进行查询的 40 | public static final String CONTENT_RESOLVER_QUERY_0 = "android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String)"; 41 | public static final String CONTENT_RESOLVER_QUERY_1 = "android.database.Cursor query(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String,android.os.CancellationSignal)"; 42 | public static final String CONTENT_RESOLVER_QUERY_2 = "android.database.Cursor query(android.net.Uri,java.lang.String[],android.os.Bundle,android.os.CancellationSignal)"; 43 | public static final HashSet QUERY_API_SET = new HashSet<>(); 44 | 45 | static { 46 | QUERY_API_SET.add(DATA_BASE_QUERY_0); 47 | QUERY_API_SET.add(DATA_BASE_QUERY_1); 48 | QUERY_API_SET.add(DATA_BASE_QUERY_2); 49 | QUERY_API_SET.add(DATA_BASE_QUERY_3); 50 | QUERY_API_SET.add(DATA_BASE_QUERY_4); 51 | QUERY_API_SET.add(DATA_BASE_QUERY_5); 52 | QUERY_API_SET.add(DATA_BASE_QUERY_6); 53 | QUERY_API_SET.add(DATA_BASE_QUERY_7); 54 | QUERY_API_SET.add(DATA_BASE_QUERY_8); 55 | QUERY_API_SET.add(DATA_BASE_QUERY_9); 56 | QUERY_API_SET.add(DATA_BASE_QUERY_10); 57 | QUERY_API_SET.add(DATA_BASE_QUERY_11); 58 | 59 | QUERY_API_SET.add(CONTENT_RESOLVER_QUERY_0); 60 | QUERY_API_SET.add(CONTENT_RESOLVER_QUERY_1); 61 | QUERY_API_SET.add(CONTENT_RESOLVER_QUERY_2); 62 | } 63 | 64 | private String entryPoint; 65 | 66 | public static HashSet evailMethodSet=new HashSet<>(); 67 | 68 | public void setEntryPoint(String entryPoint) { 69 | this.entryPoint = entryPoint; 70 | } 71 | 72 | private HashMap> res = new HashMap<>(); 73 | 74 | public final Analyze analyze = new Analyze() { 75 | @Override 76 | public boolean caseAnalyze(Unit unit, SootMethod method, List callStack, HashSet points, ValueBox taintValueBox) { 77 | InvokeExpr invokeExpr = null; 78 | if (unit instanceof InvokeStmt) { 79 | InvokeStmt invokeStmt = (InvokeStmt) unit; 80 | invokeExpr = invokeStmt.getInvokeExpr(); 81 | 82 | } else if (unit instanceof AssignStmt) { 83 | AssignStmt assignStmt = (AssignStmt) unit; 84 | if (assignStmt.containsInvokeExpr()) { 85 | invokeExpr = assignStmt.getInvokeExpr(); 86 | } 87 | } 88 | 89 | if (invokeExpr == null) 90 | return false; 91 | if (!isCollectionSink(invokeExpr, taintValueBox))//判断是不是集合类型 92 | return false; 93 | DatabaseUseDetector.logger.info("detect risky method is used:{}", method.getSignature()); 94 | //是相关的API,我们还要判断方法体内是否存在相关的API 95 | for (Unit u : method.retrieveActiveBody().getUnits()) { 96 | if (u.toString().contains("")) { 97 | DatabaseUseDetector.logger.info("[Confirmed!]"); 98 | return true; 99 | } 100 | } 101 | return false; 102 | } 103 | 104 | boolean isCollectionSink(InvokeExpr invokeExpr, ValueBox taintValueBox) { 105 | 106 | int indexOf = invokeExpr.getArgs().indexOf(taintValueBox.getValue()); 107 | if (indexOf == -1) 108 | return false; 109 | if (indexOf == 0 && StringUtil.isMatch(invokeExpr.getMethod().getSignature(), StrawPointsDefinition.COLLECTIONS_STRAWPOINT_REGEX)) 110 | return true; 111 | return false; 112 | 113 | } 114 | }; 115 | 116 | public final ForwardDataFlow forwardDetector = new ForwardDataFlow(analyze); 117 | 118 | 119 | public HashSet getAnalysisResultOfEntry(String entryPoint) { 120 | if (res.containsKey(entryPoint)) 121 | return res.get(entryPoint); 122 | return null; 123 | } 124 | 125 | public void addPoint2Res(Unit unit, SootMethod method, String type, String uri) { 126 | if (!res.containsKey(entryPoint)) { 127 | res.put(entryPoint, new HashSet()); 128 | } 129 | Point point = new Point(unit, method, type, uri); 130 | res.get(entryPoint).add(point); 131 | } 132 | 133 | class DataBaseNameConstructor { 134 | 135 | public final FileNameConstructor fileNameConstructor = new FileNameConstructor(); 136 | 137 | public String construct(Unit u, SootMethod method, List callChain) { 138 | if (FileUsageDefinition.getDataBaseQueryAPIList().contains(method.getSubSignature())) { 139 | return databaseHandler(u, method, callChain); 140 | } else { 141 | return contentResolverHandler(u, method, callChain); 142 | } 143 | } 144 | 145 | public String databaseHandler(Unit u, SootMethod method, List callChain) { 146 | // logger.info("检测到ContentResolver查询语句:" + u); 147 | InvokeExpr invokeExpr = getInvokeExpr(u); 148 | String subSignature = invokeExpr.getMethod().getSubSignature(); 149 | Value tableValue = null; 150 | Value sql = null; 151 | if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_0) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_1)) { 152 | tableValue = invokeExpr.getArg(0); 153 | } else if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_2) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_3)) { 154 | tableValue = invokeExpr.getArg(1); 155 | } else if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_4) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_5)) { 156 | sql = invokeExpr.getArg(0); 157 | } else if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_6) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_7)) { 158 | sql = invokeExpr.getArg(1); 159 | } else if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_8) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_9)) { 160 | tableValue = invokeExpr.getArg(2); 161 | } else { 162 | sql = invokeExpr.getArg(0); 163 | } 164 | String tableName = null; 165 | if (tableValue != null) 166 | tableName = getValueOfObject(tableValue, u, method, callChain); 167 | if (sql != null) { 168 | String sqlStmt = getValueOfObject(sql, u, method, callChain); 169 | if (sqlStmt == null) 170 | return null; 171 | String[] s = sqlStmt.trim().split(" "); 172 | if (!s[0].toLowerCase().equals("select") || s.length < 3) 173 | return null; 174 | tableName = s[3]; 175 | } 176 | List useBoxes = invokeExpr.getUseBoxes(); 177 | String dataBaseName = fileNameConstructor.getRef2Str(useBoxes.get(useBoxes.size() - 1).getValue(), u, method,0); 178 | return String.format("Database: {}, Table: {}", dataBaseName, tableName); 179 | } 180 | 181 | public String contentResolverHandler(Unit u, SootMethod method, List callChain) { 182 | // logger.info("检测到ContentResolver查询语句:" + u); 183 | InvokeExpr invokeExpr = getInvokeExpr(u); 184 | if (invokeExpr == null) 185 | return null; 186 | Value arg = invokeExpr.getArg(0); 187 | return getValueOfObject(arg, u, method, callChain); 188 | } 189 | 190 | private String getValueOfObject(Value value, Unit u, SootMethod method, List callChain) { 191 | if (value instanceof Local) 192 | return fileNameConstructor.getRef2Str(value, u, method,0); 193 | return value.toString(); 194 | } 195 | } 196 | 197 | public final DataBaseNameConstructor constructor = new DataBaseNameConstructor(); 198 | 199 | class PreferencesNameConstructor{ 200 | 201 | public final FileNameConstructor fileNameConstructor = new FileNameConstructor(); 202 | 203 | public String construct(Unit u, SootMethod method, List callChain){ 204 | InvokeExpr invokeExpr = getInvokeExpr(u); 205 | if (invokeExpr == null) 206 | return ""; 207 | SootMethod m = invokeExpr.getMethod(); 208 | List useBoxes = invokeExpr.getUseBoxes(); 209 | String preferenceName=""; 210 | if(FileUsageDefinition.getSharedPreferencesReadAPIList().contains(m.getSignature())){ 211 | String str = fileNameConstructor.getRef2Str(useBoxes.get(useBoxes.size() - 1).getValue(), u, method,0); 212 | preferenceName=String.format("READ: %s",str); 213 | }else if(FileUsageDefinition.getSharedPreferencesWriteAPIList().contains(m.getSubSignature())){ 214 | String str = fileNameConstructor.getRef2Str(useBoxes.get(useBoxes.size() - 1).getValue(), u, method,0); 215 | preferenceName=String.format("WRITE: %s",str); 216 | } 217 | return preferenceName; 218 | } 219 | } 220 | 221 | public final PreferencesNameConstructor preferencesNameConstructor=new PreferencesNameConstructor(); 222 | 223 | @Override 224 | public boolean caseAnalyze(Unit unit, SootMethod method, List callStack, HashSet res, ValueBox taintValueBox) { 225 | InvokeExpr invokeExpr = getInvokeExpr(unit); 226 | if (invokeExpr == null) 227 | return false; 228 | SootMethod m = invokeExpr.getMethod(); 229 | if(QUERY_API_SET.contains(m.getSubSignature())&&(unit instanceof DefinitionStmt)){ 230 | return analyzeDataBaseOp(unit,method,callStack); 231 | }else if(FileUsageDefinition.getSharedPreferencesWriteAPIList().contains(m.getSubSignature())||FileUsageDefinition.getSharedPreferencesReadAPIList().contains(m.getSignature())){ 232 | return analyzePreferences(unit,method,callStack); 233 | } 234 | // evailMethodSet.add(method); 235 | // DefinitionStmt definitionStmt = (DefinitionStmt) unit; 236 | // ValueBox valueBox = definitionStmt.getDefBoxes().get(0); 237 | // forwardDetector.inter_forward_dataflow(method, unit, valueBox, -1, 0, callStack); 238 | // if (!forwardDetector.getFindFlag()) 239 | // return false; 240 | // //将找到的结果记录下来 241 | // String dataBaseInfo = constructor.construct(unit, method, callStack); 242 | // addPoint2Res(unit, method, "COLLECTIONS", dataBaseInfo); 243 | // //分析完需要讲前向分析的标志位设置为false 244 | // forwardDetector.setFindFlag(false); 245 | return true; 246 | } 247 | 248 | public boolean analyzeDataBaseOp(Unit unit, SootMethod method, List callStack){ 249 | DefinitionStmt definitionStmt = (DefinitionStmt) unit; 250 | ValueBox valueBox = definitionStmt.getDefBoxes().get(0); 251 | boolean flag=false; 252 | forwardDetector.inter_forward_dataflow(method, unit, valueBox, -1, 0, callStack); 253 | if (!forwardDetector.getFindFlag()) 254 | flag=true; 255 | //将找到的结果记录下来 256 | String dataBaseInfo = constructor.construct(unit, method, callStack); 257 | if(!flag){ 258 | dataBaseInfo=String.format("ONLY_QUERY: %s",dataBaseInfo); 259 | }else { 260 | dataBaseInfo=String.format("DOS_EFFECT_SIDE: %s",dataBaseInfo); 261 | } 262 | addPoint2Res(unit, method, "DATA_BASE", dataBaseInfo); 263 | //分析完需要讲前向分析的标志位设置为false 264 | forwardDetector.setFindFlag(false); 265 | return flag; 266 | } 267 | 268 | public boolean analyzePreferences(Unit unit, SootMethod method, List callStack){ 269 | String preferencesInfo = preferencesNameConstructor.construct(unit, method, callStack); 270 | addPoint2Res(unit,method,"SHARED_PREFERENCES",preferencesInfo); 271 | return true; 272 | } 273 | 274 | 275 | 276 | 277 | 278 | public void inter_forward(SootMethod method, int depth, List callStack) { 279 | if (depth > MAX_DEPTH) { 280 | return; 281 | } 282 | if (method == null) 283 | return; 284 | if (isSystemClass(method.getDeclaringClass())) 285 | return; 286 | if (isMethodCalled(callStack, method)) 287 | return; 288 | if (!method.isConcrete()) 289 | return; 290 | if(method.getName().equals("run")||method.getName().equals("call")) 291 | return; 292 | Iterator iterator = Scene.v().getCallGraph().edgesOutOf(method); 293 | while (iterator.hasNext()){ 294 | Edge edge = iterator.next(); 295 | caseAnalyze(edge.srcUnit(), edge.src(),callStack,null,null); 296 | List temp_call_stack = new ArrayList<>(callStack); 297 | temp_call_stack.add(new CallSite(method, edge.srcUnit(), -1)); 298 | inter_forward(edge.tgt(),depth+1,temp_call_stack); 299 | } 300 | // for (Unit u : method.retrieveActiveBody().getUnits()) { 301 | // InvokeExpr invokeExpr = getInvokeExpr(u); 302 | // if (invokeExpr != null) { 303 | // if(caseAnalyze(u, method, callStack, null, null)) 304 | // return; 305 | // HashSet methods=new HashSet<>(); 306 | // Iterator edgeIterator = Scene.v().getCallGraph().edgesOutOf(u); 307 | // while (edgeIterator.hasNext()){ 308 | // SootMethod sootMethod = edgeIterator.next().tgt(); 309 | // methods.add(sootMethod); 310 | // 311 | // } 312 | // for (SootMethod m : methods) { 313 | // List temp_call_stack = new ArrayList<>(callStack); 314 | // temp_call_stack.add(new CallSite(method, u, -1)); 315 | // inter_forward(m, depth + 1, temp_call_stack); 316 | // } 317 | // } 318 | // } 319 | } 320 | 321 | public SootMethod getConcreteMethod(SootMethod m) { 322 | //该方法是Abstract或者接口方法,找到该方法的实际的方法 323 | FastHierarchy fastHierarchy = Scene.v().getOrMakeFastHierarchy(); 324 | Collection subclassesOf = fastHierarchy.getSubclassesOf(m.getDeclaringClass()); 325 | for (SootClass sootClass : subclassesOf) { 326 | if (sootClass.declaresMethod(m.getSubSignature())) { 327 | return sootClass.getMethodUnsafe(m.getSubSignature()); 328 | } 329 | } 330 | return m; 331 | } 332 | 333 | 334 | 335 | 336 | 337 | 338 | } 339 | -------------------------------------------------------------------------------- /src/main/java/dataflow/Event.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import soot.Unit; 4 | import soot.ValueBox; 5 | import soot.tagkit.Tag; 6 | 7 | import java.util.Objects; 8 | 9 | public class Event { 10 | 11 | public Unit unit; 12 | public ValueBox valueBox; 13 | public String accessPath=""; 14 | 15 | public Event(Unit unit, ValueBox valueBox){ 16 | this.unit=unit; 17 | this.valueBox=valueBox; 18 | Tag accessPath = valueBox.getTag("AccessPath"); 19 | if(accessPath!=null){ 20 | this.accessPath=((AccessPathTag)accessPath).getFieldChain(); 21 | } 22 | } 23 | 24 | 25 | 26 | public Event(Unit unit,ValueBox valueBox,String accessPath){ 27 | this.unit=unit; 28 | this.valueBox=valueBox; 29 | this.accessPath=accessPath; 30 | } 31 | 32 | public boolean equals(Event event){ 33 | return this.unit.equals(event.unit)&& 34 | this.valueBox.getValue().equals(event.valueBox.getValue())&& 35 | this.accessPath.equals(event.accessPath); 36 | } 37 | 38 | @Override 39 | public boolean equals(Object obj) { 40 | if(!(obj instanceof Event)) 41 | return false; 42 | Event event=(Event) obj; 43 | return equals(event); 44 | 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | return Objects.hash(unit,valueBox.getValue().toString(),accessPath); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dataflow/EventQueue.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | 4 | import java.util.Iterator; 5 | import java.util.LinkedList; 6 | import java.util.Queue; 7 | 8 | public class EventQueue { 9 | 10 | private Queue eventQueue = new LinkedList<>(); 11 | 12 | public boolean isEmpty() { 13 | return eventQueue.isEmpty(); 14 | } 15 | 16 | public boolean add(Event event) { 17 | return eventQueue.add(event); 18 | } 19 | 20 | public Event poll() { 21 | return eventQueue.poll(); 22 | } 23 | 24 | public boolean contains(Event event) { 25 | return eventQueue.contains(event); 26 | } 27 | 28 | public Queue getEventQueue() { 29 | return eventQueue; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object obj) { 34 | if (!(obj instanceof EventQueue)) 35 | return false; 36 | 37 | EventQueue eventQueue = (EventQueue) obj; 38 | return this.eventQueue.equals(eventQueue.getEventQueue()); 39 | } 40 | 41 | @Override 42 | public int hashCode() { 43 | return eventQueue.hashCode(); 44 | } 45 | 46 | public int size(){ 47 | return eventQueue.size(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/dataflow/FileInfo.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import constant.FileUsageDefinition; 4 | 5 | import java.util.Objects; 6 | 7 | 8 | public class FileInfo { 9 | 10 | public String type; 11 | public String name; 12 | public String mode; 13 | public String entry; 14 | 15 | public FileInfo(String type,String name,String mode,String entry){ 16 | this.type=type; 17 | this.name=name; 18 | this.mode=mode; 19 | this.entry=entry; 20 | 21 | } 22 | 23 | @Override 24 | public boolean equals(Object obj) { 25 | if(obj==null) 26 | return false; 27 | FileInfo fileInfo = (FileInfo) obj; 28 | if(!fileInfo.name.equals(name)) 29 | return false; 30 | if(!fileInfo.type.equals(type)) 31 | return false; 32 | if(!fileInfo.mode.equals(mode)) 33 | return false; 34 | if(!fileInfo.entry.equals(entry)) 35 | return false; 36 | return true; 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | return Objects.hash(type,name,mode,entry); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/dataflow/FileType.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | public enum FileType { 4 | DATA_BASE,SHARED_PREFERENCES,COLLECTION,FILE,LONG_LIVE_OBJECT,EDITOR,UN_DEFINE 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dataflow/FileUseDetector.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import constant.FileUsageDefinition; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import soot.*; 7 | import soot.jimple.AssignStmt; 8 | import soot.jimple.IdentityStmt; 9 | import soot.jimple.InvokeExpr; 10 | import util.Log; 11 | 12 | import java.io.File; 13 | import java.util.*; 14 | 15 | 16 | public class FileUseDetector extends AbstractDataFlow { 17 | 18 | public static final Logger logger = LoggerFactory.getLogger(FileUseDetector.class); 19 | 20 | enum OpType { 21 | SHARED_PREFERENCES_READ, SHARED_PREFERENCES_WRITE, DATA_BASE_QUERY, CONTENT_RESOLVER_OP, FILE_INPUT_STREAM_INIT, FILE_OUTPUT_STREAM_INIT, NULL 22 | } 23 | 24 | private HashSet fileInfos = new HashSet<>(); 25 | 26 | private String mode; 27 | private String type; 28 | private String name; 29 | private String entry; 30 | 31 | private String packageName; 32 | 33 | public void setPackageName(String packageName) { 34 | this.packageName = packageName; 35 | } 36 | 37 | @Override 38 | public boolean caseAnalyze(Unit unit, SootMethod method, List callStack, HashSet res, ValueBox taintValueBox) { 39 | InvokeExpr invokeExpr = getInvokeExpr(unit); 40 | if (invokeExpr == null) 41 | return false; 42 | SootMethod m = invokeExpr.getMethod(); 43 | cleanFileInfo(); 44 | switch (getType(m)) { 45 | case FILE_INPUT_STREAM_INIT: 46 | fileHandler(unit, method, callStack, 0); 47 | break; 48 | case FILE_OUTPUT_STREAM_INIT: 49 | fileHandler(unit, method, callStack, 1); 50 | break; 51 | case SHARED_PREFERENCES_READ: 52 | sharedPreferencesHandler(unit, method, callStack, 0); 53 | break; 54 | case SHARED_PREFERENCES_WRITE: 55 | sharedPreferencesHandler(unit, method, callStack, 1); 56 | break; 57 | case DATA_BASE_QUERY: 58 | databaseHandler(unit, method, callStack); 59 | break; 60 | case CONTENT_RESOLVER_OP: 61 | contentResolverHandler(unit, method, callStack); 62 | break; 63 | default: 64 | break; 65 | } 66 | if (callStack.size() == 0) { 67 | this.entry = method.getSignature(); 68 | } else { 69 | this.entry = callStack.get(0).caller.getSignature(); 70 | } 71 | addFileInfo(); 72 | return false; 73 | } 74 | 75 | private void cleanFileInfo() { 76 | this.type = null; 77 | this.name = null; 78 | this.mode = null; 79 | this.entry = null; 80 | } 81 | 82 | private void addFileInfo() { 83 | if (this.name == null) 84 | return; 85 | if (this.name.contains("package")) { 86 | this.name=this.name.replaceAll("package", this.packageName); 87 | } 88 | this.name=this.name.replaceAll("\"",""); 89 | 90 | fileInfos.add(new FileInfo(type, name, mode, entry)); 91 | } 92 | 93 | public void fileHandler(Unit u, SootMethod method, List callChain, int mode) { 94 | // logger.info("检测到在{} 使用了文件", u); 95 | InvokeExpr invokeExpr = getInvokeExpr(u); 96 | if (invokeExpr == null) 97 | return; 98 | String fileName; 99 | fileName = getValueOfObject(invokeExpr.getArg(0), u, method, callChain); 100 | if (fileName.contains("FAILED")) 101 | return; 102 | this.name = fileName; 103 | if (mode == 0) { 104 | this.mode = "READ"; 105 | } else { 106 | this.mode = "WRITE"; 107 | } 108 | this.type = "FILE"; 109 | // logger.info("恢复的文件名是:" + fileName); 110 | } 111 | 112 | public void sharedPreferencesHandler(Unit u, SootMethod method, List callChain, int mode) { 113 | InvokeExpr invokeExpr = getInvokeExpr(u); 114 | if (invokeExpr == null) 115 | return; 116 | List useBoxes = invokeExpr.getUseBoxes(); 117 | this.type = "SAHREDPREFERENCES"; 118 | String preferenceName = null; 119 | if (mode == 0) { 120 | this.mode = "READ"; 121 | preferenceName = nameConstructor.getRef2Str(useBoxes.get(useBoxes.size() - 1).getValue(), u, method,0); 122 | } else { 123 | this.mode = "WRITE"; 124 | preferenceName = nameConstructor.getRef2Str(useBoxes.get(useBoxes.size() - 1).getValue(), u, method,0); 125 | } 126 | if (preferenceName != null && preferenceName.contains("FAILED")) 127 | return; 128 | this.name = preferenceName; 129 | } 130 | 131 | public void databaseHandler(Unit u, SootMethod method, List callChain) { 132 | // logger.info("检测到ContentResolver查询语句:" + u); 133 | InvokeExpr invokeExpr = getInvokeExpr(u); 134 | String subSignature = invokeExpr.getMethod().getSubSignature(); 135 | Value tableValue = null; 136 | Value sql = null; 137 | if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_0) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_1)) { 138 | tableValue = invokeExpr.getArg(0); 139 | } else if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_2) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_3)) { 140 | tableValue = invokeExpr.getArg(1); 141 | } else if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_4) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_5)) { 142 | sql = invokeExpr.getArg(0); 143 | } else if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_6) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_7)) { 144 | sql = invokeExpr.getArg(1); 145 | } else if (subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_8) || subSignature.equals(FileUsageDefinition.DATA_BASE_QUERY_9)) { 146 | tableValue = invokeExpr.getArg(2); 147 | } else { 148 | sql = invokeExpr.getArg(0); 149 | } 150 | String tableName = null; 151 | if (tableValue != null) 152 | tableName = getValueOfObject(tableValue, u, method, callChain); 153 | if (sql != null) { 154 | String sqlStmt = getValueOfObject(sql, u, method, callChain); 155 | if (sqlStmt == null) 156 | return; 157 | String[] s = sqlStmt.trim().split(" "); 158 | if (!s[0].toLowerCase().equals("select") || s.length < 3) 159 | return; 160 | tableName = s[3]; 161 | } 162 | List useBoxes = invokeExpr.getUseBoxes(); 163 | String dataBaseName = nameConstructor.getRef2Str(useBoxes.get(useBoxes.size() - 1).getValue(), u, method,0); 164 | if (tableName == null || dataBaseName == null) 165 | return; 166 | 167 | if (dataBaseName.contains("FAILED") || tableName.contains("FAILED")) 168 | return; 169 | this.name = String.format("DataBase: %s, Table: %s", dataBaseName, tableName); 170 | this.mode = "Read"; 171 | this.type = "DATABASE"; 172 | // logger.info("数据库:" + dataBaseName + " " + tableName); 173 | } 174 | 175 | public void contentResolverHandler(Unit u, SootMethod method, List callChain) { 176 | // logger.info("检测到ContentResolver查询语句:" + u); 177 | InvokeExpr invokeExpr = getInvokeExpr(u); 178 | if (invokeExpr == null) 179 | return; 180 | Value arg = invokeExpr.getArg(0); 181 | String uri = getValueOfObject(arg, u, method, callChain); 182 | 183 | if (uri == null) 184 | return; 185 | if (uri.contains("FAILED")) 186 | return; 187 | this.name = uri; 188 | this.type = "CONTENT_RESOLVER"; 189 | this.mode = "READ"; 190 | // logger.info("Uri信息是:" + uri); 191 | } 192 | 193 | 194 | private final FileNameConstructor nameConstructor = new FileNameConstructor(); 195 | 196 | private String getValueOfObject(Value value, Unit u, SootMethod method, List callChain) { 197 | if (value instanceof Local) 198 | return nameConstructor.getRef2Str(value, u, method,0); 199 | return value.toString(); 200 | } 201 | 202 | //判断调用语句的类型 203 | public static OpType getType(SootMethod method) { 204 | if (FileUsageDefinition.getSharedPreferencesReadAPIList().contains(method.getSignature())) 205 | return OpType.SHARED_PREFERENCES_READ; 206 | if (FileUsageDefinition.getSharedPreferencesWriteAPIList().contains(method.getSubSignature())) 207 | return OpType.SHARED_PREFERENCES_WRITE; 208 | if (FileUsageDefinition.getDataBaseQueryAPIList().contains(method.getSubSignature())) 209 | return OpType.DATA_BASE_QUERY; 210 | if (FileUsageDefinition.getContentResolverQueryAPIList().contains(method.getSubSignature())) 211 | return OpType.CONTENT_RESOLVER_OP; 212 | if (FileUsageDefinition.getInputStreamConstructorList().contains(method.getSignature())) 213 | return OpType.FILE_INPUT_STREAM_INIT; 214 | if (FileUsageDefinition.getOutputStreamConstructorList().contains(method.getSignature())) 215 | return OpType.FILE_OUTPUT_STREAM_INIT; 216 | return OpType.NULL; 217 | } 218 | 219 | public void inter_forward(SootMethod method, int depth, List callStack) { 220 | if (depth > MAX_DEPTH) 221 | return; 222 | if (isSystemClass(method.getDeclaringClass())) 223 | return; 224 | if (isMethodCalled(callStack, method)) 225 | return; 226 | if (!method.isConcrete()) 227 | return; 228 | for (Unit u : method.retrieveActiveBody().getUnits()) { 229 | InvokeExpr invokeExpr = getInvokeExpr(u); 230 | if (invokeExpr != null) { 231 | caseAnalyze(u, method, callStack, null, null); 232 | for (SootMethod m : getMethodFromCG(u)) { 233 | List temp_call_stack = new ArrayList<>(callStack); 234 | temp_call_stack.add(new CallSite(method, u, -1)); 235 | inter_forward(m, depth + 1, temp_call_stack); 236 | } 237 | } 238 | } 239 | } 240 | 241 | 242 | 243 | public void writeLog() { 244 | for (FileInfo fileInfo : fileInfos) { 245 | Log.write(Log.Mode.FILE_INFO, fileInfo.type, fileInfo.name, fileInfo.mode, fileInfo.entry); 246 | } 247 | fileInfos.clear(); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/main/java/dataflow/Icc.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import soot.Local; 6 | import soot.SootMethod; 7 | import soot.Unit; 8 | import soot.Value; 9 | import soot.jimple.*; 10 | import soot.jimple.toolkits.ide.icfg.JimpleBasedInterproceduralCFG; 11 | import soot.toolkits.graph.BriefUnitGraph; 12 | 13 | import java.util.*; 14 | 15 | 16 | public class Icc { 17 | /* 18 | model组件间通信,目的是给定一条icc指令,如startActivity,我们的目的是,跳转到哪一个组件 19 | */ 20 | 21 | public static final Logger logger= LoggerFactory.getLogger(Icc.class); 22 | 23 | 24 | //过程间分析Intent的来源 25 | 26 | private JimpleBasedInterproceduralCFG icfg=null; 27 | 28 | public static final String STRAT_ACTIVITY="void startActivity(android.content.Intent)"; 29 | public static final String STRAT_ACTIVITY_IF_NEED="boolean startActivityIfNeeded(android.content.Intent,int)"; 30 | 31 | public static final String START_ACTIVITY_FRAGMENT_0="void startActivityFromFragment(androidx.fragment.app.Fragment,android.content.Intent,int)"; 32 | public static final String START_ACTIVITY_FRAGMENT_1="void startActivityFromFragment(androidx.fragment.app.Fragment,android.content.Intent,int,android.os.Bundle)"; 33 | 34 | public static final String START_ACTIVITY_FOR_RESULT="void startActivityForResult(android.content.Intent,int,android.os.Bundle)"; 35 | 36 | public static final String START_SERVICE="android.content.ComponentName startService(android.content.Intent)"; 37 | 38 | public static final String SEND_BROADCAST_0="void sendBroadcast(android.content.Intent)"; 39 | public static final String SEND_BROADCAST_1="void sendBroadcast(android.content.Intent,java.lang.String)"; 40 | 41 | public static final String[] iccAPI={STRAT_ACTIVITY,STRAT_ACTIVITY_IF_NEED,START_ACTIVITY_FRAGMENT_0,START_ACTIVITY_FRAGMENT_1,START_ACTIVITY_FOR_RESULT, 42 | START_SERVICE,SEND_BROADCAST_0,SEND_BROADCAST_1}; 43 | 44 | public static final List iccAPIList= Arrays.asList(iccAPI); 45 | 46 | private static final String GET_INTENT = "android.content.Intent getIntent()"; 47 | 48 | 49 | public static final String INTENT_INIT_0="()>"; 50 | public static final String INTENT_INIT_1="(java.lang.String)>"; 51 | public static final String INTENT_INIT_2="(java.lang.String,android.net.Uri)>"; 52 | public static final String INTENT_INIT_3="(android.content.Intent)>"; 53 | public static final String INTENT_INIT_4="(android.content.Context,java.lang.Class)>"; 54 | public static final String INTENT_INIT_5="(java.lang.String,android.net.Uri,android.content.Context,java.lang.Class)>"; 55 | 56 | 57 | 58 | public static final String COMPONENT_INIT_0="(java.lang.String,java.lang.String)>"; 59 | public static final String COMPONENT_INIT_1="(android.content.Context,java.lang.Class)>"; 60 | 61 | public static final String INTENT_SET_1=""; 62 | public static final String INTENT_SET_2=""; 63 | public static final String INTENT_SET_3=""; 64 | public static final String INTENT_SET_4=""; 65 | 66 | public static final String[] INTENT_CONSTRUCT_API={INTENT_INIT_4,INTENT_INIT_5,INTENT_SET_1,INTENT_SET_2,INTENT_SET_3,INTENT_SET_4}; 67 | 68 | public static final List INTENT_CONSTRUCT_API_LIST=Arrays.asList(INTENT_CONSTRUCT_API); 69 | 70 | 71 | public Icc(JimpleBasedInterproceduralCFG icfg){ 72 | this.icfg=icfg; 73 | } 74 | 75 | public HashSet getJumpComponentInfo(Unit jumpUnit){ 76 | InvokeExpr invokeExpr = getInvokeExpr(jumpUnit); 77 | //我们需要确定是那种类型的组件间通信 78 | String subSignature = invokeExpr.getMethod().getSubSignature(); 79 | if(iccAPIList.contains(subSignature)){ 80 | if(START_ACTIVITY_FRAGMENT_1.equals(subSignature)||START_ACTIVITY_FRAGMENT_0.equals(subSignature)){ 81 | return getComponentInfo(jumpUnit,invokeExpr.getArg(1)); 82 | }else { 83 | return getComponentInfo(jumpUnit,invokeExpr.getArg(0)); 84 | } 85 | }else { 86 | logger.warn("This is not an ICC API Invoke!"); 87 | } 88 | 89 | return null; 90 | } 91 | 92 | public HashSet getComponentInfo(Unit curUnit, Value curValue){ 93 | 94 | HashSet res=new HashSet<>(); 95 | //该方法返回找到的Intent的 96 | 97 | HashSet intentInitUnit = getIntentInitUnit(curUnit, curValue, 0); 98 | if(intentInitUnit.isEmpty()){ 99 | logger.info("Can't find the intent Created"); 100 | return res; 101 | } 102 | 103 | //为所有的new出来的Intent,寻找他们的初始化方法 104 | 105 | for(Unit initUnit:intentInitUnit){ 106 | res.addAll(getJumpClassName(initUnit)); 107 | } 108 | 109 | return res; 110 | 111 | } 112 | 113 | public HashSet getJumpClassName(Unit initUnit){ 114 | HashSet res=new HashSet<>(); 115 | Value leftOp = ((AssignStmt) initUnit).getLeftOp(); 116 | //我们要找到intent所有可能指定它要跳转的组件的信息地方 117 | SootMethod curMethod = icfg.getMethodOf(initUnit); 118 | for(Unit u:curMethod.retrieveActiveBody().getUnits()){ 119 | if(u instanceof InvokeStmt){ 120 | InvokeExpr invokeExpr = ((InvokeStmt) u).getInvokeExpr(); 121 | String signature = invokeExpr.getMethod().getSignature(); 122 | if((invokeExpr instanceof InstanceInvokeExpr)&&INTENT_CONSTRUCT_API_LIST.contains(signature) 123 | &&((InstanceInvokeExpr)invokeExpr).getBase().equals(leftOp)){ 124 | if(signature.equals(INTENT_INIT_4)||signature.equals(INTENT_SET_4)){ 125 | Value classValue = invokeExpr.getArg(1); 126 | if(classValue instanceof ClassConstant){ 127 | String className = ((ClassConstant) classValue).getValue(); 128 | String s = className.substring(1,className.length()-1).replaceAll("/", "."); 129 | res.add(s); 130 | } 131 | }else if(signature.equals(INTENT_INIT_5)){ 132 | Value classValue = invokeExpr.getArg(3); 133 | if(classValue instanceof ClassConstant){ 134 | String className = ((ClassConstant) classValue).getValue(); 135 | String s = className.substring(1,className.length()-1).replaceAll("/", "."); 136 | res.add(s); 137 | } 138 | }else if(signature.equals(INTENT_SET_2)||signature.equals(INTENT_SET_3)){ 139 | Value classValue = invokeExpr.getArg(1); 140 | if(classValue instanceof StringConstant){ 141 | res.add(classValue.toString().replaceAll("\"","")); 142 | } 143 | 144 | }else { 145 | Value componentValue = invokeExpr.getArg(0); 146 | //我们假定component是在本方法中创建的 147 | //因此这里可能存在漏报 148 | res.addAll(getComponentInitInfo(componentValue,curMethod)); 149 | 150 | } 151 | } 152 | } 153 | } 154 | return res; 155 | } 156 | 157 | private HashSet getComponentInitInfo(Value component,SootMethod method){ 158 | HashSet res=new HashSet<>(); 159 | for(Unit u:method.retrieveActiveBody().getUnits()){ 160 | if(u instanceof InvokeStmt){ 161 | InvokeStmt invokeStmt = (InvokeStmt) u; 162 | InvokeExpr invokeExpr = invokeStmt.getInvokeExpr(); 163 | String signature = invokeExpr.getMethod().getSignature(); 164 | if(!(invokeExpr instanceof InstanceInvokeExpr)) 165 | continue; 166 | Value base = ((InstanceInvokeExpr) invokeExpr).getBase(); 167 | if(!base.equals(component)) 168 | continue; 169 | if(signature.equals(COMPONENT_INIT_0)){ 170 | Value classValue = invokeExpr.getArg(1); 171 | if(classValue instanceof StringConstant){ 172 | res.add(classValue.toString().replaceAll("\"","")); 173 | } 174 | }else if(signature.equals(COMPONENT_INIT_1)){ 175 | Value classValue = invokeExpr.getArg(1); 176 | if(classValue instanceof ClassConstant){ 177 | String className = ((ClassConstant) classValue).getValue(); 178 | String s = className.substring(1,className.length()-1).replaceAll("/", "."); 179 | res.add(s); 180 | } 181 | } 182 | } 183 | } 184 | return res; 185 | } 186 | 187 | 188 | 189 | //该方法是去寻找所有intent被new的初始位置 190 | //Intent这个对象可能来自参数,可能来自返回值,也可能来自new 191 | //但是我们这里认为最终所有的Intent都是来自new出来的,因此我们去追踪他们new出来的地方 192 | public HashSet getIntentInitUnit(Unit curUnit,Value value,int depth){ 193 | HashSet res=new HashSet<>(); 194 | 195 | if(depth>10){ 196 | logger.info("Exceed the max depth"); 197 | return res; 198 | } 199 | SootMethod curMethod = icfg.getMethodOf(curUnit); 200 | 201 | HashSet defUnits = getAllDirectDefUnit(value, curUnit, curMethod); 202 | if(defUnits.isEmpty()){ 203 | logger.info("Can't find definition for {}",value); 204 | return res; 205 | } 206 | 207 | for(Unit defUnit:defUnits){ 208 | //找到所有的定义语句, 209 | if(defUnit instanceof IdentityStmt){ 210 | //如果是参数的话,我们直接找他的调用者 211 | Collection callers = icfg.getCallersOf(curMethod); 212 | if(callers.isEmpty()){ 213 | logger.info("Can't find caller for {}",curMethod.getSignature()); 214 | continue; 215 | } 216 | Value rightOp = ((IdentityStmt) defUnit).getRightOp(); 217 | if (rightOp instanceof ThisRef) 218 | continue; 219 | ParameterRef parameterRef = (ParameterRef) rightOp; 220 | int index = parameterRef.getIndex(); 221 | for(Unit callSite:callers){ 222 | //找到调用点对应的实参 223 | InvokeExpr invokeExpr = getInvokeExpr(callSite); 224 | res.addAll(getIntentInitUnit(callSite,invokeExpr.getArg(index),depth+1)); 225 | } 226 | }else{ 227 | //如果是来自返回值,new,或者变量赋值 228 | AssignStmt assignStmt = (AssignStmt) defUnit; 229 | Value rightOp = assignStmt.getRightOp(); 230 | if(rightOp instanceof NewExpr){ 231 | //如果是new出来的记下来 232 | res.add(defUnit); 233 | }else if(rightOp instanceof InvokeExpr){ 234 | //如果是调用返回值 235 | Collection callees = icfg.getCalleesOfCallAt(defUnit); 236 | for(SootMethod callee:callees){ 237 | String subSignature = callee.getSubSignature(); 238 | if(subSignature.equals(GET_INTENT)) { 239 | logger.info("Intent is get by ICC"); 240 | continue; 241 | } 242 | if(isSystemClass(callee.getDeclaringClass().getName())){ 243 | //判断是不是系统类 244 | logger.info("Intent is get by android api: {}",callee.getSignature()); 245 | continue; 246 | } 247 | 248 | if(!callee.isConcrete()||callee.isPhantom()) 249 | continue; 250 | for(Unit u:callee.retrieveActiveBody().getUnits()){ 251 | if(u instanceof ReturnStmt){ 252 | ReturnStmt returnStmt = (ReturnStmt) u; 253 | Value op = returnStmt.getOp(); 254 | if(op instanceof NullConstant) 255 | continue; 256 | res.addAll(getIntentInitUnit(returnStmt,op,depth+1)); 257 | } 258 | } 259 | } 260 | }else if(rightOp instanceof Local){ 261 | //如果是局部变量赋值 262 | res.addAll(getIntentInitUnit(defUnit,rightOp,depth+1)); 263 | } 264 | 265 | } 266 | 267 | } 268 | 269 | 270 | 271 | 272 | return res; 273 | 274 | } 275 | 276 | 277 | public static HashSet getAllDirectDefUnit(Value value,Unit curUnit,SootMethod method){ 278 | //找到指定标量的所有直接赋值语句 279 | HashSet res=new HashSet<>(); 280 | BriefUnitGraph graph = new BriefUnitGraph(method.retrieveActiveBody()); 281 | Queue queue=new LinkedList<>(); 282 | queue.add(curUnit); 283 | HashSet visited=new HashSet<>(); 284 | while (!queue.isEmpty()){ 285 | Unit poll = queue.poll(); 286 | visited.add(poll); 287 | for(Unit preUnit:graph.getPredsOf(poll)){ 288 | if(visited.contains(preUnit)) 289 | continue; 290 | if(isValueDefinedInUnit(value,preUnit)){ 291 | res.add(preUnit); 292 | continue; 293 | } 294 | queue.add(preUnit); 295 | } 296 | } 297 | return res; 298 | } 299 | 300 | public static boolean isValueDefinedInUnit(Value value,Unit u){ 301 | if (u instanceof AssignStmt) { 302 | AssignStmt assignStmt = (AssignStmt) u; 303 | if(assignStmt.getLeftOp().equals(value)) 304 | return true; 305 | } 306 | if(u instanceof IdentityStmt){ 307 | IdentityStmt identityStmt = (IdentityStmt) u; 308 | if(identityStmt.getLeftOp().equals(value)) 309 | return true; 310 | } 311 | return false; 312 | 313 | } 314 | 315 | 316 | 317 | private InvokeExpr getInvokeExpr(Unit u){ 318 | if(u instanceof InvokeStmt){ 319 | InvokeStmt invokeStmt = (InvokeStmt) u; 320 | return invokeStmt.getInvokeExpr(); 321 | } 322 | if(u instanceof AssignStmt){ 323 | AssignStmt assignStmt = (AssignStmt) u; 324 | if(assignStmt.containsInvokeExpr()) 325 | return assignStmt.getInvokeExpr(); 326 | } 327 | return null; 328 | } 329 | 330 | public static boolean isSystemClass(String clsName) { 331 | if (clsName.startsWith("java.") || clsName.startsWith("javax.")) 332 | return true; 333 | if (clsName.startsWith("android.") || clsName.startsWith("androidx.") || clsName.startsWith("com.google.")) 334 | return true; 335 | if (clsName.startsWith("jdk")) 336 | return true; 337 | if (clsName.startsWith("sun.")) 338 | return true; 339 | if (clsName.startsWith("org.omg") || clsName.startsWith("org.w3c.dom")) 340 | return true; 341 | return false; 342 | } 343 | 344 | 345 | 346 | 347 | 348 | 349 | } 350 | -------------------------------------------------------------------------------- /src/main/java/dataflow/Point.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import soot.SootMethod; 4 | import soot.Unit; 5 | 6 | import java.util.Objects; 7 | 8 | 9 | public class Point { 10 | public String unit; 11 | public String method; 12 | public String type; 13 | public String otherMsg; 14 | 15 | public Point(Unit unit, SootMethod method, String type) { 16 | this.unit = unit.toString(); 17 | this.method = method.getSignature(); 18 | this.type = type; 19 | } 20 | 21 | public Point(Unit unit,SootMethod method,String type,String otherMsg){ 22 | this.unit = unit.toString(); 23 | this.method = method.getSignature(); 24 | this.type = type; 25 | this.otherMsg=otherMsg; 26 | 27 | 28 | } 29 | 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | Point point = (Point) obj; 34 | return point.method.equals(this.method) && point.unit.equals(this.unit) && point.type.equals(this.type)&&point.otherMsg.equals(this.otherMsg); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return Objects.hash(method, unit, type,otherMsg); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/dataflow/RuleChecker.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import soot.SootMethod; 4 | import soot.Unit; 5 | import soot.ValueBox; 6 | 7 | 8 | public interface RuleChecker { 9 | //判断数据流如到某个调用是不是sink 10 | boolean isSink(Unit checkedUnit, ValueBox taintValueBox); 11 | 12 | //判断某个依赖于控制流的调用是不是sink 13 | boolean isDependConditionSink(SootMethod method,SootMethod caller); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dataflow/SensitiveActionChecker.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import cg.CallGraphUtils; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import soot.Scene; 7 | import soot.SootMethod; 8 | import soot.Unit; 9 | import soot.ValueBox; 10 | import soot.jimple.AssignStmt; 11 | import soot.jimple.InvokeExpr; 12 | import soot.jimple.InvokeStmt; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.FileReader; 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.HashSet; 19 | import java.util.List; 20 | 21 | 22 | public class SensitiveActionChecker implements RuleChecker { 23 | 24 | private Logger logger= LoggerFactory.getLogger(SensitiveActionChecker.class); 25 | 26 | //指定的敏感方法API文件 27 | 28 | public static final String sensitiveMethodAPIFile="D:\\App资源耗尽漏洞检测\\DF_SINK_INFO.txt"; 29 | 30 | 31 | private HashMap sinks=new HashMap<>(); 32 | 33 | private HashSet methodsCanReachSink=new HashSet<>(); 34 | 35 | private HashSet controlFlowMethods=new HashSet<>(); 36 | 37 | 38 | 39 | 40 | private HashSet sinkMethodTrue = new HashSet<>(); 41 | 42 | //获取可以触发的敏感行为 43 | public HashSet getSinkMethodTrue() { 44 | return this.sinkMethodTrue; 45 | } 46 | 47 | public void clearSinkMethodTrue(){ 48 | this.sinkMethodTrue.clear(); 49 | } 50 | 51 | public SensitiveActionChecker(){ 52 | } 53 | 54 | //这里需要修改 55 | 56 | public SensitiveActionChecker(HashMap sinks){ 57 | logger.info("Init Sensitive Checker"); 58 | this.sinks=sinks; 59 | initReachableMethod(); 60 | } 61 | 62 | private void initReachableMethod(){ 63 | for(String signature:this.sinks.keySet()){ 64 | try{ 65 | String type = this.sinks.get(signature).trim(); 66 | //获取指定 67 | if(type.equals("C5")||type.equals("C4-AD")) { 68 | SootMethod method = Scene.v().getMethod(signature); 69 | controlFlowMethods.add(method); 70 | } 71 | }catch (Exception e){ 72 | // e.printStackTrace(); 73 | } 74 | } 75 | 76 | if(sinks.isEmpty()) 77 | return; 78 | //计算CG中所有可以执行到控制流的方法集合 79 | methodsCanReachSink=CallGraphUtils.getAllMethodsCanReachTargetMethods(controlFlowMethods); 80 | } 81 | 82 | 83 | 84 | @Override 85 | public boolean isSink(Unit checkedUnit, ValueBox taintValueBox) { 86 | 87 | InvokeExpr invokeExpr = getInvokeExpr(checkedUnit); 88 | if(invokeExpr==null) 89 | return false; 90 | //对于数据流的sink我们认为它的参数应该受到污染,尽管这有时 91 | if (sinks.keySet().contains(invokeExpr.getMethod().getSignature()) && invokeExpr.getArgs().contains(taintValueBox.getValue())) { 92 | logger.info("Detect sensitive data flow method {} is trigger!",invokeExpr.getMethod().getSignature()); 93 | String info = getFormatInfo(invokeExpr.getMethod(), 0,null); 94 | sinkMethodTrue.add(info); 95 | } 96 | return false; 97 | } 98 | 99 | @Override 100 | public boolean isDependConditionSink(SootMethod method,SootMethod caller) { 101 | if(methodsCanReachSink.contains(method)){ 102 | logger.info("Detect sensitive control flow method {} is trigger!",method.getSignature()); 103 | String info = getFormatInfo(method, 1,caller); 104 | if(info==null) 105 | return false; 106 | sinkMethodTrue.add(info); 107 | } 108 | return false; 109 | } 110 | 111 | 112 | 113 | private InvokeExpr getInvokeExpr(Unit u) { 114 | if (u instanceof AssignStmt) { 115 | AssignStmt assignStmt = (AssignStmt) u; 116 | if (assignStmt.containsInvokeExpr()) 117 | return assignStmt.getInvokeExpr(); 118 | } 119 | if (u instanceof InvokeStmt) { 120 | InvokeStmt invokeStmt = (InvokeStmt) u; 121 | return invokeStmt.getInvokeExpr(); 122 | } 123 | return null; 124 | } 125 | 126 | private String getFormatInfo(SootMethod method,int mode,SootMethod caller){ 127 | //获取标准的分析信息,恢复字符串主要是为了懒得写代码。。。。 128 | String msg=""; 129 | if(mode==0){ 130 | //如果是0则表示是数据流依赖的 131 | msg+="SINK TYPE: DATA_FLOW_SINK\n"; 132 | msg+="METHOD: "+method.getSignature()+"\n"; 133 | msg+="TYPE: "+sinks.get(method.getSignature()); 134 | }else { 135 | //计算该方法可以达到的危险方法 136 | List res=new ArrayList<>(); 137 | CallGraphUtils.getReachableMethodInGivenSet(method,controlFlowMethods,0,res,new HashSet<>()); 138 | if(res.isEmpty()) 139 | return null; 140 | msg+="SINK TYPE: CONTROL_FLOW_SINK\n"; 141 | msg+="DEPEND METHOD: "+method.getSignature()+'\n'; 142 | msg+="DEPEMD METHOD CALLER: "+caller.getSignature()+"\n"; 143 | int counter=0; 144 | for(String sinkMethod:res){ 145 | msg+="SINK METHOD "+"["+counter+"]: "+sinkMethod+'\n'; 146 | if(counter> res = new HashMap<>(); 34 | 35 | 36 | private String getStrawPointType(Unit unit,ValueBox taintValueBox) { 37 | InvokeExpr invokeExpr = null; 38 | if (unit instanceof InvokeStmt) { 39 | InvokeStmt invokeStmt = (InvokeStmt) unit; 40 | invokeExpr = invokeStmt.getInvokeExpr(); 41 | 42 | } else if (unit instanceof AssignStmt) { 43 | AssignStmt assignStmt = (AssignStmt) unit; 44 | if (assignStmt.containsInvokeExpr()) { 45 | invokeExpr = assignStmt.getInvokeExpr(); 46 | } 47 | } 48 | 49 | if(invokeExpr==null) 50 | return NOT; 51 | int index = invokeExpr.getArgs().indexOf(taintValueBox.getValue()); 52 | if(index==-1) 53 | return NOT; 54 | String signature = invokeExpr.getMethod().getSignature(); 55 | if (index==0&&StrawPointsDefinition.getFileWriteMethodList().contains(signature)) 56 | return FILE; 57 | if (StrawPointsDefinition.getDatabaseInsertMethodList().contains(signature)) 58 | return DATA_BASE; 59 | if (StrawPointsDefinition.getSharedPreferencesWriteMethodList().contains(signature)) 60 | return SHARED_PREFERENCES; 61 | if (index==0&&StringUtil.isMatch(signature, StrawPointsDefinition.COLLECTIONS_STRAWPOINT_REGEX)) 62 | return COLLECTIONS; 63 | return NOT; 64 | } 65 | 66 | 67 | private BackwardDataFlow backwardForObjectType = new BackwardDataFlow(new Analyze() { 68 | @Override 69 | public boolean caseAnalyze(Unit unit, SootMethod method, List callStack, HashSet res,ValueBox taintValueBox) { 70 | if (unit instanceof AssignStmt) { 71 | AssignStmt assignStmt = (AssignStmt) unit; 72 | if (assignStmt.containsFieldRef()) { 73 | if (assignStmt.getFieldRef().getField().isStatic()) { 74 | logger.info("the unit: {} in method: {} tell this a static field", unit, method.getSignature()); 75 | return true; 76 | } else { 77 | //对于实例字段还需要进一步判断,这里我们仅认为属于service或者单例类的字段是长生命周期的 78 | logger.info("the unit: {} in method: {} is a instance field", unit, method.getSignature()); 79 | return isInstanceFiledLongLife(unit); 80 | 81 | } 82 | } else if (assignStmt.getLeftOp() instanceof NewExpr) { 83 | logger.info("unit: {}, this a local...", unit); 84 | } 85 | } else if (unit instanceof IdentityStmt) { 86 | logger.info("the unit: {} is a Identity stmt", unit); 87 | } 88 | return false; 89 | } 90 | 91 | public boolean isInstanceFiledLongLife(Unit u){ 92 | AssignStmt assignStmt = (AssignStmt) u; 93 | SootClass declaringClass = assignStmt.getFieldRef().getField().getDeclaringClass(); 94 | while (declaringClass.hasSuperclass()){//判断是不是服务的字段 95 | declaringClass=declaringClass.getSuperclass(); 96 | if(declaringClass.getName().equals("android.app.Service")) 97 | return true; 98 | } 99 | 100 | boolean flag=true; 101 | for(SootMethod method:declaringClass.getMethods()){ 102 | if(method.getName().equals("")&&method.isPublic()){ 103 | flag=false; 104 | } 105 | } 106 | return flag; 107 | } 108 | }); 109 | 110 | private FileNameConstructor findFileName = new FileNameConstructor(); 111 | 112 | private ForwardDataFlow forwardForStrawDetect = new ForwardDataFlow(new Analyze() { 113 | 114 | @Override 115 | public boolean caseAnalyze(Unit unit, SootMethod method, List callStack, HashSet points,ValueBox taintValueBox) { 116 | 117 | if (points != null) { 118 | if (!res.containsKey(entryPoint)) 119 | res.put(entryPoint, new HashSet<>()); 120 | res.get(entryPoint).addAll(points); 121 | 122 | } else { 123 | String type = getStrawPointType(unit,taintValueBox); 124 | boolean flag=false; 125 | switch (type) { 126 | case COLLECTIONS: 127 | flag=handleForCollections(unit, method, callStack); 128 | break; 129 | case SHARED_PREFERENCES: 130 | flag=handleForSharePreferences(unit,method,callStack,taintValueBox); 131 | break; 132 | case FILE: 133 | flag=handleForFile(unit, method, callStack); 134 | break; 135 | case DATA_BASE: 136 | flag=handleForDataBase(unit,method,callStack); 137 | break; 138 | default: 139 | } 140 | 141 | if(flag){ 142 | ForwardDataFlow.addCheckedInfo2MethodParam2StrawPoint(callStack,getAnalysisResultOfEntry(entryPoint)); 143 | } 144 | } 145 | return true; 146 | } 147 | 148 | public boolean handleForSharePreferences(Unit unit,SootMethod method,List callStack,ValueBox taintValueBox){ 149 | //需要判断可以影响的这个Preference是影响的Key,还是Value 150 | InvokeExpr invokeExpr = AbstractDataFlow.getInvokeExpr(unit); 151 | if(invokeExpr==null) 152 | return false; 153 | int index = invokeExpr.getArgs().indexOf(taintValueBox.getValue()); 154 | if(index==-1) 155 | return false; 156 | // List useBoxes = invokeExpr.getUseBoxes(); 157 | // String preferencesName = nameConstructor.getRef2Str(useBoxes.get(useBoxes.size() - 1).getValue(), unit, method, callStack,0); 158 | String mark=index==0?"true":"false"; 159 | String msg=String.format("Affect Key: %s",mark); 160 | return handleInsertActions(unit, method, "SHARED_PREFERENCES",msg); 161 | } 162 | 163 | public boolean handleForDataBase(Unit unit,SootMethod method,List callStack){ 164 | InvokeExpr invokeExpr = AbstractDataFlow.getInvokeExpr(unit); 165 | if(invokeExpr==null) 166 | return false; 167 | 168 | // //获取数据库的名字 169 | // List useBoxes = invokeExpr.getUseBoxes(); 170 | // String dataBaseName = nameConstructor.getRef2Str(useBoxes.get(useBoxes.size() - 1).getValue(), unit, method, callStack,0); 171 | // //获取表的名字 172 | // String tableName=""; 173 | // if(invokeExpr.getMethod().getSignature().equals(StrawPointsDefinition.DATABASE_EXECSQL)){ 174 | // String sqlStmt = getValueOfObject(invokeExpr.getArg(0), unit, method, callStack); 175 | // if(sqlStmt!=null){ 176 | // String[] s = sqlStmt.toLowerCase().trim().split(" "); 177 | // if(s[0].contains("update")&&s.length>1){ 178 | // tableName=s[1]; 179 | // }else if(s[0].length()>2){ 180 | // tableName=s[2]; 181 | // } 182 | // } 183 | // }else { 184 | // tableName = getValueOfObject(invokeExpr.getArg(0), unit, method, callStack); 185 | // } 186 | // String otherMsg=String.format("DataBase: %s\nTable: %s",dataBaseName,tableName); 187 | return handleInsertActions(unit, method, "DATA_BASE",""); 188 | } 189 | 190 | public boolean handleForCollections(Unit unit, SootMethod method, List callStack) { 191 | InvokeExpr invokeExpr = null; 192 | if (unit instanceof InvokeStmt) { 193 | invokeExpr = ((InvokeStmt) unit).getInvokeExpr(); 194 | } else if (unit instanceof AssignStmt) { 195 | invokeExpr = ((AssignStmt) unit).getInvokeExpr(); 196 | } 197 | assert invokeExpr != null; 198 | ValueBox refValueBox = invokeExpr.getUseBoxes().get(invokeExpr.getUseBoxes().size() - 1); 199 | 200 | //开启检测 201 | if (!backwardForObjectType.getFlag()) 202 | backwardForObjectType.startBackward(); 203 | 204 | backwardForObjectType.run(method, unit, refValueBox, 0, callStack); 205 | //判断检测结果 206 | if (backwardForObjectType.getFlag()) { 207 | logger.info("unit: {} is not a real straw point", unit); 208 | return false; 209 | } else { 210 | addPoint2Res(unit, method, "COLLECTION",""); 211 | logger.info("STRAW_POINT! unit:{} in method:{}", unit, method.getSignature()); 212 | return true; 213 | } 214 | } 215 | 216 | public boolean handleInsertActions(Unit unit, SootMethod method, String type,String otherMsg) { 217 | addPoint2Res(unit, method, type,otherMsg); 218 | logger.info("STRAW_POINT! unit:{} in method:{}", unit, method.getSignature()); 219 | return true; 220 | 221 | } 222 | 223 | public boolean handleForFile(Unit unit, SootMethod method, List callStack) { 224 | //这里是不完备的 225 | addPoint2Res(unit, method, "FILE",""); 226 | return true; 227 | } 228 | 229 | private String getValueOfObject(Value value, Unit u, SootMethod method, List callChain) { 230 | if (value instanceof Local) 231 | return nameConstructor.getRef2Str(value, u, method,0); 232 | return value.toString(); 233 | } 234 | 235 | }); 236 | 237 | public void setTaintWrapper(HashSet taintWrapper){ 238 | forwardForStrawDetect.setTaintWrapper(taintWrapper); 239 | } 240 | 241 | 242 | public void run(SootMethod method, Unit unit, ValueBox valueBox, int index, int depth, List callStack) { 243 | forwardForStrawDetect.run(method, unit, valueBox, index, depth, callStack); 244 | } 245 | 246 | public void addPoint2Res(Unit unit, SootMethod method, String type,String otherMsg) { 247 | if (!res.containsKey(entryPoint)) { 248 | res.put(entryPoint, new HashSet()); 249 | } 250 | Point point = new Point(unit, method, type,otherMsg); 251 | res.get(entryPoint).add(point); 252 | } 253 | 254 | public HashSet getAnalysisResultOfEntry(String entryPoint){ 255 | if(res.containsKey(entryPoint)) 256 | return res.get(entryPoint); 257 | return null; 258 | } 259 | 260 | public static void recordCallSite(List callStack){ 261 | File file = new File("CallStack.log"); 262 | try { 263 | FileWriter fileWriter = new FileWriter(file, true); 264 | if(!callStack.isEmpty()) { 265 | fileWriter.write(callStack.toString() + "\n"); 266 | }else { 267 | fileWriter.write("调用栈为空"); 268 | } 269 | fileWriter.close(); 270 | }catch (Exception e){ 271 | e.printStackTrace(); 272 | } 273 | } 274 | 275 | 276 | public void setMaxDepth(int forward_maxDepth,int backward_maxDepth){ 277 | forwardForStrawDetect.setMAX_DEPTH(forward_maxDepth); 278 | backwardForObjectType.setMAX_DEPTH(backward_maxDepth); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/main/java/dataflow/TaintWrapper.java: -------------------------------------------------------------------------------- 1 | package dataflow; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import soot.SootMethod; 6 | 7 | import java.util.HashMap; 8 | import java.util.HashSet; 9 | 10 | 11 | public class TaintWrapper { 12 | //本类负责处理一些基本方法的wrapper 13 | 14 | 15 | //要求输入相应的文本,根据文本的规则,对一些方法的数据流分析进行总结 16 | //文件的格式 17 | //方法名,(污染实例,污染参数编号), 返回值被污染,参数被污染编号,实例被污染 18 | //举例,getIntent,True,_,True,_:表示该方法需要实例被污染,它的返回值才会被污染 19 | 20 | 21 | public static final Logger logger= LoggerFactory.getLogger(TaintWrapper.class); 22 | 23 | 24 | class SummeryEffect{ 25 | //前两条是条件,默认是不需要任何条件的 26 | boolean instanceTaint=false; 27 | int taintedParamIndex=-1; 28 | 29 | //后面是结果,默认是无条件可以影响到返回值和实例的 30 | boolean taintRetValue=true; 31 | int taintParamIndex=-1; 32 | boolean taintInstance=true; 33 | } 34 | 35 | HashMap methodToEffect=new HashMap<>(); 36 | 37 | 38 | 39 | 40 | boolean isInInTaintWrapper(String signature){ 41 | return methodToEffect.containsKey(signature); 42 | } 43 | 44 | public boolean canAffectRetValue(String signature,boolean instanceTaint,int taintedParamIndex){ 45 | if(!isInInTaintWrapper(signature)) { 46 | logger.warn("No taint wrapper for the method"); 47 | return false; 48 | } 49 | SummeryEffect summeryEffect = methodToEffect.get(signature); 50 | if(summeryEffect.instanceTaint&&!instanceTaint) 51 | return false; 52 | if(summeryEffect.taintedParamIndex!=-1&&taintedParamIndex!= summeryEffect.taintedParamIndex) 53 | return false; 54 | return summeryEffect.instanceTaint; 55 | 56 | 57 | } 58 | 59 | public boolean canAffectInstance(String signature,int taintedParamIndex){ 60 | if(!isInInTaintWrapper(signature)) { 61 | logger.warn("No taint wrapper for the method"); 62 | return false; 63 | } 64 | SummeryEffect summeryEffect = methodToEffect.get(signature); 65 | if(summeryEffect.taintedParamIndex!=-1&&taintedParamIndex!= summeryEffect.taintedParamIndex) 66 | return false; 67 | return summeryEffect.taintRetValue; 68 | } 69 | 70 | public int getAffectedParamIndex(String signature,boolean instanceTaint,int taintedParamIndex){ 71 | if(!isInInTaintWrapper(signature)) { 72 | logger.warn("No taint wrapper for the method"); 73 | return -1; 74 | } 75 | SummeryEffect summeryEffect = methodToEffect.get(signature); 76 | if(summeryEffect.instanceTaint&&!instanceTaint) 77 | return -1; 78 | if(summeryEffect.taintedParamIndex!=-1&&taintedParamIndex!= summeryEffect.taintedParamIndex) 79 | return -1; 80 | return summeryEffect.taintParamIndex; 81 | } 82 | 83 | public void setTaintWrapper(String filePath){ 84 | 85 | if(filePath==null) 86 | filePath=""; 87 | //根据指定文件创建wrapper 88 | } 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/timeout/TimeOutTask.java: -------------------------------------------------------------------------------- 1 | package timeout; 2 | 3 | import java.util.concurrent.*; 4 | 5 | 6 | public abstract class TimeOutTask { 7 | private ExecutorService executorService= Executors.newFixedThreadPool(1); 8 | 9 | public void run(int timeoutSeconds) { 10 | Future future = executorService.submit(new Callable() { 11 | @Override 12 | public String call() { 13 | task(); 14 | return "OK"; 15 | } 16 | }); 17 | try { 18 | String result = future.get(timeoutSeconds, TimeUnit.SECONDS); 19 | } catch (InterruptedException | TimeoutException e) { 20 | future.cancel(true); 21 | System.out.println("TimeOutTask: TimeOut"); 22 | timeoutHandler(); 23 | } catch (ExecutionException e){ 24 | e.printStackTrace(); 25 | } 26 | executorService.shutdown(); 27 | } 28 | 29 | protected abstract void task(); 30 | 31 | protected void timeoutHandler(){}; 32 | 33 | } 34 | 35 | class Demo{ 36 | public static void main(String[] args) { 37 | new TimeOutTask() { 38 | @Override 39 | protected void task() { 40 | try { 41 | TimeUnit.SECONDS.sleep(10); 42 | System.out.println("finish"); 43 | } catch (InterruptedException e) { 44 | System.out.println("任务被中断"); 45 | } 46 | } 47 | 48 | @Override 49 | protected void timeoutHandler(){ 50 | System.out.println("回收资源"); 51 | } 52 | }.run(5); 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/util/DirTraversal.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.io.File; 4 | import java.util.logging.Logger; 5 | 6 | 7 | public interface DirTraversal { 8 | void work(File f) throws Exception; 9 | 10 | Logger logger = Logger.getLogger(DirTraversal.class.getName()); 11 | 12 | default void traverse(File dir){ 13 | File[] files = dir.listFiles(); 14 | assert files != null; 15 | for(File f : files){ 16 | if(f.isDirectory())traverse(f); 17 | if(f.isFile()){ 18 | try { 19 | 20 | work(f); 21 | } catch (Exception e) { 22 | logger.warning("[DIRECTORY TRAVERSAL ERROR] " + f + " (This may cause unexpected skipping the file.)"); 23 | e.printStackTrace(); 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/util/Log.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.BufferedWriter; 7 | import java.io.File; 8 | import java.io.FileWriter; 9 | import java.io.IOException; 10 | 11 | 12 | public class Log { 13 | 14 | public enum Mode { 15 | APP, COMPONENT, SINK,SERVICE,INTERFACE,SENTENCE,FILE_INFO,UNIT,CLASS,DATA_BASE_SINK,VERSION,PATH 16 | } 17 | 18 | private static final Logger logger = LoggerFactory.getLogger(Log.class); 19 | private static File logFile = null; 20 | 21 | private static boolean startLog=false;//设置是否开启log 22 | 23 | private static BufferedWriter bufferedWriter = null; 24 | 25 | public static void openLog(String fileName, boolean append) { 26 | try { 27 | logFile = new File(fileName); 28 | FileWriter writer = new FileWriter(logFile, append); 29 | bufferedWriter = new BufferedWriter(writer); 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | 35 | public static void closeLog() { 36 | try { 37 | if (bufferedWriter != null) 38 | bufferedWriter.close(); 39 | }catch (Exception e){ 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | public static void write(String message) { 45 | try { 46 | bufferedWriter.write(message + '\n'); 47 | }catch (Exception e){ 48 | e.printStackTrace(); 49 | } 50 | } 51 | 52 | 53 | 54 | public static void write(Mode mode, String... message) { 55 | try { 56 | switch (mode) { 57 | case APP: 58 | bufferedWriter.write("================================APP========================================\n"); 59 | bufferedWriter.write("APP: " + message[0] + '\n'); 60 | break; 61 | case PATH: 62 | bufferedWriter.write("PATH: " + message[0] + '\n'); 63 | break; 64 | case VERSION: 65 | bufferedWriter.write("VERSION: " + message[0] + '\n'); 66 | break; 67 | case SINK: 68 | bufferedWriter.write("ENTRY_POINT: " + message[0] + '\n'); 69 | bufferedWriter.write("UNIT:" + message[1] + '\n'); 70 | bufferedWriter.write("METHOD:" + message[2] + '\n'); 71 | bufferedWriter.write("TYPE:" + message[3] + '\n'); 72 | if(!message[4].equals("")) 73 | bufferedWriter.write(message[4]+'\n'); 74 | break; 75 | case COMPONENT: 76 | 77 | bufferedWriter.write("****************************************************************\n"); 78 | bufferedWriter.write("COMPONENT:" + message[0] + '\n'); 79 | bufferedWriter.write('\n'); 80 | break; 81 | case SERVICE: 82 | bufferedWriter.write("===================================System Service==========================\n"); 83 | bufferedWriter.write("System Service: "+message[0]+'\n'); 84 | break; 85 | case INTERFACE: 86 | bufferedWriter.write("***************************************Interface***************************\n"); 87 | bufferedWriter.write("Interface: "+message[0]+'\n'); 88 | break; 89 | case SENTENCE: 90 | bufferedWriter.write("method: "+message[0]+'\n'); 91 | break; 92 | case UNIT: 93 | bufferedWriter.write("Unit: "+message[0]+'\n'); 94 | break; 95 | case CLASS: 96 | bufferedWriter.write("Class: "+message[0]+'\n'); 97 | break; 98 | case FILE_INFO: 99 | bufferedWriter.write("================================File Info=====================================\n"); 100 | bufferedWriter.write("Type: "+message[0]+'\n'); 101 | bufferedWriter.write("Name: "+message[1]+'\n'); 102 | bufferedWriter.write("Mode: "+message[2]+'\n'); 103 | bufferedWriter.write("Entry: "+message[3]+'\n'); 104 | break; 105 | case DATA_BASE_SINK: 106 | bufferedWriter.write("=================================DataBase Info=====================================\n"); 107 | bufferedWriter.write("ENTRY_POINT: " + message[0] + '\n'); 108 | bufferedWriter.write("UNIT:" + message[1] + '\n'); 109 | bufferedWriter.write("METHOD:" + message[2] + '\n'); 110 | bufferedWriter.write("TYPE:" + message[3] + '\n'); 111 | bufferedWriter.write(message[4]+'\n'); 112 | break; 113 | default: 114 | logger.warn("the mode is not define!"); 115 | } 116 | bufferedWriter.flush(); 117 | } catch (Exception e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/util/SootInit.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import soot.G; 4 | import soot.Scene; 5 | import soot.options.Options; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | 12 | public class SootInit { 13 | 14 | public static void initSootForJava(String programPath){ 15 | G.reset(); 16 | soot.options.Options.v().set_prepend_classpath(true); 17 | soot.options.Options.v().set_allow_phantom_refs(true); 18 | soot.options.Options.v().set_src_prec(soot.options.Options.src_prec_class); 19 | Options.v().set_process_dir(Collections.singletonList(programPath)); 20 | Scene.v().loadNecessaryClasses(); 21 | } 22 | 23 | public static void initSootForAndroid(String apkPath,String androidJarPath){ 24 | G.reset(); 25 | Options.v().set_prepend_classpath(true); 26 | Options.v().set_allow_phantom_refs(true); 27 | Options.v().set_process_multiple_dex(true); 28 | Options.v().set_src_prec(Options.src_prec_apk); 29 | Options.v().set_process_dir(Collections.singletonList(apkPath)); 30 | Options.v().set_android_jars(androidJarPath); 31 | Options.v().set_whole_program(true); 32 | List excludeList=new ArrayList<>(); 33 | excludeList.add("java.*"); 34 | excludeList.add("javax.*"); 35 | excludeList.add("android.*"); 36 | excludeList.add("androidx.*"); 37 | excludeList.add("com.google.*"); 38 | excludeList.add("sun.*"); 39 | excludeList.add("io.*"); 40 | excludeList.add("kotlin.*"); 41 | excludeList.add("kotlinx.*"); 42 | Options.v().set_exclude(excludeList); 43 | Options.v().set_no_bodies_for_excluded(true); 44 | Options.v().set_output_format(Options.output_format_jimple); 45 | Options.v().set_process_multiple_dex(true); 46 | Options.v().set_keep_line_number(true); 47 | Scene.v().loadNecessaryClasses(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/util/StringConvertUtil.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.HashSet; 7 | import java.util.Iterator; 8 | 9 | 10 | public class StringConvertUtil { 11 | 12 | //数据恢复工具,将从文件中恢复的数据处理成标准的样式 13 | 14 | //建立图 15 | //邻接表 16 | 17 | public static final Logger logger= LoggerFactory.getLogger(StringConvertUtil.class); 18 | static class Node{ 19 | //邻接表 20 | HashSet next; 21 | String var; 22 | 23 | public Node(){ 24 | next=new HashSet<>(); 25 | var=""; 26 | } 27 | } 28 | 29 | 30 | private Node[] node=null; 31 | 32 | private int top_node=0; 33 | 34 | private int alloc_node(){ 35 | return top_node++; 36 | } 37 | //括号匹配 38 | private int[] bracket_match=new int[10000]; 39 | //自己设置的栈 40 | private int[] stack=new int[10000]; 41 | //栈顶 42 | private int top=0; 43 | 44 | //入栈操作 45 | private void push(int index){ 46 | stack[top++]=index; 47 | } 48 | 49 | private int pop(){ 50 | if(top==0) 51 | return -1; 52 | return stack[--top]; 53 | } 54 | 55 | private boolean cal_bracket_match(String s){ 56 | //计算括号匹配,哪两个括号是匹配的 57 | for(int i=0;i res) 134 | { 135 | for(Integer i:node[nid].next){ 136 | dfs_print(i,s+node[i].var,res); 137 | } 138 | if (node[nid].next.size() == 0) 139 | { 140 | if(!s.isEmpty()&&!s.equals("null")) { 141 | String ans = s.replaceAll("\"", ""); 142 | res.add(ans.toLowerCase()); 143 | } 144 | } 145 | } 146 | 147 | public StringConvertUtil(){ 148 | node=new Node[10000]; 149 | for(int i=0;i<10000;i++) 150 | node[i]=new Node(); 151 | } 152 | 153 | 154 | public HashSet getFormatInfo(String input){ 155 | //得到格式化的信息 156 | HashSet res=new HashSet<>(); 157 | if(input.length()==0||input.length()>10000) 158 | return res; 159 | int start=alloc_node(); 160 | int end=alloc_node(); 161 | boolean flag = cal_bracket_match(input); 162 | if(!flag) { 163 | logger.info("括号匹配失败!"); 164 | return new HashSet<>(); 165 | } 166 | dfs(input,0,input.length()-1,start,end); 167 | dfs_print(0,"",res); 168 | res.removeIf(next -> next.contains("failed")); 169 | return res; 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class StringUtil { 9 | 10 | 11 | public static boolean isMatch(String str, String reg) { 12 | Pattern pattern = Pattern.compile(reg); 13 | Matcher matcher = pattern.matcher(str); 14 | return matcher.matches(); 15 | } 16 | 17 | public static boolean isFind(String str,String reg){ 18 | Pattern pattern=Pattern.compile(reg); 19 | Matcher matcher=pattern.matcher(str); 20 | return matcher.find(); 21 | 22 | } 23 | 24 | public static String findString(String str,String reg){ 25 | Pattern pattern = Pattern.compile(reg); 26 | Matcher matcher = pattern.matcher(str); 27 | if(matcher.find()) 28 | return matcher.group(0); 29 | return null; 30 | } 31 | 32 | public static int getParameterOrder(String input) { 33 | String[] str = input.split(":=")[1].split(":"); 34 | Pattern pattern = Pattern.compile("[^0-9]"); 35 | Matcher matcher = pattern.matcher(str[0]); 36 | return Integer.parseInt(matcher.replaceAll("")); 37 | } 38 | 39 | public static String insertDot(String str) { 40 | List insertMark= Arrays.asList(".","$","[","]","(",")"); 41 | StringBuilder stringBuilder=new StringBuilder(); 42 | for(int i=0;i