+HCU 1997, Project2: Winice cracking
Phase 4

Courtesy of Reverser'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 Reverser, 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 

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  
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; 


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 

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:
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 

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 reverser's page of reverse engineering, choose your way out:

homepage links red anonymity +ORC students' essays tools cocktails
antismut CGI-tricks search_forms mailFraVia
Is software reverse engineering legal?