a4lg の準技術的日記 (縮小運営中) このページをアンテナに追加

2009/03/11

妨害ポリシー

| 11:09 |  妨害ポリシー - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  妨害ポリシー - a4lg の準技術的日記 (縮小運営中)

このくらいやらないと妨害を正当化できないと思うんだ。

キーボード壊れた?

| 10:43 |  キーボード壊れた? - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  キーボード壊れた? - a4lg の準技術的日記 (縮小運営中)

かと思ったけど掃除したら直った。RealForce を今失うのは痛い。

復帰!

| 10:43 |  復帰! - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  復帰! - a4lg の準技術的日記 (縮小運営中)

八方美人になっておくより、銭ゲバナルシストになっておく方が得だと思うんだ。少なくとも今は。

なので今は妨害を実用化することだけに集中する。

Kademlia への攻撃手段を確立。

| 10:07 |  Kademlia への攻撃手段を確立。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Kademlia への攻撃手段を確立。 - a4lg の準技術的日記 (縮小運営中)

これで、トラッカーなしの BitTorrent や profes への攻撃が可能になるわけだ。*1思ったより簡単だったかも。(しかも、DHT ネットワークに限れば流通をほぼ完全にストップすることが可能になった。これは WinnyShare への妨害に比べると劇的。)

分散ハッシュテーブル (DHT) のような特定のトポロジをとるネットワークについては、トポロジの仮定をちょっと崩壊させてやることで、簡単に特定ハッシュをもつファイル流通経路を崩壊させられることがわかった。

そういえば profes は WCF を使っているわけだけれど、もしかして TCP/IP を使って DHT ネットワークを組んでるの? (いや、実装の前提は UDP/IP だからそれが気になっただけ。)

*1:ただし変形版のアルゴリズムをこれらのソフトが使っていた場合は別。profes は XOR 距離算出の部分が若干異なるけど、これは問題ないはず。

トラックバック - http://d.hatena.ne.jp/xna/20090311

2009/03/06

セキュリティ設計ができていないために丸裸になってしまう例

| 20:54 |  セキュリティ設計ができていないために丸裸になってしまう例 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  セキュリティ設計ができていないために丸裸になってしまう例 - a4lg の準技術的日記 (縮小運営中)

PS3ハードディスクの中身を復号する方法があったのだとか。

方法を見る限り、AES か DES かは知らないが、ブロック暗号ECB モード (ないし同様の脆弱性がある他のモード) を使っていたために、単にブロック位置を移動するだけで生データが抜き出せてしまった、という話のようだ。

では CBC モードを使えばいいかというと、そういう訳でもない。なぜなら、暗号HDDランダムアクセスを要求されるからだ。というわけで、一部の暗号モードは当然使えない。

いや、使えないことはない。CBC モードをブロックごとに適用することは、当然可能だ。しかし、それは問題を発生させるブロックサイズを単に増やすことにしかならない。例えば、16 バイト毎にブロック入れ替えができたものを 512 バイト毎にする、という程度の意味で。

教訓 : ブロック暗号は最低でも CTR モードを使うようにしよう。(CTR もある種の攻撃には弱いので、できればもっとセキュアなモードを使いたい。)

トラックバック - http://d.hatena.ne.jp/xna/20090306

2009/02/24

何があっても、たぶんただのご乱心です。

| 18:15 |  何があっても、たぶんただのご乱心です。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  何があっても、たぶんただのご乱心です。 - a4lg の準技術的日記 (縮小運営中)

ここ1ヶ月にわたり日記が消えているのは仕様です。

Winny 話は一貫性のない行動が目立ちすぎるため一旦全削除。

トラックバック - http://d.hatena.ne.jp/xna/20090224

2009/01/12

仮想化ソフト書くよ! (5)

| 16:48 |  仮想化ソフト書くよ! (5) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  仮想化ソフト書くよ! (5) - a4lg の準技術的日記 (縮小運営中)

前エントリ

番外編 : Shadow Walker の拡張

Shadow Walker で使うページ属性は、なにもアクセス不可だけじゃなくてもいい。通常の Shadow Walker は、次の組み合わせのページ属性を利用する。

例えば、単なるデータ参照を効率的にしたいなら、次の手もある。

  • デフォルト : 読み書き (実行不可) ページ
  • 実行アクセス (ITLB) : 実行/読み取りページ

実行不可の属性は、ご存知 NX ビットを使用する。こうすることにより、単なるデータ参照が行われるページの参照速度が向上する。パフォーマンス測定目的には、こんなのもある。

  • デフォルト : 読み取りページ
  • データアクセス (DTLB) : 読み書きページ

