Androidキーストアのパスワードを紛失したが、Jetbrains Ideaを使用して回復できた方法

背景



昔々、数千人のユーザーがいるGoogle Play Androidアプリケーションがありました。 1年後、更新する必要がありました。 OK、アイデアを起動し、「ビルド」-「署名済みAPKを生成」を選択します。 この間、私はLinuxに転送することができたので、大丈夫です。キーでファイルを選択し、以前に慎重に記録したパスワードを入力します。 うーん...私はもう一度それを紹介しています...オプションの列挙、同僚の尋問...すべてが悪いです。



その結果、潜在的に3つのアプリケーションがGoogle Playにハングアップし、適切なオプションはありません。 Windowsがデュアルブートのままであり、そこで再起動したことを思い出します。幸いなことに、Ideaのこのインスタンスではパスワードが保存されていました。



署名済みAPK



解決策



アプリケーションは正常に更新されましたが、パスワードの問題は残りました。 残念ながらJetbrainsのリクエストは役に立たず、サポートは迅速に対応しましたが、答えはパスワードを回復することができないという精神にあり、 ソースへのリンクを提供し、独自のハックを提供することを申し出ました。 これは一般的に論理的です。



まあ、あなたは考える必要があります。 Ideaは通常のJavaアプリケーションであるため、コードをリポジトリからパスワードが読み取られる場所に接続するというアイデアでした。 javaagentに関するトピックを読んだ後、ロードされたすべてのクラスの名前をファイルに書き込むだけのJavaエージェントすばやくスケッチしました。 IdeaがJavaエージェントで開始するために必要なことは、idea.exe.vmoptionsファイル(またはidea64.exe.vmoptions)にフォームの行を書き込むことだけです。

-javaagent:C:\projects\agent\out\artifacts\agent_jar\agent.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年のソースコードを見つけました



ここにコードの一部があります:

  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に役立ちます。 次のコードを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ユーティリティ変数です)。



このような簡単な方法で、パスワードが復元され保存されました。



PSそして、パスワードは記録されたものと同じであることが判明しましたが、ロシア語のレイアウトで入力されました。 すべてが本当に...



All Articles