aboutsummaryrefslogtreecommitdiffhomepage
path: root/ext/detours/src/uimports.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/detours/src/uimports.cpp')
-rw-r--r--ext/detours/src/uimports.cpp280
1 files changed, 280 insertions, 0 deletions
diff --git a/ext/detours/src/uimports.cpp b/ext/detours/src/uimports.cpp
new file mode 100644
index 0000000..5f454dd
--- /dev/null
+++ b/ext/detours/src/uimports.cpp
@@ -0,0 +1,280 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// 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: %d\n",
+ 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: %d\n",
+ 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: %d\n",
+ pbModule + dwSec + sizeof(ish) * i,
+ pbModule + dwSec + sizeof(ish) * (i + 1),
+ GetLastError()));
+ goto finish;
+ }
+
+ DETOUR_TRACE(("ish[%d] : va=%08x sr=%d\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.
+ //
+ 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;
+ }
+ }
+
+ DETOUR_TRACE((" Imports: %p..%p\n",
+ (DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress,
+ (DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress +
+ inh.IMPORT_DIRECTORY.Size));
+
+ DWORD nOldDlls = inh.IMPORT_DIRECTORY.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ DWORD obRem = sizeof(IMAGE_IMPORT_DESCRIPTOR) * nDlls;
+ DWORD obOld = obRem + sizeof(IMAGE_IMPORT_DESCRIPTOR) * nOldDlls;
+ DWORD obTab = PadToDwordPtr(obOld);
+ DWORD obDll = obTab + sizeof(DWORD_XX) * 4 * nDlls;
+ DWORD obStr = obDll;
+ cbNew = obStr;
+ for (n = 0; n < nDlls; n++) {
+ cbNew += PadToDword((DWORD)strlen(plpDlls[n]) + 1);
+ }
+
+ _Analysis_assume_(cbNew >
+ sizeof(IMAGE_IMPORT_DESCRIPTOR) * (nDlls + nOldDlls)
+ + sizeof(DWORD_XX) * 4 * nDlls);
+ 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;
+
+ 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=%x\n", dwProtect));
+
+ 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: %d\n", GetLastError()));
+ 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: %d\n", GetLastError()));
+ 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: %d\n", GetLastError()));
+ goto finish;
+ }
+
+ DWORD nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * (4 * n));
+ 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));
+ 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;
+ 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: %d\n", GetLastError()));
+ goto finish;
+ }
+
+ DETOUR_TRACE(("obBaseBef = %08x..%08x\n",
+ inh.IMPORT_DIRECTORY.VirtualAddress,
+ inh.IMPORT_DIRECTORY.VirtualAddress + inh.IMPORT_DIRECTORY.Size));
+ DETOUR_TRACE(("obBaseAft = %08x..%08x\n", obBase, obBase + obStr));
+
+ // 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.
+ //
+ 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: %d\n", GetLastError()));
+ goto finish;
+ }
+
+ inh.OptionalHeader.CheckSum = 0;
+
+ if (!WriteProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
+ DETOUR_TRACE(("WriteProcessMemory(idh) failed: %d\n", GetLastError()));
+ 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: %d\n", GetLastError()));
+ 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: %d\n", GetLastError()));
+ goto finish;
+ }
+
+ fSucceeded = TRUE;
+ goto finish;
+}