debug_wechat_send4.js 5.27 KB
/**
 * Frida debug script v4 — comprehensive WCDB hook
 * Hooks insert, insertWithOnConflict, replace, replaceOrThrow, execSQL, rawQuery
 * Usage: frida -U -p PID -l debug_wechat_send4.js
 */

function log(tag, msg) {
    console.log("[" + tag + "] " + msg);
}

function printStack(tag) {
    var Exception = Java.use("java.lang.Exception");
    var Log = Java.use("android.util.Log");
    var trace = Log.getStackTraceString(Exception.$new());
    console.log("[" + tag + " STACK]\n" + trace + "\n[END STACK]");
}

function hookAllWcdbMethods(clsName) {
    try {
        var cls = Java.use(clsName);
        log("HOOK", "Hooking " + clsName);

        // insert
        try {
            cls.insert.overload('java.lang.String', 'java.lang.String', 'android.content.ContentValues').implementation = function(table, nullCol, values) {
                if (table && table.toLowerCase().indexOf("message") !== -1) {
                    var isSend = values ? values.getAsInteger("isSend") : null;
                    var talker = values ? values.getAsString("talker") : null;
                    var content = values ? values.getAsString("content") : null;
                    log("INSERT", "table=" + table + " isSend=" + isSend + " talker=" + talker + " content=" + (content ? content.substring(0, 40) : "null"));
                    if (isSend && isSend == 1) printStack("INSERT_SEND");
                }
                return this.insert(table, nullCol, values);
            };
        } catch(e) {}

        // insertWithOnConflict
        try {
            cls.insertWithOnConflict.overload('java.lang.String', 'java.lang.String', 'android.content.ContentValues', 'int').implementation = function(table, nullCol, values, conflict) {
                if (table && table.toLowerCase().indexOf("message") !== -1) {
                    var isSend = values ? values.getAsInteger("isSend") : null;
                    log("INSERT2", "table=" + table + " isSend=" + isSend);
                    if (isSend && isSend == 1) printStack("INSERT2_SEND");
                }
                return this.insertWithOnConflict(table, nullCol, values, conflict);
            };
        } catch(e) {}

        // replace
        try {
            cls.replace.overload('java.lang.String', 'java.lang.String', 'android.content.ContentValues').implementation = function(table, nullCol, values) {
                if (table && table.toLowerCase().indexOf("message") !== -1) {
                    log("REPLACE", "table=" + table);
                    printStack("REPLACE");
                }
                return this.replace(table, nullCol, values);
            };
        } catch(e) {}

        // execSQL(String)
        try {
            cls.execSQL.overload('java.lang.String').implementation = function(sql) {
                var lowerSql = sql ? sql.toLowerCase() : "";
                if (lowerSql.indexOf("message") !== -1 && (lowerSql.indexOf("insert") !== -1 || lowerSql.indexOf("replace") !== -1)) {
                    log("EXECSQL", sql.substring(0, 200));
                    printStack("EXECSQL");
                }
                return this.execSQL(sql);
            };
        } catch(e) {}

        // execSQL(String, Object[])
        try {
            cls.execSQL.overload('java.lang.String', '[Ljava.lang.Object;').implementation = function(sql, args) {
                var lowerSql = sql ? sql.toLowerCase() : "";
                if (lowerSql.indexOf("message") !== -1 && (lowerSql.indexOf("insert") !== -1 || lowerSql.indexOf("replace") !== -1)) {
                    log("EXECSQL2", sql.substring(0, 200));
                    printStack("EXECSQL2");
                }
                return this.execSQL(sql, args);
            };
        } catch(e) {}

        // rawQuery
        try {
            cls.rawQuery.overload('java.lang.String', '[Ljava.lang.String;').implementation = function(sql, args) {
                var lowerSql = sql ? sql.toLowerCase() : "";
                if (lowerSql.indexOf("message") !== -1 && lowerSql.indexOf("insert") !== -1) {
                    log("RAWQUERY", sql.substring(0, 200));
                    printStack("RAWQUERY");
                }
                return this.rawQuery(sql, args);
            };
        } catch(e) {}

        log("HOOK", clsName + " hooks installed");
    } catch (e) {
        log("HOOK", "Failed to hook " + clsName + ": " + e);
    }
}

Java.perform(function () {
    log("MAIN", "Installing comprehensive WCDB hooks...");

    // Hook WCDB
    hookAllWcdbMethods("com.tencent.wcdb.database.SQLiteDatabase");

    // Also hook standard Android SQLite
    hookAllWcdbMethods("android.database.sqlite.SQLiteDatabase");

    // Hook SQLiteStatement for prepared statement execution
    try {
        var Stmt = Java.use("com.tencent.wcdb.database.SQLiteStatement");
        Stmt.execute.overload().implementation = function() {
            var sql = this.mSql ? this.mSql.value : "unknown";
            if (sql.toLowerCase().indexOf("message") !== -1) {
                log("STMT", "execute: " + sql.substring(0, 100));
                printStack("STMT");
            }
            return this.execute();
        };
        log("HOOK", "SQLiteStatement hooked");
    } catch(e) {
        log("HOOK", "SQLiteStatement hook failed: " + e);
    }

    log("MAIN", "All hooks installed. Send a message manually now.");
});