PowerShell调用.NET程序集参数不匹配及非.NET程序执行问题
.NET程序集内存加载问题与非.NET程序执行方案
问题描述
- 加载WinForm类型的.NET可执行文件时,调用
Main方法传入参数出现「parameter number mismatch」错误,传入$null则正常运行 - 想了解如何在内存中执行非.NET类型的可执行文件
原测试代码
$ByteArray = (Invoke-WebRequest "https://DOTNETEXEfile.exe").Content # Base64 $Base64String = [System.Convert]::ToBase64String($ByteArray); $PsEBytes = [System.Convert]::FromBase64String($Base64String) # Run EXE in memory $assembly = [System.Reflection.Assembly]::Load($PsEBytes) # Get the static method that is the executable's entry point. # Note: # * Assumes 'Program' as the class name, # and a static method named 'Main' as the entry point. # * Should there be several classes by that name, the *first* # - public or non-public - type returned is used. # If you know the desired type's namespace, use, e.g. # $assembly.GetType('MyNameSpace.Program').GetMethod(...) $entryPointMethod = $assembly.GetTypes().Where({ $_.Name -eq 'Program' }, 'First'). GetMethod('Main', [Reflection.BindingFlags] 'Static, Public, NonPublic') # Now you can call the entry point. # This example passes two arguments, 'foo' and 'bar' $entryPointMethod.Invoke($null, (, [string[]] ('foo', 'bar')))
错误信息
PS C:\Users\sadettin\Desktop> C:\Users\sadettin\Desktop\PE.ps1 Exception calling "Invoke" with "2" argument(s): "parameter number mismatch." At C:\Users\sadettin\Desktop\PE.ps1:25 char:1 + $entryPointMethod.Invoke($null, (, [string[]] ('foo', 'bar'))) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : TargetParameterCountException
解决方案
一、WinForm程序Main方法传参错误修复
原因
WinForm程序的Main方法通常有两种重载:无参数的Main()和带参数的Main(string[] args)。原代码直接获取名为Main的方法,可能拿到的是无参数重载,导致传参时参数数量不匹配。
修正代码
$ByteArray = (Invoke-WebRequest "https://DOTNETEXEfile.exe").Content $PsEBytes = $ByteArray # 取消冗余的Base64转码,直接使用原始字节数组 $assembly = [System.Reflection.Assembly]::Load($PsEBytes) # 方案1:通过程序集EntryPoint属性直接获取入口点(推荐,无需依赖类名) $entryPointMethod = $assembly.EntryPoint # 方案2:指定参数类型获取带string[]参数的Main方法 # $entryPointMethod = $assembly.GetTypes().Where({ $_.Name -eq 'Program' }, 'First').GetMethod('Main', [Reflection.BindingFlags] 'Static, Public, NonPublic', $null, [Type[]] ([string[]]), $null) # 根据入口点是否有参数,选择对应调用方式 if ($entryPointMethod.GetParameters().Count -eq 0) { $entryPointMethod.Invoke($null, $null) } else { $entryPointMethod.Invoke($null, (, [string[]] ('foo', 'bar'))) }
关键说明
- 取消冗余的Base64转换:
Invoke-WebRequest返回的Content本身就是字节数组,无需转码后再转回 - 使用
EntryPoint属性更可靠:.NET程序集的入口点不一定在Program类,该属性直接指向程序定义的入口方法 - 增加参数判断:兼容无参数的WinForm程序重载,避免参数不匹配错误
二、非.NET类型可执行文件的内存执行方案
非.NET原生PE文件无法像.NET程序集那样直接反射加载执行,因为原生PE需要操作系统加载器处理重定位、导入表修复等底层操作,常见实现方式有两种:
1. 手动调用Windows API加载
通过反射调用LoadLibraryEx、GetProcAddress等Kernel32 API,手动完成PE文件的内存分配、重定位修复、导入表解析等操作。示例框架如下(仅作演示,实际需完善PE解析逻辑):
# 加载Kernel32程序集 $kernel32 = [System.Reflection.Assembly]::LoadWithPartialName("kernel32") $loadLibraryEx = $kernel32.GetType("Win32.Kernel32").GetMethod("LoadLibraryEx", [Reflection.BindingFlags] "Public, Static", $null, [Type[]] ([string], [IntPtr], [UInt32]), $null) $getProcAddress = $kernel32.GetType("Win32.Kernel32").GetMethod("GetProcAddress", [Reflection.BindingFlags] "Public, Static") # 注:原生PE内存执行需自行解析PE头、处理内存分配与重定位,逻辑复杂且易被安全软件拦截
2. 借助第三方工具/库
可以使用专门的PE内存加载工具(多为C#编写),编译为.NET程序集后在PowerShell中调用。这类工具封装了底层PE解析逻辑,降低实现难度,但需注意合规性与安全风险。
内容的提问来源于stack exchange,提问作者NoobCoding
相关产品推荐
相关产品推荐

