debug_wechat_send2.js 5.32 KB
/**
 * Frida debug script v2 — auto-test multiple send message call chains
 * Usage: frida -U -f com.tencent.mm --no-pause -l debug_wechat_send2.js
 */

var testResults = [];

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

// ── Test 1: builder.a() -> m1.a() (current path, returns boolean) ──
function testChain1(toUser, content) {
    log("TEST1", "builder.a() -> m1.a()");
    try {
        var r1 = Java.use("u01.r1");
        var builder = r1.a(toUser);
        log("TEST1", "builder class=" + builder.getClass().getName());

        var q1 = Java.use("u01.q1");
        q1.e.call(builder, content);
        q1.h.call(builder, 0);

        var m1 = q1.a.call(builder);
        log("TEST1", "result class=" + m1.getClass().getName());

        var m1cls = Java.use("u01.m1");
        var ret = m1cls.a.call(m1);
        log("TEST1", "m1.a() returned=" + ret);
        return { chain: "builder.a()->m1.a()", result: ret };
    } catch (e) {
        log("TEST1", "ERROR: " + e);
        return { chain: "builder.a()->m1.a()", error: e.toString() };
    }
}

// ── Test 2: builder.b() (returns void, maybe real submit) ──
function testChain2(toUser, content) {
    log("TEST2", "builder.b() direct submit");
    try {
        var r1 = Java.use("u01.r1");
        var builder = r1.a(toUser);

        var q1 = Java.use("u01.q1");
        q1.e.call(builder, content);
        q1.h.call(builder, 0);

        q1.b.call(builder);  // void return
        log("TEST2", "builder.b() called successfully (void)");
        return { chain: "builder.b()", result: "void-ok" };
    } catch (e) {
        log("TEST2", "ERROR: " + e);
        return { chain: "builder.b()", error: e.toString() };
    }
}

// ── Test 3: builder.a() then call m1.a() with different args ──
function testChain3(toUser, content) {
    log("TEST3", "Try m1 other methods");
    try {
        var r1 = Java.use("u01.r1");
        var builder = r1.a(toUser);

        var q1 = Java.use("u01.q1");
        q1.e.call(builder, content);
        q1.h.call(builder, 0);

        var m1 = q1.a.call(builder);
        var m1cls = Java.use("u01.m1");

        // List all public methods on m1
        var methods = m1cls.class.getDeclaredMethods();
        for (var i = 0; i < methods.length; i++) {
            var m = methods[i];
            if ((m.getModifiers() & 0x0001) && m.getParameterTypes().length === 0) {
                log("TEST3", "m1 method: " + m.getName() + "() -> " + m.getReturnType().getName());
            }
        }

        // Try calling a() again to see if it's idempotent
        var ret = m1cls.a.call(m1);
        log("TEST3", "m1.a() = " + ret);
        return { chain: "m1.inspect", result: ret };
    } catch (e) {
        log("TEST3", "ERROR: " + e);
        return { chain: "m1.inspect", error: e.toString() };
    }
}

// ── Test 4: Try g(String) setter instead of e(String) ──
function testChain4(toUser, content) {
    log("TEST4", "builder.g() instead of e()");
    try {
        var r1 = Java.use("u01.r1");
        var builder = r1.a(toUser);

        var q1 = Java.use("u01.q1");
        q1.g.call(builder, content);  // g(String) returns q1
        q1.h.call(builder, 0);

        var m1 = q1.a.call(builder);
        var m1cls = Java.use("u01.m1");
        var ret = m1cls.a.call(m1);
        log("TEST4", "m1.a() = " + ret);
        return { chain: "builder.g()->m1.a()", result: ret };
    } catch (e) {
        log("TEST4", "ERROR: " + e);
        return { chain: "builder.g()->m1.a()", error: e.toString() };
    }
}

// ── Test 5: Try f(az0.h7) ──
function testChain5(toUser, content) {
    log("TEST5", "Skip f() - requires az0.h7 object");
    return { chain: "builder.f()", skipped: true };
}

// ── Test 6: Hook WCDB to see which chain triggers INSERT ──
function hookWCDB() {
    try {
        var SQLiteDatabase = Java.use("com.tencent.wcdb.database.SQLiteDatabase");
        SQLiteDatabase.insert.overload('java.lang.String', 'java.lang.String', 'android.content.ContentValues').implementation = function(table, nullCol, values) {
            if (table && table.indexOf("message") !== -1) {
                var talker = values.getAsString("talker");
                var content = values.getAsString("content");
                var isSend = values.getAsInteger("isSend");
                log("WCDB", "INSERT talker=" + talker + " isSend=" + isSend + " content=" + (content ? content.substring(0, 40) : "null"));
            }
            return this.insert(table, nullCol, values);
        };
        log("WCDB", "Hook installed");
    } catch (e) {
        log("WCDB", "Hook failed: " + e);
    }
}

// ── Main ──
Java.perform(function() {
    log("MAIN", "Waiting 5s for WeChat to initialize...");

    hookWCDB();

    setTimeout(function() {
        log("MAIN", "Running tests...");

        var toUser = "filehelper";
        var content = "FridaTest_" + Date.now();

        testChain1(toUser, content);

        setTimeout(function() {
            testChain2(toUser, content + "_B");
        }, 2000);

        setTimeout(function() {
            testChain3(toUser, content + "_C");
        }, 4000);

        setTimeout(function() {
            testChain4(toUser, content + "_D");
        }, 6000);

        setTimeout(function() {
            log("MAIN", "All tests done. Check WeChat chat for messages.");
        }, 8000);

    }, 5000);
});