Search This Blog

Showing posts with label shared object. Show all posts
Showing posts with label shared object. Show all posts

Wednesday, March 21, 2012

Linux Threads Through a Magnifier: Remote Threads

Source code for this article may be found here.

Sometimes, a need may rise to start a thread in a separate process and the need is not necessarily malicious. For example, one may want to replace library functions or to place some code between the executable and a library function. However, Linux does not provide a system call that would do anything similar to CreateRemoteThread Windows API despite the fact that I see people searching for such functionality. You may google for "CreateRemoteThread equivalent in Linux" yourself and see that at least 90% of the results end up with something like "why would you want to do that?" There is a certain type of people in forums, most likely, thinking if they do not have an answer, then, probably, it does not exist and no one would ever need it. Others truly believe, that if they know why, they can tell you how to do that in another way. The latest is sometimes true, but most of the time, the solution being requested is the only one acceptable and that's what people refuse to understand.

So, let's say, you need to inject a thread into a running process for whatever reason (may be you want to perform a "DLL injection" the Linux way - your business). Although, there is no specific system call to allow you that, there are plenty of other system calls and library functions that would "happily" assist you.

Unavoidable ptrace()
First time you take a look at ptrace() it is a bit frightening (just like ioctl()) - one function, lots of possible requests and go figure out when and which parameter is being ignored. In practice, it quite simple. This function is used by debuggers and in cases when one needs to monitor the execution of a process for whatever reason. We will use this function for thread injection in this article.

The first thing you would want to do is to attach to the target process:

   ptrace(PTRACE_ATTACH, pid, NULL, NULL);

PTRACE_ATTACH - request to attach to a running process;
pid - the ID of the process you want to attach to.

If the return value is equal to the pid of the target process - voila, you are attached. If it is -1, however, this means that an error has occurred and you need to check errno to know what has happened. you should keep in mind, that on certain systems you may not be able to attach to a process which is not a descendant of the attaching one or has not specified it as tracer (using prctl()). For example, in Ubuntu, since Ubuntu 10.10 this is exactly the situation. If you want to change that, however, you then need to locate your ptrace.conf file and set ptrace scope to 0.

Since I am using Ubuntu and I can only attach to a child process (unless I want some additional headache) and this is what I am going to cover in this article.


Preparations
The first step, just like in case of Windows, you need to write an injector. It will load the victim process, inject the shellcode and exit. This is the simplest part and the skeleton of such loader would look like this:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>

int   main(int argc, char** argv)
{
   pid_t   pid;
   int     status;

   if(0 == (pid = fork()))
   {
      // We are in the child process, so we just ptrace() and execl()
      ptrace(PTRACE_TRACEME, 0, NULL, NULL);
      execl(*(argv+1), NULL, NULL);
   }
   else
   {
      // We are in the parent (injector)
      ptrace(PTRACE_SETOPTIONS, pid, PTRACE_O_TRACEEXEC, NULL);
      // Wait for exec in the child
      waitpid(pid, &status, 0);
      
      // The rest of the code comes here

   }
   return 0;
}

As you can see, the loader forks and then behaves depending on the return value of the fork() function. If it returns 0, this means that we are in the child process (actually, you should check whether it returned -1, which would indicate an error), otherwise, it is a pid of the child process and we are in the parent.

Child
The child code does not have too many things to do. All that needs to be done is to tell the OS that it may be traced and replace itself with the victim executable by calling execl().

Parent
In case of parent, the situation is much different and much more complicated. You should tell the OS, that you want to get notification when the victim process issues sys_execve by calling ptrace() with PTRACE_SETOPTIONS  and PTRACE_O_TRACEEXEC. Then you simply waitpid().

When waitpid() returns (and you should check the return value for -1, which means error), it is still not the best time to start the injection. Especially, given that you may have no idea of what is where in the victim process. The next step is to wait for a system call to occur by telling the OS (and it would be good to skip a couple of system calls, so that the victim may initialize properly):

ptrace(PTRACE_SYSCALL, pid, NULL, NULL);

followed by a loop:

while(1)
{
   if(-1 == waitpid(pid, &status, 0))
   {
      //Some error occurred. Print a message and
      break;
   }

   if(WIFEXITED(status))
   {
      //The victim process has terminated. Print a message and
      break;
   }

   if(WIFSTOPPED(status))
   {
      // Here comes the actual injection code. Actually, all its stages.
   }
   
   if(WIFSIGNALED(status))
   {
      // The victim process received a signal and terminated. Print a message and
      break;
   }

   // All done.
   return 0;
}


