12/2006 by Zadig.

Introduction:

BeOS comes with a nice feature called replicants. Replicants are graphic objects that can be used by any application. This allows to reuse big parts of code. For example NetPositive is a replicant. This allows any application to add a NetPositive view in it. When originaly presented by be inc, this features was compared to activex components of windows: They both are binary objects that can be embedded in any application. However this behaviour brings some security problems because a replicant is part of the host application and not a standalone application. Thus replicants are probably the easiest way for a malware to hide in a BeOS system. All tests of this document where done on Zeta 1.1, but similar results should be obtained on any BeOS compatible system.


I- Application definition.

II- Images, addons, and replicants.

1- Using addons.

2- Replicants.

III- A hidden replicant.

1- Replicant hiding.

2- Automatic replication.

IV- Identification of replicants.

1- Identify replicants in the running image.

2- Determining the calling replicant.

V- Conclusion.

VI- References.


I- Application definition.

Before trying to identify a replicant running in an application we must know what defines an application. An application can be considered as several things depending on where we look. From a storage point of view an application is identified by a Mime-type and has executable attributes. System wise, a running application is composed of several parts: Each running application is a team, and a team is composed of several elements: threads, images and areas:

  • Threads are the differents tasks of the application.
  • Images are the different files that has been loaded to be able to run the application: The app file and the needed shared libraries/addons.
  • Areas are memory segments. Each loaded image will need several areas to run. These areas correspond to the elf segments of each image.

There are several shell tools available to list these elements on a running application. The following example shows the content of a running terminal:

>ps
...
/boot/beos/apps/Terminal (team 119) (uid 0) (gid 0)
  17551      Terminal
  17555    Terminal 2
  17556  RNodeManager
...

>listimage 119
TEAM  119 (/boot/beos/apps/Terminal):
   ID                 name                   text       data 
----------------------------------------------------------------
 1370 /boot/beos/apps/Terminal             0x80000000 0x80037f40
 1371 /boot/beos/system/lib/libzeta.so     0xec354000 0xec3e56a0
 1372 /boot/beos/system/lib/libbe.so       0xec0a8000 0xec2d27a0
 1373 /boot/beos/system/lib/libtracker.so  0xec6e0000 0xec87d540
 1374 /boot/beos/system/lib/libroot.so     0xec000000 0xec074120
...

>listarea 119
memory: total: 524288KB, used: 328816KB, left: 195472KB

/boot/beos/apps/Terminal (team 119)
  ID                              name   address     size
---------------------------------------------------------
21253    user stack of Terminal  fc000000 ffd000     4000
21255             Terminal_seg0  80000000  37000    2e000
21256             Terminal_seg1  80037000  12000    12000 
21257           libzeta.so_seg0  ec354000  91000    81000
21258           libzeta.so_seg1  ec3e5000  1a000    1a000
21259             libbe.so_seg0  ec0a8000 22a000   1e9000
21260             libbe.so_seg1  ec2d2000  82000    81000
21261        libtracker.so_seg0  ec6e0000 19d000   15b000
21262        libtracker.so_seg1  ec87d000  6b000    6a000
21263           libroot.so_seg0  ec000000  74000    72000
21264           libroot.so_seg1  ec074000  34000     c000
...
21281                      heap  80049000  2f000    1f000
21285            rw_server_area  d0000000   1000     1000
21286            ro_server_area  de000000   1000     1000
21287     global_ro_server_area  df000000   9000     9000
21290  user stack of Terminal 2  fcffd000  3d000     3000
21291            user stack of   fd03a000  3d000     1000
21300            user stack of   fd077000  3d000     1000
21301            user stack of   fd0b4000  3d000     1000

In this truncated example there ios a running terminal in the team 119 that is composed of 5 images: The application and 4 libraries. 2 areas are needed for each library. These areas correspond to the code and data segments of the elf files. Finally some other areas are created for the stacks of each thread. These are the main elements that will be used in this text.

II- Images, addons, and replicants.

BeOS systems recognize 3 types of images: Application, library, and addon. An application is an executable code that can be launched. A library is a shared library (!). An addon is some code that can be loaded by an application. Basically an addon can be seen as an application or a library that has been loaded by another application. This last sentence is very important because it says one very specific thing about BeOS binaries: On BeOS all elf files are DYN files. This means that applications and libraries have the same elf type. We will see later that this is very important for the replicants.

1- Using addons.

