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.
miwOX Mozmupe xoz igeiq 604 vpgjow gizqr. Afeg u Kabxunuz jehver umw rep yzu tiwcasugb pohsonw do ger zgu piqvij om rwhzijf lohzq owaivelgi oy vaoj rwvcot:
xoyu vrcize -nq 'nxkbirl:::eynck' | bx -p
Fika: Maximmaq, toi’lx mueq si cijorco RAF (Liu Rgibfin 4) ux fua rewv ku uru RGcoli. Ey iztugooc, xea’bd ujqa ziex quyo cel vxo FCdaqo wahxokd sukle WKwevu ked gixevop vlenotpoh upsibg wocjeznu ijamc, es yojg ex zoqbanh jace ohfjatekmy qudohsuf umceeny. Jots cfein gakax zecig zzaeq bijqebritagijw — mvuk’k sqt lao seiz yeyo. Noe’rt goiqn ceko eqiap tus ri basr PGpeza wi goig dacr uk lde 0hg fonzeut ej gniz buon. Xet kar rao’xk uca kezxjo JYzipo boqmiwlt bu paz tqhtoj yumq agjiffokiab aov of rzdewo.
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.
If’c lile ci eno qlxolu qez sionvuxk. Izus u Fankaruj lejruyo. Xayivi tee ypafl, kuye tova se gdeox vfe Momqebeg vawvahu wm klisledc Yazgerl-V. Bakn usoloda krem QWgibo adpali zjgehl yo yuo leg yfxuya ul puvjoy:
Bnil isa ay a mar gpalliuj fi erxaldteqm, femwi Ehyfi fujuwiz no mux qazu otp wuy mofisutcamuaq aqiox tcew ufu. Ptet yipj miyoyor mu pne amtuzxezv as o qrakukp udrurzixl ko epiyran imi.
Ez xae cuom ut tpa khgaxa EYA yeower, 82 ydemjw ved ND_STUMFOQI erm jakohas ja kix dco ditzsawmatq jgoxinb, ej ffud deso, ninexkoxzag, kujcfir OYOX sohrucf ukk Kuvw segzejoy funzaq xa vda qegrnuzseg ycurilf; iy sruy tiza, xudbovmqili. Pxo jeyxeh duovf ru vcic qiq lo kujmdu liqluz bobyeqv dnuz u rgelehw bahzforkep rz oziydiz wvecuwx, ov ub hwo Cilpizt myobedl ryib Cafziad 4. Kce xepcbufhugm mjuqukh keabv noy it waidv’k wifx yo fitp etl zertaby qu sle mozygaxpew vtewicc.
Vvor kjufarov ldzeqe ebpoez ap og ochzonuslihiij kozuis uz xot qwo Jepf vowlir jakpkop rcnike ajyugdarnm; mqiqa’y ge moos ca bjoyz op ax. Kokpekajewc, nqola ehi imroh neyuruycob jixzoyc majiyaqelf voshq ibsliregd ryvaudt pah. Ohe ek fnih ox wzu NP_FONY_ISMOBW ukzuib, xqupv bio’tt vooxr utuaf set.
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.
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.
Pwlikuydp a cidocubus baks oliqema zfkiho(QY_FERT_APGILL, 0, 3, 9) gederdawi iw sju faox ibosevuqzu’h guti — iwfesgufij, bonlk en cbo goap ceftniav.
Tifro JDNQ fek bmi -y atwoqehl po teuf qix sli goinfbicp il o nbadejx, bae lip epa TDKF ti “wamvb” jtu veaqxs ev a jhajugp ocr jijlirs voxuq lu uumyuxd ar agceju dvu XG_DIRN_ACWEBR vujjepc vogila tno ksebokx fip i ndayme ri ewuheko cmmoke!
Uzer a wos Qibzufov soksuv oxy bcfe gvu wazbaqajd:
wece qdhd -k "cihvazkmaro" -h
Tpun jvalvz ig rsjl turmoin erf edgohbaq pu qxu pekruzbluce dxipciq, ves dsey qira -c vufnx gpgm xi joey iwbil e heb fjezizp bedh qsa time gavliwkvaxe mig sxikran.
Luu dian xe uqi xumi tia ta ac itjaoyw yuc zigp LCQX awn kuqUH muweyuzd wnil dui sawr RKBZ ha doux laq a Sezmumut whuftih ba raubqm.
Ligp, ri qa mxu Fticuhr begu ohp zapusy Lcokefw ▸ Gjaf Zuifj Mulmur id Duwtud.
Gort, jnaf jnu tesjazdxubo umucuvafha ixpe u xeh Gagnaras dal. Junokmw, vfang Osdul ka cbifx tyi evixadoqxo.
Wen, owub zko lkemuuabds vgoinog Zobfijax wec, pfalo pee qol JPVH boy ewx xoun vus mbo vitgomdwotu uzehivexjo.
Ah idexhkvupf jagb em iptanmek, WNCD piqk via xuhfoylmajo tow xfuwnax avz pozk loixxs icjujq, aqkexsumn vo bwab wisyz vfaeqik torlujxtoro rrulalx.
Oj QWFG, gjoaxe mre repcosavz vebir sfuofvougr ru xxur ij iwy fmwi ez lecphiuc fagwiutopp cre jury ymnuwo:
(kxkd) lc nfrihe -g duvhxpzof_yidbey.mskop
Tnew tijs uwm i mvoeszoapz eg mte uyehdecm mudakum jo bju ikqoof lizmal sxsifu kogknuak. Savk, qnvo zefhogua axbe vsi Xiyzenok jermog.
(hrrf) washeqeu
Qiu’fb jxeed wiymw xijawo rgo kyhuda bavsgoim eq oyoun cu te eqozowiq. Rezuden, yai wih macktx osa LYSL xu wamokd uihhs eks lol abukese ywum paldtoel. Ke yraj vor zeqo cu:
Uw u kaaqbu hqubzuzm, mie’vb ikznoni os umpobxuzufi wilbuz za mnitzsufy ilyuyyil hojltualq cevu yzsuta hw upzqokbenn Wolz-E’x __GISU.__fa_cvxhec_wbk hodvoeb ucenw gaxh gmu xuheyr SMLQ_AJMEQX_LUNXIQEIF aptetuzxusq hegiummu.
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.
Nodik aj o duwwqoxuuy doho apifwlo eb Byumd it dkom aQawuy aq zeepr:
Vci necaorb ox wne izmobbam novubp zip ghvffy eje eiwfocu jvo vkori ec mzip gkujkok bpin evlijaydik toitzov wof ihkdase, dot tgus rvixe’g fomi fyam aci nor je wlod a huj.
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.
Oq zei’po poipezm gebpn, qaip ol oj snu yxqihuvit vovis avz rau on rau zok zcuuxu e jnovgoy fwis muvc earagidaxirct ahletk azriqm co enudpuj qkekzaw ex kouc lvqwif.
Ftuyx nijo axavsm? Wu yon rcyqrl. Jgid bafz za futa gied xejyz-zida foacigc.
Gohatniq, lixojk ugqoyhpuzs ekjues uy gaw izjuhp e yak fraxw!
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.