Solved

Crack proofing programs.

Posted on 1997-08-05
18
611 Views
Last Modified: 2012-08-14
This question developed from another I posted a day or so ago concerning inline function emulation.

I want to prevent programs of mine from being cracked. i.e. reverse engineered. This is to do with a copy protection system I have developed.

I have developed a number of schemes for DOS based programs which will also be of some use for other platforms (win16 and win32) but I am always on the lookout for other suggestions.

Don't be too keen to 'answer' this question, I would like to leave it open to get many different opinions. I promise I will give at least 100 points to every method that I implement that I had not already thought about.

I won't enumerate the methods I am already using since someone else may come up with another wrinkle I had not thought about!

I have at least 600 points to distribute on this question if I get ideas that I can use.
0
Comment
Question by:icd
18 Comments
 
LVL 4

Expert Comment

by:md041797
ID: 1166787
It has been shown that the effort it takes to reverse engineer a system will exceed the effort required to write an equivelent system from scratch.
0
 
LVL 5

Author Comment

by:icd
ID: 1166788
That may be the case, but if the program is preventing unauthorised copying of other programs and data then it might very well be cost effective for a professional cracker and an 'interesting challange' to a 'hobby cracker'

0
 
LVL 1

Accepted Solution

by:
mosfet earned 200 total points
ID: 1166789
One of the best ways to trick a decompiler is to make it get confused on what is code and what is data. Most decompilers step through the program and mark code as anything that is executed and data as anything that isn't executed. So, to trick it, make code look like data. For example, you could have the beginning of your program load the rest of the program somewhere else in memory, and then execute it from memory. It will think all the code you loaded to be data. An example of a common program that a decompiler would be confused on would be an interrupt handler. The decompiler thinks the interrupt handler is data because it is never executed, but is loaded into memory and then executed from there.

If you can use assembly, this is probably the best way to decieve a decompiler:

instead of writing

call SubProc

you can create the following reference

SubRef dw offset SubProc
call SubRef

Now the disassembler won't see the procedure SubProc. So, by creating massive tables with procedure addresses, or by using object-oriented programming techniques, you can protect your code from the disassembler more efficiently. If you don't know assembly, sorry, when you do a "call", you are calling a procedure, which is like a function.

You can also encrypt your code, and have the decrypter at the beginning of the code. When the whole program is loaded into memory, the decrypter will decrypt the rest of the code. I like to encrypt by using xor.

There are also some tricks to fool debuggers like MS-DOS's debug.
Protection from debugging is different. You can't fool them by an implicit call.

This assembly program will normally work if you just compile, link, and then run it. But, if you try to debug it, it will most likely crash. It declares the stack segment as the code segment. When it runs in DOS, it says "hello" and terminates. But when the debugger loads and starts the program, it uses the program's stack, and 10 bytes of stack aren't enough for the debugger, so the stack overflows and the code that comes before it is overwritten. The program says nothing and dies.

.model large
.data
 mess db 'hello',10,13,'$'
.code

mov ax,@data
mov ds,ax
mov ax,cs

cli
mov ss,ax
mov sp,offset stck
sti

call tell
mov ax,4c00h
int 21h

db 10 dup(?)
stck:

tell proc near

lea dx,mess
mov ah,9
int 21h
ret

tell endp
end


This next assembly program could probably be included into a C++ program with inline assembly. It will detect if a debugger is being used by checking for tracing mode.

.model small
.data
 mess db 'We are not being traced',10,13,'$'
 trace db 'We are being traced',10,13,'$'
.code
 mov ax,@data
 mov ds,ax

 lea dx,mess
Modify:
 mov byte ptr cs:m+1,0
m:
 jmp short norm_ex
 lea dx,trace
norm_ex:
 mov ah,9
 int 21h

 mov ax,4c00h
 int 21h
end

A very nasty way to kill the debugger is to tarnish interrupt vectors 1 and 3 (which control the tracing mode). If you zero the vectors, the debugger(and the system) will crash.

 xor ax,ax
 mov es,ax
 mov es:[4],ax
 mov es:[6],ax

