移動導出表到新增節

一、爲什麼要移動導出表

移動導出表是指將導出表以及其內部的三張子表移動到新增節,這個操作是軟件加密的第一步。因爲軟件加密會把節進行加密,而數據目錄是在節裏的,加密後操作系統不認識了,因此我們要把數據目錄裏面的東西移動到一個新的節裏。

二、怎麼移動

在這裏插入圖片描述

上圖是導出表的結構,移動導出表的步驟,我個人的做法分爲以下幾步:

  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 識別正確
在這裏插入圖片描述

在這裏插入圖片描述
調用新DLL的函數,沒有問題。
在這裏插入圖片描述

五、完整代碼

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章