1
1
Fork 0
mirror of https://github.com/pbatard/rufus.git synced 2024-08-14 23:57:05 +00:00

Compare commits

...

3 commits

Author SHA1 Message Date
marcellogianola
7fa83aa479
[loc] update Italian translation
* Closes #1841
2022-01-05 18:37:47 +00:00
Austin Wise
ef2ff7179d
[msvc] add provision to prevent DLL search order hijacking through delay loading
* Hypothetically if the user's current directory contains a malicious DLL that DLL
  could be loaded instead of the one in System32.
* Whereas the previous patch should have taken care of the one DLL referenced by
  Rufus that may be vulnerable to this attack (version.dll), we nonetheless add
  delay loading for all the libraries we reference as a precautionary measure.
* One can confirm that this works by using dumpbin.exe /IMPORTS to make sure
  a specific DLL is delay loaded. Then putting a breakpoint in the delay load
  hook should also confirm that the hook is used.
* Closes #1838
2022-01-05 18:33:59 +00:00
Pete Batard
f6ac559f4d
[iso] don't link with version.lib to prevent DLL sideloading issues
* This is part of #1838, where we need to sort the version.dll sideloading problem for MinGW.
* A subsequent patch will be applied to MSVC, to more generally delay the loading of DLLs.
* Also fix a typo with an assert expression.
2022-01-05 16:49:06 +00:00
9 changed files with 73 additions and 38 deletions

View file

