flat assembler
Message board for the users of flat assembler.

Index > Windows > fs and gs segment registers in 64 bit windows

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
tthsqe



Joined: 20 May 2009
Posts: 767
tthsqe 29 Apr 2023, 15:04
Hello yall. To what extent can I set fs and/or gs offsets to whatever I want and still expect calls into kernel32.dll to work? If it is not possible to set either of them safely, how much space is there in each that I can use?
Post 29 Apr 2023, 15:04
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20461
Location: In your JS exploiting you and your system
revolution 29 Apr 2023, 15:11
You can't use them. They are selectors, not segment registers.

Also, FS points to the exception handling. I can't remember what GS is for, but even if it is nothing you still can't set it to any arbitrary value.
Post 29 Apr 2023, 15:11
View user's profile Send private message Visit poster's website Reply with quote
tthsqe



Joined: 20 May 2009
Posts: 767
tthsqe 29 Apr 2023, 16:04
I can't use them means I can't set them? The main problem is setting them. I am perfectly happy saving what the system expects them to be before any calls to foreign code and then restoring them after the foreign code returns. I do not care about exceptions from foreign code.
Post 29 Apr 2023, 16:04
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20461
Location: In your JS exploiting you and your system
revolution 29 Apr 2023, 16:31
You can set to other values, but not to values of your choosing, only to matching slots in the GDT. The kernel only defines a few slots in the GDT, and nothing else. They are are little value to user-mode programs.
Post 29 Apr 2023, 16:31
View user's profile Send private message Visit poster's website Reply with quote
tthsqe



Joined: 20 May 2009
Posts: 767
tthsqe 29 Apr 2023, 16:42
So you are saying that when my threads are minding their own business and not calling into any foreign code, the kernel still expects gs and fs to point to valid-but-unspecified locations (for context switches)?
Post 29 Apr 2023, 16:42
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20461
Location: In your JS exploiting you and your system
revolution 29 Apr 2023, 16:47
It isn't the kernel, it is the CPU.

Each time you set GS it will read the GDT to find the slot and load the shadow space. Your code will fault if you try to set a value that the GDT doesn't have. And the GDT has very few slots. You can probably make copy of CS, or DS, but that is about all you get, everything else will fault.
Post 29 Apr 2023, 16:47
View user's profile Send private message Visit poster's website Reply with quote
tthsqe



Joined: 20 May 2009
Posts: 767
tthsqe 29 Apr 2023, 17:36
Let's start over since I am clearly not making myself clear. As a poor user in user land, I couldn't care less about which 16 bits actually are in fs and gs. I care about the addresses to and from which instructions like
mov rax,qword[gs:0] ; read from location X+0
mov qword[gs:8],rdx ; write to location X+8
read and write. It is my understanding that this can be any 64 bit offset X provided that gs is "set" to X. I am simply asking how I can control X as a normal user and if I need to save and/or restore X before calling into kernel32.dll, for example. The following works perfectly in linux and illustrates the point. Here I "set" gs to buffer, so X = address of buffer as above.

Code:
; this program should print "good"
ARCH_SET_GS = 0x1001
ARCH_SET_FS = 0x1002
ARCH_GET_FS = 0x1003
ARCH_GET_GS = 0x1004
sys_write      = 0x0001 
sys_arch_prctl = 0x009e
sys_exit_group = 0x00e7

format ELF64 executable
entry Start

segment readable writeable
str_good db "good",10
str_bad db "bad",10
buffer rb 1000

segment readable executable
Start:
   ; set "gs" (whatever that means) to the address of buffer
   lea rsi,[buffer]
   mov eax,sys_arch_prctl
   mov edi,ARCH_SET_GS
   syscall
   mov eax,"asdf"
   mov dword[buffer],eax
   ; the following should read from the same location as mov edx,dword[buffer]
   mov edx,dword[gs:0]
   cmp eax,edx
   je .good
.bad:
   mov edi,1
   lea rsi,[str_bad]
   mov edx,4
   mov eax,sys_write
   syscall
   xor edi,edi
   mov eax,sys_exit_group
   syscall
.good:
   mov edi,1
   lea rsi,[str_good]
   mov edx,5
   mov eax,sys_write
   syscall
   xor edi,edi
   mov eax,sys_exit_group
   syscall
    
Post 29 Apr 2023, 17:36
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20461
Location: In your JS exploiting you and your system
revolution 29 Apr 2023, 17:46
Okay, let's recap, just in case. As a user you can't set GS to a value of your choosing. The GDT table doesn't support it. The CPU will prevent you from setting a random value.

For the Linux code, you are using the kernel to make a new entry in the GDT which allows GS to point to some random memory location. But there is no similar Windows API you can call to ask the kernel to make a new entry in the GDT.
Post 29 Apr 2023, 17:46
View user's profile Send private message Visit poster's website Reply with quote
tthsqe



Joined: 20 May 2009
Posts: 767
tthsqe 29 Apr 2023, 17:58
Ok, thanks. Confirms my suspicions.
Post 29 Apr 2023, 17:58
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20461
Location: In your JS exploiting you and your system
revolution 29 Apr 2023, 18:10
I think some confusion has come about because you mentioned segment registers in the title.

So for anyone reading that might get confused.. Segment registers only exist in real mode. In protected mode the same names (CS, DS, etc. ) are reused for the selectors, which point to entries in the GDT (and LDT). The GDT/LDT are protected memory areas that the kernel maintains, to define the execution environment for the CPU.
Post 29 Apr 2023, 18:10
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 29 Apr 2023, 18:27
I think you are adding to the confusion here by name-dropping GDT, when in long mode the base of FS and GS is not controlled by GDT, but by dedicated MSR. And there even are specialized instructions (RDFSBASE/RDGSBASE and WRFSBASE/WRGSBASE) that allow to access these values directly. As explained by Feryno (in the linked thread) there are additional settings to consider - but as demonstrated by my example, at least under some circumstances it can be done even from usermode.
Post 29 Apr 2023, 18:27
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: 20461
Location: In your JS exploiting you and your system
revolution 29 Apr 2023, 18:34
Oh my. I am sorry if I misled.

So in that case, does Windows enable those instructions by default? The other thread makes them sound dangerous and the results are unreliable. Heisenbugs galore. Yuck. Smile
Post 29 Apr 2023, 18:34
View user's profile Send private message Visit poster's website Reply with quote
tthsqe



Joined: 20 May 2009
Posts: 767
tthsqe 29 Apr 2023, 19:01
So, I am going to call the address from which mov eax,[fs:0] reads as "fsbase". How far have you pushed this fs example link? Do you really need to restore fsbase before calling the messagebox, and it is generally safe to have fsbase containing random locations while your thread is doing its own thing? This is a windows 64 question, as the answer is "yes" to the second one on my ubuntu.
Post 29 Apr 2023, 19:01
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 30 Apr 2023, 12:20
Yes, it was very much needed to restore FS base immediately, and even then I don't think it was generally safe, because of possibility of an interrupt (and if scheduler wanted to drop in and do something before the FS base was restored, I would expect various kinds of disasters).

The support for such tricks on Windows comes and goes. There probably isn't anything you could rely on, because nothing on this level was officially documented as supported. See this article from 2017, which has interesting details about CR4[FSGSBASE] feature being enabled on Windows because it was used by UMS, but at the same time this information is severely outdated, because:
Microsoft wrote:
As of Windows 11, user-mode scheduling is not supported. All calls fail with the error ERROR_NOT_SUPPORTED.
Post 30 Apr 2023, 12:20
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: 20461
Location: In your JS exploiting you and your system
revolution 30 Apr 2023, 17:38
An easy to test for problems is to put a loop and wait.
Code:
 wrgsbase ...
.loop:
  jmp .loop    
An interrupt is guaranteed to happen within a handful milliseconds, so if you can run it and still have lots of time to press CTRL-C and the system is still up then that gives one data point.

Another data point can be to call a system API and see how it goes. You could put wrgsbase as the first instruction of a fasm build and use it to assemble something.

It won't give proof that it works 100%, but is could give proof of failure.

Edit: forgot to mention to make sure to use all cores/threads to ensure the core servicing the interrupt is involved.
Post 30 Apr 2023, 17:38
View user's profile Send private message Visit poster's website Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 2572
Furs 01 May 2023, 13:43
What's the worst that can happen if it does end up interrupting? Can it crash your system? Does it generate an exception you can catch in your app? Or just silently kill your process?

Before you say "test it", I'm not going to test what happens if I detonate a bomb in my home either. Call it paranoia.
Post 01 May 2023, 13:43
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20461
Location: In your JS exploiting you and your system
revolution 01 May 2023, 13:51
Furs wrote:
What's the worst that can happen ...
The universe will cease to exist when the VM we are all in gets into an anomalous condition. Twisted Evil

Failing that, I'd expect the worst to be a system crash (i.e. BSOD), requiring reboot. Perhaps using a VM with a maximum of one available thread could give some insight.
Post 01 May 2023, 13:51
View user's profile Send private message Visit poster's website Reply with quote
tthsqe



Joined: 20 May 2009
Posts: 767
tthsqe 02 May 2023, 00:42
So, I know that libraries rely on gs (gs:88 is special, but why they chose 88 is either lucky or a bit offensive), so I won't be touching gs. When I get a brand new windows 11 installation with a top-of-the-line cpu supporting wrfsbase, i'll be sure that the first thing I do is set fsbase to a random number and put the thread in an infinite nop loop.
Post 02 May 2023, 00:42
View user's profile Send private message Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 514
Location: Czech republic, Slovak republic
Feryno 02 May 2023, 03:52
FS, GS can be changed usually in kernelmode. On instructions like MOV FS,AX / MOV GS,AX the fs/gs bases are loaded from GDT (only 32 bit bases due to GDT limitations). Because these bases are expanded to 64 bits to support 64 bit OS-es, the kernel after the MOV FS,AX / MOV GS,AX executes WRMSR instructions with these values in ECX:
Code:
MSR_IA32_FS_BASE                =       0C0000100h
MSR_IA32_GS_BASE                =       0C0000101h
MSR_IA32_KERNEL_GS_BASE         =       0C0000102h    

You cannot execute WRMSR in usermode to adjust these bases, the wrmsr instruction is ring0 privileged.
Later additional methods were introduced like instructions RDFSBASE WRFSBASE RDGSBASE WRGSBASE - read the link posted by Tomasz.
tthsqe found an interesting method of adjusting the bases from usermode in Linux by a syscall. On ms win x64 I don't know similar easy method, but you can run a child process as a debuggee and then adjust its registers using system calls dedicated for debugger:
https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
it is necessary to set dwCreationFlags to DEBUG_PROCESS or to DEBUG_PROCESS+DEBUG_ONLY_THIS_PROCESS
Kernel on switches from ring3 into ring0 executes SWAPGS as a quick way of swapping ring3 and ring0 GS bases and it also executes the same just before switching back from ring0 to ring3 (usually code sequence swapgs \ iretq, swapgs \ sysretq). So do not play with ring0 gs base Smile
Post 02 May 2023, 03:52
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Hrstka



Joined: 05 May 2008
Posts: 61
Location: Czech republic
Hrstka 02 May 2023, 08:50
In 32-bit version of Windows XP there is an API call to add a new entry into the GDT. But I don't know if it still works in 64-bit Windows.
Post 02 May 2023, 08:50
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2  Next

< 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.