Last active
August 5, 2025 23:16
-
-
Save leptos-null/1f7fc9de9f7ae018f149f13446f9c8ae to your computer and use it in GitHub Desktop.
Revisions
-
leptos-null revised this gist
Aug 31, 2024 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -116,7 +116,7 @@ We'll look at this line by line. ``` This is the Swift function in `Tweak.swift`. Note that we do not place the leading underscore (`_`) in this declaration. We could use `dlsym` or `MSFindSymbol`, however since we're able to link against this symbol, I prefer to do that. This line is simply declaring that a function with this symbol name exists. Since this is C, the value of this function is a pointer to the code for the function. -
leptos-null revised this gist
Aug 24, 2024 . No changes.There are no files selected for viewing
-
leptos-null revised this gist
Aug 24, 2024 . 1 changed file with 10 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,7 +4,7 @@ Thanks to help from [@NightwindDev](https://github.com/NightwindDev) for discuss Overall, the idea is simple: Write our own Swift code that will have the same calling convention as the target code, then get a pointer to our own code and the target code, and call `MSHookFunction` with these values. ### Code @@ -37,7 +37,7 @@ This particular code is in an app called "SwizzleSandbox". Let's say that we want to hook the `greet(name:)` function. The first thing we'll do is create a Swift file in our tweak. I'll name this file `Tweak.swift`. We want to match the original source as much as possible, so we'll create a `struct` matching the original declaration, and a matching instance method: @@ -118,8 +118,7 @@ We'll look at this line by line. This is the Swift function in `Tweak.swift`. Note that we do not place the leading underscore (`_`) in this declaration. We could use `dlsym` or `MSFindAddress`, however since we're able to link against this symbol, I prefer to do that. This line is simply declaring that a function with this symbol name exists. Since this is C, the value of this function is a pointer to the code for the function. ```c void *const target = dlsym(RTLD_MAIN_ONLY, "$s14SwizzleSandbox11ContentViewV5greet4nameS2S_tF"); @@ -133,16 +132,17 @@ This time, we are using `dlsym`, since we cannot link against the application we assert(target != NULL); ``` I check that the `target` function is found to make sure the symbol name is correct, since this won't be checked at link time, as the previous technique is. You may want to change this behavior, once you've verified a particular hook. ```c MSHookFunction(target, $s5Tweak11ContentViewV10greet_hook4nameS2S_tF, NULL); ``` This is straight-forward: Replace the implementation of `target` with the implementation of `$s5Tweak11ContentViewV10greet_hook4nameS2S_tF`. This code passes `NULL` to the last parameter, which is the "original" function pointer. Currently, we don't need the original function, however we'll cover that next. ### Calling orig @@ -185,7 +185,9 @@ we replace our "orig" function with `next`. We have to take this approach because the original implementation is expected to be called with the Swift calling convention. As far as I know, Swift does not allow a function with the Swift calling convention to be stored in a variable. This is the reason we created another Swift function earlier: we replace the implementation of that new `_orig` function with the actual original implementation. We can now use `greet_orig(name:)` wherever we would like in our Swift code: -
leptos-null revised this gist
Aug 23, 2024 . 1 changed file with 72 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,6 +2,11 @@ This article aims to describe how to hook Swift functions. Thanks to help from [@NightwindDev](https://github.com/NightwindDev) for discussion and testing. Overall, the idea is simple: Write our own Swift code that will have the same calling convention as the target code, then get a pointer to our own code and the target function, and call `MSHookFunction` with these values. ### Code For simplicity, let's say that we have the following code in the application we're hooking: @@ -28,13 +33,14 @@ public struct ContentView: View { } ``` This particular code is in an app called "SwizzleSandbox". Let's say that we want to hook the `greet(name:)` function. The first thing we'll do is create a Swift file in our tweak. I'll name it `Tweak.swift`. We want to match the original source as much as possible, so we'll create a `struct` matching the original declaration, and a matching instance method: ```swift import SwiftUI @@ -78,17 +84,20 @@ If you get multiple results, or just want to see what these strings mean, pipe t ```bash printf '_$s5Tweak11ContentViewV10greet_hook4nameS2S_tF' | swift demangle ``` ```swift Tweak.ContentView.greet_hook(name: Swift.String) -> Swift.String ``` Now that we have our symbols, we just want to hook the Swift function like a normal C function. We'll create a Logos file- I'll call this file `Hooks.x`. ```objc #import <assert.h> #import <dlfcn.h> #import <substrate.h> %ctor { void $s5Tweak11ContentViewV10greet_hook4nameS2S_tF(void); @@ -191,3 +200,57 @@ public struct ContentView { } } ``` ### Hooking system code This technique works the same way for system code. In this demo, I'll hook `SwiftUI.Color.orange`, which has the following declaration: ```swift extension Color { public static let orange: Color } ``` In `Tweak.swift`, I'll add this code: ```swift extension Color { public static var orange_hook: Color { .teal } } ``` To get the symbol name from the SDK, I use ```bash grep 'Color.*orange' $(xcrun --show-sdk-path)/System/Library/Frameworks/SwiftUI.framework/SwiftUI.tbd ``` ``` _$s7SwiftUI5ColorV6orangeACvgZ ``` And the usual for our own symbol: ```bash nm .theos/obj/Tweak.dylib | grep 'Color.*orange_hook' ``` ``` _$s7SwiftUI5ColorV5TweakE11orange_hookACvgZ ``` We put this together in the same way as above, in `Hooks.x`: ```c void $s7SwiftUI5ColorV5TweakE11orange_hookACvgZ(void); void $s7SwiftUI5ColorV6orangeACvgZ(void); MSHookFunction($s7SwiftUI5ColorV6orangeACvgZ, $s7SwiftUI5ColorV5TweakE11orange_hookACvgZ, NULL); ``` Since we can link against the symbol that we want to hook, I chose to do that instead of using `dlsym`. -
leptos-null created this gist
Aug 23, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,193 @@ This article aims to describe how to hook Swift functions. Thanks to help from [@NightwindDev](https://github.com/NightwindDev) for discussion and testing. ### Code For simplicity, let's say that we have the following code in the application we're hooking: ```swift import SwiftUI public struct ContentView: View { public func greet(name: String) -> String { "Hello \(name)" } public var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(Color.orange) Text(greet(name: "Nightwind")) } .padding() } } ``` This particular code is in an app called SwizzleSandbox. The first thing we'll do is create a Swift file in our tweak. I'll name it `Tweak.swift`. Let's say that we want to hook the `greet(name:)` function. We want to match the original source as much as possible, so we'll create a `struct` as well: ```swift import SwiftUI public struct ContentView { public func greet_hook(name: String) -> String { "Hi \(name)" } } ``` Next, we need to get the symbol names for the original function and our replacement function. To get the name for the original function, I'll simply use `nm` with `grep`: ```bash nm SwizzleSandbox.app/SwizzleSandbox | grep 'ContentView.*greet' ``` `nm` lists the symbol names in a binary. The `grep` parameter is `ContentView` for the name of the enclosing type, and then `greet` is the leading portion of the function name. `.*` matches any characters in-between. This command prints 1 result for me: ``` _$s14SwizzleSandbox11ContentViewV5greet4nameS2S_tF ``` We'll use the same technique for our own tweak: ```bash nm .theos/obj/Tweak.dylib | grep 'ContentView.*greet' ``` This also prints 1 result: ``` _$s5Tweak11ContentViewV10greet_hook4nameS2S_tF ``` If you get multiple results, or just want to see what these strings mean, pipe them into `swift demangle`: ```bash printf '_$s5Tweak11ContentViewV10greet_hook4nameS2S_tF' | swift demangle # Tweak.ContentView.greet_hook(name: Swift.String) -> Swift.String ``` Now that we have our symbols, we just want to hook the Swift function like a normal C function. We'll create a Logos file. I'll call this file `Hooks.x`. ```objc #include <assert.h> #include <dlfcn.h> #include <substrate.h> %ctor { void $s5Tweak11ContentViewV10greet_hook4nameS2S_tF(void); void *const target = dlsym(RTLD_MAIN_ONLY, "$s14SwizzleSandbox11ContentViewV5greet4nameS2S_tF"); assert(target != NULL); MSHookFunction(target, $s5Tweak11ContentViewV10greet_hook4nameS2S_tF, NULL); } ``` We'll look at this line by line. ```c void $s5Tweak11ContentViewV10greet_hook4nameS2S_tF(void); ``` This is the Swift function in `Tweak.swift`. Note that we do not place the leading underscore (`_`) in this declaration. We could use `dlsym` or `MSFindAddress`, however since we're able to link against this symbol, I prefer to do that. This line is simply declaring that a function with this symbol name exists. We're also able to get the address of this symbol, which we'll use later. ```c void *const target = dlsym(RTLD_MAIN_ONLY, "$s14SwizzleSandbox11ContentViewV5greet4nameS2S_tF"); ``` On this line, we're doing the same thing - getting the address of the symbol we're looking for in the application we're hooking. Same as in the previous line, we do not put the leading underscore in the string. This time, we are using `dlsym`, since we cannot link against the application we're hooking. ```c assert(target != NULL); ``` I check that the `target` function is found to make sure the symbol name is correct. You may want to change this behavior, once you've verified a particular hook. ```c MSHookFunction(target, $s5Tweak11ContentViewV10greet_hook4nameS2S_tF, NULL); ``` This is straight-forward: Replace the implementation of `target` with the implementation of `$s5Tweak11ContentViewV10greet_hook4nameS2S_tF`. This code passes `NULL` to the last parameter, which is the "original" function pointer. Currently, we don't need the original function, however we'll cover that next. ### Calling orig To support calling the original implementation of a Swift function that we've hooked, we'll start by adding another function with a similar signature: ```swift public func greet_orig(name: String) -> String { fatalError("This implementation should never be called") } ``` We get the symbol name the same way as before: ```bash nm .theos/obj/Tweak.dylib | grep 'ContentView.*greet_orig' ``` ``` _$s5Tweak11ContentViewV10greet_orig4nameS2S_tF ``` We then bring this symbol into `Hooks.x`: ```c %ctor { void $s5Tweak11ContentViewV10greet_hook4nameS2S_tF(void); void $s5Tweak11ContentViewV10greet_orig4nameS2S_tF(void); void *const target = dlsym(RTLD_MAIN_ONLY, "$s14SwizzleSandbox11ContentViewV5greet4nameS2S_tF"); assert(target != NULL); void *next; MSHookFunction(target, $s5Tweak11ContentViewV10greet_hook4nameS2S_tF, &next); MSHookFunction($s5Tweak11ContentViewV10greet_orig4nameS2S_tF, next, NULL); } ``` In this snippet, we first store the address of the original implementation in `next`, then we replace our "orig" function with `next`. We have to take this approach because the original implementation is expected to be called with the Swift calling convention. As far as I know, Swift does not allow a function with the Swift calling convention to be stored in a variable. We can now use `greet_orig(name:)` wherever we would like in our Swift code: ```swift public struct ContentView { public func greet_orig(name: String) -> String { fatalError("This implementation should never be called") } public func greet_hook(name: String) -> String { greet_orig(name: name) + "!" } } ```