- PVSM.RU - https://www.pvsm.ru -
Предыстория
Жило-было в Google Play Android приложение с несколькими тысячами пользователей. Через год понадобилось его обновить. Ок, запускаем Idea, выбираем «Build» — «Generate Signed APK». Вспоминаю что за это время успел пересесть в Linux, ничего страшного, выбираю файл с ключами, ввожу ранее заботливо записанный пароль… Не подходит. Хмм… Ввожу еще раз, еще… Перебор вариантов, переспрос коллег… Всё плохо.
В итоге потенциально три приложения зависли в Google play, ни один из вариантов не подходит. Вспоминаю, что Windows остался на dual-boot, перезагружаюсь туда, к счастью в этом экземпляре Idea остался сохраненный пароль.

Решение
Приложение успешно обновлено, но проблема с паролем осталась. Запрос в Jetbrains к сожалению не помог, поддержка ответила быстро, но ответ был в том духе что пароль восстановить не является возможным, дали ссылку на исходники [1] и предложили сделать свой хак. Что в общем то логично.
Ну что же, надо думать. Так как Idea это обычное java-приложение, то возникла мысль подключить свой код к тому месту, где из хранилища считываются пароли. После прочтения топика про javaagent [2] быстро набросал свой java agent который просто записывал в файл имена всех загружаемых классов. Все что нужно чтобы Idea запускалась с java agent, это прописать в файл idea.exe.vmoptions (или idea64.exe.vmoptions) строку вида
-javaagent:C:projectsagentoutartifactsagent_jaragent.jar
После запуска с агентом текстовый файл быстро наполнился строками вида
com/intellij/openapi/ui/impl/DialogWrapperPeerImpl$MyDialog
com/intellij/openapi/ui/impl/DialogWrapperPeerImpl$MyDialog$1
com/intellij/openapi/ui/impl/DialogWrapperPeerImpl$MyDialog$DialogRootPane
com/intellij/openapi/ui/impl/DialogWrapperPeerImpl$MyDialog$MyWindowListener
com/intellij/openapi/ui/DialogWrapper$19
com/intellij/openapi/ui/DialogWrapper$ErrorPaintingType
com/intellij/ide/wizard/AbstractWizard$1
Затем жму на «Generate Signed APK» и смотрю на вывод в файле:
org/jetbrains/android/exportSignedPackage/KeystoreStep
org/jetbrains/android/compiler/artifact/ApkSigningSettingsForm
org/jetbrains/android/exportSignedPackage/ExportSignedPackageWizardStep
Кажется, все нужное нам лежит в exportSignedPackage
Небольшое гугление, и находим исходники 2012 г. [3]
Здесь нас привлекает кусочек кода:
String password = passwordSafe.getPassword(project, KeystoreStep.class, KEY_STORE_PASSWORD_KEY);
if (password != null) {
myKeyStorePasswordField.setText(password);
}
password = passwordSafe.getPassword(project, KeystoreStep.class, KEY_PASSWORD_KEY);
if (password != null) {
myKeyPasswordField.setText(password);
}
Здесь видно, что пароли вытаскивается из защищенного хранилища и сохраняются в JPasswordField (стандартный контрол Swing для ввода паролей).
Осталось всего ничего — вытащить данные из текстовых полей. В этом нам поможет Javassist [4] — библиотека для манипулирования байт-кодом «на лету». Пишем в нашем java-agent следующий кусочек кода:
public byte[] transform(final ClassLoader loader, String className,
final Class classBeingRedefined, final ProtectionDomain protectionDomain,
final byte[] classfileBuffer) throws IllegalClassFormatException {
if ("javax/swing/JPasswordField".equals(className)) {
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("javax.swing.JPasswordField");
CtMethod m = cc.getDeclaredMethod("getPassword");
m.insertAfter("{System.out.println("password is: " + $_);}");
byte[] byteCode = cc.toBytecode();
cc.detach();
return byteCode;
} catch (Exception ex) {
ex.printStackTrace();
}
}
return classfileBuffer;
}
Что он делает? Перехватываем момент загрузки класса JPasswordField, находим в нем метод getPassword() и добавляем в конец метода наш фрагмент кода, который печатает в консоль искомый пароль ($_ это служебная переменная javassist, где лежит значение возвращаемое методом).
Таким нехитрым способом пароли были восстановлены и спасены.
P. S. А пароль оказался тем же самым, что и был записан, но вводился в русской раскладке. Всё было просто на самом деле…
Автор: bald2b
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/76705
Ссылки в тексте:
[1] исходники: https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java
[2] топика про javaagent: http://habrahabr.ru/post/230239/
[3] исходники 2012 г.: https://android.googlesource.com/platform/tools/adt/idea/+/e3cdd23fc8ac743f5d98e93ac62384be914f13a0/android/src/org/jetbrains/android/exportSignedPackage/KeystoreStep.java
[4] Javassist: http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/
[5] Источник: http://habrahabr.ru/post/245365/
Нажмите здесь для печати.