It’s a pretty popular question on forums – how do I get the serial number for a hard disk drive. The serial number is then used for some form of homebrew security. The consensus amongst those who’ve been programming a while is that it is a waste of time and effort, unreliable and not particularly good at securing your programs…
Anyway, as a fun challenge I had a go at getting the serial number from my laptops ATA-attached drive, by using DeviceIOControl to send the IDENTIFY DEVICE ATA command, as defined in the ATA spec. This won’t work on SCSI drives, or raid or …,… ,…, so it’s not reliable and isn’t the answer for those of you looking for a unique id (there isn’t a reliable unique id).
Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles
Imports System.Security
Imports System.ComponentModel
Imports System.Text
Module Module1
<SuppressUnmanagedCodeSecurity()> _
Private Class NativeMethods
<DllImport("kernel32", SetLastError:=True)> _
Public Shared Function CreateFile( _
ByVal FileName As String, _
ByVal DesiredAccess As Integer, _
ByVal ShareMode As Integer, _
ByVal SecurityAttributes As IntPtr, _
ByVal CreationDisposition As Integer, _
ByVal FlagsAndAttributes As Integer, _
ByVal hTemplateFile As IntPtr) As SafeFileHandle
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Friend Shared Function DeviceIoControl( _
ByVal deviceHandle As SafeFileHandle, _
ByVal controlCode As Integer, _
ByRef inBuffer As ATA_PASS_THROUGH_EX_WITH_BUFFERS, _
ByVal inBufferSize As Integer, _
ByRef outBuffer As ATA_PASS_THROUGH_EX_WITH_BUFFERS, _
ByVal outBufferSize As Integer, _
ByRef bytesReturned As Integer, _
ByVal overlapped1 As IntPtr) As Boolean
End Function
End Class
<StructLayout(LayoutKind.Sequential)> _
Private Structure ATA_PASS_THROUGH_EX
Public Length As Short
Public AtaFlags As Short
Public PathId As Byte
Public TargetId As Byte
Public Lun As Byte
Public ReservedAsUchar As Byte
Public DataTransferLength As Integer
Public TimeOutValue As Integer
Public ReservedAsUlong As Integer
Public DataBufferOffset As IntPtr
<MarshalAs(UnmanagedType.ByValArray, sizeconst:=8)> _
Public PreviousTaskFile() As Byte
<MarshalAs(UnmanagedType.ByValArray, sizeconst:=8)> _
Public CurrentTaskFile() As Byte
End Structure
<StructLayout(LayoutKind.Sequential)> _
Private Structure ATA_PASS_THROUGH_EX_WITH_BUFFERS
Public Apt As ATA_PASS_THROUGH_EX
<MarshalAs(UnmanagedType.ByValArray, sizeconst:=512)> _
Public Data() As Byte
End Structure
Sub Main()
Console.WriteLine("Enter a drive letter")
Dim letter As Char = Console.ReadKey.KeyChar
Console.WriteLine()
Const GenericRead As Integer = &H80000000
Const GenericWrite As Integer = &H40000000
Const FileShareRead As Integer = 1
Const FileShareWrite As Integer = 2
Const OpenExisting As Integer = 3
Dim drivePath As String = String.Concat("\\.\" & letter & ":")
Console.WriteLine("Trying path: " & drivePath)
Using driveHandle As SafeFileHandle = NativeMethods.CreateFile( _
drivePath, _
GenericRead Or GenericWrite, _
FileShareRead Or FileShareWrite, _
IntPtr.Zero, _
OpenExisting, _
0, _
IntPtr.Zero)
If driveHandle.IsInvalid Then
Console.WriteLine("CreateFile ERROR: " & (New Win32Exception).Message)
Console.ReadKey()
Return
End If
Dim apex As New ATA_PASS_THROUGH_EX
apex.Length = Marshal.SizeOf(apex)
apex.AtaFlags = 2 ' ATA_FLAGS_DATA_IN
apex.DataTransferLength = 512 ' The command returns a 512 byte package of info.
apex.TimeOutValue = 10 ' 10 second timeout.
apex.DataBufferOffset = Marshal.OffsetOf(GetType(ATA_PASS_THROUGH_EX_WITH_BUFFERS), "Data")
apex.CurrentTaskFile = New Byte(7) {} ' This contains the command we are requesting.
apex.CurrentTaskFile(6) = &HEC ' <-- the command "IDENTIFY DEVICE"
Dim apexb As New ATA_PASS_THROUGH_EX_WITH_BUFFERS
apexb.Apt = apex
Dim inBufferSize As Integer = Marshal.SizeOf(GetType(ATA_PASS_THROUGH_EX_WITH_BUFFERS))
Dim bytesReturned As Integer
Const IOCTL_ATA_PASS_THROUGH As Integer = &H4D02C
Dim result As Boolean = NativeMethods.DeviceIoControl(driveHandle, IOCTL_ATA_PASS_THROUGH, _
apexb, inBufferSize, apexb, inBufferSize, bytesReturned, IntPtr.Zero)
If result = False Then
Console.WriteLine("DeviceIOControl ERROR: " & (New Win32Exception).Message)
Console.ReadKey()
Return
End If
DumpString("S/N: ", apexb.Data, 20, 20)
DumpString("Firmware: ", apexb.Data, 46, 8)
DumpString("Model: ", apexb.Data, 54, 40)
Console.WriteLine("Press any key")
Console.ReadKey()
End Using
End Sub
Private Sub DumpString(msg As String, bytes() As Byte, offset As Integer, length As Integer)
' The strings are slightly weird - endianness? If you use ASCII.GetBytes then each character
' pair is reversed.
Dim sb As New StringBuilder(msg & " '")
For i As Integer = offset To offset + length - 1 Step 2
sb.Append(Chr(bytes(i + 1)))
sb.Append(Chr(bytes(i)))
Next
sb.Append("'"c)
Console.WriteLine(sb.ToString)
End Sub
End Module
Output on my laptop:
Enter a drive letter
c
Trying path: \\.\c:
S/N: ' 5TG077W9'
Firmware: 'DE14 '
Model: 'ST9160411ASG '
Press any key
very usefull notes and we need more information on this topic please post updated data . thanks for your post.
ReplyDeleteDot net online tutorials
it's works perfect. thank you so much for sharing
ReplyDelete
ReplyDeletethanks for sharing information,nice article
SAP Success Factors Training In Hyderabad
Everything is fine, am happy about your blog. Thanks admin for sharing the unique content, you have done a great job I appreciate your effort and I hope you will get more positive comments from the web users.
ReplyDeleteHadoop Training in Chennai
Hadoop Training
Best Hadoop Training in Chennai
Best Hadoop Training Institute in Chennai
ReplyDeleteThanks for sharing this quality information with us. I really enjoyed reading.
Best Ice Fishing Gloves Best Ice Fishing Gloves Best Ice Fishing Gloves