Отлом байды
Как устроена защита Android-приложений Baidu
warning
Статья написана в исследовательских целях, имеет ознакомительный характер и предназначена для специалистов по безопасности. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Использование или распространение ПО без лицензии производителя может преследоваться по закону.
Если ты регулярно читаешь «Хакер», у тебя могло сложиться впечатление, будто всякие хитрые протекторы, использующие обфускацию с шифрованием кода, виртуализацию и прочие страшные антихакерские приемы, существуют только для десктопных защит (типа Themida, WMProtect и им подобных). А мобильные, в частности андроидовские, приложения практически беззащитны, в связи с чем ломаются легко и приятно, даже без помощи отладчика в статике.
Конечно же, это не так: под Android существует множество серьезных коммерческих решений, сложность и цена которых оправдывают сохранение приватности кода. Разумеется, спрос рождает предложение, и специально обученные люди (большей частью китайцы) успели напридумывать множество протекторов, защищающих код мобильных приложений от взлома и реверса. И конечно, тут же нашлись другие китайцы, которые исследуют подобные протекторы на предмет уязвимостей и клепают инструменты для взлома.
Например, лет десять назад простые студенты Гонконгского политехнического института провели собственное исследование наиболее популярных (китайских, как ты, наверное, догадался) протекторов Android-приложений и даже создали собственный пакет DexHunter, позволяющий более‑менее успешно их обходить. Желающие могут подробно ознакомиться с общими принципами построения таких протекторов и способами их обхода, так сказать, из первых рук. Мы же, как обычно, перейдем от теории к практике и разберем взлом одной из описанных авторами систем защиты — Baidu.
Выбранный мною пример будет полезен еще и тем, что прогресс не стоит на месте и защиты совершенствуются. В частности, актуальные версии Baidu уже не обойти при помощи DexHunter и прочих инструментов реверс‑инженера. А значит, за неимением однокнопочного решения приходится мастерить его собственной головой и руками, чем мы сегодня и займемся.
Исследуем задачу
Итак, условие задачи. У нас имеется некое Android-приложение, работающее с экзотическим железом. Поддержка этого приложения прекращена, исходники недоступны. Требуется получить исходный код для последующего анализа. Если ты уже немного знаком с отладкой и реверсом Android-приложений, наверняка умеешь пользоваться таким полезным инструментом, как JEB. Загружаем в него наш .
и для начала попытаемся найти в коде ссылку на текстовую строку «Authentication Failed. Wrong password?», выскакивающую при неправильном вводе пароля.

Пока все нормально, строка успешно найдена в ресурсах: она присутствует там в явном виде, и даже идентификатор ее известен — authentication_failed_wrong_password
. А вот дальше все становится грустно: поиск этого идентификатора дает только варианты исходной строки на языках народов мира, а ссылок на код нет ни малейших.

Спасибо хотя бы на том, что нам теперь известен шестнадцатеричный идентификатор искомой строки — 0x7f0f009a
, по которому можно попробовать попытаться найти ее в откомпилированном smali-коде. Однако здесь нас тоже ждет разочарование — ни в одном из распакованных файлов проекта подобная комбинация не встречается.
Попутно мы обнаруживаем в разобранном проекте несколько интересных вещей. В нем, по сути, нет кода, как‑либо связанного с функциями приложения, — только вспомогательные библиотечные пакеты и ресурсы. Однако в коде присутствует странный, частично обфусцированный класс com.
вот с таким содержимым.

Код явно ссылается на нативные библиотеки baiduprotect
, версии которых под различные архитектуры лежат в подкаталогах Assets
и Libraries
. Открыв для примера в дизассемблере IDA версию такой библиотеки под архитектуру x86, обнаруживаем, что внутри она состоит из криптованного и виртуализованного кода чуть менее, чем полностью, и версии под остальные архитектуры выглядят не лучше.

