Xcode_Debug

iOS开发调试Bug的方法

Posted by kunnan on October 18, 2016

前言

Debug,除了解决bug,还常常是逆向分析的手段之一;

个人习惯的分析方式:运行时API+LLDB +CycriptTricks+ powerful-private-methods

基础概念

lldb 相关的概念

  • ASLR offset: 进程在虚拟内存相对于模块基地址的偏移量
    image list -o -f |grep knPenFaceBeauty
    
  • 模块在内存中的起始地址—-模块基地址

断点调试

  • 普通断点
  • 全局断点
  • 条件断点

1.普通断点 略

2.全局断点 All Exceptions

当程序运行出现崩溃时,就会自动断点到出现crash的代码行

3.条件断点: 给断点添加命令:po;对应到lldb 命令行命令br com add 2

  • 编辑断点
  • 添加条件Condition
  • 还可以Action中在条件断点触发时执行事件 如:输出信息po
 po $x2
 po $x3
 c
  • 例子:添加Action以在断点时,执行自定义事件;输入DONE做为结束。

    Breakpoint 1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00
    (lldb) breakpoint modify -c 'i == 99' 1 #指添加action的触发条件。
    (lldb) breakpoint command add 1
    Enter your debugger command(s).  Type 'DONE' to end.
    > p i
    > DONE
    

4.方法断点

打印调试(NSLog)

强化NSLog

//A better version of NSLog
#define NSLog(format, ...) do { \
fprintf(stderr, "<%s : %d> %s\n", \
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \
__LINE__, __func__); \
(NSLog)((format), ##__VA_ARGS__); \
fprintf(stderr, "-------\n"); \
} while (0)

控制台输出

<ViewController.m : 32> -[ViewController viewDidLoad]
2016-10-14 17:33:31.022 DEUBG[12852:1238167] Hello World!
-------

利用NSString输出多种类型

开启僵尸对象 enable Zomble Objects

Xcode可以把那些已经release掉得对象,变成“僵尸”,当我们访问一个Zombie对象时,Xcode可以告诉我们正在访问的对象是一个不应该存在的对象了。因为Xcode知道这个对象是什么,所以可以让我们知道这个对象在哪里,以及这是什么时候发生的。

  • 具体这样做:(僵尸只能用在模拟器和OC语言) enable Zomble Objects
    Product ->Scheme->run->Diagnostics-> enable Zomble Objects
    

I 、lldb

LLDB 是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。

1.0 register read –all

1.1基础

help

在控制台输入help,显示控制台支持的lldb命令

apropos

搜索相关的命令信息。

print是 expression -- 的缩写

  • print 可以指定格式打印 默认 p 十六进制 p/x二进制 p/t
(lldb) p 16
16
(lldb) p/x 16
0x10
(lldb) p/t 16
0b00000000000000000000000000010000
(lldb) p/t (char)16
0b00010000

你也可以使用 p/c 打印字符,或者 p/s 打印以空终止的字符串 p/d打印ACRSII(译者注:以 ‘\0’ 结尾的字符串)。

po

打印对象,是 e -o --的缩写

 po [0x10b45cb90 _ivarDescription]
 po [0x10b45cb90 _shortMethodDescription]
 po [0x10b47c250 _ivarDescription]
<SRWebSocket: 0x10b47c250>:
in SRWebSocket:
	_webSocketVersion (long): 13
	_delegateOperationQueue (NSOperationQueue*): nil
	_delegateDispatchQueue (NSObject<OS_dispatch_queue>*): <OS_dispatch_queue_main: 0x106a5cc80>
	_workQueue (NSObject<OS_dispatch_queue>*): <OS_dispatch_queue: 0x10b47cec0>

1.2流程控制:continue,step over,step into,step out;help frame ,help thread , help process

  • continue :取消暂停,继续执行函数 ,别名是 continue,简写是 c
    在 LLDB 中,你可以使用 process continue 命令来达到同样的效果,它的别名为 continue,或者也可以缩写为 c。
    
  • step over: 执行当前函数中的一行代码,但是不跳进函数。简写是 next ,n
    LLDB 则可以使用 thread step-over,next,或者 n 命令。
    
  • step in:跳进函数中, 简写 step,s
    LLDB中使用 thread step in,step,或者 s 命令。注意,当前行不是函数调用时,next 和 step 效果是一样的。
    
  • step out:跳出函数,简写是 finish
