2025/04/18 12:18 iOS 18.4 – dlsym considered harmful

やっほー、ロボ子!今日もITニュースの時間じゃぞ!

こんにちは、博士。今日のニュースは何でしょう?

今日はiOSのカスタムアプリで`dlopen()`と`dlsym()`を使った時に、`strcmp()`関数でバグるって話じゃ。

`dlopen()`と`dlsym()`ですか。動的にシンボルを解決する時に問題が起きるんですね。

そうそう!特にarm64eアーキテクチャで、`strcmp()`を動的に解決して使うとアプリがクラッシュするらしい。

それは大変ですね。原因は何なのでしょう?

どうやらポインタが不正に署名されてるのが原因みたいじゃな。カーネルポインタがPCレジスタに存在するとか。

ポインタの署名ですか。セキュリティ関連の問題でしょうか?

`strcmp`が`__platform_strcmp`として再エクスポートされてて、`libsystem_platform.dylib`で遅延解決されるのがミソじゃ。

遅延解決ですか。初回実行時に実際の実装が解決されるんですね。

`EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER`フラグってのがあって、`dlsym()`内でリゾルバオフセットが取得されて、アドレスに変換された後、署名されて呼び出されるんじゃ。

なるほど。リゾルバ関数から返されたポインタが、オフセットに変換される前に署名解除されていないのが問題なのですね。

そう!IDA Proでdyldのコードを確認したら、XPACI命令が欠落してて、リゾルバからの戻り値が直接オフセットに変換されてたってわけ。

XPACI命令の欠落ですか。それが二重署名につながるんですね。

Apple A15 SoCのEnhancedPAC2では、PAC署名がポインタの上位ビットとXORされるんじゃ。不正な署名は、2回目の署名がカーネルポインタとして扱われた場合に発生する。

カーネルポインタとして扱われる条件があるんですね。

ビット55がセットされていると、ポインタがカーネルポインタとして扱われるらしいぞ!

それは興味深い発見ですね。

でも、`strcmp`をインポートする全てのアプリがクラッシュするわけじゃないんだって。dyldのローダーが`Loader::skipResolver`引数付きで`hasExportedSymbol`を呼んでるから、バグは発生しない場合もある。

影響範囲は限定的なんですね。原因はdyldの内部構造、共有キャッシュ、iPhoneのPAC実装にあると。

そうそう!dyldのコードにXPACI命令が欠落しているのが根本原因じゃ。

勉強になりました。しかし、こんな深いところにバグが潜んでいるとは驚きです。

じゃろ?ところでロボ子、strcmpって何の略か知ってるか?

文字列比較(string compare)の略ですよね。

ブー!正解は… "String Compare To My Problems!" …って、つまらんかったかの?

…博士、お疲れ様です。
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。
