Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Utilizing the LDT (Local Descriptor Table)

Tags:

c

x86

assembly

I am trying to do some experiments using different segments besides the default code and data user and kernel segments. I hope to achieve this through use of the local descriptor table and the modify_ldt system call. Through the system call I have created a new entry in LDT which is a segment descriptor with a base address of a global variable I want to "isolate" and a limit of 4 bytes.

I try to load the data segment register with the segment selector of my custom LDT entry through inline assembly in a C program, but when I try to access the variable I receive a segmentation fault.

My suspicion is that there is an issue with the offset of my global variable, and when the address is calculated, it exceeds the limit of my custom segment therefore causing a seg fault.

Does anyone know of a work around to this situation?

Oh, by the way, this is on an x86 architecture in Linux. This is my first time asking a question like this on a forum, so if there is any other information that could prove to be useful, please let me know.

Thank you in advance.

Edit: I realized that I probably should include the source code :)

struct user_desc* table_entry_ptr = NULL;

/* Allocates memory for a user_desc struct */
table_entry_ptr = (struct user_desc*)malloc(sizeof(struct user_desc));

/* Fills the user_desc struct which represents the segment for mx */
table_entry_ptr->entry_number = 0;
table_entry_ptr->base_addr = ((unsigned long)&mx);
table_entry_ptr->limit = 0x4;
table_entry_ptr->seg_32bit = 0x1;
table_entry_ptr->contents = 0x0;
table_entry_ptr->read_exec_only = 0x0;
table_entry_ptr->limit_in_pages = 0x0;
table_entry_ptr->seg_not_present = 0x0;
table_entry_ptr->useable = 0x1;

/* Writes a user_desc struct to the ldt */
num_bytes = syscall( __NR_modify_ldt,
                   LDT_WRITE, // 1
                   table_entry_ptr,
                   sizeof(struct user_desc)
                   );

asm("pushl %eax");
asm("movl $0x7, %eax"); /* 0111: 0-Index 1-Using the LDT table 11-RPL of 3 */
asm("movl %eax, %ds");
asm("popl %eax");

mx = 0x407CAFE;

The seg fault occurs at that last instruction.

like image 230
Brian Avatar asked Nov 20 '09 15:11

Brian


People also ask

What is the use of local descriptor table?

A Local Descriptor Table (LDT) is a memory table used in the x86 architecture in protected mode and containing memory segment descriptors, just like the GDT: address start in linear memory, size, executability, writability, access privilege, actual presence in memory, etc.

How many descriptor can be stored in LDT at the most?

The GDT can hold up to 8192 descriptors including obligatory NULL descriptor. Descriptors are identified by 16-bit selectors of the following form. Each application (task) can have its own address space defined by descriptors written in its Local Descriptor Table (LDT).

What is difference between GDT and LDT?

The main differences between GDT and LDT is: 1) GDT have only one copy in system while LDT can have many, 2) GDT may not changed during execution which LDT often changes when task switches, 3) entry of LDT is save in GDT. Entries in GDT and LDT have the same structure.

What information does the global descriptor table hold?

The Global Descriptor Table (GDT) is a binary data structure specific to the IA-32 and x86-64 architectures. It contains entries telling the CPU about memory segments. A similar Interrupt Descriptor Table exists containing task and interrupt descriptors. It is recommended to read the GDT Tutorial.


1 Answers

I can only guess, since I don't have the assembly available to me.

I'm guessing that the line at which you get a segfault is compiled to something like:

mov ds:[offset mx], 0x407cafe

Where offset mx is the offset to mx in the program's data segment (if it's a static variable) or in the stack (if it's an automatic variable). Either way, this offset is calculated at compile time, and that's what will be used regardless of what DS points to.

Now what you've done here is create a new segment whose base is at the address of mx and whose limit is either 0x4 or 0x4fff (depending on the G-bit which you didn't specify).

If the G-bit is 0, then the limit is 0x4, and since it's highly unlikely that mx is located between addresses 0x0 and 0x4 of the original DS, when you access the offset to mx inside the new segment you're crossing the limit.

If the G-bit is 1, then the limit is 0x4fff. Now you'll get a segfault only if the original mx was located above 0x4fff.

Considering that the new segment's base is at mx, you can access mx by doing:

mov ds:[0], 0x407cafe

I don't know how I'd go about writing that in C, though.

like image 134
Nathan Fellman Avatar answered Sep 21 '22 14:09

Nathan Fellman