Debugger Commands Po Call Frame Variable Continue

The LLDB Debugger

  • Quickstart
  • Attach
  • Finding variables
  • Getting started
  • Disassemble
  • Registers
  • Print and Expression
  • Breakpoints
  • Memory
  • Scripting
  • Watchpoint
  • Settings
  • lldb with Swift
  • lldb with Objective C
  • lldb with Objective-C Blocks
  • lldb with C code
  • Read Pointer Array
  • Cast
  • Stripped Binary, decayed pointers and memory writes
  • Find, read and amend variable inside Parent Frame
  • Structs
  • Symbols
  • Advanced
  • Endians
  • stdout
  • Playing with the User Interface
  • Facebook's Chisel
  • Thread Pause / Thread Exit
  • help lldb by setting the language
  • lldb & rootless
  • lldb bypass Certificate Pinning
  • lldb on Jailbroken iOS device
  • lldb bypass iOS Jailbreak detections
  • lldb inspect third party SDK
  • lldb lifting code iOS app
  • lldb references

Quickstart

It is hard to do things quickly in lldb unless you have set up an lldbinit file.

cat              ~/.lldbinit                              #                Great scripts from https://github.com/DerekSelander/LLDB              command              script import              ~/LLDB/lldb_commands/dslldb.py                              #                Facebook's chisel              command              script import /usr/local/opt/chisel/libexec/fbchisellldb.py                              #                Personal commands              command              script import python_lldb_scripts.py                              #                Alias              command              alias              -h                              "Run a command in the UNIX shell."                            -- yd_shell platform shell              command              alias              yd_thread_beautify settings              set              thread-format                              "thread: #${thread.index}\t${thread.id%tid}\n{                ${module.file.basename}{                  `                  ${function.name-with-args}                  \n                                      "                                                                                                command alias yd_register_beautify register read -f d                                                                              command alias yd_smoke exp let                    $z                    = 5                                                                              command alias yd_swift settings set target.language swift                                                                              command alias yd_objc settings set target.language objc                                                                              command alias yd_c settings set target.language c                                                                              command alias yd_stack_vars frame variable --no-args                                                                              command alias yd_connect process connect connect://localhost:6666                                          

Attach

                              #                Load debugger              lldb                              #                Launch code from within lldb              (lldb) process launch c_playground                              #                Create target from within lldb              (lldb) target create patched.bin                              #                Create a target              lldb --wait-for patched.bin (lldb) target create patched.bin                              #                Attach to running app              lldb -n open_app

Finding variables

                              #                Frame              frame info                              #                Print variables in the Frame              frame variable -A -T                              #                Get pointer to variables inside the Frame              fr v -L                              #                Show the current thread's call stack              bt                              #                Move to another Frame to find variables              frame              select              1            

Getting started

                              #                Threads              thread list                              #                Brief list of attached Libraries              image list -b                              #                Sections of all loaded code              image dump sections                              #                Sections of a Module              image dump sections myApp                              #                Symbols of a Module              image dump symtab myApp                              #                Symbols of all loaded code (BAD IDEA)              image dump symtab                              #                Lookup help              help              image lookup                              #                Lookup a Debug Symbol              image lookup -r -n YDClass                              #                Lookup non-debug symbols              image lookup -r -s YDClass                              #                Lookup Address              image lookup -a 0x1000016a0                              #                Search for Object on Heap              search -r 0x0000000100610570

Disassemble

                              #                By address              disas -s 0x00001620                              #                By function name              disas -n Foo.Bar                              #                By ObjC method              disas -n                              "+[YDFileChecker asmSyscallFunction:]"                          

Registers

Argument Register x86_64 arm64
Return - RAX -
First arg1 RDI x0
Second arg2 RSI x1
Third arg3 RDX x2
Fourth arg4 RCX x3
Fifth arg5 R8 x4
Sixth arg6 R9 x5
Syscalls - syscall x16

Print and Expression

                              #                Print a register with useful aliase              po              $arg2                              #                Hex to Decimal              p/d 0x1a        // (int)              $2              = 26                              #                Create char * and persist it ( the $ symbol )              po char              *              $new                              #                Check for substring in a register              po              $new              = (char              *) strnstr                ((char                *)$rsi                ,                "Info.plist",                (int)strlen((char                *)                $rsi                ))                                            #                Create NSString from Selector              exp NSString              *              $myMethod              = NSStringFromSelector(_cmd)                              #                Get Selector              po NSSelectorFromString($meth)

Breakpoints

                              #                Getting the options              help              breakpoint              set                              #                Options to add script to Breakpoint              help              break              command              add                              #                Delete all breakpoints              b delete                              #                List              b list                              #                Breakpoint on symbol name              b syscall                              #                Breakpoint on fullname              breakpoint              set              -F access                              #                Breakpoint on fullname in a single Module              breakpoint              set              -F access -s libsystem_kernel.dylib                              #                Breakpoint on Name and give the breakpoint a name              b -n task_get_exception_ports -N fooName --auto-continue              true                              #                Breakpoint on Address ( gdb syntax )              b              *0x1000016ce                              #                Breakpoint on ObjC Class Method              b                              "+[YDFileChecker foobar:]"                                            #                Breakpoint on Function, name the breakpoint and set condition              br              set              -b                              "+[YDFileChecker foobar:]"                            -N fooName  -c                              "                $arg1                == 0x33"                                            #                Breakpoint on Address with name (lldb syntax )              br s -a 0x1000016ce -N fooName                              #                Break on Register value ( SVC calls )              b              set              -N fooName --auto-continue              true              -c              $x16==26                              #                Break on Register holding Info.plist substring              br s -n syscall -c                              '(char *) strnstr((char *)$rsi, "Info.plist", (int)strlen((char *) $rsi)) != NULL'                                            #                Breakpoint on Selector              breakpoint              set              --selector URLSession:didReceiveChallenge:completionHandler:                              #                Breakpoint on Selector in Module              breakpoint              set              --selector blah:blah: -s playModule                              #                Regex Breakpoint on Selector ( good for Swift )              rb Foo.handleBarChallenge -s playModule -N fooName                              #                Breakpoint naming              breakpoint              set              --selector blah:blah: -s objc_play -N fooName                              #                Breakpoint condition              br mod -c              $arg2              ==                              "URLSession:didReceiveChallenge:completionHandler:"                            fooName                              #                Break on exact ObjC Method              b                              "-[MyUser name:]"                                            #                Breakpoint on completionHandler              b -[YDURLSessionDel URLSession:didReceiveChallenge:completionHandler:]                              #                Regex Breakpoint              rb                              '\-\[UIViewController\                '                            rb                              '\-\[YDUser(\(\w+\))?\                '                            breakpoint              set              --func-regex=. --shlib=objc_play                              #                Python script when Breakpoint fires              (lldb) breakpoint              command              add -s python fooName Enter your Python command(s). Type                              'DONE'                            to end.     print(                "[!]found it"              )     DONE                              #                Callback to Python function when Breakpoint hits              (lldb) breakpoint              command              add -F ydscripts.YDHelloWorld fooName                              #                Add & continue Python script when Breakpoint fires              (lldb) breakpoint              command              add -s python fooName     print lldb.frame.register[                "rsi"              ].value     lldb.frame.register[                "rsi"              ].value =                              "1"                            print(                "[*]new value set."              )     thread =              frame.GetThread()     process =              thread.GetProcess()              process.Continue()     DONE                              #                Breakpoint all code inside a function              (lldb) script >>>              for              a              in              range(0x1000016bc, 0x1000016d1): ... 	lldb.target.BreakpointCreateByAddress(a)

Memory

Read the string that is pointed to by a char* pointer

memory read 0x00007fff36d99fb5

Read five instructions after address

memory read --format instruction --count 5 0x10463d970

Read memory and print in format Decimal

mem read 0x00007ffee5f99610 -f d

