TensorFlow Lite Dumper

by
4 views 6e0d0423...

Description

Hooks TensorFlow Lite classes to initialize a *.tflite model and writes them to disk. Adjust the file path as needed!

How to Use

Download the script and run it with Frida CLI:

Download Script

Then run with Frida:

frida -U -f YOUR_PACKAGE_NAME -l tensorflow-lite-dumper.js

Replace YOUR_PACKAGE_NAME with the target app's package name.

Source Code

JavaScript
// Change if needed! Requires chmod 777 /data/local/tmp && disabling SELinux (setenforce 0)
const FOLDER_PATH = "/data/local/tmp/";

// util fn
function getRandomInt(max) {
    return Math.floor(Math.random() * max);
}

// accepts java.nio.ByteBuffer, writes it to file
function dumpByteBuffer(buf) {
    // Dumping the model to file
    var remaining = buf.remaining();
    // Unique filename to prevent collisions
    const filename = FOLDER_PATH + 'dump' + getRandomInt(99999) + '.tflite';

    console.log("[*] Opening file name=" + filename + " to write " + remaining + " bytes");
    const f = new File(filename, 'wb');
    // Uint8Array that will be written to file
    var outBuf = new Uint8Array(remaining);

    // Progress interval (every 1% or 100 bytes, whichever comes first)
    const progressInterval = Math.max(100, Math.floor(remaining / 100));

    // Transfer ByteBuffer to Uint8Array, byte by byte.
    for (var i = 0; i < remaining; i += 1000) {
        outBuf[i] = buf.get();

        // Log progress at intervals
        if (i % progressInterval === 0) {
            const progress = Math.floor((i / remaining) * 100);
            console.log(`[*] Writing... ${progress}% completed (${i} / ${remaining} bytes)`);
        }
    }

    console.log("[*] Writing " + remaining + " bytes...");
    f.write(outBuf);
    f.close();

    // checking
    remaining = buf.remaining();
    if (remaining > 0) {
        console.log("[-] Error: There are " + remaining + " remaining bytes!");
    } else {
        console.log("[+] File dumped successfully in " + filename);
    }
}

// Hooking
Java.perform(() => {
    const NativeInterpreterWrapper = (function() {
        try {
            return Java.use('org.tensorflow.lite.NativeInterpreterWrapper')
        } catch {
            return false
        }
    })()

    if (NativeInterpreterWrapper) {
        // Hook NativeInterpreterWrapper.createModelWithBuffer
        NativeInterpreterWrapper.createModelWithBuffer.overload('java.nio.ByteBuffer', 'long').implementation = function(byteBuffer, errorReporterPtr) {
            console.log("[+] Called NativeInterpreterWrapper.createModelWithBuffer");
            // Call the original method
            const result = this.createModelWithBuffer(byteBuffer, errorReporterPtr);
            // Dump the java.nio.ByteBuffer to file
            dumpByteBuffer(byteBuffer)
            // return original result
            return result;
        };
        // Hook NativeInterpreterWrapper.createModel
        NativeInterpreterWrapper.createModel.overload('java.lang.String', 'long').implementation = function(str, errorReporterPtr) {
            console.log("[+] Called NativeInterpreterWrapper.createModel");
            // Call the original method
            const result = this.createModel(str, errorReporterPtr);
            // Not implemented yet
            console.log("[*] Capturing model from string is not implemented yet! The string is either a filepath, or a buffer. The buffer may break your terminal and/or crash your app.")
            const answer = prompt("Print the string to console? (y/n)")
            if (answer.toLowerCase() == "y") {
                console.log(str)
            }
            return result

        }
    } else {
        console.log("org.tensorflow.lite.NativeInterpreterWrapper.createModelWithBuffer not found! Is your app using TFLite?")
    }

});
Share this script:
Twitter LinkedIn

Comments

Login or Sign up to leave a comment.
Loading comments...