mirror of
https://github.com/LSPosed/LSPlt.git
synced 2025-05-06 21:48:48 +08:00
Support hooking so in an archive
This commit is contained in:
parent
b30d5f4529
commit
d6a78a935f
@ -41,28 +41,63 @@ struct MapInfo {
|
|||||||
[[maybe_unused, gnu::visibility("default")]] static std::vector<MapInfo> Scan();
|
[[maybe_unused, gnu::visibility("default")]] static std::vector<MapInfo> Scan();
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Register a hook to a function.
|
/// \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.
|
||||||
/// \param inode The inode of the library to hook. You can obtain the inode by #stat() or by finding
|
/// \param inode The inode of the library to hook. You can obtain the inode by #stat() or by finding
|
||||||
/// the library in the list returned by #MapInfo::Scan().
|
/// the library in the list returned by #lsplt::v1::MapInfo::Scan().
|
||||||
/// \param symbol The function symbol to hook.
|
/// \param symbol The function symbol to hook.
|
||||||
/// \param callback The callback function pointer to call when the function is called.
|
/// \param callback The callback function pointer to call when the function is called.
|
||||||
/// \param backup The backup function pointer which can call the original function. This is optional.
|
/// \param backup The backup function pointer which can call the original function. This is
|
||||||
|
/// optional.
|
||||||
/// \return Whether the hook is successfully registered.
|
/// \return Whether the hook is successfully registered.
|
||||||
/// \note This function is thread-safe.
|
/// \note This function is thread-safe.
|
||||||
/// \note \p backup will not be available until #CommitHook() is called.
|
/// \note \p backup will not be available until #CommitHook() is called.
|
||||||
/// \note \p backup will be nullptr if the hook fails.
|
/// \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
|
/// \note You can unhook the function by calling this function with \p callback set to the backup
|
||||||
/// set by previous call.
|
/// set by previous call.
|
||||||
/// \note LSPlt will backup the hook memory region and restore it when the hook is restored
|
/// \note LSPlt will backup the hook memory region and restore it when the
|
||||||
/// to its original function pointer so that there won't be dirty pages.
|
/// hook is restored to its original function pointer so that there won't be dirty pages. LSPlt will
|
||||||
/// LSPlt will do hooks on a copied memory region so that the original memory region will not be
|
/// do hooks on a copied memory region so that the original memory region will not be modified. You
|
||||||
/// modified. You can invalidate this behaviour and hook the original memory region by calling
|
/// can invalidate this behaviour and hook the original memory region by calling
|
||||||
/// #InvalidateBackup().
|
/// #InvalidateBackup().
|
||||||
/// \see #CommitHook()
|
/// \see #CommitHook()
|
||||||
/// \see #InvalidateBackup()
|
/// \see #InvalidateBackup()
|
||||||
[[maybe_unused, gnu::visibility("default")]] bool RegisterHook(ino_t inode, std::string_view symbol,
|
[[maybe_unused, gnu::visibility("default")]] bool RegisterHook(ino_t inode, std::string_view symbol,
|
||||||
void *callback, void **backup);
|
void *callback, void **backup);
|
||||||
|
|
||||||
|
/// \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.
|
||||||
|
/// \param inode The inode of the library to hook. You can obtain the inode by #stat() or by finding
|
||||||
|
/// the library in the list returned by #lsplt::v1::MapInfo::Scan().
|
||||||
|
/// \param offset The to the library in the file.
|
||||||
|
/// \param size The upper bound size to the library in the file.
|
||||||
|
/// \param symbol The function symbol to hook.
|
||||||
|
/// \param callback The callback function pointer to call when the function is called.
|
||||||
|
/// \param backup The backup function pointer which can call the original function. This is
|
||||||
|
/// 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.
|
||||||
|
/// \see #CommitHook()
|
||||||
|
/// \see #InvalidateBackup()
|
||||||
|
[[maybe_unused, gnu::visibility("default")]] bool RegisterHook(ino_t inode, uintptr_t offset,
|
||||||
|
size_t size, std::string_view symbol,
|
||||||
|
void *callback, void **backup);
|
||||||
/// \brief Commit all registered hooks.
|
/// \brief Commit all registered hooks.
|
||||||
/// \return Whether all hooks are successfully committed. If any of the hooks fail to commit,
|
/// \return Whether all hooks are successfully committed. If any of the hooks fail to commit,
|
||||||
/// the result is false.
|
/// the result is false.
|
||||||
|
@ -24,6 +24,7 @@ inline auto PageEnd(uintptr_t addr) {
|
|||||||
|
|
||||||
struct RegisterInfo {
|
struct RegisterInfo {
|
||||||
ino_t inode;
|
ino_t inode;
|
||||||
|
std::pair<uintptr_t, uintptr_t> offset_range;
|
||||||
std::string symbol;
|
std::string symbol;
|
||||||
void *callback;
|
void *callback;
|
||||||
void **backup;
|
void **backup;
|
||||||
@ -34,7 +35,10 @@ struct HookInfo : public lsplt::MapInfo {
|
|||||||
uintptr_t backup;
|
uintptr_t backup;
|
||||||
std::unique_ptr<Elf> elf;
|
std::unique_ptr<Elf> elf;
|
||||||
bool self;
|
bool self;
|
||||||
[[nodiscard]] bool Match(const RegisterInfo &info) const { return info.inode == inode; }
|
[[nodiscard]] bool Match(const RegisterInfo &info) const {
|
||||||
|
return info.inode == inode && offset >= info.offset_range.first &&
|
||||||
|
offset < info.offset_range.second;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class HookInfos : public std::map<uintptr_t, HookInfo, std::greater<>> {
|
class HookInfos : public std::map<uintptr_t, HookInfo, std::greater<>> {
|
||||||
@ -172,7 +176,7 @@ public:
|
|||||||
for (auto &[_, info] : *this) {
|
for (auto &[_, info] : *this) {
|
||||||
for (auto iter = register_info.begin(); iter != register_info.end();) {
|
for (auto iter = register_info.begin(); iter != register_info.end();) {
|
||||||
const auto ® = *iter;
|
const auto ® = *iter;
|
||||||
if (info.offset != 0 || !info.Match(reg)) {
|
if (info.offset != iter->offset_range.first || !info.Match(reg)) {
|
||||||
++iter;
|
++iter;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -268,11 +272,33 @@ inline namespace v1 {
|
|||||||
[[maybe_unused]] bool RegisterHook(ino_t inode, std::string_view symbol, void *callback,
|
[[maybe_unused]] bool RegisterHook(ino_t inode, std::string_view symbol, void *callback,
|
||||||
void **backup) {
|
void **backup) {
|
||||||
if (inode == 0 || symbol.empty() || !callback) return false;
|
if (inode == 0 || symbol.empty() || !callback) return false;
|
||||||
LOGV("RegisterHook %lu %s", inode, symbol.data());
|
|
||||||
|
|
||||||
std::unique_lock lock(hook_mutex);
|
std::unique_lock lock(hook_mutex);
|
||||||
register_info.emplace_back(RegisterInfo{inode, std::string{symbol}, callback, backup});
|
static_assert(std::numeric_limits<uintptr_t>::min() == 0);
|
||||||
|
static_assert(std::numeric_limits<uintptr_t>::max() == -1);
|
||||||
|
const auto &info = register_info.emplace_back(
|
||||||
|
RegisterInfo{inode,
|
||||||
|
{std::numeric_limits<uintptr_t>::min(), std::numeric_limits<uintptr_t>::max()},
|
||||||
|
std::string{symbol},
|
||||||
|
callback,
|
||||||
|
backup});
|
||||||
|
|
||||||
|
LOGV("RegisterHook %lu %s", info.inode, info.symbol.data());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] bool RegisterHook(ino_t inode, uintptr_t offset, size_t size,
|
||||||
|
std::string_view symbol, void *callback, void **backup) {
|
||||||
|
if (inode == 0 || symbol.empty() || !callback) return false;
|
||||||
|
|
||||||
|
std::unique_lock lock(hook_mutex);
|
||||||
|
static_assert(std::numeric_limits<uintptr_t>::min() == 0);
|
||||||
|
static_assert(std::numeric_limits<uintptr_t>::max() == -1);
|
||||||
|
const auto &info = register_info.emplace_back(
|
||||||
|
RegisterInfo{inode, {offset, offset + size}, std::string{symbol}, callback, backup});
|
||||||
|
|
||||||
|
LOGV("RegisterHook %lu %" PRIxPTR "-%" PRIxPTR " %s", info.inode, info.offset_range.first,
|
||||||
|
info.offset_range.second, info.symbol.data());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user