Reverse engineering licensing in an Android library

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 😛

Receiving captured packets in Wireshark from PCAP Remote running on Android emulator

Accessing the PCAP Remote SSH server running on Android emulator is a bit tricky since the emulator network is not directly accessible from the host machine. To make Wireshark be able to connect to PCAP Remote we need to enable port forwarding.

To do that, go to the platform-tools directory in your Android SDK directory. Then, run the bellow cmd:

adb forward tcp:15432 tcp:15432

Now, you can connect to the SSH server if you specify the connection details in Wireshark as follows:

host: 127.0.0.1
port: 15432

PCAP Remote

Today I’m releasing an initial version of my Android app called PCAP Remote. The app is a non-root network sniffer that allows you to debug and analyze traffic in Wireshark on the fly using the app’s built-in SSH server, which is useful and often a must when developing Android software solutions that use complex/custom network protocols.

Although the app is primarily designed to be used in conjunction with Wireshark, other similar tools can also be used as packets are captured in the commonly used pcapng format.

Let me know if you have any ideas/issues.
email: egorovandreyrm@gmail.com

Download from Play
Tutorial

PcapNg Decryption Secrets block

PcapNg is a new file format to store captured packets. Decryption Secrets block is one of the most interesting features of it.

Decryption Secrets block allows Wireshark and other similar tools to decrypt TLS traffic. Specifying Key log file or RSA keys is not needed in this case. The block is effectively is a key log file embedded in a pcapng file. Details can be read in the pcapng format document: https://github.com/pcapng/pcapng

To support the feature in my android application, I developed a simple lib for writing pcapng blocks, that has only 4 functions. The lib can be found here: https://github.com/egorovandreyrm/pcapng_dsb
An example of using the lib is included in the repository.

The API of the lib looks the following way:

size_t write_section_header_block(uint8_t *out_buffer, size_t out_buffer_len);

size_t write_network_interfaces_description_block(
    uint32_t snapshot_max_len, uint8_t *out_buffer, size_t out_buffer_len);

size_t write_enhanced_packet_block(
    const uint8_t *packet,
    const size_t packet_len,
    uint8_t *out_buffer,
    size_t out_buffer_len);

size_t write_decryption_secrets_block(
   const uint8_t *tls_key_log,
   const size_t tls_key_log_len,
   uint8_t *out_buffer,
   size_t out_buffer_len);

if out_buffer is NULL, the functions do nothing but return the size that out_buffer is required to have.

Decryption secrets block has to be written prior to any packet blocks that require the secrets.

A typical usage would be:

write_section_header_block(); // every pcap file needs this block 
write_network_interfaces_description_block() // every pcap file needs this block 

write_enhanced_packet_block() // syn
write_enhanced_packet_block() // syn ask
write_enhanced_packet_block() // ask
write_enhanced_packet_block() // client hello
write_enhanced_packet_block() // ask
write_enhanced_packet_block() // server hello
write_enhanced_packet_block() // ask

write_decryption_secrets_block() // tls key log file

// any packets that are written after Decryption Secrets block can be decrypted
write_enhanced_packet_block()
write_enhanced_packet_block()

Compiling libssh for Android

As libssh does not provide official android support, compiling it for Android is a bit tricky. In this post, I’m providing my solution. Boringssl is used as the ssl lib.

The building script is developed for Linux but could be easily adapted for Windows, I think.

libssh android build scripts along with all the other stuff can be found here.
Compiled libssh and boringssl libs git repository can be found here.

The solution is based on a project called multipass (https://github.com/CanonicalLtd/multipass/tree/master/3rd-party/libssh) and some posts from https://www.libssh.org/archive/libssh/2015-11/0000011.html

The multipass project uses its own libssh, which is pretty much the same thing.

Using the libs in a cmake project looks like:

 target_link_libraries(pcapremote  
     ${log-lib}  
     ${PROJECT_SOURCE_DIR}/ext_libs/${ANDROID_ABI}/libssh.a  
     ${PROJECT_SOURCE_DIR}/ext_libs/${ANDROID_ABI}/libssl.a  
     ${PROJECT_SOURCE_DIR}/ext_libs/${ANDROID_ABI}/libcrypto.a  
     ${PROJECT_SOURCE_DIR}/ext_libs/${ANDROID_ABI}/libdecrepit.a  
     ${PROJECT_SOURCE_DIR}/ext_libs/${ANDROID_ABI}/libssh-boringssl-compat.a  
     ${z-lib})  

Make sure you link libssh-boringssl-compat.a lib to your project.