解析 Authenticode 部分代码。

#include "base/strings/utf_string_conversions.h"
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#define WINCRYPT_USE_SYMBOL_PREFIX

#include <Windows.h>

#include <ImageHlp.h>

#include "base/command_line.h"
#include "base/containers/heap_array.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"

//
#include "third_party/boringssl/src/crypto/x509/internal.h"
#include "third_party/boringssl/src/include/openssl/asn1.h"
#include "third_party/boringssl/src/include/openssl/asn1t.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
#include "third_party/boringssl/src/include/openssl/x509.h"

#include "third_party/boringssl/src/pki/parser.h"

namespace switches {
constexpr char kInput[] = "input";
}

constexpr uint8_t kOidSpcIndirectDataContent[] = {
    0x2b, 0x06, 0x01, 0x04, 0x01,
    0x82, 0x37, 0x02, 0x01, 0x04};  //  1.3.6.1.4.1.311.2.1.4

constexpr uint8_t kOidSpcPEImageData[] = {
    0x2b, 0x06, 0x01, 0x04, 0x01,
    0x82, 0x37, 0x02, 0x01, 0x0F};  // 1.3.6.1.4.1.311.2.1.15

constexpr uint8_t kOidPKCS7SignedData[] = {
    0x2a, 0x86, 0x48, 0x86, 0xf7,
    0x0d, 0x01, 0x07, 0x02};  // 1.2.840.113549.1.7.2

// SpcAttributeTypeAndOptionalValue ::= SEQUENCE {
//     type                    ObjectID,
//     value                   [0] EXPLICIT ANY OPTIONAL
// }

// SpcPeImageData ::= SEQUENCE {
//    flags                   SpcPeImageFlags DEFAULT { includeResources },
//    file                    SpcLink
// } --#public--

// SpcPeImageFlags ::= BIT STRING {
//     includeResources            (0),
//     includeDebugInfo            (1),
//     includeImportAddressTable   (2)
// }

// SpcLink ::= CHOICE {
//     url                     [0] IMPLICIT IA5STRING,
//     moniker                 [1] IMPLICIT SpcSerializedObject,
//     file                    [2] EXPLICIT SpcString
// } --#public--

// SpcString ::= CHOICE {
//     unicode                 [0] IMPLICIT BMPSTRING,
//     ascii                   [1] IMPLICIT IA5STRING
// }

bool Parse_SpcString(CBS* cbs) {
  if (CBS_peek_asn1_tag(cbs, CBS_ASN1_CONTEXT_SPECIFIC | 0)) {  // unicode
    CBS child;
    if (!CBS_get_asn1(cbs, &child, CBS_ASN1_CONTEXT_SPECIFIC | 0)) {
      LOG(ERROR) << "Failed to parse unicode SpcString";
      return false;
    }
    if (CBS_len(&child) == 0) {
      LOG(INFO) << "SpcString (unicode): <empty>";
      return true;
    }

    CBS str;
    if (!CBS_get_asn1(&child, &str, CBS_ASN1_BMPSTRING)) {
      LOG(ERROR) << "Failed to parse unicode SpcString";
      return false;
    }
    std::u16string u16_str(reinterpret_cast<const char16_t*>(CBS_data(&str)),
                           CBS_len(&str) / sizeof(char16_t));
    std::string u8_str = base::UTF16ToUTF8(u16_str);

    LOG(INFO) << "SpcString (unicode): " << u8_str;
  } else if (CBS_peek_asn1_tag(cbs, CBS_ASN1_CONTEXT_SPECIFIC | 1)) {  // ascii
    CBS child;
    CBS str;
    if (!CBS_get_asn1(cbs, &child, CBS_ASN1_CONTEXT_SPECIFIC | 1) ||
        !CBS_get_asn1(&child, &str, CBS_ASN1_IA5STRING)) {
      LOG(ERROR) << "Failed to parse ascii SpcString";
      return false;
    }
    std::string u8_str(reinterpret_cast<const char*>(CBS_data(&str)),
                       CBS_len(&str));
    LOG(INFO) << "SpcString (ascii): " << u8_str;
  } else {
    LOG(ERROR) << "Failed to parse SpcString choice";
    return false;
  }
  if (CBS_len(cbs) != 0) {
    LOG(ERROR) << "Trailing data in SpcString";
    return false;
  }
  return true;
}

