Creating a vDSO: the Colonel's Other Chicken

 in

Compiling, Linking and Running

Wait, is that all there is to adding a vDSO? Um, yes. Of course, if the function was something supported by the C library (glibc, in our case), you can hack that to do the detection of vDSO and then the actual call. However, I mentioned we wouldn't be hacking glibc. And, you don't need to anyway, because getting the code to work is pretty simple. With the chunks described above all in place, it's time to start building. Just configure and compile your kernel as you typically would:


make menuconfig
make bzImage
make modules
make modules_install

Now, install and boot your new modified vDSO kernel. Once that is up and running, it's time to test a few things, mainly the vDSO stuff you just added. Let's compile a test case to exercise the vDSO call:


/* notb.c */
#include <stdio.h>

int main(void)
{
    int notb = number_of_the_beast();
	
    printf("His number is %d\n", notb);

    return 0;
}

Then, compile the code above as:


gcc notb.c -o notb vdso.so

The file you link against is vdso.so, which provides the symbol resolution needed to make the kernel call. The kernel version of number_of_the_beast() is called, even if the code for that function is completely different in vdso.so. Where is vdso.so located? It's located in the kernel build directory after building the kernel: linux-2.6.37/arch/x86/vdso/vdso.so.

At runtime, when a program executes number_of_the_beast, the kernel code is called and not the version of number_of_the_beast() in the vdso.so file. If you modify the kernel and, say, have number_of_the_beast() return 42, then unless you load that kernel, you still will get 666. Even if you compile the test example above with the newer modified-to-42 vdso.so.

Another way of getting the vdso.so file is by writing a program that extracts the vDSO memory from a running executable. Numerous sources on-line explain how to do this, but I briefly describe it here. The vDSO page, which is mapped into the memory of every running process, can be in a non-deterministic memory range of your executing process, thanks to Linux's address space layout randomization (ASLR). To get this address, a running program can find its memory information from the file /proc/self/maps. In there, a line with the text [vdso] exists. That line contains the address range in the executing process of the vDSO page. For example, you could run cat /proc/self/maps.

Note that running this command multiple times produces different address ranges for [vdso] thanks to (if your kernel supports it) address space layout randomization.

The output should look something similar to:


...
7fff40d71000-7fff40d72000 r-xp 00000000 00:00 0 [vdso]
...

The above range is showing for the cat process you just executed that the address range for the vDSO page is located starting at 7fff40d71000 and ending at 7fff40d7200. Subtracting the start and end range, you get 0x1000 or 4096 bytes. 4096 is the page size often used in the kernel. Listing 1 shows code for extracting the vDSO from a running kernel, and it is based on code from the "Examining the Linux VDSO" article listed in Resources.

A simple dumping of the dynamic object symbols can be conducted via:


objdump -T vdso.so

Because a shared library is also an elf, the readelf tool also can be used on vdso.so.

Listing 1. Extracting the vDSO from a Running Kernel


/* extract_vdso.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char **argv)
{
    char        buf[256], *mem;
    const char *range_name;
    FILE       *rd, *wr;
    long long   start_addr, end_addr;

    /* Open file for writing the vdso data to */
    if (argc != 3)
    {
        fprintf(stderr,
                "Usage: %s <file> <string>\n"
                "\t<file>:   File to write the vdso data to.\n"
                "\t<string>: Name of the mapped in region, e.g. vdso\n",
                argv[0]);

        abort();
    }

    range_name = argv[2];

    if (!(wr = fopen(argv[1], "w")))
    {
        perror("Error: fopen() - output file");
        abort();
    }

    /* Get this process' memory layout */
    if (!(rd = fopen("/proc/self/maps", "r")))
    {
        perror("Error: fopen() - /proc/self/maps");
        abort();
    }

    /* Find the line in /proc/self/maps that contains 
       the substring [vdso] * */
    while (fgets(buf, sizeof(buf), rd))
    {
        if (strstr(buf, range_name))
          break;
    }

    fclose(rd);

    /* Locate the end memory range for [vdso] */
    end_addr = strtoll((strchr(buf, '-') + 1), NULL, 16);

    /* Terminate the string so we can get the start 
       address really easily * */
    *(strchr(buf, '-')) = '\0';
    start_addr = strtoll(buf, NULL, 16);

    /* Open up the memory page and extract the vdso */
    if (!(rd = fopen("/proc/self/mem", "r")))
    {
        perror("Error: fopen() - /proc/self/mem");
        abort();
    }

    /* Hop to the vdso portion */
    fseek(rd, start_addr, SEEK_SET);

    /* Copy the memory locally and then move it to the file */
    mem = malloc(end_addr - start_addr);
    if (!fread(mem, 1, end_addr - start_addr, rd))
    {
        perror("Error: read() - /proc/self/mem");
        abort();
    }

    /* Write the data to the specified output file */
    if (!fwrite(mem, 1, end_addr - start_addr, wr))
    {
        perror("Error: fwrite() - output file");
        abort();
    }

    free(mem);
    fclose(rd);
    fclose(wr);

    printf("Start: %p\nEnd:   %p\nBytes: %d\n",
            (void *)start_addr, (void *)end_addr, (int)(end_addr -
             ↪start_addr));

    return 0;
}

