I will just publish, from now on, the essays I like exactly as I get them.
Authors are invited to
- 1) download the source code;
- 2) correct it;
- 3) very important CHECK on an Opera browser that it looks ok;
- 4) resend it to me corrected
And I'll update it.
Note that if the essay should infringe on anyone copyrights, and if I receive a legitimate request
to nuke it, it will immediately disappear from my site, so you always better write software reversing
essays that are not "specific" target related... so, pointing out deficiences is OK, showing lamers
how to register (or how to make a coward keygen for the idiots) is definitely NOT "fraviatiquette".
Indeed from now on I want to HELP, not to damage programmers.
This said I publish this because I reckon that you'll be able to enjoy a very nice (especially in the
second part) reversing approach. And I believe that protectors should study this with the
outmost attention...
cracking the hotline sw client v1.2 and server v1.0 b8
by douby^dread
one of the problems with hotline is that it doesn't
have any api input calls to
hook on ... when entering name & serial hooking on
calls like getdlgitemtexta,
getwindowtexta or even hmemcpy won't work ... so how
are wo going to locate
the protection scheme if we can't hook on api input
calls ???
the createwindowexa method
the solution to this problem is using the
createwindowexa method ... by hooking
the createwindowexa api function soft-ice will pop up
every time a window is
created ... so if the protection scheme window is
created si will hopefully pop
up somewhere in/before the protection scheme code ...
the entire protection
scheme method is probably located in a single call ...
so if si pops up the only
thing we'll have to do to locate the protection scheme
call is press the f12
button until we're back in hotline ... press the
cancel button ... et voila
back in softice ... right after the protection scheme
call ... no ain't that
great :-) ... this method works for both the hotline
server and client ...
using this method with the hotline client will get you
here:
:00403B14 8B4DCC mov ecx, dword ptr
[ebp-34]
:00403B17 E824320000 call 00406D40
;protection scheme
:00403B1C E98F000000 jmp 00403BB0
ok so let's disable the bpx on createwindowexa and put
one on :00403b17 ...
open the about box .. click on the unlock button and.
.. back in softice!! ...
ok let's trace into this function ... press f10 until
we're back in hotline ...
ok let's put some text in the name box and ... back in
soft-ice ... start
pressing f10 again till you're back in hl enter the
rest of your name ... now
start typing some kind of false serial ... wow .. back
in si again ... press
f10 till you're back in hl ... enter the rest of your
false serial ... and press
unlock ... back in si again ... ok this is the code
where the REAL protecion starts
we could put some breakpoints on our name and serial
but since the program may copy
our name/serial a lot of times to different memory
locations it may be a boring
exercise ... so let's just step over functions
compares and see what happens ...
using this method you'll soon get to a cmp with
0eh=14d ... let's see what it does
:00406E05 80BD90FBFFFF0E cmp byte ptr
[ebp+FFFFFB90], 0E ;if serial length
;< 0e get the hell out of here else continue
:00406E0C 0F853E030000 jne 00407150
:00406E12 8D95E4FBFFFF lea edx, dword ptr
[ebp+FFFFFBE4]
:00406E18 8995E0FBFFFF mov dword ptr
[ebp+FFFFFBE0], edx
:00406E1E 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00406E24 FF85E0FBFFFF inc dword ptr
[ebp+FFFFFBE0]
:00406E2A C60241 mov byte ptr [edx],
41
:00406E2D 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00406E33 FF85E0FBFFFF inc dword ptr
[ebp+FFFFFBE0]
:00406E39 C60248 mov byte ptr [edx],
48
:00406E3C 0FB605B8E34700 movzx eax, byte ptr
[0047E3B8]
in the code following the serial length check the
program sets some values for
creating some kind of compare value using these values
and the entered name ... we
will get back to these values later ....
now just step & trace until you get here...
:00406ECF BA00000000 mov edx, 00000000
:00406ED4 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:00406EDA 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:00406EE0 6BD21A imul edx, 0000001A
:00406EE3 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:00406EE9 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00406EEF 0FB65206 movzx edx, byte ptr
[edx+06] ;get letter #7 from serial
:00406EF3 83C2BF add edx, FFFFFFBF
;subs 41h from letter
:00406EF6 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx ;add result to mem location
:00406EFC 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8] ;move result in edx
:00406F02 6BD21A imul edx, 0000001A
;imul with 1ah
:00406F05 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx ;store result
:00406F0B 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00406F11 0FB65205 movzx edx, byte ptr
[edx+05] ;get letter #6 from serial
:00406F15 83C2BF add edx, FFFFFFBF
;subs 41h from letter
:00406F18 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx ;add result to mem location
:00406F1E 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8] ;move result in edx
:00406F24 6BD21A imul edx, 0000001A
;imul with 1ah
:00406F27 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx ;store result
:00406F2D 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00406F33 0FB65204 movzx edx, byte ptr
[edx+04] ;etc....
:00406F37 83C2BF add edx, FFFFFFBF
:00406F3A 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:00406F40 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:00406F46 6BD21A imul edx, 0000001A
:00406F49 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:00406F4F 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00406F55 0FB65203 movzx edx, byte ptr
[edx+03]
:00406F59 83C2BF add edx, FFFFFFBF
:00406F5C 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:00406F62 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:00406F68 6BD21A imul edx, 0000001A
:00406F6B 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:00406F71 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00406F77 0FB65202 movzx edx, byte ptr
[edx+02]
:00406F7B 83C2BF add edx, FFFFFFBF
:00406F7E 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:00406F84 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:00406F8A 6BD21A imul edx, 0000001A
:00406F8D 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:00406F93 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00406F99 0FB65201 movzx edx, byte ptr
[edx+01]
:00406F9D 83C2BF add edx, FFFFFFBF
:00406FA0 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:00406FA6 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:00406FAC 6BD21A imul edx, 0000001A
:00406FAF 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:00406FB5 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00406FBB 0FB612 movzx edx, byte ptr
[edx]
:00406FBE 83C2BF add edx, FFFFFFBF
:00406FC1 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:00406FC7 6AFE push FFFFFFFE
:00406FC9 8B85DCFBFFFF mov eax, dword ptr
[ebp+FFFFFBDC]
:00406FCF 50 push eax
:00406FD0 8D85E4FBFFFF lea eax, dword ptr
[ebp+FFFFFBE4]
:00406FD6 50 push eax
:00406FD7 E8D42D0300 call 00439DB0
:00406FDC 83C40C add esp, 0000000C
:00406FDF 3985D8FBFFFF cmp dword ptr
[ebp+FFFFFBD8], eax ;cmp serial value w/ name value
:00406FE5 0F8565010000 jne 00407150
if you study the code you will see the first 7
characters of the serial
are used to create a value ... this value is compared
with a value created using
the previousely set values .. say our serial looks
something like this:
ABCDEFGHIJKLMN
in the code before the call at 00406fd7 the following
mathimatical operations are
being used on the serial ... (#A represents the
uni-code of A(=41h))
((((((((#G-41H)*1AH + (#F-41H))*1AH + (#E-41H))*1AH +
(#D-41H))*1AH +
(#C-41H))*1AH + (#B-41H))*1AH + (#A-41H)) = X
if X = value created using our name -> the first part
of our serial is valid
else wrong serial
so if we copy the code used for making the name value
and if we are able to "substract"
the right serial out of this name value we've got the
first part of the serial!
hmmm let's take another look at the formula above ...
if X - (#A-41H) = Y then Y mod 1ah
must be 0 ... becoz of the - 41H and the * 1AH a valid
serial will probably only contain
uppercase chars that means #A must be a value between
0 and 19H
using this knowledge we could create an algorythm
using backtracking that will "substract"
the right serial out of the given name value ... it
looks something like this:
String getSerial(long a, String s){
if(a==0 && s.length()==7) return s;
else if(s.length()>6||((s.length()<5)&&(a<26)))
return null;
else {
for (int i=0;i<26;i++){
if((a-i)%26==0){
s=getSerial((a-i)/26,s+(char)(i+65));
if(s!=null) return s;
}
}
}
return null;
}
param: a = name value, s = serial
we can use this function to substract the first part
of the serial out of the name value
but what about the second part ???
well let's take a look at the rest of the code:
:00407003 8D9590FBFFFF lea edx, dword ptr
[ebp+FFFFFB90]
:00407009 83C208 add edx, 00000008
:0040700C 8995E0FBFFFF mov dword ptr
[ebp+FFFFFBE0], edx
:00407012 BA00000000 mov edx, 00000000
:00407017 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:0040701D 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:00407023 6BD21A imul edx, 0000001A
:00407026 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:0040702C 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00407032 0FB65206 movzx edx, byte ptr
[edx+06]
:00407036 83C2BF add edx, FFFFFFBF
:00407039 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:0040703F 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:00407045 6BD21A imul edx, 0000001A
:00407048 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:0040704E 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00407054 0FB65205 movzx edx, byte ptr
[edx+05]
:00407058 83C2BF add edx, FFFFFFBF
:0040705B 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:00407061 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:00407067 6BD21A imul edx, 0000001A
:0040706A 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:00407070 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00407076 0FB65204 movzx edx, byte ptr
[edx+04]
:0040707A 83C2BF add edx, FFFFFFBF
:0040707D 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:00407083 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:00407089 6BD21A imul edx, 0000001A
:0040708C 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:00407092 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:00407098 0FB65203 movzx edx, byte ptr
[edx+03]
:0040709C 83C2BF add edx, FFFFFFBF
:0040709F 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:004070A5 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:004070AB 6BD21A imul edx, 0000001A
:004070AE 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:004070B4 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:004070BA 0FB65202 movzx edx, byte ptr
[edx+02]
:004070BE 83C2BF add edx, FFFFFFBF
:004070C1 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:004070C7 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:004070CD 6BD21A imul edx, 0000001A
:004070D0 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:004070D6 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:004070DC 0FB65201 movzx edx, byte ptr
[edx+01]
:004070E0 83C2BF add edx, FFFFFFBF
:004070E3 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:004070E9 8B95D8FBFFFF mov edx, dword ptr
[ebp+FFFFFBD8]
:004070EF 6BD21A imul edx, 0000001A
:004070F2 8995D8FBFFFF mov dword ptr
[ebp+FFFFFBD8], edx
:004070F8 8B95E0FBFFFF mov edx, dword ptr
[ebp+FFFFFBE0]
:004070FE 0FB612 movzx edx, byte ptr
[edx]
:00407101 83C2BF add edx, FFFFFFBF
:00407104 0195D8FBFFFF add dword ptr
[ebp+FFFFFBD8], edx
:0040710A 6A02 push 00000002
:0040710C 8B85DCFBFFFF mov eax, dword ptr
[ebp+FFFFFBDC]
:00407112 50 push eax
:00407113 8D85E4FBFFFF lea eax, dword ptr
[ebp+FFFFFBE4]
:00407119 50 push eax
:0040711A E8F12C0300 call 00439E10
:0040711F 83C40C add esp, 0000000C
:00407122 3985D8FBFFFF cmp dword ptr
[ebp+FFFFFBD8], eax ;cmp serial value w/ name value
:00407128 7526 jne 00407150
HEY!!! it's almost the same ... but instead of the
first 7 chars the last 7 chars
of the serial are used ... so we can use the same
algorythm to substract the last
part of the serial out of the SECOND name value ...
okay so now we know what algorhytm to use to substract
the right serial out off the name
codes ... but how can we create these name codes ?
well that's pretty easy study the call that creates
these name values and just copy the
code ... you will see this call uses a table to create
the name serials .. so you'll
have to copy the table too ... the call also uses the
values set at the beginning of the
protection scheme to create the name value ... as you
will see the only difference between
the hotline server and client are these values set at
beginning of the protection scheme ..
now you've got all the information you need to crack
these babies ... good luck ...
well that's all folks ... i'm sorry i didn't explain
the creating of the name values ...
but hey see it as an exercise ... and try to find out
for yourself ...
for questions/suggestions/remarks/etc. email me at
douby_(at)hotmail(dot)com or visit
the #dread channel on efnet ...
greetz go out to everyone of faith2000, rvl & ecolove
---------------------------------------cut
here------------------------------------------------------
Cracking the Hypersnap-DX v3.30.00 CRC check and it's
anti-debugging trick. by douby^dread
WARNING!
1) The essence of this essay isn't about the serial
protection scheme of Hypersnap but about it's
anti-debugging trick and about it's CRC check.
2) When you install Hypersnap-DX make sure softice
isn't resident, otherwise Hypersnap won't
install.
Tools of the Trade.
- Softice
- Smartcheck
- Your favorite hex editor
Poppin the hood.
When you try firing up HSDX.EXE while softice is
resident you'll notice it won't start. How come?
To answer that question we have to know what the
program does before it quits. So how are we going
to find that out? Well we'll have to use a program
flow analyser and what's the best program
flow analyser we know? Smartcheck! Ok fire up
smartcheck and open HSDX.EXE (make sure supress system
API and OLE calls is off), press the play button and
after Smartcheck is finished click on the show
all events button. We want to know which API calls
we're called right before the exitprocess call.
So scroll to the end of the list and take a good look
around. Hmm OuputDebugStringA and CreateFileA
with lpFileName pointing to \\.\SICE ?? Very
suspicious don't you think ? Let's see what the API
says about the return value of OuputDebugStringA. Hmm
the API says it doesn't return shit so it has
to be the CreateFileA call that does the trick. Let's
put a bpx on OutputDebugStringA (since using
CreateFileA will cause softice to pop up a lot of
times). Ok now start Hypersnap and... bang! Back
in Softice. Press F12 to get out of the call, and this
is what you'll see:
* Reference To: KERNEL32.OutputDebugStringA, Ord:01F5h
|
:6302120F FF1540500263 Call dword ptr
[63025040]
:63021215 55 push ebp
:63021216 6880000000 push 00000080
:6302121B 6A03 push 00000003
:6302121D 55 push ebp
:6302121E 6A03 push 00000003
:63021220 68000000C0 push C0000000
:63021225 68F0670263 push 630267F0
:6302122A FFD3 call ebx ;
CreateFileA
:6302122C 83F8FF cmp eax, FFFFFFFF ;
Is SI resident?
:6302122F 7406 jz 63021237 ; if
so exit else continue
:63021231 55 push ebp
:63021232 E8D6060000 call 6302190D
Hmmz the only thing we have to do is change the jz at
6302122F to a jmp by changing 7406 into EB06.
Fire up your favorite hex editor, make the change,
fire up HSDX.EXE and... DONE! It works!
There's also a check for NTICE a bit further down the
code and it works exactely the same, so it
shouldn't be a problem to disable that one too. So
what's up next?
Removing the CRC check.
If you try to make a change in HSDX.EXE and try
running it afterwards you'll get a message box saying
something like:"Can't load Hshelper.DLL". How come?
Well probably Hypersnap uses some kind of CRC
protection to keep the program from being changed. So
how are we going to remove it? Simple we want
to know the difference between what the program does
when it encounters an incorrect CRC and what it
does when it encounters a correct CRC. So how are we
going to do that? Smartcheck! We can use it to
compare the differences between the flow of Hypersnap
when HSDX.EXE is changed and when HSDX.EXE isn't
changed.
Ok first open the unchanged HSDX.EXE in smartcheck,
run the program and save the results. Next change a
string in HSDX.EXE using your hex editor. Open it in
smartcheck and run the program, open the saved
results of HSDX.EXE running unchanged. Tile the result
windows and compare the differences (could it
get any easier ? :-) You'll soon see the first
difference between both flows, it occurs when the
ReadFile loop is finished. In the unchanged version it
calls the API call CloseHandle, in the changed
version it doesn't call it. So what are we going to
do? We're going to put a bpx on OuputDebugStringA
(the ReadFile loop comes right after the
OutputDebugStringA) get out of the loop and look for
jumps
that will take us out of the call. Using this method
you'll soon get here:
:63021329 8815E5670263 mov byte ptr
[630267E5], dl
:6302132F 8B1544600263 mov edx, dword ptr
[63026044] ;load the CRC in edx
:63021335 33D5 xor edx, ebp ;edx
xor ebp = esi
:63021337 33D6 xor edx, esi
:63021339 740F je 6302134A ;if
edx = esi CRC is correct
:6302133B 5F pop edi
:6302133C 5E pop esi
:6302133D 5D pop ebp
:6302133E 33C0 xor eax, eax
:63021340 5B pop ebx
:63021341 81C404040000 add esp, 00000404
:63021347 C20C00 ret 000C
* Referenced by a (U)nconditional or (C)onditional
Jump at Address:
|:63021339(C)
|
:6302134A 8A4006 mov al, byte ptr
[eax+06]
Hmm it's pretty simple to solve that problem, you
could for example change the je in a jmp by changing
740F into EB0F. There are many more ways of solving
this problem ofcourse but hey we don't care
about that do we?
For suggestions,corrections or just to say hello mail
me at:
douby(at)newmail(dot)com
Or visit us at #dread on EFNET.
Have fun.