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
1 Reply
and the following is the code used to make the calculation
and finally, here is the usage
the idea is that the
so the next member after the
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);
}
}
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
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;
Padding
fixed size array is the target
, in other words: ConsoleHandle
nvm, this tweak fixed the miscalculationinternal 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;
}