Grab’n Run, a simple and effective Java Library for Android projects to secure dynamic code loading.

Overview

Logo

Research paper

We present the findings of this work in a research paper:

Grab'n Run: Secure and Practical Dynamic Code Loading for Android Applications
Luca Falsina, Yanick Fratantonio, Stefano Zanero, Christopher Kruegel, Giovanni Vigna, Federico Maggi.
In Proceedings of the Annual Computer Security Applications Conference (ACSAC). Los Angeles, CA December, 2015 [PDF] [Bibtex]

If you use Grab'n Run in a scientific publication, we would appreciate citations to the previous paper.
Please use this Bibtex entry:

@InProceedings{falsina15:grabandrun,
  author = {Luca Falsina and Yanick Fratantonio and Stefano Zanero and Christopher Kruegel and Giovanni Vigna and Federico Maggi},
  title = {{Grab'n Run: Secure and Practical Dynamic Code Loading for Android Applications}},
  booktitle = {Proceedings of the Annual Computer Security Applications Conference (ACSAC)},
  month = {December},
  year = {2015},
  address = {Los Angeles, CA}
}

News

  • 10/10/2015 - The repackaging tool is now online. Use it to patch automatically your applications to use Grab'n Run APIs.
  • 01/17/2015 - Grab'n Run is now available on JCenter
  • 01/16/2015 - Grab'n Run project migrates to Android Studio, the official IDE for Android application development. However, you can still use the library also with your ADT projects! (see below the "Quick Setup" section for further details)
  • 11/26/2014 - Grab'n Run is on line!

Introduction

Grab’n Run (aka GNR) is a simple and effective Java Library that you can easily add to your Android projects to perform secure dynamic class loading operations over standard DexClassLoader.

Previous research has shown that many applications often need to perform dynamic class loading to implement, for example, non-invasive self-update features. However, research has also shown that it is really challenging to safely implement these features. This is of particular importance as, in this context, one single mistake could open the application (and, therefore, the entire device) to serious security vulnerabilities, such as remote code execution.

The main goal of Grab's Run is to offer an alternative to the native Android APIs, and its design enforces that even the most inexperienced developer cannot perform well-known, serious mistakes.

For a brief presentation of the library and some of its features you can give a look at these slides, while if you prefer a more structured and complete description with set up information, tutorials, examples, tips&tricks, and a full presentation of the API you should definitely check the documentation.

If you desire to suggest new features, improvements, criticisms or whatever, I would be more than glad to hear any kind of constructive feedback :D
You can contact me either by dropping an email at [email protected] or by pinging me on Twitter @lfalsina.

Main features

Securely load code dynamically into your Android application from APK containers or JAR libraries translated to be executable by both the Dalvik Virtual Machine (DVM) and the Android Runtime (ART) (don't worry a section of the docs explains step-by-step how to do it).

  • JAR and APK containers can be either already stored on the device or automatically fetched from remote locations by GNR.
  • Retrieved containers signatures are compared against a valid developer certificate. Only containers that are correctly signed are allowed to have their classes loaded dynamically. This ensures integrity and developer authentication on all the retrieved containers.
  • Developer certificates are retrieved from remote locations securely and cached on the mobile phone for future verifications.
  • Cached classes, containers and certificates used for the signature verification are stored into application-private folders. This prevents your application from code injection attacks at runtime.
  • GNR implements an effective caching system that speeds up its execution and at the same time enables it to work in most cases also when no connectivity is available.
  • Transition to GNR is smooth for the application developer since its API where thought to be as close as possible to the standard API provided by the Android framework.
  • When many containers are provided as sources for class loading, Grab'n Run performs a concurrent multi-thread signature verification in order to limit the performance overhead.
  • GNR helps the application developer to implement silent updating on remote third-party libraries in a secure and concise way.

Quick Setup

This section explains how to setup Grab'n Run as a library for your Android applications.

1. Include library

a. Android Studio (AS)
  • Modify the build.gradle file in the app module of your Android project by adding the following compile line in the dependencies body:
dependencies {
    // Grab'n Run will be imported from JCenter.
    // Verify that the string "jcenter()" is included in your repositories block!
    compile 'it.necst.grabnrun:grabnrun:1.0.4'
}
  • Resync your project to apply changes.
b. Android Development Tool (ADT)
  • Download JAR
  • Put the JAR in the libs subfolder of your Android project

2. Android Manifest

Modify the Android Manifest of your application by adding a couple of required permissions:

<manifest>
	
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	
	<uses-permission android:name="android.permission.INTERNET" />
	
	<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	...
manifest>

Quick example of use

This quick use case gives you a taste on how to use GNR once you have added it to your project.

1. Create a key pair to sign your code and export your developer certificate

  • Open a terminal and type the following command to generate a keystore and a keypair:
$ 	keytool -genkey -v -keystore my-tests-key.keystore -alias test_dev_key 
	-keyalg RSA -keysize 2048 -validity 10000
  • Next export the public key into a certificate that will be used to verify your library code before dynamically loading it:
$	keytool -exportcert -keystore my-tests-key.keystore -alias test_dev_key 
	-file certificate.pem
  • You should now see in the folder a certificate file called certificate.pem

2. Publish your developer certificate on line at a remote location which uses HTTPS protocol

You can publish the certificate wherever you like as long as HTTPS protocol is used and everyone can access this location from the web. As a test example you could store the certificate.pem in your "Public" Dropbox folder and then retrieve the associated public link, which could be for example something like "https://dl.dropboxusercontent.com/u/00000000/certificate.pem". Note this URL down, you will need it soon.

3. Export an unsigned container and sign it with your developer key

Let's say that in your IDE (i.e., the Android Development Tools (ADT)) you have an Android project called "LoaderApp" from which you want to load some of its classes dynamically in another project.

  • In the ADT Package Explorer right click on "LoaderApp" -> Android Tools -> Export Unsigned Application Package... Screenshot
  • Next select the same folder where you have previously saved the keystore and the keypair as the destination folder and press OK.
  • Open a terminal which points to the destination folder and sign the apk container with the previously created key:
$	jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 
	-keystore my-tests-key.keystore LoaderApp.apk test_dev_key
  • Finally align the apk container to optimize access time to its resources:
$	<path_to_your_sdk>/sdk/build-tools/<sdk_version_number>/zipalign -v 4 
	LoaderApp.apk LoaderAppAligned.apk

P.S. Step 3 can also be directly performed by means of your favorite IDE. In ADT you would have to select the option "Android Tools -> Export Signed Application Package..." and, when it is required, navigate to the location of your keystore and inserting its password, the key id and the key password. On the other hand in Android Studio the signature process can be automatized by setting up a proper signing configuration as described here.

4. Publish the signed and aligned version of the source container

Once you have obtained LoaderAppAligned.apk, you need to make also this resource available on line. Notice that, in this case, both remote locations that use HTTP or HTTPS protocols are fine as long as they are accessible from the web. Again, as an example, you can store the container in your "Public" Dropbox folder and get back a public URL like "https://dl.dropboxusercontent.com/u/00000000/LoaderAppAligned.apk".

5. Set up dynamic code loading with GNR in the application

In the end, it is time to set up a SecureDexClassLoader instance to fetch your remote container and developer certificate, store it in a safe place and perform a signature verification before dynamically loading your code.

Copy and paste the code below in one of the Activity in your target Android project, where you have already imported GNR, to dynamically and securely load an instance of the class "com.example.MyClass":

MyClass myClassInstance = null;
jarContainerPath = "https://dl.dropboxusercontent.com/u/00000000/LoaderAppAligned.apk";

try {
	Map<String, URL> packageNamesToCertMap = new HashMap<String, URL>();
	packageNamesToCertMap.put("com.example", new URL("https://dl.dropboxusercontent.com/u/00000000/certificate.pem"));

	SecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);
	SecureDexClassLoader mSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(	jarContainerPath, 
												null, 
												getClass().getClassLoader(),
												packageNamesToCertMap);
		
	Class loadedClass = mSecureDexClassLoader.loadClass("com.example.MyClass");

	// Check whether the signature verification process succeeded
	if (loadedClass != null) {

		// No security constraints were violated and so
		// class loading was successful.
		myClassInstance = (MyClass) loadedClass.newInstance();
				
		// Do something with the loaded object myClassInstance
		// i.e. myClassInstance.doSomething();
	}

} catch (ClassNotFoundException e) {
	// This exception will be raised when the container of the target class
	// is genuine but this class file is missing..
	e.printStackTrace();
} catch (InstantiationException e) {
	e.printStackTrace();
} catch (IllegalAccessException e) {
	e.printStackTrace();
} catch (MalformedURLException e) {
	// The previous URL used for the packageNamesToCertMap entry was a malformed one.
	Log.e("Error", "A malformed URL was provided for a remote certificate location");
}

Et voilà.. now you have an instance of "MyClass" loaded in a secure way at run time!

Next steps :)

  • If you want to learn how to use Grab'n Run I suggest to start from the tutorial and then moving on by analyzing the example application.
  • If you are interested in understanding what are the security threats of improper dynamic code loading fixed by GNR check out the security resume.
  • If you would like to implement cool features of GNR like silent updates, handling more containers, concurrent code loading or dynamically loading JAR libraries in your applications you should give a look at the complementary topics.
  • You may also need to consult the JavaDoc-like API documentation.
  • Finally, you may want to convert automatically your applications to use Grab'n Run APIs for secure dynamic code loading. Give a try at the repackaging tool.

License

Grab'n Run is released under the Apache license. Check the COPYRIGHT file for further details.

Android Arsenal

Comments
  • Cannont load classes which come from different package names which have a common relevant prefix

    Cannont load classes which come from different package names which have a common relevant prefix

    Hi, First of all, thanks for you excellent work on this library. this is really usefull. :) I'm trying to use Grab-n-Run to dynamically different classes on one apk. In my apk i have a structure like this:

    com
    |-->impecabel
          |-->playerwrapper
                 |-->Player.class
                 |-->utils
                       |-->CustomSettings.class
    

    Based on the sample app provided i made the following code:

    protected void setUpProfileSecureDexClassLoader() {
    
            // First check: this operation can only start after
            // that the proper button has just been pressed..
            String classNameInAPK = "com.impecabel.playerwrapper.Player";
            //String classNameInAPK = "com.impecabel.playerwrapper.utils.CustomSettings";
    
            Log.d(TAG_MAIN, "Setting up SecureDexClassLoader for profiling..");
            // For the profiling test with SecureDexClassLoader I consider the worst performance scenario and so:
            // 1. The container is in a remote location and must be downloaded first
            // 2. The certificate, as well it's not cached but found at a remote URL and then imported
            // 3. The container in the end is correct so the full signature verification step is performed
            // 4. After the loading operation, the method to wipe out both the certificate and the container is invoked
    
            // Create an instance of SecureLoaderFactory..
            // It needs as a parameter a Context object (an Activity is an extension of such a class..)
            SecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);
    
            SecureDexClassLoader mSecureDexClassLoader;
    
            // Creating the apk paths list (only one path to a remote container in this case)
            String listAPKPaths = "https://dl.dropboxusercontent.com/u/XXXXXXXXX/Player.apk";
    
            // Filling the associative map to link package name and certificate..
            Map<String, URL> packageNamesToCertMap = new HashMap<String, URL>();
            // 1st Entry: valid REMOTE certificate location
            try {
                packageNamesToCertMap.put("com.impecabel.playerwrapper", new URL("https://dl.dropboxusercontent.com/u/XXXXXXXXX/cert.pem"));
            } catch (MalformedURLException e) {
                // An invalid URL was provided for remote certificate location
                Log.e(TAG_MAIN, "Invalid URL for remote certificate location!");
            }
    
            // Instantiation of SecureDexClassLoader
            mSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(  listAPKPaths,
                    null,
                    ClassLoader.getSystemClassLoader().getParent(),
                    packageNamesToCertMap);
    
    
            try {
    
                // Attempt to load dynamically the target class..
                Class<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);
    
                // Immediately wipe out all the cached data (certificate, container)
                mSecureDexClassLoader.wipeOutPrivateAppCachedData(true, true);
    
                if (loadedClass != null) {
    
    
                    Log.i(TAG_MAIN, "Found valid class: " + loadedClass.getSimpleName() + " : Success!");
    
                    toastHandler.post(new Runnable() {
    
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this,
                                    "SecureDexClassLoader was successful! ",
                                    Toast.LENGTH_SHORT).show();
                        }
    
                    });
    
                } else {
    
                    Log.w(TAG_MAIN, "This time the chosen class should pass the security checks!");
                }
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                Log.w(TAG_MAIN, "Class should be present in the provided path!!");
            } 
        }
    

    When i try to load com.impecabel.playerwrapper.Player everything works fine and the class is loaded, but when i try to load the com.impecabel.playerwrapper.utils.CustomSettings it doesn't work and i get "This time the chosen class should pass the security checks!" on the logs. Lookint at the documentation i think this should work. I'm i missing something? If i put all the classes under com.impecabel.playerwrapper the both load.

    Can you help? Do you need any logs?

    Tanks in advance.

    enhancement 
    opened by jrocharodrigues 14
  • java.lang.UnsatisfiedLinkError: Library libmy.so not found when dynamically loading library with native code

    java.lang.UnsatisfiedLinkError: Library libmy.so not found when dynamically loading library with native code

    Hi. First of all, thanks for a great library to securely load classes.

    When trying to implement provided in README example, I decided to use native code in my loadable library. But I didn't find a way to specify the correct path to the native library (since factory method createDexClassLoader in SecureLoaderFactory takes library path as the second argument). Should I download library separately (I used stored in Dropbox implementation)? How to do it properly (since FileDownloader is protected, it is not accessible from my code) and where to store this library?

    Thanks.

    opened by geaden 6
  • The Dex Path String Must Not Be Empty

    The Dex Path String Must Not Be Empty

    I am getting this error while trying to load a class

    java.lang.IllegalArgumentException: The dex path string must not be empty
                                                                                 at com.google.common.base.Preconditions.checkArgument(Preconditions.java:122)
                                                                                 at it.necst.grabnrun.DexPathStringProcessor.<init>(DexPathStringProcessor.java:27)
                                                                                 at it.necst.grabnrun.SecureDexClassLoader.generatePackageNameToContainerPathMap(SecureDexClassLoader.java:206)
                                                                                 at it.necst.grabnrun.SecureDexClassLoader.<init>(SecureDexClassLoader.java:185)
                                                                                 at it.necst.grabnrun.SecureLoaderFactory.createDexClassLoader(SecureLoaderFactory.java:292)
                                                                                 at it.necst.grabnrun.SecureLoaderFactory.createDexClassLoader(SecureLoaderFactory.java:204)
                                                                                 at com.adroyte.stellar.Stellar.loadClass(Stellar.java:107)
                                                                                 at com.adroyte.stellar.Stellar.execute(Stellar.java:58)
    

    Code is here:

    	private boolean loadClass(JSONArray args, CallbackContext callbackContext) throws JSONException {
    		String classPath = args.getString(0);
    		Log.d("Stellar: Class Path", classPath);
    		Log.d("Stellar: Apk Path", jarContainerPath);
    		if(initialized){
    			try{
    				SecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this.cordova.getActivity());
    				mSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(jarContainerPath, null, this.getClass().getClassLoader(), packageNamesToCertMap);
    				loadedClass = mSecureDexClassLoader.loadClass(classPath);
    				// Check whether the signature verification process succeeds
    				if (loadedClass != null) {
    					String type = classPath;
    					// Class loading was successful and performed in a safe way.
    					classInstance = loadedClass.newInstance();
    					classLoaded = true;
    					return classLoaded;
    				}else{
    					callbackContext.error("Class was not loaded!");
    					return false;
    				}
    			} catch (ClassNotFoundException e) {
    					// This exception will be raised when the container of the target class
    					// is genuine but this class file is missing..
    					callbackContext.error("Class not found!");
    					e.printStackTrace();
    			} catch (InstantiationException e) {
    					e.printStackTrace();
    					callbackContext.error("Library is not initialized!");
    			} catch (IllegalAccessException e) {
    					e.printStackTrace();
    					callbackContext.error("Library is not initialized!");
    			}
    		}else {
                callbackContext.error("Library is not initialized!");
    			return false;			
    		}
    
    		return false;
    	}
    
    opened by darmie 5
  • Why isn't the library growing anymore?

    Why isn't the library growing anymore?

    This library seemed very interesting to me and I wanted to know why the library is not updated. What is the reason for this? Maybe there are some problems? I'm wondering if this library will work now?

    opened by Siletskiy 1
  • Unable to instantiate activity

    Unable to instantiate activity

    java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{it.polimi.poccodeloading/com.labinapp.demo.MainActivity}: java.lang.ClassNotFoundException: Didn't find class "com.labinapp.demo.MainActivity" on path: DexPathList[[zip file "/data/app/it.polimi.poccodeloading-1/base.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_dependencies_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_0_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_1_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_2_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_3_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_4_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_5_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_6_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_7_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_8_apk.apk", zip file "/data/app/it.polimi.poccodeloading-1/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]

    I was trying to open a apk by dexclassloader but i am getting error. I am not sure what exactly is the problem can you help me regarding this error. And is it possible to access resource files of other apk by using this library.

    opened by anandpurushottam 0
