PrehistoryOnce upon a time there was a Google Play Android app with several thousand users. A year later it was necessary to update it. Ok, we launch Idea, we select "Build" - "Generate Signed APK". I remember that during this time I managed to transfer to Linux, that's okay, I select the file with the keys, enter the previously carefully written password ... It does not work. Hmm ... I enter again, again ... Busting through options, asking colleagues again ... Everything is bad.
As a result, potentially three applications are stuck in Google play, none of the options are suitable. I remember that Windows stayed on dual-boot, I reboot there, fortunately in this instance of Idea there is a saved password.

')
DecisionThe application was successfully updated, but the problem with the password remained. The request to Jetbrains unfortunately did not help, the support responded quickly, but the answer was in the spirit that the password could not be recovered, they gave a link to the
source code and offered to make their hack. What in general is logical.
Well, I must think. Since Idea is a regular java application, the idea arose to connect your code to the place where passwords are read from the repository. After reading the
topic about the javaagent, I quickly sketched my java agent, which simply wrote down the names of all the loaded classes in the file. All that is needed for Idea to start with the java agent is to enter a line of the form in the file idea.exe.vmoptions (or idea64.exe.vmoptions)
-javaagent:C:\projects\agent\out\artifacts\agent_jar\agent.jar
After launching with the agent, the text file was quickly filled with lines of the form
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
Then I click on “Generate Signed APK” and look at the output in the file:
org/jetbrains/android/exportSignedPackage/KeystoreStep org/jetbrains/android/compiler/artifact/ApkSigningSettingsForm org/jetbrains/android/exportSignedPackage/ExportSignedPackageWizardStep
It seems that everything we need is in
exportSignedPackageA little google, and we find the
source of 2012Here we are attracted to a piece of code:
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); }
Here you can see that the passwords are pulled out from the secure storage and are saved in JPasswordField (standard Swing control for entering passwords).
All that remains is to pull the data out of the text fields.
Javassist , a library for manipulating bytecode on the fly, will help us in this. We write in our java-agent the following piece of code:
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; }
What is he doing? We intercept the moment of loading the
JPasswordField class, find the
getPassword () method in it and add our code fragment to the end of the method, which prints the required password to the console (
$ _ is the javassist service variable where the value returned by the method lies).
In such an uncomplicated way, passwords were recovered and saved.
PS And the password was the same as it was recorded, but entered in the Russian layout. Everything was just really ...