はい、タイトルどおりなわけですが、1.9.6のUpdateからMalware対策ソフトによって検知される事はなくなりそうです。
ApperHand自体C&Cサーバが沈黙してたりしたので機能していないと見て外したのか、Malwareの指摘を受けて外したのか、いずれにしてもこれで多少リスクは軽減された事になります。
ただし、他の広告モジュールは以前入っていますので、広告モジュールの端末識別情報や位置情報送信によるリスクは変わりませんのでその点はご注意ください。
はい、タイトルどおりなわけですが、1.9.6のUpdateからMalware対策ソフトによって検知される事はなくなりそうです。
ApperHand自体C&Cサーバが沈黙してたりしたので機能していないと見て外したのか、Malwareの指摘を受けて外したのか、いずれにしてもこれで多少リスクは軽減された事になります。
ただし、他の広告モジュールは以前入っていますので、広告モジュールの端末識別情報や位置情報送信によるリスクは変わりませんのでその点はご注意ください。
マルウェア対策ソフトで検知されても、アプリが持っている危険性に対して信じようとしない(信じたくない)方がいらっしゃるようなので…。
超光懐中電灯無料アプリは、起動するたびに端末を特定する情報と共に現在の位置情報を広告企業に送っていますし、いまでも遠隔操作用の命令を送り込むサーバに接続を試みています。
それでもご自身の端末で使うことを厭わない方は、ご自身の責任でご使用ください。でも、「(リスクすら説明せずに無邪気に)他人に勧めるのはやめてください」。
以下、かなり大雑把ですが超光懐中電灯無料アプリの問題点をまとめておきます。
超光懐中電灯無料アプリにはApperHandと呼ばれる不適切なプログラムや端末の位置情報/識別情報を収集する広告プログラムが多数組み込まれています。
この組み込まれたプログラムによってアプリを立ち上げるたびに、次の動作をします。
これによって、以下の脅威にさらされる可能性があります
懐中電灯の代わりに便利だからという理由で使う代償として上のリスクを受容できるかどうか考えてから使いましょう。
不適切なアプリとして(私を含め複数の人が)通報してるのだが…Googleさん本当に動く気ないのな…。
ご無沙汰しております。
本業が年度末作業で多忙を極めておりまして、Blogはおろか調査すらままならぬ今日この頃です。
さて、機を逸した感はありますが、今回は以前のエントリで前振りしていたApperhandについて書こうと思います。
今さらながら Apperhand についてエントリを書くのにはいくつか理由があります。
2~4については全くの偶然によるものなのですが、ここまで絡まれると因縁めいたものを感じざるを得ません。
ここまで引きずるつもりもなかったのですが、これで超光懐中電灯無料アプリとの付き合いに終止符を打とうと思います。
さて、Apperhand の話は 2 月初めまでさかのぼります。念のため当時の関連するエントリをまとめておきます。
そもそも、ベンダ各社で用語の定義や判断基準が異なる事に起因しているわけですが、Symantec の言う「ユーザがより確実な情報に基づいてインストールの可否を判断できること」というポリシーについては同意です。
一方で Lookout が Malware ではないという言い方をしたことについても、見方によって理解はできます。彼らにとって "Malware" というのが破壊的・攻撃的な(悪意をもった)意図がその機能にあるかどうか、つまりより狭義の意味で Malware という言葉を使うべきだという主張と考えられます。ただし、ユーザの視点から見ると、"Apperhand がアグレッシブな広告である" という表現は消極的すぎると感じられます。
残念ながら両者の議論はここで止まってしまいました。私としてはこれをきっかけに他のベンダを巻き込んで用語に対する定義統一の方向に向いてくれる事を望んでいます。
Symantec が Counterclank の情報公開してほどなく Twitter で偶然にも超光懐中電灯無料アプリで Malware を検知したという Tweet を見かけ、ご本人にどの対策ソフトで何が引っかかったのか確認しました。
Malware の種類はわからなかったものの、カスペルスキーで引っかかったとの事で早速カスペルスキーをダウンロードしスキャンをしてみました。 すると、偶然にもカスペルスキーが検知したのは Plangton つまり Apphand かその前身となったモジュールと言う事が判明しました。
そこで詳細を調べるべく apk を解析する事にしました。
まずは Malware として検知されたコードの特定です。
Plangton と言うことは独立したモジュールとして存在しているはずなので、まずは classes.dex のファイル構成を確認します。
#pwd /Android/tmp/light 1.9.3/out/com #ls -l total 0 drwxr-xr-x+ 1 users None 0 Feb 8 17:55 adserver drwxr-xr-x+ 1 users None 0 Feb 8 17:55 adwhirl drwxr-xr-x+ 1 users None 0 Feb 8 17:55 amobee drwxr-xr-x+ 1 users None 0 Feb 8 17:55 apperhand drwxr-xr-x+ 1 users None 0 Feb 8 17:55 flurry drwxr-xr-x+ 1 users None 0 Feb 8 17:55 google drwxr-xr-x+ 1 users None 0 Feb 8 17:55 inmobi drwxr-xr-x+ 1 users None 0 Feb 8 17:55 jumptap drwxr-xr-x+ 1 users None 0 Feb 8 17:55 mdotm drwxr-xr-x+ 1 users None 0 Feb 8 17:55 millennialmedia drwxr-xr-x+ 1 users None 0 Feb 8 17:55 mobclix drwxr-xr-x+ 1 users None 0 Feb 8 17:55 zestadz
ありました、Apperhand です。
これで Plangton の正体は Android.Counterclank (または亜種)である可能性がかなり高くなりました。
次に、Symantec のサイトで公開されている Malware の特徴と照合して Counterclank か否かを確認します。
Counterclank は C&C サーバ(http://[Censored].com/ProtocolGW/protocol)に問い合わせをしてコマンドを取得します。 これは /com/apperhand/device/android/AndroidSDKProvider.smali にありました。
.line 148 const-string v0, "M_SERVER_URL"
const-string v1, "http://[Censored].com/ProtocolGW/protocol"
invoke-virtual {v2, v0, v1}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
Symantec のサイトでは Apperhand は以下の機能を持っているとしています
これらの機能は、/com/apperhand/common/dto 配下にありました。
#pwd /Android/tmp/light 1.9.3/out/com/apperhand/common/dto #ls -l total 168 -rwxr-xr-x 1 users None 4587 Feb 8 17:55 Activation.smali -rwxr-xr-x 1 users None 11389 Feb 8 17:55 ApplicationDetails.smali -rwxr-xr-x 1 users None 3642 Feb 8 17:55 AssetInformation$State.smali -rwxr-xr-x 1 users None 5829 Feb 8 17:55 AssetInformation.smali -rwxr-xr-x 1 users None 8849 Feb 8 17:55 BaseBrowserItem.smali -rwxr-xr-x 1 users None 1124 Feb 8 17:55 BaseDTO.smali -rwxr-xr-x 1 users None 2820 Feb 8 17:55 Bookmark.smali -rwxr-xr-x 1 users None 7326 Feb 8 17:55 Build.smali -rwxr-xr-x 1 users None 12978 Feb 8 17:55 Command$Commands.smali -rwxr-xr-x 1 users None 971 Feb 8 17:55 Command$ParameterNames.smali -rwxr-xr-x 1 users None 9212 Feb 8 17:55 Command.smali -rwxr-xr-x 1 users None 6071 Feb 8 17:55 CommandInformation.smali -rwxr-xr-x 1 users None 6952 Feb 8 17:55 CommandStatus$Status.smali -rwxr-xr-x 1 users None 5065 Feb 8 17:55 CommandStatus.smali -rwxr-xr-x 1 users None 7713 Feb 8 17:55 DisplayMetrics.smali -rwxr-xr-x 1 users None 2186 Feb 8 17:55 Homepage.smali -rwxr-xr-x 1 users None 8785 Feb 8 17:55 NotificationDTO.smali -rwxr-xr-x 1 users None 3820 Feb 8 17:55 OptOutDetails.smali -rwxr-xr-x 1 users None 12038 Feb 8 17:55 Shortcut.smali -rwxr-xr-x 1 users None 4031 Feb 8 17:55 Status.smali drwxr-xr-x+ 1 users None 0 Feb 8 17:55 protocol
前述の機能と照らし合わせると以下の通りとなります。
実際のコードの確認については長くなるので省略しますが、これで超光懐中電灯無料アプリに Counterclank が組み込まれている事が明らかになりました。
超光懐中電灯無料アプリは、私が昨年からずっとその動向を見てきただけに、当時かなり衝撃を受けました。今までは端末情報を抜き出す広告モジュールの問題を取り上げてきたものがここで Malware 対策ベンダによって Trojan として完全に Malware 認定されてしまったわけです。
それからしばらくして超光懐中電灯無料アプリがバージョンアップされてことに気付き、Market から入手してみてみました。
この当時、徳丸先生が Twitter や Blog 等で Android アプリにおける暗号化や難読化を話題にされており、私も関心をもって見ていました。
ここでも偶然に Apperhand とこの話題が結びつく変更が行われていました。
Market 上の超光懐中電灯無料アプリの変更点は、特定の端末のLEDライトが点灯するよう対応したというものだったのですが、実際にはApperhand モジュールも変更されていました。
Apperhand は、モジュールとして配布されているのでおそらく配布元がバージョンアップしたものを取り込んだのではないかと推測しています。
変更点を確認すべく Apperhand モジュールの解析を始めようと diff(テキストファイルの差分を表示するコマンド)で確認したところところどころに差分があることがわかりました。 diff の結果はエントリに収まりきらないくらいの量なので割愛しますが、機能的にはほとんど変更点はなく、ロジックの多少の変更と難読化処理がかけられている事が原因でした。
前回のコードと比較すると文字列が暗号化されていることがわかります。
以下に該当のコードを記載します。
.line 146 const-string v3, "APPLICATION_ID"
invoke-virtual {v2, v3, v1}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
.line 147 const-string v1, "DEVELOPER_ID"
invoke-virtual {v2, v1, v0}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
.line 148 const-string v0, "M_SERVER_URL"
const-string v1, "http://[Censored].com/ProtocolGW/protocol"
invoke-virtual {v2, v0, v1}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
.line 155 const-string v3, "CRoQAlVGS1keGVoEHgRLEBoOGRdLEUE+agQtJzsiJj8tABJOHhYdGwYHQQU="
invoke-static {v3, v5}, Lcom/apperhand/device/a/d/a;->a(Ljava/lang/String;I)[B
move-result-object v3
.line 158 new-instance v4, Ljava/lang/String;
invoke-static {v3, v6}, Lcom/apperhand/device/a/d/b;->a([B[B)[B
move-result-object v3
invoke-direct {v4, v3}, Ljava/lang/String;->([B)V
.line 163 const-string v3, "APPLICATION_ID"
invoke-virtual {v2, v3, v1}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
.line 164 const-string v1, "DEVELOPER_ID"
invoke-virtual {v2, v1, v0}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
.line 165 const-string v0, "M_SERVER_URL"
invoke-virtual {v2, v0, v4}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
上記は AndroidSDKProvider Class の中で Intent をする際に付加するパラメータ(putExtra)の設定している部分です。
注目するところは ver1.9.5 の line155, 158, 165 の処理です。ver1.9.3 では "M_SERVER_URL" というキーで"http://[Censored].com/ProtocolGW/protocol"という C&C サーバの URLをIntentの際に渡しているのですが、ver1.9.5ではこの処理が複雑になっています。
まず、"APPLICATION_ID"の前に暗号化されたStringがあります。これは暗号化されたC&CサーバのURLです。これをその下にある Lcom/apperhand/device/a/d/a;->a(Ljava/lang/String;I)[B で下処理したバイナリに変換し、line158で復号したバイナリ文字列をStringに置き換えています。
復号された文字列はv4というレジストリに保持され、後のline165で"M_SERVER_URL"というキーといっしょにIntent時に渡されます。つまり、このClassの外から見るとこの暗号化には意味がありません。
つまり、ここでは難読化の目的で文字列が暗号化されているということになります。
難読化をかけること自体に問題はありませんが、C&Cサーバへのアクセス機能含め、問題のロジックをそのままに難読化をかけてくる辺り悪意を感じます。
念のため、ApperhandがいまだにC&Cサーバとやりとりを行っているか確認するためにLogを確認してみました。
03-01 12:39:53.296 E/b ( 5645): Unexpected error!!! 03-01 12:39:53.296 E/b ( 5645): com.apperhand.device.a.d.f: Status code is 503, address = [http://[Censored].com/ProtocolGW/protocol/commands],Headers = [[Lorg.apache.http.Header;@40580fc8],statusLine = [HTTP/1.1 503 Service Unavailable] 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.android.c.c.a(NetworkUtils.java:142) 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.android.b.b.a(TransportHttpApache.java:94) 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.a.b.a(SDKManager.java:52) 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.android.AndroidSDKProvider$1.a(AndroidSDKProvider.java:229) 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.android.AndroidSDKProvider.onHandleIntent(AndroidSDKProvider.java:316) 03-01 12:39:53.296 E/b ( 5645): at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:59) 03-01 12:39:53.296 E/b ( 5645): at android.os.Handler.dispatchMessage(Handler.java:99) 03-01 12:39:53.296 E/b ( 5645): at android.os.Looper.loop(Looper.java:143) 03-01 12:39:53.296 E/b ( 5645): at android.os.HandlerThread.run(HandlerThread.java:60) 03-01 12:39:53.296 E/b ( 5645): Exception!!! 03-01 12:39:53.296 E/b ( 5645): com.apperhand.device.a.d.f: Status code is 503, address = [http://[Censored].com/ProtocolGW/protocol/commands],Headers = [[Lorg.apache.http.Header;@40580fc8],statusLine = [HTTP/1.1 503 Service Unavailable] 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.android.c.c.a(NetworkUtils.java:142) 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.android.b.b.a(TransportHttpApache.java:94) 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.a.b.a(SDKManager.java:52) 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.android.AndroidSDKProvider$1.a(AndroidSDKProvider.java:229) 03-01 12:39:53.296 E/b ( 5645): at com.apperhand.device.android.AndroidSDKProvider.onHandleIntent(AndroidSDKProvider.java:316) 03-01 12:39:53.296 E/b ( 5645): at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:59) 03-01 12:39:53.296 E/b ( 5645): at android.os.Handler.dispatchMessage(Handler.java:99) 03-01 12:39:53.296 E/b ( 5645): at android.os.Looper.loop(Looper.java:143) 03-01 12:39:53.296 E/b ( 5645): at android.os.HandlerThread.run(HandlerThread.java:60) 03-01 12:39:53.296 D/b ( 5645): After executing commands 03-01 12:39:53.306 D/b ( 5645): Next command is on [60] seconds
どうやら、C&Cサーバは存在しているものの機能していない模様です。サーバから503が返されExceptionが発生しています。
ブラウザからもアクセスしてみましたが、やはり503が返ってきました。
しかし、アプリは相変わらずC&Cサーバへアクセスしコマンドの取得を試みている事が分かります。
結論としては、超光懐中電灯無料アプリはインストールすべきではないということです。
超光懐中電灯無料アプリについてはこれで終わりにしましょう。
※Apperhandの難読化に関してもう少しこのエントリで書こうと思いましたが、長くなりそうなのでまた別の機会にしようと思います。
「超光懐中電灯」で調べて本Blogエントリにたどり着いた方へ
本エントリは、2011年11月時点のアプリケーションを解析した結果です。その後アプリケーションは幾度かのバージョンアップを経てApperHandという広告モジュールを導入した結果、いくつかのウィルス対策ベンダからPUP(利用することが望ましくないプログラム)としてスキャンソフトで検知されるようになっています。
本アプリについては、本Blogエントリ「改めて警告します。「超光懐中電灯無料アプリ」は利用することが望ましくないアプリです」を参照ください。
前編は後々まとめて公開します。
@ChihiroShiiji さんの「どうしてこうなった?不思議なパーミッションを要求するAndroidアプリ・カタログ」 をご覧ください。
(前回のまとめ)
「迷惑な広告無し」と謳いながらアプリの機能に不要と思われるPermissionが付与されている超光懐中電灯。
AndroidManifest.xmlを見ると10件もの広告モジュールが組み込まれている事がわかった。
さらにdexを解析すると、広告モジュールの多くが端末情報や位置情報を取得するメソッドを実行している事が判明する。
不可思議なパーミッションの問題は、プライバシー情報の漏えい問題へと発展するのか?!
というわけで、実際に広告モジュールが収集した情報が外部に送信されているか解析を進めてみました。
結論から申し上げますと超光懐中電灯の広告モジュールは黒です。しかもある広告モジュールの仕様はかなり危険です。スパイウェア認定していいくらいに。
なお、限られた時間でもくもくと書きながら解析をしているので、誤字脱字含め勘違い等あればご指摘ください。
結論だけが知りたい方は記事の最下部をご覧ください。。。
先日のTwieetにも書きましたが、広告モジュールが直接HttpRequestを送信しているケースもあり、MAISTのように端末のLogから送信内容を確認するのは困難です。
そこでアプリを実際に動かして通信をキャプチャし解析するか、コードを直接解析するかの2択になるのですが残念なことに前者は上手くいきませんでした。(多分私の環境の問題で広告モジュールが上手く動作しなかった)
そこで(いたしかたなく)コードを解析することに。(smaliのコードを読むのは初めてなので、金床さんのサイトを参考にさせていただきました。)
ここからは私の解析の流れをそのまま記録しておきます。
先に書いた通り、smaliのコードを読むのが初めて、というか静的解析自体がほぼ初めてなので後で何方かが再検証できるようにという目的です。
まず、この膨大な広告モジュールのどこから手をつけるかというところで、取得している情報から最もインパクトの大きいであろうJumpTapというモジュールに絞ります。
こやつは、LocationManagerと、TelephonyManagerから取得できるであろうあらゆる端末情報を収集している憎いやつです。
まず、前回の解析結果の続きから見てみます。
以下はLocatoinManagerのgrepの結果です。
./com/jumptap/adtag/utils/JtLocation$1.smali(66,91): invoke-static {}, Lcom/jumptap/adtag/utils/JtLocation;->access$100()Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation$1.smali(70,48): invoke-virtual {v0, p0}, Landroid/location/LocationManager;->removeUpdates(Landroid/location/LocationListener;)V
./com/jumptap/adtag/utils/JtLocation.smali(11,23): .field private static locationManager:Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation.smali(32,59): sput-object v0, Lcom/jumptap/adtag/utils/JtLocation;->locationManager:Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation.smali(64,56): .method static synthetic access$100()Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation.smali(69,59): sget-object v0, Lcom/jumptap/adtag/utils/JtLocation;->locationManager:Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation.smali(128,59): sget-object v0, Lcom/jumptap/adtag/utils/JtLocation;->locationManager:Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation.smali(168,38): check-cast v0, Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation.smali(170,59): sput-object v0, Lcom/jumptap/adtag/utils/JtLocation;->locationManager:Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation.smali(173,59): sget-object v0, Lcom/jumptap/adtag/utils/JtLocation;->locationManager:Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation.smali(177,48): invoke-virtual {v0, v1}, Landroid/location/LocationManager;->getLastKnownLocation(Ljava/lang/String;)Landroid/location/Location;
./com/jumptap/adtag/utils/JtLocation.smali(193,59): sget-object v0, Lcom/jumptap/adtag/utils/JtLocation;->locationManager:Landroid/location/LocationManager;
./com/jumptap/adtag/utils/JtLocation.smali(203,56): invoke-virtual/range {v0 .. v5}, Landroid/location/LocationManager;->requestLocationUpdates(Ljava/lang/String;JFLandroid/location/LocationListener;)V
どうやらJtLocation.smaliでロケーションを取得しているようです。
とりあえずJtLocation.smaliを見てみるとありました。
.method private registerLocationListeners(Landroid/content/Context;)Vの中の56行目になります。
.line 56
sget-object v0, Lcom/jumptap/adtag/utils/JtLocation;->locationManager:Landroid/location/LocationManager;
const-string v1, "network"
invoke-virtual {v0, v1}, Landroid/location/LocationManager;->getLastKnownLocation(Ljava/lang/String;)Landroid/location/Location;
move-result-object v0
invoke-static {v0}, Lcom/jumptap/adtag/utils/JtLocation;->setCurrentLocation(Landroid/location/Location;)V
registerLocationListenersメソッドの中でandroid.location.LocationManager.getLastKnownLocationをCallし、その結果をsetCurrentLocationメソッドに投げています。
名前の通りsetCurrentLocationは現在地の情報を格納するためのsetアクセサです。
となるとgetアクセサもあるはず。
.method public static getCurrentLocation()Landroid/location/Location;
.registers 1
.prologue
.line 107
sget-object v0, Lcom/jumptap/adtag/utils/JtLocation;->currentLocation:Landroid/location/Location;
return-object v0
.end method
ありました。次にこのgetCurrentLocationが使われているメソッドを探してみましょう。
「getCurrentLocation」をgrepします。
./com/jumptap/adtag/utils/JtAdManager.smali(743,61): invoke-static {}, Lcom/jumptap/adtag/utils/JtLocation;->getCurrentLocation()Landroid/location/Location;
./com/jumptap/adtag/utils/JtLocation.smali(89,23): .method public static getCurrentLocation()Landroid/location/Location;
JtAdManager.smaliで使われています。
早速該当箇所を探してみます。
.method public static getLocation(Landroid/content/Context;)Ljava/lang/String;
.registers 6
.parameter "context"
.prologue
.line 95
invoke-static {p0}, Lcom/jumptap/adtag/utils/JtLocation;->init(Landroid/content/Context;)V
.line 96
const/4 v1, 0x0
.line 97
.local v1, retVal:Ljava/lang/String;
invoke-static {}, Lcom/jumptap/adtag/utils/JtLocation;->getCurrentLocation()Landroid/location/Location;
move-result-object v0
この後、位置情報はStringBuilderで「llatitude/longitude=xxx%2Cxxx」という形に形成されてCall元にReturnしていました。
さて、このメソッドをCallしてるのはどこでしょうか。「getLocation」でgrepした結果です。
./com/jumptap/adtag/callbacks\JtWebviewCb.smali(73,16): .method public getLocation(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
./com/jumptap/adtag/callbacks\JtWebviewCb.smali(90,36): const-string v3, "JtWebViewCB::getLocation options: "
./com/jumptap/adtag/utils/JtAdManager.smali(730,23): .method public static getLocation(Landroid/content/Context;)Ljava/lang/String;
./com/jumptap/adtag/utils/JtAdUrlBuilder.smali(191,64): invoke-static {v2}, Lcom/jumptap/adtag/utils/JtAdManager;->getLocation(Landroid/content/Context;)Ljava/lang/String;
最終行(4行目)に注目してください。「JtAdUrlBuilder.smali」つまり「JtAdUrlBuilder」クラスで利用されています。
クラス名から推測するとURLの生成クラスである可能性があります。核心に近づいてきました。
該当メソッドはかなり長いので該当部分だけ抜き出します。
.method public getAdUrl(Landroid/webkit/WebView;Ljava/lang/String;)Ljava/lang/String;
.line 40
iget-object v2, p0, Lcom/jumptap/adtag/utils/JtAdUrlBuilder;->context:Landroid/content/Context;
invoke-static {v2}, Lcom/jumptap/adtag/utils/JtAdManager;->getLocation(Landroid/content/Context;)Ljava/lang/String;
move-result-object v1
.line 41
.local v1, location:Ljava/lang/String;
if-eqz v1, :cond_9a
.line 42
const-string v2, "&ll="
invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
invoke-static {v1}, Lcom/jumptap/adtag/utils/JtAdUrlBuilder;->encodeParam(Ljava/lang/String;)Ljava/lang/String;
move-result-object v3
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
今まで取得してきたパラメータをStringBuilderで次々に連結させているようです。
位置情報は"&ll="というパラメータ名と結合しエンコードしています。
ここで、端末情報が含まれるか合わせて確認しましょう。
.line 51
const-string v2, "&hid="
invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
iget-object v3, p0, Lcom/jumptap/adtag/utils/JtAdUrlBuilder;->context:Landroid/content/Context;
invoke-static {v3}, Lcom/jumptap/adtag/utils/JtAdManager;->getHID(Landroid/content/Context;)Ljava/lang/String;
move-result-object v3
invoke-static {v3}, Lcom/jumptap/adtag/utils/JtAdUrlBuilder;->encodeParam(Ljava/lang/String;)Ljava/lang/String;
move-result-object v3
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
これのようですね。JtAdManager.smaliに該当のメソッドがありました。
.method public static getHID(Landroid/content/Context;)Ljava/lang/String;
.registers 3
.parameter "context"
.prologue
.line 79
invoke-static {p0}, Lcom/jumptap/adtag/utils/JtAdManager;->getTelephonyManager(Landroid/content/Context;)Landroid/telephony/TelephonyManager;
move-result-object v1
.line 81
.local v1, telephonyManager:Landroid/telephony/TelephonyManager;
const-string v0, ""
.line 82
.local v0, imei:Ljava/lang/String;
if-eqz v1, :cond_c
.line 83
invoke-virtual {v1}, Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;
move-result-object v0
.line 85
return-object v0
.end method
「Landroid/telephony/TelephonyManager;->getDeviceId()」からIMEIを取得している事がわかります。
つまり"&hid="はIMEIのパラメータが入っているということですね。
さぁ、こうやって一つ筒見ていくと、このUrlBuilderは以下の文字列を生成します。
&ua=(UserAgent)&pub=(publisherId)&spot=(SpotId)&site=(SiteId)&ll=(Location)&country=&pc=postalCode&mt-age=age&mt-gender=&mt-hhi=hhi&hid=(DeviceID)&a=(AdultContentType)&client-ip=(LocalIpAddress)&l=(Language)&c=3&version=(Version(広告モジュールのバージョン))&mt-speed=(ConnectionType)&mt-jtlib=(JtLibVer(広告モジュールのライブラリバージョン))&mt-bundle=(BundleVersion)&mt-os=(Os)&mt-osversion=(SDKVersion)&mt-model=(AndroidModel)&mt-make=(android.os.Build.Brand)&mt-fw=(KernelVersion)&mt-operator=(OperatorName)&mt-nradio=(NetworkType)&mt-dradio=(PhoneType)&mt-subno=(SubscriberId)&
…パラメータ名を見てドキッとしました。postalCodeとかAgeとかgenderとか…
固定値もしくはNullが入っているようですが、パラメータが多すぎて調べきれない…
いずれにしても広告モジュールの仕組みではこれらのパーソナルデータを送信する為のパラメータが用意されているということだけでもかなり凶悪です。
今日のところはこのくらいにしておきます。(小出しですいません)
あ、それから実機の通信データをキャプチャしていただける方緩募です。
| &ua= | (UserAgent) | android.webkit.WebView.getSettings() |
| &pub= | (publisherId) | (JtAdWidgetSettings内のパラメータに依存。デフォルトNull) |
| &spot= | (SpotId) | (JtAdWidgetSettings内のパラメータに依存。デフォルトNull) |
| &site= | (SiteId) | (JtAdWidgetSettings内のパラメータに依存。デフォルトNull) |
| &ll= | (Location) | android.location.LocationManager.getLastKnownLocation() |
| &country= | (Country) | (JtAdWidgetSettings内のパラメータに依存。デフォルト"") |
| &pc= | (postalCode) | (JtAdWidgetSettings内のパラメータに依存。デフォルト"") |
| &mt-age= | (age) | (JtAdWidgetSettings内のパラメータに依存。デフォルト"") |
| &mt-gender= | (gender) | (JtAdWidgetSettings内のパラメータに依存。デフォルト"") |
| &mt-hhi= | (hhi) | (JtAdWidgetSettings内のパラメータに依存。デフォルト"") |
| &hid= | (DeviceID) | android.telephony.TelephonyManager.getDeviceId() |
| &a= | (AdultContentType) | (JtAdWidgetSettings内のパラメータに依存。デフォルト"notallowed") |
| &client-ip= | (LocalIpAddress) | java.net.NetworkInterface.getInetAddresses() |
| &l= | (Language) | (JtAdWidgetSettings内のパラメータに依存。デフォルト"en") |
| &c= | 3 | |
| &version= | (Version(広告モジュールのバージョン)) | |
| &mt-speed= | (ConnectionType) | "WiFi"or"3G" |
| &mt-jtlib= | (JtLibVer(広告モジュールのライブラリバージョン)) | |
| &mt-bundle= | (BundleVersion) | (JtAdWidgetSettings内のパラメータに依存。デフォルト"com.jumptap.adtag-android") |
| &mt-os= | (Os) | (JtAdWidgetSettings内のパラメータに依存。デフォルト"Linux") |
| &mt-osversion= | (SDKVersion) | android.os.Build$VERSION.RELEASE |
| &mt-model= | (AndroidModel) | android.os.Build.DEVICE |
| &mt-make= | (Brand) | android.os.Build.Brand() |
| &mt-fw= | (KernelVersion) | (/proc/versionから取得) |
| &mt-operator= | (OperatorName) | android.telephony.TelephonyManager.getNetworkOperatorName() |
| &mt-nradio= | (NetworkType) | android.telephony.TelephonyManager.getNetworkType() |
| &mt-dradio= | (PhoneType) | android.telephony.TelephonyManager.getPhoneType() |
| &mt-subno= | (SubscriberId) | android.telephony.TelephonyManager.getSubscriberId() |