This is the sixth day of my participation in Gwen Challenge

IOS basic principles + reverse article summary

This article focuses on OC object disassembly and disassembly of common types of blocks

OC disassembly

Create a Person class and initialize a Person object in the main function

@interface Person : NSObject

@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) int age;

+ (instancetype)person;

@end

@implementation Person

+ (instancetype)person{
    return [[self alloc] init];
}

@end

<!--main.m中-->
int main(int argc, char * argv[]) {
    
    Person *p = [Person person];

    return 0;
}
Copy the code
  • Run to view its assembly code

1. Static debugging

Obtain the address through adRP +add, and read x0 and x1 respectively

  • Read x0 and read Person:x 0x100c68eb0 + po 0x0100c68f98

  • Read x1 and get the person method:x 0x100c68e88 + p (SEL)0x01c019aef5

2. Dynamic debuggingVerify that x0 and X1 are consistent with static debugging results by executing the assembly step by step.And it turns out, through debugging, that it’s consistent, actually, hereX0, x1isobjc_msgSendHidden parameter of(id self, SEL _cmd)

Let’s continue debugging assembly

  • Click step into to enter directly[Person person]Method (note: the assembly code seen here is different for different iOS versions)
    • In ios13.4, alloc and init do not go objc_msgSend

    • In ios11, you can see objc_msgSend, which is essentially calling the init method

Dynamic debugging for verification, the results as shown below, is consistentIf you look at x0 at this point, it’s already an instance object, because alloc has opened up memory, and space has been allocated. See this article for an internal implementationIOS – Underlying principles 02: Alloc & Init & New source code analysis Question: why is the call different from version to version?– The system runs differently under different versions. Because the system has optimized alloc and init

  • Then look down and click Step out to pop out [Person Person], which returns the value in X0

  • Perform tobl ... objc_storeStrongObjc_storeStrong is the OC object decorated with strong. This function is called at the bottom. See this article for detailsIOS – Underlying principles 10: Strong&Copy & Weak Low-level analysis and method signatures and attribute abbreviations.

Question: Are we not using the strong modifier at this point?: The local variable p is a strong reference by default. And after this method is executed, it is equivalent to destroying PView the currentX0, x1, which is equivalent toObjc_storeStrong (&p, nil), retain nil, equal to p (i.e. P =nil), p is released

  • To viewobjc_storeStrongThe source code
  • purposeRetain +1 for a strong decorated object and release for an old object
    • Why Pointers? Because the function is a value transfer, and inside the function you need to change the value of p
/* -id *location a pointer to an object is in essence &p (i.e. a local variable address) -id obj object */ void objc_storeStrong(id *location, id obj) {//prev = p, location = &p id prev = *location; If (obj == prev) {return; } //retain+1 objc_retain(obj); // Change the value of p to point to the second object *location = obj; // Release the old objc_release(prev); }Copy the code

The equivalent of

Person *p = p1; p = p2; P2retain +1Copy the code

So the implementation code for objc_storeStrong (& P, nil) in the above assembly is as follows

Objc_storeStrong (&p, nil) {id prev = p; if nil == p{ return; } objc_retain(nil); p = nil; // pointer to nil objc_release(p); // Free heap space}Copy the code
  • So let’s do dynamic verification and find outThe Person object points to nil

[[self alloc] init] Optimization process

  • In the original version (iOS9), this was the equivalent of two messagesobjc_msgSend
  • In iOS11, a message is sentobjc_alloc + objc_msgSend
  • For iOS13.5.1 and up, objc_msgSend is no longer objc_msgSendobjc_alloc_init

Person *p = [Person Person]; / / objc_msgSend x0, x1

Look at complex OC code through tools

Add some code to the OC code above, and then do the static analysis

int main(int argc, char * argv[]) { Person *p = [Person person]; //objc_msgSend x0, x1 p.name = @"CJL"; p.age = 18; return 0; }Copy the code
  • CMD + B compiles the program, generates the Mach-o file, and finds it
  • The analysis of the main function from the Hopper disassembly mach-o file is as follows

  • Double click on the objc_cls_ref_Person, check the address of P, yes000000010000ce88Is in the Data section

throughMachOViewOpen Mach-O analysis and find000000010000ce88, is consistent with the display in Hopper

  • Double click on the @selector(person)Look at the disassembly of the Person method

Double click on the 0x10000cc68 Double click on the"Person", the address is 0x10000752aLook in Mach0x10000752a, all method names are presentCStringIn the

Block the disassembly

Define a block

int main(int argc, char * argv[]) {
    
    void(^block)(void) = ^(){
        NSLog(@"block");
    };
    block();

    return 0;
}
Copy the code

The purpose of disassembling a block is to locate the block quicklyinvokeSince invoke is the implementation code, here is the assembly code for block

  • To viewx0What is?: is a__block_literal_global, it is aGlobal static block(i.e., block,Do not reference block external variablesIn theCompile timeYou canDetermine the memoryOperations such as allocation exist in executable filesThe constant area). Other details are also availableIOS – Basic Principles 30: Block basic principlesThis article

The following is the source block definition, is a structure

struct Block_layout{
    void *isa;
    volatile int32_t flags; //contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    //imported variables
};
Copy the code

Then debug dynamically to see the block’s memory structure

  • Is it possible to check adRP + add is a block using Hopper?

The answer is yes– double click on the___block_literal_global – double click on the 0x0000000100006838To see theinvoke – double click on the 0x0000000100008008View Descriptor, similar to the source structure of Block

What if the block refers to an external variable?

Define a block that refers to an external variable and look at the assembly code

int main(int argc, char * argv[]) {
    int a = 10;
    void(^block)(void) = ^(){
        NSLog(@"block -- %d", a);
    };
    block();

    return 0;
}
Copy the code

1. LLDB debugging

  • Below is a compilation of the code

  • Verify that it is an ISA pointer to a block

adrp x10, 2Get pointer address –ldr x10, [x10]Value:

  • Look at the block’s memory at this point and find invoke (invoke is implemented in code and needs to be called bydis -s(Print out an assembly of the code)

2. Static analysis

  • The hopper static analysis is as follows, and the following is a disassembly of the main function

  • Double click on the ___main_block_invokeJump to the concrete implementation of invoke (not in main, a separate implementation)

  • Double click on the___block_descriptor_36_e5_v8? 0l“Is a separate description

conclusion

  • [[self alloc] init] Optimization process

    • In the original version (iOS9), this was equivalent to sending objc_msgSend twice

    • In iOS11, the message is sent objC_alloc + objc_msgSend once

    • For iOS13.5.1 and up, objc_msgSend is no longer objc_alloc_init

  • Disassembly analysis method:

    • Dynamic debugging through LLDB

    • Static analysis by Hopper + MachOView