;/*++
;
;    DRX General Detection Concept In Pratice.
;
;    Copyright (c) 2003 yates
;
;    Contact information:
;         web: www.yates2k.net
;         mail: yates@reverse-engineering.info
;
;
;    This program is free software; you can redistribute it and/or
;    modify it under the terms of the GNU General Public License
;    as published by the Free Software Foundation; either version 2
;    of the License, or (at your option) any later version.
;
;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License
;    along with this program; if not, write to the Free Software
;    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
;
; 
;Module Name:
;    ring0.asm
;
;Revision History:
;
; yates     19/09/2003  Basic use
; yates     20/09/2003  Stable
;--*/

.586P
.MODEL FLAT, STDCALL

OPTION      CASEMAP:NONE
UNICODE     = 0
ARGUMENTS   = 1

INCLUDE     APIMACRO.MAC
include     w32main.inc


INCLUDELIB  iNTOSKRNL.LIB
INCLUDELIB  iKERNEL32.LIB


ASSUME FS:NOTHING ; assume nothing :p

; --------------------------------------------------------------------------


; --------------------------------------------------------------------------

o equ offset

.CODE


Device_name                       dw '\','D','e','v','i','c','e','\','r','i','n','g','0',0,0,0
Device_type                       dw '\','D','o','s','D','e','v','i','c','e','s','\','r','i','n','g','0',0,0,0,0
                                  dd 0   
IDT_Base dd 0,0,0,0

old_handler dd 0

LAST_DRX dd 0
DATA_DRX dd 0

DRX_COUNT dd 0

LOG_DRX DD 1
;-----------------------.Driver.Entry.----------------------------------------

DriverEntry:  ; pDriverObject, pusRegistryPath

SymbolicLinkName = dword ptr -14h
DeviceName       = dword ptr -0Ch
DeviceObject     = dword ptr -4
DriverObject     = dword ptr  8



         push    ebp
         mov     ebp,esp
         sub     esp,14h
         push    ebx
         push    esi
         iMOV    esi,RtlInitUnicodeString
         lea     eax,[ebp+DeviceName]

         push    offset Device_name
         push    eax
         call    esi ; RtlInitUnicodeString

         mov     ebx,[ebp+DriverObject] ; DRIVER_OBJECT
         lea     eax,[ebp+DeviceObject] ; DEVICE_OBJECT

         push    eax                  ;DeviceObject
         push    0                    ;Exclusive
         push    0                    ;DeviceCharacteristics
         lea     eax,[ebp+DeviceName]
         push    22h                  ;DeviceType FILE_DEVICE_UNKNOWN
         push    eax                  ;DeviceName
         push    0                    ;DeviceExtensionSize
         push    ebx                  ;pDriverObject
         iWin32  IoCreateDevice
         test    eax,eax
         jnz     Exit_on_failed_creation

         lea     eax, [ebp+SymbolicLinkName] ; SymbolicLinkName
         push    offset Device_type
         push    eax
         call    esi ; RtlInitUnicodeString

         lea     eax, [ebp+DeviceName]
         push    eax
         lea     eax, [ebp+SymbolicLinkName]
         push    eax
         iWin32  IoCreateSymbolicLink
         mov     esi, eax
         test    esi, esi
         jz      Symbolic_link_success

         push    [ebp+DeviceObject]
         iWin32  IoDeleteDevice

         mov     eax, esi
         jmp     Exit_on_failed_creation

Symbolic_link_success:

         mov     dword ptr [ebx+34h], offset UnloadDriver       ; DRIVER_OBJECT.PDRIVER_UNLOAD
         mov     dword ptr [ebx+38h], offset RequestHandler     ; DRIVER_OBJECT.PDISPATCH_IRP_MJ_CREATE
         mov     dword ptr [ebx+40h], offset RequestHandler     ; DRIVER_OBJECT.PDISPATCH_IRP_MJ_CLOSE
         mov     dword ptr [ebx+70h], offset ServiceHandler     ; DRIVER_OBJECT.PDISPATCH_IRP_MJ_DEVICE_CONTROL

         nop     ; << important!
         call    initmysys
         call    hookint1
         

Exit_on_failed_creation:

         pop     esi
         pop     ebx
         leave
         retn    8