Owner
Luca Falsina
Luca Falsina
A Paper fork with secure feature seed based on Secure Seed mod by Earthcomputer

GitHub | Download | Discord Matter Matter is a Paper fork that currently only adds what we call a secure feature seed. Based on Secure Seed mod by Ear

Plasmo 51 Dec 26, 2022
MiHawk 🦅👁️ is simple and secure 🔒 Android Library to store and retrieve pair of key-value data with encryption , internally it use jetpack DataStore Preferences 💽 to store data.

MiHawk MiHawk ?? ??️ is simple and secure ?? Android Library to store and retrieve pair of key-value data with encryption , internally it use jetpack

Nedal Hasan Ibrahem 5 Sep 3, 2022
Appdbg - make it possible to run android dex file in original Java Virtual Machine

Appdbg - make it possible to run android dex file in original Java Virtual Machine

null 137 Dec 20, 2022
Secure your REST APIs with Spring Security, Resource and Authorization Server from zero to JWT

Secure REST APIs with Spring ./mvnw RTFM YouTube: Spring Security Patterns YouTube: Spring Security 5.5 From Taxi to Takeoff Official Apache Maven doc

Maksim Kostromin 1 Dec 5, 2021
Secure Secret Keys With Kotlin

Secure-Secret-Keys How to Secure Secrets ?? in Android CMake — Native Libraries

