iOS逆向实战--023:lldb调试技巧

lldbLow Lever Debug):默认内置于Xcode中的动态调试工具。标准的lldb提供了一组广泛的命令,旨在与老版本的GDB命令兼容。 除了使用标准配置外,还可以很容易地自定义lldb以满足实际需要

lldb语法:

<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
  • <command>(命令)和<subcommand>(子命令):lldb调试命令的名称
  • <action>:执行命令的操作
  • <options>:命令选项
  • <arguement>:命令的参数
  • []:表示命令是可选的,可以有也可以没有

示例:

breakpoint set -n test
  • commandbreakpoint表示断点命令
  • actionset表示设置断点
  • option-n表示根据方法name设置断点
  • arguementtest表示方法名为tset
断点设置

日常开发中,最常用的是Xcode断点

但是在逆向环境中,我们并没有第三方应用的源码,也不可能通过界面设置断点

这种情况,只能使用breakpoint指令,在Xcodelldb控制台,或直接在终端进行断点设置

案例1:

breakpoint指令的使用

打开ViewController.m文件,写入以下代码:

#import "ViewController.h"

@implementation ViewController

void test1(){
   NSLog(@"3");
}

- (void)viewDidLoad {
   [super viewDidLoad];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
   NSLog(@"1");
   NSLog(@"2");
   test1();
}

@end

test1函数设置断点

breakpoint set -n test1
-------------------------
Breakpoint 2: where = 001--LLDB调试`test1 + 16 at ViewController.m:13:5, >address = 0x0000000102ef1cb4
  • 对符号设置断点
  • set是子命令
  • -n是选项,是--name的缩写
  • 显示Breakpoint 2,说明在001--LLDB调试的进程中,设置的第2个断点,第1个是使用XcodetouchesBegan中设置的

案例2:

对控制器的指定方法设置断点

打开ViewController.m文件,写入以下代码:

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
}

- (IBAction)save:(id)sender {
   NSLog(@"保存");
}

- (IBAction)pause:(id)sender {
   NSLog(@"暂停");
}

- (IBAction)continueGame:(id)sender {
   NSLog(@"继续");
}

@end

在逆向环境中,运行第三方程序,使用暂停可进入lldb控制台,相当于Debug进程附加

ViewController中的多个符号设置断点

breakpoint set -n "-[ViewController save:]" -n "-[ViewController pause:]" -n "-[ViewController continueGame:]"
-------------------------
Breakpoint 1: 3 locations.
  • Breakpoint 1表示分组1
  • 3 locations表示分组1下设置了3个断点

使用breakpoint list查看断点

breakpoint list
-------------------------
Current breakpoints:
1: names = {'-[ViewController save:]', '-[ViewController pause:]', '-[ViewController continueGame:]'}, locations = 3, resolved = 3, hit count = 0
 1.1: where = 001--LLDB调试`-[ViewController save:] + 60 at ViewController.m:17:5, address = 0x0000000104995cd0, resolved, hit count = 0 
 1.2: where = 001--LLDB调试`-[ViewController pause:] + 60 at ViewController.m:21:5, address = 0x0000000104995d28, resolved, hit count = 0 
 1.3: where = 001--LLDB调试`-[ViewController continueGame:] + 60 at ViewController.m:25:5, address = 0x0000000104995d80, resolved, hit count = 0 

案例3:

断点的禁用、启动和删除

禁用断点

breakpoint disable 1
-------------------------
1 breakpoints disabled.
  • 分组1下的所有断点禁用

启用断点

breakpoint enable 1
-------------------------
1 breakpoints enabled.
  • 分组1下的所有断点启用

也可以对单一断点禁用或启用,例如:

breakpoint disable 1.1
-------------------------
1 breakpoints disabled.

删除分组1下的所有断点

breakpoint delete 1
-------------------------
1 breakpoints deleted; 0 breakpoint locations disabled.

删除全部断点

breakpoint delete
-------------------------
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (1 breakpoint)

不支持删除某单一断点,例如:

breakpoint delete 4.1
-------------------------
0 breakpoints deleted; 1 breakpoint locations disabled.

案例4:

了解更多breakpoint指令

使用help指令

help breakpoint
-------------------------
    Commands for operating on breakpoints (see 'help b' for shorthand.)

Syntax: breakpoint <subcommand> [<command-options>]

