Reverse engineering NetScanTools' protection scheme
("Reverse engineering a good protection: checksums and decoys")

by Hackmore Readrite

(08 July 1997, slightly edited by Fravia)
Courtesy of Fravia's page of reverse engineering

Well, we have already a "Most stupid protection" award, but what do we do in the rare case we find a GOOD protection? The answer is here (from Hackmore Readrite): in sign of respect for the good work of its author, we DO NOT give away a solution that every lamer could immediatly use to crack it...
Unless said protection has been made by the enemies of humanity (aka Micro$oft), we just study and understand the protection scheme (of course) and that's it!

                           Cracking NetScanTools

                  By Hackmore Readrite, DataMiners Inc.


                     Northwest Performance Software
                              PO Box 148
                         Maple Valley, WA. 98038
                           (253) 630-7206
                         FAX: (253) 639-9865

               WWW-main site: 
               WWW-mirror site:
               NetScanTools FAQ:
               NetScanTools Main Page:

File Name:
File size: 1.06 Mb
File Type: Shareware, with three files missing
Version: 2.42 And 3.02

Evaluation period: 30 days for those who have never used it before.
                   15 days for those who have used earlier versions.
                    7 days for those who have used the same version before.
NOTE: The program DOES NOT stop operating after the evaluation period,
      it just keeps "reminding" you that it is NOT registered yet.

   I began cracking NetScanTools version 2.42 to help out a friend of mine
who needed a very good system to PING various addresses on the internet. It
did not take long for me to figure out that the author of this program was
VERY good at protecting his software.

   Within an hour after I completed the crack, (had to "clean-up" my hard
drive), I used the program for the very first time. I stared in horror at my
screen as, before the GUI even came into view, the program informed me that
"a new version is now available for download."

   So I diligently downloaded version 3.0, cracked it in the same manor, but
checked the website for any "bug" fixes before inserting the "cracks" into
the program permanently. I figured right, since version 3.0 was a major
revision of the program, a few "bugs" had been found and corrected, so I
updated to version 3.02 before "hardwiring" my cracks into the program. This
same cracking "technique" will work on any of these versions.

   The protection scheme in NetScanTools is one of the best I've ever seen.
The author of the program, Kirk Thomas, deserves our respect for his great
job. I'm sure he has spent very much time and hard work in perfecting his
protection scheme, and we should ALL pay him the "registration fee" just for
the honor of being able to work on such a great piece of work.

   While I'm on this subject, allow me to point out that Northwest
Performance Software is a tiny company, NOT a huge corporation like Microsoft
or Symantec. Kirk Thomas is just a poor guy just like you and me. 
He deserves to make a living by selling his creations at a reasonable price, 
which he does ($25.00 U.S. is NOT that much!).
He DOES NOT deserve to have his life ruined by people like us turning his 
"shareware" into "freeware", especialy since he has been kind enough to 
let the program keep running, even AFTER the evaluation period. 
Keep this in mind as you work on this program.

   Because of the complexity of this protection scheme, I will teach you what
this program does, instead of "how" to crack it. You'll also understand 
enough to crack it, but you will not be able to break this protection unless 
you are able to find the missing files (and if you are you would have cracked 
the protection anyway). 
This lesson is meant to teach you HOW a good protection scheme works, which 
is important for our studies, NOT how to crack this program, in respect 
for the good work of its author.

   We begin by getting the program (see above) and installing it onto our 
system. Now run the program, notice all of the features it provides. 
Notice that nasty word "unregistered" at the top. And, of course, you can't 
help noticing that UGLY picture on the front of the GUI. Put your sun 
glasses on to shade your eyes from that UGLY picture, because we've got 
alot of work ahead of us.

   Some of you wont mind that UGLY picture. You will be thinking, all I have
to do is get rid of that "unregistered" word at the top. So get out your
hex editors and try. Others may have +Fravia's tool "Borland Resources
Workshop" and will be thinking "All I have to do is load it up and delete
the resource for the "nag" screen." So get out your Toolkits and try.

   If you've tried to change this program in any way, you have found out that
you also had to uninstall it, then reinstall it again in order to make it
work. The program would have told you this when it told you that it had been
corrupted. "Ahh... The date thing!" you say. Of course, it's got to be that.
when you modify a file, the date changes!

   Now you change the file again, but this time you reset the date in your
computer to match the date of the program before you changed it. But you will
still have to un/reinstall. "The REGISTRY!", play with it all you like,
nothing will change. If you change even ONE BYTE in the program, you will
CRASH it, and have to un/reinstall the whole program again.

   We will have to do this the hard way. By THINKing. So take a look inside
the programs directory. You will see the executable program, a bunch of .dll
files, readme, and a .db file. Note the names of the .dll files, they are the
same names of all of those nice features you got to try out awhile ago. This
tells us that we need the EXE to run the program, we need a DLL for each
feature we might want to use, we have something to read at bedtime, AND there
is a database file. That last one looks interesting. Seems like a good place
to store a file date, or a program size.

   Let's get to work. With WDASM, we disassemble the file NST32.EXE. A look
at the "imported files" list shows us he uses KERNEL32, and another look at
KERNEL32 shows us he's using the functions GetFileSize, GetFileTime,
GetLocalTime, GetSystemTime, FileTimeToLocalFileTime, FileTimeToSystemTime,
etcetera. Thats far too many things to waste our time on. Lets forget about
time for now, and take a look at that database file.

   I always like to use HighMemCpy to see whats being loaded into a program
and when. So we'll fire up SoftIce, load the program, and set a breakpoint
on HighMemCpy. We need to find out WHERE NST32.db gets put into memory, and
HOW the information in this file gets used. NST32.DB is only just over 1 Kb
in size, but trying to decipher every byte would take forever.

   Eventualy, we get to the point where NST32.DB is being loaded into memory.
We find it will take up residence between DS:00469738 and DS:00469AE8. Right
after the REPNZ MOVSW finishes up, there are a bunch of instructions loading
even more data into memory. Keep these instructions in mind for later use.

   Right now, we THINK we're looking for a DATE or TIME, but we're not sure
so we'll place a BPR on the whole data block...

   BPR DS:00469738 DS:00469AE8 RW
   CTRL-D (to let the program run)

   We'll break a couple of times adding '0' to 'FF', and then we'll be right
in the gut's of the protection schemes math process. Take off your shoes and
get comfortable, this is alot of code.

