Files
histsdk/scripts/frida/aahclientmanaged-system-boundary.js
dohertj2 c95824a65d Initial commit: managed .NET 10 AVEVA Historian SDK + reverse-engineering toolkit
Full read-only SDK (src/AVEVA.Historian.Client) implementing the CLAUDE.md required
surface against AVEVA Historian's binary WCF protocol — no native AVEVA runtime
dependency. All operations live-verified against a local Historian:

- ProbeAsync, ReadRawAsync, ReadAggregateAsync, ReadAtTimeAsync, ReadEventsAsync
- BrowseTagNamesAsync, GetTagMetadataAsync (17 native data-type codes mapped)
- GetConnectionStatusAsync, GetStoreForwardStatusAsync, GetSystemParameterAsync
- 108/108 unit + integration tests pass

Includes the reverse-engineering toolkit (tools/AVEVA.Historian.ReverseEngineering)
used to decode the protocol: WCF probes, IL inspection via dnlib, and IL-rewrite
instrumentation (instrument-wcf-{write,read}message etc.) plus the .NET Framework
trace harness (tools/AVEVA.Historian.NativeTraceHarness) for parity testing.

Sanitized handoff evidence under docs/reverse-engineering/. Native AVEVA binaries
(current/, aveva-install-x64/, aveva-install-x86/) are gitignored — fetch separately
from the AVEVA installer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 06:31:48 -04:00

864 lines
24 KiB
JavaScript