The following subcommands are supported:

     clear   -- Delete or disable breakpoints matching the specified source
                file and line.
     command -- Commands for adding, removing and listing LLDB commands
                executed when a breakpoint is hit.
     delete  -- Delete the specified breakpoint(s).  If no breakpoints are
                specified, delete them all.
     disable -- Disable the specified breakpoint(s) without deleting them.  If
                none are specified, disable all breakpoints.
     enable  -- Enable the specified disabled breakpoint(s). If no breakpoints
                are specified, enable all of them.
     list    -- List some or all breakpoints at configurable levels of detail.
     modify  -- Modify the options on a breakpoint or set of breakpoints in
                the executable.  If no breakpoint is specified, acts on the
                last created breakpoint.  With the exception of -e, -d and -i,
                passing an empty argument clears the modification.
     name    -- Commands to manage name tags for breakpoints
     read    -- Read and set the breakpoints previously saved to a file with
                "breakpoint write".  
     set     -- Sets a breakpoint or set of breakpoints in the executable.
     write   -- Write the breakpoints listed to a file that can be read in
                with "breakpoint read".  If given no arguments, writes all
                breakpoints.

For more help on any particular subcommand, type 'help <command> <subcommand>'.

也可以了解更多选项的参数设置,例如:

help breakpoint set
-------------------------
    Sets a breakpoint or set of breakpoints in the executable.

Syntax: breakpoint set <cmd-options>

Command Options Usage:
 breakpoint set [-DHd] -l <linenum> [-G <boolean>] [-C <command>] [-c <expr>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-u <column>] [-f <filename>] [-m <boolean>] [-s <shlib-name>] [-K <boolean>]
 breakpoint set [-DHd] -a <address-expression> [-G <boolean>] [-C <command>] [-c <expr>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-s <shlib-name>]
...

案例5:

对包含字符串的符号设置断点

使用breakpoint set -n,对touchesBegan方法设置断点

breakpoint set -n touchesBegan:
-------------------------
Breakpoint 5: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
  • 并没有设置上断点,因为完整的符号为touchesBegan:withEvent:

使用breakpoint set -r,对包含touchesBegan的符号设置断点

breakpoint set -r touchesBegan:
-------------------------
Breakpoint 7: 101 locations.

使用breakpoint set --selector,对项目内指定名称的selector设置断点

breakpoint set --selector touchesBegan:withEvent:
-------------------------
Breakpoint 9: 96 locations.

案例6:

指定文件设置断点

使用breakpoint set --file,在指定文件内设置断点

breakpoint set --file ViewController.m --selector touchesBegan:withEvent:
-------------------------
Breakpoint 2: where = 001--LLDB调试`-[ViewController touchesBegan:withEvent:] + 92 at ViewController.m:30:4, address = 0x0000000100745d44

案例7:

lldb强大的缩写功能

对包含touchesBegan的符号设置断点

b -r touchesBegan
-------------------------
Breakpoint 3: 105 locations.

查看断点的列表

break list

更简单的写法:

br list

对禁用断点disable选项的简写:

br dis 1
-------------------------
1 breakpoints disabled.

对启用断点enable选项的简写:

br en 1
-------------------------
1 breakpoints enabled.
代码执行

expression指令,用于执行代码,缩写指令为pexp

配合p指令使用的po指令,意思是print object,用于打印对象,本质上调用了对象的description

案例1:

使用expression指令执行代码

ViewController中的touchesBegan设置断点

br set -f ViewController.m -r touchesBegan
-------------------------
Breakpoint 2: where = 001--LLDB调试`-[ViewController touchesBegan:withEvent:] + 92 at ViewController.m:30:4, address = 0x0000000102de5d44

点击屏幕,进入断点,使用expression指令执行代码

expression self.view.subviews
-------------------------
(__NSArrayM *) $0 = 0x00000002822eaee0 @"3 elements"

案例2:

设置背景色

使用expression指令,设置self.view的背景色

expression self.view.backgroundColor = [UIColor redColor]
-------------------------
error: <user expression 16>:1:11: property 'backgroundColor' not found on object of type 'UIView *'
  • 报错,无法直接修改backgroundColor属性

换一种方式,修改layer下的backgroundColor属性

expression self.view.layer.backgroundColor = [UIColor yellowColor].CGColor
-------------------------
(CGColorRef) $7 = 0x00000002808a0d20

案例3:

对数组追加元素

打开ViewController.m文件,写入以下代码:

#import "ViewController.h"
#import "Person.h"

