l-r

33
L R firewood@Hatena hotpepsi@Twitter

Upload: firewood

Post on 13-Jul-2015

2.112 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: L-R

L R⇔

firewood@Hatenahotpepsi@Twitter

Page 2: L-R

Windows でキーを入れ替える 7 つの方法

既存のソフト レジストリ MSKLC メッセージフック LL フック フィルタドライバ その他

Page 3: L-R

対象範囲

以下の入れ替えを 7 種類紹介– 「 L 」キーを押すと「 R 」が入力される

– 「 R 」キーを押すと「 L 」が入力される

対象 OS: Windows 2000/XP– たぶん Vista/7 でも可

Page 4: L-R

なぜ L-R 入れ替えなのか

日本人は L と R の区別が苦手

入れ替えても気づかないかも!

Page 5: L-R

参考 ULR

Vista/Windows 7 におけるキーボードカスタマイズ問題http://d.hatena.ne.jp/LM-7/20090614/1244980470

Page 6: L-R

今回の手法の位置関係

(4)メッセージフック

(5)LL フック

Windows サブシステム

キーボード

クラスドライバ

キーボードドライバ

キーボード

アプリケーション

(2)レジストリ

(3)MSKLC

(6)フィルタドライバ

(7)その他

ユーザ

レベ

カーネ

レベ

ハー

ウェ

Page 7: L-R

紹介順序

手軽なものから順に紹介– 手軽≒高レベル

より低レベルな方法を追求する– 「普通のやつらの下を行け」– 「下には下がいる」

Page 8: L-R

方式 1 既存のソフト

お手軽な方法 マクロなど凝ったことができる

Page 9: L-R

既存のソフトと手法

メッセージフック– XKeymacs ( emacs な人用)

LL フック– AutoHotkey (スクリプトで色々やりたい人用)

フィルタドライバ– 窓使いの憂鬱( XP まで)

– のどか( Vista 以降)

Page 10: L-R

方式 2 レジストリ

単純な入れ替えに最適 設定方法

– 「 windows keyboard layout 」でぐぐる

– ヘルパーアプリもある( KeyLayout Fix など)

Page 11: L-R

レジストリのメリット

Windows 標準– 安全、安定、比較的簡単– 処理コストがたぶん低い

全ユーザー、またはユーザー毎に変更できる– 共通のキーとユーザー毎のキーがある

特殊なキーも入れ替えられる– 定番は Caps キーと Ctrl キーの交換

Page 12: L-R

方式 3 MSKLC

Microsoft Keyboard Layout Creator– 「サポートされていない言語のキーボードレイアウトを、簡単に定義できます」

キーマップを定義する DLL を作成・変更できる– オレオレ kbd???.dll が作成可能

日本語キーボードには未対応( ! )

Page 13: L-R

MSKLC のメリット

発音記号やデッドキーの割り当てが可能– ウムラウトや 2 ストローク入力

「ほげキー」が作れる

Page 14: L-R

方式 4 メッセージフック

SetWindowsHookEx– WH_KEYBOARD または WH_GETMESSAGE– グローバルフックの場合、 DLL 内で設定する

全プロセス、全スレッドに対してフック可能– ただし 64bit 環境では、同じ環境に対してのみ有効(両方フックするには、 32bit と 64bit 版が必要になる)

メッセージの発生の捕捉と消去が可能– 交換に使える

Page 15: L-R

WH_KEYBOARD での交換部

LRESULT CALLBACK KeyboardProc(){ if ( nCode == HC_ACTION && ScanCode > 0 ) { switch ( VirtualKeyCode ) { case VK_L: case VK_R: Input.type = INPUT_KEYBOARD; Input.ki.wVk = (WORD)(VirtualKeyCode ^ (VK_L ^ VK_R)); Input.ki.dwFlags = (lParam & 0x80000000) ? KEYEVENTF_KEYUP : 0; ::SendInput( 1, &Input, sizeof(INPUT) ); return 1; } } return ::CallNextHookEx( HookHandle, nCode, VirtualKeyCode, lParam );}

Page 16: L-R

WH_KEYBOARD について補足

プロセス間通信が必要– 処理に必要な DLL がプロセス毎に読み込まれるため

– 今回はお手軽な共有メモリを使用

自分で挿入したキー操作に対しても反応する– 対策しないと無限ループする– 今回は、スキャンコードがゼロかどうかで識別

キーを離した操作も交換すること

Page 17: L-R

WH_GETMESSAGE の場合

処理方法は WH_KEYBOARD と大差なし 全てのメッセージが取れるので、パフォーマンスに影響するかもしれない

メッセージを消したい場合は、メッセージの種類を WM_NULL に変更する

Page 18: L-R

方式 5 LL フック

Lightweight Language× ふせいかい

Low Level ○ ふつう

Page 19: L-R

方式 5 LL フック

SetWindowsHookEx(WH_KEYBOARD_LL)– グローバルフック専用 API– DLL 内である必要はない

使い勝手は WH_KEYBOARD とほぼ一緒

NT系のみ( Windows 9x系では使えない)

C# でも使える 自分で挿入したかどうかがわかる

Page 20: L-R

LL フックの交換部

