preface

As we all know, the last time said how to shell 360 reinforcement, roughly means to install an Xposed plug-in, and then automatically will shell, so this plug-in is how to work, this time focus on this.

Last time we talked about dumpDex unshell 360 hardening, in fact, it is a general idea, from the NDK layer and Java layer, adapt to different systems, hook key functions, and then dump the dex file at runtime.

If you just want to know how to use it, see the previous article

Point me: Android reverse road – decoupling 360 reinforcement, and Xposed hook notes

DumpDex Github project address: Click me click me, dumpDex

Required environment

  • No, read the article to get the general idea

(Of course, if you want to compile the dumpDex project, you will need the following tools.)

  • Android Studio
  • sdk ndk

The entrance

All programs execute with an entry point, and dumpDex projects are no exception. Due to a xposed plug-in, so let’s see. Com wrbug. Dumpdex. XposedInit class.

public class XposedInit implements IXposedHookLoadPackage {

    / / -- -- -- -- -- -- -- - a -- -- -- -- -- -- -- -- --

    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) {
        PackerInfo.Type type = PackerInfo.find(lpparam);
        if (type == null) {
            return;
        }
        final String packageName = lpparam.packageName;
        // This is mainly for each app to parse its own process
        if (lpparam.packageName.equals(packageName)) {
            // Create a directory in the specified directory of the current app, so that the dex file can be unshucked later
            String path = "/data/data/" + packageName + "/dump";
            File parent = new File(path);
            if(! parent.exists() || ! parent.isDirectory()) { parent.mkdirs(); } log("sdk version:" + Build.VERSION.SDK_INT);
            
            if (DeviceUtils.isOreo()) {
                // For API version 27 or 27, execute the following line to unshell
                OreoDump.init(lpparam);
            } else {
                // The lower version API executes the following line to unshellLowSdkDump.init(lpparam,type); }}}}Copy the code

Init () and lowsdkdump.init (). Let’s start with oreodump.init

public class OreoDump {

    / / -- -- -- -- -- -- -- - a -- -- -- -- -- -- -- -- --

    public static void init(final XC_LoadPackage.LoadPackageParam lpparam) { Native.dump(lpparam.packageName); }}Copy the code

Native.dump(),

  • Note: It doesn’t matter if you don’t know Android NDK, you can read on

Native hook

To find the JNICALL Java_com_wrbug_dumpdex_Native_dump method in the native. CPP file.

Now that I’ve switched to C, I’m going to help you cut out some of the details of the code and just look at the backbone.

Again, the following methods will be used for Native layer peeling by default on Android version 26 or 27

JNIEXPORT void JNICALL Java_com_wrbug_dumpdex_Native_dump
        (JNIEnv *env, jclass obj, jstring packageName) {

    Is_hook = is_hook = is_hook = is_hook = is_hook
    static bool is_hook = false;
    char *p = (char *) env->GetStringUTFChars(packageName, 0);
    if (is_hook) {
        __android_log_print(ANDROID_LOG_INFO, TAG, "hooked ignore");
        return;
    }
    init_package_name(p);
    env->ReleaseStringChars(packageName, (const jchar *) p);
    

    // Because the third-party library is used here, the initialization operation of the third-party library is performed first. For details about the third-party library, see below
    ndk_init(env);
    
    // Open libart.so in RTLD_NOW mode
    void *handle = ndk_dlopen("libart.so", RTLD_NOW);
    if (handle == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, TAG, "Error: unable to find the SO : libart.so");
        return;
    }
    // Depending on the version, get the corresponding loaded symbol
    void *open_common_addr = ndk_dlsym(handle, get_open_function_flag());

    / / -- -- -- -- -- -- -- - a -- -- -- -- -- -- -- -- --
    // Omit many branches and give a single name, see below
    if (registerInlineHook((uint32_t) open_common_addr, (uint32_t) get_new_open_function_addr(),
                           (uint32_t**) get_old_open_function_addr()) ! = ELE7EN_OK) { __android_log_print(ANDROID_LOG_ERROR, TAG,"register1 hook failed!");
        return;
    } else {
        __android_log_print(ANDROID_LOG_ERROR, TAG, "register1 hook success!");
    }
    // Set the hook flag to true
    is_hook = true;
}
Copy the code
registerInlineHook(
    (uint32_t) open_common_addr, (uint32_t) get_new_open_function_addr(),
                           (uint32_t **) get_old_open_function_addr())

Copy the code

Now that the address of the function is located, the next step is to Hook the previous function and replace it with our own defined function, such as the following function

static void *new_arm64_open_common(uint8_t *base, size_t size, void *location,
                                   uint32_t location_checksum, void *oat_dex_file,
                                   bool verify,
                                   bool verify_checksum,
                                   void *error_meessage, void *verify_result) {
    / / -- -- -- -- -- -- -- - a -- -- -- -- -- -- -- -- --
    // First, save the dex while the program is running to complete the unshell
    save_dex_file(base, size);
    // Call the previous function to ensure that the program executes correctly.
    void *result = old_arm64_open_common(base, size, location, location_checksum,
                                        oat_dex_file, verify, verify_checksum,
                                        error_meessage,
                                        verify_result);
    return result;
}
Copy the code

NDK layer hook completed

So far, the HOOK analysis of NDK layer is completed, and the third-party library is used to hook the method of loading dex function of the specified version of Android respectively, and then the new function of Hook is added to the function saved in the dump directory to achieve the purpose of decoupling.

NDK Hook is mainly used by the library

Github.com/rrrfff/ndk_…

Github.com/ele7enxxh/A…

The SDK layer hook

Going back to the chapter at the entrance, remember, there was lowsdkdump.init which was the logic at the lower version

public static void init(final XC_LoadPackage.LoadPackageParam lpparam, PackerInfo.Type type) {
        // if the SDK is one of 23,24,25,26,27, then continue using native layer hooks
        if (DeviceUtils.supportNativeHook()) {
            Native.dump(lpparam.packageName);
        }
        // Err... Baidu may charge money
        if (type == PackerInfo.Type.BAI_DU) {
            return;
        }
        // See below
        XposedHelpers.findAndHookMethod("android.app.Instrumentation", lpparam.classLoader, "newApplication", ClassLoader.class, String.class, Context.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                // Execute the real dump method and savedump(lpparam.packageName, param.getResult().getClass()); attachBaseContextHook(lpparam, ((Application) param.getResult())); }}); }Copy the code

The main thing above is to test whether the hook of natvie layer can be used first, and the hook of native layer should be given priority if possible, and then Baidu may charge money. Of course, JUST kidding, we can try this by ourselves.

Next is the Java layer hook, hook load class Instrumentation class, newApplication method, and then dump.

AttachBaseContextHook is also the loadClass method of the main Hook ClassLoader,

Focus on the Dump function in the Java layer

private static void dump(String packageName, Class
        aClass) {
        Object dexCache = XposedHelpers.getObjectField(aClass, "dexCache");
        log("decCache=" + dexCache);
        Object o = XposedHelpers.callMethod(dexCache, "getDex");
        byte[] bytes = (byte[]) XposedHelpers.callMethod(o, "getBytes");
        String path = "/data/data/" + packageName + "/dump";
        File file = new File(path, "source-" + bytes.length + ".dex");
        if (file.exists()) {
            log(file.getName() + " exists");
            return;
        }
        FileUtils.writeByteToFile(bytes, file.getAbsolutePath());
    }

Copy the code

You’re done

After a look at the code, you can try to draw a flow chart

Afterword.

In fact, the specific hook function involved in the article, we need to see specifically, to read android source code. That’s how we get it all together.

Understand how the system loads a dex, and really understand how to intercept and dump it out of memory. Dump when using someone else’s library, tools, are ok, mainly ideas. How to find key points and dump.

How to use it? See my last article

Point me: Android reverse road – decoupling 360 reinforcement, and Xposed hook notes

Write in the last

Now and then we talk about technology, now and then we talk about reverse, now and then we talk about life

We can’t talk about technology all the time. Let’s talk about something lighter next time.

The blogger is also a lazy blogger.

About me

Personal website: MartinHan’s station

Blog site: Hanhan12312 column

Zhihu: MartinHan01

My public number: program technology refers to the north (just opened soon, recently in pondering new things, cautious attention!