NathanD Posted July 21, 2011 Share Posted July 21, 2011 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: expandcollapse popup#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! Link to comment Share on other sites More sharing options...
joakim Posted July 27, 2011 Share Posted July 27, 2011 I am also working on something related, and will run more tests. Did you find a solution that worked for writing to raw disk? Link to comment Share on other sites More sharing options...
Tripredacus Posted July 27, 2011 Share Posted July 27, 2011 Have you tried using Terrabyte Lab's MBR tools? Twitter | MSFN | VGCollect Link to comment Share on other sites More sharing options...
joakim Posted July 27, 2011 Share Posted July 27, 2011 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? Link to comment Share on other sites More sharing options...
joakim Posted July 28, 2011 Share Posted July 28, 2011 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). Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now