Hook加壳app

xiaoeryu Lv5

本章主要分析怎么处理加壳app

之前几篇文章编写的Xposed插件都是在没有加壳的情况下编写的,如果加壳后应该怎么处理

在应用内部代码被加固的情况下,如何hook到内部代码

  1. 因为ClassLoader是壳的ClassLoader

    因为Xposed框架Hook的时机是比较早的,如果一个app加壳了,那么Xposed hook默认使用的ClassLoader默认使用的是壳程序的ClassLoader,这个时候我们去hook,只能hook到壳代码。是无法hook到应用内部的代码的。

  2. 要获取到应用本身的ClassLoader

    因为app最先获得执行权限的是app中声明的Application类中的attachBaseContextonCreate函数,因此壳要想完成应用中加固代码的解密以及执行权的交付就都是在这两个函数上做文章,我们需要当前壳在函数attachBaseContextonCreate中执行完加密的dex文件的解密后,通过利用java的反射机制获取到应用运行中的ClassLoader。拿到之后就可以对应用本身的函数进行hook了

案例

这里从网上下载了一个漫画的小APP

  • GDA打开后可以看到是一个360的壳,正常来说我们要先脱壳拿到dex分析之后再来进行hook
  • 但是暂时现在手里没那么多设备刷来刷去比较麻烦,这里也只验证流程可行性达到目的就可以

开始编写插件

例如我们尝试一下直接hook它里面的com.stub.StubApp.onCreate

代码片段

XposedHelpers.findAndHookMethod("com.stub.StubApp", loadPackageParam.classLoader, "onCreate", new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    super.beforeHookedMethod(param);
                    XposedBridge.log("com.stub.StubApp->beforeHookedMethod->onCreate: " + param.thisObject);
                    XposedBridge.log("com.stub.StubApp: " + loadPackageParam.classLoader);
                }

成功的hook了壳的onCreate函数

然后,我们就可以通过反射去获取当前运行的ClassLoader,来hook原有应用的函数了。

获取ClassLoader

代码片段

    public static ClassLoader getClassloader() {
        ClassLoader resultClassloader = null;

        // 使用XposedHelpers.findClass找到ActivityThread类
        Class<?> activityThreadClass = XposedHelpers.findClass("android.app.ActivityThread", null);

        // 调用currentActivityThread静态方法获取当前ActivityThread对象
        Object currentActivityThread = XposedHelpers.callStaticMethod(activityThreadClass, "currentActivityThread");

        // 获取mBoundApplication字段的值
        Object mBoundApplication = XposedHelpers.getObjectField(currentActivityThread, "mBoundApplication");

        // 获取LoadedApk对象
        Object loadedApkInfo = XposedHelpers.getObjectField(mBoundApplication, "info");

        // 获取mApplication字段的值
        Application mApplication = (Application) XposedHelpers.getObjectField(loadedApkInfo, "mApplication");

        // 获取ClassLoader
        resultClassloader = mApplication.getClassLoader();

        return resultClassloader;
    }
  • 这样就可以通过反射和Xposed提供的api来获取当前程序的ClassLoader

  • 拿到ClassLoader之后就可以hook当前程序的代码了

  • 不过我们现在还不知道它的类名是什么以及类里面有哪些方法,可以去AndroidManifest里面找找看

    • 比如说这里我们可以hook它的MainActivity_类,它里面肯定有onCreate方法
    • 当然我们也可以用获取到的ClassLoader把类中的类列表全部打印出来(放在文章末尾单独介绍一下)

hook解密后程序中的方法

代码片段

// 获取当前ClassLoader
ClassLoader finalClassLoader = getClassloader();
XposedBridge.log("finalClassLoader: " + finalClassLoader);
// 打印类中的所有方法
Class MainActivity_Class = XposedHelpers.findClass("com.omyga.app.ui.activity.MainActivity_",finalClassLoader);
Method[] methods = MainActivity_Class.getDeclaredMethods();
for (Method i:methods){
    XposedBridge.log("com.omyga.app.ui.activity.MainActivity_: " + i);
}

// hook方法
XposedHelpers.findAndHookMethod("com.omyga.app.ui.activity.MainActivity_", finalClassLoader, "onCreate", Bundle.class, new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        super.beforeHookedMethod(param);
        XposedBridge.log("beforeHookedMethod com.omyga.app.ui.activity.MainActivity_.onCreate");
    }

成功的hook了MainActivity_中的onCreate函数

总结:这样我们就成功的hook到了加壳app中的函数,其实步骤也不是很复杂。不过里面有一些细节例如获取ClassLoader和类加载的函数列表,需要我们对源码有一定的了解才能通过反射获取到。


加壳app的hook流程也比较简单,下面分析一下获取类中所有方法的原理

分析如何获取dex中的类列表

这里先打印一下它的双亲委派关系,看看它是使用PathClassLoader还是DexClassLoader或者其它的ClassLoader加载的dex

            ClassLoader classLoader = loadPackageParam.classLoader;

            XposedBridge.log("loadPackageParam.classLoader: " + classLoader);
            ClassLoader parent = classLoader.getParent();
            while (parent != null) {
                XposedBridge.log("lebo->parent: " + parent);
                parent = parent.getParent();
            }
loadPackageParam.classLoader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.f0208.lebo-_PxU7X4MfMsuNOqGdX74Vw==/base.apk"],nativeLibraryDirectories=[/data/app/com.f0208.lebo-_PxU7X4MfMsuNOqGdX74Vw==/lib/arm, /data/app/com.f0208.lebo-_PxU7X4MfMsuNOqGdX74Vw==/base.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib, /system/product/lib]]]
lebo->parent: java.lang.BootClassLoader@8e4a8a6
  • 通过这段打印出来的信息我们可以知道,这里使用了PathClassLoader来加载dex
  • 至于BootClassLoader是根节点

回顾一下之前学习FART脱壳的时候分析过的源码

在dex修复的过程中可以找到当前ClassLoader加载的所有类

首先在源码的libcore文件找到PathClassLoader

/**
 * Provides a simple {@link ClassLoader} implementation that operates on a list
 * of files and directories in the local file system, but does not attempt to
 * load classes from the network. Android uses this class for its system class
 * loader and for its application class loader(s).
 */
public class PathClassLoader extends BaseDexClassLoader {
    /**
     * Creates a {@code PathClassLoader} that operates on a given list of files
     * and directories. This method is equivalent to calling
     * {@link #PathClassLoader(String, String, ClassLoader)} with a
     * {@code null} value for the second argument (see description there).
     *
     * @param dexPath the list of jar/apk files containing classes and
     * resources, delimited by {@code File.pathSeparator}, which
     * defaults to {@code ":"} on Android
     * @param parent the parent class loader
     */
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    /**
     * Creates a {@code PathClassLoader} that operates on two given
     * lists of files and directories. The entries of the first list
     * should be one of the following:
     *
     * <ul>
     * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as
     * well as arbitrary resources.
     * <li>Raw ".dex" files (not inside a zip file).
     * </ul>
     *
     * The entries of the second list should be directories containing
     * native library files.
     *
     * @param dexPath the list of jar/apk files containing classes and
     * resources, delimited by {@code File.pathSeparator}, which
     * defaults to {@code ":"} on Android
     * @param librarySearchPath the list of directories containing native
     * libraries, delimited by {@code File.pathSeparator}; may be
     * {@code null}
     * @param parent the parent class loader
     */
    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }

    /**
     * @hide
     */
    @libcore.api.CorePlatformApi
    public PathClassLoader(
            String dexPath, String librarySearchPath, ClassLoader parent,
            ClassLoader[] sharedLibraryLoaders) {
        super(dexPath, librarySearchPath, parent, sharedLibraryLoaders);
    }
}
  • PathClassLoader继承自BaseDexClassLoader,它没有自己的属性,继续跟进BaseDexClassLoader
public class BaseDexClassLoader extends ClassLoader {

    /**
     * Hook for customizing how dex files loads are reported.
     *
     * This enables the framework to monitor the use of dex files. The
     * goal is to simplify the mechanism for optimizing foreign dex files and
     * enable further optimizations of secondary dex files.
     *
     * The reporting happens only when new instances of BaseDexClassLoader
     * are constructed and will be active only after this field is set with
     * {@link BaseDexClassLoader#setReporter}.
     */
    /* @NonNull */ private static volatile Reporter reporter = null;

    @UnsupportedAppUsage
    private final DexPathList pathList;
    
    ...
  • 在这里可以看到DexPathList定义了一个重要的属性pathList

  • 继续跟进DexPathList

public final class DexPathList {
    private static final String DEX_SUFFIX = ".dex";
    private static final String zipSeparator = "!/";

    /** class definition context */
    @UnsupportedAppUsage
    private final ClassLoader definingContext;

    /**
     * List of dex/resource (class path) elements.
     * Should be called pathElements, but the Facebook app uses reflection
     * to modify 'dexElements' (http://b/7726934).
     */
    @UnsupportedAppUsage
    private Element[] dexElements;
    
    ...
  • 这里的dexElements数组,就保存有当前ClassLoader加载的所有dex

获取所有加载的类

通过反射拿到ClassList

    public void GetClassLoaderClasslist(ClassLoader classLoader){
        XposedBridge.log("lebo->start dealwith classLoader: " + classLoader);
        // private final DexPathList pathList;
        Object pathListObj = XposedHelpers.getObjectField(classLoader, "pathList");
        // private Element[] dexElements;
        Object[] dexElementsObj = (Object[]) XposedHelpers.getObjectField(pathListObj,"dexElements");
        for (Object i:dexElementsObj){
            // private final DexFile dexFile;
            Object dexFileObj = XposedHelpers.getObjectField(i,"dexFile");
            // private Object mCookie;
            Object mCookieObj = XposedHelpers.getObjectField(dexFileObj,"mCookie");
            // private static native String[] getClassNameList(Object cookie);
            // callStarticMethod可以调用静态Native函数
            Class DexFileClass = XposedHelpers.findClass("dalvik.system.DexFile",classLoader);
            String[] classNameList = (String[]) XposedHelpers.callStaticMethod(DexFileClass,"getClassNameList",mCookieObj);
            for (String j:classNameList){
                XposedBridge.log(dexFileObj+ "---" + j);
            }
        }
        XposedBridge.log("lebo->end dealwith classLoader: " + classLoader);
    }
  • 通过反射一步步拿到DexFile中的mCookie

  • 用mCookie作为参数调用库中的native函数getClassNameList获取到当前ClassLoader加载的所有类名

本章源码

  • 标题: Hook加壳app
  • 作者: xiaoeryu
  • 创建于 : 2023-12-20 19:18:29
  • 更新于 : 2023-12-22 17:35:05
  • 链接: https://github.com/xiaoeryu/2023/12/20/Hook加壳app/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论