Введение
В этой статье я расскажу вам, что такое Frida и как ее можно использовать на практике для реверс-инжиниринга и обхода нн-ых защит. Сегодняшним нашим подопытным станет официальный клиент DrKLO/Telegram.
Теория
В этой статье я расскажу вам, что такое Frida и как ее можно использовать на практике для реверс-инжиниринга и обхода нн-ых защит. Сегодняшним нашим подопытным станет официальный клиент DrKLO/Telegram.
Теория
Я думаю все из вас, кто на данный момент читает эту статью хотя бы от части понимают что такое реверс-инжиниринг на ПК. Но знали ли вы, что братья наши меньшие в лице Android реверс-инженеров тоже не отстают. Сначала давайте вспомним, что такое хукинг.
Хукинг (англ. hooking) - технология, позволяющая изменить стандартное поведение тех или иных компонентов информационной системы. Простыми словами подмена исполняемого кода программы на свой с целью достижения нужного результата от программы, изменения ее поведения. Например, подмена функции проверки пароля на свою, которая, например, всегда возвращает TRUE. Это заставляет программу думать, что наш пароль верный. Это был самый простой пример, давайте рассмотрим техническую реализацию.
Как мы привыкли делать на ПК - реверсить программу -> извлекать сигнатуры функций -> сканить сигнатуры -> хукать Minhook'ом. Выглядит, да и реализуется сложно, не так ли? А теперь посмотрим, как с этим же справляется Frida на Android.
Весь процесс хукинга выглядит так - реверсим программу -> извлекаем классы/функции -> пишем простой js скрипт -> хукаем с помощью Frida. Вроде бы так-же 4 пункта как и с ПК. Но вот наглядно как реализован хукинг в MinHook и Frida.
#include <windows.h>
#include <iostream>
#include <vector>
#include "MinHook.h"
// 1. Простой сканер сигнатур (Pattern: 48 89 5C ? ? 57)
uintptr_t FindPattern(const char* pattern, const char* mask) {
uintptr_t base = (uintptr_t)GetModuleHandle(NULL);
MODULEINFO mi; GetModuleInformation(GetCurrentProcess(), (HMODULE)base, &mi, sizeof(mi));
DWORD size = mi.SizeOfImage;
for (DWORD i = 0; i < size - strlen(mask); i++) {
bool found = true;
for (DWORD j = 0; j < strlen(mask); j++) {
if (mask[j] != '?' && *(char*)(base + i + j) != pattern[j]) {
found = false; break;
}
}
if (found) return base + i;
}
return 0;
}
// 2. Оригинал и Хук
typedef bool (__stdcall* tCheck)(void);
tCheck pOriginalCheck = nullptr;
bool __stdcall MyHook() {
return true; // Всегда валидно!
}
void Start() {
// Ищем сигнатуру нашей функции
uintptr_t addr = FindPattern("\x48\x89\x5C\x00\x00\x57", "xxx??x");
if (addr && MH_Initialize() == MH_OK) {
MH_CreateHook((LPVOID)addr, &MyHook, (LPVOID*)&pOriginalCheck);
MH_EnableHook((LPVOID)addr);
}
}
C
#include <iostream>
#include <vector>
#include "MinHook.h"
// 1. Простой сканер сигнатур (Pattern: 48 89 5C ? ? 57)
uintptr_t FindPattern(const char* pattern, const char* mask) {
uintptr_t base = (uintptr_t)GetModuleHandle(NULL);
MODULEINFO mi; GetModuleInformation(GetCurrentProcess(), (HMODULE)base, &mi, sizeof(mi));
DWORD size = mi.SizeOfImage;
for (DWORD i = 0; i < size - strlen(mask); i++) {
bool found = true;
for (DWORD j = 0; j < strlen(mask); j++) {
if (mask[j] != '?' && *(char*)(base + i + j) != pattern[j]) {
found = false; break;
}
}
if (found) return base + i;
}
return 0;
}
// 2. Оригинал и Хук
typedef bool (__stdcall* tCheck)(void);
tCheck pOriginalCheck = nullptr;
bool __stdcall MyHook() {
return true; // Всегда валидно!
}
void Start() {
// Ищем сигнатуру нашей функции
uintptr_t addr = FindPattern("\x48\x89\x5C\x00\x00\x57", "xxx??x");
if (addr && MH_Initialize() == MH_OK) {
MH_CreateHook((LPVOID)addr, &MyHook, (LPVOID*)&pOriginalCheck);
MH_EnableHook((LPVOID)addr);
}
}
C
Код:
#include <windows.h>
#include <iostream>
#include <vector>
#include "MinHook.h"
// 1. Простой сканер сигнатур (Pattern: 48 89 5C ? ? 57)
uintptr_t FindPattern(const char* pattern, const char* mask) {
uintptr_t base = (uintptr_t)GetModuleHandle(NULL);
MODULEINFO mi; GetModuleInformation(GetCurrentProcess(), (HMODULE)base, &mi, sizeof(mi));
DWORD size = mi.SizeOfImage;
for (DWORD i = 0; i < size - strlen(mask); i++) {
bool found = true;
for (DWORD j = 0; j < strlen(mask); j++) {
if (mask[j] != '?' && *(char*)(base + i + j) != pattern[j]) {
found = false; break;
}
}
if (found) return base + i;
}
return 0;
}
// 2. Оригинал и Хук
typedef bool (__stdcall* tCheck)(void);
tCheck pOriginalCheck = nullptr;
bool __stdcall MyHook() {
return true; // Всегда валидно!
}
void Start() {
// Ищем сигнатуру нашей функции
uintptr_t addr = FindPattern("\x48\x89\x5C\x00\x00\x57", "xxx??x");
if (addr && MH_Initialize() == MH_OK) {
MH_CreateHook((LPVOID)addr, &MyHook, (LPVOID*)&pOriginalCheck);
MH_EnableHook((LPVOID)addr);
}
}
Java.perform(function () {
var AbobaClass = Java.use("com.aboba.classes.Aboba");
LicenseManager.isAboba.implementation = function () {
console.log("[*] Вызвана проверка абобы, подменяем!");
// Вместо вызова оригинала return this.isAboba()
// Просто возвращаем истину
return true;
};
});
JS
var AbobaClass = Java.use("com.aboba.classes.Aboba");
LicenseManager.isAboba.implementation = function () {
console.log("[*] Вызвана проверка абобы, подменяем!");
// Вместо вызова оригинала return this.isAboba()
// Просто возвращаем истину
return true;
};
});
JS
Код:
Java.perform(function () {
var AbobaClass = Java.use("com.aboba.classes.Aboba");
LicenseManager.isAboba.implementation = function () {
console.log("[*] Вызвана проверка абобы, подменяем!");
// Вместо вызова оригинала return this.isAboba()
// Просто возвращаем истину
return true;
};
});
Да вы можете сказать, что притянуто за уши ты сравнил нативное приложение на ПК и приложение на java, но это сравнение самое легкое чтобы показать наглядное отличие Frida от MinHook
Я думаю вы заметили разницу. Думаю пора рассказать, что такое этот чудо-инструмент - Frida.
Frida - это мощный инструмент, который позволяет исследовать и модифицировать приложения на Android без долгих перекомпиляций и чтения байт-кода.* Frida позволяет встраивать в приложение свой код, основным языком написания таких встраиваемых скриптов является Javascript. С помощью фриды можно так-же удобно хукать функции как с тем же MinHook'ом. Давайте приступим к практике
Практика
И так для практики я выбрал необычную цель - официальный клиент телеграмма. И хукать сегодня мы будем проверку код-пароля(т.е четырехзначного пасскода при входе в приложение), Т.к исходный код клиента полностью открыт мы можем полность посмотреть где находится обработка введенного пасскода. При входе в приложение нас встречает окно с пасскодом за которое отвечает данный класс - PasscodeActivity.java. В нем мы находим проверку введенного пасскода.
Нам здесь интересна 1000-ая строка с условием if (!SharedConfig.checkPasscode(password)). Т.е за проверку введенного пасс-кода отвечает метод checkPasscode из класса SharedConfig. Давайте посмотрим, что из себя представляет данная функция.
И вот здесь я хотел бы остановиться. Когда я смотрел этот код я секунд на 5 застыл в недоумении. А где расшифровка tdata(как сделано в офиц. клиенте на десктоп telegramdesktop/tdesktop. Только вот проблема в том, что ее просто нет. ТИПО ВООБЩЕ. Ну на этом я заострять внимание не буду, просто скажу, что защита на уровне
Теперь мы знаем, что нам хукать в фриде. А именно функцию checkPasscode(). Приступим.
В первую очередь скажу, что мы будем все это делать на эмуляторе. Я лично использую официальные эмуляторы, предоставляемые с Android Studio, так что все дальнейшие действия будут происходит в нем.
- Создаем эмулятор - открываем AS и выбираем под-меню More Actions. В нем нам необходимо нажать на Virtual Device Manager
- В нем мы нажимаем на Плюсик
- В открывшемся списке мы должны выбрать пресет девайса, ВЫБИРАЕМ ДЕВАЙС РЯДОМ С КОТОРЫМ НЕТ ЗНАЧКА GOOGLE PLAY. В пресетах с гугл плей имеются гугл сервисы, это production билды - в них нельзя просто поставить adb root, что усложняет реализацию.
- После выбора пресета устанавливайте его и запускаете.
- Теперь нам необходимо поставить frida-server на девайс - в первую очередь вам необходимо узнать архитектуру выбранного вами девайса. Пропишите команду
Вам выдастся в большинстве случаев либо x86_64(96) либо x86. Запомните это на будущее.adb shell getprop ro.product.cpu.abi - После этого скачивайте с официального репозитория необходимый релиз frida-server. Его название будет таким(по паттерну) - frida-server-XX.X.X-android-АРХИТЕКТУРА_ИЗ_5_ШАГА.xz
- После этого распакуйте архив и закиньте на эмулятор. Команды даны ниже.
unxz frida-server-*.xz
adb push frida-server-*-android-* /data/local/tmp/frida-server - После этого вам необходимо настроить права для сервера и его запустить. Делается это в несколько команд.
adb shell
su
chmod 755 /data/local/tmp/frida-server
/data/local/tmp/frida-server & - Теперь нам необходимо установить фриду на хост(не спрашивайте почему такой порядок действий). Выполняется это одной установкой python-пакета.
pip install frida-tools - Теперь когда у нас подготовлена среда выполнения мы можем приступать к "хакингу".
- Установим с официального сайта телеграмма APK-клиента
- После установки входим в аккаунт и ставим код-пароль.
- Проверяем, что с клиентом все в норме, пробуем ввести неправильный код-пароль. Если выдает ошибку то это нормально. Мы можем приступать к хукингу.
- Нам необходимо написать скрипт который будет подменять функцию checkPasscode() класса SharedConfig. Он будет оставлен ниже.
Его мы сохраняем в удобное вам место.Java.perform(function () {
var SharedConfig = Java.use("org.telegram.messenger.SharedConfig");
// Хукаем функцию
SharedConfig.checkPasscode.implementation = function (passcode) {
console.log("Попытка ввода пароля: " + passcode);
// Подменяем на тру
return true;
};
});
JS
Код:Java.perform(function () { var SharedConfig = Java.use("org.telegram.messenger.SharedConfig"); // Хукаем функцию SharedConfig.checkPasscode.implementation = function (passcode) { console.log("Попытка ввода пароля: " + passcode); // Подменяем на тру return true; }; }); - Нам необходимо вшить в клиент этот скрипт. Делается это одной простой командой.
frida -U -f org.telegram.messenger.web -l СКРИПТ.js - После выполнения команды Frida пересоздаст процесс с уже вшитым кодом. И так мы вводим любой(абсолютно) код-пароль и PROFIT!!! Мы успешно обошли защиту мобильного тг клиента.
Вывод
Frida - это очень мощный инструмент для реверс-инжиниринга мобильны Android(да и не только) приложений. Он не уступает по функционалу тому же MinHook'у. Если вы хотите обучиться в RE на Android Frida это определенно ваш выбор. А какой вывод по Андройд клиенту тг? Хз
Критика и предложения по статье приветствуются!





Комментарии 0