Injection 
You should introduce a variable to count stages. Let's name it step

Stage 0 (step = 0)
I have not mentioned it, but ptrace() would notify you twice during a system call. First time right before the system call (so you can inspect registers), the second notification would arrive right after system call's completion (so you can inspect the return value). Therefore, this time we do nothing, but resume the traced victim:

ptrace(PTRACE_SYSCALL, pid, NULL, NULL);

and increment the stage variable.


Stage 1 (step = 1)
Backup victim's registers, portion of victim's code that would be overwritten with your shellcode and, finally, inject your shellcode.

Use ptrace(PTRACE_GETREGS, pid, NULL, regs) where regs is a pointer to struct user_regs (declared in sys/user.h). The content of the victim's registers would be copied there.

Use ptrace(PTRACE_PEEKTEXT, pid, address_in_victim, NULL) to copy the executable code from the victim (to make a backup) and ptrace(PTRACE_POKETEXT, pid, address_in_victim, shellcode) where address_in_victim is what its name suggests (you obtain the initial value from victim's RIP on 64 or EIP on 32 bit systems). Shellcode, however, contains bytes of the code being injected packed into an unsigned long value. You, most probably, would have to make those calls for several iterations, as I do not think your shellcode would be at most 8 bytes.

The start of your shellcode will allocate memory for the thread function (unless you are going to run code that already is there).

start:
   mov   rax, 9      ;sys_mmap
   mov   rdi, 0      ;requested address
   mov   rsi, 0x1000 ;one page
   mov   rdx, 7      ;PROT_READ | PROT_WRITE | PROT_EXEC
   mov   r10, 0x22   ;MAP_ANON | MAP_PRIVATE
   mov   r8, -1      ;fd
   mov   r9, 0       ;offset
   syscall
   db 0xCC

Increment stage variable. Resume the victim process with

ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);


Stage 2 (step = 2)
Ignore all stops until

0xCC == (unsigned char)(ptrace(PTRACE_PEEKTEXT, pid,
      ptrace(PTRACE_PEEKUSER, pid, offsetof(struct user, regs.rip), NULL), NULL) & 0xFF

which would mean that you have reached your break point. Check victim's rax register for return value

retval = ptrace(PTRACE_PEEKUSER, pid, offsetof(struct user, regs.rax), NULL);

and abort if it contains an error code.

You have to increment the Instruction Pointer (RIP/EIP) before letting the victim to resume:

ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, regs.rip),
       ptrace(PTRACE_PEEKUSER,pid, offsetof(struct user, regs.rip), NULL) + 1);


Increment stage counter and 

ptrace(PTRACE_SINGLESTEP, pid, NULLNULL);


Stage 3 (step = 3)
After allocating memory, your shellcode should copy the thread function there and, actually, create a thread (similar to this).

You should, again, ignore all stops as long as

