Some Tipz & trix for Win2k 1. Introduction I just wanted to write an article about NTFS5. But I am reading a lot of documentation about Win2k and I found there many functions and sequences that could be very usefull for us, virus coders. So i decided to write some tipz and trix that anybody could use. I hope I succeeded. btw It's my first english written article so pls be patient. My english sux so if you don't know what something means, just contact me. And now we can begin ... 2. NTFS5 I think you all expected this:) And i also read on virus.cyberspace.sk that english version of my article for Igi is requested. I won't exactly translate what i wrote there becoz it wasn't for coders. This will be :) 2.1. Streams Streams is not a new feature of NTFS5 and it was implemented in NTFS since the very beginning of WinNT(version 3.1) but it has been downplayed by Micro$oft. In Win2k the position of Streams is much better. And there also exists the first virus that uses Streams. It's of course mine and Benny's/29a Win2k.Stream. I think ya all have heard about it becoz of big medial success. It's an very easy and simple virus with a good idea I think. First we heard about Streams from a man called GriYo/29a (heya and thx man!) on meeting in Brno. And then when Benny came to me for some days we decided to write our first common virus (and my first). It was really funny becoz we coded through the nite and very lately we didn't even know what we are typing :) There also existed a version of Win2k.Stream with polymorfic name of stream! But next day when we woke up and talked about it in the pub we decided to write it as simple as possible. And I think we succeeded - the comment is longer than the whole code XD. First we'll look what Streams exactly are and then we'll talk more about our virus. On filesystems such as FAT, FAT32 and others exists only one unnamed stream. What do ya think it is? Exactly! The file alone. But on NTFS there exist also others (data) streams with a name. The name begins with ':' to indicate that it's a named stream (part of file) and pastes together with filename (the unnamed stream). Look at this: We have a file file.txt. It is also the unnmed stream. We would like to create a new stream within the file file.txt. We want to name it "RAT" for example. So we simply add ':' before stream name and paste it to file name. So now we have somewhere in the buffer this: "file.txt:RAT". And now there's nothing easier than just use CreateFile(A|W) to create our stream. If creation succeed you will get a handle that you can uses as it would be a normal file (it is exactly a normal file ...). Well we have a stream within the file but we forgot its name :) Any solution? Yeah there is one. It's not so comfortable as it should be but there is. For our needs we'll need a function called BackupRead that can be found in kernel32.dll. Look what MSDN says: BOOL BackupRead( HANDLE hFile, // handle to file or directory LPBYTE lpBuffer, // read buffer DWORD nNumberOfBytesToRead, // number of bytes to read LPDWORD lpNumberOfBytesRead, // number of bytes read BOOL bAbort, // termination type BOOL bProcessSecurity, // process security options LPVOID *lpContext // context information ); For our purposes we can ignore such thingiez as security and context. hFile is handle to file we want to enumerate streams. lpBuffer should point to a structure called WIN32_STREAM_ID. WIN32_STREAM_ID struc DWORD dwStreamId; DWORD dwStreamAttributes; QWORD Size; DWORD dwStreamNameSize; WCHAR cStreamName[ANYSIZE_ARRAY]; WIN32_STREAM_ID ends The first bytes of this structure represent the header of each stream. Then begins the name of the stream and after the name there is the content of stream. To enumerate all the streams, you just need to loop until BackupRead returns False. Just look at the code snippet: ; in ebx - file handle to enumerate streams enumerate_streams: push offset lpcontext push 0 push 0 @pushvar
push 20 push offset buffer push ebx call BackupRead ; read the stream header xchg eax, ecx jecxz end_enumerate_streams ; error ? push offset lpcontext push 0 push 0 @pushvar
push dword ptr [buffer+16] ; push stream_name_size push offset buffer+20 ; stream_name_size store to buffer+ push ebx ; header_size call BackupRead xchg eax, ecx ; error ? jecxz end_enumerate_streams ; Now we have in buffer+20 the stream_ ; name in Unicode. Its length is ; [buffer+16] ... push offset lpcontext ; becoz BackupRead loox at file and its @pushvar
; streams as it would be on file we must @pushvar
; seek after stream content. push dword ptr [buffer+12] push dword ptr [buffer+8] push ebx call BackupSeek xchg eax, ecx ; error ? jecxz end_enumerate_streams jmp enumerate_streams ; go on with another stream_name ... end_enumerate_streams: Well i think that this is all you should know about streams for the beginning. Just make some more coding with it and i think you will become more familiar with it and you will use it in the future. Remember the words from Kaspersky/AVP: Stream companion is a new breaktrough infection which is very hard to detect! Just make some more wrinkles to AVers ... 2.1.1. Win2k.Stream And now something more about our babe. After the execution tries to find via FindFirst&FindNextFile find victimz to infect. It infectz only *.exe files in current directory (there were no reasons to spread it). The infection worx as follows: first it chex if the file is compressed (viz. next chapter) then it creates a temp file and copies the main stream to it copies virus_body to main_victim_stream moves tempfile to stream :STR compresses the file so after infection the file loox as this: (This are pictures from AVP :)) File before infection File after infection ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³°°°°°°°°°°°°°°°°°°°³ ³°°°°°°°°°°°°°°°°°°°³ ³°°°°°°°°°°°°°°°°°°°³ ³°°° main stream°°°°³ ³°°°°°°°°°°°°°°°°°°°³ ³°°° virus body°°°°°³ ³°°°°main stream°°°°³ ³°°°°°°°°°°°°°°°°°°°³ ³°°°°°°°°°°°°°°°°°°°³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³°°°°program body°°°³ ³°°°°°°°°°°°°°°°°°°°³ ³°°°°°°°°°°°°°°°°°°°³ ³°additional stream°³ ³°°°°°°°°°°°°°°°°°°°³ ³°°°°°° :STR °°°°°°°³ ³°°°°°°°°°°°°°°°°°°°³ ³°°°°°°°°°°°°°°°°°°°³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³±±±±±±±±±±±±±±±±±±±³ ³±±±±±±±±±±±±±±±±±±±³ ³±±service streams±±³ ³±±service streams±±³ ³±±±±±±±±±±±±±±±±±±±³ ³±±±±±±±±±±±±±±±±±±±³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ then it tries to find next file etc. At the end it just runs via CreateProcess the :STR stream where is victim_body. When the victim ends it just invokes ExitProcess and ends. If any error occures it displays following text: "Win2k.Stream by Benny/29A & Ratter" "This cell has been infected by [Win2k.Stream] virus!" and ends. This is also a payload on FAT, FAT32 and others filesystems that do not support streams. And that's all. Simple ain't it? 2.2. Compression and encryption We also as first used in our babe NTFS ability to compress files. It is transparent for application so it is a great way how to reduce disk free space decreasing after infection occures. If we want to compress file we must call file_system driver via DeviceIoControl with the rite IoControlCode ... look at this code snippet from Win2k.Stream and also from my Win2k.Purple (but the first who did this was Benny/29a in his Win32.HIV. On our mini-meeting he decided that we will use it in Win2k.Stream first ...) FSCTL_SET_COMPRESSION equ 9 shl 16 or 3 shl 14 or 16 shl 2 xor eax,eax push eax @pushvar
push eax push eax push 4 @pushvar
;default compression push FSCTL_SET_COMPRESSION push ebx ;NTFS compress it = call DeviceIoControl ;mark as already infected ; = and save disk space :) and now what MSDN says: BOOL DeviceIoControl( (HANDLE) hDevice, // handle to file FSCTL_GET_COMPRESSION, // dwIoControlCode operation NULL, // lpInBuffer; must be NULL 0, // nInBufferSize; must be zero (LPVOID) lpOutBuffer, // output buffer (DWORD) nOutBufferSize, // size of output buffer (LPDWORD) lpBytesReturned, // number of bytes returned (LPOVERLAPPED) lpOverlapped // OVERLAPPED structure ); I think that it is clear. And also simple to implement to your virus. Just do it! Next thingie is Encryption. It can be easyly used by calling functions EncryptFile and DecryptFile :). I think that it could be aplied as a payload becoz if you encrypt on the machine with Win2k a file then only the user who encrypted the file has access to the file. After encyption of some files there can be very good chaos on the machine :) BOOL EncryptFile( LPCTSTR lpFileName // file name ); BOOL DecryptFile( LPCTSTR lpFileName, // file name DWORD dwReserved // reserved; must be zero ); I think i'm repeating myself but - easy to implement, easy to use ... 2.3. Sparse files I dunno if anyone finds use for sparse files in virus coding but i found this as a very nice feature of NTFS5 so i would like to talk about it here. Have you ever imagined how much space must be wasted in databases in which most of the file is null (free records)? A lot of :) And here comes a solution for such applications. Sparse files. (sounds like a promote of M$ :)) We as programmers can define where in the file lie such holes (with nulls) and say it to the filesystem. Filesystem will just store to disk datas which by which we say that are not null ... code snippet will show more: BOOL DeviceIoControl( (HANDLE) hDevice, // handle to a file FSCTL_SET_SPARSE, // dwIoControlCode operation NULL, // lpInBuffer; must be NULL 0, // nInBufferSize; must be zero NULL, // lpOutBuffer; must be NULL 0, // nOutBufferSize; must be zero (LPDWORD) lpBytesReturned, // number of bytes returned (LPOVERLAPPED) lpOverlapped // OVERLAPPED structure ); FSCTL_SET_SPARSE equ 9 shl 16 or 2 shl 14 or 49 shl 2 FILE_BEGIN equ 0 push 0 push 0 push CREATE_ALWAYS push 0 ; create file SparseFile push 0 push GENERIC_WRITE @pushsz "SparseFile" call CreateFileA xchg eax, ebx xor eax,eax push eax @pushvar
push eax push eax push eax push eax ; Sign this file as a SparseFile push FSCTL_SET_SPARSE push ebx call DeviceIoControl push FILE_BEGIN @pushvar
push 0 ; Move filepointer to 32GigaBytes push ebx ; (hyea Gig :)) call SetFilePointer push ebx ; SetEndOfFile == call SetEndOfFile ; fill with nulls to 32 gigz push ebx call CloseHandle This code snippet will create a file which size is 32GB! But acutally the real size is null :) Nice aint it ? And how to let the filesystem know that we have sparse in our file? Here's a prototype of function that we can use ... BOOL DeviceIoControl( (HANDLE) hDevice, // handle to a file FSCTL_SET_ZERO_DATA, // dwIoControlCode operation (LPVOID) lpInBuffer, // pointer to FILE_ZERO_DATA_INFORMATION (DWORD) nInBufferSize, // size of input buffer NULL, // lpOutBuffer; must be NULL 0, // nOutBufferSize; must be zero (LPDWORD) lpBytesReturned, // number of bytes returned (LPOVERLAPPED) lpOverlapped // OVERLAPPED structure typedef struct _FILE_ZERO_DATA_INFORMATION { LARGE_INTEGER FileOffset; LARGE_INTEGER BeyondFinalZero; } FILE_ZERO_DATA_INFORMATION, *PFILE_ZERO_DATA_INFORMATION; And that's all about sparse files for now ... 2.3. Reparse Points This thingy is my little favourite :) What are reparse points? A reparse point is a block of user defined data associated with a file or directory. The content of that data knows aplication and file system driver (filter) which will filtrate it. When NTFS wants to open a file and recognises that that file has a reparse point it firstly tries to find a file system filter which belongs to that reparse point (in it's structure is a tag ...). If succeeds then passes that raw data (max 16KB) to that filter and what that driver does is on him. The file system driver you install is on the top of file systems drivers. What you intercept depends on you. Do you see it? You can do everything with that file. You can infect files just by setting reparse point to it. You can change some datas in that file, store it to reparse point and whenever the file is opened you renew that content and on the file close you reinfect it. Without your file system filter will be in the file broken content ... With this you can infect !_all_! files! I must say that it is charming. But it has some holes. We must find out how to spread the mother (file_system_driver). But firstly we must create that mother :) This will be a little problem becoz we need IFSkit (kit to write installable filesystem drivers) and M$ wants too much money (for me ...) for it. If someone has it pls contact me. And it also needs some more studying. But one time it will come :)) 2.4. Mounting To this theme is not so much to say. I think that most of ya know mounting from various *nix systems such as Linux. If you want to set a volume point you will need 3 functions. GetVolumeNameForVolumeMountPoint, SetVolumeMountPoint and sometimes DeleteVolumeMountPoint. If you want documentation, lemme know. I'll give it to you. Just one thing to mention. In *nixes is this feature implemented for 30 years. Micro$oft implemented it now. That means 30 years hole between technologies?? Everyone must answer this question on his own :)) That's all for now about NTFS5. There's more to say in each of that themes I was talking about in this article but i think it is enough for the beginning. Just code and study and if you will have problems contact me. If I can help you (==if I will know it) I will help you. 3. Job kernel object You have problems while managing processes in your virus? Your virus uses IPC and creates a lot of processes and you want and comfort way how to destroy them all? In Win2k you can use a Job kernel object which lets you to group processes together and create a sandbox that restricts what these processes are allowed to do. Then you can destroy all the processes just by destroying the Job object. Let's go deeper. First you must create a job object. This can be done via CreateJobObject api fc. HANDLE CreateJobObject( LPSECURITY_ATTRIBUTES lpJobAttributes, // SD (can be null for our purposes) LPCTSTR lpName // job name (if null then job is ); // a noname job :)) So now we have created a job and we have handle for it. Now we must assign some process to it. Just use AssignProcessToJobObject ... BOOL AssignProcessToJobObject( HANDLE hJob, // handle to job HANDLE hProcess // handle to process ); Easy. Now we can place some restrictions to the processes within the job but that's not so necessary for now. I promised terminating of all processes via one api fc rite? Here it is ... BOOL TerminateJobObject( HANDLE hJob, // handle to job UINT uExitCode // exit code ); After calling this function with rite job handle will be all processes within the job terminated. 4. Otherz - in Win2k Toolhelp32 library is implemented. You can again use fc as CreateToolhelp32Snapshot, Process32First etc. It is very usefull when writing for Win9x and Win2k a per(multi)-process residency. In WinNT you can use only EnumProcesses and EnumProcessModules from psapi until now. These two functions weren't in Win9x so there were double code in viruses for both operating systems. - for easier access to registry you can use functions from Shell Light Weight API (shlwapi.dll). These functions are: SHDeleteEmptyKey SHDeleteKey SHDeleteValue SHGetValue SHSetValue SHQueryValueEx SHEnumKeyEx SHEnumValue SHQueryInfoKey SHRegGetBoolIUSValue e.g. to read a subkey, you had to open registry subkey, call RegQueryValueEx and then close the registry key. SHGetValue does everything in one step. - when you are infecting a file check it with SFCIsFileProtected which will tell you whether the file is protected or not. (I'm writing an article about how to fuck SFP and then it will be easier :)) - if you want to go to some system directories such as system32 etc. use fc ExpandEnvironmentStrings which let you use environment variables. E.g. until now you had to get windows directory and then paste system32. But now you just use %system32% environment variable which you pass to Expand ... that will return expanded path. DWORD ExpandEnvironmentStrings( LPCTSTR lpSrc, // string with environment variables LPTSTR lpDst, // string with expanded strings DWORD nSize // maximum characters in expanded string ); 5. End I need rest !!! If you aren't crazy after reading this article then you are not normal :) For such people a little song: Settle for nothing A jail cell is freedom from the pain in my home Hatred passed on, passed on and passed on A world of violent rage But it's one that I can recognize Having never seen the color of my father's eyes Yes, I dwell in hell but it's a hell that i can grip I tried to grip my family But I sliped To escape from the pain and an existence mundane I gotta 9, a sign, a set and now I gotta name Read my writing on the wall No one's here to catch me when I fall But death is on my side Suicide!!!!!! Read my writing on the wall No one's here to catch me when I fall Caught between my culture and the system Genocide!!!!!! Read my writing on the wall No one's here to catch me when I fall If ignorance is bliss Then knock the smile off my face If we don't take action now We settle for nothing later We'll settle for nothing now And we'll settle for nothing later Do you know who sings this? It's my beloved song from my beloved group. If you know name of that group tell it to me on #virus and you will get a prize. (well still dunno what the prize will look like but you will :)) And that's all for now ... If you'll find any errors just contact me pls. Thx for reading! Ratter (ratter@atlas.cz) - I'm a stranger in the world i haven't made.