Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Ideas to moderately reduce crackability for fun & profit

This thread is locked; no one can reply to it. rss feed Print
Ideas to moderately reduce crackability for fun & profit
m c
Member #5,337
December 2004
avatar

For Dustin and anyone.
I can't remember all of it but the essence is as follows:

Every computer program, even those using external hardware protections, can be cracked. But not all programs actually are.

A certain way to think of it, is that there is a spectrum between cracked and uncracked. The more people that are interested in it, drags it into the cracked domain as soon as cracker.motivation + cracker.capability > work_to_do. Motivation and capability vary with individual but the more popular something is, the more that will come.

If the program is just everyday average small tier then discount the chance that a pro hacker will come along, and instead increase the work_to_do until it overcomes the combined power of any amateurs with their ollydbs and their softices (or cracked releases of IDA Pro or w/e).

The way that they work is they scan for values, they set watches, they set breakpoints, and they home in on the solution, taking any hints or insights as they go along, until they figure out what will work. Backtracking as necessary. Some get it some don't. They don't have your source code, only their disassembly to work with. They cannot possibly study all of the assembler and map out how the entire program functions (well they can, but that would make them pro, or rather fanatics).

Also on this note, you can screw up and ruin their disassemblies but this requires inserting junk and then manually fixing all further offsets, plus you have to bridge the junk yourself (can be done by various ways besides a jump or conditional jump that is always taken or whatever, for added extra 5734|_7|-| ;) ).

The way it works is put in a prefix byte that will then offset all further instructions and mess up their decoding in the disassembler, unless disassembly is started at an appropriate (ie uncommon) position after the anomaly, which will probably only be done when the cracker is on to you & your scheme.

To not get screwed up yourself, you will have to jump over the anomaly. A simple jmp would do, or a conditional jmp that is set to always go is a bet less obvious. That is also too easy to notice.

Even better still is to CALL a function that RETurns 1 byte further than it would, by screwing with it's stack frame (popping the EIP value into a register, modifying it, and pushing it back onto the stack or something). The function should only perform that in one of various alternate return paths so that it can be called multiple times so that it isn't suspicious. Maybe in a keyboard polling routine that is called once before the main loop starts, and all of the instructions after that are all offset by the "hiccup" byte or two.

Instead of passing it different arguments which can be noticed easier, it should read a global like fps counter, that will (HOPEFULLY) only be zero before any frames have actually been drawn, and there is your conditional path within it. Now they will have to study the function that is called before the bogus instructions and they might not get it, because you're just being too tricky for them.

This Disassembly-corrupting junk insertion requires you to twiddle with the binary image after assembly and fix relocation and such, so I wouldn't bother but you can try if you wish. It will probably stop them dead in their tracks when they can't even disassemble + reassemble your binary, considering we are only targeting amateurs and intermediates here after all.

There is also no need to always RETurn properly in the end either, ie instead of jumping to a block of code, you could CALL+POP+MODIFY+PUSH+RET your way there, if you wanted to, they see that you call a certain named function, and keep watching after it would have returned, to see your highly plausible but false verification routines, and then they'd be confused that their changes aren't occurring, because it never does return to that point. Probably not that useful in this situation because their breakpoints won't be hit and that will tip them off, but if you want to run some secret scan or initialize code first, you can do it this way that is more sneaky (if it were in the body of the called function then they see it right away anyway). It's an idea and because it is unusual it may trip them up.

OTHER THINGS:

>Remember that if there is a weakest link in the chain, then that is probably where they will end up. Don't modularize too much, make them have to go all over the binary modifying things. Use macros source-wise to make this easier on your part.

>Don't compare inputs or results of calculations on inputs to test values, instead use them in a way that still enforces your restrictions, even if only loosely or non-absolutely.

The reason why is because you are trying your best so that the compiler will not emit any CMP instructions, as those will be sought out by a cracker.

Maybe the demo limits the number of things to 35.

struct things* list;
...
if(numOfThings < 35)
{
    struct things* newThing = newThing();
    add_newthing_to_list(list,newThing);
    ++numOfThings;
}
...

Easy to crack, he just has to alter the constant in the comparison in his disassembley.

#define MAX_STUFF_FOR_DEMO 35
struct things list[MAX_STUFF_FOR_DEMO];
...
if(numOfThings < MAX_STUFF_FOR_DEMO)
{
    list[numOfThings].stuff = whatever;
    ++numOfThings;
}
...

Easy to crack, now he just has to realize to alter some .bss section as well. He will be clued about that by looking at the code that he has found at his compare function.

#define MAX_STUFF_FOR_DEMO (3*35)

..
if(numOfThings*3 < MAX_STUFF_FOR_DEMO)
...

Better, now at least a search for the value that is the limit in CMP won't show up, but the difference is so marginal it might not matter because he'll see it when he sets the breakpoints and homes in.

struct things* list;
...
void add_new_thing()
{
    float cs = Currentsize;
    cs /= cs * 0.028; // 1/35, 35 Being our max demo value
    realloc(list, currentSize, (int)cs);
}

Or whatever you feel like doing. It doesn't really matter, so long as it's another thing that has to be changed to either unlock a demo or make something think it is registered when it isn't. This one has no branches, just a segfault when something greater than element 35 is accessed. Good to put in as a second measure past an easily crackable comparison check (Ideas to moderately reduce crackability for fun & profitto protect the innocents from devastation).

>For every line of code of the protection functions, write 5 similar lines doing similar things, then spread them all out, and mix them up with (other) "junk code". Also spread the protection "function" up into multiple functions and use heap state to flag if the protection payload is to execute this time, or if it is just being executed by coincidence (maybe use a value to be used as an offset to the memory location to write to, instead of a comparison, so that it still always executes). Also the code to flag that could be done indirectly by "junk code" elsewhere (again using special offsets to make it effectively active or inactive). Several layers of this will give BIG headaches, and it is best if the output of this is necessary to continue (as in the rest of the code is encrypted, you unaligned jump into the middle of a big mess or anything that you can think of, where the program does not work as expected unless the cracker does some work).

An example of that would be removing the floating points calculation from the last example just above, and instead embedding it interleaved in a big block of such stuff, and the above function access the truncated integer result as a named global maybe.

>Think about writing a 32bit int to one location, and readying a 16bit short from the address 2 beyond it (on intel this would give you the higher half I believe). The code doesn't read from the location that the other writes to on paper, but I get the feeling that a debugger will catch this anyway.

>It's okay if your protections are not absolute (they do not halt the executable), but just bring in subtle bugs like MEMORY LEAKS (those are great ones never forget). So long as a blatant security check (no matter how easy it is to bypass) is inserted first, you know that innocents will not be harmed.

The only down side is that those that use the not-completely cracked warez will have a low opinion of your software, but unless your software is AAA game title were the warez bunnies opinions online might have some influence (This excuse has been used to explain commercial failure before, most likely just a cop out), well you know who cares about them?

>Instead of sending the calculated value (resulting of part of validation function on a serial number or something) to memory or calling a function with it as a parameter, instead just calculated it and then log it (logprintf or something), and the logging function that will perhaps not be inspected as much will instead set some memory in the heap that will then by used by some junk code to write to the real location (or another that will be used by more junk code, etc). It should be that only certain values will manage to propagate due to the logic and mathematical routines (combined sums being (indirectly) compared, ands, xors, etc).

Also the part of the logging function that will pick up the signaling could be embedded in a part that accumulates a running score on the various different classes of logged data, to blend in with similar functionality so a bit less obvious.

That example doesn't matter at all. It is the concept behind it. But if you find yourself thinking too much, then you are thinking TOO MUCH, so just go type some code. Things may be pretty obvious to you in C or C++, but from the disassembly it isn't so easy.

>Use asymmetric encryption for the key generator. Anyone can decrypt (validation function that crackers can inspect, to test if the key is valid), but only you can encrypt (create valid serial numbers). This guards against keygens, as they will then need to crack your secret key. It took 2.5 years to just recently crack a RSA 720bit value, so 1024bit RSA or higher is safe safe safe.

>To test if functions returned true or false, AND a hard-coded value like 0x11111110 with a heap value, the heap value should be a specific value, not just any non-zero. By a process of bit shifts and divisions using values precalculated during "junk calculations" it will be tested for being the proper true value (or maybe one of it's many equivalents due to mathematical processing).

For game values, store them as an offset / modulo / bitshift / xor using a macro function.

What does this gain you? Protection from art money / cheat engine type scanning.

>It doesn't matter if you are doing more of the same thing again and again, because that's more work to be countered. Think of it as you debuffing the enemy's attack skill.

>Figure out your validation function, the logic that ultimately decides to keep locked or unlock the extra functionality, or however your scheme works, and then break it up across multiple functions, and multiple calls to the same function. Transfer state in various ways. Embed each line that matters in 5 that don't. It takes no extra time for you to do that (which is the point, it's easy to do), but it's not so readily apparent from the crackers POV. Maybe I already mentioned this but it bears repeating, and I think don't get too caught up trying to do anything much beyond because really, what else can you do?

(\ /)_____#_____####__#
(O.o)¯¯¯#¯¯¯¯¯#¯¯¯#¯¯#
(> <)__####__####__####
Megabytes are where I keep my Data.

SiegeLord
Member #7,827
October 2006
avatar

I have an even better idea for my upcoming RPG game. I will release it freely, with a GPL3 license on its code. No hacker will be able to crack it >:(

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]

bamccaig
Member #7,536
July 2006
avatar

Dustin Dettmer
Member #3,935
October 2003
avatar

m c
Member #5,337
December 2004
avatar

Do whatever you want. And whatever you do do, is done only because you wanted to do it.

When I started looking into this, it got me motivated to learn about the portable executable format, about making my own table-based assembler, learning more about IDA pro, about a lot of things that I wouldn't have bothered to really look into otherwise.

Personally I do not like the FSF-clique, I think that they're all tossers. I'd rather release stuff BSD license, because if I have not made some kind of product out of it then I really don't mind if someone else does.

And if I did make a product out of software, I'd probably feel motivated enough to give hardening of the protection scheme a go. It's like the natural instinct to improve, strengthen, make better. Or I might just go outside and sip alcohol in a deck chair. Who knows. If you really want to make money then I feel that the internet and your own personal server need to fit into the picture somehow.

(\ /)_____#_____####__#
(O.o)¯¯¯#¯¯¯¯¯#¯¯¯#¯¯#
(> <)__####__####__####
Megabytes are where I keep my Data.

Paul whoknows
Member #5,081
September 2004
avatar

Use Molebox just as a temporary solution and spend your time in game development.
Also you can XOR all your data, compress it, encrypt it, all these things can be fun.

____

"The unlimited potential has been replaced by the concrete reality of what I programmed today." - Jordan Mechner.

Ben Delacob
Member #6,141
August 2005
avatar

This old article has some good advice (especially relating to a tricky checksum but other things too):

Keeping the Pirates at Bay. Gavin Dodd on Spyro 2's 3's copy protection

__________________________________
Allegro html mockup code thread -website-
"two to the fighting eighth power"

Thomas Fjellstrom
Member #476
June 2000
avatar

I think the one kind of "copy protection" I don't hate is making the game demo-ish if it detects its unlawfully copied.

Sadly though there might be instances of the game detecting wrong, and inconveniencing actual paying customers which is NEVER a good thing.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro SVN Snapshots] - [Allegro TODO] - [Web Hosting]
"God Bless Joe Pesci" -- George Carlin
"Goto is the buldozer of coding. Sometimes, the buldozer is just the right tool for the job. Not often, but sometimes." -- LordBob

Go to: