KVC: Kemyam Virus Capture
1993 · C:\KVC\KVC.EXE
Loading KVC.EXE, 11 KB
An antivirus for Koko, an Egyptian virus that wiped hard drives and that no scanner on the market could find. KVC knows it byte by byte.
The story
No antivirus on the market could see Koko, a virus that was wiping hard disks across Egypt. KVC could. Version 1.05 asks the virus a question only Koko answers, then freezes it in memory without a restart. It repairs each program from the original bytes Koko carried inside itself. Its help screen promises a free new version for every new virus.
The virus no scanner could see
In 1993 a virus called Koko was spreading across PCs in Egypt, and it could wipe a hard disk. No antivirus on the market could find it. So Yasser and Ebrahim El-Etr took it apart by hand, learned its secrets, and turned them against it.
Koko lived in memory. Run one infected program and Koko stayed behind, like a background service that never stops. From then on it slipped a copy of itself into every program the PC ran. It signed each one with its name, KoKo (1). Most days it did nothing else you could see. Two days a year, it struck.
- A clean PC. Every program asks DOS, the system, for everything through one door: slot 21h. Think of one API endpoint that every app on the machine calls.
- Someone runs a program that carries Koko. Koko moves into memory and stays there, even after that program ends.
- Koko writes its own address into slot 21h, so every request reaches Koko first. It keeps the real DOS address for itself, in slot 3, a slot normal programs never use.
- Now every program that starts gets a copy of Koko, signed
KoKo (1). - On February 17, Koko shows its warning on the screen.
- On July 29, the warning comes again, and the data on the hard disk is lost. So is the data on any floppy used that day.
Yasser and Ebrahim, of the KEMYAM Computer Group in Port Said, studied its code byte by byte until they knew every trick it had. Yasser’s verdict sits in KVC’s help screen: It is a funny virus, all its tricks are untricky.
Watch KVC catch it
This PC runs in your browser, with Koko in its memory and two programs that wear its marks. Type SCAN and watch Yasser’s own KVC.EXE find Koko, sound the alarm, freeze it, and repair both files.
13 KB. It starts in a few seconds.
The screen shows what KVC prints when it is done: Koko found in memory and frozen, two files cleaned.
KVC.EXE here is his 1993 program, byte for byte. When it is done, HELLO.COM and GREET.EXE are identical to their clean originals, down to the last byte. Type DIR to see their sizes shrink back to 61 and 96 bytes, or run them: all they do now is say hello.
Hear the alarm
When KVC finds Koko in memory, it sounds a siren on the PC’s little speaker. A high tone and a low one, twenty times in a row, with no gap.
A PC speaker can only push its cone out or pull it back in. A timer chip in the PC ticks 1,193,182 times a second. KVC tells it how many ticks make one wave: 1,000 for the high tone, 1,600 for the low one. So the pitch comes out exact on every PC. Play his alarm, then drag the number and hear what the chip makes of it.
20 tones, high then low, with no gap: 1,193 Hz and 746 Hz, 65 ms each.
1,193,182 ticks a second ÷ =
For engineers: the siren, line by line
mov cx,14h ; 20 tones cmp ax,3E8h ; was the last tone the high one? mov ax,3E8h ; high: the clock divided by 1,000 mov ax,640h ; low: divided by 1,600 mov al,0B6h / out 43h,al ; timer channel 2: make a square wave out 42h,al / mov al,ah / out 42h,al ; the divisor, low byte then high byte in al,61h / or al,3 / out 61h,al ; connect the timer to the speaker mov cx,0FF00h / loop $ ; hold the tone: 65,280 turns of the loop loop 080F ; next tone in al,61h / and al,0FCh / out 61h,al ; speaker off One secret question
A virus that lives in memory needs a way to ask, am I already here? Koko’s question was a number, FF0Ch, and its answer was C0C1h. Yasser and Ebrahim found that handshake and turned it into a detector.
It works like finding a hidden service on a machine: send a request that only that service understands, and see if its handshake comes back. A clean PC has never heard the question. A PC with Koko answers it every time. Flip Koko on and off, and send a few requests.
Koko is in memory. Send the secret question and see who answers.
mov ax,0FF0Ch; the secret questionint 21h; ask through slot 21hcmp ax,0C0C1h; Koko's answer?const reply = await dos({ ax: 0xFF0C });// a request no real service hasif (reply.ax === 0xC0C1)// the handshake only Koko knows alarm('Koko is in memory');KVC asks this question first, every time it runs, before it looks at a single file. The same number marks Koko’s EXE files too. Koko writes C0C1h into their headers, and that is the first thing KVC checks in every EXE it opens.
Switched off with its own hiding place
DOS had no task manager. You could not stop a program that sat in memory. So KVC did something smarter: it used the back door Koko had built for itself.
Koko kept the real DOS address in slot 3 so it could still reach DOS itself. KVC talks to DOS through that same slot, so its calls skip Koko completely. Then it copies slot 3 into slot 21h. Every program now reaches DOS directly, and Koko’s code sits in memory with nothing pointing at it. Frozen.
In JavaScript terms, Koko had monkey-patched the system call and kept the original in a spare variable. KVC found the spare and put the original back:
slots[0x21] = slots[0x03]; // every program reaches DOS directly again Found --KoKo (1) virus-- active in RAM ##### Virus Freezed #####
- 07EDask:
push cs / pop ds - 07EF
mov ax,0FF0Ch; the secret question - 07F2
int 21h; through slot 21h - 07F4
cmp ax,0C0C1h; Koko's answer? - 07F7
jne clean; no: nobody is there - 07F9
mov ah,9 / mov dx,found; Found... - 080B
int 3; print, via slot 3 - 080C
mov cx,20; the alarm: 20 tones - 083A
out 61h,al; speaker off - 0840
mov ax,3503h; what is in slot 3? - 0843
int 3; answer: the real DOS - 0846
mov dx,bx; keep that address - 0848
mov ax,2521h; into slot 21h - 084B
int 3 - 085C
mov again,1; second look - 0862
jmp ask - 0864clean:
cmp again,0 - 086C
mov ah,9 / mov dx,freezed; Freezed - 0871
int 21h; slot 21h is safe - 0873
ret; on to the files
-
int again = 0; -
ask: -
if (dos(0xFF0C) == 0xC0C1) {// Koko answered -
puts3(again ? REBOOT : FOUND);// print through slot 3 -
siren(20); -
real = getvect3(0x03); -
setvect3(0x21, real);// slot 21h = DOS -
if (again) return;// a second copy: reboot -
again = 1; -
goto ask; -
} -
if (again) puts(FREEZED);
- AX
- 0900
- 21h
- → DOS slot 21h, the door every program uses
- 03h
- → DOS slot 3, where Koko parked the real DOS
- 2nd
- yes is this the second look?
Koko’s code is still in memory, but no program can reach it. Frozen. KVC goes on to clean the disk. Had a second copy still answered, KVC would have sounded the alarm again and told the user to restart from a clean disk after cleaning.
Koko could load itself into memory more than once. KVC was ready for that as well. After freezing, it asks the question again. If a second copy still answers, KVC sounds the alarm again and tells the user plainly what to do. Once the files are clean, turn the PC off and start it from a clean system disk.
For engineers: why slot 3
Slot 3 is the breakpoint interrupt, INT 3, which only debuggers use. Its instruction is a single byte, CCh. Koko copied the DOS entry point there, so INT 3 became a private line to DOS. KVC makes three calls on that line: AH = 09h to print, AX = 3503h to read vector 3 (DOS hands back its own address in ES:BX), and AX = 2521h to write that address into vector 21h. None of them passes through Koko’s handler.
Every file rebuilt, byte for byte
Koko had to keep each program working, or people would notice. So it carried the original bytes inside its own body. KVC needed no backup: it rebuilt every file from what Koko carried.
KVC walks every folder on the disk and opens every program. There are two kinds. A COM program is a plain run of code that starts at its first byte, like a script. An EXE starts with a header, a small manifest that tells DOS where to begin. Koko had a trick for each, and KVC undid each one. Step through both repairs on the real bytes of the demo’s two files.
1,844 bytes · clean, it was 61
The first 64 bytes
Koko’s the program’s filler
What KVC reads
KoKo (1)0A 0D 24
KoKo (1). All 21 match.2,132 bytes · clean, it was 96
| Field | Means | Value |
|---|---|---|
| bytes 2 to 5 | file size | 2,132 |
| bytes 14 to 17 | where the stack starts | 0004:C0C1 |
| bytes 20 to 23 | where the program starts | 0004:0100 |
| byte 352 | the first instructions that run | EB 60 90 EA |
| Koko’s body | two pieces of Koko’s code | 20 + 20 bytes |
C0C1h: the same number Koko gives as its answer in memory.EB 60 90 EA.Before it writes a single byte, KVC checks two of Koko’s marks in a COM file and four in an EXE. If a file shows only some of them, KVC leaves it alone and asks the user to contact the KEMYAM Computer Group. It unlocks read-only files before repairing them, so protected programs get cleaned too. Two switches shape a scan: /NS checks one folder only, and /NC reports without cleaning.
For engineers: the COM check and repair
mov cx,1Ah / mov ah,3Fh ; read the first 26 bytes mov di,059Fh / mov cx,15h ; his copy of Koko's start and name, 21 bytes repe cmpsb ; mark 1 mov dl,[0514h] / mov dh,[0515h] ; bytes 3 and 4: where the body starts shl dx,1 (4 times) ; blocks of 16 bytes into a byte count add dx,127h ; body + 295 repe cmpsb ; mark 2: BF 00 01 BE 22 01 mov ax,5700h / int 21h ; keep the date and time call 07A0h ; reopen for writing, read-only cleared int 21h ; AH = 40h: write the 26 saved bytes at the start mov ax,5701h / int 21h ; date and time back | 1991 to 1993 | 2026 | Why it is the same idea |
|---|---|---|
Fixed bytes at fixed places: KoKo (1), Koko’s repair code, a stack value of C0C1h | Antivirus signatures and YARA rules | The fastest, surest way to name a known family. |
| Asking the virus its own “are you there?” question | Using malware’s own markers, like the WannaCry kill-switch domain in 2017 | The attacker’s own check becomes the defender’s tool. |
| Pointing slot 21h back at DOS | Isolating a process so nothing can reach it | Stop it first, clean up second. |
| Rebuilding each file from the bytes Koko carried | An EDR rollback that restores changed files | Undo exactly what was done, no more. |
Every Byte of KVC.EXE
Each square is one byte of the program in memory, colored by what it is. Tap a part to open it in the full map, down to the single instruction.
11,000 bytes on the disk, packed by DIET
16,928 bytes in memory, 8,896 of them his own code
- Borland library36%
- Text32%
- Code19%
- Data8.6%
- Start-up code3.1%
- Empty space0.8%
- Table0.5%
- Header0.2%
- Fix-up list0.2%
The biggest parts, his first
- Help pages3,146 bytesYasser
- Messages of the C part1,126 bytesYasser
- EXE check and repair730 bytesYasser
- Folder scanner693 bytesYasser
- COM check and repair670 bytesYasser
- main635 bytesYasser
Mapped by hand from the program’s own bytes.