windows核心編程3(DllMain,SetWindowsHookEx,結構化異常處理等)

來源:互聯網
上載者:User

本文轉自:http://hi.baidu.com/huguosheng/blog/item/1f925438eb826cf1b311c7f0.html

1DLL的進入點函數DllMain
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD ul_reason_for_call,
                       LPVOID lpReserved
      )
{

    switch (ul_reason_for_call)
{
   case DLL_PROCESS_ATTACH:
   case DLL_THREAD_ATTACH:
   case DLL_THREAD_DETACH:
   case DLL_PROCESS_DETACH:
    break;
    }
    return TRUE;
}

DLL_PROCESS_ATTACH:當DLL初次映射到進程的地址空間時,系統將調用DllMain,並為ul_reason_for_call傳遞值DLL_PROCESS_ATTACH。此時DLL可以執行任何與進程相關的初始化。執行個體,如果我將DLL注入到一個新的進程後,如果要自動執行一些任務,則代碼寫在case DLL_PROCESS_ATTACH:後。
DLL_PROCESS_DETACH:當DLL從進程的地址空間被卸載後,系統將調用DllMain,並為ul_reason_for_call傳遞值DLL_PROCESS_DETACH。例如調用FreeLibrary,先調用DllMain完成DLL_PROCESS_DETACH通知後,才從FreeLibrary中返回。
DLL_THREAD_ATTACH:當在一個進程中建立線程時,系統要察看當前映射到該進程的地址空間中的所有DLL檔案映像,並調用每個帶有DLL_THREAD_ATTACH的DllMain函數,此時可以告訴DLL執行線程的初始化操作。注意,系統不為進程的主線程調用帶有DLL_THREAD_ATTACH值的任何DllMain.進程初次啟動進程地址空間的DLL接到的是DLL_PROCESS_ATTACH通知,而不是DLL_THREAD_ATTACH通知。
DLL_THREAD_DETACH:線程終止時調用ExitThread來撤銷線程,此時先調用帶DLL_THREAD_DETACH的DLL的DllMain,在撤銷線程。

 

2 DLL注入

2.1通過windows 掛鈎訊息來注入DLL
先看下面代碼:
HHOOK hHook=SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hinstDll,0);
WH_GETMESSAGE:指出鉤子的類型,是訊息鉤子。
GetMsgProc:視窗準備處理訊息的時候,系統調用的函數地址??????????
hinstDll:DLL被映射到的進程的地址空間中的RVA。
0:掛鈎所有的GUI進程。

下面我們看看發生了什麼:
1,進程B的一個線程準備把一條訊息發送到一個視窗。
2,系統察看該線程是否安裝WH_GETMESSAGE鉤子。
3,系統察看包含GetMsgProc的DLL是否被映射到進程B的地址空間。
4,如果沒有被映射,則強制映射到B的地址空間,實現了DLL的注入。
5,系統調用進程B空間的GetMsgProc函數(剛被映射)。

