VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
附件中为本例中的示例工程下载:
一、VC++6.0生成示例DLL项目1.新建项目,这里选择Win32 Dynamic-link Library,如下图:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
2.选择“A simple DLL project”,然后点击完成,如下:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
3.生成示例项目后,如下图:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
4.添加自己的示例函数,这里以简单的求和函数为例:
- /*
- 程序功能:DLL生成项目,生成测试的DLL文件,
- 作者:依星
- QQ:34596561,312337667
- 日期:2023/8/15
- */
- #include "stdafx.h"
-
- extern "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 a+b;
- }
复制代码 5.设置项目为release,开始编译构建DLL文件,按“F7”。
二、用WinHex复制取得DLL文件的全部数据这里使用WinHex来取得DLL的全部数据,具体使用流程如下: 1.打开WinHex,然后将DLL文件拖入到主界面中,如下图:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
2.选择“编辑”---“复制所有”---“C源码”,这里实际是复制了所有的数据,如下图:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
3.新建一个记事本,命名为“dll.h” ,将上面复制的数据粘贴到记事本中,然后将此文件复制到VC2015项目中。 三、建立VC2015示例工程,调用CMemLoadDll类源码1. 这里先用VC2015建立一个标准的MFC工程项目,新建---项目,如下:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
2. 选择MFC--MFC应用程序,然后选择保存的目录,并命名项目名称,如下图:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
3.下一步,然后再选择“基于对话框”,点击“在静态库中使用MFC”,如下图:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
4.至此没有特别要设置的了,点击下一步,直到完成。 5.项目创建后,默认为Unicode字符集,这里改为多字节;(PS:其实改不改都行,主要是项目中都是使用的多字节,习惯了)如下图:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
6.将上一步生成的Dll.h文件添加到解决方案的头文件中,然后再新建一个头文件和源文件,用于把网上的CMemLoadDll类源码复制过来,源码将在后面贴出来,咱们先说流程。添加好之后,如下图工程目录:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
7.在主对话框中,加一个按钮,用于调用示例:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
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.h"
-
- typedef int(*addNumberProc)(int, int);
复制代码10.生成EXE并运行,正常运行,如下:
VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)
四、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
- // ----------- ----------- ----
- // 给出节的偏移 总尺寸=8+6*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;
- }
复制代码 链接:https://pan.baidu.com/s/1OUK8VxrJZZ-Cwc5t3uJLBA
附件中为本例中的示例工程下载:
|