上一篇我们介绍了TLS。
下面来看关于函数入口函数:
// get entry point of loaded library
if(result->headers->OptionalHeader.AddressOfEntryPoint != 0) {
if (result->isDLL) {
DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(code +result->headers->OptionalHeader.AddressOfEntryPoint);
// notify library about attaching to process
BOOL successfull =(*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0);
if (!successfull) {
SetLastError(ERROR_DLL_INIT_FAILED);
goto error;
}
result->initialized = TRUE;
} else {
result->exeEntry = (ExeEntryProc)(LPVOID)(code +result->headers->OptionalHeader.AddressOfEntryPoint);
}
} else {
result->exeEntry = NULL;
}
得到函数的入口函数并返回。
另外,如果DLL 中没有定义DllMain函数,链接器将自动填充生成一个只是返回TRUE的默认DllMain函数。
到这里,DLL 的加载就结束了,下面来介绍DLL 的使用:
导出函数的调用
原本程序中并没有考虑到转发函数的情况,我们首先介绍转发DLL 的原理,然后介绍一份自己实现的函数:
ForwardDll
FORWARD DLL
/*DEF 文件*/
LIBRARY
EXPORTS
Forward=NormalDll.NormalFunc
/*.h 声明文件*/
#pragma once
#ifdef FORWARDDLL
#else
#define FORWARDDLL extern "C" __declspec( dllimport)
#endif
FORWARDDLL void Forward();
FORWARDDLL void ForwardNormal();
/*.cpp 实现文件*/
// ForwardDll.cpp: 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include <Windows.h>
#define FORWARDDLL extern "C" __declspec( dllexport)
#include "ForwardDll.h"
#include "NormalDll.h"
#pragma comment (lib, "NormalDll");
void ForwardNormal ()
{
MessageBoxA(NULL ,"Success", "ForwardDll",MB_OK );
}
#include "ForwardDll.h"
#pragma comment (lib, "ForwardDll")
int _tmain (int argc, _TCHAR * argv[])
{
Forward();
ForwardNormal();
return 0;
}
或者:EXE 动态加载
#pragma once
#ifdef FORWARDDLL
#else
#define FORWARDDLL extern "C" __declspec( dllimport)
#endif
FORWARDDLL void Forward();
FORWARDDLL void ForwardNormal();
// ForwardDll.cpp: 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include <Windows.h>
#define FORWARDDLL extern "C" __declspec( dllexport)
#include "ForwardDll.h"
//#include"NormalDll.h"
//#pragma comment(lib,"NormalDll")
#pragma comment (linker,"/export:Forward=NormalDll.NormalFunc")
void ForwardNormal ()
{
MessageBoxA(NULL ,"Success", "ForwardDll",MB_OK );
}
typedef VOID (*pfnDllFuc)( VOID);
int _tmain (int argc, _TCHAR * argv[])
{
HMODULE hDll = LoadLibraryA( "ForwardDll.dll");
if(hDll == NULL ) {
ErrorShow();
} else {
pfnDllFuc DllFunc = (pfnDllFuc) GetProcAddress(hDll ,("Forward"));
if(DllFunc != NULL) {
DllFunc();
}
FreeLibrary(hDll );
}
return 0;
}
我们查看ForwardDll.dll的导出表
两个函数对应的四个RVA,Forward函数对应的Function RVA和Name RVA相差很小,且与ForwardNormal 函数的Name RVA差不多,而ForwardNormal的Function RVA 却与其它三个有很大不同,通过查看其指向的内容发现:
如图所示的三个区域对应上述的三个相差不大的三个RVA,中间以00 分开,都指向同一块区域,且都指向以00结尾的字符串。
另外:
我们发现导出表存在于.rdata段中,上述三个区域都存在于导出表中。
而那个不同的段
如图所示属于.text段中。
因此,转发导出函数的函数RVA 指向导出表内部的一个字符串,而普通的导出函数对应的函数RVA 指向的是代码段的一个地址。由此我们可以得到如下获取DLL 导出函数地址的函数:
ULONG_PTR MyGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
int i;
PIMAGE_DOS_HEADER pImageDos = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS pImageNt =(PIMAGE_NT_HEADERS)((ULONG_PTR)hModule + pImageDos->e_lfanew);
PIMAGE_EXPORT_DIRECTORY pImageExportDirectory= (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)hModule + pImageNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
DWORD dwExportRVA =pImageNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
DWORD dwExportSize =pImageNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
DWORD* pAddressOfFunc = (DWORD*)(pImageExportDirectory->AddressOfFunctions+ (DWORD)hModule);
DWORD* pAddressOfNames = (DWORD*)(pImageExportDirectory->AddressOfNames+ (DWORD)hModule);
DWORD dwNumberOfNames = (DWORD)(pImageExportDirectory->NumberOfFunctions);
DWORD dwBase = (DWORD)(pImageExportDirectory->Base);
WORD* pAddressOfNameOrdinals = (WORD*)(pImageExportDirectory->AddressOfNameOrdinals+ (DWORD)hModule);
ULONG_PTR dwName = (ULONG_PTR)lpProcName;
char* strFunc = NULL;
char* pRet = NULL;
if ((dwName &0xffff0000) == 0)
{
goto Ordinal;
}
for (i = 0; i < (int)dwNumberOfNames;i++)
{
strFunc= (char*)((ULONG_PTR)pAddressOfNames[i]+ (ULONG_PTR)hModule);
if (strcmp(strFunc, lpProcName) == 0)
{
pRet= (char*)((ULONG_PTR)(pAddressOfFunc[pAddressOfNameOrdinals[i]]+ (ULONG_PTR)hModule));
goto Finish;
}
}
Ordinal:
if (dwName < dwBase|| dwName > dwBase + pImageExportDirectory->NumberOfFunctions)
{
//不在导出表的内部,因此是通常的函数
return (ULONG_PTR)0;
}
//得到对应的DLL 和 函数名称,然后递归调用本函数
pRet= (char*)((ULONG_PTR)pAddressOfFunc[dwName- dwBase] + (ULONG_PTR)hModule);
Finish:
if ((ULONG_PTR)pRet <dwExportRVA + (ULONG_PTR)hModule || (ULONG_PTR)pRet > dwExportRVA + (ULONG_PTR)hModule + dwExportSize)
{
return (ULONG_PTR)pRet;
}
if ((LONG_PTR)strFunc +(strlen(strFunc) + 1) * sizeof(char) == (LONG_PTR)pRet)
{
char* Temp =strchr(pRet, '.');
if (Temp == NULL)
{
//printf("forward 却找不到'.' ");
}
char szNewDll[100];
//*Temp = 0; 不可写
for (i = 0; pRet + i< Temp; i++)
{
szNewDll[i]= *(pRet + i);
}
szNewDll[i]= 0;
strcat_s(szNewDll,".dll");
//*Temp = '.';
HMODULE h = LoadLibraryA(szNewDll);
if (h == NULL)
{
return (ULONG_PTR)pRet;
}
return MyGetProcAddress(h,Temp + 1);
}
else
{
return (ULONG_PTR)pRet;
}
}