Filter Manager APIを使ってボリュームを列挙する例 [CodeTips]
年明け最初に上げようと思って、昨年末に用意していた内容を上げておきます。
ということは、「あけましておめでとうございます」ですね。明日から5月だというのに…(^^;
GitHubの説明などで 'FltMgr' (Filter Manager) APIと書いていますが、正確には"Filter Manager Support for Minifilter Drivers"の”User-Mode Library” (FltLib.dll) のAPI を使用しています。
Windowsにfltmcというコマンドがありますが、このサンプルではfltmcの instancesやvolumesオプションで表示される様な内容を取得する例を示します。fltmcがコマンド目的上フィルタドライバを中心にした情報の見せ方なのに対し、サンプルではボリュームを中心とした見せ方となってします。
なお、このサンプルは管理者モードで実行してください。
あと、すっかり当たり前に公開してしまったのですが、コード中、print_string()でUNICODE_STRINGを使用している箇所があります(どさくさに紛れて__based変数も)。ここは当然ながらUNICODE_STRINGは必ずしも必須ではありません(使用しない場合はこの部分を適切なC文字列の処理に変更してください)。表示したい内容がポインタとオフセット、レングスで与えられるため、手軽に処理する例としてこの様な使い方をしています。
Visual Studio 2010プロジェクト,Windows 7 WDK用 sourcesを含むすべての内容は GitHubに公開しています。
GitHub レポジトリ:
https://github.com/katsu-y/fsfltview
ということは、「あけましておめでとうございます」ですね。明日から5月だというのに…(^^;
GitHubの説明などで 'FltMgr' (Filter Manager) APIと書いていますが、正確には"Filter Manager Support for Minifilter Drivers"の”User-Mode Library” (FltLib.dll) のAPI を使用しています。
Windowsにfltmcというコマンドがありますが、このサンプルではfltmcの instancesやvolumesオプションで表示される様な内容を取得する例を示します。fltmcがコマンド目的上フィルタドライバを中心にした情報の見せ方なのに対し、サンプルではボリュームを中心とした見せ方となってします。
なお、このサンプルは管理者モードで実行してください。
あと、すっかり当たり前に公開してしまったのですが、コード中、print_string()でUNICODE_STRINGを使用している箇所があります(どさくさに紛れて__based変数も)。ここは当然ながらUNICODE_STRINGは必ずしも必須ではありません(使用しない場合はこの部分を適切なC文字列の処理に変更してください)。表示したい内容がポインタとオフセット、レングスで与えられるため、手軽に処理する例としてこの様な使い方をしています。
Visual Studio 2010プロジェクト,Windows 7 WDK用 sourcesを含むすべての内容は GitHubに公開しています。
GitHub レポジトリ:
https://github.com/katsu-y/fsfltview
// // fsfltview // // Sample: Using the filter manager for volume and filter instance enumeration. // // Note: This code is need to run under administrator mode. // // Author: YAMASHITA Katsuhiro // #include <SDKDDKVer.h> #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <fltuser.h> // for FltMgr #if 0 #include <winternl.h> // for UNICODE_STRING #else typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING; typedef UNICODE_STRING *PUNICODE_STRING; #endif #define _VOLUME_NAME_LENGTH 256 WORD g_wVersion = 0; HRESULT _FindFirst_VolumeInstance( PCWSTR pszVolumeName, INSTANCE_INFORMATION_CLASS dwInformationClass, LPVOID *lpReturnedBuffer, LPHANDLE lpVolumeInstanceFind ) { HRESULT hr; HANDLE hVolumeInstanceFind; DWORD BytesReturned; PVOID lpBuffer = NULL; DWORD dwBufferSize = 0; for(;;) { hr = FilterVolumeInstanceFindFirst( pszVolumeName, dwInformationClass, lpBuffer, dwBufferSize, &BytesReturned, &hVolumeInstanceFind ); if( HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER ) { if( lpBuffer ) free(lpBuffer); lpBuffer = malloc(BytesReturned); if( lpBuffer == NULL ) { hr = E_OUTOFMEMORY; break; } dwBufferSize = BytesReturned; continue; } else { break; } } if( hr == S_OK && lpBuffer != NULL ) { *lpReturnedBuffer = lpBuffer; *lpVolumeInstanceFind = hVolumeInstanceFind; } else { *lpReturnedBuffer = NULL; *lpVolumeInstanceFind = NULL; if( lpBuffer != NULL ) free(lpBuffer); } return hr; } HRESULT _FindNext_VolumeInstance( HANDLE hVolumeInstanceFind, INSTANCE_INFORMATION_CLASS dwInformationClass, LPVOID *lpReturnedBuffer ) { HRESULT hr; DWORD BytesReturned; PVOID lpBuffer = NULL; DWORD dwBufferSize = 0; for(;;) { hr = FilterVolumeInstanceFindNext( hVolumeInstanceFind, dwInformationClass, lpBuffer, dwBufferSize, &BytesReturned ); if( HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER ) { if( lpBuffer ) free(lpBuffer); lpBuffer = malloc(BytesReturned); if( lpBuffer == NULL ) { hr = E_OUTOFMEMORY; break; } dwBufferSize = BytesReturned; continue; } else { break; } } if( hr == S_OK && lpBuffer != NULL ) { *lpReturnedBuffer = lpBuffer; } else { *lpReturnedBuffer = NULL; if( lpBuffer != NULL ) free(lpBuffer); } return hr; } void print_string(PCSTR Title,PVOID pBuffer,USHORT offset,USHORT len) { BYTE __based(pBuffer) *pBased = 0; UNICODE_STRING us; us.Length = len; us.MaximumLength = us.Length; us.Buffer = (PWSTR)(pBased + offset); printf("%s%wZ\n",Title,&us); } void EnumVolumeInstance(PCWSTR pszVolumeName) { HRESULT hr; HANDLE hVolumeInstanceFind; INSTANCE_INFORMATION_CLASS InfoClass; PVOID pBuffer; if( g_wVersion >= _WIN32_WINNT_VISTA ) InfoClass = InstanceAggregateStandardInformation; else InfoClass = InstanceFullInformation; hr = _FindFirst_VolumeInstance(pszVolumeName,InfoClass, (PVOID *)&pBuffer,&hVolumeInstanceFind); if( hr == S_OK ) { do { if( g_wVersion >= _WIN32_WINNT_VISTA ) { INSTANCE_AGGREGATE_STANDARD_INFORMATION *piasi = (INSTANCE_AGGREGATE_STANDARD_INFORMATION *)pBuffer; if( piasi->Flags == FLTFL_IASI_IS_MINIFILTER ) { print_string("\tInstance Name: ",piasi, piasi->Type.MiniFilter.InstanceNameBufferOffset, piasi->Type.MiniFilter.InstanceNameLength); print_string("\tFilter Name : ",piasi, piasi->Type.MiniFilter.FilterNameBufferOffset, piasi->Type.MiniFilter.FilterNameLength); print_string("\tAltitude : ",piasi, piasi->Type.MiniFilter.AltitudeBufferOffset, piasi->Type.MiniFilter.AltitudeLength); printf("\n"); } else if( piasi->Flags == FLTFL_IASI_IS_LEGACYFILTER ) { print_string("\tFilter Name : ",piasi, piasi->Type.LegacyFilter.FilterNameBufferOffset, piasi->Type.LegacyFilter.FilterNameLength); print_string("\tAltitude : ",piasi, piasi->Type.LegacyFilter.AltitudeBufferOffset, piasi->Type.LegacyFilter.AltitudeLength); printf("\n"); } } else { INSTANCE_FULL_INFORMATION *pifi = (INSTANCE_FULL_INFORMATION *)pBuffer; print_string("\tInstance Name: ",pifi, pifi->InstanceNameBufferOffset, pifi->InstanceNameLength); print_string("\tFilter Name : ",pifi, pifi->FilterNameBufferOffset, pifi->FilterNameLength); print_string("\tAltitude : ",pifi, pifi->AltitudeBufferOffset, pifi->AltitudeLength); printf("\n"); } free(pBuffer); hr = _FindNext_VolumeInstance(hVolumeInstanceFind, InfoClass,(PVOID *)&pBuffer); } while( hr == S_OK ); } else { printf("\tenum volume instance error: 0x%08X\n\n",hr); } } int __cdecl wmain(int /*argc*/, WCHAR* /*argv*/[]) { HRESULT hr; HANDLE hFilterFind; DWORD BytesReturned; WORD wVersion = LOWORD(GetVersion()); g_wVersion = MAKEWORD(HIBYTE(wVersion),LOBYTE(wVersion)); DWORD dwBufferSize = sizeof(FILTER_VOLUME_STANDARD_INFORMATION) + (sizeof(WCHAR) * _VOLUME_NAME_LENGTH); FILTER_VOLUME_STANDARD_INFORMATION *lpBuffer = (FILTER_VOLUME_STANDARD_INFORMATION *)malloc(dwBufferSize); if( lpBuffer == NULL ) return -1; hr = FilterVolumeFindFirst( FilterVolumeStandardInformation, lpBuffer, dwBufferSize, &BytesReturned, &hFilterFind ); if( hr == S_OK ) { do { WCHAR sz[_VOLUME_NAME_LENGTH+1]; WCHAR szDosDrive[MAX_PATH]; memcpy(sz,lpBuffer->FilterVolumeName,lpBuffer->FilterVolumeNameLength); sz[ lpBuffer->FilterVolumeNameLength/sizeof(WCHAR) ] = UNICODE_NULL; if( FilterGetDosName(sz,szDosDrive,MAX_PATH) != S_OK ) { szDosDrive[0] = 0; } if( szDosDrive[0] != L'\0' ) printf("%S (%s:)\n",sz,szDosDrive); else printf("%S\n",sz); EnumVolumeInstance(sz); hr = FilterVolumeFindNext( hFilterFind, FilterVolumeStandardInformation, lpBuffer, dwBufferSize, &BytesReturned ); } while( hr == S_OK ); } else { printf("error: 0x%08X\n",hr); } free(lpBuffer); return 0; } |
SetupAPIを使ってディスクドライブを列挙する [CodeTips]
前回の記事は8月だったので、約四ヶ月!間が空いてしまいました。
でも、ここは思い出したようにやっていきます。
さて、今回はWindows Setup API を使って、ディスクドライブデバイスを列挙する例です。
この例は、SetupAPIのDevice Installation Function(SetupDiのプリフィクスを持つ)を使って、ディスクドライブクラスに登録されているデバイスを列挙して表示します。
SetupDiGetClassDevsEx関数にGUID_DEVCLASS_DISKDRIVEを指定してディスクドライブのみを列挙する様に指定し、同時にDIGCF_PROFILEを指定して現在のハードウェアプロファイルすべてを列挙する様にしています。
GUID_DEVCLASS_DISKDRIVEは、ディスクドライブ(DiskDrive)クラスを示すGUIDで、そのデバイスの列挙を指示します。一般的には内蔵ハードディスクやUSBの外付けディスク、SDカード、メモリスティックなどや仮想ディスクなども含め「ディスク」として認識され、通常エクスプローラ上でドライブが割り当てられるデバイスを指します。
フラグにDIGCF_PROFILEを指定する(0でも可。その場合はすべてのハードウェアプロファイルが対象になる)と面白い情報を得ることができます。アクティブなディスクデバイス以外に非アクティブなデバイスも列挙することができるのです。このサンプルを実行すると、例えば過去にPCに接続して、その時点でそうはたUSBメモリなども表示されます。私も長期間使っているPCに試したところ、いろいろ懐かしいUSBメモリや今は手元に無いデジカメや携帯プレーヤーなどが表示され感慨深いものがありました。
ただ、この例は対象をディスクデバイスクラスのみとしているため、DVD/CD-ROMドライブやフロッピーディスクなどディスクドライブ以外のクラスに属するデバイスは表示されません(恐らくスマートフォンもポータブルデバイス扱いで表示できません。ただ、モバイルデバイスに詳しくないので判りませんが、ものによってはソフトのインストールの為CD-ROMクラスデバイスを登録する機種ある様です。なので、ディスクドライブで表示するものもあるかもしれません)。しかし、これらのクラスを含めるのは簡単でGUID_DEVCLASS_DISKDRIVEの部分をそれぞれのクラスGUID(GUID_DEVCLASS_CDROMなど)に置き換えれば同じ様に列挙できます(もちろんGUIDを指定せず、すべてのデバイスクラス、デバイスを列挙することも可能です)。デバイスクラスはdevguid.hに記述されています。
SetupDiGetClassDevsEx関数が成功したら、後は返されたHDEVINFO を使ってSetupDiEnumDeviceInfo関数でデバイスを列挙します。デバイス情報がSP_DEVINFO_DATAに返されるので、それを使ってSetupDiGetDeviceProperty関数を呼び出し、デバイスのプロパティを得ます。取得したいプロパティの種類は引数で指定できます。ここでは単純にDEVPKEY_Device_FriendlyNameのみを取得しています。フレンドリ名とはその名の通り人間が読む為のデバイス名や商品名で構成されます。
プロパティには型があるので、一応PropertyTypeに返されるプロパティタイプをチェックし、文字列(DEVPROP_TYPE_STRING)の場合のみ表示しています。このPropertyTypeにはいろいろな型があるので、より多くのプロパティを取得したい場合には、それぞれきちんと確認する必要があります。
SetupDiGetDevicePropertyはWindows Vista以降で登場したAPIなので、Windows XP以前では使えません。もし、Windows XPで実行したい場合はSetupDiGetDevicePropertyの部分を以下の様に書き換えます。
この様にコンフィグレーションマネージャのAPIを使用するため、cfgmgr32.hのインクルードも追加してください。
こうやって多数のデバイスが列挙されたら、不要なものを削除したくなるかもしれません。その様な場合はデバイスマネージャやDeviceWalkerなどで削除すると良いでしょう。
今回、この例の様なフレンドリ名に加え、もう少しだけ表示するプロパティを追加した「完全版」サンプルをGitHubに上げておきました。以下はその一部(mainのみ)です。このサンプルもディスクドライブクラスのみ列挙しています。
すべてのソースコードを以下に載せましたので、興味のある方はどうぞ。
GitHub レポジトリ:
https://github.com/katsu-y/fsstoragedevice
でも、ここは思い出したようにやっていきます。
さて、今回はWindows Setup API を使って、ディスクドライブデバイスを列挙する例です。
#include <stdio.h> #include <windows.h> #include <setupapi.h> #include <devguid.h> #define INITGUID #include <devpkey.h> int wmain(int argc, WCHAR* argv[]) { HDEVINFO hDevInfo; hDevInfo = SetupDiGetClassDevsEx(&GUID_DEVCLASS_DISKDRIVE,NULL,NULL, DIGCF_PROFILE,NULL,NULL,NULL); if( hDevInfo != INVALID_HANDLE_VALUE ) { SP_DEVINFO_DATA DevInfoData = {0}; DevInfoData.cbSize = sizeof(DevInfoData); BYTE Buffer[4096]; DWORD Index = 0; while( SetupDiEnumDeviceInfo(hDevInfo,Index,&DevInfoData) ) { DEVPROPTYPE PropertyType; if( SetupDiGetDeviceProperty(hDevInfo,&DevInfoData, &DEVPKEY_Device_FriendlyName,&PropertyType, Buffer,sizeof(Buffer),NULL,0) ) { if( PropertyType == DEVPROP_TYPE_STRING ) { wprintf(L"%s\n",(PWSTR)Buffer); } } Index++; } SetupDiDestroyDeviceInfoList(hDevInfo); } return 0; } |
この例は、SetupAPIのDevice Installation Function(SetupDiのプリフィクスを持つ)を使って、ディスクドライブクラスに登録されているデバイスを列挙して表示します。
SetupDiGetClassDevsEx関数にGUID_DEVCLASS_DISKDRIVEを指定してディスクドライブのみを列挙する様に指定し、同時にDIGCF_PROFILEを指定して現在のハードウェアプロファイルすべてを列挙する様にしています。
GUID_DEVCLASS_DISKDRIVEは、ディスクドライブ(DiskDrive)クラスを示すGUIDで、そのデバイスの列挙を指示します。一般的には内蔵ハードディスクやUSBの外付けディスク、SDカード、メモリスティックなどや仮想ディスクなども含め「ディスク」として認識され、通常エクスプローラ上でドライブが割り当てられるデバイスを指します。
フラグにDIGCF_PROFILEを指定する(0でも可。その場合はすべてのハードウェアプロファイルが対象になる)と面白い情報を得ることができます。アクティブなディスクデバイス以外に非アクティブなデバイスも列挙することができるのです。このサンプルを実行すると、例えば過去にPCに接続して、その時点でそうはたUSBメモリなども表示されます。私も長期間使っているPCに試したところ、いろいろ懐かしいUSBメモリや今は手元に無いデジカメや携帯プレーヤーなどが表示され感慨深いものがありました。
ただ、この例は対象をディスクデバイスクラスのみとしているため、DVD/CD-ROMドライブやフロッピーディスクなどディスクドライブ以外のクラスに属するデバイスは表示されません(恐らくスマートフォンもポータブルデバイス扱いで表示できません。ただ、モバイルデバイスに詳しくないので判りませんが、ものによってはソフトのインストールの為CD-ROMクラスデバイスを登録する機種ある様です。なので、ディスクドライブで表示するものもあるかもしれません)。しかし、これらのクラスを含めるのは簡単でGUID_DEVCLASS_DISKDRIVEの部分をそれぞれのクラスGUID(GUID_DEVCLASS_CDROMなど)に置き換えれば同じ様に列挙できます(もちろんGUIDを指定せず、すべてのデバイスクラス、デバイスを列挙することも可能です)。デバイスクラスはdevguid.hに記述されています。
SetupDiGetClassDevsEx関数が成功したら、後は返されたHDEVINFO を使ってSetupDiEnumDeviceInfo関数でデバイスを列挙します。デバイス情報がSP_DEVINFO_DATAに返されるので、それを使ってSetupDiGetDeviceProperty関数を呼び出し、デバイスのプロパティを得ます。取得したいプロパティの種類は引数で指定できます。ここでは単純にDEVPKEY_Device_FriendlyNameのみを取得しています。フレンドリ名とはその名の通り人間が読む為のデバイス名や商品名で構成されます。
プロパティには型があるので、一応PropertyTypeに返されるプロパティタイプをチェックし、文字列(DEVPROP_TYPE_STRING)の場合のみ表示しています。このPropertyTypeにはいろいろな型があるので、より多くのプロパティを取得したい場合には、それぞれきちんと確認する必要があります。
SetupDiGetDevicePropertyはWindows Vista以降で登場したAPIなので、Windows XP以前では使えません。もし、Windows XPで実行したい場合はSetupDiGetDevicePropertyの部分を以下の様に書き換えます。
ULONG ulRegDataType; ULONG cbBuffer = sizeof(Buffer); if( CM_Get_DevNode_Registry_Property(DevInfoData.DevInst,CM_DRP_FRIENDLYNAME, &ulRegDataType,Buffer,&cbBuffer,0) == CR_SUCCESS ) { wprintf(L"%s\n",(PWSTR)Buffer); } |
この様にコンフィグレーションマネージャのAPIを使用するため、cfgmgr32.hのインクルードも追加してください。
#include <cfgmgr32.h> |
こうやって多数のデバイスが列挙されたら、不要なものを削除したくなるかもしれません。その様な場合はデバイスマネージャやDeviceWalkerなどで削除すると良いでしょう。
今回、この例の様なフレンドリ名に加え、もう少しだけ表示するプロパティを追加した「完全版」サンプルをGitHubに上げておきました。以下はその一部(mainのみ)です。このサンプルもディスクドライブクラスのみ列挙しています。
int wmain(int /*argc*/, WCHAR* /*argv[]*/) { _wsetlocale(LC_ALL, L""); CSimpleArray |
すべてのソースコードを以下に載せましたので、興味のある方はどうぞ。
GitHub レポジトリ:
https://github.com/katsu-y/fsstoragedevice
NtDisplayString/NtDelayExecution [CodeTips]
今回はWindows ネイティブの実行ファイルを作成します。
Win32の実行ファイルではないので、エクスプローラなどからは実行できません。後述する様にレジストリを編集してPC起動時に実行する様にします。
ソースファイル名 'ntdisplay.cpp'
作成するのがネイティブ実行ファイルなので、WDKは必須といえます。今回も使い慣れたWindows 7のWDKを使用します。
Windows Driver Kit Version 7.1.0
(もちろん、コンパイルやリンカの設定を自前で行えば、VisualStudioなどに添付された開発環境でもバイナリを生成できる筈です)
WDKでビルドする為に必要なsourcesファイルとmakefileも示しておきます。これらをソースファイルと同じディレクトリに置き、buildコマンドでビルドします。
ファイル名 'sources'
ファイル名 'makefile'
このプログラムは特に何もしません。PCを起動(再起動)すると、画面に10秒間"Hello World!"と表示して終了します。
画面にテキストを表示する部分です。
10秒間待機する部分です。 li.QuadPartに指定する値は100ns単位で、負の値だと呼び出し時点からの相対時間を意味します。
注:大きな値を与えると、長時間PCが立ち上がらないことになりますので注意してください。
前述の通り、エクスプローラなどからは実行できないので、PC起動時に実行されるプログラムとして登録します。それには以下の手順で行ってください。
1. ビルドしたntdisplay.exeを %systemroot% (例えばC:\Windows)にコピーします。
2.レジストリエディタで以下のレジストリ値を編集します。
上図の様に実行ファイル名である"ntdisplay"を追加します。
通常、"autocheck autochk *"が記述されていると思いますが、その次の行に追加するとよいでしょう。
3.[OK]を選択して値を保存し、レジストリエディタを終了して、PCを再起動します。
PC起動時にテキストで"Hello World!"と表示されたでしょうか?
今回はサンプルプログラムですので意味のある処理はしていませんが、実際にはchkdskの様なタスクを実行します。私のサイトでも、このプログラムと同じくPC起動時にファイルのコピーをするプログラムを公開していますので、興味のある方は参照してみてください。
FSNtCopy
Win32の実行ファイルではないので、エクスプローラなどからは実行できません。後述する様にレジストリを編集してPC起動時に実行する様にします。
ソースファイル名 'ntdisplay.cpp'
#include <ntddk.h> EXTERN_C NTSTATUS NTAPI NtDisplayString( PUNICODE_STRING String ); EXTERN_C NTSTATUS NTAPI NtDelayExecution( BOOLEAN Alertable, PLARGE_INTEGER Interval ); void __cdecl main() { UNICODE_STRING s; RtlInitUnicodeString(&s,L"Hello world!\n"); NtDisplayString( &s ); LARGE_INTEGER li; li.QuadPart = -(10 * 10000000); // wait for relative 10 sec NtDelayExecution( FALSE, &li ); } |
作成するのがネイティブ実行ファイルなので、WDKは必須といえます。今回も使い慣れたWindows 7のWDKを使用します。
Windows Driver Kit Version 7.1.0
(もちろん、コンパイルやリンカの設定を自前で行えば、VisualStudioなどに添付された開発環境でもバイナリを生成できる筈です)
WDKでビルドする為に必要なsourcesファイルとmakefileも示しておきます。これらをソースファイルと同じディレクトリに置き、buildコマンドでビルドします。
ファイル名 'sources'
TARGETTYPE=PROGRAM TARGETNAME=ntdisplay TARGETPATH=obj UMTYPE=nt _NT_TARGET_VERSION=$(_NT_TARGET_VERSION_WINXP) MINWIN_SDK_LIB_PATH=$(DDK_LIB_PATH) INCLUDES=$(DDK_INC_PATH) SOURCES=ntdisplay.cpp LINKLIBS=$(DDK_LIB_PATH)\ntdll.lib |
ファイル名 'makefile'
!INCLUDE $(NTMAKEENV)\makefile.def |
このプログラムは特に何もしません。PCを起動(再起動)すると、画面に10秒間"Hello World!"と表示して終了します。
RtlInitUnicodeString(&s,L"Hello world!\n"); NtDisplayString( &s ); |
LARGE_INTEGER li; li.QuadPart = -(10 * 10000000); // wait for relative 10 sec NtDelayExecution( FALSE, &li ); |
注:大きな値を与えると、長時間PCが立ち上がらないことになりますので注意してください。
前述の通り、エクスプローラなどからは実行できないので、PC起動時に実行されるプログラムとして登録します。それには以下の手順で行ってください。
1. ビルドしたntdisplay.exeを %systemroot% (例えばC:\Windows)にコピーします。
2.レジストリエディタで以下のレジストリ値を編集します。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager BootExecute (REG_MULTI_SZ) |
上図の様に実行ファイル名である"ntdisplay"を追加します。
通常、"autocheck autochk *"が記述されていると思いますが、その次の行に追加するとよいでしょう。
3.[OK]を選択して値を保存し、レジストリエディタを終了して、PCを再起動します。
PC起動時にテキストで"Hello World!"と表示されたでしょうか?
今回はサンプルプログラムですので意味のある処理はしていませんが、実際にはchkdskの様なタスクを実行します。私のサイトでも、このプログラムと同じくPC起動時にファイルのコピーをするプログラムを公開していますので、興味のある方は参照してみてください。
FSNtCopy
NtOpenFile [CodeTips]
今回はWindows ネイティブAPIのNtOpenFile関数を使ってファイルをオープンする処理の例です。
ネイティブAPIを呼び出すので、WDK(Windows Driver Kit)を使って、コマンドプロンプトで実行できるモジュールを作成します。今ではVisualStudioと結合されているWDKですが、ここではスタンドアロンで環境を用意できるWindows 7のWDKを使います。
Windows Driver Kit Version 7.1.0
ソースファイル名 'ntopen.cpp'
WDKでビルドする為に必要なsourcesファイルとmakefileも示しておきます。これらをソースファイルと同じディレクトリに置き、buildコマンドでビルドします。
ファイル名 'sources'
ファイル名 'makefile'
実行例
実行時にオープンするファイルの完全のパスをパラメータとして指定します。
NtOpenFileを使っているので、パスはWin32形式やMS-DOSドライブではなく、NTのオブジェクトネームスペースを使います。ドライブ名が割り当てられている場合は、ドライブパスのプリフィックスに'\??\'を付ける書式が指定し易いでしょう。
この例はオープンするだけで何もしません。取得できたファイルハンドルを使ってファイルを読み書きしたり情報を取得したりしてみてください。また、NtOpenFileの引数の詳細はmsdnなどで公開されていますので、いろいろ試すこともできます(ドキュメントではカーネルモードでの名称ZwOpenFileとして公開されています)。
msdn : ZwOpenFile
ネイティブAPIを呼び出すので、WDK(Windows Driver Kit)を使って、コマンドプロンプトで実行できるモジュールを作成します。今ではVisualStudioと結合されているWDKですが、ここではスタンドアロンで環境を用意できるWindows 7のWDKを使います。
Windows Driver Kit Version 7.1.0
ソースファイル名 'ntopen.cpp'
// ntopen.cpp // This sample code is opens file using native api function. // I have built using the Windows 7 WDK (7600.16385.1). // #include <ntifs.h> #include <stdio.h> #include <locale.h> // undefined native APIs EXTERN_C VOID NTAPI RtlSetLastWin32Error( ULONG ErrorCode ); EXTERN_C ULONG NTAPI RtlGetLastWin32Error( VOID ); // // open file by NT namespace // HANDLE _open_file(HANDLE hRoot,LPCWSTR FilePath, ULONG DesiredAccess,ULONG ShareAccess,ULONG OpenOptions) { OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatus = {0}; UNICODE_STRING NtPathName; NTSTATUS Status; HANDLE hFile = NULL; RtlInitUnicodeString(&NtPathName,FilePath); InitializeObjectAttributes(&ObjectAttributes,&NtPathName,0,hRoot,NULL); Status = NtOpenFile( &hFile, DesiredAccess, &ObjectAttributes, &IoStatus, ShareAccess, OpenOptions ); // Substitute from Win32 error code mechanism. RtlSetLastWin32Error(Status); return hFile; } // // wmain() // int __cdecl wmain(int argc, WCHAR* argv[]) { _wsetlocale(LC_ALL,L""); if( argc < 2 ) { wprintf(L"parameter required.\n"); return -1; } HANDLE hFile; hFile = _open_file(NULL,argv[1], FILE_READ_DATA|FILE_READ_ATTRIBUTES|SYNCHRONIZE, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_ALERT); if( hFile != NULL ) { // To do something... wprintf(L"open succeeded.\n"); NtClose(hFile); } else { wprintf(L"error : 0x%08X\n",RtlGetLastWin32Error()); } return 0; } |
WDKでビルドする為に必要なsourcesファイルとmakefileも示しておきます。これらをソースファイルと同じディレクトリに置き、buildコマンドでビルドします。
ファイル名 'sources'
TARGETTYPE=PROGRAM TARGETNAME=ntopen TARGETPATH=obj UMTYPE=console UMENTRY=wmain _NT_TARGET_VERSION=$(_NT_TARGET_VERSION_WINXP) INCLUDES=$(DDK_INC_PATH) USE_MSVCRT=1 SOURCES=\ ntopen.cpp LINKLIBS=\ $(DDK_LIB_PATH)\ntdll.lib |
ファイル名 'makefile'
!INCLUDE $(NTMAKEENV)\makefile.def |
実行例
C:>ntopen \Device\HarddiskVolume2\windows\system32\notepad.exe open succeeded. C:>ntopen \??\C:\windows\system32\notepad.exe open succeeded. |
実行時にオープンするファイルの完全のパスをパラメータとして指定します。
NtOpenFileを使っているので、パスはWin32形式やMS-DOSドライブではなく、NTのオブジェクトネームスペースを使います。ドライブ名が割り当てられている場合は、ドライブパスのプリフィックスに'\??\'を付ける書式が指定し易いでしょう。
この例はオープンするだけで何もしません。取得できたファイルハンドルを使ってファイルを読み書きしたり情報を取得したりしてみてください。また、NtOpenFileの引数の詳細はmsdnなどで公開されていますので、いろいろ試すこともできます(ドキュメントではカーネルモードでの名称ZwOpenFileとして公開されています)。
msdn : ZwOpenFile
CryptBinaryToString/CryptStringToBinary [CodeTips]
このブログは放置しておくと「荒れ野」になってしまうので(^^;、それを回避するために備忘録的なコードを載せておくことにします。
今回はCryptAPIのCryptBinaryToString/CryptStringToBinary関数です。
使用法を示す事が目的の為、エラー処理は一切省いてあります。バッファも単純に扱っています。実際には出力データの長さを問い合わせるなどの処理が必要となるでしょう。
コンパイルし、実行すると以下の結果になります。今回はtest.cppというソースに落とし、test.exeという実行ファイルを作成しました。
今回はCryptAPIのCryptBinaryToString/CryptStringToBinary関数です。
// 'cl -DUNICODE <sourcefilename>' #ifndef UNICODE #define UNICODE #endif #include <stdio.h> #include <windows.h> #include <wincrypt.h> #pragma comment(lib, "crypt32.lib") void wmain() { WCHAR wszString[4096]; DWORD cchString; /* test sample data (this data is nonsensical) */ UCHAR data[] = "\xA1\x23\x1C\x56\x4f\x48\x06\x71\x01\x39\xCC\x55\xAA\x01\x02\x03\xac\xde\xff\xa9"; /* encode */ BYTE *pbBinary = data; DWORD cbBinary = sizeof(data) - 1; // exclude termination null cchString = ARRAYSIZE(wszString); CryptBinaryToString(pbBinary,cbBinary,CRYPT_STRING_BASE64|CRYPT_STRING_NOCRLF,wszString,&cchString); printf("\nbase64:\n%S\n",wszString); /* decode */ BYTE bufDecode[4096]; DWORD cbDecode = ARRAYSIZE(bufDecode); CryptStringToBinary(wszString,cchString,CRYPT_STRING_BASE64,bufDecode,&cbDecode,NULL,NULL); /* print */ cchString = ARRAYSIZE(wszString); CryptBinaryToString(bufDecode,cbDecode,CRYPT_STRING_HEXASCIIADDR,wszString,&cchString); printf("\nhex dump:\n%S\n",wszString); } |
使用法を示す事が目的の為、エラー処理は一切省いてあります。バッファも単純に扱っています。実際には出力データの長さを問い合わせるなどの処理が必要となるでしょう。
コンパイルし、実行すると以下の結果になります。今回はtest.cppというソースに落とし、test.exeという実行ファイルを作成しました。
C:\Test\code>cl -DUNICODE test.cpp |
C:\Test\code>test base64: oSMcVk9IBnEBOcxVqgECA6ze/6k= hex dump: 0000 a1 23 1c 56 4f 48 06 71 01 39 cc 55 aa 01 02 03 .#.VOH.q.9.U.... 0010 ac de ff a9 .... |