1.2.1 frame info:查看当前的断点的函数信息。

会告诉你当前的行数和源码文件

(lldb) frame info
frame #0: 0x000000010a53bcd4 DebuggerDance`main + 68 at main.m:17
frame #0: 0x00000001069a337c libweixinDylib.dylib`-[KNSocketManagerTool sentgroupInfo](self=0x000000010b45cb90, _cmd="sentgroupInfo") at KNSocketManagerTool.m:187
1.2.3 Thread
  • Return: <RETURN EXPRESSION> : 直接返回当前函数, 可以设置返回值

(lldb) thread return NO

  • thread backtrace all :输出所有线程堆栈

  • thread select 2 : 进行切换线程

  • backtrace : 列出堆栈信息

    (lldb) thread backtrace 
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
      * frame #0: 0x00000001069a337c libweixinDylib.dylib`-[KNSocketManagerTool sentgroupInfo](self=0x000000010b45cb90, _cmd="sentgroupInfo") at KNSocketManagerTool.m:187
        frame #1: 0x00000001069a2f28 libweixinDylib.dylib`__44-[KNSocketManagerTool setupfirstconnecTimer]_block_invoke_2((null)=<unavailable>) at KNSocketManagerTool.m:161
        frame #2: 0x0000000106a09218 libdispatch.dylib`_dispatch_client_callout + 16
        frame #3: 0x0000000106a15334 libdispatch.dylib`_dispatch_continuation_pop + 708
        frame #4: 0x0000000106a23f94 libdispatch.dylib`_dispatch_source_latch_and_call + 204
        frame #5: 0x0000000106a0b300 libdispatch.dylib`_dispatch_source_invoke + 836
        frame #6: 0x0000000106a0e05c libdispatch.dylib`_dispatch_main_queue_callback_4CF + 652
        frame #7: 0x000000018bc8a810 CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
        frame #8: 0x000000018bc883fc CoreFoundation`__CFRunLoopRun + 1660
        frame #9: 0x000000018bbb62b8 CoreFoundation`CFRunLoopRunSpecific + 444
        frame #10: 0x000000018d66a198 GraphicsServices`GSEventRunModal + 180
        frame #11: 0x0000000191bfd7fc UIKit`-[UIApplication _run] + 684
        frame #12: 0x0000000191bf8534 UIKit`UIApplicationMain + 208
        frame #13: 0x0000000100239550 WeChat`___lldb_unnamed_symbol4942$$WeChat + 608
        frame #14: 0x000000018ab995b8 libdyld.dylib`start + 4
      
    
    • thread list : 列出线程列表
1.2.4 自定义alias: 可以修改~/.lldbinit文件,以存储自己的快捷键
command alias bfl breakpoint set -f %1 -l %2
bfl foo.c 12

1.3 高级调试技巧

1.3.1 使用python调用lldb的API

两个开源库:[DerekSelander](https://github.com/DerekSelander)/**LLDB**Chisel

lldb 会默认从~/.lldbinit 加载自定义脚本。新增command script之后,重启Xcode,或者直接在lldb交互模式下输入command source ~/.lldbinit来加载脚本。

  • Commands

    (lldb) pviews
    <iConsoleWindow: 0x10b093b70; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x10a7a2ce0>; layer = <UIWindowLayer: 0x10b07f110>>
    
1.3.2 条件断点

image

1.3.3 debug memory graph

1.3.5 动态调用函数

  • po : 打印对象,是 e -o --的缩写

    (lldb) po [UIApplication sharedApplication]
    <UIApplication: 0x10a79bad0>
    
  • e

    (lldb) e UIApplication *$app = [UIApplication sharedApplication]
    (lldb) po $app
    <UIApplication: 0x10a79bad0>
    

1.3.6 使用Xcode查看当前进行加载的模块

  • debug->debug workflow ->share libraries

    image

1.3.7 查看地址指向的内存信息

  • debug->debug workflows -> view memory

    image

II、Xcode 调试第三方应用

instruments

leaks内存泄漏检查工具

运行后查看

View视图调试-Reveal的替代产品

模拟器调试

编译并运行应用程序,选中模拟器,从 Debug菜单中选择Color Blended Layers选项。

see also


```

转载请注明: > Xcode_Debug