/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Copy instead of passing UniquePtr<wchar_t, LocalFreeDeleter> back to // the caller. int sidLen = ::lstrlenW(sid.get()) + 1; auto outSid = MakeUnique<wchar_t[]>(sidLen);
memcpy(outSid.get(), sid.get(), sidLen * sizeof(wchar_t));
return outSid;
}
/* * Create the string which becomes the input to the UserChoice hash. * * @see GenerateUserChoiceHash() for parameters. * * @return The formatted string, nullptr on failure. * * NOTE: This uses the format as of Windows 10 20H2 (latest as of this writing), * used at least since 1803. * There was at least one older version, not currently supported: On Win10 RTM * (build 10240, aka 1507) the hash function is the same, but the timestamp and * User Experience string aren't included; instead (for protocols) the string * ends with the exe path. The changelog of SetUserFTA suggests the algorithm * changed in 1703, so there may be two versions: before 1703, and 1703 to now.
*/ static UniquePtr<wchar_t[]> FormatUserChoiceString(constwchar_t* aExt, constwchar_t* aUserSid, constwchar_t* aProgId,
SYSTEMTIME aTimestamp) {
aTimestamp.wSecond = 0;
aTimestamp.wMilliseconds = 0;
// This string is built into Windows as part of the UserChoice hash algorithm. // It might vary across Windows SKUs (e.g. Windows 10 vs. Windows Server), or // across builds of the same SKU, but this is the only currently known // version. There isn't any known way of deriving it, so we assume this // constant value. If we are wrong, we will not be able to generate correct // UserChoice hashes. constwchar_t* userExperience =
L"User Choice set via Windows User Experience "
L"{D18B6DD5-6124-4341-9318-804003BAFA0B}";
constwchar_t* userChoiceFmt =
L"%s%s%s"
L"%08lx"
L"%08lx"
L"%s"; int userChoiceLen = _scwprintf(userChoiceFmt, aExt, aUserSid, aProgId,
fileTime.dwHighDateTime,
fileTime.dwLowDateTime, userExperience);
userChoiceLen += 1; // _scwprintf does not include the terminator
// @return The MD5 hash of the input, nullptr on failure. static UniquePtr<DWORD[]> CNG_MD5(constunsignedchar* bytes, ULONG bytesLen) {
constexpr ULONG MD5_BYTES = 16;
constexpr ULONG MD5_DWORDS = MD5_BYTES / sizeof(DWORD);
UniquePtr<DWORD[]> hash;
BCRYPT_ALG_HANDLE hAlg = nullptr; if (NT_SUCCESS(::BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_MD5_ALGORITHM,
nullptr, 0))) {
BCRYPT_HASH_HANDLE hHash = nullptr; // As of Windows 7 the hash handle will manage its own object buffer when // pbHashObject is nullptr and cbHashObject is 0. if (NT_SUCCESS(
::BCryptCreateHash(hAlg, &hHash, nullptr, 0, nullptr, 0, 0))) { // BCryptHashData promises not to modify pbInput. if (NT_SUCCESS(::BCryptHashData(hHash, const_cast<unsignedchar*>(bytes),
bytesLen, 0))) {
hash = MakeUnique<DWORD[]>(MD5_DWORDS); if (!NT_SUCCESS(::BCryptFinishHash(
hHash, reinterpret_cast<unsignedchar*>(hash.get()),
MD5_DWORDS * sizeof(DWORD), 0))) {
hash.reset();
}
}
::BCryptDestroyHash(hHash);
}
::BCryptCloseAlgorithmProvider(hAlg, 0);
}
return hash;
}
// @return The input bytes encoded as base64, nullptr on failure. static UniquePtr<wchar_t[]> CryptoAPI_Base64Encode(constunsignedchar* bytes,
DWORD bytesLen) {
DWORD base64Len = 0; if (!::CryptBinaryToStringW(bytes, bytesLen,
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
nullptr, &base64Len)) { return nullptr;
} auto base64 = MakeUnique<wchar_t[]>(base64Len); if (!::CryptBinaryToStringW(bytes, bytesLen,
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
base64.get(), &base64Len)) { return nullptr;
}
return base64;
}
staticinline DWORD WordSwap(DWORD v) { return (v >> 16) | (v << 16); }
/* * Generate the UserChoice Hash. * * This implementation is based on the references listed above. * It is organized to show the logic as clearly as possible, but at some * point the reasoning is just "this is how it works". * * @param inputString A null-terminated string to hash. * * @return The base64-encoded hash, or nullptr on failure.
*/ static UniquePtr<wchar_t[]> HashString(constwchar_t* inputString) { auto inputBytes = reinterpret_cast<constunsignedchar*>(inputString); int inputByteCount = (::lstrlenW(inputString) + 1) * sizeof(wchar_t);
// Compute an MD5 hash. md5[0] and md5[1] will be used as constant multipliers // in the scramble below. auto md5 = CNG_MD5(inputBytes, inputByteCount); if (!md5) { return nullptr;
}
// The following loop effectively computes two checksums, scrambled like a // hash after every DWORD is added.
// Constant multipliers for the scramble, one set for each DWORD in a block. const DWORD C0s[DWORDS_PER_BLOCK][5] = {
{md5[0] | 1, 0xCF98B111uL, 0x87085B9FuL, 0x12CEB96DuL, 0x257E1D83uL},
{md5[1] | 1, 0xA27416F5uL, 0xD38396FFuL, 0x7C932B89uL, 0xBFA49F69uL}}; const DWORD C1s[DWORDS_PER_BLOCK][5] = {
{md5[0] | 1, 0xEF0569FBuL, 0x689B6B9FuL, 0x79F8A395uL, 0xC3EFEA97uL},
{md5[1] | 1, 0xC31713DBuL, 0xDDCD1F0FuL, 0x59C3AF2DuL, 0x35BD1EC9uL}};
// The checksums.
DWORD h0 = 0;
DWORD h1 = 0; // Accumulated total of the checksum after each DWORD.
DWORD h0Acc = 0;
DWORD h1Acc = 0;
for (int i = 0; i < blockCount; ++i) { for (size_t j = 0; j < DWORDS_PER_BLOCK; ++j) { const DWORD* C0 = C0s[j]; const DWORD* C1 = C1s[j];
/* * NOTE: The passed-in current user SID is used here, instead of getting the SID * for the owner of the key. We are assuming that this key in HKCU is owned by * the current user, since we want to replace that key ourselves. If the key is * owned by someone else, then this check will fail; this is ok because we would * likely not want to replace that other user's key anyway.
*/
CheckUserChoiceHashResult CheckUserChoiceHash(constwchar_t* aExt, constwchar_t* aUserSid) { auto keyPath = GetAssociationKeyPath(aExt); if (!keyPath) { return CheckUserChoiceHashResult::ERR_OTHER;
}
for (size_t i = 0; i < std::size(exts); ++i) { switch (CheckUserChoiceHash(exts[i], userSid.get())) { case CheckUserChoiceHashResult::OK_V1: break; case CheckUserChoiceHashResult::ERR_MISMATCH: case CheckUserChoiceHashResult::ERR_OTHER: returnfalse;
}
}
returntrue;
}
UniquePtr<wchar_t[]> FormatProgID(constwchar_t* aProgIDBase, constwchar_t* aAumi) { constwchar_t* progIDFmt = L"%s-%s"; int progIDLen = _scwprintf(progIDFmt, aProgIDBase, aAumi);
progIDLen += 1; // _scwprintf does not include the terminator
auto progID = MakeUnique<wchar_t[]>(progIDLen);
_snwprintf_s(progID.get(), progIDLen, _TRUNCATE, progIDFmt, aProgIDBase,
aAumi);
nsresult GetMsixProgId(constwchar_t* assoc, UniquePtr<wchar_t[]>& aProgId) { // Retrieve the registry path to the package from registry path: // clang-format off // HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages\[Package Full Name]\App\Capabilities\[FileAssociations | URLAssociations]\[File | URL] // clang-format on
constwchar_t* assocPathFmt =
LR"(Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages\%s\%s)"; int assocPathLen = _scwprintf(assocPathFmt, pfn.get(), assocSuffix);
assocPathLen += 1; // _scwprintf does not include the terminator
auto assocPath = MakeUnique<wchar_t[]>(assocPathLen);
_snwprintf_s(assocPath.get(), assocPathLen, _TRUNCATE, assocPathFmt,
pfn.get(), assocSuffix);
LSTATUS ls;
// Retrieve the package association's ProgID, always in the form `AppX[32 hash // characters]`. const size_t appxProgIdLen = 37; auto progId = MakeUnique<wchar_t[]>(appxProgIdLen);
DWORD progIdLen = appxProgIdLen * sizeof(wchar_t);
ls = ::RegGetValueW(HKEY_CLASSES_ROOT, assocPath.get(), assoc, RRF_RT_REG_SZ,
nullptr, (LPBYTE)progId.get(), &progIdLen); if (ls != ERROR_SUCCESS) { return NS_ERROR_WDBA_NO_PROGID;
}
aProgId.swap(progId);
return NS_OK;
}
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.