basic copy proection

Started by
34 comments, last by DaBookshah 18 years, 1 month ago
The #1 thing to do to make reverse engineering more difficult is to turn on the right optimization and other compiler/linker settings. Which compiler (and language, for that matter) are you using?

Another evil trick would be to use functions for read-only data arrays (implemented using 'naked functions' with the content being solely "__asm __emit #" or equiv) and/or crafting code manually and putting it in data variables.
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
Advertisement
Quote:Original post by _Sigma
__try{  _asm  {	pushfd	or dword ptr [esp], 0x100 //set the trap flag	popfd	nop  }}__except(EXCEPTION_EXECUTE_HANDLER){	bExceptionHit = true;}if(!bExceptionHit)	printf("A debugger is present");


I'm not the least bit familiar with assembly. Can someone walk me through this little bit and explain what it does? Very interesting!
Quote:IsDebuggerPresent isn't worth your time :P Unless your code is packed, its so damn easy to pass. You just search the imports for it, then if present, bp on it, once you hit, change the first OP to a 'ret' or 'ret 8' Can't remeber off the top of my head. And bam. Its defeated. See how easy? So put it in there, but don't rely on it.

Constant arms race. Might be a good idea to verify integrity of that API's code - if changed, it's a surefire indicator of messing around.

Quote:*BUT* There is a way around this, again described by Elad. Just impliment your own 'IsDebuggerPresnt' code.

That is exactly how the API is implemented. Incidentally, there's little benefit as pretty much any access to PEB except for registering __except handler is a red flag and worthy of investigation. Also, and easy way around this is to clear that flag (stored in the PEB as mentioned) at startup.

Quote:Again, Elad's book is brilliant, so I'd grab a copy of that if you can.

Unfortunately it's not available in Uni library and there's no room in the budget.. too bad.

Quote:rdtsc
push eax
ret

Have fun tracking that one down ;)

You'd have to wipe out the current stack frame (otherwise cracker gets a backtrace directly to your clever code, which is counterproductive!), but idea is promising.

Quote:Another evil trick would be to use functions for read-only data arrays (implemented using 'naked functions' with the content being solely "__asm __emit #" or equiv) and/or crafting code manually and putting it in data variables.

Snag: .text is read-only; you'd have to use VirtualProtect (another red flag of cleverness).
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
Quote:Original post by Jan Wassenberg
[...]
Quote:[...]read-only data arrays[...]

Snag: .text is read-only;[...]
Not exactly a problem =-)

"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
Quote:Original post by leiavoia
Quote:Original post by _Sigma
__try{  _asm  {	pushfd	or dword ptr [esp], 0x100 //set the trap flag	popfd	nop  }}__except(EXCEPTION_EXECUTE_HANDLER){	bExceptionHit = true;}if(!bExceptionHit)	printf("A debugger is present");


I'm not the least bit familiar with assembly. Can someone walk me through this little bit and explain what it does? Very interesting!

Are you familiar with what the 'stack' is? Its pretty much just the ram that your app has been allocated. It's first-in-last-out.
PUSH blah
pushes blah onto the stack
POP blah
pops blah off the stack.

PUSHFD pushes the entire contents of the EFLAGS register onto the stack. So this is done to preserve the state of the EFLAGS register.

or dword ptr [esp], 0x100
That just sets the trap flag.
" Trap generating instructions generate an exception that transfer control from software (usually application programs) to the operating system."

I'm not 100% on the basics of the trap flag tho, so if someone wants to add to this.

POPFD will just pop the contents of the EFLAGS register back into the registers.

NOP is just "No operation" aka 0x90 in hex.

__except(EXCEPTION_EXECUTE_HANDLER)
{
bExceptionHit = true;
}

this just attempts to trap the exception raised by setting the trap flag. (this is like a try-catch block)

So ya...does that make sense?

Quote:
That is exactly how the API is implemented. Incidentally, there's little benefit as pretty much any access to PEB except for registering __except handler is a red flag and worthy of investigation. Also, and easy way around this is to clear that flag (stored in the PEB as mentioned) at startup.

Yes, but this is much less likely to be spoted. Guaranteed it'll stump someone for a few minutes, and 100% it'll blow the mind of a shitty cracker.

Quote:
rdtsc
push eax
ret

I don't get this...Why is this bad?
Ever hear of NOP? It kills all your futile security attempts.
Try EXECryptor from http://www.strongbit.com. The 2.x serie remains uncrackable since its inception on July 2004. Also they frequently improve it to keep up to date. I think this is the strongest among commercial protection schemes.

Regards
Bill
Quote:Original post by Anonymous Poster
Ever hear of NOP? It kills all your futile security attempts.

Eh? NOP = No Operation.
Quote:Original post by _Sigma
Quote:Original post by Anonymous Poster
Ever hear of NOP? It kills all your futile security attempts.

Eh? NOP = No Operation.
Any test you put in can be replaced by a number of NOPs that takes the same number of bytes.

Machine-code obfuscation is probably the best bet as far as a one-click type of solution, since it make it very difficult to understand what needs to be removed and what the correct way to do it would be.

Next to that, I think the best option might be to put all the source code into a single file (including those portions of the CRT that are relied upon), because that would help the compiler's optimizer (which in MSVC is much more advanced than the one in the linker, it seems) out and it would make 'function library signature' packages fail to match most of the standard-library functions. If you add in a change of calling convention (such as from "cdecl" to "fastcall"), certain optimizer settings ("Global Optimization" on, "Inline Functions" to highest setting, "Intrisics" to on, "Omit Frame Pointers" to on, all runtime checks disabled, if at all possible RTTI and C++ exceptions disabled), and certain linker settings ("/Fixed:X" where X is 0x80000000 minus the size of the executable in memory, "/GY /OPT:REF,ICF=20 /SAFESEH:NO"). The idea is to have as much information as possible removed from the executable, so that reversers have a much more difficult time figuring out what each function does.

Also, be careful with error messages. You should avoid error messages like "SaveGameChecksum: Unable to verify checksum of save game %s" and should instead go for something like "Invalid or corrupted save game". The reason is that the latter gives away less information about what is being done. If you want to get usefull logs to help debug problems, use error codes instead of names, such as "Error 392: Unable to load save game". That gives away even less information, but could still be usefull to you since you'll have a table of codes to descriptions.
The reason for this is that a good debugger will show which strings are referenced in each function, and seeing an error message prefixed by a function name means the reverse won't have to figure out what the function does because you just labeled it for him/her.
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
The checksum will detect if anyone changes a bit of your code. If they place NOPs in all your fancy debug detecting code, you'll detect that before they even get a chance to execute the NOPs.
-------Harmotion - Free 1v1 top-down shooter!Double Jump StudiosBlog

This topic is closed to new replies.

Advertisement