-
Notifications
You must be signed in to change notification settings - Fork 30
Usage
ARTful is an open-source tool that allows users to manipulate the Android runtime. It works by hooking two specified Android methods "targetArtMethod" and "newArtMethod". ARTful then overwrites the associated method data of "targetArtMethod" with the contents of "newArtMethod" causing the new method to unexpectedly execute instead each time the target is invoked. This tool was written and tested on Android13, but will also work on multiple other versions.
The following is a list of important notes for using ARTful and understanding suitable target methods:
- Method signatures must match
- Methods must be static
- Replacement of methods occurs within the application context only
- Target methods can be public or private
- No root required!
The "Source" folder of the ARTful repository contains an example Android application. Simply import this project into Android Studio and begin manipulating the Android Runtime!
The source code for the ARTful library is written in native C++ and can be found at native-lib.cpp while example use and invocations of the library are inside
Add libartful.so to your project and load the library from Java code:
System.loadLibrary("artful");
Then you can declare any native methods that you would like to use in your Java application. The full list of native methods can be found below.
public native void replaceAppMethodByObject(Object targetObject, Object newObject);
public native void replaceAppMethodBySignature(String targetClassName, String targetMethodName, String newClassName, String newMethodName, String methodSignature);
public native void replaceGetRadioVersionByObject(Object newObject);
public native void replaceGetRadioVersionBySignature(String newClassName, String newMethodName);
Passing objects is easier syntactically, but must also catch and handle runtime exceptions. Example:
try {
replaceAppMethodByObject(MainActivity.class.getDeclaredMethod("benignMethod"), MainActivity.class.getDeclaredMethod("newMethod"));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
Example:
replaceAppMethodBySignature("com/app/artful/MainActivity", "benignMethod", "com/app/artful/MainActivity", "newMethod", "()Ljava/lang/String;");
You can replace any static method located inside the Android Framework with your own method inside of your app. A pre-coded list of multiple Android APIs that ARTful can automatically replace can be found below. If you would like to replace one not already added to ARTful, you could instead call the replaceAppMethodBySignature or replaceAppMethodByObject methods and simply pass in the references to the Android Framework method.
try {
replaceGetRadioVersionByObject(MainActivity.class.getDeclaredMethod("newMethod"));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
Once the replacement has occurred, every time Build.getRadioVersion() is invoked, the code from newMethod will be run.
If you want to replace any method in the Android Framework, simply write a new static method in your Java code with a matching signature. Then you can call the replace method and your new code will execute. For example, the declaration of the Build.getRadioVersion() method is defined as follows:
public static String getRadioVersion();
This means that we could replace this with our own method with a matching signature like the following:
public static String newMethod() {
Log.d("ARTful", "I should not execute >:)");
return "lol";
}
Implement a replacement method with a matching signature:
public static int newMethod(String tag, String msg) {
Log.d("ARTful", "I should not execute >:)");
// Your code goes here!
return 1;
}
From Java code, declare and call the replace method from the ARTful library:
// Declare methods in your class
public native void replaceLogEByObject(Object newObject);
public native void replaceLogEBySignature(String newClassName, String newMethodName);
// Call by object or signature
try {
replaceLogEByObject(MainActivity.class.getDeclaredMethod("newMethod", String.class, String.class));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
// New invocations below will actually trigger newMethod
Log.e("ARTful", "I'm benign");
Implement a replacement method with a matching signature:
public static String newMethod() {
Log.d("ARTful", "I should not execute >:)");
// Your code goes here!
return "lol";
}
From Java code, declare and call the replace method from the ARTful library:
// Declare methods in your class
public native void replaceGetRadioVersionByObject(Object newObject);
public native void replaceGetRadioVersionBySignature(String newClassName, String newMethodName);
// Call by object or signature
try {
replaceGetRadioVersionByObject(MainActivity.class.getDeclaredMethod("newMethod"));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
// New invocations below will actually trigger newMethod
Build.getRadioVersion();
Implement a replacement method with a matching signature:
public static Toast newMethod(Context context, CharSequence text, int duration) {
Log.d("ARTful", "I should not execute >:)");
// Your code goes here!
return new Toast(context);
}
From Java code, declare and call the replace method from the ARTful library:
// Declare methods in your class
public native void replaceToastMakeTextByObject(Object newObject);
public native void replaceToastMakeTextBySignature(String newClassName, String newMethodName);
// Call by object or signature
try {
replaceToastMakeTextByObject(MainActivity.class.getDeclaredMethod("newMethod", Context.class, CharSequence.class, int.class));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
// New invocations below will actually trigger newMethod
Toast.makeText(getApplicationContext(), "This will not print", Toast.LENGTH_LONG);
Implement a replacement method with a matching signature:
public static boolean newMethod(String regex, CharSequence input) {
Log.d("ARTful", "I should not execute >:)");
// Your code goes here!
return true;
}
From Java code, declare and call the replace method from the ARTful library:
// Declare methods in your class
public native void replacePatternMatchesByObject(Object newObject);
public native void replacePatternMatchesBySignature(String newClassName, String newMethodName);
// Call by object or signature
try {
replacePatternMatchesByObject(MainActivity.class.getDeclaredMethod("newMethod", String.class, CharSequence.class));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
// New invocations below will actually trigger newMethod
Pattern.matches("MyRegex", "Not actually tested");
Dummy classes are included to allow ARTful to print the member variable offsets of the ArtMethod class in Android13. These offsets can change between versions of Android. They are defined inside of art_method.h. Print offsets from Java code by calling:
printArtMethodOffsets();