Any DMG file has 'Koly' block, I don't think you will easily find ready to go code on windows capable to read it in C#...But take a look here http://newosxbook.com/DMG.html
What you practically interested in is last 512 bytes of a file.
typedef struct {
char Signature[4]; // Magic ('koly')
uint32_t Version; // Current version is 4
uint32_t HeaderSize; // sizeof(this), always 512
uint32_t Flags; // Flags
uint64_t RunningDataForkOffset; //
uint64_t DataForkOffset; // Data fork offset (usually 0, beginning of file)
uint64_t DataForkLength; // Size of data fork (usually up to the XMLOffset, below)
uint64_t RsrcForkOffset; // Resource fork offset, if any
uint64_t RsrcForkLength; // Resource fork length, if any
uint32_t SegmentNumber; // Usually 1, may be 0
uint32_t SegmentCount; // Usually 1, may be 0
uuid_t SegmentID; // 128-bit GUID identifier of segment (if SegmentNumber !=0)
uint32_t DataChecksumType; // Data fork
uint32_t DataChecksumSize; // Checksum Information
uint32_t DataChecksum[32]; // Up to 128-bytes (32 x 4) of checksum
uint64_t XMLOffset; // Offset of property list in DMG, from beginning
uint64_t XMLLength; // Length of property list
uint8_t Reserved1[120]; // 120 reserved bytes - zeroed
uint32_t ChecksumType; // Master
uint32_t ChecksumSize; // Checksum information
uint32_t Checksum[32]; // Up to 128-bytes (32 x 4) of checksum
uint32_t ImageVariant; // Commonly 1
uint64_t SectorCount; // Size of DMG when expanded, in sectors
uint32_t reserved2; // 0
uint32_t reserved3; // 0
uint32_t reserved4; // 0
} __attribute__((__packed__)) UDIFResourceFile;
Consider the following lines of code as example of reading bytes
public static uint ToUInt32BigEndian(byte[] buffer, int offset)
{
uint val = (uint)(((buffer[offset + 0] << 24) & 0xFF000000U) | ((buffer[offset + 1] << 16) & 0x00FF0000U)
| ((buffer[offset + 2] << 8) & 0x0000FF00U) | ((buffer[offset + 3] << 0) & 0x000000FFU));
return val;
}
public static ulong ToUInt64BigEndian(byte[] buffer, int offset)
{
return ((ulong)ToUInt32BigEndian(buffer, offset + 0) << 32) | ToUInt32BigEndian(buffer, offset + 4);
}
internal class UdifChecksum : IByteArraySerializable
{
public uint ChecksumSize;
public byte[] Data;
public uint Type;
public int Size
{
get { return 136; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Type = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0);
ChecksumSize = EndianUtilities.ToUInt32BigEndian(buffer, offset + 4);
Data = EndianUtilities.ToByteArray(buffer, offset + 8, 128);
return 136;
}
public void WriteTo(byte[] buffer, int offset)
{
throw new NotImplementedException();
}
}
Here you can see example of reading all properties from file header
internal class UdifResourceFile : IByteArraySerializable
{
public UdifChecksum DataForkChecksum;
public ulong DataForkLength;
public ulong DataForkOffset;
public uint Flags;
public uint HeaderSize;
public uint ImageVariant;
public UdifChecksum MasterChecksum;
public ulong RsrcForkLength;
public ulong RsrcForkOffset;
public ulong RunningDataForkOffset;
public long SectorCount;
public uint SegmentCount;
public Guid SegmentGuid;
public uint SegmentNumber;
public uint Signature;
public uint Version;
public ulong XmlLength;
public ulong XmlOffset;
public bool SignatureValid
{
get { return Signature == 0x6B6F6C79; }
}
public int Size
{
get { return 512; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Signature = EndianUtilities.ToUInt32BigEndian(buffer, offset + 0);
Version = EndianUtilities.ToUInt32BigEndian(buffer, offset + 4);
HeaderSize = EndianUtilities.ToUInt32BigEndian(buffer, offset + 8);
Flags = EndianUtilities.ToUInt32BigEndian(buffer, offset + 12);
RunningDataForkOffset = EndianUtilities.ToUInt64BigEndian(buffer, offset + 16);
DataForkOffset = EndianUtilities.ToUInt64BigEndian(buffer, offset + 24);
DataForkLength = EndianUtilities.ToUInt64BigEndian(buffer, offset + 32);
RsrcForkOffset = EndianUtilities.ToUInt64BigEndian(buffer, offset + 40);
RsrcForkLength = EndianUtilities.ToUInt64BigEndian(buffer, offset + 48);
SegmentNumber = EndianUtilities.ToUInt32BigEndian(buffer, offset + 56);
SegmentCount = EndianUtilities.ToUInt32BigEndian(buffer, offset + 60);
SegmentGuid = EndianUtilities.ToGuidBigEndian(buffer, offset + 64);
DataForkChecksum = EndianUtilities.ToStruct<UdifChecksum>(buffer, offset + 80);
XmlOffset = EndianUtilities.ToUInt64BigEndian(buffer, offset + 216);
XmlLength = EndianUtilities.ToUInt64BigEndian(buffer, offset + 224);
MasterChecksum = EndianUtilities.ToStruct<UdifChecksum>(buffer, offset + 352);
ImageVariant = EndianUtilities.ToUInt32BigEndian(buffer, offset + 488);
SectorCount = EndianUtilities.ToInt64BigEndian(buffer, offset + 492);
return Size;
}
public void WriteTo(byte[] buffer, int offset)
{
throw new NotImplementedException();
}
}
More details for code here (https://github.com/DiscUtils/DiscUtils)
If you do read XML definition of file (check XMLOffset and XMLLength) you should be able to identify file structure and extract files (and it's properties).
The XML Property list (which is uncompressed and easily viewable by seeking to the DOCTYPE declaration using more(1) or using tail(1)) is technically the resource fork of the DMG. The property list file contains, at a minimum, a "blkx" key, though it may contain other key/values, most commonly "plst", and sometimes a service level agreement (SLA) which will be displayed by the OS (specifically, /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/Resources/DiskImages UI Agent.app/Contents/MacOS/DiskImages UI Agent) as a pre-requisite to attaching the DMG*. Due to XML parser restrictions, data in the property list is 7-bit. This forces all binary (8-bit) data to be encoded using Base-64 encoding (a wiser choice would have been using CDATA blocks)
Since MAC is signing files (as far as I remember) the signature could be taken from there. Checksum at top level is protecting from package modification and should be made with same certificate that is used for signing.
Hope this helps.