移动导出表到新增节

一、为什么要移动导出表

移动导出表是指将导出表以及其内部的三张子表移动到新增节,这个操作是软件加密的第一步。因为软件加密会把节进行加密,而数据目录是在节里的,加密后操作系统不认识了,因此我们要把数据目录里面的东西移动到一个新的节里。

二、怎么移动

移动导出表到新增节_第1张图片

上图是导出表的结构,移动导出表的步骤,我个人的做法分为以下几步:

  1. 计算新增节的大小,必须能放下导出表,3张子表,所有按名字导出的函数名;
  2. 新增一个节,并设置其属性为可读,含已初始化数据;
  3. 原封不动地拷贝3张子表到新增节;
  4. 遍历 AddressOfNames,计算函数名的字节数,然后依次添加到新增节,添加同时更新 AddressOfNames 表里的RVA;
  5. 拷贝导出表到新增节,修改3张子表的RVA;
  6. 修改目录项中新导出表的RVA。

三、代码实现

// 移动导出表到新增节
DWORD MoveExportTableToNewSection(LPVOID pFileBuffer, LPVOID *pNewFileBuffer, DWORD dwFileBufferSize)
{
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionHeader = \
		(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

	PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + \
		RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));
	PDWORD pAddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportTable->AddressOfFunctions));
	PWORD pAddressOfNameOrdinals = (PWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportTable->AddressOfNameOrdinals));
	PDWORD pAddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportTable->AddressOfNames));

	// 计算新增节的大小
	// = NumberOfFunctions * 4 + NumberOfNames * (2 + 4) + 所有函数名的字节 + sizeof(_IMAGE_EXPORT_DIRECTORY)
	// 然后文件对齐
	DWORD dwNewSectionSize = 0;
	dwNewSectionSize += pExportTable->NumberOfFunctions * 4; // AddressOfFunctions 的空间
	dwNewSectionSize += pExportTable->NumberOfNames * (2 + 4); // AddressOfNames + AddressOfNameOrdinals 的空间
	size_t i = 0;
	for (i = 0; i < pExportTable->NumberOfNames; i++)
	{		
		LPCSTR lpszFuncName = (LPCSTR)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pAddressOfNames[i]));
		dwNewSectionSize += strlen(lpszFuncName) + 1;
	}
	dwNewSectionSize += sizeof(_IMAGE_EXPORT_DIRECTORY);
	dwNewSectionSize = Align(dwNewSectionSize, pOptionHeader->FileAlignment);
	//printf("新增节的大小 = %x\n", dwNewSectionSize);

	DWORD dwNewBufferSize = AddSection(pFileBuffer, pNewFileBuffer, dwFileBufferSize, dwNewSectionSize);
	
	pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;
	pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

	// 修改新增节属性为可读、含已初始化数据
	pSectionHeader[pPEHeader->NumberOfSections - 1].Characteristics = 0x40000040;

	pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)*pNewFileBuffer + \
		RvaToFoa(*pNewFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));

	pAddressOfFunctions = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFoa(*pNewFileBuffer, pExportTable->AddressOfFunctions));
	pAddressOfNameOrdinals = (PWORD)((DWORD)*pNewFileBuffer + RvaToFoa(*pNewFileBuffer, pExportTable->AddressOfNameOrdinals));
	pAddressOfNames = (PDWORD)((DWORD)*pNewFileBuffer + RvaToFoa(*pNewFileBuffer, pExportTable->AddressOfNames));
	
	// 把3张子表拷贝到新节,更新指针
	LPVOID pInsert = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData);
	memcpy(pInsert, pAddressOfFunctions, 4 * pExportTable->NumberOfFunctions);
	pAddressOfFunctions = (PDWORD)pInsert;
	pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfFunctions);
	memcpy(pInsert, pAddressOfNameOrdinals, 2 * pExportTable->NumberOfNames);
	pAddressOfNameOrdinals = (PWORD)pInsert;
	pInsert = (LPVOID)((DWORD)pInsert + 2 * pExportTable->NumberOfNames);
	memcpy(pInsert, pAddressOfNames, 4 * pExportTable->NumberOfNames);
	pAddressOfNames = (PDWORD)pInsert;
	pInsert = (LPVOID)((DWORD)pInsert + 4 * pExportTable->NumberOfNames);
	// 拷贝函数名
	for (i = 0; i < pExportTable->NumberOfNames; i++)
	{
		LPCSTR lpszFuncName = (LPCSTR)((DWORD)*pNewFileBuffer + RvaToFoa(*pNewFileBuffer, pAddressOfNames[i]));
		memcpy(pInsert, lpszFuncName, strlen(lpszFuncName) + 1);
		// 更新函数名的RVA地址
		pAddressOfNames[i] = FoaToRva(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer);
		pInsert = (LPVOID)((DWORD)pInsert + strlen(lpszFuncName) + 1);
	}
	// 拷贝导出表
	memcpy(pInsert, pExportTable, sizeof(_IMAGE_EXPORT_DIRECTORY));
	pExportTable = (PIMAGE_EXPORT_DIRECTORY)pInsert;
	pExportTable->AddressOfFunctions = FoaToRva(*pNewFileBuffer, (DWORD)pAddressOfFunctions - (DWORD)*pNewFileBuffer);
	pExportTable->AddressOfNameOrdinals = FoaToRva(*pNewFileBuffer, (DWORD)pAddressOfNameOrdinals - (DWORD)*pNewFileBuffer);
	pExportTable->AddressOfNames = FoaToRva(*pNewFileBuffer, (DWORD)pAddressOfNames - (DWORD)*pNewFileBuffer);
	
	// 修改目录项,指向新的导出表
	pOptionHeader->DataDirectory[0].VirtualAddress = FoaToRva(*pNewFileBuffer, (DWORD)pExportTable - (DWORD)*pNewFileBuffer);
		
	return dwNewBufferSize;
}

四、运行结果

depends walker 和 LordPE 识别正确
移动导出表到新增节_第2张图片

移动导出表到新增节_第3张图片
调用新DLL的函数,没有问题。
移动导出表到新增节_第4张图片

五、完整代码

https://blog.csdn.net/Kwansy/article/details/106234264

你可能感兴趣的:(PE)