前回は大体6年前だそうです。Setsの仮想.dll対応関係などが確認できるReadApiSetSchema更新しました。1903のが読めるようになってます。SDKが提供する.libファイルには関数群を提供するもの、単一.dllへの参照を提供するものがありますが、複数.dllへの参照を提供するものがあることは意外と知られていません。今日はそういった複数の.dllへの参照を提供する.libの中でも代表的なOneCoreシリーズとMinCoreシリーズについて私の持つ知見を紹介しようと思います。※これらは私が独自に調査蒐集した情報であり、公式の動作を保証するものではありません。SetsとはWindows7から入った「OSのAPI群を厳格な名前空間に再配置したもの」及び「それらの名前から実体へリンクするライブラリローダー」です。Sets上はapi-ms-win-core-version-l1-1-0.dllにより提供されており、実行時に適切な.dll(環境によりversion.dllだったりKernelBase.dllなど)にリンクされ実行されます。Windows10においては旧来のWin32APIと呼ばれていたものは全てカバーされており、旧来の.dllにリンクせずともほぼすべてのアプリを実装することが出来ます。Setsのカバー範囲が狭いため、それらのOSでの動作も必要な場合はまだ安易に利用できる状態ではありません。ライブラリローダー自体が別系統であるため、DLLプリロード攻撃などに対しても耐性があります。公式には旧来の方式に比べメモリー使用量が削減されることもあると説明されています。OneCoreとはAPI関係で数ある.libをすべてまとめたオールインワンな.libです。具体的にはOneCore系の.libにさえリンクしておけばOS提供のAPI群は全て使用することが出来ます。リンク時に必要なAPIのみ参照する方式なのでexeサイズの肥大化なども特にありません。Setsの使用有無で若干のバリエーションがあります。OneCore_downlevel.lib旧来の.dllで提供できるAPIだけに絞ったOneCoreで、リンクされる.dllも全て旧来の.dllになります。Windows7/8.1などの古い環境でも動作するアプリを作る開発者向けです。Setsで提供するOneCoreです。基本的にWindows10でしか動作しないようになりますが、全てのAPIを使用できDLLプリロード攻撃などに対しても耐性があります。今はまだ遠い未来の話ですが、Windows10未満のOSがすべてサポート切れした後、MSはこれを使うことを主流としたいのだと予想されます。WindowsSDKのバージョンが古い場合は提供されていないことがあります。Setsで提供するOneCoreです。コード中で使用するAPIによって対象OSが変化してしまう問題があるため、私個人は使用を回避すべきだと考えます。Windows10未満で動作する必要があればOneCore_downlevel.lib、Windows10でしか動作しなくてよいならOneCore_apiset.libの使用をお勧めします。使用する場合は各APIのMSDNを良く参照の上、OSごとの動作確認をしっかり行うように注意してください。WindowsSDKのバージョンが古いとOneCore_apiset.libと同じように動作することがあります。MinCoreとは旧来の.dllは環境(モバイルなど)により提供されないことがあります。Setsへリンクする.libが必要となりました、それがMinCoreです。Setsを利用したい場合などがあげられます。必要が無ければ基本的にはOneCoreを使うべきです。Setsを提供するMinCoreです。これのみを使用してリンクエラーになっていないexeはWindows7上でも動作することが確約出来ます。Setsはごく少量であるため(MSDN)あまり多くのことはできないでしょう。具体的にこのMinCoreで動作が保証される特殊環境に何があるかは私は把握していません。Setsを提供するMinCoreです。Setsへのリンクも提供されており実態が不明確です、可能なら使用は避けた方がよいでしょう。Setsはdownlevelに比べれば多彩ですがまだかなりの制限があると思うべきです(MSDN)最低でもWindowsPEでMinCore.libの動作が保証されています(MSDN)使い方では実際に使ってみましょう。要点は二つ、NODEFAULTLIBでkernel32.libとole32.libを外す、必要なOneCoreかMinCoreへのリンクを追加する、だけです。手動で何かしらシステム.libへのリンクを追加しているなら撤去する必要があります。実際にやっていきましょう。改造元として適当なC++プロジェクトを用意しました。これはGetFileVersionInfoExWを使用していますがOneCoreもMinCoreも利用していないので、ビルドしてdumpbinすると以下のようにversion.dllへの参照がついています。改造していきましょう。従来のlibと同時に使用すると正しく適用できないため外します。一部は暗黙でリンクされるので追加で除外設定を入れます。リンクする.libを変更します(今回はわかりやすくOneCore_apiset.lib無事api-ms-win-core-version-l1-1-0.dllを参照する形に変わりました。※OneCore_downlevel.libかOneCore.libの場合version.dllのままとなります。今回はコード中からOneCore_apiset.libのリンク指示を入れましたが、プロジェクトプロパティの追加の依存ファイルでももちろん問題はありません。終わりに今回はOneCore/MinCoreについてある程度知見を広めることを目的とした記事でした。巷では不親切なドキュメントのせいで誤ってリンクしてしまい問題になるケースも多く、そういった悲劇を少しでも減らしたいという思いもあります。自分自身OneCoreやMinCoreに思うところがないではないですが、OneCoreの煩雑なリンク作業の手間を軽減しようとする試み自体は良いことだと思います。非公式ですが東方鬼形獣のバグを直して回るパッチ作ったので公開します。Guard(解説記事)の派生技術をご紹介。指定する前置きWindowsの32bitアプリケーションはアドレスランダマイズが不足しており危険だという話はPCセキュリティ界隈では比較的よく聞く話です。その中でも特に脆弱なのがdllのアドレスランダマイズで、過去の記事でも触れたとおり比較的軽微なメモリ漏洩でも関数のアドレスが推測可能になるのが現状です。以降はサンプルコード前提で進みます。本機能を有効にする/guard:exportsuppressは2019/09/17現在、MSDN上では非公開なオプションであり正式に提供されている機能ではない可能性があります。今後正式に提供されるとしても挙動など変更になっている可能性を念頭においてご利用ください。話をシンプルにするためにexport関数のアドレスを漏洩する関数を持つdllを用意します。int>(proc);}leak関数はprocの関数を漏洩しています。関数ポインタの取得ではない旨を示すために__declspec(guard(ignore))するのを忘れずに。ESの対象外となるため攻撃対象のproc関数はただ標準出力にメッセージを出すだけの関数です。攻撃側は既に脆弱性を突かれている前提でleak関数の返り値を実行するのを直に実装します。0;}実際に上記のソースコードと実行ファイルがフォルダ1に入れてあるので実行してみましょう、特に問題もなくproc関数が実行できてしまいます。ESで対抗しましょう。有効でないなら有効にします。※本アプリでは最初から有効になっています。/guard:exportsuppressを設定するだけです。今回も設定済みの物をフォルダ2に入れてあるので実行すると、今回はクラッシュしました。「proc関数はGetProcAddressされていないのにアドレスを知っているのはおかしい、攻撃だな!」との判断により強制終了された結果です。このチェックによりエラーとされない方法は以下の通りです。GetProcAddressによりアドレスを取得する静的リンクして関数を呼び出すdll内部で関数ポインタとして取得されたものを受け取る/guard:cfを解除する(セキュリティが低下します)一般的な使い方をしていれば回避する方が難しいのがよくわかるかと思います。上記以外で不正に取得したアドレスでcallやjmpすることが難しくなるというわけですね。このチェックは/guard:cfの機能により行われるために/guard:cfも有効でないといけません。ESは/guard:cfと/guard:exportsuppressをつければ有効になる有効になると不正に入手したexport関数アドレスは実行できない実用性GetProcAddressを呼び出してしまえば許可されてしまうため、100%の防御というよりは攻撃者の手札を減らす類のもの、場合により気休め程度の効果にしかならないことも。Guardが有効でなければ効果が薄いのと、一定より古いOSではそもそも無視される問題があり頼りづらい。Overflowなど任意コード実行を目指す攻撃は動的アドレスへのcallやjmpの利用を目指すことがあるため、そういった攻撃を防げるのは水際防御としては有効そう。Checkなどの技術との併用は必須。GuardはJITエンジンなど食い合わせが悪い機能が多く採用が難しいという問題がある、採用する際は十分テストを行い問題ないことを確認したい。メモリや計算量にオーバーヘッドが存在するため速度がダイレクトに響く分野での採用も難しい。メリットは小さくデメリットは大きい傾向がある技術、採用はほかの技術を優先したい。Guardの派生技術ということで取り上げましたがいかがだったでしょうか。ESのかかったバイナリは手元ではEdgeしか発見できていないレア物だったりもします。無条件で使えるものではないのは確かですが、ここまで誰も使わないほど有用性が低い技術でもないと思うので、少しでも採用アプリが増えてくれたらうれしいんですがどうでしょうね?ではまた。今回は被害を防ぐのではなく被害を抑える系技術のご紹介。前置き攻撃を防ぐ技術は数えきれないほど存在しますが、それでも100%完全に攻撃を防ぐことは現代社会においては非現実的とされています。そのため攻撃を防ぐことだけ考え、攻撃成功した後のことを考えなかった場合、あっという間に全てを乗っ取られ取り返しのつかない事態になってしまいます。そこで攻撃が成功しても被害の拡大を防ぐ技術についても日々研究されています。Guard(以降ACG)です。以降はサンプルコード前提で進みます。ArbitraryCodeGuard.zip例その1:メモリ書き換えによるコード注入フォルダ1内のTarget.exeを攻撃します。Target.exeの実装は以下の通り。std::cin.get();}ただ入力待ちするだけのいたって善良な子です。こいつにdllを注入します。今回は注入に成功したことが分かればいいので特に悪事を働かせたりはしません。TRUE;}ただロード時に「Injection!」と出力するだけです。次に攻撃者ですが、既に侵入に成功している想定で普通に攻撃を実装します。};長くなる上ACGとは関係がないので端折りますが、要するに上記のコードを相手プロセス内に配置して実行するコードです。内容的には::LoadLibraryA(“dll.dll”);に相当します。Target.exe>Attacker.exeの順に起動してみましょう。上記のようにTarget.exe側にInjection!と出力されたはずです。※普通に攻撃処理なのでアンチウイルスなどに止められる可能性があります。次はこの攻撃をACGで失敗させます。例その2:ACG実演ACGとはメモリアクセス権(読み書き実行)の変更権限を制限する機能です。攻撃者に侵入される前のアプリでACGを有効にしていたと仮定します。sizeof(policy));}上記でACGが有効になるのでmainの頭で呼んでおきます。上記の変更が入ったものがフォルダ2に入っているので実行します。これは実行権限付きメモリー確保に失敗したためです。実行権限が付与できなければいくらコードを書き込めても実行させることが出来ず任意コード実行まで到達できません。このようにACGを有効にしておくことで、マルウェアに制御を奪われても被害拡大を防ぐことが出来ます。例その3:拡大阻止ではなく防御には使えないのか結論から言うと防御には使えません、以下で実演します。Target.exe内でACGを有効にします。入れた物がフォルダ3に用意してあるので実行します。このように、あくまで自身が他者を攻撃しないようにするだけで、他者からの攻撃を防ぐ用途では使えません。例その4:攻撃者にACGは外されないのか結論から言うと外せません。sizeof(policy));}フォルダ4に上記修正を入れました。普通の方法では後から外すことはできません。一般的なプログラマー視点であれば実用性はほぼないとみてよい。ACGを導入してもファイルシステム経由でのdllインジェクションなどがありうるため、基本的に単独では意味が薄いのも痛い。ユーザーがとても多く、防御策も軒並み全部採用している状態でなおセキュリティを高める場合にようやく選択肢に上る程度。上記に該当するのは例えばブラウザなど。いえそれほどデメリットもきつくないので、食い合わせの悪い技術(JITコンパイラなど)を使わないならば採用してもよい。終わりに今回は啓蒙というよりは紹介で、特に採用必要性のない技術でした。いえブラウザ保護などには役立っているので、世の中はこういうものまで含めた積み重ねで安全が担保されているのだなと思っていただければ。本記事でACGとは何かがちょっとでも伝わっていれば幸い。デバッグ難度、権利問題、いろいろあるとは思います。今回はそのうちの1つ「VisualStudioに比べてセキュリティは低下しないの?」を調べてみました。終わりに調査範囲VisualStudioのコンパイラ及びリンカ提供の機能のうちセキュリティ的に主要機能と思われるものを独断と偏見で選びだし調査しました。コンパイラ/GS/guard:cfリンカ/DYNAMICBASE/SAFESEH/GUARD:CFまたこれらについては過去に本ブログで個別記事を書いています。結論から先に書いておきます。ある程度は問題ないですが、節々が怪しいので未実装の機能を使わないとしてもセンシティブなプロダクトでは危ないと感じました。手順に従いVisualStudioでビルドしたclang8.0.0の32bit版を使用。VisualStudioは2019で、clangビルドでコンパイルエラーした箇所のみパッチを当てた物。正しい動作が得られなかったものについてのみ検証時のorigin/masterのHEADである9fa56f7829aa5f5cca911c400bb43d854b46dc15をビルドしたものも使用した。0;}上記をオプションありとなしでコンパイルして大量の入力を行い、オプションなしはデバッガにてreturnまで到達してから異常終了すること、ありはreturnより前に検知され強制終了するかを検証した。両者共に正しく強制終了した、逆アセンブラ結果もカナリアの値も問題なし。/guard:cf(ControlFlowGuard)本項はコンパイラとリンカ両方合わせての検証となる。a.cpp-cfguardオプションはそのままだと無視されるバグが存在しており、パッチを当てて有効化して使用した。CmdArgs.push_back(Args.MakeArgString(TripleStr));パッチを当てない場合-vオプションでいったん-cc1ありの長いコマンド例を出した後、そこに-cfguardを加えて実行すると認識する。0;}上記をオプションありとなしでコンパイルしてそれぞれの仮想関数テーブルや関数ポインターなどをデバッガで別関数へ書き換え実行、オプションなしは別関数が実行されること、ありは強制終了するかを検証した。結果clang+lldでは強制終了しなかった。functions.とある通りチェック処理は未実装の模様。解析した範囲ではチェック関数用の関数アドレスリストの生成や有効化フラグ自体は問題なく発行されているように見えた。0;}上記をオプションありとなしでコンパイルしてデバッガ越しに実行、a.exeがロードされるアドレスを確認しtouchした後再度同様の作業をする、オプションなしはアドレスが変化していないこと、オプションありは変化していることを検証した。問題なし、ロードされるアドレスも自身が知っているランダマイズの範疇であった。0;}上記をコンパイルオプションありとなしでコンパイルしてデバッガ越しに実行、大量の入力によりseh連鎖を上書きした後デバッガにより例外を発生させた、オプションなしは異常終了すること、オプションありは強制終了することと検証した。結果clang+lldでは常に強制終了した。-safeseh:noを指定してもsafeseh有効のバイナリを吐くようで(おそらくバグ)、無効にする方法が見つからなかった。そのため、SafeSEHが正しく作用した結果強制終了しているのか、なんらかの別要員でたまたま強制終了したのかの区別がつかなかった。一応解析した範囲ではSafeSEHのフラグが立っており正常に適用されているように見えるので動いている可能性が高いとは思える。終わりに数にして4つのみの検証となったが未実装1、不具合により検証不能1という結果になった。実際のところこれが信頼に値するかはわからないが、自分の感性においてはまだ不安が残る数値だと考える。いえchromeでの採用実績があるとのことなので使用を志す人は検討してみるのもいいだろう。ちなみに自分が調べた範囲だとchromeは/guard:cfを有効でコンパイルしているようだが、当然チェック関数は未実装であり、ControlFlowGuardは機能していなかったことをここに記録しておく。AwoThemes.。