@ -133,11 +133,12 @@
<AdditionalOptions>/utf-8 $(ExternalCompilerOptions) %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>advapi32.lib;comctl32.lib;comdlg32.lib;crypt32.lib;gdi32.lib;ole32.lib;setupapi.lib;shell32.lib;shlwapi.lib;version.lib;wintrust.lib;psapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>advapi32.lib;comctl32.lib;comdlg32.lib;crypt32.lib;gdi32.lib;ole32.lib;setupapi.lib;shell32.lib;shlwapi.lib;wintrust.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
<DelayLoadDLLs>advapi32.dll;comctl32.dll;comdlg32.dll;crypt32.dll;gdi32.dll;ole32.dll;setupapi.dll;shell32.dll;shlwapi.dll;wintrust.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>_UNICODE;UNICODE;RUFUS_LOC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -157,11 +158,12 @@
<AdditionalOptions>/utf-8 $(ExternalCompilerOptions) %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>advapi32.lib;comctl32.lib;comdlg32.lib;crypt32.lib;gdi32.lib;ole32.lib;setupapi.lib;shell32.lib;shlwapi.lib;version.lib;wintrust.lib;ole32.lib;advapi32.lib;gdi32.lib;shell32.lib;comdlg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>advapi32.lib;comctl32.lib;comdlg32.lib;crypt32.lib;gdi32.lib;ole32.lib;setupapi.lib;shell32.lib;shlwapi.lib;wintrust.lib;ole32.lib;advapi32.lib;gdi32.lib;shell32.lib;comdlg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<AdditionalLibraryDirectories>C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\um\arm</AdditionalLibraryDirectories>
<DelayLoadDLLs>advapi32.dll;comctl32.dll;comdlg32.dll;crypt32.dll;gdi32.dll;ole32.dll;setupapi.dll;shell32.dll;shlwapi.dll;wintrust.dll;ole32.dll;advapi32.dll;gdi32.dll;shell32.dll;comdlg32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>_UNICODE;UNICODE;RUFUS_LOC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -183,11 +185,12 @@
<AdditionalOptions>/utf-8 $(ExternalCompilerOptions) %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>advapi32.lib;comctl32.lib;comdlg32.lib;crypt32.lib;gdi32.lib;ole32.lib;setupapi.lib;shell32.lib;shlwapi.lib;version.lib;wintrust.lib;ole32.lib;advapi32.lib;gdi32.lib;shell32.lib;comdlg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>advapi32.lib;comctl32.lib;comdlg32.lib;crypt32.lib;gdi32.lib;ole32.lib;setupapi.lib;shell32.lib;shlwapi.lib;wintrust.lib;ole32.lib;advapi32.lib;gdi32.lib;shell32.lib;comdlg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<AdditionalLibraryDirectories>C:\Program Files (x86)\Windows Kits\10\Lib\10.0.16299.0\um\arm64</AdditionalLibraryDirectories>
<DelayLoadDLLs>advapi32.dll;comctl32.dll;comdlg32.dll;crypt32.dll;gdi32.dll;ole32.dll;setupapi.dll;shell32.dll;shlwapi.dll;wintrust.dll;ole32.dll;advapi32.dll;gdi32.dll;shell32.dll;comdlg32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>_UNICODE;UNICODE;RUFUS_LOC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -214,11 +217,12 @@
<AdditionalOptions>/utf-8 $(ExternalCompilerOptions) %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>advapi32.lib;comctl32.lib;comdlg32.lib;crypt32.lib;gdi32.lib;ole32.lib;setupapi.lib;shell32.lib;shlwapi.lib;version.lib;wintrust.lib;psapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>advapi32.lib;comctl32.lib;comdlg32.lib;crypt32.lib;gdi32.lib;ole32.lib;setupapi.lib;shell32.lib;shlwapi.lib;wintrust.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<TargetMachine>MachineX64</TargetMachine>
<DelayLoadDLLs>advapi32.dll;comctl32.dll;comdlg32.dll;crypt32.dll;gdi32.dll;ole32.dll;setupapi.dll;shell32.dll;shlwapi.dll;wintrust.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>_UNICODE;UNICODE;RUFUS_LOC;%(PreprocessorDefinitions)</PreprocessorDefinitions>

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: 3.14\n"
"Report-Msgid-Bugs-To: pete@akeo.ie\n"
"POT-Creation-Date: 2021-04-02 12:38+0100\n"
"PO-Revision-Date: 2021-10-23 13:09+0100\n"
"PO-Revision-Date: 2022-01-05 18:36+0000\n"
"Language: it_IT\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -1275,11 +1275,11 @@ msgstr "Operazione fallita"
#.
#. Used when a new update has been downloaded and launched
msgid "Launching new application..."
msgstr "Esecuzione nuova applicazione..."
msgstr "Avvio nuova applicazione..."
#. • MSG_214
msgid "Failed to launch new application"
msgstr "Esecuzione nuova applicazione fallita"
msgstr "Avvio nuova applicazione fallito"
#. • MSG_215
#.
@ -1395,7 +1395,7 @@ msgstr "Blocchi danneggiati: test con pattern 0x%02X"
#.
#. Example: "Partitioning (MBR)..."
msgid "Partitioning (%s)..."
msgstr "Partitionamento (%s)..."
msgstr "Partizionamento (%s)..."
#. • MSG_239
msgid "Deleting partitions (%s)..."
@ -1687,7 +1687,7 @@ msgstr ""
"\n"
"Se hai scaricato questo file da internet, prova a scaricarne una nuova copia e verifica che la checksum MD5 o SHA corrisponda alla checksum ufficiale.\n"
"\n"
"Nota che puoi calcolare in Rufus la checksum facendo clic sul pulsante (✓)."
"Nota che puoi calcolare in Rufus la checksum cliccando sul pulsante (✓)."
#. • MSG_299
msgid "Timestamp validation error"

View file

@ -6395,8 +6395,8 @@ t MSG_209 "Rilevati %d dispositivi"
t MSG_210 "PRONTO"
t MSG_211 "Annullata"
t MSG_212 "Operazione fallita"
t MSG_213 "Esecuzione nuova applicazione..."
t MSG_214 "Esecuzione nuova applicazione fallita"
t MSG_213 "Avvio nuova applicazione..."
t MSG_214 "Avvio nuova applicazione fallito"
t MSG_215 "Aperto %s"
t MSG_216 "Salvato %s"
t MSG_217 "Formattazione: %s"
@ -6420,7 +6420,7 @@ t MSG_234 "Installazione Syslinux %s..."
t MSG_235 "Blocchi danneggiati: %s %d/%d - %0.2f%% (%d/%d/%d errori)"
t MSG_236 "Blocchi danneggiati: test con pattern casuali"
t MSG_237 "Blocchi danneggiati: test con pattern 0x%02X"
t MSG_238 "Partitionamento (%s)..."
t MSG_238 "Partizionamento (%s)..."
t MSG_239 "Eliminazione partizioni (%s)..."
t MSG_240 "La firma per l'aggiornamento scaricato non può essere validata. Questo può significare che il sistema non è correttamente configurato per la validazione della firma o indica un download non corretto.\n\nIl file scaricato verrà eliminato. Per maggiori dettagli consulta il registro eventi."
t MSG_241 "Download: %s"
@ -6480,7 +6480,7 @@ t MSG_294 "Questa versione di Windows non è più supportata da Rufus."
t MSG_295 "Attenzione: versione non ufficiale"
t MSG_296 "Questa versione di Rufus non è stata sviluppata dai suoi sviluppatori ufficiali.\n\nSei sicuro di volerla eseguire?"
t MSG_297 "Rilevato file ISO troncato"
t MSG_298 "Il file ISO selezionato non corrisponde alla dimensione dichiarata: %s dei dati mancanti!\n\nSe hai scaricato questo file da internet, prova a scaricarne una nuova copia e verifica che la checksum MD5 o SHA corrisponda alla checksum ufficiale.\n\nNota che puoi calcolare in Rufus la checksum facendo clic sul pulsante (✓)."
t MSG_298 "Il file ISO selezionato non corrisponde alla dimensione dichiarata: %s dei dati mancanti!\n\nSe hai scaricato questo file da internet, prova a scaricarne una nuova copia e verifica che la checksum MD5 o SHA corrisponda alla checksum ufficiale.\n\nNota che puoi calcolare in Rufus la checksum cliccando sul pulsante (✓)."
t MSG_299 "Errore validazione data/ora"
t MSG_300 "Rufus non può validare che la data/ora del pacchetto aggiornamento sia più recente di quello per l'attuale eseguibile.\n\nPer prevenire possibili scenari di un attacco malware, il processo di aggiornamento è stato interrotto e il file scaricato verrà eliminato. Per maggiori dettagli controlla il registro eventi."
t MSG_301 "Visualizza impostazioni applicazione"

View file

@ -15,5 +15,6 @@ rufus_SOURCES = badblocks.c checksum.c dev.c dos.c dos_locale.c drive.c format.c
rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/syslinux/libinstaller -I$(srcdir)/syslinux/win -I$(srcdir)/libcdio $(AM_CFLAGS) \
-DEXT2_FLAT_INCLUDES=0 -DSOLUTION=rufus
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
# Note: Do not link with -lversion as this will results in DLL sideloading issue. See https://github.com/pbatard/rufus/pull/1838
rufus_LDADD = rufus_rc.o bled/libbled.a ext2fs/libext2fs.a ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a syslinux/win/libwin.a \
libcdio/iso9660/libiso9660.a libcdio/udf/libudf.a libcdio/driver/libdriver.a -lsetupapi -lole32 -lgdi32 -lshlwapi -lcrypt32 -lwintrust -lcomdlg32 -lcomctl32 -luuid -lpsapi -lversion
libcdio/iso9660/libiso9660.a libcdio/udf/libudf.a libcdio/driver/libdriver.a -lsetupapi -lole32 -lgdi32 -lshlwapi -lcrypt32 -lwintrust -lcomdlg32 -lcomctl32 -luuid -lpsapi

View file

