サービスを最小特権で実行する
今回は Windows サービスを作成する上でのセキュリティ上のポイントを軽く紹介する.
Windows Vista では,サービスをより安全に実行するために Service Control Manager (SCM) の改善が行われている.ポイントとなるのは,必要特権リストの指定が可能になったこと,および制限された SID を割り当てられるようになったことだ.
たとえば,特定ファイルを物理メモリ上に保持し続けるサービスを作りたいとする.この処理をサービスにする必要があるのは,それが特権を必要とするからだ.VirtualLock API でロック可能なメモリ領域は通常 30 ページに制限されており,SetProcessWorkingSetSize API でその制限を拡大するには,SE_INC_BASE_PRIORITY_NAME 特権が必要である.しかし,単純に System アカウントで動くサービスを作ったのでは,余分な特権まで有効にされてしまう.これは最小特権の原則に反する.
ここで,以下のように ChangeServiceConfig2 API を利用することで,サービスに付与される特権を制限できる.
SERVICE_REQUIRED_PRIVILEGES_INFO privileges_info = {};
privileges_info.pmszRequiredPrivileges = SE_INC_BASE_PRIORITY_NAME _T("\0");
ChangeServiceConfig2(service_handle, SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO,
&privileges_info);
また,このサービスはファイルやレジストリに書き込む必要がない.そこで,以下のように設定することで,意図せずファイルやレジストリに書き込もうとしたときに失敗するように構成できる.
SERVICE_SID_INFO sid_info = {};
sid_info.dwServiceSidType = SERVICE_SID_TYPE_RESTRICTED;
ChangeServiceConfig2(service_handle, SERVICE_CONFIG_SERVICE_SID_INFO,
&sid_info);
このように設定したサービスを起動し,Process Explorer でセキュリティ情報を見てみる.

Restricted SID が設定されていること,"NT AUTHORITY\WRITE RESTRICTED" SID が設定されていること,特権が大幅に削除されていることなどがわかる.
実際,OS 標準のサービスは概ね最小特権で実行されている一方で,(残念ながら Microsoft Office を含む) OS 非標準のサービスは,デフォルトの特権を持ったまま動作しているものがほとんどだ.例えば,Office 2010 に付属する ImeDictUpdateService (Microsoft IME Dictionary Update) のセキュリティ属性は以下のようになる.

デフォルトで付与される全ての特権を持ったまま実行されていることが分かる.これが本当に IME の辞書アップデートサービスであるなら,SeDebugPrivilege (スーパー特権のひとつ.プロセスのセキュリティ設定を無視して,(保護されたプロセスをのぞく) すべてのプロセスを開くことができる.) や,SeTimeZonePrivilege (文字通りタイムゾーンを設定するための特権) をはじめとした,多くの特権を削除しても恐らく動作は可能だろう.
参考

