0x00 Введение
Иногда при реверсе мобильных приложений в отснифанном трафике встречаются непонятные зашифрованные запросы и если вы раньше не сталкивались с ними то вам будет очень тяжело понять что же делать.
Что такое Protobuf?
Protocol Buffers — протокол сериализации структурированных данных предложенный Google.
На странице проекта Google Protocol Buffers описывается как «не зависящий от языка и платформы расширяемый механизм для сериализации структурированных данных». Также там есть пояснение: «Как XML, но меньше, быстрее и проще».
И хотя одним из преимуществ Protocol Buffers является поддержка различных языков программирования, в этой статье речь пойдет про реверс инжиниринг этого протокола, который я часто встречаю в Java приложениях и скомпилированном C++ коде.
0x01 Как понять что приложение использует Protobuf?
Это бинарный формат. Для тех кто знаком с языком си, это почти тот же struct, отправляемый по сети. Информация о типах данных хранится в файле .proto, скомпилированном под нужную платформу и не передается в запросе, клиент и сервер уже имеют этот файл заранее.
Пример исходного кода файла .proto:
Здесь ключевое слово "message" определяет структуру "Album", которую мы хотим представить. В ней есть четыре поля, три из которых строки (string), а одно — целое число (int32). Два из них могут присутствовать в сообщении более одного раза, так как для них указано зарезервированное слово repeated.
После компиляции файл превращается в Java-класс в случае Java или в .pb.cc и .pb.h в случае C++, понять что используется данный протокол можно по наличию "com.google.protobuf" в декомпиляторе jadx, методов SerializeToString()/ParseFromString() в результатах декомпиляции IDA Pro, а так же по наличию заголовков Content-Type: application/x-protobuf или application/x-google-protobuf в отснифанных через Fiddler или Charles HTTP-запросах.
Окей, в чем тогда загвоздка? Поскольку исходный файл .proto не используется приложением напрямую, а используется только для создания шаблона, десериализовать фрагмент данных, не зная его формата, не так уж и просто.
0x02 Реверсим протокол как бог
Мы будем использовать библиотеку pbtk для реверс-инжиниринга и восстановления оригинальных структур .proto и Charles для просмотра расшифрованных сообщений
PBTK требует Python ≥ 3.5, PyQt 5, Python-Protobuf 3 и несколько программ (chromium, jad, dex2jar...) для запуска скриптов экстрактора.
Установка
Для примера откроем Android-приложение. После недолгого изучения приложения с помощью своего любимого декомпилятора мы выяснили, что оно передает Protobuf как данные POST по HTTPS.
Первый шаг который нам нужно сделать — преобразовать файлы .proto в текстовый формат. Загрузим APK и подождем, PBTK выдаст результат.
После этого переходим в папку ~/.pbtk/protos/<ваш APK> (или %APPDATA%\pbtk\protos в Windows). Все .proto файлы действительно здесь.
Вернувшись в Jadx или любой другой декомпилятор, мы находим класс, который создает данные, отправляемые на интересующий нас HTTPS-адрес. Он сериализует сообщение Protobuf, вызывая класс из сгенерированного Java-кода.
Этот класс будет соответствовать файлу .proto внутри папки ~/.pbtk/protos/<ваш APK> (т.е. com.foo.bar.a.b будет соответствовать com/foo/bar/a/b.proto).
Следующим шагом будет выбор желаемого входного .proto и заполнение информации о запросе.
Мы также можем засунуть образец необработанных данных Protobuf, которые были отправлены на сервер, вытащив их из пакетов Charles и вставив в шестнадцатеричном формате.
Теперь мы можем просмотреть древовидное представление, представляющее каждое поле в структуре Protobuf (повторяющиеся поля имеют суффикс «+», обязательные поля не имеют плюса).
И мы можем тыкать на разные кнопки, изменяя структуру и переименовывать поля щелкнув по ним.
0x03 Использование .proto
Отлично, мы извлекли .proto файлы, но для Charles нам нужны .desc. Для этого установим protoc и запустим команду
Открываем Charles
Выберите Viewer Mappings в меню View.
Включите Viewer Mappings, добавьте новый маппинг и выберите Response Type: Protocol Buffers.
Нажмите Open Descriptor Registry и добавьте сгенерированный файл .desc.
Выберите соответствующий тип сообщения из раскрывающегося меню типа сообщения.
Бам! Наше ответное сообщение теперь отображается со всеми ключами, сопоставленными с их соответствующими значениями.
Удачного реверса!
Иногда при реверсе мобильных приложений в отснифанном трафике встречаются непонятные зашифрованные запросы и если вы раньше не сталкивались с ними то вам будет очень тяжело понять что же делать.
Что такое Protobuf?
Protocol Buffers — протокол сериализации структурированных данных предложенный Google.
На странице проекта Google Protocol Buffers описывается как «не зависящий от языка и платформы расширяемый механизм для сериализации структурированных данных». Также там есть пояснение: «Как XML, но меньше, быстрее и проще».
И хотя одним из преимуществ Protocol Buffers является поддержка различных языков программирования, в этой статье речь пойдет про реверс инжиниринг этого протокола, который я часто встречаю в Java приложениях и скомпилированном C++ коде.
0x01 Как понять что приложение использует Protobuf?
Это бинарный формат. Для тех кто знаком с языком си, это почти тот же struct, отправляемый по сети. Информация о типах данных хранится в файле .proto, скомпилированном под нужную платформу и не передается в запросе, клиент и сервер уже имеют этот файл заранее.
Пример исходного кода файла .proto:
SCSS:
syntax = "proto3";
option java_outer_classname = "AlbumProtos";
option java_package = "com.examples.protobuf";
message Album {
string title = 1;
repeated string artist = 2;
int32 release_year = 3;
repeated string song_title = 4;
}
Здесь ключевое слово "message" определяет структуру "Album", которую мы хотим представить. В ней есть четыре поля, три из которых строки (string), а одно — целое число (int32). Два из них могут присутствовать в сообщении более одного раза, так как для них указано зарезервированное слово repeated.
После компиляции файл превращается в Java-класс в случае Java или в .pb.cc и .pb.h в случае C++, понять что используется данный протокол можно по наличию "com.google.protobuf" в декомпиляторе jadx, методов SerializeToString()/ParseFromString() в результатах декомпиляции IDA Pro, а так же по наличию заголовков Content-Type: application/x-protobuf или application/x-google-protobuf в отснифанных через Fiddler или Charles HTTP-запросах.
Окей, в чем тогда загвоздка? Поскольку исходный файл .proto не используется приложением напрямую, а используется только для создания шаблона, десериализовать фрагмент данных, не зная его формата, не так уж и просто.
0x02 Реверсим протокол как бог
Мы будем использовать библиотеку pbtk для реверс-инжиниринга и восстановления оригинальных структур .proto и Charles для просмотра расшифрованных сообщений
PBTK требует Python ≥ 3.5, PyQt 5, Python-Protobuf 3 и несколько программ (chromium, jad, dex2jar...) для запуска скриптов экстрактора.
Установка
Bash:
# linux:
sudo apt install python3-pip git openjdk-9-jre libqt5x11extras5 python3-pyqt5.qtwebengine python3-pyqt5
# macOS:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew tap AdoptOpenJDK/openjdk
brew install python git adoptopenjdk/openjdk/adoptopenjdk-openjdk9 qt@5
# Windows:
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "[System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
choco install python3 git openjdk11 qt5-default
python3 -m ensurepip
pip3 install protobuf pyqt5 pyqtwebengine requests websocket-client
git clone https://github.com/marin-m/pbtk
cd pbtk
python3 ./gui.py
Для примера откроем Android-приложение. После недолгого изучения приложения с помощью своего любимого декомпилятора мы выяснили, что оно передает Protobuf как данные POST по HTTPS.
Первый шаг который нам нужно сделать — преобразовать файлы .proto в текстовый формат. Загрузим APK и подождем, PBTK выдаст результат.
После этого переходим в папку ~/.pbtk/protos/<ваш APK> (или %APPDATA%\pbtk\protos в Windows). Все .proto файлы действительно здесь.
Вернувшись в Jadx или любой другой декомпилятор, мы находим класс, который создает данные, отправляемые на интересующий нас HTTPS-адрес. Он сериализует сообщение Protobuf, вызывая класс из сгенерированного Java-кода.
Java:
package com.examples.protobuf;
import java.util.ArrayList;
import java.util.List;
/**
* Music album.
*/
public class Album {
private final String title;
private final List < String > artists;
private final int releaseYear;
private final List < String > songsTitles;
private Album(final String newTitle, final List < String > newArtists,
final int newYear, final List < String > newSongsTitles) {
title = newTitle;
artists = newArtists;
releaseYear = newYear;
songsTitles = newSongsTitles;
}
public String getTitle() {
return title;
}
...
Этот класс будет соответствовать файлу .proto внутри папки ~/.pbtk/protos/<ваш APK> (т.е. com.foo.bar.a.b будет соответствовать com/foo/bar/a/b.proto).
Следующим шагом будет выбор желаемого входного .proto и заполнение информации о запросе.
Мы также можем засунуть образец необработанных данных Protobuf, которые были отправлены на сервер, вытащив их из пакетов Charles и вставив в шестнадцатеричном формате.
Теперь мы можем просмотреть древовидное представление, представляющее каждое поле в структуре Protobuf (повторяющиеся поля имеют суффикс «+», обязательные поля не имеют плюса).
И мы можем тыкать на разные кнопки, изменяя структуру и переименовывать поля щелкнув по ним.
0x03 Использование .proto
Отлично, мы извлекли .proto файлы, но для Charles нам нужны .desc. Для этого установим protoc и запустим команду
Bash:
# linux
sudo apt install protobuf-compiler
# macOS
brew install protobuf
# Windows
choco install protoc
~/.pbtk/protos/<ваш APK>/com/foo/bar/a/
protoc -oModel.desc Model.proto
Открываем Charles
Выберите Viewer Mappings в меню View.
Включите Viewer Mappings, добавьте новый маппинг и выберите Response Type: Protocol Buffers.
Нажмите Open Descriptor Registry и добавьте сгенерированный файл .desc.
Выберите соответствующий тип сообщения из раскрывающегося меню типа сообщения.
Бам! Наше ответное сообщение теперь отображается со всеми ключами, сопоставленными с их соответствующими значениями.
Удачного реверса!