HTML Diff
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 &amp;&amp; info.signatures.length &gt; 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 &amp;&amp; info.signatures.length &gt; 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