Sign in to follow this  
Followers 0
NathanD

Problem writing to raw disk sectors.

5 posts in this topic

Before everyone starts getting on me about the dangers and inherent questionable nature of writing to the Master Boot Record sector on a disk, let me explain my necessity.

I am creating an application that will make spare CompactFlash memory cards for Siemens industrial motion controllers. This application is intended to automate this process for our in-house production as well as for our customers who need to make replacement cards on occasion. The problem is, while any off-the-shelf compact flash will work in the device, only the official Siemens cards come with the proper bootloader code pre-installed in the MBR. The motion controller will not boot without the bootloader code in place. Siemens provides the bootloader binary image file to OEMs such as myself, but their tools for putting the binary file on a new CompactFlash are seriously lacking. That is why I am trying to write my own tool.

So far, I have been able to read in the source MBR binary data, and can write that data back to another file. But I keep running into errors when trying to write the binary data to a physical disk location. I am trying to use WinAPI WriteFile methods, so if there is a better method, please share! I have even gone as far as breaking down the _WinAPI_WriteFile UDF into a DLL call to try to get more information on the error, but am not having much luck.

Here is my code:

#include <Array.au3>
#include <WinAPI.au3>

Global $G_objFSO = ObjCreate("Scripting.FileSystemObject")

Global CONST $MBR_SIZE = 0x0200 ;512 bytes total
Global CONST $tag_MBR = _               ;Total of 512 (0x0200) bytes
    'BYTE MasterBootCode[440];' & _     ;0x0000 to 0x01B0 (440 bytes)
    'BYTE DiskSignature[4];' & _        ;0x01B8 to 0x01BB (4 bytes)
    'BYTE Unused[2];' & _               ;0x01BC to 0x01BD (2 bytes)
    'BYTE PartitionEntry1[16];' & _     ;0x01BE to 0x01CD (16 bytes)
    'BYTE PartitionEntry2[16];' & _     ;0x01CE to 0x01DD (16 bytes)
    'BYTE PartitionEntry3[16];' & _     ;0x01DE to 0x01ED (16 bytes)
    'BYTE PartitionEntry4[16];' & _     ;0x01EE to 0x01FD (16 bytes)
    'BYTE BootSignature[2];'            ;0x01FE to 0x01FF (2 bytes)
Global Enum $DISKMBR_ERR_NONE, _
    $DISKMBR_ERR_SOURCE, _
    $DISKMBR_ERR_OPEN, _
    $DISKMBR_ERR_POINTER, _
    $DISKMBR_ERR_STRUCT, _
    $DISKMBR_ERR_READ, _
    $DISKMBR_ERR_WRITE

;Establish source data for new disk's MBR bootloader. (uses UDF defined below)
;$G_refMBR = _DeviceReadMBR('C:\Dev\Scripts\Siemens_CF_Tool\CF Image Data\Siemens Original CF.bin')
$G_refMBR = _DeviceReadMBR(0) ;read in Disk0's MBR as an example.
ConsoleWrite("_DeviceReadMBR: err|ext|retType = " & @error & "|" & @extended & "|" & VarGetType($G_refMBR) & @CRLF)

Select ;easy swap between disk/file targets.
    Case False      ;Use True here to test with physical disk.
    ;test using physical disk as target.
        $iDisk = 6      ;My CompactFlash is Disk6 - change as needed.
        $myTarget = '\\.\PhysicalDrive' & $iDisk 
        $iCreation = 2
    Case Else
    ;test using a file as target.
        $myTarget = 'C:\temp\data.bin' 
        Select
            Case $G_ObjFSO.FileExists($myTarget)
                $iCreation = 2
            Case Else
                $iCreation = 3
        EndSelect
EndSelect
;Open a handle to the target.
$hFile = _WinAPI_CreateFile($myTarget, $iCreation, 4)
If Not $hFile Then
    $extRet = @error
    ConsoleWrite("Unable to access " & $myTarget & " [@error = " & $extRet & "]" & @CRLF)
    ConsoleWrite("WinAPI Last Error = " & _
        _WinAPI_GetLastError() & _
        ": " & _WinAPI_GetLastErrorMessage() & @CRLF)
Else ;Handle to target created successfully 
    _WinAPI_SetFilePointer($hFile, 0, $FILE_BEGIN)
    If @error Then
        $extRet = @error
        ConsoleWrite("Failed to set file pointer for " & $myTarget & " [@error = " & $extRet & "]" & @CRLF)
        ConsoleWrite("WinAPI Last Error = " & _
            _WinAPI_GetLastError() & _
            ": " & _WinAPI_GetLastErrorMessage() & @CRLF)
    Else ;Pointer was set to beginning of target.
        $pBuffer = DLLStructGetPtr($G_refMBR)
        $iWriteLength = 440
        ;Using raw DLLCall instead of _WinAPI_WriteFile() to verify the return data.
        Local $aResult = DllCall("kernel32.dll", _
                            "bool", "WriteFile", _
                            "handle", $hFile, _
                            "ptr", $pBuffer, _
                            "dword", $iWriteLength, _
                            "dword*", 0, _
                            "ptr", 0)
        If @error Then
            ConsoleWrite("WriteFile Error|Extended = " & @error & "|" & @extended & @CRLF)
            ConsoleWrite("WinAPI Last Error = " & _
                _WinAPI_GetLastError() & _
                ": " & _WinAPI_GetLastErrorMessage() & @CRLF)
        Else ;no error from DLLCall
            ConsoleWrite("WriteFile Results: " & @CRLF)
            ConsoleWrite("WinAPI Last Error = " & _
                _WinAPI_GetLastError() & _
                ": " & _WinAPI_GetLastErrorMessage() & @CRLF)
            If IsArray($aResult) Then
                _ArrayDisplay($aResult)
            EndIf
        EndIf ;check results of DLLCall
    EndIf ;Check for errors setting pointer.
    
    ;Make sure to close handle(s) when finished!
    _WinAPI_CloseHandle($hFile)
    
EndIf ;Check for valid handle to target.





; #FUNCTION ....:   _DeviceReadMBR ****************************************
; Date Added ....:  20110712
; Author ........:  Nathan Durnan
; Description ...:  Read the binary Master Boot Record sector for the Disk/Drive/File.
; Syntax ........:  _DeviceReadMBR($mySource)
; Parameters ....:  $mySource - Source specification to read the MBR from.
;               * A numeric value is assumed to be a Physical Disk.
;               * A String value is assumed to be a Logical Drive or File.
; Return values .:  Success - A DLLStructure containing the MBR data.
;           Failure - 0
;           |@error = (1) $DISKMBR_ERR_SOURCE - Source Argument type could not be determined.
;           |@error = (2) $DISKMBR_ERR_OPEN - Failed to open handle to disk.
;           |@error = (3) $DISKMBR_ERR_POINTER - Failed to set file position pointer.
;           |@error = (4) $DISKMBR_ERR_STRUCT - Failed to create MBR Structure object.
;           |@error = (5) $DISKMBR_ERR_READ - Failed to read data from disk.
; *****************************************************************************
Func _DeviceReadMBR($mySource)
    Local $errRet, $extRet, $myRet = 0
    Local $myTarget = -1, $sTargetText
    Local $hFile, $structBuffer, $pBuffer, $iBufferReadSize
    
    Select ;Determine the Source Target type.
        Case IsNumber($mySource)
            $myTarget = '\\.\PhysicalDrive' & $mySource
            $sTargetText = "Disk" & $mySource
        Case IsString($mySource)
            If $G_ObjFSO.FileExists($mySource) Then
                $myTarget = $mySource
                $sTargetText = "File " & $mySource
            Else ;Source is not an existing file.
                If (StringLen($mySource) <= 3) _
                AND $G_ObjFSO.DriveExists($mySource) Then
                    $mySource = $G_objFSO.GetDrive($mySource).Path
                    $myTarget = '\\.\' & $mySource
                    $sTargetText = "Drive " & $mySource
                Else ;Source is not an existing drivespec.
                    ContinueCase ;jump out to the ELSE case to report error.
                EndIf ;check Source for existing drivespec.
            Endif ;check source for existing file.
        Case Else ;Source was not Disk/Drive/File.
            $errRet = $DISKMBR_ERR_SOURCE
    EndSelect ;Determine the Source Target type.
    
    If ($myTarget <> -1) Then
        ;Open a handle to the target.
        $hFile = _WinAPI_CreateFile($myTarget, 2, 2)
        If Not $hFile Then
            $extRet = @error
            $errRet = $DISKMBR_ERR_OPEN
            ConsoleWrite("Unable to access " & $sTargetText & " [@error = " & $extRet & "]" & @CRLF)
        Else ;Handle to target created successfully 
            _WinAPI_SetFilePointer($hFile, 0, $FILE_BEGIN)
            If @error Then
                $extRet = @error
                $errRet = $DISKMBR_ERR_POINTER
                ConsoleWrite("Failed to set pointer to beginning of " & $sTargetText & " [@error = " & $extRet & "]" & @CRLF)
            Else ;Pointer was set to beginning of target.
                $structBuffer = DLLStructCreate($tag_MBR)
                If @error Then
                    $extRet = @error
                    $errRet = $DISKMBR_ERR_STRUCT
                    ConsoleWrite("Failed to create MBR Structure ojbect for " & $sTargetText & " [@error = " & $extRet & "]" & @CRLF)
                Else ;structure created successfully.
                    $pBuffer = DLLStructGetPtr($structBuffer)
                    _WinAPI_ReadFile( _
                            $hFile, _
                            $pBuffer, _
                            DLLStructGetSize($structBuffer), _
                            $iBufferReadSize)
                    If @error Then
                        $extRet = @error
                        $errRet = $DISKMBR_ERR_READ
                        ConsoleWrite("Failed to read MBR for " & $sTargetText & " [@error = " & $extRet & "]" & @CRLF)
                    Else ;MBR was read successfully.
                        ConsoleWrite("MBR read OK! [" & $iBufferReadSize & " bytes] " & $sTargetText & @CRLF)
                        $myRet = $structBuffer
                    EndIf ;Check for error reading MBR.
                EndIf ;check for valid structure creation.
            EndIf ;Check for errors setting pointer.
            ;Make sure to close handle(s) when finished!
            _WinAPI_CloseHandle($hFile)
        EndIf ;Check for valid handle to target.
    EndIf ;check for valid target.
    
    Return SetError($errRet, $extRet, $myRet)
EndFunc ;==> _DeviceReadMBR

The error it keeps reporting is "Error 87: The parameter is incorrect." after the WriteFile DLLCall with a physical disk. I do not get this error when I flip my Select statement to use a file as the target instead of the disk. What am I missing here?

Thanks in advance for any insights!

Share this post


Link to post
Share on other sites



I am also working on something related, and will run more tests. Did you find a solution that worked for writing to raw disk?

Share this post


Link to post
Share on other sites

Have you tried using Terrabyte Lab's MBR tools?

Share this post


Link to post
Share on other sites

But I think we (or at least me) want to learn how to do this using winapi. I was trying basically the same code as the OP 2 days ago, but _WinAPI_WriteFile always return 0. Can it have anything to do with lpSecurityAttributes ($pSecurity) in _WinAPI_CreateFile?

Share this post


Link to post
Share on other sites

I solved it on my code so I guess the answer is that your $iWriteLength must be a multiple of 512 (requirement when writing to physical disk).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0