0

我想使用 CreateThread WinApi 创建在 powershell 中运行脚本代码的线程。我使用此代码但发生错误:

function local:Get-ProcAddress {
Param (
    [OutputType([IntPtr])]

    [Parameter( Position = 0, Mandatory = $True )]
    [String]
    $Module,

    [Parameter( Position = 1, Mandatory = $True )]
    [String]
    $Procedure
)

# Get a reference to System.dll in the GAC
$SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
    Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
$UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
# Get a reference to the GetModuleHandle and GetProcAddress methods
$GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
$GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress')
# Get a handle to the module specified
$Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
$tmpPtr = New-Object IntPtr
$HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)

# Return the address of the function
$GetProcAddress.Invoke($null, @([Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
}
function local:Get-DelegateType {
Param (
    [OutputType([Type])]

    [Parameter( Position = 0)]
    [Type[]]
    $Parameters = (New-Object Type[](0)),

    [Parameter( Position = 1 )]
    [Type]
    $ReturnType = [Void]
)

$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object Reflection.AssemblyName('ReflectedDelegate')
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
$TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
$ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
$MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
$MethodBuilder.SetImplementationFlags('Runtime, Managed')

$TypeBuilder.CreateType()
}

并定义createthread winapi:

# CreateThread
$CreateThreadAddr = Get-ProcAddress kernel32.dll CreateThread
$CreateThreadDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr])
$CreateThread = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateThreadAddr, $CreateThreadDelegate)

我以这种方式调用createthread winapi:

$ThreadProcScript = {
Param (
    [Parameter()]
    [IntPtr]$lpParameter

)
calc.exe
return [Int32]0

}
$Delegate = Get-DelegateType @([IntPtr]) ([int32])
$Callback = $ThreadProcScript -as $Delegate
$CreateThread.Invoke(0,0,$Callback ,0,0,0)

但发生错误:无法将类型“MyDelegateType”的“MyDelegateType”值转换为类型“System.IntPtr”我该如何解决这个错误?还有另一种在powershell中使用创建线程winapi的方法吗?

4

1 回答 1

4

通过“固定”对象可以将托管对象转换为 IntPtr,但这是非常棘手的事情,因为它只允许在“不安全”代码中使用。您可以使用“固定”语句在 C# 中执行此操作。GCHandle.AddrOfPinnedObject ()方法是可以直接从 PowerShell 调用的替代方法,但我不能向您推荐它。

但是,我会警告您,如果您尝试在 PowerShell 中进行完全通用的“winapi”编程(甚至使用非托管的 CreateThread),结果会变得非常复杂。您需要了解高级 .NET 互操作和 PowerShell 对线程的限制,这两者都只是部分记录。使用 .NET 的 System.Thread 类而不是 windows API 可以帮助其中的一部分而不需要“不安全”代码,但即使是这种方法,我也建议你不要在没有更高级别解释的情况下在 PowerShell 中执行,而不仅仅是在 PowerShell 中“想要”winapi 编程。

有关有助于抽象一般用途的基于轻量级(线程)的作业所需的最新 Powershell 模块,请参阅GitHub 上的PoshRSJob。与尝试自己调用 WINAPI 函数相比,它可能会为您节省大量调试时间。https://learn-powershell.net/2015/03/31/introducing-poshrsjob-as-an-alternative-to-powershell-jobs/上的介绍性文章

于 2016-09-11T12:10:18.150 回答