Грязный Джо. Взламываем Java-приложения с помощью dirtyJOE

Спо­собы обхо­да три­ала в раз­личных прог­раммах — одна из самых инте­рес­ных тем прик­ладно­го реверс‑инжи­нирин­га, и я не уже не раз пос­вящал ей свои статьи. Нас­тало вре­мя вер­нуть­ся к этой темати­ке сно­ва. Наш сегод­няшний паци­ент — при­ложе­ние, выпол­ненное в виде JAR-модуля, которое мы иссле­дуем без пол­ного ревер­са и перес­борки про­екта.

warning

Статья име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для спе­циалис­тов по безопас­ности, про­водя­щих тес­тирова­ние в рам­ках кон­трак­та. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Рас­простра­нение вре­донос­ных прог­рамм, наруше­ние работы сис­тем и наруше­ние тай­ны перепис­ки прес­леду­ются по закону.

В замет­ке «В обход стра­жи. Отла­жива­ем код на PHP, упа­кован­ный SourceGuardian» мы рас­смат­ривали прог­рамму, реали­зован­ную в виде локаль­ного веб‑интерфей­са. Работа­ет она так: под Windows запус­кает­ся локаль­ный сер­вер Apache c набором PHP-модулей, а поль­зователь вза­имо­дей­ству­ет с при­ложе­нием через бра­узер, в котором набира­ет адрес localhost. Прог­рамма, взло­мом которой мы зай­мем­ся сегод­ня, дей­ству­ет похожим обра­зом, толь­ко написа­на она на Java и пос­тавля­ется в виде фай­ла .JAR. Наша задача — оту­чить при­ложе­ние от деморе­жима.

По счастью, нам извес­тно, где лежат стар­тующие в виде сер­виса исполня­емые модули прог­раммы в фор­мате .EXE и соот­ветс­тву­ющий JAR-файл. По сво­ей сути JAR — это обыч­ный ZIP-архив, в который упа­кова­ны час­ти про­екта. Пос­коль­ку мы собира­емся пра­вить код, нас инте­ресу­ют модули *.CLASS, содер­жащие откомпи­лиро­ван­ный JVM-байт‑код. Деком­пилято­ров и спо­собов их при­мене­ния мно­жес­тво, сущес­тву­ют даже инс­тру­мен­ты вро­де JD-GUI, спо­соб­ные пол­ностью вос­ста­новить про­ект из исполня­емо­го фай­ла. Чаще все­го взлом­щики исполь­зуют обще­извес­тный JAD, который из‑за его рас­простра­нен­ности лов­кие обфуска­торы дав­но научи­лись обма­нывать, что, в свою оче­редь, ста­ло при­чиной появ­ления более прод­винутых деком­пилято­ров вро­де CFR. Эта вой­на щитов и мечей, пуль и бро­нежи­летов обе­щает быть дол­гой, нам оста­ется толь­ко запас­тись поп­корном. Но не будем тут оста­нав­ливать­ся, а вмес­то это­го пред­положим, что мы деком­пилиро­вали про­ект одним из опи­сан­ных спо­собов до Java-исходни­ков и даже про­ана­лизи­рова­ли получен­ный код.

При­мени­тель­но к нашему подопыт­ному при­ложе­нию это выг­лядело при­мер­но так. Деком­пилиро­вав все‑все‑все CLASS-фай­лы, мы так и не обна­ружи­ли ничего похоже­го на обра­щение к лицен­зии, одна­ко в под­катало­ге BOOT-INF/lib нашего JAR-архи­ва наш­лось мно­жес­тво упа­кован­ных JAR-биб­лиотек, сре­ди которых сра­зу бро­силась в гла­за биб­лиоте­ка license-1.2.12.jar. Рас­паковав и деком­пилиро­вав ее, мы нат­кну­лись на два CLASS-модуля, содер­жащих две любопыт­ные фун­кции. Одна воз­вра­щает демонс­тра­цион­ный режим, вто­рая акти­виру­ет опцию 1 по умол­чанию:

