




已阅读5页,还剩30页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
一种保护应用程序的方法 模拟Windows PE加载某天睡觉醒来,我发现我会开机了方法,过程,目标,成就是时候起兵作反了不要太多感情posts-787,comments-225,trackbacks-0,articles-1【转载】一种保护应用程序的方法模拟Windows PE加载器,从内存资源中加载DLL编程2008-07-27 21:23:47阅读375 1、前言目前很多敏感和重要的DLL(Dynamic-link library)都没有提供静态版本供编译器进行静态连接(.lib文件),即使提供了静态版本也因为兼容性问题导致无法使用,而只提供DLL版本,并且很多专业软件的授权部分的API,都是单独提供一个DLL来完成,而主模块通过调用DLL中的接口来完成授权功能。虽然这些软件一般都采用了加壳和反调试等保护,但是一旦这些功能失去作用,比如脱壳,反反调试,HOOK API或者干脆写一个仿真的授权DLL(模拟授权DLL的所有导出函数接口),然后仿真的DLL再调用授权DLL,这样所有的输入首先被仿真DLL截获再传递给授权DLL,而授权DLL的输出也首先传递给仿真DLL再传递给主程序,这样就可以轻易的监视二者之间的输入输出之间的关系,从而轻易的截获DLL中的授权信息进行修改再返回给主程序。2、目前隐式调用敏感DLL中可能存在的安全隐患以下通过两个软件的授权DLL来说明这种问题的严重性。如下是两个软件中授权DLL的部分信息,如下图所示:(图1)通过工具OllyICE可以轻易的看出IoMonitor.exe调用授权DLL(XKeyAPI.DLL),这样就很容易在调用这些API的地方设置断点,然后判断输入输出的关系,从而达到破解的目的。(图2)通过工具OllyICE可以轻易的看出sfeng.DLL中导出了很多函数,其中含义也很明显。GetHDID获取硬盘的ID,GetCpuId获取cpu的ID,WinAntiDebug反调试接口。而这些都是主程序需要调用的,比如:主程序通过GetHDID来获取硬盘编码,以这个硬盘ID的伪码来生成授权码,破解者很容易修改这些接口的输出值或者干脆写一个sfeng.DLL来导出跟目标sfeng.DLL一模一样的导出函数,而主程序却完全不知晓。只要用户有一套授权码就可以让GetHDID不管什么机器都返回一样的值,从而达到任何机器都可以使用同一套授权码。(图3)如上图所示,直接修改DLL中函数GetHDID(RVA地址:0093FF3C开始)的实现,让它直接返回固定的硬盘ID就可以达到一个授权到处使用的目的。其中:WD-Z=AM9N 086529ksaiy为需要返回的已经授权的硬盘ID,我们直接返回这个值即可。把原来0093FF3C部分的代码用nop替换掉,添加Call 008FFF60,后面添加字符串WD-Z=AM9N 086529ksaiy,Call 008FFF60之后,ESP=Call后的返回地址(Call指令的下一行),也就是字符串WD-Z=AM9N 086529ksaiy的首地址,然后pop EAX后,返回值就是字符串的首地址,通过这种简单的修改就可以达到破解的目的,说明这种隐式的调用是非常危险的。3、模拟Windows PE加载器,从资源中加载DLL本文主要介绍将DLL文件进行加密压缩后存放在程序的资源段,然后在程序中读取资源段数据进行解压和解密工作后,从内存中加载这个DLL,然后模拟PE加载器完成DLL的加载过程。本文主要以Visual C+6.0为工具进行介绍,其它开发工具实现过程与此类似。这样作的好处也很明显,DLL文件存放在主程序的资源段,而且经过了加密压缩处理,破解者很难找到下断点的地方,也不能轻易修改资源DLL,因为只有主程序完成解压和解密工作,完成PE加载工作后此DLL才开始工作。我们知道,要显式加载一个DLL,并取得其中导出的函数地址一般是通过如下步骤:(1)用LoadLibrary加载DLL文件,获得该DLL的模块句柄;(2)定义一个函数指针类型,并声明一个变量;(3)用GetProcAddress取得该DLL中目标函数的地址,赋值给函数指针变量;(4)调用函数指针变量。这个方法要求DLL文件位于硬盘上面,而我们的DLL现在在内存中。现在假设我们的DLL已经位于内存中,比如通过脱壳、解密或者解压缩得到,能不能不把它写入硬盘文件,而直接从内存加载呢?答案是肯定的,方法就是完成跟Windows PE加载器同样的工作即可。加载过程大致包括以下几个部分:1、调用API读取DLL资源数据拷贝到内存中2、调用解压和解密函数对内存中的DLL进行处理3、检查DOS头和PE头判断是否为合法的PE格式4、计算加载该DLL所需的虚拟地址空间大小5、向操作系统申请指定大小的虚拟地址空间并提交6、将DLL数据复制到所分配的虚拟内存块中,注意文件段对齐方式和内存段对齐方式7、对每个DLL文件来说都存在一个重定位节(.reloc),用于记录DLL文件的重定位信息,需要处理重定位信息8、读取DLL的引入表部分,加载引入表部分需要的DLL,并填充需要的函数入口的真实地址9、根据DLL每个节的属性设置其对应内存页的读写属性10、调用入口函数DLLMain,完成初始化工作11、保存DLL的基地址(即分配的内存块起始地址),用于查找DLL的导出函数12、不需要DLL的时候,释放所分配的虚拟内存,释放所有动态申请的内存以下部分分别介绍这几个步骤,以改造过的网上下载的CMemLoadDLL类为例程(原类存在几个错误的地方)A.调用API读取DLL资源数据拷贝到内存中/加载资源DLL#define strKey(char)0x15 char DLLtype4=DstrKey,lstrKey,lstrKey,0x00;HINSTANCE hinst=AfxGetInstanceHandle();HRSRC hr=NULL;HGLOBAL hg=NULL;/对资源名称字符串进行简单的异或操作,达到不能通过外部字符串参考下断点for(int i=0;i sizeof(DLLtype)-1;i+)DLLtypei=strKey;hr=FindResource(hinst,MAKEINTRESOURCE(IDR_DLL),TEXT(DLLtype);if(NULL=hr)return FALSE;/获取资源的大小DWORD dwSize=SizeofResource(hinst,hr);if(0=dwSize)return FALSE;hg=LoadResource(hinst,hr);if(NULL=hg)return FALSE;/锁定资源LPVOID pBuffer=(LPSTR)LockResource(hg);if(NULL=pBuffer)return FALSE;FreeResource(hg);/在资源使用完毕后我们不需要使用UnlockResource和FreeResource来手动地释放资源,因为它们都是16位Windows遗留下来的,在Win32中,在使用完毕后系统会自动回收B.调用解压和解密函数对内存总的DLL进行处理对于上面获取的pBuffer可以进行解压和解密操作,算法应该跟你加入的资源采取的算法进行逆变换即可,具体算法可以自己选择,此处省略。C.检查DOS头和PE头判断是否为合法的PE格式/CheckDataValide函数用于检查缓冲区中的数据是否有效的DLL文件/是一个可执行的DLL TRUE,否则返回FALSE。/lpFileData:存放DLL数据的内存缓冲区/DataLength:DLL文件的长度BOOL CMemLoadDLL:CheckDataValide(void*lpFileData,int DataLength)/检查长度if(DataLength sizeof(IMAGE_DOS_HEADER)return FALSE;pDosHeader=(PIMAGE_DOS_HEADER)lpFileData;/DOS头/检查dos头的标记if(pDosHeader-e_magic!=IMAGE_DOS_SIGNATURE)return FALSE;/0*5A4D:MZ/检查长度if(DWORD)DataLength(pDosHeader-e_lfanew+sizeof(IMAGE_NT_HEADERS)return FALSE;/取得pe头pNTHeader=(PIMAGE_NT_HEADERS)(unsigned long)lpFileData+pDosHeader-e_lfanew);/PE头/检查pe头的合法性if(pNTHeader-Signature!=IMAGE_NT_SIGNATURE)return FALSE;/0*00004550:PE00 if(pNTHeader-FileHeader.Characteristics&IMAGE_FILE_DLL)=0)/0*2000:File is aDLL return FALSE;if(pNTHeader-FileHeader.Characteristics&IMAGE_FILE_EXECUTABLE_IMAGE)=0)/0*0002:指出文件可以运行return FALSE;if(pNTHeader-FileHeader.SizeOfOptionalHeader!=sizeof(IMAGE_OPTIONAL_HEADER)return FALSE;/取得节表(段表)pSectionHeader=(PIMAGE_SECTION_HEADER)(int)pNTHeader+sizeof(IMAGE_NT_HEADERS);/验证每个节表的空间for(int i=0;i pNTHeader-FileHeader.NumberOfSections;i+)if(pSectionHeaderi.PointerToRawData+pSectionHeaderi.SizeOfRawData)(DWORD)DataLength)return FALSE;return TRUE;D.计算加载该DLL所需的虚拟地址空间大小计算整个DLL映像文件的尺寸,最大映像尺寸应该为VOffset最大的一个段的VOffset+VSize,然后补齐段对齐即可。如下图中,最大映像尺寸应该为0x0000D000+0x00000DA6,然后按段对齐(如为:0x1000对齐)则结果为0x0000E000。其中DOS Header和PE Header就占用0x1000字节,代码段.text从0x1000开始占用了0x7000字节。段名称虚拟地址虚拟大小物理地址物理大小标志int CMemLoadDLL:CalcTotalImageSize()int Size;if(pNTHeader=NULL)return 0;int nAlign=pNTHeader-OptionalHeader.SectionAlignment;/段对齐字节数/计算所有头的尺寸。包括dos,coff,pe头和段表的大小Size=GetAlignedSize(pNTHeader-OptionalHeader.SizeOfHeaders,nAlign);/计算所有节的大小for(int i=0;i pNTHeader-FileHeader.NumberOfSections;+i)/得到该节的大小int CodeSize=pSectionHeaderi.Misc.VirtualSize;int LoadSize=pSectionHeaderi.SizeOfRawData;int MaxSize=(LoadSize CodeSize)?(LoadSize):(CodeSize);int SectionSize=GetAlignedSize(pSectionHeaderi.VirtualAddress+MaxSize,nAlign);if(Size SectionSize)Size=SectionSize;/Use the Max;return Size;/计算对齐边界int CMemLoadDLL:GetAlignedSize(int Origin,int Alignment)return(Origin+Alignment-1)/Alignment*Alignment;E.向操作系统申请指定大小的虚拟地址空间并提交调用操作系统API VirtualAlloc保留指定大小的虚拟内存并提交内存,VirtualAlloc的第一个参数不能指定地址,如果指定地址已经被占用或者指定地址后面没有足够的连续的地址空间来满足提交的大小则会调用失败,而我们也没有必要获取指定地址空间,这样第一个参数必须保留为NULL(0)。void*pMemoryAddress=VirtualAlloc(LPVOID)NULL,ImageSize,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);if(pMemoryAddress=NULL)return FALSE;F.将DLL数据复制到所分配的虚拟内存块中,注意文件段对齐方式和内存段对齐方式拷贝内存DLL到提交的虚拟地址空间,拷贝的部分包括PE文件的所有部分,DOS Header、PE Header、Section Table、Section 1Section N,如下图所示:DOS MZ header DOS stub PE header Section table Section 1Section 2Section.Section n/CopyDLLDatas函数将DLL数据复制到指定内存区域,并对齐所有节/pSrc:存放DLL数据的原始缓冲区/pDest:目标内存地址void CMemLoadDLL:CopyDLLDatas(void*pDest,void*pSrc)/计算需要复制的PE头+段表字节数int HeaderSize=pNTHeader-OptionalHeader.SizeOfHeaders;int SectionSize=pNTHeader-FileHeader.NumberOfSections*sizeof(IMAGE_SECTION_HEADER);int MoveSize=HeaderSize+SectionSize;/复制头和段信息memmove(pDest,pSrc,MoveSize);/复制每个节for(int i=0;i pNTHeader-FileHeader.NumberOfSections;+i)if(pSectionHeaderi.VirtualAddress=0|pSectionHeaderi.SizeOfRawData=0)continue;/定位该节在内存中的位置void*pSectionAddress=(void*)(unsigned long)pDest+pSectionHeaderi.VirtualAddress);/复制段数据到虚拟内存memmove(void*)pSectionAddress,(void*)(DWORD)pSrc+pSectionHeaderi.PointerToRawData),pSectionHeaderi.SizeOfRawData);/修正指针,指向新分配的内存/新的dos头pDosHeader=(PIMAGE_DOS_HEADER)pDest;/新的pe pNTHeader=(PIMAGE_NT_HEADERS)(int)pDest+(pDosHeader-e_lfanew);/新的节表地址pSectionHeader=(PIMAGE_SECTION_HEADER)(int)pNTHeader+sizeof(IMAGE_NT_HEADERS);return;G.每个DLL文件来说都存在一个重定位节(.reloc),用于记录DLL文件的重定位信息,需要处理重定位信息Windows加载DLL时就可以按照该节的信息对需要重定位的地址进行修正,在32位代码中,凡涉及到直接寻址的指令都是需要重定位的,而PE文件的的(.reloc)段则是可选的,因为PE文件一般都可以加载到默认地址(如:0x 00400000)。当然系统的DLL其默认加载地址都能满足要求,因为这些DLL都在系统加载其它程序前首先被加载(如:Kernel32.DLL,User32.DLL)等。对于操作系统来说,其任务就是在对可执行程序透明的情况下完成重定位操作,在现实中,重定位信息是在编译的时候由编译器生成并被保留在可执行文件中的,在程序被执行前由操作系统根据重定位信息修正代码,这样在开发程序的时候就不用考虑重定位问题了。重定位信息在DLL文件中被存放在重定位表中,重定位的算法可以描述为:将直接寻址指令中的双字地址加上模块实际装入地址与模块建议装入地址之差。为了进行这个运算,需要有3个数据,首先是需要修正的机器码地址;其次是模块的建议装入地址;最后是模块的实际装入地址。在这3个数据中,模块的建议装入地址已经在PE文件头中定义了(编译后就已经确定),而模块的实际装入地址是Windows装载器确定的,到装载文件的时候自然会知道,所以被保存在重定位表中的仅仅是需要修正的代码的地址。事实上正是如此,DLL文件的重定位表中保存的就是一大堆需要修正的代码的地址。重定位表一般会被单独存放在一个可丢弃的以.reloc命名的节中,但是这并不是必然的,因为重定位表放在其他节中也是合法的,惟一可以肯定的是,假如重定位表存在的话,它的地址肯定可以在DLL文件头中的数据目录中找到。重定位表的位置和大小可以从数据目录中的第6个IMAGE_DATA_DIRECTORY结构中获取,虽然重定位表中的有用数据是那些需要重定位机器码的地址指针,但为了节省空间,DLL文件对存放的方式做了一些优化。在正常的情况下,每个32位的指针占用4个字节,假如有n个重定位项,那么重定位表的总大小是4n字节大小。直接寻址指令在程序中还是比较多的,在比较靠近的重定位表项中,32位指针的高位地址总是相同的,假如把这些相近表项的高位地址统一表示,那么就可以省略一部分的空间,当按照一个内存页来分割时,在一个页面中寻址需要的指针位数是12位(一页等于4096字节,等于2的12次方),假如将这12位凑齐16位放入一个字类型的数据中,并用一个附加的双字来表示页的起始指针,另一个双字来表示本页中重定位项数的话,那么占用的总空间会是4+4+2n字节大小,计算一下就可以发现,当某个内存页中的重定位项多于4项的时候,后一种方法的占用空间就会比前面的方法要小。/重定向PE用到的地址void CMemLoadDLL:DoRelocation(void*NewBase)/*重定位表的结构:/DWORD sectionAddress,DWORD size(包括本节需要重定位的数据/例如1000节需要修正5个重定位数据的话,重定位表的数据是/00 10 00 00 14 00 00 00 xxxx xxxx xxxx xxxx xxxx 0000/-/给出节的偏移总尺寸=8+6*2需要修正的地址用于对齐4字节/重定位表是若干个相连,如果address和size都是0表示结束/需要修正的地址是12位的,高4位是形态字,intel cpu下是3*/假设NewBase是0600000,而文件中设置的缺省ImageBase是0400000,则修正偏移量就是0200000 DWORD Delta=(DWORD)NewBase-pNTHeader-OptionalHeader.ImageBase;/注意重定位表的位置可能和硬盘文件中的偏移地址不同,应该使用加载后的地址PIMAGE_BASE_RELOCATION pLoc=(PIMAGE_BASE_RELOCATION)(unsigned long)NewBase+pNTHeader-OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_BASERELOC.VirtualAddress);while(pLoc-VirtualAddress+pLoc-SizeOfBlock)!=0)/开始扫描重定位表WORD*pLocData=(WORD*)(int)pLoc+sizeof(IMAGE_BASE_RELOCATION);/计算本节需要修正的重定位项(地址)的数目int NumberOfReloc=(pLoc-SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION)/sizeof(WORD);for(int i=0;i NumberOfReloc;i+)if(DWORD)(pLocDatai&0xF000)=0x 00003000)/这是一个需要修正的地址/举例:/pLoc-VirtualAddress=01000;/pLocDatai=0313E;表示本节偏移地址013E处需要修正/因此pAddress=+0113E/里面的内容是A1(0c d4 02 10)汇编代码是:mov eax,1002d40c/需要修正1002d40c这个地址DWORD*pAddress=(DWORD*)(unsigned long)NewBase+pLoc-VirtualAddress+(pLocDatai&0x0FFF);*pAddress+=Delta;/转移到下一个节进行处理pLoc=(PIMAGE_BASE_RELOCATION)(DWORD)pLoc+pLoc-SizeOfBlock);H.读取DLL的引入表部分,加载引入表部分需要的DLL,并填充需要的函数入口的真实地址对引入表中的DLL,通过GetModuleHandle获得其加载基地址,如果这些DLL在加载本DLL之前还没有加载,那么先调用LoadLibrary进行加载,如果加载失败则不能继续处理直接报错,说明找不到依赖的DLL。/填充引入地址表BOOL CMemLoadDLL:FillRavAddress(void*pImageBase)/引入表实际上是一个IMAGE_IMPORT_DESCRIPTOR结构数组,全部是0表示结束/数组定义如下:/DWORD OriginalFirstThunk;/0表示结束,否则指向未绑定的IAT结构数组/DWORD TimeDateStamp;/DWORD ForwarderChain;/-1 if no forwarders/DWORD Name;/给出DLL的名字/DWORD FirstThunk;/指向IAT结构数组的地址绑定后,这些IAT里面就是实际的函数地址unsigned long Offset=pNTHeader-OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_IMPORT.VirtualAddress;if(Offset=0)return TRUE;/No Import Table PIMAGE_IMPORT_DESCRIPTOR pID=(PIMAGE_IMPORT_DESCRIPTOR)(unsigned long)pImageBase+Offset);while(pID-Characteristics!=0)PIMAGE_THUNK_DATA pRealIAT=(PIMAGE_THUNK_DATA)(unsigned long)pImageBase+pID-FirstThunk);PIMAGE_THUNK_DATA pOriginalIAT=(PIMAGE_THUNK_DATA)(unsigned long)pImageBase+pID-OriginalFirstThunk);/获取DLL的名字char buf256;/DLL name;/修改,需要将buf清零,否则DLL名称不对memset(buf,0,sizeof(buf);BYTE*pName=(BYTE*)(unsigned long)pImageBase+pID-Name);for(int i=0;i 256;i+)if(pNamei=0)break;bufi=pNamei;HMODULE hDLL=GetModuleHandle(buf);if(hDLL=NULL)hDLL=LoadLibrary(buf);/有可能依赖的DLL还没有加载,如果没有加载加载后再判断是否加载成功if(hDLL=NULL)return FALSE;/NOT FOUND DLL/获取DLL中每个导出函数的地址,填入IAT/每个IAT结构是/unionPBYTE ForwarderString;/PDWORD Function;/DWORD Ordinal;/PIMAGE_IMPORT_BY_NAME AddressOfData;/u1;/长度是一个DWORD,正好容纳一个地址。for(i=0;i+)if(pOriginalIATi.u1.Function=0)break;FARPROC lpFunction=NULL;if(pOriginalIATi.u1.Ordinal&IMAGE_ORDINAL_FLAG)/这里的值给出的是导出序号lpFunction=GetProcAddress(hDLL,(LPCSTR)(pOriginalIATi.u1.Ordinal&0x0000FFFF);else/按照名字导入/获取此IAT项所描述的函数名称PIMAGE_IMPORT_BY_NAME pByName=(PIMAGE_IMPORT_BY_NAME)(DWORD)pImageBase+(DWORD)(pOriginalIATi.u1.AddressOfData);/if(pByName-Hint!=0)/lpFunction=GetProcAddress(hDLL,(LPCSTR)pByName-Hint);/else lpFunction=GetProcAddress(hDLL,(char*)pByName-Name);if(lpFunction!=NULL)/找到了!pRealIATi.u1.Function=(PDWORD)lpFunction;else return FALSE;/move to next pID=(PIMAGE_IMPORT_DESCRIPTOR)(DWORD)pID+sizeof(IMAGE_IMPORT_DESCRIPTOR);return TRUE;I.根据DLL每个节的属性设置其对应内存页的读写属性修改段属性。应该根据每个段的属性单独设置其对应内存页的属性。这里简化一下。统一设置成一个属性PAGE_EXECUTE_READWRITE,如果代码段没有执行属性,调用的时候会产生异常,页属性的设置单位至少为一个页。unsigned long old;VirtualProtect(pMemoryAddress,ImageSize,PAGE_EXECUTE_READWRITE,&old);J.调用入口函数DLLMain,完成初始化工作接下来要调用一下DLL的入口函数,做初始化工作,每个PE文件都有一个OEP,它就是AddressOfEntryPoint,一切代码都是从这里开始,OEP+DLL基地址就是其真实入口地址,当然这个入口地址一般都不是你所写的main或者DLLMain,而是运行库提供的一段代码,先完成全局变量的一些初始化和库函数相关的初始化等,而这段代码最后会调用真正的main或者DLLMain。pDLLMain=(ProcDLLMain)(pNTHeader-OptionalHeader.AddressOfEntryPoint+(DWORD)pMemoryAddress);BOOL InitResult=pDLLMain(HINSTANCE)pMemoryAddress,DLL_PROCESS_ATTACH,0);if(!InitResult)/初始化失败pDLLMain(HINSTANCE)pMemoryAddress,DLL_PROCESS_DETACH,0);VirtualFree(pMemoryAddress,0,MEM_RELEASE);pDLLMain=NULL;return FALSE;K.保存DLL的基地址(即分配的内存块起始地址),用于查找DLL的导出函数/修正基地址pNTHeader-OptionalHeader.ImageBase=(DWORD)pMemoryAddress;/MemGetProcAddress函数从dll中获取指定函数的地址/成功返回函数地址,失败返回NULL/lpProcName:要查找函数的名字或者序号FARPROC CMemLoadDll:MemGetProcAddress(LPCSTR lpProcName)if(pNTHeader-OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_EXPORT.VirtualAddress=0|pNTHeader-OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_EXPORT.Size=0)return NULL;if(!isLoadOk)return NULL;DWORD OffsetStart=pNTHeader-OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_EXPORT.VirtualAddress;DWORD Size=pNTHeader-OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_EXPORT.Size;PIMAGE_EXPORT_DIRECTORY pExport=(PIMAGE_EXPORT_DIRECTORY)(DWORD)pImageBase+pNTHeader-OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_EXPORT.VirtualAddress);int iBase=pExport-Base;int iNumberOfFunctions=pExport-NumberOfFunctions;int iNumberOfNames=pExport-NumberOfNames;/=iNumberOfFunctions LPDWORD pAddressOfFunctions=(LPDWORD)(pExport-AddressOfFunctions+pImageBase);LPWORD pAddressOfOrdinals=(LPWORD)(pExport-AddressOfNameOrdinals+pImageBase);LPDWORD pAddressOfNames=(LPDWORD)(pExport-AddressOfNames+pImageBase);int iOrdinal=-1;if(DWORD)lpProcName&0xFFFF0000)=0)/IT IS AORDINAL!iOrdinal=(DWORD)lpProcName&0x0000FFFF-iBase;else/use nameint iFound=-1;for(int i=0;i iNumberOfNames;i+)char*pName=(char*)(pAddressOfNamesi+pImageBase);if(strcmp(pName,lpProcName)=0)iFound=i;break;if(iFound=0)iOrdinal=(int)(pAddressOfOrdinalsiFound);if(iOrdinal 0|iOrdinal=iNumberOfFunctions)return NULL;elseDWORD pFunctionOffset=pAddressOfFunctionsiOrdinal;if(pFunctionOffset OffsetStart&pFunctionOffset(OffsetStart+Size)/maybe Export Forwarding return NULL;else return(FARPROC)(pFunctionOffset+pImageBase);L.不需要DLL的时候,释放所分配的虚拟内存,释放所有动态申请的内存CMemLoadDll:CMemLoadDll()if(isLoadOk)ASSERT(pImageBase!=NULL);ASSERT(pDllMain!=NULL);/脱钩,准备卸载dll pDllMain(HINSTANCE)pImageBase,DLL_PROCESS_DETACH,0);VirtualFree(LPVOID)pImageBase,0,MEM_RELEASE);4、全部详细代码/以下代码经过Win2k Sp4/WinXp Sp2下测试通过/MemLoadDll.h:interface for the CMemLoadDll class./#if!defined(AFX_MEMLOADDLL_H_E1F5150A_B534_4940_9FBF_1E6CA0E50576_INCLUDED_)#define AFX_MEMLOADDLL_H_E1F5150A_B534_4940_9FBF_1E6CA0E50576_INCLUDED_#if _MSC_VER 1000#pragma once#endif/_MSC_VER 1000 typedef BOOL(_stdcall*ProcDllMain)(HINSTANCE,DWORD,LPVOID);class CMemLoadDllpublic:CMemLoadDll();virtualCMemLoadDll();BOOL MemLoadLibrary(void*lpFileData,int DataLength);/Dll file data buffer FARPROC MemGetProcAddress(LPCSTR lpProcName);private:BOOL isLoadOk;BOOL CheckDataValide(void*lpFileData,int DataLength);int CalcTotalImageSize();void CopyDllDatas(void*pDest,void*pSrc);BOOL FillRavAddress(void*pBase);void DoRelocation(void*pNewBase);int GetAlignedSize(int Origin,int Alignment);private:ProcDllMain pDllMain;private:DWORD pImageBase;PIMAGE_DOS_HEADER pDosHeader;PIMAGE_NT_HEADERS pNTHeader;PIMAGE_SECTION_HEADER pSectionHeader;#endif/!defined(AFX_MEMLOADDLL_H_E1F5150A_B534_4940_9FBF_1E6CA0E50576_INCLUDED_)/MemLoadDll.cpp:implementation of the CMemLoadDll class./#includestdafx.h#includeMemLoadDll.h#ifdef _DEBUG#undef THIS_FILE static char THIS_FILE=_FILE_;#define new DEBUG_NEW#endif/Construction/Destruction/CMemLoadDll:CMemLoadDll()isLoadOk=FALSE;pImageBase=NULL;pDllMain=NULL;CMemLoadDll:CMemLoadDll()if(isLoadOk)ASSERT(pImageBase!=NULL);ASSERT(pDllMain!=NULL);/脱钩,准备卸载dll pDllMain(HINSTANCE)pImageBase,DLL_PROCESS_DETACH,0);VirtualFree(LPVOID)pImageBase,0,MEM_RELEASE);/MemLoadLibrary函数从内存缓冲区数据中加载一个dll到当前进程的地址空间,缺省位置010000000/成功返回TRUE,失败返回FALSE/lpFileData:存放dll文件数据的缓冲区/DataLength:缓冲区中数据的总长度BOOL CMemLoadDll:MemLoadLibrary(void*lpFileData,int DataLength)if(pImageBase!=NULL)return FALSE;/已经加载一个dll,还没有释放,不能加载新的dll/检查数据有效性,并初始化if(!CheckDataValide(lpFileData,DataLength)return FALSE;/计算所需的加载空间int ImageSize=CalcTotalImageSize();if(ImageSize=0)return FALSE;/分配虚拟内存/void*pMemoryAddress=VirtualAlloc(LPVOID)0x 10000000,ImageSize,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);/修改,不指定dll基址申请内存void*pMemoryAddress=VirtualAlloc(LPVOID)NULL,ImageSize,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);if(pMemoryAddress=NULL)return FALSE;elseCopyDllDatas(pMemoryAddress,lpFileData);/复制dll数据,并对齐每个段/重定位信息if(pNTHeader-OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_BASERELOC.VirtualAddress 0&pNTHeader-OptionalHeader.DataDirectoryIMAGE_DIRECTORY_ENTRY_BASERELOC.Size 0)DoRelocation(pMemoryAddress);/填充引入地址表if(!FillRavAddress(pMemoryAddress)/修正引入地址表失败VirtualFree(pMemoryAddress,0,MEM_RELEASE);return FALSE;/修改页属性。应该根据每个页的属性单独设置其对应内存页的属性。这里简化一下。/统一设置成一个属性PAGE_EXECUTE_READWRITE unsigned long old;VirtualProtect(pMemoryAddress,ImageSize,PAGE_EXECUTE_READWRITE,&old);/修正基地址pNTHeader-OptionalHeader.ImageBase=(DWORD)pMemoryA
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 真石漆施工及施工安全防护措施合同
- 彩钢活动房租赁与应急救援服务合同
- 大型泼水活动方案
- 大班早操舞蹈活动方案
- 大型奶茶活动方案
- 天台县新城广场活动方案
- 垃圾换水活动方案
- 大学生文学常识活动方案
- 大型环卫活动方案
- 大班桂花活动方案
- 2025年高考河北卷物理真题(解析版)
- 2025春季学期国开电大本科《经济学(本)》一平台在线形考(形考任务1至6)试题及答案
- 武汉大学2020年强基计划物理试题(解析版)
- 2024年海原县社区专职工作者招聘考试真题
- 2025年中考物理一轮复习知识清单专题14 电学基础(6大模块知识清单+5个易混易错+7种方法技巧+典例真题精析)(解析版)
- 2024年长沙市雨花区招聘社区专职工作人员真题
- 2025年乡村振兴战略相关知识考试题及答案
- 2024-2025年第二学期散学典礼活动方案-书香盈夏韵成长向新程
- 语言政策与语言多样性保护-洞察阐释
- 人工智能在畜牧业中的应用研究-洞察阐释
- 2025春国开《创业基础》形考任务1-4答案
评论
0/150
提交评论