Introduction to big data security Android Frida exercises

learning target

Today I will take you to do a simple Frida Hook Javamulti-level question. There are seven small checkpoints in total. Each checkpoint has a small inspection point to examine our Fridabasic knowledge. Students who have already learned it can also use it as a reference.

APP: FridaTest questions
Download address: see the end of the article

first round

First install and run the APP, each level has a requirement and inspection point

Inspection point: modification of method parameters

Analysis: Take a general look at the logic, that is, click the next level, the onClickmethod will be called, and the method onClickwill call checkthe method, but there is one parameter. falseIn checkthe method, according to this parameter, choose whether to enter the next level or prompt failure. If we don't make any modifications, y will never be able to enter the next level, so we need to use Frida to modify checkthe parameters of the method to become true, to help us enter the next level.

function main(){
    
    
    Java.perform(function(){
    
    
        // Frist
        Java.use("com.dta.test.frida.activity.FirstActivity").check.implementation = function(z){
    
    
            z = true
            this.check(z)
        }

    })
}
setImmediate(main)

second level

Inspection point: modification of method return value

Analysis: The calling process is the same, or call onClickthe method, and then analyze the logic. It is also very simple, call checkthe method, and checkchoose whether to enter the next level or prompt failure according to the return value of the method. So we can directly modify checkthe return value of the methodtrue

function main(){
    
    
    Java.perform(function(){
    
    
        //Second
        Java.use("com.dta.test.frida.activity.SecondActivity").check.implementation = function(){
    
    
            return true
        }

    })
}
setImmediate(main)

third level

Inspection point: modification of class member variables, value of enumeration class

Analysis: This question is to judge unkownwhether the value of the member variable is equal Level.Fouth, so unkownthe value we need to modify Level.Fouth, the default value Level.Unkownis relatively simple, right?
The first point of investigation is how to modify the value of this member variable, which is divided into two situations:
1. Static member variable, modify the value of the static member variable and use it directly to get Java.usea class wraper, directly. Variable name value. Modify its value directly.
2. For instance member variables, we need to Java.chooseobtain this instance first, and then modify it by means of .
The second point of investigation is the value of the enumeration class, because our unkownmember variable wants to assign a value to it Level.Fouth, so we need to get this Level.Fouth, and this Levelis an enumeration class, as can be seen from the name, enumeration A class is nothing more than a special class, and its value can also be obtained directly by using the method of .name.value. Look at the code

function main(){
    
    
    Java.perform(function(){
    
    
        //Third
        Java.choose("com.dta.test.frida.activity.ThirdActivity",{
    
    
            onMatch: function(ins){
    
    
                console.log(ins)
                ins.unknown.value = Java.use("com.dta.test.frida.base.Level").Fourth.value
            },onComplete: function(){
    
    
                console.log("Search Completed!")
            }
        })

    })
}
setImmediate(main)

A classmate asked a question about this topic. He did not modify the value of the class member variable to enter the next level. This is what we have said in the first topic. Do not use any methods other than the inspection point to reach the next level. , otherwise there will be many solutions to a problem, and the purpose of our investigation will not be achieved.

fourth pass

Inspection point: active call of the method

Analysis: The purpose of this question is to let us learn Fridahow to actively call a method, and the active call is also divided into two situations
1. The active call of a static method can be called directly by Java.usegetting the class directly. 2. To actively call an instance method, you need to obtain an instance of the class first. There are many ways to get an instance of a class, such as an object, to get an existing instance of the class in memory, method When , the object is also an instance of the class, and with this instance, the instance method can be called actively. look at the codewraper
Frida$new()newJava.chooseHookthis

function main(){
    
    
    Java.perform(function(){
    
    
        //Fourth
        Java.choose("com.dta.test.frida.activity.FourthActivity",{
    
    
            onMatch: function(ins){
    
    
                console.log(ins)
                ins.next()
            },onComplete: function(){
    
    
                console.log("Search Completed!")
            }
        })

    })
}
setImmediate(main)

Fifth level

Inspection point: Fridathe construction of the array

Analysis: When we want to actively call a method, the most important thing is how to construct the parameters of this method. For example, this topic is also a checkmethod. Its parameter is an array, which is judged internally. If the length of the array is judged to be 5, it can pass, otherwise the prompt fails. So we need Hook checka method, just modify its parameter to an array of length 5 String. The construction of the array Fridaalso provides API: Java.array, the first parameter is the data type to be constructed, the basic data type can be written directly, for example int char, and the complex data type needs to fill in the fully qualified class name, for example java.lang.String, see the code

function main(){
    
    
    Java.perform(function(){
    
    
        //Fifth
        var strarr = Java.array("java.lang.String",["d","t","a","b","c"])
        Java.use("com.dta.test.frida.activity.FifthActivity").check.implementation = function(arr){
    
    
            arr = strarr
            this.check(arr)
        }

    })
}
setImmediate(main)

Sixth hurdle

Inspection point: Fridacustom class

Analysis: This question is a more interesting one. It was not considered when the question was written at the time ClassLoader. I only found out that this question has pitfalls when I was doing it. Let's solve this problem according to the normal way of thinking: just a few lines of code, first pass Class.forNameto load a class, get com.dta.test.frida.activity.RegisterClassthis Classobject, then call getDeclaredMethodthe method to get the next method in this class, and finally call this nextmethod, get After reaching the return value, call booleanValuethe method to get a booleantype of result, and use this result to choose whether to enter the next level or prompt failure

In fact, the above analysis process is also relatively easy to understand, which is equivalent to the fact that we need a class, as follows

package com.dta.test.frida.activity;

public class RegisterClass{
    
    

    public RegisterClass{
    
    

    }

    public boolean next(){
    
    
        return true;
    }

}

The topic is to call the method RegisterClassin this class , so we build such a class nextthrough the Fridaprovided API according to this topicJava.registerClass

function main(){
    
    
    Java.perform(function(){
    
    
        //Sixth
        var RegisterClass = Java.registerClass({
    
    
            name: "com.dta.test.frida.activity.RegisterClass",
            methods: {
    
    
                next: {
    
    
                    returnType: "boolean",
                    argumentTypes:[],
                    implementation: function(){
    
    
                        return true
                    }
                }
            }
        })
    })
}
setImmediate(main)

We used Frida to help us construct the classes we need, and they will be automatically loaded into the memory, but we found that the problem is still not passable. At this time, we have to think about where the problem lies? If you think from the top down, Class.forNameis the execution of the first line successful? Did the method on the second line getDeclaredMethodfind the method we need next? Is the return value obtained successfully?

With these questions in mind, let's investigate. Because catchthe exception in the block in the title has not been processed print, we use other methods to troubleshoot. Check the simple items first, getDeclaredMethodhave you found the method? We can call the next method by using our custom object RegisterClass, and find that it can be printed, and the result isnewtrue

console.log(RegisterClass.$new().next())
// true

Then it means that our custom Classis successfully created and can be used normally. Then the question arises in the first one, Class.forNameis the loading of this class successful? In fact, it was unsuccessful. When we click the next level button, the exception thrown here is actually that we cannot find the custom class ClassNotFoundExceptionwe use . FridaWhere is the reason? let's look down

First, let's look at Class.forNamethe process

@CallerSensitive
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    
    
    return forName(className, true, VMStack.getCallingClassLoader());
}

The overload that is called internally forName, note that the third parameter isVMStack.getCallingClassLoader

/**
* Returns the defining class loader of the caller's caller.
*
* @return the requested class loader, or {@code null} if this is the
*         bootstrap class loader.
*/
@FastNative
native public static ClassLoader getCallingClassLoader();

It is a nativemethod, the translation of the comment is to return the request class loader, if it nullis returned bootstrap class loader, then what we call this method here is the SixthActivitysame asClassLoader

This method has only three parameters, the first one is className, ours classNameis correct, so we have to ClassLoaderstart from scratch to solve this problem. ClassLoaderThe following knowledge will be supplemented

