Hey!
Did you know that the first version of Windows 95 accepted simply 10 ones as a valid CD Key?
I didn’t, and when I was recently installing Windows on an old computer, a friend of mine,
Jon Sawyer aka jcase on twitter, told me about it, and that got me curious: How exactly did
the serial check of Windows 95 actually work? And why would it accept a very simple key
such as 11111111111?
While the algorithm is well known nowadays, I thought it would still be interesting to
try and reverse engineer the Windows 95 serial check using modern tools - more than 25 years
later!
Lets start by looking at the serial number dialog: The windows 95 serial has 11 characters
in the format of 3 numeric digits, a dash, and then 7 additional numeric digits. And
if we enter 3 ones a dash, and another 7 ones, you can see it will indeed be accepted as
a valid serial number!
And funnily enough, we can kind of figure out what’s going on just by trial and error.
Lets start by modifying the first three digits: When we replace the ones with all zeroes,
it still works.. And if we try 001 it works, 567, 789… pretty much anything will work!
Only a couple of numbers, such as 333 and 444 don’t work. So it seems like for now,
we can pretty much simply ignore the first three digits.
Next, lets look at the other 7 digits: If we replace it with all zeroes.. It works!
So even just entering 10 zeroes is a valid windows 95 CD key! Next, lets try some other
numbers… 1 doesn’t work, 2 doesn’t, 3 doesn’t, 4 5 and 6 dont work either..
But 7 works! And after that 8 doesn’t work, 9, 10, 11, 12, 13, 14, 15, but 16 works! And
the next number that works is 25! What do all these numbers have in common? Well if
you stare at them for a while, you might notice that the sum of all digits of the valid serial
numbers always is divisible by 7 without a remainder!
This explains why 7 ones 1 work: the sum of the digits is seven, seven divided by 7 is
1, and there’s no remainder
Pretty simple, and relatively easy to figure out compared to more sophisticated CD key
algorithms, we didn’t even have to reverse engineer anything
Now I was curious how that check is actually implemented, and especially how the first
three digits are checked, so I decided I’d try to load the setup into Ghidra to reverse
engineer it there!
But before we can do that, we first need to figure out in which file of the Windows 95
installation the serial number check is implemented.For that I simply took a Windows 95 installation
CD, extracted all the cab-archives in it using `cabextract *.cab`, and then grepped for the
title of the dialog: “Product Identification”
And as we can see, we get two hits:: suwin.exe and setupx.dll! I decided to go with setupx.dll
first, as I assumed the actual check would probably be implemented in a DLL. To load
it, we can simply drag it into Ghidra! I analyzed using the default options, and after analysis
was finished I used to the Symbol Tree to take a look at the exported symbols of the
binary. And after scrolling through it for a bit, I noticed this function called PID
VALIDATE: Could this maybe stand for Product identification number validate?
So lets jump into that function, and we can see that it’s basically a big switch statement:
I stared at it for a while, and checking out the different functions, and realized that
the switch statement checks the ProductType of our Windows 95: There are the different
Windows versions, such as OEM install, Retail Upgrade, Retail Full installation and so on,
and the format of the CD key is different between some of these versions.
To keep things short I’ve already annotated all the variables and function names here,
if you are interested in learning more about reverse engineering in Ghidra check out some
of my other videos linked in the description!
The PIDVALIDATE function seems to take three parameters: The CD key as a string, the length
of that CD-key as an integer, and the product type as a string.
First, the product type is converted to an integer, which then goes into the switch statement
that checks our producttype.
As you can see, we have two main CD key checking functions, I named them check_retail_key and
check_oem_key. Now my Windows 95 disk is of producttype 6, which is a retail installation,
so lets check out the check_retail_key function: It simply takes the key as the first argument,
and the key length as a second argument. integer.
The function starts with copying 3 bytes from the key into a second string: These are the
first three digits of our CD key! The string is then null terminated, and converted
into an integer. That integer is then compared against 7 different
numbers: 333, 444 555, 6666, 777,. 888 an d999 - the numbers that wouldn’t pass the
check earlier! So it’s really just an explicit blacklist for the first 3 digits!
Another interesting thing to notice is that there is no check whether the first three
digits are actually numeric digits, and so even a serial number that starts with characters
is completely valid. As a matter of fact the dash isn’t checked either, so you can just
fill in whatever, for example YOLO0000000 is a completely valid serial number!
After we’ve passed this check of the first 3 digits, the rest of the key is checked by
another function, that I called mod7 check:
This function simply iterates over the last 7 digits of the key, and creates the sum of
all digits.
Once that’s done the sum is checked whether it’s divisible by 7 without a remainder,
and if that’s the case, the function returns true, otherwise it returns false.
And that’s all there is to the serial number check of windows 95!
The first 3 digits are simply a blacklist, the next digit isn’t checked at all, and
the last 7 digits need to be divisible by 7 without a rest!
Simple!
Now before we finish, I also want to quickly look at the OEM key algorithm:
The format of the OEM is slightly different: It starts with 5 digits, then -OEM-, 7 digits,
another dash and again 5 digits.
I’ve linked the decompiled code of it in the description if you want to check it out,
but basically the first three digits are a number between 1 and 366, and the second number
is between 95 and 03 - the common theory is that this is the day and year in which the
key was generated. The next digits must be -OEM-, then we have 7 digits that use the
same mod7-check we saw earlier, though the first digit MUST be a 0, and finally, we have
the last 5 digits, which can be whatever you want as long as they are numeric!
I hope you enjoyed this short journey to the past, and to see you on this channel again
soon!
Thank you for watching!