@ -281,8 +281,9 @@ rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/sy
-DEXT2_FLAT_INCLUDES=0 -DSOLUTION=rufus
rufus_LDFLAGS = $(AM_LDFLAGS) -mwindows
# Note: Do not link with -lversion as this will results in DLL sideloading issue. See https://github.com/pbatard/rufus/pull/1838
rufus_LDADD = rufus_rc.o bled/libbled.a ext2fs/libext2fs.a ms-sys/libmssys.a syslinux/libfat/libfat.a syslinux/libinstaller/libinstaller.a syslinux/win/libwin.a \
libcdio/iso9660/libiso9660.a libcdio/udf/libudf.a libcdio/driver/libdriver.a -lsetupapi -lole32 -lgdi32 -lshlwapi -lcrypt32 -lwintrust -lcomdlg32 -lcomctl32 -luuid -lpsapi -lversion
libcdio/iso9660/libiso9660.a libcdio/udf/libudf.a libcdio/driver/libdriver.a -lsetupapi -lole32 -lgdi32 -lshlwapi -lcrypt32 -lwintrust -lcomdlg32 -lcomctl32 -luuid -lpsapi
all: all-recursive

View file

@ -858,6 +858,12 @@ void GetGrubVersion(char* buf, size_t buf_size)
img_report.grub2_version[0] = 0;
}
// Linking to version.lib would result in DLL sideloading issues, so we don't
// See https://github.com/pbatard/rufus/pull/1838
PF_TYPE_DECL(WINAPI, DWORD, GetFileVersionInfoSizeW, (LPCWSTR, LPDWORD));
PF_TYPE_DECL(WINAPI, BOOL, GetFileVersionInfoW, (LPCWSTR, DWORD, DWORD, LPVOID));
PF_TYPE_DECL(WINAPI, BOOL, VerQueryValueA, (LPCVOID, LPCSTR, LPVOID, PUINT));
BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan)
{
size_t i, j, size, sl_index = 0;
@ -878,6 +884,10 @@ BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan)
if ((!enable_iso) || (src_iso == NULL) || (dest_dir == NULL))
return FALSE;
PF_INIT_OR_OUT(GetFileVersionInfoSizeW, Version);
PF_INIT_OR_OUT(GetFileVersionInfoW, Version);
PF_INIT_OR_OUT(VerQueryValueA, Version);
scan_only = scan;
if (!scan_only)
spacing = "";
@ -1113,14 +1123,19 @@ out:
VS_FIXEDFILEINFO* ver_info = NULL;
DWORD ver_handle = 0, ver_size;
UINT value_len = 0;
assert(pfGetFileVersionInfoSizeW != NULL);
assert(pfGetFileVersionInfoW != NULL);
assert(pfVerQueryValueA != NULL);
// coverity[swapped_arguments]
if (GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, path) != 0) {
wconvert(path);
assert(wpath != NULL);
size = (size_t)ExtractISOFile(src_iso, "sources/compatresources.dll", path, FILE_ATTRIBUTE_NORMAL);
ver_size = GetFileVersionInfoSizeU(path, &ver_handle);
ver_size = pfGetFileVersionInfoSizeW(wpath, &ver_handle);
if (ver_size != 0) {
buf = malloc(ver_size);
if ((buf != NULL) && GetFileVersionInfoU(path, ver_handle, ver_size, buf) &&
VerQueryValueA(buf, "\\", (LPVOID)&ver_info, &value_len) && (value_len != 0)) {
if ((buf != NULL) && pfGetFileVersionInfoW(wpath, ver_handle, ver_size, buf) &&
pfVerQueryValueA(buf, "\\", (LPVOID)&ver_info, &value_len) && (value_len != 0)) {
if (ver_info->dwSignature == VS_FFI_SIGNATURE) {
img_report.win_version.major = HIWORD(ver_info->dwFileVersionMS);
img_report.win_version.minor = LOWORD(ver_info->dwFileVersionMS);
@ -1132,7 +1147,8 @@ out:
}
free(buf);
}
DeleteFileU(path);
DeleteFileW(wpath);
free(wpath);
}
}
StrArrayDestroy(&config_path);

View file

@ -36,6 +36,7 @@
#include <io.h>
#include <getopt.h>
#include <assert.h>
#include <delayimp.h>
#include "rufus.h"
#include "missing.h"
@ -3191,6 +3192,23 @@ static HANDLE SetHogger(void)
return hogmutex;
}
// For delay-loaded DLLs, use LOAD_LIBRARY_SEARCH_SYSTEM32 to avoid DLL search order hijacking.
FARPROC WINAPI dllDelayLoadHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
if (dliNotify == dliNotePreLoadLibrary) {
// Windows 7 without KB2533623 does not support the LOAD_LIBRARY_SEARCH_SYSTEM32 flag.
// That is is OK, because the delay load handler will interrupt the NULL return value
// to mean that it should perform a normal LoadLibrary.
return (FARPROC)LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
}
return NULL;
}
#if defined(_MSC_VER)
// By default the Windows SDK headers have a `const` while MinGW does not.
const
#endif
PfnDliHook __pfnDliNotifyHook2 = dllDelayLoadHook;
/*
* Application Entrypoint
@ -3202,7 +3220,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
#endif
{
const char* rufus_loc = "rufus.loc";
wchar_t kernel32_path[MAX_PATH];
int i, opt, option_index = 0, argc = 0, si = 0, lcid = GetUserDefaultUILanguage();
int wait_for_mutex = 0;
FILE* fd;
@ -3238,22 +3255,18 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
// Still, we invoke it, for platforms where the following call might actually work...
SetDllDirectoryA("");
// Also, even if you use SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32), you're
// still going to be brought down if you link to wininet.lib or dwmapi.lib, as these two
// perform their DLL invocations before you've had a chance to execute anything.
// Of course, this is not something that security "researchers" will bother looking into
// to try to help fellow developers, when they can get an ego fix by simply throwing
// generic URLs around and deliberately refusing to practice *responsible disclosure*...
// For libraries on the KnownDLLs list, the system will always load them from System32.
// For other DLLs we link directly to, we can delay load the DLL and use a delay load
// hook to load them from System32. Note that, for this to work, something like:
// 'somelib.dll;%(DelayLoadDLLs)' must be added to the 'Delay Loaded Dlls' option of
// the linker properties in Visual Studio (which means this won't work with MinGW).
// For all other DLLs, use SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32).
// Finally, we need to perform the whole gymkhana below, where we can't call on
// SetDefaultDllDirectories() directly, because Windows 7 doesn't have the API exposed.
GetSystemDirectoryW(kernel32_path, ARRAYSIZE(kernel32_path));
wcsncat(kernel32_path, L"\\kernel32.dll", ARRAYSIZE(kernel32_path) - wcslen(kernel32_path) - 1);
// NB: Because kernel32 should already be loaded, what we do above to ensure that we
// (re)pick the system one is mostly unnecessary. But since for a hammer everything is a
// nail... Also, no, Coverity, we never need to care about freeing kernel32 as a library.
// Also, no, Coverity, we never need to care about freeing kernel32 as a library.
// coverity[leaked_storage]
pfSetDefaultDllDirectories = (SetDefaultDllDirectories_t)
GetProcAddress(LoadLibraryW(kernel32_path), "SetDefaultDllDirectories");
GetProcAddress(LoadLibraryW(L"kernel32.dll"), "SetDefaultDllDirectories");
if (pfSetDefaultDllDirectories != NULL)
pfSetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32);

View file

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 3.18.1856"
CAPTION "Rufus 3.18.1859"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@ -395,8 +395,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,18,1856,0
PRODUCTVERSION 3,18,1856,0
FILEVERSION 3,18,1859,0
PRODUCTVERSION 3,18,1859,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -414,13 +414,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "3.18.1856"
VALUE "FileVersion", "3.18.1859"
VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2021 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-3.18.exe"
VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "3.18.1856"
VALUE "ProductVersion", "3.18.1859"
END
END
BLOCK "VarFileInfo"

View file

@ -577,7 +577,7 @@ void SetSectionHeaders(HWND hDlg)
memset(wtmp, 0, sizeof(wtmp));
GetWindowTextW(hCtrl, wtmp, ARRAYSIZE(wtmp) - 4);
wlen = wcslen(wtmp);
assert(wlen < ARRAYSIZE(wtmp - 2));
assert(wlen < ARRAYSIZE(wtmp) - 2);
wtmp[wlen++] = L' ';
wtmp[wlen++] = L' ';
SetWindowTextW(hCtrl, wtmp);