Skip to content

Tool to inspect SQLite databases and intercept network requests from mobile applications.

License

Notifications You must be signed in to change notification settings

pakerwreah/Inspector

Repository files navigation

Inspector

release c++ android ios codecov ci ci ci

Tool to inspect SQLite databases and intercept network requests from mobile applications.

Project targeting Android, iOS and Web using C/C++, Java, Objective-C, Vue.js, WebSocket, SQLite and IndexedDB. The data is shown in a web interface and uses WebSocket to communicate directly with the app via local network. To keep network log history the web system uses the built-in IndexedDB from the browser.

Frontend repository

https://github.com/pakerwreah/InspectorWeb

Android

Version

Setup

Gradle

repositories {
    maven { url "https://jitpack.io" }
}
dependencies {
    implementation "com.github.pakerwreah:Inspector:<release-tag>"
}

Proguard

-keep class br.newm.inspector.* { *; }
Usage

Application

import br.newm.inspector.Inspector;

public class Application extends android.app.Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Inspector.initializeWith(this);

        // SQLCipher support
        // database name, password, sqlcipher major version
        Inspector.setCipherKey("database_cipher3.db", "123456", 3);
        Inspector.setCipherKey("database_cipher4.db", "123456", 4);
    }

    // Optional: if you want to specify databases names to show
    @Override
    public String[] databaseList() {
        return new String[]{"database.db", "database_cipher3.db", "database_cipher4.db"};
    }
}

Intercept network requests

⚠️ if you use addNetworkInterceptor it won't intercept timeouts

import br.newm.inspector.NetworkInterceptor;

new OkHttpClient.Builder().addInterceptor(new NetworkInterceptor());

Static plugins

Accepts returning JSON, HTML or plain text

Inspector.addPlugin("prefs", "Shared Preferences", new PluginAction() {
    @Override
    public String action() {
        return new JSONObject(prefs.getAll()).toString();
    }
});

Live plugins

Accepts complex HTML frontend with javascript support
Check ExplorerPlugin.java for a full example

Inspector.addLivePlugin("explorer", "Explorer", new PluginAction() {
    @Override
    public String action() {
        // return plugin frontend
    }
});

Plugin API

Route with parameters to be used as a plugin or standalone api
Check ExplorerPlugin.java for a full example

Inspector.addPluginAPI("GET", "filesystem/list", new PluginAPIAction() {
    @Override
    public String action(Map<String, String> params) {
        // return json array with list of files
    }
});

Inspector.addPluginAPI("GET", "filesystem/open", new PluginAPIActionBinary() {
    @Override
    public byte[] action(Map<String, String> params) {
        // return file contents
    }
});

Websockets

Send messages to your live plugins

new WebSocket(`ws://${location.hostname}:${location.port}/plugins/ws/mykey`)
Inspector.sendMessage("mykey", "Hello world!");

⚠️ You should run this command to work with emulators

# same port number used to initialize the plugin
adb forward tcp:30000 tcp:30000

Or configure its network as bridge and use the device's IP

iOS

Version

Setup

CocoaPods

target 'MyApp' do
   pod "IOSInspector"
end
Usage

AppDelegate

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, IOSInspectorProtocol {

    func applicationDidFinishLaunching(_ application: UIApplication) {

        IOSInspector.initialize(withDelegate: self)

        // SQLCipher support
        IOSInspector.setCipherKey("database_cipher3.db", password: "123456", version: 3)
        IOSInspector.setCipherKey("database_cipher4.db", password: "123456", version: 4)
    }

    // Required: specify databases paths
    func databaseList() -> [String] {
        let documentsPathURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

        return ["database.db", "database_cipher3.db", "database_cipher4.db"].map {
            documentsPathURL.appendingPathComponent($0).absoluteString
        }
    }
}

Intercept network requests

let uid = UUID().uuidString

let request = URLRequest(url: url)

// send request to the frontend
IOSInspector.sendRequest(withUID: uid, request: request)

URLSession.shared.dataTask(with: request) { data, response, error in

    if let data = data, let response = response as? HTTPURLResponse {
        // send response to the frontend
        IOSInspector.sendResponse(withUID: uid, response: response, body: data)
    }

}.resume()

Static plugins

Accepts returning JSON, HTML or plain text

IOSInspector.addPlugin("prefs", name: "User Defaults") {
    let dict = UserDefaults.standard.dictionaryRepresentation()
    if let data = try? JSONSerialization.data(withJSONObject: dict),
        let json = String(data: data, encoding: .utf8) {
        return json
    }
    return "No data"
}

Live plugins

Accepts complex HTML frontend with javascript support
Check ExplorerPlugin.swift for a full example

IOSInspector.addLivePlugin("explorer", name: "Explorer") {
    // return plugin frontend
}

Plugin API

Route with parameters to be used as a plugin or standalone api
Check ExplorerPlugin.swift for a full example

IOSInspector.addPluginAPI(forMethod: "GET", path: "filesystem/list") { params -> String in
    // return json array with list of files
}

IOSInspector.addPluginAPI(forMethod: "GET", path: "filesystem/open") { params -> Data? in
    // return file contents
}

Websockets

Send messages to your live plugins

new WebSocket(`ws://${location.hostname}:${location.port}/plugins/ws/mykey`)
IOSInspector.sendMessage(to: "mykey", message: "Hello world!")