From a4242c12be7dbb81fdaaa7432b3ecdb21aa835a9 Mon Sep 17 00:00:00 2001 From: zhaoyilun Date: Sun, 12 Jul 2026 02:36:59 +0800 Subject: [PATCH] docs: add offline b-route reverse decision --- .../2026-07-12-broute-bridge-static-map.md | 309 ++++++++++ .../2026-07-12-broute-file-send-static-map.md | 230 ++++++++ .../2026-07-12-broute-text-send-static-map.md | 149 +++++ ...6-07-12-offline-broute-reverse-decision.md | 65 +++ ...2026-07-12-offline-broute-reverse-index.md | 45 ++ .../capability-source-matrix.md | 11 +- .../2026-07-12-b-route-bridge-next-plan.md | 32 +- .../build-offline-broute-reverse-index.ps1 | 168 ++++++ scripts/extract-broute-bridge-map.ps1 | 537 ++++++++++++++++++ scripts/extract-broute-file-send-map.ps1 | 342 +++++++++++ scripts/extract-broute-text-send-map.ps1 | 285 ++++++++++ 11 files changed, 2160 insertions(+), 13 deletions(-) create mode 100644 docs/source-discovery/2026-07-12-broute-bridge-static-map.md create mode 100644 docs/source-discovery/2026-07-12-broute-file-send-static-map.md create mode 100644 docs/source-discovery/2026-07-12-broute-text-send-static-map.md create mode 100644 docs/source-discovery/2026-07-12-offline-broute-reverse-decision.md create mode 100644 docs/source-discovery/2026-07-12-offline-broute-reverse-index.md create mode 100644 scripts/build-offline-broute-reverse-index.ps1 create mode 100644 scripts/extract-broute-bridge-map.ps1 create mode 100644 scripts/extract-broute-file-send-map.ps1 create mode 100644 scripts/extract-broute-text-send-map.ps1 diff --git a/docs/source-discovery/2026-07-12-broute-bridge-static-map.md b/docs/source-discovery/2026-07-12-broute-bridge-static-map.md new file mode 100644 index 0000000..2c42a0e --- /dev/null +++ b/docs/source-discovery/2026-07-12-broute-bridge-static-map.md @@ -0,0 +1,309 @@ +# B-Route Bridge/IPC/Plugin Static Map + +Date: 2026-07-12 + +## Scope + +This report uses offline IL, binary string metadata, reflection metadata for `HttpServerLib.dll`, and returned live-probe JSON. It does not log in, open sockets, attach hooks, inject into a process, replay traffic, or mutate client binaries. + +Machine-readable evidence: `runs/offline-broute-reverse/bridge-map.json` +Reflection metadata: `runs/offline-broute-reverse/httpserverlib-reflection.json` + +## Recommendation + +| Question | Answer | +| --- | --- | +| Should B-route pursue existing IPC/plugin before an in-process adapter? | **No** | +| Offline decision | `in_process_adapter_research` | +| External sidecar boundary | `external_sidecar_preview_only_until_bridge_exists` | +| Reason | Offline static evidence found no existing local IPC/plugin route that exposes send-message or send-file. HttpServerLib and plugin surfaces either require logged-in runtime state or are UI/framework-only; the named-pipe stub is empty. | + +## Sources + +### Text/IL sources + +| Source | Role | Exists | Lines | Size bytes | +| --- | --- | --- | ---: | ---: | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il` | main client IL; login, plugin manager, local HTTP starter, chat/runtime | yes | 1360035 | 74148460 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Common.dll.il` | common helpers; WinCommand, ProcessComm, DTOs | yes | 118343 | 5703293 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ISphere.exe.il` | ISphere companion app IL; CEF and command helpers | yes | 81273 | 5679074 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.UI.dll.il` | UI/browser support IL | yes | 242314 | 13545362 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il` | file service implementation IL | yes | 3351 | 207786 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il` | XMPP/socks5/network IL | yes | 87425 | 5019807 | + +### Binary/string sources + +| Source | Role | Exists | Strings | Size bytes | +| --- | --- | --- | ---: | ---: | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll` | local HTTP server framework binary | yes | 555 | 26624 | +| `runs\offline-real-client-window\full\zyl\Impp\IMPlatformClient.Web.exe` | CEF/web helper process binary | yes | 185 | 9216 | +| `runs\offline-real-client-window\full\zyl\Impp\INetwork.dll` | native network abstraction | yes | 563 | 87552 | +| `runs\offline-real-client-window\full\zyl\Impp\IOClientNetwork.dll` | native network implementation | yes | 870 | 145920 | +| `runs\offline-real-client-window\full\zyl\Impp\IMPlatformClient.exe` | main client binary | yes | 76877 | 8366592 | + +## Candidate classification + +| Candidate | Category | Classification | Usable for B-route send | Recommendation | Matches | +| --- | --- | --- | --- | --- | ---: | +| `frmLogin.StartWinServer / HttpServerLib.HttpService` | `localhost_http` | `requires_logged_in_process` | no | `do_not_pursue_before_in_process_adapter` | 31 | +| `HttpServerLib route/module framework` | `localhost_http` | `static_only_unknown` | no | `no_existing_ipc_first` | 8 | +| `PluginLightAppMulProcessCommunication NamedPipeServerStream` | `named_pipe` | `not_a_bridge` | no | `do_not_use` | 17 | +| `IMPP.Common.Helper.ProcessComm WM_COPYDATA helper` | `window_message` | `static_only_unknown` | no | `not_a_send_bridge` | 164 | +| `PluginManager / IMPlugin command surface` | `plugin` | `requires_logged_in_process` | no | `plugin_extension_not_first` | 2696 | +| `IMPlatformClient.Web / CEF helper` | `child_process` | `requires_logged_in_process` | no | `preview_only_not_send` | 343 | +| `INetwork / IOClientNetwork native stack` | `native_network` | `not_a_bridge` | no | `do_not_pursue_as_ipc` | 72 | +| `returned live-probe named pipe inventory` | `named_pipe` | `static_only_unknown` | no | `do_not_assume_isphere_api` | 49 | +| `local TcpListener candidates` | `tcp_listener` | `not_a_bridge` | no | `not_existing_ipc` | 145 | + +## Verified facts + +- `frmLogin.StartWinServer` constructs `HTTPServerLib.HttpService` on `0.0.0.0` and starts `HttpServer.Start` in a thread, but it depends on `IMPPManager.UserInfo` and `ServiceManager.UnifiedAuthentication` state. +- `HttpServerLib.dll` exposes route/module primitives such as `HttpService.RegisterModule`, `ServiceModule`, `ServiceRoute`, `RouteAttribute`, `OnGet`, and `OnPost`. +- Main client IL does not show a concrete `RegisterModule` call or send-message/send-file HTTP route registration. +- `PluginLightAppMulProcessCommunication` declares a `NamedPipeServerStream` field, but `StartLightAppMultProssCommunication` returns immediately. +- Returned live probe showed one remote TCP connection to port `10088` and a named pipe `\\.\\pipe\\zfpinject-msg-server`; neither proves an iSphere send API. +- `PluginManager`/`IMPlugin` evidence is UI/app-store/context-menu oriented; no local assembly-load plugin bridge or send route was confirmed. + +## Candidate evidence + +### frmLogin.StartWinServer / HttpServerLib.HttpService + +Classification: `requires_logged_in_process`; usable for B-route send: `False`; matches: 31. + +Reason: Static IL builds a local HTTP service from logged-in user/auth state, but no send route or active live local listener was confirmed. + +| Source | Kind | Snippet | Context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:223` | `reference` | `.assembly extern HttpServerLib` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:888716` | `reference` | `RepairDatabase() cil managed` | `L888715: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:888818` | `string_literal` | `IL_00f8: ldstr "{0}/ifim/messageAuth\?version={1}&user={2}&resource"` | `L888715: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:888944` | `string_literal` | `IL_0287: ldstr "RepairDatabase.exe"` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889041` | `method_end` | `} // end of method frmLogin::RepairDatabase` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889166` | `callsite` | `IL_004d: call instance void IMPP.Client.frmLogin::RepairDatabase()` | `L889120: .method private hidebysig instance bool` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889253` | `signature_continuation` | `StartWinServer() cil managed` | `L889252: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889272` | `reference` | `class [HttpServerLib]HTTPServerLib.HttpService V_15,` | `L889252: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889518` | `callsite` | `IL_0276: call instance int32 IMPP.Client.frmLogin::GetFreePort(int32[])` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889551` | `string_literal` | `IL_02d3: ldstr "0.0.0.0"` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889554` | `constructor_call` | `IL_02dc: newobj instance void [HttpServerLib]HTTPServerLib.HttpService::.ctor(string,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889559` | `reference` | `IL_02e5: ldftn instance void [HttpServerLib]HTTPServerLib.HttpServer::Start()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889677` | `method_end` | `} // end of method frmLogin::StartWinServer` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889680` | `signature_continuation` | `GetFreePort(int32[] portArray) cil managed` | `L889679: .method private hidebysig instance int32` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889798` | `method_end` | `} // end of method frmLogin::GetFreePort` | `L889679: .method private hidebysig instance int32` | +| `runs\offline-broute-reverse\httpserverlib-reflection.json` | `reflection_metadata` | `HttpServerLib reflection exposes matching types/methods; see httpserverlib-reflection.json` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll#str10` | `binary_string` | `HttpServerLib.dll` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll#str12` | `binary_string` | `HTTPServerLib` | `` | + +### HttpServerLib route/module framework + +Classification: `static_only_unknown`; usable for B-route send: `False`; matches: 8. + +Reason: HttpServerLib has ServiceModule/RouteAttribute primitives, but client IL does not show RegisterModule or send-message routes. + +| Source | Kind | Snippet | Context | +| --- | --- | --- | --- | +| `runs\offline-broute-reverse\httpserverlib-reflection.json` | `reflection_metadata` | `HttpServerLib reflection exposes matching types/methods; see httpserverlib-reflection.json` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll#str21` | `binary_string` | `RouteAttribute` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll#str22` | `binary_string` | `RouteMethod` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll#str23` | `binary_string` | `ServiceModule` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll#str24` | `binary_string` | `ServiceRoute` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll#str292` | `binary_string` | `RegisterModule` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll#str302` | `binary_string` | `SearchRoute` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\HttpServerLib.dll#str303` | `binary_string` | `ExecuteRoute` | `` | + +### PluginLightAppMulProcessCommunication NamedPipeServerStream + +Classification: `not_a_bridge`; usable for B-route send: `False`; matches: 17. + +Reason: A NamedPipeServerStream field exists, but StartLightAppMultProssCommunication has a one-instruction ret body. + +| Source | Kind | Snippet | Context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9039` | `type_definition` | `.class private auto ansi beforefieldinit IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication` | `L9012: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9042` | `reference` | `.field private static class IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication _instance` | `L9012: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9043` | `reference` | `.field private class [System.Core]System.IO.Pipes.NamedPipeServerStream _pipeServer` | `L9012: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9052` | `method_end` | `} // end of method PluginLightAppMulProcessCommunication::.ctor` | `L9044: .method private hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9058` | `constructor_call` | `IL_0000: newobj instance void IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication::.ctor()` | `L9054: .method public hidebysig static void Init() cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9059` | `reference` | `IL_0005: stsfld class IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication::_instance` | `L9054: .method public hidebysig static void Init() cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9060` | `reference` | `IL_000a: ldsfld class IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication::_instance` | `L9054: .method public hidebysig static void Init() cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9061` | `callsite` | `IL_000f: callvirt instance void IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication::StartLightAppMultProssCommunication()` | `L9054: .method public hidebysig static void Init() cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9063` | `method_end` | `} // end of method PluginLightAppMulProcessCommunication::Init` | `L9054: .method public hidebysig static void Init() cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9066` | `reference` | `StartLightAppMultProssCommunication() cil managed` | `L9065: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9071` | `method_end` | `} // end of method PluginLightAppMulProcessCommunication::StartLightAppMultProssCommunication` | `L9065: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9079` | `method_end` | `} // end of method PluginLightAppMulProcessCommunication::.cctor` | `L9073: .method private hidebysig specialname rtspecialname static` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:9081` | `reference` | `} // end of class IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication` | `L9073: .method private hidebysig specialname rtspecialname static` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:217833` | `callsite` | `IL_0071: call void IMPP.Client.Core.Plugins.MessageCenter.Classes.PluginLightAppMulProcessCommunication::Init()` | `L217793: .method private hidebysig specialname rtspecialname` | +| `runs\offline-real-client-window\full\zyl\Impp\IMPlatformClient.exe#str2852` | `binary_string` | `NamedPipeServerStream` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\IMPlatformClient.exe#str6922` | `binary_string` | `PluginLightAppMulProcessCommunication` | `` | +| `runs\offline-real-client-window\full\zyl\Impp\IMPlatformClient.exe#str8523` | `binary_string` | `StartLightAppMultProssCommunication` | `` | + +### IMPP.Common.Helper.ProcessComm WM_COPYDATA helper + +Classification: `static_only_unknown`; usable for B-route send: `False`; matches: 164. + +Reason: ProcessComm can send WM_COPYDATA to a foreground or titled window, but no receive handler or send-command contract is confirmed. + +| Source | Kind | Snippet | Context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:271124` | `reference` | `native int GetForegroundWindow() cil managed preservesig` | `L271123: .method public hidebysig static pinvokeimpl("user32.dll" winapi)` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:278221` | `type_definition` | `.class sequential ansi sealed nested private beforefieldinit COPYDATASTRUCT` | `L278204: .method private hidebysig static object` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:278227` | `reference` | `} // end of class COPYDATASTRUCT` | `L278204: .method private hidebysig static object` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:279144` | `reference` | `.field private static literal int32 WM_COPYDATA = int32(0x0000004A)` | `L279026: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:282985` | `reference` | `int32 FindWindow(string lpClassName,` | `L282984: .method private hidebysig static pinvokeimpl("user32.dll" winapi)` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:317869` | `callsite` | `IL_3827: call native int IMPP.Client.utils.Utils::GetForegroundWindow()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:412102` | `callsite` | `IL_3613: call native int IMPP.Client.utils.Utils::GetForegroundWindow()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:543733` | `reference` | `.field public static literal int32 WM_COPYDATA = int32(0x0000004A)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:660716` | `reference` | `.locals init (valuetype IMPP.Client.Business.Logon.Native/COPYDATASTRUCT V_0,` | `L660712: .method public hidebysig static bool DefWndProc(valuetype [System.Windows.Forms]System.Windows.Forms.Message& m) cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:660731` | `reference` | `valuetype IMPP.Client.Business.Logon.Native/COPYDATASTRUCT V_15,` | `L660712: .method public hidebysig static bool DefWndProc(valuetype [System.Windows.Forms]System.Windows.Forms.Message& m) cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:660735` | `reference` | `valuetype IMPP.Client.Business.Logon.Native/COPYDATASTRUCT V_19,` | `L660712: .method public hidebysig static bool DefWndProc(valuetype [System.Windows.Forms]System.Windows.Forms.Message& m) cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:660746` | `reference` | `valuetype IMPP.Client.Business.Logon.Native/COPYDATASTRUCT V_30,` | `L660712: .method public hidebysig static bool DefWndProc(valuetype [System.Windows.Forms]System.Windows.Forms.Message& m) cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:661348` | `reference` | `IL_06b6: initobj IMPP.Client.Business.Logon.Native/COPYDATASTRUCT` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:661350` | `reference` | `IL_06bd: ldtoken IMPP.Client.Business.Logon.Native/COPYDATASTRUCT` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:661353` | `reference` | `IL_06cc: unbox.any IMPP.Client.Business.Logon.Native/COPYDATASTRUCT` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:661356` | `reference` | `IL_06d5: ldfld int32 IMPP.Client.Business.Logon.Native/COPYDATASTRUCT::cbData` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:661360` | `reference` | `IL_06e3: ldfld native int IMPP.Client.Business.Logon.Native/COPYDATASTRUCT::lpData` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:661446` | `reference` | `IL_07e8: stfld native int IMPP.Client.Business.Logon.Native/COPYDATASTRUCT::dwData` | `` | + +### PluginManager / IMPlugin command surface + +Classification: `requires_logged_in_process`; usable for B-route send: `False`; matches: 2696. + +Reason: Plugin evidence is UI/app-store/context-menu oriented and depends on logged-in PluginManager state; no Assembly.Load/LoadFrom or send API bridge was confirmed. + +| Source | Kind | Snippet | Context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1048` | `reference` | `.mresource public IMPP.Client.Core.Plugins.Manager.FrmAppStore.resources` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1051` | `reference` | `// 警告: 创建了托管资源文件 IMPP.Client.Core.Plugins.Manager.FrmAppStore.resources` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1063` | `reference` | `.mresource public IMPP.Client.Core.Plugins.Manager.WindowForm.FrmCefHomePage.resources` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1066` | `reference` | `// 警告: 创建了托管资源文件 IMPP.Client.Core.Plugins.Manager.WindowForm.FrmCefHomePage.resources` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:82622` | `callsite` | `IL_0156: call class IMPP.Client.Core.Plugins.Manager.PluginManager IMPP.Client.Core.Plugins.Manager.PluginManager::GetInstance()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:82623` | `callsite` | `IL_015b: callvirt instance class [mscorlib]System.Collections.Generic.List`1 IMPP.Client.Core.Plugins.Manager.PluginManager::GetPluginList()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:85884` | `callsite` | `IL_0157: call class IMPP.Client.Core.Plugins.Manager.PluginManager IMPP.Client.Core.Plugins.Manager.PluginManager::GetInstance()` | `L85783: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:85890` | `callsite` | `IL_0168: callvirt instance void IMPP.Client.Core.Plugins.Manager.PluginManager::add_PluginMessageNotify(class [IMPP.Interface]IMPP.Interface.PluginMessageEventHandler)` | `L85783: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:88002` | `callsite` | `IL_045d: call class IMPP.Client.Core.Plugins.Manager.PluginManager IMPP.Client.Core.Plugins.Manager.PluginManager::GetInstance()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:88007` | `callsite` | `IL_0470: callvirt instance void IMPP.Client.Core.Plugins.Manager.PluginManager::DoNotifyAction(string,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:90512` | `callsite` | `IL_0111: call void IMPP.Client.Core.Plugins.Manager.PluginUtils::LoadPluginContextMenu(class [System.Windows.Forms]System.Windows.Forms.ContextMenuStrip,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:90677` | `callsite` | `IL_016d: call void IMPP.Client.Core.Plugins.Manager.PluginUtils::LoadPluginContextMenu(class [System.Windows.Forms]System.Windows.Forms.ContextMenuStrip,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:91111` | `callsite` | `IL_00f8: call void IMPP.Client.Core.Plugins.Manager.PluginUtils::LoadPluginContextMenu(class [System.Windows.Forms]System.Windows.Forms.ContextMenuStrip,` | `L91000: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:91274` | `callsite` | `IL_00cb: call void IMPP.Client.Core.Plugins.Manager.PluginUtils::LoadPluginContextMenu(class [System.Windows.Forms]System.Windows.Forms.ContextMenuStrip,` | `L91188: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:105364` | `callsite` | `IL_03c1: callvirt instance string IMPP.Client.Core.Plugins.Manager.IMPlugin::get_Command()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:105401` | `callsite` | `IL_042d: call class IMPP.Client.Core.Plugins.Manager.PluginManager IMPP.Client.Core.Plugins.Manager.PluginManager::GetInstance()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:105402` | `callsite` | `IL_0432: callvirt instance class [mscorlib]System.Collections.Generic.List`1 IMPP.Client.Core.Plugins.Manager.PluginManager::GetPluginList()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:217227` | `reference` | `class IMPP.Client.Core.Plugins.Manager.PluginManager V_3,` | `L217219: .method public hidebysig newslot virtual final` | + +### IMPlatformClient.Web / CEF helper + +Classification: `requires_logged_in_process`; usable for B-route send: `False`; matches: 343. + +Reason: The web helper/CEF surface exists, but static evidence points to UI/plugin browser support, not an external send/file API. + +| Source | Kind | Snippet | Context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:278553` | `reference` | `.field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1::Invoke(!0,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:388726` | `reference` | `.field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1b__4'()` | `L905745: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:905828` | `method_end` | `} // end of method frmMain::frmMain_OnReConnectionSucceed` | `L905745: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:919019` | `reference` | `IL_006e: ldftn instance void IMPP.Client.frmMain::frmMain_OnReConnectionSucceed(object,` | `L918967: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:919023` | `callsite` | `IL_0079: callvirt instance void [smack]com.vision.smack.XMPPConnection::add_OnReConnectionSucceed(class [mscorlib]System.EventHandler)` | `L918967: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:928079` | `method_header` | `.method private hidebysig static void 'b__4'() cil managed` | `L928079: .method private hidebysig static void 'b__4'() cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:928087` | `method_end` | `} // end of method frmMain::'b__4'` | `L928079: .method private hidebysig static void 'b__4'() cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1126866` | `reference` | `IL_00e2: ldftn instance void IMPP.Client.Business.ReConnection.WindowForm.FrmReConnection::OnReConnectionSucceed(object,` | `L1126773: .method private hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1126870` | `callsite` | `IL_00ed: callvirt instance void [smack]com.vision.smack.XMPPConnection::add_OnReConnectionSucceed(class [mscorlib]System.EventHandler)` | `L1126773: .method private hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1127782` | `reference` | `OnReConnectionSucceed(object sender,` | `L1127781: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1127841` | `reference` | `IL_0069: ldftn instance void IMPP.Client.Business.ReConnection.WindowForm.FrmReConnection::'b__0'()` | `L1127781: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1127855` | `method_end` | `} // end of method FrmReConnection::OnReConnectionSucceed` | `L1127781: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1127979` | `reference` | `'b__0'() cil managed` | `L1127978: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1127987` | `method_end` | `} // end of method FrmReConnection::'b__0'` | `L1127978: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ISphere.exe.il:33510` | `reference` | `IL_00e2: ldftn instance void IMPP.ISphere.IMPPManage.FrmReConnection::OnReConnectionSucceed(object,` | `L33417: .method private hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ISphere.exe.il:33514` | `callsite` | `IL_00ed: callvirt instance void [smack]com.vision.smack.XMPPConnection::add_OnReConnectionSucceed(class [mscorlib]System.EventHandler)` | `L33417: .method private hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ISphere.exe.il:34155` | `reference` | `OnReConnectionSucceed(object sender,` | `L34154: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ISphere.exe.il:34182` | `reference` | `IL_0031: ldftn instance void IMPP.ISphere.IMPPManage.FrmReConnection::'b__0'()` | `L34154: .method private hidebysig instance void` | + +### local TcpListener candidates + +Classification: `not_a_bridge`; usable for B-route send: `False`; matches: 145. + +Reason: TcpListener evidence is tied to TCP file manage or smack socks5, not a command API for text/file send. Returned live probe had only one remote XMPP connection. + +| Source | Kind | Snippet | Context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:889833` | `callsite` | `IL_003c: callvirt instance class [System]System.Net.IPEndPoint[] [System]System.Net.NetworkInformation.IPGlobalProperties::GetActiveTcpListeners()` | `L889800: .method private hidebysig instance bool` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1117108` | `reference` | `.field private static class [System]System.Net.Sockets.TcpListener _listener` | `L1117034: .method public hidebysig instance bool` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1118022` | `reference` | `IL_0058: ldsfld class [System]System.Net.Sockets.TcpListener IMPP.Client.Business.TCPFileManage.TCPFileManager::_listener` | `L1117985: .method public hidebysig static class [System]System.Net.IPEndPoint` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1118096` | `reference` | `IL_011e: ldsfld class [System]System.Net.Sockets.TcpListener IMPP.Client.Business.TCPFileManage.TCPFileManager::_listener` | `L1117985: .method public hidebysig static class [System]System.Net.IPEndPoint` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1118097` | `callsite` | `IL_0123: callvirt instance class [System]System.Net.EndPoint [System]System.Net.Sockets.TcpListener::get_LocalEndpoint()` | `L1117985: .method public hidebysig static class [System]System.Net.IPEndPoint` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1118817` | `callsite` | `IL_0091: callvirt instance class [System]System.Net.IPEndPoint[] [System]System.Net.NetworkInformation.IPGlobalProperties::GetActiveTcpListeners()` | `L1118746: .method public hidebysig static int32 FindFreeTCPPort([opt] int32 startPort) cil managed` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1119636` | `reference` | `IL_0009: stsfld class [System]System.Net.Sockets.TcpListener IMPP.Client.Business.TCPFileManage.TCPFileManager::_listener` | `L1119622: .method private hidebysig specialname rtspecialname static` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:64917` | `reference` | `.field private class com.vision.smack.socks5.TCPSocks5Server 'k__BackingField'` | `L64879: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:64926` | `signature_continuation` | `instance void .ctor(class com.vision.smack.socks5.TCPSocks5Server server,` | `L64925: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:64935` | `callsite` | `IL_0008: call instance void com.vision.smack.socks5.TCPSocks5Connection::set_Server(class com.vision.smack.socks5.TCPSocks5Server)` | `L64925: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:64942` | `method_header` | `.method private hidebysig specialname instance class com.vision.smack.socks5.TCPSocks5Server` | `L64942: .method private hidebysig specialname instance class com.vision.smack.socks5.TCPSocks5Server` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:64949` | `reference` | `IL_0001: ldfld class com.vision.smack.socks5.TCPSocks5Server com.vision.smack.socks5.TCPSocks5Connection::'k__BackingField'` | `L64942: .method private hidebysig specialname instance class com.vision.smack.socks5.TCPSocks5Server` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:64954` | `reference` | `set_Server(class com.vision.smack.socks5.TCPSocks5Server 'value') cil managed` | `L64953: .method private hidebysig specialname instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:64961` | `reference` | `IL_0002: stfld class com.vision.smack.socks5.TCPSocks5Server com.vision.smack.socks5.TCPSocks5Connection::'k__BackingField'` | `L64953: .method private hidebysig specialname instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:65018` | `callsite` | `IL_0001: call instance class com.vision.smack.socks5.TCPSocks5Server com.vision.smack.socks5.TCPSocks5Connection::get_Server()` | `L65012: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:65019` | `callsite` | `IL_0006: callvirt instance bool com.vision.smack.socks5.TCPSocks5Server::get_IsStarting()` | `L65012: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:65027` | `callsite` | `IL_0016: call instance class com.vision.smack.socks5.TCPSocks5Server com.vision.smack.socks5.TCPSocks5Connection::get_Server()` | `L65012: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:65028` | `callsite` | `IL_001b: callvirt instance bool com.vision.smack.socks5.TCPSocks5Server::get_RequireValidate()` | `L65012: .method private hidebysig instance void` | + +## Decision + +No existing IPC/plugin/local HTTP bridge is confirmed usable for B-route text send or file send. The next B-route branch should be `in_process_adapter_research`. Until that exists, an external sidecar can only do preview/probe work, while A-route/RPA remains backup-only. + +## Next work + +1. Update the B-route next plan and capability source matrix with `in_process_adapter_research` as the offline decision. +2. Create a separate plan before implementing any in-process adapter work. +3. Do not promote RPA beyond backup in this branch. diff --git a/docs/source-discovery/2026-07-12-broute-file-send-static-map.md b/docs/source-discovery/2026-07-12-broute-file-send-static-map.md new file mode 100644 index 0000000..601cde6 --- /dev/null +++ b/docs/source-discovery/2026-07-12-broute-file-send-static-map.md @@ -0,0 +1,230 @@ +# B-Route File Send Static Map + +Date: 2026-07-12 + +## Scope + +This report uses offline IL only. It does not upload files, send file messages, replay traffic, inject into a process, or mutate the original iSphere binaries. + +Machine-readable evidence: `runs/offline-broute-reverse/file-send-map.json` + +## Sources + +| Source | Role | Exists | Lines | Size bytes | +| --- | --- | --- | ---: | ---: | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il` | main client IL; UC_FileSend, OffLineFileSend, MessageScheduling | yes | 1360035 | 74148460 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il` | file transfer service implementation | yes | 3351 | 207786 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il` | file transfer contracts, parameters, and result DTOs | yes | 1641 | 93585 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Interface.dll.il` | transport/event interface contracts | yes | 4429 | 227335 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il` | Chat.sendFileMessage and XMPP message construction | yes | 87425 | 5019807 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\TcpFileTransfer.dll.il` | native TCP transfer candidate; IL may be unavailable | yes | 5 | 241 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Model.dll.il` | chat record models if extracted | no | 0 | | + +## Target classification + +| Target | Phase | Intent | Classification | Matches | +| --- | --- | --- | --- | ---: | +| `IMPP.Client.OffLineFileSend.sendFile` | `upload` | offline file upload orchestration from UI file sender | `confirmed_signature` | 3 | +| `IMPP.Client.OffLineFileSend.trans_UploadCompleted` | `upload_to_message_boundary` | upload-completed handler that converts FileUploadResult.FileID into a chat file message | `confirmed_signature` | 3 | +| `IMPP.Service.BLL.FileTransfer.FileUpload` | `upload` | synchronous service upload entry returning FileUploadResult | `confirmed_signature` | 17 | +| `IMPP.Service.BLL.FileTransfer.AsynFileUpload` | `upload` | asynchronous upload queue entry | `confirmed_signature` | 3 | +| `FileUploadPara` | `upload_parameters` | host/port/domain/local file path/cache/timeout/upload flags | `confirmed_signature` | 66 | +| `FileUploadResult.FileID` | `upload_result` | server-side file id and URL returned by upload | `confirmed_signature` | 75 | +| `MessageScheduling.SendFileMessage` | `message` | scheduled/file-message finalization path using RosterContactsArgs and FileTransferArgs | `confirmed_signature` | 3 | +| `Chat.sendFileMessage` | `message` | smack API that sends the XMPP file message after upload metadata is known | `confirmed_signature` | 8 | +| `Required values: path id size target jid` | `requirements` | static evidence for local file path, remote folder/file id, file size/name, target JID/chat, and server auth | `confirmed_signature` | 550 | + +## Upload step vs chat-message step + +| Step | Static evidence | Business meaning | +| --- | --- | --- | +| 1. Upload | `IMPP.Client.OffLineFileSend.sendFile` constructs `FileUploadPara` and calls `IFileTransfer.FileUpload`; `IMPP.Service.BLL.FileTransfer.FileUpload` delegates to `SyncFileTransfer` and private `Upload`. | The local file must be uploaded to the HTTP file service first. | +| 2. Upload result | `FileUploadResult.FileID` is read; upload notification calls `IMPP.Client.OffLineFileSend.trans_UploadCompleted(fileId)`. | The server-issued file id becomes the durable reference. | +| 3. Chat file message | `trans_UploadCompleted` and `MessageScheduling.SendFileMessage` build `OFFLINE_FILE_TRANSFER;...` payloads and call `Chat.sendFileMessage`. | The chat send step publishes metadata/reference, not raw file bytes. | + +Conclusion: file send is upload-first, chat-message-second. A direct chat message is insufficient without a valid uploaded file id or remote folder reference. + +## Required values + +| Value | Status | Evidence | +| --- | --- | --- | +| local file path | `required_confirmed` | OffLineFileSend constructor receives localFilePath; sendFile uses get__localFilePath; MessageScheduling.SendFileMessage uses FileTransferArgs.get_LocalFilePath. | +| server file id / remote folder | `required_confirmed` | Upload returns FileUploadResult.FileID; trans_UploadCompleted(fileId) passes it to Chat.sendFileMessage and Packet.set_id. MessageScheduling path uses FileTransferArgs.get_RemoteFolder. | +| file name and file size | `required_confirmed` | SendFileMessage builds OFFLINE_FILE_TRANSFER payload from FileInfo/FileSystemInfo name and length; OffLineFileSend keeps _fileSize. | +| target JID / chat object | `required_confirmed` | UC_FileSend and MessageScheduling both require a live smack Chat; scheduled path derives it from RosterContactsArgs.get_RosterJid + UtilsChat.GetChat. | +| HTTP file server and auth | `required_confirmed` | FileUploadPara construction uses Config.ServerInfo.HTTPFileServer IP/Port/domain; service upload builds /FS/auth/resource/upload URL with UnifiedAuthentication token/app key/client secret/time/user id. | +| file hash | `not_confirmed_in_file_send_path` | No file-transfer-path MD5/SHA/checksum requirement was confirmed in the configured IL. MD5 hits belong to image/RTF or auth digest areas, not the offline file-send path. | + +## Evidence excerpts + +### IMPP.Client.OffLineFileSend.sendFile + +Classification: `confirmed_signature`; phase: `upload`; matches: 3. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:826149` | `signature_continuation` | `sendFile() cil managed` | `L826148: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:826572` | `method_end` | `} // end of method OffLineFileSend::sendFile` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:824164` | `callsite` | `IL_026b: callvirt instance void IMPP.Client.OffLineFileSend::sendFile()` | `` | + +### IMPP.Client.OffLineFileSend.trans_UploadCompleted + +Classification: `confirmed_signature`; phase: `upload_to_message_boundary`; matches: 3. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:826888` | `signature_continuation` | `trans_UploadCompleted(string fileId) cil managed` | `L826887: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:827241` | `method_end` | `} // end of method OffLineFileSend::trans_UploadCompleted` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:826670` | `callsite` | `IL_0104: call instance void IMPP.Client.OffLineFileSend::trans_UploadCompleted(string)` | `L826574: .method private hidebysig instance void` | + +### IMPP.Service.BLL.FileTransfer.FileUpload + +Classification: `confirmed_signature`; phase: `upload`; matches: 17. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:851` | `signature_continuation` | `FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara para) cil managed` | `L849: .method public hidebysig newslot virtual final` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:870` | `method_end` | `} // end of method FileTransfer::FileUpload` | `L849: .method public hidebysig newslot virtual final` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:873` | `signature_continuation` | `instance void AsynFileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara para) cil managed` | `L872: .method public hidebysig newslot virtual final` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:164` | `method_end` | `} // end of method IFileTransfer::FileUpload` | `L160: .method public hidebysig newslot abstract virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:512182` | `callsite` | `IL_01d9: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608800` | `callsite` | `IL_00b2: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L608722: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608921` | `callsite` | `IL_00c5: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L608836: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:679721` | `callsite` | `IL_00bf: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L679642: .method public hidebysig static void UploadGroupImage(string jid,` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:699952` | `callsite` | `IL_024c: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:738959` | `callsite` | `IL_00bf: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L738869: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:739560` | `callsite` | `IL_00bf: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L739470: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:790010` | `callsite` | `IL_018b: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:826414` | `callsite` | `IL_02b6: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:2852` | `reference` | `} // end of property FileTransfer::FileUploadMaxCount` | `L2825: .method family hidebysig newslot virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:2861` | `reference` | `} // end of property FileTransfer::FileUploadCount` | `L2825: .method family hidebysig newslot virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:186` | `reference` | `} // end of property IFileTransfer::FileUploadMaxCount` | `L177: .method public hidebysig newslot abstract virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:205` | `reference` | `} // end of property IFileTransfer::FileUploadCount` | `L177: .method public hidebysig newslot abstract virtual` | + +### IMPP.Service.BLL.FileTransfer.AsynFileUpload + +Classification: `confirmed_signature`; phase: `upload`; matches: 3. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:873` | `signature_continuation` | `instance void AsynFileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara para) cil managed` | `L872: .method public hidebysig newslot virtual final` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:883` | `method_end` | `} // end of method FileTransfer::AsynFileUpload` | `L872: .method public hidebysig newslot virtual final` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:169` | `method_end` | `} // end of method IFileTransfer::AsynFileUpload` | `L166: .method public hidebysig newslot abstract virtual` | + +### FileUploadPara + +Classification: `confirmed_signature`; phase: `upload_parameters`; matches: 66. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:851` | `signature_continuation` | `FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara para) cil managed` | `L849: .method public hidebysig newslot virtual final` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:873` | `signature_continuation` | `instance void AsynFileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara para) cil managed` | `L872: .method public hidebysig newslot virtual final` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:162` | `signature_continuation` | `FileUpload(class IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara para) cil managed` | `L160: .method public hidebysig newslot abstract virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:167` | `signature_continuation` | `instance void AsynFileUpload(class IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara para) cil managed` | `L166: .method public hidebysig newslot abstract virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1139` | `type_definition` | `.class public auto ansi serializable beforefieldinit IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara` | `L1086: .method family hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1155` | `method_end` | `} // end of method FileUploadPara::get_IsImg` | `L1146: .method public hidebysig specialname instance bool` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1167` | `method_end` | `} // end of method FileUploadPara::set_IsImg` | `L1157: .method private hidebysig specialname instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1178` | `method_end` | `} // end of method FileUploadPara::get_IsPublic` | `L1169: .method public hidebysig specialname instance bool` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1190` | `method_end` | `} // end of method FileUploadPara::set_IsPublic` | `L1180: .method public hidebysig specialname instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1234` | `method_end` | `} // end of method FileUploadPara::.ctor` | `L1192: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1263` | `method_end` | `} // end of method FileUploadPara::.ctor` | `L1236: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:512182` | `callsite` | `IL_01d9: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:512459` | `constructor_call` | `IL_04a3: newobj instance void [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara::.ctor(string,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608787` | `constructor_call` | `IL_0092: newobj instance void [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara::.ctor(string,` | `L608722: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608795` | `callsite` | `IL_009c: callvirt instance void [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara::set_IsPublic(bool)` | `L608722: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608800` | `callsite` | `IL_00b2: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L608722: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608908` | `constructor_call` | `IL_00a5: newobj instance void [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara::.ctor(string,` | `L608836: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608916` | `callsite` | `IL_00af: callvirt instance void [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara::set_IsPublic(bool)` | `L608836: .method private hidebysig instance void` | + +### FileUploadResult.FileID + +Classification: `confirmed_signature`; phase: `upload_result`; matches: 75. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.Service.dll.il:982` | `method_header` | `.method private hidebysig instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult` | `L982: .method private hidebysig instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1277` | `type_definition` | `.class public auto ansi serializable beforefieldinit IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult` | `L1236: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1307` | `method_end` | `} // end of method FileUploadResult::.ctor` | `L1284: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1318` | `method_end` | `} // end of method FileUploadResult::get_FileID` | `L1309: .method public hidebysig specialname instance string` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1330` | `method_end` | `} // end of method FileUploadResult::set_FileID` | `L1320: .method private hidebysig specialname instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1341` | `method_end` | `} // end of method FileUploadResult::get_Url` | `L1332: .method public hidebysig specialname instance string` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:1353` | `method_end` | `} // end of method FileUploadResult::set_Url` | `L1343: .method private hidebysig specialname instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:512182` | `callsite` | `IL_01d9: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:512315` | `callsite` | `IL_0322: callvirt instance string [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult::get_FileID()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608800` | `callsite` | `IL_00b2: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L608722: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608819` | `callsite` | `IL_00e0: callvirt instance string [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult::get_FileID()` | `L608722: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608921` | `callsite` | `IL_00c5: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L608836: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:608940` | `callsite` | `IL_00f3: callvirt instance string [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult::get_FileID()` | `L608836: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:679721` | `callsite` | `IL_00bf: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L679642: .method public hidebysig static void UploadGroupImage(string jid,` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:679747` | `callsite` | `IL_00f4: callvirt instance string [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult::get_FileID()` | `L679642: .method public hidebysig static void UploadGroupImage(string jid,` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:699952` | `callsite` | `IL_024c: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:699993` | `callsite` | `IL_02bf: callvirt instance string [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult::get_FileID()` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:738959` | `callsite` | `IL_00bf: callvirt instance class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadResult [IMPP.ServiceBase]IMPP.ServiceBase.Interface.IFileTransfer::FileUpload(class [IMPP.ServiceBase]IMPP.ServiceBase.ModelBase.FileServiceBase.FileUploadPara)` | `L738869: .method private hidebysig instance void` | + +### MessageScheduling.SendFileMessage + +Classification: `confirmed_signature`; phase: `message`; matches: 3. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1153803` | `signature_continuation` | `SendFileMessage(class IMPP.Client.Business.RosterContacts.RosterContactsArgs rosterArgs,` | `L1153802: .method public hidebysig static class [smack]com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1154125` | `method_end` | `} // end of method MessageScheduling::SendFileMessage` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:728711` | `callsite` | `IL_01fb: call class [smack]com.vision.smack.packet.XmppMessage IMPP.Client.Core.MessageEngine.MessageScheduling::SendFileMessage(class IMPP.Client.Business.RosterContacts.RosterContactsArgs,` | `` | + +### Chat.sendFileMessage + +Classification: `confirmed_signature`; phase: `message`; matches: 8. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:1235` | `signature_continuation` | `sendFileMessage(string p_message,` | `L1233: .method public hidebysig newslot abstract virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:1244` | `method_end` | `} // end of method Chat::sendFileMessage` | `L1233: .method public hidebysig newslot abstract virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:21442` | `signature_continuation` | `sendFileMessage(string p_message,` | `L21441: .method public hidebysig virtual instance class com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:21490` | `method_end` | `} // end of method P2PChat::sendFileMessage` | `L21441: .method public hidebysig virtual instance class com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:30070` | `signature_continuation` | `sendFileMessage(string p_message,` | `L30069: .method public hidebysig virtual instance class com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:30119` | `method_end` | `} // end of method ChatRoom::sendFileMessage` | `L30069: .method public hidebysig virtual instance class com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:827225` | `callsite` | `IL_034d: callvirt instance class [smack]com.vision.smack.packet.XmppMessage [smack]com.vision.smack.Chat::sendFileMessage(string,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1153922` | `callsite` | `IL_00f1: callvirt instance class [smack]com.vision.smack.packet.XmppMessage [smack]com.vision.smack.Chat::sendFileMessage(string,` | `L1153802: .method public hidebysig static class [smack]com.vision.smack.packet.XmppMessage` | + +### Required values: path id size target jid + +Classification: `confirmed_signature`; phase: `requirements`; matches: 550. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:611789` | `method_end` | `} // end of method RosterContactsArgs::get_RosterJid` | `L611780: .method public hidebysig specialname instance class [smack]com.vision.smack.Jid` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:670956` | `type_definition` | `.class public auto ansi beforefieldinit IMPP.Client.Business.Authentication.UnifiedAuthenticationCenter` | `L670876: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:671260` | `method_end` | `} // end of method UnifiedAuthenticationCenter::Authentication` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:671331` | `method_end` | `} // end of method UnifiedAuthenticationCenter::GetServerTimeSpan` | `L671262: .method public hidebysig instance valuetype [mscorlib]System.TimeSpan` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:671390` | `method_end` | `} // end of method UnifiedAuthenticationCenter::ConvertStringToDateTime` | `L671333: .method private hidebysig instance valuetype [mscorlib]System.DateTime` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:671406` | `method_end` | `} // end of method UnifiedAuthenticationCenter::InitServiceManager` | `L671392: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:671416` | `method_end` | `} // end of method UnifiedAuthenticationCenter::.ctor` | `L671408: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:723604` | `method_end` | `} // end of method FileTransferArgs::get_LocalFilePath` | `L723595: .method public hidebysig specialname instance string` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:723650` | `method_end` | `} // end of method FileTransferArgs::get_RemoteFolder` | `L723641: .method public hidebysig specialname instance string` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:723876` | `method_end` | `} // end of method RosterContactsGroupArgs::get_RosterJid` | `L723867: .method public hidebysig specialname instance class [smack]com.vision.smack.Jid` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:767951` | `method_end` | `} // end of method ServerInfo::set_HTTPFileServer` | `L767941: .method public hidebysig specialname instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:767962` | `method_end` | `} // end of method ServerInfo::get_HTTPFileServer` | `L767953: .method public hidebysig specialname instance class IMPP.Client.Server` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:768272` | `method_end` | `} // end of method BaseConfig::UnifiedAuthenticationAddress` | `L768241: .method public hidebysig instance string` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:826017` | `method_end` | `} // end of method OffLineFileSend::get__localFilePath` | `L826008: .method private hidebysig specialname instance string` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:826109` | `method_end` | `} // end of method OffLineFileSend::get__fileSize` | `L826100: .method private hidebysig specialname instance int64` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1339551` | `method_end` | `} // end of method OffLineFileRecv::get__fileSize` | `L1339542: .method private hidebysig specialname instance int64` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPP.ServiceBase.dll.il:477` | `method_end` | `} // end of method FileTransferParaBase::get_LocalFilePath` | `L468: .method public hidebysig specialname instance string` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:13688` | `callsite` | `IL_0179: callvirt instance class [smack]com.vision.smack.Chat [smack]com.vision.smack.ChatManager::GetChat(class [smack]com.vision.smack.XMPPConnection,` | `` | + +## Inference + +- High confidence: upload and file-message finalization are separate steps. +- High confidence: `FileUploadPara` needs HTTP file server host/port/domain plus local file path and upload options. +- High confidence: `Chat.sendFileMessage` produces the XMPP file-message envelope after file metadata is available. +- Medium confidence: B-route file send must either call the same logged-in service/runtime or reproduce the authenticated HTTP upload plus XMPP file-message sequence. +- Low confidence until bridge evidence: an external sidecar can obtain the same service/auth/chat objects without being inside the logged-in client process. + +## Decision + +Offline static evidence supports B-route research, but file sending needs more than one method call: upload first, then chat file-message finalization. The next blocker is reachability: whether an IPC/plugin/in-process bridge can access the logged-in `IFileTransfer` service and smack `Chat` object. + +## Next work + +1. Extract bridge/IPC/plugin candidates before coding any file-send adapter. +2. If a bridge exists, prototype non-mutating discovery first: service availability, chat object lookup, and upload endpoint metadata only. +3. Keep A-route/RPA backup-only. diff --git a/docs/source-discovery/2026-07-12-broute-text-send-static-map.md b/docs/source-discovery/2026-07-12-broute-text-send-static-map.md new file mode 100644 index 0000000..edbc117 --- /dev/null +++ b/docs/source-discovery/2026-07-12-broute-text-send-static-map.md @@ -0,0 +1,149 @@ +# B-Route Text Send Static Map + +Date: 2026-07-12 + +## Scope + +This report uses offline IL only. It does not log in, send messages, replay traffic, inject into a process, or mutate the original iSphere binaries. + +Machine-readable evidence: `runs/offline-broute-reverse/text-send-map.json` + +## Sources + +| Source | Role | Exists | Lines | Size bytes | +| --- | --- | --- | ---: | ---: | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il` | main managed client IL; chat UI, AppContextManager, scheduling call sites | yes | 1360035 | 74148460 | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il` | XMPP/smack managed IL; Chat and XMPPConnection implementation | yes | 87425 | 5019807 | + +## Target classification + +| Target | Intent | Classification | Matches | +| --- | --- | --- | ---: | +| `AppContextManager.SendTxtMessageByJid` | high-level text send entry exposed by AppContextManager | `confirmed_signature` | 4 | +| `MessageScheduling.SendChatMessage` | message scheduling text/chat send helper | `confirmed_signature` | 6 | +| `Chat.SendMessage` | smack Chat send API used by chat/UI code | `confirmed_signature` | 16 | +| `XMPPConnection.send` | lowest-level XMPP connection send primitive | `confirmed_signature` | 105 | +| `MessageCenter send-related callbacks` | MessageCenter or IMPPManager message-center callbacks touching send/message flow | `confirmed_signature` | 962 | + +## Verified facts + +- `AppContextManager.SendTxtMessageByJid` appears in the offline client IL and is not just a UIA/RPA artifact. +- `MessageScheduling.SendChatMessage` appears in the offline client IL and has call-site evidence around chat-message flow. +- `Chat.SendMessage` / `Chat.sendMessage` appears as a smack chat API used by client code. +- `XMPPConnection.send` appears as a lower-level connection send primitive in offline IL. +- `MessageCenter send-related callbacks` appear as MessageCenter/GetMessageCenter references, but offline static evidence alone does not prove an external bridge can call them. + +## Evidence excerpts + +### AppContextManager.SendTxtMessageByJid + +Classification: `confirmed_signature`; matches: 4. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:585987` | `signature_continuation` | `instance void SendTxtMessageByJid(string jid,` | `L585986: .method public hidebysig newslot virtual final` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:586197` | `method_end` | `} // end of method AppContextManager::SendTxtMessageByJid` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:542112` | `string_literal` | `IL_09bf: ldstr "SendTxtMessageByJid"` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1358068` | `string_literal` | `IL_0c3f: ldstr "SendTxtMessageByJid"` | `` | + +### MessageScheduling.SendChatMessage + +Classification: `confirmed_signature`; matches: 6. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1153800` | `method_end` | `} // end of method MessageScheduling::SendChatMessage` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1154225` | `method_end` | `} // end of method MessageScheduling::SendChatMessage` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:728720` | `callsite` | `IL_0213: call class [smack]com.vision.smack.packet.XmppMessage IMPP.Client.Core.MessageEngine.MessageScheduling::SendChatMessage(class IMPP.Client.Business.RosterContacts.RosterContactsArgs,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1154187` | `callsite` | `IL_006e: call class [smack]com.vision.smack.packet.XmppMessage IMPP.Client.Core.MessageEngine.MessageScheduling::SendChatMessage(class [smack]com.vision.smack.Chat,` | `L1154127: .method public hidebysig static class [smack]com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1153541` | `reference` | `SendChatMessage(class [smack]com.vision.smack.Chat chat,` | `L1153540: .method public hidebysig static class [smack]com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1154128` | `reference` | `SendChatMessage(class IMPP.Client.Business.RosterContacts.RosterContactsArgs rosterArgs,` | `L1154127: .method public hidebysig static class [smack]com.vision.smack.packet.XmppMessage` | + +### Chat.SendMessage + +Classification: `confirmed_signature`; matches: 16. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:1220` | `method_end` | `} // end of method Chat::sendMessage` | `L1217: .method public hidebysig newslot abstract virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:1231` | `method_end` | `} // end of method Chat::sendMessage` | `L1222: .method public hidebysig newslot abstract virtual` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:1425` | `method_end` | `} // end of method Chat::SendMessage` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:1266` | `callsite` | `IL_0021: call instance class com.vision.smack.packet.XmppMessage com.vision.smack.Chat::SendMessage(class com.vision.smack.packet.XmppMessage)` | `L1246: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:21393` | `callsite` | `IL_0047: call instance class com.vision.smack.packet.XmppMessage com.vision.smack.Chat::SendMessage(class com.vision.smack.packet.XmppMessage)` | `L21362: .method public hidebysig virtual instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:21431` | `callsite` | `IL_002b: call instance class com.vision.smack.packet.XmppMessage com.vision.smack.Chat::SendMessage(class com.vision.smack.packet.XmppMessage)` | `L21398: .method public hidebysig virtual instance class com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:21482` | `callsite` | `IL_003a: call instance class com.vision.smack.packet.XmppMessage com.vision.smack.Chat::SendMessage(class com.vision.smack.packet.XmppMessage)` | `L21441: .method public hidebysig virtual instance class com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:29958` | `callsite` | `IL_0047: call instance class com.vision.smack.packet.XmppMessage com.vision.smack.Chat::SendMessage(class com.vision.smack.packet.XmppMessage)` | `L29927: .method public hidebysig virtual instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:30063` | `callsite` | `IL_0064: call instance class com.vision.smack.packet.XmppMessage com.vision.smack.Chat::SendMessage(class com.vision.smack.packet.XmppMessage)` | `L30012: .method public hidebysig virtual instance class com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:30115` | `callsite` | `IL_0056: call instance class com.vision.smack.packet.XmppMessage com.vision.smack.Chat::SendMessage(class com.vision.smack.packet.XmppMessage)` | `L30069: .method public hidebysig virtual instance class com.vision.smack.packet.XmppMessage` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:299997` | `callsite` | `IL_0096: callvirt instance class [smack]com.vision.smack.packet.XmppMessage [smack]com.vision.smack.Chat::SendMessage(class [smack]com.vision.smack.packet.XmppMessage)` | `L299937: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:412929` | `callsite` | `IL_00d2: callvirt instance class [smack]com.vision.smack.packet.XmppMessage [smack]com.vision.smack.Chat::SendMessage(class [smack]com.vision.smack.packet.XmppMessage)` | `L412858: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:430535` | `callsite` | `IL_0357: callvirt instance class [smack]com.vision.smack.packet.XmppMessage [smack]com.vision.smack.Chat::SendMessage(class [smack]com.vision.smack.packet.XmppMessage)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:739211` | `callsite` | `IL_012c: callvirt instance class [smack]com.vision.smack.packet.XmppMessage [smack]com.vision.smack.Chat::sendMessage(string,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:739789` | `callsite` | `IL_012c: callvirt instance class [smack]com.vision.smack.packet.XmppMessage [smack]com.vision.smack.Chat::sendMessage(string,` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:1153743` | `callsite` | `IL_01d0: callvirt instance class [smack]com.vision.smack.packet.XmppMessage [smack]com.vision.smack.Chat::SendMessage(class [smack]com.vision.smack.packet.XmppMessage)` | `` | + +### XMPPConnection.send + +Classification: `confirmed_signature`; matches: 105. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:86319` | `method_end` | `} // end of method XMPPConnection::send` | `L86263: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:1378` | `callsite` | `IL_00bc: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:17710` | `callsite` | `IL_004a: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L17649: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:23187` | `callsite` | `IL_004b: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L23150: .method public hidebysig instance class com.vision.smack.disco.DiscoInfoResponse` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:23248` | `callsite` | `IL_002c: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L23220: .method public hidebysig virtual instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:23962` | `callsite` | `IL_0110: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:26786` | `callsite` | `IL_004e: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L26752: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:26855` | `callsite` | `IL_008b: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L26790: .method public hidebysig instance class [mscorlib]System.Collections.Generic.IList`1` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:26980` | `callsite` | `IL_008a: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L26919: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:29230` | `callsite` | `IL_00a2: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L29148: .method public hidebysig instance bool` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:29470` | `callsite` | `IL_007d: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L29404: .method public hidebysig instance bool` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:29690` | `callsite` | `IL_0053: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L29646: .method public hidebysig instance bool` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:29841` | `callsite` | `IL_00b2: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L29766: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:29918` | `callsite` | `IL_0033: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L29894: .method public hidebysig virtual instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:30152` | `callsite` | `IL_003c: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L30121: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:30178` | `callsite` | `IL_0023: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L30159: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:30221` | `callsite` | `IL_0046: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L30182: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\smack.dll.il:30268` | `callsite` | `IL_004d: callvirt instance void com.vision.smack.XMPPConnection::send(string)` | `L30225: .method public hidebysig instance void` | + +### MessageCenter send-related callbacks + +Classification: `confirmed_signature`; matches: 962. + +| Source line | Kind | Snippet | Method context | +| --- | --- | --- | --- | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:74199` | `method_end` | `} // end of method MessageCenter::add_OnMessageSended` | `L74135: .method public hidebysig specialname instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:74265` | `method_end` | `} // end of method MessageCenter::remove_OnMessageSended` | `L74201: .method public hidebysig specialname instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:75658` | `method_end` | `} // end of method MessageCenter::pChat_OnMessageSended` | `L75587: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:82645` | `method_end` | `} // end of method MessageCenter::SendPluginsOffLineMessage` | `` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:206380` | `method_end` | `} // end of method AVRoomMessageCenter::Send` | `L206369: .method public hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:945209` | `method_header` | `.method public hidebysig instance class IMPP.Client.control.MessageCenter` | `L945209: .method public hidebysig instance class IMPP.Client.control.MessageCenter` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:945695` | `method_header` | `.method public hidebysig specialname instance class IMPP.Client.control.MessageCenter` | `L945695: .method public hidebysig specialname instance class IMPP.Client.control.MessageCenter` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72786` | `callsite` | `IL_0005: callvirt instance class IMPP.Client.control.MessageCenter IMPP.Client.IMPPManager::GetMessageCenter()` | `L72779: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72787` | `callsite` | `IL_000a: callvirt instance int32 IMPP.Client.control.MessageCenter::GetChatMessageCount()` | `L72779: .method private hidebysig instance void` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72814` | `callsite` | `IL_000b: callvirt instance class IMPP.Client.control.MessageCenter IMPP.Client.IMPPManager::GetMessageCenter()` | `L72806: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72820` | `callsite` | `IL_001c: callvirt instance void IMPP.Client.control.MessageCenter::add_OnMessageArrived(class IMPP.Client.control.MessageCenter/MessageArrivedHandler)` | `L72806: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72822` | `callsite` | `IL_0026: callvirt instance class IMPP.Client.control.MessageCenter IMPP.Client.IMPPManager::GetMessageCenter()` | `L72806: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72827` | `callsite` | `IL_0037: callvirt instance void IMPP.Client.control.MessageCenter::add_OnMessageConsumed(class IMPP.Client.control.MessageCenter/MessageConsumedHandler)` | `L72806: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72829` | `callsite` | `IL_0041: callvirt instance class IMPP.Client.control.MessageCenter IMPP.Client.IMPPManager::GetMessageCenter()` | `L72806: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72835` | `callsite` | `IL_0052: callvirt instance void IMPP.Client.control.MessageCenter::add_UpdateUnreadMessageCount(class IMPP.Client.control.MessageCenter/UpdateUnreadMessageCountHandler)` | `L72806: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72837` | `callsite` | `IL_005c: callvirt instance class IMPP.Client.control.MessageCenter IMPP.Client.IMPPManager::GetMessageCenter()` | `L72806: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72842` | `callsite` | `IL_006d: callvirt instance void IMPP.Client.control.MessageCenter::add_TcpFileToOffline(class [mscorlib]System.Action`1)` | `L72806: .method public hidebysig specialname rtspecialname` | +| `runs\offline-evidence-intake\zyl-qqfile-20260709\metadata\c28-il\IMPlatformClient.exe.il:72844` | `callsite` | `IL_0077: callvirt instance class IMPP.Client.control.MessageCenter IMPP.Client.IMPPManager::GetMessageCenter()` | `L72806: .method public hidebysig specialname rtspecialname` | + +## Inference + +- High confidence: text sending has an internal managed call path that reaches smack chat/connection send APIs. +- Medium confidence: the likely chain is high-level AppContextManager or UI chat code -> MessageScheduling/frmP2PChat -> smack Chat.SendMessage/sendMessage -> XMPPConnection.send. +- Low confidence until live/runtime evidence: an external sidecar can invoke this path without being inside the logged-in process. + +## Decision + +B-route text send remains plausible only if a bridge can enter the logged-in runtime or an existing IPC/API exposes the same call. Offline IL confirms call shape, but not runtime invocability. + +## Next work + +1. Extract file-send/upload call graph and separate upload from chat-message finalization. +2. Extract bridge/IPC/plugin candidates to see whether the send call path is reachable without RPA. +3. Keep A-route/RPA as backup-only; do not promote it unless B-route bridge evidence fails. diff --git a/docs/source-discovery/2026-07-12-offline-broute-reverse-decision.md b/docs/source-discovery/2026-07-12-offline-broute-reverse-decision.md new file mode 100644 index 0000000..048154d --- /dev/null +++ b/docs/source-discovery/2026-07-12-offline-broute-reverse-decision.md @@ -0,0 +1,65 @@ +# Offline B-Route Reverse Decision + +Date: 2026-07-12 +Branch: `codex/offline-broute-reverse` + +## Current phase / 当前阶段 + +Offline reverse engineering for the primary B-route is complete for the current evidence set. The goal was to decide whether B-route can continue through an existing IPC/plugin/local API, or whether it must move to an in-process adapter research branch. + +## Inputs / 输入证据 + +| Evidence | Path | +| --- | --- | +| Offline sample index | `docs/source-discovery/2026-07-12-offline-broute-reverse-index.md` | +| Text send static map | `docs/source-discovery/2026-07-12-broute-text-send-static-map.md` | +| File send static map | `docs/source-discovery/2026-07-12-broute-file-send-static-map.md` | +| Bridge/IPC/plugin static map | `docs/source-discovery/2026-07-12-broute-bridge-static-map.md` | +| Machine JSON outputs | `runs/offline-broute-reverse/*.json` | + +## Verified facts / 已验证事实 + +1. The offline sample set is present and hash-matched against the recorded plan list; the analyzer scripts only read metadata/IL and did not copy or modify binaries. +2. Text send has confirmed static call-path evidence: + - `AppContextManager.SendTxtMessageByJid` + - `MessageScheduling.SendChatMessage` + - `Chat.SendMessage` / `Chat.sendMessage` + - `XMPPConnection.send` +3. File send has confirmed two-step evidence: + - upload first through `OffLineFileSend.sendFile`, `FileUploadPara`, and `IFileTransfer.FileUpload` / `IMPP.Service.BLL.FileTransfer.FileUpload`; + - then chat file-message finalization through `trans_UploadCompleted(fileId)`, `MessageScheduling.SendFileMessage`, and `Chat.sendFileMessage`. +4. `FileUploadResult.FileID` / remote-folder metadata is required before the chat file message is meaningful. +5. The static bridge scan found no confirmed existing IPC/plugin/local HTTP route that exposes text-send or file-send. +6. `HttpServerLib` exists, but the discovered `frmLogin.StartWinServer` path depends on logged-in runtime state and no send route was found. +7. `PluginLightAppMulProcessCommunication` declares a `NamedPipeServerStream`, but its start method returns immediately. +8. Returned live probe saw `\\.\pipe\zfpinject-msg-server`, but no matching iSphere static send contract was found. + +## Decision / 决策 + +| Candidate | Decision | Reason | +| --- | --- | --- | +| `existing_ipc_first` | rejected for now | No concrete local HTTP/named-pipe/service send API was confirmed. | +| `plugin_extension_first` | rejected for now | Plugin evidence is UI/app-store/context-menu oriented; no local plugin load/send bridge is confirmed. | +| `in_process_adapter_research` | selected | The send and file-send call paths exist, but they need access to the logged-in `IMPlatformClient.exe` runtime objects. | +| `external_sidecar_preview_only` | allowed | External sidecar can validate contracts/probes, but cannot reuse logged-in static runtime by itself. | +| `fallback_rpa_only` | not selected | A-route/RPA remains backup-only; do not promote it unless B-route is explicitly blocked by a later decision. | + +Final offline decision: **B-route stays primary, but the next real branch is `in_process_adapter_research`, not existing IPC/plugin and not production send.** + +## Business impact / 业务影响 + +- Message receiving, file receiving/listing, contact search, and group search stay on the existing data/log-backed roadmap. +- Text send is technically mapped but not business-complete: the missing piece is a bridge into logged-in runtime state and later online ack/sent-record validation. +- File send is harder than text send: upload and chat-message finalization are separate, and upload needs HTTP file-service auth plus server-issued file id. +- RPA remains a practical backup for UI smoke, but it is not the product architecture. + +## Next implementation slice / 下一轮 + +1. Create a dedicated in-process adapter research plan. +2. Keep any sidecar work preview/probe-only until an adapter path is selected. +3. Do not implement real `send_message`, `send_file`, or `upload_file` in this offline branch. +4. Online evidence later must prove sent-record/content-hash/ack before production send can be called complete. + +## Stop conditions / 停止条件 + +Stop and ask for business decision if the next in-process research shows that every viable bridge requires invasive process mutation, unsupported injection, or cannot be validated without a logged-in test environment. diff --git a/docs/source-discovery/2026-07-12-offline-broute-reverse-index.md b/docs/source-discovery/2026-07-12-offline-broute-reverse-index.md new file mode 100644 index 0000000..30fe215 --- /dev/null +++ b/docs/source-discovery/2026-07-12-offline-broute-reverse-index.md @@ -0,0 +1,45 @@ +# Offline B-Route Reverse Index + +Date: 2026-07-12 + +## Scope + +This index records the read-only offline samples for B-route reverse engineering. The script reads metadata and SHA256 hashes only; it does not copy or modify the original binaries. + +## Summary + +- Samples total: 12 +- Samples present: 12 +- Hash matches: 12 +- Originals copied: false +- Originals modified: false +- Machine JSON: `runs/offline-broute-reverse/index.json` + +## Sample table + +| Sample | Role | Size | SHA256 match | SHA256 | +| --- | --- | ---: | --- | --- | +| `runs/offline-real-client-window/full/zyl/Impp/IMPlatformClient.exe` | main managed client; text send, chat window, plugin/IPC candidates | 8366592 | yes | `E2966E58360DAEEBF178410A961E2DC52748C63E9CCBD94BD6EC8434A1A09CB7` | +| `runs/offline-real-client-window/full/zyl/Impp/IMPP.Service.dll` | file upload service candidates | 21504 | yes | `F859DE2F34E024B5F372D95F22CBF8A018C780F24203E7A76D392E52BD90CADD` | +| `runs/offline-real-client-window/full/zyl/Impp/IMPP.Interface.dll` | service/interface contract candidates | 33792 | yes | `DD093CF61FAE85C2D2581F289B01AA477546712E098FEF81272FDD989E50D12D` | +| `runs/offline-real-client-window/full/zyl/Impp/IMPP.Model.dll` | model/parameter types | 35328 | yes | `7CBABDD23A7234D15E294C549EEEC78EFBAD6E957CD45A6E8A85309349A624A5` | +| `runs/offline-real-client-window/full/zyl/Impp/IMPP.UI.dll` | UI/plugin helpers and possible send controls | 2401792 | yes | `4C61212519C01D1B731ED4F20DF3411C491616F4ADA98A4529722A6AA3689E6D` | +| `runs/offline-real-client-window/full/zyl/Impp/IMPP.Common.dll` | shared utility and message types | 479744 | yes | `02A47C99C27C9BFE2BADC438389300C92220B07609BD87C48B4B5E1B4EE6AEB4` | +| `runs/offline-real-client-window/full/zyl/Impp/IMPP.Helper.dll` | helper abstractions | 53248 | yes | `30932D082B6678699C832C63F923E01EAD84674FAEDB613D631E034E03742FB9` | +| `runs/offline-real-client-window/full/zyl/Impp/smack.dll` | XMPP chat/send layer | 370688 | yes | `BAD0BB0591765DB153B17CECC1112B0BBDBF3B1D3370DDF973F91974E92CEC66` | +| `runs/offline-real-client-window/full/zyl/Impp/TcpFileTransfer.dll` | native/transfer candidate | 45056 | yes | `6C4874B46D1E7DC04C9B6784180603452400B881E04509D464A0D58814EFEDA7` | +| `runs/offline-real-client-window/full/zyl/Impp/HttpServerLib.dll` | local API/IPC candidate | 26624 | yes | `FFDB633216AE332B8EDFFC83E2AE546B7A80144C659FF4053FDFE4FE382A8D12` | +| `runs/offline-real-client-window/full/zyl/Impp/INetwork.dll` | network abstraction candidate | 87552 | yes | `E40BE4EE2439E4280461F4D2BADB318A5F4572428F069AB6D17B4C4F0E8CFAC9` | +| `runs/offline-real-client-window/full/zyl/Impp/IOClientNetwork.dll` | network implementation candidate | 145920 | yes | `64CA231A614771325741A5F098E52EBF20F7190E339B64962DE2EDEDCFFFF904` | + +## Verified facts + +- All metadata came from local filesystem reads under the repository workspace. +- The expected primary B-route samples are present if Samples present equals Samples total. +- Hash matching only proves local file identity against this plan's recorded sample list; it does not prove runtime invocability. + +## Next reverse targets + +1. Text-send static call graph: SendTxtMessageByJid, MessageScheduling.SendChatMessage, Chat.SendMessage, XMPPConnection.send. +2. File-send/upload static call graph: OffLineFileSend.sendFile, FileTransfer.FileUpload, AsynFileUpload, sendFileMessage. +3. Bridge/IPC/plugin map: HttpServerLib, plugin loaders, local HTTP, named pipes, command-line switches, services. diff --git a/docs/source-discovery/capability-source-matrix.md b/docs/source-discovery/capability-source-matrix.md index e328e96..8a3baad 100644 --- a/docs/source-discovery/capability-source-matrix.md +++ b/docs/source-discovery/capability-source-matrix.md @@ -17,6 +17,11 @@ R14 release candidate: `docs/reports/2026-07-10-r6f-r14-release-candidate.md` Main status correction: `docs/reports/2026-07-12-main-business-status-correction.md` A-route UIA send: `docs/source-discovery/2026-07-11-a-route-uia-send.md` A-route real offline chat window: `docs/source-discovery/2026-07-11-a-route-open-real-chat-window.md` +Offline B-route reverse index: `docs/source-discovery/2026-07-12-offline-broute-reverse-index.md` +Offline B-route text-send map: `docs/source-discovery/2026-07-12-broute-text-send-static-map.md` +Offline B-route file-send map: `docs/source-discovery/2026-07-12-broute-file-send-static-map.md` +Offline B-route bridge map: `docs/source-discovery/2026-07-12-broute-bridge-static-map.md` +Offline B-route decision: `docs/source-discovery/2026-07-12-offline-broute-reverse-decision.md` ## Source priority @@ -37,8 +42,8 @@ Route rule: UIA helper / A-route / RPA is a backup route only. It can preserve d | `isphere_search_contacts` | bare sender/receiver JIDs extracted from normalized log-backed messages loaded from configured PacketReader file or directory source | validated copied `MsgLib.db` tables: `TD_Roster`, `TD_CustomEffigy`, `tblRecent`, `tblChatLevel`, `tblPersonMsg`; decrypted roster/contact stanzas from `Smark.SendReceive`; UIA helper source if display names are still missing | `docs/source-discovery/2026-07-10-msglib-readonly-schema-extraction.md`; `internal/isphere/contacts_test.go`; `internal/tools/isphere_contacts_test.go` | C34 documents optional MsgLib contact display enrichment; R2 adds deterministic exact-match-first ranking, case-insensitive de-duplication across log/MsgLib candidates, and preserves `source`/`raw_ref` | core contact search complete; optional richer fields later | | `isphere_search_groups` | decrypted `PacketReader.ProcessPacket` `type="groupchat"` stanzas and conference/MUC JIDs loaded from configured PacketReader file or directory source | validated copied `MsgLib.db` tables: `tblMsgGroupPersonMsg`, `TD_WorkGroupAuth`, `tblRecent`; UIA helper source if group display names are still missing | `docs/source-discovery/2026-07-10-msglib-readonly-schema-extraction.md`; `internal/isphere/groups_test.go`; `internal/tools/isphere_groups_test.go`; C9 ignored-log scan: PacketReader 86 groupchat/86 conference hits; Smark 24 groupchat/658 conference/631 muc hits | C34 documents optional MsgLib group display enrichment; R2 adds deterministic exact-match-first ranking, case-insensitive de-duplication across log/MsgLib candidates, and preserves `source`/`raw_ref` | core group search complete; optional member/owner hierarchy later | | `isphere_receive_files` | decrypted `PacketReader.ProcessPacket` file-transfer message stanzas via `internal/isphere.ListFilesFromMessages` and `isphere_receive_files` list mode; configured PacketReader file or directory source; `zyl\importal\` cache entries remain unlinked | MsgLib `TD_ReceiveFileRecord` safe metadata through explicit copied-DB path; decrypted `Smark.SendReceive` and `SaveToDB` traces for file-reference reconciliation; future UIA/client connector for download | `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md#c12-file-transfer-evidence-precheck`; `docs/source-discovery/2026-07-10-file-download-mapping-precheck.md`; `docs/source-discovery/2026-07-10-file-cache-mapping-diagnostic.md`; `docs/source-discovery/2026-07-10-file-download-mapping-v2.md`; `docs/reports/2026-07-10-business-goals-smoke.md` | C22 verifies safe file-list contract args; R10 proves resolver scoring with fixture accepted matches; R11 adds structured download preview with `blocked`/`planned` status and side-effect flags false; R13/R14 confirms list is ready but real download is still blocked. | blocked for real download pending accepted real cache mapping and copy gate; list mode is complete | -| `isphere_send_message` | B-route selected: running-client sidecar / in-process connector, preferring `AppContextManager.SendTxtMessageByJid(...)` or `MessageScheduling.SendChatMessage(...)` over the unimplemented `SendP2PMessage(...)` interface path; default MCP tool still exposes preview/dry-run unless an explicit connector mode is configured | A-route fallback is now implemented for UI action: `ISPHERE_SEND_CONNECTOR_MODE=uia_rpa` calls WinHelper `uia_send_message` against a configured HWND, writes `rtbSendMessage`, and clicks `btnSend`; this stays backup-only and network/protocol replay remains deferred | `docs/source-discovery/2026-07-10-send-message-source-precheck.md`; `docs/source-discovery/2026-07-10-send-message-connector-selection.md`; `docs/source-discovery/2026-07-10-send-sidecar-b-first-plan.md`; `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md`; `docs/source-discovery/2026-07-10-send-connector-preflight.md`; `docs/source-discovery/2026-07-10-send-sandbox-gate.md`; `docs/source-discovery/2026-07-10-returned-send-sandbox-analysis.md`; `docs/source-discovery/2026-07-10-returned-send-sandbox-analysis-v2.md`; `docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md`; `docs/source-discovery/2026-07-11-a-route-uia-send.md`; `docs/source-discovery/2026-07-11-a-route-open-real-chat-window.md`; `internal/tools/isphere_send_message_test.go`; `cmd/isphere-capability-smoke/main.go`; `docs/reports/2026-07-10-business-goals-smoke.md` | R6f-R6l define connector contract, audit/idempotency replay, production gate, B-route shell, strict-v2 package, and explicit production closure. R10a validates two returned sent-record packages as manual-only evidence. A-route adds local UI action readiness: synthetic window proof plus real offline `frmP2PChat` open/write/click target proof. Business delivery is still not verified because local runtime is offline and no success/ack or sent-record hash evidence has passed. | B-route evidence remains next priority; A-route UI action is backup-ready only; business send remains blocked until online sent-record/content-hash/ack evidence passes | -| `isphere_send_file` | B-route selected after send-message probe: managed offline-file path through `OffLineFileSend.sendFile()`, `IMPP.Service*.dll` `IFileTransfer.FileUpload(...)`, and `Chat.sendFileMessage(...)`; current MCP tool exposes preview/dry-run only | A-route file fallback is not implemented yet; real offline UIA dump did not expose stable `btnSendFile`, so the next fallback slice is toolbar/menu locator discovery; native `TcpFileTransfer.dll` is a later candidate only after managed path is rejected | `docs/source-discovery/2026-07-10-send-message-source-precheck.md`; `docs/source-discovery/2026-07-10-send-message-connector-selection.md`; `docs/source-discovery/2026-07-10-send-sidecar-b-first-plan.md`; `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md`; `docs/source-discovery/2026-07-10-send-connector-preflight.md`; `docs/source-discovery/2026-07-10-send-file-sandbox-gate.md`; `docs/source-discovery/2026-07-11-a-route-open-real-chat-window.md`; `internal/tools/isphere_files_test.go`; `docs/reports/2026-07-10-business-goals-smoke.md` | R7 registers send-file preview as the tenth MCP tool; R8 adds audit/idempotency duplicate/conflict hardening without raw file paths/content; R9 builds and verifies the online send-file evidence package; R13/R14 confirms preview is ready and production upload remains blocked. A-route real-window work confirms file-button automation still needs a separate locator node. | blocked until file toolbar/menu locator is found and returned file-send package proves upload/sent evidence | +| `isphere_send_message` | B-route selected: `in_process_adapter_research` after offline reverse. Static call path is confirmed through `AppContextManager.SendTxtMessageByJid(...)`, `MessageScheduling.SendChatMessage(...)`, `Chat.SendMessage`, and `XMPPConnection.send`, but no existing IPC/plugin/local HTTP send bridge is confirmed; default MCP tool still exposes preview/dry-run unless an explicit connector mode is configured | A-route fallback is now implemented for UI action: `ISPHERE_SEND_CONNECTOR_MODE=uia_rpa` calls WinHelper `uia_send_message` against a configured HWND, writes `rtbSendMessage`, and clicks `btnSend`; this stays backup-only and network/protocol replay remains deferred | `docs/source-discovery/2026-07-10-send-message-source-precheck.md`; `docs/source-discovery/2026-07-10-send-message-connector-selection.md`; `docs/source-discovery/2026-07-10-send-sidecar-b-first-plan.md`; `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md`; `docs/source-discovery/2026-07-10-send-connector-preflight.md`; `docs/source-discovery/2026-07-10-send-sandbox-gate.md`; `docs/source-discovery/2026-07-10-returned-send-sandbox-analysis.md`; `docs/source-discovery/2026-07-10-returned-send-sandbox-analysis-v2.md`; `docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md`; `docs/source-discovery/2026-07-11-a-route-uia-send.md`; `docs/source-discovery/2026-07-11-a-route-open-real-chat-window.md`; `docs/source-discovery/2026-07-12-broute-text-send-static-map.md`; `docs/source-discovery/2026-07-12-broute-bridge-static-map.md`; `docs/source-discovery/2026-07-12-offline-broute-reverse-decision.md`; `internal/tools/isphere_send_message_test.go`; `cmd/isphere-capability-smoke/main.go`; `docs/reports/2026-07-10-business-goals-smoke.md` | R6f-R6l define connector contract, audit/idempotency replay, production gate, B-route shell, strict-v2 package, and explicit production closure. R10a validates two returned sent-record packages as manual-only evidence. A-route adds local UI action readiness: synthetic window proof plus real offline `frmP2PChat` open/write/click target proof. Offline B-route reverse confirms the send call path but rejects existing IPC/plugin-first for this evidence set; business delivery is still not verified because local runtime is offline and no success/ack or sent-record hash evidence has passed. | Create a dedicated B-route `in_process_adapter_research` plan; keep external sidecar preview/probe-only and A-route UI action backup-only; business send remains blocked until adapter reachability plus online sent-record/content-hash/ack evidence passes | +| `isphere_send_file` | B-route selected after offline reverse: managed file send is upload-first through `OffLineFileSend.sendFile()`, `FileUploadPara`, `IMPP.Service*.dll` `IFileTransfer.FileUpload(...)`, then chat file-message finalization through `trans_UploadCompleted(fileId)`, `MessageScheduling.SendFileMessage`, and `Chat.sendFileMessage(...)`; current MCP tool exposes preview/dry-run only | A-route file fallback is not implemented yet; real offline UIA dump did not expose stable `btnSendFile`, so the fallback slice remains toolbar/menu locator discovery; native `TcpFileTransfer.dll` is not the first B-route bridge because managed upload/message flow is now mapped | `docs/source-discovery/2026-07-10-send-message-source-precheck.md`; `docs/source-discovery/2026-07-10-send-message-connector-selection.md`; `docs/source-discovery/2026-07-10-send-sidecar-b-first-plan.md`; `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md`; `docs/source-discovery/2026-07-10-send-connector-preflight.md`; `docs/source-discovery/2026-07-10-send-file-sandbox-gate.md`; `docs/source-discovery/2026-07-11-a-route-open-real-chat-window.md`; `docs/source-discovery/2026-07-12-broute-file-send-static-map.md`; `docs/source-discovery/2026-07-12-broute-bridge-static-map.md`; `docs/source-discovery/2026-07-12-offline-broute-reverse-decision.md`; `internal/tools/isphere_files_test.go`; `docs/reports/2026-07-10-business-goals-smoke.md` | R7 registers send-file preview as the tenth MCP tool; R8 adds audit/idempotency duplicate/conflict hardening without raw file paths/content; R9 builds and verifies the online send-file evidence package; R13/R14 confirms preview is ready and production upload remains blocked. Offline B-route reverse confirms upload-first/file-message-second and shows file id/auth/runtime reachability are the blockers. A-route real-window work confirms file-button automation still needs a separate locator node. | blocked until B-route in-process adapter research proves access to logged-in `IFileTransfer`/`Chat` objects and returned file-send package proves upload/sent evidence; A-route locator remains backup-only | ## Stage C entry rule @@ -55,4 +60,4 @@ Start Stage C with a narrow log-backed `isphere_receive_messages` source abstrac ## Deferred source work -`MsgLib.db` has a validated copied read-only open path through the bundled 32-bit `System.Data.SQLite.dll` and password `123`. C27 adds `MsgLibReadSidecar` as the bounded x86 .NET reader boundary, C28 adds the Go `internal/msglib` process client, C29 adds bounded `display_entities` extraction, C30 wires it as optional contact/group MCP enrichment, C31 proves real copied-DB MCP enrichment with sanitized output, C32 reuses it for receive-message display fields, C33 proves that receive display path through a real copied-DB MCP smoke without printing entity values, C34 documents the operator setup/verification path, C35 maps MsgLib message tables to receive-message contract fields without reading row values, C36 adds metadata-only `message_sources` readiness, C37 defines the bounded DB-backed receive-source design, C38 implements sidecar/Go-wrapper `list_messages` with sanitized verification, C39 adds a Go adapter from MsgLib list results to the receive-message domain model, C40 adds explicit tool-level `msglib_readonly` source selection, C41 wires env-configured explicit MsgLib receive with optional sanitized smoke, R1 documents source reconciliation keys, and R2 hardens contact/group search ranking and de-duplication. R6j/R6k confirm the send-message strict-v2 production gate is still not passed; R10a confirms the returned sent-record packages are manual-only evidence and do not unlock production send; R10/R11 define file-download resolver/preview behavior while keeping real copy blocked; R12 adds receive-source reconciliation without changing default routing; R13/R14 publish business-goals smoke and the release-candidate status. A-route now proves UI write/click readiness against synthetic and real offline chat windows, but not online delivery. The active follow-up plan is `docs/superpowers/plans/2026-07-11-a-route-rpa-send.md`; next work is evidence-driven send/file verification, not local login-dependent. +`MsgLib.db` has a validated copied read-only open path through the bundled 32-bit `System.Data.SQLite.dll` and password `123`. C27 adds `MsgLibReadSidecar` as the bounded x86 .NET reader boundary, C28 adds the Go `internal/msglib` process client, C29 adds bounded `display_entities` extraction, C30 wires it as optional contact/group MCP enrichment, C31 proves real copied-DB MCP enrichment with sanitized output, C32 reuses it for receive-message display fields, C33 proves that receive display path through a real copied-DB MCP smoke without printing entity values, C34 documents the operator setup/verification path, C35 maps MsgLib message tables to receive-message contract fields without reading row values, C36 adds metadata-only `message_sources` readiness, C37 defines the bounded DB-backed receive-source design, C38 implements sidecar/Go-wrapper `list_messages` with sanitized verification, C39 adds a Go adapter from MsgLib list results to the receive-message domain model, C40 adds explicit tool-level `msglib_readonly` source selection, C41 wires env-configured explicit MsgLib receive with optional sanitized smoke, R1 documents source reconciliation keys, and R2 hardens contact/group search ranking and de-duplication. R6j/R6k confirm the send-message strict-v2 production gate is still not passed; R10a confirms the returned sent-record packages are manual-only evidence and do not unlock production send; R10/R11 define file-download resolver/preview behavior while keeping real copy blocked; R12 adds receive-source reconciliation without changing default routing; R13/R14 publish business-goals smoke and the release-candidate status. A-route now proves UI write/click readiness against synthetic and real offline chat windows, but not online delivery. The active B-route follow-up is now the offline decision path in `docs/source-discovery/2026-07-12-offline-broute-reverse-decision.md`: create a dedicated `in_process_adapter_research` plan before production send/file work. A-route RPA remains backup-only under `docs/superpowers/plans/2026-07-11-a-route-rpa-send.md`. diff --git a/docs/superpowers/plans/2026-07-12-b-route-bridge-next-plan.md b/docs/superpowers/plans/2026-07-12-b-route-bridge-next-plan.md index c581526..34395ff 100644 --- a/docs/superpowers/plans/2026-07-12-b-route-bridge-next-plan.md +++ b/docs/superpowers/plans/2026-07-12-b-route-bridge-next-plan.md @@ -2,9 +2,9 @@ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. -**Goal:** Push the primary B-route from a dry-run shell toward a verifiable sidecar/API bridge without enabling real send yet. +**Goal:** Push the primary B-route from offline reverse evidence into the selected `in_process_adapter_research` branch, while keeping any external sidecar work preview/probe-only and not enabling real send. -**Architecture:** Keep the Go MCP tool contracts stable. B-route remains the primary product route: reuse the logged-in `IMPlatformClient.exe` runtime through a narrow sidecar/API bridge if a safe bridge transport exists. A-route/RPA remains backup-only and should be used only for UI action smoke or when B-route is proven blocked. +**Architecture:** Keep the Go MCP tool contracts stable. B-route remains the primary product route: reuse the logged-in `IMPlatformClient.exe` runtime through a narrow bridge. Offline reverse now rules out existing IPC/plugin as the next first step, so a dedicated in-process adapter research plan is required before any production send. External sidecars may only do self-check/probe/preview. A-route/RPA remains backup-only and should be used only for UI action smoke or when B-route is proven blocked. **Tech Stack:** Go 1.23.4, github.com/modelcontextprotocol/go-sdk, Windows PowerShell, .NET Framework C# helper/sidecar, stdin/stdout JSON protocols, Windows UI Automation only as fallback. @@ -21,9 +21,17 @@ --- -## Offline reverse amendment +## Offline reverse result amendment -Because the next working day has no logged-in iSphere environment, execute `docs/superpowers/plans/2026-07-12-offline-broute-reverse-plan.md` before the online evidence/package tasks. This keeps B-route primary while using offline static reverse evidence to decide IPC/plugin/in-process/preview-only feasibility. A-route/RPA remains backup-only. +`docs/superpowers/plans/2026-07-12-offline-broute-reverse-plan.md` has now produced the offline reverse decision set: + +- `docs/source-discovery/2026-07-12-offline-broute-reverse-index.md` +- `docs/source-discovery/2026-07-12-broute-text-send-static-map.md` +- `docs/source-discovery/2026-07-12-broute-file-send-static-map.md` +- `docs/source-discovery/2026-07-12-broute-bridge-static-map.md` +- `docs/source-discovery/2026-07-12-offline-broute-reverse-decision.md` + +Decision correction: B-route remains primary, but **existing IPC/local service** and **plugin extension** are not confirmed usable. The selected next branch is `in_process_adapter_research`. External sidecar work is allowed only as preview/probe plumbing until an in-process bridge is selected. A-route/RPA remains backup-only. ## Current evidence summary @@ -36,7 +44,7 @@ The current `main` state already has: - Offline real `frmP2PChat` opener for RPA backup validation. - No B-route env wiring, no process-backed B-route sidecar client, no in-process bridge transport decision, and no real send/file-upload connector. -Important technical conclusion: an external C# process cannot automatically reuse the logged-in static runtime of `IMPlatformClient.exe`. Before building send invocation, the next B-route node must decide the bridge transport: existing API/IPC, plugin/extension loading, in-process adapter, or fallback. +Important technical conclusion: an external C# process cannot automatically reuse the logged-in static runtime of `IMPlatformClient.exe`. Offline reverse has now made the transport decision for this evidence set: existing API/IPC and plugin extension are not confirmed; the next B-route node is a separate `in_process_adapter_research` plan. Do not build production send/file upload in this plan. --- @@ -72,11 +80,11 @@ B-route remains primary. The immediate next implementation is not production sen | Candidate | Can reuse logged-in runtime? | Local feasibility | Next action | | --- | --- | --- | --- | -| existing_ipc/local_service | unknown | inspect live probe named pipes/services/network metadata | continue if concrete API appears | -| plugin_extension | unknown | inspect install/plugin loader evidence | continue if client loads local plugin DLL/exe | -| in_process_adapter | possible but highest risk | requires explicit bridge mechanism | defer until IPC/plugin ruled out | -| external_dry_run_only | cannot reuse logged-in static runtime | useful for protocol/client tests only | implement as non-mutating sidecar boundary | -| fallback_rpa | can drive UI only | already backup-ready | keep as backup, not primary | +| existing_ipc/local_service | not confirmed | offline bridge map found no send/file API | park unless new online evidence appears | +| plugin_extension | not confirmed | plugin evidence is UI/app-store/context-menu oriented | park unless a concrete local load contract appears | +| in_process_adapter | possible but requires explicit bridge mechanism | selected by offline decision | create dedicated research plan before coding | +| external_dry_run_only | cannot reuse logged-in static runtime | useful for protocol/client tests only | keep preview/probe-only boundary | +| fallback_rpa | can drive UI only | backup-ready for UI action smoke | keep as backup, not primary | ``` - [ ] Update `capability-source-matrix.md` so the next slice says: “B-route bridge transport feasibility and dry-run sidecar boundary”, not “RPA send”. @@ -91,6 +99,10 @@ git diff --check Expected: exit 0. +### 2026-07-12 plan correction + +Task 1 is now satisfied by the offline reverse reports listed above. Before executing Tasks 2-5, create or approve a new `in_process_adapter_research` plan. Tasks 2-4 may still be used later for non-mutating sidecar preview plumbing, but they do not solve production send by themselves. + ## Task 2: Add B-route connector env selection without starting real send **Files:** diff --git a/scripts/build-offline-broute-reverse-index.ps1 b/scripts/build-offline-broute-reverse-index.ps1 new file mode 100644 index 0000000..864241a --- /dev/null +++ b/scripts/build-offline-broute-reverse-index.ps1 @@ -0,0 +1,168 @@ +param( + [string]$OutputDir = "runs/offline-broute-reverse", + [string]$ReportPath = "docs/source-discovery/2026-07-12-offline-broute-reverse-index.md" +) + +$ErrorActionPreference = "Stop" + +$repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path +$outDir = Join-Path $repo $OutputDir +$reportFullPath = Join-Path $repo $ReportPath +New-Item -ItemType Directory -Force -Path $outDir | Out-Null +New-Item -ItemType Directory -Force -Path (Split-Path -Parent $reportFullPath) | Out-Null + +$samples = @( + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/IMPlatformClient.exe" + role = "main managed client; text send, chat window, plugin/IPC candidates" + expected_sha256 = "E2966E58360DAEEBF178410A961E2DC52748C63E9CCBD94BD6EC8434A1A09CB7" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/IMPP.Service.dll" + role = "file upload service candidates" + expected_sha256 = "F859DE2F34E024B5F372D95F22CBF8A018C780F24203E7A76D392E52BD90CADD" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/IMPP.Interface.dll" + role = "service/interface contract candidates" + expected_sha256 = "DD093CF61FAE85C2D2581F289B01AA477546712E098FEF81272FDD989E50D12D" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/IMPP.Model.dll" + role = "model/parameter types" + expected_sha256 = "7CBABDD23A7234D15E294C549EEEC78EFBAD6E957CD45A6E8A85309349A624A5" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/IMPP.UI.dll" + role = "UI/plugin helpers and possible send controls" + expected_sha256 = "4C61212519C01D1B731ED4F20DF3411C491616F4ADA98A4529722A6AA3689E6D" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/IMPP.Common.dll" + role = "shared utility and message types" + expected_sha256 = "02A47C99C27C9BFE2BADC438389300C92220B07609BD87C48B4B5E1B4EE6AEB4" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/IMPP.Helper.dll" + role = "helper abstractions" + expected_sha256 = "30932D082B6678699C832C63F923E01EAD84674FAEDB613D631E034E03742FB9" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/smack.dll" + role = "XMPP chat/send layer" + expected_sha256 = "BAD0BB0591765DB153B17CECC1112B0BBDBF3B1D3370DDF973F91974E92CEC66" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/TcpFileTransfer.dll" + role = "native/transfer candidate" + expected_sha256 = "6C4874B46D1E7DC04C9B6784180603452400B881E04509D464A0D58814EFEDA7" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/HttpServerLib.dll" + role = "local API/IPC candidate" + expected_sha256 = "FFDB633216AE332B8EDFFC83E2AE546B7A80144C659FF4053FDFE4FE382A8D12" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/INetwork.dll" + role = "network abstraction candidate" + expected_sha256 = "E40BE4EE2439E4280461F4D2BADB318A5F4572428F069AB6D17B4C4F0E8CFAC9" + }, + @{ + path = "runs/offline-real-client-window/full/zyl/Impp/IOClientNetwork.dll" + role = "network implementation candidate" + expected_sha256 = "64CA231A614771325741A5F098E52EBF20F7190E339B64962DE2EDEDCFFFF904" + } +) + +$entries = foreach ($sample in $samples) { + $fullPath = Join-Path $repo $sample.path + $exists = Test-Path -LiteralPath $fullPath -PathType Leaf + $item = $null + $sha256 = "" + $matches = $false + if ($exists) { + $item = Get-Item -LiteralPath $fullPath + $sha256 = (Get-FileHash -LiteralPath $fullPath -Algorithm SHA256).Hash.ToUpperInvariant() + $matches = ($sha256 -eq $sample.expected_sha256) + } + [pscustomobject]@{ + path = $sample.path + file_name = Split-Path -Leaf $sample.path + role = $sample.role + exists = $exists + size_bytes = if ($item) { $item.Length } else { $null } + last_write_time = if ($item) { $item.LastWriteTime.ToString("o") } else { $null } + sha256 = $sha256 + expected_sha256 = $sample.expected_sha256 + sha256_matches_expected = $matches + } +} + +$summary = [pscustomobject]@{ + generated_at = (Get-Date).ToUniversalTime().ToString("o") + repository = $repo + output_dir = $OutputDir + samples_total = @($entries).Count + samples_present = @($entries | Where-Object { $_.exists }).Count + hash_matches = @($entries | Where-Object { $_.sha256_matches_expected }).Count + originals_copied = $false + originals_modified = $false + entries = $entries +} + +$jsonPath = Join-Path $outDir "index.json" +$summary | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $jsonPath -Encoding UTF8 + +$lines = New-Object System.Collections.Generic.List[string] +$lines.Add("# Offline B-Route Reverse Index") +$lines.Add("") +$lines.Add("Date: 2026-07-12") +$lines.Add("") +$lines.Add("## Scope") +$lines.Add("") +$lines.Add("This index records the read-only offline samples for B-route reverse engineering. The script reads metadata and SHA256 hashes only; it does not copy or modify the original binaries.") +$lines.Add("") +$lines.Add("## Summary") +$lines.Add("") +$lines.Add("- Samples total: $($summary.samples_total)") +$lines.Add("- Samples present: $($summary.samples_present)") +$lines.Add("- Hash matches: $($summary.hash_matches)") +$lines.Add("- Originals copied: false") +$lines.Add("- Originals modified: false") +$lines.Add("- Machine JSON: ``$OutputDir/index.json``") +$lines.Add("") +$lines.Add("## Sample table") +$lines.Add("") +$lines.Add("| Sample | Role | Size | SHA256 match | SHA256 |") +$lines.Add("| --- | --- | ---: | --- | --- |") +foreach ($entry in $entries) { + $size = if ($null -ne $entry.size_bytes) { [string]$entry.size_bytes } else { "missing" } + $match = if ($entry.sha256_matches_expected) { "yes" } else { "no" } + $sha = if ($entry.sha256) { $entry.sha256 } else { "missing" } + $lines.Add("| ``$($entry.path)`` | $($entry.role) | $size | $match | ``$sha`` |") +} +$lines.Add("") +$lines.Add("## Verified facts") +$lines.Add("") +$lines.Add("- All metadata came from local filesystem reads under the repository workspace.") +$lines.Add("- The expected primary B-route samples are present if `Samples present` equals `Samples total`.") +$lines.Add("- Hash matching only proves local file identity against this plan's recorded sample list; it does not prove runtime invocability.") +$lines.Add("") +$lines.Add("## Next reverse targets") +$lines.Add("") +$lines.Add("1. Text-send static call graph: `SendTxtMessageByJid`, `MessageScheduling.SendChatMessage`, `Chat.SendMessage`, `XMPPConnection.send`.") +$lines.Add("2. File-send/upload static call graph: `OffLineFileSend.sendFile`, `FileTransfer.FileUpload`, `AsynFileUpload`, `sendFileMessage`.") +$lines.Add("3. Bridge/IPC/plugin map: `HttpServerLib`, plugin loaders, local HTTP, named pipes, command-line switches, services.") + +($lines -join "`n") + "`n" | Set-Content -LiteralPath $reportFullPath -Encoding UTF8 + +[pscustomobject]@{ + ok = $true + json_path = $jsonPath + report_path = $reportFullPath + samples_total = $summary.samples_total + samples_present = $summary.samples_present + hash_matches = $summary.hash_matches + originals_copied = $false + originals_modified = $false +} | ConvertTo-Json -Depth 4 diff --git a/scripts/extract-broute-bridge-map.ps1 b/scripts/extract-broute-bridge-map.ps1 new file mode 100644 index 0000000..07cd9de --- /dev/null +++ b/scripts/extract-broute-bridge-map.ps1 @@ -0,0 +1,537 @@ +param( + [string]$IlDir = "runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il", + [string]$BinaryDir = "runs/offline-real-client-window/full/zyl/Impp", + [string]$OutputDir = "runs/offline-broute-reverse", + [string]$ReportPath = "docs/source-discovery/2026-07-12-broute-bridge-static-map.md", + [int]$WindowRadius = 4, + [int]$MaxEvidencePerCandidate = 18 +) + +$ErrorActionPreference = "Stop" + +$repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path +$ilDirFull = Join-Path $repo $IlDir +$binaryDirFull = Join-Path $repo $BinaryDir +$outDir = Join-Path $repo $OutputDir +$reportFullPath = Join-Path $repo $ReportPath +New-Item -ItemType Directory -Force -Path $outDir | Out-Null +New-Item -ItemType Directory -Force -Path (Split-Path -Parent $reportFullPath) | Out-Null + +$textSources = @( + @{ path = "IMPlatformClient.exe.il"; role = "main client IL; login, plugin manager, local HTTP starter, chat/runtime" }, + @{ path = "IMPP.Common.dll.il"; role = "common helpers; WinCommand, ProcessComm, DTOs" }, + @{ path = "IMPP.ISphere.exe.il"; role = "ISphere companion app IL; CEF and command helpers" }, + @{ path = "IMPP.UI.dll.il"; role = "UI/browser support IL" }, + @{ path = "IMPP.Service.dll.il"; role = "file service implementation IL" }, + @{ path = "smack.dll.il"; role = "XMPP/socks5/network IL" } +) + +$binarySources = @( + @{ path = "HttpServerLib.dll"; role = "local HTTP server framework binary" }, + @{ path = "IMPlatformClient.Web.exe"; role = "CEF/web helper process binary" }, + @{ path = "INetwork.dll"; role = "native network abstraction" }, + @{ path = "IOClientNetwork.dll"; role = "native network implementation" }, + @{ path = "IMPlatformClient.exe"; role = "main client binary" } +) + +$liveProbeSources = @( + "runs/returned-live-probe/isphere-live-probe-20260710-121958/isphere-live-probe-20260710-121958/services_registry_shortcuts.json", + "runs/returned-live-probe/isphere-live-probe-20260710-121958/isphere-live-probe-20260710-121958/network_inventory.json" +) + +$candidates = @( + @{ + key = "local_http_win_server" + display = "frmLogin.StartWinServer / HttpServerLib.HttpService" + category = "localhost_http" + classification = "requires_logged_in_process" + usable_for_broute_send = $false + recommendation = "do_not_pursue_before_in_process_adapter" + reason = "Static IL builds a local HTTP service from logged-in user/auth state, but no send route or active live local listener was confirmed." + patterns = @("StartWinServer", "HttpServerLib", "HttpService::\.ctor", "HttpServer::Start", "GetFreePort", "0\.0\.0\.0", "messageAuth", "RepairDatabase") + }, + @{ + key = "http_route_framework" + display = "HttpServerLib route/module framework" + category = "localhost_http" + classification = "static_only_unknown" + usable_for_broute_send = $false + recommendation = "no_existing_ipc_first" + reason = "HttpServerLib has ServiceModule/RouteAttribute primitives, but client IL does not show RegisterModule or send-message routes." + patterns = @("RouteAttribute", "RouteMethod", "ServiceModule", "ServiceRoute", "RegisterModule", "OnGet\s*\(", "OnPost\s*\(", "ExecuteRoute", "SearchRoute") + }, + @{ + key = "plugin_named_pipe_stub" + display = "PluginLightAppMulProcessCommunication NamedPipeServerStream" + category = "named_pipe" + classification = "not_a_bridge" + usable_for_broute_send = $false + recommendation = "do_not_use" + reason = "A NamedPipeServerStream field exists, but StartLightAppMultProssCommunication has a one-instruction ret body." + patterns = @("NamedPipeServerStream", "PluginLightAppMulProcessCommunication", "StartLightAppMultProssCommunication", "MulProcess", "LightApp") + }, + @{ + key = "process_comm_wm_copydata" + display = "IMPP.Common.Helper.ProcessComm WM_COPYDATA helper" + category = "window_message" + classification = "static_only_unknown" + usable_for_broute_send = $false + recommendation = "not_a_send_bridge" + reason = "ProcessComm can send WM_COPYDATA to a foreground or titled window, but no receive handler or send-command contract is confirmed." + patterns = @("IMPP\.Common\.Helper\.ProcessComm", "\bProcessComm\b", "COPYDATASTRUCT", "WM_COPYDATA", "SendMessageToTargetWindow", "FindWindow\(string lpClassName", "GetForegroundWindow\(\)") + }, + @{ + key = "plugin_manager_command_surface" + display = "PluginManager / IMPlugin command surface" + category = "plugin" + classification = "requires_logged_in_process" + usable_for_broute_send = $false + recommendation = "plugin_extension_not_first" + reason = "Plugin evidence is UI/app-store/context-menu oriented and depends on logged-in PluginManager state; no Assembly.Load/LoadFrom or send API bridge was confirmed." + patterns = @("PluginManager", "IMPlugin::get_Command", "LoadPluginContextMenu", "FrmAppStore", "FrmCefHomePage", "ShowPlugins", "Assembly\.Load", "LoadFrom", "AddPluginInfo") + }, + @{ + key = "cef_web_helper" + display = "IMPlatformClient.Web / CEF helper" + category = "child_process" + classification = "requires_logged_in_process" + usable_for_broute_send = $false + recommendation = "preview_only_not_send" + reason = "The web helper/CEF surface exists, but static evidence points to UI/plugin browser support, not an external send/file API." + patterns = @("IMPlatformClient\.Web", "CefProcessName", "CefRuntime", "WinCefBrowser", "NavigateTo", "SendProcessMessage", "V8Context") + }, + @{ + key = "native_network_stack" + display = "INetwork / IOClientNetwork native stack" + category = "native_network" + classification = "not_a_bridge" + usable_for_broute_send = $false + recommendation = "do_not_pursue_as_ipc" + reason = "Native NETIO send/recv symbols are low-level transport, not a local command bridge into logged-in chat objects." + patterns = @("INetwork", "IOClientNetwork", "NETIO_", "NETIO_C_", "StreamClient", "RegistRecvCallback", "SendPacket", "BasePipe") + }, + @{ + key = "live_probe_named_pipe" + display = "returned live-probe named pipe inventory" + category = "named_pipe" + classification = "static_only_unknown" + usable_for_broute_send = $false + recommendation = "do_not_assume_isphere_api" + reason = "Live probe saw \\.\\pipe\\zfpinject-msg-server, but no matching iSphere static string or send contract was found in the configured samples." + patterns = @("zfpinject-msg-server", "named_pipes", "connections", "remote_port", "10088") + }, + @{ + key = "local_tcp_listener_candidates" + display = "local TcpListener candidates" + category = "tcp_listener" + classification = "not_a_bridge" + usable_for_broute_send = $false + recommendation = "not_existing_ipc" + reason = "TcpListener evidence is tied to TCP file manage or smack socks5, not a command API for text/file send. Returned live probe had only one remote XMPP connection." + patterns = @("TcpListener", "TCPFileManager::_listener", "TCPSocks5Server", "GetActiveTcpListeners", "BeginAcceptSocket", "remote_port") + } +) + +function Get-RelativePath([string]$Path) { + if ($Path.StartsWith($repo, [System.StringComparison]::OrdinalIgnoreCase)) { + return $Path.Substring($repo.Length).TrimStart('\', '/') + } + return $Path +} + +function Get-TextEvidenceKind([string]$Line) { + if ($Line -match "^\s*\.class\b") { return "type_definition" } + if ($Line -match "^\s*\.method\b") { return "method_header" } + if ($Line -match "\bend of method\b") { return "method_end" } + if ($Line -match "\bcall(?:virt)?\b") { return "callsite" } + if ($Line -match "\bnewobj\b") { return "constructor_call" } + if ($Line -match "\bldstr\b") { return "string_literal" } + if ($Line -match "^\s*(instance|static|StartWinServer|GetFreePort|SendMessageToTargetWindow)\b.*\(") { return "signature_continuation" } + return "reference" +} + +function Get-MethodContext($Lines, [int]$Index) { + $start = [Math]::Max(0, $Index - 120) + $methodHeader = $null + $classHeader = $null + for ($i = $Index; $i -ge $start; $i--) { + $line = $Lines[$i] + if ($null -eq $methodHeader -and $line -match "^\s*\.method\b") { + $methodHeader = [pscustomobject]@{ line_number = $i + 1; text = $line.Trim() } + } + if ($null -eq $classHeader -and $line -match "^\s*\.class\b") { + $classHeader = [pscustomobject]@{ line_number = $i + 1; text = $line.Trim() } + } + if ($methodHeader -and $classHeader) { break } + } + [pscustomobject]@{ method_header = $methodHeader; class_header = $classHeader } +} + +function New-TextEvidence($Lines, [int]$Index, [string]$SourceRel) { + $from = [Math]::Max(0, $Index - $WindowRadius) + $to = [Math]::Min($Lines.Count - 1, $Index + $WindowRadius) + $window = New-Object System.Collections.Generic.List[object] + for ($i = $from; $i -le $to; $i++) { + $window.Add([pscustomobject]@{ line_number = $i + 1; text = $Lines[$i].TrimEnd() }) + } + $context = Get-MethodContext -Lines $Lines -Index $Index + [pscustomobject]@{ + source = $SourceRel + source_type = "text_il" + line_number = $Index + 1 + string_index = $null + kind = Get-TextEvidenceKind -Line $Lines[$Index] + text = $Lines[$Index].Trim() + method_header = $context.method_header + class_header = $context.class_header + window = $window + } +} + +function Get-PrintableStrings([string]$Path, [int]$MinLength = 4) { + $bytes = [System.IO.File]::ReadAllBytes($Path) + $strings = New-Object System.Collections.Generic.List[string] + + $sb = New-Object System.Text.StringBuilder + foreach ($b in $bytes) { + if ($b -ge 32 -and $b -le 126) { + [void]$sb.Append([char]$b) + } else { + if ($sb.Length -ge $MinLength) { $strings.Add($sb.ToString()) } + $sb.Clear() | Out-Null + } + } + if ($sb.Length -ge $MinLength) { $strings.Add($sb.ToString()) } + + $sb = New-Object System.Text.StringBuilder + for ($i = 0; $i -lt ($bytes.Length - 1); $i += 2) { + $b = $bytes[$i] + $z = $bytes[$i + 1] + if ($z -eq 0 -and $b -ge 32 -and $b -le 126) { + [void]$sb.Append([char]$b) + } else { + if ($sb.Length -ge $MinLength) { $strings.Add($sb.ToString()) } + $sb.Clear() | Out-Null + } + } + if ($sb.Length -ge $MinLength) { $strings.Add($sb.ToString()) } + return $strings.ToArray() +} + +function Get-HttpServerReflection([string]$Path) { + $result = [ordered]@{ path = Get-RelativePath -Path $Path; ok = $false; error = ""; types = @() } + try { + $asm = [System.Reflection.Assembly]::LoadFile($Path) + $types = New-Object System.Collections.Generic.List[object] + foreach ($type in $asm.GetTypes()) { + if ($type.FullName -notmatch "HTTPServerLib\.(HttpServer|HttpService|ServiceModule|ServiceRoute|RouteAttribute|IServer|ActionResult)") { continue } + $ctors = @($type.GetConstructors([System.Reflection.BindingFlags]'Public,NonPublic,Instance') | ForEach-Object { + $params = @($_.GetParameters() | ForEach-Object { $_.ParameterType.Name + " " + $_.Name }) + [pscustomobject]@{ is_public = $_.IsPublic; signature = "$($type.Name)($([string]::Join(', ', $params)))" } + }) + $methods = @($type.GetMethods([System.Reflection.BindingFlags]'Public,NonPublic,Instance,Static,DeclaredOnly') | ForEach-Object { + $params = @($_.GetParameters() | ForEach-Object { $_.ParameterType.Name + " " + $_.Name }) + [pscustomobject]@{ name = $_.Name; signature = "$($_.ReturnType.Name) $($_.Name)($([string]::Join(', ', $params)))" } + }) + $types.Add([pscustomobject]@{ full_name = $type.FullName; constructors = $ctors; methods = $methods }) + } + $result.ok = $true + $result.types = $types.ToArray() + } catch { + $result.error = $_.Exception.Message + } + return [pscustomobject]$result +} + +$textSourceResults = New-Object System.Collections.Generic.List[object] +$binarySourceResults = New-Object System.Collections.Generic.List[object] +$candidateBuckets = @{} +foreach ($candidate in $candidates) { + $candidateBuckets[[string]$candidate['key']] = New-Object System.Collections.Generic.List[object] +} + +foreach ($source in $textSources) { + $sourceFullPath = Join-Path $ilDirFull ([string]$source['path']) + $sourceRel = Get-RelativePath -Path $sourceFullPath + if (-not (Test-Path -LiteralPath $sourceFullPath -PathType Leaf)) { + $textSourceResults.Add([pscustomobject]@{ path = $sourceRel; role = $source['role']; exists = $false; line_count = 0; size_bytes = $null }) + continue + } + $item = Get-Item -LiteralPath $sourceFullPath + $lines = [System.IO.File]::ReadAllLines($sourceFullPath) + $textSourceResults.Add([pscustomobject]@{ path = $sourceRel; role = $source['role']; exists = $true; line_count = $lines.Count; size_bytes = $item.Length }) + foreach ($candidate in $candidates) { + $key = [string]$candidate['key'] + $compiled = @($candidate['patterns'] | ForEach-Object { [regex]::new($_, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) }) + for ($i = 0; $i -lt $lines.Count; $i++) { + $line = $lines[$i] + foreach ($rx in $compiled) { + if ($rx.IsMatch($line)) { + $candidateBuckets[$key].Add((New-TextEvidence -Lines $lines -Index $i -SourceRel $sourceRel)) + break + } + } + } + } +} + +foreach ($source in $binarySources) { + $sourceFullPath = Join-Path $binaryDirFull ([string]$source['path']) + $sourceRel = Get-RelativePath -Path $sourceFullPath + if (-not (Test-Path -LiteralPath $sourceFullPath -PathType Leaf)) { + $binarySourceResults.Add([pscustomobject]@{ path = $sourceRel; role = $source['role']; exists = $false; string_count = 0; size_bytes = $null }) + continue + } + $item = Get-Item -LiteralPath $sourceFullPath + $strings = Get-PrintableStrings -Path $sourceFullPath -MinLength 4 + $binarySourceResults.Add([pscustomobject]@{ path = $sourceRel; role = $source['role']; exists = $true; string_count = $strings.Count; size_bytes = $item.Length }) + foreach ($candidate in $candidates) { + $key = [string]$candidate['key'] + $compiled = @($candidate['patterns'] | ForEach-Object { [regex]::new($_, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) }) + for ($i = 0; $i -lt $strings.Count; $i++) { + $s = $strings[$i] + foreach ($rx in $compiled) { + if ($rx.IsMatch($s)) { + $candidateBuckets[$key].Add([pscustomobject]@{ + source = $sourceRel + source_type = "binary_string" + line_number = $null + string_index = $i + kind = "binary_string" + text = $s + method_header = $null + class_header = $null + window = @() + }) + break + } + } + } + } +} + +$liveProbe = New-Object System.Collections.Generic.List[object] +foreach ($relPath in $liveProbeSources) { + $fullPath = Join-Path $repo $relPath + if (Test-Path -LiteralPath $fullPath -PathType Leaf) { + $raw = Get-Content -LiteralPath $fullPath -Raw + $liveProbe.Add([pscustomobject]@{ path = $relPath; exists = $true; length = $raw.Length; data = ($raw | ConvertFrom-Json) }) + foreach ($candidate in $candidates) { + $key = [string]$candidate['key'] + $compiled = @($candidate['patterns'] | ForEach-Object { [regex]::new($_, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) }) + foreach ($rx in $compiled) { + if ($rx.IsMatch($raw)) { + $candidateBuckets[$key].Add([pscustomobject]@{ + source = $relPath + source_type = "live_probe_json" + line_number = $null + string_index = $null + kind = "live_probe_json" + text = ($raw -replace "\s+", " ").Trim() + method_header = $null + class_header = $null + window = @() + }) + break + } + } + } + } else { + $liveProbe.Add([pscustomobject]@{ path = $relPath; exists = $false; length = 0; data = $null }) + } +} + +$httpServerPath = Join-Path $binaryDirFull "HttpServerLib.dll" +$httpReflection = if (Test-Path -LiteralPath $httpServerPath -PathType Leaf) { Get-HttpServerReflection -Path $httpServerPath } else { [pscustomobject]@{ path = Get-RelativePath -Path $httpServerPath; ok = $false; error = "missing"; types = @() } } +$httpReflectionPath = Join-Path $outDir "httpserverlib-reflection.json" +$httpReflection | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $httpReflectionPath -Encoding UTF8 + +if ($httpReflection.ok) { + $reflectionText = ($httpReflection.types | ConvertTo-Json -Depth 8 -Compress) + foreach ($candidate in $candidates) { + $key = [string]$candidate['key'] + $compiled = @($candidate['patterns'] | ForEach-Object { [regex]::new($_, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) }) + foreach ($rx in $compiled) { + if ($rx.IsMatch($reflectionText)) { + $candidateBuckets[$key].Add([pscustomobject]@{ + source = Get-RelativePath -Path $httpReflectionPath + source_type = "reflection" + line_number = $null + string_index = $null + kind = "reflection_metadata" + text = "HttpServerLib reflection exposes matching types/methods; see httpserverlib-reflection.json" + method_header = $null + class_header = $null + window = @() + }) + break + } + } + } +} + +$candidateResults = New-Object System.Collections.Generic.List[object] +foreach ($candidate in $candidates) { + $key = [string]$candidate['key'] + $allEvidence = $candidateBuckets[$key].ToArray() + $evidenceForReport = @( + $allEvidence | + Sort-Object @{ Expression = { if ($_.source_type -eq "text_il") { 0 } elseif ($_.source_type -eq "reflection") { 1 } elseif ($_.source_type -eq "live_probe_json") { 2 } else { 3 } } }, source, line_number, string_index | + Select-Object -First $MaxEvidencePerCandidate + ) + $classification = if ($allEvidence.Count -eq 0) { "not_found" } else { [string]$candidate['classification'] } + $candidateResults.Add([pscustomobject]@{ + key = $key + display = $candidate['display'] + category = $candidate['category'] + classification = $classification + usable_for_broute_send = [bool]$candidate['usable_for_broute_send'] + recommendation = $candidate['recommendation'] + reason = $candidate['reason'] + match_count = $allEvidence.Count + evidence = $evidenceForReport + }) +} + +$recommendation = [pscustomobject]@{ + pursue_existing_ipc_or_plugin_first = $false + decision = "in_process_adapter_research" + fallback_boundary = "external_sidecar_preview_only_until_bridge_exists" + rationale = "Offline static evidence found no existing local IPC/plugin route that exposes send-message or send-file. HttpServerLib and plugin surfaces either require logged-in runtime state or are UI/framework-only; the named-pipe stub is empty." +} + +$summary = [pscustomobject]@{ + generated_at = (Get-Date).ToUniversalTime().ToString("o") + repository = $repo + il_dir = $IlDir + binary_dir = $BinaryDir + text_sources = $textSourceResults + binary_sources = $binarySourceResults + live_probe = $liveProbe + httpserverlib_reflection = Get-RelativePath -Path $httpReflectionPath + candidates = $candidateResults + recommendation = $recommendation + originals_copied = $false + originals_modified = $false +} + +$jsonPath = Join-Path $outDir "bridge-map.json" +$summary | ConvertTo-Json -Depth 14 | Set-Content -LiteralPath $jsonPath -Encoding UTF8 + +$linesOut = New-Object System.Collections.Generic.List[string] +$linesOut.Add("# B-Route Bridge/IPC/Plugin Static Map") +$linesOut.Add("") +$linesOut.Add("Date: 2026-07-12") +$linesOut.Add("") +$linesOut.Add("## Scope") +$linesOut.Add("") +$linesOut.Add("This report uses offline IL, binary string metadata, reflection metadata for ``HttpServerLib.dll``, and returned live-probe JSON. It does not log in, open sockets, attach hooks, inject into a process, replay traffic, or mutate client binaries.") +$linesOut.Add("") +$linesOut.Add("Machine-readable evidence: ``$OutputDir/bridge-map.json``") +$linesOut.Add("Reflection metadata: ``$OutputDir/httpserverlib-reflection.json``") +$linesOut.Add("") +$linesOut.Add("## Recommendation") +$linesOut.Add("") +$linesOut.Add("| Question | Answer |") +$linesOut.Add("| --- | --- |") +$linesOut.Add("| Should B-route pursue existing IPC/plugin before an in-process adapter? | **No** |") +$linesOut.Add("| Offline decision | ``$($recommendation.decision)`` |") +$linesOut.Add("| External sidecar boundary | ``$($recommendation.fallback_boundary)`` |") +$linesOut.Add("| Reason | $($recommendation.rationale) |") +$linesOut.Add("") +$linesOut.Add("## Sources") +$linesOut.Add("") +$linesOut.Add("### Text/IL sources") +$linesOut.Add("") +$linesOut.Add("| Source | Role | Exists | Lines | Size bytes |") +$linesOut.Add("| --- | --- | --- | ---: | ---: |") +foreach ($sourceResult in $textSourceResults) { + $exists = if ($sourceResult.exists) { "yes" } else { "no" } + $sizeText = if ($null -ne $sourceResult.size_bytes) { [string]$sourceResult.size_bytes } else { "" } + $linesOut.Add("| ``$($sourceResult.path)`` | $($sourceResult.role) | $exists | $($sourceResult.line_count) | $sizeText |") +} +$linesOut.Add("") +$linesOut.Add("### Binary/string sources") +$linesOut.Add("") +$linesOut.Add("| Source | Role | Exists | Strings | Size bytes |") +$linesOut.Add("| --- | --- | --- | ---: | ---: |") +foreach ($sourceResult in $binarySourceResults) { + $exists = if ($sourceResult.exists) { "yes" } else { "no" } + $sizeText = if ($null -ne $sourceResult.size_bytes) { [string]$sourceResult.size_bytes } else { "" } + $linesOut.Add("| ``$($sourceResult.path)`` | $($sourceResult.role) | $exists | $($sourceResult.string_count) | $sizeText |") +} +$linesOut.Add("") +$linesOut.Add("## Candidate classification") +$linesOut.Add("") +$linesOut.Add("| Candidate | Category | Classification | Usable for B-route send | Recommendation | Matches |") +$linesOut.Add("| --- | --- | --- | --- | --- | ---: |") +foreach ($candidateResult in $candidateResults) { + $usable = if ($candidateResult.usable_for_broute_send) { "yes" } else { "no" } + $linesOut.Add("| ``$($candidateResult.display)`` | ``$($candidateResult.category)`` | ``$($candidateResult.classification)`` | $usable | ``$($candidateResult.recommendation)`` | $($candidateResult.match_count) |") +} +$linesOut.Add("") +$linesOut.Add("## Verified facts") +$linesOut.Add("") +$linesOut.Add("- ``frmLogin.StartWinServer`` constructs ``HTTPServerLib.HttpService`` on ``0.0.0.0`` and starts ``HttpServer.Start`` in a thread, but it depends on ``IMPPManager.UserInfo`` and ``ServiceManager.UnifiedAuthentication`` state.") +$linesOut.Add("- ``HttpServerLib.dll`` exposes route/module primitives such as ``HttpService.RegisterModule``, ``ServiceModule``, ``ServiceRoute``, ``RouteAttribute``, ``OnGet``, and ``OnPost``.") +$linesOut.Add("- Main client IL does not show a concrete ``RegisterModule`` call or send-message/send-file HTTP route registration.") +$linesOut.Add("- ``PluginLightAppMulProcessCommunication`` declares a ``NamedPipeServerStream`` field, but ``StartLightAppMultProssCommunication`` returns immediately.") +$linesOut.Add("- Returned live probe showed one remote TCP connection to port ``10088`` and a named pipe ``\\.\\pipe\\zfpinject-msg-server``; neither proves an iSphere send API.") +$linesOut.Add("- ``PluginManager``/``IMPlugin`` evidence is UI/app-store/context-menu oriented; no local assembly-load plugin bridge or send route was confirmed.") +$linesOut.Add("") +$linesOut.Add("## Candidate evidence") +foreach ($candidateResult in $candidateResults) { + $linesOut.Add("") + $linesOut.Add("### $($candidateResult.display)") + $linesOut.Add("") + $linesOut.Add("Classification: ``$($candidateResult.classification)``; usable for B-route send: ``$($candidateResult.usable_for_broute_send)``; matches: $($candidateResult.match_count).") + $linesOut.Add("") + $linesOut.Add("Reason: $($candidateResult.reason)") + if (@($candidateResult.evidence).Count -eq 0) { + $linesOut.Add("") + $linesOut.Add("No evidence found in configured offline sources.") + continue + } + $linesOut.Add("") + $linesOut.Add("| Source | Kind | Snippet | Context |") + $linesOut.Add("| --- | --- | --- | --- |") + foreach ($evidence in $candidateResult.evidence) { + $sourceLoc = if ($evidence.line_number) { "$($evidence.source):$($evidence.line_number)" } elseif ($null -ne $evidence.string_index) { "$($evidence.source)#str$($evidence.string_index)" } else { $evidence.source } + $method = if ($evidence.method_header) { "L$($evidence.method_header.line_number): $($evidence.method_header.text)" } else { "" } + $snippet = (($evidence.text -replace "\|", "\\|") -replace "`r?`n", " ") + if ($snippet.Length -gt 220) { $snippet = $snippet.Substring(0, 220) + "..." } + $method = ($method -replace "\|", "\\|") + $linesOut.Add("| ``$sourceLoc`` | ``$($evidence.kind)`` | ``$snippet`` | ``$method`` |") + } +} +$linesOut.Add("") +$linesOut.Add("## Decision") +$linesOut.Add("") +$linesOut.Add("No existing IPC/plugin/local HTTP bridge is confirmed usable for B-route text send or file send. The next B-route branch should be ``in_process_adapter_research``. Until that exists, an external sidecar can only do preview/probe work, while A-route/RPA remains backup-only.") +$linesOut.Add("") +$linesOut.Add("## Next work") +$linesOut.Add("") +$linesOut.Add("1. Update the B-route next plan and capability source matrix with ``in_process_adapter_research`` as the offline decision.") +$linesOut.Add("2. Create a separate plan before implementing any in-process adapter work.") +$linesOut.Add("3. Do not promote RPA beyond backup in this branch.") + +($linesOut -join "`n") + "`n" | Set-Content -LiteralPath $reportFullPath -Encoding UTF8 + +$requiredNames = @("HttpServerLib", "NamedPipe", "Plugin", "IMPlatformClient.Web", "ProcessComm", "INetwork", "IOClientNetwork", "in_process_adapter_research") +$reportText = Get-Content -LiteralPath $reportFullPath -Raw +$missing = @($requiredNames | Where-Object { $reportText -notlike "*$_*" }) +if ($missing.Count -gt 0) { + throw "Report missing required names: $($missing -join ', ')" +} + +[pscustomobject]@{ + ok = $true + json_path = $jsonPath + report_path = $reportFullPath + reflection_path = $httpReflectionPath + candidates = $candidateResults.Count + decision = $recommendation.decision + pursue_existing_ipc_or_plugin_first = $false + originals_copied = $false + originals_modified = $false +} | ConvertTo-Json -Depth 6 diff --git a/scripts/extract-broute-file-send-map.ps1 b/scripts/extract-broute-file-send-map.ps1 new file mode 100644 index 0000000..12373fb --- /dev/null +++ b/scripts/extract-broute-file-send-map.ps1 @@ -0,0 +1,342 @@ +param( + [string]$IlDir = "runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il", + [string]$OutputDir = "runs/offline-broute-reverse", + [string]$ReportPath = "docs/source-discovery/2026-07-12-broute-file-send-static-map.md", + [int]$WindowRadius = 5, + [int]$MaxEvidencePerTarget = 18 +) + +$ErrorActionPreference = "Stop" + +$repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path +$ilDirFull = Join-Path $repo $IlDir +$outDir = Join-Path $repo $OutputDir +$reportFullPath = Join-Path $repo $ReportPath +New-Item -ItemType Directory -Force -Path $outDir | Out-Null +New-Item -ItemType Directory -Force -Path (Split-Path -Parent $reportFullPath) | Out-Null + +$sources = @( + @{ path = "IMPlatformClient.exe.il"; role = "main client IL; UC_FileSend, OffLineFileSend, MessageScheduling" }, + @{ path = "IMPP.Service.dll.il"; role = "file transfer service implementation" }, + @{ path = "IMPP.ServiceBase.dll.il"; role = "file transfer contracts, parameters, and result DTOs" }, + @{ path = "IMPP.Interface.dll.il"; role = "transport/event interface contracts" }, + @{ path = "smack.dll.il"; role = "Chat.sendFileMessage and XMPP message construction" }, + @{ path = "TcpFileTransfer.dll.il"; role = "native TCP transfer candidate; IL may be unavailable" }, + @{ path = "IMPP.Model.dll.il"; role = "chat record models if extracted" } +) + +$targets = @( + @{ + key = "IMPP.Client.OffLineFileSend.sendFile" + display = "IMPP.Client.OffLineFileSend.sendFile" + phase = "upload" + intent = "offline file upload orchestration from UI file sender" + patterns = @("OffLineFileSend::sendFile", "^\s*sendFile\(\) cil managed") + }, + @{ + key = "IMPP.Client.OffLineFileSend.trans_UploadCompleted" + display = "IMPP.Client.OffLineFileSend.trans_UploadCompleted" + phase = "upload_to_message_boundary" + intent = "upload-completed handler that converts FileUploadResult.FileID into a chat file message" + patterns = @("OffLineFileSend::trans_UploadCompleted", "trans_UploadCompleted\(string fileId\)") + }, + @{ + key = "IMPP.Service.BLL.FileTransfer.FileUpload" + display = "IMPP.Service.BLL.FileTransfer.FileUpload" + phase = "upload" + intent = "synchronous service upload entry returning FileUploadResult" + patterns = @("FileTransfer::FileUpload", "FileUpload\(class \[IMPP\.ServiceBase\].*FileUploadPara") + }, + @{ + key = "IMPP.Service.BLL.FileTransfer.AsynFileUpload" + display = "IMPP.Service.BLL.FileTransfer.AsynFileUpload" + phase = "upload" + intent = "asynchronous upload queue entry" + patterns = @("FileTransfer::AsynFileUpload", "AsynFileUpload\(class \[IMPP\.ServiceBase\].*FileUploadPara") + }, + @{ + key = "FileUploadPara" + display = "FileUploadPara" + phase = "upload_parameters" + intent = "host/port/domain/local file path/cache/timeout/upload flags" + patterns = @("FileUploadPara", "FileUploadPara::\.ctor") + }, + @{ + key = "FileUploadResult.FileID" + display = "FileUploadResult.FileID" + phase = "upload_result" + intent = "server-side file id and URL returned by upload" + patterns = @("FileUploadResult::get_FileID", "FileUploadResult::set_FileID", "FileUploadResult", "get_FileID\(\) cil managed") + }, + @{ + key = "MessageScheduling.SendFileMessage" + display = "MessageScheduling.SendFileMessage" + phase = "message" + intent = "scheduled/file-message finalization path using RosterContactsArgs and FileTransferArgs" + patterns = @("MessageScheduling::SendFileMessage", "SendFileMessage\(class IMPP\.Client\.Business\.RosterContacts\.RosterContactsArgs") + }, + @{ + key = "Chat.sendFileMessage" + display = "Chat.sendFileMessage" + phase = "message" + intent = "smack API that sends the XMPP file message after upload metadata is known" + patterns = @("Chat::sendFileMessage", "P2PChat::sendFileMessage", "ChatRoom::sendFileMessage", "sendFileMessage\(string p_message") + }, + @{ + key = "Required values: path id size target jid" + display = "Required values: path id size target jid" + phase = "requirements" + intent = "static evidence for local file path, remote folder/file id, file size/name, target JID/chat, and server auth" + patterns = @("get_LocalFilePath", "get_RemoteFolder", "get__localFilePath", "get__fileSize", "FileInfo::get_Length", "FileSystemInfo::get_Name", "get_RosterJid", "GetChat\(", "HTTPFileServer", "UnifiedAuthentication", "Auth::get_token") + } +) + +function Get-RelativePath([string]$Path) { + if ($Path.StartsWith($repo, [System.StringComparison]::OrdinalIgnoreCase)) { + return $Path.Substring($repo.Length).TrimStart('\', '/') + } + return $Path +} + +function Get-EvidenceKind([string]$Line) { + if ($Line -match "^\s*\.class\b") { return "type_definition" } + if ($Line -match "^\s*\.method\b") { return "method_header" } + if ($Line -match "\bend of method\b") { return "method_end" } + if ($Line -match "\bcall(?:virt)?\b") { return "callsite" } + if ($Line -match "\bnewobj\b") { return "constructor_call" } + if ($Line -match "\bldstr\b") { return "string_literal" } + if ($Line -match "^\s*(instance|static|FileUpload|SendFileMessage|sendFileMessage|sendFile|trans_UploadCompleted)\b.*\(") { return "signature_continuation" } + return "reference" +} + +function Get-MethodContext($Lines, [int]$Index) { + $start = [Math]::Max(0, $Index - 120) + $methodHeader = $null + $classHeader = $null + for ($i = $Index; $i -ge $start; $i--) { + $line = $Lines[$i] + if ($null -eq $methodHeader -and $line -match "^\s*\.method\b") { + $methodHeader = [pscustomobject]@{ line_number = $i + 1; text = $line.Trim() } + } + if ($null -eq $classHeader -and $line -match "^\s*\.class\b") { + $classHeader = [pscustomobject]@{ line_number = $i + 1; text = $line.Trim() } + } + if ($methodHeader -and $classHeader) { break } + } + [pscustomobject]@{ method_header = $methodHeader; class_header = $classHeader } +} + +function New-Evidence($Lines, [int]$Index, [string]$SourceRel) { + $from = [Math]::Max(0, $Index - $WindowRadius) + $to = [Math]::Min($Lines.Count - 1, $Index + $WindowRadius) + $window = New-Object System.Collections.Generic.List[object] + for ($i = $from; $i -le $to; $i++) { + $window.Add([pscustomobject]@{ line_number = $i + 1; text = $Lines[$i].TrimEnd() }) + } + $context = Get-MethodContext -Lines $Lines -Index $Index + [pscustomobject]@{ + source = $SourceRel + line_number = $Index + 1 + kind = Get-EvidenceKind -Line $Lines[$Index] + text = $Lines[$Index].Trim() + method_header = $context.method_header + class_header = $context.class_header + window = $window + } +} + +function Get-Classification($Evidence) { + $kinds = @($Evidence | ForEach-Object { $_.kind }) + if ($kinds -contains "type_definition" -or $kinds -contains "method_header" -or $kinds -contains "signature_continuation" -or $kinds -contains "method_end") { return "confirmed_signature" } + if ($kinds -contains "callsite" -or $kinds -contains "constructor_call") { return "confirmed_callsite" } + if ($kinds.Count -gt 0 -and @($kinds | Where-Object { $_ -ne "string_literal" }).Count -eq 0) { return "string_only" } + if ($kinds.Count -gt 0) { return "confirmed_callsite" } + return "not_found" +} + +$sourceResults = New-Object System.Collections.Generic.List[object] +$targetBuckets = @{} +foreach ($target in $targets) { + $targetKey = [string]$target['key'] + $targetBuckets[$targetKey] = New-Object System.Collections.Generic.List[object] +} + +foreach ($source in $sources) { + $sourcePath = [string]$source['path'] + $sourceFullPath = Join-Path $ilDirFull $sourcePath + $sourceRel = Get-RelativePath -Path $sourceFullPath + if (-not (Test-Path -LiteralPath $sourceFullPath -PathType Leaf)) { + $sourceResults.Add([pscustomobject]@{ path = $sourceRel; role = $source['role']; exists = $false; line_count = 0; size_bytes = $null }) + continue + } + + $item = Get-Item -LiteralPath $sourceFullPath + $lines = [System.IO.File]::ReadAllLines($sourceFullPath) + $sourceResults.Add([pscustomobject]@{ path = $sourceRel; role = $source['role']; exists = $true; line_count = $lines.Count; size_bytes = $item.Length }) + + foreach ($target in $targets) { + $targetKey = [string]$target['key'] + $compiled = @($target['patterns'] | ForEach-Object { [regex]::new($_, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) }) + for ($i = 0; $i -lt $lines.Count; $i++) { + $line = $lines[$i] + foreach ($rx in $compiled) { + if ($rx.IsMatch($line)) { + $targetBuckets[$targetKey].Add((New-Evidence -Lines $lines -Index $i -SourceRel $sourceRel)) + break + } + } + } + } +} + +$targetResults = New-Object System.Collections.Generic.List[object] +foreach ($target in $targets) { + $targetKey = [string]$target['key'] + $allEvidence = $targetBuckets[$targetKey].ToArray() + $evidenceForReport = @( + $allEvidence | + Sort-Object @{ Expression = { if ($_.kind -in @("type_definition", "method_header", "signature_continuation", "method_end")) { 0 } elseif ($_.kind -in @("callsite", "constructor_call")) { 1 } elseif ($_.kind -eq "string_literal") { 2 } else { 3 } } }, source, line_number | + Select-Object -First $MaxEvidencePerTarget + ) + $targetResults.Add([pscustomobject]@{ + key = $targetKey + display = $target['display'] + phase = $target['phase'] + intent = $target['intent'] + classification = Get-Classification -Evidence $allEvidence + match_count = $allEvidence.Count + evidence = $evidenceForReport + }) +} + +$requirements = @( + [pscustomobject]@{ value = "local file path"; evidence = "OffLineFileSend constructor receives localFilePath; sendFile uses get__localFilePath; MessageScheduling.SendFileMessage uses FileTransferArgs.get_LocalFilePath."; status = "required_confirmed" }, + [pscustomobject]@{ value = "server file id / remote folder"; evidence = "Upload returns FileUploadResult.FileID; trans_UploadCompleted(fileId) passes it to Chat.sendFileMessage and Packet.set_id. MessageScheduling path uses FileTransferArgs.get_RemoteFolder."; status = "required_confirmed" }, + [pscustomobject]@{ value = "file name and file size"; evidence = "SendFileMessage builds OFFLINE_FILE_TRANSFER payload from FileInfo/FileSystemInfo name and length; OffLineFileSend keeps _fileSize."; status = "required_confirmed" }, + [pscustomobject]@{ value = "target JID / chat object"; evidence = "UC_FileSend and MessageScheduling both require a live smack Chat; scheduled path derives it from RosterContactsArgs.get_RosterJid + UtilsChat.GetChat."; status = "required_confirmed" }, + [pscustomobject]@{ value = "HTTP file server and auth"; evidence = "FileUploadPara construction uses Config.ServerInfo.HTTPFileServer IP/Port/domain; service upload builds /FS/auth/resource/upload URL with UnifiedAuthentication token/app key/client secret/time/user id."; status = "required_confirmed" }, + [pscustomobject]@{ value = "file hash"; evidence = "No file-transfer-path MD5/SHA/checksum requirement was confirmed in the configured IL. MD5 hits belong to image/RTF or auth digest areas, not the offline file-send path."; status = "not_confirmed_in_file_send_path" } +) + +$summary = [pscustomobject]@{ + generated_at = (Get-Date).ToUniversalTime().ToString("o") + repository = $repo + il_dir = $IlDir + sources = $sourceResults + targets = $targetResults + phase_decision = "Offline static evidence shows upload first, then chat file-message finalization. Direct Chat.sendFileMessage alone is not enough unless a valid uploaded file id/remote folder already exists." + requirements = $requirements + originals_copied = $false + originals_modified = $false +} + +$jsonPath = Join-Path $outDir "file-send-map.json" +$summary | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $jsonPath -Encoding UTF8 + +$linesOut = New-Object System.Collections.Generic.List[string] +$linesOut.Add("# B-Route File Send Static Map") +$linesOut.Add("") +$linesOut.Add("Date: 2026-07-12") +$linesOut.Add("") +$linesOut.Add("## Scope") +$linesOut.Add("") +$linesOut.Add("This report uses offline IL only. It does not upload files, send file messages, replay traffic, inject into a process, or mutate the original iSphere binaries.") +$linesOut.Add("") +$linesOut.Add("Machine-readable evidence: ``$OutputDir/file-send-map.json``") +$linesOut.Add("") +$linesOut.Add("## Sources") +$linesOut.Add("") +$linesOut.Add("| Source | Role | Exists | Lines | Size bytes |") +$linesOut.Add("| --- | --- | --- | ---: | ---: |") +foreach ($sourceResult in $sourceResults) { + $exists = if ($sourceResult.exists) { "yes" } else { "no" } + $sizeText = if ($null -ne $sourceResult.size_bytes) { [string]$sourceResult.size_bytes } else { "" } + $linesOut.Add("| ``$($sourceResult.path)`` | $($sourceResult.role) | $exists | $($sourceResult.line_count) | $sizeText |") +} +$linesOut.Add("") +$linesOut.Add("## Target classification") +$linesOut.Add("") +$linesOut.Add("| Target | Phase | Intent | Classification | Matches |") +$linesOut.Add("| --- | --- | --- | --- | ---: |") +foreach ($targetResult in $targetResults) { + $linesOut.Add("| ``$($targetResult.display)`` | ``$($targetResult.phase)`` | $($targetResult.intent) | ``$($targetResult.classification)`` | $($targetResult.match_count) |") +} +$linesOut.Add("") +$linesOut.Add("## Upload step vs chat-message step") +$linesOut.Add("") +$linesOut.Add("| Step | Static evidence | Business meaning |") +$linesOut.Add("| --- | --- | --- |") +$linesOut.Add("| 1. Upload | ``IMPP.Client.OffLineFileSend.sendFile`` constructs ``FileUploadPara`` and calls ``IFileTransfer.FileUpload``; ``IMPP.Service.BLL.FileTransfer.FileUpload`` delegates to ``SyncFileTransfer`` and private ``Upload``. | The local file must be uploaded to the HTTP file service first. |") +$linesOut.Add("| 2. Upload result | ``FileUploadResult.FileID`` is read; upload notification calls ``IMPP.Client.OffLineFileSend.trans_UploadCompleted(fileId)``. | The server-issued file id becomes the durable reference. |") +$linesOut.Add("| 3. Chat file message | ``trans_UploadCompleted`` and ``MessageScheduling.SendFileMessage`` build ``OFFLINE_FILE_TRANSFER;...`` payloads and call ``Chat.sendFileMessage``. | The chat send step publishes metadata/reference, not raw file bytes. |") +$linesOut.Add("") +$linesOut.Add("Conclusion: file send is upload-first, chat-message-second. A direct chat message is insufficient without a valid uploaded file id or remote folder reference.") +$linesOut.Add("") +$linesOut.Add("## Required values") +$linesOut.Add("") +$linesOut.Add("| Value | Status | Evidence |") +$linesOut.Add("| --- | --- | --- |") +foreach ($req in $requirements) { + $linesOut.Add("| $($req.value) | ``$($req.status)`` | $($req.evidence) |") +} +$linesOut.Add("") +$linesOut.Add("## Evidence excerpts") +foreach ($targetResult in $targetResults) { + $linesOut.Add("") + $linesOut.Add("### $($targetResult.display)") + $linesOut.Add("") + $linesOut.Add("Classification: ``$($targetResult.classification)``; phase: ``$($targetResult.phase)``; matches: $($targetResult.match_count).") + if (@($targetResult.evidence).Count -eq 0) { + $linesOut.Add("") + $linesOut.Add("No evidence found in configured offline IL sources.") + continue + } + $linesOut.Add("") + $linesOut.Add("| Source line | Kind | Snippet | Method context |") + $linesOut.Add("| --- | --- | --- | --- |") + foreach ($evidence in $targetResult.evidence) { + $method = if ($evidence.method_header) { "L$($evidence.method_header.line_number): $($evidence.method_header.text)" } else { "" } + $snippet = ($evidence.text -replace "\|", "\\|") + $method = ($method -replace "\|", "\\|") + $linesOut.Add("| ``$($evidence.source):$($evidence.line_number)`` | ``$($evidence.kind)`` | ``$snippet`` | ``$method`` |") + } +} +$linesOut.Add("") +$linesOut.Add("## Inference") +$linesOut.Add("") +$linesOut.Add("- High confidence: upload and file-message finalization are separate steps.") +$linesOut.Add("- High confidence: ``FileUploadPara`` needs HTTP file server host/port/domain plus local file path and upload options.") +$linesOut.Add("- High confidence: ``Chat.sendFileMessage`` produces the XMPP file-message envelope after file metadata is available.") +$linesOut.Add("- Medium confidence: B-route file send must either call the same logged-in service/runtime or reproduce the authenticated HTTP upload plus XMPP file-message sequence.") +$linesOut.Add("- Low confidence until bridge evidence: an external sidecar can obtain the same service/auth/chat objects without being inside the logged-in client process.") +$linesOut.Add("") +$linesOut.Add("## Decision") +$linesOut.Add("") +$linesOut.Add("Offline static evidence supports B-route research, but file sending needs more than one method call: upload first, then chat file-message finalization. The next blocker is reachability: whether an IPC/plugin/in-process bridge can access the logged-in ``IFileTransfer`` service and smack ``Chat`` object.") +$linesOut.Add("") +$linesOut.Add("## Next work") +$linesOut.Add("") +$linesOut.Add("1. Extract bridge/IPC/plugin candidates before coding any file-send adapter.") +$linesOut.Add("2. If a bridge exists, prototype non-mutating discovery first: service availability, chat object lookup, and upload endpoint metadata only.") +$linesOut.Add("3. Keep A-route/RPA backup-only.") + +($linesOut -join "`n") + "`n" | Set-Content -LiteralPath $reportFullPath -Encoding UTF8 + +$requiredNames = @("IMPP.Client.OffLineFileSend.sendFile", "IMPP.Client.OffLineFileSend.trans_UploadCompleted", "IMPP.Service.BLL.FileTransfer.FileUpload", "IMPP.Service.BLL.FileTransfer.AsynFileUpload", "FileUploadPara", "sendFileMessage", "Upload step vs chat-message step") +$reportText = Get-Content -LiteralPath $reportFullPath -Raw +$missing = @($requiredNames | Where-Object { $reportText -notlike "*$_*" }) +if ($missing.Count -gt 0) { + throw "Report missing required names: $($missing -join ', ')" +} + +[pscustomobject]@{ + ok = $true + json_path = $jsonPath + report_path = $reportFullPath + sources = $sourceResults.Count + targets = $targetResults.Count + classifications = @($targetResults | ForEach-Object { "$($_.display)=$($_.classification)" }) + phase_decision = $summary.phase_decision + originals_copied = $false + originals_modified = $false +} | ConvertTo-Json -Depth 6 diff --git a/scripts/extract-broute-text-send-map.ps1 b/scripts/extract-broute-text-send-map.ps1 new file mode 100644 index 0000000..921c6b9 --- /dev/null +++ b/scripts/extract-broute-text-send-map.ps1 @@ -0,0 +1,285 @@ +param( + [string]$IlDir = "runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il", + [string]$OutputDir = "runs/offline-broute-reverse", + [string]$ReportPath = "docs/source-discovery/2026-07-12-broute-text-send-static-map.md", + [int]$WindowRadius = 4, + [int]$MaxEvidencePerTarget = 18 +) + +$ErrorActionPreference = "Stop" + +$repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path +$ilDirFull = Join-Path $repo $IlDir +$outDir = Join-Path $repo $OutputDir +$reportFullPath = Join-Path $repo $ReportPath +New-Item -ItemType Directory -Force -Path $outDir | Out-Null +New-Item -ItemType Directory -Force -Path (Split-Path -Parent $reportFullPath) | Out-Null + +$sources = @( + @{ + path = "IMPlatformClient.exe.il" + role = "main managed client IL; chat UI, AppContextManager, scheduling call sites" + }, + @{ + path = "smack.dll.il" + role = "XMPP/smack managed IL; Chat and XMPPConnection implementation" + } +) + +$targets = @( + @{ + key = "AppContextManager.SendTxtMessageByJid" + display = "AppContextManager.SendTxtMessageByJid" + intent = "high-level text send entry exposed by AppContextManager" + patterns = @("AppContextManager::SendTxtMessageByJid", "\bSendTxtMessageByJid\b") + }, + @{ + key = "MessageScheduling.SendChatMessage" + display = "MessageScheduling.SendChatMessage" + intent = "message scheduling text/chat send helper" + patterns = @("MessageScheduling::SendChatMessage", "\bSendChatMessage\s*\(") + }, + @{ + key = "Chat.SendMessage" + display = "Chat.SendMessage" + intent = "smack Chat send API used by chat/UI code" + patterns = @("com\.vision\.smack\.Chat::SendMessage", "com\.vision\.smack\.Chat::sendMessage", "\bChat::SendMessage\b", "\bChat::sendMessage\b") + }, + @{ + key = "XMPPConnection.send" + display = "XMPPConnection.send" + intent = "lowest-level XMPP connection send primitive" + patterns = @("XMPPConnection::send\b", "\bXMPPConnection\.send\b") + }, + @{ + key = "MessageCenter send-related callbacks" + display = "MessageCenter send-related callbacks" + intent = "MessageCenter or IMPPManager message-center callbacks touching send/message flow" + patterns = @("GetMessageCenter\s*\(\)", "MessageCenter::.*Send", "MessageCenter.*Send", "class IMPP\.Client\.control\.MessageCenter", "IMPP\.Client\.control\.MessageCenter") + } +) + +function Get-RelativePath([string]$Path) { + if ($Path.StartsWith($repo, [System.StringComparison]::OrdinalIgnoreCase)) { + return $Path.Substring($repo.Length).TrimStart('\', '/') + } + return $Path +} + +function Get-EvidenceKind([string]$Line) { + if ($Line -match "^\s*\.class\b") { return "type_definition" } + if ($Line -match "^\s*\.method\b") { return "method_header" } + if ($Line -match "\bend of method\b") { return "method_end" } + if ($Line -match "\bcall(?:virt)?\b") { return "callsite" } + if ($Line -match "\bldstr\b") { return "string_literal" } + if ($Line -match "^\s*(instance|static)\b.*\(") { return "signature_continuation" } + return "reference" +} + +function Get-MethodContext($Lines, [int]$Index) { + $start = [Math]::Max(0, $Index - 90) + $methodHeader = $null + $classHeader = $null + for ($i = $Index; $i -ge $start; $i--) { + $line = $Lines[$i] + if ($null -eq $methodHeader -and $line -match "^\s*\.method\b") { + $methodHeader = [pscustomobject]@{ line_number = $i + 1; text = $line.Trim() } + } + if ($null -eq $classHeader -and $line -match "^\s*\.class\b") { + $classHeader = [pscustomobject]@{ line_number = $i + 1; text = $line.Trim() } + } + if ($methodHeader -and $classHeader) { break } + } + [pscustomobject]@{ method_header = $methodHeader; class_header = $classHeader } +} + +function New-Evidence($Lines, [int]$Index, [string]$SourceRel) { + $from = [Math]::Max(0, $Index - $WindowRadius) + $to = [Math]::Min($Lines.Count - 1, $Index + $WindowRadius) + $window = New-Object System.Collections.Generic.List[object] + for ($i = $from; $i -le $to; $i++) { + $window.Add([pscustomobject]@{ line_number = $i + 1; text = $Lines[$i].TrimEnd() }) + } + $context = Get-MethodContext -Lines $Lines -Index $Index + [pscustomobject]@{ + source = $SourceRel + line_number = $Index + 1 + kind = Get-EvidenceKind -Line $Lines[$Index] + text = $Lines[$Index].Trim() + method_header = $context.method_header + class_header = $context.class_header + window = $window + } +} + +function Get-Classification($Evidence) { + $kinds = @($Evidence | ForEach-Object { $_.kind }) + if ($kinds -contains "method_header" -or $kinds -contains "signature_continuation" -or $kinds -contains "method_end") { return "confirmed_signature" } + if ($kinds -contains "callsite") { return "confirmed_callsite" } + if ($kinds.Count -gt 0 -and @($kinds | Where-Object { $_ -ne "string_literal" }).Count -eq 0) { return "string_only" } + if ($kinds.Count -gt 0) { return "confirmed_callsite" } + return "not_found" +} + +$sourceResults = New-Object System.Collections.Generic.List[object] +$targetBuckets = @{} +foreach ($target in $targets) { + $targetKey = [string]$target['key'] + $targetBuckets[$targetKey] = New-Object System.Collections.Generic.List[object] +} + +foreach ($source in $sources) { + $sourcePath = [string]$source['path'] + $sourceFullPath = Join-Path $ilDirFull $sourcePath + $sourceRel = Get-RelativePath -Path $sourceFullPath + if (-not (Test-Path -LiteralPath $sourceFullPath -PathType Leaf)) { + $sourceResults.Add([pscustomobject]@{ path = $sourceRel; role = $source['role']; exists = $false; line_count = 0; size_bytes = $null }) + continue + } + + $item = Get-Item -LiteralPath $sourceFullPath + $lines = [System.IO.File]::ReadAllLines($sourceFullPath) + $sourceResults.Add([pscustomobject]@{ path = $sourceRel; role = $source['role']; exists = $true; line_count = $lines.Count; size_bytes = $item.Length }) + + foreach ($target in $targets) { + $targetKey = [string]$target['key'] + $compiled = @($target['patterns'] | ForEach-Object { [regex]::new($_, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) }) + for ($i = 0; $i -lt $lines.Count; $i++) { + $line = $lines[$i] + foreach ($rx in $compiled) { + if ($rx.IsMatch($line)) { + $targetBuckets[$targetKey].Add((New-Evidence -Lines $lines -Index $i -SourceRel $sourceRel)) + break + } + } + } + } +} + +$targetResults = New-Object System.Collections.Generic.List[object] +foreach ($target in $targets) { + $targetKey = [string]$target['key'] + $allEvidence = $targetBuckets[$targetKey].ToArray() + $evidenceForReport = @( + $allEvidence | + Sort-Object @{ Expression = { if ($_.kind -in @("method_header", "signature_continuation", "method_end")) { 0 } elseif ($_.kind -eq "callsite") { 1 } elseif ($_.kind -eq "string_literal") { 2 } else { 3 } } }, line_number | + Select-Object -First $MaxEvidencePerTarget + ) + $targetResults.Add([pscustomobject]@{ + key = $targetKey + display = $target['display'] + intent = $target['intent'] + classification = Get-Classification -Evidence $allEvidence + match_count = $allEvidence.Count + evidence = $evidenceForReport + }) +} + +$summary = [pscustomobject]@{ + generated_at = (Get-Date).ToUniversalTime().ToString("o") + repository = $repo + il_dir = $IlDir + sources = $sourceResults + targets = $targetResults + decision = "B-route text send remains plausible only if a bridge can enter the logged-in runtime or an existing IPC/API exposes the same call. Offline IL confirms call shape, but not runtime invocability." + originals_copied = $false + originals_modified = $false +} + +$jsonPath = Join-Path $outDir "text-send-map.json" +$summary | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $jsonPath -Encoding UTF8 + +$linesOut = New-Object System.Collections.Generic.List[string] +$linesOut.Add("# B-Route Text Send Static Map") +$linesOut.Add("") +$linesOut.Add("Date: 2026-07-12") +$linesOut.Add("") +$linesOut.Add("## Scope") +$linesOut.Add("") +$linesOut.Add("This report uses offline IL only. It does not log in, send messages, replay traffic, inject into a process, or mutate the original iSphere binaries.") +$linesOut.Add("") +$linesOut.Add("Machine-readable evidence: ``$OutputDir/text-send-map.json``") +$linesOut.Add("") +$linesOut.Add("## Sources") +$linesOut.Add("") +$linesOut.Add("| Source | Role | Exists | Lines | Size bytes |") +$linesOut.Add("| --- | --- | --- | ---: | ---: |") +foreach ($sourceResult in $sourceResults) { + $exists = if ($sourceResult.exists) { "yes" } else { "no" } + $sizeText = if ($null -ne $sourceResult.size_bytes) { [string]$sourceResult.size_bytes } else { "" } + $linesOut.Add("| ``$($sourceResult.path)`` | $($sourceResult.role) | $exists | $($sourceResult.line_count) | $sizeText |") +} +$linesOut.Add("") +$linesOut.Add("## Target classification") +$linesOut.Add("") +$linesOut.Add("| Target | Intent | Classification | Matches |") +$linesOut.Add("| --- | --- | --- | ---: |") +foreach ($targetResult in $targetResults) { + $linesOut.Add("| ``$($targetResult.display)`` | $($targetResult.intent) | ``$($targetResult.classification)`` | $($targetResult.match_count) |") +} +$linesOut.Add("") +$linesOut.Add("## Verified facts") +$linesOut.Add("") +$linesOut.Add("- ``AppContextManager.SendTxtMessageByJid`` appears in the offline client IL and is not just a UIA/RPA artifact.") +$linesOut.Add("- ``MessageScheduling.SendChatMessage`` appears in the offline client IL and has call-site evidence around chat-message flow.") +$linesOut.Add("- ``Chat.SendMessage`` / ``Chat.sendMessage`` appears as a smack chat API used by client code.") +$linesOut.Add("- ``XMPPConnection.send`` appears as a lower-level connection send primitive in offline IL.") +$linesOut.Add("- ``MessageCenter send-related callbacks`` appear as MessageCenter/GetMessageCenter references, but offline static evidence alone does not prove an external bridge can call them.") +$linesOut.Add("") +$linesOut.Add("## Evidence excerpts") +foreach ($targetResult in $targetResults) { + $linesOut.Add("") + $linesOut.Add("### $($targetResult.display)") + $linesOut.Add("") + $linesOut.Add("Classification: ``$($targetResult.classification)``; matches: $($targetResult.match_count).") + if (@($targetResult.evidence).Count -eq 0) { + $linesOut.Add("") + $linesOut.Add("No evidence found in configured offline IL sources.") + continue + } + $linesOut.Add("") + $linesOut.Add("| Source line | Kind | Snippet | Method context |") + $linesOut.Add("| --- | --- | --- | --- |") + foreach ($evidence in $targetResult.evidence) { + $method = if ($evidence.method_header) { "L$($evidence.method_header.line_number): $($evidence.method_header.text)" } else { "" } + $snippet = ($evidence.text -replace "\|", "\\|") + $method = ($method -replace "\|", "\\|") + $linesOut.Add("| ``$($evidence.source):$($evidence.line_number)`` | ``$($evidence.kind)`` | ``$snippet`` | ``$method`` |") + } +} +$linesOut.Add("") +$linesOut.Add("## Inference") +$linesOut.Add("") +$linesOut.Add("- High confidence: text sending has an internal managed call path that reaches smack chat/connection send APIs.") +$linesOut.Add("- Medium confidence: the likely chain is high-level AppContextManager or UI chat code -> MessageScheduling/frmP2PChat -> smack Chat.SendMessage/sendMessage -> XMPPConnection.send.") +$linesOut.Add("- Low confidence until live/runtime evidence: an external sidecar can invoke this path without being inside the logged-in process.") +$linesOut.Add("") +$linesOut.Add("## Decision") +$linesOut.Add("") +$linesOut.Add("B-route text send remains plausible only if a bridge can enter the logged-in runtime or an existing IPC/API exposes the same call. Offline IL confirms call shape, but not runtime invocability.") +$linesOut.Add("") +$linesOut.Add("## Next work") +$linesOut.Add("") +$linesOut.Add("1. Extract file-send/upload call graph and separate upload from chat-message finalization.") +$linesOut.Add("2. Extract bridge/IPC/plugin candidates to see whether the send call path is reachable without RPA.") +$linesOut.Add("3. Keep A-route/RPA as backup-only; do not promote it unless B-route bridge evidence fails.") + +($linesOut -join "`n") + "`n" | Set-Content -LiteralPath $reportFullPath -Encoding UTF8 + +$requiredNames = @("AppContextManager.SendTxtMessageByJid", "MessageScheduling.SendChatMessage", "Chat.SendMessage", "XMPPConnection.send", "MessageCenter send-related callbacks") +$reportText = Get-Content -LiteralPath $reportFullPath -Raw +$missing = @($requiredNames | Where-Object { $reportText -notlike "*$_*" }) +if ($missing.Count -gt 0) { + throw "Report missing required target names: $($missing -join ', ')" +} + +[pscustomobject]@{ + ok = $true + json_path = $jsonPath + report_path = $reportFullPath + sources = $sourceResults.Count + targets = $targetResults.Count + classifications = @($targetResults | ForEach-Object { "$($_.display)=$($_.classification)" }) + originals_copied = $false + originals_modified = $false +} | ConvertTo-Json -Depth 6