Салют, Хабр! Я думаю, каждый из вас знаком или, по крайней мере, слышал о такой прекрасной утилите как NoDPI написанной на питоне (большое спасибо @Lord_of_Rings!). Сегодня я хочу представить вам (почти) свою разработку, не требующую ни питона ни прокси. Мы будем патчить прямо на диске библиотеку chrome.dll - входяющую в пакет Chrome на Windows и лежащую в директории "C:\Program Files\Google\Chrome\Application\140.0.7339.208\chrome.dll". Цифры могут меняться в зависимости от версии. Данный патч занимает всего 8 байт и после него у нас появится YouTube.

Сначала скачаем https://boringssl.googlesource.com/boringssl это библиотека TLS/SSL, на которой построен Chrome/Chrominium.

Нас будет интересовать файл s3_both.cc вот в этом месте:

bool tls_add_message(SSL *ssl, Array<uint8_t> msg) {
  // Pack handshake data into the minimal number of records. This avoids
  // unnecessary encryption overhead, notably in TLS 1.3 where we send several
  // encrypted messages in a row. For now, we do not do this for the null
  // cipher. The benefit is smaller and there is a risk of breaking buggy
  // implementations.
  //
  // TODO(crbug.com/374991962): See if we can do this uniformly.
  Span<const uint8_t> rest = msg;
  if (!SSL_is_quic(ssl) && ssl->s3->aead_write_ctx->is_null_cipher()) {
    while (!rest.empty()) {
      Span<const uint8_t> chunk = rest.subspan(0, ssl->max_send_fragment);
      rest = rest.subspan(chunk.size());

      if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, chunk)) {
        return false;
      }
    }
  } else {

Можно легко увидеть, что здесь пакет нарезается на chunk размером max_send_fragment и далее из этого формируются пакеты с заголовком SSL3_RT_HANDSHAKE (равно 22). Кто следил как устроен NoDPI на питоне, то там всё тоже самое, но нарезка происходит не фиксированной длины, а случайным образом. Примерно так:

        while data:
            chunk_len = random.randint(1, len(data))
            parts.append(
                bytes.fromhex("160304")
                + chunk_len.to_bytes(2, "big")
                + data[:chunk_len]
            )
            data = data[chunk_len:]

Прекрасно, модифицируем код s3_both.cc, чтобы уменьшить размер max_send_fragment (по умолчанию он равен 16384). Таким образом пакеты начнут дробиться на кусочки. Случайность делать не будем, как выяснилось, она не нужна.

См код ниже. Новыми являются первые две строчки, остальной код не трогаем.

  ssl->max_send_fragment=0x3e8;
  if (1) {
    while (!rest.empty()) {
      Span<const uint8_t> chunk = rest.subspan(0, ssl->max_send_fragment);
      rest = rest.subspan(chunk.size());

      if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, chunk)) {
        return false;
      }
    }
  } else {

А вот как это выглядит на языке Ассемблера.

Было:

00D02D21: 4883B99800000000      cmp         q,[rcx][000000098],0 ;!SSL_is_quic(ssl)
00D02D29: 0F85B9000000          jnz         000D02DE8
00D02D2F: 488B4730              mov         rax,[rdi][030] ;ssl->s3->aead_write_ctx->is_null_cipher()
00D02D33: 488B8010010000        mov         rax,[rax][000000110]
00D02D3A: 48833800              cmp         q,[rax],0
00D02D3E: 0F85A4000000          jnz         000D02DE8
00D02D44: 4D85E4                test        r12,r12
00D02D47: 0F8439010000          jz          000D02E86
00D02D4D: 440FB74F10            movzx       r9d,w,[rdi][010] ;ssl->max_send_fragment

Стало:

00D02D21: 66C74710E803          mov         w,[rdi][010],003E8 ;max_send_fragment=0x3e8
00D02D27: EB1B                  jmps        000D02D44   ; if (1) --->
00D02D29: 0F85B9000000          jnz         000D02DE8   ; далее ничего не трогаем
00D02D2F: 488B4730              mov         rax,[rdi][030]
00D02D33: 488B8010010000        mov         rax,[rax][000000110]
00D02D3A: 48833800              cmp         q,[rax],0
00D02D3E: 0F85A4000000          jnz         000D02DE8
00D02D44: 4D85E4                test        r12,r12     ;приходим сразу сюда <---
00D02D47: 0F8439010000          jz          000D02E86
00D02D4D: 440FB74F10            movzx       r9d,w,[rdi][010]

В итоге мы поменяли всего 8 байт в файле chrome.dll по оффсету с 2D21 по 2D28

Создаём маску поиска 4883B998000000000F85B9000000488B4730 это байты с 2D21 по 2D32
Теперь маска замены: 66C74710E803EB1B0F85B9000000488B4730

Маска немного больше размером, чем 8 байт, так как иначе мы можем найти сторонний код, так как последовательность cmp q,[rcx][000000098],0 и jnz 000D02DE8 часто встречается, а если добавить еще одну команду mov rax,[rdi][030] то такая последовательность встречается в chrome.dll только один раз, что и требуется для патчинга.

Далее качаем патч-утилиту, воспользуемся этим проектом https://github.com/pbatard/winpatch, здесь кроме патча бинарного кода, также производится модификация контрольной суммы PE-заголовка и подпись кода самоподписанным SSL-сертификатом, что скорее всего излишне, но и не помешает.

Пишем bat-файл, производящий поиск chrome.dll в каталоге "C:\Program Files\Google\Chrome\Application", код более-менее универсальный, чтобы обработать все подкаталоги с разными версиями.

@echo off
taskkill /F /IM chrome.exe
timeout 2
for /r "C:\Program Files\Google\Chrome\Application" %%f in (chrome.dll) do IF EXIST "%%f" ( %~dp0winpatch.exe "%%f" 4883B998000000000F85B9000000488B4730 66C74710E803EB1B0F85B9000000488B4730 )
echo Patch done
echo Press ENTER to close window
pause

Здесь дополнительно удаляется процесс сhrome.exe (вкладки можно будет восстановить после перезапуска), иначе chrome.dll будет залочен и недоступен для патча. Далее ждём 2 секунды и скармливаем утилите winpatch.exe полный путь chrome.dll а также маску поиска и маску замены.

Пишем эти 8 строк в runme.bat и запускаем bat из-под администратора. Патч произведён!

Процесс патча библиотеки chrome.dll
Процесс патча библиотеки chrome.dll

Запускаем патченный Chrome - Youtube работает отлично! Проверено на 3х компьютерах, 2х провайдерах и 5ти версиях Chrome.

В заключение создадим самораспаковыващийся WinRar архив (это полноценный exe-файл), в который поместим утилиту winpatch.exe и runme.bat, назначим ему через диалог конфигурации привилегии администратора на запуск, это чтобы мы смогли модифицировать chrome.dll, лежащий в системной директории, а также ставим в автостарт runme.bat и контрольный вопрос - патчим Chrome? Назовём утилиту youtube_patch_v1.0.exe

Конечно, для полноценной работы нужны списки blacklist.txt - но 8 байт, это 8 байт!

Если что-то пошло не так, восстановите chrome.dll из бэкапа chrome.dll.bak

Если Chrome обновится на новую версию, конечно же, файл chrome.dll будет заменён на оригинальный и патч нужно будет повторить.

Самораспаковывающийся и самозапускающийся архив, полностью готовый для патчинга, выложен на гугл-диске: youtube_patch_v1.0.exe

Комментарии (7)


  1. hurtavy
    07.10.2025 14:31

    Спасибо. На Вивальди патч тоже работает. Жаль, что это только полумера и не все блокировки обходит. Наверное, всё таки рандом нужен


  1. zanzack
    07.10.2025 14:31

    Сайт https://login.microsoft.com не работает после патча

    Попробовал оригинальный NoDPI на питоне и там тоже не работает. Похоже, что Майкрософт не умеет собирать обратно разобранные по кусочкам tls-пакеты, а если сервер на Nginx/Linux, то это не помеха.

    Слава Линусу Торвальдсу и примкнувшему к нему Сысоеву Игорю Владимировичу!
    Позор Биллу Гейтсу ))


    1. hurtavy
      07.10.2025 14:31

      у меня работает этот сайт. Но у меня не NoDPI, а его упрощенная версия. В том же гитхабе sample_version.py


      1. zanzack
        07.10.2025 14:31

        Так у вас, наверное, сайт майкрософта в blacklist.txt не входит?


        1. hurtavy
          07.10.2025 14:31

          Ну вы уж совсем из меня идиота не делайте :) Я даже специально весь траффик завернул через эту прогу, всё работает


          1. zanzack
            07.10.2025 14:31

            Попробовал на sample_version.py - отличия от оригинального в том, что в консоль много мусора сыпется, но майкрософт здесь тоже не работает, а если убираешь из blacklist.txt но оставляешь прокси - начинает работать. Ютуб при этом работает всегда.

            Какой бы еще сайт попробовать, который на Internet Information Services (IIS) сделан? У них там еще расширение .asp вместо .html

            UPD: нашёл сайт, похоже он на asp и не работает с модифицированными tls-пакетами
            https://www.online-convert.com/ru/file-format/aspx


    1. Javian
      07.10.2025 14:31

      Часто попадаются сайты, которые с включенным nodpi не работают.