Re: Breakpoint Implementation
Re: Breakpoint Implementation
- Subject: Re: Breakpoint Implementation
- From: Terry Lambert <email@hidden>
- Date: Thu, 7 Oct 2010 23:42:39 -0700
On Oct 7, 2010, at 10:26 PM, Derek Darwin Lists wrote:
> thread_suspend() does not return until the target thread has been suspended, i.e. the target thread is in a state where it longer executes instructions in user space until it is resumed. See
> http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_suspend.html
The point is that the thread continues executing in kernel space until it reaches the system return code. If the system trap it was executing within was for the creation of another thread, the newly created thread will not be suspended by the thread_suspend() on the creating thread. In order for this race to be avoided, it's necessary to perform a task_suspend() rather than a thread suspend. This sets the task suspend count and the thread suspend count for the extant threads at the time of the suspend under the task_lock() and thread_lock(), and newly created threads will get an inherited suspend count from the task.
This is why I specifically stated:
>> Typically, this is done after a task_suspend(), which iterates over the threads in the task and sets suspend counts on all of them. Eventually they drain out of the scheduler run queues to an actually suspended state.
[ ... ]
>> This is somewhat problematic:
>>
>> (1) Security features will often prevent a page from being both writeable and executable, and may prevent a page from moving to executable if it has once been writable. The only exception to this would be operations in protected mode in the kernel to change these mappings (e.g. this is how dtrace emulates instructions when it replaces function preambles for FBT - Function Boundary Tracing). You can see this sort of thing in AntiVirus software or in copy protection, etc..
>>
>
> Given that the poster is referring to Apple intel systems, he can use mprotect() on the heap to confer execute protection and turn off read permission (to account for any future W^X enforcement; 10.6 Intel does not enforce W^X see below). dtrace and antivirus are not relevant.
> void *range = mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0);
> void (*funcptr) (void) = NULL;
> perror("mmap");
> memset(range, 0xCC, 4096);
> funcptr = range;
> (funcptr)();
I wanted to be crystal clear, since this list is archived, and the information posted here could be used as a reference by someone who didn't want to read the source code, and couldn't obtain the information anywhere else. Not everyone will read back in the thread to obtain the context that this is a hobby project, or that this is Intel-specific and limited current Apple manufactured Intel systems. This is not a good assumption for Darwin in general.
> (2) Your code signature for the modified page will no longer be valid. If it gets checked after you modify it, depending on your signing flags, your program may be immediately killed.
>
> The poster is proposing to generate code on the heap on an Apple Intel system (where there is no concept of "code signatures" for the heap), and repoint a thread's PC to the generated trampoline, not modify pages backed by code signatures on disk.
Several companies on this list have implemented "code signing" KEXTs in the past, utilizing kauth callbacks as verification point, as part of security products.
It's conceivable and reasonable that one might generate a code signature on the fly for any page that gets marked executable; for example, for use in a JIT, and that that signature would (and should) be checked if the page is paged out to swap and back in. One could also reasonably consider checking the pages either periodically at random, to eventually catch the case of modification while in core in the absence of swapping, and/or at the time the mapping privilege is explicitly changed, prior to allowing the VM system to mark the page executable in the first place.
[ ... ]
> dtrace will refuse to instrument locations already populated by INT3s, see fasttrap_tracepoint_init() and
> http://blogs.sun.com/ahl/entry/dtrace_is_open
> Presumably Dave Keck's debugger can also atomically check and decline to break on pre-existing INT3s, or break on the prior instruction and instruction single step etc.
Careful... without a specific API being published by Apple for this sort of code modification operation, there's no guarantee here.
The assumption that a multibyte instruction will always be replaced using the same technique and ordering of operations in the code, e.g. replacing a 2 byte instruction with NOP,INT3 rather than INT3,NOP, is not warranted. Without knowing that the location previously contained a multibyte instruction, and how many bytes were actually consumed by the instruction, the previous contents are not available to the instrumenting program to allow it to make correct assumptions in any case. You could also consider only replacing the first byte of the instruction rather than NOP'ing the remainder. If so, subsequent instrumentation by another program could fall within the same original instruction, unknowingly.
Ideally, you'd want an API, a defined ordering, and a means of sharing instrumentation points among an arbitrary number of observing programs.
-- Terry _______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden