Search code examples
iosswiftsecurityhookreverse-engineering

How does the following code detect hooking in iOS?


static func amIRuntimeHook(
    dyldAllowList: [String],
    detectionClass: AnyClass,
    selector: Selector,
    isClassMethod: Bool
  ) -> Bool {
    var method: Method?
    if isClassMethod {
      method = class_getClassMethod(detectionClass, selector)
    } else {
      method = class_getInstanceMethod(detectionClass, selector)
    }
    
    guard let method = method else {
      // method not found
      return true
    }
    
    let imp = method_getImplementation(method)
    var info = Dl_info()
    
    _ = swiftOnceDenyFishHooK
    
    // dladdr will look through vm range of allImages for vm range of an Image that contains pointer 
    // of method and return info of the Image
    if dladdr(UnsafeRawPointer(imp), &info) != 1 {
      return false
    }

How dladdr(UnsafeRawPointer(imp), &info) != 1 assures that the function is not hooked?

Reference: https://github.com/securing/IOSSecuritySuite/blob/master/IOSSecuritySuite/RuntimeHookChecker.swift


Solution

  • The hooking is done by injecting an extra dynamic library that defines the symbol. If dladdr returns 0, then the symbol was not found in any dynamic library. Typically this means it's defined directly in the executable (which includes from a static library). With the right linker flags, you can sometimes still get those symbols with dladdr, but if the return value is 0, it did not come from a dynamic library, and so is not hooked in that way.

    Generally dladdr only promises to return a "non-zero value" if found, So I'm not certain why this is testing explicitly for != 1 rather than == 0. That feels fragile to me, given this fails "open" (i.e. allows the code to run). But that's what it's trying to do at least.