In one of my Android projects we are using a library that requires licensing and out of curiosity I decided to reverse engineer how it works and it turned out that the implementation is not complicated nor hard to “workaround”. In this post I’m explaining how I managed to make it work with my own license key and I’m intentionally not showing you the name of the library since the purpose of the post is just to show you how it can be done.
Library licensing API
The lib comes as a .aar file with a very simple API to initialize and check the license key. Licensing works offline and licensing key is a JSON file:
{
"licenseKey": "---",
"signature": "---",
"result": 0,
"message": ""
}
This is the method that initializes the lib and checks the license file:
SDK.getInstance().init(assets)
if it returns
SDK.SDK_SUCCESS
it means the license key is valid and the error code otherwise.
This is the decompiled code of the library that Android studio provides:
public class SDK {
public static final int SDK_SUCCESS = 0;
public static final int SDK_ACTIVATE_INVALID_LICENSE = 1;
public static final int SDK_ACTIVATE_APPID_ERROR = 2;
public static final int SDK_ACTIVATE_LICENSE_EXPIRED = 3;
public static final int SDK_NO_ACTIVATED = 4;
public static final int SDK_INIT_ERROR = 5;
private static SDK instance;
public static SDK getInstance() {
return instance;
}
public static SDK createInstance(Context context) {
if (instance == null) {
SDK sdk = new SDK(context);
instance = sdk;
}
return instance;
}
public SDK(Context context) {
super(context);
}
public native int init(AssetManager var1);
static {
System.loadLibrary("native_lib");
}
}
from the snippet above we can see that the initialization and license check happens in the native part.
public native int init(AssetManager var1);
System.loadLibrary("native_lib");
Since init has “native” prefix, it means that the method is implemented in .so library and “native_lib” is the name of the library we need look at in detail.
Reverse engineering how native lib works
.aar file is just a zip archive so I decompressed it and found native lib in
library/jni/arm64-v8a/native_lib.so
As Java (Kotlin) can be decompiled so do native libraries but it is a bit more trickier since you have to deal with Assembler or pseudo C code.
To decompile native library I used Ghidra.
In the Symbol Tree we can see all the functions that are being used in the Java part of the library. We need to look at the “init” closely since this is where license key check is done as I showed earlier.
In the decompiled code we can immediately see two things:
First, there is a hardcoded public key:
Second, there is info on which 3party lib is used to check the license:
“include/cryptolens/imports/…” Cryptolens is a solution for licensing software. I will not dig into how it works in detail but what we need to do is to replace the public key with ours and product id in the lib.
The blurred part has the product id that we need to replace.
To replace public key and product id I used a hex editor (Ghidra probably can also do that but I just did it in the editor).
Replacing the product id was a bit tricky since the product id is not just plain bytes so I used Ghidra to help me with that. After I double clicked on the code, it showed me this part in Assembler, then I used “Patch instruction”, modified it to my product it, checked which op-code it now has and modified the needed in the hex editor.
After re-zipping the library, it worked perfectly with my own license key 😛