Tuesday, October 1, 2013

Simple CIL Opcode Execution in PowerShell using the DynamicMethod Class and Delegates

tl:dr version

It is possible to assemble .NET methods with CIL opcodes (i.e. .NET bytecode) in PowerShell in only a few lines of code using dynamic methods and delegates.



I’ll admit, I have a love/hate relationship with PowerShell. I love that it is the most powerful scripting language and shell but at the same time, I often find quirks in the language that consistently bother me. One such quirk is the fact that integers don’t wrap when they overflow. Rather, they saturate – they are cast into the next largest type that can accommodate them. To demonstrate what I mean, observe the following:


You’ll notice that [Int16]::MaxValue (i.e. 0x7FFF) understandably remains an Int16. However, rather than wrapping when adding one, it is upcast to an Int32. Admittedly, this is probably the behavior that most PowerShell users would desire. I, on the other hand wish I had the option to perform math on integers that wrapped. To solve this, I originally thought that I would have to write an addition function using complicated binary logic. I opted not to go that route and decided to assemble a function using raw CIL (common intermediate language) opcodes. What follows is a brief explanation of how to accomplish this task.


Common Intermediate Language Basics

CIL is the bytecode that describes .NET methods. A description of all the opcodes implemented by Microsoft can be found here. Every time you call a method in .NET, the runtime either interprets its opcodes or it executes the assembly language equivalent of those opcodes (as a result of the JIT process - just-in-time compilation). The calling convention for CIL is loosely related to how calls are made in X86 assembly – arguments are pushed onto a stack, a method is called, and a return value is returned to the caller.

Since we’re on the subject of addition, here are the CIL opcodes that would add two numbers of similar type together and would wrap in the case of an overflow:

IL_0000: Ldarg_0 // Loads the argument at index 0 onto the evaluation stack.
IL_0001: Ldarg_1 // Loads the argument at index 1 onto the evaluation stack.
IL_0002: Add // Adds two values and pushes the result onto the evaluation stack.
IL_0003: Ret // Returns from the current method, pushing a return value (if present) from the callee's evaluation stack onto the caller's evaluation stack.

Per Microsoft documentation, “integer addition wraps, rather than saturates” when using the Add instruction. This is the behavior I was after in the first place. Now let’s learn how to build a method in PowerShell that uses these opcodes.


Dynamic Methods

In the System.Reflection.Emit namespace, there is a DynamicMethod class that allows you to create methods without having to first go through the steps of creating an assembly and module. This is nice when you want a quick and dirty way to assemble and execute CIL opcodes. When creating a DynamicMethod object, you will need to provide the following arguments to its constructor:

1) The name of the method you want to create
2) The return type of the method
3) An array of types that will serve as the parameters

The following PowerShell command will satisfy those requirements for an addition function:

$MethodInfo = New-Object Reflection.Emit.DynamicMethod('UInt32Add', [UInt32], @([UInt32], [UInt32]))

Here, I am creating an empty method that will take two UInt32 variables as arguments and return a UInt32.

Next, I will actually implement the logic of the method my emitting the CIL opcodes into the method:

$ILGen = $MethodInfo.GetILGenerator()
$ILGen.Emit([Reflection.Emit.OpCodes]::Ldarg_0)
$ILGen.Emit([Reflection.Emit.OpCodes]::Ldarg_1)
$ILGen.Emit([Reflection.Emit.OpCodes]::Add)
$ILGen.Emit([Reflection.Emit.OpCodes]::Ret)

Now that the logic of the method is complete, I need to create a delegate from the $MethodInfo object. Before this can happen, I need to create a delegate in PowerShell that matches the method signature for the UInt32Add method. This can be accomplished by creating a generic Func delegate with the following convoluted syntax:

$Delegate = [Func``3[UInt32, UInt32, UInt32]]

The previous command states that I want to create a delegate for a function that accepts two UInt32 arguments and returns a UInt32. Note that the Func delegate wasn't introduced until .NET 3.5 which means that this technique will only work in PowerShell 3+. With that, we can now bind the method to the delegate:

$UInt32Add = $MethodInfo.CreateDelegate($Delegate)

And now, all we have to do is call the Invoke method to perform normal integer math that wraps upon an overflow:

$UInt32Add.Invoke([UInt32]::MaxValue, 2)

Here is the code in its entirety:


For additional information regarding the techniques I described, I encourage you to read the following articles:

Introduction to IL Assembly Language
Reflection Emit Dynamic Method Scenarios
How to: Define and Execute Dynamic Methods

3 comments:

  1. When i downloaded PowerSploit on GitHub it flag that kappfree.dll is a viruses. Looking at it more it seem there a whole folder called mimikatz-1.0 under folder Exfiltration seem like things to make a viruses work. Is this really safe and something you added to make this tool work?

    ReplyDelete
    Replies
    1. While those dlls are not necessary for the functionality of Invoke-Mimikatz, they were placed there in case the user of Invoke-Mimikatz didn't trust the embedded dll that was to be loaded in memory. In one regard, I sympathize with you that malicious dlls that aren't required don't necessarily need to be there. On the other hand, PowerSploit in and of itself is malicious and while malicious PowerShell scripts have yet to be flagged by AV, there's no reason they shouldn't be. A malicious PowerShell script should flag AV just like a malicious dll.

      I guess the point I'm making is that you should always exercise caution when downloading malicious tools. While I can tell you that I'm an honest person and would never intend to harm the users of PowerSploit, you should always validate that a malicious tool doesn't compromise your own machine.

      Personally, I'd like to see the mimikatz dlls dropped from PowerSploit but I'll confer with Joe Bialek (the author of Invoke-Mimikatz) first and see if he's okay with dropping them.

      Happy hacking,
      Matt

      Delete
  2. Thanks for the reply and I felt the same way the whole PowerSploit is or can be called malicious by a AV. But lucky the tool I want to work is PETools and I have removed everything but that module to feel more safe. What make me feel safe with PETools is that it 100% done in PowerShell script and I can read line by line and know it safe as is. With compiled binary I would never fully know what it doing. Thanks for your great tool as it is helping me find types of dlls (dotnet, C, C++ and somewhat what version of runtime they need). The one thing you said you were looking at was to display more info about dotnet. For now I call out to [System.Reflection.Assembly]::ReflectionOnlyLoadFrom($FileName) to get the runtime need for donet types. Hope what it returns is right 95% of time, :)

    ReplyDelete