深入理解PE结构_导入表注入实战
PE结构学习,其实最主要的学习节表、重定位表、导入表、IAT表;
1、是学习这些表的结构,理解各个表设计的原理
2、移动、修改、修复这些表中的内容,才能真正理解各个表的意思
注入的本质是什么,注入的本质是把自己编写的dll能够贴到目标程序的4GB(针对x86系统)虚拟内存中(不管哪些注入都是这个目的)。
前边刚学过导入表,本篇就介绍导入表注入:
通过编写代码,理解导入表各个结构的意思,并深入理解导入表的作用。
第一步:根据数据目录项得到导入表的VA和SIZE,这2个值最后都需要修正
第二步:需要新增的空间大小,PE32+:新增一个导入表20字节,6个字节的INT,8个字节IAT,dll字符串长度+1,函数名长度+1+2
PE64+:新增一个导入表20字节,16个字节的INT,16个字节IAT,dll字符串长度+1,函数名长度+1+2
第三步:判断哪个空间的空白区 > Size(原导入表大小) + 20 + 32 + dll_Length+1 + FunctionNameLength + 3
或者新增一个节,新增节的时候注意,节的属性要为NewSectionHeader->Characteristics = 0xC0000040;不然可能最后代码跑不起来,我是遇到这样的问题。
第四步:在新的导入表后边,追加一个导入表
第五步:追加8个字节的INT表,8个字节的IAT表;PE64得追加16个字节的INT表和16个字节的IAT表
第六步 追加一个IMAGE_BY_NAME结构,前2个字节是0,后边是函数名字符串
第七步:将IMAGE_BY_NAME的RVA赋值给INT和IAT表中的第一项
第八步:分配空间存储DLL名称字符串,兵将该字符串的RVA赋值给Name属性
第九步:修正IMAGE_DATA_DIRECTORY结构中的VA和SIZE
#include "stdafx.h"
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <time.h>
//
#define DiskFileParh "IATunstand.exe" //"notepad.exe"//"kernel32.dll" // "LearnDll.dll"//
#define SaveDll "IATunstand_release3.exe"
#define IATInjectDllName "IATInject.dll"
#define IATInjectDllFunctionName "IATInFun"
#define word unsigned short
#define dword unsigned int
#define ULONGLONG unsigned long long
#define OLDIMAGEBASEADDRESS 0x10000000
#define AddSectionSpec 0x00001000
struct PE_ntHeader{
unsigned char PEsig[4];
word Machine;
word NumberOfSections;
dword TimeDateStamp;
dword PointerToSymbolTable;
dword NumberOfSymbols;
word SizeofOptionalHeader;
word Characteristics;
};
struct IMAGE_DATA_DICECTORY{
dword RVA;
dword SIZE;
};
struct PE_OptionalHeader32{
word Magic ;
unsigned char MajorLinkerVersion ;
unsigned char MinorLinkerVersion ;
dword SizeOfCode ;
dword SizeOfIntiaizeData ;
dword SizeOfUninitalizedData ;
dword AddressOfEntryPoint;
dword BaseOfCode ;
dword BaseOfData;
dword ImageBase;
dword SectionAlignment ;
dword FileAlignment ;
word MajorOperationSystemVersion ;
word MinorOperationSystemVersion ;
word MajorImageVersion ;
word MinorImageVersion;
word MajorSubSystemVersion ;
word MinorSubSystemVersion;
dword Win32VersionVaue ;
dword SizeOfImage ;
dword SizeOfHeaders ;
dword CheckSum ;
word SubSystem ;
word DllCharacteristics ;
dword SizeOfStackReverse ;
dword SizeOfStackCommit ;
dword SIzeOfHeapReverse;
dword SizeOfHeapCommit;
dword LoaderFlags ;
dword NumberOfRvaAndSizes ;
IMAGE_DATA_DICECTORY DataDirectory[16] ;
};
struct PE_OptionalHeader64{
word Magic ;
unsigned char MajorLinkerVersion ;
unsigned char MinorLinkerVersion;
dword SizeOfCode ;
dword SizeOfIntiaizeData ;
dword SizeOfUninitalizedData;
dword AddressOfEntryPoint ;
dword BaseOfCode ;
ULONGLONG ImageBase ;
dword SectionAlignment ;
dword FileAlignment ;
word MajorOperationSystemVersion ;
word MinorOperationSystemVersion ;
word MajorImageVersion ;
word MinorImageVersion ;
word MajorSubSystemVersion ;
word MinorSubSystemVersion ;
dword Win32VersionValue;
dword SizeOfImage ;
dword SizeOfHeaders ;
dword CheckSum ;
word SubSystem ;
word DllCharacteristics;
ULONGLONG SizeOfStackReverse ;
ULONGLONG SizeOfStackCommit;
ULONGLONG SIzeOfHeapReverse ;
ULONGLONG SizeOfHeapCommit;
dword LoaderFlags ;
dword NumberOfRvaAndSizes ;
IMAGE_DATA_DICECTORY DataDirectory[16];
};
struct SectionHeader{
unsigned char Name[8];
union{
dword PhysicalAddress;
dword VirtualSize;
}Misc;
dword VirtualAddress ;
dword SizeOfRawData;
dword PointerToRawData ;
dword PointerToRelocations;
dword PointerToLinenumbers;
word NumberOfRelocations ;
word NumberOfLinenumbers ;
dword Characteristics ;
} ;
struct Image_Ex_Directory{
dword Characteristics;
dword TimeStamp;
word MajorVersion;
word MinorVersion;
dword Name;
dword Base;
dword NumberOfFunctions;
dword NumberOfNames;
dword AddressOfFunctions;
dword AddressOfNames;
dword AddressOfNameOrdinals;
};
struct Image_Relocation{
dword VirtualAddress;
dword SizeOfBlock;
//word TypeOffset[1];
};
struct Image_ImportTable{
union {
dword Characteristics;// 0 for terminating null import descriptor
dword OriginalFirstThunk;// RVA 指向(IMAGE_THUNK_DATA)结构体数组 INT
} INTName;
dword TimeDateStamp; // 时间戳 0 if not bound
dword ForwarderChain; // -1 if no forwarders
dword Name; //RVA dll文件名称
dword FirstThunk; // RVA to IAT 导入地址表 IMAGE_THUNK_DATA数组
};
struct Image_ImportByName{
word Hint;//导入函数索引
char Name[1];
};
struct Image_Bound_Import{
dword TimeDateStamp; //真正的绑定时间,时间戳
word OffsetModuleName; //
word NumberOfModuleForwarderRefs; //,一个dll中可能需要导入其他的dll中的函数,这个值就是指依赖的其他dll有几个
};
struct Module_Forwarder_Ref{
dword TimeDateStamp; //时间戳,检查引用的其他dll是否被更新
dword OffsetModuleName; //引用的其他dll的名字
dword Reserved; //保留(未使用)
};
int ReadDiskFileToFileBuffer(void* path,void** FileBuffer){
FILE* fp = fopen(DiskFileParh,"rb");
if(fp == NULL)
{
printf("打开磁盘文件失败!\n");
///
return 0;
}
fseek(fp,0,SEEK_END);
long pos = ftell(fp);
printf("磁盘文件大小为%d字节\n",pos);
fseek(fp,0,SEEK_SET);
void* MemFileBuffer=malloc(pos);
if(MemFileBuffer==NULL)
printf("分配内存失败\n");
else
{
printf("分配内存成功\n");
memset(MemFileBuffer,0,pos);
}
//memcpy(FileData,OpenFile,pos);
fread(MemFileBuffer,pos,1,fp);
*FileBuffer = MemFileBuffer;
fclose(fp);
return 1;
}
void MovFileHeadersToStub(void* lpFileBuffer,void*** lpmemBuffer)
{
//void* lpFileBuffer=**lpmemBuffer;
unsigned int* NT_offset =(unsigned int* )((unsigned char*)lpFileBuffer + 60);
PE_ntHeader* lp_NT_header =(PE_ntHeader*)((unsigned char*)lpFileBuffer + *NT_offset);
//Image_OPtional_header* lp_Optional_header =(Image_OPtional_header*)(lpFileBuffer1 + *NT_offset + 24);
int AllHeadSize= 24 + lp_NT_header->SizeofOptionalHeader +lp_NT_header->NumberOfSections*40;
void* MoveHead = malloc(AllHeadSize + 80);//预留2个节表的空间
memset(MoveHead,0,AllHeadSize+80);
memcpy(MoveHead,(unsigned char*)lpFileBuffer+*NT_offset,AllHeadSize);
memset((unsigned char*)lpFileBuffer+64,0,AllHeadSize+*NT_offset-60);
memcpy((unsigned char*)lpFileBuffer+64,MoveHead,AllHeadSize+ 80);
*NT_offset = 0x40;
free(MoveHead);
PE_ntHeader* lp_Moved_NT_header =(PE_ntHeader*)((unsigned char*)lpFileBuffer+ *NT_offset);
if(lp_Moved_NT_header->SizeofOptionalHeader == 0xE0)
{
PE_OptionalHeader32* lp_Moved_Optional_header = (PE_OptionalHeader32*)((unsigned char*)lpFileBuffer+ *NT_offset +24);
SectionHeader* lp_Moved_NewSection_header = (SectionHeader*)((unsigned char*)lpFileBuffer+ *NT_offset +AllHeadSize);
SectionHeader* lp_Moved_LastSection_header= (SectionHeader*)((unsigned char*)lpFileBuffer+ *NT_offset +AllHeadSize-40);
//修改新节表中的内容
memset(lp_Moved_NewSection_header->Name,0,8);
memcpy(lp_Moved_NewSection_header->Name,".NewSec",8);
lp_Moved_NewSection_header->Misc.VirtualSize = 0x00001000;
lp_Moved_NewSection_header->VirtualAddress=lp_Moved_Optional_header->SizeOfImage;//一种是SizeOfImage,另一种是最后一个节的内存偏移+maxsize(Misc,RawOfData)
lp_Moved_NewSection_header->SizeOfRawData =AddSectionSpec;
lp_Moved_NewSection_header->PointerToRawData=lp_Moved_LastSection_header->PointerToRawData+lp_Moved_LastSection_header->SizeOfRawData;
lp_Moved_NewSection_header->Characteristics=0x20000060;
lp_Moved_NT_header->NumberOfSections = lp_Moved_NT_header->NumberOfSections+1;
lp_Moved_Optional_header->SizeOfImage =lp_Moved_Optional_header->SizeOfImage + AddSectionSpec;
//*NT_offset = 0x60;
//0、先0x1000扩大空间
void* lpNewFileBuffer = malloc(lp_Moved_LastSection_header->PointerToRawData+lp_Moved_LastSection_header->SizeOfRawData+AddSectionSpec);
memset(lpNewFileBuffer,0,lp_Moved_LastSection_header->PointerToRawData+lp_Moved_LastSection_header->SizeOfRawData+AddSectionSpec);
printf("lpNewFileBuffer:%x\n",lpNewFileBuffer);
//1、把原来的内容原封不动的复制到这个大空间中。
memcpy(lpNewFileBuffer,lpFileBuffer,lp_Moved_LastSection_header->PointerToRawData+lp_Moved_LastSection_header->SizeOfRawData);
//lpFileBuffer1 = (char*)lpNewFileBuffer;这句也是指针复制
**lpmemBuffer = lpNewFileBuffer;
//free(lpNewFileBuffer);
}else if(lp_Moved_NT_header->SizeofOptionalHeader == 0xF0)
{
PE_OptionalHeader64* lp_Moved_Optional_header = (PE_OptionalHeader64*)((unsigned char*)lpFileBuffer+ *NT_offset +24);
SectionHeader* lp_Moved_NewSection_header = (SectionHeader*)((unsigned char*)lpFileBuffer+ *NT_offset +AllHeadSize);
SectionHeader* lp_Moved_LastSection_header= (SectionHeader*)((unsigned char*)lpFileBuffer+ *NT_offset +AllHeadSize-40);
//修改新节表中的内容
memset(lp_Moved_NewSection_header->Name,0,8);
memcpy(lp_Moved_NewSection_header->Name,".NewSec",8);
lp_Moved_NewSection_header->Misc.VirtualSize = AddSectionSpec;
lp_Moved_NewSection_header->VirtualAddress=lp_Moved_Optional_header->SizeOfImage;//一种是SizeOfImage,另一种是最后一个节的内存偏移+maxsize(Misc,RawOfData)
lp_Moved_NewSection_header->SizeOfRawData =AddSectionSpec;
lp_Moved_NewSection_header->PointerToRawData=lp_Moved_LastSection_header->PointerToRawData+lp_Moved_LastSection_header->SizeOfRawData;
lp_Moved_NewSection_header->Characteristics=0x20000060;
lp_Moved_NT_header->NumberOfSections = lp_Moved_NT_header->NumberOfSections+1;
lp_Moved_Optional_header->SizeOfImage =lp_Moved_Optional_header->SizeOfImage + AddSectionSpec;
//*NT_offset = 0x60;
//0、先0x1000扩大空间
void* lpNewFileBuffer = malloc(lp_Moved_LastSection_header->PointerToRawData+lp_Moved_LastSection_header->SizeOfRawData+AddSectionSpec);
memset(lpNewFileBuffer,0,lp_Moved_LastSection_header->PointerToRawData+lp_Moved_LastSection_header->SizeOfRawData+AddSectionSpec);
printf("lpNewFileBuffer:%x\n",lpNewFileBuffer);
//1、把原来的内容原封不动的复制到这个大空间中。
memcpy(lpNewFileBuffer,lpFileBuffer,lp_Moved_LastSection_header->PointerToRawData+lp_Moved_LastSection_header->SizeOfRawData);
//lpFileBuffer1 = (char*)lpNewFileBuffer;这句也是指针复制
**lpmemBuffer = lpNewFileBuffer;
}
}
unsigned int RvaToFoa(void* FileBuffer, int RvaAddress){
unsigned int* NTOffset = (unsigned int*)((char*)FileBuffer + 60);
PE_ntHeader* lpNtHeader=(PE_ntHeader*)((char*)FileBuffer + *NTOffset);
SectionHeader* SectionFirsteader = (SectionHeader*)((char*)FileBuffer + *NTOffset + 24 + lpNtHeader->SizeofOptionalHeader);
if(lpNtHeader->SizeofOptionalHeader == 0xE0)
{
PE_OptionalHeader32* lpOPtionalHeader = (PE_OptionalHeader32*)((char*)FileBuffer + *NTOffset + 24 );
if(RvaAddress<=lpOPtionalHeader->SizeOfHeaders)
return RvaAddress;
}else if(lpNtHeader->SizeofOptionalHeader == 0xF0)
{
PE_OptionalHeader64* lpOPtionalHeader = (PE_OptionalHeader64*)((char*)FileBuffer + *NTOffset + 24 );
if(RvaAddress<=lpOPtionalHeader->SizeOfHeaders)
return RvaAddress;
}
for(int j=lpNtHeader->NumberOfSections-1;j>=0;j--)
{
if(RvaAddress >= SectionFirsteader[j].VirtualAddress)
{
return SectionFirsteader[j].PointerToRawData + RvaAddress - SectionFirsteader[j].VirtualAddress;
//x-SectionFirsteader[j].PointerToRawData = RvaAddress - VirtualAddress
}else
continue;
}
return 0;
}
unsigned int FoaToRva(void* FileBuffer, int FileOffset){
unsigned int* NTOffset = (unsigned int*)((char*)FileBuffer + 60);
PE_ntHeader* lpNtHeader=(PE_ntHeader*)((char*)FileBuffer + *NTOffset);
SectionHeader* SectionFirsteader = (SectionHeader*)((char*)FileBuffer + *NTOffset + 24 + lpNtHeader->SizeofOptionalHeader);
for(int j=lpNtHeader->NumberOfSections-1;j>=0;j--)
{
if(FileOffset >= SectionFirsteader[j].PointerToRawData)
{
return FileOffset - SectionFirsteader[j].PointerToRawData + SectionFirsteader[j].VirtualAddress;
//FileOffset-SectionFirsteader[j].PointerToRawData = RvaAddress(x) - VirtualAddress
}else
continue;
}
return 0;
}
void AddNewSection(void* FileBuffer,void** NewFileBuffer){
//第一种判断节区最后有80个空字节没
//第二种移动到Dos Stub
//第三种合并成一个节
//第四种扩大最后一个节
unsigned int* Nt_offset = (unsigned int*)((unsigned char*)FileBuffer+60);
PE_ntHeader* lpNtHeader = (PE_ntHeader*)((unsigned char*)FileBuffer + *Nt_offset);
PE_OptionalHeader32* lpOptionHead32 = (PE_OptionalHeader32*)((unsigned char*)FileBuffer + *Nt_offset + 24 );
unsigned char* SpecNum=((unsigned char*)FileBuffer + *Nt_offset + 24 + lpNtHeader->SizeofOptionalHeader + lpNtHeader->NumberOfSections*40);
SectionHeader* FrstSectionHeader = (SectionHeader*)((unsigned char*)FileBuffer + *Nt_offset + 24 + lpNtHeader->SizeofOptionalHeader);
SectionHeader* LastSectionHeader = FrstSectionHeader + lpNtHeader->NumberOfSections-1;
SectionHeader* NewSectionHeader = FrstSectionHeader + lpNtHeader->NumberOfSections;
unsigned char i=0;
while(SpecNum[i] == 0x0 && i<=79)
i++;
if(i>=79)
{
printf("节后空间够,可以添加新节!\n");
//计算要添加节的空间
//把最后一个节的内容复制到新节中
memcpy(SpecNum,FrstSectionHeader + lpNtHeader->NumberOfSections-1,40);
//然后修改新节的内容
char* NewSectionName=".NewSec";
memcpy(SpecNum,NewSectionName,8);
NewSectionHeader->VirtualAddress = lpOptionHead32->SizeOfImage;
NewSectionHeader->Misc.VirtualSize = AddSectionSpec;
NewSectionHeader->SizeOfRawData = AddSectionSpec;
NewSectionHeader->PointerToRawData = LastSectionHeader->PointerToRawData + LastSectionHeader->SizeOfRawData;
NewSectionHeader->Characteristics = 0xC0000040;
lpNtHeader->NumberOfSections++;
lpOptionHead32->SizeOfImage = lpOptionHead32->SizeOfImage+AddSectionSpec;
void* NewBuffer = malloc(NewSectionHeader->PointerToRawData+AddSectionSpec);
memset(NewBuffer,0,NewSectionHeader->PointerToRawData+AddSectionSpec);
memcpy(NewBuffer,FileBuffer,NewSectionHeader->PointerToRawData);
*NewFileBuffer = NewBuffer;
//存盘
}else{
printf("节后空间不够,需要移动、合并或者扩大节,现在采取移动头部到Dos Stub的位置!\n");
MovFileHeadersToStub(FileBuffer,&NewFileBuffer);
return;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
void* FileBuffer = NULL;
void* NewFileBuffer = NULL;
ReadDiskFileToFileBuffer(DiskFileParh,&FileBuffer);
if(FileBuffer==NULL)
{
printf("读取文件失败");
return 0;
}
//PrintfDataTatDirectory(FileBuffer);
AddNewSection(FileBuffer,&NewFileBuffer);
unsigned int MovLength =0;
MoveImportToNewSection(NewFileBuffer,MovLength);
getchar();
return 0;
}
上边是导入表注入代码。
我们还需要一个自己生成一个IATInject.dll,里面含有一个IATInFun()函数。生成动态链接库的方法,前文有介绍,这里简单介绍一下:
先新建一个IATInject的win32项目

然后再选择DLL 项目,完成

然后在项目右键---->添加一个新的类


完成之后,在我们新建的InjectTest.cpp编写导出函数和普通函数代码

然后再头文件中编写函数声明

最后再dllMain入口中编写,当exe加载dll或者卸载dll的时候弹框

然后F7生成,要注入的dll

最后我们用前边的生成的exe,把要注入的程序运行一下,注入之后,把该dll放在注入后的exe同一个目录,运行之后,就会发现已成功注入。

浙公网安备 33010602011771号