'use strict';
const maxPrefixBytes = 96;
const handles = new Map();
const sockets = new Map();
const pendingHooks = [];
const installedHooks = new Set();
let getPeerName = null;
function emit(kind, payload) {
payload.kind = kind;
payload.pid = Process.id;
payload.tid = Process.getCurrentThreadId();
payload.timestamp = new Date().toISOString();
console.log('FRIDA_SYS ' + JSON.stringify(payload));
}
function toHex(bytes) {
const parts = [];
for (let i = 0; i < bytes.length; i++) {
const text = bytes[i].toString(16);
parts.push(text.length === 1 ? '0' + text : text);
}
return parts.join('');
}
function asciiPreview(bytes) {
let text = '';
for (let i = 0; i < bytes.length; i++) {
const b = bytes[i];
text += b >= 32 && b <= 126 ? String.fromCharCode(b) : '.';
}
return text;
}
function payloadSummary(buffer, length, includePrefix) {
if (includePrefix === undefined) {
includePrefix = true;
}
const byteCount = Math.max(0, Math.min(length, maxPrefixBytes));
if (!includePrefix || buffer.isNull() || byteCount === 0) {
return { byteCount: length, prefixByteCount: 0, prefixHex: '', asciiPrefix: '' };
}
try {
const raw = buffer.readByteArray(byteCount);
const bytes = Array.from(new Uint8Array(raw));
return {
byteCount: length,
prefixByteCount: byteCount,
prefixHex: toHex(bytes),
asciiPrefix: asciiPreview(bytes)
};
} catch (e) {
return { byteCount: length, prefixByteCount: 0, prefixHex: '', asciiPrefix: '', error: String(e) };
}
}
function socketKey(socket) {
return socket.toString();
}
function readPortNetworkOrder(address) {
const hi = address.readU8();
const lo = address.add(1).readU8();
return (hi << 8) | lo;
}
function parseSockaddr(address) {
if (address.isNull()) {
return null;
}
try {
const family = address.readU16();
if (family === 2) {
return {
family: 'IPv4',
address: [address.add(4).readU8(), address.add(5).readU8(), address.add(6).readU8(), address.add(7).readU8()].join('.'),
port: readPortNetworkOrder(address.add(2))
};
}
if (family === 23) {
const parts = [];
for (let i = 0; i < 16; i += 2) {
parts.push(((address.add(8 + i).readU8() << 8) | address.add(8 + i + 1).readU8()).toString(16));
}
return { family: 'IPv6', address: parts.join(':'), port: readPortNetworkOrder(address.add(2)) };
}
return { family: String(family), address: null, port: null };
} catch (e) {
return { error: String(e) };
}
}
function ensurePeer(socket) {
const key = socketKey(socket);
if (sockets.has(key) || getPeerName === null) {
return;
}
try {
const address = Memory.alloc(128);
const length = Memory.alloc(4);
length.writeU32(128);
if (getPeerName(socket, address, length) === 0) {
const peer = parseSockaddr(address);
if (peer !== null) {
sockets.set(key, peer);
}
}
} catch (_) {
}
}
function readWsaBuffers(lpBuffers, count) {
const result = [];
const stride = Process.pointerSize === 8 ? 16 : 8;
const pointerOffset = Process.pointerSize === 8 ? 8 : 4;
for (let i = 0; i < count; i++) {
const item = lpBuffers.add(i * stride);
const length = item.readU32();
const buffer = item.add(pointerOffset).readPointer();
result.push(payloadSummary(buffer, length));
}
return result;
}
function readSecBufferDesc(secBufferDesc) {
if (secBufferDesc.isNull()) {
return null;
}
try {
const version = secBufferDesc.readU32();
const bufferCount = secBufferDesc.add(4).readU32();
const buffersPointer = secBufferDesc.add(Process.pointerSize === 8 ? 8 : 8).readPointer();
const buffers = [];
const stride = Process.pointerSize === 8 ? 16 : 12;
const pointerOffset = Process.pointerSize === 8 ? 8 : 8;
for (let i = 0; i < Math.min(bufferCount, 8); i++) {
const item = buffersPointer.add(i * stride);
const length = item.readU32();
const type = item.add(4).readU32();
const buffer = item.add(pointerOffset).readPointer();
buffers.push({
index: i,
type,
payload: payloadSummary(buffer, length)
});
}
return { version, bufferCount, buffers };
} catch (e) {
return { error: String(e) };
}
}
function shouldCaptureFilePayload(path) {
if (path === undefined || path === null) {
return false;
}
return path.indexOf('\\pipe\\') !== -1 || path.indexOf('\\Device\\Afd') !== -1;
}
function hookExport(moduleName, exportName, callbacks) {
const key = moduleName.toLowerCase() + '!' + exportName;
if (installedHooks.has(key)) {
return true;
}
try {
let module = null;
const targetModuleName = moduleName.toLowerCase();
for (const candidate of Process.enumerateModules()) {
if (candidate.name.toLowerCase() === targetModuleName) {
module = candidate;
break;
}
}
if (module === null) {
module = Process.getModuleByName(moduleName);
}
const address = module.getExportByName(exportName);
Interceptor.attach(address, callbacks);
installedHooks.add(key);
emit('hooked', { api: exportName, module: moduleName, address: address.toString() });
return true;
} catch (e) {
if (String(e).indexOf('unable to find module') !== -1) {
if (pendingHooks.filter(h => h.key === key).length === 0) {
pendingHooks.push({ key, moduleName, exportName, callbacks });
emit('hook-pending', { api: exportName, module: moduleName });
}
return false;
}
if (String(e).indexOf('unable to find export') !== -1) {
installedHooks.add(key);
emit('hook-missing-export', { api: exportName, module: moduleName, error: String(e) });
return false;
}
emit('hook-error', { api: exportName, module: moduleName, error: String(e) });
return false;
}
}
function installPendingHooks() {
for (const hook of pendingHooks.slice()) {
hookExport(hook.moduleName, hook.exportName, hook.callbacks);
}
}
function readUnicodeString(unicodeString) {
if (unicodeString.isNull()) {
return '';
}
try {
const length = unicodeString.readU16();
const bufferOffset = Process.pointerSize === 8 ? 8 : 4;
const buffer = unicodeString.add(bufferOffset).readPointer();
if (buffer.isNull() || length === 0) {
return '';
}
return buffer.readUtf16String(length / 2);
} catch (e) {
return '<unicode-read-error:' + String(e) + '>';
}
}
function readObjectAttributesName(objectAttributes) {
if (objectAttributes.isNull()) {
return '';
}
try {
const objectNameOffset = Process.pointerSize === 8 ? 16 : 8;
const unicodeString = objectAttributes.add(objectNameOffset).readPointer();
return readUnicodeString(unicodeString);
} catch (e) {
return '<object-attributes-read-error:' + String(e) + '>';
}
}
function rememberHandle(handle, path, api) {
if (!handle.equals(ptr('-1')) && !handle.isNull()) {
handles.set(handle.toString(), path || '<empty>');
emit('handle-open', { api, handle: handle.toString(), path: path || '<empty>' });
}
}
function forgetHandle(handle, api, retval) {
const key = handle.toString();
const path = handles.get(key);
if (path !== undefined) {
emit('handle-close', { api, handle: key, path, retval: retval.toString() });
handles.delete(key);
}
}
emit('startup', {
arch: Process.arch,
platform: Process.platform,
modules: Process.enumerateModules()
.filter(m => {
const lower = m.name.toLowerCase() + ' ' + m.path.toLowerCase();
return lower.indexOf('aah') !== -1 || lower.indexOf('historian') !== -1 || lower.indexOf('histsdk') !== -1;
})
.map(m => ({ name: m.name, base: m.base.toString(), size: m.size, path: m.path }))
});
try {
Process.attachModuleObserver({
onAdded(module) {
const lower = module.name.toLowerCase() + ' ' + module.path.toLowerCase();
if (lower.indexOf('aah') !== -1 || lower.indexOf('historian') !== -1 || lower.indexOf('histsdk') !== -1 || lower.indexOf('secur') !== -1 || lower.indexOf('netapi') !== -1) {
emit('module-added', { name: module.name, base: module.base.toString(), size: module.size, path: module.path });
}
installPendingHooks();
}
});
} catch (e) {
emit('module-observer-error', { error: String(e) });
}
const pendingTimer = setInterval(() => {
installPendingHooks();
if (pendingHooks.filter(h => !installedHooks.has(h.key)).length === 0) {
clearInterval(pendingTimer);
}
}, 100);
hookExport('kernel32.dll', 'CreateFileW', {
onEnter(args) {
this.path = args[0].isNull() ? '' : args[0].readUtf16String();
},
onLeave(retval) {
rememberHandle(retval, this.path, 'CreateFileW');
}
});
hookExport('kernel32.dll', 'CloseHandle', {
onEnter(args) {
this.handle = args[0];
},
onLeave(retval) {
forgetHandle(this.handle, 'CloseHandle', retval);
}
});
hookExport('kernel32.dll', 'ReadFile', {
onEnter(args) {
this.handle = args[0];
this.path = handles.get(this.handle.toString());
this.buffer = args[1];
this.requested = args[2].toInt32();
this.readPointer = args[3];
},
onLeave(retval) {
if (this.path === undefined) {
return;
}
let read = null;
try {
read = this.readPointer.isNull() ? null : this.readPointer.readU32();
} catch (_) {
read = null;
}
emit('read-file', {
api: 'ReadFile',
handle: this.handle.toString(),
path: this.path,
requestedBytes: this.requested,
resultBytes: read,
retval: retval.toInt32(),
payload: payloadSummary(this.buffer, Math.max(0, read === null ? 0 : read), shouldCaptureFilePayload(this.path))
});
}
});
hookExport('kernel32.dll', 'WriteFile', {
onEnter(args) {
this.handle = args[0];
this.path = handles.get(this.handle.toString());
this.buffer = args[1];
this.requested = args[2].toInt32();
this.writtenPointer = args[3];
},
onLeave(retval) {
if (this.path === undefined) {
return;
}
let written = null;
try {
written = this.writtenPointer.isNull() ? null : this.writtenPointer.readU32();
} catch (_) {
written = null;
}
emit('write-file', {
api: 'WriteFile',
handle: this.handle.toString(),
path: this.path,
requestedBytes: this.requested,
resultBytes: written,
retval: retval.toInt32(),
payload: payloadSummary(this.buffer, Math.max(0, written === null ? this.requested : written), shouldCaptureFilePayload(this.path))
});
}
});
hookExport('ntdll.dll', 'NtCreateFile', {
onEnter(args) {
this.fileHandlePointer = args[0];
this.path = readObjectAttributesName(args[2]);
},
onLeave(retval) {
if (retval.toInt32() < 0) {
return;
}
try {
rememberHandle(this.fileHandlePointer.readPointer(), this.path, 'NtCreateFile');
} catch (e) {
emit('handle-open-error', { api: 'NtCreateFile', path: this.path, error: String(e) });
}
}
});
hookExport('ntdll.dll', 'NtReadFile', {
onEnter(args) {
this.handle = args[0];
this.path = handles.get(this.handle.toString());
this.ioStatusBlock = args[4];
this.buffer = args[5];
this.requested = args[6].toInt32();
},
onLeave(retval) {
if (this.path === undefined) {
return;
}
let transferred = null;
try {
transferred = this.ioStatusBlock.add(Process.pointerSize).readPointer().toUInt32();
} catch (_) {
transferred = retval.toInt32() >= 0 ? this.requested : 0;
}
emit('nt-read-file', {
api: 'NtReadFile',
handle: this.handle.toString(),
path: this.path,
requestedBytes: this.requested,
resultBytes: transferred,
ntstatus: retval.toString(),
payload: payloadSummary(this.buffer, Math.max(0, transferred || 0), shouldCaptureFilePayload(this.path))
});
}
});
hookExport('ntdll.dll', 'NtWriteFile', {
onEnter(args) {
this.handle = args[0];
this.path = handles.get(this.handle.toString());
this.ioStatusBlock = args[4];
this.buffer = args[5];
this.requested = args[6].toInt32();
},
onLeave(retval) {
if (this.path === undefined) {
return;
}
let transferred = null;
try {
transferred = this.ioStatusBlock.add(Process.pointerSize).readPointer().toUInt32();
} catch (_) {
transferred = this.requested;
}
emit('nt-write-file', {
api: 'NtWriteFile',
handle: this.handle.toString(),
path: this.path,
requestedBytes: this.requested,
resultBytes: transferred,
ntstatus: retval.toString(),
payload: payloadSummary(this.buffer, Math.max(0, transferred || this.requested), shouldCaptureFilePayload(this.path))
});
}
});
hookExport('ntdll.dll', 'NtDeviceIoControlFile', {
onEnter(args) {
this.handle = args[0];
this.ioStatusBlock = args[4];
this.ioControlCode = args[5].toUInt32();
this.inputBuffer = args[6];
this.inputLength = args[7].toInt32();
this.outputBuffer = args[8];
this.outputLength = args[9].toInt32();
},
onLeave(retval) {
let transferred = null;
try {
transferred = this.ioStatusBlock.add(Process.pointerSize).readPointer().toUInt32();
} catch (_) {
transferred = null;
}
emit('nt-device-io-control', {
api: 'NtDeviceIoControlFile',
handle: this.handle.toString(),
path: handles.get(this.handle.toString()),
ioControlCode: '0x' + this.ioControlCode.toString(16),
inputBytes: this.inputLength,
outputBytes: this.outputLength,
transferredBytes: transferred,
ntstatus: retval.toString(),
input: payloadSummary(this.inputBuffer, Math.max(0, this.inputLength), shouldCaptureFilePayload(handles.get(this.handle.toString()))),
output: payloadSummary(this.outputBuffer, retval.toInt32() >= 0 ? Math.max(0, transferred === null ? this.outputLength : transferred) : 0, shouldCaptureFilePayload(handles.get(this.handle.toString())))
});
}
});
hookExport('ws2_32.dll', 'GetAddrInfoW', {
onEnter(args) {
this.nodeName = args[0].isNull() ? '' : args[0].readUtf16String();
this.serviceName = args[1].isNull() ? '' : args[1].readUtf16String();
},
onLeave(retval) {
emit('dns', { api: 'GetAddrInfoW', nodeName: this.nodeName, serviceName: this.serviceName, retval: retval.toInt32() });
}
});
try {
const ws2 = Process.getModuleByName('ws2_32.dll');
getPeerName = new NativeFunction(ws2.getExportByName('getpeername'), 'int', ['pointer', 'pointer', 'pointer']);
} catch (e) {
emit('getpeername-error', { error: String(e) });
}
hookExport('ws2_32.dll', 'connect', {
onEnter(args) {
this.socket = args[0];
this.peer = parseSockaddr(args[1]);
},
onLeave(retval) {
if (this.peer !== null) {
sockets.set(socketKey(this.socket), this.peer);
}
emit('socket-connect', { api: 'connect', socket: socketKey(this.socket), peer: this.peer, retval: retval.toInt32() });
}
});
hookExport('ws2_32.dll', 'WSAConnect', {
onEnter(args) {
this.socket = args[0];
this.peer = parseSockaddr(args[1]);
},
onLeave(retval) {
if (this.peer !== null) {
sockets.set(socketKey(this.socket), this.peer);
}
emit('socket-connect', { api: 'WSAConnect', socket: socketKey(this.socket), peer: this.peer, retval: retval.toInt32() });
}
});
hookExport('ws2_32.dll', 'send', {
onEnter(args) {
this.socket = args[0];
this.buffer = args[1];
this.length = args[2].toInt32();
},
onLeave(retval) {
ensurePeer(this.socket);
emit('socket-send', {
api: 'send',
socket: socketKey(this.socket),
peer: sockets.get(socketKey(this.socket)),
requestedBytes: this.length,
resultBytes: retval.toInt32(),
payload: payloadSummary(this.buffer, Math.max(0, Math.min(this.length, retval.toInt32())))
});
}
});
hookExport('ws2_32.dll', 'recv', {
onEnter(args) {
this.socket = args[0];
this.buffer = args[1];
},
onLeave(retval) {
const length = retval.toInt32();
if (length <= 0) {
return;
}
ensurePeer(this.socket);
emit('socket-recv', {
api: 'recv',
socket: socketKey(this.socket),
peer: sockets.get(socketKey(this.socket)),
resultBytes: length,
payload: payloadSummary(this.buffer, length)
});
}
});
hookExport('ws2_32.dll', 'WSASend', {
onEnter(args) {
this.socket = args[0];
this.buffers = args[1];
this.count = args[2].toInt32();
this.sentPointer = args[3];
this.summaries = readWsaBuffers(this.buffers, this.count);
},
onLeave(retval) {
let sent = null;
try {
sent = this.sentPointer.isNull() ? null : this.sentPointer.readU32();
} catch (_) {
sent = null;
}
ensurePeer(this.socket);
emit('socket-send', {
api: 'WSASend',
socket: socketKey(this.socket),
peer: sockets.get(socketKey(this.socket)),
retval: retval.toInt32(),
resultBytes: sent,
buffers: this.summaries
});
}
});
hookExport('ws2_32.dll', 'WSARecv', {
onEnter(args) {
this.socket = args[0];
this.buffers = args[1];
this.count = args[2].toInt32();
this.receivedPointer = args[3];
},
onLeave(retval) {
let received = null;
try {
received = this.receivedPointer.isNull() ? null : this.receivedPointer.readU32();
} catch (_) {
received = null;
}
ensurePeer(this.socket);
emit('socket-recv', {
api: 'WSARecv',
socket: socketKey(this.socket),
peer: sockets.get(socketKey(this.socket)),
retval: retval.toInt32(),
resultBytes: received,
buffers: readWsaBuffers(this.buffers, this.count)
});
}
});
hookExport('ws2_32.dll', 'WSAIoctl', {
onEnter(args) {
this.socket = args[0];
this.controlCode = args[1].toUInt32();
this.inputBuffer = args[2];
this.inputLength = args[3].toInt32();
this.outputBuffer = args[4];
this.outputLength = args[5].toInt32();
this.bytesReturnedPointer = args[6];
},
onLeave(retval) {
let bytesReturned = null;
try {
bytesReturned = this.bytesReturnedPointer.isNull() ? null : this.bytesReturnedPointer.readU32();
} catch (_) {
bytesReturned = null;
}
ensurePeer(this.socket);
emit('socket-ioctl', {
api: 'WSAIoctl',
socket: socketKey(this.socket),
peer: sockets.get(socketKey(this.socket)),
controlCode: '0x' + this.controlCode.toString(16),
inputBytes: this.inputLength,
outputBytes: this.outputLength,
bytesReturned,
retval: retval.toInt32(),
input: payloadSummary(this.inputBuffer, Math.max(0, this.inputLength)),
output: payloadSummary(this.outputBuffer, Math.max(0, bytesReturned === null ? 0 : bytesReturned))
});
}
});
hookExport('mswsock.dll', 'ConnectEx', {
onEnter(args) {
this.socket = args[0];
this.peer = parseSockaddr(args[1]);
this.sendBuffer = args[3];
this.sendBytes = args[4].toInt32();
},
onLeave(retval) {
if (this.peer !== null) {
sockets.set(socketKey(this.socket), this.peer);
}
emit('socket-connect', {
api: 'ConnectEx',
socket: socketKey(this.socket),
peer: this.peer,
retval: retval.toInt32(),
initialPayload: payloadSummary(this.sendBuffer, Math.max(0, this.sendBytes))
});
}
});
hookExport('mswsock.dll', 'TransmitPackets', {
onEnter(args) {
this.socket = args[0];
this.packetArray = args[1];
this.elementCount = args[2].toInt32();
this.sendSize = args[3].toInt32();
},
onLeave(retval) {
ensurePeer(this.socket);
emit('socket-send', {
api: 'TransmitPackets',
socket: socketKey(this.socket),
peer: sockets.get(socketKey(this.socket)),
elementCount: this.elementCount,
sendSize: this.sendSize,
retval: retval.toInt32()
});
}
});
hookExport('secur32.dll', 'AcquireCredentialsHandleW', {
onEnter(args) {
this.principal = args[0].isNull() ? '' : args[0].readUtf16String();
this.packageName = args[1].isNull() ? '' : args[1].readUtf16String();
this.credentialUse = args[2].toUInt32();
},
onLeave(retval) {
emit('secur32', {
api: 'AcquireCredentialsHandleW',
packageName: this.packageName,
principalPresent: this.principal.length > 0,
credentialUse: this.credentialUse,
status: retval.toString()
});
}
});
hookExport('sspicli.dll', 'AcquireCredentialsHandleW', {
onEnter(args) {
this.principal = args[0].isNull() ? '' : args[0].readUtf16String();
this.packageName = args[1].isNull() ? '' : args[1].readUtf16String();
this.credentialUse = args[2].toUInt32();
},
onLeave(retval) {
emit('secur32', {
api: 'AcquireCredentialsHandleW',
module: 'sspicli.dll',
packageName: this.packageName,
principalPresent: this.principal.length > 0,
credentialUse: this.credentialUse,
status: retval.toString()
});
}
});
hookExport('secur32.dll', 'InitializeSecurityContextW', {
onEnter(args) {
this.target = args[2].isNull() ? '' : args[2].readUtf16String();
this.requestFlags = args[3].toUInt32();
this.inputToken = readSecBufferDesc(args[6]);
this.outputTokenPointer = args[9];
},
onLeave(retval) {
emit('secur32', {
api: 'InitializeSecurityContextW',
target: this.target,
requestFlags: this.requestFlags,
inputToken: this.inputToken,
outputToken: readSecBufferDesc(this.outputTokenPointer),
status: retval.toString()
});
}
});
hookExport('sspicli.dll', 'InitializeSecurityContextW', {
onEnter(args) {
this.target = args[2].isNull() ? '' : args[2].readUtf16String();
this.requestFlags = args[3].toUInt32();
this.inputToken = readSecBufferDesc(args[6]);
this.outputTokenPointer = args[9];
},
onLeave(retval) {
emit('secur32', {
api: 'InitializeSecurityContextW',
module: 'sspicli.dll',
target: this.target,
requestFlags: this.requestFlags,
inputToken: this.inputToken,
outputToken: readSecBufferDesc(this.outputTokenPointer),
status: retval.toString()
});
}
});
hookExport('secur32.dll', 'AcceptSecurityContext', {
onEnter(args) {
this.requestFlags = args[3].toUInt32();
},
onLeave(retval) {
emit('secur32', { api: 'AcceptSecurityContext', requestFlags: this.requestFlags, status: retval.toString() });
}
});
hookExport('sspicli.dll', 'AcceptSecurityContext', {
onEnter(args) {
this.requestFlags = args[3].toUInt32();
},
onLeave(retval) {
emit('secur32', { api: 'AcceptSecurityContext', module: 'sspicli.dll', requestFlags: this.requestFlags, status: retval.toString() });
}
});
hookExport('secur32.dll', 'QuerySecurityContextToken', {
onLeave(retval) {
emit('secur32', { api: 'QuerySecurityContextToken', status: retval.toString() });
}
});
hookExport('sspicli.dll', 'QuerySecurityContextToken', {
onLeave(retval) {
emit('secur32', { api: 'QuerySecurityContextToken', module: 'sspicli.dll', status: retval.toString() });
}
});
hookExport('crypt32.dll', 'CryptBinaryToStringW', {
onEnter(args) {
this.inputBytes = args[1].toUInt32();
this.flags = args[2].toUInt32();
this.outputLengthPointer = args[4];
},
onLeave(retval) {
let outputChars = null;
try {
outputChars = this.outputLengthPointer.isNull() ? null : this.outputLengthPointer.readU32();
} catch (_) {
outputChars = null;
}
emit('crypt32', { api: 'CryptBinaryToStringW', inputBytes: this.inputBytes, flags: this.flags, outputChars, retval: retval.toInt32() });
}
});
hookExport('crypt32.dll', 'CryptStringToBinaryW', {
onEnter(args) {
this.inputChars = args[1].toUInt32();
this.flags = args[2].toUInt32();
this.outputLengthPointer = args[4];
},
onLeave(retval) {
let outputBytes = null;
try {
outputBytes = this.outputLengthPointer.isNull() ? null : this.outputLengthPointer.readU32();
} catch (_) {
outputBytes = null;
}
emit('crypt32', { api: 'CryptStringToBinaryW', inputChars: this.inputChars, flags: this.flags, outputBytes, retval: retval.toInt32() });
}
});
hookExport('netapi32.dll', 'NetUserGetLocalGroups', {
onEnter(args) {
this.server = args[0].isNull() ? '' : args[0].readUtf16String();
this.user = args[1].isNull() ? '' : args[1].readUtf16String();
},
onLeave(retval) {
emit('netapi', { api: 'NetUserGetLocalGroups', server: this.server, user: this.user, status: retval.toString() });
}
});