JVMclass loader

  • JVMThe class loader includes three types:

    • Bootstrap ClassLoader(Bootstrap class loader): C/C++The loader implemented by the code is used to load the specified JDKcore class library, such as java.lang.*, java.util.* and other system classes. JavaThe startup of the virtual machine is through Bootstrap, which cannot be obtained ClassLoaderin it, and is only responsible for the loaded classes.Java/lib
    • Extensions ClassLoader(Extended class loader): JavaThe implementation class in is ExtClassLoader, which provides functions other than system classes, can Javabe obtained in it, and is responsible for loading /lib/extthe next class.
    • Application ClassLoader(Application class loader): JavaThe implementation class in is AppClassLoader, which is the class loader that we have the most contact with. The code written by developers is loaded by it by default, and ClassLoader.getSystemClassLoaderit is what is returned.
    • To customize the class loader, you only need to java.lang.ClassLoaderimplement your own class loader by inheriting the class.

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-u6n1o1lO-1628489892248)(http://www.dtasecurity.cn:20080/tmp/pic/08.png "ClassLoader")]

We have several ClassLoaderinheritance relationships in the figure, and the red arrow describes the parental delegation mechanism. When our custom class loader wants to load a class, it will not think about loading this class first, but will first ask the Applicationclass loader, can you help me load this class? ApplicationThe class loader said, I still have your grandfather's class loader on it, and that's it, until Bootstrapthe class loader is found, if Bootstrapthe class loader can't load this class, then it will reverse and let its own subclass find a way to handle the loading. If the upper-level class loader can load the class, it will be directly loaded successfully. To put it simply, every son is unwilling to work, and every time there is work, his father will do it. Until the father says that I can't do it, let the son find a way to complete it. This is the parental delegation mechanism.

What are the benefits of the parental delegation mechanism?

  • Security: We already know that Bootstrapthe class loader will load the system class library, for example, it loads a Stringclass, and our custom class loader wants to load an identical class, it must not work, because the upper layer has already loaded The class cannot be reloaded in the lower layer, so the system core library can be protected from tampering.
  • Efficiency: This is how to ensure that a class can only be loaded once, will not be loaded repeatedly, and can directly read the loaded ones Class.

when the class is loaded

  • Implicit loading: Developers don't deliberately want to load a class, or they don't know the concept of class loading
    • Example of creating a class
    • Access static variables of a class
    • Calling a static method of a class
    • Use reflection to force the creation of java.lang.Classobjects corresponding to a class or interface
    • Initialize a subclass of a class
  • Explicit loading:
    • LoadClassload using
    • forNameload using

Androidclass loader in the system

AndroidThe class loader of the platform is not Androidunique, but Javainherited from it, so what is the difference between Androidthe class loader and the one in it? JavaLet's introduce them one by one:

  • ClassLoader: an abstract class, all class loaders inherit it directly or indirectly
  • BootClassLoader: Preload the commonly used class loader, which is a singleton mode. JavaUnlike in , BootStrapClassLoaderit is not Cimplemented by the code, but by Javathe code
  • BaseDexClassLoaderIt is PathClassLoaderthe parent class of DexClassLoader, InMemoryDexClassLoader, and the main logic of class loading is being BaseDexClassLoadercompleted.
  • SecureClassLoaderInherit the abstract class of the class ClassLoader, expand the class ClassLoaderclass, add the function of the class permission, and strengthen the security. Its subclass URLClassLoaderuses URLthe path to load the class from the network resource
  • PathClassLoaderIt is Androidthe class loader used by default, and the classes apkin one Activityare loaded by it
  • DexClassLoaderdexCan load , jar, apk, zipfiles in any directory , which is PathClassLoadermore flexible thandex
  • InMemoryDexClassLoaderis loaded directly from memory dex, which was Android8.0introduced later

Note: The classes of the commonly used Framworklayers in the system are all loaded BootClassLoaderby the source . The classes of APPthe system used to develop a system APIare all loaded by it, and APPthe classes in the system are all PathClassLoaderloaded by the source.

Going back to the sixth level, in fact, the root cause is that the one used to Fridaload our custom class is not the same one , so it will appear when we use it to load . Knowing the problem, we can solve this problem. The following provides Two solutions, thanks to the solution provided by Simp1erRegisterClassClassLoaderSisthActivityPathClassLoaderClassLoaderPathClassLoaderClassNotFoundException

  • The first way of thinking: the parental delegation mechanism

Since we can't load it with the default one PathClassLoader, we know that its loading process is first loaded through the parent loader, that is, it will be called to BootClassLoaderload our target class, and of course BootClassLoaderit cannot be loaded successfully, so we can do it in PathClassLoaderthe BootClassLoadermiddle Insert ours RegisterClass, ClassLoaderand it can change from PathClassLoader-> to -> of -> , so that the class that was originally used to load our class will be entrusted to the father to load first, and the loading will fail, and finally we will load it, and this time can be completed loaded. Insertion is also very simple, because one of the middle marks is the father of the other , which is represented by using, we can directly modify this value, see the first implementation belowBootClassLoaderPathClassLoaderRegisterClassClassLoaderBootClassLoaderPathClassLoaderPathClassLoaderBootClassLoaderRegisterClassClassLoaderClassLoaderClassLoaderClassLoaderparent

function main(){
    
    
    Java.perform(function(){
    
    
        //Sixth
        var RegisterClass = Java.registerClass({
    
    
            name: "com.dta.test.frida.activity.RegisterClass",
            methods: {
    
    
                next: {
    
    
                    returnType: "boolean",
                    argumentTypes:[],
                    implementation: function(){
    
    
                        return true
                    }
                }
            }
        })
        var targetClassLoader = RegisterClass.class.getClassLoader()
        Java.enumerateClassLoaders({
    
    
            onMatch: function(loader){
    
    
                try{
    
    
                    if(loader.findClass("com.dta.test.frida.activity.SixthActivity")){
    
    
                         // PathClassLoader
                         var PathClassLoader = loader
                         var BootClassLoader = PathClassLoader.parent.value
                         PathClassLoader.parent.value = targetClassLoader
                         targetClassLoader.parent.value = BootClassLoader
                    }
                    
                }catch(e){
    
    

                }
                
            },onComplete: function(){
    
    
                console.log("Completed!")
            }
        })
    })
}
setImmediate(main)
  • The second way of thinking: the method Hook Classof the class forName, you can modify the third parameter to ours RegisterClass, ClassLoaderso that Class.forNameit can be loaded correctly
function main(){
    
    
    Java.perform(function(){
    
    
        //Sixth
        var RegisterClass = Java.registerClass({
    
    
            name: "com.dta.test.frida.activity.RegisterClass",
            methods: {
    
    
                next: {
    
    
                    returnType: "boolean",
                    argumentTypes:[],
                    implementation: function(){
    
    
                        return true
                    }
                }
            }
        })
        var targetClassLoader = RegisterClass.class.getClassLoader()
        var Class = Java.use('java.lang.Class')
        Class.forName.overload('java.lang.String', 'boolean', 'java.lang.ClassLoader').implementation = function (str, init, loader) {
    
    
            console.log('loader', loader)
            console.log('className', str)
            console.log('iniit', init)
            return this.forName(str, init, targetClassLoader)
        }
    })
}
setImmediate(main)

Seventh pass

Paste the source code of the seventh level below

package com.dta.test.frida.activity;

import android.view.View;
import com.dta.test.frida.R;
import com.dta.test.frida.base.BaseActivity;
import com.dta.test.frida.base.Level;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import dalvik.system.InMemoryDexClassLoader;

public class SeventhActivity extends BaseActivity {
    
    
    private Class<?> MyCheckClass;

    @Override
    protected String getDescription() {
    
    
        return getString(R.string.seventh);
    }

    @Override
    public void onClick(View v) {
    
    
        super.onClick(v);
        try {
    
    
            loadDex();
            Method check = MyCheckClass.getDeclaredMethod("check");
            Object handle = MyCheckClass.newInstance();
            boolean flag = (boolean) check.invoke(handle);
            if (flag){
    
    
                gotoNext(Level.Eighth);
            }else {
    
    
                failTip();
            }
        } catch (Exception e) {
    
    
            failTip();
        }
    }

    private void loadDex() {
    
    
        if (MyCheckClass != null){
    
    
            return;
        }
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    
    
            try {
    
    
                InputStream is_dex = getAssets().open("mycheck.dex");
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] bytes = new byte[1024];
                int len;
                while ( ( len = is_dex.read(bytes,0,bytes.length) ) != -1 ){
    
    
                    bos.write(bytes,0,len);
                }
                bos.flush();

                ByteBuffer buffer = ByteBuffer.wrap(bos.toByteArray());

                InMemoryDexClassLoader classLoader = new InMemoryDexClassLoader(buffer, getClassLoader());

                MyCheckClass = classLoader.loadClass("com.dta.frida.MyCheck");

            }catch (Exception e){
    
    
                e.printStackTrace();
                failTip();
            }
        }
    }

    @Override
    protected String getActivityTitle() {
    
    
        return "第七关";
    }
}

