Twitter

Entries in Android (2)

Tuesday
Dec162014

Hooking Non-Public Methods With the NDK - Using Mobile Substrate For Android Part 2

One day, you decide to put on the security ninja gloves, open your laptop, plug your android phone and take apart your dear Android application using Part 1 of our tutorial on Android Hooking using Mobile Substrate; and you realize there is something wrong – something is not working. Then you come to the realization that your hook can’t access non-public methods. This post will discuss an option for hooking non-public methods by utilizing Mobile Substrate from NDK code.

Getting Started

The application we will be using in this demonstration is a demo application called SuperMath and can be downloaded here. This application will perform various mathematical operations within private functions and display the results to the user. The focus of this post will be hooking into private functions of a class, accessing and modifying the function parameters, and viewing and modifying the function’s return values.

Note: This guide assumes you have a rooted device, have already installed Mobile Substrate onto the device, and have already downloaded the Mobile Substrate API.

In case you haven’t done so yet, you can download the Mobile Substrate APK here: https://play.google.com/store/apps/details?id=com.saurik.substrate

You can also read the following documentation on how to download the Mobile Substrate APIs here: http://www.cydiasubstrate.com/id/73e45fe5-4525-4de7-ac14-6016652cc1b8/

Preparing NDK/JNI Support

After following steps “Setting Up the IDE” and “Adding Permissions to AndroidManifest.xml” from Part 1 of our tutorial, we need to make sure that we have installed and enabled NDK support for the ADT plugin and that the environment variables are set properly. To ensure that we have NDK support available, we will navigate to the ‘About Eclipse’ menu item, click the ‘Installation Details’ button and check for ‘Android Native Development Tools’ under ‘Installed Software’. If an entry for ‘Android Native Development Tools’ is missing then we are missing NDK support and NDK support can be installed from the ADT Plugin Update Site. 

Once the plugin has been installed successfully, ensure that SDK and NDK paths have been properly set in Eclipse Properties. This can be done as follows:

  • Navigate to the ‘Preferences’ menu item, select ‘Android’ preferences, and set SDK path
  • Navigate to the ‘Preferences’ menu item, expand ‘Android’ preferences, select ‘NDK’ preferences, and set NDK path

Adding NDK/JNI Support

Next, we will need to add support for NDK/JNI to our project. This will allow us to use NDK functions to interact with the Android system and applications. In order to add NDK/JNI support, Right Click on project, navigate to the ‘Android Tools’ context menu, and click ‘Add Native Support…’. Once you click the menu item, Eclipse will show you a dialog box and will ask you for the name of your library. You can specify whatever name that pleases you. However, it is a good practice to append suffix ‘.cy’ to help one identify it is a Cydia extension.

 

 

 

 

Once you click Finish, the ADT plugin will process the provided information and generate a new directory ‘jni’ under the project directory and create following two new files –

  •  <libName>.cpp: This file will contain the code for the Substrate hook
  • Android.mk: This is a Makefile file which contains make commands used to build our hook

 

 

Including Substrate Libraries

Next, we need to include the Substrate libraries. The easiest way to do this is to navigate to the ‘/sdk/extras/saurikit/cydia_substrate/’ directory and copy ‘substrate.h’ and navigate to the ‘/sdk/extras/saurikit/cydia_substrate/lib/<ARCH>/’ directory and copy ‘libsubstrate-dvm.so’ file into the jni folder of the project.

 

 

Finding Target Functions

The ‘SuperMath.apk’ contains a private method to perform multiplication (This can be identified by decompiling the application using a utility like Dex2Jar) :

private String mul(int a, int b){
  return "" + (a * b);
}

 

If we recall, the steps outlined in previous tutorial will not allow us to hook into non-public members of a class.  By creating our hooks using NDK code we will be able to overcome this limitation.

Coding a Substrate NDK Hook

We will start by including appropriate header files in the ’.cpp’ file, and calling the ‘MSConfig’ Substrate macro.

