C
C#13mo ago
ophura

Calculate Field Offset Based On Members' Name

I'd like to know if the offset calculation is correct. the input used is as follows
private const string DUMP = @"
+0x000 MaximumLength : Uint4B
+0x004 Length : Uint4B
+0x008 Flags : Uint4B
+0x00c DebugFlags : Uint4B
+0x010 ConsoleHandle : Ptr64 Void
+0x018 ConsoleFlags : Uint4B
+0x020 StandardInput : Ptr64 Void
+0x028 StandardOutput : Ptr64 Void
+0x030 StandardError : Ptr64 Void
+0x038 CurrentDirectory : _CURDIR
+0x050 DllPath : _UNICODE_STRING
+0x060 ImagePathName : _UNICODE_STRING
+0x070 CommandLine : _UNICODE_STRING
+0x080 Environment : Ptr64 Void
+0x088 StartingX : Uint4B
+0x08c StartingY : Uint4B
+0x090 CountX : Uint4B
+0x094 CountY : Uint4B
+0x098 CountCharsX : Uint4B
+0x09c CountCharsY : Uint4B
+0x0a0 FillAttribute : Uint4B
+0x0a4 WindowFlags : Uint4B
+0x0a8 ShowWindowFlags : Uint4B
+0x0b0 WindowTitle : _UNICODE_STRING
+0x0c0 DesktopInfo : _UNICODE_STRING
+0x0d0 ShellInfo : _UNICODE_STRING
+0x0e0 RuntimeData : _UNICODE_STRING
+0x0f0 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR
+0x3f0 EnvironmentSize : Uint8B
+0x3f8 EnvironmentVersion : Uint8B
+0x400 PackageDependencyData : Ptr64 Void
+0x408 ProcessGroupId : Uint4B
+0x40c LoaderThreads : Uint4B
+0x410 RedirectionDllName : _UNICODE_STRING
+0x420 HeapPartitionName : _UNICODE_STRING
+0x430 DefaultThreadpoolCpuSetMasks : Ptr64 Uint8B
+0x438 DefaultThreadpoolCpuSetMaskCount : Uint4B
+0x43c DefaultThreadpoolThreadMaximum : Uint4B
+0x440 HeapMemoryTypeMask : Uint4B";
// NOTE: +0xoffset fieldName : FieldType
private const string DUMP = @"
+0x000 MaximumLength : Uint4B
+0x004 Length : Uint4B
+0x008 Flags : Uint4B
+0x00c DebugFlags : Uint4B
+0x010 ConsoleHandle : Ptr64 Void
+0x018 ConsoleFlags : Uint4B
+0x020 StandardInput : Ptr64 Void
+0x028 StandardOutput : Ptr64 Void
+0x030 StandardError : Ptr64 Void
+0x038 CurrentDirectory : _CURDIR
+0x050 DllPath : _UNICODE_STRING
+0x060 ImagePathName : _UNICODE_STRING
+0x070 CommandLine : _UNICODE_STRING
+0x080 Environment : Ptr64 Void
+0x088 StartingX : Uint4B
+0x08c StartingY : Uint4B
+0x090 CountX : Uint4B
+0x094 CountY : Uint4B
+0x098 CountCharsX : Uint4B
+0x09c CountCharsY : Uint4B
+0x0a0 FillAttribute : Uint4B
+0x0a4 WindowFlags : Uint4B
+0x0a8 ShowWindowFlags : Uint4B
+0x0b0 WindowTitle : _UNICODE_STRING
+0x0c0 DesktopInfo : _UNICODE_STRING
+0x0d0 ShellInfo : _UNICODE_STRING
+0x0e0 RuntimeData : _UNICODE_STRING
+0x0f0 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR
+0x3f0 EnvironmentSize : Uint8B
+0x3f8 EnvironmentVersion : Uint8B
+0x400 PackageDependencyData : Ptr64 Void
+0x408 ProcessGroupId : Uint4B
+0x40c LoaderThreads : Uint4B
+0x410 RedirectionDllName : _UNICODE_STRING
+0x420 HeapPartitionName : _UNICODE_STRING
+0x430 DefaultThreadpoolCpuSetMasks : Ptr64 Uint8B
+0x438 DefaultThreadpoolCpuSetMaskCount : Uint4B
+0x43c DefaultThreadpoolThreadMaximum : Uint4B
+0x440 HeapMemoryTypeMask : Uint4B";
// NOTE: +0xoffset fieldName : FieldType
No description
1 Reply
ophura
ophuraOP13mo ago
and the following is the code used to make the calculation
namespace WinDbg;

using System;

