Archive for 2014/02

ネイティブコードから C# へのコールバックについて補足

今回は、2012/05/14 のエントリ(「ネイティブコードから C# のメソッドをコールバック」)についての補足を書きます。補足するのは、次の二点です:

・コールバック関数のコーリングコンベンション
・ネイティブコードへのデリゲートオブジェクト渡し

それぞれについて、以下に記します。

■コールバック関数のコーリングコンベンション
2012/05/14 のエントリでは、マネージドコードからアンマネージコードに渡すコールバック関数の関数ポインタを得る API として、GetFunctionPointerForDelegate() を紹介しました。この API で取り出す関数ポインタに対しては、アンマネージコード側からみた場合、留意すべき点があります。リファレンスページの “Remarks” の項にある注意書きです。

 Marshal.GetFunctionPointerForDelegate Method
 http://msdn.microsoft.com/en-us/library/at4fb09f(v=vs.90).aspx

上のページの “Remarks” の項には、次の注意書きがあります:

The delegate d is converted to a function pointer that can be passed to unmanaged code using the __stdcall calling convention.

つまり、アンマネージコード(ネイティブコード)から見た場合、マネージコードから渡される関数ポインタのコーリングコンベンション(呼出規約)は __stdcall になるので、アンマネージコード側では、受け取る関数ポインタの型を、__stdcall 付きで宣言しておく必要がある、というわけです。__stdcall をはじめ、Visual C++ の C/C++ コンパイラにおけるコーリングコンベンションの説明は、リファレンスの次のページをご覧ください。

 Argument Passing and Naming Conventions
 http://msdn.microsoft.com/ja-jp/library/984×0h58(v=vs.90).aspx

デフォルトのコーリングコンベンションは、スタックに積まれた手続きの引数を、手続きを呼び出した側がスタックから取り除く __cdecl であり、呼び出された手続きが取り除く __stdcall とは異なりますので、その点に注意して下さい。

さて、アンマネージコードの既存のライブラリがあり、そのライブラリに対してコールバック関数を設定するマネージドコードを実装しようとした際、既存のライブラリが受け取るコールバック関数が __cdecl であった場合は、どうすればよいのでしょうか?そのままでは、コーリングコンベンションが合っていないため、実行時エラーを引き起こす可能性があります。

ご安心下さい。その問題は、マネージドコードからの呼び出し方を変えることで解決できます。これは、冒頭に示した二点目の補足に関係します。

注意:実は、WinCE/WEC では、後述するようにコーリングコンベンションの問題はありません。この問題を考慮する必要があるのは、フル版の .NET Framework の場合だけなのです。

■ネイティブコードへのデリゲートオブジェクト渡し
2012/05/15 のエントリでは、マネージドコードからアンマネージコードへ渡す関数ポインタを得るために、delegate 実体に GetFunctionPointerForDelegate() を適用する必要があると述べました。しかし、この呼び出しは必須ではなく、delegate 実体をそのまま渡すことができます。MSDN にある .NET Framework プログラミングガイドにある、「アンマネージコードとの相互運用」にあるサンプルコードでも、delegate 実体をそのまま渡しています。2012/05/15 のエントリを書いた時点では、そのことを理解していませんでした。ごめんなさい。

MSDN にあるサンプルコードは、次のページをご覧ください:

 Callback のサンプル(マネージドコード)
 http://msdn.microsoft.com/ja-jp/library/5zwkzwf4(v=vs.90)

 PinvokeLib.dll(アンマネージコード)
 http://msdn.microsoft.com/ja-jp/library/as6wyhwt(v=vs.90).aspx

さて、コーリングコンベンションに話を戻します。.NET Framework には、マネージドコードとアンマネージコードの間での、(アンマネージコードにおける)コーリングコンベンションを表現する UnmanagedFunctionPointerAttribute というクラスがあります。このクラスを使って、delegate のコーリングコンベンションを変更できます。具体的には、delegate を宣言する際、次のように属性記述すれば、delegate のコーリングコンベンションを __cdecl に設定できるようです:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate bool FPtr( int value );


しかし、この UnmanagedFunctionPointerAttribute は、WinCE/WEC 環境、つまり .NET Compact Framework ではサポートされていません。さらに、UnmanagedFunctionPointerAttribute クラスのコンストラクタ引数である、enumeration 型の CallingConvention は、.NET Compact Framework の場合には、メンバとして Winapi しか持たず、これは __cdecl に対応するとリファレンスに書かれているのです。

 CallingConvention Enumeration
 http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention(v=vs.90).aspx

このリファレンスには、デフォルトのコーリングコンベンションを示す Winapi は、「Windows 環境」、つまりフル版の .NET Framework では __stdcall に対応し、WinCE/WEC 環境(.NET Compact Framework)では __cdecl に該当するという旨の説明があります。これは一体、どいうことなのでしょう?Win32 API は、WINAPI 付きで宣言されており、__stdcall のコーリングコンベンションを設定されています。一方、UnmanagedFunctionPointerAttribute が __cdecl にしか対応しないのでは、両者が整合しないということです。

実は、WinCE/WEC のコーリングコンベンションは __cdecl だけなのです。このことは、次のヘッダファイルを見れば分かります:
 %_WINCEROOT%/public/common/sdk/inc/windef.h

windef.h には、次の行があり、’__stdcall’ が __cdecl の別名として定義されているのです。

#ifdef UNDER_CE
#define __stdcall __cdecl // Note this doesn't match the desktop definition
#define _stdcall __cdecl // Note this doesn't match the desktop definition
#endif


従って、.NET Compact Framework の場合には、コーリングコンベンションのことを意識する必要は、ありません。とはいえ、.NET Compact Framework 版と .NET Framework 版とで、アンマネージコードを含めてソースコードを共有できるように、可搬なコードを書く場合には、.NET Framework 版でも問題なく動作するよう、コーリングコンベンションを意識する必要があります。

■まとめ
今回は、2012/05/14 のエントリに対して二つの補足を書きました。二つのうち、コーリングコンベンションについては、WinCE/WEC では考慮せずともよいのですが、Win7 や Win8 でも動かせる可搬なコードを書く場合には、考慮する必要があります。

もう一つの、デリゲート渡しについては、わざわざ GetFunctionPointerForDelegate() を呼ぶ必要はない、というのが結論です。これは、2012/05/14 のエントリで、どちらかといえば、不適切な内容だったと思います。同様の見解が、.NET Framework に関する次の記事にも書かれています:

 C# からコールバック関数を使う C の関数を呼ぶ
 http://sgry.jp/pgarticles/cs_pinvoke_callback.html

今回の補足ですが、2012/05/15 のエントリに頂いていたコメントを拝見して書きました。コメントを頂いたのは、2013/05/14 ですから、半年以上の時間が経ってしまったのですが、参考になりました。有難うございます。

Add comment 2014/02/16 koga

WinCE/WEC の Secure Loader

前回(2013/02/10)から、一年ほど間が空いてしまいました。その間に、Windows Embedded Comact の最新版である Windows Embedded Comapct 2013 (WEC 2013) の一般提供も始まりました。この Blog でも、今後 WEC 2013 のことも交えて書く予定です。

ですが、今回は、前回の続編となることを書きます。組込み機器用の OS として WinCE/WEC が備えている、セキュリティを確保するための機能です。

■拡張性と脆弱性
前回は、ActivateDevice[Ex]() を用いてデバイスドライバを動的ロードすることができると説明しました。これは、便利な機能である反面、脆弱性の要因でもあります。

デバイスドライバを動的ロードできるということは、OS イメージに含めていないデバイスドライバを、OS の動作中に追加して、あらかじめ組み込まれてはいない機能を利用できるということです。たとえば、Flash メモリなどに配置した OS イメージを書き換えてアップデートする場合に、Flash メモリ用のデバイスドライバを OS イメージに組み込んでいなくとも、アップデート処理を実行するアプリケーションの .exe と、Flash メモリ用のデバイスドライバの .dll、および新しい OS イメージを USB メモリに入れて WEC デバイスにマウントし、アプリケーションを実行してアップデート処理を行うことが可能です。アップデート処理を実行するアプリケーションは、最初に ActivateDevice[Ex]() を使って、Flash メモリ用のデバイスドライバをカーネルにロードさせ、その後、DeviceIoControl() を使ってデバイスドライバを呼び出すことにより、OS イメージを Flash メモリに書き込む、というわけです。

デバイスドライバ、つまりカーネルモジュールを動的ロードできれば、このように拡張性を得られる一方で、悪意を持ったソフトウェアによる攻撃を容易にするという側面もあります。(悪意を持った)アプリケーションが可能な攻撃は、仮想メモリと実行モードによって制限されていますから、カーネルに対して致命的な障害を与えることは困難です。しかし、(悪意を持った)カーネルモジュールをアプリケーションがロードできてしまえば、カーネルのメモリ空間をアクセスできますし、プロセッサの特権モードで動きますので、カーネルに致命的な障害を引き起こすことは難しくありません。
(WinCE 5.0 までの純粋マイクロカーネルの場合には、デバイスドライバは、ユーザプロセスであるデバイスマネージャにロードされて動きますので、カーネルの保護は、より堅牢だったと言えます。)

■WinCE/WEC の Secure Loader
WinCE/WEC には、悪意を持ったソフトウェアによる攻撃を防ぐ機能として、Secure Loader (Loader Verifier Module) が用意されています。これは、あらかじめ OS イメージに組み込まれているアプリケーションや、認証されたアプリケーション以外は実行できないようにする仕組みです。WEC 7 のリファレンスでは、次のページで説明されています:

 Security Loader (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg155695(v=winembedded.70).aspx

Secure Loader を有効にした OS イメージを作るには、OS Design のカタログ項目において、Loader Verifier Module を選択してビルドします。Loader Verifier Module は、Platform Builder のカタログ項目ビューにおいて、次の場所にあります:

 
  Core OS
   Windows Embedded Comapct
    Security
★    Loader Verifier Module

Secure Loader を有効にすると、OS イメージに組み込まれていないアプリケーションは、実行できなくなります。つまり、上で述べたデバイスドライバの動的ロードの例のように、USB メモリに入れたアプリケーションを実行しようとしても、実行できません。

OS イメージに組み込まれていないアプリケーションの実行を許さない処理は、WinCE/WEC のローダーによって行われます。ローダーが実行ファイル(executable)をロードする際に、ロードして構わないファイルかどうかをチェックして、条件に合わないものが指定された場合はエラーとするのです。

ローダーのソースコードは、
 C:/WINCE700/private/winceos/COREOS/nk/kernel/loader.c
にあります。loader.c で実装されている OpenExecutable() が、実行ファイルをロードする関数ですが、OpenExecutable() は、OS イメージに収録されたファイルに対しては OpenFileFromROM() を、ファイルシステム上のファイルに対しては OpenFileFromFilesys() を呼び出します。OpenFileFromROM() と OpenFileFromFilesys() は、FSOpenModule() という関数を呼び出して実行ファイルの内容をロードしますが、この FSOpenModule() において、ロードして構わないファイルかどうかのチェックが行われるようです。FSOpenModule() のソースは
 C:/WINCE700/private/winceos/COREOS/nk/kernel/fscall.c
にありますが、この関数は、filesys.dll を呼び出します。filesys.dll のソースは開示されていないため、詳細は分かりませんが、OpenFileFromROM() の場合、FSOpenModule() に渡す第二引数のフラグビット列で OPENMODULE_OPEN_ROMMODULE ビットが必ず 1 になるため、このビットを使って、OS イメージに収録されたファイルと、それ以外のファイルを区別して扱っているのだと思われます。

なお、Secure Loader 機能自体は、filesys.dll ではなく、lvmod.dll という DLL で実装されており、filesys.dll が lvmod.dll を呼び出すようです。カタログ項目の Loader Verifier Module を選択すると lvmod.dll が OS イメージに組み込まれることは、
 C:/WINCE700/public/common/oak/files/common.bib
を見ると分かります。Loader Verifier Module に関連付られた SysGen 変数は CE_MODULES_LVMOD ですので、これをキーにして common.bib を検索してみて下さい。

■署名による認証
Secure Loader を有効にすることにより、OS イメージに組み込まれていないアプリケーションを実行できないようになります。これでセキュリティは非常に高まりますが、拡張性は損なわれます。あらかじめ固定された機能しか動作しない、昔ながらの組込み機器であれば、ROM に配置した OS イメージに組み込まれたアプリケーションしか動かせなくても、問題ありません。しかし、サードパーティ製のアプリケーションを動かせるようにしたり、冒頭で述べたアップデート機能など、特定の場合にだけしか使わない機能を OS イメージには組み込まず、必要な時にだけ使いたい、という場合には、 OS イメージに組み込まれたアプリケーションしか実行できないのでは、対応できません。

ご安心下さい。WinCE/WEC の Secure Loader は、そのような場合に対応するための機能も備えています。実行ファイルに対する署名と、署名に対する認証機構により、OS イメージに組み込まれていないアプリケーションでも、認証できるものは実行を許すようになっています。上述した lvmod.dll が、この認証処理を行います。lmvod.dll が行う認証処理は、上述したローダーから呼び出される他、API としても提供されています。Secure Loader の API については、リファレンスの次のページをご覧ください:

 Security Loader Reference (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg155306(v=winembedded.70).aspx

このリファレンスのページを見ると、ファイルを認証する API に加え、ブロックリスト、つまり、署名に対して認証エラーとするファイルの一覧を扱うものがあります。署名されており、署名に対する認証処理が成功するアプリケーション(実行ファイル)であっても、不正な動作をすることが分かった場合に、それをブロック対象として登録し、実行できないようにすることが可能なのです。

このように、不正なソフトウェアが実行されるのを防ぎ、セキュリティを確保するための仕組みを、拡張性と共に提供しているのが WinCE/WEC の Secure Loader というわけです。Secure Loader 用に、実行ファイルに署名を付ける手順は、リファレンスの次のページで説明されています:

 Signing Binaries (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg156011(v=winembedded.70).aspx

 Deploy an Application on an OS with Security Loader (Compact 7)
 http://msdn.microsoft.com/en-us/library/jj200466(v=winembedded.70).aspx

WEC の最新版である WEC 2013 のリファレンスにも、同じ内容の説明があります:

 Signing Binaries (Compact 2013)
 http://msdn.microsoft.com/en-us/library/gg156011.aspx

 Deploy an Application on an OS with Security Loader (Compact 2013)
 http://msdn.microsoft.com/en-us/library/jj200466.aspx

■WinCE/WEC のセキュリティ
組込み機器のセキュリティは、一つの方策だけで確保できるものでは、ありません。機器の特性に応じて、複数の方策を組み合わせる必要があります。WinCE/WEC でのセキュリティの考え方や実現方策については、以下のページが参考になるでしょう。興味のある方は、ご覧になってみて下さい。

 Security for Windows Embedded Compact (Compact 7)
 http://msdn.microsoft.com/en-us/library/ee498894(v=WinEmbedded.70).aspx

 Trusted Environment Creation (Windows Embedded CE 6.0)
 http://msdn.microsoft.com/en-us/library/ee498576(v=winembedded.60).aspx

 Windows Embedded CE 6.0 Security Model
 http://msdn.microsoft.com/en-us/library/ee497961(v=winembedded.60).aspx

 Security for Windows Embedded Compact Code Samples (Compact 7)
 http://msdn.microsoft.com/en-us/library/hh802407(v=winembedded.70).aspx

Add comment 2014/02/02 koga


Categories

Links

Posts by Authors

Recent Posts

Calendar

2014年2月
« 2月   8月 »
 1
2345678
9101112131415
16171819202122
232425262728  

Posts by Month

Posts by Category

Meta