Skip to content

Commit

Permalink
Merge pull request #3 from 0vercl0k/master
Browse files Browse the repository at this point in the history
gdt.js: display segment attributes (0vercl0k#4)
  • Loading branch information
fengjixuchui authored Mar 9, 2022
2 parents 268d856 + 3a211f8 commit ae34e8d
Show file tree
Hide file tree
Showing 4 changed files with 361 additions and 6 deletions.
22 changes: 22 additions & 0 deletions Manifest/Manifest.1.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,28 @@
</Components>
</ExtensionPackage>

<ExtensionPackage>
<Name>Gdt</Name>
<Version>1.0.0.1</Version>
<Description>Gdt dump</Description>
<Components>
<ScriptComponent Name='Gdt' Type='Engine' File='..\gdt\gdt.js' FilePathKind='RepositoryRelative'>
<FunctionAliases>
<FunctionAlias Name='gdt'>
<AliasItem>
<Syntax>
<![CDATA[!gdt <segment selector>]]>
</Syntax>
<Description>
<![CDATA[Dump the GDT or a GDT entry.]]>
</Description>
</AliasItem>
</FunctionAlias>
</FunctionAliases>
</ScriptComponent>
</Components>
</ExtensionPackage>

<ExtensionPackage>
<Name>sm</Name>
<Version>1.0.0.1</Version>
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

`windbg-scripts` is a collection of [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extensions for WinDbg.

* [basics](https://github.com/0vercl0k/windbg-scripts/tree/master/basics): various examples of basic usage of various APIs,
* [parse_eh_win64](https://github.com/0vercl0k/windbg-scripts/blob/master/parse_eh_win64): example of extending the data-model with exception handling related information (cf [Debugger data model, Javascript & x64 exception handling](https://doar-e.github.io/blog/2017/12/01/debugger-data-model/)),
* [telescope](https://github.com/0vercl0k/windbg-scripts/blob/master/telescope): [telescope](https://gef.readthedocs.io/en/latest/commands/dereference/) like command for WinDbg,
* [sm](https://github.com/0vercl0k/windbg-scripts/blob/master/sm): pretty-printing of Spidermonkey `js::Value` and `JSObject` objects,
* [codecov](https://github.com/0vercl0k/windbg-scripts/blob/master/codecov): extract code-coverage out of a [TTD](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview) trace,
* [policybuffer](https://github.com/0vercl0k/windbg-scripts/blob/master/policybuffer): disassemble a Chrome policy buffer program.
* [basics](basics/): various examples of basic usage of various APIs,
* [parse_eh_win64](parse_eh_win64/): example of extending the data-model with exception handling related information (cf [Debugger data model, Javascript & x64 exception handling](https://doar-e.github.io/blog/2017/12/01/debugger-data-model/)),
* [telescope](telescope/): [telescope](https://gef.readthedocs.io/en/latest/commands/dereference/) like command for WinDbg,
* [sm](sm/): pretty-printing of Spidermonkey `js::Value` and `JSObject` objects,
* [codecov](codecov/): extract code-coverage out of a [TTD](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview) trace,
* [policybuffer](policybuffer/): disassemble a Chrome policy buffer program,
* [gdt](gdt/): dump the [Global Descriptor Table](https://wiki.osdev.org/Global_Descriptor_Table).

## Installing the script gallery

Expand Down
152 changes: 152 additions & 0 deletions gdt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# gdt.js

`gdt.js` is a [JavaScript](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/javascript-debugger-scripting) debugger extension for WinDbg that dumps the [Global Descriptor Table](https://wiki.osdev.org/Global_Descriptor_Table) on 64-bit kernels. I wrote this extension because I always find the output of the `dg` command confusing, if not broken.


## Usage

Run `.scriptload gdt.js` to load the script. You can dump a specific entry by passing the segment selector to the `!gdt` command, or it will dump the entire table if nothing is passed. Run `!wow64exts.sw` if you are running the script while being in the context of a [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details) thread.

## Examples

* Dumping the GDT entry that enables [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details):
```
32.kd> !gdt @cs
dt nt!_KGDTENTRY64 0xfffff8045215dfd0
Base: [0x0 -> 0xffffffff]
Type: Code Execute/Read Accessed (0xb)
DPL: 0x3
Present: 0x1
Mode: 32b Compat
@$gdt(@cs)
32.kd> dg @cs
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0023 00000000`00000000 00000000`ffffffff Code RE Ac 3 Bg Pg P Nl 00000cfb
```

* Dumping the GDT entry that allows 32-bit code to invoke 64-bit code:
```
32.kd> !gdt 0x33
dt nt!_KGDTENTRY64 0xfffff8045215dfe0
Base: [0x0 -> 0x0]
Type: Code Execute/Read Accessed (0xb)
DPL: 0x3
Present: 0x1
Mode: 64b
@$gdt(0x33)
32.kd> dg 33
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0033 00000000`00000000 00000000`00000000 Code RE Ac 3 Nb By P Lo 000002fb
```

* Dumping the [Task State Segment](https://wiki.osdev.org/Task_State_Segment):
```
32.kd> !gdt @tr
dt nt!_KGDTENTRY64 0xfffff8045215dff0
Base: [0xfffff8045215c000 -> 0xfffff8045215c067]
Type: TSS64 Busy (0xb)
DPL: 0x0
Present: 0x1
@$gdt(@tr)
32.kd> dg @tr
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0040 00000000`5215c000 00000000`00000067 TSS32 Busy 0 Nb By P Nl 0000008b
```

* Dumping the [Thread Environment Block](https://en.wikipedia.org/wiki/Win32_Thread_Information_Block) of a [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details) thread:
```
32.kd> !gdt @fs
dt nt!_KGDTENTRY64 0xfffff8045215e000
Base: [0x326000 -> 0x329c00]
Type: Data Read/Write Accessed (0x3)
DPL: 0x3
Present: 0x1
@$gdt(@fs)
32.kd> !teb
Wow64 TEB32 at 0000000000326000
ExceptionList: 00000000004ff59c
StackBase: 0000000000500000
StackLimit: 00000000004f2000
SubSystemTib: 0000000000000000
FiberData: 0000000000001e00
ArbitraryUserPointer: 0000000000000000
Self: 0000000000326000
EnvironmentPointer: 0000000000000000
ClientId: 0000000000001ad8 . 0000000000001adc
RpcHandle: 0000000000000000
Tls Storage: 0000000000834188
PEB Address: 0000000000323000
LastErrorValue: 0
LastStatusValue: c000007c
Count Owned Locks: 0
HardErrorMode: 0
```
* Dumping the entire GDT on a Windows 10 64-bit Virtual Machine:
```
32.kd> !gdt
Dumping the GDT from 0xfffff8045215dfb0 to 0xfffff8045215e007..
[0]: dt nt!_KGDTENTRY64 0xfffff8045215dfb0
Base: [0x0 -> 0x0]
Type: Reserved (0x0)
DPL: 0x0
Present: 0x0
[1]: dt nt!_KGDTENTRY64 0xfffff8045215dfb8
Base: [0x0 -> 0x0]
Type: Reserved (0x0)
DPL: 0x0
Present: 0x0
[2]: dt nt!_KGDTENTRY64 0xfffff8045215dfc0
Base: [0x0 -> 0x0]
Type: Code Execute/Read Accessed (0xb)
DPL: 0x0
Present: 0x1
Mode: 64b
[3]: dt nt!_KGDTENTRY64 0xfffff8045215dfc8
Base: [0x0 -> 0x0]
Type: Data Read/Write Accessed (0x3)
DPL: 0x0
Present: 0x1
[4]: dt nt!_KGDTENTRY64 0xfffff8045215dfd0
Base: [0x0 -> 0xffffffff]
Type: Code Execute/Read Accessed (0xb)
DPL: 0x3
Present: 0x1
Mode: 32b Compat
[5]: dt nt!_KGDTENTRY64 0xfffff8045215dfd8
Base: [0x0 -> 0xffffffff]
Type: Data Read/Write Accessed (0x3)
DPL: 0x3
Present: 0x1
[6]: dt nt!_KGDTENTRY64 0xfffff8045215dfe0
Base: [0x0 -> 0x0]
Type: Code Execute/Read Accessed (0xb)
DPL: 0x3
Present: 0x1
Mode: 64b
[7]: dt nt!_KGDTENTRY64 0xfffff8045215dfe8
Base: [0x0 -> 0x0]
Type: Reserved (0x0)
DPL: 0x0
Present: 0x0
[8]: dt nt!_KGDTENTRY64 0xfffff8045215dff0
Base: [0xfffff8045215c000 -> 0xfffff8045215c067]
Type: TSS64 Busy (0xb)
DPL: 0x0
Present: 0x1
[9]: dt nt!_KGDTENTRY64 0xfffff8045215e000
Base: [0x326000 -> 0x329c00]
Type: Data Read/Write Accessed (0x3)
DPL: 0x3
Present: 0x1
@$gdt()
```
180 changes: 180 additions & 0 deletions gdt/gdt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// Axel '0vercl0k' Souchet - October 8 2021

'use strict';

//
// Small reminders from the manual intels on segments in x64:
// - Because ES, DS, and SS segment registers are not used in 64-bit mode, their fields (base, limit, and attribute) in
// segment descriptor registers are ignored.
// - Selector.Index selects one of 8192 descriptors in the GDT or LDT. The processor multiplies
// the index value by 8 (the number of bytes in a segment descriptor) and adds the result to the base
// address of the GDT or LDT (from the GDTR or LDTR register, respectively).
// - The first entry of the GDT is not used by the processor.
// - The hidden descriptor register fields for FS.base and GS.base are physically mapped to MSRs in order to load all
// address bits supported by a 64-bit implementation. Software with CPL = 0 (privileged software) can load all
// supported linear-address bits into FS.base or GS.base using WRMSR.
//

const log = host.diagnostics.debugLog;
const logln = p => log(`${p}\n`);
const hex = p => `0x${p.toString(16)}`;

const GdtSystemEntryTypes = new Map([
[0, 'Reserved'],
[1, 'Reserved'],
[2, 'LDT'],
[3, 'Reserved'],
[4, 'Reserved'],
[5, 'Reserved'],
[6, 'Reserved'],
[7, 'Reserved'],
[8, 'Reserved'],
[9, 'TSS64 Available'],
[10, 'Reserved'],
[11, 'TSS64 Busy'],
[12, 'CallGate64'],
[13, 'Reserved'],
[14, 'InterruptGate64'],
[15, 'TrapGate64'],
]);

const GdtNonSystemEntryTypes = new Map([
[0, 'Data Read-Only'],
[1, 'Data Read-Only Accessed'],
[2, 'Data Read/Write'],
[3, 'Data Read/Write Accessed'],
[4, 'Data Read-Only Expand-Down'],
[5, 'Data Read-Only Expand-Down Accessed'],
[6, 'Data Read/Write Expand-Down'],
[7, 'Data Read/Write Expand-Down Accessed'],
[8, 'Code Execute-Only'],
[9, 'Code Execute-Only Accessed'],
[10, 'Code Execute/Read'],
[11, 'Code Execute/Read Accessed'],
[12, 'Code Execute-Only Conforming'],
[13, 'Code Execute-Only Conforming Accessed'],
[14, 'Code Execute/Read Conforming'],
[15, 'Code Execute/Read Conforming Accessed'],
]);

const GdtEntryTypes = new Map([
[0, GdtSystemEntryTypes],
[1, GdtNonSystemEntryTypes]
]);

class GdtEntry {
constructor(Addr) {
this._Addr = Addr;
const Entry = host.createPointerObject(Addr, 'nt', '_KGDTENTRY64*');
const LimitHigh = Entry.Bits.LimitHigh.bitwiseShiftLeft(16);
const LimitLow = Entry.LimitLow;
this._Limit = LimitHigh.add(LimitLow);
// For whatever reason _KGDTENTRY64 is 5 bits long. The intel manuals describes
// it as 4bits and the 'Descriptor type' bit.
// We grab the lower 4 bits as the type, and the MSB as the 'Descriptor type'.
this._Type = Entry.Bits.Type & 15;
this._NonSystem = (Entry.Bits.Type >> 4) & 1;
this._TypeS = GdtEntryTypes.get(this._NonSystem).get(this._Type);
// Note that system descriptors in IA-32e mode are 16 bytes instead
// of 8 bytes.
this._Size = 8;
if (!this._NonSystem && this._TypeS != 'Reserved') {
this._Size = 16;
}
this._Dpl = Entry.Bits.Dpl;
this._Granularity = Entry.Bits.Granularity;
this._Present = Entry.Bits.Present;
this._LongMode = Entry.Bits.LongMode;
this._DefaultBig = Entry.Bits.DefaultBig;
const BaseUpper = this._Size == 8 ? 0 : Entry.BaseUpper.bitwiseShiftLeft(32);
const BaseHigh = Entry.Bytes.BaseHigh.bitwiseShiftLeft(24);
const BaseMiddle = Entry.Bytes.BaseMiddle.bitwiseShiftLeft(16);
const BaseLow = Entry.BaseLow;
this._Base = BaseUpper.add(BaseHigh).add(BaseMiddle).add(BaseLow);
const Flags1 = Entry.Bytes.Flags1;
const Flags2 = Entry.Bytes.Flags2.bitwiseShiftLeft(8);
this._Attrs = Flags2.add(Flags1);
}

toString() {
const Increments = this._Granularity == 1 ? 1024 * 4 : 1;
// For example, when the granularity flag is set, a limit of 0 results in
// valid offsets from 0 to 4095.
const Size = this._Limit * Increments + (this._Granularity ? 0xfff : 0);
let S = `dt nt!_KGDTENTRY64 ${hex(this._Addr)}
Base: [${hex(this._Base)} -> ${hex(this._Base.add(Size))}]
Type: ${this._TypeS} (${hex(this._Type)})
DPL: ${hex(this._Dpl)}
Present: ${hex(this._Present)}
Atributes: ${hex(this._Attrs)}`;
if (this._TypeS.startsWith('Code')) {
S += `
Mode: ${this._LongMode ? '64b' : (this._DefaultBig ? '32b Compat' : '16b Compat')}`
}
return S;
}
}

function GetGdt() {
const Control = host.namespace.Debugger.Utility.Control;
const [_, GdtrValue] = Control.ExecuteCommand('r @gdtr').First().split('=');
const [__, GdtlValue] = Control.ExecuteCommand('r @gdtl').First().split('=');
return [host.parseInt64(GdtrValue, 16), host.parseInt64(GdtlValue, 16)];
}

function DumpGdtEntry(Addr) {
return new GdtEntry(Addr);
}

function DumpAllGdt() {
const [GdtBase, Gdtl] = GetGdt();
const GdtEnd = GdtBase.add(Gdtl);
logln(`Dumping the GDT from ${hex(GdtBase)} to ${hex(GdtEnd)}..`);
for (let CurrentEntry = GdtBase, Idx = 0;
CurrentEntry.compareTo(GdtEnd) < 0;
Idx++) {
const Entry = DumpGdtEntry(CurrentEntry);
logln(`[${Idx}]: ${Entry}`);
CurrentEntry = CurrentEntry.add(Entry._Size);
}
}

function DumpGdt(Selector) {
const [GdtBase, _] = GetGdt();
const Index = Selector.bitwiseShiftRight(3);
const Offset = Index.multiply(8);
const EntryAddress = GdtBase.add(Offset);
const Entry = DumpGdtEntry(EntryAddress);
logln(Entry);
}

function Gdt(Selector) {
const Attributes = host.currentSession.Attributes;
const IsKernel = Attributes.Target.IsKernelTarget;
//
// XXX: Not sure how to do this better?
// Attributes.Machine.PointerSize is 4 when running in a Wow64 thread :-/.
//
let Is64Bit = true;
try { host.createPointerObject(0, 'nt', '_KGDTENTRY64*'); } catch(e) { Is64Bit = false; }
if (!IsKernel || !Is64Bit) {
logln('The running session is not a kernel session or it is not running a 64-bit OS, so exiting');
return;
}

if (Selector == undefined) {
DumpAllGdt();
} else {
DumpGdt(Selector);
}
}

function initializeScript() {
return [
new host.apiVersionSupport(1, 3),
new host.functionAlias(
Gdt,
'gdt'
),
];
}

0 comments on commit ae34e8d

Please sign in to comment.