平和的なリバースエンジニアリングがYandex.Moneyアプリケーションのわずかな改善にどのように役立ったか

リバースエンジニアリングは、サングラスや光沢のある革のコートを着た邪悪なハッカーの活動であるというステレオタイプがあります。 壁の周りを走り回って、特殊部隊の群衆との白兵戦の合間に、これらのコンピューターの悪人は、プログラム、五角形、その他のデータベースの恐ろしいハッキングを行います。 ハック自体は通常、事前の準備を必要とせず、数秒で完了します。 そしてもちろん、理解できないOSを備えた地獄のハッカーラップトップの黒い画面でのほとんどすべてのハッキングの過程で、緑の詐欺師がcraい回り、そして/または何らかの種類の3Dがらくたが回ります...







今日、邪悪なコンピュータークラッカーに関する使い古されたハリウッドの決まり文句から離れて、親愛なる読者に、平和的なリバースエンジニアリングがYandex.Moneyアプリケーションをわずかに改善したことをお伝えします。 この話が、リバースエンジニアリングは必然的に悪いものであり、悪い人だけがそれを必要とするという安定したステレオタイプを揺るがすことを願っています。



1か月弱前に、Android用Yandex.Moneyバージョン1.71(当時の最新バージョン)をわずかに反転しました。 他の興味深いものの中で、不思議なメソッドru.yandex.core.CrashHandler.sendBug(String paramString)



が見つかりました:



SmaliメソッドのコードsendBug(String paramString)(非常に多く、言う必要があります)
 .class public abstract Lru/yandex/core/CrashHandler; .super Landroid/app/Activity; .source "CrashHandler.java" # ... #   -  # ... .method sendBug(Ljava/lang/String;)V .locals 5 .parameter "p1" .prologue .line 76 new-instance v0, Lorg/json/JSONObject; .line 79 .local v0, v0:Ljava/lang/Object; invoke-direct {v0}, Lorg/json/JSONObject;-><init>()V .line 84 .local v0, v0:Ljava/lang/Object; :try_start_5 const-string v1, "model" .line 87 .local v1, v1:Ljava/lang/Object; sget-object v2, Landroid/os/Build;->MODEL:Ljava/lang/String; .line 90 .local v2, v2:Ljava/lang/Object; invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;-> put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject; .line 93 const-string v1, "systemVersion" .line 95 sget-object v2, Landroid/os/Build$VERSION;->RELEASE:Ljava/lang/String; .line 97 invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;-> put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject; .line 100 const-string v1, "component" .line 102 const-string v2, "Android" .line 104 invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;-> put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject; .line 107 const-string v1, "appVersion" .line 109 invoke-static {}, Lru/yandex/core/CoreApplication;->getAppBuildIdFromNative()Ljava/lang/String; .line 111 move-result-object v2 .line 113 invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;-> put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject; .line 116 const-string v1, "appName" .line 118 invoke-static {}, Lru/yandex/core/CoreApplication;->getAppNameFromNative()Ljava/lang/String; .line 120 move-result-object v2 .line 122 invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;-> put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject; .line 125 const-string v1, "summary" .line 127 const-string v2, "Android Native Crash" .line 129 invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;-> put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject; :try_end_33 .catch Lorg/json/JSONException; {:try_start_5 .. :try_end_33} :catch_80 .line 137 .end local v2 #v2:Ljava/lang/Object; :goto_33 :try_start_33 new-instance v1, Lru/yandex/core/ClientHttpRequest; .line 140 .local v1, v1:Ljava/lang/Object; new-instance v2, Ljava/net/URL; .line 143 .local v2, v2:Ljava/lang/Object; new-instance v3, Ljava/lang/StringBuilder; .line 146 .local v3, v3:Ljava/lang/Object; const-string v4, "http://dmitriyap.dyndns.org:9091/rest/jconnect/latest/issue/create?project=" .line 149 .local v4, v4:Ljava/lang/Object; invoke-direct {v3, v4}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V .line 152 .local v3, v3:Ljava/lang/Object; invoke-virtual {p0}, Lru/yandex/core/CrashHandler;->getJiraProjectName()Ljava/lang/String; .line 154 move-result-object v4 .line 156 invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;-> append(Ljava/lang/String;)Ljava/lang/StringBuilder; .line 158 move-result-object v3 .line 160 invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; .line 162 move-result-object v3 .line 164 invoke-direct {v2, v3}, Ljava/net/URL;-><init>(Ljava/lang/String;)V .line 167 .local v2, v2:Ljava/lang/Object; invoke-direct {v1, v2}, Lru/yandex/core/ClientHttpRequest;-><init>(Ljava/net/URL;)V .line 171 .local v1, v1:Ljava/lang/Object; const-string v2, "issue" .line 173 const-string v3, "issue.json" .line 175 new-instance v4, Ljava/io/ByteArrayInputStream; .line 178 .local v4, v4:Ljava/lang/Object; invoke-virtual {v0}, Lorg/json/JSONObject;->toString()Ljava/lang/String; .line 180 move-result-object v0 .line 182 invoke-virtual {v0}, Ljava/lang/String;->getBytes()[B .line 184 move-result-object v0 .line 186 invoke-direct {v4, v0}, Ljava/io/ByteArrayInputStream;-><init>([B)V .line 189 .local v4, v4:Ljava/lang/Object; const-string v0, "application/json" .line 191 invoke-virtual {v1, v2, v3, v4, v0}, Lru/yandex/core/ClientHttpRequest;-> setParameter(Ljava/lang/String;Ljava/lang/String;Ljava/io/InputStream;Ljava/lang/String;)V .line 194 const-string v0, "crash" .line 196 const-string v2, "log.txt" .line 198 new-instance v3, Ljava/io/ByteArrayInputStream; .line 201 .local v3, v3:Ljava/lang/Object; invoke-virtual {p1}, Ljava/lang/String;->toString()Ljava/lang/String; .line 203 move-result-object v4 .line 205 invoke-virtual {v4}, Ljava/lang/String;->getBytes()[B .line 207 move-result-object v4 .line 209 invoke-direct {v3, v4}, Ljava/io/ByteArrayInputStream;-><init>([B)V .line 212 .local v3, v3:Ljava/lang/Object; invoke-virtual {v1, v0, v2, v3}, Lru/yandex/core/ClientHttpRequest;-> setParameter(Ljava/lang/String;Ljava/lang/String;Ljava/io/InputStream;)V .line 215 invoke-virtual {v1}, Lru/yandex/core/ClientHttpRequest;->post()Ljava/io/InputStream; :try_end_7d .catch Ljava/io/IOException; {:try_start_33 .. :try_end_7d} :catch_7e .line 222 .end local v1 #v1:Ljava/lang/Object; .end local v2 #v2:Ljava/lang/Object; .end local v3 #v3:Ljava/lang/Object; .end local v4 #v4:Ljava/lang/Object; :goto_7d return-void .line 226 :catch_7e move-exception v0 .line 228 goto :goto_7d .line 232 :catch_80 move-exception v1 .line 235 .local v1, v1:Ljava/lang/Object; goto :goto_33 .end method
      
      





