Reading contents of GDT and LDT from a user mode process? - Linux

This is a discussion on Reading contents of GDT and LDT from a user mode process? - Linux ; Hi, Is there a way to read (not write) the contents of the GDT and LDT from user mode code for Linux running on the IA-32 arch? Actually, I'm not all that interested in reading it, but I want to ...

+ Reply to Thread
Results 1 to 10 of 10

Thread: Reading contents of GDT and LDT from a user mode process?

  1. Reading contents of GDT and LDT from a user mode process?

    Hi,

    Is there a way to read (not write) the contents of the GDT and LDT
    from user mode code for Linux running on the IA-32 arch? Actually, I'm
    not all that interested in reading it, but I want to know if address
    space randomization also randomizes the values of the segment base
    address.

    Thanks in advance,
    Pramod

  2. Re: Reading contents of GDT and LDT from a user mode process?

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    Pramod Subramanyan wrote:
    > Is there a way to read (not write) the contents of the GDT and LDT
    > from user mode code for Linux running on the IA-32 arch? Actually, I'm
    > not all that interested in reading it, but I want to know if address
    > space randomization also randomizes the values of the segment base
    > address.


    You can use /proc/kcore to get access to the memory. AFAIK GDT address
    doesn't changes, so you can get it by looking into Linux source code.
    LDT is defined by processes and can be different for each of them.

    Linux uses memory model known as "protected flat model" what means that
    base addresses of kernel code, kernel data, user code and user data are
    the same (actually it's 0). That makes many things much easier.

    As long as address space randomization is to make buffer overflow-like
    attacks much more complicated, changed are addresses of the following
    parts of address space: executable (if it's allowed by its format),
    dynamically loaded libraries, stack, heap and a few less important
    kernel depended structures. As you see, it's not exactly the address
    space which is different, but some structures within it.

    Pawel Dziepak
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.9 (GNU/Linux)
    Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org

    iEYEARECAAYFAkkTCwgACgkQPFW+cUiIHNqwqgCdFwrsMXHxr4 fZMVVPIRi3afek
    gWYAoJeylMPNraERiuRb0I/kGLG1y81q
    =pNjZ
    -----END PGP SIGNATURE-----

  3. Re: Reading contents of GDT and LDT from a user mode process?

    > Is there a way to read (not write) the contents of the GDT and LDT
    > from user mode code for Linux running on the IA-32 arch?


    "man 2 modify_ldt" describes the system call for read/write of the LDT.
    See also "man 2 set_thread_area". A base address in the LDT is a
    process-virtual address, and can be anything that the process desires.
    For most user code, glibc chooses the base of the thread area:
    (0 mod 4) is required for some atomicity operations,
    (0 mod 8) speeds many floating-point operations,
    (0 mod 16) is required by some SSE3 instructions.

    The GDT is private to the kernel, although any program can read the
    current segment selectors by "mov %es,%ax" etc., then test bit 2
    (the (1<<2) bit) to determinte GDT versus LDT. With current Native Posix
    Thread Library (NPTL) implementation, the initial values for the segment
    registers are the same for each process. Changing process context involves
    re-writing the GDT (or a separate GDT per process.) With some older
    thread implementations, there was a system-wide one-to-one mapping
    between threads and selectors. The base address of a GDT entry
    which refers to per-process memory is always on a page boundary.

    --

  4. Re: Reading contents of GDT and LDT from a user mode process?

    On Nov 6, 4:40*pm, Pramod Subramanyan wrote:
    > Hi,
    >
    > Is there a way to read (not write) the contents of the GDT and LDT
    > from user mode code for Linux running on the IA-32 arch? Actually, I'm
    > not all that interested in reading it, but I want to know if address
    > space randomization also randomizes the values of the segment base
    > address.
    >
    > Thanks in advance,
    > Pramod


    You can read the GDT/LDT using the sgdt/sldt assembly instructions.
    According to Intel® 64 and IA-32 Architectures
    Software Developer’s Manual, Volume 2B:
    "SGDT is useful only by operating-system software. However, it can be
    used in application
    programs without causing an exception to be generated"

  5. Re: Reading contents of GDT and LDT from a user mode process?

    Thanks everybody. Based on your suggestions, I wrote a program that
    dumped read the gdtr using the sgdt instruction and then read /proc/
    kmap. [Its available at https://ssl.serc.iisc.ernet.in/~pramod/prog.c].
    It does appear that the base address for the segment registers change
    from run to run on my machine. I am compiling with -m32 on an AMD64
    kernel [2.6.24-19].

    Cheers,
    Pramod

  6. Re: Reading contents of GDT and LDT from a user mode process?

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    Pramod Subramanyan wrote:
    > Thanks everybody. Based on your suggestions, I wrote a program that
    > dumped read the gdtr using the sgdt instruction and then read /proc/
    > kmap. [Its available at https://ssl.serc.iisc.ernet.in/~pramod/prog.c].
    > It does appear that the base address for the segment registers change
    > from run to run on my machine. I am compiling with -m32 on an AMD64
    > kernel [2.6.24-19].


    On 2.6.26-5 (x86) all four segments (kernel and user data and code) have
    the same base address despite the fact that GDT address is different
    (that's probably due to address space randomization). I think that your
    result is caused by some stuff connected with legacy mode on AMD64, but
    I'm not familiar with it.
    What I have to find out is why those segments base addresses are no
    longer 0.

    Pawel Dziepak
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.9 (GNU/Linux)
    Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org

    iEYEARECAAYFAkkTPxQACgkQPFW+cUiIHNqCWwCgsjKYIeQXaV pK70eD6rHd6/Nc
    4XUAnijaJAo4mq1KtbAz7gwMZAtyCrlp
    =+gQ7
    -----END PGP SIGNATURE-----

  7. Re: Reading contents of GDT and LDT from a user mode process?

    Pramod Subramanyan writes:

    > Thanks everybody. Based on your suggestions, I wrote a program that
    > dumped read the gdtr using the sgdt instruction and then read /proc/
    > kmap. [Its available at https://ssl.serc.iisc.ernet.in/~pramod/prog.c].
    > It does appear that the base address for the segment registers change
    > from run to run on my machine. I am compiling with -m32 on an AMD64
    > kernel [2.6.24-19].


    Hmm, I don't think this is right.

    - With an AMD64 kernel, the machine is in long mode. The kernel is in
    the 64-bit submode, while your process is in the compatibility
    submode. The GDT should really be some structure within the kernel,
    and in 64-bit mode the GDTR contains a 64-bit pointer. I'm having
    some trouble understanding AMD's docs, which seem to imply that in
    compatibility mode the GDTR is 32 bits but the LDTR is 64 bits. In
    any case, I have a suspicion that the value returned by sgdt is not
    what you want.

    - You try to read kernel memory by seeking to an appropriate address in
    /proc/kcore. But /proc/kcore isn't a flat image of memory, it's in
    ELF format. On my machine, the lseek fails (you don't check its
    return value). I bet you're reading from the synthetic ELF header.
    There are also /dev/mem and /dev/kmem; on i386 I couldn't figure out
    which was appropriate, since I want to read from a linear address,
    which doesn't seem to be either "physical" or "virtual".

    I think a better way to answer your question would be to read the kernel
    source, and maybe insert some printk's at strategic spots to help
    understand what it's doing.


  8. Re: Reading contents of GDT and LDT from a user mode process?

    On Nov 7, 1:20*am, Nate Eldredge wrote:
    > Pramod Subramanyan writes:
    > > Thanks everybody. Based on your suggestions, I wrote a program that
    > > dumped read the gdtr using the sgdt instruction and then read /proc/
    > > kmap. [Its available athttps://ssl.serc.iisc.ernet.in/~pramod/prog.c].
    > > It does appear that the base address for the segment registers change
    > > from run to run on my machine. I am compiling with -m32 on an AMD64
    > > kernel [2.6.24-19].

    >
    > Hmm, I don't think this is right.
    >
    > - With an AMD64 kernel, the machine is in long mode. *The kernel is in
    > * the 64-bit submode, while your process is in the compatibility
    > * submode. *The GDT should really be some structure within the kernel,
    > * and in 64-bit mode the GDTR contains a 64-bit pointer. *I'm having
    > * some trouble understanding AMD's docs, which seem to imply that in
    > * compatibility mode the GDTR is 32 bits but the LDTR is 64 bits. *In
    > * any case, I have a suspicion that the value returned by sgdt is not
    > * what you want.
    >
    > - You try to read kernel memory by seeking to an appropriate address in
    > * /proc/kcore. *But /proc/kcore isn't a flat image of memory, it's in
    > * ELF format. *On my machine, the lseek fails (you don't check its
    > * return value). *I bet you're reading from the synthetic ELF header.
    > * There are also /dev/mem and /dev/kmem; on i386 I couldn't figure out
    > * which was appropriate, since I want to read from a linear address,
    > * which doesn't seem to be either "physical" or "virtual".
    >
    > I think a better way to answer your question would be to read the kernel
    > source, and maybe insert some printk's at strategic spots to help
    > understand what it's doing.


    Thanks for pointing that out. I'll try looking at the kernel source
    and try to figure what's really going on.

    --Pramod

  9. Re: Reading contents of GDT and LDT from a user mode process?

    Pramod Subramanyan wrote in news:455e7845-eb84-
    41d5-953a-7ddf37f40351@b2g2000prf.googlegroups.com:

    > On Nov 7, 1:20*am, Nate Eldredge wrote:
    >> Pramod Subramanyan writes:
    >> > Thanks everybody. Based on your suggestions, I wrote a program that
    >> > dumped read the gdtr using the sgdt instruction and then read

    /proc/
    >> > kmap. [Its available athttps://ssl.serc.iisc.ernet.in/

    ~pramod/prog.c].
    >> > It does appear that the base address for the segment registers

    change
    >> > from run to run on my machine. I am compiling with -m32 on an AMD64
    >> > kernel [2.6.24-19].

    >>
    >> Hmm, I don't think this is right.


    >> - You try to read kernel memory by seeking to an appropriate address

    in
    >> * /proc/kcore. *But /proc/kcore isn't a flat image of memory, it's in
    >> * ELF format. *On my machine, the lseek fails (you don't check its
    >> * return value). *I bet you're reading from the synthetic ELF header.
    >> * There are also /dev/mem and /dev/kmem; on i386 I couldn't figure

    out
    >> * which was appropriate, since I want to read from a linear address,
    >> * which doesn't seem to be either "physical" or "virtual".


    Linear is the same as virtual except that it isn't subject to a segment
    selector. It's the address just before paging is applied. Since linux
    sets up its code and data segments with base address of 0, it's
    equivalent to kernel virtual memory and hence /dev/kmem should work.

    Unfortunately, the offset we need to seek to in /dev/kmem is negative
    when considered as a 32-bit number, and somehow the file position is
    getting corrupted when I use the normal 32-bit lseek (the seek wasn't
    apparently failing but the subsequent read was, with EINVAL). OTOH when
    I use lseek64, the return value is being incorrectly sign-extended (I
    think the seek was actually working, but - for example - lseek64 to
    0xdff08000 was returning 0xffffffffdff08000). Hence, I dropped down to
    the raw "_llseek" system call and all works correctly.

    The version below works for i386. (Not sure what it would take to make
    it work on x86_64.)

    GH


    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include

    int get_cs(void) {
    int cs;
    __asm__ volatile(
    "mov %%cs, %%eax\n"
    : "=a"(cs));
    return cs;
    }

    int get_ds(void) {
    int ds;
    __asm__ volatile(
    "mov %%ds, %%eax\n"
    : "=a"(ds));
    return ds;
    }

    int get_ss(void) {
    int ss;
    __asm__ volatile(
    "mov %%ss, %%eax\n"
    : "=a"(ss));
    return ss;
    }

    int get_es(void) {
    int es;
    __asm__ volatile(
    "mov %%es, %%eax\n"
    : "=a"(es));
    return es;
    }

    int get_fs(void) {
    int fs;
    __asm__ volatile(
    "mov %%fs, %%eax\n"
    : "=a"(fs));
    return fs;
    }

    int get_gs(void) {
    int gs;
    __asm__ volatile(
    "mov %%gs, %%eax\n"
    : "=a"(gs));
    return gs;
    }

    #pragma pack(push, 1)
    struct gdtr {
    uint16_t limit;
    uint32_t addr;
    };
    struct gdt_entry
    {
    uint16_t limit_low;
    uint16_t base_low;
    uint8_t base_middle;
    uint8_t access;
    uint8_t granularity;
    uint8_t base_high;
    };
    #pragma pack(pop)

    loff_t mylseek(int fd, loff_t offset, unsigned int whence)
    {
    loff_t pos;
    int rval = syscall(__NR__llseek, fd,
    (unsigned long)(offset >> 32),
    (unsigned long)(offset & 0xffffffff),
    &pos, whence);
    if (rval == -1)
    return (loff_t)-1;
    return pos;
    }

    struct gdt_entry readentry(uint32_t base, int offset)
    {
    static int fd = -1;
    loff_t targ, tmp;
    if (fd == -1) {
    fd = open("/dev/kmem", O_RDONLY);
    if(fd == -1) {
    perror("open");
    exit(1);
    }
    }
    targ = base+offset*sizeof(struct gdt_entry);
    tmp = mylseek(fd, targ, SEEK_SET);
    if (tmp != targ) {
    printf("lseek returned %llx (not %llx) - %s\n",
    tmp, targ, strerror(errno));
    exit(1);
    }

    struct gdt_entry e;
    int ti;
    if((ti = read(fd, &e, sizeof e)) != sizeof e) {
    printf("read returned %d not %d - %s\n",
    ti, sizeof e, strerror(errno));
    exit(1);
    }
    return e;
    }

    int main(int argc, char* argv[]) {
    printf("cs = %x; index = %d\n", get_cs(), get_cs() >> 3);
    printf("ds = %x; index = %d\n", get_ds(), get_ds() >> 3);
    printf("ss = %x; index = %d\n", get_ss(), get_ss() >> 3);
    printf("es = %x; index = %d\n", get_es(), get_es() >> 3);
    printf("fs = %x; index = %d\n", get_fs(), get_fs() >> 3);
    printf("gs = %x; index = %d\n", get_gs(), get_gs() >> 3);

    struct gdtr gdtr = {0, -1};
    __asm__ volatile("sgdt %0\n" : :"m"(gdtr));
    printf("gdtr base: %x\n", gdtr.addr);
    printf("gdtr limit: %d\n", gdtr.limit);
    int gdt_size = (gdtr.limit + 1) / sizeof(struct gdt_entry);
    size_t index;
    for (index = 0; index < gdt_size; ++index) {
    struct gdt_entry e = readentry(gdtr.addr, index);
    uint32_t base = ((uint32_t)e.base_high << 24) |
    ((uint32_t)e.base_middle << 16) |
    (uint32_t)e.base_low;
    printf("%2d: base address %8x, access %2x, gran %2x\n",
    index, base, e.access, e.granularity);
    }
    return 0;
    }

  10. Re: Reading contents of GDT and LDT from a user mode process?

    Gil Hamilton writes:

    > Pramod Subramanyan wrote in news:455e7845-eb84-
    > 41d5-953a-7ddf37f40351@b2g2000prf.googlegroups.com:
    >
    >> On Nov 7, 1:20*am, Nate Eldredge wrote:
    >>> Pramod Subramanyan writes:
    >>> > Thanks everybody. Based on your suggestions, I wrote a program that
    >>> > dumped read the gdtr using the sgdt instruction and then read

    > /proc/
    >>> > kmap. [Its available athttps://ssl.serc.iisc.ernet.in/

    > ~pramod/prog.c].
    >>> > It does appear that the base address for the segment registers

    > change
    >>> > from run to run on my machine. I am compiling with -m32 on an AMD64
    >>> > kernel [2.6.24-19].
    >>>
    >>> Hmm, I don't think this is right.

    >
    >>> - You try to read kernel memory by seeking to an appropriate address

    > in
    >>> * /proc/kcore. *But /proc/kcore isn't a flat image of memory, it's in
    >>> * ELF format. *On my machine, the lseek fails (you don't check its
    >>> * return value). *I bet you're reading from the synthetic ELF header.
    >>> * There are also /dev/mem and /dev/kmem; on i386 I couldn't figure

    > out
    >>> * which was appropriate, since I want to read from a linear address,
    >>> * which doesn't seem to be either "physical" or "virtual".

    >
    > Linear is the same as virtual except that it isn't subject to a segment
    > selector. It's the address just before paging is applied. Since linux
    > sets up its code and data segments with base address of 0, it's
    > equivalent to kernel virtual memory and hence /dev/kmem should work.
    >
    > Unfortunately, the offset we need to seek to in /dev/kmem is negative
    > when considered as a 32-bit number, and somehow the file position is
    > getting corrupted when I use the normal 32-bit lseek (the seek wasn't
    > apparently failing but the subsequent read was, with EINVAL). OTOH when
    > I use lseek64, the return value is being incorrectly sign-extended (I
    > think the seek was actually working, but - for example - lseek64 to
    > 0xdff08000 was returning 0xffffffffdff08000). Hence, I dropped down to
    > the raw "_llseek" system call and all works correctly.


    Aha. That was the same problem I had, but I assumed that I was
    misunderstanding how /dev/kmem worked. I've got it now. Thanks.

    It still doesn't apply on amd64, of course.

+ Reply to Thread