Skip to content

Commit

Permalink
Merge pull request #58 from p-x9/feature/code-signature
Browse files Browse the repository at this point in the history
Code Signature
  • Loading branch information
p-x9 committed Mar 12, 2024
2 parents 6391489 + ae4382c commit 516cea0
Show file tree
Hide file tree
Showing 25 changed files with 2,270 additions and 5 deletions.
14 changes: 14 additions & 0 deletions Sources/MachOKit/Extension/Sequence+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,17 @@ extension Sequence where Element == MachOImage.Symbol {
return nil
}
}

extension Sequence where Element == CodeSignCodeDirectory {
public var bestHashTyped: CodeSignCodeDirectory? {
let hashTypes: [CodeSignHashType] = [
.sha384,
.sha256,
.sha256_truncated,
.sha1
]
return self.min(by: { lhs, rhs in
hashTypes.firstIndex(of: lhs.hashType)! < hashTypes.firstIndex(of: rhs.hashType)!
})
}
}
140 changes: 140 additions & 0 deletions Sources/MachOKit/MachOFile+CodeSign.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//
// MachOFile+CodeSign.swift
//
//
// Created by p-x9 on 2024/03/03.
//
//

import Foundation
import MachOKitC

extension MachOFile {
public struct CodeSign {
public let data: Data
public let isSwapped: Bool // bigEndian => false
}
}

extension MachOFile.CodeSign {
init(data: Data) {
self.data = data
self.isSwapped = CFByteOrderGetCurrent() != CFByteOrderBigEndian.rawValue
}
}

extension MachOFile.CodeSign: CodeSignProtocol {
public var superBlob: CodeSignSuperBlob? {
data.withUnsafeBytes {
guard let basePtr = $0.baseAddress else { return nil }
var layout = basePtr.assumingMemoryBound(to: CS_SuperBlob.self).pointee
if isSwapped { layout = layout.swapped }
return .init(
layout: layout,
offset: 0
)
}
}

public var codeDirectories: [CodeSignCodeDirectory] {
data.withUnsafeBytes { bufferPointer in
guard let baseAddress = bufferPointer.baseAddress,
let superBlob else {
return []
}
let blobIndices = superBlob.blobIndices(in: self)
return blobIndices
.compactMap {
let offset: Int = numericCast($0.offset)
let ptr = baseAddress.advanced(by: offset)
let _blob = ptr.assumingMemoryBound(to: CS_GenericBlob.self).pointee
let blob = CodeSignGenericBlob(
layout: isSwapped ? _blob.swapped : _blob
)
guard blob.magic == .codedirectory else {
return nil
}
return (
ptr.assumingMemoryBound(to: CS_CodeDirectory.self).pointee,
offset
)
}
.map {
isSwapped ? .init(layout: $0.swapped, offset: $1)
: .init(layout: $0, offset: $1)
}
}
}

public var requirementsBlob: CodeSignSuperBlob? {
guard let superBlob else {
return nil
}
let blobIndices = superBlob.blobIndices(in: self)
guard let index = blobIndices.first(
where: { $0.type == .requirements }
) else {
return nil
}
return data.withUnsafeBytes { bufferPointer in
guard let baseAddress = bufferPointer.baseAddress else {
return nil
}
let offset: Int = numericCast(index.offset)
let ptr = baseAddress.advanced(by: offset)
var _blob = ptr
.assumingMemoryBound(to: CS_SuperBlob.self)
.pointee
if isSwapped { _blob = _blob.swapped }

return .init(
layout: _blob,
offset: offset
)
}
}
}

extension MachOFile.CodeSign {
/// Get blob data as `Data`
/// - Parameters:
/// - superBlob: SuperBlob to which index belongs
/// - index: Index of the blob to be gotten
/// - includesGenericInfo: A boolean value that indicates whether the data defined in the ``CodeSignGenericBlob``, such as magic and length, are included or not.
/// - Returns: Data of blob
///
/// Note that when converting from this data to other blob models, byte swapping must be performed appropriately for the ``MachOFile.CodeSign.isSwapped`` parameter.
public func blobData(
in superBlob: CodeSignSuperBlob,
at index: CodeSignBlobIndex,
includesGenericInfo: Bool = true
) -> Data? {
data.withUnsafeBytes { bufferPointer in
guard let baseAddress = bufferPointer.baseAddress else {
return nil
}
let offset: Int = numericCast(superBlob.offset) + numericCast(index.offset)
guard let _blob: CodeSignGenericBlob = .load(
from: baseAddress,
offset: offset,
isSwapped: isSwapped
) else { return nil }

let data = Data(
bytes: baseAddress.advanced(by: offset),
count: numericCast(_blob.length)
)
if includesGenericInfo {
return data
} else {
return data.advanced(by: CodeSignGenericBlob.layoutSize)
}
}
}

public func blobIndices(
of superBlob: CodeSignSuperBlob
) -> AnySequence<CodeSignBlobIndex> {
superBlob.blobIndices(in: self)
}
}
14 changes: 14 additions & 0 deletions Sources/MachOKit/MachOFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -412,3 +412,17 @@ extension MachOFile {
return false
}
}

extension MachOFile {
public var codeSign: CodeSign? {
guard let info = loadCommands.codeSignature else {
return nil
}
let data = fileHandle.readData(
offset: UInt64(headerStartOffset) + numericCast(info.dataoff),
size: numericCast(info.datasize)
)

return .init(data: data)
}
}
171 changes: 171 additions & 0 deletions Sources/MachOKit/MachOImage+CodeSign.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//
// MachOImage+CodeSign.swift
//
//
// Created by p-x9 on 2024/03/10.
//
//

import Foundation

extension MachOImage {
public struct CodeSign {
public let basePointer: UnsafeRawPointer
public let codeSignatureSize: Int
public let isSwapped: Bool // bigEndian => false
}
}

extension MachOImage.CodeSign: CodeSignProtocol {
init?(
codeSignature: linkedit_data_command,
linkedit: SegmentCommand64,
vmaddrSlide: Int
) {
guard let linkeditStartPtr = linkedit.startPtr(
vmaddrSlide: vmaddrSlide
) else {
return nil
}

let start = linkeditStartPtr
.advanced(by: -numericCast(linkedit.fileoff))
.advanced(by: numericCast(codeSignature.dataoff))
let size: Int = numericCast(codeSignature.datasize)
let isSwapped = CFByteOrderGetCurrent() != CFByteOrderBigEndian.rawValue

self.init(
basePointer: start,
codeSignatureSize: size,
isSwapped: isSwapped
)
}

init?(
codeSignature: linkedit_data_command,
linkedit: SegmentCommand,
vmaddrSlide: Int
) {
guard let linkeditStartPtr = linkedit.startPtr(
vmaddrSlide: vmaddrSlide
) else {
return nil
}

let start = linkeditStartPtr
.advanced(by: -numericCast(linkedit.fileoff))
.advanced(by: numericCast(codeSignature.dataoff))
.assumingMemoryBound(to: UInt8.self)
let size: Int = numericCast(codeSignature.datasize)
let isSwapped = CFByteOrderGetCurrent() != CFByteOrderBigEndian.rawValue

self.init(
basePointer: start,
codeSignatureSize: size,
isSwapped: isSwapped
)
}
}

extension MachOImage.CodeSign {
public var superBlob: CodeSignSuperBlob? {
var layout = basePointer
.assumingMemoryBound(to: CS_SuperBlob.self)
.pointee
if isSwapped { layout = layout.swapped }
return .init(
layout: layout,
offset: 0
)
}

public var codeDirectories: [CodeSignCodeDirectory] {
guard let superBlob else {
return []
}
let blobIndices = superBlob.blobIndices(in: self)
return blobIndices
.compactMap {
let offset: Int = numericCast($0.offset)
let ptr = basePointer.advanced(by: offset)
let _blob = ptr.assumingMemoryBound(to: CS_GenericBlob.self).pointee
let blob = CodeSignGenericBlob(
layout: isSwapped ? _blob.swapped : _blob
)
guard blob.magic == .codedirectory else {
return nil
}
return (
ptr.assumingMemoryBound(to: CS_CodeDirectory.self).pointee,
offset
)
}
.map {
isSwapped ? .init(layout: $0.swapped, offset: $1)
: .init(layout: $0, offset: $1)
}
}

public var requirementsBlob: CodeSignSuperBlob? {
guard let superBlob else {
return nil
}
let blobIndices = superBlob.blobIndices(in: self)
guard let index = blobIndices.first(
where: { $0.type == .requirements }
) else {
return nil
}

let offset: Int = numericCast(index.offset)
let ptr = basePointer.advanced(by: offset)
var _blob = ptr
.assumingMemoryBound(to: CS_SuperBlob.self)
.pointee
if isSwapped { _blob = _blob.swapped }

return .init(
layout: _blob,
offset: offset
)
}
}

extension MachOImage.CodeSign {
/// Get blob data as `Data`
/// - Parameters:
/// - superBlob: SuperBlob to which index belongs
/// - index: Index of the blob to be gotten
/// - includesGenericInfo: A boolean value that indicates whether the data defined in the ``CodeSignGenericBlob``, such as magic and length, are included or not.
/// - Returns: Data of blob
///
/// Note that when converting from this data to other blob models, byte swapping must be performed appropriately for the ``MachOImage.CodeSign.isSwapped`` parameter.
public func blobData(
in superBlob: CodeSignSuperBlob,
at index: CodeSignBlobIndex,
includesGenericInfo: Bool = true
) -> Data? {
let offset: Int = numericCast(superBlob.offset) + numericCast(index.offset)
guard let _blob: CodeSignGenericBlob = .load(
from: basePointer,
offset: offset,
isSwapped: isSwapped
) else { return nil }

let data = Data(
bytes: basePointer.advanced(by: offset),
count: numericCast(_blob.length)
)
if includesGenericInfo {
return data
} else {
return data.advanced(by: CodeSignGenericBlob.layoutSize)
}
}

public func blobIndices(
of superBlob: CodeSignSuperBlob
) -> AnySequence<CodeSignBlobIndex> {
superBlob.blobIndices(in: self)
}
}
25 changes: 25 additions & 0 deletions Sources/MachOKit/MachOImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -543,3 +543,28 @@ extension MachOImage {
return nil
}
}

extension MachOImage {
public var codeSign: CodeSign? {
guard let vmaddrSlide,
let codeSignature = loadCommands.codeSignature,
codeSignature.datasize > 0 else {
return nil
}

if let linkedit = loadCommands.linkedit64 {
return .init(
codeSignature: codeSignature.layout,
linkedit: linkedit,
vmaddrSlide: vmaddrSlide
)
} else if let linkedit = loadCommands.linkedit {
return .init(
codeSignature: codeSignature.layout,
linkedit: linkedit,
vmaddrSlide: vmaddrSlide
)
}
return nil
}
}

0 comments on commit 516cea0

Please sign in to comment.