Here you go:
(01 March 1998)
To Jack of Shadows (JS) and +Seniors:
I'm pleased for many reasons, first, it has been proven that a good
protection can be written in matter of hours with plain assembly using few
or none dirty ready-made tricks at all; second, this protection has
surpassed all my expectations as it was only a game and not a serious
attempt to protect anything, still, it took 4 hours to be defeated, while it
just took 2 hours to be created. Many serious protections, with the aid of
ready-made tricks and hugh amounts of code take fewer time than that to be
cracked.
The protection, as I designed it, has many important leads to
facilitate the cracking process, because as I said, it was not meant to be
uncrackable, on the contrary, it was weak in many (on purpose) points. Both
strings: Reg and Unreg, are very close which other and not in any manner
fragmented or created on the fly. The encryption system used is as simple as
possible, but it will defeat most of wannabes. It is very easy to neutralize
if you realize the encryption key changes all the time and it is word sized
which gives you much more possible encrypted variations than usual (byte
sized). The whole program ends up encrypted with a different key each time
it is ran, so the only way to read it is annihilating the encryption system
or as JS did, creating an ex profeso gadget to do the job.
With the naked program in hand, there are several additional
facilities. First let's talk about the Random code generator. It has 6 main
functions, each one of them is capable of creating perfectly working valid
"senseless" code. The first function creates paired PUSH and POP
instructions. That configuration has the advantage to preserve the stack
pointer unchanged, so the coder can still use the stack without any risk of
corrupting it or overwriting the main program itself. The second function
creates MOVE instructions using as source and destination operands any
REGister except AX or DX because they are needed intact for latter use or
CS, ES, as it can't be done. The third function mixes all up creating sbb,
cmp, xor, etc., except it won't execute any DIV instruction, to preserve DX
as it uses a 16 bits destination operand. The fourth branch creates XCHG
instructions. The fifth and sixth functions create direct addressing mode
operations. First, MOV to REG from directly pointed memory location using a
16 bits random displacement. Notice that this random value could be FFFFh
producing therefore an error because Intel CPU process a 16 bit addressing
mode operation pointing to the last byte of any segment, by loading the last
byte of that segment and the first byte of the following one. The primitive
intel processors would simply wrap around back to zero and load the last and
first bytes of the same segment. That's why the code generator will only use
8 bits registers as destination operand in this function, as it is obvious,
you cannot control the content of every register at any time during
execution ending up probably with a general fault protection (win95 will
snoop that at once). For compatibility reasons the sixth function works
right the same but using the most complexes Intel architecture addressing
modes. I was not sure to include this last function because complex memory
addressing modes like: base plus indexed plus 16 bits displacement are CPU
intensive operations which probably makes this configuration unpractical if
we would want to employ it at major scale in a commercial product. All six
functions are able to create perfectly working code that never touches the
stack, important registers or any memory location. The code is easy to spot
because it performs suspicious operations as PUSHing and POPing the same
register, exchanging values using the same register, etc. It was meant that
way again just to facilitate the job of the potential cracker, off course,
with several few additional precautions and another 10 or 11 more functions,
you could create realistic code hard to fish for the untrained eye.
JS could not explain why the junk registers are cleaned up, he said
that it was an unnecessary action; he stated this, because he knew what the
hell was going on, however, if the potential cracker doesn't understand the
protection and we pretend (by using random code) to simulate a very complex
mathematical process, it would be very nice to add a register initialization
sequence as if the value in these register needed to be cleared before going
any further. Many instructions in a protection scheme are there just to
confuse, that's part of the game, isn't it?
JS could not understand the last segment because it is unfinished,
two hours was not enough to fix everything, it changes the encryption key at
random, stores it in memory, encrypts the whole program on memory, saves it
back on the dead file, decrypts it back to prevent CS:IP from pointing to
junk and the execution continues cleanly. There's an error recovery addition
at the end, just in case some wannabe decides he's to clever and tries to
fools the protection by changing the file name or anything like that,
however, at the end I decided to leave that door open to ease things. JS
found it and changed the filename, that was the way to do it.
The Unreg string is presented using INT 21h, no direct screen write,
no binary level code generation or complex displacement calculation. All of
that, guess why? Again, to facilitate the cracker's job.
Finally, the ultimate code, the one which encodes the meaningful
instructions provides a subtle randomicity system, preventing the main
operations from falling in the same offset every time because that would
make the whole code generator worthless. Still the string displaying
sequence is nothing more than basic.
Even with all the facilities named, this baby made your life
miserable at list for four hours, je, je :-)
There's no point on which language you use to protect, if you do it
because you want to earn money or limit people's choice, you'll fail. If you
do it as an art, you'll succeed. I don't agree with the statement in regard
to the fact that assembly is only useful if you crack DOS targets. On the
contrary, it doesn't matter what language you've used to protect, the
cracker will end up dealing with assembly, I don't have to hook an API using
assembly to probe my point or Do I? I sincerely wish to see more assembly
targets out there, or maybe more clever ideas as +RCG's even if they are not
written in assembly. Remember this, if you program in any language,
depending on how flexible it is, you can play God, if you master assembly,
you don't have to play God, coz you're him...
To JS: You're good, so many years out of the scene and you're not
rusted at all. I wanna hear more about your ideas, so be good to us an write
about some protection you like or anything else you think could be helpful
for us, I'm sure Fravia+ will publish it as he's the best to seize the
brightest stars out there in the cracking scenario. There are bad news too,
the baby's little brother will be delivered as soon as I have some spare
time to code again.
To the +Seniors: Coding in assembly permits you to downgrade your
communication with the CPU at the very binary level itself, where there are
no barriers and all of the CPU's resources respond merely as slaves to your
will. Experiment the sensation of leaving aside your asm interpreter and
talk with the CPU in binary is sensational, there's no limit for what you
can accomplish. +ORC's warned us about junk random generated code being used
as a a mean of effective protection. Encryption is already being effectively
used (timelock for instance) to protect. Random code is not yet a
frequent practice, but we have to teach protectionists to use it, I'm sure
it'll be great fun to crack their pathetic random code.
Aesculapius.