:00418DCC 33C9                 xor ecx, ecx              ; clear registers
:00418DCE 46                   inc esi                   ; get start of file
:00418DCF 46                   inc esi
:00418DD0 46                   inc esi
:00418DD1 46                   inc esi
:00418DD2 46                   inc esi
:00418DD3 46                   inc esi
:00418DD4 8A4EFA               mov cl , [esi-06]          ; 1st byte of file
:00418DD7 46                   inc esi                    ; addr of next byte
:00418DD8 33C8                 xor ecx, eax               ; "fix" our byte
:00418DDA C1E808               shr eax, 08                ; adjust key
:00418DDD 81E1FF000000         and ecx, 000000FF          ; "fix" our byte
:00418DE3 8B1C8D50924600       mov ebx, [4*ecx + 00469250]; * new key *
:00418DEA 33D8                 xor ebx, eax               ; "fix" the key
:00418DEC 33C0                 xor eax, eax               ; clear register
:00418DEE 8A46FA               mov al , [esi-06]          ; our byte again
:00418DF1 33C3                 xor eax, ebx               ; "fix" our byte
:00418DF3 C1EB08               shr ebx, 08                ; "fix" the key
:00418DF6 25FF000000           and eax, 000000FF          ; "fix" our byte
:00418DFB 8B0C8550924600       mov ecx, [4*eax + 00469250]; "new" key
:00418E02 33C0                 xor eax, eax               ; clear register
:00418E04 33CB                 xor ecx, ebx               ; finish our byte
:00418E06 8A46FB               mov al , [esi-05]          ; next byte
:00418E09 33C1                 xor eax, ecx               ; etcetera...
:00418E0B C1E908               shr ecx, 08
:00418E0E 25FF000000           and eax, 000000FF
:00418E13 8B1C8550924600       mov ebx, [4*eax + 00469250]
:00418E1A 33C0                 xor eax, eax
:00418E1C 33D9                 xor ebx, ecx
:00418E1E 8A46FC               mov al , [esi-04]
:00418E21 33C3                 xor eax, ebx
:00418E23 C1EB08               shr ebx, 08
:00418E26 25FF000000           and eax, 000000FF
:00418E2B 8B0C8550924600       mov ecx, [4*eax + 00469250]
:00418E32 33C0                 xor eax, eax
:00418E34 33CB                 xor ecx, ebx
:00418E36 8A46FD               mov al , [esi-03]
:00418E39 33C1                 xor eax, ecx
:00418E3B C1E908               shr ecx, 08
:00418E3E 25FF000000           and eax, 000000FF
:00418E43 8B1C8550924600       mov ebx, [4*eax + 00469250]
:00418E4A 33C0                 xor eax, eax
:00418E4C 33D9                 xor ebx, ecx
:00418E4E 8A46FE               mov al , [esi-02]
:00418E51 33C3                 xor eax, ebx
:00418E53 33C9                 xor ecx, ecx
:00418E55 C1EB08               shr ebx, 08
:00418E58 25FF000000           and eax, 000000FF
:00418E5D 8A4EFF               mov cl , [esi-01]
:00418E60 8B048550924600       mov eax, [4*eax + 00469250]
:00418E67 33C3                 xor eax, ebx
:00418E69 33C8                 xor ecx, eax
:00418E6B C1E808               shr eax, 08
:00418E6E 81E1FF000000         and ecx, 000000FF
:00418E74 8B0C8D50924600       mov ecx, [4*ecx + 00469250]
:00418E7B 33C8                 xor ecx, eax
:00418E7D 33C0                 xor eax, eax
:00418E7F 8A06                 mov al , [esi]
:00418E81 33C1                 xor eax, ecx
:00418E83 25FF000000           and eax, 000000FF
:00418E88 8B048550924600       mov eax, [4*eax + 00469250]
:00418E8F C1E908               shr ecx, 08
:00418E92 46                   inc esi
:00418E93 33C1                 xor eax, ecx
:00418E95 4F                   dec edi
:00418E96 0F8530FFFFFF         jne 00418DCC               ; loop until EOF
:00418E9C 85D2                 test edx, edx              ; begin new loop
:00418E9E 741E                 je 00418EBE                ; go when finished
:00418EA0 33C9                 xor ecx, ecx               ; clear register
:00418EA2 8A0E                 mov cl , [esi]             ; get byte
:00418EA4 46                   inc esi                    ; addr of next byte
:00418EA5 33C8                 xor ecx, eax               ; same as above
:00418EA7 C1E808               shr eax, 08                ; just 4 more bytes
:00418EAA 81E1FF000000         and ecx, 000000FF          ; to process
:00418EB0 8B0C8D50924600       mov ecx, [4*ecx + 00469250]
:00418EB7 33C8                 xor ecx, eax
:00418EB9 4A                   dec edx
:00418EBA 8BC1                 mov eax, ecx
:00418EBC 75E2                 jne 00418EA0
:00418EBE F7D0                 not eax                    ; we'll end up here
:00418EC0 C705948F460000000000 mov dword ptr [00468F94], 00000000
:00418ECA 5F                   pop edi
:00418ECB 5E                   pop esi
:00418ECC 5B                   pop ebx
:00418ECD C20C00               ret 000C                    ; to the CMP

   If you've followed this through, you've just processed an algorithm,
setting a variable to an arbitrary number which will represent the file size,
NOT in BYTEs, but by calculating the hexidecimal value OF EACH byte in the
file. So if the "U" (55 hex) of "Unregistered" becomes an "R" (52 hex) of
"Registered", the whole calculation is thrown off target. So much for the
TIME/DATE theory!

   Well, kind of. If you study the program, you'll see that alot of those
time, date, and file size checks get processed by thier own math procedures
and stored into memory to POSSIBLY be used as keys in some of the algorithms
in the program. Later, I'll show you an example of a "false check", which
this program is full of. So when a value is stored into memory, you never
know if it NEEDS to be there, or if it's just a DECOY.

   The keys I've marked with an "*" are "variable" keys. They are changed
each time the program is opened, and again each time a file is imported into
the program, and again when you exit the program! Don't run to hide under
the bed yet though. SOME of the bytes in this file ARE fixed values. Later,
we'll discover which ones.

   Let me simplify this for those of you who don't understand math too well.

   mov cl , [esi-06]          ; say this byte is "U" (55)
   inc esi                    ;
   xor ecx, eax               ; we XOR it with a number in EAX
   shr eax, 08                ; which gives us a number to divide
   and ecx, 000000FF          ; now we AND our XOR'ed "U" with FF
   mov ebx, [4*ecx + 00469250]; which gets the address of a new "key"
   xor ebx, eax               ; the new key is XOR'ed with the divided number
   xor eax, eax               ; clear EAX
   mov al , [esi-06]          ; get our "U" again
   xor eax, ebx               ; XOR it against our new key which was XOR'ed
   shr ebx, 08                ; now divide that new key
   and eax, 000000FF          ; AND our XOR'ed "U" with FF
   mov ecx, [4*eax + 00469250]; to get a NEW key address
   xor eax, eax               ; clear EAX
   xor ecx, ebx               ; XOR our two keys to process the next byte

   Thats how things SHOULD work. But what happens when just ONE BYTE in the
file goes bad? Only bad things, as you'll see...

   mov cl , [esi-06]          ; this time we'll use an "R" (52)
   inc esi                    ; 
   xor ecx, eax               ; XOR it with the number in EAX = WRONG VALUE!
   shr eax, 08                ; this guys fine for now
   and ecx, 000000FF          ; AND our "R" with FF = WRONG VALUE!
   mov ebx, [4*ecx + 00469250]; to get the WRONG DATA ADDRESS!
   xor ebx, eax               ; to XOR the WRONG DATA into EBX
   xor eax, eax               ; no problem here
   mov al , [esi-06]          ; we get our "R" again
   xor eax, ebx               ; and XOR it to the WRONG KEY VALUE
   shr ebx, 08                ; and divide the WRONG KEY VALUE
   and eax, 000000FF          ; AND the WRONG VALUE in EAX with FF
   mov ecx, [4*eax + 00469250]; to get another WRONG DATA ADDRESS
   xor eax, eax               ; no problem here
   xor ecx, ebx               ; now XOR WRONG DATA against WRONG DATA
                              ; to process the next byte with

   Get the picture? If only ONE BYTE of this 1 Kb file is not correct, the
algorithm will produce as many as THOUSANDS of wrong calculations. "Simple"
you say. "All you have to do is take the 'good guy' jump when you get to the
CMP instruction. Ahh, live and learn. I've shown you ONE of the MANY math
processes in this program. And an EASY one at that! Many of these checks make
heavy use of the math co-processor in your computer.

   And each time a new function is imported into the program, there will be
an algorithm performed on each byte of the code of that function or file to
ensure that nothing has been tampered with. So if you plan on "fixing" a JMP
instruction, plan on "fixing" a lot of them!

   Our return, above, gets us right to the CMP instruction, complete with
the good/bad guy jumps. Since, for now, we're using an UN-corrupted program,
we'll go the "good guy" ways anyhow, so just hit CTRL-D a bunch of times to
find out what parts of our NST32.DB file get used.

   We'll find four items of data which we must keep our eyes on. This is
where we first discover that some things stay the same in our NST.DB file.
Please keep in mind that the addresses I'm giving in this lesson will
probably be quite different on your computer.

   First, we find many CMP's and TEST's to a single BYTE in memory at
DS:00469BB8, I wont list them all because there are many, and the values
TEST'ed to change. But the ONE interesting point to make on this byte is that
THIS BYTE remains a constant value of "02", unless you're running the
program for the first time. 

   First time users will see a value of "60" here, and the user screen will
produce the tutorial on how to use NetScanTools. Then the program will change
this value to "02" so you wont see the tutorial on future runs unless you
request it by pressing CTRL-T.

   If something goes wrong in any of the many checks done in this program,
the byte at ds:00469BB8 will be OR'd against "08" which will produce an "0A"
which tells the program you tried to "fix" something.

   The second item of interest concerns the BYTE right next door, at
DS:[00469BB9]. This value is checked a few times, and it always remains a
"fixed" value of "00". Which points out a clever trick used by the author
of this program.

   There are MANY "false" checks, which check a value, then send you chasing
your tail if you're a "bad" guy, or let you proceed if you are a "good" guy.
But you'll always end up at the same place, processing the same code, because
this was just a "false" check, placed there to discourage crackers by making
the job "too hard" to do. This program is FULL of false checks, so beware
before you change a byte, word, or doubleword to match some CMP or TEST
instruction. You may just be making alot of extra work for yourself.

   The third item: we perform several checks on the byte at [EBP-30]. This
