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.

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.

Sunday, November 1, 2015

Embedded Linux based internet appliance keygen - Follow up!

The publisher of the software package that I mentioned in a previous post entitled " Embedded Linux based internet appliance keygen" just published an update.  So, I grabbed the pertinent file (the shared object) and threw it into IDA.  A quick cursory glace shows that in one aspect they STILL haven't learned their lesson.  As they still don't strip it before shipping it.  So, all the important functions "VerifyKey" "IsTrialKey", etc. are still labeled automatically by IDA.  And it looks like their algorithm is still the same for computing the keys.

BUT!  Maybe they've learned something.  Now, when you enter a key, they package it up, and send it to their webserver for verification.   How do I know this?  Simple!  There's now a function called "SendActivationRequest".  And, a quick spin through that function shows that it builds a nice URL, with a hashed component, and the normal sections for serial number, etc.  Then, they use libcurl to send this to themselves for verification.

Could I whack this step, and make it always assume that it succeeded?  Sure, but the truth is.  There's NO WAY, short of a FULL tracing of all the possible code paths, to know that they don't send this data somewhere else, at some random time.  And, since this isn't a "warez" release for the world, and just a mod for some friends, I don't want to be responsible for them getting in trouble for using it.  So, I'm not going to touch this new version.

The device that this code runs on, HAS to be connected to the network, and in some cases, the internet.  So, short of blocking all the companies IPs/domains in the firewall, you just can't be sure.

Lastly, they now appear to have *2* blacklists for unauthorized serial numbers.  The normal "compile time" one.  (list of them is in the .so itself), and they also now have a list on their server.  How do I know this?  Maybe it could be because of the function called "UpdateOnlineBlacklist".

So, there you have it.  Some people DO pay attention, and learn, while others do the same thing over and over.  Or worse yet, rely on some 3rd party to do it for them.

Wednesday, July 8, 2015

Who bases their product on Mozilla? (I mean, OTHER than Firefox?)

Today's entry is a nameless application that's based on Mozilla's framework.  I'm not sure WHY they did it this way, but it posed some interesting challenges.  To start with, the initial application launcher didn't appear to actually DO anything, and then exited.  I can only assume it did some sort of magic behind the scenes to kick off the application, and then it exits.  It's very small, so I guess that's all it NEEDED to do.  What was kinda interesting, is that it was named the same name as the main executable.  So, if you looked at a task list, you would assume it was the launcher, and not the main application itself.

Once I found the main application, I threw it into IDA like normal, and that reveals nothing worthwhile from the outset, but running it with "Break on DLL load" was VERY informative.  The program loads the usual suspects, and then loads yet ANOTHER xxlicense.dll.  (Name changed to protect the innocent).  I exit the application, and go find xxlicense.dll.  Turns out, it's a screaming 23KB.  At least the LAST target attempted to thwart me by putting their licensing code in a HUGE .dll to make it harder to locate the pertinent bits.  Not so this time.  It's all right there in black and white.  Sorta.  So, I toss that file into IDA, it disassembles nicely, and I figure I'll just go to the exports list, and find the functions that I'm interested in, and start there.  To my surprise, the exports list is almost empty.  It contains 2 entries:  DllEntryPoint, and NSModule.  I've seen this sort of thing in the past when I was tinkering with a COM object's .dll.  So what's going on here?  Well, Mozilla has it's own list of methods that it uses, and IDA doesn't know about them.  (Or at least *I* don't know how to make IDA know about them.)  Either way.  So, no hints about what the functions are named.  We'll have to do it the hard way.  I open the file in Hex Workshop, and examine the strings contained inside.  There are lots of good ones!  I pick a nice one like "'License file ',27h,'%s',27h,' does not exist',0".  Go back to IDA, search for it, and boom.  I now have a pointer back to where the license file is opened.  A careful study of the function shows that it simply opens the file, and calls fgets in a loop to read all the contents into a buffer, after which, it closes the file.  Using IDA's cross reference feature, I see that this function is called from *1* place.  I go to that function, and see that it reads the license file into a buffer, and then passes the buffer to a function that processes it.

