天天看点

如何在EXE上绑定一个DLL

下面代码将在EXE文件中重建一个导入表,并将指定的DLL文件作为新的引用添加到导入表中,为什么要这样做呢?呵呵,用得到这种代码的人肯定不会想拿去做什么好事吧,所以用途就不介绍了!顺便说一下,下面代码已经处理了程序的附加数据和绑定引入,不会因为EXE存在附加数据或绑定引入导致添加失败。

#define NTSIGNATURE(a) ((LPVOID)((BYTE *)a + ((PIMAGE_DOS_HEADER)a)->e_lfanew))

#define PEFHDROFFSET(a) ((LPVOID)((BYTE *)NTSIGNATURE(a) + 4))

#define OPTHDROFFSET(a) ((LPVOID)((BYTE *)NTSIGNATURE(a) + 4 + sizeof(IMAGE_FILE_HEADER)))

#pragma pack(push)   // 保存对齐状态

#pragma pack(1)

typedef struct _NEW_IMPORT

{

 char szDllName[20];  // 导入DLL的名字

 WORD Hint;    // 导入函数的Hint

 char szFuncName[20]; // 导入函数的名字

 IMAGE_THUNK_DATA data[2];

} NEW_IMPORT, *PNEW_IMPORT;

#pragma pack(pop)

//---------------------------------------------------------------------------

// 返回RVA所指向的节

//---------------------------------------------------------------------------

PIMAGE_SECTION_HEADER GetSectionByRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader)

{

 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);

 DWORD i;

 for (i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)

 {

  if ((rva >= section[i].VirtualAddress) && (rva < (section[i].VirtualAddress + section[i].Misc.VirtualSize)))

   return &section[i];

 }

 return NULL;

}

//---------------------------------------------------------------------------

// 返回文件偏移所指向的节

//---------------------------------------------------------------------------

PIMAGE_SECTION_HEADER GetSectionByFileOffset(DWORD dwFileOffset, PIMAGE_NT_HEADERS pNTHeader)

{

 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);

 DWORD i;

 for (i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)

 {

  if ((dwFileOffset >= section[i].PointerToRawData) && (dwFileOffset < (section[i].PointerToRawData + section[i].SizeOfRawData)))

   return &section[i];

 }

 return NULL;

}

//---------------------------------------------------------------------------

// 将RVA转换为文件偏移

//---------------------------------------------------------------------------

DWORD RVAToFileOffset(DWORD rva, PIMAGE_NT_HEADERS pNTHeader)

{

 PIMAGE_SECTION_HEADER section = GetSectionByRVA(rva, pNTHeader);

 return section->PointerToRawData + (rva - section->VirtualAddress);

}

//---------------------------------------------------------------------------

// 将文件偏移转换为RVA

//---------------------------------------------------------------------------

DWORD FileOffsetToRVA(DWORD dwFileOffset, PIMAGE_NT_HEADERS pNTHeader)

{

 PIMAGE_SECTION_HEADER section = GetSectionByFileOffset(dwFileOffset, pNTHeader);

 return section->VirtualAddress + (dwFileOffset - section->PointerToRawData);

}

int CMiniShellDlg::AddDllImport(LPVOID lpFile, ULONG nFileSize, LPCSTR lpDllName, LPCSTR lpFuncName)

{

 PIMAGE_SECTION_HEADER pLastSection, pNewSection;

 DWORD AddBase, nImportCount;

 PIMAGE_DOS_HEADER pdh;

 PIMAGE_NT_HEADERS pnh;

 PIMAGE_FILE_HEADER pfh;

 PIMAGE_OPTIONAL_HEADER poh;

 PIMAGE_IMPORT_DESCRIPTOR pOldImport, pNewImport;

 NEW_IMPORT Add_Data = {0};

 // 判断是不是有效的PE文件

 if (*(PWORD)lpFile != IMAGE_DOS_SIGNATURE || *(PDWORD)NTSIGNATURE(lpFile) != IMAGE_NT_SIGNATURE)

 {

  MessageBox(_T("错误,不是PE文件/r/n"), _T("错误"), MB_ICONERROR);

  return 0;

 }

 pdh = (PIMAGE_DOS_HEADER)lpFile;

 pnh = (PIMAGE_NT_HEADERS)NTSIGNATURE(lpFile);

 pfh = (PIMAGE_FILE_HEADER)PEFHDROFFSET(lpFile);

 poh = (PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET(lpFile);

 if (!(poh->Magic & 0x100))

 {

  MessageBox(_T("错误:无效 Win32 程序/r/n"), _T("错误"), MB_ICONERROR);

  return 0;

 }

 pLastSection = IMAGE_FIRST_SECTION(pnh) + pfh->NumberOfSections - 1;

 // 一般PE中的节都采用512字节或4K字节的对齐方式

 if (poh->FileAlignment != 0x200 && poh->FileAlignment != 0x1000)

 {

  MessageBox(_T("错误:无法处理该文件/r/n"), _T("错误"), MB_ICONERROR);

  return 0;

 }

 PBYTE pOverlayData = (PBYTE)lpFile + pLastSection->PointerToRawData + pLastSection->SizeOfRawData;

 DWORD nOverlayData = nFileSize - (pLastSection->PointerToRawData + pLastSection->SizeOfRawData);

 if (pLastSection->PointerToRawData + pLastSection->SizeOfRawData < nFileSize)

 {

  MessageBox(_T("文件具有附加数据,生成的文件可能无法运行/r/n"), _T("警告"), MB_ICONWARNING);

 }

 if ((DWORD)((PBYTE)(pLastSection + 2) - (PBYTE)lpFile) > poh->SizeOfHeaders)

 {

  MessageBox(_T("错误:空间不足,无法添加节/r/n"), _T("错误"), MB_ICONERROR);

  return 0;

 }

 pNewImport = (PIMAGE_IMPORT_DESCRIPTOR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMAGE_IMPORT_DESCRIPTOR) * 30);

 pOldImport = (PIMAGE_IMPORT_DESCRIPTOR)((PBYTE)lpFile + RVAToFileOffset(poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, pnh));

 for (nImportCount = 0;; nImportCount++)

 {

  if (pOldImport[nImportCount].OriginalFirstThunk == 0 &&

   pOldImport[nImportCount].TimeDateStamp == 0 &&

   pOldImport[nImportCount].ForwarderChain == 0 &&

   pOldImport[nImportCount].Name == 0 &&

   pOldImport[nImportCount].FirstThunk == 0)

   break;

  pNewImport[nImportCount] = pOldImport[nImportCount];  // 拷贝原导入描述

 }

 AddBase = (DWORD)(pLastSection->VirtualAddress + pLastSection->Misc.VirtualSize + (poh->SectionAlignment - 1)) & ~(poh->SectionAlignment - 1);

 strcpy(Add_Data.szDllName, lpDllName);

 Add_Data.Hint = 0;

 strcpy(Add_Data.szFuncName, lpFuncName);

 Add_Data.data[0].u1.Function = 0;

 Add_Data.data[0].u1.Ordinal = 0;

 Add_Data.data[0].u1.ForwarderString = (PBYTE)(AddBase + FIELD_OFFSET(NEW_IMPORT, Hint));

 // 添加一个导入描述

 pNewImport[nImportCount].FirstThunk = AddBase + FIELD_OFFSET(NEW_IMPORT, data);

 pNewImport[nImportCount].Name = AddBase + FIELD_OFFSET(NEW_IMPORT, szDllName);

 pNewImport[nImportCount].ForwarderChain = -1;

 pNewImport[nImportCount].OriginalFirstThunk = AddBase + FIELD_OFFSET(NEW_IMPORT, data);

 pNewImport[nImportCount].TimeDateStamp = -1;

 nImportCount += 2;

 // 添加一个新节

 pfh->NumberOfSections++;

 pNewSection = pLastSection + 1;

 strcpy((char *)pNewSection->Name, ".rdata");

 pNewSection->Characteristics = 0x40000040;

 pNewSection->PointerToLinenumbers = 0;

 pNewSection->NumberOfLinenumbers = 0;

 pNewSection->PointerToRelocations = 0;

 pNewSection->NumberOfRelocations = 0;

 pNewSection->VirtualAddress = AddBase;

 pNewSection->PointerToRawData = (pLastSection->PointerToRawData + pLastSection->SizeOfRawData + (poh->FileAlignment - 1)) & ~(poh->FileAlignment - 1);

 pNewSection->SizeOfRawData = sizeof(Add_Data) + nImportCount * sizeof(IMAGE_IMPORT_DESCRIPTOR);

 pNewSection->Misc.VirtualSize = pNewSection->SizeOfRawData;

 poh->SizeOfImage = pNewSection->VirtualAddress + pNewSection->Misc.VirtualSize;

 // 修改导入描述表位置

 poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = AddBase + sizeof(NEW_IMPORT);

 poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (nImportCount - 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);

 PBYTE pNewOverlayData = (PBYTE)lpFile + pNewSection->PointerToRawData + pNewSection->SizeOfRawData;

 // 将文件中的附加数据往后移动,腾出空间来添加代码

 memmove(pNewOverlayData, pOverlayData, nOverlayData);

 // 在新节中写入导入描述

 memcpy((PBYTE)lpFile + RVAToFileOffset(AddBase, pnh), &Add_Data, sizeof(Add_Data));

 memcpy((PBYTE)lpFile + RVAToFileOffset(AddBase, pnh) + sizeof(Add_Data), pNewImport, nImportCount * sizeof(IMAGE_IMPORT_DESCRIPTOR));

 if (poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress != 0)

 {

  // IAT所在节改成可写

  GetSectionByRVA(poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress, pnh)->Characteristics |= 0x80000000;

 }

 // 删除Windows程序的导入绑定

 poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress = 0;

 poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size = 0;

 poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;

 poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;

 poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0;

 poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0;

 return pNewSection->PointerToRawData + pNewSection->SizeOfRawData + nOverlayData;

}

继续阅读