Google Chrome и Secure Preferences

chrome_art

На нашем форуме и не только, с некоторой периодичностью люди интересуются алгоритмом генерации "защитного кода" в файле Secure Preferences для браузера Google Chrome.
Зачем он браузеру? Этот код используется для проверки целостности настроек расширений и некоторых других параметров, проще говоря - HMAC. Зачем он людям? Вероятно, это необходимый этап для тихой установки расширений или изменения настроек браузера. Давайте разберемся, где и как происходит генерация этих HMAC'ов.

Для начала заглянем в исходный код проекта Chromium. Беглый поиск по слову hash вывел меня на файл pref_hash_calculator.cc. И, что характерно, именно здесь все и происходит. Процитирую код основных методов, которые отвечают за генерацию:

Авторское форматирование было задвинуто подальше, так как у меня личная неприязнь к такому стилю. Код выглядит довольно простым, осталось понять, что вообще подается на вход. Чтобы не скачивать весь исходный код Chromium и не собирать его, поступим следующим образом: поставим официальный Chrome, скачаем один лишь файл pref_hash_calculator.cc и укажем в Visual Studio путь к отладочным символам для Google Chrome:

Все готово к исследованию. Запускаем браузер, атачимся к процессу, ждем, пока прогрузятся отладочные символы (можно в настройках указать загрузку символов только для модуля chrome.dll, этого будет достаточно), открываем файл pref_hash_calculator.cc в MSVC и ставим брейкпоинт на методе Calculate. Теперь нам необходимо совершить какое-нибудь действие, которое приведет к вычислению хэша, например, установить произвольное расширение из Chrome Web Store. Устанавливаем и попадем на наш брейкопинт.

breakpoint1

Мы видим значения seed_ (перевел в hex для удобства):

И device_id:

Причем seed_ - постоянная величина (но, скорее всего, может меняться от версии к версии), а device_id - уникальный идентификатор компьютера. Откуда берется seed_? Не вдаваясь в подробности поиска, скажу, что он содержится в файле resources.pak, который находится в директории с браузером. Формат содержимого файла известный и давно описан, например, тут. Давайте попробуем самостоятельно извлечь seed_ из resources.pak. Для этого я напишу простенький скрипт на Perl:

Делаем тестовый прогон и узнаем, что seed_ содержится в ресурсе с ID 609, который скорее всего тоже меняется.

script

Теперь нам необходимо получить device_id. Откуда его берет Chrome? Не буду вас утомлять отладчиком, просто скажу, что нас интересует функция GetMachineId из файла machine_id.cc. Приведу ее исходный код на всякий случай:

Этот код является частью сторонней библиотеки RLZ. Я не стал особо копаться в логике вызовов, а просто выдрал код, немного подредактировал и сделал из него отдельный файл, который можно смело собирать и тестировать под Windows (генерация device_id отличается в зависимости от ОС). Ссылка на проект для Microsoft Visual Studio 2013 в конце статьи.
Итак, у нас есть seed_, device_id, осталось обратить свое внимание на два оставшихся аргумента, которые передаются в GetMessage - это path и value. Зайдем сразу внутрь функции GetMessage и посмотрим, что она формирует.

breakpoint2

Мы видим, что path содержит путь к настройкам расширения в Secure Preferences:

А value (value_as_string) - настройки расширения в JSON:

Подытожим логику вычисления:

В качестве hmac используется HMAC SHA256, это видно по коду. Приведу пример простого скрипта, который парсит расширения из файла Secure Preferences и вычисляет hmac для каждого из них, а также super_mac. Алгоритм вычисления прост:

super_mac используется для проверки целостности некоторых настроек браузера и массива пар ид_расширения - hmac. Наконец-таки скрипт:

Запустим скрипт, указав в качестве аргументов device_id, seed и путь к файлу Secure Preferences:

script2

Как мы видим, скрипт успешно отработал и корректно вычислил HMAC'и.

Скрипты из статьи и проект для MSVC 2013, вычисляющий device_id: скачать

Google Chrome и Secure Preferences: 72 комментария

  1. A lil bit offtop, but who wants use Chrome, Opera(nu) & Vivaldi, etc with WebRTC, that cannot be disabled.

    vasya_voin: Otherwise - u can always filter FF quotes, and other stuff with "Privoxy", check rdot forum topic for this.

      1. @Kaimi,
        да не вопрос, просто компиляция ещё часов 10 возьмёт.
        А вообще спасибо за статью.

        1. Зачем компилировать? Я же писал, что достаточно отладочные символы подключить и интересующие одиночные файлы скачать

  2. Кстати, для Яндекс-браузера device_id не нужен. Сообщение формируется extension_path + extensio_setting. А в качестве key можно передать 0x00

  3. Privet Kaimi, iskal infu nas4iot faila Secure Preferences i natknulsia na tvoi sait(o4eni interesnii kstati, molodez), doljen skazati cho ti edinstvenii isto4nik takoi poleznoi informazii (poleznoi dlia griaznih del pravda :D).
    Ia doljen sdelati installer kotorii v konze dobavliaet stranizu nashei kompanii v startup_pages v chrome. Vsio kak po maslu no na samom glavnom:

    c:\Users\operatore 3\AppData\Local\Google\Chrome\User Data\Default>hmac.pl 837552B31736B91B48643763FE6E1F494FC1D936AE09173C3D E748F336D85EA5F9DCDF25D8F347A65B4CDF667600F02DF6724A2AF18A212D26B788A25086910CF3A90313696871F3DC05823730C91DF8BA5C4FD9C884B505A8
    "Secure Preferences"

    values on reference is experimental at C:\Users\operatore 3\AppData\Local\Google\Chrome\User Data\Default\hmac.pl line 91.
    Can't use an undefined value as a HASH reference at C:\Users\operatore 3\AppData\Local\Google\Chrome\User Data\Default\hmac.pl line 36.

    K sojalenii ia v Perle ne 4eshu, i ia eshio doljen perevesti eto vsio na C#... Smojesh proveriti po4emu tvoi script u menia ne rabotaet (Ia kru4u ego na Perl 5.22.0 Strawberry)? a esli esti jelanie i vremia to podmodifizirovati ego 4tob kak parametri polu4al: "device_id, seed, key, value" e vidoval hmac dlia "value" i novii super-mac (vsio na stdout). Potom ia ego sobiru v exe, sunu v installer i budu parsiti ego stdout kogda nado.

    Ogromnoe spasibo za takuiu infu, i sait krasava :)

    Privet iz Italii

    1. Izvini, script rabotaet otli4no, prosto ia emu podoval osobii file "Secure preferences", neznaiu po4emu no na moiom pk tam nahoditsia tolko super-mac i vesit 105byte, daje s novoi ustanovkoi, a na drugih pk 30-50kb i oni rabotaiut na tvoiom scripte.

  4. Izviniaiusi, po4ital script paru raz i vsio ponial, Perl menia pugal a v konze konzov vsio o4eni prosto, u menia on ne rabotal potomu4to ia imel levii "Secure Preferences" file, tam tolko super-mac nohoditsia, a ostalnie mac v "Preferences".

  5. Скрипт, запущенный в версиях Perl под Windows (ActivePerl 5.20.? x64, Strawberry 5.22.0 x32) каждый раз возвращают разные MAC для расширений: как собственно посчитанные, так и ref (

    SuperMac всегда считается верно

    1. Не наблюдаю такой проблемы с Strawberry Perl 5.22 x64.
      Правда выдает warning на строку if(scalar values $ref->{$key} == 0) , но считает корректно.

      Другое дело список расширений каждый раз в произвольном порядке выдается, но это ни на что не влияет.

      1. > Другое дело список расширений каждый раз в произвольном порядке выдается, но это ни на что не влияет.
        Ааа, точно, в произвольном порядке.

        Спасибо.

  6. Здравствуйте.
    Я получил $seed с помощью скрипта и $device_id с помощью проекта rlz_id. Вместо аргументов командной строки в скрипте вычисления hmac указал:
    my $sec_pref_file = 'C:\\Users\\...\\Default\\Secure Preferences';
    my $seed = 'e748f33...505a8';
    my $device_id = '6A7411E...290539F';
    При запуске получаю такие ошибки:
    values on reference is experimental at startpage.pl line 90.
    Global symbol "$device_id" requires explicit package name (did you forget to declare "my $device_id"?) at startpage.pl line 48.
    Global symbol "$key" ... line 50.
    Global symbol "$device_id" ... line 66.
    Global symbol "$key" ... line 68.

    Строки кода:
    48 while(my $ref = shift @queue)
    50 next unless ref $ref eq ref {};
    66 {
    68 delete $ref->{$key} ;

    Среда:
    Window 10 x64
    perl v5.22.1 built for MSWin32-x86-multi-thread-64int
    Binary build 2201 [299574] provided by ActiveState http://www.ActiveState.com
    Built Dec 24 2015 12:36:28

    Подобные ошибки perl выдает если переменные не инициализированы в режиме strict, однако они инициализированы! не понимаю в чем причина.
    В perl я новичок, установил только ради Вашего скрипта

    Помогите устранить указанные ошибки компиляции, пожалуйста :)

  7. спасибо. заработало. я запускал и редактировал разные файлы :(
    Существует ли один скрипт, выполняющий все этапы вычисления hmac включая определение входных параметров?

  8. Kaimi, большое спасибо за пост, очень познавательно. Есть вопрос..я в MSVC 2012 Ultimate указал путь к серверу с отладочными символами и при подключении к процессу chrome.exe, идет их подгрузка, но когда я открываю файл pref_hash_calculator.cc и ставлю бряк, появляется мессага о том, что символы для данного элемента не подгружены и остановки не будет?.

    1. Может символы не подгрузились (можно посмотреть в логе студии), также не вижу pref_hash_calculator в исходниках по старому пути (видимо куда-то перенесли), может не к тому процессу хрома подключился, может еще что-то...

  9. Hello,
    When I try to get the HMAC from your example in C++ or Python I have the same hash, but is different than yours. What is wrong? (Please see below)

    import hmac
    import hashlib
    import base64
    import binascii

    device_id = "7CD6D9C7354E9165A3A4CBE097021669F4C026E7AA578B0CA9";
    key = "e748f336d85ea5f9dcdf25d8f347a65b4cdf667600f02df6724a2af18a212d26b788a25086910cf3a90313696871f3dc05823730c91df8ba5c4fd9c884b505a8";
    content = '{"ack_external":true,"app_launcher_ordinal":"zs","creation_flags":137,"from_bookmark":false,"from_webstore":true,"initial_keybindings_set":true,"install_time":"13074357069686027","lastpingday":"13074332401422358","location":1,"manifest":{"api_console_project_id":"889782162350","app":{"launch":{"local_path":"main.html"}},"container":"GOOGLE_DRIVE","current_locale":"en_US","default_locale":"en_US","description":"Create and edit presentations ","icons":{"128":"icon_128.png","16":"icon_16.png"},"key":"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLOGW2Hoztw8m2z6SmCjm7y4Oe2o6aRqO+niYKCXhZab572by7acqFIFF0On3e3a967SwNijsTx2n+7Mt3KqWzEKtnwUZqzHYSsdZZK64vWIHIduawP0EICWRMf2RGIBEdDC6I1zErtcDiSrJWeRlnb0DHWXDXlt1YseM7RiON9wIDAQAB","manifest_version":2,"name":"Google Slides","offline_enabled":true,"update_url":"https://clients2.google.com/service/update2/crx","version":"0.9"},"page_ordinal":"n","path":"aapocclcgogkmnckokdopfmhonfmgoek\\0.9_0","state":1,"was_installed_by_default":true,"was_installed_by_oem":false}'
    path = "extensions.settings.aapocclcgogkmnckokdopfmhonfmgoek";
    msg = device_id + path + content;

    dig = hmac.new(binascii.unhexlify(key), msg=msg, digestmod=hashlib.sha256).digest()
    hmac_res = base64.b64encode(dig).decode() # py3k-mode
    print binascii.hexlify(dig)

    Results:
    24664baa780f87978734adc88e531d3e50e2daa230401e92ef718d3b9de07169

      1. It's not about Chrome, it's all hardcoded right now with the values of your example. The fact is that my HMAC method doesn't return the same hash as you, so I'm wondering where my mistake is.
        I'm trying to validating the algorithm before actually working on real data.

          1. I understand your statement, however I'm just playing with the data (already normalized) you've put in your demonstration. The blocking point is the very last part, where we calculate the HMAC. I'm sure there's something, non-obvious, missing in the explanations...

          2. Ok, I will answer to myself. I have been able to get the correct value on Python, by fixing several things:
            - Json needs to be parsed/dumped as string (validation)
            - Json needs to PRESERVE keys order
            - I had an error in Json string, \\ needs to be \

            Here's the correct Python:

            import hmac
            import hashlib
            import base64
            import binascii
            import json
            from collections import OrderedDict

            device_id = "7CD6D9C7354E9165A3A4CBE097021669F4C026E7AA578B0CA9"
            key = "e748f336d85ea5f9dcdf25d8f347a65b4cdf667600f02df6724a2af18a212d26b788a25086910cf3a90313696871f3dc05823730c91df8ba5c4fd9c884b505a8"
            content = '{"ack_external":true,"app_launcher_ordinal":"zs","creation_flags":137,"from_bookmark":false,"from_webstore":true,"initial_keybindings_set":true,"install_time":"13074357069686027","lastpingday":"13074332401422358","location":1,"manifest":{"api_console_project_id":"889782162350","app":{"launch":{"local_path":"main.html"}},"container":"GOOGLE_DRIVE","current_locale":"en_US","default_locale":"en_US","description":"Create and edit presentations ","icons":{"128":"icon_128.png","16":"icon_16.png"},"key":"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLOGW2Hoztw8m2z6SmCjm7y4Oe2o6aRqO+niYKCXhZab572by7acqFIFF0On3e3a967SwNijsTx2n+7Mt3KqWzEKtnwUZqzHYSsdZZK64vWIHIduawP0EICWRMf2RGIBEdDC6I1zErtcDiSrJWeRlnb0DHWXDXlt1YseM7RiON9wIDAQAB","manifest_version":2,"name":"Google Slides","offline_enabled":true,"update_url":"https://clients2.google.com/service/update2/crx","version":"0.9"},"page_ordinal":"n","path":"aapocclcgogkmnckokdopfmhonfmgoek\\\\0.9_0","state":1,"was_installed_by_default":true,"was_installed_by_oem":false}'
            path = "extensions.settings.aapocclcgogkmnckokdopfmhonfmgoek"

            try:
            content_json = json.loads(content, object_pairs_hook=OrderedDict)
            except Exception, e:
            print 'Invalid json: %s' % e
            exit(1)

            content_str = json.dumps(content_json, separators=(',', ':'))
            msg = device_id + path + content_str

            dig = hmac.new(binascii.unhexlify(key), msg=msg, digestmod=hashlib.sha256).digest()
            hmac_res = base64.b64encode(dig).decode() # py3k-mode
            print content_str + '\n'
            print binascii.hexlify(dig)

  10. Спасибо за интересную и очень полезную работу!!!
    проблему с кодом:
    Experimental values on scalar is now forbidden at hmac.pl line 91.
    я решил так:
    if(scalar values %{$ref->{$key}} == 0)

  11. Hi everyone,

    I am using the latest version of Chrome (58.0.3029.81) and this code/technique about is not longer working.

    Is anybody experiencing the same? If so, any ideas on how to fix it?

    Many thanks!

      1. Hi Kaimi, How are you doing?
        I'm looking for a good C++ dev. to :

        Create C++ (MSVC any version) tool/library that allows to write and update configs in Chrome "secure preferences" in latest Chrome 58-59 - write values to extension and global configs, correctly calculate HMAC for extension configs, Super HMAC.
        This project can be used as base code:
        https://kaimi.io/2015/04/google-chrome-and-secure-preferences/
        but many small things changes in Chrome 58. At least machine id calculated/formated differently, seed formated/grabbed

        basically - make it work again - $2,000 offer.
        Email : [email protected]

        1. Hello,
          Generally my code is working, though there was a minor change in logic: device_id value is now equivalent to User SID (without group id part). You can get it using wmic useraccount get name,sid or using WinAPI function LookupAccountName

          Example:
          >wmic useraccount get name,sid

          Administrator S-1-5-21-1180699209-877415012-3182924384-500
          Guest S-1-5-21-1180699209-877415012-3182924384-501
          HomeGroupUser$ S-1-5-21-1180699209-877415012-3182924384-1002
          Tim S-1-5-21-1180699209-877415012-3182924384-1004
          UpdatusUser S-1-5-21-1180699209-877415012-3182924384-1007

          device_id = S-1-5-21-1180699209-877415012-3182924384

  12. Hi Kaimi, How are you?

    What about Opera 51? Now new secure string in Preferences perf_counters.ext_load_metric What is the calculation algorithm for the new secure string? I tried to use a debugger to figure out the calculation algorithm but did not find out! Please help, you're a genius!

    1. 1. Attach to Chromium
      2. set breakpoint around digest function
      3. notice how it looks in assembly
      4. search similar instructions sequences in opera dll
      5. set breakpoints for such places
      6. observe stack and registers

      This way you'll likely find, what is current hmac composition

      1. Kaimi, в новой опере другой алгоритм вычисления хеша совсем не тот что в хроме, отладка по аналогии с хромом здесь не применима, Kaimi, пожалуйста посмотри, что используется для вычисления хеша?

  13. Как подключиться к процессу в VS2017, где указать символы? И еще вопрос - что с Яндекс браузером, действительно ли он берет только path + value без ключа для HMAC'а, и в таком случае как использовать HMAC без ключа(я уже про шарп)?

  14. Здравствуйте! А как приатачиться к процессу, как загрузить символы? Выдет ошибку Не удается присоединиться к процессу. Операция недопустима в текущем состоянии. А что на счет Яндекс.Браузера?

    1. Убедиться, что студия запущена от администратора или попробовать запустить браузер из самой студии.
      Про Яндекс не знаю, не смотрел

  15. Здравствуй, с хромом разобрался. В новой версии у resources.pak структура такая: версия 5(4 байта) кодировка 1(4 байта), кол-во элементов(2 байта), 2 байта непонятной шняги, после идут элементы. Нашел совпадения у resources.pak от хрома и ЯБ: там 64 байта одинаковые, но в ЯБ секции по 64 байта встречаются один раз, в хроме - 2, первый(id 173) - seed_, второй - аналогичный ЯБ(даже данные одинаковые), но id разный. Есть ли вариант как-то расковырять Яндекс Браузер чтобы узнать что он делает? Очень хочется)))
    С уважением,
    Александр

    1. К яндексу отладочных символов наверное нет публичных. Если предположить, что device_id они получают тот же, что и Chrome, то попробовать поставить breakpoint на вызов LookupAccountName (если она используется для получения User SID) и потрассировать.
      Можно поставить breakpoint на вызовы функций (CreateFileA, CreateFileW, ...) работы с файлами и попробовать поймать момент, когда устанавливаешь расширение в браузер и его данные заносятся в Secure Preferences.
      Также можно предположить, что код функции в хроме и яндексе +- сопоставим, тогда можно взять BinDiff (может что-то лучше и новее есть) и попробовать найти функцию генерации hmac.

      В общем можно пытаться трассировать от тех или иных событий, сравнивать код функций или по исходному коду посмотреть, откуда функция вызывается, может рядом есть какие-то характерные строки или вызовы и найти по ним.

  16. Здравствуйте, Kaimi, я нашел алгоритм для вычисления Hmac, но вычисленный хэш не соответствует хэшу в файле опций безопасности. Я из Китая. Мой электронный адрес Google - [email protected]. Можете ли вы связаться с технической биржей?

    Hello, Kaimi, I found the algorithm to calculate Hmac, but the calculated Hash is not consistent with the Hash in the security options file. I am from China. My Google email is [email protected]. Can you contact technical exchange?

    1. Hello,
      Problems with a hash could be caused by JSON data normalization before the hashing procedure call.
      Under debugger it should be obvious, what's the problem.
      I haven't examined Google Chrome code for a while, so I have no idea, how is it calculated nowadays and is something changed since the article publication.

      1. Thank you. Would you please tell me if the version you are debugging at that time calculated the hash through this file pref_hash_calculator.cc and wrote it directly to the file. My Google Chrome source code now calculates the hash location, but I don't know what hash does. Is this hash participating in the calculation and calculating another new hash to write to the file?

        1. There were several calls to this function: for each extension json and for the whole preferences json.
          So you should probably just log and examine all input-output pairs after, for example, adding an extension to Chrome.

  17. Thank you for your reply, I now find the algorithm generated by Chrome Id extension, and it has come out. Now there is only one super_mac hash not found and the extended Hash calculation is not found. Although only one check hash is found, the distance is still far away. Thank you, foreign friends.

Добавить комментарий для Tiffany Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *