DLL注入學習總結
dll注入
所謂DLL 注入就是將一個DLL放進某個程序的地址空間裡,讓它成為那個程序的一部分。要實現DLL注入,首先需要開啟目標程序。
中文名 dll注入 外文名 hRemoteProcess 意 義 將一個DLL放進程序的地址空間裡 方 法 開啟目標程序
例:
hRemoteProcess = OpenProcess( PROCESS_CREATE_THREAD | //允許遠端建立執行緒
PROCESS_VM_OPERATION | //允許遠端VM操[2] 作
PROCESS_VM_WRITE, //允許遠端VM寫
FALSE, dwRemoteProcessId )
由於我們後面需要寫入遠端程序的記憶體地址空間並建立遠端執行緒,所以需要申請足夠的許可權(PROCESS_CREATE_THREAD、VM_OPERATION、VM_WRITE)。
如果程序打不開,以後的操作就別想了。程序開啟後,就可以建立遠執行緒了,不過別急,先想想這個遠執行緒的執行緒函式是什麼?我們的目的是注入一個DLL。而且我們知道用LoadLibrary可以載入一個DLL到本程序的地址空間。於是,自然會想到如果可以在目標程序中呼叫LoadLibrary,不就可以把DLL載入到目標程序的地址空間了嗎?對!就是這樣。遠執行緒就在這兒用了一次,建立的遠執行緒的執行緒函式就是LoadLibrary,而引數就是要注入的DLL的檔名。(這裡需要自己想一想,注意到了嗎,執行緒函式ThreadProc和LoadLibrary函式非常相似,返回值,引數個數都一樣) 還有一個問題,LoadLibrary這個函式的地址在哪兒?也許你會說,這個簡單,GetProcAddress就可以得出。於是程式碼就出來了。
char *pszLibFileRemote="my.dll";
PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");
CreateRemoteThread( hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL);
但是不對!不要忘了,這是遠執行緒,不是在你的程序裡,而pszLibFileRemote指向的是你的程序裡的資料,到了目標程序,這個指標都不知道指向哪兒去了,同樣pfnStartAddr這個地址上的程式碼到了目標程序裡也不知道是什麼了,不知道是不是你想要的LoadLibraryA了。但是,問題總是可以解決的,Windows有些很強大的API函式,他們可以在目標程序裡分配記憶體,可以將你的程序中的資料拷貝到目標程序中。因此pszLibFileRemote的問題可以解決了。
char *pszLibFileName="my.dll";//注意,這個一定要是全路徑檔名,除非它在系統目錄裡;原因大家自己想想。
//計算DLL路徑名需要的記憶體空間
int cb = (1 + lstrlenA(pszLibFileName)) * sizeof(char);
//使用VirtualAllocEx函式在遠端程序的記憶體地址空間分配DLL檔名緩衝區
pszLibFileRemote = (char *) VirtualAllocEx( hRemoteProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);
//使用WriteProcessMemory函式將DLL的路徑名複製到遠端程序的記憶體空間
iReturnCode = WriteProcessMemory(hRemoteProcess, pszLibFileRemote, (PVOID) pszLibFileName, cb, NULL);
OK,現在目標程序也認識pszLibFileRemote了,但是pfnStartAddr好像不好辦,我怎麼可能知道LoadLibraryA在目標程序中的地址呢?其實Windows為我們解決了這個問題,LoadLibraryA這個函式是在Kernel32.dll這個核心DLL裡的,而這個DLL很特殊,不管對於哪個程序,Windows總是把它載入到相同的地址上去。因此你的程序中LoadLibraryA的地址和目標程序中LoadLibraryA的地址是相同的(其實,這個DLL裡的所有函式都是如此)。至此,DLL注入結束了。
========
Dll注入經典方法完整版
http://pnig0s1992.blog.51cto.com/393390/804484/
Pnig0s1992:算是複習了,最經典的教科書式的Dll注入。
總結一下基本的注入過程,分注入和解除安裝
注入Dll:
1,OpenProcess獲得要注入程序的控制代碼
2,VirtualAllocEx在遠端程序中開闢出一段記憶體,長度為strlen(dllname)+1;
3,WriteProcessMemory將Dll的名字寫入第二步開闢出的記憶體中。
4,CreateRemoteThread將LoadLibraryA作為執行緒函式,引數為Dll的名稱,建立新執行緒
5,CloseHandle關閉執行緒控制代碼
解除安裝Dll:
1,CreateRemoteThread將GetModuleHandle注入到遠端程序中,引數為被注入的Dll名
2,GetExitCodeThread將執行緒退出的退出碼作為Dll模組的控制代碼值。
3,CloseHandle關閉執行緒控制代碼
3,CreateRemoteThread將FreeLibraryA注入到遠端程序中,引數為第二步獲得的控制代碼值。
4,WaitForSingleObject等待物件控制代碼返回
5,CloseHandle關閉執行緒及程序控制代碼。
#include <stdio.h> #include <Windows.h> #include <TlHelp32.h> DWORD getProcessHandle(LPCTSTR lpProcessName)//根據程序名查詢程序PID { DWORD dwRet = 0; HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if(hSnapShot == INVALID_HANDLE_VALUE) { printf("\n獲得程序快照失敗%d",GetLastError()); return dwRet; } PROCESSENTRY32 pe32;//宣告程序入口物件 pe32.dwSize = sizeof(PROCESSENTRY32);//填充程序入口物件大小 Process32First(hSnapShot,&pe32);//遍歷程序列表 do { if(!lstrcmp(pe32.szExeFile,lpProcessName))//查詢指定程序名的PID { dwRet = pe32.th32ProcessID; break; } } while (Process32Next(hSnapShot,&pe32)); CloseHandle(hSnapShot); return dwRet;//返回 } INT main(INT argc,CHAR * argv[]) { DWORD dwPid = getProcessHandle((LPCTSTR)argv[1]); LPCSTR lpDllName = "EvilDll.dll"; HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,FALSE,dwPid); if(hProcess == NULL) { printf("\n獲取程序控制代碼錯誤%d",GetLastError()); return -1; } DWORD dwSize = strlen(lpDllName)+1; DWORD dwHasWrite; LPVOID lpRemoteBuf = VirtualAllocEx(hProcess,NULL,dwSize,MEM_COMMIT,PAGE_READWRITE); if(WriteProcessMemory(hProcess,lpRemoteBuf,lpDllName,dwSize,&dwHasWrite)) { if(dwHasWrite != dwSize) { VirtualFreeEx(hProcess,lpRemoteBuf,dwSize,MEM_COMMIT); CloseHandle(hProcess); return -1; } }else { printf("\n寫入遠端程序記憶體空間出錯%d。",GetLastError()); CloseHandle(hProcess); return -1; } DWORD dwNewThreadId; LPVOID lpLoadDll = LoadLibraryA; HANDLE hNewRemoteThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)lpLoadDll,lpRemoteBuf,0,&dwNewThreadId); if(hNewRemoteThread == NULL) { printf("\n建立遠端執行緒失敗%d",GetLastError()); CloseHandle(hProcess); return -1; } WaitForSingleObject(hNewRemoteThread,INFINITE); CloseHandle(hNewRemoteThread); //準備解除安裝之前注入的Dll DWORD dwHandle,dwID; LPVOID pFunc = GetModuleHandleA;//獲得在遠端執行緒中被注入的Dll的控制代碼 HANDLE hThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)pFunc,lpRemoteBuf,0,&dwID); WaitForSingleObject(hThread,INFINITE); GetExitCodeThread(hThread,&dwHandle);//執行緒的結束碼即為Dll模組兒的控制代碼 CloseHandle(hThread); pFunc = FreeLibrary; hThread = CreateRemoteThread(hThread,NULL,0,(LPTHREAD_START_ROUTINE)pFunc,(LPVOID)dwHandle,0,&dwID); //將FreeLibraryA注入到遠端執行緒中去解除安裝Dll WaitForSingleObject(hThread,INFINITE); CloseHandle(hThread); CloseHandle(hProcess); return 0; }
DLL注入技術
http://blog.csdn.net/chenyujing1234/article/details/7860629
轉載自: http://hi.baidu.com/xwind85/blog/item/ae5332ad04bb7f034a36d662.html
一、DLL注入技術的用途
DLL注入技術的用途是很廣泛的,這主要體現在:
1、假如你要操縱的物件涉及的資料不在程序內;
2、你想對目標程序中的函式進行攔截(甚至API函式,嘿嘿,由此編寫個攔截timeGettime的過程,變速齒輪不就出來了麼?改天我試試),比如對它所屬視窗進行子類化。
3、你想編寫一些函式用於增強或增加目標程序功能,比如可以給目標程序的某個視窗插入個訊息迴圈增加其響應能力。(Mfc Windows程式設計稱之為訊息泵)。
4、隱藏自己的程式,很多惡意程式都是這樣做的,即使你將惡意程式的程序結束掉也毫無意義了,因為它自己已經插入到很多程序中去了,唯一有效的辦法只有登出windows.。如果你是個愛搞破壞的人就更應該掌握該技術了,不但可以利用該技術實現隱藏自己的程序,還可以破壞某個目標程序。因為將破壞程式碼插入到目標程序進行破壞的話簡直易如反掌。不信試試?:(
二、實現DLL注入的另一種方法
1、將DLL注入程序技術在實現Api函式的監視程式中不可缺少的一項工作。其中最常見的就是用SetWindowsHookEx函式實現了。不過,該方法的缺點是被監視的目標程序必須有視窗,這樣,SetWindowsHookEx才能將DLL注入目標程序中。而且,目標程式已經運行了,那麼,在視窗建立之前的Api函式就不能被Hook了。
2、另外一種方法用Debug方案,就可以實現在程式建立時監視所有的Api了,缺點是必須是目標程序的Debug源,在監視程式終了時,目標程序會無條件終了。最大的缺點就是無法除錯注入的DLL。
3、還有其他多種方案也可以實現DLL的注入,在《Windows核心程式設計》一書中就介紹了8-9種,其中有一種採用CreateProcess的方法,實現起來比較複雜,但沒有上面幾種方法的侷限性。且可以用其他工具(VC等)除錯注入的DLL。下面進行介紹。
原理如下:
1). 用CreateProcess(CREATE_SUSPENDED)啟動目標程序。
2). 找到目標程序的入口,用ImageHlp中的函式可以實現。
3). 將目標程序入口的程式碼儲存起來。
4). 在目標程序的入口寫入LoadLibrary(MyDll)實現Dll的注入。
5). 用ResumeThread執行目標程序。
6). 目標程序就運行了LoadLibrary(MyDll),實現DLL的注入。
7). 目標程序執行完LoadLibrary(MyDll)後,將原來的程式碼寫回目標程序的入口。
8). 目標程序Jmp至原來的入口,繼續執行程式。
從原理上可以看出,DLL的注入在目標程序的開始就運行了,而且不是用Debug的方案,這樣,就沒有上面方案的侷限性了。該方案的關鍵在6,7,8三步,實現方法需要監視程序和DLL合作。下面,結合程式碼進行分析。
在監視程序中,建立FileMapping,用來儲存目標程序的入口程式碼,同時保證DLL中可以訪問。在第7步實現將原目的碼寫回目標程序的入口。
// 監視程式和DLL共用的結構體
#pragma pack (push ,1) // 保證下面的結構體採用BYTE對齊(必須)
typedef struct
{
BYTE int_PUSHAD; // pushad 0x60
BYTE int_PUSH; // push &szDLL 0x68
DWORD push_Value; // &szDLL = "ApiSpy.dll"的path
BYTE int_MOVEAX; // move eax &LoadLibrary 0xB8
DWORD eax_Value; // &LoadLibrary
WORD call_eax; // call eax 0xD0FF(FF D0) (LoadLibrary("ApiSpy.dll");
BYTE jmp_MOVEAX; // move eax &ReplaceOldCode 0xB8
DWORD jmp_Value; // JMP的引數
WORD jmp_eax; // jmp eax 0xE0FF(FF E0) jmp ReplaceOldCode;
char szDLL[MAX_PATH]; // "ApiSpy.dll"的FullPath
}INJECT_LOADLIBRARY_CODE, *LPINJECT_CODE;
#pragma pack (pop , 1)
上面結構體的程式碼為彙編程式碼,對應的彙編為:
[cpp] view plain copy
pushad
push szDll
mov eax, &LoadLibraryA
call eax // 實現呼叫LoadLibrary(szDll)的程式碼
mov eax, oldentry
jmp eax // 實現在LoadLibrary執行完後, 跳至目標程序的入口繼續執行
// FileMaping的結構體
typedef struct
{
LPBYTE lpEntryPoint; // 目標程序的入口地址
BYTE oldcode[sizeof(INJECT_CODE)]; // 目標程序的程式碼儲存
}SPY_MEM_SHARE, * LPSPY_MEM_SHARE;
準備工作:
第一步:用CreateProcess(CREATE_SUSPENDED)啟動目標程序。
[cpp] view plain copy
CreateProcessA(0, szRunFile, 0, 0, FALSE, CREATE_SUSPENDED
0, NULL, &stInfo,
&m_proInfo) ;
用CreateProcess啟動一個暫停的目標程序;
找到目標程序的入口點,函式如下
第二步:找到目標程序的入口,用ImageHlp中的函式可以實現。
[cpp] view plain copy
pEntryPoint = GetExeEntryPoint(szRunFile);
LPBYTE GetExeEntryPoint(char *filename)
{
PIMAGE_NT_HEADERS pNTHeader;
DWORD pEntryPoint;
PLOADED_IMAGE pImage;
pImage = ImageLoad(filename, NULL);
if(pImage == NULL)
return NULL;
pNTHeader = pImage->FileHeader;
pEntryPoint = pNTHeader->OptionalHeader.AddressOfEntryPoint + pNTHeader->OptionalHeader.ImageBase;
ImageUnload(pImage);
return (LPBYTE)pEntryPoint;
}
// 建立FileMapping
[cpp] view plain copy
hMap = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL,
PAGE_READWRITE, 0, sizeof(SPY_MEM_SHARE), “MyDllMapView”);
// 儲存目標程序的程式碼
第三步:將目標程序入口的程式碼儲存起來。
[cpp] view plain copy
LPSPY_MEM_SHARE lpMap = pMapViewOfFile(hMap, FILE_MAP_ALL_ACCESS,0, 0, 0);
ReadProcessMemory(m_proInfo.hProcess, pEntryPoint,&lpMap->oldcode, sizeof(INJECT_CODE),&cBytesMoved);
lpMap->lpEntryPoint = pEntryPoint;
// 第四步:在目標程序的入口寫入LoadLibrary(MyDll)實現Dll的注入。
// 準備注入DLL的程式碼
[cpp] view plain copy
INJECT_CODE newCode;
// 寫入MyDll―――用全路徑
lstrcpy(newCode.szDLL, szMyDll);
// 準備硬程式碼(彙編程式碼)
newCode.int_PUSHAD = 0x60;
newCode.int_PUSH = 0x68;
newCode.int_MOVEAX = 0xB8;
newCode.call_eax = 0xD0FF;
newCode.jmp_MOVEAX = 0xB8;
newCode.jmp_eax = 0xE0FF;
newCode.eax_Value = (DWORD)&LoadLibrary;
newCode.push_Value=(pEntryPoint + offsetof(INJECT_CODE,szDLL));
// 將硬程式碼寫入目標程序的入口
// 修改記憶體屬性
DWORD dwNewFlg, dwOldFlg;
dwNewFlg = PAGE_READWRITE;
VirtualProtectEx(m_proInfo.hProcess, (LPVOID)pEntryPoint, sizeof(DWORD), dwNewFlg, &dwOldFlg);
WriteProcessMemory(m_proInfo.hProcess, pEntryPoint,&newCode, sizeof(newCode), NULL);//&dwWrited);
VirtualProtectEx(proInfo.hProcess, (LPVOID)pEntryPoint, sizeof(DWORD), dwOldFlg, &dwNewFlg);
// 釋放FileMaping 注意,不是Closehandle(hMap)
UnmapViewOfFile(lpMap);
// 繼續目標程序的執行
第五步:用ResumeThread執行目標程序。
[cpp] view plain copy
ResumeThread(m_proInfo.hThread);
在監視程序中就結束了自己的任務,剩下的第6,7,8步就需要在Dll的DllMain中進行配合。
DLL中用來儲存資料的結構體
[cpp] view plain copy
typedef struct
{
DWORD lpEntryPoint;
DWORD OldAddr;
DWORD OldCode[4];
}JMP_CODE,* LPJMP_CODE;
static JMP_CODE _lpCode;
// 在DllMain的DLL_PROCESS_ATTACH中呼叫InitApiSpy函式
// 在該函式中實現第6,7,8步
第六步:目標程序就運行了LoadLibrary(MyDll),實現DLL的注入。
[cpp] view plain copy
int WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
return InitApiSpy();
……
// InitApiSpy函式的實現
[cpp] view plain copy
BOOL WINAPI InitApiSpy()
{
HANDLE hMap;
LPSPY_MEM_SHARE lpMem;
DWORD dwSize;
BOOL rc;
BYTE* lpByte;
// 取得FileMapping的控制代碼
hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, “MyDllMapView”);
if(hMap)
{
lpMem = (LPSPY_MEM_SHARE)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0, 0, 0);
if(lpMem)
{
第七步:目標程序執行完LoadLibrary(MyDll)後,將原來的程式碼寫回目標程序的入口。
[cpp] view plain copy
BOOL WINAPI InitApiSpy()
{
HANDLE hMap;
LPSPY_MEM_SHARE lpMem;
DWORD dwSize;
BOOL rc;
BYTE* lpByte;
// 取得FileMapping的控制代碼
hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, “MyDllMapView”);
if(hMap)
{
lpMem = (LPSPY_MEM_SHARE)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0, 0, 0);
if(lpMem)
{
// 恢復目標程序的入口程式碼
// 得到mov eax, value程式碼的地址
_lpCode.OldAddr = (DWORD)((BYTE*)lpMem->lpEntryPoint + offsetof(INJECT_CODE, jmp_MOVEAX));
_lpCode.lpEntryPoint = (DWORD)lpMem->lpEntryPoint;
// 儲存LoadLibrary()後面的程式碼
memcpy(&_lpCode.OldCode, (BYTE*)lpMem->oldcode + offsetof(INJECT_CODE, jmp_MOVEAX), 2*sizeof(DWORD));
// 恢復目標程序的入口程式碼
rc = WriteProcessMemory(GetCurrentProcess(), lpMem->lpEntryPoint, lpMem->oldcode, sizeof(INJECT_CODE), &dwSize);
lpByte = (BYTE*)lpMem->lpEntryPoint + offsetof(INJECT_CODE, jmp_MOVEAX);
UnmapViewOfFile(lpMem);
}
CloseHandle(hMap);
}
// 實現自己Dll的其他功能,如匯入表的替換
// ……
// 將LoadLibrary後面的程式碼寫為轉入處理程式中
// 指令為:mov eax, objAddress
// jmp eax
{
BYTE* lpMovEax;
DWORD* lpMovEaxValu;
WORD* lpJmp;
DWORD fNew, fOld;
fNew = PAGE_READWRITE;
lpMovEax = lpByte;
VirtualProtect(lpMovEax, 2*sizeof(DWORD), fNew, &fOld);
*lpMovEax = 0xB8;
lpMovEaxValu = (DWORD*)(lpMovEax + 1);
*lpMovEaxValu = (DWORD)&DoJmpEntryPoint;
lpJmp = (WORD*)(lpMovEax + 5);
*lpJmp = 0xE0FF; // (FF E0)
VirtualProtect(lpMovEax, 2*sizeof(DWORD), fOld, &fNew);
}
return TRUE;
}
[cpp] view plain copy
// 轉入處理程式
DWORD* lpMovEax;
DWORD fNew, fOld;
void __declspec(naked) DoJmpEntryPoint ()
{
// 恢復LoadLibrary後面的程式碼
_gfNew = PAGE_READWRITE;
_glpMovEax = (DWORD*)_lpCode.OldAddr;
VirtualProtect(_glpMovEax, 2*sizeof(DWORD), _gfNew, &_gfOld);
*_glpMovEax = _lpCode.OldCode[0];
*(_glpMovEax + 1) = _lpCode.OldCode[1];
VirtualProtect(_glpMovEax, 2*sizeof(DWORD), _gfOld, &_gfNew);
//第八步:目標程序Jmp至原來的入口,繼續執行程式。
// 跳至目的碼的入口
_asm popad
_asm jmp _lpCode.lpEntryPoint
}
第八步:目標程序Jmp至原來的入口,繼續執行程式。
[cpp] view plain copy
// 跳至目的碼的入口
_asm popad
_asm jmp _lpCode.lpEntryPoint
}
這樣就實現了原來的目標,將DLL的注入放在目標程序的入口執行,實現了目標程序執行之前執行我們的注入Dll的功能。
========
https://blog.csdn.net/bcbobo21cn/article/details/70526865