Get start and end of search
              (lldb) section [0x0000010462c000-0x00000107744000] 0x0003118000 MyApp`__TEXT [0x00000107744000-0x00000107d48000] 0x0000604000 MyApp`__DATA /* removed sections for brevity */                          
Find String in memory range

mem find -s "youtube" -- 0x00000107744000 0x00000107d48000

Read 100 bytes from address

memory read -c100 0x10793362c

Find every "B" character. Stop after 5 matches.
              (lldb) mem find -s "B" -c 5 -- 0x00000100000000 0x00000100004000 data found at location: 0x10000063b 0x10000063b: 42 2e 64 79 6c 69 62 00 00 00 00 00 00 26 00 00  B.dylib......&.. data found at location: 0x100003f60 0x100003f60: 42 00 00 00 61 00 00 00 62 00 00 00 6f 00 00 00  B...a...b...o... 0x100003f70: 6f 00 00 00 6e 00 00 00 00 00 00 00 0a 5b 2a 5d  o...n........[*] no more matches within the range.                          

Scripting

              //See how many times a C function is called when running an iOS app.  breakpoint set -n getenv breakpoint modify --auto-continue 1 breakpoint command add 1   po (char *)$arg1								// telling lldb how to cast $arg1   DONE continue                          

Watchpoint

Help

help watchpoint set

watchpoint list

watchpoint list

watchpoint delete

watch del 1

watchpoint on Global variable

watchpoint set variable file_exists

Once it stops

po file_exists = NO

watchpoint on frame variable

watchpoint set variable completionHandler

watchpoint on address in function

watchpoint set expression -w write -- "+[YDFileChecker checkFileExists]" + 32

watchpoint on register

watchpoint set expression -- $arg1

watchpoint on register

watchpoint set expression -w read_write -- $arg1

Delete some watchpoints, if you see this error

error: sending gdb watchpoint packet failed

Settings

show target.run-args

settings show target.run-args

show target.env-vars

settings show target.env-vars

Add setting to lldbinit file

echo "settings set target.x86-disassembly-flavor intel" >> ~/.lldbinit

Logging

settings set target.process.extra-startup-command QSetLogging:bitmask=LOG_ALL;

lldb with Swift

              class lyftClass {      static let request_number = 1     static let uri = "https://my.url/"     let app_version = "app_99"      func facebook() -> Int {         return 96     }      static func google() -> Int {         return 42     } }                          
Invoking code from a Framework or the main application ?

If you are invoking code from a Swift dynamic framework, make sure to tell lldb about the Framework. Below is why...

              (lldb) exp let $a = RustyAppInfo() error: <EXPR>:3:10: error: use of unresolved identifier 'RustyAppInfo' let $a = RustyAppInfo()        ^~~~~~~~~~~~  (lldb) expr -- import rusty_nails (lldb) exp let $a = RustyAppInfo() // success. now you have a class instant.                          

lldb knew I was trying to write swift code. In an iOS app, where you have Swift and Objective-C code, I always find it useful to type:

(lldb) settings set target.language swift

Connect to app via lldb

I liked to put a breakpoint on the the AppDelegate class. If you let the app load, the context of Swift is automagically lost by lldb.

Print your class
              (lldb) po lyftClass() <lyftClass: 0x60c000051dc0>                          
Create a class instance

expression let $lyft = rusty.lyftClass()

Invoke a member function from instance class

po $lyft.app_version()

Try and print a Class member
              po $lyft.request_number error: <EXPR>:3:1: error: static member 'uri' cannot be used on instance of type 'lyftClass'                          

This fails as it is a Static class member and not accessible to the instantiated class.

Print a Class member
              (lldb) po lyftClass.uri "https://my.url/"                          
Print Class function member
              (lldb) po lyftClass.google() 42                          
Invoke Swift Class with Initializers
              class lyftClass {      static let url = "https://my.url/"     let version = "app_99.0"     let mickey: String     let mouse: Int      init(mickey: String, mouse: Int) {         self.mickey = mickey         self.mouse = mouse     }      convenience init(mickey: String){         self.init(mickey: mickey, mouse: 100)     }      func facebook_string() -> String {         return "jibber jabber"     }      static func google_int() -> Int {         return 42     } }                          
Create a class instance
              (lldb) expression let $a = lyftClass() error: <EXPR>:3:10: error: cannot invoke initializer for type 'lyftClass' with no arguments  <EXPR>:3:10: note: overloads for 'lyftClass' exist with these partially matching parameter lists: (mickey: String, mouse: Int), (mickey: String) let $a = lyftClass()                          
Invoke a function from instant class
              (lldb) expression let $b = lyftClass(mickey: "zer", mouse: 500) (lldb) po $b <lyftClass: 0x60c000284470>  (lldb) po $b.mickey "zer"  (lldb) po $b.mouse 500                          
Create Class with convenience initializer
              (lldb) expression let $a = lyftClass(mickey: "wow") (lldb) po $a <lyftClass: 0x60000009b580> (lldb) po $a.mickey "wow" (lldb) po $a.mouse 100                          

lldb with Objective C

