Advanced protection schemes
(How to defeat us crackers at our own game :-)
by tibit
(13 December 1997)
Our protections
programmer's corner
Courtesy of fravia's page
of reverse engineering
Well, I'm probably not yet accoustomed to the PEARLS that the
web can give us... out of nothing here come tibit with some ideas
for protectionists which, if implemented, would 'cut the leg' of
great part of the crackers around (and may be some +HCUkers too... :-)
Why? Because the protection approaches that tibit describes are based
on very SOUND ('historical') fundaments, and many 'trend crackers' still
believe that dos is a pathetical old operating system, that they don't
need to study... Ah!
Head tibit's words:
analyze 1-2-3 for dos "diskette" protection... it's only
dos code, it's probably 100 times cracked already, but there
are some niceties to learn from it.
Not a very good protection scheme, has many holes in it, but
remember what +ORC said: a cracker well educated in historical
code is nearly always a perfect one :-)
Quite right! Unfortunately I doubt that many of the readers will
ever know what 1-2-3 is (not to mention the 'As-easy-as' spreadsheet :-), since Micro$oft's excel will have
already 'lobotomized' them... :-(
Read this essay, and then re-read it, and then WORK on this stuff...
it won't be easy, it won't work at the first try... yet if you are a
serious protector or a serious cracker you must go through this, and
come back!
Hi dear +fravia,
I'm not a cracker myself, but I've been creating some protections inside some of my
scientific research data analysis programs. These were only to protect myself from my
too-lazy coworkers - they would like my programs, and sometimes they were so lazy
that they wouldn't bother writing a 2000 line program, but would spend much time
unprotecting what I did (thinking these protections were of "easy" kind). No luck to
them, though :-)
[note: these programs are not to be found on web, they were for my personal use,
I won't publish them, usual stuff goes here...]
Here are some hints about how one can quite cleverly protect software. They are
not going to be like any kind of "golden rules" or "software protection for dummies".
Some rules given here were already stated in various essays on +fravia's site, but
are listed here for completness. Many of these apply especially to windoze, but can be
"ported" to xwindows (xforms especially) or anywhere else.
1. No nagscreens. Making enemies among your customers is very stupid, indeed.
2. Create important menus and dialog boxes dynamically, whether in something
like turbo-vision, xforms or m$windoze. Use your own format of rcdata like borland
did in delphi if you don't want to write too much code.
3. If your program doesn't save data in "crapware" edition, don't include a "grayed"
menu item. No saving means no saving. That's it.
4. The only way to tell user that he is unregistered should be in the "about" dialog.
The latter should also be created dynamically.
5. Avoid using any kind of string resources like "this crap is unregistered, blah blah"
6. Link statically unless your program is a complex one (at least of m$word 2.0
complexity)
7. Don't loose time on writing anything that will kill disassemblers or debuggers.
Take my word on it - doing it is worthless, people who made them or HCU'ers :-)
will soon find the way around, so shift your interest to more important stuff.
8. Use your language of choice fully. Even if it's visual basic, learn it thoroughly first.
Using some advanced constructs it is possible to make debugging a nightmare.
9. Use run time library fully when writing the beta versions, in final release rewrite
some functions at least to make crackers life harder.
9a) Example: many ocx'es for vbasic take PLENTY of space, and they are really
easy to hook to. Use only ones which you need, usually much of ocx crap can be
rewritten to use plain windows api.
10. Take SOME time to THINK about protecting your software. Is it worth the
protection? Wouldn't it be better to IMPROVE YOUR SOFTWARE, rather than
improving protections?
11. Try to embed at least part of the protection inside the data manipulation.
Data structures can take ages to understand basing only on disassembly listing,
they also are more error-prone for cracker. Crackers usually take notes on loose
sheets of paper, and not everyone reads his own handwriting 100% accurately.
12. A protection that mangles data is a good one usually.
12a) Example: Got a charting program. The crapware shouldn't print. Disabling
printing and later on enabling it basing on some registration# is the most often
committed suicide. Let your thingo print. When creating data structures for
printing, mangle them in some way. Unmangle them just before printing, using
reg# or something other for that purpose. Even more, make this mangling subtle.
Assume that you've got a pie chart to print. Don't alter anything, but add some
not too big random numbers to values of data series - this is mangling then.
The chart will look "not that bad", but will be otherwise unuseable (if the changes
are random and on the order of 20%, for example). Finding such protection, if
its connection with reg# is not self-evident can take much time. One has to delve
inside your data structures and find that dreaded mangling and unmangling code. Not
too easy, especially if you program high level, not in asm.
13. When mangling data, use some tricks so that it is not self-evident where that
reg# comes from into unmangling code.
13a) Example: I did it for a dos platform once. The reg# (in a "go/no-go" protection)
was specified as a command line argument. The command line parser was clean and
it was the only place where command line was ever referenced. In some other place
the "irrevelant" code was using a "bad" version of borland pascal move routine from
rtl (move copies a range of data from here to there). The routine in rtl was altered
to copy a few bytes more and to wrap around the segment boundary. The "irrevelant" code
called copy with parameters set so that it would copy some useless crap together with
command line somewhere else, and that "somewhere" was left unused for some time. It was
evaluated in the wholly other part of program. Athough setting bpx on memory range would
find that, it was not THAT evident that one should place the "enable#" on command line
(it was nowhere mentioned since I needed to protect from my coworkers only).
14. Be economically inclined. Calculate whether you need to write your "to be protected"
program at all. Isn't there (albeit big) a commercial, widely available package that
does the same thing? If you need that tiny program for yourself only, in 99% of cases
it's cheaper to use existing package that supports scritping (to tailor it to your needs).
If you want to sell it, check your market. The problem of protecting software vanishes
if no one will use your software. Don't overestimate your work's "importance to the world".
Search the net. There's nothing like you want to do? Search more, then. Start work only
when you're sure there's need.
15. Always explore your language of choice on the level proposed (or, more often these
times, pushed) by the language developers. If you're real programmer that doesn't want
his "hello world" app to be 2megs+ big, shift down to the api level asap - ie. after
exploring all the nice "visualties" the commercial types might be pushing you to use.
15a) Example: This pertains mostly to things like borland c++, delphi and m$ vc++ -
write your app using the existing user interface crap (foundation classes etc). When
the innards are debugged (hah, they never are, but if you at least don't find any bugs
in the first 20 minutes of post-build testing), rewrite your user interface to use
windows api directly. Do it cleverly, think first, think a lot, and you'll usually get
an app that is at most 1/4 of the original size, and that works much faster. Do you
believe that corel draw 3 was written using nearly pure api? Such a HUGE app? - you
say it's impossible. No, it quite is, ever more in the 32bit world when you don't need
to spend so much time moving data around the 16bit segment boundary (corel 3.0 was for
windows 3.x - it was 16 bit).
16. What does p.15 have to do with protections, you think. Oh well, it does have much.
Since your code doesn't use the "prepackaged" stuff, it's more personal, more custom.
Since each programmer's style of programming is different, it take more time for a
potential cracker to find out what's going inside your app. Alas, all this is useless
if cracker doesn't have to know the innards to deprotect. So always protect in a way
that is tied in MANY PLACES to the innards (data structures etc).
17. A monolithic app that doesn't reference to much more than kernel or device drivers
is usually harder to crack. There are less dependencies to get from watching the .exe
and .dll's with quickview for example. Try to export only the callbacks needed by
windoze for example. Check what goes to the outside world. Try to include no or little
details of registration procedure. Your code should give as little information in "clear
text" to the cracker as possible.
18. For god's sake, don't give away the working code! Try to provide users with:
- The crapware version, that DOESN'T have the "missing features" compiled in. This thing
should also have no nag screens nor "enter reg# here" suff - it's obvious, since it's
the CRAPWARE ONLY.
- The full version, that HAS the full functionality compiled in but that also requires
user to enter the reg# once somewhere. This version should be only given away to those
that have paid. At least there is no way to get the thing to crack from your site, since
you only expose the crapware for lamers. Alas, your "full" version, maybe even cracked,
will be to be found shortly on warez sites, but not everyone knows where to search,
so you at least can be proud that your app has been stolen by "the enlightened" :-)
19. As has been said many times: don't overuse the "registered", "unregistered",
"registration code", etc. words - both in clear text and in encrypted form. If possible,
invent some clever algorith to generate these on the fly basing on some variables,
definitely don't use the "bool Is_Registered" flag anywhere!
19a) Notes: It would be quite nice if the program will be having some sample (bad?)
reg# stored inside in some place(s). The user, after entering the registration#, should
have no IMMEDIATE indication of any kind that the software is registered now. THis
indication shouldn't also be deferred using timer, like in unix login. It's childish
easy to find the "sleep x" code. Each function, like save, print, etc. that depends
on registration, should check the reg# by itself, and it should do it in such way
that user nor cracker doesn't see that something is going wrong or good. As said before:
no disabled stuff. If saving is disabled, either don't provide it at all, or make
the unregistered save work just like real one, just that data isn't written to disk
or is written badly. Consider such scheme: user isn't presented with any "this funct
disabled" box, the save works ok (save dialog opens, save completed "%" bar scrolls
as usually), but you may for example lseek back to the beginning of file after writing
each record if the user isn't reg#. This approach is hard to crack since such attempt
requires to delve DEEP into the program's internals and to find what is going bad
and where. People also think sometimes that their version is simply damaged and throw
their program away, whereas that dreaded save might work ok when registered.
19b) Notes: CLEARLY INDICATE facts mentioned in 19a), like that:
<<"Reminder: This program, when not registered, won't save nor print any data.
These two functions will operate ONLY when the CORRECT registration number is
entered in the registration dialog box.
Warning: we restate that save and print options WON'T WORK until the CORRECT
registration number is specified. All other features are unaffected in the
unregistered version.
Note: You cannot verify that correct registration number has been entered.
Program doesn't display any warinings that you've entered invalid number.
You know that it is ok when save and printing works, else please retype the
registration number and try again.">>
This will indicate:
a) to the user that he definitely WILL enable the program when the CORRECT number
has been entered,
b) to the hacker that it is going to be a tough job since the registration #
checker sits somewhere else.
19c) Implementation notes: Move your reg# to the checker routine in an unsuspicious way.
If you can, don't use the "existing" textbox and gettexta, implement the reg#
entering routine by hand, it would be quite wise even to refrain from using the
existing text rendering procedures and render the number on-screen as it is
entered using your own renderer. You can edit your own font resource or use
some preexisting font, then just encode it in some way so that it will be
unreadable by resource workshops, even better include it in the source as
static constants. Although it is still easy to find where the "drawbitmap"
equivalent was used to paint the reg#, it is definitely harder to understand
the whole underlying routine, especially noticing where does that
reg# go. Using a multi-layered approach here is the most feasible one:
- dynamically create accelerator keys in your 'register me' dialog box, these
accells should be for keys used in reg# entry (0-9,a-z for example)
- each accell should call different routine, if feasible (makes breakpointing tougher)
- each routine should store the flag that given char was entered somewhere else,
it would be nice if each keypress would modify some global variable, in some way
that is decodeable for you, but not to the cracker (at first glance)
- then there should be some kind of 'monitoring' routine that acts accordingly,
paining the characters on the dialog box and taking actions upon backspace and enter,
for example
- yet another routine should collect all entered characters and create a reg# and store
it in some untrivial place. using a .vxd here to manipulate the virtual memory to make
some 'backup' copies in a not-easily-debuggeable way is a nice idea. a .vxd can work
like a tiny embedded debugger, bpxing on the place where that reg# goes. it can then copy
it to some quite other place, all that happening in the background and not to be noticed
easily. the cracker will of course (at the first try, at least) try to check where this
location is accessed. since it is accessed nowhere, he will scream: how the hell does this
app know that the reg# has been entered, if it even does not access it? oh well, just
a tiny .vxd or even a background thread has copied it somewhere else. they'll get at
it later, but at first it can stir crackers minds, though.
20. Notes on registration numbers:
- balance between security, feasiblity, programmability and end-user headaches
- too long, non-alphanumeric reg#'s tend to be continuously entered badly. at least
provide a "non-persistent" reg# entry field so that user will rewrite the reg# each
time, possibly correctly at last. many people will just "glance-compare" the entered
reg# and the one (possibly) emailed to them, arriving at the final thought that they
did enter it correctly, whereas the font is too small or they are too tired to notice
that this '1' and 'l' have been interchanged (in a reg# like 'l83jjd_0)pH1lTe' )
- refrain from any user feedback. the reg# entry box should accept strings of any
length, without any validation. don't give crackers the knowledge about reg#.
sometimes knowing that it's 10 chars long or that is contains only uppercase alphabet
helps, so don't help them
- the reg# verification scheme (I'm pretty sorry about it, but it just is like that)
needs to take into account the number of prospective users, and thus you oughta do some
"market analysis"
- if your reg# is 10 numbers long, there are 10^10 possible reg#'s. but since your app
might find let's say only 10^4 (10'000) users, you should invent an algorithm that
assigns each one of 10^4 users one of 10^10 reg#'s, and does it somewhat uniformly. This
prevents people and programs (some .vxd based "macro" players, like the one presented
some time ago in DDJ, for example) to be used for brute force approach. If there are
only 10^4 users and you allow 10^9 "valid" reg#s out of 10^10, on average each 10th
reg# tried brute-force will be valid, whereas on the case of 10^4 prospective users, that
many valid reg#'s and space of 10^10 reg#s, on average only each 10^6th reg# tried brute
force will be valid. Ever calculated how much time it would take to brute-force search
10^6 numbers, even using a fast machine and extremenly fast macro player (keystroke
generator simulating reg# entry and checking for results)?
- the assignment operator that assigns user# to reg# shouldn't be trivial, and it's
implementation should be done in asm by someone experienced both in maths (topology
known by +orc helps here, also :-) and asm. check your operator. create graphs of
how it works. understand your own work, especially its drawbacks and vulnerabilities
- be inventive. don't use anything that seems simple, quick and effective. unless you've
come with something like Einstein's relativity theory, your approach is yes, simple,
yes, quick, but no, not effective, and yes, easy to crack. I'm sorry but we aren't
geniuses and developing a good protection scheme takes time, it took time myself, and
still takes although i'm creating protections for fun since 5 years or so. It will
definitely take some time you, dear reader, and don't believe your self-confidence.
Protections written by self-confident and unexperienced prgrmrs end up in the "most
stupid protection" page of +fravia. a nice and exposed place, and with what a
neighboorhood, but a protection ending there is still the "most stupid". not a nice
definition of your work, huh? :-]'
21. Play same game twice. It helps. If you invent some nice and hard to crack memory
move-around scheme to protect reg# inside your data space, do the same to parts of your
data, even better using the same routine just with other parameters. Crackers are
lived up to the fact that protection algorithms are always used for protections only.
If integrity of your data will rely, at least partly, on proper working of your
protection code, crackers get a tough work to do. it's called functional verification
of the "okayness" of protection code. you don't checksum nor crc it, but simply call
it "from the other place" and let it process the protection-unrelated data. if it
will process this data ok, then cracker mustn't have altered it. this places end on
easy cracks like "change that jump here and that cmp there to nops"..
22. Strainer to you: does lotus 1-2-3 for dos "diskette" protection (it let you install
it up to 3 times without "zapping" it from hd first) work still in win95? if yes, why?
if not, why? analyze the code, I've learnt MUCH from it. it's only dos code, it's
probably 100 times cracked already, but there are some niceties to learn from it.
not a very good protection scheme, has many holes in it, but remember what +orc said:
a cracker well educated in historical code is nearly always a perfect one :-)))
(ok, ok, i know, there's nothing perfect aside from moskovskaya in this bad world, but,
can't we joke at times ? 8-0=)
cheerz,
tibit
23. Ever thought about using free domain names as registration keys? Try www.eu.org,
the waiting time is about a week. Nice way to validate (with a bit of delay) to the
user that he's registered. Attach your company's own dns server together with a backup
one under a free domain, and assign temporarily (eg. for one month or so) a registration
number domains. User's software simply transforms mathematically in some way the
user name and birthdate, for example, into the domainname, and checks for it's existence.
Useful as a step to confirm registration of internet-bound software. Nice, since the
program doesn't need to know what it's looking for, it can just generate dname, fetch
it's administrational guy's name, and hash user name back with it. Since the hash
function can be practically one-way-only, with no good way back, it can create some
QUITE SECURE means of remote registration. Only you, the serwer, needs to know how
what from the hashed thingo was generated. Quite like the unix password crypt mechanism.
It's not that tough from the cryptoanalysis point of view, but a better stuff can
be easily developed, and, btw, who will know that crack needs to be run in order to
register your software ? :-)
[a bit paranoid idea, but who the hell knowz? ]
(c) tibit All rights reversed
You are deep inside fravia's searchlores org,
choose your way out:
Our Protections
homepage
links
anonymity
+ORC
students' essays
academy database
tools
cocktails
javascripts
antismut CGI-scripts
search_forms
mail_fravia
Is reverse engineering legal?