windows核心编程3(DllMain,SetWindowsHookEx,结构化异常处理等)

本文转自: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));
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值