Muhammad Shams 4 Jan 21, 2022
BlackDex is an Android unpack tool, it supports Android 5.0~12 and need not rely to any environment. BlackDex can run on any Android mobile phones or emulators, you can unpack APK File in several seconds.

BlackDex is an Android unpack tool, it supports Android 5.0~12 and need not rely to any environment. BlackDex can run on any Android mobile phones or emulators, you can unpack APK File in several seconds.

null 4.3k Jan 2, 2023
enjarify 8.6 0.0 L5 Python Enjarify is a tool for translating Dalvik bytecode to equivalent Java bytecode. This allows Java analysis tools to analyze Android applications.

Note: This repository may be out of date. Future development will occur at https://github.com/Storyyeller/enjarify. Introduction Enjarify is a tool fo

Google 2.7k Jan 8, 2023
CLI tool for decompiling Android apps to Java. It does resources! It does Java! Its real easy!

Easy-as-pie Android Decompiler Why One stop shop I got pretty tired of decompiling Android apps with a bunch of steps that I had to remember all the t

Alex Davis 619 Dec 27, 2022
Signal Protocol library for Java/Android

Overview A ratcheting forward secrecy protocol that works in synchronous and asynchronous messaging environments. PreKeys This protocol uses a concept

Signal 1.8k Dec 24, 2022
A Java ePub reader and parser framework for Android.