2.2使用遠程進程來插入DLL
思路:在你希望的進程中建立一個遠程線程,通過這個線程調用LoadLibrary載入需要的DLL。我們似乎只需要調用
CreateRemoteThread(hRemoteProcess,NULL,0,LoadLibraryA,"c://mylib.dll",0,NULL)即可。但不可以。
錯誤一,LoadLibraryA不能直接作為參數傳入,因為這導致遠程線程執行一些莫名其妙的東西,很可能造成訪問違規。
        解決辦法:pfnAddr=(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")),"LoadLibraryA");然後把        pfnAddr傳入。
錯誤二,不可以直接傳入"c://mylib.dll",因為這段緩衝區在本進程,不在遠程線程內。應該在遠程線程申請空間(VirtualAllocEx),並 把"c://mylib.dll"寫入這段遠程線程空間(WriteProcessMemory)。

範例程式碼:

#include <windows.h>
#include <stdio.h>
void main()
{
LPCTSTR dllpath="d://print.dll";//將要被注入的DLL,內容是向硬碟上寫記錄檔
DWORD sizedllpath=0,dwWritten=0;
HANDLE pRemoteThread,hRemoteProcess;
PTHREAD_START_ROUTINE pfnAddr;
DWORD pId;
void *pFileRemote;
BOOL fok;
sizedllpath=lstrlenA( dllpath ) + 1;
printf("size:%d",sizedllpath);
HWND hWinPro=::FindWindow("ProgMan",NULL);//獲得explorer視窗
  
if(!hWinPro)
   printf("Exploere沒有啟動");
else
{
   ::GetWindowThreadProcessId(hWinPro,&pId); //獲得explorer控制代碼
   hRemoteProcess=::OpenProcess(PROCESS_ALL_ACCESS|PROCESS_VM_WRITE,false,pId);
   pFileRemote=::VirtualAllocEx(hRemoteProcess,NULL,sizedllpath,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
   //在explorer進程分配空間
   if(pFileRemote)
   {
    printf("分配成功/n");
   }
   fok=::WriteProcessMemory(hRemoteProcess,pFileRemote,(LPVOID)dllpath,sizedllpath,&dwWritten);
   printf("實際寫入的位元組數:%d/n",dwWritten);
   printf("需要寫入的位元組數:%d/n",sizedllpath);
   if (fok==false)
   {
    printf("WriteProcessMemory error/n");
      return;
   }
   pfnAddr=(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")),"LoadLibraryA");
   pRemoteThread=::CreateRemoteThread(hRemoteProcess,NULL,0,pfnAddr,pFileRemote,0,NULL);
   if(pRemoteThread==NULL)
    return;
   else
    printf("success!/n");
}
}

結構化異常處理(SEH)
結構化異常處理包括兩部分:結束處理和異常處理。

4 結束處理
4.1 形式
__try
{
//被保護部分
}
__finally
{
//結束處理部分
}

無論try部分以如何方式退出(調用return,break,continue,goto等),在退出之前finally部分總會被執行。如果try部分沒有return,break,continue等,則程式的流程是執行完try部分自動流入finally部分(注意,這與異常處理是不同的)。
4.2 分析一個程式
為了加深對結束處理的理解,看如下程式。
DWORD FuncaDoodleDoo()
{
DWORD dwTemp=0;
while(dwTemp<10)
{
   __try{
   if(dwTemp==2)
    continue;
   if(dwTemp==3)
    break;
   }

   __finally{
   dwTemp++;
   }
   dwTemp++;
}
dwTemp+=10;
return(dwTemp) ;
}

當dwTemp進入while後,順序進入finally中,dwTemp變為1,接著執行dwTemp++,變為2,再次進入迴圈開始部分,遇到了continue指令,這將改變程式流程進入finally塊,dwTemp++變為3,這時返回迴圈開頭,注意不執行finally塊外的dwTemp++。再次進入try塊,遇到break指令,再次進入finally塊,dwTemp變為4,然後退出while,執行dwTemp+=10,所以最終返回14。
4.3 結束處理常式的應用
    可以用結束處理常式來簡化編程,但是有一點必須注意:盡量避免在try塊中使用return,break,continue,goto等,因為這會使編譯器產生很多代碼來進行程式流程改變的處理。
    先看一個程式
BOOL Fun1()
{
HANDLE hFile=INVALID_HANDLE_VALUE;
PVOID pvBuf=NULL;
DWORD dwNumBytesRead;
BOOL fok;
hFile=CreateFile("SOMEDATA.BAT",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
if(hFile==INVALID_HANDLE_VALUE)
   return FALSE;
pvBuf=VirtualAlloc(NULL,1024,MEM_COOMIT,PAGE_READWRITE);
if( pvBuf==NULL)
{
   CloseHandle(hFile);
   return FALSE;
}

fok=ReadFile(hFile,pvBuf,1024,&dwNumBytesRead,NULL);
if(!fok||dwNumBytesRead==0)
{
   VirtualFree(pvBuf,MEM_RELEASE|MEM_DECOMMIT);
   CloseHandle(hFile);
   return FALSE;
}

//do some calculation on the data
.
.
.
.

VirtualFree(pvBuf,MEM_RELEASE|MEM_DECOMMIT);
CloseHandle(hFile);
return TRUE;

}

這個程式的一個缺點是清理代碼VirtualFree,CloseHandle重複出現,羅嗦的很,使程式可讀性降低。fun2對其改進。
BOOL Fun2()
{
HANDLE hFile=INVALID_HANDLE_VALUE;
PVOID pvBuf=NULL;
DWORD dwNumBytesRead;
BOOL fok;
__try
{
   hFile=CreateFile("SOMEDATA.BAT",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
   if(hFile==INVALID_HANDLE_VALUE)
    return FALSE;
   pvBuf=VirtualAlloc(NULL,1024,MEM_COOMIT,PAGE_READWRITE);
   if( pvBuf==NULL)
   {
    return FALSE;
   }
   fok=ReadFile(hFile,pvBuf,1024,&dwNumBytesRead,NULL);
   if(!fok||dwNumBytesRead==0)
   {
    return FALSE;
   }
}

//do some calculation on the data
.
.
.
.
__finally
{

   if( pvBuf!=NULL)
    VirtualFree(pvBuf,MEM_RELEASE|MEM_DECOMMIT);  
   if(hFile!=INVALID_HANDLE_VALUE)
    CloseHandle(hFile);
}
return TRUE;

}
fun2程式碼可讀性有了很大提高,但是try塊中出現了return這會降低程式的效率。fun3使用__leave很好地解決了這個問題。
BOOL Fun3()
{
HANDLE hFile=INVALID_HANDLE_VALUE;
PVOID pvBuf=NULL;
DWORD dwNumBytesRead;
BOOL fok;
BOOL functionok=FALSE;
__try
{
   hFile=CreateFile("SOMEDATA.BAT",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
   if(hFile==INVALID_HANDLE_VALUE)
    __leave;
   pvBuf=VirtualAlloc(NULL,1024,MEM_COOMIT,PAGE_READWRITE);
   if( pvBuf==NULL)
   {
    __leave;
   }
   fok=ReadFile(hFile,pvBuf,1024,&dwNumBytesRead,NULL);
   if(!fok||dwNumBytesRead==0)
   {
    __leave
   }
}

functionok=TRUE;
//do some calculation on the data
.
.
.
.
__finally
{

   if( pvBuf!=NULL)
    VirtualFree(pvBuf,MEM_RELEASE|MEM_DECOMMIT);  
   if(hFile!=INVALID_HANDLE_VALUE)
    CloseHandle(hFile);
}
return functionok;

}
fun2達到了理想的效果,只需要增加變數functionok進行控制。
4 總結
結束處理的好處如下:
(1)簡化錯誤處理,因所有的清理工作都在一個位置並且保證被執行。
(2)可讀性提高
(3)使用得當,可以寫出健壯卻有很小系統開銷的代碼。

 

5 異常處理
cpu引發的異常是硬體異常(除0異常,非法記憶體訪問等),作業系統和應用程式也可以自己引發異常,稱為軟體異常。
5.1 形式
__try
{
//被保護部分
}
__except(exception filter)
{
//異常處理部分
}
注意,如果程式運行正常,則不進入except塊,這與finally是不同的。
5.2 異常過濾器
__except(exception filter)中的filter參數就是異常過濾器,他可以取三個值:
EXCEPTION_EXECUTE_HANDLER
EXCEPTION_CONTINUE_SEARCH
EXCEPTION_CONTINUE_EXECUTION
(1)EXCEPTION_EXECUTE_HANDLER
    這個值的意思是告訴系統:“我認出了一個異常。即我感覺這個異常可能在某個時刻發生,我已編寫了代碼來處理,現在我想執行這個     代碼”。
    一個例子。
    char * strcpy(char *strDestionation,char *strSource)
    當傳遞了無效地址時,這會導致進程結束。為了使程式更加健壯,可以:
    char * RobustStrCpy
    {
__try
{
strcpy(strDestionation,strSource)
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
//nothing to do here
}  
       return strDestionation;
    }
   再看一個例子。
   PBYTE RobustMemDup(PBYTE pbSrc,size_t cb)
   {
PBYTE pbDup=NULL;
        __try
   {
   pbDup=(PBYTE)mallo(cb);
   memcpy(pbDup,pbSrc,cb);
}
__except
{
   free(pbDup);
   pbDup=NULL;
}
return pbDup;
   }
   但函數失敗的時候,會釋放分配的空間,並以傳回值通知調用函數者知道。
(2)EXCEPTION_CONTINUE_EXECUTION
   當代碼產生異常,而過濾器值為EXCEPTION_CONTINUE_EXECUTION時,系統跳回到產生異常的指令,然後再重新執行一次。所以,如果我們確實知道哪條指令產生了什麼什麼異常時,我們可以在except塊進行修正,然後系統自動回去執行發生異常的指令,而此時已經修正了,則程式可以正常執行。這個功能如果好好利用的話會產生很奇妙的效果。後面試算表的例子會看到它的巧妙的應用。
(3) EXCEPTION_CONTINUE_SEARCH
這個值告訴系統尋找前面一個與except塊相匹配的try塊,並調用這個try塊的異常處理代碼。
5.3 一個應用執行個體--試算表
試算表程式(如excel),如果在程式開始執行時就分配空間,則會浪費很多的記憶體(因為通常試算表很大,每個格一個值的話,1024*1024就會浪費1024*1024*sizeof(int)的記憶體)。解決的策略是:先使用VirtualAlloc(.. ,MEM_RESERVE,..)在進程虛擬空間保留(reserve)一個足夠大的空間,而不提交,所以不消耗實體記憶體。當訪問某個格的時候就 VirtualAlloc(.. ,MEM_COMMIT,..)提交哪個格的記憶體。具體實現的時候,當訪問某個表格的時候,而沒有提交記憶體,將會引起EXCEPTION_ACCESS_VIOLATION異常,而在except的過濾器的值設為
EXCEPTION_CONTINUE_EXECUTION,在except塊中為要訪問的記憶體提交實體記憶體,即VirtualAlloc(.. ,MEM_COMMIT,..),然後重新執行存取碼而此時已經分配了實體記憶體則程式正常執行。下面程式採用了windows核心編程中的思想,具體實現的時候參考了網上的很多例子,並進行了改進,最終形成耗記憶體少,效率高的代碼。
//VirtualMatrix.h標頭檔
#include <windows.h>

class VirtualMatrix
{
public:
VirtualMatrix(int nRows, int nCols);
~VirtualMatrix();
void setElement(int i, int j, int value);
int getElement(int i, int j);
        LONG ExpFilter(DWORD dwExceptionCode,int i,int j);
protected:
int nCols;
int nRows;
LPVOID m_pdata;
};

//cpp檔案

#include <stdio.h>
#include <windows.h>
#include "VirtualMatrix.h"

VirtualMatrix::VirtualMatrix(int nRows, int nCols):
m_pdata (NULL),
nCols(0),
nRows(0)
{
this->nCols = nCols;
this->nRows = nRows;

m_pdata = VirtualAlloc (NULL, nRows*nCols*sizeof(int), MEM_RESERVE, PAGE_READWRITE );

if (m_pdata == NULL)
{
MessageBox(NULL, TEXT("reserve failed"), TEXT("virtual matrix"), MB_OK);
return;
}

}

VirtualMatrix::~VirtualMatrix(void)
{
if (m_pdata != NULL)
VirtualFree (m_pdata,0,MEM_RELEASE);
}

void VirtualMatrix::setElement(int i, int j, int value)
{

if (i < 0 || i >= nRows)
return;

if (j < 0 || j >= nRows)
return;

int * p = (int*)(m_pdata);

__try
{  

*(p + i*nCols+j) = value;
}
__except (ExpFilter(GetExceptionCode(),i,j))
{

}
}

LONG VirtualMatrix::ExpFilter(DWORD dwExceptionCode,int i,int j)
{

if(dwExceptionCode==EXCEPTION_ACCESS_VIOLATION)
{
   VirtualAlloc (LPVOID((long)m_pdata+sizeof(int)*(i*nCols+j)), 10, MEM_COMMIT, PAGE_READWRITE );
   return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_EXECUTE_HANDLER;

}

int VirtualMatrix::getElement(int i, int j)
{
if (i < 0 || i >= nRows)
return -1;

if (j < 0 || j >= nRows)
return -1;

__try
{
int * p = (int*)(m_pdata);
int val = *(p + i*nCols+j);

return val;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return -1;
}
}

void main()
{
VirtualMatrix a(100,100);
a.setElement(10,10,3);
printf("%d",a.getElement(10,10));
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.