こうすると、書き込み対象のページに対して、対応するページテーブルエントリが DTLB に乗っている最長期間を計測することが可能となります。パフォーマンスアナライザに使えますね。

トラックバック - http://d.hatena.ne.jp/xna/20090112

2009/01/11

仮想化ソフト書くよ! (4)

| 11:33 |  仮想化ソフト書くよ! (4) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  仮想化ソフト書くよ! (4) - a4lg の準技術的日記 (縮小運営中)

前エントリ

解決っ!

少々無理やりっぽいですが、命令ブレークポイントの問題を回避することに成功しました。

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 オブジェクトを取得し、ここからブレークポイントを仕掛けるように変更。この変更でハードウェアブレークポイントも用途さえあるなら使用可能になりました。

トラックバック - http://d.hatena.ne.jp/xna/20090111

2009/01/10

Windows 7 使ってみた。& 検証してみた。

| 20:55 |  Windows 7 使ってみた。& 検証してみた。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Windows 7 使ってみた。& 検証してみた。 - a4lg の準技術的日記 (縮小運営中)

パフォーマンス

ぶっちゃけわからん (Vista とそれほど顕著な変化はない)。ただパフォーマンスインデックスが 5.9/5.9 (ボトルネックなし)→5.5/7.9 (ボトルネック : メモリ) になってしまいました。

セキュリティ (UAC)

自動昇格が確かに有効になっていました。プログラムを書いて調べていますが、簡単に思いつきそうな脆弱性はないようでした。つまり、プログラムから勝手に設定変更などはされないだろう、ということです。

ただ気になったのが Flash Player をインストールした時で、[インストールする] ボタンを押すだけでインストールが完了してしまった点。(ActiveX の実行がブロックされたという表示すら出ない) 署名済みのかつ悪意のある (両者は両立する。) ActiveX をインストールする手間を減らしてしまう危険性があるかも。

具体的には、次のようなクリック数です。いずれも、Windows + Internet Explorer の組み合わせ。

Windows XP Service Pack 23 (ActiveX の実行を選択するために 2、インストールの確認に 1)
Windows Vista4 (ActiveX の実行を選択するために 2、UAC に 1、インストールの確認に 1)
Windows 71 (インストールの確認に 1)

タスクバー

タスクバーの進歩が、意外と使いやすさを増してることにビックリ。

実際のウィンドウが多数になったとしても、アイコンはひとつだけ。つまり、タスクバーを有効活用できるし、目的のウィンドウを探す手間も最小限に抑えられる。

今までのタスクバーが普通のブラウザだとするなら、Windows 7 のタスクバーは (使い勝手が良い) ツリー表示つきのタブブラウザに相当します。

唯一使いにくい点を挙げるとすれば、ボタンを押す操作が、アプリを起動するのか、ウィンドウを最小化するのか、はたまた他の操作なのかわかり辛くなっている点。

と、ここまで書いた。

そのほかにも細かいところが使いやすくなっている様子。ただし再描画にバグがあるようで、タブやリストビューの項目がたまに「無効」になったときの表示になるみたいです。

仮想化ソフト書くよ! (3)

| 17:06 | 仮想化ソフト書くよ! (3) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク - 仮想化ソフト書くよ! (3) - a4lg の準技術的日記 (縮小運営中)

前エントリ

二転三転

またまた autotools のお世話になることにしました。

NASM…

NASM は、x86 専用のアセンブラですが、相当進んでます。というか、最近の命令 (Intel VT-x の命令 : vmread など) までキッチリサポートしてます。ただし、Cygwin のリポジトリに入ってるバージョン (2.01) は INVEPT や INVVPID 命令をサポートしていません (今の段階では関係ないし、2.03 でサポートされている) が。

マズった。

EFLAGS.RF のことをすっかり忘れてた。このフラグはハードウェア命令ブレークポイントをスキップする役目をもつが…

これのせいで、あらゆる状況でもブレークするようにはできないことがわかってきた。

具体的には、''IRET 命令で'' 戻った直後の命令が実行不可能になる可能性がある、と…。

あとデータブレークポイントはフォルト (実行しようとした命令がスタックに積まれる) じゃなくトラップ (これから実行しようとする命令がスタックに積まれる) なのね…。

2009/01/09

仮想化ソフト書くよ! (2)

| 19:26 | 仮想化ソフト書くよ! (2) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク - 仮想化ソフト書くよ! (2) - a4lg の準技術的日記 (縮小運営中)