Inspection point: FridaswitchClassLoader

Analysis: This question is to load a dexfile from the outside, then reflectively load com.dta.frida.MyCheckthe class, and call the method in it check. And we want to use the MyCheck class in Fridathe middle , it is not possible directly, it is also because of the problem, now that we have understood , this topic is very simple, we only need to learn how to switch in the middle , use to enumerate , Then judge which one is what we want, and then assign it to what we need , let’s look at the codeHookuseClassLoaderClassLoaderFridaClassLoaderJava.enumerateClassLoadersClassLoaderClassLoaderJava.classFactory.loaderloader

function main(){
    
    
    Java.perform(function(){
    
    
       //Seven
        Java.enumerateClassLoaders({
    
    
            onMatch: function(loader){
    
    
                try{
    
    
                    if(loader.findClass("com.dta.frida.MyCheck")){
    
    
                        console.log("Found!")
                        Java.classFactory.loader = loader
                    }
                }catch(e){
    
    
                    
                }
            },onComplete: function(){
    
    
                console.log("Completed!")
            }
        })

        Java.use("com.dta.frida.MyCheck").check.implementation = function(){
    
    
            return true
        }
    })
}
setImmediate(main)

Summarize

This article mainly introduces the usage of Frida Hook Java layer. The APIs are dead, but we need to master them flexibly and use different API combinations according to different purposes to achieve our goals. These seven topics are also relatively basic. content, because Android's Java is inherently very simple. Of course, it is necessary to know why, so in the sixth question, the content of ClassLoader is explained, so that we can understand the essence through the problem, so that we can improve.

To download the APP practice questions, please scan the QR code to visit:

Guess you like

Origin blog.csdn.net/u010559109/article/details/119537034