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 ...
-
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
-
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-----
-
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.
--
-
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"
-
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
-
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-----
-
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.
-
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
-
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;
}
-
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.