コード例 #1
0
ファイル: main.cpp プロジェクト: whb224117/hadesmem
void HandleLongOrUnprintableString(std::wstring const& name,
                                   std::wstring const& description,
                                   std::size_t tabs,
                                   WarningType warning_type,
                                   std::string value)
{
  std::wostream& out = GetOutputStreamW();

  // TODO: Fix perf for extremely long names. Instead of reading indefinitely
  // and then checking the size after the fact, we should perform a bounded
  // read.
  auto const unprintable = FindFirstUnprintableClassicLocale(value);
  std::size_t const kMaxNameLength = 1024;
  if (unprintable != std::string::npos)
  {
    WriteNormal(out,
                L"WARNING! Detected unprintable " + description +
                  L". Truncating.",
                tabs);
    WarnForCurrentFile(warning_type);
    value.erase(unprintable);
  }
  else if (value.size() > kMaxNameLength)
  {
    WriteNormal(out,
                L"WARNING! Detected suspiciously long " + description +
                  L". Truncating.",
                tabs);
    WarnForCurrentFile(warning_type);
    value.erase(kMaxNameLength);
  }
  WriteNamedNormal(out, name, value.c_str(), tabs);
}
コード例 #2
0
ファイル: main.cpp プロジェクト: lvous/hadesmem
void HandleLongOrUnprintableString(std::wstring const& name,
                                   std::wstring const& description,
                                   std::size_t tabs,
                                   WarningType warning_type,
                                   std::string value)
{
  std::wostream& out = std::wcout;

  auto const unprintable = FindFirstUnprintableClassicLocale(value);
  std::size_t const kMaxNameLength = 1024;
  if (unprintable != std::string::npos)
  {
    WriteNormal(out,
                L"WARNING! Detected unprintable " + description +
                  L". Truncating.",
                tabs);
    WarnForCurrentFile(warning_type);
    value.erase(unprintable);
  }
  else if (value.size() > kMaxNameLength)
  {
    WriteNormal(out,
                L"WARNING! Detected suspiciously long " + description +
                  L". Truncating.",
                tabs);
    WarnForCurrentFile(warning_type);
    value.erase(kMaxNameLength);
  }
  WriteNamedNormal(out, name, value.c_str(), tabs);
}
コード例 #3
0
ファイル: disassemble.cpp プロジェクト: GliderPro/hadesmem
void DisassembleEp(hadesmem::Process const& process,
                   hadesmem::PeFile const& pe_file,
                   std::uintptr_t ep_rva,
                   void* ep_va,
                   std::size_t tabs)
{
  if (!ep_va)
  {
    return;
  }

  std::wostream& out = GetOutputStreamW();

  // Get the number of bytes from the EP to the end of the file.
  std::size_t max_buffer_size = GetBytesToEndOfFile(pe_file, ep_va);
  // Clamp the amount of data read to the theoretical maximum.
  std::size_t const kMaxInstructions = 10U;
  std::size_t const kMaxInstructionLen = 15U;
  std::size_t const kMaxInstructionsBytes =
    kMaxInstructions * kMaxInstructionLen;
  max_buffer_size = (std::min)(max_buffer_size, kMaxInstructionsBytes);
  auto const disasm_buf =
    hadesmem::ReadVector<std::uint8_t>(process, ep_va, max_buffer_size);
  std::uint64_t const ip = hadesmem::GetRuntimeBase(process, pe_file) + ep_rva;

  ud_t ud_obj;
  ud_init(&ud_obj);
  ud_set_input_buffer(&ud_obj, disasm_buf.data(), max_buffer_size);
  ud_set_syntax(&ud_obj, UD_SYN_INTEL);
  ud_set_pc(&ud_obj, ip);
  ud_set_mode(&ud_obj, pe_file.Is64() ? 64 : 32);

  // Be pessimistic. Use the minimum theoretical amount of instrutions we could
  // fit in our buffer.
  std::size_t const instruction_count = max_buffer_size / kMaxInstructionLen;
  for (std::size_t i = 0U; i < instruction_count; ++i)
  {
    std::uint32_t const len = ud_disassemble(&ud_obj);
    if (len == 0)
    {
      WriteNormal(out, L"WARNING! Disassembly failed.", tabs);
      // If we can't disassemble at least 5 instructions there's probably
      // something strange about the function. Even in the case of a nullsub
      // there is typically some INT3 or NOP padding after it...
      WarnForCurrentFile(i < 5U ? WarningType::kUnsupported
                                : WarningType::kSuspicious);
      break;
    }

    char const* const asm_str = ud_insn_asm(&ud_obj);
    HADESMEM_DETAIL_ASSERT(asm_str);
    char const* const asm_bytes_str = ud_insn_hex(&ud_obj);
    HADESMEM_DETAIL_ASSERT(asm_bytes_str);
    auto const diasm_line =
      hadesmem::detail::MultiByteToWideChar(asm_str) + L" (" +
      hadesmem::detail::MultiByteToWideChar(asm_bytes_str) + L")";
    WriteNormal(out, diasm_line, tabs);
  }
}
コード例 #4
0
ファイル: sections.cpp プロジェクト: karliky/hadesmem
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);
  }
}
コード例 #5
0
ファイル: tls.cpp プロジェクト: GliderPro/hadesmem
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);
}
コード例 #6
0
ファイル: main.cpp プロジェクト: lvous/hadesmem
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);
}
コード例 #7
0
ファイル: bound_imports.cpp プロジェクト: GliderPro/hadesmem
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);
    }
  }
}
コード例 #8
0
ファイル: exports.cpp プロジェクト: lvous/hadesmem
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);
      }
    }
  }
}
コード例 #9
0
ファイル: relocations.cpp プロジェクト: GliderPro/hadesmem
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);
      }
    }
  }
}