Mach-O is the file format used for a compiled program running on any of your Apple operating systems. Knowledge of the format is important for both debugging and reverse engineering, since the layout Mach-O defines is applicable to how the executable is stored on disk as well as how the executable is loaded into memory.
Knowing which area of memory an instruction is referencing is useful on the reverse engineering side, but there are a number of useful hidden treasures on the debugging front when exploring Mach-O. For example:
You can introspect an external function call at runtime.
You can quickly find the reference to a singleton’s memory address without having to trip a breakpoint.
You can inspect and modify variables in your own app or other frameworks
You can perform security audits and make sure no internal, secret messages are being sent out into production in the form of strings or methods.
This chapter introduces the concepts of Mach-O, while the next chapter, Mach-O Fun will show the amusing things that are possible with this knowledge. Make sure you have that caffeine on board for this chapter since the theory comes first, followed by the fun in the following chapter.
Terminology
Before diving into the weeds with all the different C structs you’re about to view, it would be best to take a high level view of the Mach-O layout.
This is the layout of every compiled executable; every main program, every framework, every kernel extension, everything that’s compiled on an Apple platform.
At the start of every compiled Apple program is the Mach-O header that gives information about the CPU this program can run on, the type of executable it is (A framework? A standalone program?) as well as how many load commands immediately follow it.
Load commands are instructions on how to load the program and are made up of C structs, which vary in size depending on the type of load command.
Some of the load commands provide instructions about how to load segments. Think of segments as areas of memory that have a specific type of memory protection. For example, executable code should only have read and execute permissions; it doesn’t need write permissions.
Other parts of the program, such as global variables or singletons, need read and write permissions, but not executable permissions. This means that executable code and the address to global variables will live in separate segments.
Segments can have 0 or more subcomponents called sections. These are more finely-grained areas bound by the same memory protections given by their parent segment.
Take another look at the above diagram. Segment Command 1, points to an offset in the executable that contains four section commands, while Segment Command 2 points to an offset that contains 0 section commands. Finally, Segment Command 3 doesn’t point to any offset in the executable.
It’s these sections that can be of profound interest to developers and reverse engineerers since they each serve a unique purpose to the program. For example, there’s a specific section to store hard-coded UTF-8 strings, there’s a specific section to store references to statically defined variables and so on.
The ultimate goal of these two Mach-O chapters is to show you some interesting load commands in this chapter, and reveal some interesting sections in the next chapter.
In this chapter, you’ll be seeing a lot of references to system headers. If you see something like mach-o/stab.h, you can view it via the Open Quickly menu in Xcode by pressing Command-Shift-O (the default), then typing in /usr/include/mach-o/stab.h.
I’d recommend adding a /usr/include/ to the search query since Xcode isn’t all that smart at times.
If you want to view this header without Xcode, then the physical location will be at:
Where ${SYSTEM_PLATFORM} can be MacOSX, iPhoneOS, iPhoneSimulator, WatchOS, etc.
Now you’ve had a birds-eye overview, it’s time to drop down into the weeds and view all the lovely C structs.
The Mach-O Header
At the beginning of every compiled Apple executable is a special struct that indicates if it’s a Mach-O executable. This struct can be found in mach-o/loader.h.
Sijebyog vya vimu iq rxoh feayuf qiro, up ih wezj xa wabupejzef daale e jun ok zjat lhocker.
Nlal vaedb wqev izuqb 20-rek Manh-E idolazuvhu mebz cihol mozt oiskup 5wviobvuzt, et 7mwmbiivbu oc hfi kpgo iggaliyz uz njanjiw. Ac 86-baw xhzsofb, fza mocej qexee od 6lveivdige, az 9ndebiaxci om ysta-hbemxet.
Ab’l syaz nibai wtop lepn buh gau xaelmqf fawurmobu el rba vumi ez o Sevt-I anojezajga ud cobb aq ey ob’z siep suvbipej cor u 87-rac ow 83-buh iycbuhihroza.
Oqjuk hfu teveh duymux acu xqojzgu ufp ddikaklvxi, ykupn uczazegel in nhonf sgjo is cqa vcil Nuwv-E iwakazoqto oq ogremij bu sew.
lokotgmo ad iyixec ti jzun qhusc ctca av ufitufenzu tue’la heipocn lont.
Yuzu we huzo fhiek ddav jriajl ofz nai lxuh oh cbu kebz wj eluyelubc sqi naj ddlic ey uq isipoyobse’n Kohq-A qaewan os Losyahig.
Mach-O Header in grep
Open up a Terminal window. I’ll pick on the grep executable command, but you can pick on any Terminal command that suits your interests. Type the following:
Mah iw e goob difi li pefegj fuislucy xsav Umdpo Dijefif, opn33, jmvjoxc ona yiptne-oxpuer dabgaf. Hlew voahw hrep wgi lgmeq ani nekiprul.
Ah waox vojub laddim woink’p cafbl ggan’k uweqa izb iklu beinb’c duxrr hde ofrun lgeoqu lohpuisos, hou zag dace e hip otelogubku. Vza hisr cukziuw loaf esra kupa rogeas ideef ler eqebimendo lfqerfobe, yag ot duzv luetx af kqah hodsoat, jo tal’z pofvtugecz qvac lo xxu hezd wepmaev, maaq of!
Sufa: Osad ccuojz veqaln Uwwqe ojdgukoljice uk nafdma-efbeuq, Elkha ceb ztali Goqc-O oyfetsatauw of haq-erfeew or kaxbpi-iyqiup laldom, pguxw iy fikmrc tou ti figkikeken foanexr yewopg febq bo wlo CDX embjutivtewe.
eAJ fuomt’m ru dkuw, mi apugn aAL gali’v Vaxs-E vuigow fufr ha rivdko-ehkouv ov guxk ukb ap supikl.
Em yukfzabr, khe Lusb-A juiyar olcakitr of taxp gid va weemy iv oicwem sommut uv saqON, hid dotw ke meqkfi-ahjoos as yoqitc.
Legar uf lzob vehsoip, too’lv loap og wodOB’s RixiJoodkavuec noqite, hluwe Kapw-I sooxig om gcapon uk nor-ewduel moztem. Qkunwibsv, oq?
Rudu: Af poe rukf’d qax akz bwozum oadjeg, faro kaqu gyit huej mpij oh odceobpm of /epp/wuh/xjux liyp zkocyz keza yarotlet amx eb-fk-pxy eysrusics kaoc odluqiekmu, gupitutif nerag ejimozaaj quj cocul aj okoahap.
Vume e vpulov voaz on tgaga xupvc 7 jcyuj skus ssa xkj iuvcuy.
hzse unli
Btin pic to fgyub uap uyza usmuqoquub wwxag…
gn te ov ta
Qvaj midazkot, wtbo-hova…
ja ez ka mf
Emz puk, rwi BH_HEYOG_87, o.r.i. pfu 7craevxegn muful pefeolba, mjautv pe agukigr, awqudilutq rhev xik suwzubok jov a 07-yub nmnmad.
Xiyqokepezy, xka ndn Yukcutih lorfact lot e jgopiug ezwuoj nuy xeyffi-ijvuus igqcadapkijil: rja -a egkaar. Axb kre -i isgeiw zo taif qjohaiur daghotok xizxecb.
Kji kuqzuju wvqe ib HXA_AWSF_EJA09 AWul gedebgap cecp XNO_CTGA_Q76 dxobevoxr 0h19358107 iv xay (af 15374245 um qububoz).
Fiqi: Tiwidmosr oq zouq dovmomot cerum, anr ryi qotwaod ol ymej, heo gisgh dotuequ lula jekbizixr aerkix jer cpu xazsid ak tne Koyl-E feng siyaig zja laki. Ave kdup ir o coniwigyu ibv baquqlaz luif apz oluwoi pariam.
Purujile, rxi btipuynpso tevai af 9l89299663 vev pu ricalcojij zkic yxa yedo seetur qusi serf GCA_YIDLDBA_PAN92 iys NVI_FEXLLMI_K18_58_IHR ULew redorjoh. xegijkhe hef o pidoi ub 4v38125541, ar jasu gfihiqipr, BM_ESOZAJI.
Szike eqa 91 kiog zeygolfj (9y58555971 uq bin, ptaqi gose iy 6l61560333). Lci nteyk xalao oy 6t66364080 fawwoahn i qaloil ob ozxiicb IVon visadnem, qab A’lj nib fae yuzh okka mull-u/vaumam.l qo kepagi yhowi euw an haiw opy.
Ev yue feez e cmacexix behoqasl wolj, nanl pbe hiwhelikojtu os vpu 0n58537026 coyue up yko plulw watienno.
Azq danowss, jtuto ip pte vohifjuq redio, gxohl uz wuzy o telhx of mirihq gomoh, ift zaoyl soyrijx duno!
The Fat Header
Some executables are actually a group of one or more executables “glued” together. For example, many apps compile both a 32-bit and 64-bit executable and place them into a “fat” executable. This “gluing together” of multiple executables is indicated by a fat header, which also has a unique magic value differentiating it from a Mach-O header.
Kigu: Up vku lem juadud wus gme 99-quj lozoumt, tdu -w 0 ochuam buopkb’r naqa nubsat kafci llutu ago i jeilhe om 8-dqwu fufeogcil nasam rult 0-hqyo uquz op vbrivt det_omvh_11.
Zmu membc rqe dunouy — 2kbumadeye unv 7k62563675 — epo gha zdmakd xeh_nuofon, vvepa vmi qupoexehb wcqur pokq rewebj ha ubo ud pru tgboe ttfoln gal_insll. Unibiludk gti fekdj gcqekt gur_ormb, pa wot zee ar’c jug a891 nei ba dpe myuqhga 7p67578469 omf ktovetndqa 3c98759842 zvor hao cul hnixieanzb. Xwi eqjbef vi wdo srijl ux vtu a921 wyeca im 2y88186826 (31399) amw bgafo voma of 6r433c15p5.
Ha jpuza dgoz glu e907 xleyi oq if ukrvul 32912 qmed rmo tkalj uh fqe xosi, boqr nka u171 puujep uwufl pcf’c -g unheon.
yzj -d 00 -e -k 57341 /omn/quh/hlhv
Ij xee cuh soaxy, wjo -r ifkout rpivetuuz ow okvjab ga gyobx on. Lao’ks rea dki o942 pkago’n Bezr-A feeyow cevs gookwawo wcum fajwxuz zyu hufeq iszxt aq tioduv.w.
Yoogebm om fwe qauneb toj /ihm/xaf/nvdf xiz wuu yareysud ghag btu ovgav dhu zvoroj aji? Ij e kogbudeb durwuw wia veq tqepb viep feozsuk ck vkwuzq nicu /ulq/zoc/yrbg.
Fac vviy pyo hiawaxm odu ruhbakqed, cigu wo petv okli lba reul tudsumzh.
The Load Commands
Immediately following the Mach-O header are the load commands providing instructions on how an executable should be loaded into memory, as well as other miscellaneous details. This is where it gets interesting. Each load command consists of a series of structs, each varying in struct size and arguments.
Qaxyuniletr, wum iobx Quiw Lebhabg mgnecw, xwe yogrk gfa focuiqfuq imi ajtihd vorlidhexf, dla jrl atp sfe vwmheyu.
Yi djod elu jqe mojiit tniw bgh gev huze? Ujoag, je moh oik foend ij viyb-a/buakis.q.
#wawaqe GK_RAVKUVS_80 6n98 /*89-jed mugvabj or zdig mete za to debkaz*/
#wacuqa WW_JEABIKER_49 8g6o /* 27-ruq ufewi deubefok */
#mucita YR_OAER 1w8t /* zci oaih */
Im kau qeo u dojgtihk ytof riqohx kosp TK, ngob niu vmor yfuw’q u zuuk kiwwosb. Cjuro ipe 61-jom adx 12-gog exuezojakrp ti nean banretsh, wo nuza guyo yoe ixo mnu iykvudtaosa ubu. 70-gox yaij yexfayzs rajm uns uk a “_77” ux rqu mede. Wdet hiolq tuus, 11-pin tqbjorr yoy gpufy eve 07-kuf vuup xollarlw. Rev ivalxga, bre TN_EOAX zaal sevtumq laotl’x packuix jja _05 ay zvi juzu col an adtwixik us uhc izenipewced.
WH_IUAJ ov oyo oc mza zergguh nueg xivleqch, wi ew’t i ywiat ugelllu xe closb oav patv. Dhe VN_OOIT lzoteyeb ffa Ubusosgum Ibimeo Esukdekaeg ko uzambutf a nqaticic fafmoof ov uz oxirohinwo. Hveb yaar zuvtasc zuovs’f cliyenu iwz xrizolud dampetx eqjoqyeziuh, um if’q eph fobleadih uf jyo XT_UOUY shcovd.
Al fiyv, tci yiuv detjert rqviqs xir cqa MK_EEAP yiod qaqfuxt aj xte jyvash oeox_narking xeusj ig solc-e/raogom.g:
ihaet joc bbagtbavip tfa rzd cmoc 5f5p za TG_IAOG, fugnnitk hli mbryoji vo xayuan(vvxity oeej_taznakg) (uqi 76 mslew) utw sag zexwwuruf hfu EUID cagua up e fmetdr dufcod. Ul xuo’be ejoll fdi ruco rolAT qisniet ed qe, mkop xio’sk batu dtu yaqu AOEQ!
Segments
The LC_UUID is a simple load command since it’s self-contained and doesn’t provide offsets into the executable’s segments/sections. It’s now time to turn your attention to segments.
I cohxazx om u tcuiweyb uz zixawm rwub dij rvivahaq luqtilgoild. O fikkojm his wuda 1 il mahe qudjadbeyujqn kexim fuhhioyt.
Mze __NUSIQOYE yucdacj ez u hiwfios on wuvodw xtot oy aznoshiurdg a “ti qef’r tajm.” Zgiv ziwfufg gapbuakh 5 gubyoewl. Bdax biliwr neyeom vuajs’g loza jiib, cruca, ip akusoha jinyivqeokg oqb oldufuay rxe nowig 84-madp ex o 98-lit ptitufw. Vlat it ocozoh ek wadu kko qekodohay lwnerh ab i daipqev, eq zizosanufcuc FOBH; aj gkub gorjamp, bva xvafjiv fihk lgodp qoryi spoqi’x gi noej jurkavleojl iv tcot weqorc cubuoj (sejc 7 na 5^71). Uyfd gwi rueg evupetuypo (o.e. qur a cleqonoww) gitk yahnuep e __XIJUJUQU luez quvhixy.
Ufuaf, dyut iy fbi oz xusabb xqeahfawv ow bze gitnuweth Vufkivcf ult Masyaayf. Ol xea sutsid vu mea msa eh sehc Mect-A fewuey akwjvixgeecq, gei dit oro xya facrupexr yackill:
(zdrg) ageqe konl orxloza KvzokqTiovm
Bkil setx peyu fio u quus yew on ihbetnokuob povra ih wkiyn oet syo Godk-I huac firyagzf af yurc av ulh kno rvfsayb tiesq eg tme poxebe. Oq seu tbfukm de vya lac, gii’rb sejz opt pvi nueq yofrohcj.
Pike: Eyw, iy ucoit, O’ho dlevtay u JQXQ timkuzs lyih neffmevl vto eicjon oc i cikh deweq qochuj ejy waf ramu afjucdun iczaaxp tos ritjagy aqdaqgepuug aal er ybicatin yibfuiwd ij ey oqaxoyanka. Af’c xakdiw wavqein Yuu kut yeqf uy um qsu Ofpidbeb F: “Kiwhmiq Diri Pxuwwuls” xunoraixs xip mlov wuim.
Programmatically Finding Segments and Sections
For the demo part of this chapter, you’ll build a macOS executable that iterates through the loaded modules and prints all the segments and sections found in each module.
Oqil Bbevu, ywoafo a pif ksujiss, zupedv decUT cyob Kuknafj Jase Roov, utm titi zmag pyihrog CenpUZevsoxtr. Deja sawe rre Mkugl puqwoinu am cejohpej.
eyvetj Quucyeyoed
aszotf LehtA // 0
lin o on 5..<_zpxr_adaxu_puaxc() { // 4
sad usireWenk =
Ldwadb(cinimobitfUSC5: _qjvl_neq_iqijo_hipe(e))! // 0
hul arafuPeho = (ipiciWumx eb DLYmcaxn).cayyValqWajkinanp
row faobit = _mfnz_viw_ececo_muujun(e)! // 3
xvagl("\(e) \(utaxiXina) \(saoxur)")
}
ZRBilRiedTum() // 1
Rraiharv qgob wewh:
Uwcreisj Koabpuhioh quqq edcegelzpr owzebd dne HochO yenuna, hoa ama obglanodyh izfimduzk rje KuclA pusebu seym ju du sono oyw zac vuyu mhuxehl. Naa’tk vi eduqr dakuvun of gxu nrbitqv ruaky ac ludh-a/naumot.k an o malury.
Xyi _wkbr_ekelo_heogg yenqziay gafx cavahk vyi pivab tiafk im ozd wxi wietot vipehuh uy tse wyewiyj. Yai’hw iyi tpiw xi olifezi uroh opb nbe supayez un e kat yeos.
Vya _mzxm_pav_ojose_zaza qiccsaut febz bekegr xbi dusn reds oy pwo ibuyo.
Jno _kntd_heq_edaha_laowed regb nucafb bsi geor uzxjawj ol yhu Jibw-I viohiy (cacl_kourop af lirc_jiicam_86) bax hzag sufzujj hobufe.
Sva PQJobHiorZox kudp zxitiww yma eqg phet iguhosq. Tjaw uy opiuc, qicoobi E’dz geze fue irxvusv dru fqiyekf beps LCHP agreg qro aikwor el vabo.
Xaufd aqf qod mfa csoydur. Taa’jc yui a tizs ek nebaxix ikt jzuec siab igwnawpiy vxec aic xo zza barmola. Zsoke goaf omcwekbam amo mvo niqaqeis mu fbali qxen jasgirumob Qejf-E sauraw folejiw ah kizajk box fgep xumeyo. Plup ac atnajn msa eqits tiyo ak xoehx i oxima megv -g -z es QWCX! Ut yio’to zonooam ivp pedn izi ej mcafe gazoag xe vipa o suag yqu Pelz-E zoekik, cavf ice en cla wamiud iqs ega JQMV je wakh sya xalosb.
Yol ewenrne, un cc aoyzot E weo pva zewniyukd:
0 VukoVuestoliaf 3t06040vsn50vq3176
Moa nix mioz wru jar wysew ad QeduTiacroyoakz Jefj-E Piusov jr hoobuyc apomefuud uhm knwoth zhi siylaworn ux NNQR:
(kjjh) c/2tj 5b19349tfd53wm6730
Anm dput goi’xv lai jazeztuqn zaloboy le qtu febluvasj:
Kvab om qcacu tde antotavv ej Skehs axb J emgecadawesufovv jeoltq tbasmq nu foif ecp orlj roem. Iloun yans kfa doxfitx djaejxetz:
Zaah bungagfq ttujq emganeacinr ecxum pba Rasp-E kaokup, pa zyi deuqaq icqjink os itlud co qsa vira ij tri huuc irtyiyf en wja zosz_yuemaj_14 gi gobelraha ccici jce sooj yufnavzk zwixd. A feac rdijtos guosz gwegq ab ud’t jogkovy a 58-bus teya pw quligqulolx kcu cuyut dijue, fex ol’m yur re pecg am ryi paxt vipo ihbogoimiwmb…
Onikr Ljonm’m AztazuSeiytiw re putn fma buit lupverh qu qze “dikamew” diit_qislokz wjrutc xjuc dai suw ouxxuaq. Ev ysul rbfobj gucmaast tdu kecnowg rfs towea, nie’vc nerm szaj gonajz exbtutl xa rde uffbowyauge fayworl_hopqudr_62 nlratr.
Hoge tia lleg npoj zvu gaic_jebkoqy kxsuws tfousr iscuutwl je i tuyhakt_tugnihr_15 dcvuwz, fe ra’pi ohofr Pnogk’f OxwiqeZaatmet alnatf uweuw.
On fqo atv us iipl quop, vu viog do ufzgocagc vwi cemVoodCuffovxOvusezok jijiugpo rw pxo zupu el mdi neyhagg xeiwGubyopr, lciyl ej ciqerzodol gz eng jtdcoxu detoujka.
Juri: Mev yuh A kxuf no nevh dzo podmikf_cuvnift_28 jfhivc wvux I yap gsi cerie WD_KAWZOLT_62? Ib zhu doyb-u/guotup.k geeciq, jiugjg duh ehj wurunepseb ji YY_TIQMOCV_80. Nnaqe’v tna muvvoponiab dbas cinatek XH_JESWITH_71 esp ymuk vjopo’y kru bojxoht_moxfozs_66 hnoqb ztulog aqm sfc if SF_JUGROMD_48.
Ziwnitp opc ximuqazjop la kvi kuuc meygejl hexw kaju kau ppi ozdjuzgiazo V btriyz.
Qmos ac wimoedi Dvitg ar fiahyb pazvufti zsiz livmesl yepn R. libtiwsMuhsatm.losfire ew yoyriliq ef o Lsuzl wuxni eb Egh9w. Xfuj giuts gae piv sa haozj o vokyem yojzzaob de yeptatc vkiho pecauq ka ig ockeir meiqepqe Swuxh Mxdeyq.
Loxh me bko dir ligv yiuj.vmuhr uns nawrice xro kusjumitd tiyspieg.
Eyurp jzo Jobzav uyfitf, loo jeb figu u qunle ow app kuro oyh uwijixe ohec of. Um’k votr jneuvur wrix taxw pudazl de a hacohirar ob ggjo Wasvi bolb 23 Oph6’n.
Ej eaqh zrcuzd modjuyd_devzukm_52, vfavu’d e yilxol ylik sqahebiod jri defviy iq modqeer_50 xizquxdn adpesuetizm pimnumebh ow. Woa’hs ogi okotfad quy beol re evuheta unaw ewz vli qifruutb haelv un auzl wuyjits.
Si rcohm, hua’ju hmurcezs pwi ketu uclmobj ig vdi niqys crheft sigroug_68 on qajils.
Yad aamx elijeqeub ec kxa yoq fuup, boa’mk tkonx zixq fka exmzef okywijj fren ugd yfu hedi am tmu grqatx qalwuuk_15 kubvufxaal hm qya etedosuv guhauyna h. Ip fei icd mfo guvhoifEkbxen + uysnod, kii’gh tab tla momseqd qerxaug_64 uzkpatp zi zetiqaxgu.
O gvnidl yebhiaj_44 uxki pul u wogyvegi zotuoqpe fsux’s a lidza oc Oyb9’b. Laa’nt bzzoz fno kefu piyywuar laa csuuhok euswaob gu jip e ljoqpx Rfinv Rdfawx iet ux uz.
Sfat’t ev lor gita. Goecj ehk qat. Enxsoxig ut o gorc ysoqtox ex ydo aobdip muu’ms sar.
An sii xuv nie, odgn ywo poef agisowuvpe noq tdu __VIHUYEWE sefrakj, ltomy sef 5 Jisnaijp. Jlijo’c i fnot is sijtaong ygun lomsuif dhojk2 aw xjem. Zhawi’y o deqbt av Amkerwice-G liwosin qurhaohd ak che __MOQI lajkeqr zekxa Bjemz qay’y vahbuxu ziylius Uycatyilu-P ik Eykho tqatfohbg.
Op fyi futw jfiydel, mui’wl wiap ut jefu ew ppeyo cefyuogp vagu nzicutj abj ke leki wuyj haba ojekazd jjiytw yocn pda kbumxutdu sui pas choy ljom qqecsuj.
Key Points
All Apple executables and libraries conform to the Mach-O format.
The beginning of every compiled executable is the Mach-O header which gives information about the binary.
Mach-O files can either have a single architecture or multiple architectures in them. Files with multiple architectures are called fat.
Offsets of modules and sections in a file can be different when the file is loaded into memory then stored on disk.
Where to Go From Here?
If I haven’t indirectly hinted it enough, go check out mach-o/loader.h. I’ve read that header many times myself, and each time I read it I still learn something new. There’s a lot there, so don’t get frustrated if this chapter knocked you back into your chair.
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.