Classes
              @import Foundation;  @interface Box:NSObject {     double length;    // Length of a box     double breadth;   // Breadth of a box     double height;    // Height of a box }  @property(nonatomic, readwrite) double height;  // Property -(double) volume; @end  @implementation Box  @synthesize height;  -(id)init {     self = [super init];     length = 1.0;     breadth = 1.0;     return self; }  -(double) volume {     return length*breadth*height; }  @end  int main(int argc, const char * argv[]) {     @autoreleasepool {         Box *box1 = [[Box alloc]init];    // Create box1 object of type Box          double volume = 0.0;             // Store the volume of a box here          // box 1 specification         box1.height = 5.0;          // volume of box 1         volume = [box1 volume];         NSLog(@"Volume of Box1 : %f", volume);     }     return 0; }                          
Create a new Class Instance
              (lldb) exp Box *$b = [Box new];  I could have easily written: (lldb) exp Box *$a = [[Box alloc]init];                          
Print an initialised Class variable
Print the Class pointer
              (lldb) po $b <Box: 0x100777d50>  (lldb) po (Box*)$b <Box: 0x100777d50>                          
Set a Class variable
              (lldb) exp $b->height = 20.0 (double) $8 = 20  (lldb) po $b->height 20                          
Access Instance Method

Invoke Instance Method with several parameters

              - (void)getVersion:(int*)num1 minor:(int*)num2 patch:(int*)num3;  (lldb) e SampleClass *$sample = [[SampleClass alloc]init]; (lldb) po $sample <SampleClass: 0x10040ae70>  (lldb) exp [$sample getVersion:&a minor:&b patch:&c];                          

NSString to NSData and back

              (lldb) exp @import Foundation (lldb) exp NSString* $str = @"hello string"; (lldb) po $str hello string (lldb) exp NSData* $data = [$str dataUsingEncoding:NSUTF8StringEncoding]; (lldb) po $data <74657374 73747269 6e67> (lldb) po (NSData*) $data <74657374 73747269 6e67>  (lldb) exp NSString* $newStr = [[NSString alloc] initWithData:$data encoding:NSUTF8StringEncoding]; (lldb) po $newStr hello string                          

Booleans

              (lldb) expression BOOL $myflag = YES (lldb) print $myflag (BOOL) $myflag = NO (lldb) expression $myflag = YES (BOOL) $7 = YES (lldb) print $myflag (BOOL) $myflag = YES                          

lldb with Objective-C Blocks

Write Block
              --> remember, Blocks are on the Stacks. So if your debugger moves around and you created a Block it won't be available ( if you changed Frames / Stacks )  (lldb) exp 1 void (^$simpleBlock)(void) = ^{ 2 (void)NSLog(@"hello from a block!"); 3 }; 4                          
Call Block
              (lldb) po $simpleBlock() [1136:66563] hello from a block!                          
Get Pointer to Block
              (lldb) po $simpleBlock        // get pointer to Block (void (^)()) $simpleBlock = 0x00000001025a9900                          
While Loop inside a Block
              (lldb) expression 1 void (^$helloWhile)(int) = 2 ^(int a) { 3 while(a <10) { 4 printf("Hello %d\n", a); 5 a++; 6 }};  (lldb) po $helloWhile(2) Hello 2 Hello 3 Hello 4 ......                          
Add two numbers with a Block
              (lldb) expression 1 int (^$add)(int, int) = 2 ^(int a, int b) { return a+b; }  (lldb) p $add(3,4) (int) $0 = 7  (lldb) po $add 0x0000000101424110  (lldb) p $add (int (^)(int, int)) $add = 0x0000000101424110                          
Void Block
              po void (^$fakeBlock)(int, NSURLCredential * _Nullable) =^(int a, NSURLCredential *b) {NSLog(@"hello. Original enum was set to %d", a);}  po $fakeBlock(2,0)                          
Use Global Dispatch Block
              (lldb) expression 1 dispatch_sync(dispatch_get_global_queue(0,0),          ^(){ printf("Hello world\n"); });                          
Calling the Block with a Name
              A more complicated example that gives the Block a name so it can be called like a function.  (lldb) exp 1 double (^$multiplyTwoValues)(double, double) = 2 ^(double firstValue, double secondValue) { 3 return firstValue * secondValue; 4 }; 5  (lldb) po $multiplyTwoValues(2,4) 8   (lldb) exp double $result (lldb) p $result (double) $result = 0 (lldb) exp $result = $multiplyTwoValues(2,4) (double) $1 = 8 (lldb) po $result 8                          
Get the syntax
              (lldb) expression Enter expressions, then terminate with an empty line to evaluate: 1 void(^$remover)(id, NSUInteger, BOOL *) = ^(id string, NSUInteger i,BOOL *stop){ 2 NSLog(@"ID: %lu String: %@", (unsigned long)i, string); 3 }; 4  (lldb) p $remover (void (^)(id, NSUInteger, BOOL *)) $remover = 0x00000001021a4110  (lldb) exp [oldStrings enumerateObjectsUsingBlock:$remover]  ID: 0 String: odd ID: 1 String: raygun ID: 2 String: whoop whoop 3 String: doctor pants                          

lldb with C code

malloc / strcpy

Create a malloc char array, copy with strcpy, and free.

              (lldb) e char *$str = (char *)malloc(8) (lldb) e (void)strcpy($str, "munkeys") (lldb) e $str[1] = 'o' (lldb) p $str (char *) $str = 0x00000001c0010460 "monkeys"                          
Warm-up - getenv
              (lldb) e const char *$home = NULL (lldb) p *$home error: Couldn't apply expression side effects : Couldn't dematerialize a result variable: couldn't read its memory (lldb) e $home = getenv("HOME") (const char *) $3 = 0x00007ffeefbff8d2 "/Users/foobar" (lldb) po $home "/Users/foobar" (lldb) p $home (const char *) $home = 0x00007ffeefbff8d2 "/Users/foobar"                          
Examine
              (lldb) malloc_info --type 0x1c0010480 [+][+][+][+] The string is in the heap!  [+][+][+][+]  (lldb) memory read 0x00000001c0010460                          
Free
              (lldb) e (void)free($str)                          
Print Bool
              (lldb) po (bool) result <object returned empty description>  (lldb) p result (bool) $2 = true  (lldb) p/x result (bool) $0 = 0x01  (lldb) exp result = false (bool) $1 = false  (lldb) p/x result (bool) $2 = 0x00  (lldb) p/t result (bool) $4 = 0b00000000  (lldb) exp result = true (bool) $5 = true  (lldb) p/t result (bool) $6 = 0b00000001                          
Print Char Array
              (lldb) po (char*) message "AAAA"  (lldb) po message "AAAA"  (lldb) p message (char *) $5 = 0x0000000100000fa9 "AAAA"  (lldb) p *message (char) $1 = 'A'                          
Struct initialize
              (lldb) expr struct YD_MENU_ITEMS $menu = {.menu_option = "a", .description = "all items"};  (lldb) expr struct VERSION_INFO $b error: typedef 'VERSION_INFO' cannot be referenced with a struct specifier  (lldb) expr VERSION_INFO $b (lldb) p $b (VERSION_INFO) $b = (Major = 0, Minor = 0, Build = 0)                          
Enum initialize
              (lldb) expr PAS_RESULT $a (lldb) po $a <nil> (lldb) p $a (PAS_RESULT) $a = 0 (lldb) exp $a = 2 (PAS_RESULT) $0 = 2                          
Cast return types

The flexibility of void * is great. If you don't know how to cast the return handle you can point it to void.

              (lldb) exp (void*) getCurrentVersion(&$b); (void *) $2 = 0x0000000000000000 (lldb) p $b (VERSION_INFO) $b = (Major = 4, Minor = 6, Build = 13)                          
Banana Skins

Make sure you add the $ sign before a variable. Else you will hit:

error: warning: got name from symbols: b

Read Pointer Array

Source code
              void foo_void ( float *input ) {     printf("Pointer: %p.\n", input);        <-- breakpoint here }  int main ( void ) {     float tiny_array[4];     tiny_array[0] = 1.0;     tiny_array[1] = 2.0;     tiny_array[2] = 3.0;     tiny_array[3] = 4.0;     foo_void ( tiny_array );     return 0; }                          
Solution
              (lldb) fr v -L 0x00007ffeefbff4c8: (float *) input = 0x00007ffeefbff4f0  (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()'.  >>> ptr = lldb.frame.FindVariable('input')  >>> print(ptr.GetValue())       // this prints the value NOT the offset 0x00007ffeefbff4f0  >>> print(ptr.GetType()) float *  >>> print(ptr.GetLoadAddress()) 140732920755400  >>> ptr_type = ptr.GetType().GetPointeeType()  >>> print(ptr_type) float  >>> ptr_size_type = ptr_type.GetByteSize()  >>> print(ptr_size_type) 4   >>> for i in range (0, 4): ...     offset = ptr.GetValueAsUnsigned() + i * ptr_size_type ...     val = lldb.target.CreateValueFromAddress("temp", lldb.SBAddress(offset, lldb.target), ptr_type) ...     print(offset, val.GetValue()) ... (140732920755440, '1') (140732920755444, '2') (140732920755448, '3') (140732920755452, '4')                          

Cast

In a stripped binary - you can get a value from a register - as you will know the register position from documentation. But you won't have a variable symbol name and will probably need to help lldb with the Type. For example:

                              #                same information in `input` and `arg1`                              #                debug build              (lldb) fr v -L 0x00007ffeefbff4c8: (int              *) input = 0x00007ffeefbff4f0  (lldb) mem              read              0x00007ffeefbff4f0 -c 16 0x7ffeefbff4f0: 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00  (lldb) script >>> ptr = lldb.frame.FindVariable(                'input'              ) >>>              print(ptr.GetType()) int              *            

What happens if I don't have debug symbols ?

>>> ptr = lldb.frame.FindRegister(                "arg1"              ) >>>              print(ptr.GetType()) unsigned long  >>>              ptr.GetNumChildren() 1

Were you expecting it to have 4 children? lldb doesn't even know it is a pointer to a single integer or array of integers. At this point you are stuck, unless you help lldb.

                              >>> options = lldb.SBExpressionOptions()  // help lldb by casting to int * >>> val = lldb.frame.EvaluateExpression("(int *) $arg1", options) >>> print(val) (int *) $0 = 0x00007ffeefbff4f0 >>> print(val.GetType()) int *                          

Stripped Binary, decayed pointers and memory writes

Source code
              void              foo_void              (int              *input) {              printf(                "Pointer:                %p.\n                "              , input); }              int              main              (void) {              int              tiny_array[4];     tiny_array[0] =              1;     tiny_array[1] =              2;     tiny_array[2] =              3;     tiny_array[3] =              4;              foo_void              (tiny_array);              return              0; }

When the breakpoint fires - I want to modify tiny_array[3]. First, I will cast the register value to the type you expect:

>>> options =              lldb.SBExpressionOptions() >>> ptr = lldb.frame.EvaluateExpression(                "(int *)                $arg1                "              , options) >>> print(ptr) (int              *)              $0              = 0x00007ffeefbff4f0  >>>              print(ptr.GetType()) int              *              >>> ptr_type =              ptr.GetType().GetPointeeType() int  >>> ptr_size_type =              ptr_type.GetByteSize() 4  >>> offset =              ptr.GetValueAsUnsigned() + 3              *              ptr_size_type 140732920755452  >>> error =              lldb.SBError() >>> new_int_as_bytes = str(                '\xFF\x00\x00\x00'              ) >>> result = lldb.process.WriteMemory(offset, new_int_as_bytes, error) >>>              if              not              error.Success() or result              != len(new_int_as_bytes): ...              print('SBProcess.WriteMemory() failed!                              ')                            >>> offset = ptr.GetValueAsUnsigned() + 3 * ptr_size_type              >>> print(lldb.target.CreateValueFromAddress("temp", lldb.SBAddress(offset, lldb.target), ptr_type))              (int) temp = 255                            >>> for i in range (0, 4):              ... 	offset = ptr.GetValueAsUnsigned() + i * ptr_size_type              ... 	val = lldb.target.CreateValueFromAddress("temp", lldb.SBAddress(offset, lldb.target), ptr_type)              ... 	print(offset, val.GetValueAsUnsigned())                            (140732920755440,                '              1                ')              (140732920755444,                '              2                ')              (140732920755448,                '              3                ')              (140732920755452,                '              32                ')                            >>> exit              (lldb) mem read 0x00007ffeefbff4f0 -c 16              0x7ffeefbff4f0: 01 00 00 00 02 00 00 00 03 00 00 00 ff 00 00 00            

Find, read and amend variable inside Parent Frame

Source code
              void              foo_void              (              float              *input ) {              printf(                "Pointer:                %p.\n                "              , input);      <-- Breakpoint here }              int              main              (              void              ) {              float              tiny_array[4];     tiny_array[0] =              1.0;     tiny_array[1] =              2.0;     tiny_array[2] =              3.0;     tiny_array[3] =              4.0;              foo_void              ( tiny_array );              return              0; }
Solution
>>>              print(lldb.frame.GetFunctionName()) foo_void  >>>              print(lldb.frame.get_parent_frame().GetFunctionName()) main  >>> f = lldb.thread.GetFrameAtIndex(1)  >>> ptr = f.FindVariable(                'tiny_array'              )  >>> print(ptr) (float [4]) tiny_array = (1, 2, 3, 4)  >>> print(ptr.GetChildAtIndex(1)) (float) [1] = 2  >>>              print(ptr.AddressOf()) (float (*)[4])              &tiny_array = 0x00007ffeefbff540  >>>              print(ptr.AddressOf().GetType()) float (*)[4]  >>>              print(ptr.TypeIsPointerType()) False  >>>              print(ptr.GetNumChildren()) 4  >>>              print(ptr.GetLoadAddress()) 140732920755520  >>> ptr_type =              ptr.AddressOf().GetType()  >>> print(ptr_type) float (*)[4]  >>> pointee_type =              ptr_type.GetPointeeType()  >>> print(pointee_type) float [4]  >>>              print(pointee_type.GetByteSize()) 16  >>>              for              i              in              range (0,              ptr.GetNumChildren()): ... 	offset =              ptr.GetLoadAddress() + i              *              (pointee_type.GetByteSize() /              ptr.GetNumChildren()) ... 	print(offset, str(ptr.GetChildAtIndex(i))) ... (140732920755520,                              '(float) [0] = 1'              ) (140732920755524,                              '(float) [1] = 2'              ) (140732920755528,                              '(float) [2] = 3'              ) (140732920755532,                              '(float) [3] = 4'              )   >>> error =              lldb.SBError() >>> result = ptr.GetChildAtIndex(i).SetValueFromCString(                '0xFF'              , error)  >>> print(ptr.GetChildAtIndex(3)) (float) [3] = 255            

Structs

C code:

              // https://stackoverflow.com/questions/38251944/lldb-python-api-sbaddress-constructor-error  struct Foo {     int a;     int b; };  void bar_void ( void *input ) {     printf("Pointer: %p.\n", input);		// BREAKPOINT HERE }  int main ( void ) {     struct Foo my_foo = { 111, 222 };     bar_void ( &my_foo );     return 0; }                          

LLDB commands:

                              (lldb) fr v -L 0x00007ffeefbff528: (void *) input = 0x00007ffeefbff550  (lldb) mem read 0x00007ffeefbff4f0 -c 16 0x7ffeefbff4f0: 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00  (lldb) script  >>> ptr_type = lldb.target.FindFirstType('Foo').GetPointerType()  >>> print(ptr_type) struct Foo *  >>> print(type(ptr_type)) <class 'lldb.SBType'>  >>> root = lldb.target.CreateValueFromAddress("root", lldb.SBAddress(0x00007ffeefbff538, lldb.target), ptr_type)  >> print(root) (Foo *) root = 0x00007ffeefbff550  >>> root.GetValue() '0x00007ffeefbff550'  >>> root.GetChildAtIndex(0).GetValue() '111'  >>> root.GetChildAtIndex(1).GetValue() '222'                          

Symbols

              >>> process = lldb.debugger.GetSelectedTarget().GetProcess() >>> target = debugger.GetSelectedTarget() >>> target = lldb.debugger.GetSelectedTarget() >>> module = target.GetModuleAtIndex(0) >>> print(type(module)) <class 'lldb.SBModule'>  >>> for symbol in module: ... 	name = symbol.GetName() ... 	saddr = symbol.GetStartAddress() ... 	print(name, saddr)                          

Advanced

Check versions ( python, lldb )

script import sys; print(sys.version)

Launch

lldb attach -p $(ps x | grep -i -m1 debugger_challenge | awk '{print $1}') // 'debugger_challenge' is app name

Import lldb script

command source <file_path>/lldb_script.txt

Import Python script

command script import <file_path>/lldb_python.py

lldb command line ( no XCode )
              - Kill xcode - Run iOS app in the simulator - lldb attach -p $(ps x | grep -i -m1 debugger_challenge | awk '{print $1}')                          
Watch Packets ( caution )

log enable gdb-remote packets

Custom prompt

settings set prompt \-\>

Print with NSLog

exp (void)NSLog(@"πŸ˜€foobar woobar"); // on a real iOS device, you don't need to caflush for this to appear in console.app

Endians

              >>> if byteOrder == lldb.eByteOrderLittle: ... 	pass ... elif byteOrder == lldb.eByteOrderBig: ... 	print("big endian") ...   byteOrder.reverse()  >>> print(lldb.eByteOrderLittle) 4 >>> print(lldb.eByteOrderBig) 1                          

stdout

If you use lldb --wait-for or lldb -attach you are attaching after a decision on where to send stdout was made. For example:

              // NSLog only sent to Console.app when you attach  ./objc_playground_2 ps -ax lldb -p 3668  (lldb) exp @import Foundation (lldb) exp (void)NSLog(@"hello"); (lldb) c Process 3668 resuming < you can see the output to NSLog when you open console.app >                          

But you can control stdout.

              $) lldb (lldb) target create my_playground (lldb) process launch (lldb) exp @import Foundation (lldb) exp (void)NSLog(@"hello"); 2018-12-13 10:14:09.638801+0000 objc_playground_2[2776:61771] hello                          

Playing with the User Interface

lldb - print all View Controllers connected to current hierarchy

(lldb) po [[[UIWindow keyWindow] rootViewController] _printHierarchy]

lldb - Recursive description of current view

po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]

