diff options
-rw-r--r-- | Base/usr/share/man/man1/pmemdump.md | 38 | ||||
-rw-r--r-- | Userland/Utilities/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Utilities/pmemdump.cpp | 120 |
3 files changed, 159 insertions, 0 deletions
diff --git a/Base/usr/share/man/man1/pmemdump.md b/Base/usr/share/man/man1/pmemdump.md new file mode 100644 index 0000000000..a8fbe15824 --- /dev/null +++ b/Base/usr/share/man/man1/pmemdump.md @@ -0,0 +1,38 @@ +## Name + +pmemdump - dump physical memory + +## Synopsis + +```**sh +$ pmemdump [-r] <offset> <length> +``` + +## Description + +Dump a portion of the physical memory space. + + +## Options + +* `-r`: Dump from /dev/mem with `read(2)` instead of doing `mmap(2)` on it. + +## Examples + +```sh +$ pmemdump -r 983040 65536 +$ pmemdump 983040 65536 +``` + +## Notes + +The pmemdump utility opens the `/dev/mem` file, and gets a mapping by doing `mmap(2)` +on it. + +Using the `-r` flag might be useful sometimes, especially when reading from an unaligned +reserved physical memory region when trying to `mmap(2)` `/dev/mem` on the specified +offset fails. + +## See also + +* [`mem`(4)](../man4/mem.md) diff --git a/Userland/Utilities/CMakeLists.txt b/Userland/Utilities/CMakeLists.txt index aad7dddba2..7ca285a0d0 100644 --- a/Userland/Utilities/CMakeLists.txt +++ b/Userland/Utilities/CMakeLists.txt @@ -130,6 +130,7 @@ target_link_libraries(paste LibGUI) target_link_libraries(pgrep LibRegex) target_link_libraries(pls LibCrypt LibMain) target_link_libraries(pmap LibMain) +target_link_libraries(pmemdump LibMain) target_link_libraries(pro LibMain LibProtocol) target_link_libraries(ps LibMain) target_link_libraries(pwd LibMain) diff --git a/Userland/Utilities/pmemdump.cpp b/Userland/Utilities/pmemdump.cpp new file mode 100644 index 0000000000..09b58e22eb --- /dev/null +++ b/Userland/Utilities/pmemdump.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/String.h> +#include <AK/StringUtils.h> +#include <LibCore/ArgsParser.h> +#include <LibCore/System.h> +#include <LibMain/Main.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> + +static bool try_set_offset_and_length_parameters(String const& arg_offset, String const& arg_length, u64& offset, u64& length) +{ + // TODO: Add support for hex values + auto possible_offset = arg_offset.to_uint<u64>(); + if (!possible_offset.has_value()) + return false; + auto possible_length = arg_length.to_uint<u64>(); + if (!possible_length.has_value()) + return false; + offset = possible_offset.value(); + length = possible_length.value(); + return true; +} + +static void try_to_dump_with_memory_mapping(int fd, u64 offset, u64 length) +{ + VERIFY(fd >= 0); + u64 mmoffset = offset % sysconf(_SC_PAGESIZE); + void* mmp = mmap(NULL, mmoffset + length, PROT_READ, MAP_SHARED, fd, offset - mmoffset); + if (mmp == MAP_FAILED) { + perror("mmap"); + return; + } + + size_t ncomplete = 0; + while (ncomplete < length) { + ssize_t nwritten = write(STDOUT_FILENO, static_cast<u8*>(mmp) + ncomplete, length - ncomplete); + if (nwritten < 0) { + perror("write"); + return; + } + ncomplete += nwritten; + } + if (munmap(mmp, mmoffset + length) < 0) { + perror("munmap"); + } +} + +static void try_to_dump_with_read(int fd, u64 offset, u64 length) +{ + VERIFY(fd >= 0); + auto rs = lseek(fd, offset, SEEK_SET); + if (rs < 0) { + fprintf(stderr, "Couldn't seek to offset %" PRIi64 " while verifying: %s\n", offset, strerror(errno)); + return; + } + u8 buf[4096]; + size_t ncomplete = 0; + while (ncomplete < length) { + size_t length_to_be_read = min<size_t>((length - ncomplete), sizeof(buf)); + if (read(fd, buf, length_to_be_read) < 0) { + perror("read"); + return; + } + ssize_t nwritten = write(STDOUT_FILENO, buf, length_to_be_read); + if (nwritten < 0) { + perror("write"); + return; + } + ncomplete += nwritten; + } +} + +ErrorOr<int> serenity_main(Main::Arguments arguments) +{ + TRY(Core::System::pledge("stdio rpath")); + + StringView arg_offset; + StringView arg_length; + bool use_read_instead_of_mmap = false; + Core::ArgsParser args; + args.add_positional_argument(arg_offset, "Physical Address (Offset)", "offset", Core::ArgsParser::Required::Yes); + args.add_positional_argument(arg_length, "Length of that region", "length", Core::ArgsParser::Required::Yes); + args.add_option(use_read_instead_of_mmap, "Read /dev/mem instead of try to map it", nullptr, 'r'); + + args.parse(arguments); + + u64 offset = 0; + u64 length = 0; + if (!try_set_offset_and_length_parameters(arg_offset, arg_length, offset, length)) { + warnln("pmemdump: Invalid length or offset parameters\n"); + return 1; + } + + int fd = open("/dev/mem", O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + if (use_read_instead_of_mmap) + try_to_dump_with_read(fd, offset, length); + else + try_to_dump_with_memory_mapping(fd, offset, length); + + close(fd); + + return 0; +} |