LRESULT CALLBACK LowLevelKeyboardProc(){ KBDLLHOOKSTRUCT *kbhs = (KBDLLHOOKSTRUCT *)lParam; if ( nCode == HC_ACTION && (kbhs->flags & LLKHF_INJECTED) == 0 ) { switch ( kbhs->vkCode ) { case VK_L: case VK_R: Input.type = INPUT_KEYBOARD; Input.ki.wVk = (WORD)(kbhs->vkCode ^ (VK_L ^ VK_R)); Input.ki.dwFlags = (kbhs->flags & 0x80) ? KEYEVENTF_KEYUP : 0; ::SendInput( 1, &Input, sizeof(INPUT) ); return 1; } } return ::CallNextHookEx( HookHandle, nCode, wParam, lParam );}

Page 21: L-R

方式 6 フィルタドライバ

デバイスドライバの一種 ドライバとドライバの間に配置する中間ドライバ

– すでに存在するデバイスオブジェクト同士の間にはさみこむ

上位と下位の間の入出力(主にIRP )を監視、変更できる

上位

IRP

フィルタドライバ

下位

IRP

戻り値

戻り値

Page 22: L-R

キーボード入力の通知経路

上位が kbdclass.sys に対してIRP_MJ_READ を発行する

kbdclass.sys は上位にSTATUS_PENDING を返し、待つ

キー入力があると、キーボードドライバが kbdclass.sys のサービスコールバックを呼び出す

– キーのデータがバッファに書き込まれる

kbdclass.sys が上位に読み取り完了を通知する

上位

IRP

kbdclass.sys

キーボードドライバ

ポインタ サービス

コール

バック

戻り値

Page 23: L-R

フィルタドライバ 1

Windows サブシステムとキーボードクラスドライバ( kbclass.sys )との間に配置

上位からの IRP_MJ_READ を横取りし、完了(キーデータの通知)を待つ

キーデータが通知されたら、変更して上位に返す

上位

IRP

フィルタドライバ 1

kbdclass.sys

IRP

戻り値

戻り値

Page 24: L-R

フィルタドライバ 1 の処理(準備)

NTSTATUS ReadDispatch(){ ... // 読み取り完了ルーチンを設定し、ペンディングにする IoSetCompletionRoutine(Irp, OnReadComplete, Extension, TRUE, TRUE, TRUE); IoMarkIrpPending(Irp);

IoCallDriver(Extension->NextLowerDriver, Irp); return STATUS_PENDING;}

Page 25: L-R

フィルタドライバ 1 の処理(交換)

NTSTATUS OnReadComplete(){ if (Buffer[0].MakeCode == MAKE_L) { Buffer[0].MakeCode = MAKE_R; } else if (Buffer[0].MakeCode == MAKE_R) { Buffer[0].MakeCode = MAKE_L; } return STATUS_CONTINUE_COMPLETION;}

Page 26: L-R

フィルタドライバ 1 について補足

IRP のフィルタドライバとしては素直な実装 マウスにも適用可能

– kbdclass.sys が mouclass.sys になるだけ

処理待ちしている IRP を管理する必要がある– 抜かれたときにキャンセルする責務がある

kbdclass.sys よりも後にインスタンス化する– UpperFilters に追記する

Page 27: L-R

フィルタドライバ 2

kbdclass.sys とキーボードドライバとの間に配置

kbdclass.sys が提供するサービスコールバックをフックする

– キーボードドライバに自分の関数をサービスコールバックとして教える

– キー入力データを変更し、 kbdclass.sys のサービスコールバックを呼び出す

WDK のサンプル( kbfiltr )

kbdclass.sys

ポインタ

フィルタドライバ 2

キーボードドライバ

ポインタ サービス

コール

バック

サービス

コール

バック

Page 28: L-R

フィルタドライバ 2 の処理(フック)

NTSTATUS InternalIoControlDispatch(){ switch ( IrpStack->Parameters.DeviceIoControl.IoControlCode ) { case IOCTL_INTERNAL_KEYBOARD_CONNECT: CONNECT_DATA *ConnectData = (CONNECT_DATA *) (IrpStack->Parameters.DeviceIoControl.Type3InputBuffer); Extension->KeyboardConnectData = *ConnectData; ConnectData->ClassDeviceObject = DeviceObject; ConnectData->ClassService = (PSERVICE_CALLBACK_ROUTINE) KeyboardServiceCallback; break; }}

Page 29: L-R

フィルタドライバ 2 の処理(交換)

void KeyboardServiceCallback(){ PKEYBOARD_INPUT_DATA p; for ( p = InputDataStart; p < InputDataEnd; ++p ) { if (p->MakeCode == MAKE_L) { p->MakeCode = MAKE_R; } else if (p->MakeCode == MAKE_R) { p->MakeCode = MAKE_L; } } ClassService(ClassDeviceObject, InputDataStart, InputDataEnd, InputDataConsumed);}

Page 30: L-R

フィルタドライバ 2 について補足

WDK のサンプルにもある通り、キーボードでは定番の方法

マウスにも適用可能 IRP のキャンセルが不要

サービスコールバックは DPC レベルなので注意

kbdclass.sys よりも先にインスタンス化する– UpperFilters の先頭に記述する

Page 31: L-R

フィルタドライバについて補足

64bit 環境では 32bit プロセスにも有効– ただし配布するには署名が必須

ドライバの処理時点では、レジストリによる交換は反映されていない

Page 32: L-R

方式 7 その他

エクストリームダイレクト フィジカルフォースメソッド

訳 : ぶっちゃけ直接交換しちゃえば

Page 33: L-R

End of Fire