UILabel change text

Find the ID of the UIlabel after running the recursiveDescription command above.

              (lldb) e id $myLabel = (id)0x104ec9370  (lldb) po $myLabel <MyApp.CustomUILabel: 0x104ec9370; baseClass = UILabel; frame = (0 0; 287 21); text = 'Boring default text...'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x1d4291b70>>  (lldb) po [$myLabel superview] <UIStackView: 0x104ec8f70; frame = (56 0; 287 88); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x1d443a620>>  (lldb) p (int)[$myLabel setText:@"Bye World"] Nothing will happen.  You need to refresh the screen or continue the app.  (lldb) e (void)[CATransaction flush]                          

Change background Color

              (lldb) e id $myView2 = (id)0x104f474e0 (lldb)  v <UIView: 0x104f474e0; frame = (0 0; 375 603); autoresize = RM+BM; layer = <CALayer: 0x1d0239c20>> (lldb) e (void)[$myView2 setBackgroundColor:[UIColor blueColor]]  (lldb) caflush // this is the Chisel alias for e (void)[CATransaction flush]                          

TabBar

              po [[UIWindow keyWindow] rootViewController] e id $rootvc = (id)0x7fb9ce868200 expression -lobjc -O -- [`$rootvc` _shortMethodDescription] expression (void)[$rootvc setSelectedIndex:1] caflush expression (void)[$rootvc setSelectedIndex:0] caflush  (lldb) po [$rootvc selectedViewController] <tinyDormant.YDJediVC: 0x7fb9cd613a80>  (lldb) po [$rootvc viewControllers] <__NSArrayM 0x600001038810>( <tinyDormant.YDJediVC: 0x7fb9cd613a80>, <tinyDormant.YDMandalorianVC: 0x7fb9cd41f1c0> )                          

