发表于 2020-10-10 18:35:04
|
查看: 5971
|
回复: 0
记录菜鸡学习PE结构的最后一个基础关,包括实现步骤,实现思路,排查问题的过程,若有不对之处,欢迎各位大佬指正
注入分类 注册表注入导入表注入特洛伊注入远程线程注入无dll注入Apc注入Windows挂钩注入DLL输入法注入 目前只懂得以下两种注入的原理
(1)导入表注入 :当程序被加载时,系统会根据程序导入表信息来加载需要用到的dll,导入表注入的原理就是修改程序的导入表,将自己的dll添加到程序的导入表中,这样程序运行时可以将自己的DLL加载到程序的进程空间.
(2)特洛伊注入 :也可以称之为dll劫持。导入表某些dll被其他的dll(与被替换的dll调用函数数量相同)替换,程序运行时可以加载替换的dll,进而可以通过替换后的dll进行一些危险操作.
导入表注入实现 结构图:
PE基础之导入表注入
步骤指导 :
1.将要注入dll的程序写入到内存中,并新增一个节
2.拷贝原来的导入表到新节中
3.在新节拷贝的导入表后新增一个导入表_IMAGE_IMPORT_DESCRIPTOR
4.增加8字节的INT表和8字节的IAT表
5.存储要注入的dll的名称
6.增加一个_IMAGE_IMPORT_BY_NAME结构,并将函数名称存进结构体第一个变量后的内存中
7.将_IMAGE_IMPORT_BY_NAME结构的地址的RVA赋值给INT表和IAT表第一项
8.将dll名称所在位置的首地址的RVA赋值给新增导入表的Name
9.修改IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size
10.存盘
排雷提示 :
1.根据IMAGE_DATA_DIRECTORY结构的size去拷贝导入表,将会多拷贝20字节的0,这也是导入表结束的标志,新增导入表的时候应该将此处的0进行覆盖,因为20字节的0,否则系统不会加载新增的导入表代表导入表结束了!!!鄙人当时完成这个代码的书写后很兴奋,但发现新增的dll始终无法加载,卡了很久,仔细调试了很久,对比原来的导入表所在内存区域和新节所在内存区域,才发现这里的问题,说明我对导入表理解的不够透彻,不能很好的排查问题
2.INT表和IAT表的8字节是因为这两张表至少包含一项内容,才会被系统加载,剩余的4字节为0标志着表的结束
3.结构体存储地址的变量存储都是RVA,使用时需要进行转换,有时是FOA转RVA,有时是RVA转FOA
4.新增节的属性(Characteristics)需要改为0xc0000040,否则会报0xc0000005错误
将要注入导入表的程序与用来注入dll放到同一级目录
相关函数 :
[pre]#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#define EVERYSECTIONTABLELENGTH 0x28
#define DLLNAME "InjectDll.dll"
#define DLLNAMELENGTH 0xE
#define FUNCTIONNAME "ExportFunction"
#define FUNCTIONNAMELENGTH 0xF
#define SECTIONNAME ".import"
#define INPUTFILENAME "D:/inject/ipmsg.exe"
#define INPUTMODE "rb"
#define OUTPUTFILENAME "D:/inject/test.exe"
#define OUTPUTMODE "wb"
/*************************************************
函数功能:读取一个文件,将文件内容写入到内存中;
函数参数:无
函数返回值:pFileBuffer(LPVOID)
**************************************************/
LPVOID FileBuffer() {
FILE* pFile = NULL; //文件指针
LPVOID pFileBuffer = NULL; //存放数据内存的首地址
DWORD filesize = 0;//记录文件大小
size_t result = 0;//记录写入的返回结果
//打开一个文件
pFile = fopen(INPUTFILENAME, INPUTMODE);
if (pFile == NULL) {
printf("打开文件失败!\n");
return NULL;
}
//统计文件的大小
fseek(pFile, 0, SEEK_END);
filesize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
//申请一块内存
pFileBuffer = malloc(filesize);
if (pFileBuffer == NULL) {
printf("申请内存失败!\n");
fclose(pFile);
return NULL;
}
//将文件数据写入内存
result = fread(pFileBuffer, 1, filesize, pFile);
if (result != filesize) {
printf("文件写入内存失败!\n");
fclose(pFile);
free(pFileBuffer);
return NULL;
}
fclose(pFile);
return pFileBuffer;
}
/*************************************************
函数功能:返回对齐后的大小;
函数参数:virtualsize(原有长度),alignment(对齐方式)
函数返回值:ret(int型)
**************************************************/
DWORD AlignSize(DWORD virtualsize, DWORD alignment) {
DWORD ret = alignment;
if (virtualsize <= alignment) {
return ret;
}
else {
ret = (virtualsize / alignment) * alignment + alignment;
return ret;
}
}
/*************************************************
函数功能:计算一个filebuffer的大小;
函数参数:pFileBuffer(LPVOID)
函数返回值:filesize(int)
**************************************************/
DWORD CountFileBufferSize(LPVOID pFileBuffer) {
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
DWORD filesize = 0;//记录文件大小
if (pFileBuffer == NULL) {
printf("文件写入内存失败!\n");
return NULL;
}
//判读是否具有MZ标志
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) {
printf("不具有MZ标志!\n");
free(pFileBuffer);
return 0;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
//判断是否具有PE标志
if (*(PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE) {
printf("不具有PE标志\n");
free(pFileBuffer);
return 0;
}
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//最后一个节头地址 + 最后一节大小
filesize = (DWORD)((pSectionHeader + pPEHeader->NumberOfSections - 1)->PointerToRawData + (pSectionHeader + pPEHeader->NumberOfSections - 1)->SizeOfRawData);
return filesize;
}
/*************************************************
函数功能:计算一个文件大小;
函数参数:filename(文件的绝对路径,LPSTR),mode(读取文件的形式,LPSTR)
函数返回值:filesize(int)
**************************************************/
DWORD CountFileSize(LPSTR filename, LPSTR mode) {
FILE* pFile = NULL;
LPSTR pFileBuffer = NULL;
DWORD filesize = 0;
//打开文件
pFile = fopen(filename, mode);
if (pFile == NULL) {
printf("打开文件失败!\n");
return NULL;
}
//统计文件大小
fseek(pFile, 0, SEEK_END);
filesize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
fclose(pFile);
return filesize;
}
/*************************************************
函数功能:PE文件尾部新增一个节;
函数参数:pFileBuffer(LPVOID),sectionLength(DWORD)
函数返回值:pNewFileBuffer(LPVOID)
**************************************************/
LPVOID AddLastSection(LPVOID pFileBuffer, DWORD sectionLength) {
LPVOID pNewFileBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
if (pFileBuffer == NULL) {
printf("文件写入内存失败!\n");
return NULL;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 0x4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 0x14);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//判断节表空间是否足够,节表后需要预留一个节表空间的位置
if (pSectionHeader->PointerToRawData - (DWORD)(pSectionHeader + pPEHeader->NumberOfSections) < 0x50) {
printf("节表空间不够!\n");
free(pFileBuffer);
return NULL;
}
//开辟新的内存
DWORD filesize = CountFileBufferSize(pFileBuffer);
pNewFileBuffer = malloc(filesize + sectionLength);
if (pNewFileBuffer == NULL) {
printf("申请内存失败!\n");
free(pFileBuffer);
return NULL;
}
//新内存初始化为0
memset(pNewFileBuffer, 0, filesize + sectionLength);
//拷贝原来的文件内容到新内存并释放旧文件内存
memcpy(pNewFileBuffer, pFileBuffer, filesize);
free(pFileBuffer);
//结构体指针再次初始化
pDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pNewFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//修改sizeofimage
pOptionHeader->SizeOfImage += sectionLength;
//copy第一个节表内容
memcpy(pSectionHeader + pPEHeader->NumberOfSections, pSectionHeader, EVERYSECTIONTABLELENGTH);
//修改新增的节表的项目
PIMAGE_SECTION_HEADER changeSection1 = pSectionHeader + pPEHeader->NumberOfSections;//新增节表首地址
PIMAGE_SECTION_HEADER changeSection2 = pSectionHeader + pPEHeader->NumberOfSections - 1;//新增节表的前一个节表首地址
changeSection1->Misc.VirtualSize = sectionLength;//修改内存中的尺寸
changeSection1->SizeOfRawData = sectionLength;//修改文件中的尺寸
changeSection1->Characteristics = 0xc0000040;
memcpy(changeSection1, SECTIONNAME, 0x8);//修改名字
changeSection1->PointerToRawData = changeSection2->PointerToRawData + changeSection2->SizeOfRawData;
if (changeSection2->SizeOfRawData > changeSection2->Misc.VirtualSize){
changeSection1->VirtualAddress = changeSection2->VirtualAddress + changeSection2->SizeOfRawData;
}
else{
changeSection1->VirtualAddress = changeSection2->VirtualAddress + changeSection2->Misc.VirtualSize;
}
//修改NumberOfSections
pPEHeader->NumberOfSections += 1;
return pNewFileBuffer;
}
/*************************************************
函数功能:将RVA的值转换成FOA;
函数参数:pFileBuffer(LPVOID),virtualAddress(LPSTR)
函数返回值:fileAddress(LPVOID)
**************************************************/
LPVOID RvaToFoa(LPVOID pFileBuffer, LPSTR virtualAddress) {
LPSTR sectionAddress = NULL;//记录距离节头的距离
LPSTR fileAddress = NULL;//记录文件中的偏移
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
if (pFileBuffer == NULL) {
printf("文件写入内存失败!\n");
return NULL;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
if ((DWORD)virtualAddress <= pOptionHeader->SizeOfHeaders){
return virtualAddress;
}
for (DWORD i = 1; i <= pPEHeader->NumberOfSections; i++) {
if ((DWORD)virtualAddress < pSectionHeader->VirtualAddress) {
pSectionHeader--;
break;
}
else if (i == pPEHeader->NumberOfSections){
break;
}
else{
pSectionHeader++;
}
}
//距离该节头的距离
sectionAddress = virtualAddress - pSectionHeader->VirtualAddress;
fileAddress = pSectionHeader->PointerToRawData + sectionAddress;
return (LPVOID)fileAddress;
}
/*************************************************
函数功能:将FOA的值转换成RVA;
函数参数:pFileBuffer(LPVOID),virtualAddress(LPSTR)
函数返回值:fileAddress(LPVOID)
**************************************************/
LPVOID FoaToRva(LPVOID pFileBuffer, LPSTR fileaddress) {
LPSTR sectionAddress = NULL;//记录距离节头的距离
LPSTR virtualaddress = NULL;//记录内存中的偏移
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
if (pFileBuffer == NULL) {
printf("文件写入内存失败!\n");
return NULL;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
if ((DWORD)fileaddress <= pOptionHeader->SizeOfHeaders){
return fileaddress;
}
for (DWORD i = 1; i <= pPEHeader->NumberOfSections; i++) {
if ((DWORD)fileaddress < pSectionHeader->PointerToRawData) {
pSectionHeader--;
break;
}
else if (i == pPEHeader->NumberOfSections){
break;
}
else{
pSectionHeader++;
}
}
//距离该节头的距离
sectionAddress = fileaddress - pSectionHeader->PointerToRawData;
virtualaddress = pSectionHeader->VirtualAddress + sectionAddress;
return (LPVOID)virtualaddress;
}
/*************************************************
函数功能:导入表注入;
函数参数:pFileBuffer(LPVOID)
函数返回值:无
**************************************************/
void MoveImportTable(LPVOID pFileBuffer){
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;//定位表目录
PIMAGE_IMPORT_DESCRIPTOR importTableAddress = NULL;//定位导入表的真正位置
LPVOID returnAddress = NULL;//记录RVAtoFOA的返回值
if (pFileBuffer == NULL) {
printf("文件写入内存失败!\n");
return;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 0x4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 0x14);
//定位导入表目录位置(第二个表)
pDataDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;
pDataDirectory += 0x1;
//计算新增节的大小 = 原导入表大小 + 新增导入表大小 + INT + IAT + dllname + _IMAGE_IMPORT_BY_NAME大小
DWORD sectionLength = pDataDirectory->Size + 0x28 + + 0x10 + DLLNAMELENGTH + FUNCTIONNAMELENGTH + 0x2;
sectionLength = AlignSize(sectionLength, pOptionHeader->FileAlignment);
//新增一个节
LPVOID pNewFileBuffer = AddLastSection(pFileBuffer, sectionLength);
if (pNewFileBuffer == NULL){
printf("新增节失败!\n");
return;
}
pDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pNewFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 0x4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 0x14);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//定位导入表目录位置(第二个表)
pDataDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;
pDataDirectory += 0x1;
//定位到新节的位置和导入表的位置
PDWORD pNewSection = (PDWORD)((pSectionHeader + pPEHeader->NumberOfSections - 1)->PointerToRawData + (DWORD)pNewFileBuffer);
returnAddress = RvaToFoa(pNewFileBuffer, (LPSTR)pDataDirectory->VirtualAddress);
importTableAddress = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)returnAddress + (DWORD)pNewFileBuffer);
//复制原导入表
memcpy(pNewSection, importTableAddress, pDataDirectory->Size);
//原导入表后新增一个导入表
importTableAddress = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pNewSection + pDataDirectory->Size - 0x14);
//增加8字节INT表
PIMAGE_THUNK_DATA32 pIntTable = (PIMAGE_THUNK_DATA32)((DWORD)importTableAddress + 0x28);//保留20字节的0
PIMAGE_THUNK_DATA32 repairIntTable = pIntTable;
pIntTable++;
pIntTable->u1.Ordinal = 0x0;
pIntTable++;
//增加8字节IAT表
PIMAGE_THUNK_DATA32 pIatTable = (PIMAGE_THUNK_DATA32)(pIntTable);
PIMAGE_THUNK_DATA32 repairIatTable = pIatTable;
pIatTable++;
pIatTable->u1.Ordinal = 0x0;
pIatTable++;
//分配空间存储DLL名称字符串
PDWORD dllNameAddress = (PDWORD)pIatTable;
memcpy(dllNameAddress, DLLNAME, DLLNAMELENGTH);
//增加IMAGE_IMPORT_BY_NAME 结构
PIMAGE_IMPORT_BY_NAME functionNameAddress = (PIMAGE_IMPORT_BY_NAME)((DWORD)dllNameAddress + DLLNAMELENGTH);
PDWORD pFunctionName = (PDWORD)((DWORD)functionNameAddress + 0x2);
memcpy(pFunctionName, FUNCTIONNAME, FUNCTIONNAMELENGTH);
//将IMAGE_IMPORT_BY_NAME结构的RVA赋值给INT和IAT表中的第一项
repairIntTable->u1.AddressOfData = (DWORD)FoaToRva(pNewFileBuffer, (LPSTR)((DWORD)functionNameAddress - (DWORD)pNewFileBuffer));
repairIatTable->u1.AddressOfData = repairIntTable->u1.Ordinal;
//修正导入表Name、OriginalFirstThunk、FirstThunk
importTableAddress->Name = (DWORD)FoaToRva(pNewFileBuffer, (LPSTR)((DWORD)dllNameAddress - (DWORD)pNewFileBuffer));
importTableAddress->OriginalFirstThunk = (DWORD)FoaToRva(pNewFileBuffer, (LPSTR)((DWORD)repairIntTable - (DWORD)pNewFileBuffer));
importTableAddress->FirstThunk = (DWORD)FoaToRva(pNewFileBuffer, (LPSTR)((DWORD)repairIatTable - (DWORD)pNewFileBuffer));
//修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size
pDataDirectory->VirtualAddress = (DWORD)FoaToRva(pNewFileBuffer, (LPSTR)((DWORD)pNewSection - (DWORD)pNewFileBuffer));
pDataDirectory->Size += 0x14;
//存盘
FILE* pFile = NULL;
DWORD newfilesize = 0;
size_t newresult = 0;
pFile = fopen(OUTPUTFILENAME, OUTPUTMODE);
if (pFile == NULL) {
printf("打开文件失败!\n");
free(pNewFileBuffer);
return;
}
newfilesize = CountFileSize(INPUTFILENAME, INPUTMODE) + sectionLength;
newresult = fwrite(pNewFileBuffer, 1, newfilesize, pFile);
if (newresult != newfilesize) {
printf("写入文件失败!\n");
fclose(pFile);
free(pNewFileBuffer);
return;
}
printf("存盘成功!\n");
fclose(pFile);
free(pNewFileBuffer);
}[/pre]
主函数 :
[pre]int main(){
LPVOID pFileBuffer = FileBuffer();
MoveImportTable(pFileBuffer);
return 0;
}[/pre]
结果展示 注入程序 :
PE基础之导入表注入
启动程序 :
PE基础之导入表注入
关闭程序 :
PE基础之导入表注入
温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【
投诉建议 】板块发帖举报。