第二十八节课 数据目录
1.数据目录是什么
可选PE头最后一个成员,就是数据目录,一共有16个
分别是:导出表的数据目录、导入表的数据目录、资源表的数据目录、异常信息表的数据目录、安全证书表的数据目录、重定位表的数据目录、调试信息表的数据目录、版权所有表的数据目录、全局指针表的数据目录、TLS表的数据目录、加载配置表的数据目录、绑定导入表的数据目录、IAT表的数据目录、延迟导入表的数据目录、COM信息表的数据目录、最后一个保留未使用
2.数据目录的结构
struct _IMAGE_DATA_DIRECTORY{
DWORD VirtualAddress; //内存偏移,必须有
DWORD Size; //大小,破坏了也不会影响程序运行
};
char* tableNameArr[16] = {
"IMAGE_DIRECTORY_ENTRY_EXPORT(导出表)",
"IMAGE_DIRECTORY_ENTRY_IMPORT(导入表)",
"IMAGE_DIRECTORY_ENTRY_RESOURCE(资源表)",
"IMAGE_DIRECTORY_ENTRY_EXCEPTION(异常信息表)",
"IMAGE_DIRECTORY_ENTRY_SECURITY(安全证书表)",
"IMAGE_DIRECTORY_ENTRY_BASERELOC(重定位表)",
"IMAGE_DIRECTORY_ENTRY_DEBUG(调试信息表)",
"IMAGE_DIRECTORY_ENTRY_COPYRIGHT(版权所有表)",
"IMAGE_DIRECTORY_ENTRY_GLOBALPTR(全局指针表)",
"IMAGE_DIRECTORY_ENTRY_TLS(TLS表)",
"IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG(加载配置表)",
"IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT(绑定导入表)",
"IMAGE_DIRECTORY_ENTRY_IAT(IAT表)",
"IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT(延迟导入表)",
"IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR(COM信息表)",
"保留"
};
3.数据目录的位置
4.数据目录的作用
记录了16个表的虚拟偏移地址和大小,并不是真正的表,只是记录了表的位置
作业
编写一个函数,打印出数据目录表中的 VirtualAddress 和 Size
LPVOID data_directory_printf(LPVOID pImageBuffer)
{
PIMAGE_DOS_HEADER pDos_header = NULL;
PIMAGE_NT_HEADERS pNT_header = NULL;
PIMAGE_FILE_HEADER pPE_header = NULL;
PIMAGE_OPTIONAL_HEADER32 pOption_header = NULL;
//算出ImageBuffer中的dos头nt头pe头节表地址
pDos_header = (PIMAGE_DOS_HEADER)pImageBuffer;
pNT_header = (PIMAGE_NT_HEADERS)((DWORD)pDos_header + pDos_header->e_lfanew);
pPE_header = (PIMAGE_FILE_HEADER)((DWORD)pNT_header + 4);
pOption_header = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPE_header + IMAGE_SIZEOF_FILE_HEADER);
for (int i = 0; i < pOption_header->NumberOfRvaAndSizes; i++)
{
printf("第%d个表结构\n", i);
printf("virtualAddress:%x\n", pOption_header->DataDirectory[i].VirtualAddress);
printf("size:%x\n", pOption_header->DataDirectory[i].Size);
}
return pImageBuffer;
}
第二十九课 静态链接库 动态链接库
在一段代码经常需要被我们用到,那么他称之为函数,如果一段代码经常需要被多人用到,我们可以使用以下三种方式,可以避免我们只能把代码复制给别人的麻烦
1.静态链接库的创建
lib文件的编写
然后我们可以删剩下一个.h头文件和一个.cpp函数
用一个简单的加减乘除功能演示静态链接库,先在.h头文件写下加减乘除四个函数的声明
然后再cpp文件写下四个函数的功能实现代码
然后运行编译,虽然会运行不成功,因为单个lib文件是无法直接运行的,但是lib文件和h文件已经生成了。
lib文件的调用
资源文件调用
右键资源文件->添加->现有项, 把刚刚拷贝进来的.lib文件添加,添加后资源文件会有显示,然后就可以直接调用静态链接库里的函数了
不使用资源文件调用
#include"MyLib.h"
#pragma comment(lib, "testlib.lib")
2.静态链接库的缺点
3.动态链接库的创建
int __stdcall Plus(int x,int y){
return x+y;
}
int __stdcall Sub(int x,int y){
return x-y;
}
int __stdcall Mul(int x,int y){
return x*y;
}
int __stdcall Div(int x,int y){
return x/y;
}
extern "C" _declspec(dllexport) __stdcall int Plus (int x,int y);
extern "C" _declspec(dllexport) __stdcall int Sub (int x,int y);
extern "C" _declspec(dllexport) __stdcall int Mul (int x,int y);
extern "C" _declspec(dllexport) __stdcall int Div (int x,int y);
- extern:表示这是一个全局函数,可以供各个其他函数调用
- “C”:指的是此函数按照C语言的方式进行编译、链接。为什么要按照C的方式导出呢?
因为不指定的话,编译器可能会理解成通过C++的方式导出,但是C++允许函数的重载(可以定义相同名字的函数,但参数不同),而假如此时编译器导出后有相同名字的函数,但是此时有一个C程序要使用,但C不支持重载,那么此程序就不知道用哪个函数。①故如果没有指定"C",编译器会自动在导出.dll的时候把当中定义的所有函数名都改了,保证不出现同名函数,这样任何程序使用此动态链接库中函数时就不会出现同名的情况。②如果加上"C",因为C中不能出现同名函数,所以你在动态链接库中定义的函数名是啥就导出啥,不会改名。
- _declspec(dllexport):指告诉编译器此函数为导出函数,可以供别人使用(一定要写的,固定格式)
- __stdcall:就是函数的调用约定使用stdcall,我们前面学过,stdcall调用约定的函数会使用内平栈,如果不加VC默认使用cdcall,外平栈。建议如果在Windows下使用动态链接库最后都使用stdcall的调用约定导出.dll
4.动态链接库的使用
方式一:隐式连接
步骤1:将 *.dll *.lib 放到工程目录下面
步骤2:将 #pragma comment(lib,"DLL名.lib") 添加到调用文件中
步骤3:加入函数的声明
extern "C" __declspec(dllimport) __stdcall int Plus (int x,int y);
extern "C" __declspec(dllimport) __stdcall int Sub (int x,int y);
extern "C" __declspec(dllimport) __stdcall int Mul (int x,int y);
extern "C" __declspec(dllimport) __stdcall int Div (int x,int y);
说明:
__declspec(dllimport)告诉编译器此函数为导入函数;
方式二:显示链接
步骤1: //定义函数指针
typedef int (__stdcall *lpPlus)(int,int);
typedef int (__stdcall *lpSub)(int,int);
typedef int (__stdcall *lpMul)(int,int);
typedef int (__stdcall *lpDiv)(int,int);
步骤2: //声明函数指针变量
lpPlus myPlus;
lpSub mySub;
lpMul myMul;
lpDiv myDiv;
步骤3: // //动态加载dll到内存中
HINSTANCE hModule = LoadLibrary("DllDemo.dll");
步骤4: //获取函数地址
myPlus = (lpPlus)GetProcAddress(hModule, "_Plus@8");
mySub = (lpSub)GetProcAddress(hModule, "_Sub@8");
myMul = (lpMul)GetProcAddress(hModule, "_Mul@8");
myDiv = (lpDiv)GetProcAddress(hModule, "_Div@8");
步骤5: //调用函数
int a = myPlus(10,2);
int b = mySub(10,2);
int c = myMul(10,2);
int d = myDiv(10,2);
特别说明:
- Handle 是代表系统的内核对象,如文件句柄,线程句柄,进程句柄。
- HMODULE 是代表应用程序载入的模块
- HINSTANCE 在win32下与HMODULE是相同的东西 Win16 遗留
- HWND 是窗口句柄
其实就是一个无符号整型,Windows之所以这样设计有2个目的:
1、可读性更好
2、避免在无意中进行运算
方式三:def
和dll一样在动态链接库项目下创建一个cpp和.h头文件,cpp实现代码,头文件声明函数,但是这时候多创建了一个.def文件
EXPORTS
Plus @12
Sub @15 NONAME
Mul @13
Div @16
:::info
Export表示导出
Plus @12:表示Plus函数的导出序号为12
Sub @15 NONAME:表示Sub函数的导出序号为15;NONAME关键字表示Sub函数只有序号,没有名字,这样做就可以将Sub函数名字隐藏(学完导出表后,就可以知道怎么调用这种没有名字的函数)
动态链接库的那种以名字的导出方式也会给函数默认生成一个导出序号;.def导出的方式的序号是自己随便定义的
:::
第三十课 导出表
这节课很多参考这位大佬的文章https://blog.csdn.net/Edimade/article/details/130020286
1.导出表结构
真正的导出表结构如下:
1、name
指向该导出表文件名字符串的RVA,比如一个DBGHELP.dll的PE文件提供函数,那么这个PE文件的Name指向的字符串为dbghelp.dll
2、base
- 导出函数起始序号(最小的序号)
比如有序号为14、6、10、8的导出函数,那么Base的值为6
3.NumberOfFunctions
- 所有导出函数的个数
注意:day35中讲过,这个值是通过导出函数的最大序号 - 最小序号 + 1算出来的;正常来说这个值是多少,那么此PE文件中导出函数的个数就是多少。但是如果使用自定义序号,序号定义时不是连续的,而是中间有空缺的序号,那么此时NumberOfFunctions的值会比实际的定义的导出函数个数多
4.NumberOfNames
以函数名字导出的函数个数:比如以动态链接库的方式导出(注意和只以序号导出函数区分)
如果导出时加了NONAME关键字,那么就不计数
5.AddressOfFunctions
导出函数地址表RVA,即这个地址指向一个表!这个表中记录了此PE文件的所有导出函数的地址
比如使用自定义序号导出函数,即.def的方式导出,定义了序号13、14、16、17、19的导出函数,那么Base的值应为13,那么序号为13的函数相对相对下标就是0,序号14的导出函数相对下标就是1,序号为15的导出函数虽然没有,但是会把位置空出来,只是地址值为NULL,即0x00000000,序号16的导出函数相对下标就是3…以此类推
6.AddressOfNames
- 导出函数名称表RVA(拉伸后的内存地址偏移,所以要先转成FOA),即这个地址指向一个表,这个表中记录的是导出函数的名称字符串地址!!不是直接存储名称(且此字符串地址也是RVA)
注意:
- 如果函数导出时添加了NONAME,即函数没有名称,那么这个表中就不会出现这个函数名地址
- 所以AddressOfNames表中元素个数可能比AddressOfFunctions表中元素个数少!(AddressOfFunctions表中不管导出函数有没有名字,都会有地址)
- 也有可能AddressOfNames表中元素个数比AddressOfFunctions表中元素个数多!因为导出函数时可以让多个不同名字的函数指向同一个函数地址
7.AddressOfNameOrdinals
2.导出表获取函数地址
按函数名字查找函数地址
注意:表里的偏移地址都是RVA,需要转成FOA再去计算才能得出FileBuffer中 的地址
按序号找函数地址
给定的序号 - Base = 相对序号i
然后在AddressOfFunctions
表找出下标为i的元素值就是函数地址
所以按序号查找和序号表一点关系都没有
RVA转换FOA
RVA需要转换FOA的情况就是内存对齐和文件对齐是不一样的,所以需要对齐
1、如果RVA在文件头里面,那么RVA=FOA,因为文件头在内存和文件中展开都是一样的
2、如果RVA不在文件头里,就需要判断在哪个节里
判断节开始位置到节结束位置 我们的RVA是否在这个范围里面,总共分为三步骤:
第一步:指定节.VirtualAddress <= RVA <= 指定节.VirtualAddress + VirtualSize(当前节内存实际大小)
第二步:差值 = RVA - 指定节.VirtualAddress
第三步:FOA = 指定节.PointerToRawData + 差值
作业
1、输出导出表
2、按名字搜索函数地址
3、按序号搜索函数地址
#pragma warning(disable:4996)
#include<stdio.h>
#include<string.h>
#include<windows.h>
#include<malloc.h>
DWORD Read_File(LPVOID* ppFileBuffer)
{
FILE* fp = fopen("E:\\7-zip\\7-zip32.dll","rb");
//FILE* fp = fopen("E:\\notepad1.exe", "rb");
//FILE* fp = fopen("E:\\IPMSG2007.exe", "rb");
if (!fp)
{
printf("打开文件失败");
return 0;
}
fseek(fp, 0, 2);
int File_len = ftell(fp);
fseek(fp, 0, 0);
*ppFileBuffer = malloc(File_len);
if (!*ppFileBuffer)
{
printf("开辟空间失败");
}
size_t t = fread(*ppFileBuffer, File_len, 1, fp);
if (!t)
{
printf("复制失败");
}
fclose(fp);
return File_len;
}
DWORD RVAtoFOA(LPVOID pFileBuffer,DWORD Rva)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER NextSectionHeader = NULL;
DWORD Foa = 0;
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + 20);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
if (Rva < pOptionalHeader->SizeOfHeaders)
{
printf("Rva在Header里面\n");
return Rva;
}
NextSectionHeader = pSectionHeader + 1;
for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++, NextSectionHeader++)
{
if (Rva >= pSectionHeader->VirtualAddress && Rva < NextSectionHeader->VirtualAddress)
{
Foa = pSectionHeader->PointerToRawData + (Rva - pSectionHeader->VirtualAddress);
return Foa;
}
}
if (Rva >= pSectionHeader->VirtualAddress )
{
Foa = pSectionHeader->PointerToRawData + (Rva - pSectionHeader->VirtualAddress);
return Foa;
}
else
{
printf("Rav大于sizeofimage!!!\n");
return 0;
}
}
VOID PrintfExport(LPVOID pFileBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirHeader = NULL;
PIMAGE_EXPORT_DIRECTORY pExportHeader = NULL;
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + 20);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
if (pDosHeader->e_magic!=IMAGE_DOS_SIGNATURE) {
printf("不是有效的MZ标志\n");
}
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\n");
}
pDataDirHeader = (PIMAGE_DATA_DIRECTORY)((DWORD)pOptionalHeader + 0x60);
printf("--------------导出表----------------\n");
printf("导出表size:%x\n", pDataDirHeader->Size);
printf("导出表VirtualAddress:%x\n", pDataDirHeader->VirtualAddress);
pExportHeader = (PIMAGE_EXPORT_DIRECTORY)(RVAtoFOA(pFileBuffer, (DWORD)(pDataDirHeader->VirtualAddress)) + (DWORD)pFileBuffer);
printf("------------------------------------\n");
printf("Name:%x\n", pExportHeader->Name);
printf("Base:%x\n", pExportHeader->Base);
printf("NumberOfFunctions:%x\n", pExportHeader->NumberOfFunctions);
printf("NumberOfNames:%x\n", pExportHeader->NumberOfNames);
printf("-------------AddrOfFun-------------\n");
PDWORD AddrOfFun = (PDWORD)(RVAtoFOA(pFileBuffer,(DWORD)pExportHeader->AddressOfFunctions) + (DWORD)pFileBuffer);
for (int i = 0; i < pExportHeader->NumberOfFunctions; i++, AddrOfFun++)
{
printf("下标:%d\n", i);
printf("AddressOfFunctions:%x\n", *AddrOfFun);
}
printf("-------------AddrOfNameOrdinal-------------\n");
PWORD AddrOfNameOrdinal = (PWORD)(RVAtoFOA(pFileBuffer, (DWORD)pExportHeader->AddressOfNameOrdinals) + (DWORD)pFileBuffer);
for (int t = 0; t < pExportHeader->NumberOfNames; t++, AddrOfNameOrdinal++)
{
printf("下标:%d\n", t);
printf("序号:%x\n", *(PWORD)AddrOfNameOrdinal);
}
printf("-------------AddrOfName-------------\n");
PDWORD AddrOfName = (PDWORD)(RVAtoFOA(pFileBuffer, (DWORD)pExportHeader->AddressOfNames) + (DWORD)pFileBuffer);
for (int t = 0; t < pExportHeader->NumberOfNames; t++, AddrOfName++)
{
char* name = (char*)(RVAtoFOA(pFileBuffer, *(PDWORD)AddrOfName) + (DWORD)pFileBuffer);
printf("下标:%d\n", t);
printf("AddressOfName:%x\n", AddrOfName);
printf("name:%s\n", name);
}
}
VOID GetAddrByOrdinal(LPVOID pFileBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirHeader = NULL;
PIMAGE_EXPORT_DIRECTORY pExportHeader = NULL;
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + 20);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
pDataDirHeader = (PIMAGE_DATA_DIRECTORY)((DWORD)pOptionalHeader + 0x60);
pExportHeader = (PIMAGE_EXPORT_DIRECTORY)(RVAtoFOA(pFileBuffer, (DWORD)(pDataDirHeader->VirtualAddress)) + (DWORD)pFileBuffer);
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
printf("不是有效的MZ标志\n");
}
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\n");
}
int num = 0;
printf("请输入你要查询的序号:");
scanf("%d",&num);
int Addr_num = num - pExportHeader->Base ;
PDWORD Addr_Fun = (PDWORD)(RVAtoFOA(pFileBuffer, pExportHeader->AddressOfFunctions) + (DWORD)pFileBuffer);
for (int i = 0; i < Addr_num; i++, Addr_Fun++)
{
}
printf("你查找的函数地址为:%x",*Addr_Fun);
}
VOID GetAddrByName(LPVOID pFileBuffer,const char* str)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirHeader = NULL;
PIMAGE_EXPORT_DIRECTORY pExportHeader = NULL;
int flag = 0;
int t = 0;
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + 20);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
pDataDirHeader = (PIMAGE_DATA_DIRECTORY)((DWORD)pOptionalHeader + 0x60);
pExportHeader = (PIMAGE_EXPORT_DIRECTORY)(RVAtoFOA(pFileBuffer, (DWORD)(pDataDirHeader->VirtualAddress)) + (DWORD)pFileBuffer);
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
printf("不是有效的MZ标志\n");
}
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\n");
}
PDWORD Addr_Name = (PDWORD)(RVAtoFOA(pFileBuffer, pExportHeader->AddressOfNames) + (DWORD)pFileBuffer);
for (int i = 0; i < pExportHeader->NumberOfFunctions; i++, Addr_Name++)
{
char* name = (char*)(RVAtoFOA(pFileBuffer, *Addr_Name) + (DWORD)pFileBuffer);
if (strcmp(name, str) == 0)
{
printf("下标为:%d\n", i);
flag = i;
break;
}
}
PWORD Addr_Ordinal = (PWORD)(RVAtoFOA(pFileBuffer, pExportHeader->AddressOfNameOrdinals) + (DWORD)pFileBuffer);
for (int j = 0; j < flag; j++, Addr_Ordinal++)
{
}
int Ordinal = * (PWORD)Addr_Ordinal;
PDWORD Addr_Fun = (PDWORD)(RVAtoFOA(pFileBuffer, pExportHeader->AddressOfFunctions) + (DWORD)pFileBuffer);
for (; t < Ordinal; t++, Addr_Fun++)
{
}
printf("fun下标为:%d\n", t);
printf("查找的函数地址为:%x\n", *Addr_Fun);
}
标签:printf,函数,int,导出,30,28,29,DWORD,PIMAGE
From: https://www.cnblogs.com/xiaoxin07/p/18076797