2012年03月27日

[C++]物理ドライブの先頭セクタ(MBR領域)を読み込んでみる

今回はC++(というかC言語)で物理ドライブへアクセスして、先頭セクタ(つまり先頭512バイトのMBR領域)を読み込む方法を紹介したいと思う。ちなみに、今から紹介するのはWindows上でMinGWを動かして試した方法である。

サンプルソースを読む前に、まずは自分の環境の物理ドライブの DeviceID を知っておく必要がある。MinGWのターミナル上で以下のコマンドを叩けば表示される。

$ wmic diskdrive list brief


Windows環境をお使いなら、例えば、\\.\PhysicalDrive0 などと表示されていると思う。これが DevieID、つまり物理ドライブのパスである。プログラムで物理ディスクにアクセスする際は、この DeviceID を利用することになる。

では、サンプルソースを見てみよう。WindowsAPIを使う方法とLinuxシステムコールを使う方法の二種類用意する俺の親切さw

まずはWindowsAPIを使ってMBR領域を読み込み&表示する例からだ。

hoge_win.c
#include <stdio.h>
#include <windows.h> // WinAPI

// PC/AT互換機のセクタ長は512バイト
#define SECTOR 512

int main(){

// 読み込んだサイズ
unsigned long sizeRead;
// セクタデータ格納バッファ
unsigned char buf[SECTOR];

// ハンドル取得
HANDLE h = CreateFile("\\\\.\\PHYSICALDRIVE0", // 物理ドライブパス(DeviceID)
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);

if (h == INVALID_HANDLE_VALUE) {
// ハンドル取得失敗
int errCode = GetLastError();
printf("invalid handle value! ErrorCode:%d\n", errCode);
if (errCode == ERROR_ACCESS_DENIED) {
// 多分、管理者権限で実行されてないよ!
printf("This code means 'Access Denied.'\n");
}
exit(EXIT_FAILURE);
}

// 先頭の1セクタ(つまりMBR領域)を読み込む
ReadFile(h, buf, sizeof(buf), &sizeRead, NULL);

// ハンドルを閉じる
CloseHandle(h);

// バッファの中身を16進数ダンプ表示
int i;
for (i = 0; i < sizeof(buf); i++){
if (i % 16 == 0) {
printf("%04X", i);
}
printf(" %02X", buf[i]);
if (i % 16 == 15){
printf("\n");
}
}

// 続行するには何かキーを押して下さい...
system("PAUSE");

return EXIT_SUCCESS;
}


これをMinGW上でコンパイルし、Windowsで実行すると、MBR領域が16進数でダンプされるはずである。この際、必ず管理者権限で実行すること。そうしなければ正しい情報が表示されないか、もしくはハンドルの取得で失敗する。

管理者権限で実行する方法は、exeファイルのプロパティ→互換性タブ→「管理者としてプログラムを実行する」にチェックすればOKだ。


次にLinux(MinGW/MSYS)のシステムコールを使った例である。個人的にはこちらが好みだ。

hoge_linux.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> // "O_RDONLY" などの定義
#include <unistd.h> // open, read, write, close

// PC/AT互換機のセクタ長は512バイト
#define SECTOR 512

int main(){

// セクタデータ格納バッファ
unsigned char buf[SECTOR];

// ハンドル取得
int h = open("\\\\.\\PHYSICALDRIVE0", O_RDONLY);

// 最初の1セクタ(つまりMBR領域)を読み込む
read(h, buf, sizeof(buf));

// ハンドルを閉じる
close(h);

// バッファの中身を16進数ダンプ表示
int i;
for (i = 0; i < sizeof(buf); i++){
if (i % 16 == 0) {
printf("%04X", i);
}
printf(" %02X", buf[i]);
if (i % 16 == 15){
printf("\n");
}
}

// 続行するには何かキーを押して下さい...
system("PAUSE");

return EXIT_SUCCESS;
}


これをMinGW上でコンパイルし、Windowsのコマンドプロンプト上で実行すると以下のような出力結果となる。

0000 33 C0 8E D0 BC 00 7C 8E C0 8E D8 BE 00 7C BF 00
0010 06 B9 00 02 FC F3 A4 50 68 1C 06 CB FB B9 04 00
0020 BD BE 07 80 7E 00 00 7C 0B 0F 85 0E 01 83 C5 10
0030 E2 F1 CD 18 88 56 00 55 C6 46 11 05 C6 46 10 00
0040 B4 41 BB AA 55 CD 13 5D 72 0F 81 FB 55 AA 75 09
0050 F7 C1 01 00 74 03 FE 46 10 66 60 80 7E 10 00 74
0060 26 66 68 00 00 00 00 66 FF 76 08 68 00 00 68 00
0070 7C 68 01 00 68 10 00 B4 42 8A 56 00 8B F4 CD 13
0080 9F 83 C4 10 9E EB 14 B8 01 02 BB 00 7C 8A 56 00
0090 8A 76 01 8A 4E 02 8A 6E 03 CD 13 66 61 73 1C FE
00A0 4E 11 75 0C 80 7E 00 80 0F 84 8A 00 B2 80 EB 84
00B0 55 32 E4 8A 56 00 CD 13 5D EB 9E 81 3E FE 7D 55
00C0 AA 75 6E FF 76 00 E8 8D 00 75 17 FA B0 D1 E6 64
00D0 E8 83 00 B0 DF E6 60 E8 7C 00 B0 FF E6 64 E8 75
00E0 00 FB B8 00 BB CD 1A 66 23 C0 75 3B 66 81 FB 54
00F0 43 50 41 75 32 81 F9 02 01 72 2C 66 68 07 BB 00
0100 00 66 68 00 02 00 00 66 68 08 00 00 00 66 53 66
0110 53 66 55 66 68 00 00 00 00 66 68 00 7C 00 00 66
0120 61 68 00 00 07 CD 1A 5A 32 F6 EA 00 7C 00 00 CD
0130 18 A0 B7 07 EB 08 A0 B6 07 EB 03 A0 B5 07 32 E4
0140 05 00 07 8B F0 AC 3C 00 74 09 BB 07 00 B4 0E CD
0150 10 EB F2 F4 EB FD 2B C9 E4 64 EB 00 24 02 E0 F8
0160 24 02 C3 49 6E 76 61 6C 69 64 20 70 61 72 74 69
0170 74 69 6F 6E 20 74 61 62 6C 65 00 45 72 72 6F 72
0180 20 6C 6F 61 64 69 6E 67 20 6F 70 65 72 61 74 69
0190 6E 67 20 73 79 73 74 65 6D 00 4D 69 73 73 69 6E
01A0 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74
01B0 65 6D 00 00 00 63 7B 9A 3D 91 C5 E0 00 00 00 01
01C0 01 00 1C FE FF FF 3F 00 00 00 37 16 71 02 80 FE
01D0 FF FF 07 FE FF FF 76 16 71 02 08 A4 50 09 00 FE
01E0 FF FF 0F FE FF FF 00 C0 C1 0B 00 20 81 19 00 00
01F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA
続行するには何かキーを押してください . . .


最後の2バイトがMBRブートシグニチャ("55 AA" = 0xAA55)を示していればMBR領域の読み込み成功とみてよいだろう。

禁断のMBRへの書込みを行いたい場合は、write関数を使えばできるのだが、MBRを下手にいじくるとOSが起動しなくなる恐れがある。それでもやってみたい人は十分に注意の上、自己責任でやってみてほしい。

関連エントリ


MinGW/MSYS+gccでGUIアプリ開発環境を構築
http://sunabako.sblo.jp/article/54560054.html

参考にさせていただいたサイト


MinGWでMBR読み出し
物理ディスク等のデバイスに直接アクセスする (Windows NT系OS)
マスターブートレコード(wikipedia)

C++プログラミング入門
グレゴリー サティア ダウグ ブラウン
オライリー・ジャパン
売り上げランキング: 61971

PR:お買い物はAmazonで
posted by 寄り道退屈男 at 19:40 | Comment(0) | TrackBack(0) | C++
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス: [必須入力]

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/54654579
※ブログオーナーが承認したトラックバックのみ表示されます。
※言及リンクのないトラックバックは受信されません。

この記事へのトラックバック