@interface ViewController ()
@property(nonatomic, strong) NSMutableArray<Person *> * models;
@end

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   
   Person * p1 = [[Person alloc] initWithName:@"one" age:1];
   Person * p2 = [[Person alloc] initWithName:@"two" age:2];
   Person * p3 = [[Person alloc] initWithName:@"three" age:3];
    
   [self.models addObject:p1];
   [self.models addObject:p2];
   [self.models addObject:p3];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  NSLog(@"models:%@",self.models);
}

-(NSMutableArray<Person *> *)models {
   if (!_models) {
       _models = [NSMutableArray array];
   }
   return _models;
}

@end

ViewController中的touchesBegan设置断点

br set -f ViewController.m -r touchesBegan
-------------------------
Breakpoint 1: where = 001--LLDB调试`-[ViewController touchesBegan:withEvent:] + 84 at ViewController.m:33:25, address = 0x0000000100cc5a6c

点击屏幕,进入touchesBegan方法的断点,追加数组元素

p [self.models addObject:[[Person alloc] initWithName:@"haha" age:4]]

使用ccontinue)指令,继续执行

c
-------------------------
001--LLDB调试[12107:2333568] models:(
   "<Person: 0x281043640>",
   "<Person: 0x281043680>",
   "<Person: 0x2810436a0>",
   "<Person: 0x2810591e0>"
)

输出的models数组中存储了4个对象,元素追加成功

案例4:

修改数组中对象的属性

点击屏幕,进入touchesBegan方法的断点,获取数组的第一个元素

p (Person *)self.models.firstObject
-------------------------
(Person *) $4 = 0x0000000281043640
  • $4为标号,代表Person对象,可以使用

使用标号,修改对象name属性

p $4.name=@"123"
-------------------------
(NSTaggedPointerString *) $5 = 0xb868e91f5b072f35 @"123"

验证name属性是否修改成功

po self.models.firstObject
-------------------------
<Person: 0x281043640>
p ((Person *)0x281043640).name
-------------------------
(NSTaggedPointerString *) $10 = 0xb868e91f5b072f35 @"123"

案例5:

执行多行代码

点击屏幕,进入touchesBegan方法的断点

通过Option+Enter进行换行,在lldb控制台写入以下代码:

p Person * $tmp = self.models.firstObject;
p $tmp.name = @"Zang";
p $tmp.age = 18;
-------------------------
(NSTaggedPointerString *) $6 = 0x996ea49769521a4e @"Zang"
(int) $7 = 18

案例6:

其他流程控制的指令

使用ccontinue)指令,继续执行

c

使用nnext)指令,单步运行,将子函数当做整体一步执行

n

使用s指令,单步运行,遇到子函数会进去

s
堆栈信息

案例1:

查看函数调用栈

使用bt指令,查看函数调用栈

  • *指向当前的函数调用栈

使用up指令,查看上一个函数

up
-------------------------
frame #1: 0x0000000100151c68 001--LLDB调试`test1 at >ViewController.m:22:5
  19      }
  20      
  21      void test1(){
-> 22          test2();
          ^
  23      }
  24      
  25      void test2(){

同时*也指向上一个函数

使用down指令,查看下一个函数

down
-------------------------
frame #0: 0x0000000100151c80 001--LLDB调试`test2 at >ViewController.m:26:4
  23      }
  24      
  25      void test2(){
-> 26         NSLog(@"test2");
         ^
  27      }
  28      
  29      @end

使用frame select指令,选择指定函数

