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); } }
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); } } } }