From 67c7b3fa7131e90beef1234470416c9291eae49c Mon Sep 17 00:00:00 2001 From: Liang Jiaqing Date: Mon, 16 Feb 2026 22:33:50 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=A1=A5=E5=85=85mem=5Fscanner?= =?UTF-8?q?=E5=92=8Cweb=5Fsetup=20SOP=EF=BC=9B=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=A3=8E=E6=A0=BC=E5=92=8C=E4=B8=8A=E4=B8=8B=E6=96=87?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TMWebDriver.py | 5 +---- memory/mem_scanner_sop.md | 19 ++++++++++++++++++- memory/web_setup_sop.md | 31 +++++++++++++++++++++++++++++++ sidercall.py | 4 ++-- simphtml.py | 18 ++++++++++-------- 5 files changed, 62 insertions(+), 15 deletions(-) diff --git a/TMWebDriver.py b/TMWebDriver.py index 35847c5..c4b3598 100644 --- a/TMWebDriver.py +++ b/TMWebDriver.py @@ -247,10 +247,7 @@ class TMWebDriver: return result['data'] def _remote_cmd(self, cmd): - resp = requests.post(self.remote, - headers={"Content-Type": "application/json"}, - json=cmd).json() - return resp + return requests.post(self.remote, headers={"Content-Type": "application/json"}, json=cmd).json() def get_all_sessions(self): if self.is_remote: diff --git a/memory/mem_scanner_sop.md b/memory/mem_scanner_sop.md index 4aad196..eb1baf7 100644 --- a/memory/mem_scanner_sop.md +++ b/memory/mem_scanner_sop.md @@ -30,4 +30,21 @@ python ../memory/mem_scanner.py "pattern" --llm ## 3. 注意事项 - **权限**: 并非强制要求管理员权限,但需具备对目标进程的 `PROCESS_QUERY_INFORMATION` 和 `PROCESS_VM_READ` 权限。 -- **效率**: 搜索大块内存时,尽量提供更唯一的特征码以减少误报。 \ No newline at end of file +- **效率**: 搜索大块内存时,尽量提供更唯一的特征码以减少误报。 + +## 4. 典型场景:CE式差集扫描定位动态字段(已验证) +用于定位微信等自绘UI中「当前会话标题」等随操作变化的内存字段。 + +**方法(类似Cheat Engine找游戏数值):** +1. 找到主窗口PID(Weixin.exe有多个进程,用win32gui.GetWindowThreadProcessId取有窗口的那个) +2. 切到会话A → `scan_memory(pid, "人名A", mode="string")` → 得地址集S_A +3. 切到会话B → `scan_memory(pid, "人名B", mode="string")` → 得地址集S_B +4. 差集:S_A独有地址(A时有、B时无)= 候选地址 +5. 切回A → 用ReadProcessMemory逐个读候选地址,确认内容变回"人名A"的即为目标 +6. 再切第3、4个人交叉验证 + +**坑点:** +- 搜索切换会污染结果(搜索框缓存也含人名),最终验证应用列表点击而非搜索 +- 地址是绝对虚拟地址,进程重启后失效,需重新校准(约10秒) +- ReadProcessMemory读UTF-8,用`raw.split(b'\x00')[0].decode('utf-8')`提取干净文本 +- 微信主进程名为Weixin.exe(非WeChat.exe) \ No newline at end of file diff --git a/memory/web_setup_sop.md b/memory/web_setup_sop.md index a0bf28c..6f8bf70 100644 --- a/memory/web_setup_sop.md +++ b/memory/web_setup_sop.md @@ -19,6 +19,37 @@ browser = "chrome" if shutil.which("chrome") else "msedge" # Edge内置必存 - Edge: `start "" "https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd"` 2. 提示用户点击"安装"并确认。 +## 阶段 1.5:开启「允许运行用户脚本」 +**前置**:TM 已安装,但 Chrome 可能默认未开启此权限。 +需打开 TM 的扩展详情页,手动开启相关开关。 + +### 自动打开详情页 +1. 从文件系统读取 TM 扩展 ID: + ```python + import os, json, glob + ext_dir = os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\User Data\Default\Extensions') + for eid in os.listdir(ext_dir): + for ver in glob.glob(os.path.join(ext_dir, eid, '*')): + mf = os.path.join(ver, 'manifest.json') + if os.path.isfile(mf): + with open(mf, encoding='utf-8') as f: + m = json.load(f) + if 'tampermonkey' in m.get('name','').lower() or 'tampermonkey' in m.get('description','').lower(): + tm_id = eid; break + ``` +2. 导航到 `chrome://extensions/?id={tm_id}`: + - ⚠️ `chrome://` 协议无法通过命令行参数或 JS(`window.open`) 打开 + - ✅ 用 ljqCtrl(需先打开一个 Chrome 窗口并置顶)或剪贴板+地址栏方案: + ```python + # 剪贴板方案:写入URL → Ctrl+L → Ctrl+V → Enter + import win32clipboard + win32clipboard.OpenClipboard(); win32clipboard.EmptyClipboard() + win32clipboard.SetClipboardText(f'chrome://extensions/?id={tm_id}') + win32clipboard.CloseClipboard() + # 然后用 ljqCtrl 或 SendKeys 发送 Ctrl+L, Ctrl+V, Enter + ``` +3. 提示用户在详情页中开启「允许运行用户脚本」开关。 + ## 阶段二:安装 ljq_web_driver.user.js **脚本路径**: `../assets/ljq_web_driver.user.js` diff --git a/sidercall.py b/sidercall.py index 80ddd2d..8c0c1ec 100644 --- a/sidercall.py +++ b/sidercall.py @@ -41,7 +41,7 @@ class SiderLLMSession: return full_text class ClaudeSession: - def __init__(self, api_key, api_base, model="claude-opus", context_win=10000): + def __init__(self, api_key, api_base, model="claude-opus", context_win=9000): self.api_key, self.api_base, self.default_model, self.context_win = api_key, api_base.rstrip('/'), model, context_win self.raw_msgs, self.lock = [], threading.Lock() def _trim_messages(self, messages): @@ -53,7 +53,7 @@ class ClaudeSession: if (msg_len := len(msg['prompt'])) + current <= target: result.append(msg); current += msg_len else: break - if current > 10000 * 4: print(f'[DEBUG] Whole context length {current//4}.') + if current > self.context_win * 3.6: print(f'[DEBUG] {len(result)} contexts, whole length {current//4} tokens.') return result[::-1] or messages[-2:] def raw_ask(self, messages, model=None, temperature=0.5, max_tokens=4096): model = model or self.default_model diff --git a/simphtml.py b/simphtml.py index 1236872..73c3b4a 100644 --- a/simphtml.py +++ b/simphtml.py @@ -875,14 +875,16 @@ def get_html(driver, cutlist=False, maxchars=28000, instruction=""): rr = driver.execute_js(js_findMainList + js_findMainContent + """ return findMainList(findMainContent(document.body));""") sel = rr.get("selector", None) if isinstance(rr, dict) else None - if not sel: return html[:maxchars] - s = BeautifulSoup(str(soup), "html.parser"); items = s.select(sel) - hit = [it for it in items if instruction and instruction.strip() and instruction in it.get_text(" ",strip=True)] - keep = hit[:6] if hit else items[:3] - for it in items: - if it not in keep: it.decompose() - s = optimize_html_for_tokens(s) - return str(s)[:maxchars] + if sel: + s = BeautifulSoup(str(soup), "html.parser"); items = s.select(sel) + hit = [it for it in items if instruction and instruction.strip() and instruction in it.get_text(" ",strip=True)] + keep = hit[:6] if hit else items[:3] + for it in items: + if it not in keep: it.decompose() + ss = str(optimize_html_for_tokens(s)) + else: ss = html + if len(ss) > maxchars: ss = ss[:maxchars] + ' ... [TRUNCATED]' + return ss def execute_js_rich(script, driver): try: start_temp_monitor(driver)