Bootmgrの学習。 パート1-Windowsの読み込みの初期段階をデバッグするためのツールと基本原則

はじめに


おそらく、一部の読者は、VHDイメージからWindowsをブートすることに専念したリソースに関する私の最初の記事を思い出すでしょう。 おそらく、自宅のコンピューターでこの技術を繰り返し使用しようとした人がいなかったら、このトピックには戻らないでしょう。 当然、このソリューションの実装では、主にbootmgrが何も気に入らない場合に吐き出すエラーに関する問題が発生しました。 グーグルで0xc03a0003のような読み込みエラーを解釈しようとしても、特に価値のある結果は得られず、この件に関するMicrosoftのドキュメントは意味のある沈黙のままです。 直接情報を受け取った、つまりローダー自体からVHD画像を処理するプロセスを研究するというアイデアがありました。


ネットワーク上ですでに利用可能な情報に目を向けると、素晴らしいブログ「Windowsに関するEnikeyschikのメモ」があり、そのページ( 1、2、3 )は、私の意見では、bootmgrデバイスに関する最も価値のある情報です。 著者は、MBRおよびPBRコードの調査、bootmbr構造に焦点を合わせ、その操作中に発生するプロセスを簡単に説明するなど、ブートプロセスを詳細に調査しました。


さらに先に進みます-ブートローダーデバイスの研究に使用できるツールについて説明し、興味のあるアルゴリズムのいくつかに対処してみます。 そのような申し出が誰かにとって興味深いと思われる場合、あなたは猫の下で歓迎されています


1.システムからBootmgrコードを取得します


Bootmgrブートローダーは、Windows Vista以降、Windowsオペレーティングシステムに導入されています。 開発の理由は、NTラインで使用されている古き良きntldrが、UEFIを搭載したマザーボードを搭載したコンピューターでシステムを起動できなかったという事実でした(当時)(2005年)。


デフォルトでは、標準インストールでは、このブートローダーはHDDの先頭にある別のパーティションに配置され、bootmgr自体とその構成ファイルを収容するのに十分なサイズになっています。 このセクションは、システムの通常モードではマウントされず、ドライブ文字は割り当てられません。 MBRシステムでは、事前にパーティション化されフォーマットされたHDDにWindowsをインストールすることにより、このパーティションの作成を回避できます。 この場合、ブートローダーはOSファイルと同じセクションに配置されます。 EFI + GPTを搭載したシステムでは、最初にタイプ0xefのパーティションが必要で、FATでフォーマットされています。


したがって、最初のタスクはbootmgrを取得することです。 テスト対象として機能するシステムから取得することをお勧めします。 これを行うには、仮想マシンにWindowsをインストールします。 VirtualBox、VMware、QEMUのいずれでも構いません。すべては、使用している仮想化ツールに依存します。 私は主にLinuxで働いており、主にそこで使用されるツールに焦点を合わせますが、Windowsにも注意を払います。


したがって、Windows 7(x86)がインストールされた仮想マシン(VM)があるとします。 ディスクのパーティション分割はMBRに基づいており、システムは1つのパーティションにインストールされます。 これがQEMUであるとします。テストがインストールされているディスクの形式はrawです。 つまり、通常のバイナリイメージです。 この画像をマウント


$ sudo modprobe -r loop $ sudo modprobe loop max_part=15 $ sudo losetup -f win7.hdd $ sudo mount /dev/loop0p1 ~/virt-win $ ls -l ~/virt-win 

マウントされたセクションには、次の内容が表示されます


  5504541 -rwxrwxrwx 1 root root 24  11 2009 autoexec.bat drwxrwxrwx 1 root root 4096  21 09:08 Boot -rwxrwxrwx 1 root root 391640  21 2015 bootmgr -rwxrwxrwx 1 root root 8192  21 09:08 BOOTSECT.BAK -rwxrwxrwx 1 root root 10  11 2009 config.sys lrwxrwxrwx 2 root root 60  14 2009 'Documents and Settings' -> /home/maisvendoo/virt-win/Users -rwxrwxrwx 1 root root 2415517696  21 09:26 hiberfil.sys -rwxrwxrwx 1 root root 3220692992  21 09:26 pagefile.sys drwxrwxrwx 1 root root 0  14 2009 PerfLogs drwxrwxrwx 1 root root 4096  21 09:14 ProgramData drwxrwxrwx 1 root root 4096  12 2011 'Program Files' drwxrwxrwx 1 root root 0  21 09:14 Recovery drwxrwxrwx 1 root root 0  21 09:14 '$Recycle.Bin' drwxrwxrwx 1 root root 4096  21 09:09 'System Volume Information' drwxrwxrwx 1 root root 4096  21 09:14 Users drwxrwxrwx 1 root root 16384  21 09:09 Windows 

bootmgrファイルは興味の対象です。 ただし、以前はまったく必要ありませんでしたが、32ビットのbootmgr.exeブートローダーイメージはパッケージ形式でbootmgrにあります。 解凍するには、Windows向けに一般的に書かれているbmzipユーティリティを使用する必要があります(Linuxからゼロからアセンブルすることはできませんでした)。したがって、仮想マシンで解凍します。 通常は機能するこのユーティリティのバイナリアセンブリは、リンクにもかかわらず、見つけるのがかなり難しいことがわかりました。 その結果、パッケージはbootmgrのカスタマイズ専用サイトの1つで見つかりました。 bmzipが機能するには、MSCompression.dllライブラリが必要でした。 すぐに使用できるパッケージをここからダウンロードできます


VMディスクにutilsフォルダーを作成し、MSCompression.dllとともにbmzip.exeをそこにコピーします。 イメージをアンマウントし、VMを起動します。 管理者としてコマンドラインを実行します。 誤ってブートローダーを台無しにしないために、それをコピーしてください


 C:\ Windows\System32>cd c:\ C:\ xcopy bootmgr utils\bootmgr /h 

ブートローダーファイルは非表示で体系的であるため、これらの属性を削除します。


 C:\ cd utils C:\ attrib -S -H /s 

ブートローダーを開梱します


 C:\ bmzip bootmgr bootmgr.exe 

その結果、解凍されたbootmgr.exeイメージを取得します


画像


VMの電源を切り、そのディスクをLinuxに再度マウントします。 ローダーを逆アセンブラーで切断し、展開されたイメージをそこにコピーするフォルダーを作成しましょう


 $ mkdir -p ~/work/bootmgr/ $ cp ~/virt-win/utils/bootmgr.exe ~/work/bootmgr/ 

2. bootmgr.exeを逆アセンブルします


次に、結果の実行可能ファイルを逆アセンブラにフィードします。 たとえば、IDA Pro。 「go」を実行し、その中に抽出されたファイルを開きます。


画像


IDAは、ファイルを32ビットPE形式の実行可能ファイルとして正しく識別します。 OKをクリックします。 これで、pdb-filesを操作するためのプラグインがIDA Proにインストールされている場合、どこからでもMicrosoftサイトから、分解中にデバッグシンボルをダウンロードできます。


画像


私たちは同意し、そのような写真を得る


画像


ええ、左側には、デバッグシンボルの読み込みに同意したという事実のため、調査中のファイルに含まれる関数のプロトタイプがあります。 これにより、その後の作業が大幅に容易になります。 それまでの間、ローダーコードへのエントリポイントを決定しますが、これがBmMain()関数になると推測するのは簡単です。 ただし、当然のことではなく、Ctrl + Eを押します


画像


推測が正しいことを確認します-BmMain()は0x401000にあるエントリポイントです。 [OK]をクリックして、コードの先頭に移動します


画像


BmMain()関数のヘッダーには、ローカル変数の印象的なリストがあり、関数コード自体のすぐ下にあります


画像


アセンブラーコードのハッシュを理解することは非常に困難です。 まず、学習したいブートローダー機能を決定します。 VHDについて何か言いましたか? さて、仮想ディスクに関連するコードを探してみましょう。 左側の関数のリストを右クリックして、ポップアップコンテキストメニューで[クイックフィルター]を選択します(またはプロトタイプウィンドウに移動してCtrl + Fを押します)。 検索バーに「vhd」と入力して...


画像


はい、そのような機能は33個の量で利用可能です。 このうち、 VhdOpen()は明らかに仮想ディスクを開く役割を果たしますが、たとえば、 VhdiVerifyVhdFooter()の飲み方は、VHDディスクのフッターの正確さを確認する役割を果たします。 つまり、デバッガでブレークポイントを設定する場所を大まかに想像します。 ところで、デバッグについて話す時間です


3. QEMU + IDA ProバンドルでのBootmgrのデバッグ


-s -Sスイッチを使用して仮想マシンを起動します-これにより、デバッグモードが有効になります


 $ qemu-system-x86_64 ~/VM/qemu/win7-efi/win-x86.hdd -m 4096 -s -S 

VMが起動してすぐに一時停止し、デバッガーの接続を待機します


