‘Say it with flowers’, demikian ungkapan entah dari mana. Namun di era teknologi informasi sekarang ini, hacker yang iseng punya ungkapan tersendiri: ‘Say it with virus!’.
Virus berisi 4250 byte kode ini termasuk virus yang sederhana dan jinak. Tugas intinya hanya untuk menginfeksi file Exe/Com yang akan dijalankan. Ada kemampuan tambahan yang memberikan nilai tambah pada virus ini, yaitu rutin pengacak kode (polimorfik) dan (sepertinya) rutin anti heuristik. Sayangnya teknik pemrograman yang digunakan sangat mudah dibaca, sehingga upaya untuk mempersulit pembuatan serum tampaknya sia-sia. Pada waktu-waktu tertentu, menit=30 dan detik<15, virus ini menampilkan efek video berupa tampilan berbahasa Perancis dalam mode grafik. Sesuai nama pembuatnya, kita sebut saja virus ini dengan nama Spanska.4250.
Instalasi dan Pencegatan
Pada saat pertama aktif virus ini membuka kunci sandinya (dekrip) lewat rutin yang dapat berubah-ubah bentuk (polimorfik). Setelah seluruh kode virus kembali ke bentuk aslinya, virus lalu mengecek kehadirannya di memori lewat fungsi AX=6969h Int 21h, dan bila menghasilkan BX=6969h berarti virus telah aktif dan rutin instalasi tidak dilanjutkan. Selanjutnya virus memodifikasi alokasi memori, memindahkan tubuhnya ke sana, dan membelokkan vektor DOS ke rutin manipulasinya. Lalu virus berusaha mencari file WIN.COM pada direktori WINDOWS, dan bila ditemukan maka file ini akan langsung diinfeksi. Pada akhir proses dilakukan pengecekan terhadap waktu sistem, yang bila sesuai maka virus ini akan melaksanakan rutin efeknya. Setelah semuanya beres maka virus melompat ke awal rutin asli program dan program berjalan seperti semula.
Dalam fungsi DOS virus ini melakukan beberapa cegatan:
- 11h (Search First using FCB), 12h (Search Next using FCB), fungsi yang biasa digunakan oleh instruksi internal DOS, DIR, dimanipulasi agar seakan-akan file yang terinfeksi tidak berubah ukurannya. Virus ini menggunakan tanda detik=30 untuk menentukan apakah suatu file telah terinfeksi, yang tentu saja tidak selalu benar. Selain itu fungsi ini juga telah usang (obsolete), dan mulai dari Windows 95 sudah tidak digunakan lagi.
- 4B00h (Load and Execute Program), dimanipulasi untuk menularkan file Exe/Com yang akan dijalankan. Ada kecenderungan virus ini hanya menulari file yang berekstensi hanya Exe atau Com saja, dan tidak untuk ekstensi yang lain. Setiap kali menginfeksi virus ini selalu melakukan pengecekan tanggal dan melakukan proses acak lewat acuan tanggal tersebut. Juga virus ini mengecek apakah file yang akan dijalankan sejenis ‘New Executable’, program-program Windows atau OS/2, dan menolak untuk menginfeksi bila ya. Dan untuk mencegah agar infeksinya menimbulkan kecurigaan, virus ini juga menolak menginfeksi file-file program yang diawali huruf berikut: TB, VI, AV, NA, VS, FI, F-, FV, IV, DR, SC, GU, CO, dan memberi perlakuan khusus terhadap program berawalan PK, AR, RA, LH, dan BA.
- 6969h, menghasilkan BX = 6969h, sebagai tanda virus telah aktif di memori.
Rutin Polimorfik
Pada virus Spanska.4250 ini mempunyai kemampuan untuk berubah-ubah bentuk. Untuk keperluan ini virus telah dilengkapi dengan kombinasi beberapa rutin dengan proses kerja yang sama namun dengan kode instruksi yang berbeda-beda. Dengan memecah proses pembuka sandi ke beberapa bagian, dan tiap bagian memiliki beberapa kemungkinan, menghasilkan kode program yang dapat berbeda-beda pada tiap kali infeksi.
Rutin intinya sbb:
|
sub_6 proc near sub_7 proc near loc_xx: |
Kelemahan utama dari cara ini adalah lokasi instruksi tetap untuk tiap-tiap bagian. Akibatnya tentu panjang rutin, walaupun isinya berubah-ubah, adalah tetap, yang mengakibatkan awal kode tersandi juga tetap. Apalagi dengan metode penyandian yang sangat sederhana, terdiri dari 6 kemungkinan: NEG, NOT, DEC, ROR, SUB, dan XOR, membuat cara untuk membuka sandinya juga sangat mudah. Kita cukup membaca awal kode tersandi, pada virus ini mulai alamat 0087h, dan lakukan operasi terbalik terhadap 6 kemungkinan di atas, yaitu: NEG, NOT, INC, ROL, ADD dan XOR. Tiap-tiap operasi dibandingkan terhadap rutin yang tidak tersandi. Bila sesuai maka metode itulah yang digunakan untuk menyandi seluruh tubuh virus. Parameter sandi, yang acak menurut tanggal, diletakkan virus pada akhir file, atau alamat relatif 1099h.
Teknik Anti Heuristik
Pada beberapa program anti virus dikenal teknik heuristik, yang tujuannya untuk mengenali serangkaian kode pada suatu program terhadap operasi yang mencurigakan. Ada beberapa kesamaan proses pada kebanyakan virus, yang umumnya tidak dijumpai pada program/pemrograman yang normal. Dengan mengenali keanehan tersebut, suatu program dapat dicurigai terserang suatu virus, walaupun virus tersebut tidak dikenal.
Melihat beberapa rutinnya, tampaknya virus Spanska.4250 ini berupaya untuk mengelabui teknik heuristik tersebut. Tampak dari beberapa instruksi yang bisa dikatakan aneh. Misalnya untuk membelokkan vektor interupsi DOS (21h), virus ini melakukan cara sbb:
| mov dx,12Dh ;alamat rutin manipulasi push ax push bp mov bp,sp mov word ptr [bp+2],2522h ;fungsi 25h pop bp pop ax dec ax ;vektor 21h int 21h |
Walaupun dengan penelusuran tubuh virus ini secara manual, cara kerja virus dapat diketahui dengan mudah. Tampaknya proses konversi dari instruksi yang normal ke bentuk anti heuristik dilakukan lewat suatu makro, fasilitas dalam bahasa assembly, yang mengakibatkan banyak terjadi perulangan instruksi hingga besar virus jadi membengkak. Untuk ukuran 4250 pada program virus yang biasa, ini bisa berupa suatu virus kompleks dengan kemampuan stealth (siluman) yang canggih.
Program Serum Virus
Bagi Anda yang berminat untuk mempelajari cara membuat program serum untuk virus ini, penulis telah menyertakan listing programnya. Program ditulis dengan bahasa Turbo Pascal versi 6.0+ pada mode real. Cara kerja maupun petunjuk pemakaian program dapat Anda pelajari dari listing tersebut. Sebenarnya listing ini hanya modifikasi dari program serum virus file buatan penulis, yang telah disesuaikan khusus untuk memberantas virus Spanska.4250.
Penutup
Melihat cara kerja dan rutin prosesnya, bolehlah virus ini masuk kategori sederhana. Namun ada satu hal yang membuat penulis salut, efek videonya. Tampilannya cukup menarik dan terkesan profesional. Untuk ukuran 4250 efek ini bisa dibilang cukup mengesankan.
Reviens. Respire. Puis repars. J’aime ton mouvement. Bruja con ojos verdes Eres un grito de vida, un canto de libertad. (eh, apa sih artinya?)
Akhirnya penulis berharap semoga program ini bermanfaat bagi pembaca sekalian. Komentar, saran, kritik, atau apapun silakan layangkan ke Antivirus Media untuk ditanggapi seperlunya.
Listing Program
{$S-,R-,I+,G+,X+,A+,B-}
{$M 7505,0,0}
{***********************************************}
{ File: ANTISPAN.PAS => ANTISPAN.EXE }
{ Deskripsi: Anti Virus Spanska.4250 }
{ Kompiler: Turbo Pascal 6.0+, Borland, Inc }
{ Utilitas: Debug.Exe, Microsoft, Corp }
{ Penulis: M. Harjono }
{ Tanggal: 27 Oktober 1997 }
{ Dedikasi: * Pembaca Antivirus Media }
{ * BL-TI-93 }
{ (c)1997 Antivirus Media }
{***********************************************}
program Anti_Virus_Spanska;
uses
Dos;
type
R_Hdr = record
case Byte of
1
Kod: Byte;
JMP: Word;
KodInfek: Byte);
2
MZ: word;
_Mod: word;
_Div: word;
Rel: word;
Hed: word;
_M1,_M2: word;
_SS,_SP: word;
CkSum: word;
_IP,_CS: word);
end;
R_Pch = record
Lc: Word;
Kb: Word;
Kj: Word;
end;
const
BnPch = 1;
SpanJ: array[1..BnPch] of R_Pch =
((Lc:$012D;Kb:$802E;Kj:$26EB));
Par_Tidak: set of 0..15 = [];
Pil: set of 0..7 = [];
BnExt = 5;
DaftExt: array[1..BnExt*3] of Char =
'EXECOMBINOVROVL';
MulaiDekrip = $0087;
ParDekrip = $1099;
LokHdrAsli = $04A0;
KodeVirus: array[1..8] of Byte =
($1F,$07,$06,$1E,$B8,$69,$69,$CD);
OpsDek: array[0..5] of Word =
($D8F6,$D0F6,$C0FE,$C0D0,$D000,$D030);
Kena: Word = 0;
Proses: Word = 0;
NSubDir: Word = 0;
TotPar = 5;
DafPar: array[1..TotPar] of Char = 'RACGN';
var
Par: string[128];
NPar: Word;
Exe: Byte;
Buf: array[0..4249] of Byte;
BufH: R_Hdr;
StrBar: string[80];
procedure Kondisi(K: Shortint);
begin
case K of
-1: Writeln(' [terinfeksi, gagal dipulihkan]');
-2: Writeln(' [terinfeksi]');
0: Writeln(' [ok]');
1: Writeln(' [terinfeksi, dipulihkan]');
end;
end;
function MCBAkhir: Word; assembler;
asm
{-- init UMB_Konv --}
mov dl,-1
mov ax,5802h
int 21h
jc @@4
xchg dx,ax
mov ax,5803h
xor bx,bx
int 21h
jnc @@4
mov dl,-1
@@4:
push dx
xor si,si
{-- mulai dari MCB pertama --}
mov ah,52h
int 21h
mov dx,es:[bx-2]
{-- telusuri MCB --}
@@1:
mov es,dx
mov al,es:[si]
cmp al,5Ah
jz @@2
cmp al,4Dh
mov ax,si
jnz @@3
add dx,es:[si+3]
inc dx
jmp @@1
@@2:
inc dx
mov es,dx
{-- cari Id virus --}
mov cx,$4000 {16K byte akhir}
xor di,di
lea si,KodeVirus
cld
lodsb
@@7:
repnz scasb
jnz @@6
push si
push di
push cx
mov cx,type KodeVirus-1
repz cmpsb
pop cx
pop di
pop si
jnz @@7
@@6:
xchg cx,ax
jnz @@3
not ax
add ax,$4000
sub ax,MulaiDekrip
mov cl,4
shr ax,cl
add ax,dx
@@3:
{-- kembalikan mem alloc stra --}
xchg dx,ax
pop ax
cmp al,-1
jz @@5
cbw
xchg bx,ax
mov ax,5803h
int 21h
@@5:
xchg dx,ax
end;
procedure WinKrit(M: Word); assembler;
asm
mov ax,$1680
add ax,M
int 2Fh
end;
procedure PMem;
var
Seg,I,J: Word;
Kena: Shortint;
begin
Kena := 0;
Seg := MCBAkhir;
if (Seg<>0) then
for I := 1 to BnPch do
with SpanJ[I] do
if (MemW[Seg:Lc] = Kb) then
begin
WinKrit(1);
MemW[Seg:Lc] := Kj;
WinKrit(2);
Kena := 1;
end;
Kondisi(Kena);
end;
procedure SetFTime(var F: file; Wkt: Longint); assembler;
asm
les di,F
mov bx,es:[di]
mov cx,word ptr Wkt
mov dx,word ptr Wkt+2
mov ax,$5701
pushf
call SaveInt21
jc @@1
xor ax,ax
@@1:
mov DosError,ax
end;
function CekVirus(var BufD): Boolean; assembler;
asm
les di,BufD
lea si,OpsDek
mov cx,type OpsDek/2
mov dl,es:[di+ParDekrip]
add di,MulaiDekrip
cld
@@1:
lodsw
push cx
push si
push di
mov cl,type KodeVirus
lea si,KodeVirus
mov word ptr cs:[@@Ops],ax
jmp @@2
@@2:
mov al,es:[di]
@@Ops:
not al {2 Byte}
inc di
inc si
cmp al,ds:[si-1]
loopz @@2
pop di
pop si
pop cx
jz @@3
loop @@1
jmp @@U
@@3:
mov ax,ds:[si-2]
mov word ptr cs:[@@Dek],ax
les di,BufD
add di,LokHdrAsli
push di
mov cl,$08
jmp @@4
@@4:
mov al,es:[di]
@@Dek:
not al {2 Byte}
stosb
loop @@4
pop si
lea di,BufH
cmp Exe,1
jz @@Exe
@@Com:
SegES lodsw
mov ds:[di],ax
SegES lodsw
mov ds:[di+2],ax
jmp @@OK
@@Exe:
SegES lodsw
mov ds:[di.R_Hdr._IP],ax
SegES lodsw
mov ds:[di.R_Hdr._CS],ax
SegES lodsw
mov ds:[di.R_Hdr._SS],ax
SegES lodsw
mov ds:[di.R_Hdr._SP],ax
mov ds:[di.R_Hdr.CkSum],cx
@@OK:
inc cx
@@U:
xchg cx,ax
end;
{$I-}
function PulihFile(NmF: string): shortint;
var
F1: file;
PjBc,Attr: Word;
LokV,Wkt: Longint;
procedure PrsFile;
begin
PulihFile := 0;
FileMode := 0;
Exe := 0;
Reset(F1,1);
if (InOutRes <> 0) then
Exit;
BlockRead(F1,BufH,SizeOf(BufH),PjBc);
if (PjBc<>SizeOf(BufH)) then
Exit;
with BufH do
begin
if (MZ = $4D5A) or (MZ = $5A4D) then
begin
if (CkSum<>$006B) or (Rel=$0040) then
Exit;
LokV := Longint(Hed + _CS) * $10 + _IP;
Inc(Exe);
end
else
if (Kod=$E9) and (KodInfek=$6B) then
LokV := JMP + 3
else
Exit;
end;
Seek(F1,LokV);
BlockRead(F1,Buf,SizeOf(Buf),PjBc);
if (PjBc<>SizeOf(Buf)) or not (CekVirus(Buf)) then
Exit;
{-- positif bervirus --}
PulihFile := -2;
if not (3 in Pil) then
Exit;
PulihFile := -1;
Close(F1);
GetFAttr(F1,Attr);
if (Attr and ReadOnly = ReadOnly) then
SetFAttr(F1,Attr and not(ReadOnly));
FileMode := 2;
Reset(F1,1);
if (InOutRes<>0) then
Exit;
GetFTime(F1,Wkt);
{-- sesuaikan hdr dengan pj file --}
if (Exe=1) then
with BufH do
begin
_Mod := LokV mod $200;
_Div := LokV div $200;
if (_Mod<>0) then
Inc(_Div);
end;
{-- tulis hdr --}
Seek(F1,0);
BlockWrite(F1,BufH,SizeOf(BufH),PjBc);
if (PjBc<>SizeOf(BufH)) or (InOutRes<>0) then
Exit;
{-- buang virus --}
Seek(F1,LokV);
Truncate(F1);
{-- normalkan waktu file --}
Wkt := Wkt and $FFFFFFF0;
SetFTime(F1,Wkt);
if (Attr and ReadOnly = ReadOnly) then
SetFAttr(F1,Attr);
if (InOutRes<>0) or (DosError<>0) then
Exit;
PulihFile := 1;
end;
begin
Assign(F1,NmF);
PrsFile;
Close(F1);
InOutRes := 0;
end;
{$I+}
{-- cari vektor 21h asli --}
function Vektor21h: Pointer; assembler;
asm
push ds
mov ax,PrefixSeg
mov ds,ax
mov si,$0005
xor bx,bx
xor dx,dx
cld
@@3:
lodsb
cmp al,$9A
jz @@1
cmp al,$EA
jnz @@2
@@1:
lds si,ds:[si]
jmp @@3
@@2:
add si,4
lodsw
cmp ax,$FF2E
jnz @@4
lodsb
cmp al,$2E
jnz @@4
lodsw
xchg si,ax
les di,ds:[si]
mov cx,$0100
@@5:
mov al,$FA
repnz scasb
jnz @@4
cmp word ptr es:[di],$FC80
jnz @@5
mov dx,es
lea bx,[di-1]
@@4:
pop ds
xchg bx,ax
end;
{-- konversi huruf kecil mjd besar --}
procedure HBesar(var S); assembler;
asm
les di,S
xor cx,cx
mov cl,es:[di]
jcxz @@L3
cld
inc di
@@L1:
mov al,es:[di]
cmp al,'a'
jb @@L2
cmp al,'z'
ja @@L2
sub al,' '
@@L2:
stosb
loop @@L1
@@L3:
end;
{-- cek ekstensi file terdaftar --}
function CekNmf(var Buf): Boolean; assembler;
asm
mov dl,0
les di,Buf
mov al,'.'
xor cx,cx
mov cl,es:[di]
inc di
cld
repnz scasb
jnz @@1
cmp cx,3
jnz @@1
lea si,DaftExt
mov bx,BnExt
@@2:
push si
push di
push cx
repz cmpsb
pop cx
pop di
pop si
jz @@3
add si,cx
dec bx
jnz @@2
jmp @@1
@@3:
mov dl,1
@@1:
xchg dx,ax
end;
function CekKbdIn: Word; assembler;
asm
mov ah,01
int 16h
jnz @@1
xor ax,ax
@@1:
end;
function BacaKar: Char; assembler;
asm
mov ax,$0C08
int 21h
cmp al,'a'
jb @@1
cmp al,'z'
ja @@1
sub al,' '
@@1:
end;
procedure CekBatal;
var
Kar: Char;
begin
if (CekKbdIn=$011B) then
begin
Write(' ':79,#13' Batal Proses (Y/T)? ');
repeat
Kar := BacaKar;
until (Kar='Y') or (Kar='T');
if (Kar='Y') then
DosError := $1001;
end;
end;
{$S+}
procedure PDir(S: PathStr);
var
SR: SearchRec;
Kond: Shortint;
begin
FindFirst(S+'*.*',AnyFile,SR);
if (DosError<>3) then
Inc(NSubDir);
while (DosError=0) do
begin
if (SR.Name[1] <> '.') and (SR.Attr and VolumeID <> VolumeID) then
begin
if (SR.Attr and Directory = Directory) then
begin
if not (1 in Pil) then
PDir(S+SR.Name+'\');
end
else
begin
if (2 in Pil) or CekNmf(Sr.Name) then
begin
Inc(Proses);
StrBar := S+SR.Name;
Kond := PulihFile(StrBar);
Write(#13' ',StrBar,' ':77-Length(StrBar),#13);
if (Kond<>0) then
begin
Write(' ',StrBar);
Kondisi(Kond);
Inc(Kena);
while (Kond=1) and (PulihFile(S+SR.Name)=1) do;
end;
end;
end;
end;
if (DosError<>$1001) then
begin
FindNext(SR);
CekBatal;
end;
end;
end;
{$S-}
function DriveDisket(Drv: Byte): Boolean; assembler;
asm
mov ax,$4408
mov bl,Drv
int 21h
mov dl,0
jc @@1
or al,al
jnz @@1
inc dx
@@1:
xchg dx,ax
end;
function DriveAktif: Byte; assembler;
asm
mov ah,$19
int 21h
end;
procedure IntNull; assembler;
asm
mov al,03
iret
end;
function ProsesPar: Shortint;
var
I,J: Integer;
begin
ProsesPar := -1;
if (Npar=0) then
Exit;
for I := 1 to NPar do
begin
Par := ParamStr(I);
HBesar(Par);
if (Par[1]='/') or (Par[1]='-') then
begin
if (Length(Par)<>2) then
Exit;
J := 1;
repeat
if (Par[2]=DafPar[J]) then
begin
Pil := Pil + [J];
J := TotPar+1;
end;
Inc(J);
until (J>TotPar);
if (J=TotPar+1) then
Exit;
Par_Tidak := Par_Tidak + [I];
end;
end;
ProsesPar := 0;
end;
var
I: Integer;
Drv: Byte;
Int21h: Pointer;
begin
Writeln(#13#254' Anti Virus Spanska.4250 v1.02'#10+
#13#254' Dibuat oleh M. Harjono'#10+
#13#254' (c)1997 Antivirus Media'#10);
{-- proses parameter --}
NPar := ParamCount;
if (ProsesPar<>0) then
begin
Writeln('Sintak: ANTISPAN <[dir1] [dir2]...> [/r /a /c /g /n]'#10#13+
' /r = hanya per direktori aktif'#10#13+
' /a = proses semua file'#10#13+
' /c = deteksi dan bersihkan'#10#13+
' /g = tunggu ganti disket'#10#13+
' /n = vektor 21h normal');
Writeln('Contoh: ANTISPAN C: D: /a /c => Bersihkan semua file di drive C: dan D:');
Exit;
end;
{-- init program --}
Int21h := Vektor21h;
if (Int21h<>nil) and not(5 in Pil) then
SetIntVec($21,Int21h);
SetIntVec($24,@IntNull);
{-- periksa memori --}
Write('Cek Memori....');
PMem;
{-- periksa file --}
Writeln('Periksa File:');
for I := 1 to NPar do
begin
if not (I in Par_Tidak) then
begin
Par := ParamStr(I);
HBesar(Par);
Par[Length(Par)+1] := '\';
if (Par[Length(Par)]<>'\') then
begin
if (1 in Pil) and (Par[Length(Par)] = ':') then
Dec(Par[0]);
Inc(Par[0]);
end;
Drv := DriveAktif;
if (Par[2] = ':') and (Par[1] in ['A'..'Z']) then
Drv := Ord(Par[1])-Ord('A');
if (Drv<=1) and (DriveDisket(Drv+1)) then
begin
if (4 in Pil) then
begin
Write(' <Tekan Enter...>',' ':61,#13);
repeat
until (BacaKar=#13);
end;
end;
PDir(Par);
end;
end;
Writeln(#13'Hasil:',' ':73,#13#10+
' Direktori = ',NSubDir:6,#13#10+
' File = ',Proses:6,#13#10+
' Terkena = ',Kena:6,#13#10);
end.