This is the 10th day of my participation in the August More text Challenge. For details, see: August More Text Challenge
Said to instrumentObjcMessageSends, so this method is how to?
SUPPORT_MESSAGE_LOGGING is identified in log_and_fill_cache.
If you go to the logMessageSend method, you see that it writes to/TMP /msgSends-%d.
So as long as objcMsgLogEnabled, then it’s going to write. Search for objcMsgLogEnabled. Found in instrumentObjcMessageSends facing the assignment
So, instrumentObjcMessageSends was found.
Fast forwarding process
In this paper, we see that on will also go the forwardingTargetForSelector msgSends.
Search forwardingTargetForSelector in the developer documentation. The discovery explains that if the method is executed and returns a result that is not empty, the returned result will be treated as a new message receiver, and the message flow will be redone on the new receiver. If it returns itself, then it loops indefinitely.
Here’s how:
And when you run it, you see that before you crash, you come to this method.
Said forwardingTargetForSelector can switch in front of the receiver, then implement this method in another object, then the object inside the forwardingTargetForSelector convert into new message recipients.
Run it, find successful call LGTeacher method, and the program does not crash.
So why don’t we just write the method to LGPerson. In fact here’s role is to prevent the collapse method, as so passed quickly find, slow lookup, resolution method of dynamic here, that method is not implemented, then we can create a special class, and then use this class to add methods, so that other classes are still safe and healthy.
So what if LGTeacher doesn’t have a way to do that? So here comes the slow forwarding process,
Slow forwarding process
Behind a saw before forwardingTargetForSelector methodSignatureForSelector, in developing document search.
This method is used with the Fore Invocation:
So let’s do that.
Let’s run it.
And the reason for the invocation is that it wasn’t implemented, so let’s do that.
If you run it, you still get an error. The reason is that no signature was returned. The return signature needs to be added. Click on NSMethodSignature to see how to create signatures, and you find a signatureWithObjCTypes method.
Add:
Run discovery did not crash, but no action was taken.
At the system level, all method functions are called system messages, also known as transactions. The transaction can be done or not. If you go through so many processes, you get to slow forwarding, and the signature is presented, then the system will save the transaction, but it will not be triggered. That means if the forwardInvocation is not processed in the method, it won’t break. Check to see if it’s actually saved.
I ran it, and it did.
Of course you can also handle the Invocation transaction to make the method work.
So if we do slow forwarding in NSObject, then all the methods that don’t exist are not going to get an error. Experiment.
It’s true that it doesn’t crash here, but it’s not recommended because it’s a waste of memory and effort.
Explore the Hopper Disassembler/IDA decompile
If you don’t know instrumentObjcMessageSends method, then can also through the Hopper Disassembler or IDA to decompiled exploration. After running the program found a crash, input BT to view the process. DoesNotRecognizeSelector is found in the CoreFoundation framework with ___forwarding___ and _CF_forwarding_prep_0 in front of it.
CoreFoundation is not fully open source, so if ___forwarding___ and _CF_forwarding_prep_0 are not found on the official website, you should explore decompilation using Hopper Disassembler/IDA. Drag the CoreFoundation executable into Hopper and select X86 (64bits), then search for __forwarding_. Notice that ___forwarding___ is called after _CF_forwarding_prep_0.
Click go in, determine whether response forwardingTargetForSelector method here, if there is no response, jump to loc_64a67. That is, the fast forwarding process does not respond, and the slow forwarding process starts. If the call returns null or itself, it is referred to loc_64a67, otherwise proceed further.
Then loc_64a67 determines if it is a zombie, if it is, go to loc_64dc1, otherwise go down. Then decide whether you can response methodSignatureForSelector. This is where the slow lookup process begins. If methodSignatureForSelector return values are not empty, they will continue to walk down to the _forwardStackInvocation. The _forwardStackInvocation is an internal system invocation that is not exposed and cannot be invoked.
Go down to the loc_64C19 and see the forwardInvocation. The response goes down to the call, and if not, it jumps to loc_64ec2.
Flow chart of the message forwarding mechanism
Why does resolveInstanceMethod go twice
Why does resolveInstanceMethod go twice. Go to the place where resolveInstanceMethod is called, hit a breakpoint and run.
It turns out that this is indeed the method called in Main.
On the second entry, enter the BT view method in the LLDB. ResolveInstanceMethod is called after _CF_forwarding_prep_0, So it comes in after _CF_forwarding_prep_0 ->___forwarding___->doesNotRecognizeSelector.
Find the place where doesNotRecognizeSelector was initiated
And then to call loc_64e3c local search, discovery was methodSignatureForSelector will come in when no response.
Go down inside doesnot Trecognizeselector, then go to loc_64ec2. If you look at loc_64EC2, you’ll get to loc_64ed1.
If you look at loc_64ed1, that’s where the whole process is.
Go to ____forwarding___.cold.4, do some callback here, call the internal system method and go to libsystem, then go to class_respondsToSelector_inst.
Look up class_respondsToSelector_inst and find that lookUpImpOrNilTryCache is called, and lookUpImpOrNilTryCache goes into lookUpImpOrForward, So that’s why the dynamic method resolution is called twice.
Through code derivation
To open and print the forwardingTargetForSelector method, if resolveInstanceMethod before forwardingTargetForSelector, before it’s infinite recursion, The second call to resolveInstanceMethod is made after a fast forward
Run it, and found that after the second resolveInstanceMethod forwardingTargetForSelector calls. That may be forwardingTargetForSelector call or call later.
Try again slowly forward process, find it after methodSignatureForSelector print resolveInstanceMethod.
Try the First Invocation again, and see that the resolveInstanceMethod comes before the First Invocation
Here you can know: the second dynamic method resolution between methodSignatureForSelector and forwardInvocation method.
conclusion
The general process of objc_msgSend:
- Quick lookup – Look for method implementations in the method cache of the class and its parent class or metaclass and its parent metaclass.
- Slow lookup – Looking for methods in the method list of the class and its parent class or metaclass and its parent metaclass.
- Dynamic message resolution – if slow search couldn’t find chances for the first time resolution is to try a dynamic method, namely rewrite resolveInstanceMethod/resolveClassMethod method.
- Fast forwarding –
forwardingTargetForSelector
To process the message receiver. - Slow forwarding –
methodSignatureForSelector
As well asforwardInvocation
Process the method signature. - If there is no after forwarding, the program directly reported an error crash
Unrecognized selector sent to instance.