pE file operation -- move export table

1, What is an export table?
First review the structure of the export table: the relatively complex three sub tables are AddressOfFunctions, AddressOfNames and AddressOfNameOrdinals; The address table stores the exported function address, the name table stores the RVA of the function name of the function exported by name, and the sequence number table stores the sequence number of the function exported by sequence number (sequence number + Base is the real sequence number value)

2, Why move tables?

  • These tables are generated by the compiler and store very important information;
  • When the program starts, the system will initialize according to these tables, such as storing the function address in the DLL used in the IAT table;
  • In order to protect the program, the binary code of exe can be encrypted, but the information of various tables is mixed with the code and data of customer bytes. If encryption is carried out uniformly, the system will have problems during initialization;
  • The most important thing is: learning to move various tables is the basis for program encryption / cracking;

3, To move an exported table

  • 1) Add a new section in the DLL and return the added FOA; (the default size of the newly added section here is 0x1000. In fact, the size of the newly added section should be determined according to the size of the exported table.)
  • 2) Copy address table AddressOfFunctions to the location of FOA, length: 4*NumberOfFunctions (4 bytes for each table item)
  • 3) Copy the sequence number table AddressOfNameOrdinals, and then seamlessly copy the above position. The length is NumberOfNames*2 (4 bytes for each table item)
  • 4) Copy the name table AddressOfNames, and connect seamlessly with the above position. The length is NumberOfNames*4 (4 bytes for each table item)
  • 5) Copy all function name strings. The name table copied in step 4 only copies the RVA of the function name, not the function name. Because the length of the function name is uncertain, AddressOfNames is directly repaired during copying
  • 6) Copy other keywords of the exported table;
  • 7) Repair image_ EXPORT_ In the directory structure
    AddressOfFunctions;
    AddressOfNameOrdinals;
    AddressOfNames;
    1. Fix the VirtualAddress value in the data directory entry and point to the new IMAGE_EXPORT_DIRECTORY