#include "substrate.h"
#include <android/log.h>

#define TAG "NDK_HOOK"

/*
 * Decide where we would like to hook
 */
MSConfig(MSFilterExecutable, "/system/bin/app_process") 

 

The first import statement imports substrate headers into the current program to provide us all Substrate functionality. The second import statement imports Android logging headers into the current program to provide us logging features, which will be used later in the program to log information to LogCat.

MSConfig is a Substrate macro that allows developers to configure and ask the Substrate runtime to allow our extension to hook into the application or the library of our choice. MSConfig takes two string constant arguments: the name of a configuration option and a string value.

Macro Signature: MSConfig(configOption, configOptionValueAsString)

Configuration Option

Description

MSFilterExecutable

This tells the Substrate runtime that we would like to hook into an application. If this configuration option is specified as the first argument to the MSConfig macro, the second argument is a string value containing the fully qualified path to the process that we are trying to hook. Usually this argument is set to “/system/bin/app_process” as this process points to zygote but can be other processes as well.  For more information on why we wish to hook zygote, refer to the blogpost by David Ehringer on The Dalvik Virtual Machine Architecture. For this blogpost, we will hook into zygote.


MSFilterLibrary

This tells the Substrate runtime that we would like to hook into a library. If this configuration option is specified as the first argument to the MSConfig macro, the second argument is a string value containing the library that we are trying to hook. If we are trying to hook __android_log, the value of this argument will be “liblog.so”.

 

Then we will call the MSInitialize macro in order for us to write the function definition used to initialize the hook. This is the primary entry point into the application and where we begin to write our hooks.

/*
 * Substrate entry point
 */
