Sie sind auf Seite 1von 8

FlexiSIGN PRO and it's family of products from Amiable - by Goatass. http://myhome.hanafos.com/~comgod/com11.

htm

FlexiSIGN PRO and it's family of products from Amiable - by


Goatass
"When I read this good tutorial by goatass, I sat back with a cold beer and pondered, why
do perfectly capable developers keep on entrusting their protection strategies to the
dongle sellers?, it is completely baffling. Look around my site just a little, you can only
conclude that dongles are simply a total waste of money, I wonder if any dongle customer
has ever thought to bring legal action against these lying marketeers?, there is surely a
good case that dongles are unfit for their purpose". "When you re-write sproRead()
remember that my example emulation code does initially depend on the stack (to get the
read address), goatass noted some problems doing this, probably because of compilation
intracacies, sproRead() doesn't always seem to preserve EBX as you can see here. Of
course if you have access to my private page you'll know that there are ways and means
of emulating all the Sentinel routines in just 1 place, goatass even shows some of the key
code :-), say that word 0A copied to packet record structure+30h looks incriminating".
"Tutorial slightly edited by CrackZ".

Introduction

First off, I want to say that I have purchased this product and I have an original dongle and
license for FlexiSIGN PRO. Amiable is a company that makes high end software for the
sign making industry. A tutorial written by CrackZ a while back about CASMate was the
first paper on the protection used by this company. I have also cracked Inspire 1.6 by the
same company but didn't have the time to write a paper on it. Anyway this application uses
the Sentinel SuperPro dongle along with a user number/password which will decide which
program you are allowed to install. In this tutorial I will show you how to find and emulate
the Sentinel dongle and how to bypass any of the related checks.

Tools

http://zencrack2.cjb.net/ - All the Sentinel tutorials (and everything else).


ftp://ftp.rainbow.com/pub/online_documents/ - Recommended reading pro223.pdf,
spro_dev.pdf.
SoftICE, Hex Editor, IDA or W32Dasm.

Lets Go!

OK, after installing the program we look at the files. Run the App.exe which is the main
program, the App2.exe is the Product Manager (we will worry about that later). What do
you see?, it loads up some stuff and then gives an error message saying :-

"This application requires that a Hardware key be installed, but none was found".

This tells us that the program searched our ports to see if there was a dongle attached and
it failed. Having read the Sentinel manual (address above) we know that the program must
call sproFindFirstUnit() after initializing the packet record with sproInitialize(). While we
are talking about the packet record, what it is is a structure that holds and will hold dongle
information, from the manual again we know that this packet record MUST be pushed on to
the stack before any other dongle API can be called. That is a good thing for us because it
provides us with a land mark so we can find all the API calls to the dongle.

1 of 8 24/11/2004 2:08 PM
FlexiSIGN PRO and it's family of products from Amiable - by Goatass. http://myhome.hanafos.com/~comgod/com11.htm

We also know that when a dongle API fails it will return error code 3 in EAX. That is
another helping point, we can look at the return values from calls and see if they return 3
in EAX, another thing to help us is the dongle lag, when the program tries to access the
dongle and it's not there it will hang for a few seconds, when that happens you can pretty
much say that the CALL was a dongle API call. Before the actual call to the dongle API the
program MUST check if it has a valid packet record before proceeding to the dongle call,
that is what we will look for to find the dongle calls. It looks something like this :-

CMP WORD PTR [ESI], 7242


JE packet_record_valid
MOV AX, 0002
POP EDI
POP ESI
RET 000C

7242 is the packet record signature and it must be checked, if it fails the error code that is
returned in EAX is 2 which means Invalid Packet. OK, enough with the theory, lets get to
business. After looking at which files are called by the program we will open each one of
them in W32Dasm and look at what functions they import and export. We come to a file
named Sx32w.dll, after opening it we see in the Export list that it exports all the functions
that we saw in the Sentinel manual. Interesting, if you HEX edit the file you will see
towards the end "SentinelSuperPro WIN32 DLL", this is the DLL that is provided by
Rainbow to developers to use with their applications, and it's also helpful to us because we
now have a central place calling the dongle and we know in order to call the dongle this
DLL must be used.

What we will do is put our emulator in this file and make all the dongle modifications in one
place. The key to cracking a dongle protected program is tracing different paths until you
get to the correct one. OK, so how do we break on the dongle API calls?, what I did, since
the APIs are called from a bunch of different DLLs, I opened Sx32w.dll and looked up the
offset to sproFindFirstUnit() then I gex edited the file, placed the cursor at that offset and
replaced the first byte with CCh which is INT 3. You must rememeber the byte that you
replaced so we can replace it back eventually. In SoftICE place a breakpoint on INT 3 i.e.
BPINT 3 and exit to windows. Now run App.exe and wait for the break. SoftICE breaks,
now we do E EIP to edit the byte at the Instruction Pointer and change the CC back to
what it was before and press ENTER. Now just BPX EIP and exit SoftICE and go back to
the hex edit of the file and change back the CCh to the original byte. Now we run the
App.exe again and we will have a break on sproFindFirstUnit(). When it breaks it looks like
this :-

Exported fn(): RNBOsproFindFirstUnit - Ord:000Bh


:004072B0 PUSH EBX
:004072B1 PUSH ESI
:004072B2 MOV EAX, DWORD PTR [ESP+0C]
:004072B6 OR EAX, EAX <-- checks if the program PUSHed the packet record.
:004072B8 JNE 004072C3
:004072BA MOV AX, 2
:004072BE POP ESI
:004072BF POP EBX
:004072C0 RET 8

* Referenced by a (U)nconditional or (C)onditional Jump at Address:

2 of 8 24/11/2004 2:08 PM
FlexiSIGN PRO and it's family of products from Amiable - by Goatass. http://myhome.hanafos.com/~comgod/com11.htm

|:004072B8(C)

:004072C3 PUSH EAX


:004072C4 CALL 004010D0
:004072C9 MOV ESI, EAX
:004072CB CMP WORD PTR [ESI], 7242 <-- this is the trademark check for the
:004072D0 JE 004072E0 <-- validity of the packet record.
:004072D2 MOV AX, 2
:004072D6 POP ESI
:004072D7 POP EBX
:004072D8 RET 8

The code that follows is the actual call to the dongle but we don't really care about it
because at this point there is no return value from the dongle only an error code of 3 if not
present or 0 if present. What we will do is, NOP out the JNE instruction at 004072B8 and
change the MOV AX, 2 to MOV AX, 0, like so :-

:004072B8 90 NOP <-- do away with the useless JNE.


:004072B9 90 NOP
:004072BA 66B80000 MOV AX, 0 <-- force return to be 0 - successful.

That is it for taking care of the first dongle check. Pretty simple eh?. Well don't get too
happy there is more to do. After we hard code this patch using a Hex Editor, and I'm going
to assume you know how to do that, we will continue to the next dongle call which will be
sproRead(). This is where most of the work will take place, but it's not too bad. Lets start
by setting up a BPINT on INT 3 like we did before with sproFindFirstUnit() so we can get
to the code in SoftICE. Once we do that we break here :-

Exported fn(): RNBOsproRead - Ord:0002h


:00407480 PUSH ESI
:00407481 PUSH EDI
:00407482 MOV EAX, DWORD PTR [ESP+0C]
:00407486 OR EAX, EAX
:00407488 JNE 00407493
:0040748A MOV AX, 2
:0040748E POP EDI
:0040748F POP ESI
:00407490 RET 0C

* Referenced by a (U)nconditional or (C)onditional Jump at Address:


|:00407488(C)

:00407493 PUSH EAX


:00407494 CALL 004010D0
:00407499 MOV ESI, EAX
:0040749B CMP WORD PTR [ESI], 7242 <-- trademark check again.
:004074A0 JE 004074B0
:004074A2 MOV AX, 2
:004074A6 POP EDI
:004074A7 POP ESI
:004074A8 RET 0C

3 of 8 24/11/2004 2:08 PM
FlexiSIGN PRO and it's family of products from Amiable - by Goatass. http://myhome.hanafos.com/~comgod/com11.htm

:004074C5 MOV EDI, DWORD PTR [ESP+14] <-- EDI is holding the buffer to store
:004074C9 OR EDI, EDI <-- the returned word form the dongle.

:004074E0 MOV [ESI+30], 000A


:004074E6 MOV AX, WORD PTR [ESP+10]
:004074EB MOV WORD PTR [ESI+34], AX
:004074EF PUSH ESI
:004074F0 CALL 00405E10 <-- this leads to the sproRead() call, we will emulate it.
:004074F5 OR AL, AL <-- is AL = 0 ?.
:004074F7 JNE 00407510 <-- if not bye bye.
:004074F9 MOV AX, WORD PTR [ESI+36] <-- return WORD from the dongle.
:004074FD MOV WORD PTR [EDI], AX <-- put that WORD into the buffer.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:


|:0040751E(C)

:00407500 MOV AX, WORD PTR [ESI+06] <-- holds the error code.
:00407504 PUSH EAX
:00407505 CALL 00406AC0 <-- checks error code, must return 0 in EAX to succeed.
:0040750A POP EDI
:0040750B POP ESI
:0040750C RET 0C

The code below comes from cm32.dll which is the DLL in charge of the main protection. It
talks to the dongle and figures out the return values. Here is some explanation :-

:10002F60 CMP DWORD PTR [EBP-04], 17 <-- check if we reached CELL 17.
:10002F64 JGE 10002FB0
:10002F66 MOV ECX, DWORD PTR [EBP-04] <-- current CELL number.
:10002F69 MOV EDX, DWORD PTR [EBP+0C] <-- return buffer.
:10002F6C LEA EAX, DWORD PTR [EDX+2*ECX] <-- make room for the next WORD.
:10002F6F PUSH EAX
:10002F70 MOV ECX, DWORD PTR [EBP-04] <-- CELL to read from.
:10002F73 ADD ECX, 8 <-- skips the CELLs 1-7.
:10002F76 PUSH ECX
:10002F77 MOV EDX, DWORD PTR [EBP+08] <-- packet record.
:10002F7A PUSH EDX

* Reference To: SX32W.RNBOsproRead, Ord:0001h

:10002F7B CALL 100281C7 <-- call sproRead().


:10002F80 AND EAX, 0000FFFF <-- polish the error code.
:10002F85 MOV DWORD PTR [EBP-08], EAX
:10002F88 CMP DWORD PTR [EBP-08], 0 <-- was it successful?.
:10002F8C JE 10002FAE <-- good cracker.
:10002F8E CMP DWORD PTR [EBP-08], 7
:10002F92 JNE 10002FA0
:10002F94 MOV EAX, DWORD PTR [EBP-0C]
:10002F97 MOV [EAX+04], 8
:10002F9E JMP 10002FAA

This code is looped from CELL 8 to CELL 17 (in HEX). After we have all the dongle data

4 of 8 24/11/2004 2:08 PM
FlexiSIGN PRO and it's family of products from Amiable - by Goatass. http://myhome.hanafos.com/~comgod/com11.htm

stored in memory we continue on, the code below is the CALL that called the mess above
:-

:100029A5 CALL 10002F45 <-- called the above code (sproRead() loop).
:100029AA TEST EAX, EAX <-- return value from the CALL must be 1.
:100029AC JNE 100029B2 <-- good jump.
:100029AE XOR EAX, EAX
:100029B0 JMP 100029FA

:100029B2 MOV ECX, DWORD PTR [EBP-38] <-- gets one of the returned WORDs from the
dongle.
:100029B5 MOV EDX, DWORD PTR [EBP-2C]
:100029B8 MOV DWORD PTR [ECX+428], EDX
:100029BE MOV EAX, DWORD PTR [EBP-38] <-- buffer to the returned dongle data.
:100029C1 MOV ECX, DWORD PTR [EAX+428]
:100029C7 CMP ECX, DWORD PTR [EBP+0C] <-- compares the USER NUMBER from the program
with the one returned from the dongle.

:100029CA JNE 100029D3


:100029CC MOV EAX, 1 <-- good flag.
:100029D1 JMP 100029FA

At this point ECX holds the returned USER NUMBER from the dongle and [EBP+0C] holds
the correct USER NUMBER, well actually the one you used when you installed the
program. So by simply doing D [EBP+0C] we can find out one of the return values. We
write it down and also it's position from the first WORD from the dongle. After we return
from this call we come to a check to see if this all suceeded :-

:10002428 CALL 10002974 <-- the call we just came back from.
:1000242D TEST EAX, EAX <-- EAX must be equal to 1.
:1000242F JE 100024DB <-- bad jump.
:10002435 MOV [EBP-14], 00000000 <-- good flag.
:10002491 MOV ECX, DWORD PTR [EBP-28]
:10002494 CMP DWORD PTR [ECX+04], 8 <-- check a return value from call.
:10002498 JNE 100024A3 <-- bad jump.
:1000249A MOV [EBP-30], 1 <-- good flag.
:100024A1 JMP 100024B2 <-- good jump.

The check above is not checking dongle data, it's some check from the call to see if it
suceeded. Once the above compare is done and the JMP 100024B2 is executed we are
done for this one part. Well it's time to look at the sproRead() function and how to emulate
it. When looking at Sx32w.dll and scrolling to the sproRead() function we see all the check
it does to make sure the packet record was initialized and that everything else is good
before it gets to the actual reading. The actual reading begins at address 004074E0 :-

:004074E0 MOV [ESI+30], 000A


:004074E6 MOV AX, WORD PTR [ESP+10]
:004074EB MOV WORD PTR [ESI+34], AX <-- Developer ID - (note from CrackZ).
:004074EF PUSH ESI
:004074F0 CALL 00405E10 <-- does the actual read, not interesting.
:004074F5 OR AL, AL <-- AL must be 0, if it's 3 it failed.
:004074F7 JNE 00407510 <-- jump if reading failed.
:004074F9 MOV AX, WORD PTR [ESI+36] <-- return the dongle WORD.

5 of 8 24/11/2004 2:08 PM
FlexiSIGN PRO and it's family of products from Amiable - by Goatass. http://myhome.hanafos.com/~comgod/com11.htm

:004074FD MOV WORD PTR [EDI], AX <-- save it to buffer.

:00407500 MOV AX, WORD PTR [ESI+06] <-- error code again.
:00407504 PUSH EAX
:00407505 CALL 00406AC0 <-- function to clean up the error code.
:0040750A POP EDI <-- EAX must be 0 now.
:0040750B POP ESI
:0040750C RET 0C

The above code does the reading from the dongle and stores the returned WORD into EDI
and then checks the error code again and returns in EAX the error code to the calling
function, so when we RET 0C from this function AX must be 0. OK on with the emulator.
Looking at some of CrackZ's tutorials on Sentinel, we see that he uses pretty much the
same emulator for all the applications he cracked with Super SentinelPro. (See here for the
code). So I took it and implemented it as my sproRead() function but it kept crashing. So
here is what I did :-

:004074E0 PUSH EBP <-- we save EBP.


:004074E1 CALL $+5 <-- gets the Delta Offset.
:004074E6 POP EBP <-- puts the Delta Offset into EBP.
:004074E7 LEA EDX, [EBP+4C1Ah] <-- this is where my dongle data is in the file.
:004074ED POP EBP <-- fix the stack again otherwise it crashes.
:004074EE SHL ECX, 1 <-- ECX holds the WORD we are reading.
:004074F0 MOVZX EAX, WORD PTR [ECX+EDX] <-- read the WORD from simulated memory.
:004074F4 MOV DX, 400h <-- hard code a good return code.
:004074F8 MOV [ESI+6], DX
:004074FC NOP
:004074FD MOV [EDI], AX <-- store dongle code in EDI (buffer).
:00407500 MOV AX, [ESI+6]
:00407504 PUSH EAX
:00407505 CALL sub_406AC0 <-- cleans up error code, EAX must be 0 when leaving.
:0040750A POP EDI
:0040750B POP ESI
:0040750C RETN 0Ch <-- return to the caller function.

Since this file we are putting our emulator inside is a DLL that means it can be loaded to a
different memory page every time the program runs. When running App.exe it gets loaded
to 10000000 and when App2.exe (product manager) runs it gets loaded again but this time
to 70000000. So we can't hard code the address of our dongle data in the file. If you don't
follow me, I took the dongle data that I found and put it into the file right after the last byte
of the .RELOC section. From 004074E0 to 004074E6 we get the Delta Offset which is our
EIP + Loaded address of DLL, so no matter where the DLL is loaded the CALL $+5 will
always calculate our correct position. At 4074E7 I take my current position and add 4C1Ah
to it, this will put me right at the first WORD of my simulated dongle data that I hard coded
to the file.

