diff --git a/AMBuilder b/AMBuilder index 9cd7281..8d50cb6 100644 --- a/AMBuilder +++ b/AMBuilder @@ -17,11 +17,24 @@ for sdk_target in MMSPlugin.sdk_targets: ])] binary.compiler.cxxincludes += [ + os.path.join(builder.currentSourcePath, 'vendor', 'funchook', 'include'), os.path.join(builder.sourcePath, 'public'), os.path.join(builder.sourcePath, 'include'), os.path.join(builder.sourcePath, '..', 'SchemaEntity'), ] + if binary.compiler.target.platform == 'linux': + binary.compiler.postlink += [ + os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'libfunchook.a'), + os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'libdistorm.a'), + ] + elif binary.compiler.target.platform == 'windows': + binary.compiler.postlink += [ + os.path.join('psapi.lib'), + os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'funchook.lib'), + os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'distorm.lib'), + ] + binary.sources += [ 'statusmodifier.cpp', 'gameconfig.cpp', diff --git a/gamedata/statusmodifier.jsonc b/gamedata/statusmodifier.jsonc index cff4d70..217158c 100644 --- a/gamedata/statusmodifier.jsonc +++ b/gamedata/statusmodifier.jsonc @@ -1,4 +1,13 @@ { + "Signatures": { + // Find string 'Client %d(%s) tried to send a RebroadcastSourceId msg.' which is in + // a logging call. On linux search for the one which is being used in CServerSideClient. + "FilterMessage": { + "library": "engine", + "windows": "40 53 48 83 EC ? 48 8B 02 48 8B D9 48 8B CA", + "linux": "55 48 89 E5 53 48 89 FB 48 89 F7 48 83 EC ? 48 8B 06 FF 50 ? 48 89 C2 31 C0 48 39 15 ? ? ? ? 74 ? 48 8B 5D ? C9 C3 0F 1F 80 ? ? ? ? 8B 3D ? ? ? ? BE ? ? ? ? E8 ? ? ? ? 84 C0 74 ? 48 8B 43 ? 4C 8D 05 ? ? ? ? BE ? ? ? ? 0F B7 4B" + } + }, "Offsets": { "GameEntitySystem": { "windows": 88, @@ -8,11 +17,10 @@ "windows": 32, "linux": 32 }, - "SendNetMessage": { - "windows": 15, - "linux": 16 + "CServerSideClientBase::m_nClientSlot": { + "windows": 72, + "linux": 72 } }, - "Signatures": {}, "Patches": {} } \ No newline at end of file diff --git a/statusmodifier.cpp b/statusmodifier.cpp index caf4c2c..6e94eef 100644 --- a/statusmodifier.cpp +++ b/statusmodifier.cpp @@ -7,11 +7,9 @@ #include #include #include -#include #include "protobuf/generated/cstrike15_usermessages.pb.h" #include "protobuf/generated/usermessages.pb.h" -#include "protobuf/generated/netmessages.pb.h" #include #include @@ -21,7 +19,6 @@ #include "gameconfig.h" #include "playermanager.h" #include "recipientfilter.h" -#include "serversideclient.h" #include "utils.h" StatusModifier g_StatusModifier; @@ -29,14 +26,18 @@ PLUGIN_EXPOSE(StatusModifier, g_StatusModifier); IVEngineServer2 *g_pEngineServer2 = nullptr; CGameEntitySystem *g_pEntitySystem = nullptr; IGameEventSystem *g_pGameEventSystem = nullptr; -int g_iSendNetMessageId = -1; CGameConfig *g_GameConfig = nullptr; CPlayerManager *g_PlayerManager = nullptr; std::unordered_set g_mExcludeSlots; + +void (*FilterMessage_t)(void *pClient, CNetMessage *pMessage, INetChannel *pChannel) = nullptr; + using namespace DynLibUtils; +funchook_t *m_FilterMessageHook; + std::vector g_StatusArray; std::string g_sServerIP; std::chrono::time_point g_chServerStartTime; @@ -56,8 +57,6 @@ class GameSessionConfiguration_t { }; -SH_DECL_MANUALHOOK2_void(SendNetMessage_t, 0, 0, 0, CNetMessage *, NetChannelBufType_t); - SH_DECL_HOOK3_void(INetworkServerService, StartupServer, SH_NOATTRIB, 0, const GameSessionConfiguration_t &, ISource2WorldSession *, const char *); @@ -71,38 +70,7 @@ SH_DECL_HOOK6(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, CPlayerSlot, const char *, uint64, const char *, bool, CBufferString *); -void ShowCustomStatusMessage(int slot) -{ - bool playerlist = false; - std::string buffer; - for (const std::string &message : g_StatusArray) - { - if (message.rfind("#HEADER#", 0) == 0) - { - ClientPrint(slot, HUD_PRINTCONSOLE, message.c_str() + 8); - continue; - } - - if (!playerlist && message.find("{PLAYER") != std::string::npos && message.find("{PLAYERC") == std::string::npos) - { - for (int i = 0; i < MAXPLAYERS; i++) - { - if (g_mExcludeSlots.find(i) != g_mExcludeSlots.end()) - continue; - - buffer = CheckMessageVariables(message, i, g_HeaderLayout); - if (buffer.length() > 0) - ClientPrint(slot, HUD_PRINTCONSOLE, buffer.c_str()); - } - playerlist = true; - continue; - } - - buffer = CheckMessageVariables(message, -1, g_HeaderLayout); - if (buffer.length() > 0) - ClientPrint(slot, HUD_PRINTCONSOLE, buffer.c_str()); - } -} +void Hook_FilterMessage(void *pClient, CNetMessage *pMessage, INetChannel *pChannel); bool StatusModifier::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) @@ -152,16 +120,23 @@ bool StatusModifier::Load(PluginId id, ISmmAPI *ismm, char *error, CModule libengine(g_pEngineServer2); - void *pServerSideClientVTable = libengine.GetVirtualTableByName("CServerSideClient"); - if (!pServerSideClientVTable) + const char *szSignature = g_GameConfig->GetSignature("FilterMessage"); + + FilterMessage_t = libengine.FindPattern(szSignature) + .RCast(); + if (!FilterMessage_t) { - snprintf(error, maxlen, "Failed to find ServerSideClient vtable"); - ErrorLog("[StatusModifier] Failed to find ServerSideClient vtable"); + snprintf(error, maxlen, "Failed to find function to get FilterMessage"); + ErrorLog("[%s] Failed to find function to get FilterMessage", + g_PLAPI->GetLogTag()); return false; } - SH_MANUALHOOK_RECONFIGURE(SendNetMessage_t, g_GameConfig->GetOffset("SendNetMessage"), 0, 0); - g_iSendNetMessageId = SH_ADD_MANUALDVPHOOK(SendNetMessage_t, pServerSideClientVTable, SH_MEMBER(this, &StatusModifier::Hook_SendNetMessage), false); + m_FilterMessageHook = funchook_create(); + funchook_prepare(m_FilterMessageHook, (void **)&FilterMessage_t, + (void *)Hook_FilterMessage); + funchook_install(m_FilterMessageHook, 0); + ConMsg("[StatusModifier] FilterMessage hooked successfully.\n"); META_CONVAR_REGISTER(FCVAR_RELEASE | FCVAR_GAMEDLL); @@ -196,7 +171,8 @@ bool StatusModifier::Unload(char *error, size_t maxlen) SH_REMOVE_HOOK(IServerGameClients, ClientConnect, g_pSource2GameClients, SH_MEMBER(this, &StatusModifier::Hook_ClientConnect), false); - SH_REMOVE_HOOK_ID(g_iSendNetMessageId); + if (m_FilterMessageHook) + funchook_destroy(m_FilterMessageHook); if (g_PlayerManager) delete g_PlayerManager; @@ -213,6 +189,55 @@ void StatusModifier::Hook_StartupServer( g_mExcludeSlots.clear(); } +void FASTCALL Hook_FilterMessage(void *pClient, CNetMessage *pMessage, INetChannel *pChannel) +{ + if (!pClient || !pMessage) + { + FilterMessage_t(pClient, pMessage, pChannel); + return; + } + + int msgid = pMessage->GetNetMessage()->GetNetMessageInfo()->m_MessageId; + static int playerIndex = g_GameConfig->GetOffset("CServerSideClientBase::m_nClientSlot") - WIN_LINUX(8, 48); + int slot = *(int *)((uintptr_t)pClient + playerIndex); + + if (msgid != clc_ServerStatus) + { + FilterMessage_t(pClient, pMessage, pChannel); + return; + } + + bool playerlist = false; + std::string buffer; + for (const std::string &message : g_StatusArray) + { + if (message.rfind("#HEADER#", 0) == 0) + { + ClientPrint(slot, HUD_PRINTCONSOLE, message.c_str() + 8); + continue; + } + + if (!playerlist && message.find("{PLAYER") != std::string::npos && message.find("{PLAYERC") == std::string::npos) + { + for (int i = 0; i < MAXPLAYERS; i++) + { + if (g_mExcludeSlots.find(i) != g_mExcludeSlots.end()) + continue; + + buffer = CheckMessageVariables(message, i, g_HeaderLayout); + if (buffer.length() > 0) + ClientPrint(slot, HUD_PRINTCONSOLE, buffer.c_str()); + } + playerlist = true; + continue; + } + + buffer = CheckMessageVariables(message, -1, g_HeaderLayout); + if (buffer.length() > 0) + ClientPrint(slot, HUD_PRINTCONSOLE, buffer.c_str()); + } +} + void StatusModifier::Hook_OnClientConnected(CPlayerSlot slot, const char *pszName, uint64 xuid, const char *pszNetworkID, @@ -247,22 +272,6 @@ void StatusModifier::Hook_ClientDisconnect(CPlayerSlot slot, g_PlayerManager->OnClientDisconnect(slot); } -void StatusModifier::Hook_SendNetMessage(CNetMessage *pData, NetChannelBufType_t bufType) -{ - CServerSideClient *pClient = META_IFACEPTR(CServerSideClient); - if (!pClient || !pData) - RETURN_META(MRES_IGNORED); - - int msgid = pData->GetNetMessage()->GetNetMessageInfo()->m_MessageId; - // ConMsg("%d called messageId: %d\n", pClient->GetPlayerSlot().Get(), msgid); - if (msgid == svc_Print) - { - ShowCustomStatusMessage(pClient->GetPlayerSlot().Get()); - RETURN_META(MRES_SUPERCEDE); - } - RETURN_META(MRES_IGNORED); -} - void LoadConfig() { const char *pszConfigPath = diff --git a/statusmodifier.h b/statusmodifier.h index cd25df5..7e1aee4 100644 --- a/statusmodifier.h +++ b/statusmodifier.h @@ -16,7 +16,9 @@ #include "convar.h" #include "module.h" +#include "funchook.h" #include "bitvec.h" +#include "serversideclient.h" #ifdef _WIN32 #define ROOTBIN "/bin/win64/" @@ -41,7 +43,6 @@ class StatusModifier final : public ISmmPlugin, public IMetamodListener bool Unload(char *error, size_t maxlen); void Hook_StartupServer(const GameSessionConfiguration_t &config, ISource2WorldSession *pSession, const char *pszMapName); - void Hook_SendNetMessage(CNetMessage *pData, NetChannelBufType_t bufType); void Hook_OnClientConnected(CPlayerSlot slot, const char *pszName, uint64 xuid, const char *pszNetworkID, const char *pszAddress, bool bFakePlayer); bool Hook_ClientConnect(CPlayerSlot slot, const char *pszName, uint64 xuid, const char *pszNetworkID, bool unk1, CBufferString *pRejectReason); void Hook_ClientDisconnect(CPlayerSlot slot, ENetworkDisconnectionReason reason, const char *pszName, uint64 xuid, const char *pszNetworkID); diff --git a/utils.h b/utils.h index 32cc194..d20dd2e 100644 --- a/utils.h +++ b/utils.h @@ -1,13 +1,19 @@ #pragma once extern IGameEventSystem *g_pGameEventSystem; -extern CGameConfig *g_GameConfig; extern ISmmAPI *g_SMAPI; +extern CGameConfig *g_GameConfig; extern std::string g_sServerIP; #define HUD_PRINTCONSOLE 2 +#ifdef _WIN32 +#define WIN_LINUX(win, linux) win +#else +#define WIN_LINUX(win, linux) linux +#endif + void ClientPrint(CPlayerSlot slot, int hud_dest, const char *msg, ...) { va_list args; diff --git a/vendor/funchook/LICENSE b/vendor/funchook/LICENSE new file mode 100644 index 0000000..2da840d --- /dev/null +++ b/vendor/funchook/LICENSE @@ -0,0 +1,360 @@ +Funchook is distributed under the terms of the GNU General Public +License version 2 or later with the following clarification and +special exception. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from or +based on this library. If you modify this library, you must extend this +exception to your version of the library. + +===================================================================== + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/vendor/funchook/README.md b/vendor/funchook/README.md new file mode 100644 index 0000000..419021f --- /dev/null +++ b/vendor/funchook/README.md @@ -0,0 +1,273 @@ +Funchook - an API hook library +============================== + +[![tests](https://github.com/kubo/funchook/actions/workflows/run-tests.yml/badge.svg?branch=master)](https://github.com/kubo/funchook/actions/workflows/run-tests.yml) + +This library depends on one of the following disassemblers. + +On x86_64 and x86 +* [diStorm3][] (default) +* [zydis][] (when `-DFUNCHOOK_DISASM=zydis` is passed to the `cmake` command) +* [capstone][] (when `-DFUNCHOOK_DISASM=capstone` is passed to the `cmake` command) + +On arm64 +* [capstone][] + +TODO +---- + +* write documents. + +News +---- + +### 2.0.0 (20XX-XX-XX) + +* Add `funchook_prepare_with_params()` to support prehook. +* Add `funchook_get_arg()` to get arguments in prehook. + +### 1.1.1 (2022-10-02) + +* More permissive check for page allocation mmap ([#25][]) +* Flush instruction cache for arm64. It does nothing for intel CPU. +* Disassember engine + * Upgrade capstone to 4.0.2 + * Upgrade distorm to 3.5.2 +* CMake + * Allow user to specify FUNCHOOK_CPU explicitly ([#19][]) + * Avoid polluting global include and link dirs ([#20][]) + * Use target based compile options for gcc's -Wall ([#21][]) + * Use ExternalProject_add to download captone only ([#30][]) + * Add option FUNCHOOK_INSTALL ([#31][]) + * Use "FUNCHOOK_CPU MATCHES " ([#32][]) +* Documentation + * added example usage from python ([#22][]) +* Fix tests on Android ([#29][]) + +### 1.1.0 (2020-03-22) + +* Arm64 Linux support. [capstone][] is used as the disassembler library on arm64. +* Options to use [zydis][] and [capstone][] as a disassembler library on x86_64 and x86. +* `extern "C"` was added in funchook.h for C++. ([#15][]) +* Libc-compatible functions were removed to simplify code. + +### 1.0.0 (2020-01-19) + +* [diStorm3][] is used as the disassembler library. +* Libc-compatible functions were implemented on Linux in order not to hook function calls issued by funchook itself. + +Supported Platforms +------------------- + +* Linux x86_64 +* Linux x86 +* Linux arm64 (since 1.1.0) +* macOS x86_64 (Functions in executables cannot be hooked when Xcode version >= 11.0. (*1)) +* Windows x64 (except C-runtime functions under [Wine][]) +* Windows 32-bit + +*1 [`mprotect`] fails with EACCES. + +Unsupported Platforms +--------------------- + +* macOS arm64 (*1) + +*1 I received a mail that [`mprotect`] failed with `EINVAL`. Apple seems to prevent executable memory regions from being writable. + +Compilation and installation +----------- + +### Unix + +```shell +$ git clone --recursive https://github.com/kubo/funchook.git +$ mkdir build +$ cd build +$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/path/to/install/directory ../funchook +$ make +$ make install +``` + +* Available [`CMAKE_BUILD_TYPE`][] values are empty(default), `Debug`, `Release`, `RelWithDebInfo`(release build with debug information) and `MinSizeRel`. +* When [`CMAKE_INSTALL_PREFIX`][] isn't set, funchook is installed at `/usr/local`. + + installed files: + * `${CMAKE_INSTALL_PREFIX}/include/funchook.h` (header file) + * `${CMAKE_INSTALL_PREFIX}/lib/libfunchook.so` (symbolic link to `libfunchook.so.1`) + * `${CMAKE_INSTALL_PREFIX}/lib/libfunchook.so.1` ([soname][]; symbolic link to `libfunchook.so.1.1.0`) + * `${CMAKE_INSTALL_PREFIX}/lib/libfunchook.so.1.1.0` (shared library) + * `${CMAKE_INSTALL_PREFIX}/lib/libfunchook.a` (static library) + +### Windows + +Here is an example to compile funchook with Visual Studio 2017 Win64. +Change the argument of `-G` to use other compilers. + +```shell +$ git clone --recursive https://github.com/kubo/funchook.git +$ mkdir build +$ cd build +$ cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_INSTALL_PREFIX=c:\path\to\install\directory ..\funchook +$ cmake --build . --config Release --target INSTALL +``` + +* Available `-G` arguments (generators) are listed in the output of `cmake --help`. +* Available `--config` arguments are `Debug`(default), `Release`, `RelWithDebInfo` and `MinSizeRel`. +* When [`CMAKE_INSTALL_PREFIX`][] isn't set, funchook is installed at `c:\Program Files\funchook`. + + installed files: + * `${CMAKE_INSTALL_PREFIX}\include\funchook.h` (header file) + * `${CMAKE_INSTALL_PREFIX}\bin\funchook.dll` (shared library) + * `${CMAKE_INSTALL_PREFIX}\bin\funchook.pdb` (debug file for `funchook.dll` when `--config` is `Debug` or `RelWithDebInfo`) + * `${CMAKE_INSTALL_PREFIX}\lib\funchook.lib` (static library) + * `${CMAKE_INSTALL_PREFIX}\lib\funchook_dll.lib` (import library for `funchook.dll`) + +Example +------- + +```c +static ssize_t (*send_func)(int sockfd, const void *buf, size_t len, int flags); +static ssize_t (*recv_func)(int sockfd, void *buf, size_t len, int flags); + +static ssize_t send_hook(int sockfd, const void *buf, size_t len, int flags); +{ + ssize_t rv; + + ... do your task: logging, etc. ... + rv = send_func(sockfd, buf, len, flags); /* call the original send(). */ + ... do your task: logging, checking the return value, etc. ... + return rv; +} + +static ssize_t recv_hook(int sockfd, void *buf, size_t len, int flags); +{ + ssize_t rv; + + ... do your task: logging, etc. ... + rv = recv_func(sockfd, buf, len, flags); /* call the original recv(). */ + ... do your task: logging, checking received data, etc. ... + return rv; +} + +int install_hooks() +{ + funchook_t *funchook = funchook_create(); + int rv; + + /* Prepare hooking. + * The return value is used to call the original send function + * in send_hook. + */ + send_func = send; + rv = funchook_prepare(funchook, (void**)&send_func, send_hook); + if (rv != 0) { + /* error */ + ... + } + + /* ditto */ + recv_func = recv; + rv = funchook_prepare(funchook, (void**)&recv_func, recv_hook); + if (rv != 0) { + /* error */ + ... + } + + /* Install hooks. + * The first 5-byte code of send() and recv() are changed respectively. + */ + rv = funchook_install(funchook, 0); + if (rv != 0) { + /* error */ + ... + } +} + +``` + +Example - Using Python ctypes +----------------------------- +```python +# should work on python 2.7/3 windows/linux + +# load funchook +import ctypes +fh_lib = ctypes.cdll.LoadLibrary('/path/to/funchook/dll/or/so') + +# define signatures +funchook_create = fh_lib.funchook_create +funchook_create.restype = ctypes.c_void_p +funchook_create.argtypes = [] + +funchook_prepare = fh_lib.funchook_prepare +funchook_prepare.restype = ctypes.c_ssize_t +funchook_prepare.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] + +funchook_install = fh_lib.funchook_install +funchook_install.restype = ctypes.c_ssize_t +funchook_install.argtypes = [ctypes.c_void_p, ctypes.c_int] + +PySys_WriteStdout = ctypes.pythonapi.PySys_WriteStdout +PySys_WriteStdout.restype = None +PySys_WriteStdout.argtypes=[ctypes.c_char_p] + +# must keep those references alive, or stuff will be GC'd and weird errors will occur +global orig_write, hook, orig_write_ptr + +# create hook (this function will replace the original function) +hook_type = ctypes.PYFUNCTYPE(None, ctypes.c_char_p) +orig_write = None +def hook_impl(msg): + print('about to write: ' + str(msg)) # do what we want + orig_write(msg) # call the original function + +hook = hook_type(hook_impl) + +fh = funchook_create() +# create a pointer object with the function address +orig_write_ptr = ctypes.c_void_p(ctypes.c_void_p.from_address(ctypes.addressof(PySys_WriteStdout)).value) +# orig_write_ptr.value will get a ptr to the original PySys_WriteStdout and PySys_WriteStdout will now point to the hook +ret = funchook_prepare(fh, ctypes.addressof(orig_write_ptr), hook) +assert not ret, 'ret is ' + str(ret) +ret = funchook_install(fh, 0) +assert not ret, 'ret is ' + str(ret) +orig_write = hook_type.from_address(ctypes.addressof(orig_write_ptr)) +PySys_WriteStdout(b'hi there\n') +``` + +License +------- + +GPLv2 or later with a [GPL linking exception][]. + +You can use funchook in any software. Though funchook is licensed under +the GPL, it doesn't affect outside of funchook due to the linking exception. +You have no need to open your souce code under the GPL except funchook itself. + +If you modify funchook itself and release it, the modifed part must be +open under the GPL with or without the linking exception because funchook +itself is under the GPL. + +[diStorm3][] and [capstone][] are released under the 3-clause BSD license. +[zydis][] is released under the MIT license. They are compatible with the GPL. + +[GPL linking exception]: https://en.wikipedia.org/wiki/GPL_linking_exception +[diStorm3]: https://github.com/gdabah/distorm/ +[zydis]: https://github.com/zyantific/zydis +[capstone]: https://github.com/aquynh/capstone +[Wine]: https://www.winehq.org/ +[`CMAKE_BUILD_TYPE`]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html +[`CMAKE_INSTALL_PREFIX`]: https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html +[soname]: https://en.wikipedia.org/wiki/Soname +[#15]: https://github.com/kubo/funchook/issues/15 +[#19]: https://github.com/kubo/funchook/pull/19 +[#20]: https://github.com/kubo/funchook/pull/20 +[#21]: https://github.com/kubo/funchook/pull/21 +[#22]: https://github.com/kubo/funchook/pull/22 +[#25]: https://github.com/kubo/funchook/pull/25 +[#29]: https://github.com/kubo/funchook/pull/29 +[#30]: https://github.com/kubo/funchook/pull/30 +[#31]: https://github.com/kubo/funchook/pull/31 +[#32]: https://github.com/kubo/funchook/pull/32 +[`mprotect`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/mprotect.2.html diff --git a/vendor/funchook/include/funchook.h b/vendor/funchook/include/funchook.h new file mode 100644 index 0000000..5c6ed34 --- /dev/null +++ b/vendor/funchook/include/funchook.h @@ -0,0 +1,167 @@ +/* + * This file is part of Funchook. + * https://github.com/kubo/funchook + * + * Funchook is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * As a special exception, the copyright holders of this library give you + * permission to link this library with independent modules to produce an + * executable, regardless of the license terms of these independent + * modules, and to copy and distribute the resulting executable under + * terms of your choice, provided that you also meet, for each linked + * independent module, the terms and conditions of the license of that + * module. An independent module is a module which is not derived from or + * based on this library. If you modify this library, you may extend this + * exception to your version of the library, but you are not obliged to + * do so. If you do not wish to do so, delete this exception statement + * from your version. + * + * Funchook is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with Funchook. If not, see . + */ +#ifndef FUNCHOOK_H +#define FUNCHOOK_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Only functions with FUNCHOOK_EXPORT are visible from outside of funchook.dll + * or libfunchook.so. Others are invisible. + */ +#ifdef FUNCHOOK_EXPORTS +#if defined(_WIN32) +#define FUNCHOOK_EXPORT __declspec(dllexport) +#elif defined(__GNUC__) +#define FUNCHOOK_EXPORT __attribute__((visibility("default"))) +#endif +#endif /* FUNCHOOK_EXPORTS */ +#ifndef FUNCHOOK_EXPORT +#define FUNCHOOK_EXPORT +#endif + +typedef struct funchook funchook_t; + +#define FUNCHOOK_ERROR_INTERNAL_ERROR -1 +#define FUNCHOOK_ERROR_SUCCESS 0 +#define FUNCHOOK_ERROR_OUT_OF_MEMORY 1 +#define FUNCHOOK_ERROR_ALREADY_INSTALLED 2 +#define FUNCHOOK_ERROR_DISASSEMBLY 3 +#define FUNCHOOK_ERROR_IP_RELATIVE_OFFSET 4 +#define FUNCHOOK_ERROR_CANNOT_FIX_IP_RELATIVE 5 +#define FUNCHOOK_ERROR_FOUND_BACK_JUMP 6 +#define FUNCHOOK_ERROR_TOO_SHORT_INSTRUCTIONS 7 +#define FUNCHOOK_ERROR_MEMORY_ALLOCATION 8 /* memory allocation error */ +#define FUNCHOOK_ERROR_MEMORY_FUNCTION 9 /* other memory function errors */ +#define FUNCHOOK_ERROR_NOT_INSTALLED 10 +#define FUNCHOOK_ERROR_NO_AVAILABLE_REGISTERS 11 +#define FUNCHOOK_ERROR_NO_SPACE_NEAR_TARGET_ADDR 12 + +#define FUNCHOOK_FLAG_THISCALL (1u << 0) +#define FUNCHOOK_FLAG_FASTCALL (1u << 1) + +typedef struct funchook_arg_handle funchook_arg_handle_t; + +typedef struct funchook_info { + void *original_target_func; + void *target_func; + void *trampoline_func; + void *hook_func; + void *user_data; + funchook_arg_handle_t *arg_handle; +} funchook_info_t; + +typedef void (*funchook_hook_t)(funchook_info_t *fi); + +typedef struct { + void *hook_func; + funchook_hook_t prehook; + void *user_data; + unsigned int flags; +} funchook_params_t; + +/** + * Create a funchook handle + * + * @return allocated funchook handle. NULL when out-of-memory. + */ +FUNCHOOK_EXPORT funchook_t *funchook_create(void); + +/** + * Prepare hooking + * + * @param funchook a funchook handle created by funchook_create() + * @param target_func function pointer to be intercepted. The pointer to trampoline function is set on success. + * @param hook_func function pointer which is called istead of target_func + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_prepare(funchook_t *funchook, void **target_func, void *hook_func); + +FUNCHOOK_EXPORT int funchook_prepare_with_params(funchook_t *funchook, + void **target_func, const funchook_params_t *params); + +/** + * Install hooks prepared by funchook_prepare(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_install(funchook_t *funchook, int flags); + +/** + * Uninstall hooks installed by funchook_install(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_uninstall(funchook_t *funchook, int flags); + +/** + * Destroy a funchook handle + * + * @param funchook a funchook handle created by funchook_create() + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_destroy(funchook_t *funchook); + +/** + * Get error message + * + * @param funchook a funchook handle created by funchook_create() + * @return pointer to buffer containing error message + */ +FUNCHOOK_EXPORT const char *funchook_error_message(const funchook_t *funchook); + +/** + * Set log file name to debug funchook itself. + * + * @param name log file name + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_set_debug_file(const char *name); + +/* This function is under developemnt. It will be used by C++ template functions later. */ +FUNCHOOK_EXPORT void *funchook_arg_get_int_reg_addr(const funchook_arg_handle_t *arg_handle, int pos); + +/* This function is under developemnt. It will be used by C++ template functions later. */ +FUNCHOOK_EXPORT void *funchook_arg_get_flt_reg_addr(const funchook_arg_handle_t *arg_handle, int pos); + +/* This function is under developemnt. It will be used by C++ template functions later. */ +FUNCHOOK_EXPORT void *funchook_arg_get_stack_addr(const funchook_arg_handle_t *arg_handle, int pos); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/vendor/funchook/lib/distorm.lib b/vendor/funchook/lib/distorm.lib new file mode 100644 index 0000000..746006f Binary files /dev/null and b/vendor/funchook/lib/distorm.lib differ diff --git a/vendor/funchook/lib/funchook.lib b/vendor/funchook/lib/funchook.lib new file mode 100644 index 0000000..7ce8959 Binary files /dev/null and b/vendor/funchook/lib/funchook.lib differ diff --git a/vendor/funchook/lib/libdistorm.a b/vendor/funchook/lib/libdistorm.a new file mode 100644 index 0000000..7094a7b Binary files /dev/null and b/vendor/funchook/lib/libdistorm.a differ diff --git a/vendor/funchook/lib/libfunchook.a b/vendor/funchook/lib/libfunchook.a new file mode 100644 index 0000000..1b25984 Binary files /dev/null and b/vendor/funchook/lib/libfunchook.a differ