最初から始めましょう。 しばらく前に、当社はモバイルデバイスセキュリティの分野の研究に基づいて、Android向けの小さなアプリケーション( WebGuard )をリリースして、既に退屈しているすべての広告をブロックし、ブラウザーから作業するときのスヌーピングやウイルスからの保護を決定しました。 この機能を実装するには、多くの問題を解決する必要がありましたが、最も時間がかかったのはアプリケーショントラフィックの傍受と処理でした。 ブラウザー接続をインターセプトおよびフィルター処理するために、VpnService APIを使用することにしました。これは、バージョン4.0.3でAndroidに登場し、必要なすべての機能を提供します(ただし、この機能はさまざまな理由で機能しない場合がありますが、少し後で判明しました)。
使用されているテクノロジーについて少し
ここでは、このAPIが選ばれた理由と、「ルート化されていない」Androidデバイス上の別のアプリケーションでアプリケーションのネットワークトラフィックをインターセプトするために一般的に存在するものを詳細に説明する価値があります。 実際には(さまざまな脆弱性を除いて)3つの方法しかなく、さまざまなゲーム内購入メカニズムを持つアプリケーション開発者がそれらについて知るのに役立ちます。 それらの多くは、アプリケーションのネットワークトラフィックの置換は「ルート化された」デバイス上でのみ可能であると考えているため、送信データに変更を加える「不正」アプリケーションが出現する可能性があります(かなり高い)が、送信データの保護を「強化」しません。
そのため、 最初の方法はローカルプロキシサーバーをインストールすることです。 Android向けのほとんどのアンチウイルスはこの方法を使用します。 トラフィックを傍受するには、 RFC 2068標準で説明されているHTTPプロキシサポートを実装し、WiFiネットワーク設定のモバイルネットワークの設定でプロキシサーバーまたはAPNを設定するだけで十分です。 この場合、データ転送は下図のように行われます。

しかし、この方法には多くの深刻な問題があります。
- モバイルネットワーク用のWiFiプロキシまたはAPNのインストールは、非表示API(WifiManagerの非表示メソッドなど)を使用する場合にのみプログラムで可能です。
- プロキシ(APN)をインストールして、新しいWiFiまたはモバイルネットワークの出現を監視する必要があります。
- このメソッドは、プロキシを使用しないように明示的に指定しない限り、 Http(s)URLConnectionクラスを使用して作成された接続に対してのみ機能します(たとえば、url.openConnection(Proxy.NO_PROXY))。 プロキシサーバーの存在を確認して使用する(またはその逆は使用しない)アプリケーションもあります。
- ユーザーがプロキシサーバーでアプリケーションをアンインストールするか、単に起動しない場合、プロキシはネットワーク設定で指定されたままであり、ユーザーはそれを設定から削除する必要があるため、残りのアプリケーションはサーバーに接続できません( 例 :);
- それぞれ複数のプロキシのチェーンを構築することはできません。また、トラフィックをインターセプトするアプリケーションは1つだけです。
最後の問題を除くこれらすべての問題は、 2番目の方法で正常に解決されます。VpnServiceクラスとVpnService.Builderクラスを使用してVPNクライアントアプリケーションを作成することです。 アプリケーションがいくつかの関数を呼び出すだけで十分です(コードでは必須のチェックは省略されています)。
// Activity Intent intent = VpnService.prepare(PromptActivity.this); startActivityForResult(intent, VPN_REQUEST_CODE); //
// onActivityResult // requestCode == VPN_REQUEST_CODE && resultCode == RESULT_OK VpnService.Builder vpnBuilder = new VpnService.Builder(); vpnBuilder.addAddress(options.address, options.maskBits); vpnBuilder.addRoute("0.0.0.0", 0); ParcelFileDescriptor pfd = vpnBuilder.establish(); FileInputStream in = new FileInputStream(parcelFileDescriptor.getFileDescriptor()); FileOutputStream out = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
、およびすべてのアプリケーション(ルートとして実行されているものも含む)のすべてのTCP / IPトラフィックは、Androidが作成するTUNインターフェースにリダイレクトされ、アプリケーションはデバイスファイル/ dev / tun(または/ dev / tun0、/ dev / tun1など)、そこから発信ネットワークパケットを読み取り、それらを処理のためにリモートVPNサーバーに送信し(通常は暗号化された接続を介して)、次に着信ネットワークパケットを記録します。 VPNクライアントの接続がTUNで「ラップアップ」するのを防ぐため、アプリケーションによって作成されたTCPまたはUDPソケットでVpnService.protectメソッドが使用されます。
この場合、上記のスキームは次のようになります。

