0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Теги: android, signature, андроид, подпись, ndk, native</p>
1
<p>Теги: android, signature, андроид, подпись, ndk, native</p>
2
<p>Несколько лет назад обнаружил, что в интернете появляются свежие версии моих, слегка изменённых (была убрана монетизация),<strong>apk</strong>буквально спустя пару часов после публикации версии. Был очень заинтересован этим, т. к. в моём приложении были проверки подписи в разных местах, что-то вроде:</p>
2
<p>Несколько лет назад обнаружил, что в интернете появляются свежие версии моих, слегка изменённых (была убрана монетизация),<strong>apk</strong>буквально спустя пару часов после публикации версии. Был очень заинтересован этим, т. к. в моём приложении были проверки подписи в разных местах, что-то вроде:</p>
3
PackageInfo info = null; try { info = getPackageManager() .getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } if (null != info && info.signatures.length > 0) { byte[] rawCertJava = info.signatures[0].toByteArray(); }<p>Исследовав свои сломанные apk-файлы, я обнаружил, что для обхода проверок подписи был создан класс PmsHookApplication, который наследовал существующий класс Application и в себе хранил набор байт моей оригинальной подписи, реализацию прокси класса PackageManager, который всегда возвращал этот набор байт оригинальной подписи.</p>
3
PackageInfo info = null; try { info = getPackageManager() .getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } if (null != info && info.signatures.length > 0) { byte[] rawCertJava = info.signatures[0].toByteArray(); }<p>Исследовав свои сломанные apk-файлы, я обнаружил, что для обхода проверок подписи был создан класс PmsHookApplication, который наследовал существующий класс Application и в себе хранил набор байт моей оригинальной подписи, реализацию прокси класса PackageManager, который всегда возвращал этот набор байт оригинальной подписи.</p>
4
<p>В гугле обнаружил<a>исходники инструмента</a>, который вносил все эти изменения автоматически.</p>
4
<p>В гугле обнаружил<a>исходники инструмента</a>, который вносил все эти изменения автоматически.</p>
5
<p>Этот инструмент может быть использован пользователями, обладающими минимальными техническими знаниями путём запуска файлов скриптов<a>'run.bat'</a>or<a>'run.sh'</a>.</p>
5
<p>Этот инструмент может быть использован пользователями, обладающими минимальными техническими знаниями путём запуска файлов скриптов<a>'run.bat'</a>or<a>'run.sh'</a>.</p>
6
<p>В результате простоты его использования и универсальности обхода всевозможных проверок подписи (избавлял от поиска этих проверок, т. к. все эти проверки считали, что apk не изменялся и не срабатывали), этот инструмент получил широкое<a>распространение в определённых кругах</a>любителей создания модификаций apk.</p>
6
<p>В результате простоты его использования и универсальности обхода всевозможных проверок подписи (избавлял от поиска этих проверок, т. к. все эти проверки считали, что apk не изменялся и не срабатывали), этот инструмент получил широкое<a>распространение в определённых кругах</a>любителей создания модификаций apk.</p>
7
<p>На рисунке ниже изображен результат работы скрипта:</p>
7
<p>На рисунке ниже изображен результат работы скрипта:</p>
8
<p>На рисунке ниже часть манифеста<strong>до изменения</strong>инструментом обхода проверок подписи:</p>
8
<p>На рисунке ниже часть манифеста<strong>до изменения</strong>инструментом обхода проверок подписи:</p>
9
<p>На рисунке ниже часть манифеста<strong>после изменения</strong>инструментом обхода проверок подписи (видно указание на внедрённый класс<strong>PmsHookApplication</strong>):</p>
9
<p>На рисунке ниже часть манифеста<strong>после изменения</strong>инструментом обхода проверок подписи (видно указание на внедрённый класс<strong>PmsHookApplication</strong>):</p>
10
<p>Сам класс PmsHookApplication, как он выглядит в dex:</p>
10
<p>Сам класс PmsHookApplication, как он выглядит в dex:</p>
11
<p>Пример того, как выглядит набор байт подписи в этом классе:</p>
11
<p>Пример того, как выглядит набор байт подписи в этом классе:</p>
12
<p>Решил реализовать проверку подписи с помощью NDK используя чистый си, т. к. реверс-инженеров, которые могут декомпилировать нативные исполняемые файлы, значительно меньше. Игра в кошки-мышки :)</p>
12
<p>Решил реализовать проверку подписи с помощью NDK используя чистый си, т. к. реверс-инженеров, которые могут декомпилировать нативные исполняемые файлы, значительно меньше. Игра в кошки-мышки :)</p>
13
<p>Сама проверка состоит из нескольких стадий: ● получаем путь апк файла; ● извлекаем 'META-INF/CERT.RSA' из apk с помощью zlib; ● парсим 'META-INF/CERT.RSA'; ● проверям набор байт из подписи либо в нативном слое, либо передаем его в Java-слой приложения.</p>
13
<p>Сама проверка состоит из нескольких стадий: ● получаем путь апк файла; ● извлекаем 'META-INF/CERT.RSA' из apk с помощью zlib; ● парсим 'META-INF/CERT.RSA'; ● проверям набор байт из подписи либо в нативном слое, либо передаем его в Java-слой приложения.</p>
14
<p><strong>Результат работы проверки</strong>можно увидеть ниже:</p>
14
<p><strong>Результат работы проверки</strong>можно увидеть ниже:</p>
15
<p>1) до применения<strong>nsktool</strong>(инструмент подделки подписи):</p>
15
<p>1) до применения<strong>nsktool</strong>(инструмент подделки подписи):</p>
16
<p>2) после применения nsktool (инструмент подделки подписи):</p>
16
<p>2) после применения nsktool (инструмент подделки подписи):</p>
17
<p>Полную версию кода проверки можно увидеть<a>тут</a>.</p>
17
<p>Полную версию кода проверки можно увидеть<a>тут</a>.</p>
18
<p>На момент написания заметки известен следующий путь обхода: - атакующие подменяют путь к подписи и указывают путь к оригинальной подписи, которая имеет уже какое-нибудь другое имя.</p>
18
<p>На момент написания заметки известен следующий путь обхода: - атакующие подменяют путь к подписи и указывают путь к оригинальной подписи, которая имеет уже какое-нибудь другое имя.</p>
19
<p>Для предотвращения этого: - скрывайте чувствительные строковые константы в коде; - используйте при компиляции флаг -fvisibility=hidden.</p>
19
<p>Для предотвращения этого: - скрывайте чувствительные строковые константы в коде; - используйте при компиляции флаг -fvisibility=hidden.</p>
20
20