Monks,

At work I often have the need to do a licence audit.

A few years ago I rewrote this small section of code in perl, based on another script (which I seem to recall was visual basic). This will decode the licence keys from your registry for Windows and Office, for all the versions I have come across.

For Windows, you will need to look in HKLM\Software\Microsoft\Windows Nt\CurrentVersion\DigitalProductID

sub decodekey { my @digits = ('B','C','D','F','G','H','J','K','M','P','Q','R','T','V +','W','X','Y','2','3','4','6','7','8','9'); return "No key found\n" if ($_[0] eq ""); my @id = split(/,/,shift); my $key=""; for (my $i=28; $i>=0; $i--) { if (($i+1)%6) { my $ac=0; for (my $j=66; $j>=52; $j--) { $ac<<=8; $ac+=hex $id[$j]; $id[$j]=sprintf("%x",($ac/24)&255); $ac%=24; } $key=$digits[$ac].$key; } else { $key='-'.$key; } } return $key; }

This can be used by feeding the hexadecimal found in the registry (separated by single commas) to the function.

I also use the following snippet to pull that key remotely using samba4's reg command. (this isn't pretty, but it gives you an idea of usage)

sub regexec { my $computer = shift; my $outkey = ""; my $key = `reg query \\\\\\\\$computer\\\\HKLM\\\\Software\\\\Micros +oft\\\\Windows\\\ Nt\\\\CurrentVersion /v DigitalProductID 2>&1`; foreach (split(/\n/,$key)) { if (m/\s+DigitalProductID\s+REG_BINARY\s+([A-F0-9]+)/) { $_ = $1; s/([A-F0-9][A-F0-9])/$1,/g; $outkey = $_; } } return $outkey; }

Just yesterday, I realised that Office 2008 for the Mac also uses the same format, only it is base64 encoded in this file: /Applications/Microsoft Office 2008/Office/OfficePID.plist

You can use something like this to extract the key from this file.

use strict; use MIME::Base64; my $file = '/Applications/Microsoft Office 2008/Office/OfficePID.plist +'; open (FILE,'<',$file); my $flag = 0; my $section = 0; my $string; foreach(<FILE>) { $flag = 1 if (m/<key>2000<\/key>/); $section++ if ($section == 1); $section = 1 if (m/<data>/ && $flag==1); if (m/<\/data>/) { $flag = 0; $section = 0; } if ($section == 2) { $string .= $_; } } close(FILE); $string =~ s/\s//g; my $hex = uc(unpack("H*", decode_base64(shift))); $hex =~ s/(..)/$1,/g; chop($hex); decodekey($hex);

Yes, this is an odd way to parse XML, but it seems that Mac's put some illegal characters in there, causing XML::Parser to die.

I have been pondering whether to post this or not, but considering there are many programs available to do this job hopefully it is OK.

Replies are listed 'Best First'.
Re: Retrieving Microsoft licence keys from your computer
by Burak (Chaplain) on Sep 23, 2010 at 23:55 UTC
    ++ for the mac portion (although I can't test it), but this was already done in Finding Windows XP CD Key and I also copied that into m Sys::Info where you can just say:
    perl -MSys::Info -wle "my$o=Sys::Info->new->os;print for$o->cdkey,$o-> +cdkey(office=>1)"
    however, it'll only create results under windows.

      Thats even easier! Though I usually do audits from a Linux computer. Any chance of making Sys::Info work on multiple platforms for things like this? It would have to use some sort of remote registry which could be left as an exercise for the user if it was provided with the reg files.

      Does it include functionality for Office keys too?

Re: Retrieving Microsoft licence keys from your computer
by syphilis (Archbishop) on Sep 24, 2010 at 00:35 UTC
    This will decode the licence keys from your registry for Windows and Office

    Doesn't work for me on Windows Vista 64 - sub decodekey returns BBBBB-BBBBB-BBBBB-BBBBB-BBBBB, though I was hoping to see my Windows product key.

    Same thing with the other script referred to by Burak.

    Mind you, some of the Win32API::Registry and Win32::TieRegistry tests failed (which I haven't investigated). Maybe that's part of the problem.
    Have you successfully run that code on Vista ?

    Cheers,
    Rob

    UPDATE: Probably should provide the complete code I ran:
    use strict; use Win32::TieRegistry; print decodekey(qq!HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT +\\CurrentVersion\\DigitalProductId!), "\n"; sub decodekey { my @digits = ('B','C','D','F','G','H','J','K','M','P','Q','R','T','V +','W','X','Y','2','3','4','6','7','8','9'); return "No key found\n" if ($_[0] eq ""); my @id = split(/,/,shift); my $key=""; for (my $i=28; $i>=0; $i--) { if (($i+1)%6) { my $ac=0; for (my $j=66; $j>=52; $j--) { $ac<<=8; $ac+=hex $id[$j]; $id[$j]=sprintf("%x",($ac/24)&255); $ac%=24; } $key=$digits[$ac].$key; } else { $key='-'.$key; } } return $key; }
    UPDATE: Don't worry - my script is obviously crap. No warnings, doesn't actually make any use of Win32::TieRegistry ... and I don't feel disposed to fixing it.