このメソッドには2つの機能があります。
- アプリケーションは、startActivityForResultの呼び出しを通じてVpnServiceを使用する権利を取得する必要があり、システムはこのダイアログをユーザーに表示します。
さらに、Androidは、デバイスが再起動されるまでのみアプリケーションに権限を発行したことを記憶しているため、再起動後に再度権限を要求する必要があります。 判明したように(これも予想していなかった)、10分ごとに電話を再起動したいユーザー(「はい、数百人」©)がいます。このウィンドウはユーザーを悩ませます。
- 「悲しみ」は以前のものより少し大きいです。 VPNをオンにした後、Androidは、通知アイコンをキーの形で表示し、実際に修正された通知を表示します。クリックすると、いくつかの統計を確認できます。
「カーテン」での通知。 通知アイコンとステータスダイアログ
ここでは、多くの現在のアプリケーションが通知を「一度に」ハングアップするのが大好きで、ステータスバーが新年の飾りに変わるため、ユーザーの不満が予想されました。
また、ユーザーが「切断」ボタンをクリックすると、AndroidはVPNを切断し、アプリケーションから権利を奪います。その後、VPNをアクティブにするには、startActivityForResultの呼び出しを通じて権利を再度要求する必要があります。
また、このメソッドには小さな追加の問題があります(以下の「機能」はカウントしません)-動作させるには、リモートサーバーが必要です。
サーバーの問題は、 3番目の方法 (わずかに変更された方法2)で解決されます-VPNクライアントアプリケーションには、アプリケーションとプロセス接続からのトラフィックを解析するTCP / IPスタックも含まれています(準備が整っているか、 欠陥があるため、自分で作成することもできます)プロキシサーバーとして。 次に、アプリケーションのトラフィック処理スキームが多少変更され、次のようになります。

