Beispiel #1
0
void DumpSections(hadesmem::Process const& process,
                  hadesmem::PeFile const& pe_file)
{
  hadesmem::SectionList sections(process, pe_file);

  std::wostream& out = GetOutputStreamW();

  if (std::begin(sections) != std::end(sections))
  {
    WriteNewline(out);
    WriteNormal(out, L"Sections:", 1);
  }
  else
  {
    // Other checks on number of sections are done as part of header handling.
    hadesmem::NtHeaders const nt_hdrs(process, pe_file);
    if (nt_hdrs.GetNumberOfSections())
    {
      WriteNewline(out);
      WriteNormal(out, L"WARNING! Section list is inavlid.", 1);
      WarnForCurrentFile(WarningType::kUnsupported);
    }
  }

  for (auto const& s : sections)
  {
    WriteNewline(out);
    if (s.IsVirtual())
    {
      WriteNormal(out, L"WARNING! Section is virtual.", 2);
      WarnForCurrentFile(WarningType::kSuspicious);
    }
    HandleLongOrUnprintableString(
      L"name", L"section name", 2, WarningType::kSuspicious, s.GetName());
    WriteNamedHex(out, L"VirtualAddress", s.GetVirtualAddress(), 2);
    WriteNamedHex(out, L"VirtualSize", s.GetVirtualSize(), 2);
    WriteNamedHex(out, L"PointerToRawData", s.GetPointerToRawData(), 2);
    WriteNamedHex(out, L"SizeOfRawData", s.GetSizeOfRawData(), 2);
    WriteNamedHex(out, L"PointerToRelocations", s.GetPointerToRelocations(), 2);
    WriteNamedHex(out, L"PointerToLinenumbers", s.GetPointerToLinenumbers(), 2);
    WriteNamedHex(out, L"NumberOfRelocations", s.GetNumberOfRelocations(), 2);
    WriteNamedHex(out, L"NumberOfLinenumbers", s.GetNumberOfLinenumbers(), 2);
    WriteNamedHex(out, L"Characteristics", s.GetCharacteristics(), 2);
  }
}
Beispiel #2
0
void DumpTls(hadesmem::Process const& process, hadesmem::PeFile const& pe_file)
{
  std::unique_ptr<hadesmem::TlsDir const> tls_dir;
  try
  {
    tls_dir = std::make_unique<hadesmem::TlsDir>(process, pe_file);
  }
  catch (std::exception const& /*e*/)
  {
    return;
  }

  std::wostream& out = GetOutputStreamW();

  WriteNewline(out);
  WriteNormal(out, L"TLS:", 1);

  WriteNewline(out);
  WriteNamedHex(
    out, L"StartAddressOfRawData", tls_dir->GetStartAddressOfRawData(), 2);
  WriteNamedHex(
    out, L"EndAddressOfRawData", tls_dir->GetEndAddressOfRawData(), 2);
  WriteNamedHex(out, L"AddressOfIndex", tls_dir->GetAddressOfIndex(), 2);
  WriteNamedHex(
    out, L"AddressOfCallBacks", tls_dir->GetAddressOfCallBacks(), 2);
  if (tls_dir->GetAddressOfCallBacks())
  {
    std::vector<ULONGLONG> callbacks;
    try
    {
      tls_dir->GetCallbacks(std::back_inserter(callbacks));
    }
    catch (std::exception const& /*e*/)
    {
      WriteNormal(out, L"WARNING! TLS callbacks are inavlid.", 2);
      WarnForCurrentFile(WarningType::kSuspicious);
    }
    for (auto const& c : callbacks)
    {
      WriteNamedHex(out, L"Callback", static_cast<DWORD_PTR>(c), 2);
    }
  }
  WriteNamedHex(out, L"SizeOfZeroFill", tls_dir->GetSizeOfZeroFill(), 2);
  WriteNamedHex(out, L"Characteristics", tls_dir->GetCharacteristics(), 2);
}
Beispiel #3
0
void DumpBoundImports(hadesmem::Process const& process,
                      hadesmem::PeFile const& pe_file,
                      bool has_new_bound_imports_any)
{
  std::wostream& out = GetOutputStreamW();

  if (!HasBoundImportDir(process, pe_file))
  {
    // Sample: dllmaxvals.dll (Corkami PE Corpus)
    if (has_new_bound_imports_any)
    {
      WriteNewline(out);
      WriteNormal(
        out,
        L"WARNING! No bound import directory on file with an import dir "
        L"indicating the presence of a bound import dir.",
        1);
      WarnForCurrentFile(WarningType::kSuspicious);
    }

    return;
  }

  if (!has_new_bound_imports_any)
  {
    WriteNewline(out);
    WriteNormal(
      out,
      L"WARNING! Seemingly valid bound import directory on file with an "
      L"import dir indicating no new bound import dir.",
      1);
    WarnForCurrentFile(WarningType::kSuspicious);
    return;
  }

  hadesmem::BoundImportDescriptorList const bound_import_descs(process,
                                                               pe_file);

  if (std::begin(bound_import_descs) == std::end(bound_import_descs))
  {
    WriteNewline(out);
    WriteNormal(out, L"WARNING! Empty or invalid bound import directory.", 1);
    WarnForCurrentFile(WarningType::kSuspicious);
    return;
  }

  WriteNewline(out);
  WriteNormal(out, L"Bound Import Descriptors:", 1);

  std::uint32_t num_descs = 0U;
  for (auto const& desc : bound_import_descs)
  {
    WriteNewline(out);

    if (num_descs++ == 1000)
    {
      WriteNormal(out,
                  L"WARNING! Processed 1000 bound import descriptors. Stopping "
                  L"early to avoid resource exhaustion attacks.",
                  2);
      WarnForCurrentFile(WarningType::kUnsupported);
      break;
    }

    DWORD const time_date_stamp = desc.GetTimeDateStamp();
    std::wstring time_date_stamp_str;
    if (!ConvertTimeStamp(time_date_stamp, time_date_stamp_str))
    {
      WriteNormal(out, L"WARNING! Invalid timestamp.", 2);
      WarnForCurrentFile(WarningType::kSuspicious);
    }
    WriteNamedHexSuffix(
      out, L"TimeDateStamp", time_date_stamp, time_date_stamp_str, 2);
    WriteNamedHex(out, L"OffsetModuleName", desc.GetOffsetModuleName(), 2);
    WriteNamedNormal(out, L"ModuleName", desc.GetModuleName().c_str(), 2);
    WriteNamedHex(out,
                  L"NumberOfModuleForwarderRefs",
                  desc.GetNumberOfModuleForwarderRefs(),
                  2);
    hadesmem::BoundImportForwarderRefList const forwarder_refs(
      process, pe_file, desc);
    if (std::begin(forwarder_refs) != std::end(forwarder_refs))
    {
      WriteNewline(out);
      WriteNormal(out, L"Module Forwarder Refs:", 2);
    }
    for (auto const& forwarder : forwarder_refs)
    {
      WriteNewline(out);

      DWORD const fwd_time_date_stamp = forwarder.GetTimeDateStamp();
      std::wstring fwd_time_date_stamp_str;
      if (!ConvertTimeStamp(fwd_time_date_stamp, fwd_time_date_stamp_str))
      {
        WriteNormal(out, L"WARNING! Invalid timestamp.", 3);
        WarnForCurrentFile(WarningType::kSuspicious);
      }
      WriteNamedHexSuffix(
        out, L"TimeDateStamp", fwd_time_date_stamp, fwd_time_date_stamp_str, 3);
      WriteNamedHex(
        out, L"OffsetModuleName", forwarder.GetOffsetModuleName(), 3);
      WriteNamedNormal(
        out, L"ModuleName", forwarder.GetModuleName().c_str(), 3);
      WriteNamedHex(out, L"Reserved", forwarder.GetReserved(), 3);
    }
  }
}
Beispiel #4
0
void DumpExports(hadesmem::Process const& process,
                 hadesmem::PeFile const& pe_file)
{
  std::unique_ptr<hadesmem::ExportDir const> export_dir;
  try
  {
    export_dir = std::make_unique<hadesmem::ExportDir const>(process, pe_file);
  }
  catch (std::exception const& /*e*/)
  {
    return;
  }

  std::wostream& out = std::wcout;

  WriteNewline(out);
  WriteNormal(out, L"Export Dir:", 1);
  WriteNewline(out);

  WriteNamedHex(out, L"Characteristics", export_dir->GetCharacteristics(), 2);
  DWORD const time_date_stamp = export_dir->GetTimeDateStamp();
  std::wstring time_date_stamp_str;
  if (!ConvertTimeStamp(time_date_stamp, time_date_stamp_str))
  {
    WriteNormal(out, L"WARNING! Invalid timestamp.", 2);
    WarnForCurrentFile(WarningType::kSuspicious);
  }
  WriteNamedHexSuffix(
    out, L"TimeDateStamp", time_date_stamp, time_date_stamp_str, 2);
  WriteNamedHex(out, L"MajorVersion", export_dir->GetMajorVersion(), 2);
  WriteNamedHex(out, L"MinorVersion", export_dir->GetMinorVersion(), 2);
  WriteNamedHex(out, L"Name (Raw)", export_dir->GetNameRaw(), 2);
  // Name is not guaranteed to be valid.
  // Sample: dllord.dll (Corkami PE Corpus)
  try
  {
    auto name = export_dir->GetName();
    HandleLongOrUnprintableString(
      L"Name", L"export module name", 2, WarningType::kSuspicious, name);
  }
  catch (std::exception const& /*e*/)
  {
    WriteNormal(out, L"WARNING! Failed to read export dir name.", 2);
    WarnForCurrentFile(WarningType::kSuspicious);
  }
  WriteNamedHex(out, L"OrdinalBase", export_dir->GetOrdinalBase(), 2);
  WriteNamedHex(
    out, L"NumberOfFunctions", export_dir->GetNumberOfFunctions(), 2);
  WriteNamedHex(out, L"NumberOfNames", export_dir->GetNumberOfNames(), 2);
  WriteNamedHex(
    out, L"AddressOfFunctions", export_dir->GetAddressOfFunctions(), 2);
  WriteNamedHex(out, L"AddressOfNames", export_dir->GetAddressOfNames(), 2);
  WriteNamedHex(
    out, L"AddressOfNameOrdinals", export_dir->GetAddressOfNameOrdinals(), 2);

  std::set<std::string> export_names;

  hadesmem::ExportList const exports(process, pe_file);
  if (std::begin(exports) != std::end(exports))
  {
    WriteNewline(out);
    WriteNormal(out, L"Exports:", 2);
  }
  else
  {
    WriteNewline(out);
    WriteNormal(out, L"WARNING! Empty or invalid export list.", 2);
    WarnForCurrentFile(WarningType::kSuspicious);
  }

  std::uint32_t num_exports = 0U;
  for (auto const& e : exports)
  {
    WriteNewline(out);

    // Some legitimate DLLs have well over 1000 exports (e.g. ntdll.dll).
    if (num_exports++ == 10000)
    {
      WriteNormal(
        out,
        L"WARNING! Processed 10000 exports. Stopping early to avoid resource "
        L"exhaustion attacks.",
        2);
      WarnForCurrentFile(WarningType::kSuspicious);
      break;
    }

    if (e.ByName())
    {
      auto const name = e.GetName();
      // Sample: dllweirdexp.dll
      HandleLongOrUnprintableString(
        L"Name", L"export name", 3, WarningType::kSuspicious, name);

      // PE files can have duplicate exported function names (or even have them
      // all identical) because the import hint is used to check the name first
      // before performing a search.
      // Sample: None ("Import name hint" section of "Undocumented PECOFF"
      // whitepaper).
      if (!export_names.insert(name).second)
      {
        WriteNormal(out, L"WARNING! Detected duplicate export name.", 3);
        WarnForCurrentFile(WarningType::kSuspicious);
      }
    }

    WriteNamedHex(out, L"ProcedureNumber", e.GetProcedureNumber(), 3);
    WriteNamedHex(out, L"OrdinalNumber", e.GetOrdinalNumber(), 3);

    if (e.IsForwarded())
    {
      WriteNamedNormal(out, L"Forwarder", e.GetForwarder().c_str(), 3);
      WriteNamedNormal(
        out, L"ForwarderModule", e.GetForwarderModule().c_str(), 3);
      WriteNamedNormal(
        out, L"ForwarderFunction", e.GetForwarderFunction().c_str(), 3);
      WriteNamedNormal(
        out, L"IsForwardedByOrdinal", e.IsForwardedByOrdinal(), 3);
      if (e.IsForwardedByOrdinal())
      {
        try
        {
          WriteNamedHex(out, L"ForwarderOrdinal", e.GetForwarderOrdinal(), 3);
        }
        catch (std::exception const& /*e*/)
        {
          WriteNormal(out, L"WARNING! ForwarderOrdinal invalid.", 3);
          WarnForCurrentFile(WarningType::kSuspicious);
        }
      }
    }
    else
    {
      auto const ep_rva = e.GetRva();
      WriteNamedHex(out, L"RVA", e.GetRva(), 3);
      auto const ep_va = e.GetVa();
      WriteNamedHex(out, L"VA", reinterpret_cast<std::uintptr_t>(ep_va), 3);

      if (ep_va)
      {
        DisassembleEp(process, pe_file, ep_rva, ep_va, 4);
      }
      else
      {
        WriteNormal(out, L"WARNING! Export VA is invalid.", 3);
        WarnForCurrentFile(WarningType::kSuspicious);
      }
    }
  }
}
Beispiel #5
0
void DumpRelocations(hadesmem::Process const& process,
                     hadesmem::PeFile const& pe_file)
{
  if (!HasRelocationsDir(process, pe_file))
  {
    return;
  }

  std::wostream& out = GetOutputStreamW();

  WriteNewline(out);

  hadesmem::RelocationBlockList const reloc_blocks(process, pe_file);
  if (std::begin(reloc_blocks) != std::end(reloc_blocks))
  {
    WriteNormal(out, L"Relocation Blocks:", 1);
  }
  else
  {
    // Sample: dllmaxvals.dll (Corkami PE Corpus)
    // Sample: fakerelocs.exe (Corkami PE Corpus)
    WriteNormal(out, L"WARNING! Relocation block list is invalid.", 1);
    WarnForCurrentFile(WarningType::kSuspicious);
  }

  for (auto const& block : reloc_blocks)
  {
    WriteNewline(out);

    auto const va = block.GetVirtualAddress();
    WriteNamedHex(out, L"VirtualAddress", va, 2);
    auto const size = block.GetSizeOfBlock();
    WriteNamedHex(out, L"SizeOfBlock", block.GetSizeOfBlock(), 2);

    WriteNewline(out);

    if (!size)
    {
      WriteNormal(out, L"WARNING! Detected zero sized relocation block.", 2);
      WarnForCurrentFile(WarningType::kUnsupported);
      continue;
    }

    WriteNormal(out, L"Relocations:", 2);

    hadesmem::RelocationList const relocs(process,
                                          pe_file,
                                          block.GetRelocationDataStart(),
                                          block.GetNumberOfRelocations());
    for (auto const& reloc : relocs)
    {
      WriteNewline(out);

      auto const type = reloc.GetType();
      WriteNamedHex(out, L"Type", type, 3);
      WriteNamedHex(out, L"Offset", reloc.GetOffset(), 3);

      // 11 = IMAGE_REL_BASED_HIGH3ADJ
      if (type > 11)
      {
        WriteNormal(out, L"WARNING! Unknown relocation type.", 3);
        WarnForCurrentFile(WarningType::kUnsupported);
      }
    }
  }
}