{"id":150,"date":"2016-10-13T18:29:03","date_gmt":"2016-10-13T18:29:03","guid":{"rendered":"https:\/\/www.spaceflint.com\/?p=150"},"modified":"2025-04-21T05:58:15","modified_gmt":"2025-04-21T05:58:15","slug":"using-ptrace-on-os-x","status":"publish","type":"post","link":"https:\/\/www.spaceflint.com\/?p=150","title":{"rendered":"Using ptrace on OS X"},"content":{"rendered":"<p>Documentation on the subject of using <strong>ptrace<\/strong> in OS X seems somewhat lacking.  This text attempts to summarize the steps necessary to trace another process.  In OS X, the <strong>ptrace<\/strong> service itself offers little more than attaching to and detaching from the target process.  Other system calls are necessary to accomplish additional jobs, such as accessing target memory and registers.<\/p>\n<h2>task_for_pid<\/h2>\n<p>The first of these system calls is <strong>task_for_pid<\/strong>, which gets a &#8220;task port&#8221; for the target process.  (Some parts of the OS X kernel use the term &#8220;task&#8221; for a process.)  This port (or handle) is used in other system calls to query or manipulate the target process.<\/p>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\n#include &lt;mach\/mach.h&gt;\r\n\r\ntask_for_pid(mach_task_self(),\r\n             target_pid,\r\n             &amp;target_task_port);\r\n\r\n<\/pre>\n<p>Note that the first argument specifies which process receives the new task port, with <strong>mach_task_self()<\/strong> indicating the current process, and the third argument points to the variable which actually receives the port.<\/p>\n<p>To release the port, at the end of the ptrace session:<\/p>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\nmach_port_deallocate(mach_task_self(),\r\n                     target_task_port);\r\n\r\n<\/pre>\n<p>The <strong>task_for_pid<\/strong> system call is protected by the gatekeeper security framework, and will fail (with error code 5, <strong>KERN_FAILURE<\/strong>) unless the calling process is digitally signed, and its <strong>Info.plist<\/strong> includes the <strong>SecTaskAccess<\/strong> key.<\/p>\n<p>An example of a minimal <strong>Info.plist<\/strong>:<\/p>\n<pre class=\"brush: xml; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\r\n&lt;!DOCTYPE plist PUBLIC &quot;-\/\/Apple\/\/DTD PLIST 1.0\/\/EN&quot; &quot;http:\/\/www.apple.com\/DTDs\/PropertyList-1.0.dtd&quot;&gt;\r\n&lt;plist version=&quot;1.0&quot;&gt;\r\n&lt;dict&gt;\r\n    &lt;key&gt;SecTaskAccess&lt;\/key&gt;\r\n    &lt;array&gt;\r\n        &lt;string&gt;allowed&lt;\/string&gt;\r\n        &lt;string&gt;debug&lt;\/string&gt;\r\n    &lt;\/array&gt;\r\n&lt;\/dict&gt;\r\n&lt;\/plist&gt;\r\n\r\n<\/pre>\n<p>If desired, this <strong>Info.plist<\/strong> can be embedded into the executable by specifying the sectcreate option in the link step:<\/p>\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\ngcc -sectcreate __TEXT __info_plist Info.plist -o program object1 object2...\r\n\r\n<\/pre>\n<p>Finally, digitally signing the executable is accomplished with <strong>codesign(1)<\/strong> utility.  This text will not go into the details of using this utility, or generating a self-signed certificate.  For more information, please see <a href=\"http:\/\/os-tres.net\/blog\/2010\/02\/17\/mac-os-x-and-task-for-pid-mach-call\/\">Mac OS X and Task_for_pid() Mach Call<\/a>.<\/p>\n<h2>Mach exceptions<\/h2>\n<p>As part of controlling a process using <strong>ptrace<\/strong>, the controlling process expects to be notified about signals in the target process.  Using <strong>ptrace<\/strong> with the <strong>PT_ATTACH<\/strong> request, these notifications are delivered via <strong>SIGCHLD<\/strong> signals sent to the controlling process.<\/p>\n<p>However, OS X version 10.2 introduced the <strong>PT_ATTACHEXC<\/strong> form of <strong>ptrace<\/strong>, which delivers mach exceptions (as opposed to just UNIX signals) via mach messages.  And in recent versions of OS X, the old <strong>PT_ATTACH<\/strong> form is marked deprecated.  This text will therefore focus on the newer <strong>PT_ATTACHEXC<\/strong> form.<\/p>\n<p>Mach exception are enumerated in <strong>\/usr\/include\/mach\/exception_types.h<\/strong> and deal with various faults and traps.  For example, <strong>EXC_BAD_ACCESS<\/strong> is a memory access fault, and <strong>EXC_BREAKPOINT<\/strong> is a software breakpoint trap.  One of the exceptions, <strong>EXC_SOFTWARE<\/strong> (with code <strong>EXC_SOFT_SIGNAL<\/strong>) is used to represent a UNIX signal as a mach exception.<\/p>\n<h2>Requesting mach exceptions<\/h2>\n<p>To receive mach exceptions, the controlling process must register an &#8220;exception port&#8221; with the target process, to request notifications on a set of exception types. The controlling process should then listen on the port to receive notifications about exceptions (including UNIX signals) that occur in the target process.<\/p>\n<p>The set of exception types is defined in <strong>exception_types.h<\/strong> as <strong>EXC_MASK_ALL<\/strong>, but note that previous versions of OS X may not support all exception types, and will fail requests that specify exception types which are not recognized.  Note also that <a href=\"http:\/\/llvm.org\/viewvc\/llvm-project?view=revision&#038;revision=173390\">LLDB ignores EXC_RESOURCE<\/a> so it may be advisable to do the same.<\/p>\n<p>It is useful to save the set of exception ports already registered for the target process, before replacing them.  This makes it easier to detach the process, as will be discussed in a later section of this document.<\/p>\n<p>Example Code:<\/p>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\n\/* save the set of exception ports registered in the process *\/\r\n\r\nexception_mask_t       saved_masks&#x5B;EXC_TYPES_COUNT];\r\nmach_port_t            saved_ports&#x5B;EXC_TYPES_COUNT];\r\nexception_behavior_t   saved_behaviors&#x5B;EXC_TYPES_COUNT];\r\nthread_state_flavor_t  saved_flavors&#x5B;EXC_TYPES_COUNT];\r\nmach_msg_type_number_t saved_exception_types_count;\r\n\r\ntask_get_exception_ports(target_task_port,\r\n                        EXC_MASK_ALL,\r\n                        saved_masks,\r\n                        &amp;saved_exception_types_count,\r\n                        saved_ports,\r\n                        saved_behaviors\r\n                        saved_flavors);\r\n\r\n\/* allocate and authorize a new port *\/\r\n\r\nmach_port_allocate(mach_task_self(),\r\n                   MACH_PORT_RIGHT_RECEIVE,\r\n                   &amp;target_exception_port);\r\n\r\nmach_port_insert_right(mach_task_self(),\r\n                       target_exception_port,\r\n\/* and again *\/        target_exception_port,\r\n                       MACH_MSG_TYPE_MAKE_SEND);\r\n\r\n\/* register the exception port with the target process *\/\r\n\r\ntask_set_exception_ports(target_task_port,\r\n                         EXC_MASK_ALL,\r\n                         target_exception_port,\r\n                         EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,\r\n                         THREAD_STATE_NONE);\r\n\r\n<\/pre>\n<h2>Attaching to the target process<\/h2>\n<p>With an exception port registered on the target process, it is finally possible to attach to the process with <strong>ptrace<\/strong>.<\/p>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\n\/* attach to the target process *\/\r\n\r\nptrace(PT_ATTACHEXC, target_pid, 0, 0);\r\n\r\n<\/pre>\n<p>As part of the attach sequence, the target process will be signalled with <strong>SIGSTOP<\/strong>, and this signal will be delivered to the controlling process as a mach &#8220;soft signal&#8221; exception, that is, exception type <strong>EXC_SOFTWARE<\/strong> with code <strong>EXC_SOFT_SIGNAL<\/strong>, and sub-code <strong>SIGSTOP<\/strong>.<\/p>\n<h2>Receiving mach exceptions<\/h2>\n<p>When an exception occurs in some thread in the target process, the kernel pushes an exception message into a queue associated with the exception port.  The thread then blocks (in the kernel) while waiting for a reply to the exception message.  This wait is indefinite; it does not include a timeout.<\/p>\n<p>If the controlling process is not listening on the exception port at the time when an exception is sent, the exception message will remain in the exception port queue, and the thread which sent the exception will remain blocking while waiting for a reply.<\/p>\n<p>While a thread is waiting for a reply in this way, other threads in the process continue to execute, and can generate exceptions, which are handled in the same way as described above.<\/p>\n<p>This queueing mechanism allows the controlling process to have a main loop in which it waits for an exception message, processes the exception, sends a reply message, and then restarts the loop.  Processing the exception may take any length of time.<\/p>\n<p>If the exception is a UNIX signal, the UNIX status of the process is set to stopped, before the thread sends the message.  This does not prevent other threads in the process from continuing to execute.  However, delivery of additional UNIX signal exceptions will be suspended while the UNIX status of the process remains stopped.<\/p>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\n\/* wait indefinitely to receive an exception message *\/\r\n\r\nwait_for_exception:\r\n\r\nchar req&#x5B;128], rpl&#x5B;128];\t\t\t\/* request and reply buffers *\/\r\n\r\nmach_msg((mach_msg_header_t *)req,\t\/* receive buffer *\/\r\n\t\t MACH_RCV_MSG,\t\t\t\t\/* receive message *\/\r\n\t\t 0, \t\t\t\t\t\t\/* size of send buffer *\/\r\n\t\t sizeof(req),\t\t\t\t\/* size of receive buffer *\/\r\n\t\t target_exception_port,\t\t\/* port to receive on *\/\r\n\t\t MACH_MSG_TIMEOUT_NONE,\t\t\/* wait indefinitely *\/\r\n\t\t MACH_PORT_NULL);\t\t\t\/* notify port, unused *\/\r\n\r\n\/* suspend all threads in the process after an exception was received *\/\r\n\r\ntask_suspend(target_task_port);\r\n\r\n<\/pre>\n<h2>Parsing mach exceptions<\/h2>\n<p>Functions for handling the exception message are defined in <strong>\/usr\/include\/mach\/mach_exc.defs<\/strong> and generated using the <strong>mig(1)<\/strong> program.  The kernel-side calls the generated <strong>mach_exception_raise<\/strong> function when sending the exception.<\/p>\n<p>The controlling process, listening on the exception port, is the receiving end, and receives the exception as a message buffer.  It then calls <strong>mach_exc_server<\/strong> to extract the data from the message buffer and convert these to function arguments, which are used in the call to the <strong>catch_mach_exception_raise<\/strong> function.<\/p>\n<p>The <strong>catch_mach_exception_raise<\/strong> function is provided by the program code of the controlling process.  In other words, the stub generated by <strong>mig(1)<\/strong> calls catch_mach_exception_raise.<\/p>\n<p>Note that <strong>mach_exc.defs<\/strong> defines <strong>exception_raise<\/strong>, <strong>exception_raise_state<\/strong>, and <strong>exception_raise_state_identity<\/strong>.  A flag passed to <strong>task_set_exception_ports<\/strong> (<strong>EXCEPTION_DEFAULT<\/strong> in the code fragment above) selects which exception function is used.<\/p>\n<p>To generate the <strong>mach_exc_server<\/strong> function for the receiving end, <strong>mig(1)<\/strong> is invoked:<\/p>\n<pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\nmig \/usr\/include\/mach\/mach_exc.defs\r\n\r\n<\/pre>\n<p>This generates <strong>mach_excUser.c<\/strong>, <strong>mach_excServer.c<\/strong>, and <strong>mach_exc.h<\/strong>.  The second file, <strong>mach_excServer.c<\/strong>, contains the <strong>mach_exc_server<\/strong> function.  This file should be compiled into the executable of the controlling process.<\/p>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\nif (kret_from_mach_msg == KERN_SUCCESS) {\r\n\r\n\t\/* mach_exc_server calls catch_mach_exception_raise *\/\r\n\t\r\n\tboolean_t message_parsed_correctly =\r\n\t\r\n\t\t\t\tmach_exc_server((mach_msg_header_t *)req,\r\n\t\t\t\t\t\t\t\t(mach_msg_header_t *)rpl);\r\n\t\r\n\tif (! message_parsed_correctly) {\r\n\t\r\n\t\tkret_from_catch_mach_exception_raise =\r\n\t\t\t\t((mig_reply_error_t *)rpl)-&gt;RetCode;\r\n\t}\r\n}\r\n\r\n\/* resume all threads in the process before replying to the exception *\/\r\n\r\ntask_resume(target_task_port);\r\n\r\n\/* reply to the exception *\/\r\n\r\nmach_msg_size_t send_sz = ((mach_msg_header_t *)rpl)-&gt;msgh_size;\r\n\r\nmach_msg((mach_msg_header_t *)rpl,\t\/* send buffer *\/\r\n\t\t MACH_SEND_MSG,\t\t\t\t\/* send message *\/\r\n\t\t send_sz,\t\t\t\t\t\/* size of send buffer *\/\r\n\t\t 0,\t\t\t\t\t\t\t\/* size of receive buffer *\/\r\n\t\t MACH_PORT_NULL,\t\t\t\/* port to receive on *\/\r\n\t\t MACH_MSG_TIMEOUT_NONE,\t\t\/* wait indefinitely *\/\r\n\t\t MACH_PORT_NULL);\t\t\t\/* notify port, unused *\/\r\n\r\n\/* target process is now running.  wait for a new exception *\/\r\n\r\ngoto wait_for_exception;\r\n\r\n<\/pre>\n<p>The last remaining piece is implementing <strong>catch_mach_exception_raise<\/strong>,<br \/>\nwhich is called by the <strong>mach_exc_server<\/strong> function after it has parsed the incoming exception message and extracted the message arguments.<\/p>\n<p>As discussed in a preceding section, if the exception is a UNIX soft signal exception, an additional step is required.  Before sending the soft signal exception message to the controlling process, the kernel sets the process status to stopped (<strong>p_stat = SSTOP<\/strong>).<\/p>\n<p>Similarly, before replying to the message, the controlling process must tell the kernel to reset the process status (<strong>p_stat = SRUN<\/strong>).  This is accomplished with a call to <strong>ptrace<\/strong> with the <strong>PT_THUPDATE<\/strong> request, which also modifies the signal number.<\/p>\n<p>By changing the signal number, the controlling process can hide the signal from the target process, by modifying the signal number to zero.  For example, for the case of handling <strong>SIGSTOP<\/strong>.  Or the controlling process can leave the signal to be handled by the target process, or request that some other signal be handled instead.<\/p>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\n\/* catch_mach_exception_raise *\/\r\n\r\nkern_return_t catch_mach_exception_raise(\r\n    mach_port_t exception_port, \r\n    mach_port_t thread_port,\r\n    mach_port_t task_port,\r\n    exception_type_t exception_type,\r\n    mach_exception_data_t codes,\r\n    mach_msg_type_number_t num_codes)\r\n{\r\n\t\/* exception_type is defined in exception_types.h \t\t\t\t\t\t*\/\r\n\r\n\t\/* an exception may include a code and a sub-code.  num_codes specifies *\/\r\n\t\/* the number of enties in the codes argument, between zero and two.    *\/\r\n\t\/* codes&#x5B;0] is the code, codes&#x5B;1] is the sub-code.                      *\/\r\n\r\n\tif (exception_type == EXC_SOFTWARE &amp;&amp; code&#x5B;0] == EXC_SOFT_SIGNAL) {\r\n\t\r\n\t\t\/* handling UNIX soft signal: \t\t\t\t\t\t\t     \t\t*\/\r\n\t\t\/* this example clears SIGSTOP before resuming the process.  \t\t*\/\r\n\t\r\n\t\tif (codes&#x5B;2] == SIGSTOP)\r\n\t\t\tcodes&#x5B;2] = 0;\r\n\r\n\t\tptrace(PT_THUPDATE,\r\n\t\t\t   target_pid,\r\n\t\t\t   (caddr_t)(uintptr_t)thread_port,\r\n\t\t\t   codes&#x5B;2]);\r\n\t}\r\n\t\r\n\treturn KERN_SUCCESS;\r\n}\r\n\r\n\/* catch_mach_exception_raise_state *\/\r\n\r\nkern_return_t catch_mach_exception_raise_state(\r\n    mach_port_t exception_port, \r\n    exception_type_t exception,\r\n    const mach_exception_data_t code, \r\n    mach_msg_type_number_t codeCnt,\r\n    int *flavor, \r\n    const thread_state_t old_state,\r\n    mach_msg_type_number_t old_stateCnt, \r\n    thread_state_t new_state,\r\n    mach_msg_type_number_t *new_stateCnt)\r\n{\r\n\t\/* not used because EXCEPTION_STATE is not specified in the call *\/\r\n\t\/* to task_set_exception_ports, but referenced by mach_exc_server *\/\r\n\t\r\n    return MACH_RCV_INVALID_TYPE;\r\n}\r\n\r\n\/* catch_mach_exception_raise_state_identity *\/\r\n\r\nkern_return_t catch_mach_exception_raise_state_identity(\r\n    mach_port_t exception_port, \r\n    mach_port_t thread, \r\n    mach_port_t task,\r\n    exception_type_t exception, \r\n    mach_exception_data_t code,\r\n    mach_msg_type_number_t codeCnt, \r\n    int *flavor, \r\n    thread_state_t old_state,\r\n    mach_msg_type_number_t old_stateCnt, \r\n    thread_state_t new_state,\r\n    mach_msg_type_number_t *new_stateCnt)\r\n{\r\n\t\/* not used because EXCEPTION_STATE_IDENTITY is not specified in the   *\/\r\n\t\/* call to task_set_exception_ports, but referenced by mach_exc_server *\/\r\n\t\r\n    return MACH_RCV_INVALID_TYPE;\r\n}\r\n\r\n<\/pre>\n<h2>Detaching target process<\/h2>\n<p>As discussed in a preceding section, an exception in the target process causes the thread that triggered the exception to block until the controlling process send a reply message.  Detaching from the target process does not release such threads, so all pending exceptions should be handled before detaching.<\/p>\n<p>The saved exception port information should be restored, to prevent new exceptions being sent to the controlling process.  However, it is possible the exception port queue contains additional exceptions that have not yet been handled, and must be handled by replying to them.<\/p>\n<p>Detaching from the process can only be requested if the process has a stopped status, as discussed above in the context of UNIX signals.  At that point <strong>ptrace<\/strong> can be called with request <strong>PT_DETACH<\/strong> (or <strong>PT_KILL<\/strong>) to detach (or kill) the target process.<\/p>\n<p>Note that <strong>PT_THUPDATE<\/strong> resets the stopped status, so it conflicts with <strong>PT_DETACH<\/strong> and <strong>PT_KILL<\/strong>.  If the controlling process wishes to use the stopped status (as a result of an exception) to detach or kill the target, then it should not use <strong>PT_THUPDATE<\/strong> in the context of handling that same exception.<\/p>\n<p>The target process may not have a stopped status at the time detaching is requested:  The process may be running, or it may be stopped due to an exception that is not a UNIX soft signal exception.  In either case, it would be necessary for the controlling process to signal the target process with <strong>SIGSTOP<\/strong>, which would generate a UNIX soft signal exception.  Then the controlling process continues to handle exceptions, while waiting to receive a UNIX soft signal exception.  When it is received, the target process can be detached.<\/p>\n<p>Alternatively, if the target process is to be killed rather than detached, it is possible to handle all pending exceptions, and then send a <strong>SIGKILL<\/strong> to the target process, without taking into account whether the target process has a stopped status or not.<\/p>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n\r\n\/* restore saved exception ports *\/\r\n\r\nfor (uint32_t i = 0; i &lt; saved_exception_types_count; ++i) {\r\n\r\n\ttask_set_exception_ports(target_task_port,\r\n\t\t\t\t\t\t\t saved_masks&#x5B;i],\r\n\t\t\t\t\t\t\t saved_ports&#x5B;i],\r\n\t\t\t\t\t\t\t saved_behaviors&#x5B;i],\r\n\t\t\t\t\t\t\t saved_flavors&#x5B;i]);\r\n}\r\n\r\n\/* issue PT_DETACH if there is a pending exception *\/\r\n\r\nif (last_exception_type was a UNIX soft signal) {\r\n\r\n\tif (reply not yet sent for last exception) {\r\n\t\r\n\t\tptrace(PT_DETACH, target_pid, 0, 0);\r\n\t}\r\n}\r\n\r\n\/* process all pending exceptions *\/\r\n\r\nkern_return_t kret_recv = 0;\r\n\r\nfor (kret_recv == 0) {\r\n\r\n\t\/* reply to last exception *\/\r\n\r\n\tmach_msg((mach_msg_header_t *)rpl,\t\/* send buffer *\/\r\n\t\t\t MACH_SEND_MSG,\t\t\t\t\/* send message *\/\r\n\t\t\t send_sz,\t\t\t\t\t\/* size of send buffer *\/\r\n\t\t\t 0,\t\t\t\t\t\t\t\/* size of receive buffer *\/\r\n\t\t\t MACH_PORT_NULL,\t\t\t\/* port to receive on *\/\r\n\t\t\t MACH_MSG_TIMEOUT_NONE,\t\t\/* wait indefinitely *\/\r\n\t\t\t MACH_PORT_NULL);\t\t\t\/* notify port, unused *\/\r\n\t\r\n\t\/* get next exception, with timeout option.   when the result   *\/\r\n\t\/* is timeout, it means the exception port queue was exhausted. *\/\r\n\t\/* no new messages will be queued, because the exception port   *\/\r\n\t\/* is no longer associated with the target process.\t\t\t\t*\/\r\n\r\n\tkret_recv =\r\n\tmach_msg((mach_msg_header_t *)req,\t\t\/* receive buffer *\/\r\n\t\t MACH_RCV_MSG | MACH_RCV_TIMEOUT,\t\/* receive message *\/\r\n\t\t 0, \t\t\t\t\t\t\t\t\/* size of send buffer *\/\r\n\t\t sizeof(req),\t\t\t\t\t\t\/* size of receive buffer *\/\r\n\t\t target_exception_port,\t\t\t\t\/* port to receive on *\/\r\n\t\t 1,\t\t\t\t\t\t\t\t\t\/* wait one millisecond *\/\r\n\t\t MACH_PORT_NULL);\t\t\t\t\t\/* notify port, unused *\/\r\n}\r\n\r\n\/* release exception port *\/\r\n\r\nmach_port_deallocate(mach_task_self(), target_exception_port);\r\n\r\n\/* if detach was not possible, kill the target process *\/\r\n\r\nif (! ptraced_detached_called) {\r\n\r\n\tkill(target_pid, SIGKILL);\r\n}\r\n\r\n<\/pre>\n<h2>Conclusion<\/h2>\n<p>This concludes the basic overview of dealing with exception ports in OS X.  Please note that some aspects in the example code were simplified for sake of brevity and clarity.  As an example, a real debugger would let the user decide how handle an exception, and therefore would not issue <strong>ptrace(PT_THUPDATE)<\/strong> in <strong>catch_mach_exception_raise<\/strong> immediately after receiving the exception.<\/p>\n<p>As mentioned in the opening paragraph, <strong>ptrace<\/strong> on OS X is limited.  The reader may wish to look further into <strong>thread_get_state<\/strong> and <strong>thread_set_state<\/strong> for access to the register state in the target process, and the <strong>mach_vm_*<\/strong> functions for access to the virtual memory space of the target process.<\/p>\n<p>References<\/p>\n<p><a href=\"https:\/\/github.com\/opensource-apple\/xnu\">https:\/\/github.com\/opensource-apple\/xnu<\/a><\/p>\n<p><a href=\"http:\/\/web.archive.org\/web\/20090627062246\/http:\/\/www.matasano.com\/log\/1100\/what-ive-been-doing-on-my-summer-vacation-or-it-has-to-work-otherwise-gdb-wouldnt\/\n\">http:\/\/web.archive.org\/web\/20090627062246\/http:\/\/www.matasano.com\/log\/1100\/what-ive-been-doing-on-my-summer-vacation-or-it-has-to-work-otherwise-gdb-wouldnt\/<\/a><\/p>\n<p><a href=\"http:\/\/os-tres.net\/blog\/2010\/02\/17\/mac-os-x-and-task-for-pid-mach-call\/\">http:\/\/os-tres.net\/blog\/2010\/02\/17\/mac-os-x-and-task-for-pid-mach-call\/<\/a><\/p>\n<p><a href=\"http:\/\/uninformed.org\/index.cgi?v=4&#038;a=3&#038;p=14\">http:\/\/uninformed.org\/index.cgi?v=4&#038;a=3&#038;p=14<\/a><\/p>\n<p><a href=\"http:\/\/unixjunkie.blogspot.co.il\/2006\/01\/darwin-ptrace-and-registers.html\">http:\/\/unixjunkie.blogspot.co.il\/2006\/01\/darwin-ptrace-and-registers.html<\/a><\/p>\n<p><a href=\"http:\/\/stackoverflow.com\/questions\/2824105\/handling-mach-exceptions-in-64bit-os-x-application\">http:\/\/stackoverflow.com\/questions\/2824105\/handling-mach-exceptions-in-64bit-os-x-application<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Documentation on the subject of using ptrace in OS X seems somewhat lacking. This text attempts to summarize the steps necessary to trace another process. In OS X, the ptrace service itself offers little more than attaching to and detaching from the target process. Other system calls are necessary to accomplish additional jobs, such as &hellip; <a href=\"https:\/\/www.spaceflint.com\/?p=150\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Using ptrace on OS X<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-150","post","type-post","status-publish","format-standard","hentry","category-dev-tools"],"_links":{"self":[{"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/posts\/150","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=150"}],"version-history":[{"count":7,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/posts\/150\/revisions"}],"predecessor-version":[{"id":158,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/posts\/150\/revisions\/158"}],"wp:attachment":[{"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=150"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=150"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=150"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}