Security Implication

Anytime you dabble with the kernel, you should consider the security implications. If you think you can "own" someone by creating your own vDSO calls, you might want to think again. Because adding a vDSO requires users to bake their own kernels, the only people they could be compromising is their system and the users on their system. Of course, any dabbling with kernel resources should be done with much consideration. Remember, playing with vDSO goodies occurs in userland; however, your vDSOs can access kernel data. And, your kernel can read vDSO data. That can be a concern, but I'll leave that up to you as an exercise for finding anything exploitable.

Finally, this article is just a little one-two on how to cook up your own vDSO. Now go make yourself a smoking kernel.

Resources

GNU/Linux Kernel. 2.6.37: http://www.kernel.org

"6.30 Declaring Attributes of Functions" (GCC Manual): http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

"Weak Symbol" (Wikipedia): http://en.wikipedia.org/wiki/Weak_symbol

"Examining the Linux VDSO" (Truth, Computing and Fail): http://anomit.com/2010/04/18/examining-the-linux-vdso

Johan Peterson's "What is linux-gate.so.1?": http://www.trilithium.com/johan/2005/08/linux-gate

Matt Davis' "Linux syscall, vsyscall, and vDSO...Oh My!": http://davisdoesdownunder.blogspot.com/2011/02/linux-syscall-vsyscall-and-vdso-oh-my.html

______________________

Matt Davis is a software engineer on leave from his job in the US to pursue a PhD from the Computer Science Department at the University of Melbourne, where he is focusing his hackery toward the compiler field.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

----- http://ai.vc/zd

bdso8f9sa's picture

----- http://ai.vc/zd -----

Hi,Dear Ladies and Gentlemen,
1. sport shoes : Jordan ,Nike, adidas, Puma, Gucci, LV, UGG , etc. including women shoes and kids shoes.
2. T-Shirts : BBC T-Shirts, Bape T-Shirts, Armani T-Shirts, Polo T-Shirts,etc.
3. Hoodies : Bape hoody, hoody, AFF hoody, GGG hoody, ED hoody ,etc.
4. Jeans : Levis jeans , Gucci jeans, jeans, Bape jeans , DG jeans ,etc.
----- http://ai.vc/zd -----
----- http://ai.vc/zd -----

Service is our Lift.

enjoy yourself.

thank you!!

::∴★∵**☆.∴★∵**☆.∴★∵**☆.
█████.::∴★∵**☆.∴★∵**☆.
█田█田█::∴★∵**☆.∴★∵**☆.
█田█田█.∴★∵**☆.∴★∵**☆.
█田█田█∴★∵**☆.∴★∵**☆.
█田█田█.★∵**☆.∴★∵**☆.
█████.*******************
◢██□██◣.~~~~~*^_^*

Good content, I trust this is

Pryanka's picture

Good content, I trust this is a good weblog about Wish to see refreshing content material next time. Thanks for sharing this publish with us. Keep it up.
hebergement audiotel voyance

Re : Creating a vDSO: the Colonel's Other Chicken

Sedot WC's picture

script code is my weakness and it is difficult for me to learn
but in this tutorial I will try hard to understand
thank you

why x86(32bit) does not have gettimeofday vdso

Fredrick's picture

Very nice article. Thanks for explaining vdso concept.
Why x86(32bit) does not have gettimeofday vdso?
Only 64bit x86 has this call implemented. Why?
Does x86 32bit have limitations?

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix