Thursday, July 12, 2018

*sigh* People are still doing this...

I just made a "registration tool" for an app that a friend asked me to look at for him.  It was compressed/encrypted with some weird version of TeLock, and I was unable to find an automated tool/script that would unpack it.  I saved it from memory, fixed the IAT, and then tossed it into IDA Pro to have a look.  Using the Hex-Rays plug-in, this is the code I found for the all important function:

int __thiscall ValidateEnteredCode(void *this)
{
  void *_this; // ebp@1
  char *workBuffer; // edi@1
  unsigned int counter; // esi@1
  int character; // ST14_4@2
  int result; // eax@3
  int currentUser; // eax@4
  int data; // [sp-194h] [bp-194h]@1
  int digest; // [sp-188h] [bp-188h]@1
  int context; // [sp-168h] [bp-168h]@1
  int computedCode; // [sp-100h] [bp-100h]@1
  int enteredCode; // [sp-80h] [bp-80h]@3

  _this = this;
  readSIDsFromRegistry(this, this);
  sha256_init(&context);
  sha256_update(&context, &data, 12);
  sha256_final(&context, &digest);
  workBuffer = &computedCode;
  counter = 0;
  do
  {
    character = *(&digest + counter);
    workBuffer += sprintf(_this, workBuffer, "%02X");
    ++counter;
  }
  while ( counter < 32 );
  getEnteredCode(_this + 152, &enteredCode, 128);
  result = strcmp(&computedCode, &enteredCode);
  if ( !result )
  {
    currentUser = getCurrentUserKey(_this);
    saveRegKey(*(currentUser + 4), _this);
    MessageBox(_this);
    result = endDialog(_this);
  }
  return result;
}

I renamed the functions, and variables after a little study, and some of the code output isn't 100% correct, (like the weird sprintf syntax), but there's enough here to illustrate what I'm talking about.  The read the user SIDs from the registry into a buffer, and then hash the buffer, sprintf it into a string of characters, and then STRING COMPARE it against what you typed in. 

D U M B!

Why is it dumb you ask?  Well, to make my keygen for this, I used an old tool from 2000 called the Risc Process Patcher.  It loads a program, and watches for memory locations to change from a specified value TO a specified value, and then applies patches that you provide.  It's a very simple, and ultimately, a very USEFUL tool.  I've used it since 1999 or so for just such things.  So, back to this target.  Using RPP, I had it watch for the "getEnteredCode" code to be unpacked/decrypted.  Once it was, I just pushed the necessary values onto the stack, and called MessageBox to print out the "Correct" registration code for this machine/user.  You write down what's in the MessageBox, and press OK, and the application crashes, and exits.  (Yes, I could have made it cleaner, but the GAFs were low).  You then run the regular app again, and just enter the correct code it just gave you, and boom.  Registered. 

If you are going to write a registration routine, do a few minutes more studying, and do something that's not clearly so stupid.  If you are computing the correct code in such a way that I could read it from memory, and enter it to bypass your registration routine, then you're doing it wrong.

Some quick notes.  Another reason that I did it the way that I did was that there was something weird going on.  If I entered too big of a patch, when you ran the loader, it would load the executable and patch the code in the correct place, but the app would then crash somewhere else, totally unrelated.  This could have been the unpacker catching it, or something else, but I shrunk my patch down to just what I told you above, and it worked fine.  My 1st patch just changed what MessageBox printed.  But that crashed, even before it got to the code.  But, in the end, I was able to find the right balance between "tell me what I need to know", and "not crashing the app". 

And, as they used to say in the movies, all's well that ends well.

No comments: