为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

windows核心编程

2017-10-22 18页 doc 39KB 39阅读

用户头像

is_882336

暂无简介

举报
windows核心编程windows核心编程 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; }...
windows核心编程
windows核心编程 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(GetModuleHand le(TEXT("Kernel32")),"LoadLibraryA");然后把 pfnAddr传入。 错误二,不可以直接传入"c:\\mylib.dll",因为这段缓冲区在本进程,不在远程线程内。应该在远程线程申请空间(VirtualAllocEx),并 把"c:\\mylib.dll"写入这段远程线程空间 (WriteProcessMemory)。 示例代码: #include #include 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(GetModuleH andle(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_REA D,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 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 #include #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)); }
/
本文档为【windows核心编程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索