Click here to Skip to main content
15,888,579 members
Articles / Security
Tip/Trick

Windows Software Licensing Data and Information in a Nutshell

Rate me:
Please Sign up or sign in to vote.
4.80/5 (3 votes)
2 Jul 2015CPOL2 min read 11.2K   300   8  
Windows software licensing data and information in a nutshell

Introduction

Windows has so many different editions that fit better for all sort of applications but with all the different Windows editions and kernel images, how does the system know which to boot and what settings to use?

The answer is product options key in registry(HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ProductOptions\), by querying the registry values ProductType, ProductSuite and ProductPolicy, the system collects that information that is required to boot the way it should, this information contains the allowed physical memory supported, edition of windows, kernel mui language SKU and much more.

Requirements

Nothing is essential to use the DLL, but to understand it, you would need basic understanding of memorystream, accessing reg., bitwise operations, datatypes, pinvoking, structs/structlayout.

Background

Microsoft never released any documentation for the ProductPolicy key which made it hard to figure out the data structure, but after that, it's as easy as calling an unmanaged function in slc.dll called SLGetWindowsInformation.

C++
HRESULT WINAPI SLGetWindowsInformation(
  _In_      PCWSTR     pwszValueName, //pointer to constant wide string
  _Out_opt_ SLDATATYPE *peDataType, //software licensing datatype
  _Out_     UINT       *pcbValue, 
  _Out_     PBYTE      *ppbValue //pointer to a byte
);

In a nutshell, you invoke SLGetWindowsInformation with a PCSWTR/string name of the query and it sets the values of datatype, value's byte[] length and set a point to this value's byte[], using the datatype you can easily mainpulate the byte[].

For more information, please visit HERE.

The datatype is the same as the usual registry type/enum but it only includes fewer options. I used it as it was documented in Microsoft, but other than that, you can use the usual registry type/enum.

This is the usual reg types I usually use:

C++
public const int
        REG_NONE = 0,
        REG_SZ = 1,
        REG_EXPAND_SZ = 2,
        REG_BINARY = 3,
        REG_DWORD = 4,
        REG_DWORD_LITTLE_ENDIAN = 4,
        REG_DWORD_BIG_ENDIAN = 5,
        REG_LINK = 6,
        REG_MULTI_SZ = 7,
        REG_RESOURCE_LIST = 8,
        REG_FULL_RESOURCE_DESCRIPTOR = 9,
        REG_RESOURCE_REQUIREMENTS_LIST = 10,
        REG_QWORD = 11,
        REG_QWORD_LITTLE_ENDIAN = 11;

Here is the one documented:

C++
typedef enum _tagSLDATATYPE { 
  SL_DATA_NONE      = REG_NONE,
  SL_DATA_SZ        = REG_SZ,
  SL_DATA_DWORD     = REG_DWORD,
  SL_DATA_BINARY    = REG_BINARY,
  SL_DATA_MULTI_SZ  = REG_MULTI_SZ,
  SL_DATA_SUM       = 100
} SLDATATYPE;

For more information, please visit HERE:

Using the Code

I've composed the whole thing in a library and named it WINSLMGR.

How to use? (Check out the demo or follow those steps.)
Include the library as a reference in your project, then call:

C++
WINSLMGR.Initialize();

Now you are able to access the WINSLMGR.SlDATA list which contains all the information you need, it is a list of simple struct (name : string, type : SLDATATYPE, value : string)

C++
foreach (var info in WINSLMGR.SlDATA)
{
    listView1.Items.Add(new ListViewItem(new string[] { info.Name, info.Type.ToString(), info.value }));
}

Let's see some code snippets (the whole project is posted as open source for everyone to learn from and use)

The initialize function which is used to retrieve the data from the registry then using a memory stream, I cast that data into its appropriate structs, then one last call to GetSLInfo which pinvokes SLGetWindowsInformation.

C++
public static void Initialize()
{
    ProductPolicy = GetByteArrayREGKey(registryPath, ProductPolicyStr);
    ProductSuite = GetStringArrayREGKey(registryPath, ProductSuiteStr);
    ProductType = GetStringREGKey(registryPath, ProductTypeStr);
    using (var stream = new MemoryStream(WINSLMGR.ProductPolicy))
    {
        var header = stream.ReadStruct<SLHeader>();
        while (stream.Position + 16 <= stream.Length)
        {
            var InitialPosition = stream.Position;
            var DATA = stream.ReadStruct<SLDATA>();
            var namebytes = new byte[DATA.NameLen];
            stream.Read(namebytes, 0, namebytes.Length);
            stream.Seek(InitialPosition + DATA.Size, SeekOrigin.Begin);
            SlDATA.Add(GetSLInfo(namebytes));
        }
    }
}

The GetSLInfo function simply pinvokes the SLGetWindowsInformation, then manipulates the data to output the information in the appropriate format and type:

C++
public static SLDATAINFO GetSLInfo(byte[] nameBytes)
{
    var name = Encoding.ASCII.GetString(nameBytes).Replace("\0", string.Empty);
    SLDATATYPE DataType;
    uint cbValue;
    IntPtr ValuePtr;
    var result = (HRESULT)SLGetWindowsInformation
(name, out DataType, out cbValue, out ValuePtr);
    if(result != HRESULT.S_OK || ValuePtr == IntPtr.Zero)
        return new SLDATAINFO()
        {
            Name = name,
            Type = SLDATATYPE.SL_DATA_NONE,
            value = ValuePtr == IntPtr.Zero ?
    "NO DATA, SLGetWindowsInformation return null ptr" : result.ToString()
        };
    var Value = new byte[cbValue];
    Marshal.Copy(ValuePtr, Value, 0, Value.Length);
    Marshal.FreeHGlobal(ValuePtr);
    var val = string.Empty;
    switch (DataType)
    {
        case SLDATATYPE.SL_DATA_SUM:
        case SLDATATYPE.SL_DATA_BINARY:
        case SLDATATYPE.SL_DATA_NONE:
            val = ByteArrayToHexString.ByteArrayToHexViaLookup32UnsafeDirectWithDumpStyling
        (Value);
            break;
        case SLDATATYPE.SL_DATA_MULTI_SZ:
        case SLDATATYPE.SL_DATA_SZ:
            val = Encoding.ASCII.GetString(Value).Replace("\0", string.Empty);
            break;
        case SLDATATYPE.SL_DATA_DWORD:
            val = BitConverter.ToUInt32(Value, 0).ToString();
            break;
    }
    return new SLDATAINFO()
    {
        Name = name,
        Type = DataType,
        value = val
    };
}

Using the library is as easy as:

C++
namespace windows_licensing_manager
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            listView1.View = View.Details;
            listView1.GridLines = true;
            listView1.FullRowSelect = true;
            listView1.Columns.Add("Name", 250);
            listView1.Columns.Add("Type", 150);
            listView1.Columns.Add("Value", 450);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            WINSLMGR.Initialize();
            label1.Text = "ProductSuite : " + WINSLMGR.ProductSuite;
            label2.Text = "ProductType : " + WINSLMGR.ProductType;

            foreach (var info in WINSLMGR.SlDATA)
            {
                listView1.Items.Add(new ListViewItem(new string[] 
			{ info.Name, info.Type.ToString(), info.value }));
            }
        }
    }
}

Credits

Credits go to the following people:

  • Remko Weijnen for his productpolicy struct (after 7 hours of testing, found his blog post while I was half way through)
  • CodesInChaos for his super efficient ByteArrayToHexString functions (edited to change the format)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Egypt Egypt
because it's what i do, it's who i am, it's all i know.

Comments and Discussions

 
-- There are no messages in this forum --