If you install your own INT 01 handler, the processor will stay in trace mode, but the debugger will lose control over your code. The program will run without stopping.

Hope these help!

Tell me if you need help with the assembly. If you don't know it, LEARN IT!! I feel it is a necessity of any accomplished programmer.
0
 
LVL 5

Author Comment

by:icd
ID: 1166790
Adjusted points to 200
0
 
LVL 5

Author Comment

by:icd
ID: 1166791
mosfet.

Thanks for that comprehensive reply.

My code is predominantly C code with some in-line assembler so I don't think I can use your technique for using the code segment as the stack segment.

I am already using most of your other suggestions, in one way or another, but I was particularly pleased with the technique to detect trace mode. It took me about five minutes or so to work out how it worked, I assume it is to do with instruction caching.

As you can see I increased my points for this question in line with the quality of your answer.

I was about to grade you an 'A' but I thought I would test your program first! I find that the following code only ever prints the value '1'.

#include <stdio.h>
void main(void)
{
      int      trace;
_asm
      {
            mov      ax, 0
            mov byte ptr cs:m+1,0
m:
            jmp short n
            mov ax, 1
n:
            mov trace, ax
      }
      printf("Trace is %i\n", trace);
}

The program is compiled in small model and looking at the code the assembler seems to match your own. The jmp address would seem to always be set to zero whether the program is run in a debugger (softice) or not.

Does this program work on all 80*86 processors? I can't use a routine that is un-reliable.


0
 
LVL 5

Author Comment

by:icd
ID: 1166792
Drat. I did not intend to grade the answer just yet.

You stated that by installing my own int1 handler the debugger would lose control over my code. I always assumed it was the other way about, the debugger would take over the int1 and my own int1 handler would not run.

I suppose it depend on who got in there last. If you try to single step through the code that takes the int1 vector then you lose control but if you put a breakpoint in the code at a point after the vector has been taken and *then* start to single step then the debugger has control.

0
 
LVL 1

Expert Comment

by:mosfet
ID: 1166793
Regarding what you said about detecting trace mode, that isn't true. This is how it works:

The instruction at the label Modify zeroes the jump distance in the instruction that follows right after it(jmp short norm_ex). Remember, a jmp short is a distance jump, its value isn't the address it is jumping to, its value is added on to the current executing address (cs:ip). So when you change the value to 0, it is jumping 0 bytes, so it doesn't go anywhere. When tracing mode is off, by the time the modifying code command has been executed, the jump command has already been loaded into the processor's command pipeline, so it changes nothing but memory. The code in memory has been changed, but its already been loaded before this. So instead of jumping 0 bytes, it goes to the normal execution. But when trace mode is on, instructions are loaded into the pipeline one at a time, so the code does get modified and the other message is loaded.

All this really isn't something most people would know about just by looking at this code. Don't bother using a debugger on C code though, it will probably take forever to find the code. Its better to fool around with this with an assembler.

And about the last thing you said, of course the debugger takes control of int 01, but that's only in the beginning. It then loads your code which deletes the handler. This won't cause problems when the program is normally executing out of a debugger.

If you'd like a better understanding of assembly, I suggest getting the book "Revolutionary Guide to Assembly Language". And if you would like to continue learning about more advanced topics like the ones I was just discussing, I suggest "Assembly Language Master Class".
0
 
LVL 1

Expert Comment

by:mosfet
ID: 1166794
I think I deserve more points, just look at all I typed.
0
 
LVL 1

Expert Comment

by:mosfet
ID: 1166795
Nevermind, didn't see that you gave me that much.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 5

Author Comment

by:icd
ID: 1166796
Now now mosfet don't be greedy, it is quality not quantity that counts :-)

I did actually understand the reason the trace detection worked.

I also have more findings on why it does not work on my machine. It works on 486 (and presumably earlier) but does not work on pentium or pentium pro. I assume that Intel thought that this 'feature' was a bug and fixed it on later processors.

