2019-04-14 00:21:56 +08:00
//////////////////////////////////////////////////////////////////////////////
//
// 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 ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " ReadProcessMemory(idh@%p..%p) failed: %lu \n " ,
2019-04-14 00:21:56 +08:00
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 ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " ReadProcessMemory(inh@%p..%p) failed: %lu \n " ,
2019-04-14 00:21:56 +08:00
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 ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " ReadProcessMemory(ish@%p..%p) failed: %lu \n " ,
2019-04-14 00:21:56 +08:00
pbModule + dwSec + sizeof ( ish ) * i ,
pbModule + dwSec + sizeof ( ish ) * ( i + 1 ) ,
GetLastError ( ) ) ) ;
goto finish ;
}
2021-05-17 09:04:54 +08:00
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.
//
2019-04-14 00:21:56 +08:00
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 ;
}
}
2021-05-17 09:04:54 +08:00
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 ) ;
}
}
2019-04-14 00:21:56 +08:00
DETOUR_TRACE ( ( " Imports: %p..%p \n " ,
2021-05-17 09:04:54 +08:00
pbModule + inh . IMPORT_DIRECTORY . VirtualAddress ,
pbModule + inh . IMPORT_DIRECTORY . VirtualAddress +
2019-04-14 00:21:56 +08:00
inh . IMPORT_DIRECTORY . Size ) ) ;
2021-05-17 09:04:54 +08:00
// 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.
2019-04-14 00:21:56 +08:00
DWORD nOldDlls = inh . IMPORT_DIRECTORY . Size / sizeof ( IMAGE_IMPORT_DESCRIPTOR ) ;
2021-05-17 09:04:54 +08:00
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 ;
}
2019-04-14 00:21:56 +08:00
DWORD obTab = PadToDwordPtr ( obOld ) ;
2021-05-17 09:04:54 +08:00
// 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 ;
}
2019-04-14 00:21:56 +08:00
DWORD obStr = obDll ;
cbNew = obStr ;
for ( n = 0 ; n < nDlls ; n + + ) {
2021-05-17 09:04:54 +08:00
if ( DWordAdd ( cbNew , PadToDword ( ( DWORD ) strlen ( plpDlls [ n ] ) + 1 ) , & cbNew ) ! = S_OK ) {
DETOUR_TRACE ( ( " Overflow adding string table entry \n " ) ) ;
goto finish ;
}
2019-04-14 00:21:56 +08:00
}
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 ;
2021-05-17 09:04:54 +08:00
IMAGE_THUNK_DATAXX * pt = NULL ;
2019-04-14 00:21:56 +08:00
DWORD obBase = ( DWORD ) ( pbNewIid - pbModule ) ;
DWORD dwProtect = 0 ;
if ( inh . IMPORT_DIRECTORY . VirtualAddress ! = 0 ) {
// Read the old import directory if it exists.
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " IMPORT_DIRECTORY perms=%lx \n " , dwProtect ) ) ;
2019-04-14 00:21:56 +08:00
if ( ! ReadProcessMemory ( hProcess ,
pbModule + inh . IMPORT_DIRECTORY . VirtualAddress ,
& piid [ nDlls ] ,
nOldDlls * sizeof ( IMAGE_IMPORT_DESCRIPTOR ) , & cbRead )
| | cbRead < nOldDlls * sizeof ( IMAGE_IMPORT_DESCRIPTOR ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " ReadProcessMemory(imports) failed: %lu \n " , GetLastError ( ) ) ) ;
2019-04-14 00:21:56 +08:00
goto finish ;
}
}
for ( n = 0 ; n < nDlls ; n + + ) {
HRESULT hrRet = StringCchCopyA ( ( char * ) pbNew + obStr , cbNew - obStr , plpDlls [ n ] ) ;
if ( FAILED ( hrRet ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " StringCchCopyA failed: %08lx \n " , hrRet ) ) ;
2019-04-14 00:21:56 +08:00
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 ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " ReplaceOptionalSizeA failed: %08lx \n " , hrRet ) ) ;
2019-04-14 00:21:56 +08:00
goto finish ;
}
2021-05-17 09:04:54 +08:00
DWORD nOffset = obTab + ( sizeof ( IMAGE_THUNK_DATAXX ) * ( 4 * n ) ) ;
2019-04-14 00:21:56 +08:00
piid [ n ] . OriginalFirstThunk = obBase + nOffset ;
2021-05-17 09:04:54 +08:00
// 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 ) ) ;
2019-04-14 00:21:56 +08:00
piid [ n ] . FirstThunk = obBase + nOffset ;
2021-05-17 09:04:54 +08:00
pt = ( ( IMAGE_THUNK_DATAXX * ) ( pbNew + nOffset ) ) ;
pt [ 0 ] . u1 . Ordinal = IMAGE_ORDINAL_FLAG_XX + 1 ;
pt [ 1 ] . u1 . Ordinal = 0 ;
2019-04-14 00:21:56 +08:00
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 ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " WriteProcessMemory(iid) failed: %lu \n " , GetLastError ( ) ) ) ;
2019-04-14 00:21:56 +08:00
goto finish ;
}
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " obBaseBef = %08lx..%08lx \n " ,
2019-04-14 00:21:56 +08:00
inh . IMPORT_DIRECTORY . VirtualAddress ,
inh . IMPORT_DIRECTORY . VirtualAddress + inh . IMPORT_DIRECTORY . Size ) ) ;
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " obBaseAft = %08lx..%08lx \n " , obBase , obBase + obStr ) ) ;
2019-04-14 00:21:56 +08:00
2021-05-17 09:04:54 +08:00
// 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.
//
2019-04-14 00:21:56 +08:00
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 ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " VirtualProtectEx(inh) write failed: %lu \n " , GetLastError ( ) ) ) ;
2019-04-14 00:21:56 +08:00
goto finish ;
}
inh . OptionalHeader . CheckSum = 0 ;
if ( ! WriteProcessMemory ( hProcess , pbModule , & idh , sizeof ( idh ) , NULL ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " WriteProcessMemory(idh) failed: %lu \n " , GetLastError ( ) ) ) ;
2019-04-14 00:21:56 +08:00
goto finish ;
}
DETOUR_TRACE ( ( " WriteProcessMemory(idh:%p..%p) \n " , pbModule , pbModule + sizeof ( idh ) ) ) ;
if ( ! WriteProcessMemory ( hProcess , pbModule + idh . e_lfanew , & inh , sizeof ( inh ) , NULL ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " WriteProcessMemory(inh) failed: %lu \n " , GetLastError ( ) ) ) ;
2019-04-14 00:21:56 +08:00
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 ) ) {
2021-05-17 09:04:54 +08:00
DETOUR_TRACE ( ( " VirtualProtectEx(idh) restore failed: %lu \n " , GetLastError ( ) ) ) ;
2019-04-14 00:21:56 +08:00
goto finish ;
}
fSucceeded = TRUE ;
goto finish ;
}