package com.hermes.xhshook; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; public class Hook implements IXposedHookLoadPackage { // Hermes event_server endpoint — POST xhs_params events here private static final String HERMES_URL = "http://127.0.0.1:8765/xhs_params"; @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { if (!lpparam.packageName.equals("com.xingin.xhs")) return; XposedBridge.log("[XHSHook] Loaded into XHS!"); try { Class clz = XposedHelpers.findClass("okhttp3.Request$Builder", lpparam.classLoader); XposedHelpers.findAndHookMethod(clz, "build", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { try { Object url = XposedHelpers.getObjectField(param.thisObject, "url"); String urlStr = url != null ? url.toString() : "null"; if (!urlStr.contains("edith.xiaohongshu.com")) return; Object method = XposedHelpers.getObjectField(param.thisObject, "method"); String methodStr = method != null ? method.toString() : "GET"; Object headersBuilder = XposedHelpers.getObjectField(param.thisObject, "headers"); String headersStr = ""; if (headersBuilder != null) { Object headers = XposedHelpers.callMethod(headersBuilder, "build"); headersStr = headers != null ? headers.toString() : ""; } XposedBridge.log("[XHSHook] " + methodStr + " " + urlStr); XposedBridge.log("[XHSHook] Headers:\n" + headersStr); final String json = buildJson(methodStr, urlStr, headersStr); new Thread(new Runnable() { public void run() { postToHermes(json); } }).start(); } catch (Throwable t) { XposedBridge.log("[XHSHook] Error in hook: " + t.getMessage()); } } }); XposedBridge.log("[XHSHook] OkHttp Request.Builder.build() hooked!"); } catch (Throwable t) { XposedBridge.log("[XHSHook] Failed to hook OkHttp: " + t.getMessage()); XposedBridge.log(t); } } private static String buildJson(String method, String url, String headersStr) { StringBuilder sb = new StringBuilder(); sb.append("{"); sb.append("\"method\":").append(jsonStr(method)).append(","); sb.append("\"url\":").append(jsonStr(url)).append(","); sb.append("\"headers\":{"); boolean first = true; for (String line : headersStr.split("\n")) { int colon = line.indexOf(": "); if (colon < 0) continue; String k = line.substring(0, colon).trim(); String v = line.substring(colon + 2).trim(); if (!first) sb.append(","); sb.append(jsonStr(k)).append(":").append(jsonStr(v)); first = false; } sb.append("}}"); return sb.toString(); } private static String jsonStr(String s) { if (s == null) return "null"; return "\"" + s.replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", "\\r") + "\""; } private static void postToHermes(String json) { try { HttpURLConnection conn = (HttpURLConnection) new URL(HERMES_URL).openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/json"); conn.setDoOutput(true); conn.setConnectTimeout(1000); conn.setReadTimeout(1000); byte[] body = json.getBytes(StandardCharsets.UTF_8); conn.setFixedLengthStreamingMode(body.length); try (OutputStream os = conn.getOutputStream()) { os.write(body); } int code = conn.getResponseCode(); conn.disconnect(); if (code != 200 && code != 201 && code != 204) { XposedBridge.log("[XHSHook] Hermes POST " + code); } } catch (Throwable t) { // Hermes server not running — silent fail, logcat capture still works } } }