/// <summary>
/// represents a class for working with type dumps of <see href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/">WinDbg</see>
/// <see href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/dt--display-type-">td</see> command.
/// </summary>
internal class DisplayTypeDump
{
private readonly string[] dump;

/// <summary>
/// initializes a new instance of the <see cref="DisplayTypeDump"/> class.
/// </summary>
/// <param name="dump">an array of strings representing the type dump.</param>
internal DisplayTypeDump(string[] dump) => this.dump = dump;

/// <summary>
/// initializes a new instance of the <see cref="DisplayTypeDump"/> class.
/// </summary>
/// <param name="dump">a string representing the type dump.</param>
/// <param name="seperator">the separator used to split the dump string.</param>
internal DisplayTypeDump(string dump, string seperator) =>
this.dump = dump.Split(seperator);

/// <summary>
/// represents the 32nd character of the ASCII table.
/// </summary>
private const char BlankSpace = (char)ConsoleKey.Spacebar;

/// <summary>
/// gets the hexadecimal value from a given string.
/// </summary>
/// <param name="vessel">the input string containing the hexadecimal value.</param>
/// <returns>the hexadecimal value as an integer.</returns>
internal static int GetHexValue(string vessel)
{
const int HexBase = 16;
var start = new Index(3);
var end = new Index(vessel.IndexOf(BlankSpace, 6));
var hexNumberString = vessel[start..end];
var hexValue = Convert.ToInt32(hexNumberString, HexBase);
return hexValue;
}

/// <summary>
/// gets the field name from the input string.
/// </summary>
/// <param name="vessel">the input string containing the field name.</param>
/// <returns>the extracted field name.</returns>
internal static string GetFieldName(string vessel)
{
var start = new Index(7);
var end = new Index(vessel.IndexOf(BlankSpace, start.Value));
var fieldName = vessel[start..end];
return fieldName;
}

/// <summary>
/// gets the offset in bytes for a target field in the type dump.
/// </summary>
/// <param name="target">the target field whose offset is to be calculated.</param>
/// <param name="startField">the optional starting field for offset calculation.</param>
/// <returns>the offset in bytes.</returns>
internal int GetOffsetInBytes(string target, string startField = null!)
{
var previousHex = 0;
var startFieldProvided = !string.IsNullOrEmpty(startField);
var offsetSum = 0;

for (var i = 0; i < dump.Length; ++i)
{
var fieldName = GetFieldName(dump[i]);

// target reached!
if (fieldName.Equals(target)) break;

var currentHex = GetHexValue(dump[i]);

// skip duplicates of sequentially laid values,
// only considering the first occurrence.
// --------------------------------------------
// potentially skipping the very first value
// of the array since it's often 0.
if (currentHex == previousHex) continue;

if (startFieldProvided && !fieldName.Equals(startField))
{
offsetSum -= currentHex;
continue;
}

offsetSum += currentHex;
previousHex = currentHex;
}

return Math.Abs(offsetSum);
}
}
namespace WinDbg;

using System;

/// <summary>
/// represents a class for working with type dumps of <see href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/">WinDbg</see>
/// <see href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/dt--display-type-">td</see> command.
/// </summary>
internal class DisplayTypeDump
{
private readonly string[] dump;

/// <summary>
/// initializes a new instance of the <see cref="DisplayTypeDump"/> class.
/// </summary>
/// <param name="dump">an array of strings representing the type dump.</param>
internal DisplayTypeDump(string[] dump) => this.dump = dump;

/// <summary>
/// initializes a new instance of the <see cref="DisplayTypeDump"/> class.
/// </summary>
/// <param name="dump">a string representing the type dump.</param>
/// <param name="seperator">the separator used to split the dump string.</param>
internal DisplayTypeDump(string dump, string seperator) =>
this.dump = dump.Split(seperator);

/// <summary>
/// represents the 32nd character of the ASCII table.
/// </summary>
private const char BlankSpace = (char)ConsoleKey.Spacebar;

/// <summary>
/// gets the hexadecimal value from a given string.
/// </summary>
/// <param name="vessel">the input string containing the hexadecimal value.</param>
/// <returns>the hexadecimal value as an integer.</returns>
internal static int GetHexValue(string vessel)
{
const int HexBase = 16;
var start = new Index(3);
var end = new Index(vessel.IndexOf(BlankSpace, 6));
var hexNumberString = vessel[start..end];
var hexValue = Convert.ToInt32(hexNumberString, HexBase);
return hexValue;
}

/// <summary>
/// gets the field name from the input string.
/// </summary>
/// <param name="vessel">the input string containing the field name.</param>
/// <returns>the extracted field name.</returns>
internal static string GetFieldName(string vessel)
{
var start = new Index(7);
var end = new Index(vessel.IndexOf(BlankSpace, start.Value));
var fieldName = vessel[start..end];
return fieldName;
}

/// <summary>
/// gets the offset in bytes for a target field in the type dump.
/// </summary>
/// <param name="target">the target field whose offset is to be calculated.</param>
/// <param name="startField">the optional starting field for offset calculation.</param>
/// <returns>the offset in bytes.</returns>
internal int GetOffsetInBytes(string target, string startField = null!)
{
var previousHex = 0;
var startFieldProvided = !string.IsNullOrEmpty(startField);
var offsetSum = 0;

for (var i = 0; i < dump.Length; ++i)
{
var fieldName = GetFieldName(dump[i]);

// target reached!
if (fieldName.Equals(target)) break;

var currentHex = GetHexValue(dump[i]);

// skip duplicates of sequentially laid values,
// only considering the first occurrence.
// --------------------------------------------
// potentially skipping the very first value
// of the array since it's often 0.
if (currentHex == previousHex) continue;

if (startFieldProvided && !fieldName.Equals(startField))
{
offsetSum -= currentHex;
continue;
}

offsetSum += currentHex;
previousHex = currentHex;
}

return Math.Abs(offsetSum);
}
}
and finally, here is the usage
using System;
using WinDbg;

var dtd = new DisplayTypeDump(
dump: DUMP,
seperator: Environment.NewLine
);

var offset = dtd.GetOffsetInBytes(
target: "ConsoleHandle",
startField: "Length"
);

Console.WriteLine(offset); // NOTE: output is 16
using System;
using WinDbg;

var dtd = new DisplayTypeDump(
dump: DUMP,
seperator: Environment.NewLine
);

var offset = dtd.GetOffsetInBytes(
target: "ConsoleHandle",
startField: "Length"
);

Console.WriteLine(offset); // NOTE: output is 16
the idea is that the offset result is a value that could be used in a C struct like so:
#define OFFSET_RESULT 16
typedef struct _placeholder_t {
unsigned int MaximumLength;
unsigned int Length; // this is the starting field.
unsigned char /* aka: byte */ Padding[OFFSET_RESULT];
void* ConsoleHandle; // this is the target.
} placeholder_t;
#define OFFSET_RESULT 16
typedef struct _placeholder_t {
unsigned int MaximumLength;
unsigned int Length; // this is the starting field.
unsigned char /* aka: byte */ Padding[OFFSET_RESULT];
void* ConsoleHandle; // this is the target.
} placeholder_t;
so the next member after the Padding fixed size array is the target, in other words: ConsoleHandle nvm, this tweak fixed the miscalculation
internal unsafe long GetOffsetOf<T>(string target, string startAtField = null!)
where T : unmanaged
{
var currentHex = GetHexValue(dump[1]);
var previousHex = 0;
var startFieldPresent = !string.IsNullOrEmpty(startAtField);
var offsetSum = 0;

for (var index = 2; index < dump.Length; index++)
{
var fieldName = GetFieldName(dump[index - 2]);

// target reached!
if (fieldName.Equals(target)) break;

// skip duplicates of sequentially laid values,
// only considering the first occurrence.
if (currentHex != previousHex)
{
var fieldTypeSize = currentHex - previousHex;
previousHex = currentHex;

if (startFieldPresent)
{
if (fieldName.Equals(startAtField))
startFieldPresent = false;
else
offsetSum -= fieldTypeSize;
}
else
{
offsetSum += fieldTypeSize;
Console.WriteLine("{0}: {1}", fieldName, fieldTypeSize);
}
}

currentHex = GetHexValue(dump[index]);
}

var offset = checked(Math.Abs(offsetSum)*((long)sizeof(T)));
Console.WriteLine("{0} _[{1}];", typeof(T).Name.ToLower(), offset);
return offset;
}
internal unsafe long GetOffsetOf<T>(string target, string startAtField = null!)
where T : unmanaged
{
var currentHex = GetHexValue(dump[1]);
var previousHex = 0;
var startFieldPresent = !string.IsNullOrEmpty(startAtField);
var offsetSum = 0;

for (var index = 2; index < dump.Length; index++)
{
var fieldName = GetFieldName(dump[index - 2]);

// target reached!
if (fieldName.Equals(target)) break;

// skip duplicates of sequentially laid values,
// only considering the first occurrence.
if (currentHex != previousHex)
{
var fieldTypeSize = currentHex - previousHex;
previousHex = currentHex;

if (startFieldPresent)
{
if (fieldName.Equals(startAtField))
startFieldPresent = false;
else
offsetSum -= fieldTypeSize;
}
else
{
offsetSum += fieldTypeSize;
Console.WriteLine("{0}: {1}", fieldName, fieldTypeSize);
}
}

currentHex = GetHexValue(dump[index]);
}

var offset = checked(Math.Abs(offsetSum)*((long)sizeof(T)));
Console.WriteLine("{0} _[{1}];", typeof(T).Name.ToLower(), offset);
return offset;
}
Want results from more Discord servers?
Add your server