;------------------------.IRP.PROCESS.------------------------------------

RequestHandler:

         mov     ecx, [esp+8]
         xor     dl, dl
         and     dword ptr [ecx+18h], 0 ; _IRP.IoStatus.IO_STATUS_BLOCK.Status < STATUS_SUCCESS 
         and     dword ptr [ecx+1Ch], 0 ; _IRP.IoStatus.IO_STATUS_BLOCK.Information < nowt
         iWin32  IofCompleteRequest
         xor     eax, eax
         retn    8


;--------------------------------------------------------------------------



;--------------------------------------------------------------------------
initmysys:
 sidt    fword ptr [IDT_Base]
 
 call msg2
 db 'INIT SYS CALLED',13,10,0
 msg2:
 iWin32 DbgPrint
 add esp,4
 
 xor eax,eax
ret

failload:
 mov eax,1
ret
;--------------------------------------------------------------------------

;--------------------------------------------------------------------------
hookint1:
pushad

				  mov     ebx,dword ptr [IDT_Base+2]
				  add     ebx,1h*8   

		          mov     dx,word ptr [ebx+6]
		          shl     edx,10h
		          mov     dx,word ptr [ebx]
		          mov     [old_handler],edx
		 
		          mov     edx,offset myint1
		          mov     word ptr [ebx],dx
		          shr     edx,10h
		          mov     word ptr [ebx+6],dx
		          mov     byte ptr [ebx+5],08eh ; ADJUST DPL

popad
ret
;--------------------------------------------------------------------------

c_cr2 dd 0
c_cr3 dd 0
c_es dw 0
c_fs dw 0
c_ds dw 0
c_gs dw 0
c_ss dw 0
c_cs dw 0

c_edi dd 0
c_esi dd 0
c_esp dd 0
c_ebp dd 0
c_eax dd 0
c_ebx dd 0
c_ecx dd 0
c_edx dd 0
c_eip dd 0
c_eflags dd 0



tr_0 db 'EAX',0
tr_1 db 'ECX',0
tr_2 db 'EDX',0
tr_3 db 'EBX',0
tr_4 db 'Unknown',0
tr_5 db 'Unknown',0
tr_6 db 'ESI',0
tr_7 db 'EDI',0
tr_8 db 'Unknown',0



REG_LOOKUP:
dd o tr_0
dd o tr_1
dd o tr_2
dd o tr_3
dd o tr_4
dd o tr_5
dd o tr_6
dd o tr_7
dd o tr_8


td_0 db 'DR0',0
td_1 db 'DR1',0
td_2 db 'DR2',0
td_3 db 'DR3',0
td_4 db 'DR4',0
td_5 db 'DR5',0
td_6 db 'DR6',0
td_7 db 'DR7',0
td_8 db 'Unknown',0

tc_d db ' - ',0

DRX_LOOKUP:
dd o td_0
dd o td_1
dd o td_2
dd o td_3
dd o td_4
dd o td_5
dd o td_6
dd o td_7
dd o td_8

drx_id dd 0
reg_id dd 0

acomma db ', ',0
noidea db 'no idea wuts goin on',0
amov db 'Alert: MOV xxx, xxx ADDR: ',0
db 254 dup(0)

conv db 100h dup (0)
formath db '%08X',0

align 4

;--------------------------------------------------------------------------
myint1 proc
        PUSHAD
        PUSH FS
        PUSH DS
        PUSH ES
        MOV  EAX,00000030h
        MOV  FS,AX
        mov eax,23h
        mov ds,ax
        mov es,ax
        mov ebp,esp		
		
;----------------------------
		mov eax, cr2
		mov [c_cr2],eax
		
		mov eax,cr3
		mov [c_cr3],eax
		
		mov ax,[esp]
		mov word ptr [c_es],ax
		        
		mov ax,[esp+4]
		mov word ptr [c_ds],ax
		        
		mov ax,[esp+8]
		mov word ptr [c_fs],ax
		mov word ptr [c_gs],gs
	    mov word ptr [c_ss],ss
		
	    mov eax,[esp+12]
		mov [c_edi],eax
		
		mov eax,[esp+12+4]
		mov [c_esi],eax
		
		mov eax,[esp+12+8]
		mov [c_ebp],eax
		
		mov eax,[esp+12+12]
		add eax,12
		mov [c_esp],eax     
		
		mov eax,[esp+12+16]
		mov [c_ebx],eax
		        
		mov eax,[esp+12+20]
		mov [c_edx],eax
		        
		mov eax,[esp+12+24]
		mov [c_ecx],eax
		        
		mov eax,[esp+12+28]
		mov [c_eax],eax
		
		mov eax,[esp+12+32]
		mov [c_eip],eax
		
		mov ax,[esp+12+32+4]
		mov word ptr [c_cs],ax
		
	    mov eax,[esp+12+32+8]
        mov [c_eflags],eax
