Android逆向分析之Cydia

最近開始使用Cydia Substrate框架進行逆向分析。

Cydia是什麼東東我就不多說了,自行百度,聽說它可以越獄什麼的,修改手機配置什麼的,但這裏只是通過一個例子,介紹如何使用Cydia Substrate框架進行hook Android java代碼。

相比Xposed框架,Cydia是有一定的優勢的,根據我目前使用的情況,暫時有如下兩點:

一是,Cydia能hook除了第一個smali文件夾外的第二第三個smali文件夾(如下圖)。

這話怎說?APKTool反編譯一個APK時,有時會分割成好幾個smali文件,Xposed只能hook第一個smali文件夾下的smali文件,有點坑,但Cydia卻都可以hook。


二是,Cydia能hook本地方法,而Xposed不可以



基於以上兩個優點,個人認爲Cydia比Xposed好用,Xposed侷限性較大,起碼目前是這麼認爲的~

但有個技巧的方法只能在Xposed上用,就是通過刻意拋異常,根據異常日誌找方法調用路徑,這個在逆向分析常用的技巧無法再Cydia上用,因爲Cydia拋異常後貌似不會以日誌的形式打印出來,反正我試了很多次都不行。。。


開始說說如何使用吧

-------------------------------------------------------

1.  首先,下載com.saurik.substrate.apk,然後安裝,手機必須得root過。這個是Cydia Substrate框架,需要另寫模塊加載進去。


安裝之後點擊 “Link  Substrate  Files” 即可。


2.  確定需要hook的對象APK中的方法,編寫模塊代碼。

這裏,我將hook一個apk裏被混淆過的方法,並說明注意點。


因爲混淆過,所以類名和方法名都被改成a,b,c等

該方法所在類:com.dianping.h.f.a.o

方法名:b, 

參數:com.dianping.nvnetwork.u 類

返回值 :com.dianping.nvnetwork.u 類


好,開始寫模塊,先在Android Studio裏創建一個工程,該工程不需要Activity,在工程創建一個java文件,該文件就是模塊代碼,然後下載Cydia Substrate SDK,解壓後將其放到libs文件夾下,並將jar包“Add as library”。



接下來,打開AndroidManifest,配置好一個meta-data和一個permission即可:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.samuelzhan.substratehook">

    <application>
        <meta-data android:name="com.saurik.substrate.main"
            android:value=".Module"/>
    </application>

    <uses-permission android:name="cydia.permission.SUBSTRATE"/>

</manifest>

然後,編寫模塊代碼:

public class Module {

    static void initialize(){

        MS.hookClassLoad("com.dianping.h.f.a.o",
                new MS.ClassLoadHook() {
                    public void classLoaded(Class<?> resources) {
                        Method method;
                        try {
//                       不能使用本進程的ClassLoader,必須使用hook對象進程的ClassLoader
//                            Class<?> u=ClassLoader.getSystemClassLoader().loadClass("com.dianping.nvnetwork.u");
//                            Class<?> u = Class.forName("com.dianping.nvnetwork.u").getClass();
                            Class<?> u= resources.getClassLoader().loadClass("com.dianping.nvnetwork.u");

//                       getMethod只能獲取public方法,getDeclareMethod能獲取所有方法
//                            method = resources.getMethod("b", u);
                            method = resources.getDeclaredMethod("b", u);
                            

                        } catch (Exception e) {
                            Log.d("zz", "exception: "+e.toString());
                            method = null;
                        }
                        if (method != null) {
                            final MS.MethodPointer old = new MS.MethodPointer();
                            MS.hookMethod(resources, method, new MS.MethodHook() {
                                @Override
                                public Object invoked(Object o, Object... objects) throws Throwable {
                                    // before hook
                                    Log.d("zz", "hook 成功");

                                    Object result = old.invoke(o, objects);

                                    // after hook

                                    return result;
                                }
                            }, old);
                        }
                    }
                });
    }


經過一個下午的摸索,我這裏提出三點注意的地方:

第一,模塊和hook對象的進程是兩個進程,所以使用ClassLoader加載 u 類時要從resources那裏getClassLoader,這是獲取hook對象的ClassLoader,不然你加載不了 u 類而拋異常ClassNotFound(當然,如果只是使用java原生的類,即不是自定義的類的話,那麼直接  .class後綴就可以獲取到 class<?>了)。

第二,獲取該類下的方法時,使用getDeclaredMethod可以獲取所有方法,包括public,private,protected,而getMethod只能獲取public

因爲我hook的那個函數是protected的,但由於筆者之前一直用getMethod而導致沒法hook到這方法。。。

Method method = resources.getDeclaredMethod("b", u);
第三,在方法被hook時,回調invoked方法,在此方法內有一句

Object result = old.invoke(o, objects);
該行代碼的上面和下面分別對應Xposed的beforeMethodHook和afterMethodHook兩個回調方法,如下圖:


3.  模塊寫好後,導出項目,然後安裝到手機上,手機若安裝了Cydia SubStrate,則會在通知欄提醒軟重啓。

重啓手機後,我們運行我們的目標APK即可以被Cydia Substrate 模塊識別到,並打印出相關日誌。

------------------------------------------------

到此爲止,Cydia Substrate的基本使用已到此爲止。

附加內容(可以忽略):

我上面提到的參數和返回值都是 u類實例,我之所以要hook這個 u類,是因爲該 u 類內有個變量 InputStream,混淆後以 g 字符命名。

爲了看到這個InputStream的內容,我必須通過反射找到該類的字段g,所以模塊的寫法會有所變化,但變化不大,增加了Field變量的獲取,直接代碼:

public class Module {

    static void initialize(){

        MS.hookClassLoad("com.dianping.h.f.a.o",
                new MS.ClassLoadHook() {
                    public void classLoaded(final Class<?> resources) {
                        try {
                            Class<?> u= resources.getClassLoader().loadClass("com.dianping.nvnetwork.u");
                            final Method method = resources.getDeclaredMethod("b", u);
                            final Field field=u.getDeclaredField("g");

                            final MS.MethodPointer old = new MS.MethodPointer();
                            MS.hookMethod(resources, method, new MS.MethodHook() {
                                @Override
                                public Object invoked(Object o, Object... objects) throws Throwable{
                                        // before hook
                                        InputStream is1=(InputStream)field.get(objects[0]);
                                        String before=byte2HexString(inputStream2Byte(is1));
                                        Log.d("zz", "Before HexString:"+before);
                                        is1.reset();

                                        Object result = old.invoke(o, objects);

                                        // after hook
                                        InputStream is2=(InputStream)field.get(result);
                                        String after=byte2HexString(inputStream2Byte(is2));
                                        Log.d("zz", "After HexString:"+after);
                                        is2.reset();
                                        return result;
                                }
                            }, old);

                        } catch (Exception e) {
                            Log.d("zz", "exception: "+e.toString());
                        }
                    }
                });
    }

    public static byte[] inputStream2Byte(InputStream is) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int n = 0;
        while ((n = is.read(buffer)) != -1) {
            baos.write(buffer, 0, n);
        }
        return baos.toByteArray();
    }

    public static byte[] hexString2Byte(String hex) {
        int len = (hex.length() / 2);
        hex=hex.toUpperCase();
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }
        return result;
    }
運行結果:


好了,Cydia的介紹到此爲止,其實還有其它功能的,比如hook C/C++的方法,這個日後再繼續研究。


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