Tuesday, February 1, 2011

Non-trivial key names

It seems that you run into instances where someone working with the registry doesn't know quite how to use it. Well, let's start with some code first.

Say we have a regex to carve out the data we want:

Regex nk = new Regex(@"nk[\x2c|\x20]\x00.{7}\x01.{117}");

Chances are this data chunk will have a lot of junk at the end. Most key names as far as I can tell under < 65 characters long, but there are instances where a name legitimately runs longer than that. Here is one example:


It's not a root key!
Offset to Parent: 4145008
Number of Subkeys: 0
Offset to Subkey LF Blocks: 1061109567
Number of values: 1
Offset to value list: 4144959
Offset to security key: 7421704
Offset to classname: 1061109567
Offset to ?trash?: 1634757999
Name Length: 58 bytes
Classname Length: 0
Partial Name: C:|WINDOWS|Microsoft.NET|Framework|v3.5|AddInProcess3
Name: C:|WINDOWS|Microsoft.NET|Framework|v3.5|AddInProcess3

It's not a root key!
Offset to Parent: 4144959
Number of Subkeys: 0
Offset to Subkey LF Blocks: 1061109567
Number of values: 1
Offset to value list: 4144992
Offset to security key: 1061109592
Offset to classname: 4156278
Offset to ?trash?: 1231316033
Name Length: 20590 bytes
Classname Length: 28530
Partial Name: cess32,version="3.5.0.0",publicKeyToken="b77a5c561934


The first node key name has a length of 58 bytes. Pretty normal. But the second node key has a name 20590 bytes long. It also has to do with the .NET Framework. (*sigh* Microsoft...)

I can't carve out 20000 byte long chunks for each key node to satisfy the needs of names that shouldn't really be names, that would be crazy. It just so happened that this name was throwing an IndexOutOfrangeException. I decided I could use this to my advantage.

I could pick a sane number for the size of the regex that would get 90% of my key names and simply work around the longer names (in the short term at any rate).

My code ended up looking like this:


else if (i == (int)0x0048) //name length
{
byte[] lengthBytes = new byte[dword]; //should only be a word length. not sure why I need to make this dword

for (int k = 0;k<word;k++)
{
lengthBytes[k] = bs[i+k];
}

nameLength = BitConverter.ToInt32(lengthBytes, 0);

Console.WriteLine(String.Format("Name Length: {0} bytes", nameLength.ToString()));

i += word;
}

... //other else if's here

else if (i == (int)0x004C) //key name
{
int length = nameLength;

char[] blah = new char[length];

for (int k = 0; k < length;k++)
{
try
{
blah[k] = (char)bs[i+k];
}
catch(Exception ex)
{
//sometimes you get stupid long names (someone not know inghow the registry works)
//when this happens, we will just read what we can and return what we get. Doesn't have to
//be perfect since we can load the full name at a later time when the user clicks the list item
//for most names, this won't be needed.
if (ex.GetType() == typeof(IndexOutOfRangeException))
{
Console.WriteLine("Partial Name: " + new string(blah));

i += bs.Length - i;
k = length;

continue;
}
else
throw ex;

}
}

Console.WriteLine("Name: " + new string(blah));

i += bs.Length - i; //we are done.
}


I figure for a list of values, you won't be showing more than 100 or so characters until you pick the specific key out of the list and it loads the full details. At that point you can read the entire name and show it to the full user.

No comments:

Post a Comment