frame select 10
-------------------------
frame #10: 0x000000019ef67480 UIKitCore`__eventFetcherSourceCallback + 156
UIKitCore`__eventFetcherSourceCallback:
->  0x19ef67480 <+156>: ldr    x0, [x19, #0x10]
   0x19ef67484 <+160>: mov    x1, x21
   0x19ef67488 <+164>: bl     0x19c2d1e28
   0x19ef6748c <+168>: mov    x1, x0

使用上述指令,可以将断点定位到指定函数。它的作用可以查看函数的调用者,通过汇编代码分析参数的传递。但寄存器的环境并不会发生变化,数据保存的还是最后一个函数执行完毕的结果

案例2:

查看方法的参数和局部变量

打开ViewController.m文件,写入以下代码:

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
   [self lalala1:@"HAHA"];
}

-(void)lalala1:(NSString *)str{
   [self lalala2:str];
}

-(void)lalala2:(NSString *)str{
   NSString *str1 = @"Zang";
   NSLog(@"%@:%@",str1, str);
}

@end

点击屏幕,进入lalala2方法的断点,使用frame variable指令,查看方法的参数和局部变量

frame variable
-------------------------
(ViewController *) self = 0x0000000105506860
(SEL) _cmd = "lalala2:"
(__NSCFConstantString *) str = 0x0000000104f94080 @"HAHA"
(__NSCFConstantString *) str1 = 0x0000000104f940a0 @"Zang"

在逆向过程中,进入一个方法,最想看到的就是该方法的调用者、方法名称、参数等信息,我们可以使用frame variable指令进行查看。还可以配合updownframe select指令,查看调用栈中其他方法的信息

案例3:

修改方法的参数

上述案例,点击屏幕,进入lalala2方法的断点

使用frame variable指令,查看方法的参数和局部变量

frame variable
-------------------------
(ViewController *) self = 0x0000000100708b60
(SEL) _cmd = "lalala2:"
(__NSCFConstantString *) str = 0x0000000100124080 @"HAHA"
(__NSCFConstantString *) str1 = 0x00000001001240a0 @"Zang"

使用p指令,修改str的值

p str = @"COOL"
-------------------------
(NSTaggedPointerString *) $0 = 0xb4f673513386c4f4 @"COOL"

使用c指令,继续运行

c
-------------------------
001--LLDB调试[13539:2617881] Zang:COOL

输出结果变为修改后的内容,只针对当前未执行完的方法有效。对于已经执行完的方法,修改里面的内容,并不影响最终的结果

案例4:

让方法直接返回,不执行里面的代码

打开ViewController.m文件,写入以下代码:

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
   [self check:@"HOOK"];
   NSLog(@"一切正常~");
}

-(void)check:(NSString *)str{
   
   if([str isEqualToString:@"HOOK"]){
       NSLog(@"有人HOOK我...");
       exit(0);
   }
}

@end

点击屏幕,进入check方法的断点

使用thread return指令,让check方法直接返回,不执行里面的代码

thread return

使用frame variable指令,查看thread return指令执行后的函数

frame variable
-------------------------
(ViewController *) self = 0x0000000100e07ee0
(SEL) _cmd = "touchesBegan:withEvent:"
(__NSSetM *) touches = 0x0000000283e4dbc0 1 element
(UITouchesEvent *) event = 0x0000000280b61200
  • 已经回到touchesBegan:withEvent:方法

使用c指令,继续运行

c
-------------------------
001--LLDB调试[13574:2625998] 一切正常~
  • check方法直接返回,方法内验证str参数的代码没有被触发

thread return指令,可用于调试阶段,绕过指定方法

使用场景:原本执行到某方法,执行就会中断。使用thread return指令绕过方法,如果可以正常执行,证明此方法为检测方法。后续可针对不同情况,选择Method SwizzlefishHookInlineHook对其进行HOOK,将方法直接return

内存断点

案例1:

在对象的属性上设置断点

p1修改name属性时,设置断点

进入断点,使用watchpoint指令,在p1对象的name上设置断点

watchpoint set variable p1->_name
-------------------------
Watchpoint created: Watchpoint 1: addr = 0x280207570 size = 8 state = enabled type = w
   declare @ '/Users/zang/Zang/Spark/LG/14/LLDB/001--LLDB调试/ViewController.m:13'
   watchpoint spec = 'p1->_name'
   new value: 0x0000000102404088

使用c指令,继续运行

c
-------------------------
Watchpoint 1 hit:
old value: 0x0000000102404088
new value: 0x00000001024040a8

使用po指令

po 0x0000000102404088
-------------------------
one
po 0x00000001024040a8
-------------------------
new

当调用name属性的get/set方法,都会触发此断点。可获取到name属性的原始值,和即将修改的值。配合bt指令,查看函数调用栈,可以跟踪name属性的修改是由哪个方法触发的

案例2:

对属性地址设置内存断点

进入断点,获取name属性的地址

p &p1->_name
-------------------------
(NSString **) $0 = 0x0000000281e2ebb0

使用watchpoint指令,对属性地址设置内存断点

watchpoint set expression 0x0000000281e2ebb0
-------------------------
Watchpoint created: Watchpoint 1: addr = 0x281e2ebb0 size = 8 state = enabled type = w
   new value: 4371808392

使用c指令,继续运行

c
-------------------------
Watchpoint 1 hit:
old value: 4371808392
new value: 4371808424

使用po指令

po 4371808392
-------------------------
one
po 4371808424
-------------------------
new
其他指令

案例1:

当分组下断点被触发,自动执行指令

touchesBegan方法设置断点

br set -f ViewController.m -r touchesBegan:
-------------------------
Breakpoint 3: where = 001--LLDB调试`-[ViewController touchesBegan:withEvent:] + 84 at ViewController.m:20:26, address = 0x0000000104945a6c