重要! ハードウェア仮想化を使用して-enable-kvmスイッチを使用しないでください。 これを使用すると、QEMUでのデバッグは機能しません。


IDAのツールバーで、「リモートGDBデバッガー」を選択します


画像


いくつかの質問に「はい」と答えると、ウィンドウが表示されます


画像


接続パラメーターをVMに接続します:ポート1234のlocalhost。[OK]をクリックします。 いくつかのプロセスが既に開始されており、デバッガーが接続するのを待っていることが通知されます-参加しますか? もちろん行きます!


画像


したがって、「はい」と答えると...


画像


仮想マシンのBIOSの最初のどこかで一時停止します。 すばらしいですが、今度はbootmgrを開始する場所に到達する必要があります。 BmMain()関数にブレークポイントを設定します。 ツールバーのブレークポイントのリストをクリックし、キーボードの[挿入]をクリックして、コードの実行を中断してデバッグに入るアドレスを指定します。


画像


アドレス0x401000を駆動します。 必要な関数にブレーカーを設定する場合は、メインメニューに移動し、デバッグセッションで関数のリストを開きます:[表示]-> [サブビューを開く]-> [関数]。 表示されるリストで、コンテキストメニューを右クリックし、[ブレークポイントの追加]を選択します。 ここでF9を押すと、少し待ってからブートローダーコードの最初に到達します。


画像


これで、コードを段階的に確認し、レジスタとスタックの値を確認し、呼び出しスタックを追跡することができます。 ある程度まで、IDAに組み込まれたデバッガーは便利で直感的です。


おそらく彼らは私に尋ねるでしょう-GDBを使用することは可能ですか? デバッグモードでVMを実行し、コンソールでgdbを実行できます


 $ gdb -q 

リモートVMセッションに接続する


 (gdb) target remote localhost:1234 

逆アセンブルされた命令の表示をオンにします


 (gdb) display/4i $pc 

AT&T構文に慣れていない場合は、Intelに切り替えてください


 (gdb) set disassembly-flavor intel 

BmMain()にブレークポイントを設定し、実行を開始します


 (gdb) b *0x401000 Breakpoint 1 at 0x401000 (gdb) c Continuing. Breakpoint 1, 0x00401000 in ?? () 1: x/4i $pc => 0x401000: mov edi,edi 0x401002: push ebp 0x401003: mov ebp,esp 0x401005: and esp,0xfffffff8 (gdb) 

GDBのすべての機能を備えながら、IDAで見たものとほぼ同じものをご覧ください。 ほとんど、ここではMicrosoftのデバッグシンボルを使用できません。GDBがそれらを理解していないためです。 しかし、GDBの機能は、デバッグプロセスとその自動化に関して、IDAの機能よりも決して広いわけではありません。


ただし、無視できない別のデバッグ機能があります。


3. WinDbg + VirtualBoxを使用したデバッグ


Windows用のドライバーを開発している人は、この素晴らしいデバッガーに確かに精通しています。 Linux GDBの機能に匹敵する機能を備えているという点で注目に値します。 唯一の欠点は、インターフェースをカスタマイズするひどい方法です。 ただし、これらの点を省略し、解決するタスクのこのデバッガーの機能を使用します。


それでは、VirtualBoxに基づいたVMを用意しましょう。 次のパラメーターを使用して、このVMのCOMポートを作成します


画像


これは、名前付きパイプに転送される仮想COMポートです。 シリアルポート経由でデバッグするには、それに応じて仮想マシンを構成する必要があります。 それをダウンロードし、管理者権限でコンソールを実行します。 それを使用して、デバッグ用のブートローダー設定コマンドを入力します


 c:\ Windows\system32> bcdedit /bootdebug {bootmgr} on 

このコマンドは、ブートローダーのデバッグを有効にします。 次に、デバッグ用にポートを構成します。


 c:\ Windows\system32> bcdedit /dbgsettings serial debugport:1 baudrate:115200 

COM1を115200ボーで使用していることを示しています。 素晴らしい、VMをオフにしてデバッガーを実行します。


WinDbgデバッガーは、ドライバー開発キットと一緒にMicrosoft Webサイトから公式にダウンロードできます。 ただし、このデバッガーアセンブリには問題があります。レジスタ値の表示に不具合があります。 したがって、特定のDominic Wongのトゥイーターからリンクされている同じRedmodovサイトからダウンロードするアセンブリを使用します。 このビルドにはこのバグがありません。 次のコマンドでWinDbgを起動します


 c:\Wingdbx86> windbg -b -k com:pipe,port=\\.\pipe\com1,resets=0,reconnect 

他のパラメーターの中でも特に、Microsoftサーバーからデバッグシンボルをロードするためのパスhttp://msdl.microsoft.com/download/symbolsが保存されているインターフェイス設定([ファイル]-> [ファイルでワークスペースを開く])を開きます。 このパスを設定に事前に組み込み(ファイル->シンボルファイルパス)、WinDbgのテーマに保存します。 この設定により、ブートローダーのデバッグ情報を自動的に受信できます。


次に、VMを実行します。 すぐに一時停止し、デバッガーウィンドウに次の画像が表示されます。


画像


ええ、デバッガーはVMに接続し、マイクロソフトから親切に提供されたポイントに立っていました。 さて、これでwindbgを使用してすべてのデバッグオプションを使用できます。


ただし、ブートローダーコードの最初で停止するのではなく、もう少し先に停止します。 段階的なデバッグが示すように、初期機器の初期化を提供するBlInitializeLibrary()関数のすぐ後ろにいます。


画像


また、IDAを使用してデバッグする場合、ここには到達しません。 したがって、WinDbgを使用してデバッグする場合、bootmgrアクションの一部は開始直後に回避されます。 これは、Microsoftが提供する標準のデバッグツールを使用することの欠点です。 ただし、アクセスできないコードは、IDAを使用していつでも個別に調べることができます。


次に、例として、bootmgrが固定サイズのVHDイメージでどのように機能するかを見てみましょう。


4. VHDからのブートのデバッグ


以下はすべて、VirtualBox上のVMに接続されたWinDbgデバッガーで説明されていますが、機能を考慮して、他のデバッグ方法にも同様に当てはまります。 この例で使用するVMには2つのシステムが含まれています。1つはHDDにインストールされ、もう1つはVHDイメージにインストールされます。 VhdOpen()関数にブレークポイントを置く


 kd> bp VhdOpen 

F5を押します。 デバッガーは指定された関数の上に立つ


画像


さらに、注意-まだブートメニューに移動せず、VHDからのブートを選択しませんでした。 そしてこれは、メニューが表示されるずっと前にVHDチェックが行われることを意味します。 たとえば、bootmgrを空のVHDにスリップした場合など、同じ動作を観察します。 ブートメニューはまったく表示されませんが、コード0xc000000Fのエラーが表示されます。


F10を押すか、コマンドラインにpを入力してさらに進んで、 VhdiAllocateVhdData()を呼び出します -明らかに、これはイメージを操作するためのメモリ内にいくつかの構造を作成しています


画像


もう少し低いのは、 VhdiVerifyAndInitializeVhd()の呼び出しです-画像の正確さを明らかに確認します。 私には面白そうだったので、中に入りました(F11)


画像


以下では、いくつかの準備操作の後、ブートローダーはイメージの最後の512バイトを読み取ります。これには、イメージのいわゆる「フッター」が含まれ、 VhdiReadVhdInformation()関数を呼び出します。 占い師に行く必要はありません-関数はフッターデータを含む構造体へのポインターを返します。 私が見つけたように、 VhdiReadVhdInformation()を呼び出した後、このポインターはecxレジスターにあります。 その値は0x110098です。 そのアドレスのメモリを見てみましょう


 kd> db 0x110098 

このコマンドは、指定されたアドレスのメモリを読み取り、デバッガウィンドウに一連のバイトとして表示します。


 00110098 63 6f 6e 65 63 74 69 78-00 00 00 02 00 00 01 00 conectix........ 001100a8 ff ff ff ff ff ff ff ff-70 5e d3 1e 77 69 6e 20 ........p^..win 001100b8 00 06 00 01 57 69 32 6b-00 00 00 40 06 00 00 00 ....Wi2k...@.... 001100c8 00 00 00 40 06 00 00 00-cb 2c 10 3f 02 00 00 00 ...@.....,.?.... 001100d8 83 e6 ff ff 75 11 0a 5a-eb 03 c6 43 b9 c9 d6 df ....u..Z...C.... 001100e8 24 b6 76 57 00 00 00 00-00 00 00 00 00 00 00 00 $.vW............ 001100f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 00110108 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 

ええ、よく知られている単語conectixがあります。 このフィールドはVHDイメージフッターの前にあり、Cookieと呼ばれ、MicrosoftがConectixからVHDテクノロジーを購入し、古いMacintoshコンピューター用にこのフォーマットの仮想ディスクを開発したメモリを保存します。作成された(Wi2k)勝利シーケンスは、VHDがWindowsによって作成されたことも示します。 はい、そうでした。 さらに進んで、フッターの形式をチェックするVhdiVerifyVhdFooter()の呼び出しに遭遇します。 パラメーターとして、何らかの理由で、esiレジスタ(???)を介して上記の構造体へのポインターを受け取ります。


