You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
goldberg_emulator/detours/uimports.cc

334 lines
12 KiB
C++

5 years ago
//////////////////////////////////////////////////////////////////////////////
//
// Add DLLs to a module import table (uimports.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Note that this file is included into creatwth.cpp one or more times
// (once for each supported module format).
//
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
// UpdateImports32 aka UpdateImports64
static BOOL UPDATE_IMPORTS_XX(HANDLE hProcess,
HMODULE hModule,
__in_ecount(nDlls) LPCSTR *plpDlls,
DWORD nDlls)
{
BOOL fSucceeded = FALSE;
DWORD cbNew = 0;
BYTE * pbNew = NULL;
DWORD i;
SIZE_T cbRead;
DWORD n;
PBYTE pbModule = (PBYTE)hModule;
IMAGE_DOS_HEADER idh;
ZeroMemory(&idh, sizeof(idh));
if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), &cbRead)
|| cbRead < sizeof(idh)) {
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
5 years ago
pbModule, pbModule + sizeof(idh), GetLastError()));
finish:
if (pbNew != NULL) {
delete[] pbNew;
pbNew = NULL;
}
return fSucceeded;
}
IMAGE_NT_HEADERS_XX inh;
ZeroMemory(&inh, sizeof(inh));
if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), &cbRead)
|| cbRead < sizeof(inh)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
5 years ago
pbModule + idh.e_lfanew,
pbModule + idh.e_lfanew + sizeof(inh),
GetLastError()));
goto finish;
}
if (inh.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC_XX) {
DETOUR_TRACE(("Wrong size image (%04x != %04x).\n",
inh.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR_MAGIC_XX));
SetLastError(ERROR_INVALID_BLOCK);
goto finish;
}
// Zero out the bound table so loader doesn't use it instead of our new table.
inh.BOUND_DIRECTORY.VirtualAddress = 0;
inh.BOUND_DIRECTORY.Size = 0;
// Find the size of the mapped file.
DWORD dwSec = idh.e_lfanew +
FIELD_OFFSET(IMAGE_NT_HEADERS_XX, OptionalHeader) +
inh.FileHeader.SizeOfOptionalHeader;
for (i = 0; i < inh.FileHeader.NumberOfSections; i++) {
IMAGE_SECTION_HEADER ish;
ZeroMemory(&ish, sizeof(ish));
if (!ReadProcessMemory(hProcess, pbModule + dwSec + sizeof(ish) * i, &ish,
sizeof(ish), &cbRead)
|| cbRead < sizeof(ish)) {
DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n",
5 years ago
pbModule + dwSec + sizeof(ish) * i,
pbModule + dwSec + sizeof(ish) * (i + 1),
GetLastError()));
goto finish;
}
DETOUR_TRACE(("ish[%lu] : va=%08lx sr=%lu\n", i, ish.VirtualAddress, ish.SizeOfRawData));
// If the linker didn't suggest an IAT in the data directories, the
// loader will look for the section of the import directory to be used
// for this instead. Since we put out new IMPORT_DIRECTORY outside any
// section boundary, the loader will not find it. So we provide one
// explicitly to avoid the search.
//
5 years ago
if (inh.IAT_DIRECTORY.VirtualAddress == 0 &&
inh.IMPORT_DIRECTORY.VirtualAddress >= ish.VirtualAddress &&
inh.IMPORT_DIRECTORY.VirtualAddress < ish.VirtualAddress + ish.SizeOfRawData) {
inh.IAT_DIRECTORY.VirtualAddress = ish.VirtualAddress;
inh.IAT_DIRECTORY.Size = ish.SizeOfRawData;
}
}
if (inh.IMPORT_DIRECTORY.VirtualAddress != 0 && inh.IMPORT_DIRECTORY.Size == 0) {
// Don't worry about changing the PE file,
// because the load information of the original PE header has been saved and will be restored.
// The change here is just for the following code to work normally
PIMAGE_IMPORT_DESCRIPTOR pImageImport = (PIMAGE_IMPORT_DESCRIPTOR)(pbModule + inh.IMPORT_DIRECTORY.VirtualAddress);
do {
IMAGE_IMPORT_DESCRIPTOR ImageImport;
if (!ReadProcessMemory(hProcess, pImageImport, &ImageImport, sizeof(ImageImport), NULL)) {
DETOUR_TRACE(("ReadProcessMemory failed: %lu\n", GetLastError()));
goto finish;
}
inh.IMPORT_DIRECTORY.Size += sizeof(IMAGE_IMPORT_DESCRIPTOR);
if (!ImageImport.Name) {
break;
}
++pImageImport;
} while (TRUE);
DWORD dwLastError = GetLastError();
OutputDebugString(TEXT("[This PE file has an import table, but the import table size is marked as 0. This is an error.")
TEXT("If it is not repaired, the launched program will not work properly, Detours has automatically repaired its import table size for you! ! !]\r\n"));
if (GetLastError() != dwLastError) {
SetLastError(dwLastError);
}
}
5 years ago
DETOUR_TRACE((" Imports: %p..%p\n",
pbModule + inh.IMPORT_DIRECTORY.VirtualAddress,
pbModule + inh.IMPORT_DIRECTORY.VirtualAddress +
5 years ago
inh.IMPORT_DIRECTORY.Size));
// Calculate new import directory size. Note that since inh is from another
// process, inh could have been corrupted. We need to protect against
// integer overflow in allocation calculations.
5 years ago
DWORD nOldDlls = inh.IMPORT_DIRECTORY.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
DWORD obRem;
if (DWordMult(sizeof(IMAGE_IMPORT_DESCRIPTOR), nDlls, &obRem) != S_OK) {
DETOUR_TRACE(("too many new DLLs.\n"));
goto finish;
}
DWORD obOld;
if (DWordAdd(obRem, sizeof(IMAGE_IMPORT_DESCRIPTOR) * nOldDlls, &obOld) != S_OK) {
DETOUR_TRACE(("DLL entries overflow.\n"));
goto finish;
}
5 years ago
DWORD obTab = PadToDwordPtr(obOld);
// Check for integer overflow.
if (obTab < obOld) {
DETOUR_TRACE(("DLL entries padding overflow.\n"));
goto finish;
}
DWORD stSize;
if (DWordMult(sizeof(DWORD_XX) * 4, nDlls, &stSize) != S_OK) {
DETOUR_TRACE(("String table overflow.\n"));
goto finish;
}
DWORD obDll;
if (DWordAdd(obTab, stSize, &obDll) != S_OK) {
DETOUR_TRACE(("Import table size overflow\n"));
goto finish;
}
5 years ago
DWORD obStr = obDll;
cbNew = obStr;
for (n = 0; n < nDlls; n++) {
if (DWordAdd(cbNew, PadToDword((DWORD)strlen(plpDlls[n]) + 1), &cbNew) != S_OK) {
DETOUR_TRACE(("Overflow adding string table entry\n"));
goto finish;
}
5 years ago
}
pbNew = new BYTE [cbNew];
if (pbNew == NULL) {
DETOUR_TRACE(("new BYTE [cbNew] failed.\n"));
goto finish;
}
ZeroMemory(pbNew, cbNew);
PBYTE pbBase = pbModule;
PBYTE pbNext = pbBase
+ inh.OptionalHeader.BaseOfCode
+ inh.OptionalHeader.SizeOfCode
+ inh.OptionalHeader.SizeOfInitializedData
+ inh.OptionalHeader.SizeOfUninitializedData;
if (pbBase < pbNext) {
pbBase = pbNext;
}
DETOUR_TRACE(("pbBase = %p\n", pbBase));
PBYTE pbNewIid = FindAndAllocateNearBase(hProcess, pbModule, pbBase, cbNew);
if (pbNewIid == NULL) {
DETOUR_TRACE(("FindAndAllocateNearBase failed.\n"));
goto finish;
}
PIMAGE_IMPORT_DESCRIPTOR piid = (PIMAGE_IMPORT_DESCRIPTOR)pbNew;
IMAGE_THUNK_DATAXX *pt = NULL;
5 years ago
DWORD obBase = (DWORD)(pbNewIid - pbModule);
DWORD dwProtect = 0;
if (inh.IMPORT_DIRECTORY.VirtualAddress != 0) {
// Read the old import directory if it exists.
DETOUR_TRACE(("IMPORT_DIRECTORY perms=%lx\n", dwProtect));
5 years ago
if (!ReadProcessMemory(hProcess,
pbModule + inh.IMPORT_DIRECTORY.VirtualAddress,
&piid[nDlls],
nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR), &cbRead)
|| cbRead < nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR)) {
DETOUR_TRACE(("ReadProcessMemory(imports) failed: %lu\n", GetLastError()));
5 years ago
goto finish;
}
}
for (n = 0; n < nDlls; n++) {
HRESULT hrRet = StringCchCopyA((char*)pbNew + obStr, cbNew - obStr, plpDlls[n]);
if (FAILED(hrRet)) {
DETOUR_TRACE(("StringCchCopyA failed: %08lx\n", hrRet));
5 years ago
goto finish;
}
// After copying the string, we patch up the size "??" bits if any.
hrRet = ReplaceOptionalSizeA((char*)pbNew + obStr,
cbNew - obStr,
DETOURS_STRINGIFY(DETOURS_BITS_XX));
if (FAILED(hrRet)) {
DETOUR_TRACE(("ReplaceOptionalSizeA failed: %08lx\n", hrRet));
5 years ago
goto finish;
}
DWORD nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * (4 * n));
5 years ago
piid[n].OriginalFirstThunk = obBase + nOffset;
// We need 2 thunks for the import table and 2 thunks for the IAT.
// One for an ordinal import and one to mark the end of the list.
pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset));
pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1;
pt[1].u1.Ordinal = 0;
nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * ((4 * n) + 2));
5 years ago
piid[n].FirstThunk = obBase + nOffset;
pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset));
pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1;
pt[1].u1.Ordinal = 0;
5 years ago
piid[n].TimeDateStamp = 0;
piid[n].ForwarderChain = 0;
piid[n].Name = obBase + obStr;
obStr += PadToDword((DWORD)strlen(plpDlls[n]) + 1);
}
_Analysis_assume_(obStr <= cbNew);
#if 0
for (i = 0; i < nDlls + nOldDlls; i++) {
DETOUR_TRACE(("%8d. Look=%08x Time=%08x Fore=%08x Name=%08x Addr=%08x\n",
i,
piid[i].OriginalFirstThunk,
piid[i].TimeDateStamp,
piid[i].ForwarderChain,
piid[i].Name,
piid[i].FirstThunk));
if (piid[i].OriginalFirstThunk == 0 && piid[i].FirstThunk == 0) {
break;
}
}
#endif
if (!WriteProcessMemory(hProcess, pbNewIid, pbNew, obStr, NULL)) {
DETOUR_TRACE(("WriteProcessMemory(iid) failed: %lu\n", GetLastError()));
5 years ago
goto finish;
}
DETOUR_TRACE(("obBaseBef = %08lx..%08lx\n",
5 years ago
inh.IMPORT_DIRECTORY.VirtualAddress,
inh.IMPORT_DIRECTORY.VirtualAddress + inh.IMPORT_DIRECTORY.Size));
DETOUR_TRACE(("obBaseAft = %08lx..%08lx\n", obBase, obBase + obStr));
5 years ago
// In this case the file didn't have an import directory in first place,
// so we couldn't fix the missing IAT above. We still need to explicitly
// provide an IAT to prevent to loader from looking for one.
//
5 years ago
if (inh.IAT_DIRECTORY.VirtualAddress == 0) {
inh.IAT_DIRECTORY.VirtualAddress = obBase;
inh.IAT_DIRECTORY.Size = cbNew;
}
inh.IMPORT_DIRECTORY.VirtualAddress = obBase;
inh.IMPORT_DIRECTORY.Size = cbNew;
/////////////////////// Update the NT header for the new import directory.
//
if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders,
PAGE_EXECUTE_READWRITE, &dwProtect)) {
DETOUR_TRACE(("VirtualProtectEx(inh) write failed: %lu\n", GetLastError()));
5 years ago
goto finish;
}
inh.OptionalHeader.CheckSum = 0;
if (!WriteProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(idh) failed: %lu\n", GetLastError()));
5 years ago
goto finish;
}
DETOUR_TRACE(("WriteProcessMemory(idh:%p..%p)\n", pbModule, pbModule + sizeof(idh)));
if (!WriteProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(inh) failed: %lu\n", GetLastError()));
5 years ago
goto finish;
}
DETOUR_TRACE(("WriteProcessMemory(inh:%p..%p)\n",
pbModule + idh.e_lfanew,
pbModule + idh.e_lfanew + sizeof(inh)));
if (!VirtualProtectEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders,
dwProtect, &dwProtect)) {
DETOUR_TRACE(("VirtualProtectEx(idh) restore failed: %lu\n", GetLastError()));
5 years ago
goto finish;
}
fSucceeded = TRUE;
goto finish;
}