+HCU 1997, Project2: Winice cracking
Phase 4
Courtesy of Fravia's page of reverse engineering
Phase 4
By IgNorAMUS -
27 May 1997
SoftIce 3.01 cracking again, NTIce this time...
(How EXE checksums work)
------------------------------------------------
When I found Project 2 on Fravia, I said to myself: Great, let's try
it. But then I thought: I have the full version of SoftIce 3.01 for
Windows 95 already, so why should I crack the trial version?
Let's crack the demo of SoftIce 3.01 for Windows NT instead.
As I expected, everything was exactly the same. The only tool I needed
was HIEW (which is really a great thing, I hope you all know it).
I took the Project 2 page and searched for the pieces of code described
there. Within a few minutes the crack of Loader32 was done.
At the time ofd my session only the first part of Project 2 was available,
so I had to do all the rest by myself. I disassembled the NTICE.SYS (which
is the NT version of WINICE.EXE) and looked for the substring "time",
following +ORC's advice in his lesson 4.2.
I found 2 imported functions: RtlTimeToTimeFields and KeQuerySystemTime.
The second function looked pretty interesting. Wow! Only one occurence
in the whole file. A few lines below KeQuerySystemTime I found the call to
a routine VERY similar to NmGetNumDaysLeft. Well, you know all this from
+Rcg's essay. So I simply changed the end of the check and thought that
everything was done.
But when I tried to start the NTIce, the only result was an error
messagebox:
System Process - Unable to Load Device Drivers
\SystemRoot\System32\DRIVERS\NTice.sys device driver could not be
loaded. Error Status was 0xc0000221.
What's this? Does our target hyde some checksum protection inside?
I tried to change some stupid byte inside the string "This program
cannot be run in DOS mode", in the file header. The result was exactly the
same - again this nasty message: checksum protection!
OK. I wanted to know where the checksum was counted and checked.
So I loaded the original (unaltered) version of NTIce - to be able to trace
it, I renamed the "cracked" NTICE.SYS to PNPISA.SYS and tried to fire
it. Again the same message.
Whenever I started up my version of NTIce (called PNPISA now), my
computer seemed to do something for a few tenths of second (I have a
P100).
So I started it again and pressed CTRL+D immediately afterwards,
"manually" trying to fish the scheme.
I landed in a piece of code that really looked like some kind of checksum
counting. I followed... after a while I got here:
0008:80178A0E CALL 80187400
0008:80178A13 TEST AL,AL
0008:80178A15 JNZ 80178A29
0008:80178A17 JMP 80178A22
0008:80178A19 MOV EAX,000000001
0008:80178A1E RET
0008:80178A1F MOV ESP,[EBP-18]
0008:80178A22 MOV DWORD PTR [EBP-1C],C0000221;
STATUS_IMAGE_CHECKSUM_MISMATCH
0008:80178A29 MOV DWORD PTR [EBP-04],FFFFFFFF
0008:80178A30 PUSH DWORD PTR [EBP-24]
0008:80178A33 PUSH FF
0008:80178A35 CALL 80178AAA
...
Where are we? Hey, look! We're not in NTIce, but in
NTOSKRNL.EXE. So it's not a Numega's protection anymore,
it's Microsoft's own protection! Interesting.
I run HIEW a looked at NTICE.SYS header description
- there is a checksum there. I wrote down its value and
traced inside the checking call (the one at 80178A0E).
I found CMP EAX,EDX at the end of the function.
The value of EDX was exactly the same as the checksum
reported by HIEW. That means that EAX must contain the
REAL checksum. I wrote this value into my NTIce/PNPISA
header, fired it - and it worked!!!
So I rebooted - NTIce was cracked! :-)
Later I searched the Internet for some EXE file description
-I wanted to know if I had traced the NT kernel just to
find something everybody knows already.
I found quite comprehensive PE EXE description at
http://www.microsoft.com/win32dev/base/pefile.htm.
Let's take a look at it: description of EXE header, PE signature, PE
header, PE Optional Header - here it is!
...
ULONG CheckSum;
...
Where's its description? Ah, here:
...
CheckSum.
A checksum value is used to validate the executable file at load time.
The value is set and verified by the linker. The algorithm used for
creating these checksum values is proprietary information and will not be
published.
...
Do you love Microsoft as much as I do?
A pretty scary world... let's take a better look at the algorithm.
Here's the code from NTOSKRNL.
(Sorry it's written by hand, but I could not disassemble it, because
WDASM8 crashes down before it reaches this piece of code and
WDASM7 simply stops around the same position :-(
; this is just a subroutine, the main entry point is a little below...
; ----------------------------------------------------------------------
873C2: push ebp
mov edx,[esp+10] ; number of word components in file
mov ebp,esp
mov eax,edx
push esi
dec edx
mov ecx,[ebp+8] ; always 0
test eax,eax
je 873F3
mov esi,[ebp+0C] ; buffer address
873D7: movzx eax,[word ptr esi] ; LOOP beginning
add ecx,eax
add esi,2
mov eax,ecx ; in this loop
and ecx,0ffff ; the checksum is done
shr eax,10
add ecx,eax
mov eax,edx
dec edx
test eax,eax
jne 873D7 ; LOOP end
873F3: mov eax,ecx
pop esi
shr eax,10
pop ebp
add ax,cx
retn 0c ; <<--------- this is the entry point
push ebx
push esi
mov esi,[esp+10] ; file length
push edi
push ebp
lea eax,[esi+1]
shr eax,1
push eax ; number of word components in file
; if the file length is odd,
; 00h is appended after the last byte
push [dword ptr esp+18] ; buffer (file image in memory)
push dword 00 ; checksum start value
call 873C2
push [dword ptr esp+14] ; buffer address
mov di,ax ; ax changes during call, so store it
call RtlImageNtHeader ; returns position of 'PE' in buffer
test eax,ea
je 87454
mov edx,[eax+58] ; checksum from the file header
mov ebx,1
cmp di,dx
mov ebp,ebx
adc ebp,-1
mov cx,dx
sub di,bp ; subtract low word of checksum
sub di,cx
mov ax,[eax+5A] ; high word of the checksum
cmp di,ax
adc ebx,-1
sub di,bx
sub di,ax ; subtract high word of checksum
jmp 87459
87454: xor di,di
mov edx,esi
87459: movzx eax,di
add eax,esi ; add file length
pop ebp
pop edi
cmp eax,edx ; compare real and reported checksum sete al
pop esi
pop ebx
retn 08
So that's all. The algorithm is just simply adding the file (by words). Whenever overflow
occurs, checksum is cut to word and incremented. The position of the checksum itself is
excluded from this processing. After "summing" the whole file, this checksum is expanded
to DWORD and the file length is added. That's all. Simple but effective. Just two remarks:
1) If you wanna easily get inside the checksum test, set breakpoint on ZwOpenFile. When
you get there, type P RET once. I'm sure you'll find the way then :-)
2) As you can see from the code "above,.class" the PE EXE file checksum is stored at
offset 05A, starting from "PE". For more info see the relative Microsoft documentation.
Happy NT drivers cracking!!
by igNorAMUS, 27 May 1997
You are deep inside Fravia's page of reverse engineering,
choose your way out:
homepage
links
anonymity
+ORC
students' essays
tools
cocktails
antismut CGI-tricks
search_forms
mailFravia
Is software reverse engineering legal?