It appears that the "fields" in the license are all text, separated by bar "|" characters.  So, there is code to find the first bar, and copy from that point to the end of the license, to a new variable.  They then pass a pointer to this buffer to another function.

In the text area, there are some strings of garbage that aren't ASCII strings.  This always makes me suspicious.  In this case, it was warranted.  We have this:

loc_72AD1730 r
.data:72AD7061                 db 0E0h ; a
.data:72AD7062                 db 0E6h ; ยต
.data:72AD7063                 db 0F7h ; ˜
.data:72AD7064                 db 0E0h ; a
.data:72AD7065                 db 0F1h ; ±
.data:72AD7066                 db 0EEh ; e
.data:72AD7067                 db 0E2h ; G
.data:72AD7068                 db 0E7h ; t
.data:72AD7069                 db 0EEh ; e
.data:72AD706A                 db 0E0h ; a
.data:72AD706B                 db 0FCh ; n

The 1st character of that "string" is accessed inside the function that I'm looking at.  It's copied to a buffer, and then there's code that decrypts it.  It's simply XOR'd with 0xA5.  So, looking at the result yields "SECRETKGBKEY".  :-)  Funny guys!

They have that at the start of their buffer, and then copy the rest of the license over to it.  Once they have it all in the same place, they run a simple MD5 against it.  They save off the resultant hash, and return.  The produced hash is then compared against the 1st "field" of the license they read in from the file.  (The data before the 1st bar).  If it matches?  Code is GOOD!

There is one last thing that they do.  Inside the license text is a VERSION of the product.  "Product9" or "Product5".  Even though the hash matches, that doesn't mean that this license is for this version of the product.  So, they compare it.  (After they decrypt it that is!).  So, to keep you from just reading the strings from the .dll, and knowing what to put in the file, they make you work a LITTLE harder.

The product version string is stored with the high bit set, so that makes it also not a visible string when you look for it.  There is a loop that grabs a byte from this "string", ands it with 0x7F, and then compares it to a character from your license.  If they match, it goes on.  Otherwise, the return value is the same as your run of the mill strcmp.  (-1, 0, 1).  Once you product string matches, you're good to go!

One thing you have to be on the lookout for, it expects the LETTERS in your hash to be LOWERCASE.  Uppercase will cause the compare to fail.

So, the steps to make a license.  Take an existing license (Maybe even from another product from the same company!), change the product name to match what you find in the .DLL.  Replace the hash in the 1st field with SECRETKGBKEY.  Generate an MD5 hash on that, and replace SECRETKGBKEY with the hash.  Save, and enjoy your product!






Sunday, July 5, 2015

Embedded Linux based internet appliance keygen

A friend contacted me to tell me that an internet appliance that we both own has some extra functionality in it, if you enable it by entering a key code.  Of course you know that means that I'm interested.  The fact that said appliance is Linux based makes it more special for me, being a Linux hacker at heart.  He sends me some screenshots of the webpages involved, and I start grepping for the strings that I see in the pictures.  I find them, in a strings file.  (Since this app is localized to many different languages, this is to be expected).  I then search for references to that string ID.  And find them in a javascript file.  Of course it's compressed like those JS programmers do.  So, I go to my favorite online JS beautifier site, paste the code in, and viola!  I have readable code.  A quick search with a text editor, and I'm looking at where the "Invalid code entered" box is displayed.  And, I see that it's just a response to an error code.  Some tracing back through the other JS files brings me to a call to a function.  A quick grep for that function tells me that it's not in the JS anymore.  Sounds like we're going in the right direction.

I back out of the JS directory, and find the directory where the .so libraries are stored.  A quick ls of the directory shows me a file called xxlicense.so.  (Name has been changed to protect the innocent).  I toss that into IDA Pro, and see that there are some functions here, but that they call to some centralized function not present in this file.  The list of libraries that this thing loaded was impressive!  The normal ones for glibc, and the like, but also a TON of other libraries.  Not sure if they used them all, or if they were just trying to obfuscate where the protection bits were.  So, I start loading the libs, one at a time, looking for where the functions were that it was calling.  I FINALLY find them in a very large library.  A little digging around shows me that in typical Linux style, they didn't strip the symbols from the library, so not only do I have the names for the EXPORTED functions, but I also have the names of the utility functions used by the exported functions.  A little digging reveals verifyKey.  A cursory glance through here is initially intimidating.  OpenSSL, SHA1, big nums, etc.  All the makings for a genuine nightmare.  But, since you're reading about it here, you know where this is going.

