自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android漏洞掃描工具Code Arbiter

企業(yè)動態(tài)
目前Android應(yīng)用代碼漏洞掃描工具種類繁多,效果良莠不齊,這些工具有一個共同的特點,都是在應(yīng)用打包完成后對應(yīng)用進行解包掃描。這種掃描有非常明顯的缺點,掃描周期較長,不能向開發(fā)者實時反饋代碼中存在的安全問題,并且對于問題代碼的定位需要手動搜索匹配源碼,這樣就更不利于開發(fā)者對問題代碼進行及時的修改。

[[201139]]

目前Android應(yīng)用代碼漏洞掃描工具種類繁多,效果良莠不齊,這些工具有一個共同的特點,都是在應(yīng)用打包完成后對應(yīng)用進行解包掃描。這種掃描有非常明顯的缺點,掃描周期較長,不能向開發(fā)者實時反饋代碼中存在的安全問題,并且對于問題代碼的定位需要手動搜索匹配源碼,這樣就更不利于開發(fā)者對問題代碼進行及時的修改。Code Arbiter正是為解決上述兩個問題而開發(fā)的,專門對Android Studio中的源碼進行安全掃描。

1 背景介紹

為實現(xiàn)對Android Studio中的源碼進行掃描,最方便的方式便是將掃描工具以IDE插件的形式進行工作。此時一個很自然的想法便是從頭構(gòu)建一個Android Studio插件,但是進行仔細(xì)的評估后會發(fā)現(xiàn),這樣做難度并不小:

工作量大,許多知識需要學(xué)習(xí),如IDE開放API接口、插件UI構(gòu)建等,同時許多底層模塊需要從頭構(gòu)建;

插件的穩(wěn)定性、檢測問題的準(zhǔn)確性上都不一定能夠達到已有開源工具的效果。

因此我們轉(zhuǎn)而考慮在已有漏洞檢測插件的基礎(chǔ)上進行擴展,以滿足需求。經(jīng)過調(diào)研,最終入圍的兩款檢測插件是PMD和FindBugs,其中PMD是對Java源碼進行掃描,而FindBugs則是對Java源碼編譯后的class文件進行掃描??紤]到可擴展性及檢測的準(zhǔn)確性,最終選定了FindBugs。FindBugs是一個靜態(tài)分析工具,它檢查類或者JAR文件,將字節(jié)碼與一組缺陷模式進行對比來發(fā)現(xiàn)可能的問題,可以以獨立的JAR包形式運行,也可以作為集成開發(fā)工具的插件形式存在。

擴展優(yōu)化

那么,怎么擴展FindBugs呢?調(diào)研發(fā)現(xiàn)FindBugs插件具有著極強的可擴展性,只需要將擴展的JAR包導(dǎo)入FindBugs插件,重啟,即可完成相關(guān)功能的擴展。安裝JAR包示意圖如下所示。

 

下面的問題是如何構(gòu)建可安裝的JAR包。繼續(xù)調(diào)研,發(fā)現(xiàn)FindBugs有一款專門對安全問題進行檢測的擴展插件Find Security Bugs,該插件主要用于對Web安全問題進行檢測,也有極少對Android相關(guān)安全問題的檢測規(guī)則??紤]以下幾個原因,需要對該插件的源碼進行重構(gòu)。

對Android安全問題的檢測太少,只包含外部文件使用、Webview、Broadcast使用等寥寥幾項;

檢測的細(xì)粒度上考慮不夠完全,會造成大量的誤報,無法滿足檢測精度的要求;

檢測問題的上報只支持英文模式,且問題展示的邏輯性不夠嚴(yán)謹(jǐn),不便于開發(fā)者進行問題排查。

基于以上三個原因,我們需要對Find Security Bugs的源碼進行重寫、優(yōu)化,通過增加檢測項來檢測盡可能多的安全問題,通過優(yōu)化檢測規(guī)則來減少檢測的誤報,問題展示使用中文進行描述,同時優(yōu)化問題描述的邏輯性,使得開發(fā)者能夠更易理解并修改相關(guān)問題,至此插件實現(xiàn)及優(yōu)化的方案確定。