Part 1 : UITabBarController add a tab

              (lldb) po [[UIWindow keyWindow] rootViewController] <UITabBarController: 0x7fdf0f036000>  (lldb) e id $tbc = (id)0x7fdf0f036000  (lldb) po $tbc <UITabBarController: 0x7fdf0f036000>  (lldb) po [$tbc description] <UITabBarController: 0x7fdf0f036000>  // METHOD 1 (lldb) e Class $sithVcClass = (Class) objc_getClass("tinyDormant.YDSithVC") (lldb) e id $sithvc = (id)[$sithVcClass new] (lldb) po $sithvc <tinyDormant.YDSithVC: 0x7fb9cd426880>  // METHOD 2 e id $newClass = (id)class_createInstance($sithVcClass, 100);   (lldb) po [$tbc viewControllers] <__NSArrayM 0x6000029fc930>( <tinyDormant.YDJediVC: 0x7fdf0ef194e0>, <tinyDormant.YDMandalorianVC: 0x7fdf0ed23c50> )  // Create mutable array (lldb) e NSMutableArray *$listofvcontrollers = (NSMutableArray *)[$tbc viewControllers]  // Add and Delete and View Controller from the array (lldb) po [$listofvcontrollers addObject:$sithvc] (lldb) po [$listofvcontrollers removeObjectAtIndex:0]  // Print the array (lldb) po $listofvcontrollers <__NSArrayM 0x600001c32580>( <tinyDormant.YDMandalorianVC: 0x7fa476e15c40>, <tinyDormant.YDSithVC: 0x7fa476d033d0> )  (lldb) po [$tbc setViewControllers:$listofvcontrollers]  nil                          

Part 2 : UITabBarController beautify

                              (lldb) search UITabBar  <UITabBar: 0x7fa476e16be0; frame = (0 618; 375 49); autoresize = W+TM; gestureRecognizers = <NSArray: 0x60000082b690>; layer = <CALayer: 0x600000678b40>>   (lldb) e id $tabs = (id)0x7fa476e16be0   (lldb) po [$tabs items]  <__NSArrayI 0x600000826580>(  <UITabBarItem: 0x7fae2f6164c0>,  <UITabBarItem: 0x7fae2f6195a0>,  <UITabBarItem: 0x7fae2f502380> selected  )   (lldb) e int $sithIndex = [$listofvcontrollers indexOfObject:$sithvc]  (lldb) po $sithIndex  2   (lldb) po [[[$tabs items] objectAtIndex:$sithIndex] setBadgeValue:@"99"];   (lldb) e UIImage *$sithimage = [UIImage imageNamed:@"approval"];  (lldb) e [[[$tabs items] objectAtIndex:$sithIndex] setImage:$sithimage]   nil  (lldb) caflush                          

Part 3 : UITabBarController add tint color

              (lldb) po [$tabs barTintColor]  nil  (lldb) e (void)[$tabs setBarTintColor: [UIColor lightGrayColor]] 0x0000000108ea9e30  (lldb) caflush  (lldb) e (void)[$tabs setBarTintColor: [UIColor greenColor]] (lldb) caflush                          

Part 3 : UINavigationBar add Right-Sided Button

              (lldb) search UINavigationBar (lldb) e id $nc = (id)0x113d960e0 (lldb) po [$nc setBarTintColor: [UIColor greenColor]] (lldb) caflush                          

Push a new ViewController

              (lldb) po [[UIWindow keyWindow] rootViewController] <UINavigationController: 0x105074a00>  (lldb) e id $rootvc = (id)0x105074a00 (lldb) po $rootvc <UINavigationController: 0x105074a00>  (lldb) e id $vc = [UIViewController new] (lldb) po $vc <UIViewController: 0x1067116e0>  (lldb) e (void)[$rootvc pushViewController:$vc animated:YES] (lldb) caflush                          

Advanced : Wiring a more complicated U.I.

  • Find a UITabBarController in memory
  • Create a new UINavigationController
  • Create a new UIViewController
  • Connect the UIViewController to the UINavigationController
  • Create a new array of UINavigationControllers
  • Check the View Hierarchy to ensure it is wired correctly
                              (lldb) search UITabBarController  <UITabBarController: 0x7fa3b0029600>   (lldb) search UINavigationController  <UINavigationController: 0x7fa3b0813600>   (lldb) po id $nc = [[UINavigationController alloc] initWithRootViewController:$sithvc]  (lldb) po $nc  <UINavigationController: 0x7fa3af80a000>   (lldb) search UINavigationController  <UINavigationController: 0x7fa3af81a200>   <UINavigationController: 0x7fa3b0813600>   (lldb) po [$nc2 viewControllers]  <__NSSingleObjectArrayI 0x600002005570>(  <tinyDormant.YDSithVC: 0x7fa3af704430>  )   (lldb) e Class $sithVcClass = (Class) objc_getClass("tinyDormant.YDSithVC")  (lldb) e id $sithvc = (id)[$sithVcClass new]  (lldb) po $sithvc  <tinyDormant.YDSithVC: 0x7fa3af704430>  // NOW I NEED TO ADD IT...   (lldb) po [[UIWindow keyWindow] rootViewController] <UITabBarController: 0x7fa3b0029600>  (lldb) e id $tbc = (id)0x7fa3b0029600 (lldb) po $tbc <UITabBarController: 0x7fa3b0029600>  (lldb) e NSMutableArray *$listofvcontrollers = (NSMutableArray *)[$tbc viewControllers]  (lldb) po $listofvcontrollers  <__NSArrayM 0x600002cb89f0>(  <tinyDormant.YDJediVC: 0x7fa3af727240>,  <UINavigationController: 0x7fa3b0813600>  )  (lldb) po [$listofvcontrollers addObject:$nc]   (lldb) po $listofvcontrollers <__NSArrayM 0x600002cb89f0>( <tinyDormant.YDJediVC: 0x7fa3af727240>, <UINavigationController: 0x7fa3b0813600>, <UINavigationController: 0x7fa3af80a000> )  (lldb) po [$tbc setViewControllers:$listofvcontrollers]  (lldb) search UITabBar <UITabBar: 0x7fa476e16be0; frame = (0 618; 375 49); autoresize = W+TM; gestureRecognizers = <NSArray: 0x60000082b690>; layer = <CALayer: 0x600000678b40>>  (lldb) e id $tabs = (id)0x7fa476e16be0 (lldb) e UIImage *$sithimage = [UIImage imageNamed:@"UIBarButtonSystemItemFastForward"] (lldb) e (void)[[[$tabs items] objectAtIndex:$sithIndex] setImage:$sithimage]  nil  (lldb) caflush                          

