Windows 7 Product-Key Verification

Well you might have heared of it a tool from a Russian programmer where you can validate your windows seven product key. Be careful!

The tool itself was written in Delphi and so it was easy to look at it a little deeper. Well from what I’ve found I can say it’s sending your windows product key to a server on the net!

However I found it interesting how the validating process is done. So again research on the net and with the mentioned Delphi tool I found out it uses the pidgenx.dll which resides inside your system folder.

The Function inside of the DLL (PidGenX or its ordinal value 118) which is used is undocumented and may change in further windows versions.

Function PidgenX (
           param,
           param,
           param : PWideChar;
           Unknown : Integer;
           param,
           param,
           param : Pointer) HResult; stdcall;

Let me please describe the parameters:

Param1 = the Windows product key
Param2 = the pkeyconfig.xrm-ms file which is probably used for hashes.
Param3 = MCPID can be found in registry under:

Path: HKEY_Local_Machine\Software\Microsoft\Windows NT\CurrentVersion
Value: Productid (The first 5 digits are your MCPID)

Param4 = Unknown.
Param5 – 7 = Pointer which holds the data the PidgenX function returns after validating successfully your key.

If you don’t want to dive into the registry you can do it :

  1. Open System by clicking the Start button, clicking Control Panel, clicking System and Maintenance, and then clicking System.

  2. Under Windows activation, you can view your activation status. You can also change your product key by clicking Change product key.

Btw. As you can see the function returns a HResult so when the function returns S_OK your key is valid otherwise not.

I should mention you might want to run the function call inside of a separate thread otherwise your GUI will freeze until the function call finished its work.

With my programmer fellow Remko Weijnen I could define the following structure for the PidgenX function.

First Block of Data:

Type
  TDigitalPidId4 = packed record
    cbSize: DWORD;
    Unknown1: DWORD;
    ExtendedPid: array [0 .. 63] of Char;
    Activationid:  array [0 .. 71] of Char;
    Edition: array [0 .. 63] of Char;
    Unknown3: array [0 .. 399] of Byte;
    Unknown2: array [0 .. 79] of Byte;
    EditionId: array [0 .. 63] of Char;
    LicenseType: array [0 .. 63] of Char;
    LicenseChannel: array [0 .. 63] of Char;
  end;
  PDigitalPidId4 = ^TDigitalPidId4;

Second Block of Data:

 TDigitalPidId = packed record
    cbSize: DWORD;
    Unknown: array [0 .. 27] of Byte;
    CryptoID: DWORD;
    Unknown2: array [0 .. 127] of Byte;
  end;

Third Block of Data:

  TGeneratedPid = packed record
    case Byte of
      0:
        (cbSize: DWORD);
      1:
        (Pid: array [0 .. 24] of Char);
  end;

The most interesting record is probably the DigitalPidID4 for you, because it holds the most interesting data inside of it. With all of the above we now can start validating our product keys. So let’s call the PidgenX function dynamically:

var
  PidGenX_: function(WindowsKey, PkeyPath, MPCID: PWideChar;
    UnknownUsage: Integer; var GeneratedProductID: TGeneratedPid;
    var OldDigitalProductID: TDigitalPidId;
    var DigitalProductID: TDigitalPidId4): HRESULT;
stdcall;
Const
  // instead of hardcoding the pathes use system function to make it dynamic...
  DLLPath: String = 'C:\windows\system32\pidgenx.dll';
var
  hr: HRESULT;
  GenPID: TGeneratedPid;
  DigitialPid: TDigitalPidId;
begin
  GetProcedureAddress(@PidGenX_, AnsiString(DLLPath), 'PidGenX');

  ZeroMemory(@GenPID, sizeof(GenPID));
  ZeroMemory(@DigitialPid, sizeof(DigitialPid));
  ZeroMemory(DigitialPid4, sizeof(DigitialPid4^));

  GenPID.cbsize := sizeof(GenPID);
  DigitialPid.cbsize := sizeof(DigitialPid);
  DigitialPid4^.cbsize := sizeof(DigitialPid4^);

  try
    hr := PidGenX_(PWideChar(szKey),
      'C:\Windows\System32\spp\tokens\pkeyconfig\pkeyconfig.xrm-ms', MCPId, 0,
      GenPID, DigitialPid, DigitialPid4^);
  except
    RaiseLastOSError;
  end;

  case hr of
    S_OK:
      ShowMessageDlg('Your key is valid');
    else
       ShowMessageDlg('Your key is invalid');
  end;

  if Succeeded(hr) then
  begin
     // return your data here e.g. in a memo, label or whatever you like...
     // ValidationLog.Lines.Add(Format(‘Extended-Pid: %s’,  [ DigitialPid4.ExtendedPID]));
  end
  else
  begin
     // display a nice error message here
  • stOrM!

    Well after some research again it might be a better idea to change the result of the pidgenX function to an integer because you can then check for the following:

    -2147024809 = Invalid Arguments
    -1979645695 = Invalid Key
    -2147024894 = pkeyconfig.xrm.ms file is not found

    else
    Invalid input