1

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 
}
chentiangemalc
  • 1,235
  • 8
  • 16

1 Answers1

1

This can be resolved using ResolveField method, fixed example. Note this example still has limitation in that it is failing to resolve fields where I need to specify genericTypeArguments and genericMethodArguments to resolvefield.

$fieldReference = [BitConverter]::ToInt32($Ilbytes,$offset)
                    try
                    {
                        $field = $method.Module.ResolveField($fieldReference)
                        Write-Host "$($field.FieldType) $($field.ReflectedType.ToString())::$($field.Name)"
                    }
                    catch
                    {
                        Write-Host ("<Unresolved reference 0x{0:x}>" -f $fieldReference)
                    }
                    $offset += 4

Updated within script:

# 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)
    {
        Write-Host ".maxstack $($methodBody.MaxStackSize)"
        if ($methodBody.LocalVariables -ne $null)
        {
            Write-Host ".locals " -NoNewline
            if ($methodBody.InitLocals -eq $True)
            {
                Write-Host "init " -NoNewline
            }

            Write-Host "(" -NoNewLine
            ForEach ($local in $methodBody.LocalVariables)
            {
                if ($local.LocalIndex -eq 0)
                {
                    Write-Host "$($local.LocalType) V_$($local.LocalIndex)" -NoNewline
                }
                else
                {
                    Write-Host ",`n`t$($local.LocalType) V_$($local.LocalIndex)"
                }
            }

            Write-Host ")"
        }
        $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.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
                }

                "InlineString"
                {
                    $stringReference = [BitConverter]::ToInt32($Ilbytes,$offset)
                    try
                    {
                        $stringInfo = $method.Module.ResolveString($stringReference)
                        Write-Host "`"$stringInfo`""
                    }
                    catch
                    {
                        Write-Host ("<Unresolved reference 0x{0:x}>" -f $fieldReference)
                    }
                    $offset += 4
                }

                "InlineType"
                {
                    $typeReference = [BitConverter]::ToInt32($Ilbytes,$offset)
                    try
                    {
                        $typeInfo = $method.Module.ResolveType($typeReference)
                        Write-Host "$($typeInfo.BaseType.ToString())::$($typeInfo.Name)"
                    }
                    catch
                    {
                        Write-Host ("<Unresolved reference 0x{0:x}>" -f $fieldReference)
                    }
                    $offset += 4
                }

                { $_ -eq "InlineBrTarget" -or
                  $_ -eq "InlineField" -or
                  $_ -eq "InlineI" -or
                  $_ -eq "InlineSig" -or
                  $_ -eq "InlineTok" -or
                  $_ -eq "ShortInlineR" }
                {
                    $fieldReference = [BitConverter]::ToInt32($Ilbytes,$offset)
                    try
                    {
                        $field = $method.Module.ResolveField($fieldReference)
                        Write-Host "$($field.FieldType) $($field.ReflectedType.ToString())::$($field.Name)"
                    }
                    catch
                    {
                        Write-Host ("<Unresolved reference 0x{0:x}>" -f $fieldReference)
                    }
                    $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 "" 
        }
    }

    Write-Host "} // end of method $($method.DeclaringType.ToString())::$($method.Name)"
    Write-Host 
}
chentiangemalc
  • 1,235
  • 8
  • 16