/*++ Copyright (c) 1992 Microsoft Corporation Module Name: init.c Abstract: This is the initialization module for the INSTALER program. Author: Steve Wood (stevewo) 09-Aug-1994 Revision History: --*/ #include "instaler.h" #if TRACE_ENABLED ULONG EnabledTraceEvents = DBG_MASK_INTERNALERROR | DBG_MASK_MEMORYERROR; #endif MODULE_INFO ModuleInfo[ MAXIMUM_MODULE_INDEX ] = { {L"NTDLL", NULL}, {L"KERNEL32", NULL}, {L"ADVAPI32", NULL}, {L"USER32", NULL} }; API_INFO ApiInfo[ MAXIMUM_API_INDEX ] = { {NTDLL_MODULE_INDEX, "NtCreateFile", NULL, NtCreateFileHandler , sizeof( CREATEFILE_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtOpenFile", NULL, NtOpenFileHandler , sizeof( OPENFILE_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtDeleteFile", NULL, NtDeleteFileHandler , sizeof( DELETEFILE_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtSetInformationFile", NULL, NtSetInformationFileHandler , sizeof( SETINFORMATIONFILE_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtQueryAttributesFile", NULL, NtQueryAttributesFileHandler , sizeof( QUERYATTRIBUTESFILE_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtQueryDirectoryFile", NULL, NtQueryDirectoryFileHandler , sizeof( QUERYDIRECTORYFILE_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtCreateKey", NULL, NtCreateKeyHandler , sizeof( CREATEKEY_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtOpenKey", NULL, NtOpenKeyHandler , sizeof( OPENKEY_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtDeleteKey", NULL, NtDeleteKeyHandler , sizeof( DELETEKEY_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtSetValueKey", NULL, NtSetValueKeyHandler , sizeof( SETVALUEKEY_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtDeleteValueKey", NULL, NtDeleteValueKeyHandler , sizeof( DELETEVALUEKEY_PARAMETERS ) , sizeof( NTSTATUS )}, {NTDLL_MODULE_INDEX, "NtClose", NULL, NtCloseHandleHandler , sizeof( CLOSEHANDLE_PARAMETERS ) , sizeof( NTSTATUS )}, {KERNEL32_MODULE_INDEX, "GetVersion", NULL, GetVersionHandler , 0 , sizeof( DWORD )}, {KERNEL32_MODULE_INDEX, "GetVersionExW", NULL, GetVersionExWHandler , sizeof( GETVERSIONEXW_PARAMETERS ) , sizeof( BOOL )}, {KERNEL32_MODULE_INDEX, "SetCurrentDirectoryA", NULL, SetCurrentDirectoryAHandler , sizeof( SETCURRENTDIRECTORYA_PARAMETERS ) , sizeof( BOOL )}, {KERNEL32_MODULE_INDEX, "SetCurrentDirectoryW", NULL, SetCurrentDirectoryWHandler , sizeof( SETCURRENTDIRECTORYW_PARAMETERS ) , sizeof( BOOL )}, {KERNEL32_MODULE_INDEX, "WritePrivateProfileStringA", NULL, WritePrivateProfileStringAHandler , sizeof( WRITEPRIVATEPROFILESTRINGA_PARAMETERS ) , sizeof( DWORD )}, {KERNEL32_MODULE_INDEX, "WritePrivateProfileStringW", NULL, WritePrivateProfileStringWHandler , sizeof( WRITEPRIVATEPROFILESTRINGW_PARAMETERS ) , sizeof( DWORD )}, {KERNEL32_MODULE_INDEX, "WritePrivateProfileSectionA", NULL, WritePrivateProfileSectionAHandler, sizeof( WRITEPRIVATEPROFILESECTIONA_PARAMETERS ) , sizeof( DWORD )}, {KERNEL32_MODULE_INDEX, "WritePrivateProfileSectionW", NULL, WritePrivateProfileSectionWHandler, sizeof( WRITEPRIVATEPROFILESECTIONW_PARAMETERS ) , sizeof( DWORD )}, {KERNEL32_MODULE_INDEX, "GetPrivateProfileStringW", NULL, NULL, 0, sizeof( DWORD )}, {KERNEL32_MODULE_INDEX, "GetPrivateProfileStringA", NULL, NULL, 0, sizeof( DWORD )}, {KERNEL32_MODULE_INDEX, "GetPrivateProfileSectionW", NULL, NULL, 0, sizeof( DWORD )}, {KERNEL32_MODULE_INDEX, "GetPrivateProfileSectionA", NULL, NULL, 0, sizeof( DWORD )}, {ADVAPI32_MODULE_INDEX, "RegConnectRegistryW", NULL, RegConnectRegistryWHandler , sizeof( REGCONNECTREGISTRYW_PARAMETERS ) , sizeof( DWORD )}, {USER32_MODULE_INDEX, "ExitWindowsEx", NULL, ExitWindowsExHandler , sizeof( EXITWINDOWSEX_PARAMETERS ) , sizeof( BOOL )} }; BOOLEAN LoadApplicationForDebug( PWSTR CommandLine ); BOOLEAN LoadDriveLetterDefinitions( VOID ); WCHAR WindowsDirectoryBuffer[ MAX_PATH ]; UNICODE_STRING WindowsDirectory; BOOLEAN InitializeInstaler( int argc, char *argv[] ) { UINT ModuleIndex, ApiIndex; PWSTR CommandLine; UINT i; LPSTR s; UCHAR c; SYSTEM_INFO SystemInfo; BOOLEAN ProcessingDebugSwitch; InitCommonCode( "INSTALER", "[-9] [-r] [-dAE] CommandLine...", "-9 specifies that GetVersion should lie and tell application\n" " that it is running on Windows 95\n" "-r specifies that attempts to do a wildcard scan of the root\n" " directory of a drive should fail.\n" "-d specifies one or more debugging message options\n" " A - shows all errors\n" " E - shows all debug events\n" " C - shows all create API calls\n" ); AppHeap = GetProcessHeap(); InstalerModuleHandle = GetModuleHandle( NULL ); if (GetWindowsDirectoryW( WindowsDirectoryBuffer, MAX_PATH ) == 0) { return FALSE; } RtlInitUnicodeString( &WindowsDirectory, WindowsDirectoryBuffer ); GetSystemInfo( &SystemInfo ); TemporaryBufferLengthGrowth = SystemInfo.dwPageSize; TemporaryBufferMaximumLength = ((128 * 1024) + SystemInfo.dwAllocationGranularity - 1) & ~SystemInfo.dwAllocationGranularity; TemporaryBuffer = VirtualAlloc( NULL, TemporaryBufferMaximumLength, MEM_RESERVE, PAGE_READWRITE ); if (TemporaryBuffer == NULL) { return FALSE; } InitializeListHead( &ProcessListHead ); InitializeListHead( &FileReferenceListHead ); InitializeListHead( &KeyReferenceListHead ); InitializeListHead( &IniReferenceListHead ); InitializeCriticalSection( &BreakTable ); // // Loop over the table of modules and make sure each is loaded in our // address space so we can access their export tables. // for (ModuleIndex=0; ModuleIndex L' ') { CommandLine += 1; } CommandLine = wcsstr( CommandLine, GetArgAsUnicode( s ) ); break; } } if (ImlPath == NULL) { Usage( "Must specify an installation name as first argument", 0 ); } if (CommandLine == NULL) { Usage( "Command line missing", 0 ); } pImlNew = CreateIml( ImlPath, FALSE ); if (pImlNew == NULL) { FatalError( "Unable to create %ws (%u)\n", (ULONG)ImlPath, GetLastError() ); } // // Ready to roll. Cache the drive letter information and then load // the application with the DEBUG option so we can watch what it does // if (!LoadDriveLetterDefinitions() || !LoadApplicationForDebug( CommandLine ) ) { return FALSE; } return TRUE; } BOOLEAN LoadApplicationForDebug( PWSTR CommandLine ) { STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; PWSTR ImageFilePath; PWSTR ImageFileName; PWSTR CurrentDirectory; PWSTR TemporaryNull; WCHAR TemporaryChar; ULONG Length; WCHAR PathBuffer[ MAX_PATH ]; TemporaryNull = CommandLine; while(TemporaryChar = *TemporaryNull) { if (TemporaryChar == L' ' || TemporaryChar == L'\t') { *TemporaryNull = UNICODE_NULL; break; } TemporaryNull += 1; } ImageFilePath = AllocMem( MAX_PATH * sizeof( WCHAR ) ); Length = SearchPath( NULL, CommandLine, L".exe", MAX_PATH, ImageFilePath, &ImageFileName ); *TemporaryNull = TemporaryChar; if (!Length || Length >= MAX_PATH) { DeclareError( INSTALER_CANT_DEBUG_PROGRAM, GetLastError(), CommandLine ); return FALSE; } if (ImageFileName != NULL && ImageFileName > ImageFilePath && ImageFileName[ -1 ] == L'\\' ) { ImageFileName[ -1 ] = UNICODE_NULL; CurrentDirectory = wcscpy( PathBuffer, ImageFilePath ); ImageFileName[ -1 ] = L'\\'; SetCurrentDirectory( CurrentDirectory ); } else { CurrentDirectory = NULL; } memset( &StartupInfo, 0, sizeof( StartupInfo ) ); StartupInfo.cb = sizeof(StartupInfo); // // Create the process with DEBUG option, as a separate WOW so we only see our // application's damage. // if (!CreateProcess( ImageFilePath, CommandLine, NULL, NULL, FALSE, // No handles to inherit DEBUG_PROCESS | CREATE_SEPARATE_WOW_VDM, // Ignored for 32 bit apps NULL, CurrentDirectory, &StartupInfo, &ProcessInformation ) ) { DeclareError( INSTALER_CANT_DEBUG_PROGRAM, GetLastError(), CommandLine ); return FALSE; } sprintf( (LPSTR)PathBuffer, "%ws%ws.log", InstalerDirectory, InstallationName ); InstalerLogFile = fopen( (LPSTR)PathBuffer, "w" ); // // Will pick up the process and thread handles with the // CREATE_PROCESS_DEBUG_EVENT and CREATE_PROCESS_THREAD_EVENT // return TRUE; } PVOID LookupEntryPointInIAT( HMODULE ModuleHandle, PCHAR EntryPointName ) { PIMAGE_EXPORT_DIRECTORY Exports; PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTableEntries; DWORD NumberOfFunctionTableEntries; ULONG ExportSize, FunctionTableSize; PCHAR *EntryPointNames; PULONG EntryPointAddresses; PUSHORT EntryPointOrdinals; PVOID EntryPointAddress; ULONG i; Exports = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData( ModuleHandle, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportSize ); if (Exports == NULL) { return NULL; } FunctionTableEntries = (PIMAGE_RUNTIME_FUNCTION_ENTRY) RtlImageDirectoryEntryToData( ModuleHandle, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &FunctionTableSize ); if (FunctionTableEntries != NULL) { NumberOfFunctionTableEntries = FunctionTableSize / sizeof( IMAGE_RUNTIME_FUNCTION_ENTRY ); } else { NumberOfFunctionTableEntries = 0; } EntryPointNames = (PCHAR *)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfNames); EntryPointOrdinals = (PUSHORT)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfNameOrdinals); EntryPointAddresses = (PULONG)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfFunctions); for (i=0; iNumberOfNames; i++) { EntryPointAddress = (PVOID)((PCHAR)ModuleHandle + EntryPointAddresses[ EntryPointOrdinals[ i ] ] ); if ((ULONG)EntryPointAddress > (ULONG)Exports && (ULONG)EntryPointAddress < ((ULONG)Exports + ExportSize) ) { // // Skip this... It's a forwarded reference // } else if (!_stricmp( EntryPointName, (PCHAR)ModuleHandle + (ULONG)EntryPointNames[ i ] )) { return GetAddressForEntryPointBreakpoint( EntryPointAddress, NumberOfFunctionTableEntries, FunctionTableEntries ); } } DbgEvent( INTERNALERROR, ( "Unable to find entry point '%s' in module at %x\n", EntryPointName, ModuleHandle ) ); return NULL; } PVOID GetAddressForEntryPointBreakpoint( PVOID EntryPointAddress, DWORD NumberOfFunctionTableEntries OPTIONAL, PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTableEntries OPTIONAL ) { PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry; PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTable; LONG High; LONG Low; LONG Middle; // // If no function table, then okay to set breakpoint at exported // address. // if (NumberOfFunctionTableEntries == 0) { return EntryPointAddress; } // // Initialize search indicies. // Low = 0; High = NumberOfFunctionTableEntries - 1; FunctionTable = FunctionTableEntries; // // Perform binary search on the function table for a function table // entry that subsumes the specified PC. // while (High >= Low) { // // Compute next probe index and test entry. If the specified PC // is greater than of equal to the beginning address and less // than the ending address of the function table entry, then // return the address of the function table entry. Otherwise, // continue the search. // Middle = (Low + High) >> 1; FunctionEntry = &FunctionTable[Middle]; if ((ULONG)EntryPointAddress < FunctionEntry->BeginAddress) { High = Middle - 1; } else if ((ULONG)EntryPointAddress >= FunctionEntry->EndAddress) { Low = Middle + 1; } else { break; } } // // A function table entry for the specified PC was not found. Just use // the exported address and hope for the best. // return EntryPointAddress; } BOOLEAN LoadDriveLetterDefinitions( VOID ) { PVOID Buffer; ULONG BufferSize; ULONG cchDeviceName; ULONG cch; ULONG cchTargetPath; PWSTR DeviceName; PWSTR TargetPath; ULONG DriveLetterIndex; ULONG cb; PDRIVE_LETTER_INFO p; RtlInitUnicodeString( &AltDriveLetterPrefix, L"\\DosDevices\\" ); RtlInitUnicodeString( &DriveLetterPrefix, L"\\??\\" ); RtlInitUnicodeString( &UNCPrefix, L"UNC\\" ); BufferSize = 0x1000; Buffer = VirtualAlloc( NULL, BufferSize, MEM_COMMIT, PAGE_READWRITE ); if (Buffer == NULL) { DbgEvent( INTERNALERROR, ( "VirtualAlloc( %0x8 ) failed (%u)\n", BufferSize, GetLastError() ) ); return FALSE; } cchTargetPath = BufferSize / sizeof( WCHAR ); cchDeviceName = QueryDosDeviceW( NULL, Buffer, cchTargetPath ); if (cchDeviceName == 0) { DbgEvent( INTERNALERROR, ( "QueryDosDeviceW( NULL ) failed (%u)\n", GetLastError() ) ); return FALSE; } cchTargetPath -= (cchDeviceName + 2); DeviceName = Buffer; TargetPath = DeviceName + 2 + cchDeviceName; while (*DeviceName) { if (wcslen( DeviceName ) == 2 && DeviceName[ 1 ] == L':' && _wcsupr( DeviceName ) && DeviceName[ 0 ] >= L'@' && DeviceName[ 0 ] <= L'_' ) { cch = QueryDosDeviceW( DeviceName, TargetPath, cchTargetPath ); if (cch == 0) { DbgEvent( INTERNALERROR, ( "QueryDosDeviceW( %ws ) failed (%u)\n", DeviceName, GetLastError() ) ); return FALSE; } DriveLetterIndex = DeviceName[ 0 ] - L'@'; p = &DriveLetters[ DriveLetterIndex ]; p->DriveLetter = (WCHAR)(L'@' + DriveLetterIndex); cb = (cch+1) * sizeof( WCHAR ); p->NtLinkTarget.Buffer = AllocMem( cb ); if (p->NtLinkTarget.Buffer == NULL) { return FALSE; } MoveMemory( p->NtLinkTarget.Buffer, TargetPath, cb ); p->NtLinkTarget.Length = (USHORT)( cb - sizeof( WCHAR )); p->NtLinkTarget.MaximumLength = (USHORT)cb; p->DriveLetterValid = TRUE; } while (*DeviceName++) {} } return TRUE; } BOOLEAN IsDriveLetterPath( PUNICODE_STRING Path ) { ULONG DriveLetterIndex; PDRIVE_LETTER_INFO p; PUNICODE_STRING Prefix; if (RtlPrefixUnicodeString( Prefix = &DriveLetterPrefix, Path, TRUE ) || RtlPrefixUnicodeString( Prefix = &AltDriveLetterPrefix, Path, TRUE ) ) { Path->Length -= Prefix->Length; RtlMoveMemory( Path->Buffer, (PCHAR)(Path->Buffer) + Prefix->Length, Path->Length + sizeof( UNICODE_NULL ) ); if (RtlPrefixUnicodeString( &UNCPrefix, Path, TRUE )) { Path->Length -= UNCPrefix.Length; Path->Buffer[0] = L'\\'; Path->Buffer[1] = L'\\'; RtlMoveMemory( &Path->Buffer[2], (PCHAR)(Path->Buffer) + UNCPrefix.Length, Path->Length + sizeof( UNICODE_NULL ) ); Path->Length += 2 * sizeof( WCHAR ); } return TRUE; } for (DriveLetterIndex=0, p = &DriveLetters[ DriveLetterIndex ]; DriveLetterIndex<32; DriveLetterIndex++, p++ ) { if (p->DriveLetterValid && RtlPrefixUnicodeString( &p->NtLinkTarget, Path, TRUE ) ) { Path->Length -= p->NtLinkTarget.Length; RtlMoveMemory( &Path->Buffer[ 2 ], (PCHAR)(Path->Buffer) + p->NtLinkTarget.Length, Path->Length + sizeof( UNICODE_NULL ) ); Path->Buffer[ 0 ] = p->DriveLetter; Path->Buffer[ 1 ] = L':'; Path->Length += 2; return TRUE; } } return FALSE; } VOID TrimTemporaryBuffer( VOID ) { PVOID CommitAddress; if (TemporaryBufferLength == 0) { return; } if (VirtualFree( (PCHAR)TemporaryBuffer, TemporaryBufferLength, MEM_DECOMMIT ) ) { TemporaryBufferLength = 0; } return; } BOOLEAN GrowTemporaryBuffer( ULONG NeededSize ) { PVOID CommitAddress; if (NeededSize <= TemporaryBufferLength) { return TRUE; } if (TemporaryBufferLength == TemporaryBufferMaximumLength) { return FALSE; } CommitAddress = VirtualAlloc( (PCHAR)TemporaryBuffer + TemporaryBufferLength, NeededSize - TemporaryBufferLength, MEM_COMMIT, PAGE_READWRITE ); if (CommitAddress == NULL) { DbgEvent( INTERNALERROR, ( "VirtualAlloc( %0x8 ) failed (%u)\n", NeededSize - TemporaryBufferLength, GetLastError() ) ); return FALSE; } TemporaryBufferLength += TemporaryBufferLengthGrowth; return TRUE; } ULONG FillTemporaryBuffer( PPROCESS_INFO Process, PVOID Address, BOOLEAN Unicode, BOOLEAN DoubleNullTermination ) { ULONG BytesRead; ULONG TotalBytesRead; PVOID Source; PVOID Destination; LPSTR s1; PWSTR s2; BOOLEAN MoreToRead; BOOLEAN SeenOneNull; TotalBytesRead = 0; Destination = (PCHAR)TemporaryBuffer; MoreToRead = TRUE; SeenOneNull = FALSE; while (MoreToRead) { TotalBytesRead += TemporaryBufferLengthGrowth; if (!GrowTemporaryBuffer( TotalBytesRead )) { return 0; } Source = Destination; BytesRead = 0; if (!ReadProcessMemory( Process->Handle, Address, Destination, TemporaryBufferLengthGrowth, &BytesRead ) ) { if (BytesRead == 0) { MoreToRead = FALSE; } else { return 0; } } Destination = (PCHAR)Destination + TotalBytesRead; if (Unicode) { s2 = (PWSTR)Source; while (s2 < (PWSTR)Destination) { if (*s2 == UNICODE_NULL) { if (SeenOneNull || !DoubleNullTermination) { return (PCHAR)s2 - (PCHAR)TemporaryBuffer; } else { SeenOneNull = TRUE; } } else { SeenOneNull = FALSE; } s2++; } } else { s1 = (LPSTR)Source; while (s1 < (LPSTR)Destination) { if (*s1 == '\0') { if (SeenOneNull || !DoubleNullTermination) { return (PCHAR)s1 - (PCHAR)TemporaryBuffer; } else { SeenOneNull = TRUE; } } else { SeenOneNull = FALSE; } s1++; } } } return 0; }