解析 GetILAsByteArray 时识别 .NET 中的 ldfld 引用

逆向工程 。网
2021-06-19 21:37:03

我正在尝试使用反射反编译 PowerShell cmdlet 的想法。

虽然我的一些基础知识似乎可以工作,但我如何解析 OperandField 中的值,例如在这种情况下,如果反编译正确,ldfld cmd 可能如下所示:

ldfld      string[] Microsoft.PowerShell.Commands.GetAclCommand::path

目前我只是显示为“ldfld”

这在这部分代码中解析:

{ $_ -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
                }

完整脚本在这里:

# 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个回答

这可以使用 ResolveField 方法解决,固定示例。请注意,此示例仍然存在局限性,因为它无法解析需要将 genericTypeArguments 和 genericMethodArguments 指定为 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

在脚本中更新:

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