Advanced+apple+debugging (17)

Source: Internet
Author: User
Tags python script

Scary compilation, Part two

It's time to revisit Objc_class: The second interesting part of the:D Emangledname (BOOL) C + + function. This time the assembler code will focus on if Char is not in the initial position of Char -that is, If the class has not been loaded yet, what does the logic do?
You need to create a breakpoint at the assembly instruction position immediately following the offset 61 at offset 55.
You can call a class to see which classes are not loaded and run, and I don't know what's going on in your process and you don't know what's going on in my process!
Instead, a symbolic breakpoint is created at a position that stops at Objc_class::d emangledname (BOOL) at offset 61.
Use the following steps in Xcode to create a symbolic breakpoint:
? Use Dlopen for this symbolic
? First step: Remove this breakpoint with BR Dis 1
? Step Two: Set a breakpoint at Objc_class::d emangledname (BOOL) offset 61 with the following command

BR set-m Objc_class::d emangledname (BOOL)-R 61
? Select "Automatically continue after evaluating actions".

Picture. png

Re-build and run the Vctransitions application.
You will not go very deep into your program until this breakpoint is punished, and you will see that Dyld is still busy setting up.
The second round; we depart from here:
Picture. png

? Offset 61: Provides the initialization location in memory is Nil, continues to run to the Rax + 0x8 and then stores it again in Rax.
? Offset 65: Value 0x18 is added to Rax and then saved back to Rax. Rax may be a struct that holds data that can explain the offset of this address.
? The value of Offset 69:rax is then referenced and stored in RBX, which is later passed to the RDI 2 instruction. After that, the function calls the Copyswiftv1demangledname function and sets the logic to add the class to the runtime.
But for you, until you need to navigate through this function.
Always confirm that RDI will produce a valid char* at offset 77, but again, it will be done in your own time. You still need to write a dtrace script.
Re-convert to code search

You've done the necessary re-search to figure out how to snake in memory. Gets an array of characters representing a class. It's time to do it.
There is a dtrace script called MSGSENDSNOOP.D in the Starter folder. You'll start with this dtrace script and build the code accordingly. If testing is possible, you need to convert that code into a Lldb Python script that allows you to dynamically get the code you want.
In the terminal, CD to the Starter folder. The path is automatically generated by dragging the folder to the terminal.
The contents of the cat script:

Cat./MSGSENDSNOOP.D
Here's what the output says:

#!/usr/sbin/dtrace-s
#pragma D option quiet
Dtrace:::begin
{
printf ("Starting ... Hits ctrl-c to end.\n ");
}
Pid$target::objc_msgsend:entry
{
This->selector = Copyinstr (arg1);
printf ("0x%016p, +|-[%s%s]\n", arg0, "TODO",
}
Let's break it down. This script will stop at the entrance probe of the objc_msgsend that is passed into the corresponding PID (this is the function of Pid$target). Once triggered, selector's char* is copied to the kernel and printed out.
As will happen in the example, that is,-[uiview initWithFrame:] will be called. Will print out the following content:

0x00000000deadbeef, +|-[TODO initWithFrame:]
You can verify that this is true by tracing the objc_msgsend of all calls in the Vctransitions program.

sudo./msgsendsnoop.d-ppgrep VCTransitions
Click on some classes. This lets you see how often these methods are called.

Picture. png
It's time to fix the annoying TODO and replace him with the actual name of the class.
Open the MSGSENDSNOOP.D file and replace Pid$target::objc_msgsend:entry with the following code:

Note: I would recommend that you enter each line of code and then make sure it is operational, rather than entering all the code at once. Some of the DTrace errors are captured.
Pid$target::objc_msgsend:entry
{
/ 1 /
This->selector = Copyinstr (arg1);
/ 2 /
size = sizeof (uintptr_t);
/ 3 /
This->isa = ((uintptr_t ) copyin (arg0, size));
/ 4 /
This->rax = ((uintptr_t ) copyin ((This->isa + 0x20), size);
This->rax = (This->rax & 0X7FFFFFFFFFF8);
/ 5 /
THIS->RBX = ((uintptr_t ) copyin ((This->rax + 0x38), size);
This->rax = ((uintptr_t ) copyin ((This->rax + 0x8), size);
/ 6 /
This->rax = ((uintptr_t ) copyin ((This->rax + 0x18), size);
/ 7 /
This->classname = COPYINSTR (this->rbx! = 0?
This->rbx:this->rax);
printf ("0x%016p +|-[%s%s]\n", arg0, This->classname,
This->selector);
}
take a deep breath. Here's what each line of code means:

This->selector did a copyinstr because you know that the second argument (ARG1) is a objective-c selector (it's a C string). Because C char* is terminated with a null character, DTrace can automatically detect how much data to read.
in the blink of an eye, you're going to copyin some data. However, Copyin needs a size because unlike string, DTrace does not know where the data ends. You declare a variable called size, which is equal to the length of a pointer. In x64, it is 8 bytes.
This is how you get a reference to a class instance. Remember that a pointer to the starting position of the OBJECTIVE-C or Swift class instance points to the class.
Now is the interesting part you learned in Objc_class::d Emangledname (BOOL) assembly. You will copy the logic found in the register and even use the same register name! You are using Rax to simulate the logic of this function execution.
This is the code (Rax + 0x38) Setting the THIS->RBX, just like in the real assembly.
If THIS->RBX is 0, this is the last line of code (this class has not yet been loaded).
you are using a ternary operator to figure out which local variable is being used. If THIS->RBX is non-null, use THIS->RBX. Otherwise, This->rax is used.
Save the work you just did. Go back to the terminal and start the DTrace script from the heart:
sudo./msgsendsnoop.d-p pgrep vctransitions
Whoa, whoa, whoa, whoa, whoa. ! That crazy script really works!
Scan the contents of your script and it seems that this script throws some errors when Objc_msgsend calls a nil object (that is, RDI is arg0 is 0x0).
You can use the following command to view this error:

sudo./msgsendsnoop.d-p pgrep VCTransitions | grep invalid
Let's fix that bug with a simple judgment sentence.
Add a judgment sentence after Pid$target::objc_msgsend:entry:

pid$target::objc_msgsend:entry/arg0 > 0x100000000/
This sentence means "If the first parameter is nil or if the memory is not being exploited, do not run this dtrace action".
Typically, this section of memory is not allowed to read and write in MacOS user processes. If the number there is less than 0x100000000, DTrace will not use the data in that memory. Therefore, if it is less than that number, DTrace skips it. Of course you can also confirm with the following line of code in LLDB:

(lldb) Image dump sections vctransitions
You can confirm it in your spare time. You still need to end this script.

Remove interference

To be honest, I don't care about the compiler-generated memory management code. That means we have to exclude retain or release related code.
Create a new DTrace probe with a new clause on your current probe:

Pid$target::objc_msgsend:entry
{
This->selector = Copyinstr (arg1);
}
/Old code below /
pid$target::objc_msgsend:entry/arg0 > 0x100000000/
Now you declare a selector in the new clause before the master-slave clause skips all memory logic. This will allow you to filter the Objective-c method in the sentence section of the master sentence.
Speaking of which, the parameters of judging sentences in the principal and subordinate sentences are:

Pid$target::objc_msgsend:entry/arg0 > 0x100000000/&&
This->selector! = "Retain" &&
This->selector! = "Release"/
now ignores all code that is equal to retain or release. Now you don't need to reassign the this->selector in the master and slave sentences, you have already done the other clause. Although he does not have a bad influence, it is still superfluous logic. Remove it, or you can not remove it if you are happy.
your two clauses should now look like this:

Pid$target::objc_msgsend:entry
{
This->selector = Copyinstr (arg1);
}
pid$target::objc_msgsend:entry/arg0 > 0x100000000/&&
This->selector! = "Retain" &&
This->selector! = "Release"/
size = sizeof (uintptr_t);
{
This->isa = ((uintptr_t ) copyin (arg0, size));
}
This->rax = ((uintptr_t ) copyin ((This->isa + 0x20), size);
This->rax = (This->rax & 0X7FFFFFFFFFF8);
THIS->RBX = ((uintptr_t ) copyin ((This->rax + 0x38), size);
This->rax = ((uintptr_t ) copyin ((This->rax + 0x8), size);
This->rax = ((uintptr_t ) copyin ((This->rax + 0x18), size);
This->classname = COPYINSTR (this->rbx! = 0?)
This->rbx:this->rax);
printf ("0x%016p +|-[%s%s]\n", arg0, This->classname,
This->selector);
Restart the script:

sudo./msgsendsnoop.d-ppgrep VCTransitions
Picture. png

Oh, that's great, it's better this time!
But there are still a lot of distractions here. It's time to combine this script with lldb to get some output from the main execution file.

Limit range with Lldb

In the Starter folder there is a Lldb Python script that creates a DTrace script and then runs the script with the logic you just implemented.
You can only use this script in the first place. But it's not so much fun.
The name of this file is called snoopie.py. Copy this file to your ~/lldb directory. If you have seen the 22nd chapter "SB Examples, improved Lookup", then there should be a ~/lldb file in the lldbinit.py directory. It will automatically load all the scripts in this directory for you.
If you skip that chapter, then you need to use the following line of code in your ~/.lldbinit file:

Command Script Import ~/lldb/snoopie.py
You will filter out the code using a creative solution that uses only the values in this DTrace script to track only the objective-c/dynamic Swift code that is part of the vctransitions executable. Typically, when prying code is in the framework , I often crawl the modules loaded into the text part of the memory and then compare the instruction pointer boundaries before and after the text (this part of the memory is responsible for executing the code). If the instruction pointer is between the upper and lower bounds then you can assume that the code you want to trace with DTrace is it.
Unfortunately, after objc_msgsend, this blocking point is applied to the OBJECTIVE-C code in all modules. This means that you can rely on the instruction pointer to tell yourself which module you are in.
Otherwise, you will need to get the module you are currently in by using a separate address that contains only one of the classes in the __data section of the main execution file.
Back to your previous Vctransitions Xcodex project.
Build and run, stop execution, and then go into lldb. Then enter the following:

(LLDB) p/x (void *) nsclassfromstring (@ "Objcviewcontroller")
You will get the address of the Objcviewcontroller class:

(void *) $ = 0x000000010db34080
Use this address to check the location of these things in memory:

(lldb) Image Lookup-a 0x000000010db34080
You will get some output similar to the following:

ADDRESS:VCTRANSITIONS[0X0000000100012080]
(Vctransitions. DATA. Objc_data + 40)
Summary: (void *) 0x000000010db34058
Therefore, you can infer that this class is in the Objc_data sub-section of the Vctransitions data section .
You will use the Lldb Python module to find the upper and lower bounds of this __data segment.
Now you'll use the old script command to find out how you can create this code from LLDB.
Back in Lldb, enter the following:

(lldb) Script path = Lldb.target.executable.fullpath
This gives you a Sbfilespec representing the executable file vctransitions, and copies the sbfilespec to the variable path.
Print out this path to confirm that it is valid:

(lldb) Script path
You will get the full path to the executable file. You can use this path to get the correct sbmodule from the Sbtarget. Enter the following in LLDB:

(lldb) script print Lldb.target.module[path]
You will get a sbmodule representing the main executable file.
There is a sbsections in the sbmodule. You can use the sections property to get all sections in Sbmodule, or you can use Section[index] to get a specified section. Yes, that attribute follows the Python getitem format. Enter the following in the LLDB:

(lldb) script print lldb.target.module[path].section[0]
You will get something similar to the following:

[0x0000000000000000-0x0000000100000000] vctransitions. Pagezero
This
GetItem implementation can also use Sbsection as a directory. So you can also access pagezerosection like this :

(lldb) script print lldb.target.module[path].section['Pagezero ']
This means that you can easily access the
DATA sbsection like this:

(lldb) script print lldb.target.module[path].section[' __data ']
Cool, that's doable. Copy this sbsection to a variable called section, like this:

(lldb) Script section = lldb.target.module[path].section[' __data ']
Now you have a reference to the correct section. Here are some of the subsections you can subdivide, but you might want to crawl the full section because they are a contiguous area in memory.
To get the load address from the section, consider this:

(lldb) Script section. Getloadaddress (Lldb.target)
This will print out the starting position. Grab the size of your location at the same time:

(lldb) Script Section.size
Picture. png

So what information does this give you? You can create a DTrace judgment sentence to check if the class is in the middle of these values in memory. If so, perform this dtrace action. If they are not there, ignore.
Let's make it come true!

Repairing Snoopie Scripts

As it shows, this snoopie script can be run, so you just need to add some logical judgments and filter out the instances.
Open the ~/lldb/snoopie.py file, and then locate the Generatedtracescript function.
Remove Datasectionfilter = ... This line, and then add the following code:

target = debugger. Getselectedtarget ()
Path = Target.executable.fullpath
Section = target.module[path].section[' __data ')
start_address = section. Getloadaddress (target)
end_address = start_address + section.size
Datasectionfilter = ' {} <= ((uintptr_t ) copyin (arg0,
sizeof (uintptr_t)) &&
((uintptr_t ) copyin (arg0, sizeof (uintptr_t))) <= {}
". Format (start_address, end_address)
The interesting point is that you take the arg0 parameter and if (and only if) arg0 is larger than 0x100000000 to dereference this parameter, which indicates that there is a valid instance in memory. That's it! No more code needed! You're done!
Save what you've done, jump to the LLDB console, and either use your custom Reload_script command in LLDB or manually enter script import ~/.lldbinit to reload the content.
After the load is successful, in Lldb, try running:

(LLDB) Snoopie
Paste the content into the terminal window and run it.
Now DTrace will only parse the code in your (streamlined) main executable file.

Picture. png

Enjoy the script's performance on other apps on your computer!

Why are we learning this?

I'll leave you some homework at the end. This script does not fit well with the OBJECTIVE-C classification. For example, there might be a class in the main file that is implemented in different modules using the OBJECTIVE-C classification. You need some creative way to check if the objc_msgsend of OBJECTIVE-C selector is implemented in the main executable file.
In addition, printf in your current code cannot indicate that arg0 is a class method or not a class method. You need to go into memory and figure out how to check if the arg0 parameter is a class or just an instance.
How can you achieve the above content?
? If Arg0 is an instance of a class, the ISA pointer points to the Non-meta class.
? If ARG0 is a class, then the ISA pointer will point to the Meta class.
? Check out the assembly of Class_ismetaclass to figure out which of the values in a class indicates that it is a meta class or not a meta class.
Once you jump into memory and find a way to decide if a class is a meta class, copy the Class_ismetaclass logic in your DTrace script. Because this can be an instance of a class or the class object itself, you can use a ternary operator similar to the following in the DTrace script:

This->ismeta = ...//logic here
This->ismetachar = This->ismeta? ' + ': '-'
printf ("0x%016p%c[%s%s]\n", arg0, This->ismetachar,
This->classname,
This->selector);
The amount ... ismetachar. One day in the future it will become a Pokemon (Pokémon) name.
Good luck!

Advanced+apple+debugging (17)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.