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();
|
||||
};
|
||||
|
||||
/// \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
|
||||
/// 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 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.
|
||||
/// \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
|
||||
/// \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().
|
||||
/// \see #CommitHook()
|
||||
/// \see #InvalidateBackup()
|
||||
[[maybe_unused, gnu::visibility("default")]] bool RegisterHook(ino_t inode, std::string_view symbol,
|
||||
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.
|
||||
/// \return Whether all hooks are successfully committed. If any of the hooks fail to commit,
|
||||
/// the result is false.
|
||||
|
@ -24,6 +24,7 @@ inline auto PageEnd(uintptr_t addr) {
|
||||
|
||||
struct RegisterInfo {
|
||||
ino_t inode;
|
||||
std::pair<uintptr_t, uintptr_t> offset_range;
|
||||
std::string symbol;
|
||||
void *callback;
|
||||
void **backup;
|
||||
@ -34,7 +35,10 @@ struct HookInfo : public lsplt::MapInfo {
|
||||
uintptr_t backup;
|
||||
std::unique_ptr<Elf> elf;
|
||||
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<>> {
|
||||
@ -172,7 +176,7 @@ public:
|
||||
for (auto &[_, info] : *this) {
|
||||
for (auto iter = register_info.begin(); iter != register_info.end();) {
|
||||
const auto ® = *iter;
|
||||
if (info.offset != 0 || !info.Match(reg)) {
|
||||
if (info.offset != iter->offset_range.first || !info.Match(reg)) {
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
@ -268,11 +272,33 @@ inline namespace v1 {
|
||||
[[maybe_unused]] bool RegisterHook(ino_t inode, std::string_view symbol, void *callback,
|
||||
void **backup) {
|
||||
if (inode == 0 || symbol.empty() || !callback) return false;
|
||||
LOGV("RegisterHook %lu %s", inode, symbol.data());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user