bool Parse_Authenticode_SpcPeImageData(CBS* cbs) {
  // SpcPeImageData ::= SEQUENCE {
  //    flags                   SpcPeImageFlags DEFAULT { includeResources },
  //    file                    SpcLink
  // } --#public--
  //
  // flags SpcPeImageFlags DEFAULT { includeResources },
  //
  // SpcPeImageFlags ::= BIT STRING {
  //     includeResources            (0),
  //     includeDebugInfo            (1),
  //     includeImportAddressTable   (2)
  // }
  CBS flags;
  if (!CBS_get_asn1(cbs, &flags, CBS_ASN1_BITSTRING)) {
    LOG(ERROR) << "Failed to parse flags";
    return false;
  }
  if (CBS_len(&flags) > 1) {
    const uint8_t* flag_bytes = CBS_data(&flags) + 1;
    // includeResources is bit 0.
    if (flag_bytes[0] & 0x80) {
      LOG(INFO) << "Flag: includeResources";
    }
    // includeDebugInfo is bit 1.
    if (flag_bytes[0] & 0x40) {
      LOG(INFO) << "Flag: includeDebugInfo";
    }
    // includeImportAddressTable is bit 2.
    if (flag_bytes[0] & 0x20) {
      LOG(INFO) << "Flag: includeImportAddressTable";
    }
  } else {
    LOG(INFO) << "Flags: default (includeResources)";
  }

  // file SpcLink
  // SpcLink ::= CHOICE {
  //     url                     [0] IMPLICIT IA5STRING,
  //     moniker                 [1] IMPLICIT SpcSerializedObject,
  //     file                    [2] EXPLICIT SpcString
  // } --#public--

  CBS spc_link;
  if (!CBS_get_asn1(cbs, &spc_link,
                    CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
    LOG(ERROR) << "Failed to parse SpcLink wrapper";
    return false;
  }

  if (CBS_peek_asn1_tag(
          &spc_link, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {  // url
    CBS child;
    CBS url;
    if (!CBS_get_asn1(&spc_link, &child,
                      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
        !CBS_get_asn1(&child, &url, CBS_ASN1_IA5STRING)) {
      LOG(ERROR) << "Failed to parse url";
      return false;
    } else {
      LOG(INFO) << "SpcLink (url): "
                << std::string(reinterpret_cast<const char*>(CBS_data(&url)),
                               CBS_len(&url));
    }
  } else if (CBS_peek_asn1_tag(
                 &spc_link, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)) {
    CBS child;
    CBS moniker;
    if (!CBS_get_asn1(&spc_link, &child,
                      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1) ||
        !CBS_get_asn1(&child, &moniker, CBS_ASN1_SEQUENCE)) {
      LOG(ERROR) << "Failed to parse url";
      return false;
    } else {
      LOG(INFO) << "SpcLink (moniker): // TODO: implement";
    }
  } else if (CBS_peek_asn1_tag(
                 &spc_link, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 2)) {
    CBS file;
    if (!CBS_get_asn1(&spc_link, &file,
                      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 2)) {
      LOG(ERROR) << "Failed to parse url";
      return false;
    } else {
      Parse_SpcString(&file);
    }
  } else {
    LOG(ERROR) << "Failed to parse SpcLink choice";
    return false;
  }
  if (CBS_len(cbs) != 0) {
    LOG(ERROR) << "Trailing data in SpcLink";
    return false;
  }

  return true;
}

// DigestInfo ::= SEQUENCE {
//   digestAlgorithm AlgorithmIdentifier,
//   digest OCTET STRING
// }
//
// AlgorithmIdentifier ::= SEQUENCE {
//   algorithm OBJECT IDENTIFIER,
//   parameters ANY DEFINED BY algorithm OPTIONAL
// }
bool Parse_DigestInfo(CBS* cbs) {
  const EVP_MD* md = EVP_parse_digest_algorithm(cbs);
  if (md != EVP_sha256() && md != EVP_sha1()) {
    LOG(ERROR) << "Failed to parse message digest algorithm";
    return false;
  }

  CBS digest;
  if (!CBS_get_asn1(cbs, &digest, CBS_ASN1_OCTETSTRING)) {
    LOG(ERROR) << "Failed to parse digest";
    return false;
  }
  if (CBS_len(cbs) != 0) {
    LOG(ERROR) << "Trailing data in message digest";
    return false;
  }
  return true;
}
// IndirectDataContent ::= SEQUENCE {
//   data ContentInfo,
//   messageDigest DigestInfo
// }
bool Parse_Authenticode_SpcIndirectDataContent(CBS* cbs) {
  // SpcAttributeTypeAndOptionalValue ::= SEQUENCE {
  //     type                    ObjectID,
  //     value                   [0] EXPLICIT ANY OPTIONAL
  // }
  CBS data;
  if (!CBS_get_asn1(cbs, &data, CBS_ASN1_SEQUENCE)) {
    LOG(ERROR) << "Failed to parse data";
    return false;
  }

  CBS data_oid;
  if (!CBS_get_asn1(&data, &data_oid, CBS_ASN1_OBJECT)) {
    LOG(ERROR) << "Failed to parse data content type";
    return false;
  }

  if (CBS_mem_equal(&data_oid, kOidSpcPEImageData,
                    sizeof(kOidSpcPEImageData))) {
    CBS pe_image_data;
    if (!CBS_get_asn1(&data, &pe_image_data, CBS_ASN1_SEQUENCE)) {
      LOG(ERROR) << "Failed to parse SpcPeImageData wrapper";
      return false;
    }

    if (!Parse_Authenticode_SpcPeImageData(&pe_image_data)) {
      LOG(ERROR) << "Failed to parse SpcPeImageData";
      return false;
    }
  } else {
    LOG(ERROR) << "Unknown data content type";
    return false;
  }

  CBS message_digest;
  if (!CBS_get_asn1(cbs, &message_digest, CBS_ASN1_SEQUENCE)) {
    LOG(ERROR) << "Failed to parse message digest";
    return false;
  }
  if (!Parse_DigestInfo(&message_digest)) {
    LOG(ERROR) << "Failed to parse message digest";
    return false;
  }
  return true;
}

bool Parse_Authenticode_SignedData(CBS* cbs) {
  CBS in = *cbs;

  uint64_t version_number;
  if (!CBS_get_asn1_uint64(&in, &version_number) || version_number != 1) {
    LOG(ERROR) << "Failed to parse version";
    return false;
  }

  CBS digest_algorithms;
  if (!CBS_get_asn1(&in, &digest_algorithms, CBS_ASN1_SET)) {
    LOG(ERROR) << "Failed to parse digest algorithms";
    return false;
  }

  // DigestAlgorithmIdentifiers SET OF DigestAlgorithmIdentifier
  // DigestAlgorithmIdentifier ::= SEQUENCE {
  //   algorithm OBJECT IDENTIFIER,
  //   parameters ANY DEFINED BY algorithm OPTIONAL
  // }
  while (CBS_len(&digest_algorithms) > 0) {
    const EVP_MD* md = EVP_parse_digest_algorithm(&digest_algorithms);
    if (md != EVP_sha256() && md != EVP_sha1()) {
      LOG(ERROR) << "Failed to parse digest algorithm";
      return false;
    }
  }

  //  EncapsulatedContentInfo ::= SEQUENCE {
  //    eContentType ContentType,
  //    eContent [0] EXPLICIT OCTET STRING OPTIONAL }
  CBS content_info;
  if (!CBS_get_asn1(&in, &content_info, CBS_ASN1_SEQUENCE)) {
    LOG(ERROR) << "Failed to parse content info";
    return false;
  }
  CBS content_type;
  if (!CBS_get_asn1(&content_info, &content_type, CBS_ASN1_OBJECT)) {
    LOG(ERROR) << "Failed to parse content type";
    return false;
  }

  if (!CBS_mem_equal(&content_type, kOidSpcIndirectDataContent,
                     sizeof(kOidSpcIndirectDataContent))) {
    LOG(ERROR) << "Unknown content type";
    return false;
  }

  CBS wrapped_indirect_data_content, indirect_data_content;
  if (!CBS_get_asn1(&content_info, &wrapped_indirect_data_content,
                    CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
      !CBS_get_asn1(&wrapped_indirect_data_content, &indirect_data_content,
                    CBS_ASN1_SEQUENCE)) {
    LOG(ERROR) << "Failed to parse wrapped content";
    return false;
  }
  Parse_Authenticode_SpcIndirectDataContent(&indirect_data_content);

  return true;
}

bool Parse_Authenticode_ContentInfo(base::span<const uint8_t> data) {
  CBS cbs;
  CBS_init(&cbs, data.data(), data.size());

  CBS content_info;
  CBS content_type;
  if (!CBS_get_asn1(&cbs, &content_info, CBS_ASN1_SEQUENCE) ||
      !CBS_get_asn1(&content_info, &content_type, CBS_ASN1_OBJECT)) {
    LOG(ERROR) << "Failed to parse PKCS#7 sequence";
    return false;
  }

  if (!CBS_mem_equal(&content_type, kOidPKCS7SignedData,
                     sizeof(kOidPKCS7SignedData))) {
    LOG(ERROR) << "Not PKCS7SignedData";
    return false;
  }

  CBS wrapped_signed_data, signed_data;
  // See https://tools.ietf.org/html/rfc2315#section-9.1
  if (!CBS_get_asn1(&content_info, &wrapped_signed_data,
                    CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
      !CBS_get_asn1(&wrapped_signed_data, &signed_data, CBS_ASN1_SEQUENCE)) {
    LOG(ERROR) << "";
    return false;
  }

  if (!Parse_Authenticode_SignedData(&signed_data)) {
    LOG(ERROR) << "Failed to parse SignedData";
    return false;
  }
  return true;
}

int main(int argc, char** argv) {
  base::CommandLine::Init(0, nullptr);

  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  base::FilePath input = command_line->GetSwitchValuePath(switches::kInput);

  base::File pe_file(input, base::File::FLAG_OPEN | base::File::FLAG_READ);
  DWORD cert_count;
  if (!ImageEnumerateCertificates(pe_file.GetPlatformFile(),
                                  CERT_SECTION_TYPE_ANY, &cert_count, nullptr,
                                  0)) {
    PLOG(ERROR) << "ImageEnumerateCertificates";
    return -1;
  }

  std::vector<DWORD> cert_indices(cert_count);
  if (!ImageEnumerateCertificates(pe_file.GetPlatformFile(),
                                  CERT_SECTION_TYPE_ANY, &cert_count,
                                  &cert_indices[0], cert_indices.size())) {
    PLOG(ERROR) << "ImageEnumerateCertificates";
  }

  CHECK(cert_indices.size() == cert_count);

  for (auto idx : cert_indices) {
    WIN_CERTIFICATE cert = {};
    if (!ImageGetCertificateHeader(pe_file.GetPlatformFile(), idx, &cert)) {
      PLOG(ERROR) << "ImageGetCertificateHeader";
      return -1;
    }

    DWORD cert_length = sizeof(WIN_CERTIFICATE) + cert.dwLength;

    auto cert_data = base::HeapArray<uint8_t>::WithSize(cert_length);
    if (!ImageGetCertificateData(
            pe_file.GetPlatformFile(), idx,
            reinterpret_cast<WIN_CERTIFICATE*>(cert_data.data()),
            &cert_length)) {
      PLOG(ERROR) << "ImageGetCertificateData";
      return -1;
    }

    base::WriteFile(input.AddExtensionASCII(base::StringPrintf("%d.der", idx)),
                    cert_data.subspan(offsetof(WIN_CERTIFICATE, bCertificate)));

    Parse_Authenticode_ContentInfo(
        cert_data.subspan(offsetof(WIN_CERTIFICATE, bCertificate)));
  }

  return 0;
}
posted @ 2025-09-23 09:17  吱吱的笔记  阅读(31)  评论(0)    收藏  举报