Frida is a dynamic code instrumentation toolkit. Frida allows you to write tweaks in JavaScript:
- to hook a functions invocation
- to show and modify incoming arguments to functions
- to print and modify function returns
- to inject custom code before and after a functions invocation
This is used to monitor how AMSI reacts to certain VBA, PowerShell, JScript etc commands.
frida-trace generates JavaScript templates which can then be modified. For example, after handlers\AmsiScanBuffer.js has been generated, you can open and modify it using the below code (AmsiScanBufferVebose.js) for a for more verbose output.
Steps:
- Find PID of PowerShell process to hook using sysinternals etc.
- Run ./tracer.py as below specifying the PID:
- Type string or test command and check output. A successful string returns '1' a malicious string returns '32768'.
AmsiScanBufferVerbose.js:
/*
* Auto-generated by Frida. Please modify to match the signature of AmsiScanBuffer.
* This stub is currently auto-generated from manpages when available.
*
* For full API reference, see: https://frida.re/docs/javascript-api/
*/
{
/**
* Called synchronously when about to call AmsiScanBuffer.
*
* @this {object} - Object allowing you to store state for use in onLeave.
* @param {function} log - Call this function with a string to be presented to the user.
* @param {array} args - Function arguments represented as an array of NativePointer objects.
* For example use args[0].readUtf8String() if the first argument is a pointer to a C string encoded as UTF-8.
* It is also possible to modify arguments by assigning a NativePointer object to an element of this array.
* @param {object} state - Object allowing you to keep state across function calls.
* Only one JavaScript function will execute at a time, so do not worry about race-conditions.
* However, do not use this to store function arguments across onEnter/onLeave, but instead
* use "this" which is an object for keeping state local to an invocation.
*/
onEnter: function(log, args, state) {
log('AmsiScanBuffer()');
log('|- amsiContext: ' + args[0]);
log('|- buffer: ' + Memory.readUtf16String(args[1]));
log('|- length: ' + args[2]);
log('|- contentName ' + args[3]);
log('|- amsiSession ' + args[4]);
log('|- result ' + args[5] + "\n");
this.resultPointer = args[5];
},
/**
* Called synchronously when about to return from AmsiScanBuffer.
*
* See onEnter for details.
*
* @this {object} - Object allowing you to access state stored in onEnter.
* @param {function} log - Call this function with a string to be presented to the user.
* @param {NativePointer} retval - Return value represented as a NativePointer object.
* @param {object} state - Object allowing you to keep state across function calls.
*/
onLeave: function(log, retval, state) {
log('[*] AmsiScanBuffer() Exit');
resultPointer = this.resultPointer;
log('|- Result value is: ' + Memory.readUShort(resultPointer) + "\n");
}
}