# 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) ```java 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 ```javascript 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); }; }); ``` ## Hybrid Approach (Recommended) 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).