Some further study reveals that the unlock code that you enter is 20 characters long.  They have a list of allowable characters.  (No 0's, O's, 1's, l's, or anything that could be construed as being something else).

char validCharacters[]= {"BCDFGHJKMPQRTVWXY2346789\0"};

These characters are processed one at a time, and a pseudo-summation is performed to generate a key.  The key contains some data, and a checksum of the key.  My mock up of their code looks like this:
      counter = 0;
      do
      {
        BN_mul_word(pOutput, 24);
        keyCharacter = pInput[counter];
        compareCharacter = 'B';
        innerLoopCounter = 0;
        while ( keyCharacter != compareCharacter )
        {
          ++innerLoopCounter;
          if ( innerLoopCounter == 24 )
          {
            innerLoopCounter = -1;
            break;
          }
          compareCharacter = validCharacters[innerLoopCounter];
        }
        ++counter;
        BN_add_word(pOutput, innerLoopCounter);
      }
      while ( counter != length );

There are 24 characters in their "safe list". That's why there's a comparison of innerLoopCounter and 24. The 1st character of their list is 'B'. That's why they "pre-load" B into compareCharacter.
 Now, as you see, they don't add the  CHARACTER to the sum, they add the INDEX of the character in their "safe list". And then, multiply the whole thing by 24. (Except for the LAST character). Being that the code is 20 characters long, this yields a valid number in the 46-91 bit range. That's why they use the BN (Big Number) functionality from OpenSSL. Once this is done, they then slice, and dice the results. The upper 45 bits are a SHA1 hash of the lower 46 bits. And the lower 46 bits contains 31 bits of serial number, and 4 bits of license count, and 11 unused bits. Since we understand all of this, we simply need to do things in the REVERSE order to make our keygen. So, we take a serial number, and number of licenses, and shift those into place. Generate a SHA1 of that half, truncate it to 45 bits, and slap it into the TOP half. THEN comes the interesting part. Reversing the function you see above. It took me a while to figure out exactly how to do it, since brute forcing 24^19th didn't seem like something that I would like to do on a regular basis. The way that I came to understand what would be the final technique was to start with a code of 00000000000000000001. I looked at the output. Then, I moved the 1 to the left, like this: 00000000000000000010. Looked at THAT value. I did that all the way across, and examined the values. It came out as 1 * 24 ^nth. (Where nth represents the position of the 1 character in the string).

 This triggered a thought. What if we do the LEFT most character first. And by DO, I mean divide it by 24^19th, use the result as the index into the character array, and then work on the remainder.

 So, I whipped up code to do that. It looks like this:
  // Set the divisor to 24^19
  BN_set_word(result, 24);
  BN_set_word(remainder, 19);
  BN_exp(rollingDivisor, result, remainder, ctx);

  // Process each digit (codes are 20 digits long)
  while ( count < 20 )
  {
    // If we make it to 0, just spit out B's for the rest (or hot fire).
    if ( BN_is_zero(code) )
    {
      outputCode[count++] = 'B';
      continue;
    }

    // Divide the code by the divisor
    BN_div(result, remainder, code, rollingDivisor, ctx);

    // The remainder becomes the new code
    BN_copy(code, remainder);

    // Adjust the divisor
    BN_div_word(rollingDivisor, 24);

    // Value should be in the realm of 0-23
    if ( BN_num_bits(result) > 5)
    {
      printf("Something is broken, too many bits\n");
      goto Exit;
    }

    // Horrible hack because BN_bn2bin didn't appear to want to work.
    index = atoi(BN_bn2dec(result));

    // Save the letter of the code
    outputCode[count++] = validCharacters[index];
  }
I ran it, and it worked! Some additional digging was required, as the target application also maintains an internal blacklist of codes. I'm not sure if these are codes that they've seen shared online, or what the deal is, but there's a list of 40-some codes. So, I added code to verify that the entered code isn't in the blacklisted code. And lastly, the serial number that you select at generation time has to be within some defined ranges as well. So, added some quick code to verify that, and there you have it. An embedded Linux based internet appliance keygen.

Friday, January 16, 2015

Cross platform code sharing

Recently, I've had the opportunity to examine a massively cross-platform application that's compiled for Windows, Linux, OSX, HP-UX, Solaris, AIX, etc.  I've been in the software world long enough to know that companies like to write code ONCE, and then compile it on different platforms with a little platform specific "shim" code, and then ship it.  It makes PERFECT sense to do this, as you can have all your bugs fixed in a shared tree, and only have to fix them once.  It's even BETTER when you can leverage this work with a protection vendor who offers a protection system that runs on all your target platforms, as it allows you to write your protection code only once.

But therein lies the problem.  Various compilers on various OSes do things differently.  For instance, the MAC compiler might leave in the debugging info and tell me that that random byte in program memory is really called "licenseObtained".  And, once I know that, I can do an XREF on that memory location, and find the 2 places that it's written to.  The first writes a "1" there, and exits the function, and the other writes a "0" there, and drops to a function that displays a messagebox with a message that it was "unable to obtain a license".  When this happens, it doesn't matter that the compilers on the other platforms removed this label.  The cat is firmly out of the bag.

I heard that!  You said "Big deal, you found a variable's name in one version of the application, how does that doom all the OTHER platforms?"  Simple!  The function that made the decision on the value to write to that memory location also read an environment variable that isn't read anywhere else in the code.  So, you simply search for that string in memory, find where it's accessed, and that leads you back to the function.  On ALL the platforms.  Also, it helps when you don't strip your binaries, and I see that the function is named "LicenseCheck".  In this case, you load the application into IDA, let it run for a while, and then tell it to GOTO "LicenseCheck", and there you are.

There are a MULTITUDE of protection failures on display in this application, but the application itself is a great tool.  And, I will admit that it's completely possible that the authors don't care about these failures, as protection is there solely "to keep the honest people honest".  I can respect that!  As someone who has seen the nastiest protection circumvented easily, it makes sense to not spend a ton of time/money trying to protect your application, as really, protection isn't your core business, and a minority of your business customers would use a pirated version, so it's not WORTH it.

But, there you have it.  The perils of sharing the same codebase on a multitude of platforms.

Tuesday, July 8, 2014

Mac protection

So, I spent some time studying the facilities built into OSX that would be used by loaders, decryptors, and the like as the basis of copy protection.  If you are used to protections on the PC under windows, then the Mac appears to be a dream come true for protection tourists.

I decided to try to implement a simple protection of the sort I detailed in a previous post.  It would be architected as follows:

A simple loader application would load a 2nd application in a suspended state, locate it's initial entry point, and remove the INT 3 ($CC) instruction that I had put there.  Once this was done, it would launch the app, and allow it to run.  (Which would simply print "Hello World", and exit).  An adequate proof of concept I thought.   Since OSX is BSD based, I could just use the POSIX stuff, right?  Start an app, use the apple extension to start it suspended, and then just go read/write the spawned applications memory.

That's where the problems started.  Evidently, there has been LOTS of activity on OSX with regards to trojans, rootkits, and the like.  These have forced Apple to lock up lots of avenues into the kernel, and they've sealed off lots of tools that you would need to do good things.  So, this means that most of the casual gaming protections I've seen on the Mac are shameful.  As I mention elsewhere on this blog, the preferred method appears to be this:  Make the loader, and license enforcer, 1 application, and it's the app that gets executed when you use the launcher.  The game's main executable is renamed, and/or hidden.  (Such tactics as its name starting with a ".", so that it's not shown when you ls the files in the bundle.  If it's not THAT technique, then it's "rename the executable to look like a data file".  Anyway, where this leads is that you 1st run the loader, and it verifies your license, and then it execs the game.  No in memory patching, NOTHING.  It just simply launches the game.  So, to crack these games, you just delete the loader, and rename the game to match what the loader used to be called.

I'll definitely be keeping my eye on Mac protections from now on, as this is somewhat fascinating to me!  Stop trojans, and malware, and also stop most protection methods.