Skip to content

Instantly share code, notes, and snippets.

@e455a81e-d3ba-41a2-bc6d-7aafb1d9a5cd
Last active December 30, 2022 23:48
Show Gist options
  • Save e455a81e-d3ba-41a2-bc6d-7aafb1d9a5cd/e1d303a963d45887f29bfd8cfe8d363d to your computer and use it in GitHub Desktop.
Save e455a81e-d3ba-41a2-bc6d-7aafb1d9a5cd/e1d303a963d45887f29bfd8cfe8d363d to your computer and use it in GitHub Desktop.
Read the page at '0x00000000`7ffe0000' using F# and print its content.
open System
open System.Runtime.InteropServices
[<Literal>]
let MAXIMUM_XSTATE_FEATURES = 64
[<Literal>]
let PROCESSOR_FEATURE_MAX = 64
[<Struct; StructLayout(LayoutKind.Sequential)>]
type XSTATE_FEATURE =
{
Offset: uint32
Size: uint32
}
[<Struct; StructLayout(LayoutKind.Sequential)>]
type XSTATE_CONFIGURATION =
{
EnabledFeatures: uint64
Size: uint32
OptimizedSave: bool
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = MAXIMUM_XSTATE_FEATURES)>]
Features: XSTATE_FEATURE array
}
type NT_PRODUCT_TYPE =
| NtProductWinNt = 1
| NtProductLanManNt = 2
| NtProductServer = 3
[<Struct; StructLayout(LayoutKind.Sequential)>]
type KSYSTEM_TIME =
{
LowPart: uint32
High1Time: int32
High2Time: int32
}
type ALTERNATIVE_ARCHITECTURE_TYPE =
| StandardDesign
| NEC98x86
| EndAlternatives
[<Struct; StructLayout(LayoutKind.Sequential)>]
type KUSER_SHARED_DATA =
{
TickCountLowDeprecated: uint32
TickCountMultiplier: uint32
InterruptTime: KSYSTEM_TIME
SystemTime: KSYSTEM_TIME
TimeZoneBias: KSYSTEM_TIME
ImageNumberLow: uint16
ImageNumberHigh: uint16
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)>]
NtSystemRoot: int16 array
MaxStackTraceDepth: uint32
CryptoExponent: uint32
TimeZoneId: uint32
LargePageMinimum: uint32
AitSamplingValue: uint32
AppCompatFlag: uint32
RNGSeedVersion: uint64
GlobalValidationRunlevel: uint32
TimeZoneBiasStamp: int32
NtBuildNumber: uint32
NtProductType: NT_PRODUCT_TYPE
ProductTypeIsValid: bool
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)>]
Reserved0: bool array
NativeProcessorArchitecture: uint16
NtMajorVersion: uint32
NtMinorVersion: uint32
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = PROCESSOR_FEATURE_MAX)>]
ProcessorFeatures: bool array
Reserved1: uint32
Reserved3: uint32
TimeSlip: uint32
AlternativeArchitecture: ALTERNATIVE_ARCHITECTURE_TYPE
BootId: uint32
SystemExpirationDate: int64
SuiteMask: uint32
KdDebuggerEnabled: bool
MitigationPolicies: byte
CyclesPerYield: uint16
ActiveConsoleId: uint32
DismountCount: uint32
ComPlusPackage: uint32
LastSystemRITEventTickCount: uint32
NumberOfPhysicalPages: uint32
SafeBootMode: bool
VirtualizationFlags: byte
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)>]
Reserved12: byte array
SharedDataFlags: uint32
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)>]
DataFlagsPad: uint32 array
TestRetInstruction: uint64
QpcFrequency: int64
SystemCall: uint32
Reserved2: uint32
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)>]
SystemCallPad: uint64 array
TickCount: KSYSTEM_TIME
Cookie: uint32
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)>]
CookiePad: uint32 array
ConsoleSessionForegroundProcessId: int64
TimeUpdateLock: uint64
BaselineSystemTimeQpc: uint64
BaselineInterruptTimeQpc: uint64
QpcSystemTimeIncrement: uint64
QpcInterruptTimeIncrement: uint64
QpcSystemTimeIncrement32: uint32
QpcInterruptTimeIncrement32: uint32
QpcSystemTimeIncrementShift: byte
QpcInterruptTimeIncrementShift: byte
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)>]
Reserved8: byte array
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)>]
UserModeGlobalLogger: uint16 array
ImageFileExecutionOptions: uint32
LangGenerationCount: uint32
Reserved4: uint64
InterruptTimeBias: uint64
QpcBias: uint64
ActiveProcessorCount: uint32
ActiveGroupCount: uint8
Reserved9: uint8
QpcData: uint8
TimeZoneBiasEffectiveStart: uint64
TimeZoneBiasEffectiveEnd: uint64
XState: XSTATE_CONFIGURATION
}
// Struct used by the VirtualQueryEx function
[<Struct; StructLayout(LayoutKind.Sequential)>]
type MEMORY_BASIC_INFORMATION =
{ BaseAddress: IntPtr
AllocationBase: IntPtr
AllocationProtect: uint
RegionSize: IntPtr
State: uint
Protect: uint
Type: uint }
[<DllImport("kernel32.dll", SetLastError = true)>]
extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[<Out>] byte[] lpBuffer,
int dwSize,
int& lpNumberOfBytesRead
)
[<DllImport("kernel32.dll", SetLastError = true)>]
extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, MEMORY_BASIC_INFORMATION& lpBuffer, uint dwLength)
// Reads one 4kb page at the specified address from the current process
let readPage (address: IntPtr) =
let pageSize = 4 * 1024
let mutable buffer = Array.zeroCreate<byte> pageSize
let mutable bytesRead = 0
let success =
ReadProcessMemory(Diagnostics.Process.GetCurrentProcess().Handle, address, buffer, pageSize, &bytesRead)
if success then buffer else failwith "Failed to read memory"
// Determines whether the specified address is a valid memory address in the current process
let isValidAddress (address: IntPtr) =
let mutable info: MEMORY_BASIC_INFORMATION =
{ BaseAddress = new IntPtr(0)
AllocationBase = new IntPtr(0)
AllocationProtect = 0u
RegionSize = new IntPtr(0)
State = 0u
Protect = 0u
Type = 0u }
if
VirtualQueryEx(
Diagnostics.Process.GetCurrentProcess().Handle,
address,
&info,
(uint) sizeof<MEMORY_BASIC_INFORMATION>
) = 0
then
false
else
info.State = 0x1000u
let hexdump (page: byte[]) =
let sb = new Text.StringBuilder()
let hexLine (i: int) =
sb.AppendFormat("{0:X8}: ", i) |> ignore
let hexByte (j: int) =
if j < page.Length - i then
sb.AppendFormat("{0:X2} ", page.[i + j]) |> ignore
else
sb.Append(" ") |> ignore
[ 0..15 ] |> List.iter hexByte
sb.Append(" ")
let stringLine (i: int) =
let stringChar (j: int) =
if (j < page.Length) then
sb.Append(
if page.[i + j] >= 32uy && page.[i + j] <= 126uy then
char page.[i + j]
else
'.'
)
|> ignore
else
sb.Append(" ") |> ignore
[ 0..15 ] |> List.iter stringChar
let rec dumpLines i =
if i < page.Length then
hexLine i |> ignore
stringLine i
sb.AppendLine() |> ignore
dumpLines (i + 16)
dumpLines 0
sb.ToString()
let pageAddress = new IntPtr(0x000000007ffe0000L)
if isValidAddress (pageAddress) then
let page = readPage (pageAddress)
printfn "%A" page |> ignore
hexdump page |> printfn "%s" |> ignore
let pagePtr = Marshal.UnsafeAddrOfPinnedArrayElement (page, 0)
let data = Marshal.PtrToStructure<KUSER_SHARED_DATA>(pagePtr)
printfn "%A" data |> ignore
let systemRoot =
data.NtSystemRoot
|> Array.map (fun c -> char c)
|> Array.toList
|> String.Concat
printfn "%s" systemRoot |> ignore
else
printfn "Invalid address"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment