'use strict'; const moduleName = 'aahClient.dll'; const maxDumpBytes = 256; const interestingExportFragments = [ 'mdas_OpenConnection', 'mdas_CloseConnection', 'mdas_StartDataRetrievalQuery', 'mdas_GetNextDataQueryResult', 'mdas_StartEventDataRetrievalQuery', 'mdas_GetNextEventDataQueryResult', 'mdas_StartBlockRetrievalQuery', 'mdas_GetNextBlockQueryResult', 'mdas_EndQuery' ]; let hooksInstalled = false; function emit(kind, payload) { payload.kind = kind; payload.pid = Process.id; payload.tid = Process.getCurrentThreadId(); payload.timestamp = new Date().toISOString(); console.log('FRIDA_EVENT ' + 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 utf16Preview(bytes) { let text = ''; const count = Math.min(bytes.length - (bytes.length % 2), 128); for (let i = 0; i < count; i += 2) { const code = bytes[i] | (bytes[i + 1] << 8); text += code >= 32 && code <= 126 ? String.fromCharCode(code) : '.'; } return text; } function isReadablePointer(value) { if (value.isNull() || value.compare(ptr('0x10000')) < 0) { return false; } const range = Process.findRangeByAddress(value); return range !== null && range.protection.indexOf('r') !== -1; } function dumpPointer(value) { const range = Process.findRangeByAddress(value); const available = range === null ? 0 : Math.min(maxDumpBytes, range.base.add(range.size).sub(value).toNumber()); if (available <= 0) { return null; } const raw = Memory.readByteArray(value, available); if (raw === null) { return null; } const bytes = Array.from(new Uint8Array(raw)); return { address: value.toString(), rangeBase: range.base.toString(), rangeSize: range.size, protection: range.protection, byteCount: bytes.length, hexPrefix: toHex(bytes.slice(0, Math.min(bytes.length, 128))), asciiPrefix: asciiPreview(bytes.slice(0, Math.min(bytes.length, 128))), utf16Prefix: utf16Preview(bytes.slice(0, Math.min(bytes.length, 256))) }; } function inspectArgs(args, count) { const inspected = []; for (let i = 0; i < count; i++) { const value = args[i]; const item = { index: i, value: value.toString() }; if (isReadablePointer(value)) { try { item.memory = dumpPointer(value); } catch (e) { item.memoryError = String(e); } } inspected.push(item); } return inspected; } function isInterestingExport(name) { for (const fragment of interestingExportFragments) { if (name.indexOf(fragment) !== -1) { return true; } } return false; } function installHooks() { if (hooksInstalled) { return true; } let module = null; try { module = Process.getModuleByName(moduleName); } catch (e) { return false; } hooksInstalled = true; const exports = module.enumerateExports().filter(e => e.type === 'function' && isInterestingExport(e.name)); emit('module-loaded', { module: module.name, base: module.base.toString(), size: module.size, path: module.path, exportCount: exports.length, exports: exports.map(e => ({ name: e.name, address: e.address.toString() })) }); for (const exported of exports) { try { Interceptor.attach(exported.address, { onEnter(args) { this.name = exported.name; this.address = exported.address.toString(); this.argsSnapshot = inspectArgs(args, 18); emit('enter', { function: this.name, address: this.address, args: this.argsSnapshot }); }, onLeave(retval) { emit('leave', { function: this.name, address: this.address, retval: retval.toString(), args: this.argsSnapshot }); } }); emit('hooked', { function: exported.name, address: exported.address.toString() }); } catch (e) { emit('hook-error', { function: exported.name, address: exported.address.toString(), error: String(e) }); } } return true; } function isInterestingModule(module) { const name = module.name.toLowerCase(); const path = module.path.toLowerCase(); return name.indexOf('aah') !== -1 || path.indexOf('histsdk') !== -1 || path.indexOf('aveva') !== -1; } emit('startup', { arch: Process.arch, platform: Process.platform, modules: Process.enumerateModules() .filter(isInterestingModule) .map(m => ({ name: m.name, base: m.base.toString(), size: m.size, path: m.path })) }); try { Process.attachModuleObserver({ onAdded(module) { if (isInterestingModule(module)) { emit('module-added', { name: module.name, base: module.base.toString(), size: module.size, path: module.path }); } installHooks(); } }); } catch (e) { emit('module-observer-error', { error: String(e) }); } if (!installHooks()) { const timer = setInterval(() => { if (installHooks()) { clearInterval(timer); } }, 50); }