pstools.psm1

using namespace System.Reflection.Emit
using namespace System.Runtime.InteropServices

$GetDllExports = {
  param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][String]$Module)

  end {
    ($exp = $ExecutionContext.SessionState.PSVariable.Get("__$Module").Value) ? $exp : $(
      $mod = ($ps = Get-Process -Id $PID).Modules.Where{$_.ModuleName -match "^$Module"}.BaseAddress
      $ps.Dispose() && $($jmp = ($mov = [Marshal]::ReadInt32($mod, 0x3C)) + [Marshal]::SizeOf([UInt32]0))
      $jmp = switch ([BitConverter]::ToUInt16([BitConverter]::GetBytes([Marshal]::ReadInt16($mod, $jmp)), 0)) {
        0x014C { 0x20, 0x78, 0x7C } 0x8664 { 0x40, 0x88, 0x8C } default { [SystemException]::new() }
      }
      $tmp, $fun = $mod."ToInt$($jmp[0])"(), @{}
      $va, $sz = [Marshal]::ReadInt32($mod, $mov + $jmp[1]), [Marshal]::ReadInt32($mod, $mov + $jmp[2])
      ($ed = @{bs = 0x10; nf = 0x14; nn = 0x18; af = 0x1C; an = 0x20; ao = 0x24}).Keys.ForEach{
        $val = [Marshal]::ReadInt32($mod, $va + $ed.$_)
        Set-Variable -Name $_ -Value ($_.StartsWith('a') ? $tmp + $val : $val) -Scope Script
      }
      function Assert-Forwarder([UInt32]$fa) { end { ($va -le $fa) -and ($fa -lt ($va + $sz)) } }
      (0..($nf - 1)).ForEach{
        $fun[$bs + $_] = (Assert-Forwarder ($fa = [Marshal]::ReadInt32([IntPtr]($af + $_ * 4)))) ? @{
          Address = ''; Forward = [Marshal]::PtrToStringAnsi([IntPtr]($tmp + $fa))
        } : @{Address = [IntPtr]($tmp + $fa); Forward = ''}
      }
      Set-Variable -Name "__$Module" -Value ($exp = (0..($nn - 1)).ForEach{
        [PSCustomObject]@{
          Ordinal = ($ord = $bs + [Marshal]::ReadInt16([IntPtr]($ao + $_ * 2)))
          Address = $fun[$ord].Address
          Name = [Marshal]::PtrToStringAnsi([IntPtr]($tmp + [Marshal]::ReadInt32([IntPtr]($an + $_ * 4))))
          Forward = $fun[$ord].Forward
        }
      }) -Option ReadOnly -Scope Global -Visibility Private
      $exp
    )
  }
}

function ConvertFrom-PtrToMethod {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory, Position=0)]
    [ValidateScript({$_ -ne [IntPtr]::Zero})]
    [IntPtr]$Address,

    [Parameter(Mandatory, Position=1)]
    [ValidateNotNull()]
    [Type]$Prototype,

    [Parameter(Position=2)]
    [ValidateNotNullOrEmpty()]
    [Alias('cc')]
    [CallingConvention]$CallingConvention = 'Cdecl'
  )

  end {
    $method = $Prototype.GetMethod('Invoke')
    $returntype, $paramtypes = $method.ReturnType, ($method.GetParameters().ParameterType ?? $null)
    $il, $to_i = ($holder = [DynamicMethod]::new('Invoke', $returntype, $paramtypes, $Prototype)
    ).GetILGenerator(), "ToInt$(($sz = [IntPtr]::Size) * 8)"
    if ($paramtypes) { (0..($paramtypes.Length - 1)).ForEach{$il.Emit([OpCodes]::ldarg, $_)} }
    $il.Emit([OpCodes]::"ldc_i$sz", $Address.$to_i())
    $il.EmitCalli([OpCodes]::calli, $CallingConvention, $returntype, $paramtypes)
    $il.Emit([OpCodes]::ret)
    $holder.CreateDelegate($Prototype)
  }
}

($scheme = @{
  kernelbase = @{
    FreeLibrary = [Func[IntPtr, Boolean]]
    GetModuleHandleW = [Func[[Byte[]], IntPtr]]
    GetProcAddress = [Func[IntPtr, String, IntPtr]]
    LoadLibraryW = [Func[[Byte[]], IntPtr]]
  }
  ntdll = @{
    RtlNtStatusToDosError = [Func[Int32, Int32]]
  }
}).Keys.ForEach{
  $functions = $scheme[($module = $_)]
  $GetDllExports.Invoke($module).Where{$_.Name -in $functions.Keys}.ForEach{
    Set-Variable -Name (
      $_.Name.EndsWith('W') ? $_.Name.Substring(0, $_.Name.Length - 1) : $_.Name
    ) -Value (
      ConvertFrom-PtrToMethod -Address $_.Address -Prototype $functions[$_.Name] -cc StdCall
    ) -Scope Global -Option ReadOnly -Force
  }
}

$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
  ('FreeLibrary',
   'GetModuleHandle',
   'GetProcAddress',
   'LoadLibrary',
   'RtlNtStatusToDosError'
  ).ForEach{ Remove-Variable -Name $_ -Scope Global -Force }
}

('lib', 'usr').ForEach{
  (Get-ChildItem -Path "$PSScriptRoot\$_" -Filter *.ps1).ForEach{.$_.FullName}
}