トップ回答者
x64 での__readmsr apiについて。

質問
-
はじめまして、 もしスレ違いでしたら、ご指摘下さい。
(間違って、VC++スレに書き込んでしまいましたので、こちらに改めて書き込みます。)
現在Windows7のドライバとして下記の様な関数を作成いたしました。
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 関数名:AccessReadMSR
// MSRのReadを行う「rdmsr」の発行。
// ドライバ内は、デフォルトでRing0??
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
NTSTATUS AccessReadMSR(
ULONG IoctlCode, // IoControlCode
void *lpInBuffer, // 上位アプリからの入力データバッファ
// rdmsrの読み出しを行うレジスタ値
ULONG nInBufferSize, // 上位アプリからの入力データの大きさ
void *lpOutBuffer, // 上位アプリへの出力データバッファ
// rdmsrからの出力値
ULONG nOutBufferSize, // 上位アプリへの出力データの大きさ
ULONG_PTR* lpBytesReturned) // 転送完了バイト数を格納する
{
ULONGLONG data = 0;
try{
data = __readmsr(*(ULONG*)lpInBuffer); // MSR ReadAPI
memcpy((PULONG)lpOutBuffer,&data,8);
*lpBytesReturned = 8;
return STATUS_SUCCESS;
} except (EXCEPTION_EXECUTE_HANDLER) {
*lpBytesReturned = 0; // x64ではこちらに飛ぶ?。
return STATUS_UNSUCCESSFUL;
}
}
///////////////////////////////////////////////////////////////////////////////////
同じコードをx86(32Bit)で作成した物は、MSRの値を取得出来ていた様だったので
そのままx64でビルドしてみたのですが、STATUS_UNSUCCESSFULが返ってきます。
(例外が発生??)
__readmsrは、x86/x64対応の様でしたが、x64では何らかの前処理(オマジナイ?)
が必要なのでしょうか?
利用DDK(WDK):7600.16385.1
上位アプリ、DLLはVS(VC++) 2008 Sp1を利用。
実行OSは、Windows7 Pro (x86(32Bitドライバ/アプリ時)/x64(64Bitドライバ/アプリ時))
もし、なにか解決策などをご存知でしたら、大変お忙しいかと思いますが、
ご教授いただけませんでしょうか??
よろしくお願いいたします。
- 移動 Mike Wang (MSCS) 2012年10月2日 12:54 (移動元:Windows デバイスドライバー開発)
回答
-
投稿された質問のコード中で、以下の部分が気になったのですが。。。
> ULONG IoctlCode, // IoControlCode
これってもしかして、上位アプリから DeviceIoControl() でドライバに対して I/O 用のバッファを渡すような実装なんでしょうか?
だとしたらその I/O Control Code は、独自に定義した Custom I/O Control Code だと思いますが、その Code の定義 (値) は大丈夫でしょか?
I/O Control Code の中には、「User Mode から Kernel Mode に対して、どのような方法でバッファを渡すか。。。」の情報も含まれています。
なので、Custom I/O Control Code に 0 とか 1 とか適当な値を指定してもバッファが適切に Kernel Mode 側に渡されないので、まともに動かないと思います。
(Kernel Mode 側では User Mode 側で提供されている「メモリ保護」のような気の利いた機能はないので、アクセスさせ出来れば、他のドライバが使っている領域でもかまわずぶっ壊しちゃいます。)I/O Control Code は通常 CTL_CODE マクロを使って行いますので、そのマクロをご覧になれば、この Code の中にどのような情報が含まれているのかご理解いただけると思います。
(ちなみに、バッファのやり取りに関する情報は下位2ビットになります。)ご参考になりましたら幸いです。
- 回答としてマーク GFKBXFA4 2012年3月12日 1:37
-
1行目のみがコメントで、それ以降は蛇足だったのですが、誤解を与えてしまったようなので補足します。
reinterpret_castは深い意味はありません。それよりも
- __readmsr()の引数はintです。しかしULONG*にキャストして読み出しています。
- __readmsr()の戻り値は__int64です。しかしその値をULONGLONGの変数に代入しています。
- memcpy()の第1引数はvoid*です。元々void*だった引数を無意味にもPULONGにキャストしています。
- 1.のULONG*と3.のPULONGの2つの表記が使われています。
- そもそも2.で直接代入できているのにわざわざmemcpy()を使っています。
この辺りを踏まえて「ポインターについて無頓着」と表現しました。
# 改めてまとめてみると、ポインターについてと言うよりデータ型についてですね…。- 回答としてマーク GFKBXFA4 2012年3月12日 1:37
-
関係無いのかもしれませんが、一応。。。
前回の私の返信の意図は、以下の様に考えたからです。
「上位アプリから DeviceIoControl() でターゲット ドライバに対し I/O バッファの受け渡しを行っているのであれば、User Mode - Kernel Mode 間で、そのバッファ ポインタの受け渡しが適切に行われていることが保証されない限り、__readmsr() や memcpy() コールでのポインタ指定を議論しても意味がない。」
先の返信でも触れましたが、I/O Control Code にはバッファ受け渡し方法に関する情報が含まれています。
GFKBXFA4さんのケースでは METHOD_BUFFERED を指定されているとのことですが、この場合 NT カーネル (I/O マネージャ) は、一旦 Kernel Mode 側での処理用に別途バッファを確保してから、その I/O Control Code をハンドリングするドライバに処理を渡し、ドライバから処理が戻されたタイミングで NT カーネルが DeviceIoControl() API の lpOutBuffer パラメータで指定されたバッファに結果をコピーする。。。という動作になると思います。
つまり、User Mode 側のアプリで確保されたバッファ ポインタが、そのまま Kernel Mode 側のドライバに渡される訳ではない、ということになるので、まずはその部分が確実に期待通りの動きになっているかを確認することが重要だと考えています。私だったら、まず以下の様なコードでこの点を確認します。
-------------------------------------------------
#define IOCTL_CUSTOM_CONTROL_CODE CTL_CODE(5000, 0x0980, METHOD_BUFFERED, FILE_ANY_ACCESS)++++++++++++++++++++++++++++++++++++++++++++++
<User Mode 側アプリ>....
BOOL bResult;
DWORD dwInBuffer = 0x12345678;
DWORD dwOutBuffer = 0;
DWORD dwBytesReturned = 0;// DebugBreak();
bResult = DeviceIoControl( hDevice,
IOCTL_CUSTOM_CONTROL_CODE,
&dwInBuffer,
sizeof( DWORD ),
&dwOutBuffer,
sizeof( DWORD ),
&dwBytesReturned,
NULL );if ( bResult && (dwBytesReturned == sizeof( DWORD )) )
{
if ( dwOutBuffer == 0x98765432 )
{
// 成功!! <バッファの受け渡しに問題なし>
}
else
{
// 失敗... <バッファの受け渡しに問題がある可能性大>
}
}
else
{
// 失敗... <バッファの受け渡し以前に、ドライバ側でIRP_MJ_DEVICE_CONTROL 処理に問題がある可能性大>
}....
++++++++++++++++++++++++++++++++++++++++++++++
<Kernel Mode 側ドライバ>NTSTATUS AccessReadMSR
(
ULONG IoctlCode, // IoControlCode
void *lpInBuffer, // 上位アプリからの入力データバッファ
ULONG nInBufferSize, // 上位アプリからの入力データの大きさ
void *lpOutBuffer, // 上位アプリへの出力データバッファ
ULONG nOutBufferSize, // 上位アプリへの出力データの大きさ
ULONG_PTR *lpBytesReturned // 転送完了バイト数を格納する
)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;if ( IoctlCode == IOCTL_CUSTOM_CONTROL_CODE )
{
if ( lpInBuffer )
{
PULONG inputData = (PULONG)lpInBuffer;if ( (nInBufferSize == sizeof( ULONG )) && (*inputData == 0x12345678) )
{
if ( (lpOutBuffer) && (nOutBufferSize == sizeof( ULONG )) )
{
PULONG outputData = (PULONG)lpOutBuffer;*outputData = 0x98765432;
*lpBytesReturned = nOutBufferSize;ntStatus = STATUS_SUCCESS;
}
}
}return ntStatus;
}
++++++++++++++++++++++++++++++++++++++++++++++
-------------------------------------------------既に、上記部分に問題がないことをご確認されている場合はご容赦ください。
なお蛇足ですが。。。
I/O Control Code の CTL_CODE によるマクロ定義で METHOD_BUFFERED を指定した場合、ドライバに渡される lpInBuffer と lpOutBuffer のポインタは同じアドレスになるはずだったと思います。
-
大変申し訳ございません。
先の私の返信内容に誤りがありました。
つきましては、下記の様に修正させていただきました。-----------------------------------------------
[誤]
// 失敗... <バッファの受け渡し以前に、ドライバ側での IRP_MJ_DIRECTORY_CONTROL 処理に問題がある可能性大>[正]
// 失敗... <バッファの受け渡し以前に、ドライバ側での IRP_MJ_DEVICE_CONTROL 処理に問題がある可能性大>
-----------------------------------------------なお上位アプリでの DeviceIoControl() API コールが FALSE になるとのことですので、まずはその デバイス I/O リクエストが GFKBXFA4 さんの作成されたドライバの、IRP_MJ_DEVICE_CONTROL ディスパッチ ルーチンおよび AccessReadMSR() に届いているかを確認された方が良いのかも。
またカーネル デバッガの使用を躊躇されているようですが、上記確認は WinDBG を使えばすぐに判別できますので、ドライバ開発をされているのであれば、カーネル モード デバッグ環境を構築されておくことをお勧めいたします。
(WinDBG のカーネル モード デバッグ環境を用意しておくと、サービスなどのユーザ モード側のデバッグもプロセス間の「壁」を超えてトレース出来るので、とっても便利です。)
- 回答としてマーク GFKBXFA4 2012年3月12日 11:06
すべての返信
-
-
投稿された質問のコード中で、以下の部分が気になったのですが。。。
> ULONG IoctlCode, // IoControlCode
これってもしかして、上位アプリから DeviceIoControl() でドライバに対して I/O 用のバッファを渡すような実装なんでしょうか?
だとしたらその I/O Control Code は、独自に定義した Custom I/O Control Code だと思いますが、その Code の定義 (値) は大丈夫でしょか?
I/O Control Code の中には、「User Mode から Kernel Mode に対して、どのような方法でバッファを渡すか。。。」の情報も含まれています。
なので、Custom I/O Control Code に 0 とか 1 とか適当な値を指定してもバッファが適切に Kernel Mode 側に渡されないので、まともに動かないと思います。
(Kernel Mode 側では User Mode 側で提供されている「メモリ保護」のような気の利いた機能はないので、アクセスさせ出来れば、他のドライバが使っている領域でもかまわずぶっ壊しちゃいます。)I/O Control Code は通常 CTL_CODE マクロを使って行いますので、そのマクロをご覧になれば、この Code の中にどのような情報が含まれているのかご理解いただけると思います。
(ちなみに、バッファのやり取りに関する情報は下位2ビットになります。)ご参考になりましたら幸いです。
- 回答としてマーク GFKBXFA4 2012年3月12日 1:37
-
お馬鹿 様
はじめまして、そして返信頂きありがとう御座います。
ドライバでレベルでのメモリアクセスやI/Oアクセス(特にWrite)の危険性は承知しております。
また、I/O Control Codeは、CTL_CODEマクロにて以下の様に設定しております。
CTL_CODE(5000,0x0980,METHOD_BUFFERED,FILE_READ_ACCESS)
( CTL_CODE(5000,0x0980,METHOD_BUFFERED,FILE_WRITE_ACCESS) でもだめでした。)
イロイロデバックにて試している最中ですが、どうやらアプリ-DLL-ドライバ間の何処かで、
バッファのアドレスを壊している様ですので、デバックにて追いかけてみようと思っております。
(VSのデバックに慣れてしまうと、カーネルデバッカの使い勝手が。。。)
大変貴重なお時間をお掛けし、アドバイスを頂戴し感謝いたします。
-
1行目のみがコメントで、それ以降は蛇足だったのですが、誤解を与えてしまったようなので補足します。
reinterpret_castは深い意味はありません。それよりも
- __readmsr()の引数はintです。しかしULONG*にキャストして読み出しています。
- __readmsr()の戻り値は__int64です。しかしその値をULONGLONGの変数に代入しています。
- memcpy()の第1引数はvoid*です。元々void*だった引数を無意味にもPULONGにキャストしています。
- 1.のULONG*と3.のPULONGの2つの表記が使われています。
- そもそも2.で直接代入できているのにわざわざmemcpy()を使っています。
この辺りを踏まえて「ポインターについて無頓着」と表現しました。
# 改めてまとめてみると、ポインターについてと言うよりデータ型についてですね…。- 回答としてマーク GFKBXFA4 2012年3月12日 1:37
-
関係無いのかもしれませんが、一応。。。
前回の私の返信の意図は、以下の様に考えたからです。
「上位アプリから DeviceIoControl() でターゲット ドライバに対し I/O バッファの受け渡しを行っているのであれば、User Mode - Kernel Mode 間で、そのバッファ ポインタの受け渡しが適切に行われていることが保証されない限り、__readmsr() や memcpy() コールでのポインタ指定を議論しても意味がない。」
先の返信でも触れましたが、I/O Control Code にはバッファ受け渡し方法に関する情報が含まれています。
GFKBXFA4さんのケースでは METHOD_BUFFERED を指定されているとのことですが、この場合 NT カーネル (I/O マネージャ) は、一旦 Kernel Mode 側での処理用に別途バッファを確保してから、その I/O Control Code をハンドリングするドライバに処理を渡し、ドライバから処理が戻されたタイミングで NT カーネルが DeviceIoControl() API の lpOutBuffer パラメータで指定されたバッファに結果をコピーする。。。という動作になると思います。
つまり、User Mode 側のアプリで確保されたバッファ ポインタが、そのまま Kernel Mode 側のドライバに渡される訳ではない、ということになるので、まずはその部分が確実に期待通りの動きになっているかを確認することが重要だと考えています。私だったら、まず以下の様なコードでこの点を確認します。
-------------------------------------------------
#define IOCTL_CUSTOM_CONTROL_CODE CTL_CODE(5000, 0x0980, METHOD_BUFFERED, FILE_ANY_ACCESS)++++++++++++++++++++++++++++++++++++++++++++++
<User Mode 側アプリ>....
BOOL bResult;
DWORD dwInBuffer = 0x12345678;
DWORD dwOutBuffer = 0;
DWORD dwBytesReturned = 0;// DebugBreak();
bResult = DeviceIoControl( hDevice,
IOCTL_CUSTOM_CONTROL_CODE,
&dwInBuffer,
sizeof( DWORD ),
&dwOutBuffer,
sizeof( DWORD ),
&dwBytesReturned,
NULL );if ( bResult && (dwBytesReturned == sizeof( DWORD )) )
{
if ( dwOutBuffer == 0x98765432 )
{
// 成功!! <バッファの受け渡しに問題なし>
}
else
{
// 失敗... <バッファの受け渡しに問題がある可能性大>
}
}
else
{
// 失敗... <バッファの受け渡し以前に、ドライバ側でIRP_MJ_DEVICE_CONTROL 処理に問題がある可能性大>
}....
++++++++++++++++++++++++++++++++++++++++++++++
<Kernel Mode 側ドライバ>NTSTATUS AccessReadMSR
(
ULONG IoctlCode, // IoControlCode
void *lpInBuffer, // 上位アプリからの入力データバッファ
ULONG nInBufferSize, // 上位アプリからの入力データの大きさ
void *lpOutBuffer, // 上位アプリへの出力データバッファ
ULONG nOutBufferSize, // 上位アプリへの出力データの大きさ
ULONG_PTR *lpBytesReturned // 転送完了バイト数を格納する
)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;if ( IoctlCode == IOCTL_CUSTOM_CONTROL_CODE )
{
if ( lpInBuffer )
{
PULONG inputData = (PULONG)lpInBuffer;if ( (nInBufferSize == sizeof( ULONG )) && (*inputData == 0x12345678) )
{
if ( (lpOutBuffer) && (nOutBufferSize == sizeof( ULONG )) )
{
PULONG outputData = (PULONG)lpOutBuffer;*outputData = 0x98765432;
*lpBytesReturned = nOutBufferSize;ntStatus = STATUS_SUCCESS;
}
}
}return ntStatus;
}
++++++++++++++++++++++++++++++++++++++++++++++
-------------------------------------------------既に、上記部分に問題がないことをご確認されている場合はご容赦ください。
なお蛇足ですが。。。
I/O Control Code の CTL_CODE によるマクロ定義で METHOD_BUFFERED を指定した場合、ドライバに渡される lpInBuffer と lpOutBuffer のポインタは同じアドレスになるはずだったと思います。
-
お馬鹿 様
返信頂きありがとう御座います。
あらためて、ご指摘ありがとう御座います。
試してみたところ、やはり下記の行に飛んできておりました。
++++++++++++++++++++++++++++++++++++++++++++++
// 失敗... <バッファの受け渡し以前に、ドライバ側での IRP_MJ_DIRECTORY_CONTROL 処理に問題がある可能性大>++++++++++++++++++++++++++++++++++++++++++++++
やはりドライバとしての、もっと根本的な部分が間違っていたようです。
あらためて、I/O Control Codeのコードや、ドライバのほかの部分を再度見直し、勉強して出直してみたいと思います。大変お手数をお掛けいたしました事を、お礼いたします。
-
大変申し訳ございません。
先の私の返信内容に誤りがありました。
つきましては、下記の様に修正させていただきました。-----------------------------------------------
[誤]
// 失敗... <バッファの受け渡し以前に、ドライバ側での IRP_MJ_DIRECTORY_CONTROL 処理に問題がある可能性大>[正]
// 失敗... <バッファの受け渡し以前に、ドライバ側での IRP_MJ_DEVICE_CONTROL 処理に問題がある可能性大>
-----------------------------------------------なお上位アプリでの DeviceIoControl() API コールが FALSE になるとのことですので、まずはその デバイス I/O リクエストが GFKBXFA4 さんの作成されたドライバの、IRP_MJ_DEVICE_CONTROL ディスパッチ ルーチンおよび AccessReadMSR() に届いているかを確認された方が良いのかも。
またカーネル デバッガの使用を躊躇されているようですが、上記確認は WinDBG を使えばすぐに判別できますので、ドライバ開発をされているのであれば、カーネル モード デバッグ環境を構築されておくことをお勧めいたします。
(WinDBG のカーネル モード デバッグ環境を用意しておくと、サービスなどのユーザ モード側のデバッグもプロセス間の「壁」を超えてトレース出来るので、とっても便利です。)
- 回答としてマーク GFKBXFA4 2012年3月12日 11:06
-
馬鹿 様
返信頂きありがとう御座います。
IRP_MJ_DIRECTORY_CONTROL >IRP_MJ_DEVICE_CONTROLは、ソースの方では
正しくなっておりましたので、コードを追加時に補正していたようです。
たしかに、WinDBGをキッチリ動作する環境を用意しておいた方が良さそうですね。
最近のPCでは減りつつあるCOMやIEEE1394、専用?のUSBケーブル等での接続がネックですが、
環境を整えたいと思います。(倉庫に行って、USB-COM変換を探してきます。)
(どうせなら、LAN(クロスケーブル)にも対応していただけると便利なのですが。。>MSさん)
-
やっとで纏まった時間が出来まして、再挑戦いたしました。
結果は・・・動いて居たようでした。
どうやら、デバック用に未署名のドライバで確認していたつもりが、改造前の同名署名付きドライバ
情報がレジストリに登録されている場合、そちらを優先して使われてしまう様です。
(運用の都合でinfファイルなどを利用したインストールを行わず、実行ファイルのフォルダにドライバや
DLLを置いていたので、同名の署名付きドライバが別フォルダにもあった。)
そりゃ書名付きドライバ内では本関数は存在しないため、デバックをしていても変なアドレスに
飛んで正しく動作しませんね。
別途マッサラな環境を作成してみたら動作してましたので・・・なんでだろ?とイロイロ足掻いて
ました。
レジストリ内の書名付きの方のドライバエントリー情報を削除して、改めて実行した所取得
できておりました。
ドライバの改良などで複数のドライバで動作確認をする場合など、嵌りそうですね・・・・・
まさか、この様な落とし穴に嵌っていたとは思っても居ませんでしたが・・・・汗
(こちらも実行環境の詳細や経緯なども書くべきだったと反省しております。)
佐祐理 様、お馬鹿 様 大変ありがとう御座いました。
(特にお馬鹿さま、最後の返信にてお名前を間違えましたことをお詫び申し上げます。)
また、この様な場を提供してくださるMSさんにも感謝いたします。