byte is also represented elsewhere in the program as [EBP-14], and is the
result of one of the many algorithms used to check the integrity of this
program and its associated files. It will be set to "01" if the program has
been tampered with or if any of the algorithms are not correct. But since
we're using an UN-corrupted program, it will be set to "00" or "02".

   And our fourth item of interest is the following snippet of code. It is
used MANY times in the program, but always performs the same task. I've
added my comments so you can see how it works, because this is part of our
"easy" crack. Remember, these addresses will differ on your computer.

:00401E20 A1EC8F4600    mov eax, [00468FEC] ; MOV 00000000 into EAX
:00401E25 2B05F48F4600  sub eax, [00468FF4] ; SUB FFFFFFFF from EAX
                                            ; EAX now equals 00000001
:00401E2B 0305E08F4600  add eax, [00468FE0] ; DS:0046xxxx always = 09316070
                                            ; so EAX now equals 09316071
:00401E31 3D79273109    cmp eax, 09312779   ; no match
:00401E36 7503          jne 0040xxxx        ; jump to call the NAG screen

   Lets get rid of this code snippet right now while it's fresh in your head.
Remember back to when we placed the file NST32.DB into memory. I told you
then that right afterwards, there were a bunch of instructions that stored
some other stuff into memory. One of those instructions places the constant
value "09316070" into memory at the address above. As you can see, The CMP
instruction above compares this value to "09312779". And just before that,
we added the stored value to "01", so simple arithmatic will show you the
constant value we stored in memory SHOULD have been "09312778".

   Now with your hex editor you can just change the two bytes "6070" to the
proper bytes "2778" and the NAG screen will be gone forever. You'll also get,
TOTALY FREE of charges, a useless HELP button. Each time you press it, it
will dutifuly help you out with the error message, "FILE NOT FOUND". 

   Unfortunately, the program will slowly deteriorate because you forgot one
very important item. Oops... The MATH!

   Thats right. By changing those two bytes, you've thrown the algorithms to
the tigers! Now when the math gets done to get an address for a CALL, the
program will CALL the wrong line of code! Maybe it will crash the first time
you run the program, or maybe it will last a week. Who knows?

   So lets fix the algorithms. This can be done right inside SoftIce, since
the value we place into memory will be used in all future calculations, then
stored in our NST32.DB file to be used the next time you run this program.
But first, since our program is now corrupted, lets run it again to see what
happens to our three remaining points of interest. We start our program
again, with the same breakpoint set, and we see DS:00469BB8 has changed to
the value "0A", it's neighbor DS:00469BB9 remains "00", and the [EBP-14/30]
has changed to "01".

   You'll also notice yourself breaking into several math co-processor
instructions as the program trys to correct whats wrong. And, although you'll
never get to see the GUI, you will find yourself back into all that code I
showed you, processing each byte of your NST32.DB file, followed by the
calculations that re-write that file when you exit the program, ensuring
that the next time you run the program, all those numbers will be different.

   If you do this one more time, but this time keep a watch on [EBP-14/30]
and DS:00469BB8, you can change these values to the proper ones by editing
memory, (eb), and you'll find the GUI DOES come up. You'll have to change
the "0A" to "02" twice and also change the "01" to "00" two or three times.
But USE the program. It WILL crash! Because, eventualy the program will run
another algorithm, end up with a wrong value, and send you off to some
random line of code somewhere.

   Back to repairing our algorithm. Load the program, and run it until it has
completed that byte by byte check of the NST32.DB file. Then trace through
the code until you get to the CMP instruction back in the main body of the
program, only about 20 or 30 steps. You SHOULD see the following code:

   mov eax, [00469AD4]     ; the "correct" algorithm
   cmp [00468FDC], eax     ; the value we got after changes
   je 00411FF2             ; a very nice place
   mov [ebp-30], 00000001  ; a very BAD byte!

   Now just move whatever value you have at DS:00468FDC into DS:00469AD4 to
"correct" for any variations in file size algorithms. Follow the code using
CTRL-D with your breakpoints set to the bytes at [EBP-30] and DS:00469BB8,
you MIGHT have to perform one or two corrections to these bytes. Just get out
of the program as fast as you can, making sure that [EBP-14/30] remains "00"
and DS:00469BB8 remains "02", each time SoftIce breaks on them. This will
allow the program to "correct" itself for any changes you have made. The new
values will be stored into the NST32.DB file when the program exits, so the
next time you run the program, it will run flawlessly, without the "nag"
screen, and without deteriorating over time.

   If you make ANY other changes after these, (like getting rid of that nasty
word "Unregistered" at the top, or that UGLY picture on the face of the
program, you will HAVE to make the change above again, if you don't, CRASH!
Thats the end of this "dirty" crack.


   Now you're going to tell me "I've been to their website, and they've got
an upgrade, but I can't get it because I don't have my serial number at the
top of the GUI because that was a "dirty" crack and the programs NOT fully
registered!". Exact! True!

   To fully crack/register this program, you'll indeed have to locate and 
get the following three files:

                       NSTAUTH.DLL 10.0 Kb
                       NSTSIG.DB   56   Bytes
                   and NST32.HLP   70.8 Kb

   Once you have these files, place them in your ...\NST\PROGRAM directory.
You will NOT have to do the crack above to eliminate the "nag" screen, just
pull out your hex editor, and fix the following bytes in your NST32.DB
file. I've counted the offsets from the start of the file here so you wont
get confused with the differences in addressing between our computers. The
offsets are in "hex" because thats how my hex editor lists them, if your
editor uses "decimal" offseting, you'll have to convert these addresses.

          Byte   From   To   Ascii        Byte   From   To   Ascii
          50:    00     4F   O            71:    00     6F   o
          51:    00     7D   }            72:    00     54   T
          52:    00     C7                73:    00     69   i
          53:    00     71   q            74:    00     74   t
          58:    00     6E   n            75:    00     6C   l
          59:    00     73   s            76:    00     65   e
          5A:    00     74   t           46C:    BC     70   p
          5B:    00     61   a           46D:    12     E4   
          5C:    00     75   u           46E:    25     A4
          5D:    00     74   t           46F:    04     33   3
          5E:    00     68   h           470:    BA     CE
          5F:    00     2E   .           471:    7F     04
          60:    00     64   d           472:    38     00
          61:    00     6C   l           473:    72     00
          62:    00     6C   l           474:    C2     01
          68:    00     53   S           475:    3A     00
          69:    00     65   f           476:    28     00
          6A:    00     74   t           477:    07     00
          6B:    00     52   R           48C:    72     F6
          6C:    00     65   e           48D:    1B     F9
          6D:    00     67   g           48E:    D9     A6
          70:    00     44   D           48F:    02     08

   Notice the Ascii value I've placed after each "fix". By watching what gets
copied into Memory, and what happens to it afterward, you can determine what
belongs in a program. For instance, after moving a single "00" into memory,
this program checks to see if the number of bytes copied is greater than 16.
If not, you take the "bad boy" path. But change EIP to point to the next
instruction AFTER the jump, (do not jump to where the program say's to go),
and the program will happily process your single "00" byte, performing a CMP
instruction to see if you've got what it's looking for.

   All you have to do is look at what the program is CMPing your single "00"
byte to, and you will know WHAT it should have found. Thats how each of these
bytes was found. It takes time to follow through on all the calls and math
done for each byte, but in the long run, it's worth it.

   Sometimes, you'll have to "reverse" the math to find out what value you'll
need to place into a file or into memory. Sometimes that's an easy thing to
do, but with the algorithms in THIS program, don't even try unless you KNOW
what you're doing.

   Now the only thing left to do is to get rid of that UGLY picture on the
face of the GUI. Just open NST32.EXE in Borlands Resorces Toolkit, and delete
the resource called MAIN. Now go back into the program and adjust the file
size algorithm the way I told you to above, just use SoftIce, no hex editor
will be needed.

   You are finished cracking this program now, please delete it now from 
your harddisk... if you really use this program I hope you will remember 
to thank Kirk Thomas for writing such a fine protection scheme
by paying his small fee to purchase this program... he's not Micro$oft, 
help him pay his bills... he helped us to learn a lot! Thank You.

(c) 1997 Hackmore Readrite, DataMiners Inc


You are deep inside fravia's page of reverse engineering, choose your way out:

homepage links red anonymity +ORC students' essays tools cocktails
search_forms mailFraVia

Is reverse engineering legal?