PE目录项之导出表(解析、移动)
文章目录
0.说明
观看滴水逆向视频总结(部分截图来自于滴水课件)
编译器:vc++6.0
编写语言:c
欢迎大家留言交流或询问😃^ __ ^
有不懂的直接留言,我必回!😄🤣
1.简述导出表
导出表,顾名思义就是要导出的函数的表。为别人提供函数,供别人调用。就要根据这种表,将函数导出。
所以这张表,就是提供一个要导出的函数的清单,方便导出。
一般是dll为exe提供函数,dll有导出表,但是exe自身也可以为别人提供函数,也是通过导出表。
a.位置
目录项的第一个结构就是导出表
IMAGE_DIRECTORY_ENTRy_EXPORt | 意思 |
---|---|
VirtualAddress | 这个表结构的RVA |
Size | 这个表结构的大小 |
我们根据他给的VirtualAddress找到对应的表结构。
b.导出表的结构
struct_IMAGE_EXPORT_DIRECTORY | 作用 |
---|---|
Characteristics | (未使用) |
TimeDateStamp | 时间戳 |
MajorVersion | (未使用) |
MinorVersion | (未使用) |
Name | 指向该导出表文件名字符串 |
Base | 导出函数起始序号(基值) |
NumberOfFuctions | 所有导出函数的个数 |
NumberOfNames | 以函数名字导出的函数个数 |
AddressOfFunctions | 所有导出函数的地址表的RVA |
AddressOfNames | 导出函数名称表的RVA |
AddressOfNameOrdinals | 导出函数序号表的RVA |
前面四个不用怎么管,后面红色的要全部记熟,结构中最后面的三个RVA分别指向三个表结构。根据箭头指向,表结构大概如图所示。
注意:AddressOfNameOrdinals指向的表结构,宽度为2个字节,也就是每个序号站2个字节。
c.导出表的工作方式
导出表的意义是为别人 提供一个要导出的函数的清单,方便别人调用。一般有两种方式。
① 根据函数名导出
-
一般用调用者提供一个函数名,根据这个函数名,
-
挨个遍历
AddressOfName
里面的RVA指向的字符串,去对比这个函数名 -
若相等,再根据这个RVA在
AddressOfNames
表中的排序序号,对照获得AddressOfNameOrdinals
表中相同排序序号的值, -
再根据这个值作为排序序号查找
AddressOfFunctions
表中相同排序序号的值, 这个值是RVA,指向这个函数的真正地址。
注意这里的排序序号都是从0开始的
② 根据序号导出函数
- 调用者提供一个序号
- 用这个序号值减去base值得到一个新值
- 根据这个新值作为
AddressOfFuctions
表中的排序序号,直接查找函数RVA,即指向这个函数的真正地址。
③ 总结
- 我们发现,虽然导出表最后有三张表,但是,如果按照序号导出函数的话,只需要一张表
AddressOfFuctions
,而另外两张表,我完全是为了方便通过函数名导出函数而建造的。
2.解析导出表
a.注意要点
- 导出表的结构比较复杂,整个结构是牵连在一起的,要自己去画图或者自己去解析依次后才能有一个抽象到具体的认识。
- 导出表中涉及到的
VirtualAddress
全是RVA,在内存中寻址时,要注意先把RVA转换成FOA,在通过FOA这个偏移去寻址。所以自己编写一个独立的函数ConvertRvaToFoa将RVA转换为FOA。(具体见源代码中) - 对指针的运用要求比较高
- 函数中会涉及到很多结构体的定义,均来自头文件
windows.h
。(准确的来说是winnt.h
)。
b.源代码
编写语言:C
编译环境:VC++6.0
#include<stdio.h>
#include<windows.h>
char FileName[200]={0};//用于存放我们要解析的程序的地址
//功能:将相应程序读取到内存
//参数:指向程序名字(地址)指针
//返回值:指向该程序被读取到内存的指针
LPVOID ReadPeFile( char FileName[]);
//功能:解析、打印导出表
//参数:指向该程序被读取到内存的指针
//返回值:无
void ReadPE( LPVOID pFileBuffer );
//功能:将RVA转化为FOA
//参数:参数1:要转换的RVA的值(size_t);参数2:指向文件缓冲区的指针(LPVOID)
//返回值:成功则返回对应的FOA,失败则返回0(这个RVA是没有对应的FOA,为内存拉伸后系统填充的地址,或者RVA超出内存范围)。
size_t ConvertRvaToFoa( size_t RVA , LPVOID pFileBuffer );
int main()
{
LPVOID pFileBuffer = NULL;
printf("please input: (for example: D:/user/Desktop/学习笔记/2020.6.22_PE目录项之导出表/myDll.dll )\n");
gets(FileName);
pFileBuffer = ReadPeFile( FileName );
if( pFileBuffer )
ReadPE( pFileBuffer );
}
//功能:将相应程序读取到内存
//参数:指向程序名字(地址)指针
//返回值:指向该程序被读取到内存的指针
LPVOID ReadPeFile( char FileName[] )
{
LPVOID pFileBuffer = NULL;
FILE* pFile = NULL;
DWORD FileSize = 0;
pFile = fopen( FileName , "rb");
if(!pFile)
{
printf("open file failure!\n");
return NULL;
}
fseek(pFile , 0 , SEEK_END);
FileSize = ftell(pFile);
fseek(pFile , 0 , SEEK_SET);
pFileBuffer = calloc(1 , FileSize);
if(!pFileBuffer)
{
printf("Failure to allocate memory space!\n");
fclose(pFile);
return NULL;
}
fread(pFileBuffer , FileSize , 1 ,pFile);
fclose(pFile);
return pFileBuffer ;
}
//功能:解析、打印导出表
//参数:指向该程序被读取到内存的指针
//返回值:无
void ReadPE( LPVOID pFileBuffer )
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS32 pNtHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;//定义目录项导出表结构的指针
PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;//定义指向真正导出表的指针
size_t i;
PDWORD pAddressOfFunctions;//指向函数地址表
PWORD pAddressOfNameOrdinals;//指向函数序号表
PDWORD pAddressOfNames;//指向函数名字表
pDosHeader = (PIMAGE_DOS_HEADER )pFileBuffer ;
pNtHeader = (PIMAGE_NT_HEADERS32 )( (DWORD)pFileBuffer + pDosHeader->e_lfanew );
pFileHeader = (PIMAGE_FILE_HEADER )( (DWORD)pNtHeader + 4 );
pOptionHeader = (PIMAGE_OPTIONAL_HEADER )( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );
pDataDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;//目录项结构
//注意将RVA转换为FOA
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)( (DWORD)pDosHeader+ ConvertRvaToFoa(pDataDirectory->VirtualAddress, pFileBuffer ) );
printf("\n");
//解析导出表的结构
printf("Name: %X\n",pExportDirectory->Name );
printf("Base: %X\n",pExportDirectory->Base );
printf("NumberOfFunctions: %X\n",pExportDirectory->NumberOfFunctions );
printf("NumberOfNames : %x\n\n",pExportDirectory->NumberOfNames );
//打印AddressOfFunctions这个表结构。
printf("****** AddressOfFuctions ******\n");
pAddressOfFunctions = (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->AddressOfFunctions , pFileBuffer ) ) ;
for(i=0 ; i < pExportDirectory->NumberOfFunctions ; i++ ,pAddressOfFunctions++ )
printf("RVA_%d : %x\n",i, *pAddressOfFunctions );
printf("*******************************\n\n");
//打印AddressOfNameOrdinals这个表结构
printf("****** AddressOfNameOrdinals ******\n");
pAddressOfNameOrdinals = (PWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->AddressOfNameOrdinals , pFileBuffer ) );
for(i=0 ; i<pExportDirectory->NumberOfNames ; i++ ,pAddressOfNameOrdinals++ )
printf("Ordinal_%d : %d\n",i, *pAddressOfNameOrdinals );
printf("***********************************\n\n");
//打印AddressOfNames这表结构
printf("****** AddressOfNames ******\n");
pAddressOfNames = (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->AddressOfNames , pFileBuffer ) );
for(i=0 ; i<pExportDirectory->NumberOfNames ; i++ ,pAddressOfNames++)
{
// printf("Name_%d : ",i);
// printf("%x ",*pAddressOfNames);
printf("Name_%d : %-10s ( RVA :%-8x)\n",i ,(PDWORD)( (DWORD)pDosHeader+ ConvertRvaToFoa(*pAddressOfNames ,pFileBuffer) ) , *pAddressOfNames );
}
printf("****************************\n\n");
free( pFileBuffer );
}
//功能:将RVA转化为FOA
//参数:参数1:要转换的RVA的值(size_t);参数2:指向文件缓冲区的指针(LPVOID)
//返回值:成功则返回对应的FOA,失败则返回0(这个RVA是没有对应的FOA,为内存拉伸后系统填充的地址,或者RVA超出内存范围)。
size_t ConvertRvaToFoa( size_t RVA , LPVOID pFileBuffer )
{
int i ;//用于遍历节表
// size_t FOA = 0;
//定义表头指针
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS32 pNtHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//给表头赋初值
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader+4 );
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一个节表头
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);
if(RVA < pSectionHeader->VirtualAddress)//判断RVA是否在PE头区
{
if(RVA < pSectionHeader->PointerToRawData)
return RVA;//此时FOA == RVA
else
return 0;
}
for(i=0 ; i<pFileHeader->NumberOfSections ; i++)//循环遍历节表头
{
if( i )//遍历节表头,第一次不遍历,
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
if(RVA >= pSectionHeader->VirtualAddress )//是否大于这个节表的RVA
{
if( RVA <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData )//判断是否在这个节区
return ( RVA - pSectionHeader->VirtualAddress ) + pSectionHeader->PointerToRawData;//确定节区后,计算FOA
}
else//RVA不可能此时的pSectionHeader->VirtuallAddress小,除非是返回值为0的情况。
return 0;
}
return 0;
}
3.移动导出表所有结构到新建的节区
a.移动导出表的原因
b.大概流程
新增节的部分前面几章讲过,这里就就直接贴代码过来(稍稍修改)IncreaseSection。
c.注意事项
- 有很多个地方,寻址的时候要将RVA转换为FOA在转换为现在内存中的真实位置。
- 在修正RVA的时候,注意要将此时的FOA转为RVA
- 反正有就是FOA和RVA互转,很多。
- 表中的地址是个RVA,并且是DWORD类型的,所以要转为指针才能调用,也有很多地方要强转类型,特别繁琐。
- 特别注意:在复制字符串的时候,一定要在字符串的末尾加上一个0,代表字符串末尾。
可能刚开始有很多疑问,建议直接动手写。
d.源代码
如何其他数据都移动成功了,但是新的dll还是调用不了,那很有可能是新增节那一块出问题了!
#include<stdio.h>
#include<windows.h>
#include<string.h>
char FileName[200]={0};
DWORD FileSize = 0;
DWORD NewFileSize = 0;
//功能:将相应程序读取到内存
//参数:指向程序名字(地址)指针
//返回值:指向该程序被读取到内存的指针
LPVOID ReadPeFile( char FileName[]);
//功能:移动导出表中所有结构到新建的节区
//参数:指向该程序被读取到内存的指针
//返回值:返回一个新的pNewFileBuffer(重点:末尾节区已经复制的导出表的所有结构),失败则返回NULL。
LPVOID MoveExportDirectory( LPVOID pFileBuffer );
//功能:将RVA转化为FOA
//参数:参数1:要转换的RVA的值(DWORD);参数2:指向文件缓冲区的指针(LPVOID)
//返回值:成功则返回对应的FOA,失败则返回0(这个RVA是没有对应的FOA,为内存拉伸后系统填充的地址,或者RVA超出内存范围)
DWORD ConvertRvaToFoa( size_t RVA , LPVOID pFileBuffer );
//功能:将FOA转换为RVA
//参数:参数1:要转换的FOA值(DWORD);参数2:指向文件内存的指针(LPVOID);
//返回值:成功则返回对应的RVA,失败返回0(此时为FOA越界)。
DWORD ConvertFoaToRva( size_t FOA , LPVOID pFileBuffer );
//功能:添加一个节表和节区
//参数:指向文件读取到内存的指针
//返回值:返回一个新的pNewFileBUffer(重点:末尾有个新增节且空白),失败则返回NULL
LPVOID IncreaseSection( LPVOID pFileBuffer );
//功能:将内存中的数据写入文件
//参数:指向内存中数据的指针
//返回值:无
void WritePeFile(LPVOID pFileBuffer);
int main()
{
LPVOID pFileBuffer = NULL;
LPVOID pNewFileBuffer = NULL;
printf("please input: (for example: D:/user/Desktop/学习笔记/2020.6.22_PE目录项之导出表/myDll.dll )\n");
gets(FileName);
pFileBuffer = ReadPeFile( FileName );
if( pFileBuffer )
pNewFileBuffer = MoveExportDirectory( pFileBuffer );
if( pNewFileBuffer )
WritePeFile( pNewFileBuffer );
}
//功能:将相应程序读取到内存
//参数:指向程序名字(地址)指针
//返回值:指向该程序被读取到内存的指针
LPVOID ReadPeFile( char FileName[] )
{
LPVOID pFileBuffer = NULL;
FILE* pFile = NULL;
pFile = fopen( FileName , "rb");
if(!pFile)
{
printf("open file failure!\n");
return NULL;
}
fseek(pFile , 0 , SEEK_END);
FileSize = ftell(pFile);
fseek(pFile , 0 , SEEK_SET);
pFileBuffer = calloc(1 , FileSize);
if(!pFileBuffer)
{
printf("Failure to allocate memory space!\n");
fclose(pFile);
return NULL;
}
fread(pFileBuffer , FileSize , 1 ,pFile);
fclose(pFile);
return pFileBuffer ;
}
//功能:移动导出表中所有结构到新建的节区
//参数:指向该程序被读取到内存的指针
//返回值:返回一个新的pNewFileBuffer(重点:末尾节区已经复制的导出表的所有结构),失败则返回NULL。
LPVOID MoveExportDirectory( LPVOID pFileBuffer )
{
LPVOID pNewFileBuffer ;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS32 pNtHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory =NULL;
DWORD i;
// PDWORD pAddressOfFunctions = NULL;//函数地址表指针
// PWORD pAddressOfNameOrdinals = NULL;//函数序号表指针
PDWORD pAddressOfNames = NULL;//指向函数名字表指针
DWORD Offset = 0;//复制导出表结构的位置至DosHeader的偏移,相当于FOA
//第一步:在dll新增一个节区,并返回新增节区的FOA
pNewFileBuffer = IncreaseSection( pFileBuffer );
if(!pNewFileBuffer)
{
printf("sorry , increase section failure!\n");
free( pFileBuffer );
return NULL;
}
Offset = FileSize ;//偏移至新建的节区。
//读取这个PE结构
pDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuffer ;
pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );
pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pDataDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory ;
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)( (DWORD)pDosHeader + ConvertRvaToFoa( pDataDirectory->VirtualAddress , pNewFileBuffer) );//RVA转FOA再转现在内存中的真实地址
//第二步:将导出表那张大的数据表给复制到新增节区
//同时修正对应目录项中的VirtualAddress
memcpy( (PDWORD)( (DWORD)pDosHeader + Offset) , pExportDirectory , pDataDirectory->Size );
pDataDirectory->VirtualAddress = ConvertFoaToRva( Offset , pNewFileBuffer );//注意FOA转为RVA
//修正内存中导出表的位置
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)( (DWORD)pDosHeader + Offset) ;
//修正偏移
Offset += pDataDirectory->Size ;
//第三步:移动导出表所在的文件名到新到新节区
//同时修正导出表中的Name
memcpy( (PDWORD)( (DWORD)pDosHeader + Offset) , (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->Name ,pNewFileBuffer) ) , strlen( (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->Name ,pNewFileBuffer) ) ) );//注意RVA转FOA再转现在内存中的真实地址
pExportDirectory->Name = ConvertFoaToRva( Offset , pNewFileBuffer );
//修正偏移
Offset += strlen( (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->Name , pNewFileBuffer) ) ) + 1;
//第四步:将函数地址表挨着给复制过去
//同时修正导出表中的AddressOfFunctions
memcpy( (PDWORD)( (DWORD)pDosHeader + Offset) , (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->AddressOfFunctions ,pNewFileBuffer) ) , 4 * pExportDirectory->NumberOfFunctions );//注意RVA转FOA再转现在内存中的真实地址
pExportDirectory->AddressOfFunctions = ConvertFoaToRva( Offset , pNewFileBuffer );//注意FOA转为RVA
//修正偏移
Offset += 4 * pExportDirectory->NumberOfFunctions ;
//第五步:将函数序号表挨着复制过去
//同时修正导出表中的AddressOfNameOrdinals
memcpy( (PDWORD)( (DWORD)pDosHeader + Offset) , (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->AddressOfNameOrdinals , pNewFileBuffer ) ) , 2 * pExportDirectory->NumberOfNames );//注意RVA转FOA再转现在内存中的真实地址
pExportDirectory->AddressOfNameOrdinals = ConvertFoaToRva( Offset , pNewFileBuffer );//注意FOA转RVA
//修正偏移
Offset += 2 * pExportDirectory->NumberOfNames ;
//第六步:将函数名称表挨着复制过去
//同时修正导出表中的AddressOfNames
memcpy( (PDWORD)( (DWORD)pDosHeader + Offset) , (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->AddressOfNames ,pNewFileBuffer) ) , 4 * pExportDirectory->NumberOfNames );//注意RVA转FOA再转现在内存中的真实地址
pExportDirectory->AddressOfNames = ConvertFoaToRva( Offset , pNewFileBuffer ) ;//注意FOA转RVA
//修正偏移
Offset += 4 * pExportDirectory->NumberOfNames ;
//第七步:将函数名称表中指向的名称也挨着复制过去
//同时修正函数名称表
pAddressOfNames = (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( pExportDirectory->AddressOfNames ,pNewFileBuffer) );//注意RVA转FOA再转现在内存中的真实地址
for( i = pExportDirectory->NumberOfNames ; i>0 ; i--,pAddressOfNames++ )//遍历函数名字表,循环复制函数名过来
{
memcpy( (PDWORD)( (DWORD)pDosHeader + Offset) , (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( *pAddressOfNames , pNewFileBuffer) ), strlen( (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( *pAddressOfNames ,pNewFileBuffer) ) ) );//注意RVA转FOA再转现在内存中的真实地址
*pAddressOfNames = ConvertFoaToRva( Offset ,pNewFileBuffer ); //注意FOA转RVA
//修正偏移
Offset += strlen( (PDWORD)( (DWORD)pDosHeader + ConvertRvaToFoa( *pAddressOfNames ,pNewFileBuffer) ) ) +1;//偏移多加1,给名字留个末尾0
}
pDataDirectory->Size = 0x500 ;//NewFileSize - FileSize ;
return pNewFileBuffer;
}
//功能:将RVA转化为FOA
//参数:参数1:要转换的RVA的值(DWORD);参数2:指向文件缓冲区的指针(LPVOID)
//返回值:成功则返回对应的FOA,失败则返回0(这个RVA是没有对应的FOA,为内存拉伸后系统填充的地址,或者RVA超出内存范围)。
DWORD ConvertRvaToFoa( size_t RVA , LPVOID pFileBuffer )
{
int i ;//用于遍历节表
// size_t FOA = 0;
//定义表头指针
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS32 pNtHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//给表头赋初值
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader+4 );
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一个节表头
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);
if(RVA < pSectionHeader->VirtualAddress)//判断RVA是否在PE头区
{
if(RVA < pSectionHeader->PointerToRawData)
return RVA;//此时FOA == RVA
else
return 0;
}
for(i=0 ; i<pFileHeader->NumberOfSections ; i++)//循环遍历节表头
{
if( i )//遍历节表头,第一次不遍历,
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
if(RVA >= pSectionHeader->VirtualAddress )//是否大于这个节表的RVA
{
if( RVA <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData )//判断是否在这个节区
return ( RVA - pSectionHeader->VirtualAddress ) + pSectionHeader->PointerToRawData;//确定节区后,计算FOA
}
else//RVA不可能此时的pSectionHeader->VirtuallAddress小,除非是返回值为0的情况。
return 0;
}
return 0;
}
//功能:将FOA转换为RVA
//参数:参数1:要转换的FOA值(DWORD);参数2:指向文件内存的指针(LPVOID);
//返回值:成功则返回对应的RVA,失败返回0(此时为FOA越界)。
DWORD ConvertFoaToRva( size_t FOA , LPVOID pFileBuffer )
{
int i = 0;//用于遍历节表。
// size_t RVA = 0;
//定义表头指针
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS32 pNtHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//给表头赋初值
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );
pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一个节表头
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
if( FOA < pSectionHeader->PointerToRawData )//判断是否位于 头区
return FOA ; //这是RVA == FOA ;
for(i=0 ; i<pFileHeader->NumberOfSections ; i++)//循环遍历节表头
{
if( i )//遍历节表头,第一次不遍历,
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
if( FOA >= pSectionHeader->PointerToRawData )//是否大于这个节表的FOA
{
if( FOA < pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData )//判断是否在这个节表区域
return (FOA - pSectionHeader->PointerToRawData ) + pSectionHeader->VirtualAddress ;//计算并返回RVA
}
}
return 0;
}
//功能:添加一个节表和节区
//参数:指向文件读取到内存的指针
//返回值:返回一个新的pNewFileBUffer(重点:末尾有个新增节且空白),失败则返回NULL
LPVOID IncreaseSection( LPVOID pFileBuffer )
{
LPVOID pNewFileBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS32 pNtHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader_First = NULL;//多增加一个节表指针指向第一个节表首地址,查看PointerToRawData。
PIMAGE_SECTION_HEADER pSectionHeader_Last = NULL;//定义最后一个节表指针,方便获取VirtualAddress 和 SizeOfRawData
int i = 0;//循环节
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );
pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
pSectionHeader_First = pSectionHeader ;
//循环遍历,指向倒数第一个节表再往后移,指向我们将要添加节表 头 的位置 ,i>0。
for(i=pFileHeader->NumberOfSections ; i>0 ; i-- )
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
//先判断是否有节表的区域是否还有空间 用于添加节表。确保在SizeOfRawData范围内。
//这里一般要空出两个节表位置,一个用于我们添加新节表,一个用于空的空间填充0(约定俗成)。
if( ((DWORD)pDosHeader + pSectionHeader_First->PointerToRawData) - (DWORD)pSectionHeader < 2*IMAGE_SIZEOF_SECTION_HEADER )
{
//如果没有足够空间,就考虑缩短 pDosHeader->e_lfanew,有没有足够空间,用于开辟新的空间。0x40为DOS头的大小
if( (pDosHeader->e_lfanew - 0x40) + ( ((DWORD)pDosHeader + pSectionHeader_First->PointerToRawData) - (DWORD)pSectionHeader ) > 2*IMAGE_SIZEOF_SECTION_HEADER )
{
memcpy( (LPVOID)( (DWORD)pDosHeader+ 0x40) , pNtHeader , ( (DWORD)pSectionHeader- (DWORD)pFileHeader ) );
//修改e_lfanew
pDosHeader->e_lfanew = 0x40;
//重新计算各头区的偏移
pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );
pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
pSectionHeader_First = pSectionHeader ;
//同样 //循环遍历,指向倒数第一个节表再往后移,指向我们将要添加节表的位置 ,i>0。
for(i=pFileHeader->NumberOfSections ; i>0 ; i-- )
pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
//此时已经符合条件,有足够空间用于新增节表头。
//即退出if语句后。
}
else//没有足够空间则不能添加节,flag=0,失败
{
free(pFileBuffer);//释放空间
printf("Sorry,There is not enough space of new section header!\n");
return NULL; ;
}
}
//复制一个节表头过来。
memcpy( pSectionHeader , pSectionHeader_First , IMAGE_SIZEOF_SECTION_HEADER );
//更改节表名.NewSec
strcpy( pSectionHeader->Name,".NewSec");
//Misc.VirtualSize = 0,那下面构造内存3的文件状态时,就不会往节区填充垃圾数据
pSectionHeader->Misc.VirtualSize = 0;
//SizeOfRawData = FileAlignment
pSectionHeader->SizeOfRawData = pOptionHeader->FileAlignment ;
//RVA
pSectionHeader_Last = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader - IMAGE_SIZEOF_SECTION_HEADER);
pSectionHeader->VirtualAddress = pSectionHeader_Last->VirtualAddress + ( (pSectionHeader_Last->SizeOfRawData-1) / pOptionHeader->SectionAlignment + 1 )*pOptionHeader->SectionAlignment ;
//PointerToRawData
pSectionHeader->PointerToRawData = pSectionHeader_Last->PointerToRawData + pSectionHeader_Last->SizeOfRawData;
//Characteristics,权限,相 或 即可,我给他加上数据段的权限0x42000040
pSectionHeader->Characteristics = 0x40000040;
//SizeOfImage
pOptionHeader->SizeOfImage += ( (pSectionHeader->SizeOfRawData-1) / pOptionHeader->FileAlignment +1 ) * pOptionHeader->SectionAlignment ;
//NumberOfSection
pFileHeader->NumberOfSections += 1;
//*********************************************************
//注意:下面的代码是调整后,新加的,需要特别注意。
//更改文件大小,方便开辟新的空间 和 存盘。
NewFileSize = FileSize + pSectionHeader->SizeOfRawData ;
pNewFileBuffer = calloc( 1 , NewFileSize );
if(!pNewFileBuffer)
{
printf("Sorry , Allocte space failed!\n");
return NULL;
}
memcpy( pNewFileBuffer , pFileBuffer , FileSize);
//释放原来的文件缓冲区。
free(pFileBuffer);
return pNewFileBuffer;
}
//功能:将内存中的数据写入文件
//参数:指向内存中数据的指针
//返回值:无
void WritePeFile(LPVOID pNewFileBuffer)
{
FILE* pNewFile = NULL;
char NewFileName[10]="_New";
int len = 0;//文件名长度
//更改新的文件名
len = strlen( FileName );
strcat(NewFileName , FileName + len - 4 );//构造一个文件尾
strcpy(FileName + len - 4 , NewFileName );
pNewFile = fopen( FileName , "wb");
if(!pNewFile)
{
printf("file creation failed!\n");
return ;
}
fwrite(pNewFileBuffer , NewFileSize , 1 , pNewFile);
fclose(pNewFile);
free(pNewFileBuffer);
}
欢迎大家留言交流^ _ ^