画像


このコードは私に最も興味を持っていたので、どこかでIDA Proを使用し、どこかで手を使って、Cの擬似コードに変換しました。


 signed int __usercall VhdiVerifyVhdFooter(int footer) { signed int error_code; // Error code int cur_checksum; // Actual checksum, writed in VHD int calc_checksum; // Calculated checksum int disk_type; // Disk type int creator_host_os; // Creator host OS // Error code error_code = -1069940733; // 0xc03a0003 // Check cookie if ( RtlCompareMemory((const void *)footer, "conectix", 8) == 8 ) { // Store actual checksumm cur_checksum = *(_DWORD *)(footer + 64); // Write zero to checksum in footer structure *(_DWORD *)(footer + 64) = 0; // Calculate check summ calc_checksum = BlUtlCheckSum(0x40001, 0, footer, 0x200); // Restore checsum in footer *(_DWORD *)(footer + 64) = cur_checksum; // Checksum verify if ( calc_checksum == cur_checksum ) { // File type verify if ( *(_WORD *)(footer + 14) == 1 ) { // Check disk type disk_type = *(_DWORD *)(footer + 60); if ( disk_type == 2 || disk_type == 3 || disk_type == 4 ) { // Check creator host OS creator_host_os = *(_DWORD *)(footer + 36); if ( creator_host_os != 1798465879 && creator_host_os ) { error_code = -1073741637; // 0xc00000bb } // Check disk size (by integer sectors count) else if ( *(_DWORD *)(footer + 48) & 0x1FF || *(_DWORD *)(footer + 40) & 0x1FF ) { error_code = -1069940718; // 0xc03a0012 } else { error_code = 0; } } else { error_code = -1069940732; // 0xc03a0004 } } else { error_code = -1069940731; // 0xc03a0005 } } else { error_code = -1069940734; // 0xc03a0002 } } return error_code; } 

VHDフッターは次の構造として表すことができます(コメントは先頭からのオフセットを示します)。


 //----------------------------------------------------------------------------- // VHD foother's data //----------------------------------------------------------------------------- struct vhd_footer_t { char cookie[8]; // +0 uint32_t features; // +8 uint32_t file_format_version; // +12 uint64_t data_offset; // +16 uint32_t time_stamp; // +24 char creator_application[4]; // +28 uint32_t creator_version; // +32 char creator_host_os[4]; // +36 uint64_t original_size; // +40 uint64_t current_size; // +48 vhd_disk_geometry_t disk_geometry; // +56 uint32_t disk_type; // +60 uint32_t checksum; // +64 vhd_uuid_t unique_id; // +68 uint8_t saved_state; // +84 uint8_t reserved[427]; }; 

このデータを使用して、どのbootmgrフィールドがどのフッターフィールドとどのエラーをスローするかを確認できます。 正しいVHDイメージでは、この関数はゼロを返します。他の場合、アライメントは次のようになります


 0xc03a0003 -  cookie 0xc03a0002 -     0xc03a0005 -     0xc03a0004 -     0xc00000bb -      Windows 0xc0300012 -     512 (   VHD) 

私が受け取った情報は、VHDからWindowsを起動する方法を議論したフォーラムで同僚と生じた論争を解決しました。 VirtualBoxによって作成されたイメージがbootmgrを使用して起動しないことを考慮して、私はそれを失いました。 このような画像を作成するVirtualBoxは、Microsoftの仕様に従ってすべてのフィールドを書き込みます。ただし、creator_applicationフィールドには、virtualboxの場合は元の画像のwinとvboxが含まれます。 しかし、このフィールドはbootmgrによってチェックされないため、ディスクは機能しますが、まったく異なる理由で機能しませんでした。


おわりに


おそらくこの記事はやや混乱しています。 しかし、彼女はポットが神によって焼き付けられていないと言いますが、低レベルのWindowsコードのデバッグは技術の問題です。 あなたが興味を持っている情報は、これにあなたの頭と手をつなぐことによって常に得られます。 このテキストでは、bootmgrのデバッグの問題に関してネットワーク上で収集したさまざまな情報を要約しようとしました。 私は成功したことを願っています、すべての読者の注意に感謝します...


続けられる!



Source: https://habr.com/ru/post/J301334/


All Articles