前エントリ

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:実際にはもっと多くのレジスタを退避しています。

egggardenegggarden 2009/01/09 22:56 Intel VTの機能を利用してみたいのですけれども、
仮想マシンはVTをサポートしませんよね。
一連の開発でもVTを利用していると思うのですが、デバッグ作業はどうされてますか?
実機2台でシリアル接続でデバッグ、なのでしょうか?

xnaxna 2009/01/10 12:24 再起動しまくりの実機 only デバッグです。シリアル接続も何もないので。
ただし最初にテンプレートとしてどっかから持ってきた VT 対応の仮想化ソフトをパクって、
それを順々に自作のコードに置き換えるといったことをしてます。

xnaxna 2009/01/10 12:32 あ、ちなみに、既存の環境を VT に移動させるだけなら簡単です。 (Blue Pill や既存の PoC コードのようにね。)
ここまでできると基本的に (重大なバグがなければ) 再起動なしでデバッグが可能になるので、
開発がずいぶんと楽になります。

egggardenegggarden 2009/01/10 15:07 >再起動しまくりの実機 only デバッグです。シリアル接続も何もないので。
それはすごいですけど、真似したくないですね、わたしは(笑
実機はやむを得ないとしてもステップ実行できないのは、私にはやっぱり辛いです…。

とはいえ、実機デバッグの覚悟はできました。
情報ありがとうございます!

2009/01/08

仮想化ソフト書くよ! (1)

| 20:58 |  仮想化ソフト書くよ! (1) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  仮想化ソフト書くよ! (1) - a4lg の準技術的日記 (縮小運営中)

つーか書いてる途中です。

最初は簡単な make を使っていて、途中から autotools を使い、また make に戻ってきました。

というのも、autotools は移植性のあるプログラムには向いているけど、そうでないプログラムにはぜんぜん向いていないという事情が。例えば、x86_CC や ppc_CC (プラットフォーム依存 cc) のようなことができない。(いや、できなくはないが、make を使う以上に見た目が複雑になってしまう。)

ただ生の make だとやはり面倒があるので、マクロ言語でテンプレートを直に書き、それを configure スクリプト (自作) で置換を行うようにしてみました。

2009/01/05

フォレンジック話を始めることにした。

| 19:59 |  フォレンジック話を始めることにした。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  フォレンジック話を始めることにした。 - a4lg の準技術的日記 (縮小運営中)

というわけでカテゴリ新設。ただししばらくはカテゴリ 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でやればいいじゃない!

| 19:04 |  メモリフォレンジック?Firewireでやればいいじゃない! - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  メモリフォレンジック?Firewireでやればいいじゃない! - a4lg の準技術的日記 (縮小運営中)

から始まるメモリフォレンジック話。

信頼できる物理メモリイメージを取得したければ、Firewire や PCI のハードウェアを通じて DMA を発行してあげればよい。もっと信頼できる解析がしたければ PC の CR0, CR3 を含むレジスタの内容も同時に読み取っておくが吉。

Firewire を通じてコードのパッチを行う例には winlockpwn があって、同じ手法を用いれば何でもできる。たとえば、rootkit を強制的にインストールしてしまったり、VRAM を読み取って画面のハードコピーを取ってしまったり。というわけで、間違っても Firewire (または IEEE 1394、i.Link) ポートつきのノートパソコンを人の触れる場所に置かないこと :) わずか数秒であらゆる認証機構が排除されちゃうからね。

Shadow Walker について更新。

| 17:22 |  Shadow Walker について更新。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Shadow Walker について更新。 - a4lg の準技術的日記 (縮小運営中)

id:xna:20080923 のエントリに、メモリフォレンジックの面から考察した節を追記。

ギリギリお年玉が貰える歳

| 11:31 |  ギリギリお年玉が貰える歳 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  ギリギリお年玉が貰える歳 - a4lg の準技術的日記 (縮小運営中)

イコール成人式がある歳なんだけど。(12 日だってさ。)

暇はあるので、勉強会行こうかと思う。

hideakiihideakii 2009/01/06 07:52 Firewire標準搭載のPCってなかなか見かけないんですけど、どうなんでしょうね?、メモリのダンプ自体はddでもクラッシュダンプでもFireWireでもなんでもよいんではないかという気は個人的にはしますが、Firewire経由での取得も面白そうですね!

