Thursday, November 7, 2019

*35th Anniversary of cracking/reversing*

Has it really been that long?  This post will be different from my normal posts, in that it'll contain very little technical content.

I'd like to take this opportunity to thank the people who nurtured my obsession along the way, and remember the fallen friends that I met as a result.

As detailed elsewhere on this blog, I fell into cracking copy protection literally by accident.  A well timed "Run/Stop Restore" press on my late best friend's Commodore 64 got me past the disk check on "Scrolls of Abadon" by Access software.  A short while later, Darren "Dr. Who" from Donelson Tennessee showed me how to crack Raid on Bungling Bay by NOP'ing a couple of bytes, and the ball was rolling.  Here we are 35 years later.  I've cracked software on all sorts of platforms.  C-64, Amiga, TRS-80, DOS, Windows, Linux, some embedded products that ran NO OS.  It's been a helluva ride, and I've MOSTLY enjoyed the experiences.  It's amazing to me that we've come to the point that cracks on new Denuvo protected games can only be done by a couple of people, and in some cases can take A YEAR.  When I first cracked Rob Northen's Copylock, it took me a MONTH, and that seemed to be both forever, and the lamest thing in the history of the world.  So much so that I wrote a tool to automatically remove it from all titles in the future.  And I remember when cracking Ultima 6 took ALL DAY.  A full 8 hours at work was spent crawling through the code until I finally found a single "TEST" instruction that made the difference.  If the test failed, it would ask you the doc check question, whereas a pass would skip it.

My most recent crack was last night.  I did an E-Commerce management app.  It was a trial that would pop up a nag every X seconds.  It fully worked, but it just nagged you.  Written in one of the Borland languages, it was a slight challenge as the string handling wasn't normal.  It didn't use static strings in the executable, nor did it use strings in the resource section either.  It was some weird Borland specific DKLang thing.  But ultimately, it was the call to MessageBox that did them in.  That lead me right back to the code that was showing the box, which was in a function triggered by a timer message arriving on the main thread.  A quick RETN, and all was right in the world again.

So, in closing, I'd like to call out my departed friends who meant so much to me during this run.

Michael Todd Jackson - Had it not been your willingness to let me hack around on your C-64, your encouragement, and your friendship, none of this would ever have happened.  I miss you as much today as I did the day you passed.  I'm still dancing my friend.

Dave Francis (Candyman) - Had it not been for your connections, THG would never have happened, and no one would know who I am, or care.  Thanks for all the time, and money you spent on this hobby that we did "so that we could be popular with 15 year olds using daddy's PC."

Rick Cook (Mongo) - Thanks for your constant friendship and encouragement through the years my friend.  I hope everything is better for you now.  The ghosts of traumas past are finally gone, and you can be at peace.  See you (relatively) soon my friend.

Pierre Barkett (PieMaN) - Thanks for your friendship, dedication to THG, and for taking one for the team with the Novell raid.  You were a good dude, and I'll never forget you.

And now, on to the next 35 years!
-Fab

Saturday, March 30, 2019

Yet another peril of off the shelf copy protection

A friend contacted me to "help him with a program".  That can only mean one thing, "can you fix this for me?".  He sends it along, I install it, and start looking at the files that I get.  Right away I notice something suspicious.  There's a .dll in here named "KEYLIB32.DLL".  Could you be any more obvious?  A quick check of the version info confirms that it's from a different company from the target, and a quick Google points me at them.  I toss the main app into IDA, and when the disassembly is done, I look at the imports to see that all the functions called from keylib32 are things that just SOUND like copy protection.  I save the disassembly, and toss keylib32 into IDA.  "Import section destroyed" (or something similar).  Huh, is this PACKED?  I fire up some "protection and packer identifier" apps, and see that it IS packed.  With a packer made by the same company that makes keylib32 itself.  NICE!  At this point I'm thinking "This could be hairy if they really know what they're doing".  (But how many people truly do?)

I run the app for the first time, get presented with the typical "You don't have a license, what do you want to do?" style dialog, I choose "activate" of course, and then "activate by phone" so that it won't attempt to connect to anyone over the internet.  It provided 2 numbers for me, and asked for 2 numbers FROM me.  I enter some BS as I normally do, and press "Next", and it pops up a nice messagebox telling me that code 1 was incorrect.  That's almost ALWAYS helpful.  I flip over to IDA, and search for that string.  What do you MEAN not found?  I search for smaller parts, nothing!  I fire up hexworkshop and search the .exe for the string in either ascii or unicode.  Not there.  Huh.  I've seen this before!  Some authors of apps will put all their strings in an external .DLL to make the app easier to port to other languages.  They just recreate the app with the translated strings and the correct "resource IDs", and boom.  It's done.  So now, the search is on for the .DLL.  It's not hard to find, and a few minutes with Resource Hacker and I have the resource IDs for the dialog box, and the error strings for when things go bad.  A quick search for the dialogs resource ID, and I discover that there are WAYY too many instances of this value in the program.  I need a new tact.  So, instead, I search for CDialog::CD, and this walks right through the app, pointing out every place that a dialog is created, or DoModal is called to handle it.  A minute or so of this searching takes me right to the code that creates, and calls the dialog.  A quick look at the code before it shows what must be the "Do you have a license file?" code.  A quick 5 byte change, and the jnz is now a jmp, and we can skip the "You don't have a license, what do you want to do?" dialog, and use the program.  I give this patch to my bud, as he was in a bit of a crunch, and now with the pressure off, I had some time to do a deeper dive into this protection.

