I was experimenting with idea of decompiling a PowerShell cmdlet using reflection.
While I got some basics seem to work, how do I resolve the value in OperandField, for example ldfld cmd in this case if decompiled correctly may look like this:
ldfld string[] Microsoft.PowerShell.Commands.GetAclCommand::path
Where as currently I am just displaying as "ldfld"
This is parsed in this section of code:
{ $_ -eq "InlineBrTarget" -or
$_ -eq "InlineField" -or
$_ -eq "InlineI" -or
$_ -eq "InlineSig" -or
$_ -eq "InlineString" -or
$_ -eq "InlineTok" -or
$_ -eq "InlineType" -or
$_ -eq "ShortInlineR" }
{
$count = [BitConverter]::ToInt32($Ilbytes,$offset+1)
$offset += 4
}
The full script here:
# command to decompile
$cmd = "Get-Acl"
$cmdInfo = Get-Command -CommandType Cmdlet -Name $cmd
$methods = $cmdInfo.ImplementingType.UnderlyingSystemType.GetMethods()
$opCodeDictionary = @{}
ForEach ($field in [System.Reflection.Emit.OpCodes].GetFields())
{
$fieldValue = $field.GetValue($null)
$opCodeDictionary.Add($fieldValue.Value,$fieldValue)
}
ForEach ($method in $Methods)
{
Write-Host ".method " -NoNewline
if ($method.IsPublic)
{
Write-Host "public " -NoNewline
}
if ($method.IsPrivate)
{
Write-Host "private " -NoNewline
}
if ($method.IsHideBySig)
{
Write-Host "hidebysig " -NoNewline
}
if ($method.IsSpecialName)
{
Write-Host "specialname " -NoNewline
}
if ($method.GetParameters() -ne $null)
{
Write-Host $method.GetParameters()[0]
}
else
{
"$($method.ReturnType.Name) $($method.Name)"
}
Write-Host "{"
$methodBody = $method.GetMethodBody()
if ($methodBody -ne $null)
{
$ilBytes = $methodBody.GetILAsByteArray()
$offset = 0
while ($offset -lt $ilBytes.Length)
{
Write-Host ("IL_{0:x4}: " -f $offset) -NoNewline
[System.Int16]$code = $ilBytes[$offset++];
if ($code -eq 0xfe)
{
[System.Int16]$code = $ilBytes[$offset++] -bor 0xfe00
}
$opCode = $opCodeDictionary[[System.Int16]$code]
Write-Host $opCode.Name -NoNewline
switch ($opCode.Name)
{
{ $_ -eq "call" -or
$_ -eq "callvirt" }
{
Write-Host " " -NoNewline
}
default {
Write-Host
}
}
switch ($opCode.OperandType)
{
"InlineMethod"
{
$metaDataToken = [BitConverter]::ToInt32($ilBytes,$offset)
$genericMethodArguments = $null
if ($method.IsGenericMethod -eq $true)
{
$genericMethodArguments = $method.GetGenericArguments()
}
($method.Module.ResolveMethod(
$metaDataToken,
$method.DeclaringType.GetGenericArguments(),
$genericMethodArguments)).Name
$offset += 4
}
"InlineNone"
{
}
{ $_ -eq "ShortInlineBrTarget" -or
$_ -eq "ShortInlineI" -or
$_ -eq "ShortInlineVar" }
{
$offset++
}
"InlineVar"
{
$offset += 2
}
{ $_ -eq "InlineBrTarget" -or
$_ -eq "InlineField" -or
$_ -eq "InlineI" -or
$_ -eq "InlineSig" -or
$_ -eq "InlineString" -or
$_ -eq "InlineTok" -or
$_ -eq "InlineType" -or
$_ -eq "ShortInlineR" }
{
$count = [BitConverter]::ToInt32($Ilbytes,$offset+1)
$offset += 4
}
{ $_ -eq "InlineI8" -or
$_ -eq "InlineR"
}
{
$offset += 8
}
"InlineSwitch"
{
$count = [BitConverter]::ToInt32($Ilbytes,$offset+1)
$offset += 4 * $count
}
default
{ throw "$($opCode.OperandType) not implemented" }
}
}
}
Write-Host "} // end of method $($method.DeclaringType.ToString())::$($method.Name)"
Write-Host
}