1、课程目标
- 理解静态库和动态库的区别
- 掌握DLL的创建和使用
- 学会DLL的显式和隐式加载
- 掌握DLL导出表和导入表
2、名词解释
| 术语 |
英文 |
说明 |
| 静态库 |
Static Library (.lib) |
编译时链接到可执行文件 |
| 动态库 |
Dynamic Link Library (.dll) |
运行时加载 |
| 导出表 |
Export Table |
DLL提供的函数列表 |
| 导入表 |
Import Table |
可执行文件引用的DLL函数 |
3、使用工具
| 工具 |
用途 |
| Dumpbin |
查看导入/导出表 |
| Dependency Walker |
分析DLL依赖 |
4、代码实现
4.1、示例1:创建DLL
// mydll.h
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
MYDLL_API int Add(int a, int b);
MYDLL_API int Subtract(int a, int b);
// mydll.c
#define MYDLL_EXPORTS
#include "mydll.h"
MYDLL_API int Add(int a, int b) {
return a + b;
}
MYDLL_API int Subtract(int a, int b) {
return a - b;
}
// DLL入口点
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
// 进程加载DLL
break;
case DLL_PROCESS_DETACH:
// 进程卸载DLL
break;
case DLL_THREAD_ATTACH:
// 新线程创建
break;
case DLL_THREAD_DETACH:
// 线程退出
break;
}
return TRUE;
}
4.2、示例2:隐式加载DLL
// 使用头文件和.lib文件
#include "mydll.h"
#pragma comment(lib, "mydll.lib")
int main() {
int result = Add(10, 20);
printf("Result: %d\n", result);
return 0;
}
4.3、示例3:显式加载DLL
#include <windows.h>
#include <stdio.h>
typedef int (*AddFunc)(int, int);
void ExplicitLoadDemo() {
HMODULE hDll = LoadLibrary(TEXT("mydll.dll"));
if (hDll == NULL) {
printf("Failed to load DLL: %d\n", GetLastError());
return;
}
// 获取函数地址
AddFunc pAdd = (AddFunc)GetProcAddress(hDll, "Add");
if (pAdd) {
int result = pAdd(10, 20);
printf("Result: %d\n", result);
}
FreeLibrary(hDll);
}
4.4、示例4:DEF文件导出
; mydll.def
LIBRARY "mydll"
EXPORTS
Add @1
Subtract @2
; 导出时使用不同名称
MyAdd=Add @3
4.5、示例5:遍历模块导出表
#include <windows.h>
#include <stdio.h>
void ListExports(const char* dllName) {
HMODULE hMod = LoadLibraryA(dllName);
if (!hMod) return;
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)hMod + pDos->e_lfanew);
DWORD exportRVA = pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if (exportRVA == 0) {
FreeLibrary(hMod);
return;
}
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hMod + exportRVA);
DWORD* pNames = (DWORD*)((BYTE*)hMod + pExport->AddressOfNames);
printf("Exports from %s:\n", dllName);
for (DWORD i = 0; i < pExport->NumberOfNames; i++) {
char* name = (char*)((BYTE*)hMod + pNames[i]);
printf(" %s\n", name);
}
FreeLibrary(hMod);
}
5、课后作业
- 基础练习:创建一个包含数学函数的DLL
- 显式加载:动态加载DLL并调用函数
- 导出分析:使用Dumpbin分析系统DLL的导出表
- DLL劫持:理解DLL搜索顺序和劫持原理