#define FilePathOut_NewSec  "C://Users/Ron/Desktop/exePath/ConfSdk_NewSec.dll"
//Move export table
/***************************************************************************************************************************
//1)Add a new section in the DLL and return the added FOA; (the default size of the newly added section here is 0x1000. In fact, the size of the newly added section should be determined according to the size of the exported table.)
//2)Copy address table AddressOfFunctions to the location of FOA, length: 4*NumberOfFunctions (4 bytes for each table item)
	At the same time, repair the address table RVA in the export table
//3)Copy the sequence number table AddressOfNameOrdinals, and then seamlessly copy the above position. The length is NumberOfNames*2 (2 bytes for each table item)
	At the same time, repair the serial number table RVA in the export table
//4)Copy the name table AddressOfNames, and connect seamlessly with the above position. The length is NumberOfNames*4 (4 bytes for each table item)
	At the same time, fix the function name RVA in the exported table
//5)Copy all function name strings. The name table copied in step 4 only copies the RVA of the function name, not the function name. Because the length of the function name is uncertain, AddressOfNames is directly repaired during copying
//6)Copy all keywords of the exported table;
//7) Fix the VirtualAddress value in the data directory entry and point to the new IMAGE_EXPORT_DIRECTORY
******************************************************************************************************************************/
PVOID MoveImageExportDirectory(PVOID* ppFileBuffer,DWORD sizeOfFileBuffer)
{
	//Add a new section and return the FOA of the new section
	DWORD NewSectionFOA = AddNewSection(*ppFileBuffer, sizeOfFileBuffer, 0x500);
	
	//After adding a section, define the header pointer
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)(*ppFileBuffer);
	PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)(*ppFileBuffer) + pDos->e_lfanew);
	PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNT + 4);
	PIMAGE_OPTIONAL_HEADER pOptional = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
	PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptional + pFileHeader->SizeOfOptionalHeader);
	PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)(*ppFileBuffer) + RvaToFoa((*ppFileBuffer), pNT->OptionalHeader.DataDirectory[0].VirtualAddress));//pNT->OptionalHeader.DataDirectory
	//Pointer to address table, name table and sequence number table
	DWORD* pAddressOfFunctions = (DWORD*)((DWORD)(*ppFileBuffer) + RvaToFoa((*ppFileBuffer), pExportTable->AddressOfFunctions));
	//DWORD* pAddressOfNames = (DWORD*)((DWORD)(*ppFileBuffer) + RvaToFoa((*ppFileBuffer), pExportTable->AddressOfNames));
	WORD* pAddressOfNameOrdinals = (WORD*)((DWORD)(*ppFileBuffer) + RvaToFoa((*ppFileBuffer), pExportTable->AddressOfNameOrdinals));

	int P = 0;//The offset relative to the new section, that is, the offset relative to pnewsectionheader - > pointtorawdata, will always change with the data filling, and the initial value must be 0

	PIMAGE_SECTION_HEADER pNewSectionHeader = pSectionHeader + pFileHeader->NumberOfSections - 1;
	PDWORD pNewSection = (PDWORD)((DWORD)(*ppFileBuffer) + pNewSectionHeader->PointerToRawData);

	
	cout << "/******************************Move function address table********************************/" << endl;
	//Copy AddressOfFunctions to the new section. The size of the entry in the address table is 4 bytes
		memcpy((PBYTE)pNewSection,pAddressOfFunctions,pExportTable->NumberOfFunctions * 4);
	//Fix the value of AddressOfFunctions
	pExportTable->AddressOfFunctions = pNewSectionHeader->VirtualAddress + P;
	P += pExportTable->NumberOfFunctions * 4; //P records the distance from the beginning of the new section after moving the address table
	//Output after moving the function address table to check whether it is consistent with that before moving
	//New function address table test
	DWORD AddressOfFunctionsFOA = RvaToFoa(*ppFileBuffer, pExportTable->AddressOfFunctions);
	DWORD AddressOfFunctions = 0;
	for (int i = 0; i < pExportTable->NumberOfFunctions; i++)
	{	//Because the Address table element is 4 bytes, the absolute Address plus i*4 directly takes the ith element
		AddressOfFunctions = *(PDWORD)((DWORD)*ppFileBuffer + AddressOfFunctionsFOA + i * 4);
		printf("subscript:%d,Function address:%x\n", i, AddressOfFunctions);
	}

	cout << "/***********************************Move function sequence number table****************************/" << endl;
	//Copy AddressOfNameOrdinals
	memcpy((PBYTE)pNewSection + P, pAddressOfNameOrdinals, pExportTable->NumberOfNames *2);
	//Modify the address value of the serial number table in the export table, which is also section header + offset
	pExportTable->AddressOfNameOrdinals = pNewSectionHeader->VirtualAddress + P;
	WORD* pNewOrdinalTable = (WORD*)((PBYTE)pNewSection + P);
	//After all operations are completed, the offset can be added forward
	P += pExportTable->NumberOfNames * 2;
	//New serial number table test
	for (int i = 0; i < pExportTable->NumberOfNames; i++)
	{
		printf("subscript:%d,Serial number:%d\n", i, *pNewOrdinalTable++);
	}

	cout << "/********************************Move function name table****************************/" << endl;	
	//Record the address of the item in the function name table in the FOA, that is, now the address of the table can be found according to this address, ∵ after that, P will change, ∵ you need to record it first
	DWORD* pAddressOfNames = (DWORD*)((DWORD)(*ppFileBuffer) + RvaToFoa((*ppFileBuffer), pExportTable->AddressOfNames));
	//First copy the function name RVA of the name table to the new section
	memcpy((PBYTE)pNewSection + P, pAddressOfNames, pExportTable->NumberOfNames * 4);
	//Modify AddressOfNames in the exported table
	pExportTable->AddressOfNames = pNewSectionHeader->VirtualAddress + P;
	P += pExportTable->NumberOfNames * 4;
	//Find the function name according to each RVA in the name table, calculate the FOA corresponding to each RVA, copy the function name to the new section one by one, and correct the RVA in the name table at the same time
	DWORD namesFOA = 0;
	//DWORD namesNewRVA = 0;
	char* names = NULL;
	for (int i = 0; i < pExportTable->NumberOfNames; i++)
	{
		namesFOA = RvaToFoa(*ppFileBuffer, pAddressOfNames[i]);
		names = (char*)((DWORD)*ppFileBuffer + namesFOA);
		printf("subscript:%d,Function name:%s,String length%d\n", i, names, strlen(names));
		memcpy((PBYTE)pNewSection + P, names, strlen(names));
		pAddressOfNames[i] = FoaToRva(*ppFileBuffer, (PBYTE)pNewSection + P - *ppFileBuffer);
		P += strlen(names)+1;
	}
	PVOID pNewFileBuffer = *ppFileBuffer;
	//To check whether the memory data written to the file exceeds this memory page, if more than 4kb of data is written to the file through fwrite, no error will be reported, but the generated file size is 0kb
	//writeMemoryToFile(FilePathOut_NewSec, pNewFileBuffer, sizeOfFileBuffer+0x500);
	cout << "/***********************************Move export table****************************/" << endl;
	memcpy((PBYTE)pNewSection + P, pExportTable, sizeof(IMAGE_EXPORT_DIRECTORY));

	//Modify the export table RVA in the data catalog item
	pOptional->DataDirectory[0].VirtualAddress = FoaToRva(*ppFileBuffer, (PBYTE)pNewSection - (PBYTE)*ppFileBuffer);
	
	writeMemoryToFile(FilePathOut_NewSec, pNewFileBuffer, sizeOfFileBuffer + 0x500);
	return NULL;
}

Before moving:

After moving:

Keywords: C++

Added by pbalsamo on Mon, 03 Jan 2022 12:19:01 +0200