APP爬蟲技術總結

一、APP打包流程

  1. 打包資源文件,生成R.java文件
    打包資源的工具是aapt(The Android Asset Packaing Tool)(E:\Documents\Android\sdk\build-tools\25.0.0\aapt.exe)。
    在這個過程中,項目中的AndroidManifest.xml文件和佈局文件XML都會編譯,然後生成相應的R.java,另外AndroidManifest.xml會被aapt編譯成二進制。
    存放在APP的res目錄下的資源,該類資源在APP打包前大多會被編譯,變成二進制文件,並會爲每個該類文件賦予一個resource id。對於該類資源的訪問,應用層代碼則是通過resource id進行訪問的。Android應用在編譯過程中aapt工具會對資源文件進行編譯,並生成一個resource.arsc文件,resource.arsc文件相當於一個文件索引表,記錄了很多跟資源相關的信息。
  2. 處理aidl文件,生成相應的Java文件
    這一過程中使用到的工具是aidl(Android Interface Definition Language),即Android接口描述語言(E:\Documents\Android\sdk\build-tools\25.0.0\aidl.exe)。
    aidl工具解析接口定義文件然後生成相應的Java代碼接口供程序調用。如果在項目沒有使用到aidl文件,則可以跳過這一步。
  3. 編譯項目源代碼,生成class文件
    項目中所有的Java代碼,包括R.java和.aidl文件,都會變Java編譯器(javac)編譯成.class文件,生成的class文件位於工程中的bin/classes目錄下。
  4. 轉換所有的class文件,生成classes.dex文件
    dx工具生成可供Android系統Dalvik虛擬機執行的classes.dex文件,該工具位於(E:\Documents\Android\sdk\build-tools\25.0.0\dx.bat)。
    任何第三方的libraries和.class文件都會被轉換成.dex文件。dx工具的主要工作是將Java字節碼轉成成Dalvik字節碼、壓縮常量池、消除冗餘信息等。
  5. 打包生成APK文件
    所有沒有編譯的資源,如images、assets目錄下資源(該類文件是一些原始文件,APP打包時並不會對其進行編譯,而是直接打包到APP中,對於這一類資源文件的訪問,應用層代碼需要通過文件名對其進行訪問);編譯過的資源和.dex文件都會被apkbuilder工具打包到最終的.apk文件中。
    打包的工具apkbuilder位於 android-sdk/tools目錄下。apkbuilder爲一個腳本文件,實際調用的是(E:\Documents\Android\sdk\tools\lib)文件中的com.android.sdklib.build.ApkbuilderMain類。
  6. 對APK文件進行簽名
    一旦APK文件生成,它必須被簽名才能被安裝在設備上。
    在開發過程中,主要用到的就是兩種簽名的keystore。一種是用於調試的debug.keystore,它主要用於調試,在Eclipse或者Android Studio中直接run以後跑在手機上的就是使用的debug.keystore。
    另一種就是用於發佈正式版本的keystore。
  7. 對簽名後的APK文件進行對齊處理
    如果你發佈的apk是正式版的話,就必須對APK進行對齊處理,用到的工具是zipalign(E:\Documents\Android\sdk
    build-tools\25.0.0\zipalign.exe)
    對齊的主要過程是將APK包中所有的資源文件距離文件起始偏移爲4字節整數倍,這樣通過內存映射訪問apk文件時的速度會更快。對齊的作用就是減少運行時內存的使用。

二、APP常用抓包技術

  • 工具
    Charles,fiddler,burpsuite,Packet Capture等,具體操作可以查找百度
  • 抓包方法
    a.常規操作直接抓
    b.使用Xposed+JustTrustMe關閉SSL證書驗證抓包
    關閉SSL證書校驗之前抓包
    在這裏插入圖片描述
    關閉SSL證書校驗之後抓包
    關閉SSL證書校驗之後抓包
    c.使用Packet Capture抓取TCP數據包
    在這裏插入圖片描述
    d.通過寫xposed hook插件打印請求url和請求參數(示例不方便透露有疑問可聯繫)

四、APP脫殼

  • .加殼的原理
    給dex文件加層殼,反編譯後的代碼就是加殼的代碼,看不到原dex代碼,在一定程度上來說,還是可以起到防破解的,也可以防止二次打包
  • .常用的APP加固殼
    360 騰訊樂固、百度、網易、阿里、愛加密、梆梆、娜迦、頂象等 3手寫脫殼工具
  • 手寫脫殼工具:
    脫殼方式有好多種,企業殼最難搞,這裏分享一種使用xposed插件完成脫殼,主要的代碼如下:
public class PackageHook {
    Class Dex;
    Method Dex_getBytes;
    Method getDex;
    String packagename;
    public PackageHook(XC_LoadPackage.LoadPackageParam sharePkgParam) {
        packageHook(sharePkgParam);
    }

    private void packageHook(final XC_LoadPackage.LoadPackageParam lpparam) {
        Log.i("jyy", lpparam.packageName);

        final String packagename = "你要脫殼的app包名";
        //添加程序包名
        initRefect();
        XposedBridge.log("目標包名:" + lpparam.packageName);
        String str = "java.lang.ClassLoader";
        String str2 = "loadClass";

        XposedHelpers.findAndHookMethod(str, lpparam.classLoader, str2, String.class, Boolean.TYPE, new XC_MethodHook() {
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Class cls = (Class) param.getResult();
                if (cls == null) {
                    //XposedBridge.log("cls == null");
                    return;
                }
                String name = cls.getName();
                XposedBridge.log("當前類名:" + name);
                byte[] bArr = (byte[]) Dex_getBytes.invoke(getDex.invoke(cls, new Object[0]), new Object[0]);
                if (bArr == null) {
                    XposedBridge.log("數據爲空:返回");
                    return;
                }
                XposedBridge.log("開始寫數據");
                String dex_path = "/data/data/" + packagename + "/" + packagename + "_" + bArr.length + ".dex";
                XposedBridge.log(dex_path);
                File file = new File(dex_path);
                if (file.exists()) return;
                writeByte(bArr, file.getAbsolutePath());
            }
        } );
    }
        public void initRefect() {
            try {
                Dex = Class.forName("com.android.dex.Dex");
                Dex_getBytes = Dex.getDeclaredMethod("getBytes", new Class[0]);
                getDex = Class.forName("java.lang.Class").getDeclaredMethod("getDex", new Class[0]);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }

        }

    public  void writeByte(byte[] bArr, String str) {
        try {
            OutputStream outputStream = new FileOutputStream(str);
            outputStream.write(bArr);
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            XposedBridge.log("文件寫出失敗");
        }
    }

通過代碼可以發現就是反射拿到Android中com.android.dex.Dex對象然後調用getDex方法然後把字節寫到文件,其實就是脫殼後dex文件。Fdex2脫殼工具的核心原理就是上述代碼。

五、App逆向分析

  • 目標:cgv.apk 用戶註冊接口分析(僅僅用於學習,違法使用後果自負)
  • 工具:jadx,jeb, Android studio等
  • 抓包:註冊包
    在這裏插入圖片描述
    如果沒有逆向分析APP的看到請求參數是不是有點傻眼了,下面來探探究竟,先脫殼然後使用jadx打開脫殼後的dex,然後搜索關鍵詞
    在這裏插入圖片描述
    繼續跟蹤代碼,發現CLIENT_KEY是通過一個算法得到這個值
    在這裏插入圖片描述
    繼續跟進代碼(選中該方法按ctrl+鼠標左鍵)
    在這裏插入圖片描述
    先看h方法其實就是一個MD5算法
    在這裏插入圖片描述
    繼續跟進上述a方法中的a方法:
    在這裏插入圖片描述
    其實f方法就是做了一些字符串拼接操作,g方法就是做了數組排序,可以自行繼續追蹤下去,如果是Java開發則只需要拷貝就行,其他語言改寫就可以。到這裏算法基本已經明確,現在最主要的問題就是a方法的參數:在這裏插入圖片描述
    常規操作是去找這個方法在哪裏調用,這裏先介紹一個技巧可以使用xposed 寫一個hook插件把參數打印出來,如果打出來的參數還有加密,還是得去找它被調用的地方。不熟悉xposed框架可以去百度,一個神器的框架,hook插件代碼:
private void cgvHook(final XC_LoadPackage.LoadPackageParam lpp) {
        Log.i("jyy", lpp.packageName);
        if (!lpp.packageName.contains("com.cgv.cn.movie")) {
            return;
        }

        XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                ClassLoader cl = ((Context) param.args[0]).getClassLoader();
                Class<?> hookclass = null;
                try {
                    hookclass = cl.loadClass("com.cgv.cn.movie.b.ar");
                } catch (Exception e) {
                    Log.e("jyy", "尋找xxx.xxx.xxx報錯", e);
                    return;
                }
                //cn.ikicker.moviefans.ui.activity.b
                //a(String str, String str2, String str3)
                Log.e("jyy", "尋找xxx.xxx.xxx成功");
                //com.loopj.android.http

                Class hookclasss = cl.loadClass("com.loopj.android.http.RequestParams");
                XposedHelpers.findAndHookMethod(hookclass, "a",hookclasss,String.class, new XC_MethodHook() {

                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        Object params =  param.args[0];
                        String param1 = (String) param.args[1];
                        XposedBridge.log("a params:"+params.toString());
                        XposedBridge.log("a param1:"+param1);
                        super.beforeHookedMethod(param);
                    }
                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        String result = (String) param.getResult();
                        XposedBridge.log("a result:"+result);
                        super.afterHookedMethod(param);
                    }
                });
         });
 }

打開ddms就可以查看a params打印出來的就是我們a方法的第一個參數,a param1打印出的就是第二參數,a result打印出的就是a方法的返回值:

圖稍後補上

加密算法和加密參數已經知道了,CLIENT_KEY基本搞定了,其他的參數分析過程類似,這裏就不贅述,這些都是一些初級的操作,還有很多app算法套路更深需要不斷提升自己的實力才能擊破他。其實這種Java基礎加密完全可以寫一套常用算法hook也就是可以直接打印出加密參數和加密結果,本人寫了一個Java基本算法自吐插件,常規算法可以不需要脫殼和逆向app,直接打印加密算法和加密參數,但也有缺陷僅僅是Java層的算法而且是常規算法,如果算法有做修改則還是需要逆向代碼,但是可以給我做一些前期的準備,過濾掉一些參數算法逆向操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章