我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP

小肥陽發表於2019-01-17

前陣子剛從總公司擼了一臺Google親兒子Nexus 6,Root後並刷了Xposed,剛想爽一把,結果其中一個APP開啟後就這幅德行了。

我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP
沒辦法,既然不讓我爽,我自己親自動手擼。

分析APK

最基本的分析可以用Android Studio,我們將安裝包拷貝到Android Studio開啟的工程目錄下,粗略檢視下

我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP
dex檔案中竟然沒有com.huimai365的包,而且lib下面的so檔案竟然佔比達到了50%多。心裡頓時涼了半截,我擦,這是加固了呀!
我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP
不行,作為一執行緒序猿,怎麼能輕言放棄!

脫殼

經過百般思考(google),我終於找到了一個傻瓜式脫殼工具dumpDex,沒想到還挺好用的

我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP
我們“點選脫殼”後,在/data/data/com.huimai365/dump資料夾下發現了很多dex檔案,感覺離成功又進了一步!
我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP
接下來就是體力活了,我們把dex全部pull下來,逐個用dex2jar工具將dex轉為jar。為此我寫了一個批量操作的指令碼,“xxx”目錄存放的是所有的dex檔案

dir=../xxx/
 
for file in $dir/*; do
    ./d2j-dex2jar.sh $file
done
複製程式碼

然後用JD-GUI尋找我們需要的包名com.huimai365,幸運的是我終於終於找到了,不幸的是特麼程式碼混淆。小夥子可以呀!

我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP

分析程式碼

我們先用腳指頭分析下,這種檢測Xposed基本在ApplicationonCreate方法中去執行,然後進行彈窗。好,那我們就尋找下他們自己實現的Application類。

我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP
然後分析onCreate中的方法
我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP
我發現有個ar.a();的方法,點進去一看,果然!(這裡只是展示部分程式碼)這裡不僅檢測Xposed,竟然還想關閉Xposed,沃日!

public class ar
{
  public static void a()
  {
    try
    {
      Field localField = ClassLoader.getSystemClassLoader().loadClass("de.robv.android.xposed.XposedBridge").getDeclaredField("disableHooks");
      localField.setAccessible(true);
      localField.set(null, Boolean.valueOf(true));
      return;
    }
    catch (Throwable localThrowable) {}
  }
  
  public static boolean a(Context paramContext)
  {
    return (b(paramContext)) || (c(paramContext)) || (b()) || (c());
  }
  
  private static boolean b(Context paramContext)
  {
    paramContext = paramContext.getPackageManager().getInstalledApplications(128);
    if (paramContext == null) {
      return false;
    }
    paramContext = paramContext.iterator();
    boolean bool = false;
    if (paramContext.hasNext())
    {
      ApplicationInfo localApplicationInfo = (ApplicationInfo)paramContext.next();
      if (localApplicationInfo.packageName.equals("de.robv.android.xposed.installer"))
      {
        ac.d("HookDetection", "Xposed found on the system.");
        bool = true;
      }
      if (!localApplicationInfo.packageName.equals("com.saurik.substrate")) {
        break label92;
      }
      ac.d("HookDetection", "Substrate found on the system.");
      bool = true;
    }
    label92:
    for (;;)
    {
      break;
      return bool;
    }
  }
  
  private static boolean c()
  {
    try
    {
      Object localObject = ClassLoader.getSystemClassLoader().loadClass("de.robv.android.xposed.XposedHelpers").newInstance();
      if (localObject != null) {
        if ((!a(localObject, "fieldCache")) && (!a(localObject, "methodCache")))
        {
          boolean bool = a(localObject, "constructorCache");
          if (!bool) {}
        }
        else
        {
          return true;
        }
      }
    }
    catch (Throwable localThrowable) {}
    return false;
  }
}
複製程式碼

Hook方法

從上面的分析看,我們只要Hook ar中的a()a(Context paramContext)方法就行了。那麼就動手吧!

public class XposedHookInit implements IXposedHookLoadPackage {
    private static final String TAG = "XposedHookInit";

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {

        if ("com.huimai365".equals(lpparam.packageName)) {
            Log.e(TAG, "Find 優品惠 " + lpparam.packageName);
            hookCheckoutXposed(lpparam.classLoader);
        }
    }

    private void hookCheckoutXposed(ClassLoader classLoader) {
        XposedHelpers.findAndHookMethod("com.huimai365.util.ar", classLoader, "a", new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                Log.e(TAG, "Replace close xposed");
                return null;
            }
        });

        XposedHelpers.findAndHookMethod("com.huimai365.util.ar", classLoader, "a", Context.class, new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                Log.e(TAG, "Replace find xposed");
                return false;
            }
        });
    }
}
複製程式碼

可是事情並沒有想象的那麼簡單,報錯了

我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP

2019-01-17 12:46:17.038 5888-5888/? E/Xposed: de.robv.android.xposed.XposedHelpers$ClassNotFoundError: java.lang.ClassNotFoundException: com.huimai365.util.ar
        at de.robv.android.xposed.XposedHelpers.findClass(XposedHelpers.java:71)
        at de.robv.android.xposed.XposedHelpers.findAndHookMethod(XposedHelpers.java:260)
        at com.example.xposeddemo.XposedHookInit.hookCheckoutXposed(XposedHookInit.java:35)
        at com.example.xposeddemo.XposedHookInit.handleLoadPackage(XposedHookInit.java:20)
        at de.robv.android.xposed.IXposedHookLoadPackage$Wrapper.handleLoadPackage(IXposedHookLoadPackage.java:34)
        at de.robv.android.xposed.callbacks.XC_LoadPackage.call(XC_LoadPackage.java:61)
        at de.robv.android.xposed.callbacks.XCallback.callAll(XCallback.java:106)
        at de.robv.android.xposed.XposedInit$2.beforeHookedMethod(XposedInit.java:134)
        at de.robv.android.xposed.XposedBridge.handleHookedMethod(XposedBridge.java:340)
        at android.app.ActivityThread.handleBindApplication(<Xposed>)
        at android.app.ActivityThread.-wrap2(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1546)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6121)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
        at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:107)
     Caused by: java.lang.ClassNotFoundException: com.huimai365.util.ar
        at java.lang.Class.classForName(Native Method)
        at java.lang.Class.forName(Class.java:400)
        at external.org.apache.commons.lang3.ClassUtils.getClass(ClassUtils.java:823)
複製程式碼

分析問題

為什麼明明有這個類,卻ClassNotFoundError呢?去了解下加固原理,才知道原來APP加殼之後,更改了classloader,因此我們用原本的classloader是會報類無法被找到的異常的。該APP使用的是騰訊的樂固,我們看到AndroidManifest.xml裡面的application已經被替換為com.tencent.StubShell.TxAppEntry

我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP
那我們再來看看TxAppEntry這個類,就在沒有加殼的classes.dex檔案中。

  protected void attachBaseContext(Context paramContext)
  {
    super.attachBaseContext(paramContext);
    SystemClassLoaderInjector.fixAndroid(paramContext, this);
    if (!b(this)) {
      return;
    }
    d(paramContext);
    a(this);
  }
複製程式碼

原來就在這裡更改了classloader,那我們獲取更改後classloader,然後再Hook不就行了?!OK,OK,OK,我們換個姿勢再來一次

public class XposedHookInit implements IXposedHookLoadPackage {
    private static final String TAG = "XposedHookInit";

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {

        if ("com.huimai365".equals(lpparam.packageName)) {
            Log.e(TAG, "Find 優品惠 " + lpparam.packageName);
            XposedHelpers.findAndHookMethod("com.tencent.StubShell.TxAppEntry", lpparam.classLoader,
                    "attachBaseContext", Context.class, new XC_MethodHook() {
                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            super.afterHookedMethod(param);
                            //獲取到Context物件,通過這個物件來獲取classloader
                            Context context = (Context) param.args[0];
                            //獲取classloader,之後hook加固後的就使用這個classloader
                            ClassLoader realClassLoader = context.getClassLoader();
                            //下面就是將classloader修改成殼的classloader就可以成功的hook了
                            hookCheckoutXposed(realClassLoader);

                        }
                    });
        }
    }

    private void hookCheckoutXposed(ClassLoader classLoader) {
        XposedHelpers.findAndHookMethod("com.huimai365.util.ar", classLoader, "a", new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                Log.e(TAG, "Replace close xposed");
                return null;
            }
        });

        XposedHelpers.findAndHookMethod("com.huimai365.util.ar", classLoader, "a", Context.class, new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                Log.e(TAG, "Replace find xposed");
                return false;
            }
        });

    }
}
複製程式碼

結局

我的手機憑什麼不給我爽——Xposed Hook混淆且加固後的APP
啊,整個世界清靜了。隨著身體的一陣哆嗦,這個APP變得索然無味……

總結

其實本人在這裡也只是將各種工具整合起來,大部分的知識都是從網上獲取的。希望能有個拋磚引玉的作用,讓各位讀者能有一點收穫。再者,真心膜拜這些造工具的大牛,看來我還有很長的路要走呀!

其實這也是我第一次寫技術文章,我特意放在了掘金上,因為這是我最喜歡的一個國內平臺(沒有之一),希望掘金越來越好,勿忘初心!

參考資料

抱歉,Xposed真的可以為所欲為——5.我自己刷的Xposed憑什麼不給我用

dumpDex-Android脫殼

Android逆向之路---脫殼360加固

相關文章