Skip to content

Commit

Permalink
feat: implement entitlement parsing for Mach-O
Browse files Browse the repository at this point in the history
  • Loading branch information
latonis committed Jan 22, 2024
1 parent c5fe3b8 commit 8151d1a
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 36 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions yara-x/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ yara-x-proto = { workspace = true }

lingua = { version = "1.6.0", optional = true, default-features = false, features = ["english", "german", "french", "spanish"] }
cryptographic-message-syntax = "0.26.0"
roxmltree = "0.19.0"

[build-dependencies]
protobuf = { workspace = true }
Expand Down
98 changes: 68 additions & 30 deletions yara-x/src/modules/macho/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const _CS_MAGIC_CODEDIRECTORY: u32 = 0xfade0c02;
const _CS_MAGIC_EMBEDDED_SIGNATURE: u32 = 0xfade0cc0;
const _CS_MAGIC_DETACHED_SIGNATURE: u32 = 0xfade0cc1;
const CS_MAGIC_BLOBWRAPPER: u32 = 0xfade0b01;
const CS_MAGIC_EMBEDDED_ENTITLEMENTS: u32 = 0xfade7171;

/// Define Mach-O CPU type constants
const CPU_TYPE_MC680X0: u32 = 0x00000006;
Expand Down Expand Up @@ -3249,7 +3250,6 @@ fn parse_macho_code_signature(
macho_file: &mut File,
) -> Result<(), MachoError> {
if macho_file.code_signature_data.is_some() {
let certificates = macho_file.certificates.mut_or_insert_default();
let code_signature_data =
macho_file.code_signature_data.as_mut().unwrap();
let data_offset = code_signature_data.dataoff() as usize;
Expand All @@ -3261,35 +3261,72 @@ fn parse_macho_code_signature(
.map_err(|e| MachoError::ParsingError(format!("{:?}", e)))?;

for blob_index in super_blob.index {
if blob_index.blob.magic == CS_MAGIC_BLOBWRAPPER {
let offset = blob_index.offset as usize;
let length = blob_index.blob.length as usize;
let size_of_blob = std::mem::size_of::<CSBlob>();

let signage = SignedData::parse_ber(
&super_data[offset + size_of_blob
..offset + size_of_blob + length],
)
.map_err(|e| {
MachoError::ParsingError(format!("{:?}", e))
})?;
// .unwrap();

let signers = signage.signers();
let certs = signage.certificates();

certs.for_each(|cert| {
let name = cert.subject_common_name().unwrap();
certificates.common_names.push(name);
});

signers.for_each(|signer| {
let (name, _) =
signer.certificate_issuer_and_serial().unwrap();
certificates.signer_names.push(
name.user_friendly_str().unwrap().to_string(),
);
});
let offset = blob_index.offset as usize;
let length = blob_index.blob.length as usize;
let size_of_blob = std::mem::size_of::<CSBlob>();

match blob_index.blob.magic {
CS_MAGIC_BLOBWRAPPER => {
let certificates =
macho_file.certificates.mut_or_insert_default();

let signage = SignedData::parse_ber(
&super_data
[offset + size_of_blob..offset + length],
)
.map_err(|e| {
MachoError::ParsingError(format!("{:?}", e))
})?;
// .unwrap();

let signers = signage.signers();
let certs = signage.certificates();

certs.for_each(|cert| {
let name = cert.subject_common_name().unwrap();
certificates.common_names.push(name);
});

signers.for_each(|signer| {
let (name, _) = signer
.certificate_issuer_and_serial()
.unwrap();
certificates.signer_names.push(
name.user_friendly_str().unwrap().to_string(),
);
});
}
CS_MAGIC_EMBEDDED_ENTITLEMENTS => {
let xml_data = &super_data
[offset + size_of_blob..offset + length];
let xml_string =
std::str::from_utf8(xml_data).unwrap_or_default();

let opt = roxmltree::ParsingOptions {
allow_dtd: true,
..roxmltree::ParsingOptions::default()
};

let parsed_xml =
roxmltree::Document::parse_with_options(
xml_string, opt,
)
.map_err(|e| {
MachoError::ParsingError(format!("{:?}", e))
})?;

for node in parsed_xml
.descendants()
.filter(|n| n.has_tag_name("key"))
{
if let Some(entitlement) = node.text() {
macho_file
.entitlements
.push(entitlement.to_string());
}
}
}
_ => {}
}
}
}
Expand Down Expand Up @@ -3931,6 +3968,7 @@ fn main(data: &[u8]) -> Macho {
macho_proto.code_signature_data =
file_data.code_signature_data;
macho_proto.certificates = file_data.certificates;
macho_proto.entitlements = file_data.entitlements;
macho_proto.entry_point = file_data.entry_point;
macho_proto.stack_size = file_data.stack_size;
}
Expand Down
9 changes: 8 additions & 1 deletion yara-x/src/modules/macho/tests/testdata/chess.out
Original file line number Diff line number Diff line change
Expand Up @@ -838,4 +838,11 @@ certificates:
- "Apple Root CA"
- "Software Signing"
signer_names:
- "CN=Apple Code Signing Certification Authority, OU=Apple Certification Authority, O=Apple Inc., C=US"
- "CN=Apple Code Signing Certification Authority, OU=Apple Certification Authority, O=Apple Inc., C=US"
entitlements:
- "com.apple.developer.game-center"
- "com.apple.private.tcc.allow"
- "com.apple.security.app-sandbox"
- "com.apple.security.device.microphone"
- "com.apple.security.files.user-selected.read-write"
- "com.apple.security.network.client"
11 changes: 6 additions & 5 deletions yara-x/src/modules/protos/macho.proto
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ message File {
optional Symtab symtab = 19;
optional LinkedItData code_signature_data = 20;
optional CodeSignature certificates = 21;

repeated string entitlements = 22;
}

message Macho {
Expand All @@ -170,16 +170,17 @@ message Macho {
optional Symtab symtab = 19;
optional LinkedItData code_signature_data = 20;
optional CodeSignature certificates = 21;
repeated string entitlements = 22;



// Add fields for Mach-O fat binary header
optional uint32 fat_magic = 22 [(yaml.field).fmt = "x"];
optional uint32 nfat_arch = 23;
repeated FatArch fat_arch = 24;
optional uint32 fat_magic = 23 [(yaml.field).fmt = "x"];
optional uint32 nfat_arch = 24;
repeated FatArch fat_arch = 25;

// Nested Mach-O files
repeated File file = 25;
repeated File file = 26;
}

enum HEADER {
Expand Down

0 comments on commit 8151d1a

Please sign in to comment.