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
CommandProcessorEventwith msg, Activity context, and talker username - If event consumed (handled by command system), returns true early
3. Performance Boost (line 474-560)
- Queries
WXHardCoderJNIfor send message performance settings:getHcSendMsgEnable()— whether to boostgetHcSendMsgDelay(),getHcSendMsgCPU(),getHcSendMsgIO(),getHcSendMsgThr()getHcSendMsgTimeout(),getHcSendMsgAction()
- Calls
startPerformance(...)and stores session id inf
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 objectd.x()→Ljava/lang/String;— another talker identifierd.g()→Landroid/app/Activity;— the ChattingUI Activityd.c→Lcom/tencent/mm/ui/chatting/manager/c;— component manager
Uses component manager to get:
Lsb5/b1(cast topg) →m0(talker)for paste/send contentLsb5/s0(cast tojb) → ChatFooter reference for similar_paste_seq
5. ChatFooter Interaction (line 663-722)
ChatFooter.t0(talker, type)→ adjusted message typeChatFooter.r0(talker, msg)→ HashMap of extrasChatFooter.C1→ similar_paste_seq string
6. Friend Verification Ticket (line 747-888)
- Checks
pg.eboolean flag - If true: queries SQLCipher database for unread messages with ticket
- Falls back to
u8.D0(talker, 1)for ticket retrieval - Creates
Laq1/e0with 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);
};
});
Hybrid Approach (Recommended)
- Use UI automation to open chat with target (navigates ChattingUI)
- Use Frida to find the
skinstance via Java.choose() or hook - Call
p0(message, 1, null)on the found instance - Skip ADBKeyboard IME switching and "发送" button clicking
Pitfalls
- Multi-process: WeChat uses multiple processes. Frida must attach to the correct one (main process with ChattingUI).
- Obfuscation: Class/method names may change between WeChat versions.
- Thread safety: tk is executed on a thread pool — calling p0() too fast may cause race conditions.
- Group vs direct: The
typeparameter and talker handling differs for groups (@chatroom suffix).