Javaのような擬似コードの同じsendBug(String paramString)



メソッドをsendBug(String paramString)



メソッドは、 Java Decompillerを使用してdexファイルを操作した後に取得されます。



Javaのような擬似コードの同じメソッド
 package ru.yandex.core; # ... #  -  ,  # ... public abstract class CrashHandler extends Activity { # ... #   -  # ... void sendBug(String paramString) { JSONObject localJSONObject = new JSONObject(); try { localJSONObject.put("model", Build.MODEL); localJSONObject.put("systemVersion", Build.VERSION.RELEASE); localJSONObject.put("component", "Android"); localJSONObject.put("appVersion", CoreApplication.getAppBuildIdFromNative()); localJSONObject.put("appName", CoreApplication.getAppNameFromNative()); localJSONObject.put("summary", "Android Native Crash"); try { ClientHttpRequest localClientHttpRequest = new ClientHttpRequest( new URL("http://dmitriyap.dyndns.org:9091/rest/jconnect/latest/issue/create?project=" + getJiraProjectName())); localClientHttpRequest.setParameter("issue", "issue.json", new ByteArrayInputStream(localJSONObject.toString().getBytes()), "application/json"); localClientHttpRequest.setParameter("crash", "log.txt", new ByteArrayInputStream(paramString.toString().getBytes())); localClientHttpRequest.post(); return; } catch (IOException localIOException) { //  Java Decompiller   -  // ... } } catch (JSONException localJSONException) { //  ...    Java Decompiller  ,  // ... } } }
      
      





この擬似コードは、Java言語の構文の観点からは完全に有効ではありませんが、 sendBug(String paramString)



メソッドのロジックを明確に示しています。 このメソッドを呼び出すとき、特定のアドレスに dmitriyap.dyndns.org:9091



dmitriyap.dyndns.org:9091



ClientHttpRequest.post()



助けを借りて暗号化せずに、多くの異なる情報が送信されます。 特に、 paramString



渡されるparamString



引数はcrash



パラメーターで送信されます。 クエリ文字列と変数の名前から判断すると、 アトラシアン sendBug(String paramString)



は「反対側」で発生し、 sendBug(String paramString)



メソッドは送信されるすべての情報をすぐに追加する問題を作成します。 つまり 実際、 sendBug(String paramString)



メソッドは、その名前が示すとおりに機能します。バグトラッカーでバグレポートを開発者に送信します。 大丈夫のようです、多くのプログラムがこれを行います。 ただし、メソッド自体のコードには疑問が生じます。



  1. ドメインの所有者 dmitriyap.dyndns.org



    dmitriyap.dyndns.org



    ? これは明らかにYandexの企業ドメインではありません。
  2. どのsendBug(String paramString)



    情報がsendBug(String paramString)



    引数のsendBug(String paramString)



    メソッドに渡され、したがってに送信されるか dmitriyap.dyndns.org



    dmitriyap.dyndns.org



  3. Yandex.MoneyプログラムsendBug(String paramString)



    どのような条件下でsendBug(String paramString)



    メソッドを呼び出しますか?


最初の質問に対する答えは十分に速いです。 小さなGoogle検索でdmitriyapがYandexのモバイルサービス開発部長のインターネットニックネームであることが示唆されています。 おそらくドメイン dmitriyap.dyndns.org



dmitriyap.dyndns.org



登録したのは彼 dmitriyap.dyndns.org



。 データが何らかの方法で暗号化されず、dyndns.orgサブドメインに送信され、Yandexドメインに送信されないという事実は、このバグ報告システム全体がYandex.Money Androidアプリケーションの開発者によって急いで作られたことを示唆しています。 「。 これはおそらく開発プロセスで使用されたものであり、リリースされるべきではありませんでした。 しかし、おそらく見落としのために、私はそれを得た。



最初の質問については多かれ少なかれ明確です。 2番目の質問にsendBug(String paramString)



ましょう。どのsendBug(String paramString)



情報がsendBug(String paramString)



引数でsendBug(String paramString)



メソッドに渡され、次に送信されますか dmitriyap.dyndns.org



dmitriyap.dyndns.org



? これを行うには、まず、匿名のCrashHandler$1



内部クラスのdoInBackground(...)



メソッドのコードを調べます。



