blog

フック技術によるアンドロイドの透過的な暗号化と復号化により、データの安全性を確保する

ユーザーは、Android携帯デバイスのベンダーで重要なプライベートファイルを保存するために、通常はいくつかの暗号化された保存ソフトウェアを使用します。しかし、携帯電話では、ソフトウェアのスケールのプ...

Jul 11, 2025 · 10 min. read
シェア

序文

ユーザーがAndroid携帯端末のベンダーに重要なプライベートファイルを保存するために、通常、いくつかの暗号化保存ソフトウェアを使用します。しかし、携帯電話上のプライバシー空間を実装するソフトウェアは、スケーリングされますが、問題は、ファイルを開くために、すべてのそのプライバシー空間を使用する必要があることです、一時ファイルに暗号化されたファイルを復号化し、ファイルを開くためにアプリケーションを選択します。このため、ユーザーの重要なファイルが平文のまま端末に流出する危険性があります。

II.テクニカルポイント

AndroidはLinuxカーネルをベースにしたオープンソースシステムであるため、言語環境の違いにより、Java層、Native C層、Linuxカーネル層に分けることができます。Java層のセキュリティは、SDKをベースにJava言語を使って開発され、実現できる機能は比較的シンプルです。Linuxカーネル層のセキュリティは、ソースコードから行う必要があるため、独自のシステムをコンパイルする必要があり、汎用性に欠けます。そのため、ネイティブC層では、JNI開発を通じて、Linuxが提供する機能を利用することで、より多くの機能を実現することができます。

フックAPIとLinuxフックの面でptrace関数とpltテーブルの使用と同様に達成するために、あなたも達成するためにインラインフックの方法を使用することができますが、それは非常に安定していない、操作は困難です。本質は、関数コールをハイジャックすることです。

Ptrace関数はプログラムのデバッグに使用され、プロセスをアタッチするだけでなく、ターゲットプロセスのメモリ空間やレジスタまでも自由に変更できる強力な関数です。

基本的な処理は、レジスタ命令割り込みを使用することです:

1.PTRACE_ATTACH、ターゲットプロセスをバインドします。

2.PTRACE_GETREGSで、ターゲット・プロセスのレジスタ・ステータスを取得し、保存します。

PTRACE_GETREGS でレジスタを再保存します。

PTRACE_PEEKDATAはPTRACE_POKEDATAと連携してコードを復元します。

PTRACE_SETREGSを実行すると、レジスタが復元され、ターゲット・プロセスは実行を続行します。

PTRACE_DETACH で対象プロセスのバインドを解除。

フックの動作メカニズムを理解した後、Android上で透過的な暗号化と復号化の実装では、オープンとクローズ関数のシンボルを見つける必要がありますどのダイナミックリンクライブラリに存在し、このダイナミックリンクライブラリのアプリケーションをフックし、オープン操作では、メモリに暗号文ファイルのチャンクで復号化され、メモリ内のファイル識別子に返されます。閉じる操作が実行されると、メモリ内の平文は、ローカルの暗号文ストレージに暗号化されます。

III.主要プロセス

1、Androidのコードを読んで、ファイルを開いたり閉じたりするプロセスを見つけます。これが透過的な暗号化と復号化を実現する鍵です。

あなたは、同じオープンファイル操作を達成するために関数を読み取るためにJNIの方法を介してファイルストリームを読み取る関数は、最終的にopen関数に起因していることがわかります。

動的ライブラリのJavaコードJNIサポートの実装はnativehelper.soなので、nativehelper.soの動的ライブラリをフックする必要があります。

注:Androidの初期バージョン、すなわちandroid2.3、Android4.0ではnativehelper.soの開閉シンボルに、ファイルのサイズが140kを持っています。そして、android4.1以上のバージョンでは、Googleがandroidネイティブライブラリの実装を書き直したため、nativehelper.soが分割されました。

一方、Androidインジェクタ・ライブラリの使用は比較的簡単で、実行ファイルを呼び出すメイン関数に実装できます:

*1、do_hook関数を通してlibhook.soをロードする関数で、元の開閉関数アドレスに戻り、新しい開閉関数アドレスに置き換えます。

* 次に、libnativehelper ダイナミック・ライブラリを静的に開き、その構造体を読み込んでセクションテーブ ルを走査し、外部依存シンボルのアドレスを格納するグローバル・シンボル・テーブルを見つけます。

* 元のオープン関数とクローズ関数のアドレスをそれぞれ見つけ、新しいオープン関数とクローズ関数に置き換えます。

3、これを学ぶ過程で、LinuxのELF形式を理解する必要があり、以下はELFノートの研究です:あなたが精通している場合は、"プログラマの自己育成 "を参照してくださいスキップすることができます。

ehdr->e_shstrndx インデックスは、shstrtab のセクションを指し、セクションヘッダの文字列名記述のインデックスに使用できます。shstrtab テーブルは、セグメントテーブルで使用される文字列を保持し、最も一般的なのはセグメント名です、

よく使われる段落名 説明
.rodata1	Read Only Data,この種のセグメントは、文字列定数、グローバル定数変数などの読み取り専用データに格納されている。と一緒に”.rodata” 
.comment	コンパイラのバージョン情報が格納されている
.debug	 デバッグ情報
.dynamic	ダイナミック・リンク情報
.hash	 記号ハッシュテーブル
.line	 デバッグ行番号表
.note	 コンパイラの追加情報。例えば、プログラムの会社名、リリースバージョン番号など。
.strtab	 String Table.文字列テーブル
.symtab	 Symbol Table. 
.shstrtab	Section String Table. 
.plt .got	ジャンプテーブルとグローバルエントリーテーブルへのダイナミックリンク
.init .fini	プログラムの初期化と終了コードセグメント

セクションヘッダをトラバースするときのシンボルセクション。各セクションのタイプが SHT_SYMTAB か SHT_DYNSYM かを判断し、対応するセクションがシンボルセクションであることを確認します。シンボルセクションはシンボルテーブルを格納します。

ELFファイルのシンボル・セクションは1つだけではなく、通常は2つあります。1つはダイナミック・セクション・タイプSHT_DYNSYMを持つ".dynsym "で、すべての外部シンボルが導入され、もう1つはすべての有用なシンボル情報を保持する".symtab "という名前のSHT_SYMTABです。.symtab "という名前のSYMTABは、すべての有用なシンボル情報を保持します。

シンボル・テーブル シンボル・テーブルは、プログラムが位置決めや再配置を行うために必要な定義と 参照情報を保持します。シンボル・テーブルのインデックスは、対応する添え字です。シンボルテーブルの存在の意義は、複数のターゲットファイルのリンクに反映され、ターゲットファイルのリンクでは、ターゲットファイルのコロケーションは、実際には参照のアドレスの間にターゲットファイル、つまり、関数と変数のアドレスへの参照であり、関数と変数はシンボルと総称することができ、関数名または変数名はシンボルの名前です。シンボルはリンクの接着剤とみなすことができ、シンボルに基づいて全体のリンクプロセスを正しく完了することができます。シンボルテーブル".symtab "は、セグメントテーブルの構造のように、配列で、各配列の要素は、シンボル名、シンボルの対応する値、シンボルのサイズなどのシンボルに関連する情報を保存する固定構造です。シンボルテーブルに記録されるシンボルは、通常、グローバル変数、グローバル関数などのグローバルシンボルです。

ターゲット・ファイルのシンボル・テーブルには、プログラム・シンボルの定義と 参照を検索または再配置するために必要な情報が含まれています。シンボル・テーブルのエントリ構造は次のように定義されています。

typedef struct{
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
Unsigned char st_info;
Unsigned char st_other;
Elf32_Half st_shndx;
}Elf32_Sym;

st_nameには、シンボル名を取得するためのシンボルテーブルの文字列テーブルへのインデックスが含まれます。st_valueはシンボルの値を示し、絶対値、アドレスなどである場合があります。st_sizeはシンボルに関連するメモリサイズを示し、例えばデータ構造に含まれるバイト数などです。St_info はシンボルのタイプとバインディング属性を指定し、シンボルがデータ名、関数名、セクション名、ソース・ファイル名のどれであるか、シンボルのバインディング属性がローカルかグローバルか弱いかを示します。

GOTテーブルとPLTテーブル

GOT(Global Offset Table)テーブルの各項目は、このランタイム・モジュールが 参照するグローバル変数や関数のアドレスです。GOT テーブルを使用してグローバル変数や関数を間接的に参照することも、GOT テーブルの最初のアドレスをベースとして使用し、そのベースからの相対オフセットで静的変数や関数を参照することもできます。ローダーは固定アドレスにランタイム・モジュールをロードしないため、各ランタイム・モジュールの絶対アドレスと相対位置は、異なるプロセスのアドレス空間で異なります。この違いはGOTテーブルに反映されます。つまり、各プロセスの各ランタイム・モジュールは独立したGOTテーブルを持つため、プロセス間でGOTテーブルを共有することはできません。

ダイナミックリンキングメカニズム

まず、あるモジュールAが別のモジュールBの関数を呼び出す必要がある場合、Linuxプラットフォームのダイナミック・リンク・メカニズムを思い出してください:

1.モジュールAは、コンパイル時に参照するモジュールBの名前と関数名を自身のシンボルテーブルに書き込みます。

2、ランタイムモジュールAの呼び出しは、呼び出し元のコードからPLTテーブルにGOTテーブルとし、モジュールBにジャンプするプロセスです。

そして、モジュールAのコードがそのPLT/GOTから正しいモジュールBのエントリにジャンプできるようにするにはどうすればいいか、それはリンカーが行うことです。

標準のLinuxリンカは、遅延バインディングをサポートしているld.soは、つまり、コンパイル時にモジュールBを呼び出すには、モジュールによって生成された元のコードは、フローはリンカにPLTテーブルに呼び出しコードからです。ランタイムリンカに最初にモジュールBを呼び出すには、最初の時間は、リンカは、そのシンボルを検索するモジュールBをロードする呼び出し情報によると、GOTテーブルに充填された関数のアドレスで発見され、その後の呼び出しプロセスは、PLT / GOTテーブルに直接移動します。このメカニズムは、ロードのオーバーヘッドを削減し、Linuxディストリビューションなどで使用されています。

AndroidカーネルはLinuxをベースにしていますが、その動的リンク機構はld.soではなく、独自のリンカーであり、遅延バインディングをサポートしていません。つまり、上記のモジュールAとBがAndroidプラットフォーム上にある場合、モジュールAがロードされると、リンカはモジュールAの.rel.pltテーブルと文字列テーブルの内容に従ってモジュールBをロードし、必要な関数のアドレスを検索してGOTテーブルをプリフィルします。その後、呼び出しフローは毎回PLT/GOTテーブルに直接行き、もはやリンカーに入ることはありません。PLTテーブルもリンカーのコードにジャンプする必要性をなくします。このフローは「ハードワーキング」バインディングに似ており、インターセプトに少し利便性を提供します。モジュールBがロードされていないときに遅延バインディング・エントリをインターセプトすると、アドレスが見つからないため、インターセプトを実行できません。

4.コードの注釈を読む

Androidインジェクタ・ライブラリを読む際に、いくつか注意すべき点があります。

1) SIGSEGV による無効なメモリ参照またはセグメント・エラーをキャッチする例外シグナ ルを使用して ptrace を実行します。

2)ptrace(PTRACE_PEEKTEXT, pid, addr, data)

説明:メモリアドレスから1バイトを読み込みます。pidは追跡されるサブプロセスを示し、メモリアドレスはaddrで与えられ、dataは読み込んだデータを返すために使われるユーザー変数のアドレスです。

Linux(i386)では、ユーザー・コード・セグメントとユーザー・データ・セグメントが重なっているため、コード・セグメントの読み込みとデータ・セグメントのデータ処理は同じです。

3)リンカは、主に共有ライブラリのロードとリンクを実現するために使用されます。これは、暗黙的および明示的な呼び出しのライブラリ関数のアプリケーションをサポートしています。/system/bin/linkerはlibdl.soをロードし、固定ロード場所、定義されたdlopen、dlcose、dlsym、dlerrorを検索します。

4) dynsymとsymtabの関係について、以下のコードを理解していること。

5) dynsym記号の読み取り順序について、コードに誤りがあります。しかし、使用に影響はありません。androidSDKのreadelfというツールを使ってください。

5、暗号化と復号化操作を達成するために、独自のオープン関数とクローズ関数を記述する必要があります。

このプロセスはアンドロイド・プラットフォーム上でopenssl EVPを使ってプログラムされており、それほど難しいものではありません。

キーポイント1は、キースペースの構造にあります。キースペースには配列を使用することをお勧めします。文字列の末尾に''があっても、char*文字列を使用すると、メモリ上の他の内容がキーの初期化に影響するため、予期しない問題が発生する可能性があります。

ポイント2は、引数はファイルディスクリプタのみで、ファイル名は以下のコードで取得できます。

3つ目のキーポイントは、Opensslを使用した対称暗号化と復号化では、適切なブロックサイズにパディングされるため、手動でパディングを取り除く必要があるということです。パディングは国際的に一般的なパディング方法で構築することもできますし、暗号文ファイルのヘッダーにパディングサイズを記録することもできます。

http://......//ng_。

6.忘れずにMakefileファイルを

LOCAL_LDLIBS+=-L$/usr/lib -llog

LOCAL_LDLIBS+=-L$/usr/lib -lcrypto

LOCAL_LDLIBS+=-L$/usr/lib -lssl

7は、キー管理モジュールの開発を実施する必要があります、プロセスはもはや説明されていません。

概要

Read next

ブルートゥース4.1規格が利用可能になった

Bluetoothの4.1規格が正式にBluetooth SIGによって開始され、大幅にBluetoothデバイス間の接続効率を向上させ、ユーザーエクスペリエンスを向上させ、モノのインターネット市場でしっかりと立つためにBluetooth技術を支援し、大量のデータを転送する能力を強化します。

Jul 11, 2025 · 2 min read