;----------------------------

mov eax,dr6
btc eax,13 ; BD EXCEPTION ?
jnc OldH
        
inc dword ptr [DRX_COUNT]
 
xor eax,eax
mov edx,[c_eip]
mov al,byte ptr [edx+2]

call get_regs ; EAX=DRX EBX=REG
mov [drx_id], eax
mov [reg_id], ebx

;------------------------

mov ecx,[c_eip]
cmp word ptr [ecx], 230Fh ; mov drx, reg
jne regdrx

drxreg:

mov eax,[drx_id]
mov ebx,[reg_id]

mov edi, o amov   ; build up output string
add edi,4+7
mov esi, dword ptr [DRX_LOOKUP+eax*4]
mov ecx, 3
rep movsb

mov eax,[drx_id]
mov ebx,[reg_id]

mov edi, o amov
add edi,9+7
mov esi, dword ptr [REG_LOOKUP+ebx*4]
mov ecx, 3
rep movsb

mov eax,[c_eip]
push   eax
push   offset formath
push   offset conv
iWin32 sprintf
add esp,0Ch

mov edi, o amov
add edi,26
mov esi, o conv
mov ecx, 8
rep movsb

push o amov      ; db 'MOV xxx, xxx',0
iWin32 DbgPrint
add esp,4
jmp return_control
;------------------------



;------------------------
regdrx:
cmp word ptr [ecx], 210Fh  ; mov reg, drx
jne panicreg


mov eax,[drx_id]
mov ebx,[reg_id]

mov edi, o amov
add edi,4+7
mov esi, dword ptr [REG_LOOKUP+ebx*4]
mov ecx, 3
rep movsb

mov eax,[drx_id]
mov ebx,[reg_id]

mov edi, o amov
add edi,9+7
mov esi, dword ptr [DRX_LOOKUP+eax*4]
mov ecx, 3
rep movsb

mov eax,[c_eip]
push   eax
push   offset formath
push   offset conv
iWin32 sprintf
add esp,0Ch

mov edi, o amov
add edi,26
mov esi, o conv
mov ecx, 8
rep movsb

push o amov      ; db 'MOV xxx, xxx',0
iWin32 DbgPrint
add esp,4



jmp return_control
;------------------------

panicreg:

push o noidea
iWin32 DbgPrint
add esp,4

mov eax,dr6
xor eax,2000h ; clear exception status
mov dr6,eax

jmp ExitH


return_control:
mov eax,dr6
xor eax,2000h ; clear exception status
mov dr6,eax

call setup_cycle

jmp ExitH


		
;----------------------------
OldH: ; dont handled exception
 mov eax,[c_eip]
 mov [esp+12+32],eax
 mov ax,word ptr [c_cs]
 mov [esp+12+32+4],ax
 mov eax,[c_eflags]
 mov [esp+12+32+8],eax
 POP     ES
 POP     DS
 POP     FS
 POPAD
 jmp     dword ptr cs:[old_handler]
;----------------------------
ExitH: ; we handled exception
 mov eax,[c_eip]
 mov [esp+12+32],eax
 mov ax,word ptr [c_cs]
 mov [esp+12+32+4],ax
 mov eax,[c_eflags]
 mov [esp+12+32+8],eax
 

 		mov eax,[c_edi]
 	    mov [esp+12],eax
 		
 		mov eax,[c_esi]
		mov [esp+12+4],eax

 		
 		
 		mov eax,[c_ebx]
		mov [esp+12+16],eax

 		        
 		
 		mov eax,[c_edx]
		mov [esp+12+20],eax
 		        
 		
 		mov eax,[c_ecx]
		mov [esp+12+24],eax
 		        
 
 		mov eax,[c_eax]
		mov [esp+12+28],eax
 		
 
 POP     ES
 POP     DS
 POP     FS
 POPAD
 iretd
