ReverseEngineering/Get-ILDisassembly.ps1

function Get-ILDisassembly
{
<#
.SYNOPSIS
 
A MSIL (Microsoft Intermediate Language) disassembler.
 
PowerSploit Function: Get-ILDisassembly
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
 
.DESCRIPTION
 
Get-ILDisassembly disassembles a raw MSIL byte array passed in from a MethodInfo object in a manner similar to that of Ildasm.
 
The majority of this code was simply translated from C# (with permission) from a code example taken from: "C# 4.0 in a Nutshell", Copyright 2010, Joseph Albahari and Ben Albahari, pg. 728-733
 
.PARAMETER MethodInfo
 
A MethodInfo object that describes the implementation of the method and contains the IL for the method.
 
.EXAMPLE
 
C:\PS> [Int].GetMethod('Parse', [String]) | Get-ILDisassembly | Format-Table Position, Instruction, Operand -AutoSize
 
Position Instruction Operand
-------- ----------- -------
IL_0000 ldarg.0
IL_0001 ldc.i4.7
IL_0002 call System.Globalization.NumberFormatInfo.get_CurrentInfo
IL_0007 call System.Number.ParseInt32
IL_000C ret
 
Description
-----------
Disassembles the System.Int32.Parse(String) method
 
.EXAMPLE
 
C:\PS> $MethodInfo = [Array].GetMethod('BinarySearch', [Type[]]([Array], [Object]))
C:\PS> Get-ILDisassembly $MethodInfo | Format-Table Position, Instruction, Operand -AutoSize
 
Position Instruction Operand
-------- ----------- -------
IL_0000 ldarg.0
IL_0001 brtrue.s IL_000E
IL_0003 ldstr 'array'
IL_0008 newobj System.ArgumentNullException..ctor
IL_000D throw
IL_000E ldarg.0
IL_000F ldc.i4.0
IL_0010 callvirt System.Array.GetLowerBound
IL_0015 stloc.0
IL_0016 ldarg.0
IL_0017 ldloc.0
IL_0018 ldarg.0
IL_0019 callvirt System.Array.get_Length
IL_001E ldarg.1
IL_001F ldnull
IL_0020 call System.Array.BinarySearch
IL_0025 ret
 
Description
-----------
Disassembles the System.Array.BinarySearch(Array, Object) method
 
.INPUTS
 
System.Reflection.MethodInfo, System.Reflection.ConstructorInfo
 
A method or constructor description containing the raw IL bytecodes.
 
.OUTPUTS
 
System.Object
 
Returns a custom object consisting of a position, instruction, and opcode parameter.
  
.LINK
 
http://www.exploit-monday.com
http://www.albahari.com/nutshell/cs4ch18.aspx
http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.aspx
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf
#>


    Param (
        [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
        [ValidateScript({$_ -is [Reflection.MethodInfo] -or $_ -is [Reflection.ConstructorInfo]})]
        [Object]
        $MethodInfo
    )

    if (!($MethodInfo.GetMethodBody())) {
        return
    }
    
    $IL = $MethodInfo.GetMethodBody().GetILAsByteArray()
    $MethodModule = $MethodInfo.DeclaringType.Module

    $OpCodeTable = @{}

    # Fill OpCodeTable with every OpCode so that it can be referenced by numeric byte value
    [System.Reflection.Emit.OpCodes].GetMembers() |
        ForEach-Object {
            try {
                $OpCode = $_.GetValue($null)
                $OpCodeTable[[Int16] $OpCode.Value] = $OpCode
            } catch {}
        }

    $Position = 0

    # Disassemble every instruction until the end of the IL bytecode array is reached
    while ($Position -lt $IL.Length) {

        # Get current instruction position
        $InstructionPostion = "IL_{0}" -f ($Position.ToString('X4'))

        if ($IL[$Position] -eq 0xFE) {
            # You are dealing with a two-byte opcode in this case
            $Op = $OpCodeTable[[Int16] ([BitConverter]::ToInt16($IL[($Position+1)..$Position], 0))]
            $Position++
        } else {
            # Otherwise, it's a one-byte opcode
            $Op = $OpCodeTable[[Int16] $IL[$Position]]
        }
        
        $Position++
        
        $Type = $Op.OperandType
        $Operand = $null
        $OpInt = $null
        
        if ($Type -eq 'InlineNone') {
            $OperandLength = 0
        } elseif (($Type -eq 'ShortInlineBrTarget') -or ($Type -eq 'ShortInlineI') -or ($Type -eq 'ShortInlineVar')) {
            $OperandLength = 1
            
            if ($Type -eq 'ShortInlineBrTarget') { # Short relative jump instruction
                # [SByte]::Parse was used because PowerShell doesn't handle signed bytes well
                $Target = $Position + ([SByte]::Parse($IL[$Position].ToString('X2'), 'AllowHexSpecifier')) + 1
                $Operand = "IL_{0}" -f ($Target.ToString('X4'))
            }
        } elseif ($Type -eq 'InlineVar') {
            $OperandLength = 2
        } elseif (($Type -eq 'InlineI8') -or (($Type -eq 'InlineR'))) {
            $OperandLength = 8
        } elseif ($Type -eq 'InlineSwitch') {
            # This is the only operand type with a variable number of operands
            $TargetCount = [BitConverter]::ToInt32($IL, $Position)
            $OperandLength = 4 * ($TargetCount + 1)
            $Targets = New-Object String[]($TargetCount)
            
            foreach ($i in 0..($TargetCount - 1)) {
                # Get all switch jump targets
                $Target = [BitConverter]::ToInt32($IL, ($Position + ($i + 1) * 4))
                $Targets[$i] = "IL_{0}" -f (($Position + $Target + $OperandLength).ToString('X4'))
            }
            
            $Operand = "({0})" -f ($Targets -join ',')
        } else {
            $OperandLength = 4
            $Operand = $null
            
            $OpInt = [BitConverter]::ToInt32($IL, $Position)
            
            if (($Type -eq 'InlineTok') -or ($Type -eq 'InlineMethod') -or ($Type -eq 'InlineField') -or ($Type -eq 'InlineType')) {
                # Resolve all operands with metadata tokens
                Write-Verbose "OpCode Metadata for member: $OpInt"
                try { $MemberInfo = $MethodModule.ResolveMember($OpInt) } catch { $Operand = $null }
                if (!$MemberInfo) { $Operand = $null }
                
                # Retrieve the actual name of the class and method
                if ($MemberInfo.ReflectedType) {
                    $Operand = "{0}.{1}" -f ($MemberInfo.ReflectedType.Fullname), ($MemberInfo.Name)
                } elseif ($MemberInfo -is [Type]) {
                    $Operand = $MemberInfo.GetType().FullName
                } else {
                    $Operand = $MemberInfo.Name
                }
            } elseif ($Type -eq 'InlineString') {
                # Retrieve the referenced string
                $Operand = "`'{0}`'" -f ($MethodModule.ResolveString($OpInt))
            } elseif ($Type -eq 'InlineBrTarget') {
                $Operand = "IL_{0}" -f (($Position + $OpInt + 4).ToString('X4'))
            } else {
                $Operand = $null
            }
        }
        
        if (($OperandLength -gt 0) -and ($OperandLength -ne 4) -and ($Type -ne 'InlineSwitch') -and ($Type -ne 'ShortInlineBrTarget')) {
            # Simply print the hex for all operands with immediate values
            $Operand = "0x{0}" -f (($IL[($Position+$OperandLength-1)..$Position] | ForEach-Object { $_.ToString('X2') }) -join '')
        }
        
        $Instruction = @{
            Position = $InstructionPostion
            Instruction = $Op
            Operand = $Operand
            MetadataToken = $OpInt
        }
        
        # Return a custom object containing a position, instruction, and fully-qualified operand
        $InstructionObject = New-Object PSObject -Property $Instruction
        $InstructionObject.PSObject.TypeNames.Insert(0, 'IL_INSTRUCTION')
        
        $InstructionObject

        # Adjust the position in the opcode array accordingly
        $Position += $OperandLength
    }
}