dll注入工具_UnmanagedPowerShell工具分析
簡單介紹:從非托管進程執行PowerShell。通過一些修改,可以在將這些相同的技術注入到不同的進程時使用(例如,如果需要,可以讓任何進程執行PowerShell)
下面借用網上的一張圖來說明這個流程,上面說了可以讓任何進程執行powershell其實也就是說使用具有注入功能的程序將一個非托管的C++DLL注入到目標進程中,然后該非托管DLL啟動CLR,并加載要執行的托管DLL,最后調用CLR執行托管代碼。
而我們下面的工具實現的是非托管進程啟動CLR,并加載要執行的托管的程序集,最后調用CLR執行托管代碼
下面就對UnmanagedPowerShell工具源碼來解釋下整個流程的工作運轉
關于PowerShellRunner.cs的部分
相關類的定義:
CustomPSHostUserInterface:可以替換我們要輸出的內容
CustomPSRHostRawUserInterface:配置用戶的界面
PSHost:為了讓Write-Host工作,我必須實現一個自定義PSHost。如果所有的PowerShell腳本都使用Write-Output而不是Write-Host,那么這就不是問題,但是如果使用了足夠多的Write-Host,那么實現一個定制PSHost是值得的
在C#中調用PowerShell會用到這個程序集,System.Management.Automation.dll,所以下面就是在適配調用時需要產生的類
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Management.Automation;
using System.Globalization;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
namespace PowerShellRunner
{
public class PowerShellRunner
{
public static string InvokePS(string command)
{
// I had to implement a custom PSHost in order to get Write-Host to work.
// This wouldn't be an issue if all PowerShell scripts used Write-Output
// instead of Write-Host, but enough use Write-Host that it's worth it
// to implement a custom PSHost
//Write-Output返回的輸出被傳遞給管道,因此管道之后的函數/cmdlet可以讀取該輸出以進行進一步處理。如果您使用Write-Host,這是不可能的。
//為了讓Write-Host工作,我必須實現一個自定義PSHost。如果所有的PowerShell腳本都使用Write-Output而不是Write-Host,那么這就不是問題,但是如果使用了足夠多的Write-Host,那么實現一個定制PSHost是值得的
CustomPSHost host = new CustomPSHost();
//允許您定義在創建會話狀態時應該出現的元素集
//使用默認的cmdlet、提供程序等創建默認的PowerShell。內置函數,別名需要通過默認的InitialSessionstate構造函數可用。還需要對包裝進行討論。
var state = InitialSessionState.CreateDefault();
//指定此會話狀態實例使用的授權管理器。如果沒有指定授權管理器,那么將使用PowerShell的缺省授權管理器,它在運行命令之前檢查ExecutionPolicy
state.AuthorizationManager = null; // Bypass PowerShell execution policy?繞過PowerShell執行策略?
//RunspaceFactory--定義用于創建Runspace對象的工廠類
//使用指定的PSHost和InitialSessionState創建運行空間
using (Runspace runspace = RunspaceFactory.CreateRunspace(host, state))
{
//同步打開運行空間。運行空間在使用之前必須打開。
runspace.Open();
//Create an empty pipeline
using (Pipeline pipeline = runspace.CreatePipeline())
{
//Commands--獲取此管道的命令集合
//AddScript(String)????Adds a new script command ?添加一個新的腳本命令
pipeline.Commands.AddScript(command);
//合并此命令結果
//myResult:PipelineResultTypes:要重定向的管道流
//toResult:PipelineResultTypes:將myResult合并到的管道流
pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
//將輸出發送到默認格式化程序和默認輸出cmdlet。
pipeline.Commands.Add("out-default");
//同步調用管道,以對象數組的形式返回結果
pipeline.Invoke();
}
}
//獲取托管應用程序的PSHostUserInterface抽象基類的實現。不希望支持用戶交互的主機應該返回null。
string output = ((CustomPSHostUserInterface)host.UI).Output;
return output;
}
//定義承載MSH運行空間的應用程序提供的屬性和功能
//System.Management.Automation.Runspaces到Msh運行時的公共接口。提供用于創建管道、訪問會話狀態等的api。
//GUID數據類型是表示類標識符(ID)的文本字符串
//托管應用程序派生自此類,并重寫抽象方法和屬性。托管應用程序將創建其派生類的實例,然后將其傳遞給RunspaceFactory CreateRunspace方法。
class CustomPSHost : PSHost
{
//初始化Guid結構的新實例
private Guid _hostId = Guid.NewGuid();
//設置PSHostUserInterface抽象基類的宿主應用程序的實現 。不想支持用戶交互的主機應返回null
private CustomPSHostUserInterface _ui = new CustomPSHostUserInterface();
//獲取唯一標識此主機實例的GUID。該值應在此實例的生命周期內保持不變
public override Guid InstanceId
{
get { return _hostId; }
}
//以某種用戶友好的方式獲取托管應用程序的標識。腳本和cmdlet可以引用這個名稱來標識執行它們的主機。值的格式沒有定義,但建議使用簡短的字符串。
public override string Name
{
get { return "ConsoleHost"; }
}
//獲取宿主應用程序的版本。對于主機的特定構建,此值應該保持不變。此值可由腳本和cmdlet引用。?
public override Version Version
{
get { return new Version(1, 0); }
}
//獲取托管應用程序的PSHostUserInterface抽象基類的實現。不希望支持用戶交互的主機應該返回null。?
public override PSHostUserInterface UI
{
get { return _ui; }
}
//獲取主機的區域性:運行空間應使用該區域性在新線程上設置CurrentCulture
public override CultureInfo CurrentCulture
{
get { return Thread.CurrentThread.CurrentCulture; }
}
//獲取主機的UI區域性:運行空間和cmdlet應該用來加載資源的區域性。
//每次啟動管道時,運行空間都會將線程當前ui區域性設置為這個值。
public override CultureInfo CurrentUICulture
{
get { return Thread.CurrentThread.CurrentUICulture; }
}
//指示主機中斷當前正在運行的管道并啟動一個新的“嵌套”輸入循環,其中輸入循環是提示、輸入和執行的循環。
public override void EnterNestedPrompt()
{
throw new NotImplementedException("EnterNestedPrompt is not implemented. The script is asking for input, which is a problem since there's no console. Make sure the script can execute without prompting the user for input.");
}
//導致主機結束當前運行的輸入循環。如果先前調用EnterNestedPrompt創建了輸入循環,則封閉管道將恢復。如果當前輸入循環是最上面的循環,那么主機將執行SetShouldExit調用。
public override void ExitNestedPrompt()
{
throw new NotImplementedException("ExitNestedPrompt is not implemented. The script is asking for input, which is a problem since there's no console. Make sure the script can execute without prompting the user for input.");
}
//由引擎調用,通知主機它將執行“遺留”命令行應用程序。遺留應用程序被定義為控制臺模式的可執行文件,它可以執行以下一個或多個操作:。讀stdin。寫信給stdout。寫信給stderr。使用任何win32控制臺api
public override void NotifyBeginApplication()
{
return;
}
//由引擎調用,通知主機遺留命令的執行已經完成
public override void NotifyEndApplication()
{
return;
}
//引擎請求結束當前引擎運行空間(關閉并終止主機的根運行空間)
public override void SetShouldExit(int exitCode)
{
return;
}
}
//提供一個對話框,允許用戶從一組選項中選擇一個選項
//定義由PSHost派生的托管應用程序提供的屬性和功能,該托管應用程序 提供了面向對話框和面向行的交互功能
class CustomPSHostUserInterface : PSHostUserInterface
{
// Replace StringBuilder with whatever your preferred output method is (e.g. a socket or a named pipe)
private StringBuilder _sb;
//將StringBuilder替換為您首選的輸出方法(例如,套接字或命名管道);
//獲取實現該類的--PSHostRawUserInterface抽象基類的托管應用程序實現。
private CustomPSRHostRawUserInterface _rawUi = new CustomPSRHostRawUserInterface();
public CustomPSHostUserInterface()?
{
_sb = new StringBuilder();
}
//將字符寫入屏幕緩沖區。不附加回車。
public override void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value)
{
_sb.Append(value);
}
//默認實現將回車寫入屏幕緩沖區
public override void WriteLine()
{
_sb.Append("\n");
}
//與WriteLine(String)相同,只是可以指定顏色。
public override void WriteLine(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value)
{
_sb.Append(value + "\n");
}
//Writes characters to the screen buffer. Does not append a carriage return.
public override void Write(string value)
{
_sb.Append(value);
}
//由WriteDebug(String)調用,向用戶顯示調試消息。
public override void WriteDebugLine(string message)
{
_sb.AppendLine("DEBUG: " + message);
}
//向主機的“錯誤顯示”寫入一行,而不是由and的變體寫入的“輸出顯示”
public override void WriteErrorLine(string value)
{
_sb.AppendLine("ERROR: " + value);
}
//將字符寫入屏幕緩沖區,并附加回車
public override void WriteLine(string value)
{
_sb.AppendLine(value);
}
//由WriteVerbose(String)調用,向用戶顯示詳細的處理消息
public override void WriteVerboseLine(string message)
{
_sb.AppendLine("VERBOSE: " + message);
}
//由WriteWarning(String)調用,向用戶顯示警告處理消息。
public override void WriteWarningLine(string message)
{
_sb.AppendLine("WARNING: " + message);
}
//Invoked by System.Management.Automation.Cmdlet.WriteProgress(System.Int64,System.Management.Automation.ProgressRecord) to display a progress record.
public override void WriteProgress(long sourceId, ProgressRecord record)
{
return;
}
public string Output
{
get { return _sb.ToString(); }
}
//構造一個“對話框”,其中向用戶顯示許多要為其提供值的字段。
public override Dictionary Prompt(string caption, string message, System.Collections.ObjectModel.Collection descriptions)
{
throw new NotImplementedException("Prompt is not implemented. The script is asking for input, which is a problem since there's no console. Make sure the script can execute without prompting the user for input.");
}
//提供一個對話框,允許用戶從一組選項中選擇一個選項
public override int PromptForChoice(string caption, string message, System.Collections.ObjectModel.Collection choices, int defaultChoice)
{
throw new NotImplementedException("PromptForChoice is not implemented. The script is asking for input, which is a problem since there's no console. Make sure the script can execute without prompting the user for input.");
}
//Prompt for credentials.
public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options)
{
throw new NotImplementedException("PromptForCredential1 is not implemented. The script is asking for input, which is a problem since there's no console. Make sure the script can execute without prompting the user for input.");
}
//提示輸入憑證
public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName)
{
throw new NotImplementedException("PromptForCredential2 is not implemented. The script is asking for input, which is a problem since there's no console. Make sure the script can execute without prompting the user for input.");
}
public override PSHostRawUserInterface RawUI
{
get { return _rawUi; }
}
//從控制臺讀取字符,直到遇到換行(回車)。
public override string ReadLine()
{
throw new NotImplementedException("ReadLine is not implemented. The script is asking for input, which is a problem since there's no console. Make sure the script can execute without prompting the user for input.");
}
//與ReadLine相同,只是結果是SecureString,并且在收集輸入時不會回顯給用戶(或者以某種模糊的方式回顯,比如為每個字符顯示一個點)。
public override System.Security.SecureString ReadLineAsSecureString()
{
throw new NotImplementedException("ReadLineAsSecureString is not implemented. The script is asking for input, which is a problem since there's no console. Make sure the script can execute without prompting the user for input.");
}
}
//PSHostRawUserInterface 讀取用戶操作的界面
//可以提供Shell窗口物理操作方法的途徑,包括字符標記,屬性,以及文字前景和背景顏色等
class CustomPSRHostRawUserInterface : PSHostRawUserInterface
{
// Warning: Setting _outputWindowSize too high will cause OutOfMemory execeptions. I assume this will happen with other properties as well
//警告:設置_outputWindowSize過高將導致OutOfMemory執行。我想這也會發生在其他性質上
private Size _windowSize = new Size { Width = 120, Height = 100 };
private Coordinates _cursorPosition = new Coordinates { X = 0, Y = 0 };
private int _cursorSize = 1;
private ConsoleColor _foregroundColor = ConsoleColor.White;
private ConsoleColor _backgroundColor = ConsoleColor.Black;
private Size _maxPhysicalWindowSize = new Size
{
Width = int.MaxValue,
Height = int.MaxValue
};
private Size _maxWindowSize = new Size { Width = 100, Height = 100 };
private Size _bufferSize = new Size { Width = 100, Height = 1000 };
private Coordinates _windowPosition = new Coordinates { X = 0, Y = 0 };
private String _windowTitle = "";
public override ConsoleColor BackgroundColor
{
get { return _backgroundColor; }
set { _backgroundColor = value; }
}
public override Size BufferSize
{
get { return _bufferSize; }
set { _bufferSize = value; }
}
public override Coordinates CursorPosition
{
get { return _cursorPosition; }
set { _cursorPosition = value; }
}
public override int CursorSize
{
get { return _cursorSize; }
set { _cursorSize = value; }
}
//重置鍵盤輸入緩沖區
public override void FlushInputBuffer()
{
throw new NotImplementedException("FlushInputBuffer is not implemented.");
}
//獲取或設置用于在屏幕緩沖區上呈現字符的顏色。屏幕緩沖區中的每個字符單元可以具有單獨的前景色
public override ConsoleColor ForegroundColor
{
get { return _foregroundColor; }
set { _foregroundColor = value; }
}
//提取屏幕緩沖區的矩形區域
public override BufferCell[,] GetBufferContents(Rectangle rectangle)
{
throw new NotImplementedException("GetBufferContents is not implemented.");
}
//一個非阻塞調用,用于檢查擊鍵是否在輸入緩沖區中等待
public override bool KeyAvailable
{
get { throw new NotImplementedException("KeyAvailable is not implemented."); }
}
public override Size MaxPhysicalWindowSize
{
get { return _maxPhysicalWindowSize; }
}
public override Size MaxWindowSize
{
get { return _maxWindowSize; }
}
public override KeyInfo ReadKey(ReadKeyOptions options)
{
throw new NotImplementedException("ReadKey is not implemented. The script is asking for input, which is a problem since there's no console. Make sure the script can execute without prompting the user for input.");
}
public override void ScrollBufferContents(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill)
{
throw new NotImplementedException("ScrollBufferContents is not implemented");
}
public override void SetBufferContents(Rectangle rectangle, BufferCell fill)
{
throw new NotImplementedException("SetBufferContents is not implemented.");
}
public override void SetBufferContents(Coordinates origin, BufferCell[,] contents)
{
throw new NotImplementedException("SetBufferContents is not implemented");
}
public override Coordinates WindowPosition
{
get { return _windowPosition; }
set { _windowPosition = value; }
}
public override Size WindowSize
{
get { return _windowSize; }
set { _windowSize = value; }
}
public override string WindowTitle
{
get { return _windowTitle; }
set { _windowTitle = value; }
}
}
}
}
關于
UnmanagedPowerShell/UnmanagedPowerShell/UnmanagedPowerShell.cpp的部分
運行托管與非托管代碼根本區別在于托管代碼是進程首先加載CLR然后通過CLR運行托管程序,而非托管代碼則是操作系統直接根據其PE Header加載程序分配內存從而運行。因此如果需要通過托管代碼來擴展非托管程序,首先要加載CLR來使非托管程序獲得運行托管代碼的能力。所以下面代碼做的就是這個事情
// UnmanagedPowerShell.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#pragma region Includes and Imports
#include
#include
#include
#include "PowerShellRunnerDll.h"
#include
#pragma comment(lib, "mscoree.lib")
// Import mscorlib.tlb (Microsoft Common Language Runtime Class Library).
//?微軟公共語言運行時類庫
//raw_interface_only:僅使用原始接口
//Foo([out, retval] long * pVal);這個函數,缺省時調用:long val = obj->Foo();
//如果用了raw_interface_only就要:
//long val;
//xx->Foo(&val);
#import "mscorlib.tlb" raw_interfaces_only \
//high_property_prefixes屬性
//high_property_prefixes("GetPrefix,""PutPrefix,""PutRefPrefix")
//GetPrefix
//用于propget方法的前綴
//PutPrefix
//用于propput方法的前綴
//PutRefPrefix
//用于propputref方法的前綴
//在缺省情況下,高級錯誤處理方法,如propget、propput和propputref,分別采用以前綴Get、Put和PutRef命名的成員函數來說明。high_property_prefixes屬性用于分別說明這三種屬性方法的前綴。
high_property_prefixes("_get","_put","_putref") \
//rename屬性
//rename("OldName,""NewName")
//OldName
//類型庫中的舊名
//NewName
//用于替換舊名的名稱
//rename屬性用于解決名稱沖突的問題。若該屬性被指定,編譯器將在類型庫中的OldName的所有出現處用結果頭文件中用戶提供的NewName替換。
//此屬性用于類型庫中
rename("ReportEvent", "InteropServices_ReportEvent")
using namespace mscorlib;
#pragma endregion
//創建接口實例,返回接口指針
//
typedef HRESULT(WINAPI *funcCLRCreateInstance)(
REFCLSID clsid,
REFIID riid,
LPVOID * ppInterface
);
//使非托管主機可以將公共語言運行庫(CLR)加載到進程中。該CorBindToRuntime和CorBindToRuntimeEx功能執行相同的操作,但該CorBindToRuntimeEx功能允許您設置標志來指定CLR的行為
typedef HRESULT (WINAPI *funcCorBindToRuntime)(
LPCWSTR pwszVersion,
LPCWSTR pwszBuildFlavor,
REFCLSID rclsid,
REFIID riid,
LPVOID* ppv);
extern const unsigned int PowerShellRunner_dll_len;
extern unsigned char PowerShellRunner_dll[];
void InvokeMethod(_TypePtr spType, wchar_t* method, wchar_t* command);
//適配.Net4
bool createDotNetFourHost(HMODULE* hMscoree, const wchar_t* version, ICorRuntimeHost** ppCorRuntimeHost)
{
HRESULT hr = NULL;
funcCLRCreateInstance pCLRCreateInstance = NULL;
ICLRMetaHost *pMetaHost = NULL;
ICLRRuntimeInfo *pRuntimeInfo = NULL;
bool hostCreated = false;
//尋找CLRCreateInstance函數的地址
pCLRCreateInstance = (funcCLRCreateInstance)GetProcAddress(*hMscoree, "CLRCreateInstance");
if (pCLRCreateInstance == NULL)
{
wprintf(L"Could not find .NET 4.0 API CLRCreateInstance");
goto Cleanup;
}
//若要獲取此接口的實例的唯一方法是通過調用CLRCreateInstance函數,如下所示:
//ICLRMetaHost *pMetaHost = NULL; HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost);
hr = pCLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
if (FAILED(hr))
{
// Potentially fails on .NET 2.0/3.5 machines with E_NOTIMPL
wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
//獲取與特定版本的公共語言運行時 (CLR) 相對應的ICLRRuntimeInfo接口
hr = pMetaHost->GetRuntime(L"v2.0.50727", IID_PPV_ARGS(&pRuntimeInfo));
if (FAILED(hr))
{
wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
//檢查指定的運行時是否可以加載到流程中
// Check if the specified runtime can be loaded into the process.
BOOL loadable;
//指示與此接口關聯的運行時是否可以加載到當前進程中,考慮到可能已加載到進程的其他運行時。
hr = pRuntimeInfo->IsLoadable(&loadable);
if (FAILED(hr))
{
wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
if (!loadable)
{
wprintf(L".NET runtime v2.0.50727 cannot be loaded\n");
goto Cleanup;
}
// Load the CLR into the current process and return a runtime interface
//?將CLR加載到當前進程并返回運行時接口
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_PPV_ARGS(ppCorRuntimeHost));
if (FAILED(hr))
{
wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
hostCreated = true;
Cleanup:
if (pMetaHost)
{
pMetaHost->Release();
pMetaHost = NULL;
}
if (pRuntimeInfo)
{
pRuntimeInfo->Release();
pRuntimeInfo = NULL;
}
return hostCreated;
}
//適配.Net2
HRESULT createDotNetTwoHost(HMODULE* hMscoree, const wchar_t* version, ICorRuntimeHost** ppCorRuntimeHost)
{
HRESULT hr = NULL;
bool hostCreated = false;
funcCorBindToRuntime pCorBindToRuntime = NULL;
//CorBindToRuntime--使非托管的宿主能夠將公共語言運行時 (CLR) 加載到進程中,.NET Framework 4 中已棄用此函數
pCorBindToRuntime = (funcCorBindToRuntime)GetProcAddress(*hMscoree, "CorBindToRuntime");
if (!pCorBindToRuntime)
{
wprintf(L"Could not find API CorBindToRuntime");
goto Cleanup;
}
//HRESULT CorBindToRuntime (??
// ? ?[in]??LPCWSTR?????pwszVersion, ? ?想要加載的 CLR 版本描述的字符串,.NET Framework 中的版本號用句點分隔的四個部分組成:major.minor.build.revision。將字符串作為傳遞pwszVersion必須以字符"v"跟版本號 (例如,"v1.0.1529") 的前三個部分開頭,如果調用方指定為 null pwszVersion,加載的運行時的最新版本。
// ? ?[in]??LPCWSTR?????pwszBuildFlavor, ? ?一個字符串,指定是否加載在服務器或工作站的 clr 版本。有效值為 svr 和 wks。服務器生成經過優化,可充分利用多個處理器,用于垃圾回收和工作站生成優化的單處理器計算機上運行的客戶端應用程序,如果pwszBuildFlavor設置為 null,則將加載工作站版本。在單處理器計算機上運行時,工作站生成始終處于加載狀態,即使pwszBuildFlavor設置為svr。但是,如果pwszBuildFlavor設置為svr,并且指定并發垃圾回收 (請參閱的說明flags參數),將加載服務器版本。
// ? ?[in]??REFCLSID????rclsid,???CLSID的實現的組件類ICorRuntimeHost或ICLRRuntimeHost接口。支持的值為 CLSID_CorRuntimeHost 或 CLSID_CLRRuntimeHost
// ? ?[in]??REFIID??????riid,???IID從所請求的接口的rclsid。支持的值為 IID_ICorRuntimeHost 或 IID_ICLRRuntimeHost
// ? ?[out] LPVOID FAR??*ppv??指向返回的接口指針riid
//);??
hr = pCorBindToRuntime(version, L"wks", CLSID_CorRuntimeHost, IID_PPV_ARGS(ppCorRuntimeHost));
if (FAILED(hr))
{
wprintf(L"CorBindToRuntime failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
hostCreated = true;
Cleanup:
return hostCreated;
}
HRESULT createHost(const wchar_t* version, ICorRuntimeHost** ppCorRuntimeHost)
{
bool hostCreated = false;
HMODULE hMscoree = LoadLibrary(L"mscoree.dll");
if (hMscoree)
{
if (createDotNetFourHost(&hMscoree, version, ppCorRuntimeHost) || createDotNetTwoHost(&hMscoree, version, ppCorRuntimeHost))
{
hostCreated = true;
}
}
return hostCreated;
}
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr;
ICorRuntimeHost *pCorRuntimeHost = NULL;
IUnknownPtr spAppDomainThunk = NULL;
_AppDomainPtr spDefaultAppDomain = NULL;
// The .NET assembly to load.
bstr_t bstrAssemblyName("PowerShellRunner");
_AssemblyPtr spAssembly = NULL;
// The .NET class to instantiate.
bstr_t bstrClassName("PowerShellRunner.PowerShellRunner");
_TypePtr spType = NULL;
// Create the runtime host
if (!createHost(L"v2.0.50727", &pCorRuntimeHost))
{
wprintf(L"Failed to create the runtime host\n");
goto Cleanup;
}
// Start the CLR
hr = pCorRuntimeHost->Start();
if (FAILED(hr))
{
wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr);
goto Cleanup;
}
DWORD appDomainId = NULL;
//獲取類型的接口指針System._AppDomain,表示當前進程的默認域
//[out]類型的接口指針System._AppDomain到AppDomain表示進程的默認應用程序域的實例
hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
if (FAILED(hr))
{
wprintf(L"RuntimeClrHost::GetCurrentAppDomainId failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Get a pointer to the default AppDomain in the CLR.
//獲取類型的接口指針System._AppDomain,表示當前進程的默認域
hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
if (FAILED(hr))
{
wprintf(L"ICorRuntimeHost::GetDefaultDomain failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
//此指針被類型化為IUnknown,因此調用方通常應調用QueryInterface若要獲取類型的接口指針System._AppDomain。
hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));?
if (FAILED(hr))
{
wprintf(L"Failed to get default AppDomain w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Load the .NET assembly.
// (Option 1) Load it from disk - usefully when debugging the PowerShellRunner app (you'll have to copy the DLL into the same directory as the exe)
加載.net程序集。
//(選項1)從磁盤加載它—在調試PowerShellRunner應用程序時非常有用(您必須將DLL復制到與exe相同的目錄中)
//參數
//assemblyString
//String
//程序集的顯示名稱。請參閱 FullName。
//assemblySecurity
//Evidence
//用于加載程序集的證據。
// hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly);
// (Option 2) Load the assembly from memory
SAFEARRAYBOUND bounds[1];
bounds[0].cElements = PowerShellRunner_dll_len;
bounds[0].lLbound = 0;
//創建一個新的數組描述符,分配和初始化該數組的數據,并返回一個指向新數組描述符的指針
//VT_UI1 type property MUST be a 1-byte unsigned integer
SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds);
//vt
//數組的基本類型(數組每個元素的VARTYPE)。VARTYPE僅限于變體類型的子集。VT_ARRAY和VT_BYREF標志都不能設置。VT_EMPTY和VT_NULL是該數組的無效基本類型。所有其他類型都是合法的。
//cDims
//數組中的維數。創建陣列后不能更改該數字。
//rgsabound
//為數組分配的邊界向量(每個維度一個)。
//遞增數組的鎖計數,并將指向數組數據的指針放在數組描述符的pvData中
SafeArrayLock(arr);
//memcpy指的是C和C ++使用的內存拷貝函數,函數原型為void * memcpy(void * destin,void * source,unsigned n)
memcpy(arr->pvData, PowerShellRunner_dll, PowerShellRunner_dll_len);
SafeArrayUnlock(arr);
hr = spDefaultAppDomain->Load_3(arr, &spAssembly);
if (FAILED(hr))
{
wprintf(L"Failed to load the assembly w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Get the Type of PowerShellRunner.
//spType表示類型聲明:類類型、接口類型、數組類型、值類型、枚舉類型、類型參數、泛型類型定義,以及開放或封閉構造的泛型類型。
hr = spAssembly->GetType_2(bstrClassName, &spType);
if (FAILED(hr))
{
wprintf(L"Failed to get the Type interface w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Call the static method of the class
wchar_t* argument = L"Get-Process\n\
#This is a PowerShell Comment\n\
Write-Host \"`n`n******* The next command is going to throw an exception. This is planned *********`n`n\"\n\
Read-Host\n";
InvokeMethod(spType, L"InvokePS", argument);
Cleanup:
if (pCorRuntimeHost)
{
pCorRuntimeHost->Release();
pCorRuntimeHost = NULL;
}
return 0;
}
void InvokeMethod(_TypePtr spType, wchar_t* method, wchar_t* command)
{
HRESULT hr;
bstr_t bstrStaticMethodName(method);
SAFEARRAY *psaStaticMethodArgs = NULL;
variant_t vtStringArg(command);
variant_t vtPSInvokeReturnVal;
variant_t vtEmpty;
//SAFEARRAY* SafeArrayCreateVector(???//用于建立一維普通數組。
// ?VARTYPE vt,??????????????????????????????????????????//數組類型
// ?long lLbound,??????????????????????????????????????????//數組的最小下標(可以取負數)
// ?unsigned int cElements???????????????????????????//數組的長度
//);
//創建方法參數
psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
LONG index = 0;
//放元素到數組當中
hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg);
if (FAILED(hr))
{
wprintf(L"SafeArrayPutElement failed w/hr 0x%08lx\n", hr);
return;
}
// Invoke the method from the Type interface.
//使用指定的綁定約束和匹配的指定參數列表及區域性來調用指定成員。
hr = spType->InvokeMember_3(
bstrStaticMethodName, //字符串,它包含要調用的構造函數、方法、屬性或字段成員的名稱
static_cast(BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),
invokeAttr
BindingFlags
枚舉值的按位組合,這些值指定如何進行搜索。 訪問可以是 BindingFlags 之一,如 Public、NonPublic、Private、InvokeMethod 和 GetField 等。查找類型無需指定。如果省略查找的類型,則將使用 BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static。
NULL,?一個對象,該對象定義一組屬性并啟用綁定,而綁定可能涉及選擇重載方法、強制參數類型和通過反射調用成員。
vtEmpty,?對其調用指定成員的對象
psaStaticMethodArgs,?包含傳遞給要調用的成員的參數的數組
&vtPSInvokeReturnVal); ?表示要使用的全局化區域設置的對象,它對區域設置特定的轉換可能是必需的,比如將數字 String 轉換為 Double。
if (FAILED(hr))
{
wprintf(L"Failed to invoke InvokePS w/hr 0x%08lx\n", hr);
return;
}
else
{
// Print the output of the command
wprintf(vtPSInvokeReturnVal.bstrVal);
}
SafeArrayDestroy(psaStaticMethodArgs);
psaStaticMethodArgs = NULL;
}?
總結
以上是生活随笔為你收集整理的dll注入工具_UnmanagedPowerShell工具分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第五人格地毯怎么掀开(《第五人格》官方网
- 下一篇: jvm内存参数配置_idea中设置JVM