public boolean isDemo() { return this.getPublicDataHash().isEmpty();}public void setDefault() { if (this.hasModule(1)) { Iterator iter = this.modulesItems.entrySet().iterator(); while (iter.hasNext()) { Map.Entry item = iter.next(); if ((Integer)item.getKey() == 1) continue; ((BaseModule)item.getValue()).close(); iter.remove(); this.onModuleUpdated((Integer)item.getKey()); } } else { this.closeModules(); if (!this.modulesConfig.containsKey(1)) { return; } BaseModule mod = this.getModule(1); if (mod != null) { mod.setEnabled(true); this.modulesItems.put(1, mod); log.info("Default module loaded {}", (Object)mod.getName()); this.onModuleUpdated(1); } }}

На­ша задача — сде­лать так, что­бы фун­кция isDemo всег­да воз­вра­щала false, а в фун­кции setDefault нуж­но заменить опцию 1 опци­ей 256. Вот здесь и начина­ется самое инте­рес­ное, то, ради чего и написа­на эта статья.

Ты спро­сишь: раз у нас име­ются в наличии все исходни­ки и код, то почему бы прос­то не переком­пилиро­вать весь про­ект, поменяв эти две про­цеду­ры на нуж­ные? К сожале­нию, пря­мой метод не всег­да самый прос­той. В нашем слу­чае в инте­ресу­ющих нас модулях мно­го зависи­мос­тей, а про­ект очень боль­шой, мно­гие модули силь­но обфусци­рова­ны. Кро­ме того, код вос­ста­новил­ся час­тично с кучей оши­бок, из‑за чего про­ект пол­ностью не соберет­ся. Мож­но, конеч­но, покопать обфуска­цию и поп­робовать руками вытащить исходный текст прог­раммы, но решать эту (воз­можно, даже, гораз­до более слож­ную) задачу ради двух прос­тых пат­чей в коде как‑то лень. Вдо­бавок, перес­борке про­екта может помешать отсутс­твие уста­нов­ленно­го JDK на компь­юте­ре. Уста­нав­ливать его и раз­бирать­ся в осо­бен­ностях ком­пиляции Java-про­ектов мне тоже неохо­та. Поэто­му мы, как обыч­но, ищем самый прос­той путь — патч откомпи­лиро­ван­ного JVM-кода.

В этом нам поможет инте­рес­ная, но мало­извес­тная ути­лита dirtyJOE. Откры­ваем в ней наш CLASS-модуль, на вклад­ке Methods видим пол­ный спи­сок методов клас­са. Находим в нем иско­мую isDemo и тыка­ем в нее, откры­вая окно редак­тирова­ния.

Ок­но редак­тирова­ния dirtyJOE

Это, конеч­но, не исходник на Java, но здесь хотя бы мож­но редак­тировать байт‑код, све­ряясь с логикой исходни­ка. Воз­можнос­ти прог­раммы минима­лис­тичны: редак­тировать мож­но толь­ко в виде hex-зна­чений кодов инс­трук­ций. По счастью, мне­мони­ка и опи­сание текущей исправ­ленной инс­трук­ции отоб­ража­ется в окош­ке над окном кода, а сам спи­сок инс­трук­ций с опи­сани­ем каж­дой име­ется в хел­пе (при­чем толь­ко спи­сок, без опко­дов: явно, что­бы хакерам жизнь медом не казалась и приш­лось искать шес­тнад­цатерич­ные опко­ды инс­трук­ций самос­тоятель­но). По сути, нам надо закоро­тить дан­ную фун­кцию, сде­лав воз­вра­щаемым зна­чени­ем 0 (false). Находим в таб­лице инс­трук­цию помеще­ния 0 на стек (iconst_0), ее опкод (3) и ста­вим ее в самое начало метода, а пос­ле нее — сра­зу воз­врат (ireturn).

Ис­прав­ляем инс­трук­цию

Зак­рыва­ем окно редак­тирова­ния, сох­раня­ем CLASS-модуль, затем меня­ем исправ­ленный модуль в архи­ве license-1.2.12.jar, который, в свою оче­редь, копиру­ем на мес­то ста­рого в основном JAR-модуле. С пред­вку­шени­ем переза­пус­каем прог­рамму и обна­ружи­ваем, что она не работа­ет. Мы что‑то сде­лали не так.

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *