TestDll
// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "stdafx.h"#include <windows.h>extern "C" __declspec(dllexport)void funA(){    MessageBox(0, L"funA", L"", 0);}extern "C" __declspec(dllexport)void funB(){    MessageBox(0, L"funB", L"", 0);}BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved                     ){    switch (ul_reason_for_call)    {    case DLL_PROCESS_ATTACH:    case DLL_THREAD_ATTACH:    case DLL_THREAD_DETACH:    case DLL_PROCESS_DETACH:        break;    }    return TRUE;}
导入导出
// win原理Day002.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <windows.h>#define DLL "D:\\ProgramVS\\win原理Day002\\Debug\\TestDll.dll"//1 把文件读到内存中char* ReadFileToMemory(char* pFilePath){    //1 获取文件句柄    HANDLE hFile = CreateFileA(pFilePath,        GENERIC_READ | GENERIC_WRITE,        FALSE,        NULL,        OPEN_EXISTING,        FILE_ATTRIBUTE_NORMAL,        NULL);    if (hFile == INVALID_HANDLE_VALUE)    {        printf("文件打开失败\n");        return 0;    }    //2.获取文件大小    DWORD dwFileSize = GetFileSize(hFile, NULL);    //3.申请内存空间    char* pBuf = new char[dwFileSize]{};    if (!pBuf)    {        CloseHandle(hFile);        printf("内存申请失败\n");        return 0;    }    //4.读取文件内容到内存空间    DWORD dwRead;    ReadFile(hFile, pBuf, dwFileSize, &dwRead, NULL);    //5. 返回内存地址    return pBuf;}//2 是否是PE文件bool IsPeFile(char* pBuf){    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;    if (pDos->e_magic != IMAGE_DOS_SIGNATURE)    {        printf("不是PE文件\n");        return false;    }    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)        (pDos->e_lfanew + pBuf);    if (pNt->Signature != IMAGE_NT_SIGNATURE)    {        printf("不是PE文件\n");        return false;    }    return true;}//3 解析PE(头部重要字段)void ShowImportantHead(char* pBuf){    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);    //文件默认加载基址    printf("默认加载基址:0x%08X\n", pNt->OptionalHeader.ImageBase);    //文件入口点    printf("文件入口点:0x%08X\n", pNt->OptionalHeader.AddressOfEntryPoint);    //文件区段个数    printf("文件区段个数:%d\n", pNt->FileHeader.NumberOfSections);    //。。。}//RVA to FOADWORD RVAtoFOA(DWORD dwRVA, char* pBuf){    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);    //区段个数(文件头第二个字段)    DWORD dwCount = pNt->FileHeader.NumberOfSections;    //区段首地址(IMAGE_FIRST_SECTION)    PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(pNt);    for (DWORD i = 0; i < dwCount; i++)    {        //FOA = RVA - 内存中区段首地址 + 文件中区段首地址        if (dwRVA >= pSec->VirtualAddress &&             dwRVA < pSec->VirtualAddress + pSec->SizeOfRawData)        {            return dwRVA - pSec->VirtualAddress + pSec->PointerToRawData;        }        //下一个区段表        pSec++;    }    return 0;}//遍历导出表void ShowExportTable(char* pBuf){    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);    //找到导出表(数据目录表第一项)    PIMAGE_DATA_DIRECTORY pData = &pNt->OptionalHeader.DataDirectory[0];    //导出表RVA转FOA    DWORD dwExportFOA = RVAtoFOA(pData->VirtualAddress, pBuf);    //得到导出表在文件中的具体地址 = FOA + pBuf    PIMAGE_EXPORT_DIRECTORY pExport =         (PIMAGE_EXPORT_DIRECTORY)(dwExportFOA + pBuf);    //解析导出表    printf("模块名:%s\n", RVAtoFOA(pExport->Name, pBuf) + pBuf);    //导出地址表中函数个数    //pExport->NumberOfFunctions    //导出名称表中函数名称个数    //pExport->NumberOfNames;    DWORD* pFuncAddr = (DWORD*)(RVAtoFOA(pExport->AddressOfFunctions,pBuf)+pBuf);    DWORD* pFuncNameAddr = (DWORD*)(RVAtoFOA(pExport->AddressOfNames, pBuf) + pBuf);    WORD*  pFuncOrdinalAddr = (WORD*)(RVAtoFOA(pExport->AddressOfNameOrdinals, pBuf) + pBuf);    for (int i = 0; i < pExport->NumberOfFunctions;i++)    {        if (pFuncAddr[i] == 0)        {            continue;        }        //检测是否存在函数名称,如果有就输出        //判断条件:序号表中存在的序号都是有名称的        bool bFlag = FALSE;        for (int j = 0; j < pExport->NumberOfNames; j++)        {            if (i == pFuncOrdinalAddr[j])            {                //有名称的函数                bFlag = TRUE;                DWORD dwNameRVA = pFuncNameAddr[j];                printf("函数序号:%d 函数名称【%s】\n",                     i + pExport->Base,                    RVAtoFOA(dwNameRVA,pBuf)+pBuf);                break;            }        }        if (!bFlag)        {            //i+pExport->Base调用号            printf("函数序号:%d 函数名称【NULL】\n", i+pExport->Base);        }    }}//遍历导入表void ShowImportTable(char* pBuf){    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);    //找到导入表(数据目录表的第二项)    DWORD dwImportRVA = pNt->OptionalHeader.DataDirectory[1].VirtualAddress;    PIMAGE_IMPORT_DESCRIPTOR pImport =        (PIMAGE_IMPORT_DESCRIPTOR)(RVAtoFOA(dwImportRVA, pBuf) + pBuf);    //解析导入表(以全0结构为结尾)    while (pImport->Name)    {        //导入模块名        printf("导入模块名称:%s\n", RVAtoFOA(pImport->Name, pBuf) + pBuf);        //导入名称表(以0结构为结尾)        PIMAGE_THUNK_DATA pThunkINT =             (PIMAGE_THUNK_DATA)            (RVAtoFOA(pImport->OriginalFirstThunk, pBuf) + pBuf);        //导入地址表(以0结构为结尾)        PIMAGE_THUNK_DATA pThunkIAT =            (PIMAGE_THUNK_DATA)            (RVAtoFOA(pImport->FirstThunk, pBuf) + pBuf);        while (pThunkINT->u1.AddressOfData)        {            //判断导入函数的导入方式            if (IMAGE_SNAP_BY_ORDINAL32(pThunkINT->u1.AddressOfData))            {                //序号导入                //LOWORD(pThunkINT->u1.AddressOfData)                printf("\t函数序号:【%d】函数名称:【NULL】\n",                    pThunkINT->u1.AddressOfData & 0xFFFF);            }             else            {                //名称导入                DWORD dwNameFOA = RVAtoFOA(pThunkINT->u1.AddressOfData, pBuf);                PIMAGE_IMPORT_BY_NAME pName =                     (PIMAGE_IMPORT_BY_NAME)                    (dwNameFOA + pBuf);                printf("\t函数序号:【%d】函数名称:【%s】\n",                    pName->Hint,pName->Name);            }            //下一个导入函数            pThunkINT++;            //pThunkIAT++;如果是加载到内存中再做遍历,在磁盘上时,与INT是一样的        }        //下一个导入表(下一个导入模块)        pImport++;    }}int _tmain(int argc, _TCHAR* argv[]){//     void(*pFun)();//     HMODULE hMod = LoadLibraryA(DLL);//     pFun = (void(*)())GetProcAddress(hMod, (char*)1);//     pFun();    char* pBuf = ReadFileToMemory("TestDll.dll");    if (IsPeFile(pBuf))    {        //ShowImportantHead(pBuf);        //ShowExportTable(pBuf);        ShowImportTable(pBuf);    }    //释放内存    delete pBuf;    return 0;}
TLS
// TLS.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include<windows.h>#pragma comment(linker, "/INCLUDE:__tls_used")// TLS变量__declspec (thread) int  g_nNum = 0x11111111;__declspec (thread) char g_szStr[] = "TLS g_nNum = 0x%p ...\r\n";// TLS回调函数Avoid NTAPI t_TlsCallBack_A(PVOID DllHandle, DWORD Reason, PVOID Red) {    if (DLL_THREAD_DETACH == Reason) // 如果线程退出则打印信息        printf("t_TlsCallBack_A -> ThreadDetach!\r\n");    return;}// TLS回调函数Bvoid NTAPI t_TlsCallBack_B(PVOID DllHandle, DWORD Reason, PVOID Red) {    if (DLL_THREAD_DETACH == Reason) // 如果线程退出则打印信息        printf("t_TlsCallBack_B -> ThreadDetach!\r\n");    return;}/** 注册TLS回调函数,".CRT$XLB"的含义是:* CRT表明使用C RunTime机制* X表示标识名随机* L表示TLS callback section* B其实也可以为B-Y的任意一个字母*/#pragma data_seg(".CRT$XLB")PIMAGE_TLS_CALLBACK p_thread_callback[] = {    t_TlsCallBack_A,    t_TlsCallBack_B,    NULL };#pragma data_seg()DWORD WINAPI t_ThreadFun(PVOID pParam) {    printf("t_Thread ->  first printf:");    printf(g_szStr, g_nNum);    g_nNum = 0x22222222; // 注意这里    printf("t_Thread -> second printf:");    printf(g_szStr, g_nNum);    return 0;}int _tmain(int argc, _TCHAR* argv[]) {    printf("_tmain -> TlsDemo.exe is runing...\r\n\r\n");    CreateThread(NULL, 0, t_ThreadFun, NULL, 0, 0);    Sleep(100);  // 睡眠100毫秒用于确保第一个线程执行完毕    printf("\r\n");    CreateThread(NULL, 0, t_ThreadFun, NULL, 0, 0);    system("pause");    return 0;}
延迟加载
// 延迟加载.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <windows.h>//包含头文件和库文件#include <delayimp.h>#pragma comment(lib, "Delayimp.lib")//设置“连接器”>“输入”>“延迟加载的DLL”选项//中的值为我们需要延迟加载的DLL名称(大小写必须完全一致)int _tmain(int argc, _TCHAR* argv[]){    MessageBox(0, 0, 0, 0);    return 0;}