There are several solutions to load addons from an application. The first way is to use the function:

   image_id load_add_on(const char *pathname)
This allows to load the addon in the application by providing the elf file of the addon. But there is also another way to load addons indirectly:
   BArchivable *instantiate_object(BMessage *archive)

This allows to instantiate a new object of a class. If the archive param contains only the class name, the class definition is searched in the application code. However you can also add a mime-type information that will tell the system in which addon the class is defined. In this case the system will search for the addon, load it and instantiate the class. The important point here is that when using the second method, the addon must have registered a mime-type to be loadable. Otherwise the system does not know where to load the file.

2- Replicants.

Now let's see what is a replicant. Basically a replicant is just an addon with mandatory elements:

  • It must have a mime-type so that applications can load it.
  • It must exports a BView that will be replicated.
So a replicant is just a way to export a graphic class to other applications. A replicant usually uses the BDragger class to be exported to a host application. The BDragger class has a graphic component (a hand) that can be dragged on other applications. When being dragged this componement archives the replicant BView and sends the archived BMessage to the host application. To handle these archieved BView messages, the hosts application uses the BShelf class. A BShelf object is attached to a BView and handles the replicant archived messages to instantiate the replicants. The next figure shows a simplified view of the replication process:

repapp

  • The replicant app generates a BMessage containing the archive of the view to replicate.
  • The replicant app sends this BMessage to the host application.
  • The host application loads the replicant addon and instantiate the BView object(by calling instantiate_object()).
More details on replicants and how to code them can be found in [REP1] and [REP2].

III- A hidden replicant.

For the purpose of this document, the MalRep replicant has been written. The sources of MalRep are available at [MALREP]. This replicant is just a small square with a context menu to try the different techniques explained here. To be considered as really hidden to the user, a replicant must meet two conditions:

  • There must be no visible element on screen.
  • It should automatically replicates to a host when the replicant app is launched.
To ease the debug of MalRep, the shelfer tool has been used. Shelfer is a program that just contains a BShelf. Using it avoids crashing the deskbar or the tracker when developping replicants. Moreover by starting it from a terminal we can see printf outputs in the terminal. Shelfer is available at [SHELFER].

1- Replicant hiding.

Hiding a replicant is quite simple. The BView of the replicant must just implement the Draw virtual method. In this method it must set its own color to the one of its parent BView:

void  MalRepView::Draw(BRect c_UpdateRect)
{   
   if(Parent() != NULL)
   {
      SetHighColor(Parent()->ViewColor());
      FillRect(c_UpdateRect);
   }
}
That is all: The replicant is invisible with only 5 lines of code. However to make MalRep visible for the tests, a white rectangle is drawn on the borders of the view:
      SetHighColor(255,255,255);
      StrokeRect(BRect(0.0,0.0,10.0,10.0));

2- Automatic replication.

There are several solutions to make the replication automatic:

  • Use the deskbar as a host. The deskbar exports the BDeskbar::AddItem method to allow any application to add/remove replicants in the deskbar.
  • Send a message to the host app so that its BShelf adds the replicant.
The advantage of the first method is that the deskbar is always running and restarts all its replicants each time it is started. The second method seems only interesting to add the replicant in the Tracker because it is the only other application accepting replicants that is always started. Nevertheless it is explained here because it uses the application scripting feature of BeOS. Readers unfamiliar with it should read [SCRIPTING] for a detailed description of it. Sending a message to the shelfer so that it adds the replicant is done in 2 steps: Get the correct messenger in the host app, then send a B_ARCHIVED_OBJECT to this messenger. Here is how to get the BMessenger:
BMessenger MalRepView::MessengerForShelfer()
{
   BMessage c_Request(B_GET_PROPERTY), c_Reply;
   BMessenger  c_Host;
   BMessenger  c_Result;

   c_Request.AddSpecifier("Messenger");
   c_Request.AddSpecifier("View", "view");
   c_Request.AddSpecifier("Window", (int32) 0);
   c_Host = BMessenger("application/N3S.shelfer", -1);
   
   if (c_Host.SendMessage(&c_Request, &c_Reply) == B_OK) 
      c_Reply.FindMessenger("result", &c_Result);
   return c_Result;
}
This code is specific to Shelfer. Different specifiers would be needed for other host applications. Then sending the message to force replication just require 3 lines of code:
status_t MalRepView::ExportToShelfer()
{
   status_t i_ret;
   BMessenger  c_Target;

   c_Target = MessengerForShelfer();

   BMessage c_Request(B_ARCHIVED_OBJECT);
   Archive(&c_Request, false);
   i_ret = c_Target.SendMessage(&c_Request);

   return(i_ret);
}
The Archive method will archive our BView class in a message. It is then sent to the host app. This process is quite the same than when using a dragger. The replicant is now hidden, and replicates automatically when the application is started.

