#include "pch.h" #include "loader.h" #include #pragma hdrstop #define ISNT() (g_VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) #define ISOSR2() (LOWORD(g_VersionInfo.dwBuildNumber) > 1080) #define BUILDNUM() (g_VersionInfo.dwBuildNumber) // // Global variables defined here // // // TargetNativeLangID : this is native language ID of running system // LANGID TargetNativeLangID; // // SourceNativeLangID : this is native language ID of new NT you want to install // LANGID SourceNativeLangID; // // g_IsLanguageMatched : if source and target language are matched (or compatible) // // 1. if SourceNativeLangID == TargetNativeLangID // // 2. if SourceNativeLangID's alternative ID == TargetNativeLangID // BOOL g_IsLanguageMatched; typedef struct _tagAltSourceLocale { LANGID LangId; LANGID AltLangId; DWORD MajorOs; DWORD MinorOs; DWORD ExcludedOs; } ALTSOURCELOCALE, *PALTSOURCELOCALE; ALTSOURCELOCALE g_AltSourceLocale [] = {{0x00000C04, 0x00000409, 0x0200, 0xFFFFFFFF, 0xFFFFFFFF}, {0x0000040D, 0x00000409, 0x0200, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00000401, 0x00000409, 0x0200, 0xFFFFFFFF, 0xFFFFFFFF}, {0x0000041E, 0x00000409, 0x0200, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00000809, 0x00000409, 0x00FF, 0xFFFFFFFF, 0xFFFFFFFF}, {0x0000080A, 0x00000C0A, 0x00FF, 0xFFFFFFFF, 0xFFFFFFFF}, {0x0000040A, 0x00000C0A, 0x0300, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00000425, 0x00000409, 0x00FF, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00000801, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00000c01, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00001001, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00001401, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00001801, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00001c01, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00002001, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00002401, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00002801, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00002c01, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00003001, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00003401, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00003801, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00003c01, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0x00004001, 0x00000401, 0x0001, 0xFFFFFFFF, 0xFFFFFFFF}, {0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}}; typedef struct _tagTrustedSourceLocale { LANGID LangId; DWORD MajorOs; DWORD MinorOs; DWORD ExcludedOs; } TRUSTEDSOURCELOCALE, *PTRUSTEDSOURCELOCALE; TRUSTEDSOURCELOCALE g_TrustedSourceLocale [] = {{0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}}; typedef struct _tagOSVERSIONMAJORID { PCTSTR Name; DWORD MajorId; DWORD Platform; DWORD Major; DWORD Minor; } OSVERSIONMAJORID, *POSVERSIONMAJORID; OSVERSIONMAJORID g_OsVersionMajorId [] = {{TEXT("Win95"), 0x0001, 1, 4, 0}, {TEXT("Win98"), 0x0002, 1, 4, 10}, {TEXT("WinME"), 0x0004, 1, 4, 90}, {TEXT("WinNT351"), 0x0100, 2, 3, 51}, {TEXT("WinNT40"), 0x0200, 2, 4, 0}, {NULL, 0, 0, 0, 0}}; typedef struct _tagOSVERSIONMINORID { PCTSTR Name; DWORD MajorId; DWORD MinorId; DWORD Platform; DWORD Major; DWORD Minor; DWORD Build; PCTSTR CSDVer; } OSVERSIONMINORID, *POSVERSIONMINORID; OSVERSIONMINORID g_OsVersionMinorId [] = {{NULL, 0, 0, 0, 0, 0, 0, NULL}}; typedef struct _tagLANGINFO { LANGID LangID; INT Count; } LANGINFO,*PLANGINFO; BOOL TrustedDefaultUserLocale ( LANGID LangID ); BOOL CALLBACK EnumLangProc( HANDLE hModule, // resource-module handle LPCTSTR lpszType, // pointer to resource type LPCTSTR lpszName, // pointer to resource name WORD wIDLanguage, // resource language identifier LONG_PTR lParam // application-defined parameter ) /*++ Routine Description: Callback that counts versions stamps. Arguments: Details of version enumerated version stamp. (Ignore.) Return Value: Indirectly thru lParam: count, langID --*/ { PLANGINFO LangInfo; LangInfo = (PLANGINFO) lParam; LangInfo->Count++; // // for localized build contains multiple resource, // it usually contains 0409 as backup lang. // // if LangInfo->LangID != 0 means we already assigned an ID to it // // so when wIDLanguage == 0x409, we keep the one we got from last time // if ((wIDLanguage == 0x409) && (LangInfo->LangID != 0)) { return TRUE; } LangInfo->LangID = wIDLanguage; return TRUE; // continue enumeration } LANGID GetNTDLLNativeLangID ( VOID ) /*++ Routine Description: This function is designed specifically for getting native lang of ntdll.dll This is not a generic function to get other module's language the assumption is: 1. if only one language in resource then return this lang 2. if two languages in resource then return non-US language 3. if more than two languages, it's invalid in our case, but returns the last one. Arguments: None Return Value: Native lang ID in ntdll.dll --*/ { LPCTSTR Type = (LPCTSTR) RT_VERSION; LPCTSTR Name = (LPCTSTR) 1; LANGINFO LangInfo; ZeroMemory(&LangInfo,sizeof(LangInfo)); EnumResourceLanguages ( GetModuleHandle(TEXT("ntdll.dll")), Type, Name, EnumLangProc, (LONG_PTR) &LangInfo ); if ((LangInfo.Count > 2) || (LangInfo.Count < 1) ) { // // put error log here // // so far, for NT 3.51, only JPN has two language resources } return LangInfo.LangID; } BOOL IsHongKongVersion ( VOID ) /*++ Routine Description: Try to identify HongKong NT 4.0 It based on: NTDLL's language is English and build is 1381 and pImmReleaseContext return TRUE Arguments: Return Value: Language ID of running system --*/ { HMODULE hMod; BOOL bRet=FALSE; typedef BOOL (*IMMRELEASECONTEXT) (HWND,HANDLE); IMMRELEASECONTEXT pImmReleaseContext; LANGID TmpID = GetNTDLLNativeLangID(); if ((g_VersionInfo.dwBuildNumber == 1381) && (TmpID == 0x0409)){ hMod = LoadLibrary(TEXT("imm32.dll")); if (hMod) { pImmReleaseContext = (IMMRELEASECONTEXT) GetProcAddress(hMod,"ImmReleaseContext"); if (pImmReleaseContext) { bRet = pImmReleaseContext(NULL,NULL); } FreeLibrary(hMod); } } return (bRet); } LANGID GetDefaultUserLangID ( VOID ) { LONG dwErr; HKEY hkey; DWORD dwSize; CHAR buffer[512]; LANGID langid = 0; dwErr = RegOpenKeyEx( HKEY_USERS, TEXT(".DEFAULT\\Control Panel\\International"), 0, KEY_READ, &hkey ); if( dwErr == ERROR_SUCCESS ) { dwSize = sizeof(buffer); dwErr = RegQueryValueExA(hkey, "Locale", NULL, //reserved NULL, //type buffer, &dwSize ); if(dwErr == ERROR_SUCCESS) { langid = LANGIDFROMLCID(strtoul(buffer,NULL,16)); } RegCloseKey(hkey); } return langid; } LANGID GetTargetNativeLangID ( VOID ) /*++ Routine Description: Applies different rules to different platforms NT build number <= 1840 : check ntdll's language, we scaned all 3.51's ntdll on boneyard\intl, it looks like we can trust them. build number > 1840 : user MUI language Win9x use default user's resource language Arguments: Return Value: Language ID of running system --*/ { LONG dwErr; HKEY hkey; DWORD dwSize; CHAR buffer[512]; LANGID rcLang; LANGID langid = 0; // Find out if we are running on NT or WIN9X if( ISNT() ) { // // We're on NT, but which version? GetSystemDefaultUILanguage() was broke until 1840... // if( g_VersionInfo.dwBuildNumber > 1840 ) { FARPROC NT5API; // // Use the API to find out our locale. // if( NT5API = GetProcAddress( GetModuleHandle(TEXT("kernel32.dll")), "GetSystemDefaultUILanguage") ) { rcLang = (LANGID)NT5API(); // // need to convert decimal to hex, LANGID to chr. // langid = rcLang; } } else { // // by looking into \\boneyard\intl, almost every ntdll.dll marked correct lang ID // so get langID from ntdll.dll // langid = GetNTDLLNativeLangID(); if (langid == 0x0409) { if (IsHongKongVersion()) { langid = 0x0C04; } else { // // if default user's locale is in [TrustedDefaultUserLocale] // // then this is a backdoor for some localized build that its ntdll.dll marked // // as English but can't be upgrade by US version. // LANGID DefaultUserLangID = GetDefaultUserLangID(); if (DefaultUserLangID && TrustedDefaultUserLocale (DefaultUserLangID)) { langid = DefaultUserLangID; } } } } } else { // // We're on Win9x. // dwErr = RegOpenKeyEx( HKEY_USERS, TEXT(".Default\\Control Panel\\desktop\\ResourceLocale"), 0, KEY_READ, &hkey ); if (dwErr == ERROR_SUCCESS) { dwSize = sizeof(buffer); dwErr = RegQueryValueExA( hkey, "", NULL, //reserved NULL, //type buffer, &dwSize ); if(dwErr == ERROR_SUCCESS) { langid = LANGIDFROMLCID(strtoul(buffer,NULL,16)); } RegCloseKey(hkey); } if ( dwErr != ERROR_SUCCESS ) { // Check HKLM\System\CurrentControlSet\Control\Nls\Locale dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\Nls\\Locale"), 0, KEY_READ, &hkey ); if (dwErr == ERROR_SUCCESS) { dwSize = sizeof(buffer); dwErr = RegQueryValueExA( hkey, "", NULL, //reserved NULL, //type buffer, &dwSize ); if (dwErr == ERROR_SUCCESS) { langid = LANGIDFROMLCID(strtoul(buffer,NULL,16)); } RegCloseKey(hkey); } } } return (langid); } LANGID GetSourceNativeLangID ( VOID ) /*++ Routine Description: [DefaultValues] Locale = xxxx every localized build has it's own Locale in intl.inf, so we use this value to identify source languag Arguments: Return Value: Language ID of source --*/ { // BUGBUG - implement this by reading our own version info. LPCTSTR Type = (LPCTSTR) RT_VERSION; LPCTSTR Name = (LPCTSTR) 1; LANGINFO LangInfo; ZeroMemory(&LangInfo,sizeof(LangInfo)); EnumResourceLanguages ( NULL, // our own module Type, Name, EnumLangProc, (LONG_PTR) &LangInfo ); if ((LangInfo.Count > 2) || (LangInfo.Count < 1) ) { // // put error log here // // so far, for NT 3.51, only JPN has two language resources } return LangInfo.LangID; } DWORD GetOsMajorId ( VOID ) { POSVERSIONMAJORID p = g_OsVersionMajorId; while (p->Name) { if ((p->Platform == g_VersionInfo.dwPlatformId) && (p->Major == g_VersionInfo.dwMajorVersion) && (p->Minor == g_VersionInfo.dwMinorVersion) ) { return p->MajorId; } p++; } return 0; } DWORD GetOsMinorId ( VOID ) { POSVERSIONMINORID p = g_OsVersionMinorId; while (p->Name) { if ((p->Platform == g_VersionInfo.dwPlatformId) && (p->Major == g_VersionInfo.dwMajorVersion) && (p->Minor == g_VersionInfo.dwMinorVersion) && (p->Build == g_VersionInfo.dwBuildNumber) && ((p->CSDVer == NULL) || _tcsicmp (p->CSDVer, g_VersionInfo.szCSDVersion)) ) { return p->MinorId; } p++; } return 0; } BOOL TrustedDefaultUserLocale ( LANGID LangID ) { PTRUSTEDSOURCELOCALE p = g_TrustedSourceLocale; while (p->LangId) { if ((!(p->ExcludedOs & GetOsMinorId ())) && ((p->MinorOs & GetOsMinorId ()) || (p->MajorOs & GetOsMajorId ())) ) { return TRUE; } p++; } return FALSE; } BOOL CheckLanguageVersion ( LANGID SourceLangID, LANGID TargetLangID ) /*++ Routine Description: Check if the language of source NT is same as target NT or ,at least, compatibile Arguments: Inf handle of intl.inf Return Value: TRUE They are same or compatibile FALSE They are different --*/ { PALTSOURCELOCALE p = g_AltSourceLocale; TCHAR TargetLangIDStr[9]; LANGID SrcLANGID; LANGID DstLANGID; LANGID AltSourceLangID; // // If either one is 0, allow the upgrade. This is Windows 2000 Beta3 behavior. // if (SourceLangID == 0 || TargetLangID == 0) { return TRUE; } if (SourceLangID == TargetLangID) { return TRUE; } // // if Src != Dst, then we need to look up inf file to see // // if we can open a backdoor for Target language // // // use TargetLangID as key to find alternative SourceLangID // while (p->LangId) { // // Check if we found alternative locale // AltSourceLangID = LANGIDFROMLCID(p->AltLangId); if ((TargetLangID == p->LangId) && (SourceLangID == AltSourceLangID) ) { // // We are here if we found alternative source lang, // // now check the version criteria // if ((!(p->ExcludedOs & GetOsMinorId ())) && ((p->MinorOs & GetOsMinorId ()) || (p->MajorOs & GetOsMajorId ())) ) { return TRUE; } } p++; } return FALSE; } BOOL InitLanguageDetection ( VOID ) /*++ Routine Description: Initialize language detection and put the result in 3 global variables SourceNativeLangID - LANGID of Source (NT is going to be installed) TargetNativeLangID - LANGID of Target (OS system which is running) g_IsLanguageMatched - If language is not matched, then blocks upgrade Arguments: None Return Value: TRUE init correctly FALSE init failed --*/ { // // Init Global Variables // SourceNativeLangID = GetSourceNativeLangID(); TargetNativeLangID = GetTargetNativeLangID(); g_IsLanguageMatched = CheckLanguageVersion(SourceNativeLangID,TargetNativeLangID); if (!g_IsLanguageMatched) { if (SourceNativeLangID == 0x00000409) { // This is a localized system running an English wizard. // We want to allow that. g_IsLanguageMatched = TRUE; } } return TRUE; }