Это означает, что статическим анализом декомпилированного кода в нашем случае не обойтись, — придется каким‑то образом загружать программу в отладчик и исследовать работу защиты в динамике. Попробуем совместить приятное с полезным и решить еще одну сопутствующую задачу. Ты, вероятно, уже слышал, что серьезные исследования мобильных приложений проводятся на специально рутованных подопытных Android-устройствах, подключенных к компьютеру через отладочные интерфейсы. А мы люди жадные и небогатые, покупать телефон‑жертву специально для этой цели не будем.
Попробуем решить нашу задачу, не выходя за рамки Windows-компьютера, — будем использовать для этой цели Android-эмулятор. Для примера возьмем популярный условно‑бесплатный эмулятор BlueStacks. Я выбрал именно его по ряду соображений: во‑первых, как я сказал выше, он бесплатный, во‑вторых, прекрасно работает офлайн, а в‑третьих, поддерживает отладочный интерфейс ADB (не буду отвлекаться от повествования, объясняя, что это такое, — думаю, что ты все‑таки имеешь небольшой опыт реверса и отладки под Android и читал соответствующие статьи). И что самое главное, наше приложение прекрасно устанавливается и запускается на этом эмуляторе, хотя, как пишут в интернете, далеко не каждый эмулятор воспринимает Baidu, как родной. А раз так, то включаем в нашем BlueStacks доступ к отладочному интерфейсу ADB (напоминаю, для этого в настройках программы нужно установить флажок «Включить Android Debug Bridge (ADB)». Заодно обращаем внимание на адрес подключения 127.
.

Теперь загружаем и устанавливаем наше приложение в эмулятор (снова не буду подробно останавливаться на этом моменте: все подробно описано в интернете). После этого устанавливаем на свой компьютер Android Debug Bridge и пробуем по инструкции подключиться к эмулятору. Если ты все сделал правильно, то команда adb
покажет эмулятор в списке доступных устройств.

Теперь можно подключиться к нему, выполнив команду adb
. Это необязательно получится с первого раза, но если ты аккуратно следовал инструкции, то рано или поздно подключение будет установлено. Выполнив команду ps
, можно убедиться, что исследуемое приложение запущено и находится в списке активных процессов.
Раз так, попробуем проверить гипотезу, что расшифрованный ODEX-образ защищенного приложения находится в памяти процесса и его можно сдампить. На это указано в статье китайских студентов, кроме того, еще один китаец утверждал, что ему удалось это сделать. Конечно, если бы это было действительно так, все было бы чересчур просто. Возможно, лет десять назад, когда писались эти инструкции, метод работал, но, к сожалению, не в нашем случае. Я перепробовал все нагугленные способы дампа памяти процесса, и на BlueStacks у меня успешно сработал такой:
am dumpheap <process PID> /data/local/tmp/dump.bin
Однако в полученном файле dump.
ни малейших следов распакованного кода не обнаружилось. Похоже, мы вернулись на исходную: сдампить расшифрованные данные из памяти процесса нельзя, поскольку они там отсутствуют. Но что, если поискать эти данные не в памяти процесса, а в памяти эмулятора? Ведь BlueStacks — это обычное Windows-приложение, можно открыть его отладчиком x64dbg, получить доступ к памяти всех загруженных в эмулятор процессов и даже отслеживать оттуда их работу и загрузку.
Буквально на первом шаге по этому пути нас ожидает небольшая проблема: а к какому же процессу аттачиться? Запущенный эмулятор порождает сразу несколько активных процессов: BstkSVC
, HD-Agent
, HD-Player
, BlueStacksServices
(несколько экземпляров) и собственно Bluestacks
(их тоже, внезапно, несколько). Поскольку мы проверяем гипотезу, что расшифрованный образ находится в памяти процесса, смотрим, есть ли в памяти шестнадцатеричные идентификаторы используемых строк, на которые нет прямых ссылок в распакованном .
(0x7f0f009a
, 0x7F0F0693
, 0x7F0F0694
и так далее). Процедура достаточно муторная, ведь нам надо убедиться, что это не случайное вхождение 32-битной последовательности, а именно ссылка на правдоподобный далвиковский код, в котором используется эта константа. Но вот наше терпение вознаграждено — приаттачившись к процессу HD-Player
, мы видим вот такое вхождение константы 0x7F0F0694
(на скриншоте выделено красным).

Ищем расшифрованный образ
Почему нас заинтересовало именно это вхождение константы? Если внимательно посмотреть на соседний код, то предшествующие ей два байта (DW
, выделено желтым) представляют собой опкод Dalvik-команды const
, что намекает на осмысленный код функции, начало которой явно прослеживается несколькими строками выше:
.short 4 # Выделено оранжевым: Number of registers : 4
.short 3 # Выделено светло-зеленым: Size of input args (in words) : 3
.short 2 # Выделено голубым: Size of output args (in words) : 2
.short 0 # Выделено черным: Number of try_items : 0
.int 0x23D82B # Выделено синим: Адрес Debug info
.int 0x41 # Выделено темно-зеленым: Size of bytecode (in 16-bit units): 0x41
Из этой информации мы даже видим смещения до Debug info функций, анализируя которые и используя документацию по формату DEX мы можем при известном старании найти адреса начала секций Code
(на следующем рисунке выделено красным) и Debug
(на следующем рисунке выделено синим). А по их вхождениям — найти секцию Map.

В этой секции присутствует ссылка на саму себя (0x4CC338
, выделено серым). По ней уже находится заголовок всего расшифрованного DEX-образа.

Казалось бы, теперь мы победили хитрых китайцев, остается только взять из заголовка образа его размер (0x4CC414
, выделено серым) и сдампить его на диск для последующего препарирования. Однако и тут нас ждет страшный облом. Хоть расшифрованный образ и находится, по всей видимости, в памяти HD-Player
, но не одним непрерывным куском, а фрагментированно, разбросанными блоками размером, кратным 0x1000
байт. Cобрать такую мозаику руками чертовски сложно.
Нет, не невозможно, несмотря на то что количество этих блоков может быть свыше тысячи. Если хорошо поднапрячься и покопать виртуальную машину BlueStacks (а она в основном содержится в модулях BstkVMM.
, BstkRT.
и BstkDD.
), то можно разработать массу хитрых вариантов слежения за дисковыми операциями виртуального образа, в частности за выделением и чтением виртуальной памяти. В конце концов, можно даже контролировать исполнение кода в виртуальной песочнице. Возможно, я когда‑нибудь разберу этот тернистый путь на конкретном примере в своей очередной статье, но не в этот раз, поскольку он все‑таки чертовски сложен.
Призываем на помощь «Фриду»
Перейдем к следующему еще не использованному нами инструменту — Frida. Снова надеюсь на твою осведомленность, тем более про эту программу «Хакер» уже рассказывал. Поэтому коснусь лишь сложностей и проблем, связанных с работой Frida в сочетании с эмулятором BlueStacks. А их немало, и они весьма мерзкие.
Скажем прямо: в интернете бытует мнение, что Frida — не для эмуляторов, а BlueStacks ее вообще не поддерживает. Но мы любознательные и недоверчивые, в связи с чем попробуем разобраться в этом сами. Главное препятствие установки сервера Frida на BlueStacks — отсутствие у последнего рута «из коробки», а «Фриде», как известно, нужен именно рут или модифицированное приложение. Это вообще не наш вариант, поскольку Baidu к модификации относится болезненно и разбираться еще и с этой проблемой уж точно не входит в наши планы.
В интернете описано множество костыльных способов получения root на эмуляторе BlueStacks, большая часть из которых не работает из‑за закрытой для записи файловой системы (эта проблема нам еще попортит крови в дальнейшем). Поэтому не буду останавливаться на их обзоре, а приведу наиболее простой метод, который лично у меня более‑менее заработал на BlueStacks 4. Речь идет о BlueStacks Tweaker. Решение исключительно для ленивых: ничего никуда копировать и собирать не надо, просто загружаешь программу с относительно дружественным интерфейсом, жмешь несколько кнопок — и твой BlueStacks рутован.
Из плюсов — в эмуляторе теперь доступна команда su
и работает приложение SuperSU, которым можно и нужно предоставить права суперпользователя для приложений ADB Shell. И если ты хочешь использовать эмулятор терминала Termux для прохождения дальнейшего квеста установки Frida, то ему тоже нужно прописать права по этой инструкции.
Итак, мы преодолели первое препятствие на пути установки Frida на наш эмулятор, но цена этого преодоления оказалась достаточно высока: перезапустив рутованный BlueStacs, мы с ужасом обнаруживаем, что исследуемое приложение перестало запускаться, — оно сразу молча закрывается. Baidu наконец‑то понял, что сейчас его начнут ломать, и в ответ он принялся саботировать работу программы.
Казалось бы, смысл дальнейших телодвижений полностью отсутствует, но на всякий случай загружаем эмулятор в x64dbg и проверяем наличие расшифрованного DEX в памяти эмулятора. И он там есть! Перед тем как совершить сеппуку, Baidu успевает‑таки расшифровать наш DEX, а значит, есть смысл трудиться дальше, даже с учетом такой странной работы приложения.
Вдобавок, выполнив команду ps
, мы обнаруживаем, что приложение не рушится окончательно, несмотря на то что его окно мгновенно захлопывается при загрузке. Сам процесс в запущенном виде присутствует в общем списке, поэтому продолжаем двигаться дальше.
Теперь нам предстоит установить сервер Frida на эмулятор. C установкой клиента на компьютер по инструкции никаких проблем не возникает, чего не скажешь о сервере. Все приведенные выше инструкции предполагают установку питона на Android-устройство и сборку сервера, но, к сожалению, у BlueStacks и с этим возникают проблемы. Лично я сколько ни старался, так и не смог пройти шаг pip
.

В итоге я просто скачал уже скомпилированную версию Frida server под платформу x86 отсюда. Нужен именно сервер под x86, поскольку для BlueStacks это родная платформа. Самое странное, что ARM-сервер тоже запустится и даже будет работать, но чудить при этом станет изрядно. В общем, далее действуем по инструкции — закидываем сервер в эмулятор:
adb push frida-server-16.5.6-android-x86 /data/local/tmp/frida-server
После этого в Termux или ADB Shell даем права и запускаем сервер su
(после этой команды курсор приглашения командной строки должен смениться на #
):
cd /data/local/tmp
chmod 755 frida-server
./frida-server
Проверяем работоспособность сервера, выполнив из командной строки Windows команду frida-ps
. Она тоже может сработать не с первого раза, но, если все‑таки выдаст список процессов, в котором присутствует и наш исследуемый, значит, этот этап мы прошли.
Теперь для начала попробуем сдампить память процесса уже через Frida. Для этого воспользуемся утилитой fridump. К сожалению, и на этот раз чуда не происходит — в дампе памяти процесса никаких следов расшифрованного DEX нет. Это означает, что расшифрованный код присутствует в памяти только на краткий миг между загрузкой с диска с расшифровкой и компиляцией. К счастью, у нас теперь под рукой прекрасный инструмент, с использованием которого мы можем внедряться в любые, даже самые интимные места программы в любой момент ее исполнения. Давай подумаем, где может быть слабое место у китайской защиты, бОльшая часть кода которой зашифрована? Разумеется, это импортируемые символы. Ведь программа, по сути, расшифровывает код, может, она использует при этом какие‑то стандартные криптографические библиотеки? Снова открываем библиотеку baiduprotect.
в IDA и смотрим, что именно она импортирует из стандартных библиотек.

К сожалению, никакой криптографии в списке импортируемых функций с ходу не видно, однако бросаются в глаза сразу несколько функций работы с компрессией — inflate
, uncompress
и подобные. На использование компрессии как бы намекает и тот факт, что нам точно известен размер расшифрованного образа: 0x4CC414
— 5 030 932 байта. А бесхозных файлов такого размера в .
нету, да и внутри существующих DEX-образов пакета такой размер скрыть негде.
Самые подозрительные файлы (явно покриптованные или запакованные) под названием baiduprotect*.
находятся в каталоге Accets
рядом с библиотеками libbaiduprotect*.
, но они сильно меньше искомого размера. Попробуем проверить эту гипотезу — трассируем вызовы uncompress
в запущенной программе. Как я говорил ранее, несмотря на закрытие программы при запуске на рутованном BlueStacks, она продолжает висеть в активных процессах и ее видно при выполнении команды frida-ps
.
Сразу предупреждаю, что Frida сама по себе очень капризная штука, а в сочетании с BlueStacks вообще может взбрыкивать непредсказуемо, поэтому все действия надо выполнять крайне аккуратно:
- подключиться к BlueStacks по ADB;
- выполнив команду
ps
, убедиться, что исследуемое приложение запущено; - посмотреть его
PID
и классовое имя; - если у тебя установлен Termux, из него запустить
su
(курсор поменяется на#
); - потом запустить
/
(можноdata/ local/ tmp/ frida-server /
);data/ local/ tmp/ frida-server -D - после чего из командной строки Windows выполнить команду
frida-trace
(где-i "uncompress" -U 4577 4577
—PID
нашего процесса).
Frida-trace зависнет в ожидании, однако, если ткнуть в значок нашего приложения в BlueStacks, она выдаст три строки "uncompress", после которых обнаруживается исключение в libbaiduprotect.
. Выходит, что при запуске приложения libbaiduprotect
перед смертью трижды распаковывает некий код.
Поскольку в каталоге Assert
именно три зашифрованных файла (точнее, три пары) — baiduprotect1.
(baiduprotect1.
), baiduprotect2.
(baiduprotect2.
) и baiduprotect3.
(baiduprotect3.
), то, похоже, мы на верном пути. Чтобы окончательно убедиться в этом, напишем скрипт внедрения и перехвата вызова uncompress
.
Пишем скрипт
Для этого заглянем в маны и посмотрим на параметры этого вызова. Собственно, алгоритм скрипта будет следующий: на входе в uncompress
мы печатаем параметры dest
(адрес буфера распакованных данных), destLen
(длину распакованных данных), source
(адрес буфера упакованных данных), sourceLen
(длину упакованных данных). Ну и заодно адрес вызова, который нам может потом пригодиться для анализа кода. Запоминаем адреса буфера и счетчика распакованных данных, чтобы при выходе из функции сдампить их. В итоге получается такой скрипт:
const f = Module.getExportByName('libz.so', // Отслеживаемая функция uncompress библиотеки libz.so 'uncompress');// Указатель на буфер распаковкиvar buf;// Указатель на счетчик распакованных байтовvar buflen;// Дамп первых строк распакованных данныхfunction outputArray(data,len){ var cnt=0; for (var i=0;i<10;i++) { var st=""; if (cnt==len) break; for(var j=0;j<16;j++) { st+=('0' + (data[cnt] & 0xFF).toString(16)).slice(-2); st+=" "; cnt++; if (cnt==len) break; } console.log(st); }}Interceptor.attach(f, { // Код, выполняемый на входе в uncompress onEnter(args) { console.log('uncompress called from:\n' + // Откуда был вызов? Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join('\n') + '\n'); // Адрес буфера распаковки console.log('Bytef * dest: '+args[0]); // Указатель на счетчик распакованных байтов console.log('uLongf * destLen: '+args[1]); // Указатель на упакованные байты console.log('const Bytef * source: '+args[2]); // Длина упакованных данных console.log('uLong sourceLen: '+args[3]); // Запоминаем адрес буфера распаковки buf=args[0]; // Запоминаем указатель на счетчик распакованных байтов buflen=args[1]; }, // Код, выполняемый на выходе в uncompress onLeave(retval) { console.log('uncompress exited\n'); // Адрес буфера распаковки console.log('dest: '+buf); // Печатаем количество распакованных байтов console.log('destLen: '+buflen.readUInt()); const data = buf.readByteArray(buflen.readUInt()); // Дампим распакованные данные outputArray(new Uint8Array(data), buflen.readUInt()); }});
Сохраняем этот код в файл uncompress.
, который запускаем (аккуратно выполнив все указанные выше шаги) из Windows-консоли следующей командой:
frida -U -f <классовое имя нашего приложения, полученное командой ps> -l uncompress.js
В эмуляторе моргает и гаснет окошко запущенного приложения, и в консоль вываливается следующая простыня логируемых данных:
uncompress called from:
0x899d80cc libbaiduprotect.so!0x130cc
0xbf6633d0
Bytef * dest: 0x891fb000
uLongf * destLen: 0xbf84fb3c
const Bytef * source: 0x88fc9010
uLong sourceLen: 0x20457f
uncompress exited
dest: 0x891fb000
destLen: 5030932
64 65 78 0a 30 33 35 00 ee ca ca fd a3 5a 33 c8
93 99 93 ac 92 3f be ac ef 15 ba f6 6f 07 58 cc
14 c4 4c 00 70 00 00 00 78 56 34 12 00 00 00 00
00 00 00 00 38 c3 4c 00 78 b1 00 00 70 00 00 00
ab 18 00 00 50 c6 02 00 fb 1e 00 00 fc 28 03 00
4e 3e 00 00 c0 9c 04 00 c0 78 00 00 30 8f 06 00
d8 11 00 00 30 55 0a 00 e4 33 40 00 30 90 0c 00
30 90 0c 00 32 90 0c 00 36 90 0c 00 39 90 0c 00
4b 90 0c 00 59 90 0c 00 5c 90 0c 00 60 90 0c 00
6b 90 0c 00 95 90 0c 00 9f 90 0c 00 06 91 0c 00
uncompress called from:
0x899d80cc libbaiduprotect.so!0x130cc
0xbf6633d0
Bytef * dest: 0x89158000
uLongf * destLen: 0xbf84fb3c
const Bytef * source: 0x89101010
uLong sourceLen: 0x29992
uncompress exited
dest: 0x89158000
destLen: 409340
64 65 78 0a 30 33 35 00 49 f9 37 58 e0 82 9b db
ac eb 66 8c 61 1c 3d 36 dc 7c f7 a6 6f e3 e4 59
fc 3e 06 00 70 00 00 00 78 56 34 12 00 00 00 00
00 00 00 00 2c 3e 06 00 5e 13 00 00 70 00 00 00
df 02 00 00 e8 4d 00 00 ed 04 00 00 64 59 00 00
1d 06 00 00 80 94 00 00 6d 0e 00 00 68 c5 00 00
76 01 00 00 d0 38 01 00 6c d7 04 00 90 67 01 00
90 67 01 00 92 67 01 00 c0 67 01 00 e0 67 01 00
48 68 01 00 4b 68 01 00 5d 68 01 00 63 68 01 00
6e 68 01 00 71 68 01 00 80 68 01 00 8f 68 01 00
uncompress called from:
0x899d80cc libbaiduprotect.so!0x130cc
0xbf6633d0
Bytef * dest: 0x89127000
uLongf * destLen: 0xbf84fb3c
const Bytef * source: 0x890e3010
uLong sourceLen: 0x165d4
uncompress exited
dest: 0x89127000
destLen: 196864
64 65 78 0a 30 33 35 00 60 cc 07 49 66 67 41 78
97 05 88 65 0e a4 0b ea 46 ae 87 88 dc 40 93 14
00 01 03 00 70 00 00 00 78 56 34 12 00 00 00 00
00 00 00 00 30 00 03 00 1b 07 00 00 70 00 00 00
d8 01 00 00 dc 1c 00 00 3b 02 00 00 3c 24 00 00
7c 02 00 00 00 3f 00 00 ac 06 00 00 e0 52 00 00
ce 00 00 00 40 88 00 00 00 5f 02 00 00 a2 00 00
3a 46 02 00 3c 46 02 00 40 46 02 00 43 46 02 00
46 46 02 00 49 46 02 00 4c 46 02 00 4f 46 02 00
52 46 02 00 55 46 02 00 58 46 02 00 5b 46 02 00
Мы видим, что действительно при вызове uncompress
из libbaiduprotect.
(код, из которого выполняется вызов RVA=0x130cc
, тоже на этот момент расшифрован) распаковываются три зашифрованных DEX-модуля. Это понятно по заголовкам. Искомым модулем, судя по размеру, является самый большой — первый. Можно даже сравнить заголовок с тем, который мы получили при анализе памяти BlueStacks в x64dbg.

Выводы
Можно пить шампанское: мы победили хитрых китайцев, при этом ни один Android-девайс не пострадал. В заключение я кратко расскажу о дальнейших перспективах этого пути и великих проблемах, ожидающих тебя на нем.
Начнем с плохих новостей: несмотря на то что мы получили на блюдечке полный расшифрованный код модуля DEX одним куском, чтобы вытащить его из Frida, тебе придется еще повозиться. Потому как дамп в консоли — это, конечно, красиво и хорошо, но пять мегабайт бинарного кода так не извлечешь.
Разумеется, у Frida есть функция записи блока данных в файл (File.
), однако файл этот, по странному капризу разработчиков, должен находиться в файловой системе Android-устройства, и его в лучшем случае придется переносить на компьютер. При использовании BlueStacks вдобавок проблемой является общая закрытость файловой системы для записи, даже на рутованном виртуальном устройстве.
К примеру, у меня не получилось записать из Frida файл даже в /
, куда мы перед этим благополучно пересадили саму «Фриду». Разумеется, твой пытливый ум найдет массу других обходных способов извлекать распакованные данные, пусть это будет домашним заданием.
Хорошая же новость заключается в том, что при использовании Frida сами библиотеки перестают быть зашифрованным «черным ящиком». К примеру, ничего не мешает из вышеприведенного скрипта сдампить код самого libbaiduprotect
в расшифрованном виде, проанализировать его и на его основе написать, к примеру, свой собственный инструмент для расшифровки DEX безо всякого эмулятора и Frida.