CFStrings 的运行过程分析
平时我们编写代码的时候,经常会这样写 NSLog(@"test");
,字符串的内部到底做了些什么?我们来调试分析一下。
一、编写测试代码
编写代码如下:
1 2 3 4 5 6 7 |
#include <Foundation/Foundation.h> int main(){ NSLog(@"test") NSLog(@"test2") return 0; } |
查看 SDK 路径
1 2 3 |
$ xcrun --sdk iphoneos --show-sdk-path /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.2.sdk |
编译:
1 2 |
clang -arch armv7 -fobjc-arc -isysroot "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.2.sdk" -framework Foundation decryptcode.m -o decryptcode.out |
签名:
1 2 |
codesign -s - --entitlements ~/dev/tools/ent.plist -f decryptcode.out |
上传到手机上,运行一下看看效果
1 2 3 4 |
root# ./decryptcode.out 2017-12-16 16:32:38.524 decryptcode.out[2052:152518] test 2017-12-16 16:32:38.531 decryptcode.out[2052:152518] test2 |
二、CFString 的数据结构
NSLog(@"test") 里的字符串保存在 __TEXT 段里的 __cstring 节里,地址为 0x7FF0 如图:
然后再来看看 __DATA 段里的 cstring 节,里面保存了字符串的数据结构,在 0x8014 处里的数据为 f0 0f 08 00
,对应了代码段里的 (0x7FF0+0x4000),0x8018 处里的数据是 4,表示的是字符串的长度,如下图:
三、调试运行过程
在手机用 debugserver 将程序跑起来
1 2 |
debugserver -x backboard *:1234 ./decryptcode.out |
lldb 连接成功之后, main 函数下断点,看一下反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
(lldb) b main Breakpoint 1: no locations (pending). WARNING: Unable to resolve breakpoint to any actual locations. (lldb) c Process 2054 resuming 1 location added to breakpoint 1 7 locations added to breakpoint 1 Process 2054 stopped * thread #1: tid = 0x25437, 0x00080f94 decryptcode.out`main, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x00080f94 decryptcode.out`main decryptcode.out`main: -> 0x80f94 <+0>: push {r7, lr} 0x80f96 <+2>: mov r7, sp 0x80f98 <+4>: sub sp, #0x4 0x80f9a <+6>: movw r0, #0x66 (lldb) dis decryptcode.out`main: -> 0x80f94 <+0>: push {r7, lr} 0x80f96 <+2>: mov r7, sp 0x80f98 <+4>: sub sp, #0x4 0x80f9a <+6>: movw r0, #0x66 0x80f9e <+10>: movt r0, #0x0 0x80fa2 <+14>: add r0, pc 0x80fa4 <+16>: movs r1, #0x0 0x80fa6 <+18>: str r1, [sp] 0x80fa8 <+20>: blx 0x80ffc ; symbol stub for: NSLog 0x80fac <+24>: movw r0, #0x64 0x80fb0 <+28>: movt r0, #0x0 0x80fb4 <+32>: add r0, pc 0x80fb6 <+34>: blx 0x80ffc ; symbol stub for: NSLog 0x80fba <+38>: movs r0, #0x0 0x80fbc <+40>: add sp, #0x4 0x80fbe <+42>: pop {r7, pc} (lldb) |
查看一下模块的地址,偏移后的地址是 0x0000000000079000
1 2 3 4 |
(lldb) image list -o -f [ 0] 0x00075000 //decryptcode.out(0x0000000000079000) ............................ |
在 0x80fa8 处下断点,然后再看看参数 r0 寄存器
1 2 3 4 5 6 7 8 |
decryptcode.out`main: -> 0x80fa8 <+20>: blx 0x80ffc ; symbol stub for: NSLog 0x80fac <+24>: movw r0, #0x64 0x80fb0 <+28>: movt r0, #0x0 0x80fb4 <+32>: add r0, pc (lldb) p/x $r0 (unsigned int) $0 = 0x0008100c |
看一下 0x0008100c 内存的数据,可以到了 f0 0f 08 00
这块数据对应代码段里的字符串 “test” 的地址 0x00080ff0,这个地址的计算方法是模块的偏移地址 + (数据段的 cfstring 看到的地址 - 0x4000),相当于 0x79000 + (0xbff0 - 0x400) = 0x00080ff0,(0xbff0 - 0x400) 实际上就是代码段 cstring 看到的地址 0x7ff0
1 2 3 4 |
(lldb) x 0x0008100c 0x0008100c: 44 e6 4c 37 c8 07 00 00 f0 0f 08 00 04 00 00 00 D?L7?...?....... 0x0008101c: 44 e6 4c 37 c8 07 00 00 f5 0f 08 00 05 00 00 00 D?L7?...?....... |
然后看看 0x00080ff0 的内存数据,果然是 “test” 字符串
1 2 3 4 |
(lldb) x 0x00080ff0 0x00080ff0: 74 65 73 74 00 74 65 73 74 32 00 00 04 f0 1f e5 test.test2...?.? 0x00081000: e4 0f 08 00 80 b0 0f 37 00 00 00 00 44 e6 4c 37 ?....?.7....D?L7 |