Apple高级调试与逆向工程逆向学习

(四) iOS静态、动态注入库

2020-03-06  本文已影响0人  收纳箱

1. 动态库

1.1 为什么是动态库?

系统核心可以让一个动态库供多个进程使用。比如大家都用同一套UIKit,就只需要加载一份到内存。既然是动态库,我们就可以在运行时加载库,在运行时用LLDB进行探索并执行代码。

1.2 静态地检查一个可执行的动态库

这些动态库在运行时加载,并通过动态加载器dyld加载到内存。如果一个必需要加载的框架加载失败了,dyld会关闭这个程序;但一个可选的框架加载失败了,程序完全可以运行起来,只不过那个库的代码就没法执行了。

在项目中加入Callkit.framework(可选)和Social.framework(必选),然后进行build

动态库

找到Demo.app文件夹。

Demo.app

显示包内容。

Demo.app包

用终端切到这个文件夹下,并输入

 Demo.app> otool -L Demo
Demo:
    /System/Library/Frameworks/CallKit.framework/CallKit (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/Social.framework/Social (compatibility version 1.0.0, current version 87.0.0)
    /System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1673.126.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.0.0)
    /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 1673.126.0)
    /System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 61000.0.0)

我们就用otool命令把Demo用到的所有动态库列出来了。之前加入的CallKitSocial也在列表里面。我们注意到,它们的读取路径都是这两个开头的。

/System/Library/Frameworks/
/usr/lib/

我们继续研究,把之前的L缓存l,再执行一次。

 Demo.app> otool -l Demo
...
Load command 13
          cmd LC_LOAD_WEAK_DYLIB
      cmdsize 80
         name /System/Library/Frameworks/CallKit.framework/CallKit (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 1.0.0
compatibility version 1.0.0
Load command 14
          cmd LC_LOAD_DYLIB
      cmdsize 80
         name /System/Library/Frameworks/Social.framework/Social (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 87.0.0
compatibility version 1.0.0
...

对比两个库的cmd操作,我们会发现可选库使用LC_LOAD_WEAK_DYLIB加载,而必选库使用LC_LOAD_DYLIB加载。

1.3修改加载命令

install_name_tool就是用来修改加载命令的。

我们把工程运行起来,并点击暂停,用image找到加载的CallKit

(lldb) image list CallKit
[  0] B4ECBAE6-A49A-3ED6-94FF-457D6D015513 0x00007fff230dc000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CallKit.framework/CallKit 

我们在终端新建一个tab,找到Demo的完整地址。

pgrep -fl Demo
 ~> pgrep -fl Demo
4323 /Users/xxx/Library/Developer/CoreSimulator/Devices/85225EEE-8D5B-4091-A742-5BEBAE1C4906/data/Containers/Bundle/Application/FD25F38B-E73C-4CBD-8CFE-605E9B208E70/Demo.app/Demo

利用install_name_tool来改变加载的库。

// 用NotificationCenter替换CallKit库
 ~> install_name_tool -change \
> /System/Library/Frameworks/CallKit.framework/CallKit \
> /System/Library/Frameworks/NotificationCenter.framework/NotificationCenter \
> /Users/xxx/Library/Developer/CoreSimulator/Devices/85225EEE-8D5B-4091-A742-5BEBAE1C4906/data/Containers/Bundle/Application/FD25F38B-E73C-4CBD-8CFE-605E9B208E70/Demo.app/Demo
// 查看是否生效
 ~> otool -L /Users/xxx/Library/Developer/CoreSimulator/Devices/85225EEE-8D5B-4091-A742-5BEBAE1C4906/data/Containers/Bundle/Application/FD25F38B-E73C-4CBD-8CFE-605E9B208E70/Demo.app/Demo
/Users/xxx/Library/Developer/CoreSimulator/Devices/85225EEE-8D5B-4091-A742-5BEBAE1C4906/data/Containers/Bundle/Application/FD25F38B-E73C-4CBD-8CFE-605E9B208E70/Demo.app/Demo:
    /System/Library/Frameworks/NotificationCenter.framework/NotificationCenter (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/Social.framework/Social (compatibility version 1.0.0, current version 87.0.0)
    /System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1673.126.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.0.0)
    /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 1673.126.0)
    /System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 61000.0.0)

我们点击模拟器上的Demo应用。

~> lldb -n Demo
(lldb) process attach --name "Demo"
Process 4708 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x00007fff523b625a libsystem_kernel.dylib`mach_msg_trap + 10
libsystem_kernel.dylib`mach_msg_trap:
->  0x7fff523b625a <+10>: ret
    0x7fff523b625b <+11>: nop

