PE Sections
Sections can be read and modified by accessing the PEFile.Sections
property, which is a collection of PESection objects.
PEFile file = ...
foreach (var section in file.Sections)
{
    Console.WriteLine(section.Name);
    Console.WriteLine($"\tFile Offset:  0x{section.Offset:X8}");
    Console.WriteLine($"\tRva:          0x{section.Rva:X8}");
    Console.WriteLine($"\tFile Size:    0x{section.Contents.GetPhysicalSize():X8}");
    Console.WriteLine($"\tVirtual Size: 0x{section.Contents.GetVirtualSize():X8}");
}
Reading Section Data
Each PESection object has a Contents property defined of type
IReadableSegment. This object is capable of creating a
BinaryStreamReader instance to read and parse data from the section:
var reader = section.CreateReader();
If you want to get the entire section in a byte array, you can take the
ToArray shortcut:
byte[] data = section.ToArray();
Alternatively, if you have a file offset or RVA to start read from, it
is also possible use one of the PEFile::CreateReaderAtXXX methods:
var reader = file.CreateReaderAtOffset(0x200);
var reader = file.CreateReaderAtRva(0x2000);
These methods will automatically find the right section to read from, and provide a reader that points to the start of this data.
Adding a new Section
The Sections property is mutable, which means it is possible to add
new sections and remove others from the PE. New sections can be created
using the PESection constructors:
var section = new PESection(".asmres", SectionFlags.MemoryRead | SectionFlags.ContentInitializedData);
section.Contents = new DataSegment(new byte[] {1, 2, 3, 4});
file.Sections.Add(section);
Some sections (such as .data or .bss) contain uninitialized data,
and might be resized in virtual memory at runtime. As such, the virtual
size of the contents might be different than its physical size. To make
dynamically sized sections, it is possible to use the VirtualSegment
to decorate a normal ISegment with a different virtual
size.
var section = new PESection(".asmres", SectionFlags.MemoryRead | SectionFlags.ContentUninitializedData);
var physicalContents = new DataSegment(new byte[] {1, 2, 3, 4});
section.Contents = new VirtualSegment(physicalContents, 0x1000); // Create a new segment with a virtual size of 0x1000 bytes.
file.Sections.Add(section);
For composing the contents of a section, see also Reading and Writing File Segments.
Updating Section Offsets
For performance reasons, offsets and sizes are not computed unless you
explicitly tell AsmResolver to align all sections and update all offsets
within a section. To force a recomputation of all section offsets and
sizes, you can use the PEFile::AlignSections method:
PESection section = ...;    
file.Sections.Add(section);
file.AlignSections();
Console.WriteLine($"New section RVA: 0x{section.Rva:X8}");
If you want to align the sections and also automatically update the
fields in the file and optional header of the PE file, it is also
possible to use PEFile::UpdateHeaders instead:
PESection section = ...;    
file.Sections.Add(section);
file.UpdateHeaders();
Console.WriteLine($"New section RVA: 0x{section.Rva:X8}");
Console.WriteLine($"New section count: {file.FileHeader.NumberOfSections}");
Note
While both AlignSections and UpdateHeaders do a traversal of the
segment tree, they may not update all offsets and sizes stored in the
sections themselves. When reading a PE file using any of the
PEFile::FromXXX, AsmResolver initializes every section's Contents
property with a single contiguous chunk of raw memory, and does not
parse any of the section contents. As such, if some code or data stored
in one of these raw section references code or data in another, this
will not be automatically updated. If, however, the Contents property
is an ISegment that does implement UpdateOffsets appropriately
(e.g., when using a SegmentBuilder), then all references stored in
such a segment will be updated accordingly.