├── README.md ├── java_analysis ├── CLAnalysis.jar ├── copy_jar_bash.sh ├── extract_jar_bash.sh └── hk │ └── polyu │ ├── Config.java │ ├── Main.java │ ├── MainCMD.java │ ├── soot │ ├── cg │ │ ├── BinderPatcher.java │ │ ├── CallgraphBuilder.java │ │ ├── CallgraphPatcherPost.java │ │ ├── CallgraphPatcherPre.java │ │ ├── CallgraphWrapper.java │ │ ├── ConstructorPatcher.java │ │ ├── EdgePatcher.java │ │ ├── EntryPointCreator.java │ │ ├── HandlerPatcher.java │ │ ├── InvalidEdgePatcher.java │ │ ├── JNIAnalyzer.java │ │ ├── ParcelPatcher.java │ │ ├── PermissionCheckFinder.java │ │ ├── RunPatcher.java │ │ ├── SPARKPatcher.java │ │ ├── StaticFieldResolver.java │ │ ├── UIDCheckFinder.java │ │ └── UserIDCheckFinder.java │ └── preprocess │ │ ├── PermissionCheckFinder.java │ │ ├── SootInitializer.java │ │ └── UidCheckFinder.java │ └── util │ ├── BashRunner.java │ └── ExtractClasses.java ├── native_analysis ├── analyze_bitcode.py └── extract_bitcode.py └── patch_framework ├── merge.py └── patch.py /README.md: -------------------------------------------------------------------------------- 1 | # IAceFinder: Uncovering Cross-Context Inconsistent Access Control Enforcement in Android 2 | 3 | ## Publication 4 | 5 | If you are interested about the details of IAceFinder, please refer to our research paper: 6 | 7 | @inproceedings {iacefinder22, 8 | title={Uncovering Cross-Context Inconsistent Access Control Enforcement in Android}, 9 | author={Zhou, Hao and Wang, Haoyu and Luo, Xiapu and Chen, Ting and Zhou, Yajin and and Wang, Ting}, 10 | booktitle={Network and Distributed Systems Security Symposium (NDSS)}, 11 | year={2022}, 12 | } 13 | -------------------------------------------------------------------------------- /java_analysis/CLAnalysis.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moonZHH/IAceFinder/d737977c4cbefd518e2e0c2a27cb84701e0609da/java_analysis/CLAnalysis.jar -------------------------------------------------------------------------------- /java_analysis/copy_jar_bash.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | ## $1 = the dst path of jar file 4 | ## $2 = the src path of jar file 5 | 6 | toPath="$1" 7 | fromPath="$2" 8 | ANDROID_CLASS_DIR="/home/zhouhao/CrossFrameworkAnalysis/JavaLayer/output/classes/" 9 | 10 | if [ ! -d "$ANDROID_CLASS_DIR" ]; then 11 | mkdir "$ANDROID_CLASS_DIR" 12 | fi 13 | 14 | cp $fromPath $toPath 15 | -------------------------------------------------------------------------------- /java_analysis/extract_jar_bash.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | ## $1 = the src path of jar file 4 | ## $2 = the API level of the analyzed AOSP version 5 | 6 | ANDROID_CLASS_DIR="/home/zhouhao/CrossFrameworkAnalysis/JavaLayer/output/classes/" 7 | 8 | if [ $2 -gt 8 ] && [ $2 -lt 21 ]; then 9 | export JAVA_HOME=/home/zhouhao/Jdk_Version/jdk1.6.0_45 10 | export PATH=$JAVA_HOME/bin:$PATH 11 | elif [ $2 -gt 20 ] && [ $2 -lt 24 ]; then 12 | export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64/jre 13 | export PATH=$JAVA_HOME/bin:$PATH 14 | else 15 | export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre 16 | export PATH=$JAVA_HOME/bin:$PATH 17 | fi 18 | 19 | cd "$ANDROID_CLASS_DIR" 20 | jar xf $1 21 | rm -f $1 22 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/Config.java: -------------------------------------------------------------------------------- 1 | package hk.polyu; 2 | 3 | public class Config { 4 | 5 | public static int APILevel = 30; 6 | 7 | // AOSP (Android-10) 8 | /* 9 | public static String AOSPDirectory = "/home/zhouhao/data_fast/android_0805"; 10 | public static String InstalledTxtFile = AOSPDirectory + "/out/target/product/generic_x86/installed-files.txt"; 11 | public static String SystemAppDirectory = AOSPDirectory + "/out/target/common/obj/APPS"; 12 | public static String FrameworkDirectory = AOSPDirectory + "/out/target/common/obj/JAVA_LIBRARIES"; 13 | */ 14 | 15 | // AOSP (Android-11) 16 | /* 17 | public static String AOSPDirectory = "/home/zhouhao/data_fast/android_1005"; 18 | public static String InstalledTxtFile = AOSPDirectory + "/out/target/product/generic_x86/installed-files.txt"; 19 | public static String SystemAppDirectory = AOSPDirectory + "/out/target/common/obj/APPS"; 20 | public static String FrameworkDirectory = AOSPDirectory + "/out/target/common/obj/JAVA_LIBRARIES"; 21 | */ 22 | /* 23 | public static String AOSPDirectory = "/home/zhouhao/data_fast/android_1205"; 24 | public static String InstalledTxtFile = AOSPDirectory + "/out/target/product/generic_x86/installed-files.txt"; 25 | public static String SystemAppDirectory = AOSPDirectory + "/out/target/common/obj/APPS"; 26 | public static String FrameworkDirectory = AOSPDirectory + "/out/target/common/obj/JAVA_LIBRARIES"; 27 | */ 28 | /* 29 | public static String AOSPDirectory = "/home/zhouhao/AOSP/aosp_11_0805"; 30 | public static String InstalledTxtFile = AOSPDirectory + "/out/target/product/generic_x86/installed-files.txt"; 31 | public static String SystemAppDirectory = AOSPDirectory + "/out/target/common/obj/APPS"; 32 | public static String FrameworkDirectory = AOSPDirectory + "/out/target/common/obj/JAVA_LIBRARIES"; 33 | */ 34 | 35 | // AOSP (Android-12) 36 | public static String AOSPDirectory = "/home/zhouhao/AOSP/aosp_12_0905"; 37 | public static String InstalledTxtFile = AOSPDirectory + "/out/target/product/generic_x86/installed-files.txt"; 38 | public static String SystemAppDirectory = AOSPDirectory + "/out/target/common/obj/APPS"; 39 | public static String FrameworkDirectory = AOSPDirectory + "/out/target/common/obj/JAVA_LIBRARIES"; 40 | 41 | // LineageOS 42 | /* 43 | public static String AOSPDirectory = "/home/zhouhao/data/lineage-17.1"; 44 | public static String InstalledTxtFile = AOSPDirectory + "/out/target/product/generic_x86/installed-files.txt"; 45 | public static String SystemAppDirectory = AOSPDirectory + "/out/target/common/obj/APPS"; 46 | public static String FrameworkDirectory = AOSPDirectory + "/out/target/common/obj/JAVA_LIBRARIES"; 47 | */ 48 | 49 | // GrapheneOS 50 | /* 51 | public static String AOSPDirectory = "/home/zhouhao/data/grapheneos-11"; 52 | public static String InstalledTxtFile = AOSPDirectory + "/out/target/product/generic_x86_64/installed-files.txt"; 53 | public static String SystemAppDirectory = AOSPDirectory + "/out/target/common/obj/APPS"; 54 | public static String FrameworkDirectory = AOSPDirectory + "/out/target/common/obj/JAVA_LIBRARIES"; 55 | */ 56 | 57 | // OmniROM 58 | /* 59 | public static String AOSPDirectory = "/home/zhouhao/data/omnirom"; 60 | public static String InstalledTxtFile = AOSPDirectory + "/out/target/product/generic_x86/installed-files.txt"; 61 | public static String SystemAppDirectory = AOSPDirectory + "/out/target/common/obj/APPS"; 62 | public static String FrameworkDirectory = AOSPDirectory + "/out/target/common/obj/JAVA_LIBRARIES"; 63 | */ 64 | 65 | // crDroid 66 | /* 67 | public static String AOSPDirectory = "/home/zhouhao/data/crDroid"; 68 | public static String InstalledTxtFile = AOSPDirectory + "/out/target/product/generic_x86_64/installed-files.txt"; 69 | public static String SystemAppDirectory = AOSPDirectory + "/out/target/common/obj/APPS"; 70 | public static String FrameworkDirectory = AOSPDirectory + "/out/target/common/obj/JAVA_LIBRARIES"; 71 | */ 72 | 73 | public static String[] CommonCommand = {"/bin/sh", "-c"}; 74 | 75 | public static String Workspace = "/home/zhouhao/CrossFrameworkAnalysis/JavaLayer"; 76 | 77 | public static String CopyJarBash = Workspace + "/copy_jar_bash.sh"; 78 | public static String ExtractJarBash = Workspace + "/extract_jar_bash.sh"; 79 | 80 | public static String OutputClassesDirectory = Workspace + "/output/classes"; 81 | public static String OutputClassListTxtFile = Workspace + "/output/class_list.txt"; 82 | 83 | // ClassHierarchy.java 84 | // public static String OutputClassHierarchyTxtFile = Workspace + "/output/class_hierarchy.txt"; 85 | 86 | // CallGraph.java 87 | // public static String OutputRawCallGraphTxtFile = Workspace + "/output/raw_callgraph.txt"; 88 | 89 | // FindMessage.java 90 | // public static String OutputMessageTxtFile = Workspace + "/output/message.txt"; 91 | 92 | // AnalyzeHandleMessage.java 93 | // public static String OutputHandleMessageSwitchTxtFile = Workspace + "/output/handle_message_switch.txt"; 94 | 95 | // FindRPCMethod.java 96 | // public static String OutputRPCMethodTxtFile = Workspace + "/output/rpc_method.txt"; 97 | 98 | // BuildBasicCallgraph.java 99 | // public static String OutputCallGraphTxtFile = Workspace + "/output/call_graph.txt"; 100 | 101 | // ParseDex.java 102 | // public static String Dex2JarBash = "/home/zhouhao/dex2jar/d2j-dex2jar.sh"; 103 | // public static String EnjarifyBash = "/home/zhouhao/enjarify/enjarify.sh"; 104 | 105 | public static boolean DEBUG = false; 106 | 107 | // 108 | public static String OutputPermissionCheckFile = Workspace + "/output/permission_check.txt"; 109 | public static String OutputUidCheckFile = Workspace + "/output/uid_check.txt"; 110 | 111 | public static String OutputJNIMethodFile = Workspace + "/output/jnimethods.txt"; 112 | } 113 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/Main.java: -------------------------------------------------------------------------------- 1 | package hk.polyu; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.apache.commons.io.FileUtils; 7 | 8 | import hk.polyu.util.ExtractClasses; 9 | 10 | public class Main { 11 | 12 | public static void main(String[] args) { 13 | init(); 14 | 15 | ExtractClasses.extract(); 16 | } 17 | 18 | private static void init() { 19 | System.gc(); 20 | 21 | try { 22 | File outputDirectory = new File(Config.Workspace + "/output"); 23 | if (!outputDirectory.exists()) 24 | outputDirectory.mkdir(); 25 | 26 | File outputClassesDirectory = new File(Config.OutputClassesDirectory); 27 | if (!outputClassesDirectory.exists()) { 28 | outputClassesDirectory.mkdir(); 29 | } else { 30 | FileUtils.deleteDirectory(outputClassesDirectory); 31 | outputClassesDirectory.mkdir(); 32 | } 33 | } catch (IOException ioe) { 34 | ioe.printStackTrace(); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/MainCMD.java: -------------------------------------------------------------------------------- 1 | package hk.polyu; 2 | 3 | import hk.polyu.soot.cg.CallgraphBuilder; 4 | import hk.polyu.soot.cg.CallgraphPatcherPost; 5 | import hk.polyu.soot.cg.CallgraphPatcherPre; 6 | import hk.polyu.soot.cg.JNIAnalyzer; 7 | import hk.polyu.soot.cg.PermissionCheckFinder; 8 | import hk.polyu.soot.cg.StaticFieldResolver; 9 | import hk.polyu.soot.cg.UIDCheckFinder; 10 | import hk.polyu.soot.cg.UserIDCheckFinder; 11 | import hk.polyu.soot.preprocess.SootInitializer; 12 | import soot.Scene; 13 | import soot.jimple.toolkits.callgraph.CallGraph; 14 | 15 | public class MainCMD { 16 | 17 | public static void main(String[] args) { 18 | SootInitializer.init(); 19 | 20 | Config.OutputJNIMethodFile = args[1]; 21 | 22 | // perform the analysis that does not rely on Callgraph 23 | // hk.polyu.soot.preprocess.PermissionCheckFinder.find(); 24 | // hk.polyu.soot.preprocess.UidCheckFinder.find(); 25 | 26 | // build Callgraph 27 | CallgraphPatcherPre.patch(); 28 | CallgraphBuilder.generateCallgraph(args[0]); 29 | CallgraphPatcherPost.patch(); 30 | 31 | // perform the analysis that relies on Callgraph 32 | CallGraph cg = Scene.v().getCallGraph(); 33 | StaticFieldResolver.resolve(cg); 34 | PermissionCheckFinder.find(cg); 35 | UIDCheckFinder.find(cg); 36 | // UserIDCheckFinder.find(cg); 37 | 38 | JNIAnalyzer.analyze(cg); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/BinderPatcher.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.Map.Entry; 9 | 10 | import hk.polyu.Config; 11 | import soot.ArrayType; 12 | import soot.Body; 13 | import soot.BooleanType; 14 | import soot.ByteType; 15 | import soot.CharType; 16 | import soot.DoubleType; 17 | import soot.FloatType; 18 | import soot.IntType; 19 | import soot.Local; 20 | import soot.LongType; 21 | import soot.NullType; 22 | import soot.RefType; 23 | import soot.Scene; 24 | import soot.ShortType; 25 | import soot.SootClass; 26 | import soot.SootMethod; 27 | import soot.Type; 28 | import soot.Unit; 29 | import soot.Value; 30 | import soot.VoidType; 31 | import soot.javaToJimple.LocalGenerator; 32 | import soot.jimple.AssignStmt; 33 | import soot.jimple.IdentityStmt; 34 | import soot.jimple.IntConstant; 35 | import soot.jimple.InvokeExpr; 36 | import soot.jimple.InvokeStmt; 37 | import soot.jimple.Jimple; 38 | import soot.jimple.NewExpr; 39 | import soot.jimple.NullConstant; 40 | import soot.jimple.ParameterRef; 41 | import soot.jimple.ReturnVoidStmt; 42 | import soot.jimple.Stmt; 43 | import soot.jimple.ThisRef; 44 | import soot.jimple.toolkits.callgraph.CallGraph; 45 | import soot.jimple.toolkits.callgraph.Edge; 46 | 47 | public class BinderPatcher { 48 | 49 | // public static HashMap serviceManagerMap = new HashMap(); 50 | // public static HashSet serviceManagerSet = new HashSet(); 51 | 52 | // public static HashSet serviceInterfaceSet = new HashSet(); 53 | public static HashSet binderProxySet = new HashSet(); 54 | public static HashSet binderStubSet = new HashSet(); 55 | public static HashSet binderRemoteSet = new HashSet(); 56 | 57 | // public static HashMap serviceManagerInterfaceMap = new HashMap(); 58 | // public static HashMap serviceInterfaceProxyMap = new HashMap(); 59 | // public static HashMap serviceInterfaceStubMap = new HashMap(); 60 | public static HashMap binderStubProxyMap = new HashMap(); 61 | public static HashMap> binderStubRemoteMap = new HashMap>(); 62 | public static HashMap> binderProxyRemoteMap = new HashMap>(); 63 | 64 | public static void init() { 65 | // fetchServiceManager(); 66 | // serviceManagerSet = new HashSet(serviceManagerMap.values()); 67 | 68 | // collect Binder proxy and stub classes 69 | for (SootClass sc : Scene.v().getClasses()) { 70 | if (!sc.getName().endsWith("$Stub$Proxy")) 71 | continue; 72 | 73 | SootClass binderProxyClass = sc; 74 | binderProxySet.add(binderProxyClass); 75 | 76 | String binderStubName = binderProxyClass.getName().replace("$Stub$Proxy", "$Stub"); 77 | SootClass binderStubClass = Scene.v().getSootClassUnsafe(binderStubName, false); 78 | if (binderStubClass == null) { 79 | if (Config.DEBUG) 80 | System.err.println("[ERR]: Can *not* find the SootClass for Binder stub <" + binderStubName + ">"); 81 | throw new RuntimeException(); 82 | } 83 | binderStubSet.add(binderStubClass); 84 | 85 | binderStubProxyMap.put(binderStubClass, binderProxyClass); 86 | } 87 | 88 | // collect Binder remote classes 89 | for (SootClass sc : Scene.v().getClasses()) { 90 | if (!sc.hasSuperclass()) 91 | continue; 92 | 93 | SootClass superClass = sc.getSuperclass(); 94 | if (binderStubSet.contains(superClass)) { 95 | // handle DevicePolicyManagerService 96 | if (sc.getName().equals("com.android.server.devicepolicy.BaseIDevicePolicyManager")) { 97 | sc = Scene.v().getSootClassUnsafe("com.android.server.devicepolicy.DevicePolicyManagerService", false); 98 | assert sc != null; 99 | } 100 | /* 101 | // handle AbstractAccessibilityServiceConnection 102 | if (sc.getName().equals("com.android.server.accessibility.AbstractAccessibilityServiceConnection")) { 103 | sc = Scene.v().getSootClassUnsafe("com.android.server.accessibility.AccessibilityServiceConnection", false); 104 | assert sc != null; 105 | } 106 | */ 107 | // handle PeopleServiceInternal 108 | if (sc.getName().equals("com.android.server.people.PeopleServiceInternal")) { 109 | sc = Scene.v().getSootClassUnsafe("com.android.server.people.PeopleService$LocalService", false); 110 | assert sc != null; 111 | } 112 | 113 | binderRemoteSet.add(sc); 114 | 115 | if (!binderStubRemoteMap.containsKey(superClass)) 116 | binderStubRemoteMap.put(superClass, new HashSet()); 117 | binderStubRemoteMap.get(superClass).add(sc); 118 | 119 | SootClass pClass = binderStubProxyMap.get(superClass); 120 | if (!binderProxyRemoteMap.containsKey(pClass)) 121 | binderProxyRemoteMap.put(pClass, new HashSet()); 122 | binderProxyRemoteMap.get(pClass).add(sc); 123 | } 124 | } 125 | 126 | // debug 127 | /* 128 | for (Entry> each : binderStubRemoteMap.entrySet()) { 129 | SootClass binderStubClass = each.getKey(); 130 | HashSet binderRemoteClasses = each.getValue(); 131 | if (binderRemoteClasses.isEmpty()) 132 | continue; 133 | if (binderRemoteClasses.size() > 1) 134 | continue; 135 | 136 | for (SootClass binderRemoteClass : binderRemoteClasses) { 137 | System.out.println(binderStubClass.getName() + " <- " + binderRemoteClass.getName()); 138 | } 139 | } 140 | */ 141 | // 142 | for (Entry> each : binderProxyRemoteMap.entrySet()) { 143 | SootClass binderProxyClass = each.getKey(); 144 | HashSet binderRemoteClasses = each.getValue(); 145 | if (binderRemoteClasses.isEmpty()) 146 | continue; 147 | if (binderRemoteClasses.size() > 1) 148 | continue; 149 | 150 | for (SootClass binderRemoteClass : binderRemoteClasses) { 151 | System.out.println(binderProxyClass.getName() + " <- " + binderRemoteClass.getName()); 152 | } 153 | } 154 | // 155 | } 156 | 157 | /* 158 | private static void fetchServiceManager() { 159 | SootClass ServiceRegistrySC = Scene.v().getSootClass("android.app.SystemServiceRegistry"); 160 | SootMethod ClinitSM = ServiceRegistrySC.getMethodByName(""); 161 | SootMethod RegisterServiceSM = ServiceRegistrySC.getMethodByName("registerService"); 162 | 163 | Body ClinitBD = ClinitSM.retrieveActiveBody(); 164 | for (Unit unit : ClinitBD.getUnits()) { 165 | Stmt stmt = (Stmt) unit; 166 | if (stmt.containsInvokeExpr()) { 167 | SootMethod callee = stmt.getInvokeExpr().getMethod(); 168 | if (callee.getSignature().equals(RegisterServiceSM.getSignature())) { 169 | InvokeExpr expr = stmt.getInvokeExpr(); 170 | String serviceName = ((StringConstant) expr.getArg(0)).value; 171 | String serviceClassName = ((ClassConstant) expr.getArg(1)).value; 172 | if (serviceClassName.equals("Landroid/os/IBinder;")) { 173 | // special case 174 | System.err.println("[ERR]: service manager <" + serviceClassName + "> for <" + serviceName + "> is strange"); 175 | continue; 176 | } 177 | 178 | SootClass serviceSC = Scene.v().getSootClass(serviceClassName.substring(1, serviceClassName.length() - 1).replace("/", ".")); 179 | serviceManagerMap.put(serviceName, serviceSC); 180 | // System.out.println(serviceName + " -> " + serviceSC.getName()); // debug 181 | } 182 | } 183 | } 184 | } 185 | */ 186 | 187 | public static void patch() { 188 | // main logic 189 | for (Entry> each : binderProxyRemoteMap.entrySet()) { 190 | SootClass proxySC = each.getKey(); 191 | HashSet remoteSCs = each.getValue(); 192 | 193 | HashSet handledMethods = new HashSet(); 194 | // override methods 195 | for (SootMethod proxySM : proxySC.getMethods()) { 196 | // System.out.println(proxySM.getSignature()); 197 | handledMethods.add(proxySM.getSubSignature()); 198 | 199 | if (!proxySM.isConcrete()) 200 | continue; 201 | if (proxySM.isConstructor()) 202 | continue; 203 | if (proxySM.getName().equals("asBinder")) 204 | continue; 205 | if (proxySM.getName().equals("getInterfaceDescriptor")) 206 | continue; 207 | if (!willCallTransact(proxySM)) 208 | continue; 209 | 210 | String subsignature = proxySM.getSubSignature(); 211 | for (SootClass remoteSC : remoteSCs) { 212 | SootMethod remoteSM = remoteSC.getMethodUnsafe(subsignature); 213 | if (remoteSM == null) { 214 | System.out.println("[ERR]: can *not* find <" + subsignature + "> in " + remoteSC.getName()); 215 | continue; 216 | } 217 | 218 | // do the patch 219 | // System.out.println("[Patch] " + proxySM.getSignature() + " -->> " + remoteSM.getSignature()); 220 | patchBody(proxySM, remoteSM); 221 | } 222 | } 223 | } 224 | 225 | // additional logic 226 | SootClass binderSC = Scene.v().getSootClass("android.os.IBinder"); 227 | SootMethod transactSM = binderSC.getMethod("boolean transact(int,android.os.Parcel,android.os.Parcel,int)"); 228 | 229 | // ==>> [FIX]: Binder.dump will not be overrode by $Stub$Proxy classes, causing miss of edge problem 230 | for (Entry> each : binderProxyRemoteMap.entrySet()) { 231 | SootClass proxySC = each.getKey(); 232 | HashSet remoteSCs = each.getValue(); 233 | 234 | for (SootClass remoteSC : remoteSCs) { 235 | for (SootMethod remoteSM : remoteSC.getMethods()) { 236 | if (remoteSM.getName().equals("dump")) { 237 | // do the patch 238 | SootMethod proxySM = proxySC.getMethodUnsafe(remoteSM.getSubSignature()); 239 | if (proxySM != null && !proxySM.isPhantom()) { 240 | // do nothing 241 | } else { 242 | proxySM = new SootMethod("dump", remoteSM.getParameterTypes(), remoteSM.getReturnType()); 243 | proxySC.addMethod(proxySM); 244 | Body body = Jimple.v().newBody(proxySM); 245 | proxySM.setActiveBody(body); 246 | 247 | ArrayList stmts = new ArrayList(); 248 | 249 | LocalGenerator lg = new LocalGenerator(body); 250 | 251 | ThisRef thisRef = Jimple.v().newThisRef(proxySC.getType()); 252 | Local thisLocal = lg.generateLocal(proxySC.getType()); 253 | IdentityStmt thisStmt = Jimple.v().newIdentityStmt(thisLocal, thisRef); 254 | stmts.add(thisStmt); 255 | 256 | for (int paraIdx = 0; paraIdx < proxySM.getParameterCount(); paraIdx++) { 257 | Type paraType = proxySM.getParameterType(paraIdx); 258 | ParameterRef paraRef = Jimple.v().newParameterRef(paraType, paraIdx); 259 | Local paraLocal = lg.generateLocal(paraType); 260 | IdentityStmt idStmt = Jimple.v().newIdentityStmt(paraLocal, paraRef); 261 | stmts.add(idStmt); 262 | } 263 | 264 | InvokeExpr invExpr = Jimple.v().newInterfaceInvokeExpr(thisLocal, transactSM.makeRef(), IntConstant.v(0), NullConstant.v(), NullConstant.v(), IntConstant.v(0)); 265 | InvokeStmt invStmt = Jimple.v().newInvokeStmt(invExpr); 266 | stmts.add(invStmt); 267 | 268 | ReturnVoidStmt retStmt = Jimple.v().newReturnVoidStmt(); 269 | stmts.add(retStmt); 270 | 271 | body.getUnits().addAll(stmts); 272 | } 273 | 274 | // System.out.println("[Patch] " + proxySM.getSignature() + " -->> " + remoteSM.getSignature()); 275 | patchBody(proxySM, remoteSM); 276 | } 277 | } 278 | } 279 | } 280 | } 281 | 282 | private static boolean willCallTransact(SootMethod proxySM) { 283 | boolean ret = false; 284 | 285 | Body body = proxySM.retrieveActiveBody(); 286 | // System.out.println(body); // debug 287 | for (Unit unit : body.getUnits()) { 288 | Stmt stmt = (Stmt) unit; 289 | if (!stmt.containsInvokeExpr()) 290 | continue; 291 | 292 | SootMethod calleeSM = stmt.getInvokeExpr().getMethod(); 293 | if (calleeSM.getName().equals("transact")) { 294 | // System.out.println(stmt); 295 | ret = true; 296 | } 297 | } 298 | 299 | return ret; 300 | } 301 | 302 | private static void patchBody(SootMethod proxySM, SootMethod remoteSM) { 303 | // System.out.println(proxySM.getSignature() + " -> " + remoteSM.getSignature()); // debug 304 | 305 | // adjust the Body of the Binder proxy method 306 | Unit tgtUnit = null; 307 | ArrayList injectUnits = new ArrayList(); 308 | 309 | Body body = proxySM.retrieveActiveBody(); 310 | for (Unit unit : body.getUnits()) { 311 | Stmt stmt = (Stmt) unit; 312 | if (!stmt.containsInvokeExpr()) 313 | continue; 314 | 315 | SootMethod calleeSM = stmt.getInvokeExpr().getMethod(); 316 | if (calleeSM.getName().equals("transact")) { 317 | tgtUnit = stmt; 318 | 319 | // construct inject Units 320 | LocalGenerator lg = new LocalGenerator(body); 321 | 322 | Local boolLocal = lg.generateLocal(BooleanType.v()); 323 | Local byteLocal = lg.generateLocal(ByteType.v()); 324 | Local charLocal = lg.generateLocal(CharType.v()); 325 | Local doubleLocal = lg.generateLocal(DoubleType.v()); 326 | Local floatLocal = lg.generateLocal(FloatType.v()); 327 | Local intLocal = lg.generateLocal(IntType.v()); 328 | Local longLocal = lg.generateLocal(LongType.v()); 329 | Local nullLocal = lg.generateLocal(NullType.v()); 330 | Local shortLocal = lg.generateLocal(ShortType.v()); 331 | Local voidLocal = lg.generateLocal(VoidType.v()); 332 | ArrayList constantLocals = new ArrayList(); 333 | constantLocals.add(boolLocal); 334 | constantLocals.add(byteLocal); 335 | constantLocals.add(charLocal); 336 | constantLocals.add(doubleLocal); 337 | constantLocals.add(floatLocal); 338 | constantLocals.add(intLocal); 339 | constantLocals.add(longLocal); 340 | constantLocals.add(nullLocal); 341 | constantLocals.add(shortLocal); 342 | constantLocals.add(voidLocal); 343 | 344 | SootClass remoteSC = remoteSM.getDeclaringClass(); 345 | SootClass outerSC = remoteSC.getOuterClassUnsafe(); 346 | 347 | Local remoteLocal = lg.generateLocal(remoteSC.getType()); 348 | NewExpr newExpr = Jimple.v().newNewExpr(remoteSC.getType()); 349 | AssignStmt newStmt = Jimple.v().newAssignStmt(remoteLocal, newExpr); 350 | injectUnits.add(newStmt); 351 | 352 | try { 353 | SootMethod clinitSM = remoteSC.getMethodByName(""); 354 | InvokeExpr clinitInvokeExpr = Jimple.v().newStaticInvokeExpr(clinitSM.makeRef()); 355 | InvokeStmt clinitInvokeStmt = Jimple.v().newInvokeStmt(clinitInvokeExpr); 356 | injectUnits.add(clinitInvokeStmt); 357 | } catch (RuntimeException e) { } 358 | 359 | try { 360 | SootMethod initSM = CallgraphPatcherPre.fetchInitMethod(remoteSC); 361 | 362 | List params = new ArrayList(); 363 | for (int paramIdx = 0; paramIdx < initSM.getParameterCount(); paramIdx++) { 364 | Type paramType = initSM.getParameterType(paramIdx); 365 | if (paramType instanceof ArrayType) { 366 | Type arrayBaseType = ((ArrayType) paramType).baseType; 367 | int arrayDimension = ((ArrayType) paramType).numDimensions; 368 | Local arrayLocal = lg.generateLocal(ArrayType.v(arrayBaseType, arrayDimension)); 369 | params.add(arrayLocal); 370 | } else if (paramType instanceof BooleanType) { 371 | params.add(boolLocal); 372 | } else if (paramType instanceof ByteType) { 373 | params.add(byteLocal); 374 | } else if (paramType instanceof CharType) { 375 | params.add(charLocal); 376 | } else if (paramType instanceof DoubleType) { 377 | params.add(doubleLocal); 378 | } else if (paramType instanceof FloatType) { 379 | params.add(floatLocal); 380 | } else if (paramType instanceof IntType) { 381 | params.add(intLocal); 382 | } else if (paramType instanceof LongType) { 383 | params.add(longLocal); 384 | } else if (paramType instanceof NullType) { 385 | params.add(nullLocal); 386 | } else if (paramType instanceof ShortType) { 387 | params.add(shortLocal); 388 | } else if (paramType instanceof VoidType) { 389 | params.add(voidLocal); 390 | } else if (paramType instanceof RefType) { 391 | RefType paramRef = ((RefType) paramType); 392 | if (paramRef.hasSootClass()) { 393 | String typeRaw = paramRef.getSootClass().getType().toString(); 394 | if (typeRaw.equals("android.content.Context")) 395 | typeRaw = "android.app.ContextImpl"; 396 | SootClass typeSC = Scene.v().getSootClassUnsafe(typeRaw, false); 397 | 398 | Local refLocal = lg.generateLocal(typeSC.getType()); 399 | params.add(refLocal); 400 | 401 | NewExpr newRefExpr = Jimple.v().newNewExpr(typeSC.getType()); 402 | AssignStmt newRefStmt = Jimple.v().newAssignStmt(refLocal, newRefExpr); 403 | injectUnits.add(newRefStmt); 404 | 405 | // deal with outer class 406 | if (outerSC != null && typeSC == outerSC) { 407 | List initUnits = handleOuterClass(outerSC, refLocal, lg, constantLocals); 408 | injectUnits.addAll(initUnits); 409 | } 410 | } else { 411 | System.out.println("[WARN] [BinderPatcher]: RefType -> " + paramType); // debug 412 | params.add(nullLocal); 413 | } 414 | } else { 415 | System.out.println("[WARN] [BinderPatcher]: Special Type -> " + paramType); // debug 416 | params.add(nullLocal); 417 | } 418 | } 419 | 420 | InvokeExpr exprInvoke = Jimple.v().newVirtualInvokeExpr(remoteLocal, initSM.makeRef(), params); 421 | InvokeStmt stmtInvoke = Jimple.v().newInvokeStmt(exprInvoke); 422 | injectUnits.add(stmtInvoke); 423 | } catch (RuntimeException e) { } 424 | 425 | ArrayList params = new ArrayList(); 426 | for (int paramIdx = 0; paramIdx < body.getParameterLocals().size(); paramIdx++) { 427 | Local param = body.getParameterLocal(paramIdx); 428 | params.add(param); 429 | } 430 | InvokeExpr remoteInvokeExpr = Jimple.v().newVirtualInvokeExpr(remoteLocal, remoteSM.makeRef(), params); 431 | InvokeStmt remoteInvokeStmt = Jimple.v().newInvokeStmt(remoteInvokeExpr); 432 | injectUnits.add(remoteInvokeStmt); 433 | 434 | break; 435 | } 436 | } 437 | 438 | if (tgtUnit != null) { 439 | body.getUnits().insertAfter(injectUnits, tgtUnit); 440 | } 441 | 442 | // update the Body of the Binder proxy method 443 | proxySM.setActiveBody(body); 444 | // System.out.println(body); // debug 445 | } 446 | 447 | private static List handleOuterClass(SootClass outerSC, Local outerLocal, LocalGenerator lg, ArrayList constantLocals) { 448 | ArrayList injectUnits = new ArrayList(); 449 | 450 | Local boolLocal = constantLocals.get(0); 451 | Local byteLocal = constantLocals.get(1); 452 | Local charLocal = constantLocals.get(2); 453 | Local doubleLocal = constantLocals.get(3); 454 | Local floatLocal = constantLocals.get(4); 455 | Local intLocal = constantLocals.get(5); 456 | Local longLocal = constantLocals.get(6); 457 | Local nullLocal = constantLocals.get(7); 458 | Local shortLocal = constantLocals.get(8); 459 | Local voidLocal = constantLocals.get(9); 460 | 461 | try { 462 | SootMethod clinitSM = outerSC.getMethodByName(""); 463 | InvokeExpr clinitInvokeExpr = Jimple.v().newStaticInvokeExpr(clinitSM.makeRef()); 464 | InvokeStmt clinitInvokeStmt = Jimple.v().newInvokeStmt(clinitInvokeExpr); 465 | injectUnits.add(clinitInvokeStmt); 466 | } catch (RuntimeException e) { } 467 | 468 | try { 469 | SootMethod initSM = CallgraphPatcherPre.fetchInitMethod(outerSC); 470 | 471 | List params = new ArrayList(); 472 | for (int paramIdx = 0; paramIdx < initSM.getParameterCount(); paramIdx++) { 473 | Type paramType = initSM.getParameterType(paramIdx); 474 | if (paramType instanceof ArrayType) { 475 | Type arrayBaseType = ((ArrayType) paramType).baseType; 476 | int arrayDimension = ((ArrayType) paramType).numDimensions; 477 | Local arrayLocal = lg.generateLocal(ArrayType.v(arrayBaseType, arrayDimension)); 478 | params.add(arrayLocal); 479 | } else if (paramType instanceof BooleanType) { 480 | params.add(boolLocal); 481 | } else if (paramType instanceof ByteType) { 482 | params.add(byteLocal); 483 | } else if (paramType instanceof CharType) { 484 | params.add(charLocal); 485 | } else if (paramType instanceof DoubleType) { 486 | params.add(doubleLocal); 487 | } else if (paramType instanceof FloatType) { 488 | params.add(floatLocal); 489 | } else if (paramType instanceof IntType) { 490 | params.add(intLocal); 491 | } else if (paramType instanceof LongType) { 492 | params.add(longLocal); 493 | } else if (paramType instanceof NullType) { 494 | params.add(nullLocal); 495 | } else if (paramType instanceof ShortType) { 496 | params.add(shortLocal); 497 | } else if (paramType instanceof VoidType) { 498 | params.add(voidLocal); 499 | } else if (paramType instanceof RefType) { 500 | RefType paramRef = ((RefType) paramType); 501 | if (paramRef.hasSootClass()) { 502 | String typeRaw = paramRef.getSootClass().getType().toString(); 503 | if (typeRaw.equals("android.content.Context")) 504 | typeRaw = "android.app.ContextImpl"; 505 | SootClass typeSC = Scene.v().getSootClassUnsafe(typeRaw, false); 506 | 507 | Local refLocal = lg.generateLocal(typeSC.getType()); 508 | params.add(refLocal); 509 | 510 | NewExpr newRefExpr = Jimple.v().newNewExpr(typeSC.getType()); 511 | AssignStmt newRefStmt = Jimple.v().newAssignStmt(refLocal, newRefExpr); 512 | injectUnits.add(newRefStmt); 513 | } else { 514 | System.out.println("[WARN] [BinderPatcher]: RefType -> " + paramType); // debug 515 | params.add(nullLocal); 516 | } 517 | } else { 518 | System.out.println("[WARN] [BinderPatcher]: Special Type -> " + paramType); // debug 519 | params.add(nullLocal); 520 | } 521 | } 522 | 523 | InvokeExpr exprInvoke = Jimple.v().newVirtualInvokeExpr(outerLocal, initSM.makeRef(), params); 524 | InvokeStmt stmtInvoke = Jimple.v().newInvokeStmt(exprInvoke); 525 | injectUnits.add(stmtInvoke); 526 | } catch (RuntimeException e) { } 527 | 528 | return injectUnits; 529 | } 530 | 531 | // ---- // 532 | 533 | public static void patchPost() { 534 | CallGraph cg = Scene.v().getCallGraph(); 535 | 536 | HashSet removes = new HashSet(); 537 | 538 | // case-0 539 | Iterator edgeIter = cg.iterator(); 540 | while (edgeIter.hasNext()) { 541 | Edge edge = edgeIter.next(); 542 | SootMethod srcSM = edge.src(); 543 | SootClass srcSC = srcSM.getDeclaringClass(); 544 | if (binderProxyRemoteMap.containsKey(srcSC)) { 545 | SootMethod tgtSM = edge.tgt(); 546 | SootClass tgtSC = tgtSM.getDeclaringClass(); 547 | if (binderProxyRemoteMap.get(srcSC).contains(tgtSC) && tgtSM.getName().equals("")) { 548 | removes.add(edge); 549 | } 550 | } 551 | } 552 | 553 | // case-1 554 | edgeIter = cg.iterator(); 555 | while (edgeIter.hasNext()) { 556 | Edge edge = edgeIter.next(); 557 | SootMethod tgtSM = edge.tgt(); 558 | if (tgtSM.getName().equals("onTransact")) { 559 | // System.out.println(tgtSM.getSignature()); 560 | removes.add(edge); 561 | } 562 | } 563 | 564 | // case-2 565 | edgeIter = cg.iterator(); 566 | while (edgeIter.hasNext()) { 567 | Edge edge = edgeIter.next(); 568 | SootMethod tgtSM = edge.tgt(); 569 | if (tgtSM.getName().equals("writeToParcel")) { 570 | // System.out.println(tgtSM.getSignature()); 571 | removes.add(edge); 572 | } 573 | } 574 | 575 | for (Edge remove : removes) { 576 | cg.removeEdge(remove); 577 | } 578 | 579 | Scene.v().setCallGraph(cg); 580 | } 581 | 582 | } 583 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/CallgraphBuilder.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.Collections; 4 | 5 | import hk.polyu.soot.preprocess.SootInitializer; 6 | import soot.PackManager; 7 | import soot.Scene; 8 | import soot.SootClass; 9 | import soot.SootMethod; 10 | import soot.jimple.toolkits.callgraph.CallGraph; 11 | import soot.jimple.toolkits.callgraph.Edge; 12 | 13 | public class CallgraphBuilder { 14 | 15 | public static void build() { 16 | 17 | } 18 | 19 | public static void generateCallgraph(String binderProxy) { 20 | EntryPointCreator.specBinderProxyName = binderProxy; 21 | SootMethod entryPoint = EntryPointCreator.create(); 22 | Scene.v().setEntryPoints(Collections.singletonList(entryPoint)); 23 | PackManager.v().runPacks(); 24 | System.out.println(" -->> Build Callgraph Finish"); 25 | } 26 | 27 | // ---- ---- ---- ---- ---- // 28 | 29 | // module test 30 | public static void main(String[] args) { 31 | SootInitializer.init(); 32 | 33 | // SootClass sc = Scene.v().getSootClassUnsafe("com.android.server.display.DisplayManagerService$BinderService", false); 34 | // SootMethod sm = sc.getMethodByName("createVirtualDisplay"); 35 | 36 | // perform the analysis that does not rely on Callgraph 37 | // hk.polyu.soot.preprocess.PermissionCheckFinder.find(); 38 | // hk.polyu.soot.preprocess.UidCheckFinder.find(); 39 | 40 | // 41 | int classNumber = 0; 42 | int methodNumber = 0; 43 | for (SootClass sc : Scene.v().getClasses()) { 44 | // System.out.println(sc.getName()); 45 | classNumber++; 46 | for (SootMethod sm : sc.getMethods()) { 47 | if (sm.isConcrete()) 48 | methodNumber++; 49 | } 50 | } 51 | System.out.println("Class Number: " + classNumber); 52 | System.out.println("Method Number: " + methodNumber); 53 | // System.exit(0); 54 | // 55 | 56 | // build Callgraph 57 | CallgraphPatcherPre.patch(); 58 | // generateCallgraph("lineageos.media.ILineageAudioService$Stub$Proxy"); 59 | generateCallgraph("android.media.IAudioService$Stub$Proxy"); 60 | // generateCallgraph("android.hardware.display.IDisplayManager$Stub$Proxy"); 61 | // generateCallgraph("android.os.IVibratorService$Stub$Proxy"); 62 | // generateCallgraph("android.hardware.IConsumerIrService$Stub$Proxy"); 63 | // generateCallgraph("android.app.IActivityTaskManager$Stub$Proxy"); 64 | CallgraphPatcherPost.patch(); 65 | System.exit(0); 66 | 67 | /* 68 | for (Edge edge : Scene.v().getCallGraph()) { 69 | System.out.println(edge); 70 | } 71 | */ 72 | 73 | /* 74 | for (SootClass sc : Scene.v().getClasses()) { 75 | if (sc.getName().equals("android.app.UiAutomationConnection")) { 76 | System.out.println(sc); 77 | for (SootMethod sm : sc.getMethods()) { 78 | // if (sm.getSignature().equals("")) { 79 | // if (sm.getName().equals("hasSystemFeature")) { 80 | // if (sm.getSignature().equals("")) { 81 | if (true) { 82 | System.out.println(sm.getSignature()); 83 | if (sm.isConcrete()) 84 | System.out.println(sm.retrieveActiveBody()); 85 | } 86 | } 87 | } 88 | } 89 | */ 90 | 91 | // System.exit(0); 92 | 93 | // perform the analysis that relies on Callgraph 94 | CallGraph cg = Scene.v().getCallGraph(); 95 | StaticFieldResolver.resolve(cg); 96 | PermissionCheckFinder.find(cg); 97 | UIDCheckFinder.find(cg); 98 | // UserIDCheckFinder.find(cg); // ignore currently 99 | 100 | JNIAnalyzer.analyze(cg); 101 | // 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/CallgraphPatcherPost.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | public class CallgraphPatcherPost { 4 | 5 | public static void patch() { 6 | // patch the confusing edges brought by BinderPatcher 7 | BinderPatcher.patchPost(); 8 | // patch the confusing edges brought by android.os.Handler 9 | HandlerPatcher.patchPost(); 10 | // patch the confusing edges brought by android.os.Parcel 11 | ParcelPatcher.patchPost(); 12 | // patch edges to and 13 | ConstructorPatcher.patchPost(); 14 | // patch invalid edges introduced by the "run" method 15 | RunPatcher.patchPost(); 16 | // patch miscellaneous invalid edges 17 | InvalidEdgePatcher.patchPost(); 18 | // patch the missing edges 19 | EdgePatcher.patchPost(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/CallgraphPatcherPre.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.HashSet; 4 | 5 | import soot.Body; 6 | import soot.SootClass; 7 | import soot.SootMethod; 8 | import soot.Unit; 9 | import soot.jimple.Stmt; 10 | 11 | public class CallgraphPatcherPre { 12 | 13 | public static void patch() { 14 | // patch SPART analysis 15 | SPARKPatcher.init(); 16 | SPARKPatcher.patch(); 17 | System.out.println(" -->> Patch SPARK Finish"); 18 | // patch Binder-related issue 19 | BinderPatcher.init(); 20 | BinderPatcher.patch(); 21 | System.out.println(" -->> Patch IPC (Binder) Finish"); 22 | } 23 | 24 | public static SootMethod fetchInitMethod(SootClass sc) throws RuntimeException { 25 | HashSet initSMs = new HashSet(); 26 | 27 | for (SootMethod sm : sc.getMethods()) { 28 | if (!sm.isConcrete()) 29 | continue; 30 | if (sm.getName().equals("")) 31 | initSMs.add(sm); 32 | } 33 | 34 | if (initSMs.size() == 1) 35 | return sc.getMethodByName(""); 36 | 37 | HashSet indirectInitSMs = new HashSet(); 38 | for (SootMethod candidate : initSMs) { 39 | Body body = candidate.retrieveActiveBody(); 40 | for (Unit unit : body.getUnits()) { 41 | Stmt stmt = (Stmt) unit; 42 | if (!stmt.containsInvokeExpr()) 43 | continue; 44 | 45 | SootMethod callee = stmt.getInvokeExpr().getMethod(); 46 | if ((callee.getDeclaringClass() == sc) && callee.getName().equals("")) 47 | indirectInitSMs.add(callee); 48 | } 49 | } 50 | 51 | for (SootMethod candidate : initSMs) 52 | if (!indirectInitSMs.contains(candidate)) 53 | return candidate; // randomly select one 54 | 55 | throw new RuntimeException(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/CallgraphWrapper.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.Iterator; 6 | 7 | import soot.Scene; 8 | import soot.SootClass; 9 | import soot.SootMethod; 10 | import soot.jimple.toolkits.callgraph.CallGraph; 11 | import soot.jimple.toolkits.callgraph.Edge; 12 | 13 | public class CallgraphWrapper { 14 | 15 | public static int PATH_MAX_LENGTH = 13; // (16-3) 16 | 17 | public static HashSet> jniPaths = null; 18 | 19 | public static HashSet> findJNIPaths() { 20 | // for speed-up 21 | if (jniPaths != null && !jniPaths.isEmpty()) 22 | return jniPaths; 23 | 24 | if (jniPaths == null) { 25 | jniPaths = new HashSet>(); 26 | } 27 | 28 | CallGraph cg = Scene.v().getCallGraph(); 29 | 30 | SootMethod mainSM = Scene.v().getMethod(""); 31 | ArrayList curPath = new ArrayList(); 32 | curPath.add(mainSM); 33 | 34 | Iterator edgeIter = cg.edgesOutOf(mainSM); 35 | while (edgeIter.hasNext()) { 36 | Edge edge = edgeIter.next(); 37 | SootMethod tgt = edge.tgt(); 38 | findJNIPathsInternal(cg, tgt, curPath); 39 | } 40 | 41 | /* 42 | try { 43 | File tmp = new File("tmp.txt"); 44 | BufferedWriter bw = new BufferedWriter(new FileWriter(tmp)); 45 | int idx = 0; 46 | for (ArrayList path : jniPaths) { 47 | // System.out.println("[Path-" + idx + "]"); 48 | bw.write("[Path-" + idx + "]" + "\n"); 49 | for (SootMethod sm : path) { 50 | // System.out.println("\t" + sm.getSignature()); 51 | bw.write("\t" + sm.getSignature() + "\n"); 52 | } 53 | idx += 1; 54 | } 55 | bw.flush(); 56 | } catch(Exception e) { 57 | // pass 58 | } 59 | */ 60 | 61 | return jniPaths; 62 | } 63 | 64 | private static void findJNIPathsInternal(CallGraph cg, SootMethod src, ArrayList path) { 65 | // exceeds the maximum path length 66 | if (path.size() >= PATH_MAX_LENGTH) 67 | return; 68 | // contains more than one Binder transactions 69 | int binderCnt = 0; 70 | for (SootMethod sm : path) { 71 | SootClass sc = sm.getDeclaringClass(); 72 | if (sc.getName().endsWith("$Stub$Proxy")) 73 | binderCnt += 1; 74 | } 75 | if (binderCnt > 1) 76 | return; 77 | if (binderCnt == 1 && src.getDeclaringClass().getName().endsWith("$Stub$Proxy")) 78 | return; 79 | // finds nested method call 80 | if (path.contains(src)) 81 | return; 82 | 83 | // ---- // 84 | 85 | ArrayList curPath = new ArrayList(path); 86 | curPath.add(src); 87 | if (src.isNative() && !src.isPhantom() && binderCnt == 1) { 88 | jniPaths.add(curPath); 89 | return; 90 | } 91 | 92 | Iterator edgeIter = cg.edgesOutOf(src); 93 | while (edgeIter.hasNext()) { 94 | Edge edge = edgeIter.next(); 95 | SootMethod tgt = edge.tgt(); 96 | findJNIPathsInternal(cg, tgt, curPath); 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/ConstructorPatcher.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | 6 | import soot.Scene; 7 | import soot.SootMethod; 8 | import soot.jimple.toolkits.callgraph.CallGraph; 9 | import soot.jimple.toolkits.callgraph.Edge; 10 | 11 | public class ConstructorPatcher { 12 | 13 | public static void init() {} 14 | 15 | public static void patchPost() { 16 | CallGraph cg = Scene.v().getCallGraph(); 17 | 18 | HashSet removes = new HashSet(); 19 | 20 | Iterator edgeIter = cg.iterator(); 21 | while (edgeIter.hasNext()) { 22 | Edge edge = edgeIter.next(); 23 | SootMethod tgtSM = edge.tgt(); 24 | if (tgtSM.getName().equals("")) { 25 | // System.out.println(tgt.getSignature()); 26 | removes.add(edge); 27 | continue; 28 | } 29 | if (tgtSM.getName().equals("")) { 30 | // System.out.println(tgt.getSignature()); 31 | removes.add(edge); 32 | continue; 33 | } 34 | } 35 | 36 | for (Edge remove : removes) { 37 | cg.removeEdge(remove); 38 | } 39 | 40 | Scene.v().setCallGraph(cg); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/EdgePatcher.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | 6 | import soot.Body; 7 | import soot.Scene; 8 | import soot.SootMethod; 9 | import soot.Unit; 10 | import soot.jimple.Stmt; 11 | import soot.jimple.toolkits.callgraph.CallGraph; 12 | import soot.jimple.toolkits.callgraph.Edge; 13 | 14 | public class EdgePatcher { 15 | 16 | public static void init() {} 17 | 18 | public static void patchPost() { 19 | CallGraph cg = Scene.v().getCallGraph(); 20 | 21 | // 1. collect source SootMethods from callgraph 22 | HashSet srcSMs = new HashSet(); 23 | 24 | Iterator edgeIter = cg.iterator(); 25 | while (edgeIter.hasNext()) { 26 | Edge edge = edgeIter.next(); 27 | SootMethod srcSM = edge.src(); 28 | srcSMs.add(srcSM); 29 | } 30 | 31 | // 2. heuristically add the missing callgraph edges 32 | for (SootMethod srcSM : srcSMs) { 33 | if (!srcSM.isConcrete()) 34 | continue; 35 | if (!cg.edgesInto(srcSM).hasNext()) 36 | continue; 37 | 38 | Body body = srcSM.retrieveActiveBody(); 39 | for (Unit unit : body.getUnits()) { 40 | Stmt stmt = (Stmt) unit; 41 | if (!stmt.containsInvokeExpr()) 42 | continue; 43 | 44 | if (cg.edgesOutOf(unit).hasNext()) 45 | continue; 46 | 47 | SootMethod src = srcSM; 48 | SootMethod tgt = stmt.getInvokeExpr().getMethod(); 49 | if (tgt.getName().equals("") || tgt.getName().equals("")) 50 | continue; 51 | Edge newEdge = new Edge(src, stmt, tgt); 52 | // System.out.println("[Add] " + newEdge); 53 | cg.addEdge(newEdge); 54 | } 55 | } 56 | 57 | Scene.v().setCallGraph(cg); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/EntryPointCreator.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import soot.ArrayType; 8 | import soot.Body; 9 | import soot.BooleanType; 10 | import soot.ByteType; 11 | import soot.CharType; 12 | import soot.DoubleType; 13 | import soot.FloatType; 14 | import soot.IntType; 15 | import soot.Local; 16 | import soot.LongType; 17 | import soot.Modifier; 18 | import soot.NullType; 19 | import soot.RefType; 20 | import soot.Scene; 21 | import soot.ShortType; 22 | import soot.SootClass; 23 | import soot.SootMethod; 24 | import soot.Type; 25 | import soot.Unit; 26 | import soot.Value; 27 | import soot.VoidType; 28 | import soot.javaToJimple.LocalGenerator; 29 | import soot.jimple.AssignStmt; 30 | import soot.jimple.IdentityStmt; 31 | import soot.jimple.InvokeExpr; 32 | import soot.jimple.InvokeStmt; 33 | import soot.jimple.Jimple; 34 | import soot.jimple.NewExpr; 35 | 36 | public class EntryPointCreator { 37 | 38 | public static String specBinderProxyName = ""; 39 | 40 | public static SootMethod create() { 41 | // create dummy main class 42 | SootClass dummyMainClass = Scene.v().makeSootClass("hk.polyu.Main"); 43 | dummyMainClass.setResolvingLevel(SootClass.BODIES); 44 | Scene.v().addClass(dummyMainClass); 45 | 46 | // create entry-point method 47 | Type stringArrayType = ArrayType.v(RefType.v("java.lang.String"), 1); 48 | SootMethod dummyMainMethod = Scene.v().makeSootMethod("main", Collections.singletonList(stringArrayType), VoidType.v()); 49 | dummyMainMethod.setModifiers(Modifier.PUBLIC | Modifier.STATIC); 50 | 51 | // create method body 52 | createBody(dummyMainMethod); 53 | // System.out.println(dummyMainMethod.retrieveActiveBody()); // debug 54 | 55 | // add entry-point method to dummy main class 56 | dummyMainClass.addMethod(dummyMainMethod); 57 | 58 | // add dummy main class to soot 59 | dummyMainClass.setApplicationClass(); 60 | 61 | return dummyMainMethod; 62 | } 63 | 64 | private static void createBody(SootMethod dummyMainMethod) { 65 | Body body = Jimple.v().newBody(); 66 | body.setMethod(dummyMainMethod); 67 | dummyMainMethod.setActiveBody(body); 68 | 69 | LocalGenerator lg = new LocalGenerator(body); 70 | 71 | List bodyStmtList = new ArrayList(); 72 | 73 | // add the parameter reference to the body 74 | Local paramLocal = lg.generateLocal(ArrayType.v(RefType.v("java.lang.String"), 1)); 75 | IdentityStmt stmtParam = Jimple.v().newIdentityStmt(paramLocal, Jimple.v().newParameterRef(ArrayType.v(RefType.v("java.lang.String"), 1), 0)); 76 | bodyStmtList.add(stmtParam); 77 | 78 | Local boolLocal = lg.generateLocal(BooleanType.v()); 79 | Local byteLocal = lg.generateLocal(ByteType.v()); 80 | Local charLocal = lg.generateLocal(CharType.v()); 81 | Local doubleLocal = lg.generateLocal(DoubleType.v()); 82 | Local floatLocal = lg.generateLocal(FloatType.v()); 83 | Local intLocal = lg.generateLocal(IntType.v()); 84 | Local longLocal = lg.generateLocal(LongType.v()); 85 | Local nullLocal = lg.generateLocal(NullType.v()); 86 | Local shortLocal = lg.generateLocal(ShortType.v()); 87 | Local voidLocal = lg.generateLocal(VoidType.v()); 88 | 89 | { 90 | String binderProxy = specBinderProxyName; 91 | // System.out.println(binderProxy); // debug 92 | SootClass binderProxySC = Scene.v().getSootClassUnsafe(binderProxy, false); 93 | assert binderProxySC != null && !binderProxySC.isPhantom(); 94 | 95 | // constructors 96 | Local serviceManagerLocal = lg.generateLocal(binderProxySC.getType()); 97 | NewExpr newExpr = Jimple.v().newNewExpr(binderProxySC.getType()); 98 | AssignStmt newStmt = Jimple.v().newAssignStmt(serviceManagerLocal, newExpr); 99 | bodyStmtList.add(newStmt); 100 | 101 | try { 102 | SootMethod clinitSM = binderProxySC.getMethodByName(""); 103 | InvokeExpr clinitInvokeExpr = Jimple.v().newStaticInvokeExpr(clinitSM.makeRef()); 104 | InvokeStmt clinitInvokeStmt = Jimple.v().newInvokeStmt(clinitInvokeExpr); 105 | bodyStmtList.add(clinitInvokeStmt); 106 | } catch (RuntimeException e) { } 107 | 108 | try { 109 | SootMethod initSM = binderProxySC.getMethodByName(""); 110 | List params = new ArrayList(); 111 | for (int paramIdx = 0; paramIdx < initSM.getParameterCount(); paramIdx++) { 112 | Type paramType = initSM.getParameterType(paramIdx); 113 | if (paramType instanceof ArrayType) { 114 | Type arrayBaseType = ((ArrayType) paramType).baseType; 115 | int arrayDimension = ((ArrayType) paramType).numDimensions; 116 | Local arrayLocal = lg.generateLocal(ArrayType.v(arrayBaseType, arrayDimension)); 117 | params.add(arrayLocal); 118 | } else if (paramType instanceof BooleanType) { 119 | params.add(boolLocal); 120 | } else if (paramType instanceof ByteType) { 121 | params.add(byteLocal); 122 | } else if (paramType instanceof CharType) { 123 | params.add(charLocal); 124 | } else if (paramType instanceof DoubleType) { 125 | params.add(doubleLocal); 126 | } else if (paramType instanceof FloatType) { 127 | params.add(floatLocal); 128 | } else if (paramType instanceof IntType) { 129 | params.add(intLocal); 130 | } else if (paramType instanceof LongType) { 131 | params.add(longLocal); 132 | } else if (paramType instanceof NullType) { 133 | params.add(nullLocal); 134 | } else if (paramType instanceof ShortType) { 135 | params.add(shortLocal); 136 | } else if (paramType instanceof VoidType) { 137 | params.add(voidLocal); 138 | } else if (paramType instanceof RefType) { 139 | RefType paramRef = ((RefType) paramType); 140 | if (paramRef.hasSootClass()) { 141 | Local refLocal = lg.generateLocal(paramRef.getSootClass().getType()); 142 | params.add(refLocal); 143 | 144 | NewExpr newRefExpr = Jimple.v().newNewExpr(paramRef.getSootClass().getType()); 145 | AssignStmt newRefStmt = Jimple.v().newAssignStmt(refLocal, newRefExpr); 146 | bodyStmtList.add(newRefStmt); 147 | } else { 148 | System.out.println("\t\t" + "RefType -> " + paramType); // debug 149 | params.add(nullLocal); 150 | } 151 | } else { 152 | System.out.println("\t\t" + "Special Type -> " + paramType); // debug 153 | params.add(nullLocal); 154 | } 155 | } 156 | 157 | InvokeExpr exprInvoke = Jimple.v().newVirtualInvokeExpr(serviceManagerLocal, initSM.makeRef(), params); 158 | InvokeStmt stmtInvoke = Jimple.v().newInvokeStmt(exprInvoke); 159 | bodyStmtList.add(stmtInvoke); 160 | } catch (RuntimeException e) { } 161 | 162 | // other methods 163 | for (SootMethod calleeSM : binderProxySC.getMethods()) { 164 | String calleeName = calleeSM.getName(); 165 | if (calleeName.equals("") || calleeName.equals("") || calleeName.startsWith("access$") || calleeName.startsWith("lambda$")) 166 | continue; // ignore special cases of methods 167 | 168 | // System.out.println("\t" + calleeSM.getSignature()); 169 | // System.out.println(calleeSM.retrieveActiveBody()); 170 | 171 | List params = new ArrayList(); 172 | for (int paramIdx = 0; paramIdx < calleeSM.getParameterCount(); paramIdx++) { 173 | Type paramType = calleeSM.getParameterType(paramIdx); 174 | if (paramType instanceof ArrayType) { 175 | Type arrayBaseType = ((ArrayType) paramType).baseType; 176 | int arrayDimension = ((ArrayType) paramType).numDimensions; 177 | Local arrayLocal = lg.generateLocal(ArrayType.v(arrayBaseType, arrayDimension)); 178 | params.add(arrayLocal); 179 | } else if (paramType instanceof BooleanType) { 180 | params.add(boolLocal); 181 | } else if (paramType instanceof ByteType) { 182 | params.add(byteLocal); 183 | } else if (paramType instanceof CharType) { 184 | params.add(charLocal); 185 | } else if (paramType instanceof DoubleType) { 186 | params.add(doubleLocal); 187 | } else if (paramType instanceof FloatType) { 188 | params.add(floatLocal); 189 | } else if (paramType instanceof IntType) { 190 | params.add(intLocal); 191 | } else if (paramType instanceof LongType) { 192 | params.add(longLocal); 193 | } else if (paramType instanceof NullType) { 194 | params.add(nullLocal); 195 | } else if (paramType instanceof ShortType) { 196 | params.add(shortLocal); 197 | } else if (paramType instanceof VoidType) { 198 | params.add(voidLocal); 199 | } else if (paramType instanceof RefType) { 200 | RefType paramRef = ((RefType) paramType); 201 | if (paramRef.hasSootClass()) { 202 | Local refLocal = lg.generateLocal(paramRef.getSootClass().getType()); 203 | params.add(refLocal); 204 | 205 | NewExpr newRefExpr = Jimple.v().newNewExpr(paramRef.getSootClass().getType()); 206 | AssignStmt newRefStmt = Jimple.v().newAssignStmt(refLocal, newRefExpr); 207 | bodyStmtList.add(newRefStmt); 208 | } else { 209 | System.out.println("\t\t" + "RefType -> " + paramType); // debug 210 | params.add(nullLocal); 211 | } 212 | } else { 213 | System.out.println("\t\t" + "Special Type -> " + paramType); // debug 214 | params.add(nullLocal); 215 | } 216 | } 217 | 218 | if (calleeSM.isStatic()) { 219 | InvokeExpr exprInvoke = Jimple.v().newStaticInvokeExpr(calleeSM.makeRef(), params); 220 | InvokeStmt stmtInvoke = Jimple.v().newInvokeStmt(exprInvoke); 221 | bodyStmtList.add(stmtInvoke); 222 | } else { 223 | try { 224 | InvokeExpr exprInvoke = Jimple.v().newVirtualInvokeExpr(serviceManagerLocal, calleeSM.makeRef(), params); 225 | InvokeStmt stmtInvoke = Jimple.v().newInvokeStmt(exprInvoke); 226 | bodyStmtList.add(stmtInvoke); 227 | } catch (Exception e) { 228 | // pass 229 | } 230 | } 231 | } 232 | } 233 | 234 | body.getUnits().addAll(bodyStmtList); 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/HandlerPatcher.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | 6 | import soot.Scene; 7 | import soot.SootMethod; 8 | import soot.jimple.toolkits.callgraph.CallGraph; 9 | import soot.jimple.toolkits.callgraph.Edge; 10 | 11 | public class HandlerPatcher { 12 | 13 | public static void init() {} 14 | 15 | public static void patchPost() { 16 | CallGraph cg = Scene.v().getCallGraph(); 17 | 18 | HashSet removes = new HashSet(); 19 | 20 | Iterator edgeIter = cg.iterator(); 21 | while (edgeIter.hasNext()) { 22 | Edge edge = edgeIter.next(); 23 | SootMethod tgtSM = edge.tgt(); 24 | if (tgtSM.getDeclaringClass().getName().equals("android.os.Handler")) { 25 | // System.out.println(tgtSM.getSignature()); 26 | removes.add(edge); 27 | } 28 | } 29 | 30 | for (Edge remove : removes) { 31 | cg.removeEdge(remove); 32 | } 33 | 34 | Scene.v().setCallGraph(cg); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/InvalidEdgePatcher.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.HashMap; 4 | import java.util.HashSet; 5 | import java.util.Iterator; 6 | 7 | import soot.Scene; 8 | import soot.SootMethod; 9 | import soot.jimple.toolkits.callgraph.CallGraph; 10 | import soot.jimple.toolkits.callgraph.Edge; 11 | 12 | public class InvalidEdgePatcher { 13 | 14 | public static void init() {} 15 | 16 | public static void patchPost() { 17 | CallGraph cg = Scene.v().getCallGraph(); 18 | 19 | HashSet removes = new HashSet(); 20 | 21 | // case-1 22 | 23 | // 1. collect the source methods from the callgraph 24 | HashSet srcs = new HashSet(); 25 | Iterator edgeIter = cg.iterator(); 26 | while (edgeIter.hasNext()) { 27 | Edge edge = edgeIter.next(); 28 | if ((edge.srcStmt() != null) && (edge.srcStmt().toString().contains(".permission."))) 29 | continue; 30 | 31 | SootMethod srcSM = edge.src(); 32 | if (srcSM.getName().contains("permission") || srcSM.getName().contains("Permission")) 33 | continue; 34 | 35 | srcs.add(srcSM); 36 | } 37 | 38 | // 2. analyze each source method 39 | for (SootMethod srcSM : srcs) { 40 | HashMap> callees = new HashMap>(); 41 | 42 | Iterator outIter = cg.edgesOutOf(srcSM); 43 | while (outIter.hasNext()) { 44 | Edge outEdge = outIter.next(); 45 | if ((outEdge.srcStmt() != null) && (outEdge.srcStmt().toString().contains(".permission."))) 46 | continue; 47 | 48 | SootMethod tgtSM = outEdge.tgt(); 49 | if (tgtSM.getName().contains("permission") || tgtSM.getName().contains("Permission")) 50 | continue; 51 | 52 | if (!callees.containsKey(tgtSM.getName())) { 53 | callees.put(tgtSM.getName(), new HashSet()); 54 | } 55 | callees.get(tgtSM.getName()).add(outEdge); 56 | } 57 | 58 | for (String tgtName : callees.keySet()) { 59 | HashSet outEdges = callees.get(tgtName); 60 | if (outEdges.size() > 1) { 61 | removes.addAll(outEdges); 62 | } 63 | } 64 | } 65 | 66 | for (Edge remove : removes) { 67 | // System.out.println("[Remove] " + remove); 68 | cg.removeEdge(remove); 69 | } 70 | 71 | Scene.v().setCallGraph(cg); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/JNIAnalyzer.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | import java.util.Iterator; 11 | import java.util.LinkedList; 12 | import java.util.List; 13 | import java.util.Queue; 14 | 15 | import hk.polyu.Config; 16 | import soot.Body; 17 | import soot.SootClass; 18 | import soot.SootField; 19 | import soot.SootMethod; 20 | import soot.Unit; 21 | import soot.ValueBox; 22 | import soot.jimple.StaticFieldRef; 23 | import soot.jimple.Stmt; 24 | import soot.jimple.StringConstant; 25 | import soot.jimple.toolkits.callgraph.CallGraph; 26 | import soot.jimple.toolkits.callgraph.Edge; 27 | import soot.toolkits.graph.BriefUnitGraph; 28 | import soot.toolkits.graph.MHGDominatorsFinder; 29 | 30 | public class JNIAnalyzer { 31 | 32 | public static HashSet> paths = CallgraphWrapper.findJNIPaths(); 33 | 34 | public static void analyze(CallGraph cg) { 35 | HashSet jniSMs = collectJNIMethods(cg); 36 | 37 | // Phase-1: we get the permission-guarded JNI methods 38 | System.out.println(" -->> " + "Phase-1: Start"); 39 | HashMap> jni2permissions = checkPermission(cg, jniSMs); 40 | System.out.println(" <<-- " + "Phase-1: Finish"); 41 | // Phase-2: we get the UID-guarded JNI methods 42 | System.out.println(" -->> " + "Phase-2: Start"); 43 | HashMap> jni2uids = checkUID(cg, jniSMs); 44 | System.out.println(" <<-- " + "Phase-2: Finish"); 45 | // Phase-3 [IGNORE]: we get the UserID-guarded JNI methods 46 | // System.out.println(" -->> " + "Phase-3: Start"); 47 | // HashSet jni1userid = checkUserID(cg, jniSMs); 48 | // System.out.println(" <<-- " + "Phase-3: Finish"); 49 | 50 | HashSet guardJNISMs = new HashSet(); 51 | guardJNISMs.addAll(jni2permissions.keySet()); 52 | guardJNISMs.addAll(jni2uids.keySet()); 53 | // guardJNISMs.addAll(jni1userid); 54 | 55 | // write to file "jnimethods.txt" 56 | File jniFile = new File(Config.OutputJNIMethodFile); 57 | try { 58 | if (!jniFile.exists()) 59 | jniFile.createNewFile(); 60 | 61 | BufferedWriter bw = new BufferedWriter(new FileWriter(jniFile, true)); 62 | for (SootMethod jniSM : guardJNISMs) { 63 | System.out.println("[JNI method] " + jniSM.getSignature()); 64 | bw.write(jniSM.getSignature() + "\n"); 65 | if (jni2permissions.containsKey(jniSM)) { 66 | for (String permission : jni2permissions.get(jniSM)) { 67 | System.out.println(" " + "[Permission] " + permission); 68 | bw.write(" " + permission + "\n"); 69 | } 70 | } 71 | if (jni2uids.containsKey(jniSM)) { 72 | for (String uid : jni2uids.get(jniSM)) { 73 | System.out.println(" " + "[UID] " + uid); 74 | bw.write(" " + uid + "\n"); 75 | } 76 | } 77 | // if (jni1userid.contains(jniSM)) { 78 | // System.out.println(" " + "[UserId] " + "True"); 79 | // bw.write(" " + "UserID" + "\n"); 80 | // } 81 | } 82 | bw.flush(); 83 | bw.close(); 84 | } catch (IOException ioe) { 85 | ioe.printStackTrace(); 86 | } 87 | } 88 | 89 | private static HashSet collectJNIMethods(CallGraph cg) { 90 | // collect reachable JNI methods in CallGraph 91 | HashSet jniSMs = new HashSet(); // output 92 | 93 | for (ArrayList path : paths) { 94 | SootMethod jniMethod = path.get(path.size() - 1); 95 | if (jniMethod.getDeclaringClass().getName().startsWith("android.util.") 96 | || jniMethod.getDeclaringClass().getName().startsWith("android.os.") 97 | || jniMethod.getDeclaringClass().getName().startsWith("android.net.") 98 | || jniMethod.getDeclaringClass().getName().startsWith("android.content.res.") 99 | || jniMethod.getDeclaringClass().getName().startsWith("android.graphics.") 100 | || jniMethod.getDeclaringClass().getName().startsWith("android.database.") 101 | || jniMethod.getDeclaringClass().getName().startsWith("com.android.internal.os.") 102 | || jniMethod.getDeclaringClass().getName().startsWith("com.android.internal.content.") 103 | || jniMethod.getDeclaringClass().getName().startsWith("com.android.server.security.") 104 | || jniMethod.getDeclaringClass().getName().equals("android.view.Surface") 105 | || jniMethod.getDeclaringClass().getName().equals("android.view.MotionEvent") 106 | || jniMethod.getDeclaringClass().getName().equals("android.app.admin.SecurityLog") 107 | || jniMethod.getDeclaringClass().getName().equals("android.hardware.HardwareBuffer")) 108 | continue; 109 | 110 | if (jniMethod.isPhantom()) 111 | continue; 112 | if (jniMethod.isAbstract()) 113 | continue; 114 | if (jniMethod.isNative()) 115 | jniSMs.add(jniMethod); 116 | } 117 | 118 | // for (SootMethod sm : jniSMs) 119 | // System.out.println(sm.getSignature()); 120 | 121 | return jniSMs; 122 | } 123 | 124 | // ---- // 125 | 126 | private static HashMap> checkPermission(CallGraph cg, HashSet jniSMs) { 127 | HashMap> jni2permissions = new HashMap>(); // output 128 | 129 | for (SootMethod jniSM : jniSMs) { 130 | System.out.println("[JNI] " + jniSM.getSignature()); 131 | 132 | HashSet> jniPaths = new HashSet>(); 133 | for (ArrayList path : paths) { 134 | SootMethod jniMethod = path.get(path.size() - 1); 135 | if (!jniMethod.getSignature().equals(jniSM.getSignature())) 136 | continue; 137 | if (!path.get(1).getDeclaringClass().getName().endsWith("$Stub$Proxy")) 138 | continue; 139 | if (path.size() == CallgraphWrapper.PATH_MAX_LENGTH) { 140 | // just ignore the abnormal cases 141 | continue; 142 | } 143 | { 144 | boolean ignore = false; 145 | HashSet safeSCs = new HashSet(); 146 | SootClass serviceSC = path.get(2).getDeclaringClass(); 147 | safeSCs.add(serviceSC); 148 | while (serviceSC.hasOuterClass()) { 149 | serviceSC = serviceSC.getOuterClass(); 150 | safeSCs.add(serviceSC); 151 | } 152 | for (int i = 3; i < path.size(); i++) { 153 | SootClass other = path.get(i).getDeclaringClass(); 154 | while (other.hasOuterClass()) { 155 | other = other.getOuterClass(); 156 | } 157 | 158 | if (safeSCs.contains(other)) 159 | continue; 160 | if ((other.getName().contains("Service")) 161 | && !other.getName().contains("GnssManagerService") 162 | && !other.getName().contains("BroadcastRadioService")) { 163 | ignore = true; 164 | break; 165 | } 166 | } 167 | 168 | if (ignore == true) 169 | continue; 170 | } 171 | 172 | jniPaths.add(path); 173 | } 174 | 175 | if (jniPaths.size() > 4) { 176 | // heuristic filter (??) 177 | continue; 178 | } 179 | 180 | int pathIdx = 0; 181 | for (ArrayList path : jniPaths) { 182 | boolean ignore = false; 183 | for (SootMethod sm : path) { 184 | if (sm.getDeclaringClass().getName().startsWith("android.util.")) { 185 | ignore = true; 186 | break; 187 | } 188 | } 189 | if (ignore == true) 190 | continue; 191 | 192 | System.out.println(String.format(" " + "[Path-%d] length: %d", pathIdx, path.size())); 193 | for (SootMethod sm : path) { 194 | System.out.println(" " + " " + sm.getSignature()); 195 | } 196 | 197 | pathIdx += 1; 198 | 199 | // ---- // 200 | 201 | HashSet permissions = new HashSet(); 202 | 203 | for (int srcIdx = 2; srcIdx < (path.size() - 1); srcIdx++) { 204 | SootMethod srcSM = path.get(srcIdx); 205 | SootMethod tgtSM = path.get(srcIdx + 1); 206 | Iterator edgeIter = cg.edgesOutOf(srcSM); 207 | 208 | HashSet stmts = new HashSet(); 209 | while (edgeIter.hasNext()) { 210 | Edge curEdge = edgeIter.next(); 211 | if (curEdge.tgt().getSignature().equals(tgtSM.getSignature())) { 212 | Stmt curStmt = curEdge.srcStmt(); 213 | stmts.add(curStmt); 214 | } 215 | } 216 | 217 | for (Stmt stmt : stmts) { 218 | for (Unit domUnit : getDominators(srcSM, stmt)) { 219 | // System.out.println(" " + " " + "[Dom] " + domUnit); 220 | if (!((Stmt) domUnit).containsInvokeExpr()) 221 | continue; 222 | 223 | Iterator domEdgeIterator = cg.edgesOutOf(domUnit); 224 | 225 | while (domEdgeIterator.hasNext()) { 226 | Edge domEdge = domEdgeIterator.next(); 227 | SootMethod domCalleeSM = domEdge.tgt(); 228 | // System.out.println(" " + " " + " " + "[Dom-Callee] " + domCalleeSM.getSignature()); 229 | // case-1 230 | if (PermissionCheckFinder.permissionCheckMethodCGSet.contains(domCalleeSM)) { 231 | if (!BinderPatcher.binderRemoteSet.contains(srcSM.getDeclaringClass())) { 232 | // System.out.println(" " + " " + "[Ignore0] " + srcSM.getSignature()); 233 | continue; 234 | } 235 | 236 | // System.out.println(" " + " " + "[Found0] " + srcSM.getSignature()); 237 | 238 | HashSet localPermissions = new HashSet(); 239 | for (ValueBox pValue : domEdge.srcStmt().getUseAndDefBoxes()) { 240 | if (pValue.getValue() instanceof StringConstant) { 241 | String permission = ((StringConstant) pValue.getValue()).value; 242 | if (permission.contains(".permission.")) { 243 | localPermissions.add(permission); 244 | } 245 | } 246 | if (pValue.getValue() instanceof StaticFieldRef) { 247 | SootField permissionSF = ((StaticFieldRef) pValue.getValue()).getField(); 248 | if (StaticFieldResolver.field2permission.containsKey(permissionSF)) { 249 | String permission = StaticFieldResolver.field2permission.get(permissionSF); 250 | localPermissions.add(permission); 251 | } 252 | } 253 | } 254 | if (domCalleeSM.isConcrete()) { 255 | for (Unit unit : domCalleeSM.retrieveActiveBody().getUnits()) { 256 | for (ValueBox pValue : unit.getUseAndDefBoxes()) { 257 | if (pValue.getValue() instanceof StringConstant) { 258 | String permission = ((StringConstant) pValue.getValue()).value; 259 | if (permission.contains(".permission.")) { 260 | localPermissions.add(permission); 261 | } 262 | } 263 | if (pValue.getValue() instanceof StaticFieldRef) { 264 | SootField permissionSF = ((StaticFieldRef) pValue.getValue()).getField(); 265 | if (StaticFieldResolver.field2permission.containsKey(permissionSF)) { 266 | String permission = StaticFieldResolver.field2permission.get(permissionSF); 267 | localPermissions.add(permission); 268 | } 269 | } 270 | } 271 | } 272 | } 273 | 274 | for (String permission : localPermissions) { 275 | // System.out.println(" " + " " + " " + "[Permission] " + permission); 276 | } 277 | 278 | permissions.addAll(localPermissions); 279 | 280 | continue; 281 | } 282 | // case-2 283 | Iterator calleeEdgeIterator = cg.edgesOutOf(domCalleeSM); 284 | while (calleeEdgeIterator.hasNext()) { 285 | Edge calleeEdge = calleeEdgeIterator.next(); 286 | SootMethod calleeSM = calleeEdge.tgt(); 287 | if (PermissionCheckFinder.permissionCheckMethodCGSet.contains(calleeSM)) { 288 | if (!BinderPatcher.binderRemoteSet.contains(srcSM.getDeclaringClass())) { 289 | // System.out.println(" " + " " + "[Ignore1] " + srcSM.getSignature()); 290 | continue; 291 | } 292 | 293 | // System.out.println(" " + " " + "[Found1] " + srcSM.getSignature() + " ==> " + domCalleeSM.getSignature()); 294 | 295 | HashSet localPermissions = new HashSet(); 296 | for (ValueBox pValue : calleeEdge.srcStmt().getUseAndDefBoxes()) { 297 | if (pValue.getValue() instanceof StringConstant) { 298 | String permission = ((StringConstant) pValue.getValue()).value; 299 | if (permission.contains(".permission.")) { 300 | localPermissions.add(permission); 301 | } 302 | } 303 | if (pValue.getValue() instanceof StaticFieldRef) { 304 | SootField permissionSF = ((StaticFieldRef) pValue.getValue()).getField(); 305 | if (StaticFieldResolver.field2permission.containsKey(permissionSF)) { 306 | String permission = StaticFieldResolver.field2permission.get(permissionSF); 307 | localPermissions.add(permission); 308 | } 309 | } 310 | } 311 | if (calleeSM.isConcrete()) { 312 | for (Unit unit : calleeSM.retrieveActiveBody().getUnits()) { 313 | for (ValueBox pValue : unit.getUseAndDefBoxes()) { 314 | if (pValue.getValue() instanceof StringConstant) { 315 | String permission = ((StringConstant) pValue.getValue()).value; 316 | if (permission.contains(".permission.")) { 317 | localPermissions.add(permission); 318 | } 319 | } 320 | if (pValue.getValue() instanceof StaticFieldRef) { 321 | SootField permissionSF = ((StaticFieldRef) pValue.getValue()).getField(); 322 | if (StaticFieldResolver.field2permission.containsKey(permissionSF)) { 323 | String permission = StaticFieldResolver.field2permission.get(permissionSF); 324 | localPermissions.add(permission); 325 | } 326 | } 327 | } 328 | } 329 | } 330 | 331 | for (String permission : localPermissions) { 332 | // System.out.println(" " + " " + " " + "[Permission] " + permission); 333 | } 334 | 335 | permissions.addAll(localPermissions); 336 | 337 | continue; 338 | } 339 | } 340 | } 341 | } 342 | } 343 | } 344 | 345 | if (permissions.isEmpty()) { 346 | System.out.println(" " + "[Permission] NULL"); 347 | } else { 348 | System.out.print(" " + "[Permission] "); 349 | int permIdx = 0; 350 | for (String permission : permissions) { 351 | if (permIdx == (permissions.size() - 1)) 352 | System.out.print(permission); 353 | else 354 | System.out.print(permission + ", "); 355 | 356 | permIdx++; 357 | } 358 | System.out.print("\n"); 359 | 360 | if (!jni2permissions.containsKey(jniSM)) 361 | jni2permissions.put(jniSM, new HashSet()); 362 | jni2permissions.get(jniSM).addAll(permissions); 363 | } 364 | } 365 | } 366 | 367 | return jni2permissions; 368 | } 369 | 370 | // ---- // 371 | 372 | private static HashMap> checkUID(CallGraph cg, HashSet jniSMs) { 373 | HashMap> jni2uids = new HashMap>(); // output 374 | 375 | for (SootMethod jniSM : jniSMs) { 376 | System.out.println("[JNI] " + jniSM.getSignature()); 377 | 378 | HashSet> jniPaths = new HashSet>(); 379 | for (ArrayList path : paths) { 380 | SootMethod jniMethod = path.get(path.size() - 1); 381 | if (!jniMethod.getSignature().equals(jniSM.getSignature())) 382 | continue; 383 | if (!path.get(1).getDeclaringClass().getName().endsWith("$Stub$Proxy")) 384 | continue; 385 | if (path.size() == CallgraphWrapper.PATH_MAX_LENGTH) { 386 | // just ignore the abnormal cases 387 | continue; 388 | } 389 | { 390 | boolean ignore = false; 391 | HashSet safeSCs = new HashSet(); 392 | SootClass serviceSC = path.get(2).getDeclaringClass(); 393 | safeSCs.add(serviceSC); 394 | while (serviceSC.hasOuterClass()) { 395 | serviceSC = serviceSC.getOuterClass(); 396 | safeSCs.add(serviceSC); 397 | } 398 | for (int i = 3; i < path.size(); i++) { 399 | SootClass other = path.get(i).getDeclaringClass(); 400 | while (other.hasOuterClass()) { 401 | other = other.getOuterClass(); 402 | } 403 | 404 | if (safeSCs.contains(other)) 405 | continue; 406 | if ((other.getName().contains("Service")) 407 | && !other.getName().contains("GnssManagerService") 408 | && !other.getName().contains("BroadcastRadioService")) { 409 | ignore = true; 410 | break; 411 | } 412 | } 413 | 414 | if (ignore == true) 415 | continue; 416 | } 417 | 418 | jniPaths.add(path); 419 | } 420 | 421 | if (jniPaths.size() > 4) { 422 | // heuristic filter (??) 423 | continue; 424 | } 425 | 426 | int pathIdx = 0; 427 | for (ArrayList path : jniPaths) { 428 | boolean ignore = false; 429 | for (SootMethod sm : path) { 430 | if (sm.getDeclaringClass().getName().startsWith("android.util.")) { 431 | ignore = true; 432 | break; 433 | } 434 | } 435 | if (ignore == true) 436 | continue; 437 | 438 | System.out.println(" " + String.format("[Path-%d] length: %d", pathIdx, path.size())); 439 | for (SootMethod sm : path) { 440 | System.out.println(" " + " " + sm.getSignature()); 441 | } 442 | 443 | pathIdx += 1; 444 | 445 | // ---- // 446 | 447 | HashSet uids = new HashSet(); 448 | 449 | for (int srcIdx = 2; srcIdx < (path.size() - 1); srcIdx++) { 450 | SootMethod srcSM = path.get(srcIdx); 451 | SootMethod tgtSM = path.get(srcIdx + 1); 452 | Iterator edgeIter = cg.edgesOutOf(srcSM); 453 | 454 | HashSet stmts = new HashSet(); 455 | while (edgeIter.hasNext()) { 456 | Edge curEdge = edgeIter.next(); 457 | if (curEdge.tgt().getSignature().equals(tgtSM.getSignature())) { 458 | Stmt curStmt = curEdge.srcStmt(); 459 | stmts.add(curStmt); 460 | } 461 | } 462 | 463 | for (Stmt stmt : stmts) { 464 | for (Unit domUnit : getDominators(srcSM, stmt)) { 465 | // System.out.println(" " + " " + "[Dom] " + domUnit); 466 | 467 | // case-1 468 | if (UIDCheckFinder.uidCheckMethod2Stmt.containsKey(srcSM) 469 | && UIDCheckFinder.uidCheckMethod2Stmt.get(srcSM).contains(domUnit)) { 470 | String uid = UIDCheckFinder.uidCheckStmt2UID.get(domUnit); 471 | 472 | // System.out.println(" " + " " + "[Found0] " + srcSM.getSignature()); 473 | // System.out.println(" " + " " + " " + "[UID] " + uid); 474 | 475 | uids.add(uid); 476 | 477 | continue; 478 | } 479 | 480 | if (!((Stmt) domUnit).containsInvokeExpr()) 481 | continue; 482 | 483 | Iterator domEdgeIterator = cg.edgesOutOf(domUnit); 484 | while (domEdgeIterator.hasNext()) { 485 | Edge domEdge = domEdgeIterator.next(); 486 | SootMethod domCalleeSM = domEdge.tgt(); 487 | // case-2 488 | if (UIDCheckFinder.uidCheckMethodCGSet.contains(domCalleeSM)) { 489 | if (!BinderPatcher.binderRemoteSet.contains(srcSM.getDeclaringClass())) { 490 | // System.out.println(" " + " " + "[Ignore1] " + srcSM.getSignature()); 491 | continue; 492 | } 493 | 494 | // System.out.println(" " + " " + "[Found1] " + srcSM.getSignature()); 495 | 496 | HashSet localUIDs = new HashSet(); 497 | for (Stmt uidCheckStmt : UIDCheckFinder.uidCheckMethod2Stmt.get(domCalleeSM)) { 498 | String uid = UIDCheckFinder.uidCheckStmt2UID.get(uidCheckStmt); 499 | // System.out.println(" " + " " + " " + "[UID] " + uid); 500 | localUIDs.add(uid); 501 | } 502 | 503 | // for (String uid : localUIDs) { 504 | // System.out.println(" " + " " + " " + "[UID] " + uid); 505 | // } 506 | 507 | uids.addAll(localUIDs); 508 | 509 | continue; 510 | } 511 | } 512 | } 513 | } 514 | } 515 | 516 | if (uids.isEmpty()) { 517 | System.out.println(" " + "[UID] NULL"); 518 | } else { 519 | System.out.print(" " + "[UID] "); 520 | int permIdx = 0; 521 | for (String uid : uids) { 522 | if (permIdx == (uids.size() - 1)) 523 | System.out.print(uid); 524 | else 525 | System.out.print(uid + ", "); 526 | 527 | permIdx++; 528 | } 529 | System.out.print("\n"); 530 | 531 | if (!jni2uids.containsKey(jniSM)) 532 | jni2uids.put(jniSM, new HashSet()); 533 | jni2uids.get(jniSM).addAll(uids); 534 | } 535 | } 536 | } 537 | 538 | return jni2uids; 539 | } 540 | 541 | // ---- // 542 | 543 | private static HashSet checkUserID(CallGraph cg, HashSet jniSMs) { 544 | HashSet jni1userid = new HashSet(); // output 545 | 546 | for (SootMethod jniSM : jniSMs) { 547 | System.out.println("[JNI] " + jniSM.getSignature()); 548 | 549 | HashSet> jniPaths = new HashSet>(); 550 | for (ArrayList path : paths) { 551 | SootMethod jniMethod = path.get(path.size() - 1); 552 | if (!jniMethod.getSignature().equals(jniSM.getSignature())) 553 | continue; 554 | if (!path.get(1).getDeclaringClass().getName().endsWith("$Stub$Proxy")) 555 | continue; 556 | if (path.size() == CallgraphWrapper.PATH_MAX_LENGTH) { 557 | // just ignore the abnormal cases 558 | continue; 559 | } 560 | { 561 | boolean ignore = false; 562 | HashSet safeSCs = new HashSet(); 563 | SootClass serviceSC = path.get(2).getDeclaringClass(); 564 | safeSCs.add(serviceSC); 565 | while (serviceSC.hasOuterClass()) { 566 | serviceSC = serviceSC.getOuterClass(); 567 | safeSCs.add(serviceSC); 568 | } 569 | for (int i = 3; i < path.size(); i++) { 570 | SootClass other = path.get(i).getDeclaringClass(); 571 | while (other.hasOuterClass()) { 572 | other = other.getOuterClass(); 573 | } 574 | 575 | if (safeSCs.contains(other)) 576 | continue; 577 | if ((other.getName().contains("Service")) 578 | && !other.getName().contains("GnssManagerService") 579 | && !other.getName().contains("BroadcastRadioService")) { 580 | ignore = true; 581 | break; 582 | } 583 | } 584 | 585 | if (ignore == true) 586 | continue; 587 | } 588 | 589 | jniPaths.add(path); 590 | } 591 | 592 | if (jniPaths.size() > 4) { 593 | // heuristic filter (??) 594 | continue; 595 | } 596 | 597 | int pathIdx = 0; 598 | for (ArrayList path : jniPaths) { 599 | boolean ignore = false; 600 | for (SootMethod sm : path) { 601 | if (sm.getDeclaringClass().getName().startsWith("android.util.")) { 602 | ignore = true; 603 | break; 604 | } 605 | } 606 | if (ignore == true) 607 | continue; 608 | 609 | System.out.println(" " + String.format("[Path-%d] length: %d", pathIdx, path.size())); 610 | for (SootMethod sm : path) { 611 | System.out.println(" " + " " + sm.getSignature()); 612 | } 613 | 614 | pathIdx += 1; 615 | 616 | // ---- // 617 | 618 | boolean hasUserIdCheck = false; 619 | 620 | for (int srcIdx = 2; srcIdx < (path.size() - 1); srcIdx++) { 621 | SootMethod srcSM = path.get(srcIdx); 622 | SootMethod tgtSM = path.get(srcIdx + 1); 623 | Iterator edgeIter = cg.edgesOutOf(srcSM); 624 | 625 | HashSet stmts = new HashSet(); 626 | while (edgeIter.hasNext()) { 627 | Edge curEdge = edgeIter.next(); 628 | if (curEdge.tgt().getSignature().equals(tgtSM.getSignature())) { 629 | Stmt curStmt = curEdge.srcStmt(); 630 | stmts.add(curStmt); 631 | } 632 | } 633 | 634 | for (Stmt stmt : stmts) { 635 | for (Unit domUnit : getDominators(srcSM, stmt)) { 636 | // System.out.println(" " + " " + "[Dom] " + domUnit); 637 | 638 | // case-1 639 | if (UserIDCheckFinder.useridCheckMethod2Stmt.containsKey(srcSM) 640 | && UserIDCheckFinder.useridCheckMethod2Stmt.get(srcSM).contains(domUnit)) { 641 | // System.out.println(" " + " " + "[Found0] " + srcSM.getSignature()); 642 | 643 | hasUserIdCheck = true; 644 | 645 | continue; 646 | } 647 | 648 | if (!((Stmt) domUnit).containsInvokeExpr()) 649 | continue; 650 | 651 | Iterator domEdgeIterator = cg.edgesOutOf(domUnit); 652 | while (domEdgeIterator.hasNext()) { 653 | Edge domEdge = domEdgeIterator.next(); 654 | SootMethod domCalleeSM = domEdge.tgt(); 655 | // case-2 656 | if (UserIDCheckFinder.useridCheckMethodCGSet.contains(domCalleeSM)) { 657 | if (!BinderPatcher.binderRemoteSet.contains(srcSM.getDeclaringClass())) { 658 | // System.out.println(" " + " " + "[Ignore1] " + srcSM.getSignature()); 659 | continue; 660 | } 661 | 662 | // System.out.println(" " + " " + "[Found1] " + srcSM.getSignature()); 663 | 664 | hasUserIdCheck = true; 665 | 666 | continue; 667 | } 668 | } 669 | } 670 | } 671 | } 672 | 673 | if (hasUserIdCheck == false) { 674 | System.out.println(" " + "[UserID] False"); 675 | } else { 676 | System.out.println(" " + "[UserID] True"); 677 | 678 | jni1userid.add(jniSM); 679 | } 680 | } 681 | } 682 | 683 | return jni1userid; 684 | } 685 | 686 | // ---- // 687 | 688 | private static HashSet getDominators(SootMethod sm, Stmt stmt) { 689 | Body body = sm.retrieveActiveBody(); 690 | BriefUnitGraph cfg = new BriefUnitGraph(body); 691 | MHGDominatorsFinder domFinder = new MHGDominatorsFinder(cfg); 692 | 693 | HashSet domUnits = new HashSet(); 694 | 695 | List domUnitFinds = null; 696 | try { 697 | domUnitFinds = domFinder.getDominators(stmt); 698 | } catch (NullPointerException npe) { 699 | return domUnits; 700 | } 701 | domUnits.addAll(domUnitFinds); 702 | 703 | // System.out.println(body); 704 | // System.out.println(stmt); 705 | 706 | Queue queue = new LinkedList(); 707 | for (Unit domUnit : domFinder.getDominators(stmt)) { 708 | for (Unit preUnit : cfg.getPredsOf(domUnit)) { 709 | if (domUnits.contains(preUnit)) 710 | continue; 711 | if (queue.contains(preUnit)) 712 | continue; 713 | queue.add(preUnit); 714 | } 715 | } 716 | 717 | while (!queue.isEmpty()) { 718 | Unit curUnit = queue.poll(); 719 | domUnits.add(curUnit); 720 | 721 | for (Unit domUnit : domFinder.getDominators(curUnit)) { 722 | if (domUnits.contains(domUnit)) 723 | continue; 724 | if (queue.contains(domUnit)) 725 | continue; 726 | queue.add(domUnit); 727 | } 728 | } 729 | 730 | // for (Unit domUnit : domUnits) 731 | // System.out.println(" " + "[Dom] " + domUnit); 732 | 733 | return domUnits; 734 | } 735 | 736 | } 737 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/ParcelPatcher.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | 6 | import soot.Scene; 7 | import soot.SootMethod; 8 | import soot.jimple.toolkits.callgraph.CallGraph; 9 | import soot.jimple.toolkits.callgraph.Edge; 10 | 11 | public class ParcelPatcher { 12 | 13 | public static void init() {} 14 | 15 | public static void patchPost() { 16 | CallGraph cg = Scene.v().getCallGraph(); 17 | 18 | HashSet removes = new HashSet(); 19 | 20 | Iterator edgeIter = cg.iterator(); 21 | while (edgeIter.hasNext()) { 22 | Edge edge = edgeIter.next(); 23 | SootMethod tgtSM = edge.tgt(); 24 | // case-1 25 | if (tgtSM.getDeclaringClass().getName().equals("android.os.Parcel")) { 26 | // System.out.println(tgtSM.getSignature()); 27 | removes.add(edge); 28 | } 29 | // case-2 30 | if (tgtSM.getName().equals("readFromParcel")) { 31 | // System.out.println(tgtSM.getSignature()); 32 | removes.add(edge); 33 | } 34 | // case-3 35 | if (tgtSM.getName().equals("writeToParcel")) { 36 | // System.out.println(tgtSM.getSignature()); 37 | removes.add(edge); 38 | } 39 | } 40 | 41 | for (Edge remove : removes) { 42 | cg.removeEdge(remove); 43 | } 44 | 45 | Scene.v().setCallGraph(cg); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/PermissionCheckFinder.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | import java.util.LinkedList; 6 | import java.util.Queue; 7 | 8 | import soot.Body; 9 | import soot.Local; 10 | import soot.Scene; 11 | import soot.SootClass; 12 | import soot.SootField; 13 | import soot.SootMethod; 14 | import soot.Unit; 15 | import soot.Value; 16 | import soot.jimple.AssignStmt; 17 | import soot.jimple.FieldRef; 18 | import soot.jimple.InvokeExpr; 19 | import soot.jimple.ReturnStmt; 20 | import soot.jimple.StaticFieldRef; 21 | import soot.jimple.Stmt; 22 | import soot.jimple.StringConstant; 23 | import soot.jimple.VirtualInvokeExpr; 24 | import soot.jimple.toolkits.callgraph.CallGraph; 25 | import soot.jimple.toolkits.callgraph.Edge; 26 | import soot.toolkits.graph.BriefUnitGraph; 27 | import soot.toolkits.scalar.SimpleLocalDefs; 28 | 29 | public class PermissionCheckFinder { 30 | 31 | // find the permission check methods in CallGraph 32 | public static HashSet permissionCheckMethodCGSet = new HashSet(); 33 | 34 | public static void find(CallGraph cg) { 35 | // case-1 36 | HashSet cgSMs = new HashSet(); 37 | for (Edge edge : cg) { 38 | SootMethod src = edge.src(); 39 | if (src.isConcrete()) 40 | cgSMs.add(src); 41 | SootMethod tgt = edge.tgt(); 42 | if (tgt.isConcrete()) 43 | cgSMs.add(tgt); 44 | } 45 | 46 | for (SootMethod sm : cgSMs) { 47 | boolean isPermissionCheckMethod = false; 48 | 49 | if (!sm.isConcrete()) 50 | continue; 51 | if (!sm.getReturnType().toString().equals("int")) 52 | continue; 53 | 54 | Body body = sm.retrieveActiveBody(); 55 | BriefUnitGraph cfg = new BriefUnitGraph(body); 56 | SimpleLocalDefs localDefs = new SimpleLocalDefs(cfg); 57 | 58 | for (Unit tail : cfg.getTails()) { 59 | if (!(tail instanceof ReturnStmt)) 60 | continue; 61 | 62 | Value retValue = ((ReturnStmt) tail).getOp(); 63 | if (!(retValue instanceof Local)) 64 | continue; 65 | 66 | Local retLocal = (Local) retValue; 67 | for (Unit defRetLocalUnit : localDefs.getDefsOf(retLocal)) { 68 | Stmt defRetLocalStmt = (Stmt) defRetLocalUnit; 69 | if (!(defRetLocalStmt instanceof AssignStmt)) 70 | continue; 71 | if (!defRetLocalStmt.containsInvokeExpr()) 72 | continue; 73 | if (!defRetLocalStmt.getInvokeExpr().getMethod().getSignature().equals("")) 74 | continue; 75 | 76 | Value baseValue = ((VirtualInvokeExpr) defRetLocalStmt.getInvokeExpr()).getBase(); 77 | if (!(baseValue instanceof Local)) 78 | continue; 79 | 80 | Local baseLocal = (Local) baseValue; 81 | for (Unit defBaseLocalUnit : localDefs.getDefsOf(baseLocal)) { 82 | if (!(defBaseLocalUnit instanceof AssignStmt)) 83 | continue; 84 | Value rightValue = ((AssignStmt) defBaseLocalUnit).getRightOp(); 85 | if (!(rightValue instanceof FieldRef)) 86 | continue; 87 | 88 | String fieldName = ((FieldRef) rightValue).getField().getName(); 89 | if ((fieldName.startsWith("PERMISSION_") && fieldName.endsWith("_GRANTED")) 90 | || (fieldName.startsWith("PERMISSION_") && fieldName.endsWith("_DENIED"))) { 91 | isPermissionCheckMethod = true; 92 | break; 93 | } 94 | } 95 | } 96 | } 97 | 98 | if (isPermissionCheckMethod) { 99 | // System.out.println("[INFO] [CG-PermissionChecker][case-1]: " + sm.getSignature()); // debug 100 | permissionCheckMethodCGSet.add(sm); 101 | } 102 | } 103 | 104 | // patch 105 | HashSet extraSMs = new HashSet(); 106 | for (SootMethod sm : permissionCheckMethodCGSet) { 107 | String className = sm.getDeclaringClass().getName(); 108 | String methodName = sm.getName(); 109 | if (className.equals("com.android.server.pm.permission.PermissionManagerService")) { 110 | SootClass tgtSC = Scene.v().getSootClass("android.permission.PermissionManager"); 111 | for (SootMethod tgtSM : tgtSC.getMethods()) { 112 | if (tgtSM.getName().equals(methodName)) 113 | extraSMs.add(tgtSM); 114 | } 115 | } 116 | } 117 | permissionCheckMethodCGSet.addAll(extraSMs); 118 | 119 | // case-2 120 | HashSet potentialSMs = new HashSet(); 121 | for (Edge edge : cg) { 122 | SootMethod callee = edge.tgt(); 123 | if (callee.getDeclaringClass().toString().startsWith("android.util.")) 124 | continue; 125 | if (callee.getDeclaringClass().toString().startsWith("java.util.")) 126 | continue; 127 | 128 | Stmt stmt = edge.srcStmt(); 129 | if (stmt == null) 130 | continue; 131 | if (!stmt.containsInvokeExpr()) 132 | continue; 133 | 134 | InvokeExpr expr = stmt.getInvokeExpr(); 135 | 136 | boolean candidateFound = false; 137 | for (int argIdx = 0; argIdx < stmt.getInvokeExpr().getArgCount(); argIdx++) { 138 | Value arg = expr.getArg(argIdx); 139 | if ((arg instanceof StringConstant) && arg.toString().contains(".permission.")) { 140 | candidateFound = true; 141 | // System.out.println(edge); 142 | break; 143 | } 144 | if (arg instanceof StaticFieldRef) { 145 | SootField field = ((StaticFieldRef) arg).getField(); 146 | if (StaticFieldResolver.field2permission.containsKey(field)) { 147 | candidateFound = true; 148 | // System.out.println("polyu == polyu == polyu == polyu"); 149 | break; 150 | } 151 | } 152 | } 153 | 154 | if (!candidateFound) 155 | continue; 156 | if (potentialSMs.contains(callee)) 157 | continue; 158 | // System.out.println(stmt); 159 | // System.out.println(" " + callee.getSignature()); 160 | 161 | HashSet reachableSMs = new HashSet(); 162 | Queue queue = new LinkedList(); 163 | queue.add(callee); 164 | 165 | while (!queue.isEmpty()) { 166 | SootMethod curSM = queue.poll(); 167 | reachableSMs.add(curSM); 168 | 169 | Iterator edgeIterator = cg.edgesOutOf(curSM); 170 | while (edgeIterator.hasNext()) { 171 | Edge curEdge = edgeIterator.next(); 172 | SootMethod nxtSM = curEdge.tgt(); 173 | if (!queue.contains(nxtSM) && !reachableSMs.contains(nxtSM)) 174 | queue.add(nxtSM); 175 | } 176 | } 177 | 178 | // for (SootMethod s : reachableSMs) 179 | // System.out.println(" " + s.getSignature()); 180 | 181 | if (reachableSMs.size() == 1) { 182 | // System.out.println("[INFO] [CG-PermissionChecker][case-2]: " + callee.getSignature()); // debug 183 | potentialSMs.add(callee); // to keep *permissionCheckMethodCGSet* unchanged, do not add *callee* here 184 | } else { 185 | reachableSMs.retainAll(permissionCheckMethodCGSet); 186 | if (!reachableSMs.isEmpty()) { 187 | // System.out.println("[INFO] [CG-PermissionChecker][case-2]: " + callee.getSignature()); // debug 188 | potentialSMs.add(callee); // to keep *permissionCheckMethodCGSet* unchanged, do not add *callee* here 189 | } 190 | } 191 | } 192 | 193 | // case-3 194 | HashSet suspiciousSMs = new HashSet(); 195 | for (Edge edge : cg) { 196 | SootMethod caller = edge.src(); 197 | if (!caller.getReturnType().toString().equals("void") && !caller.getReturnType().toString().equals("boolean")) 198 | continue; 199 | if (!caller.getName().contains("Permission")) 200 | continue; // ?? 201 | if (suspiciousSMs.contains(caller)) 202 | continue; 203 | // System.out.println("\t" + callee.getSignature()); 204 | 205 | int isSuspicious = 0; 206 | Iterator edgeIter = cg.edgesOutOf(caller); 207 | while (edgeIter.hasNext()) { 208 | SootMethod tgtSM = edgeIter.next().tgt(); 209 | if (tgtSM.getName().equals("") || tgtSM.getName().equals("")) { 210 | isSuspicious += 0; // normal cases 211 | if (tgtSM.getDeclaringClass().getName().equals("java.lang.SecurityException")) { 212 | // critical cases 213 | isSuspicious += 100; 214 | } 215 | continue; 216 | } else if (permissionCheckMethodCGSet.contains(tgtSM)) { 217 | // special cases 218 | isSuspicious += 1; 219 | continue; 220 | } else if (potentialSMs.contains(tgtSM)) { 221 | // special cases 222 | isSuspicious += 1; 223 | continue; 224 | } else if (tgtSM.getName().contains("uid") || tgtSM.getName().contains("pid")) { 225 | isSuspicious += 0; // normal cases 226 | continue; 227 | } else if (tgtSM.getDeclaringClass().getName().equals("android.util.Log") || tgtSM.getDeclaringClass().getName().equals("android.util.Slog")) { 228 | isSuspicious += 0; // normal cases 229 | continue; 230 | } else { 231 | isSuspicious -= 10000; 232 | break; 233 | } 234 | } 235 | 236 | if ((isSuspicious > 100) && (isSuspicious % 100) > 0) { 237 | // System.out.println("[INFO] [CG-PermissionChecker][case-3]: " + caller.getSignature()); // debug 238 | suspiciousSMs.add(caller); 239 | } 240 | } 241 | 242 | permissionCheckMethodCGSet.addAll(potentialSMs); // add potential methods here 243 | permissionCheckMethodCGSet.addAll(suspiciousSMs); // add suspicious methods here 244 | 245 | // 246 | // for (SootMethod pmSM : permissionCheckMethodCGSet) { 247 | // System.out.println(pmSM.getSignature()); 248 | // } 249 | // 250 | 251 | System.out.println(" -->> Find Permission Check Methods in Callgraph Finish"); 252 | } 253 | 254 | } 255 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/RunPatcher.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | 6 | import soot.Scene; 7 | import soot.SootMethod; 8 | import soot.jimple.toolkits.callgraph.CallGraph; 9 | import soot.jimple.toolkits.callgraph.Edge; 10 | 11 | public class RunPatcher { 12 | 13 | public static void init() {} 14 | 15 | public static void patchPost() { 16 | CallGraph cg = Scene.v().getCallGraph(); 17 | 18 | HashSet removes = new HashSet(); 19 | 20 | // 1. collect source methods from the callgraph 21 | HashSet srcs = new HashSet(); 22 | Iterator edgeIter = cg.iterator(); 23 | while (edgeIter.hasNext()) { 24 | Edge edge = edgeIter.next(); 25 | SootMethod srcSM = edge.src(); 26 | srcs.add(srcSM); 27 | } 28 | 29 | // 2. analyze the callee methods of each source method 30 | for (SootMethod srcSM : srcs) { 31 | HashSet edges = new HashSet(); // store the edges that call the "run" method 32 | 33 | Iterator outIter = cg.edgesOutOf(srcSM); 34 | while (outIter.hasNext()) { 35 | Edge outEdge = outIter.next(); 36 | SootMethod tgtSM = outEdge.tgt(); 37 | if (tgtSM.getName().equals("run")) { 38 | edges.add(outEdge); 39 | } 40 | } 41 | 42 | if (edges.size() > 1) { 43 | removes.addAll(edges); 44 | } 45 | } 46 | 47 | for (Edge remove : removes) { 48 | cg.removeEdge(remove); 49 | } 50 | 51 | Scene.v().setCallGraph(cg); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/SPARKPatcher.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Map.Entry; 8 | 9 | import soot.ArrayType; 10 | import soot.Body; 11 | import soot.BooleanType; 12 | import soot.ByteType; 13 | import soot.CharType; 14 | import soot.DoubleType; 15 | import soot.FloatType; 16 | import soot.IntType; 17 | import soot.Local; 18 | import soot.LongType; 19 | import soot.NullType; 20 | import soot.RefType; 21 | import soot.Scene; 22 | import soot.ShortType; 23 | import soot.SootClass; 24 | import soot.SootField; 25 | import soot.SootMethod; 26 | import soot.Type; 27 | import soot.Unit; 28 | import soot.Value; 29 | import soot.VoidType; 30 | import soot.javaToJimple.LocalGenerator; 31 | import soot.jimple.AssignStmt; 32 | import soot.jimple.FieldRef; 33 | import soot.jimple.InvokeExpr; 34 | import soot.jimple.InvokeStmt; 35 | import soot.jimple.Jimple; 36 | import soot.jimple.NewExpr; 37 | import soot.jimple.Stmt; 38 | import soot.toolkits.graph.BriefUnitGraph; 39 | import soot.toolkits.scalar.SimpleLocalDefs; 40 | 41 | public class SPARKPatcher { 42 | 43 | public static HashMap fieldInitMap = new HashMap(); 44 | public static HashSet fieldInitSet = new HashSet(); 45 | 46 | public static void init() { 47 | for (SootClass sc : Scene.v().getClasses()) { 48 | if (sc.isPhantom()) 49 | continue; 50 | 51 | for (SootMethod sm : sc.getMethods()) { 52 | if (!sm.isConcrete()) 53 | continue; 54 | 55 | boolean willInitField = false; 56 | 57 | Body body = sm.retrieveActiveBody(); 58 | BriefUnitGraph cfg = new BriefUnitGraph(body); 59 | SimpleLocalDefs localDefs = new SimpleLocalDefs(cfg); 60 | 61 | for (Unit unit : body.getUnits()) { 62 | Stmt stmt = (Stmt) unit; 63 | if (!(stmt instanceof AssignStmt)) 64 | continue; 65 | 66 | AssignStmt assignStmt = (AssignStmt) stmt; 67 | Value leftOp = assignStmt.getLeftOp(); 68 | Value rightOp = assignStmt.getRightOp(); 69 | if (!((leftOp instanceof FieldRef) && (rightOp instanceof Local))) 70 | continue; 71 | 72 | willInitField = true; // indicate the method will initialize class field 73 | 74 | FieldRef leftRef = (FieldRef) leftOp; 75 | SootField leftField = leftRef.getField(); 76 | String leftTypeRaw = leftField.getType().toString(); 77 | SootClass leftTypeSC = Scene.v().getSootClassUnsafe(leftTypeRaw, false); 78 | if (leftTypeSC == null) { 79 | // for machine types, just ignore 80 | continue; 81 | } 82 | 83 | for (Unit defUnit : localDefs.getDefsOf((Local) rightOp)) { 84 | if (!(defUnit instanceof AssignStmt)) 85 | continue; 86 | 87 | AssignStmt defStmt = (AssignStmt) defUnit; 88 | Value defRightOp = defStmt.getRightOp(); 89 | 90 | SootClass concreteSC = null; 91 | if (defRightOp instanceof NewExpr) { 92 | concreteSC = ((NewExpr) defRightOp).getBaseType().getSootClass(); 93 | } 94 | if (defRightOp instanceof InvokeExpr) { 95 | String concreteRaw = ((InvokeExpr) defRightOp).getMethod().getReturnType().toString(); 96 | concreteSC = Scene.v().getSootClassUnsafe(concreteRaw, false); 97 | } 98 | 99 | if (concreteSC == null) { 100 | // for machine types, just ignore 101 | continue; 102 | } 103 | 104 | if (!concreteSC.isConcrete() || concreteSC.isPhantom()) { 105 | if (!concreteSC.getName().startsWith("java.")) { 106 | // System.err.println(stmt); 107 | // System.err.println("\t" + defStmt); 108 | // System.err.println("\t\t" + concreteSC.getName()); 109 | } 110 | continue; 111 | } 112 | 113 | if (fieldInitMap.containsKey(leftField) && fieldInitMap.get(leftField) != concreteSC) { 114 | // System.err.println("ERR: " + leftField.toString() + " -> <" + fieldInitMap.get(leftField).getName() + ">, <" + concreteSC.getName() + ">"); 115 | fieldInitMap.remove(leftField); // TODO: incorrect 116 | } else { 117 | fieldInitMap.put(leftField, concreteSC); 118 | } 119 | } 120 | } 121 | 122 | if (willInitField) 123 | fieldInitSet.add(sm); 124 | } 125 | } 126 | } 127 | 128 | public static void patch() { 129 | for (SootClass sc : Scene.v().getClasses()) { 130 | if (sc.isPhantom()) 131 | continue; 132 | /* test 133 | if (!sc.getName().equals("com.android.server.display.DisplayManagerService") 134 | && !sc.getName().equals("com.android.server.display.VirtualDisplayAdapter")) 135 | continue; 136 | */ 137 | 138 | for (SootMethod sm : sc.getMethods()) { 139 | if (!sm.isConcrete()) 140 | continue; 141 | if (fieldInitSet.contains(sm)) 142 | continue; 143 | // System.out.println(sm.getSignature()); // debug 144 | 145 | HashMap swapMap = new HashMap(); 146 | HashMap> injectMap = new HashMap>(); 147 | 148 | Body body = sm.retrieveActiveBody(); 149 | 150 | for (Unit unit : body.getUnits()) { 151 | Stmt stmt = (Stmt) unit; 152 | if (!(stmt instanceof AssignStmt)) 153 | continue; 154 | 155 | AssignStmt assignStmt = (AssignStmt) stmt; 156 | Value leftOp = assignStmt.getLeftOp(); 157 | Value rightOp = assignStmt.getRightOp(); 158 | if (!((leftOp instanceof Local) && (rightOp instanceof FieldRef))) 159 | continue; 160 | 161 | // System.out.println("\t" + stmt); // debug 162 | SootField rightField = ((FieldRef) rightOp).getField(); 163 | String rightFieldRaw = rightField.getType().toString(); 164 | SootClass rightFieldSC = Scene.v().getSootClassUnsafe(rightFieldRaw, false); 165 | if (rightFieldSC == null) { 166 | // for machine types, just ignore 167 | continue; 168 | } 169 | 170 | if (fieldInitMap.containsKey(rightField)) { 171 | NewExpr newExpr = Jimple.v().newNewExpr(fieldInitMap.get(rightField).getType()); 172 | AssignStmt newStmt = Jimple.v().newAssignStmt(leftOp, newExpr); 173 | // System.out.println("\t\t" + newStmt); // debug 174 | swapMap.put(stmt, newStmt); 175 | 176 | List injectUnits = new ArrayList(); 177 | 178 | try { 179 | SootMethod clinitSM = rightFieldSC.getMethodByName(""); 180 | InvokeExpr clinitInvokeExpr = Jimple.v().newStaticInvokeExpr(clinitSM.makeRef()); 181 | InvokeStmt clinitInvokeStmt = Jimple.v().newInvokeStmt(clinitInvokeExpr); 182 | injectUnits.add(clinitInvokeStmt); 183 | } catch (RuntimeException e) { } 184 | 185 | try { 186 | LocalGenerator lg = new LocalGenerator(body); 187 | 188 | Local boolLocal = lg.generateLocal(BooleanType.v()); 189 | Local byteLocal = lg.generateLocal(ByteType.v()); 190 | Local charLocal = lg.generateLocal(CharType.v()); 191 | Local doubleLocal = lg.generateLocal(DoubleType.v()); 192 | Local floatLocal = lg.generateLocal(FloatType.v()); 193 | Local intLocal = lg.generateLocal(IntType.v()); 194 | Local longLocal = lg.generateLocal(LongType.v()); 195 | Local nullLocal = lg.generateLocal(NullType.v()); 196 | Local shortLocal = lg.generateLocal(ShortType.v()); 197 | Local voidLocal = lg.generateLocal(VoidType.v()); 198 | 199 | SootMethod initSM = CallgraphPatcherPre.fetchInitMethod(rightFieldSC); 200 | 201 | List params = new ArrayList(); 202 | for (int paramIdx = 0; paramIdx < initSM.getParameterCount(); paramIdx++) { 203 | Type paramType = initSM.getParameterType(paramIdx); 204 | if (paramType instanceof ArrayType) { 205 | Type arrayBaseType = ((ArrayType) paramType).baseType; 206 | int arrayDimension = ((ArrayType) paramType).numDimensions; 207 | Local arrayLocal = lg.generateLocal(ArrayType.v(arrayBaseType, arrayDimension)); 208 | params.add(arrayLocal); 209 | } else if (paramType instanceof BooleanType) { 210 | params.add(boolLocal); 211 | } else if (paramType instanceof ByteType) { 212 | params.add(byteLocal); 213 | } else if (paramType instanceof CharType) { 214 | params.add(charLocal); 215 | } else if (paramType instanceof DoubleType) { 216 | params.add(doubleLocal); 217 | } else if (paramType instanceof FloatType) { 218 | params.add(floatLocal); 219 | } else if (paramType instanceof IntType) { 220 | params.add(intLocal); 221 | } else if (paramType instanceof LongType) { 222 | params.add(longLocal); 223 | } else if (paramType instanceof NullType) { 224 | params.add(nullLocal); 225 | } else if (paramType instanceof ShortType) { 226 | params.add(shortLocal); 227 | } else if (paramType instanceof VoidType) { 228 | params.add(voidLocal); 229 | } else if (paramType instanceof RefType) { 230 | RefType paramRef = ((RefType) paramType); 231 | if (paramRef.hasSootClass()) { 232 | Local refLocal = lg.generateLocal(paramRef.getSootClass().getType()); 233 | params.add(refLocal); 234 | 235 | NewExpr newRefExpr = Jimple.v().newNewExpr(paramRef.getSootClass().getType()); 236 | AssignStmt newRefStmt = Jimple.v().newAssignStmt(refLocal, newRefExpr); 237 | injectUnits.add(newRefStmt); 238 | } else { 239 | System.out.println("WARN [SPARKPatcher]: RefType -> " + paramType); // debug 240 | params.add(nullLocal); 241 | } 242 | } else { 243 | System.out.println("WARN [SPARKPatcher]: Special Type -> " + paramType); // debug 244 | params.add(nullLocal); 245 | } 246 | } 247 | 248 | InvokeExpr exprInvoke = Jimple.v().newVirtualInvokeExpr((Local) leftOp, initSM.makeRef(), params); 249 | InvokeStmt stmtInvoke = Jimple.v().newInvokeStmt(exprInvoke); 250 | injectUnits.add(stmtInvoke); 251 | } catch (RuntimeException e) { } 252 | 253 | injectMap.put(newStmt, injectUnits); 254 | } 255 | } 256 | 257 | // update Body 258 | for (Entry each : swapMap.entrySet()) { 259 | Unit out = each.getKey(); 260 | Unit in = each.getValue(); 261 | body.getUnits().swapWith(out, in); 262 | } 263 | 264 | for (Entry> each : injectMap.entrySet()) { 265 | Unit tgtUnit = each.getKey(); 266 | List injectUnits = each.getValue(); 267 | body.getUnits().insertAfter(injectUnits, tgtUnit); 268 | } 269 | 270 | sm.setActiveBody(body); 271 | } 272 | } 273 | } 274 | 275 | } 276 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/StaticFieldResolver.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.HashMap; 4 | 5 | import soot.Scene; 6 | import soot.SootClass; 7 | import soot.SootField; 8 | import soot.jimple.toolkits.callgraph.CallGraph; 9 | 10 | public class StaticFieldResolver { 11 | 12 | public static HashMap field2permission = new HashMap(); 13 | 14 | public static void resolve(CallGraph cg) { 15 | // for AOSP 16 | SootClass aospSC = Scene.v().getSootClassUnsafe("android.Manifest$permission", false); 17 | if (!aospSC.isPhantom()) { 18 | for (SootField sf : aospSC.getFields()) { 19 | assert !field2permission.containsKey(sf); 20 | 21 | String permission = "android.permission." + sf.getName(); 22 | field2permission.put(sf, permission); 23 | } 24 | } 25 | 26 | // for LineageOS 27 | /* 28 | SootClass lineageosSC = Scene.v().getSootClassUnsafe("lineageos.platform.Manifest$permission", false); 29 | if (!lineageosSC.isPhantom()) { 30 | for (SootField sf : lineageosSC.getFields()) { 31 | assert !field2permission.containsKey(sf); 32 | 33 | String permission = "lineageos.permission." + sf.getName(); 34 | field2permission.put(sf, permission); 35 | } 36 | } 37 | */ 38 | 39 | // for OmniROM 40 | // do nothing for OmniROM (reuse the logic for AOSP) 41 | 42 | 43 | // debug 44 | /* 45 | for (SootField field : field2permission.keySet()) { 46 | String permission = field2permission.get(field); 47 | System.out.println("[" + field.toString() + "]" + " -->> " + permission); 48 | } 49 | */ 50 | 51 | System.out.println(" -->> Resolve Static Field Finish"); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/UIDCheckFinder.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | import soot.Body; 10 | import soot.Local; 11 | import soot.Scene; 12 | import soot.SootMethod; 13 | import soot.Unit; 14 | import soot.Value; 15 | import soot.jimple.AssignStmt; 16 | import soot.jimple.EqExpr; 17 | import soot.jimple.IfStmt; 18 | import soot.jimple.IntConstant; 19 | import soot.jimple.NeExpr; 20 | import soot.jimple.Stmt; 21 | import soot.jimple.toolkits.callgraph.CallGraph; 22 | import soot.jimple.toolkits.callgraph.Edge; 23 | import soot.toolkits.graph.BriefUnitGraph; 24 | import soot.toolkits.graph.UnitGraph; 25 | import soot.toolkits.scalar.SimpleLocalDefs; 26 | import soot.toolkits.scalar.SimpleLocalUses; 27 | import soot.toolkits.scalar.UnitValueBoxPair; 28 | 29 | public class UIDCheckFinder { 30 | 31 | // find the UID check methods in CallGraph 32 | public static HashSet uidCheckMethodCGSet = new HashSet(); 33 | public static HashMap> uidCheckMethod2Stmt = new HashMap>(); 34 | public static HashMap uidCheckStmt2UID = new HashMap(); 35 | 36 | public static void find(CallGraph cg) { 37 | SootMethod getUIDSM = Scene.v().getMethod(""); 38 | assert !getUIDSM.isPhantom(); 39 | 40 | HashSet potentialSMs = new HashSet(); 41 | 42 | Iterator edgeIter = cg.edgesInto(getUIDSM); 43 | while (edgeIter.hasNext()) { 44 | Edge edge = edgeIter.next(); 45 | SootMethod srcSM = edge.src(); 46 | potentialSMs.add(srcSM); 47 | } 48 | 49 | for (SootMethod sm : potentialSMs) { 50 | if (!sm.isConcrete()) 51 | continue; 52 | 53 | boolean isUIDCheckMethod = false; 54 | 55 | Body body = sm.retrieveActiveBody(); 56 | for (Unit unit : body.getUnits()) { 57 | Stmt stmt = (Stmt) unit; 58 | if (!stmt.containsInvokeExpr()) 59 | continue; 60 | if (!(stmt instanceof AssignStmt)) 61 | continue; 62 | 63 | SootMethod callee = stmt.getInvokeExpr().getMethod(); 64 | if (!callee.getName().equals("getCallingUid")) 65 | continue; 66 | 67 | Local uidLocal = (Local) ((AssignStmt) stmt).getLeftOp(); 68 | List useUnits = findUses(body, stmt, uidLocal); 69 | for (Unit useUnit : useUnits) { 70 | Stmt useStmt = (Stmt) useUnit; 71 | if (!(useStmt instanceof IfStmt)) 72 | continue; 73 | 74 | Value ifCondition = ((IfStmt) useStmt).getCondition(); 75 | if (ifCondition instanceof EqExpr) { 76 | int uid = -1; 77 | Value op1 = ((EqExpr) ifCondition).getOp1(); 78 | Value op2 = ((EqExpr) ifCondition).getOp2(); 79 | if (op1 == uidLocal && op2 instanceof IntConstant) 80 | uid = ((IntConstant) op2).value; 81 | if (op2 == uidLocal && op1 instanceof IntConstant) 82 | uid = ((IntConstant) op1).value; 83 | 84 | if (uid != -1) { 85 | isUIDCheckMethod = true; 86 | 87 | if (!uidCheckMethod2Stmt.containsKey(sm)) 88 | uidCheckMethod2Stmt.put(sm, new HashSet()); 89 | uidCheckMethod2Stmt.get(sm).add(useStmt); 90 | 91 | assert !uidCheckStmt2UID.containsKey(useStmt); 92 | uidCheckStmt2UID.put(useStmt, resolveUID(uid)); 93 | } 94 | } else if (ifCondition instanceof NeExpr) { 95 | int uid = -1; 96 | Value op1 = ((NeExpr) ifCondition).getOp1(); 97 | Value op2 = ((NeExpr) ifCondition).getOp2(); 98 | if (op1 == uidLocal && op2 instanceof IntConstant) 99 | uid = ((IntConstant) op2).value; 100 | if (op2 == uidLocal && op1 instanceof IntConstant) 101 | uid = ((IntConstant) op1).value; 102 | 103 | if (uid != -1) { 104 | isUIDCheckMethod = true; 105 | 106 | if (!uidCheckMethod2Stmt.containsKey(sm)) 107 | uidCheckMethod2Stmt.put(sm, new HashSet()); 108 | uidCheckMethod2Stmt.get(sm).add(useStmt); 109 | 110 | assert !uidCheckStmt2UID.containsKey(useStmt); 111 | uidCheckStmt2UID.put(useStmt, resolveUID(uid)); 112 | } 113 | } else { 114 | // pass 115 | } 116 | } 117 | } 118 | 119 | if (isUIDCheckMethod == true) { 120 | // System.out.println("[INFO] [CG-UIDChecker]: " + sm.getSignature()); // debug 121 | uidCheckMethodCGSet.add(sm); 122 | } 123 | } 124 | 125 | System.out.println(" -->> Find UID Check Methods in Callgraph Finish"); 126 | } 127 | 128 | private static String resolveUID(int uid) { 129 | switch (uid) { 130 | case 0: 131 | return "ROOT_UID"; 132 | case 1000: 133 | return "SYSTEM_UID"; 134 | case 2000: 135 | return "SHELL_UID"; 136 | default: 137 | return "OTHER_UID"; 138 | } 139 | } 140 | 141 | // ---- // 142 | 143 | /* 144 | private static List findDefs(Body body, Stmt stmt, Local local) { 145 | Unit unit = (Unit) stmt; 146 | UnitGraph cfg = new BriefUnitGraph(body); 147 | SimpleLocalDefs defsResolver = new SimpleLocalDefs(cfg); 148 | List defs = defsResolver.getDefsOfAt(local, unit); 149 | 150 | return defs; 151 | } 152 | */ 153 | 154 | private static List findUses(Body body, Stmt stmt, Local local) { 155 | Unit unit = (Unit) stmt; 156 | UnitGraph cfg = new BriefUnitGraph(body); 157 | SimpleLocalDefs defsResolver = new SimpleLocalDefs(cfg); 158 | SimpleLocalUses usesResolver = new SimpleLocalUses(cfg, defsResolver); 159 | 160 | List uses = new ArrayList(); 161 | List defs = defsResolver.getDefsOfAt(local, unit); 162 | for (Unit defUnit : defs) { 163 | List pairs = usesResolver.getUsesOf(defUnit); 164 | for (UnitValueBoxPair pair : pairs) { 165 | uses.add(pair.unit); 166 | } 167 | } 168 | 169 | return uses; 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/cg/UserIDCheckFinder.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.cg; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | import soot.Body; 10 | import soot.Local; 11 | import soot.Scene; 12 | import soot.SootMethod; 13 | import soot.Unit; 14 | import soot.Value; 15 | import soot.jimple.AssignStmt; 16 | import soot.jimple.EqExpr; 17 | import soot.jimple.IfStmt; 18 | import soot.jimple.NeExpr; 19 | import soot.jimple.Stmt; 20 | import soot.jimple.toolkits.callgraph.CallGraph; 21 | import soot.jimple.toolkits.callgraph.Edge; 22 | import soot.toolkits.graph.BriefUnitGraph; 23 | import soot.toolkits.graph.UnitGraph; 24 | import soot.toolkits.scalar.SimpleLocalDefs; 25 | import soot.toolkits.scalar.SimpleLocalUses; 26 | import soot.toolkits.scalar.UnitValueBoxPair; 27 | 28 | public class UserIDCheckFinder { 29 | 30 | // find the UID check methods in CallGraph 31 | public static HashSet useridCheckMethodCGSet = new HashSet(); 32 | public static HashMap> useridCheckMethod2Stmt = new HashMap>(); 33 | 34 | public static void find(CallGraph cg) { 35 | SootMethod getUserIDSM = Scene.v().getMethod(""); 36 | assert !getUserIDSM.isPhantom(); 37 | 38 | HashSet potentialSMs = new HashSet(); 39 | 40 | Iterator edgeIter = cg.edgesInto(getUserIDSM); 41 | while (edgeIter.hasNext()) { 42 | Edge edge = edgeIter.next(); 43 | SootMethod srcSM = edge.src(); 44 | potentialSMs.add(srcSM); 45 | } 46 | 47 | for (SootMethod sm : potentialSMs) { 48 | if (!sm.isConcrete()) 49 | continue; 50 | 51 | boolean isUserIDCheckMethod = false; 52 | 53 | Body body = sm.retrieveActiveBody(); 54 | for (Unit unit : body.getUnits()) { 55 | Stmt stmt = (Stmt) unit; 56 | if (!stmt.containsInvokeExpr()) 57 | continue; 58 | if (!(stmt instanceof AssignStmt)) 59 | continue; 60 | 61 | SootMethod callee = stmt.getInvokeExpr().getMethod(); 62 | if (!callee.getName().equals("getCallingUserId")) 63 | continue; 64 | 65 | Local useridLocal = (Local) ((AssignStmt) stmt).getLeftOp(); 66 | List useUnits = findUses(body, stmt, useridLocal); 67 | for (Unit useUnit : useUnits) { 68 | Stmt useStmt = (Stmt) useUnit; 69 | if (!(useStmt instanceof IfStmt)) 70 | continue; 71 | 72 | Value ifCondition = ((IfStmt) useStmt).getCondition(); 73 | if (ifCondition instanceof EqExpr) { 74 | Value op1 = ((EqExpr) ifCondition).getOp1(); 75 | Value op2 = ((EqExpr) ifCondition).getOp2(); 76 | if (op1 == useridLocal || op2 == useridLocal) { 77 | isUserIDCheckMethod = true; 78 | 79 | if (!useridCheckMethod2Stmt.containsKey(sm)) 80 | useridCheckMethod2Stmt.put(sm, new HashSet()); 81 | useridCheckMethod2Stmt.get(sm).add(useStmt); 82 | } 83 | } else if (ifCondition instanceof NeExpr) { 84 | Value op1 = ((NeExpr) ifCondition).getOp1(); 85 | Value op2 = ((NeExpr) ifCondition).getOp2(); 86 | if (op1 == useridLocal || op2 == useridLocal) { 87 | isUserIDCheckMethod = true; 88 | 89 | if (!useridCheckMethod2Stmt.containsKey(sm)) 90 | useridCheckMethod2Stmt.put(sm, new HashSet()); 91 | useridCheckMethod2Stmt.get(sm).add(useStmt); 92 | } 93 | } else { 94 | // pass 95 | } 96 | } 97 | } 98 | 99 | if (isUserIDCheckMethod == true) { 100 | // System.out.println("[INFO] [CG-UIDChecker]: " + sm.getSignature()); // debug 101 | useridCheckMethodCGSet.add(sm); 102 | } 103 | } 104 | 105 | System.out.println(" -->> Find UserID Check Methods in Callgraph Finish"); 106 | } 107 | 108 | // ---- // 109 | 110 | /* 111 | private static List findDefs(Body body, Stmt stmt, Local local) { 112 | Unit unit = (Unit) stmt; 113 | UnitGraph cfg = new BriefUnitGraph(body); 114 | SimpleLocalDefs defsResolver = new SimpleLocalDefs(cfg); 115 | List defs = defsResolver.getDefsOfAt(local, unit); 116 | 117 | return defs; 118 | } 119 | */ 120 | 121 | private static List findUses(Body body, Stmt stmt, Local local) { 122 | Unit unit = (Unit) stmt; 123 | UnitGraph cfg = new BriefUnitGraph(body); 124 | SimpleLocalDefs defsResolver = new SimpleLocalDefs(cfg); 125 | SimpleLocalUses usesResolver = new SimpleLocalUses(cfg, defsResolver); 126 | 127 | List uses = new ArrayList(); 128 | List defs = defsResolver.getDefsOfAt(local, unit); 129 | for (Unit defUnit : defs) { 130 | List pairs = usesResolver.getUsesOf(defUnit); 131 | for (UnitValueBoxPair pair : pairs) { 132 | uses.add(pair.unit); 133 | } 134 | } 135 | 136 | return uses; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/preprocess/PermissionCheckFinder.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.preprocess; 2 | 3 | import java.io.File; 4 | import java.io.FileWriter; 5 | import java.io.IOException; 6 | import java.util.HashSet; 7 | 8 | import hk.polyu.Config; 9 | import soot.Body; 10 | import soot.Local; 11 | import soot.Scene; 12 | import soot.SootClass; 13 | import soot.SootMethod; 14 | import soot.Unit; 15 | import soot.Value; 16 | import soot.jimple.AssignStmt; 17 | import soot.jimple.FieldRef; 18 | import soot.jimple.ReturnStmt; 19 | import soot.jimple.Stmt; 20 | import soot.jimple.VirtualInvokeExpr; 21 | import soot.toolkits.graph.BriefUnitGraph; 22 | import soot.toolkits.scalar.SimpleLocalDefs; 23 | 24 | public class PermissionCheckFinder { 25 | 26 | // find the permission check methods in Scene 27 | private static HashSet permissionCheckMethodSet = new HashSet(); 28 | 29 | public static void find() { 30 | for (SootClass sc : Scene.v().getClasses()) { 31 | if (sc.isPhantom()) 32 | continue; 33 | 34 | for (SootMethod sm : sc.getMethods()) { 35 | boolean isPermissionCheckMethod = false; 36 | 37 | if (!sm.isConcrete()) 38 | continue; 39 | if (!sm.getReturnType().toString().equals("int")) 40 | continue; 41 | 42 | Body body = sm.retrieveActiveBody(); 43 | BriefUnitGraph cfg = new BriefUnitGraph(body); 44 | SimpleLocalDefs localDefs = new SimpleLocalDefs(cfg); 45 | 46 | for (Unit tail : cfg.getTails()) { 47 | if (!(tail instanceof ReturnStmt)) 48 | continue; 49 | 50 | Value retValue = ((ReturnStmt) tail).getOp(); 51 | if (!(retValue instanceof Local)) 52 | continue; 53 | 54 | Local retLocal = (Local) retValue; 55 | for (Unit defRetLocalUnit : localDefs.getDefsOf(retLocal)) { 56 | Stmt defRetLocalStmt = (Stmt) defRetLocalUnit; 57 | if (!(defRetLocalStmt instanceof AssignStmt)) 58 | continue; 59 | if (!defRetLocalStmt.containsInvokeExpr()) 60 | continue; 61 | if (!defRetLocalStmt.getInvokeExpr().getMethod().getSignature().equals("")) 62 | continue; 63 | 64 | Value baseValue = ((VirtualInvokeExpr) defRetLocalStmt.getInvokeExpr()).getBase(); 65 | if (!(baseValue instanceof Local)) 66 | continue; 67 | 68 | Local baseLocal = (Local) baseValue; 69 | for (Unit defBaseLocalUnit : localDefs.getDefsOf(baseLocal)) { 70 | if (!(defBaseLocalUnit instanceof AssignStmt)) 71 | continue; 72 | Value rightValue = ((AssignStmt) defBaseLocalUnit).getRightOp(); 73 | if (!(rightValue instanceof FieldRef)) 74 | continue; 75 | 76 | String fieldName = ((FieldRef) rightValue).getField().getName(); 77 | if (fieldName.equals("PERMISSION_DENIED") || fieldName.equals("PERMISSION_GRANTED")) { 78 | isPermissionCheckMethod = true; 79 | break; 80 | } 81 | } 82 | } 83 | } 84 | 85 | if (isPermissionCheckMethod) 86 | permissionCheckMethodSet.add(sm); 87 | } 88 | } 89 | 90 | // for (SootMethod sm : permissionCheckMethodSet) 91 | // System.out.println("INFO [PermissionChecker]: " + sm.getSignature()); // debug 92 | // System.out.println(" -->> Find Permission Check Methods Finish"); 93 | 94 | save(); 95 | } 96 | 97 | private static void save() { 98 | try { 99 | File PermissionCheckFile = new File(Config.OutputPermissionCheckFile); 100 | if (PermissionCheckFile.exists()) 101 | PermissionCheckFile.delete(); 102 | PermissionCheckFile.createNewFile(); 103 | 104 | FileWriter fw = new FileWriter(PermissionCheckFile); 105 | for (SootMethod sm : permissionCheckMethodSet) 106 | fw.write(sm.getSignature() + "\n"); 107 | 108 | fw.flush(); 109 | fw.close(); 110 | } catch (IOException ioe) { 111 | System.err.println("[error] file " + Config.OutputPermissionCheckFile + " fail to be created !"); 112 | System.exit(0); 113 | } 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/preprocess/SootInitializer.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.preprocess; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.ConcurrentModificationException; 6 | import java.util.List; 7 | 8 | import hk.polyu.Config; 9 | import soot.G; 10 | import soot.Scene; 11 | import soot.SootClass; 12 | import soot.SootMethod; 13 | import soot.options.Options; 14 | 15 | public class SootInitializer { 16 | 17 | public static void init() { 18 | // clean up 19 | G.reset(); 20 | 21 | // set soot environment 22 | Options.v().set_no_bodies_for_excluded(true); 23 | Options.v().set_allow_phantom_refs(true); 24 | Options.v().set_whole_program(true); 25 | Options.v().set_output_format(Options.output_format_none); 26 | Options.v().set_process_dir(Collections.singletonList(Config.OutputClassesDirectory)); 27 | Options.v().set_src_prec(Options.src_prec_class); 28 | Options.v().set_keep_line_number(false); 29 | Options.v().set_keep_offset(false); 30 | Options.v().set_ignore_resolving_levels(true); 31 | Options.v().setPhaseOption("cg", "enabled:true"); 32 | Options.v().setPhaseOption("cg.spark", "enabled:true"); 33 | Options.v().setPhaseOption("cg.spark", "string-constants:true"); 34 | 35 | // exclude certain packages for better performance 36 | List excludeList = new ArrayList(); 37 | excludeList.add("java."); 38 | Options.v().set_exclude(excludeList); 39 | Options.v().set_no_bodies_for_excluded(true); 40 | 41 | Scene.v().loadNecessaryClasses(); 42 | 43 | // ensure every SootClass instance has been resolved 44 | while (true) { 45 | try { 46 | // note: Scene.v().getClasses() may throw ConcurrentModificationException 47 | for (SootClass sc : Scene.v().getClasses()) { 48 | if (sc.isPhantom()) 49 | continue; 50 | if (sc.getName().startsWith("java.")) { 51 | sc.setPhantomClass(); 52 | for (SootMethod sm : sc.getMethods()) { 53 | sm.setPhantom(true); 54 | } 55 | } else { 56 | for (SootMethod sm : sc.getMethods()) { 57 | if (!sm.isConcrete()) 58 | continue; 59 | sm.retrieveActiveBody(); 60 | } 61 | } 62 | } 63 | break; 64 | } catch (ConcurrentModificationException e) { /* pass */ } 65 | } 66 | 67 | System.out.println(" -->> Soot Initialization Finish"); // debug 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/soot/preprocess/UidCheckFinder.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.soot.preprocess; 2 | 3 | import java.io.File; 4 | import java.io.FileWriter; 5 | import java.io.IOException; 6 | import java.util.HashSet; 7 | 8 | import hk.polyu.Config; 9 | import soot.Body; 10 | import soot.Local; 11 | import soot.Scene; 12 | import soot.SootClass; 13 | import soot.SootField; 14 | import soot.SootMethod; 15 | import soot.Unit; 16 | import soot.Value; 17 | import soot.jimple.AssignStmt; 18 | import soot.jimple.EqExpr; 19 | import soot.jimple.FieldRef; 20 | import soot.jimple.IfStmt; 21 | import soot.jimple.InvokeExpr; 22 | import soot.jimple.NewExpr; 23 | import soot.jimple.Stmt; 24 | import soot.jimple.VirtualInvokeExpr; 25 | import soot.toolkits.graph.BriefUnitGraph; 26 | import soot.toolkits.graph.MHGDominatorsFinder; 27 | import soot.toolkits.scalar.SimpleLocalDefs; 28 | 29 | public class UidCheckFinder { 30 | 31 | // find the uid check methods in Scene 32 | private static HashSet uidCheckMethodSet = new HashSet(); 33 | 34 | public static void find() { 35 | for (SootClass sc : Scene.v().getClasses()) { 36 | if (sc.isPhantom()) 37 | continue; 38 | 39 | for (SootMethod sm : sc.getMethods()) { 40 | boolean isUidCheckMethod = false; 41 | 42 | if (!sm.isConcrete()) 43 | continue; 44 | if (!sm.getReturnType().toString().equals("void")) 45 | continue; 46 | 47 | Body body = sm.retrieveActiveBody(); 48 | BriefUnitGraph cfg = new BriefUnitGraph(body); 49 | SimpleLocalDefs localDefs = new SimpleLocalDefs(cfg); 50 | 51 | for (Unit unit : body.getUnits()) { 52 | if (!(unit instanceof AssignStmt)) 53 | continue; 54 | 55 | AssignStmt assignStmt = (AssignStmt) unit; 56 | Value rightOp = assignStmt.getRightOp(); 57 | if (!(rightOp instanceof NewExpr)) 58 | continue; 59 | if (!((NewExpr) rightOp).getType().toString().equals("java.lang.SecurityException")) 60 | continue; 61 | 62 | // System.out.println(unit); 63 | MHGDominatorsFinder cfgDom = new MHGDominatorsFinder(cfg); 64 | for (Unit domUnit : cfgDom.getDominators(unit)) { 65 | if (!(domUnit instanceof IfStmt)) 66 | continue; 67 | Value ifCondition = ((IfStmt) domUnit).getCondition(); 68 | if (!(ifCondition instanceof EqExpr)) 69 | continue; 70 | 71 | // System.out.println("\t" + domUnit); 72 | 73 | Value ifOp1 = ((EqExpr) ifCondition).getOp1(); 74 | if (ifOp1 instanceof Local) { 75 | for (Unit defUnit : localDefs.getDefsOf((Local) ifOp1)) { 76 | // System.out.println("\t\t" + "Definition Unit: " + defUnit); 77 | 78 | Stmt defStmt = (Stmt) defUnit; 79 | if (!defStmt.containsInvokeExpr()) 80 | continue; 81 | InvokeExpr invokeExpr = defStmt.getInvokeExpr(); 82 | if (!invokeExpr.getMethod().getSignature().equals("")) 83 | continue; 84 | 85 | Value baseValue = ((VirtualInvokeExpr) invokeExpr).getBase(); 86 | if (!(baseValue instanceof Local)) 87 | continue; 88 | for (Unit baseUnit : localDefs.getDefsOf((Local) baseValue)) { 89 | if (!(baseUnit instanceof AssignStmt)) 90 | continue; 91 | Value rhsOp = ((AssignStmt) baseUnit).getRightOp(); 92 | if (!(rhsOp instanceof FieldRef)) 93 | continue; 94 | 95 | SootField refField = ((FieldRef) rhsOp).getField(); 96 | if (refField.toString().equals("") 97 | || refField.toString().equals("") 98 | || refField.toString().equals("")) { 99 | isUidCheckMethod = true; 100 | break; 101 | } 102 | } 103 | 104 | if (isUidCheckMethod) 105 | break; 106 | } 107 | } 108 | 109 | if (isUidCheckMethod) 110 | break; 111 | 112 | Value ifOp2 = ((EqExpr) ifCondition).getOp2(); 113 | if (ifOp2 instanceof Local) { 114 | for (Unit defUnit : localDefs.getDefsOf((Local) ifOp2)) { 115 | // System.out.println("\t\t" + "Definition Unit: " + defUnit); 116 | 117 | Stmt defStmt = (Stmt) defUnit; 118 | if (!defStmt.containsInvokeExpr()) 119 | continue; 120 | InvokeExpr invokeExpr = defStmt.getInvokeExpr(); 121 | if (!invokeExpr.getMethod().getSignature().equals("")) 122 | continue; 123 | 124 | Value baseValue = ((VirtualInvokeExpr) invokeExpr).getBase(); 125 | if (!(baseValue instanceof Local)) 126 | continue; 127 | for (Unit baseUnit : localDefs.getDefsOf((Local) baseValue)) { 128 | if (!(baseUnit instanceof AssignStmt)) 129 | continue; 130 | Value rhsOp = ((AssignStmt) baseUnit).getRightOp(); 131 | if (!(rhsOp instanceof FieldRef)) 132 | continue; 133 | 134 | SootField refField = ((FieldRef) rhsOp).getField(); 135 | if (refField.toString().equals("") 136 | || refField.toString().equals("") 137 | || refField.toString().equals("")) { 138 | isUidCheckMethod = true; 139 | break; 140 | } 141 | } 142 | 143 | if (isUidCheckMethod) 144 | break; 145 | } 146 | } 147 | 148 | if (isUidCheckMethod) 149 | break; 150 | } 151 | 152 | if (isUidCheckMethod) 153 | break; 154 | } 155 | 156 | if (isUidCheckMethod) 157 | uidCheckMethodSet.add(sm); 158 | } 159 | } 160 | 161 | for (SootMethod sm : uidCheckMethodSet) 162 | System.out.println("INFO [UidChecker]: " + sm.getSignature()); // debug 163 | System.out.println(" -->> Find Uid Check Methods Finish"); 164 | 165 | save(); 166 | } 167 | 168 | private static void save() { 169 | try { 170 | File UidCheckFile = new File(Config.OutputUidCheckFile); 171 | if (UidCheckFile.exists()) 172 | UidCheckFile.delete(); 173 | UidCheckFile.createNewFile(); 174 | 175 | FileWriter fw = new FileWriter(UidCheckFile); 176 | for (SootMethod sm : uidCheckMethodSet) 177 | fw.write(sm.getSignature() + "\n"); 178 | 179 | fw.flush(); 180 | fw.close(); 181 | } catch (IOException ioe) { 182 | System.err.println("[error] file " + Config.OutputUidCheckFile + " fail to be created !"); 183 | System.exit(0); 184 | } 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/util/BashRunner.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.util; 2 | 3 | import java.io.IOException; 4 | import java.lang.ProcessBuilder.Redirect; 5 | import java.util.ArrayList; 6 | 7 | public class BashRunner { 8 | 9 | private final ArrayList bashCommands; 10 | private final boolean async; 11 | 12 | public BashRunner(ArrayList bashCommands, boolean async){ 13 | this.bashCommands = bashCommands; 14 | this.async = async; 15 | } 16 | 17 | public void run(){ 18 | try { 19 | ProcessBuilder builder = new ProcessBuilder(this.bashCommands); 20 | builder.redirectOutput(Redirect.INHERIT); 21 | builder.redirectError(Redirect.INHERIT); 22 | Process process = builder.start(); 23 | // wait for thread if not asynchronous 24 | if(!async) process.waitFor(); 25 | } catch(IOException ioe) { 26 | ioe.printStackTrace(); 27 | } catch (InterruptedException ie) { 28 | ie.printStackTrace(); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /java_analysis/hk/polyu/util/ExtractClasses.java: -------------------------------------------------------------------------------- 1 | package hk.polyu.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.BufferedWriter; 5 | import java.io.File; 6 | import java.io.FileNotFoundException; 7 | import java.io.FileReader; 8 | import java.io.FileWriter; 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.LinkedList; 13 | 14 | import hk.polyu.Config; 15 | 16 | public class ExtractClasses { 17 | 18 | private static ArrayList appJarList = new ArrayList(); 19 | private static ArrayList frameworkJarList = new ArrayList(); 20 | 21 | public static void extract() { 22 | readInstalledTxtFile(); 23 | showJarList(); 24 | extractJar(); 25 | createClassList(); 26 | } 27 | 28 | private static void readInstalledTxtFile() { 29 | String filePath = Config.InstalledTxtFile; 30 | File installedTxtFile = new File(filePath); 31 | if (!installedTxtFile.exists()) { 32 | System.err.println("the path of \"installed-files.txt\" is invalid (input path: " + filePath + ")"); 33 | System.err.println("exit ......"); 34 | System.exit(0); 35 | } 36 | 37 | // read the file content 38 | try { 39 | BufferedReader br = new BufferedReader(new FileReader(installedTxtFile)); 40 | String content = null; 41 | while ((content = br.readLine()) != null) { 42 | if (content.contains("/system/app") && content.endsWith(".apk")) { 43 | /* 44 | int index = content.split("/").length; 45 | String appName = content.split("/")[index - 1].split(".apk")[0]; 46 | String appJarPath = Config.SystemAppDirectory + "/" + appName + "_intermediates/classes-full-debug.jar"; 47 | File appJarFile = new File(appJarPath); 48 | if (appJarFile.exists()) { 49 | appJarList.add(appJarPath); 50 | copyJarFile(appName, appJarPath); 51 | } else { 52 | System.err.println("cannot find compiled classes for " + appName + " ......"); 53 | } 54 | */ 55 | } else if (content.contains("/system/framework") && content.endsWith(".jar")) { 56 | int index = content.split("/").length; 57 | String jarName = content.split("/")[index - 1].split(".jar")[0]; 58 | jarName = adjustJarName(jarName); 59 | String jarPath = Config.FrameworkDirectory + "/" + jarName + "_intermediates/classes.jar"; 60 | File jarFile = new File(jarPath); 61 | if (jarFile.exists()) { 62 | frameworkJarList.add(jarPath); 63 | copyJarFile(jarName, jarPath); 64 | } else { 65 | System.err.println("cannot find compiled classes for " + jarName + " ......"); 66 | } 67 | } else { 68 | // other cases 69 | } 70 | } 71 | br.close(); 72 | } catch(FileNotFoundException fnfe) { 73 | fnfe.printStackTrace(); 74 | } catch(IOException ioe) { 75 | ioe.printStackTrace(); 76 | } 77 | 78 | /* 79 | // special issue: NFC (app) is not installed to generic target, thus not found in the file "installed-files.txt" 80 | String nfcJarPath = Config.SystemAppDirectory + "/" + "Nfc_intermediates/classes-full-debug.jar"; 81 | File nfcJarFile = new File(nfcJarPath); 82 | if (nfcJarFile.exists()) { 83 | appJarList.add(nfcJarPath); 84 | copyJarFile("Nfc", nfcJarPath); 85 | } else { 86 | System.err.println("cannot find compiled classes for NFC ......"); 87 | } 88 | */ 89 | } 90 | 91 | private static String adjustJarName(String originName) { 92 | // for AOSP-10 93 | /* 94 | switch (originName) { 95 | case "monkey": 96 | return "monkeylib"; 97 | case "input": 98 | return "inputlib"; 99 | case "svc": 100 | return "svclib"; 101 | default: 102 | break; 103 | } 104 | */ 105 | 106 | // for AOSP-11 107 | /* 108 | switch (originName) { 109 | case "framework": 110 | return "framework-minus-apex"; 111 | case "monkey": 112 | return "monkeylib"; 113 | default: 114 | break; 115 | } 116 | */ 117 | 118 | // for AOSP-12 119 | switch (originName) { 120 | case "framework": 121 | return "framework-minus-apex"; 122 | default: 123 | break; 124 | } 125 | 126 | // for LineageOS_17_1 127 | /* 128 | switch (originName) { 129 | case "monkey": 130 | return "monkeylib"; 131 | case "input": 132 | return "inputlib"; 133 | case "svc": 134 | return "svclib"; 135 | default: 136 | break; 137 | } 138 | */ 139 | 140 | // for GrapheneOS 141 | /* 142 | switch (originName) { 143 | case "framework": 144 | return "framework-minus-apex"; 145 | case "monkey": 146 | return "monkeylib"; 147 | default: 148 | break; 149 | } 150 | */ 151 | 152 | // for OmniROM 153 | /* 154 | switch (originName) { 155 | case "monkey": 156 | return "monkeylib"; 157 | case "input": 158 | return "inputlib"; 159 | case "svc": 160 | return "svclib"; 161 | default: 162 | break; 163 | } 164 | */ 165 | 166 | // for crDroid 167 | /* 168 | switch (originName) { 169 | case "framework": 170 | return "framework-minus-apex"; 171 | case "monkey": 172 | return "monkeylib"; 173 | default: 174 | break; 175 | } 176 | */ 177 | 178 | return originName; 179 | } 180 | 181 | private static void copyJarFile(String jarName, String jarPath) { 182 | ArrayList commands = new ArrayList(); 183 | commands.addAll(Arrays.asList(Config.CommonCommand)); 184 | commands.add(String.format("%s %s %s", Config.CopyJarBash, Config.OutputClassesDirectory + "/" + jarName + ".jar", jarPath)); 185 | BashRunner bash = new BashRunner(commands, false); 186 | bash.run(); 187 | } 188 | 189 | private static void showJarList() { 190 | System.out.println("---- SYSTEM APP JAR LIST ----"); 191 | for (String jarPath : appJarList) { 192 | System.out.println("\t" + jarPath); 193 | } 194 | System.out.println("---- FRAMEWORK JAR LIST ----"); 195 | for (String jarPath : frameworkJarList) { 196 | System.out.println("\t" + jarPath); 197 | } 198 | } 199 | 200 | private static void extractJar() { 201 | File classOutputDir = new File(Config.OutputClassesDirectory); 202 | if (classOutputDir.exists() && classOutputDir.isDirectory()) { 203 | File[] jarFiles = classOutputDir.listFiles(); 204 | for (File jarFile : jarFiles) { 205 | ArrayList commands = new ArrayList(); 206 | commands.addAll(Arrays.asList(Config.CommonCommand)); 207 | commands.add(String.format("%s %s %s", Config.ExtractJarBash, jarFile.getAbsolutePath(), Config.APILevel)); 208 | BashRunner bash = new BashRunner(commands, false); 209 | bash.run(); 210 | } 211 | } else { 212 | System.err.println("the path of Config.OutputClassesDirectory is invalid (input path: " + Config.OutputClassesDirectory + ")"); 213 | System.err.println("exit ......"); 214 | System.exit(0); 215 | } 216 | } 217 | 218 | private static void createClassList() { 219 | File classOutputDir = new File(Config.OutputClassesDirectory); 220 | if (!classOutputDir.exists() || !classOutputDir.isDirectory()) { 221 | System.err.println("the path of Config.OutputClassesDirectory is invalid (input path: " + Config.OutputClassesDirectory + ")"); 222 | System.err.println("exit ......"); 223 | System.exit(0); 224 | } 225 | 226 | ArrayList classList = new ArrayList(); 227 | LinkedList dirQueue = new LinkedList(); 228 | dirQueue.add(classOutputDir); 229 | while (!dirQueue.isEmpty()) { 230 | File dir = dirQueue.remove(); 231 | File[] files = dir.listFiles(); 232 | for (File file : files) { 233 | if (file.isDirectory()) { 234 | dirQueue.add(file); 235 | } else if (file.getAbsolutePath().endsWith(".class")) { 236 | String relativePath = file.getAbsolutePath().replace(Config.OutputClassesDirectory + "/", ""); 237 | String replaceSlash = relativePath.replace("/", "."); 238 | String className = replaceSlash.replace(".class", ""); 239 | classList.add(className); 240 | } else { 241 | // other cases 242 | } 243 | } 244 | } 245 | 246 | // write to file "class_list.txt" 247 | File classListFile = new File(Config.OutputClassListTxtFile); 248 | if (classListFile.exists()) 249 | classListFile.delete(); 250 | try { 251 | classListFile.createNewFile(); 252 | BufferedWriter bw = new BufferedWriter(new FileWriter(classListFile)); 253 | for(String className : classList) { 254 | bw.write(className + "\n"); 255 | } 256 | bw.flush(); 257 | bw.close(); 258 | } catch (IOException ioe) { 259 | ioe.printStackTrace(); 260 | } 261 | } 262 | 263 | // ---- ---- ---- ---- ---- // 264 | 265 | // module test 266 | public static void main(String[] args) { 267 | readInstalledTxtFile(); 268 | showJarList(); 269 | // extractJar(); 270 | // createClassList(); 271 | } 272 | 273 | } 274 | -------------------------------------------------------------------------------- /native_analysis/extract_bitcode.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import os 3 | import re 4 | import shutil 5 | import subprocess 6 | import Queue 7 | 8 | ## Android 10 9 | # aosp_out_dir = "/home/zhouhao/data_fast/android_0805/out/target/product/generic_x86" 10 | # bitcode_dir = "/home/zhouhao/data_fast/SECURITY-2021/native_analysis/bitcode_aosp_0805" 11 | # installed = "/home/zhouhao/data_fast/android_0805/out/target/product/generic_x86/installed-files.txt" 12 | ## Android 11 13 | # aosp_out_dir = "/home/zhouhao/data_fast/android_1005/out/target/product/generic_x86" 14 | # bitcode_dir = "/home/zhouhao/data_fast/SECURITY-2021/native_analysis/bitcode_aosp_1005" 15 | # installed = "/home/zhouhao/data_fast/android_1005/out/target/product/generic_x86/installed-files.txt" 16 | ## Android 11 17 | # aosp_out_dir = "/home/zhouhao/AOSP/aosp_11_0805/out/target/product/generic_x86" 18 | # bitcode_dir = "/home/zhouhao/AOSP/NDSS21/native_analysis/bc_aosp_11_0805" 19 | # installed = "/home/zhouhao/AOSP/aosp_11_0805/out/target/product/generic_x86/installed-files.txt" 20 | ## Android 12 21 | aosp_out_dir = "/home/zhouhao/AOSP/aosp_12_0905/out/target/product/generic_x86" 22 | bitcode_dir = "/home/zhouhao/AOSP/NDSS21/native_analysis/bc_aosp_12_0905" 23 | installed = "/home/zhouhao/AOSP/aosp_12_0905/out/target/product/generic_x86/installed-files.txt" 24 | 25 | #### ==== #### PRE-PROCESS #### ==== #### 26 | 27 | ''' 28 | ## STEP-1 29 | so_paths = [] ## store the absolute path of each shared library and each native service 30 | 31 | fp = open(installed, "r") 32 | for line in fp.readlines(): 33 | line = line.strip() 34 | ## library 35 | if line.endswith(".so"): 36 | if "/system/product/" in line: 37 | continue 38 | 39 | path = os.path.join(aosp_out_dir, line[line.find("/")+1:]) 40 | if os.path.islink(path): 41 | continue 42 | if not ("/lib/" in path): 43 | continue 44 | assert os.path.exists(path) 45 | 46 | is_32bit = False 47 | command = "file %s" % (path) 48 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 49 | for line in p.stdout.readlines(): 50 | line = line.strip() 51 | # print line 52 | if "32-bit" in line: 53 | is_32bit = True 54 | if is_32bit == True: 55 | # print path 56 | so_paths.append(path) 57 | ## executable (NOTE: some native services are native executable applications) 58 | else: 59 | if not ("/system/bin/" in line): 60 | continue 61 | 62 | path = os.path.join(aosp_out_dir, line[line.find("/")+1:]) 63 | if os.path.islink(path): 64 | continue 65 | assert os.path.exists(path) 66 | 67 | is_32bit = False 68 | command = "file %s" % (path) 69 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 70 | for line in p.stdout.readlines(): 71 | line = line.strip() 72 | # print line 73 | if "32-bit" in line: 74 | is_32bit = True 75 | 76 | is_service = False 77 | command = "llvm-objdump -p %s | grep \"NEEDED\"" % (path) 78 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 79 | for line in p.stdout.readlines(): 80 | line = line.strip() 81 | # print line 82 | if "libbinder.so" in line: 83 | is_service = True 84 | 85 | if is_32bit and is_service: 86 | if "app_process" in path: 87 | continue 88 | # print path + "(*)" 89 | so_paths.append(path) 90 | fp.close() 91 | 92 | so_paths.sort() 93 | # for so_path in so_paths: 94 | # print so_path 95 | # exit() 96 | 97 | ## 98 | 99 | soname2sopath = {} ## [so name] -> [so path] 100 | for so_path in so_paths: 101 | if not so_path.endswith(".so"): 102 | so_name = so_path.split("/")[-1] 103 | if soname2sopath.has_key(so_name): 104 | print "[WARN] %s" % (so_name) 105 | assert True 106 | soname2sopath[so_name] = so_path 107 | continue 108 | 109 | so_name = so_path.split("/")[-1] 110 | if soname2sopath.has_key(so_name): 111 | if ("/system/lib/%s" % (so_name)) in soname2sopath[so_name]: 112 | continue ## we assume that shared libraries prefer to being placed in "/system/lib/" 113 | 114 | if ("/system/lib/%s" % (so_name)) in so_path: 115 | # print "[UPDATE] %s -> %s" % (so_name, so_path) 116 | soname2sopath[so_name] = so_path 117 | elif ("/system/apex/com.android.runtime.debug/lib/%s" % (so_name)) in so_path: 118 | # print "[UPDATE] %s -> %s" % (so_name, so_path) 119 | soname2sopath[so_name] = so_path 120 | # for android 10 121 | elif "/vndk-28/" in soname2sopath[so_name]: 122 | if os.path.exists(soname2sopath[so_name].replace("/vndk-28/", "/vndk-29/")): 123 | # print "[UPDATE] %s -> %s" % (so_name, soname2sopath[so_name].replace("/vndk-28/", "/vndk-29/")) 124 | soname2sopath[so_name] = soname2sopath[so_name].replace("/vndk-28/", "/vndk-29/") 125 | elif "/vndk-sp-28/" in soname2sopath[so_name]: 126 | if os.path.exists(soname2sopath[so_name].replace("/vndk-sp-28/", "/vndk-sp-29/")): 127 | # print "[UPDATE] %s -> %s" % (so_name, soname2sopath[so_name].replace("/vndk-sp-28/", "/vndk-sp-29/")) 128 | soname2sopath[so_name] = soname2sopath[so_name].replace("/vndk-sp-28/", "/vndk-sp-29/") 129 | # for android 11-12 130 | elif ".vndk.v28" in soname2sopath[so_name]: 131 | if os.path.exists(soname2sopath[so_name].replace(".vndk.v28", ".vndk.current")): 132 | # print "[UPDATE] %s -> %s" % (so_name, soname2sopath[so_name].replace(".vndk.v28", ".vndk.current")) 133 | soname2sopath[so_name] = soname2sopath[so_name].replace(".vndk.v28", ".vndk.current") 134 | elif ".vndk.v29" in soname2sopath[so_name]: 135 | if os.path.exists(soname2sopath[so_name].replace(".vndk.v29", ".vndk.current")): 136 | # print "[UPDATE] %s -> %s" % (so_name, soname2sopath[so_name].replace(".vndk.v29", ".vndk.current")) 137 | soname2sopath[so_name] = soname2sopath[so_name].replace("vndk.v29", ".vndk.current") 138 | elif ".vndk.v30" in soname2sopath[so_name]: 139 | if os.path.exists(soname2sopath[so_name].replace(".vndk.v30", ".vndk.current")): 140 | # print "[UPDATE] %s -> %s" % (so_name, soname2sopath[so_name].replace(".vndk.v30", ".vndk.current")) 141 | soname2sopath[so_name] = soname2sopath[so_name].replace("vndk.v30", ".vndk.current") 142 | else: 143 | # print "[IGNORE] %s -> %s" % (so_name, so_path) 144 | pass 145 | else: 146 | # print "%s -> %s" % (so_name, so_path) 147 | soname2sopath[so_name] = so_path 148 | 149 | # for so_name in soname2sopath.keys(): 150 | # so_path = soname2sopath[so_name] 151 | # print "%s -> %s" % (so_name, so_path) 152 | 153 | soname2sopath_fp = open("soname2sopath.txt", "w") 154 | for so_name in soname2sopath.keys(): 155 | so_path = soname2sopath[so_name] 156 | soname2sopath_fp.write("%s -> %s\n" % (so_name, so_path)) 157 | soname2sopath_fp.flush() 158 | soname2sopath_fp.close() 159 | 160 | ## 161 | 162 | soname2depname = {} ## [so name] -> [dep so name] 163 | for so_name in soname2sopath.keys(): 164 | # print so_name 165 | so_path = soname2sopath[so_name] 166 | so_deps = [] 167 | dep_queue = Queue.Queue() 168 | dep_queue_util = set() 169 | ## put direct dependent libraries into dep_queue 170 | command = "llvm-objdump -p %s | grep \"NEEDED\"" % (so_path) 171 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 172 | for line in p.stdout.readlines(): 173 | line = line.replace("NEEDED", "") 174 | line = line.strip() 175 | if soname2sopath.has_key(line): 176 | # print " " + line 177 | dep_queue.put(line) 178 | dep_queue_util.add(line) 179 | else: 180 | print " [UNKNOWN] " + line 181 | assert False 182 | ## put indirect dependent libraries into dep_queue 183 | while not dep_queue.empty(): 184 | cur_so = dep_queue.get() 185 | if cur_so in so_deps: 186 | continue 187 | if not soname2sopath.has_key(cur_so): 188 | continue 189 | so_deps.append(cur_so) 190 | # cur_path = soname2sopath[cur_so] 191 | # command = "llvm-objdump -p %s | grep \"NEEDED\"" % (cur_path) 192 | # p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 193 | # for line in p.stdout.readlines(): 194 | # line = line.replace("NEEDED", "") 195 | # line = line.strip() 196 | # if soname2sopath.has_key(line): 197 | # # print " " + line 198 | # if not (line in so_deps) and not (line in dep_queue_util): 199 | # dep_queue.put(line) 200 | # dep_queue_util.add(line) 201 | # else: 202 | # print " [UNKNOWN] " + line 203 | # assert False 204 | 205 | if soname2depname.has_key(so_name): 206 | assert False 207 | soname2depname[so_name] = so_deps 208 | 209 | # for so_name in soname2depname.keys(): 210 | # print so_name 211 | # so_deps = soname2depname[so_name] 212 | # for so_dep in so_deps: 213 | # print " => " + so_dep 214 | 215 | soname2depname_fp = open("soname2depname.txt", "w") 216 | for so_name in soname2depname.keys(): 217 | so_deps = soname2depname[so_name] 218 | for so_dep in so_deps: 219 | soname2depname_fp.write("%s -> %s\n" % (so_name, so_dep)) 220 | soname2depname_fp.flush() 221 | soname2depname_fp.close() 222 | 223 | exit() 224 | ''' 225 | 226 | #''' 227 | soname2sopath = {} ## [so name] -> [so path] 228 | soname2sopath_fp = open("soname2sopath.txt", "r") 229 | for line in soname2sopath_fp.readlines(): 230 | line = line.strip() 231 | so_name = line.split(" -> ")[0].strip() 232 | so_path = line.split(" -> ")[1].strip() 233 | assert not soname2sopath.has_key(so_name) 234 | soname2sopath[so_name] = so_path 235 | soname2sopath_fp.close() 236 | 237 | soname2depname = {} ## [so name] -> [dep so name] 238 | soname2depname_fp = open("soname2depname.txt", "r") 239 | for line in soname2depname_fp.readlines(): 240 | line = line.strip() 241 | so_name = line.split(" -> ")[0].strip() 242 | if not soname2depname.has_key(so_name): 243 | soname2depname[so_name] = [] 244 | so_dep = line.split(" -> ")[1].strip() 245 | soname2depname[so_name].append(so_dep) 246 | soname2depname_fp.close() 247 | #''' 248 | 249 | #### ==== #### ==== #### 250 | 251 | so_tests = [ 252 | # "libmedia_jni.so", 253 | # "libandroid_servers.so", 254 | # "libandroid_runtime.so", 255 | 256 | # "surfaceflinger", 257 | # "libsurfaceflinger.so", 258 | # "libSurfaceFlingerProp.so", 259 | 260 | # "audioserver", 261 | # "libaudioflinger.so", 262 | # "libaudiopolicyservice.so" 263 | # "libaaudioservice.so", 264 | 265 | # "libcameraservice.so", 266 | ] 267 | 268 | ''' 269 | ## STEP-2 (I. extract-bc) 270 | for so_name in soname2sopath.keys(): 271 | if len(so_tests) > 0 and not (so_name in so_tests): 272 | continue 273 | 274 | so_path = soname2sopath[so_name] 275 | bc_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".bc" 276 | if not os.path.exists(os.path.dirname(bc_path)): 277 | os.makedirs(os.path.dirname(bc_path)) 278 | 279 | # NOTE: it is possible that some native libraries may not have the ".llvm_bc" ELF section (should raraely happen) 280 | command = "extract-bc --output %s %s" % (bc_path, so_path) 281 | print "extrace-bc -> " + so_path 282 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 283 | for line in p.stdout.readlines(): 284 | line = line.strip() 285 | print line 286 | 287 | exit() 288 | ''' 289 | 290 | ''' 291 | ## STEP-2 (II. llvm-dis) 292 | for so_name in soname2sopath.keys(): 293 | if len(so_tests) > 0 and not (so_name in so_tests): 294 | continue 295 | 296 | so_path = soname2sopath[so_name] 297 | bc_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".bc" 298 | if not os.path.exists(bc_path): 299 | continue 300 | ll_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".ll" 301 | 302 | command = "llvm-dis %s -o=%s" % (bc_path, ll_path) 303 | print "llvm-dis -> " + bc_path 304 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 305 | for line in p.stdout.readlines(): 306 | line = line.strip() 307 | print line 308 | 309 | exit() 310 | ''' 311 | 312 | ''' 313 | ## For Debug Purpose ## 314 | ## STEP-2 (III. llvm-link) 315 | ignores = [ "libc.so", "libc++.so", "libstdc++.so", "libm.so", "libz.so", 316 | "libdl.so", "libdl_android.so", "ld-android.so", 317 | "libbpf.so", "libbpf_android.so", "libnetdbpf.so", 318 | "libbacktrace.so", "libunwindstack.so", 319 | "liblog.so", 320 | "libEGL.so", "libGLESv1_CM.so", "libGLESv2.so", "libGLESv3.so", 321 | "libhwui.so" ] 322 | for so_name in soname2sopath.keys(): 323 | if not (so_name in so_tests): 324 | continue 325 | 326 | ## TODO: cannot handle libhwui.so 327 | if so_name in ignores: 328 | continue 329 | if not soname2depname.has_key(so_name): 330 | continue 331 | 332 | ## TODO: correct? 333 | so_deps = soname2depname[so_name] 334 | if not ("libbinder.so" in so_deps): 335 | continue 336 | 337 | print so_name 338 | so_path = soname2sopath[so_name] 339 | ll_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".ll" 340 | assert os.path.exists(ll_path) 341 | command = ("llvm-link --only-needed %s" % (ll_path)) 342 | for so_dep in so_deps: 343 | if so_dep in ignores: 344 | print " [IGNORE] " + so_dep 345 | continue 346 | print " " + so_dep 347 | so_dep_path = soname2sopath[so_dep] 348 | ll_dep_path = so_dep_path.replace(aosp_out_dir, bitcode_dir) + ".ll" 349 | if os.path.exists(ll_dep_path): 350 | command = "%s %s" % (command, ll_dep_path) 351 | link_output = so_path.replace(aosp_out_dir, bitcode_dir) + ".lnk" 352 | command = "%s -o %s" % (command, link_output) 353 | print command 354 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 355 | for line in p.stdout.readlines(): 356 | line = line.strip() 357 | print line 358 | 359 | exit() 360 | ''' 361 | 362 | ''' 363 | ## STEP-2 (III. llvm-link) 364 | binder_sonames = set() 365 | binder_sonames.add("libbinder.so") 366 | for so_name in soname2sopath.keys(): 367 | if not (so_name in soname2depname.keys()): 368 | continue 369 | so_deps = soname2depname[so_name] 370 | if "libbinder.so" in so_deps: 371 | binder_sonames.add(so_name) 372 | 373 | for so_name in soname2sopath.keys(): 374 | if len(so_tests) > 0 and not (so_name in so_tests): 375 | continue 376 | 377 | if not (so_name in soname2depname.keys()): 378 | continue 379 | so_deps = soname2depname[so_name] 380 | if not ("libbinder.so" in so_deps): 381 | continue 382 | 383 | print so_name 384 | so_path = soname2sopath[so_name] 385 | ll_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".ll" 386 | assert os.path.exists(ll_path) 387 | command = ("llvm-link --only-needed %s" % (ll_path)) 388 | for so_dep in so_deps: 389 | if not (so_dep in binder_sonames) and not (so_dep == "libutils.so"): 390 | print " [IGNORE] " + so_dep 391 | continue 392 | #if so_dep == "libandroid_runtime.so": 393 | #print " [IGNORE] " + so_dep 394 | #continue 395 | if so_dep == "libhwui.so": 396 | print " [IGNORE] " + so_dep 397 | continue 398 | ## test -->> 399 | # if so_dep == "libbinder.so": 400 | # print " [IGNORE] " + so_dep 401 | # continue 402 | ## test <<-- 403 | print " " + so_dep 404 | so_dep_path = soname2sopath[so_dep] 405 | ll_dep_path = so_dep_path.replace(aosp_out_dir, bitcode_dir) + ".ll" 406 | if os.path.exists(ll_dep_path): 407 | command = "%s %s" % (command, ll_dep_path) 408 | link_output = so_path.replace(aosp_out_dir, bitcode_dir) + ".lnk" 409 | command = "%s -o %s" % (command, link_output) 410 | print command 411 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 412 | for line in p.stdout.readlines(): 413 | line = line.strip() 414 | print line 415 | 416 | exit() 417 | ''' 418 | 419 | ''' 420 | ## STEP-2 (IV. opt) 421 | for so_name in soname2sopath.keys(): 422 | if len(so_tests) > 0 and not (so_name in so_tests): 423 | continue 424 | 425 | so_path = soname2sopath[so_name] 426 | link_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".lnk" 427 | if not os.path.exists(link_path): 428 | continue 429 | print link_path 430 | opt_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".opt" 431 | 432 | command = "opt --mem2reg %s -o %s" % (link_path, opt_path) 433 | print command 434 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 435 | for line in p.stdout.readlines(): 436 | line = line.strip() 437 | print line 438 | 439 | exit() 440 | ''' 441 | 442 | ''' 443 | ## STEP-2 (V. llvm-dis) 444 | for so_name in soname2sopath.keys(): 445 | if len(so_tests) > 0 and not (so_name in so_tests): 446 | continue 447 | 448 | so_path = soname2sopath[so_name] 449 | opt_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".opt" 450 | if not os.path.exists(opt_path): 451 | continue 452 | print opt_path 453 | dis_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".dis" 454 | 455 | command = "llvm-dis %s -o=%s" % (opt_path, dis_path) 456 | print command 457 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 458 | for line in p.stdout.readlines(): 459 | line = line.strip() 460 | print line 461 | 462 | exit() 463 | ''' 464 | 465 | #### ==== #### ==== #### 466 | 467 | #''' 468 | ## STEP-3 469 | OOM_list = [ "libhwui.so" ] 470 | for so_name in soname2sopath.keys(): 471 | if len(so_tests) > 0 and not (so_name in so_tests): 472 | continue 473 | 474 | # if not ("surfaceflinger" in so_name): 475 | # continue 476 | # if not ("libsurfaceflinger.so" in so_name): 477 | # continue 478 | # if not ("libgui.so" in so_name): 479 | # continue 480 | # if not ("libinputflinger.so" in so_name): 481 | # continue 482 | 483 | # if not ("libaudiopolicyservice.so" in so_name): 484 | # continue 485 | # if not ("gpuservice" in so_name): 486 | # continue 487 | 488 | # if not ("audioserver" in so_name): 489 | # continue 490 | 491 | if so_name in OOM_list: 492 | continue 493 | 494 | so_path = soname2sopath[so_name] 495 | dis_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".dis" 496 | if not os.path.exists(dis_path): 497 | continue 498 | cg_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".cg" 499 | svfg_path = so_path.replace(aosp_out_dir, bitcode_dir) + ".svfg" 500 | if os.path.exists(cg_path) and os.path.exists(svfg_path): 501 | continue 502 | dis_size = round(os.path.getsize(dis_path) / float(1024 * 1024), 2) 503 | # if dis_size >= 36.0: 504 | # continue 505 | print "%s, %f" % (dis_path, dis_size) 506 | 507 | # command = "wpa --fspta --vcall-cha --dump-callgraph --dump-svfg %s" % (dis_path) # memory-consuming 508 | command = "wpa --ander --svfg --vcall-cha --dump-callgraph --dump-icfg --dump-inst --dump-svfg %s" % (dis_path) 509 | # command = "wpa --ander --svfg --v-call-cha --dump-callgraph --dump-icfg --dump-vfg %s" % (dis_path) 510 | # print command 511 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 512 | for line in p.stdout.readlines(): 513 | line = line.strip() 514 | print line 515 | if "Aborted (core dumped)" in line: 516 | print "[ERROR] [ERROR] [ERROR] " + so_name 517 | command = "sync" 518 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 519 | for line in p.stdout.readlines(): 520 | line = line.strip() 521 | print line 522 | 523 | ## move the generated callgraph_final.dot, svfg_final.dot, icfg_final.dot 524 | cg_src = os.path.join(os.path.dirname(os.path.abspath(__file__)), "callgraph_final.dot") 525 | cg_tgt = so_path.replace(aosp_out_dir, bitcode_dir) + ".cg" 526 | if os.path.exists(cg_src): 527 | shutil.copy(cg_src, cg_tgt) 528 | svfg_src = os.path.join(os.path.dirname(os.path.abspath(__file__)), "svfg_final.dot") 529 | svfg_tgt = so_path.replace(aosp_out_dir, bitcode_dir) + ".svfg" 530 | if os.path.exists(svfg_src): 531 | shutil.copy(svfg_src, svfg_tgt) 532 | icfg_src = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icfg_final.dot") 533 | icfg_tgt = so_path.replace(aosp_out_dir, bitcode_dir) + ".icfg" 534 | if os.path.exists(icfg_src): 535 | shutil.copy(icfg_src, icfg_tgt) 536 | 537 | command = "sync" 538 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 539 | for line in p.stdout.readlines(): 540 | line = line.strip() 541 | print line 542 | command = "rm callgraph_*" 543 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 544 | for line in p.stdout.readlines(): 545 | line = line.strip() 546 | print line 547 | command = "rm svfg_*" 548 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 549 | for line in p.stdout.readlines(): 550 | line = line.strip() 551 | print line 552 | command = "rm icfg_*" 553 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 554 | for line in p.stdout.readlines(): 555 | line = line.strip() 556 | print line 557 | command = "sync" 558 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 559 | for line in p.stdout.readlines(): 560 | line = line.strip() 561 | print line 562 | 563 | # break 564 | 565 | exit() 566 | #''' 567 | 568 | -------------------------------------------------------------------------------- /patch_framework/merge.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | #log_dir = "/home/zhouhao/data_fast/android_0805" 4 | #log_output = "/home/zhouhao/data_fast/SECURITY-2021/patch_framework/aosp_0805.log" 5 | 6 | #log_dir = "/home/zhouhao/data_fast/android_1005" 7 | #log_output = "/home/zhouhao/data_fast/SECURITY-2021/patch_framework/aosp_1005.log" 8 | 9 | log_dir = "/home/zhouhao/AOSP/aosp_11_0805" 10 | log_output = "/home/zhouhao/AOSP/NDSS21/patch_framework/aosp_0805.log" 11 | 12 | out_fp = open(log_output, 'w') 13 | 14 | log_list = os.listdir(log_dir) 15 | log_list.sort() 16 | #print flist 17 | 18 | for log_name in log_list: 19 | if not log_name.startswith("log"): 20 | continue 21 | print log_name 22 | 23 | log_path = os.path.join(log_dir, log_name) 24 | assert os.path.exists(log_path) 25 | 26 | in_fp = open(log_path, 'r') 27 | for line in in_fp.readlines(): 28 | out_fp.write(line) 29 | pass 30 | in_fp.close() 31 | 32 | out_fp.flush() 33 | out_fp.close() 34 | 35 | -------------------------------------------------------------------------------- /patch_framework/patch.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import sys 4 | 5 | # aosp_dir = "/home/zhouhao/data_fast/android_0805" 6 | # log_path = "/home/zhouhao/data_fast/SECURITY-2021/patch_framework/aosp_0805.log" 7 | 8 | # aosp_dir = "/home/zhouhao/data_fast/android_1005" 9 | # log_path = "/home/zhouhao/data_fast/SECURITY-2021/patch_framework/aosp_1005.log" 10 | 11 | # aosp_dir = "/home/zhouhao/AOSP/aosp_11_0805" 12 | # log_path = "/home/zhouhao/AOSP/NDSS21/patch_framework/aosp_0805.log" 13 | 14 | aosp_dir = "/home/zhouhao/AOSP/aosp_12_0905" 15 | log_path = "/home/zhouhao/AOSP/NDSS21/patch_framework/aosp_0905.log" 16 | 17 | # STEP-1 18 | step1_error = False 19 | 20 | dir2so = {} 21 | fp1 = open(log_path, "r") 22 | for line in fp1.readlines(): 23 | line = line.strip() 24 | if (" link " in line) and (".so" in line): 25 | #if (("[x86" in line) or ("x86]" in line)) or (("[linux_glibc" in line) or ("linux_glibc]" in line)): 26 | if ("[linux_glibc" in line) or ("linux_glibc]" in line): 27 | continue 28 | # print line 29 | src = line.split("]")[1].split(" ")[1] 30 | tgt = line.split("]")[1].split(" ")[3] 31 | # print " %s ==>> %s" % (src, tgt) 32 | 33 | if dir2so.has_key(src): 34 | if tgt == dir2so[src]: 35 | pass 36 | else: 37 | print " pre:%s, cur:%s" % (dir2so[src], tgt) 38 | step1_error = True 39 | else: 40 | dir2so[src] = tgt 41 | fp1.close() 42 | 43 | if step1_error: 44 | #sys.exit(1) 45 | pass 46 | 47 | ''' 48 | for src in dir2so.keys(): 49 | tgt = dir2so[src] 50 | print "%s ==>> %s" % (src, tgt) 51 | ''' 52 | 53 | # STEP-2 54 | so2src = {} 55 | so2head = {} 56 | fp2 = open(log_path, "r") 57 | fp2_lines = fp2.readlines() 58 | line_idx = -1 59 | for line in fp2_lines: 60 | line_idx += 1 # <- current line index 61 | line = line.strip() 62 | if (("clang " in line) or ("clang++ " in line)) and ("[" in line) and ("]" in line) and ("%" in line): 63 | #if (("[x86" in line) or ("x86]" in line)) or (("[linux_glibc" in line) or ("linux_glibc]" in line)): 64 | if ("[linux_glibc" in line) or ("linux_glibc]" in line): 65 | continue 66 | # print line 67 | src = line.split("]")[1].split(" ")[1] 68 | # tgt = line.split("]")[1].split(" ")[3] 69 | if not dir2so.has_key(src): 70 | continue 71 | so = dir2so[src] 72 | # print " %s ==>> %s" % (src, so) 73 | 74 | if not so2src.has_key(so): 75 | so2src[so] = [] 76 | if not so2head.has_key(so): 77 | so2head[so] = [] 78 | 79 | line = fp2_lines[line_idx + 1].strip() 80 | assert line.startswith("INFO::") 81 | 82 | tgt = line.split(" ")[-1][:-1] 83 | 84 | src_path = os.path.join(aosp_dir, tgt) 85 | #if not os.path.exists(src_path): ## patch 86 | #src_path = src_path.replace("x86_64", "x86") ## patch 87 | assert os.path.exists(src_path) 88 | # print " %s ==>> %s" % (so, src_path) 89 | if not (src_path in so2src[so]): 90 | so2src[so].append(src_path) 91 | 92 | for item in line.split(" "): 93 | if item.startswith("["): 94 | item = item[1:] 95 | if item.endswith("]"): 96 | item = item[:-1] 97 | if item.startswith("-I"): 98 | head_dir = os.path.join(aosp_dir, item[2:]) 99 | if os.path.exists(head_dir): 100 | for root, _, files in os.walk(head_dir): 101 | for head_file in files: 102 | if not head_file.endswith(".h"): 103 | continue 104 | 105 | head_path = os.path.join(root, head_file).replace("/./", "/") 106 | if os.path.exists(head_path): 107 | if not (head_path in so2head[so]): 108 | so2head[so].append(head_path) 109 | else: 110 | pass 111 | fp2.close() 112 | 113 | ''' 114 | for so in so2src.keys(): 115 | print so 116 | for src in so2src[so]: 117 | print " %s" % (src) 118 | for head in so2head[so]: 119 | print " %s" % (head) 120 | ''' 121 | 122 | # STEP-3 123 | candidate_heads = [] 124 | for so in so2src.keys(): 125 | for src in so2src[so]: 126 | if not (src in candidate_heads): 127 | candidate_heads.append(src) 128 | for head in so2head[so]: 129 | if not (head in candidate_heads): 130 | candidate_heads.append(head) 131 | 132 | heads = [] 133 | pass_heads = [] 134 | for head in candidate_heads: 135 | if head in pass_heads: 136 | continue 137 | 138 | flag_cnt = 0 139 | fp_head = open(head, "r") 140 | for head_line in fp_head: 141 | head_line = head_line.strip() 142 | if "#include " in head_line: 143 | flag_cnt += 1 144 | if (not ("class IInterface" in head_line)) and ("class " in head_line) and ("IInterface" in head_line): 145 | flag_cnt += 1 146 | fp_head.close() 147 | 148 | pass_heads.append(head) 149 | if flag_cnt >= 2: 150 | heads.append(head) 151 | heads.sort() 152 | 153 | ''' 154 | for head in heads: 155 | print head 156 | sys.exit(1) 157 | ''' 158 | 159 | ''' 160 | # STEP-4 (backup or recover) 161 | head_idx = 0 162 | for head in heads: 163 | if "_backup." in head: # for backup 164 | # if not ("_backup." in head): # for recover 165 | continue 166 | 167 | head_idx += 1 168 | 169 | print "%d: %s" % (head_idx, head) 170 | assert head.endswith(".h") 171 | 172 | shutil.copy(head, (head[:-2] + "_backup.h")) # for backup 173 | # shutil.copy(head, head.replace("_backup.h", ".h")) # for recover 174 | exit() 175 | ''' 176 | 177 | #''' 178 | # STEP-4 179 | head_idx = 0 180 | for head in heads: 181 | if "_backup." in head: 182 | continue 183 | if os.path.islink(head): 184 | continue 185 | 186 | head_idx += 1 187 | print "%d: %s" % (head_idx, head) 188 | 189 | fp_head = open(head, "r") 190 | head_lines = fp_head.readlines() 191 | fp_head.close() 192 | 193 | fp_head = open(head, "w") 194 | insert_idx = -1 195 | line_idx = -1 196 | cnt = 0 197 | for line in head_lines: 198 | line_idx += 1 199 | fp_head.write(line) 200 | if insert_idx == line_idx: 201 | insert_stmt = "/* polyu */ int polyuIdentifier[%d];\n" % (head_idx * 7 + cnt) 202 | cnt += 1 203 | print " %d: %s" % (head_idx, insert_stmt.strip()) 204 | fp_head.write(insert_stmt) 205 | insert_idx = -1 206 | line = line.strip() 207 | if ("class " in line) and ("IInterface" in line): 208 | if "public:" in head_lines[line_idx + 1].strip(): 209 | insert_idx = line_idx + 1 210 | elif "public:" in head_lines[line_idx + 2].strip(): 211 | insert_idx = line_idx + 2 212 | elif "public:" in head_lines[line_idx + 3].strip(): 213 | insert_idx = line_idx + 3 214 | elif "public:" in head_lines[line_idx + 4].strip(): 215 | insert_idx = line_idx + 4 216 | elif "public:" in head_lines[line_idx + 5].strip(): 217 | insert_idx = line_idx + 5 218 | elif "public:" in head_lines[line_idx + 6].strip(): 219 | insert_idx = line_idx + 6 220 | elif "public:" in head_lines[line_idx + 7].strip(): 221 | insert_idx = line_idx + 7 222 | elif "public:" in head_lines[line_idx + 8].strip(): 223 | insert_idx = line_idx + 8 224 | elif "public:" in head_lines[line_idx + 9].strip(): 225 | insert_idx = line_idx + 9 226 | elif "public:" in head_lines[line_idx + 10].strip(): 227 | insert_idx = line_idx + 10 228 | else: 229 | assert False 230 | fp_head.flush() 231 | fp_head.close() 232 | #''' 233 | 234 | ## NOTE: IApInterface.h, IClientInterface.h, IWifiScannerImpl.h 235 | 236 | 237 | --------------------------------------------------------------------------------