f1630ebb03
- Docker container with auto-restart - systemd webhook receiver on :5804 - Full send/receive loop: WeChat ↔ Docker ↔ Hermes - Fixed login token for persistence - Firewall rules for container-host communication
153 lines
5.1 KiB
Python
153 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
GDB startup script for WeChat message hooking.
|
|
Run with:
|
|
gdb -x this_script.py --args ./WeChatLinux.AppImage --no-sandbox --disable-gpu
|
|
|
|
GDB disables ASLR by default, so wechat base = 0x555555554000
|
|
"""
|
|
|
|
import gdb
|
|
import os
|
|
import time
|
|
|
|
# Breakpoint RVA from Ajax's blog (4.1.0.16, confirmed valid for 4.1.7)
|
|
BP_RVA = 0x4994BEB
|
|
# Fixed base when GDB starts process (ASLR disabled)
|
|
FIXED_BASE = 0x555555554000
|
|
|
|
LOG_FILE = "/home/hmo/projects/AgentsMeeting/gateway/linux/logs/wechat_messages.log"
|
|
|
|
|
|
def log(msg):
|
|
with open(LOG_FILE, "a") as f:
|
|
f.write(f"{msg}\n")
|
|
gdb.write(f"{msg}\n")
|
|
|
|
|
|
class WechatMessageBreakpoint(gdb.Breakpoint):
|
|
"""Breakpoint that fires on each incoming WeChat message."""
|
|
|
|
def __init__(self, address):
|
|
super().__init__(f"*{address}")
|
|
self.suppress = True
|
|
|
|
def stop(self):
|
|
try:
|
|
msg_ptr = int(gdb.parse_and_eval("$rsi"))
|
|
if msg_ptr == 0:
|
|
return False
|
|
|
|
raw_type = gdb.selected_inferior().read_memory(msg_ptr + 0x14, 4)
|
|
msg_type = int.from_bytes(raw_type, byteorder='little', signed=True)
|
|
|
|
raw_svrid = gdb.selected_inferior().read_memory(msg_ptr + 0x50, 8)
|
|
svrid = int.from_bytes(raw_svrid, byteorder='little')
|
|
|
|
raw_holder = gdb.selected_inferior().read_memory(msg_ptr + 0x20, 8)
|
|
holder = int.from_bytes(raw_holder, byteorder='little', signed=False)
|
|
if holder == 0:
|
|
return False
|
|
|
|
raw_inner = gdb.selected_inferior().read_memory(holder + 0x8, 8)
|
|
inner = int.from_bytes(raw_inner, byteorder='little', signed=False)
|
|
if inner == 0:
|
|
return False
|
|
|
|
# Read content length from inner + 0x10
|
|
raw_len = gdb.selected_inferior().read_memory(inner + 0x10, 4)
|
|
content_len = int.from_bytes(raw_len, byteorder='little', signed=False)
|
|
if content_len <= 0 or content_len > 100000:
|
|
return False
|
|
|
|
# Read content pointer from inner + 0x0
|
|
raw_cp = gdb.selected_inferior().read_memory(inner + 0x0, 8)
|
|
content_ptr = int.from_bytes(raw_cp, byteorder='little', signed=False)
|
|
if content_ptr == 0:
|
|
return False
|
|
|
|
raw_content = gdb.selected_inferior().read_memory(content_ptr, min(content_len * 2, 100000))
|
|
try:
|
|
content = raw_content.tobytes()[:content_len * 2].decode('utf-16le', errors='replace')
|
|
except:
|
|
content = str(raw_content)
|
|
|
|
# Try to read sender info from msg_ptr + 0x38 (talker wxid)
|
|
try:
|
|
raw_talker = gdb.selected_inferior().read_memory(msg_ptr + 0x38, 8)
|
|
talker_ptr = int.from_bytes(raw_talker, byteorder='little', signed=False)
|
|
if talker_ptr:
|
|
talker_data = gdb.selected_inferior().read_memory(talker_ptr, 64)
|
|
talker = talker_data.tobytes().split(b'\x00')[0].decode('utf-8', errors='replace')
|
|
else:
|
|
talker = "unknown"
|
|
except:
|
|
talker = "unknown"
|
|
|
|
info = f"[WECHAT_MSG] type={msg_type} svrid={hex(svrid)} talker={talker}"
|
|
log(info)
|
|
log(f"[WECHAT_MSG] content: {content[:300]}")
|
|
|
|
# Forward to Hermes
|
|
try:
|
|
import urllib.request
|
|
import json
|
|
payload = json.dumps({
|
|
"model": "nova-4",
|
|
"messages": [
|
|
{"role": "user", "content": f"[WeChat from {talker}] {content[:500]}"}
|
|
]
|
|
}).encode()
|
|
req = urllib.request.Request(
|
|
"http://192.168.1.246:8642/v1/chat/completions",
|
|
data=payload,
|
|
headers={"Content-Type": "application/json", "Authorization": "Bearer hermes123"},
|
|
method="POST"
|
|
)
|
|
urllib.request.urlopen(req, timeout=2)
|
|
except:
|
|
pass
|
|
|
|
except Exception as e:
|
|
log(f"[WECHAT_MSG] ERROR: {e}")
|
|
|
|
return False
|
|
|
|
|
|
class AutoHookWechat(gdb.Command):
|
|
"""Auto-hook WeChat messages on startup."""
|
|
|
|
def __init__(self):
|
|
super().__init__("auto-hook-wechat", gdb.COMMAND_USER)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
bp_addr = FIXED_BASE + BP_RVA
|
|
WechatMessageBreakpoint(bp_addr)
|
|
log(f"[WECHAT_MSG] Breakpoint set at 0x{bp_addr:x}")
|
|
|
|
|
|
class OnStart(gdb.Breakpoint):
|
|
"""Breakpoint on _start to set up hooks after process loads."""
|
|
|
|
def __init__(self):
|
|
super().__init__("_start")
|
|
|
|
def stop(self):
|
|
gdb.execute("auto-hook-wechat")
|
|
return True # Stop so user can continue
|
|
|
|
|
|
# Configure GDB
|
|
gdb.execute("set pagination off")
|
|
gdb.execute("set confirm off")
|
|
gdb.execute("handle SIG33 pass nostop noprint")
|
|
gdb.execute("set follow-fork-mode child")
|
|
|
|
# Register our commands
|
|
AutoHookWechat()
|
|
|
|
# Set breakpoint at _start so we hook after process loads
|
|
OnStart()
|
|
|
|
log("[WECHAT_MSG] GDB startup script loaded. Type 'run' to start WeChat.")
|