xnaxna 2009/01/06 11:49 最近のノート PC ならそこそこありますよ。(Firewire ポート
少なくとも \\.\PhysicalMemoryを使うよりは信頼できる情報が取れる (ソフトウェアによる妨害ができない) ので、私的にはオススメ。

Firewire ポートのついてる PC が幸いにも 2 台あるので、そのうち試してみようかと思います。

hideakiihideakii 2009/01/06 15:09 最近会社で購入したMac BookではFirewireポートがなくなっているんですよね。個人的にちょっと興味があるのが、Firewire経由の場合、OS管理外のメモリ領域までダンプできちゃったりするのか?という点です。例えば4GB搭載のPCでXPが稼働している状況でFirewire経由では3.3GBの取得になるのか4GBいけるのか。試されたらぜひレポート希望です。

xnaxna 2009/01/06 16:11 あら〜確かになくなっちゃってますね。(MacBook の Firewire ポート
となると次は DMA 転送に対応した (理論上 Firewire と同等の攻撃が可能そうな) USB 3.0 になるのかな。
> OS 管理外のメモリ…
これは既存のツールを改造するだけでできますよ。ちょっと win32dd で試してみます。

xnaxna 2009/01/06 16:53 32 ビット最大である 4GB 固定のダンプを吐くよう改良してみたら、BSOD になってしまいました。
どうやらリード操作がグラフィックボードへのコマンドと解釈され、本来のドライバがコケてしまった様子。
OS 管理外のメモリを叩くのは慎重にしたほうがよさそうです。
(Windows は管理外のメモリにデバイスをマッピングすることがある)

ただ管理外メモリを読み取ることそのものは不可能ではない様子で、
win32dd に追加のオプション (実際の物理メモリ量を教えてやる) を加えることもできそうです。

hideakiihideakii 2009/01/07 09:53 商用のダンプツールでは、グラフィックカードの利用しているメモリ空間はスキップさせるなどの処理が可能なものもあるようですが、そのあたりを考えてないツールだとBSODか止まるみたいですね。HWデバイスが使っているメモリ空間だけ避けて?管理外が取れれば安全なのでしょうかね(実はよくわかってませんが;)。OS管理外のメモリがRAMディスクなどで使われていなければそもそも取得する必要がないのかもしれませんけど。

hideakiihideakii 2009/01/07 12:47 12日って成人式なご予定なんですかね?

xnaxna 2009/01/07 16:38 一応そういう歳です…

hideakiihideakii 2009/01/07 17:51 「勉強会行こうかと」がてっきりダンプなやつかな?とか思っていたんですが(笑)

xnaxna 2009/01/07 19:06 読み直してみたら確かにそんな文面に取れるw
私は成人式に行きます。(東京は遠いのです…。

hideakiihideakii 2009/01/07 19:35 成人式は一生に一度ですから(笑)、今年もあるのかわからないですがセキュリティ&プログラミングキャンプとかに参加されると東京にこれますよ、あとは講師とかでぜひ;−)

ccicci 2009/01/08 14:05 こんにちは。興味深く読ませてもらいました。
クラッシュダンプファイルもデバイスにreserveされた領域のデータは取れないようなので、揮発性のプライマリなデータとしては悩ましいところですね。
http://www.msuiche.net/2008/10/16/microsoft-crash-dump-analysis-weaknesses/

Forensicsの世界でPhysicalMemoryの取得がメジャーなのは単純に対応しているツールの問題な気がします。クラッシュダンプファイルやハイバネーションファイルへの対応はon goingな印象です。私見ですが。

トラックバック - http://d.hatena.ne.jp/xna/20090105

2009/01/04

仮想化ソフト書いてる。

| 22:11 |  仮想化ソフト書いてる。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  仮想化ソフト書いてる。 - a4lg の準技術的日記 (縮小運営中)

そのうちオープンソースで公開できたらいいな〜と思って。ちょっとだけ現時点の進捗まとめ。

無限ブレークポイント

I/O ブレークポイント、メモリブレークポイント、実行ブレークポイントを無制限に設定することが可能。もちろんインビジブル。ハードウェアブレークポイント (それがゲストによって使用されていない場合) とページ属性の変更を巧みに利用。

Detours Hook

Detours Hook を行うライブラリを標準で装備。(開発完了)

スクリプトによる拡張

今後 JIT コンパイラと統合したい (たとえばコードキャッシュと統合するなどしたい) けど、現時点では分離した JIT コンパイラを持っている。高級言語を使いたいが現時点ではアセンブラに近い (検証できない) 低レベル言語。無限ブレークポイントを含めてすべての機能を利用可能にし、セキュリティ拡張を独自に書けるようにする。

もし開発が終わったら

彼女と結婚するんだ…

じゃなくて、(そっちは死亡フラグ) VMware に先んじて世界で初めてのセキュアな仮想化"プラットフォーム"になるね。Intel VT-x 版の開発はそれなりに早く終わるだろうし。

2008/11/25

LGPL談話 (誰も守らない LGPL 編その1)

| 13:43 |  LGPL談話 (誰も守らない LGPL 編その1) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  LGPL談話 (誰も守らない LGPL 編その1) - a4lg の準技術的日記 (縮小運営中)

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リバースエンジニアリング許可義務。プログラム本体を含めて改変、リバースエンジニアリングを許可しなければならないというのは、結構プロプライエタリなソフトウェアを作る業者にとっては痛手かもしれません。ただし、「顧客自身の利用」という条件がつく以上、あまり広い範囲に波及しなくて済むのは現実的ですが。

*1:LGPLv3 の「最小限の対応するソース」は、それがダイナミックライブラリであるなら、ライブラリのソースコードそのもの、スタティックライブラリであるなら、再リンクを行うために必要なスクリプト類を含みます。

*2:確かに LGPL 3.0 にも同様の条項があるのだけど、2.1 以前は強力。

2008/11/24

Core i7 920 テスト中

| 13:53 |  Core i7 920 テスト中 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Core i7 920 テスト中 - a4lg の準技術的日記 (縮小運営中)

いろいろな仮想化テストソフトが動くからすごい。

vmxcpu.rar を PAE 対応にしてみてもちゃんと動作。おー。

ついでに初めてのオーバークロックも実施。133MHz * 20 から 147MHz * 20 で、Core i7 940 以上のクロックと性能 (2.94GHz) に。ただサウンドに微妙にノイズが乗ってるから、やっぱマズかったのかも。

トラックバック - http://d.hatena.ne.jp/xna/20081124

2008/11/12

仮想化関連メモ / EPT 概要

| 11:25 |  仮想化関連メモ / EPT 概要 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  仮想化関連メモ / EPT 概要 - a4lg の準技術的日記 (縮小運営中)

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 で高速化できるかも。

トラックバック - http://d.hatena.ne.jp/xna/20081112

2008/11/10

Core i7 マシン買う。

| 09:13 |  Core i7 マシン買う。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Core i7 マシン買う。 - a4lg の準技術的日記 (縮小運営中)

というわけで家でも Intel VT-x ベース (あとは Core i7 からの EPT) の仮想マシンを動かす環境ができるわけです。

トラックバック - http://d.hatena.ne.jp/xna/20081110

2008/11/03

難読化

| 13:01 |  難読化 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  難読化 - a4lg の準技術的日記 (縮小運営中)

難読化面白そうなんだけどな〜。

例えばゲームを保護するとした場合に、どのデータを保護するかというのを列挙するだけでも、PDB から抽出したデータでは不十分になる場合があり得るし…。

究極のところ、構文解析木レベルで変更を加えるようにしなきゃならないのかもね。

トラックバック - http://d.hatena.ne.jp/xna/20081103

2008/10/22

2ch の XNA スレで税務についての話が出たので。

| 22:59 |  2ch の XNA スレで税務についての話が出たので。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  2ch の XNA スレで税務についての話が出たので。 - a4lg の準技術的日記 (縮小運営中)

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 が仲介してくれているおかげで、アメリカの税務だけをきちんとやっておけば全世界に売れるという点は良いと思います。

訳…

"アメリカ人" という訳し方はマズかったかも。

トラックバック - http://d.hatena.ne.jp/xna/20081022

2008/09/26

rootkit の動作とその対策についてまとめてみる (HVM rootkit と HVM security software)

| 06:01 |  rootkit の動作とその対策についてまとめてみる (HVM rootkit と HVM security software) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  rootkit の動作とその対策についてまとめてみる (HVM rootkit と HVM security software) - a4lg の準技術的日記 (縮小運営中)

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)

実装例 (security software)

。。。終了。

まとめてみて思ったのは、HVM の可能性。HVM からゲストを防御することも可能だし、HVM でゲストを欺くことも可能になる。なかなか面白いと思った次第。

*1:AMD-V では、Nested Paging に対応していればこの手間をある程度省くことが可能です。

*2:本来の "Shadow Walker" にも同一の問題があり、実行用ページとメモリ参照用ページが同一であると仮定して フロー制御 (jmp/call/ret など) 命令を発行した際にフローが破壊される危険性があります。検出に使うには不安定すぎるため、通常バグとして現れることになるでしょう。

*3:あ、もちろんメモリを書き換えての Detours Hook なら普通に可能です。が、PatchGuard に抵触する可能性があるため推奨はしません。

*4:ただし、こんなことしなくても通常 HVM rootkit は検出されません。

*5:ただし、実際には一部の MSR も参照する必要があるかもしれません。注意してください。

トラックバック - http://d.hatena.ne.jp/xna/20080926

2008/09/23

rootkit の動作とその対策についてまとめてみる (Shadow Walker 編)

| 00:13 |  rootkit の動作とその対策についてまとめてみる (Shadow Walker 編) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  rootkit の動作とその対策についてまとめてみる (Shadow Walker 編) - a4lg の準技術的日記 (縮小運営中)

個人的な興味から。

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 の仮想化支援機能でネストされたページテーブルがサポートされていない場合に使用されるテクニックです。

egggardenegggarden 2008/09/23 21:42 Shadow Walkerは仮想メモリ関連の理解を深めるために、
わりと高い優先度で実装しようと考えていたのですが、
ここまで丁寧に解説されているとは。脱帽です。

解説はシリーズものでしょうか。 
急かすつもりはありませんが、期待しています!

xnaxna 2008/09/25 00:38 わーいほめられたー。
一応シリーズモノの予定ですが、不定期更新であることは間違いないと思います。

2008/09/20 このエントリーを含むブックマーク

トラックバック - http://d.hatena.ne.jp/xna/20080920

2008/08/09

仮想 8086 モードってよく考えられてるよね。

13:13 |  仮想 8086 モードってよく考えられてるよね。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  仮想 8086 モードってよく考えられてるよね。 - a4lg の準技術的日記 (縮小運営中)

(80386 以降に対応するプロテクトモードの一機能である仮想 8086 モードのこと…無論。)

x86 の仮想化で問題となるセンシティブな非特権命令に関して例外を出すようにできるんですよね。(EFLAGS の IOPL を 2 以下にすることで。) これはよくできてる。

トラックバック - http://d.hatena.ne.jp/xna/20080809

2008/08/07

Xbox 360 の XNA は基本的に unsafe かも。

| 19:31 |  Xbox 360 の XNA は基本的に unsafe かも。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Xbox 360 の XNA は基本的に unsafe かも。 - a4lg の準技術的日記 (縮小運営中)

XNA+Xbox 360 継続中。例えばシームレスな町並みを逐次ロードで再現しようと思っても… (ビデオメモリが足らない例外が発生したら要らなさそうなデータを退避などを行うような処理をやっている場合)

例外が発生しない!

んですよね。OutOfVideoMemoryException が。少なくとも Texture2D と RenderTarget2D では。

で代わりにどうなるかというと、強制終了するでもなく、ダッシュボードだけ操作できるハング状態になるわけでもなく、完全にフリーズですよorz

もしそういうゲームを作ろうと思うのなら、確保している資源の量を自分で把握したほうがいいです。

  • 以前のエントリでも確認したように、使用可能なメモリの総量はだいたい 413.3MB で、ここから管理用のメモリなどなどが順々に抜けていくため、レンダリングに使えるのは 350MB 未満と見積もっておいたほうがいいでしょう。
トラックバック - http://d.hatena.ne.jp/xna/20080807

2008/08/01

unsafe コードで色々。 (2)

| 12:27 |  unsafe コードで色々。 (2) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  unsafe コードで色々。 (2) - a4lg の準技術的日記 (縮小運営中)

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)