IV- Identification of replicants.

Starting MalRep in Shelfer confirms that replicants are hidden from the system: MalRep is not visible in the team monitor. Only shelfer is listed.

1- Identify replicants in the running image.

Let's see first how to detect that a replicant is running in an application. This can be done by listing the images loaded by the team. The API get_next_image_info lists all images of a team. It can be used to build a list of the images. A first filter can be applied by looking at the images type: All potential replicants have a B_ADD_ON_IMAGE type. Finally if the image file has a mime type it can be considered as a replicant. All this can be done in a few lines of code:

while(get_next_image_info(0, &i_Cookie, &s_Info) == B_OK)
{
   BNode c_ImgNode(s_Info.name);
   BNodeInfo c_ImgNodeInfo(&c_ImgNode);

   c_File.SetTo(s_Info.name, B_READ_ONLY);
   if(c_Res.SetTo(&c_File) != B_OK)
      continue;

   sz_Mime = (char*)c_Res.LoadResource('MIMS', "BEOS:APP_SIG", &i_Size);
   if(sz_Mime == NULL)
      continue;

   if(s_Info.type == B_ADD_ON_IMAGE)
   {         
      printf("replicant '%s' is running in image %d\n", 
            sz_Mime, s_Info.id);
   }
   else if(s_Info.type == B_APP_IMAGE)
   {
      printf("I am application '%s' loaded in image %d\n", 
            sz_Mime, s_Info.id);
   }
}
This method is easy to use and allow any application to check if another application contains replicants.

2- Determining the calling replicant.

Telling if the current context has been called from a replicant is much more difficult. Only few options are available:

Current BHandler:

Replicants are always BViews. This means that they are attached to a window, i.e a BLooper. If there is a way to find the current BHandler of the BLooper, then the replicant should be identified. The current BLooper can be found by using the static method:

   BLooper *LooperForThread(thread_id thread)
So we can identify the current BLooper when we are called from its context. Now we must know its active BHandler. The list of BHandlers of a BLooper can be obtained with the following method:
   BHandler *HandlerAt(int32 index)
But we still have to know which one has been called. Unfortunataly it seems that it is not possible: The context of the current called handler is not saved anywhere. This solution is a dead end.

Stack call:

An obvious solution is to look at the stack call and stop at the first caller that is in a replicant image space. The code is not shown here because it is too long and without special interest. Thus you should read the sources in [MALREP] to study it. This method works well but has some limitations:

  • If the replicant has been compiled with the "fomit-frame-pointer" option, the stack call is no more available.
  • If the current context comes from another thread that the replicant one, the stack call will be lost. Since BeOS is highly multi-threaded, this can be a usual condition. This issue is shown in the next figure.

repthread

However the first limitation is not completely right. Even if the replicant does not save the frame pointer, we will probably get a reference to the replicant in the stack: Since all system libraries save the frame pointer, there will be a reference to the replicant in the stack call. Thus if the detection is in or after a system library, the replicants will be found.

V- Conclusion.

Replicant injection is an easy way to hide some code in a BeOS system. BeOS is still a niche OS and there is no known malware yet on this system. However it will probably get more success when Haiku R1 will be out. Then a lot of changes will be necessary to make the os more secure. Since security was not an important concern when it was designed, there are probably a lot of possible attacks of this kind. The developpers of Zeta already began to work on this: The upcoming R1.5 will add multi-user support. This should remove a lot of threats but it is just the begining.

Zadig.


VI- References.

[REP1] :
Replicants - More application than an application.
http://haiku-os.org/node/85

[REP2] :
RepliShow - A Replicable Image Viewer.
http://haiku-os.org/node/87

[MALREP] :
http://www.reveng.cjb.net/downloader.php?file=malrep.zip

[SHELFER] :
http://www.bebits.com/app/3910

[SCRIPTING] :
The ZETA Developer Guide - scripting. http://www.zeta-os.com/cms/custom/API/Scripting.html