iOS.& Swift Books 高级Apple调试& Reverse Engineering

16
挂钩&执行代码使用dlopen& dlsym 由Derek Selander撰写

使用LLDB,你已经看到了它是多么容易创建断点和检查自己感兴趣的东西。你也看到了如何创建类你通常不会访问。不幸的是,你已经无法挥舞在开发时这种权力,因为如果框架,或任何其类或方法的,被标记为私有,你不能得到一个公开的API。然而,所有这些都是即将改变。

是时候了解与这些框架开发的互补技能了。在本章中,您将了解“挂钩”进入SWIFT和C代码的方法和策略以及通常在开发时无法访问的执行方法。

当您使用私人框架等某些内容并希望在您自己的应用程序中执行或增强现有代码时,这是一个关键技能。要做到这一点,你会在两个令人敬畏的函数的帮助下调用: dlopendlsym.

目标-C运行时与SWIFT& C

Objective-C, thanks to its powerful runtime, is a truly dynamic language. Even when compiled and running, not even the program knows what will happen when the next objc_msgSend comes up.

挂钩和执行目标-C代码有不同的策略;您将在下一章中浏览这些。本章侧重于如何在SWIFT下挂钩并使用这些框架。

Swift acts a lot like C or C++. If it doesn’t need the dynamic dispatch of Objective-C, the compiler doesn’t have to use it. This means when you’re looking at the assembly for a Swift method that doesn’t need dynamic dispatch, the assembly can simply call the address containing the method. This “direct” function calling is where the dlopendlsym combo really shines. This is what you’re going to learn about in this chapter.

设置您的项目

对于这一章,你要使用命名启动项目 水印 , 位于 起动机 folder.

简单模式:挂钩C功能

When learning how to use the dlopendlsym functions, you’ll be going after the getenv C function. This simple C function takes a char * (null terminated string) for input and returns the environment variable for the parameter you supply.

po (char *)$rdi

"DYLD_INSERT_LIBRARIES"
"NSZombiesEnabled"
"OBJC_DEBUG_POOL_ALLOCATION"
"MallocStackLogging"
"MallocStackLoggingNoCompact"
"OBJC_DEBUG_MISSING_POOLS"
"LIBDISPATCH_DEBUG_QUEUE_INVERSIONS"
"LIBDISPATCH_CONTINUATION_ALLOCATOR"
... etc ...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
  if let cString = getenv("HOME") {
    let homeEnv = String(cString: cString)
    print("HOME env: \(homeEnv)")
  }
  return true
}
HOME env: /Users/derekselander/Library/Developer/CoreSimulator/Devices/2B9F4587-F75E-4184-861E-C2CAE8F6A1D9/data/Containers/Data/Application/D7289D91-D73F-47CE-9FAC-E9EED14219E2

#import <dlfcn.h>
#import <assert.h>
#import <stdio.h>
#import <dispatch/dispatch.h>
#import <string.h>
char * getenv(const char *name) {
  return "YAY!";
}
HOME env: YAY!
char * getenv(const char *name) {
  return getenv(name);    
  return "YAY!";
}
(lldb) image lookup -s getenv
1 symbols match 'getenv' in /Users/derekselander/Library/Developer/Xcode/DerivedData/Watermark-frqludlofnmrzcbjnkmuhgeuogmp/Build/Products/Debug-iphonesimulator/Watermark.app/Frameworks/HookingC.framework/HookingC:
        Address: HookingC[0x0000000000000f60] (HookingC.__TEXT.__text + 0)
        Summary: HookingC`getenv at getenvhook.c:16
1 symbols match 'getenv' in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//usr/lib/system/libsystem_c.dylib:
        Address: libsystem_c.dylib[0x000000000005f1c4] (libsystem_c.dylib.__TEXT.__text + 385956)
        Summary: libsystem_c.dylib`getenv
extern void * dlopen(const char * __path, int __mode);
extern void * dlsym(void * __handle, const char * __symbol);
char * getenv(const char *name) {
  void *handle = dlopen("/usr/lib/system/libsystem_c.dylib",
                        RTLD_NOW);
  assert(handle);
  void *real_getenv = dlsym(handle, "getenv");
  printf("Real getenv: %p\nFake getenv: %p\n",
          real_getenv,
          getenv);
  return "YAY!";
}
Real getenv: 0x10d2451c4
Fake getenv: 0x10a8f7de0
2016-12-19 16:51:30.650 Watermark[1035:19708] HOME env: YAY!
char * getenv(const char *name) {
  static void *handle;      // 1 
  static char * (*real_getenv)(const char *); // 2
  
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{  // 3 
    handle = dlopen("/usr/lib/system/libsystem_c.dylib",
                    RTLD_NOW); 
    assert(handle);
    real_getenv = dlsym(handle, "getenv");
  });
  
  if (strcmp(name, "HOME") == 0) { // 4
    return "/WOOT";
  }
  
  return real_getenv(name); // 5
}
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
  if let cString = getenv("HOME") {
    let homeEnv = String(cString: cString)
    print("HOME env: \(homeEnv)")
  }
  
  if let cString = getenv("PATH") {
    let homeEnv = String(cString: cString)
    print("PATH env: \(homeEnv)")
  }
  return true
}
HOME env: /WOOT
PATH env: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/bin:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/bin:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/sbin:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/sbin:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/local/bin

硬模式:挂钩SWIFT方法

追逐迅速的代码,这不是动态的很多,就像在C函数之后一样。然而,这种方法存在一些并发症,使得钩住SWIFT方法可能更难。

if let handle = dlopen("", RTLD_NOW) {}

if let handle = dlopen("./Frameworks/HookingSwift.framework/HookingSwift", RTLD_NOW) {
}
(lldb) image lookup -rn HookingSwift.*originalImage 
1 match found in /Users/derekselander/Library/Developer/Xcode/DerivedData/Watermark-gbmzjibibkpgfjefjidpgkfzlakw/Build/Products/Debug-iphonesimulator/Watermark.app/Frameworks/HookingSwift.framework/HookingSwift:
        Address: HookingSwift[0x0000000000001550] (HookingSwift.__TEXT.__text + 368)
        Summary: HookingSwift`HookingSwift.CopyrightImageGenerator.(originalImage in _71AD57F3ABD678B113CF3AD05D01FF41).getter : Swift.Optional<__C.UIImage> at CopyrightImageGenerator.swift:36
(lldb) image dump symtab -m HookingSwift

[    4]      9 D X Code            0x0000000000001550 0x000000010baa4550 0x00000000000000f0 0x000f0000 $S12HookingSwift23CopyrightImageGeneratorC08originalD033_71AD57F3ABD678B113CF3AD05D01FF41LLSo7UIImageCSgvg
let sym = dlsym(handle, "$S12HookingSwift23CopyrightImageGeneratorC08originalD033_71AD57F3ABD678B113CF3AD05D01FF41LLSo7UIImageCSgvg")!
print("\(sym)")
0x0000000103105770
(lldb) b 0x0000000103105770
Breakpoint 1: where = HookingSwift`HookingSwift.CopyrightImageGenerator.(originalImage in _71AD57F3ABD678B113CF3AD05D01FF41).getter : Swift.Optional<__ObjC.UIImage> at CopyrightImageGenerator.swift:35, address = 0x0000000103105770
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

然后去哪儿?

你学习如何玩的动态框架。在前面的章节展示了如何在LLDB动态加载。本章介绍了如何修改或执行雨燕或C代码,你通常不会能。在下一章中,你会用Objective-C运行发挥动态加载的框架,并使用Objective-C的动态调度执行类,您不必为这些API。

有一个技术问题?想报告一个错误吗? 你可以问的问题和bug报告书的著者在我们的官书论坛 这里 .

有关于网上阅读体验反馈份额? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您正在阅读的自由,与本章展示的部分作为 混淆了 文本。解开这本书,我们的书籍和视频整个目录,具有raywenderlich.com专业订阅。

现在解锁

突出或做笔记,你需要自己的这本书在认购或购买本身。