AsmResolver Version
6.0.0-beta.6
.NET Version
.NET 10.0
Operating System
Windows
Describe the Bug
When a PE has unsorted ClassLayout table rows with the sorted flag set to false, AsmResolver can silently fail to read and write FieldRvas
How To Reproduce
demo.zip
Reading:
var file = "./test.obf.dll";
var module = ModuleDefinition.FromFile(file);
var field = module.GetAllTypes()
.SelectMany(t => t.Fields)
.First(f => f.Name == "XorKey");
// check IsSorted
var ts = module.DotNetDirectory!.Metadata!.GetStream<TablesStream>();
var cl = ts.GetTable<ClassLayoutRow>(TableIndex.ClassLayout);
Console.WriteLine($"ClassLayout IsSorted: {cl.IsSorted}"); // false
Console.WriteLine("from FieldRva:");
var fromFieldRva = field.FieldRva?.WriteIntoArray() ?? [];
Console.WriteLine(fromFieldRva.Length); // prints 0
Console.WriteLine("from manually reading the table:");
var fieldType = field.Signature!.FieldType.Resolve(module.RuntimeContext);
var size = (int)(fieldType.ClassLayout?.ClassSize ?? 0); // get size from layout
var rvaTable = ts.GetTable<FieldRvaRow>(TableIndex.FieldRva);
for (var i = 0; i < rvaTable.Count; i++)
{
var row = rvaTable.GetByRid((uint)(i + 1));
if (row.Field == field.MetadataToken.Rid)
{
var raw = row.Data.CreateReader().ReadBytes(size);
Console.WriteLine(raw.Length); // prints 22
Console.WriteLine(Encoding.UTF8.GetString(raw)); // prints the key
break;
}
}
Writing:
var file = "./test.obf.dll";
var module = ModuleDefinition.FromFile(file);
module.Write("test.rebuilt.dll");
var rebuiltModule = ModuleDefinition.FromFile("test.rebuilt.dll");
var field = rebuiltModule.GetAllTypes()
.SelectMany(t => t.Fields)
.First(f => f.Name == "XorKey");
// check IsSorted
var ts = rebuiltModule.DotNetDirectory!.Metadata!.GetStream<TablesStream>();
var cl = ts.GetTable<ClassLayoutRow>(TableIndex.ClassLayout);
Console.WriteLine($"rebuilt ClassLayout IsSorted: {cl.IsSorted}"); // true since we re-built it (even if we use PreserveAll it will still be sorted)
var fromFieldRva = field.FieldRva?.WriteIntoArray() ?? [];
Console.WriteLine(fromFieldRva.Length); // prints 22
Console.WriteLine(Encoding.UTF8.GetString(fromFieldRva)); // garbage data (verifiable in a hex editor that it was overwritten)
Expected Behavior
Reading: field.FieldRva should have the data
Writing: It should preserve the field.FieldRva data and potentially stay unsorted?
Actual Behavior
Reading: field.FieldRva does not contain the data
Writing: The data stored in the field rva is overwritten
Additional Context
I haven't done much debugging, but it looks like whatever is reading it assumes sorted rows.
This doesn't seem like it'd ever output from the compiler, but the CLR runs it. I don't think this is compliant with the spec, but if it works, then it works.
The Ciphertext field rva is read and written correctly. I think that is just a coincidence though
AsmResolver Version
6.0.0-beta.6
.NET Version
.NET 10.0
Operating System
Windows
Describe the Bug
When a PE has unsorted
ClassLayouttable rows with the sorted flag set to false, AsmResolver can silently fail to read and writeFieldRvasHow To Reproduce
demo.zip
Reading:
Writing:
Expected Behavior
Reading:
field.FieldRvashould have the dataWriting: It should preserve the
field.FieldRvadata and potentially stay unsorted?Actual Behavior
Reading:
field.FieldRvadoes not contain the dataWriting: The data stored in the field rva is overwritten
Additional Context
I haven't done much debugging, but it looks like whatever is reading it assumes sorted rows.
This doesn't seem like it'd ever output from the compiler, but the CLR runs it. I don't think this is compliant with the spec, but if it works, then it works.
The
Ciphertextfield rva is read and written correctly. I think that is just a coincidence though