flat assembler
Message board for the users of flat assembler.

Index > Windows > LSL / Load Segment Limit

Author
Thread Post new topic Reply to topic
anil0142



Joined: 12 Jun 2024
Posts: 3
anil0142 12 Jun 2024, 06:13
Recently I was testing this instruction on Windows x64, and I realized I was getting inconsistent results when testing the segment KGDT64_R3_CMTEB (0x50), here is the code:

Code:
static __inline__ __attribute__((always_inline)) unsigned short segmentlimit(const unsigned long a)
{
    unsigned short retval = 0;
    __asm__ __volatile__("lsl %[a], %[retval]"
        : [retval] "=r"(retval)
        : [a] "r"(a));
    return retval; 
}

const auto limit_teb = segmentlimit(KGDT64_R3_CMTEB + RPL_MASK);
    


%90 of my tests showed that the limit for this segment was 0x3c00, and that is also what is stated in the book, Windows Internals Part2 and ReactOS source code.
But when I restart my process, in rare cases, I will get the value 0xbc00, this value has same first 14 bits with 0x3c00, which is suspicious. I checked the Intel manual for this instruction and it states that:


Quote:

segment limit is a 20-bit value contained in bytes 0 and 1 and in the first 4 bits of byte 6 of the segment descriptor


So it doesn't seem like the value is actually 14 bits, and the randomly reported 0xbc00 is correct. But the story doesn't end here. I actually went ahead and tested accessing the segment via:

Code:
if(limit_teb == 0xbc00)
{
    unsigned char b = __readgsbyte(0xb000);
    (void)(b);
}
    


Which raised access violation exception, which clearly means the limit reported to me was wrong.

Anyone has any idea what is going on here? My first idea is the value returned by LSL must be casted to contain only 14 bits, but there is no documentation about this anywhere.
Post 12 Jun 2024, 06:13
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 12 Jun 2024, 06:48
The segment limit is unrelated to the memory paging.

So even if the segment is valid for the entire address space, you still need to have memory mapped by the paging unit to access it.
Post 12 Jun 2024, 06:48
View user's profile Send private message Visit poster's website Reply with quote
anil0142



Joined: 12 Jun 2024
Posts: 3
anil0142 12 Jun 2024, 08:24
revolution wrote:
The segment limit is unrelated to the memory paging.

So even if the segment is valid for the entire address space, you still need to have memory mapped by the paging unit to access it.


Thanks for the answer, this was bugging me out for the past few days. I made some tests and confirmed that is the case.

Though, now I wonder what the segment limit is even used for? Because I conducted a new test, which I selected a mapped accessible page, that was 0x17E000 bytes away from the GS segment's base (TEB), and this page was clearly off limits whether the segment limit was 0xBC00 or 0x3C00. I then tried reading the page with __readgsbyte(0x17E000), and to my surprise the page was successfully read without any exceptions! So does the processor/OS not enforce the segment limit? Or I am missing something else again?

Have a nice day!
Post 12 Jun 2024, 08:24
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 12 Jun 2024, 08:34
It depends upon the processor mode.

In 32-bit mode the segment limits are honoured, and Windows simply makes all the segments cover the entire 4GB range, effectively disabling segmentation.

64-bit mode doesn't use segments and ignores them. With the exception for FS/GS with significant limitations.

The limit in the descriptor is not very useful for any modern OS. The main way to control memory access is with paging.
Post 12 Jun 2024, 08:34
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 12 Jun 2024, 18:06
I ran this for both 32 and 64 bit modes on Linux
Code:
;       fasm lsl_test.asm -d bits=32 && ./lsl_test && fasm lsl_test.asm -d bits=64 && ./lsl_test

if bits=32
        format elf executable at 1 shl 16
        SYS_write       = 4
        SYS_exit_group  = 252
else
        format elf64 executable at 1 shl 16
        SYS_write       = 1
        SYS_exit_group  = 231
end if

STDOUT_FILENO   = 1

entry $
        irp reg, cs, ds, es, fs, gs, ss {
                mov     ecx,reg
                mov     eax,0x55555555
                lsl     eax,cx
                call    print_hex8
                mov     dl,10
                call    print_char
        }
        mov     eax,SYS_exit_group
        if bits=32
                xor     ebx,ebx
                int     0x80
        else
                xor     edi,edi
                syscall
        end if

hex_table:      db '0123456789abcdef'

