From d47996821c75d1e3739550fd313d52881f6881fe Mon Sep 17 00:00:00 2001 From: IcePixelx <41352111+PixieCore@users.noreply.github.com> Date: Tue, 27 Jul 2021 23:08:20 +0200 Subject: [PATCH] Switched hooking lib for r5-dev and r5-dedicated. Refactored code base. --- external/minhook/include/MinHook.h | 186 +++++++++++++++ external/minhook/lib/libMinHook.x64.lib | Bin 0 -> 433164 bytes r5dedicated/cnetchan.cpp | 46 ---- r5dedicated/cnetchan.h | 34 --- r5dedicated/concommand.cpp | 39 ---- r5dedicated/concommand.h | 7 - r5dedicated/console.cpp | 10 +- r5dedicated/cvengineserver.cpp | 24 +- r5dedicated/cvengineserver.h | 8 - r5dedicated/dllmain.cpp | 4 +- r5dedicated/hooks.cpp | 136 +++++------ r5dedicated/hooks.h | 119 +++++++--- r5dedicated/iconvar.cpp | 51 ++++- r5dedicated/iconvar.h | 7 - r5dedicated/msgbox.cpp | 21 +- r5dedicated/msgbox.h | 7 - r5dedicated/net.cpp | 71 ++++++ r5dedicated/pch.h | 2 +- r5dedicated/r5dedicated.vcxproj | 16 +- r5dedicated/r5dedicated.vcxproj.filters | 118 +++++----- r5dedicated/sqvm.cpp | 49 ++-- r5dedicated/sqvm.h | 10 - r5dev/include/console.h | 16 +- r5dev/include/enums.h | 252 +++++++++++++++++++++ r5dev/include/gameclasses.h | 255 --------------------- r5dev/include/hooks.h | 93 +++++++- r5dev/include/input.h | 12 +- r5dev/include/patterns.h | 44 ++-- r5dev/include/pch.h | 5 +- r5dev/r5dev.vcxproj | 23 +- r5dev/r5dev.vcxproj.filters | 220 +++++++++++------- r5dev/src/console.cpp | 123 +--------- r5dev/src/dllmain.cpp | 11 +- r5dev/src/hooks.cpp | 289 ------------------------ r5dev/src/hooks/chlclient.cpp | 25 ++ r5dev/src/hooks/cvengineserver.cpp | 34 +++ r5dev/src/hooks/hooks.cpp | 173 ++++++++++++++ r5dev/src/hooks/iconvar.cpp | 58 +++++ r5dev/src/hooks/msgbox.cpp | 15 ++ r5dev/src/hooks/net.cpp | 37 +++ r5dev/src/hooks/sqvm.cpp | 90 ++++++++ r5dev/src/hooks/winapi.cpp | 59 +++++ r5dev/src/id3dx.cpp | 65 +++--- r5dev/src/input.cpp | 119 +--------- r5dev/src/overlay.cpp | 10 +- 45 files changed, 1619 insertions(+), 1374 deletions(-) create mode 100644 external/minhook/include/MinHook.h create mode 100644 external/minhook/lib/libMinHook.x64.lib delete mode 100644 r5dedicated/cnetchan.cpp delete mode 100644 r5dedicated/cnetchan.h delete mode 100644 r5dedicated/concommand.cpp delete mode 100644 r5dedicated/concommand.h delete mode 100644 r5dedicated/cvengineserver.h delete mode 100644 r5dedicated/iconvar.h delete mode 100644 r5dedicated/msgbox.h create mode 100644 r5dedicated/net.cpp delete mode 100644 r5dedicated/sqvm.h delete mode 100644 r5dev/src/hooks.cpp create mode 100644 r5dev/src/hooks/chlclient.cpp create mode 100644 r5dev/src/hooks/cvengineserver.cpp create mode 100644 r5dev/src/hooks/hooks.cpp create mode 100644 r5dev/src/hooks/iconvar.cpp create mode 100644 r5dev/src/hooks/msgbox.cpp create mode 100644 r5dev/src/hooks/net.cpp create mode 100644 r5dev/src/hooks/sqvm.cpp create mode 100644 r5dev/src/hooks/winapi.cpp diff --git a/external/minhook/include/MinHook.h b/external/minhook/include/MinHook.h new file mode 100644 index 00000000..15c0a875 --- /dev/null +++ b/external/minhook/include/MinHook.h @@ -0,0 +1,186 @@ +/* + * 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 + +#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) + #error MinHook supports only x86 and x64 systems. +#endif + +#include + +// MinHook Error Codes. +typedef enum MH_STATUS +{ + // Unknown error. Should not be returned. + MH_UNKNOWN = -1, + + // Successful. + MH_OK = 0, + + // MinHook is already initialized. + MH_ERROR_ALREADY_INITIALIZED, + + // MinHook is not initialized yet, or already uninitialized. + MH_ERROR_NOT_INITIALIZED, + + // The hook for the specified target function is already created. + MH_ERROR_ALREADY_CREATED, + + // The hook for the specified target function is not created yet. + MH_ERROR_NOT_CREATED, + + // The hook for the specified target function is already enabled. + MH_ERROR_ENABLED, + + // The hook for the specified target function is not enabled yet, or already + // disabled. + MH_ERROR_DISABLED, + + // The specified pointer is invalid. It points the address of non-allocated + // and/or non-executable region. + MH_ERROR_NOT_EXECUTABLE, + + // The specified target function cannot be hooked. + MH_ERROR_UNSUPPORTED_FUNCTION, + + // Failed to allocate memory. + MH_ERROR_MEMORY_ALLOC, + + // Failed to change the memory protection. + MH_ERROR_MEMORY_PROTECT, + + // The specified module is not loaded. + MH_ERROR_MODULE_NOT_FOUND, + + // The specified function is not found. + MH_ERROR_FUNCTION_NOT_FOUND +} +MH_STATUS; + +// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, +// MH_QueueEnableHook or MH_QueueDisableHook. +#define MH_ALL_HOOKS NULL + +#ifdef __cplusplus +extern "C" { +#endif + + // Initialize the MinHook library. You must call this function EXACTLY ONCE + // at the beginning of your program. + MH_STATUS WINAPI MH_Initialize(VOID); + + // Uninitialize the MinHook library. You must call this function EXACTLY + // ONCE at the end of your program. + MH_STATUS WINAPI MH_Uninitialize(VOID); + + // Creates a Hook for the specified target function, in disabled state. + // Parameters: + // pTarget [in] A pointer to the target function, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + // ppTarget [out] A pointer to the target function, which will be used + // with other functions. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); + + // Removes an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); + + // Enables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // enabled in one go. + MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); + + // Disables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // disabled in one go. + MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); + + // Queues to enable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be enabled. + MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); + + // Queues to disable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be disabled. + MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); + + // Applies all queued changes in one go. + MH_STATUS WINAPI MH_ApplyQueued(VOID); + + // Translates the MH_STATUS to its name as a string. + const char * WINAPI MH_StatusToString(MH_STATUS status); + +#ifdef __cplusplus +} +#endif + diff --git a/external/minhook/lib/libMinHook.x64.lib b/external/minhook/lib/libMinHook.x64.lib new file mode 100644 index 0000000000000000000000000000000000000000..27efcff2095b39f1a2b13c2471ee916c9a138e8e GIT binary patch literal 433164 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 gM`ehfR>>Cev>;rSp+fP16pIv9rV(#^M* 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�HSi{N5I7xjY4V+_vbIjDlXp8g9;A3&nhjUC2=jfa`&wyESoZ$QlE(Isa zf^lwzHE?dhNpO+|&WXS|VQQkp;yfFC-0k{sP6*;G$%*qDFiY+wIM2bQ;3Qcv&hNk) zIKPdP;3N&4rNBAKg1it+EN|Y1n@4Bxi!Ag>K=m<88MnM>zfYn4;xVG$KC++Sx(G&s zi)6vLz7K2Qx>Vrcf;4bNfornGbp@DM+w|d@JRGia!F45!1Q*GIas33=fO8E_5--xg zRRLVnEUxWfV#&aVYua$QrVFm0!ANkCEEv~Kum-Lh1P(4p1J`Wenl&{s(?Yu$e5|>u z`Zma-oHZ3CClA%~kU-CS-<78@|weqER)4-%ZW;8JjsEEwnAu!g_eagz9v2F^<0 zTrf2;&*Hond@RWNa4rbqoR<^leK1Qd6r6Z`t#Fbo80Vv~2F~B&BsfU}=V8Ek$kasA z;(Q!@tiJeg9umZv%!%{&FiRdTIG@0!#FJ#fI2+0Z=Te*mCu!iU1h75 zGrdxl_RB4-*jUBaTGE0n12aKJvS4J(VT}m33LspN2C^f7Y^gGj2{MueBkO@R0Cfu>T#yE` zqk(MM)I^&F*9(5I6)yw0En~fA+y?qAuYh$kJ_&ZoV+3d)E(Ivbf`NV!*1&f%PEzei z1Lzk3^th>sl@{oi!4Ed$x$FAjxJHupU@!f_U3pSv69?l5lVlKon;(<=eXt*E1zIKcb| zGL!g$Y(yVyg}wHyyQEo?wKni2Ya|QK+CQ2lYk$K@vPK$N+j{}FMl487SYTrOPw)l( z&m5pP+vrDg%RF;Y;$;IIcfx|LTh1=WEJ$slAAB9wnyKCxK5NUBW5sSB3W?EqBd9TY zI@U4D-HGsXPB}t&j)d?{C`nfLS+EOP{l#+0>Ww%_R!JkPcURUW?yNNVbis7*ti&v? z+=mo-iWsDK1~rKE2Iz8-0MH8r=v_gb66oCM>0Bf_cL#MQLFeaI=gdn)=boTW6goSs z4pL;~?hWe9fX**Goy)|}eL+@ev{ z!F>XZi-<g#%6P zvhq(YjQ>DKV7#;|Fr1?W#>WN*X~6h9V7y{sgurxPu`qlsLn?pGLg^XN2vk=VhPohn zbypCLQHVwg5Cn!a5WNmW|Ex^BWpV7z-tV^2V+}nFwnj zC=mp>APp$*0Lt5yi8n2jy})R$GSl0bQPSD-{;4FqL^02ibI z5-lZrt}pnSM1P!5DO5X=w+xF8KEp8(277RM|w z-H$8|ugZjSR+$9U1<|Lwg6Lpa1IRo9f(z0>6kb@Da2A?@?GP|sXCVhRnT>#y6eA0F zp&MQcYb3Nv62b*(q&Rb7ok?yTILzvxQ^fg#%yA~nX-5;mvd9(}Z-vV-c_5O<>>k>J zhGfBWn3G@)CmRF=E=U8-To27AaE73X8Z_Qb-aJ9m4{bq1vS2h{f;B*#Dj;w{8fdCK zG-rSVba=l9DG&at1TQ-xCOgWpQvw`4xmzSqz5-Q&Lb4Dj^^1}8(;&GjM>^RfQUw18uq9cN1tutsXHNNTtsjbvNBWM2bElKns@i=Ui^;y=KaWJwm1>_2K?eNRM^{S{6UWYS3X zjx-)CrW3cO6St%jH>VRfr4u{Si5t_Vf!qtViW}0p65NhTz*`sjU>WPBdwUv3xw_R} z-I7K_lJ`JvPP3}W1lzsIa(DQ+H+t86H;hXdccz)!-yvbiEy+T1`}c*gp0K~*b>wkMdFSF07EJCQZ_i52X4fcN}zSv;Ev4FU1=id_Iv25VrMBx&P< zG(h|bAf8>3c*f$`AAHOW6^?s|<5}RifH*EFcc00^G#T20>Cd}@X*#R{B_>dCK^mC; z3QRAqNIbtH@tlP<10u{56|DOS>%|qE;I;GMJ`Y$I5#mMV?sHiX@(lA79zMk_BQOGz7$}y8ahbO^Ks(;K^jsSehFQY}z$K^mCe0j9TCB;K@`mVl2rhr;wb zVtRW8XBM~%-d^s$nT2U7v<1_TM{KyCXMa4^-N7%CMffN95rb84Ua(BSB_> z>;a#=UlOooK95o0->9VID`1kfTkMKSE%`QDWSRQISc1 zAC5cUBc+7YcM5JiyTuoTNZWqd-qX0(FT~a7+yFun7lK zpa(#u;)`Ge3UoTSE)BdOeHE1Q8aE~`?V#b(Vg3TmEgg}^%Eor4O-2JT0X+}`6sQ0~ zZWC5d!`Vrh&9?ds2H|TvyG}X>8ir||mFVVoI^%$3=Mn`=OEGq#v<`y@II6@+N{ci~ z>sv?wpC`jHaW)TIc{q!QGkMsIgDJejVNvmIFam{#eL3ouF|goW#077cevswhnm7Hj z_ZOmb`B!ZmDfHQ8laFgI5)v5GKzfi3rw%K!p$~$Rj~YR1Dkzo}nF%AFU@Hk;lr`&qm_&$4HHVCQ*~y zrfjtm6(gN9qt;9{mNy5^Mc=U*JqmoA(H0QMs7R5~Hjpw!*zSL_As+}LTYV|Wk5DDe z&Q@;4OmZmqpGIZO*Y|YKbAo6LA$_F0!K)ibBh|PG{R+Z6SC_-FgvTbK7`xCU*1!WY z_63}zNsva9_#P6dOI(U$;$j{y;^6`uOhZ@;y^8OHAr-~MbRD@r$mVvFdj+{NN5P8Y zld`UK0{K^2?ak`z4eP;mnUoosy&57hF0T41gbM8j=q9fwi9GaxKv{_tWN)?~Z-f`j zQ>wOGKYiJ{Ub$)AYprfSbXCs%1Mt+=m6zQ?>&72vZ;BQ`<%P|n+AMD4;ufgR1nIJd0FbMJ zMJ%$CSk?7RU#i#tK-Cu5S;~z_;btq{$!DJ>Lz?=s}#fzDVzgRDx%C^ z(eyC+9DI>Y&m^mwo~@zZqyg)*%ph+={+z+T=Fo?L6Q6U5iWQxnUD`>Y{g%mv z;Kof8`Yzp21#MBiOmsO2Hm-{yk)B2|cA=-a3LfC-a-5_pkVa2)GZMh&e;gA#c(@S< z)5%;7m5N)ykn%T$`a^PmmCe0|+*>Ud8DNQQ1J|X?p@XB#*Q@D;p!(K(7lKh&Rv9T;?80n+)Cn0aCb;DfmHp zDY(o&EMkl9W+M5t=p0(phS&0k$LniHR^3I{aY+DZehxHJZaZ*cgYpXy_)LwcLg|+v zC8L3ud$>&wZ>F9<>v?iuCy2MeAfV&*m0m3F9TE$lmWf5xJ(4Za1>#@9v1IF3T-a>g z1_Idm~q;Ow$3ckMC z7X5FAWY?!&$zI;e~#%F#YGiLe<2x(3mlLyfDFL! z+IfKOp^H^LG6Yy2Bd_I#^EjKPoB_y703zW3iVF+=WrbE!gwo%#(R#1~33zy7RgVrq zo5#qjgh8~PvuVm1fV^tZzUD{!4~14zgwp?kw4<3FtKR@OZpNdxK)4+6Q2EqY?Gi4G z-=;6#6)46ojMX2&104MuCkZTRjMcwK0(FVUa5N+IhY+}AY~AvTUZgY>XCY%^C#=&471!={f!H||)aB%6$eZsw?^5X4P<%Rm?$YT@or%pYLx4~bM6#n^=^QvnZf6vIiX3~5xEzafFT z#LGA)Uc$jtndy*Lyb6ZYDRm$4PuvGc3Ce?4YP06x2IATB1{2+xuyg5+k)%D&Q&nUA zJ18PJ5Gs-t#n^?cEPw|%nuC*Mg*3AAIugKhG8_}H;b5|IFr*c4f)S|w3&C}%W@^7z zQ*tngb*N2- zxn?ptIXl%j92#*0Jq5yOsOt5G&QvudQYMPA3uV#>4+zH+oTN-hqf9wZMgI*y<*fj{#h! z1#?qjTeH>l`XVu!sxfqZu`Sz5O^97))aQH|)%>2aDp|D3dCpX_qoFH-FbD&NL#vG6 zttTCk$I4cj71_wuh-DHrVL3KiEvl9jb?}Y4t!FEtk5(bsK{YrIWb$JKgLR^VGLFuQ zu?v-}8y?_jHBM5wNTYI%Lh$MmBXCSacnINO>eUHQsn`vSK&wBI+%ehQ_2d>??q+o- z-2<*mmqV+8%j8(ySg-td(&NBa5I4d)IhG`V_kuulL<+Jm+p}-v-Q9ZFTSv8)nLq@*i)+4>p?WJ^?`^mUSvWQkaM z#rh4f1E9PDk}4lvwD&NGluFfJ;#3ro(m8M`aK4EP3+G!*zbG!MP&$_+ewC4yApaMn zZ98_}em=NyQ~NIf;j(#cHuLt;+W12HV$Yx$h4c&`zaD1tFW|8RPS4>a$VsDTn2z7N zuS*<&qv;P`gn*e2Rm}ufDZd0>#XhHDS(Kj=e}l`MGy%yMp}@mvqCXyvXZAB_hqU+u zJ1CC>Nya^AFVmR=Cf#9`^&P)EIx3g1koz3HB97wX2!8)YlEF|3?Lsi#h6gx$9VZC} zX#`^qK-DE?p_&K{6{<9IB+7e(SMg;Xst`A$&_ag=ufib*@1lvIiQ=1$v_eYz zzy@KdBN_KxO`@xDV5+bb`JKVd2ib1+6*qBl1Fxkd83>ikE(D?h9^hyxPLem$s5lLP zgwHVIm{^K~iAE!&DjLn;DvKuYD#n>#F&M=h(C*CGN$vP|Y0mb5R;NOPpIg8_)vb7C zuaQ2Gcm^I2o1;j^J;%*-JONB}Ltn?ko3Z&^>lzbudXzYgi&Lc0LXweEiS9zAj)ey} zT8@)MiZmj%3;^pAZ8#=ca4?Zt38{+I3UHOh=fSI(5Qr4*Or&U@CsNDl1Bo984~W#U zB;%gr4mzF&raw}-Tn8d$oPI%^#>FYp=p@NVDfuo$>PPSZM_1t_ks^&qeE|URjx3Ie zl{lD4ZG%)rYBjjZ;yTS6Y``Yw#T>d^+GRz@r5o0btRW7SCD}<|0C596A!1!5<6Z(c zf?cr>m^xy6ux^olUb*8vR zq#pgN-$;=tY+!modpA-;aQjHcJrMU2M0Riu4i#MEIuKmr_C9eN7q>{|ev%BXlJ7!r zAB6`v`W;RZT+#^c0C3kOHsNSO`WOT%q^B}ZqWrk#4Q8Y+;s+aOkrh(Z4{YR2o=hvG z^n2JKFsG4>d#;`UyJ8BM>W5SIV)+-pxwh zNFxwu08(AzOE@M@$H7E{uW6`gYz9|ZyaZmw>$-wXVg_iYqoJl%H%DE1$>Qnp@GJBL z2!DkiL}&}ixaa<5x-SFMH%%}JsG4J3>v+i^Yn<|x5KQRBDbn~GNybab3yIgE_`R8Q zCREEPr4gZh-bI9TT$YH>x!@GGiI_g|T>A+6JQ6h11Ug^>yPF}9%?1yNiBX_xrS7gUADd_^X74D+!9@i`nG z>Jo?InD`tIi*V3IaBM{igi8+5$pZ?#o#Hqpq8v@qr8%-C8YA@&v~(&Jzer0~#cQDN zxW=04+61On)l!%VttP^*OlVC-ajj~}2QrLEZVn??Q3o|dZClLv!T5yz-C$i4!!K#0 zqnJ|Fpj@p@iqE(vyTz8J=G}3t;&DPSYE(ui1pVr_eil<0*DC9+BknobQ7&(sNe}`N ziB&Zzmm4$=O>UG3eKsEv9r#|<1BqT-hpA81TR*%^Xp>Y9!CKG`d^VobGNDr$`qNN3 z+JU<+(TZc@C>}C6=*n?=#c~iXIkxOH#xE-o<*Xbu&#oMUXb6r{^NaU@J~yRohBST* z>>Ss4Vfr?q@~RaSLURi=iFWWzT;TLS^qafFtb~cGFxqF13e&0Go&`5aNh5ApTj`lT z@>ZPVwei=XX>hAFbm=UkLfoi*gANJBdUdjDkrnXu^{d9tL3F}qKhw92wTZ8cA!<{Z z3FEm|$3)Q|Y|~H&zYR51D~Wg91ok{I1651FBf{rXoBh3F6x#O;ei!y|a}iEbaHLVy zx)6xE#3~%kz<9Azyi}boQ>V+->H9cUOsr9F$;?N;P;mk*QM%X1WV7%OC|}3elFt%j z3%!{gbg&~Eyn>qDeocN*AeZLV&~ypV`d}|J2<~G1JJYJphYa5@rv|Ehe$7ZssUiwL zgrVRZPF;#TF5Y4CBbc}ZJbWVIX;t4L9$0K+BD5GL5mm3^#^k5QM6&ql9w6~&etPRV zT%*X=(q(WkP4+X*cpr~SP9+XxIS0wOsJosHd+=!0CYl5I^^y)QevS*Pr8-z0=GdYO zshlO$;h9=>0fliRw6q!8ZjFy8Jo z-eeHRJr;sDm@_WOse11>+dJ=yI{d}x z(s0e`1Q*V&hS$hd$gCFE(*s)EVfC*4XwLvW{T({-Z)+H@8W^w9k}y=Sf#*sXLhg+P zoN@fo{R&dNMR5HiJ4K~rU0G=Vgkl8~Ao#@xn|%Er*m%-?LvTaNi^f_V+ep04R4$gKrT@ByI|bj|!}kzo$e9`N%-# z57KoOJUx|Ve7ND_zSM#D<)WetxbSIw9L@aUKM+9i1DqV!r@bDMI9&X0qB;hBGxxf^_ z#uxu_G~pi$LB)?5ir^Fh#^WgZ2}HI9s>tt7{&t$sk?md8Gz;Se|Kx2)89hi9ndvZ} zF~qNhmE$r^ujryx*O7}<$3sORPr!xMQq6^lVlbLgwL>ebM-{N*qltAJgDN^%ME6P~dT~LWnD< z{~6Xwj5pO@*&R=ZW!s3j$y*#`(iHbh%8KHqKxZ)#X4i%Z0an#6K@pu>k(Y6kOQkuR zJ|jt~XK|CzL1glVXq&{a;2Wa(ut#3z;Uon^8Z9aYZ*_^AaWpMzfl@per#!c~zk)WR zgEbM_>AnriA&k{Rno)t7%U<|E=g)Mdpk`fXU&qOv>j%1qh(Foa z>1f!vs&jo;NAIfs(|Wr)I{W%MPjj|L;AS-4gn@fpcwA&;B(#T4w!|homfo|RYUPPq zc`vIxKELw5P!35(_Rgt<#P_!Tk zjFPy7!ZV@NhQO^Jsu@xdoB&N^U;+HH&<;jGR9@#=|31Ie4uuYaC}(*lAjlK(LywNh*P~roPldMKh9*tA1 zMn5&q3Y#iC%lP=R)i_-=Y$Z<^jV)GVu+-_zh2!g1meJFUv?#CjHc1?gN4GRZt(HSU#?8LIJ!)p#gRe#^^L#hp>cmJx}>)j2#q_(GvFEA~~0N)eSpRNJ+wzq_Zi zzk6d(PV-4Y9iNZ6lKpX!acFNbXr)i>l^&L14oQUi(LN&Mib6#PMO5-R%7R>H5&(d`n__V8VX$vfy%rOS(T}tPg)0$L$9`LefNfLbmTtO#Zny} zrI-G`?w++iQObDPHrc@V;p99pu7Y5QgBs)Dl_|!-OH-U>!I5}ED1=|OO08bq)w9}{ z_)=s0iM7QKHYwY!?EhHL_Y8&plf(81V|xRBZWJvld|OnttlQN&{A)S01zj6;TGw^2 z>324>?V$EL(NJi6)M*@8-{0N4zI#(u_f&Q<+gtF4?3No6s-BkK4qeUhI?oj`g@RF1fW ze-U+hy4H5~cQI!nl{2n|=lL?G8`I{96h0|3I#cFm+}G zF?cYT5ufBYSs78Ha%USg+3~Gd|ijH<~EtyH_AFFih1CygK%tT4vMXV(XpX9Z`L^c6?6TeXQ+7pFE;`he8wb0dFxm-`_eY&E;UyP92n62a|K4Z__!C z8RvG`nSh*ESlg+?*iO%Bo3OSseR4JEqobOU54i2E=357Iaygj2QwIyJg9SOzFVi`% zGS1Nf>3#w^Kg8ND8pig}oVGRAcCk-xyV=9?0k?f^y>(ER%R%i<9W+`8N8~_n+If?4 z9%$$7*0#kbw@s}%U*fi{uCNZ4=SbW%wPTI*KvO&3+IINlwz+&hU*fj8thEkS=SbYt z_I1X2ptf(cw(EUzTVqeim$Bdtb=QFIoO`h!D@rRwGM8|;~L z-3I9ItlERasr}xnJvyA)AFbLm2KAYo&u|DW4~3q}hq}kWdC59>K9_^%cIx0C*1_L% zIe2BK4&Jg3UeA%!O$O+{t=c=oseNqKJ{V4I#8fje3Qsk&dB%-*#xX3pP0cf}In}@! zXB~{mxV=9}gdoA0!BuJsn4(0HQ4 zs_xu`wQAd@#)UDtn`O=7^PN>$s{qsIJH7q>+j@qDw`7U=&dFM$=81z_5fQlCXTcN% zj<VK+qQmgvAPnIP#gsC65@jj=QiPL94 zlhc>1)8{{v{;O91mCvOAhSmRPrBl0p{Zg!JtjCIBRy%s_GdX+LI(xg)slyWD`tH+J z40IjvMg9s2!keGT?}yg!`=}p%-5dM5`%m*N-EJ3O?|mj;pIToZeJ1_LLQ|KWg`cG^ z9|(Oe%}Lvw=O-5Vi6c@zwTz!=^AmTbO`mXk+G*}w+l6(Y{w~xlG$gMd2!(Dp$Em}>IR<@frk_4-PFr8lh7KUX*#Hu^hI72MbI+rDdU z-(Hcwy1$vv z!xYV9eVRuPtJ&$(JZ@Oc6SQU|d=jo?AvfHouQ-13tJY4_z=q?y`Z8;3JNr9ZyZT!? zd)9Vs^5r8G>Y`e6XJ6-rF06o8qY<6h(ABfH-}k@`5wr{k?>b>MW_UL3N3b;@&L6YusL9DqHtJPU!=xRSO9e{ zBns4d)Y0RmJc~Ua3>DDt=qutqhiW^;dv-u;Xs@!B^1Ah%YlFqWPci4s=9yOL8<|+S zLZQtR4I6=PQ)6fZq+l+GOvOOHvgwb(Pa#q<*lw@*QB~nZ_!U`ob)C5iUvIB&&{vGw zrMPx<(fOzK7n5)LSNpgi6uOA~eC5rrh~{Z!q3{(H>KcyvspoZ1GECvCMAp?m(AVQz z_z#8oMc5(1359>4(plXlZQfUz`FNX^TB7GdOGWQC>YM2CVf!6msOa5kt|I&prHa|z zaup6oBK(S@;_;)8xC%$Y{Ft7y<=5<#l%F+JSNsYhws*kxLCfQJ0u}Eeb%m#;{4k!f z;CJ+J1<#KdY4}ln#S-EdKJ!L=hZPQ~di>Vl@Kujrfa9HT_v%2<`7J~TPwm(b>DY$C zzY=D*jO#-K&algqQyuyzpv}+||6{bw4D<*4-d9ZD%g=DBQhJVxrj)qn;(0J6M&TFi z6~8t{9zF8|VA^xOj*%Z38$Jm9SlN&&X6q2YtYtF7k8$bba`bilir4Td@grVCQbJG1 zFGZ;c$mgX@1lS+*+gK*I{4iDi}o~gDbRKex60~_%#>uSl9S%pZwY8=Y8^Jo8RRbKHL1FOOAHP zPu&b@hx}rY(>Syp@)@bLKvbw1Z-fynwxF4~lDvfZZ4_QHF9+b-8S>csEoqUu0nfEA z_{|aJf}aa9F8DDM?P8v#f~;3p|m0{q;C%0ZpJVgmeThW3#UBfr(4T=1(9$^}2@V1jT2U2vNopMm!|b`|!d zP-rPdLqhzaKg9gLf%48TDJbv!2!ip>4=CjE&QCS$#5=zPpuF>|1Ijx;7ht^eV*^8% z5D738T1i>%r^LHqd$=t_z7Vg{;LGpAc=IuePeZ z)Zj|_<_odK++-Nxd%lvt2w&7ySA6Hz5<8ec#*w#W zkg2>KgDaJ{6}T#9Ul-x)s47Rt>nk?%R$PCk9Pu?;7QkEs(5|S81_@IysdF0wqn#ipcuk4&z@mzb-f zOYIe3U{X@P=)x<;eig2r-V9c0FRRTR%c! zzNhDGFtyGn;9EWU8Vtw9P~-vqdf}k7S{%n!lP|J5l zRMq4QFD3wdk7k$v@Qs$7E<<6yWs^HUNQ~R**_}kf+;6Yi0r%XicEEl8QWizr{V!J| z_yU5u;;RJ)9^WNFA_HsI1oqaM^5HuHPH%Hx*U7$su@4G``BsS2+t%5)wyR(FQoh3A z8w0t)WAMlRNxl=3GvDJzAa~q2Z>HSbtnc*ZZ+6_ZkNPmQvl#1)kw<&jpg$&5(vOY$ zA?NM4cwKIr$vPHm~crE^+3};0=0&2bVp$Pv7iCpM7|$J*Q;xVo7VXN$aqk`dp1wBWH*|a<#89#@8_T zHgL<}@mLt@$Z{7Hz$4=qE*Wcl&WubLw^zg|2JK*{->UAOH5;Kr-Z=0^qt7$EVx*7Y zgm)KR6{8X-XB39!$3%{KY8kONt!t7ivBFP) z-M?w&+&L@jdseLh&kt(spsZ}yy(11OZ0t2Xv^;XHet^H%ZhBlq3|xJvLp z79g2NDFdk&;S%Og%D(9{l}AVK!136BIW!xLt`^!@PBgxerqo5&vEx)+_lC@IZ1s`X zJe+^2__98awz3V!cdQ!d>p*{L&r~0uv6DWDSTTb0&*W{MNdq$K@z2N>7#U(K zSX94OauRe*nhTB2?x6`NDu)}cnKtvuGfK@_1Rl#LX>@UY5^zY|bxcXq_7LjAmNa|C zL$&Q;K3hpswAR22;u}AWK0d}etDni@8~vkveBv?wTYY@uG5mIqFZG@~1R29#=rAJ7Nh6;}dwL~N zNR0i8Jp7f0uVctPg1xY#Z&g>q&T-(;cZbnM4C&kCr85tImB$y%+*nAzj1Mt)k%r$j zo}P@e2JbC0nxjVa5L+%MVE%Gk(5i_{ZlQ*J|2Z+cJ$Et;e*s zr5iia%hFA49c?Z3OO~WtI%+aab@fZs(6Dp8#ft^s*G+oJ&q)$3HJ|u~;a8|}kMYJ0 zHD3&pxdb8a;5`@rPdB5y zQC`fK^r=VWjDK5mM@_1sp*mG_WJkJbS$#{UsbkrEm51Ci9q03m-`V;6HfJ&o))5I< zs^E8`jOU zmjnM~e>+r9LOG|W4sO?-;@KnF{zh&Cox^`yF8@E){_mewy>>QElaEpN)SG^QF*W2qC)t<#WM?ua~+TVCI^EY(n7JFHGkroE|+(JrnY zwbuyrIGjcP6glb01)H&5z96RvL6*CW?K%ow5{1_h1;&;21FQcmU+kJu^ZN^+Ia+Ns zlR9t_=n_Xi^pGuMs&Bjm{7C_R)>blIS6EM@c=o!g3wx+^8r8FC#&~)95$9gg$j5y+OI&=uiQm%=I}d2x-{jC`T>ZMg)w&NE z-Q4q#+Vryensi5FswuT3-BFurOLerS+d5Khn2c1nx8dWcvH-GEHCt8>oAd^63ba6P z#=(*(l9qu6sap_vqxI8J(`v_N?KaW7C2Y+v$&%}b##TPjsr7IB(dMcLV0x2+a&3+p zhe%<|BaOvnE`cgT-CoG3oXEnR{Iz*KaT&|V^JG&&wO+~Y*DwQ znwIF{Ij6{$>=UMBD^<^`X63E@oqc#;e60?zE>E_;(R$xYeUI9HLp))`^(kLmF|*8> zXrR^wp(;Gc>(e@~gXz1(S=zZtZ|yLxVxoQyF3ZB6Q(NmN z`Q_RnBhMQBM_=T<8eJO*!A_%t$p1-4ehnk9`^-nclm18g){yfYY4mG1&KjI1Z;II+{#*pX>(Yi`#w+0Uwt@+an(P1^Xm7XK__SNgC_Z&#oC zm_mPN|G*|+>6bX^Q!(RQ-L*R1*SE1xMteqKqN5i6YrFb8vHGf|Uagtrv~+Fi>N~k> zHQF9s`ufl^f9dNcX=+Z?FEcd*(cKL-<8M+jQUuO4+a~jm3cDO#)Ska1E%s4Ivi9iF z?pe^xJ8O`t31R0o@K_rEz*)^6)KdhmhEY$giiMp2(|WIadU6L`#6A)BZ+d)LRp3Sp z+Vd>;4y2*?FRK?Wv3JcH-@B0UpLU$5B4o~c+V0=hPThHr(CPli^1~(eTKnVUYySKF zeMA~KKDKr$zrl1v&Zk9_)U&QF?w7Ig#6#_4Ycz~&OM;hHg+ek>-H@p{Qgx1g&f>*_YhU9781412 z{#qL{x^vR*$bdIhXUmK~J=X3A8taX+CxPvm4@O5jy>)$EUD9uG<-5cg*wnRJeG8;m z->ps-JC&J9_(;c2^Wv?{w4;yNw6d|Y@8s^Dm9-mB=~=(AbM>Z`csjgpRYw)czNJCdzIYVp}kK5hA93I1`L)V^CI8Nk2raqx(1rqLg~T#pjW z2{zdIUaC;NHu|~r%Dt%fcSzKU*}OU<)db%B%{D#URXn=?lLPDjLU4FtflMZZdo)ztz!vexDGEsDVRUhAYXyzDdGRlea@!SHu3RyXKy#| zbXfbikEIg-2LTLg2g^x~h2awRjpo|+qq|4;ib9w1U+iGLNUw$`B|l!j`Z=Lh4<`K= z@*PIL4-r0$@weEvoMHNKU_2By?Dr0Yjd^NIocw1wN-f7VIL_hFV?Gw+EZFxBO(>!S za$=@_(5A?#);2Z92KLO4%Db97(4OgVTM5U#mB)LpgiWNx5eweG|Y)>9)p8nTwI&ef?^Ck=G>byZ1OX*(lp+E~|x7e52C4%jWn zZo96QF1#SR2|~}Om03ei#wTyS^yXIj?2JsC1!N~?r3azSgOz!PZx!#Y;>4$HzVw`I zzBaKjAIM6_9}g=tMr_YIR;LHksa)X8myv0+fb1(-=|O0HTA62P^JU7WrSlY3E;_$9 z_96g2%{Q?HTg6rLZS0ibEwnGG$0lxI+nCw>lgYemA0~fbtGsEx#jgbDE0k}p{cnyh)Xxj3fuxboN5i`UJJLfg7NRMlR-*=&4qwJ)F(SbPS?K$<>Zfd?3# z9#;ed{7%YE*tQKPoR?RnU2riab9j*{qoxpM)>YMIUxKdzs%)ptgtK<0( zzb=5O#T9)sOx%-I87_dHSJHY5UzWVRM_vb%x}Y_zJ^OLI0*w#1b)C#fx|+iQv-WS~ zoX`j4x?Qq=2E4|u4IBGTQ#$~$zGqgybw93kPlc}DQQT#5_-0Vj=bJiDT)^YkQ}?sl zr^fds=+$pZpW3x*puh9@_4Xy<;=$60db$*Hs_UC-Td_>sw4`HcI#ru)X=P1QPmO%C zR838~xvitIz7dPbN*?js;&Ee3s-wN7K|L+PxaRq7z}jRx7N)h`2*0eoHQmybYBaKu zepz#>wRL%>rB>UG^2^#&9nJMeryE*TdZV*sHJOG?i?-X%FUz#bQ?oIC9x-$PA0A{e z-^G5J&Ud=0CR2+TnY@qn>ojLt+wjaab7Xzm#BSUWxh#J&e3HiQemSG2W4?!+i5oRbZtuqo+vJ5$CNq#vBE!~2rqOIu;^Q3ZLzdQ?HsbAKLde>Utmeyt0(SQvcsK3SY zM(tIK*&Z#cys&H`F(p=ej|EwJoW- zHeK39S$6eJD6S# zpME7M%d{;`vy~SgHcFnFvcxtcVnHoD#BP^IBg+@$5J>6$ckXX``!@D=ujXSMv+fL2TT zsP=TL>g!EEF7lcN3^?_*sb#79hE#Qf8Iw$3E;{VN+m2~YdrE@E^ykVCVVF+OQ)KHk z{k!N%*|Z*mp`|(9qE@)5WBPj00py^DijnE(JwNTu?5h1`WBPf~QDRfVg4y)-CLNy# zk(<6=E}hZEZtIHSD&uU6F<}Ei>^w8TaFqUuwJVo zqw;O~e$lH<2hubBzQ`C1#6Z)MZqBr{nLKE{GQVC;18PuBs!8pOYO7DR**3kuU$4Hs>BuIGuU_?#m{0cW zsFuyKN3c)v%UV*)EezdWqkg%JtF`GiU%yfAm#cY=JT*3RnwVd%rZd<@pmz5#c2k4$ z#%Am+>uAn2)Ylx-fi5UrzbtL%E7O9yoEmkswl`zoX>Em;pWm$K;$oZd%UTgYwZ%_jnjQDqeqHn`t@YTMicP7yIITL%^B})&&~Ix8nqFqP zSDthHy3);ZPT7JjlB!+K4akSIE7KWDe&+dgr4r-jp}8Sd-_%&&ij{seK&c<|{d&#q z)eZHn81B6W!#phT>#!j=q>kZK)6-J;4)*J{wl=gM-GQbjPlJ$2rBfNuNw=WOlKY)t zcdv9826UV1>*~_A*!G!o{HXHlRxYTnN2@W@DCQv`hYK_NTI9Dw!y%&DjyV#;WKP*! zk$oobftRQY~xuYvbvly)$cCJDO2rwX--+4_^i()v}`6F`6=NkEux#jbW zydk%Ieoi^-6{;O~2ybHk8>7gsP=}217p@b4p}lw#*FrB(B(bJ&#~3Bcra=2 zNt!aXxB>I;7Y#YWOE;t&Ifh7!4lADBmS&Jf6kql%vqcmsx`wkE3_+OsV!w|bNu;i!L=_KM;w)KROKOQ2YTRR$yeWebj@c=$9JSXtT>@WMB~Y7bY{DyO@-WEUX8L$2 z1Nizc-Dz&vbm|7)smOiG*+~Bz_&+nmQaumPL77{u$E90qjzpWmO=gpdaLD7;wKvsJ zLF#(g<1b6K2wO@#;_(?QPT@tr$m7>HHe(7=-ZgwgN0A2)6Ga9iV^{wouy$ zG_Csz^w<|bkF~Z94Z3)c`{`BNuEA~5lC-pL+%{rUMKx}bq-KO1pR5)gINohiVpaNK zzZ~0)Io~q(5RojIsj2>ZxFWv}R$Vctv+JKD{4zbBaKs|!k$#ziScf}xJAWJHm$TPk zX=0qiesYP}MJi%m=4{YBmMO{a>ZpAePY{Y_544Kf(Txop(b$)Dn4wKYCFJ;JGL%U) z!vQ`zKp7O2O(Y;Qu~MZ_6qL1@S?Y)Yzq#Jjosj{)S{Y+q=8h4ri(R5}Pmm~0HFTLl z#?LenxD@&v6Mn=oK|iD5UYTXxefyH}f>N`y$++%7K2ZRp1gVolK^3*j+c+Krx z{PXeg%Y@WLhiTX*9>Uj+Lh*`}u5AQt1Li%G%T=Ce6les7Ve;~Q=&=nPByHev{py&Wa+i%LiSCuXuVRaSC>v}jQd%h+?QjXj-B5$#F~t07iCrlHx<~=sa`)r zKVLU$ul;pTcLiEH2k3e}8baE-repa)wrZ-;JBQHp7FWWaqxf-n2vj_HpPlDn)fg$J zk8ZA4i?w{uMvY8iN6FgQ4`k{(pYRyXZDB~y9WSf~uW3-l`>f@nNx(yFjZB5-x%P>h)OX#|k#&~?)tkbm; zcP%gW#iFqg6LYF$GFU8XrWuk$fYjrgIi5mCj$%O%>wV}UIqSf%DQW!(wy2DrvN!9n_L9zj=NZ(hjT?@~jwo-B z-8Q8EWBeZ}_jYymxjz=FYub)xBg36BZnC-Dr6P*?T|nkvKgZln$z<3tW@@A!ZDawT z*zlwKL%f@*Y+JB@83Tbo&8U%STb-ut7Gh0WY~*MIvJN&^VI9oo&r^ISWYbdm^EE%5 z#m9~(`ge*nzbS)01#200^ig?pMrYX_tnEsCcIsmQjkq;gYGy!ARLk)IoFj2g--|sT zoX~Pc(vVT}i145@sHLVuO{MhRQqdsy0tWq4$ox=d%Tey$@a9wswG;@{yWx6v1wC7) zGJD(54$aYn-(I_k!tOzZ?|z6QWvm$&tP>-eempC8j&r3-HZT-ErsURj@pI>?1UV( z*M7Q%|5UedsoY-c7E)UFvj6w*By7U>GrLx=Jf$B$3$Td|T<;Osj8-92}$;Xn$ z9c_KedkQsBibsanii|1#@cLuzcXjaxc?CiLAyvvu;~TB(sUJs{3J|ZXAM3l;MaeX+Df4Tv){;os_Osb z44hAOTnzsca65})F(NZBTZ>!)# z5QDb$rhmM8V)`~vdbZm7GbgGqj){;tm6EBHzB2YN{T7s(V3f9wIMF7B&?_4dDRaB#B)E>hCTuTT zT%4j!?YzyuN3UVUG(sk0T9F7`--L}Bd3ABHaGWw7^|`TZ`j=jfU`(0ra}a%c6n|~_ zbCRk|_vu+gDO-9brf?2qYVY8MQ886>A%a*rU1!jkb76CP4qhQ(*|j4u1#sO%@&G5B zz=5=Z3DtqTA2h9d6(kMb*BZT%bsW@(V zaFSEozF?eksXczV&YvWy2>oX!>yu82sQ9SzI;QjM^h^CuChIf1PG~kO3Af`?T3whd zqjO&BjDp*DX+BN*@~YXNR*rcRT;O&}&hps((?MgYeH3-LZ)U-E+&80JRB9a`fUQIL z^Wm|u>sp^Lwbs;>6rUkBbPSd941AnfDv#>~w_jlU6WtA` zC#dSd*)Re7sIIZU(VcarD|Meobl)-)^`ks^5&>-b%HYC-Z;f0EO$HHr$=ZzM6Pga7-zaJN9w9NR{! zG%{Husr$B21h-RyY?4%r^4Lc@7uJqzicj zhmuR%pXSP}(I^%*{Vpz|`2u5XZKnQ zt3#Fs<*kd-zKXt$lDG4d_KM2e`AQ>bE3csrPW zi&0cBF1N0G4f*P7cXc7hkP}`X8>XWOsy*RiItNrI56-)tWzD>EPvk zv$`y~@Xe@P6cqN_Y6o*&Jqvf4V7wubISp@tYLEbm6o~+Rjw$}OQv9-(iN1=T&j!?) zV_7fhKjL;$nG0DjFm)Kp=pp9?*4$Fpi9<00gNzrL%zp#+Ze1Bu70yjfa+b7R$a%rf zwMde*Pzdr}U`UzEy7+FD!r%sg_rp>c_Zr*xWBREp@un-z{=0Ve$?`0Zrr!Y^McyMt z@V914%x}E^J`4x!)d(wal`P)nY)IZ_wy49--A6_b6l*e$7+e;zaEoZWL~MmR&j@ zaW0-4!&y7^=fdVs;^YfqAz#~;ZL{$Z*zn16*iJ3QMk+Y%`9`>?bp{XxOX3xgE7yf| zBjz`>B1EjRcxp+aeMwvhYb5N{*LK>2d+a;$#_vDGNCS^BMIKfJ>z!}4IlF*Q8a-wG zQDwrh@M)C8^Ei7daE`SNv&d9IBn#8)iW9$$(Wzlu-i5RlI?B~As~GGva>ebS$+~oM zgt=O`az~`?d;!Pqnp$8xA{#u!DUm`!@R%Xq&Wu}?({@7Ho928IF3Mt^wytcn+LPt= zO>x=uRI&_l{d`UmQC!%qd6Ru(R&J1m^*12nX7n?GB$Sxc7wE$Pr6|`*)FwSXVKWVG+G{*}B{VQ)<3Nhng0Q0rT3#qbBCS(Xg}$(hkp@zx zZCkji;p#z1w^hvs(+I~+2N$(1ALzx66hpcG!KUs(T+*<7uxFWkw-+Bw7C-1j7~n^0 z(9rH?pe^oE-U4!@+m9;BeXY(mF6@H~&fuY=1k?8r7+ z3RX?nk8*c0MgwoL^O2@8sxm+Dn754YE*>Av$*zo2>TC@trMEg90Ojyuc76@MWd*vwOM#*H}n8#3$xb!$Px-ao2BF*x&9|B{U%JH%Yk&W)4-RKT` z#Yv%lVow9kmjIK!PjPq~oW4VDzf0G+JuKhdyS)9X)x-Ek5h6`iIyCeTF2g;U4$A%@ zo5$@17lTIhnP_ps2rCCVn$7_}?WGrJFX?`MIcNh?h`;3G)Fp_vrax185p=R~e*b!> zYIf#&^J8<1xx;MmZuB;K_j|{@6W(*)pS)Avi{9(ro8D%ByT8pp;Q!43g@3>QuwOcf zmMUjm_$lN0!N7NhSFg+5oY|P!l-ZHlnYlZ2Ci70_Y^G$cHQzJeH`kf#%?5J=Ua`?^ z!fS3dx8YS=%{FtF*=}~2o#t+{%j`Dyn!V;Bv)}yA95#=eBjyS7q&aGyGEbXh=D7Kz zIbohL&zk4VN%OpU!MtMLFmIVN=B)X^jA+Ly`X<{(Aog z|3?32{}%stf3v^E-wIOP34-kOf9mh@?*UPE`#<;Z71BK5@AV%9fgbVq`TIezgZ`ub zA^$P|u>ZJ!#DCxa!2i%6@z({{1wROG3^oS026qHogYCi2U{|m^xG&ff>qkf`nKxL)pKw<_@dG!SRFI3KpNFRO;2~%>ZuhYBbd0<6)To68LpW(@1}|h z1bqdvroXplRW~-TRLVkXy79fNVd($(5n5T%!T%t5>WP7#1*a)(|!E6P%U3 z0;7wfvf?Yv2?RU{4n7#j9|RC^#x!Gb@bz#0xRmh?`%Ust_K&nPv{oQEU;~^!-o}>r W2~PJ?fx-C#oH?r!2zX#0&i@~^;pB$^ literal 0 HcmV?d00001 diff --git a/r5dedicated/cnetchan.cpp b/r5dedicated/cnetchan.cpp deleted file mode 100644 index bfd915ba..00000000 --- a/r5dedicated/cnetchan.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "pch.h" -#include "cnetchan.h" - -//----------------------------------------------------------------------------- -// Hook and log the receive datagram -//----------------------------------------------------------------------------- -bool HNET_ReceiveDatagram(int sock, void* inpacket, bool raw) -{ - bool result = org_NET_ReceiveDatagram(sock, inpacket, raw); - if (result) - { - int i = NULL; - netpacket_t* pkt = (netpacket_t*)inpacket; - - // Log received packet data - HexDump("[+] NET_ReceiveDatagram", 0, &pkt->data[i], pkt->wiresize); - } - return result; -} - -//----------------------------------------------------------------------------- -// Hook and log send datagram -//----------------------------------------------------------------------------- -unsigned int HNET_SendDatagram(SOCKET s, const char* buf, int len, int flags) -{ - unsigned int result = org_NET_SendDatagram(s, buf, len, flags); - if (result) - { - // Log transmitted packet data - HexDump("[+] NET_SendDatagram", 0, buf, len); - } - return result; -} - -/////////////////////////////////////////////////////////////////////////////// -void AttachCNetChanHooks() -{ - DetourAttach((LPVOID*)&org_NET_ReceiveDatagram, &HNET_ReceiveDatagram); - DetourAttach((LPVOID*)&org_NET_SendDatagram, &HNET_SendDatagram); -} - -void DetachCNetChanHooks() -{ - DetourDetach((LPVOID*)&org_NET_ReceiveDatagram, &HNET_ReceiveDatagram); - DetourDetach((LPVOID*)&org_NET_SendDatagram, &HNET_SendDatagram); -} \ No newline at end of file diff --git a/r5dedicated/cnetchan.h b/r5dedicated/cnetchan.h deleted file mode 100644 index b0e425db..00000000 --- a/r5dedicated/cnetchan.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "pch.h" -#include "hooks.h" - -bool HNET_ReceiveDatagram(int sock, void* inpacket, bool raw); -unsigned int HNET_SendDatagram(SOCKET s, const char* buf, int len, int flags); - -void AttachCNetChanHooks(); -void DetachCNetChanHooks(); - -typedef unsigned __int64 QWORD; -struct __declspec(align(8)) netpacket_t -{ - DWORD family_maybe; - sockaddr_in sin; - WORD sin_port; - BYTE gap16; - BYTE byte17; - DWORD source; - double received; - unsigned __int8* data; - QWORD label; - BYTE byte38; - QWORD qword40; - QWORD qword48; - BYTE gap50[8]; - QWORD qword58; - QWORD qword60; - QWORD qword68; - int less_than_12; - DWORD wiresize; - BYTE gap78[8]; - QWORD qword80; -}; \ No newline at end of file diff --git a/r5dedicated/concommand.cpp b/r5dedicated/concommand.cpp deleted file mode 100644 index bbe14b4d..00000000 --- a/r5dedicated/concommand.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "pch.h" -#include "concommand.h" - -//----------------------------------------------------------------------------- -// Purpose: test each ConCommand query before execution -// Input : *cmd - flag -// Output : true if execution is not permitted, false if permitted -//----------------------------------------------------------------------------- -bool HConCommand_IsFlagSet(int* cmd, int flag) -{ - int real_flags = *((cmd + (56 / sizeof(int)))); - if (g_bDebugConsole) - { - printf("--------------------------------------------------\n"); - printf(" Flaged: %08X\n", real_flags); - } - // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY - real_flags &= 0xFFFFBFFD; - if (g_bDebugConsole) - { - printf(" Masked: %08X\n", real_flags); - printf(" Verify: %08X\n", flag); - printf("--------------------------------------------------\n"); - } - if (flag & 0x80000) { return true; } - - if (!g_bReturnAllFalse) { return (real_flags & flag) != 0; } - else { return false; } // Returning false on all queries may cause problems -} - -void AttachConCommandHooks() -{ - DetourAttach((LPVOID*)&org_ConCommand_IsFlagSet, &HConCommand_IsFlagSet); -} - -void DetachConCommandHooks() -{ - DetourDetach((LPVOID*)&org_ConCommand_IsFlagSet, &HConCommand_IsFlagSet); -} \ No newline at end of file diff --git a/r5dedicated/concommand.h b/r5dedicated/concommand.h deleted file mode 100644 index 6442c1e1..00000000 --- a/r5dedicated/concommand.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "hooks.h" - -bool HConCommand_IsFlagSet(int* cmd, int flag); - -void AttachConCommandHooks(); -void DetachConCommandHooks(); \ No newline at end of file diff --git a/r5dedicated/console.cpp b/r5dedicated/console.cpp index 8c22fb47..3d77c848 100644 --- a/r5dedicated/console.cpp +++ b/r5dedicated/console.cpp @@ -1,8 +1,6 @@ #include "pch.h" #include "hooks.h" #include "console.h" -#include "iconvar.h" -#include "concommand.h" //############################################################################# // INITIALIZATION @@ -71,8 +69,8 @@ DWORD __stdcall ProcessConsoleWorker(LPVOID) /////////////////////////////////////////////////////////////////////// // Engine toggles - if (sCommand == "toggle net") { ToggleNetTrace(); continue; } - if (sCommand == "toggle dev") { ToggleDevCommands(); continue; } + if (sCommand == "toggle net") { Hooks::ToggleNetHooks(); continue; } + if (sCommand == "toggle dev") { Hooks::ToggleDevCommands(); continue; } if (sCommand == "toggle fal") { g_bReturnAllFalse = !g_bReturnAllFalse; continue; } /////////////////////////////////////////////////////////////////////// // Debug toggles @@ -80,12 +78,12 @@ DWORD __stdcall ProcessConsoleWorker(LPVOID) if (sCommand == "console test") { g_bDebugConsole = !g_bDebugConsole; continue; } /////////////////////////////////////////////////////////////////////// // Exec toggles - if (sCommand == "1") { org_CommandExecute(NULL, "exec autoexec_dev"); } + if (sCommand == "1") { addr_CommandExecute(NULL, "exec autoexec_dev"); } if (sCommand == "2") { g_bDebugLoading = !g_bDebugLoading; continue; } /////////////////////////////////////////////////////////////////////// // Execute the command in the r5 SQVM - org_CommandExecute(NULL, sCommand.c_str()); + addr_CommandExecute(NULL, sCommand.c_str()); sCommand.clear(); /////////////////////////////////////////////////////////////////////// diff --git a/r5dedicated/cvengineserver.cpp b/r5dedicated/cvengineserver.cpp index bf15cee5..450639f4 100644 --- a/r5dedicated/cvengineserver.cpp +++ b/r5dedicated/cvengineserver.cpp @@ -1,10 +1,15 @@ #include "pch.h" -#include "cvengineserver.h" +#include "hooks.h" + +namespace Hooks +{ + IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable = nullptr; +} //----------------------------------------------------------------------------- -// Sets the persistence var in the playerstruct to ready for each client +// Sets the persistence var in the playerstruct to ready for each client //----------------------------------------------------------------------------- -bool HPersistence_IsAvailable(__int64 thisptr, int client) +bool Hooks::IsPersistenceDataAvailable(__int64 thisptr, int client) { static bool isPersistenceVarSet[256]; @@ -27,16 +32,5 @@ bool HPersistence_IsAvailable(__int64 thisptr, int client) isPersistenceVarSet[client] = true; } - /////////////////////////////////////////////////////////////////////////// - return org_Persistence_IsAvailable(thisptr, client); -} - -void AttachCEngineServerHooks() -{ - DetourAttach((LPVOID*)&org_Persistence_IsAvailable, &HPersistence_IsAvailable); -} - -void DetachCEngineServerHooks() -{ - DetourDetach((LPVOID*)&org_Persistence_IsAvailable, &HPersistence_IsAvailable); + return originalIsPersistenceDataAvailable(thisptr, client); } \ No newline at end of file diff --git a/r5dedicated/cvengineserver.h b/r5dedicated/cvengineserver.h deleted file mode 100644 index 933c2a15..00000000 --- a/r5dedicated/cvengineserver.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "pch.h" -#include "hooks.h" - -bool HPersistence_IsAvailable(__int64 thisptr, int client); - -void AttachCEngineServerHooks(); -void DetachCEngineServerHooks(); \ No newline at end of file diff --git a/r5dedicated/dllmain.cpp b/r5dedicated/dllmain.cpp index 8d135d1e..89df758c 100644 --- a/r5dedicated/dllmain.cpp +++ b/r5dedicated/dllmain.cpp @@ -10,7 +10,7 @@ void InitializeR5Dedicated() { SetupConsole(); - InstallHooks(); + Hooks::InstallHooks(); printf("+-----------------------------------------------------------------------------+\n"); printf("| R5 DEDICATED SERVER --------------------------------------------------- |\n"); printf("+-----------------------------------------------------------------------------+\n"); @@ -20,7 +20,7 @@ void InitializeR5Dedicated() void TerminateR5Dedicated() { FreeConsole(); - RemoveHooks(); + Hooks::RemoveHooks(); } //############################################################################# diff --git a/r5dedicated/hooks.cpp b/r5dedicated/hooks.cpp index 90e52c41..67fa4dc9 100644 --- a/r5dedicated/hooks.cpp +++ b/r5dedicated/hooks.cpp @@ -1,78 +1,99 @@ #include "pch.h" #include "hooks.h" -#include "iconvar.h" -#include "concommand.h" -#include "cvengineserver.h" -#include "cnetchan.h" -#include "sqvm.h" -#include "msgbox.h" #include "opcodes.h" -//################################################################################# -// MANAGEMENT -//################################################################################# - -void InstallHooks() +void Hooks::InstallHooks() { /////////////////////////////////////////////////////////////////////////////// - // Begin the detour transaction, to hook the the process - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); + // Initialize Minhook + MH_Initialize(); + + // Hook Squirrel functions + MH_CreateHook(addr_SQVM_Print, &Hooks::SQVM_Print, NULL); + MH_CreateHook(addr_SQVM_LoadRson, &Hooks::SQVM_LoadRson, reinterpret_cast(&originalSQVM_LoadRson)); + MH_CreateHook(addr_SQVM_LoadScript, &Hooks::SQVM_LoadScript, reinterpret_cast(&originalSQVM_LoadScript)); /////////////////////////////////////////////////////////////////////////////// - // Hook functions - AttachIConVarHooks(); - AttachConCommandHooks(); - AttachCEngineServerHooks(); - AttachSQVMHooks(); - AttachMSGBoxHooks(); + // Hook Game Functions + // MH_CreateHook(addr_CHLClient_FrameStageNotify, &Hooks::FrameStageNotify, reinterpret_cast(&originalFrameStageNotify)); + MH_CreateHook(addr_CVEngineServer_IsPersistenceDataAvailable, &Hooks::IsPersistenceDataAvailable, reinterpret_cast(&originalIsPersistenceDataAvailable)); /////////////////////////////////////////////////////////////////////////////// - // Commit the transaction - if (DetourTransactionCommit() != NO_ERROR) - { - // Failed to hook into the process, terminate - TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); - } + // Hook Netchan functions + MH_CreateHook(addr_NET_ReceiveDatagram, &Hooks::NET_ReceiveDatagram, reinterpret_cast(&originalNET_ReceiveDatagram)); + MH_CreateHook(addr_NET_SendDatagram, &Hooks::NET_SendDatagram, reinterpret_cast(&originalNET_SendDatagram)); + + /////////////////////////////////////////////////////////////////////////////// + // Hook ConVar | ConCommand functions. + MH_CreateHook(addr_ConVar_IsFlagSet, &Hooks::ConVar_IsFlagSet, NULL); + MH_CreateHook(addr_ConCommand_IsFlagSet, &Hooks::ConCommand_IsFlagSet, NULL); + + /////////////////////////////////////////////////////////////////////////////// + // Hook Utility functions + MH_CreateHook(addr_MSG_EngineError, &Hooks::MSG_EngineError, reinterpret_cast(&originalMSG_EngineError)); + + /////////////////////////////////////////////////////////////////////////////// + // Enable Squirrel hooks + MH_EnableHook(addr_SQVM_Print); + MH_EnableHook(addr_SQVM_LoadRson); + MH_EnableHook(addr_SQVM_LoadScript); + + /////////////////////////////////////////////////////////////////////////////// + // Enable Game hooks + MH_EnableHook(addr_CHLClient_FrameStageNotify); + MH_EnableHook(addr_CVEngineServer_IsPersistenceDataAvailable); + + /////////////////////////////////////////////////////////////////////////////// + // Enabled Utility hooks + MH_EnableHook(addr_MSG_EngineError); InstallOpcodes(); } -void RemoveHooks() +void Hooks::RemoveHooks() { /////////////////////////////////////////////////////////////////////////////// - // Begin the detour transaction, to unhook the the process - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); + // Unhook Squirrel functions + MH_RemoveHook(addr_SQVM_Print); + MH_RemoveHook(addr_SQVM_LoadRson); + MH_RemoveHook(addr_SQVM_LoadScript); /////////////////////////////////////////////////////////////////////////////// - // Unhook functions - DetachIConVarHooks(); - DetachConCommandHooks(); - DetachCEngineServerHooks(); - DetachSQVMHooks(); - DetachMSGBoxHooks(); + // Unhook Game Functions + MH_RemoveHook(addr_CHLClient_FrameStageNotify); + MH_RemoveHook(addr_CVEngineServer_IsPersistenceDataAvailable); /////////////////////////////////////////////////////////////////////////////// - // Commit the transaction - DetourTransactionCommit(); + // Unhook Netchan functions + MH_RemoveHook(addr_NET_ReceiveDatagram); + MH_RemoveHook(addr_NET_SendDatagram); + + /////////////////////////////////////////////////////////////////////////////// + // Unhook ConVar | ConCommand functions. + MH_RemoveHook(addr_ConVar_IsFlagSet); + MH_RemoveHook(addr_ConCommand_IsFlagSet); + + /////////////////////////////////////////////////////////////////////////////// + // Unhook Utility functions + MH_RemoveHook(addr_MSG_EngineError); + + /////////////////////////////////////////////////////////////////////////////// + // Reset Minhook + MH_Uninitialize(); } //################################################################################# // TOGGLES //################################################################################# -void ToggleDevCommands() +void Hooks::ToggleDevCommands() { - static bool bDev = true; - - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); + static bool bDev = true;; if (!bDev) { - AttachIConVarHooks(); - AttachConCommandHooks(); + MH_EnableHook(addr_ConVar_IsFlagSet); + MH_EnableHook(addr_ConCommand_IsFlagSet); printf("\n"); printf("+--------------------------------------------------------+\n"); printf("|>>>>>>>>>>>>>| DEVONLY COMMANDS ACTIVATED |<<<<<<<<<<<<<|\n"); @@ -82,8 +103,8 @@ void ToggleDevCommands() } else { - DetachIConVarHooks(); - DetachConCommandHooks(); + MH_DisableHook(addr_ConVar_IsFlagSet); + MH_DisableHook(addr_ConCommand_IsFlagSet); printf("\n"); printf("+--------------------------------------------------------+\n"); printf("|>>>>>>>>>>>>| DEVONLY COMMANDS DEACTIVATED |<<<<<<<<<<<<|\n"); @@ -91,24 +112,17 @@ void ToggleDevCommands() printf("\n"); } - if (DetourTransactionCommit() != NO_ERROR) - { - TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); - } - bDev = !bDev; } -void ToggleNetTrace() +void Hooks::ToggleNetHooks() { static bool bNet = true; - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - if (!bNet) { - AttachCNetChanHooks(); + MH_EnableHook(addr_NET_ReceiveDatagram); + MH_EnableHook(addr_NET_SendDatagram); printf("\n"); printf("+--------------------------------------------------------+\n"); printf("|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); @@ -117,7 +131,8 @@ void ToggleNetTrace() } else { - DetachCNetChanHooks(); + MH_DisableHook(addr_NET_ReceiveDatagram); + MH_DisableHook(addr_NET_SendDatagram); printf("\n"); printf("+--------------------------------------------------------+\n"); printf("|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); @@ -125,10 +140,5 @@ void ToggleNetTrace() printf("\n"); } - if (DetourTransactionCommit() != NO_ERROR) - { - TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); - } - bNet = !bNet; } diff --git a/r5dedicated/hooks.h b/r5dedicated/hooks.h index cdded7a3..66ab2187 100644 --- a/r5dedicated/hooks.h +++ b/r5dedicated/hooks.h @@ -1,11 +1,5 @@ #pragma once #include "pch.h" -#include "iconvar.h" -#include "concommand.h" -#include "cvengineserver.h" -#include "cnetchan.h" -#include "sqvm.h" -#include "msgbox.h" // Define the signatures or offsets to be searched and hooked namespace { @@ -13,77 +7,130 @@ namespace #pragma region Console /*0x140202090*/ - FUNC_AT_ADDRESS(org_CommandExecute, void(*)(void*, const char*), r5_patterns.PatternSearch("48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8").GetPtr()); + FUNC_AT_ADDRESS(addr_CommandExecute, void(*)(void*, const char*), r5_patterns.PatternSearch("48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8").GetPtr()); /*0x14046FE90*/ - FUNC_AT_ADDRESS(org_IConVar_IsFlagSet, bool(*)(int**, int), r5_patterns.PatternSearch("48 8B 41 48 85 50 38").GetPtr()); + FUNC_AT_ADDRESS(addr_ConVar_IsFlagSet, bool(*)(int**, int), r5_patterns.PatternSearch("48 8B 41 48 85 50 38").GetPtr()); /*0x14046F490*/ - FUNC_AT_ADDRESS(org_ConCommand_IsFlagSet, bool(*)(int*, int), r5_patterns.PatternSearch("85 51 38 0F 95 C0 C3").GetPtr()); + FUNC_AT_ADDRESS(addr_ConCommand_IsFlagSet, bool(*)(int*, int), r5_patterns.PatternSearch("85 51 38 0F 95 C0 C3").GetPtr()); #pragma endregion #pragma region Squirrel /*0x141057FD0*/ - FUNC_AT_ADDRESS(org_SQVM_PrintFunc, void*, r5_patterns.PatternSearch("83 F8 01 48 8D 3D ? ? ? ?").OffsetSelf(0x3).FollowNearCallSelf(0x3, 0x7).GetPtr()); + FUNC_AT_ADDRESS(addr_SQVM_Print, void*, r5_patterns.PatternSearch("83 F8 01 48 8D 3D ? ? ? ?").OffsetSelf(0x3).FollowNearCallSelf(0x3, 0x7).GetPtr()); //DWORD64 p_SQVM_LoadScript = FindPattern("r5apex.exe", (const unsigned char*)"\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); // For S0 and S1 /*0x141055630*/ // For anything S2 and above (current S8 - FUNC_AT_ADDRESS(org_SQVM_LoadScript, bool(*)(void*, const char*, const char*, int), r5_patterns.PatternSearch("48 8B C4 48 89 48 08 55 41 56 48 8D 68").GetPtr()); + FUNC_AT_ADDRESS(addr_SQVM_LoadScript, bool(*)(void*, const char*, const char*, int), r5_patterns.PatternSearch("48 8B C4 48 89 48 08 55 41 56 48 8D 68").GetPtr()); /*0x140C957E0*/ - FUNC_AT_ADDRESS(org_SQVM_LoadRson, int(*)(const char*), r5_patterns.PatternSearch("4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33").GetPtr()); + FUNC_AT_ADDRESS(addr_SQVM_LoadRson, int(*)(const char*), r5_patterns.PatternSearch("4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33").GetPtr()); #pragma endregion #pragma region NetChannel /*0x1402655F0*/ - FUNC_AT_ADDRESS(org_NET_ReceiveDatagram, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr()); + FUNC_AT_ADDRESS(addr_NET_ReceiveDatagram, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr()); /*0x1402662D0*/ - FUNC_AT_ADDRESS(org_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); + FUNC_AT_ADDRESS(addr_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); #pragma endregion #pragma region CHLClient /*0x1405C0740*/ - FUNC_AT_ADDRESS(org_CHLClient_FrameStageNotify, void(*)(void* rcx, int curStage), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); + FUNC_AT_ADDRESS(addr_CHLClient_FrameStageNotify, void(*)(void* rcx, int curStage), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); #pragma endregion #pragma region CVEngineServer /*0x140315CF0*/ - FUNC_AT_ADDRESS(org_Persistence_IsAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); + FUNC_AT_ADDRESS(addr_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); #pragma endregion #pragma region Utility /*0x140295600*/ - FUNC_AT_ADDRESS(org_MSG_EngineError, int(*)(char*, va_list), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 00 00 48 8B DA").GetPtr()); + FUNC_AT_ADDRESS(addr_MSG_EngineError, int(*)(char*, va_list), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 00 00 48 8B DA").GetPtr()); #pragma endregion - - inline bool g_bDebugConsole = false; - inline bool g_bReturnAllFalse = false; - inline bool g_bDebugLoading = false; + // Un-used atm. + // DWORD64 p_KeyValues_FindKey = /*1404744E0*/ reinterpret_cast(PatternScan("r5apex.exe", "40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45")); void PrintHAddress() // Test the sigscan results { std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("CommandExecute", org_CommandExecute); - PRINT_ADDRESS("IConVar_IsFlagSet", org_IConVar_IsFlagSet); - PRINT_ADDRESS("ConCommand_IsFlagSet", org_ConCommand_IsFlagSet); - PRINT_ADDRESS("SQVM_Print", org_SQVM_PrintFunc); - PRINT_ADDRESS("SQVM_LoadScript", org_SQVM_LoadScript); - PRINT_ADDRESS("SQVM_LoadRson", org_SQVM_LoadRson); - PRINT_ADDRESS("NET_ReceiveDatagram", org_NET_ReceiveDatagram); - PRINT_ADDRESS("NET_SendDatagram ", org_NET_SendDatagram); - PRINT_ADDRESS("CHLClient::FrameStageNotify", org_CHLClient_FrameStageNotify); - PRINT_ADDRESS("CVEngineServer::Persistence_IsAvailable", org_Persistence_IsAvailable); - PRINT_ADDRESS("MSG_EngineError", org_MSG_EngineError); + PRINT_ADDRESS("CommandExecute", addr_CommandExecute); + PRINT_ADDRESS("ConVar_IsFlagSet", addr_ConVar_IsFlagSet); + PRINT_ADDRESS("ConCommand_IsFlagSet", addr_ConCommand_IsFlagSet); + PRINT_ADDRESS("SQVM_Print", addr_SQVM_Print); + PRINT_ADDRESS("SQVM_LoadScript", addr_SQVM_LoadScript); + PRINT_ADDRESS("SQVM_LoadRson", addr_SQVM_LoadRson); + PRINT_ADDRESS("NET_ReceiveDatagram", addr_NET_ReceiveDatagram); + PRINT_ADDRESS("NET_SendDatagram ", addr_NET_SendDatagram); + PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify); + PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable); + PRINT_ADDRESS("MSG_EngineError", addr_MSG_EngineError); std::cout << "+--------------------------------------------------------+" << std::endl; // TODO implement error handling when sigscan fails or result is 0 } } -void InstallHooks(); -void RemoveHooks(); +inline bool g_bDebugLoading = false; +inline bool g_bReturnAllFalse = false; +inline bool g_bDebugConsole = false; -void ToggleDevCommands(); -void ToggleNetTrace(); \ No newline at end of file +namespace Hooks +{ +#pragma region CHLClient +// void __fastcall FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage); + +// using FrameStageNotifyFn = void(__fastcall*)(CHLClient*, ClientFrameStage_t); +// extern FrameStageNotifyFn originalFrameStageNotify; +#pragma endregion + +#pragma region Squirrel + void* SQVM_Print(void* sqvm, char* fmt, ...); + __int64 SQVM_LoadRson(const char* rson_name); + bool SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag); + + using SQVM_LoadRsonFn = __int64(*)(const char*); + extern SQVM_LoadRsonFn originalSQVM_LoadRson; + + using SQVM_LoadScriptFn = bool(*)(void*, const char*, const char*, int); + extern SQVM_LoadScriptFn originalSQVM_LoadScript; +#pragma endregion + +#pragma region CVEngineServer + bool IsPersistenceDataAvailable(__int64 thisptr, int client); + + using IsPersistenceDataAvailableFn = bool(*)(__int64, int); + extern IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable; +#pragma endregion + +#pragma region NetChannel + bool NET_ReceiveDatagram(int sock, void* inpacket, bool raw); + unsigned int NET_SendDatagram(SOCKET s, const char* buf, int len, int flags); + + using NET_ReceiveDatagramFn = bool(*)(int, void*, bool); + extern NET_ReceiveDatagramFn originalNET_ReceiveDatagram; + + using NET_SendDatagramFn = unsigned int(*)(SOCKET, const char*, int, int); + extern NET_SendDatagramFn originalNET_SendDatagram; +#pragma endregion + +#pragma region ConVar + bool ConVar_IsFlagSet(int** cvar, int flag); + bool ConCommand_IsFlagSet(int* cmd, int flag); +#pragma endregion + +#pragma region Other + int MSG_EngineError(char* fmt, va_list args); + + using MSG_EngineErrorFn = int(*)(char*, va_list); + extern MSG_EngineErrorFn originalMSG_EngineError; +#pragma endregion + + void InstallHooks(); + void RemoveHooks(); + void ToggleNetHooks(); + void ToggleDevCommands(); +} \ No newline at end of file diff --git a/r5dedicated/iconvar.cpp b/r5dedicated/iconvar.cpp index 169c9060..75002dff 100644 --- a/r5dedicated/iconvar.cpp +++ b/r5dedicated/iconvar.cpp @@ -1,12 +1,13 @@ #include "pch.h" -#include "iconvar.h" +#include "hooks.h" //----------------------------------------------------------------------------- // Purpose: test each ConVar query before setting the cvar // Input : **cvar - flag // Output : true if change is not permitted, false if permitted //----------------------------------------------------------------------------- -bool HConVar_IsFlagSet(int** cvar, int flag) + +bool Hooks::ConVar_IsFlagSet(int** cvar, int flag) { int real_flags = *(*(cvar + (72 / (sizeof(void*)))) + (56 / sizeof(int))); if (g_bDebugConsole) @@ -24,16 +25,46 @@ bool HConVar_IsFlagSet(int** cvar, int flag) } if (flag & 0x80000) { return true; } - if (!g_bReturnAllFalse) { return (real_flags & flag) != 0; } - else { return false; } // Returning false on all queries may cause problems + if (!g_bReturnAllFalse) + { + return (real_flags & flag) != 0; + } + else + { + return false; + } } -void AttachIConVarHooks() -{ - DetourAttach((LPVOID*)&org_IConVar_IsFlagSet, &HConVar_IsFlagSet); -} +//----------------------------------------------------------------------------- +// Purpose: test each ConCommand query before execution +// Input : *cmd - flag +// Output : true if execution is not permitted, false if permitted +//----------------------------------------------------------------------------- -void DetachIConVarHooks() +bool Hooks::ConCommand_IsFlagSet(int* cmd, int flag) { - DetourDetach((LPVOID*)&org_IConVar_IsFlagSet, &HConVar_IsFlagSet); + int real_flags = *((cmd + (56 / sizeof(int)))); + if (g_bDebugConsole) + { + printf("--------------------------------------------------\n"); + printf(" Flaged: %08X\n", real_flags); + } + // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY + real_flags &= 0xFFFFBFFD; + if (g_bDebugConsole) + { + printf(" Masked: %08X\n", real_flags); + printf(" Verify: %08X\n", flag); + printf("--------------------------------------------------\n"); + } + if (flag & 0x80000) { return true; } + + if (!g_bReturnAllFalse) + { + return(real_flags & flag) != 0; + } + else + { + return false; + } } \ No newline at end of file diff --git a/r5dedicated/iconvar.h b/r5dedicated/iconvar.h deleted file mode 100644 index 25880877..00000000 --- a/r5dedicated/iconvar.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "hooks.h" - -bool HConVar_IsFlagSet(int** cvar, int flag); - -void AttachIConVarHooks(); -void DetachIConVarHooks(); \ No newline at end of file diff --git a/r5dedicated/msgbox.cpp b/r5dedicated/msgbox.cpp index 24e1ea15..58a7727b 100644 --- a/r5dedicated/msgbox.cpp +++ b/r5dedicated/msgbox.cpp @@ -1,24 +1,19 @@ #include "pch.h" #include "hooks.h" +namespace Hooks +{ + MSG_EngineErrorFn originalMSG_EngineError = nullptr; +} + //----------------------------------------------------------------------------- // Engine Error message box //----------------------------------------------------------------------------- -int HMSG_EngineError(char* fmt, va_list args) + +int Hooks::MSG_EngineError(char* fmt, va_list args) { printf("\nENGINE ERROR #####################################\n"); vprintf(fmt, args); - /////////////////////////////////////////////////////////////////////////// - return org_MSG_EngineError(fmt, args); -} - -void AttachMSGBoxHooks() -{ - DetourAttach((LPVOID*)&org_MSG_EngineError, &HMSG_EngineError); -} - -void DetachMSGBoxHooks() -{ - DetourDetach((LPVOID*)&org_MSG_EngineError, &HMSG_EngineError); + return originalMSG_EngineError(fmt, args); } \ No newline at end of file diff --git a/r5dedicated/msgbox.h b/r5dedicated/msgbox.h deleted file mode 100644 index ea1e094d..00000000 --- a/r5dedicated/msgbox.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "hooks.h" - -int HMSG_EngineError(char* fmt, va_list args); - -void AttachMSGBoxHooks(); -void DetachMSGBoxHooks(); \ No newline at end of file diff --git a/r5dedicated/net.cpp b/r5dedicated/net.cpp new file mode 100644 index 00000000..340a4be2 --- /dev/null +++ b/r5dedicated/net.cpp @@ -0,0 +1,71 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + NET_ReceiveDatagramFn originalNET_ReceiveDatagram = nullptr; + NET_SendDatagramFn originalNET_SendDatagram = nullptr; +} + +typedef unsigned __int64 QWORD; + +struct __declspec(align(8)) netpacket_t +{ + DWORD family_maybe; + sockaddr_in sin; + WORD sin_port; + BYTE gap16; + BYTE byte17; + DWORD source; + double received; + unsigned __int8* data; + QWORD label; + BYTE byte38; + QWORD qword40; + QWORD qword48; + BYTE gap50[8]; + QWORD qword58; + QWORD qword60; + QWORD qword68; + int less_than_12; + DWORD wiresize; + BYTE gap78[8]; + QWORD qword80; +}; + +//----------------------------------------------------------------------------- +// Hook and log the receive datagram +//----------------------------------------------------------------------------- + +bool Hooks::NET_ReceiveDatagram(int sock, void* inpacket, bool raw) +{ + bool result = originalNET_ReceiveDatagram(sock, inpacket, raw); + if (result) + { + int i = NULL; + netpacket_t* pkt = (netpacket_t*)inpacket; + + /////////////////////////////////////////////////////////////////////////// + // Log received packet data + HexDump("[+] NET_ReceiveDatagram", 0, &pkt->data[i], pkt->wiresize); + } + + return result; +} + +//----------------------------------------------------------------------------- +// Hook and log send datagram +//----------------------------------------------------------------------------- + +unsigned int Hooks::NET_SendDatagram(SOCKET s, const char* buf, int len, int flags) +{ + unsigned int result = originalNET_SendDatagram(s, buf, len, flags); + if (result) + { + /////////////////////////////////////////////////////////////////////////// + // Log transmitted packet data + HexDump("[+] NET_SendDatagram", 0, buf, len); + } + + return result; +} \ No newline at end of file diff --git a/r5dedicated/pch.h b/r5dedicated/pch.h index 975aba77..7159ba8a 100644 --- a/r5dedicated/pch.h +++ b/r5dedicated/pch.h @@ -3,7 +3,7 @@ #define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. #include -#include +#include #include #include #include diff --git a/r5dedicated/r5dedicated.vcxproj b/r5dedicated/r5dedicated.vcxproj index 7e6fe2bd..055520ed 100644 --- a/r5dedicated/r5dedicated.vcxproj +++ b/r5dedicated/r5dedicated.vcxproj @@ -87,8 +87,8 @@ false dedicated - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)external\detours\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dedicated;$(IncludePath) - $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(SolutionDir)external\detours\libs;$(LibraryPath) + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)external\minhook\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dedicated;$(IncludePath) + $(SolutionDir)external\minhook\lib;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(LibraryPath) @@ -164,19 +164,18 @@ true true true - detours.lib;%(AdditionalDependencies) + libMinHook.x64.lib r5dedicated.def - - + Create @@ -186,6 +185,7 @@ + @@ -284,17 +284,11 @@ - - - - - - diff --git a/r5dedicated/r5dedicated.vcxproj.filters b/r5dedicated/r5dedicated.vcxproj.filters index b0039216..c95890fb 100644 --- a/r5dedicated/r5dedicated.vcxproj.filters +++ b/r5dedicated/r5dedicated.vcxproj.filters @@ -17,27 +17,9 @@ {a0165b58-06b1-4b6a-b5d1-5d643517ad14} - - {06affed3-5a59-4b95-88ca-72d92c91909b} - - - {0742106d-702f-499e-99f1-e43f2a6ae561} - - - {338a4fb7-7519-4628-9206-679d33824965} - - - {cc424eef-0c7a-4fb0-9d84-30bf8db2e253} - - - {74afa89f-72af-4e13-aa90-70f7a1957154} - {35b40ed1-12bd-4bcf-9c05-5a42a0096619} - - {05e6e9a7-801b-49b0-9c5a-21c4868befb7} - {a7199092-e8a9-49fa-97e1-b2d0ea21001b} @@ -62,6 +44,42 @@ {eaefe9b7-d14d-48b6-878a-53a5ada7454b} + + {245e8064-9b24-4631-9326-340dfb761fde} + + + {485b5648-149f-4664-a961-be9cd520e9e3} + + + {f5326cf2-826e-4499-98de-e818e096939d} + + + {4c680991-cc41-4265-a6f3-b46d698bd72f} + + + {8aac7eb6-9810-4fa2-bbfe-499fb2950f01} + + + {f28b1a49-9b41-48d2-9462-1674af3d72a2} + + + {cc424eef-0c7a-4fb0-9d84-30bf8db2e253} + + + {74afa89f-72af-4e13-aa90-70f7a1957154} + + + {05e6e9a7-801b-49b0-9c5a-21c4868befb7} + + + {06affed3-5a59-4b95-88ca-72d92c91909b} + + + {338a4fb7-7519-4628-9206-679d33824965} + + + {31cdde4d-3641-497c-9b34-20d3d7c89d87} + @@ -79,36 +97,12 @@ core\include - - hooks\netchannel - core\include - - hooks\concommand - - - hooks\cvengineserver - - - hooks\squirrel - - - hooks\other - - - hooks - core\include - - hooks\other - - - hooks\iconvar - shared\libraries\spdlog\include @@ -391,6 +385,15 @@ shared\libraries\spdlog\include\sinks + + shared\libraries\minhook\include + + + r5-sdk\include + + + hooks\include + @@ -402,33 +405,30 @@ core - - hooks - - - hooks\netchannel - - - hooks\concommand + + hooks\src\netchannel - hooks\cvengineserver + hooks\src\cvengineserver - hooks\squirrel + hooks\src\squirrel - hooks\other - - - hooks\other - - - hooks\iconvar + hooks\src\other shared + + hooks\src\iconvar + + + r5-sdk\src + + + hooks\src + diff --git a/r5dedicated/sqvm.cpp b/r5dedicated/sqvm.cpp index b94a76b3..c3654922 100644 --- a/r5dedicated/sqvm.cpp +++ b/r5dedicated/sqvm.cpp @@ -1,11 +1,18 @@ #include "pch.h" -#include "sqvm.h" +#include "hooks.h" + +namespace Hooks +{ + SQVM_LoadRsonFn originalSQVM_LoadRson = nullptr; + SQVM_LoadScriptFn originalSQVM_LoadScript = nullptr; +} //--------------------------------------------------------------------------------- // Print the output of the VM. // TODO: separate SV CL and UI //--------------------------------------------------------------------------------- -void* HSQVM_PrintFunc(void* sqvm, char* fmt, ...) + +void* Hooks::SQVM_Print(void* sqvm, char* fmt, ...) { va_list args; va_start(args, fmt); @@ -17,11 +24,13 @@ void* HSQVM_PrintFunc(void* sqvm, char* fmt, ...) //--------------------------------------------------------------------------------- // Load the include file from the mods directory //--------------------------------------------------------------------------------- -__int64 HSQVM_LoadRson(const char* rson_name) + +__int64 Hooks::SQVM_LoadRson(const char* rson_name) { char filepath[MAX_PATH] = { 0 }; sprintf_s(filepath, MAX_PATH, "platform\\%s", rson_name); + /////////////////////////////////////////////////////////////////////////////// // Flip forward slashes in filepath to windows-style backslash for (int i = 0; i < strlen(filepath); i++) { @@ -31,15 +40,17 @@ __int64 HSQVM_LoadRson(const char* rson_name) } } + /////////////////////////////////////////////////////////////////////////////// // Returns the new path if the rson exists on the disk - if (FileExists(filepath) && org_SQVM_LoadRson(rson_name)) + if (FileExists(filepath) && originalSQVM_LoadRson(rson_name)) { printf("\n"); printf("##################################################\n"); printf("] '%s'\n", filepath); printf("##################################################\n"); printf("\n"); - return org_SQVM_LoadRson(filepath); + + return originalSQVM_LoadRson(filepath); } printf("\n"); @@ -47,17 +58,20 @@ __int64 HSQVM_LoadRson(const char* rson_name) printf("] '%s'\n", rson_name); printf("##################################################\n"); printf("\n"); - return org_SQVM_LoadRson(rson_name); + + return originalSQVM_LoadRson(rson_name); } //--------------------------------------------------------------------------------- // Load the script file from the mods directory //--------------------------------------------------------------------------------- -bool HSQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag) + +bool Hooks::SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag) { char filepath[MAX_PATH] = { 0 }; sprintf_s(filepath, MAX_PATH, "platform\\%s", script_path); + /////////////////////////////////////////////////////////////////////////////// // Flip forward slashes in filepath to windows-style backslash for (int i = 0; i < strlen(filepath); i++) { @@ -70,9 +84,9 @@ bool HSQVM_LoadScript(void* sqvm, const char* script_path, const char* script_na { printf(" [+] Loading SQVM Script '%s' ...\n", filepath); } - + /////////////////////////////////////////////////////////////////////////////// // Returns true if the script exists on the disk - if (FileExists(filepath) && org_SQVM_LoadScript(sqvm, filepath, script_name, flag)) + if (FileExists(filepath) && originalSQVM_LoadScript(sqvm, filepath, script_name, flag)) { return true; } @@ -81,20 +95,5 @@ bool HSQVM_LoadScript(void* sqvm, const char* script_path, const char* script_na printf(" [!] FAILED. Try SP / VPK for '%s'\n", filepath); } - /////////////////////////////////////////////////////////////////////////////// - return org_SQVM_LoadScript(sqvm, script_path, script_name, flag); -} - -void AttachSQVMHooks() -{ - DetourAttach((LPVOID*)&org_SQVM_PrintFunc, &HSQVM_PrintFunc); - DetourAttach((LPVOID*)&org_SQVM_LoadRson, &HSQVM_LoadRson); - DetourAttach((LPVOID*)&org_SQVM_LoadScript, &HSQVM_LoadScript); -} - -void DetachSQVMHooks() -{ - DetourDetach((LPVOID*)&org_SQVM_PrintFunc, &HSQVM_PrintFunc); - DetourDetach((LPVOID*)&org_SQVM_LoadRson, &HSQVM_LoadRson); - DetourDetach((LPVOID*)&org_SQVM_LoadScript, &HSQVM_LoadScript); + return originalSQVM_LoadScript(sqvm, script_path, script_name, flag); } \ No newline at end of file diff --git a/r5dedicated/sqvm.h b/r5dedicated/sqvm.h deleted file mode 100644 index 2798c54d..00000000 --- a/r5dedicated/sqvm.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include "pch.h" -#include "hooks.h" - -void* HSQVM_PrintFunc(void* sqvm, char* fmt, ...); -__int64 HSQVM_LoadRson(const char* rson_name); -bool HSQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag); - -void AttachSQVMHooks(); -void DetachSQVMHooks(); \ No newline at end of file diff --git a/r5dev/include/console.h b/r5dev/include/console.h index a7879bb3..6e5f6aed 100644 --- a/r5dev/include/console.h +++ b/r5dev/include/console.h @@ -2,18 +2,4 @@ ///////////////////////////////////////////////////////////////////////////// // Initialization -void SetupConsole(); -void RemoveCMHooks(); -void ToggleDevCommands(); - -///////////////////////////////////////////////////////////////////////////// -// Hooks -bool HConVar_IsFlagSet(int** cvar, int flag); -bool HConCommand_IsFlagSet(int* cmd, int flag); - -///////////////////////////////////////////////////////////////////////////// -// Globals -inline bool g_bDebugConsole = false; -inline bool g_bReturnAllFalse = false; - -///////////////////////////////////////////////////////////////////////////// +void SetupConsole(); \ No newline at end of file diff --git a/r5dev/include/enums.h b/r5dev/include/enums.h index 4d75b736..9d0d470b 100644 --- a/r5dev/include/enums.h +++ b/r5dev/include/enums.h @@ -79,3 +79,255 @@ enum class DXGISwapChainVTbl : short GetFrameStatistics = 16, GetLastPresentCount = 17, }; + +#define MAX_SPLITSCREEN_CLIENT_BITS 2 +#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 + +enum +{ + MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, + MOUSE_BUTTON_COUNT = 5, +}; + +enum JoystickAxis_t +{ + JOY_AXIS_X = 0, + JOY_AXIS_Y, + JOY_AXIS_Z, + JOY_AXIS_R, + JOY_AXIS_U, + JOY_AXIS_V, + MAX_JOYSTICK_AXES, +}; + +enum +{ + JOYSTICK_MAX_BUTTON_COUNT = 32, + JOYSTICK_POV_BUTTON_COUNT = 4, + JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, +}; + +#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) + +#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) + +enum ButtonCode_t +{ + BUTTON_CODE_INVALID = -1, + BUTTON_CODE_NONE = 0, + + KEY_FIRST = 0, + + KEY_NONE = KEY_FIRST, + KEY_0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_A, + KEY_B, + KEY_C, + KEY_D, + KEY_E, + KEY_F, + KEY_G, + KEY_H, + KEY_I, + KEY_J, + KEY_K, + KEY_L, + KEY_M, + KEY_N, + KEY_O, + KEY_P, + KEY_Q, + KEY_R, + KEY_S, + KEY_T, + KEY_U, + KEY_V, + KEY_W, + KEY_X, + KEY_Y, + KEY_Z, + KEY_PAD_0, + KEY_PAD_1, + KEY_PAD_2, + KEY_PAD_3, + KEY_PAD_4, + KEY_PAD_5, + KEY_PAD_6, + KEY_PAD_7, + KEY_PAD_8, + KEY_PAD_9, + KEY_PAD_DIVIDE, + KEY_PAD_MULTIPLY, + KEY_PAD_MINUS, + KEY_PAD_PLUS, + KEY_PAD_ENTER, + KEY_PAD_DECIMAL, + KEY_LBRACKET, + KEY_RBRACKET, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_BACKQUOTE, + KEY_COMMA, + KEY_PERIOD, + KEY_SLASH, + KEY_BACKSLASH, + KEY_MINUS, + KEY_EQUAL, + KEY_ENTER, + KEY_SPACE, + KEY_BACKSPACE, + KEY_TAB, + KEY_CAPSLOCK, + KEY_NUMLOCK, + KEY_ESCAPE, + KEY_SCROLLLOCK, + KEY_INSERT, + KEY_DELETE, + KEY_HOME, + KEY_END, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_BREAK, + KEY_LSHIFT, + KEY_RSHIFT, + KEY_LALT, + KEY_RALT, + KEY_LCONTROL, + KEY_RCONTROL, + KEY_LWIN, + KEY_RWIN, + KEY_APP, + KEY_UP, + KEY_LEFT, + KEY_DOWN, + KEY_RIGHT, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_CAPSLOCKTOGGLE, + KEY_NUMLOCKTOGGLE, + KEY_SCROLLLOCKTOGGLE, + + KEY_LAST = KEY_SCROLLLOCKTOGGLE, + KEY_COUNT = KEY_LAST - KEY_FIRST + 1, + + // Mouse + MOUSE_FIRST = KEY_LAST + 1, + + MOUSE_LEFT = MOUSE_FIRST, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_4, + MOUSE_5, + MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up + MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down + + MOUSE_LAST = MOUSE_WHEEL_DOWN, + MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, + + // Joystick + JOYSTICK_FIRST = MOUSE_LAST + 1, + + JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, + JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), + JOYSTICK_FIRST_POV_BUTTON, + JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), + JOYSTICK_FIRST_AXIS_BUTTON, + JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), + + JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, + + BUTTON_CODE_LAST, + BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, + + // Helpers for XBox 360 + KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons + KEY_XBUTTON_RIGHT, + KEY_XBUTTON_DOWN, + KEY_XBUTTON_LEFT, + + KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons + KEY_XBUTTON_B, + KEY_XBUTTON_X, + KEY_XBUTTON_Y, + KEY_XBUTTON_LEFT_SHOULDER, + KEY_XBUTTON_RIGHT_SHOULDER, + KEY_XBUTTON_BACK, + KEY_XBUTTON_START, + KEY_XBUTTON_STICK1, + KEY_XBUTTON_STICK2, + KEY_XBUTTON_INACTIVE_START, + + KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE + KEY_XSTICK1_LEFT, // XAXIS NEGATIVE + KEY_XSTICK1_DOWN, // YAXIS POSITIVE + KEY_XSTICK1_UP, // YAXIS NEGATIVE + KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE + KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE + KEY_XSTICK2_RIGHT, // UAXIS POSITIVE + KEY_XSTICK2_LEFT, // UAXIS NEGATIVE + KEY_XSTICK2_DOWN, // VAXIS POSITIVE + KEY_XSTICK2_UP, // VAXIS NEGATIVE +}; + +// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. + +enum KeyValuesTypes +{ + TYPE_NONE = 0x0, + TYPE_STRING = 0x1, + TYPE_INT = 0x2, + TYPE_FLOAT = 0x3, + TYPE_PTR = 0x4, + TYPE_WSTRING = 0x5, + TYPE_COLOR = 0x6, + TYPE_UINT64 = 0x7, + TYPE_COMPILED_INT_BYTE = 0x8, + TYPE_COMPILED_INT_0 = 0x9, + TYPE_COMPILED_INT_1 = 0xA, + TYPE_NUMTYPES = 0xB, +}; + +enum ClientFrameStage_t +{ + FRAME_UNDEFINED = -1, // (haven't run any frames yet) + FRAME_START, + + // A network packet is being recieved + FRAME_NET_UPDATE_START, + // Data has been received and we're going to start calling PostDataUpdate + FRAME_NET_UPDATE_POSTDATAUPDATE_START, + // Data has been received and we've called PostDataUpdate on all data recipients + FRAME_NET_UPDATE_POSTDATAUPDATE_END, + // We've received all packets, we can now do interpolation, prediction, etc.. + FRAME_NET_UPDATE_END, + + // We're about to start rendering the scene + FRAME_RENDER_START, + // We've finished rendering the scene. + FRAME_RENDER_END, + + FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE +}; diff --git a/r5dev/include/gameclasses.h b/r5dev/include/gameclasses.h index c495cc2e..e0cb8599 100644 --- a/r5dev/include/gameclasses.h +++ b/r5dev/include/gameclasses.h @@ -1,223 +1,6 @@ #pragma once #include "patterns.h" -///////////////////////////////////////////////////////////////////////////// -// Enums - -#define MAX_SPLITSCREEN_CLIENT_BITS 2 -#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 - -enum -{ - MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, - MOUSE_BUTTON_COUNT = 5, -}; - -enum JoystickAxis_t -{ - JOY_AXIS_X = 0, - JOY_AXIS_Y, - JOY_AXIS_Z, - JOY_AXIS_R, - JOY_AXIS_U, - JOY_AXIS_V, - MAX_JOYSTICK_AXES, -}; - -enum -{ - JOYSTICK_MAX_BUTTON_COUNT = 32, - JOYSTICK_POV_BUTTON_COUNT = 4, - JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, -}; - -#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) - -#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) - -enum ButtonCode_t -{ - BUTTON_CODE_INVALID = -1, - BUTTON_CODE_NONE = 0, - - KEY_FIRST = 0, - - KEY_NONE = KEY_FIRST, - KEY_0, - KEY_1, - KEY_2, - KEY_3, - KEY_4, - KEY_5, - KEY_6, - KEY_7, - KEY_8, - KEY_9, - KEY_A, - KEY_B, - KEY_C, - KEY_D, - KEY_E, - KEY_F, - KEY_G, - KEY_H, - KEY_I, - KEY_J, - KEY_K, - KEY_L, - KEY_M, - KEY_N, - KEY_O, - KEY_P, - KEY_Q, - KEY_R, - KEY_S, - KEY_T, - KEY_U, - KEY_V, - KEY_W, - KEY_X, - KEY_Y, - KEY_Z, - KEY_PAD_0, - KEY_PAD_1, - KEY_PAD_2, - KEY_PAD_3, - KEY_PAD_4, - KEY_PAD_5, - KEY_PAD_6, - KEY_PAD_7, - KEY_PAD_8, - KEY_PAD_9, - KEY_PAD_DIVIDE, - KEY_PAD_MULTIPLY, - KEY_PAD_MINUS, - KEY_PAD_PLUS, - KEY_PAD_ENTER, - KEY_PAD_DECIMAL, - KEY_LBRACKET, - KEY_RBRACKET, - KEY_SEMICOLON, - KEY_APOSTROPHE, - KEY_BACKQUOTE, - KEY_COMMA, - KEY_PERIOD, - KEY_SLASH, - KEY_BACKSLASH, - KEY_MINUS, - KEY_EQUAL, - KEY_ENTER, - KEY_SPACE, - KEY_BACKSPACE, - KEY_TAB, - KEY_CAPSLOCK, - KEY_NUMLOCK, - KEY_ESCAPE, - KEY_SCROLLLOCK, - KEY_INSERT, - KEY_DELETE, - KEY_HOME, - KEY_END, - KEY_PAGEUP, - KEY_PAGEDOWN, - KEY_BREAK, - KEY_LSHIFT, - KEY_RSHIFT, - KEY_LALT, - KEY_RALT, - KEY_LCONTROL, - KEY_RCONTROL, - KEY_LWIN, - KEY_RWIN, - KEY_APP, - KEY_UP, - KEY_LEFT, - KEY_DOWN, - KEY_RIGHT, - KEY_F1, - KEY_F2, - KEY_F3, - KEY_F4, - KEY_F5, - KEY_F6, - KEY_F7, - KEY_F8, - KEY_F9, - KEY_F10, - KEY_F11, - KEY_F12, - KEY_CAPSLOCKTOGGLE, - KEY_NUMLOCKTOGGLE, - KEY_SCROLLLOCKTOGGLE, - - KEY_LAST = KEY_SCROLLLOCKTOGGLE, - KEY_COUNT = KEY_LAST - KEY_FIRST + 1, - - // Mouse - MOUSE_FIRST = KEY_LAST + 1, - - MOUSE_LEFT = MOUSE_FIRST, - MOUSE_RIGHT, - MOUSE_MIDDLE, - MOUSE_4, - MOUSE_5, - MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up - MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down - - MOUSE_LAST = MOUSE_WHEEL_DOWN, - MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, - - // Joystick - JOYSTICK_FIRST = MOUSE_LAST + 1, - - JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, - JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), - JOYSTICK_FIRST_POV_BUTTON, - JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), - JOYSTICK_FIRST_AXIS_BUTTON, - JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), - - JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, - - BUTTON_CODE_LAST, - BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, - - // Helpers for XBox 360 - KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons - KEY_XBUTTON_RIGHT, - KEY_XBUTTON_DOWN, - KEY_XBUTTON_LEFT, - - KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons - KEY_XBUTTON_B, - KEY_XBUTTON_X, - KEY_XBUTTON_Y, - KEY_XBUTTON_LEFT_SHOULDER, - KEY_XBUTTON_RIGHT_SHOULDER, - KEY_XBUTTON_BACK, - KEY_XBUTTON_START, - KEY_XBUTTON_STICK1, - KEY_XBUTTON_STICK2, - KEY_XBUTTON_INACTIVE_START, - - KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE - KEY_XSTICK1_LEFT, // XAXIS NEGATIVE - KEY_XSTICK1_DOWN, // YAXIS POSITIVE - KEY_XSTICK1_UP, // YAXIS NEGATIVE - KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE - KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE - KEY_XSTICK2_RIGHT, // UAXIS POSITIVE - KEY_XSTICK2_LEFT, // UAXIS NEGATIVE - KEY_XSTICK2_DOWN, // VAXIS POSITIVE - KEY_XSTICK2_UP, // VAXIS NEGATIVE -}; - -// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. - ///////////////////////////////////////////////////////////////////////////// // Classes and Structs @@ -351,22 +134,6 @@ public: int m_mutex; // 0x0130 }; -enum KeyValuesTypes -{ - TYPE_NONE = 0x0, - TYPE_STRING = 0x1, - TYPE_INT = 0x2, - TYPE_FLOAT = 0x3, - TYPE_PTR = 0x4, - TYPE_WSTRING = 0x5, - TYPE_COLOR = 0x6, - TYPE_UINT64 = 0x7, - TYPE_COMPILED_INT_BYTE = 0x8, - TYPE_COMPILED_INT_0 = 0x9, - TYPE_COMPILED_INT_1 = 0xA, - TYPE_NUMTYPES = 0xB, -}; - class KeyValues { public: @@ -485,28 +252,6 @@ public: bool m_bWorkshopMapDownloadPending; //0x026A }; -enum ClientFrameStage_t -{ - FRAME_UNDEFINED = -1, // (haven't run any frames yet) - FRAME_START, - - // A network packet is being recieved - FRAME_NET_UPDATE_START, - // Data has been received and we're going to start calling PostDataUpdate - FRAME_NET_UPDATE_POSTDATAUPDATE_START, - // Data has been received and we've called PostDataUpdate on all data recipients - FRAME_NET_UPDATE_POSTDATAUPDATE_END, - // We've received all packets, we can now do interpolation, prediction, etc.. - FRAME_NET_UPDATE_END, - - // We're about to start rendering the scene - FRAME_RENDER_START, - // We've finished rendering the scene. - FRAME_RENDER_END, - - FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE -}; - class CHLClient { public: diff --git a/r5dev/include/hooks.h b/r5dev/include/hooks.h index 025eb1e0..ab484fd0 100644 --- a/r5dev/include/hooks.h +++ b/r5dev/include/hooks.h @@ -1,14 +1,87 @@ #pragma once +#include "patterns.h" +#include "structs.h" +#include "overlay.h" +#include "hooks.h" +#include "gameclasses.h" -///////////////////////////////////////////////////////////////////////////// -// Initialization -void InstallENHooks(); -void RemoveENHooks(); -void ToggleDevCommands(); -void ToggleNetHooks(); - -///////////////////////////////////////////////////////////////////////////// -// Globals inline bool g_bDebugLoading = false; +inline bool g_bReturnAllFalse = false; +inline bool g_bDebugConsole = false; +extern bool g_bBlockInput; -///////////////////////////////////////////////////////////////////////////// \ No newline at end of file +namespace Hooks +{ +#pragma region CHLClient + void __fastcall FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage); + + using FrameStageNotifyFn = void(__fastcall*)(CHLClient*, ClientFrameStage_t); + extern FrameStageNotifyFn originalFrameStageNotify; +#pragma endregion + +#pragma region Squirrel + void* SQVM_Print(void* sqvm, char* fmt, ...); + __int64 SQVM_LoadRson(const char* rson_name); + bool SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag); + + using SQVM_LoadRsonFn = __int64(*)(const char*); + extern SQVM_LoadRsonFn originalSQVM_LoadRson; + + using SQVM_LoadScriptFn = bool(*)(void*, const char*, const char*, int); + extern SQVM_LoadScriptFn originalSQVM_LoadScript; +#pragma endregion + +#pragma region CVEngineServer + bool IsPersistenceDataAvailable(__int64 thisptr, int client); + + using IsPersistenceDataAvailableFn = bool(*)(__int64, int); + extern IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable; +#pragma endregion + +#pragma region NetChannel + bool NET_ReceiveDatagram(int sock, void* inpacket, bool raw); + unsigned int NET_SendDatagram(SOCKET s, const char* buf, int len, int flags); + + using NET_ReceiveDatagramFn = bool(*)(int, void*, bool); + extern NET_ReceiveDatagramFn originalNET_ReceiveDatagram; + + using NET_SendDatagramFn = unsigned int(*)(SOCKET, const char*, int, int); + extern NET_SendDatagramFn originalNET_SendDatagram; +#pragma endregion + +#pragma region ConVar + bool ConVar_IsFlagSet(int** cvar, int flag); + bool ConCommand_IsFlagSet(int* cmd, int flag); +#pragma endregion + +#pragma region WinAPI + BOOL WINAPI GetCursorPos(LPPOINT lpPoint); + BOOL WINAPI SetCursorPos(int X, int Y); + BOOL WINAPI ClipCursor(const RECT* lpRect); + BOOL WINAPI ShowCursor(BOOL bShow); + + using GetCursorPosFn = BOOL(WINAPI*)(LPPOINT); + extern GetCursorPosFn originalGetCursorPos; + + using SetCursorPosFn = BOOL(WINAPI*)(int, int); + extern SetCursorPosFn originalSetCursorPos; + + using ClipCursorFn = BOOL(WINAPI*)(const RECT*); + extern ClipCursorFn originalClipCursor; + + using ShowCursorFn = BOOL(WINAPI*)(BOOL); + extern ShowCursorFn originalShowCursor; +#pragma endregion + +#pragma region Other + int MSG_EngineError(char* fmt, va_list args); + + using MSG_EngineErrorFn = int(*)(char*, va_list); + extern MSG_EngineErrorFn originalMSG_EngineError; +#pragma endregion + + void InstallHooks(); + void RemoveHooks(); + void ToggleNetHooks(); + void ToggleDevCommands(); +} \ No newline at end of file diff --git a/r5dev/include/input.h b/r5dev/include/input.h index d686f3c7..7b9637ef 100644 --- a/r5dev/include/input.h +++ b/r5dev/include/input.h @@ -1,11 +1 @@ -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// Internals -void InstallIPHooks(); -void RemoveIPHooks(); - -///////////////////////////////////////////////////////////////////////////// -// Globals -extern BOOL g_bBlockInput; -///////////////////////////////////////////////////////////////////////////// +#pragma once \ No newline at end of file diff --git a/r5dev/include/patterns.h b/r5dev/include/patterns.h index 567abe9b..e262cfd7 100644 --- a/r5dev/include/patterns.h +++ b/r5dev/include/patterns.h @@ -7,50 +7,50 @@ namespace #pragma region Console /*0x140202090*/ - FUNC_AT_ADDRESS(org_CommandExecute, void(*)(void*, const char*), r5_patterns.PatternSearch("48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8").GetPtr()); + FUNC_AT_ADDRESS(addr_CommandExecute, void(*)(void*, const char*), r5_patterns.PatternSearch("48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8").GetPtr()); /*0x14046FE90*/ - FUNC_AT_ADDRESS(org_ConVar_IsFlagSet, bool(*)(int**, int), r5_patterns.PatternSearch("48 8B 41 48 85 50 38").GetPtr()); + FUNC_AT_ADDRESS(addr_ConVar_IsFlagSet, bool(*)(int**, int), r5_patterns.PatternSearch("48 8B 41 48 85 50 38").GetPtr()); /*0x14046F490*/ - FUNC_AT_ADDRESS(org_ConCommand_IsFlagSet, bool(*)(int*, int), r5_patterns.PatternSearch("85 51 38 0F 95 C0 C3").GetPtr()); + FUNC_AT_ADDRESS(addr_ConCommand_IsFlagSet, bool(*)(int*, int), r5_patterns.PatternSearch("85 51 38 0F 95 C0 C3").GetPtr()); #pragma endregion #pragma region Squirrel /*0x141057FD0*/ - FUNC_AT_ADDRESS(org_SQVM_Print, void*, r5_patterns.PatternSearch("83 F8 01 48 8D 3D ? ? ? ?").OffsetSelf(0x3).FollowNearCallSelf(0x3, 0x7).GetPtr()); + FUNC_AT_ADDRESS(addr_SQVM_Print, void*, r5_patterns.PatternSearch("83 F8 01 48 8D 3D ? ? ? ?").OffsetSelf(0x3).FollowNearCallSelf(0x3, 0x7).GetPtr()); //DWORD64 p_SQVM_LoadScript = FindPattern("r5apex.exe", (const unsigned char*)"\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); // For S0 and S1 /*0x141055630*/ // For anything S2 and above (current S8 - FUNC_AT_ADDRESS(org_SQVM_LoadScript, bool(*)(void*, const char*, const char*, int), r5_patterns.PatternSearch("48 8B C4 48 89 48 08 55 41 56 48 8D 68").GetPtr()); + FUNC_AT_ADDRESS(addr_SQVM_LoadScript, bool(*)(void*, const char*, const char*, int), r5_patterns.PatternSearch("48 8B C4 48 89 48 08 55 41 56 48 8D 68").GetPtr()); /*0x140C957E0*/ - FUNC_AT_ADDRESS(org_SQVM_LoadRson, int(*)(const char*), r5_patterns.PatternSearch("4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33").GetPtr()); + FUNC_AT_ADDRESS(addr_SQVM_LoadRson, int(*)(const char*), r5_patterns.PatternSearch("4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33").GetPtr()); #pragma endregion #pragma region NetChannel /*0x1402655F0*/ - FUNC_AT_ADDRESS(org_NET_ReceiveDatagram, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr()); + FUNC_AT_ADDRESS(addr_NET_ReceiveDatagram, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr()); /*0x1402662D0*/ - FUNC_AT_ADDRESS(org_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); + FUNC_AT_ADDRESS(addr_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); #pragma endregion #pragma region CHLClient /*0x1405C0740*/ - FUNC_AT_ADDRESS(org_CHLClient_FrameStageNotify, void(*)(void* rcx, int curStage), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); + FUNC_AT_ADDRESS(addr_CHLClient_FrameStageNotify, void(*)(void* rcx, int curStage), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); #pragma endregion #pragma region CVEngineServer /*0x140315CF0*/ - FUNC_AT_ADDRESS(org_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); + FUNC_AT_ADDRESS(addr_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); #pragma endregion #pragma region Utility /*0x140295600*/ - FUNC_AT_ADDRESS(org_MSG_EngineError, int(*)(char*, va_list), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 00 00 48 8B DA").GetPtr()); + FUNC_AT_ADDRESS(addr_MSG_EngineError, int(*)(char*, va_list), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 00 00 48 8B DA").GetPtr()); #pragma endregion // Un-used atm. // DWORD64 p_KeyValues_FindKey = /*1404744E0*/ reinterpret_cast(PatternScan("r5apex.exe", "40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45")); @@ -58,17 +58,17 @@ namespace void PrintHAddress() // Test the sigscan results { std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("CommandExecute", org_CommandExecute); - PRINT_ADDRESS("ConVar_IsFlagSet", org_ConVar_IsFlagSet); - PRINT_ADDRESS("ConCommand_IsFlagSet", org_ConCommand_IsFlagSet); - PRINT_ADDRESS("SQVM_Print", org_SQVM_Print); - PRINT_ADDRESS("SQVM_LoadScript", org_SQVM_LoadScript); - PRINT_ADDRESS("SQVM_LoadRson", org_SQVM_LoadRson); - PRINT_ADDRESS("NET_ReceiveDatagram", org_NET_ReceiveDatagram); - PRINT_ADDRESS("NET_SendDatagram ", org_NET_SendDatagram); - PRINT_ADDRESS("CHLClient::FrameStageNotify", org_CHLClient_FrameStageNotify); - PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", org_CVEngineServer_IsPersistenceDataAvailable); - PRINT_ADDRESS("MSG_EngineError", org_MSG_EngineError); + PRINT_ADDRESS("CommandExecute", addr_CommandExecute); + PRINT_ADDRESS("ConVar_IsFlagSet", addr_ConVar_IsFlagSet); + PRINT_ADDRESS("ConCommand_IsFlagSet", addr_ConCommand_IsFlagSet); + PRINT_ADDRESS("SQVM_Print", addr_SQVM_Print); + PRINT_ADDRESS("SQVM_LoadScript", addr_SQVM_LoadScript); + PRINT_ADDRESS("SQVM_LoadRson", addr_SQVM_LoadRson); + PRINT_ADDRESS("NET_ReceiveDatagram", addr_NET_ReceiveDatagram); + PRINT_ADDRESS("NET_SendDatagram ", addr_NET_SendDatagram); + PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify); + PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable); + PRINT_ADDRESS("MSG_EngineError", addr_MSG_EngineError); std::cout << "+--------------------------------------------------------+" << std::endl; // TODO implement error handling when sigscan fails or result is 0 } diff --git a/r5dev/include/pch.h b/r5dev/include/pch.h index e4cb717f..37c8e86e 100644 --- a/r5dev/include/pch.h +++ b/r5dev/include/pch.h @@ -3,7 +3,7 @@ #define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. #include -#include +#include #include #include #include @@ -19,8 +19,6 @@ #include #include - - // Our headers #include "imgui.h" @@ -33,6 +31,7 @@ #include "json.hpp" #include "address.h" +#include "enums.h" #define FUNC_AT_ADDRESS(name, funcbody, addr) \ using _##name = funcbody; \ diff --git a/r5dev/r5dev.vcxproj b/r5dev/r5dev.vcxproj index 1c771135..c87b1ab0 100644 --- a/r5dev/r5dev.vcxproj +++ b/r5dev/r5dev.vcxproj @@ -87,8 +87,8 @@ false - $(SolutionDir)external\detours\include;$(SolutionDir)external\imgui\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dev\include;$(IncludePath) - $(SolutionDir)external\detours\libs;$(LibraryPath) + $(SolutionDir)external\minhook\include;$(SolutionDir)external\imgui\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dev\include;$(IncludePath) + $(SolutionDir)external\minhook\lib;$(LibraryPath) @@ -165,6 +165,7 @@ Speed true true + MultiThreadedDLL Windows @@ -173,16 +174,13 @@ true false r5dev.def - detours.lib;%(AdditionalDependencies) + libMinHook.x64.lib;%(AdditionalDependencies) del "$(TargetDir)\r5detours.dll" && rename "$(TargetPath)" "r5detours.dll" - - - @@ -191,6 +189,7 @@ + @@ -345,10 +344,14 @@ Use pch.h - - Use - pch.h - + + + + + + + + Use pch.h diff --git a/r5dev/r5dev.vcxproj.filters b/r5dev/r5dev.vcxproj.filters index 89f3a4a9..34b18dc7 100644 --- a/r5dev/r5dev.vcxproj.filters +++ b/r5dev/r5dev.vcxproj.filters @@ -1,9 +1,6 @@  - - {a80939e5-3b3c-4873-885e-834e95d68d34} - {927ea852-3616-4fc4-8b32-781f65853a6b} @@ -16,25 +13,15 @@ {633d6e7a-c709-4d64-a134-b383d43c8c8e} - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - {7cef4f92-94d9-43af-bea5-c6963d68fff2} - - {584562a8-0382-488c-909c-67bbb1d1af3c} - {c72b9789-72e9-4657-8a42-8712aaf8690e} {757af774-e575-4623-8582-71efb0ae53a4} - - {3fa7f9a9-dc07-4d42-9172-0944829c010f} - {c18fb898-adc3-4aa8-902c-4777bbc76e5b} @@ -59,32 +46,62 @@ {30005d10-4213-441c-b18b-4dd47fcb8812} + + {e901ef66-ddeb-4aff-9d7e-786d74a791e9} + + + {da82c6eb-abba-4b52-84bf-9f3758a169bf} + + + {6a6d7e33-2ce0-4b53-bcf6-41dc6dacdc5f} + + + {e449786f-692c-4d6c-a292-b0720bf9e2a9} + + + {46b49d69-ea42-467c-86d8-00611f2718ff} + + + {2ec9179b-7320-47da-b170-f021d0279310} + + + {83391c47-4a5b-4231-a9ef-29e119c5bb69} + + + {c27e3539-0070-40fa-b262-0ecd39f47919} + + + {afb847fc-853f-4e6f-bde6-91bf345369b4} + + + {9f8f15a7-6e69-4a79-b644-a92ad815c600} + + + {25c5fe7f-23c0-4d44-b92d-ea4b127eec05} + + + {0420f99f-468e-4ba5-abe3-960cc332b636} + + + {a40dcf17-855b-4965-b58d-6c7d24edc627} + + + {03d79a58-4706-4ee0-b0ea-6251a2fafada} + + + {89bf5311-5686-4077-9b79-c6306dc1bd91} + + + {3a4ca756-24d2-47e8-af7f-0e8a75933c4b} + + + {58dbc5f6-cf3c-4d71-80c8-caefffb39beb} + + + {0136e8e8-91ef-45c1-8661-476b5c20fc48} + - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - core @@ -115,35 +132,53 @@ shared + + hooks\src\chlclient + + + hooks\src\squirrel + + + hooks\src\cvengineserver + + + hooks\src\other + + + hooks\src\netchannel + + + hooks\src\iconvar + + + hooks\src\winapi + + + hooks\src + + + gui\src + + + gui\src + + + gui\src + + + r5-sdk\src + + + core + + + core + + + r5-sdk\src + - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - shared\libraries\detours\include - - - shared\libraries\detours\include - - - shared\libraries\detours\include - shared\libraries\imgui\include @@ -168,18 +203,6 @@ shared\libraries\imgui\include - - Header Files - - - Header Files - - - Header Files - - - Header Files - shared\libraries\spdlog\include @@ -462,12 +485,6 @@ shared\libraries\spdlog\include\sinks - - Header Files - - - Header Files - shared\include @@ -483,6 +500,45 @@ core\include + + shared\libraries\minhook\include + + + hooks\include + + + gui\include + + + gui\include + + + gui\include + + + r5-sdk\include + + + core\include + + + core + + + r5-sdk\include + + + r5-sdk\include + + + r5-sdk\include + + + r5-sdk\include + + + core\include + diff --git a/r5dev/src/console.cpp b/r5dev/src/console.cpp index c5ab1a52..7efe42c6 100644 --- a/r5dev/src/console.cpp +++ b/r5dev/src/console.cpp @@ -54,62 +54,13 @@ void SetupConsole() } } -//############################################################################# -// CONSOLE HOOKS -//############################################################################# - -bool HConVar_IsFlagSet(int** cvar, int flag) -{ - int real_flags = *(*(cvar + (72 / (sizeof(void*)))) + (56 / sizeof(int))); - if (g_bDebugConsole) - { - printf("--------------------------------------------------\n"); - printf(" Flaged: %08X\n", real_flags); - } - // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY - real_flags &= 0xFFFFBFFD; - if (g_bDebugConsole) - { - printf(" Masked: %08X\n", real_flags); - printf(" Verify: %08X\n", flag); - printf("--------------------------------------------------\n"); - } - if (flag & 0x80000) { return true; } - - if (!g_bReturnAllFalse) { return (real_flags & flag) != 0; } - else { return false; } -} - -bool HConCommand_IsFlagSet(int* cmd, int flag) -{ - int real_flags = *((cmd + (56 / sizeof(int)))); - if (g_bDebugConsole) - { - printf("--------------------------------------------------\n"); - printf(" Flaged: %08X\n", real_flags); - } - // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY - real_flags &= 0xFFFFBFFD; - if (g_bDebugConsole) - { - printf(" Masked: %08X\n", real_flags); - printf(" Verify: %08X\n", flag); - printf("--------------------------------------------------\n"); - } - if (flag & 0x80000) { return true; } - - if (!g_bReturnAllFalse) { return (real_flags & flag) != 0; } - else { return false; } -} - //############################################################################# // WORKER THREAD //############################################################################# DWORD __stdcall ProcessConsoleWorker(LPVOID) { - // Loop forever - while (true) + while (true) // Loop forever { std::string sCommand; @@ -120,8 +71,8 @@ DWORD __stdcall ProcessConsoleWorker(LPVOID) /////////////////////////////////////////////////////////////////////// // Engine toggles - if (sCommand == "toggle net") { ToggleNetHooks(); continue; } - if (sCommand == "toggle dev") { ToggleDevCommands(); continue; } + if (sCommand == "toggle net") { Hooks::ToggleNetHooks(); continue; } + if (sCommand == "toggle dev") { Hooks::ToggleDevCommands(); continue; } if (sCommand == "toggle fal") { g_bReturnAllFalse = !g_bReturnAllFalse; continue; } /////////////////////////////////////////////////////////////////////// // Debug toggles @@ -130,12 +81,12 @@ DWORD __stdcall ProcessConsoleWorker(LPVOID) if (sCommand == "console test") { g_bDebugConsole = !g_bDebugConsole; continue; } /////////////////////////////////////////////////////////////////////// // Exec toggles - if (sCommand == "1") { ToggleDevCommands(); org_CommandExecute(NULL, "exec autoexec_dev"); } + if (sCommand == "1") { Hooks::ToggleDevCommands(); addr_CommandExecute(NULL, "exec autoexec_dev"); } if (sCommand == "2") { g_bDebugLoading = !g_bDebugLoading; continue; } /////////////////////////////////////////////////////////////////////// // Execute the command in the r5 SQVM - org_CommandExecute(NULL, sCommand.c_str()); + addr_CommandExecute(NULL, sCommand.c_str()); sCommand.clear(); /////////////////////////////////////////////////////////////////////// @@ -144,66 +95,4 @@ DWORD __stdcall ProcessConsoleWorker(LPVOID) } return 0; -} - -//############################################################################# -// MANAGEMENT -//############################################################################# - -void RemoveCMHooks() -{ - /////////////////////////////////////////////////////////////////////////// - // Begin the detour transaction, to unhook the the process - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - /////////////////////////////////////////////////////////////////////////// - // Unhook Console functions - DetourDetach((LPVOID*)&org_ConVar_IsFlagSet, &HConVar_IsFlagSet); - DetourDetach((LPVOID*)&org_ConCommand_IsFlagSet, &HConCommand_IsFlagSet); - - /////////////////////////////////////////////////////////////////////////// - // Commit the transaction - DetourTransactionCommit(); -} - -//############################################################################# -// TOGGLES -//############################################################################# - -void ToggleDevCommands() -{ - static bool g_dev = false; - - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - if (!g_dev) - { - DetourAttach((LPVOID*)&org_ConVar_IsFlagSet, &HConVar_IsFlagSet); - DetourAttach((LPVOID*)&org_ConCommand_IsFlagSet, &HConCommand_IsFlagSet); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>>| DEVONLY COMMANDS ACTIVATED |<<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - - } - else - { - DetourDetach((LPVOID*)&org_ConVar_IsFlagSet, &HConVar_IsFlagSet); - DetourDetach((LPVOID*)&org_ConCommand_IsFlagSet, &HConCommand_IsFlagSet); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>| DEVONLY COMMANDS DEACTIVATED |<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - } - - if (DetourTransactionCommit() != NO_ERROR) - { - TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); - } - - g_dev = !g_dev; -} +} \ No newline at end of file diff --git a/r5dev/src/dllmain.cpp b/r5dev/src/dllmain.cpp index 4c2f5c93..c996b69b 100644 --- a/r5dev/src/dllmain.cpp +++ b/r5dev/src/dllmain.cpp @@ -5,7 +5,6 @@ #include "hooks.h" #include "opcptc.h" #include "console.h" -#include "gameclasses.h" //############################################################################# // INITIALIZATION @@ -14,9 +13,7 @@ void InitializeR5Dev() { SetupConsole(); - InstallENHooks(); - InstallIPHooks(); - InstallDXHooks(); + Hooks::InstallHooks(); InstallOpcodes(); SetupDXSwapChain(); printf("+-----------------------------------------------------------------------------+\n"); @@ -27,10 +24,8 @@ void InitializeR5Dev() void TerminateR5Dev() { - RemoveCMHooks(); - RemoveENHooks(); - RemoveIPHooks(); RemoveDXHooks(); + Hooks::RemoveHooks(); FreeConsole(); } @@ -38,7 +33,7 @@ void TerminateR5Dev() // ENTRYPOINT //############################################################################# -BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) +BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { switch (dwReason) diff --git a/r5dev/src/hooks.cpp b/r5dev/src/hooks.cpp deleted file mode 100644 index d1bc3689..00000000 --- a/r5dev/src/hooks.cpp +++ /dev/null @@ -1,289 +0,0 @@ -#include "pch.h" -#include "patterns.h" -#include "structs.h" -#include "overlay.h" -#include "hooks.h" -#include "gameclasses.h" - -//################################################################################# -// NETCHANNEL HOOKS -//################################################################################# - -bool HNET_ReceiveDatagram(int sock, void* inpacket, bool raw) -{ - bool result = org_NET_ReceiveDatagram(sock, inpacket, raw); - if (result) - { - int i = NULL; - netpacket_t* pkt = (netpacket_t*)inpacket; - - /////////////////////////////////////////////////////////////////////////// - // Log received packet data - HexDump("[+] NET_ReceiveDatagram", 0, &pkt->data[i], pkt->wiresize); - } - - return result; -} - -unsigned int HNET_SendDatagram(SOCKET s, const char* buf, int len, int flags) -{ - unsigned int result = org_NET_SendDatagram(s, buf, len, flags); - if (result) - { - /////////////////////////////////////////////////////////////////////////// - // Log transmitted packet data - HexDump("[+] NET_SendDatagram", 0, buf, len); - } - - return result; -} - -//################################################################################# -// CHLCLIENT HOOKS -//################################################################################# - -void __fastcall HCHLClient__FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage) /* __fastcall so we can make sure first argument will be RCX and second RDX. */ -{ - switch (curStage) - { - case FRAME_START: // FrameStageNotify gets called every frame by CEngine::Frame with the stage being FRAME_START. We can use this to check/set global variables. - { - if (!GameGlobals::IsInitialized) - GameGlobals::InitGameGlobals(); - - break; - } - default: - break; - } - - org_CHLClient_FrameStageNotify(rcx, curStage); -} - -//################################################################################# -// SQUIRRELVM HOOKS -//################################################################################# - -void* HSQVM_Print(void* sqvm, char* fmt, ...) -{ - char buf[1024]; - va_list args; - va_start(args, fmt); - vprintf(fmt, args); - vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); - buf[IM_ARRAYSIZE(buf) - 1] = 0; - va_end(args); - Items.push_back(Strdup(buf)); - return NULL; -} - -__int64 HSQVM_LoadRson(const char* rson_name) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", rson_name); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - - /////////////////////////////////////////////////////////////////////////////// - // Returns the new path if the rson exists on the disk - if (FileExists(filepath) && org_SQVM_LoadRson(rson_name)) - { - printf("\n"); - printf("##################################################\n"); - printf("] '%s'\n", filepath); - printf("##################################################\n"); - printf("\n"); - return org_SQVM_LoadRson(filepath); - } - - printf("\n"); - printf("##################################################\n"); - printf("] '%s'\n", rson_name); - printf("##################################################\n"); - printf("\n"); - return org_SQVM_LoadRson(rson_name); -} - -bool HSQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", script_path); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - if (g_bDebugLoading) - { - printf(" [+] Loading SQVM Script '%s' ...\n", filepath); - } - /////////////////////////////////////////////////////////////////////////////// - // Returns true if the script exists on the disk - if (FileExists(filepath) && org_SQVM_LoadScript(sqvm, filepath, script_name, flag)) - { - return true; - } - if (g_bDebugLoading) - { - printf(" [!] FAILED. Try SP / VPK for '%s'\n", filepath); - } - return org_SQVM_LoadScript(sqvm, script_path, script_name, flag); -} - -//################################################################################# -// UTILITY HOOKS -//################################################################################# - -int HMSG_EngineError(char* fmt, va_list args) -{ - printf("\nENGINE ERROR #####################################\n"); - vprintf(fmt, args); - return org_MSG_EngineError(fmt, args); -} - -// TODO: turn this into a playerstruct constructor if it ever becomes necessary -bool HCVEngineServer_IsPersistenceDataAvailable(__int64 thisptr, int client) -{ - static bool isPersistenceVarSet[256]; - - // TODO: Maybe not hardcode - std::uintptr_t playerStructBase = 0x16073B200; - std::uintptr_t playerStructSize = 0x4A4C0; - std::uintptr_t persistenceVar = 0x5BC; - - std::uintptr_t targetPlayerStruct = playerStructBase + client * playerStructSize; - - *(char*)(targetPlayerStruct + persistenceVar) = (char)0x5; - - if (!isPersistenceVarSet[client]) - { - printf("\n"); - printf("##################################################\n"); - printf("] SETTING PERSISTENCE VAR FOR CLIENT #%d\n", client); - printf("##################################################\n"); - printf("\n"); - isPersistenceVarSet[client] = true; - } - - return org_CVEngineServer_IsPersistenceDataAvailable(thisptr, client); -} - -//################################################################################# -// MANAGEMENT -//################################################################################# - -void InstallENHooks() -{ - /////////////////////////////////////////////////////////////////////////////// - // Begin the detour transaction - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Squirrel functions - DetourAttach((LPVOID*)&org_SQVM_Print, &HSQVM_Print); - DetourAttach((LPVOID*)&org_SQVM_LoadRson, &HSQVM_LoadRson); - DetourAttach((LPVOID*)&org_SQVM_LoadScript, &HSQVM_LoadScript); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Game Functions - DetourAttach((LPVOID*)&org_CHLClient_FrameStageNotify, &HCHLClient__FrameStageNotify); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Utility functions - DetourAttach((LPVOID*)&org_MSG_EngineError, &HMSG_EngineError); - DetourAttach((LPVOID*)&org_CVEngineServer_IsPersistenceDataAvailable, &HCVEngineServer_IsPersistenceDataAvailable); - - /////////////////////////////////////////////////////////////////////////////// - // Commit the transaction - if (DetourTransactionCommit() != NO_ERROR) - { - // Failed to hook into the process, terminate - TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); - } -} - -void RemoveENHooks() -{ - /////////////////////////////////////////////////////////////////////////////// - // Begin the detour transaction, to unhook the the process - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Squirrel functions - DetourDetach((LPVOID*)&org_SQVM_Print, &HSQVM_Print); - DetourDetach((LPVOID*)&org_SQVM_LoadRson, &HSQVM_LoadRson); - DetourDetach((LPVOID*)&org_SQVM_LoadScript, &HSQVM_LoadScript); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Game Functions - DetourDetach((LPVOID*)&org_CHLClient_FrameStageNotify, &HCHLClient__FrameStageNotify); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Netchan functions - DetourDetach((LPVOID*)&org_NET_SendDatagram, &HNET_SendDatagram); - DetourDetach((LPVOID*)&org_NET_ReceiveDatagram, &HNET_ReceiveDatagram); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Utility functions - DetourDetach((LPVOID*)&org_MSG_EngineError, &HMSG_EngineError); - DetourDetach((LPVOID*)&org_CVEngineServer_IsPersistenceDataAvailable, &HCVEngineServer_IsPersistenceDataAvailable); - - /////////////////////////////////////////////////////////////////////////////// - // Commit the transaction - DetourTransactionCommit(); -} - -//################################################################################# -// TOGGLES -//################################################################################# - -void ToggleNetHooks() -{ - static bool g_net = false; - - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - if (!g_net) - { - DetourAttach((LPVOID*)&org_NET_SendDatagram, &HNET_SendDatagram); - DetourAttach((LPVOID*)&org_NET_ReceiveDatagram, &HNET_ReceiveDatagram); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - } - else - { - DetourDetach((LPVOID*)&org_NET_SendDatagram, &HNET_SendDatagram); - DetourDetach((LPVOID*)&org_NET_ReceiveDatagram, &HNET_ReceiveDatagram); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - } - - if (DetourTransactionCommit() != NO_ERROR) - { - TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); - } - - g_net = !g_net; -} diff --git a/r5dev/src/hooks/chlclient.cpp b/r5dev/src/hooks/chlclient.cpp new file mode 100644 index 00000000..4ae9839b --- /dev/null +++ b/r5dev/src/hooks/chlclient.cpp @@ -0,0 +1,25 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + FrameStageNotifyFn originalFrameStageNotify = nullptr; +} + +void __fastcall Hooks::FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage) +{ + switch (curStage) + { + case FRAME_START: // FrameStageNotify gets called every frame by CEngine::Frame with the stage being FRAME_START. We can use this to check/set global variables. + { + if (!GameGlobals::IsInitialized) + GameGlobals::InitGameGlobals(); + + break; + } + default: + break; + } + + originalFrameStageNotify(rcx, curStage); +} \ No newline at end of file diff --git a/r5dev/src/hooks/cvengineserver.cpp b/r5dev/src/hooks/cvengineserver.cpp new file mode 100644 index 00000000..0c7cad18 --- /dev/null +++ b/r5dev/src/hooks/cvengineserver.cpp @@ -0,0 +1,34 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable = nullptr; +} + +// TODO: turn this into a playerstruct constructor if it ever becomes necessary +bool Hooks::IsPersistenceDataAvailable(__int64 thisptr, int client) +{ + static bool isPersistenceVarSet[256]; + + // TODO: Maybe not hardcode + std::uintptr_t playerStructBase = 0x16073B200; + std::uintptr_t playerStructSize = 0x4A4C0; + std::uintptr_t persistenceVar = 0x5BC; + + std::uintptr_t targetPlayerStruct = playerStructBase + client * playerStructSize; + + *(char*)(targetPlayerStruct + persistenceVar) = (char)0x5; + + if (!isPersistenceVarSet[client]) + { + printf("\n"); + printf("##################################################\n"); + printf("] SETTING PERSISTENCE VAR FOR CLIENT #%d\n", client); + printf("##################################################\n"); + printf("\n"); + isPersistenceVarSet[client] = true; + } + + return originalIsPersistenceDataAvailable(thisptr, client); +} \ No newline at end of file diff --git a/r5dev/src/hooks/hooks.cpp b/r5dev/src/hooks/hooks.cpp new file mode 100644 index 00000000..054083b8 --- /dev/null +++ b/r5dev/src/hooks/hooks.cpp @@ -0,0 +1,173 @@ +#include "pch.h" +#include "hooks.h" + +bool g_bBlockInput = false; + +void Hooks::InstallHooks() +{ + /////////////////////////////////////////////////////////////////////////////// + // Initialize Minhook + MH_Initialize(); + + /////////////////////////////////////////////////////////////////////////////// + // Hook Squirrel functions + MH_CreateHook(addr_SQVM_Print, &Hooks::SQVM_Print, NULL); + MH_CreateHook(addr_SQVM_LoadRson, &Hooks::SQVM_LoadRson, reinterpret_cast(&originalSQVM_LoadRson)); + MH_CreateHook(addr_SQVM_LoadScript, &Hooks::SQVM_LoadScript, reinterpret_cast(&originalSQVM_LoadScript)); + + /////////////////////////////////////////////////////////////////////////////// + // Hook Game Functions + MH_CreateHook(addr_CHLClient_FrameStageNotify, &Hooks::FrameStageNotify, reinterpret_cast(&originalFrameStageNotify)); + MH_CreateHook(addr_CVEngineServer_IsPersistenceDataAvailable, &Hooks::IsPersistenceDataAvailable, reinterpret_cast(&originalIsPersistenceDataAvailable)); + + /////////////////////////////////////////////////////////////////////////////// + // Hook Netchan functions + MH_CreateHook(addr_NET_ReceiveDatagram, &Hooks::NET_ReceiveDatagram, reinterpret_cast(&originalNET_ReceiveDatagram)); + MH_CreateHook(addr_NET_SendDatagram, &Hooks::NET_SendDatagram, reinterpret_cast(&originalNET_SendDatagram)); + + /////////////////////////////////////////////////////////////////////////////// + // Hook ConVar | ConCommand functions. + MH_CreateHook(addr_ConVar_IsFlagSet, &Hooks::ConVar_IsFlagSet, NULL); + MH_CreateHook(addr_ConCommand_IsFlagSet, &Hooks::ConCommand_IsFlagSet, NULL); + + /////////////////////////////////////////////////////////////////////////////// + // Hook WinAPI + HMODULE user32dll = GetModuleHandleA("user32.dll"); + void* SetCursorPosPtr = GetProcAddress(user32dll, "SetCursorPos"); + void* ClipCursorPtr = GetProcAddress(user32dll, "ClipCursor"); + void* GetCursorPosPtr = GetProcAddress(user32dll, "GetCursorPos"); + void* ShowCursorPtr = GetProcAddress(user32dll, "ShowCursor"); + + MH_CreateHook(SetCursorPosPtr, &Hooks::SetCursorPos, reinterpret_cast(&originalSetCursorPos)); + MH_CreateHook(ClipCursorPtr, &Hooks::ClipCursor, reinterpret_cast(&originalClipCursor)); + MH_CreateHook(GetCursorPosPtr, &Hooks::GetCursorPos, reinterpret_cast(&originalGetCursorPos)); + MH_CreateHook(ShowCursorPtr, &Hooks::ShowCursor, reinterpret_cast(&originalShowCursor)); + + /////////////////////////////////////////////////////////////////////////////// + // Hook Utility functions + MH_CreateHook(addr_MSG_EngineError, &Hooks::MSG_EngineError, reinterpret_cast(&originalMSG_EngineError)); + + /////////////////////////////////////////////////////////////////////////////// + // Enable Squirrel hooks + MH_EnableHook(addr_SQVM_Print); + MH_EnableHook(addr_SQVM_LoadRson); + MH_EnableHook(addr_SQVM_LoadScript); + + /////////////////////////////////////////////////////////////////////////////// + // Enable Game hooks + MH_EnableHook(addr_CHLClient_FrameStageNotify); + MH_EnableHook(addr_CVEngineServer_IsPersistenceDataAvailable); + + /////////////////////////////////////////////////////////////////////////////// + // Enable WinAPI hooks + MH_EnableHook(SetCursorPosPtr); + MH_EnableHook(ClipCursorPtr); + MH_EnableHook(GetCursorPosPtr); + MH_EnableHook(ShowCursorPtr); + + /////////////////////////////////////////////////////////////////////////////// + // Enabled Utility hooks + MH_EnableHook(addr_MSG_EngineError); +} + +void Hooks::RemoveHooks() +{ + /////////////////////////////////////////////////////////////////////////////// + // Unhook Squirrel functions + MH_RemoveHook(addr_SQVM_Print); + MH_RemoveHook(addr_SQVM_LoadRson); + MH_RemoveHook(addr_SQVM_LoadScript); + + /////////////////////////////////////////////////////////////////////////////// + // Unhook Game Functions + MH_RemoveHook(addr_CHLClient_FrameStageNotify); + MH_RemoveHook(addr_CVEngineServer_IsPersistenceDataAvailable); + + /////////////////////////////////////////////////////////////////////////////// + // Unhook Netchan functions + MH_RemoveHook(addr_NET_ReceiveDatagram); + MH_RemoveHook(addr_NET_SendDatagram); + + /////////////////////////////////////////////////////////////////////////////// + // Unhook ConVar | ConCommand functions. + MH_RemoveHook(addr_ConVar_IsFlagSet); + MH_RemoveHook(addr_ConCommand_IsFlagSet); + + /////////////////////////////////////////////////////////////////////////////// + // Unhook WinAPI + HMODULE user32dll = GetModuleHandleA("user32.dll"); + void* SetCursorPosPtr = GetProcAddress(user32dll, "SetCursorPos"); + void* ClipCursorPtr = GetProcAddress(user32dll, "ClipCursor"); + void* GetCursorPosPtr = GetProcAddress(user32dll, "GetCursorPos"); + void* ShowCursorPtr = GetProcAddress(user32dll, "ShowCursor"); + + MH_RemoveHook(SetCursorPosPtr); + MH_RemoveHook(ClipCursorPtr); + MH_RemoveHook(GetCursorPosPtr); + MH_RemoveHook(ShowCursorPtr); + + /////////////////////////////////////////////////////////////////////////////// + // Unhook Utility functions + MH_RemoveHook(addr_MSG_EngineError); + + /////////////////////////////////////////////////////////////////////////////// + // Reset Minhook + MH_Uninitialize(); +} + +void Hooks::ToggleNetHooks() +{ + static bool g_net = false; + + if (!g_net) + { + MH_EnableHook(addr_NET_ReceiveDatagram); + MH_EnableHook(addr_NET_SendDatagram); + printf("\n"); + printf("+--------------------------------------------------------+\n"); + printf("|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); + printf("+--------------------------------------------------------+\n"); + printf("\n"); + } + else + { + MH_DisableHook(addr_NET_ReceiveDatagram); + MH_DisableHook(addr_NET_SendDatagram); + printf("\n"); + printf("+--------------------------------------------------------+\n"); + printf("|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); + printf("+--------------------------------------------------------+\n"); + printf("\n"); + } + + g_net = !g_net; +} + +void Hooks::ToggleDevCommands() +{ + static bool g_dev = false; + + if (!g_dev) + { + MH_EnableHook(addr_ConVar_IsFlagSet); + MH_EnableHook(addr_ConCommand_IsFlagSet); + printf("\n"); + printf("+--------------------------------------------------------+\n"); + printf("|>>>>>>>>>>>>>| DEVONLY COMMANDS ACTIVATED |<<<<<<<<<<<<<|\n"); + printf("+--------------------------------------------------------+\n"); + printf("\n"); + + } + else + { + MH_DisableHook(addr_ConVar_IsFlagSet); + MH_DisableHook(addr_ConCommand_IsFlagSet); + printf("\n"); + printf("+--------------------------------------------------------+\n"); + printf("|>>>>>>>>>>>>| DEVONLY COMMANDS DEACTIVATED |<<<<<<<<<<<<|\n"); + printf("+--------------------------------------------------------+\n"); + printf("\n"); + } + + g_dev = !g_dev; +} diff --git a/r5dev/src/hooks/iconvar.cpp b/r5dev/src/hooks/iconvar.cpp new file mode 100644 index 00000000..ab5f4a61 --- /dev/null +++ b/r5dev/src/hooks/iconvar.cpp @@ -0,0 +1,58 @@ +#include "pch.h" +#include "hooks.h" + +bool Hooks::ConVar_IsFlagSet(int** cvar, int flag) +{ + int real_flags = *(*(cvar + (72 / (sizeof(void*)))) + (56 / sizeof(int))); + if (g_bDebugConsole) + { + printf("--------------------------------------------------\n"); + printf(" Flaged: %08X\n", real_flags); + } + // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY + real_flags &= 0xFFFFBFFD; + if (g_bDebugConsole) + { + printf(" Masked: %08X\n", real_flags); + printf(" Verify: %08X\n", flag); + printf("--------------------------------------------------\n"); + } + if (flag & 0x80000) { return true; } + + if (!g_bReturnAllFalse) + { + return (real_flags & flag) != 0; + } + else + { + return false; + } +} + +bool Hooks::ConCommand_IsFlagSet(int* cmd, int flag) +{ + int real_flags = *((cmd + (56 / sizeof(int)))); + if (g_bDebugConsole) + { + printf("--------------------------------------------------\n"); + printf(" Flaged: %08X\n", real_flags); + } + // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY + real_flags &= 0xFFFFBFFD; + if (g_bDebugConsole) + { + printf(" Masked: %08X\n", real_flags); + printf(" Verify: %08X\n", flag); + printf("--------------------------------------------------\n"); + } + if (flag & 0x80000) { return true; } + + if (!g_bReturnAllFalse) + { + return(real_flags & flag) != 0; + } + else + { + return false; + } +} \ No newline at end of file diff --git a/r5dev/src/hooks/msgbox.cpp b/r5dev/src/hooks/msgbox.cpp new file mode 100644 index 00000000..4592d1f1 --- /dev/null +++ b/r5dev/src/hooks/msgbox.cpp @@ -0,0 +1,15 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + MSG_EngineErrorFn originalMSG_EngineError = nullptr; +} + +int Hooks::MSG_EngineError(char* fmt, va_list args) +{ + printf("\nENGINE ERROR #####################################\n"); + vprintf(fmt, args); + + return originalMSG_EngineError(fmt, args); +} \ No newline at end of file diff --git a/r5dev/src/hooks/net.cpp b/r5dev/src/hooks/net.cpp new file mode 100644 index 00000000..59eb65f0 --- /dev/null +++ b/r5dev/src/hooks/net.cpp @@ -0,0 +1,37 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + NET_ReceiveDatagramFn originalNET_ReceiveDatagram = nullptr; + NET_SendDatagramFn originalNET_SendDatagram = nullptr; +} + +bool Hooks::NET_ReceiveDatagram(int sock, void* inpacket, bool raw) +{ + bool result = originalNET_ReceiveDatagram(sock, inpacket, raw); + if (result) + { + int i = NULL; + netpacket_t* pkt = (netpacket_t*)inpacket; + + /////////////////////////////////////////////////////////////////////////// + // Log received packet data + HexDump("[+] NET_ReceiveDatagram", 0, &pkt->data[i], pkt->wiresize); + } + + return result; +} + +unsigned int Hooks::NET_SendDatagram(SOCKET s, const char* buf, int len, int flags) +{ + unsigned int result = originalNET_SendDatagram(s, buf, len, flags); + if (result) + { + /////////////////////////////////////////////////////////////////////////// + // Log transmitted packet data + HexDump("[+] NET_SendDatagram", 0, buf, len); + } + + return result; +} \ No newline at end of file diff --git a/r5dev/src/hooks/sqvm.cpp b/r5dev/src/hooks/sqvm.cpp new file mode 100644 index 00000000..5285507c --- /dev/null +++ b/r5dev/src/hooks/sqvm.cpp @@ -0,0 +1,90 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + SQVM_LoadRsonFn originalSQVM_LoadRson = nullptr; + SQVM_LoadScriptFn originalSQVM_LoadScript = nullptr; +} + +void* Hooks::SQVM_Print(void* sqvm, char* fmt, ...) +{ + char buf[1024]; + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); + buf[IM_ARRAYSIZE(buf) - 1] = 0; + va_end(args); + Items.push_back(Strdup(buf)); + return NULL; +} + +__int64 Hooks::SQVM_LoadRson(const char* rson_name) +{ + char filepath[MAX_PATH] = { 0 }; + sprintf_s(filepath, MAX_PATH, "platform\\%s", rson_name); + + /////////////////////////////////////////////////////////////////////////////// + // Flip forward slashes in filepath to windows-style backslash + for (int i = 0; i < strlen(filepath); i++) + { + if (filepath[i] == '/') + { + filepath[i] = '\\'; + } + } + + /////////////////////////////////////////////////////////////////////////////// + // Returns the new path if the rson exists on the disk + if (FileExists(filepath) && originalSQVM_LoadRson(rson_name)) + { + printf("\n"); + printf("##################################################\n"); + printf("] '%s'\n", filepath); + printf("##################################################\n"); + printf("\n"); + + return originalSQVM_LoadRson(filepath); + } + + printf("\n"); + printf("##################################################\n"); + printf("] '%s'\n", rson_name); + printf("##################################################\n"); + printf("\n"); + + return originalSQVM_LoadRson(rson_name); +} + +bool Hooks::SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag) +{ + char filepath[MAX_PATH] = { 0 }; + sprintf_s(filepath, MAX_PATH, "platform\\%s", script_path); + + /////////////////////////////////////////////////////////////////////////////// + // Flip forward slashes in filepath to windows-style backslash + for (int i = 0; i < strlen(filepath); i++) + { + if (filepath[i] == '/') + { + filepath[i] = '\\'; + } + } + if (g_bDebugLoading) + { + printf(" [+] Loading SQVM Script '%s' ...\n", filepath); + } + /////////////////////////////////////////////////////////////////////////////// + // Returns true if the script exists on the disk + if (FileExists(filepath) && originalSQVM_LoadScript(sqvm, filepath, script_name, flag)) + { + return true; + } + if (g_bDebugLoading) + { + printf(" [!] FAILED. Try SP / VPK for '%s'\n", filepath); + } + + return originalSQVM_LoadScript(sqvm, script_path, script_name, flag); +} \ No newline at end of file diff --git a/r5dev/src/hooks/winapi.cpp b/r5dev/src/hooks/winapi.cpp new file mode 100644 index 00000000..a9a96905 --- /dev/null +++ b/r5dev/src/hooks/winapi.cpp @@ -0,0 +1,59 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + namespace + { + static POINT g_pLastCursorPos{ 0 }; + } + + GetCursorPosFn originalGetCursorPos = nullptr; + SetCursorPosFn originalSetCursorPos = nullptr; + ClipCursorFn originalClipCursor = nullptr; + ShowCursorFn originalShowCursor = nullptr; +} + +BOOL WINAPI Hooks::GetCursorPos(LPPOINT lpPoint) +{ + if (g_bBlockInput) + { + assert(lpPoint != nullptr); + *lpPoint = g_pLastCursorPos; + } + + return originalGetCursorPos(lpPoint); +} + +BOOL WINAPI Hooks::SetCursorPos(int X, int Y) +{ + g_pLastCursorPos.x = X; + g_pLastCursorPos.y = Y; + + if (g_bBlockInput) + { + return TRUE; + } + + return originalSetCursorPos(X, Y); +} + +BOOL WINAPI Hooks::ClipCursor(const RECT* lpRect) +{ + if (g_bBlockInput) + { + lpRect = nullptr; + } + + return originalClipCursor(lpRect); +} + +BOOL WINAPI Hooks::ShowCursor(BOOL bShow) +{ + if (g_bBlockInput) + { + bShow = TRUE; + } + + return originalShowCursor(bShow); +} \ No newline at end of file diff --git a/r5dev/src/id3dx.cpp b/r5dev/src/id3dx.cpp index 8b672a56..013f4c06 100644 --- a/r5dev/src/id3dx.cpp +++ b/r5dev/src/id3dx.cpp @@ -1,9 +1,7 @@ #include "pch.h" #include "id3dx.h" -#include "input.h" -#include "enums.h" +#include "hooks.h" #include "console.h" -#include "detours.h" #include "overlay.h" #include "patterns.h" #include "gameclasses.h" @@ -269,7 +267,7 @@ void DrawImGui() GameGlobals::InputSystem->EnableInput(false); // Disable input. DrawConsole(); } - if(g_bShowBrowser) + if (g_bShowBrowser) { GameGlobals::InputSystem->EnableInput(false); // Disable input. DrawBrowser(); @@ -354,6 +352,8 @@ HRESULT GetDeviceAndCtxFromSwapchain(IDXGISwapChain* pSwapChain, ID3D11Device** return ret; } +IDXGIResizeBuffers originalResizeBuffers = nullptr; + HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags) { g_bShowConsole = false; @@ -369,16 +369,18 @@ HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount ImGui_ImplDX11_CreateDeviceObjects(); /////////////////////////////////////////////////////////////////////////////// - return g_oResizeBuffers(pSwapChain, nBufferCount, nWidth, nHeight, dxFormat, nSwapChainFlags); + return originalResizeBuffers(pSwapChain, nBufferCount, nWidth, nHeight, dxFormat, nSwapChainFlags); } +IDXGISwapChainPresent originalPresent = nullptr; + HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags) { if (!g_bInitialized) { if (FAILED(GetDeviceAndCtxFromSwapchain(pSwapChain, &g_pDevice, &g_pDeviceContext))) { - return g_fnIDXGISwapChainPresent(pSwapChain, nSyncInterval, nFlags); + return originalPresent(pSwapChain, nSyncInterval, nFlags); std::cout << "+--------------------------------------------------------+" << std::endl; std::cout << "| >>>>>>>>>>| GET DVS AND CTX FROM SCP FAILED |<<<<<<<<< |" << std::endl; std::cout << "+--------------------------------------------------------+" << std::endl; @@ -399,7 +401,7 @@ HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT n DrawImGui(); g_bInitialized = true; /////////////////////////////////////////////////////////////////////////////// - return g_fnIDXGISwapChainPresent(pSwapChain, nSyncInterval, nFlags); + return originalPresent(pSwapChain, nSyncInterval, nFlags); } //################################################################################# @@ -408,42 +410,45 @@ HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT n void InstallDXHooks() { - g_oPostMessageA = (IPostMessageA)DetourFindFunction("user32.dll", "PostMessageA"); - g_oPostMessageW = (IPostMessageW)DetourFindFunction("user32.dll", "PostMessageW"); - /////////////////////////////////////////////////////////////////////////////// - // Begin the detour transaction - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); + HMODULE user32dll = GetModuleHandleA("user32.dll"); + + IPostMessageA PostMessageA = (IPostMessageA)GetProcAddress(user32dll, "PostMessageA"); + IPostMessageW PostMessageW = (IPostMessageW)GetProcAddress(user32dll, "PostMessageW"); + /////////////////////////////////////////////////////////////////////////////// // Hook PostMessage - DetourAttach(&(LPVOID&)g_oPostMessageA, (PBYTE)HPostMessageA); - DetourAttach(&(LPVOID&)g_oPostMessageW, (PBYTE)HPostMessageW); + MH_CreateHook(PostMessageA, &HPostMessageA, reinterpret_cast(&g_oPostMessageA)); + MH_CreateHook(PostMessageW, &HPostMessageW, reinterpret_cast(&g_oPostMessageW)); + /////////////////////////////////////////////////////////////////////////////// // Hook SwapChain - DetourAttach(&(LPVOID&)g_fnIDXGISwapChainPresent, (PBYTE)Present); - DetourAttach(&(LPVOID&)g_oResizeBuffers, (PBYTE)GetResizeBuffers); + MH_CreateHook(g_fnIDXGISwapChainPresent, &Present, reinterpret_cast(&originalPresent)); + MH_CreateHook(g_oResizeBuffers, &GetResizeBuffers, reinterpret_cast(&originalResizeBuffers)); + /////////////////////////////////////////////////////////////////////////////// - // Commit the transaction - DetourTransactionCommit(); + // Enable hooks + MH_EnableHook(PostMessageA); + MH_EnableHook(PostMessageW); + MH_EnableHook(g_fnIDXGISwapChainPresent); + MH_EnableHook(g_oResizeBuffers); } void RemoveDXHooks() { - /////////////////////////////////////////////////////////////////////////////// - // Begin the detour transaction - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); + HMODULE user32dll = GetModuleHandleA("user32.dll"); + + IPostMessageA PostMessageA = (IPostMessageA)GetProcAddress(user32dll, "PostMessageA"); + IPostMessageW PostMessageW = (IPostMessageW)GetProcAddress(user32dll, "PostMessageW"); + /////////////////////////////////////////////////////////////////////////////// // Unhook PostMessage - DetourDetach(&(LPVOID&)g_oPostMessageA, (PBYTE)HPostMessageA); - DetourDetach(&(LPVOID&)g_oPostMessageW, (PBYTE)HPostMessageW); + MH_RemoveHook(PostMessageA); + MH_RemoveHook(PostMessageW); + /////////////////////////////////////////////////////////////////////////////// // Unhook SwapChain - DetourDetach(&(LPVOID&)g_fnIDXGISwapChainPresent, (PBYTE)Present); - DetourDetach(&(LPVOID&)g_oResizeBuffers, (PBYTE)GetResizeBuffers); - /////////////////////////////////////////////////////////////////////////////// - // Commit the transaction - DetourTransactionCommit(); + MH_RemoveHook(g_fnIDXGISwapChainPresent); + MH_RemoveHook(g_oResizeBuffers); /////////////////////////////////////////////////////////////////////////////// // Shutdown ImGui diff --git a/r5dev/src/input.cpp b/r5dev/src/input.cpp index d1403e21..e85ccc19 100644 --- a/r5dev/src/input.cpp +++ b/r5dev/src/input.cpp @@ -1,119 +1,2 @@ #include "pch.h" -#include "input.h" - -/*----------------------------------------------------------------------------- - * _input.cpp - *-----------------------------------------------------------------------------*/ - -/////////////////////////////////////////////////////////////////////////////// -typedef BOOL(WINAPI* IGetCursorPos)(LPPOINT lpPoint); -typedef BOOL(WINAPI* ISetCursorPos)(int nX, int nY); -typedef BOOL(WINAPI* IClipCursor)(const RECT* lpRect); -typedef BOOL(WINAPI* IShowCursor)(BOOL bShow); - -/////////////////////////////////////////////////////////////////////////////// -static IGetCursorPos g_oGetCursorPos = nullptr; -static ISetCursorPos g_oSetCursorPos = nullptr; -static IClipCursor g_oClipCursor = nullptr; -static IShowCursor g_oShowCursor = nullptr; - -/////////////////////////////////////////////////////////////////////////////// -static POINT g_pLastCursorPos { 0 }; -extern BOOL g_bBlockInput = false; - -//############################################################################# -// INITIALIZATION -//############################################################################# - -void SetupIPHooks() -{ - g_oSetCursorPos = (ISetCursorPos)DetourFindFunction("user32.dll", "SetCursorPos"); - g_oClipCursor = (IClipCursor )DetourFindFunction("user32.dll", "ClipCursor" ); - g_oGetCursorPos = (IGetCursorPos)DetourFindFunction("user32.dll", "GetCursorPos"); - g_oShowCursor = (IShowCursor )DetourFindFunction("user32.dll", "ShowCursor" ); -} - -//############################################################################# -// INPUT HOOKS -//############################################################################# - -BOOL WINAPI HGetCursorPos(LPPOINT lpPoint) -{ - if (g_bBlockInput) - { - assert(lpPoint != nullptr); - *lpPoint = g_pLastCursorPos; - } - - return g_oGetCursorPos(lpPoint); -} - -BOOL WINAPI HSetCursorPos(int X, int Y) -{ - g_pLastCursorPos.x = X; - g_pLastCursorPos.y = Y; - - if (g_bBlockInput) - { - return TRUE; - } - - return g_oSetCursorPos(X, Y); -} - -BOOL WINAPI HClipCursor(const RECT* lpRect) -{ - if (g_bBlockInput) - { - lpRect = nullptr; - } - - return g_oClipCursor(lpRect); -} - -BOOL WINAPI HShowCursor(BOOL bShow) -{ - if (g_bBlockInput) - { - bShow = TRUE; - } - - return g_oShowCursor(bShow); -} - -//############################################################################# -// MANAGEMENT -//############################################################################# - -void InstallIPHooks() -{ - SetupIPHooks(); - /////////////////////////////////////////////////////////////////////////// - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - /////////////////////////////////////////////////////////////////////////// - DetourAttach(&(LPVOID&)g_oGetCursorPos, (PBYTE)HGetCursorPos); - DetourAttach(&(LPVOID&)g_oSetCursorPos, (PBYTE)HSetCursorPos); - DetourAttach(&(LPVOID&)g_oClipCursor, (PBYTE)HClipCursor); - DetourAttach(&(LPVOID&)g_oShowCursor, (PBYTE)HShowCursor); - - /////////////////////////////////////////////////////////////////////////// - DetourTransactionCommit(); -} - -void RemoveIPHooks() -{ - /////////////////////////////////////////////////////////////////////////// - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - /////////////////////////////////////////////////////////////////////////// - DetourDetach(&(LPVOID&)g_oGetCursorPos, (PBYTE)HGetCursorPos); - DetourDetach(&(LPVOID&)g_oSetCursorPos, (PBYTE)HSetCursorPos); - DetourDetach(&(LPVOID&)g_oClipCursor, (PBYTE)HClipCursor); - DetourDetach(&(LPVOID&)g_oShowCursor, (PBYTE)HShowCursor); - - /////////////////////////////////////////////////////////////////////////// - DetourTransactionCommit(); -} +#include "input.h" \ No newline at end of file diff --git a/r5dev/src/overlay.cpp b/r5dev/src/overlay.cpp index cfce7624..898e63d6 100644 --- a/r5dev/src/overlay.cpp +++ b/r5dev/src/overlay.cpp @@ -67,7 +67,7 @@ void CGameConsole::Draw(const char* title) /////////////////////////////////////////////////////////////////////// if (ImGui::SmallButton("Developer mode")) { - ToggleDevCommands(); + Hooks::ToggleDevCommands(); AddLog("+--------------------------------------------------------+\n"); AddLog("|>>>>>>>>>>>>>>| DEVONLY COMMANDS TOGGLED |<<<<<<<<<<<<<<|\n"); AddLog("+--------------------------------------------------------+\n"); @@ -76,7 +76,7 @@ void CGameConsole::Draw(const char* title) ImGui::SameLine(); if (ImGui::SmallButton("Netchannel Trace")) { - ToggleNetHooks(); + Hooks::ToggleNetHooks(); AddLog("+--------------------------------------------------------+\n"); AddLog("|>>>>>>>>>>>>>>| NETCHANNEL TRACE TOGGLED |<<<<<<<<<<<<<<|\n"); AddLog("+--------------------------------------------------------+\n"); @@ -266,7 +266,7 @@ void CGameConsole::ProcessCommand(const char* command_line) void CGameConsole::ExecCommand(const char* command_line) { - org_CommandExecute(NULL, command_line); + addr_CommandExecute(NULL, command_line); } /////////////////////////////////////////////////////////////////////////// @@ -589,7 +589,7 @@ void CCompanion::HostServerSection() if (StartAsDedi) { - ToggleDevCommands(); + Hooks::ToggleDevCommands(); } } else @@ -671,7 +671,7 @@ void CCompanion::ProcessCommand(const char* command_line) void CCompanion::ExecCommand(const char* command_line) { - org_CommandExecute(NULL, command_line); + addr_CommandExecute(NULL, command_line); } //#############################################################################