So it looks like I will not be able to use any of your ideas (that I was not already using) and 800 quality points would seem to be a bargain :-)

0
 
LVL 1

Expert Comment

by:mosfet
ID: 1166797
It probably doesn't work on Pentiums because, as you probably know, Pentiums have a dual pipeline that allows instructions to be executed in parallel, so it read ahead even more.

And what are you talking about not being able to use any of my ideas. All you have to do is get some CPUID code to detect the processor and use my technique if not a Pentium or later.

Also, another idea of mine you probably didn't notice had to do with int 03h. Get rid of this and debuggers won't be able to use break points.

And I would suggest trying to defeat debuggers rather than disassemblers. Disassemblers are a lot less reliable than a good debugger. I've disassembled simple programs that just say "hello" and the disassembler mostly prints out junk. Most people use debuggers anyway.

Also, I would suggest searching on the internet for sites that teach you how to crack games/programs. No better way to defeat a cracker than to learn how they do it.
0
 
LVL 5

Author Comment

by:icd
ID: 1166798
OK, perhaps I could use some CPUID code as you suggest. There might be some benefit in doing so but I would expect most crackers of my program to have a fairly high powered machine anyway. There are however some other problems.

If there is an interrupt after the mov byte ptr cs:m+1,0 instruction but before the jmp short norm_ex instruction then the program will assume a debugger is running.

I *did* notice your int3 comment. I use both int3 and int1 interrupts to do useful functions in my program already. So either it will bugger up a debugger or my code will not be executed and hence fail (I don't care which).

Debuggers is indeed the target of my work, I am not too worried about disassemblers for the reasons you stated.

I have been looking at a few crack sites, but they don't seem to stay around for very long! The site http://www.x86.org/ also looks to be a good site for useful wrinkles on how the intel chips work.


0
 
LVL 1

Expert Comment

by:mosfet
ID: 1166799
Who cares if there's an int there? It shouldn't be there anyway, you don't need one there because all it's doing is detecting trace mode. And if the debugger puts an interrupt there, GOOD, then you probably just prevented a breakpoint from working right too.

And if you install interrupt handlers on 01 and 03, don't use a chain interrupt function. Now it will "bugger up" the debugger anyway. You shouldn't use chain interrupt functions in the handlers for 01 and 03 anyway because they're only used by debuggers.

Also, my technique for detecting trace mode can be used in another way. You can change the code slightly so that the debugger will start executing the instructions wrong.
Example:

If you have something like

EB 00               jmp start
                   start:
B8 00 00            mov ax,0

you can use my technique to trick the debugger into starting at the 00 instead of the b8, so it will interpret the rest of the code entirely wrong. This is almost like taking vengeance on someone for debugging your code. They could spend hours and hours trying to debug your code, except they're not really looking at your code, they're really looking at jumbled data interpreted as code. This would be one of the best things to do because you could also end up crashing their debugger.
 


 

0
 
LVL 5

Author Comment

by:icd
ID: 1166800
Sorry, I did not explain myself precisely. I mean a hardware interrupt such as the clock interrupt not a software interrupt.

0
 
LVL 1

Expert Comment

by:mosfet
ID: 1166801
Ohh, I think you could fix that by doing this:

sti   ; disable interrupts

; Rest of code here

cli   ; enable interrupts
0
 
LVL 5

Author Comment

by:icd
ID: 1166802
Not in a DOS box under Windows 95, these cause exceptions which result in the disable being 'faked'. Windows 95 can still interrupt.
0
 
LVL 1

Expert Comment

by:mosfet
ID: 1166803
You can get around this by making your own VxD.
0
 

Expert Comment

by:wy2lam
ID: 1166804
There is nothing as a truely "uncrackable" program.
It is just a matter of time before your protection scheme is cracked (hint: the more challenging your scheme is to the crackers, more vigorously they'll take on it)

I'd advice anyone who's about to employ any copy-protection scheme to spend more time improve the program itself.
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. A…
Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now