Skip to content

Instantly share code, notes, and snippets.

@EpicVoyage
Created July 2, 2020 12:08
Show Gist options
  • Save EpicVoyage/5e587f43c449e6990143b9228bdcdadc to your computer and use it in GitHub Desktop.
Save EpicVoyage/5e587f43c449e6990143b9228bdcdadc to your computer and use it in GitHub Desktop.
/**
* Shared in the hope that this will be useful to others who need USB serial numbers in Windows. This should be valid
* for Windows 2000, XP, Vista, 7.
*
* It is possible for USB devices to not have a serial number. When that happens Windows assigns one based on the bus
* that the device is attached to. I do not have a thumb drive like that to test it with, but others have said that when
* the second character of the “serial” is an ampersand that it means the device does not have a serial number. This
* code does not test for it.
*/
int NextDevice(DWORD *index, TCHAR *drive, TCHAR *serial, DWORD szs, TCHAR *parentidprefix, DWORD szpip)
{
/* Declare some variables we'll need */
TCHAR *val, *data, *ptr, *ptr2;
DWORD sz, len, szval, szdata, type;
int ret = 0;
/* Make the HKEY instance persistent */
static HKEY hkey = NULL;
/* Obtain share name and timeout from HKEY_CURRENT_USER first */
if (hkey == NULL)
{
if (RegOpenKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\MountedDevices"), &hkey) != ERROR_SUCCESS)
hkey = NULL;
}
if (hkey != NULL)
{
/* Size of largest data blocks we anticipate needing */
szval = 256;
szdata = 1024;
/* Create buffers big enough for the anticipated data */
val = (TCHAR *)malloc(sizeof(TCHAR) * szval);
data = (TCHAR *)malloc(sizeof(TCHAR) * szdata);
while ((sz = RegEnumValue(hkey, (*index)++, val, &szval, NULL, &type, (LPBYTE)data, &szdata)) == ERROR_SUCCESS)
{
/* Windows XP/2000 mehod:
* Only use the entries that have a DOS drive letter associated with them.
*/
if ((wcsncmp(val, _T("\\DosDevices\\"), 12) == 0) && (wcsncmp(data, _T("\\??\\STORAGE#RemovableMedia#"), 27) == 0))
{
/* Get the drive letter */
wcsncpy_s(drive, 3, val + 12, 2);
/* Supply our own terminating NULL character */
*(drive + 2) = 0;
/* This should pull the data between the 2nd and 3rd hash marks since
* we want to use it instead of the registry key that it represents.
*/
ptr = wcschr(data + 26, '#') + 1;
ptr2 = wcschr(ptr, '#');
/* Copy out the ParentIdPrefix to return */
len = (DWORD)(((DWORD)(ptr2 - ptr) < szpip) ? (ptr2 - ptr) : (szpip - 1));
wcsncpy_s(parentidprefix, szpip, ptr, len);
/* Let's add a NULL to this string also */
*(parentidprefix + len) = 0;
/* Kill the Removable Device tag on the string */
if (ptr = wcsstr(parentidprefix, _T("&RM")))
{
*ptr++ = 0;
*ptr++ = 0;
*ptr++ = 0;
}
ret = 1;
break;
}
/* Vista/7 mehod:
* Only use the entries that have a DOS drive letter associated with them. Vista
* uses "_??_", Windows 7 uses "#??#".
*/
else if (((wcsncmp(data + 1, _T("??"), 2) == 0) && (wcsncmp(data + 4, _T("USBSTOR#Disk"), 12) == 0)) &&
(wcsncmp(val, _T("\\DosDevices\\"), 12) == 0))
{
/* Get the drive letter */
wcsncpy_s(drive, 3, val + 12, 2);
/* Supply our own terminating NULL character */
*(drive + 2) = 0;
/* This should pull the data between the 2nd and 3rd hash marks since
* we want to use it instead of the registry key that it represents.
*/
ptr = wcschr(data + 4, '#') + 1;
ptr = wcschr(ptr, '#') + 1;
ptr2 = wcschr(ptr, '#');
/* Copy out the serial number to return */
len = (DWORD)(((DWORD)(ptr2 - ptr) < szs) ? (ptr2 - ptr) : (szs - 1));
wcsncpy_s(serial, szs, ptr, len);
/* Let's add a NULL to this string also */
*(serial + len) = 0;
/* Kill the &# at the end of the string… */
if (ptr = wcsstr(serial, _T("&")))
{
while (*ptr)
*ptr++ = 0;
}
ret = 1;
break;
}
szval = 256;
szdata = 1024;
}
/* Free up the memory we were using… */
free(val);
free(data);
if (sz != ERROR_SUCCESS)
RegCloseKey(hkey);
}
return ret;
}
int GetXPSerial(TCHAR *parentidprefix, TCHAR *serial, DWORD szs, TCHAR *friendlyname, DWORD szfn)
{
/* Declare some variables we'll need */
TCHAR *path, *val, *pip, *ptr;
DWORD index, index2, szval, len;
HKEY hkey, hkey2, hkey3;
path = (TCHAR *)malloc(sizeof(wchar_t) * 255);
wcscpy_s(path, 255, _T("SYSTEM\\CurrentControlSet\\Enum\\USBSTOR"));
/* Obtain share name and timeout from HKEY_CURRENT_USER first */
if (RegOpenKey(HKEY_LOCAL_MACHINE, path, &hkey) == ERROR_SUCCESS)
{
/* Size of largest data blocks we anticipate needing */
szval = 256;
/* Create buffers big enough for the anticipated data */
val = (TCHAR *)malloc(sizeof(TCHAR) * szval);
pip = (TCHAR *)malloc(sizeof(TCHAR) * szval);
/* Iterate through the child keys. This will probably be a list of all USB
* storage devices ever connected to the computer.
*/
index = 0;
while (RegEnumKeyEx(hkey, index++, val, &szval, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
{
/* Prepare the path for another iteration */
*(val + szval) = 0;
wcscpy_s(path, 255, _T("SYSTEM\\CurrentControlSet\\Enum\\USBSTOR\\"));
wcscat_s(path, 255, val);
len = (DWORD)wcslen(path);
/* Descend into each key. This will be the serial number of the device, if
* it has one, with "&0" added to the end.
*/
if (RegOpenKey(HKEY_LOCAL_MACHINE, path, &hkey2) == ERROR_SUCCESS)
{
/* We should only have on serial number but we still have to call this
* function.
*/
index2 = 0;
szval = 256;
while (RegEnumKeyEx(hkey2, index2++, val, &szval, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
{
/* Prepare the path for another peek… */
*(val + szval) = 0;
/* Use a little trickery to truncate path, just in case there are multiple serials */
wcscpy_s(path + len, 255 - len, _T("\\"));
wcscat_s(path, 255, val);
if (RegOpenKey(HKEY_LOCAL_MACHINE, path, &hkey3) == ERROR_SUCCESS)
{
szval = 256;
if (RegQueryValueEx(hkey3, _T("ParentIdPrefix"), NULL, NULL, (LPBYTE)pip, &szval) == ERROR_SUCCESS)
{
*(pip + szval) = 0;
if (wcscmp(pip, parentidprefix) == 0)
{
/* Copy the Serial number back */
wcscpy_s(serial, szs, val);
/* Kill "&0" from the end */
if (ptr = wcsstr(serial, _T("&0")))
{
*ptr++ = 0;
*ptr++ = 0;
}
/* It's easier to just return from here instead of trying
* to break from two loops.
*/
free(val);
free(pip);
RegCloseKey(hkey2);
RegCloseKey(hkey);
return 1;
}
}
}
szval = 256;
}
RegCloseKey(hkey2);
}
szval = 256;
}
/* Free up the memory we were using… */
free(val);
free(pip);
RegCloseKey(hkey);
}
return 0;
}
// Pick out USB removable drives
void LoadDevices()
{
/* Create some variables */
TCHAR *validdrives, *drive, *parentidprefix, *serial, *friendlyname, *ptr;
DWORD sz = 64, szvd, index = 0;
/* And allocate them… */
parentidprefix = (TCHAR *)malloc(sizeof(wchar_t) * sz);
friendlyname = (TCHAR *)malloc(sizeof(wchar_t) * sz);
serial = (TCHAR *)malloc(sizeof(wchar_t) * sz);
drive = (TCHAR *)malloc(sizeof(wchar_t) * 3);
*serial = 0;
/* Get a list of currently valid drive letters */
szvd = GetLogicalDriveStrings(0, NULL);
validdrives = (TCHAR *)malloc(sizeof(wchar_t) * szvd + 2);
GetLogicalDriveStrings(szvd, validdrives);
while (NextDevice(&index, drive, serial, sz, parentidprefix, sz))
{
ptr = validdrives;
while (*ptr)
{
if (wcsncmp(ptr++, drive, 2) == 0)
{
if (*serial == 0)
GetXPSerial(parentidprefix, serial, sz, friendlyname, sz);
MessageBox(NULL, serial, drive, MB_OK);
break;
}
ptr += wcslen(ptr) + 1;
}
*serial = 0;
}
free(parentidprefix);
free(friendlyname);
free(validdrives);
free(serial);
free(drive);
return;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment