You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Windows系统下用Go语言实现底层原始磁盘读写?

Accessing Raw Disk Drives on Windows with Go (Read/Write Sectors like MBR)

Got it, let's break down how to work with raw disks on Windows using Go—since you're familiar with the Linux approach and need to replicate functionality similar to that C# MSDN example and the C CreateFile snippet you shared.

Core Concepts

Windows exposes raw physical drives via special device paths:

  • For the first physical disk: \\.\PhysicalDrive0
  • Replace the number to target other disks (e.g., PhysicalDrive1 for the second disk)

To interact with these, we'll use Go's syscall package to invoke Windows API functions directly—just like the C code does. The key APIs we'll use are:

  • CreateFileW (to get a handle to the disk)
  • SetFilePointerEx (to seek to specific sectors)
  • ReadFile/WriteFile (to read/write raw sector data)

Full Code Example

Here's a complete implementation that can read the MBR (sector 0, 512 bytes) and write to a specified sector. Warning: Writing to raw disks can destroy data—test on a non-production disk only!

package main

import (
	"fmt"
	"syscall"
	"unsafe"
)

const (
	GENERIC_READ        = 0x80000000
	GENERIC_WRITE       = 0x40000000
	FILE_SHARE_READ     = 0x00000001
	FILE_SHARE_WRITE    = 0x00000002
	OPEN_EXISTING       = 3
	INVALID_HANDLE_VALUE = ^syscall.Handle(0)
)

// ReadSector reads a single sector from the specified physical disk
func ReadSector(diskNumber int, sectorIndex int64, sectorSize uint32) ([]byte, error) {
	// Build the device path: \\.\PhysicalDriveN
	devicePath := fmt.Sprintf("\\\\.\\PhysicalDrive%d", diskNumber)
	pathPtr, err := syscall.UTF16PtrFromString(devicePath)
	if err != nil {
		return nil, fmt.Errorf("failed to convert path: %w", err)
	}

	// Open the disk handle (requires admin rights)
	handle, err := syscall.CreateFileW(
		pathPtr,
		GENERIC_READ,
		FILE_SHARE_READ|FILE_SHARE_WRITE,
		nil,
		OPEN_EXISTING,
		0,
		0,
	)
	if err != nil || handle == INVALID_HANDLE_VALUE {
		return nil, fmt.Errorf("failed to open disk: %w", err)
	}
	defer syscall.CloseHandle(handle)

	// Seek to the target sector: offset = sectorIndex * sectorSize
	var offset int64 = sectorIndex * int64(sectorSize)
	var newOffset int64
	err = syscall.SetFilePointerEx(handle, offset, &newOffset, 0)
	if err != nil {
		return nil, fmt.Errorf("failed to seek to sector: %w", err)
	}

	// Read the sector data
	data := make([]byte, sectorSize)
	var bytesRead uint32
	err = syscall.ReadFile(handle, data, &bytesRead, nil)
	if err != nil || bytesRead != sectorSize {
		return nil, fmt.Errorf("failed to read sector: %w, bytes read: %d", err, bytesRead)
	}

	return data, nil
}

// WriteSector writes data to a specified sector on the physical disk
func WriteSector(diskNumber int, sectorIndex int64, data []byte) error {
	sectorSize := uint32(len(data))
	// Validate sector size (must be a valid sector size for the disk)
	if sectorSize != 512 && sectorSize != 4096 {
		return fmt.Errorf("invalid sector size: %d (expected 512 or 4096)", sectorSize)
	}

	devicePath := fmt.Sprintf("\\\\.\\PhysicalDrive%d", diskNumber)
	pathPtr, err := syscall.UTF16PtrFromString(devicePath)
	if err != nil {
		return fmt.Errorf("failed to convert path: %w", err)
	}

	// Open disk with write access
	handle, err := syscall.CreateFileW(
		pathPtr,
		GENERIC_READ|GENERIC_WRITE,
		FILE_SHARE_READ|FILE_SHARE_WRITE,
		nil,
		OPEN_EXISTING,
		0,
		0,
	)
	if err != nil || handle == INVALID_HANDLE_VALUE {
		return fmt.Errorf("failed to open disk for writing: %w", err)
	}
	defer syscall.CloseHandle(handle)

	// Seek to target sector
	var offset int64 = sectorIndex * int64(sectorSize)
	var newOffset int64
	err = syscall.SetFilePointerEx(handle, offset, &newOffset, 0)
	if err != nil {
		return fmt.Errorf("failed to seek to sector: %w", err)
	}

	// Write data
	var bytesWritten uint32
	err = syscall.WriteFile(handle, data, &bytesWritten, nil)
	if err != nil || bytesWritten != sectorSize {
		return fmt.Errorf("failed to write sector: %w, bytes written: %d", err, bytesWritten)
	}

	return nil
}

func main() {
	// Example: Read MBR (sector 0, 512 bytes) from PhysicalDrive0
	mbrData, err := ReadSector(0, 0, 512)
	if err != nil {
		fmt.Printf("Error reading MBR: %v\n", err)
		return
	}
	fmt.Println("MBR first 16 bytes:", mbrData[:16])

	// Example: Write to a test sector (UNCOMMENT AT YOUR OWN RISK!)
	// testData := make([]byte, 512)
	// copy(testData, []byte("TEST_SECTOR_DATA"))
	// err = WriteSector(1, 0, testData) // Use a non-system disk!
	// if err != nil {
	// 	fmt.Printf("Error writing sector: %v\n", err)
	// 	return
	// }
	// fmt.Println("Sector written successfully")
}

Key Notes & Warnings

  • Admin Rights Required: Your Go program must be run as Administrator. Without this, CreateFileW will return an invalid handle with an access denied error.
  • Sector Size Awareness: While 512 bytes is standard, many modern disks use 4096-byte sectors. To get the exact sector size of your disk, you can call DeviceIoControl with the IOCTL_DISK_GET_DRIVE_GEOMETRY control code (adds a bit more complexity, but worth it for robustness).
  • Data Loss Risk: Writing to raw disks (especially sector 0, the MBR) will corrupt your operating system or data if done on a live disk. Always test on a spare, empty disk.
  • API Error Handling: The example includes basic error handling, but you can expand it to check for specific Windows error codes using syscall.GetLastError().

Mapping to Your C Code

The Go syscall.CreateFileW call directly mirrors your C code:

  • \\\\.\\PhysicalDrive0 → Same device path (converted to UTF-16 for Windows API)
  • GENERIC_ALL → We split into GENERIC_READ|GENERIC_WRITE for explicit access
  • FILE_SHARE_READ | FILE_SHARE_WRITE → Same sharing mode
  • OPEN_EXISTING → Same creation disposition

内容的提问来源于stack exchange,提问作者legonporto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:22:02