Skip to content

Commit

Permalink
Merge pull request #10 from p-x9/feature/direct-method
Browse files Browse the repository at this point in the history
Handle method exchange/hook if not in vtable
  • Loading branch information
p-x9 authored Feb 21, 2024
2 parents 10a4c99 + 5da452a commit 4b1d34a
Show file tree
Hide file tree
Showing 2 changed files with 224 additions and 23 deletions.
6 changes: 3 additions & 3 deletions Sources/SwiftHook/SwiftHook+function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ extension SwiftHook {
}

@inline(__always)
private static func searchSymbol(
package static func searchSymbol(
_ name: inout String,
isMangled: Bool
) -> UnsafeMutableRawPointer? {
Expand All @@ -135,7 +135,7 @@ extension SwiftHook {

extension SwiftHook {
@discardableResult
private static func _exchangeFuncImplementation(
package static func _exchangeFuncImplementation(
_ first: String,
_ second: String,
_ firstSymbol: UnsafeMutableRawPointer,
Expand Down Expand Up @@ -183,7 +183,7 @@ extension SwiftHook {

extension SwiftHook {
@discardableResult
private static func _hookFuncImplementation(
package static func _hookFuncImplementation(
target: String,
replacement: String,
original: String?,
Expand Down
241 changes: 221 additions & 20 deletions Sources/SwiftHook/SwiftHook+method.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,62 @@ extension SwiftHook {
for `class`: AnyClass
) throws {
var isSucceeded: Bool

/* Use Objective-C Runtime */
isSucceeded = exchangeImplWithObjCRuntime(
first,
second,
for: `class`
)
if isSucceeded { return }

isSucceeded = exchangeImplWithVtable(
let (firstEntry, secondEntry, _) = searchSymbolsFromVtable(
first,
second,
for: `class`
)

/* Swap VTable entry */
isSucceeded = exchangeImplWithVtable(
first,
second,
firstEntry,
secondEntry
)
if isSucceeded { return }

var first: String = first
var second: String = second
let (firstSymbol, secondSymbol) = try searchSymbols(
&first,
&second,
isMangled: false
)

/* Other */
switch (firstEntry, secondEntry, firstSymbol, secondSymbol) {
case let (firstEntry?, nil, _, secondSymbol?):
try exchangeImplWithVtableAndSymbol(
first,
second,
firstEntry,
secondSymbol
)
return
case let (nil, secondEntry?, firstSymbol?, _):
try exchangeImplWithVtableAndSymbol(
second,
first,
secondEntry,
firstSymbol
)
return
case let (nil, nil, firstSymbol?, secondSymbol?):
try _exchangeFuncImplementation(first, second, firstSymbol, secondSymbol)
return
default: break
}

throw SwiftHookError.failedToExchangeMethodImplementation
}

Expand All @@ -42,6 +84,8 @@ extension SwiftHook {
for `class`: AnyClass
) throws {
var isSucceeded: Bool

/* Use Objective-C Runtime */
isSucceeded = hookImplWithObjCRuntime(
target,
replacement,
Expand All @@ -50,14 +94,88 @@ extension SwiftHook {
)
if isSucceeded { return }

isSucceeded = hookImplWithVtable(
let entries = searchSymbolsFromVtable(
target,
replacement,
original,
for: `class`
)
let (targetEntry, replacementEntry, originalEntry) = entries

var original: String? = original
var originalSymbol: UnsafeMutableRawPointer?
if originalEntry == nil,
let originalName = original {
var originalName = originalName
originalSymbol = searchSymbol(&originalName, isMangled: false)
original = originalName
}

/* Swap VTable entry */
isSucceeded = hookImplWithVtable(
target,
replacement,
original,
targetEntry,
replacementEntry,
originalEntry,
originalSymbol
)
if isSucceeded { return }

var target: String = target
var replacement: String = replacement
let (targetSymbol, replacementSymbol) = try searchSymbols(
&target,
&replacement,
isMangled: false
)

/* Other */
var replacedSymbol: UnsafeMutableRawPointer?
switch (targetEntry, replacementEntry, targetSymbol, replacementSymbol) {
case let (targetEntry?, nil, _, replacementSymbol?):
replacedSymbol = try hookImplWithVtableWithSymbol(
target,
replacement,
targetEntry,
replacementSymbol
)
case let (nil, replacementEntry?, targetSymbol?, _):
replacedSymbol = try hookImplWithVtableWithSymbol(
target,
replacement,
targetSymbol,
replacementEntry
)
case let (nil, nil, targetSymbol?, replacementSymbol?):
replacedSymbol = try hookImplWithVtableWithSymbol(
target,
replacement,
targetSymbol,
replacementSymbol
)
default: break
}

if let replacedSymbol {
if let originalEntry {
originalEntry.pointee = unsafeBitCast(replacedSymbol, to: ClassMetadata.SIMP.self)
} else if let original, let originalSymbol {
try _hookFuncImplementation(
target: original,
replacement: target,
original: nil,
targetSymbol: originalSymbol,
replacementSymbol: replacedSymbol,
originalSymbol: nil
)
} else if original != nil {
throw SwiftHookError.failedToSetOriginal
}
return
}

throw SwiftHookError.failedToHookMethod
}
}
Expand Down Expand Up @@ -122,20 +240,32 @@ extension SwiftHook {

return (firstEntry, secondEntry, thirdEntry)
}

private static func searchSymbols(
_ first: inout String,
_ second: inout String,
isMangled: Bool
) throws -> (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) {
let firstSymbol: UnsafeMutableRawPointer? = searchSymbol(
&first,
isMangled: isMangled
)
let secondSymbol: UnsafeMutableRawPointer? = searchSymbol(
&second,
isMangled: isMangled
)

return (firstSymbol, secondSymbol)
}
}

extension SwiftHook {
private static func exchangeImplWithVtable(
_ first: String,
_ second: String,
for `class`: AnyClass
_ firstEntry: UnsafeMutablePointer<ClassMetadata.SIMP?>?,
_ secondEntry: UnsafeMutablePointer<ClassMetadata.SIMP?>?
) -> Bool {
let (firstEntry, secondEntry, _) = searchSymbolsFromVtable(
first,
second,
for: `class`
)

if let firstEntry, let secondEntry {
var tmp: ClassMetadata.SIMP?
tmp = firstEntry.pointee
Expand All @@ -148,6 +278,24 @@ extension SwiftHook {
return false
}

private static func exchangeImplWithVtableAndSymbol(
_ first: String,
_ second: String,
_ firstEntry: UnsafeMutablePointer<ClassMetadata.SIMP?>,
_ secondSymbol: UnsafeMutableRawPointer
) throws {
let tmp = firstEntry.pointee
firstEntry.pointee = unsafeBitCast(secondSymbol, to: ClassMetadata.SIMP.self)
try _hookFuncImplementation(
target: second,
replacement: first,
original: nil,
targetSymbol: secondSymbol,
replacementSymbol: unsafeBitCast(tmp, to: UnsafeMutableRawPointer.self),
originalSymbol: nil
)
}

private static func exchangeImplWithObjCRuntime(
_ first: String,
_ second: String,
Expand Down Expand Up @@ -176,22 +324,30 @@ extension SwiftHook {
_ target: String,
_ replacement: String,
_ original: String?,
for `class`: AnyClass
_ targetEntry: UnsafeMutablePointer<ClassMetadata.SIMP?>?,
_ replacementEntry: UnsafeMutablePointer<ClassMetadata.SIMP?>?,
_ originalEntry: UnsafeMutablePointer<ClassMetadata.SIMP?>?,
_ originalSymbol: UnsafeMutableRawPointer?
) -> Bool {
let entries = searchSymbolsFromVtable(
target,
replacement,
original,
for: `class`
)
let (targetEntry, replacementEntry, originalEntry) = entries

if original != nil && originalEntry == nil {
if original != nil && originalEntry == nil && originalSymbol == nil {
return false
}

if let targetEntry, let replacementEntry {
originalEntry?.pointee = targetEntry.pointee
if let originalEntry {
originalEntry.pointee = targetEntry.pointee
} else if let original, let originalSymbol {
do {
try _hookFuncImplementation(
target: original,
replacement: target,
original: nil,
targetSymbol: originalSymbol,
replacementSymbol: unsafeBitCast(targetEntry.pointee, to: UnsafeMutableRawPointer.self),
originalSymbol: nil
)
} catch { return false }
}
targetEntry.pointee = replacementEntry.pointee

return true
Expand All @@ -200,6 +356,51 @@ extension SwiftHook {
return false
}

private static func hookImplWithVtableWithSymbol(
_ target: String,
_ replacement: String,
_ targetEntry: UnsafeMutablePointer<ClassMetadata.SIMP?>,
_ replacementSymbol: UnsafeMutableRawPointer
) throws -> UnsafeMutableRawPointer {
let targetOriginal = targetEntry.pointee
targetEntry.pointee = unsafeBitCast(replacementSymbol, to: ClassMetadata.SIMP.self)
return unsafeBitCast(targetOriginal, to: UnsafeMutableRawPointer.self)
}

private static func hookImplWithVtableWithSymbol(
_ target: String,
_ replacement: String,
_ targetSymbol: UnsafeMutableRawPointer,
_ replacementEntry: UnsafeMutablePointer<ClassMetadata.SIMP?>
) throws -> UnsafeMutableRawPointer {
try _hookFuncImplementation(
target: target,
replacement: replacement,
original: nil,
targetSymbol: targetSymbol,
replacementSymbol: unsafeBitCast(replacementEntry.pointee, to: UnsafeMutableRawPointer.self),
originalSymbol: nil
)
return targetSymbol
}

private static func hookImplWithVtableWithSymbol(
_ target: String,
_ replacement: String,
_ targetSymbol: UnsafeMutableRawPointer,
_ replacementSymbol: UnsafeMutableRawPointer
) throws -> UnsafeMutableRawPointer {
try _hookFuncImplementation(
target: target,
replacement: replacement,
original: nil,
targetSymbol: targetSymbol,
replacementSymbol: replacementSymbol,
originalSymbol: nil
)
return targetSymbol
}

private static func hookImplWithObjCRuntime(
_ target: String,
_ replacement: String,
Expand Down

0 comments on commit 4b1d34a

Please # to comment.