Anti-Debugging metodları iki katagoride incelenebilir:
1. Koruma Amaçlı;
2. Kendi Kendini Değiştiren Kodlar.
Bu tür koruma metodları bugünlerde en fazla virüslerde kullanılmaktadır. Bu tür trikler virüsün yaptığı işin çözülmesini engeller böylece virüsün nasıl bulaştığı ve orjinal kodu çözülemez. Ayrıca kaynak kodu da çıkartılamaz. Bu tür örneklere ait kaynak kodlar dökümanın sonuna doğru verilmiştir.
Bu tür triklerin en fazla kullanım alanı bulduğu başka bir alan ise kopyalamaya karşı koruma programlarıdır. Bu tür programlar içeride nasıl kontrol yaptıkları tespit edilemesin diye Anti-Debug, Anti-Trace ve Anti-Source metodlarıyla korunmaktadırlar. Dolayısı ile kırılmaları oldukça güçleşmektedir.
1. Koruma Amaçlı:
Koruma amaçlı metodlar kullanıcının kodu debug etmesi veya kaynak kodunu çıkartmasını engelemek için kullanılır. Bu tür metodlar normal çalışma esnasında düzgün çalışmasına rağmen Debug yapılırken sistemi sakatlamakta veya Debug programını göçertmektedirler.
1.1. Interrupt"ların devre dışı bırakılması:
Interrupt"ların devre dışı bırakılması en çok kullanılan metodlardan biridir. Değişik metodları vardır.
1.1.1. Donanım kontrolü ile interrupt"ın devre dışı bırakılması:
Port 21h üzerinden kontrol edilen 8259 Interrupt kontrolcüsüne komut göndererek IRQ hatlarının devre dışı bırakılması esasına dayanır. Port 21h adresindeki her bit bir IRQ hattını kontrol eder. Bit 0 IRQ 0"ı, Bit 1 IRQ 1"i şeklinde. Buradan 0-7 arası IRQ"lar kontrol edilebilir. Bu arada IRQ 1 klavye kontrolcüsünden gelen sinyali taşır. Klavyeden her tuşa basılışta IRQ1 çağrılır. Bu IRQ"nun karşılığı olan INTERRUPT CPU tarafından çağrılarak basılan tuşla ilgili bilginin klavyenin belleğinden alınması sağlanır. Böylece her tuşa bastığınızda bilgi uçmaz. Belleğe alınır. Eğer Debug işlemi esnasında bir sonraki komuta geç komutu verilemezse program debug edilemez. Basitçe klavyeyi kilitleyerek kullanıcının bir sonraki tuşa basması engellenir. Böylece program korunmuş olur.
Örnek:
Adres Hex Instruction
| CS:0100 |
E4 21 |
IN AL,21 |
| CS:0102 |
0C 02 |
OR AL,02 |
| CS:0104 |
E6 21 |
OUT 21,AL |
Diğer taraftan, Klavyeye ait PPI (Programmable Peripheral Interface)"ın bulunduğu Port 61h"a komut gönderilerekte klavye kilitlenebilir.
Örnek:
Adres Hex Instruction
| CS:0100 |
E4 61 |
IN AL,61 |
| CS:0102 |
0C 80 |
OR AL,80 |
| CS:0104 |
E6 61 |
OUT 61,AL |
1.1.2. Yazılımla Interrupt"ın devre dışı bırakılması:
Bu biraz daha kolay bir anti-debugging metodu"dur. Tüm yapmanız gereken Debugger"lar tarafından kullanılabileceğini düşündüğünüz interrupt vektörlerini farklı adreslere yöneltmek veya boşaltmak. Bir başka metod ise hataları kontrol eden interrupt vektörlerini kendi üzerinize alarak üstünüze aldığınız hata konrolcüsünün hatasını devreye sokmak. Unutmamanız gereken işlem bittikten sonra eski vektörleri yerine getirmenizdir. Interrupt vektörünü Int 21h"ın 25h fonksiyonunu kullanmak yerine kendinizin değiştirmesi.Çünki Int 21h"ı çağırdığınızda debugger bunu anlayıp kendisini zincire ekleyerek sizi kandırabilir. Bu sebeble gidip kendiniz değiştirmeniz önerilir. Aşağıdaki örnek interrupt 03h - Breakpoint interrupt"ının ele geçirilmesi gösterilmektedir.
Örnek:
Adres Hex Instruction
| CS:0100 |
EB 04 |
JMP 0106 |
| CS:0102 |
00 00 |
ADD [BX+SI],AL |
| CS:0104 |
00 00 |
ADD [BX+SI],AL |
| CS:0106 |
31 C0 |
XOR AX,AX |
| CS:0108 |
8E C0 |
MOV ES,AX |
| CS:010A |
26 8B 1E 0C 00 |
MOV BX,ES:[000C] |
| CS:010F |
89 1E 02 01 |
MOV [0102],BX |
| CS:0113 |
26 8B 1E 0E 00 |
MOV BX,ES:[000E] |
| CS:0118 |
89 1E 04 01 |
MOV [0104],BX |
| CS:011C |
26 C7 06 4C 00 00 00 |
MOV Word Ptr ES:[000C],0000 |
| CS:0123 |
26 C7 06 4E 00 00 00 |
MOV Word Ptr ES:[000E],0000 |
1.1.3. Vektör Ayarlamaları
Bu metod interrupt vektörleri üzerinde oynayarak, bu vektörlere akışı yönlendirmeye dayanır. Bu tür bir işlem kodçözücü kodda da kullanılmaktadır. (Bkz. 2.1) Bu metodda bilgiler interrupt vektörlerinin işaret ettiği yerde tutulur. Elbetteki normal çalışma esnasında 01h, 03h kullanılmaz. Tabi debug etmeyi düşünmüyorsanız. Bu metod gayet güzel çalışmaktadır.
Örnek:
Adres Hex Instruction
| CS:0100 |
31 C0 |
XOR AX,AX |
| CS:0102 |
8E D0 |
MOV SS,AX |
| CS:0104 |
BC 0E 00 |
MOV SP,000E |
| CS:0107 |
2E 8B 0E 34 12 |
MOV CX,CS:[1234] |
| CS:010C |
50 |
PUSH AX |
| CS:010D |
31 C8 |
XOR AX,CX |
| CS:010F |
21 C5 |
AND BP,AX |
| CS:0111 |
58 |
POP AX |
| CS:0112 |
E2 F8 |
LOOP 010C |
1.1.4. Interrupt Yerdeğiştirme
Bu biraz çatlakça bir trik. Ve sadece programınızın artık daha fazla debug istemediğini düşünüyorsanız kullanın. Interrupt 16h ve 21h"ın vektörlerini 01h ve 03h"a kopyalamakla ne yapılabilir. Normal bir çalışmada böyle bir şey yapılmaz. Eğer kullanıcı programı debug etmek isterse programın içinde geçen INT 01 komutları normal bir INT komutuna döndürmek zorundadır. Bu trik çok etkili olabilir. Çünki herhangi bir şekilde CD01(INT 01)"i CD16 (INT 16)"ya değiştirmek kolaydır. Ancak Int 03"için özel komut olan 0CCh tek byte"tır. ve iki byte"lık bir komutla değiştirilemez.
Örnek:
Adres Hex Instruction
| CS:0100 |
FA |
CLI |
| CS:0101 |
31 C0 |
XOR AX,AX |
| CS:0103 |
8E C0 |
MOV ES,AX |
| CS:0105 |
26 A1 84 00 |
MOV AX,ES:[0084] |
| CS:0109 |
26 A3 04 00 |
MOV ES:[0004],AX |
| CS:010D |
26 A1 86 00 |
MOV AX,ES:[0086] |
| CS:0111 |
26 A3 06 00 |
MOV ES:[0006],AX |
| CS:0115 |
B4 4C |
MOV AH,4C |
| CS:0117 |
CD 01 |
INT 01 |
1.2. Zaman Kontrolü:
Nispeten daha az kullanılan bir metoddur. Ancak programın çalışması esnasında tüm interrupt"ları devre dışı bırakan debuggerları (Örnek: Borland Turbo Debugger) devre dışı bırakmak için gayet kullanışlı bir metoddur. Bu metod basitçe saati kontrol eder ve değişene kadar kısır döngüde bekler. Bu değer interrupt 08h tarafından değiştirilir. Diğer bir örnek eğer Port 21h"dan okunan değer (IN) 01h ile OR"lanır ve tekrar Port 21h"a yazılsa bile (IRQ 0"ı devreye sokar) Eğer hala devreye girmemiş ise o zaman debugger aktiftir. Şunu dikkate almak gerekiyor. Bu metod RUN yapılıyorsa etkindir. Eğer TRACE veya STEP by STEP çalıştırılsa çalışmaz.
Örnek:
Adres Hex Instruction
| CS:0100 |
2B C0 |
SUB AX,AX |
| CS:0102 |
FB |
STI |
| CS:0103 |
8E D8 |
MOV DS,AX |
| CS:0105 |
8A 26 6C 04 |
MOV AH,[046C] |
| CS:0109 |
A0 6C 04 |
MOV AL,[046C] |
| CS:010C |
3A C4 |
CMP AL,AH |
| CS:010E |
74 F9 |
JZ 0109 |
1.3. Debugger"ı yanıltmak:
Bu metod gayet güzel bir tekniktir. Turbo Debugger ve benzeri debugger"lar için kullanılır. Bu işlem bir komutun ortasına atlayarak yapılır. Aslında bir sonraki komut aptal bir komuttur. ve içerisinde başka bir komutu içerir. Siz aptal komutu atlayıp gerçek komuta atladığınızda debugger"ı yanıltmış olursunuz. Ancak normal step debugger kullanıyorsanız (Örnek Debug veya SymDeb) bu işe yaramayacaktır. Çünki komutlarınız komut komut çalıştırıldığından jump ettiği noktada sizi bekleyecektir.
Örnek:
Adres Hex Instruction
| CS:0100 |
E4 21 |
IN AL,21 |
| CS:0102 |
B0 FF |
MOV AL,FF |
| CS:0104 |
EB 02 |
JMP 0108 |
| CS:0106 |
C6 06 E6 21 00 |
MOV Byte Ptr [21E6],00 |
| CS:010B |
CD 20 |
INT 20 |
Şuna dikkat edin :
Adres Hex Instruction
Dikkat:
Bu trik herhangi bir debugger"ın çalışmasını etkilemez. Bu sadece kullanıcının başka bir komutun çalıştırldığını düşünürken başka bir komut çalıştırılır.
1.4. Check CPU Flags:
This is a nice trick, effective against almost any real mode debugger. What you should do is simply set the trace flag off somewhere in your program, and check for it later. If it was turned on, a debugger runs in the background...
Örnek:
Adres Hex Instruction
| CS:0100 |
9C |
PUSHF |
| CS:0101 |
58 |
POP AX |
| CS:0102 |
25 FF FE |
AND AX,FEFF |
| CS:0105 |
50 |
PUSH AX |
| CS:0106 |
9D |
POPF |
Programın ortasında :
Adres Hex Instruction
| CS:1523 |
9C |
PUSHF |
| CS:1524 |
58 |
POP AX |
| CS:1525 |
25 00 01 |
AND AX,0100 |
| CS:1528 |
74 02 |
JZ 152C |
| CS:152A |
CD 20 |
INT 20 |
1.5. Debugger"ın çalışmasını kesme:
Bu teknik debugger RUN modunda iken çalışmasını keser. Yapacağınız şey programın içerisinde rastgele yerlere INT 3 komutu yerleştirmek. RUN modunda olduğunuz halde program hep bir yerlerde kesilir ve siz sürekli RUN demek durumunda kalırsınız. Bu ise oldukça sıkıcı bir durum. Eğer debugger dışında iseniz bu bir çırpıda geçecektir.
Örnek:
Adres Hex Instruction
| CS:0100 |
B9 64 02 |
MOV CX,0264 |
| CS:0103 |
BE 10 01 |
MOV SI,0110 |
| CS:0106 |
AC |
LODSB |
| CS:0107 |
CC |
INT 3 |
| CS:0108 |
98 |
CBW |
| CS:0109 |
01 C3 |
ADD BX,AX |
| CS:010B |
E2 F9 |
LOOP 0106 |
1.6. Bilgisayarı Stack kullanarak çökertmek:
Bu trik debugger"ların kendi stack"larını kullanmak yerine kullanıcı programının stack"ını kullanması esasına dayanır. Eğer debug çalışıyorsa stack değişecektir. Stack kod alanını gösterdiğinden komutlar işletilirken aradaki stack bölgesindeki abuk sabuk komutlar sistemi çökertir. Böylece debug yapılırken sistem çöker. Eğer debug çalışmıyorsa bu alanlar değişmeyeceğinden program normal çalışır ve bu alanı geçer.
Önemli not: Bu kodu çalıştırırken CLI yapıp iş bittikten sonra STI yapmayı unutmayın. Eğer bu komut aralığında herhangi bir interrupt çağrılırsa bu alan değişeceğinden bilgisayar olmadık bir yerde çöker.
Örnek:
Adres Hex Instruction
| CS:0100 |
8C D0 |
MOV AX,SS |
| CS:0102 |
89 E3 |
MOV BX,SP |
| CS:0104 |
0E |
PUSH CS |
| CS:0105 |
17 |
POP SS |
| CS:0106 |
BC 0B 01 |
MOV SP,010B |
| CS:0109 |
90 |
NOP |
| CS:010A |
90 |
NOP |
| CS:010B |
EB 02 |
JMP 010F |
| CS:010D |
90 |
NOP |
| CS:010E |
90 |
NOP |
| CS:010F |
89 DC |
MOV SP,BX |
| CS:0111 |
8E D0 |
MOV SS,AX |
1.7. TD386"yı V8086 modundayken çökertmek:
Bu Turbo Debugger"ın V8086 modulü (TD386)"nü çökertmek için güzel bir metoddur. Temeli sıfıra bölme işlemidir. Sıfıra bölme işlemi sistemi çökertip programın kesilmesine neden olur. Halbuki sıfıra bölme işlemi INT 00h"ı çağırır. Eğer siz INT 00h vektörünü bir sonraki komuta getirirseniz böylece sistem çakılmadan yoluna devam eder. Halbuki Turbo Debugger bunu farkettiği anda programı keser. Normal çalışırken ise sorun çıkmaz.
Önemli not: Eski INT 00h vektörünü kaydetmeniz gerekiyor. İşiniz bittiğinde bu vektörünü düzeltin. Eğer bunu yapmazsanız sistem bir sonraki INT 00h çağrısında çökecektir.
Örnek:
Adres Hex Instruction
| CS:0100 |
31 C0 |
XOR AX,AX |
| CS:0102 |
8E D8 |
MOV DS,AX |
| CS:0104 |
C7 06 00 00 12 01 |
MOV WORD PTR [0000],0112 |
| CS:010A |
8C 0E 02 00 |
MOV [0002],CS |
| CS:010E |
B4 00 |
MOV AH,00 |
| CS:0110 |
F6 F4 |
DIV AH |
| CS:0112 |
B8 00 4C |
MOV AX,4C00 |
| CS:0115 |
CD 21 |
INT 21 |
1.8. Herhangi bir V8086 Prosesini çökertme:
TD386"yı göçertme metodlarından biri Hatalı Komut işletmektir. Ne yazıkki, V8086 ortamında çalışan başka bir program tarafından işletilen bir hatalı komut ta sizin sisteminizi etkileyebilir. Metod sıfıra bölme metodu ile aynıdır. Ancak bu metodda interrupt 0Dh (13) kullanılır. İşletilen hatalı bir komut programda bir sonraki satırdan devam etmenizi sağlar. Ancak Debugger üzerinden çalıştırıyorsanız işletilen komutla debugger duracak ve programı kesecektir. Böylece debug yapılması engellenmiş olur.
Önemli not: Orjinal interrupt vektörlerini eski değerlerine döndürmezseniz herhangi bir sakatlık durumunda sistemin çakılmasına neden olur.
Örnek:
Adres Hex Instruction
| CS:0100 |
31 C0 |
XOR AX,AX |
| CS:0102 |
8E D8 |
MOV DS,AX |
| CS:0104 |
C7 06 34 00 13 01 |
MOV WORD PTR [0034],0113 |
| CS:010A |
8C 0E 36 00 |
MOV [0036],CS |
| CS:010E |
83 3E FF FF 00 |
CMP WORD PTR [FFFF],+00 |
| CS:0113 |
B8 00 4C |
MOV AX,4C00 |
| CS:0116 |
CD 21 |
INT 21 |
2. Kendi Kendini Değiştiren Kodlar:
2.1. Şifreleme / Çözme algoritmaları :
İlk katagori basit bir kod, bu kod şifrelidir ve basit bir çözme rutini eklenmiştir. Bu arada eğer debugger bir breakpoint koymuşsa buda arada kaynayacağından farklı bir komuta dönüşecektir. Dolayısıyla sistemin çakılmasına neden olur. Böylece debugging işlemi kesilir. Aşağıdaki örnek Haifa virüsünden alınmıştır. Eğer siz CS:0110 adresine breakpoint koyacak olursanız, asla bu adrese ulaşamazsınız. Sebebi işlemin sonucunda ne olacağı kimse tarafından bilinemez.Dolayısıyla ne kod işletileceği bilinemez.
Not: eğer trace işlemi için çok fazla uğraşmak istemiyorsanız. çözme işlemini kodun sonundan itibaren başlatın. Böylece enazından çok fazla uğraşmadan debug işlemini gerçekleştirebilirsiniz.
Örnek:
Adres Hex Instruction
| CS:0100 |
BB 71 09 |
MOV BX,0971 |
| CS:0103 |
BE 10 01 |
MOV DI,0110 |
| CS:0106 |
91 |
XCHG AX,CX |
| CS:0107 |
91 |
XCHG AX,CX |
| CS:0108 |
2E 80 35 97 |
XOR Byte Ptr CS:[DI],97 |
| CS:010C |
47 |
INC DI |
| CS:010D |
4B |
DEC BX |
| CS:010E |
75 F6 |
JNZ 0106 |
| CS:0110 |
07 |
POP ES |
| CS:0111 |
07 |
POP ES |
2.2. Kendini-Değiştiren Kodlar
2.2.1. Basit Kendini Değiştirme:
Bu metod basit bir şifreleme metodlarına dayanır. Bir komutu işletmeden evvel değiştirmeye dayanır. Aşağıdaki örnekte Call işlemi yapıldıktan sonra Call"dan sonra gelen komut değiştirilir. Eğer "P"/Debug veya F8/Turbo Debugger ile işletirseniz programın bir anda sonlandığını göreceksiniz. Trace yaparsanız CD 20 olan bir sonraki komut trace işlemi esnasında CC 20"ye dönecektir. CC Int 03h"ı çağırdığından debugger kontrolü ele alabilir. Oysa burası CALL yapıldıktan sonra rutinin içerisinde başka bir kod ile değiştirilir.
Örnek:
Adres Hex Instruction
| CS:0100 |
E8 04 00 |
CALL 0107 |
| CS:0103 |
CD 20 |
INT 20 |
| CS:0105 |
CD 21 |
INT 21 |
| CS:0107 |
C7 06 03 01 B4 4C |
MOV Word Ptr [0103],4CB4 |
| CS:010D |
C3 |
RET |
Dikkat:
Adres Hex Instruction
2.2.2. The Running Line (Kendi kendini şifreleme):
Bu örnek Kendi kendini kontrol eden ve kendi kendini değiştiren bir koddur. Bazen "The Running Line" ismiyle anılıur. Bu Serge Pachkovsky tarafından kodlanmıştır.Basit trik noktaları olan bir gösterimdir. Ancak burada bahsedilen diğer tekniklerden farklı olarak, Bu göreceli olarak vektör tablosundaki korumaları engeller. Bu son derece basitleştirilmiş bir örnektir.
XOR AX, AX
MOV ES, AX
MOV WORD PTR ES:[4*1+0],OFFSET TRACER
MOV WORD PTR ES:[4*1+2],CS
MOV BP, SP
PUSHF
XOR BYTE PTR [BP-1], 1
POPF
MOV AX, 4C00H ; This will not be traced!
DB 3 DUP ( 98H )
DB C5H, 21H
TRACER:
PUSH BP
MOV BP, SP
MOV BP, WORD PTR [BP+2]
XOR BYTE PTR CS:[BP-1], 8
XOR BYTE PTR CS:[BP+0], 8
POP BP
IRET
2.2.3. Prefetch Instruction Queue (PIQ) Ayarlamaları
Bu metod herhangi bir debugger"ı atlatabilir. veya herhangi bir bir seferde bir işlem yapan debugger"ları atlatabilir.PIQ CPU üzerinde bulunur. Komutları işletmeden evvel belleğe çekilir. Böylece işletmek için tekrar çağrı yapılmaz. Zaten CPU içerisinde mevcuttur. Eski CPU"larda bu 6 veya 4"tür. Yenilerde ise 25 kadardır. işlem basitçe şu şekilde tanımlanabilir. Bir şekilde PIQ ile Bellekteki bilgi farklılaştırılır. Kod bu şekilde yazılır. Eğer debugger içerisinde ise adım adım program çalıştırıldığından PIQ her defasında temizlenir. Dolayısıyla her zaman güncel kalır. Oysa normal çalışma esnasında orjinal kalan ve değişmeyen PIQ"dan dolayı eski kod işleme sokulur. Ve program bir sonraki satırı işler geçer. Oysa farklılaşmanın olmadığında program döner durur.
Örnek:
Adres Hex Instruction
| CS:0100 |
B9 75 02 |
MOV CX,0275 |
| CS:0103 |
BE 90 01 |
MOV SI,0190 |
| CS:0106 |
89 F7 |
MOV DI,SI |
| CS:0108 |
AC |
LODSB |
| CS:0109 |
C7 06 0F 01 24 06 |
MOV Word Ptr [010F],0624 |
| CS:010F |
34 73 |
XOR AL,73 |
| CS:0111 |
AA |
STOSB |
| CS:0112 |
C7 06 0F 01 24 06 |
MOV Word Ptr [010F],0624 |
| CS:0118 |
E2 EE |
LOOP 0108 |
Şuna Dikkat edin.
Adres Hex Instruction
===============================================================================
Açıklamalar:
Program örneklerinde CLI/STI çifti kullanılmamıştır. Interrupt kontrolü veya üzerine alma durumlarında bu komutların başta ve sonra yer alması gereklidir. Bunun sebebi eğer siz interrupt vektörünü değiştirirken interrupt çağrılırsa sistem çökecektir.