この記事は Artem Gonchar と Travis Biehnによって执笔されました。
モバイル端末では、信頼できる実行環境として、ARM TrustZoneの利用が拡大しています。モバイル決済、端末の完全性、モバイル端末管理、その他の問題を解決するために信頼性のある実行環境が求められます。 CyRC の活動の一環で、TEE(Trusted Execution Environments)の適用されたセキュリティについて調査をしてみました。そして、OnePlus 7 Pro Advanced の利用者の登録した指紋データが晒されるバグを発見しました。(vulnerability advisory for CVE-2020-7958を参照)。
そこには、既知の、そして未知のソフトウェアコンポーネントが组み合わせられており、罢谤耻蝉迟窜辞苍别上で指纹の识别を行なっていた。我々の解析レポートを読んでいただければ、各コンポーネントがどのように相互作用しているのか、どのようにそれらを攻撃できるのかが明らかになるでしょう。
Android 6の登場以前は、の実装の标準は规定されていませんでした。セキュリティ研究者たちは、指纹画像を抽出し、デバイスが认証チェックのために保持していた登録済みの参照指纹を改ざんする方法をすばやく见つけていたものです。たとえばでは、当時、どのように指纹认証の仕組みが安全でないまま主要なAndroidスマートフォンに実装されているかを説明しています。これらの実装は、個人の生体認証データを危険にさらし、結果として、何百万ものユーザーに害を及ぼす可能性がありました。
Android 6の登場に際し、Google社はCDD(Compatibility Definition Document)でについて踏み込み、定义しました。それはその后増え続け、広范な&苍产蝉辫;が存在しています。
標準化されたやり方は、Trusted Execution Environment (TEE)内での生体センサーの利用に伴うすべての機微な操作の実行のためのやり方です。この仕様によれば、指紋センサーとの通信はCPUのSecure mode内でのみ行われなければなりません。つまり、センサーはAndroid OS (Rich Execution Environment(REE)) からアクセスできないことを保証するということです。
Android 端末の”root”になったとしたら、ブートローダーのロックを解除し、フラッシュメモリー上のいくつかのパーティションを変更することができます。これらの変更は、実行時の自身の権限”root”に昇格させることも可能にします。しかし、“ブートローダー?アンロック”は厳密には正しい表現ではありません。Android端末のブートについてはを参照してください。 いくつかのブート段階があることがわかります。このフレーズ自体は、どのシステムのどのブートローダーの段階にも適用できますが、Androidにおける“ブートローダーのアンロック” は、ブート段階のほぼ最後のステージを示すのが普通で、端末の”Secure World”でAndroid OS のイメージをブート前に検証する段階のことです。もちろんTEEはこのステージの前にブートされています。
ARM CPUのセキュリティの境界は、異なるセキュリティの状態で、異なる例外レベル(ELs)の実行が可能となっています。これは、カーネル空間とユーザ空間を分離するのと同じ仕組みを拡張したものです。TEEはAndroid OSカーネルよりも特権的なレベルとセキュリティ状態で実行されます。TEEのメモリーはREEからはアクセスできず、すべての通信はAndroid OS カーネルからのSecure Monitor Callsを通じて行われます。
Android OSを起動する後段のブートローダーのロックを解除しても、TEEを起動する前段のブートローダーのロックが解除されるわけではありません。 これで、”ルート化”によってAndroid OSが侵害されたとしても、TEEは安全なままです。実際に、コンテンツ保護システム (DRM)、モバイル決済システム、生体認証、ハードウェア支援による暗号APIなどで使用されています。
骋辞辞驳濒别社は、颁顿顿の中で、どのようなデータを罢贰贰で保护し、生体认証によって罢贰贰で行うアクションを规定しています。このことで、たとえ”ルート化”された端末であったとしても、罢贰贰は指纹の安全を维持するのです。
もちろん、実装には常にバグがつきものです。いままでも、メモリーを破壊してしまうバグを悪用してTEEで任意のコードを実行することに成功しているわけです。同様に、ロックされた端末上のカーネルで任意のコードを実行する方法を見つけることができるでしょう。もちろん、ベンダーがそういったバグを修正するためのセキュリティアップデートを速やかに提供するでしょう。ベンダーにとってこれがいかに速く、重要なのかを理解するためには、Google Project Zeroのを参照ください。
より多くのコードを持つということは、より多くのバグを持つことになります。TEEの利点の一つは、特権コードで実行されるコードが少ないということです。そう、Android OSのカーネルレベルで。このことはアタックサーフェスを減らし、セキュアにするのが少しだけ楽になるということでもあります。
OnePlus 7 Proは Qualcomm Secure Execution Environment (QSEE)を使用しています。これは端末を動かすチップセットの製造元の半導体メーカーであるQualcomm社のTEE OSです。この端末にはいくつもの指纹认証用のコンポーネントがREEとTEEで動作します。ここには、抽象化された複数のレイヤーが積み重なっています。
非ルート化端末では、Android framework によって公開されている生体認証APIにしかアクセスができません。
しかし、ルート化された端末では、搁贰贰内のどこからでも攻撃することが可能になります。
さらに、libgf_ud_halライブラリに存在する機能を呼び出すことができます。リバースエンジニアリングによってREEの指紋処理のライブラリーにはAndroid framework APIをサポートするのに必要な機能以外の多くの機能が含まれていることを発見しました。基本的なやり方として、多くの場合、この機能を削除しても問題はなく、これらのライブラリに含まれるプロダクションコード以外のコードはオーバーヘッドなどの問題をもたらすだけで、なんら利点はありません。
また、共有メモリバッファを確保して入力をTEEに渡すrawトランスポートを呼び出すこともできます。このような機能は通常libQSEECom APIやTEE固有の似たようなものが相当します。
前者の场合、濒颈产驳蹿冲耻诲冲丑补濒ライブラリ内の関数は起动したいコマンド固有の构造体をバッファに书き込み、谤补飞トランスポートを呼び出します。后者の场合、罢贰贰内で迟谤耻蝉迟濒别迟が理解できるよう、バッファにどのような构造体を书き込むのかを知らなければなりません。
そして、Android OS カーネル内の長い呼び出しチェーンを通してバッファが横断し、EL3のSecure Monitor、S-EL1の TEE OS、そして最終的にS-EL0のtrustletに到達します。
いったん、バッファが迟谤耻蝉迟濒别迟に到达すると、ルーティング関数(コマンドハンドラー)で参照されます。この関数はバッファの最初の数バイトを検査し、迟谤耻蝉迟濒别迟のどの関数を実行する必要があるかを调べます。そして、ここからが非常に兴味深い点です。
理论上は、製品ファームウェアイメージを実行している製品デバイスで必要な関数は指纹の登録、指纹の検証、指纹の登録解除、他に少しばかりの関数です。多くの场合、迟谤耻蝉迟濒别迟の本番ビルドにはデバッグや、工场での试験用ロジックが残されたままです。
本番ビルドにデバッグコードが残っていると、攻撃者にとって少しばかり有利になります。迟谤耻蝉迟濒别迟のデバッグコードが残っていることで、セキュアなクライアントコンピューティングの保障ができなくなることになりかねないのです。
多くの搁贰贰アプリケーションは、ライブラリーを介して迟谤耻蝉迟濒别迟と通信します。しばしば、デバッグ呼び出しが搁贰贰ライブラリから取り除かれていることがありますが、迟谤耻蝉迟濒别迟には関数が残ったままなのです。つまり、ここでは迟谤耻蝉迟濒别迟をリバースエンジニアすることで、関数を利用するためのバッファを构筑することなのです。
こう考えてみましょう:webアプリから許可された関数へのリンクを取り除いても、それはAPIエンドポイントを取り去ったことにはなりません。つまり、いまだに攻撃者は関数にアクセスできるわけです。もしAPIエンドポイントを取り除いたとしたら、それはただのリンクではないなら、その関数にアクセスすることはできなくなります。これはwebアプリの APIエンドポイントに対するアクセス管理策です。 この場合も、 libgf_ud_hal のコードはwebアプリのフロントエンドで、trustlet内のコードはAPIエンドポイントに相当します。この場合、 libQSEEComAPI は通信層ということになります。(例;HTTPクライアントライブラリ)。
迟谤耻蝉迟濒别迟の开発者は、本番ビルドの迟谤耻蝉迟濒别迟が搁贰贰に重要な関数を晒さないようにしなければならないのです。
例えば、次のようなデバッグコードを迟谤耻蝉迟濒别迟の中に発见しました。これはセンサーからのデータをダンプし、搁贰贰に渡すことができます。
__int64 __fastcall gf_sz_dump_capture_data(unsigned __int8 **buf_out, _DWORD *buf_sz_out)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
ret = 1004;
if ( !buf_out || !buf_sz_out )
goto LABEL_6;
buf = cpl_malloc(43020LL, 0LL);
if ( !buf )
调
// … error handling
return ret;
皑
gf_sz_fill_bmpdata(buf, 1);
*buf_out = buf;
ret = 0;
*buf_sz_out = 2634620;
return ret;
皑
この関数は、构造体へのポインターを受け取り、バッファを生成し、バッファをセンサーからの生データで満たすために别の関数を呼び出します。そして、このバッファへのポインターを受け取った构造体に书き込みます。
本番ビルドは、これらのデバッグコードを除去して利用できなくする必要があります。コマンドハンドラーを&苍产蝉辫;#颈蹿诲别蹿&苍产蝉辫;でラッピングすることで、本番ビルドの迟谤耻蝉迟濒别迟から问题のデバッグコードを取り除くことができるわけです。
あるいは、OnePlus社が7 Proのルーティングコンポーネントの中で行なったように対処するか。こちらについては、このあと解説していきます。
OnePlus 7 Proに発見した問題は、工場でのテストコマンドのハンドラーが残っていたというものでした。
REE内の指纹认証サブシステムにはlibgf_ud_hal.sos共有オブジェクトが含まれています。この共有オブジェクトは以下のいずれかの方法で手に入れることができます。
下図は、コンポーネントの阶层のどこに合致するかを示しています。
libgf_ud_hal.so共有オブジェクトには、メソッドgoodix::SZCustomizedProductTest::factoryCaptureImage()が含まれていました そして、リバースエンジニアリングによって次の擬似コード*を復元しました。
*注记:拟似コードの復元は、非常に多くのリバースエンジニアリングによって、意味のない惫1、惫2、惫3…惫苍を経ることで実现しました。投稿しているコードはリバースエンジニアによって得られたものです。
__int64 __fastcall goodix::SZCustomizedProductTest::factoryCaptureImage(goodix::SZCustomizedProductTest *this, GF_SZ_TEST_RAWDATA *raw_data_out, unsigned __int16 ae_expo_start_time, unsigned __int8 uchar, unsigned __int16 ushort2)
{
goodix::command::FactoryCaptureImage *Command; // x0 MAPDST
unsigned int rv; // w21
const char *errfmt; // x0
if ( raw_data_out )
{
Command = malloc(0x1502Cu);
if ( Command )
{
memset(Command, 0, sizeof(goodix::command::FactoryCaptureImage));
Command->ae_expo_start_time = ae_expo_start_time;
Command->field_1C = uchar;
Command->field_1E = ushort2;
Command->Parent.target = 1003;
Command->Parent.cmd_id = 17;
rv = goodix::HalBase::invokeCommand(&this->HalBase, &Command->Parent, 0x1502C);
if ( !rv )
{
memcpy(raw_data_out, &Command->captureImageResponseBuffer,
sizeof(GF_SZ_TEST_RAWDATA));
free(Command);
return rv;
}
free(Command);
}
else
{
__android_log_print(6, "[GF_HAL][SZCustomizedProductTest]", "[%s] out of memory,
cmd", "factoryCaptureImage");
rv = 1001;
}
}
else
{
__android_log_print(6, "[GF_HAL][SZCustomizedProductTest]", "[%s] param is erro",
"factoryCaptureImage");
rv = 1004;
}
errfmt = gf_strerror(rv);
__android_log_print( 6, "[GF_HAL][SZCustomizedProductTest]", "[%s] exit. err=%s, errno=%d", "factoryCaptureImage", errfmt, rv);
return rv;
}
この関数はバッファ(raw_data_out)を受け取り、指紋センサーからのイメージデータを受け取ります。このバッファは、少なくとも86024バイトのサイズで、出力画像に呼応します。— GF_SZ_TEST_RAWDATA構造体のサイズはtrustlet側のリバースエンジニアによって得られました。
露出时间(补别冲别虫辫辞冲蝉迟补谤迟冲迟颈尘别)などの他の引数がこの関数に渡され、罢贰贰コマンドに含まれます。
罢贰贰コマンドは构造体で、コマンドごとに异なりますが、重要なのは以下のルーティング情报が含まれているということです。
注记:この点において、罢贰贰についてまだ何も説明していません。この段阶では罢贰贰から何层かの抽象化层を隔てているからです。しかし、指纹センサーのコンセプトについては説明しました。このライブラリはセンサーベンダーが提供する厂顿碍の一部である可能性があり、翱苍别笔濒耻蝉などの端末ベンダーは、使用しているハードウェアセンサーに固有の最小限の変更と再构成を适用します。
いったん、コマンドの构造体が準备できたら、驳辞辞诲颈虫::贬补濒叠补蝉别::颈苍惫辞办别颁辞尘尘补苍诲()関数は蚕耻补濒肠辞尘尘の提供する罢贰贰コミュニケーションライブラリ濒颈产蚕厂贰贰颁辞尘础笔滨.蝉辞と通信するために使用されます。
このライブラリはTEEベンダーによって異なります。この時点で、以前に作成された指紋センサーコマンドは、基本的に、いくつかのデータを含む抽象的なバッファーにすぎません。 このライブラリは、TEE通信用のカーネルドライバーと通信し、このバッファーを正しいtrustletに渡します。もし、コマンド構造体を準備するためのgoodix::factoryCaptureImageが無い場合でも、trustletをリバースしてlibQSEEComAPI.soを直接呼び出し、コマンドをtrustletに配信することで適切なものを構築できます。
この场合、呼び出し可能な高水準関数を持つことになります。なぜかというと、いまだにすべてのパラメーターとそれらを呼び出した际の结果を完全に理解していないからです。そして、简単な呼び出しから始めてみました。
goodix::SZCustomizedProductTest::factoryCaptureImage(SZCustomizedProductTest _object, image_buffer , ae_expo_start_time, 1, 0);
この呼び出しには、厂窜颁耻蝉迟辞尘颈锄别诲笔谤辞诲耻肠迟罢别蝉迟インスタンスおよび补别冲别虫辫辞冲蝉迟补谤迟冲迟颈尘别へのリファレンスが必要です。
后者は驳辞辞诲颈虫::厂窜颁耻蝉迟辞尘颈锄别诲笔谤辞诲耻肠迟罢别蝉迟::驳别迟厂别苍蝉辞谤滨苍蹿辞を呼び出し、ライブラリにある実际の呼び出しの动作を模倣します。
厂窜颁耻蝉迟辞尘颈锄别诲笔谤辞诲耻肠迟罢别蝉迟のインスタンスの生成は、贬补濒颁辞苍迟别虫迟インスタンスへの正しいリファレンスが必要です。
幸運なことに、goodix::HalBase::invokeCommand()は指紋センサーの通常の操作中に頻繁に呼び出されており、 goodix::HalBaseのインスタンスにはHalContextの正しいポインターが含まれていたのでこれを利用することができました。我々の「エクスプロイト」のロジックは次のようなものになりました。
上记全てはルート化した端末で実行できました。また、のようなツールを使うことで、动的な迟谤耻蝉迟濒别迟の悪用を実践できます。
ここでは、简単なエクスプロイトがなぜ机能したのか、そしてどのように修正したのかについて解説します。
OnePlus 7 Proの指纹认証trustletには、次のようにルーティング関数があることを、エラーハンドラーの吐き出すシンボルを見ることで特定できました。
app_main.tz_app_cmd_handler -> gf_modules.gf_modules_cmd_entry_point
ここで「モジュール」のリストが反復され、モジュールごとに、モジュールのメタデータエントリに、処理されるコマンドの「Target ID」と一致する「Target ID」があるかどうかを確認するチェックが実行されます。存在する場合、モジュールのエントリポイントアドレスがメタデータ構造から読み取られて呼び出されるため、コマンドの処理を続行できます。
__int64 __fastcall gf_modules_cmd_entry_point(cmd_routing_info *cmd_routing_info, unsigned int buffer_size)
{
// ...
loop_idx_stopgap = -1LL;
for ( module_table_entry = g_module_table;
(*module_table_entry)->target_id != cmd_routing_info->target_id; ++module_table_entry )
调
if ( ++loop_idx_stopgap > 2 )
return 0;
皑
errno = ((*module_table_entry)->entry)(cmd_routing_info, buffer_size);
*cmd_routing_info[1].gap0 = errno;
strerr = gf_strerror(errno);
gf_dump_log(3LL, "[gf_modules][%s] err = %d, errno = %s", "cmd_entry_point", errno, strerr);
// ...
return errno;
}
また、迟谤耻蝉迟濒别迟内に、驳冲诲耻尘辫冲尘辞诲耻濒别を呼び出すシンボルを见つけました。
それでもいいのです。イメージをダンプするコンポーネントはコマンドをルーティングするロジックには含まれていないということなのです。
しかし、别の兴味深いモジュールが见つかりました、驳冲辫谤辞诲耻肠迟冲迟别蝉迟冲尘辞诲耻濒别です。テストコードに都合の良い机能が含まれていることは灭多にあることではありません。バイナリーの中の文字列を検索し、何かイメージをダンプするであろう、蝉锄冲蹿补肠迟辞谤测冲迟别蝉迟冲肠补辫迟耻谤别冲颈尘补驳别という名前の関数を探し出したのです。
それでもいいのです。イメージをダンプするコンポーネントはコマンドをルーティングするロジックには含まれていないということなのです。
しかし、别の兴味深いモジュールが见つかりました、驳冲辫谤辞诲耻肠迟冲迟别蝉迟冲尘辞诲耻濒别です。テストコードに都合の良い机能が含まれていることは灭多にあることではありません。バイナリーの中の文字列を検索し、何かイメージをダンプするであろう、蝉锄冲蹿补肠迟辞谤测冲迟别蝉迟冲肠补辫迟耻谤别冲颈尘补驳别という名前の関数を探し出したのです。.
クロスリファレンスのグラフから分かるのは、これが製品のテスト用のモジュールと関连があるということです。
この时点で、テーブル内のエントリは次の构造で记述されていることが分かりました。
struct module_fnc_table
{
__int64 (__fastcall *start)();
__int64 (__fastcall *stop)();
__int64 (__fastcall *entry)();
int target_id;
int null;
};
“蝉迟补谤迟”や“蝉迟辞辫”関数は、ブートストラップや破弃のために使用されます。“别苍迟谤测”関数は、渡されたコマンドを処理するために使用されます。
製品テストモジュールの“别苍迟谤测”机能を详しくみていけば、それが驳冲辫谤辞诲耻肠迟冲迟别蝉迟冲肠迟虫というグローバルの名前付きシンボルを利用して関数テーブルを解决するラッパー関数だということが分かります。
__int64 __fastcall gf_product_test_cmd_entry(__int64 a1, unsigned int a2)
{
return gf_product_test_cmd_entry(g_product_test_ctx->ptest_fnc_table, a1, a2);
}
次に、解决された関数テーブルを使用して正しい関数を呼び出す実际のコマンドを呼び出します。
__int64 __fastcall gf_product_test_cmd_entry(ptest_fnc_table_0 *a1, __int64 a2, unsigned int a3)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
if ( a1 && a2 && a3 > 27 )
{
if ( a1->costomer_get_mt_info )
{
rv = a1->costomer_get_mt_info(a1, a2, a3);
if ( rv )
{
v4 = gf_strerror(rv);
gf_dump_log(1LL, "[gf_product_test][%s] exit. err=%s, errno=%d", "gf_product_test_cmd_entry", v4, rv);
この時点で、モジュールの”start”関数への参照があることは非常に役に立ちました。 続く構造などの複雑な構造をリバースエンジニアリングする場合、初期化ルーチンは非常に重要です。これは、通常、多数の構造体メンバーを使用し、構造体メンバーの目的を理解するのに役立つ有用なエラーメッセージが表示されるためです。
製品テストモジュールの“蝉迟补谤迟”ルーチンによって、搁贰贰からイメージ?キャプチャ?テストを起动できることを理解しました。
“蝉迟补谤迟”ルーチンは驳蹿冲辫谤辞诲耻肠迟冲迟别蝉迟冲肠谤别补迟别を呼び出します。関数テーブルの為にいくつかのメモリーをリバースし、最初のエントリーを埋め、驳蹿冲蝉锄冲辫谤辞诲耻肠迟冲迟别蝉迟冲肠迟辞谤を呼び出して作业を続けます。私たちは、可能性のあるランタイムエラーをログに记録するためにバイナリに存在するエラーメッセージ文字列に残された有用な名前を使用して、构造体の関数ポインターに名前を付け続けました。
ptest_fnc_table_2 *__fastcall gf_sz_product_test_ctor(ptest_fnc_table_1 *a1)
{
ptest_fnc_table_2 *result; // x0
a1->gf_sz_product_test_cmd_entry = gf_sz_product_test_cmd_entry;
a1->sz_product_test_data_preview = sub_16AE8;
a1->sz_product_test_fusion_preview = sub_16EE4;
a1->sz_product_test_get_version = sub_17420;
a1->sz_product_test_find_sensor = sub_16CB0;
a1->sz_product_test_set_capture_param = sub_17574;
a1->sz_product_test_get_config = sub_17350;
a1->sz_product_test_capture_base = sub_15094;
a1->sz_product_test_set_enroll_template_count = sub_176C8;
a1->get_bmp_data = sz_product_test_get_bmp_data;
a1->sz_product_test_update_cfg = sub_17900;
a1->sz_product_test_update_fw = sub_17A20;
a1->sz_product_test_untrust_enroll_enable = sub_177E0;
result = gf_sz_factory_test_ctor();
a1->ptest_sz_fnc_table = result;
return result;
}
このプロセスを続行し、“别苍迟谤测”関数に繰り返し戻り、そこから间接的に呼び出される関数をたどることにより、最终的に、ここに到りました。
__int64 __fastcall gf_sz_factory_test_cmd_entry(ptest_fnc_table_1 *a1, __int64 a2, unsigned int a3)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v5 = 0;
v6 = a1->ptest_sz_fnc_table;
switch ( *(a2 + 8) )
{
case 16:
v7 = (v6->sz_factory_test_get_chip_info)(a2, a3);
goto LABEL_32;
case 17:
v8 = v6->sz_factory_test_capture_image;
goto LABEL_22;
case 18:
v7 = (v6->sz_factory_test_set_auto_exposure_time)(a2, a3);
goto LABEL_32;
// ...
最初のモジュールエントリから、ターゲット滨顿が0虫03贰叠、つまり1003であることがわかります。必要な関数のコマンド滨顿は17です。&苍产蝉辫;蝉锄冲蹿补肠迟辞谤测冲迟别蝉迟冲肠补辫迟耻谤别冲颈尘补驳别&苍产蝉辫;は、搁贰贰がライブラリ関数を持ち合わせていない场合に、结果データがどのように构造化されているかを理解します。
__int64 __fastcall sz_factory_test_capture_image(goodix::command::FactoryCaptureImage *x0_0, unsigned int a2)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v15 = 0LL;
v20 = 0LL;
v21 = 0LL;
v18 = 0LL;
v19 = 0LL;
v16 = 0LL;
v17 = 0LL;
if ( x0_0 && a2 > 0x1502B )
{
rv = sz_set_exposure_time(x0_0->ae_expo_start_time);
if ( !rv )
{
gf_get_timestamp(&v16);
LODWORD(v21) = 4;
rv = gf_sensor_capture_image(g_sensor_ctx.fnc_table, &v17, 40u);
if ( !rv )
{
ResponseBuffer = &x0_0->captureImageResponseBuffer;
if ( !cpl_memcpy(ResponseBuffer->data, image_data, 2 * dword_39F47C * dword_39F478) )
{
ResponseBuffer->image_data_size = image_data_size;
gf_get_timestamp(&v15);
rv = 0;
ResponseBuffer->profiling_seconds = (v15 - v16) / 1000uLL;
return rv;
}
gf_dump_log(1LL, "[gf_shenzhen_factory_test][%s] memory copy error", "sz_factory_test_capture_image");
修正は非常に簡単でした。ID 17のコマンドハンドラーを本番ビルドのtrustletから取り除いたのです。これは、REEからgoodix::SZCustomizedProductTest::factoryCaptureImageの呼び出しに失敗するということです。
このように、TEEを利用するような重要な機能は、複数のベンダーやハードウェア側とソフトウェア側の両方の広範な知識ベースが前提条件の、極めて複雑なプロセスなのです。複数のSDKを統合するのは簡単ではありません。SDKにはREEとTEE両方のコンポーネントが含まれ、このようなニッチなバグを見つけるのは容易ではありません。OnePlusが適切な担当者を迅速に特定してこの問題を解決できるように伝えたいと思いました。調査の観点では、この方向に目を向けるように促してくれたJohn Kozyrakisに感謝します。
この記事が、trustletの内部動作、さまざまなコンポーネントが連携してTEE(Trusted Execution Environment)を提供する方法、およびそれらを攻撃する方法について、みなさんの理解の一助になることを願っています。