[NTUSER][USER32] Refactor NtUserLoadKeyboardLayoutEx (#5107)

- Split some code of NtUserLoadKeyboardLayoutEx to newly-added co_IntLoadKeyboardLayoutEx helper function.
- Modify NtUserLoadKeyboardLayoutEx prototype.
- Move co_UserImmLoadLayout code.
- Implement KLF_REORDER.
- Rename UserLoadKbdLayout as co_UserLoadKbdLayout.
- Improve LoadKeyboardLayoutEx.
CORE-11700
This commit is contained in:
Katayama Hirofumi MZ 2023-03-05 11:41:32 +09:00 committed by GitHub
parent 0fb580a855
commit 77e6348f5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 165 additions and 110 deletions

View file

@ -2736,12 +2736,12 @@ NtUserKillTimer(
HKL
NTAPI
NtUserLoadKeyboardLayoutEx(
IN HANDLE Handle,
IN HANDLE hFile,
IN DWORD offTable,
IN PUNICODE_STRING puszKeyboardName,
IN HKL hKL,
IN PVOID pTables,
IN HKL hOldKL,
IN PUNICODE_STRING puszKLID,
IN DWORD dwKLID,
IN DWORD dwNewKL,
IN UINT Flags);
BOOL

View file

@ -369,12 +369,12 @@ cleanup:
}
/*
* UserLoadKbdLayout
* co_UserLoadKbdLayout
*
* Loads keyboard layout and creates KL object
*/
static PKL
UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL)
co_UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL)
{
LCID lCid;
CHARSETINFO cs;
@ -846,6 +846,129 @@ IntUnloadKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta, _In_ HKL hKL)
return co_IntUnloadKeyboardLayoutEx(pWinSta, pKL, 0);
}
PIMEINFOEX FASTCALL co_UserImmLoadLayout(_In_ HKL hKL)
{
PIMEINFOEX piiex;
if (!IS_IME_HKL(hKL) && !IS_CICERO_MODE())
return NULL;
piiex = ExAllocatePoolWithTag(PagedPool, sizeof(IMEINFOEX), USERTAG_IME);
if (!piiex)
return NULL;
if (!co_ClientImmLoadLayout(hKL, piiex))
{
ExFreePoolWithTag(piiex, USERTAG_IME);
return NULL;
}
return piiex;
}
HKL APIENTRY
co_IntLoadKeyboardLayoutEx(
IN OUT PWINSTATION_OBJECT pWinSta,
IN HANDLE hSafeFile,
IN HKL hOldKL,
IN PUNICODE_STRING puszSafeKLID,
IN HKL hNewKL,
IN UINT Flags)
{
PKL pOldKL, pNewKL;
UNREFERENCED_PARAMETER(hSafeFile);
if (hNewKL == NULL || (pWinSta->Flags & WSS_NOIO))
return NULL;
/* If hOldKL is specified, unload it and load new layput as default */
if (hOldKL && hOldKL != hNewKL)
{
pOldKL = UserHklToKbl(hOldKL);
if (pOldKL)
UserUnloadKbl(pOldKL);
}
/* FIXME: It seems KLF_RESET is only supported for WINLOGON */
/* Let's see if layout was already loaded. */
pNewKL = UserHklToKbl(hNewKL);
if (!pNewKL)
{
/* It wasn't, so load it. */
pNewKL = co_UserLoadKbdLayout(puszSafeKLID, hNewKL);
if (!pNewKL)
return NULL;
if (gspklBaseLayout)
{
/* Find last not unloaded layout */
PKL pLastKL = gspklBaseLayout->pklPrev;
while (pLastKL != gspklBaseLayout && (pLastKL->dwKL_Flags & KLF_UNLOAD))
pLastKL = pLastKL->pklPrev;
/* Add new layout to the list */
pNewKL->pklNext = pLastKL->pklNext;
pNewKL->pklPrev = pLastKL;
pNewKL->pklNext->pklPrev = pNewKL;
pNewKL->pklPrev->pklNext = pNewKL;
}
else
{
/* This is the first layout */
pNewKL->pklNext = pNewKL;
pNewKL->pklPrev = pNewKL;
gspklBaseLayout = pNewKL;
}
pNewKL->piiex = co_UserImmLoadLayout(hNewKL);
}
/* If this layout was prepared to unload, undo it */
pNewKL->dwKL_Flags &= ~KLF_UNLOAD;
/* Reorder if necessary */
if (Flags & KLF_REORDER)
IntReorderKeyboardLayouts(pWinSta, pNewKL);
/* Activate this layout in current thread */
if (Flags & KLF_ACTIVATE)
co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pNewKL, Flags);
/* Send shell message */
if (!(Flags & KLF_NOTELLSHELL))
co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hNewKL);
/* FIXME: KLF_REPLACELANG */
return hNewKL;
}
HANDLE FASTCALL IntVerifyKeyboardFileHandle(HANDLE hFile)
{
PFILE_OBJECT FileObject;
NTSTATUS Status;
if (hFile == INVALID_HANDLE_VALUE)
return NULL;
Status = ObReferenceObjectByHandle(hFile, FILE_READ_DATA, NULL, UserMode,
(PVOID*)&FileObject, NULL);
if (!NT_SUCCESS(Status))
{
ERR("0x%08X\n", Status);
return NULL;
}
/* FIXME: Is the file in the system directory? */
if (FileObject)
ObDereferenceObject(FileObject);
return hFile;
}
/* EXPORTS *******************************************************************/
/*
@ -1009,50 +1132,34 @@ cleanup:
return bRet;
}
/* Win: xxxImmLoadLayout */
PIMEINFOEX FASTCALL co_UserImmLoadLayout(_In_ HKL hKL)
{
PIMEINFOEX piiex;
if (!IS_IME_HKL(hKL) && !IS_CICERO_MODE())
return NULL;
piiex = ExAllocatePoolWithTag(PagedPool, sizeof(IMEINFOEX), USERTAG_IME);
if (!piiex)
return NULL;
if (!co_ClientImmLoadLayout(hKL, piiex))
{
ExFreePoolWithTag(piiex, USERTAG_IME);
return NULL;
}
return piiex;
}
/*
* NtUserLoadKeyboardLayoutEx
*
* Loads keyboard layout with given locale id
*
* NOTE: We adopt a different design from Microsoft's one for security reason.
* We don't use the 1st and 3rd parameters of NtUserLoadKeyboardLayoutEx.
* NOTE: We adopt a different design from Microsoft's one due to security reason.
* We don't use the 3rd parameter of NtUserLoadKeyboardLayoutEx.
* See https://bugtraq.securityfocus.com/detail/50056B96.6040306
*/
HKL
APIENTRY
NTAPI
NtUserLoadKeyboardLayoutEx(
IN HANDLE Handle, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c)
IN DWORD offTable, // Offset to KbdTables
IN PUNICODE_STRING puszKeyboardName, // Not used?
IN HKL hklUnload,
IN PUNICODE_STRING pustrKLID,
IN DWORD hkl,
IN HANDLE hFile,
IN DWORD offTable,
IN PVOID pTables,
IN HKL hOldKL,
IN PUNICODE_STRING puszKLID,
IN DWORD dwNewKL,
IN UINT Flags)
{
HKL hklRet = NULL;
PKL pKl = NULL, pklLast;
HKL hRetKL;
WCHAR Buffer[KL_NAMELENGTH];
UNICODE_STRING ustrSafeKLID;
UNICODE_STRING uszSafeKLID;
PWINSTATION_OBJECT pWinSta;
HANDLE hSafeFile;
UNREFERENCED_PARAMETER(offTable);
UNREFERENCED_PARAMETER(pTables);
if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG|
KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS|
@ -1063,14 +1170,12 @@ NtUserLoadKeyboardLayoutEx(
return NULL;
}
/* FIXME: It seems KLF_RESET is only supported for WINLOGON */
RtlInitEmptyUnicodeString(&ustrSafeKLID, Buffer, sizeof(Buffer));
RtlInitEmptyUnicodeString(&uszSafeKLID, Buffer, sizeof(Buffer));
_SEH2_TRY
{
ProbeForRead(pustrKLID, sizeof(*pustrKLID), 1);
ProbeForRead(pustrKLID->Buffer, sizeof(pustrKLID->Length), 1);
RtlCopyUnicodeString(&ustrSafeKLID, pustrKLID);
ProbeForRead(puszKLID, sizeof(*puszKLID), 1);
ProbeForRead(puszKLID->Buffer, sizeof(puszKLID->Length), 1);
RtlCopyUnicodeString(&uszSafeKLID, puszKLID);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
@ -1081,67 +1186,19 @@ NtUserLoadKeyboardLayoutEx(
UserEnterExclusive();
/* If hklUnload is specified, unload it and load new layput as default */
if (hklUnload && (hklUnload != UlongToHandle(hkl)))
{
pKl = UserHklToKbl(hklUnload);
if (pKl)
UserUnloadKbl(pKl);
}
hSafeFile = (hFile ? IntVerifyKeyboardFileHandle(hFile) : NULL);
pWinSta = IntGetProcessWindowStation(NULL);
hRetKL = co_IntLoadKeyboardLayoutEx(pWinSta,
hSafeFile,
hOldKL,
&uszSafeKLID,
(HKL)(DWORD_PTR)dwNewKL,
Flags);
if (hSafeFile)
ZwClose(hSafeFile);
/* Let's see if layout was already loaded. */
pKl = UserHklToKbl(UlongToHandle(hkl));
if (!pKl)
{
/* It wasn't, so load it. */
pKl = UserLoadKbdLayout(&ustrSafeKLID, UlongToHandle(hkl));
if (!pKl)
goto cleanup;
if (gspklBaseLayout)
{
/* Find last not unloaded layout */
pklLast = gspklBaseLayout->pklPrev;
while (pklLast != gspklBaseLayout && pklLast->dwKL_Flags & KLF_UNLOAD)
pklLast = pklLast->pklPrev;
/* Add new layout to the list */
pKl->pklNext = pklLast->pklNext;
pKl->pklPrev = pklLast;
pKl->pklNext->pklPrev = pKl;
pKl->pklPrev->pklNext = pKl;
}
else
{
/* This is the first layout */
pKl->pklNext = pKl;
pKl->pklPrev = pKl;
gspklBaseLayout = pKl;
}
pKl->piiex = co_UserImmLoadLayout(UlongToHandle(hkl));
}
/* If this layout was prepared to unload, undo it */
pKl->dwKL_Flags &= ~KLF_UNLOAD;
/* Activate this layout in current thread */
if (Flags & KLF_ACTIVATE)
co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl, Flags);
/* Send shell message */
if (!(Flags & KLF_NOTELLSHELL))
co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl);
/* Return hkl on success */
hklRet = UlongToHandle(hkl);
/* FIXME: KLF_REPLACELANG
KLF_REORDER */
cleanup:
UserLeave();
return hklRet;
return hRetKL;
}
/*

View file

@ -721,9 +721,9 @@ VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName)
/*
* @unimplemented
*
* NOTE: We adopt a different design from Microsoft's one for security reason.
* NOTE: We adopt a different design from Microsoft's one due to security reason.
* See NtUserLoadKeyboardLayoutEx.
*/
/* Win: LoadKeyboardLayoutWorker */
HKL APIENTRY
IntLoadKeyboardLayout(
_In_ HKL hklUnload,
@ -733,7 +733,6 @@ IntLoadKeyboardLayout(
_In_ BOOL unknown5)
{
DWORD dwKLID, dwHKL, dwType, dwSize;
UNICODE_STRING ustrKbdName;
UNICODE_STRING ustrKLID;
WCHAR wszRegKey[256] = L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
WCHAR wszLayoutId[10], wszNewKLID[KL_NAMELENGTH], szImeFileName[80];
@ -807,7 +806,7 @@ IntLoadKeyboardLayout(
szImeFileName[_countof(szImeFileName) - 1] = UNICODE_NULL;
GetSystemLibraryPath(szPath, _countof(szPath), szImeFileName);
/* We don't allow the invalid "IME File" values for security reason */
/* We don't allow the invalid "IME File" values due to security reason */
if (dwType != REG_SZ || szImeFileName[0] == 0 ||
wcscspn(szImeFileName, L":\\/") != wcslen(szImeFileName) ||
GetFileAttributesW(szPath) == INVALID_FILE_ATTRIBUTES) /* Does not exist? */
@ -833,9 +832,8 @@ IntLoadKeyboardLayout(
dwHKL = MAKELONG(wLow, wHigh);
ZeroMemory(&ustrKbdName, sizeof(ustrKbdName));
RtlInitUnicodeString(&ustrKLID, pwszKLID);
hNewKL = NtUserLoadKeyboardLayoutEx(NULL, 0, &ustrKbdName, NULL, &ustrKLID, dwHKL, Flags);
hNewKL = NtUserLoadKeyboardLayoutEx(NULL, 0, NULL, hklUnload, &ustrKLID, dwHKL, Flags);
CliImmInitializeHotKeys(SETIMEHOTKEY_ADD, hNewKL);
return hNewKL;
}