MSInitialize
{
  // Let the user know that the extension has been
  // extension has been registered
  __android_log_print(ANDROID_LOG_ERROR, TAG,
      "Substrate initialized.");

// Hook into specified class and call OnClazzLoad // method when hooked MSJavaHookClassLoad(NULL, "me/rahilparikh/SuperMath/ShowMathOps", &OnClazzLoad); }

 

A call is made to the MSJavaHookClassLoad function, which is used to hook into the class specified by the second argument to the function.  A callback function is passed as the third argument which will be called once the ‘ShowMathOps’ object is loaded.

Method Signature: MSJavaHookClassLoad( jniPtr, classNameAsString, referenceToCallbackMethod)

The class name passed to MSJavaHookClassLoad follows the JNI naming convention. So, me.rahilparikh.SuperMath.ShowMathOps is written as me/rahilparikh/SuperMath/ShowMathOps. The OnClazzLoad callback function will now need to be defined as follows:

/*
 * This method should be used to perform various operations
 *  within the class
 */
static void OnClazzLoad(JNIEnv *jniPtr, jclass clazz,
    void *data) {

  // Let user know that the class has been hooked
  __android_log_print(ANDROID_LOG_ERROR, TAG,
      "Hooked into the application.");

  // Search for method
  jmethodID methodMul = jniPtr->GetMethodID(clazz, "mul",
      "(II)Ljava/lang/String;");
  if (methodMul != NULL) {
    // Let user know that we have found the method
    __android_log_print(ANDROID_LOG_ERROR, TAG,
        "mul called.");
    // Hook into method
    MSJavaHookMethod(jniPtr, clazz, methodMul,
        &newMulMethod, &oldMulMethod);
  }
}

 

The onClazzLoad callback function is invoked once the specified object is loaded by the Android device. The first parameter to this function is a JNI Pointer, which will provide us access to various JNI methods. The second parameter to this function is an instance of jclass, which will provide us access to the members of the class.

In onClazzLoad, we call the GetMethodId method to get a reference to the method that we are trying to hook into. Refer to JNI and JNI Types and Data Structures documents to for more information on how to use GetMethodId and other JNI methods. On the next line, we check if an instance of the returned methodId is null or not. If the instance is not null, a Java method that matches our signature was found and we will use MSJavaHookMethod to hook into our desired method.

The MSJavaHookMethod function is used to replace the implementation of the function at runtime. The third parameter contains the reference to the ‘methodId’ that was previously returned by the GetMethodID function. The fourth parameter contains the reference to the new implementation of the ‘mul’ method that will need to be defined within our Substrate code. The fifth parameter will contain a callback reference to the original ‘mul’ method. We will still need to the define a prototype for the function passed as the fifth parameter. Substrate will handle setting the appropriate values once the ‘mul’ method is hooked.

The following walks through how to create the function prototype to the ‘oldMulMethod’ and a definition for the new ‘newMulMethod’ method to include the code we want to execute within the hook.

/*
 * Original method template
 */
static jstring (*oldMulMethod)(JNIEnv *jni, jobject _this, ...);

/*
 * Modified method
 */
static jstring newMulMethod(JNIEnv *jni, jobject _this,
    jint param1, jint param2) {

  // Let user know that the method has been hooked
  __android_log_print(ANDROID_LOG_ERROR, TAG,
      "Hooked into mul.");

  // Print all original parameters
  __android_log_print(ANDROID_LOG_ERROR, TAG, "Param1 : %d",
      param1);
  __android_log_print(ANDROID_LOG_ERROR, TAG, "Param2 : %d",
      param2);

  // Call original method
  jstring originalRetVal = (*oldMulMethod)(jni, _this, param1,
      param2);
  __android_log_print(ANDROID_LOG_ERROR, TAG,
      "Original Answer : %s",
      jni->GetStringUTFChars(originalRetVal, 0));

  // Modify return value of original method
  char *modifiedRetValC = (char*) malloc(3);
  strcpy(modifiedRetValC, "10");
  __android_log_print(ANDROID_LOG_ERROR, TAG,
      "Modified Answer : %s", modifiedRetValC);
  jstring modifiedRetValS = jni->NewStringUTF(
      modifiedRetValC);

  // Be a nice kid, return what you've got
  free(modifiedRetValC);

  // Return modified return value
  return modifiedRetValS;
}

 

The hook will simply log the values of the parameters that were passed by the application to the ‘mul’ method. This allows us to gain visbility into the values being passed at runtime without requiring a debugger. We then call the ‘oldMulMethod’ function which causes the original ‘mul’ method that was defined by the hooked application to be invoked. We could have modified the values for the param1 and param2 variables if we would have liked, but in this scenario we simply pass them to the original method. The return value of the original mul function is then logged as well. Lastly, we then decide to modify the expected return value of the ‘mul’ funtion so that the value of “10” will always be returned.

Configuring Android.mk

Once the hook has been created we need to update the Android.mk Makefile to help the Android build system understand what we are trying to achieve and how it should compile our application. The Android system creates a Android.mk make file with the following as default content –

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := SuperMathHook.cy
LOCAL_SRC_FILES := SuperMathHook.cy.cpp

include $(BUILD_SHARED_LIBRARY)

This would have allowed us to compile a simple ‘Hello World’ application but we will need to make some modifications to account for the Substrate library dependencies. The resulting Makefile should look similar to the following:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE:= substrate-dvm
LOCAL_SRC_FILES := libsubstrate-dvm.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE    := SuperMathHook.cy
LOCAL_SRC_FILES := SuperMathHook.cy.cpp
LOCAL_LDLIBS := -llog
LOCAL_LDLIBS += -L$(LOCAL_PATH) -lsubstrate-dvm

include $(BUILD_SHARED_LIBRARY)

 

For more information on how to configure the Android.mk file, refer to the NDK Programmer’s Guide, which is shipped with the NDK SDK.

Running our Substrate Hook

With the skeleton of the Substrate hook in place, running this code should allow us to hook into the desired class and method. The created Substrate hook can then be run using the instructions found in Part I - Using Mobile Substrate With Android Applications  After the screen flashes, we can run the SuperMath application and monitor the output of Logcat for the tag “NDKHook”. 

Conclusion

JNI does not suffer from the access restrictions that are imposed by Java to access various fields, methods and other members of an application. Therefore, writing hooks using the NDK Substrate APIs can give us low-level access to the application code and allows us to make changes to the existing code at runtime.

One should also remember that some of the method signatures provided here are simplified, minimal and essential for writing a hook. There are instances where these methods can take more arguments but are not specified here as they have been omitted to improve the understanding of the hook writing process and/or they are not required in most cases.

All of the code discussed within this blog post can be found on our Github page here.

 

References/Further Reading

Ehringer, D. (2010, March). The Dalvik Virtual Machine Architecture. Retrieved October 2014, from http://davidehringer.com/software/android/The_Dalvik_Virtual_Machine.pdf

Java Programming Tutorial – Java Native Interface (JNI) . (2014, February). Retrieved October 2014, from https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html

JNI Functions. (n.d.). (Oracle Corp.) Retrieved Oct 2014, from Java SE Documentation: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html

JNI Types and Data Structures. (n.d.). (Oracle Corp.) Retrieved October 2014, from Java SE Documentation: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html

Kumar, A. (2012, October). Understanding Android: Zygote and DalvikVM. Retrieved October 2014, from StackOverflow: http://stackoverflow.com/a/12703292

NDK Programmer’s Guide. (n.d.). Retrieved October 2014, from Android.mk: /ndk/docs/Programmers_Guide/html/md_3__key__topics__building__chapter_1-section_8__android_8mk.html

 

Thursday
Jun192014

Using Mobile Substrate with Android Applications

Android application assessments are a frequent occurrence here at GDS. When our clients do not provide us with source code, we take a black box testing approach which includes intercepting and/or modifying function calls. This can range from parameters passed to a function, function return values, or even logic within a function.

Recently we’ve been utilizing Mobile Substrate as an alternative to the tried and true APK decompile, modify, and recompile methodology. A key advantage being you don’t have to work with smali. 

Aside from the documentation on Mobile Substrate’s home site, there aren’t many posts on the web on how to use it and we’ve encountered a few errors and problems along the way. That being said, this post aims to provide a more in depth walkthrough for those interested in using Mobile Substrate in the most common scenarios.

Post Outline

The outline below illustrates the major topics that will be covered in this post:

  • Hooking functions
  • Viewing function parameters
  • Modifying function parameters
  • Viewing return values
  • Modifying return values

Getting Started

The application we will be using in this demonstration is a demo application called ListLock. This application allows users to password protect a super secret list from prowling eyes.

The techniques in this post will be focused on bypassing ListLock’s lock screen.

Note: This guide assumes you have a rooted device, have already installed Mobile Substrate onto the device, and have already downloaded the Mobile Substrate API.

In case you haven’t done so yet, you can download the Mobile Substrate APK here: https://play.google.com/store/apps/details?id=com.saurik.substrate 

You can also read the following documentation on how to download the Mobile Substrate APIs here: http://www.cydiasubstrate.com/id/73e45fe5-4525-4de7-ac14-6016652cc1b8/

Setting Up the IDE

Note: I will be using the Android Development Toolkit (Eclipse) IDE that is packaged with the Android SDK for creating our Substrate hook.

In order to start building our Substrate hooks, we need to create a new Android application project. The name of the class will be ‘ListBypass’. Uncheck the “Create custom launcher icon” and “Create activity” options and click “Finish”.

 

Adding Permissions to AndroidManifest.xml

The first thing we like to do before coding the actual Substrate hook is adding permissions to the AndroidManifiest.xml file. This is usually the step that we tend to forget whenever we’re developing a Substrate hook, so we prefer to take care of it first.

To do this, we can add the following tag towards the top of the manifest file:

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

Including Substrate Libraries

Next, we need to include the Substrate libraries. The easiest way to do this is to navigate to the ‘android/adt/sdk/extras/saurikit/cydia_substrate/’ directory and drag-and-drop the ‘substrate-api.jar’ file into the libs folder of your project.

Eclipse will prompt you to “Copy files” or “Link to files”. You want to select “Copy files”

Note: One of the biggest pitfalls we see people make is to select the ‘Link to files’ option. While this will stop your IDE from complaining about missing dependencies, this approach will not work when you attempt to load your Substrate hook on the device. This is because the ‘Link to files’ option points to the jar on your local computer. Since the Substrate hook will be running on the device, it will be unable to locate the jar.

Finding Target Function

Before we can start hooking, we need to know what to hook. To do this, we can decompile the ListLock.apk using a tool like Dex2Jar and look through the code until we find the appropriate function. Instructions for decompiling an APK will not be discussed in this post. 

Let’s target the validatePassword() method found in the com.historypeats.listlock.classes.Authenticate class. By looking at the source-code, this method looks to be responsible for allowing a user to log into the ListLock application.

Below is a code snippet of the validatePassword() method:

public boolean validatePassword(String password, String storedPassword){

    if(storedPassword.equals(password)){
        return true;
    }
    return false;
}
Let’s see if we can bypass this using a Substrate hook. We have two attack vectors for accomplishing this:
  • Print out the storedPassword argument
  • Modify the return value

Coding a Substrate Hook

Let’s start off our code by adding some variables and making sure we import our dependencies:

import java.lang.reflect.Method;

import android.util.Log;

import com.saurik.substrate.MS;

 

public class ListBypass {

    private static String TAG = “ListBypass”;

    private static String className = “com.historypeats.listlock.classes.Authenticate”;

}

The ‘TAG’ variable identifies the logging statements that pertain to this Substrate hook. The ‘className’ variable represents the fully qualified class name that contains the method we want to hook. In this case, ‘com.historypeats.listlock.classes.Authenticate’ is the class that contains the ‘validatePassword’ method that we are targeting.

Next we’ll dive into the meat of our Substrate hook. We’ll go ahead and add the essential layout to get started with the hook.

private static String className = "com.historypeats.listlock.classes.Authenticate";

public static void initialize() {

 Log.i(TAG, "Substrate Initialized.");

         MS.hookClassLoad(className, new MS.ClassLoadHook() {

         @SuppressWarnings({ "unchecked", "rawtypes" })

             public void classLoaded(Class<?> _class) {

              Method method;

              final String methodName = "validatePassword";

              Log.i(TAG, "Class Loaded.");

                 try{

                  method = _class.getMethod(methodName, String.class, String.class); // public boolean validatePassword(String password, String storedPassword)

                 }catch(NoSuchMethodException e){

                  method = null;

                  Log.i(TAG, "No such method: " + methodName);

                 }

              if (method != null) {

              Log.i(TAG, "Method Hooked.");

              }

             }

         }); // End MS.hookClassLoad

}

Our IDE tells us that the MS.hookClassLoad() method is looking for a class name, and a hook object. Let’s go over the following essential code segments.

The variable ‘methodName’ contains the name of the method that we want to hook, which in this case is ‘validatePassword’. Next, we assign a value to the method variable by returning a method object from ‘_class.getMethod()’. The first argument to ‘_class.getMethod’ represents the target method to hook (‘validatePassword’). The subsequent arguments represent the target method’s parameter types (parameters accepted by ‘validatePassword’). The ‘validatePassword’ method accepts two string parameters, specifically a password and storedPassword. Note, the names of the parameters are irrelevant for hooking; only the types are important.

//public boolean validatePassword(String password, String storedPassword)

Some other commonly encountered parameter types you can pass to getMethod()include:

  • Byte.class
  • Integer.class
  • Boolean.class
  • Double.class
  • Float.class

Note: The primitive array equivalents work as well. For example, Integer[].class for an array of integers.

Running our Substrate Hook

With the skeleton of the Substrate hook in place, running this code should yield the following debug message in Logcat: “Method Hooked”. In order to test this out, install and run the ListBypass hook from ADT by clicking the ‘Run’ icon in the toolbar or by navigating to the ‘Run’ option at the top of the window and then selecting the ‘Run’ button in the dropdown menu.

This should result in an alert from the Mobile Substrate application about new/updated extensions. We can either click on this alert or open the Mobile Substrate application directly.There will be several options to choose from. Select the “Restart System (Soft)” option.The device will perform a quick restart, which will result in the loading of our Substrate hook. After the screen flashes, we can run the ListLock application and monitor the Logcat output for our debug message.

04-26 22:15:21.536: I/ListBypasss(11604): Substrate Initialized.

04-26 22:16:22.085: I/ListBypasss(12596): Class Loaded.

04-26 22:16:22.085: I/ListBypasss(12596): Method Hooked. 

 


Perfect! Based on the output, we have successfully hooked the method we had originally targeted. Now we can start performing the actions that we want.

First Attack: Print out method arguments

If you recall, the first goal was to discover the argument passed into the method. In this case, we want to find the value of the storedPassword parameter that is being passed into the validatePassword() method. Let’s start by writing the skeleton code with stub placeholders.

public static void initialize() {

 Log.i(TAG, "Substrate Initialized.");

         MS.hookClassLoad(className, new MS.ClassLoadHook() {

         @SuppressWarnings({ "unchecked", "rawtypes" })

             public void classLoaded(Class<?> _class) {

              Method method;

              final String methodName = "validatePassword";

              Log.i(TAG, "Class Loaded.");

                 try{

                  method = _class.getMethod(methodName, String.class, String.class); // public boolean validatePassword(String password, String storedPassword)

                 }catch(NoSuchMethodException e){

                  method = null;

                  Log.i(TAG, "No such method: " + methodName);

                 }

              if (method != null) {

              Log.i(TAG, "Method Hooked.");

              MS.hookMethod(_class, method, new MS.MethodAlteration<Object, Boolean>() {

                public Boolean invoked(Object _class, Object... args) throws Throwable

                {

                //Do Something 

                return invoke(_class, args);

                }

});// End MS.hookMethod

              }

             }

         }); // End MS.hookClassLoad

  }

In the code above, there is a call to the ‘MS.hookMethod()’ from the Substrate API. This method requires the following parameters: the class we hooked, the method we want to modify, and the altered method. What is an altered method? See below for an excerpt from the Substrate documentation:

“An instance of MS.MethodAlteration whose boxed invoked method will be called instead of member. This instance will also be filled in using information from the original implementation, allowing you to use invoke to call the original method implementation.”

This means that we can build a method named ‘invoked’ that will be called in place of the original method. Additionally, our invoked() method will have access to the parameters passed into the original method, allowing us to print out the values of the parameters. We also have the ability to call the original method (with the original parameters) from within our own method to allow the application to function as normal.

The items highlighted mark what the return type of the method we are hooking has. In this case, the ‘validatePassword()’ method that we are hooking has a return type of Boolean. In future hooks, you’ll want to ensure you have the proper return types that match the method you are hooking.

Finally, we need to invoke the original method to allow the application to function as normal. This is accomplished by calling ‘invoke()’, passing in the class and arguments that were originally provided. In this case, we immediately return the value returned by ‘invoke()’. As we’ll see later on, we can also store this value in a variable, modify its contents, etc.

Argument Tampering

Now that we have our own method that will be called in place of the original ‘validatePassword()’, let’s write the code to print out the juicy password arguments.

MS.hookMethod(_class, method, new MS.MethodAlteration<Object, Boolean>() {

    public Boolean invoked(Object _class, Object… args) throws Throwable {

        //Do Something

        String password = (String)args[0];

        String storedPassword = (String)args[1];

        Log.i(TAG, “Arg0 (password): ” + password);

        Log.i(TAG, “Arg1 (storedPassword): ” + storedPassword);

        return invoke(_class, args); // Call original validatePassword() method and pass it the original arguments then return the value to the caller

    }

});// End MS.hookMethod 

In the code above, the first argument represents the password the user entered into the application. This value is stored in the variable ‘password’. The second argument represents the stored password, which is the correct password to login to the list. This value is stored in the variable ‘storedPassword’. Since the args variable is of type Object, we need to remember to cast it to the proper type before we store it. In this case, we cast the values to String objects and store the values in String variables.

Viewing Arguments

We can now run our new Substrate hook following the steps previously outlined. The use of the Log() function should result in the values being printed out to Logcat.

04-26 23:12:39.621: I/ListBypasss(16551): Arg0 (password): wrongPassw0rd

04-26 23:12:39.621: I/ListBypasss(16551): Arg1 (storedPassword): [email protected]!

 

Here we can see that the password we entered, wrongPassw0rd, does not match the correct password, which is [email protected]!. Now that we know the correct password, we can simply log into the application!

Second Attack: Modify Return Values

Thus far, we have written a Substrate hook that will print out the stored password, thereby allowing us to login to the application. What if we didn’t want to type in the correct password every single time, but still be able to login? To accomplish this, we can tweak our Substrate hook to modify the return value of the ‘validatePassword’ method.

Let’s begin by printing out the return value of the ‘validatePassword’ method.

MS.hookMethod(_class, method, new MS.MethodAlteration<Object, Boolean>() {

    public Boolean invoked(Object _class, Object... args) throws Throwable {

        //Do Something

        Boolean originalRetValue = invoke(_class, args);

        Log.i(TAG, "Original Return Value: " + originalRetValue);

       return originalRetValue;

    }

});// End MS.hookMethod

In the code segment above, we use the ‘invoke()’ method to call the original ‘validatePassword()’ method with the original arguments. We then store the return value from ‘validatePassword()’ in a Boolean variable named ‘originalRetValue’, print this value to Logcat, and return it to the caller.

04-26 23:59:18.421: I/ListBypasss(22365): Original Return Value: false

As seen above, the ‘validatePassword()’ method returns ‘false’ when an incorrect password is entered. The next logical step is to modify our Substrate hook to return ‘true’ to the caller instead of the original return value.

MS.hookMethod(_class, method, new MS.MethodAlteration<Object, Boolean>() {

    public Boolean invoked(Object _class, Object... args) throws Throwable {

        //Do Something

        Boolean originalRetValue = invoke(_class, args);

        Boolean newRetValue = true;

        Log.i(TAG, "Original Return Value: " + originalRetValue);

        Log.i(TAG, "New Return Value: " + newRetValue);

        return newRetValue;

    }

});// End MS.hookMethod

Note that instead of returning ‘originalRetValue’, we return ‘newRetValue’, which is always true. This means that regardless of the password entered, we will always gain access to the List. It should be noted that we did not need to run the original method. So there is no need to call invoke() in this situation. We could have easily written: “return true”.

Image of super secret, embarrassing list

 

What if you want to hook multiple methods or classes? Well, if you want to create a single Substrate hook that hooks into multiple classes, all you need to do is create multiple MS.hookClassLoad methods, one after another. Sure, you can engineer a better object-oriented design, but for one or two classes, it may be simpler to just call multiple MS.hookClassLoad methods. Similarly for hooking multiple methods in a single class, you just call getMethod() multiple times with the proper arguments. 

In conclusion, we learned the basics of hooking with Mobile Substrate. From setting up our Substrate code in an IDE to extracting and modifying the data that we want. If you want to practice this, the Substrate code and demo APK can be found the Github link below:

 https://github.com/GDSSecurity/SubstrateDemo