インサイドWindows 第6版 上 (マイクロソフト公式解説書) であれば,P.352 「4.2.2 サービスアカウント/最小特権」あたりに詳しい.その他の参考資料については以下など.
IWordBreaker とファイル検索
「『プリキュア』で検索したら『ハートキャッチプリキュア』にマッチしない」という Windows Search の話.
Windows7に深刻なバグを発見したので、警鐘を鳴らすために晒してみます。
再現に使用したOSはWindows7 Home Premium x64です。バグの再現手順
!!! 悪用厳禁 !!!
●1.適当にフォルダを作る 名前は何でもOK
●2.作ったフォルダーを開いて、
「ハートキャッチプリキュア」
「ふたりはプリキュア」
「プリキュア」
の3つのフォルダを新規作成する
●3.検索窓に「プリキュア」と入力してみる
●4.「ハートキャッチプリキュア」が無かったことにされる
ちくしょう!誰がこんなことを!メディーック!!メディーーーーック!!
対処方法
検索窓に「*プリキュア」と入れると全部ヒットするみたい。
でも、XPの頃は「プリキュア」で全部ヒットしてたのでなんか腑に落ちないアレが。
ちなみに検索インデックスの有無は関係ないみたいです。
#2010/10/30 11:05 追記
VistaやMacOSでも再現するとか。
Windowsの人は、「Everything」を使うと幸せになれるらしいです。
「従来何も考えずにファイル名の部分文字列で検索できていたのものを,どうしてアスタリスクが必要にしちゃったの?」という方向の話のような気もしますが,その辺は置いておいて久しぶりに IWordBreaker とか.
Windows 7 に標準で付いてくる日本語向け IWordBreaker 実装に「ハートキャッチプリキュア」等を食わせてみます.
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using Microsoft.Win32; using WordBreaker; namespace WordBreakerTest { using HRESULT = System.UInt32; public struct HResults { public const HRESULT S_OK = 0x00000000; public const HRESULT S_FALSE = 0x00000001; public const HRESULT E_FAIL = 0x80004005; public const HRESULT WBREAK_E_END_OF_TEXT = 0x80041780; public const HRESULT LANGUAGE_S_LARGE_WORD = 0x00041781; public const HRESULT WBREAK_E_QUERY_ONLY = 0x80041782; public const HRESULT WBREAK_E_BUFFER_TOO_SMALL = 0x80041783; public const HRESULT LANGUAGE_E_DATABASE_NOT_FOUND = 0x80041784; public const HRESULT WBREAK_E_INIT_FAILED = 0x80041785; } public enum WORDREP_BREAK_TYPE { WORDREP_BREAK_EOW = 0, WORDREP_BREAK_EOS = 1, WORDREP_BREAK_EOP = 2, WORDREP_BREAK_EOC = 3 } [SuppressUnmanagedCodeSecurity] [ComImport, Guid("CC907054-C058-101A-B554-08002B33B0E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IWordSink { [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT PutWord( uint cwc, [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U2)] char[] pwcInBuf, uint cwcSrcLen, uint cwcSrcPos); [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT PutAltWord( uint cwc, [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U2)] char[] pwcInBuf, uint cwcSrcLen, uint cwcSrcPos); [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT StartAltPhrase(); [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT EndAltPhrase(); [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT PutBreak(WORDREP_BREAK_TYPE breakType); } [SuppressUnmanagedCodeSecurity] [ComImport, Guid("CC906FF0-C058-101A-B554-08002B33B0E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IPhraseSink { [Obsolete("Not supported.")] [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT PutSmallPhrase( [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.U2)] char[] pwcNoun, uint cwcNoun, [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3, ArraySubType = UnmanagedType.U2)] char[] pwcModifier, uint cwcModifier, uint ulAttachmentType); [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT PutPhrase( [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.U2)] char[] pwcPhrase, uint cwcPhrase); } public class WordSink : IWordSink { public Action<string, uint, uint> OnWord { get; set; } public Action<string, uint, uint> OnAltWord { get; set; } public Action<WORDREP_BREAK_TYPE> OnBreak { get; set; } #region CWordSink Members public HRESULT PutWord( uint cwc, [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U2)] char[] pwcInBuf, uint cwcSrcLen, uint cwcSrcPos) { if (OnWord != null) { OnWord(new string(pwcInBuf), cwcSrcLen, cwcSrcPos); } return HResults.S_OK; } public HRESULT PutAltWord( uint cwc, [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U2)] char[] pwcInBuf, uint cwcSrcLen, uint cwcSrcPos) { if (OnAltWord != null) { OnAltWord(new string(pwcInBuf), cwcSrcLen, cwcSrcPos); } return HResults.S_OK; } public HRESULT StartAltPhrase() { return HResults.S_OK; } public HRESULT EndAltPhrase() { return HResults.S_OK; } public HRESULT PutBreak(WORDREP_BREAK_TYPE breakType) { if (OnBreak != null) { OnBreak(breakType); } return HResults.S_OK; } #endregion } public class CPhraseSink : IPhraseSink { #region CPhraseSink Members public HRESULT PutSmallPhrase( [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.U2)] char[] pwcNoun, uint cwcNoun, [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3, ArraySubType = UnmanagedType.U2)] char[] pwcModifier, uint cwcModifier, uint ulAttachmentType) { return HResults.S_OK; } public HRESULT PutPhrase( [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.U2)] char[] pwcPhrase, uint cwcPhrase) { return HResults.S_OK; } #endregion } [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate uint FillTextBufferDelegate(ref TEXT_SOURCE pTextSource); [StructLayout(LayoutKind.Sequential)] public struct TEXT_SOURCE { [MarshalAs(UnmanagedType.FunctionPtr)] public FillTextBufferDelegate pfnFillTextBuffer; [MarshalAs(UnmanagedType.LPWStr)] public string awcBuffer; public uint iEnd; public uint iCur; } [SuppressUnmanagedCodeSecurity] [ComImport, Guid("D53552C8-77E3-101A-B552-08002B33B0E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IWordBreaker { [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT Init( [MarshalAs(UnmanagedType.Bool)] bool fQuery, uint maxTokenSize, [MarshalAs(UnmanagedType.Bool)] out bool pfLicense); [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT BreakText( ref TEXT_SOURCE pTextSource, [MarshalAs(UnmanagedType.Interface)] IWordSink pWordSink, [MarshalAs(UnmanagedType.Interface)] IPhraseSink pPhraseSink); [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] HRESULT GetLicenseToUse([MarshalAs(UnmanagedType.LPWStr)] out string ppwcsLicense); } public static class Program { public static void BreakText(string text, bool forQuery) { const string kWordBreakerKey = @"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ContentIndex\Language\Japanese_Default"; var guid = new Guid(Registry.GetValue(kWordBreakerKey, @"WBreakerClass", string.Empty) as string); var wordBreakerType = Type.GetTypeFromCLSID(guid); // A newer wordbreaker shipped with MS Office 2010. // wordBreakerType = Type.GetTypeFromProgID("NLG.Japanese Wordbreaker.4.1"); var wordBreaker = default(IWordBreaker); try { wordBreaker = Activator.CreateInstance(wordBreakerType) as IWordBreaker; var license = true; wordBreaker.Init(forQuery, 4096, out license); var filler = (FillTextBufferDelegate)((ref TEXT_SOURCE _) => HResults.WBREAK_E_END_OF_TEXT); var pTextSource = new TEXT_SOURCE() { pfnFillTextBuffer = filler, awcBuffer = text, iCur = 0, iEnd = checked((uint)text.Length), }; var dictionary = new Dictionary<WORDREP_BREAK_TYPE, string> { {WORDREP_BREAK_TYPE.WORDREP_BREAK_EOC, "[EOC]"}, {WORDREP_BREAK_TYPE.WORDREP_BREAK_EOP, "[EOP]"}, {WORDREP_BREAK_TYPE.WORDREP_BREAK_EOS, "[EOS]"}, {WORDREP_BREAK_TYPE.WORDREP_BREAK_EOW, "[EOW]"}, }; var words = new List<string>(); var altWords = new List<string>(); wordBreaker.BreakText(ref pTextSource, new WordSink { OnWord = (word, _, __) => words.Add(word), OnAltWord = (word, _, __) => altWords.Add(word), OnBreak = type => { words.Add(dictionary[type]); altWords.Add(dictionary[type]); }, }, new CPhraseSink()); GC.KeepAlive(filler); Console.WriteLine("Words: " + string.Join("/", words)); Console.WriteLine("Alt Words: " + string.Join("/", altWords)); } catch { if (wordBreaker != null) { Marshal.ReleaseComObject(wordBreaker); wordBreaker = null; } } } [MTAThread] static void Main(string[] args) { BreakText("プリキュア", false); BreakText("ふたりはプリキュア", false); BreakText("ハートキャッチプリキュア", false); BreakText("マイコンピューター", false); BreakText("情シス", false); } } }
Words: プリキュア Alt Words: Words: ふたり/は/プリキュア Alt Words: Words: ハトキアッチプリキュア Alt Words: ハートキャッチプリキュア Words: マイコンピュタ Alt Words: マイコンピューター Words: 情/シス Alt Words:
さすがに "プリキュア" で分割してくれたりはしないようですね.というかそもそも,「欧文地名以外の複合語をカタカナ表記するときは分かち書き」という Microsoft のスタイルガイド が遵守されているのが前提なのか,カタカナの連続は何も考えずにくっつけているだけのような挙動にも見えました.あんまりちゃんと実験してませんが.
ちなみに,SharePoint に付属する WordBreaker では,以下のようにユーザ辞書ファイルを使うことが出来るようです.
4. 以下に従い、ファイルを保存します。
場所 "C:\Program Files\Microsoft Office Servers\12.0\Bin"
(日本語ワードブレーカ nlsdata0011.dllが存在する場所)ファイル名 "Custom0011.lex" (0011 は言語 ID) 文字コード "Unicode"
さらにこの nlsdata0011.dll というファイルですが,手元の Windows 7 Ja 環境では同名のファイルがシステムディレクトリに存在します.試しに %SystemRoot%\System32\Custom0011.lex (と %SystemRoot%\SysWOW64\Custom0011.lex) というファイルを作り,以下の内容を入力し,BOM 付き UTF-16 ファイルで保存してみます.
#CUSTOMER_WB 情シス プリキュア
改めて最初のコードを実行すると,結果は以下のようになりました.
Words: プリキュア Alt Words: Words: ふたり/は/プリキュア Alt Words: Words: ハトキアッチプリキュア Alt Words: ハートキャッチプリキュア Words: マイコンピュタ Alt Words: マイコンピューター Words: 情シス Alt Words:
少なくとも「情シス」の方は 1 word として認識されるようになりました.また,実行中に Custom0011.lex が読み込まれていることも,Process Monitor のログから確かめられました.
一方,ユーザ辞書に「プリキュア」を追加しても,"ハートキャッチ/プリキュア" と分割されませんでした.これは,以下の SharePoint での事例と同じもののようです.
発端の話も,「ワードを分割して認識させられないか」の一種だと思いますが,どうも現世代の Microsoft 製 IWordBreaker 実装ではユーザ辞書を使ってもこの問題を回避できなさそうな感じです. 次なる手段としては,自分で IWordBreaker を実装 して,HKLM\SYSTEM\CurrentControlSet\Control\ContentIndex\Language\Japanese_Default 以下の WBreakerClass を置き換えてしまう,あたりでしょうか.試してはいないので,うまくいくかは分かりませんが.ワードブレーキング (設定箇所 : サーバー定義ファイル)
こちらは、セミナーの資料では省略していましたが、懇親会でご質問がありましたので記載しておきます (懇親会場でご回答させて頂きました)。
例えば、「ペドロ&カプリシャス」のようなキーワードを検索したい場合、インデックス収集時に、間の記号(アンパサント &)によって、「ペドロ」と「カプリシャス」でキーワードが自動的に区切られます。こうした場合には、カスタムディクショナリー(Custom Dictionary) を設定することで、こうした自動ブレークを阻止し、「ペドロ&カプリシャス」で完全マッチの検索をおこなうことができます。
カスタムディクショナリの設定ファイルを配置する場所は、シソーラスファイルとは異なり、%programfiles%\Microsoft Office Servers\12\bin\CustomLANG.lex です。(日本語の場合は、Custom0011.lex です。) 設定を反映させるには、インデックスの再収集以外に、クエリー時のブレーク箇所も正しく認識させる必要があるため、ファイル編集後は、 Office SharePoint Server Search サービス (osearch) の再起動と、再クロールの双方をおこなってください。
カスタムディクショナリの作成方法については、以下の記事が参考になります。
TechNet : ユーザー辞書を作成する (Office SharePoint Server 2007)
http://technet.microsoft.com/ja-jp/library/cc263242.aspx実は、懇親会では、「ワードブレークを阻止したい」 というご質問ではなく、逆に 「ワードを分割して認識させられないか」 というものでした。私は、この回答として、「カスタム辞書 (上記の CustomLANG.lex) を編集することで認識させられる可能性があるかもしれない」 とお答えしてしまいましたが、すみません、動作を確認してみたところ、本来分割されていないワードを分割して認識させることは不可能でした。(この予測は誤っておりました。申し訳ありません . . .)
未来の標準は現在のブラウザ実装を制約するか
Internet Explorer 6 以前の DirectX Filter みたいなのと誤解しているのならアレですが,(まだドラフト段階の) 標準と (現在初期実装段階にある製品の) 実装技術が切り離せるかという切り口で見ると結構奥深い話かもしれません.今後たとえば WebGL が標準として意味を持つのであれば,OpenGL をブラウザの実装技術の中核にしておいた方が都合が良いという議論はありえると思います.
WebGL の場合,それが OpenGL コマンドをそのまま流用したような命令形態であることがポイントとなります.仮にブラウザのレンダリングエンジンを Direct3D / Direct2D ベースで行っていた場合,WebGL サポートのためには 1)部分的に OpenGL を使用する 2) Direct3D で WebGL の処理をエミュレートする,といった対応が必要となります.いずれの場合も,最初から OpenGL ベースでブラウザを実装するよりは複雑性が増すこととなるでしょう.
WebGLは、3Dグラフィックスを活用したWebアプリの実現を目指し制定された、OpenGL準拠のAPIセット。OpenGL関連であることからうかがえるように、各種グラフィックス技術の標準化を進める団体Khronosグループが仕様の策定を進めている。今回、Safariなど多数のWebブラウザに採用されているHTMLレンダリングエンジン「WebKit」が、WebGL実装を含む開発者向けテスト版の配布を開始したことを受け、どのように表示されるかなどエンドユーザからの目線でテストを実施した。
WebGL最大の特徴は、その3D描画をプラグインなしに実現できることだろう。これまでもWebブラウザ上に3Dグラフィックスを表示する試みは存在し、現在もGoogleが「O3D」の開発を進めるなど動きはあるが、Web標準ではない。プラグインの力を借りず、次世代のWeb標準たるHTML5でサポートされることが、WebGLが注目を集める大きな理由だ。
HTML5 の一部として WebGL がサポートされるかのように海上氏は書かれていますが,果たしてこれが本当かどうかはよく分かりません.少なくとも 『HTML 5 - W3C Working Draft 25 August 2009』にそれらしい記述はみつけられませんでした.とはいえ,今後 HTML5 の延長線上に WebGL が "標準" の地位を手に入れる可能性はあります.このシナリオは Microsoft にとってあまり好ましくないものかもしれません.そして,未来の標準「勧告」に合わせようとすると,現在の技術選定の幅が狭められかねないという構図はちとアレです.
もちろん,WebGL と Direct3D/2D が絶対に共存不可能ということはなく,実際現場レベルではこんな話もあります.以下は,WebKit (WebCore) が現在実装中の CSS 3D Transforms Module Level 3 (W3C Working Draft 20 March 2009) (これもまだ Working Draft) を,Windows でどう行うかに関するものです.
- Bug 27314 – Implement Accelerated Compositing on Windows (3D CSS Transforms)
- Bug 29813 – [WinCairo] 3D CSS Transforms are not implemented in WebKit for Windows
現在コミットされているパッチを見る限り,WebKit は,CSS 3D Transforms に Direct3D を使う方向で動いているようです.この流れに対し Brent Fulgham 氏は将来的に WebGL 実装が必要となったときの懸念を示しましたが,Apple の Chris Marrin 氏は「Both OpenGL and Direct3D can handle multiple contexts without any trouble.」と述べ,(WebGL のために必要ならば) OpenGL と Direct3D の同時に使用することも念頭に置いているようでした.とまあ外野としては「それが世界の選択か」とつぶやく前にとりあえずソースと関係者の議論を読むってのがいいのかもしれません.
ちなみに,WebKit での DirectWrite サポートも,既にトラック用のバグは登録されています.
同様に Mozilla でも DirectWrite に関する議論は開始されています.流れを見るに Mozilla の方が先に何か出てくるかもしれません.
追記2
Firefox の関係者この発言も興味深い.WebGL が,Direct3D のような他の Native 3D API でも実装できることを確認したいとのこと.
We still have some ways to go, as there are issues in shader security and portability, not to mention figuring out what to do on platforms where OpenGL is not available. (The latter is an interesting problem; we're trying to ensure that the API can be implementable on top of a non-GL native 3D API, such as Direct3D, so that might be one option.) But progress is being quickly made.
Windows における例外ハンドリングとか 64-bit プロセス固有のあれそれとか
すごくまとまってました.個人的に印象深かったのはこの辺.
実は、x64 版 Windows 用のバイナリにおいては、fs:[0] に例外ハンドリング構造体へのポインタを設定するなどという行為は一切行いません。というより、コード上には、例外をハンドルする如何なる追加コードも存在しません。これは、例外ハンドリングを行わない場合のプログラムには一切オーバーヘッドが存在しないということを意味します。
Windows の細かい部分まで見てみると,64-bit プロセスのみ影響を受ける仕様というのがいくつかあって,今回のケースもそのひとつですかね.64-bit アプリケーションの場合,「従来から存在するバイナリ」というのが存在しないので,互換性に悩まされることなく 32-bit 時代の仕様をリセットできる,と.
Windows の 64-bit 移行に伴うドライバの変更とかは有名でしょうから,アプリケーション制作者に関係しそうな部分で,64-bit アプリケーションにのみ適用される変更からいくつか挙げてみると,
- UAC に伴う自動昇格 (install.exe とか setup.exe の自動昇格) は,64-bit プロセスには適用されない*1
- UAC に伴うファイルとレジストリの仮想化は,(デフォルトでは?) 64-bit プロセスに適用されない.
- 64-bit プロセス向けの関数呼び出し規約が統一された*2.64-bit をターゲットとした場合,__cdecl, __stdcall, __fastcall, __thiscall, __stdcall はいずれも無視される.
- Windows 7 で導入された User Mode Scheduling (UMS) は,64-bit プロセスでのみ使用可能*3.
- (ハードウェア) DEP は64-bit プロセスで常にオンである./NXCOMPAT:NO リンカオプションを使用してビルドしても効果がない*4.
- Vista (以降?) の 64-bit プロセスでは HeapEnableTerminationOnCorruption がデフォルトでオンになる*5.
- いくつかの Legacy なライブラリは 64-bit 版が存在しない.例えば DirectMusic performance layer, DirectPlay 4 以前, DirectDraw 6 以前, Direct3D 8 以前, and DirectInput 7 以前の 64-bit 版は存在しない*6.
てな感じですかね.
WOW64 と異種バイナリの混在
ついでに思い出したのが,Windows でユーザ作成の 64-bit バイナリと 32-bit バイナリが単一プロセスで混在できないという WOW64 の問題を,メモリレイアウト由来としている Wikipedia のこの記述.さすがに問題を単純化しすぎてるんじゃないでしょーか.
問題点
Windowsの64ビットABIは、そのままWin32の32ビットABIを64ビットに拡張した物である。従って、64ビットABIのアプリケーションは8TBのアドレス空間を独占的に使える様になっている。ここに一つの問題点がある。32ビット ABIのコードを格納可能な仮想空間下位4GBが64ビットABIに独占されてしまった事である。この為、32ビットABIを格納する場所が無く、32ビットアプリケーションはもとより、DLLやOCXをロードして呼び出すことも不可能となってしまった。そのため、マイクロソフトは、32ビットABIのコードと64ビットABIのコードとの相互な呼び出しを禁止している(COMインターフェースを経由して相互乗り入れは可能であるが、x64アーキテクチャで本来可能であったシームレスな32ビットコードと64ビットコードの相互呼び出し機能は全く生かされていない)。この顕著な例として、Internet Explorerの振舞があげられる。32ビットのActiveXコンポーネントを検出すると、64ビット版Internet Explorerは処理を中断して32ビット版Internet Explorerに処理を引き継ぐ。32ビットアプリケーションと64ビットアプリケーションの間には、実行ファイル以外のコンポーネント群を互いに利用することができない深い溝がある。
「Windowsの64ビットABIは、そのままWin32の32ビットABIを64ビットに拡張した物である。」て時点でたぶん外していて,例外処理に関する ABI が違うじゃんと.
リーフ関数 [関数を呼び出したり、任意のスタック空間に自身を割り当てたりしない関数] 以外のすべての関数には、不揮発性レジスタを回復するために、それらを正しくアンワインドする方法をオペレーティング システムに対して記述したデータで注釈を付ける必要があります。このデータは xdata または ehdata と呼ばれ、pdata から指されます。Prolog と epilog は、xdata で正しく記述されるように大幅に制限されます。スタック ポインタは、リーフ関数を除いて、epilog または prolog の一部でない任意のコード領域で 16 バイトに配置される必要があります。prolog および epilog 関数の正しい構造の詳細については、「プロローグとエピローグ」を参照してください。例外処理および例外処理/アンワインディング pdata および xdata の詳細については、「例外処理 (x64)」を参照してください。
仮にメモリレイアウトを工夫して 64-bit コードから 32-bit コードを (またはその逆を) 呼び出せたとして,そこで例外起きたらどうすりゃいいんでしょうね? さらにその応用編として,64-bit コードから 32-bit コードを呼び出し,さらに 64-bit コードにコールバックされた状態で例外が発生した場合も.
もちろん上に挙げたような 32-bit プロセスと 64-bit プロセスの違いもあります.UMS を使う DLL が 32-bit プロセスにロードされた場合,果たして UMS をサポートすべきか否かとか.
他にも API をフックするコードに,プロセス環境ブロック (PEB) やスレッド環境ブロック (TEB) を見に行くコード,自分でコールスタックを遡っちゃうコード,プロセス内でひとつしか存在しないと仮定して書かれた DLL の 32-bit 版と 64-bit 版が同時にロードされてしまうケース,環境変数は 64-bit モジュールと 32-bit モジュールで分かれていた方が良いのか否か,等々.
そんなあれそれで,本気で異種バイナリの混在させたかったら,メモリレイアウト云々は所詮と氷山の一角と思うわけです.
*1:[http://msdn.microsoft.com/ja-jp/magazine/cc163486.aspx:title=アプリケーションで Windows Vista のユーザー アカウント制御を有効に活用する - MSDN Magazine 2007 年 1 月号]
*2:[http://msdn.microsoft.com/ja-jp/library/ms235286.aspx:title=x64 呼び出し規約の概要 - MSDN ライブラリ]
*3:[http://msdn.microsoft.com/en-us/library/dd627187.aspx:title=User-Mode Scheduling - MSDN ライブラリ]
*4:[http://d.hatena.ne.jp/egggarden/20091018/1255844431:title=実行可能属性のページとDEP - やや温め納豆]
*5:[http://blogs.msdn.com/michael_howard/archive/2008/02/18/faq-about-heapsetinformation-in-windows-vista-and-heap-based-buffer-overruns.aspx:title=FAQ about HeapSetInformation in Windows Vista and Heap Based Buffer Overruns - Michael Howard's Web Log]
*6:[http://msdn.microsoft.com/en-us/library/ee418798.aspx:title=64-bit programming for Game Developers - MSDN ライブラリ]
電卓の話
また、電卓など標準アプリケーションに入っているのも昔のまま。AppleのCMでクールなアプリケーション(笑)として挙げられているのをご存知の方も多いと思う。過去との互換性もあるので(別のアプリケーションから起動している可能性がある)変更が難しいのかも知れないが、どうせ変えるならとことんWindows Vista風にして欲しかった。
Windows史上初? 大きく変わった「電卓」
誰もが一度は使ったことがあるWindows付属アプリケーションの筆頭と言えば、やはり電卓だろう。ごく単純なメモリー機能付きの四則計算に使うのが一般的だろうが、2/8/10/16進数を扱う関数電卓にもなる。Windowsの電卓は「あって当たり前」の空気のような存在で、Windowsが手を変え品を変えて進化してきても、電卓だけはほとんど、機能も見た目も変わることなく、そのまま続いてきた。その電卓がなぜか、Windows 7では大幅にパワーアップしている。
Windows 95以来、スタートメニューのアクセサリには、電卓が含まれていた。その機能はVistaまでほとんど変わることなく続いてきた
『When you change the insides, nobody notices - The Old New Thing』より.
14.20 中身を変えても誰も気付かない
電卓とメモ帳が変化していないことに人々が不満を漏らしたときはわけがわからなかった。実際には、どちらのプログラムも長年にわたって変化してきた(メモ帳にはメニューやステータスバーオプションが追加されたし、電卓には大掛かりな改修作業が行われた)。
これらの人々が、「なぜ Microsoft は Windows をかっこよく見せることに全力を注いでいるのか。技術の改善に全力を注ぐべきであり、外観をよくするための改良はやめるべきだ」と訴える人々と同じであったとしても、驚くにはあたらない。
そして電卓には、まさにそれが起きていた。大がかりな技術的改善である。外観は改善されていない。そして、そのことに誰も気付かなかった。それどころか、不満は続出した。「電卓を見てごらん、これまでとずっと同じじゃないか。」
電卓の内部(算術エンジン)は完全に最初から書き換えられていた。IEEE 準拠の浮動小数点ライブラリが任意の精度の算術ライブラリに置き換えられた。これは、電卓が小数の計算を正しく行えないことを小ばかにする記事が書き立てられたからだ(たとえば、「10.21 - 10.2」は 0.0100000000000016 になった)。
(中略)
好きか嫌いかはともかく、UI を変更しなければ誰も気付かない。新しい UI にあれほど労力を注いでいるのはこのためだ。
Windows 95 付属の電卓にすでに拡張精度機能が実装されていたとすれば,「Windows 95 以来ほとんど変わることなく」ってのはまあそこまで外れてはいないかもしれませんが,さすがに Windows 3.x 以前から何 1 つ仕様は変わっていないって言われるのはかわいそう.
DPI が足りない
ん?!

んんん!!
あわせて読みたい
私たちがWindows7のプラニングを始めた頃、ディスプレイについてのユーザー データを調べている際に、非常に興味深い (そして驚くべき) ことを発見しまた。ほぼ半数のユーザーがフル ネイティブ画面解像度を使用するためのPC設定を行っていなかったのです。下の表は、以前の記事でChristinaが取り上げたWindows Feedback Program から得られたデータを示したものです。
なぜユーザーが低解像度に設定しているのかを明確に把握する方法はありません。しかし、私たちが目にした多くのコメントは、「高解像度のディスプレイでは、デフォルトのテキストが読みづらいので、低解像度にしているのではないか」、という私たちの仮説と一致していました。もしかしたら、たまたまこのような設定になってしまった、というユーザーもいるかもしれません。例えば、ディスプレイ ドライバが合っていない、あるいは何らかの理由でアプリケーションが解像度を変更し元に戻らなかった、というケースが考えられます。どんな理由で画面解像度が低くなったにせよ、結果的にぼやけたテキストが表示されてしまいます。PCの画面で長時間このようなテキストを読むと本当に目が疲れます。
High DPI 解像度に対するフォローアップ - マイクロソフトのEngineering Windows 7 ブログ
いくつかの白熱した議論とともに、High DPIに対する多くのコメントが寄せられています。それらの多くは、我々が収集してきたデータと一致する、よい例になっています。私たちがデータを持たない分野に対して、これらのコメントは私たちの持つ多くの仮説を裏付けとなっています。もう一つ明らかになったのは、この機能の一部について誤解があり、またこの機能の理想、可能な事、機能そのものに対してある種の「神話」のような部分も見受けられます。この記事では主にそれら問題の要約と、誤解と思われる部分についての詳細を提供したいと思います。
以下はコメントにて裏付けられた私たちの「仮説」です。
High DPIについてのさらなるフォローアップ - マイクロソフトのEngineering Windows 7 ブログ
- ほとんどの人は画面解像度を、文字を大きくするため、またはただの偶然でのみ調整している
- 一部のコアユーザだけがhigh DPIを知っていて、使用している
- 一部の人は画面の広さを好み、他の人は大きなUIを好む
- 一部の人はDPI設定の見つけやすさに対して懸念を持っている
- アプリケーションの互換性は大きな問題で、一部の人はこれによってこの機能を利用しない
- IEでのスケーリングはもっとも大きな問題である(IE8をご覧になって多くの修正を確認してください)
- 多くの複雑かつ微妙な理由で、この機能を多数の人々に説明するのは困難である
Windows XP/Vista のスレッドスケジューラと Hyper-Threading
4gamer の『Intelのベンチマーク担当に,LynnfieldやArrandaleなどの話をいろいろ聞いてきた』という記事より.
ここで少し,現在,すなわちWindows Vistaまでのスケジューラにどういう問題があるのかを,ざっと説明しておこう。
Windows Vistaに限ったことではなく,Windows XP以前もそうなのだが,現行世代のWindowsは,複数のCPUコアに対して,負荷バランスだけを根拠としてスレッドを割り当てていく。スケジューラは,割り当てようとしている先のCPUコアが物理コアなのか論理コアなのかを考慮しないわけだ。そのため,「ある物理コアにスレッドが割り当てられている状況で,別の物理コアは完全に空いているにもかかわらず,『すでにスレッドが割り当てられている物理コアの論理コア』へ,新たにスレッドが割り当てられる」という悲劇が起こり得る。あるいは,一つのCPUコア上にある物理コアと論理コアに,データをまったく共有していない二つのスレッドなど,競合するスレッドを割り当てるということもあり,いずれも,Hyper-Threadingのパフォーマンスは,極端に低下してしまう。
んー,この説明だと,XP/Vista のスケジューラは論理 CPU の物理的な位置を全く区別しない残念な子に読めてしまいますが,なんか全く別の記述を『インサイド Microsoft Windows』で読んだ記憶が……
スレッド用のプロセッサを選択するとき、すべての論理プロセッサがアイドル状態にある物理プロセッサが存在する場合には、その物理プロセッサの論理スレッドが選択される。スレッドを実行させている別の論理プロセッサを所有している物理プロセッサ上のアイドルプロセッサが選択されるわけではない。
まあそれはそれとして,Windows Internals 第5版が (本当に本当なら) あと一週間で発売.いやー長かった.

Windows® Internals, Fifth Edition (PRO-Developer)
- 作者: Mark E. Russinovich,David A. Solomon,Alex Ionescu
- 出版社/メーカー: Microsoft Press
- 発売日: 2009/06/17
- メディア: ハードカバー
- 購入: 1人 クリック: 87回
- この商品を含むブログ (15件) を見る
追記
こんどこそ確実に発売っぽい.
RTM'd today: Windows Internals, Fifth Edition
追記2
元記事に追記が入った模様.
ご指摘感謝 > 名無しさん
追記3
- Windows Support for Hyper-Threading Technology (word file)
- XP/2003 Server 時代に書かれた資料."Hyper-Threading Aware Thread Scheduling" のあたりに,当時 Microsoft が重視していたことが書かれている.