WARNING - careful with copy and paste of text into lldb. I spent hours trying to work out why one the above commands was not working.

Facebook's Chisel

Read all Objects on a screen
Change a Swift View border
              (lldb) pvc <UINavigationController 0x7fa47905fa00>, state: appeared, view: <UILayoutContainerView 0x7fa47862e9c0>    | <DELETE_PROV_PROFILE_MACHINE.ydHomeVC 0x7fa47861dcc0>, state: appeared, view: <UIView 0x7fa4786327a0> (lldb) expr -l objc -- @import UIKit (lldb) border -c red -w 1.0 0x7fa4786327a0 (lldb) border -c red -w 5.0 0x7fa4786327a0                          
Find View Controller (fvc)
              (lldb) fvc --view=0x7fc2c4410970 Found the owning view controller. <MYAPP.ydHomeVC: 0x7fc2c443d850>                          
hide a View
              (lldb) pvc The current UIViewController that you want to hide… <UIViewController 0x1067116e0>, state: appearing, view: <UIView 0x10b707740>  lldb) hide 0x10b707740                          
Show a hidden ViewControlller
              var $window: UIWindow? $window = UIWindow(frame: UIScreen.main.bounds) let $mainViewController = ydHiddenVC() window?.rootViewController = $mainViewController $window?.makeKeyAndVisible()  https://medium.com/@Dougly/a-uiviewcontroller-and-uiviews-without-storyboard-swift-3-543096e78f2                          
UILabel fun
              <UIButtonLabel: 0x7f826bd2b090; frame = (0 3; 56 20.5); text = 'Submit'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600003dc3ed0>>    |    |    |    |    | <UILabel: 0x7f826be15710; frame = (148.5 12; 78.5 20.5); text = 'Feedback';  (lldb) mask 0x7f826bd2b090 (lldb) unmask 0x7f826bd2b090 (lldb) border -c yellow -w 2.0 0x7f826be15710 (lldb) border 0x7f826be15710                          
Cast from Swift to Objective-C object
              <UILabel: 0x7f826bd2e8c0; frame = (33 10.5; 302 20); text = 'General feedback'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600003db0e10>> (lldb) expression id $alien = (id)0x7f826bd2e8c0  // UILabel Object was created in Swift but you need access in Objective-C (lldb) po $alien <UILabel: 0x7f826bd2e8c0; frame = (33 10.5; 302 20); text = 'General feedback'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600003db0e10>> (lldb) exp (void*)[$alien setText:@"odd"] (void *) $11 = 0x0000000107116010 You won't see anything until you..  (lldb) caflush  (lldb) po $alien <UILabel: 0x7fd0b36444a0; frame = (172 12; 31 20.5); text = 'odd'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000031c8820>>                          
Demangle Swift ViewController Names
              (lldb) search UIView -m myFramework      // observe the de-mangled Swift name <myFramework.PageViewController: 0x7f826bf0c980>  (lldb) search UIViewController -m myFramework    // Great for Swift _TtC8myFramework18PageViewController * [0x00007f826bf0c980]  (lldb) search -r 0x7f826bf0c980       // Now get all references to ViewController                          

Thread Pause / Thread Exit

              Thread ID: dec:2264260 hex: 0x228cc4 Thread ID: dec:2264261 hex: 0x228cc5 shark: 0 jelly: 0 shark: 1 jelly: 1 shark: 2 jelly: 2 shark: 3 jelly: 3 shark: 4 jelly: 4 Program ended with exit code: 0                          
Output
                              (lldb) settings set thread-format "thread: #${thread.index}\t${thread.id%tid}\n{ ${module.file.basename}{`${function.name-with-args}\n"  (lldb) thread list  Process 3106 stopped  thread: #1    0x1659a  libsystem_kernel.dylib`__ulock_wait  * thread: #2    0x165f7  objc_playground_2`hello_world(voidptr=0x0000000100633f50)  thread: #3    0x165f2  libsystem_kernel.dylib`__workq_kernreturn  thread: #4    0x165f4  libsystem_kernel.dylib`__workq_kernreturn  thread: #5    0x165f8  objc_playground_2`hello_world(voidptr=0x0000000100634370)    (lldb) exp NSTimeInterval $blockThreadTimer = 2  (lldb) exp [NSThread sleepForTimeInterval:$blockThreadTimer]  (lldb) c  Process 49868 resuming  [+]Tiger Shark: thread ID: 0x14a075  [+]Lemon Shark: thread ID: 0x14a07a  Tiger Shark: 0  Tiger Shark: 1  Tiger Shark: 2  Tiger Shark: 3  Tiger Shark: 4  Lemon Shark: 5  Lemon Shark: 6  Lemon Shark: 7  Lemon Shark: 8  Lemon Shark: 9                          

help lldb by setting the language

I hit this error too many times!
              (lldb) e id $my_hello = [hello_from_objc new]  error: <EXPR>:3:3: error: consecutive statements on a line must be separated by ';' id $my_hello = [hello_from_objc new]   ^   ;                          
Tell lldb you are using Objective-C

(lldb) expression -l objective-c -o -- id $my_hello = [hello_from_objc new]

print all instance methods available to you..

expression -lobjc -O -- [$my _shortMethodDescription]

Set the expression language to Swift explicitly:

(lldb) expression -l swift -o -- let $myHello = HelloClass()

Set the expression language to Swift and call a function

(lldb) expression -l swift -o -- $myHello.hello()

Swift:

(lldb) settings set target.language swift

lldb & rootless

Apple's System Integrity Protection

              $ sudo lldb -n Finder (lldb) process attach —name "Finder" /* fails if you don't disable Rootless */                          

lldb bypass Certificate Pinning

Bypass overview

"Do I trust the server, before sending data?". You will often find that question in iOS and Android app code. It refers to certificate pinning.

The below script overwrites the answer to that question. The bypass requires a debugger (lldb) a scripting language (python) and writing values in memory (registers).

