Win32编程

5、静态库与动态库

1、课程目标

  1. 理解静态库和动态库的区别
  2. 掌握DLL的创建和使用
  3. 学会DLL的显式和隐式加载
  4. 掌握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、课后作业

  1. 基础练习:创建一个包含数学函数的DLL
  2. 显式加载:动态加载DLL并调用函数
  3. 导出分析:使用Dumpbin分析系统DLL的导出表
  4. DLL劫持:理解DLL搜索顺序和劫持原理