日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Apk去签名校验详解

發(fā)布時間:2025/3/15 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Apk去签名校验详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
某些apk為了防止重打包,使用了簽名校驗。所以在破解的時候我們需要破解簽名校驗。在定位簽名校驗位置時常用的關(guān)鍵詞有sign,signature,checkSign,signCheck,getPackageManager,getPackageInfo,verify,same等。   java層簽名校驗代碼示例: 1 //原簽名信息 2 private static final String SIGNATURE = "478yYkKAQF+KST8y4ATKvHkYibo="; 3 private static final int VALID = 0; 4 private static final int INVALID = 1; 5 6 public static int checkAppSignature(Context context) { 7 try { 8 PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),PackageManager.GET_SIGNATURES); 9 10 for (Signature signature : packageInfo.signatures) { 11 byte[] signatureBytes = signature.toByteArray(); 12 MessageDigest md = MessageDigest.getInstance("SHA"); 13 md.update(signature.toByteArray()); 14 15 final String currentSignature = Base64.encodeToString(md.digest(), Base64.DEFAULT); 16 17 Log.d("REMOVE_ME", "Include this string as a value for SIGNATURE:" + currentSignature); 18 19 //compare signatures 20 if (SIGNATURE.equals(currentSignature)){ 21 return VALID; 22 }; 23 } 24 } catch (Exception e) { 25 //assumes an issue in checking signature., but we let the caller decide on what to do. 26 27 } 28 return INVALID; 29 }

  ? 最近遇到一個進行了簽名校驗的apk,它是在客戶端獲取簽名信息然后在服務(wù)器端進行簽名比對的,而且簽名的獲取是在native代碼中實現(xiàn)的。這種簽名校驗的破解思路一般是在sd卡中保存一個原apk包,修改apk路徑,讓程序獲取原apk的簽名信息。修改辦法一般有三種,下面一一介紹。

(一)在java層修改context進行破解

首先安裝apk,啟動DDMS查看log信息: 根據(jù)這個“verifyHashByC”這個字符串猜測跟簽名校驗有關(guān)系,于是在java層搜索verifyHashByC,沒有找到對應(yīng)的log打印處,只發(fā)現(xiàn)同名的native函數(shù)調(diào)用: 猜測此log打印信息是在so中,于是找到該native函數(shù)對應(yīng)的so加載處,發(fā)現(xiàn)加載的是libAppVerify.so這個動態(tài)庫: 用ida打開libAppVerify.so,搜索verifyHashByC,在VerifyHash_BySha函數(shù)中發(fā)現(xiàn)了log日志打印的代碼: 光看函數(shù)名稱VerifyHash_BySha就知道是通過sha算法驗證Hash,但是這是不是用于簽名校驗的呢,利用ida的交叉引用查看函數(shù)調(diào)用關(guān)系,發(fā)現(xiàn)這個函數(shù)在VerifyHash(_JNIEnv *, char *, char const*, int)函數(shù)中被調(diào)用。而VerifyHash則是由前面提到的native函數(shù)verifyHashByC來調(diào)用的。 VerifyHash函數(shù)先獲取應(yīng)用包名,然后調(diào)用GetApkMFData函數(shù): 在該函數(shù)中獲取應(yīng)用路徑,然后解壓應(yīng)用安裝包,讀取里面的MANIFEST.MF文件內(nèi)容,然后再進行簽名比對: 獲取apk路徑的示例代碼如下: 1 private static String getApkPath(String pkgName) { 2 PackageManager pm = mContext.getPackageManager(); 3 ApplicationInfo pi = null; 4 try { 5 pi = pm.getApplicationInfo(pkgName,PackageManager.GET_UNINSTALLED_PACKAGES); 6 if(pi != null) 7 return pi.sourceDir; 8 else 9 return null; 10 } catch (NameNotFoundException e) { 11 e.printStackTrace(); 12 return null; 13 } 14 }

sourceDir保存了apk的完整路徑:

?

