All Projects → steipete → Interposekit

steipete / Interposekit

Licence: mit
A modern library to swizzle elegantly in Swift.

Programming Languages

swift
15916 projects

Labels

Projects that are alternatives of or similar to Interposekit

Kiero
Universal graphical hook for a D3D9-D3D12, OpenGL and Vulkan based games.
Stars: ✭ 374 (-51.87%)
Mutual labels:  hook
Dexcalibur
[Official] Android reverse engineering tool focused on dynamic instrumentation automation. Powered by Frida. It disassembles dex, analyzes it statically, generates hooks, discovers reflected methods, stores intercepted data and does new things from it. Its aim is to be an all-in-one Android reverse engineering platform.
Stars: ✭ 512 (-34.11%)
Mutual labels:  hook
Hyperdbg
The Source Code of HyperDbg Debugger 🐞
Stars: ✭ 658 (-15.32%)
Mutual labels:  hook
Use Local Storage State
React hook that persists data in local storage
Stars: ✭ 392 (-49.55%)
Mutual labels:  hook
React Cool Portal
😎 🍒 React hook for Portals, which renders modals, dropdowns, tooltips etc. to <body> or else.
Stars: ✭ 458 (-41.06%)
Mutual labels:  hook
React Laag
Hooks to build things like tooltips, dropdown menu's and popovers in React
Stars: ✭ 568 (-26.9%)
Mutual labels:  hook
Use Onclickoutside
React hook for listening for clicks outside of an element.
Stars: ✭ 361 (-53.54%)
Mutual labels:  hook
Fakerandroid
A tool translate a apk file to stantard android project include so hook api and il2cpp c++ scaffolding when apk is a unity il2cpp game. Write code on a apk file elegantly.
Stars: ✭ 695 (-10.55%)
Mutual labels:  hook
X Apm
应用管理 Xposed
Stars: ✭ 482 (-37.97%)
Mutual labels:  hook
Useeffectreducer
useReducer + useEffect = useEffectReducer
Stars: ✭ 642 (-17.37%)
Mutual labels:  hook
Inputmethodholder
A keyboard listener for Android which by hooking the InputMethodManager. 通过hook监听系统键盘显示
Stars: ✭ 417 (-46.33%)
Mutual labels:  hook
React Cool Dimensions
😎 📏 React hook to measure an element's size and handle responsive components.
Stars: ✭ 419 (-46.07%)
Mutual labels:  hook
React Illustration Series
图解react源码, 用大量配图的方式, 致力于将react原理表述清楚.
Stars: ✭ 588 (-24.32%)
Mutual labels:  hook
Androididchanger
Xposed Module for Changing Android Device Info
Stars: ✭ 394 (-49.29%)
Mutual labels:  hook
Virtuallocation
利用Hook技术对APP进行虚拟定位,可修改微信、QQ、以及一些打卡APP等软件,随意切换手机所处位置!
Stars: ✭ 666 (-14.29%)
Mutual labels:  hook
Rbtray
A fork of RBTray from http://sourceforge.net/p/rbtray/code/.
Stars: ✭ 365 (-53.02%)
Mutual labels:  hook
Superdllhijack
SuperDllHijack:A general DLL hijack technology, don't need to manually export the same function interface of the DLL, so easy! 一种通用Dll劫持技术,不再需要手工导出Dll的函数接口了
Stars: ✭ 530 (-31.79%)
Mutual labels:  hook
Use Places Autocomplete
😎 📍 React hook for Google Maps Places Autocomplete.
Stars: ✭ 739 (-4.89%)
Mutual labels:  hook
Scripthookvdotnet
An ASI plugin for Grand Theft Auto V, which allows running scripts written in any .NET language in-game.
Stars: ✭ 679 (-12.61%)
Mutual labels:  hook
Sandvxposed
Xposed environment without root (OS 5.0 - 10.0)
Stars: ✭ 604 (-22.27%)
Mutual labels:  hook
InterposeKit

SwiftPM xcodebuild pod lib lint Xcode 11.4+ Swift 5.2+

InterposeKit is a modern library to swizzle elegantly in Swift, supporting hooks on classes and individual objects. It is well-documented, tested, written in "pure" Swift 5.2 and works on @objc dynamic Swift functions or Objective-C instance methods. The Inspiration for InterposeKit was a race condition in Mac Catalyst, which required tricky swizzling to fix, I also wrote up implementation thoughts on my blog.

Instead of adding new methods and exchanging implementations based on method_exchangeImplementations, this library replaces the implementation directly using class_replaceMethod. This avoids some of the usual problems with swizzling.

You can call the original implementation and add code before, instead or after a method call.
This is similar to the Aspects library, but doesn't yet do dynamic subclassing.

Compare: Swizzling a property without helper and with InterposeKit

Usage

Let's say you want to amend sayHi from TestClass:

class TestClass: NSObject {
    // Functions need to be marked as `@objc dynamic` or written in Objective-C.
    @objc dynamic func sayHi() -> String {
        print("Calling sayHi")
        return "Hi there 👋"
    }
}

let interposer = try Interpose(TestClass.self) {
    try $0.prepareHook(
        #selector(TestClass.sayHi),
        methodSignature: (@convention(c) (AnyObject, Selector) -> String).self,
        hookSignature: (@convention(block) (AnyObject) -> String).self) {
            store in { `self` in
                print("Before Interposing \(`self`)")
                let string = store.original(`self`, store.selector) // free to skip
                print("After Interposing \(`self`)")
                return string + "and Interpose"
            }
    }
}

// Don't need the hook anymore? Undo is built-in!
interposer.revert()

Want to hook just a single instance? No problem!

let hook = try testObj.hook(
    #selector(TestClass.sayHi),
    methodSignature: (@convention(c) (AnyObject, Selector) -> String).self,
    hookSignature: (@convention(block) (AnyObject) -> String).self) { store in { `self` in
        return store.original(`self`, store.selector) + "just this instance"
        }
}

Here's what we get when calling print(TestClass().sayHi())

[Interposer] Swizzled -[TestClass.sayHi] IMP: 0x000000010d9f4430 -> 0x000000010db36020
Before Interposing <InterposeTests.TestClass: 0x7fa0b160c1e0>
Calling sayHi
After Interposing <InterposeTests.TestClass: 0x7fa0b160c1e0>
Hi there 👋 and Interpose

Key Features

  • Interpose directly modifies the implementation of a Method, which is safer than selector-based swizzling.
  • Interpose works on classes and individual objects.
  • Hooks can easily be undone via calling revert(). This also checks and errors if someone else changed stuff in between.
  • Mostly Swift, no NSInvocation, which requires boxing and can be slow.
  • No Type checking. If you have a typo or forget a convention part, this will crash at runtime.
  • Yes, you have to type the resulting type twice This is a tradeoff, else we need NSInvocation.
  • Delayed Interposing helps when a class is loaded at runtime. This is useful for Mac Catalyst.

Object Hooking

InterposeKit can hook classes and object. Class hooking is similar to swizzling, but object-based hooking offers a variety of new ways to set hooks. This is achieved via creating a dynamic subclass at runtime.

Caveat: Hooking will fail with an error if the object uses KVO. The KVO machinery is fragile and it's to easy to cause a crash. Using KVO after a hook was created is supported and will not cause issues.

Various ways to define the signature

Next to using methodSignature and hookSignature, following variants to define the signature are also possible:

methodSignature + casted block

let interposer = try Interpose(testObj) {
    try $0.hook(
        #selector(TestClass.sayHi),
        methodSignature: (@convention(c) (AnyObject, Selector) -> String).self) { store in { `self` in
            let string = store.original(`self`, store.selector)
            return string + testString
            } as @convention(block) (AnyObject) -> String }
}

Define type via store object

