反編譯工具apktool使用問(wèn)題

1 工具篇

反編譯和回編用到的一些工具:

apktool是解包APK 文件最常用的工具

keytool是一個(gè)Java數(shù)據(jù)證書的管理工具

jarsigner是JDK提供的針對(duì)jar包簽名的通用工具

apksigner是Google官方提供的針對(duì)Android apk簽名及驗(yàn)證的專用工具

zipalign是對(duì)zip包對(duì)齊的工具,使APK包內(nèi)未壓縮的數(shù)據(jù)有序排列對(duì)齊,從而減少APP運(yùn)行時(shí)內(nèi)存消耗

2 調(diào)試包回編操作

通過(guò)apktool d xxx.apk得到反編譯后smali文件和manifest文件,進(jìn)行修改后,利用apktool build命令進(jìn)行重新打包。

2.1 apktool編譯時(shí)錯(cuò)誤

資源文件找不到:
\res\values-v19\styles.xml:11: error: Error: No resource found that matches the given name: attr 'android:actionModeFindDrawable'.
\res\values-v19\styles.xml:10: error: Error: No resource found that matches the given name: attr 'android:actionModeShareDrawable'.
\res\values-v19\styles.xml:21: error: Error: No resource found that matches the given name: attr 'android:actionModeFindDrawable'.

Exception in thread "main" brut.androlib.AndrolibException: brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [/tmp/brut_util_Jar_7346229083806801174.tmp, p, --forced-package-id, 127, --min-sdk-version, 15, --target-sdk-version, 26, --version-code, 1, --version-name, 1.0, --no-version-vectors, -F, /tmp/APKTOOL6548347216162541619.tmp, -0, arsc, -0, arsc, -I, /root/.local/share/apktool/framework/1.apk, -S, /root/Desktop/test1/res, -M, /root/Desktop/test1/AndroidManifest.xml]
    at brut.androlib.Androlib.buildResourcesFull(Androlib.java:492)
    at brut.androlib.Androlib.buildResources(Androlib.java:426)
    at brut.androlib.Androlib.build(Androlib.java:305)
    at brut.androlib.Androlib.build(Androlib.java:270)
    at brut.apktool.Main.cmdBuild(Main.java:227)
    at brut.apktool.Main.main(Main.java:75)
Caused by: brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [/tmp/brut_util_Jar_7346229083806801174.tmp, p, --forced-package-id, 127, --min-sdk-version, 15, --target-sdk-version, 26, --version-code, 1, --version-name, 1.0, --no-version-vectors, -F, /tmp/APKTOOL6548347216162541619.tmp, -0, arsc, -0, arsc, -I, /root/.local/share/apktool/framework/1.apk, -S, /root/Desktop/test1/res, -M, /root/Desktop/test1/AndroidManifest.xml]
    at brut.androlib.res.AndrolibResources.aaptPackage(AndrolibResources.java:456)
    at brut.androlib.Androlib.buildResourcesFull(Androlib.java:478)
    ... 5 more
Caused by: brut.common.BrutException: could not exec (exit code = 1): [/tmp/brut_util_Jar_7346229083806801174.tmp, p, --forced-package-id, 127, --min-sdk-version, 15, --target-sdk-version, 26, --version-code, 1, --version-name, 1.0, --no-version-vectors, -F, /tmp/APKTOOL6548347216162541619.tmp, -0, arsc, -0, arsc, -I, /root/.local/share/apktool/framework/1.apk, -S, /root/Desktop/test1/res, -M, /root/Desktop/test1/AndroidManifest.xml]
    at brut.util.OS.exec(OS.java:95)
    at brut.androlib.res.AndrolibResources.aaptPackage(AndrolibResources.java:450)
    ... 6 more 

錯(cuò)誤原因很清楚,大致是資源文件和一些api版本兼容的問(wèn)題,比如style,manifest一些application屬性等識(shí)別不了。
通過(guò)google有些說(shuō)需要升級(jí)新版本,或者低版本反編譯高版本打包,網(wǎng)上的一些答案我一般是不信的(哈哈哈)。
通過(guò)查apktool的命令幫助,可以通過(guò)-r參數(shù)來(lái)避免resc的反編譯,見(jiàn)下圖。這樣在打包的時(shí)候就不會(huì)重新編譯resc文件包括xml。

usage: apktool
 -advance,--advanced   prints advance information.
 -version,--version    prints the version then exits
usage: apktool if|install-framework [options] <framework.apk>
 -p,--frame-path <dir>   Stores framework files into <dir>.
 -t,--tag <tag>          Tag frameworks using <tag>.
usage: apktool d[ecode] [options] <file_apk>
 -f,--force              Force delete destination directory.
 -o,--output <dir>       The name of folder that gets written. Default is apk.out
 -p,--frame-path <dir>   Uses framework files located in <dir>.
 -r,--no-res             Do not decode resources.
 -s,--no-src             Do not decode sources.
 -t,--frame-tag <tag>    Uses framework files tagged by <tag>.
usage: apktool b[uild] [options] <app_path>
 -f,--force-all          Skip changes detection and build all files.
 -o,--output <dir>       The name of apk that gets written. Default is dist/name.apk
 -p,--frame-path <dir>   Uses framework files located in <dir>.


那么,我們需要修改xml的時(shí)候還是需要反編譯resc,或者通過(guò)二進(jìn)制寫入hex中。其實(shí)上面編譯失敗的原因是不同level的API造成的。
查看apktool.jar可發(fā)現(xiàn)android-framework.jar:
apktool.jar\brut\androlib\android-framework.jar

而android-framework.jar其實(shí)是用來(lái)指定默認(rèn)的反編譯和編譯的framework(不同手機(jī)和不同版本有不同的framework,但一般都是向下兼容的)。
執(zhí)行apktool命令會(huì)檢查/Users/Quan/Library/apktool/framework/路徑下是否包含1.apk,否則會(huì)通過(guò)android-framework.jar生成1.apk,然后通過(guò)該apk作為framework的路徑。通過(guò)apktool -p可以指定 framework的路徑。
結(jié)論:導(dǎo)致以上編譯失敗的原因很可能是,前期用過(guò)的apktool版本比較低,在編譯api level較高的apk時(shí)候出現(xiàn)資源找不到的情況,我們只需要?jiǎng)h除/Users/Quan/Library/apktool/framework/1.apk然后最新版本的apktool重新執(zhí)行編譯命令就可以。

在重新打包我們的APK后,其實(shí)app是不能夠正常運(yùn)行的,首頁(yè)會(huì)在application初始化中拋出apk簽名錯(cuò)誤的異常,然后退出。

2.2 修改smali的技巧

可以直接修改后或者通過(guò)編寫java文件先生成dex然后通過(guò)反編譯smali之后進(jìn)行在源文件中插入。
javac helloworld.java #編譯生成.class文件
dx --dex --output=helloworld.dex helloworld.class #生成dex
apktool d helloworld.dex #反編譯生成smali

2.3 調(diào)試APK

1.反編譯插入xml android:debuggable="true"
2.修改系統(tǒng)默認(rèn)的配置ro.debuggable=1
此文不贅述。
參考:https://www.bodkin.ren/index.php/archives/533/

3 關(guān)于apk的簽名

3.1 簽名和數(shù)字證書

簽名:
消息發(fā)送方生成一對(duì)公私鑰,消息發(fā)送過(guò)程中:
1)對(duì)要發(fā)送的原始消息提取消息摘要;
2)對(duì)提取的信息摘要用自己的私鑰加密。
數(shù)字證書
一般包含以下一些內(nèi)容:
1)證書的發(fā)布機(jī)構(gòu)(Issuer)
2)證書的有效期(Validity)
3)消息發(fā)送方的公鑰
4)證書所有者(Subject)
5)數(shù)字簽名所使用的算法
6)數(shù)字簽名


簽名和驗(yàn)證過(guò)程

3.2 APK簽名工具

jarsign是Java本生自帶的一個(gè)工具,可以對(duì)jar進(jìn)行簽名
signapk是后面專門為了Android應(yīng)用程序apk進(jìn)行簽名的工具,7.0之后強(qiáng)制要求使用該工具進(jìn)行簽名
區(qū)別:

  • 使用的簽名文件不一樣
    jarsign工具簽名時(shí)使用的是
    signapk工具簽名時(shí)使用的是pk8,x509.pem文件
  • 生成的簽名文件名不一樣
    jarsign的簽名文件名包含了keystore的別名


    jarsign簽名的產(chǎn)物

signapk固定的名字,默認(rèn)為CERT


signapk簽名的產(chǎn)物
  • 使用的默認(rèn)簽名算法不一樣
    jarsign默認(rèn)使用sha-256做摘要算法
    signapk 默認(rèn)使用sha-1做摘要算法

3.3 keystore和pk8,x509.pem可互相轉(zhuǎn)換

在線轉(zhuǎn)換地址:https://myssl.com/cert_convert.html

3.4 APK簽名后文件

signapk簽名apk之后會(huì)包含以下文件,位于META-INF。

1.MANIFEST.MF

Manifest-Version: 1.0
Built-By: Generated-by-ADT
Created-By: Android Gradle 3.0.1

Name: AndroidManifest.xml
SHA1-Digest: OOwo62rpBBm58uyA9DaVke/pjYM= 對(duì)資源和源碼文件sha1散列,base64加密

Name: META-INF/android.arch.core_runtime.version
SHA1-Digest: BeF7ZGqBckDCBhhvlPj0xwl01dw=

Name: META-INF/android.arch.lifecycle_extensions.version
SHA1-Digest: BeF7ZGqBckDCBhhvlPj0xwl01dw=

Name: META-INF/android.arch.lifecycle_livedata-core.version
SHA1-Digest: BeF7ZGqBckDCBhhvlPj0xwl01dw=

Name: META-INF/android.arch.lifecycle_livedata.version
SHA1-Digest: BeF7ZGqBckDCBhhvlPj0xwl01dw=

Name: META-INF/android.arch.lifecycle_runtime.version
SHA1-Digest: BeF7ZGqBckDCBhhvlPj0xwl01dw=

Name: META-INF/android.arch.lifecycle_viewmodel.version
SHA1-Digest: BeF7ZGqBckDCBhhvlPj0xwl01dw=

Name: META-INF/proguard/com.rong360.cccredit.base.comInputWidget.BaseV
 iewHolder.pro
SHA1-Digest: 2A1Gn5+G2UZfIRuOe+B8eayD3h4=

Name: META-INF/proguard/com.rong360.cccredit.base.view.BaseItemView.pr
 o
SHA1-Digest: 5NlorzmAOWo2s54IOAzJ6fpbRso=

Name: META-INF/rxjava.properties
SHA1-Digest: vKB1Ac/XQ8wiPI/th9N8DZ/+T9Y=

Name: assets/getui_popup_bg.9.png
SHA1-Digest: 0i2ug9zD61+mUJM+VRU0su+rZBA=

Name: assets/getui_popup_close.png
SHA1-Digest: 3PxcPZ1vRVg/shO9Q8m4kCl+++0=

Name: assets/home_producing.json
SHA1-Digest: F7l2niyrW1QfhVDBJwGIfu9OIqU=

Name: assets/loading_blue.json
SHA1-Digest: aARfFQziHhwO3GhIK9Yy1aowXYc=

2.CERT.SF

Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA1-Digest-Manifest: wMk2wph8ittVFJEdaSigTm0sCiU= 
//對(duì)MANIFEST.MF SHA1+base64

Name: AndroidManifest.xml
SHA1-Digest: hgUK+DK9MfGkBwlilMAQGpievZ8= 
//對(duì)MANIFEST.MF中塊+2次回車 SHA1+base64

Name: META-INF/android.arch.core_runtime.version
SHA1-Digest: OPQCkzMXJVPQryHeMowVNZmfRMw=

Name: META-INF/android.arch.lifecycle_extensions.version
SHA1-Digest: 6wRGJv7GN0UPnHsBck6G9DEz/wQ=

Name: META-INF/android.arch.lifecycle_livedata-core.version
SHA1-Digest: TSBGEIW1zN2n2sraHWcuRYSO8JU=

Name: META-INF/android.arch.lifecycle_livedata.version
SHA1-Digest: 2SW0lUzWE/vv2diFzKyGpyt9JW8=

Name: META-INF/android.arch.lifecycle_runtime.version
SHA1-Digest: yMBVn3OtS/Z9YTo02MlcvU6yfFM=

Name: META-INF/android.arch.lifecycle_viewmodel.version
SHA1-Digest: 5aVTTikyBvFlGq287kN/q6fe0kA=

3.CERT.RSA
證書文件,包含了.sf文件的簽名信息(sha256+私鑰加密)公鑰信息和發(fā)布機(jī)構(gòu)信息

3.5 查看證書信息

通過(guò)openssl工具可以查看rsa文件,該文件包含了摘要算法、加密算法、簽名和公鑰等信息。
如下:


證書的內(nèi)容

4 項(xiàng)目中APK的簽名驗(yàn)證

驗(yàn)證應(yīng)用的簽名證書信息和release包真實(shí)的簽名是否一致,因?yàn)樗借€不可能被人獲取。這也是app store上認(rèn)領(lǐng)app的方式。

5 通過(guò)xposed繞過(guò)簽名驗(yàn)證機(jī)制

由于目標(biāo)的apk有在初始化進(jìn)行證書hash的認(rèn)證,我需要通過(guò)以下方法繞過(guò)簽名驗(yàn)證機(jī)制。

兩方式:
1.修改Signature的方法
so中可以用IDA進(jìn)行搜索,通過(guò)IDA分析so庫(kù)中簽名散列值。
通過(guò)hook android.content.pm.Signature#hashCode方法的返回值,將其置為和通過(guò)逆向得到的值一致,從而達(dá)到了繞過(guò)簽名驗(yàn)證的機(jī)制。
2.Hook系統(tǒng)的PMS服務(wù)

public static void hookPms(Context context, String signed, int hashCode){
    try{
        // 獲取全局的ActivityThread對(duì)象
        Class<!--?--> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod =
                activityThreadClass.getDeclaredMethod("currentActivityThread");
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);
        // 獲取ActivityThread里面原始的sPackageManager
        Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
        sPackageManagerField.setAccessible(true);
        Object sPackageManager = sPackageManagerField.get(currentActivityThread);
        // 準(zhǔn)備好代理對(duì)象, 用來(lái)替換原始的對(duì)象
        Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<!--?-->[] {Class.forName("android.content.pm.IPackageManager") },
                new PmsHookBinderInvocationHandler(sPackageManager, signed, hashCode));
        //  替換掉ActivityThread里面的 sPackageManager 字段
        sPackageManagerField.set(currentActivityThread, proxy);
        //  替換 ApplicationPackageManager里面的 mPM對(duì)象
        PackageManager pm = context.getPackageManager();
        Field mPmField = pm.getClass().getDeclaredField("mPM");
        mPmField.setAccessible(true);
        mPmField.set(pm, proxy);
    }catch (Exception e){
    }
}
public class PmsHookBinderInvocationHandler implements InvocationHandler{
 
    private Object base;
    //應(yīng)用正確的簽名信息
    private String SIGN;
    private int hashCode = 0;
    public PmsHookBinderInvocationHandler(Object base, String sign, int hashCode) {
        try {
            this.base = base;
            this.SIGN = sign;
            this.hashCode = hashCode;
        } catch (Exception e) {
        }
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if("getPackageInfo".equals(method.getName())){
            Integer flag = (Integer)args[1];
            if(flag == PackageManager.<em>GET_SIGNATURES</em>){
               Signature sign = new Signature(SIGN);
               if(hashCode != 0){
                  try{
                      Class<!--?--> clazz = sign.getClass();
                      Field mHaveHashCodeF = clazz.getDeclaredField("mHaveHashCode");
                      mHaveHashCodeF.setAccessible(true);
                      mHaveHashCodeF.set(sign, true);
                      Field mHashCodeF = clazz.getDeclaredField("mHashCode");
                      mHashCodeF.setAccessible(true);
                      mHashCodeF.set(sign, hashCode);
                   }catch(Exception e){
                   }
               }
               PackageInfo info = (PackageInfo) method.invoke(base, args);
               info.signatures[0] = sign;
               return info;
            }
        }
        return method.invoke(base, args);
    }
}
private void getSignature() {
    try {
        PackageInfo packageInfo = getPackageManager().getPackageInfo(
                getPackageName(), PackageManager.<em>GET_SIGNATURES</em>);
        if (packageInfo.signatures != null) {
           Log.d("", "sig:"+packageInfo.signatures[0].toCharsString()+
                    "hashcode:"+packageInfo.signatures[0].hashCode());
        }
    } catch (Exception e2) {
    }
}

該方法不需要借助xposed也無(wú)需root,將方法插入application首行(通過(guò)smali來(lái)插入),然后編譯成apk。因?yàn)楂@取簽名服務(wù)的在Security#init中所以我們可以在此之前hook 住pms,從而修改簽名的信息。Hook部分的代碼參考Android Hook技術(shù)實(shí)踐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容