myint1 endp
align 4
;--------------------------------------------------------------------------
NTICE DD 0  ; my friend :-)
;--------------------------------------------------------------------------
setup_cycle:

; ACCESS_RIGHTS

 mov eax,[c_eip]
 shr eax,18h      ; Check For NTICE
 cmp eax,0B6h     ; B6000000 -> B6FFFFFF may modify DRX
 jne fillregs

mov [NTICE],1     ; jah ok then

fillregs:

mov ecx,[c_eip]

;-------------------.Ignore.placing.values.in.drx.unless.ntice.------------------------------------------
cmp word ptr [ecx], 230Fh ; mov drx, reg
jne fill_regdrx


xor ebx,ebx
mov ecx,[c_eip]
mov bl,byte ptr [ecx+2]

and bl,7

cmp bl,0
jne c_1
mov edx,[c_eax]
call show_edx
jmp got_reg

c_1:
cmp bl,1
jne c_2
mov edx,[c_ecx]
call show_edx
jmp got_reg

c_2:
cmp bl,2
jne c_3
mov edx,[c_edx]
call show_edx
jmp got_reg

c_3:
cmp bl,3
jne c_6
mov edx,[c_ebx]
call show_edx
jmp got_reg

c_6:
cmp bl,6
jne c_7
mov edx,[c_esi]
call show_edx
jmp got_reg

c_7:
cmp bl,7
jne c_x
mov edx,[c_edi]
call show_edx
jmp got_reg

c_x:
mov edx,0 ; :-o

;-----------------

got_reg:

xor eax,eax
mov ecx,[c_eip]
mov al,byte ptr [ecx+2]
and al,0F8h

cmp al,0F8h
jne rd6
cmp [NTICE],1
je  do_7
jmp drxupd
do_7:
mov dr7,edx
jmp drxupd

rd6:
cmp al,0F0h
jne rd3
cmp [NTICE],1
je  do_6
jmp drxupd
do_6:
mov dr6,edx
jmp drxupd


rd3:
cmp al,0D8h
jne rd2
cmp [NTICE],1
je  do_3
jmp drxupd
do_3:
mov dr3,edx
jmp drxupd

rd2:
cmp al,0D0h
jne rd1
cmp [NTICE],1
je  do_2
jmp drxupd
do_2:
mov dr2,edx
jmp drxupd

rd1:
cmp al,0C8h
jne rd0
cmp [NTICE],1
je  do_1
jmp drxupd
do_1:
mov dr1,edx
jmp drxupd

rd0:
cmp al,0C0h
jne drxupd
cmp [NTICE],1
je  do_0
jmp drxupd
do_0:
mov dr0,edx
jmp drxupd

drxupd:

cmp [NTICE],1
je  emureg1

call f12
db '[o] Ignored  MOV DRX, REG',0
f12:
iWin32 DbgPrint
add esp,4
jmp Finish_Up_And_Leave

emureg1:
call f22
db 'DRX Updated',0
f22:
iWin32 DbgPrint
add esp,4
jmp Finish_Up_And_Leave
;----------------------------------------------------------------------------




;-------------------.ignore.drx.move.unless.ntice.-----------------------------------------------

fill_regdrx:
mov ecx,[c_eip]
cmp word ptr [ecx], 210Fh ; mov reg, drx
jne do_nothing


xor eax,eax
mov ecx,[c_eip]
mov al,byte ptr [ecx+2]
and al,0F8h

cmp al,0F8h
jne gd6
cmp [NTICE],1
je  gdo_7
mov edx,400h  ; fake no bpms are set
call show_edx
jmp gdrxupd
gdo_7:
mov edx, dr7
call show_edx
jmp gdrxupd

gd6:
cmp al,0F0h
jne gd3
cmp [NTICE],1
je  gdo_6
mov edx,dr6  ; return normal dr6, nowt usefull to an app
call show_edx
jmp gdrxupd
gdo_6:
mov edx,dr6
call show_edx
jmp gdrxupd

