Thank you for reaching out.
The absence of the command text (e.g., "ping google.com") in your IL disassembly is expected because of how PowerShell processes scripts. When you run powershell.exe -c "ping google.com", the command string is parsed by PowerShell and converted into an Abstract Syntax Tree (AST) before execution. This means:
- The script text is not embedded as a literal in the JIT-compiled IL for methods like
FuncCallInstruction.RunorActionCallInstruction.Run. - IL instructions such as
ldstronly represent hardcoded string literals inside the method body. Dynamic content like user-entered commands is passed as arguments at runtime, not compiled into IL.
Your IL dump shows dynamic invocation logic (callvirt, ldfld, Invoke) and DLR (Dynamic Language Runtime) operations, which handle execution dynamically rather than storing the script text in IL.
Why IL Parsing Alone Cannot Capture PowerShell Commands
The CLR Profiling API approach you implemented (GetILFunctionBody + scanning for 0x72 (ldstr)) works for static literals but fails for dynamic scenarios like PowerShell scripts. The command string is consumed by methods such as:
-
System.Management.Automation.PowerShell.AddScript(string script) -
System.Management.Automation.PowerShell.AddCommand(string command) -
System.Management.Automation.PowerShell.Invoke()
These methods receive the script text as arguments, not as IL constants. Therefore, ResolveStringToken will never return "ping google.com" because the token refers only to compile-time literals.
Workaround: Capture Arguments Instead of IL
To monitor PowerShell commands effectively, you need to inspect method arguments at runtime rather than relying on IL parsing. The CLR Profiling API provides a mechanism for this:
Enable Enter/Leave Callbacks
Use COR_PRF_MONITOR_ENTERLEAVE and implement FunctionEnter3WithInfo to intercept method calls.
Retrieve Argument Values
Use GetFunctionEnter3Info to access argument metadata and object IDs. For string arguments, extract the actual text using GetStringLayout2 or by dereferencing the object ID.
Target Specific Methods
Focus on PowerShell APIs where script text is passed:
-
PowerShell.AddScript-
PowerShell.AddCommand -
PowerShell.Invoke
-
This approach captures the raw command text before execution, similar to AMSI but without relying on AMSI.
By intercepting method entry and reading arguments, you capture the script text before execution, regardless of whether the script is inline (-c) or loaded from a file. This avoids parsing complex IL that does not contain the command text.
Please let us know if you require any further assistance, we’re happy to help.
If you found this information useful, kindly mark this as "Accept Answer".