387 lines
13 KiB
C++
387 lines
13 KiB
C++
|
////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// utility to read/write bootsectors
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
|
||
|
#define UNICODE
|
||
|
#define _UNICODE
|
||
|
#define _WIN32_WINNT 0x500
|
||
|
|
||
|
#include <winlean.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
// do a raw read from pwzDisk to pbBuffer of dwBytes bytes, starting at dwOffset
|
||
|
int ReadRaw(PCWSTR pwzDisk, PBYTE pbBuffer, DWORD dwOffset, DWORD dwBytes)
|
||
|
{
|
||
|
DWORD cbRead;
|
||
|
LARGE_INTEGER liPos;
|
||
|
HANDLE hDisk = CreateFile(pwzDisk, // drive to open
|
||
|
GENERIC_READ, // no access to the drive
|
||
|
FILE_SHARE_READ | // share mode
|
||
|
FILE_SHARE_WRITE,
|
||
|
NULL, // default security attributes
|
||
|
OPEN_EXISTING, // disposition
|
||
|
FILE_FLAG_NO_BUFFERING, // attributes
|
||
|
NULL); // do not copy file attributes
|
||
|
|
||
|
if (hDisk == INVALID_HANDLE_VALUE) // cannot open the drive
|
||
|
{
|
||
|
printf("CreateFile failed: %d\n", GetLastError());
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
liPos.QuadPart = dwOffset;
|
||
|
SetFilePointerEx(hDisk, liPos, NULL, FILE_BEGIN);
|
||
|
|
||
|
if (!ReadFile(hDisk, pbBuffer, dwBytes, &cbRead, NULL)) {
|
||
|
printf("ReadFile failed: %d\n", GetLastError());
|
||
|
return 1;
|
||
|
}
|
||
|
CloseHandle(hDisk);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// print a buffer
|
||
|
void DisplayBuffer(PBYTE pbBuffer, DWORD dwOffset, DWORD dwBytes)
|
||
|
{
|
||
|
static const int MaxLineWidth = 16;
|
||
|
|
||
|
for (DWORD i = 0; i < dwBytes; i += MaxLineWidth){
|
||
|
printf("%08x: ", dwOffset + i);
|
||
|
|
||
|
DWORD dwLineWidth = dwBytes - i;
|
||
|
if (dwLineWidth > MaxLineWidth) {
|
||
|
dwLineWidth = MaxLineWidth;
|
||
|
}
|
||
|
|
||
|
DWORD j;
|
||
|
for (j = 0; j < dwLineWidth; j++) {
|
||
|
printf("%02x ", pbBuffer[dwOffset + i + j]);
|
||
|
}
|
||
|
for (; j < MaxLineWidth + 1; j++) {
|
||
|
printf(" ");
|
||
|
}
|
||
|
for (j = 0; j < dwLineWidth; j++) {
|
||
|
BYTE c = pbBuffer[dwOffset + i + j];
|
||
|
if (c < 31 || c > 126) {
|
||
|
c = '.';
|
||
|
}
|
||
|
printf("%c", c);
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Write a file - this forces create, and is thus inappropriate for direct disk access
|
||
|
int WriteRawCreate(PCWSTR pwzFile, PBYTE pbBuffer, DWORD dwOffset, DWORD dwBytes)
|
||
|
{
|
||
|
DWORD cbRead;
|
||
|
|
||
|
HANDLE hFile = CreateFile(pwzFile, // drive to open
|
||
|
GENERIC_WRITE | GENERIC_READ,
|
||
|
NULL,
|
||
|
NULL, // default security attributes
|
||
|
CREATE_ALWAYS, // disposition
|
||
|
FILE_FLAG_NO_BUFFERING, // attributes
|
||
|
NULL); // do not copy file attributes
|
||
|
|
||
|
if (hFile == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
printf("CreateFile failed: %d\n", GetLastError());
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (!WriteFile(hFile, pbBuffer, dwBytes, &cbRead, NULL)) {
|
||
|
printf("WriteFile failed: %d\n", GetLastError());
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// exact same as above, except with OPEN_EXISTING, so that it is appropriate
|
||
|
// for writing a bootsector direct to a disk
|
||
|
int WriteRaw(PCWSTR pwzFile, PBYTE pbBuffer, DWORD dwOffset, DWORD dwBytes)
|
||
|
{
|
||
|
DWORD cbRead;
|
||
|
|
||
|
HANDLE hFile = CreateFile(pwzFile, // drive to open
|
||
|
GENERIC_WRITE | GENERIC_READ,
|
||
|
NULL,
|
||
|
NULL, // default security attributes
|
||
|
OPEN_EXISTING, // disposition
|
||
|
FILE_FLAG_NO_BUFFERING, // attributes
|
||
|
NULL); // do not copy file attributes
|
||
|
|
||
|
if (hFile == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
printf("CreateFile failed: %d\n", GetLastError());
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (!WriteFile(hFile, pbBuffer, dwBytes, &cbRead, NULL)) {
|
||
|
printf("WriteFile failed: %d\n", GetLastError());
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static inline bool IsPowerOfTwo(DWORD dwValue)
|
||
|
{
|
||
|
return ((dwValue - 1) & dwValue) == 0;
|
||
|
}
|
||
|
|
||
|
static inline WORD GetLE16(PBYTE pbBuffer, DWORD dwOffset)
|
||
|
{
|
||
|
return (WORD)pbBuffer[dwOffset] + (((WORD)pbBuffer[dwOffset + 1]) << 8);
|
||
|
}
|
||
|
|
||
|
static inline DWORD GetLE32(PBYTE pbBuffer, DWORD dwOffset)
|
||
|
{
|
||
|
return ((DWORD)pbBuffer[dwOffset] +
|
||
|
(((DWORD)pbBuffer[dwOffset + 1]) << 8) +
|
||
|
(((DWORD)pbBuffer[dwOffset + 2]) << 16) +
|
||
|
(((DWORD)pbBuffer[dwOffset + 3]) << 24));
|
||
|
}
|
||
|
|
||
|
static int GetFatVersion(PBYTE pbBuffer, DWORD dwBytes)
|
||
|
{
|
||
|
if (!IsPowerOfTwo(dwBytes) || dwBytes < 512 ||
|
||
|
GetLE16(pbBuffer, 510) != 0xaa55) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// These strings mean *nothing* with regard to actual FAT
|
||
|
// type - they are just valid signatures.
|
||
|
if (strncmp((const char*)(pbBuffer + 52), "FAT ", 8) &&
|
||
|
strncmp((const char*)(pbBuffer + 52), "FAT12 ", 8) &&
|
||
|
strncmp((const char*)(pbBuffer + 52), "FAT16 ", 8) &&
|
||
|
strncmp((const char*)(pbBuffer + 82), "FAT32 ", 8)) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Calculate number of clusters to determine FAT type per
|
||
|
// page 14 of FAT spec.
|
||
|
DWORD dwBytesPerSector = GetLE16(pbBuffer, 11);
|
||
|
DWORD dwSectorsPerCluster = (DWORD)pbBuffer[13];
|
||
|
DWORD dwReservedSectorCount = GetLE16(pbBuffer, 14);
|
||
|
DWORD dwNumberOfFats = (DWORD)pbBuffer[16];
|
||
|
DWORD dwRootEntryCount = GetLE16(pbBuffer, 17);
|
||
|
|
||
|
DWORD dwRootDirSectors =
|
||
|
((dwRootEntryCount * 32) + (dwBytesPerSector - 1)) / dwBytesPerSector;
|
||
|
|
||
|
DWORD dwFatSize = 0;
|
||
|
DWORD dwTotalSectors = 0;
|
||
|
|
||
|
DWORD dwFatSize16 = GetLE16(pbBuffer, 22);
|
||
|
if (dwFatSize16 == 0) {
|
||
|
dwFatSize = GetLE32(pbBuffer, 36);
|
||
|
}
|
||
|
else {
|
||
|
dwFatSize = dwFatSize16;
|
||
|
}
|
||
|
|
||
|
DWORD dwTotalSectors16 = GetLE16(pbBuffer, 19);
|
||
|
if (dwTotalSectors16 == 0) {
|
||
|
dwTotalSectors = GetLE32(pbBuffer, 32);
|
||
|
}
|
||
|
else {
|
||
|
dwTotalSectors = dwTotalSectors16;
|
||
|
}
|
||
|
|
||
|
DWORD dwDataSectors = (dwTotalSectors -
|
||
|
(dwReservedSectorCount +
|
||
|
dwNumberOfFats * dwFatSize +
|
||
|
dwRootDirSectors)
|
||
|
);
|
||
|
|
||
|
DWORD dwCountOfClusters = dwDataSectors / dwSectorsPerCluster;
|
||
|
|
||
|
if (dwCountOfClusters < 4085) {
|
||
|
return 12;
|
||
|
}
|
||
|
else if (dwCountOfClusters < 65525) {
|
||
|
return 16;
|
||
|
}
|
||
|
else {
|
||
|
return 32;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this copies the BPB from the oldbuffer to the newbuffer
|
||
|
// so we don't trash the BPB when we write the new buffer
|
||
|
BOOL MergeSectors(PBYTE pbOldBuffer,
|
||
|
PBYTE pbBuffer,
|
||
|
DWORD dwBytes,
|
||
|
BOOL fCheckFatSanity)
|
||
|
{
|
||
|
if (fCheckFatSanity) {
|
||
|
DWORD dwOldFat = GetFatVersion(pbOldBuffer, dwBytes);
|
||
|
DWORD dwNewFat = GetFatVersion(pbBuffer, dwBytes);
|
||
|
if (dwOldFat != dwNewFat) {
|
||
|
fprintf(stderr,
|
||
|
"New boot sectors FAT version FAT differs (%d != %d).\n",
|
||
|
dwNewFat, dwOldFat);
|
||
|
// We'll call it a pint then, shall we :-)
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// copy the BPB and BS from the old sector into the new
|
||
|
int preservebytes = pbBuffer[1] + 2;
|
||
|
|
||
|
for (int i = 3; i < preservebytes; i++) {
|
||
|
pbBuffer[i] = pbOldBuffer[i];
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
int __cdecl wmain(int argc, WCHAR **argv)
|
||
|
{
|
||
|
BOOL fNeedHelp = FALSE;
|
||
|
BOOL fRead = FALSE;
|
||
|
BOOL fWrite = FALSE;
|
||
|
BOOL fDisplay = FALSE;
|
||
|
BOOL fNtldr = FALSE;
|
||
|
BOOL fCheckFat = FALSE;
|
||
|
PBYTE pbBuffer;
|
||
|
PBYTE pbOldBuffer;
|
||
|
WCHAR wzPartition[64] = L"";
|
||
|
WCHAR wzFile[64] = L"";
|
||
|
WCHAR wzFile2[64] = L"";
|
||
|
|
||
|
if (argc==1){
|
||
|
fNeedHelp = TRUE;
|
||
|
}
|
||
|
else{
|
||
|
// first check the first param
|
||
|
if (argv[1][0] == '-' || argv[1][0] == '/'){
|
||
|
switch(argv[1][1]){
|
||
|
case 'd':
|
||
|
case 'D':
|
||
|
fDisplay = TRUE;
|
||
|
break;
|
||
|
case 'n':
|
||
|
case 'N':
|
||
|
fNtldr = TRUE;
|
||
|
break;
|
||
|
case 'r':
|
||
|
case 'R':
|
||
|
fRead = TRUE;
|
||
|
break;
|
||
|
case 'w':
|
||
|
case 'W':
|
||
|
fWrite = TRUE;
|
||
|
fCheckFat = TRUE;
|
||
|
break;
|
||
|
case 'x':
|
||
|
case 'X':
|
||
|
fWrite = TRUE;
|
||
|
fCheckFat = FALSE;
|
||
|
break;
|
||
|
default:
|
||
|
fNeedHelp = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
fNeedHelp = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// verify parameter counts
|
||
|
if ((fDisplay && argc !=3) || (fRead && argc !=4) || (fWrite && argc !=4) || (fNtldr && argc !=5)){
|
||
|
fNeedHelp = TRUE;
|
||
|
}
|
||
|
|
||
|
if (fNeedHelp) {
|
||
|
printf(
|
||
|
"Usage:\n"
|
||
|
" grabsector [options] {drive letter:} {file1} {file2}\n"
|
||
|
"Options:\n"
|
||
|
" /d -- Display bootsector from drive.\n"
|
||
|
" /n -- Create NTLDR-compatible bootsector file2 from drive and file1.\n"
|
||
|
" /r -- Read drive's bootsector, write to file1.\n"
|
||
|
" /w -- Write drive's bootsector from file1 to drive.\n"
|
||
|
" /x -- Write drive's bootsector from file1 to drive (no sanity check).\n"
|
||
|
" /? -- Display this help screen.\n"
|
||
|
"Advanced:\n"
|
||
|
" To access the MBR directly, use PHYSICALDRIVE0 as the drive name.\n"
|
||
|
"Examples:\n"
|
||
|
" grabsector /d c:\n"
|
||
|
" grabsector /r c: bootsec.old \n"
|
||
|
" grabsector /w c: bootsec.new \n"
|
||
|
" grabsector /n c: bootsec.new bootsec.NT \n"
|
||
|
" grabsector /d PHYSICALDRIVE0 \n"
|
||
|
);
|
||
|
return 1;
|
||
|
}
|
||
|
else{
|
||
|
pbBuffer = (PBYTE)VirtualAlloc(NULL, 512, MEM_COMMIT, PAGE_READWRITE);
|
||
|
if (pbBuffer == NULL) {
|
||
|
printf("VirtualAlloc failed: %d\n", GetLastError());
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
wsprintf(wzPartition, L"\\\\.\\%ls", argv[2]);
|
||
|
if (fRead || fWrite || fNtldr)
|
||
|
wsprintf(wzFile, L"%ls", argv[3]);
|
||
|
}
|
||
|
|
||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
||
|
printf("Disk: %ls\n", wzPartition);
|
||
|
printf("File: %ls\n", wzFile);
|
||
|
if (fDisplay) {
|
||
|
if(ReadRaw(wzPartition, pbBuffer, 0, 512)==0) {
|
||
|
DisplayBuffer(pbBuffer, 0, 512);
|
||
|
}
|
||
|
}
|
||
|
if (fRead){
|
||
|
if (ReadRaw(wzPartition, pbBuffer, 0, 512)==0)
|
||
|
WriteRawCreate(wzFile, pbBuffer, 0, 512);
|
||
|
}
|
||
|
if (fWrite){
|
||
|
// set up a buffer for the old bootsector
|
||
|
pbOldBuffer = (PBYTE)VirtualAlloc(NULL, 512, MEM_COMMIT, PAGE_READWRITE);
|
||
|
if (pbOldBuffer == NULL) {
|
||
|
printf("VirtualAlloc failed: %d\n", GetLastError());
|
||
|
return 1;
|
||
|
}
|
||
|
// get the old bootsector
|
||
|
if(ReadRaw(wzPartition, pbOldBuffer, 0, 512)!=0)
|
||
|
return -1;
|
||
|
// open the file
|
||
|
if (ReadRaw(wzFile, pbBuffer, 0, 512)!=0)
|
||
|
return -1;
|
||
|
// merge the file with the bootsector
|
||
|
if (MergeSectors(pbOldBuffer, pbBuffer, 512, fCheckFat)) {
|
||
|
WriteRaw(wzPartition, pbBuffer, 0, 512);
|
||
|
}
|
||
|
}
|
||
|
if (fNtldr){
|
||
|
// get the second filename
|
||
|
wsprintf(wzFile2, L"%ls", argv[4]);
|
||
|
// set up a buffer for the old bootsector
|
||
|
pbOldBuffer = (PBYTE)VirtualAlloc(NULL, 512, MEM_COMMIT, PAGE_READWRITE);
|
||
|
if (pbOldBuffer == NULL) {
|
||
|
printf("VirtualAlloc failed: %d\n", GetLastError());
|
||
|
return 1;
|
||
|
}
|
||
|
// get the old bootsector
|
||
|
if(ReadRaw(wzPartition, pbOldBuffer, 0, 512)!=0)
|
||
|
return -1;
|
||
|
// open the input file
|
||
|
if (ReadRaw(wzFile, pbBuffer, 0, 512)!=0)
|
||
|
return -1;
|
||
|
// merge the file with the bootsector
|
||
|
if (MergeSectors(pbOldBuffer, pbBuffer, 512, fCheckFat)) {
|
||
|
WriteRawCreate(wzFile2, pbBuffer, 0, 512);
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|