2009/03/11
Kademlia への攻撃手段を確立。
Winny | |
これで、トラッカーなしの BitTorrent や profes への攻撃が可能になるわけだ。*1思ったより簡単だったかも。(しかも、DHT ネットワークに限れば流通をほぼ完全にストップすることが可能になった。これは Winny や Share への妨害に比べると劇的。)
分散ハッシュテーブル (DHT) のような特定のトポロジをとるネットワークについては、トポロジの仮定をちょっと崩壊させてやることで、簡単に特定ハッシュをもつファイルの流通経路を崩壊させられることがわかった。
そういえば profes は WCF を使っているわけだけれど、もしかして TCP/IP を使って DHT ネットワークを組んでるの? (いや、実装の前提は UDP/IP だからそれが気になっただけ。)
2009/03/06
セキュリティ設計ができていないために丸裸になってしまう例
Diary | |
PS3 のハードディスクの中身を復号する方法があったのだとか。
方法を見る限り、AES か DES かは知らないが、ブロック暗号の ECB モード (ないし同様の脆弱性がある他のモード) を使っていたために、単にブロック位置を移動するだけで生データが抜き出せてしまった、という話のようだ。
では CBC モードを使えばいいかというと、そういう訳でもない。なぜなら、暗号化 HDD はランダムアクセスを要求されるからだ。というわけで、一部の暗号モードは当然使えない。
いや、使えないことはない。CBC モードをブロックごとに適用することは、当然可能だ。しかし、それは問題を発生させるブロックサイズを単に増やすことにしかならない。例えば、16 バイト毎にブロック入れ替えができたものを 512 バイト毎にする、という程度の意味で。
教訓 : ブロック暗号は最低でも CTR モードを使うようにしよう。(CTR もある種の攻撃には弱いので、できればもっとセキュアなモードを使いたい。)
2009/01/12
仮想化ソフト書くよ! (5)
Virtualization | |
前エントリ
- id:xna:20090108 (1)
- id:xna:20090109 (2)
- id:xna:20090110 (3)
- id:xna:20090111 (4)
番外編 : Shadow Walker の拡張
Shadow Walker で使うページ属性は、なにもアクセス不可だけじゃなくてもいい。通常の Shadow Walker は、次の組み合わせのページ属性を利用する。
例えば、単なるデータ参照を効率的にしたいなら、次の手もある。
- デフォルト : 読み書き (実行不可) ページ
- 実行アクセス (ITLB) : 実行/読み取りページ
実行不可の属性は、ご存知 NX ビットを使用する。こうすることにより、単なるデータ参照が行われるページの参照速度が向上する。パフォーマンス測定目的には、こんなのもある。
- デフォルト : 読み取りページ
- データアクセス (DTLB) : 読み書きページ
こうすると、書き込み対象のページに対して、対応するページテーブルエントリが DTLB に乗っている最長期間を計測することが可能となります。パフォーマンスアナライザに使えますね。
2009/01/11
仮想化ソフト書くよ! (4)
Virtualization | |
前エントリ
- id:xna:20090108 (1)
- id:xna:20090109 (2)
- id:xna:20090110 (3)
解決っ!
少々無理やりっぽいですが、命令ブレークポイントの問題を回避することに成功しました。
Shadow Walker を使う
ブレークポイントをセットするページの複写を取ります。その前後で当該「物理メモリ」への書き込みを禁止します。
ここにブレークポイントである int3 命令を複写し、Shadow Walker で実行時にだけ見えるようにします。例えば、次のように。
55 | push ebp 89E5 | mov ebp, esp 90 | nop ↓ 55 | push ebp CC | int3 CC | int3 90 | nop
ただし、このままでは不都合が出ます。というのも、例えば、push ebp 命令の前に、次のようなバイトが潜んでいたとしたらどうでしょう?
05 | ?? 55 | push ebp CC | int3 CC | int3 90 | nop
そして、この 05 というバイトが偶然にも実行されるとき、何が起こるでしょうか?
055589E590 | add eax, 0x90e58955 ↓ 0555CCCC90 | add eax, 0x90CCCC55
なんと、int3 で書き換えた部分によって書き換わってしまうのです。このような命令に対しても、パッチを行います。結果として…
CC | int3 CC | int3 CC | int3 CC | int3 CC | int3
全バイトが int3 命令になってしまいました。これでもまだ終わりません。メモリを最初のほうに向かって探索し、同様の事態が生じるアドレスを探索しきらなければなりません。
ただし、ページオフセットで 0x0f を下回った場合 (ないし、ブレークポイントのページオフセットが 0x0f を下回っている場合)、ページ境界を越えて探索しなければならないため、ここで打ち切り、代わりにページ全体を実行アクセス不能にします。これもまた、Shadow Walker や NX ビットで実現可能です。
このような解釈をされうるすべてのバイトにパッチを当てなければならないのですが、幸いにも x86 の命令は 15 バイト以下と決まっているため、探索する範囲はそんなに広くありません。前段落でページオフセットの 0x0f が基準だったのも、この命令長が関係するためです。
ただ、この改良により、QEMU の元で使用すると x86 の動作に齟齬が発生する可能性があります。というのも、QEMU は 15 バイト以下という命令長の制限を無視するからです。
スクリプト仕様も変更
前までは InstructionBreakpoint クラスを利用してフックを仕掛ける仕様にしていましたが、これを変更。InstructionBreakpointServiceProvider クラスから必要な要求レベル (例えば、iretd の直後でブレークしなくてもよい、など) を指定し、必要な InstructionBreakpointService オブジェクトを取得し、ここからブレークポイントを仕掛けるように変更。この変更でハードウェアブレークポイントも用途さえあるなら使用可能になりました。
2009/01/10
Windows 7 使ってみた。& 検証してみた。
Diary | |
パフォーマンス
ぶっちゃけわからん (Vista とそれほど顕著な変化はない)。ただパフォーマンスインデックスが 5.9/5.9 (ボトルネックなし)→5.5/7.9 (ボトルネック : メモリ) になってしまいました。
セキュリティ (UAC)
自動昇格が確かに有効になっていました。プログラムを書いて調べていますが、簡単に思いつきそうな脆弱性はないようでした。つまり、プログラムから勝手に設定変更などはされないだろう、ということです。
ただ気になったのが Flash Player をインストールした時で、[インストールする] ボタンを押すだけでインストールが完了してしまった点。(ActiveX の実行がブロックされたという表示すら出ない) 署名済みのかつ悪意のある (両者は両立する。) ActiveX をインストールする手間を減らしてしまう危険性があるかも。
具体的には、次のようなクリック数です。いずれも、Windows + Internet Explorer の組み合わせ。
Windows XP Service Pack 2 | 3 (ActiveX の実行を選択するために 2、インストールの確認に 1) |
Windows Vista | 4 (ActiveX の実行を選択するために 2、UAC に 1、インストールの確認に 1) |
Windows 7 | 1 (インストールの確認に 1) |
タスクバー
タスクバーの進歩が、意外と使いやすさを増してることにビックリ。
実際のウィンドウが多数になったとしても、アイコンはひとつだけ。つまり、タスクバーを有効活用できるし、目的のウィンドウを探す手間も最小限に抑えられる。
今までのタスクバーが普通のブラウザだとするなら、Windows 7 のタスクバーは (使い勝手が良い) ツリー表示つきのタブブラウザに相当します。
唯一使いにくい点を挙げるとすれば、ボタンを押す操作が、アプリを起動するのか、ウィンドウを最小化するのか、はたまた他の操作なのかわかり辛くなっている点。
と、ここまで書いた。
そのほかにも細かいところが使いやすくなっている様子。ただし再描画にバグがあるようで、タブやリストビューの項目がたまに「無効」になったときの表示になるみたいです。
仮想化ソフト書くよ! (3)
Virtualization | |
前エントリ
- id:xna:20090108 (1)
- id:xna:20090109 (2)
二転三転
またまた autotools のお世話になることにしました。
NASM…
NASM は、x86 専用のアセンブラですが、相当進んでます。というか、最近の命令 (Intel VT-x の命令 : vmread など) までキッチリサポートしてます。ただし、Cygwin のリポジトリに入ってるバージョン (2.01) は INVEPT や INVVPID 命令をサポートしていません (今の段階では関係ないし、2.03 でサポートされている) が。
マズった。
EFLAGS.RF のことをすっかり忘れてた。このフラグはハードウェア命令ブレークポイントをスキップする役目をもつが…
これのせいで、あらゆる状況でもブレークするようにはできないことがわかってきた。
具体的には、''IRET 命令で'' 戻った直後の命令が実行不可能になる可能性がある、と…。
あとデータブレークポイントはフォルト (実行しようとした命令がスタックに積まれる) じゃなくトラップ (これから実行しようとする命令がスタックに積まれる) なのね…。
2009/01/09
仮想化ソフト書くよ! (2)
Virtualization | |
前エントリ
Windows のスレッド管理と統合する
仮想化ソフトが無駄に CPU 時間を消費してしまうと、ホスト OS の動きが悪くなります。というわけで、Windows のスレッド管理と密に統合されるよう実装する必要がありました。
ほかにも、次の要件が必要です。
- PatchGuard で弾かれることがない
- 64 ビット版でも同様の実装が使用できる
- 複数の仮想マシンが動いても問題が出ない
- バイナリ変換サブシステムでも同様に使用できる
これをバイナリ変換サブシステムでは独特な方法で回避しましたが、今のところ頻繁なハードウェア割り込みで破綻するため、改善中です。
Intel VT-x では、ハードウェア割り込みを仮想化ソフトが代わりに受け取り、コンテキストを調整することで破綻を避けています。
バイナリ変換サブシステムでは
カーネルモードの仮想化スレッドの初期化時、そしてスケジューリングが回ってきた時に、次の命令列を実行します。*1
cli sidt [orig_idt] ; IDTR を退避 mov eax, cr3 ; CR3 を退避 mov [orig_cr3], eax lidt [task_idt] ; IDTR を置換 mov eax, [task_cr3] ; CR3 を置換 mov cr3, eax mov [orig_esp], esp ; ESP を退避 mov esp, [task_esp] ; ESP を置換 sti
ハードウェア割り込みが発生した場合、タスクの情報を Windows のものに戻し、オリジナルの IDT エントリを呼び出します。(この部分にバグが残っているため、コードは示しません。というのも、割り込みを禁止する前に 2 回以上ハードウェア割り込みが発生したり、NMI が発生すると、途端に破綻するためです。)
*1:実際にはもっと多くのレジスタを退避しています。
2009/01/08
仮想化ソフト書くよ! (1)
Virtualization | |
つーか書いてる途中です。
最初は簡単な make を使っていて、途中から autotools を使い、また make に戻ってきました。
というのも、autotools は移植性のあるプログラムには向いているけど、そうでないプログラムにはぜんぜん向いていないという事情が。例えば、x86_CC や ppc_CC (プラットフォーム依存 cc) のようなことができない。(いや、できなくはないが、make を使う以上に見た目が複雑になってしまう。)
ただ生の make だとやはり面倒があるので、マクロ言語でテンプレートを直に書き、それを configure スクリプト (自作) で置換を行うようにしてみました。
2009/01/05
フォレンジック話を始めることにした。
というわけでカテゴリ新設。ただししばらくはカテゴリ rootkit の下位でやるつもり。
フォレンジックに詳しくない私が気になるのは、彼らの一部がそれしか必要ないと単純な物理メモリダンプや論理メモリダンプのみを用いている点。せめてシステムレジスタの情報をもつ dmp やハイバネーションファイルを使うのが良いと思う。
というのは、単純な論理メモリや物理メモリの内容、特に物理メモリの内容は、ほとんどが木を隠すなら森の中の論理で、フォレンジックソフトウェアを「騙す」ために変更が可能である、という点。
例えば調査を行うユーザがカーネルメモリを元に解析を始める場合、次の問題がある。次のような単純な構造な物理メモリマップのカーネルがあったとする。
0x100000-0x1fffff | (カーネル) |
0x200000-0x5fffff | (ページテーブル) |
ここで、CR3 の内容は 0x200000 で、IDTR は 0x100000-0x1fffff 内のアドレスをポイントしているはずだ。
これらに対して次のように変更を加える。
0x100000-0x1fffff | (オリジナルカーネル) |
0x200000-0x5fffff | (オリジナルページテーブル) |
0x600000-0x601fff | (改ざんされたページテーブル) |
0x602000-0x602fff | (カーネルパッチとしてのマルウェア) |
この例では、ページテーブルを変更することで、マルウェアをカーネルの一部に埋め込んでいる。
ここで、CR3 の内容は 0x600000 で、IDTR はオリジナルのままだ。あるいは次のようになってもよい。
0x100000-0x1fffff | (カーネル) |
0x200000-0x5fffff | (ページテーブル) |
0x600000-0x600fff | (改ざんされた IDT) |
0x601000-0x601fff | (カーネルパッチとしてのマルウェア) |
こっちの例では、IDT を変更することで、マルウェアが確実に実行されることを保証できる。
ここでは IDTR の内容が 0x600000-0x600fff を指すように変更されるだろう。
このいずれも、システムレジスタがなければ最初に示したメモリイメージと区別することはできないのだ。なぜなら、オリジナルの物理メモリには何も手を加えていないから。
改ざんされたテーブルやマルウェアは、実のところ簡単に隠してしまうことが (理論上) 可能。
しかも、Shadow Walker のような複雑な方法を使わなくても、そこに「存在」するのに見つからない、という寸法だ。ここに「木を隠すなら森の中」の論法が使えるわけだ。
…とここまで書いてみた。間違いとかあったら訂正コメントをお願いします。
メモリフォレンジック?Firewireでやればいいじゃない!
から始まるメモリフォレンジック話。
信頼できる物理メモリイメージを取得したければ、Firewire や PCI のハードウェアを通じて DMA を発行してあげればよい。もっと信頼できる解析がしたければ PC の CR0, CR3 を含むレジスタの内容も同時に読み取っておくが吉。
Firewire を通じてコードのパッチを行う例には winlockpwn があって、同じ手法を用いれば何でもできる。たとえば、rootkit を強制的にインストールしてしまったり、VRAM を読み取って画面のハードコピーを取ってしまったり。というわけで、間違っても Firewire (または IEEE 1394、i.Link) ポートつきのノートパソコンを人の触れる場所に置かないこと :) わずか数秒であらゆる認証機構が排除されちゃうからね。
hideakii
Firewire標準搭載のPCってなかなか見かけないんですけど、どうなんでしょうね?、メモリのダンプ自体はddでもクラッシュダンプでもFireWireでもなんでもよいんではないかという気は個人的にはしますが、Firewire経由での取得も面白そうですね!
xna
最近のノート PC ならそこそこありますよ。(Firewire ポート
少なくとも \\.\PhysicalMemoryを使うよりは信頼できる情報が取れる (ソフトウェアによる妨害ができない) ので、私的にはオススメ。
Firewire ポートのついてる PC が幸いにも 2 台あるので、そのうち試してみようかと思います。
hideakii
最近会社で購入したMac BookではFirewireポートがなくなっているんですよね。個人的にちょっと興味があるのが、Firewire経由の場合、OS管理外のメモリ領域までダンプできちゃったりするのか?という点です。例えば4GB搭載のPCでXPが稼働している状況でFirewire経由では3.3GBの取得になるのか4GBいけるのか。試されたらぜひレポート希望です。
xna
あら〜確かになくなっちゃってますね。(MacBook の Firewire ポート
となると次は DMA 転送に対応した (理論上 Firewire と同等の攻撃が可能そうな) USB 3.0 になるのかな。
> OS 管理外のメモリ…
これは既存のツールを改造するだけでできますよ。ちょっと win32dd で試してみます。
xna
32 ビット最大である 4GB 固定のダンプを吐くよう改良してみたら、BSOD になってしまいました。
どうやらリード操作がグラフィックボードへのコマンドと解釈され、本来のドライバがコケてしまった様子。
OS 管理外のメモリを叩くのは慎重にしたほうがよさそうです。
(Windows は管理外のメモリにデバイスをマッピングすることがある)
ただ管理外メモリを読み取ることそのものは不可能ではない様子で、
win32dd に追加のオプション (実際の物理メモリ量を教えてやる) を加えることもできそうです。
hideakii
商用のダンプツールでは、グラフィックカードの利用しているメモリ空間はスキップさせるなどの処理が可能なものもあるようですが、そのあたりを考えてないツールだとBSODか止まるみたいですね。HWデバイスが使っているメモリ空間だけ避けて?管理外が取れれば安全なのでしょうかね(実はよくわかってませんが;)。OS管理外のメモリがRAMディスクなどで使われていなければそもそも取得する必要がないのかもしれませんけど。
hideakii
12日って成人式なご予定なんですかね?
xna
一応そういう歳です…
hideakii
「勉強会行こうかと」がてっきりダンプなやつかな?とか思っていたんですが(笑)
xna
読み直してみたら確かにそんな文面に取れるw
私は成人式に行きます。(東京は遠いのです…。
hideakii
成人式は一生に一度ですから(笑)、今年もあるのかわからないですがセキュリティ&プログラミングキャンプとかに参加されると東京にこれますよ、あとは講師とかでぜひ;−)
cci
こんにちは。興味深く読ませてもらいました。
クラッシュダンプファイルもデバイスにreserveされた領域のデータは取れないようなので、揮発性のプライマリなデータとしては悩ましいところですね。
http://www.msuiche.net/2008/10/16/microsoft-crash-dump-analysis-weaknesses/
Forensicsの世界でPhysicalMemoryの取得がメジャーなのは単純に対応しているツールの問題な気がします。クラッシュダンプファイルやハイバネーションファイルへの対応はon goingな印象です。私見ですが。
2009/01/04
仮想化ソフト書いてる。
Virtualization | |
そのうちオープンソースで公開できたらいいな〜と思って。ちょっとだけ現時点の進捗まとめ。
無限ブレークポイント
I/O ブレークポイント、メモリブレークポイント、実行ブレークポイントを無制限に設定することが可能。もちろんインビジブル。ハードウェアブレークポイント (それがゲストによって使用されていない場合) とページ属性の変更を巧みに利用。
Detours Hook
Detours Hook を行うライブラリを標準で装備。(開発完了)
スクリプトによる拡張
今後 JIT コンパイラと統合したい (たとえばコードキャッシュと統合するなどしたい) けど、現時点では分離した JIT コンパイラを持っている。高級言語を使いたいが現時点ではアセンブラに近い (検証できない) 低レベル言語。無限ブレークポイントを含めてすべての機能を利用可能にし、セキュリティ拡張を独自に書けるようにする。
もし開発が終わったら
彼女と結婚するんだ…
じゃなくて、(そっちは死亡フラグ) VMware に先んじて世界で初めてのセキュアな仮想化"プラットフォーム"になるね。Intel VT-x 版の開発はそれなりに早く終わるだろうし。
2008/11/25
LGPL談話 (誰も守らない LGPL 編その1)
GPL | |
LGPL は、自由にライブラリとしての使用が可能なライセンスと思われるかもしれませんが、実は違います。そして、それを守っている人もあまり多くありません。
あなた、LGPL 守ってる?チェックシート
- LGPL でカバーされたライブラリがあり、またそれらは LGPL で保護されているということを目立つように表示していない
- ライブラリのソースコード (最小限の対応するソース) を頒布していない*1
- 次の両方に当てはまる
- 共有ライブラリメカニズム (場合によっては DLL も含まれるだろう) を使用していない
- 再リンクを可能なようにせず、またライセンス条項で許可していない
- LGPL 3.0 でライセンスされたライブラリがあり、かつ次のうち 1 つでも当てはまる
- LGPL 3.0 と GPL 3.0 のライセンス文書 (全文) を添付していない
- プログラムのうち、LGPL でカバーされた部分の改変とリバースエンジニアリングを許可していない
- インストール情報を添付していない (コンシューマ製品に限る)
- LGPL 2.1 以前でライセンスされたライブラリがあり、かつ次のうち 1 つでも当てはまる
- LGPL のライセンス文書 (全文) を添付していない
- プログラム本体の改変とリバースエンジニアリングを許可していない
このうち 1 つでも当てはまれば、あなたも LGPL 違反者の仲間入りです :)
特に見逃されがちなのが、LGPL 2.1 以前の*2リバースエンジニアリング許可義務。プログラム本体を含めて改変、リバースエンジニアリングを許可しなければならないというのは、結構プロプライエタリなソフトウェアを作る業者にとっては痛手かもしれません。ただし、「顧客自身の利用」という条件がつく以上、あまり広い範囲に波及しなくて済むのは現実的ですが。
2008/11/24
Core i7 920 テスト中
Diary | |
いろいろな仮想化テストソフトが動くからすごい。
vmxcpu.rar を PAE 対応にしてみてもちゃんと動作。おー。
ついでに初めてのオーバークロックも実施。133MHz * 20 から 147MHz * 20 で、Core i7 940 以上のクロックと性能 (2.94GHz) に。ただサウンドに微妙にノイズが乗ってるから、やっぱマズかったのかも。
2008/11/12
仮想化関連メモ / EPT 概要
Virtualization | |
Intel VT-x 拡張である EPT を調べてみました。
ページングの拡張
EPT が提供する追加のページテーブルは、ゲストの物理アドレスをホストの物理アドレスに変換する機能を提供します。また、これらに対してアクセス許可を与えることが可能です。
Bit 2,1,0 | (アクセス許可の種類) |
b000 | アクセス不可 |
b001 | 読み取り専用 |
b010 | 無効 (EPT Misconfiguration) |
b011 | 読み書き (実行不可) |
b100 | 実行のみ (サポートしている場合のみ。さもなくば EPT Misconfiguration) |
b101 | 読み/実行 |
b110 | 無効 (EPT Misconfiguration) |
b111 | 読み/書き/実行 |
これらのアクセス許可に逆らった操作を行うと、EPT Violation が発生します。これはページフォルトとは発生が異なるため、わざわざコストの高い検索処理を行うことなく、ゲストで発生したページフォルトであることを検出できます。
アイデア
バイナリ変換型の仮想化ソフトウェアも EPT や RVI で高速化できるかも。
2008/11/10
Core i7 マシン買う。
Diary, Virtualization | |
というわけで家でも Intel VT-x ベース (あとは Core i7 からの EPT) の仮想マシンを動かす環境ができるわけです。
2008/11/03
難読化
Obfuscation | |
難読化面白そうなんだけどな〜。
例えばゲームを保護するとした場合に、どのデータを保護するかというのを列挙するだけでも、PDB から抽出したデータでは不十分になる場合があり得るし…。
究極のところ、構文解析木レベルで変更を加えるようにしなきゃならないのかもね。
2008/10/22
2ch の XNA スレで税務についての話が出たので。
XNA | |
XNA スレの 585 に一応意訳を書いてみたんだけど若干間違ってる雰囲気があるので、こっちで全文を若干ブラッシュアップして書いてみる。
原文 : http://creators.xna.com/en-us/tax_law_and_community_games
Community Games と税の取り扱いについて (翻訳版)
2008 年のホリデーシーズンに、Creators Club Online メンバーはゲームをアップロードし、レビューを受け、Xbox Live にて購入可能にすることができるようになります。すべてのロイヤリティはアメリカドル (U.S. Dollars) で計算され、クリエータの最小支払い金額に達すると、クリエータの国の通貨で支払いを受け取ることができます。Xbox Live で購入されたゲームの支払いを受け取るには、まずあなたがどこに居住しているか、そして、それに関連する情報を調べる必要があります。
アメリカ国内居住のクリエータ
アメリカ国内居住のクリエータは、米国国税庁 (IRS) の W-9 Form の分類において "アメリカ人" として扱われます。
IRS W-9 Form における 2008 年 9 月時点での情報は次のとおりです。
"アメリカ人" の定義は次のいずれかに合致する人をいいます。
- アメリカ市民権をもつ者あるいはアメリカ居住の外国人
- アメリカ合衆国法のもとで設立された法人 (原文の表現を若干簡略化)
- An estate (?)
- A domestic trust (?)
もしあなたが "アメリカ人" である場合、次の納税者識別番号 (TIN) のうちいずれかが必要です。
- SSN (社会保障番号)
- EIN (米国法人番号)
アメリカ国外居住のクリエータ
アメリカ国外居住のクリエータは、W-9 Form における "アメリカ人" とはみなされません。
支払いを受けるためには、厳密にいえば納税者識別番号 (TIN) は必要ありません。ただし、その場合には 30% の源泉徴収を受けることとなります。(訳注 : 実際には日本とアメリカの二重課税になるため、実際に受け取れる金額がわずかになる可能性が高いです。) あなたの国がアメリカと租税条約を締結しており、(訳注 : 日本は締結しています。) 関連する納税者識別番号をもつ場合、租税条約に基づく免責を受けることができます。
アメリカとあなたの国との租税条約に基づいた免責を受けるには、次の納税者識別番号のうちいずれかが必要です。
- ITIN (個人用納税者識別番号) または SSN (社会保障番号)
- EIN (米国法人番号)
詳細については、米国国税庁サイト (http://www.irs.gov/) を参照してください。
租税に関しては、税理士のアドバイスを受けることを推奨します。
以上。
Apple の App Store と似た手続きをやるように見えます。(EIN [事業者の場合。個人事業主を含む。2ch スレの訳に書いた「会社の雇用者」は間違いでした。] か ITIN [個人の場合。] を記した W-8BEN Form を Microsoft に提出し、二重課税を排除する。) Apple や Microsoft が仲介してくれているおかげで、アメリカの税務だけをきちんとやっておけば全世界に売れるという点は良いと思います。
訳…
"アメリカ人" という訳し方はマズかったかも。
2008/09/26
rootkit の動作とその対策についてまとめてみる (HVM rootkit と HVM security software)
rootkit | |
HVM とは、Hardware Virtual Machine の略で、特に CPU の仮想化支援機能を使用した仮想マシンのことをいいます。
今回は、この HVM を使用した rootkit と、逆に rootkit の検出や阻止、あるいはセキュリティ拡張を行うソフトウェアを紹介します。
CPU の仮想化支援機能
Intel や AMD の最近の CPU には、x86/x64 のプロテクトモードの仮想化を支援する機能が含まれるようになってきています。
リアルモードに関しては仮想 8086 モードで仮想化が可能で、これは 80386 系列の CPU が必ず持っています。
(脇道) CPU の仮想化は必ずしも透過的な方法とは限らない
仮想 8086 モードでは、EFLAGS の IOPL ビット (I/O 操作を行うことの可能な権限レベルを指定する) に 3 未満 (0〜2) を指定すると、完全に隔離された状態となります。例えば、通常センシティブな非特権命令である PUSHF や POPF が一時的に特権命令となるのです。
8086 エミュレータは、このとき発生する一般保護例外をキャッチし、該当する操作をエミュレーションすることで仮想マシンはゲストに対して実際にリアルモードで動いているように錯覚させることが可能となります。
Intel VT-x では、これより若干複雑となります。ページングを行う際に、Shadow Paging と呼ばれるテクニックを使用しなければなりません。これは、Intel VT-x が ゲストの物理アドレスからホストの論理アドレスへ変換することを行わない (つまり、ゲストの物理アドレスからホストの物理アドレスへ変換するためのページテーブルを別途作成しなければならない) からです。*1
仮想マシンの利点
幾つかありますが、rootkit や security software の観点からは次の点が挙げられます。
- 自己の検出を不可能か、限りなく難しくできる (特に rootkit において)
- 仮想マシンの存在は (ほぼあるいは完全に) 不可視で、ゲストから仮想マシンに対して任意の操作を行うことが不可能
- VMM (仮想マシンモニタ) は仮想マシンに対してほとんどあらゆる操作や走査を行うことが可能
ここからは一番最後に挙げた項目を中心に見てみようと思います。
メモリ走査
ゲストでの "Shadow Walker" に惑わされることなくメモリを探索することが可能です。これを使用し、検出の難しい rootkit を検出することが可能です。
重要な操作を検出する
Intel VT-x や AMD-V では、次のような操作を検出可能です。
- システムレジスタ (CR0, CR3, MSRs...) の変更あるいは参照
- CPUID 命令の実行
- VMX 命令の実行
- ページフォルト
ここで特に見る価値があるのは CR0 です。ここにはシステムにとって重要なパラメータが豊富に含まれています。
例えば、通常 Windows は重要なページを書き込み保護 (CR0 の WP ビットによって制御) します。rootkit はこれを解除しないとアクセスを得ることができないため、CR0 の WP ビットを 0 にしようとします。通常これはカーネルモードで常に許可されますが、VMM の監視下ではこの操作をトラップし、必要であれば例外とみなすことが可能となります。
また、ページテーブルは VMM の制御下にあるため、これらに対して別にアクセス制御を行うこともできます。たとえば、オペレーティングシステムのデータ領域を書き込み禁止とし、オペレーティングシステム以外からの書き込みをシャットアウトするなどです。
CPUID 命令のフックの用途は、主に VMX 命令の無効化です。Intel VT-x であれば、EAX = 1 に対する戻り値のうち、EBX の 0x20 ビット (VMX) を 0 にすることで、CPU が VMX に対応していないと錯覚させることが可能となります。
Blue Pill の非公開バージョンで使用されていると謳われているのは、VMX のエミュレーションです。つまり、
- CPUID 命令をフックしない
- VMX 命令の実行をフックする
- これらの命令をソフトウェアでエミュレートする
ということが行われる (はずです)。
メモリを隠す
前回のエントリでは、"Shadow Walker" は完全に隠すことが不可能であることを説明しましたが、HVM の場合は事情が異なります。ページフォルトはゲストのページフォルトハンドラが受け取るのではなく、VMM のページフォルトハンドラが受け取るのです。これを使用すると、完全にメモリを隠すことも可能となります。
Detours Hook を行う
Detours Hook は、例えば関数の先頭などにおいて別の処理を挿入するフックです。
; 簡単のために、nop を挿入しておきます。 DetoursStart: 55 | push ebp 89 E5 | mov ebp, esp 90 | nop 90 | nop DetoursEnd:
を、次のようにします。
DetoursStart: E9 xx xx xx xx | jmp DetoursFunc DetoursEnd: DertoursFunc: | (何か操作を行う) 55 | push ebp ; 本来の操作 89 E5 | mov ebp, esp ; 本来の操作 E9 xx xx xx xx | jmp DetoursEnd
わかるでしょうか。本来 DetoursStart からまっすぐ DetoursEnd に向かうべきコードが、DetoursFunc (フック用コード) にジャンプしています。そして、本来そこにあった処理を行った後、DetoursEnd に戻っています。
これを "Shadow Walker" を使用して実装することも可能ですが、Detours Hook を "Shadow Walker" で実現する際には、問題が発生する可能性があります。*2できれば、ページすべてを int3 (cc) で埋め、ソフトウェアエミュレーションなどで対処したいところです。(もちろんその分パフォーマンスは落ちますが…。) *3
これは rootkit と security software の双方に対して有用です。なぜなら rootkit は特定の関数に寄生してフィルタをかけることが可能*4で、security software は HIPS としてシステムコールを検査することが可能であるからです。
HVM を検出する
Well Known HVM を検出する
よく知られている仮想化ソフトウェアは、ユーザモードからでも参照可能なバックドアをもちます。これらは仕様化あるいは非公式での文書化が為されていることも多いと思われます。
これらの存在を検出することで、よく知られている仮想化ソフトウェアについては検出することが可能です。
CPUID を参照する
新しい CPU に対応できないなど穴の多い検出方法ですが、割と使えると思います。
具体的には、既存の CPU の正規の CPUID と、実行中のマシンの CPUID を比較します。異なっていれば、何かが動いているでしょう。*5
CPU クロックを参照する
RDTSC 命令を使用し、特定のオペレーションに対して無駄に時間がかかっていないかを計測します。これは、VM Exit が余分な CPU 時間を消費することを利用しています。
TLB のサイズを計測する
TLB のサイズを計測することにより HVM の存在を検出する方法があります。
http://x86vmm.blogspot.com/2007/07/bluepill-detection-in-two-easy-steps.html によれば、HVM が動作中であれば少なくとも TLB が 1 エントリ少なくなるため、この差によって検出可能である、とのことです。
etc...
まだまだいろいろあるみたい。論文が随所に散らばっているため、まとめるのに苦労。
HVM 検出における注意すべき点
これらは「仮想化を」検出する方法であって、「仮想化ベースのマルウェアを」検出する手法ではないことに注意するべきです。例えばサーバ仮想化が行われた環境においては、これらによって「常に」(マルウェアの存在にかかわらず) 検出されるのです。木を隠すなら森の中とは言いますが…。
実装例 (rootkit)
- Blue Pill
- Invisible Things Lab が製作 (http://www.invisiblethingslab.com/)
- http://bluepillproject.org/
- Vitriol
実装例 (security software)
- Hypersight Rootkit Detector
- North Security Labs が製作 (http://northsecuritylabs.com/)
- Viton
。。。終了。
まとめてみて思ったのは、HVM の可能性。HVM からゲストを防御することも可能だし、HVM でゲストを欺くことも可能になる。なかなか面白いと思った次第。
*1:AMD-V では、Nested Paging に対応していればこの手間をある程度省くことが可能です。
*2:本来の "Shadow Walker" にも同一の問題があり、実行用ページとメモリ参照用ページが同一であると仮定して フロー制御 (jmp/call/ret など) 命令を発行した際にフローが破壊される危険性があります。検出に使うには不安定すぎるため、通常バグとして現れることになるでしょう。
*3:あ、もちろんメモリを書き換えての Detours Hook なら普通に可能です。が、PatchGuard に抵触する可能性があるため推奨はしません。
*4:ただし、こんなことしなくても通常 HVM rootkit は検出されません。
*5:ただし、実際には一部の MSR も参照する必要があるかもしれません。注意してください。
2008/09/23
rootkit の動作とその対策についてまとめてみる (Shadow Walker 編)
個人的な興味から。
x86 系 CPU におけるページング
ページングは、論理メモリと物理メモリの分離を実現する機能で、通常のオペレーティングシステムでは、主に次の用途に使用されています。
- オペレーティングシステムの使用するメモリ領域の保護
- タスクにおけるメモリ領域の分離
このページングというのは、論理メモリ領域を (x86 系 CPU のほとんどでは) 4KB のページに分割し、その単位で物理メモリアドレスの割り当てなどが行えるメモリモデルです。x86 系 CPU では、ページテーブルを使用してこれらの変換を制御します。
例:
- プログラムが、論理アドレス 0x12345678 を参照する
- CPU が、論理アドレス 0x12345000 (上位20ビット = 4KB 単位のブロック) に対応する物理アドレスをページテーブルより参照する
- この例では参照したページテーブルに、物理アドレス 0x8173f000 が書き込まれている。
- ページテーブルから取得したデータから、物理アドレスを算出する
- 0x8173f000 + (0x12345678 & 0x00000fff) = 0x8173f678
- 物理アドレスに対して、実際のメモリ参照を行う
これで、物理アドレスと論理アドレスを分離し、処理を実行することが可能です。
x86 系 CPU におけるページフォルト
さて、ページテーブルにはさまざまな情報を付加できます。例えば、P ビット (ページが存在しているか否か) などがあります。P ビットを 0 にすると、そのページには対応する物理メモリが割り当てられていないと解釈されます。
さて、このようなページに対してうまく読み込みや書き込みを行うことはできないため、ページフォルトが発生します。このとき、x86 系 CPU では CR2 レジスタ (カーネルでのみ参照可能) を参照することによって、フォールトを発生させた論理アドレスを特定可能です。これによって、次のようなことができます。(あくまで一例です。)
- 仮想メモリを実装する
- ページフォルト時にメモリを割り当て (あるいはディスクから復元し)、使用されていないページをディスクにスワップする。
- スタックを拡張する
- スレッドの開始時に少なめにメモリを確保しておき、必要になった時点で実際のメモリを確保する。
- etc...
x86 系 CPU における TLB
ページに対して変換を行うためにはページテーブルを参照する必要がありますが、1メモリ参照に対して一々メモリ上のページテーブルを参照していれば、パフォーマンスは飛躍的に落ちてしまいます。(実際には L1/L2 キャッシュが効きますが、それでも無視できません。) ということで、CPU にはメモリキャッシュと独立してページテーブルのキャッシュを行う領域があります。それが TLB (Translation Lookaside Buffer) です。
(実装にもよりますが普通は) TLB とメモリが完全に一致することはありません。つまり、ページテーブルが変更されても TLB は自動的には変更されません。例えば次のような例です。
- (この時点で TLB がクリアされた状態とする。)
- 仮想アドレス 0x04000000 に対応するページテーブルエントリの物理アドレスを 0x12345000 とする。
- 仮想アドレス 0x04000000 からメモリを読み込む = 物理アドレス 0x12345000 からメモリを読み込む
- 仮想アドレス 0x04000000 に対応するページテーブルエントリの物理アドレスを 0x6789a000 と変更する。
- 仮想アドレス 0x04000000 からメモリを読み込む = 物理アドレス 0x12345000 からメモリを読み込む (!)
- TLB は自動で更新されておらず、ページのアドレス変換は TLB に入っているアドレス (0x12345000) が優先します。
これではタスクを切り替える際に支障が出るため、(予想できない物理メモリを参照することとなるため。) TLB をクリアする手段もあります。
- invlpg 命令 (特定の論理アドレスに対応する TLB のみをクリアする)
- CR3 レジスタ (最上位のページテーブル [PDE] の物理アドレスを格納するレジスタ) を変更する
- すべての TLB がクリアされます。
- タスク切り替えにおいては通常これによって変更される。
さらに現在の x86 系 CPU では、DTLB と ITLB に分離されています。DTLB はデータ参照時 (データの読み込み / 書き込み) に参照される TLB で、ITLB は実行時 (実行に関するメモリ読み込み) に参照される TLB です。
"Shadow Walker" は、この TLB をうまく活用したメモリ隠ぺい手段です。
"Shadow Walker" とは / その仕組み
"Shadow Walker" は、特定のメモリアドレスに格納されている悪性コードの痕跡を隠し、それによってメモリを参照して悪性コードを検索するウイルス対策ソフトウェアを欺くことが可能である、というアイデアから生まれた手法です。
"Shadow Walker" は、まずページフォルトを扱うハンドラを変更します。つまり、ページフォルトが発生した際に、必ず "Shadow Walker" 内のルーチンに制御を移すよう指示します。次に、隠したい論理アドレスに対応するページテーブルエントリの P ビットを 0 にし、さらにそこに対応する TLB をクリアします。つまり、隠したい論理アドレスを参照しようとすると、必ずページフォルトが発生するように設定するのです。
ページフォルトが発生したときに、"Shadow Walker" 内のルーチンはまず CR2 レジスタを参照し、隠すべきページであるかを判断します。(そうでなければ、通常のページフォルトハンドラに処理を移します。)
次に、スタック上に配置された「例外が発生した命令のポインタ」が隠している (かつ CR2 と同一の) ページ内かどうか判定します。そうであれば実行によって発生した例外、そうでなければデータ参照・置換によって発生した例外です。(某所の "Shadow Walker" を説明している文章はこの点で間違っています。比較するのは CR2 レジスタだけではありません。)
- 命令実行時
- ページテーブルエントリに一時的に悪性コードの物理アドレスと P ビット = 1 を書き込む
- 悪性コード領域にあるダミー関数 (すぐに戻る) を実行 (ITLB にキャッシュ)
- ページテーブルエントリの P ビットを 0 にする
- データ参照・置換時
- ページテーブルエントリに一時的にダミーデータを格納する物理アドレスと P ビット = 1 を書き込む
- なんでもいいので実行以外のデータ参照を行う (DTLB にキャッシュ)
- ページテーブルエントリの P ビットを 0 にする
こうすることにより、命令実行時には悪性コードが参照され、データ参照時には何の変哲もないデータが参照されるようになる、というのが基礎的なアイデアです。
"Shadow Walker" を完全に隠すことはできない
ページフォルトハンドラのアドレスなどを指定するのは、IDT (Interrupt Descriptor Table) という割り込みの情報を指定するテーブルです。ただし、ここを書きかえると場合によっては素早く発見されてしまいます。(ここを書きかえるというのは rootkit の発想としてはメジャーなのです。また、"Shadow Walker" は実行とデータ参照だけを分離するため、データ参照だけされる IDT を隠すことも不可能です。)
それではページフォルトハンドラを隠せばいいじゃないかと思うかもしれませんが、これもできません。ここから少々難しくなってきます。
"Shadow Walker" のジレンマ
さて、うまく ITLB にページフォルトハンドラに対応するキャッシュが載っていればそれでいいのですが、場合によってはうまくいきません。ITLB からページフォルトハンドラに対応するキャッシュが消えた場合、たちまち破たんします。なぜなら、ページフォルトハンドラを呼ぼうとすれば、ページフォルトが発生するからです。(これは別の例外であるダブルフォールトを発生させ、Windows では BSOD となります。) これを防ぐためにダブルフォールトハンドラを隠そうとすれば、同様にトリプルフォールトが発生します。これはダブルフォールトすら呼び出せない異常なシステムである、ということを意味するため、CPU がリセット信号を出力し、結果としてハードリセットがかかってしまいます。
これと同様に、ページフォルトハンドラのほとんどの部分は隠すことができないのです。(ダブルフォールトかトリプルフォールトが発生するため) もちろんページフォルトハンドラを書きかえればそれでよいのですが、正常なカーネルのイメージと比較すれば一目瞭然。また、書き換えずにページフォルトハンドラの制御だけをコントロールすることは、不可能ではないかもしれませんが非常に難しいのです。
"Shadow Walker" を検出する
これは一例です。システムの書き換えるポイントを多くすることでこれらには対処できるかもしれませんが、基礎的なアルゴリズムだけでは検出が不可能ではないということはわかるでしょう。
- IDT の正当性の検証
- カーネルイメージの正当性の検証
追記 (2009/01/05)
id:hideakii 氏が言うメモリダンプからの Shadow Walker の検出について特に考察してみます。
id:egggarden:20090104 で公開されているソースコードでは、上記 2 つ以外に、次のアプローチでの検出も可能です。
- 異常なページテーブルエントリの発見 (論理メモリダンプの場合)
つまり、アクセス可能でかつ、ページテーブルエントリの P ビット (存在の有無) がクリアされているようなページは、Shadow Walker で操作されている可能性が非常に高くなります。(少なくとも、Windows の通常の操作において、このようなことは起こりえません。)
これは少し考えればわかることで、Shadow Walker で隠されているページはほとんどの状態 (Shadow Walker がキャッシュを意図的に作成する時以外) で存在しないとマークされています。このような状態にもかかわらず、メモリには依然としてアクセスが可能なのです。
ステルス性に優れた (もっと優れた実装の) Shadow Walker であれば、ここにさらなるカーネルコードの変更と Shadow Page Table *1で対抗することができます。つまり、(Windows NT では) 論理アドレスの 0xc0000000 から現れているページテーブルと実際のページテーブルが別であれば、このようなものも隠すことができます。
ただし、このような場合においても、物理メモリの範囲内にページテーブルの構造の複製が作成されているため、物理メモリダンプで探せば似たページが見つかりそうです。
残念ながら、このアプローチもうまくいきません。ただし、Firewire や PCI を通じたハードウェアアプローチを除けば。ソフトウェアアプローチでは、このような Shadow Page Table の存在を検出することは、カーネルコードをキッチリ探索しない限り不可能です。
よって、実効的な対策としては、前述の IDT かカーネルコードの正当性の検証しかないのです。
たとえば前述した実装であれば、IDT を調査することによって Shadow Walker の存在を確かめることができます。
*1:ページテーブルは、必ずしも論理アドレスに対応づけられている必要はありません。そのため、論理的に見えるページテーブルを偽のものにし、実際は別のページテーブルを参照させることができます。これが、Shadow Page Table で、CPU の仮想化支援機能でネストされたページテーブルがサポートされていない場合に使用されるテクニックです。
2008/08/09
仮想 8086 モードってよく考えられてるよね。
|(80386 以降に対応するプロテクトモードの一機能である仮想 8086 モードのこと…無論。)
x86 の仮想化で問題となるセンシティブな非特権命令に関して例外を出すようにできるんですよね。(EFLAGS の IOPL を 2 以下にすることで。) これはよくできてる。
2008/08/07
Xbox 360 の XNA は基本的に unsafe かも。
XNA+Xbox 360 継続中。例えばシームレスな町並みを逐次ロードで再現しようと思っても… (ビデオメモリが足らない例外が発生したら要らなさそうなデータを退避などを行うような処理をやっている場合)
例外が発生しない!
んですよね。OutOfVideoMemoryException が。少なくとも Texture2D と RenderTarget2D では。
で代わりにどうなるかというと、強制終了するでもなく、ダッシュボードだけ操作できるハング状態になるわけでもなく、完全にフリーズですよorz
もしそういうゲームを作ろうと思うのなら、確保している資源の量を自分で把握したほうがいいです。
- 以前のエントリでも確認したように、使用可能なメモリの総量はだいたい 413.3MB で、ここから管理用のメモリなどなどが順々に抜けていくため、レンダリングに使えるのは 350MB 未満と見積もっておいたほうがいいでしょう。
2008/08/01
unsafe コードで色々。 (2)
Xbox 360 + XNA の unsafe コードの探求を前回エントリ (id:xna:20080731:1217469451) に引き続き。ちょっと調べると、次のことはすぐにわかる。
- ビッグエンディアンでかつ、32 ビットモードで動作する (ポインタは 4 バイト)
- ページサイズは 64KiB 以下 (Power PC には標準の MMU 方式が存在しない。)
最小の状態で確認されているアクセス可能領域は次のとおり。(間にアクセス不能領域が存在する可能性はあるが、下手すると Conncet ごと落ちるためあまり試せていないこの領域の中にアクセス不能領域は存在しなかった。)
- 0x88500000-0x8858ffff (576 KiB)
- PE ヘッダが見える。(多分 mscorlib のコピー …かと思いきや何か違う。)
- 0xbe670000-0xbfe0ffff (24192 KiB = 23.625 MiB)
- JIT されたコード、PE ファイルのコピー、スタックなどがここに存在する。(スタックは 64 KiB ?)
一応、Visual Studio の [メモリ] ウィンドウでメモリ内容を確認することが可能 (アクセス不能領域では ?? となる。) で、スタックポインタを参照することで JIT されたコードを確認することも可能…、ということは。JIT の段階でどのようなコードに還元されるかが確認可能ということですね。([メモリ] ウィンドウさえ使わなければ Release ビルドでも可能なはず。)
というわけで戻り先アドレスを見てみると…
0xbe6905e8 : 60 00 00 00 | ori r0, r0, 0 (nop) 0xbe6905ec : 60 00 00 00 | ori r0, r0, 0 (nop)
…やけに nop の多いコードですね。それともブレークポイント設定用だろうか。
あとさっきのメモリマップから使用可能なメモリ量 (グラフィックやダイナミックなバッファなど) も推定できるかもしれない。
- 512 (Xbox 360 のメモリ量) - 32 (システム予約) - 25 (.NET CF 予約) - 32 (ハードウェア使用など) = 423 MB
まぁこれを確かめるのは…難しくはないね。1 MB の固定バッファを確保し続けてみると… 413 MB。悪い見積もりではなかったようだ。
(続く。)
unsafe コードで色々。 (3)
Oops! We found him! …から始まるエントリ。(前回 id:xna:20080801:1217561243 の続き)
0x88500000-0x8858ffff の領域は何かと思えば、.NET Compact Framework の「ランタイム」 (.NET Framework の mscoree.dll 等に相当) のようです。(Connect はここには入っていないようだ)
Xbox 360 のメモリダンプから抜き出したバイナリの情報から一部抜粋。
FILE HEADER VALUES 1F2 machine (Unknown) 8 number of sections 473B9ED7 time date stamp Thu Nov 15 10:20:23 2007 0 file pointer to symbol table 0 number of symbols E0 size of optional header 2102 characteristics Executable 32 bit word machine DLL OPTIONAL HEADER VALUES 10B magic # (PE32) 8.00 linker version 44800 size of code 1B400 size of initialized data 0 size of uninitialized data 21A08 entry point (88521A08) 400 base of code 400 base of data 88500000 image base (88500000 to 88598FFF) 10000 section alignment <-- ページサイズは 64 KiB のようだ。 200 file alignment 4.00 operating system version 0.00 image version 1.00 subsystem version 0 Win32 version 99000 size of image 400 size of headers 6D285 checksum E subsystem (Xbox) <-- 注目! (中略) Debug Directories Time Type Size RVA Pointer -------- ------ -------- -------- -------- 473B9ED7 cv 51 00010C08 10C08 Format: RSDS, {C9926A46-047E-44F4-93F6-6A40D0C71553}, 1, M:\XNA\V2\RTM\7318\XBox360\bin\release\NetCFUserMode.pdb
ちなみに dumpbin のオプションによっては失敗します。(存在しないアドレスを読もうとしてしまう。)
unsafe コードで色々。 (番外編1 - NetCFUserMode.dll?)
xboxhacker.net フォーラムのハッカーは既にこれを発見していたようだ。PDB の拡張子を除いたファイル名でググるとここだけが出てきた。
2008/07/31
unsafe コードで色々。
ひゃー。unsafe コードこえー。
static unsafe void UnmanagedTest(int a) { // アンマネージドであんなことやこんなことを…? } // ... static void Main(string[] args) { UnmanagedTest(0x12345678); // 0x12345678 は戻り先アドレスの特定を容易にするためにのみ使用 using (MainGame game = new MainGame()) { game.Run(); } }
というコードでどこまで Xbox 360 / XNA のユーザモードでがんばれるのか試してみると、とんでもない。w
- スタック上のアドレスを参照可能
- 戻り先アドレス (のポインタ) を変更可能
という超仕様。これは誰か VMX 命令を Emit する猛者が現れるかもしれんね。
…と思ったけどきちんとプロテクトされてるね。JIT コード「内」への分岐はうまくいくが外は無理らしい。
(ちなみに JIT コード自体の書き換えはできません。)
追記 : もしかしたら TLB さえ都合がつけば可能かもしれないが、面倒なので省略。
2008/05/30
スピンロック速い
マルチスレッドでスピードを出そうと思うと Monitor ではなくスピンロックを使うのも最適化にはなります。
スレッドを常に動かしておいて、非同期処理が可能になった時点で Interlocked 操作を行い、ループから脱出させる、ということをやります。
ほぼ空ループだと FPS が 390% 増し、演算時間にして 10% 程度の空き (意外とデカい) ができます。
なお、スピンロックの待機操作で Thread.Sleep(0); とやると Monitor と大差なくなるので、ループ中には何も置かないか、有益な演算処理を行うのが良いでしょう。スレッドスケジューリングが空ループで停止することはないので、ばんばん使えます (排熱に直結する CPU 使用率の問題を除けば)
2008/05/28
Xbox 360 での Texture2D.GetData / SetData のコスト
Xbox 360 では、Texture2D.GetData のコストが特に高く、GPU のデータを CPU で処理し、戻すことを行うべきではありません。SetData に関してはまだ許容範囲であると思われます。
次がベンチマークの結果です。
正方形テクスチャの結果 (SurfaceFormat : Color)
サイズ | GetData (ms) | SetData (ms) |
4x4 | 0.033 | 0.021 |
8x8 | 0.038 | 0.021 |
16x16 | 0.060 | 0.024 |
32x32 | 0.145 | 0.034 |
64x64 | 0.486 | 0.077 |
128x128 | 1.859 | 0.259 |
256x256 | 7.309 | 0.939 |
512x512 | 31.764 | 5.444 |
よく使用される (かもしれない) テクスチャサイズでの結果 (SurfaceFormat : Color)
サイズ | GetData (ms) | SetData (ms) |
640x360 | 27.653 | 4.803 |
640x480 | 37.266 | 6.421 |
720x480 | 41.894 | 7.251 |
960x540 | 62.899 | 10.887 |
正方形テクスチャの結果から算出した推定スループット
- GetData : 8.27 Mpixels/sec ≒ 33.08 MB/sec
- SetData : 48.55 Mpixels/sec ≒ 194.19 MB/sec
と、かなり遅いことがわかります。
Xbox 360 での CultureInfo
XNA (Xbox 360) の .NET Compact Framework は、多言語化対応のクラスを含みます。CultureInfo.CurrentCulture と CultureInfo.CurrentUICulture は、Xbox 360 の「本体の設定」で設定された言語 / カルチャーが入ります。
日本の Xbox 360 から設定できるすべての言語でチェックした結果は次のとおりです。
言語 | LCID | Name |
日本語 | 1041 | ja-JP |
中国語 (繁体) | 1028 | zh-TW |
韓国語 | 1042 | ko-KR |
英語 | 1033 | en-US |
フランス語 | 1036 | fr-FR |
ドイツ語 | 1031 | de-DE |
イタリア語 | 1040 | it-IT |
ポルトガル語 (ブラジル) | 1046 | pt-BR |
スペイン語 | 3082 | es-ES |
ロシア語 | 1049 | ru-RU |
ポーランド語 | 1045 | pl-PL |
…つーかここに書いてあるね。
2008/05/22
今日のブルースクリーンと CmRegisterCallback
CmRegisterCallback を使用すると、レジストリをフックできます。場合によってはレジストリを隠せます。
今日のブルースクリーンの原因は両方とも緩い型付けによるもの。DbgPrint と PVOID は意外だったわ。。。(できれば全部の型に関して型をつけてほしいんだけど…)
DbgPrint に関しては、%wZ フォーマッタでは PUNICODE_STRING を指定するようです。%ws でゴミが発生するのを防ぐには、このような方法があるようです。
2008/05/20
KeServiceDescriptorTable / Shadow
NTOS | |
KeServiceDescriptorTable (SSDT) についていろいろ調べてみました。
- システムコールは、KTHREAD 構造体の ServiceTable をもとに参照される。
- 最初すべてのスレッドの ServiceTable は KeServiceDescriptorTable である。
- 最初に GDI/USER システムコールが実行された場合、次の操作を行う。
- カーネル内部 (win32k ではない!) の GDI 関数を呼び出す。
- その中で PsConvertToGuiThread (まんまですね。) 関数を呼び出す。
- この関数で ServiceTable の参照を KeServiceDescriptorTableShadow に変更する。
Windows Vista でも、CR0 Trick さえ使うことができれば、2通りの方法でフックできます。
- すべての (または特定の) KTHREAD 構造体を変更する
- KeServiceDescriptorTable / Shadow の一部を変更する
また余談ですが、Windows Vista ではテーブルのエントリ数が 4 から 2 に減少しているようです。KeServiceDescriptorTable を弄るのが (公式には) 御法度になった以上、余分なエントリを残しておくメリットはないのでしょう。
(追記) フックできていることを確認しました。KiServiceTable が変更可能なら ServiceTable に関しては自明なので、テストを飛ばしています。
2008/05/19
ごめんなさい、甘く見てました。
BSOD | |
Windbg のクラッシュダンプデバッグが便利すぎる。
Free Build でもちゃんと OS を含めたシンボルデバッグができる上に、おおよその発生箇所の特定までしてくれる。
今回の BSOD の原因は ObUnRegisterCallbacks で PVOID を指定すべき場所に PPVOID を指定してしまったこと。
(それにしても昔と比べるとバギーなコードを書かなくなったな。まだ未成年なのに「昔」と言ってる時点でアレだが。)
ObRegisterCallbacks (PreOperation) から読み取れたログ
NTOS | |
コマンドライン (PID : 9492) から calc (PID : 7876, EPROCESS : 0x85880148) を起動する前後約4秒のログです。
タスクマネージャを開いているとものすごい量のログで読みにくくなるので、開かない方がいいです。
PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 3084, TID = 3132, Op = C, DA = 101400, ODA = 101400, KH = 0, p = 86A7F020 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 : cmd が calc を起動 (PROCESS_ALL_ACCESS | 0x0f0000) PID = 7360, TID = 9492, Op = C, DA = 1fffff, ODA = 1fffff, KH = 0, p = 85880148 : csrss が calc のハンドルを複製 (PROCESS_ALL_ACCESS | 0x0f0000) PID = 656, TID = 1128, Op = D, DA = 1fffff, ODA = 1fffff, KH = 0, p = 85880148 : svchost (サービス) がハンドルをオープン : PROCESS_QUERY_INFORMATION|PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_DUP_HANDLE|PROCESS_VM_* PID = 1088, TID = 1320, Op = C, DA = 001478, ODA = 001478, KH = 0, p = 85880148 : PROCESS_QUERY_INFORMATION|PROCESS_QUERY_LIMITED_INFORMATION PID = 1088, TID = 7100, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 1088, TID = 7100, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 : explorer が ハンドルをオープン (PROCESS_QUERY_INFORMATION) PID = 1316, TID = 1696, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 85880148 : calc が自身のハンドルをオープン : PROCESS_QUERY_INFORMATION|PROCESS_QUERY_LIMITED_INFORMATION PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 8884, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 PID = 7876, TID = 3668, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 85880148 : SYNCHRONIZE|PROCESS_QUERY_INFORMATION|PROCESS_QUERY_LIMITED_INFORMATION PID = 3084, TID = 3132, Op = C, DA = 101400, ODA = 101400, KH = 0, p = 86A7F020 : 別の svchost がハンドルをオープン : PROCESS_QUERY_INFORMATION|PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_VM_READ PID = 956, TID = 7600, Op = C, DA = 001410, ODA = 001410, KH = 0, p = 85880148 : SYNCHRONIZE|PROCESS_QUERY_INFORMATION|PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_VM_READ PID = 956, TID = 6216, Op = C, DA = 101410, ODA = 101410, KH = 0, p = 85880148 : PROCESS_QUERY_INFORMATION|PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_VM_READ|PROCESS_VM_OPERATION PID = 956, TID = 6216, Op = C, DA = 001418, ODA = 001418, KH = 0, p = 85880148 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 PID = 1924, TID = 1928, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86A08A70 : ドライバのアンインストーラの起動 (コマンドライン) PID = 2684, TID = 2324, Op = C, DA = 1fffff, ODA = 1fffff, KH = 0, p = 85951260 PID = 656, TID = 1148, Op = D, DA = 1fffff, ODA = 1fffff, KH = 0, p = 85951260
- DA/ODA = DesiredAccess / OriginalDesiredAccess
ちなみに、タスクマネージャを開いていた時のログの一部はこのような感じ。
PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 866A74C8 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 866A74C8 PID = 9272, TID = 6196, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 866A74C8 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 866BAB50 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 866BAB50 PID = 9272, TID = 6196, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 866BAB50 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 86BE2CE8 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 86BE2CE8 PID = 9272, TID = 6196, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86BE2CE8 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 869EC160 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 869EC160 PID = 9272, TID = 6196, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 869EC160 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 86BE3D90 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 86BE3D90 PID = 9272, TID = 6196, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86BE3D90 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 86C2CB88 PID = 9272, TID = 6196, Op = C, DA = 001000, ODA = 001000, KH = 0, p = 86C2CB88 PID = 9272, TID = 6196, Op = C, DA = 001400, ODA = 001400, KH = 0, p = 86C2CB88
PID 9272 がタスクマネージャで、1プロセスにつき3回 OpenProcess またはそれに準ずる操作を行っていることが伺えます。
2008/05/18
Windows Server 2008 対応 Gain Exclusivity
NTOS | |
Windows Server 2008 では、CPU の Hot Add に対応しているため、Greg の rootkit 本のとおりのやりかただとマズい可能性もあります。(一応現時点ではアッフィニティに穴は存在しないと規定されてますが、いつまで続くか保障はない)
そのため、Windows Server 2008 とそれ以降に対応するはずの Gain Exclusivity ルーチンを書いてみました。
(CPU の Hot Remove はその手法が規定されていないため対応しません。)
#include <ntddk.h> typedef void (*GE_EXCLUSIVE_PROC)(PVOID Argument, ULONG IsPrimaryProcessor); #define BITS_PER_BYTE (8) #define MAX_CPU (sizeof(KAFFINITY)*BITS_PER_BYTE) #define MAX_DPC (MAX_CPU-1) static KAFFINITY GeCurrentAffinity; static KSPIN_LOCK GeCurrentAffinitySpinLock; static VOID GeCpuInitializeProc(PVOID Argument, ULONG IsPrimaryProcessor) { /* TODO : Initialize per-CPU resource */ DbgPrint("OK : %08x, %08x", IsPrimaryProcessor, KeGetCurrentProcessorNumber()); } static VOID GeExclusivityWrapper ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { volatile ULONG* state = (volatile ULONG*)DeferredContext; GE_EXCLUSIVE_PROC proc = (GE_EXCLUSIVE_PROC)SystemArgument1; InterlockedIncrement(state+0); while (InterlockedCompareExchange(state+1, 1, 1) != 1) ___nop(); proc(SystemArgument2, 0); InterlockedDecrement(state+0); while (InterlockedCompareExchange(state+1, 0, 0) != 0) ___nop(); } static VOID GeExclusiveRunInternal ( GE_EXCLUSIVE_PROC proc, KAFFINITY affinity, PVOID arg1 ) { KDPC kdpc[MAX_DPC]; KIRQL oldIrql = DISPATCH_LEVEL; int p = 0, i = 0; ULONG cur; volatile ULONG state[2] = {0}; if (KeGetCurrentIrql() < DISPATCH_LEVEL) KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); cur = KeGetCurrentProcessorNumber(); for (; i < MAX_CPU; i++) { if (cur == i) continue; if (((KAFFINITY)1 << i) & affinity) { KeInitializeDpc(&kdpc[p], GeExclusivityWrapper, (PVOID)state); KeSetTargetProcessorDpc(&kdpc[p], (CCHAR)i); KeInsertQueueDpc(&kdpc[p], proc, arg1); p++; } } while (InterlockedCompareExchange(state+0, p, p) != p) ___nop(); InterlockedIncrement(state+1); if (((KAFFINITY)1 << cur) & affinity) proc(arg1, 1); while (InterlockedCompareExchange(state+0, 0, 0) != 0) ___nop(); InterlockedDecrement(state+1); if (oldIrql < DISPATCH_LEVEL) KeLowerIrql(oldIrql); } VOID GeExclusiveRun(GE_EXCLUSIVE_PROC proc, PVOID arg1) { FAST_SPINLOCK_HANDLE hAffinitySpinLock; KeFastAcquireSpinLock(&GeCurrentAffinitySpinLock, &hAffinitySpinLock); GeExclusiveRunInternal(proc, GeCurrentAffinity, arg1); KeFastReleaseSpinLock(&GeCurrentAffinitySpinLock, &hAffinitySpinLock); } #if NTDDI_VERSION >= NTDDI_WS08 static PVOID GeProcessorCallbackHandle; static VOID GeProcessorChangeProc ( IN PVOID CallbackContext, IN PKE_PROCESSOR_CHANGE_NOTIFY_CONTEXT ChangeContext, IN OUT PNTSTATUS OperationStatus ) { FAST_SPINLOCK_HANDLE hAffinitySpinLock; if (ChangeContext->State == KeProcessorAddCompleteNotify) { KeFastAcquireSpinLock(&GeCurrentAffinitySpinLock, &hAffinitySpinLock); ((PLONG)&GeCurrentAffinity) [ChangeContext->NtNumber / (sizeof(LONG)*BITS_PER_BYTE)] |= 1 << (ChangeContext->NtNumber); GeExclusiveRunInternal(GeCpuInitializeProc, (KAFFINITY)(((KAFFINITY)1) << ChangeContext->NtNumber), NULL); KeFastReleaseSpinLock(&GeCurrentAffinitySpinLock, &hAffinitySpinLock); } *OperationStatus = STATUS_SUCCESS; } #endif /* Call me in DriverEntry routine */ NTSTATUS GeInitialize(VOID) { #if NTDDI_VERSION >= NTDDI_WS08 NTSTATUS status; #endif #if NTDDI_VERSION < NTDDI_WIN2K #error Unsupported Operating System. #endif KeInitializeSpinLock(&GeCurrentAffinitySpinLock); GeCurrentAffinity = KeQueryActiveProcessors(); #if NTDDI_VERSION >= NTDDI_WS08 GeProcessorCallbackHandle = KeRegisterProcessorChangeCallback( GeProcessorChangeProc, &status, KE_PROCESSOR_CHANGE_ADD_EXISTING); if (GeProcessorCallbackHandle == NULL) { VERPOSE_OUTPUT( ("[CPS]GE : KeRegisterProcessorChangeCallback failed.") ); if (status != STATUS_SUCCESS) return status; else return STATUS_UNSUCCESSFUL; } #else GeExclusiveRun(GeCpuInitializeProc, NULL); #endif return STATUS_SUCCESS; } /* Call me in Unload routine */ VOID GeFinalize(VOID) { #if NTDDI_VERSION >= NTDDI_WS08 KeDeregisterProcessorChangeCallback(GeProcessorCallbackHandle); #endif }
GE_EXCLUSIVE_PROC が DISPATCH_LEVEL で実行されるコールバック関数のポインタ型です。Argument は自由に投げられます。IsPrimaryProcessor は、DPC を発行したプロセッサであるか否かを示すフラグで、GeExclusiveRun を実行した際にのみ有効です。もし 1つの CPU のみで実行されるコードが必要なら、このフラグで OK です。
コードの要点だけ説明すると、
- 全体として、システムのアッフィニティを基本として DPC を発行しています。
- Windows Server 2008 では、CPU が追加されると、アッフィニティを変更し、その CPU に初期化用の DPC を発行します。
- GeExclusiveWrapper は DPC で実行される関数で、同期を取りながら指定されたコールバックを実行します。
また、このコードに書かれていない部分は以下のとおりです。
- KeFast*SpinLock は、指定されたOSで可能な限り高速なスピンロックに展開されるマクロ
- ___nop は、x86 では __asm nop に展開され、x64 では何もしない関数への呼び出しに展開されるマクロ
- VERPOSE_OUTPUT は、デバッグ用の出力関数
一応テストはしたつもりですがバグはあるかもしれません。
2008/05/17
Vista SP1 - あれれ?
KMCS | |
makecert で作った証明書が Authenticode 用ですら認識されない。。。(signtool verify にて)
Windows からだとしっかり名前が出るんだけどなぁ。(Self signed でも Root Agency にぶら下がった形態でも。。。)
ObRegisterCallbacks, PsSetCreateProcessNotifyRoutineEx, /INTEGRITYCHECK and code signing
KMCS | |
(The entry below is a Japanese version of this entry.)
Additional kernel APIs...
ObRegisterCallbacks and PsSetCreateProcessNotifyRoutineEx is newly added functions on Vista SP1.
If you use these APIs, you can filter handle (process or thread) creation and duplication and/or hook process creation.
But these functions require driver build with /INTEGRITYCHECK linker option and signed code.
/INTEGRITYCHECK linker option
If you specify /INTEGRITYCHECK linker option, even if your binary is a user-mode application, you MUST sign your code by Kernel Mode Code Signing policy. In other words, you MUST add /ac SignTool option to specify additional cross certificate by Microsoft.
For example, like following (in ONE line):
SignTool sign /v /ph /s my /n "COMMONNAME" /ac MSCV-GlobalSign.cer /t http://timestamp.globalsign.com/scripts/timstamp.dll test.exe
Timestamping
Timestamping is not required step. (Even if your binary does not have a timestamp, it works.) But recommended.
Catalog Files
Catalog files is also not required.
Congrats!
Now you can use additional kernel APIs in your code.
Using additional kernel APIs without signing your code
Some additional kernel APIs checks your callback functions to verify if binary that have callback functions is correctly signed. MmVerifyCallbackFunction (not exported) is a common routine to check that.
This function first searches LDR_DATA_TABLE_ENTRY for binary of callback functions then checks if flag 0x20 of LDR_DATA_TABLE_ENTRY::Flags is set.
So if you patch LDR_DATA_TABLE_ENTRY and/or you patch MmVerifyCallbackFunction, you can use additional kernel APIs without signing your code.
ObRegisterCallbacks、PsSetCreateProcessNotifyRoutineEx、/INTEGRITYCHECK そしてコード署名
KMCS | |
(上記エントリはこのエントリの英語版です。)
追加のカーネル API
ObRegisterCallbacks と PsSetCreateProcessNotifyRoutineEx は Vista SP1 で新しく追加された関数です。
これを使用すると、プロセスの生成をフックしたり、ハンドル (プロセス / スレッド) の生成と複製をフィルタできます。
しかし、これらの関数は /INTEGRITYCHECK リンカオプションでビルドされたドライバと署名されたコードを必要とします。
/INTEGRITYCHECK リンカオプション
もし /INTEGRITYCHECK リンカオプションを設定する場合、そのバイナリがユーザモード アプリケーションであったとしても、カーネルモードコード署名ポリシーに従って署名を行う必要があります。つまり、SignTool に /ac オプションを追加し、Microsoft のクロス証明書を指定するようにしなければなりません。
例えば、次のとおりです (実際には1行です):
SignTool sign /v /ph /s my /n "COMMONNAME" /ac MSCV-GlobalSign.cer /t http://timestamp.globalsign.com/scripts/timstamp.dll test.exe
タイムスタンプ
タイムスタンプは必要ではありません (無くても動きます) が、推奨されます。
カタログファイル
カタログファイルも必要ではありません。
おめでとうございます!
これであなたのコードで追加のカーネル API を使用できるようになりました。
追加のカーネル API を署名なしで使用する
追加のカーネル API は指定されたコールバック関数を、きちんと (コールバック関数の所属する) バイナリが署名されているかをチェックします。MmVerifyCallbackFunction (エクスポートされていない) がこれらの共通ルーチンです。
この関数はコールバック関数の所属するバイナリの LDR_DATA_TABLE_ENTRY を検索し、LDR_DATA_TABLE_ENTRY::Flags の 0x20 フラグがセットされているかチェックします。
つまり、LDR_DATA_TABLE_ENTRY にパッチを行うか、MmVerifyCallbackFunction にパッチを行うことで、追加のカーネル API を署名なしに使用できます。
仮想マシンはVTをサポートしませんよね。
一連の開発でもVTを利用していると思うのですが、デバッグ作業はどうされてますか?
実機2台でシリアル接続でデバッグ、なのでしょうか?
ただし最初にテンプレートとしてどっかから持ってきた VT 対応の仮想化ソフトをパクって、
それを順々に自作のコードに置き換えるといったことをしてます。
ここまでできると基本的に (重大なバグがなければ) 再起動なしでデバッグが可能になるので、
開発がずいぶんと楽になります。
それはすごいですけど、真似したくないですね、わたしは(笑
実機はやむを得ないとしてもステップ実行できないのは、私にはやっぱり辛いです…。
とはいえ、実機デバッグの覚悟はできました。
情報ありがとうございます!