FolioReader-Android is an EPUB reader written in Java and Kotlin. Features Custom Fonts Custom Text Size Themes / Day mode / Night mode Text Highlight

FolioReader 2.1k Jan 3, 2023
A program analysis tool to find cryptographic misuse in Java and Android.

A program analysis tool to find cryptographic misuse in Java and Android.

null 92 Dec 15, 2022
CRYLOGGER: Detecting Crypto Misuses for Android and Java Apps Dynamically

CRYLOGGER: Detecting Crypto Misuses for Android and Java Apps Dynamically

Luca Piccolboni 139 Dec 12, 2022
Analyze any Android/Java based app or game

ClassyShark Introduction ClassyShark is a standalone binary inspection tool for Android developers. It can reliably browse any Android executable and

Google 7.2k Jan 3, 2023
Smali/baksmali is an assembler/disassembler for the dex format used by dalvik, Android's Java VM implementation

About smali/baksmali is an assembler/disassembler for the dex format used by dalvik, Android's Java VM implementation. The syntax is loosely based on

Ben Gruver 5.7k Dec 27, 2022
TweetNaCl in Java - a port of TweetNaCl-js

TweetNacl in Java: port of tweetnacl-js API/Usage Suggest always use TweetNaclFast implementation Public key authenticated encryption get key pair: Bo

AppNet.Link 40 Nov 10, 2022
Dex to Java decompiler

JADX jadx - Dex to Java decompiler Command line and GUI tools for producing Java source code from Android Dex and Apk files Main features: decompile D

null 32.8k Jan 2, 2023
Java bytecode obfuscator created by x4e.

Binscure Java bytecode obfuscator created by x4e. Usage First, create a config file, (example config here). When you have a config file, run binscure

null 35 Nov 22, 2022
A RSA Cipher implementation for Kotlin/Java

A RSA Cipher implementation for Kotlin/Java.

Lyzev 3 Aug 22, 2022
Soot - A Java optimization framework

Using Soot? Let us know about it! We are regularly applying for funding to help us maintain Soot. You can help us immensely by letting us know about p

Soot Program Analysis Framework 2.5k Jan 2, 2023