2022-11-26 22:45:45 +08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
2022-11-27 00:43:46 +08:00
|
|
|
#include <string>
|
2022-11-26 22:45:45 +08:00
|
|
|
#include <string_view>
|
|
|
|
|
2022-11-29 10:50:46 +08:00
|
|
|
/// \namespace lsplt
|
2022-11-26 22:45:45 +08:00
|
|
|
namespace lsplt {
|
2023-01-17 16:10:43 +08:00
|
|
|
inline namespace v2 {
|
2022-11-27 00:43:46 +08:00
|
|
|
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \struct MapInfo
|
|
|
|
/// \brief An entry that describes a line in /proc/self/maps. You can obtain a list of these entries
|
|
|
|
/// by calling #Scan().
|
2022-11-27 00:43:46 +08:00
|
|
|
struct MapInfo {
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief The start address of the memory region.
|
2022-11-27 00:43:46 +08:00
|
|
|
uintptr_t start;
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief The end address of the memory region.
|
2022-11-27 00:43:46 +08:00
|
|
|
uintptr_t end;
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief The permissions of the memory region. This is a bit mask of the following values:
|
|
|
|
/// - PROT_READ
|
|
|
|
/// - PROT_WRITE
|
|
|
|
/// - PROT_EXEC
|
2022-11-27 01:24:21 +08:00
|
|
|
uint8_t perms;
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief Whether the memory region is private.
|
2022-11-27 00:43:46 +08:00
|
|
|
bool is_private;
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief The offset of the memory region.
|
2022-11-27 01:24:21 +08:00
|
|
|
uintptr_t offset;
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief The device number of the memory region.
|
|
|
|
/// Major can be obtained by #major()
|
|
|
|
/// Minor can be obtained by #minor()
|
2022-11-27 00:43:46 +08:00
|
|
|
dev_t dev;
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief The inode number of the memory region.
|
2022-11-27 00:43:46 +08:00
|
|
|
ino_t inode;
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief The path of the memory region.
|
2022-11-27 00:43:46 +08:00
|
|
|
std::string path;
|
|
|
|
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief Scans /proc/self/maps and returns a list of \ref MapInfo entries.
|
|
|
|
/// This is useful to find out the inode of the library to hook.
|
2024-02-25 18:44:21 +08:00
|
|
|
/// \param[in] pid The process id to scan. This is "self" by default.
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \return A list of \ref MapInfo entries.
|
2024-02-25 18:44:21 +08:00
|
|
|
[[maybe_unused, gnu::visibility("default")]] static std::vector<MapInfo> Scan(std::string_view pid = "self");
|
2022-11-27 00:43:46 +08:00
|
|
|
};
|
|
|
|
|
2022-11-28 20:58:29 +08:00
|
|
|
/// \brief Register a hook to a function by inode. For so within an archive, you should use
|
|
|
|
/// #RegisterHook(ino_t, uintptr_t, size_t, std::string_view, void *, void **) instead.
|
2023-01-17 16:10:43 +08:00
|
|
|
/// \param[in] dev The device number of the memory region.
|
2022-11-28 21:01:39 +08:00
|
|
|
/// \param[in] inode The inode of the library to hook. You can obtain the inode by #stat() or by finding
|
2022-11-28 20:58:29 +08:00
|
|
|
/// the library in the list returned by #lsplt::v1::MapInfo::Scan().
|
2022-11-28 21:01:39 +08:00
|
|
|
/// \param[in] symbol The function symbol to hook.
|
|
|
|
/// \param[in] callback The callback function pointer to call when the function is called.
|
|
|
|
/// \param[out] backup The backup function pointer which can call the original function. This is
|
2022-11-28 20:58:29 +08:00
|
|
|
/// optional.
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \return Whether the hook is successfully registered.
|
|
|
|
/// \note This function is thread-safe.
|
|
|
|
/// \note \p backup will not be available until #CommitHook() is called.
|
|
|
|
/// \note \p backup will be nullptr if the hook fails.
|
|
|
|
/// \note You can unhook the function by calling this function with \p callback set to the backup
|
|
|
|
/// set by previous call.
|
2022-11-28 20:58:29 +08:00
|
|
|
/// \note LSPlt will backup the hook memory region and restore it when the
|
|
|
|
/// hook is restored to its original function pointer so that there won't be dirty pages. LSPlt will
|
|
|
|
/// do hooks on a copied memory region so that the original memory region will not be modified. You
|
|
|
|
/// can invalidate this behaviour and hook the original memory region by calling
|
2022-11-27 17:31:49 +08:00
|
|
|
/// #InvalidateBackup().
|
|
|
|
/// \see #CommitHook()
|
|
|
|
/// \see #InvalidateBackup()
|
2023-01-17 16:10:43 +08:00
|
|
|
[[maybe_unused, gnu::visibility("default")]] bool RegisterHook(dev_t dev, ino_t inode, std::string_view symbol,
|
2022-11-27 00:29:32 +08:00
|
|
|
void *callback, void **backup);
|
2022-11-26 22:45:45 +08:00
|
|
|
|
2022-11-28 20:58:29 +08:00
|
|
|
/// \brief Register a hook to a function by inode with offset range. This is useful when hooking
|
|
|
|
/// a library that is directly loaded from an archive without extraction.
|
2023-01-17 16:10:43 +08:00
|
|
|
/// \param[in] dev The device number of the memory region.
|
2022-11-28 21:01:39 +08:00
|
|
|
/// \param[in] inode The inode of the library to hook. You can obtain the inode by #stat() or by finding
|
2022-11-28 20:58:29 +08:00
|
|
|
/// the library in the list returned by #lsplt::v1::MapInfo::Scan().
|
2022-11-28 21:01:39 +08:00
|
|
|
/// \param[in] offset The to the library in the file.
|
|
|
|
/// \param[in] size The upper bound size to the library in the file.
|
|
|
|
/// \param[in] symbol The function symbol to hook.
|
|
|
|
/// \param[in] callback The callback function pointer to call when the function is called.
|
|
|
|
/// \param[out] backup The backup function pointer which can call the original function. This is
|
2022-11-28 20:58:29 +08:00
|
|
|
/// optional.
|
|
|
|
/// \return Whether the hook is successfully registered.
|
|
|
|
/// \note This function is thread-safe.
|
|
|
|
/// \note \p backup will not be available until #CommitHook() is called.
|
|
|
|
/// \note \p backup will be nullptr if the hook fails.
|
|
|
|
/// \note You can unhook the function by calling this function with \p callback set to the backup
|
|
|
|
/// set by previous call.
|
|
|
|
/// \note LSPlt will backup the hook memory region and restore it when the
|
|
|
|
/// hook is restored to its original function pointer so that there won't be dirty pages. LSPlt will
|
|
|
|
/// do hooks on a copied memory region so that the original memory region will not be modified. You
|
|
|
|
/// can invalidate this behaviour and hook the original memory region by calling
|
|
|
|
/// #InvalidateBackup().
|
|
|
|
/// \note You can get the offset range of the library by getting its entry offset and size in the
|
|
|
|
/// zip file.
|
|
|
|
/// \note According to the Android linker specification, the \p offset must be page aligned.
|
|
|
|
/// \note The \p offset must be accurate, otherwise the hook may fail because the ELF header
|
|
|
|
/// cannot be found.
|
|
|
|
/// \note The \p size can be inaccurate but should be larger or equal to the library size,
|
|
|
|
/// otherwise the hook may fail when the hook pointer is beyond the range.
|
2022-11-28 21:01:39 +08:00
|
|
|
/// \note The behaviour of this function is undefined if \p offset + \p size is larger than the
|
|
|
|
/// the maximum value of \p size_t.
|
2022-11-28 20:58:29 +08:00
|
|
|
/// \see #CommitHook()
|
|
|
|
/// \see #InvalidateBackup()
|
2023-01-17 16:10:43 +08:00
|
|
|
[[maybe_unused, gnu::visibility("default")]] bool RegisterHook(dev_t dev, ino_t inode, uintptr_t offset,
|
2022-11-28 20:58:29 +08:00
|
|
|
size_t size, std::string_view symbol,
|
|
|
|
void *callback, void **backup);
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief Commit all registered hooks.
|
|
|
|
/// \return Whether all hooks are successfully committed. If any of the hooks fail to commit,
|
|
|
|
/// the result is false.
|
|
|
|
/// \note This function is thread-safe.
|
|
|
|
/// \note The return value indicates whether all hooks are successfully committed. You can
|
|
|
|
/// determine which hook fails by checking the backup function pointer of #RegisterHook().
|
|
|
|
/// \see #RegisterHook()
|
2022-11-27 00:29:32 +08:00
|
|
|
[[maybe_unused, gnu::visibility("default")]] bool CommitHook();
|
2022-11-26 22:45:45 +08:00
|
|
|
|
2022-11-27 17:31:49 +08:00
|
|
|
/// \brief Invalidate backup memory regions
|
|
|
|
/// Normally LSPlt will backup the hooked memory region and do hook on a copied anonymous memory
|
|
|
|
/// region, and restore the original memory region when the hook is unregistered
|
|
|
|
/// (when the callback of #RegisterHook() is the original function). This function will restore
|
|
|
|
/// the backup memory region and do all existing hooks on the original memory region.
|
|
|
|
/// \return Whether all hooks are successfully invalidated. If any of the hooks fail to invalidate,
|
|
|
|
/// the result is false.
|
|
|
|
/// \note This function is thread-safe.
|
|
|
|
/// \note This will be automatically called when the library is unloaded.
|
|
|
|
/// \see #RegisterHook()
|
2022-11-27 00:29:32 +08:00
|
|
|
[[maybe_unused, gnu::visibility("default")]] bool InvalidateBackup();
|
2023-01-17 16:10:43 +08:00
|
|
|
} // namespace v2
|
2022-11-26 22:45:45 +08:00
|
|
|
} // namespace lsplt
|