一、爲什麼要移動導出表
移動導出表是指將導出表以及其內部的三張子表移動到新增節,這個操作是軟件加密的第一步。因爲軟件加密會把節進行加密,而數據目錄是在節裏的,加密後操作系統不認識了,因此我們要把數據目錄裏面的東西移動到一個新的節裏。
二、怎麼移動
上圖是導出表的結構,移動導出表的步驟,我個人的做法分爲以下幾步:
- 計算新增節的大小,必須能放下導出表,3張子表,所有按名字導出的函數名;
- 新增一個節,並設置其屬性爲可讀,含已初始化數據;
- 原封不動地拷貝3張子表到新增節;
- 遍歷 AddressOfNames,計算函數名的字節數,然後依次添加到新增節,添加同時更新 AddressOfNames 表裏的RVA;
- 拷貝導出表到新增節,修改3張子表的RVA;
- 修改目錄項中新導出表的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的函數,沒有問題。