建设网站需要租赁主机吗,对于网站建设的体会,功能性的网站,网站开发哪里可做私活本例主要讲一下#xff0c;用VC6如何生成DLL#xff0c;用工具WinHex取得DLL全部内容#xff0c;VC2015项目加载内存中的DLL函数#xff0c;并调用函数的示例。
本例中的示例代码下载#xff0c;点击可以下载
一、VC6.0生成示例DLL项目
1.新建项目#xff0c;…本例主要讲一下用VC6如何生成DLL用工具WinHex取得DLL全部内容VC2015项目加载内存中的DLL函数并调用函数的示例。
本例中的示例代码下载点击可以下载
一、VC6.0生成示例DLL项目
1.新建项目这里选择Win32 Dynamic-link Library如下图 2.选择“A simple DLL project”,然后点击完成如下 3.生成示例项目后如下图 4.添加自己的示例函数这里以简单的求和函数为例 /*
程序功能DLL生成项目生成测试的DLL文件
作者依星
QQ:34596561,312337667
日期2023/8/15
*/
#include stdafx.hextern C __declspec(dllexport) int sum(int a,int b);BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{return TRUE;
}extern C __declspec(dllexport) int sum(int a,int b)
{return ab;
}5.设置项目为release开始编译构建DLL文件按“F7”。
二、用WinHex复制取得DLL文件的全部数据
这里使用WinHex来取得DLL的全部数据具体使用流程如下
1.打开WinHex,然后将DLL文件拖入到主界面中如下图 2.选择“编辑”---“复制所有”---“C源码”这里实际是复制了所有的数据如下图 3.新建一个记事本命名为“dll.h” 将上面复制的数据粘贴到记事本中然后将此文件复制到VC2015项目中。 三、建立VC2015示例工程调用CMemLoadDll类源码
1. 这里先用VC2015建立一个标准的MFC工程项目新建---项目如下 2. 选择MFC--MFC应用程序然后选择保存的目录并命名项目名称如下图 3.下一步然后再选择“基于对话框”点击“在静态库中使用MFC”如下图 4.至此没有特别要设置的了点击下一步直到完成。
5.项目创建后默认为Unicode字符集这里改为多字节PS:其实改不改都行主要是项目中都是使用的多字节习惯了如下图 6.将上一步生成的Dll.h文件添加到解决方案的头文件中然后再新建一个头文件和源文件用于把网上的CMemLoadDll类源码复制过来源码将在后面贴出来咱们先说流程。添加好之后如下图工程目录 7.在主对话框中加一个按钮用于调用示例 8.双击按钮显示点击方法MemRunDllDlg.cpp
/*
调用CMemLoadDll
加载内存中的DLL
并运行DLL中的函数
//
dllData:为生成的测试DLL数据文件dll.h;(注此处也可以把这个DLL文件加到资源中加载或者以文件形式读取到内存中都是可以的)
为安全起见可以把DLL的数据加密存储到DLL.H中本例不再展示。
*/
void CMemRunDllDlg::OnBnClickedButton1()
{// TODO: 在此添加控件通知处理程序代码CMemLoadDll *pMemLoadDll new CMemLoadDll();if (!pMemLoadDll-MemLoadLibrary(dllData, sizeof(dllData))) //加载dll到当前进程的地址空间{AfxMessageBox(Load DLL error!);return ;}addNumberProc addNumber (addNumberProc)pMemLoadDll-MemGetProcAddress(sum);if (addNumber NULL){AfxMessageBox(Find Add function failed!);return ;}int c addNumber(1, 2);char itc[10];sprintf(itc, %d, c);AfxMessageBox(itc);}9 .在此文件头部MemRunDllDlg.cpp加入文件引用及DLL函数声明如下
#include dll.h
#include MemLoadDll.htypedef int(*addNumberProc)(int, int);
10.生成EXE并运行正常运行如下 四、CMemLoadDll类源码网上搜索的作者不详
1.MemLoadDll.h头文件
#pragma once typedef BOOL(__stdcall *ProcDllMain)(HINSTANCE, DWORD, LPVOID);class CMemLoadDll
{
public:CMemLoadDll();~CMemLoadDll();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;};2.MemLoadDll.cpp源文件
#include stdafx.h
#include windows.h
#include assert.h
#include MemLoadDll.h 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到当前进程的地址空间缺省位置0x10000000
//返回值 成功返回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)NULL, ImageSize,MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (pMemoryAddress NULL){return FALSE;}else{CopyDllDatas(pMemoryAddress, lpFileData); //复制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);return FALSE;}//修改页属性。应该根据每个页的属性单独设置其对应内存页的属性。这里简化一下。 //统一设置成一个属性PAGE_EXECUTE_READWRITE unsigned long old;VirtualProtect(pMemoryAddress, ImageSize, PAGE_EXECUTE_READWRITE, old);}//修正基地址 pNTHeader-OptionalHeader.ImageBase (DWORD)pMemoryAddress;//接下来要调用一下dll的入口函数做初始化工作。 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;}isLoadOk TRUE;pImageBase (DWORD)pMemoryAddress;return TRUE;
}//MemGetProcAddress函数从dll中获取指定函数的地址
//返回值 成功返回函数地址 , 失败返回NULL
//lpProcName: 要查找函数的名字或者序号
FARPROC CMemLoadDll::MemGetProcAddress(LPCSTR lpProcName)
{if (pNTHeader-OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress 0 ||pNTHeader-OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size 0){return NULL;}if (!isLoadOk){return NULL;}DWORD OffsetStart pNTHeader-OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;DWORD Size pNTHeader-OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;PIMAGE_EXPORT_DIRECTORY pExport (PIMAGE_EXPORT_DIRECTORY)((DWORD)pImageBase pNTHeader-OptionalHeader.DataDirectory[IMAGE_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 A ORDINAL! {iOrdinal (DWORD)lpProcName 0x0000FFFF - iBase;}else //use name {int iFound -1;for (int i 0; i iNumberOfNames; i){char *pName (char *)(pAddressOfNames[i] pImageBase);if (strcmp(pName, lpProcName) 0){iFound i;break;}}if (iFound 0){iOrdinal (int)(pAddressOfOrdinals[iFound]);}}if (iOrdinal 0 || iOrdinal iNumberOfFunctions){return NULL;}else{DWORD pFunctionOffset pAddressOfFunctions[iOrdinal];if (pFunctionOffset OffsetStart pFunctionOffset (OffsetStart Size)) //maybe Export Forwarding {return NULL;}else{return (FARPROC)(pFunctionOffset pImageBase);}}}// 重定向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// ----------- ----------- ----// 给出节的偏移 总尺寸86*2 需要修正的地址 用于对齐4字节// 重定位表是若干个相连如果address 和 size都是0 表示结束// 需要修正的地址是12位的高4位是形态字intel cpu下是3*///假设NewBase是0x600000,而文件中设置的缺省ImageBase是0x400000,则修正偏移量就是0x200000 DWORD Delta (DWORD)NewBase - pNTHeader-OptionalHeader.ImageBase;//注意重定位表的位置可能和硬盘文件中的偏移地址不同应该使用加载后的地址 PIMAGE_BASE_RELOCATION pLoc (PIMAGE_BASE_RELOCATION)((unsigned long)NewBase pNTHeader-OptionalHeader.DataDirectory[IMAGE_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)(pLocData[i] 0xF000) 0x00003000) //这是一个需要修正的地址 {// 举例 // pLoc-VirtualAddress 0x1000; // pLocData[i] 0x313E; 表示本节偏移地址0x13E处需要修正 // 因此 pAddress 基地址 0x113E // 里面的内容是 A1 ( 0c d4 02 10) 汇编代码是 mov eax , [1002d40c] // 需要修正1002d40c这个地址 DWORD *pAddress (DWORD *)((unsigned long)NewBase pLoc-VirtualAddress (pLocData[i] 0x0FFF));*pAddress Delta;}}//转移到下一个节进行处理 pLoc (PIMAGE_BASE_RELOCATION)((DWORD)pLoc pLoc-SizeOfBlock);}
}//填充引入地址表
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里面就是实际的函数地址) int i;unsigned long Offset pNTHeader-OptionalHeader.DataDirectory[IMAGE_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的名字 TCHAR buf[256]; //dll name; BYTE *pName (BYTE *)((unsigned long)pImageBase pID-Name);for (i 0; i 256; i){if (pName[i] 0){break;}buf[i] pName[i];}if (i 256){return FALSE; // bad dll name }else{buf[i] 0;}HMODULE hDll GetModuleHandle(buf);if (hDll NULL){hDll LoadLibrary(buf);}if (hDll NULL){return FALSE; //NOT FOUND DLL }//获取DLL中每个导出函数的地址填入IAT //每个IAT结构是 // union { PBYTE ForwarderString; // PDWORD Function; // DWORD Ordinal; // PIMAGE_IMPORT_BY_NAME AddressOfData; // } u1; // 长度是一个DWORD 正好容纳一个地址。 for (i 0; ; i){if (pOriginalIAT[i].u1.Function 0){break;}FARPROC lpFunction NULL;if (pOriginalIAT[i].u1.Ordinal IMAGE_ORDINAL_FLAG) //这里的值给出的是导出序号 {lpFunction GetProcAddress(hDll, (LPCSTR)(pOriginalIAT[i].u1.Ordinal 0x0000FFFF));}else //按照名字导入 {//获取此IAT项所描述的函数名称 PIMAGE_IMPORT_BY_NAME pByName (PIMAGE_IMPORT_BY_NAME)((DWORD)pImageBase (DWORD)(pOriginalIAT[i].u1.AddressOfData));// if(pByName-Hint !0) // lpFunction GetProcAddress(hDll, (LPCSTR)pByName-Hint); // else lpFunction GetProcAddress(hDll, (char *)pByName-Name);}if (lpFunction ! NULL) //找到了 {pRealIAT[i].u1.Function (DWORD)lpFunction;//(PDWORD) lpFunction; }else{return FALSE;}}//move to next pID (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pID sizeof(IMAGE_IMPORT_DESCRIPTOR));}return TRUE;
}//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; //0x5A4D : 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; //0x00004550 : PE00 }if ((pNTHeader-FileHeader.Characteristics IMAGE_FILE_DLL) 0) //0x2000 : File is a DLL {return FALSE;}if ((pNTHeader-FileHeader.Characteristics IMAGE_FILE_EXECUTABLE_IMAGE) 0) //0x0002 : 指出文件可以运行 {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 ((pSectionHeader[i].PointerToRawData pSectionHeader[i].SizeOfRawData) (DWORD)DataLength){return FALSE;}}return TRUE;
}//计算对齐边界
int CMemLoadDll::GetAlignedSize(int Origin, int Alignment)
{return (Origin Alignment - 1) / Alignment * Alignment;
}//计算整个dll映像文件的尺寸
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 pSectionHeader[i].Misc.VirtualSize;int LoadSize pSectionHeader[i].SizeOfRawData;int MaxSize (LoadSize CodeSize) ? (LoadSize) : (CodeSize);int SectionSize GetAlignedSize(pSectionHeader[i].VirtualAddress MaxSize, nAlign);if (Size SectionSize){Size SectionSize; //Use the Max; }}return Size;
}
//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 (pSectionHeader[i].VirtualAddress 0 || pSectionHeader[i].SizeOfRawData 0){continue;}// 定位该节在内存中的位置 void *pSectionAddress (void *)((unsigned long)pDest pSectionHeader[i].VirtualAddress);// 复制段数据到虚拟内存 memmove((void *)pSectionAddress,(void *)((DWORD)pSrc pSectionHeader[i].PointerToRawData),pSectionHeader[i].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;
}