hooking Swift methods
First off, Swift often uses classes or structs in typical development. This is a unique challenge because dlsym will only give you a C function. You’ll need to augment this function so the Swift method can reference self if you’re grabbing an instance method, or reference the class if you’re calling a class method. When accessing a method that belongs to a class, the assembly will often reference offsets of self or the class when performing the method. Since dlysm will grab you a C-type function, you’ll need to creatively utilize your knowledge of assembly, parameters and registers to turn that C function into a Swift method.
The second issue you need to worry about is that Swift mangles the names of its methods. The happy, pretty name you see in your code is actually a scary long name in the module’s symbol table. You’ll need to find this method’s correct mangled name in order to reference the Swift method through dlysm.
Time to find where that framework is relative to the Watermark executable.
In Xcode, make sure the Project Navigator is visible (through Cmd + 1). Next, open the Products directory and right-click the Watermark.app. Next, select Show in Finder.
Once the Finder window pops up, right click the Watermark bundle and select Show Package Contents. It’s in this directory the actual Watermark executable is located, so you simply need to find the location of the HookingSwift framework’s executable relative to this Watermark executable.
Next, select the Frameworks directory. Finally select the HookingSwift.framework. Within this directory, you’ll come across the HookingSwift binary.
devzkndeMacBook-Pro:Watermark devzkn$ cd /Users/devzkn/Library/Developer/Xcode/DerivedData/Watermark-crlzngubnjxjalcqofxoqbitpatp/Build/Products/Debug-iphonesimulator/Watermark.app/Frameworks/HookingSwift.framework/HookingSwift
This means you’ve found the relative path you can supply to dlopen. Modify the
dlopen function call you just added so it looks like the following:
if let handle = dlopen("./Frameworks/HookingSwift.framework/
HookingSwift", RTLD_NOW) {
}
Now to the hard part. You want to grab the name of the method responsible for the originalImage property inside the CopyrightImageGenerator class. By now, you know you can use the image lookup LLDB function to search for method name compiled into an executable.
Since you know originalImage is implemented in Swift, use a “Swift style” type of search with the image lookup command. Make sure the app is running then type the following into LLDB:
(lldb) image lookup -rn HookingSwift.*originalImage
(lldb) image lookup -rn HookingSwift.*originalImage
1 match found in /Users/devzkn/Library/Developer/CoreSimulator/Devices/2499648A-9622-42D9-8931-C342DB0208CC/data/Containers/Bundle/Application/647D65C8-4D83-4D86-A7AF-8E90063F0EBF/Watermark.app/Frameworks/HookingSwift.framework/HookingSwift:
Address: HookingSwift[0x0000000000001460] (HookingSwift.__TEXT.__text + 144)
Summary: HookingSwift`HookingSwift.CopyrightImageGenerator.(originalImage in _71AD57F3ABD678B113CF3AD05D01FF41).getter : Swift.Optional<__ObjC.UIImage> at CopyrightImageGenerator.swift:36
In the output, search for the line containing Address: HookingSwift[0x0000000000001460].
This is where this method is implemented inside the HookingSwift framework. This will likely be a different address for you.
For this particular example, the function is implemented at offset 0x00000000000017a0 inside the HookingSwift framework. Copy this address and enter the following command into LLDB:
image dump symtab -m HookingSwift
This dumps the symbol table of the HookingSwift framework. In addition to dumping the symbol table, you’ve told LLDB to show the mangled names of the Swift functions. Search for (Cmd + F) the address you just copied.
Here’s the line that interests you.
Index UserID DSX Type File Address/Value Load Address Size Flags Name
[ 6] 17 D X Code 0x0000000000001460 0x000000010ee5c460 0x0000000000000090 0x000f0000 _TFC12HookingSwift23CopyrightImageGeneratorgP33_71AD57F3ABD678B113CF3AD05D01FF4113originalImageGSqCSo7UIImage_
Yep, that huge angry alphanumeric chunk at the end is the Swift mangled function name. It’s this monstrosity you’ll stick into dlsym to grab the address of the originalImage getter method.
Open ViewController.swift and add the following code inside the if let you just added:
let sym = dlsym(handle, "_TFC12HookingSwift23CopyrightImageGeneratorgP33_71AD57F3ABD678B113CF3AD05D01FF4113originalImageGSqCSo7UIImage_")!
print("\(sym)")
You’ve opted for an implicitly unwrapped optional since you want the application to crash if you got the wrong symbol name.
Build and run the application. If everything worked out, you’ll get a memory address at the tail end of the console output (yours will likely be different):
0x000000010bdc1460
This address is the location to CopyrightImageGeneratorg’s originalImage method that dlsym provided. You can verify this by creating a breakpoint on this address in LLDB:
Breakpoint 2: where = HookingSwift`HookingSwift.CopyrightImageGenerator.(originalImage in _71AD57F3ABD678B113CF3AD05D01FF41).getter : Swift.Optional<__ObjC.UIImage> at CopyrightImageGenerator.swift:39, address = 0x000000010bdc1460
Great! You can bring up the address of this function at runtime, but how do you go about calling it? Thankfully, you can use the typealias Swift keyword to cast functions signatures.
Open ViewController.swift, and add the following directly under the print call you just added:
typealias privateMethodAlias = @convention(c) (Any) -> UIImage? // 1
let originalImageFunction = unsafeBitCast(sym, to:
privateMethodAlias.self) // 2
let originalImage = originalImageFunction(imageGenerator) // 3
self.imageView.image = originalImage // 4
Here’s what this does:
1. This declares the type of function that is syntactically equivalent to the Swift
function for the originalImage property getter.
There’s something very important to notice here. privateMethodAlias is designed so it takes one parameter type of Any, but the actual Swift function expects no parameters. Why is this?
It’s due to the fact that by looking at the assembly to this method, the reference to self is expected in the RDI register.
This means you need to supply the instance of the class as the first parameter into the function to trick this C function into thinking it’s a Swift method. If you don’t do this, there’s a chance the application will crash!
2. Now you’ve made this new alias,you’re casting the sym address to this new type and calling it originalImageFunction.
3. You’re executing the method and supplying the instance of the class as the first and only parameter to the function. This will cause the RDI register to be properly set to the instance of the class. It’ll return the original image without the watermark.
4. You’re assigning the UIImageView’s image to the original image with out the watermark.
import UIKit
import HookingSwift
class ViewController: UIViewController {
// MARK: - IBOutlets
@IBOutlet weak var imageView: UIImageView!
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
let imageGenerator = CopyrightImageGenerator()
imageView.image = imageGenerator.watermarkedImage
if let handle = dlopen("./Frameworks/HookingSwift.framework/HookingSwift", RTLD_NOW) {
let sym = dlsym(handle, "_TFC12HookingSwift23CopyrightImageGeneratorgP33_71AD57F3ABD678B113CF3AD05D01FF4113originalImageGSqCSo7UIImage_")!
typealias privateMethodAlias = @convention(c) (Any) -> UIImage?
let originalImageFunction = unsafeBitCast(sym, to: privateMethodAlias.self)
let originalImage = originalImageFunction(imageGenerator)
imageView.image = originalImage
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。