2 工具實現(xiàn)介紹

FindBugs檢測的是class文件,因此當(dāng)待檢測的源碼未生成編譯文件時,F(xiàn)indBugs會先將源碼編譯生成.class文件,然后對這個class文件進行分析。FindBugs會完成對class文件的自動建模,在此模型的基礎(chǔ)上對代碼進行分析。按照在實際編寫檢測代碼過程中的總結(jié),把檢測的實現(xiàn)方式分成四種方式,下面分別進行介紹。

2.1 逐行檢查

逐行檢查主要是針對代碼中使用的一些不安全方法或參數(shù)進行檢測,其實現(xiàn)方式是重寫sawOpcode()方法,下面以Android中使用外部存儲問題作為示例進行講解。

Android中獲取外部存儲文件夾地址的方法主要包括下面這些方法:

  1. getExternalCacheDir() 
  2. getExternalCacheDirs() 
  3. getExternalFilesDir() 
  4. getExternalFilesDirs() 
  5. getExternalMediaDirs() 
  6. Environment.getExternalStorageDirectory() 
  7. Environment.getExternalStoragePublicDirectory() 

檢測的方式便是,如果發(fā)現(xiàn)存在該方法的調(diào)用,則作為一個問題進行上報,實現(xiàn)完整代碼如下所示:

  1. public class ExternalFileAccessDetector extends OpcodeStackDetector { 
  2.  
  3.     private static final String ANDROID_EXTERNAL_FILE_ACCESS_TYPE = "ANDROID_EXTERNAL_FILE_ACCESS"
  4.     private BugReporter bugReporter; 
  5.     public ExternalFileAccessDetector(BugReporter bugReporter) { 
  6.         this.bugReporter = bugReporter; 
  7.     } 
  8.  
  9.     @Override 
  10.  public void sawOpcode(int seen) { 
  11.         //printOpCode(seen); 
  12.  if (seen == Constants.INVOKEVIRTUAL && ( 
  13.         getNameConstantOperand().equals("getExternalCacheDir") || 
  14.         getNameConstantOperand().equals("getExternalCacheDirs") || 
  15.         getNameConstantOperand().equals("getExternalFilesDir") || 
  16.         getNameConstantOperand().equals("getExternalFilesDirs") || 
  17.         getNameConstantOperand().equals("getExternalMediaDirs"
  18.             )) { 
  19. // System.out.println(getSigConstantOperand()); 
  20.  bugReporter.reportBug(new BugInstance(this, ANDROID_EXTERNAL_FILE_ACCESS_TYPE, Priorities.NORMAL_PRIORITY).addClass(this).addMethod(this).addSourceLine(this)); 
  21.         } 
  22.         else if(seen == Constants.INVOKESTATIC && getClassConstantOperand().equals("android/os/Environment") && (getNameConstantOperand().equals("getExternalStorageDirectory") || getNameConstantOperand().equals("getExternalStoragePublicDirectory"))) { 
  23.             bugReporter.reportBug(new BugInstance(this, ANDROID_EXTERNAL_FILE_ACCESS_TYPE, Priorities.NORMAL_PRIORITY).addClass(this).addMethod(this).addSourceLine(this)); 
  24.         } 
  25.     } 

該類的實現(xiàn)是繼承OpcodeStackDetector類,是FindBugs中的一個抽象類,封裝了對于獲取代碼特定參數(shù)的方法調(diào)用。sawOpcode方法參數(shù)可以理解為待檢測代碼行的行號,通過printOpCode(seen)可以打印該代碼行的具體內(nèi)容。Constants.INVOKEVIRTUAL表示該行調(diào)用類的實例方法,Constants.INVOKESTATIC表示調(diào)用類的靜態(tài)方法。getNameConstantOperand方法表示獲取被調(diào)用方法的名稱,getClassConstantOperand方法表示獲取調(diào)用類的名稱,getSigConstantOperand方法表示獲取方法的所有參數(shù)。bugReporter.reportBug用于上報檢測到的漏洞信息,其中BugInstance的三個參數(shù)分別表示:檢測器、漏洞類型、漏洞等級,其中漏洞等級分為五個級別,如下表所示:

addClass、addMethod、addSourceLine用于指定該漏洞所在的類、方法、行,方便報告漏洞時定位關(guān)鍵代碼。

2.2 逐方法檢查

逐方法檢查首先獲取待檢測類的所有內(nèi)容,然后對類中的方法進行逐個檢查,多用于對方法體進行檢測,其實現(xiàn)的方法主要是通過重寫visitClassContext方法,下面以對Android TrustManager的空實現(xiàn)的檢測為例進行說明。

TrustManager的空實現(xiàn),主要是指對于檢測Server端證書是否可信的方法checkServerTrusted,是否是空實現(xiàn)。下面展示問題代碼,如果是空實現(xiàn)那么將導(dǎo)致客戶端接收任意證書,從而造成加密后的HTTPS消息被中間人解密。

  1. @Override 
  2. public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { 
  3.  

檢測的方式是通過遍歷類中的所有方法,找到checkServerTrusted方法,對方法整體進行檢測,確定其是否為空實現(xiàn),部分代碼如下所示:

  1. public class WeakTrustManagerDetector implements Detector { 
  2. ... 
  3. public WeakTrustManagerDetector(BugReporter bugReporter) { 
  4.         this.bugReporter = bugReporter; 
  5.     } 
  6.  
  7.     @Override 
  8.  public void visitClassContext(ClassContext classContext) { 
  9.         JavaClass javaClass = classContext.getJavaClass(); 
  10.  
  11.         //The class extends X509TrustManager 
  12.   boolean isTrustManager = InterfaceUtils.isSubtype(javaClass,"javax.net.ssl.X509TrustManager"); 
  13.         boolean isHostnameVerifier = InterfaceUtils.isSubtype(javaClass,"javax.net.ssl.HostnameVerifier"); 
  14.  
  15. // if (!isTrustManager && !isHostnameVerifier) return
  16.  if (!isTrustManager && !isHostnameVerifier){ 
  17.             for (Method m : javaClass.getMethods()) { 
  18.                 allow_All_Hostname_Verify(classContext, javaClass, m); 
  19.             } 
  20.         } 
  21.  
  22.         Method[] methodList = javaClass.getMethods(); 
  23.  
  24.         for (Method m : methodList) { 
  25.             MethodGen methodGen = classContext.getMethodGen(m); 
  26.  
  27.             if (DEBUG) System.out.println(">>> Method: " + m.getName()); 
  28.  
  29.             if (isTrustManager && 
  30.                     (m.getName().equals("checkClientTrusted") || 
  31.                      m.getName().equals("checkServerTrusted") || 
  32.                      m.getName().equals("getAcceptedIssuers"))) { 
  33.                 if(isEmptyImplementation(methodGen)) { 
  34.                     bugReporter.reportBug(new BugInstance(this, WEAK_TRUST_MANAGER_TYPE, Priorities.NORMAL_PRIORITY).addClassAndMethod(javaClass, m)); 
  35.                 } 
  36. ...... 

classContext.getJavaClass用于獲取整個類的所有內(nèi)容;javaClass.getMethods用于獲取該類中的所有方法,以一個方法列表的形式返回;classContext.getMethodGen用于獲取該方法的內(nèi)容;isEmptyImplementation將方法的內(nèi)容導(dǎo)入該函數(shù)進行檢測,用于確定方法是否是空實現(xiàn),該方法的代碼如下所示:

  1. private boolean isEmptyImplementation(MethodGen methodGen){ 
  2.     boolean invokeInst = false
  3.     boolean loadField = false
  4.  
  5.     for (Iterator itIns = methodGen.getInstructionList().iterator();itIns.hasNext();) { 
  6.         Instruction inst = ((InstructionHandle) itIns.next()).getInstruction(); 
  7.         if (DEBUG) 
  8.             System.out.println(inst.toString(true)); 
  9.  
  10.         if (inst instanceof InvokeInstruction) { 
  11.             invokeInst = true
  12.         } 
  13.         if (inst instanceof GETFIELD) { 
  14.             loadField = true
  15.         } 
  16.     } 
  17.     return !invokeInst && !loadField; 

該方法主要用于檢測方法中是否包含方法調(diào)用、域操作,如果沒有包含則認(rèn)為是一個空實現(xiàn)的方法。因此該方法對于只包含 return true/false 語句的方法體同樣認(rèn)為是一個空實現(xiàn)。

2.3 污點分析

數(shù)據(jù)流分析主要用于分析特定方法加載的參數(shù)是否能夠被用戶控制,即進行污點分析。做污點分析首先需要定義污染源(source點),污染源可以理解為能夠被用戶控制的輸入數(shù)據(jù),這里定義的Android污染源主要包括用戶的輸入、Intent傳入的數(shù)據(jù),下面展示定義的部分污染源(source點):

  1. - EditText 
  2. android/widget/EditText.getText()Landroid/text/Editable;:TAINTED 
  3. - Intent 
  4. android/content/Intent.getAction()Ljava/lang/String;:TAINTED 
  5. android/content/Intent.getStringExtra(Ljava/lang/String;)Ljava/lang/String;:TAINTED 
  6. ...... 
  7. - Bundle 
  8. android/os/Bundle.get(Ljava/lang/String;)Ljava/lang/Object;:TAINTED 
  9. android/os/Bundle.getString(Ljava/lang/String;)Ljava/lang/String;:TAINTED 
  10. ...... 

定義好污染源后就需要確定污染的觸發(fā)點(sink點),可以理解為會觸發(fā)危險操作的函數(shù)。定義sink點的方式有兩種,一種是直接從文件中導(dǎo)入,以命令注入為示例,代碼如下:

  1. public class CommandInjectionDetector extends BasicInjectionDetector { 
  2.  
  3.     public CommandInjectionDetector(BugReporter bugReporter) { 
  4.         super(bugReporter); 
  5.         loadConfiguredSinks("command.txt""COMMAND_INJECTION"); 
  6.  } 

從代碼中可以清楚的看到其導(dǎo)入方式是繼承BasicInjectionDetector類,然后再該類的構(gòu)造方法中通過loadConfiguredSinks方法,導(dǎo)入包含sink點的文件,下面展示該示例文件中的內(nèi)容:

  1. java/lang/Runtime.exec(Ljava/lang/String;)Ljava/lang/Process;:0 
  2. java/lang/Runtime.exec([Ljava/lang/String;)Ljava/lang/Process;:0 
  3. java/lang/Runtime.exec(Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Process;:0,1 
  4. java/lang/Runtime.exec([Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Process;:0,1 
  5. java/lang/Runtime.exec(Ljava/lang/String;[Ljava/lang/String;Ljava/io/File;)Ljava/lang/Process;:1,2 
  6. java/lang/Runtime.exec([Ljava/lang/String;[Ljava/lang/String;Ljava/io/File;)Ljava/lang/Process;:1,2 
  7. java/lang/ProcessBuilder.<init>([Ljava/lang/String;)V:0 
  8. java/lang/ProcessBuilder.<init>(Ljava/util/List;)V:0 
  9. java/lang/ProcessBuilder.command([Ljava/lang/String;)Ljava/lang/ProcessBuilder;:0 
  10. java/lang/ProcessBuilder.command(Ljava/util/List;)Ljava/lang/ProcessBuilder;:0 
  11. dalvik/system/DexClassLoader.loadClass(Ljava/lang/String;)Ljava/lang/Class;:0 

另一種是自定義導(dǎo)入,其實現(xiàn)是通過覆蓋BasicInjectionDetector類中的getInjectionPoint方法,以WebView.loadurl方法為例,示例代碼如下所示:

  1. @Override 
  2.  protected InjectionPoint getInjectionPoint(InvokeInstruction invoke, ConstantPoolGen cpg, InstructionHandle handle) { 
  3.         assert invoke != null && cpg != null
  4.         String method = invoke.getMethodName(cpg); 
  5.         String sig    = invoke.getSignature(cpg); 
  6. // System.out.println(invoke.getClassName(cpg)); 
  7.  if(sig.contains("Ljava/lang/String;")) { 
  8.             if("loadUrl".equals(method)){ 
  9.                 if(sig.contains("Ljava/util/Map;")){ 
  10.                     return new InjectionPoint(new int[]{1}, WEBVIEW_LOAD_DATA_URL_TYPE); 
  11.                 }else
  12.                     return new InjectionPoint(new int[]{0}, WEBVIEW_LOAD_DATA_URL_TYPE); 
  13.                 } 
  14.             }else if("loadData".equals(method)){ 
  15.                 return new InjectionPoint(new int[]{2}, WEBVIEW_LOAD_DATA_URL_TYPE); 
  16.             }else if("loadDataWithBaseURL".equals(method)){ 
  17.                 //BUG 
  18.  return new InjectionPoint(new int[]{4}, WEBVIEW_LOAD_DATA_URL_TYPE); 
  19.             } 
  20.         } 
  21.         return InjectionPoint.NONE; 
  22.     } 

通過實例化InjectionPoint類構(gòu)造新的sink點,其構(gòu)造方法中的第一個參數(shù)表示該方法接收污染數(shù)據(jù)參數(shù)的位置,如方法為webView.loadUrl(url),其第一個參數(shù)就是new int[]{0},其它的以此類推。

上報發(fā)現(xiàn)漏洞的情況,則通過覆蓋getPriorityFromTaintFrame方法的實現(xiàn),示例代碼如下所示:

  1. @Override 
  2.  protected int getPriorityFromTaintFrame(TaintFrame fact, int offset) 
  3.             throws DataflowAnalysisException { 
  4.         Taint stringValue = fact.getStackValue(offset); 
  5. // System.out.println(stringValue.getConstantValue()); 
  6.  if (stringValue.isTainted() || stringValue.isUnknown()) { 
  7.             return Priorities.NORMAL_PRIORITY; 
  8.         } else { 
  9.             return Priorities.IGNORE_PRIORITY; 
  10.         } 
  11.     } 

通過fact.getStackValue獲取檢測的函數(shù)變量,如果該變量被污染(isTainted)或 變量是否被污染未知(但是是可控制變量),那么作為一個中危風(fēng)險(Priorities.NORMAL_PRIORITY)進行上報,其它的情況則上報為可忽略風(fēng)險(Priorities.IGNORE_PRIORITY)。

2.4 自定義代碼檢測

自定義代碼檢測實現(xiàn)的前半部分同2.2的逐方法檢測類似,均是獲取類的內(nèi)容,然后遍歷所有的方法,對方法的內(nèi)容進行檢測,但是在具體代碼檢測實現(xiàn)上是通過自定義分析進行。目前自定義檢測只應(yīng)用到Android中本地拒絕服務(wù)的檢測。本地拒絕服務(wù)的被觸發(fā)的重要原因在于對通過Intent獲取的參數(shù)未進行異常捕獲,因此檢測實現(xiàn)的方式便是檢測獲取參數(shù)的代碼行是否被try catch包裹(這個存在誤差,待改進)。對于其代碼分析,不能使用FindBugs模型進行分析,而是使用最原始的class代碼進行分析,原始class代碼的形式通過javap命令進行查看,下圖展示示例代碼。

 

對原始class文件進行分析存在的缺陷是無法定位具體的代碼行,那么在進行問題上報時無法將問題定位到代碼行,因此第一步需要在原有模型的基礎(chǔ)上對所有包含Intent獲取參數(shù)的方法的位置存儲到一個Map結(jié)構(gòu)中,方便后面對方法的定位,代碼實現(xiàn)如下所示,獲取方法所在的行,然后以方法名作為Key值,以代碼行相關(guān)信息作為Value值,存儲到Map中。

  1. private Map<String, List<Location>> get_line_location(Method m, ClassContext classContext){ 
  2.         HashMap<String, List<Location>> all_line_location = new HashMap<>(); 
  3.         ConstantPoolGen cpg = classContext.getConstantPoolGen(); 
  4.         CFG cfg = null
  5.         try { 
  6.             cfg = classContext.getCFG(m); 
  7.         } catch (CFGBuilderException e) { 
  8.             e.printStackTrace(); 
  9.             return all_line_location; 
  10.         } 
  11.         for (Iterator<Location> i = cfg.locationIterator(); i.hasNext(); ) { 
  12.             Location loc = i.next(); 
  13.             Instruction inst = loc.getHandle().getInstruction(); 
  14.             if(inst instanceof INVOKEVIRTUAL) { 
  15.                 INVOKEVIRTUAL invoke = (INVOKEVIRTUAL) inst; 
  16.  if(all_line_location.containsKey(invoke.getMethodName(cpg))){ 
  17.                         all_line_location.get(invoke.getMethodName(cpg)).add(loc); 
  18.                     }else { 
  19.                         LinkedList<Location> loc_list = new LinkedList<>(); 
  20.                         loc_list.add(loc); 
  21.                         all_line_location.put(invoke.getMethodName(cpg), loc_list); 
  22.                     } 
  23. // } 
  24.  } 
  25.         } 
  26.         return all_line_location; 
  27.     } 

之后獲取Exception包裹的范圍,F(xiàn)indBugs中包含對Exception的建模,因此能夠通過其模型能夠直接獲取其范圍并存儲到一個列表中,代碼如下所示,其中exceptionTable[i].getStartPC用于獲取try catch 的起始代碼行,exceptionTable[i].getEndPC用于獲取try catch 的結(jié)束代碼行。

  1. public int[] getExceptionScope(){ 
  2.         try { 
  3.             CodeException[] exceptionTable = this.code.getExceptionTable(); 
  4.             int[] exception_scop = new int[exceptionTable.length * 2]; 
  5.             for (int i = 0; i < exceptionTable.length; i++) { 
  6.                 exception_scop[i * 2] = exceptionTable[i].getStartPC(); 
  7.                 exception_scop[i * 2 + 1] = exceptionTable[i].getEndPC(); 
  8.             } 
  9.             return exception_scop; 
  10.         }catch (Exception e){ 
  11.  } 
  12.         return new int[0]; 
  13.     } 

在對代碼進行逐行檢查時,因為使用的是最原始class文件形式,因此需要限定其遍歷的范圍,限定的方式是通過代碼的行號,即上圖中每行代碼的第一個數(shù)值。首先需要獲取代碼總行數(shù)的大小,獲取的方式便是解析FindBugs建模后的第一行代碼,找到關(guān)鍵詞code-length后面的數(shù)值,即為代碼的行數(shù),解析代碼如下所示:

  1. public int get_Code_Length(String firstLineCode){ 
  2.         try{ 
  3.             String[] split1 = firstLineCode.split("code_length"); 
  4. // System.out.println(split1[split1.length-1]); 
  5.  byte[] code_length_bytes = split1[split1.length-1].getBytes(); 
  6.             byte[] new_code_bytes = new byte[code_length_bytes.length]; 
  7.             for(int i=0; i<code_length_bytes.length; i++){ 
  8. // System.out.println(); 
  9.  if(code_length_bytes[i]<48 || code_length_bytes[i]>57){ 
  10.                     new_code_bytes[i] = 32; 
  11.                 }else
  12.                     new_code_bytes[i] = code_length_bytes[i]; 
  13.                 } 
  14.             } 
  15.             return Integer.parseInt(new String(new_code_bytes).trim()); 
  16.         }catch(Exception e){ 
  17.             e.printStackTrace(); 
  18.         } 
  19.         return 0; 
  20.     } 

最后對代碼進行逐行遍歷,遍歷中為防止try catch塊被遍歷到,使用行號來限制遍歷的范圍。檢測代碼行是否包含通過Intent獲取參數(shù),及該行是否被try catch 包裹,如果上述兩個條件均被觸發(fā),那么就作為一個問題進行上報。示例代碼如下,其中g(shù)et_code_line_index方法用于獲取代碼的行號,獲取的方式是截取代碼行的首字符的數(shù)值,以確定是否在代碼包裹的范圍內(nèi)。

  1. private void analyzeMethod(JavaClass javaClass, Method m, ClassContext classContext) throws CFGBuilderException { 
  2.         HashMap<String, List<Location>> all_line_location = (HashMap<String, List<Location>>) get_line_location(m, classContext); 
  3.         Code code = m.getCode(); 
  4.         StringCodeAnalysis sca = new StringCodeAnalysis(code); 
  5.         String[] codes = sca.codes_String_Array(); 
  6.         int code_length = sca.get_Code_Length(sca.get_First_Code(codes)); 
  7.         int[] exception_scop = sca.getExceptionScope(); 
  8.         for(int i=1; i<codes.length; i++){ 
  9.             int line_index = sca.get_code_line_index(codes[i]); 
  10.             if (line_index < code_length){ 
  11.                 if(codes[i].toLowerCase().contains("invokevirtual") && 
  12.                         (codes[i].contains("android.content.Intent.get")  || codes[i].contains("android.os.Bundle.get"))){ 
  13.                     if(exception_scop.length == 0){ 
  14.                         ...... 
  15.                     }else
  16.                         boolean is_scope = false
  17.                         for(int j=0; j<exception_scop.length; j+=2){ 
  18.                             int start = exception_scop[j]; 
  19.                             int end = exception_scop[j+1]; 
  20.                             if(line_index >= start && line_index <= end){ 
  21.                                 is_scope = true
  22.                             } 
  23.                             if(is_scope){ 
  24.                                 break; 
  25.                             } 
  26.                         } 
  27.                         if(!is_scope){ 
  28.                             String method_name = get_method_name(codes[i]); 
  29.                             if(all_line_location.containsKey(method_name)){ 
  30.                                 for(Location loc : all_line_location.get(method_name)){ 
  31.                                     bugReporter.reportBug(new BugInstance(this, LOCAL_DENIAL_SERVICE_TYPE, Priorities.NORMAL_PRIORITY).addClass(javaClass).addMethod(javaClass, m).addSourceLine(classContext, m, loc)); 
  32.                                 } 
  33.                             }else { 
  34.                                 bugReporter.reportBug(new BugInstance(this, LOCAL_DENIAL_SERVICE_TYPE, Priorities.NORMAL_PRIORITY).addClass(javaClass).addMethod(javaClass, m)); 
  35.  } 
  36.                         } 
  37.                     } 
  38.                 } 
  39.             } 
  40.         } 
  41.     } 

3 注冊打包

上面詳細(xì)敘述了如何構(gòu)造自己的問題檢測代碼,完成檢測方法的書寫后,下一步就是在配置文件中對檢測方法進行注冊,才能使檢測代碼運轉(zhuǎn)起來。

需要在兩個文件中進行注冊,第一個是findbugs.xml,注冊示例如下:

  1. <Detector class="com.h3xstream.findsecbugs.android.LocalDenialOfServiceDetector" reports="LOCAL_DENIAL_SERVICE"/> 
  2. <BugPattern type="LOCAL_DENIAL_SERVICE" abbrev="SECLDOS" category="Android安全問題" cweid="276"/> 

其中Detector用于注冊該檢測方法的位置及其唯一標(biāo)識,BugPattern用于對檢測出的問題進行歸類,方便展示,如此處歸類到"Android安全問題"中,那么在生成報告的時候問題也將被歸類到"Android安全問題"中。

第二個是messages.xml注冊,注冊示例如下,該注冊主要是對該問題進行說明,包括問題的危害及修復(fù)方法。

  1. <Detector class="com.h3xstream.findsecbugs.android.LocalDenialOfServiceDetector">
<Details>Local Denial of Service.</Details>
</Detector>
<BugPattern type="LOCAL_DENIAL_SERVICE">
<ShortDescription>本地拒絕服務(wù)</ShortDescription>
<LongDescription>通過Intent接收的參數(shù)未進行異常捕獲,導(dǎo)致出現(xiàn)異常使得應(yīng)用崩潰</LongDescription>
<Details>
<![CDATA[
    <p>
        <b>危害:</b><br/>
        <pre>
            應(yīng)用崩潰無法使用,影響用戶體驗;
            被競爭對手利用,進行點對點攻擊。
        </pre>
    </p>
    <p>
        <b>錯誤代碼:</b><br/>
        <pre>
            bundle.getString(""); //未try/catch
            intent.getStringExtra(""); //未try/catch
        </pre>
    </p>
    <p>
        <b>解決方案:</b><br/>
        <pre>
            對通過Intent接收的參數(shù)處理時,進行嚴(yán)格的異常捕獲。
            try {
                bundle.getString("");
                intent.getStringExtra(""); 
            }catch (Exception e){}
        </pre>
    </p>
]]>
</Details>
</BugPattern>
<BugCode abbrev="SECLDOS">本地拒絕服務(wù)</BugCode> 

一切完成就緒后使用Maven進行打包,就生產(chǎn)了供FindBugs集成開發(fā)工具插件使用的JAR包,完成安裝并重啟,即可使用自定義插件對特定問題進行檢測。

最終分析的效果圖如下圖所示:

 

4 結(jié)語

本文介紹了Android集成開發(fā)環(huán)境Android Studio的代碼實時檢測工具Code Arbiter的產(chǎn)生原因及代碼實現(xiàn),最后展示了分析的效果。通過Code Arbiter在生產(chǎn)環(huán)境中的應(yīng)用,其檢測效果還是相當(dāng)不錯,能夠發(fā)現(xiàn)很多編碼過程中存在的問題。但是Code Arbiter仍然存在許多不足,需要優(yōu)化。后續(xù)將在以下兩個方面對工具進行改進:

擴大漏洞檢測范圍,使Code Arbiter能夠囊括Android編碼常見安全問題;

優(yōu)化漏洞檢測規(guī)則,提高檢測的準(zhǔn)確性,減少誤報。

5 作者簡介

建弋,2016年加入美團點評,目前主要負(fù)責(zé)金融部門相關(guān)的安全工作。

【本文為51CTO專欄機構(gòu)“美團點評技術(shù)團隊”的原創(chuàng)稿件,轉(zhuǎn)載請通過微信公眾號聯(lián)系機構(gòu)獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2017-05-12 14:25:09

2013-05-20 11:54:55

2010-09-17 16:16:28

2014-01-16 16:34:22

2011-03-15 14:19:50

2010-12-01 11:36:44

2014-12-08 09:01:53

2010-09-25 10:25:23

2020-08-04 22:21:54

漏洞掃描工具惡意軟件

2010-09-25 10:34:20

2010-09-26 13:25:16

2021-09-30 09:00:00

漏洞安全工具

2024-01-18 09:00:00

漏洞Docker工具

2022-07-10 00:01:43

漏洞工具安全

2020-07-13 07:18:26

滲透測試漏洞掃描工具

2018-09-10 11:07:19

2024-01-18 16:11:21

2019-11-08 09:12:35

Linux掃描儀軟件

2025-03-19 13:02:57

2013-01-11 09:41:34

點贊
收藏

51CTO技術(shù)棧公眾號