I was trying to extract data from an Android app which uses SQLcipher. SQLcipher is an extension of SQLite that encrypts database files with 256-bit AES.

Most of the application code is compressed and protected with some sort of anti-reverse-engineering stuff, so no source code and no debuggers. However, the SQLcipher binary lies in lib/libsqlcipher.so. I'm curious if I can extract the encryption key simply by swapping libsqlcipher.so(e.g. modify sqlite3_key function so that it logs the encryption key to file). The plan is to compile my own version of libsqlcipher.so, repack APK, and run it on a device.

Will this method work as expected? It sounds too easy to be true:)

  • So you are trying to find out the static password/key embedded and hidden in the app that is used to protect the database? If the replacing the library works, depends on the anti-reversing measures. If those measures are implemented well each library hash is checked before it is loaded. Therefore there might be only a very small time window between checking and loading of the library. – Robert Sep 27 '20 at 13:37
  • To add to what Robert said - I assume libsqlcipher.so lives in the .apk file, doesn't it? If the app authors are really that concerned over security, even trying to replace the .so and reassemble the .apk will make some signature check fail, as the signature will be changed to a custom one. – Grimoire Sep 27 '20 at 20:10

I ended up using Frida to hook sqlite3_key dynamically and got the encryption key. Simple enough.

Brief steps I took:

  1. Setup Frida to connect to your Android device
  2. On the Python end, attach to target app and load JavaScript.
import frida

# Attach to target app
session = frida.get_usb_device().attach("cn.com.company_name.app_name")

# Load Javascript from a separate file. 
with open("script.js", "r") as file:
  script = session.create_script(file.read())

# Print any messages that Javascript send back
script.on("message", print)
  1. On the Javascript end, hook into sqlite3_key and read encryption key from memory:
// Locate the SQLCipher library
var sqlcipher = Process.findModuleByName("libsqlcipher.so");

// Locate the `sqlite3_key` function call
var func = sqlcipher.findExportByName("sqlite3_key");

// Hook into it
Interceptor.attach(func, {
    onEnter: function (args) {
        // The encryption key is the second argument to `sqlite3_key`
        var key_ptr = args[1];

        // read 32 bytes worth of data(256-bit key)
        var key = key_ptr.readByteArray(32);

        // And send it back to Python
        send("Got key", key);
  • Hi, it seems you accidentally created a duplicate account. Please refer to the help center to merge your accounts and regain ownership of the question and reply to the comments on your post. For this moment, I'll edit the post and add this information. Thanks for your understanding. – Andrew T. Sep 28 '20 at 2:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy