diff --git a/src/AsmResolver.PE/DotNet/Builder/ManagedPEFileBuilder.cs b/src/AsmResolver.PE/DotNet/Builder/ManagedPEFileBuilder.cs index 8475d8805..f2c32be92 100644 --- a/src/AsmResolver.PE/DotNet/Builder/ManagedPEFileBuilder.cs +++ b/src/AsmResolver.PE/DotNet/Builder/ManagedPEFileBuilder.cs @@ -154,6 +154,31 @@ public IFieldRvaDataReader FieldRvaDataReader } } + /// + /// Creates a new managed PE file builder with default settings. + /// + public ManagedPEFileBuilder() + : this(ThrowErrorListener.Instance) + { + } + + /// + /// Creates a new managed PE file builder with the provided error listener. + /// + public ManagedPEFileBuilder(IErrorListener errorListener) + { + ErrorListener = errorListener; + } + + /// + /// Gets or sets the object responsible for recording diagnostic information during the building process. + /// + public IErrorListener ErrorListener + { + get; + set; + } + /// protected override ManagedPEBuilderContext CreateContext(IPEImage image) => new(image); @@ -436,7 +461,7 @@ protected override uint GetSectionAlignment(PEFile peFile, IPEImage image, Manag protected override uint GetImageBase(PEFile peFile, IPEImage image, ManagedPEBuilderContext context) => (uint) image.ImageBase; - private static void ProcessRvasInMetadataTables(ManagedPEBuilderContext context) + private void ProcessRvasInMetadataTables(ManagedPEBuilderContext context) { var dotNetSegment = context.DotNetSegment; var tablesStream = dotNetSegment.DotNetDirectory.Metadata?.GetStream(); @@ -447,7 +472,7 @@ private static void ProcessRvasInMetadataTables(ManagedPEBuilderContext context) AddFieldRvasToTable(context); } - private static void AddMethodBodiesToTable(MethodBodyTableBuffer table, TablesStream tablesStream) + private void AddMethodBodiesToTable(MethodBodyTableBuffer table, TablesStream tablesStream) { var methodTable = tablesStream.GetTable(); for (int i = 0; i < methodTable.Count; i++) @@ -472,7 +497,7 @@ private static void AddMethodBodiesToTable(MethodBodyTableBuffer table, TablesSt } } - private static ISegment? GetMethodBodySegment(MethodDefinitionRow methodRow) + private ISegment? GetMethodBodySegment(MethodDefinitionRow methodRow) { if (methodRow.Body.IsBounded) return methodRow.Body.GetSegment(); @@ -485,13 +510,13 @@ private static void AddMethodBodiesToTable(MethodBodyTableBuffer table, TablesSt return CilRawMethodBody.FromReader(ThrowErrorListener.Instance, ref reader); } - throw new NotImplementedException("Native unbounded method bodies cannot be reassembled yet."); + ErrorListener.NotSupported("Native unbounded method bodies cannot be reassembled yet."); } return null; } - private static void AddFieldRvasToTable(ManagedPEBuilderContext context) + private void AddFieldRvasToTable(ManagedPEBuilderContext context) { var directory = context.DotNetSegment.DotNetDirectory; var fieldRvaTable = directory.Metadata! @@ -504,18 +529,21 @@ private static void AddFieldRvasToTable(ManagedPEBuilderContext context) var table = context.DotNetSegment.FieldRvaTable; var reader = context.FieldRvaDataReader; - for (int i = 0; i < fieldRvaTable.Count; i++) + for (uint rid = 1; rid <= fieldRvaTable.Count; rid++) { + ref var row = ref fieldRvaTable.GetRowRef(rid); var data = reader.ResolveFieldData( - ThrowErrorListener.Instance, + ErrorListener, context.Platform, directory, - fieldRvaTable[i]); + row + ); if (data is null) continue; table.Add(data); + row.Data = data.ToReference(); } } } diff --git a/test/AsmResolver.PE.Tests/DotNet/Builder/ManagedPEFileBuilderTest.cs b/test/AsmResolver.PE.Tests/DotNet/Builder/ManagedPEFileBuilderTest.cs index d9678ea24..1d2ccda32 100644 --- a/test/AsmResolver.PE.Tests/DotNet/Builder/ManagedPEFileBuilderTest.cs +++ b/test/AsmResolver.PE.Tests/DotNet/Builder/ManagedPEFileBuilderTest.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.IO; using AsmResolver.PE.Code; using AsmResolver.PE.DotNet; using AsmResolver.PE.DotNet.Builder; @@ -21,33 +22,33 @@ public ManagedPEFileBuilderTest(TemporaryDirectoryFixture fixture) { _fixture = fixture; } - + [Fact] public void HelloWorldRebuild32BitNoChange() { // Read image var image = PEImage.FromBytes(Properties.Resources.HelloWorld); - + // Rebuild var builder = new ManagedPEFileBuilder(); var peFile = builder.CreateFile(image); - + // Verify _fixture .GetRunner() .RebuildAndRun(peFile, "HelloWorld", "Hello World!" + Environment.NewLine); } - + [Fact] public void HelloWorldRebuild64BitNoChange() { // Read image var image = PEImage.FromBytes(Properties.Resources.HelloWorld_X64); - + // Rebuild var builder = new ManagedPEFileBuilder(); var peFile = builder.CreateFile(image); - + // Verify _fixture .GetRunner() @@ -59,15 +60,15 @@ public void HelloWorld32BitTo64Bit() { // Read image var image = PEImage.FromBytes(Properties.Resources.HelloWorld); - + // Change machine type and pe kind to 64-bit image.MachineType = MachineType.Amd64; image.PEKind = OptionalHeaderMagic.PE32Plus; - + // Rebuild var builder = new ManagedPEFileBuilder(); var peFile = builder.CreateFile(image); - + // Verify _fixture .GetRunner() @@ -79,20 +80,40 @@ public void HelloWorld64BitTo32Bit() { // Read image var image = PEImage.FromBytes(Properties.Resources.HelloWorld_X64); - + // Change machine type and pe kind to 32-bit image.MachineType = MachineType.I386; image.PEKind = OptionalHeaderMagic.PE32; - + // Rebuild var builder = new ManagedPEFileBuilder(); var peFile = builder.CreateFile(image); - + // Verify _fixture .GetRunner() .RebuildAndRun(peFile, "HelloWorld", "Hello World!" + Environment.NewLine); } - + + [Fact] + public void UpdateFieldRvaRowsUnchanged() + { + var image = PEImage.FromBytes(Properties.Resources.FieldRvaTest); + + using var stream = new MemoryStream(); + var file = new ManagedPEFileBuilder(EmptyErrorListener.Instance).CreateFile(image); + file.Write(stream); + + var newImage = PEImage.FromBytes(stream.ToArray()); + var table = newImage.DotNetDirectory!.Metadata! + .GetStream() + .GetTable(); + + byte[] data = new byte[16]; + table[0].Data.CreateReader().ReadBytes(data, 0, data.Length); + Assert.Equal(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, data); + Assert.Equal(0x12345678u, table[1].Data.Rva); + } + } -} \ No newline at end of file +} diff --git a/test/AsmResolver.PE.Tests/Properties/Resources.Designer.cs b/test/AsmResolver.PE.Tests/Properties/Resources.Designer.cs index bd2e43a40..1ab8eda78 100644 --- a/test/AsmResolver.PE.Tests/Properties/Resources.Designer.cs +++ b/test/AsmResolver.PE.Tests/Properties/Resources.Designer.cs @@ -268,5 +268,12 @@ public static byte[] ForwarderDlls_ForwarderTest { return ((byte[])(obj)); } } + + public static byte[] FieldRvaTest { + get { + object obj = ResourceManager.GetObject("FieldRvaTest", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/test/AsmResolver.PE.Tests/Properties/Resources.resx b/test/AsmResolver.PE.Tests/Properties/Resources.resx index d916130a3..987d82cad 100644 --- a/test/AsmResolver.PE.Tests/Properties/Resources.resx +++ b/test/AsmResolver.PE.Tests/Properties/Resources.resx @@ -114,4 +114,7 @@ ..\Resources\ForwarderTest.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\FieldRvaTest.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + diff --git a/test/AsmResolver.PE.Tests/Resources/FieldRvaTest.exe b/test/AsmResolver.PE.Tests/Resources/FieldRvaTest.exe new file mode 100644 index 000000000..d3b4ff03e Binary files /dev/null and b/test/AsmResolver.PE.Tests/Resources/FieldRvaTest.exe differ