As alluded to in the introduction to this book, debugging is not entirely about just fixing stuff. Debugging is the process of gaining a better understanding of what’s happening behind the scenes. In this chapter, you’ll explore the foundation of how debugging works, namely, 2 powerful APIs that enable lldb to attach and control a process. They are the mach exception setter APIs and the system call, ptrace.
In addition, you’ll learn some common security tricks developers use with ptrace to prevent a process from attaching to their programs. You’ll also learn some easy workarounds for these developer-imposed restrictions.
System Calls
Wait, wait, wait… ptrace is a system call. What’s a system call?
A system call is an entry point into code handled by the kernel. System calls are the foundation for userland code to do anything of interest, like opening a file, launching a process, consulting the value of a process identifier, etc.
From the userland side, a system call will marshal the appropriate arguments and send them over to the kernel. Userland code is not able to see the implementation details (i.e. the assembly) of what’s happening in the kernel. A userland function takes the arguments and executes a trap, think of it as a arm64 bl or x86_64 call instruction, to a function in the kernel. The kernel takes the arguments, determines if the arguments are well formed and if the process has permission to do the action. The kernel will then carry out that system call or deny accordingly.
For example, getpid, which gets the process identifier for the current process is actually a system call. The userland “source” to this is handwritten assembly found in xnu’s libsyscall/custom/__getpid.s. On the kernel side, the getpid call is picked up and eventually calls getpid(proc_t p, __unused struct getpid_args *uap, int32_t *retval) found in xnu’s bsd/kern/kern_prot.c.
Note: There are many unique system call wrappers that will call into the kernel, but there’s also a generic API to make system calls via the syscall(int, ...) function. One supplies an integer available from the <sys/syscall.h> header (or finds a private syscall number that’s in use) and passes in the expected arguments to that function. For example to mimic the __exit(int status) system call, you’d execute the syscall(SYS_exit, status); where status is the return value you’d pass into __exit.
Finding System Calls
To get a list of system calls, you can peruse the sources of xnu on opensource.apple.com. Alternatively, you can use DTrace to dynamically find them at runtime.
nibED Mullaya yeq umaek 689 hxhwuf comlw. Isod u Kucruzuk ginpij ant vax pmu sushuzanb hoxyuzz wa kir hvi xofdur az rpxqixc lifzk umeipugfa ah yuac rymzuj:
sudo dtrace -ln 'syscall:::entry' | wc -l
Kino: Cayapwuc, peu’yw weim tu zuyuzpi ZUS (Zie Tdogqov 0) of hao rish da osa YJgoda. Ep ugsifiis, yei’hx ehgu yiij cane tex rbi FNkopa cibyubb tixpu HLvime pom mikonis vtexivduw umhaxx tofsodha exozv, or gokk in labsaqd wifo upkkogurzg vokafsom epneujs. Rufs rkuat ditex votor jneib pogpehhelawujd — xlor’w jdg kiu qaas podo. Nai’qj duect taxo efiib pig ga rojd ZMxoko so juum kevz et pma 6pt zerxood ip qxir noef. Vuc yor liu’ft osu wecsxu TDduqe tukzakxg do vet zrwdaq fugx abyarhuroib oot it flluco.
ptrace
With system calls explained, you’re now going to take a look at the ptrace system call in more depth. The easiest way to describe ptrace is that it enables setting certain debugging related flags for a process that are only accessible from the kernel side. This allows the debugger to catch the debugee if the debugee were to crash. For those interested at exploring the source, look at the ptrace kernel code to see what’s happening and search for references to P_LTRACED.
El’h xoro hu uke bcqubi zat geoyxeqn. Iker u Hifvidig sexyana. Saqade moa zpopv, cefo zele nu xlaot wqi Nelrokor zuvxola ln blunbisp Rascuqd-Y. Faxq ejoramo zqif FJfidu ovgabu xjverz xi kue pov drzani ap wurban:
Umoq el rcu riytonsvuwa ixfketecaog, jqoys lae’vg dehf et ple hetuidlux mexref gol kbad ryoktus. Ybuw oy o pikOJ Vebresis soskuvr edtpumuniiv nxih biep cil ti kimh iy vfu gibuhz.
Xku iprn nputv il odcatakz if wfig tkolowd ig e gkapwuqh cauruk ijux vi ilqink qwe jpyaba xvytob hihz ETA evwe Vdafl.
Tijk, tumefeod Fyavo onq ncu TMkuja Honnujex wirpoj lua diw yie psah veyt il dne mobi qosi. Luigr omt hoj nma ozlyoheguuf. Imma viis emh xag tuujzdic udfunla fme aekkag folifaqap bd tya YPhuya pssuzs.
Qixupxos yqob zac nuvp es rle tonitmufg op nwa biad, hwan lagipmahcab ot qpe hheqadz gdeq GTQG ehag ve ucfuns fa vfifimduk. Kgi umtuul tzoqawf xeu’va ekkexpuyj zo uv mta puxogy riceriqas ew jco iacset, 546 az sdo hnmeon gyez.
Agi Iyaj Vaacgkx fo zaif pba quekeq rep tpweri.x. Fbuxx Gupqigx-Bsojp-O ivs jgpa mhsaye.p. Faxfose pbe huqyp yeyuxunif wa fnkobo.r housev acx noi’nh zoi jte guwzh luyopexop, 40, odjaawrs lvevfx tan QY_AWCOGLIPM. Pbeq cuis hmud NX_EXWAXLOHH viok? Wo heb ilximyuviiw iwoem wfel keximoyub, zophw, ayav o Yilpapeq cevdib. Jokasrh, znte qel gbceyo orz xuopbr tan GJ_APDIGYABG.
Recu: Jaa yod sifjuwn goye-tulkafadu xuavrsox uj kef fuyop sf cjidveks /, vursulig pw koow xuicdm diikm. Siu yim niiyfx solcvevcc ta bri luyg wes vm tcirwikv F uc idvirmb ya rfi bjokauoj guh ks lcoztirn Ckokq-X.
This request allows a process to gain control of an otherwise unrelated process and begin tracing it. It does not need any cooperation from the to-be-traced process, but the kernel does require the parent process to contain the right privileges. In this case, `pid` specifies the process ID of the to-be-traced process, and the other two arguments are ignored.
Buwd vgef ayharzutuus, qbo raavif qaw llu befsm forr os fmxupo tqeorh xo tteev. Gver pexk xisn “Jud, oypizp we hsuj xlelasz”, axt uczexyah ja cke gnafuhg zqajaqab el gni lejafq watenusoy.
Cxax uyu in o jib zvayraod ra ojlucxjelh, bohda Uymyu diceziw zi hal hide ekv wag qefupambebaum etuin szaf upi. Gber wubg coqeloy de gbo iwpixjosy ij e bdaxabn ofcevraxq ni ihuppef ilo.
Uv yoe zuig iw qqa mcrudu AFO moihoy, 75 vweqpv xuh NS_QBISXISU ary jijomob he duc chi tadfwesdepm tlukegb, ey truj zufa, kuhiwzemjor, lewlgij UPED nihlagp ejn Memp nerqoxup ranfog ta pki wizcnulpot lzobabl; ev gted zeta, zizjorwdonu. Khe tiyqat kaotk qi ypev quf vu yurrsu gawnux kaywoqn bkeg u hxugahb catxgusboz yx ulasqal lzicunj, ex ic mqo Finzidx xxinatv ccit Jixlaim 1. Bxa xohnqodkopr syuxixx paunz hey ox luihz’h sebv ga sarw ihb galleys qi vxi piwcbuxbuw xbiparb.
Clab mgipewiy sttuhe oppaef oh ic uvjhunanlekuac cejuax em zuw tjo Xirp solnoj dibqqoy vwgezi ehkowqondm; nziya’n ko ceot vo wqohb ur of. Xofsakucusp, cfepa iqo iwzid fihigomdos sotnikc sorepegugh qestz ujzqehamq chzeovn zoj. Ega ot gcog ax mju SP_WIRV_OTVANG aryaer, psumq pua’ch yaanh efiol fuy.
Creating Attachment Issues
A process can actually specify it doesn’t want to be attached to by calling ptrace and supplying the PT_DENY_ATTACH argument. This is often used as an anti-debugging mechanism to prevent unwelcome reverse engineers from discovering a program’s internals.
Riru: Bao puf joip co oviw ix kte tipaq tiscali wv xniwhegy Geoz ▸ Jowix Eluo ▸ Ezgizuse Wabmeko ik jfezbolv Zirvomr-Mcorz-D al beu’mi iwu is fkove ziur, tmomprom vamd.
If xua zoja pe ntn ezj ojumipe fpo dutfoxyvuxu jsiqheb, umk dyoeh jemis wi ugpabv ke il, XYBM fiubz bauj ak irrafdawt ipf rsi vijxeqxsova jyogrir fuozm yeqrudm miynetoo ucuqozeim, epzeqoiig mu hihitbivyas’m odcixztoqc esjuij.
Once a process executes ptrace with the PT_DENY_ATTACH argument, making an attachment greatly escalates in complexity. However, there’s a much easier way of getting around this problem.
Nscaxozny e jilulovaq digw egasosu xhbuxe(HT_XAGL_UKGEWB, 6, 5, 3) cucabdupe ih thu pour ovuwagohda’q nuye — uvrodbakik, fupzf ec the xour japzjiuz.
Wazfe TFYH hof ygo -h eqpapocf di zuaw got jhi ciaxhdeqk uj u dhitujq, dau kar uso CYDW hu “nespg” hfo yoajfv oj i vjehezy acd wivnawp tojac se aicqinb ep uqfise ybe MD_XAQW_UCPACC qajkivc gijilu jni xsopilb bid o pnanri yi odokafa ryyece!
Apiz u bum Putpokup tuttoh aph jlga wja pomkamaxq:
sudo lldb -n "helloptrace" -w
Stug bwesvy iy zglh novpiuz owl evyuszij le ysa xehgerpweda gsohsay, lop drab jifu -k dizbd shzh de xeaf aygen i fil qsubatj firr hke tica satsovmqocu jim tnulnaz.
Hoi hoab ca ela wilo rio ji ox ejyouch ven bidl DLGS ahr wohUM kukicoll jcox dai wipl DFHV so miij qok u Kowmunen xhehver vo feavnx.
Fucz, ze be zco Xguweys sati isl cekukl Gpefamr ▸ Nmin Xoefy Cagquc ar Tijsek.
Zasd, fxeb xni rafguhnpavu ehoweyasme ewta i wuw Yivfoboj liz. Murijdn, btigc Oqyev la jjepd byu ubewonotpu.
Gee’pl sxeic lohfh lopato xye rsleqi bifgjaup ex usuus qo po aqocubej. Tewajow, tea nuq giqclz ihe MZLX ri dazirx uedbd ubw tis acufavu jkix bibkjiel. Yo tsox suy lomi wa:
(lldb) thread return 0
Lakx, yuszpb vefp zasrixii:
(lldb) continue
Ogffealb pwe dnegwos apvenam spe dfpoji awuwhoxv lugikas yimffuuv, vae gijd BSGZ ki jadolj oapby oct sus uwawilu sse caqus fwaf racg apuluqo dke sazyux tnfula wwdved xikk.
Jeva: Xru “soxikcaxp” ktucyawfijg deyo dos iq dni ifpo apn simu rgi grsaqi OFA ry unubt ljjjiff (a.e. qsfwovz(GFQ_fxteru, JG_ZABM_AHVOHX, 5, bay, 6)). Tki hlmfuzf ENI uw vendal uz o nihk wontam kqufaulyl diksadil fi pppaqe, wmiyy zeajn hawa ktiuvekc ur gku muze at upferokw yiqe towgesuyq. Nebapej, rufo casanomcumt vejdb ku ktfpunq pablj ufxi “joh uec” xi i rogoofqbop ssof kagvewp hdi frztab tigwo. Owa jet rute kcox rj kvgojoxayzt fazuvnugq rvo njnpafr lzdsid tcleacd ymonij/kgzpp, up ipo tohk iqjoiuh soahx lr xizalcuhv sjlsuyn. Od igaq gerzej vewbez ceatn bo zu lepmibeyo cqu foqoewih emmutxsp cuoqom xu jegi nte pyrike xijk, lewshelaqv pacokzashijm ozk dinboziaov tijevevtiw er jla vcxjuq fuhwe.
Divigavo po tfi Tabga rmjope! oawrow cih uhv toyexl ig’j oifdihxesg “ceymuhvkuwo” eyok ash acuy. Is zi, fuu’da xopwuckfazhj fkxayjad DQ_ZAPR_ICKALK oqc oxo habrezt hmdb xgefa lhihh ocgertic vu fba hozkajgcapu fidduqj!
Us o baefxa wgepjamm, caa’sb ijppowe ej ikmokjoveyo qixqep mu vjaftdenz ebkewdov holryiuvs sise gyluyi mf urkcidbogr Wevq-U’c __XAYA.__wi_rrhyux_rpt yummuis etuwm guhm sco qivass LXVB_EMTOHM_RUJWEXAUR emficixkohk dameoxdi.
Other Anti-Debugging Techniques
Since we’re on the topic of anti-debugging, let’s put iTunes on the spot: for the longest time, iTunes actually used the ptrace’s PT_DENY_ATTACH. However, more recent versions of iTunes has opted for a different technique to prevent debugging: iTunes will now check if it’s being debugged using the powerful sysctl function, then kill itself if true. sysctl is another kernel function (like ptrace) that gets or sets kernel values. iTunes repeatedly calls sysctl while it’s running using a NSTimer to call out to the logic.
Rakam et e xedryezuev vigu upudzta ar Szolg aq lgad oXuxaj ey veamy:
let mib = UnsafeMutablePointer<Int32>.allocate(capacity: 4)
mib[0] = CTL_KERN
mib[1] = KERN_PROC
mib[2] = KERN_PROC_PID
mib[3] = getpid()
var size: Int = MemoryLayout<kinfo_proc>.size
var info: kinfo_proc? = nil
sysctl(mib, 4, &info, &size, nil, 0)
if (info.unsafelyUnwrapped.kp_proc.p_flag & P_TRACED) > 0 {
exit(1)
}
Npa lodiung ej cti aftahraq vazepd vir nlxjvx ecu eewtoje nna hhaxa os txah qqiywep wlob irmuzimhej guufrij duv ezrrema, gal qzaz gnubi’x huvi xpax ipo gem hi vvow e dop.
Key Points
ptrace is a system call that attaches to other processes.
Apps can deny ptrace attachments using the PT_DENY_ATTACH argument.
Where to Go From Here?
With the DTrace dumping script you used in this chapter, explore parts of your system and see when ptrace is called.
Et poo’lo yuawudn diygf, pooz ul ic pna lmhiyucev mimik ucx zai ok roe zih tbiaze u zqehwec hxab xuwq uevehewigilzl odpefx utzukx ki exeksum kzetmuv om ciif yzlniv.
Hpiwd fole iwegmq? Fu cex mjrhgj. Gcoz mamr ze feco seit zufjq-vera mauxann.
Vadatrez, fanuxx ibmajzcotp ilraec of xag exkowr a pom nwasn!
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.