2

I'm trying to understand how to resolve segment addressing (specifically the GS register in X64).

My toy program:

int main()
{
    unsigned long long x;
    __debugbreak();
    x = __readgsqword(0x30);
    printf("0x%I64X", x);
}

which compiles to:

kd> u
00007ff6`10201074 cc                 int     3
00007ff6`10201075 65488b142530000000 mov   rdx,qword ptr gs:[30h]
...

Then I step once to reach the instruction which reads memory using the GS register, and I retrieve the value of the GS register, the content of the GDT, etc.

kd> r @gs
gs=002b

kd> r @gdtr
gdtr=fffff80105471fb0

kd> .formats @gs
Evaluate expression:
  Hex:     00000000`0000002b
  ...
  Binary:  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00101011

kd> dq (@gdtr + (5 * 8)) L1
fffff801`05471fd8  00cff300`0000ffff

kd> .formats poi(@gdtr + (5 * 8))
Evaluate expression:
  Hex:     00cff300`0000ffff
  ...
  Binary:  00000000 11001111 11110011 00000000 00000000 00000000 11111111 11111111

kd> dg gs
                                                    P Si Gr Pr Lo
Sel        Base              Limit          Type    l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
002B 00000000`00000000 00000000`ffffffff Data RW Ac 3 Bg Pg P  Nl 00000cf3

So from dg gs, I can see that the GS segment is at offset 0x0, which is consistent with the entry retrieved from the GDT using the GS register value.

Observe that at this point, offset 0x0 is not "valid" memory:

kd> dq gs:[30]
002b:00000000`00000030  ????????`???????? ????????`????????

kd> dq 30
00000000`00000030  ????????`???????? ????????`????????

Also note that the value of RDX at this point (before storing the QWORD retrieved from memory):

kd> r @rdx
rdx=000001dfbf892d40

Then I step once, expecting a bug check as I am retrieving invalid memory.

But surprisingly, it doesn't, and RDX appeared to have gotten assigned a value from somewhere:

kd> r @rdx
rdx=00000035ed53f000

Even more surprising, the GS register still resolves to offset 0x0 which still contains "invalid" memory!

kd> r @gs
gs=002b

kd> r @gdtr
gdtr=fffff80105471fb0

kd> dq (@gdtr + (5 * 8)) L1
fffff801`05471fd8  00cff300`0000ffff

kd> dg gs
                                                    P Si Gr Pr Lo
Sel        Base              Limit          Type    l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
002B 00000000`00000000 00000000`ffffffff Data RW Ac 3 Bg Pg P  Nl 00000cf3

kd> dq gs:[30]
002b:00000000`00000030  ????????`???????? ????????`????????

So...

  1. Why does my GS register resolve to offset 0x0?

  2. And where/how does the mov rdx,qword ptr gs:[30h] read memory from?

2

In long mode, segmentation is not really used and all segment registers have base of 0. fs and gs are exceptions that were added to address thread-specific data. Their real base addresses are stored in MSRs (model specific registers) instead of the descriptor table. The MSRs are only accessible in kernel mode, but you can get the value of GS indirectly via the !teb command, or ~ (list threads). The Teb: field will show the TEB base which matches the GS base for that thread. For more info, check SWAPGS on the Osdev wiki.

| improve this answer | |
  • in other words, 1. GS base is obtained from MSR C0000101 whenever it is referenced. 2. In usermode, MSR C0000101 contains TEB base. 3. In kernel mode, MSR C0000101 is swapped with MSR C0000102 (SWAPGS during SYSCALL) and then contains KPCR base. Correct? – Edwin Lee Apr 5 '19 at 15:14
2

gs is kernel mode only

you can see the data in windbg if you are in a kd session

0: kd> ? @@c++(@$pcr->GdtBase)
Evaluate expression: -8781100130384 = fffff803`7da55fb0
0: kd> dq gs:[0] l1
002b:00000000`00000000  fffff803`7da55fb0

__readgsword() is an intrinsic it must have some magic :)

1: kd> rdmsr c0000101
msr[c0000101] = ffffe601`555ea000
1: kd> dq ffffe601`555ea000 l1
ffffe601`555ea000  ffffe601`555fdfb0
1: kd> ? @@c++(@$pcr->GdtBase)
Evaluate expression: -28581575008336 = ffffe601`555fdfb0
1: kd> dq gs:[0] l1
002b:00000000`00000000  ffffe601`555fdfb0

!teb is user_gs_base (which is swapped by swapgs on entering kmode )

1: kd> rdmsr c0000102
msr[c0000102] = 00000080`56086000
1: kd> !teb
TEB at 0000008056086000
    ExceptionList:        0000000000000000
    StackBase:            0000008055f40000
    StackLimit:           0000008055f00000
| improve this answer | |
  • 1
    What magic do you know about it? I just see only mov rax, gs:[offset]. – Biswapriyo Apr 4 '19 at 12:45
  • gs is used in user mode too for thread-local data (similar to fs in x86). – Igor Skochinsky Apr 4 '19 at 13:15
  • @Biswapriyo nothing they are read using rdmsr in ring 0 msr for GS_BASE and kernel_gs_base are respectively c000_0101 / c000_0102 see the edit – blabb Apr 4 '19 at 17:55
  • @igorsk !teb is rdmsr 0xc000_0102 gs;[0] in kmode is rdmsr 0xc000_0101 – blabb Apr 4 '19 at 18:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.