I went looking for an automated unpacker that would unpack that .DLL, and let me look at the guts of the functions.  No go.  So, I turned to Google.  There was no "pirate chatter" about the protection, but there was something better.  A website with the full SDK documentation for it.  The same stuff you'd get if you were Johnny shareware author and you'd just wasted your money on this crap.  A few minutes of reading what the individual APIs did, and it seemed simple enough.  I went back to IDA's imports tab, and found the especially interesting sounding ones that got called, and looked them up.  Hmm.  This one is interesting!  pp_eztrig1ex().  This appears to be the function that gets called when you press "Next" on the button I mentioned above.  This is the muscle that does the actual comparison of what you typed in, vs. what was expected.  This could be useful!  I jumped to where the stub was in the app, and saw that there was *1* reference to this function.  That's handy!  I went to that location, and fired up HexRays to give me a shortcut peek into what I was dealing with.  Lots of functions, lots of compares, and best of all, references to those error strings I found in the resources earlier!  We're definitely close!  I found the compare for the return value from the the checker function, and forced it to pass.  (Sometimes this will cause the app to generate a valid license and save it for you, effectively self-registering, and that's the end).  In this case though, it wasn't to be.  I got the "Congrats, your shit is registered" dialog box, but I also got a "This is an unregistered eval copy" message at the bottom of the app.  Well THAT'S no good!  I figured I should take a closer look at this protection, and see if I could find any hints.

My first thought was that I could read the documentation, and see if they showed how you were supposed to write your own key generator.  As it turns out, you DON'T.  They HAVE one.  It comes with the SDK.  To their benefit, they didn't have a "Here, please download our SDK!" link in sight.  This would have been almost too embarrassing for words.  Another quick Google search, and I have a link to TFEdit.  The generic keygen app for people using this protection.  I fire it up, and some idiot has left their info in it.  So, I could generate licenses for their apps all day.  (shakes head).  I then go BACK to the online documentation, and research how to set up a new application.  It turns out that as a developer, the ONLY things that differentiate YOUR codes from MY codes are 2 keys.  We'll call them seed1, and seed2.  (As that's more or less what THEY call them).  I tinker around, and find out that seed2 has to be between 1-254.  Now that's REAL security right there!  seed1 appears that it COULD be larger.  So there we have it, we need to know the seeds that are used to generate these licenses.  I could brute force this, but that would take longer than I was willing to invest in this crap.  So, I went back to the documentation for our friend pp_eztrig1ex, and it reveals:

LONG pp_eztrig1ex(PPLFHANDLE handle, LONG regkey1, LONG regkey2, LONG flags, LONG usercode1, LONG usercode2, LONG tcseed, LONG regkey2seed, LPLONG tcvalue, LPLONG tcdata)

Arguments:

handle is the handle to the License File given by pp_lfopen()

regkey1 is the value of Reg Key 1 entered by user

regkey2 is the value of Reg Key 2 entered by user

flags is reserved; set to 0 always

usercode1 was used as User Code 1 or Session Code

usercode2 was used as User Code 2 or Computer ID

tcseed is the Trigger Code seed used to randomize the algorithm

regkey2seed is the Trigger Code Event Data seed

tcvalue is a buffer to place the Trigger Code number. Use 0 to ignore this parameter.

tcdata is a buffer to place the Trigger Code Event Data. Use 0 to ignore this parameter.

Yes, that's right.  The call to the function passes BOTH seed values as parameters.  I load up our old friend Olly, go to the location of that function call and set a breakpoint.  Run the app, enter some crap, and press "Next", and we break.  Huh, seed2 is, as expected, 1 byte.  And seed1, the super secure one, is 2 bytes.  I plug these values into my "new product setup" in TFEdit, and generate myself a license.  The target app takes it, and says I'm registered.  I exit it, and relaunch it, and it runs, but pops up a messagebox telling me that my support has expired, and that I *MUST* renew it.  Huh, what is this?  Well, it turns out that the license has 2 parts.  1 just unlocks that app, and the other specifies the number of days left on your support contract.  (seed2 is used to "protect" those days).  I run it again, re-enter the license details into the app, and this time specify the "extra data" as the max, 53 years of support, and generate the license.  The target gobbles it down, and a restart reveals that I now have support as well. 

So, you pay $700 I think it is, for this protection package, and your only protection is 24 bits of data.  And to make matters WORSE, they must be included in your app where any hacker can just extract them, and then use the license generator to make licenses.  SAD, just SAD.