int main(int argc, char* argv[]) { try { std::cout << "HadesMem Dumper [" << HADESMEM_VERSION_STRING << "]\n"; TCLAP::CmdLine cmd("PE file format dumper", ' ', HADESMEM_VERSION_STRING); TCLAP::ValueArg<DWORD> pid_arg( "", "pid", "Target process id", false, 0, "DWORD"); TCLAP::ValueArg<std::string> name_arg( "", "name", "Target process name", false, "", "string"); TCLAP::MultiArg<std::string> path_arg( "", "path", "Target path (file or directory)", false, "string"); TCLAP::SwitchArg all_arg("", "all", "No target, dump everything"); std::vector<TCLAP::Arg*> xor_args{&pid_arg, &name_arg, &path_arg, &all_arg}; cmd.xorAdd(xor_args); TCLAP::SwitchArg warned_arg( "", "warned", "Dump list of files which cause warnings", cmd); TCLAP::ValueArg<std::string> warned_file_arg( "", "warned-file", "Dump warned list to file instead of stdout", false, "", "string", cmd); TCLAP::SwitchArg warned_file_dynamic_arg( "", "warned-file-dynamic", "Dump warnings to file on the fly rather than at the end", cmd); TCLAP::SwitchArg quiet_arg( "", "quiet", "Only output status messages (no dumping)", cmd); TCLAP::SwitchArg memonly_arg("", "memonly", "Only do PE memory dumps", cmd); TCLAP::ValueArg<int> warned_type_arg("", "warned-type", "Filter warned file using warned type", false, -1, "int", cmd); TCLAP::ValueArg<DWORD> threads_arg( "", "threads", "Number of threads", false, 0, "size_t", cmd); TCLAP::ValueArg<DWORD> queue_factor_arg( "", "queue-factor", "Thread queue factor", false, 0, "size_t", cmd); TCLAP::SwitchArg strings_arg("", "strings", "Dump strings", cmd); cmd.parse(argc, argv); g_quiet = quiet_arg.isSet(); g_strings = strings_arg.isSet(); SetWarningsEnabled(warned_arg.getValue()); SetDynamicWarningsEnabled(warned_file_dynamic_arg.getValue()); if (warned_file_arg.isSet()) { SetWarnedFilePath( hadesmem::detail::MultiByteToWideChar(warned_file_arg.getValue())); } if (GetDynamicWarningsEnabled() && GetWarnedFilePath().empty()) { HADESMEM_DETAIL_THROW_EXCEPTION( hadesmem::Error() << hadesmem::ErrorString( "Please specify a file path for dynamic warnings.")); } int const warned_type = warned_type_arg.getValue(); switch (warned_type) { case static_cast<int>(WarningType::kSuspicious): SetWarnedType(WarningType::kSuspicious); break; case static_cast<int>(WarningType::kUnsupported): SetWarnedType(WarningType::kUnsupported); break; case static_cast<int>(WarningType::kAll): SetWarnedType(WarningType::kAll); break; default: HADESMEM_DETAIL_THROW_EXCEPTION( hadesmem::Error() << hadesmem::ErrorString("Unknown warned type.")); break; } try { hadesmem::GetSeDebugPrivilege(); std::wcout << "\nAcquired SeDebugPrivilege.\n"; } catch (std::exception const& /*e*/) { std::wcout << "\nFailed to acquire SeDebugPrivilege.\n"; } auto const threads = threads_arg.isSet() ? threads_arg.getValue() : 1; auto const queue_factor = queue_factor_arg.isSet() ? queue_factor_arg.getValue() : 1; hadesmem::detail::ThreadPool thread_pool{threads, queue_factor}; if (pid_arg.isSet()) { DWORD const pid = pid_arg.getValue(); hadesmem::ProcessList const processes; auto iter = std::find_if(std::begin(processes), std::end(processes), [pid](hadesmem::ProcessEntry const& process_entry) { return process_entry.GetId() == pid; }); if (iter != std::end(processes)) { DumpProcessEntry(*iter, memonly_arg.isSet()); } else { HADESMEM_DETAIL_THROW_EXCEPTION( hadesmem::Error() << hadesmem::ErrorString("Failed to find requested process.")); } } else if (name_arg.isSet()) { auto const proc_name = hadesmem::detail::MultiByteToWideChar(name_arg.getValue()); auto const proc_entry = hadesmem::GetProcessEntryByName(proc_name, false); DumpProcessEntry(proc_entry, memonly_arg.isSet()); } else if (path_arg.isSet()) { // TODO: Use backup semantics flags and try to get backup privilege in // order to make directory enumeration find more files. auto const path_args = path_arg.getValue(); for (auto const& path : path_args) { auto const path_wide = hadesmem::detail::MultiByteToWideChar(path); if (hadesmem::detail::IsDirectory(path_wide)) { DumpDir(path_wide, thread_pool); } else { DumpFile(path_wide); } } } else { DumpThreads(static_cast<DWORD>(-1)); DumpProcesses(memonly_arg.isSet()); std::wcout << "\nFiles:\n"; // TODO: Enumerate all volumes. std::wstring const self_path = hadesmem::detail::GetSelfPath(); std::wstring const root_path = hadesmem::detail::GetRootPath(self_path); DumpDir(root_path, thread_pool); } thread_pool.WaitForEmpty(); if (GetWarningsEnabled()) { if (!GetWarnedFilePath().empty() && !GetDynamicWarningsEnabled()) { std::unique_ptr<std::wfstream> warned_file_ptr( hadesmem::detail::OpenFile<wchar_t>(GetWarnedFilePath(), std::ios::out)); std::wfstream& warned_file = *warned_file_ptr; if (!warned_file) { HADESMEM_DETAIL_THROW_EXCEPTION( hadesmem::Error() << hadesmem::ErrorString("Failed to open warned file for output.")); } DumpWarned(warned_file); } else { DumpWarned(std::wcout); } } return 0; } catch (...) { std::cerr << "\nError!\n" << boost::current_exception_diagnostic_information() << '\n'; return 1; } }
int p11_tool_main (int argc, char *argv[], const p11_tool_command *commands) { const p11_tool_command *fallback = NULL; char *command = NULL; bool want_help = false; bool skip; int in, out; int i; /* * Parse the global options. We rearrange the options as * necessary, in order to pass relevant options through * to the commands, but also have them take effect globally. */ for (in = 1, out = 1; in < argc; in++, out++) { /* The non-option is the command, take it out of the arguments */ if (argv[in][0] != '-') { if (!command) { skip = true; command = argv[in]; } else { skip = false; } /* The global long options */ } else if (argv[in][1] == '-') { skip = false; if (strcmp (argv[in], "--") == 0) { if (!command) { p11_message ("no command specified"); return 2; } else { break; } } else if (strcmp (argv[in], "--verbose") == 0) { verbose_arg (); } else if (strcmp (argv[in], "--quiet") == 0) { quiet_arg (); } else if (strcmp (argv[in], "--help") == 0) { want_help = true; } else if (!command) { p11_message ("unknown global option: %s", argv[in]); return 2; } /* The global short options */ } else { skip = false; for (i = 1; argv[in][i] != '\0'; i++) { switch (argv[in][i]) { case 'h': want_help = true; break; /* Compatibility option */ case 'l': command = "list-modules"; break; case 'v': verbose_arg (); break; case 'q': quiet_arg (); break; default: if (!command) { p11_message ("unknown global option: -%c", (int)argv[in][i]); return 2; } break; } } } /* Skipping this argument? */ if (skip) out--; else argv[out] = argv[in]; } /* Initialize tool's debugging after setting env vars above */ p11_debug_init (); if (command == NULL) { /* As a special favor if someone just typed the command, help them out */ if (argc == 1) { command_usage (commands); return 2; } else if (want_help) { command_usage (commands); return 0; } else { p11_message ("no command specified"); return 2; } } argc = out; /* Look for the command */ for (i = 0; commands[i].name != NULL; i++) { if (strcmp (commands[i].name, P11_TOOL_FALLBACK) == 0) { fallback = commands + i; } else if (strcmp (commands[i].name, command) == 0) { argv[0] = command; return (commands[i].function) (argc, argv); } } /* Got here because no command matched */ if (fallback != NULL) { argv[0] = command; return (fallback->function) (argc, argv); } /* At this point we have no command */ p11_message ("'%s' is not a valid command. See '%s --help'", command, getprogname ()); return 2; }