LIMB
Вход Регистрация

тема

UCPD.sys — драйвер для защиты Microsoft от юзеров

MishkaUmka · · 1 ответов · 541 просмотров

Введение

В Windows всегда остро стояла проблема bloatware, менявшего браузер по умолчанию. Для противодействия этому Microsoft развернули UCPD (User Choice Protection Driver). В этой статье мы разберёмся с методами обхода UCPD и его обновлениями, исправляющими эти обходы.

Предыстория

Браузер по умолчанию хранится в реестре:
Code:
HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice

HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice

Раньше поменять значение там мог любой желающий без какого-либо подтверждения или даже оповещения пользователя, что активно абузилось различным bloatware.

В Windows 8 же Microsoft решили, что с этим неплохо было бы бороться — для этого Microsoft добавили новое значение реестра с именем Hash, в котором хранится хэш браузера по умолчанию, генерируемым приложением настроек. То есть если хэш был неправильным, браузер по умолчанию не изменится.
Не удалось прогрузить медиа


Сам алгоритм хэша был закрытым и реализовывался в приложении настроек. Помимо этого, реализация хэш-алгоритма включала в себя временную метку и пользовательский SID, что исключало возможность просчитать его заранее.

Но, как нетрудно догадаться, секрет дополнительно быстро раскрыли, и уже в 2017 году ресёрчер Christoph Kolbicz разревёрсил алгоритм и написал свою CLI-утилиту SetUserFTA, позволяющей менять расширения файла и браузер по умолчанию. Позже, в 2021 году Firefox добавили аналогичную возможность, позволяющую сделать его браузером по умолчанию. Прикладываю исходный код оттуда с их имплементацией хэш-алгоритма:

C++:
// https://github.com/mozilla-firefox/firefox/blob/main/toolkit/mozapps/defaultagent/SetDefaultBrowser.cpp#L343

/*
 * Set an association with a UserChoice key
 *
 * Removes the old key, creates a new one with ProgID and Hash set to
 * enable a new asociation.
 *
 * @param aExt      File type or protocol to associate
 * @param aSid      Current user's string SID
 * @param aProgID   ProgID to use for the asociation
 * @param inMsix    Are we running from in an msix package?
 * @param aRegRename True if we should rename registry keys to prevent
 * interference from kernel drivers attempting to lock subkeys.
 *
 * @return true if successful, false on error.
 */
static bool SetUserChoice(const wchar_t* aExt, const wchar_t* aSid,
                          const wchar_t* aProgID, bool inMsix,
                          const bool aRegRename) {
  if (inMsix) {
    LOG_ERROR_MESSAGE(
        L"SetUserChoice should not be called on MSIX builds. Call "
        L"SetDefaultExtensionHandlersUserChoiceImplMsix instead.");
    return false;
  }

  SYSTEMTIME hashTimestamp;
  ::GetSystemTime(&hashTimestamp);
  auto hash = GenerateUserChoiceHash(aExt, aSid, aProgID, hashTimestamp);
  if (!hash) {
    return false;
  }

  // The hash changes at the end of each minute, so check that the hash should
  // be the same by the time we're done writing.
  const ULONGLONG kWriteTimingThresholdMilliseconds = 1000;
  // Generating the hash could have taken some time, so start from now.
  SYSTEMTIME writeEndTimestamp;
  ::GetSystemTime(&writeEndTimestamp);
  if (!AddMillisecondsToSystemTime(writeEndTimestamp,
                                   kWriteTimingThresholdMilliseconds)) {
    return false;
  }
  if (!CheckEqualMinutes(hashTimestamp, writeEndTimestamp)) {
    LOG_ERROR_MESSAGE(
        L"Hash is too close to expiration, sleeping until next hash.");
    ::Sleep(kWriteTimingThresholdMilliseconds * 2);

    // For consistency, use the current time.
    ::GetSystemTime(&hashTimestamp);
    hash = GenerateUserChoiceHash(aExt, aSid, aProgID, hashTimestamp);
    if (!hash) {
      return false;
    }
  }

  // We're outside of an MSIX package and can use the Win32 Registry API.
  return SetUserChoiceRegistry(aExt, aProgID, aRegRename, std::move(hash));
}