libsystem_kernel.dylib`mach_msg_overwrite_trap:
    0x7fff523b625c <+0>:  mov    r10, rcx
    0x7fff523b625f <+3>:  mov    eax, 0x1000020
Target 0: (Demo) stopped.

//查看加载的库有没有CallKit
(lldb) image list CallKit
error: no modules found that match 'CallKit'

//查看加载的库有没有NotificationCenter
(lldb) image list NotificationCenter
[  0] F4B21D50-3426-3D36-B510-482468F77301 0x00007fff295be000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/NotificationCenter.framework/NotificationCenter

1.4 运行时加载动态库

开始之前,先在~/.lldbinit中加入

command regex ls 's/(.+)/po @import Foundation; [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"%1" error:nil]/'

因为现在LLDB已经运行起来了,需要重新载入配置。再查找UIKit,并根据目录地址打印framework目录下所有模拟器可用的公开动态库。

(lldb) command source ~/.lldbinit
(lldb) image list -d UIKit
[  0] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/UIKit.framework

//打印framework目录下所有可用的动态库
(lldb) ls /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/

这时我们可以动态地加载一个动态库,比如Speech

(lldb) process load /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Speech.framework/Speech
Loading "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Speech.framework/Speech"...ok
Image 0 loaded.
(lldb) image list -d Speech
[  0] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Speech.framework

因为dyld默认会搜索一些目录来查找你输入的动态库,你甚至不需要输入动态库的完整路径。

(lldb) process load MessageUI.framework/MessageUI
Loading "MessageUI.framework/MessageUI"...ok
Image 1 loaded.

1.5 探索动态库

探索动态库是逆向工程的基础。虽然动态库需要把二进制代码编译到一个位置无关的可执行文件,但是你依然可以查询到关于动态库的信息(即使编译器去掉了动态库的调试符号)。二进制代码是位置无关的,因为编译器并不不知道代码应该在内存中的什么位置,而这些都是dyld完成的。

添加这样一个命令到你的~/.lldbinit文件中:

command regex dump_stuff "s/(.+)/image lookup -rn '\+\[\w+(\(\w+\))?\ \w+\]$' %1 /"

它接受一个或多个动态库作为输入,导出所有的没有参数的OC类方法。

(lldb) command source ~/.lldbinit
(lldb) dump_stuff Social
69 matches found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Social.framework/Social:
        Address: Social[0x0000000000001704] (Social.__TEXT.__text + 0)
        Summary: Social`+[SLInternalComposeServiceHostContext _extensionAuxiliaryVendorProtocol]        Address: Social[0x0000000000001770] (Social.__TEXT.__text + 108)
        Summary: Social`+[SLInternalComposeServiceHostContext _extensionAuxiliaryHostProtocol]        Address: Social[0x00000000000018d4] (Social.__TEXT.__text + 464)
        Summary: Social`+[SLInternalComposeServiceVendorContext _extensionAuxiliaryVendorProtocol]        Address: Social[0x0000000000001940] (Social.__TEXT.__text + 572)
...

还有一些好用的命令

// 导出继承NSObject的实例的所有ivars
command regex ivars 's/(.+)/expression -l objc -O -- [%1 _ivarDescription]/'
// 导出出继承NSObject的实例,或NSObject类的所有方法
command regex methods 's/(.+)/expression -l objc -O -- [%1 _shortMethodDescription]/'
// 递归导出继承NSObject的类的实例的所有方法
command regex lmethods 's/(.+)/expression -l objc -O -- [%1 _methodDescription]/'

你可以自己玩一下以下命令。

(lldb) command source ~/.lldbinit
(lldb) ivar [UIView new]
(lldb) methods UIView
(lldb) lmethods UIView

1.6 在真机上加载动态库

唯一的区别是System/Library路径!

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/

你可能注意到了,之前我们用otool -L输出的时候是/System/Library/Frameworks,可没有这么长!

之前说过,dyld会搜索一些特殊的文件路径。模拟器使用的是模拟器版本的dyld_sim。所以,如果在真机上运行,动态库将会在/System/Library/Frameworks/

(lldb) ls /
<__NSArrayM 0x28052ced0>(
.HFS+ Private Directory Data
,
.Trashes,
.ba,
.file,
.mb,
Applications,
Developer,
Library,
System,
bin,
cores,
dev,
etc,
private,
sbin,
tmp,
usr,
var
)
(lldb) ls /System/Library/
上一篇 下一篇

猜你喜欢

热点阅读