SmaliメソッドコードdoInBackground(...)
 .field log:Ljava/lang/String; .method protected varargs doInBackground([Ljava/lang/Void;)Ljava/lang/Void; .locals 5 .parameter "p1" .prologue .line 59 const/4 v4, 0x1 .line 64 .local v4, v4:I :try_start_1 invoke-static {}, Ljava/lang/Runtime;->getRuntime()Ljava/lang/Runtime; .line 66 move-result-object v0 .line 69 .local v0, v0:Ljava/lang/Object; const/4 v1, 0x4 .line 72 .local v1, v1:B new-array v1, v1, [Ljava/lang/String; .line 75 .local v1, v1:Ljava/lang/Object; const/4 v2, 0x0 .line 78 .local v2, v2:Ljava/lang/Object; const-string v3, "logcat" .line 81 .local v3, v3:Ljava/lang/Object; aput-object v3, v1, v2 .line 83 const/4 v2, 0x1 .line 86 .local v2, v2:I const-string v3, "-d" .line 88 aput-object v3, v1, v2 .line 90 const/4 v2, 0x2 .line 93 .local v2, v2:B const-string v3, "-v" .line 95 aput-object v3, v1, v2 .line 97 const/4 v2, 0x3 .line 99 const-string v3, "threadtime" .line 101 aput-object v3, v1, v2 .line 103 invoke-virtual {v0, v1}, Ljava/lang/Runtime;->exec([Ljava/lang/String;)Ljava/lang/Process; .line 105 move-result-object v0 .line 107 iput-object v0, p0, Lru/yandex/core/CrashHandler$1;->process:Ljava/lang/Process; .line 110 iget-object v0, p0, Lru/yandex/core/CrashHandler$1;->process:Ljava/lang/Process; .line 112 invoke-virtual {v0}, Ljava/lang/Process;->getInputStream()Ljava/io/InputStream; .line 114 move-result-object v0 .line 116 invoke-virtual {p0, v0}, Lru/yandex/core/CrashHandler$1;-> readAllOf(Ljava/io/InputStream;)Ljava/lang/String; .line 118 move-result-object v0 .line 120 iput-object v0, p0, Lru/yandex/core/CrashHandler$1;->log:Ljava/lang/String; :try_end_2e .catch Ljava/io/IOException; {:try_start_1 .. :try_end_2e} :catch_30 .line 127 .end local v2 #v2:B .end local v3 #v3:Ljava/lang/Object; :goto_2e const/4 v0, 0x0 .line 130 .local v0, v0:Ljava/lang/Object; return-object v0 .line 135 .end local v0 #v0:Ljava/lang/Object; .end local v1 #v1:Ljava/lang/Object; :catch_30 move-exception v0 .line 139 .local v0, v0:Ljava/lang/Object; iget-object v1, p0, Lru/yandex/core/CrashHandler$1;-> this$0:Lru/yandex/core/CrashHandler; .line 142 .local v1, v1:Ljava/lang/Object; invoke-virtual {v0}, Ljava/io/IOException;->toString()Ljava/lang/String; .line 144 move-result-object v0 .line 146 invoke-static {v1, v0, v4}, Landroid/widget/Toast;-> makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; .line 148 move-result-object v0 .line 150 invoke-virtual {v0}, Landroid/widget/Toast;->show()V .line 152 goto :goto_2e .end method
      
      





Java Decompillerを使用して取得した対応するJavaのような擬似コード:



Javaのような擬似コードの同じメソッド
 String log; protected Void doInBackground(Void[] paramArrayOfVoid) { try { Runtime localRuntime = Runtime.getRuntime(); String[] arrayOfString = new String[4]; arrayOfString[0] = "logcat"; arrayOfString[1] = "-d"; arrayOfString[2] = "-v"; arrayOfString[3] = "threadtime"; this.process = localRuntime.exec(arrayOfString); this.log = readAllOf(this.process.getInputStream()); return null; } catch (IOException localIOException) { //  Java Decompiller    ,          // ... } }
      
      





この擬似コードは、Java言語の構文の観点からは完全に有効ではありませんが、 doInBackground(...)



機能は明らかです。 Adnroidデバイスでコマンドラインを起動します

 logcat -d -v threadtime
      
      



次に、 readAllOf(...)



メソッド(同じクラスで定義readAllOf(...)



を使用して、出力をキャプチャし、 String



型のlog



フィールドに文字列として配置します。 この行には何がありますか? その中には、とりわけ、個人ユーザーデータの束-支払い履歴、いくつかのプライベートCookieなどがあります。 例えば、ここに小さな断片があります(データは私のものであり、もちろんそれらは塗りつぶされています):







通常のlogcatログはどこから大量の個人データを取得していますか? 問題は、Yandex.Moneyアプリケーションコードにandroid.util.Log.d(...)



呼び出しがちりばめられていることandroid.util.Log.d(...)



。 アプリケーションの過程で、ユーザーの個人情報を含むあらゆる種類の情報がログに書き込まれます。 なんで? わからない、わからない...開発プロセス中にアプリケーションをデバッグするために使用された可能性があり、これらの呼び出しはリリースから削除されるのを忘れていました。



しかし、2番目の質問に戻ります。 doInBackground(...)



呼び出した後、 log



フィールドの個人データの束を含むこの行はどこに行くのですか? 信じられないsendBug(String paramString)



ませんが、 sendBug(String paramString)



引数でsendBug(String paramString)



メソッドに渡されてから、 dmitriyap.dyndns.org



dmitriyap.dyndns.org



暗号化されていません。 これを確認するには、同じ匿名CrashHandler$1



内部クラスのonPostExecute(...)



メソッドコードを見てください。



onPostExecuteのSmaliメソッドコード(...)
 .method protected onPostExecute(Ljava/lang/Void;)V .locals 2 .parameter "p1" .prologue .line 188 iget-object v0, p0, Lru/yandex/core/CrashHandler$1;->this$0:Lru/yandex/core/CrashHandler; .line 191 .local v0, v0:Ljava/lang/Object; iget-object v1, p0, Lru/yandex/core/CrashHandler$1;->log:Ljava/lang/String; .line 194 .local v1, v1:Ljava/lang/Object; invoke-virtual {v0, v1}, Lru/yandex/core/CrashHandler;->sendBug(Ljava/lang/String;)V .line 197 iget-object v0, p0, Lru/yandex/core/CrashHandler$1;->val$progress:Landroid/app/ProgressDialog; .line 199 invoke-virtual {v0}, Landroid/app/ProgressDialog;->dismiss()V .line 202 iget-object v0, p0, Lru/yandex/core/CrashHandler$1;->this$0:Lru/yandex/core/CrashHandler; .line 204 invoke-virtual {v0}, Lru/yandex/core/CrashHandler;->finish()V .line 207 const/4 v0, 0x0 .line 210 .local v0, v0:Ljava/lang/Object; invoke-static {v0}, Ljava/lang/System;->exit(I)V .line 213 return-void .end method
      
      





Java Decompillerを使用して取得した対応するJavaのような擬似コード:



Javaのような擬似コードの同じメソッド
  protected void onPostExecute(Void paramVoid) { this.this$0.sendBug(this.log); this.val$progress.dismiss(); this.this$0.finish(); System.exit(0); }
      
      





そこで、2番目の質問に答えました。 おもしろいですね。 Yandex.Moneyには、特定のsendBug(String paramString)



メソッドsendBug(String paramString)



があり、多数のユーザー個人データを含むlogcatログを dmitriyap.dyndns.org



暗号化されていない形式の dmitriyap.dyndns.org







この場合、3番目の質問は、Yandex.Moneyプログラムがどのような条件下でこの恐ろしいsendBug(String paramString)



メソッドを呼び出すのかということです。 -特に興味深いものになります。 アプリケーションコードを徹底的に研究した後、正しい答えが得られます。



sendBug(String paramString)メソッドはどのような状況でも呼び出されません! 絶対に!



はい、このメソッドは呼び出されません。 これはデッドコードです。 Yandex.Moneyアプリケーションコードの注意深い研究(ここでは長くて退屈なので)は、 sendBug(String paramString)



のネイティブコンポーネントがlibcache_local.so



たときにsendBug(String paramString)



メソッドが呼び出されたとlibcache_local.so



(コンポーネントはYandex.Mapsとの対話を担当します)。 しかし、メソッド自体は忘れられていましたが、呼び出しは削除されました。 したがって、Yandex.Moneyアプリケーションは、個人データをどこにも送信しません。 そして、Yandexユーザーは安全です。



恐らく、冒頭で述べたダークグラスと光沢のあるレザーコートの非常に邪悪なコンピュータークラッカーは失望しています。 彼らは、私がYandex.Moneyでバックドアを見つけた方法を教えてくれるかもしれないし、おそらくこのバックドアの鍵を与えることさえ期待していたでしょう。 しかし、みんな! バックドアはありません(少なくともここでは)。 馬鹿げていますが、デッドコードがあり、私たちの平和的なリバースエンジニアリングがそれを明らかにしました。



Yandexへのレポート(チケット#12092801010226151)で上記のすべてを述べました。 sendBug(String paramString)



メソッドはユーザーにとって安全であるという事実にもかかわらず、Yandex.Moneyでのこのメソッドの存在自体は無秩序な混乱です。 さらに、アプリケーションはlogcatログに多数の個人ユーザーデータを書き込みます。 その結果、Yandexセキュリティチームとメールで上手にチャットできました。 すでに次のYandex.Moneyリリースバージョン1.80では、まもなく公開されましたが、前述のすべての欠点は修正されました。アプリケーションはユーザーの個人データをlogcatログに書き込まなくなり、 sendBug(String paramString)



高速メソッドは削除されました。 したがって、私たちの平和的なリバースエンジニアリングは、Yandex.Moneyを少し良くするのに役立ちました。



平和的なリバースエンジニアリングについての私の話があなたを楽しませてくれたことを願っていますが、少し長く混乱していることが判明しました。 エンディングを漏らしたように思われたら、すみません。



ハッピーデバッグ!



PSそして-はい、バージョン1.80では、YandexはついにYandex.Money AndroidアプリケーションのJavaコードを難読化しました。 大変な時間でした。



All Articles