From d126d9abbf307f3dc28a654643d428aa0c1c132b Mon Sep 17 00:00:00 2001 From: Lysann Tranvouez Date: Fri, 3 Oct 2025 23:59:07 +0200 Subject: [PATCH] even more docs --- README.md | 11 +++ .../interception-figure-1-control-flow.png | Bin 0 -> 8222 bytes .../interception-figure-2-instructions.png | Bin 0 -> 11969 bytes docs/interception.md | 32 +++++++++ docs/overview.md | 9 +++ docs/using.md | 64 ++++++++++++++++++ sample/main.c | 2 +- src/mach_detours.c | 2 - 8 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 README.md create mode 100644 docs/img/interception-figure-1-control-flow.png create mode 100644 docs/img/interception-figure-2-instructions.png create mode 100644 docs/interception.md create mode 100644 docs/overview.md create mode 100644 docs/using.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..f27137e --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# mach-detours + +An instrumentation library for modern arm64 macOS. + +[mach_override](https://github.com/rentzsch/mach_override) is no longer maintained and doesn't run on arm64 or modern macOS with tightened security.
+[Microsoft-Detours](https://github.com/microsoft/Detours) is Windows only but supports arm64.
+**mach-detours** is based on Microsoft-Detours, but adapted to macOS (using `mach/mach.h` system functions), and limited to only arm64. + +Tested on M4 Pro running macOS 15.1. + +For technical documentation, see the [documentation overview](docs/overview.md). diff --git a/docs/img/interception-figure-1-control-flow.png b/docs/img/interception-figure-1-control-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..d2a7ac8b323e830a65a647f24f0a21207841adcd GIT binary patch literal 8222 zcmdT}XH-*7w+;{_1cA_nSLsDMQlxi651kNt=mLV$lp-xuK>oJ5!-d9u%UcG|8kDXvsNV3h?3u`(tmoE=2W{tz75mK0B`iUC0)+Larl z@&Fl#xD*zF&NkuiA}3~#pkoaFyW}RZj6=4mpazt9fGz^9I1e0#cbEjjV=+X_Ku+E1y3cVJpdq~3bALG}HZDg3sQT;{f^dsbp38|#UE7J#aL7m&FU?I}PH*-rjOzc}i zMuz3v4sVX%84MXKoNJ^ zKA2tMkN6>b)jX(Q6rZXbSdW3JWLPq7JoJwYpXg~58$~6|e0R;t+N6tHapm6#nM-s# zDI0AdN6xJuO&%*%?vN4YT;_gWr@qxfqF2>?6EAS>Ly%hrS?bzkNE`o`BU#Tt@AB^A zo?3&C5vCKx%t868-Q}yL_1BMnvp|uR6Nb;vZuM-_W7S?mBJX)<2M;U*pt-d7ZEW#S`CD8!O$20ctZj(smyz(2sHH}DmZWRobIL;9f9SRs;xwWe4G{RAnz&kE$(_*d#cgq=3K#e8H8$$IFX7!=Ebqi{vV} zj`Xj>?Q`=H4`*OWs^~P}nbQ_F2$|vU*AI(eSCzmPjmWgjiDvXz_~VLGDrxq~NR?bb z&(ZcEIiqP&o=tUQdcY-2$g`mIG!;7j%xtl2%6*nb#14Y|@KT5>UKAB=CgtR+JG1np zW_AcrrWdgtp+5h`vFUJiG!gu@S9fqdHoW4BHP<=Rr=gBuRpXiG_Z zZOVOrRQ20e6N*pOaU#M(T$PJ=-1;Q;rJBS7+m}JW&5emy2t7IghZnTBfdni3#JEsqm$%A}N1#FSJP2Ng)@Esn{q6}ushY~qOpoImX z2Nq1xQW9V2@0z-zS5+2L)Ipf;d`ZvFtjH7BTJ}q8Hf2-SXr#(QoDMJmE|yFT>f}Lu z7NbzN7#6y|wez(D_Ep>hCZsi*UVn7X^$_Iqt3ldE9oM|6pgd>y0&#Nja#$7wyCseN zMt(b!8Wsn3bq!g6QWz;Gderf}$G4$XO4MMR;Kh!z`~G0dDZp+s#*RUJKg82cGw|80 z3hr5C*mhO(MUQE!2?>n<&ZIPOJR6puOBGcjQnLc-(e8<~)3Ccdx*%*ZBX9I0r_=@`&Jb`Kk-j|7C7XPIR-2cnBV{6D-Q}3iJ85H=%jI-8 z;T{Grnim)`D<&>*x`{RZEL;ZXN*sL6(%3USC!~`f+*{nAz&nme)H^IPg?lJp`d2XK zmizO((f0}^O`f9x7Ao1&ja2L#hEyxN-v)Dri=^pAXr(A-d}@isn3rl6*>@RX5%#(+ zp5jvJmrWCYH8w~fe%NG|ybF7wA+TYl+#9F4n1ahct|+l>gZ5?@k3WC zY3F7d0M9f%J^g;ZJGQ6H<+20ayOM7F$_VW^M0$qz*lx7B-nAED9H^rgx(URX6>)_k zQ%D-cLC>j>0l1IjW)9P)6RF>&JLjiS|B6^5zQ(>NzBRMgd=z+J=4+;Q(Fg0B4&ayOCLHe># zzFQlbowbHZ7{889S)p;=pIQ2Sg%={fpVFo2`7j{P`*iY^x}S{+q#=h&pUSa178*51 z^Bc&;tTm4KcO3e z)k8FTyuS;>>O>HwtK)1E-n%H-ikD?=nv7It48(un$$G5h$K_uC$1@DDH^aPCu1L@M zhS}c>!uYQ?evqjTl38VCN3CjK`2_mV5rAdBWzXwJ5>0D0T^6V@E})u6KL1uugOQ33 z+%vP!n#N%Xlz{T@8@wVp;8IaVwL4dut=>vRU49ZxqVqU!wOw@Cn{^AE( zGE18Hp@y>&sCr}ADPvQgoSL!CIwg8vdEjw)R5y;V(nb}B)xKPelNbv#U0$s1q)0>Np@?0=q64ai3yEL!m@fgCUxW&{9N zjhWR=D`i&(|CZYrstkt4>&+-G&;#D=T!tRvDkYjnVbvHrKj}w^$!S z@xJ`xier7Hy{GYv$!YtR!mGbW${d>xT`q5RUrPk;%QeUcT&;ETBIHdaO(ra!Ia8PDlFHl+*Dq`T3kT~E70AV6A69u@z zYdtS)wp~_+lmJOMOo${N!PNM>28d^B)llZs7Aa9Kh0osXsLv{?P1b)<+3CUg7BSDx z=Q7f1Se>L{LQZ*)DUdsB+%mB0o4=UZZ{EZ41DW)u^-?QM_V;_fADznTs&;y{^&E%f zAZc9d=W86d8ZB3X!(@n@%`2;B$uKL{(@!*Z0u_4*RFHK39cP4vNF0#LL;GG?;fY~_ zFRM=@(Jjcm`O|C4ykP2`+19BHS+J+zWo4#xvaB_ioTh%1QLxx`$aoa|0YNQ`4w0pu zb5mQYT7p^ZS_vX)D1S66)erBE>OrQJ3OCLky>ZlgHYem+pX!H2-Xb|ab-gom0Z zQR2_z;nM+$5ARR;lia)S4wM!ys(a3CAJL&fShJMZA=x%OU6+XWrZkKkB*PGx5AusoM@d{l22%%>bzOJZOaq<_tR% zJL(u(&zismX!H{iVD`s)YbMz}<-m1z{jQ`*&Qtp@#PVJ|ApCT~)w{lptH7^UqhcF} z>dvuBivr?A5ajYE`1$i{#O4l(uXvo5&uv>*68tA@Ud0G=Lb|=mkzvZ|7U_U0U^)yv zvBOu0N~LqNcTFsM)}$wW|K6qlr5M3dY+X^lf71>}biisoA_I)aM=8BNO`n+chO zAT!>cSBr(diKVj^?K!=xEPOd`+G>3EZceUcgyJckPJ;0KyRUURKjjHDc?ne>tRumJ zA$WSS?~ccE=_QI=MXf}Kw^eT&x4K^@_ja4$pttxnUkz60n82t!`!-ufZuVMHWrU0k zyK|yCTcGuoTjU5K|F?{5TB>&0A5#+$KKyQ3}5ooht~MQ^P_pLoWOW<7M@RrGPsNiKRTkOkzy7X6Ut zsx}fLtni}G91n}oN8V-4rKFg#SF|N6biO?nCI*I`tB%BLk>8<}si;&rqrM&jzGdIo>x5+3frc;XX^Eb&2rsu;t zl3(4%GYPqxsr%f_X@iD7uycBIs;dXWC-Aco3QeHnXDT&LFyg@^_Lx8kARc7~Ot?|v zhif~(xdTzhJvvzy00VLtwV{S{93vS5hy)C%!0rL;CejJOT@pV7F)0xMn0zvT#3TXC zM6SdPLJw8}VPaSTt|Ai)u!A=y97X^eg8b`!!3{vKlWvTPDhN=tPV~PO1?u7U=dA)f zw=&yO*2bL!f{0!uSx1{2LdNAOt*#z*LJ*7s*Vv)vRPJKO6v&}G6BW0qtb49 zH2V3S2}8w&3Eks3Plo5L>Q)^ixU5q;pJ3dl}Vn)7)=uCnahHlBWlnt@>a_ zaV_^3i(LEKBLn-)m@($VnZLSwbl#M?v?3K5VmB(Ba2S<8L+0O71~Mc?XGk$eO?euk zzU8cIb;YL8(!&7!!-#>Ic-Diy-M5zVxvL1EifJo%KS6yPd|fc+5xJlp>+n6cihje- zz`Q3`6cu2yImvlo%vXP(157wJRM?$KH_Z_aEt{!Bb@zDRD@=4=e>9wTd+(S>>!<&5 za`f7pC%k(isIWHlQ1;@TitbwO9pTLb%ss6G;~%e;{H!!i6ST!iApKe|y1(%pQ!ri$ ztbNH||Fg&trf1fWIecvT4|#k)vOym*C7`rFbF49t3{5+JT{{Ks^Zb+MGn*5ZbHqrj zT2#(weOh*^VY!!h3dc{2SJ{($_AM!B)(D5E94cp9FFTOr- zLen&anc*4==L>GB9)9{DmE>pf=A?Owb*6R|yqkjddhlh{J>1faW;B7l-5qI>=@Ns^ z32(F+wjs}y35Vl2pAkOC$>e`hUU16zGSjJ{J1!HXhFhw064?BYY$hm9GG~u)e#TVqxcAbx=1bhdlD;mk) z2{rhRywx!!nDN|v8>+mGVR$a3%u3qlNFHAOCXHPT+k3qr|6t*o#K($>GbnPov*B3w z{6}ZA&N8pTsMl7@L#3u5>N#28*36Ge*R=3*W|f;eEBxiY{@bLHZx6#|Zl~qG*RXXt z>X31MRJ<@|*tB0$`A&plR$%5UqIrXMs8XCfu!Ro(K7ogDu=MK#4-FH3`XxD8O~-0) zOxteBkMQ?l3ZVwG@Oah-)KrGknqmp(eHuK=O|y>teQ=L99{$UcVgI7v=h_`s2_}rp zaR^z`2Blk&@jSsIo)^<(4|Lt-O#Uov_;t_~3yH7(YMpQmm4gXE7~v|@FHHWl9Js!3 zy6y6!MkjF2fYA+%$DmblC9zhLHJm@oa7bahEE+Hch7_N{;yu)r@T|1Msy@(1^Y41F zr^g34ouGIQ)N^d9n>hxCJr8M7uC~Z`-Qubwn^$tlZbNt14aVp8GnqNee>dJ7QJ5|G z!fS2tDyc&zFgIR2L$o+iF5dU+47~24Qcx0^yEgQX!n2;mMQ85!#zm&|l3o)b_VfeI zHIjvkNt|&eKkprSuoOIt6^px;Nm`LJ(98SCCSp@VmRn5nhW9t=lm@5J4ycT80|*{p z^f_^Z|7*PXlC<4`cow>^)R~}aMI94*P!4y_Ul)*~gUhw-B4ZJ2p8q_By!aY;F=VZ( znf;=QPnrUGFr zxJ=!;47sWGYZ9iyp6ibZK8@KdkGFchK2Z{VG*`!9@4l_*P#F*>N;q72Do~|eI?<|+ z*OkA^mvW;`O(O%;wjzh$^r7RRWtNr@=*aWYkyqtV+_XyyuRxFzW(GqcERw%CSGDOto8Hi&T`SZ!wtKN3H|$iOiLc~nZ-+QRNRh@J*FS(ih5}Y;y1hX z<1IwB3KBO>-x%B^mnGbam)SkVz+Zh}`iJAuxqmAo!;~JKD3L+c4kd>~oMcE#9pQLH zK8WjXI#aE#jaLH%2^9@D)-rKZ72~&3SR7lax~KIC)@^e{%m0b9I`AOCub0UnKx%b)haIR|hwMZ-KvWpC%(I)MY7t<9a6^0_ns-iFr z{={d+FC?oU&E+B8B&mxZ-`?GAo-3_mHTiSI{PfYQ60UkCQgycIN*kSvcmULTT#;!0 zr@~bU;<|I+!c#{&-{}_rIL{}suXyZwfjHEDKK!F zl!n65us_XxWbC?@&I)WZ#MFrre?tU2%G6KzubIqH6^x?&_p;F|hL<+E#9@YZ*iD{F zt%1wELxS&3Xi++(c3#Ajy4%!%_g&80DYOKe8Iy_QJE2tePZ5`1cUt|d7FZ42WVhgJ zyT)WS^tQtyp2=(AUfi0-`;K{w(WK5ZSyK0qcg}Vp8P^#;oYccC&+sOz@P!zi^|@CE#I?SpJh;U|Ps(rv=((k(9t_W=&!XhR zx^@-lw5sFQNr*Y@)=CU1(pa<)5<1$ZyXmo}_f&A#!hM(4xnl3topAA)NDn>HoiTP8 zPD->-Svr7>-0PwpzQ*4;e?q1xolyVj$25P(J7Xt3 z*%4YEZPN0ovv9!A5giAQ02~0RDv^`jORKEqSVnkP6!7C__LEcL|4MeBk^f3|X+n0n zRR5Li2LChJy$=kPOTrwbey6?HnyIZf{6{nS!7J}MHpARb^XT=x-wV4%Ud@s#NgeC& z2P-qGi$9Xta(*&j#aH{h(?-@fBSeO68@QKvP|7`tRSG63ieGMXK|WY-_);&to0>(oX6i9gJSr;ca8Ku z9P^qlXprzWN9J~)-8+d5rk9~a&t0J&8cZPLHKp1?3*2VyXa0Z;2P>>1k4vfG+S3g` z$A(Q?G*I8 z$rA7UvK{j%rWJ6=d^te41K)X)LQNP31}&5mR=~%wsJq!^&5*5 z4ofupPrl|rHm=oj3sLqd^@M`!oq5%S3)uoWQ(IbvkPmeGMe9|xIRtrO)>}{h^PTxH z)>|+|xqd~Lyr0nUY+P2=<1iXMhMn+_a!lWvALlkFz^YuW}Vx*>yH1H`)e6dD6(0!6#W{M6c|HkXN%~J zL4r6?Fs!|oY_(J>YeMOb*b&F|1lhpq`G$D0nw|HEsf?VlxM_2UBIKK zP1H!{7$qJpR=vRL5OQJ@u_WKV6itd3_y;xggg3qQCs^wi&ByEI zfluyi?EYARVIOUcOS`as$uzz7oBvG#^L4)-+eKh4i_QJmzRkxqpH_7SQ#j_^qwa=3 zE-Cg(X+d~BmhJurEa+c+U38RY-lMnA>{gfhCgEqzS1@6&f{9!aL z!ajQObZL*1eJ&;jDsxOK<~mo81U?H-Qpx`<0=4D?ehbA)7&$|Dt^w2#EU3xH2?bc< z-wp|_KENg&0b1Gpj-&vfk!YZKhaC@m++qjDkB&y_$q#=vsoEap85|xaSkvwEUf-sU4 z2?7$uy~D3|w`#ZkRqxh**e`Dt6ve&W=XRey;W6wgMJbLlVKY4K@}QzrVV&f}y|FuY#|>d|b84HUIvV)L0hG zz2Jp`6_=mN=dXXz-w|eG9)8g=5qfiTb@1st39J?dR(qlZk_U~4vcf>X4-NGEd0&1u z6q1YuN$!@f6IVn0XAm58e@FJJ_bp~d7>CmY@EQxUrxC`nV=3L&2|O+2Kry0dZt(F9 z+FxO{QoC{IeCX7|qCUq#s4h=MgI|C{#JF2YTEKME@!)2*u>Cj}f+x0qB;EFuEu9hf zYW#DsurE!{KH=8>#eI|OIQOLGfyZ9!rt3rBEiVmtiVSIG>MQL+JEF+vjfVRp^ewbujA8e+?(% zgnL88%F3m)@lIrszVV{FbHdU1TSFBKH*UMRr6$1|u*E(1#dl`41(QGA@jsU_k@{gz zb#Q|lOqG+AQ%A(O5|n-AW>`;|3>%>vhrutFviYC3Ef3Fw3gp}zieLjmGSZ8?dY1Xi zO*m>Vm0S15?EN%-9RoLW;?mFAuk`ltOM?XR*axN}bjY0S<$AWb9d?7beYjxh7othv z#`+JB;@}0T3*i0TA!gKm24k9uQGRM~bkri}WhGNaoPkmetYXP>#NdS*5OmF6pu~mcAg?72ZHnHy=w%J(bm%cd! zMQTAHncvpQSojG{vUn#3IUUb@Z1TZ*3Ck+xA=qSiOBrFj)ijH&hB;Qe_dbV(@f`1R zqO>b;-B&qL|H2dl;~6-sdlC6^jp$0PW;xo+0tNwXJqi_zbXgQD>hrkzx^7nvQ@AS? zdC;p87>|;Ki`+Qq9f_9~{i5yGh4OFZtptz@<<5FR>_O`WK_{6mi?TrgV1W zm5JR!F2c~6Y&yr~pl}=xVle3-b3u|I^V)m@;k1$M%ZkA_9St#W-y`tZg!O!5D-~Ij zw+v46`It1>ySXmjvfuS>8XD6?W=%T!NHq1`o2U?Ox!lXBw`(!Vw~=aTXmxCHX}752 zC%0Yqido;I-kbP9f%qd^=uM$(Nu=R2M@$$Eb-(X4v5nH1y3hiRgJeY{vmW1fqKq#o z@Q0l2$gKSJl3Phx{eGx}sQt*$S{3Ct`#T-xC3irz1Tk9XLXmX~MG7D)z)O^LU6xv5 zpkNO#IClalSidFvCokZurlr)6*kFjQuwNYj$;! z*1u$a&IA8SGMJMfVQy16>@5ew{e!luQZ{S-q(*Eif8T9jsrx6(#3j?Trq7lzBd2k=+lLpxWC0$4s%;YnoRcrUk-|*``MWV#FW)ldLwH7 zj@C^*tZZJ09cH@}=C@&I`HdjE0*DUGiK07folU|529;U+o+yK!1A`Xa9Ss$37nl5B zEDrum&RzvK;kbv77gcsK8`IQ@ta7|FuZZ4pam$1J zyJ^XmwQewyZwh3sj@5)5JJY7Z@t)ajd+W76c#$o$75u08#VSBVg?li*q#6VCu_E# zu-O{&VjhJ8@~4de5Anh>|(3eOmbFH~MbM zV~4^%cRrr2eoeE1wAiG zs*IKMW3RVD*1&_L(NA5i!0zjPe3qW*p!O%FWHg9VkjrRZ4L=LUlLM%kGmja8fDxg+ z)?jDh>oH3A(@uvKr5FZUQGDIphxZNeO(MU)s08V%Dt@ z6eeCfHjkeL@-mDZz2WfodB=|$0hbe`AOz@MDPL$9FScs`epjhHlUU>M#>qSR_p=C9 zsxdOBlrH9eM$)nwG5vEK66zlWF2j3m)!d0!R9~<%qq(JP)2>!oqhdphju%=UtDm8% z)jE&_?SiUOz)GnDI`=}*PI#`dMP}Y<-Zs@ni4AH$K3Ve9Ng>$u2 zm#R)Y=7lYV{U+Ykc~q3Ekr_3TSqc*$-<}7plzuMr=<(R0AKRvsLHy-wA$Jn+-o}5g zh^#JU<8qt+`QtBR&C-av{MBqKUsL;83QPLC$8|kC7dafLGM(6ij9M+m%C*%DXu2fL zO0V=a%S)|gde!@@+$fQ&RndR3hp)B+EeGVzg}f}zG;1Q^!Dn1UAD-DR8lJ8i1t(H{ zn%$N`+>|r9G@Z*)#ja%kj?Y!*?kp@@=f%d&!(!Mg*dh9#+qDweGKpG=h_7_oZU>fS>O=ooYWu@NZhxYz}X+Um5uDf|mT`Ao`pgwFVnTpWMt6#N-*8So_c$?FBR%K^29$Th12|NSGCcQ@1 z9_h@9-NH*87+A5Bzgq-WB?GiqLhz;M6F)Tz*gkUd1M5+o`{)O9At}fS0Eo^+u8+TR znObAs!Es6G_z!`%iQcX0;ATb`V&A3j`t05@%O7FTJQ_GUkd3n<$Q+pFwo>SqdW&Xa z=y|#_fS5TyL2pcn`K%3IHa~$D(Y#Dy;RS2x<`BDsgG34U-j}o&4bF zQ_nS3?O@+&K1tpvvOoJ5-kDw_o*3&kc;;Bhs>=*md_;US>xL}*AM^9+=Ry8~^vwO_#I9cnKZS-MfvEwQ_ zN{|ie&RSmw8>$oNuSt3vj+X8`7bUfVE6a1X>&l{~|Lg{u00KvU7B_VNguLKGl_{M` zAdD{w3+uQTnq?!r`uJRO+{qz(N>2)5ECTN6v_}5eU1^f6;F?Y`(hC2-E~j#Ya%^Os z!L6HIA4K3#PPzF>g=3fM!!1VH2e=nx^8Idf8%DiG!%fBQFZxAm&gWuU>+GyQQwaw= zgJ4d{0LyeQ1H4s$tm_|o(|LPNft#xGto3m~;w29l{Ht;cF=H_hQv|$p^tW%fH7E4Q zlIGglxESUYx5@;lYpr-#V;t-b*!uXeI}_vukYBshS*+l9fg}FyQ~vf}5O6fdFa1@p zG10&z%!U+>!Qd-5!Ev`3yh6%B5hMm+aGI7eZv02Y8u|3Di)oVv&tG|e^)2ECigqEq zicZH7kHA)YxE%al!7I@lrTQEXVwasN(BFu>6T$H;tlidh;aMUni!6 z%@&D9*hiuhTcT67e7wmSFZXMjt5|=Yv*t@|zmuFK*+G5wPi>SR>dhi*xJl5oe#%i+ zG~@Zv%mV84Ywh75iLM_L#5jSt1LIb)Po0gAAbMtLF$SlIIlZ?w;JF}F6x%FaQ~&5bgxW|=lB_qMiHOKLqfz(I9~&$ z@%l-1rfJ0cwhgcgiPM0i5mM99+#)Jt=0p*Jb3pyhLEr`A7yy|Tr7hk{k7}f?*QT{c z33pezz!fGRW|Klp?RhWyi7B*mE7M_!k1r?8Yrg)ZJMm)`qUE;$n}xd9>~~kYzyGtv z^)laTu8|vcl${aH_p+aiz{R$DvRZ^FN zrrPNW*-Mv-9d(tqTTj!36~cWd;{-It7KK!MWhMTVrEV7>AqC+3AbyZ6La=Z<(vE5) z5HQ%E=0U26*2DEo7H(^rU}cq)HW46{SOzvVh)`~cvZ5SVVXGaHDI8(W2j4=}!`ekx)D{Y;94b&O+2 zmEf}J5V!0NZ-4yo-PaZlC*PT_dpohKASNXaBM!QC2>9gEhrWw6$?c|!VDonX&iuq> z5$ox45*0;^pK#pIhg%lcYFYUGH<(1@eHHX^mB-w7?}!g9tzv;3?Sxrq8IRE@dw#fQ z0El3H?S3GDD{*TNdCCw-e~M9z$?tq3)e81)e&j)A9VZ_4lBj?!zm=Bq{xWnOdC&2( z7OQCTqL1qjXq#;J=0;2eEG50upjy$G_pwLKx~8n7jG=|<=L=hB$)cP(_mc(BOk7~i z5GV>)J(7T7=D!|WNL63$9~%YId0e!g$Y1gsF6{yyP7KA zdwrs9vb|FehhuQN=t-Hl-&~H@+THe@tl#(e{3sLA(N!Eam|big%kpJhI^|6c_i?G3 z81{3>xI@K6^EgmPEH;6nL@aC}v1^k3HPa>}i^ZZO0xI_}cmmpaVvFO@ncq|Xydt2` zCrr-{Lxi+mNM|GhDY?xccq|Ic3yh(7W`WkZP295TQmlw!;87Yv#G>$4j_Loc9NIO2 z%u&MvNFi1ADOb)r*B0^SMB3RC>Q6vGbq4-?Pw$qWF-_7SfPTrt!d@ztUkhy%mY9~8 z>hGAs#^@j+KmYm0j?~>#-$w{v#u2WAm{?!~i$!y%^b+lPE!m*1fCwvfVoY8l_kDY8 z1-SXkmoE+NF=p}T&XY#gWF~-;di>&+Sn+(_n+=9|1IqI2M(fMpLfMeTDKf}gV31W` zfgbj)N)_1=GZ0-Q{NNkg1qK{hg-6@V0EE|H_hUb9It&u6xXdPiJO<8zZ?<)?0Hdt3 zm#N-rpfq3_$6d!(nOixH|F?1^K1OWaOU44&w1m_*E4rZbHmVldT)~30ZZNP(b6UOx9W-10#yL1y=b*Z^EW27cY+B={V_pree1wb3L{77 zREQE<59rjq(j!6ocH?KPyUAd7U>Y1}!ad+tj-B?coD#b0si;GNj;)Tk_L3Dhkc8Co zZ?3`oNuIA=`nnq_)rkx2C*%Z7z9o}>R0d%L73O5amwzBDfqW=!Z{N>3W8AELUq)^A($ywMp ze!73?_eapNE}1|k{tly2x~(QrwUMf2d$Yz5j@wNS_=BS*0=tQ{LktiU&+_QsH$l0^ zdh6pD8pUxulHu}VnqMQP!X}Nf_0-=ff&R=d=| z;`5J6foY>vX z#jkBzC9o`wYS+TOULAz1tU?THPr}L1SLo2^hupb^%;ffq0nrwoeH<0fs;CSXHVXJw zW(Uq}B}fqy6BTkbi)nF{Yz4LR7oh=imeI@KN==c$NgTT(vnF?czaUQvnTM+xStR^* zjA?yemCiY*`KD4oCz@il0i30?;+b|aMpQx7Ie(!w^mxb92e0Yt4>Yy4{_?8O3tS@` zQl~ZavDRZ5&{*lp{ZT!nWRdoF=S~rX17fu;`8IFR#^)B!7C+ThQdCO`nSUYtLUdjl(45OI(x1aPp-Q&lX$ zd+(@;1|$Nu+?IJ@Du^98`BAXu1EzGj5V`yW)&LdU*xu#ejMV&}9$;c8esxBfl|C-4 z2D_!YUnn30`6q73mno`5AW$mRFp%uMUkQntp*)?}S2Q+F?9lT(&^VJ5W4%Re3i!c~ zVlK#@HsD`=p<<&%Q$yM>#8`&B)w%(E?Na<_FIT(Y1dd+ZuRpv3R(J4ow>z$xd(Q6xw^3Yab8<&?9MS6^(#Fz1MLZfZK!ihQutUGoY=r&e%!b8priO{E&F5;t7Nu|#(BT{X3@-hJeEOksc&cSG^)V&cQc-b9~OO@P#Q zb4#kehY6>gSX!B2VL&SF>;L0&4+urObe*iBm{6pNs*jI>$Z(?;+MSn?LTV0ErAtcf0%?sGCaP+32kCHJKzumQk9D*(;ow{nKgHPL z;{=mZJkf2DQ2eW(3t~fhUmc#j4>dQAsr4k=IpC-BRlC*;;u_m1QC?p9rVv0s%Rt@~ zp?~^kGi|Fbl%Yl5T;-m^wFTjs%d#?aIf6#~jIhDub7UYJcwm{PxtaMm9{KT&#df1=k>a zq|ijHEqTGe!y%M^-~Dxe$C|~CNZQ|P&|2f zGB86;Cqf|`Cyq!b2?dimg+;8R*nV*(m8vHE*G1UN*zJajw3S}g2pX6GVOxovOCkAA z`2_db0xGMTD3N5RoLl2%4oFtPD^GEZ#L3=@80v&w6C!F5{n5KBa9)JU&B zeCg=yLVk*eXddUN{LH*wj@X8@tEWUAfe@}z3Ou+Je|ga_!*#lhN*sx~Jbt~CQ@B5} z^++dlWU)HXWamNu+9&5HX*8DS-P?J*WvQ^NWM*{nA=tpE@G=G54~|L=9 zyrZ!jN{(nIJNeGrc8C40H5JjpLs!Z6{9s{fORm3l%mCexRY+ozV!K=nc5mq8d&FT8 z=qxD8Um&mY#2OJ&O~^|D_-m@+e{pH_zuk;AVdr*?PM68f-}^>R)SLc2<4iSyw(e)H{chT_u-0_pc3oSMf`}HV|e@ z)etTNo&MUcQNGOPs?-!jlZByo2F=+u8AacxH@y@-sQlim!2$6$dcG0-hEtEDsv7os z3ND{%SeNbv%n(k)$DhB(syLbcBue|S! zaX);0Bvw36AiCWbMTYm5(y76x#$X48EfcjHXbGk31hXZa8`c~c9n$OaEj~%kfGDq2 zlXF?tZY@eN1(KIhqh?_26?$!FnT;%Hm_?k^{cbiK)-D*n{!ec_nuHw&o{cK^h8!3mWJ~A%B4+dH{ z$ig{bqZeRf{1b8gHP$luNc7_u^TeF~ny2EWdK&CjdE9!P)!q*~YGy#EtPvKOJ5ikP z3Y)Ow?DSpBa9$mp%9Yz`bPe+LIKSc${AodLSnzm>OOUPX53i~a%0r&)^gdV!Kg{-QwAO^=T%E7En|b-pgPAeJf|phVDjpvmlkBDjoD!t;A1& z3{xtcNwt)ho(D zh78SJMb^NygjDsn@d}&~Ud5vl-TM9M5%seMF4dPFPw`LCck4AT#O8J;Rqh2_!I{U4t_n!NkS>H46V9mL?o_pmkRLYp4_*| zj@C>X?kPVROQGLRDMxKh&Jdd>cN2k0hN&990wKOj+X12TVYRz$x8Z3_fHWI!Arihj zyBV<0g*ti-I`Y~jJZP2MJrIqY*bs;!|6LY)sdfC9PiA3-#qc(E6s4B z{*rhk^kJ|~-`x;Z->NJ^&z|#x*X)n1y;VRCp!3>6fXlRKP6h?LDg}hgwfLFXV@lxw za>nOEi{doKj;B6peRE_|%i*A4|GRGL3>XYVA@&w+33#C2%TGp|7>%2gf3{QWh5>Lb zY>@5dKSfhh=_6=10G(vB#7oF;30J5XBp4_}Z{ZUl3PVqX?mxmj}63bbzahHwA(U|SCR zH1qQv&j4UY%J@VAxQ+jv2&Y%<7f?U@w4_U$$sEVF&&8DN{1mWIu@bZ*{MKlngjDSf z5~#Eu3jLM4Q9XmOvyVmoJ9##YGz{k>tj$#i_K+s_6u4sn0AYBmK}_8JOt2a0%mF7M zyMgdb+F0~wraW!Ktadyi+{&f(URl+KG65rZ56rSBT=m z8fDfhZ$$X{=C{-){!0z=eLn&c-tkMoW3Q;EBkta@=ylf=d^c(1AA{_)inYF9dO+K& zxUO+@&mlWd=V6QCK&zcuT16KEU=y$sG_U`&7t?lWFdFCrs|I4Rk+NvE!Y%7E_H~7c zD$9XY7l87$%x`+`fm$^Hr&>)sU`Td<&xBwHPK@0`l>U}6_cW|M(F8RCxZE+tfi~lo zAW8ZEK#=_OVrn^+b(#cjs3+oia7em0@3zn;vwzpW9>;s{&^dLpJSZX|0K&fPEVRrK z;%8{xRMdUM+LlbL6ag%jkux`eEsW=ZtEAY|xhqn2{_lM#;Qm__FV5j9{IWu+4 z0_mc4EQJ71Bg0c-rj4x9D*;m;qP{W`DIJH&7Jjt|`j%ptyc||XIwrV5<}*iJJ9Td< zJhYp4(G%NVt@DL6Ev`1NCZirBZQz^Qtxhs=SinlyvTJJXkuVlipLGwo zC!_~wx_-U&jd1F0Y%|G+SraQyH2(e`F`;#sHRmgZxy>c|rS$*=1K4@v0Qpo?z!W!4 zYb}kluG9d%C+_cLRtre6QYrKZCByyUQzHbpw+28pwt+k{W6haf znx}P=PXp>ac9%hJ=^@XZIeHqm2AJ#>-EQY&r zgu}&L1IVUyeJ=a1g-0bsBT2tO?~K zNC_Q`5Q#Ohe}~$z^JWFPkk4|ci?1{r1BSkAa#c8v41C4*@phAq^smeK}NhDe_| zSYqil_68Bt6B z-38<#N5xIpYxKokp7WW-{#@EGFlKFB{B4L^#DLJOfuW^r)z~}0DNj{!H7iyQWNOmy zT0ysMv)oOK%De(I;Kp`7wn#YYtuxd=+_hH)t#=#B3ES|g@pCh7NDR>(qM|j2H%$E+ z*lap16C75l+WNfl#TmWW58(uEglA=}R1PWJg-rqYS1*!IV4x00gZ5x<6z^T<$9-(9 z-*m8mTs)m@2ebe@RT4hVS~i&qivCC%5dLRV-AxtLD*7!m+Aud?oSG&z=hOF(aa$rM z32KP{`BII0?$Phmw7nT2s0R;?*M+%}hllX}o!5CU^_?-YbIJN)zvmpvzzkwYr7sPnp-d*EaKdQpqO7~^yBV$+`up)26H8e|% zwO$0sHxnxXoZ=W@7jxJ!s|lnQ0hc{z`&*iHFzg~V3}BA7wElm}_&<~}QUMt%hVw6_ zTLSd$hBgaJHVtT{oXc?|*{K$Ifa%%Bf=B=qn{;m}50vo^^bk0NT8r;JGI%m|pNiApIl??#5vnz-54Q ik#(M?{NF!tXuY*H*NH7}J^}QkW2vj?DAy`J3IA`I;8$({ literal 0 HcmV?d00001 diff --git a/docs/interception.md b/docs/interception.md new file mode 100644 index 0000000..0efb124 --- /dev/null +++ b/docs/interception.md @@ -0,0 +1,32 @@ +# Interception of Binary Functions + +The mach-detours library enables interception of function calls. Interception code is applied dynamically at runtime. mach-detours replaces the first few instructions of the *target function* with an unconditional jump to the user-provided *detour function*. Instructions from the target function are preserved in a *trampoline function*. The trampoline consists of the instructions removed from the target function and an unconditional branch to the remainder of the target function. + +When execution reaches the target function, control jumps directly to the user-supplied detour function. The detour function performs whatever interception *preprocessing* is appropriate. The detour function can return control to the *source* function or it can call the trampoline function, which invokes the target function without interception. When the target function completes, it returns control to the detour function. The detour function performs appropriate *postprocessing* and returns control to the source function. **Figure 1** shows the logical flow of control for function invocation with and without interception. + +------------------------------------------------------------------------ + +
+ +
Figure 1. Control flow of invocation without and with a detour in place.
+
+ +------------------------------------------------------------------------ + +The mach-detours library intercepts target functions by rewriting their in-process binary image. For each target function, mach-detours actually rewrites two functions, the target function and the matching trampoline function, and one function pointer, the target pointer. The trampoline function is allocated dynamically by mach-detours. The trampoline contains the initial instructions from the target function and a jump to the remainder of the target function. + +The *target pointer* is initialized by the user to point to the target function. After a detour is attached to the target function, the target pointer is modified to point to the trampoline function. After the detour is detached from the target function, the target pointer is restored to point to the original target function. + +------------------------------------------------------------------------ + +
+ +
Figure 2. Trampoline and target functions, before (on the left) and after (on the right) insertion of the detour.
+
+ +------------------------------------------------------------------------ + +**Figure 2** shows the insertion of a detour. To detour a target function, mach-detours first allocates memory for the dynamic trampoline function and then enables write access to both the target and the trampoline. (Note: Making the memory containing these functions writable means it also has to be non-executable under modern macOS. This poses some restrictions on the code that can be run while the detour is being inserted. Often the user would want to suspend other threads, mach-detours provides APIs for that.) + +Starting with the first instruction, mach-detours copies instructions from the target to the trampoline until enough bytes have been copied to insert an unconditional jump to the detour. If the target function is too short, mach-detours aborts and returns an error code.
+To copy instructions, mach-detours uses a simple table-driven disassembler. mach-detours adds a jump instruction from the end of the trampoline to the first non-copied instruction of the target function. mach-detours writes an unconditional jump instruction to the detour function as the first instruction of the target function. To finish, mach-detours restores the original page permissions on both the target and trampoline functions. diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000..bc26a9e --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,9 @@ +# mach-detours Overview + +**mach-detours** is a library for intercepting binary functions on arm64 machines. Detours is most commonly used to intercept system API calls within an application, such as to add debugging instrumentation. Interception code is applied dynamically at runtime. mach-detours replaces the first few instructions of the _target function_ with an unconditional jump to the user-provided _detour function_. Instructions from the target function are placed in a _trampoline_. The address of the trampoline is placed in a _target pointer_. The detour function can either replace the target function or extend its semantics by invoking the target function as a subroutine through the target pointer to the trampoline. + +Detours are inserted at execution time. The code of the target function is modified in memory, not on disk, thus enabling interception of binary functions at a very fine granularity. For example, the procedures in a shared library can be detoured in one execution of an application, while the original procedures are not detoured in another execution running at the same time. Unlike dyld interposing or symbol rebinding, the interception techniques used in the mach-detours library are guaranteed to work regardless of the method used by application or system code to locate the target function. + +This technical overview of Detours is divided into two sections: +* [Interception of Binary Functions](interception.md) +* [Using mach-detours](using.md) diff --git a/docs/using.md b/docs/using.md new file mode 100644 index 0000000..2a59852 --- /dev/null +++ b/docs/using.md @@ -0,0 +1,64 @@ +# Using mach-detours + +Two things are necessary in order to detour a target function: a target pointer containing the address of the target function and a detour function. For proper interception the target function, detour function, and the target pointer must have exactly the same call signature. + +The code fragment in Figure 5 illustrates the usage of the mach-detours library. User code must include the `mach_detours.h` header file and link with the `mach_detours` library. + +------------------------------------------------------------------------ + +```cpp +#include +#include + +#include + +char* (*real_strerror)(int errno) = strerror; + +static int counter = 0; + +char* my_strerror(const int errno) +{ + counter++; + return real_strerror(errno); +} + +int main(int argc, const char* argv[]) +{ + counter = 0; + strerror(0); + assert(counter == 0); + + detour_transaction_begin(); + detour_attach((detour_func_t*)&real_strerror, (detour_func_t)my_strerror); + detour_transaction_commit(); + + assert(counter == 0); + strerror(0); + assert(counter == 1); + + detour_transaction_begin(); + detour_detach((detour_func_t*)&real_strerror, (detour_func_t)my_strerror); + detour_transaction_commit(); + + assert(counter == 1); + strerror(0); + assert(counter == 1); + + return 0; +} +``` +###### Figure 5. [Simple example](../sample/main.c) detour to modify the strerror API. + +------------------------------------------------------------------------ + +Interception of the target function is enabled by invoking the `detour_attach` API within a detour transaction. A detour transaction is marked by calls to the `detour_transaction_begin` API and the `detour_transaction_commit` API. The `detour_attach` API takes two arguments: the address of the target pointer and the pointer to the detour function. The target function is not given as an argument because it must already be stored in the target pointer. + +In a multi-threaded application, `detour_manage_thread` API enlists threads in the transaction so that they are suspended while the transaction is being set up and so that their instruction pointers are appropriately updated when the transaction commits. + +The `detour_attach` API allocates and prepares a trampoline for calling the target function. When the detour transaction commits, the target function and trampoline are rewritten and the target pointer is updated to point to the trampoline function. + +Once a target function has been detoured, any call to the target function will be re-routed through the detour function. It is the responsibility of the detour function to copy arguments when invoking the target function through the trampoline. This is intuitive as the target function becomes simply a subroutine callable by the detour function. + +Interception of a target function is removed by calling the `detour_detach` API within a detour transaction. Like the `detour_attach` API, the `detour_detach` API takes two arguments: the address of the target pointer and the pointer to the detour function. When the detour transaction commits, the target function is rewritten and restored to its original code, the trampoline function is deleted, and the target pointer is restored to point to the original target function. + +In cases where detour functions need to inserted into an existing application without source code access, the detour functions should be packaged in a dylib. The dylib can be inserted into a new process using `DYLD_INSERT_LIBRARIES` or the mach task port API. diff --git a/sample/main.c b/sample/main.c index 88a7d94..d59b062 100644 --- a/sample/main.c +++ b/sample/main.c @@ -3,7 +3,7 @@ #include #include -#include "mach_detours.h" +#include char* (*real_strerror)(int errno) = strerror; diff --git a/src/mach_detours.c b/src/mach_detours.c index 6999286..68b75e3 100644 --- a/src/mach_detours.c +++ b/src/mach_detours.c @@ -804,8 +804,6 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour UNUSED_VARIABLE(trampoline_code); const mach_port_t port = mach_task_self(); - //const mach_vm_address_t page_addr = internal_detour_round_down_to_page((uintptr_t)target); - vm_region_submap_short_info_data_64_t region_info; { mach_vm_address_t region_addr = (mach_vm_address_t)target;