// Functions need to be `@objc dynamic` to be hookable.
let interposer = try Interpose(testObj) {
    try $0.hook(#selector(TestClass.returnInt)) { (store: TypedHook<@convention(c) (AnyObject, Selector) -> Int, @convention(block) (AnyObject) -> Int>) in {

        // You're free to skip calling the original implementation.
        let int = store.original($0, store.selector)
        return int + returnIntOverrideOffset
        }
    }
}

Delayed Hooking

Sometimes it can be necessary to hook a class deep in a system framework, which is loaded at a later time. Interpose has a solution for this and uses a hook in the dynamic linker to be notified whenever new classes are loaded.

try Interpose.whenAvailable(["RTIInput", "SystemSession"]) {
    let lock = DispatchQueue(label: "com.steipete.document-state-hack")
    try $0.hook("documentState", { store in { `self` in
        lock.sync {
            store((@convention(c) (AnyObject, Selector) -> AnyObject).self)(`self`, store.selector)
        }} as @convention(block) (AnyObject) -> AnyObject})

    try $0.hook("setDocumentState:", { store in { `self`, newValue in
        lock.sync {
            store((@convention(c) (AnyObject, Selector, AnyObject) -> Void).self)(`self`, store.selector, newValue)
        }} as @convention(block) (AnyObject, AnyObject) -> Void})
}

FAQ

Why didn't you call it Interpose? "Kit" feels so old-school.

Naming it Interpose was the plan, but then SR-898 came. While having a class with the same name as the module works in most cases, this breaks when you enable build-for-distribution. There's some discussion to get that fixed, but this will be more towards end of 2020, if even.

I want to hook into Swift! You made another ObjC swizzle thingy, why?

UIKit and AppKit won't go away, and the bugs won't go away either. I see this as a rarely-needed instrument to fix system-level issues. There are ways to do some of that in Swift, but that's a separate (and much more difficult!) project. (See Dynamic function replacement #20333 aka @_dynamicReplacement for details.)

Can I ship this?

Yes, absolutely. The goal for this one project is a simple library that doesn't try to be too smart. I did this in Aspects and while I loved this to no end, it's problematic and can cause side-effects with other code that tries to be clever. InterposeKit is boring, so you don't have to worry about conditions like "We added New Relic to our app and now your thing crashes".

It does not do X!

Pull Requests welcome! You might wanna open a draft before to lay out what you plan, I want to keep the feature-set minimal so it stays simple and no-magic.

Installation

Building InterposeKit requires Xcode 11.4+ or a Swift 5.2+ toolchain with the Swift Package Manager.

Swift Package Manager

Add .package(url: "https://github.com/steipete/InterposeKit.git", from: "0.0.1") to your Package.swift file's dependencies.

CocoaPods

InterposeKit is on CocoaPods. Add pod 'InterposeKit' to your Podfile.

Carthage

Add github "steipete/InterposeKit" to your Cartfile.

Improvement Ideas

  • Write proposal to allow to convert the calling convention of existing types.
  • Use the C block struct to perform type checking between Method type and C type (I do that in Aspects library), it's still a runtime crash but could be at hook time, not when we call it.
  • Add a way to get all current hooks from an object/class.
  • Add a way to revert hooks without super helper.
  • Add a way to apply multiple hooks to classes
  • Enable hooking of class methods.
  • Add dyld_dynamic_interpose to hook pure C functions
  • Combine Promise-API for Interpose.whenAvailable for better error bubbling.
  • Experiment with Swift function hooking? ⚡️
  • Test against Swift Nightly as Cron Job
  • Switch to Trampolines to manage cases where other code overrides super, so we end up with a super call that's not on top of the class hierarchy.
  • I'm sure there's more - Pull Requests or comments very welcome!

Make this happen: Carthage compatible CocoaPods

Thanks

Special thanks to JP Simard who did such a great job in setting up Yams with GitHub Actions - this was extremely helpful to build CI here fast.

License

InterposeKit is MIT Licensed.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].