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 FOA
DWORD 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回调函数A
void NTAPI t_TlsCallBack_A(PVOID DllHandle, DWORD Reason, PVOID Red) {
if (DLL_THREAD_DETACH == Reason) // 如果线程退出则打印信息
printf("t_TlsCallBack_A -> ThreadDetach!\r\n");
return;
}
// TLS回调函数B
void 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;
}