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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.