Anti-Debugging & Software Protection Advice
Caveat Emptor "let the buyer beware": For this entire session please understand one thing at the
very outset; unless you plan on dedicating a significant portion of your life to writing the
ultimate protection scheme and not working on your product - understand that it is simply a
matter of time before someone finds its weakness, you can then look forward to many
(frustrating) years of trying to shut open doors.....I'm definitely not saying that it is pointless to
protect your work, but don't overestimate its importance, many of the most widespread and
profitable programs in use today have lousy protections (think mIRC/WinZip) but still generate
more than enough revenue for their authors. The last thing in the universe I'd like to see is my
HD further cluttered with authors protection monstrosities.
From my site you can clearly see that virtually every serious cracker/reverse engineer will use a
debugger or disassembler of sorts to break your application, of those tools out there I'll wager a
substantial amount that SoftICE is the ring 0 "debugger of choice" although due to lack of
quality control and subsequent abandonment of SoftICE by Compuware, many have switched to
using the capable ring 3 alternative OllyDbg. Modern day programmers have still not really
caught up with "crackers", maybe they just consider it a wasted cause?, or perhaps their desire to
roll out their latest software overrides any desire to protect with any degree of competence.
Please understand that any claim of an 'unbreakable protection' just isn't credible, similarly any
claim of 'new or unique patented technology' should be avoided like the proverbial plague, this is
usually marketing speak for 'untested', (I could probably write a whole page dedicated to terms
used by copy protection vendors with their real meanings but I'll refrain for now), a few years
back there were a glut of sites on the web offering the "ultimate in unbreakable protections", to
my knowledge not one remains that hasn't been made to eat its words. Using an off-the-shelf
protection e.g. Armadillo, ASProtect, ExeCryptor (these are some of the best) may help to raise
the individual knowledge bar required to break your application, yet frequently this is a false
economy, at any given time there might be thousands of people attacking generic instances of
these protection schemes and the odds are very good that one eventually finds a way through.
The worlds hard-core of crackers are determined, motivated and very resourceful, they might
also be situated in places where the the 'money to time' ratio is not necessarily a factor, to think
that you can beat them all is absolute folly.
Most of the following tricks require implementation in Assembly language, evidently that isn't a
problem if you are familiar with in-line ASM coding in your compiler environment (sadly many
HLL programmers are not). Most of these examples are designed to thwart SoftICE users
(virtually all of the existing documents on the web describe tricks which would have been useful
in the days of DOS Debug/Turbo Debug but are happily traced by SoftICE).
Bear in mind that the use of these techniques will only add time to a crackers progress if he/she
doesn't actually realise the debugger has been detected (use at least 2 techniques to make sure
you aren't penalising legitamate users and don't show friendly message boxes like VBox's
"Debugger Detected, please remove me screen"). Hidden detection can work very well if used in
shareware key generation schemes, set a discrete flag and then send the would-be cracker to a
routine that requires hours of analysis time for no result, eventually time is money for the best
reverse engineers.
1. - Using the API function CreateFileA to check for the presence of the SoftICE vxd/sys device
or its display driver (\\.\SICE, \\.\SIWDEBUG, \\.\SIWVID, \\.\NTICE in Windows NT). This
method is now very well known, although you could try using _lread() instead (ASProtect).
Some protectors also check for the presence of well known debugging and monitoring utilities
for example (\\.\SuperBPMDev0, \\.\TRW, \\.\FILEVXD, \\.\FILEMON, \\.\REGVXD,
\\.\REGMON).
Bypass by bpx CreateFileA (change API result) or HEX edit the strings from the detection or
patch your nmtrans.dll / Winice.exe against them. (Seen in Advanced Disk Catalog v1.16,
MeltICE, Hardlock/HASP envelope).
Another variation of this trick is to check if the NTice service is running using OpenServiceA.
In SoftICE v4.3.2 this detection method no longer works as internally the SymbolicLink name of
NTICE has a 4 digit number appended (this is based upon the serial number used to install
SoftICE), a discussion of this is available here courtesy of SoftICE guru Kayaker.
Yet another variation of this trick is to check for specific files installed by SoftICE
(DriverStudio), for operating system independence one paper recommends checking the
windows system directory for dstudio.cpl since it is installed under 9x & XP systems.
2. - INT 41h Debugger Notification.
PUSH EDI
PUSH 0000004F
PUSH 002A002A
CALL Kernel32_1 crashing loop. I have to confess I'm not convinced this is
actually effective.
16. PROCESSINFOCLASS == ProcessDebugPort, query to see if there is an active process
debug port.
PUSH EAX
PUSH 23h
CALL ZwQuerySystemInformation
NTSTATUS NTAPI Nt/ZwQuerySystemInformation (SYSTEMINFOCLASS sic, PVOID
pData, DWORD dSize, PDWORD pdSize);
23h -> SYSTEMINFOCLASS == SystemDebuggerInformation and here to see if there is any
kernel debugger running.
17. StopIce (anti-SoftICE protection for Delphi applications apparently). I don't want to get at
the person who coded the StopIce v1.0 deprotector, but to put it mildly this tool is for people
who can't use a HEX editor, included in the archive is my 32 byte example.exe (view it in a HEX
editor and have StopIce deprotect and reprotect it), you get the idea ;-).
StopIce detection works via very well known methods, CreateFileA for SICE & NTICE devices.
The v1.0 deprotector merely replaces these device names with profane alternatives, all that has
changed in v2.0 is non-profane alternatives and a reprotect option. The interesting feature of the
protector is its apparent payload (if it actually worked), the program calls GetCurrentProcess()
and then ControlService() twice, the first time with 2 missing parameters (thus messing the
stack) and the 2nd time with parameters which I assume are just nonsense because of the stack
being +8 after the first failed call, the intended payload seems to be a call to ExitWindowsEx.
Needless to say if you use FrogsICE/Icedump or even your hex editor this detection is unlikely to
be 'stopping any SoftICE' users for longer than about 15 seconds.
18. Detection from SafeDisc (thanks yAtEs for this one).
secdrv.sys, ANDs dr7 with 500, saves the value and checks it for 500.
.text:00010950 mov eax, dr7
.text:0001095C and eax, 500h
.text:00010961 mov [ecx], eax
Normally without SoftICE, DR7 would have a value of 4xx, for example :
450 AND 500 = 400
412 AND 500 = 400
But SoftICE sets all DR7 values in a 7xx range so :
750 AND 500 = 500
712 AND 500 = 500
So what is SoftICE doing? its setting LE & GE (Global/Local Exact) these are bits 9 & 8 i.e.
1100000000 (0x300), a quick scan of NTICE and we find :
.text:0003BEAC mov ebx, dr7
.text:0003BEC4 or ebx, 30Ch
.text:0003BECD mov dr7, ebx
...
.text:0003BEDD or ebx, 303h
.text:0003BEE6 mov dr7, ebx
..
.text:0003BEF6 or ebx, 330h
.text:0003BEFF mov dr7, ebx
..
.text:0003BF0F or ebx, 3C0h
.text:0003BF18 mov dr7, ebx
Patch the .sys to remove 300h.
.text:0003BEAC mov ebx, dr7
.text:0003BEC4 or ebx, 0Ch
.text:0003BECD mov dr7, ebx
...
.text:0003BEDD or ebx, 03h
.text:0003BEE6 mov dr7, ebx
..
.text:0003BEF6 or ebx, 30h
.text:0003BEFF mov dr7, ebx
..
.text:0003BF0F or ebx, C0h
.text:0003BF18 mov dr7, ebx
I haven't seen any side effects of doing this. Some older versions have an extra OR at the
beginning.
19. Detect single-stepping and turn off the monitor.
MOV DX,3C4 ; Set Port to 3C4 (Sequencer Index Register)
MOV AL,1 ; Set register to access to 1 (Clocking Mode Register)
OUT DX,AL ; Select Clocking Mode Register to be accessed at 3C5
INC DX ; Set DX to 3C5
IN AL,DX ; Get Clocking Mode Register to AL
OR AL,20 ; Set Bit 5
OUT DX,AL ; And rewrite that into the Clocking Mode Register
;
; Bit 5 in the Clocking Mode Register represents:
; Screen Off
; When set to 1, this bit turns off the display
; and assigns maximum memory bandwidth to the
; system. Although the display is blanked, the
; synchronization pulses are maintained. This
; bit can be used for rapid full-screen updates.
a few instructions later ...
MOV DX,3C4 ; Set Port to 3C4 (Sequencer Index Register)
MOV AL,1 ; Set register to access to 1 (Clocking Mode Register)
OUT DX,AL ; Select Clocking Mode Register to be accessed at 3C5
INC DX ; Set DX to 3C5
IN AL,DX ; Get Clocking Mode Register to AL
AND AL,DL ; Reset Bit 5 (AND xxxxxxxx, 11000101)
OUT DX,AL ; And rewrite into the Clocking Mode Register
; Since Bit 5 gets reset, the screen will be turned on
; again.
At runtime everything happens fast enough, whilst during debugging the first sequence simply
needs to be skipped otherwise the screen will be turned off. This trick works only on Win9x due
to priviledge levels.
20. Exit the program if its being debugged by checking execution timing.
0F31 RDTSC
33C9 XOR ECX,ECX
03C8 ADD ECX,EAX
0F31 RDTSC
2BC1 SUB EAX,ECX
3DFF0F0000 CMP EAX,0FFF
EB08 JB SHORT _FAST_ENOUGH
6A00 PUSH 0 ; ExitCode = 0
E80E060000 CALL ; ExitProcess
_FAST_ENOUGH: -> Code continues here
RDTSC is used to retrieve the time stamp counter (number of clocks since boot-up) so this is a
time-related trick. When you debug, the distance between those values that are returned in EAX
will be higher than those when the program runs without being debugged. So, if there is really a
difference you're debugging. The only way around for a cracker is a patch.
21. Anti-tracing trick. More intelligent tracer applications would most likely implement an
instruction decoder in order to follow the execution path, of course special handlers will be
needed for conditional/unconditional jump instructions as well as others. Another possibility
which could fool a tracer is a JMP instruction with a REPE or REPNE prefix. Here is how it
works :
REPE (0xF2) <-- prefix only valid with CMPS/SCAS
JMP SHORT LOC_1 (0xEBxx) <-- JMP length is 2
SOME_JUNK_BYTES_HERE
LOC_1:
RESUME_CODE
When the instruction decoder reaches the REPE it correctly decodes the instruction as REPE
JMP with an instruction length of 3 bytes. If a special handler routine isn't called the tracer will
assume the next instruction to be executed is in the junk bytes and not the JMP to LOC_1. This
trick is used by portions of the Armadillo loading code but will only be effective against single
step tracers, not those using the trap flag. Armadillo uses a SEH handler to detect if there is a
tracer using the trap flag.
22. Anti-IDA trick. I'm not entirely sure if this is a deliberate anti-IDA trick or not, however I
recently encounted a file which had the Virtual Size's of its .data section set to an extremely large
value, when IDA encountered a 100GB Virtual Size it refused to load the file due to lack of
available disk space. Of course this isn't going to stop anyone simply setting the sections Virtual
Size to the Raw Size.
23. Under Windows 2000 & Windows XP SoftICE places an INT 3 instruction on the first byte
of the function 'UnhandledExceptionFilter' apparently for its own internal use, a protector could
well check for its presence.
MOV EDI, UnhandledExceptionFilter
MOV ECX, 6 ; Check the first 6 bytes
MOV AL, 0CCh ; INT 3 opcode
REPNZ SCASB
TEST ECX, ECX
JZ NO_INT3
24. Buffer overflows against OllyDbg.
Format String Bug in OllyDbg 1.10 : http://cert.uni-
stuttgart.de/archive/bugtraq/2004/07/msg00211.html
OllyDbg long process Module debug Vulnerability : http://cert.uni-
stuttgart.de/archive/bugtraq/2005/03/msg00337.html
Anti-VMWare
25. Detect if running under VMWare (not an anti-debugging measure per se but useful
nevertheless since a lot of attackers like VMWare as an environment), if VMWare is running
EBX will be non-zero.
MOV EAX, 564D5868h ; 'VMXh'
MOV EBX, 0
MOV ECX, 0Ah
MOV EDX, 5658h ; 'VX'
IN EAX, DX
26. Detect if running under VMWare or any other Virtual Environment by querying the location
of the IDT (since SIDT?can be executed at ring 3 and does not generate any exception this is a
reliable detection method). Unlike SoftICE?which reloads the original IDT VMWare simply
relocates the IDT to its own internal location. This method is better known as the "Red
Pill?Method" and is best described here.
SIDT FWORD PTR opIDT <-- Store IDT.
CMP DWORD PTR [opIDT+2], 0F0000000h <-- Check IDT location (possibly Virtual PC
check).
JLE _check_virtualpc
CMP DWORD PTR [opIDT+2], 0FF000000h <-- Check IDT VMWare.
JG_vmware_detected
check_virtualpc:
CMP DWORD PTR [opIDT+2], 0D0000000h <-- Check IDT Virtual PC.
JGE _virtualpc_detected
Other Resources
CrackZ's Collection of Anti-Debugging Resources - A lot of historical references in here which
might not be useful as most authors are not going to want to implement DOS tricks to defeat real
mode debuggers. Windows authors should read Stone, duelist, the_owl and r!sc's works as these
are easily the best tricks you can use (174k - 178,467 bytes).
Daemons Page - Lots of anti-debugging methods, sample code (web archive) (2.09Mb).
FrogsICE - by +FrogsPrint (assisted by +spath), an anti-SoftICE detection tool for reversers.
Please note his page is MSIE hostile and will crash your browser.
SoftICE Cover v1.0 - Tool for hiding SoftICE against some of the common tricks above
(9.61Mb's).
Software Protection Analysts inc.
Top Protection Tips for Software Authors
I'm sure many authors who stumble across reverse engineering sites regard them as little more
than 'how to crack' repositories, or terrorist training camps if you want to work for the BSA or
DHS. Yes, software piracy does cost most authors a proportion of their potential revenue and I
can sympathise with this point of view but try not to let piracy concerns be the be-all and end-all
of your life. In the real world of computer software there is probably very little you as an author
can do to prevent the really determined pirate from breaking your protection, or not without
spending significant sums of time and money, even the ultimate protection can and will be
potentially compromised by a legitamate user doing some research.
Preventing the wide scale distribution of cracks for your program can actually be a worthwhile
exercise, use a search string such as "warez + crack + your application name" in AltaVista and
several of the other engines and you'll probably be able to find out if and where a crack exists for
your software. Remember that most 'crack' sites are operated by bored teenagers and hosted on
anonymous free space providers, most of these have Piracy@ e-mail addresses that will remove
the offending site within a day or 2, just devise your own 'piracy e-mail letter' and send them off
at the click of a button.
Below you'll find a collection of protection tips, these won't make your program bullet proof, in
fact at best they'll guarantee you not making the worst mistakes and maybe waste a few hours of
a crackers time if nothing else, if you want more details regarding implementation drop me an e-
mail.
I've always been convinced that knowledge is power, have a read of this piece from Kudzu's
World, entitled Piracy and Unconvential wisdom if you are a developer, it might give you some
hope.
Top Protection Tips or 'things to avoid'
1. Virtually all serious crackers will use SoftICE to break your protection, use the undocumented
INT 3 interface between SoftICE and BoundsChecker to detect its presence (or other routines),
you can then decide upon a suitable form of action should you so wish. However, remember that
most anti-debugging tricks are well documented and may be circumvented already at the OS
level by existing tools, ultimately with any anti-debugging trick a way around will be found.
2. When writing your protections functions try to avoid using very obvious names like these that
I've seen - (Auth_Check(), FindDK47(), IsValidSerialNumber() ). Instead frustrate crackers by
using less intuitive or even misleading names, never place your entire protection inside a single
dll or function.
3. Avoid issuing helpful message boxes period, and avoid "Thanks for registering" ones as well,
most legitimate users will type their serial numbers correctly first time. Encrypt trivially any
message strings and others that your program uses, since suspicious looking data will attract
attention.
4. If you must track registered status using the registry use several keys and don't use names like
"MySoftwareKey', instead use some sort of encryption to decode them. If you decide to use a file
based protection, avoid using *.ini and *.reg, use something like *.sys and *.vxd and make sure
they are of credible length and name. Please don't fall for the old tip of "give it a dll extension
and shove it in the /system / windows directory", it will be very easily isolated.
5. Avoid commercial *wrapper* protection schemes e.g. PreviewSoftware's TimeLock, VBox,
ZipLock, Release Software Corporation's SalesAgent, Ken Nesbitt's ShareLock, CrypKey,
FLEXlm like the proverbial plague. All of these protections have been generically cracked, you
might as well give your software away.
6. Forget about protecting using runtime-limits or 30 day trials, these are too easy to crack, cmp
eax, 1E - jg times_up. If you must use this means have the program disable itself completely
perhaps by deleting critical files. Don't waste your time checking file time stamps, you might at
best add a few minutes to a crackers progress and at worse generate a lot of false alarms.
7. Many crackers struggle with applications written in Visual Basic and Delphi, primarily
because the run-time dll's are a mass of spaghetti code which makes isolating your protection
more difficult, in some cases it can make patching virtually impossible. If you must use these
languages, using hyphenated serial numbers like xxx-xxx-xxx-xxx-xxx make for painful
reversing, especially via the various Trim$ functions VB provides. If you use VB always
compile your final product to p-code.
8. Bore potential crackers to death with routines using lots of maths and fake checks. Force them
to work back through your mass of maths (all to no avail). Mix your flags, in function 1 use
return 1 for good and then in function 2 use return 0 as good, this will prevent crackers from
heuristically reversing jumps.
9. Save disabling your target?, don't just grey the menu item, take out the code completely and
don't provide helpful documents detailing the precise specification of the file format or it could
be added back in (only advanced crackers will invest the time doing this).
10. Use encryption of any description, a simple XOR will add about 5 minutes to a crackers
progress, commercial and secure schemes like RSA/SHA/ECC etc. can add hours to a crackers
progress, it is worth studying the theory of some of these algorithms if you use them and
modifying them, since many of the algorithms use constants which can easily be searched for in
a disassembly listing.
11. Use CRC or integrity checks to ensure that key sections of your program have not been
modified, its not a good idea to CRC the entire file for performance and vulnerability reasons,
when you find an error be aware this could be due to virus tampering. It might also be worth
reading a copy of your program from memory and checking for modifications there, again be
subtle.
12. Pack your executable using a commercial or freeware packer (UPX, use older versions which
don't support the -d switch). Yes, there are a zillion unpackers for most packers, but many
wannabes struggle with unpacking without automated tools and it may waste some of their time
identifying which one to use.
13. ...And finally, invest the money and buy a copy of SoftICE, then attempt to reverse engineer
your own software or ask web crackers to try their hands. Make changes at ASM level if
necessary and strengthen your protection. The longer it takes you to protect, the longer it will
take a cracker to de-protect.
Whilst no protection will ever be completely cracker proof, treat software protecting like you
treat your car (or any other prized possession), if you can implement enough deterrents most
thieves will find an easier target or they will wait until a more skilled thief decides to turn his
hand too it.
I'd really like to add more advice, so if you want to post me a small text file I'll certainly add it
here.
Pavol Cerven's tricks
The following tricks (in addition to those I devised above) are taken from the book "Crack proof
your software", by Pavol Cerven, whilst Pavol might be a fairly competent assembly language
programmer, the additional tips provided here are of somewhat debatable use (to say the least),
my comments are in italics.
1. Don't start the registration number test for several seconds or even minutes after the
registration. This will make it much more difficult for the cracker to trace the application in a
debugger. Better still, require the program to restart before testing begins.
Don't waste your time doing this, it will be very easy indeed for a cracker to find your time-
wasting routine and remove it, restarting after registration is similarly pointless, he'll be running
a file and registry monitor and in a short time where you've hidden the required data will be
revealed.
2. Use checksums in your application. If you test your EXE and DLL files, and even other files,
for changes, crackers will be unable to modify them with patches. However, they will still be
able to modify the code directly in memory, so it is a good idea to test for changes to the
application code in memory, as well.
He'll simply update the CRC with the correct value, pointless, although he has to find it.
3. Put your registration information in a non-obvious place. If you keep the registration
information in the Windows registry, it can be easily discovered. Instead, keep it in a file that
nobody would suspect would contain the registration, like a DLL. By placing such a DLL into
the Windows system directory, you will make the registration situation very confusing for the
cracker. Even if a cracker uses a monitor to follow access to files, it may take a long time before
he understands that the registration is in such a file. Better still, locate the registration in more
than one place to make it likely that the protection will be removed incorrectly.
Discussed above, it'll be found in a few minutes via a trawl through the file accessing logs.
Firstly we'll delete all reads not performed by your application and then we'll analyse the rest,
crackers are very familiar indeed with the windows set of dll's and will probably spot yours a
mile off.
4. Encrypt the registration. Encrypt your registration using a technique that is dependent on the
particular computer's hardware to make it impossible to use the file to register the program on
another computer. (It's a good idea to use the serial number of the hard drive.)
Its a very silly idea to use the HD serial number since (if really need be) it can be changed
anyway. Key generators will soon appear regardless.
5. Don't be afraid of long registrations. The longer the registration file or number, the longer it
takes to understand it. If the registration file or number is sufficiently long, it may contain
important information that the program needs, and if such a program is patched, it will not run
correctly.
This one belongs in the "if you can bore them sufficiently" category, don't bet your life on it,
someone invariably has more time than you and will find the way through. Bore crackers by all
means but don't overdo it, if it looks like crap to you it'll look that way to a cracker as well.
6. Test several current bits of data when using time-limit protection, such as file date, system.dat,
and bootlog.txt. If the current date or time is the same or smaller than when the program was run
previously, it will be clear that the time was adjusted. Also, you can save the date and time after
each launch of the program, and then test the current date and time during a new launch.
Once again, often heard, easily isolated.