从内存中加载DLL_第1页
从内存中加载DLL_第2页
从内存中加载DLL_第3页
从内存中加载DLL_第4页
从内存中加载DLL_第5页
已阅读5页,还剩27页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

从内存中加载DLL[转]从内存中加载DLL减小字体增大字体[转]从内存中加载DLL转一篇文章,原文出处没有找到,已经转了好多了,感觉技术不错,好东东收藏也不错.——————————————————程序使用动态库DLL一般分为隐式加载和显式加载两种,分别对应两种链接情况。本文主要讨论显式加载的技术问题。我们知道,要显式加载一个DLL,并取得其中导岀的函数地址一般是通过如下步骤:用LoadLibrary加载dll文件,获得该dll的模块句柄;定义一个函数指针类型,并声明一个变量;用GetProcAddress取得该dll中目标函数的地址,赋值给函数指针变量;调用函数指针变量。这个方法要求dll文件位于硬盘上面。现在假设我们的dll已经位于内存中,比如通过脱壳、解密或者解压缩得到,能不能不把它写入硬盘文件,而直接从内存加载呢?答案是肯定的。经过多天的研究,非法操作了N次,修改了M个BUG,死亡了若干脑细胞后,终于有了初步的结果,下面做个总结与大家共享。一、加载的步骤由于没有相关的资料说明,只能凭借感觉来写。首先LoadLibrary是把dll的代码映射到exe进程的虚拟地址空间中,我们要实现的也是这个。所以先要弄清楚dll的文件结构。好在这个比较简单,它和exe一样也是PE文件结构,关于PE文件的资料很多,阅读一番后,基本上知道了必须做的几个工作:判断内存数据是否是一个有效的DLL。这个功能通过函数CheckDataValide完成。原型是:BOOLCMemLoadDll::CheckDataValide(void*lpFileData,intDataLength);计算加载该DLL所需的虚拟内存大小。这个功能通过函数CalcTotalImageSize完成。原型是:intCMemLoadDll::CalcTotalImageSize();将DLL数据复制到所分配的虚拟内存块中。该功能通过函数CopyDllDatas完成。要注意段对齐。voidCMemLoadDll::CopyDllDatas(void*pDest,void*pSrc);修正基地重定位数据。这个功能通过函数DoRelocation完成。原型是:voidCMemLoadDll::DoRelocation(void*NewBase);填充该DLL的引入地址表。这个功能由函数FillRavAddress完成。原型是:BOOLCMemLoadDll::FillRavAddress(void*pImageBase);根据DLL每个节的属性设置其对应内存页的读写属性。我这里做了简化,所有内存区域都设置成一样的读写属性。调用入口函数DIIMain,完成初始化工作。这一步我一开始忽略了,所以总是发现自己加载的dll和LoadLibrary加载的dII有些不同(我把整块内存区域保存到两个文件中进行比较,够晕的)。只是最近猜想到还需要这一步。保存dII的基地址(即分配的内存块起始地址),用于查找dII的导出函数。从现在开始这个dII已经完全映射到了进程的虚拟地址空间,可以使用它了。不需要dII的时候,释放所分配的虚拟内存。二、要说明的几个问题(1)目前CMemLoadDII仅仅针对win32动态库,没有考虑mfc常规和扩展dII。(2)只考虑使用dll中的函数,对于导岀类的dll,由于通常都是隐式链接,所以也没有考虑。导出变量的dII虽然也是隐式链接,但是通过查找函数的方法也可以找到该变量,不过在取值的时候一定要符合dll中对变量的定义,比如dll中导岀的是一个int变量,则得到该变量在dll中的地址后,需要强制转换成int*指针,然后取值。(3)查找函数的功能通过函数FARPROCCMemLoadDll::MemGetProcAddress(LPCSTRlpProcName);实现,参数是dll导岀的函数(或者变量)的名字。这里必须注意函数名修饰,通常不加extern”C”的函数,编译以后在dll中导岀的都是修饰名,比如:在dll头文件中:extern__declspec(dllexport)intnTestDll;在.dll中的导岀符号变成?nTestDII@@3HA所以为了能够找到我们需要的函数必须在.h中添加extern“C”修饰。最好是给dll加一个def文件,里面明确给岀每个函数的导岀名字。PE中的内容比较多,有些细节没有考虑。比如CheckDataValide函数中没有考虑dll对操作系统版本的要求。PE文件中的节有很多种。可以从节表(或者叫做区块表)中一一找到。而且每个节的属性都不同。例如:.text,.data,.rsrc,.crt等等。由于这个代码基于手头已有的pe文件资料,对于不熟悉的节,在映射dll数据的时候没有考虑是否需要处理。(6)一开始把dll映射到进程的地址空间以后,我试图直接使用GetProcAddress查找函数。最初我认为LoadLibrary返回的HINSTANCE值是000000000,把它传递给GetProcAddress可以找到目标函数,而我也把dll映射到0x10000000这个地址,但是当我把这个值传递给GetProcAddress的时候,发现无法找到函数,用GetLastError得到错误码一看是无效句柄的错误,这才明白原来LoadLibrary在加载dll的时候,同时创建了一个句柄放入进程的句柄表,而我们要做这个工作是比较麻烦的,所以只能自己写一个查找函数。释放dll所占据的虚拟内存,原来我使用VirtualFree((LPVOID)pImageBase,0,MEM_FREE);后来发现有问题,应该使用VirtualFree((LPVOID)pImageBase,0,MEM_RELEASE);MemGetProcAddress不仅支持通过函数名查找,还支持通过导出序号查找函数。例如下面的用法:DLLFUNCTIONfDll=(DLLFUNCTION)a.MemGetProcAddress((LPCTSTR)1);三、创建测试用的DLL,工程的名字取”TestDII”用VC向导创建一个WIN32DLL工程,里面选择“导岀一些符号”,为了测试需要,对源代码进行如下修改:(1)头文件//ThisclassisexportedfromtheTestDll.dllclassTESTDLL_APICTestDll{public:CTestDll(void);};externTESTDLL_APIintnTestDll;//要修改的地方,添加了extern“C”和char*参数:extern“C”TESTDLL_APIintfnTestDll(char*);(2)cpp文件添加#include“stdlib.h”DIIMain中caseDLL_PROCESS_DETACH:nTestDII=12345;break;初始化变量TESTDLL_APIintnTestDII=654321;修改函数TESTDLL_APIintfnTestDII(char*p){if(p==NULL)returnnTestDII;elsereturnatoi(p);}四、创建测试工程。使用一个dig工程,测试代码如下:假设DIINameBuffer里面保存有dll文件的路径CFiief;if(f.Open(DllNameBuffer,CFile::modeRead)){intFileLength=f.GetLength();void*lpBuf=newchar[FileLength];f.Read(lpBuf,FileLength);f.Close();CMemLoadDlla;if(a.MemLoadLibrary(lpBuf,FileLength))//加载dll到当前进程的地址空间{typedefint(*DLLFUNCTION)(char*);DLLFUNCTIONfDll=(DLLFUNCTION)a.MemGetProcAddress(”fnTestDll”);if(fDll!=NULL){MessageBox(”找到函数!!”);CStringstr;str.Format(”Resultis:%d&%d”,fDII(NULL),fDII(”100〃));MessageBox(str);}else{DWORDerr=GetLastError();CStringstr;str.Format(”Error:%d”,err);MessageBox(str);}}delete[]lpBuf;}五、加载类源代码。typedefBOOL(__stdcall*ProcDllMain)(HINSTANCE,DWORD,LPVOID);classCMemLoadDll{public:CMemLoadDll();~CMemLoadDll();BOOLMemLoadLibrary(void*lpFileData,intDataLength);//DllfiledatabufferFARPROCMemGetProcAddress(LPCSTRlpProcName);private:BOOLisLoadOk;BOOLCheckDataValide(void*lpFileData,intDataLength);intCalcTotalImageSize();voidCopyDllDatas(void*pDest,void*pSrc);BOOLFillRavAddress(void*pBase);voidDoRelocation(void*pNewBase);intGetAlignedSize(intOrigin,intAlignment);private:ProcDllMainpDllMain;private:DWORDpImageBase;PIMAGE_DOS_HEADERpDosHeader;PIMAGE_NT_HEADERSpNTHeader;PIMAGE_SECTION_HEADERpSectionHeader;};CMemLoadDll::CMemLoadDll(){isLoadOk=FALSE;pImageBase=NULL;pDllMain=NULL;}CMemLoadDll::~CMemLoadDll(){if(isLoadOk){ASSERT(pImageBase!=NULL);ASSERT(pDllMain!=NULL);//脱钩,准备卸载dllpDllMain((HINSTANCE)pImageBase,DLL_PROCESS_DETACH,0);VirtualFree((LPVOID)pImageBase,0,MEM_RELEASE);}}//MemLoadLibrary函数从内存缓冲区数据中加载一个dll到当前进程的地址空间,缺省位置0X10000000//返回值:成功返回TRUE,失败返回FALSE//lpFileData:存放dll文件数据的缓冲区//DataLength:缓冲区中数据的总长度BOOLCMemLoadDll::MemLoadLibrary(void*lpFileData,intDataLength){if(pImageBase!=NULL){returnFALSE;〃已经加载一个dll,还没有释放,不能加载新的dll}//检查数据有效性,并初始化if(!CheckDataValide(lpFileData,DataLength))returnFALSE;//计算所需的加载空间intImageSize=CalcTotalImageSize();if(ImageSize==0)returnFALSE;//分配虚拟内存void*pMemoryAddress=VirtualAlloc((LPVOID)OxlOOOOOOO,imageSize,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);if(pMemoryAddress==NULL)returnFALSE;else{CopyDIIDatas(pMemoryAddress,IpFileData);//复制dll数据,并对齐每个段//重定位信息if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress>0&&pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size>0){DoRelocation(pMemoryAddress);}//填充引入地址表if(!FillRavAddress(pMemoryAddress))//修正引入地址表失败{VirtualFree(pMemoryAddress,0,MEM_RELEASE);returnFALSE;}//修改页属性。应该根据每个页的属性单独设置其对应内存页的属性。这里简化一下。//统一设置成一个属性PAGE_EXECUTE_READWRITEunsignedlongold;VirtualProtect(pMemoryAddress,ImageSize,PAGE_EXECUTE_READWRITE,&old);}//修正基地址pNTHeader->OptionalHeader.ImageBase=(DWORD)pMemoryAddress;//接下来要调用一下dll的入口函数,做初始化工作。pDllMain=(ProcDllMain)(pNTHeader->OptionalHeader.AddressOfEntryPoint+(DWORD)pMemoryAddress);BOOLInitResult=pDllMain((HINSTANCE)pMemoryAddress,DLL_PROCESS_ATTACH,0);if(!InitResult)//初始化失败{pDllMain((HINSTANCE)pMemoryAddress,DLL_PROCESS_DETACH,0);VirtualFree(pMemoryAddress,0,MEM_RELEASE);pDllMain=NULL;returnFALSE;}isLoadOk=TRUE;pImageBase=(DWORD)pMemoryAddress;returnTRUE;}//MemGetProcAddress函数从dll中获取指定函数的地址//返回值:成功返回函数地址,失败返回NULL//lpProcName:要查找函数的名字或者序号FARPROCCMemLoadDll::MemGetProcAddress(LPCSTRlpProcName){if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress==0||pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size==0)returnNULL;if(!isLoadOk)returnNULL;DWORDOffsetStart=pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;DWORDSize=pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;PIMAGE_EXPORT_DIRECTORYpExport=(PIMAGE_EXPORT_DIRECTORY)((DWORD)pImageBase+pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);intiBase=pExport->Base;intiNumberOfFunctions=pExport->NumberOfFunctions;intiNumberOfNames=pExport->NumberOfNames;//AddressOfNameOrdinals+pImageBase);LPDWORDpAddressOfNames=(LPDWORD)(pExport->AddressOfNames+pImageBase);intiOrdinal=-1;if(((DWORD)lpProcName&0xFFFF0000)==0)//ITISAORDINAL!{iOrdinal=(DWORD)lpProcName&0X0000FFFF-iBase;}else//usename{intiFound=-1;for(inti=0;i{char*pName=(char*)(pAddressOfNames[i]+pImageBase);if(strcmp(pName,lpProcName)==0){iFound=i;break;}}if(iFound>=0){iOrdinal=(int)(pAddressOfOrdinals[iFound]);}}if(iOrdinal=iNumberOfFunctions)returnNULL;else{DWORDpFunctionOffset=pAddressOfFunctions[iOrdinal];if(pFunctionOffset>OffsetStart&&pFunctionOffset<(OffsetStart+Size))//maybeExportForwardingreturnNULL;elsereturn(FARPROC)(pFunctionOffset+pImageBase);}}//重定向PE用到的地址voidCMemLoadDll::DoRelocation(void*NewBase){/*重定位表的结构://DWORDsectionAddress,DWORDsize(包括本节需要重定位的数据)//例如1000节需要修正5个重定位数据的话,重定位表的数据是//0010000014000000xxxxxxxxxxxxxxxxxxxx0000// //给出节的偏移总尺寸=8+6*2需要修正的地址用于对齐4字节//重定位表是若干个相连,如果address和size都是0表示结束//需要修正的地址是12位的,高4位是形态字,intelcpu下是3*/〃假设NewBase是0x600000,而文件中设置的缺省ImageBase是0x400000,则修正偏移量就是0x200000DWORDDelta=(DWORD)NewBase-pNTHeader->OptionalHeader」mageBase;〃注意重定位表的位置可能和硬盘文件中的偏移地址不同,应该使用加载后的地址PIMAGE_BASE_RELOCATIONpLoc=(PIMAGE_BASE_RELOCATION)((unsignedlong)NewBase+pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);while((pLoc->VirtualAddress+pLoc->SizeOfBlock)!=0)//开始扫描重定位表{WORD*pLocData=(WORD*)((int)pLoc+sizeof(IMAGE_BASE_RELOCATION));//计算本节需要修正的重定位项(地址)的数目intNumberOfReloc=(pLoc->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))/sizeof(WORD);for(inti=0;iVirtualAddress=0x1000;//pLocData[i]=0x313E;表示本节偏移地址0x13E处需要修正//因此pAddress=基地址+0x113E//里面的内容是A1(0cd40210)汇编代码是:moveax,[1002d40c]//需要修正1002d40c这个地址DWORD*pAddress=(DWORD*)((unsignedlong)NewBase+pLoc->VirtualAddress+(pLocData[i]&OxOFFF));*pAddress+=Delta;}}//转移到下一个节进行处理pLoc=(PIMAGE_BASE_RELOCATION)((DWORD)pLoc+pLoc->SizeOfBlock);}}//填充引入地址表BOOLCMemLoadDll::FillRavAddress(void*pImageBase){//引入表实际上是一个IMAGE_IMPORT_DESCRIPTOR结构数组,全部是0表示结束//数组定义如下:////DWORDOriginalFirstThunk;//0表示结束,否则指向未绑定的IAT结构数组//DWORDTimeDateStamp;//DWORDForwarderChain;//-1ifnoforwarders//DWORDName;//给出dll的名字//DWORDFirstThunk;//指向IAT结构数组的地址(绑定后,这些IAT里面就是实际的函数地址)unsignedlongOffset=pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;if(Offset==0)returnTRUE;//NoImportTablePIMAGE_IMPORT_DESCRIPTORpID=(PIMAGE_IMPORT_DESCRIPTOR)((unsignedlong)pImageBase+Offset);while(pID->Characteristics!=0){PIMAGE_THUNK_DATApRealIAT=(PIMAGE_THUNK_DATA)((unsignedlong)pImageBase+pID->FirstThunk);PIMAGE_THUNK_DATApOriginalIAT=(PIMAGE_THUNK_DATA)((unsignedlong)pImageBase+pID->OriginalFirstThunk);〃获取dll的名字charbuf[256];//dllname;BYTE*pName=(BYTE*)((unsignedlong)pImageBase+pID->Name);for(inti=0;i<256;i++){if(pName[i]==0)break;buf[i]=pName[i];}HMODULEhDll=GetModuleHandle(buf);if(hDll==NULL){hDll=LoadLibrary(buf);if(hDll==NULL)returnFALSE;//NOTFOUNDDLL}//获取DLL中每个导出函数的地址,填入IAT//每个IAT结构是://union{PBYTEForwarderString;//PDWORDFunction;//DWORDOrdinal;//PIMAGE_IMPORT_BY_NAMEAddressOfData;//}u1;//长度是一个DWORD,正好容纳一个地址。for(i=0;;i++){if(pOriginalIAT[i].u1.Function==0)break;FARPROClpFunction=NULL;if(pOriginalIAT[i].u1.Ordinal&IMAGE_ORDINAL_FLAG)//这里的值给出的是导出序号{lpFunction=GetProcAddress(hDll,(LPCSTR)(p0riginallAT[i].u1.0rdinal&OxOOOOFFFF));}else//按照名字导入{〃获取此IAT项所描述的函数名称PIMAGE_IMPORT_BY_NAMEpByName=(PIMAGE_IMPORT_BY_NAME)((DWORD)pImageBase+(DWORD)(pOriginalIAT[i].u1.AddressOfData));//if(pByName->Hint!=0)//lpFunction=GetProcAddress(hDll,(LPCSTR)pByName->Hint);//elselpFunction=GetProcAddress(hDll,(char*)pByName->Name);}if(lpFunction!=NULL)//找到了!{pRealIAT[i].u1.Function=(PDWORD)lpFunction;}elsereturnFALSE;}//movetonextpID=(PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pID+sizeof(IMAGE_IMPORT_DESCRIPTOR));}returnTRUE;}//CheckDataValide函数用于检查缓冲区中的数据是否有效的dll文件〃返回值:是一个可执行的dll则返回TRUE,否则返回FALSE。//IpFileData:存放dll数据的内存缓冲区//DataLength:dll文件的长度BOOLCMemLoadDll::CheckDataValide(void*lpFileData,intDataLength){//检查长度if(DataLengthe_magic!=IMAGE_DOS_SIGNATURE)returnFALSE;〃0x5A4D:MZ〃检查长度if((DWORD)DataLengthe_lfanew+sizeof(IMAGE_NT_HEADERS)))returnFALSE;〃取得pe头pNTHeader=(PIMAGE_NT_HEADERS)((unsignedlong)lpFileData+pDosHeader->e_lfanew);//PE头〃检查pe头的合法性if(pNTHeader->Signature!=IMAGE_NT_SIGNATURE)returnFALSE;〃0x00004550:PEOOif((pNTHeader->FileHeader.Characteristics&IMAGE_FILE_DLL)==0)〃0x2000:FileisaDLLreturnFALSE;if((pNTHeader->FileHeader.Characteristics&IMAGE_FILE_EXECUTABLE」MAGE)==0)〃0x0002:指出文件可以运行returnFALSE;if(pNTHeader->FileHeader.SizeOfOptionalHeader!=sizeof(IMAGE_OPTIONAL_HEADER))returnFALSE;//取得节表(段表)pSectionHeader=(PIMAGE_SECTION_HEADER)((int)pNTHeader+sizeof(IMAGE_NT_HEADERS));//验证每个节表的空间for(inti=0;iFileHeader.NumberOfSections;i++){if((pSectionHeader[i].PointerToRawData+pSectionHeader[i].SizeOfRawData)>(DWORD)DataLength)returnFALSE;}returnTRUE;}//计算对齐边界intCMemLoadDll::GetAlignedSize(intOrigin,intAlignment){return(Origin+Alignment-1)/Alignment*Alignment;}//计算整个dll映像文件的尺寸intCMemLoadDll::CalcTotalImageSize(){intSize;if(pNTHeader==NULL)return0;intnAlign=pNTHeader->OptionalHeader.SectionAlignment;//段对齐字节数//计算所有头的尺寸。包括dos,coff,pe头和段表的大小Size=GetAlignedSize(pNTHeader->Optional

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论