Mac OSX 下利用GDB调试itunes
原文:http://steike.com/code/debugging-itunes-with-gdb/
调试itunes时,设置端点到<dyld_stub_ptrace>,然后run。触发端点后return,然后con。完事大吉!!!
附原文:
Debugging iTunes with GDB
It's occasionally useful to run iTunes under GDB without having it bomb out with a 'code 055' or having gdb segfault.
Why can't you just use GDB?
There's no real reason why you shouldn't be able to connect GDB to iTunes -- there's even this tech note from Apple encouraging you to do so. However, if you try, here's what happens:
/Applications/iTunes.app/Contents/MacOS$ gdb -q ./iTunes Reading symbols for shared libraries ............... done (gdb) run Starting program: /Applications/iTunes.app/Contents/MacOS/iTunes Reading symbols for shared libraries .................. done Program exited with code 055. (gdb)
Ouch. iTunes crashed with "exit code 055" on startup. What happens if we try to connect to an already-running instance of iTunes?
/Applications/iTunes.app/Contents/MacOS$ gdb -q -p 11079 /Applications/iTunes.app/Contents/MacOS/11079: No such file or directory. Attaching to process 11079. Segmentation fault
Ouch!! GDB itself explodes.
Luckily, we can use GDB to debug itself to see what's wrong:
/Applications/iTunes.app/Contents/MacOS$ /usr/libexec/gdb/gdb-powerpc-apple-darwin -q /usr/libexec/gdb/gdb-powerpc-apple-darwin Reading symbols for shared libraries .... done (gdb) run -q -p 11079 Starting program: /usr/libexec/gdb/gdb-powerpc-apple-darwin -q -p 11079 /Applications/iTunes.app/Contents/MacOS/11079: No such file or directory. Attaching to process 11079. Program received signal SIGSEGV, Segmentation fault. 0x9002d2c8 in ptrace ()
Ok, it segfaults inside the ptrace() system call. Maybe the parameters are bad?
(gdb) break ptrace Breakpoint 1 at 0x9002d2b4 (gdb) run -p 11079 The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /usr/libexec/gdb/gdb-powerpc-apple-darwin -q -p 11079 /Applications/iTunes.app/Contents/MacOS/11079: No such file or directory. Attaching to process 11079. Breakpoint 1, 0x9002d2b4 in ptrace () (gdb) p $r3 $1 = 14 (gdb) p $r4 $2 = 11079 (gdb) p $r5 $3 = 0 (gdb) p $r6 $4 = 0
ptrace manual page and header file:
SYNOPSIS #include <sys/types.h> #include <sys/ptrace.h> int ptrace(int request, pid_t pid, caddr_t addr, int data); ... #define PT_ATTACHEXC 14 /* attach to running process with signal exception */
This exact same call succeeds for all applications except iTunes, so the bomb is probably not in gdb itself.
The ptrace() function is in the Darwin module xnu
:
if (uap->req == PT_ATTACH) { /* * You can't attach to a process if: * (1) it's the process that's doing the attaching, */ if (t->p_pid == p->p_pid) return (EINVAL); /* * (2) it's already being traced, or */ if (ISSET(t->p_flag, P_TRACED)) return (EBUSY); /* * (3) it's not owned by you, or is set-id on exec * (unless you're root). */ if ((t->p_cred->p_ruid != p->p_cred->p_ruid || ISSET(t->p_flag, P_SUGID)) && (error = suser(p->p_ucred, &p->p_acflag)) != 0) return (error); if ((p->p_flag & P_TRACED) && isinferior(p, t)) return(EPERM); if (ISSET(t->p_flag, P_NOATTACH)) { psignal(p, SIGSEGV); return (EBUSY); }
Funny how the important part isn't commented at all :-)
The only other mention of this magic NOATTACH flag is this:
if (uap->req == PT_DENY_ATTACH) { if (ISSET(p->p_flag, P_TRACED)) { exit1(p, W_EXITCODE(ENOTSUP, 0), retval); /* drop funnel before we return */ thread_funnel_set(kernel_flock, FALSE); thread_exception_return(); /* NOTREACHED */ } SET(p->p_flag, P_NOATTACH); return(0); }
.. also in ptrace().
/usr/include/sys/errno.h:#define ENOTSUP 45 /* Operation not supported */
.. and so we, incidentally, have the origin of the strange '055' exit code (055 octal is 45 in decimal)
So, there is an undocumented system call:
ptrace(PT_DENY_ATTACH, 0, 0, 0);
.. that lets a process avoid being debugged.
#include <sys/types.h> #include <sys/ptrace.h> int main() { ptrace(PT_DENY_ATTACH, 0, 0, 0); sleep(100); }
~/dev/airtunes$ gdb -q ./evil Reading symbols for shared libraries .. done (gdb) run Starting program: /tmp/evil Reading symbols for shared libraries . done Program exited with code 055. (gdb)
/tmp$ ./evil & [2] 12220 /tmp$ gdb -q -p 12220 /tmp/12220: No such file or directory. Attaching to process 12220. zsh: segmentation fault gdb -q -p 12220Same exact symptoms!
Summary
~/dev/airtunes$ gdb -q /Applications/iTunes.app/Contents/MacOS/iTunes Reading symbols for shared libraries ............... done (gdb) break ptrace Breakpoint 1 at 0x9002d2b4 (gdb) run Starting program: /Applications/iTunes.app/Contents/MacOS/iTunes Reading symbols for shared libraries .................. done Breakpoint 1, 0x9002d2b4 in ptrace () (gdb) return Make selected stack frame return now? (y or n) y #0 0x00249610 in ?? () (gdb) cont Continuing. Reading symbols for shared libraries . done Reading symbols for shared libraries . done Reading symbols for shared libraries . done Reading symbols for shared libraries . done Reading symbols for shared libraries . done.. and it works. Enjoy.
Changes for Tiger
At some point since that piece was written, the example at the end of the text stopped working; now, all OSX programs call ptrace() several times on startup:
(gdb) break ptrace Breakpoint 1 at 0x900541f4 (gdb) run Starting program: /Applications/Calculator.app/Contents/MacOS/Calculator Reading symbols for shared libraries ............................ done Breakpoint 1, 0x900541f4 in ptrace () (gdb) bt #0 0x900541f4 in ptrace () #1 0x92857a00 in _NSInitializePlatform () #2 0x909ad55c in call_class_loads () #3 0x909ad470 in call_load_methods () #4 0x909a8308 in map_images () #5 0x8fe0fc94 in __dyld__ZN16ImageLoaderMachO14doNotificationE15dyld_image_modejPK15dyld_image_info () #6 0x8fe06258 in __dyld__ZN4dyld12notifyAddingERSt6vectorIP11ImageLoaderSaIS2_EE () #7 0x8fe0ef78 in __dyld__ZN11ImageLoader15runInitializersERKNS_11LinkContextE () #8 0x8fe03784 in __dyld__ZN4dyld24initializeMainExecutableEv () #9 0x000022d4 in ?? () #10 0x000023a4 in ?? () #11 0x00009390 in ?? () (gdb) p/x $r3 $5 = 0xf000 (gdb) p/x $r4 $6 = 0x0 (gdb) finish Run till exit from #0 0x900541f4 in ptrace () 0x92857a00 in _NSInitializePlatform () (gdb) p/x $r3 $7 = 0xffffffff (gdb)
As you can see, they call ptrace(0xf000, 0, 0, 0). The function code 0xF000 is clearly bad (the valid ones are all below 50 or so), so the call just fails. The error is ignored, and life goes on. This is in NSInitializePlatform, and there are more calls all over the place.
The calls are all neatly placed before and after various stages of the startup process, and are numbered neatly as well (nn00 going in, nnFF going out; sometimes a few more in-between); I'm guessing they were either used for profiling or were added as suitable places for kernel extensions to jump in and do their thing. Or they might just be there to make system call traces more readable. Who knows?
To get iTunes running in gdb again, you just need to breakpoint the right ptrace
call:
(gdb) break ptrace if $r3 == 31 Breakpoint 1 at 0x900541f4 (gdb) cont The program is not being run. (gdb) run Starting program: /Applications/iTunes.app/Contents/MacOS/iTunes Reading symbols for shared libraries ...................... done Breakpoint 1, 0x900541f4 in ptrace () (gdb) bt #0 0x900541f4 in ptrace () #1 0x002888fc in ?? () #2 0x8fe15b8c in __dyld__ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE () #3 0x8fe0b5b0 in __dyld__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextE () #4 0x8fe0ef84 in __dyld__ZN11ImageLoader15runInitializersERKNS_11LinkContextE () #5 0x8fe03784 in __dyld__ZN4dyld24initializeMainExecutableEv () #6 0x000046d4 in ?? () #7 0x000045b0 in ?? () #8 0x00004510 in ?? () (gdb) return Make selected stack frame return now? (y or n) y #0 0x002888fc in ?? () (gdb) cont Continuing. Reading symbols for shared libraries ........ done
Comments
On the original Wiki, some anonymous passer-by added this section.
I tried this on Tiger with iTunes 5, and noticed the following.
iTunes 5 on OSX Tiger (1.4) seems to call ptrace more often. I haven't checked what arguments it passes to ptrace, but there seems to come no harm from just ignoring all those ptrace-call, like outlined above. But because it's quite tendious to return from each ptrace call manually, I created a small gdb-script that does the work for me. Here it comes:
break ptrace commands 1 return continue endNow just call gdb with gdb -x -q ./iTunes, and it automatically ignores all ptrace calls without bothering you.
Another anonymous commenter sent a link to this kext to disable PT_DENY_ATTACH.
"Evil Genius" pointed out that Phrack posted instructions on how to disable it by NOPing out the libc stub for ptrace().