これは、WebGuardで使用する方法です。 欠点のうち、以前の方法と比較して、1つだけ注意することができます-アプリケーションは通常ルート権限を必要とする「生の」ソケットを作成する必要があるため、TCPまたはUDP以外のプロトコル(またはそれらのプロトコル)の通常の処理の不可能性a。 話の内容を明確にするために、簡単な例を見てみましょう。ユーザーがADBを介してシェルを起動し、 ICMPエコー要求を送信する「ping www.ya.ru 」 コマンドを実行します。 さらに、VPNクライアントアプリケーションは、/ dev / tunからIPパケットを読み取り、解析して、パケットに特定のサーバーへのICMPエコー要求が含まれていることを検出します。 また、アプリケーションはリクエストをさらにネットワークに送信できないため、2つのオプションしかありません。パケットを無視するか、目的のサーバーへの接続を試行するpingをエミュレートし、成功した場合は、/ dev / tunに偽のICMPエコー応答を記録します。
機能VpnService API
ユーザーがアプリケーションを開発し、最初のバージョンをテストして使用するプロセスで、VpnService APIに関連する多数のエラーまたは欠点に遭遇しました。 それらのいくつかは修正されました。本質的にこれらはプログラマーの欠陥であり(正直に書いてbash.org.ruにアクセスしました)、それ以外の場合はやや困難または不可能です。
- LinuxカーネルではTUNインターフェイスがサポートされていないため、VPNは機能しません。 基本的に、この問題はCyanogenMod、AOSPなどのプロジェクトに基づいた自己組み立てファームウェアで発生します。アセンブリの作成者は、カーネルからサポートを完全に削除するか、ファームウェアにtun.koモジュールを配置することを忘れます。 著者は「それが何であるかわからないので、必要ではない」という原則に導かれているようです。
- 権利と統計を要求するためのダイアログを含むVpnDialogs.apkファイルはありません; VPNは再び機能しません。 奇妙なことに、このエラーはほとんどの場合、電話メーカー(Google を含む )の公式アセンブリで発生します。
- VPNクライアントで未処理の例外が発生すると、公式ファームウェアを使用した一部のSamsung電話でGUIがフリーズします(同時に、ADBを介したシェルは非常に機能していますが、1〜2分ごとに再接続します)。 一般に、Android携帯電話のメーカーは、「まれに使用される」機能の一部を確認するのを忘れて、「素晴らしい」ファームウェアを収集することがあります。 この点で当社の開発チームは、Samsungを単に「愛して」おり、Android用アプリケーションをテストするには、異なるメーカーの複数の電話とSamsungの複数の電話が必要だと考えています。
- この変更により、ユーザーがクリックできない別のアクティビティによってブロックされている場合、ユーザーはVPNダイアログの[このアプリケーションを信頼する]チェックボックスをチェックできなくなります。 これは、上から別のダイアログを描画して、ユーザーをだましてチェックボックスをオンにすることを不可能にするために行われます。 問題は、さまざまなユーティリティ、ランチャー、および一部のプログラム(たとえば、「Read!Free」)が通常これを行い(長時間)、透過的なアクティビティを作成することです。 その結果、一部のユーザーはいじめられていると考えるかもしれません。
- ユーザーがバックグラウンドプロセスのトラフィック制限をオンにすると、動作中のVPNクライアントもディストリビューションに分類されます(ソケットを介してデータを送信する場合、Permission denied例外を伴うSecurityExceptionが発生します)。
- LinuxにはOOM Killerなどの素晴らしいメカニズムがあり、ユーザーがデバイスのRAMを大量に「食い尽くす」アプリケーションを突然起動した場合(実験にFirefoxを使用でき、常にメモリがほとんどありません)、OOM Killerが起動しますいつものように、機会の主人公(より正確には、彼は最後に殺される)を除いて、連続してユーザープロセス。 もちろん、必要なプロセス(ランチャーまたはVPNクライアント)の例外はないため、サービスを常に再起動する準備をする必要があります。
- VPNクライアントが誤ってシャットダウンした後(後で再起動しても)、まれに、Androidがパケット転送ルートを誤って再構成し、アプリケーションからのパケットが/ dev / nullに移動するため、デバイスを再起動する必要があります 。
- ユーザーがVPNクライアントと同時にWiFi経由でモバイルインターネットの配信を有効にすると、上記の場合のように、ほとんどのファームウェアでAndroidがパケットルーティングを誤って設定します。
- 適切な「機能」のリストにもかかわらず、Googleの開発者は「突然」新しいバージョンの機能を改善するために
やり直すことを決定できます。Androidの開発者は通常、すべての変更について警告しないため、Androidリポジトリへのコミットを注意深く監視し、コードを編集する必要があります。 このアプローチの結果は、誰よりも速くアップデートを受け取るNexus携帯電話のユーザーに最もよく感じられます(「PLIZZZZZZZZZ !!!!!!!!!!!!! VPN !!!!!!!!!!!! !!!」、「この問題を修正してください」 ここ 、 ここ 、 ここ 230日以上。 そのため、出張中にNexusを更新しないように注意してください。
このリストを読んで考えてみてください。おそらく彼のVPNでしょうか。 残念ながら、「ルーティングされていない」Androidフォンですべてのトラフィックを傍受する方法は他にありません。
NinePatchについて少し
上記のリストには別の問題は含まれておらず、その結果は私たちにとって非常に予想外でした(私たちだけでなく)。 したがって、結果についてより詳細に話すことにしました。 それはすべて、ある時点でアプリケーションの最も重要な部分である退屈なアイコンを作成する必要があったという事実から始まりました。 すぐに言ってやった。 美しい丸いアイコンが描かれ、突然リリースされたとき、プレリリースバージョンをテストしています。

一部の電話では、VPN通知のアプリケーションアイコンが非常に奇妙に表示されることがあります(色、サイズ、またはその他が正しくない)。 実験と短いが白熱した議論の後

、アプリケーションアイコンを9パッチ形式にする
3.アイコンを表示しようとしたときにクラッシュしたさまざまなランチャー(LauncherProなど)。
2. SamsungおよびYandexのAndroidアプリケーションを保存します。 アプリケーションをYandex.Storeにダウンロードしようとすると、「APKからアプリケーションアイコンを抽出できませんでした」という完全に理解可能なエラーの説明が発行されました。 C Samsung Appsはもっと楽しかったです。 同社はハイテク企業であるため、アプリケーションを追加する際のテスト結果は適切な形式になります。アプリケーションテストプロセスが記録され、エラーが表示されるビデオへのリンクが記載された電子メールです(理論上)。 しかし、すべてがいつものようになったことが判明し、ビデオではないリンクが付いた手紙が届きました。
1.まあ、名誉ある最初の場所は、右、ソニーがXperia電話で占有している。 リリースのリリース後しばらくして、Xperia Lの所有者は、WebGuardが電話を「殺した」というメッセージを送信し始めました( 例 )。 インストール中にアプリケーションアイコンを処理しようとすると、PackageManagerServiceがクラッシュし、その後電話が自動的に再起動して無限にダウンロードすることが判明しました。
E / AndroidRuntime(790):***システムプロセスの致命的な例外:Thread-123 <br />
E / AndroidRuntime(790):java.lang.ClassCastException:android.graphics.drawable.NinePatchDrawableはandroid.graphics.drawable.BitmapDrawableにキャストできません<br />
E / AndroidRuntime(790):com.android.server.pm.PackageManagerService $ SetIconCacheThread.run(PackageManagerService.javahaps672)<br />
... <br />
E / AndroidRuntime(2981):致命的な例外:ApplicationsProviderUpdater <br />
E / AndroidRuntime(2981):java.lang.RuntimeException:パッケージマネージャーが停止しました<br />
E / AndroidRuntime(2981):android.app.ApplicationPackageManager.queryIntentActivitiesAsUser(ApplicationPackageManager.java:487)<br />
E / AndroidRuntime(2981):android.app.ApplicationPackageManager.queryIntentActivities(ApplicationPackageManager.java:473)<br />
E / AndroidRuntime(2981):com.android.providers.applications.ApplicationsProvider.updateApplicationsList(ApplicationsProvider.java ∗ 18)で<br />
E / AndroidRuntime(2981):com.android.providers.applications.ApplicationsProvider.accessで$ 300(ApplicationsProvider.java:69)<br />
E / AndroidRuntime(2981):com.android.providers.applications.ApplicationsProviderで$ UpdateHandler.handleMessage(ApplicationsProvider.java:206)<br />
E / AndroidRuntime(2981):android.os.Handler.dispatchMessage(Handler.java:99)<br />
E / AndroidRuntime(2981):android.os.Looper.loop(Looper.java:137)<br />
E / AndroidRuntime(2981):android.os.HandlerThread.run(HandlerThread.java:60)<br />
E / AndroidRuntime(2981):原因:android.os.DeadObjectException <br />
E / AndroidRuntime(2981):android.os.BinderProxy.transact(ネイティブメソッド)<br />
E / AndroidRuntime(2981):android.content.pm.IPackageManager $スタブ$ Proxy.queryIntentActivities(IPackageManager.java:2027)<br />
E / AndroidRuntime(2981):android.app.ApplicationPackageManager.queryIntentActivitiesAsUser(ApplicationPackageManager.java:481)<br />
E / AndroidRuntime(2981):...さらに7つ<br />
その結果、バージョン1.3からは、特にアイコンの表示に関するすべての問題を解決しても解決しなかったため、9パッチなしでアイコンを使用することが決定されました。

Ainol Novo10 HeroでのVPN有効化通知
おわりに
投稿を大きくしすぎたくないので、記事をいくつかの部分に分割することにしました。 次のパートでは、トラフィックをフィルタリングするAndroidアプリケーションが(androidによると)バッテリー電力を大量に消費し、DalvikVM!= JavaVMという1つのパフォーマンステストを例に挙げて思い出す理由を説明します。
この記事が、Android向けの興味深いアプリケーションの作成に役立つことを願っています。 良い開発を!