2019-12-26 23:01:54 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.Collections.Generic ;
using System.Text ;
using System.IO ;
using iPhonePackager ;
using System.Diagnostics ;
using System.Security.Cryptography.X509Certificates ;
using System.Security.Cryptography ;
using System.Security.Cryptography.Pkcs ;
2021-10-12 21:21:22 -04:00
using SysadminsLV.Asn1Parser ;
using System.Xml ;
2014-03-14 14:13:41 -04:00
/// <summary>
/// Minimal implementation of the Mach object file format
/// See: http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html for format details
/// And: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/EXTERNAL_HEADERS/mach-o/loader.h for constant values
///
/// Only supports 32 bit little-endian executables, and writing back out is not fully supported.
/// A few classes have support for patching individual fields.
/// </summary>
namespace MachObjectHandling
{
// Values for CpuType
public enum CpuType
{
ARCH_ABI64 = 0x1000000 ,
I386 = 7 ,
ARM = 12 ,
POWERPC = 18 ,
ARM64 = ARCH_ABI64 | ARM ,
}
public class Bits
{
public enum Num
{
_32 = 32 ,
_64 = 64 ,
}
public static int Bytes ( Num num )
{
return ( num = = Num . _64 ) ? sizeof ( UInt64 ) : sizeof ( UInt32 ) ;
}
}
public abstract class RWContextBase
{
protected bool bIsOutput = false ;
public bool bStreamLittleEndian = true ;
protected Stack < long > Positions = new Stack < long > ( ) ;
protected byte [ ] SwapBuffer = new byte [ 8 ] ;
// When a file is within another file, FileOffset can be set so all external operations can act as if it is just a single file.
protected long ArchiveFileOffset ;
public void OpenFatArchiveAt ( long AtOffset )
{
if ( ArchiveFileOffset ! = 0 )
{
throw new InvalidDataException ( String . Format ( "Nested OpenFatAchriveAt() not supported:{0} {1} {2}" , Position , ArchiveFileOffset , AtOffset ) ) ;
}
// Start at beginning of file.
PushPositionAndJump ( 0 ) ;
ArchiveFileOffset = AtOffset ;
// Cause a seek to the ArchiveFileOffset.
Position = 0 ;
}
public void CloseFatArchive ( )
{
if ( ArchiveFileOffset = = 0 )
{
throw new InvalidDataException ( String . Format ( "CloseFatArchive() called without Open {0} {1}." , Position , ArchiveFileOffset ) ) ;
}
ArchiveFileOffset = 0 ;
PopPosition ( ) ;
}
public void PushPositionAndJump ( long NewOffset )
{
Positions . Push ( Position ) ;
Position = NewOffset ;
}
public void PushPosition ( )
{
Positions . Push ( Position ) ;
}
public void PopPosition ( )
{
Position = Positions . Pop ( ) ;
}
public abstract long Position
{
get ;
set ;
}
public void VerifyStreamPosition ( long StartingStreamPosition , long JustReadCount )
{
long ExpectedPosition = StartingStreamPosition + JustReadCount ;
if ( Position ! = ExpectedPosition )
{
throw new InvalidDataException ( String . Format ( "Stream offset is not as expected, {0} too {1} data!" ,
bIsOutput ? "wrote" : "read" ,
( Position < ExpectedPosition ) ? "little" : "much" ) ) ;
}
}
public void VerifyStreamPosition ( ref long StreamPosition , long JustReadCount )
{
StreamPosition + = JustReadCount ;
VerifyStreamPosition ( StreamPosition , 0 ) ;
}
}
public class ReadingContext : RWContextBase
{
protected BinaryReader SR ;
public ReadingContext ( BinaryReader Stream )
{
SR = Stream ;
}
public override long Position
{
get { return SR . BaseStream . Position - ArchiveFileOffset ; }
set { SR . BaseStream . Position = value + ArchiveFileOffset ; }
}
public UInt64 ReadUInt ( Bits . Num Num )
{
if ( Num = = Bits . Num . _64 )
return ReadUInt64 ( ) ;
else
return ReadUInt32 ( ) ;
}
public UInt32 ReadUInt32 ( )
{
if ( bStreamLittleEndian = = BitConverter . IsLittleEndian )
{
return SR . ReadUInt32 ( ) ;
}
else
{
SR . Read ( SwapBuffer , 0 , 4 ) ;
Array . Reverse ( SwapBuffer , 0 , 4 ) ;
return BitConverter . ToUInt32 ( SwapBuffer , 0 ) ;
}
}
public UInt64 ReadUInt64 ( )
{
if ( bStreamLittleEndian = = BitConverter . IsLittleEndian )
{
return SR . ReadUInt64 ( ) ;
}
else
{
SR . Read ( SwapBuffer , 0 , 8 ) ;
Array . Reverse ( SwapBuffer , 0 , 8 ) ;
return BitConverter . ToUInt64 ( SwapBuffer , 0 ) ;
}
}
public byte [ ] ReadBytes ( int DataSize )
{
return SR . ReadBytes ( DataSize ) ;
}
public byte [ ] ReadBytes ( UInt32 DataSize )
{
return SR . ReadBytes ( ( int ) DataSize ) ;
}
public byte ReadByte ( )
{
return SR . ReadByte ( ) ;
}
public string ReadFixedASCII ( int Length )
{
return Utilities . ReadFixedASCII ( SR , Length ) ;
}
public string ReadASCIIZ ( )
{
// Find the end of the string
long SavedPosition = Position ;
while ( ReadByte ( ) ! = 0 )
{
}
int Length = ( int ) ( Position - SavedPosition ) ;
Position = SavedPosition ;
// Read it
return ReadFixedASCII ( Length ) ;
}
}
public class WritingPhase
{
public delegate void DelayedWriteCallback ( WritingContext Context ) ;
public Queue < DelayedWriteCallback > PendingWrites = new Queue < DelayedWriteCallback > ( ) ;
public void Drain ( WritingContext Context )
{
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( "Draining phase with {0} writes" , PendingWrites . Count ) ;
}
while ( PendingWrites . Count > 0 )
{
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( " One delayed write at position = 0x{0:X}" , Context . Position ) ;
}
DelayedWriteCallback Job = PendingWrites . Dequeue ( ) ;
Job . Invoke ( Context ) ;
}
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( "Finished draining phase (curpos = 0x{0:X})" , Context . Position ) ;
}
}
public bool bWorkRemaining
{
get { return PendingWrites . Count > 0 ; }
}
}
public class DeferredFieldU32or64
{
// The start of the section we're counting length for
protected long AnchorPoint ;
// The offset the length is stored in
public long WritePoint ;
public Bits . Num AddressSize ;
public DeferredFieldU32or64 ( WritingContext Context , long Base , Bits . Num Address )
{
AnchorPoint = Base ;
WritePoint = Context . Position ;
AddressSize = Address ;
Context . Position + = Bits . Bytes ( AddressSize ) ;
}
public void Commit ( WritingContext Context )
{
long CurrentPos = Context . Position ;
long Length = CurrentPos - AnchorPoint ;
if ( AddressSize = = Bits . Num . _32 )
{
Debug . Assert ( ( Length > = UInt32 . MinValue ) & & ( Length < = UInt32 . MaxValue ) ) ;
}
Context . PushPositionAndJump ( WritePoint ) ;
Context . WriteUInt ( ( UInt64 ) Length , AddressSize ) ;
Context . PopPosition ( ) ;
}
}
public class LengthFieldU32or64 : DeferredFieldU32or64
{
public LengthFieldU32or64 ( WritingContext Context , long BaseOffset , Bits . Num Address )
: base ( Context , BaseOffset , Address )
{
}
public void Rebase ( long NewBaseOffset )
{
AnchorPoint = NewBaseOffset ;
}
}
public class OffsetFieldU32or64 : DeferredFieldU32or64
{
public OffsetFieldU32or64 ( WritingContext Context , long BaseOffset , Bits . Num Address )
: base ( Context , BaseOffset , Address )
{
}
}
public class WritingContext : RWContextBase
{
public WritingPhase CurrentPhase = new WritingPhase ( ) ;
Stack < WritingPhase > PendingPhases = new Stack < WritingPhase > ( ) ;
public void CreateNewPhase ( )
{
PendingPhases . Push ( CurrentPhase ) ;
CurrentPhase = new WritingPhase ( ) ;
}
public void Flush ( )
{
SW . Flush ( ) ;
SW . BaseStream . Flush ( ) ;
}
/// <summary>
/// Processes a phase worth of work, returning false when no work remains
/// </summary>
/// <returns>Returns true if more work remains</returns>
public bool ProcessEntirePhase ( )
{
CurrentPhase . Drain ( this ) ;
Debug . Assert ( CurrentPhase . PendingWrites . Count = = 0 ) ;
if ( PendingPhases . Count > 0 )
{
CurrentPhase = PendingPhases . Pop ( ) ;
}
return CurrentPhase . bWorkRemaining | | ( PendingPhases . Count > 0 ) ;
}
public LengthFieldU32or64 WriteDeferredLength ( long AlreadyCountedBytes , Bits . Num Num )
{
return new LengthFieldU32or64 ( this , Position - AlreadyCountedBytes , Num ) ;
}
public void CommitDeferredField ( DeferredFieldU32or64 Field )
{
Field . Commit ( this ) ;
}
/// <summary>
/// Emits an offset field that needs to be committed once the true offset is known
/// </summary>
/// <param name="AlreadyCountedBytes">The number of bytes ago that the offset is relative to. Pass in 0 to be relative to the current output position.</param>
/// <param name="Num">Number of bits this write will be.</param>
/// <returns></returns>
public OffsetFieldU32or64 WriteDeferredOffset ( long AlreadyCountedBytes , Bits . Num Num )
{
return new OffsetFieldU32or64 ( this , Position - AlreadyCountedBytes , Num ) ;
}
/// <summary>
/// Emits an offset field that needs to be committed once the true offset is known
/// </summary>
/// <param name="AlreadyCountedBytes">The number of bytes ago that the offset is relative to. Pass in 0 to be relative to the current output position.</param>
/// <param name="Num">Number of bits this write will be.</param>
/// <returns></returns>
public OffsetFieldU32or64 WriteDeferredOffsetFrom ( long BasePosition , Bits . Num Num )
{
return new OffsetFieldU32or64 ( this , BasePosition , Num ) ;
}
protected BinaryWriter SW ;
public WritingContext ( BinaryWriter Stream )
{
bIsOutput = true ;
SW = Stream ;
}
public override long Position
{
get { return SW . BaseStream . Position - ArchiveFileOffset ; }
set { SW . BaseStream . Position = value + ArchiveFileOffset ; }
}
public void CompleteWritingAndClose ( )
{
while ( ProcessEntirePhase ( ) )
{
}
SW . Close ( ) ;
}
public void WriteZeros ( long NumZeros )
{
if ( NumZeros > 0 )
{
byte [ ] ZeroList = new byte [ NumZeros ] ;
//@TODO: Should be unnecessary
for ( int i = 0 ; i < ZeroList . Length ; + + i )
{
ZeroList [ i ] = 0 ;
}
Write ( ZeroList ) ;
}
}
public void WriteUInt ( UInt64 Value , Bits . Num Num )
{
if ( Num = = Bits . Num . _64 )
Write ( Value ) ;
else
Write ( ( UInt32 ) Value ) ;
}
public void Write ( UInt32 Value )
{
if ( bStreamLittleEndian = = BitConverter . IsLittleEndian )
{
SW . Write ( Value ) ;
}
else
{
byte [ ] Buffer = BitConverter . GetBytes ( Value ) ;
Array . Reverse ( Buffer , 0 , 4 ) ;
SW . Write ( Buffer , 0 , 4 ) ;
}
}
public void Write ( UInt64 Value )
{
if ( bStreamLittleEndian = = BitConverter . IsLittleEndian )
{
SW . Write ( Value ) ;
}
else
{
byte [ ] Buffer = BitConverter . GetBytes ( Value ) ;
2021-05-25 02:43:26 -04:00
Array . Reverse ( Buffer , 0 , 8 ) ;
SW . Write ( Buffer , 0 , 8 ) ;
2014-03-14 14:13:41 -04:00
}
}
public void Write ( int Value )
{
Write ( ( UInt32 ) Value ) ;
}
public void Write ( byte [ ] Buffer )
{
SW . Write ( Buffer ) ;
}
public void Write ( byte [ ] Buffer , int Offset , int Length )
{
SW . Write ( Buffer , Offset , Length ) ;
}
public void Write ( byte Value )
{
SW . Write ( Value ) ;
}
public void WriteFixedASCII ( string Value , int BufferLength )
{
Utilities . WriteFixedASCII ( SW , Value , BufferLength ) ;
}
public void WriteAbsoluteOffsetAndDelayedData ( byte [ ] Data , long Alignment , Bits . Num Num )
{
WriteOffsetAndDelayedData ( Data , Alignment , 0 , Num ) ;
}
public void WriteOffsetAndDelayedData ( byte [ ] Data , long Alignment , long OffsetFrom , Bits . Num Num )
{
if ( ( Data = = null ) | | ( Data . Length = = 0 ) )
{
SW . Write ( ( UInt32 ) 0 ) ;
}
else
{
OffsetFieldU32or64 Offset = WriteDeferredOffsetFrom ( OffsetFrom , Num ) ;
CurrentPhase . PendingWrites . Enqueue ( delegate ( WritingContext Context )
{
long ModPosition = Position % Alignment ;
if ( ModPosition ! = 0 )
{
WriteZeros ( Alignment - ModPosition ) ;
}
CommitDeferredField ( Offset ) ;
Write ( Data ) ;
} ) ;
}
}
}
/// <summary>
/// Information about a single section within a segment
/// Warning: Contains absolute file offsets
/// </summary>
class MachSection
{
string SectionName ;
string SegmentName ;
UInt64 Addr ;
UInt64 Size ;
//UInt32 Offset;
UInt32 LogAlignment ;
UInt32 RelocationOffset ;
UInt32 NumRelocations ;
int Flags ;
UInt32 Reserved1 ;
UInt32 Reserved2 ;
// Only in 64 bit - Docs do not say so, but header file does http://llvm.org/docs/doxygen/html/Support_2MachO_8h_source.html.
UInt32 Reserved3 ;
Bits . Num AddressSize ;
byte [ ] SectionData ;
// These section types have an offset of 0 and no bytes of file data
const int S_ZEROFILL = 0x1 ;
const int S_GB_ZEROFILL = 0xC ;
public MachSection ( Bits . Num Num )
{
AddressSize = Num ;
}
public int SectionType
{
get { return ( ( int ) Flags ) & 0xFF ; }
set
{
Flags = ( Flags & ~ 0xFF ) | ( value & 0xFF ) ;
}
}
public void Read ( ReadingContext SR )
{
SectionName = SR . ReadFixedASCII ( 16 ) ;
SegmentName = SR . ReadFixedASCII ( 16 ) ;
Addr = SR . ReadUInt ( AddressSize ) ;
Size = SR . ReadUInt ( AddressSize ) ;
UInt32 FileOffset = SR . ReadUInt32 ( ) ;
LogAlignment = SR . ReadUInt32 ( ) ;
RelocationOffset = SR . ReadUInt32 ( ) ;
NumRelocations = SR . ReadUInt32 ( ) ;
Flags = ( int ) SR . ReadUInt32 ( ) ;
Reserved1 = SR . ReadUInt32 ( ) ;
Reserved2 = SR . ReadUInt32 ( ) ;
if ( AddressSize = = Bits . Num . _64 )
Reserved3 = SR . ReadUInt32 ( ) ;
if ( ( SectionType = = S_ZEROFILL ) | | ( SectionType = = S_GB_ZEROFILL ) )
{
Debug . Assert ( FileOffset = = 0 ) ;
SectionData = null ;
}
else
{
SR . PushPositionAndJump ( ( long ) FileOffset ) ;
SectionData = SR . ReadBytes ( ( int ) Size ) ;
SR . PopPosition ( ) ;
}
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( " v Read Section '{0}' in segment '{1}' with size {2} and offset 0x{3:X} and align {4} and flags {5:X}" , SectionName , SegmentName , Size , FileOffset , 1 < < ( byte ) LogAlignment , Flags ) ;
}
}
public void Write ( WritingContext SW )
{
SW . WriteFixedASCII ( SectionName , 16 ) ;
SW . WriteFixedASCII ( SegmentName , 16 ) ;
SW . WriteUInt ( Addr , AddressSize ) ;
SW . WriteUInt ( Size , AddressSize ) ;
SW . WriteAbsoluteOffsetAndDelayedData ( SectionData , 1 < < ( byte ) LogAlignment , AddressSize ) ;
SW . Write ( LogAlignment ) ;
SW . Write ( RelocationOffset ) ;
SW . Write ( NumRelocations ) ;
SW . Write ( ( UInt32 ) Flags ) ;
SW . Write ( Reserved1 ) ;
SW . Write ( Reserved2 ) ;
if ( AddressSize = = Bits . Num . _64 )
SW . Write ( Reserved3 ) ;
}
}
public abstract class MachLoadCommand
{
public UInt32 Command ;
public bool RequiredForDynamicLoad ;
/// <summary>
/// The offset (in bytes) from the start of the file when loading this command from a Mach
/// Object file, or -1 if this was created in memory. Only used for special modification code
/// that modifies individual commands without fully understanding the file format, since MachO
/// files aren't designed in such a way that allows piecemeal modifications...
/// </summary>
public long StartingLoadOffset = - 1 ;
/// <summary>
/// Information on a segment (payload type: MachLoadCommandSegment)
/// </summary>
public const UInt32 LC_SEGMENT = 0x01 ;
public const UInt32 LC_SEGMENT_64 = 0x19 ;
/// <summary>
/// Symbol and string tables (payload type: MachLoadCommandSymbolTable)
/// </summary>
public const UInt32 LC_SYMTAB = 0x02 ;
/// <summary>
/// Defines information about the main thread (payload type: MachLoadCommandMainThreadInfo)
/// </summary>
public const UInt32 LC_THREAD = 0x04 ;
public const UInt32 LC_UNIXTHREAD = 0x05 ;
/// <summary>
/// Information about the parts of the symbol table that are used for dynamic linking (payload type: MachLoadCommandDynamicSymbolTable)
/// </summary>
public const UInt32 LC_DYSYMTAB = 0x0B ;
// Specifies a dynamically linked library (payload type: MachLoadCommandDylib)
public const UInt32 LC_LOAD_DYLIB = 0x0C ;
public const UInt32 LC_ID_DYLIB = 0x0D ;
/// <summary>
/// Specifies the name of the dynamic linker to use (payload type: MachLoadCommandDynamicLinkerName)
/// </summary>
public const UInt32 LC_LOAD_DYLINKER = 0x0E ;
public const UInt32 LC_ID_DYLINKER = 0x0F ;
/// <summary>
/// Specifies a weak dynamically linked library (payload type: MachLoadCommandReferencedDLL)
/// </summary>
public const UInt32 LC_LOAD_WEAK_DYLIB = 0x18 ;
/// <summary>
/// Specifies a GUID (payload type: MachLoadCommandUUID)
/// </summary>
public const UInt32 LC_UUID = 0x1B ;
/// <summary>
/// Specifies the code signature information
/// </summary>
public const UInt32 LC_CODE_SIGNATURE = 0x1D ;
/// <summary>
/// Specifies an encrypted segment (payload type: MachLoadCommandEncryptedSegmentInfo)
/// </summary>
public const UInt32 LC_ENCRYPTION_INFO = 0x21 ;
/// <summary>
/// Specifies information the dynamic loader needs to load this object (payload type: MachLoadCommandDynamicLoaderInfo)
/// </summary>
public const UInt32 LC_DYLD_INFO = 0x22 ;
protected abstract void PackageData ( WritingContext SW ) ;
protected abstract void UnpackageData ( ReadingContext SR , int CommandSize ) ;
public static MachLoadCommand CreateFromStream ( ReadingContext SR )
{
long StartingStreamPosition = SR . Position ;
// Read the code and size (common to all commands)
UInt32 CommandCode = SR . ReadUInt32 ( ) ;
UInt32 CommandSize = SR . ReadUInt32 ( ) ;
UInt32 EffectiveCommand = CommandCode & ~ ( 1 < < 31 ) ;
MachLoadCommand Result ;
switch ( EffectiveCommand )
{
case LC_SEGMENT :
Result = new MachLoadCommandSegment ( Bits . Num . _32 ) ;
break ;
case LC_SEGMENT_64 :
Result = new MachLoadCommandSegment ( Bits . Num . _64 ) ;
break ;
case LC_SYMTAB :
Result = new MachLoadCommandSymbolTable ( ) ;
break ;
case LC_THREAD :
case LC_UNIXTHREAD :
Result = new MachLoadCommandMainThreadInfo ( ) ;
break ;
case LC_DYSYMTAB :
Result = new MachLoadCommandDynamicSymbolTable ( ) ;
break ;
case LC_LOAD_DYLIB :
case LC_LOAD_WEAK_DYLIB :
case LC_ID_DYLIB :
Result = new MachLoadCommandDylib ( ) ;
break ;
case LC_LOAD_DYLINKER :
case LC_ID_DYLINKER :
Result = new MachLoadCommandDynamicLinkerName ( ) ;
break ;
case LC_UUID :
Result = new MachLoadCommandUUID ( ) ;
break ;
case LC_CODE_SIGNATURE :
Result = new MachLoadCommandCodeSignature ( ) ;
break ;
case LC_ENCRYPTION_INFO :
Result = new MachLoadCommandEncryptedSegmentInfo ( ) ;
break ;
case LC_DYLD_INFO :
Result = new MachLoadCommandDynamicLoaderInfo ( ) ;
break ;
default :
Result = new MachLoadCommandOpaque ( ) ;
break ;
}
// MSB of CommandCode is LC_REQ_DYLD or 0, indicating if the command can (theoretically) be ignored
// or is critical to loading
Result . StartingLoadOffset = StartingStreamPosition ;
Result . Command = EffectiveCommand ;
Result . RequiredForDynamicLoad = ( CommandCode > > 31 ) ! = 0 ;
Result . UnpackageData ( SR , ( int ) CommandSize ) ;
SR . VerifyStreamPosition ( StartingStreamPosition , CommandSize ) ;
return Result ;
}
public void Write ( WritingContext SW )
{
long StartingPosition = SW . Position ;
// Write the command and length
SW . Write ( Command ) ;
LengthFieldU32or64 Length = SW . WriteDeferredLength ( 4 , Bits . Num . _32 ) ;
// Write the data
PackageData ( SW ) ;
// Write any pad bytes and commit the length
SW . WriteZeros ( ( SW . Position - StartingPosition ) % 4 ) ;
SW . CommitDeferredField ( Length ) ;
}
}
/// <summary>
/// Opaque mach load command
/// </summary>
class MachLoadCommandOpaque : MachLoadCommand
{
protected byte [ ] MyCommandData ;
protected override void PackageData ( WritingContext SW )
{
SW . Write ( MyCommandData , 0 , MyCommandData . Length ) ;
}
protected override void UnpackageData ( ReadingContext SR , int CommandSize )
{
int DataSize = ( int ) CommandSize - ( 2 * sizeof ( UInt32 ) ) ;
Debug . Assert ( DataSize > = 0 ) ;
MyCommandData = SR . ReadBytes ( DataSize ) ;
}
/// <summary>
/// Converts the N bytes at starting at Offset of the payload buffer to text (standard hex editor rules)
/// If N is 0, N is set to the size of the payload
/// </summary>
protected string PayloadToString ( int N , int Offset )
{
if ( N = = 0 )
{
N = int . MaxValue ;
}
N = Math . Min ( N , MyCommandData . Length - Offset ) ;
if ( N < = 0 )
{
return "" ;
}
char [ ] Buffer = new char [ N ] ;
int WriteIndex = 0 ;
while ( WriteIndex < Buffer . Length )
{
byte Value = MyCommandData [ WriteIndex + Offset ] ;
if ( Value > 0 )
{
Buffer [ WriteIndex ] = ( ( Value > = 0x20 ) & & ( Value < 0x7F ) ) ? ( char ) Value : '.' ;
WriteIndex + + ;
}
else
{
break ;
}
}
return new String ( Buffer , 0 , WriteIndex ) ;
}
public override string ToString ( )
{
return String . Format ( "Unknown 0x{0:X} with {1} bytes of data" , Command , MyCommandData . Length ) ;
}
}
/// <summary>
/// A blob of data in the __LINKEDIT segment
/// Warning: Contains absolute file offsets
/// </summary>
class MachLoadCommandLinkEditBlob : MachLoadCommand
{
public UInt32 BlobFileOffset ;
public UInt32 BlobFileSize ;
protected override void PackageData ( WritingContext SW )
{
SW . Write ( BlobFileOffset ) ;
SW . Write ( BlobFileSize ) ;
}
protected override void UnpackageData ( ReadingContext SR , int CommandSize )
{
BlobFileOffset = SR . ReadUInt32 ( ) ;
BlobFileSize = SR . ReadUInt32 ( ) ;
}
public override string ToString ( )
{
return String . Format ( "Blob at offset 0x{0:X} with a size of {1} KB" , BlobFileOffset , BlobFileSize / 1024.0f ) ;
}
}
/// <summary>
/// Payload for LC_SEGMENT && LC_SEGMENT_64
/// Warning: Contains absolute file offsets (what an awful file format!)
/// </summary>
class MachLoadCommandSegment : MachLoadCommand
{
public string SegmentName ;
public UInt64 VirtualAddress ;
public UInt64 VirtualSize ;
public UInt64 FileOffset ;
public UInt64 FileSize ;
public UInt32 MaxProt ;
public UInt32 InitProt ;
public List < MachSection > Sections = new List < MachSection > ( ) ;
public UInt32 Flags ;
public Bits . Num AddressSize ;
public MachLoadCommandSegment ( Bits . Num Addressing )
{
AddressSize = Addressing ;
}
/// <summary>
/// Patches just the file length of this segment in an existing file
/// </summary>
public void PatchFileLength ( WritingContext SW , UInt32 NewLength )
{
Debug . Assert ( StartingLoadOffset > = 0 ) ;
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( "ZZZZZZZZZZZZZZZZ" ) ;
Console . WriteLine ( " Segment Length from 0x{0:X} to 0x{1:X} (delta {2})" , FileSize , NewLength , ( long ) NewLength - ( long ) FileSize ) ;
Console . WriteLine ( "ZZZZZZZZZZZZZZZZ" ) ;
}
FileSize = NewLength ;
long PatchOffset = StartingLoadOffset + ( 2 * sizeof ( UInt32 ) ) ; // Command, CommandLength
PatchOffset + = 16 ; // SegmentName
PatchOffset + = 3 * Bits . Bytes ( AddressSize ) ; // VirtualAddress, VirtualSize, FileOffset
SW . PushPositionAndJump ( PatchOffset ) ;
SW . WriteUInt ( FileSize , AddressSize ) ;
SW . PopPosition ( ) ;
}
protected override void PackageData ( WritingContext SW )
{
// Write the segment load command
SW . WriteFixedASCII ( SegmentName , 16 ) ;
SW . WriteUInt ( VirtualAddress , AddressSize ) ;
SW . WriteUInt ( VirtualSize , AddressSize ) ;
// Offset to first segment
//@TODO: These file offsets and file lengths aren't correct (compared to the original MachO's)
OffsetFieldU32or64 FileOffset = SW . WriteDeferredOffsetFrom ( 0 , AddressSize ) ;
LengthFieldU32or64 FileLength = SW . WriteDeferredLength ( 0 , AddressSize ) ;
SW . Write ( MaxProt ) ;
SW . Write ( InitProt ) ;
SW . Write ( Sections . Count ) ;
SW . Write ( Flags ) ;
// Enqueue a job to commit the file offset to the first section
SW . CurrentPhase . PendingWrites . Enqueue ( delegate ( WritingContext Context )
{
FileLength . Rebase ( Context . Position ) ;
FileOffset . Commit ( Context ) ;
} ) ;
// Write the sections belonging to the segment
foreach ( MachSection Section in Sections )
{
Section . Write ( SW ) ;
}
// Enqueue a job to commit the length of data in all the sections
SW . CurrentPhase . PendingWrites . Enqueue ( delegate ( WritingContext Context )
{
FileLength . Commit ( Context ) ;
} ) ;
}
public override string ToString ( )
{
return String . Format ( "Segment '{0}' with {1} sections (Offset 0x{2:X} Size {3})" , SegmentName , Sections . Count , FileOffset , FileSize ) ;
}
protected override void UnpackageData ( ReadingContext SR , int CommandSize )
{
// Read the segment
SegmentName = SR . ReadFixedASCII ( 16 ) ;
VirtualAddress = SR . ReadUInt ( AddressSize ) ;
VirtualSize = SR . ReadUInt ( AddressSize ) ;
FileOffset = SR . ReadUInt ( AddressSize ) ;
FileSize = SR . ReadUInt ( AddressSize ) ;
MaxProt = SR . ReadUInt32 ( ) ;
InitProt = SR . ReadUInt32 ( ) ;
UInt32 SectionCount = SR . ReadUInt32 ( ) ;
Flags = SR . ReadUInt32 ( ) ;
// Read the segment data
//SR.PushPositionAndJump(FileOffset);
//FileData = SR.ReadBytes(FileSize);
//SR.PopPosition();
// Read the sections belonging to the segment
for ( int SectionIndex = 0 ; SectionIndex < SectionCount ; + + SectionIndex )
{
MachSection Section = new MachSection ( AddressSize ) ;
Section . Read ( SR ) ;
Sections . Add ( Section ) ;
}
}
}
/// <summary>
/// Payload for LC_SYMTAB
/// Warning: Contains absolute offsets
/// </summary>
class MachLoadCommandSymbolTable : MachLoadCommand
{
UInt32 SymbolTableOffset ;
UInt32 SymbolCount ;
UInt32 StringTableOffset ;
UInt32 StringTableSize ;
protected override void PackageData ( WritingContext SW )
{
SW . Write ( SymbolTableOffset ) ;
SW . Write ( SymbolCount ) ;
SW . Write ( StringTableOffset ) ;
SW . Write ( StringTableSize ) ;
}
protected override void UnpackageData ( ReadingContext SR , int CommandSize )
{
SymbolTableOffset = SR . ReadUInt32 ( ) ;
SymbolCount = SR . ReadUInt32 ( ) ;
StringTableOffset = SR . ReadUInt32 ( ) ;
StringTableSize = SR . ReadUInt32 ( ) ;
}
public override string ToString ( )
{
return String . Format ( "Symbol Table with {0} symbols at offset 0x{1:X} and a {2} KB string table at offset 0x{3:X}" ,
SymbolCount , SymbolTableOffset ,
StringTableSize / 1024.0f , StringTableOffset ) ;
}
}
/// <summary>
/// Payload for LC_THREAD and LC_UNIXTHREAD
/// </summary>
class MachLoadCommandMainThreadInfo : MachLoadCommandOpaque
{
public override string ToString ( )
{
switch ( Command )
{
case LC_THREAD :
return "Main thread configuration (no default stack)" ;
case LC_UNIXTHREAD :
return "Main thread configuration (with a stack)" ;
default :
return base . ToString ( ) ;
}
}
}
/// <summary>
/// Payload for LC_DYSYMTAB
/// Warning: Contains absolute file offsets
/// </summary>
class MachLoadCommandDynamicSymbolTable : MachLoadCommand
{
UInt32 [ ] Payload = new UInt32 [ 18 ] ;
// 6,8,10,12,14,16 are offsets :(
protected override void PackageData ( WritingContext SW )
{
for ( int i = 0 ; i < Payload . Length ; + + i )
{
SW . Write ( Payload [ i ] ) ;
}
}
protected override void UnpackageData ( ReadingContext SR , int CommandSize )
{
for ( int i = 0 ; i < Payload . Length ; + + i )
{
Payload [ i ] = SR . ReadUInt32 ( ) ;
}
}
public override string ToString ( )
{
return "Information on symbols needed for dynamic linking and loading" ;
}
}
/// <summary>
/// Payload for LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, and LC_ID_DYLIB
/// </summary>
class MachLoadCommandDylib : MachLoadCommandOpaque
{
public override string ToString ( )
{
string Result ;
switch ( Command )
{
case LC_LOAD_DYLIB :
Result = "Load Dynamic Library" ;
break ;
case LC_LOAD_WEAK_DYLIB :
Result = "Load Weak Dynamic Library" ;
break ;
case LC_ID_DYLIB :
Result = "Dynamic Library Install Name" ;
break ;
default :
Result = base . ToString ( ) ;
return Result ;
}
return String . Format ( "{0} '{1}'" , Result , PayloadToString ( 0 , 16 ) ) ;
}
}
/// <summary>
/// Payload for LC_LOAD_DYLINKER and LC_ID_DYLINKER
/// </summary>
class MachLoadCommandDynamicLinkerName : MachLoadCommandOpaque
{
public override string ToString ( )
{
return String . Format ( "Dynamic linker name '{0}'" , PayloadToString ( 0 , 4 ) ) ;
}
}
/// <summary>
/// Payload for LC_UUID
/// </summary>
class MachLoadCommandUUID : MachLoadCommandOpaque
{
public override string ToString ( )
{
Guid ID = new Guid ( MyCommandData ) ;
return String . Format ( "UUID: {0}" , ID ) ;
}
}
/// <summary>
/// Payload for LC_ENCRYPTION_INFO
/// </summary>
class MachLoadCommandEncryptedSegmentInfo : MachLoadCommand
{
UInt32 EncryptedFileOffset ;
UInt32 EncryptedFileSize ;
UInt32 EncryptionMode ;
protected override void PackageData ( WritingContext SW )
{
SW . Write ( EncryptedFileOffset ) ;
SW . Write ( EncryptedFileSize ) ;
SW . Write ( EncryptionMode ) ;
}
protected override void UnpackageData ( ReadingContext SR , int CommandSize )
{
EncryptedFileOffset = SR . ReadUInt32 ( ) ;
EncryptedFileSize = SR . ReadUInt32 ( ) ;
EncryptionMode = SR . ReadUInt32 ( ) ;
}
public override string ToString ( )
{
return String . Format ( "Encrypted segment at offset 0x{0:X} of size {1} KB in mode {2}" , EncryptedFileOffset , EncryptedFileSize / 1024.0f , EncryptionMode ) ;
}
}
/// <summary>
/// Payload for LC_DYLD_INFO
/// Warning: Contains absolute file offsets
/// </summary>
class MachLoadCommandDynamicLoaderInfo : MachLoadCommand
{
UInt32 [ ] Payload = new UInt32 [ 10 ] ;
// 0,2,4,6,8 are offsets :(
protected override void PackageData ( WritingContext SW )
{
for ( int i = 0 ; i < Payload . Length ; + + i )
{
SW . Write ( Payload [ i ] ) ;
}
}
protected override void UnpackageData ( ReadingContext SR , int CommandSize )
{
for ( int i = 0 ; i < Payload . Length ; + + i )
{
Payload [ i ] = SR . ReadUInt32 ( ) ;
}
}
public override string ToString ( )
{
return "Compressed information needed for dynamic loading" ;
}
}
class SigningBlob
{
const UInt32 Magic = 0xC00CDEFA ;
}
public class MachObjectFile
{
public UInt32 Magic ;
public UInt32 CpuType ;
public UInt32 CpuSubType ;
public UInt32 FileType ;
// NumCommands
// SizeOfCommand
public UInt32 Flags ;
public UInt32 Reserved64 ; // Only in mach_header_64
public List < MachLoadCommand > Commands = new List < MachLoadCommand > ( ) ;
// Values for Magic
public const UInt32 MH_MAGIC = 0xFEEDFACE ;
public const UInt32 MH_CIGAM = 0xCEFAEDFE ;
public const UInt32 MH_MAGIC_64 = 0xFEEDFACF ;
public const UInt32 MH_CIGAM_64 = 0xCFFAEDFE ;
// Values for FileType
public const UInt32 MH_EXECUTE = 2 ;
public const int MachHeaderPad = 0x1A60 ;
public void Read ( ReadingContext SR )
{
long ExpectedStreamPosition = 0 ;
int NumInt = 7 ;
// Read the header
Magic = SR . ReadUInt32 ( ) ;
CpuType = SR . ReadUInt32 ( ) ;
CpuSubType = SR . ReadUInt32 ( ) ;
FileType = SR . ReadUInt32 ( ) ;
UInt32 NumCommands = SR . ReadUInt32 ( ) ;
UInt32 SizeOfCommands = SR . ReadUInt32 ( ) ;
Flags = SR . ReadUInt32 ( ) ;
if ( Magic = = MH_MAGIC_64 )
{
Reserved64 = SR . ReadUInt32 ( ) ;
NumInt + + ;
}
SR . VerifyStreamPosition ( ref ExpectedStreamPosition , NumInt * sizeof ( UInt32 ) ) ;
// Read the commands
Commands . Clear ( ) ;
for ( int LoadCommandIndex = 0 ; LoadCommandIndex < NumCommands ; + + LoadCommandIndex )
{
MachLoadCommand Command = MachLoadCommand . CreateFromStream ( SR ) ;
Commands . Add ( Command ) ;
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( " Read command: {0}" , Command . ToString ( ) ) ;
}
}
SR . VerifyStreamPosition ( ref ExpectedStreamPosition , SizeOfCommands ) ;
}
public void Write ( WritingContext Context )
{
// Write the header
Context . Write ( Magic ) ;
Context . Write ( CpuType ) ;
Context . Write ( CpuSubType ) ;
Context . Write ( FileType ) ;
Context . Write ( ( UInt32 ) Commands . Count ) ;
LengthFieldU32or64 SizeOfCommands = Context . WriteDeferredLength ( - 2 * sizeof ( UInt32 ) , Bits . Num . _32 ) ; // Size of commands, after this field and the flags field
Context . Write ( Flags ) ;
if ( Magic = = MH_MAGIC_64 )
{
Context . Write ( Reserved64 ) ;
}
// Write each command (which may enqueue deferred work)
foreach ( MachLoadCommand Command in Commands )
{
Command . Write ( Context ) ;
}
Context . CommitDeferredField ( SizeOfCommands ) ;
//@TODO: Figure out where this offsetting comes from
long MainStartPosition = MachHeaderPad ;
Context . WriteZeros ( MainStartPosition - Context . Position ) ;
// Drain deferred work until the file is completely done
Context . CompleteWritingAndClose ( ) ;
}
public void LoadFromFile ( string Filename )
{
BinaryReader SR = new BinaryReader ( File . OpenRead ( Filename ) ) ;
ReadingContext Context = new ReadingContext ( SR ) ;
Read ( Context ) ;
SR . Close ( ) ;
}
public void LoadFromBytes ( byte [ ] Data )
{
MemoryStream Stream = new MemoryStream ( Data , false ) ;
BinaryReader SR = new BinaryReader ( Stream ) ;
ReadingContext Context = new ReadingContext ( SR ) ;
Read ( Context ) ;
SR . Close ( ) ;
}
public void WriteToFile ( string Filename )
{
BinaryWriter SW = new BinaryWriter ( File . OpenWrite ( Filename ) ) ;
WritingContext Context = new WritingContext ( SW ) ;
Write ( Context ) ;
SW . Close ( ) ;
}
}
/// <summary>
/// Blob for magic CSMAGIC_ENTITLEMENTS
/// </summary>
public class EntitlementsBlob : OpaqueBlob
{
public EntitlementsBlob ( )
{
MyMagic = CSMAGIC_ENTITLEMENTS ;
}
public override string ToString ( )
{
return "Entitlements '" + Encoding . ASCII . GetString ( MyData , 0 , MyData . Length ) + "'" ;
}
public static EntitlementsBlob Create ( string EntitlementsText )
{
EntitlementsBlob Result = new EntitlementsBlob ( ) ;
Result . MyData = Encoding . UTF8 . GetBytes ( EntitlementsText ) ;
return Result ;
}
}
2021-10-12 21:21:22 -04:00
/// <summary>
/// Blob for magic CSMAGIC_ENTITLEMENTS_DER
/// </summary>
public class EntitlementsDerBlob : OpaqueBlob
{
public EntitlementsDerBlob ( )
{
MyMagic = CSMAGIC_ENTITLEMENTS_DER ;
}
public override string ToString ( )
{
return "EntitlementsDER '" + AsnFormatter . BinaryToString ( MyData ) + "'" ;
}
public static EntitlementsDerBlob Create ( string EntitlementsXmlText )
{
EntitlementsDerBlob Result = new EntitlementsDerBlob ( ) ;
XmlDocument doc = new XmlDocument ( ) ;
doc . LoadXml ( EntitlementsXmlText ) ;
XmlNode xmlNode = doc . FirstChild ;
// eat tokens until we see a start 'plist' element.
while ( xmlNode ! = null & & ( xmlNode . NodeType ! = XmlNodeType . Element | | xmlNode . Name ! = "plist" ) )
{
xmlNode = xmlNode . NextSibling ;
}
Asn1Builder SetBuilder = new Asn1Builder ( ) ;
if ( xmlNode ! = null )
{
// dict node
xmlNode = xmlNode . FirstChild ;
if ( xmlNode ! = null & & xmlNode . NodeType = = XmlNodeType . Element & & xmlNode . Name = = "dict" )
{
for ( int i = 0 ; i < xmlNode . ChildNodes . Count ; i + + )
{
if ( xmlNode . ChildNodes [ i ] . Name = = "key" )
{
Asn1Builder KeyValueBuilder = new Asn1Builder ( ) ;
KeyValueBuilder . AddUTF8String ( xmlNode . ChildNodes [ i ] . InnerText ) ;
i + + ;
if ( i > = xmlNode . ChildNodes . Count )
{
break ;
}
if ( xmlNode . ChildNodes [ i ] . Name = = "string" )
{
KeyValueBuilder . AddUTF8String ( xmlNode . ChildNodes [ i ] . InnerText ) ;
}
else if ( xmlNode . ChildNodes [ i ] . Name = = "true" )
{
KeyValueBuilder . AddBoolean ( true ) ;
}
else if ( xmlNode . ChildNodes [ i ] . Name = = "false" )
{
KeyValueBuilder . AddBoolean ( false ) ;
}
else if ( xmlNode . ChildNodes [ i ] . Name = = "array" )
{
Asn1Builder ArrayBuilder = new Asn1Builder ( ) ;
foreach ( XmlNode child in xmlNode . ChildNodes [ i ] . ChildNodes )
{
if ( child . Name = = "string" )
{
ArrayBuilder . AddUTF8String ( child . InnerText ) ;
}
else if ( child . Name = = "true" )
{
ArrayBuilder . AddBoolean ( true ) ;
}
else if ( child . Name = = "false" )
{
ArrayBuilder . AddBoolean ( false ) ;
}
}
KeyValueBuilder . AddDerData ( ArrayBuilder . GetEncoded ( ) ) ;
}
SetBuilder . AddDerData ( KeyValueBuilder . GetEncoded ( ) ) ;
}
}
}
}
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( "Generated Entitlements DER as {0}" , AsnFormatter . BinaryToString ( SetBuilder . GetEncoded ( 0x31 ) ) ) ;
}
Result . MyData = SetBuilder . GetEncoded ( 0x31 ) ;
return Result ;
}
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Blob for magic CSMAGIC_REQUIREMENTS_TABLE
/// http://www.opensource.apple.com/source/libsecurity_codesigning/libsecurity_codesigning-36885/lib/requirement.h
///
/// Currently does not attempt to understand or parse the individual requirements and treats them as opaque data
/// </summary>
public class RequirementsBlob : SuperBlob
{
public RequirementsBlob ( )
{
MyMagic = CSMAGIC_REQUIREMENTS_TABLE ;
}
// Valid kinds of hosts that can run the executable
const int kSecHostRequirementType = 1 ;
// Valid guests the executable can run
const int kSecGuestRequirementType = 2 ;
// Explicit requirements
const int kSecDesignatedRequirementType = 3 ;
// Libraries we can link against
const int kSecLibraryRequirementType = 4 ;
public static RequirementsBlob CreateEmpty ( )
{
return new RequirementsBlob ( ) ;
}
protected string KeyToString ( int Key )
{
switch ( Key )
{
case kSecHostRequirementType :
return "kSecHostRequirementType" ;
case kSecGuestRequirementType :
return "kSecGuestRequirementType" ;
case kSecDesignatedRequirementType :
return "kSecDesignatedRequirementType" ;
case kSecLibraryRequirementType :
return "kSecLibraryRequirementType" ;
default :
return String . Format ( "UnknownKeyType_{0}" , Key ) ;
}
}
public override string ToString ( )
{
string Result = "Requirements table:\n" ;
foreach ( KeyValuePair < UInt32 , AbstractBlob > KVP in Table . Slots )
{
Result + = String . Format ( " Requirement[Type: {0}, Magic: 0x{1:X}] = {2}\n" ,
KeyToString ( ( int ) KVP . Key ) ,
KVP . Value . MyMagic , KVP . Value . ToString ( ) ) ;
}
return Result ;
}
}
2016-02-19 13:49:13 -05:00
public class RequirementBlob : OpaqueBlob
{
2016-03-07 20:55:29 -05:00
const UInt32 kOpUnknown = 1000 ;
const UInt32 kOpIdent = 2 ;
const UInt32 kOpAnchorHash = 4 ;
const UInt32 kOpAnd = 6 ;
const UInt32 kOpCertField = 11 ;
const UInt32 kOpCertGeneric = 14 ;
const UInt32 kOpGenericAnchor = 15 ;
public class ExpressionOp
{
public UInt32 OpVal = kOpUnknown ;
public ExpressionOp ( ) { }
public ExpressionOp ( UInt32 InOpVal )
{
OpVal = InOpVal ;
}
public virtual void ReadData ( ReadingContext SR )
{ }
public virtual void WriteData ( WritingContext SW )
{
SW . Write ( OpVal ) ;
}
public virtual void UpdateCertificateAndBundle ( string CertificateName , string BundleIdentifier )
{ }
static public ExpressionOp ReadOperand ( ReadingContext SR )
{
UInt32 OpVal = SR . ReadUInt32 ( ) ;
ExpressionOp Op = null ;
switch ( OpVal )
{
case kOpAnd :
Op = new AndOp ( ) ;
break ;
case kOpIdent :
Op = new IdentOp ( ) ;
break ;
case kOpGenericAnchor :
Op = new ExpressionOp ( OpVal ) ;
break ;
case kOpCertField :
Op = new CertFieldOp ( ) ;
break ;
case kOpCertGeneric :
Op = new CertGenericOp ( ) ;
break ;
case kOpAnchorHash :
Op = new AnchorHashOp ( ) ;
break ;
default :
throw new Exception ( "Unknown Expression Operand: " + OpVal . ToString ( ) ) ;
}
Op . ReadData ( SR ) ;
return Op ;
}
} ;
class AndOp : ExpressionOp
{
public ExpressionOp Op1 ;
public ExpressionOp Op2 ;
public AndOp ( )
{
OpVal = kOpAnd ;
}
public override void ReadData ( ReadingContext SR )
{
Op1 = ExpressionOp . ReadOperand ( SR ) ;
Op2 = ExpressionOp . ReadOperand ( SR ) ;
}
public override void WriteData ( WritingContext SW )
{
base . WriteData ( SW ) ;
Op1 . WriteData ( SW ) ;
Op2 . WriteData ( SW ) ;
}
public override void UpdateCertificateAndBundle ( string CertificateName , string BundleIdentifier )
{
Op1 . UpdateCertificateAndBundle ( CertificateName , BundleIdentifier ) ;
Op2 . UpdateCertificateAndBundle ( CertificateName , BundleIdentifier ) ;
}
} ;
class IdentOp : ExpressionOp
{
public string BundleIdentifier ;
public IdentOp ( )
{
OpVal = kOpIdent ;
}
public override void ReadData ( ReadingContext SR )
{
UInt32 Count = SR . ReadUInt32 ( ) ;
BundleIdentifier = SR . ReadFixedASCII ( ( int ) Count ) ;
Count = 4 - Count % 4 ;
if ( Count > 0 & & Count < 4 )
SR . ReadBytes ( Count ) ;
}
public override void WriteData ( WritingContext SW )
{
base . WriteData ( SW ) ;
SW . Write ( BundleIdentifier . Length ) ; // bundle identifier length
SW . WriteFixedASCII ( BundleIdentifier , BundleIdentifier . Length ) ; // bundle identifier string
int Count = 4 - BundleIdentifier . Length % 4 ; // may need to pad to alignment of 4 bytes
if ( Count > 0 & & Count < 4 )
SW . WriteZeros ( Count ) ;
}
public override void UpdateCertificateAndBundle ( string InCertificateName , string InBundleIdentifier )
{
BundleIdentifier = InBundleIdentifier ;
}
} ;
class CertFieldOp : ExpressionOp
{
public int CertificateIndex = 0 ;
public string FieldName = "subject.CN" ;
struct MatchSuffix
{
public UInt32 MatchOp ;
public string CertificateName ;
} ;
MatchSuffix MatchOp ;
public CertFieldOp ( )
{
OpVal = kOpCertField ;
Copying //UE4/Release-Staging-4.11 to //UE4/Main (Source: //UE4/Release-Staging-4.11 @ 2941426, //UE4/Release-4.11 @ 2927265)
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2910079 on 2016/03/15 by Taizyd.Korambayil
#jira UE-28293 Reworded some Sentences
Change 2910157 on 2016/03/15 by Taizyd.Korambayil
#jira UE-28240 Rebuilt Lighting for Sanctuary
Change 2910317 on 2016/03/15 by Ben.Marsh
Fix crash trying to print out a message explaining that you need to install the Visual Studio 2015 toolchain, if the Visual Studio 2015 toolchain is not installed!
Change 2910425 on 2016/03/15 by Ori.Cohen
Fix crash and incorrect behavior when setting physical material on a welded body.
#JIRA UE-28399
#rb Marc.Audy
Change 2910525 on 2016/03/15 by Ori.Cohen
Fix player capsule not spawning at the right place due to float precision issues.
#JIRA UE-28438
#rb Zak.Middleton
Change 2910595 on 2016/03/15 by Chris.Babcock
Fixed issue with missing event location paired with IE_Pressed if IE_DoubleClick generated
#jira UE-28051
#ue4
#codereview Marc.Audy
Change 2911442 on 2016/03/16 by Andrew.Rodham
Sequencer: Fixed frame grabbers where hardware mapped surfaces to memory of a different stride
#jira UE-28434
Change 2911596 on 2016/03/16 by andrew.porter
Test content for blueprint vertex painting
#jira UE-24473
Change 2911860 on 2016/03/16 by Jamie.Dale
Allowed SViewport to (once again) be able to use non-pre-multiplied alpha blending
SViewport now has an PreMultipliedAlpha argument (default true), which can control whether to use pre-multiplied alpha when blending is enabled (blending is disabled by default). Note: This is a change in behavior from 4.10, as non-pre-multiplied alpha blending used to be the default, but pre-multiplied alpha blending better supports the pipeline used through Slate.
This change also cleans up the use of bool parameters in the FSlateDrawElement::MakeX functions to control the render behavior, instead favoring use of advanced ESlateDrawEffect flags.
API Breaking Changes
- FSlateDrawElement::MakeGradient no longer takes a bInGammaCorrect bool, instead pass ESlateDrawEffect::NoGamma as part of InDrawEffects to disable gamma correction.
- FSlateDrawElement::MakeViewport no longer takes a bInGammaCorrect bool, instead pass ESlateDrawEffect::NoGamma as part of InDrawEffects to disable gamma correction.
- FSlateDrawElement::MakeViewport no longer takes a bInAllowBlending bool, instead pass ESlateDrawEffect::NoBlending as part of InDrawEffects to disable blending.
#jira UE-26797
Change 2912345 on 2016/03/16 by Olaf.Piesche
Removing the check that causes UE-28441, duplicating beam type data module from highest LOD in Cascade causes crash. The beam data module is the only one that explicitly checks to make sure it's always shared across LOD levels; there's no obvious reasons why duplicating beam data modules shouldn't be possible.
#codereview simon.tovey
#jira UE-28441
Change 2912526 on 2016/03/16 by Steve.Robb
Fix uninitialized variables.
#codereview robert.manuszewski
#jira UE-28391
Change 2913114 on 2016/03/17 by Steve.Robb
Fixed some private properties which caused UHT errors.
#codereview robert.manuszewski
#jira UE-28059
Change 2913295 on 2016/03/17 by Richard.TalbotWatkin
Replicated from Dev-Editor CL 2913224
Disallow assets from being deleted if PIE is active. This prevents various troubles which can occur when PIE is referencing asset objects.
#jira UE-12387 - [CrashReport] Crash when deleting assets needed for template
#RB Nick.Darnell, Frank.Fella
Change 2913310 on 2016/03/17 by Nick.Shin
merging from //UE4/Dev-Platform to //UE4/Release-4.11
--- original commit CL: #2913300 message ---
corrected VS 2015 websocket lib to look at the right offset
it is currently a high risk change to just update the libwebsocket wholesale for release-4.11.
this change is the most minimum invasive change with a lot of deep analysis (details will be put in jira: # UEPLAT-1221).
this fix will also be pushed up to release-4.11
#jira UE-22166 - HTML5 Cook on the fly will launch and then close browser
#jira UE-22513 - HTTP Network File System crashes randomly.
#jira UE-28003 - Fail to QuickLaunch HTML5 through UnrealFrontEnd
Change 2913593 on 2016/03/17 by Mark.Satterthwaite
For non-debug builds silence the warning about no deth/stencil when shader writes to depth in MetalRHI - the RHI implementation will create a temporary D/S buffer to cope but really this needs to be properly addressed elsewhere.
#jira UE-28491
Change 2913655 on 2016/03/17 by Taizyd.Korambayil
#jira UE-28492 Rebuilt Lighting For the Samples Listed
Change 2914025 on 2016/03/17 by Olaf.Piesche
Make sure ST primitives are added to NST draw list if in shader complexity mode
#codereview simon.tovey
#jira UE-28471
Change 2914027 on 2016/03/17 by Nick.Shin
[CL 2941462 by Ben Marsh in Main branch]
2016-04-12 17:04:39 -04:00
MatchOp . MatchOp = kMatchEqual ;
2016-03-07 20:55:29 -05:00
}
public override void ReadData ( ReadingContext SR )
{
CertificateIndex = ( int ) SR . ReadUInt32 ( ) ; // index in the mobile provision certificate list (always 0 for now)
UInt32 Count = SR . ReadUInt32 ( ) ;
FieldName = SR . ReadFixedASCII ( ( int ) Count ) ;
Count = 4 - Count % 4 ;
if ( Count > 0 & & Count < 4 )
SR . ReadBytes ( Count ) ;
MatchOp = new MatchSuffix ( ) ;
MatchOp . MatchOp = SR . ReadUInt32 ( ) ; // must equal
Count = SR . ReadUInt32 ( ) ;
MatchOp . CertificateName = SR . ReadFixedASCII ( ( int ) Count ) ;
Count = 4 - Count % 4 ;
if ( Count > 0 & & Count < 4 )
SR . ReadBytes ( Count ) ;
}
public override void WriteData ( WritingContext SW )
{
base . WriteData ( SW ) ;
SW . Write ( CertificateIndex ) ; // index in the mobile provision certificate list (always 0 for now)
SW . Write ( FieldName . Length ) ; // field name length
SW . WriteFixedASCII ( FieldName , FieldName . Length ) ; // field name to match
int Count = 4 - FieldName . Length % 4 ; // may need to pad to alignment of 4 bytes
if ( Count > 0 & & Count < 4 )
SW . WriteZeros ( Count ) ;
SW . Write ( MatchOp . MatchOp ) ; // must equal
SW . Write ( MatchOp . CertificateName . Length ) ; // length of certficate name
SW . WriteFixedASCII ( MatchOp . CertificateName , MatchOp . CertificateName . Length ) ; // certificate name to match
Count = 4 - MatchOp . CertificateName . Length % 4 ; // may need to pad to alignment of 4 bytes
if ( Count > 0 & & Count < 4 )
SW . WriteZeros ( Count ) ;
}
public override void UpdateCertificateAndBundle ( string InCertificateName , string InBundleIdentifier )
{
MatchOp . CertificateName = InCertificateName ;
}
} ;
class CertGenericOp : ExpressionOp
{
public int OIDIndex = 1 ;
byte [ ] OID = new byte [ ] { 0x2A , 0x86 , 0x48 , 0x86 , 0xF7 , 0x63 , 0x64 , 0x06 , 0x02 , 0x01 } ;
struct MatchSuffix
{
public UInt32 MatchOp ;
} ;
MatchSuffix MatchOp ;
public CertGenericOp ( )
{
OpVal = kOpCertGeneric ;
Copying //UE4/Release-Staging-4.11 to //UE4/Main (Source: //UE4/Release-Staging-4.11 @ 2941426, //UE4/Release-4.11 @ 2927265)
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2910079 on 2016/03/15 by Taizyd.Korambayil
#jira UE-28293 Reworded some Sentences
Change 2910157 on 2016/03/15 by Taizyd.Korambayil
#jira UE-28240 Rebuilt Lighting for Sanctuary
Change 2910317 on 2016/03/15 by Ben.Marsh
Fix crash trying to print out a message explaining that you need to install the Visual Studio 2015 toolchain, if the Visual Studio 2015 toolchain is not installed!
Change 2910425 on 2016/03/15 by Ori.Cohen
Fix crash and incorrect behavior when setting physical material on a welded body.
#JIRA UE-28399
#rb Marc.Audy
Change 2910525 on 2016/03/15 by Ori.Cohen
Fix player capsule not spawning at the right place due to float precision issues.
#JIRA UE-28438
#rb Zak.Middleton
Change 2910595 on 2016/03/15 by Chris.Babcock
Fixed issue with missing event location paired with IE_Pressed if IE_DoubleClick generated
#jira UE-28051
#ue4
#codereview Marc.Audy
Change 2911442 on 2016/03/16 by Andrew.Rodham
Sequencer: Fixed frame grabbers where hardware mapped surfaces to memory of a different stride
#jira UE-28434
Change 2911596 on 2016/03/16 by andrew.porter
Test content for blueprint vertex painting
#jira UE-24473
Change 2911860 on 2016/03/16 by Jamie.Dale
Allowed SViewport to (once again) be able to use non-pre-multiplied alpha blending
SViewport now has an PreMultipliedAlpha argument (default true), which can control whether to use pre-multiplied alpha when blending is enabled (blending is disabled by default). Note: This is a change in behavior from 4.10, as non-pre-multiplied alpha blending used to be the default, but pre-multiplied alpha blending better supports the pipeline used through Slate.
This change also cleans up the use of bool parameters in the FSlateDrawElement::MakeX functions to control the render behavior, instead favoring use of advanced ESlateDrawEffect flags.
API Breaking Changes
- FSlateDrawElement::MakeGradient no longer takes a bInGammaCorrect bool, instead pass ESlateDrawEffect::NoGamma as part of InDrawEffects to disable gamma correction.
- FSlateDrawElement::MakeViewport no longer takes a bInGammaCorrect bool, instead pass ESlateDrawEffect::NoGamma as part of InDrawEffects to disable gamma correction.
- FSlateDrawElement::MakeViewport no longer takes a bInAllowBlending bool, instead pass ESlateDrawEffect::NoBlending as part of InDrawEffects to disable blending.
#jira UE-26797
Change 2912345 on 2016/03/16 by Olaf.Piesche
Removing the check that causes UE-28441, duplicating beam type data module from highest LOD in Cascade causes crash. The beam data module is the only one that explicitly checks to make sure it's always shared across LOD levels; there's no obvious reasons why duplicating beam data modules shouldn't be possible.
#codereview simon.tovey
#jira UE-28441
Change 2912526 on 2016/03/16 by Steve.Robb
Fix uninitialized variables.
#codereview robert.manuszewski
#jira UE-28391
Change 2913114 on 2016/03/17 by Steve.Robb
Fixed some private properties which caused UHT errors.
#codereview robert.manuszewski
#jira UE-28059
Change 2913295 on 2016/03/17 by Richard.TalbotWatkin
Replicated from Dev-Editor CL 2913224
Disallow assets from being deleted if PIE is active. This prevents various troubles which can occur when PIE is referencing asset objects.
#jira UE-12387 - [CrashReport] Crash when deleting assets needed for template
#RB Nick.Darnell, Frank.Fella
Change 2913310 on 2016/03/17 by Nick.Shin
merging from //UE4/Dev-Platform to //UE4/Release-4.11
--- original commit CL: #2913300 message ---
corrected VS 2015 websocket lib to look at the right offset
it is currently a high risk change to just update the libwebsocket wholesale for release-4.11.
this change is the most minimum invasive change with a lot of deep analysis (details will be put in jira: # UEPLAT-1221).
this fix will also be pushed up to release-4.11
#jira UE-22166 - HTML5 Cook on the fly will launch and then close browser
#jira UE-22513 - HTTP Network File System crashes randomly.
#jira UE-28003 - Fail to QuickLaunch HTML5 through UnrealFrontEnd
Change 2913593 on 2016/03/17 by Mark.Satterthwaite
For non-debug builds silence the warning about no deth/stencil when shader writes to depth in MetalRHI - the RHI implementation will create a temporary D/S buffer to cope but really this needs to be properly addressed elsewhere.
#jira UE-28491
Change 2913655 on 2016/03/17 by Taizyd.Korambayil
#jira UE-28492 Rebuilt Lighting For the Samples Listed
Change 2914025 on 2016/03/17 by Olaf.Piesche
Make sure ST primitives are added to NST draw list if in shader complexity mode
#codereview simon.tovey
#jira UE-28471
Change 2914027 on 2016/03/17 by Nick.Shin
[CL 2941462 by Ben Marsh in Main branch]
2016-04-12 17:04:39 -04:00
MatchOp . MatchOp = kMatchExists ;
2016-03-07 20:55:29 -05:00
}
public override void ReadData ( ReadingContext SR )
{
OIDIndex = ( int ) SR . ReadUInt32 ( ) ; // index of the OID value (always 1)
UInt32 Count = SR . ReadUInt32 ( ) ;
OID = SR . ReadBytes ( ( int ) Count ) ;
Count = 4 - Count % 4 ;
if ( Count > 0 & & Count < 4 )
SR . ReadBytes ( Count ) ;
MatchOp . MatchOp = SR . ReadUInt32 ( ) ; // OID must exist
}
public override void WriteData ( WritingContext SW )
{
base . WriteData ( SW ) ;
SW . Write ( OIDIndex ) ; // index of the OID value (always 1)
SW . Write ( OID . Length ) ; // length of OID
SW . Write ( OID ) ; // OID to match
int Count = 4 - OID . Length % 4 ; // may need to pad to alignment of 4 bytes
if ( Count > 0 & & Count < 4 )
SW . WriteZeros ( Count ) ;
// may need to pad to alignment of 4 bytes
SW . Write ( MatchOp . MatchOp ) ; // OID must exist
}
} ;
class AnchorHashOp : ExpressionOp
{
byte [ ] Hash = new byte [ ] { 0x2A , 0x86 , 0x48 , 0x86 , 0xF7 , 0x63 , 0x64 , 0x06 , 0x02 , 0x01 } ;
int CertificateIndex = - 1 ;
public AnchorHashOp ( )
{
OpVal = kOpAnchorHash ;
}
public override void ReadData ( ReadingContext SR )
{
CertificateIndex = ( int ) SR . ReadUInt32 ( ) ;
UInt32 Count = SR . ReadUInt32 ( ) ;
Hash = SR . ReadBytes ( ( int ) Count ) ;
Count = 4 - Count % 4 ;
if ( Count > 0 & & Count < 4 )
SR . ReadBytes ( Count ) ;
}
public override void WriteData ( WritingContext SW )
{
base . WriteData ( SW ) ;
SW . Write ( CertificateIndex ) ; // index of the OID value (always 1)
SW . Write ( Hash . Length ) ; // length of OID
SW . Write ( Hash ) ; // OID to match
int Count = 4 - Hash . Length % 4 ; // may need to pad to alignment of 4 bytes
if ( Count > 0 & & Count < 4 )
SW . WriteZeros ( Count ) ;
}
} ;
2016-02-19 13:49:13 -05:00
const int kReqExpression = 1 ;
const int kMatchExists = 0 ;
const int kMatchEqual = 1 ;
string BundleIdentifier = "" ;
string CertificateName = "" ;
2016-03-07 20:55:29 -05:00
public ExpressionOp Expression = null ;
2016-02-19 13:49:13 -05:00
public RequirementBlob ( )
{
MyMagic = CSMAGIC_REQUIREMENT ;
}
2016-03-07 20:55:29 -05:00
public static RequirementBlob CreateFromCertificate ( X509Certificate2 SigningCert , string Bundle , ExpressionOp OldReq = null )
2016-02-19 13:49:13 -05:00
{
RequirementBlob Blob = new RequirementBlob ( ) ;
2016-03-07 20:55:29 -05:00
Blob . InitializeFromCert ( SigningCert , Bundle , OldReq ) ;
2016-02-19 13:49:13 -05:00
return Blob ;
}
2016-03-07 20:55:29 -05:00
protected void InitializeFromCert ( X509Certificate2 SigningCert , string Bundle , ExpressionOp OldReq = null )
2016-02-19 13:49:13 -05:00
{
BundleIdentifier = Bundle ;
2016-03-07 20:55:29 -05:00
int StartIndex = SigningCert . SubjectName . Name . IndexOf ( "CN=" ) ;
int EndIndex = - 1 ;
CertificateName = "" ;
if ( StartIndex > - 1 )
{
// find the next attribute
StartIndex + = 3 ;
char SearchChar = ',' ;
if ( SigningCert . SubjectName . Name [ StartIndex ] = = '\"' )
{
// quotes are around the string because of special characters
StartIndex + + ;
SearchChar = '\"' ;
}
EndIndex = SigningCert . SubjectName . Name . IndexOf ( SearchChar , StartIndex ) ;
if ( EndIndex = = - 1 )
{
// must be at the end, so go to the end
EndIndex = SigningCert . SubjectName . Name . Length ;
if ( SearchChar = = '\"' )
{
EndIndex - - ;
}
}
// get the string
CertificateName = SigningCert . SubjectName . Name . Substring ( StartIndex , EndIndex - StartIndex ) ;
}
if ( string . IsNullOrEmpty ( CertificateName ) )
{
2019-09-17 04:21:21 -04:00
CertificateName = CryptoAdapter . GetFriendlyNameFromCert ( SigningCert ) ;
2016-03-07 20:55:29 -05:00
}
Copying //UE4/Release-Staging-4.11 to //UE4/Main (Source: //UE4/Release-Staging-4.11 @ 2941426, //UE4/Release-4.11 @ 2927265)
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2910079 on 2016/03/15 by Taizyd.Korambayil
#jira UE-28293 Reworded some Sentences
Change 2910157 on 2016/03/15 by Taizyd.Korambayil
#jira UE-28240 Rebuilt Lighting for Sanctuary
Change 2910317 on 2016/03/15 by Ben.Marsh
Fix crash trying to print out a message explaining that you need to install the Visual Studio 2015 toolchain, if the Visual Studio 2015 toolchain is not installed!
Change 2910425 on 2016/03/15 by Ori.Cohen
Fix crash and incorrect behavior when setting physical material on a welded body.
#JIRA UE-28399
#rb Marc.Audy
Change 2910525 on 2016/03/15 by Ori.Cohen
Fix player capsule not spawning at the right place due to float precision issues.
#JIRA UE-28438
#rb Zak.Middleton
Change 2910595 on 2016/03/15 by Chris.Babcock
Fixed issue with missing event location paired with IE_Pressed if IE_DoubleClick generated
#jira UE-28051
#ue4
#codereview Marc.Audy
Change 2911442 on 2016/03/16 by Andrew.Rodham
Sequencer: Fixed frame grabbers where hardware mapped surfaces to memory of a different stride
#jira UE-28434
Change 2911596 on 2016/03/16 by andrew.porter
Test content for blueprint vertex painting
#jira UE-24473
Change 2911860 on 2016/03/16 by Jamie.Dale
Allowed SViewport to (once again) be able to use non-pre-multiplied alpha blending
SViewport now has an PreMultipliedAlpha argument (default true), which can control whether to use pre-multiplied alpha when blending is enabled (blending is disabled by default). Note: This is a change in behavior from 4.10, as non-pre-multiplied alpha blending used to be the default, but pre-multiplied alpha blending better supports the pipeline used through Slate.
This change also cleans up the use of bool parameters in the FSlateDrawElement::MakeX functions to control the render behavior, instead favoring use of advanced ESlateDrawEffect flags.
API Breaking Changes
- FSlateDrawElement::MakeGradient no longer takes a bInGammaCorrect bool, instead pass ESlateDrawEffect::NoGamma as part of InDrawEffects to disable gamma correction.
- FSlateDrawElement::MakeViewport no longer takes a bInGammaCorrect bool, instead pass ESlateDrawEffect::NoGamma as part of InDrawEffects to disable gamma correction.
- FSlateDrawElement::MakeViewport no longer takes a bInAllowBlending bool, instead pass ESlateDrawEffect::NoBlending as part of InDrawEffects to disable blending.
#jira UE-26797
Change 2912345 on 2016/03/16 by Olaf.Piesche
Removing the check that causes UE-28441, duplicating beam type data module from highest LOD in Cascade causes crash. The beam data module is the only one that explicitly checks to make sure it's always shared across LOD levels; there's no obvious reasons why duplicating beam data modules shouldn't be possible.
#codereview simon.tovey
#jira UE-28441
Change 2912526 on 2016/03/16 by Steve.Robb
Fix uninitialized variables.
#codereview robert.manuszewski
#jira UE-28391
Change 2913114 on 2016/03/17 by Steve.Robb
Fixed some private properties which caused UHT errors.
#codereview robert.manuszewski
#jira UE-28059
Change 2913295 on 2016/03/17 by Richard.TalbotWatkin
Replicated from Dev-Editor CL 2913224
Disallow assets from being deleted if PIE is active. This prevents various troubles which can occur when PIE is referencing asset objects.
#jira UE-12387 - [CrashReport] Crash when deleting assets needed for template
#RB Nick.Darnell, Frank.Fella
Change 2913310 on 2016/03/17 by Nick.Shin
merging from //UE4/Dev-Platform to //UE4/Release-4.11
--- original commit CL: #2913300 message ---
corrected VS 2015 websocket lib to look at the right offset
it is currently a high risk change to just update the libwebsocket wholesale for release-4.11.
this change is the most minimum invasive change with a lot of deep analysis (details will be put in jira: # UEPLAT-1221).
this fix will also be pushed up to release-4.11
#jira UE-22166 - HTML5 Cook on the fly will launch and then close browser
#jira UE-22513 - HTTP Network File System crashes randomly.
#jira UE-28003 - Fail to QuickLaunch HTML5 through UnrealFrontEnd
Change 2913593 on 2016/03/17 by Mark.Satterthwaite
For non-debug builds silence the warning about no deth/stencil when shader writes to depth in MetalRHI - the RHI implementation will create a temporary D/S buffer to cope but really this needs to be properly addressed elsewhere.
#jira UE-28491
Change 2913655 on 2016/03/17 by Taizyd.Korambayil
#jira UE-28492 Rebuilt Lighting For the Samples Listed
Change 2914025 on 2016/03/17 by Olaf.Piesche
Make sure ST primitives are added to NST draw list if in shader complexity mode
#codereview simon.tovey
#jira UE-28471
Change 2914027 on 2016/03/17 by Nick.Shin
[CL 2941462 by Ben Marsh in Main branch]
2016-04-12 17:04:39 -04:00
// always use the new requirements when initializing from cert
Expression = new AndOp ( ) ;
( Expression as AndOp ) . Op1 = new IdentOp ( ) ;
( ( Expression as AndOp ) . Op1 as IdentOp ) . BundleIdentifier = BundleIdentifier ;
( Expression as AndOp ) . Op2 = new AndOp ( ) ;
( ( Expression as AndOp ) . Op2 as AndOp ) . Op1 = new ExpressionOp ( kOpGenericAnchor ) ;
( ( Expression as AndOp ) . Op2 as AndOp ) . Op2 = new AndOp ( ) ;
( ( ( Expression as AndOp ) . Op2 as AndOp ) . Op2 as AndOp ) . Op1 = new CertFieldOp ( ) ;
( ( ( Expression as AndOp ) . Op2 as AndOp ) . Op2 as AndOp ) . Op2 = new CertGenericOp ( ) ;
2016-02-19 13:49:13 -05:00
}
protected override void PackageData ( WritingContext SW )
{
2016-03-07 20:55:29 -05:00
// update all of the read expressions with the certificate name and bundle identifier
Expression . UpdateCertificateAndBundle ( CertificateName , BundleIdentifier ) ;
SW . Write ( kReqExpression ) ;
Expression . WriteData ( SW ) ;
2016-02-19 13:49:13 -05:00
}
protected override void UnpackageData ( ReadingContext SR , UInt32 Length )
{
2016-03-07 20:55:29 -05:00
if ( Length > 0 )
{
UInt32 ExpressionVal = SR . ReadUInt32 ( ) ;
if ( ExpressionVal = = kReqExpression )
{
Expression = ExpressionOp . ReadOperand ( SR ) ;
}
}
2016-02-19 13:49:13 -05:00
}
}
2014-03-14 14:13:41 -04:00
public class CodeSigningTableBlob : SuperBlob
{
public CodeSigningTableBlob ( )
{
MyMagic = CSMAGIC_EMBEDDED_SIGNATURE ;
}
public static CodeSigningTableBlob Create ( )
{
CodeSigningTableBlob Blob = new CodeSigningTableBlob ( ) ;
return Blob ;
}
}
public abstract class AbstractBlob
{
// Key for CodeDirectory blob in superblob
public const UInt32 CSSLOT_CODEDIRECTORY = 0 ;
// Requirements blob
public const UInt32 CSMAGIC_REQUIREMENT = 0xFADE0C00 ;
// Internal requirements
public const UInt32 CSMAGIC_REQUIREMENTS_TABLE = 0xFADE0C01 ;
// Code directory blob (should be in a slot with key = CSSLOT_CODEDIRECTORY)
public const UInt32 CSMAGIC_CODEDIRECTORY = 0xFADE0C02 ;
// Superblob of all signature data (code directory, actual signature, etc...)
public const UInt32 CSMAGIC_EMBEDDED_SIGNATURE = 0xFADE0CC0 ;
// Entitlements
public const UInt32 CSMAGIC_ENTITLEMENTS = 0xFADE7171 ;
2021-10-12 21:21:22 -04:00
// Entitlements DER
public const UInt32 CSMAGIC_ENTITLEMENTS_DER = 0xFADE7172 ;
2014-03-14 14:13:41 -04:00
// Actual signature blob
public const UInt32 CSMAGIC_CODEDIR_SIGNATURE = 0xFADE0B01 ;
public UInt32 MyMagic ;
protected abstract void PackageData ( WritingContext SW ) ;
protected abstract void UnpackageData ( ReadingContext SR , UInt32 Length ) ;
public static AbstractBlob CreateFromStream ( ReadingContext SR )
{
// Read the magic and length (common to all blobs)
UInt32 Magic = SR . ReadUInt32 ( ) ;
UInt32 Length = SR . ReadUInt32 ( ) ;
AbstractBlob Result ;
switch ( Magic )
{
case CSMAGIC_CODEDIRECTORY :
Result = new CodeDirectoryBlob ( ) ;
break ;
case CSMAGIC_CODEDIR_SIGNATURE :
Result = new CodeDirectorySignatureBlob ( ) ;
break ;
case CSMAGIC_ENTITLEMENTS :
Result = new EntitlementsBlob ( ) ;
break ;
2021-10-12 21:21:22 -04:00
case CSMAGIC_ENTITLEMENTS_DER :
Result = new EntitlementsDerBlob ( ) ;
break ;
2014-03-14 14:13:41 -04:00
case CSMAGIC_REQUIREMENTS_TABLE :
Result = new RequirementsBlob ( ) ;
break ;
case CSMAGIC_EMBEDDED_SIGNATURE :
Result = new CodeSigningTableBlob ( ) ;
break ;
case CSMAGIC_REQUIREMENT :
2016-02-19 13:49:13 -05:00
Result = new RequirementBlob ( ) ;
break ;
2014-03-14 14:13:41 -04:00
default :
Result = new OpaqueBlob ( ) ;
break ;
}
Result . MyMagic = Magic ;
Result . UnpackageData ( SR , Length ) ;
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( "[Read blob with magic 0x{0:X} and length={1}]\n{2}" , Magic , Length , Result . ToString ( ) ) ;
}
return Result ;
}
public void Write ( WritingContext SW )
{
SW . Write ( MyMagic ) ;
LengthFieldU32or64 Length = SW . WriteDeferredLength ( 4 , Bits . Num . _32 ) ;
PackageData ( SW ) ;
SW . CommitDeferredField ( Length ) ;
}
/// <summary>
/// Always uses big endian!!!
/// Converts a blob back into an array of bytes (does not work if there are any file-absolute offsets, as it serializes starting at 0 of a fake stream)
/// </summary>
public byte [ ] GetBlobBytes ( )
{
MemoryStream MemoryBuffer = new MemoryStream ( ) ;
BinaryWriter Writer = new BinaryWriter ( MemoryBuffer ) ;
WritingContext SW = new WritingContext ( Writer ) ;
SW . bStreamLittleEndian = false ;
Write ( SW ) ;
SW . CompleteWritingAndClose ( ) ;
return MemoryBuffer . ToArray ( ) ;
}
}
/// <summary>
///
/// </summary>
public class OpaqueBlob : AbstractBlob
{
protected byte [ ] MyData ;
protected override void UnpackageData ( ReadingContext SR , UInt32 Length )
{
MyData = SR . ReadBytes ( Length - ( 2 * sizeof ( UInt32 ) ) ) ;
}
protected override void PackageData ( WritingContext SW )
{
SW . Write ( MyData ) ;
}
public override string ToString ( )
{
return "Opaque blob" ; //'" + Encoding.ASCII.GetString(MyData, 0, MyData.Length) + "'";
}
}
public abstract class SuperBlob : AbstractBlob
{
protected BlobTable Table = new BlobTable ( ) ;
public AbstractBlob GetBlobByMagic ( UInt32 Magic )
{
foreach ( KeyValuePair < UInt32 , AbstractBlob > KVP in Table . Slots )
{
if ( KVP . Value . MyMagic = = Magic )
{
return KVP . Value ;
}
}
return null ;
}
public AbstractBlob GetBlobByKey ( UInt32 Key )
{
foreach ( KeyValuePair < UInt32 , AbstractBlob > KVP in Table . Slots )
{
if ( KVP . Key = = Key )
{
return KVP . Value ;
}
}
return null ;
}
protected override void UnpackageData ( ReadingContext SR , uint Length )
{
Table . Read ( SR ) ;
}
protected override void PackageData ( WritingContext SW )
{
Table . Write ( SW ) ;
}
public void Add ( UInt32 Key , AbstractBlob Value )
{
Table . Slots . Add ( new KeyValuePair < UInt32 , AbstractBlob > ( Key , Value ) ) ;
}
}
/// <summary>
/// References:
/// http://www.opensource.apple.com/source/libsecurity_utilities/libsecurity_utilities-36984/lib/blob.h
/// http://www.opensource.apple.com/source/libsecurity_utilities/libsecurity_utilities-38535/lib/superblob.h
/// http://www.opensource.apple.com/source/libsecurity_codesigning/libsecurity_codesigning-36885/lib/cscdefs.h
///
/// BlobTable:
/// U32 SlotCount
/// SerializedSlot Slots[Count]
///
/// SerializedSlot
/// KeyType Type
/// U32 Offset (relative to start of BlobTable)
///
/// </summary>
public class BlobTable
{
public List < KeyValuePair < UInt32 , AbstractBlob > > Slots = new List < KeyValuePair < UInt32 , AbstractBlob > > ( ) ;
public AbstractBlob GetByKey ( UInt32 Key )
{
foreach ( KeyValuePair < UInt32 , AbstractBlob > KVP in Slots )
{
if ( KVP . Key = = Key )
{
return KVP . Value ;
}
}
return null ;
}
/// <summary>
/// Reads in a data blob, preserving the stream read pointer (Offset is relative to the start of the underlying stream)
/// </summary>
AbstractBlob ReadBlob ( ReadingContext SR , long Offset )
{
SR . PushPositionAndJump ( Offset ) ;
AbstractBlob Result = AbstractBlob . CreateFromStream ( SR ) ;
SR . PopPosition ( ) ;
return Result ;
}
public void Write ( WritingContext SW )
{
// Magic (written in the outer)
// Length (written in the outer)
// SlotCount
// Slot SlotTable[SlotCount]
// Each slot is Key, Offset
// <Slot-referenced data>
//WritingContext.OffsetFieldU32or64[] Offsets = new WritingContext.OffsetFieldU32or64[Slots.Count];
long StartingPosition = SW . Position - ( 2 * sizeof ( UInt32 ) ) ;
// Start a phase for writing out the superblob data
SW . CreateNewPhase ( ) ;
// Write the slot table, queuing up the individual slot writes
SW . Write ( ( UInt32 ) Slots . Count ) ;
foreach ( KeyValuePair < UInt32 , AbstractBlob > Slot in Slots )
{
SW . Write ( Slot . Key ) ;
OffsetFieldU32or64 Offset = SW . WriteDeferredOffsetFrom ( StartingPosition , Bits . Num . _32 ) ;
KeyValuePair < UInt32 , AbstractBlob > LocalSlot = Slot ;
SW . CurrentPhase . PendingWrites . Enqueue ( delegate ( WritingContext Context )
{
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( "Writing a slot. Offset={0}, SlotData={1}" , Offset . WritePoint , LocalSlot . ToString ( ) ) ;
}
SW . CommitDeferredField ( Offset ) ;
LocalSlot . Value . Write ( Context ) ;
} ) ;
}
// Force evaluation of the slots
SW . ProcessEntirePhase ( ) ;
}
public void Read ( ReadingContext SR )
{
long BaseOffset = SR . Position - ( 2 * sizeof ( UInt32 ) ) ;
UInt32 SlotCount = SR . ReadUInt32 ( ) ;
for ( UInt32 i = 0 ; i < SlotCount ; + + i )
{
// Read a slot
UInt32 Key = SR . ReadUInt32 ( ) ;
UInt32 Offset = SR . ReadUInt32 ( ) ;
// Read it's associated blob
AbstractBlob Blob = ReadBlob ( SR , BaseOffset + Offset ) ;
// Add it to the slot table
Slots . Add ( new KeyValuePair < UInt32 , AbstractBlob > ( Key , Blob ) ) ;
}
}
}
public class CodeDirectorySignatureBlob : OpaqueBlob
{
public CodeDirectorySignatureBlob ( )
{
MyMagic = CSMAGIC_CODEDIR_SIGNATURE ;
}
public static CodeDirectorySignatureBlob Create ( )
{
return new CodeDirectorySignatureBlob ( ) ;
}
public void DebugCMSData ( byte [ ] Data , string Prefix )
{
SignedCms CMS = new SignedCms ( ) ;
CMS . Decode ( Data ) ;
int Version = CMS . Version ;
Console . WriteLine ( "CMS Blob {0} Info" , Prefix ) ;
Console . WriteLine ( " Version = {0}" , Version ) ;
foreach ( SignerInfo si in CMS . SignerInfos )
{
Console . WriteLine ( " SignerInfo {" ) ;
Console . WriteLine ( " Cert issued by {0}" , si . Certificate . Issuer ) ;
foreach ( CryptographicAttributeObject Attr in si . SignedAttributes )
{
foreach ( AsnEncodedData AttrData in Attr . Values )
{
Pkcs9SigningTime SignTime = AttrData as Pkcs9SigningTime ;
Pkcs9ContentType CType = AttrData as Pkcs9ContentType ;
Pkcs9MessageDigest Digest = AttrData as Pkcs9MessageDigest ;
if ( SignTime ! = null )
Console . WriteLine ( " SignedTime {0:O}" , SignTime . SigningTime ) ;
if ( CType ! = null )
Console . WriteLine ( " ContentType {0}" , CType . ContentType . FriendlyName ) ;
if ( Digest ! = null )
Console . WriteLine ( " MessageDigest h'{0}'" , BitConverter . ToString ( Digest . RawData ) . Replace ( "-" , "" ) ) ;
}
}
Console . WriteLine ( " }" ) ;
}
Console . WriteLine ( " Inner content: h'{0}'" , BitConverter . ToString ( CMS . ContentInfo . Content ) . Replace ( "-" , "" ) ) ;
}
/// <summary>
/// Populates this CMS blob with the data from signing a code directory
/// </summary>
public void SignCodeDirectory ( X509Certificate2 SigningCert , DateTime SigningTime , CodeDirectoryBlob CodeDirectory )
{
// Create a signer
CmsSigner Signer = new CmsSigner ( SigningCert ) ;
Signer . IncludeOption = X509IncludeOption . WholeChain ;
Signer . SignerIdentifierType = SubjectIdentifierType . IssuerAndSerialNumber ;
2016-02-19 13:49:13 -05:00
Signer . DigestAlgorithm = new Oid ( CryptoConfig . MapNameToOID ( "sha256" ) , "sha256" ) ;
2014-03-14 14:13:41 -04:00
// A Pkcs9ContentType and Pkcs9MessageDigest will automatically be added, and it fails to
// compute a signature if they are added manually, so only the signing time needs to be added
Signer . SignedAttributes . Add ( new Pkcs9SigningTime ( SigningTime ) ) ;
// Sign the data (in a detached manner, so only the digest of the CodeDirectory is
// stored in the CMS blob and not the whole CodeDirectory blob)
bool bDetached = true ;
bool bSilent = true ;
ContentInfo CodeDirContentInfo = new ContentInfo ( CodeDirectory . GetBlobBytes ( ) ) ;
SignedCms CMS = new SignedCms ( CodeDirContentInfo , bDetached ) ;
CMS . ComputeSignature ( Signer , bSilent ) ;
MyData = CMS . Encode ( ) ;
}
public override string ToString ( )
{
DebugCMSData ( this . MyData , "CMS blob ToString" ) ;
return "CMS Blob" ;
}
}
public class CodeDirectoryBlob : AbstractBlob
{
2016-03-07 20:55:29 -05:00
public const UInt32 cVersion2 = 0x20200 ;
2021-05-25 02:43:26 -04:00
public const UInt32 cVersion3 = 0x20400 ;
2016-03-07 20:55:29 -05:00
public UInt32 Version ;
2014-03-14 14:13:41 -04:00
UInt32 Flags ;
//UInt32 HashOffset;
byte [ ] Hashes ;
//UInt32 IdentifierStringOffset;
string Identifier ;
UInt32 SpecialSlotCount ;
UInt32 CodeSlotCount ;
UInt32 MainImageSignatureLimit ;
byte BytesPerHash ;
byte HashType ;
byte Spare1 ;
byte LogPageSize ;
UInt32 Spare2 ;
UInt32 ScatterCount ;
2016-02-19 13:49:13 -05:00
string Team ;
2021-05-25 02:43:26 -04:00
UInt32 Spare3 ;
UInt64 CodeLimit64 ;
UInt64 ExecSegBase ;
UInt64 ExecSegLimit ;
UInt64 ExecSegFlags ;
2014-03-14 14:13:41 -04:00
// Special slot index for Info.plist
public const int cdInfoSlot = 1 ;
// Special slot index for internal requirements
public const int cdRequirementsSlot = 2 ;
// Special slot index for the resource directory
public const int cdResourceDirSlot = 3 ;
// Special slot index for the application
public const int cdApplicationSlot = 4 ;
// Special slot index for embedded entitlements
public const int cdEntitlementSlot = 5 ;
2021-10-12 21:21:22 -04:00
// Special slot index 6, unknown - all 0's in practice
public const int cdUnknown6Slot = 6 ;
// Special slot index for embedded der entitlements
public const int cdDerEntitlementSlot = 7 ;
2014-03-14 14:13:41 -04:00
// Number of special slot indexes
2021-10-12 21:21:22 -04:00
public const int cdSlotMax = cdDerEntitlementSlot ;
2014-03-14 14:13:41 -04:00
/// <summary>
/// Hash provider (SHA1)
/// </summary>
2021-05-25 02:43:26 -04:00
protected SHA256CryptoServiceProvider HashProvider2 = new SHA256CryptoServiceProvider ( ) ;
2014-03-14 14:13:41 -04:00
protected override void UnpackageData ( ReadingContext SR , UInt32 Length )
{
long StartOfBlob = SR . Position - sizeof ( UInt32 ) * 2 ;
2021-10-12 21:21:22 -04:00
// References:
// https://llvm.org/doxygen/BinaryFormat_2MachO_8h_source.html
// https://github.com/apple/darwin-xnu/blob/main/osfmk/kern/cs_blobs.h
2014-03-14 14:13:41 -04:00
Version = SR . ReadUInt32 ( ) ;
Flags = SR . ReadUInt32 ( ) ;
UInt32 HashOffset = SR . ReadUInt32 ( ) ;
UInt32 IdentifierStringOffset = SR . ReadUInt32 ( ) ;
SpecialSlotCount = SR . ReadUInt32 ( ) ;
CodeSlotCount = SR . ReadUInt32 ( ) ;
MainImageSignatureLimit = SR . ReadUInt32 ( ) ;
BytesPerHash = SR . ReadByte ( ) ;
HashType = SR . ReadByte ( ) ;
Spare1 = SR . ReadByte ( ) ;
LogPageSize = SR . ReadByte ( ) ;
Spare2 = SR . ReadUInt32 ( ) ;
ScatterCount = SR . ReadUInt32 ( ) ;
2021-05-25 02:43:26 -04:00
UInt32 TeamStringOffset = SR . ReadUInt32 ( ) ;
Spare3 = SR . ReadUInt32 ( ) ;
CodeLimit64 = SR . ReadUInt64 ( ) ;
ExecSegBase = SR . ReadUInt64 ( ) ;
ExecSegLimit = SR . ReadUInt64 ( ) ;
ExecSegFlags = SR . ReadUInt64 ( ) ;
2014-03-14 14:13:41 -04:00
2021-05-25 02:43:26 -04:00
// Read the identifier string
SR . PushPositionAndJump ( StartOfBlob + IdentifierStringOffset ) ;
Identifier = SR . ReadASCIIZ ( ) ;
SR . PopPosition ( ) ;
// Read the team string
SR . PushPositionAndJump ( StartOfBlob + TeamStringOffset ) ;
Team = SR . ReadASCIIZ ( ) ;
SR . PopPosition ( ) ;
2016-02-19 13:49:13 -05:00
2014-03-14 14:13:41 -04:00
// Read the hashes
long TotalNumHashes = SpecialSlotCount + CodeSlotCount ;
Hashes = new byte [ TotalNumHashes * BytesPerHash ] ;
SR . PushPositionAndJump ( StartOfBlob + HashOffset - BytesPerHash * SpecialSlotCount ) ;
for ( long i = 0 ; i < TotalNumHashes ; + + i )
{
byte [ ] Hash = SR . ReadBytes ( BytesPerHash ) ;
Array . Copy ( Hash , 0 , Hashes , i * BytesPerHash , BytesPerHash ) ;
}
SR . PopPosition ( ) ;
if ( Config . bCodeSignVerbose )
{
PrintHash ( "Info:" , cdSlotMax - cdInfoSlot ) ;
PrintHash ( "Requirements:" , cdSlotMax - cdRequirementsSlot ) ;
PrintHash ( "ResourceDir:" , cdSlotMax - cdResourceDirSlot ) ;
PrintHash ( "Application:" , cdSlotMax - cdApplicationSlot ) ;
PrintHash ( "Entitlements:" , cdSlotMax - cdEntitlementSlot ) ;
}
}
void PrintHash ( string Prefix , int SlotIndex )
{
Console . WriteLine ( "{0} {1}" , Prefix , BitConverter . ToString ( Hashes , SlotIndex * BytesPerHash , BytesPerHash ) . Replace ( "-" , "" ) ) ;
}
protected override void PackageData ( WritingContext SW )
{
long StartPos = SW . Position - ( 2 * sizeof ( UInt32 ) ) ;
SW . Write ( Version ) ;
SW . Write ( Flags ) ;
// The hash offset is weird, it points to the first code page hash, not the start of the hashes array...
OffsetFieldU32or64 HashOffset = SW . WriteDeferredOffsetFrom ( StartPos - ( BytesPerHash * SpecialSlotCount ) , Bits . Num . _32 ) ;
OffsetFieldU32or64 IdentifierStringOffset = SW . WriteDeferredOffsetFrom ( StartPos , Bits . Num . _32 ) ;
SW . Write ( SpecialSlotCount ) ;
SW . Write ( CodeSlotCount ) ;
SW . Write ( MainImageSignatureLimit ) ;
SW . Write ( BytesPerHash ) ;
SW . Write ( HashType ) ;
SW . Write ( Spare1 ) ;
SW . Write ( LogPageSize ) ;
SW . Write ( Spare2 ) ;
SW . Write ( ScatterCount ) ;
2021-05-25 02:43:26 -04:00
OffsetFieldU32or64 TeamStringOffset = SW . WriteDeferredOffsetFrom ( StartPos , Bits . Num . _32 ) ;
SW . Write ( Spare3 ) ;
2016-02-19 13:49:13 -05:00
2021-05-25 02:43:26 -04:00
SW . Write ( CodeLimit64 ) ;
SW . Write ( ExecSegBase ) ;
SW . Write ( ExecSegLimit ) ;
SW . Write ( ExecSegFlags ) ;
2014-03-14 14:13:41 -04:00
2021-05-25 02:43:26 -04:00
// Write the identifier
SW . CommitDeferredField ( IdentifierStringOffset ) ;
byte [ ] IdentifierOutput = Utilities . CreateASCIIZ ( Identifier ) ;
SW . Write ( IdentifierOutput ) ;
2016-02-19 13:49:13 -05:00
2021-05-25 02:43:26 -04:00
// Write the team identifier
SW . CommitDeferredField ( TeamStringOffset ) ;
byte [ ] TeamOutput = Utilities . CreateASCIIZ ( Team ) ;
SW . Write ( TeamOutput ) ;
2014-03-14 14:13:41 -04:00
// Write the hashes
SW . CommitDeferredField ( HashOffset ) ;
SW . Write ( Hashes ) ;
}
public CodeDirectoryBlob ( )
{
MyMagic = CSMAGIC_CODEDIRECTORY ;
}
/// <summary>
/// The page size in bytes
/// </summary>
public int PageSize
{
get { return 1 < < LogPageSize ; }
}
/// <summary>
/// Generates a hash for one of the special slots and copies it into the hash array
/// </summary>
public void GenerateSpecialSlotHash ( int SpecialSlotIndex , byte [ ] SourceData )
{
2021-05-25 02:43:26 -04:00
byte [ ] Hash = HashProvider2 . ComputeHash ( SourceData ) ;
2014-03-14 14:13:41 -04:00
Array . Copy (
Hash , 0 ,
Hashes , ( SpecialSlotCount - SpecialSlotIndex ) * BytesPerHash ,
BytesPerHash ) ;
}
/// <summary>
/// Generates an empty hash (all zeros) for a special slot that isn't used (only cdApplicationSlot gets this treatment)
/// </summary>
/// <param name="SpecialSlotIndex"></param>
public void GenerateSpecialSlotHash ( int SpecialSlotIndex )
{
for ( int i = 0 ; i < BytesPerHash ; + + i )
{
Hashes [ ( cdSlotMax - SpecialSlotIndex ) * BytesPerHash + i ] = 0 ;
}
}
2021-05-25 02:43:26 -04:00
public static CodeDirectoryBlob Create ( string ApplicationID , string TeamID , int SignedFileLength , UInt64 InExecSegBase , UInt64 InExecSegLimit )
2014-03-14 14:13:41 -04:00
{
CodeDirectoryBlob Blob = new CodeDirectoryBlob ( ) ;
2021-05-25 02:43:26 -04:00
Blob . Allocate ( ApplicationID , TeamID , SignedFileLength , InExecSegBase , InExecSegLimit ) ;
2014-03-14 14:13:41 -04:00
return Blob ;
}
2021-05-25 02:43:26 -04:00
public void Allocate ( string ApplicationID , string TeamID , int SignedFileLength , UInt64 InExecSegBase , UInt64 InExecSegLimit )
2014-03-14 14:13:41 -04:00
{
Identifier = ApplicationID ;
2016-02-19 13:49:13 -05:00
Team = TeamID ;
2014-03-14 14:13:41 -04:00
2021-05-25 02:43:26 -04:00
Version = cVersion3 ;
2014-03-14 14:13:41 -04:00
Flags = 0 ;
Spare1 = 0 ;
Spare2 = 0 ;
2021-05-25 02:43:26 -04:00
Spare3 = 0 ;
2014-03-14 14:13:41 -04:00
ScatterCount = 0 ;
2021-05-25 02:43:26 -04:00
CodeLimit64 = 0 ;
ExecSegBase = InExecSegBase ;
ExecSegLimit = InExecSegLimit ;
ExecSegFlags = 17 ;
2014-03-14 14:13:41 -04:00
// 4 KB pages
LogPageSize = 12 ;
int PageSize = 1 < < LogPageSize ;
2021-05-25 02:43:26 -04:00
// 32 byte SHA256 hashes
HashType = 2 ;
BytesPerHash = ( byte ) ( HashProvider2 . HashSize / 8 ) ;
Debug . Assert ( BytesPerHash = = 32 ) ;
2014-03-14 14:13:41 -04:00
// Allocate space for the hashes
MainImageSignatureLimit = ( UInt32 ) SignedFileLength ;
SpecialSlotCount = cdSlotMax ;
CodeSlotCount = ( uint ) ( ( MainImageSignatureLimit + PageSize - 1 ) / PageSize ) ;
Hashes = new byte [ ( SpecialSlotCount + CodeSlotCount ) * BytesPerHash ] ;
}
/// <summary>
/// Calculates the hashes for every 4 KB of the main executable
/// </summary>
public void ComputeImageHashes ( byte [ ] SignedFileData )
{
// Fill out the executable hash
for ( int i = 0 ; i < CodeSlotCount ; + + i )
{
int StartOffset = i * PageSize ;
int LengthRemaining = ( int ) MainImageSignatureLimit - StartOffset ;
2021-05-25 02:43:26 -04:00
byte [ ] PageHash = HashProvider2 . ComputeHash ( SignedFileData , StartOffset , Math . Min ( LengthRemaining , PageSize ) ) ;
2014-03-14 14:13:41 -04:00
Array . Copy ( PageHash , 0 , Hashes , ( SpecialSlotCount + i ) * BytesPerHash , BytesPerHash ) ;
}
}
public override string ToString ( )
{
return String . Format (
"\n CodeDirectory\n v{0:X}\n flags={1}\n Ident: {2}\n with {3}+{4} slots" ,
Version , Flags , Identifier ,
SpecialSlotCount , CodeSlotCount ) +
String . Format (
"\n SigLimit 0x{0:X}\n Hash(Len={1} Type={2} Page={3})\n Spares={4} {5}\n ScatterCount={6}\n\n" ,
MainImageSignatureLimit , BytesPerHash , HashType , 1 < < LogPageSize , Spare1 , Spare2 , ScatterCount ) ;
}
}
/// <summary>
/// Payload for LC_CODE_SIGNATURE
/// </summary>
class MachLoadCommandCodeSignature : MachLoadCommandLinkEditBlob
{
public CodeSigningTableBlob Payload ;
public override string ToString ( )
{
return "Code Signature " + base . ToString ( ) ;
}
protected override void UnpackageData ( ReadingContext SR , int CommandSize )
{
base . UnpackageData ( SR , CommandSize ) ;
SR . PushPositionAndJump ( BlobFileOffset ) ;
SR . bStreamLittleEndian = false ;
long SavedPosition = SR . Position ;
// Parse the blob
Payload = AbstractBlob . CreateFromStream ( SR ) as CodeSigningTableBlob ;
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( Payload . ToString ( ) ) ;
}
//SR.VerifyStreamPosition(SavedPosition, BlobFileSize);
SR . PopPosition ( ) ;
SR . bStreamLittleEndian = true ;
}
/// <summary>
/// Patches the the blob length and offset of the code signing blob in an existing file
/// </summary>
public void PatchPositionAndSize ( WritingContext SW , UInt32 NewOffset , UInt32 NewLength )
{
Debug . Assert ( StartingLoadOffset > = 0 ) ;
if ( Config . bCodeSignVerbose )
{
Console . WriteLine ( "ZZZZZZZZZZZZZZZZ" ) ;
Console . WriteLine ( " Blob offset from 0x{0:X} to 0x{1:X} (delta {2})" , BlobFileOffset , NewOffset , ( long ) NewOffset - ( long ) BlobFileOffset ) ;
Console . WriteLine ( " Blob size from 0x{0:X} to 0x{1:X} (delta {2})" , BlobFileSize , NewLength , ( long ) NewLength - ( long ) BlobFileSize ) ;
Console . WriteLine ( "ZZZZZZZZZZZZZZZZ" ) ;
}
BlobFileOffset = NewOffset ;
BlobFileSize = NewLength ;
long PatchOffset = StartingLoadOffset + ( 2 * sizeof ( UInt32 ) ) ; // Command, CommandLength
SW . PushPositionAndJump ( PatchOffset ) ;
SW . Write ( BlobFileOffset ) ;
SW . Write ( BlobFileSize ) ;
SW . PopPosition ( ) ;
}
}
/// <summary>
/// Minimal implementation of the Fat Binary object file format
/// See: https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html#//apple_ref/doc/uid/20001298-154889
public class FatBinaryArch
{
public UInt32 CpuType ;
public UInt32 CpuSubType ;
public UInt32 Offset ; // Offset to the beginning of the data for this CPU.
public UInt32 Size ; // Size of the data for this CPU.
public UInt32 Align ;
}
public class FatBinaryFile
{
public UInt32 Magic ;
public UInt32 NumArchs ;
// Was this really a fat binary, or are we just a wrapper for the MachObjectFile?
public bool bIsFatBinary ;
public List < FatBinaryArch > Archs ;
public List < MachObjectFile > MachObjectFiles = new List < MachObjectFile > ( ) ;
// Values for Magic
public const UInt32 FAT_MAGIC = 0xCAFEBABE ;
public const UInt32 FAT_CIGAM = 0xBEBAFECA ; // For little Endian.
protected void Read ( ReadingContext SR )
{
// Read the header to see if it is a FAT Binary or not.
SR . bStreamLittleEndian = false ;
Magic = SR . ReadUInt32 ( ) ;
bIsFatBinary = ( Magic = = FAT_MAGIC ) ;
if ( bIsFatBinary )
{
Archs = new List < FatBinaryArch > ( ) ;
NumArchs = SR . ReadUInt32 ( ) ;
for ( int ArchIdx = 0 ; ArchIdx < NumArchs ; ArchIdx + + )
{
SR . bStreamLittleEndian = false ;
FatBinaryArch Arch = new FatBinaryArch ( ) ;
Arch . CpuType = SR . ReadUInt32 ( ) ;
Arch . CpuSubType = SR . ReadUInt32 ( ) ;
Arch . Offset = SR . ReadUInt32 ( ) ;
Arch . Size = SR . ReadUInt32 ( ) ;
Arch . Align = SR . ReadUInt32 ( ) ;
Archs . Add ( Arch ) ;
MachObjectFile Exe = new MachObjectFile ( ) ;
SR . bStreamLittleEndian = true ;
SR . OpenFatArchiveAt ( Arch . Offset ) ;
Exe . Read ( SR ) ;
SR . CloseFatArchive ( ) ;
MachObjectFiles . Add ( Exe ) ;
}
SR . bStreamLittleEndian = true ;
}
else
{
SR . bStreamLittleEndian = true ;
MachObjectFile Exe = new MachObjectFile ( ) ;
SR . Position = 0 ;
Exe . Read ( SR ) ;
MachObjectFiles . Add ( Exe ) ;
}
}
protected void Write ( WritingContext Context )
{
if ( bIsFatBinary )
{
// Write the header
Context . Write ( Magic ) ;
Context . Write ( NumArchs ) ;
Context . PushPosition ( ) ;
foreach ( FatBinaryArch Arch in Archs )
{
Context . Write ( Arch . CpuType ) ;
Context . Write ( Arch . CpuSubType ) ;
Context . Write ( Arch . Offset ) ;
Context . Write ( Arch . Size ) ;
Context . Write ( Arch . Align ) ;
}
int FileIdx = 0 ;
foreach ( MachObjectFile MachFile in MachObjectFiles )
{
Archs [ FileIdx ] . Offset = Convert . ToUInt32 ( Context . Position ) ;
MachFile . Write ( Context ) ;
Archs [ FileIdx ] . Size = Convert . ToUInt32 ( Context . Position ) - Archs [ FileIdx ] . Offset ;
FileIdx + + ;
}
Context . PopPosition ( ) ;
// Write updated header.
foreach ( FatBinaryArch Arch in Archs )
{
Context . Write ( Arch . CpuType ) ;
Context . Write ( Arch . CpuSubType ) ;
Context . Write ( Arch . Offset ) ;
Context . Write ( Arch . Size ) ;
Context . Write ( Arch . Align ) ;
}
}
else
{
// Should only be one...
MachObjectFiles [ 0 ] . Write ( Context ) ;
}
}
2015-01-28 10:47:54 -05:00
public void WriteHeader ( ref byte [ ] OutputData , uint Offset )
{
if ( bIsFatBinary )
{
// Write the header
byte [ ] Data = BitConverter . GetBytes ( Magic ) ;
if ( BitConverter . IsLittleEndian )
{
Array . Reverse ( Data ) ;
}
Data . CopyTo ( OutputData , Offset ) ;
Offset + = sizeof ( UInt32 ) ;
Data = BitConverter . GetBytes ( NumArchs ) ;
if ( BitConverter . IsLittleEndian )
{
Array . Reverse ( Data ) ;
}
Data . CopyTo ( OutputData , Offset ) ;
Offset + = sizeof ( UInt32 ) ;
foreach ( FatBinaryArch Arch in Archs )
{
Data = BitConverter . GetBytes ( Arch . CpuType ) ;
if ( BitConverter . IsLittleEndian )
{
Array . Reverse ( Data ) ;
}
Data . CopyTo ( OutputData , Offset ) ;
Offset + = sizeof ( UInt32 ) ;
Data = BitConverter . GetBytes ( Arch . CpuSubType ) ;
if ( BitConverter . IsLittleEndian )
{
Array . Reverse ( Data ) ;
}
Data . CopyTo ( OutputData , Offset ) ;
Offset + = sizeof ( UInt32 ) ;
Data = BitConverter . GetBytes ( Arch . Offset ) ;
if ( BitConverter . IsLittleEndian )
{
Array . Reverse ( Data ) ;
}
Data . CopyTo ( OutputData , Offset ) ;
Offset + = sizeof ( UInt32 ) ;
Data = BitConverter . GetBytes ( Arch . Size ) ;
if ( BitConverter . IsLittleEndian )
{
Array . Reverse ( Data ) ;
}
Data . CopyTo ( OutputData , Offset ) ;
Offset + = sizeof ( UInt32 ) ;
Data = BitConverter . GetBytes ( Arch . Align ) ;
if ( BitConverter . IsLittleEndian )
{
Array . Reverse ( Data ) ;
}
Data . CopyTo ( OutputData , Offset ) ;
Offset + = sizeof ( UInt32 ) ;
}
}
}
2014-03-14 14:13:41 -04:00
public void LoadFromFile ( string Filename )
{
BinaryReader SR = new BinaryReader ( File . OpenRead ( Filename ) ) ;
ReadingContext Context = new ReadingContext ( SR ) ;
Read ( Context ) ;
SR . Close ( ) ;
}
public void LoadFromBytes ( byte [ ] Data )
{
MemoryStream Stream = new MemoryStream ( Data , false ) ;
BinaryReader SR = new BinaryReader ( Stream ) ;
ReadingContext Context = new ReadingContext ( SR ) ;
Read ( Context ) ;
SR . Close ( ) ;
}
public void WriteToFile ( string Filename )
{
BinaryWriter SW = new BinaryWriter ( File . OpenWrite ( Filename ) ) ;
WritingContext Context = new WritingContext ( SW ) ;
Write ( Context ) ;
SW . Close ( ) ;
}
}
}