Initial project state: .NET reference, design, Rust port (M0+M1), evidence
rust / build / test / clippy / fmt (push) Has been cancelled

Layout:
- src/                    .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
                          MxAsbClient, probes, tests, harnesses. Executable spec.
- design/                 Architectural plan for the Rust port (M0–M6), error
                          model, protocol invariants, risks (R1–R16), adversarial
                          review log (review.md).
- rust/                   Rust workspace. M0 skeleton + M1 codec parity.
                          mxaccess-codec: 215 unit tests + 2 cross-implementation
                          parity tests (byte-identical against .NET reference).
                          Other crates are M0 stubs awaiting M2+.
- captures/               Frida + netsh + pcap evidence per CLAUDE.md
                          ("captures are evidence, not throwaway logs").
- analysis/               Decompiled C# (frida/proxy/decompiled-*),
                          Ghidra exports for native DLLs (`exports/` only —
                          working state at `projects/` and AVEVA's input
                          binaries at `input/` are gitignored).
- docs/                   Reverse-engineering reference docs.
- tools/                  Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
                          Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/      Rust CI: fmt + build + test + clippy on Windows.
- LICENSE                 MIT (Joseph Doherty, 2026).

Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly

Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-05 06:21:00 -04:00
parent 43733699b0
commit fe2a6db786
3849 changed files with 352975 additions and 0 deletions
+480
View File
@@ -0,0 +1,480 @@
// Frida hooks generated from headless Ghidra RVAs.
// Usage: frida -f <MxTraceHarness.exe> -l analysis/frida/mx-nmx-trace.js -- <harness args>
const maxDump = 4096;
const installed = {};
function now() {
return new Date().toISOString();
}
function emit(event) {
event.time = now();
console.log(JSON.stringify(event));
}
function hexDumpSafe(ptr, length) {
try {
if (ptr.isNull() || length <= 0) return "";
const capped = Math.min(length, maxDump);
return ptr.readByteArray(capped);
} catch (e) {
return null;
}
}
function toHex(arrayBuffer) {
if (arrayBuffer === null || arrayBuffer === "") return "";
const bytes = new Uint8Array(arrayBuffer);
let out = [];
for (let i = 0; i < bytes.length; i++) out.push(bytes[i].toString(16).padStart(2, "0"));
return out.join(" ");
}
function dumpBytes(ptrValue, length) {
return toHex(hexDumpSafe(ptrValue, length));
}
function readStdWString(base, offset) {
try {
const obj = base.add(offset);
const length = obj.add(0x10).readU32();
const capacity = obj.add(0x14).readU32();
const data = capacity < 8 ? obj : obj.readPointer();
if (length > 1024 || data.isNull()) {
return { length, capacity, value: "" };
}
return { length, capacity, value: data.readUtf16String(length) };
} catch (e) {
return { error: e.message };
}
}
function readBstr(ptrValue) {
try {
if (ptrValue.isNull()) return "";
return ptrValue.readUtf16String();
} catch (e) {
return "";
}
}
function readMxHandle(ptrValue) {
try {
if (ptrValue.isNull()) return null;
return {
raw: dumpBytes(ptrValue, 20),
w0: ptrValue.add(0).readU32(),
w1: ptrValue.add(4).readU32(),
w2: ptrValue.add(8).readU32(),
w3: ptrValue.add(12).readU32(),
w4: ptrValue.add(16).readU32()
};
} catch (e) {
return { error: e.message };
}
}
function readPreboundReference(ptrValue) {
try {
if (ptrValue.isNull()) return null;
const err = ptrValue.add(0xac).readPointer();
return {
ptr: ptrValue.toString(),
referenceString: readStdWString(ptrValue, 0x18),
contextString: readStdWString(ptrValue, 0x34),
auxString: readStdWString(ptrValue, 0x70),
mxReference: ptrValue.add(0x50).readPointer().toString(),
flags10: ptrValue.add(0x10).readU32(),
word14: ptrValue.add(0x14).readU32(),
word4c: ptrValue.add(0x4c).readU32(),
word54: ptrValue.add(0x54).readU32(),
word58: ptrValue.add(0x58).readU32(),
word5c: ptrValue.add(0x5c).readU32(),
word60: ptrValue.add(0x60).readU32(),
word64: ptrValue.add(0x64).readU32(),
word68: ptrValue.add(0x68).readU32(),
word6c: ptrValue.add(0x6c).readU32(),
worda0: ptrValue.add(0xa0).readU32(),
worda4: ptrValue.add(0xa4).readU32(),
status: ptrValue.add(0xa8).readU32(),
flagb0: ptrValue.add(0xb0).readU8(),
errorText: readBstr(err),
raw: dumpBytes(ptrValue, 0xb4)
};
} catch (e) {
return { error: e.message, ptr: ptrValue.toString() };
}
}
function argValue(args, index) {
try {
return args[index].toString();
} catch (e) {
return "";
}
}
function intArg(args, index) {
try {
return args[index].toInt32();
} catch (e) {
return null;
}
}
function uintArg(args, index) {
try {
return args[index].toUInt32();
} catch (e) {
return null;
}
}
function ptrArg(args, index) {
try {
return args[index];
} catch (e) {
return ptr("0");
}
}
function hook(moduleName, rva, name, handlers) {
const key = moduleName + "!" + name;
if (installed[key]) return;
const module = Process.findModuleByName(moduleName);
if (module === null) return;
const address = module.base.add(rva);
Interceptor.attach(address, handlers(address, module));
installed[key] = true;
emit({ event: "hook.installed", module: moduleName, name, base: module.base.toString(), rva: "0x" + rva.toString(16), address: address.toString() });
}
function hookPlainArgs(moduleName, rva, name, argCount) {
hook(moduleName, rva, name, function (address, module) {
return {
onEnter(args) {
let values = [];
for (let i = 0; i < argCount; i++) values.push(argValue(args, i));
emit({
event: "call.enter",
module: moduleName,
name,
address: address.toString(),
ecx: this.context.ecx ? this.context.ecx.toString() : "",
args: values
});
},
onLeave(retval) {
emit({ event: "call.leave", module: moduleName, name, retval: retval.toString() });
}
};
});
}
function hookAuthenticateUser() {
hook("LmxProxy.dll", 0x1399f, "CLMXProxyServer.AuthenticateUser", function (address, module) {
return {
onEnter(args) {
this.userIdOut = ptrArg(args, 4);
const password = readBstr(ptrArg(args, 3));
emit({
event: "call.enter",
module: "LmxProxy.dll",
name: "CLMXProxyServer.AuthenticateUser",
address: address.toString(),
ecx: this.context.ecx ? this.context.ecx.toString() : "",
serverHandle: intArg(args, 1),
user: readBstr(ptrArg(args, 2)),
passwordLength: password.length,
userIdOut: this.userIdOut.toString()
});
},
onLeave(retval) {
let userId = null;
try {
if (!this.userIdOut.isNull()) userId = this.userIdOut.readS32();
} catch (e) {
userId = null;
}
emit({
event: "call.leave",
module: "LmxProxy.dll",
name: "CLMXProxyServer.AuthenticateUser",
retval: retval.toString(),
userId
});
}
};
});
}
function hookLmxPrebindReference() {
hook("Lmx.dll", 0xea780, "MxConnection.PrebindReference", function (address, module) {
return {
onEnter(args) {
this.self = ptrArg(args, 0);
this.out = ptrArg(args, 2);
emit({
event: "lmx.prebind.enter",
module: "Lmx.dll",
name: "MxConnection.PrebindReference",
self: this.self.toString(),
outPtr: this.out.toString(),
referencePtr: ptrArg(args, 1).toString(),
reference: ptrArg(args, 1).readUtf16String()
});
},
onLeave(retval) {
let handle = null;
try {
if (!this.out.isNull()) handle = this.out.readS32();
} catch (e) {
handle = null;
}
emit({ event: "lmx.prebind.leave", module: "Lmx.dll", name: "MxConnection.PrebindReference", handle });
}
};
});
}
function hookLmxUserRegisterPreboundReference() {
hook("Lmx.dll", 0xe1920, "MxConnection.UserRegisterPreboundReference", function (address, module) {
return {
onEnter(args) {
this.self = ptrArg(args, 0);
this.out = ptrArg(args, 4);
emit({
event: "lmx.user-register-prebound.enter",
module: "Lmx.dll",
name: "MxConnection.UserRegisterPreboundReference",
self: this.self.toString(),
preboundHandle: intArg(args, 1),
callback: argValue(args, 2),
userData: intArg(args, 3),
outPtr: this.out.toString()
});
},
onLeave(retval) {
let mxReferenceHandle = null;
try {
if (!this.out.isNull()) mxReferenceHandle = this.out.readS32();
} catch (e) {
mxReferenceHandle = null;
}
emit({
event: "lmx.user-register-prebound.leave",
module: "Lmx.dll",
name: "MxConnection.UserRegisterPreboundReference",
retval: retval.toString(),
mxReferenceHandle
});
}
};
});
}
function hookLmxReferenceHandleReader() {
hook("Lmx.dll", 0x5f730, "IMxReference.GetMxHandle", function (address, module) {
return {
onEnter(args) {
this.out = ptrArg(args, 0);
this.ref = this.context.ecx ? this.context.ecx : ptr("0");
},
onLeave(retval) {
emit({
event: "lmx.mxhandle.read",
module: "Lmx.dll",
name: "IMxReference.GetMxHandle",
referencePtr: this.ref.toString(),
outPtr: this.out.toString(),
handle: readMxHandle(this.out),
retval: retval.toString()
});
}
};
});
}
function hookLmxFixupMxHandle() {
hook("Lmx.dll", 0x8f8b0, "AccessManager.FixUpMxHandle", function (address, module) {
return {
onEnter(args) {
this.out = ptrArg(args, 0);
emit({
event: "lmx.fixup-mxhandle.enter",
module: "Lmx.dll",
name: "AccessManager.FixUpMxHandle",
accessManager: this.context.ecx ? this.context.ecx.toString() : "",
outPtr: this.out.toString(),
inWords: [uintArg(args, 1), uintArg(args, 2), uintArg(args, 3), uintArg(args, 4), uintArg(args, 5), uintArg(args, 6)]
});
},
onLeave(retval) {
emit({
event: "lmx.fixup-mxhandle.leave",
module: "Lmx.dll",
name: "AccessManager.FixUpMxHandle",
outPtr: this.out.toString(),
handle: readMxHandle(this.out),
retval: retval.toString()
});
}
};
});
}
function hookLmxResolveReference() {
hook("Lmx.dll", 0x113d40, "PreboundReference.Resolve", function (address, module) {
return {
onEnter(args) {
this.prebound = this.context.ecx ? this.context.ecx : ptr("0");
emit({
event: "lmx.prebound-resolve.enter",
module: "Lmx.dll",
name: "PreboundReference.Resolve",
prebound: readPreboundReference(this.prebound)
});
},
onLeave(retval) {
emit({
event: "lmx.prebound-resolve.leave",
module: "Lmx.dll",
name: "PreboundReference.Resolve",
prebound: readPreboundReference(this.prebound),
retval: retval.toString()
});
}
};
});
}
function hookLmxResolveCallbacks() {
hook("Lmx.dll", 0x1155a0, "PreboundReference.OnPlatformResolveReferenceResults", function (address, module) {
return {
onEnter(args) {
this.prebound = this.context.ecx ? this.context.ecx : ptr("0");
this.reference = this.context.edx ? this.context.edx : ptr("0");
emit({
event: "lmx.platform-resolve-results.enter",
module: "Lmx.dll",
name: "PreboundReference.OnPlatformResolveReferenceResults",
prebound: readPreboundReference(this.prebound),
referencePtr: this.reference.toString()
});
},
onLeave(retval) {
emit({
event: "lmx.platform-resolve-results.leave",
module: "Lmx.dll",
name: "PreboundReference.OnPlatformResolveReferenceResults",
prebound: readPreboundReference(this.prebound),
retval: retval.toString()
});
}
};
});
hook("Lmx.dll", 0x114a90, "PreboundReference.OnSetAttributeResult", function (address, module) {
return {
onEnter(args) {
this.prebound = this.context.ecx ? this.context.ecx : ptr("0");
emit({
event: "lmx.set-attribute-result.enter",
module: "Lmx.dll",
name: "PreboundReference.OnSetAttributeResult",
prebound: readPreboundReference(this.prebound),
correlationId: this.context.edx ? this.context.edx.toString() : "",
pValue: argValue(args, 0),
status: argValue(args, 3)
});
},
onLeave(retval) {
emit({
event: "lmx.set-attribute-result.leave",
module: "Lmx.dll",
name: "PreboundReference.OnSetAttributeResult",
prebound: readPreboundReference(this.prebound),
retval: retval.toString()
});
}
};
});
}
function hookNmxPutRequest(moduleName, rva, name, ex) {
hook(moduleName, rva, name, function (address, module) {
return {
onEnter(args) {
// Ghidra sees this as a C++ method. On x86 thiscall, ECX is likely `this`
// and args[0] is the first stack argument. We log broad argument state
// and dump plausible size/payload pairs for later alignment.
const candidates = [];
for (let sizeIndex = 3; sizeIndex <= 8; sizeIndex++) {
const size = uintArg(args, sizeIndex);
const dataPtr = ptrArg(args, sizeIndex + 1);
if (size !== null && size > 0 && size <= 65536 && !dataPtr.isNull()) {
candidates.push({
sizeIndex,
ptrIndex: sizeIndex + 1,
size,
ptr: dataPtr.toString(),
hex: toHex(hexDumpSafe(dataPtr, size))
});
}
}
let values = [];
for (let i = 0; i < 10; i++) values.push(argValue(args, i));
emit({
event: "nmx.enter",
module: moduleName,
name,
address: address.toString(),
ecx: this.context.ecx ? this.context.ecx.toString() : "",
args: values,
candidates
});
},
onLeave(retval) {
emit({ event: "nmx.leave", module: moduleName, name, retval: retval.toString() });
}
};
});
}
function installKnownHooks() {
hookPlainArgs("LmxProxy.dll", 0x12c0c, "CLMXProxyServer.Write.variantA", 10);
hookPlainArgs("LmxProxy.dll", 0x13280, "CLMXProxyServer.Write.variantB", 13);
hookPlainArgs("LmxProxy.dll", 0x12f24, "CLMXProxyServer.WriteSecured.variantA", 10);
hookPlainArgs("LmxProxy.dll", 0x135fe, "CLMXProxyServer.WriteSecured.variantB", 14);
hookPlainArgs("LmxProxy.dll", 0x1121d, "CLMXProxyServer.AddBufferedItem", 5);
hookPlainArgs("LmxProxy.dll", 0x0fc80, "CLMXProxyServer.SetBufferedUpdateInterval", 3);
hookPlainArgs("LmxProxy.dll", 0x142b4, "CLMXProxyServer.AdviseSupervisory", 5);
hookPlainArgs("LmxProxy.dll", 0x163c0, "CProxy_ILMXProxyServerEvents2.Fire_OnBufferedDataChange", 8);
hookPlainArgs("LmxProxy.dll", 0x16b50, "CUserConnectionCallback.OnSetAttributeResult", 4);
hookPlainArgs("LmxProxy.dll", 0x16d4b, "CUserConnectionCallback.OperationComplete", 4);
hookAuthenticateUser();
hookNmxPutRequest("NmxAdptr.dll", 0x10996, "CNmxAdapter.TransferData", false);
hookNmxPutRequest("NmxAdptr.dll", 0x112da, "CNmxAdapter.ProcessDataReceived", false);
hookNmxPutRequest("NmxAdptr.dll", 0x15169, "CNmxAdapter.PutRequest", false);
hookNmxPutRequest("NmxAdptr.dll", 0x159c3, "CNmxAdapter.PutRequestEx", true);
hookLmxPrebindReference();
hookLmxUserRegisterPreboundReference();
hookLmxReferenceHandleReader();
hookLmxFixupMxHandle();
hookLmxResolveReference();
hookLmxResolveCallbacks();
}
const timer = setInterval(function () {
installKnownHooks();
if (installed["LmxProxy.dll!CLMXProxyServer.Write.variantA"] && installed["NmxAdptr.dll!CNmxAdapter.PutRequest"]) {
clearInterval(timer);
}
}, 100);
installKnownHooks();
+379
View File
@@ -0,0 +1,379 @@
// Frida hooks for the 32-bit MIDL COM proxy path used by NmxSvcps.dll.
// Usage:
// frida -f src/MxTraceHarness/bin/Release/net481/MxTraceHarness.exe \
// -l analysis/frida/nmx-com-proxy-trace.js -- --scenario=register --duration=3
const maxDump = 8192;
const installed = {};
let lastBstrMarshalBuffer = ptr("0");
let lastBstrMarshalLength = 0;
function now() {
return new Date().toISOString();
}
function emit(event) {
event.time = now();
console.log(JSON.stringify(event));
}
function toHex(arrayBuffer) {
if (arrayBuffer === null || arrayBuffer === "") return "";
const bytes = new Uint8Array(arrayBuffer);
const out = [];
for (let i = 0; i < bytes.length; i++) out.push(bytes[i].toString(16).padStart(2, "0"));
return out.join(" ");
}
function dump(ptrValue, length) {
try {
if (ptrValue.isNull() || length <= 0) return "";
return toHex(ptrValue.readByteArray(Math.min(length, maxDump)));
} catch (e) {
return null;
}
}
function readU32(ptrValue, offset) {
try {
return ptrValue.add(offset).readU32();
} catch (e) {
return null;
}
}
function readPtr(ptrValue, offset) {
try {
return ptrValue.add(offset).readPointer();
} catch (e) {
return ptr("0");
}
}
function moduleNameFor(address) {
try {
const module = Process.findModuleByAddress(address);
return module === null ? "" : module.name;
} catch (e) {
return "";
}
}
function readUtf16(ptrValue, maxChars) {
try {
if (ptrValue.isNull()) return "";
return ptrValue.readUtf16String(maxChars);
} catch (e) {
return "";
}
}
function describePossibleBstr(ptrValue) {
try {
if (ptrValue.isNull()) return null;
const byteLength = ptrValue.sub(4).readU32();
if (byteLength > 4096 || (byteLength % 2) !== 0) return null;
return {
ptr: ptrValue.toString(),
byteLength,
charLength: byteLength / 2,
text: readUtf16(ptrValue, byteLength / 2)
};
} catch (e) {
return null;
}
}
function dumpNdrVarArgs(args) {
const values = [];
for (let i = 2; i < 12; i++) {
let value = ptr("0");
try {
value = args[i];
} catch (e) {
value = ptr("0");
}
const item = {
index: i,
value: value.toString(),
module: moduleNameFor(value),
asU32: value.toUInt32()
};
const bstr = describePossibleBstr(value);
if (bstr !== null) {
item.bstr = bstr;
}
values.push(item);
}
return values;
}
function dumpStackWords(context, count) {
const values = [];
let esp = ptr("0");
try {
esp = context.esp;
} catch (e) {
return values;
}
for (let i = 0; i < count; i++) {
const address = esp.add(i * Process.pointerSize);
let value = ptr("0");
try {
value = address.readPointer();
} catch (e) {
value = ptr("0");
}
const item = {
index: i,
address: address.toString(),
value: value.toString(),
module: moduleNameFor(value),
asU32: value.toUInt32()
};
const bstr = describePossibleBstr(value);
if (bstr !== null) {
item.bstr = bstr;
}
values.push(item);
}
return values;
}
function hookExport(moduleName, exportName, callbacks) {
const key = moduleName + "!" + exportName;
if (installed[key]) return;
let address = null;
try {
const module = Process.findModuleByName(moduleName);
if (module !== null && typeof module.findExportByName === "function") {
address = module.findExportByName(exportName);
}
if (address === null && typeof Module.findExportByName === "function") {
address = Module.findExportByName(moduleName, exportName);
}
} catch (e) {
address = null;
}
if (address === null) {
emit({ event: "hook.missing", module: moduleName, name: exportName });
return;
}
Interceptor.attach(address, callbacks(address));
installed[key] = true;
emit({ event: "hook.installed", module: moduleName, name: exportName, address: address.toString() });
}
function dumpRpcMessage(pRpcMsg) {
// 32-bit RPC_MESSAGE layout:
// Handle, DataRepresentation, Buffer, BufferLength, ProcNum, TransferSyntax,
// RpcInterfaceInformation, ReservedForRuntime, ManagerEpv, ImportContext, RpcFlags.
const buffer = readPtr(pRpcMsg, 8);
const bufferLength = readU32(pRpcMsg, 12);
const procNum = readU32(pRpcMsg, 16);
const rpcInterfaceInfo = readPtr(pRpcMsg, 24);
return {
rpcMessage: pRpcMsg.toString(),
buffer: buffer.toString(),
bufferLength,
procNum,
rpcInterfaceInfo: rpcInterfaceInfo.toString(),
rpcInterfaceModule: moduleNameFor(rpcInterfaceInfo),
hex: bufferLength === null ? null : dump(buffer, bufferLength)
};
}
function dumpStubMessage(pStubMsg) {
// Common 32-bit MIDL_STUB_MESSAGE prefix. This is intentionally broad; the
// important fields for this investigation are the RPC_MESSAGE pointer and
// the active buffer range used by the generated NmxSvcps proxy.
const rpcMsg = readPtr(pStubMsg, 0);
const buffer = readPtr(pStubMsg, 4);
const bufferStart = readPtr(pStubMsg, 8);
const bufferEnd = readPtr(pStubMsg, 12);
const bufferLength = readU32(pStubMsg, 20);
let activeLength = 0;
try {
activeLength = bufferEnd.sub(bufferStart).toInt32();
} catch (e) {
activeLength = 0;
}
return {
stubMessage: pStubMsg.toString(),
rpcMessage: rpcMsg.toString(),
buffer: buffer.toString(),
bufferStart: bufferStart.toString(),
bufferEnd: bufferEnd.toString(),
bufferLength,
activeLength,
bufferHex: activeLength > 0 ? dump(bufferStart, activeLength) : "",
rpc: rpcMsg.isNull() ? null : dumpRpcMessage(rpcMsg)
};
}
function installHooks() {
hookExport("oleaut32.dll", "BSTR_UserMarshal", function () {
return {
onEnter(args) {
this.buffer = args[1];
this.bstrSlot = args[2];
let bstr = ptr("0");
try {
bstr = this.bstrSlot.readPointer();
} catch (e) {
bstr = ptr("0");
}
emit({
event: "bstr.usermarshal.enter",
callerModule: moduleNameFor(this.returnAddress),
flags: args[0].toString(),
buffer: this.buffer.toString(),
bstrSlot: this.bstrSlot.toString(),
bstr: describePossibleBstr(bstr)
});
},
onLeave(retval) {
let length = 0;
try {
length = retval.sub(this.buffer).toInt32();
} catch (e) {
length = 0;
}
lastBstrMarshalBuffer = this.buffer;
lastBstrMarshalLength = length;
emit({
event: "bstr.usermarshal.leave",
callerModule: moduleNameFor(this.returnAddress),
buffer: this.buffer ? this.buffer.toString() : "",
retval: retval.toString(),
marshaledLength: length,
marshaledHex: length > 0 ? dump(this.buffer, length) : ""
});
}
};
});
hookExport("rpcrt4.dll", "NdrInterfacePointerMarshall", function () {
return {
onEnter(args) {
this.stubMsg = args[0];
emit({
event: "ndr.interfaceptr.marshal.enter",
callerModule: moduleNameFor(this.returnAddress),
interfacePointer: args[1].toString(),
format: args[2].toString(),
formatPrefix: dump(args[2], 32),
stub: dumpStubMessage(this.stubMsg)
});
},
onLeave(retval) {
emit({
event: "ndr.interfaceptr.marshal.leave",
callerModule: moduleNameFor(this.returnAddress),
retval: retval.toString(),
stub: this.stubMsg ? dumpStubMessage(this.stubMsg) : null
});
}
};
});
hookExport("rpcrt4.dll", "NdrClientCall2", function () {
return {
onEnter(args) {
this.format = args[1];
this.varargs = dumpNdrVarArgs(args);
emit({
event: "ndr.client.enter",
callerModule: moduleNameFor(this.returnAddress),
stubDesc: args[0].toString(),
procFormat: this.format.toString(),
procFormatPrefix: dump(this.format, 64),
varargs: this.varargs,
stack: dumpStackWords(this.context, 24)
});
},
onLeave(retval) {
let surroundingStubHex = "";
if (lastBstrMarshalBuffer && !lastBstrMarshalBuffer.isNull()) {
try {
surroundingStubHex = dump(lastBstrMarshalBuffer.sub(36), 192);
} catch (e) {
surroundingStubHex = null;
}
}
emit({
event: "ndr.client.leave",
callerModule: moduleNameFor(this.returnAddress),
procFormat: this.format ? this.format.toString() : "",
retval: retval.toString(),
lastBstrMarshalLength,
surroundingStubHex
});
}
};
});
hookExport("rpcrt4.dll", "NdrProxySendReceive", function () {
return {
onEnter(args) {
emit({
event: "ndr.proxy.sendreceive.enter",
callerModule: moduleNameFor(this.returnAddress),
thisPtr: args[0].toString(),
stub: dumpStubMessage(args[1])
});
},
onLeave(retval) {
emit({
event: "ndr.proxy.sendreceive.leave",
callerModule: moduleNameFor(this.returnAddress),
retval: retval.toString()
});
}
};
});
hookExport("rpcrt4.dll", "I_RpcSendReceive", function () {
return {
onEnter(args) {
emit({
event: "rpc.sendreceive.enter",
callerModule: moduleNameFor(this.returnAddress),
rpc: dumpRpcMessage(args[0])
});
},
onLeave(retval) {
emit({
event: "rpc.sendreceive.leave",
callerModule: moduleNameFor(this.returnAddress),
retval: retval.toString()
});
}
};
});
}
emit({ event: "script.loaded", process: Process.id, arch: Process.arch, pointerSize: Process.pointerSize });
installHooks();
const retryTimer = setInterval(function () {
installHooks();
if (installed["oleaut32.dll!BSTR_UserMarshal"]
&& installed["rpcrt4.dll!NdrInterfacePointerMarshall"]
&& installed["rpcrt4.dll!NdrClientCall2"]) {
clearInterval(retryTimer);
}
}, 100);
+239
View File
@@ -0,0 +1,239 @@
// Service-side Frida hooks for NmxSvc.exe.
// Usage: frida -p <NmxSvc pid> -l analysis/frida/nmxsvc-trace.js
const maxDump = 4096;
const installed = {};
function now() {
return new Date().toISOString();
}
function emit(event) {
event.time = now();
console.log(JSON.stringify(event));
}
function toHex(arrayBuffer) {
if (arrayBuffer === null || arrayBuffer === "") return "";
const bytes = new Uint8Array(arrayBuffer);
const out = [];
for (let i = 0; i < bytes.length; i++) out.push(bytes[i].toString(16).padStart(2, "0"));
return out.join(" ");
}
function hexDumpSafe(ptrValue, length) {
try {
if (ptrValue.isNull() || length <= 0) return "";
const capped = Math.min(length, maxDump);
return ptrValue.readByteArray(capped);
} catch (e) {
return null;
}
}
function argValue(args, index) {
try {
return args[index].toString();
} catch (e) {
return "";
}
}
function uintArg(args, index) {
try {
return args[index].toUInt32();
} catch (e) {
return null;
}
}
function ptrArg(args, index) {
try {
return args[index];
} catch (e) {
return ptr("0");
}
}
function readStackWord(context, index) {
try {
return context.esp.add(index * Process.pointerSize).readPointer();
} catch (e) {
return ptr("0");
}
}
function readUtf16(ptrValue) {
try {
if (ptrValue.isNull()) return "";
const text = ptrValue.readUtf16String(96);
if (text === null) return "";
return text.replace(/\u0000.*$/g, "");
} catch (e) {
return "";
}
}
function looksLikeSize(value) {
return value !== null && value > 0 && value <= 65536;
}
function addCandidate(candidates, source, sizeIndex, ptrIndex, size, dataPtr) {
try {
if (!looksLikeSize(size) || dataPtr.isNull()) return;
const hex = toHex(hexDumpSafe(dataPtr, size));
if (hex === "") return;
candidates.push({
source,
sizeIndex,
ptrIndex,
size,
ptr: dataPtr.toString(),
hex
});
} catch (e) {
}
}
function candidateBuffers(args, context) {
const candidates = [];
for (let sizeIndex = 0; sizeIndex <= 12; sizeIndex++) {
const size = uintArg(args, sizeIndex);
const directPtr = ptrArg(args, sizeIndex + 1);
addCandidate(candidates, "args.direct", sizeIndex, sizeIndex + 1, size, directPtr);
try {
if (!directPtr.isNull()) {
addCandidate(candidates, "args.byref", sizeIndex, sizeIndex + 1, size, directPtr.readPointer());
}
} catch (e) {
}
}
for (let sizeIndex = 0; sizeIndex <= 16; sizeIndex++) {
const sizeWord = readStackWord(context, sizeIndex);
let size = null;
try {
size = sizeWord.toUInt32();
} catch (e) {
}
const directPtr = readStackWord(context, sizeIndex + 1);
addCandidate(candidates, "stack.direct", sizeIndex, sizeIndex + 1, size, directPtr);
try {
if (!directPtr.isNull()) {
addCandidate(candidates, "stack.byref", sizeIndex, sizeIndex + 1, size, directPtr.readPointer());
}
} catch (e) {
}
}
return candidates;
}
function stackWords(context, count) {
const words = [];
for (let i = 0; i < count; i++) words.push(readStackWord(context, i).toString());
return words;
}
function hook(moduleName, rva, name, handlers) {
const key = moduleName + "!" + name;
if (installed[key]) return;
const module = Process.findModuleByName(moduleName);
if (module === null) return;
const address = module.base.add(rva);
Interceptor.attach(address, handlers(address, module));
installed[key] = true;
emit({ event: "hook.installed", module: moduleName, name, base: module.base.toString(), rva: "0x" + rva.toString(16), address: address.toString() });
}
function hookNmxServiceFunction(rva, name, argCount) {
hook("NmxSvc.exe", rva, name, function (address, module) {
return {
onEnter(args) {
const argList = [];
for (let i = 0; i < argCount; i++) argList.push(argValue(args, i));
emit({
event: "nmxsvc.enter",
module: "NmxSvc.exe",
name,
address: address.toString(),
ecx: this.context.ecx ? this.context.ecx.toString() : "",
esp: this.context.esp ? this.context.esp.toString() : "",
args: argList,
stack: stackWords(this.context, 18),
candidates: candidateBuffers(args, this.context)
});
},
onLeave(retval) {
emit({ event: "nmxsvc.leave", module: "NmxSvc.exe", name, retval: retval.toString() });
}
};
});
}
function hookWinsock(name) {
let address = null;
try {
if (typeof Module.findExportByName === "function") {
address = Module.findExportByName("ws2_32.dll", name);
} else {
const ws2 = Process.findModuleByName("ws2_32.dll");
if (ws2 !== null && typeof ws2.findExportByName === "function") {
address = ws2.findExportByName(name);
}
}
} catch (e) {
address = null;
}
if (address === null) return;
const key = "ws2_32.dll!" + name;
if (installed[key]) return;
Interceptor.attach(address, {
onEnter(args) {
const len = uintArg(args, 2);
emit({
event: "winsock.enter",
module: "ws2_32.dll",
name,
socket: argValue(args, 0),
buf: argValue(args, 1),
len,
flags: argValue(args, 3),
hex: looksLikeSize(len) ? toHex(hexDumpSafe(ptrArg(args, 1), len)) : ""
});
},
onLeave(retval) {
emit({ event: "winsock.leave", module: "ws2_32.dll", name, retval: retval.toString() });
}
});
installed[key] = true;
emit({ event: "hook.installed", module: "ws2_32.dll", name, address: address.toString() });
}
function installKnownHooks() {
hookNmxServiceFunction(0x05be1, "CFMCCallback.DataReceived", 8);
hookNmxServiceFunction(0x1807f, "CNmxControler.ProcessDataReceivedForEngine", 10);
hookNmxServiceFunction(0x1d910, "CNmxControler.DataReceived", 10);
hookNmxServiceFunction(0x1dcb5, "CNmxControler.TransferData", 10);
hookNmxServiceFunction(0x1eea5, "CNmxControler.LocalCallbackDataReceived", 10);
hookNmxServiceFunction(0x21b20, "CNmxService.TransferData", 10);
hookWinsock("send");
hookWinsock("recv");
hookWinsock("sendto");
hookWinsock("recvfrom");
}
emit({
event: "script.loaded",
process: Process.id,
arch: Process.arch,
pointerSize: Process.pointerSize
});
installKnownHooks();
@@ -0,0 +1,8 @@
capture tag type values com_variant_type putrequest_size putrequest_first_value_offset transferdata_size transferdata_first_value_offset processdatareceived_size processdatareceived_first_value_offset processdatareceived_encoding array_kind_byte element_count element_width_or_code array_descriptor_hex notes
029-frida-write-test-int-array TestChildObject.TestIntArray[] int[] 201;202;203;204;205;206;207;208;209;210 SAFEARRAY VT_I4/0x2003 86 28 132 74 134 94 int32 little-endian 0x42 10 4 42 00 00 00 00 0a 00 04 00 00 00 Packed numeric array values begin immediately after the 11-byte array descriptor.
030-frida-write-test-bool-array TestChildObject.TestBoolArray[] bool[] true;false;true;false;true;false;true;false;true;false SAFEARRAY VT_BOOL/0x200b 66 28 112 74 114 94 VARIANT_BOOL-style 16-bit values 0x41 10 2 41 00 00 00 00 0a 00 02 00 00 00 Requested alternating values returned as True,True,False,False,True,True,False,False,True,True; capture 098 confirms this is an x86 COM automation projection issue rather than a one-off capture.
098-frida-write-bool-array-pattern-10 TestChildObject.TestBoolArray[] bool[] true;false;false;true;true;false;true;false;false;true SAFEARRAY VT_BOOL/0x200b 66 28 112 74 114 94 VARIANT_BOOL-style 16-bit values 0x41 10 2 41 00 00 00 00 0a 00 02 00 00 00 Requested non-repeating values emitted as True,True,False,False,False,False,True,True,True,True through x86 MXAccess COM automation; managed native encoder keeps direct per-element bool encoding by default.
031-frida-write-test-float-array TestChildObject.TestFloatArray[] float[] 1.25;2.5;3.75;4.25;5.5;6.75;7.25;8.5;9.75;10.25 SAFEARRAY VT_R4/0x2004 86 28 132 74 134 94 float32 little-endian 0x43 10 4 43 00 00 00 00 0a 00 04 00 00 00 Packed numeric array values begin immediately after the 11-byte array descriptor.
032-frida-write-test-double-array TestChildObject.TestDoubleArray[] double[] 1.125;2.25;3.5;4.625;5.75;6.875;7.0;8.125;9.25;10.375 SAFEARRAY VT_R8/0x2005 126 28 172 74 174 94 float64 little-endian 0x44 10 8 44 00 00 00 00 0a 00 08 00 00 00 Packed numeric array values begin immediately after the 11-byte array descriptor.
033-frida-write-test-string-array TestChildObject.TestStringArray[] string[] A01;B02;C03;D04;E05;F06;G07;H08;I09;J10 SAFEARRAY VT_BSTR/0x2008 256 41 302 87 304 107 UTF-16LE per-element variable record 0x45 10 4 45 00 00 00 00 0a 00 04 00 00 00 Each element is record_length + scalar string-style variable payload.
035-frida-write-test-datetime-array-full TestChildObject.TestDateTimeArray[] datetime[] 2026-04-25T03:00:00 through 2026-04-25T03:09:00 SAFEARRAY VT_DATE/0x2007 596 41 642 87 214 94 outbound UTF-16LE per-element display string; callback FILETIME sequence 0x45 10 4 45 00 00 00 00 0a 00 04 00 00 00 Original capture 034 was truncated by the old 256-byte Frida dump cap; capture 035 uses the 4096-byte cap and has the full body.
1 capture tag type values com_variant_type putrequest_size putrequest_first_value_offset transferdata_size transferdata_first_value_offset processdatareceived_size processdatareceived_first_value_offset processdatareceived_encoding array_kind_byte element_count element_width_or_code array_descriptor_hex notes
2 029-frida-write-test-int-array TestChildObject.TestIntArray[] int[] 201;202;203;204;205;206;207;208;209;210 SAFEARRAY VT_I4/0x2003 86 28 132 74 134 94 int32 little-endian 0x42 10 4 42 00 00 00 00 0a 00 04 00 00 00 Packed numeric array values begin immediately after the 11-byte array descriptor.
3 030-frida-write-test-bool-array TestChildObject.TestBoolArray[] bool[] true;false;true;false;true;false;true;false;true;false SAFEARRAY VT_BOOL/0x200b 66 28 112 74 114 94 VARIANT_BOOL-style 16-bit values 0x41 10 2 41 00 00 00 00 0a 00 02 00 00 00 Requested alternating values returned as True,True,False,False,True,True,False,False,True,True; capture 098 confirms this is an x86 COM automation projection issue rather than a one-off capture.
4 098-frida-write-bool-array-pattern-10 TestChildObject.TestBoolArray[] bool[] true;false;false;true;true;false;true;false;false;true SAFEARRAY VT_BOOL/0x200b 66 28 112 74 114 94 VARIANT_BOOL-style 16-bit values 0x41 10 2 41 00 00 00 00 0a 00 02 00 00 00 Requested non-repeating values emitted as True,True,False,False,False,False,True,True,True,True through x86 MXAccess COM automation; managed native encoder keeps direct per-element bool encoding by default.
5 031-frida-write-test-float-array TestChildObject.TestFloatArray[] float[] 1.25;2.5;3.75;4.25;5.5;6.75;7.25;8.5;9.75;10.25 SAFEARRAY VT_R4/0x2004 86 28 132 74 134 94 float32 little-endian 0x43 10 4 43 00 00 00 00 0a 00 04 00 00 00 Packed numeric array values begin immediately after the 11-byte array descriptor.
6 032-frida-write-test-double-array TestChildObject.TestDoubleArray[] double[] 1.125;2.25;3.5;4.625;5.75;6.875;7.0;8.125;9.25;10.375 SAFEARRAY VT_R8/0x2005 126 28 172 74 174 94 float64 little-endian 0x44 10 8 44 00 00 00 00 0a 00 08 00 00 00 Packed numeric array values begin immediately after the 11-byte array descriptor.
7 033-frida-write-test-string-array TestChildObject.TestStringArray[] string[] A01;B02;C03;D04;E05;F06;G07;H08;I09;J10 SAFEARRAY VT_BSTR/0x2008 256 41 302 87 304 107 UTF-16LE per-element variable record 0x45 10 4 45 00 00 00 00 0a 00 04 00 00 00 Each element is record_length + scalar string-style variable payload.
8 035-frida-write-test-datetime-array-full TestChildObject.TestDateTimeArray[] datetime[] 2026-04-25T03:00:00 through 2026-04-25T03:09:00 SAFEARRAY VT_DATE/0x2007 596 41 642 87 214 94 outbound UTF-16LE per-element display string; callback FILETIME sequence 0x45 10 4 45 00 00 00 00 0a 00 04 00 00 00 Original capture 034 was truncated by the old 256-byte Frida dump cap; capture 035 uses the 4096-byte cap and has the full body.
+7
View File
@@ -0,0 +1,7 @@
capture tag type values com_variant_type com_value_carrier putrequest_size putrequest_value_offset putrequest_encoding transferdata_size transferdata_value_offset processdatareceived_size processdatareceived_value_offset processdatareceived_encoding notes
023-frida-write-test-int-sequence-109-111 TestChildObject.TestInt int 109,110,111 VT_I4/0x3 args[5] int32 40 18 int32 little-endian 86 64 88 84 int32 little-endian TransferData offset is 46-byte wrapper plus inner offset 18.
024-frida-write-test-bool-sequence TestChildObject.TestBool bool true,false,true VT_BOOL/0xb args[5] VARIANT_BOOL, true=0xffff false=0x0000 37 18 4-byte slot: true ff ff ff 00, false 00 ff ff 00 83 64 85 84 data-change body final byte: true ff, false 00 A separate 51-byte status-like ProcessDataReceived body appears after each write; the bool data-change value is in the 85-byte body.
025-frida-write-test-float-sequence TestChildObject.TestFloat float 1.25,2.5,3.75 VT_R4/0x4 args[5] float32 bits 40 18 float32 little-endian 86 64 88 84 float32 little-endian Same 40-byte write body shape as int with scalar slot retyped.
026-frida-write-test-double-sequence TestChildObject.TestDouble double 1.125,2.25,4.5 VT_R8/0x5 args[5]/args[6] float64 bits 44 18 float64 little-endian 90 64 92 84 float64 little-endian Body grows by 4 bytes relative to int/float.
027-frida-write-test-string-sequence TestChildObject.TestString string AlphaMX,BetaMX,GammaMX VT_BSTR/0x8 BSTR pointer 58 or 60 26 UTF-16LE string bytes 104 or 106 72 106 or 108 92 UTF-16LE string bytes Size depends on string length; value starts 8 bytes later than numeric slot.
028-frida-write-test-datetime-sequence TestChildObject.TestDateTime datetime 2026-04-25T02:30:00,2026-04-25T02:31:00,2026-04-25T02:32:00 VT_DATE/0x7 args[5]/args[6] OLE Automation DATE double 86 26 UTF-16LE display string, e.g. 4/25/2026 2:30:00 AM 132 72 98 88 FILETIME little-endian Outbound datetime is formatted as a string, while callback/update uses FILETIME.
1 capture tag type values com_variant_type com_value_carrier putrequest_size putrequest_value_offset putrequest_encoding transferdata_size transferdata_value_offset processdatareceived_size processdatareceived_value_offset processdatareceived_encoding notes
2 023-frida-write-test-int-sequence-109-111 TestChildObject.TestInt int 109,110,111 VT_I4/0x3 args[5] int32 40 18 int32 little-endian 86 64 88 84 int32 little-endian TransferData offset is 46-byte wrapper plus inner offset 18.
3 024-frida-write-test-bool-sequence TestChildObject.TestBool bool true,false,true VT_BOOL/0xb args[5] VARIANT_BOOL, true=0xffff false=0x0000 37 18 4-byte slot: true ff ff ff 00, false 00 ff ff 00 83 64 85 84 data-change body final byte: true ff, false 00 A separate 51-byte status-like ProcessDataReceived body appears after each write; the bool data-change value is in the 85-byte body.
4 025-frida-write-test-float-sequence TestChildObject.TestFloat float 1.25,2.5,3.75 VT_R4/0x4 args[5] float32 bits 40 18 float32 little-endian 86 64 88 84 float32 little-endian Same 40-byte write body shape as int with scalar slot retyped.
5 026-frida-write-test-double-sequence TestChildObject.TestDouble double 1.125,2.25,4.5 VT_R8/0x5 args[5]/args[6] float64 bits 44 18 float64 little-endian 90 64 92 84 float64 little-endian Body grows by 4 bytes relative to int/float.
6 027-frida-write-test-string-sequence TestChildObject.TestString string AlphaMX,BetaMX,GammaMX VT_BSTR/0x8 BSTR pointer 58 or 60 26 UTF-16LE string bytes 104 or 106 72 106 or 108 92 UTF-16LE string bytes Size depends on string length; value starts 8 bytes later than numeric slot.
7 028-frida-write-test-datetime-sequence TestChildObject.TestDateTime datetime 2026-04-25T02:30:00,2026-04-25T02:31:00,2026-04-25T02:32:00 VT_DATE/0x7 args[5]/args[6] OLE Automation DATE double 86 26 UTF-16LE display string, e.g. 4/25/2026 2:30:00 AM 132 72 98 88 FILETIME little-endian Outbound datetime is formatted as a string, while callback/update uses FILETIME.
@@ -0,0 +1,6 @@
capture tag write_type value value_body status_body on_write_complete notes
089-frida-write-testint-wrong-type TestChildObject.TestInt string not_an_int 0x37 string wire kind 0x05 completion-only 0x41 no String-to-integer conversion failure is not surfaced by MXAccess OnWriteComplete.
090-frida-write-invalid-reference NoSuchObject_999.NoSuchAttr int 145 none after failed registration none no Write returns S_OK after invalid registration result, but no value-bearing write body is emitted.
091-frida-write-testint-double-type TestChildObject.TestInt double 1.25 0x37 double wire kind 0x04 completion-only 0x00 no Double-to-integer path appears accepted/coerced at NMX completion level.
092-frida-write-testbool-string-type TestChildObject.TestBool string not_bool 0x37 string wire kind 0x05 completion-only 0x41 no String-to-boolean conversion failure matches string-to-integer failure completion.
093-frida-write-testdatetime-string-type TestChildObject.TestDateTime string not_a_date 0x37 string wire kind 0x05 completion-only 0x41 no String-to-time conversion failure matches string-to-integer failure completion.
1 capture tag write_type value value_body status_body on_write_complete notes
2 089-frida-write-testint-wrong-type TestChildObject.TestInt string not_an_int 0x37 string wire kind 0x05 completion-only 0x41 no String-to-integer conversion failure is not surfaced by MXAccess OnWriteComplete.
3 090-frida-write-invalid-reference NoSuchObject_999.NoSuchAttr int 145 none after failed registration none no Write returns S_OK after invalid registration result, but no value-bearing write body is emitted.
4 091-frida-write-testint-double-type TestChildObject.TestInt double 1.25 0x37 double wire kind 0x04 completion-only 0x00 no Double-to-integer path appears accepted/coerced at NMX completion level.
5 092-frida-write-testbool-string-type TestChildObject.TestBool string not_bool 0x37 string wire kind 0x05 completion-only 0x41 no String-to-boolean conversion failure matches string-to-integer failure completion.
6 093-frida-write-testdatetime-string-type TestChildObject.TestDateTime string not_a_date 0x37 string wire kind 0x05 completion-only 0x41 no String-to-time conversion failure matches string-to-integer failure completion.
+8
View File
@@ -0,0 +1,8 @@
capture tag scenario method_result com_hook com_args_summary putrequest_size putrequest_value_offset putrequest_time_offset transferdata_size transferdata_value_offset transferdata_time_offset callback_size callback_value_offset callback_time_offset notes
036-frida-write-secured-test-int TestChildObject.TestInt write-secured 0x80004021 unsupported CLMXProxyServer.WriteSecured.variantA session=1 item=1 currentUser=1 verifier=0 vt=0x3 value=112 none none none none none none none none none Operate tag rejects WriteSecured before value-bearing PutRequest.
037-frida-write-secured2-test-int TestChildObject.TestInt write-secured2 E_INVALIDARG CLMXProxyServer.WriteSecured.variantB session=1 item=1 currentUser=1 verifier=0 vt=0x3 value=113 vt_date timestamp none none none none none none none none none Operate tag rejects WriteSecured2 before value-bearing PutRequest.
038-frida-write-secured-protectedvalue TestMachine_001.ProtectedValue write-secured 0x80004021 unsupported CLMXProxyServer.WriteSecured.variantA session=1 item=1 currentUser=1 verifier=0 vt=0xb value=true none none none none none none none none none SecuredWrite tag still rejects public WriteSecured before value-bearing PutRequest.
039-frida-write-secured-verified-protectedvalue1 TestMachine_001.ProtectedValue1 write-secured 0x80004021 unsupported CLMXProxyServer.WriteSecured.variantA session=1 item=1 currentUser=1 verifier=1 vt=0xb value=true none none none none none none none none none VerifiedWrite tag still rejects public WriteSecured before value-bearing PutRequest.
040-frida-write-normal-secured-protectedvalue TestMachine_001.ProtectedValue write success CLMXProxyServer.Write.variantA session=1 item=1 vt=0xb value=true user/security=2 37 18 none 83 64 none 85 84 75 Supported public route for SecuredWrite is normal Write with fourth argument 2.
041-frida-write-normal-verified-protectedvalue1 TestMachine_001.ProtectedValue1 write success CLMXProxyServer.Write.variantA session=1 item=1 vt=0xb value=true user/security=3 37 18 none 83 64 none 85 84 75 Supported public route for VerifiedWrite is normal Write with fourth argument 3.
042-frida-write2-test-int-timestamp TestChildObject.TestInt write2 success CLMXProxyServer.Write.variantB session=1 item=1 vt=0x3 value=114 vt_date timestamp user/security=1 40 18 24 86 64 70 88 84 75 Write2 embeds FILETIME at PutRequest offset 24 and callback offset 75.
1 capture tag scenario method_result com_hook com_args_summary putrequest_size putrequest_value_offset putrequest_time_offset transferdata_size transferdata_value_offset transferdata_time_offset callback_size callback_value_offset callback_time_offset notes
2 036-frida-write-secured-test-int TestChildObject.TestInt write-secured 0x80004021 unsupported CLMXProxyServer.WriteSecured.variantA session=1 item=1 currentUser=1 verifier=0 vt=0x3 value=112 none none none none none none none none none Operate tag rejects WriteSecured before value-bearing PutRequest.
3 037-frida-write-secured2-test-int TestChildObject.TestInt write-secured2 E_INVALIDARG CLMXProxyServer.WriteSecured.variantB session=1 item=1 currentUser=1 verifier=0 vt=0x3 value=113 vt_date timestamp none none none none none none none none none Operate tag rejects WriteSecured2 before value-bearing PutRequest.
4 038-frida-write-secured-protectedvalue TestMachine_001.ProtectedValue write-secured 0x80004021 unsupported CLMXProxyServer.WriteSecured.variantA session=1 item=1 currentUser=1 verifier=0 vt=0xb value=true none none none none none none none none none SecuredWrite tag still rejects public WriteSecured before value-bearing PutRequest.
5 039-frida-write-secured-verified-protectedvalue1 TestMachine_001.ProtectedValue1 write-secured 0x80004021 unsupported CLMXProxyServer.WriteSecured.variantA session=1 item=1 currentUser=1 verifier=1 vt=0xb value=true none none none none none none none none none VerifiedWrite tag still rejects public WriteSecured before value-bearing PutRequest.
6 040-frida-write-normal-secured-protectedvalue TestMachine_001.ProtectedValue write success CLMXProxyServer.Write.variantA session=1 item=1 vt=0xb value=true user/security=2 37 18 none 83 64 none 85 84 75 Supported public route for SecuredWrite is normal Write with fourth argument 2.
7 041-frida-write-normal-verified-protectedvalue1 TestMachine_001.ProtectedValue1 write success CLMXProxyServer.Write.variantA session=1 item=1 vt=0xb value=true user/security=3 37 18 none 83 64 none 85 84 75 Supported public route for VerifiedWrite is normal Write with fourth argument 3.
8 042-frida-write2-test-int-timestamp TestChildObject.TestInt write2 success CLMXProxyServer.Write.variantB session=1 item=1 vt=0x3 value=114 vt_date timestamp user/security=1 40 18 24 86 64 70 88 84 75 Write2 embeds FILETIME at PutRequest offset 24 and callback offset 75.