windows-CODE注入(远程线程注入)
今天整理下代碼注入(遠程線程注入),所謂代碼注入,可以簡單的理解為是在指定內進程里申請一塊內存,然后把我們自己的執行代碼和一些變量拷貝進去(通常是以啟線程的方式),然后直接調用對方內存里我們拷貝進去的那部分代碼(創建一個線程)。這樣就行了,此時我們的這個線程就是目標進程的子線程了。但是要注意一點,代碼注入之所以能成功重點是:有些系統常用的dll里的某些函數,在不同的進程里面獲取到的地址是一樣的(此處注意,一樣的概念是指數值一樣,但是這個數值存的地方不一樣。也就是A里面的b變量的值等于C里面的d變量的值。這個要清楚,不然在學習API劫持的時候可能會迷茫)。
首先先介紹幾個需要的API
1.LoadLibrary() ?和 ?GetProcAddress()?? ? 這兩個函數,一個是加載dll庫,一個是從已經加載的庫句柄里拿出來某個函數的地址,可以理解成是把一個dll加到內存里,然后獲取里面某個函數的地址,得到這個地址后就可以直接調用了,這兩個簡單的函數經常用到,無論是常規調用還是靜態免殺都經常用。
2.OpenProcess()?
? ? 根據進程id獲取進程的句柄,也就是獲取進程操控權。
3.VirtualAllocEx()
? ? 在指定進程里開辟一塊內存,用于存放自己的代碼和參數。
4.WriteProcessMemory()
? ? 3里面的函數會在一個進程里開辟一塊內存,然后在那個內存里直接用本函數4進行數據寫入,就是在別人那開一塊內存然后寫自己的東西。
5.CreateRemoteThread()
? ? 最核心的函數,在指定進程的某個內存位置存儲的函數為線程函數,啟動一個線程,當然被啟動的這個線程屬于指定的這個進程。
? ? OK上面那5個API就是代碼注入需要的幾個基本的了,接下來我說一下我對線程注入的理解:
? ? 線程注入其實很好理解,就是說我們通過一定的手段在宿主也就是需要被注入的進程那獲取權限,得到權限之后我們要在這個進程上開辟一定的內存,然后把自己的線程函數內容以及參數什么的全都拷貝過去,這樣目標進程上有我們的函數,我們的參數,我們這個時候只是需要"幫"它啟動一下這個線程就OK了,直接用CreateRemoteThread函數在對方進程的某個內存位置的某個線程函數作為線程函數啟動。
? ? 然后上面的那個解釋是宏觀的,也就是泛泛而談,最最核心的思路是這樣:對于每個進程,他們調用的API大多都是自己隨時調用隨時獲取的,地址也不一樣,但是只有極少數的幾個系統核心dll他們不一樣,系統為了優化和統一管理,使得每個人load的dll然后從里面獲取的函數地址是一樣的,也就是大家共用一套已經被載入的dll地址,可以用的也是用的最多的就是User32.dll,kernel32.dll(我目前就知道這兩個,但是不重要只要知道一個kernel32.dll然后在里面做一個工廠就行了),既然大家用的地址都一樣,那么也就是說我在我本地直接獲取里面某個函數的地址,那么你的地址也是這個,那我直接幫你“慫恿”你去執行就行了唄。這樣代碼注入的思路可以總結成是:我們以所有進程加載的某些DLL地址是相同的,通過這個相同為跳板,“跳過去,然后幫宿主進程做事”,這樣想來有點局限,因為只有那么可憐的幾個dll大家的地址一樣,但是不用擔心,我下面會介紹一個我自創的思路,就是在宿主里面建造一個工廠,然后加載任意想加載的東西,如果我們每個產品都要自己生產,然后送貨,但是因為有很多貨是不合格的,都被扣押了,那么我們為什么不直接生產一個合格的工廠送過去,只要工廠被接受了,那么他所生產的所有產品都被接受了,也就是在宿主進程上加載的dll里面的api肯定和他自己加載的一樣啊,這樣突破了常規代碼注入函數受限制的弊端,下面注意問題的4還會說這個問題。
需要注意的問題:1.
? ? 參數結構體的結構體名字有說道,我之前沒注意這個問題,造成一定概率的注入失敗。
2.
? ? 注意64位32位問題,目前的經驗是64位的話就用64位程序注入,32位就用32位程序注入,這個應該可以解決,問題出現在獲取進程pid那。
3.
? ? 注意一個非常關鍵的問題,在代碼注入的時候,你所需要的任何變量最好自己以內存拷貝的方式傳過去,比如我直接在線程函數里開了一個變量 char[512] = "00000",當你把這個函數地址拷貝到指定進程里面去的時候,個"00000"所占的內容并沒有被拷貝過去,這樣你在調用的時候內存就會出錯。
4.
? ? 代碼注入限制非常多,被注入的線程函數里面內容非常有講究,大小有限制,存儲方式有限制,不能隨便調用API函數,比如直接在里面來了一個Sleep(1000),你覺得這個函數是系統的,在那都能調用,然而并不是,直接就內存錯誤,如果需要,你需要找到對應的API然后加載進去,然后調用,但是又不是所有的AIP函數地址都是統一的,那么怎么辦?我之前有一個解決辦法是直接導入兩個固定的地址LoadLibrary() ?和 ?GetProcAddress(),通常的代碼注入者的固定思路是靠這兩個函數獲取地址然后傳過去,然而大家很容易忽略一個思路,就是既然我們知道所有程序的LoadLibrary() ?和 ?GetProcAddress() 地址是一樣的,那么我們就直接本地獲取這兩個函數的地址,然后把這兩個地址傳到線程函數里,然后在里面在本地生產相應的函數,這樣我們加載的API都是目標線程能接受的API地址了,這個思路是當時睡覺時候想的,媽的激動地差點失眠(雖然現在回頭整理這些的時候已經不怎么激動了)。
5.
? ? 還有很多限制和注意問題,這里就不多說了,需要的可以自己去查一查,我知道的也不多,有的時候也是現用現查。
? ? OK上面是需要知道的思路和基礎,下面就直接說實現。
? ? 思路大體分這么幾個階段
1.提權 ? ? ? ? ? ? ?權限不夠的話可能到導致敏感函數執行失敗。
2.獲取pid ? ? ? ? 獲取宿主進程的pid
3.打開宿主進程 ? ?可以理解成是獲取宿主進程的操控權,當然這一步依賴于1的提權和2獲取的pid
4.初始化參數數據 ?這一步就是得到一些AIP地址了,參數了什么的,然后把它存在參數的結構體里。
5.在宿主進程里分配內存用于存放參數
6.把需要的參數全都拷貝到宿主繼承內存里(第4步獲取的內存位置)
7.在宿主進程里分配內存用于存放線程函數代碼
8.把線程函數直接拷到7獲取的內存里
9.啟動注入宿主進程的線程
10.善后,釋放一下垃圾什么的。
? ? 下面我給出一個最基本的代碼注入的代碼,功能是在qq或者explorer里面注入一個線程,線程就只是彈出來一個對話框(我資源里整理了一個測試項目http://download.csdn.net/detail/u013761036/9603026)。
.H #pragma once #include <Windows.h> #include <stdlib.h> #include <tlhelp32.h> #include <Psapi.h> #include <string>using std::string; using std::wstring;#pragma comment (lib,"Psapi.lib")#pragma warning(disable:4996)class CInjection { private:bool AdjustProcessTokenPrivilege();//提權bool Camp2str(wstring wsStrA ,wstring wsStrB);DWORD GetProcessIdByName(const wstring &wsProcessName);//獲取pidpublic:bool InjectionExeAndShowMessage(const wstring &wsProcessName); };.CPP #include "stdafx.h" #include "Injection.h"typedef struct _REMOTE_PARAMETER {CHAR cTitle[64];CHAR cBody[64];DWORD dwMessAgeBoxShowAddress; }RemotePara ,* PRemotePara;bool CInjection::AdjustProcessTokenPrivilege() {LUID luidTmp;HANDLE hToken;TOKEN_PRIVILEGES tkp;if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))return false;if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidTmp)){ CloseHandle(hToken);return FALSE;}tkp.PrivilegeCount = 1;tkp.Privileges[0].Luid = luidTmp;tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)){CloseHandle(hToken);return FALSE;}return true; }bool CInjection::Camp2str(wstring wsStrA ,wstring wsStrB) {int nSize = wsStrA.length();for(int i = 0 ;i < nSize ;i ++){if(wsStrA[i] >= 'A' && wsStrA[i] <= 'Z')wsStrA[i] += 'a'- 'A';}nSize = wsStrB.length();for(int i = 0 ;i < nSize ;i ++){if(wsStrB[i] >= 'A' && wsStrB[i] <= 'Z')wsStrB[i] += 'a'- 'A';}return wsStrA == wsStrB; }DWORD CInjection::GetProcessIdByName(const wstring &wsProcessName) {HANDLE hProcess = 0;DWORD dwProcess[2048] ,dwNeeded;TCHAR tcProcName[MAX_PATH] = {0};wstring wsNowProcessName = L"";int nTempSize = 0;int nPos = 0;EnumProcesses(dwProcess, sizeof(dwProcess), &dwNeeded);for(int i = 0 ;i < dwNeeded / sizeof(DWORD) ;i++){if(0 != dwProcess[i]){hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcess[i]);GetModuleFileNameEx(hProcess, NULL, tcProcName, MAX_PATH);nPos = wstring(tcProcName).find_last_of(L'\\');if(nPos != wstring::npos){wsNowProcessName = wstring(wstring(tcProcName).substr(nPos + 1));if(Camp2str(wsProcessName ,wsNowProcessName)){DWORD aa = dwProcess[i];return aa;}//if(wsProcessName == wsNowProcessName)// return dwProcess[i];}}}return 0; }//注入的線程函數 static DWORD __stdcall RemoteThread(PRemotePara myData) {typedef int (WINAPI *_MessageBoxA)(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType);_MessageBoxA mbAdd = (_MessageBoxA)myData->dwMessAgeBoxShowAddress;mbAdd(NULL ,myData->cBody ,myData->cTitle ,MB_OK);return 0; }bool CInjection::InjectionExeAndShowMessage(const wstring &wsProcessName) { //1.提權if(!AdjustProcessTokenPrivilege())return false;//2.獲取pidDWORD dwProPID = 0;if((dwProPID = GetProcessIdByName(wsProcessName)) == 0)return false;//3.打開進程HANDLE hProcess = NULL;if((hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE ,dwProPID)) == NULL)return false;//4.初始化參數數據RemotePara rpData = {0};ZeroMemory(&rpData,sizeof(RemotePara));HINSTANCE hInst=NULL;hInst=LoadLibrary(L"User32.dll");if(hInst == NULL)return false;rpData.dwMessAgeBoxShowAddress = 0;rpData.dwMessAgeBoxShowAddress = (DWORD)GetProcAddress(hInst,"MessageBoxA");if(rpData.dwMessAgeBoxShowAddress == 0)return false;FreeLibrary(hInst);strcat(rpData.cTitle ,"title");strcat(rpData.cBody ,"body");//RemoteThread(&rpData);//5.在宿主進程里分配內存,用于存參數PRemotePara pPara = NULL;pPara = (PRemotePara)VirtualAllocEx(hProcess , 0 ,sizeof(RemotePara) ,MEM_COMMIT,PAGE_READWRITE);if(pPara == NULL) return false;//6.把參數寫入宿主進程里,注意結構體的命名(_REMOTE_PARAMETER)if(!WriteProcessMemory(hProcess ,pPara ,&rpData ,sizeof(RemotePara) ,0))return false;//7.在宿主進程里分配內存,用于寫線程函數 1024這個值是我隨意填寫的,我覺得這么大可以存下上面那個線程函數void *pRemoteThr = VirtualAllocEx(hProcess , NULL ,1024 ,MEM_COMMIT | MEM_RESERVE ,PAGE_EXECUTE_READWRITE);if(pRemoteThr == NULL) return false;//8.把進程函數寫入分配的內存里if(!WriteProcessMemory(hProcess ,pRemoteThr ,&RemoteThread ,1024 ,0))return false;//9.啟動注入宿主進程的進程DWORD dwThreadId = 0;HANDLE hThread = CreateRemoteThread(hProcess ,0 ,0 ,(DWORD (WINAPI *)(LPVOID))pRemoteThr ,pPara ,0 ,&dwThreadId);if(!hThread) return false;//10.等待線程結束,然后清理內存 WaitForSingleObject(hThread ,INFINITE);CloseHandle(hThread);VirtualFreeEx(hProcess ,pPara ,0 ,MEM_RELEASE);VirtualFreeEx(hProcess ,pRemoteThr ,0 ,MEM_RELEASE);CloseHandle(hProcess);return true; }USER #include "stdafx.h" #include "ZZZTest.h" #include "Injection.h"int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPTSTR lpCmdLine,_In_ int nCmdShow) {UNREFERENCED_PARAMETER(hPrevInstance);UNREFERENCED_PARAMETER(lpCmdLine);CInjection *pciTest = new CInjection();pciTest->InjectionExeAndShowMessage(L"qq.exe"); //explorer.exedelete pciTest;return 0; }
總結
以上是生活随笔為你收集整理的windows-CODE注入(远程线程注入)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 汇编环境搭建(vs2010(2012)+
- 下一篇: windows-DLL注入