Win32编程

5、静态库与动态库

1、课程目标

  1. 理解静态库和动态库的区别
  2. 掌握DLL的创建和使用
  3. 学会DLL的显式和隐式加载
  4. 掌握DLL导出表和导入表

2、名词解释

术语英文说明
静态库Static Library (.lib)编译时链接到可执行文件
动态库Dynamic Link Library (.dll)运行时加载
导出表Export TableDLL提供的函数列表
导入表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搜索顺序和劫持原理