C++:
// https://github.com/mozilla-firefox/firefox/blob/main/browser/components/shell/WindowsUserChoice.cpp#L298

/* 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(const wchar_t* aExt,
                                                   const wchar_t* aUserSid,
                                                   const wchar_t* aProgId,
                                                   SYSTEMTIME aTimestamp) {
  aTimestamp.wSecond = 0;
  aTimestamp.wMilliseconds = 0;

  FILETIME fileTime = {0};
  if (!::SystemTimeToFileTime(&aTimestamp, &fileTime)) {
    return nullptr;
  }

  // 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.
  const wchar_t* userExperience =
      L"User Choice set via Windows User Experience "
      L"{D18B6DD5-6124-4341-9318-804003BAFA0B}";

  const wchar_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

  auto userChoice = MakeUnique<wchar_t[]>(userChoiceLen);
  _snwprintf_s(userChoice.get(), userChoiceLen, _TRUNCATE, userChoiceFmt, aExt,
               aUserSid, aProgId, fileTime.dwHighDateTime,
               fileTime.dwLowDateTime, userExperience);

  ::CharLowerW(userChoice.get());

  return userChoice;
}

// @return The MD5 hash of the input, nullptr on failure.
static UniquePtr<DWORD[]> CNG_MD5(const unsigned char* 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<unsigned char*>(bytes),
                                      bytesLen, 0))) {
        hash = MakeUnique<DWORD[]>(MD5_DWORDS);
        if (!NT_SUCCESS(::BCryptFinishHash(
                hHash, reinterpret_cast<unsigned char*>(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(const unsigned char* 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;
}

static inline 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(const wchar_t* inputString) {
  auto inputBytes = reinterpret_cast<const unsigned char*>(inputString);
  int inputByteCount = (::lstrlenW(inputString) + 1) * sizeof(wchar_t);

  constexpr size_t DWORDS_PER_BLOCK = 2;
  constexpr size_t BLOCK_SIZE = sizeof(DWORD) * DWORDS_PER_BLOCK;
  // Incomplete blocks are ignored.
  int blockCount = inputByteCount / BLOCK_SIZE;

  if (blockCount == 0) {
    return nullptr;
  }

  // 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];

      DWORD input;
      memcpy(&input, &inputBytes[(i * DWORDS_PER_BLOCK + j) * sizeof(DWORD)],
             sizeof(DWORD));

      h0 += input;
      // Scramble 0
      h0 *= C0[0];
      h0 = WordSwap(h0) * C0[1];
      h0 = WordSwap(h0) * C0[2];
      h0 = WordSwap(h0) * C0[3];
      h0 = WordSwap(h0) * C0[4];
      h0Acc += h0;

      h1 += input;
      // Scramble 1
      h1 = WordSwap(h1) * C1[1] + h1 * C1[0];
      h1 = (h1 >> 16) * C1[2] + h1 * C1[3];
      h1 = WordSwap(h1) * C1[4] + h1;
      h1Acc += h1;
    }
  }

  DWORD hash[2] = {h0 ^ h1, h0Acc ^ h1Acc};

  return CryptoAPI_Base64Encode(reinterpret_cast<const unsigned char*>(hash),
                                sizeof(hash));
          }

UniquePtr<wchar_t[]> GenerateUserChoiceHash(const wchar_t* aExt,
                                            const wchar_t* aUserSid,
                                            const wchar_t* aProgId,
                                            SYSTEMTIME aTimestamp) {
  auto userChoice = FormatUserChoiceString(aExt, aUserSid, aProgId, aTimestamp);
  if (!userChoice) {
    return nullptr;
  }
  return HashString(userChoice.get());
}

Появление UCPD.sys

Microsoft такой расклад не понравился, поэтому в марте 2024 года они ввели героя нашей статьи — UCPD.sys, который на уровне ядра защищает ключи реестра с браузером по умолчанию (а именно ключи UserChoice и UserChoiceLatest для следующих протоколов и расширений: http, https, .pdf, .htm, .html, .doc, .docx, .xls, .xlsx, .ppt, .pptx). Помимо этого, драйвер защищает несколько ключей, связанных с UI, а именно:
  • SOFTWARE\Microsoft\Windows\CurrentVersion\Feeds (Значение: ShellFeedsTaskbarViewMode)
  • SOFTWARE\Microsoft\Windows\CurrentVersion\Feeds (Значение: IsFeedsAvailable)
  • SOFTWARE\Microsoft\Windows\CurrentVersion\NandI (Значение: Control)
  • SOFTWARE\Policies\Microsoft\Windows\Windows Feeds(Значение : EnableFeeds)
  • Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced(Значение: TaskbarDa)
  • SOFTWARE\Policies\Microsoft\Dsh (Значение: AllowNewsAndInterests)
  • Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband(Значение: Favorites)
  • Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband (Значение: FavoritesResolve)
  • Software\Microsoft\Windows\Shell\BrandedKey(Значение: BrandedKeyChoiceType)
  • Software\Microsoft\Windows\Shell\BrandedKey(Значение: AppAumid)
  • Software\Microsoft\Windows\CurrentVersion\SearchSettings
  • Software\Microsoft\Windows\CurrentVersion\Search
  • Software\Microsoft\Windows\CurrentVersion\Feeds
Не удалось прогрузить медиа

Не удалось прогрузить медиа


Ещё UCPD защищает значение DeviceRegion в HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\DeviceRegion — как вы, возможно, слышали, Microsoft разрешила удаление браузера Edge, но только для стран ЕС, в других странах удалить браузер Edge нельзя. Именно поэтому UCPD защищает это поле (оно специально проверяется при попытке удалить Edge через MSIEXEC или подобные утилиты), чтоб нельзя было поставить страну ЕС и удалить Edge.

Gunnar Haslinger первым выложил расследование, посвящённое скрытому драйверу, проанализировав его функционал. Вкратце, драйвер регистрирует колбек реестра через функцию CmRegisterCallbackEx и через него блокирует доступ к защищаемым ключам не доверенным процессам, запрещая изменение, создание, удаление и другие операции.
Не удалось прогрузить медиа


Критерии для доверенного процесса следующие:
  1. Подписан Microsoft;
  2. И при этом не в списке стандартных утилитных программ по типу: reg.exe, regedit.exe, powershell.exe, rundll32.exe, wscript.exe, cscript.exe, cmd.exe, pwsh.exe, InfDefaultInstall.exe, WmiPrvSE.exe.
Не удалось прогрузить медиа

Не удалось прогрузить медиа

Не удалось прогрузить медиа


И зачем это всё?

Теперь установить браузер по умолчанию без отключения этого драйвера (смотрите далее) можно только в приложении современного меню настроек (Windows Settings):
Не удалось прогрузить медиа


Но при этом сам Edge технически поменять значение браузера по умолчанию может, так как он подписан Microsoft и не находится в чёрном списке утилит. Но, как утверждает ресёрчер Xusheng Li, Edge этим не пользуется и, когда пользователь в нём хочет назначить его браузером по умолчанию, перекидывает его в приложении настроек, что является рекомендуемым методом.

Да, в Microsoft создали драйвер для защиты своего браузера и офисных программ. Думаете, это всё? То были лишь цветочки, ягодки впереди.

Скажи "нет" инжектам

Xusheng Li был очень удивлён, увидив эти строки в драйвере:
Не удалось прогрузить медиа


Что у вас здесь происходит?! — возник у него закономерный вопрос. Оказалось, драйвер регистрирует колбек для отслеживания создания процессов через PsSetCreateProcessNotifyRoutineEx. Этот колбек проверяет, путь до изображения на наличие следующих подстрок:
  • \safemon\
  • \kingsoft\
  • \opera\
  • \msedge.exe
  • \explorer.exe
Если подстрока найдена, драйвер классифицирует процесс и присваивает ему следующие возможные значения:
  1. Тип 0x4 для msedge.exe и explorer.exe;
  2. Тип 0x8: исполняемый файл называется kwsprotect64.exe, kxescore.exe или 360Tray.exe и подписан кем-то из этого списка: Beijing Kingsoft Security software Co.,Ltd, Beijing Qihu Technology Co., Ltd., или Zhuhai Juntian Electronic Technology Co., Ltd.;
  3. Тип 0x10, если подписан Opera Norway AS — Opera браузер.
Классификация сохраняется в AVL таблицу (аналог std::map) вида процесс -> тип. Основная логика колбека представлена на скриншоте ниже. Внутри поля element.type сохраняется упомянутая выше категория процесса.
Не удалось прогрузить медиа


Информация о типе процесса используется в других регистрируемых драйверам колбеках, создающихся через функцию ObRegisterCallbacks. Колбеки регистрируются для следующих объектов: PsThreadType, PsProcessType и ExDesktopObjectType. Все колбеки являются PreOperation, то есть могут анализировать запрос и менять его.
Не удалось прогрузить медиа


PreOperation колбеки для процессов и потоков имеют общий адрес обработчика: он получают данные о вызывающем процессе, который хочет открыть хэндл, и запрашиваемом процессе, хэндл которого хотят открыть (у потоков проверяется процесс, внутри которого поток запущен). Через PID он они достают запись в AVL таблице, получая тип процесса. После этого он проверяет следующее:
  1. Целевой процесс является explorer.exe или msedge.exe (тип 0x4);
  2. Запрашивающий процесс имеет тип 0x8 (360Tray.exe/kxescore.exe/kwsprotect64.exe).
Если оба условия соблюдаются, производится модификация маски доступа:
  1. Для объектов PsProcessType удаляется маска 0x28 (PROCESS_VM_OPERATION | PROCESS_VM_WRITE)
  2. Для объектов PsThreadType удаляется маска 0x10 (THREAD_SET_CONTEXT)
Не удалось прогрузить медиа


Теперь всё встало на свои места — это предотвращает внедрение кода из 360Tray.exe/kxescore.exe/kwsprotect64.exe в explorer.exe и msedge.exe! Зачем UCPD это нужно? Единственное объяснение – китайцы пытаются обойти UCPD, внедряя код в explorer.exe/msedge.exe, поскольку эти два процесса могут изменять ключ реестра для браузера по умолчанию. А Microsoft эта идея не понравилась, поэтому они ужесточили защиту, напрямую заблокировав нарушителей.

Второй колбек для ExDesktopObjectType проверяет, относится ли текущий процесс к категории 0x10, то есть к браузеру Opera. Код прост — он просто удаляет права доступа 0x20 (с помощью &= 0xffffffdf).
Не удалось прогрузить медиа


Флаг 0x20 в контексте безопасности объектов рабочего стола имеет название DESKTOP_JOURNALPLAYBACK. Это разрешение даёт возможность воспроизводить ранее записанные пользовательские действия с клавиатуры и мыши, что может быть использовано для взаимодействия с пользовательским интерфейсом без ведома.

Похоже, Opera использовала это, чтоб обойти UCPD, поэтому Microsoft решила забанить им эту возможность.

UCPDMgr.exe (UCPD Менеджер)

Защита хэндлов работает только при наличии глобального флага 0x100; если флага нет, колбеки диспетчера объектов ничего не будут делать.
Не удалось прогрузить медиа


Значение глобальных флагов берётся из реестра HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Srevices\UCPD со значением FeatureV2.

Это значение задаётся программой UCPDMgr.exe функцией SetUCPDStatus
Не удалось прогрузить медиа


Как мы можем видеть, флаг 0x100 выставляется в том случае, если включена wil::Feature<struct __WilFeatureTraits_Feature_UCPD_ANTIINJECTION>::GetImpl::impl::~impl. И название ANTIINJECTION (анти-инжект) — ярое тому подтверждение!

Также в глаза бросается значение 0x80, скрывающееся под именем UCPD_ANTIUIA. ANTIUIA (anti-UI attack) подразумевает защиту пользовательского интерфейса от атаки, то есть тот самый колбек объекта рабочего стола.
Не удалось прогрузить медиа


Условие if ((enabled_feature).b s< 0 действительно проверяет, установлен ли бит 0x80, поскольку оно берёт младший байт enabled_feature и проверяет, является ли он отрицательным, что эквивалентно проверке, установлен ли старший бит.

Также присутствуют несколько других интересных строк:
0x200 – UCPD_NEWDENYLIST, что позволяет добавить новый список запрещенных ключей реестра.
0x800 – UCPD_RENAME_ATTACK, который защищает от атаки, переименовывающей определённые ключи.
0x8 – UCPD_WSB, который защищает Windows search bar (строку поиска).
0x10 – UCPD_TASKBAR, который защищает панель задач.

Ещё один интересный момент: когда срабатывает защита хэндлов, в ETW журнале генерируется событие.

Стэк трейсинг

В марте 2025 года драйвер UCPD обновился до четвёртой (4.0) версии.
В обновлении завезли новый список заблокированных компаний (добавилось около 16 новых).
Не удалось прогрузить медиа

UCPD снова закручивает гайки: этот список используется, когда бит 0x400 установлен в реестре. А в новых строках UCPDMgr.exe он сопоставлен как UCPD_BACKTRACE. И да, UCPD теперь анализирует стэк, чтобы определить, следует ли разрешать операцию с реестром!

Короче говоря, когда функция обратного вызова реестра обнаруживает вызов SetValueKey/DeleteKey/RenameKey для защищённого ключа, в дополнение к существующей IsTrustedProcess проверке, теперь проверяется, является ли запрашивающий процесс SystemSettings.exe (официальным приложением для установки браузера по умолчанию). И если да, то анализируется трассировка стека с помощью RtlWalkFrameChain. Если какой-либо из фреймов содержит модуль, подписанный заблокированным поставщиком, то операция отклоняется.
Не удалось прогрузить медиа


Для эффективного выполнения этой задачи UCPD теперь, помимо PsSetCreateProcessNotifyRoutineEx, использует PsSetLoadImageNotifyRoutine для сохранения списка активных процессов и загруженных в них модулей. Результаты сохраняются в таблице AVL вместе с некоторыми метаданными.
Не удалось прогрузить медиа


Как отключить?
После того, как я узнал об этом драйвере, у меня сразу появилось желание его отключить. Загруженный UCPD драйвер нельзя выгрузить, так как у него отсутствует зарегистрированная DriverUnload функция, но можно отключить его автоматическую загрузку через sc.exe или через PowerShell. Если мы это сделаем и перезагрузим компьютер, драйвер не будет загружен, но это только до следующего перезапуска. Почему?

Всё дело в том, что в библиотеке планировщика заданий в каталоге Microsoft\Windows\AppxDeploymentClient притаилась таска UCPD velocity с триггером на вход пользователя в систему.
Не удалось прогрузить медиа


То есть когда вы вводите пароль на экране блокировки Windows, эта таска запускает упомянутый ранее UCPDMgr.exe, который, помимо всего прочего, проверяет состояние драйвера и снова добавляет его в автозагрузку при необходимости.

Поэтому, если мы хотим полностью избавиться от драйвера, нужно отключить и эту таску тоже. Открываем PowerShell или Cmd от имени администратора и пишем следующие две команды:
PowerShell:
Bash:
Set-Service -Name UCPD -StartupType Disabled
Disable-ScheduledTask -TaskName "\Microsoft\Windows\AppxDeploymentClient\UCPD velocity"
Или CMD:
Bash:
sc config UCPD start=disabled
schtasks /change /Disable /TN "\Microsoft\Windows\AppxDeploymentClient\UCPD velocity"

После чего ребутаем компьютер и наслаждаемся результатом. Если вам вдруг понадобится снова включить этот драйвер, снова добавьте его в автозагрузку и включите таску UCPD velocity

Вместо заключения

На этом всё, уважаемые читатели. Надеюсь, вам понравился собранный мною материал. Я думаю, очевидно, что настоящая цель создания этого драйвера не защита пользователя от bloatware (если бы Microsoft реально об этом беспокоились, разрешили бы удалять браузер Edge во всех регионах, лол), а защита коммерческих интересов компании.

Microsoft основной доход получает не от лицензий на Windows (потому что большинство покупают компы с предустановленной Windows, где лицензия уже включена в стоимость, а те, кто могут сами накатить Windows, смогут и её активировать, поэтому Microsoft вообще никак не борится с пиратами), как можно подумать, а от своих облачных и ИИ-сервисов по типу Azure, Copilot, Office 365 и других. Чтоб на их сервисы был спрос, Microsoft завлекает пользователя в свою экосистему, чтоб он был максимально к ней привязан и привязывал к ней как можно большее число людей.

Вот представьте, что вы приходите работать в компанию, где все документы хранятся в облаке Office 365, вам придётся либо тоже пользоваться Office 365, либо уволиться. По такой же схеме работает Apple, привязывая каждый пользовательский чих к iCloud и экосистеме своих устройств, чтоб брать больше денег за комиссию в App Store. И Google тоже любители платить баснословные деньги за то, чтоб быть поисковой системой по умолчанию в чужом браузере, чтоб привязывать уже к своей экосистеме.

Именно поэтому Microsoft так яростно старается защищать свой браузер, ведь несмотря на то, что война браузеров уже прошла, всё равно свои сервисы надо продвигать. Именно поэтому Microsoft абсолютно плевать на ваш опыт пользования их операционной системой, в которую они будут и дальше пропихивать завайбкоженный тормозящий мусор поверх 40-летнего легаси в ядре и загружать это через обновления, каждый раз ломающие систему.

Моё мнение — нужно слать такие компании на три буквы и пользоваться Linux (MacOS не подойдёт, потому что им владеет такая же жадная компания). Но, к сожалению, не каждый может себе это позволить по разным причинам, поэтому остаётся только грезить об этом...
👍42 🔥4 😁7 😮8 🤔11
Чисто по факту, разбор хороший.. и затрагивает тему подобных "низкоуровневых" исследований. Да и основные этапы те же, "пробуем с лолбинов? не взяло", "идем глубже", "изучаем, что и где цепляется".  Отдаленно напоминает копания с целью найти новую системную дыру, знакомит с процессом.

Что касается микромягких, Edge в целом один из основных ее монетизаторов, отправляющий буквально каждый ваш чих на сервера, кто посидел разок с HTTP Debugger, тот поймет. Виндовс давно ушел от модели платы за лицензию, с пиратами даже не борется, берет за счет серого маркетинга: собирает сведения для таргетированных реклам и монетизации.
А что касается самого эджа, так то и winutils от исследователя ChrisTitus(вроде правильно написал) справляются с задачкой, спокойно удаляют эдж и меняют браузер по умолчанию. Но за сам разбор определенно респект, осталось услышать мнение нашего второго проверяющего, @cybergangster. 

Вам лишь скажу, что было бы интересно увидеть от вас подобного уровня разборы, но уже с более темными на них целями =)
Хорошие эксплойты всем и всегда нужны.
👍5 🔥1

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.