分组3断点,设置进入断点后的执行的指令

br command add 3
-------------------------
Enter your debugger command(s).  Type 'DONE' to end.
> 

输入指令

> frame variable
> DONE

使用c指令,继续运行

c

点击屏幕,进入touchesBegan方法的断点,同时输出以下信息:

frame variable
(ViewController *) self = 0x0000000103507fc0
(SEL) _cmd = "touchesBegan:withEvent:"
(__NSSetM *) touches = 0x0000000282cea2e0 1 element
(UITouchesEvent *) event = 0x00000002819c6640
(Person *) p1 = 0x00000001e3fe2f18

案例2:

当任何断点被触发,自动执行指令

target stop-hook add -o "frame variable"
-------------------------
Stop hook #1 added.

查看stop-hook的指令列表

target stop-hook list
-------------------------
Hook: 1
 State: enabled
 Commands: 
   frame variable

删除某一条指令

target stop-hook delete 1
undisplay 1

删除全部指令

target stop-hook delete
-------------------------
Delete all stop hooks?: [Y/n] y

禁用某一条指令

target stop-hook disable 1

启用某一条指令

target stop-hook enable 1

添加执行代码

display self.view
  • 等同于expr -- self.view

对于frame variable指令,基本上每个断点触发后都要使用。但lldb每次启动都要重新配置命令,有没有一劳永逸的方法呢?

案例3:

配置lldb初始化文件

在家目录下,存储了lldb的初始化文件

cd /
ls

lldbinit文件的作用,当lldb启动,就会加载此文件,执行文件内的指令

使用vi ~/.lldbinit,写入以下指令:

target stop-hook add -o "frame variable"

运行项目,lldb启动,输出以下内容:

Stop hook #1 added.

进入viewDidLoad断点,输出以下内容:

(ViewController *) self = 0x0000000135f093a0
(SEL) _cmd = "viewDidLoad"

总结

断点设置

  • breakpoint set -n xxx:对方法/函数名称设置断点
  • breakpoint set -r xxx:对包含字符串的符号设置断点
  • breakpoint set --selector xxx:对项目内指定名称的selector设置断点
  • breakpoint set --file xxx:在指定文件中设置断点
  • breakpoint list:查看断点列表
  • breakpoint disable:禁用断点
  • breakpoint enable:启用断点
  • breakpoint delete:删除断点
  • 缩写:breakbr,设置断点可缩写指令:b

代码执行

  • po指令:意思是print object,用于打印对象,本质上调用了对象的description
  • expression指令:用于执行代码
    ◦ 缩写:expp
    ◦ 可以使用标号
    ◦ 可执行多行代码
  • 流程控制
    ccontinue)指令:继续执行
    nnext)指令:单步运行,将子函数当做整体一步执行
    ni指令:单步运行汇编级别
    s指令:单步运行,遇到子函数会进去
    si指令:单步运行可跳转指令内部,汇编级别
    finish指令:直接走完当前方法,返回到上层frame

堆栈信息

  • bt指令:查看函数调用栈
  • up指令:查看上一个函数
  • down指令:查看下一个函数
  • frame select指令:选择指定函数
  • frame variable指令:查看方法调用者、方法名称、参数和局部变量
  • thread return指令:让方法直接返回,不执行里面的代码

内存断点:

  • watchpoint指令,设置内存断点

其他指令

  • br command add指令:给断点添加命令的命令
  • target stop-hook add -o "xxx":每次stop的时候去执行一些命令,针对breadpointwatchpoint
  • 配置lldb初始化文件,当lldb启动,就会加载此文件,执行文件内的指令,一劳永逸

lldb更多文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 断点 1.添加断点 1.给test1函数添加断点(C函数)set 是子命令, -n 是选项,是--name 的缩写...
    小宝_ab67阅读 2,765评论 0 1
  • 一 利用class-dump导出所有头文件 1. 安装class-dunmp 可以直接安装class-dump 或...
    meryin阅读 2,005评论 0 6
  • 接下来上今天的干货LLDB调试部分。日常的正向开发,Xcode提供了多种快捷键以及快捷方式方便我们开发者进行LLD...
    溪浣双鲤阅读 898评论 0 1
  • 常用指令 continue / c 执行到下一个断点 next / n 单步运行 把子函数当作整体 st...
    小狼_zy阅读 377评论 0 1
  • 一、简介 LLDB全称是Low Lever Debug。 下断点快捷键command + \ 二、LLDB断点设置...
    可惜你不是我的双子座阅读 484评论 1 0