| 13:20 |  unsafe コードで色々。 (3) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  unsafe コードで色々。 (3) - a4lg の準技術的日記 (縮小運営中)

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?)

| 15:49 |  unsafe コードで色々。 (番外編1 - NetCFUserMode.dll?) - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  unsafe コードで色々。 (番外編1 - NetCFUserMode.dll?) - a4lg の準技術的日記 (縮小運営中)

xboxhacker.net フォーラムのハッカーは既にこれを発見していたようだ。PDB の拡張子を除いたファイル名でググるとここだけが出てきた。

unsafe コードで色々。 (FINAL) は公開停止中です。

| 20:05 |  unsafe コードで色々。 (FINAL) は公開停止中です。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  unsafe コードで色々。 (FINAL) は公開停止中です。 - a4lg の準技術的日記 (縮小運営中)

自説の決定的な欠陥を見つけてしまったため :(

2008/07/31

unsafe コードで色々。

| 10:57 |  unsafe コードで色々。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  unsafe コードで色々。 - a4lg の準技術的日記 (縮小運営中)

ひゃー。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 さえ都合がつけば可能かもしれないが、面倒なので省略。

Xbox LIVE Community Games

| 10:57 |  Xbox LIVE Community Games - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Xbox LIVE Community Games - a4lg の準技術的日記 (縮小運営中)

…が欧米でローンチらしいですな。

久しぶりにイレギュラーなコードでも書いてみよう。

2008/05/30

スピンロック速い

| 18:52 |  スピンロック速い - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  スピンロック速い - a4lg の準技術的日記 (縮小運営中)

マルチスレッドでスピードを出そうと思うと Monitor ではなくスピンロックを使うのも最適化にはなります。

スレッドを常に動かしておいて、非同期処理が可能になった時点で Interlocked 操作を行い、ループから脱出させる、ということをやります。

ほぼ空ループだと FPS が 390% 増し、演算時間にして 10% 程度の空き (意外とデカい) ができます。

なお、スピンロックの待機操作で Thread.Sleep(0); とやると Monitor と大差なくなるので、ループ中には何も置かないか、有益な演算処理を行うのが良いでしょう。スレッドスケジューリングが空ループで停止することはないので、ばんばん使えます (排熱に直結する CPU 使用率の問題を除けば)

トラックバック - http://d.hatena.ne.jp/xna/20080530

2008/05/28

Xbox 360 での Texture2D.GetData / SetData のコスト

| 10:06 |  Xbox 360 での Texture2D.GetData / SetData のコスト - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Xbox 360 での Texture2D.GetData / SetData のコスト - a4lg の準技術的日記 (縮小運営中)

Xbox 360 では、Texture2D.GetData のコストが特に高く、GPU のデータを CPU で処理し、戻すことを行うべきではありません。SetData に関してはまだ許容範囲であると思われます。

次がベンチマークの結果です。

正方形テクスチャの結果 (SurfaceFormat : Color)

サイズGetData (ms)SetData (ms)
4x40.0330.021
8x80.0380.021
16x160.0600.024
32x320.1450.034
64x640.4860.077
128x1281.8590.259
256x2567.3090.939
512x51231.7645.444

よく使用される (かもしれない) テクスチャサイズでの結果 (SurfaceFormat : Color)

サイズGetData (ms)SetData (ms)
640x36027.6534.803
640x48037.2666.421
720x48041.8947.251
960x54062.89910.887

正方形テクスチャの結果から算出した推定スループット

  • GetData : 8.27 Mpixels/sec ≒ 33.08 MB/sec
  • SetData : 48.55 Mpixels/sec ≒ 194.19 MB/sec

と、かなり遅いことがわかります。

Xbox 360 での CultureInfo

| 09:05 |  Xbox 360 での CultureInfo - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Xbox 360 での CultureInfo - a4lg の準技術的日記 (縮小運営中)

XNA (Xbox 360) の .NET Compact Framework は、多言語化対応のクラスを含みます。CultureInfo.CurrentCulture と CultureInfo.CurrentUICulture は、Xbox 360 の「本体の設定」で設定された言語 / カルチャーが入ります。

日本の Xbox 360 から設定できるすべての言語でチェックした結果は次のとおりです。

言語LCIDName
日本語1041ja-JP
中国語 (繁体)1028zh-TW
韓国語1042ko-KR
英語1033en-US
フランス語1036fr-FR
ドイツ語1031de-DE
イタリア語1040it-IT
ポルトガル語 (ブラジル)1046pt-BR
スペイン語3082es-ES
ロシア語1049ru-RU
ポーランド語1045pl-PL

…つーかここに書いてあるね。

http://msdn.microsoft.com/ja-jp/library/bb975829.aspx

ネタ切れ

| 09:10 |  ネタ切れ - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  ネタ切れ - a4lg の準技術的日記 (縮小運営中)

というわけで XNA カテゴリのエントリを久し振りに書くことにする。

トラックバック - http://d.hatena.ne.jp/xna/20080528

2008/05/22

今日のブルースクリーンと CmRegisterCallback

| 17:57 |  今日のブルースクリーンと CmRegisterCallback - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  今日のブルースクリーンと CmRegisterCallback - a4lg の準技術的日記 (縮小運営中)

CmRegisterCallback を使用すると、レジストリをフックできます。場合によってはレジストリを隠せます。

今日のブルースクリーンの原因は両方とも緩い型付けによるもの。DbgPrint と PVOID は意外だったわ。。。(できれば全部の型に関して型をつけてほしいんだけど…)

DbgPrint に関しては、%wZ フォーマッタでは PUNICODE_STRING を指定するようです。%ws でゴミが発生するのを防ぐには、このような方法があるようです。

請求書届きました。

| 14:28 |  請求書届きました。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  請求書届きました。 - a4lg の準技術的日記 (縮小運営中)

GlobalSign のコードサイニング証明書 : \57,000/年 (税抜)

買える「安全」 : Priceless

トラックバック - http://d.hatena.ne.jp/xna/20080522

2008/05/20

KeServiceDescriptorTable / Shadow

| 18:38 | KeServiceDescriptorTable / Shadow - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク - KeServiceDescriptorTable / Shadow - a4lg の準技術的日記 (縮小運営中)

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 に関しては自明なので、テストを飛ばしています。

トラックバック - http://d.hatena.ne.jp/xna/20080520

2008/05/19

ごめんなさい、甘く見てました。

| 15:11 |  ごめんなさい、甘く見てました。 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  ごめんなさい、甘く見てました。 - a4lg の準技術的日記 (縮小運営中)

Windbg のクラッシュダンプデバッグが便利すぎる。

Free Build でもちゃんと OS を含めたシンボルデバッグができる上に、おおよその発生箇所の特定までしてくれる。

今回の BSOD の原因は ObUnRegisterCallbacks で PVOID を指定すべき場所に PPVOID を指定してしまったこと。

(それにしても昔と比べるとバギーなコードを書かなくなったな。まだ未成年なのに「昔」と言ってる時点でアレだが。)

ObRegisterCallbacks (PreOperation) から読み取れたログ

| 11:20 |  ObRegisterCallbacks (PreOperation) から読み取れたログ - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  ObRegisterCallbacks (PreOperation) から読み取れたログ - a4lg の準技術的日記 (縮小運営中)

コマンドライン (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

| 18:53 |  Windows Server 2008 対応 Gain Exclusivity - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Windows Server 2008 対応 Gain Exclusivity - a4lg の準技術的日記 (縮小運営中)

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 は、デバッグ用の出力関数

一応テストはしたつもりですがバグはあるかもしれません。

トラックバック - http://d.hatena.ne.jp/xna/20080518

2008/05/17

Vista SP1 - あれれ?

| 10:42 |  Vista SP1 - あれれ? - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  Vista SP1 - あれれ? - a4lg の準技術的日記 (縮小運営中)

makecert で作った証明書が Authenticode 用ですら認識されない。。。(signtool verify にて)

Windows からだとしっかり名前が出るんだけどなぁ。(Self signed でも Root Agency にぶら下がった形態でも。。。)

makecert が作成する証明書

| 10:42 |  makecert が作成する証明書 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  makecert が作成する証明書 - a4lg の準技術的日記 (縮小運営中)

どうも、これは Authenticode 用の署名ということらしい。

ObRegisterCallbacks, PsSetCreateProcessNotifyRoutineEx, /INTEGRITYCHECK and code signing

| 09:40 |  ObRegisterCallbacks, PsSetCreateProcessNotifyRoutineEx, /INTEGRITYCHECK and code signing - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  ObRegisterCallbacks, PsSetCreateProcessNotifyRoutineEx, /INTEGRITYCHECK and code signing - a4lg の準技術的日記 (縮小運営中)

(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 そしてコード署名

| 09:40 |  ObRegisterCallbacks、PsSetCreateProcessNotifyRoutineEx、/INTEGRITYCHECK そしてコード署名 - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  ObRegisterCallbacks、PsSetCreateProcessNotifyRoutineEx、/INTEGRITYCHECK そしてコード署名 - a4lg の準技術的日記 (縮小運営中)

(上記エントリはこのエントリの英語版です。)

追加のカーネル 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 を署名なしに使用できます。

早!

| 08:34 |  早! - a4lg の準技術的日記 (縮小運営中) を含むブックマーク はてなブックマーク -  早! - a4lg の準技術的日記 (縮小運営中)

GlobalSign の証明書、申請書が向こうに届いてたった 6時間で発行されてしまいました。

有効なカ−ネルモードコード署名はできたようなので、あとは /INTEGRITYCHECK したバイナリが動くか否か。

追記: /INTEGRITYCHECK したバイナリはあっさり動作しました。カタログファイルのインストールは要らないようです。

トラックバック - http://d.hatena.ne.jp/xna/20080517