Example #1
0
void DumpPeFile(hadesmem::Process const& process,
                hadesmem::PeFile const& pe_file,
                std::wstring const& path)
{
  std::wostream& out = GetOutputStreamW();

  ClearWarnForCurrentFile();

  WriteNewline(out);
  std::wstring const architecture_str{pe_file.Is64() ? L"64-Bit File: Yes"
                                                     : L"64-Bit File: No"};
  WriteNormal(out, architecture_str, 1);

  std::uint32_t const k1MB = (1U << 20);
  std::uint32_t const k100MB = k1MB * 100;
  if (pe_file.GetSize() > k100MB)
  {
    // Not actually unsupported, just want to flag large files for use in perf
    // testing.
    WriteNewline(out);
    WriteNormal(out, L"WARNING! File is over 100MB.", 0);
    // WarnForCurrentFile(WarningType::kUnsupported);
  }

  DumpHeaders(process, pe_file);

  DumpSections(process, pe_file);

  DumpOverlay(process, pe_file);

  DumpTls(process, pe_file);

  DumpExports(process, pe_file);

  bool has_new_bound_imports_any = false;
  DumpImports(process, pe_file, has_new_bound_imports_any);

  DumpBoundImports(process, pe_file, has_new_bound_imports_any);

  DumpRelocations(process, pe_file);

  if (!g_quiet && g_strings)
  {
    DumpStrings(process, pe_file);
  }

  HandleWarnings(path);
}
Example #2
0
//----------------------------------------------------------------------------------
// Name: EndCDATA
// Desc: Ends a CDATA block.
//----------------------------------------------------------------------------------
BOOL XMLWriter::EndCDATA()
{
    BOOL bResult = TRUE;
    bResult &= OutputStringFast( "]]>", 3 );
    bResult &= WriteNewline();
    m_bWriteCloseTagIndent = TRUE;
    return bResult;
}
Example #3
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);
  }
}
Example #4
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);
}
Example #5
0
//----------------------------------------------------------------------------------
// Name: EndComment
// Desc: Writes a comment close tag.
//----------------------------------------------------------------------------------
BOOL XMLWriter::EndComment()
{
    BOOL result = TRUE;
    result &= OutputStringFast( " -->", 4 );
    if( !m_bInlineComment )
        result &= WriteNewline();
    m_bWriteCloseTagIndent = !m_bInlineComment;
    return result;
}
Example #6
0
void DumpStrings(hadesmem::Process const& process,
                 hadesmem::PeFile const& pe_file)
{
  std::wostream& out = GetOutputStreamW();

  std::uint8_t* const file_beg = static_cast<std::uint8_t*>(pe_file.GetBase());
  void* const file_end = file_beg + pe_file.GetSize();

  // TODO: Fix to not require multiple passes.

  WriteNewline(out);
  WriteNormal(out, L"Narrow Strings:", 1);
  WriteNewline(out);
  DumpStringsImpl(process, pe_file, file_beg, file_end, false);

  WriteNewline(out);
  WriteNormal(out, L"Wide Strings (Pass 1):", 1);
  WriteNewline(out);
  DumpStringsImpl(process, pe_file, file_beg, file_end, true);

  WriteNewline(out);
  WriteNormal(out, L"Wide Strings (Pass 2):", 1);
  WriteNewline(out);
  DumpStringsImpl(process, pe_file, file_beg + 1, file_end, true);
}
Example #7
0
//----------------------------------------------------------------------------------
// Name: StartCDATA
// Desc: Starts a CDATA block.
//----------------------------------------------------------------------------------
BOOL XMLWriter::StartCDATA()
{
    BOOL bResult = TRUE;
    if( !m_bOpenTagFinished )
    {
        if( !EndOpenTag() ) 
            return FALSE;
        if( !WriteNewline() )
            return FALSE;
    }
    bResult &= WriteIndent();
    bResult &= OutputStringFast( "<![CDATA[", 9 );
    return bResult;
}
Example #8
0
//----------------------------------------------------------------------------------
// Name: EndElement
// Desc: Writes an element close tag corresponding with the most recent open tag.
//----------------------------------------------------------------------------------
BOOL XMLWriter::EndElement()
{
    const CHAR* strName = PopName();
    if( strName == nullptr )
        return FALSE;
    BOOL result = TRUE;
    if( !m_bOpenTagFinished )
    {
        m_bOpenTagFinished = TRUE;
        result &= OutputStringFast( " />", 3 );
        result &= WriteNewline();
        m_bWriteCloseTagIndent = TRUE;
        return result;
    }
    if( m_bWriteCloseTagIndent )
        result &= WriteIndent();
    result &= OutputStringFast( "</", 2 );
    result &= OutputString( strName );
    result &= OutputStringFast( ">", 1 );
    result &= WriteNewline();
    m_bWriteCloseTagIndent = TRUE;
    return result;
}
Example #9
0
////////////////////////////////////////////////////////////////////////////////
// WriteIndent
//
bool XmlWriter::WriteIndent()
{
    if (mbFormatPretty) {
        // Write a newline, regardless of how many spaces follow.
        // Don't write a newline if we have written nothing at all yet to the file.
        if (mnCharCount) {
            if (!WriteNewline()) 
                return false;
        }

        if (!WriteWhitespace(mnIndentLevel * mnIndentSpaces))
            return false;
    }

    return true;
}
Example #10
0
//----------------------------------------------------------------------------------
// Name: StartComment
// Desc: Writes the beginning of an XML comment tag.
//----------------------------------------------------------------------------------
BOOL XMLWriter::StartComment( BOOL bInline )
{
    if( !m_bOpenTagFinished )
    {
        if( !EndOpenTag() ) 
            return FALSE;
        if( !bInline && !WriteNewline() )
            return FALSE;
    }
    BOOL result = TRUE;
    if( !bInline )
        result &= WriteIndent();
    result &= OutputStringFast( "<!-- ", 5 );
    m_bOpenTagFinished = TRUE;
    m_bWriteCloseTagIndent = FALSE;
    m_bInlineComment = bInline;
    return result;
}
Example #11
0
//----------------------------------------------------------------------------------
// Name: StartElement
// Desc: Writes the beginning of an XML open tag.
//----------------------------------------------------------------------------------
BOOL XMLWriter::StartElement( const CHAR* strName )
{
    if( !m_bOpenTagFinished )
    {
        if( !EndOpenTag() ) 
            return FALSE;
        if( !WriteNewline() )
            return FALSE;
    }
    BOOL result = TRUE;
    result &= WriteIndent();
    PushName( strName );
    result &= OutputStringFast( "<", 1 );
    result &= OutputString( strName );
    m_bOpenTagFinished = FALSE;
    m_bWriteCloseTagIndent = FALSE;
    return result;
}
Example #12
0
void DumpPeFile(hadesmem::Process const& process,
                hadesmem::PeFile const& pe_file,
                std::wstring const& path)
{
  std::wostream& out = std::wcout;

  ClearWarnForCurrentFile();

  std::uint32_t const k1MB = (1U << 20);
  std::uint32_t const k100MB = k1MB * 100;
  if (pe_file.GetSize() > k100MB)
  {
    // Not actually unsupported, just want to flag large files.
    WriteNewline(out);
    WriteNormal(out, L"WARNING! File is over 100MB.", 0);
    WarnForCurrentFile(WarningType::kUnsupported);
  }

  DumpHeaders(process, pe_file);

  DumpSections(process, pe_file);

  DumpTls(process, pe_file);

  DumpExports(process, pe_file);

  bool has_new_bound_imports_any = false;
  DumpImports(process, pe_file, has_new_bound_imports_any);

  DumpBoundImports(process, pe_file, has_new_bound_imports_any);

  DumpRelocations(process, pe_file);

  DumpStrings(process, pe_file);

  HandleWarnings(path);
}
Example #13
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);
    }
  }
}
Example #14
0
void DumpMemory(hadesmem::Process const& process, bool continue_on_error)
{
  std::wostream& out = GetOutputStreamW();

  WriteNewline(out);
  WriteNormal(out, "Dumping image memory to disk.", 0);

  hadesmem::ModuleList modules(process);
  for (auto const& module : modules)
  {
    WriteNormal(out, "Checking for valid headers.", 0);
    try
    {
      hadesmem::PeFile const pe_file(process,
                                     module.GetHandle(),
                                     hadesmem::PeFileType::Image,
                                     static_cast<DWORD>(module.GetSize()));
      hadesmem::NtHeaders nt_headers(process, pe_file);
    }
    catch (std::exception const& /*e*/)
    {
      WriteNormal(out, "WARNING! Invalid headers.", 0);
      return;
    }

    WriteNormal(out, L"Reading memory.", 1);
    auto raw = hadesmem::ReadVectorEx<std::uint8_t>(
      process,
      module.GetHandle(),
      module.GetSize(),
      hadesmem::ReadFlags::kZeroFillReserved);
    hadesmem::Process const local_process(::GetCurrentProcessId());
    hadesmem::PeFile const pe_file(local_process,
                                   raw.data(),
                                   hadesmem::PeFileType::Image,
                                   static_cast<DWORD>(raw.size()));
    hadesmem::NtHeaders nt_headers(local_process, pe_file);

    WriteNormal(out, L"Copying headers.", 1);
    std::vector<std::uint8_t> raw_new;
    std::copy(std::begin(raw),
              std::begin(raw) + nt_headers.GetSizeOfHeaders(),
              std::back_inserter(raw_new));

    WriteNormal(out, L"Copying section data.", 1);
    hadesmem::SectionList const sections(local_process, pe_file);
    std::vector<std::pair<DWORD, DWORD>> raw_datas;
    for (auto const& section : sections)
    {
      auto const section_size =
        (std::max)(section.GetVirtualSize(), section.GetSizeOfRawData());
      auto const ptr_raw_data_new =
        section.GetPointerToRawData() < raw_new.size()
          ? static_cast<DWORD>(
              RoundUp(raw_new.size(), nt_headers.GetFileAlignment()))
          : section.GetPointerToRawData();
      raw_datas.emplace_back(ptr_raw_data_new, section_size);

      if (ptr_raw_data_new > raw_new.size())
      {
        raw_new.resize(ptr_raw_data_new);
      }

      auto const raw_data = raw.data() + section.GetVirtualAddress();
      auto const raw_data_end = raw_data + section_size;
      raw_new.reserve(raw_new.size() + section_size);
      std::copy(raw_data, raw_data_end, std::back_inserter(raw_new));
    }

    HADESMEM_DETAIL_ASSERT(raw_new.size() <
                           (std::numeric_limits<DWORD>::max)());
    hadesmem::PeFile const pe_file_new(local_process,
                                       raw_new.data(),
                                       hadesmem::PeFileType::Data,
                                       static_cast<DWORD>(raw_new.size()));

    WriteNormal(out, L"Fixing NT headers.", 1);
    hadesmem::NtHeaders nt_headers_new(local_process, pe_file_new);
    nt_headers_new.SetImageBase(
      reinterpret_cast<ULONG_PTR>(module.GetHandle()));
    nt_headers_new.UpdateWrite();

    WriteNormal(out, L"Fixing section headers.", 1);
    hadesmem::SectionList sections_new(local_process, pe_file_new);
    std::size_t n = 0;
    for (auto& section : sections_new)
    {
      section.SetPointerToRawData(raw_datas[n].first);
      section.SetSizeOfRawData(raw_datas[n].second);
      section.UpdateWrite();
      ++n;
    }

    WriteNormal(out, L"Fixing imports.", 1);
    hadesmem::ImportDirList const import_dirs(local_process, pe_file);
    hadesmem::ImportDirList const import_dirs_new(local_process, pe_file_new);
    auto i = std::begin(import_dirs), j = std::begin(import_dirs_new);
    bool thunk_mismatch = false;
    for (; i != std::end(import_dirs) && j != std::end(import_dirs_new);
         ++i, ++j)
    {
      hadesmem::ImportThunkList const import_thunks(
        local_process, pe_file, i->GetOriginalFirstThunk());
      hadesmem::ImportThunkList import_thunks_new(
        local_process, pe_file_new, j->GetFirstThunk());
      auto a = std::begin(import_thunks);
      auto b = std::begin(import_thunks_new);
      for (; a != std::end(import_thunks) && b != std::end(import_thunks_new);
           ++a, ++b)
      {
        b->SetFunction(a->GetFunction());
        b->UpdateWrite();
      }
      thunk_mismatch = thunk_mismatch || ((a != std::end(import_thunks)) ^
                                          (b != std::end(import_thunks_new)));
    }
    bool const dir_mismatch =
      (i != std::end(import_dirs)) ^ (j != std::end(import_dirs));

    WriteNormal(out, L"Writing file.", 1);
    auto const proc_path = hadesmem::GetPath(process);
    auto const proc_name = proc_path.substr(proc_path.rfind(L'\\') + 1);
    auto const proc_pid_str = std::to_wstring(process.GetId());
    std::wstring dump_path;
    std::uint32_t c = 0;
    do
    {
      dump_path = proc_name + L"_" + proc_pid_str + L"_" + module.GetName() +
                  L"_" + std::to_wstring(c++) + L".dmp";
    } while (hadesmem::detail::DoesFileExist(dump_path) && c < 10);
    auto const dump_file = hadesmem::detail::OpenFile<char>(
      dump_path, std::ios::out | std::ios::binary);
    if (!*dump_file)
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error()
                                      << hadesmem::ErrorString(
                                        "Unable to open dump file."));
    }
    if (!dump_file->write(reinterpret_cast<char const*>(raw_new.data()),
                          raw_new.size()))
    {
      HADESMEM_DETAIL_THROW_EXCEPTION(hadesmem::Error()
                                      << hadesmem::ErrorString(
                                        "Unable to write to dump file."));
    }

    try
    {
      if (dir_mismatch)
      {
        HADESMEM_DETAIL_THROW_EXCEPTION(
          hadesmem::Error()
          << hadesmem::ErrorString("Mismatch in import dir processing."));
      }

      if (thunk_mismatch)
      {
        HADESMEM_DETAIL_THROW_EXCEPTION(
          hadesmem::Error()
          << hadesmem::ErrorString("Mismatch in import thunk processing."));
      }
    }
    catch (...)
    {
      if (continue_on_error)
      {
        std::cerr << "\nError!\n"
                  << boost::current_exception_diagnostic_information() << '\n';

        std::wcerr << "\nCurrent process: " << hadesmem::GetPathNative(process)
                   << "\nCurrent module: " << module.GetName() << "\n";
      }
      else
      {
        throw;
      }
    }
  }
}
Example #15
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);
      }
    }
  }
}
Example #16
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);
      }
    }
  }
}