接下來,解壓apk,讀取MANIFEST.MF文件: 下圖為MF文件中的內(nèi)容,與上圖中內(nèi)存單元的內(nèi)容一致: 分析了這么多,如何破解呢?通過前面的分析可知在so中獲取apk包的路徑的流程是: 傳入context,通過ApplicationInfo的sourceDir獲取到apk路徑,如果我們修改sourceDir讓它始終指向原apk,那么我們的目的就達到了。 通過反編譯java代碼,找到調(diào)用libAppVerify.so的地方,如圖所示,context就是從這里傳入的: 我們要做的就是修改這個context,使它最后指向的sourceDir為原apk路徑。 根據(jù)上圖原理,我們自己實現(xiàn)一個ApkApplicationInfo類指定sourceDir為原apk(SD卡中apk)路徑,ApkApplicationInfo在getApplicationInfo函數(shù)中實例化,由于getApplicationInfo函數(shù)為PackageManager類的成員函數(shù),所以需要自己實現(xiàn)一個ApkPackageManager類(繼承自PackageManager類)來調(diào)用getApplicationInfo函數(shù)。ApkPackageManager類需要在getPackageManager函數(shù)中得到實例化,getPackageManager函數(shù)為Context的成員函數(shù),所以還需要自己實現(xiàn)一個繼承自Context的類ApkContext。 于是,創(chuàng)建一個Android工程,創(chuàng)建ApkApplicationInfo、ApkPackageManager和ApkContext這三個類,設(shè)置sourceDir為“/sdcard/myapk.apk”。編譯后,將生成的apk反編譯,得到這的三個類的smali代碼文件,如下圖所示: 然后,將原apk反編譯,把上述三個文件放入反編譯后的apk smali文件夾中,在Lcom/rytong/emp/security/AppVerify類的verifyHash函數(shù)中插入下述代碼,如圖所示: 重打包,安裝運行,成功!

(二)使用hook技術(shù)

  我們知道進行簽名校驗是在libAppVerify.so中,所以我們只需hook這個so中的函數(shù),更改傳入的apk路徑就行了。通過分析可知,該so是使用MINIZIP進行apk文件解壓縮,然后獲取簽名信息的。 使用MINIZIP進行apk文件解壓縮的示例代碼如下: 1 void uncrypt_test() 2 { 3 //采用MINIZIP進行文件解壓縮 4 unzFile uf=NULL; 5 unzFile data[1200]; 6 unz_global_info64 gi; 7 unz_file_info64 FileInfo; 8 9 //打開zip文件 10 uf = unzOpen64("D:\\myfile.zip"); 11 int result=unzGetGlobalInfo64(uf, &gi); 12 if (result != UNZ_OK) 13 throw "文件錯誤"; 14 15 //循環(huán)解壓縮文件 16 for(int i=0;i<gi.number_entry;++i) 17 { 18 if (unzGetCurrentFileInfo64(uf, &FileInfo, 0, 0,NULL,0,NULL,0)!= UNZ_OK) 19 throw "文件錯誤"; 20 21 if(!(FileInfo.external_fa & FILE_ATTRIBUTE_DIRECTORY)) //文件,否則為目錄 22 //打開文件 23 //result=unzOpenCurrentFile(uf);/* 無密碼 */ 24 result=unzOpenCurrentFilePassword(uf,"123"); /* 有密碼 */ 25 26 //讀取內(nèi)容 27 int size= unzReadCurrentFile(uf,data,sizeof(data)); 28 29 //關(guān)閉當(dāng)前文件 30 unzCloseCurrentFile(uf); 31 32 //出錯 33 if(i < gi.number_entry - 1 && unzGoToNextFile(uf) != UNZ_OK) 34 throw "error"; 35 } 36 37 //關(guān)閉流 38 unzClose(uf); 39 }

  從上述代碼可知,解壓過程中,apk路徑以參數(shù)形式傳入unzOpen64函數(shù)。所以我們需要hook這個函數(shù)。但是如何知道libAppVerify.so何時被加載呢?我們知道系統(tǒng)通過dvmLoadNativeCode函數(shù)從指定的路徑加載so,如果對系統(tǒng)函數(shù)dvmLoadNativeCode進行hook,當(dāng)它加載libAppVerify.so的時候,再hook?unzOpen64函數(shù),修改apk路徑,不就行了?

  dvmLoadNativeCode函數(shù)原型:  

bool dvmLoadNativeCode(constchar* pathName, Object* classLoader, char** detail)

  這里我們使用cydia substrate這個hook框架,關(guān)鍵代碼如下:

? 將編譯生成的so文件放到原apk lib目錄下。再通過java層進行加載,加載代碼如下: 1 const-string/jumbo v0, "substrate" 2 invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V 3 const-string/jumbo v0, "substrate-dvm" 4 invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V 5 const-string/jumbo v0, "HookVerify.cy" 6 invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

由于需要在簽名校驗代碼運行之前加載這些so,所以我們在Lcom/cgbchina/xpt/EMPView類的構(gòu)造函數(shù)中添加上述代碼,如下圖所示:

重打包簽名,安裝運行成功!

(3)直接修改so文件

由于簽名驗證驗證的是MANIFEST.MF文件,我們將原apk中MANIFEST.MF文件改名為SIGNFILE.MF放到修改了的apk META-INF文件夾下,然后將so中驗證的文件名改為SIGNFILE.MF,如圖所示:

保存,替換原so,重打包安裝,運行成功!


原文地址:?http://www.cnblogs.com/goodhacker/p/4842215.html

總結(jié)

以上是生活随笔為你收集整理的Apk去签名校验详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。