tags:

views:

184

answers:

2

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.

+3  A: 

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.

Nathan Fellman
This is right. The address of `mx` *relative to the new `DS`* is now `0`. Rather than loading your new segment descriptor into `DS`, you should load it into `ES` and use segment overrides to access it (loading `DS` will affect *all* data accesses in your C code).
caf
This is what I get when I disassemble the ELF with objdump:8048a48: c7 05 8c 9e 04 08 fe movl $0x3424cafe,0x8049e8c8048a4f: ca 24 34This is fairly new subject material for me so I'm not sure which of the bytes are the ones used for the segment register override modifier. Also, is the address shown in the assembly code an offset into the default data segment? If that's the case do I need to alter the address of where the variable is initialized to?
Brian
This is the breakdown: `c7` is the opcode for `MOV r/m32, imm32`. `05` is the MOD/RM byte, which says that the memory operand is addressed only by displacement. The displacement is in the following four bytes: `0x08049e8c`. The next four bytes are the immediate `0x3424cafe`. This means that the offset of `mx` inside the default data segment is `0x08049e8c`, and that's the offset inside the new `DS` that you're accessing.
Nathan Fellman
A: 

I got things to work smoothly. I manually went into the binary and rewrote that offset in the mov operation to a 0 and everything went OK :)

Thank you to Nathan and caf for all your help, I greatly appreciate it.

Brian