Mark- 29 Posted December 1, 2019 Also posted on Embarcadero site. Hello, Working on creating a better method to fingerprint a computer without the issues of MAC ID, memory size etc.. I was looking at the "ProductID" in the registry and found that it is only for show and can be changed to any value without altering the OS operation. Several other product ID type values are present as binary keys. I found some C# code (http://www.mrpear.net/en/blog/1207/how-to-get-windows-product-key-from-digitalproductid-exported-out-of-registry) that could convert the binary to the product key code but no code in Delphi. I converted it to Delphi and it appears to work, somewhat. It returned the correct product code on a W10 64-bit Pro and a W7 32-bit Pro install. Two places in the code I am not sure are correct. Other test returned a code but, I could not verify it was correct. On a W7 64-bit, I have the code, from the CD and the returned code was not correct. I am posting the code for anyone that might need it and check if others have a better solution or comments. Cheers, Mark program GetProdIDs; {$APPTYPE CONSOLE} {$R *.res} uses Winapi.Windows, System.SysUtils, System.Win.Registry; procedure ReadRegisteryBinary(rKey:nativeUInt; const path,keyName:string; var buf; var bufSize:integer); var r:TRegistry; begin r:=nil; try r:=TRegistry.Create(KEY_READ or KEY_WOW64_64KEY); r.RootKey:=rKey; if not r.OpenKey(path,false) then Exit; if (r.GetDataSize(keyName) > bufSize) then begin bufSize:=0; Exit; end; try bufSize:=r.ReadBinaryData(keyName,buf,bufSize); except on ERegistryException do bufSize:=0; end; finally r.Free; end; end; function DecodeDigitalProductId(var data:array of byte; dataSize:integer):string; const keyOffset = 52; digits = ANSIString('BCDFGHJKMPQRTVWXY2346789'); var // isWin8:byte; last,i,current,j:integer; key,keypart1,keypart2:string; begin result:=''; // isWin8:=((data[66] div 6) and 1); //not sure about this data[66]:=(data[66] and $F7) {or ((isWin8 and $02) * 4)}; for i:=24 downto 0 do begin current:=0; for j:=14 downto 0 do begin current:=current * 256; current:=data[j + keyOffset] + current; data[j + keyOffset]:=(current div 24); current:=current mod 24; last:=current; end; if (((current + 1) > 0) and ((current + 1) <= length(digits))) then key:=digits[current + 1] + key; end; keypart1:=key.Substring(1, last); keypart2:=key.Substring(last + 1, key.Length - (last + 1)); key:=keypart1 + 'N' + keypart2; //not sure if this is correct, any keys without 'N' i:=5; while (i < key.Length) do begin key:=key.Insert(i, '-'); Inc(i,6); end; result:=key; end; function GetProductID(const idStr:string):string; var dataSize:integer; data:array[0..$FFF] of byte; begin dataSize:=length(data); ReadRegisteryBinary(HKEY_LOCAL_MACHINE,'SOFTWARE\Microsoft\Windows NT\CurrentVersion',idStr,data,dataSize); if (dataSize > 0) then result:=DecodeDigitalProductId(data,dataSize) else result:=''; end; function ReadProductIDString:string; var r:TRegistry; begin result:=''; r:=nil; try r:=TRegistry.Create(KEY_READ or KEY_WOW64_64KEY); r.RootKey:=HKEY_LOCAL_MACHINE; if not r.OpenKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion',false) then Exit; try result:=r.ReadString('ProductId'); except on ERegistryException do result:='error'; end; finally r.Free; end; end; begin try Writeln('ProductId: ' + ReadProductIDString); Writeln(''); Writeln('DigitalProductId: ' + GetProductID('DigitalProductId')); Writeln('DigitalProductId1: ' + GetProductID('DigitalProductId1')); Writeln('DigitalProductId2: ' + GetProductID('DigitalProductId2')); Writeln('DigitalProductId3: ' + GetProductID('DigitalProductId3')); Writeln('DigitalProductId4: ' + GetProductID('DigitalProductId4')); Writeln('DigitalProductId5: ' + GetProductID('DigitalProductId5')); Writeln(''); Writeln('Press enter/return'); Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; //if the OS was updated using the free version one of these might be the product ID code // Windows 10 Home - YTMG3-N6DKC-DKB77-7M9GH-8HVX7 // Windows 10 Pro - VK7JG-NPHTM-C97JM-9MPGT-3V66T // Windows 10 Home SL- BT79Q-G7N6G-PGBYW-4YWX6-6F4BT // Windows 10 Pro VL-MAK - QJNXR-7D97Q-K7WH4-RYWQ8-6MT6Y end. Share this post Link to post
FredS 138 Posted December 1, 2019 The line with 'isWin8' is required and correct and the line following with 'data[66]' should only be executed if isWin8=true. Share this post Link to post
Mark- 29 Posted December 1, 2019 1 hour ago, FredS said: The line with 'isWin8' is required and correct and the line following with 'data[66]' should only be executed if isWin8=true. Thanks. Changed to: isWin8:=((data[66] div 6) and 1); if (isWin8 <> 0) then data[66]:=(data[66] and $F7) or ((isWin8 and $02) * 4); Did you see: key:=keypart1 + 'N' + keypart2; //not sure if this is correct, any keys without 'N' Share this post Link to post
aehimself 396 Posted December 1, 2019 Don't rely on the registry, it easily can be overwritten! Even if it's temporary, it can confuse your application. I found this code in the Internet, which I extended to generate an MD5 hash with DCPCrypt. Remove it if you don't need / have it and use it if you want. HWID.7z Share this post Link to post
FredS 138 Posted December 1, 2019 (edited) 5 hours ago, Mark- said: any keys without 'N' Works here.. but that code should only run if 'IsWin8' Edited December 2, 2019 by FredS Share this post Link to post
Mark- 29 Posted December 1, 2019 1 hour ago, aehimself said: Don't rely on the registry,.. Thanks Share this post Link to post
Lars Fosdal 1792 Posted December 2, 2019 You can also validate your findings through PowerShell PS C:\> Get-ComputerInfo | select WindowsProductName, WindowsVersion, OsHardwareAbstractionLayer WindowsProductName WindowsVersion OsHardwareAbstractionLayer ------------------ -------------- -------------------------- Windows 10 Enterprise 1809 10.0.17763.737 Share this post Link to post
Mark- 29 Posted December 2, 2019 (edited) Thanks The result: WindowsProductName WindowsVersion OsHardwareAbstractionLayer ------------------ -------------- -------------------------- Windows 10 Pro 1809 I ran it with no input parameters and most of the results are blank. Edited December 2, 2019 by Mark- Share this post Link to post
Lars Fosdal 1792 Posted December 2, 2019 Which version of PowerShell? I recommend installing PS Core 6 while waiting for 7. Faster and richer. From your Windows Powershell command line, run: iex "& { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI" Note that PS Core 6 runs on Linux and MacOS too. Share this post Link to post
Mark- 29 Posted December 2, 2019 Never used PowerShell much. The product version seems to be the same as the OS. The path has V1.0 at the end. OK installed it and much more data. Thanks, 1 Share this post Link to post
Mark- 29 Posted December 2, 2019 (edited) 21 hours ago, aehimself said: Don't rely on the registry, it easily can be overwritten! Thanks very much for the code. It is very interesting. Am I wrong to think that some values, while using WMI to fetch the values, WMI retrieves the values from the registry? Edited December 2, 2019 by Mark- Share this post Link to post
aehimself 396 Posted December 2, 2019 3 hours ago, Mark- said: Am I wrong to think that some values, while using WMI to fetch the values, WMI retrieves the values from the registry? To be honest I am not 100% sure about it, but I think no. WMI is storing information in it's own database, which is a piece of junk. Back in the days when I was a sysadmin we had to rebuild countless corrupted WMI databases 🙂 Wikipedia also seems to confirm that it's separate from Registry: "Windows Management Instrumentation (WMI) consists of a set of extensions to the Windows Driver Model that provides an operating system interface through which instrumented components provide information and notification." https://en.wikipedia.org/wiki/Windows_Management_Instrumentation Share this post Link to post