0xCC != (unsigned char)(ptrace(PTRACE_PEEKTEXT, pid,

      ptrace(PTRACE_PEEKUSER, pid, offsetof(struct user, regs.rip), NULL), NULL) & 0xFF

Once you get to this breakpoint, you know that the thread has been initiated and the injector has done what it was written for.

Now you have to restore the victim to its initial, pre-injection state by restoring the values of the registers:

ptrace(PTRACE_SETREGS, pid, NULL, regs);

and, which is even more important - you have to restore the backed up code by copying back the backed up unsigned longs.

The last thing would be detaching from the victim process:

ptrace(PTRACE_DETACH, pid, NULL, NULL);

At this point, your injector may safely exit letting the victim to continue execution.

Voila! You have just injected a thread into another process.

Output of the injector, victim program and the injected thread























P.S. Shared Object Injection (a la DLL injection)
Although, injection of executable code is quite simple, injection of shared object is a different story. Despite the fact, that Linux kernel provides sys_uselib system call, it may be unavailable on some systems. In such case, you have several options:

  • Check whether the victim uses libdl (dlopen(), dlsym() and dlclose() functions, parse the image and obtain addresses of relevant functions. However, not every program uses libdl.
  • Use sys_uselib system call. However, it may be unavailable.
  • Write your own shared object loader. This may be a real pain, but you would be able to reuse it whenever you need.

Hope this post was helpful. See you at the next.

Monday, December 19, 2011

Listing Loaded Shared Objects in Linux

I have recently come across several posts on the Internet where guys keep asking for Linux analogs of Windows API. One of the most frequent one is something like "EnumProcessModules for Linux". As usual, most of the replies are looking like "why do you need that?" or "Linux is not Windows". Although, the last one is totally true, it is completely useless. As to "why do you need that?" - why do you care? Poor guy's asking a question here so let's assume he knows what he's doing.

I remember looking for something like this myself while working on some virtualization project for one of my previous employers. One thing I've learnt - once the question is out of ordinary (and people do not usually ask for Windows API replacements in Linux), there is a really good chance of getting tones of useless replies and blamed for being unclear. More then that, as long as it comes to Linux, most people do not really understand the difference between doing something in the shell and doing something in your program (as, unfortunately, many call shell scripts programs as well).

Well, enough crying here. Let's get to business. As usual, a note for nerds (non nerds are welcome to comment, leave suggestions, etc.)

  • the code in this article may not contain all necessary checks for invalid values;
  • yes, there are other ways of doing this;
  • you are going to mess with libc here, so be careful;
What are Modules (in this case)
In Linux the word "module" has a different meaning from what you've been used to in Windows. While in Windows this word means components of a process (main executable and all loaded DLLs), in Linux it refers to a part of the kernel (usually a driver). If this what you mean, then you probably want to enumerate loaded kernel modules and this is beyond the scope of this article. What we are going to do here, is to write to the terminal paths of all loaded shared objects (Linux analog of Windows DLL) and we are going to do it in a less common way just to see how things are organized internally. Just like we have LDR_MODULE structure in Windows, we have link_map structure in Linux. In both cases these structures describe loaded libraries (well, in Windows there's also a LDR_MODULE for the main executable).

link_map Structure
We do not need to know too much about this structure (for those interested - see "include/link.h" in your glibc sources). We may even define our own structure for that (a minimal one):

struct lmap
{
   void*    base_address;   /* Base address of the shared object */
   char*    path;           /* Absolute file name (path) of the shared object */
   void*    not_needed1;    /* Pointer to the dynamic section of the shared object */
   struct lmap *next, *prev;/* chain of loaded objects */
}

There is some more information in the original structure, but we do not need it for now.

Getting There
So we know what the link_map structure looks like and it is good. But how can we get there? let me assume that you are aware of dynamic linking. In Linux we have dl* functions:

dlopen - loads a shared object (LoadLibrary);
dlclose - unloads a shared object (FreeLibrary);
dlsym - gets the address of a symbol from the shared object (GetProcAddress).

The dlopen function returns a pseudo handle to the loaded shared object. These functions are declared in "dlfcn.h". You also have to explicitly link the dl library by passing -ldl to gcc.

While in Windows HANDLE is equal to the base address of the module, in Linux pseudo handle is in fact a pointer to the corresponding link_map structure. This means that getting to the head of the list of loaded modules is quite easy:

struct lmap* get_list_head(void* handle)
{
   struct lmap* retval = (struct lmap*)handle;
   while(NULL != retval->prev->path)
      retval = retval->prev;
   return retval;
}

Things are a bit more complicated if you do not intend to load any shared object. You will still have to use the dl library, though.

First of all, you will have to call dlopen, despite that fact that you are not going to load anything. Call it with NULL passed as first argument and RTLD_NOW as the second. The return value in this case would be the pseudo handle for the main executable (similar to GetModuleHandle(NULL) in Windows), but it would point to a different structure (to be honest, I've been too lazy to dig for it in libc sources) then link_map. This structure contains different pointers and we are particularly interested in the fourth one. This pointer points to a structure (which I was too lazy to dig for as well) with some other pointers/values and we are particularly interested (again) in the fourth one. This pointer, in turn, finally gets us to the first link_map structure. In my case, it is a structure which refers to libdl.so.2. Let's take a look at the procedure in C

struct something
{
   void*  pointers[3];
   struct something* ptr;
}

struct lmap* pl;
void* ph = dlopen(NULL, RTLD_NOW);
struct something* p = (struct something*)ph;
p = p->ptr;
pl = (struct lmap*)p->ptr;


List Loaded Objects
Now we are ready to list all loaded objects. Assume p is a pointer to the first link_map (in our case lmap) structure:

while(NULL != p)
{
   printf("%s\n", p->path);
   p = p->next;
}

In my case the output is (about three times less than in a Windows process ;-) ):
/lib32/libdl.so.2
/lib32/libc.so.6
/lib/ld-linux.so.2

C'est tous. We are done. The mechanism described above may be used in order to either enumerate loaded shared objects or to get their handles. I personally used it for amusement.

Just remember, that in Linux, unlike Windows, handle to an object is not its base address, but the address of (pointer to) the corresponding link_map structure.


Hope this post was at least interesting (if not helpful). See you at the next!