gd3:
cmp al,0D8h
jne gd2
cmp [NTICE],1
je  gdo_3
mov edx,0  ;  no bpm is set, honest guv'na
call show_edx
jmp gdrxupd
gdo_3:
mov edx,dr3
call show_edx
jmp gdrxupd

gd2:
cmp al,0D0h
jne gd1
cmp [NTICE],1
je  gdo_2
mov edx,0  ; no bpm here
call show_edx
jmp gdrxupd
gdo_2:
mov edx,dr2
call show_edx
jmp gdrxupd

gd1:
cmp al,0C8h
jne gd0
cmp [NTICE],1
je  gdo_1
mov edx,0  ; none here neither
call show_edx
jmp gdrxupd
gdo_1:
mov edx,dr1
call show_edx
jmp gdrxupd

gd0:
cmp al,0C0h
jne gdrxupd
cmp [NTICE],1
je  gdo_0
mov edx,0  ; bpms? nah i dont use em ;) 
call show_edx
jmp gdrxupd
gdo_0:
mov edx,dr0
call show_edx
jmp gdrxupd

gdrxupd:


xor ebx,ebx
mov ecx,[c_eip]
mov bl,byte ptr [ecx+2]

and bl,7

cmp bl,0
jne grc_1
mov [c_eax],edx
jmp got_reg2

grc_1:
cmp bl,1
jne grc_2
mov [c_ecx],edx
jmp got_reg2

grc_2:
cmp bl,2
jne grc_3
mov [c_edx],edx
jmp got_reg2

grc_3:
cmp bl,3
jne grc_6
mov [c_ebx],edx
jmp got_reg2

grc_6:
cmp bl,6
jne grc_7
mov [c_esi],edx
jmp got_reg2

grc_7:
cmp bl,7
jne grc_x
mov [c_edi],edx
jmp got_reg2

grc_x:
mov edx,0 ; :-o

got_reg2:


cmp [NTICE],1
je  was_emu

call dri
db '[o] Faked MOV REG, DRX',0
dri:
iWin32 DbgPrint
add esp,4
jmp Finish_Up_And_Leave

was_emu:
call dre
db '[o] Emulated MOV REG, DRX',0
dre:
iWin32 DbgPrint
add esp,4
jmp Finish_Up_And_Leave

;--------------------------------------------------------------------------------------------------------



Finish_Up_And_Leave:
do_nothing:
 mov eax,[c_eip]
 add eax,3
 mov [c_eip],eax

cmp dword ptr [LOG_DRX], 0
je  turn_off_drx
 
  mov eax,dr7
  or  eax,2000h
  mov dr7,eax
 
turn_off_drx:
mov [NTICE],0
ret
;--------------------------------------------------------------------------



CURRENT_DRX dd 0


;--------------------------------------------------------------------------
get_regs proc

xor ebx,ebx
mov bl,al

and al,0F8h

d7:
cmp al,0F8h
jne d6
mov al,7
mov ecx,dr7
mov [CURRENT_DRX],ecx
jmp regv

d6:
cmp al,0F0h
jne d5
mov al,6
mov ecx,dr6
mov [CURRENT_DRX],ecx
jmp regv

d5:
cmp al,0F8h
jne d4
mov al,5
;mov ecx,dr5
;mov [CURRENT_DRX],ecx
jmp regv

d4:
cmp al,0F8h
jne d3
mov al,4
;mov ecx,dr4
;mov [CURRENT_DRX],ecx
jmp regv

d3:
cmp al,0D8h
jne d2
mov al,3
mov ecx,dr3
mov [CURRENT_DRX],ecx
jmp regv

d2:
cmp al,0D0h
jne d1
mov al,2
mov ecx,dr2
mov [CURRENT_DRX],ecx
jmp regv

d1:
cmp al,0C8h
jne d0
mov al,1
mov ecx,dr1
mov [CURRENT_DRX],ecx
jmp regv

d0:
cmp al,0C0h
jne dn
mov al,0
mov ecx,dr0
mov [CURRENT_DRX],ecx
jmp regv

dn:
 mov al,8
regv:

and bl,7

cmp bl,8
jg nrf


ret

nrf:
mov bl,8
ret
get_regs endp
;--------------------------------------------------------------------------


;--------------------------------------------------------------------------
unhookint1:
pushad

