更新了已經(jīng)過測(cè)試-Windows下如何改寫目標(biāo)進(jìn)程的窗口函數(shù)來注入DLL 收藏 此文于2011-02-23被推薦到CSDN首頁
如何被推薦?
Windows的UI線程簡(jiǎn)單地說就是創(chuàng)建了窗口的線程,
其創(chuàng)建的窗口都有窗口函數(shù),在這里,我介紹一個(gè)改寫
UI線程窗口過程來注入DLL的方法,需要調(diào)用VirtualAllocEx,
但不需要GetThreadContext和SetThreadContext
也不調(diào)用CreateRemoteThread.
對(duì)付一些常用的殺毒軟件應(yīng)該沒有問題,下面是具體代碼,包含
說明
一:主控程序
注入Dll.cpp
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#define INJECTDLL_NAME "c:\\dlltest.dll"
//0x00cd0000,不用這個(gè)地址了,0x00f00000地址我測(cè)過,對(duì)于Mspaint.exe(畫圖程序),
//WordPad.exe(寫字板程序)和我們無辜的Notepad.exe(記事本),都好用。至于
//其它的進(jìn)程,請(qǐng)讀者們多試試吧
#define REMOTE_BASEADDR 0x00f00000
typedef HMODULE (__stdcall *pLoadLibrary)(LPCSTR lpLibFileName);
typedef HANDLE (__stdcall *pCreateThread)(
LPSECURITY_ATTRIBUTES lpThreadAttributes,\
DWORD dwStackSize,\
LPTHREAD_START_ROUTINE lpStartAddress,\
LPVOID lpParameter,\
DWORD dwCreationFlags,\
LPDWORD lpThreadId
);
typedef HANDLE (__stdcall *lpOpenThread)( DWORD dwDesiredAccess, \
BOOL bInheritHandle, \
DWORD dwThreadId);
//要覆蓋目標(biāo)窗口函數(shù)的函數(shù)
long __stdcall myProc(HWND ,UINT ,WPARAM ,LPARAM )
{
//獲取CreateThread 函數(shù)地址,絕對(duì)地址,在每個(gè)進(jìn)程中都一樣
//關(guān)于如何獲取CreateThread 函數(shù)和LoadLibrary的絕對(duì)地址
//的辦法其實(shí)很簡(jiǎn)單:
// DWORD p1 = (DWORD)::GetProcAddress(::GetModuleHandle("Kernel32.dll"),
// "CreateThread")
// DWORD p2 = (DWORD)::GetProcAddress(::GetModuleHandle("Kernel32.dll"),
// "LoadLibraryA")
//將 p1和p2的值打印出來直接使用就行。
pCreateThread func = pCreateThread(2088830679);
//當(dāng)該函數(shù)在目標(biāo)進(jìn)程被當(dāng)做窗口函數(shù)調(diào)用時(shí),下面一行代碼將
//在目標(biāo)進(jìn)程內(nèi)創(chuàng)建線程,也就是在本進(jìn)程內(nèi)創(chuàng)建線程,可不是
//CreateRemoteThread 創(chuàng)建遠(yuǎn)程線程哦,這一點(diǎn)請(qǐng)看家體會(huì)。
//將LoadLibrary的絕對(duì)地址(2088770939)
//作為線程入口函數(shù)地址
//REMOTE_BASEADDR(0x00f00000)地址就是指定的分配虛擬內(nèi)存的地址,不能用變量
//因?yàn)橛昧俗兞烤彤a(chǎn)生尋址操作了,在目標(biāo)進(jìn)程中就讀取不到正確的
//值了。REMOTE_BASEADDR(0x00f00000)地址處主控程序?qū)懭?c:\\dlltest.dll"-->具體在下面講述
func(0,0,LPTHREAD_START_ROUTINE(2088770939),LPVOID(REMOTE_BASEADDR),0,0);
return 0;
}
//控制函數(shù)
//參數(shù)必須是 UNICODE 字符串 const unsigned short 等價(jià)于 const wchar_t
void Inject_WndProc(const unsigned short *pszWndClassName)
{
BYTE *pStart;
BOOL bOk;
DWORD dErr;
HANDLE hfileMap;
LPVOID pAddr;
HANDLE hThread = 0;
LPVOID pRemote = 0;
DWORD dWriten = 0;
//之前定義長(zhǎng)度是1024,測(cè)試時(shí)發(fā)現(xiàn)除了Notepad.exe可以成功之外
//MsPaint.exe和WordPad.exe都不成功,原因就是 1024長(zhǎng)度太大
//導(dǎo)致除了覆蓋了目標(biāo)的窗口程序外,把當(dāng)前目標(biāo)線程的執(zhí)行代碼也覆蓋
//掉了,導(dǎo)致喚醒目標(biāo)線程后目標(biāo)線程立即崩潰,改成110字節(jié)就好多了,
//因?yàn)槲覀冏⑷敫采w的源函數(shù)體沒多少字節(jié),一個(gè)類型轉(zhuǎn)換(3,4字節(jié))和一個(gè)
//函數(shù)調(diào)用(4次參數(shù)進(jìn)棧,一次跳轉(zhuǎn)和清理堆棧,這幾部大概是2*4+5
// + n, n肯定小于90字節(jié)),總的算來110長(zhǎng)度足夠了。
//對(duì)于一些進(jìn)程的窗口函數(shù)如果里面僅僅是調(diào)用另外一個(gè)函數(shù)的話那就需要
//繼續(xù)調(diào)小這個(gè)數(shù)值了,一般的窗口過程幾乎都包含switch和if等分支語句
//這樣就足夠了
BYTE sCodes[110];
memset(sCodes,0,sizeof(sCodes));
//通過窗口類名來調(diào)用 FindWindowW,避免目標(biāo)窗口標(biāo)題經(jīng)常變化導(dǎo)致
//FindWindowW(NULL,"window title") 失效
HWND hRemote = ::FindWindowW(pszWndClassName,NULL);
//獲取目標(biāo)進(jìn)程窗口類的注冊(cè)窗口函數(shù)地址,不能用GetWindowLongW
//因?yàn)镚etWindowLongW不能跨進(jìn)程
DWORD lpWndProc = (DWORD)::GetClassLongW(hRemote,GCL_WNDPROC);
//獲取目標(biāo)窗口所屬進(jìn)程和創(chuàng)建者線程
DWORD id,old;
DWORD threadId = ::GetWindowThreadProcessId(hRemote,&id);
LPVOID pMemRemote = 0;
lpOpenThread pOpenThread = 0;
HANDLE hProcess = ::OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ |
PROCESS_VM_WRITE,
FALSE,id);
if(!hProcess)
{
MessageBox(0,"OpenProcess failed!",0,0);
return;
}
//從REMOTE_BASEADDR(0x00f00000)地址處分配虛擬內(nèi)存,此絕對(duì)地址可以通過
//::VirtualAllocEx 調(diào)用獲取,
//但為了不在注入的函數(shù)代碼中產(chǎn)生尋址操作,因此使用絕對(duì)地址。
//Notepad.exe,MSPaint.exe,WordPad.exe可以使用這個(gè)絕對(duì)地址,
//別的進(jìn)程需要同過 ::VirtualAllocEx 來獲取。
//后面提供獲取這個(gè)絕對(duì)地址的函數(shù)
pMemRemote = ::VirtualAllocEx(hProcess,(LPVOID) REMOTE_BASEADDR,
8192,MEM_RESERVE|MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if(pMemRemote != (LPVOID)REMOTE_BASEADDR)
{
if(pMemRemote)
{
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
}
MessageBox(0,"VirtualAllocEx failed!",0,0);
goto DOEXIT;
}
//將"C:\\dlltest.dll"寫入
bOk = ::WriteProcessMemory(hProcess,pMemRemote,INJECTDLL_NAME,
strlen(INJECTDLL_NAME),&dWriten);
if(!bOk)
{
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
MessageBox(0,"WriteProcessMemory failed!",0,0);
goto DOEXIT;
}
//獲取OpenThread函數(shù)地址
pOpenThread = (lpOpenThread)::GetProcAddress(::GetModuleHandle("Kernel32.dll"),
"OpenThread");
if(!pOpenThread)
{
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
MessageBox(0,"GetProcAddress failed!",0,0);
goto DOEXIT;
}
hThread = pOpenThread(THREAD_SUSPEND_RESUME|SYNCHRONIZE,FALSE,threadId);
//掛起目標(biāo)線程
::SuspendThread(hThread);
//創(chuàng)建共享內(nèi)存
hfileMap = ::CreateFileMappingW(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,
4096,L"注入dlltest范例");
if(!hfileMap)
{
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
::ResumeThread(hThread);
MessageBox(0,"CreateFileMapping failed!",0,0);
goto DOEXIT;
}
//映射共享內(nèi)存地址
pAddr = ::MapViewOfFile(hfileMap,FILE_MAP_ALL_ACCESS,0,0,4096);
if(!pAddr)
{
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
::CloseHandle(hfileMap);
::ResumeThread(hThread);
MessageBox(0,"MapViewOfFile failed!",0,0);
goto DOEXIT;
}
//將目標(biāo)窗口函數(shù)地址所在內(nèi)存頁面更改為 PAGE_EXECUTE_READWRITE
bOk = ::VirtualProtectEx(hProcess,(LPVOID)lpWndProc,8192,PAGE_EXECUTE_READWRITE,&old);
if(!bOk)
{
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
::CloseHandle(hfileMap);
::ResumeThread(hThread);
MessageBox(0,"VirtualProtectEx failed!",0,0);
}
//將目標(biāo)窗口函數(shù)地址的110字節(jié)讀入sCodes,保證dll中DllMain恢復(fù)之用
bOk = ::ReadProcessMemory(hProcess,(LPVOID)lpWndProc,sCodes,110,&dWriten);
if(!bOk)
{
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
::CloseHandle(hfileMap);
::ResumeThread(hThread);
MessageBox(0,"ReadProcessMemory failed!",0,0);
goto DOEXIT;
}
pStart = (BYTE*)pAddr;
//將目標(biāo)窗口句柄寫入共享內(nèi)存中
::CopyMemory(pStart,&hRemote,sizeof(HWND));
pStart += sizeof(HWND);
//將目標(biāo)窗口函數(shù)110字節(jié)內(nèi)容寫入共享內(nèi)存中
::CopyMemory(pStart,sCodes,110);
pStart += 110;
//僅為調(diào)試時(shí)驗(yàn)證之用
::CopyMemory(pStart,"#$#",3);
//將目標(biāo)窗口過程函數(shù)覆蓋
bOk = ::WriteProcessMemory(hProcess,(LPVOID)lpWndProc,(LPVOID)myProc,
110,&dWriten);
if(!bOk)
{
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
::CloseHandle(hfileMap);
::ResumeThread(hThread);
MessageBox(0,"GetProcAddress failed!",0,0);
goto DOEXIT;
}
//喚醒目標(biāo)線程
::ResumeThread(hThread);
//以下是向目標(biāo)線程消息隊(duì)列發(fā)送WM_PAINT消息,從而
//調(diào)用被覆蓋的窗口函數(shù)。
::PostMessage(hRemote,WM_PAINT,0,0);
//等待目標(biāo)線程結(jié)束,只要不退出,睡眠也行
//本想在主控程序和dll中用事件內(nèi)核對(duì)象做個(gè)同步
//當(dāng)DllMain函數(shù)結(jié)束后通知主控程序,但考慮同步的問題
//不屬于注入dll的范疇,就懶得寫了,其實(shí)很容易實(shí)現(xiàn)。
::WaitForSingleObject(hThread,-1);
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
DOEXIT:
::CloseHandle(hThread);
::CloseHandle(hProcess);
}
//程序入口 WinMain
int APIENTRY WinMain( HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//Notepad.exe 記事本 進(jìn)程的主窗口類名是 "NOTEPAD",
//WordPad.exe 寫字板 進(jìn)程的主窗口類名是 "WordPadClass"
//MsPaint.exe 畫圖 進(jìn)程的主窗口類名是 "MsPaintApp"
//注意:必須是主窗口,不能是子窗口,就是說像 BUTTON,EDIT,STATIC
//等子窗口都不行,主窗口類名通過Spy++獲取
Inject_WndProc(L"NOTEPAD");
return 0;
}
//重點(diǎn)說明一下:主控程序不能在Debug模式下運(yùn)行,因?yàn)?/div>
//Debug模式下 myProc函數(shù)結(jié)束處包含了堆棧的驗(yàn)證代碼
//覆蓋目標(biāo)窗口函數(shù)后運(yùn)行時(shí)會(huì)導(dǎo)致堆棧的驗(yàn)證代碼無法通過
//產(chǎn)生異常,因此只能在Release下運(yùn)行。
二:dll程序
dlltest.cpp
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
//g_WndPRoc保存窗口函數(shù)地址
WNDPROC g_WndPRoc = 0;
BOOL g_IsRestore = FALSE;
//hook目標(biāo)窗口函數(shù)的函數(shù)
LRESULT CALLBACK newWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//攔截一下 WM_SYSCOMMAND 彈個(gè)消息框
if(g_WndPRoc)
{
/*
//unhook 目標(biāo)窗口函數(shù),記住不要破壞,要和諧,這里僅僅是測(cè)試一下,所以注釋掉了
//能直觀的體現(xiàn)出效果,否則應(yīng)該去掉注釋,還原目標(biāo)窗口函數(shù)
if(g_IsRestore)
{
::SetWindowLongW(hWnd,GWL_WNDPROC,(long)g_WndPRoc);
return 1;
}
*/
LRESULT lr = ::CallWindowProcW(g_WndPRoc,hWnd,uMsg,wParam,lParam);
if(WM_SYSCOMMAND == uMsg)
{
MessageBox(0,"newWndProc in dlltest.dll",0,0);
}
return lr;
}
return 1;
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
if(DLL_PROCESS_DETACH == ul_reason_for_call)
{
MessageBox(0,"Exit",0,0);
}
if(DLL_PROCESS_ATTACH == ul_reason_for_call)
{
__try
{
DWORD len;
char sBuf[128];
memset(sBuf,0,sizeof(sBuf));
SYSTEMTIME sm;
::GetLocalTime(&sm);
_snprintf(sBuf,127,"dlltest.dll load log %04d-%02d-%02d %02d:%02d:%02d\r\n",
sm.wYear,sm.wMonth,sm.wDay,sm.wHour,sm.wMinute,sm.wSecond);
HANDLE hFile = ::CreateFile("C:\\testdll.log",GENERIC_WRITE,
FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
::SetFilePointer(hFile,0,0,FILE_END);
::WriteFile(hFile,sBuf,strlen(sBuf),&len,0);
::CloseHandle(hFile);
//以上是記錄日志,這里不能調(diào)用UI函數(shù),例如MessageBox等,因?yàn)闀?huì)導(dǎo)致
//窗口函數(shù)重入,崩潰。
//以下打開共享內(nèi)存,恢復(fù)窗口函數(shù)
HANDLE hfileMap = ::OpenFileMappingW(FILE_MAP_READ,FALSE,L"注入dlltest范例");
LPVOID pAddr = ::MapViewOfFile(hfileMap,FILE_MAP_READ,0,0,4096);
HWND hMain;
BYTE *pStart = (BYTE*)pAddr;
BYTE sCodes[110];
//從共享內(nèi)存中讀取目標(biāo)窗口句柄
::CopyMemory(&hMain,pStart,sizeof(HWND));
pStart += sizeof(HWND);
//從共享內(nèi)存中讀取目標(biāo)窗口函數(shù)110字節(jié)內(nèi)容
::CopyMemory(sCodes,pStart,110);
pStart += 110;
//hook窗口過程,使得殺毒軟件進(jìn)程如果卸載本dll的話
//則窗口過程就失效,導(dǎo)致進(jìn)程崩潰?。ㄉ饔弥?,因?yàn)?/div>
//經(jīng)測(cè)試發(fā)現(xiàn)寫字板程序的一菜單些功能失效了,原因就是dll的
//內(nèi)存模式與exe的內(nèi)存模式不一樣,也就是說在dll函數(shù)中
//C的malloc、C++的new經(jīng)常會(huì)失敗,原因也是如此,因此
//還是不hook窗口過程好,記住不要搞破壞,要和諧 呵呵)
//
//
//先hook窗口過程好,因?yàn)镾etWindowLongW函數(shù)保證
//了目標(biāo)窗口函數(shù)調(diào)用結(jié)束后如果消息隊(duì)列不空,則立即執(zhí)行
//新的窗口函數(shù)。因?yàn)樵谀繕?biāo)窗口函數(shù)未恢復(fù)之前,目標(biāo)窗口
//執(zhí)行的是我們覆蓋的函數(shù),因此如果不先重定向窗口函數(shù)
//就CopyMemory的話,則有可能破壞目標(biāo)線程的執(zhí)行代碼
//造成崩潰。
DWORD wndProc = (DWORD)::GetClassLongW(hMain,GCL_WNDPROC);
g_WndPRoc = (WNDPROC)wndProc;
g_IsRestore = FALSE;
//注釋掉,不hook DWORD dOld = ::SetWindowLongW(hMain,GWL_WNDPROC,(long)newWndProc);
//恢復(fù)窗口函數(shù)的110字節(jié)
::CopyMemory((LPVOID)wndProc,sCodes,110);
g_IsRestore = TRUE;
}
__except(1)
{
}
}
return TRUE;
}
三:提供一個(gè)工具函數(shù)獲取目標(biāo)進(jìn)程可以分配虛擬內(nèi)存的地址的函數(shù)
//使用下面函數(shù)得到的絕對(duì)地址值,將這個(gè)值用在上面主控程序中
LPVOID GetProcessAddr(DWORD dProcessID)
{
HANDLE hProcess = ::OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ |
PROCESS_VM_WRITE,
FALSE,dProcessID);
if(!hProcess)
{
MessageBox(0,"OpenProcess failed!",0,0);
return NULL;
}
LPVOID pMemRemote = ::VirtualAllocEx(hProcess,NULL,
8192,MEM_RESERVE|MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if(!pMemRemote)
{
::CloseHandle(hProcess);
MessageBox(0,"VirtualAllocEx failed!",0,0);
return NULL;
}
::VirtualFreeEx(hProcess,pMemRemote,0,MEM_RELEASE);
::CloseHandle(hProcess);
return pMemRemote;
}
以上代碼都經(jīng)過本人測(cè)試,確認(rèn)運(yùn)行無誤。
后記:從開始有想法開始到今天完成了這個(gè)注入dll的比較“新”
(應(yīng)該是有不少人早就實(shí)現(xiàn)了,只不過沒拿出來共享而已)
的方法用了3天時(shí)間,其實(shí)技術(shù)上沒多少難度,關(guān)鍵是要對(duì)
windows操作系統(tǒng)的窗口消息、進(jìn)程、線程、動(dòng)態(tài)鏈接庫,
內(nèi)存管理,共享內(nèi)存等等都熟練,記住,不能僅僅是了解,
必須深入理解到熟練API編程等。知識(shí)的積累很重要,對(duì)于
一些初入門的開發(fā)人員來說做到深入理解了操作系統(tǒng)就等于
邁進(jìn)了一大步,希望這篇文章(都是代碼,不正規(guī)的文章啊...汗!)
能給予IT行業(yè)內(nèi)追求技術(shù)發(fā)展的同行們一點(diǎn)點(diǎn)幫助。
這個(gè)方法目前的缺點(diǎn)很明顯,就是用的API和內(nèi)存的絕對(duì)地址值,
沒有用到經(jīng)典的shellcode的計(jì)算偏移量的技術(shù),因?yàn)槲业?/div>
匯編語言語法和編寫格式都忘了,CPU寄存器也已經(jīng)不會(huì)
用了,因此就沒有用匯編去寫一些計(jì)算偏移量的代碼(看看吧
只是不用就都忘了,想用的時(shí)候也不容易了),希望讀者也能給
與我這方面的幫助,在此不勝感謝!
我的EMail: tanglg@neusoft.com (公司郵箱,建議使用,因?yàn)槟壳霸诼?
tanglg00@sina.com (私人郵箱,不建議使用,
因?yàn)楣酒帘瘟怂饺肃]箱,
或許某天辭職了就會(huì)用到了)
附調(diào)試dlltest.dll的方法:
使用VC6(VC2005/VC2010沒試過)創(chuàng)建win32 動(dòng)態(tài)鏈接庫
將 dlltest.cpp中的代碼以Build 模式 Debug 編譯鏈接,設(shè)置鏈接
輸出為:c:\dlltest.dll
設(shè)置調(diào)試程序?yàn)?C:\windows\system32\notepad.exe。
最后F5啟動(dòng)調(diào)試,在dllMain里添加斷點(diǎn)
直接運(yùn)行編譯鏈接(Release)好的主控程序(VC6->win32 Application)
如果注入成功,就會(huì)進(jìn)入dll調(diào)試斷點(diǎn)。很簡(jiǎn)單就這么玩兒。
注意xp下一定要用擁有超級(jí)用戶權(quán)限的用戶才能注入成功。
在此重申:不可以用本人提供的方法從事
計(jì)算機(jī)軟件破壞違法的行為,
這里僅僅是技術(shù)共享。如有違
法的事情發(fā)生,與本人無關(guān)
本文來自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/qman007/archive/2011/02/21/6198527.aspx
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)
點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存
查看更多類似文章