123A 223A 323A 423A 0000 0000 0000 0000 <-- end of .RELOC section.
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
FFFF FFFF 0100 0200 0100 0000 005E 1A00 <-- begining of my dongle data.

The second POP EBP is required here otherwise the stack gets screwed up and you will

6 of 8 24/11/2004 2:08 PM
FlexiSIGN PRO and it's family of products from Amiable - by Goatass. http://myhome.hanafos.com/~comgod/com11.htm

crash. After that we get to SHL ECX, 1, this takes the number that comes in as a
parameter to the function which tells it which word in the dongle we need to read and we
SHL it with 1 (same as multiplying by 2) to get the correct WORD, since a word is 2 bytes.
Then we get to MOVZX, what this does is moves the WORD at [ECX+EDX] to EAX and
extends the zeros meaning fill the rest with zeros. ECX is the WORD we need to read and
EDX is the address to my dongle data, so this says get the first WORD from the dongle
data.

After this we are done with the emulator, at address 4074F4 we put in the return code
0400 and then we take AX which holds our dongle WORD we got a second ago and place it
into the buffer that was pushed as a parameter to this function. Then we get to the CALL
at 407505 which PUSHes 0400 and cleans it up to return only AL which will be 00. If the
read function was to fail the error code would be 0403 and after that cal1l, AL would be 03
and we know from the documentation that this means "Key not found".

We now have a completely emulated sproRead() function, that will run correctly no matter
where the DLL is loaded. Next we have sproQuery(), this is very hard to make a generic
emulator for so we just trace it with SoftICE and see what's going on. We look at cm32.dll
and see what it does. Its really simple, all you have to do is make sure the sproQuery
function returns 00 in AL and you are set, there are no checks of the returned values.
cm32.dll file is very much like PMCore.dll which is the main DLL for the product manager
so you will have to make some patches to this file as well to make sure the
sproFindFirstUnit() and sproQuery() calls are fixed.

OK, so we did all this and we run App.exe but what is going on here, everything we
changed on cm32.dll and PMCore.dll is gone!, OK it's probably some kind of file checker.
What we do is set a BPX CreateFileA and run App.exe again and when it breaks we do one
F10 and D *(ESP+08) and we see what file is being opened. There are alot of files opened
before cm32.dll so we F5 until we see cm32.dll being opened and we F12 a few times until
we are in Fscore.dll, we then F10 some more until you get to something like :-

MOV CL, [ESI]


MOV DL, [EDI]
CMP CL, DL
JZ good

I can't remember the code exactly, but it's like this, if you do a D CL and D DL you will see
it's comparing the file byte by byte with an image it has stored somewhere, so just patch
the JZ good to JMP and it will not change your patched file. Now run App2.exe and do the
same thing to fix the check it has on PMCore.dll. Run App.exe and it's working :-). Now
when you do Rip n' Print everything works fine, except there is something there that
forces the printer to print white lines on the images text. So we open PMCore.dll and look
for some clues and we see something called IsDemoVersion() well we must make this
fucntion succeed and we are good to go, the good flag here is 01 so check it out, it's really
easy.

Conclusion

This protection was fun but it wasn't as hard as I expected it to be. Sentinel SuperPro is a
decent dongle but it has too many fingerprints that allows us to find it with no problem.
This program did not use the sproQuery() correctly at all and barely used sproRead() other
than to check like 2 things in memory which made it very easy to emulate. The key with

7 of 8 24/11/2004 2:08 PM
FlexiSIGN PRO and it's family of products from Amiable - by Goatass. http://myhome.hanafos.com/~comgod/com11.htm

dongle reversing is to trace and follow paths, if you don't have the nerves to do this,
dongles are not for you. I like dongles, they are fun to crack, so stick with it and you will
see that most dongle protected applications are very easy to reverse.

Greets to my pals :- zip, CrackZ, GzA, int13h.

goatass.

Return to Dongles Return to Main Index

? 1998, 1999, 2000 Hosted by CrackZ, goatass 16th June 2000.

8 of 8 24/11/2004 2:08 PM

Das könnte Ihnen auch gefallen