cli


		          mov     ebx,dword ptr [IDT_Base+2]
		          add     ebx,1h*8                
		 
		          mov     edx,old_handler
		          mov     word ptr [ebx],dx
		          shr     edx,10h
		          mov     word ptr [ebx+6],dx
                  mov     byte ptr [ebx+5],0eeh ; ADJUST DPL
				  
				  

sti

popad
ret
;--------------------------------------------------------------------------

; --------------------------------------------------------------------------
;

ServiceHandler:  ; pDeviceObject, pIrp/_IRP

         push    ebp
         mov     ebp, esp
         push    ebx
         mov     ebx, [ebp+0ch] ; hm pIrp
         push    esi
         mov     edx, [ebx+60h] ; _IRP.Tail.Overlay.CurrentStackLocation IO_STACK_LOCATION.DeviceIoControl
         mov     [ebp+0ch], edx
         mov     eax,[edx+0Ch]  ;  DeviceIoDeviceIoControl.IoControlCode
		 
         cmp     eax, 10h   ; Control Code
         jne     SH_OK      ; use 10h for same input/output buffer 
                            ; to create subservices
         push    ecx
         push    edx

         push    dword ptr [ebx+0ch] ; _IRP.SystemBuffer
         iWin32  MmIsAddressValid    
                      
         pop     edx
         pop     ecx

         cmp     AL,1
         jne     SH_FAIL       ; invalid address

         mov     eax,[ebx+0ch] ; _IRP.SystemBuffer
         mov     eax,[eax]
;-----------
  cmp eax,'DRXA'
  jne check_DO

  call DRXA
   jmp SH_OK

check_DO:  
  cmp eax,'DRXO'
  jne unknown_sub_cmd
   
   call DRXO
    jmp SH_OK


unknown_sub_cmd:
;-----------
         mov     eax,[edx+08h]  ; DeviceIoControl.InputBufferLength
         mov     ecx,[edx+04h]  ; DeviceIoControl.OutputBufferLength
         mov     edi,[ebx+0ch]  ; _IRP.SystemBuffer
         mov     [ebx+1Ch],ecx  ; _IRP.IoStatus+4 ? size to write

         mov     dword ptr [edi],'eonN' ; None, no service existed :-o
         xor     esi,esi
    
SH_FAIL:

         xor     dl, dl
         mov     ecx, ebx
         iWin32  IofCompleteRequest
         xor     eax,eax
         inc     eax    ; unhandled
         jmp     Exit_SH
         
SH_OK:

         xor     dl, dl
         mov     ecx, ebx
         iWin32  IofCompleteRequest
         xor     eax,eax  ; ok

Exit_SH:

         pop     esi
         pop     ebx
         pop     ebp
         retn    8


;--------------------------------------------------------------------------

;--------------------------------------------------------------------------
DRXA:

 mov eax,dr7
 or  eax,2000h  ; GD ON
 mov dr7,eax

ret
;--------------------------------------------------------------------------


;--------------------------------------------------------------------------
DRXO:
pushad

mov [LOG_DRX],0

mov eax,dr7 ; trigger handler to reset dr7
mov eax,dr7
;double check
 mov eax,dr7
 btc eax,13
 jne closedown
 xor  eax,2000h  ; GD OFF
 mov dr7,eax
 
closedown:
popad
ret
;--------------------------------------------------------------------------



;--------------------------------------------------------------------------

show_edx proc
pushad
push   edx
push   offset formath
push   offset conv
iWin32 sprintf
add esp,0Ch


push o conv    
iWin32 DbgPrint
add esp,4

popad
ret
show_edx endp

;--------------------------------------------------------------------------
UnloadDriver:

         push    ebp
         mov     ebp, esp
         push    ecx
         push    ecx
         mov     eax, [ebp+8]

         push    dword ptr [eax+4]
         iWin32  IoDeleteDevice

         lea     eax, [ebp-8]

         push    offset  Device_type
         push    eax
         iWin32  RtlInitUnicodeString

         lea     eax, [ebp-8]

         push    eax
         iWin32  IoDeleteSymbolicLink

         nop
         nop
         nop
         call unhookint1
      
         leave
         retn    4
db 'dont panic'
END DriverEntry
; --------------------------------------------------------------------------
