PowerSploit/ReverseEngineering/Get-ILDisassembly.ps1

216 lines
7.7 KiB
PowerShell

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
}
}