Result
              (lldb) br s -a 0x1000013ae -N fooName Breakpoint 2: where = objc_play`-[YDURLSessionDel URLSession:didReceiveChallenge:completionHandler:] + 334 at main.m:19:5, address = 0x00000001000013ae  (lldb) c Process 48838 resuming 🍭Start 🍭Challenged on: www.google.com 🍭Cert chain length: 3  (lldb) yd_bypass_urlsession       // run custom Python LLDB script  [*]URLSession trust bypass started [*]Original of NSURLSessionAuthChallengeDisposition: (unsigned long) rsi = 0x0000000000000002 [!]NSURLSessionAuthChallengeDisposition set to Cancel. [*]PATCHING result: pass 🍭HTTP Response Code: 200 🍭finish                          

Background

App's often used a completionHandler with Apple's NSURLSession on iOS and macOS when deciding whether to start a network request.

completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL);

The above line of code is typical of an app that has implemented Certificate Pinning and has decided to stop the network request from being sent.

Find the Needle in the Haystack

If you ingested the executable file into a disassembler like Hopper, you could find the assembly instruction to patch out the answer.

Hopper had a really nice pseudo code flow of: URLSession:didReceiveChallenge:completionHandler::

              /* @class YDURLSessionDel */ -(void)URLSession:(void *)arg2 didReceiveChallenge:(void *)arg3 completionHandler:(void *)arg4 {     var_30 = [[arg3 protectionSpace] serverTrust];     [[arg3 protectionSpace] host];     NSLog(cfstring___m__);     SecTrustGetCertificateCount(var_30);     NSLog(cfstring___m__);     (*(arg4 + 0x10))(arg4, 0x2, 0x0, arg4);     return; }                          

We care about the line: (*(arg4 + 0x10))(arg4, 0x2, 0x0, arg4);.

In assembly, that is this instruction:

              0x1000016ce <+174>: call   qword ptr [rcx + 0x10]                          

Sure enough, if you set a breakpoint on this instruction:

              (lldb) po $arg1 <__NSStackBlock__: 0x7000050deba8>  signature: "v24@?0q8@"NSURLCredential"16"  invoke   : 0x7fff30e47a04 (/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork`CFHTTPCookieStorageUnscheduleFromRunLoop)  copy     : 0x7fff30d3b7ed (/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork`CFURLCredentialStorageCopyAllCredentials)  dispose  : 0x7fff30d3b825 (/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork`CFURLCredentialStorageCopyAllCredentials)  (lldb) po $arg2 2  (lldb) po $arg3 <nil>  (lldb) po $arg4 <__NSStackBlock__: 0x7000050deba8>  signature: "v24@?0q8@"NSURLCredential"16"  invoke   : 0x7fff30e47a04 (/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork`CFHTTPCookieStorageUnscheduleFromRunLoop)  copy     : 0x7fff30d3b7ed (/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork`CFURLCredentialStorageCopyAllCredentials)  dispose  : 0x7fff30d3b825 (/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork`CFURLCredentialStorageCopyAllCredentials)                          

What is the 2 value in the second register (arg2)? If a server and connection was trusted or not, the result was often this value:

              typedef NS_ENUM(NSInteger, NSURLSessionAuthChallengeDisposition) {   NSURLSessionAuthChallengeUseCredential = 0,                                       /* Use the specified credential, which may be nil */   NSURLSessionAuthChallengePerformDefaultHandling = 1,                              /* Default handling for the challenge - as if this delegate were not implemented; the credential parameter is ignored. */   NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2,                       /* The entire request will be canceled; the credential parameter is ignored. */   NSURLSessionAuthChallengeRejectProtectionSpace = 3,                               /* This challenge is rejected and the next authentication protection space should be tried; the credential parameter is ignored. */ }                          
Breakpoint and script

Most of the effort and skill was placing a breakpoint.

              (lldb) br s -a 0x1000013ae -N fooName                          

You could then - now you have named the breakpoint - add instructions to the breakpoint OR you could invoke a Python script from the command line.

I choose to invoke my own Python script so it was simple to re-use this code on other apps. The main lines of the script were:

              frame = exe_ctx.frame disposition = frame.FindRegister("rsi") if disposition.unsigned == 2:      print "[!]NSURLSessionAuthChallengeDisposition set to Cancel."      result = frame.registers[0].GetChildMemberWithName('rsi').SetValueFromCString("0x1", error)      messages = {None: 'error', True: 'pass', False: 'fail'}      print ("[*]PATCHING result: " + messages[result])                          

The trick was frame = exe_ctx.frame. If you didn't have this context - from https://lldb.llvm.org/use/python-reference.html - you would get stuck for hours / days.

Try, try and try again

Like most bypass code, I tried multiple ideas. I removed the details of failed ones for brevity. If you care, essentially they were:

  • Set completionHandler to NULL
  • Overwrite the instruction with no operation ( a NOP instruction )
  • Passing a NULL Objective-C block
  • Passing a fake Objective-C block
  • Drop the (NSURLAuthenticationChallenge *)challenge ( failed as a lot of code depends on this challenge)
Source
              @interface YDURLSessionDel : NSObject <NSURLSessionDelegate> @end  @implementation YDURLSessionDel - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{      SecTrustRef trust = [[challenge protectionSpace] serverTrust];     NSLog(@"🍭Challenged on: %@", [[challenge protectionSpace] host]);     NSLog(@"🍭Cert chain length: %ld", (long)SecTrustGetCertificateCount(trust));      completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL); } @end  int main(void) {     @autoreleasepool {          dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);         NSURL *url = [NSURL URLWithString:@"https://www.google.com"];         YDURLSessionDel *del = [[YDURLSessionDel alloc] init];         NSURLRequest *request = [NSURLRequest requestWithURL:url];          NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];         NSLog(@"🍭 @property waitsForConnectivity default: %hhd", config.waitsForConnectivity);         config.waitsForConnectivity = YES;          NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:del delegateQueue:nil];         NSLog(@"🍭 start");         NSURLSessionDataTask *task = [session dataTaskWithRequest: request                                                 completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {                                      if (error) {                                         if(error.code == -999)                                             NSLog(@"🍭 Bypass failed. Connection: %@ ( %ld)", [error localizedDescription], (long)error.code);                                     }                                     if ([response isKindOfClass:[NSHTTPURLResponse class]]) {                                         NSLog(@"🍭 HTTP Response Code: %ld", (long)[(NSHTTPURLResponse *)response statusCode]);                                     }                                     dispatch_semaphore_signal(semaphore);                             }];         [task resume];         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);         NSLog(@"🍭 finish");      }     return 0; }                          

lldb on Jailbroken iOS device

                              #                macOS - install iProxy              brew install usbmuxd                              #                for lldb over USB access              iproxy 6666 6666              &                              #                For SSH over USB access              iproxy 2222 22              &                              #                SSH onto jailbroken device              ssh -p 2222 root@localhost                              #                get process ID              ps -ax              |              grep -i my_app                              #                invoke lldb              debugserver localhost:6666 -a my_app                              #                Hijack the app, before it starts              debugserver localhost:6666 --waitfor my_app                              #                start lldb from Terminal on macOS              $ lldb (lldb) process connect connect://localhost:6666 (lldb) thread list
Checkra1n Jailbreak

An SSH server is deployed on port 44 on localhost only.

iproxy 4444 44              &              ssh -p 4444 root@localhost

Although SSH is installed by default, you need scp for file copying. I installed openSSH via Cydia.

Electra Jailbreak

Electra shipped with debugserver. Previous jailbreaks had lots of manual steps to get the correct debugserver onto the device. electra

Electra app will not open

If you have a full Apple iOS developer license, you can code-sign ad-hoc apps to last one year. If the Electra app won't open, you can re-code sign the ipa file. One way to achieve this:

  • Open Cydia Impactor
  • Select \Device\InstallPackage
  • Find the Electra.ipa file
  • When prompted by Cydia Impactor enter your Apple ID.
  • Do NOT enter your password. Go to https://appleid.apple.com/ and generate a APP-SPECIFIC PASSWORD

Now Electra will work for another year.

Electra specifics

When you select Tweaks, Electra runs the debug-server from a different path:

                              #                Tweaks enabled              /Developer/usr/bin/debugserver localhost:6666 -a 794                              #                Tweaks disabled              /usr/bin/debugserver localhost:6666 -a 794
Terminted due to Code Signing Error

You need to change the entitlements inside the app bundle. Specifically: <key>get-task-allow</key>:

security cms -D -i embedded.mobileprovision | grep -i -A 1 "get" <key>get-task-allow</key> 	<true/>
References
              https://github.com/dmayer/idb/wiki/How-to:-ssh-via-usb https://kov4l3nko.github.io/blog/2018-05-25-my-experience-with-lldb-and-electra-jb/                          

lldb bypass iOS Jailbreak detections

Connect debugger

Set your iOS debugserver to wait for the app to be opened.

              ssh onto Jailbroken devices  Install the app on JB device  root# /Developer/usr/bin/debugserver localhost:6666 -v DEBUG=1 -waitfor MYAPP   // on JB device ssh session  OPEN THE APP now the debugserver is waiting for a connection  $) LLDB_SDK=ios lldb // from macOS machine  (lldb) process connect connect://localhost:6666                          
Find the target
              (lldb) lookup jail **************************************************** 2 hits in: MYAPP **************************************************** -[RSADeviceInfo jailBreak] -[RSADeviceInfo setJailBreak:]                          
Attack the Setter
              (lldb) b -[RSADeviceInfo setJailBreak:] Breakpoint 2: where = MYAPP`-[RSADeviceInfo setJailBreak:], address = 0x00000001033fe1fc  (lldb) c Process 1874 resuming Process 1874 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 2.1     frame #0: 0x00000001033fe1fc MYAPP` -[RSADeviceInfo setJailBreak:]   (lldb) p (char *) $arg2 (char *) $2 = 0x00000001034bb104 "setJailBreak:"  (lldb) p (char *) $arg3 (char *) $4 = 0x0000000000000005 <no value available>  (lldb) p (int) $arg3 (int) $5 = 5. // this is the value  register write $arg3 0 // put the value 0 (clean?) in the setter                          
Attack the getter

Kill the app and start the whole process of connecting again.

              (lldb) b -[RSADeviceInfo jailBreak] Breakpoint 1: where = MYAPP`-[RSADeviceInfo jailBreak], address = 0x0000000100b821ec  Process 1957 stopped  (lldb) step until the return register is set $x0 on a physical iOS device      frame #0: 0x0000000100b821f8 MYAPP` -[RSADeviceInfo jailBreak]  + 12 MYAPP`-[RSADeviceInfo jailBreak]: ->  0x100b821f8 <+12>: ret  (lldb) po (int) $x0 5  (lldb) register write $x0 0  (lldb) p (int) $x0 0 🐝🐝 Success 🐝🐝.                          
Summary

The getter example is a little more work; you have to place the breakpoint, step until it sets the return register and then modify the return value. All of that can be automated. Changing the Setter is normally a one-time only call.

lldb inspect third party SDK

Get helper lldb scripts

https://github.com/DerekSelander/LLDB

Dump classes

dclass -o my_app

search classes on Heap

              (lldb) search RSADeviceInfo <RSADeviceInfo: 0x1d019e780>                          

Inspect interesting Methods

              (lldb) methods 0x1d019e780 <RSADeviceInfo: 0x1d019e780>: in RSADeviceInfo: 	Properties: 		@property (retain) NSString* Timestamp;  (@synthesize Timestamp = Timestamp;) 		@property (retain) NSString* HardwareID;  (@synthesize HardwareID = HardwareID;) 		@property (retain) NSString* SIM_ID;  (@synthesize SIM_ID = SIM_ID;) 		@property (retain) NSString* PhoneNumber;  (@synthesize PhoneNumber = PhoneNumber;) 		@property (retain) RSAGeoLocationInfo* GeoLocation;  (@synthesize GeoLocation = GeoLocation;) 		@property (retain) NSString* DeviceModel;  (@synthesize DeviceModel = DeviceModel;)                          

Invoke instance methods

              (lldb) po [0x1d019e780 DeviceName] Security iPhone 8  (lldb) po [0x1d019e780 DeviceModel] iPhone  (lldb) po [0x1d019e780 jailBreak] 0x0000000000000005  // very jailbroken                          

Create a class

              (lldb) settings set target.language objc  (lldb) exp RSADeviceInfo *$rsa = (id)[[RSADeviceInfo alloc] init]  (lldb) po $rsa <RSADeviceInfo: 0x1c819ddc0>                          

lldb lifting code ( iOS app )

This article was written to show:

  • a framework can be ripped out of an iOS app
  • you can invoke Objective-C code without even importing the Modules
Find the target app

I found an app that was using a phone number validator. This was publicly available from: https://github.com/iziz/libPhoneNumber-iOS

Extract the App

I downloaded the app via the AppStore and then extracted it from a jailbroken device.

Extract the Framwork

Inside the app bundle, copy the entire libPhoneNumber-iOS framework. Ignore the fact the Framwork is codesigned. xCode will resign the Framework later, when you create a fresh app.

Xcode

Create a new hello world project and drag in your lifted Framework.

The "lifted" code inside an iOS app was thinned. Don't try and run this on a simulator. Run the app in debug mode and connect lldb.

Dump the classes
              [+] script to dump classes from: https://github.com/DerekSelander/LLDB  dclass -m libPhoneNumber_iOS  Dumping classes ************************************************************ NBPhoneNumberUtil NBPhoneNumberDesc                          
attach lldb
              (lldb) exp id $a = [NBPhoneNumberUtil new] (lldb) po $a <NBPhoneNumberUtil: 0x1c1c6b580>  (lldb) expression -lobjc -O -- [$a _shortMethodDescription] // dumps all available Class, Properties and Instance Methods  (lldb) exp NSString *$b = @"497666777000" (lldb) exp NSString *$nn = nil (lldb) exp NSNumber *$cc = (NSNumber *)[$a extractCountryCode:$b nationalNumber:&$nn] // using the github page, find how to invoke a method via Objective C, then apply it via lldb  (lldb) p $cc (__NSCFNumber *) $cc = 0xb0000000000002c3 (long)49 (lldb) p $nn (__NSCFString *) $nn = 0x00000001c4227be0 @"7666777000"                          

lldb references

Man page

http://lldb.llvm.org/man/lldb.html

Patching code

https://www.inovex.de/blog/lldb-patch-your-code-with-breakpoints/

LLDB-Python script with excellent examples

https://rderik.com/blog/scanning-a-process-memory-using-lldb/

UI manipulation

https://www.objc.io/issues/19-debugging/lldb-debugging/

Custom breakpoints

https://lldb.llvm.org/use/python-reference.html#using-the-python-api-s-to-create-custom-breakpoints

ASM arm instructions

https://armconverter.com/

iOS Syscalls

https://www.theiphonewiki.com/wiki/Kernel_Syscalls#List_of_system_calls_from_iOS_6.0_GM_

iOS Syscalls

https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/kern/syscalls.master

Debugger technical background

https://eli.thegreenplace.net/2011/01/23/how-debuggers-work-part-1

Breakpoint commands

https://developer.apple.com/library/archive/documentation/General/Conceptual/lldb-guide/chapters/C3-Breakpoints.html

Hardware Breakpoints

https://reverse.put.as/2019/11/19/how-to-make-lldb-a-real-debugger/

watchpoints

https://www.raywenderlich.com/books/advanced-apple-debugging-reverse-engineering/v3.0/chapters/8-watchpoints#toc-chapter-011-anchor-001

Fun cheat sheet

https://gist.github.com/alanzeino/82713016fd6229ea43a8

Inspiration

https://github.com/DerekSelander/LLDB

Multi-line lldb commands

https://swifting.io/blog/2016/02/19/6-basic-lldb-tips/

lldb cheatsheet

https://www.nesono.com/sites/default/files/lldb%20cheat%20sheet.pdf

some lldb commands

https://gist.github.com/ryanchang/a2f738f0c3cc6fbd71fa

great lldb overview

https://www.bignerdranch.com/blog/xcode-breakpoint-wizardry/

more lldb info

https://www.objc.io/issues/19-debugging/lldb-debugging/

lldb | python References

https://lldb.llvm.org/python-reference.html

ptrace Reference

https://www.unvanquished.net/\~modi/code/include/x86\_64-linux-gnu/sys/ptrace.h.html

ptrace Anti-debugging

http://www.vantagepoint.sg/blog/89-more-android-anti-debugging-fun

ptrace Anti-debugging

https://knight.sc/debugging/2018/08/15/macos-kernel-debugging.html

ptrace enum values

http://www.secretmango.com/jimb/Whitepapers/ptrace/ptrace.html

anti-debug code samples

https://gist.github.com/joswr1ght/fb8c9f4f3f9a2feebf7f
https://www.theiphonewiki.com/wiki/Bugging\_Debuggers

lldb and Blocks

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html

Different ARM and x86 Registers

https://azeria-labs.com/arm-data-types-and-registers-part-2/

petersonthetooday.blogspot.com

Source: https://github.com/rustymagnet3000/lldb_debugger

0 Response to "Debugger Commands Po Call Frame Variable Continue"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel