wechat-internal-send-api.md 5.25 KB

WeChat Internal Send API (sk.p0)

Discovery Date: 2026-06-26

Method Signature

Class: com.tencent.mm.ui.chatting.component.sk (extends com.tencent.mm.ui.chatting.component.a)
Method: public final boolean p0(String msg, int type, String extra)
Location: smali_classes11/com/tencent/mm/ui/chatting/component/sk.smali, line 409

Return value: true on success, false if msg is null/empty.

Class Hierarchy

com.tencent.mm.ui.chatting.component.a  (base class)
  └── com.tencent.mm.ui.chatting.component.sk  (SendTextComponent)
        implements: Lsb5/f2, Lc01/w8, Lcom/tencent/mm/modelbase/u0
        fields:
          - e: Lcom/tencent/mm/ui/chatting/i9  (smiley/emoji helper)
          - f: int  (HardCoder performance session ID)
          - g: boolean  (send state flag)
        inherited from `a`:
          - d: Lyb5/d  (chat context — CRITICAL)

Execution Flow

1. Input Validation (line 416-432)

  • Trims message via com.tencent.mm.sdk.platformtools.t8.K()
  • If null or empty, logs "doSendMessage null" and returns false

2. Command Processing (line 435-472)

  • Creates CommandProcessorEvent with msg, Activity context, and talker username
  • If event consumed (handled by command system), returns true early

3. Performance Boost (line 474-560)

  • Queries WXHardCoderJNI for send message performance settings:
    • getHcSendMsgEnable() — whether to boost
    • getHcSendMsgDelay(), getHcSendMsgCPU(), getHcSendMsgIO(), getHcSendMsgThr()
    • getHcSendMsgTimeout(), getHcSendMsgAction()
  • Calls startPerformance(...) and stores session id in f

4. Chat Context Extraction (line 562-658)

Uses d (Lyb5/d) to get:

  • d.t() → Ljava/lang/String; — talker/contact username (e.g. wxid_xxx or @chatroom)
  • d.u() → Lcom/tencent/mm/storage/z3; — contact info object
  • d.x() → Ljava/lang/String; — another talker identifier
  • d.g() → Landroid/app/Activity; — the ChattingUI Activity
  • d.c → Lcom/tencent/mm/ui/chatting/manager/c; — component manager

Uses component manager to get:

  • Lsb5/b1 (cast to pg) → m0(talker) for paste/send content
  • Lsb5/s0 (cast to jb) → ChatFooter reference for similar_paste_seq

5. ChatFooter Interaction (line 663-722)

  • ChatFooter.t0(talker, type) → adjusted message type
  • ChatFooter.r0(talker, msg) → HashMap of extras
  • ChatFooter.C1 → similar_paste_seq string

6. Friend Verification Ticket (line 747-888)

  • Checks pg.e boolean flag
  • If true: queries SQLCipher database for unread messages with ticket
  • Falls back to u8.D0(talker, 1) for ticket retrieval
  • Creates Laq1/e0 with ticket string

7. Send Task Construction (line 940-962)

new tk(
    sk,           // this SendTextComponent
    boolean,      // Z flag (medianote check result)
    String,       // talker username (d.t())
    z3,           // contact info (d.u())
    String,       // msg content (trimmed)
    boolean,      // Z flag (isNewChat)
    String,       // another talker string (d.x())
    String,       // paste content from pg.m0()
    String,       // sender? 
    int,          // adjusted message type
    Map,          // extras from ChatFooter
    String,       // extra parameter (p3)
    a4,           // ticket wrapper
    Context       // application context
)

8. Thread Pool Execution (line 940-974)

  • Dispatches via ku5.t0.h(task, "MicroMsg.ChattingUI.SendTextComponent")
  • Returns boolean

Critical Dependency: Lyb5/d (Chat Context)

The d field is initialized in a.L(Lyb5/d) method (line 88-102 of sk.smali), called during ChattingUI component setup. Without it:

  • d.t() → NullPointerException (talker)
  • d.u() → NullPointerException (contact info)
  • d.g() → NullPointerException (Activity)
  • d.c.a() → NullPointerException (component manager)

This means p0() CANNOT be called without an active ChattingUI instance with the target contact loaded.

Hook Target for Frida

Java.perform(function() {
    var SendTextComponent = Java.use("com.tencent.mm.ui.chatting.component.sk");

    // Hook p0 to observe/intercept messages
    SendTextComponent.p0.overload('java.lang.String', 'int', 'java.lang.String')
        .implementation = function(msg, type, extra) {
        console.log("[p0] msg: " + msg);
        console.log("[p0] type: " + type);
        console.log("[p0] extra: " + extra);

        // Access d field for talker info
        var d = this.d.value;
        if (d) {
            console.log("[p0] talker: " + d.t());
        }

        return this.p0(msg, type, extra);
    };
});
  1. Use UI automation to open chat with target (navigates ChattingUI)
  2. Use Frida to find the sk instance via Java.choose() or hook
  3. Call p0(message, 1, null) on the found instance
  4. Skip ADBKeyboard IME switching and "发送" button clicking

Pitfalls

  1. Multi-process: WeChat uses multiple processes. Frida must attach to the correct one (main process with ChattingUI).
  2. Obfuscation: Class/method names may change between WeChat versions.
  3. Thread safety: tk is executed on a thread pool — calling p0() too fast may cause race conditions.
  4. Group vs direct: The type parameter and talker handling differs for groups (@chatroom suffix).