iP866<0X*t$g~<8j~x%%W2Bo>k_-Vo&j;tL!+{#4?G2~
zY1&=)j}vugtAtUY_{XW8!muWX-g;tcoiMQO$i=#IZJyek^nZyTn(lA&6;=kyO)YC6
zL>Eyao=TuyPZS9XuRlY~?^rQ_m_Jh!?@Ip`%wKSc-v+LwcvJng5Mc*V!!n(b_j_4_C6x;3X$gZ4)EZ4C!Z
z^0+XUX~jfEFy8aZ)0=7pHE
z?K_j%@jBT2fY;A4^S(I=-pAU9eA)2&VY)-1N!(+oyWJf}avrSGpk!HI3{ss46$NH1o{q#?`@wjX0Y(Wf)MBhGKEiYbZC
z2i4%P?UkJg2t9-W@|aLYxWiByc;y9OxgY%C-oy!yTL&_pT?78`>1VeMym<8eVX?Me
zf4gd-``PN%PaGa^Osa}m>=`?2FLOJMGvn3S}EVe2GL
zZDo1l@=_RMGYsW~_BU82^epx`S{K^aproGNqy@lJX#auH)Uy~{jNj6GCnqy7?$=__
zN6%s}gYL1b<60;NpU{sw3kfO1kQNzOdPe4qlOYP{Mu}qpj-Ii)a&JhII2fL2CNZSv
z!e&d)V&8@CXl=b$&qy5;|G3=J~EcRrmv86DtOO;wKNf8W_7}Bf75Gm}}
zw5}N*HKU^*2c6x`@0>N6m8I_dg{7r9;k0@xR+LqgT6$4cA>yCxaz%rT65Mam=nU3^Ncj8eC|(!e=a1zrmynb={0(o&f)oVK_35Sp<{X
z5QmLlxT|Aq{?KxW{;2N(sM?dU%7eq7?#4Tadj~wrusVW+AxF2kw}8JFFilbbu--U*sB=E3>Uhd^kF_-2^i}oaPt9|2pFG)!+P42-FE=9N5cJ--GhMXyaa9^*f|E6
zH>AdFdre&d1&h!}VnMOTc}13AaMgz#253aUl^_@
zyH^5kl!R5{8@ji8m_OKa`2Zhecj~;#^bFpTuP~SKSLx6DWNnZ-!ZoGs(3&*jq!BH*T6LLatk
zH(jQ_QovP(>+7aG|0+yhcjXGx<=0`jp31>Sz`b}GcK3$q>&fnWVYr^`9s%4pmti-4
zq?{AdFSg^=+V?-$@AVGD*~~UuI1c;ega{nGPYTnA-Q=(cT%+Z|I;hgXO%fMlO^yFpWU}(U+bs0Du9v-?39NIm388{ry#g2koi0dwYaTs*X
zW#DjJH@!Pt$jk~&)#=~`R2U8i@Bfkag%3ag=KY)B7buxH5dh
z0Z$??;tLLj+rq%mdPfu)55jYhB79Nk$%`F)gI`DuWQ)DAVH620eP^|;Ii7KeZLVdz
z(j+~p2P8=%;xVN90~07*@8^9)sgFoYnp*i~{xn|S7Hstq)x7uZG3_|W$A-ZGFG1;)
z%fR5c7I{WyEZir`Wjf1*`+g^`np)N$!ynIOnvRF1>R4&
zbV=}1%Ecy8J>o!2_KQVV5vi%5SIW
z=hMdseKD#0m{cd;NEvF7;engmo
zKBqss>X5fRIuP|HG>Oc55-?sYxALGC6AF_lpC%JF)}?S6aKtK=itB*F`eR_G!<{CR
zOQG%ssst_r^Lx0{1bYf7noNHI3SV#-$QKGot8!}se5IHR1;`?{ir?|eK7zVWX12W8
zziFn07mxAHtIyZ{gBA3=_^qj>)dj`GR=BRUY>lsTkvhQTh{tu|sBY=9+dhTe1F)wB2^FYcs|b&D+*Y;|9UX0}JI@D$@a4!1No*I7IZ{uh9&q`R;UkgDcZbm-b~a}l
zNV($0Gradq&{W?6TDj)hA&?+^bAi9w8Z!
z*yr^3Wg5kq@sOiqe1@iACVU!_f(aQ*LfQb|N9F_}-g0WXCa0R}tD$~z;zj%9nt0Pa
zZg_z5lX^hhXBQ2P&Dpm|K@(3v&uFTjLj0v7fd*_l?A9t4Uf1yAdlZVWIDpaW&v`Ed
z1q0Wd!@`FS&gA%8`hJr)0P%N;g=yHMo=6+N+b*7!qREMG!zU0&!xut=_Y5nf<`h4dKiMp3$8bXU`vxAs_Q(mBfwVT+gjxaj8wurwJ0c7WmP6XZ
zJqHvWIbb!Tyib6d0+)foSFSS&H3X>Z36%wujZpJ};s|vIP?>~U3ly!&e^#gpD7|*#
z`%9px7OA_C`%dUNPt!^ycN2dA0dZhF-VftvwflgQavnZ{S(@06qbckr9qmh7)6zew
zJ|4J(_NBLTTfh$O2#8XuCooOZk3!Lau?W}X?>Ne(_&4ceG9B5}e
zewS|zd}(&bpEynR7!hLA1^qkP-GFE@$KRB1;eE!sBk;jD=Of<78{xMDe$7oeoh~4u
z1B~0>m~RmsUvQ~5D^y%A*#HBK3jt6^{n6$Aciz$
zv0S5VSYRk=opYe3ZPSC*fJ2N%TvL_-yocmKOVTUH7jF$jZNp@32nt9VfrJnHB+P;S
z#3vBHEe7SIwvi%064cdPWGxYQsCAUq&!XZ5?U?VhEF=O&%K~;kMQ|C&H#ACN-$%>G
zZ3=ZaP{=nh8{xiyP@Ca?E1^muk8ntDV6fUrWx;zqjrW#XEi{>-$4hHRt@#9}mK4%?
zI>ob2$Tjg3sp*FD9(~US6bM#`(!4xjmy{
zyqtO8&@Y@y5ltDnzOc@E(4R9Hl2?`He
ztD6Wf8}2lTL&Cm1(HU_2~-Pm_yA#8h7gDx`{b9xB>kxuU^zij03D
zz%c$jSAFuY20#XG_6BkMyMteLH0_jqtVJhGl&22jheR{>$kYVRC&UwkZaC$+TG8wp
zOBbm~IJqrTQi-%>FdPmU2M+<;G`+3?il)~@plE@`;!LSppi1C^A_;fec!@wQB)kXV
zPH~R`MT_p!Ko!GfV6a)CDeOHPy>_{<*n2i8%!N5Yw)ZS6O!J=A2}$0wdf^)HS%Wah
zdp3%1CbMV0ISM3kDBRXA(Lejw+Z7`?yk&qHUe)0(X{dZRqHqi);YKgOfua2
zL0L4Aq3mhGbHM!vLgm2yB%uo7{tcn9vIhu-l>sx+z$}OR1w!2qcd8HPqb8ymn}~Qq
zJqJ`QpnekW{?{SL!apA+t(5A*Fd4Ouy+oT&j7R
z2Olj$%8wBYX_`q*9mJAA%M^B(v`nFb#c&yzF>t?`P-;2Dj+5eWM3YW@F%>CqCQyhl
zFtZgNYCt;%QYtBhsVFs;k&{-KP>AQm@8yZBlpu8A$iuXC>g1s~D6xy}wQFs4U$Hj-
zS2`(0V4TT8w+Iu{CVL#2@$6^>!k$j1@zoy(azi>)b)LJEPag~OT)97$>iG!htb`wk
zc6is3k#ZMn0dt0oBp@OxY!VJW%Aw_j=GR6WxM2`Tnz$o@q6vhJ8BKz0g<7gm>P#Q2
zF2(tQ(j=8MV6*Vv$WQPev#VCbhs|aryJ2;LRT-u)*JgFZu_~m{l%$U0X@uh^GVFwtQ0i|)wfPz?Nz$Bn&n~C&3@ReOT5LB8{DE$!Oy-W?qf?*?L
znoSKKmgV3V>lGEH6pB7ZcsoceF$2LpL4U{{$1zldQYi8a;q@eeT?Lh(6iU2EB}VmT
zMhVX{qe`D)MorqpjA9>W5>5>29MI9vZzg%dFu3MJb|pYiO_V}SuMu8%DJ+1G>=pt+
zB`JlHqQY_Oi1=h7y^(sLroJh$IGn-0N3Z+{Kue$_(OBRD)Y#6B|
zrBHGQ;e{9AX~2~owgOa&QYf{T@W%91AU>33;TL-!Do!aBKS+36sW_CQf*2@I(aeAp
zTAmJpx9o5t?r<>KV4HOZAYYq^)eK2TDXR
zrTud%O5Q7{a!^j)n5Oc(+nc5OhHjFJ;7S|fLil0Z&Qu?H*O$7*j2R>C4jij2eH`E6
zG8V?%kF6hZ7t6OlR*=@b)$`jwG+pz4$s5)0?4Ek|9rwG$JvHEUhKE`
zFT-tT_dY#-YsN<_jyza?;NGVm`sNQ~AN>50;om;JeafvjfB)33b*2yQzy7r4r5S^N
zyl0-{yG2FEEAIa0>I=^v(3@VnH}S;*Pfe@2wQ`j^oPYTFm@i%;*nyP$pSGh^~k9rGDdYZrBXQ9gG@X+y!+zgRwh_te?D=55%t)BVd?
zi3e|N`pR1W>NV3(EZ*1gL7Z>nZR3t_9$Zs>tTyMlrwh|74!p4YUDw9dnZ|D({(RLR
ztv4tB=Hms&SLfgUxclPU`i%G28*5T+jZ7CEKBHLN|(qO|A
zQ#LG@;}`Q{^2M|Tw_HdGy!70jAtgiPaLzsQ7gP13bAKN*~}p
z7$VyBNb@^iz8}}zw{}!lOMA@0?{jD7RqY+Oe=2ipOwsZ6gGP_ek1l%9(XXiES3m0C
zw5L{Pt~tlAn%A_p>tOBBhbGM(?@D~Q<)a(ex$dS|+i%mh-Pd*F+CN|&CeHRBTtXY#
zVNmOdAxj+fz|ym3>JfG~NY9AT;hsXWw-B2kg##NDYRD2Ygz7Ylb5UB)qV+y-Enl|+
zNNmSL7fwbg7#xaZTWg+;#6%MeHsok|&1gL^!u7-u47R&C+?w{n9d|`yNVr+-O3><`
z_U-yzBnGAREH?kx(~W!DxG@rgA=R_k74`+{(?#j;M`C&r3^vqAb)Cz)5Q)KYu%5+%
z4q|S(_xWHXrZ>Uhzy@Rd!`rP8#qhXe>griKSYeo1k3F&`5@RA5OeK_dJpIP!kr*rk
zdKP1cn4!-udoB`#G1jy5;f}fT17n;MiRni$KW(c&!Thu>jJ2M{!4;;@14$i_IpJ}~
z39O#o33m*A?43CZBNWAWQ4<7af)yh8a%o^hP=O9!GYRW0xP{mvRasI7*YDU_23+brBpW+KQu^aV9G}
z%Go17!Y}1~8o@ak!GZC%;;7VB5uB6=PHqHeaRg^|1n1WZ$0CJrQv|0ag3~T>`ZBWA
zeu*b(GlO+a&^__51uaj8V(h|-j8G|hW;oKl|uoWeo6d5!?=A_EDupQEF?-&dmx={lN
zX5`+RNA@MH13u+RVI~;O_6~fvt8VYu@U6?JO1UK|f?*Ov7X4jCq*zo9?(tnQE2&g5
zU|^>WaRj4T>N^DZ+7J#>LbzJ8MKGEW4ppQO^0@btH&m(nBq@T?gm4&
z_J#+)rc$9HBp6KyM-VCO8PFxKZM>nk62cFp5E6_ggx3I84dMRjC(o!-UrSO1qp@Wr
zQrMfJdwN~o*L@UQQ3lE-7>%uw;Gx?3`Y-F6RVj-kMKBs$qlgrib9B3g4qi1tu{A}K
zA{dRW(STKLJzRLtMpY_bk|G#Q%8wyZa@lz7K;=j(6`Jw{qY2?yA|_3l*qEewDBp6KyClD#QZ0w)>j>({e
z@IJ{F!DvEwttN!ET``HO)YFm_!DwtHM%ao{Y?0+X;7>3bTN5K}sZ#Gtwg^UJ3)?(B
zYk@nuTUVYO8K>AfCP@*D##S<5)w1!s;<@8hsk4$4!DwvZ$Vi=oV3|))Y{lSU3mm~{
zY^4HMwRQU+uNz!Km08BS{gA#uoM@s;whe3_Y((O_QVu
zMq>+Kw5svK(i^SVnk7jQjK&srNvf^d_V064soNzfg3;K*H}_Nu2+|Y9Dz@&HqzFc1
z3p-8K*7I3AhpAG(m81wpQ&Xl8DY>Sc+w|IXR4TNl5R4{-=|oCSd2DTBln}lk*&-NC
z2&Za77`67JcvZ?TNfC@Dgwr%3yziD*$5W}$5E6_ggg0tJxIT074_7E5JRsR37)=PL
zYeJZP`-=Ih)EANz!DvEg(}Ylb^y3LsDl~)yqgmk25h)yppxal{RL#i4ROVljEyTb~
zJY*Sr25|H&&e+ghaF$q9ss0ihF}?>J&R)=
zbQfP++@?wuNm7W>N;!a|N)7&3$t|jsPm)56Rw|1~4TL*7@%QmCxR68GB1s`eD>VZ+
zdV2H%P#yW%kYKXZVMz)xS}C4L^@BUQ>}7+ts#1m+%0-M;$_X4jE01j+ZnnRqN{y4G
z5TljKCQ>+3LYMNaqfC{WD@h?nD>V~1dKO2&=#p)#;V#FkQIbN8R>}n&HH33FzXn}_
zEcLP^g&3_=4w1SN?&x0mtsmNUS?V)M3Nc!#T;Qmip+BwGb*fVFQWuICtyCV7ii114
z)y%*eRcfjvg&2?uS+buG96ehn2MR@ryA>otPQ@xNE?ZtzoLOF38Y-ER|xslm5cElEowJ{
z?<|e*DOd_e<&`QPc~$NT%98DPYJgR$;mvl}c$T|0EHFi%Gb=0b>=s$Ndv;d2r)H_M
z0usPOj^)c-xOk}?vAJc{f@fhl#6*B2Wz_KIRF-<+EHsj?^Hes*SUx>W+DBelS-#X=
zUX_|Mr($7M&C*JNp+QSc$ttU^QJ-=#oba7PJ(&!jLJtnYXQ~tr>d`*+d~BQckTvZA
zY?nTwt-F)l!VY5!JGV^@zjS0;XcQz?*hx{L2SRmo3|o&I@*$-$B^(t(^(w{=M;Qs^
zFgg0H#?<9v=mVHVd<|g;?&>*JWfgdYoa9YXl|-0O@_7K-Jdq9o2xCe|2ZU#s3Jy&K
z<)i^^eyZS*Ob!Wz%Eip?4o&!KP&5K?hF=dN=%WgS6c-gA%2c7D`}kJ&Htr-ZQr%*
zT6Q|iJ|}J{osaIm`SWWx+y{5n-Cna{{%ml_S#j5jhWYcW=C6LSwpK;VzlYVc2L0dQ
zZx8zy{RzF^z~qAqLJnD2aBu6(uw2Vfp>Iy_Bn93&&w>bmEg1LN|#hDv)OLX0$;f1+if<~6%CG!j@Cs-!zY0_
ztQESLm}p&ZVE2iN(m|CnfQy1h@altKK+JITq6OAvCNXvF-RwQgseKoxKgVolUSeKh
zww`&D@xwBv9orTKndTt#c93}|2^suwikl@s}+nOfmY;_lco@=;uyK!?dKzDdMFJL@uDPK>u
zdg5$N0XRM}@1jwM@226lQ}3{*`f1S2a^81Yg3am@T9S}gzVy^Do$JGnqet5;arXtYLoH5$e(ouI-@3{&}
z+4iP)`Q`x~(yDNK({FV6Q9000Vt@_gUBFB@a0FEzAj(&ndQ)Sz;Q<4xwZmP^r4~HhgFKFJ5iPJpoBm6Ph&+yH?DJKdyS^;Ngz?jxo
z=mooSbbR_g&-Vd1>5yX~Ebn(hM2*}!emK!?YT09ZJxbh)Rm;T`h2X0~;$~+NYc}t$z~fx{I)DZ6o?)C$sL@FX3*gZ-Be^
zoK9Fx;=`j2Gpy3bP8tII6{b=jct!%AF)nr~&~V7)ZqIjt0$cqZtdI*q*U3Sj9GH%J
zrg*#Zg#q3!k1)pD)mP|;tMjA0XDuEm8rx$%{R4yHH#%?%LncJor<|^(aW=CI*EEZD
zLb{;Wib0UAr#w*l_v!+1K%e$N9oTDzY<<@OC1f<}O@LMZB+8x}xz}`{*X+5@-qaOH
z;$EZtz$oI4K7zUKd^GIYeuulbm#h1yF3`(1l5>K_WvIb{x7X)oDhH4z%2EaB5&je^x
zmJWv_A6Y0YLjyy}V+?0{oy84LLj&<>%?3>js2yq-Cs{$oJ1+gsKwk*4h1M%5X;Qs1
z3mH(mY{F>pG(r`Dl467mLv@&HsB&P}?;&SNoC7l*^{0ee(1OJ&BEkqeuLjcesP7W`
zp^s4`4BB-{BUg9URBIw-JsV12Yp!A*D_#
z)T^MK^4?LXtD)_oyip33tWaqRbx5I(C{#e9zEdc6U@S9mMBfvp&fXop{IONWl9>Ex
zXpGR|XjOJNQl%?sDTQwg&4eePf0=RYncj`|^|LqLa_1NMrhAzW=RY1Z`p)NG*}AIl
zR?Ce~j@vo^p55b)_n$9o{9j(Y7czo}#DJHa*gT*caqZYFO5vT1kKh+E@M<*VbY}F?
zvzSJhB7r^wKpwJ=I7}IIxbQ=V%PNxP!!1!tyZ9jrIO?loX3yx5PoMH<1A@_fF2Sr(
zKRLa!{-vkXPoMaa4;{g1UgzRFWj%|t0jE(K6}F0S#|ra#AXe
zS7mppS=*$fq!fz9SzCW{Cgp4>+!<2fx|_AdV@8C!9?qmJ3&YvEIa}KbdU`{KBx9z4
z!`vgYv5;Iw{!+No0kcuUq3Z5%dME*{fO#7e9vs$@ZgIdLAids49J=ms_yP3_z|EAf
zJ%tZHEfxaiR}xMMU)Zb?!}Gj^0g^ad4`%|t3OIj=K7#8;pZuBPQu@9GUEf`XJ~IX~
z#Pt-u1i)D(EXKE|@MT=4zHGqFy@Wno;3Hh7z72qTBwSzEto*m2Z)=#o|JbbjAuxXP
zzj;<3a?t*NYg!Jw#>X6m@WJLTa=b(zUbMkhY-2D3&c(#IM6SwyhQhqw9h2yV6g%Oc}5fnl$^a=BLruHQBw{Wy*4IcCI@fG{L#?
zr}&0L!eD3mXVoTW`rm8f_{lX!Q$rCn4!n3(;D}2MI@2#!-(?q@T@5gx%n$|K%Eg
zE@%9O9AFo5Td;G7^Wr;TpS=`0*syG0x8?*kfI=^qxaOmr^b6G~fjv2}`+WRX?B=;Q
zY#;?G0>Y}C_z~>DHZ?p2UZA_C83JZ%z(Vaz|IxI5HxPDl4FrOR*@t-AdHOU5JBG)%
z@p{;!+g$W8QfX0+YVQjVRK0Bu$Xnzc
z*WQuf0V?r)BVHUFR>H-1A^YHXf7qBlza2-IG&Ep_P*M1A&!nQxM(dd96djZWdTa;&
zqQg(^bc{$TY}%}Z7rKuQIkLMN2r5A-lo&^Np%P1FIQ&|OFLbgJg%TJL*jEVCU&G@-
zg2SFucEf?7HYkM>*ApH)BAyu`BrqdNuV6+@8pMqFPP8F~CW
z$?mWI=Z{;@+Cj-xx2lWcxY^LeiUXlChqk
z!<-}>nL*kF?K}qC3_Xk5&~4oG@ek_azE;X4Ggs{y{c+%<&SU2CcmH0M`nx1WFq$Q1
z_$foRh08_Nll@OgQUs%!Ghqj=+Iqfx>Kaw5H%r9`Mq`W2ca?Z)m!@7LNfC_37QCwt
zvDN3zKJY>xODnqTB`Jc@*n++%BwlxPjebg9riZ--I)c&I!Zuos*ZKwPSyif3k|G$5
zE$k-LWke68OnOvZ(26Iypd%QKEnK>#+S)zHJXn=_P?91T&AhWek;0*Y);$jr;s1e3
zS-~@BRgK_Y?yOi+DJ^=D7RZnl9a>h%@*oKWU#(%G#=n1o2rTyr`KNr!YKG7?7t+EL
w=@WZ*KGLUl5)t)Z21)|qTnQYqa76-!awJf9|9L_}ck3mIg-43c?ZdeL1vDFsQvd(}
diff --git a/minhook/trampoline.c b/minhook/trampoline.c
new file mode 100644
index 0000000..8d06d43
--- /dev/null
+++ b/minhook/trampoline.c
@@ -0,0 +1,320 @@
+/*
+ * MinHook - The Minimalistic API Hooking Library for x64/x86
+ * Copyright (C) 2009-2017 Tsuda Kageyu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+
+#if defined(_MSC_VER) && !defined(MINHOOK_DISABLE_INTRINSICS)
+ #define ALLOW_INTRINSICS
+ #include
+#endif
+
+#ifndef ARRAYSIZE
+ #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#if defined(_M_X64) || defined(__x86_64__)
+ #include "./hde/hde64.h"
+ typedef hde64s HDE;
+ #define HDE_DISASM(code, hs) hde64_disasm(code, hs)
+#else
+ #include "./hde/hde32.h"
+ typedef hde32s HDE;
+ #define HDE_DISASM(code, hs) hde32_disasm(code, hs)
+#endif
+
+#include "trampoline.h"
+#include "buffer.h"
+
+// Maximum size of a trampoline function.
+#if defined(_M_X64) || defined(__x86_64__)
+ #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS))
+#else
+ #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE
+#endif
+
+//-------------------------------------------------------------------------
+static BOOL IsCodePadding(LPBYTE pInst, UINT size)
+{
+ UINT i;
+
+ if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC)
+ return FALSE;
+
+ for (i = 1; i < size; ++i)
+ {
+ if (pInst[i] != pInst[0])
+ return FALSE;
+ }
+ return TRUE;
+}
+
+//-------------------------------------------------------------------------
+BOOL CreateTrampolineFunction(PTRAMPOLINE ct)
+{
+#if defined(_M_X64) || defined(__x86_64__)
+ CALL_ABS call = {
+ 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8]
+ 0xEB, 0x08, // EB 08: JMP +10
+ 0x0000000000000000ULL // Absolute destination address
+ };
+ JMP_ABS jmp = {
+ 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
+ 0x0000000000000000ULL // Absolute destination address
+ };
+ JCC_ABS jcc = {
+ 0x70, 0x0E, // 7* 0E: J** +16
+ 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
+ 0x0000000000000000ULL // Absolute destination address
+ };
+#else
+ CALL_REL call = {
+ 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx
+ 0x00000000 // Relative destination address
+ };
+ JMP_REL jmp = {
+ 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx
+ 0x00000000 // Relative destination address
+ };
+ JCC_REL jcc = {
+ 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx
+ 0x00000000 // Relative destination address
+ };
+#endif
+
+ UINT8 oldPos = 0;
+ UINT8 newPos = 0;
+ ULONG_PTR jmpDest = 0; // Destination address of an internal jump.
+ BOOL finished = FALSE; // Is the function completed?
+#if defined(_M_X64) || defined(__x86_64__)
+ UINT8 instBuf[16];
+#endif
+
+ ct->patchAbove = FALSE;
+ ct->nIP = 0;
+
+ do
+ {
+ HDE hs;
+ UINT copySize;
+ LPVOID pCopySrc;
+ ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos;
+ ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos;
+
+ copySize = HDE_DISASM((LPVOID)pOldInst, &hs);
+ if (hs.flags & F_ERROR)
+ return FALSE;
+
+ pCopySrc = (LPVOID)pOldInst;
+ if (oldPos >= sizeof(JMP_REL))
+ {
+ // The trampoline function is long enough.
+ // Complete the function with the jump to the target function.
+#if defined(_M_X64) || defined(__x86_64__)
+ jmp.address = pOldInst;
+#else
+ jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp)));
+#endif
+ pCopySrc = &jmp;
+ copySize = sizeof(jmp);
+
+ finished = TRUE;
+ }
+#if defined(_M_X64) || defined(__x86_64__)
+ else if ((hs.modrm & 0xC7) == 0x05)
+ {
+ // Instructions using RIP relative addressing. (ModR/M = 00???101B)
+
+ // Modify the RIP relative address.
+ PUINT32 pRelAddr;
+
+ // Avoid using memcpy to reduce the footprint.
+#ifndef ALLOW_INTRINSICS
+ memcpy(instBuf, (LPBYTE)pOldInst, copySize);
+#else
+ __movsb(instBuf, (LPBYTE)pOldInst, copySize);
+#endif
+ pCopySrc = instBuf;
+
+ // Relative address is stored at (instruction length - immediate value length - 4).
+ pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4);
+ *pRelAddr
+ = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len));
+
+ // Complete the function if JMP (FF /4).
+ if (hs.opcode == 0xFF && hs.modrm_reg == 4)
+ finished = TRUE;
+ }
+#endif
+ else if (hs.opcode == 0xE8)
+ {
+ // Direct relative CALL
+ ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32;
+#if defined(_M_X64) || defined(__x86_64__)
+ call.address = dest;
+#else
+ call.operand = (UINT32)(dest - (pNewInst + sizeof(call)));
+#endif
+ pCopySrc = &call;
+ copySize = sizeof(call);
+ }
+ else if ((hs.opcode & 0xFD) == 0xE9)
+ {
+ // Direct relative JMP (EB or E9)
+ ULONG_PTR dest = pOldInst + hs.len;
+
+ if (hs.opcode == 0xEB) // isShort jmp
+ dest += (INT8)hs.imm.imm8;
+ else
+ dest += (INT32)hs.imm.imm32;
+
+ // Simply copy an internal jump.
+ if ((ULONG_PTR)ct->pTarget <= dest
+ && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL)))
+ {
+ if (jmpDest < dest)
+ jmpDest = dest;
+ }
+ else
+ {
+#if defined(_M_X64) || defined(__x86_64__)
+ jmp.address = dest;
+#else
+ jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp)));
+#endif
+ pCopySrc = &jmp;
+ copySize = sizeof(jmp);
+
+ // Exit the function if it is not in the branch.
+ finished = (pOldInst >= jmpDest);
+ }
+ }
+ else if ((hs.opcode & 0xF0) == 0x70
+ || (hs.opcode & 0xFC) == 0xE0
+ || (hs.opcode2 & 0xF0) == 0x80)
+ {
+ // Direct relative Jcc
+ ULONG_PTR dest = pOldInst + hs.len;
+
+ if ((hs.opcode & 0xF0) == 0x70 // Jcc
+ || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ
+ dest += (INT8)hs.imm.imm8;
+ else
+ dest += (INT32)hs.imm.imm32;
+
+ // Simply copy an internal jump.
+ if ((ULONG_PTR)ct->pTarget <= dest
+ && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL)))
+ {
+ if (jmpDest < dest)
+ jmpDest = dest;
+ }
+ else if ((hs.opcode & 0xFC) == 0xE0)
+ {
+ // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported.
+ return FALSE;
+ }
+ else
+ {
+ UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F);
+#if defined(_M_X64) || defined(__x86_64__)
+ // Invert the condition in x64 mode to simplify the conditional jump logic.
+ jcc.opcode = 0x71 ^ cond;
+ jcc.address = dest;
+#else
+ jcc.opcode1 = 0x80 | cond;
+ jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc)));
+#endif
+ pCopySrc = &jcc;
+ copySize = sizeof(jcc);
+ }
+ }
+ else if ((hs.opcode & 0xFE) == 0xC2)
+ {
+ // RET (C2 or C3)
+
+ // Complete the function if not in a branch.
+ finished = (pOldInst >= jmpDest);
+ }
+
+ // Can't alter the instruction length in a branch.
+ if (pOldInst < jmpDest && copySize != hs.len)
+ return FALSE;
+
+ // Trampoline function is too large.
+ if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE)
+ return FALSE;
+
+ // Trampoline function has too many instructions.
+ if (ct->nIP >= ARRAYSIZE(ct->oldIPs))
+ return FALSE;
+
+ ct->oldIPs[ct->nIP] = oldPos;
+ ct->newIPs[ct->nIP] = newPos;
+ ct->nIP++;
+
+ // Avoid using memcpy to reduce the footprint.
+#ifndef ALLOW_INTRINSICS
+ memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize);
+#else
+ __movsb((LPBYTE)ct->pTrampoline + newPos, (LPBYTE)pCopySrc, copySize);
+#endif
+ newPos += (UINT8)copySize;
+ oldPos += hs.len;
+ } while (!finished);
+
+ // Is there enough place for a long jump?
+ if (oldPos < sizeof(JMP_REL)
+ && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos))
+ {
+ // Is there enough place for a short jump?
+ if (oldPos < sizeof(JMP_REL_SHORT)
+ && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos))
+ {
+ return FALSE;
+ }
+
+ // Can we place the long jump above the function?
+ if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL)))
+ return FALSE;
+
+ if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL)))
+ return FALSE;
+
+ ct->patchAbove = TRUE;
+ }
+
+#if defined(_M_X64) || defined(__x86_64__)
+ // Create a relay function.
+ jmp.address = (ULONG_PTR)ct->pDetour;
+
+ ct->pRelay = (LPBYTE)ct->pTrampoline + newPos;
+ memcpy(ct->pRelay, &jmp, sizeof(jmp));
+#endif
+
+ return TRUE;
+}
diff --git a/minhook/trampoline.h b/minhook/trampoline.h
new file mode 100644
index 0000000..bdffdac
--- /dev/null
+++ b/minhook/trampoline.h
@@ -0,0 +1,105 @@
+/*
+ * MinHook - The Minimalistic API Hooking Library for x64/x86
+ * Copyright (C) 2009-2017 Tsuda Kageyu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#pragma pack(push, 1)
+
+// Structs for writing x86/x64 instructions.
+
+// 8-bit relative jump.
+typedef struct _JMP_REL_SHORT
+{
+ UINT8 opcode; // EB xx: JMP +2+xx
+ UINT8 operand;
+} JMP_REL_SHORT, *PJMP_REL_SHORT;
+
+// 32-bit direct relative jump/call.
+typedef struct _JMP_REL
+{
+ UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx
+ UINT32 operand; // Relative destination address
+} JMP_REL, *PJMP_REL, CALL_REL;
+
+// 64-bit indirect absolute jump.
+typedef struct _JMP_ABS
+{
+ UINT8 opcode0; // FF25 00000000: JMP [+6]
+ UINT8 opcode1;
+ UINT32 dummy;
+ UINT64 address; // Absolute destination address
+} JMP_ABS, *PJMP_ABS;
+
+// 64-bit indirect absolute call.
+typedef struct _CALL_ABS
+{
+ UINT8 opcode0; // FF15 00000002: CALL [+6]
+ UINT8 opcode1;
+ UINT32 dummy0;
+ UINT8 dummy1; // EB 08: JMP +10
+ UINT8 dummy2;
+ UINT64 address; // Absolute destination address
+} CALL_ABS;
+
+// 32-bit direct relative conditional jumps.
+typedef struct _JCC_REL
+{
+ UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx
+ UINT8 opcode1;
+ UINT32 operand; // Relative destination address
+} JCC_REL;
+
+// 64bit indirect absolute conditional jumps that x64 lacks.
+typedef struct _JCC_ABS
+{
+ UINT8 opcode; // 7* 0E: J** +16
+ UINT8 dummy0;
+ UINT8 dummy1; // FF25 00000000: JMP [+6]
+ UINT8 dummy2;
+ UINT32 dummy3;
+ UINT64 address; // Absolute destination address
+} JCC_ABS;
+
+#pragma pack(pop)
+
+typedef struct _TRAMPOLINE
+{
+ LPVOID pTarget; // [In] Address of the target function.
+ LPVOID pDetour; // [In] Address of the detour function.
+ LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function.
+
+#if defined(_M_X64) || defined(__x86_64__)
+ LPVOID pRelay; // [Out] Address of the relay function.
+#endif
+ BOOL patchAbove; // [Out] Should use the hot patch area?
+ UINT nIP; // [Out] Number of the instruction boundaries.
+ UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function.
+ UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function.
+} TRAMPOLINE, *PTRAMPOLINE;
+
+BOOL CreateTrampolineFunction(PTRAMPOLINE ct);
diff --git a/src/console.cpp b/src/console.cpp
index 957d805..262311b 100644
--- a/src/console.cpp
+++ b/src/console.cpp
@@ -4,6 +4,7 @@
#include
#include
+// Console print interface:
//48 89 5c 24 ?? 48 89 6c 24 ?? 48 89 74 24 ?? 57 b8 30 10 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 49 - new sig with 1 match
//48 89 5c 24 ?? 48 89 6c 24 ?? 48 89 74 24 ?? 57 b8 30 10 -- the 0x1030 stack size might be too unique
/*
@@ -27,11 +28,7 @@
return;
*/
-
-constexpr uint64_t OFFSET_console_vprint = 0x29177f8; //stolen from sfse, void ConsolePrintV(ConsoleMgr*, const char* fmt, va_list args)
-
-
-
+// Console run command interface:
//48 8b c4 48 89 50 ?? 4c 89 40 ?? 4c 89 48 ?? 55 53 56 57 41 55 41 56 41 57 48 8d
/*
check for this in ghidra:
@@ -42,10 +39,7 @@ memset(local_c38,0,0x400);
pcVar15 = "float fresult\nref refr\nset refr to GetSelectedRef\nset fresult to ";
*/
-constexpr uint64_t OFFSET_console_run = 0x2911d84; //void ConsoleRun(NULL, char* cmd)
-
-//TODO: get the location of betterconsole and spawn the files there?
#define OUTPUT_FILE_PATH "BetterConsoleOutput.txt"
#define HISTORY_FILE_PATH "BetterConsoleHistory.txt"
@@ -254,37 +248,20 @@ extern void setup_console(const BetterAPI* api) {
HistoryHandle = LogBuffer->Restore("Command History", HISTORY_FILE_PATH);
const auto hook_print_aob = HookAPI->AOBScanEXE("48 89 5c 24 ?? 48 89 6c 24 ?? 48 89 74 24 ?? 57 b8 30 10 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 49");
-
- if (hook_print_aob) {
- DEBUG("Hooking print function using AOB method");
- OLD_ConsolePrintV = (decltype(OLD_ConsolePrintV))HookAPI->HookFunction(
- (FUNC_PTR)hook_print_aob,
- (FUNC_PTR)console_print
- );
- }
- else {
- DEBUG("Hooking print function using offset method");
- OLD_ConsolePrintV = (decltype(OLD_ConsolePrintV))HookAPI->HookFunction(
- (FUNC_PTR)HookAPI->Relocate(OFFSET_console_vprint),
- (FUNC_PTR)console_print
- );
- }
+ ASSERT(hook_print_aob != NULL && "Could not hook console_print function (game version incompatible?)");
+ DEBUG("Hooking print function using AOB method");
+ OLD_ConsolePrintV = (decltype(OLD_ConsolePrintV))HookAPI->HookFunction(
+ (FUNC_PTR)hook_print_aob,
+ (FUNC_PTR)console_print
+ );
const auto hook_run_aob = HookAPI->AOBScanEXE("48 8b c4 48 89 50 ?? 4c 89 40 ?? 4c 89 48 ?? 55 53 56 57 41 55 41 56 41 57 48 8d");
- if (hook_run_aob) {
- DEBUG("Hooking run function using AOB method");
- OLD_ConsoleRun = (decltype(OLD_ConsoleRun))HookAPI->HookFunction(
- (FUNC_PTR)hook_run_aob,
- (FUNC_PTR)console_run
- );
- }
- else {
- DEBUG("Hooking run function using offset method");
- OLD_ConsoleRun = (decltype(OLD_ConsoleRun))HookAPI->HookFunction(
- (FUNC_PTR)HookAPI->Relocate(OFFSET_console_run),
- (FUNC_PTR)console_run
- );
- }
+ ASSERT(hook_run_aob != NULL && "Could not hook console_run function (game version incompatible?)");
+ DEBUG("Hooking run function using AOB method");
+ OLD_ConsoleRun = (decltype(OLD_ConsoleRun))HookAPI->HookFunction(
+ (FUNC_PTR)hook_run_aob,
+ (FUNC_PTR)console_run
+ );
IOBuffer[0] = 0;
}
\ No newline at end of file
diff --git a/src/hook_api.cpp b/src/hook_api.cpp
index e98fed5..1371429 100644
--- a/src/hook_api.cpp
+++ b/src/hook_api.cpp
@@ -1,28 +1,13 @@
#include "main.h"
-#include "../minhook/MinHook.h"
+#include "minhook_bridge.h"
+
+#include
static FUNC_PTR HookFunction(FUNC_PTR old, FUNC_PTR new_func) {
- ASSERT(old != NULL);
- ASSERT(new_func != NULL);
- DEBUG("OLD: '%p', NEW: '%p'", old, new_func);
- static bool init = false;
- if (!init) {
- if (MH_Initialize() != MH_OK) {
- ASSERT(false && "minhook failed to initialize");
- }
- init = true;
- }
- FUNC_PTR ret = nullptr;
- if (MH_CreateHook(old, new_func, (LPVOID*)&ret) != MH_OK) {
- ASSERT(false && "minhook failed to hook function");
- return NULL;
- }
- if (MH_EnableHook(old) != MH_OK) {
- ASSERT(false && "minhook failed to enable hook");
- return NULL;
- };
- ASSERT(ret != NULL);
+ auto ret = minhook_hook_function(old, new_func);
+ DEBUG("Hook Function: old: %p, new: %p, trampoline: %p", old, new_func, ret);
+ ASSERT(ret != NULL && "MinHook failed to hook function!");
return ret;
}
diff --git a/src/minhook_bridge.c b/src/minhook_bridge.c
new file mode 100644
index 0000000..6cdf345
--- /dev/null
+++ b/src/minhook_bridge.c
@@ -0,0 +1,30 @@
+#include "../minhook/MinHook.h"
+#include "../minhook/trampoline.h"
+#include "../minhook/buffer.h"
+
+#include "../minhook/hde/pstdint.h"
+#include "../minhook/hde/hde64.h"
+
+#include "../minhook/hde/hde64.c"
+#include "../minhook/buffer.c"
+#include "../minhook/trampoline.c"
+#include "../minhook/hook.c"
+
+
+#include "minhook_bridge.h"
+
+CEXPORT FUNC_PTR minhook_hook_function(FUNC_PTR old_func, FUNC_PTR new_func) {
+ static unsigned init = 0;
+ if (!init) {
+ MH_Initialize();
+ init = 1;
+ }
+ FUNC_PTR ret = NULL;
+ if (MH_CreateHook((LPVOID)old_func, (LPVOID)new_func, (LPVOID*)&ret) != MH_OK) {
+ return NULL;
+ }
+ if (MH_EnableHook((LPVOID)old_func) != MH_OK) {
+ return NULL;
+ };
+ return ret;
+}
\ No newline at end of file
diff --git a/src/minhook_bridge.h b/src/minhook_bridge.h
new file mode 100644
index 0000000..31a395a
--- /dev/null
+++ b/src/minhook_bridge.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "../betterapi.h"
+
+#ifdef __cplusplus
+#define CEXPORT extern "C"
+#else
+#define CEXPORT extern
+#endif
+
+// use minhook to hook old_func and redirect to new_func, return a pointer to call old_func
+// returns null on error
+CEXPORT FUNC_PTR minhook_hook_function(FUNC_PTR old_func, FUNC_PTR new_func);
\ No newline at end of file