如何實現.net程序的進程註入
如何實現.net程序的進程註入
周銀輝
進程註入比較常見,比如用IDE調試程序以及一些Spy程序,如果僅僅為了與調試器通訊,可以使用.net提供的Debugger接口(在EnvDTE.dll的EnvDTE命名空間下).但無論出於什麽目的,進程註入都是比較好玩的事情,所以不妨一試 . 進程註入的方法貌似很多(比如像特洛伊一樣喬裝打扮讓目標進程誤認為你的程序集合法而加載到目標進程),這裏提到的僅是其中的一種或某些方法的結合.
大致原理是這樣的:
- 源進程(也就是你的代碼所在的進程)獲得目標進程(也就是你的註入目標所在的進程)的ID或進程對象
- 源進程提供一回調函數methodA(也就是你想要註入到目標進程後所執行的代碼)
- 將目標進程和回調函數methodA的完整路徑(其所在的Assembly,Classic以及MethodName)提交給Injector(也就是我們編寫的負責註入的類),讓Injector來完成註入和讓目標進程執行回調函數
- Injector根據提供的目標進程ID取得目標進程對象,並獲得目標進程的一個線程(我們稱為目標線程)
- 在目標線程中分配一塊內存,將回調函數methodA的完整路徑作為字符串存入該內存中
- Injector在目標進程中安裝一個鉤子(Hook)監視某一個Windows消息(messageA),撰寫鉤子的回調函數methodB(該方法中的內容稍後解釋)
- 像目標進程發消息messageA,並將剛才分配的內存的基地址作為消息參數傳遞.
- 由於我們針對messageA安裝了鉤子,所以目標進程會調用我們鉤子函數methodB,並會把分配的內存的基地址包含在函數參數中
- methodB中, 根據函數參數中的內存基地址在內存中解析出其實際對象,也就是一個表示我們的methodA的完整路徑的字符串.根據該字符串中所表示的Assembly,className, methodName利用.net反射,反射出其MethodInfo對象(註意,關鍵點,methodB被回調時已經是在目標進程的某個線程中了)
- Invoke反射出的MethodInfo對象, 我們的methodA得到了執行.
下面這個圖可能會幫助你理解上面的話:
如果還沒明白的話,那就看代碼吧(這需要一點點C++/CLI知識,但我已經為每句加上了註釋,應該蠻好懂的,關於C++/CLI可以點擊這裏了解更多.)
#include "stdafx.h" #include "Injector.h" #include <vcclr.h> using namespace ManagedInjector; //defines a new window message that is guaranteed to be unique throughout the system. //The message value can be used when sending or posting messages. static unsigned int WM_GOBABYGO = ::RegisterWindowMessage(L"Injector_GOBABYGO!"); static HHOOK _messageHookHandle; //----------------------------------------------------------------------------- //Spying Process functions follow //----------------------------------------------------------------------------- void Injector::Launch(System::IntPtr windowHandle, System::Reflection::Assembly^ assembly, System::String^ className, System::String^ methodName) { System::String^ assemblyClassAndMethod = assembly->Location + "$" + className + "$" + methodName; //convert String to local wchar_t* or char* pin_ptr<const wchar_t> acmLocal = PtrToStringChars(assemblyClassAndMethod); //Maps the specified executable module into the address space of the calling process. HINSTANCE hinstDLL = ::LoadLibrary((LPCTSTR) _T("ManagedInjector.dll")); if (hinstDLL) { DWORD processID = 0; //get the process id and thread id DWORD threadID = ::GetWindowThreadProcessId((HWND)windowHandle.ToPointer(), &processID); if (processID) { //get the target process object (handle) HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID); if (hProcess) { int buffLen = (assemblyClassAndMethod->Length + 1) * sizeof(wchar_t); //Allocates physical storage in memory or in the paging file on disk for the specified reserved memory pages. //The function initializes the memory to zero. //The return value is the base address of the allocated region of pages. void* acmRemote = ::VirtualAllocEx(hProcess, NULL, buffLen, MEM_COMMIT, PAGE_READWRITE); if (acmRemote) { //copies the data(the assemblyClassAndMethod string) //from the specified buffer in the current process //to the address range of the target process ::WriteProcessMemory(hProcess, acmRemote, acmLocal, buffLen, NULL); //Retrieves the address of MessageHookProc method from the hintsDLL HOOKPROC procAddress = (HOOKPROC)GetProcAddress(hinstDLL, "MessageHookProc"); //install a hook procedure to the target thread(before the system sends the messages to the destination window procedure) _messageHookHandle = ::SetWindowsHookEx(WH_CALLWNDPROC, procAddress, hinstDLL, threadID); if (_messageHookHandle) { //send our custom message to the target window of the target process ::SendMessage((HWND)windowHandle.ToPointer(), WM_GOBABYGO, (WPARAM)acmRemote, 0); //removes the hook procedure installed in a hook chain by the SetWindowsHookEx function. ::UnhookWindowsHookEx(_messageHookHandle); } //removes a hook procedure installed in a hook chain by the SetWindowsHookEx function. ::VirtualFreeEx(hProcess, acmRemote, buffLen, MEM_RELEASE); } ::CloseHandle(hProcess); } } //Decrements the reference count of the loaded DLL ::FreeLibrary(hinstDLL); } } __declspec( dllexport ) // The procedure for hooking, this will be called back after hooked int __stdcall MessageHookProc(int nCode, WPARAM wparam, LPARAM lparam) { //HC_ACTION: indicate that there are argments in wparam and lparam if (nCode == HC_ACTION) { CWPSTRUCT* msg = (CWPSTRUCT*)lparam; //when the target window received our custom message if (msg != NULL && msg->message == WM_GOBABYGO) { //get the argument passed by the message //actually, the argument is the base address (a pointer) //of the assemblyClassAndMethod string in the target process memory wchar_t* acmRemote = (wchar_t*)msg->wParam; //gcnew: creates an instance of a managed type (reference or value type) on the garbage collected heap System::String^ acmLocal = gcnew System::String(acmRemote); //split the string into substring array with $. Under this context: //acmSplit[0]:the assembly‘s location //acmSplit[1]:className; //acmSplit[2]:methodName //we use these infomation to reflect the method in the source assembly, and invoke it in the target process cli::array<System::String^>^ acmSplit = acmLocal->Split(‘$‘); //refect the method, and invoke it System::Reflection::Assembly^ assembly = System::Reflection::Assembly::LoadFile(acmSplit[0]); if (assembly != nullptr) { System::Type^ type = assembly->GetType(acmSplit[1]); if (type != nullptr) { System::Reflection::MethodInfo^ methodInfo = type->GetMethod(acmSplit[2], System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::Public); if (methodInfo != nullptr) { methodInfo->Invoke(nullptr, nullptr); } } } } } return CallNextHookEx(_messageHookHandle, nCode, wparam, lparam); }
接下來,做個DEMO嘗試一下:
解決方案中的InjectorDemo就是我們上述的源進程,它會利用Injector將下面這段代碼註入到Target進程中並執行:
public static void DoSomethingEvie()
{
vartargetWindow = Application.Current.MainWindow;
if(targetWindow != null)
{
varlb = newLabel{Content = "haha, i caught you :)"};
targetWindow.Content = lb;
}
}
也就是說InjectorDemo進程會將InjectTargetApp進程的主窗口的內容修改成"haha, i caught you"這樣的一個Label.
運行程序:
上面的兩個窗口分別處於不同的進程中, 點擊 "Inject it" 按鈕, 其輝調用如下代碼:
ManagedInjector.Injector.Launch(targetProcess.MainWindowHandle, typeof(InjectorWindow).Assembly, typeof(InjectorWindow).FullName, "DoSomethingEvie");
然後:
點擊這裏下載Demo
僅供參考(日誌內容僅僅記錄了一種存在性,並非特定問題的解決方案,討論其意義性是毫無意義的,謝謝)
如何實現.net程序的進程註入