goblin/pe/
certificate_table.rsuse crate::error;
use scroll::{ctx, Pread, Pwrite};
use alloc::string::ToString;
use alloc::vec::Vec;
use super::utils::pad;
#[repr(u16)]
#[non_exhaustive]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum AttributeCertificateRevision {
Revision1_0 = 0x0100,
Revision2_0 = 0x0200,
}
impl TryFrom<u16> for AttributeCertificateRevision {
type Error = error::Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
Ok(match value {
x if x == AttributeCertificateRevision::Revision1_0 as u16 => {
AttributeCertificateRevision::Revision1_0
}
x if x == AttributeCertificateRevision::Revision2_0 as u16 => {
AttributeCertificateRevision::Revision2_0
}
_ => {
return Err(error::Error::Malformed(
"Invalid certificate attribute revision".to_string(),
))
}
})
}
}
#[repr(u16)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum AttributeCertificateType {
X509 = 0x0001,
PkcsSignedData = 0x0002,
Reserved1 = 0x0003,
TsStackSigned = 0x0004,
}
impl TryFrom<u16> for AttributeCertificateType {
type Error = error::Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
Ok(match value {
x if x == AttributeCertificateType::X509 as u16 => AttributeCertificateType::X509,
x if x == AttributeCertificateType::PkcsSignedData as u16 => {
AttributeCertificateType::PkcsSignedData
}
x if x == AttributeCertificateType::Reserved1 as u16 => {
AttributeCertificateType::Reserved1
}
x if x == AttributeCertificateType::TsStackSigned as u16 => {
AttributeCertificateType::TsStackSigned
}
_ => {
return Err(error::Error::Malformed(
"Invalid attribute certificate type".to_string(),
))
}
})
}
}
#[derive(Clone, Pread)]
struct AttributeCertificateHeader {
length: u32,
revision: u16,
certificate_type: u16,
}
const CERTIFICATE_DATA_OFFSET: u32 = 8;
#[derive(Debug)]
pub struct AttributeCertificate<'a> {
pub length: u32,
pub revision: AttributeCertificateRevision,
pub certificate_type: AttributeCertificateType,
pub certificate: &'a [u8],
}
impl<'a> AttributeCertificate<'a> {
pub fn parse(
bytes: &'a [u8],
current_offset: &mut usize,
) -> Result<AttributeCertificate<'a>, error::Error> {
let header: AttributeCertificateHeader = bytes.gread_with(current_offset, scroll::LE)?;
let cert_size = usize::try_from(header.length.saturating_sub(CERTIFICATE_DATA_OFFSET))
.map_err(|_err| {
error::Error::Malformed(
"Attribute certificate size do not fit in usize".to_string(),
)
})?;
if let Some(bytes) = bytes.get(*current_offset..(*current_offset + cert_size)) {
let attr = Self {
length: header.length,
revision: header.revision.try_into()?,
certificate_type: header.certificate_type.try_into()?,
certificate: bytes,
};
*current_offset = current_offset.saturating_add(cert_size);
*current_offset = (*current_offset + 7) & !7;
Ok(attr)
} else {
Err(error::Error::Malformed(format!(
"Unable to extract certificate. Probably cert_size:{} is malformed",
cert_size
)))
}
}
}
impl<'a> ctx::TryIntoCtx<scroll::Endian> for &AttributeCertificate<'a> {
type Error = error::Error;
fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let offset = &mut 0;
bytes.gwrite_with(self.length, offset, ctx)?;
bytes.gwrite_with(self.revision as u16, offset, ctx)?;
bytes.gwrite_with(self.certificate_type as u16, offset, ctx)?;
let maybe_certificate_padding = pad(self.certificate.len(), Some(16usize));
bytes.gwrite(self.certificate, offset)?;
if let Some(cert_padding) = maybe_certificate_padding {
bytes.gwrite(&cert_padding[..], offset)?;
}
Ok(*offset)
}
}
pub type CertificateDirectoryTable<'a> = Vec<AttributeCertificate<'a>>;
pub(crate) fn enumerate_certificates(
bytes: &[u8],
table_virtual_address: u32,
table_size: u32,
) -> Result<CertificateDirectoryTable, error::Error> {
let table_start_offset = usize::try_from(table_virtual_address).map_err(|_err| {
error::Error::Malformed("Certificate table RVA do not fit in a usize".to_string())
})?;
let table_end_offset =
table_start_offset.saturating_add(usize::try_from(table_size).map_err(|_err| {
error::Error::Malformed("Certificate table size do not fit in a usize".to_string())
})?);
let mut current_offset = table_start_offset;
let mut attrs = vec![];
if table_end_offset > bytes.len() {
return Err(error::Error::Malformed(
"End of attribute certificates table is after the end of the PE binary".to_string(),
));
}
while current_offset < table_end_offset {
attrs.push(AttributeCertificate::parse(bytes, &mut current_offset)?);
}
Ok(attrs)
}