To demonstrate how to use Substrate to build an extension, we will now walk through the implementation of a concrete example: an extension that modifies the color of numerous interface components to become shades of plum. This extension is useless, but has the advantage of being immediately noticeable.
For our code to be loaded, our package must have the cydia.permission.SUBSTRATE permission.
Additionally, we set android:hasCode to "false" and android:installLocation to "internalOnly".
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" > <application android:hasCode="false"> </application> <uses-permission android:name="cydia.permission.SUBSTRATE"/> </manifest>
Before we develop our modifications, we first must get our own code loaded. We must both specify a filter describing where to load, as well as declare a function for our library initialization code.
#include <substrate.h> MSConfig(MSFilterExecutable, "/system/bin/app_process") // this is a macro that uses __attribute__((__constructor__)) MSInitialize { // ... code to run when extension is loaded }
In order to change the code for a class, we first need a reference to that class. Substrate allows us to provide a callback that will be executed when classes with specific descriptors are loaded.
static void OnResources(JNIEnv *jni, jclass resources, void *data) { // ... code to modify the class when loaded } MSInitialize { MSJavaHookClassLoad(NULL, "android/content/res/Resources", &OnResources); }
The implementation of the method is replaced at the JNI level. We thereby specify our replacement implementation using JNI's calling convention.
To call the previous implementation, we use the function pointer provided when we hooked.
Here, we call through to the original and modify the result, removing all green from the color and jacking up the red (yielding shades of plum).
static jint (*_Resources$getColor)(JNIEnv *jni, jobject _this, ...); static jint $Resources$getColor(JNIEnv *jni, jobject _this, jint rid) { jint color = _Resources$getColor(jni, _this, rid); return color & ~0x0000ff00 | 0x00ff0000; } static void OnResources(JNIEnv *jni, jclass resources, void *data) { jmethodID method = jni->GetMethodID(resources, "getColor", "(I)I"); if (method != NULL) MSJavaHookMethod(jni, resources, method, &$Resources$getColor, &_Resources$getColor); }