print_hex8:
        ;eax = value
        mov     ecx,8
        lea     ebx,[hex_table]
    .next_nibble:
        rol     eax,4
        mov     edx,eax
        and     edx,0xf
        mov     dl,[ebx + edx]
        call    print_char
        dec     ecx
        jnz     .next_nibble
        ret

print_char:
        ;dl = character
        if bits=32
                push    eax ebx ecx edx
                mov     edx,1
                mov     ebx,STDOUT_FILENO
                mov     ecx,esp
                mov     eax,SYS_write
                int     0x80
                pop     edx ecx ebx eax
        else
                push    rax rbx rcx rdx 
                mov     edx,1
                mov     edi,STDOUT_FILENO
                mov     rsi,rsp
                mov     eax,SYS_write
                syscall
                pop     rdx rcx rbx rax
        end if
        ret    
The result
Code:
flat assembler  version 1.73.31  (16384 kilobytes memory)
2 passes, 300 bytes.
ffffffff
ffffffff
ffffffff
55555555
55555555
ffffffff
flat assembler  version 1.73.31  (16384 kilobytes memory)
2 passes, 339 bytes.
ffffffff
55555555
55555555
55555555
55555555
ffffffff
    
Both results are meaningless. The segment limits tell us nothing of interest.

MS could be using the descriptor bits as some kind of flags or other proprietary info that their code uses.
Post 12 Jun 2024, 18:06
View user's profile Send private message Visit poster's website Reply with quote
anil0142



Joined: 12 Jun 2024
Posts: 3
anil0142 13 Jun 2024, 08:11
revolution wrote:
I ran this for both 32 and 64 bit modes on Linux
Code:
;       fasm lsl_test.asm -d bits=32 && ./lsl_test && fasm lsl_test.asm -d bits=64 && ./lsl_test

if bits=32
        format elf executable at 1 shl 16
        SYS_write       = 4
        SYS_exit_group  = 252
else
        format elf64 executable at 1 shl 16
        SYS_write       = 1
        SYS_exit_group  = 231
end if

STDOUT_FILENO   = 1

entry $
        irp reg, cs, ds, es, fs, gs, ss {
                mov     ecx,reg
                mov     eax,0x55555555
                lsl     eax,cx
                call    print_hex8
                mov     dl,10
                call    print_char
        }
        mov     eax,SYS_exit_group
        if bits=32
                xor     ebx,ebx
                int     0x80
        else
                xor     edi,edi
                syscall
        end if

hex_table:      db '0123456789abcdef'

print_hex8:
        ;eax = value
        mov     ecx,8
        lea     ebx,[hex_table]
    .next_nibble:
        rol     eax,4
        mov     edx,eax
        and     edx,0xf
        mov     dl,[ebx + edx]
        call    print_char
        dec     ecx
        jnz     .next_nibble
        ret

print_char:
        ;dl = character
        if bits=32
                push    eax ebx ecx edx
                mov     edx,1
                mov     ebx,STDOUT_FILENO
                mov     ecx,esp
                mov     eax,SYS_write
                int     0x80
                pop     edx ecx ebx eax
        else
                push    rax rbx rcx rdx 
                mov     edx,1
                mov     edi,STDOUT_FILENO
                mov     rsi,rsp
                mov     eax,SYS_write
                syscall
                pop     rdx rcx rbx rax
        end if
        ret    
The result
Code:
flat assembler  version 1.73.31  (16384 kilobytes memory)
2 passes, 300 bytes.
ffffffff
ffffffff
ffffffff
55555555
55555555
ffffffff
flat assembler  version 1.73.31  (16384 kilobytes memory)
2 passes, 339 bytes.
ffffffff
55555555
55555555
55555555
55555555
ffffffff
    
Both results are meaningless. The segment limits tell us nothing of interest.

MS could be using the descriptor bits as some kind of flags or other proprietary info that their code uses.


I see, interesting. I was initially researching this instruction to see if the returned values could help detecting sandbox / emulation environment, I will test more to see what discrepancies are there compared to real OS.

The segment limit being ignored are also interesting, especially when the segment does map to the specific address, for Windows it maps to a unique address per thread , this could be used to store offsets to functions / data relative to GS base and using computations involving GS register to reach them while whitelisting a specific thread.
Post 13 Jun 2024, 08:11
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20451
Location: In your JS exploiting you and your system
revolution 13 Jun 2024, 08:16
In 64-bit mode there is the instruction WRFSBASE. This sets the base address of the segment. It doesn't use the descriptor at all. The descriptor is meaningless for most OSes used today.
Post 13 Jun 2024, 08:16
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.