1.申请ID:存钱买板子
2.个人邮箱:yuan6900956@163.com
3.原创技术文章:PE导入表注入DLL
分享一下32位PE文件如何利用导入表注入DLL,以及原创C代码。
1.原理:
当一个程序启动的时候,它所依赖的各种DLL也会被加载,而当一个DLL被加载或者销毁的时候,系统会调用这个DLL的DllMain函数。
而这些个需要加载的DLL都是什么,都提供了哪些函数,都被记录在了导入表里面。所以我们可以在导入表里面新加一个要被注入的DLL信息,以此达到我们的目的。
2.实现步骤:
1>把PE文件加载到FileBuffer: 2>遍历一下原来有多少个DLL,后面复制的时候会它求尺寸:(注意:一个PE文件可能有多个导入表结构,最后有一个全0结构标志结束)
3>新增一个节:(注意:新增节必须有可执行的属性)
4>给DLL名字字符串和导入函数名字字符串赋值:(注意:不要忘了字符串末尾的0)
5>给_IMAGE_THUNK_DATA32结构(INT表和IAT所指向的)赋值(这个结构最高位为1说明是以序号导出,否则以名字导出,此时为IMAGE_IMPORT_BY_NAME结构的RVA):
6>拷贝原导入表到新增节:(注意:不要忘了最后那个全0结构)
7>给新导入表赋值:(注意:这里面的地址都为RVA)
8>修正目录项第二个的RVA:
9>存盘
注入后新增节的内存图:
3.注入后效果:
把加载时弹窗的DLL注入到CE里效果:
4.完整C代码:(VS2010)
Inject.cpp:
[C++] 纯文本查看 复制代码 #include "InjectH.h"
#define InPutPath "E:\\测试区\\ce.exe" //输入文件目录
#define OutPutPath "E:\\测试区\\ce_new.exe" //输出文件目录
#define SizeOfNewSection 0x1000 //新增节尺寸
_PIMAGE_DOS_HEADER DosHeader = NULL; //DOS头指针
_PIMAGE_NT_HEADER NtHeader = NULL; //NT头指针
_PIMAGE_FILE_HEADER FileHeader = NULL; //标准PE头指针
_PIMAGE_OPTIONAL_HEADER OptionalHeader = NULL; //可选PE头指针
_PIMAGE_SECTION_HEADER pSectionHeader = NULL; //节表指针
_PIMAGE_IMPORT_DESCRIPTOR pImportDesriptor = NULL; //导入表指针
BYTE DllName[] = "InjectDll.dll"; //要注入的DLL名字
BYTE ImportFunName[] = "ExportFunction"; //DLL中函数名
BYTE NewSectionName[] = "Fly"; //新增节名字
//加载PE,加载到FileBuffer
size_t LoadPE(STR Path , PBYTE* Buffer)
{
//打开文件
FILE* fp = fopen(Path,"rb");
if(!fp)
{
printf("文件打开失败\n");
return 0;
}
//获取文件尺寸
fseek(fp,0,SEEK_END);
size_t FileSize = ftell(fp);
fseek(fp,0,SEEK_SET);
//申请文件缓冲区
BYTE* pFileBuffer = (BYTE*)malloc(FileSize);
if( !pFileBuffer )
{
printf("内存申请失败!\n");
fclose(fp);
return 0;
}
//内存拷贝
memset(pFileBuffer,0,FileSize);
if(!fread(pFileBuffer,FileSize,1,fp))
{
printf("写入缓冲区失败!\n");
free(pFileBuffer);
fclose(fp);
return 0;
}
*Buffer = pFileBuffer;
//判断是否是PE文件
if(*((WORD*)pFileBuffer) != 0x5A4D)
{
printf("不是有效的MZ标志\n");
free(pFileBuffer);
fclose(fp);
return 0;
}
DosHeader = (_PIMAGE_DOS_HEADER)pFileBuffer;
pFileBuffer = pFileBuffer + ((_PIMAGE_DOS_HEADER)pFileBuffer)->e_lfanew; //偏移到NT头
if(((_PIMAGE_NT_HEADER)pFileBuffer)->Signature != 0x4550)
{
printf("不是有效的PE标志\n");
free(*Buffer);
fclose(fp);
return 0;
}
NtHeader = (_PIMAGE_NT_HEADER)pFileBuffer; //获取NT头
pFileBuffer = pFileBuffer + 0x4; //偏移到标准PE头
FileHeader = (_PIMAGE_FILE_HEADER)pFileBuffer; //获取标准PE头
pFileBuffer = pFileBuffer + 0x14; //偏移到可选PE头
OptionalHeader = (_PIMAGE_OPTIONAL_HEADER)pFileBuffer; //获取可选PE头
pFileBuffer = pFileBuffer + FileHeader->SizeOfOptionalHeader; //偏移到节表
pSectionHeader = (_PIMAGE_SECTION_HEADER)pFileBuffer; //获取节表
fclose(fp);
return FileSize;
}
//获取对齐后的值
int Align(int Value,int align)
{
if(Value % align == 0)
{
return Value;
}
return ((Value / align) + 1) * align;
}
//RVA->FOA
DWORD RvaToFoa(DWORD dwRva)
{
DWORD dwFoa = 0;
//判断是否在头+节表中
if(dwRva <= OptionalHeader->SizeOfHeaders)
{
dwFoa = dwRva;
return dwFoa;
}
//判断在是否在节中
int i;
for(i = 0; i < FileHeader->NumberOfSections; i++)
{
if(dwRva >= pSectionHeader[i].VirtualAddress && dwRva <= (pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData))
{
dwFoa = dwRva - pSectionHeader[i].VirtualAddress;
return dwFoa + pSectionHeader[i].PointerToRawData;
}
}
}
//FOA->RVA
DWORD FoaToRva(DWORD dwFoa)
{
DWORD dwRva = 0;
//判断是否在头+节表中
if(dwFoa <= OptionalHeader->SizeOfHeaders)
{
dwRva = dwFoa;
return dwRva;
}
//判断是否在节中
int i = 0;
for(i = 0; i < FileHeader->NumberOfSections; i++)
{
if(dwFoa >= pSectionHeader[i].PointerToRawData && dwFoa <= (pSectionHeader[i].PointerToRawData + pSectionHeader[i].SizeOfRawData))
{
dwRva = dwFoa - pSectionHeader[i].PointerToRawData;
return dwRva + pSectionHeader[i].VirtualAddress;
}
}
}
// 存盘
STAUS SaveData(PBYTE buffer,STR Path,size_t FileSize)
{
FILE* fp = fopen(Path,"wb+");
if(!fp)
{
printf("文件打开失败\n");
return FALSE;
}
//写入文件
if(!fwrite(buffer,FileSize,1,fp))
{
printf("文件写入失败!\n");
return FALSE;
}
fclose(fp);
return TRUE;
}
//提升所有头+节表
STAUS PromoteHeaders(PBYTE FileBuffer)
{
PBYTE pFileBuffer = FileBuffer;
DWORD SizeOfCopy = (DWORD)(&pSectionHeader[FileHeader->NumberOfSections]) - (DWORD)(FileBuffer + DosHeader->e_lfanew);
DWORD FillSize = (DWORD)(&pSectionHeader[FileHeader->NumberOfSections]) - (DWORD)FileBuffer - 0x40 - SizeOfCopy;
//拷贝所有头+节表
for(DWORD i = 0; i < SizeOfCopy; i++)
{
(FileBuffer + 0x40)[i] = (FileBuffer + DosHeader->e_lfanew)[i];
}
//填充
memset((FileBuffer + 0x40 + SizeOfCopy),0x0,FillSize);
//修正e_lfanew
DosHeader->e_lfanew = 0x40;
//重新修正所有头+节表指针
pFileBuffer = pFileBuffer + DosHeader->e_lfanew; //偏移到NT头
NtHeader = (_PIMAGE_NT_HEADER)pFileBuffer; //获取NT头
pFileBuffer = pFileBuffer + 0x4; //偏移到标准PE头
FileHeader = (_PIMAGE_FILE_HEADER)pFileBuffer; //获取标准PE头
pFileBuffer = pFileBuffer + 0x14; //偏移到可选PE头
OptionalHeader = (_PIMAGE_OPTIONAL_HEADER)pFileBuffer; //获取可选PE头
pFileBuffer = pFileBuffer + FileHeader->SizeOfOptionalHeader; //偏移到节表
pSectionHeader = (_PIMAGE_SECTION_HEADER)pFileBuffer; //获取节表
return TRUE;
}
//新增节
STAUS AddSectionPlus(PBYTE FileBuffer,PBYTE* NewBuffer,size_t FileSize)
{
//申请一块新的FileBuffer
*NewBuffer = (BYTE*)malloc(FileSize + SizeOfNewSection);
memset(*NewBuffer,0x0,SizeOfNewSection + FileSize);
//把原来的内容拷贝过来
for(int i = 0; i < FileSize; i++)
(*NewBuffer)[i] = FileBuffer[i];
//更新所有指针
PBYTE pNewBuffer = *NewBuffer;
DosHeader = (_PIMAGE_DOS_HEADER)pNewBuffer; //获取DOS头
pNewBuffer = pNewBuffer + DosHeader->e_lfanew; //偏移到NT头
NtHeader = (_PIMAGE_NT_HEADER)pNewBuffer; //获取NT头
pNewBuffer = pNewBuffer + 0x4; //偏移到标准PE头
FileHeader = (_PIMAGE_FILE_HEADER)pNewBuffer; //获取标准PE头
pNewBuffer = pNewBuffer + 0x14; //偏移到可选PE头
OptionalHeader = (_PIMAGE_OPTIONAL_HEADER)pNewBuffer; //获取可选PE头
pNewBuffer = pNewBuffer + FileHeader->SizeOfOptionalHeader; //偏移到节表
pSectionHeader = (_PIMAGE_SECTION_HEADER)pNewBuffer; //获取节表
//判断节表空间是否充足
if( OptionalHeader->SizeOfHeaders - (DWORD)(&(pSectionHeader[FileHeader->NumberOfSections]) - (DWORD)(*NewBuffer)) < 0x50 )
{
printf("节表空间不足!\n");
PromoteHeaders(*NewBuffer); //提升所有头+节表
}
//偏移到要添加节表的位置
pNewBuffer = *NewBuffer + (DWORD)(&pSectionHeader[FileHeader->NumberOfSections]) - (DWORD)(*NewBuffer);
//后面填充一个全0结构
memset(pNewBuffer + 0x28,0x0,0x28);
//设置节表内容
for(int i = 0; i< strlen((char*)NewSectionName); i++) //设置节表名字
((PBYTE)((_PIMAGE_SECTION_HEADER)pNewBuffer)->Name)[i] = NewSectionName[i];
((PBYTE)((_PIMAGE_SECTION_HEADER)pNewBuffer)->Name)[strlen((char*)NewSectionName)] = 0x0;
((_PIMAGE_SECTION_HEADER)pNewBuffer)->Misc.VirtualSize = (DWORD)SizeOfNewSection; //设置内存中大小
DWORD MaxSize = pSectionHeader[FileHeader->NumberOfSections - 1].SizeOfRawData > pSectionHeader[FileHeader->NumberOfSections - 1].Misc.VirtualSize ?
pSectionHeader[FileHeader->NumberOfSections - 1].SizeOfRawData : pSectionHeader[FileHeader->NumberOfSections - 1].Misc.VirtualSize;
DWORD SizeOfData = Align(MaxSize,OptionalHeader->SectionAlignment);
((_PIMAGE_SECTION_HEADER)pNewBuffer)->VirtualAddress = pSectionHeader[FileHeader->NumberOfSections - 1].VirtualAddress + SizeOfData; //设置内存中偏移
((_PIMAGE_SECTION_HEADER)pNewBuffer)->SizeOfRawData = SizeOfNewSection; //设置文件中大小
((_PIMAGE_SECTION_HEADER)pNewBuffer)->PointerToRawData = pSectionHeader[FileHeader->NumberOfSections - 1].PointerToRawData + SizeOfData; //设置文件中偏移
for(int i = 0; i < FileHeader->NumberOfSections - 1; i++)
{
((_PIMAGE_SECTION_HEADER)pNewBuffer)->Characteristics = ((_PIMAGE_SECTION_HEADER)pNewBuffer)->Characteristics | pSectionHeader[i].Characteristics; //设置属性
}
//修改节表的数量
FileHeader->NumberOfSections++;
//设置节的内容
memset(*NewBuffer + pSectionHeader[FileHeader->NumberOfSections - 1].PointerToRawData,0x7,SizeOfNewSection);
//修改SizeOfImage
OptionalHeader->SizeOfImage = OptionalHeader->SizeOfImage + SizeOfNewSection;
return TRUE;
}
//取字符串长度(不包括0)
int strlen(char* s)
{
int ret = 0;
while(*s++) ret++;
return ret;
}
//导入表注入
STAUS ImportInject()
{
int flag = 1; //标记所有导入表结束
int sum = 0; //统计DLL个数时候用
PBYTE pTemp = NULL; //游走指针
PBYTE poTemp = NULL;
PBYTE BeginOfNewSection = NULL; //新增节开始位置
PBYTE BeginOfINT = NULL; //INT表开始位置
PBYTE BeginOfImportName = NULL; //名字导入结构开始位置
PBYTE BeginOfDLLName = NULL; //DLL名字开始位置
DWORD NewBeginAddr = 0; //保存新节开始位置
int NumberOfImport = 0; //记录原来的导入表个数
//加载PE文件
PBYTE FileBuffer = NULL;
PBYTE NewBuffer = NULL;
size_t FileSize = LoadPE(InPutPath,&FileBuffer);
if( !FileSize )
{
printf("加载PE文件失败!\n");
return FALSE;
}
//统计一共有多少个DLL
pTemp = (PBYTE)(RvaToFoa(OptionalHeader->DataDirectory[1].VirtualAddress) + (DWORD)FileBuffer);
while(flag)
{
flag = 0;
//判断导入表结束
for(int j = 0; j < sizeof(_IMAGE_IMPORT_DESCRIPTOR); j++)
{
if( ((PBYTE)pTemp)[j] != 0 )
{
flag = 1;
break;
}
}
if( sum == 0x14 )
{
NumberOfImport++;
sum = 0;
}
sum++;
pTemp++;
}
//新增一个节
if( !AddSectionPlus(FileBuffer , &NewBuffer , FileSize) )
{
printf("新增节失败!\n");
return FALSE;
}
BeginOfNewSection = NewBuffer + pSectionHeader[FileHeader->NumberOfSections - 1].PointerToRawData; //获取新增节开始位置
//获取导入表指针
pImportDesriptor = (_PIMAGE_IMPORT_DESCRIPTOR)(RvaToFoa(OptionalHeader->DataDirectory[1].VirtualAddress) + (DWORD)NewBuffer);
pTemp = (PBYTE)BeginOfNewSection;
//先拷贝DLL名字
BeginOfDLLName = pTemp;
for(int i = 0; i < strlen((char*)DllName); i++)
{
*(pTemp + i) = DllName[i];
}
*(pTemp + strlen((char*)DllName)) = 0x0;
//再拷贝导入名字结构(前俩字节为0)
pTemp = pTemp + strlen((char*)DllName) + 0x1;
BeginOfImportName = pTemp;
*((PWORD)pTemp) = 0x0;
for(int i = 0; i < strlen((char*)ImportFunName); i++)
{
*(pTemp + 0x2 + i) = ImportFunName[i];
}
*(pTemp + 0x2 + strlen((char*)ImportFunName)) = 0x0;
//再拷贝INT表和IAT表所指向的那个结构
pTemp = pTemp + strlen((char*)ImportFunName) + 0x2 + 0x1;
BeginOfINT = pTemp ;
*((PDWORD)BeginOfINT) = FoaToRva( (DWORD)BeginOfImportName - (DWORD)NewBuffer); //指向名字导出位置
*((PDWORD)BeginOfINT + 1) = 0x0; //后四个字节为0
//再拷贝原导入表到新增节
pTemp = pTemp + 0x8;
NewBeginAddr = (DWORD)pTemp;
for(int i = 0; i < (NumberOfImport + 2) * 0x14; i++)
{
pTemp[i] = ((PBYTE)pImportDesriptor)[i];
}
//先把pTemp指针偏移到全0结构
flag = 1;
while(flag)
{
flag = 0;
//判断导入表结束
for(int j = 0; j < sizeof(_IMAGE_IMPORT_DESCRIPTOR); j++)
{
if( ((PBYTE)pTemp)[j] != 0 )
{
flag = 1;
break;
}
}
pTemp++;
}
//全0结构,后40个字节再补充全0结构
memset(pTemp + 0x14,0x0,0x14);
//填充新增导入表
((_PIMAGE_IMPORT_DESCRIPTOR)pTemp)->OriginalFirstThunk = FoaToRva( BeginOfINT - NewBuffer );
((_PIMAGE_IMPORT_DESCRIPTOR)pTemp)->TimeDateStamp = 0x0;
((_PIMAGE_IMPORT_DESCRIPTOR)pTemp)->ForwarderChain = 0x0;
((_PIMAGE_IMPORT_DESCRIPTOR)pTemp)->Name = FoaToRva( BeginOfDLLName - NewBuffer );
((_PIMAGE_IMPORT_DESCRIPTOR)pTemp)->FirstThunk = FoaToRva( BeginOfINT - NewBuffer );
//修改目录项
OptionalHeader->DataDirectory[1].VirtualAddress = FoaToRva( (DWORD)NewBeginAddr - (DWORD)NewBuffer );
//存盘
SaveData(NewBuffer,OutPutPath,pSectionHeader[FileHeader->NumberOfSections - 1].PointerToRawData + SizeOfNewSection);
return TRUE;
}
int main(int argc, char* argv[])
{
if( ImportInject() )
{
printf("导入表注入成功!\n");
}
getchar();
return 0;
}
InjectH.h:
[C++] 纯文本查看 复制代码 #include<stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(AFX_GLOBE_H__9815AEC8_DF43_478B_8D19_5BAB2B5BD840__INCLUDED_)
#define AFX_GLOBE_H__9815AEC8_DF43_478B_8D19_5BAB2B5BD840__INCLUDED_
#define WORD unsigned short
#define DWORD unsigned int
#define BYTE unsigned char
#define STR char*
#define PWORD unsigned short*
#define PDWORD unsigned int*
#define PBYTE unsigned char*
#define IMAGE_SIZEOF_SHORT_NAME 8
#define STAUS int
#define TRUE 1
#define FALSE 0
//DOS头结构
typedef struct dos
{
WORD e_magic; //MZ标志
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
DWORD e_lfanew; //真正PE起始点
}_IMAGE_DOS_HEADER,*_PIMAGE_DOS_HEADER;
//目录项结构
typedef struct dataDirectory
{
DWORD VirtualAddress;
DWORD Size;
}_IMAGE_DATA_DIRECTORY,_PIMAGE_DATA_DIRECTORY;
//标准PE头结构
typedef struct filePE
{
WORD Machine;
WORD NumberOfSections;
DWORD TimeDataStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
}_IMAGE_FILE_HEADER,*_PIMAGE_FILE_HEADER;
//可选PE头结构
typedef struct optionalPE
{
WORD Magic;
BYTE MajrLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
_IMAGE_DATA_DIRECTORY DataDirectory[16];
}_IMAGE_OPTIONAL_HEADER,*_PIMAGE_OPTIONAL_HEADER;
//NT头结构
typedef struct nt
{
DWORD Signature;
_IMAGE_FILE_HEADER FileHeader;
_IMAGE_OPTIONAL_HEADER OptionalHeader;
}_IMAGE_NT_HEADER,*_PIMAGE_NT_HEADER;
//节表结构
typedef struct section
{
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
}_IMAGE_SECTION_HEADER, *_PIMAGE_SECTION_HEADER;
//导入表结构
typedef struct import_descriptor {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
}_IMAGE_IMPORT_DESCRIPTOR,*_PIMAGE_IMPORT_DESCRIPTOR;
//函数以名字导出结构
typedef struct import_by_name {
WORD Hint;
BYTE Name[1];
}_IMAGE_IMPORT_BY_NAME, *_PIMAGE_IMPORT_BY_NAME;
#endif // !defined(AFX_GLOBE_H__9815AEC8_DF43_478B_8D19_5BAB2B5BD840__INCLUDED_)
4.用到的DLL:
链接:https://pan.baidu.com/s/10eJc1cTJa2GPZ23aPt1-GQ
提取码:ru18
|