diff --git a/.gitignore b/.gitignore index 259148f..9491a2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,363 @@ -# Prerequisites -*.d +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore -# Compiled Object files -*.slo -*.lo -*.o +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta *.obj - -# Precompiled Headers -*.gch +*.iobj *.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc -# Compiled Dynamic libraries -*.so -*.dylib -*.dll +# Chutzpah Test files +_Chutzpah* -# Fortran module files -*.mod -*.smod +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb -# Compiled Static libraries -*.lai -*.la -*.a -*.lib +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap -# Executables -*.exe -*.out -*.app +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/l4d2-hikari.sln b/l4d2-hikari.sln new file mode 100644 index 0000000..8f69219 --- /dev/null +++ b/l4d2-hikari.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32922.545 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "l4d2", "l4d2\l4d2.vcxproj", "{FC0A0AE9-D014-4E51-AF01-35C39E5F272E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FC0A0AE9-D014-4E51-AF01-35C39E5F272E}.Debug|x64.ActiveCfg = Debug|x64 + {FC0A0AE9-D014-4E51-AF01-35C39E5F272E}.Debug|x64.Build.0 = Debug|x64 + {FC0A0AE9-D014-4E51-AF01-35C39E5F272E}.Debug|x86.ActiveCfg = Debug|Win32 + {FC0A0AE9-D014-4E51-AF01-35C39E5F272E}.Debug|x86.Build.0 = Debug|Win32 + {FC0A0AE9-D014-4E51-AF01-35C39E5F272E}.Release|x64.ActiveCfg = Release|x64 + {FC0A0AE9-D014-4E51-AF01-35C39E5F272E}.Release|x64.Build.0 = Release|x64 + {FC0A0AE9-D014-4E51-AF01-35C39E5F272E}.Release|x86.ActiveCfg = Release|Win32 + {FC0A0AE9-D014-4E51-AF01-35C39E5F272E}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3908AAFA-EBEE-4D3E-A4A7-74EE6CC71412} + EndGlobalSection +EndGlobal diff --git a/l4d2/dllmain.cpp b/l4d2/dllmain.cpp new file mode 100644 index 0000000..efbd32b --- /dev/null +++ b/l4d2/dllmain.cpp @@ -0,0 +1,67 @@ +// dllmain.cpp : Defines the entry point for the DLL application. + +#include +#include "framework.h" +#include "game/game.h" + +DWORD WINAPI Setup( LPVOID parameter ) +{ + try + { + Hikari::Logger = std::make_unique(); + Hikari::Memory = std::make_unique(); + + { + Game::Modules::Client = std::make_unique( "client.dll" ); + Game::Modules::Engine = std::make_unique( "engine.dll" ); + Game::Modules::MaterialSystem = std::make_unique( "materialsystem.dll" ); + Game::Modules::StudioRender = std::make_unique( "studiorender.dll" ); + Game::Modules::VStdLib = std::make_unique( "vstdlib.dll" ); + Game::Modules::MatSurface = std::make_unique( "vguimatsurface.dll" ); + Game::Modules::Server = std::make_unique( "server.dll" ); + Game::Modules::VGui = std::make_unique( "vgui2.dll" ); + Game::Modules::shaderapidx9 = std::make_unique( "shaderapidx9.dll" ); + } + + Game::Addresses::Setup(); + Game::Interfaces = std::make_unique(); + + Game::Hooks = std::make_unique(); + } + catch ( std::exception& exp ) + { + Hikari::Logger->Error( std::format( ( "{}" ), exp.what() ) ); + _RPT0( _CRT_ERROR, exp.what() ); + Hikari::Logger->Destory(); + + FreeLibraryAndExitThread( static_cast( parameter ), EXIT_SUCCESS ); + } + return TRUE; +} + +DWORD WINAPI Destroy( LPVOID parameter ) +{ + while ( !GetAsyncKeyState( VK_F10 ) ) + std::this_thread::sleep_for( std::chrono::milliseconds( 500 ) ); + + Hikari::Logger->Destory(); + FreeLibraryAndExitThread( static_cast( parameter ), EXIT_SUCCESS ); +} + +BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) +{ + if ( ul_reason_for_call == DLL_PROCESS_ATTACH ) + { + DisableThreadLibraryCalls( hModule ); + + if ( const auto thread = CreateThread( nullptr, 0, Setup, hModule, 0UL, nullptr ); thread != nullptr ) + CloseHandle( thread ); + + if ( const auto thread = CreateThread( nullptr, 0, Destroy, hModule, 0UL, nullptr ); thread != nullptr ) + CloseHandle( thread ); + + return TRUE; + } + + return TRUE; +} diff --git a/l4d2/framework.h b/l4d2/framework.h new file mode 100644 index 0000000..c29b4c8 --- /dev/null +++ b/l4d2/framework.h @@ -0,0 +1,13 @@ +#pragma once +#define DUMP_SE +#define DUMP_NETVARS + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include + +#include "utilities/lib.h" + diff --git a/l4d2/game/game.h b/l4d2/game/game.h new file mode 100644 index 0000000..7a5eac5 --- /dev/null +++ b/l4d2/game/game.h @@ -0,0 +1,13 @@ +#pragma once + +#include "modules.h" +#include "misc/addresses.h" +#include "source_engine/source_engine.h" +#include "misc/math.h" +#include "misc/render.h" +#include "hooks/hooks.h" + +namespace Game +{ + inline CTerrorPlayer_t* _local{}; +} diff --git a/l4d2/game/hooks/hooks.cpp b/l4d2/game/hooks/hooks.cpp new file mode 100644 index 0000000..2931848 --- /dev/null +++ b/l4d2/game/hooks/hooks.cpp @@ -0,0 +1,144 @@ +#include "../../framework.h" +#include "../game.h" +#include "hooks.h" +#include +#include + + +#include "../../thirdparty/minhook/MinHook.h" + + +namespace Game +{ + Hook_t::Detour_t::Detour_t() : _source( nullptr ), _target( nullptr ), _hooked( false ) + { + } + + Hook_t::Detour_t::~Detour_t() + { + Unhook(); + } + + void Hook_t::Detour_t::Hook( void* source, void* target, void** original, const std::string& name ) + { + if ( this->_hooked ) + return; + + this->_name = name; + if ( !source ) + throw std::runtime_error( std::format( "Invalid source function for {}", name ) ); + + if ( !target ) + throw std::runtime_error( std::format( "Invalid target function for {}", name ) ); + + this->_source = source; + this->_target = target; + + if ( const auto status = MH_CreateHook( this->_source, this->_target, original ); status != MH_OK ) + throw std::runtime_error( std::format( "Failed to create hook for {}. r:{}", name, MH_StatusToString( status ) ) ); + + if ( MH_EnableHook( this->_source ) != MH_OK ) + throw std::runtime_error( std::format( "Failed to enable hook for {}", name ) ); + + this->_hooked = true; + Hikari::Logger->Success( std::format( "Hooked {}. {:#09X} --> {:#09X}", name, reinterpret_cast( this->_source ), reinterpret_cast( this->_target ) ) ); + } + + WNDPROC _WndProc; + + void Hook_t::Detour_t::Unhook() + { + if ( !this->_hooked ) + return; + + if ( MH_RemoveHook( this->_source ) != MH_OK ) + throw std::runtime_error( std::format( "Failed to remove hook for {}", this->_name ) ); + + this->_hooked = false; + + Hikari::Logger->Success( std::format( "Unhooked {}. {:#09X} --> {:#09X}", this->_name, reinterpret_cast( this->_target ), reinterpret_cast( this->_source ) ) ); + } + + Hook_t::Hook_t() + { + if ( MH_Initialize() != MH_OK ) + throw std::runtime_error( "Failed to initialize hook" ); + + Render::InitFont(); + + _Paint.Hook( Addresses::Paint.Cast(), Paint::Hook, reinterpret_cast( &Paint::Original ), "CEngineVGui::Paint" ); + + _CHLCreateMove.Hook( reinterpret_cast( Hikari::Memory->VirtualFunction<20>( Interfaces->Client ) ), CHLCreateMove::Hook, reinterpret_cast( &CHLCreateMove::Original ), "CHLClient::CreateMove" ); + _LevelInitPostEntity.Hook( reinterpret_cast( Hikari::Memory->VirtualFunction<5>( Interfaces->Client ) ), LevelInitPostEntity::Hook, reinterpret_cast( &LevelInitPostEntity::Original ), "CHLClient::LevelInitPostEntity" ); + _LevelShutdown.Hook( reinterpret_cast( Hikari::Memory->VirtualFunction<6>( Interfaces->Client ) ), LevelShutdown::Hook, reinterpret_cast( &LevelShutdown::Original ), "CHLClient::LevelShutdown" ); + } + + Hook_t::~Hook_t() + { + + } + + void __fastcall CHLCreateMove::Hook(void* _ecx, void* _edx, int sequence_number, float input_sample_frametime, bool active) + { + CHLCreateMove::Original(Interfaces->Client, nullptr, sequence_number, input_sample_frametime, active); + + DWORD pep; + __asm mov pep, ebp; + auto* cmd = &Interfaces->CInput->m_pCommands[sequence_number % MULTIPLAYER_BACKUP]; + auto* VerifiedCmd = &Interfaces->CInput->m_pVerifiedCommands[sequence_number % MULTIPLAYER_BACKUP]; + bool* BSendPacket = reinterpret_cast(*reinterpret_cast(pep) - 0x1D); + + if (!cmd || !VerifiedCmd || !active) + return; + + _local = Interfaces->EntityList->Get( Interfaces->Engine->GetLocalPlayer() ); + + if ( _local && _local->IsAlive() ) + { + const auto oldAngles = cmd->viewangles; + //do ur epic stuff here. + if (cmd->buttons.HasFlag(IN_JUMP) && !_local->m_fFlags().HasFlag((1 << 0))) + cmd->buttons.RemoveFlag(IN_JUMP); + + + Math::CorrectMovement( oldAngles, cmd ); + } + + VerifiedCmd->m_cmd = *cmd; + VerifiedCmd->m_crc = cmd->GetCheckSum(); + } + + void __fastcall Paint::Hook( void* ecx, void* edx, unsigned mode ) + { + Original( ecx, edx, mode ); + + static auto StartDrawing = Addresses::StartDrawing.Cast(); + static auto FinishDrawing = Addresses::FinishDrawing.Cast(); + + if ( mode & 1 ) + { + + StartDrawing( Interfaces->Surface ); + + + static int w, h; + Interfaces->Engine->GetScreenSize( w, h ); + Interfaces->Surface->DrawColoredTexted( Fonts::WaterMark, w * 0.17, 20, 255, 192, 203, 255, "Left 4 Dead 2 Internal Base By Rico1662" ); + + + FinishDrawing( Interfaces->Surface ); + } + } + + void __fastcall LevelInitPostEntity::Hook( void* ecx, void* edx ) + { + + Original( ecx, edx ); + } + + void __fastcall LevelShutdown::Hook( void* ecx, void* edx ) + { + Original( ecx, edx ); + _local = nullptr; + } +} diff --git a/l4d2/game/hooks/hooks.h b/l4d2/game/hooks/hooks.h new file mode 100644 index 0000000..a7dedfd --- /dev/null +++ b/l4d2/game/hooks/hooks.h @@ -0,0 +1,50 @@ +#pragma once + +namespace Game +{ + #define CREATE_HOOK_DEFINE(name, type) \ + struct name { \ + using fn = std::remove_pointer_t; \ + static fn Hook; \ + static fn* Original; \ + }; \ + inline name::fn* name::Original; + + CREATE_HOOK_DEFINE( PaintTraverse, void(__fastcall*)(void*, void*, unsigned int, bool, bool) ); + CREATE_HOOK_DEFINE( Paint, void(__fastcall*)(void*, void*, unsigned int) ); + CREATE_HOOK_DEFINE( CHLCreateMove, void(__fastcall*)(void*, void*, int, float, bool) ); + CREATE_HOOK_DEFINE( LevelInitPostEntity, void(__fastcall*)(void*, void*) ); + CREATE_HOOK_DEFINE( LevelShutdown, void(__fastcall*)(void*, void*) ); + + struct Hook_t + { + private: + struct Detour_t + { + Detour_t(); + ~Detour_t(); + + void Hook( void* source, void* target, void** original, const std::string& name ); + void Unhook(); + + private: + void* _source; + void* _target; + std::string _name; + bool _hooked; + }; + + Detour_t _PaintTraverse; + Detour_t _Paint; + Detour_t _FrameStageNotify; + Detour_t _CHLCreateMove; + Detour_t _LevelInitPostEntity; + Detour_t _LevelShutdown; + + public: + Hook_t(); + ~Hook_t(); + }; + + inline std::unique_ptr Hooks; +} diff --git a/l4d2/game/misc/addresses.cpp b/l4d2/game/misc/addresses.cpp new file mode 100644 index 0000000..bc8836f --- /dev/null +++ b/l4d2/game/misc/addresses.cpp @@ -0,0 +1,11 @@ +#include "../../framework.h" +#include "../game.h" + +void Game::Addresses::Setup() +{ + Paint = Modules::Engine->FindPattern( "55 8B EC 83 EC 54 A1 ? ? ? ? 33 C5 89 45 FC A1" ); + + StartDrawing = Modules::MatSurface->FindPattern( "E8 ? ? ? ? 8B 4D 08 8B 06", true ); + FinishDrawing = Modules::MatSurface->FindPattern( "E8 ? ? ? ? 8D 65 E8", true ); + IsPlayerDead = Modules::Client->FindPattern("33 C0 80 B9"); +} diff --git a/l4d2/game/misc/addresses.h b/l4d2/game/misc/addresses.h new file mode 100644 index 0000000..661d5b3 --- /dev/null +++ b/l4d2/game/misc/addresses.h @@ -0,0 +1,12 @@ +#pragma once + +namespace Game::Addresses +{ + void Setup(); + + inline Hikari::Address_t Paint; + + inline Hikari::Address_t StartDrawing; + inline Hikari::Address_t FinishDrawing; + inline Hikari::Address_t IsPlayerDead; +} \ No newline at end of file diff --git a/l4d2/game/misc/math.cpp b/l4d2/game/misc/math.cpp new file mode 100644 index 0000000..95725b1 --- /dev/null +++ b/l4d2/game/misc/math.cpp @@ -0,0 +1,149 @@ +#include "../../framework.h" +#include "../game.h" + +#define M_PI 3.14159265358979323846 +#define DEG2RAD( x ) ( (float)(x) * (float)(M_PI_F / 180.f) ) + +namespace Game::Math +{ + Vector3D_t Transform( Vector3D_t& tr, const matrix3x4_t& matrix ) + { + return Vector3D_t( tr.DotProduct( matrix[ 0 ] ) + matrix[ 0 ][ 3 ], + tr.DotProduct( matrix[ 1 ] ) + matrix[ 1 ][ 3 ], + tr.DotProduct( matrix[ 2 ] ) + matrix[ 2 ][ 3 ] ); + } + + void Transform( const Vector3D_t& tr, matrix3x4_t& matrix, Vector3D_t& out ) + { + out.x = tr.DotProduct( matrix[ 0 ] ) + matrix[ 0 ][ 3 ]; + out.y = tr.DotProduct( matrix[ 1 ] ) + matrix[ 1 ][ 3 ]; + out.z = tr.DotProduct( matrix[ 2 ] ) + matrix[ 2 ][ 3 ]; + } + + void AngleVectors(const Angle_t& angles, Vector3D_t& out) + { + auto sine = Vector2D_t(), cosine = Vector2D_t(); + + SinCos( Deg2Rad(angles.x ), sine.x, cosine.x ); + SinCos( Deg2Rad(angles.y ), sine.y, cosine.y ); + + out.x = cosine.x * cosine.y; + out.y = cosine.x * sine.y; + out.z = -sine.x; + } + + void AngleVectors( const Angle_t& ang, Vector3D_t& forward, Vector3D_t& right, Vector3D_t& up ) + { + auto sine = Vector3D_t(), cosine = Vector3D_t(); + + SinCos( Deg2Rad( ang.x ), sine.x, cosine.x ); + SinCos( Deg2Rad( ang.y ), sine.y, cosine.y ); + SinCos( Deg2Rad( ang.z ), sine.z, cosine.z ); + + forward.x = cosine.x * cosine.y; + forward.y = cosine.x * sine.y; + forward.z = -sine.x; + + right.x = -1 * sine.z * sine.x * cosine.y + -1 * cosine.z * -sine.y; + right.y = -1 * sine.z * sine.x * sine.y + -1 * cosine.z * cosine.y; + right.z = -1 * sine.z * cosine.x; + + up.x = cosine.z * sine.x * cosine.y + -sine.z * -sine.y; + up.y = cosine.z * sine.x * sine.y + -sine.z * cosine.y; + up.z = cosine.z * cosine.x; + } + + void VectorAngles( const Vector3D_t& vec, Angle_t& out ) + { + if ( vec.x == 0.0f && vec.y == 0.0f ) + { + out.x = ( vec.z > 0.0f ) ? 270.0f : 90.0f; + out.y = 0.0f; + } else + { + out.x = std::atan2( -vec.z, vec.Length2D() ) * ( -180.f / static_cast( std::numbers::pi ) ); + out.y = std::atan2( vec.y, vec.x ) * ( 180.f / static_cast( std::numbers::pi ) ); + + if ( out.y > 90.f ) + out.y -= 180.f; + else if ( out.y < 90.f ) + out.y += 180.f; + else if ( out.y == 90.f ) + out.y = 0.f; + } + + out.z = 0.f; + } + + + void VectorAngles( const Vector3D_t& vec, const Vector3D_t& up, Angle_t& out ) + { + const Vector3D_t left = up.CrossProduct( vec ); + + if ( const auto dist = vec.Length2D(); dist > 0.001f ) + { + out.y = Rad2Deg( std::atan2( vec.y, vec.x ) ); + out.x = Rad2Deg( std::atan2( -vec.z, dist ) ); + out.z = Rad2Deg( std::atan2( left.z, left.y * vec.x - left.x * vec.y ) ); + } else + { + out.y = Rad2Deg( std::atan2( -left.x, left.y ) ); + out.x = Rad2Deg( std::atan2( -vec.z, dist ) ); + out.z = 0.f; + } + } + + void CorrectMovement( const Angle_t& wish_angle, CUserCmd* cmd ) + { + Vector3D_t view_fwd, view_right, view_up, cmd_fwd, cmd_right, cmd_up; + auto viewangles = cmd->viewangles; + viewangles.Normalize(); + + AngleVectors( wish_angle, view_fwd, view_right, view_up ); + AngleVectors( viewangles, cmd_fwd, cmd_right, cmd_up ); + + const auto v8 = sqrtf( ( view_fwd.x * view_fwd.x ) + ( view_fwd.y * view_fwd.y ) ); + const auto v10 = sqrtf( ( view_right.x * view_right.x ) + ( view_right.y * view_right.y ) ); + const auto v12 = sqrtf( view_up.z * view_up.z ); + + const Vector3D_t norm_view_fwd( ( 1.f / v8 ) * view_fwd.x, ( 1.f / v8 ) * view_fwd.y, 0.f ); + const Vector3D_t norm_view_right( ( 1.f / v10 ) * view_right.x, ( 1.f / v10 ) * view_right.y, 0.f ); + const Vector3D_t norm_view_up( 0.f, 0.f, ( 1.f / v12 ) * view_up.z ); + + const auto v14 = sqrtf( ( cmd_fwd.x * cmd_fwd.x ) + ( cmd_fwd.y * cmd_fwd.y ) ); + const auto v16 = sqrtf( ( cmd_right.x * cmd_right.x ) + ( cmd_right.y * cmd_right.y ) ); + const auto v18 = sqrtf( cmd_up.z * cmd_up.z ); + + const Vector3D_t norm_cmd_fwd( ( 1.f / v14 ) * cmd_fwd.x, ( 1.f / v14 ) * cmd_fwd.y, 0.f ); + const Vector3D_t norm_cmd_right( ( 1.f / v16 ) * cmd_right.x, ( 1.f / v16 ) * cmd_right.y, 0.f ); + const Vector3D_t norm_cmd_up( 0.f, 0.f, ( 1.f / v18 ) * cmd_up.z ); + + const auto v22 = norm_view_fwd.x * cmd->forwardmove; + const auto v26 = norm_view_fwd.y * cmd->forwardmove; + const auto v28 = norm_view_fwd.z * cmd->forwardmove; + const auto v24 = norm_view_right.x * cmd->sidemove; + const auto v23 = norm_view_right.y * cmd->sidemove; + const auto v25 = norm_view_right.z * cmd->sidemove; + const auto v30 = norm_view_up.x * cmd->upmove; + const auto v27 = norm_view_up.z * cmd->upmove; + const auto v29 = norm_view_up.y * cmd->upmove; + + cmd->forwardmove = ( ( ( ( norm_cmd_fwd.x * v24 ) + ( norm_cmd_fwd.y * v23 ) ) + ( norm_cmd_fwd.z * v25 ) ) + ( ( ( norm_cmd_fwd.x * v22 ) + ( norm_cmd_fwd.y * v26 ) ) + ( norm_cmd_fwd.z * v28 ) ) ) + ( ( ( norm_cmd_fwd.y * v30 ) + ( norm_cmd_fwd.x * v29 ) ) + ( norm_cmd_fwd.z * v27 ) ); + cmd->sidemove = ( ( ( ( norm_cmd_right.x * v24 ) + ( norm_cmd_right.y * v23 ) ) + ( norm_cmd_right.z * v25 ) ) + ( ( ( norm_cmd_right.x * v22 ) + ( norm_cmd_right.y * v26 ) ) + ( norm_cmd_right.z * v28 ) ) ) + ( ( ( norm_cmd_right.x * v29 ) + ( norm_cmd_right.y * v30 ) ) + ( norm_cmd_right.z * v27 ) ); + cmd->upmove = ( ( ( ( norm_cmd_up.x * v23 ) + ( norm_cmd_up.y * v24 ) ) + ( norm_cmd_up.z * v25 ) ) + ( ( ( norm_cmd_up.x * v26 ) + ( norm_cmd_up.y * v22 ) ) + ( norm_cmd_up.z * v28 ) ) ) + ( ( ( norm_cmd_up.x * v30 ) + ( norm_cmd_up.y * v29 ) ) + ( norm_cmd_up.z * v27 ) ); + + cmd->forwardmove = std::clamp( cmd->forwardmove, -450.f, 450.f ); + cmd->sidemove = std::clamp( cmd->sidemove, -450.f, 450.f ); + cmd->upmove = std::clamp( cmd->upmove, -320.f, 320.f ); + } + + float NormalizeYaw( float yaw ) + { + if (yaw > 180.0f) + yaw -= 360.0f; + else if (yaw < -180.0f) + yaw += 360.0f; + + return yaw; + } +} diff --git a/l4d2/game/misc/math.h b/l4d2/game/misc/math.h new file mode 100644 index 0000000..a08ed6c --- /dev/null +++ b/l4d2/game/misc/math.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace Game::Math +{ + Vector3D_t Transform( Vector3D_t& tr, const matrix3x4_t& matrix ); + void Transform( const Vector3D_t& tr, matrix3x4_t& matrix, Vector3D_t& out ); + void AngleVectors( const Angle_t& angles, Vector3D_t& out ); + void AngleVectors( const Angle_t& angles, Vector3D_t& forward, Vector3D_t& right, Vector3D_t& up ); + void VectorAngles( const Vector3D_t& vec, Angle_t& out ); + void VectorAngles( const Vector3D_t& vec, const Vector3D_t& up, Angle_t& out ); + void CorrectMovement( const Angle_t& wish_angle, CUserCmd* cmd ); + float NormalizeYaw( float yaw ); + + inline float Rad2Deg( const float rad ) + { + return static_cast( rad * ( 180.f / std::numbers::pi ) ); + } + + inline float Deg2Rad( const float deg ) + { + return static_cast( deg * ( std::numbers::pi / 180.f ) ); + } + + inline void SinCos( const float rad, float& sine, float& cosine ) + { + sine = std::sin( rad ); + cosine = std::cos( rad ); + } +} diff --git a/l4d2/game/misc/render.cpp b/l4d2/game/misc/render.cpp new file mode 100644 index 0000000..f2900e5 --- /dev/null +++ b/l4d2/game/misc/render.cpp @@ -0,0 +1,75 @@ +#include "../../framework.h" +#include "../game.h" + +namespace Game::Render +{ + void InitFont() + { + Interfaces->Surface->SetFontGlyphSet( Fonts::ESP = Interfaces->Surface->CreatFont(), "Tahoma", 12, 700, 0, 0, FONTFLAG_DROPSHADOW, 0, 0 ); + Interfaces->Surface->SetFontGlyphSet( Fonts::WaterMark = Interfaces->Surface->CreatFont(), "Tahoma", 13, 700, 0, 0, FONTFLAG_DROPSHADOW, 0, 0 ); + } + + void DrawFilledRect( const int x, const int y, const int w, const int h, Color col ) + { + Interfaces->Surface->DrawSetColor( col.r(), col.g(), col.b(), col.a() ); + Interfaces->Surface->DrawFilledRect( x, y, x + w, y + h ); + } + + void DrawOutlinedRect( const int x, const int y, const int w, const int h, Color col ) + { + Interfaces->Surface->DrawSetColor( col.r(), col.g(), col.b(), col.a() ); + Interfaces->Surface->DrawOutlinedRect( x, y, x + w, y + h ); + } + + void DrawLine( const int x, const int y, const int w, const int h, Color col ) + { + Interfaces->Surface->DrawSetColor( col.r(), col.g(), col.b(), col.a() ); + Interfaces->Surface->DrawLine( x, y, w, h ); + } + + void DrawOutlinedBox( int x, int y, int w, int h, Color col, Color col2 ) + { + DrawOutlinedRect( x - 1, y - 1, w + 2, h + 2, col2 ); + DrawOutlinedRect( x, y, w, h, col ); + DrawOutlinedRect( x + 1, y + 1, w - 2, h - 2, col2 ); + } + + Vector2D_t GetTextSize( const DWORD& font, const std::string& text ) + { + const auto buf = Hikari::String::ToUnicode( text ); + if ( buf.empty() ) + return {}; + + int width, height; + Interfaces->Surface->GetTextSize( font, buf.data(), width, height ); + + return { static_cast( width ), static_cast( height ) }; + } + + void DrawText_( const HFont font, const std::string& text, const int x, const int y, const bool centered, Color col ) + { + int _x = x; + const int _y = y; + + const auto buf = Hikari::String::ToUnicode( text ); + if ( buf.empty() ) + return; + + int width, height; + Interfaces->Surface->GetTextSize( font, buf.data(), width, height ); + + if ( centered ) + _x -= width / 2; + + Interfaces->Surface->DrawSetTextFont( font ); + Interfaces->Surface->DrawSetTextColor( col.r(), col.g(), col.b(), col.a() ); + Interfaces->Surface->DrawSetTextPos( _x, _y ); + Interfaces->Surface->DrawPrintText( buf.c_str(), buf.length() ); + } +} + +namespace Game::Fonts +{ + HFont ESP = NULL; + HFont WaterMark = NULL; +} diff --git a/l4d2/game/misc/render.h b/l4d2/game/misc/render.h new file mode 100644 index 0000000..ea99f91 --- /dev/null +++ b/l4d2/game/misc/render.h @@ -0,0 +1,24 @@ +#pragma once + +namespace Game::Render +{ + void InitFont(); + + void DrawFilledRect( int x, int y, int w, int h, Color col ); + + void DrawOutlinedRect( int x, int y, int w, int h, Color col ); + + void DrawLine( int x, int y, int w, int h, Color col ); + + void DrawOutlinedBox( int x, int y, int w, int h, Color col, Color col2 = Color( 10, 10, 10, 190 ) ); + + Vector2D_t GetTextSize( const DWORD& font, const std::string& text ); + + void DrawText_( HFont font, const std::string& text, int x, int y, bool centered, Color col ); +} + +namespace Game::Fonts +{ + extern HFont ESP; + extern HFont WaterMark; +} diff --git a/l4d2/game/modules.cpp b/l4d2/game/modules.cpp new file mode 100644 index 0000000..f1f90f1 --- /dev/null +++ b/l4d2/game/modules.cpp @@ -0,0 +1,32 @@ +#include "../framework.h" +#include "modules.h" + +namespace Game +{ + Modules_t::Modules_t( HMODULE handle ) + { + this->_info = Hikari::ModuleInfo_t( handle ); + } + + Modules_t::Modules_t( const std::string& name ) + { + this->_info = Hikari::ModuleInfo_t( name ); + + + if (const auto relative_address = reinterpret_cast(_info.GetProc("CreateInterface")) + 0x5; relative_address > 0x5) + { + const auto interface_fn = relative_address + 4U + *reinterpret_cast(relative_address); + this->_interface_list = **reinterpret_cast(interface_fn + 0x6); + } + } + + Hikari::Address_t Modules_t::FindPattern( const char* pattern, bool relative ) const + { + return Hikari::Memory->FindPattern( this->_info, pattern, relative ); + } + + Hikari::Address_t Modules_t::GetVTablePointer( const std::string& vtable ) const + { + return Hikari::Memory->GetVTablePointer( this->_info, vtable ); + } +} diff --git a/l4d2/game/modules.h b/l4d2/game/modules.h new file mode 100644 index 0000000..85c63b5 --- /dev/null +++ b/l4d2/game/modules.h @@ -0,0 +1,43 @@ +#pragma once + +namespace Game +{ + struct Modules_t + { + explicit Modules_t(HMODULE handle); + explicit Modules_t(const std::string& name); + + private: + Hikari::ModuleInfo_t _info{}; + void* _interface_list{}; + + public: + Hikari::Address_t FindPattern(const char* pattern, bool relative = false) const; + Hikari::Address_t GetVTablePointer(const std::string& vtable) const; + + void* GetInterfaceList() const + { + return this->_interface_list; + } + + Hikari::ModuleInfo_t Info() + { + return this->_info; + } + }; + + namespace Modules + { + inline std::unique_ptr Client; + inline std::unique_ptr Engine; + inline std::unique_ptr Direct9; + inline std::unique_ptr MaterialSystem; + inline std::unique_ptr StudioRender; + inline std::unique_ptr VStdLib; + inline std::unique_ptr MatSurface; + inline std::unique_ptr VGui; + inline std::unique_ptr Server; + inline std::unique_ptr shaderapidx9; + + } +} \ No newline at end of file diff --git a/l4d2/game/source_engine/classes/bitflags.h b/l4d2/game/source_engine/classes/bitflags.h new file mode 100644 index 0000000..d179406 --- /dev/null +++ b/l4d2/game/source_engine/classes/bitflags.h @@ -0,0 +1,62 @@ +#pragma once + +struct bitflag_t +{ + /// + /// Creates a default bitflag object + /// + bitflag_t() = default; + + /// + /// Creates a bitflag object with the given flags + /// + bitflag_t(uintptr_t flags) : m_flags(flags) + {} + + /// + /// Destroys the bitflag object + /// + ~bitflag_t() = default; + + /// + /// Stored flags + /// + uintptr_t m_flags{}; + + /// + /// Checks whether the given flag is present in the stored flags + /// + /// Flag that will be checked + /// Returns true if the flag is present in the stored flags + bool HasFlag(uintptr_t flag) + { + return m_flags & flag; + } + + /// + /// Adds the given flag to the stored flags + /// + /// Flag that will be added + void AddFlag(uintptr_t flag) + { + m_flags |= flag; + } + + /// + /// Removes the given flag to the stored flags + /// + /// Flag that will be removed + void RemoveFlag(uintptr_t flag) + { + m_flags &= ~flag; + } + + /// + /// Checks if there are no flags stored + /// + /// True if no flags stored + bool IsEmpty() + { + return !m_flags; + } +}; \ No newline at end of file diff --git a/l4d2/game/source_engine/classes/color.h b/l4d2/game/source_engine/classes/color.h new file mode 100644 index 0000000..a4431c9 --- /dev/null +++ b/l4d2/game/source_engine/classes/color.h @@ -0,0 +1,322 @@ +#pragma once + +#pragma warning(push) +#pragma warning(disable:4333 4244) + +#define DEFCOLOR_SRC(name, r, g, b) static Color name##(uint8_t a = 255){ \ + return Color(r, g, b, a); \ + } + +class Color +{ +public: + // constructors + Color() + { + *reinterpret_cast( this ) = 0; + } + + Color( uint8_t r, uint8_t g, uint8_t b ) + { + SetColor( r, g, b, 255 ); + } + + Color( uint8_t r, uint8_t g, uint8_t b, uint8_t a ) + { + SetColor( r, g, b, a ); + } + + void SetColor( uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0 ) + { + _color[ 0 ] = r; + _color[ 1 ] = g; + _color[ 2 ] = b; + _color[ 3 ] = a; + } + + void GetColor( uint8_t& r, uint8_t& g, uint8_t& b, uint8_t& a ) const + { + r = _color[ 0 ]; + g = _color[ 1 ]; + b = _color[ 2 ]; + a = _color[ 3 ]; + } + + void GetColor( int& _r, int& _g, int& _b, int& _a ) const + { + _r = _color[ 0 ]; + _g = _color[ 1 ]; + _b = _color[ 2 ]; + _a = _color[ 3 ]; + } + + static Color FromARGB( unsigned long color ) + { + int a = ( color & 0xFF000000 ) >> 24; + int r = ( color & 0x00FF0000 ) >> 16; + int g = ( color & 0x0000FF00 ) >> 8; + int b = ( color & 0x000000FF ); + + return Color( r, g, b, a ); + } + + void SetARGB( unsigned long color ) + { + int a = ( color & 0xFF000000 ) >> 24; + int r = ( color & 0x00FF0000 ) >> 16; + int g = ( color & 0x0000FF00 ) >> 8; + int b = ( color & 0x000000FF ); + + SetColor( r, g, b, a ); + } + + DWORD GetARGB() const + { + const int a = ( _color[ 3 ] ) >> 24; + const int r = ( _color[ 0 ] ) >> 16; + const int g = ( _color[ 1 ] ) >> 8; + const int b = ( _color[ 2 ] ); + + return a | r | g | b; + } + + static Color FromHSB( const float hue, const float saturation, const float brightness ) + { + const float h = hue == 1.0f ? 0 : hue * 6.0f; + const float f = h - static_cast( h ); + const float p = brightness * ( 1.0f - saturation ); + const float q = brightness * ( 1.0f - saturation * f ); + const float t = brightness * ( 1.0f - ( saturation * ( 1.0f - f ) ) ); + + if ( h < 1 ) + { + return Color( static_cast( brightness * 255 ), static_cast( t * 255 ), static_cast( p * 255 ) ); + } + + if ( h < 2 ) + { + return Color( static_cast( q * 255 ), static_cast( brightness * 255 ), static_cast( p * 255 ) ); + } + + if ( h < 3 ) + { + return Color( static_cast( p * 255 ), static_cast( brightness * 255 ), static_cast( t * 255 ) ); + } + + if ( h < 4 ) + { + return Color( static_cast( p * 255 ), static_cast( q * 255 ), static_cast( brightness * 255 ) ); + } + + if ( h < 5 ) + { + return Color( static_cast( t * 255 ), static_cast( p * 255 ), static_cast( brightness * 255 ) ); + } + + return Color( static_cast( brightness * 255 ), static_cast( p * 255 ), static_cast( q * 255 ) ); + } + + uint8_t& operator[]( int index ) + { + return _color[ index ]; + } + + const uint8_t& operator[]( const int& index ) const + { + return _color[ index ]; + } + + bool operator ==( const Color& rhs ) const + { + return ( *( ( int* )this ) == *( ( int* )&rhs ) ); + } + + bool operator !=( const Color& rhs ) const + { + return !( operator==( rhs ) ); + } + + int r() const + { + return _color[ 0 ]; + } + + int g() const + { + return _color[ 1 ]; + } + + int b() const + { + return _color[ 2 ]; + } + + int a() const + { + return _color[ 3 ]; + } + + void SetR( const uint8_t& _i ) + { + _color[ 0 ] = _i; + } + + void SetG( const uint8_t& _i ) + { + _color[ 1 ] = _i; + } + + void SetB( const uint8_t& _i ) + { + _color[ 2 ] = _i; + } + + void SetA( const uint8_t& _i ) + { + _color[ 3 ] = _i; + } + + Color& operator=( const Color& rhs ) + { + *reinterpret_cast( &_color[ 0 ] ) = *( int* )&rhs._color[ 0 ]; + return *this; + } + + Color operator+( const Color& rhs ) const + { + int red = _color[ 0 ] + rhs._color[ 0 ]; + int green = _color[ 1 ] + rhs._color[ 1 ]; + int blue = _color[ 2 ] + rhs._color[ 2 ]; + int alpha = _color[ 3 ] + rhs._color[ 3 ]; + + red = red > 255 ? 255 : red; + green = green > 255 ? 255 : green; + blue = blue > 255 ? 255 : blue; + alpha = alpha > 255 ? 255 : alpha; + + return Color( red, green, blue, alpha ); + } + + Color operator-( const Color& rhs ) const + { + int red = _color[ 0 ] - rhs._color[ 0 ]; + int green = _color[ 1 ] - rhs._color[ 1 ]; + int blue = _color[ 2 ] - rhs._color[ 2 ]; + int alpha = _color[ 3 ] - rhs._color[ 3 ]; + + red = red < 0 ? 0 : red; + green = green < 0 ? 0 : green; + blue = blue < 0 ? 0 : blue; + alpha = alpha < 0 ? 0 : alpha; + return Color( red, green, blue, alpha ); + } + + float* Base() const + { + float clr[ 3 ]; + + clr[ 0 ] = _color[ 0 ] / 255.0f; + clr[ 1 ] = _color[ 1 ] / 255.0f; + clr[ 2 ] = _color[ 2 ] / 255.0f; + + return &clr[ 0 ]; + } + + float rBase() const + { + return _color[ 0 ] / 255.0f; + } + + float gBase() const + { + return _color[ 1 ] / 255.0f; + } + + float bBase() const + { + return _color[ 2 ] / 255.0f; + } + + float aBase() const + { + return _color[ 3 ] / 255.0f; + } + + operator const uint8_t*() const + { + return &_color[ 0 ]; + } + + DEFCOLOR_SRC( Black, 0, 0, 0 ); + DEFCOLOR_SRC( White, 255, 255, 255 ); + DEFCOLOR_SRC( Red, 255, 0, 0 ); + DEFCOLOR_SRC( Green, 0, 128, 0 ); + DEFCOLOR_SRC( Blue, 0, 0, 255 ); + DEFCOLOR_SRC( Lime, 0, 255, 0 ); + DEFCOLOR_SRC( Yellow, 255, 255, 0 ); + DEFCOLOR_SRC( Cyan, 0, 255, 255 ); + DEFCOLOR_SRC( Magenta, 255, 0, 255 ); + DEFCOLOR_SRC( Silver, 192, 192, 192 ); + DEFCOLOR_SRC( Gray, 128, 128, 128 ); + DEFCOLOR_SRC( Maroon, 128, 0, 0 ); + DEFCOLOR_SRC( Olive, 128, 128, 0 ); + DEFCOLOR_SRC( Purple, 128, 0, 128 ); + DEFCOLOR_SRC( Teal, 0, 128, 128 ); + DEFCOLOR_SRC( Navy, 0, 0, 128 ); + DEFCOLOR_SRC( DarkRed, 139, 0, 0 ); + DEFCOLOR_SRC( Brown, 165, 42, 42 ); + DEFCOLOR_SRC( Firebrick, 178, 34, 34 ); + DEFCOLOR_SRC( Crimson, 220, 20, 60 ); + DEFCOLOR_SRC( IndianRed, 205, 92, 92 ); + DEFCOLOR_SRC( LightCoral, 240, 128, 128 ); + DEFCOLOR_SRC( DarkSalmon, 233, 150, 122 ); + DEFCOLOR_SRC( Salmon, 250, 128, 114 ); + DEFCOLOR_SRC( LightSalmon, 255, 160, 122 ); + DEFCOLOR_SRC( OrangeRed, 255, 69, 0 ); + DEFCOLOR_SRC( DarkOrange, 255, 140, 0 ); + DEFCOLOR_SRC( Orange, 255, 165, 0 ); + DEFCOLOR_SRC( Gold, 255, 215, 0 ); + DEFCOLOR_SRC( DarkGoldenRod, 184, 134, 11 ); + DEFCOLOR_SRC( GoldenRod, 218, 165, 32 ); + DEFCOLOR_SRC( YellowGreen, 154, 205, 50 ); + DEFCOLOR_SRC( DarkOliveGreen, 85, 107, 47 ); + DEFCOLOR_SRC( OliveDrab, 107, 142, 35 ); + DEFCOLOR_SRC( LawnGreen, 124, 252, 0 ); + DEFCOLOR_SRC( ChartReuse, 127, 255, 0 ); + DEFCOLOR_SRC( GreenYellow, 173, 255, 47 ); + DEFCOLOR_SRC( DarkGreen, 0, 100, 0 ); + DEFCOLOR_SRC( ForestGreen, 34, 139, 34 ); + DEFCOLOR_SRC( LimeGreen, 50, 205, 50 ); + DEFCOLOR_SRC( DarkCyan, 0, 139, 139 ); + DEFCOLOR_SRC( Aqua, 0, 255, 255 ); + DEFCOLOR_SRC( LightCyan, 224, 255, 255 ); + DEFCOLOR_SRC( DarkTurquoise, 0, 206, 209 ); + DEFCOLOR_SRC( Turquoise, 64, 224, 208 ); + DEFCOLOR_SRC( MediumTurquoise, 72, 209, 204 ); + DEFCOLOR_SRC( PaleTurquoise, 175, 238, 238 ); + DEFCOLOR_SRC( Aquamarine, 127, 255, 212 ); + DEFCOLOR_SRC( PowderBlue, 176, 224, 230 ); + DEFCOLOR_SRC( DodgerBlue, 30, 144, 255 ); + DEFCOLOR_SRC( Lightblue, 173, 216, 230 ); + DEFCOLOR_SRC( SkyBlue, 135, 206, 235 ); + DEFCOLOR_SRC( LightSkyBlue, 135, 206, 250 ); + DEFCOLOR_SRC( MidnightBlue, 25, 25, 112 ); + DEFCOLOR_SRC( DarkBlue, 0, 0, 139 ); + DEFCOLOR_SRC( MediumBlue, 0, 0, 205 ); + DEFCOLOR_SRC( RoyalBlue, 65, 105, 225 ); + DEFCOLOR_SRC( BlueViolet, 138, 43, 226 ); + DEFCOLOR_SRC( Indigo, 75, 0, 130 ); + DEFCOLOR_SRC( DarkSlateBlue, 72, 61, 139 ); + DEFCOLOR_SRC( SlateBlue, 106, 90, 205 ); + DEFCOLOR_SRC( MediumSlateBlue, 123, 104, 238 ); + DEFCOLOR_SRC( MediumPurple, 147, 112, 219 ); + DEFCOLOR_SRC( Darkmagenta, 139, 0, 139 ); + DEFCOLOR_SRC( Darkviolet, 148, 0, 211 ); + DEFCOLOR_SRC( DarkGray, 169, 169, 169 ); + DEFCOLOR_SRC( LightGray, 211, 211, 211 ); + DEFCOLOR_SRC( Gainsboro, 220, 220, 220 ); +private: + uint8_t _color[ 4 ]; +}; + +#pragma warning(pop) diff --git a/l4d2/game/source_engine/classes/utls.h b/l4d2/game/source_engine/classes/utls.h new file mode 100644 index 0000000..b9cd027 --- /dev/null +++ b/l4d2/game/source_engine/classes/utls.h @@ -0,0 +1,25 @@ +#pragma once + +template < typename T > +struct UtlMemory +{ + T& operator[]( int i ) const noexcept + { + return _memory[ i ]; + } + + T* _memory; + int _allocationCount; + int _growSize; +}; + +struct UtlString +{ + UtlMemory buffer; + int length; + + const char* data() const noexcept + { + return buffer._memory; + } +}; diff --git a/l4d2/game/source_engine/datatypes/angle.h b/l4d2/game/source_engine/datatypes/angle.h new file mode 100644 index 0000000..468f5c0 --- /dev/null +++ b/l4d2/game/source_engine/datatypes/angle.h @@ -0,0 +1,174 @@ +#pragma once + +#include +#include + +class Angle_t +{ +public: + float x, y, z; + + constexpr Angle_t( float x = 0.f, float y = 0.f, float z = 0.f ) : x( x ), y( y ), z( z ) + { + } + + constexpr Angle_t( const float* arrAngles ) : x( arrAngles[ 0 ] ), y( arrAngles[ 1 ] ), z( arrAngles[ 2 ] ) + { + } + + bool operator==( const Angle_t& angBase ) const + { + return this->IsEqual( angBase ); + } + + bool operator!=( const Angle_t& angBase ) const + { + return !this->IsEqual( angBase ); + } + + constexpr Angle_t& operator=( const Angle_t& angBase ) + { + this->x = angBase.x; + this->y = angBase.y; + this->z = angBase.z; + return *this; + } + + constexpr Angle_t& operator+=( const Angle_t& angBase ) + { + this->x += angBase.x; + this->y += angBase.y; + this->z += angBase.z; + return *this; + } + + constexpr Angle_t& operator-=( const Angle_t& angBase ) + { + this->x -= angBase.x; + this->y -= angBase.y; + this->z -= angBase.z; + return *this; + } + + constexpr Angle_t& operator*=( const Angle_t& angBase ) + { + this->x *= angBase.x; + this->y *= angBase.y; + this->z *= angBase.z; + return *this; + } + + constexpr Angle_t& operator/=( const Angle_t& angBase ) + { + this->x /= angBase.x; + this->y /= angBase.y; + this->z /= angBase.z; + return *this; + } + + constexpr Angle_t& operator+=( const float flAdd ) + { + this->x += flAdd; + this->y += flAdd; + this->z += flAdd; + return *this; + } + + constexpr Angle_t& operator-=( const float flSubtract ) + { + this->x -= flSubtract; + this->y -= flSubtract; + this->z -= flSubtract; + return *this; + } + + constexpr Angle_t& operator*=( const float flMultiply ) + { + this->x *= flMultiply; + this->y *= flMultiply; + this->z *= flMultiply; + return *this; + } + + constexpr Angle_t& operator/=( const float flDivide ) + { + this->x /= flDivide; + this->y /= flDivide; + this->z /= flDivide; + return *this; + } + + Angle_t operator+( const Angle_t& angAdd ) const + { + return Angle_t( this->x + angAdd.x, this->y + angAdd.y, this->z + angAdd.z ); + } + + Angle_t operator-( const Angle_t& angSubtract ) const + { + return Angle_t( this->x - angSubtract.x, this->y - angSubtract.y, this->z - angSubtract.z ); + } + + Angle_t operator*( const Angle_t& angMultiply ) const + { + return Angle_t( this->x * angMultiply.x, this->y * angMultiply.y, this->z * angMultiply.z ); + } + + Angle_t operator/( const Angle_t& angDivide ) const + { + return Angle_t( this->x / angDivide.x, this->y / angDivide.y, this->z / angDivide.z ); + } + + Angle_t operator+( const float flAdd ) const + { + return Angle_t( this->x + flAdd, this->y + flAdd, this->z + flAdd ); + } + + Angle_t operator-( const float flSubtract ) const + { + return Angle_t( this->x - flSubtract, this->y - flSubtract, this->z - flSubtract ); + } + + Angle_t operator*( const float flMultiply ) const + { + return Angle_t( this->x * flMultiply, this->y * flMultiply, this->z * flMultiply ); + } + + Angle_t operator/( const float flDivide ) const + { + return Angle_t( this->x / flDivide, this->y / flDivide, this->z / flDivide ); + } + + [[nodiscard]] bool IsEqual( const Angle_t& angEqual, const float flErrorMargin = std::numeric_limits::epsilon() ) const + { + return ( std::fabsf( this->x - angEqual.x ) < flErrorMargin && std::fabsf( this->y - angEqual.y ) < flErrorMargin && std::fabsf( this->z - angEqual.z ) < flErrorMargin ); + } + + [[nodiscard]] bool IsZero() const + { + return ( std::fpclassify( this->x ) == FP_ZERO && std::fpclassify( this->y ) == FP_ZERO && std::fpclassify( this->z ) == FP_ZERO ); + } + + Angle_t Clamp() + { + this->x = std::clamp( this->x, -89.f, 89.f ); + this->y = std::clamp( this->y, -180.f, 180.f ); + this->z = std::clamp( this->z, -50.f, 50.f ); + return *this; + } + + Angle_t Normalize() + { + this->x = std::isfinite( this->x ) ? std::remainderf( this->x, 360.f ) : 0.f; + this->y = std::isfinite( this->y ) ? std::remainderf( this->y, 360.f ) : 0.f; + this->z = std::clamp( this->z, -50.f, 50.f ); + return *this; + } + + Angle_t Mod( const float flValue ) + { + this->x = std::fmodf( this->x, flValue ); + this->y = std::fmodf( this->y, flValue ); + this->z = std::fmodf( this->z, flValue ); + return *this; + } +}; diff --git a/l4d2/game/source_engine/datatypes/checksum_crc.cpp b/l4d2/game/source_engine/datatypes/checksum_crc.cpp new file mode 100644 index 0000000..4a38e2d --- /dev/null +++ b/l4d2/game/source_engine/datatypes/checksum_crc.cpp @@ -0,0 +1,114 @@ +#include "checksum_crc.h" + +#define BigShort( val ) WordSwap( val ) +#define BigWord( val ) WordSwap( val ) +#define BigLong( val ) DWordSwap( val ) +#define BigDWord( val ) DWordSwap( val ) +#define LittleShort( val ) ( val ) +#define LittleWord( val ) ( val ) +#define LittleLong( val ) ( val ) +#define LittleDWord( val ) ( val ) +#define SwapShort( val ) BigShort( val ) +#define SwapWord( val ) BigWord( val ) +#define SwapLong( val ) BigLong( val ) +#define SwapDWord( val ) BigDWord( val ) + +#define CRC32_INIT_VALUE 0xFFFFFFFFUL +#define CRC32_XOR_VALUE 0xFFFFFFFFUL + +#define NUM_BYTES 256 +static const CRC32_t pulCRCTable[ NUM_BYTES ] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; + +void CRC32_Init( CRC32_t* pulCRC ) +{ + *pulCRC = CRC32_INIT_VALUE; +} + +void CRC32_Final( CRC32_t* pulCRC ) +{ + *pulCRC ^= CRC32_XOR_VALUE; +} + +CRC32_t CRC32_GetTableEntry( unsigned int slot ) +{ + return pulCRCTable[ static_cast( slot ) ]; +} + +void CRC32_ProcessBuffer( CRC32_t* pulCRC, const void* pBuffer, int nBuffer ) +{ + CRC32_t ulCrc = *pulCRC; + auto pb = ( unsigned char* )pBuffer; + unsigned int nFront; + int nMain; + +JustAfew: switch ( nBuffer ) + { + case 7: + ulCrc = pulCRCTable[ *pb++ ^ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + + case 6: + ulCrc = pulCRCTable[ *pb++ ^ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + + case 5: + ulCrc = pulCRCTable[ *pb++ ^ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + + case 4: + ulCrc ^= LittleLong( *(CRC32_t*)pb ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + *pulCRC = ulCrc; + return; + + case 3: + ulCrc = pulCRCTable[ *pb++ ^ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + + case 2: + ulCrc = pulCRCTable[ *pb++ ^ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + + case 1: + ulCrc = pulCRCTable[ *pb++ ^ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + + case 0: + *pulCRC = ulCrc; + return; + } + + // We may need to do some alignment work up front, and at the end, so that + // the main loop is aligned and only has to worry about 8 byte at a time. + // + // The low-order two bits of pb and nBuffer in total control the + // upfront work. + // + nFront = ( ( unsigned int )pb ) & 3; + nBuffer -= nFront; + switch ( nFront ) + { + case 3: + ulCrc = pulCRCTable[ *pb++ ^ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + case 2: + ulCrc = pulCRCTable[ *pb++ ^ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + case 1: + ulCrc = pulCRCTable[ *pb++ ^ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + } + + nMain = nBuffer >> 3; + while ( nMain-- ) + { + ulCrc ^= LittleLong( *(CRC32_t*)pb ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc ^= LittleLong( *(CRC32_t*)(pb + 4) ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + ulCrc = pulCRCTable[ static_cast( ulCrc ) ] ^ ( ulCrc >> 8 ); + pb += 8; + } + + nBuffer &= 7; + goto JustAfew; +} diff --git a/l4d2/game/source_engine/datatypes/checksum_crc.h b/l4d2/game/source_engine/datatypes/checksum_crc.h new file mode 100644 index 0000000..f1c4c77 --- /dev/null +++ b/l4d2/game/source_engine/datatypes/checksum_crc.h @@ -0,0 +1,19 @@ +#pragma once + +typedef unsigned long CRC32_t; + +void CRC32_Init(CRC32_t* pulCRC); +void CRC32_ProcessBuffer(CRC32_t* pulCRC, const void* p, int len); +void CRC32_Final(CRC32_t* pulCRC); +CRC32_t CRC32_GetTableEntry(unsigned int slot); + +inline CRC32_t CRC32_ProcessSingleBuffer(const void* p, int len) +{ + CRC32_t crc; + + CRC32_Init(&crc); + CRC32_ProcessBuffer(&crc, p, len); + CRC32_Final(&crc); + + return crc; +} \ No newline at end of file diff --git a/l4d2/game/source_engine/datatypes/checksum_md5.cpp b/l4d2/game/source_engine/datatypes/checksum_md5.cpp new file mode 100644 index 0000000..6864d07 --- /dev/null +++ b/l4d2/game/source_engine/datatypes/checksum_md5.cpp @@ -0,0 +1,241 @@ +#include "checksum_md5.h" +#include +#include + +// The four core functions - F1 is optimized somewhat +// #define F1(x, y, z) (x & y | ~x & z) +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +// This is the central step in the MD5 algorithm. +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +//----------------------------------------------------------------------------- +// Purpose: The core of the MD5 algorithm, this alters an existing MD5 hash to +// reflect the addition of 16 longwords of new data. MD5Update blocks +// the data and converts bytes into longwords for this routine. +// Input : buf[4] - +// in[16] - +// Output : static void +//----------------------------------------------------------------------------- +static void MD5Transform( unsigned int buf[ 4 ], const unsigned int in[ 16 ] ) +{ + register unsigned int a, b, c, d; + + a = buf[ 0 ]; + b = buf[ 1 ]; + c = buf[ 2 ]; + d = buf[ 3 ]; + + MD5STEP( F1, a, b, c, d, in[0] + 0xd76aa478, 7 ); + MD5STEP( F1, d, a, b, c, in[1] + 0xe8c7b756, 12 ); + MD5STEP( F1, c, d, a, b, in[2] + 0x242070db, 17 ); + MD5STEP( F1, b, c, d, a, in[3] + 0xc1bdceee, 22 ); + MD5STEP( F1, a, b, c, d, in[4] + 0xf57c0faf, 7 ); + MD5STEP( F1, d, a, b, c, in[5] + 0x4787c62a, 12 ); + MD5STEP( F1, c, d, a, b, in[6] + 0xa8304613, 17 ); + MD5STEP( F1, b, c, d, a, in[7] + 0xfd469501, 22 ); + MD5STEP( F1, a, b, c, d, in[8] + 0x698098d8, 7 ); + MD5STEP( F1, d, a, b, c, in[9] + 0x8b44f7af, 12 ); + MD5STEP( F1, c, d, a, b, in[10] + 0xffff5bb1, 17 ); + MD5STEP( F1, b, c, d, a, in[11] + 0x895cd7be, 22 ); + MD5STEP( F1, a, b, c, d, in[12] + 0x6b901122, 7 ); + MD5STEP( F1, d, a, b, c, in[13] + 0xfd987193, 12 ); + MD5STEP( F1, c, d, a, b, in[14] + 0xa679438e, 17 ); + MD5STEP( F1, b, c, d, a, in[15] + 0x49b40821, 22 ); + + MD5STEP( F2, a, b, c, d, in[1] + 0xf61e2562, 5 ); + MD5STEP( F2, d, a, b, c, in[6] + 0xc040b340, 9 ); + MD5STEP( F2, c, d, a, b, in[11] + 0x265e5a51, 14 ); + MD5STEP( F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20 ); + MD5STEP( F2, a, b, c, d, in[5] + 0xd62f105d, 5 ); + MD5STEP( F2, d, a, b, c, in[10] + 0x02441453, 9 ); + MD5STEP( F2, c, d, a, b, in[15] + 0xd8a1e681, 14 ); + MD5STEP( F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20 ); + MD5STEP( F2, a, b, c, d, in[9] + 0x21e1cde6, 5 ); + MD5STEP( F2, d, a, b, c, in[14] + 0xc33707d6, 9 ); + MD5STEP( F2, c, d, a, b, in[3] + 0xf4d50d87, 14 ); + MD5STEP( F2, b, c, d, a, in[8] + 0x455a14ed, 20 ); + MD5STEP( F2, a, b, c, d, in[13] + 0xa9e3e905, 5 ); + MD5STEP( F2, d, a, b, c, in[2] + 0xfcefa3f8, 9 ); + MD5STEP( F2, c, d, a, b, in[7] + 0x676f02d9, 14 ); + MD5STEP( F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20 ); + + MD5STEP( F3, a, b, c, d, in[5] + 0xfffa3942, 4 ); + MD5STEP( F3, d, a, b, c, in[8] + 0x8771f681, 11 ); + MD5STEP( F3, c, d, a, b, in[11] + 0x6d9d6122, 16 ); + MD5STEP( F3, b, c, d, a, in[14] + 0xfde5380c, 23 ); + MD5STEP( F3, a, b, c, d, in[1] + 0xa4beea44, 4 ); + MD5STEP( F3, d, a, b, c, in[4] + 0x4bdecfa9, 11 ); + MD5STEP( F3, c, d, a, b, in[7] + 0xf6bb4b60, 16 ); + MD5STEP( F3, b, c, d, a, in[10] + 0xbebfbc70, 23 ); + MD5STEP( F3, a, b, c, d, in[13] + 0x289b7ec6, 4 ); + MD5STEP( F3, d, a, b, c, in[0] + 0xeaa127fa, 11 ); + MD5STEP( F3, c, d, a, b, in[3] + 0xd4ef3085, 16 ); + MD5STEP( F3, b, c, d, a, in[6] + 0x04881d05, 23 ); + MD5STEP( F3, a, b, c, d, in[9] + 0xd9d4d039, 4 ); + MD5STEP( F3, d, a, b, c, in[12] + 0xe6db99e5, 11 ); + MD5STEP( F3, c, d, a, b, in[15] + 0x1fa27cf8, 16 ); + MD5STEP( F3, b, c, d, a, in[2] + 0xc4ac5665, 23 ); + + MD5STEP( F4, a, b, c, d, in[0] + 0xf4292244, 6 ); + MD5STEP( F4, d, a, b, c, in[7] + 0x432aff97, 10 ); + MD5STEP( F4, c, d, a, b, in[14] + 0xab9423a7, 15 ); + MD5STEP( F4, b, c, d, a, in[5] + 0xfc93a039, 21 ); + MD5STEP( F4, a, b, c, d, in[12] + 0x655b59c3, 6 ); + MD5STEP( F4, d, a, b, c, in[3] + 0x8f0ccc92, 10 ); + MD5STEP( F4, c, d, a, b, in[10] + 0xffeff47d, 15 ); + MD5STEP( F4, b, c, d, a, in[1] + 0x85845dd1, 21 ); + MD5STEP( F4, a, b, c, d, in[8] + 0x6fa87e4f, 6 ); + MD5STEP( F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10 ); + MD5STEP( F4, c, d, a, b, in[6] + 0xa3014314, 15 ); + MD5STEP( F4, b, c, d, a, in[13] + 0x4e0811a1, 21 ); + MD5STEP( F4, a, b, c, d, in[4] + 0xf7537e82, 6 ); + MD5STEP( F4, d, a, b, c, in[11] + 0xbd3af235, 10 ); + MD5STEP( F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15 ); + MD5STEP( F4, b, c, d, a, in[9] + 0xeb86d391, 21 ); + + buf[ 0 ] += a; + buf[ 1 ] += b; + buf[ 2 ] += c; + buf[ 3 ] += d; +} + +//----------------------------------------------------------------------------- +// Purpose: Start MD5 accumulation. Set bit count to 0 and buffer to mysterious initialization constants. + +// Input : *ctx - +//----------------------------------------------------------------------------- +void MD5Init( MD5Context_t* ctx ) +{ + ctx->buf[ 0 ] = 0x67452301; + ctx->buf[ 1 ] = 0xefcdab89; + ctx->buf[ 2 ] = 0x98badcfe; + ctx->buf[ 3 ] = 0x10325476; + + ctx->bits[ 0 ] = 0; + ctx->bits[ 1 ] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Update context to reflect the concatenation of another buffer full of bytes. +// Input : *ctx - +// *buf - +// len - +//----------------------------------------------------------------------------- +void MD5Update( MD5Context_t* ctx, const unsigned char* buf, unsigned int len ) +{ + unsigned int t; + + /* Update bitcount */ + + t = ctx->bits[ 0 ]; + if ( ( ctx->bits[ 0 ] = t + ( len << 3 ) ) < t ) + ctx->bits[ 1 ]++; /* Carry from low to high */ + ctx->bits[ 1 ] += len >> 29; + + t = ( t >> 3 ) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) + { + unsigned char* p = static_cast( ctx->in ) + t; + + t = 64 - t; + if ( len < t ) + { + memcpy( p, buf, len ); + return; + } + memcpy( p, buf, t ); + //byteReverse(ctx->in, 16); + MD5Transform( ctx->buf, ( unsigned int* )ctx->in ); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while ( len >= 64 ) + { + memcpy( ctx->in, buf, 64 ); + //byteReverse(ctx->in, 16); + MD5Transform( ctx->buf, ( unsigned int* )ctx->in ); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy( ctx->in, buf, len ); +} + +//----------------------------------------------------------------------------- +// Purpose: Final wrapup - pad to 64-byte boundary with the bit pattern +// 1 0* (64-bit count of bits processed, MSB-first) +// Input : digest[MD5_DIGEST_LENGTH] - +// *ctx - +//----------------------------------------------------------------------------- +void MD5Final( unsigned char digest[ MD5_DIGEST_LENGTH ], MD5Context_t* ctx ) +{ + unsigned count; + unsigned char* p; + + /* Compute number of bytes mod 64 */ + count = ( ctx->bits[ 0 ] >> 3 ) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if ( count < 8 ) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset( p, 0, count ); + //byteReverse(ctx->in, 16); + MD5Transform( ctx->buf, ( unsigned int* )ctx->in ); + + /* Now fill the next block with 56 bytes */ + memset( ctx->in, 0, 56 ); + } else + { + /* Pad block to 56 bytes */ + memset( p, 0, count - 8 ); + } + //byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ( ( unsigned int* )ctx->in )[ 14 ] = ctx->bits[ 0 ]; + ( ( unsigned int* )ctx->in )[ 15 ] = ctx->bits[ 1 ]; + + MD5Transform( ctx->buf, ( unsigned int* )ctx->in ); + //byteReverse((unsigned char *) ctx->buf, 4); + memcpy( digest, ctx->buf, MD5_DIGEST_LENGTH ); + memset( ctx, 0, sizeof( *ctx ) ); /* In case it's sensitive */ +} + +//----------------------------------------------------------------------------- +// Purpose: generate pseudo random number from a seed number +// Input : seed number +// Output : pseudo random number +//----------------------------------------------------------------------------- +unsigned int MD5_PseudoRandom( unsigned int nSeed ) +{ + MD5Context_t ctx; + unsigned char digest[ MD5_DIGEST_LENGTH ]; // The MD5 Hash + + memset( &ctx, 0, sizeof( ctx ) ); + + MD5Init( &ctx ); + MD5Update( &ctx, ( unsigned char* )&nSeed, sizeof( nSeed ) ); + MD5Final( digest, &ctx ); + + return *( unsigned int* )( digest + 6 ); // use 4 middle bytes for random value +} diff --git a/l4d2/game/source_engine/datatypes/checksum_md5.h b/l4d2/game/source_engine/datatypes/checksum_md5.h new file mode 100644 index 0000000..0747a71 --- /dev/null +++ b/l4d2/game/source_engine/datatypes/checksum_md5.h @@ -0,0 +1,19 @@ +#pragma once + +// 16 bytes == 128 bit digest +#define MD5_DIGEST_LENGTH 16 +#define MD5_BIT_LENGTH ( MD5_DIGEST_LENGTH * sizeof(unsigned char) ) + +// MD5 Hash +typedef struct +{ + unsigned int buf[4]; + unsigned int bits[2]; + unsigned char in[64]; +} MD5Context_t; + +void MD5Init(MD5Context_t* context); +void MD5Update(MD5Context_t* context, unsigned char const* buf, unsigned int len); +void MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5Context_t* context); + +unsigned int MD5_PseudoRandom(unsigned int nSeed); \ No newline at end of file diff --git a/l4d2/game/source_engine/datatypes/datamap.h b/l4d2/game/source_engine/datatypes/datamap.h new file mode 100644 index 0000000..6666d1c --- /dev/null +++ b/l4d2/game/source_engine/datatypes/datamap.h @@ -0,0 +1,133 @@ +#pragma once + +struct Datamap_t; +struct typedescription_t; +class ISaveRestoreOps; + +enum +{ + TD_OFFSET_NORMAL = 0, + TD_OFFSET_PACKED = 1, + + // Must be last + TD_OFFSET_COUNT, +}; + +enum class fieldtype_t +{ + FIELD_VOID = 0, + // No type or value + FIELD_FLOAT, + // Any floating point value + FIELD_STRING, + // A string ID (return from ALLOC_STRING) + FIELD_VECTOR, + // Any vector, QAngle, or AngularImpulse + FIELD_QUATERNION, + // A quaternion + FIELD_INTEGER, + // Any integer or enum + FIELD_BOOLEAN, + // boolean, implemented as an int, I may use this as a hint for compression + FIELD_SHORT, + // 2 byte integer + FIELD_CHARACTER, + // a byte + FIELD_COLOR32, + // 8-bit per channel r,g,b,a (32bit color) + FIELD_EMBEDDED, + // an embedded object with a datadesc, recursively traverse and embedded class/structure based on an additional typedescription + FIELD_CUSTOM, + // special type that contains function pointers to it's read/write/parse functions + + FIELD_CLASSPTR, + // CBaseEntity * + FIELD_EHANDLE, + // Entity handle + FIELD_EDICT, + // edict_t * + + FIELD_POSITION_VECTOR, + // A world coordinate (these are fixed up across level transitions automagically) + FIELD_TIME, + // a floating point time (these are fixed up automatically too!) + FIELD_TICK, + // an integer tick count( fixed up similarly to time) + FIELD_MODELNAME, + // Engine string that is a model name (needs precache) + FIELD_SOUNDNAME, + // Engine string that is a sound name (needs precache) + + FIELD_INPUT, + // a list of inputed data fields (all derived from CMultiInputVar) + FIELD_FUNCTION, + // A class function pointer (Think, Use, etc) + + FIELD_VMATRIX, + // a vmatrix (output coords are NOT worldspace) + + // NOTE: Use float arrays for local transformations that don't need to be fixed up. + FIELD_VMATRIX_WORLDSPACE, + // A VMatrix that maps some local space to world space (translation is fixed up on level transitions) + FIELD_MATRIX3X4_WORLDSPACE, + // matrix3x4_t that maps some local space to world space (translation is fixed up on level transitions) + + FIELD_INTERVAL, + // a start and range floating point interval ( e.g., 3.2->3.6 == 3.2 and 0.4 ) + FIELD_MODELINDEX, + // a model index + FIELD_MATERIALINDEX, + // a material index (using the material precache string table) + + FIELD_VECTOR2D, + // 2 floats + + FIELD_TYPECOUNT, + // MUST BE LAST +}; + +struct typedescription_t +{ + fieldtype_t type; + const char* name; + int offset[TD_OFFSET_COUNT]; // 0 == normal, 1 == packed offset + unsigned short size; + short flags; + // the name of the variable in the map/fgd data, or the name of the action + const char* external_name; + // pointer to the function set for save/restoring of custom data types + ISaveRestoreOps* save_restore_ops; + // for associating function with string names + void* input_func; + // For embedding additional datatables inside this one + Datamap_t* td; + + // Stores the actual member variable size in bytes + int field_size_in_bytes; + + // FTYPEDESC_OVERRIDE point to first baseclass instance if chains_validated has occurred + struct typedescription_t* override_field; + + // Used to track exclusion of baseclass fields + int override_count; + + // Tolerance for field errors for float fields + float field_tolerance; +}; + +//----------------------------------------------------------------------------- +// Purpose: stores the list of objects in the hierarchy +// used to iterate through an object's data descriptions +//----------------------------------------------------------------------------- +struct Datamap_t +{ + typedescription_t* data_desc; + int data_num_fields; + const char* data_class_name; + Datamap_t* base_map; + + bool chains_validated; + // Have the "packed" offsets been computed + bool packed_offsets_computed; + int packed_size; +}; diff --git a/l4d2/game/source_engine/datatypes/matrix.h b/l4d2/game/source_engine/datatypes/matrix.h new file mode 100644 index 0000000..572b97b --- /dev/null +++ b/l4d2/game/source_engine/datatypes/matrix.h @@ -0,0 +1,220 @@ +#pragma once +#include "vectors.h" + +using matrix3x3_t = float[ 3 ][ 3 ]; + +struct matrix3x4_t +{ + matrix3x4_t() = default; + + constexpr matrix3x4_t( const float m00, const float m01, const float m02, const float m03, const float m10, const float m11, const float m12, const float m13, const float m20, const float m21, const float m22, const float m23 ) + { + arrData[ 0 ][ 0 ] = m00; + arrData[ 0 ][ 1 ] = m01; + arrData[ 0 ][ 2 ] = m02; + arrData[ 0 ][ 3 ] = m03; + arrData[ 1 ][ 0 ] = m10; + arrData[ 1 ][ 1 ] = m11; + arrData[ 1 ][ 2 ] = m12; + arrData[ 1 ][ 3 ] = m13; + arrData[ 2 ][ 0 ] = m20; + arrData[ 2 ][ 1 ] = m21; + arrData[ 2 ][ 2 ] = m22; + arrData[ 2 ][ 3 ] = m23; + } + + constexpr matrix3x4_t( const Vector3D_t& xAxis, const Vector3D_t& yAxis, const Vector3D_t& zAxis, const Vector3D_t& vecOrigin ) + { + Init( xAxis, yAxis, zAxis, vecOrigin ); + } + + constexpr void Init( const Vector3D_t& vecForward, const Vector3D_t& vecLeft, const Vector3D_t& vecUp, const Vector3D_t& vecOrigin ) + { + SetForward( vecForward ); + SetLeft( vecLeft ); + SetUp( vecUp ); + SetOrigin( vecOrigin ); + } + + constexpr void SetForward( const Vector3D_t& vecForward ) + { + this->arrData[ 0 ][ 0 ] = vecForward.x; + this->arrData[ 1 ][ 0 ] = vecForward.y; + this->arrData[ 2 ][ 0 ] = vecForward.z; + } + + constexpr void SetLeft( const Vector3D_t& vecLeft ) + { + this->arrData[ 0 ][ 1 ] = vecLeft.x; + this->arrData[ 1 ][ 1 ] = vecLeft.y; + this->arrData[ 2 ][ 1 ] = vecLeft.z; + } + + constexpr void SetUp( const Vector3D_t& vecUp ) + { + this->arrData[ 0 ][ 2 ] = vecUp.x; + this->arrData[ 1 ][ 2 ] = vecUp.y; + this->arrData[ 2 ][ 2 ] = vecUp.z; + } + + constexpr void SetOrigin( const Vector3D_t& vecOrigin ) + { + this->arrData[ 0 ][ 3 ] = vecOrigin.x; + this->arrData[ 1 ][ 3 ] = vecOrigin.y; + this->arrData[ 2 ][ 3 ] = vecOrigin.z; + } + + constexpr void Invalidate() + { + for ( auto& arrSubData : arrData ) + { + for ( auto& flData : arrSubData ) + flData = std::numeric_limits::infinity(); + } + } + + float* operator[]( const int nIndex ) + { + return arrData[ nIndex ]; + } + + const float* operator[]( const int nIndex ) const + { + return arrData[ nIndex ]; + } + + [[nodiscard]] constexpr Vector3D_t at( const int nIndex ) const + { + return Vector3D_t( arrData[ 0 ][ nIndex ], arrData[ 1 ][ nIndex ], arrData[ 2 ][ nIndex ] ); + } + + float* Base() + { + return &arrData[ 0 ][ 0 ]; + } + + [[nodiscard]] const float* Base() const + { + return &arrData[ 0 ][ 0 ]; + } + + float arrData[ 3 ][ 4 ] = {}; +}; + +__declspec(align(16)) class matrix3x4a_t : public matrix3x4_t +{ +public: + matrix3x4a_t& operator=( const matrix3x4_t& matSource ) + { + std::copy_n( matSource.Base(), sizeof( float ) * 3U * 4U, this->Base() ); + return *this; + } +}; + +struct ViewMatrix_t +{ + ViewMatrix_t() = default; + + constexpr ViewMatrix_t( const float m00, const float m01, const float m02, const float m03, const float m10, const float m11, const float m12, const float m13, const float m20, const float m21, const float m22, const float m23, const float m30, const float m31, const float m32, const float m33 ) + { + arrData[ 0 ][ 0 ] = m00; + arrData[ 0 ][ 1 ] = m01; + arrData[ 0 ][ 2 ] = m02; + arrData[ 0 ][ 3 ] = m03; + arrData[ 1 ][ 0 ] = m10; + arrData[ 1 ][ 1 ] = m11; + arrData[ 1 ][ 2 ] = m12; + arrData[ 1 ][ 3 ] = m13; + arrData[ 2 ][ 0 ] = m20; + arrData[ 2 ][ 1 ] = m21; + arrData[ 2 ][ 2 ] = m22; + arrData[ 2 ][ 3 ] = m23; + arrData[ 3 ][ 0 ] = m30; + arrData[ 3 ][ 1 ] = m31; + arrData[ 3 ][ 2 ] = m32; + arrData[ 3 ][ 3 ] = m33; + } + + constexpr ViewMatrix_t( const matrix3x4_t& matFrom, const Vector4D_t& vecAdditionalColumn = {} ) + { + arrData[ 0 ][ 0 ] = matFrom.arrData[ 0 ][ 0 ]; + arrData[ 0 ][ 1 ] = matFrom.arrData[ 0 ][ 1 ]; + arrData[ 0 ][ 2 ] = matFrom.arrData[ 0 ][ 2 ]; + arrData[ 0 ][ 3 ] = matFrom.arrData[ 0 ][ 3 ]; + arrData[ 1 ][ 0 ] = matFrom.arrData[ 1 ][ 0 ]; + arrData[ 1 ][ 1 ] = matFrom.arrData[ 1 ][ 1 ]; + arrData[ 1 ][ 2 ] = matFrom.arrData[ 1 ][ 2 ]; + arrData[ 1 ][ 3 ] = matFrom.arrData[ 1 ][ 3 ]; + arrData[ 2 ][ 0 ] = matFrom.arrData[ 2 ][ 0 ]; + arrData[ 2 ][ 1 ] = matFrom.arrData[ 2 ][ 1 ]; + arrData[ 2 ][ 2 ] = matFrom.arrData[ 2 ][ 2 ]; + arrData[ 2 ][ 3 ] = matFrom.arrData[ 2 ][ 3 ]; + arrData[ 3 ][ 0 ] = vecAdditionalColumn.x; + arrData[ 3 ][ 1 ] = vecAdditionalColumn.y; + arrData[ 3 ][ 2 ] = vecAdditionalColumn.z; + arrData[ 3 ][ 3 ] = vecAdditionalColumn.w; + } + + float* operator[]( const int nIndex ) + { + return arrData[ nIndex ]; + } + + const float* operator[]( const int nIndex ) const + { + return arrData[ nIndex ]; + } + + [[nodiscard]] constexpr Vector4D_t at( const int nIndex ) const + { + return Vector4D_t( arrData[ 0 ][ nIndex ], arrData[ 1 ][ nIndex ], arrData[ 2 ][ nIndex ], arrData[ 3 ][ nIndex ] ); + } + + constexpr ViewMatrix_t& operator+=( const ViewMatrix_t& matAdd ) + { + for ( int i = 0; i < 4; i++ ) + { + for ( int n = 0; n < 4; n++ ) + this->arrData[ i ][ n ] += matAdd[ i ][ n ]; + } + + return *this; + } + + constexpr ViewMatrix_t& operator-=( const ViewMatrix_t& matSubtract ) + { + for ( int i = 0; i < 4; i++ ) + { + for ( int n = 0; n < 4; n++ ) + this->arrData[ i ][ n ] -= matSubtract[ i ][ n ]; + } + + return *this; + } + + ViewMatrix_t operator*( const ViewMatrix_t& matMultiply ) const + { + return ViewMatrix_t( arrData[ 0 ][ 0 ] * matMultiply.arrData[ 0 ][ 0 ] + arrData[ 0 ][ 1 ] * matMultiply.arrData[ 1 ][ 0 ] + arrData[ 0 ][ 2 ] * matMultiply.arrData[ 2 ][ 0 ] + arrData[ 0 ][ 3 ] * matMultiply.arrData[ 3 ][ 0 ], arrData[ 0 ][ 0 ] * matMultiply.arrData[ 0 ][ 1 ] + arrData[ 0 ][ 1 ] * matMultiply.arrData[ 1 ][ 1 ] + arrData[ 0 ][ 2 ] * matMultiply.arrData[ 2 ][ 1 ] + arrData[ 0 ][ 3 ] * matMultiply.arrData[ 3 ][ 1 ], arrData[ 0 ][ 0 ] * matMultiply.arrData[ 0 ][ 2 ] + arrData[ 0 ][ 1 ] * matMultiply.arrData[ 1 ][ 2 ] + arrData[ 0 ][ 2 ] * matMultiply.arrData[ 2 ][ 2 ] + arrData[ 0 ][ 3 ] * matMultiply.arrData[ 3 ][ 2 ], arrData[ 0 ][ 0 ] * matMultiply.arrData[ 0 ][ 3 ] + arrData[ 0 ][ 1 ] * matMultiply.arrData[ 1 ][ 3 ] + arrData[ 0 ][ 2 ] * matMultiply.arrData[ 2 ][ 3 ] + arrData[ 0 ][ 3 ] * matMultiply.arrData[ 3 ][ 3 ], arrData[ 1 ][ 0 ] * matMultiply.arrData[ 0 ][ 0 ] + arrData[ 1 ][ 1 ] * matMultiply.arrData[ 1 ][ 0 ] + arrData[ 1 ][ 2 ] * matMultiply.arrData[ 2 ][ 0 ] + arrData[ 1 ][ 3 ] * matMultiply.arrData[ 3 ][ 0 ], arrData[ 1 ][ 0 ] * matMultiply.arrData[ 0 ][ 1 ] + arrData[ 1 ][ 1 ] * matMultiply.arrData[ 1 ][ 1 ] + arrData[ 1 ][ 2 ] * matMultiply.arrData[ 2 ][ 1 ] + arrData[ 1 ][ 3 ] * matMultiply.arrData[ 3 ][ 1 ], arrData[ 1 ][ 0 ] * matMultiply.arrData[ 0 ][ 2 ] + arrData[ 1 ][ 1 ] * matMultiply.arrData[ 1 ][ 2 ] + arrData[ 1 ][ 2 ] * matMultiply.arrData[ 2 ][ 2 ] + arrData[ 1 ][ 3 ] * matMultiply.arrData[ 3 ][ 2 ], arrData[ 1 ][ 0 ] * matMultiply.arrData[ 0 ][ 3 ] + arrData[ 1 ][ 1 ] * matMultiply.arrData[ 1 ][ 3 ] + arrData[ 1 ][ 2 ] * matMultiply.arrData[ 2 ][ 3 ] + arrData[ 1 ][ 3 ] * matMultiply.arrData[ 3 ][ 3 ], arrData[ 2 ][ 0 ] * matMultiply.arrData[ 0 ][ 0 ] + arrData[ 2 ][ 1 ] * matMultiply.arrData[ 1 ][ 0 ] + arrData[ 2 ][ 2 ] * matMultiply.arrData[ 2 ][ 0 ] + arrData[ 2 ][ 3 ] * matMultiply.arrData[ 3 ][ 0 ], arrData[ 2 ][ 0 ] * matMultiply.arrData[ 0 ][ 1 ] + arrData[ 2 ][ 1 ] * matMultiply.arrData[ 1 ][ 1 ] + arrData[ 2 ][ 2 ] * matMultiply.arrData[ 2 ][ 1 ] + arrData[ 2 ][ 3 ] * matMultiply.arrData[ 3 ][ 1 ], arrData[ 2 ][ 0 ] * matMultiply.arrData[ 0 ][ 2 ] + arrData[ 2 ][ 1 ] * matMultiply.arrData[ 1 ][ 2 ] + arrData[ 2 ][ 2 ] * matMultiply.arrData[ 2 ][ 2 ] + arrData[ 2 ][ 3 ] * matMultiply.arrData[ 3 ][ 2 ], arrData[ 2 ][ 0 ] * matMultiply.arrData[ 0 ][ 3 ] + arrData[ 2 ][ 1 ] * matMultiply.arrData[ 1 ][ 3 ] + arrData[ 2 ][ 2 ] * matMultiply.arrData[ 2 ][ 3 ] + arrData[ 2 ][ 3 ] * matMultiply.arrData[ 3 ][ 3 ], arrData[ 3 ][ 0 ] * matMultiply.arrData[ 0 ][ 0 ] + arrData[ 3 ][ 1 ] * matMultiply.arrData[ 1 ][ 0 ] + arrData[ 3 ][ 2 ] * matMultiply.arrData[ 2 ][ 0 ] + arrData[ 3 ][ 3 ] * matMultiply.arrData[ 3 ][ 0 ], arrData[ 3 ][ 0 ] * matMultiply.arrData[ 0 ][ 1 ] + arrData[ 3 ][ 1 ] * matMultiply.arrData[ 1 ][ 1 ] + arrData[ 3 ][ 2 ] * matMultiply.arrData[ 2 ][ 1 ] + arrData[ 3 ][ 3 ] * matMultiply.arrData[ 3 ][ 1 ], arrData[ 3 ][ 0 ] * matMultiply.arrData[ 0 ][ 2 ] + arrData[ 3 ][ 1 ] * matMultiply.arrData[ 1 ][ 2 ] + arrData[ 3 ][ 2 ] * matMultiply.arrData[ 2 ][ 2 ] + arrData[ 3 ][ 3 ] * matMultiply.arrData[ 3 ][ 2 ], arrData[ 3 ][ 0 ] * matMultiply.arrData[ 0 ][ 3 ] + arrData[ 3 ][ 1 ] * matMultiply.arrData[ 1 ][ 3 ] + arrData[ 3 ][ 2 ] * matMultiply.arrData[ 2 ][ 3 ] + arrData[ 3 ][ 3 ] * matMultiply.arrData[ 3 ][ 3 ] ); + } + + constexpr void Identity() + { + for ( int i = 0; i < 4; i++ ) + { + for ( int n = 0; n < 4; n++ ) + this->arrData[ i ][ n ] = i == n ? 1.0f : 0.0f; + } + } + + const matrix3x4_t& As3x4() const + { + return *reinterpret_cast( this ); + } + + matrix3x4_t& As3x4() + { + return *reinterpret_cast( this ); + } + + float arrData[ 4 ][ 4 ] = {}; +}; diff --git a/l4d2/game/source_engine/datatypes/usercmd.h b/l4d2/game/source_engine/datatypes/usercmd.h new file mode 100644 index 0000000..fd5dcf5 --- /dev/null +++ b/l4d2/game/source_engine/datatypes/usercmd.h @@ -0,0 +1,101 @@ +#pragma once + +#include "angle.h" +#include "vectors.h" + +#define MULTIPLAYER_BACKUP 150// v5 = this[51 * v3 + 59] + 88 * (a3 % 150); v5 = this[51 * v3 + 59] + 88 * (a3 % 150); + +enum +{ + IN_ATTACK = 1 << 0, + IN_JUMP = 1 << 1, + IN_DUCK = 1 << 2, + IN_FORWARD = 1 << 3, + IN_BACK = 1 << 4, + IN_USE = 1 << 5, + IN_CANCEL = 1 << 6, + IN_LEFT = 1 << 7, + IN_RIGHT = 1 << 8, + IN_MOVELEFT = 1 << 9, + IN_MOVERIGHT = 1 << 10, + IN_ATTACK2 = 1 << 11, + IN_RUN = 1 << 12, + IN_RELOAD = 1 << 13, + IN_ALT1 = 1 << 14, + IN_ALT2 = 1 << 15, + IN_SCORE = 1 << 16, + IN_SPEED = 1 << 17, + IN_WALK = 1 << 18, + IN_ZOOM = 1 << 19, + IN_WEAPON1 = 1 << 20, + IN_WEAPON2 = 1 << 21, + IN_BULLRUSH = 1 << 22, + IN_GRENADE1 = 1 << 23, + IN_GRENADE2 = 1 << 24, + IN_ATTACK3 = 1 << 25, +}; + +enum +{ + MOVETYPE_NONE = 0, + MOVETYPE_ISOMETRIC, + MOVETYPE_WALK, + MOVETYPE_STEP, + MOVETYPE_FLY, + MOVETYPE_FLYGRAVITY, + MOVETYPE_VPHYSICS, + MOVETYPE_PUSH, + MOVETYPE_NOCLIP, + MOVETYPE_LADDER, + MOVETYPE_OBSERVER, + MOVETYPE_CUSTOM, + MOVETYPE_LAST = MOVETYPE_CUSTOM, + MOVETYPE_MAX_BITS = 4 +}; + +struct CUserCmd +{ + CRC32_t GetCheckSum(void) const + { + CRC32_t crc; + + CRC32_Init(&crc); + CRC32_ProcessBuffer(&crc, &commandnumber, sizeof(commandnumber)); + CRC32_ProcessBuffer(&crc, &tickcount, sizeof(tickcount)); + CRC32_ProcessBuffer(&crc, &viewangles, sizeof(viewangles)); + CRC32_ProcessBuffer(&crc, &forwardmove, sizeof(forwardmove)); + CRC32_ProcessBuffer(&crc, &sidemove, sizeof(sidemove)); + CRC32_ProcessBuffer(&crc, &upmove, sizeof(upmove)); + CRC32_ProcessBuffer(&crc, &buttons, sizeof(buttons)); + CRC32_ProcessBuffer(&crc, &impulse, sizeof(impulse)); + CRC32_ProcessBuffer(&crc, &weaponselect, sizeof(weaponselect)); + CRC32_ProcessBuffer(&crc, &weaponsubtype, sizeof(weaponsubtype)); + CRC32_ProcessBuffer(&crc, &random_seed, sizeof(random_seed)); + CRC32_ProcessBuffer(&crc, &mousedx, sizeof(mousedx)); + CRC32_ProcessBuffer(&crc, &mousedy, sizeof(mousedy)); + CRC32_Final(&crc); + + return crc; + } + CUserCmd() + { + memset(this, 0, sizeof(*this)); + }; + virtual ~CUserCmd() {}; + + int commandnumber; + int tickcount; + Angle_t viewangles; + float forwardmove; + float sidemove; + float upmove; + bitflag_t buttons; + std::uint8_t impulse; + int weaponselect; + int weaponsubtype; + int random_seed; + short mousedx; + short mousedy; + bool hasbeenpredicted; + PAD(0x18); +}; diff --git a/l4d2/game/source_engine/datatypes/vectors.h b/l4d2/game/source_engine/datatypes/vectors.h new file mode 100644 index 0000000..b353886 --- /dev/null +++ b/l4d2/game/source_engine/datatypes/vectors.h @@ -0,0 +1,310 @@ +#pragma once + +// used: isfinite, fmodf, sqrtf +#include +// used: numeric_limits +#include + +class Vector2D_t +{ +public: + constexpr Vector2D_t( float x = 0.f, float y = 0.f ) : x( x ), y( y ) + { + } + + [[nodiscard]] bool IsZero() const + { + return ( std::fpclassify( this->x ) == FP_ZERO && std::fpclassify( this->y ) == FP_ZERO ); + } + + float x, y; +}; + +class Vector3D_t +{ +public: + constexpr Vector3D_t( float x = 0.f, float y = 0.f, float z = 0.f ) : x( x ), y( y ), z( z ) + { + } + + constexpr Vector3D_t( const float* arrVector ) : x( arrVector[ 0 ] ), y( arrVector[ 1 ] ), z( arrVector[ 2 ] ) + { + } + + constexpr Vector3D_t( const Vector2D_t& vecBase2D ) : x( vecBase2D.x ), y( vecBase2D.y ), z( 0.0f ) + { + } + + [[nodiscard]] bool IsValid() const + { + return std::isfinite( this->x ) && std::isfinite( this->y ) && std::isfinite( this->z ); + } + + constexpr void Invalidate() + { + this->x = this->y = this->z = std::numeric_limits::infinity(); + } + + [[nodiscard]] float* data() + { + return reinterpret_cast( this ); + } + + [[nodiscard]] const float* data() const + { + return reinterpret_cast( const_cast( this ) ); + } + + float& operator[]( const std::size_t i ) + { + return this->data()[ i ]; + } + + const float& operator[]( const std::size_t i ) const + { + return this->data()[ i ]; + } + + bool operator==( const Vector3D_t& vecBase ) const + { + return this->IsEqual( vecBase ); + } + + bool operator!=( const Vector3D_t& vecBase ) const + { + return !this->IsEqual( vecBase ); + } + + constexpr Vector3D_t& operator=( const Vector3D_t& vecBase ) + { + this->x = vecBase.x; + this->y = vecBase.y; + this->z = vecBase.z; + return *this; + } + + constexpr Vector3D_t& operator=( const Vector2D_t& vecBase2D ) + { + this->x = vecBase2D.x; + this->y = vecBase2D.y; + this->z = 0.0f; + return *this; + } + + constexpr Vector3D_t& operator+=( const Vector3D_t& vecBase ) + { + this->x += vecBase.x; + this->y += vecBase.y; + this->z += vecBase.z; + return *this; + } + + constexpr Vector3D_t& operator-=( const Vector3D_t& vecBase ) + { + this->x -= vecBase.x; + this->y -= vecBase.y; + this->z -= vecBase.z; + return *this; + } + + constexpr Vector3D_t& operator*=( const Vector3D_t& vecBase ) + { + this->x *= vecBase.x; + this->y *= vecBase.y; + this->z *= vecBase.z; + return *this; + } + + constexpr Vector3D_t& operator/=( const Vector3D_t& vecBase ) + { + this->x /= vecBase.x; + this->y /= vecBase.y; + this->z /= vecBase.z; + return *this; + } + + constexpr Vector3D_t& operator+=( const float flAdd ) + { + this->x += flAdd; + this->y += flAdd; + this->z += flAdd; + return *this; + } + + constexpr Vector3D_t& operator-=( const float flSubtract ) + { + this->x -= flSubtract; + this->y -= flSubtract; + this->z -= flSubtract; + return *this; + } + + constexpr Vector3D_t& operator*=( const float flMultiply ) + { + this->x *= flMultiply; + this->y *= flMultiply; + this->z *= flMultiply; + return *this; + } + + constexpr Vector3D_t& operator/=( const float flDivide ) + { + this->x /= flDivide; + this->y /= flDivide; + this->z /= flDivide; + return *this; + } + + Vector3D_t operator+( const Vector3D_t& vecAdd ) const + { + return Vector3D_t( this->x + vecAdd.x, this->y + vecAdd.y, this->z + vecAdd.z ); + } + + Vector3D_t operator-( const Vector3D_t& vecSubtract ) const + { + return Vector3D_t( this->x - vecSubtract.x, this->y - vecSubtract.y, this->z - vecSubtract.z ); + } + + Vector3D_t operator*( const Vector3D_t& vecMultiply ) const + { + return Vector3D_t( this->x * vecMultiply.x, this->y * vecMultiply.y, this->z * vecMultiply.z ); + } + + Vector3D_t operator/( const Vector3D_t& vecDivide ) const + { + return Vector3D_t( this->x / vecDivide.x, this->y / vecDivide.y, this->z / vecDivide.z ); + } + + Vector3D_t operator+( const float flAdd ) const + { + return Vector3D_t( this->x + flAdd, this->y + flAdd, this->z + flAdd ); + } + + Vector3D_t operator-( const float flSubtract ) const + { + return Vector3D_t( this->x - flSubtract, this->y - flSubtract, this->z - flSubtract ); + } + + Vector3D_t operator*( const float flMultiply ) const + { + return Vector3D_t( this->x * flMultiply, this->y * flMultiply, this->z * flMultiply ); + } + + Vector3D_t operator/( const float flDivide ) const + { + return Vector3D_t( this->x / flDivide, this->y / flDivide, this->z / flDivide ); + } + + [[nodiscard]] bool IsEqual( const Vector3D_t& vecEqual, const float flErrorMargin = std::numeric_limits::epsilon() ) const + { + return ( std::fabsf( this->x - vecEqual.x ) < flErrorMargin && std::fabsf( this->y - vecEqual.y ) < flErrorMargin && std::fabsf( this->z - vecEqual.z ) < flErrorMargin ); + } + + [[nodiscard]] bool IsZero() const + { + return ( std::fpclassify( this->x ) == FP_ZERO && std::fpclassify( this->y ) == FP_ZERO && std::fpclassify( this->z ) == FP_ZERO ); + } + + [[nodiscard]] Vector2D_t ToVector2D() const + { + return Vector2D_t( this->x, this->y ); + } + + [[nodiscard]] float Length() const + { + return std::sqrtf( this->LengthSqr() ); + } + + [[nodiscard]] constexpr float LengthSqr() const + { + return DotProduct( *this ); + } + + [[nodiscard]] float Length2D() const + { + return std::sqrtf( this->Length2DSqr() ); + } + + [[nodiscard]] constexpr float Length2DSqr() const + { + return ( this->x * this->x + this->y * this->y ); + } + + [[nodiscard]] float DistTo( const Vector3D_t& vecEnd ) const + { + return ( *this - vecEnd ).Length(); + } + + [[nodiscard]] constexpr float DistToSqr( const Vector3D_t& vecEnd ) const + { + return ( *this - vecEnd ).LengthSqr(); + } + + [[nodiscard]] Vector3D_t Normalized() const + { + Vector3D_t vecOut = *this; + vecOut.NormalizeInPlace(); + return vecOut; + } + + float NormalizeInPlace() + { + const float flLength = this->Length(); + const float flRadius = 1.0f / ( flLength + std::numeric_limits::epsilon() ); + + this->x *= flRadius; + this->y *= flRadius; + this->z *= flRadius; + + return flLength; + } + + [[nodiscard]] constexpr float DotProduct( const Vector3D_t& vecDot ) const + { + return ( this->x * vecDot.x + this->y * vecDot.y + this->z * vecDot.z ); + } + + [[nodiscard]] constexpr Vector3D_t CrossProduct( const Vector3D_t& vecCross ) const + { + return Vector3D_t( this->y * vecCross.z - this->z * vecCross.y, this->z * vecCross.x - this->x * vecCross.z, this->x * vecCross.y - this->y * vecCross.x ); + } + + + + float x, y, z; +}; + +class Vector4D_t +{ +public: + constexpr Vector4D_t( float x = 0.f, float y = 0.f, float z = 0.f, float w = 0.f ) : x( x ), y( y ), z( z ), w( w ) + { + } + + float x, y, z, w; +}; + +class __declspec(align(16)) VectorAligned_t : public Vector3D_t +{ +public: + VectorAligned_t() = default; + + explicit VectorAligned_t( const Vector3D_t& vecBase ) + { + this->x = vecBase.x; + this->y = vecBase.y; + this->z = vecBase.z; + this->w = 0.f; + } + + constexpr VectorAligned_t& operator=( const Vector3D_t& vecBase ) + { + this->x = vecBase.x; + this->y = vecBase.y; + this->z = vecBase.z; + this->w = 0.f; + return *this; + } + + float w; +}; diff --git a/l4d2/game/source_engine/entity.h b/l4d2/game/source_engine/entity.h new file mode 100644 index 0000000..24807c0 --- /dev/null +++ b/l4d2/game/source_engine/entity.h @@ -0,0 +1,89 @@ +#pragma once + +struct IClientRenderable_t +{ + auto SetupBones( matrix3x4_t* pBoneToWorldOut, int MaxBones, int BoneMask, float CurrentTime ) + { + return Hikari::Memory->VirtualCall(this, pBoneToWorldOut, MaxBones, BoneMask, CurrentTime); + } + + auto GetModel() + { + return Hikari::Memory->VirtualCall( this ); + } +}; + +struct IClientNetworkable_t +{ + auto IsDormant() + { + return Hikari::Memory->VirtualCall( this ); + } + + auto GetClientClass() + { + return Hikari::Memory->VirtualCall( this ); + } +}; + +struct CBaseEntity_t +{ + IClientRenderable_t* GetRenderable() + { + return reinterpret_cast( reinterpret_cast( this ) + sizeof( void* ) ); + } + + IClientNetworkable_t* GetNetworkable() + { + return reinterpret_cast( reinterpret_cast( this ) + sizeof( void* ) * 2 ); + } + + + auto GetAbsOrigin() + { + return Hikari::Memory->VirtualCall( this ); + } + + auto GetAbsAngle() + { + return Hikari::Memory->VirtualCall(this); + } +}; + +struct CBaseAnimating_t : CBaseEntity_t +{ + +}; + +struct CBaseCombatWeapon_t : CBaseEntity_t +{ + +}; + +struct CBaseCombatCharacter_t : CBaseAnimating_t +{ + +}; + +struct CBasePlayer_t : CBaseCombatCharacter_t +{ + NETVAR(m_iHealth, "CBasePlayer", "m_iHealth", std::int32_t); + NETVAR(m_fFlags, "CBasePlayer", "m_fFlags", bitflag_t); + + bool IsPlayerDead() + { + return Game::Addresses::IsPlayerDead.Cast()(this); + } + + auto IsAlive() + { + return !IsPlayerDead(); + } +}; + +struct CTerrorPlayer_t : CBasePlayer_t +{ + NETVAR(m_iMaxHealth, "CTerrorPlayer", "m_iMaxHealth", std::int32_t); + NETVAR(m_isIncapacitated, "CTerrorPlayer", "m_isIncapacitated", std::int32_t); + NETVAR(m_healthBuffer, "CTerrorPlayer", "m_healthBuffer", std::float_t); +}; diff --git a/l4d2/game/source_engine/interfaces.cpp b/l4d2/game/source_engine/interfaces.cpp new file mode 100644 index 0000000..128220b --- /dev/null +++ b/l4d2/game/source_engine/interfaces.cpp @@ -0,0 +1,31 @@ +#include "../../framework.h" +#include "interfaces.h" +#include "../modules.h" + +namespace Game +{ + Interfaces_t::Interfaces_t() + { + #ifdef DUMP_SE + auto dump = [&]( const std::string& name, void* list ) + { + Hikari::Logger->Info( std::format( "Dumping {} interfaces", name ) ); + DumpInterfaces( list ); + }; + + dump( "client.dll", Modules::Client->GetInterfaceList() ); + dump( "engine.dll", Modules::Engine->GetInterfaceList() ); + dump( "materialsystem.dll", Modules::MaterialSystem->GetInterfaceList() ); + dump( "studiorender.dll", Modules::StudioRender->GetInterfaceList() ); + #endif + + Engine = GetInterface( Modules::Engine->GetInterfaceList(), "VEngineClient0" ); + Client = GetInterface( Modules::Client->GetInterfaceList(), "VClient0" ); + EntityList = GetInterface( Modules::Client->GetInterfaceList(), "VClientEntityList" ); + Surface = GetInterface( Modules::MatSurface->GetInterfaceList(), "VGUI_Surface0" ); + ClientMode = Modules::Client->FindPattern("E8 ? ? ? ? 8B 10 8B C8 8B 52 34").Relative().Cast()(); + CInput = Modules::Client->FindPattern("8B 0D ? ? ? ? 8B 01 8B 40 0C 52 8B 55 08").Offset(2).Dereference(2).Cast(); + GlobalVars = **(CGlobalVars_t***)((*(std::uintptr_t**)Client)[10] + 0x11); + + } +} diff --git a/l4d2/game/source_engine/interfaces.h b/l4d2/game/source_engine/interfaces.h new file mode 100644 index 0000000..8d8104f --- /dev/null +++ b/l4d2/game/source_engine/interfaces.h @@ -0,0 +1,88 @@ +#pragma once + +#include "classes/bitflags.h" +#include "classes/color.h" +#include "datatypes/checksum_crc.h" +#include "datatypes/checksum_md5.h" +#include "datatypes/angle.h" +#include "datatypes/vectors.h" +#include "datatypes/matrix.h" +#include "datatypes/usercmd.h" +#include "datatypes/datamap.h" + + +#include "interfaces/engine_client.h" +#include "interfaces/base_client.h" +#include "interfaces/client_entity_list.h" +#include "interfaces/surface.h" +#include "interfaces/globalvars.h" +#include "interfaces/input.h" +#include "interfaces/clientmode.h" + + +namespace Game +{ + struct Interfaces_t + { + Interfaces_t(); + + IEngineClient_t* Engine; + IBaseClientDll_t* Client; + IClientEntityList_t* EntityList; + TerrorClientMode_t* ClientMode; + ISurface_t* Surface; + CGlobalVars_t* GlobalVars; + IInput_t* CInput; + + + private: + class CInterfaceRegister + { + using InstantiateInterfaceFn = void* (__cdecl*)(); + public: + InstantiateInterfaceFn pCreateFn; + const char* szName; + CInterfaceRegister* pNext; + }; + + template < typename T > + T* GetInterface( void* intertface_list, std::string_view interface_name ); + + void DumpInterfaces( void* interface_list ) const; + }; + + template < typename T > + T* Interfaces_t::GetInterface( void* interface_list, std::string_view interface_name ) + { + if ( !interface_list ) + throw std::runtime_error( ( "Invalid interface list" ) ); + + if ( interface_name.empty() ) + throw std::runtime_error( ( "Empty interface name" ) ); + + for ( auto i = static_cast( interface_list ); i != nullptr; i = i->pNext ) + { + if ( std::string_view( i->szName ).compare( 0U, interface_name.length(), interface_name ) == 0 && std::atoi( i->szName + interface_name.length() ) > 0 || interface_name.compare( i->szName ) == 0 ) + { + auto address = i->pCreateFn(); + Hikari::Logger->Success( std::format( ( "Found interface {} ==> {:#09X}" ), i->szName, reinterpret_cast( address ) ) ); + return static_cast( address ); + } + } + + throw std::runtime_error( std::format( ( "Failed to get interface {}" ), interface_name ) ); + } + + inline void Interfaces_t::DumpInterfaces( void* interface_list ) const + { + if ( !interface_list ) + throw std::runtime_error( ( "Invalid interface list" ) ); + + for ( auto i = static_cast( interface_list ); i != nullptr; i = i->pNext ) + { + Hikari::Logger->Success( std::format( "Found interface: {}", i->szName ) ); + } + } + + inline std::unique_ptr Interfaces; +} diff --git a/l4d2/game/source_engine/interfaces/base_client.h b/l4d2/game/source_engine/interfaces/base_client.h new file mode 100644 index 0000000..ae7c8ec --- /dev/null +++ b/l4d2/game/source_engine/interfaces/base_client.h @@ -0,0 +1,385 @@ +#pragma once + +struct RecvProxyData; +struct RecvTable; +struct RecvProp; + +using RecvVarProxy_t = void(__cdecl*)(const RecvProxyData*, void*, void*); + +struct DVariant +{ + union + { + float m_Float; + long m_Int; + char* m_pString; + void* m_pData; + float m_Vector[3]; + __int64 m_Int64; + }; + + int m_Type; +}; + +struct RecvProxyData +{ + const RecvProp* m_pRecvProp; + DVariant m_Value; + int m_iElement; + int m_ObjectID; +}; + +struct RecvTable +{ + RecvProp* m_pProps; + int m_nProps; + void* m_pDecoder; + char* m_pNetTableName; + bool m_bInitialized; + bool m_bInMainList; +}; + +struct RecvProp +{ + char* m_pVarName; + int m_RecvType; + int m_Flags; + int m_StringBufferSize; + bool m_bInsideArray; + const void* m_pExtraData; + RecvProp* m_pArrayProp; + void* m_ArrayLengthProxy; + void* m_ProxyFn; + void* m_DataTableProxyFn; + RecvTable* m_pDataTable; + int m_Offset; + int m_ElementStride; + int m_nElements; + const char* m_pParentArrayPropName; +}; + +using CreateClientClass_t = void* (*)(int entnum, int serialNum); +using CreateEvent_t = void* (*)(); + +class ClientClass_t +{ +public: + void* m_pCreateFn; + void* m_pCreateEventFn; + const char* m_pNetworkName; + RecvTable* m_pRecvTable; + ClientClass_t* m_pNext; + int m_ClassID; +}; + +enum ClassID +{ + CFlare = 0, + CAI_BaseNPC = 0, + CFuncMonitor = 0, + Boomer = 0, + CPropJeep = 0, + CPlantedC4 = 0, + CWeaponCycler = 0, + CWeaponCubemap = 0, + CPropVehicleChoreoGeneric = 0, + CTeamTrainWatcher = 0, + CAssaultRifle, + CAutoShotgun, + CBaseAbility, + CBaseAnimating, + CBaseAnimatingOverlay, + CBaseAutoShotgun, + CBaseBackpackItem, + CBaseBeltItem, + CBaseButton, + CBaseCombatCharacter, + CBaseCombatWeapon, + CBaseCSGrenade, + CBaseCSGrenadeProjectile, + CBaseDoor, + CBaseEntity, + CBaseFlex, + CBaseGrenade, + CBaseMountedWeapon, + CBaseParticleEntity, + CBasePlayer, + CBasePropDoor, + CBaseRifle, + CBaseShotgun, + CBaseSniperRifle, + CBaseTeamObjectiveResource, + CBaseTempEntity, + CBaseToggle, + CBaseTrigger, + CBaseUpgradeItem, + CBaseViewModel, + CBeam, + CBeamSpotlight, + CBoneFollower, + CBoomerClaw, + CBreakable, + CBreakableProp, + CBreakableSurface, + CButtonTimed, + CChainsaw, + CCharge, + CChargerClaw, + CClaw, + CClientPath, + CColaBottles, + CColorCorrection, + CColorCorrectionVolume, + CCSGameRulesProxy, + CCSPlayer, + CCSRagdoll, + CCSTeam, + CDynamicLight, + CDynamicProp, + CEmbers, + CEntityDissolve, + CEntityFlame, + CEntityParticleTrail, + CEnvDetailController, + CEnvDOFController, + CEnvParticleScript, + CEnvPhysicsBlocker, + CEnvProjectedTexture, + CEnvQuadraticBeam, + CEnvScreenEffect, + CEnvScreenOverlay, + CEnvTonemapController, + CEnvWeaponFire, + CEnvWind, + CFinaleTrigger, + CFireCrackerBlast, + CFireSmoke, + CFireTrail, + CFireworkCrate, + CFirstAidKit, + CFish, + CFogController, + CFunc_Dust, + CFunc_LOD, + CFuncAreaPortalWindow, + CFuncBrush, + CFuncConveyor, + CFuncElevator, + CFuncLadder, + CFuncMoveLinear, + CFuncOccluder, + CFuncPlayerGhostInfectedClip, + CFuncPlayerInfectedClip, + CFuncRagdollFader, + CFuncReflectiveGlass, + CFuncRotating, + CFuncSimpleLadder, + CFuncSmokeVolume, + CFuncTrackTrain, + CGameRulesProxy, + CGasCan, + CGnome, + CGrenadeLauncher, + CGrenadeLauncher_Projectile, + CHandleTest, + Charger, + CHunterClaw, + CInferno, + CInfoLadderDismount, + CInfoOverlayAccessor, + CInsectSwarm, + CItem_Adrenaline, + CItem_VomitJar, + CItemAmmoPack, + CItemBaseUpgradePack, + CItemDefibrillator, + CItemUpgradePackExplosive, + CItemUpgradePackIncendiary, + CJockeyClaw, + CLeap, + CLightGlow, + CLunge, + CMagnumPistol, + CMaterialModifyControl, + CMolotov, + CMolotovProjectile, + COxygenTank, + CPainPills, + CParticleFire, + CParticlePerformanceMonitor, + CParticleSystem, + CPhysBox, + CPhysBoxMultiplayer, + CPhysicsProp, + CPhysMagnet, + CPipeBomb, + CPipeBombProjectile, + CPistol, + CPlasma, + CPlayerResource, + CPointCommentaryNode, + CPointPropUseTarget, + CPointScriptUseTarget, + CPoseController, + CPostProcessController, + CPrecipitation, + CPrecipitationBlocker, + CPredictedViewModel, + CPropaneTank, + CPropDoorRotatingCheckpoint, + CPropHealthCabinet, + CPropMinigun, + CPropMountedGun, + CPropVehicleDriveable, + CPumpShotgun, + CRagdollManager, + CRagdollProp, + CRagdollPropAttached, + CRifle_AK47, + CRifle_Desert, + CRifle_M60, + CRifle_SG552, + CRopeKeyframe, + CScavengeProgressDisplay, + CSceneEntity, + CScriptBaseButton, + CSelfDestruct, + CShadowControl, + CShotgun_Chrome, + CShotgun_SPAS, + CSlideshowDisplay, + CSMG_MP5, + CSMG_Silenced, + CSmokerClaw, + CSmokeStack, + CSniper_AWP, + CSniper_Military, + CSniper_Scout, + CSniperRifle, + CSoundMixLayer, + CSpitAbility, + CSpitterClaw, + CSpitterProjectile, + CSpotlightEnd, + CSprite, + CSpriteOriented, + CSpriteTrail, + CSteamJet, + CSubMachinegun, + CSun, + CSurvivorDeathModel, + CSurvivorPosition, + CSurvivorRescue, + CTankClaw, + CTeam, + CTeamplayRoundBasedRulesProxy, + CTEArmorRicochet, + CTEBaseBeam, + CTEBeamEntPoint, + CTEBeamEnts, + CTEBeamFollow, + CTEBeamLaser, + CTEBeamPoints, + CTEBeamRing, + CTEBeamRingPoint, + CTEBeamSpline, + CTEBloodSprite, + CTEBloodStream, + CTEBreakModel, + CTEBSPDecal, + CTEBubbles, + CTEBubbleTrail, + CTEBullets, + CTEClientProjectile, + CTEDecal, + CTEDust, + CTEDynamicLight, + CTEEffectDispatch, + CTEEnergySplash, + CTEExplosion, + CTEFizz, + CTEFootprintDecal, + CTEFoundryHelpers, + CTEGaussExplosion, + CTEGlowSprite, + CTEImpact, + CTEKillPlayerAttachments, + CTELargeFunnel, + CTEMetalSparks, + CTEMuzzleFlash, + CTEParticleSystem, + CTEPhysicsProp, + CTEPlayerAnimEvent, + CTEPlayerDecal, + CTEProjectedDecal, + CTerrorGameRulesProxy, + CTerrorGun, + CTerrorMeleeWeapon, + CTerrorPlayer, + CTerrorPlayerResource, + CTerrorViewModel, + CTerrorWeapon, + CTEShatterSurface, + CTEShowLine, + CTESmoke, + CTESparks, + CTESprite, + CTESpriteSpray, + CTest_ProxyToggle_Networkable, + CTestTraceline, + CTEWorldDecal, + CThrow, + CTongue, + CTransitioningPlayer, + CTriggerPlayerMovement, + CTutorLesson, + CVGuiScreen, + CVomit, + CVomitJarProjectile, + CVoteController, + CWaterBullet, + CWaterLODControl, + CWeaponAmmoSpawn, + CWeaponCSBase, + CWeaponCSBaseGun, + CWeaponScavengeItemSpawn, + CWeaponSpawn, + CWorld, + DustTrail, + Hunter, + Infected, + Jockey, + MovieExplosion, + NextBotCombatCharacter, + ParticleSmokeGrenade, + RocketTrail, + Smoker, + SmokeTrail, + Spitter, + SporeExplosion, + SporeTrail, + SurvivorBot, + Tank, + Witch, +}; + + +enum ClientFrameStage_t +{ + FRAME_UNDEFINED = -1, + FRAME_START, + FRAME_NET_UPDATE_START, + FRAME_NET_UPDATE_POSTDATAUPDATE_START, + FRAME_NET_UPDATE_POSTDATAUPDATE_END, + FRAME_NET_UPDATE_END, + FRAME_RENDER_START, + FRAME_RENDER_END +}; + +struct IBaseClientDll_t +{ + auto GetAllClasses() + { + return Hikari::Memory->VirtualCall(this); + } +}; diff --git a/l4d2/game/source_engine/interfaces/client_entity_list.h b/l4d2/game/source_engine/interfaces/client_entity_list.h new file mode 100644 index 0000000..f1a432c --- /dev/null +++ b/l4d2/game/source_engine/interfaces/client_entity_list.h @@ -0,0 +1,21 @@ +#pragma once + +struct IClientEntityList_t +{ + template < class T > + T Get(int index) + { + return Hikari::Memory->VirtualCall(this, index); + } + + template < class T > + T GetFromHandle(int index) + { + return Hikari::Memory->VirtualCall(this, index); + } + + auto GetHighestEntityIndex() + { + return Hikari::Memory->VirtualCall(this); + } +}; diff --git a/l4d2/game/source_engine/interfaces/clientmode.h b/l4d2/game/source_engine/interfaces/clientmode.h new file mode 100644 index 0000000..cd82ef8 --- /dev/null +++ b/l4d2/game/source_engine/interfaces/clientmode.h @@ -0,0 +1,7 @@ +#pragma once + +class TerrorClientMode_t +{ +public: + +}; \ No newline at end of file diff --git a/l4d2/game/source_engine/interfaces/engine_client.h b/l4d2/game/source_engine/interfaces/engine_client.h new file mode 100644 index 0000000..b71a564 --- /dev/null +++ b/l4d2/game/source_engine/interfaces/engine_client.h @@ -0,0 +1,73 @@ +#pragma once + +struct matrix3x4_t; +class Angle_t; + +#define MAX_PLAYER_NAME_LENGTH 32 +#define SIGNED_GUID_LEN 32 + +struct PlayerInfo_t +{ + PAD(0x08); + char name[MAX_PLAYER_NAME_LENGTH]; + int userID; + char guid[SIGNED_GUID_LEN + 1]; + uint32_t friendsID; + char friendsName[MAX_PLAYER_NAME_LENGTH]; + bool fakeplayer; + PAD(0x1B); +}; + +struct IEngineClient_t +{ + auto GetScreenSize( int& w, int& h ) + { + return Hikari::Memory->VirtualCall( this, std::ref( w ), std::ref( h ) ); + } + + auto ServerCmd(const char* Command, bool ForceReliable) + { + return Hikari::Memory->VirtualCall(this, Command, ForceReliable); + } + + auto GetPlayerInfo( int index, PlayerInfo_t* info ) + { + return Hikari::Memory->VirtualCall( this, index, info ); + } + + auto Con_IsVisible() + { + return Hikari::Memory->VirtualCall(this); + } + + auto GetLocalPlayer() + { + return Hikari::Memory->VirtualCall( this ); + } + + auto GetViewAngles( const Angle_t& angle ) + { + return Hikari::Memory->VirtualCall( this, std::ref( angle ) ); + } + + auto SetViewAngles( const Angle_t& angle ) + { + return Hikari::Memory->VirtualCall( this, std::ref( angle ) ); + } + + auto IsInGame() + { + return Hikari::Memory->VirtualCall( this ); + } + + const auto& WorldToScreenMatrix() + { + return Hikari::Memory->VirtualCall(this); + } + + auto ClientCmd_Unrestricted(const char* szCmdString) + { + return Hikari::Memory->VirtualCall(this, szCmdString); + } + +}; diff --git a/l4d2/game/source_engine/interfaces/globalvars.h b/l4d2/game/source_engine/interfaces/globalvars.h new file mode 100644 index 0000000..6060881 --- /dev/null +++ b/l4d2/game/source_engine/interfaces/globalvars.h @@ -0,0 +1,17 @@ +#pragma once + +struct CGlobalVars_t +{ + float realtime; + int framecount; + float absoluteframetime; + float curtime; + float frametime; + int max_clients; + int tickcount; + float interval_per_tick; + float interpolation_amount; + int sim_ticks_this_frame; + int network_protocol; + void* pSaveData; +}; diff --git a/l4d2/game/source_engine/interfaces/input.h b/l4d2/game/source_engine/interfaces/input.h new file mode 100644 index 0000000..976827e --- /dev/null +++ b/l4d2/game/source_engine/interfaces/input.h @@ -0,0 +1,19 @@ +#pragma once + +class CVerifiedUserCmd +{ +public: + CUserCmd m_cmd; + CRC32_t m_crc; +}; + +struct IInput_t +{ + PAD(0xB1); + bool m_fCameraInThirdPerson; //0x00B1 + PAD(0x2); + Angle_t ThirdpersonCameraAngle; //0x00B4 + PAD(0x2C); + CUserCmd* m_pCommands; //0x00EC + CVerifiedUserCmd* m_pVerifiedCommands; //0x00F0 +}; \ No newline at end of file diff --git a/l4d2/game/source_engine/interfaces/surface.h b/l4d2/game/source_engine/interfaces/surface.h new file mode 100644 index 0000000..f5a73ab --- /dev/null +++ b/l4d2/game/source_engine/interfaces/surface.h @@ -0,0 +1,84 @@ +#pragma once + +typedef unsigned long HFont; + +enum FontFlag_t +{ + FONTFLAG_NONE, + FONTFLAG_ITALIC = 0x001, + FONTFLAG_UNDERLINE = 0x002, + FONTFLAG_STRIKEOUT = 0x004, + FONTFLAG_SYMBOL = 0x008, + FONTFLAG_ANTIALIAS = 0x010, + FONTFLAG_GAUSSIANBLUR = 0x020, + FONTFLAG_ROTARY = 0x040, + FONTFLAG_DROPSHADOW = 0x080, + FONTFLAG_ADDITIVE = 0x100, + FONTFLAG_OUTLINE = 0x200, + FONTFLAG_CUSTOM = 0x400, + FONTFLAG_BITMAP = 0x800, +}; + + +struct ISurface_t +{ + auto DrawSetColor( int r, int g, int b, int a = 255 ) + { + return Hikari::Memory->VirtualCall( this, r, g, b, a ); + } + + auto DrawFilledRect( int x, int y, int w, int h ) + { + return Hikari::Memory->VirtualCall( this, x, y, w, h ); + } + + auto DrawOutlinedRect(int x, int y, int w, int h) + { + return Hikari::Memory->VirtualCall(this, x, y, w, h); + } + + auto DrawLine(int x, int y, int x2, int y2) + { + return Hikari::Memory->VirtualCall(this, x, y, x2, y2); + } + + auto DrawSetTextFont(HFont Font) + { + return Hikari::Memory->VirtualCall(this, Font); + } + + auto DrawSetTextColor(int r, int g, int b, int a) + { + return Hikari::Memory->VirtualCall(this, r, g, b, a); + } + + auto DrawSetTextPos(int x, int y) + { + return Hikari::Memory->VirtualCall(this, x, y ); + } + + auto DrawPrintText(const wchar_t* text, int text_length) + { + return Hikari::Memory->VirtualCall(this, text, text_length, 0); + } + + auto CreatFont() + { + return Hikari::Memory->VirtualCall(this); + } + + auto SetFontGlyphSet(unsigned long font, const char* name, int size, int weight, int blur, int scanlines, int flags, int min_range, int max_range) + { + return Hikari::Memory->VirtualCall(this, font, name, size, weight, blur, scanlines, flags, min_range, max_range); + } + + auto GetTextSize(unsigned long font, const wchar_t* text, int& width, int& height) + { + return Hikari::Memory->VirtualCall(this, font, text, std::ref(width), std::ref(height)); + } + + auto DrawColoredTexted( unsigned long font, int x, int y, int r, int g, int b, int a, const char* fmt, ... ) + { + return Hikari::Memory->VirtualCallCdecl( this, font, x, y, r, g, b, a, fmt ); + } +}; diff --git a/l4d2/game/source_engine/netvars.cpp b/l4d2/game/source_engine/netvars.cpp new file mode 100644 index 0000000..84e19f5 --- /dev/null +++ b/l4d2/game/source_engine/netvars.cpp @@ -0,0 +1,71 @@ +#include "../../framework.h" +#include "../game.h" +#include "netvars.h" + +namespace Game +{ + +#define DUMP_NETVARS + +#ifdef DUMP_NETVARS +#define IF_DUMPING(...) __VA_ARGS__ +#else +#define IF_DUMPING(...) +#endif + + IF_DUMPING(static FILE* s_fp;) + + const char* prop_type[7] = + { + "int", + "float", + "vec3", + "vec2", + "char *", + "array", + "table" + }; + + NetVar_t::NetVar_t() + { + IF_DUMPING(fopen_s(&s_fp, "netvar_dump.txt", "w");) + for (auto clazz = Interfaces->Client->GetAllClasses(); clazz; clazz = clazz->m_pNext) + if (clazz->m_pRecvTable) + DUMP_NETVAR(clazz->m_pNetworkName, clazz->m_pRecvTable, 0); + IF_DUMPING(fclose(s_fp);) + } + + auto NetVar_t::DUMP_NETVAR(const char* base_class, RecvTable* table, const std::uint16_t offset) -> void + { + for (auto i = 0; i < table->m_nProps; ++i) + { + const auto prop_ptr = &table->m_pProps[i]; + + if (!prop_ptr || isdigit(prop_ptr->m_pVarName[0])) + continue; + + if (strcmp(prop_ptr->m_pVarName, "baseclass") == 0) + continue; + + if (prop_ptr->m_pDataTable != nullptr) + DUMP_NETVAR(base_class, prop_ptr->m_pDataTable, std::uint16_t(offset + prop_ptr->m_Offset)); + + char hash_name[256]; + + strcpy_s(hash_name, base_class); + strcat_s(hash_name, "->"); + strcat_s(hash_name, prop_ptr->m_pVarName); + + const auto hash = fnv::hash_runtime(hash_name); + const auto total_offset = std::uint16_t(offset + prop_ptr->m_Offset); + + IF_DUMPING(fprintf(s_fp, " [%s]\t%s->%s = 0x%04X\n", prop_type[prop_ptr->m_RecvType], base_class, prop_ptr->m_pVarName, total_offset);) + + m_props[hash] = + { + prop_ptr, + total_offset + }; + } + } +} diff --git a/l4d2/game/source_engine/netvars.h b/l4d2/game/source_engine/netvars.h new file mode 100644 index 0000000..b694c89 --- /dev/null +++ b/l4d2/game/source_engine/netvars.h @@ -0,0 +1,89 @@ +#pragma once + +#include + + +namespace Game +{ + class NetVar_t + { + private: + struct stored_data + { + RecvProp* prop_ptr; + std::uint16_t class_relative_offset; + }; + + public: + // Return a const instance, others shouldnt modify this. + static auto Get() -> const NetVar_t& + { + static NetVar_t instance; + return instance; + } + + auto GetOffset(const fnv::hash hash) const -> std::uint16_t + { + return m_props.at(hash).class_relative_offset; + } + + auto GetProP(const fnv::hash hash) const -> RecvProp* + { + return m_props.at(hash).prop_ptr; + } + + // Prevent instruction cache pollution caused by automatic + // inlining of `get` and get_offset every netvar usage when + // there are a lot of netvars + __declspec(noinline) static auto GetOffsetByHASH(const fnv::hash hash) -> std::uint16_t + { + return Get().GetOffset(hash); + } + + template + static auto ToAddress(void* ptr) -> std::uintptr_t + { + static auto offset = std::uint16_t(0); + if (!offset) + offset = GetOffsetByHASH(Hash); + return std::uintptr_t(ptr) + offset; + } + + private: + NetVar_t(); + auto DUMP_NETVAR(const char* base_class, RecvTable* table, std::uint16_t offset) -> void; + + private: + std::unordered_map m_props; + }; +} + + +#define PNETVAR_OFFSET(funcname, class_name, var_name, offset, ...) \ +auto funcname() -> std::add_pointer_t<__VA_ARGS__> \ +{ \ + constexpr auto hash = fnv::hash_constexpr(class_name "->" var_name); \ + const auto addr = Game::NetVar_t::ToAddress(this); \ + return reinterpret_cast>(addr); \ +} + +#define PNETVAR(funcname, class_name, var_name, ...) \ + PNETVAR_OFFSET(funcname, class_name, var_name, 0, __VA_ARGS__) + +#define NETVAR_OFFSET(funcname, class_name, var_name, offset, ...) \ +auto funcname() -> std::add_lvalue_reference_t<__VA_ARGS__> \ +{ \ + constexpr auto hash = fnv::hash_constexpr(class_name "->" var_name); \ + const auto addr = Game::NetVar_t::ToAddress(this + offset); \ + return *reinterpret_cast>(addr); \ +} +#define NETVAR(funcname, class_name, var_name, ...) \ + NETVAR_OFFSET(funcname, class_name, var_name, 0, __VA_ARGS__) + +#define NETPROP(funcname, class_name, var_name) \ +static auto funcname() -> RecvProp* \ +{ \ + constexpr auto hash = fnv::hash_constexpr(class_name "->" var_name); \ + static auto prop_ptr = Game::NetVar_t::Get().GetProP(hash); \ + return prop_ptr; \ +} \ No newline at end of file diff --git a/l4d2/game/source_engine/source_engine.h b/l4d2/game/source_engine/source_engine.h new file mode 100644 index 0000000..33b7105 --- /dev/null +++ b/l4d2/game/source_engine/source_engine.h @@ -0,0 +1,6 @@ +#pragma once + + +#include "interfaces.h" +#include "netvars.h" +#include "entity.h" \ No newline at end of file diff --git a/l4d2/l4d2.vcxproj b/l4d2/l4d2.vcxproj new file mode 100644 index 0000000..0cd51ad --- /dev/null +++ b/l4d2/l4d2.vcxproj @@ -0,0 +1,213 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16.0 + Win32Proj + {fc0a0ae9-d014-4e51-af01-35c39e5f272e} + l4d2 + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + TurnOffAllWarnings + true + WIN32;_DEBUG;L4D2_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpplatest + stdc17 + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;L4D2_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + Level3 + true + _DEBUG;L4D2_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;L4D2_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + + \ No newline at end of file diff --git a/l4d2/l4d2.vcxproj.filters b/l4d2/l4d2.vcxproj.filters new file mode 100644 index 0000000..f27de50 --- /dev/null +++ b/l4d2/l4d2.vcxproj.filters @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/l4d2/thirdparty/minhook/MinHook.h b/l4d2/thirdparty/minhook/MinHook.h new file mode 100644 index 0000000..492d83f --- /dev/null +++ b/l4d2/thirdparty/minhook/MinHook.h @@ -0,0 +1,185 @@ +/* + * 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. + // pszProcName [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. + // pszProcName [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/l4d2/thirdparty/minhook/buffer.c b/l4d2/thirdparty/minhook/buffer.c new file mode 100644 index 0000000..55412b0 --- /dev/null +++ b/l4d2/thirdparty/minhook/buffer.c @@ -0,0 +1,312 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "buffer.h" + +// Size of each memory block. (= page size of VirtualAlloc) +#define MEMORY_BLOCK_SIZE 0x1000 + +// Max range for seeking a memory block. (= 1024MB) +#define MAX_MEMORY_RANGE 0x40000000 + +// Memory protection flags to check the executable address. +#define PAGE_EXECUTE_FLAGS \ + (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) + +// Memory slot. +typedef struct _MEMORY_SLOT +{ + union + { + struct _MEMORY_SLOT *pNext; + UINT8 buffer[MEMORY_SLOT_SIZE]; + }; +} MEMORY_SLOT, *PMEMORY_SLOT; + +// Memory block info. Placed at the head of each block. +typedef struct _MEMORY_BLOCK +{ + struct _MEMORY_BLOCK *pNext; + PMEMORY_SLOT pFree; // First element of the free slot list. + UINT usedCount; +} MEMORY_BLOCK, *PMEMORY_BLOCK; + +//------------------------------------------------------------------------- +// Global Variables: +//------------------------------------------------------------------------- + +// First element of the memory block list. +PMEMORY_BLOCK g_pMemoryBlocks; + +//------------------------------------------------------------------------- +VOID InitializeBuffer(VOID) +{ + // Nothing to do for now. +} + +//------------------------------------------------------------------------- +VOID UninitializeBuffer(VOID) +{ + PMEMORY_BLOCK pBlock = g_pMemoryBlocks; + g_pMemoryBlocks = NULL; + + while (pBlock) + { + PMEMORY_BLOCK pNext = pBlock->pNext; + VirtualFree(pBlock, 0, MEM_RELEASE); + pBlock = pNext; + } +} + +//------------------------------------------------------------------------- +#if defined(_M_X64) || defined(__x86_64__) +static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity) +{ + ULONG_PTR tryAddr = (ULONG_PTR)pAddress; + + // Round down to the allocation granularity. + tryAddr -= tryAddr % dwAllocationGranularity; + + // Start from the previous allocation granularity multiply. + tryAddr -= dwAllocationGranularity; + + while (tryAddr >= (ULONG_PTR)pMinAddr) + { + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) + break; + + if (mbi.State == MEM_FREE) + return (LPVOID)tryAddr; + + if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity) + break; + + tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity; + } + + return NULL; +} +#endif + +//------------------------------------------------------------------------- +#if defined(_M_X64) || defined(__x86_64__) +static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity) +{ + ULONG_PTR tryAddr = (ULONG_PTR)pAddress; + + // Round down to the allocation granularity. + tryAddr -= tryAddr % dwAllocationGranularity; + + // Start from the next allocation granularity multiply. + tryAddr += dwAllocationGranularity; + + while (tryAddr <= (ULONG_PTR)pMaxAddr) + { + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) + break; + + if (mbi.State == MEM_FREE) + return (LPVOID)tryAddr; + + tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; + + // Round up to the next allocation granularity. + tryAddr += dwAllocationGranularity - 1; + tryAddr -= tryAddr % dwAllocationGranularity; + } + + return NULL; +} +#endif + +//------------------------------------------------------------------------- +static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin) +{ + PMEMORY_BLOCK pBlock; +#if defined(_M_X64) || defined(__x86_64__) + ULONG_PTR minAddr; + ULONG_PTR maxAddr; + + SYSTEM_INFO si; + GetSystemInfo(&si); + minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; + maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; + + // pOrigin ± 512MB + if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE) + minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE; + + if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE) + maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE; + + // Make room for MEMORY_BLOCK_SIZE bytes. + maxAddr -= MEMORY_BLOCK_SIZE - 1; +#endif + + // Look the registered blocks for a reachable one. + for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext) + { +#if defined(_M_X64) || defined(__x86_64__) + // Ignore the blocks too far. + if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr) + continue; +#endif + // The block has at least one unused slot. + if (pBlock->pFree != NULL) + return pBlock; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Alloc a new block above if not found. + { + LPVOID pAlloc = pOrigin; + while ((ULONG_PTR)pAlloc >= minAddr) + { + pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity); + if (pAlloc == NULL) + break; + + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + } + + // Alloc a new block below if not found. + if (pBlock == NULL) + { + LPVOID pAlloc = pOrigin; + while ((ULONG_PTR)pAlloc <= maxAddr) + { + pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity); + if (pAlloc == NULL) + break; + + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + } +#else + // In x86 mode, a memory block can be placed anywhere. + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); +#endif + + if (pBlock != NULL) + { + // Build a linked list of all the slots. + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1; + pBlock->pFree = NULL; + pBlock->usedCount = 0; + do + { + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pSlot++; + } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE); + + pBlock->pNext = g_pMemoryBlocks; + g_pMemoryBlocks = pBlock; + } + + return pBlock; +} + +//------------------------------------------------------------------------- +LPVOID AllocateBuffer(LPVOID pOrigin) +{ + PMEMORY_SLOT pSlot; + PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin); + if (pBlock == NULL) + return NULL; + + // Remove an unused slot from the list. + pSlot = pBlock->pFree; + pBlock->pFree = pSlot->pNext; + pBlock->usedCount++; +#ifdef _DEBUG + // Fill the slot with INT3 for debugging. + memset(pSlot, 0xCC, sizeof(MEMORY_SLOT)); +#endif + return pSlot; +} + +//------------------------------------------------------------------------- +VOID FreeBuffer(LPVOID pBuffer) +{ + PMEMORY_BLOCK pBlock = g_pMemoryBlocks; + PMEMORY_BLOCK pPrev = NULL; + ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE; + + while (pBlock != NULL) + { + if ((ULONG_PTR)pBlock == pTargetBlock) + { + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer; +#ifdef _DEBUG + // Clear the released slot for debugging. + memset(pSlot, 0x00, sizeof(MEMORY_SLOT)); +#endif + // Restore the released slot to the list. + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pBlock->usedCount--; + + // Free if unused. + if (pBlock->usedCount == 0) + { + if (pPrev) + pPrev->pNext = pBlock->pNext; + else + g_pMemoryBlocks = pBlock->pNext; + + VirtualFree(pBlock, 0, MEM_RELEASE); + } + + break; + } + + pPrev = pBlock; + pBlock = pBlock->pNext; + } +} + +//------------------------------------------------------------------------- +BOOL IsExecutableAddress(LPVOID pAddress) +{ + MEMORY_BASIC_INFORMATION mi; + VirtualQuery(pAddress, &mi, sizeof(mi)); + + return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS)); +} diff --git a/l4d2/thirdparty/minhook/buffer.h b/l4d2/thirdparty/minhook/buffer.h new file mode 100644 index 0000000..204d551 --- /dev/null +++ b/l4d2/thirdparty/minhook/buffer.h @@ -0,0 +1,42 @@ +/* + * 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 + +// Size of each memory slot. +#if defined(_M_X64) || defined(__x86_64__) + #define MEMORY_SLOT_SIZE 64 +#else + #define MEMORY_SLOT_SIZE 32 +#endif + +VOID InitializeBuffer(VOID); +VOID UninitializeBuffer(VOID); +LPVOID AllocateBuffer(LPVOID pOrigin); +VOID FreeBuffer(LPVOID pBuffer); +BOOL IsExecutableAddress(LPVOID pAddress); diff --git a/l4d2/thirdparty/minhook/hde/hde32.c b/l4d2/thirdparty/minhook/hde/hde32.c new file mode 100644 index 0000000..eb6af9b --- /dev/null +++ b/l4d2/thirdparty/minhook/hde/hde32.c @@ -0,0 +1,324 @@ +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#if defined(_M_IX86) || defined(__i386__) + +#include +#include "hde32.h" +#include "table32.h" + +unsigned int hde32_disasm(const void *code, hde32s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0; + + memset(hs, 0, sizeof(hde32s)); + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde32_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde32_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde32_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde32_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde32_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if ((*ht++ & pref) && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + break; + } + + if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + break; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (pref & PRE_66) { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } else { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } + } + + if (cflags & C_IMM16) { + if (hs->flags & F_IMM32) { + hs->flags |= F_IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else if (hs->flags & F_IMM16) { + hs->flags |= F_2IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + } + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#endif // defined(_M_IX86) || defined(__i386__) diff --git a/l4d2/thirdparty/minhook/hde/hde32.h b/l4d2/thirdparty/minhook/hde/hde32.h new file mode 100644 index 0000000..1112450 --- /dev/null +++ b/l4d2/thirdparty/minhook/hde/hde32.h @@ -0,0 +1,105 @@ +/* + * Hacker Disassembler Engine 32 + * Copyright (c) 2006-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde32.h: C/C++ header file + * + */ + +#ifndef _HDE32_H_ +#define _HDE32_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include "pstdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_DISP8 0x00000020 +#define F_DISP16 0x00000040 +#define F_DISP32 0x00000080 +#define F_RELATIVE 0x00000100 +#define F_2IMM16 0x00000800 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_ANY 0x3f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde32s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde32_disasm(const void *code, hde32s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE32_H_ */ diff --git a/l4d2/thirdparty/minhook/hde/hde64.c b/l4d2/thirdparty/minhook/hde/hde64.c new file mode 100644 index 0000000..55a702e --- /dev/null +++ b/l4d2/thirdparty/minhook/hde/hde64.c @@ -0,0 +1,333 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#if defined(_M_X64) || defined(__x86_64__) + +#include +#include "hde64.h" +#include "table64.h" + +unsigned int hde64_disasm(const void *code, hde64s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; + uint8_t op64 = 0; + + memset(hs, 0, sizeof(hde64s)); + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (op64) { + hs->flags |= F_IMM64; + hs->imm.imm64 = *(uint64_t *)p; + p += 8; + } else if (!(pref & PRE_66)) { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else + goto imm16_ok; + } + + + if (cflags & C_IMM16) { + imm16_ok: + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#endif // defined(_M_X64) || defined(__x86_64__) diff --git a/l4d2/thirdparty/minhook/hde/hde64.h b/l4d2/thirdparty/minhook/hde/hde64.h new file mode 100644 index 0000000..ecbf4df --- /dev/null +++ b/l4d2/thirdparty/minhook/hde/hde64.h @@ -0,0 +1,112 @@ +/* + * Hacker Disassembler Engine 64 + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde64.h: C/C++ header file + * + */ + +#ifndef _HDE64_H_ +#define _HDE64_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include "pstdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE64_H_ */ diff --git a/l4d2/thirdparty/minhook/hde/pstdint.h b/l4d2/thirdparty/minhook/hde/pstdint.h new file mode 100644 index 0000000..84d82a0 --- /dev/null +++ b/l4d2/thirdparty/minhook/hde/pstdint.h @@ -0,0 +1,39 @@ +/* + * 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 AUTHOR "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 AUTHOR 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 + +#include + +// Integer types for HDE. +typedef INT8 int8_t; +typedef INT16 int16_t; +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT8 uint8_t; +typedef UINT16 uint16_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; diff --git a/l4d2/thirdparty/minhook/hde/table32.h b/l4d2/thirdparty/minhook/hde/table32.h new file mode 100644 index 0000000..7b3e12e --- /dev/null +++ b/l4d2/thirdparty/minhook/hde/table32.h @@ -0,0 +1,73 @@ +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xf1 +#define DELTA_FPU_MODRM 0xf8 +#define DELTA_PREFIXES 0x130 +#define DELTA_OP_LOCK_OK 0x1a1 +#define DELTA_OP2_LOCK_OK 0x1b9 +#define DELTA_OP_ONLY_MEM 0x1cb +#define DELTA_OP2_ONLY_MEM 0x1da + +unsigned char hde32_table[] = { + 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3, + 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f, + 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3, + 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa, + 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90, + 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d, + 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59, + 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59, + 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0, + 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01, + 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11, + 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8, + 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca, + 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff, + 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03, + 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00, + 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f, + 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a, + 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, + 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a, + 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06, + 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06, + 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08, + 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01, + 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba, + 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00, + 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00, + 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07, + 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf, + 0xe7,0x08,0x00,0xf0,0x02,0x00 +}; diff --git a/l4d2/thirdparty/minhook/hde/table64.h b/l4d2/thirdparty/minhook/hde/table64.h new file mode 100644 index 0000000..01d4541 --- /dev/null +++ b/l4d2/thirdparty/minhook/hde/table64.h @@ -0,0 +1,74 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xfd +#define DELTA_FPU_MODRM 0x104 +#define DELTA_PREFIXES 0x13c +#define DELTA_OP_LOCK_OK 0x1ae +#define DELTA_OP2_LOCK_OK 0x1c6 +#define DELTA_OP_ONLY_MEM 0x1d8 +#define DELTA_OP2_ONLY_MEM 0x1e7 + +unsigned char hde64_table[] = { + 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, + 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, + 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, + 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, + 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, + 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, + 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, + 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, + 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, + 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, + 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, + 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, + 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, + 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, + 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, + 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, + 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, + 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, + 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, + 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, + 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, + 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, + 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, + 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, + 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, + 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, + 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, + 0x00,0xf0,0x02,0x00 +}; diff --git a/l4d2/thirdparty/minhook/hook.c b/l4d2/thirdparty/minhook/hook.c new file mode 100644 index 0000000..abb2397 --- /dev/null +++ b/l4d2/thirdparty/minhook/hook.c @@ -0,0 +1,923 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "MinHook.h" +#include "buffer.h" +#include "trampoline.h" + +#ifndef ARRAYSIZE + #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +// Initial capacity of the HOOK_ENTRY buffer. +#define INITIAL_HOOK_CAPACITY 32 + +// Initial capacity of the thread IDs buffer. +#define INITIAL_THREAD_CAPACITY 128 + +// Special hook position values. +#define INVALID_HOOK_POS UINT_MAX +#define ALL_HOOKS_POS UINT_MAX + +// Freeze() action argument defines. +#define ACTION_DISABLE 0 +#define ACTION_ENABLE 1 +#define ACTION_APPLY_QUEUED 2 + +// Thread access rights for suspending/resuming threads. +#define THREAD_ACCESS \ + (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT) + +// Hook information. +typedef struct _HOOK_ENTRY +{ + LPVOID pTarget; // Address of the target function. + LPVOID pDetour; // Address of the detour or relay function. + LPVOID pTrampoline; // Address of the trampoline function. + UINT8 backup[8]; // Original prologue of the target function. + + UINT8 patchAbove : 1; // Uses the hot patch area. + UINT8 isEnabled : 1; // Enabled. + UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled. + + UINT nIP : 4; // Count of the instruction boundaries. + UINT8 oldIPs[8]; // Instruction boundaries of the target function. + UINT8 newIPs[8]; // Instruction boundaries of the trampoline function. +} HOOK_ENTRY, *PHOOK_ENTRY; + +// Suspended threads for Freeze()/Unfreeze(). +typedef struct _FROZEN_THREADS +{ + LPDWORD pItems; // Data heap + UINT capacity; // Size of allocated data heap, items + UINT size; // Actual number of data items +} FROZEN_THREADS, *PFROZEN_THREADS; + +//------------------------------------------------------------------------- +// Global Variables: +//------------------------------------------------------------------------- + +// Spin lock flag for EnterSpinLock()/LeaveSpinLock(). +volatile LONG g_isLocked = FALSE; + +// Private heap handle. If not NULL, this library is initialized. +HANDLE g_hHeap = NULL; + +// Hook entries. +struct +{ + PHOOK_ENTRY pItems; // Data heap + UINT capacity; // Size of allocated data heap, items + UINT size; // Actual number of data items +} g_hooks; + +//------------------------------------------------------------------------- +// Returns INVALID_HOOK_POS if not found. +static UINT FindHookEntry(LPVOID pTarget) +{ + UINT i; + for (i = 0; i < g_hooks.size; ++i) + { + if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget) + return i; + } + + return INVALID_HOOK_POS; +} + +//------------------------------------------------------------------------- +static PHOOK_ENTRY AddHookEntry() +{ + if (g_hooks.pItems == NULL) + { + g_hooks.capacity = INITIAL_HOOK_CAPACITY; + g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc( + g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY)); + if (g_hooks.pItems == NULL) + return NULL; + } + else if (g_hooks.size >= g_hooks.capacity) + { + PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( + g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY)); + if (p == NULL) + return NULL; + + g_hooks.capacity *= 2; + g_hooks.pItems = p; + } + + return &g_hooks.pItems[g_hooks.size++]; +} + +//------------------------------------------------------------------------- +static VOID DeleteHookEntry(UINT pos) +{ + if (pos < g_hooks.size - 1) + g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1]; + + g_hooks.size--; + + if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size) + { + PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( + g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY)); + if (p == NULL) + return; + + g_hooks.capacity /= 2; + g_hooks.pItems = p; + } +} + +//------------------------------------------------------------------------- +static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip) +{ + UINT i; + + if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL))) + return (DWORD_PTR)pHook->pTarget; + + for (i = 0; i < pHook->nIP; ++i) + { + if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i])) + return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Check relay function. + if (ip == (DWORD_PTR)pHook->pDetour) + return (DWORD_PTR)pHook->pTarget; +#endif + + return 0; +} + +//------------------------------------------------------------------------- +static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip) +{ + UINT i; + for (i = 0; i < pHook->nIP; ++i) + { + if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i])) + return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]; + } + + return 0; +} + +//------------------------------------------------------------------------- +static VOID ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action) +{ + // If the thread suspended in the overwritten area, + // move IP to the proper address. + + CONTEXT c; +#if defined(_M_X64) || defined(__x86_64__) + DWORD64 *pIP = &c.Rip; +#else + DWORD *pIP = &c.Eip; +#endif + UINT count; + + c.ContextFlags = CONTEXT_CONTROL; + if (!GetThreadContext(hThread, &c)) + return; + + if (pos == ALL_HOOKS_POS) + { + pos = 0; + count = g_hooks.size; + } + else + { + count = pos + 1; + } + + for (; pos < count; ++pos) + { + PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; + BOOL enable; + DWORD_PTR ip; + + switch (action) + { + case ACTION_DISABLE: + enable = FALSE; + break; + + case ACTION_ENABLE: + enable = TRUE; + break; + + default: // ACTION_APPLY_QUEUED + enable = pHook->queueEnable; + break; + } + if (pHook->isEnabled == enable) + continue; + + if (enable) + ip = FindNewIP(pHook, *pIP); + else + ip = FindOldIP(pHook, *pIP); + + if (ip != 0) + { + *pIP = ip; + SetThreadContext(hThread, &c); + } + } +} + +//------------------------------------------------------------------------- +static BOOL EnumerateThreads(PFROZEN_THREADS pThreads) +{ + BOOL succeeded = FALSE; + + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hSnapshot != INVALID_HANDLE_VALUE) + { + THREADENTRY32 te; + te.dwSize = sizeof(THREADENTRY32); + if (Thread32First(hSnapshot, &te)) + { + succeeded = TRUE; + do + { + if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD)) + && te.th32OwnerProcessID == GetCurrentProcessId() + && te.th32ThreadID != GetCurrentThreadId()) + { + if (pThreads->pItems == NULL) + { + pThreads->capacity = INITIAL_THREAD_CAPACITY; + pThreads->pItems + = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD)); + if (pThreads->pItems == NULL) + { + succeeded = FALSE; + break; + } + } + else if (pThreads->size >= pThreads->capacity) + { + pThreads->capacity *= 2; + LPDWORD p = (LPDWORD)HeapReAlloc( + g_hHeap, 0, pThreads->pItems, pThreads->capacity * sizeof(DWORD)); + if (p == NULL) + { + succeeded = FALSE; + break; + } + + pThreads->pItems = p; + } + pThreads->pItems[pThreads->size++] = te.th32ThreadID; + } + + te.dwSize = sizeof(THREADENTRY32); + } while (Thread32Next(hSnapshot, &te)); + + if (succeeded && GetLastError() != ERROR_NO_MORE_FILES) + succeeded = FALSE; + + if (!succeeded && pThreads->pItems != NULL) + { + HeapFree(g_hHeap, 0, pThreads->pItems); + pThreads->pItems = NULL; + } + } + CloseHandle(hSnapshot); + } + + return succeeded; +} + +//------------------------------------------------------------------------- +static MH_STATUS Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action) +{ + MH_STATUS status = MH_OK; + + pThreads->pItems = NULL; + pThreads->capacity = 0; + pThreads->size = 0; + if (!EnumerateThreads(pThreads)) + { + status = MH_ERROR_MEMORY_ALLOC; + } + else if (pThreads->pItems != NULL) + { + UINT i; + for (i = 0; i < pThreads->size; ++i) + { + HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); + if (hThread != NULL) + { + SuspendThread(hThread); + ProcessThreadIPs(hThread, pos, action); + CloseHandle(hThread); + } + } + } + + return status; +} + +//------------------------------------------------------------------------- +static VOID Unfreeze(PFROZEN_THREADS pThreads) +{ + if (pThreads->pItems != NULL) + { + UINT i; + for (i = 0; i < pThreads->size; ++i) + { + HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); + if (hThread != NULL) + { + ResumeThread(hThread); + CloseHandle(hThread); + } + } + + HeapFree(g_hHeap, 0, pThreads->pItems); + } +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableHookLL(UINT pos, BOOL enable) +{ + PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; + DWORD oldProtect; + SIZE_T patchSize = sizeof(JMP_REL); + LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget; + + if (pHook->patchAbove) + { + pPatchTarget -= sizeof(JMP_REL); + patchSize += sizeof(JMP_REL_SHORT); + } + + if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) + return MH_ERROR_MEMORY_PROTECT; + + if (enable) + { + PJMP_REL pJmp = (PJMP_REL)pPatchTarget; + pJmp->opcode = 0xE9; + pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL))); + + if (pHook->patchAbove) + { + PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget; + pShortJmp->opcode = 0xEB; + pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL))); + } + } + else + { + if (pHook->patchAbove) + memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); + else + memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL)); + } + + VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect); + + // Just-in-case measure. + FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize); + + pHook->isEnabled = enable; + pHook->queueEnable = enable; + + return MH_OK; +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableAllHooksLL(BOOL enable) +{ + MH_STATUS status = MH_OK; + UINT i, first = INVALID_HOOK_POS; + + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + first = i; + break; + } + } + + if (first != INVALID_HOOK_POS) + { + FROZEN_THREADS threads; + status = Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE); + if (status == MH_OK) + { + for (i = first; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + status = EnableHookLL(i, enable); + if (status != MH_OK) + break; + } + } + + Unfreeze(&threads); + } + } + + return status; +} + +//------------------------------------------------------------------------- +static VOID EnterSpinLock(VOID) +{ + SIZE_T spinCount = 0; + + // Wait until the flag is FALSE. + while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE) + { + // No need to generate a memory barrier here, since InterlockedCompareExchange() + // generates a full memory barrier itself. + + // Prevent the loop from being too busy. + if (spinCount < 32) + Sleep(0); + else + Sleep(1); + + spinCount++; + } +} + +//------------------------------------------------------------------------- +static VOID LeaveSpinLock(VOID) +{ + // No need to generate a memory barrier here, since InterlockedExchange() + // generates a full memory barrier itself. + + InterlockedExchange(&g_isLocked, FALSE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Initialize(VOID) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap == NULL) + { + g_hHeap = HeapCreate(0, 0, 0); + if (g_hHeap != NULL) + { + // Initialize the internal function buffer. + InitializeBuffer(); + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_ALREADY_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Uninitialize(VOID) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + status = EnableAllHooksLL(FALSE); + if (status == MH_OK) + { + // Free the internal function buffer. + + // HeapFree is actually not required, but some tools detect a false + // memory leak without HeapFree. + + UninitializeBuffer(); + + HeapFree(g_hHeap, 0, g_hooks.pItems); + HeapDestroy(g_hHeap); + + g_hHeap = NULL; + + g_hooks.pItems = NULL; + g_hooks.capacity = 0; + g_hooks.size = 0; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour)) + { + UINT pos = FindHookEntry(pTarget); + if (pos == INVALID_HOOK_POS) + { + LPVOID pBuffer = AllocateBuffer(pTarget); + if (pBuffer != NULL) + { + TRAMPOLINE ct; + + ct.pTarget = pTarget; + ct.pDetour = pDetour; + ct.pTrampoline = pBuffer; + if (CreateTrampolineFunction(&ct)) + { + PHOOK_ENTRY pHook = AddHookEntry(); + if (pHook != NULL) + { + pHook->pTarget = ct.pTarget; +#if defined(_M_X64) || defined(__x86_64__) + pHook->pDetour = ct.pRelay; +#else + pHook->pDetour = ct.pDetour; +#endif + pHook->pTrampoline = ct.pTrampoline; + pHook->patchAbove = ct.patchAbove; + pHook->isEnabled = FALSE; + pHook->queueEnable = FALSE; + pHook->nIP = ct.nIP; + memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs)); + memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs)); + + // Back up the target function. + + if (ct.patchAbove) + { + memcpy( + pHook->backup, + (LPBYTE)pTarget - sizeof(JMP_REL), + sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); + } + else + { + memcpy(pHook->backup, pTarget, sizeof(JMP_REL)); + } + + if (ppOriginal != NULL) + *ppOriginal = pHook->pTrampoline; + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_UNSUPPORTED_FUNCTION; + } + + if (status != MH_OK) + { + FreeBuffer(pBuffer); + } + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_ALREADY_CREATED; + } + } + else + { + status = MH_ERROR_NOT_EXECUTABLE; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + if (g_hooks.pItems[pos].isEnabled) + { + FROZEN_THREADS threads; + status = Freeze(&threads, pos, ACTION_DISABLE); + if (status == MH_OK) + { + status = EnableHookLL(pos, FALSE); + + Unfreeze(&threads); + } + } + + if (status == MH_OK) + { + FreeBuffer(g_hooks.pItems[pos].pTrampoline); + DeleteHookEntry(pos); + } + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (pTarget == MH_ALL_HOOKS) + { + status = EnableAllHooksLL(enable); + } + else + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + if (g_hooks.pItems[pos].isEnabled != enable) + { + FROZEN_THREADS threads; + status = Freeze(&threads, pos, ACTION_ENABLE); + if (status == MH_OK) + { + status = EnableHookLL(pos, enable); + + Unfreeze(&threads); + } + } + else + { + status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED; + } + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget) +{ + return EnableHook(pTarget, TRUE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget) +{ + return EnableHook(pTarget, FALSE); +} + +//------------------------------------------------------------------------- +static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (pTarget == MH_ALL_HOOKS) + { + UINT i; + for (i = 0; i < g_hooks.size; ++i) + g_hooks.pItems[i].queueEnable = queueEnable; + } + else + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + g_hooks.pItems[pos].queueEnable = queueEnable; + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget) +{ + return QueueHook(pTarget, TRUE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget) +{ + return QueueHook(pTarget, FALSE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_ApplyQueued(VOID) +{ + MH_STATUS status = MH_OK; + UINT i, first = INVALID_HOOK_POS; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable) + { + first = i; + break; + } + } + + if (first != INVALID_HOOK_POS) + { + FROZEN_THREADS threads; + status = Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED); + if (status == MH_OK) + { + for (i = first; i < g_hooks.size; ++i) + { + PHOOK_ENTRY pHook = &g_hooks.pItems[i]; + if (pHook->isEnabled != pHook->queueEnable) + { + status = EnableHookLL(i, pHook->queueEnable); + if (status != MH_OK) + break; + } + } + + Unfreeze(&threads); + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, + LPVOID *ppOriginal, LPVOID *ppTarget) +{ + HMODULE hModule; + LPVOID pTarget; + + hModule = GetModuleHandleW(pszModule); + if (hModule == NULL) + return MH_ERROR_MODULE_NOT_FOUND; + + pTarget = (LPVOID)GetProcAddress(hModule, pszProcName); + if (pTarget == NULL) + return MH_ERROR_FUNCTION_NOT_FOUND; + + if(ppTarget != NULL) + *ppTarget = pTarget; + + return MH_CreateHook(pTarget, pDetour, ppOriginal); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal) +{ + return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL); +} + +//------------------------------------------------------------------------- +const char * WINAPI MH_StatusToString(MH_STATUS status) +{ +#define MH_ST2STR(x) \ + case x: \ + return #x; + + switch (status) { + MH_ST2STR(MH_UNKNOWN) + MH_ST2STR(MH_OK) + MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED) + MH_ST2STR(MH_ERROR_NOT_INITIALIZED) + MH_ST2STR(MH_ERROR_ALREADY_CREATED) + MH_ST2STR(MH_ERROR_NOT_CREATED) + MH_ST2STR(MH_ERROR_ENABLED) + MH_ST2STR(MH_ERROR_DISABLED) + MH_ST2STR(MH_ERROR_NOT_EXECUTABLE) + MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION) + MH_ST2STR(MH_ERROR_MEMORY_ALLOC) + MH_ST2STR(MH_ERROR_MEMORY_PROTECT) + MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND) + MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND) + } + +#undef MH_ST2STR + + return "(unknown)"; +} diff --git a/l4d2/thirdparty/minhook/trampoline.c b/l4d2/thirdparty/minhook/trampoline.c new file mode 100644 index 0000000..617baf3 --- /dev/null +++ b/l4d2/thirdparty/minhook/trampoline.c @@ -0,0 +1,320 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifdef _MSC_VER + #include +#endif + +#ifndef ARRAYSIZE + #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#if defined(_M_X64) || defined(__x86_64__) + #include "./hde/hde64.h" + typedef hde64s HDE; + #define HDE_DISASM(code, hs) hde64_disasm(code, hs) +#else + #include "./hde/hde32.h" + typedef hde32s HDE; + #define HDE_DISASM(code, hs) hde32_disasm(code, hs) +#endif + +#include "trampoline.h" +#include "buffer.h" + +// Maximum size of a trampoline function. +#if defined(_M_X64) || defined(__x86_64__) + #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS)) +#else + #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE +#endif + +//------------------------------------------------------------------------- +static BOOL IsCodePadding(LPBYTE pInst, UINT size) +{ + UINT i; + + if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC) + return FALSE; + + for (i = 1; i < size; ++i) + { + if (pInst[i] != pInst[0]) + return FALSE; + } + return TRUE; +} + +//------------------------------------------------------------------------- +BOOL CreateTrampolineFunction(PTRAMPOLINE ct) +{ +#if defined(_M_X64) || defined(__x86_64__) + CALL_ABS call = { + 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8] + 0xEB, 0x08, // EB 08: JMP +10 + 0x0000000000000000ULL // Absolute destination address + }; + JMP_ABS jmp = { + 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] + 0x0000000000000000ULL // Absolute destination address + }; + JCC_ABS jcc = { + 0x70, 0x0E, // 7* 0E: J** +16 + 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] + 0x0000000000000000ULL // Absolute destination address + }; +#else + CALL_REL call = { + 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx + 0x00000000 // Relative destination address + }; + JMP_REL jmp = { + 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx + 0x00000000 // Relative destination address + }; + JCC_REL jcc = { + 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx + 0x00000000 // Relative destination address + }; +#endif + + UINT8 oldPos = 0; + UINT8 newPos = 0; + ULONG_PTR jmpDest = 0; // Destination address of an internal jump. + BOOL finished = FALSE; // Is the function completed? +#if defined(_M_X64) || defined(__x86_64__) + UINT8 instBuf[16]; +#endif + + ct->patchAbove = FALSE; + ct->nIP = 0; + + do + { + HDE hs; + UINT copySize; + LPVOID pCopySrc; + ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos; + ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos; + + copySize = HDE_DISASM((LPVOID)pOldInst, &hs); + if (hs.flags & F_ERROR) + return FALSE; + + pCopySrc = (LPVOID)pOldInst; + if (oldPos >= sizeof(JMP_REL)) + { + // The trampoline function is long enough. + // Complete the function with the jump to the target function. +#if defined(_M_X64) || defined(__x86_64__) + jmp.address = pOldInst; +#else + jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp))); +#endif + pCopySrc = &jmp; + copySize = sizeof(jmp); + + finished = TRUE; + } +#if defined(_M_X64) || defined(__x86_64__) + else if ((hs.modrm & 0xC7) == 0x05) + { + // Instructions using RIP relative addressing. (ModR/M = 00???101B) + + // Modify the RIP relative address. + PUINT32 pRelAddr; + + // Avoid using memcpy to reduce the footprint. +#ifndef _MSC_VER + memcpy(instBuf, (LPBYTE)pOldInst, copySize); +#else + __movsb(instBuf, (LPBYTE)pOldInst, copySize); +#endif + pCopySrc = instBuf; + + // Relative address is stored at (instruction length - immediate value length - 4). + pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4); + *pRelAddr + = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len)); + + // Complete the function if JMP (FF /4). + if (hs.opcode == 0xFF && hs.modrm_reg == 4) + finished = TRUE; + } +#endif + else if (hs.opcode == 0xE8) + { + // Direct relative CALL + ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32; +#if defined(_M_X64) || defined(__x86_64__) + call.address = dest; +#else + call.operand = (UINT32)(dest - (pNewInst + sizeof(call))); +#endif + pCopySrc = &call; + copySize = sizeof(call); + } + else if ((hs.opcode & 0xFD) == 0xE9) + { + // Direct relative JMP (EB or E9) + ULONG_PTR dest = pOldInst + hs.len; + + if (hs.opcode == 0xEB) // isShort jmp + dest += (INT8)hs.imm.imm8; + else + dest += (INT32)hs.imm.imm32; + + // Simply copy an internal jump. + if ((ULONG_PTR)ct->pTarget <= dest + && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) + { + if (jmpDest < dest) + jmpDest = dest; + } + else + { +#if defined(_M_X64) || defined(__x86_64__) + jmp.address = dest; +#else + jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp))); +#endif + pCopySrc = &jmp; + copySize = sizeof(jmp); + + // Exit the function if it is not in the branch. + finished = (pOldInst >= jmpDest); + } + } + else if ((hs.opcode & 0xF0) == 0x70 + || (hs.opcode & 0xFC) == 0xE0 + || (hs.opcode2 & 0xF0) == 0x80) + { + // Direct relative Jcc + ULONG_PTR dest = pOldInst + hs.len; + + if ((hs.opcode & 0xF0) == 0x70 // Jcc + || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ + dest += (INT8)hs.imm.imm8; + else + dest += (INT32)hs.imm.imm32; + + // Simply copy an internal jump. + if ((ULONG_PTR)ct->pTarget <= dest + && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) + { + if (jmpDest < dest) + jmpDest = dest; + } + else if ((hs.opcode & 0xFC) == 0xE0) + { + // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported. + return FALSE; + } + else + { + UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F); +#if defined(_M_X64) || defined(__x86_64__) + // Invert the condition in x64 mode to simplify the conditional jump logic. + jcc.opcode = 0x71 ^ cond; + jcc.address = dest; +#else + jcc.opcode1 = 0x80 | cond; + jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc))); +#endif + pCopySrc = &jcc; + copySize = sizeof(jcc); + } + } + else if ((hs.opcode & 0xFE) == 0xC2) + { + // RET (C2 or C3) + + // Complete the function if not in a branch. + finished = (pOldInst >= jmpDest); + } + + // Can't alter the instruction length in a branch. + if (pOldInst < jmpDest && copySize != hs.len) + return FALSE; + + // Trampoline function is too large. + if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) + return FALSE; + + // Trampoline function has too many instructions. + if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) + return FALSE; + + ct->oldIPs[ct->nIP] = oldPos; + ct->newIPs[ct->nIP] = newPos; + ct->nIP++; + + // Avoid using memcpy to reduce the footprint. +#ifndef _MSC_VER + memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); +#else + __movsb((LPBYTE)ct->pTrampoline + newPos, (LPBYTE)pCopySrc, copySize); +#endif + newPos += copySize; + oldPos += hs.len; + } + while (!finished); + + // Is there enough place for a long jump? + if (oldPos < sizeof(JMP_REL) + && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos)) + { + // Is there enough place for a short jump? + if (oldPos < sizeof(JMP_REL_SHORT) + && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos)) + { + return FALSE; + } + + // Can we place the long jump above the function? + if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL))) + return FALSE; + + if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL))) + return FALSE; + + ct->patchAbove = TRUE; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Create a relay function. + jmp.address = (ULONG_PTR)ct->pDetour; + + ct->pRelay = (LPBYTE)ct->pTrampoline + newPos; + memcpy(ct->pRelay, &jmp, sizeof(jmp)); +#endif + + return TRUE; +} diff --git a/l4d2/thirdparty/minhook/trampoline.h b/l4d2/thirdparty/minhook/trampoline.h new file mode 100644 index 0000000..bdffdac --- /dev/null +++ b/l4d2/thirdparty/minhook/trampoline.h @@ -0,0 +1,105 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#pragma pack(push, 1) + +// Structs for writing x86/x64 instructions. + +// 8-bit relative jump. +typedef struct _JMP_REL_SHORT +{ + UINT8 opcode; // EB xx: JMP +2+xx + UINT8 operand; +} JMP_REL_SHORT, *PJMP_REL_SHORT; + +// 32-bit direct relative jump/call. +typedef struct _JMP_REL +{ + UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx + UINT32 operand; // Relative destination address +} JMP_REL, *PJMP_REL, CALL_REL; + +// 64-bit indirect absolute jump. +typedef struct _JMP_ABS +{ + UINT8 opcode0; // FF25 00000000: JMP [+6] + UINT8 opcode1; + UINT32 dummy; + UINT64 address; // Absolute destination address +} JMP_ABS, *PJMP_ABS; + +// 64-bit indirect absolute call. +typedef struct _CALL_ABS +{ + UINT8 opcode0; // FF15 00000002: CALL [+6] + UINT8 opcode1; + UINT32 dummy0; + UINT8 dummy1; // EB 08: JMP +10 + UINT8 dummy2; + UINT64 address; // Absolute destination address +} CALL_ABS; + +// 32-bit direct relative conditional jumps. +typedef struct _JCC_REL +{ + UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx + UINT8 opcode1; + UINT32 operand; // Relative destination address +} JCC_REL; + +// 64bit indirect absolute conditional jumps that x64 lacks. +typedef struct _JCC_ABS +{ + UINT8 opcode; // 7* 0E: J** +16 + UINT8 dummy0; + UINT8 dummy1; // FF25 00000000: JMP [+6] + UINT8 dummy2; + UINT32 dummy3; + UINT64 address; // Absolute destination address +} JCC_ABS; + +#pragma pack(pop) + +typedef struct _TRAMPOLINE +{ + LPVOID pTarget; // [In] Address of the target function. + LPVOID pDetour; // [In] Address of the detour function. + LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function. + +#if defined(_M_X64) || defined(__x86_64__) + LPVOID pRelay; // [Out] Address of the relay function. +#endif + BOOL patchAbove; // [Out] Should use the hot patch area? + UINT nIP; // [Out] Number of the instruction boundaries. + UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function. + UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function. +} TRAMPOLINE, *PTRAMPOLINE; + +BOOL CreateTrampolineFunction(PTRAMPOLINE ct); diff --git a/l4d2/utilities/address/address.h b/l4d2/utilities/address/address.h new file mode 100644 index 0000000..d98d60e --- /dev/null +++ b/l4d2/utilities/address/address.h @@ -0,0 +1,161 @@ +#pragma once + +#include + +#define CONCAT_IMPL( x, y ) x##y +#define MACRO_CONCAT( x, y ) CONCAT_IMPL( x, y ) +#define PAD( size ) uint8_t MACRO_CONCAT( _pad, __COUNTER__ )[ size ]; + +namespace Hikari +{ + template < typename type = std::uintptr_t > + struct AddressBase_t + { + type ptr; + + AddressBase_t() : ptr{} + { + } + + AddressBase_t( type src ) : ptr{ src } + { + } + + AddressBase_t( type* src ) : ptr{ type( src ) } + { + } + + AddressBase_t( void* src ) : ptr{ type( src ) } + { + } + + AddressBase_t( const void* src ) : ptr{ type( src ) } + { + } + + ~AddressBase_t() = default; + + operator type() const + { + return ptr; + } + + operator void*() const + { + return reinterpret_cast( ptr ); + } + + bool IsValid() + { + #ifdef ENVIRONMENT32 + return ptr >= 0x1000 && ptr < 0xFFFFFFEF; + #else + return ptr >= 0x1000 && ptr < 0x7FFFFFFEFFFF; + #endif + } + + template < typename T = type > + T Cast() + { + return T( ptr ); + } + + template < typename T = AddressBase_t > + T Relative( ptrdiff_t offset = 0x1 ) + { + // An explanation about how to get the actual address from relative signatures in case you are pepega: + // Let's take these lines of opcodes as example: + + // .text:100683D2 E8 99 F5 FF FF call sub_10067970 ; x86, function + // .text:100683D7 83 C4 18 add esp, 18h + + // .text:1400C2786 48 8D 0D 33 EB D1 01 lea rcx, qword_141DE12C0 ; x64, pointer/table or whaterver it is + // .text:1400C278D E8 0E 04 68 00 call sub_140742BA0 + + // In order to get the actual address, we need to get the relative address first + // so we need to do base_address + offset = relative_address, which is 0x100683D2 + 1 = 0x100683D3 for x86 example, and 0x1400C2786 + 3 = 1400C2789 for x64 example + // the reason why we are adding different offset is to skip the opcode(s) that we aren't interested in ( E8 for x86 example, 48 8D 0D for x64 example ) + // both lead to the address of where relative address starts + // next, we need to dereference it to read the relative address ( the address will never reach to std::numeric_limits::max() so we don't have to worry about it ) + // so the actual_relative_address will be 0xFFFFF599 or 0x1D1EB33 from the example + // then we can get the actual address with this formula: + // (1)relative_signature_address + offset + sizeof(std::int32_t) + actual_relative_address = actual_address + + // (1): This refers to the address where the given signature points to, you can think it as 0x100683D2 or 0x1400C2786 from the example + + type base = ptr + offset; + const auto relative_address = *reinterpret_cast( base ); + base += sizeof( std::int32_t ) + relative_address; + + return T( base ); + } + + template < typename T = AddressBase_t > + T SelfRelative( ptrdiff_t offset = 0x1 ) + { + ptr = Relative( offset ); + return *this; + } + + template < typename T = AddressBase_t > + T FindOpcode( std::uint8_t opcode, bool forward = true ) + { + auto base = ptr; + + while ( true ) + { + if ( const auto current_opcode = *reinterpret_cast( base ); current_opcode == opcode ) + break; + + forward ? ++base : --base; + } + + return T( base ); + } + + template < typename T = AddressBase_t > + T SelfFindOpcode( std::uint8_t opcode, bool forward = true ) + { + ptr = FindOpcode( opcode, forward ); + return *this; + } + + template < typename T = AddressBase_t > + T Dereference( uint8_t count = 1 ) + { + type base = ptr; + while ( count-- ) + { + if ( T( base ).IsValid() ) + base = *reinterpret_cast( base ); + } + + return T( base ); + } + + template < typename T = AddressBase_t > + T SelfDereference( uint8_t count = 1 ) + { + ptr = Dereference( count ); + return *this; + } + + template < typename T = AddressBase_t > + T Offset( ptrdiff_t offset ) + { + return T( ptr + offset ); + } + + template < typename T = AddressBase_t > + T SelfOffset( ptrdiff_t offset ) + { + ptr = Offset( offset ); + return *this; + } + }; + + using Address_t = AddressBase_t; + + // using Address32_t = AddressBase_t; + // using Address64_t = AddressBase_t; +} diff --git a/l4d2/utilities/color/color.h b/l4d2/utilities/color/color.h new file mode 100644 index 0000000..a97fe1b --- /dev/null +++ b/l4d2/utilities/color/color.h @@ -0,0 +1,40 @@ +#pragma once +#include +#include + +namespace Hikari +{ + struct Color_t + { + uint8_t r{}, g{}, b{}, a{}; + + Color_t() = default; + + Color_t( std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a = 255 ) : r{ r }, g{ g }, b{ b }, a{ a } + { + } + + Color_t( int r, int g, int b, int a = 255 ) : r{ static_cast( r ) }, g{ static_cast( g ) }, b{ static_cast( b ) }, a{ static_cast( a ) } + { + } + + Color_t( float r, float g, float b, float a = 255 ) : r{ static_cast( r ) }, g{ static_cast( g ) }, b{ static_cast( b ) }, a{ static_cast( a ) } + { + } + + Color_t( const int* arr ) : r{ static_cast( arr[ 0 ] ) }, g{ static_cast( arr[ 1 ] ) }, b{ static_cast( arr[ 2 ] ) }, a{ static_cast( arr[ 3 ] ) } + { + } + + Color_t( const float* arr ) : r{ static_cast( arr[ 0 ] * 255.f ) }, g{ static_cast( arr[ 1 ] * 255.f ) }, b{ static_cast( arr[ 2 ] * 255.f ) }, a{ static_cast( arr[ 3 ] * 255.f ) } + { + } + + Color_t( const std::uint8_t* arr ) : r{ arr[ 0 ] }, g{ arr[ 1 ] }, b{ arr[ 2 ] }, a{ arr[ 3 ] } + { + } + + constexpr auto operator<=>( const Color_t& ) const = default; + constexpr bool operator==( const Color_t& ) const = default; + }; +} diff --git a/l4d2/utilities/console/console.cpp b/l4d2/utilities/console/console.cpp new file mode 100644 index 0000000..3121b34 --- /dev/null +++ b/l4d2/utilities/console/console.cpp @@ -0,0 +1,96 @@ +#include "../lib.h" + +#include +#include + +namespace Hikari +{ + Console_t::Console_t() + { + Initialize(); + } + + Console_t::~Console_t() + { + this->Destory(); + } + + bool Console_t::Initialize() + { + if ( this->_initialized ) + return true; + + #ifdef WINDOWS + if ( !AllocConsole() ) + throw std::runtime_error( ( "Failed to allocate a new console" ) ); + + freopen_s( reinterpret_cast<_iobuf**>( stdout ), ( "CONOUT$" ), ( "w" ), stdout ); + SetConsoleTitleA( ( "Hikari" ) ); + + this->_initialized = true; + + this->Success( ("Console initialized") ); + + return true; + #endif + } + + void Console_t::Destory() + { + if ( !this->_initialized ) + return; + + this->_initialized = false; + + #ifdef WINDOWS + fclose( stdin ); + fclose( stdout ); + fclose( stderr ); + FreeConsole(); + #endif + } + + void Console_t::Log( std::string_view str ) + { + if ( !this->_initialized ) + return; + + const auto& timezones = std::chrono::get_tzdb(); + const auto& current_timezone = timezones.current_zone(); + + const auto time = std::format( ( "[{:%H:%M:%S}]" ), current_timezone->to_local( std::chrono::system_clock::now() ) ); + // #ifdef _DEBUG + std::cout << std::format( ( "{} {} \n" ), time, str ); + // #endif + } + + void Console_t::Info( std::string_view str ) + { + if ( !this->_initialized ) + return; + + // #ifdef _DEBUG + this->Log( std::format( ( "[-] {}" ), str ) ); + // #endif + } + + void Console_t::Error( std::string_view str ) + { + if ( !this->_initialized ) + return; + + // #ifdef _DEBUG + this->Log( std::format( ( "[x] {}" ), str ) ); + // #endif + } + + void Console_t::Success( std::string_view str ) + { + if ( !this->_initialized ) + return; + + // #ifdef _DEBUG + this->Log( std::format( ( "[+] {}" ), str ) ); + // #endif + } +} diff --git a/l4d2/utilities/console/console.h b/l4d2/utilities/console/console.h new file mode 100644 index 0000000..dd64530 --- /dev/null +++ b/l4d2/utilities/console/console.h @@ -0,0 +1,27 @@ +#pragma once + +namespace Hikari +{ + struct Console_t + { + Console_t(); + ~Console_t(); + + bool Initialize(); + void Destory(); + private: + void Log( std::string_view str ); + + public: + void Info( std::string_view str ); + + void Error( std::string_view str ); + + void Success( std::string_view str ); + + private: + bool _initialized = false; + }; + + inline std::unique_ptr Logger; +} diff --git a/l4d2/utilities/hash/hash.h b/l4d2/utilities/hash/hash.h new file mode 100644 index 0000000..344fc0b --- /dev/null +++ b/l4d2/utilities/hash/hash.h @@ -0,0 +1,67 @@ +#pragma once +#include +#include + +namespace detail +{ + template < typename Type, Type OffsetBasis, Type Prime > + struct size_dependant_data + { + using type = Type; + constexpr static auto k_offset_basis = OffsetBasis; + constexpr static auto k_prime = Prime; + }; + + template < size_t Bits > + struct size_selector; + + template <> + struct size_selector<32> + { + using type = size_dependant_data; + }; + + template <> + struct size_selector<64> + { + using type = size_dependant_data; + }; + + // Implements FNV-1a hash algorithm + template < std::size_t Size > + class fnv_hash + { + private: + using data_t = typename size_selector::type; + + public: + using hash = typename data_t::type; + + private: + constexpr static auto k_offset_basis = data_t::k_offset_basis; + constexpr static auto k_prime = data_t::k_prime; + + public: + template < std::size_t N > + static __forceinline constexpr hash hash_constexpr( const char (&str)[ N ], const std::size_t size = N ) + { + return static_cast( 1ull * ( size == 1 ? ( k_offset_basis ^ str[ 0 ] ) : ( hash_constexpr( str, size - 1 ) ^ str[ size - 1 ] ) ) * k_prime ); + } + + static __forceinline hash hash_runtime( const char* str ) + { + auto result = k_offset_basis; + do + { + result ^= *str++; + result *= k_prime; + } while ( *( str - 1 ) != '\0' ); + + return result; + } + }; +} + +using fnv = detail::fnv_hash; + +#define FNV(str) (std::integral_constant::value) diff --git a/l4d2/utilities/lib.h b/l4d2/utilities/lib.h new file mode 100644 index 0000000..b44e43d --- /dev/null +++ b/l4d2/utilities/lib.h @@ -0,0 +1,34 @@ +#pragma once + +#if _WIN32 || _WIN64 + #define WINDOWS + #if _WIN64 + #define ENVIRONMENT64 + #else + #define ENVIRONMENT32 + #endif +// Check GCC +#elif __GNUC__ + #if __x86_64__ || __ppc64__ + #define ENVIRONMENT64 + #else + #define ENVIRONMENT32 + #endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "address/address.h" +#include "console/console.h" +#include "module_info/module_info.h" +#include "string/string.h" +#include "memory/memory.h" +#include "string/string.h" +#include "hash/hash.h" \ No newline at end of file diff --git a/l4d2/utilities/memory/memory.cpp b/l4d2/utilities/memory/memory.cpp new file mode 100644 index 0000000..52c3c63 --- /dev/null +++ b/l4d2/utilities/memory/memory.cpp @@ -0,0 +1,204 @@ +#include "../lib.h" +#include +#include +#include + +namespace Hikari +{ + Address_t Memory_t::FindPattern( ModuleInfo_t info, const char* pattern, bool relative ) const + { + if ( !strlen( pattern ) ) + return {}; + + const auto bytes = PatternToBytes( pattern ); + + for ( const auto segments = info.Segments(); const auto& [ address, data, size, name ] : segments ) + { + if ( Address_t result = FindPattern( data, size, bytes ) ) + { + return relative ? result.Relative() : result; + } + } + + return {}; + } + + std::vector Memory_t::PatternToBytes( const char* pattern ) + { + auto bytes = std::vector{}; + const auto start = const_cast( pattern ); + const auto end = const_cast( pattern ) + strlen( pattern ); + + for ( auto current = start; current < end; ++current ) + { + if ( *current == '?' ) + { + ++current; + + if ( *current == '?' ) + ++current; + + bytes.push_back( -1 ); + } else + { + bytes.push_back( strtoul( current, ¤t, 16 ) ); + } + } + return bytes; + } + + std::uintptr_t Memory_t::FindPattern( const std::uint8_t* data, std::size_t size, const std::vector& pattern ) + { + std::uintptr_t result{}; + for ( auto i = 0UL; i < size - pattern.size(); ++i ) + { + bool found = true; + + for ( auto s = 0UL; s < pattern.size(); ++s ) + { + if ( data[ i + s ] != pattern[ s ] && pattern[ s ] != -1 ) + { + found = false; + break; + } + } + + if ( found ) + { + result = reinterpret_cast( &data[ i ] ); + break; + } + } + return result; + } + + std::string bytes_to_ida_pattern( byte* bytes, size_t size ) + { + std::stringstream ida_pattern; + ida_pattern << std::hex << std::setfill( '0' ); + for ( size_t i = 0; i < size; i++ ) + { + if ( const int32_t current_byte = bytes[ i ]; current_byte != 255 ) + ida_pattern << std::setw( 2 ) << current_byte; + else + ida_pattern << std::setw( 1 ) << "?"; + + if ( i != size - 1 ) + ida_pattern << " "; + } + + return ida_pattern.str(); + } + + bool get_section_info( uintptr_t base_address, const std::string& section_name, uintptr_t& section_start, uintptr_t& section_size ) + { + const auto dos_header = reinterpret_cast( base_address ); + if ( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) + return false; + + const auto nt_headers = reinterpret_cast( base_address + dos_header->e_lfanew ); + if ( nt_headers->Signature != IMAGE_NT_SIGNATURE ) + return false; + + auto section_header = IMAGE_FIRST_SECTION( nt_headers ); + uint16_t number_of_sections = nt_headers->FileHeader.NumberOfSections; + while ( number_of_sections > 0 ) + { + // If we're at the right section + if ( !strcmp( section_name.c_str(), reinterpret_cast( section_header->Name ) ) ) + { + section_start = base_address + section_header->VirtualAddress; + section_size = section_header->SizeOfRawData; + return true; + } + + section_header++; + number_of_sections--; + } + + return false; + } + + std::vector Memory_t::GetXrefsTo( uintptr_t address, uintptr_t start, uintptr_t size ) + { + std::vector xrefs = {}; + + // Convert the address over to an IDA pattern string + const std::string ida_pattern = bytes_to_ida_pattern( ( byte* )&address, 4 ); + + // Get the end of the section (in our case the end of the .rdata section) + const uintptr_t end = start + size; + while ( start && start < end ) + { + auto xref = FindPattern( reinterpret_cast( start ), size, PatternToBytes( ida_pattern.c_str() ) ); + + // If the xref is 0 it means that there either were no xrefs, or there are no remaining xrefs. + // So we should break out of our loop, otherwise it will keep on trying to look for xrefs. + if ( !xref ) + break; + + // We've found an xref, save it in the vector, and add 4 to start, so it wil now search for xrefs + // from the previously found xref untill we're at the end of the section, or there aren't any xrefs left. + xrefs.push_back( xref ); + start = xref + 4; + } + + return xrefs; + } + + Address_t Memory_t::GetVTablePointer( ModuleInfo_t info, std::string table_name ) + { + const std::string type_descriptor_name = ( ".?AV" ) + table_name + ( "@@" ); + + std::string ida_pattern = bytes_to_ida_pattern( ( unsigned char* )type_descriptor_name.data(), type_descriptor_name.size() ); + auto rtti_type_descriptor = FindPattern( info, ida_pattern.c_str() ); + if ( !rtti_type_descriptor ) + return {}; + + rtti_type_descriptor.Offset( -0x8 ); + + uintptr_t rdata_start = 0, rdata_size = 0; + if ( !get_section_info( info.BaseAddress(), ".rdata", rdata_start, rdata_size ) ) + return {}; + + const auto xrefs = GetXrefsTo( rtti_type_descriptor, rdata_start, rdata_size ); + for ( const uintptr_t xref : xrefs ) + { + // xref - 0x8 = offset of this vtable in complete class (from top) + // So if it's 0 it means it's the class we need, and not some class it inherits from (again, opening up client_panorama.dll in IDA will help you understand) + const int32_t offset_from_class = *reinterpret_cast( xref - 0x8 ); + if ( offset_from_class != 0 ) + continue; + + // We've found the correct xref, the object locator is 0xC bytes before the xref. (Again, client_panorama.dll yada yada yada) + const uintptr_t object_locator = xref - 0xC; + + // Now we need to get an xref to the object locator, as that's where the vtable is located + { + // Convert the object locator address to an IDA pattern + ida_pattern = bytes_to_ida_pattern( ( byte* )&object_locator, 4 ); + + const uintptr_t vtable_address = FindPattern( reinterpret_cast( rdata_start ), rdata_size, PatternToBytes( ida_pattern.c_str() ) ) + 0x4; + + // Here I'm checking for <= 4 as we're adding 0x4 to it. So if the pattern scan returns 0 we still head the fuck out + if ( vtable_address <= 4 ) + return {}; + + // We've now found the vtable address, however, we probably want a pointer to the vtable (which is in .text). + // To do this, we're going to find a reference to the vtable address, and use that as pointer. + + // If you don't need a pointer to the vtable in your implementation however, just return vtable_address + uintptr_t text_start = 0, text_size = 0; + if ( !get_section_info( info.BaseAddress(), ( ".text" ), text_start, text_size ) ) + return {}; + + // Convert the vtable address to an IDA pattern + ida_pattern = bytes_to_ida_pattern( ( byte* )&vtable_address, 4 ); + return FindPattern( reinterpret_cast( text_start ), text_size, PatternToBytes( ida_pattern.c_str() ) ); + } + } + + // We for some odd reason didn't find any valid xrefs + return {}; + } +} diff --git a/l4d2/utilities/memory/memory.h b/l4d2/utilities/memory/memory.h new file mode 100644 index 0000000..a2f401c --- /dev/null +++ b/l4d2/utilities/memory/memory.h @@ -0,0 +1,46 @@ +#pragma once + +namespace Hikari +{ + struct Memory_t + { + Address_t FindPattern( ModuleInfo_t info, const char* pattern, bool relative = false ) const; + Address_t GetVTablePointer(ModuleInfo_t info, std::string table_name); + + template < size_t Index > + static auto VirtualFunction( void* pInstance ) + { + return ( *static_cast( pInstance ) )[ Index ]; + } + + #ifdef WINDOWS + template < typename T, size_t Index, typename... Args > + constexpr T VirtualCall( void* pInstance, Args ... args ) + { + using Fn = T(__thiscall*)( void*, decltype(args)... ); + return reinterpret_cast( VirtualFunction( pInstance ) )( pInstance, args... ); + } + + template < typename T, size_t Index, typename... Args > + constexpr T VirtualCallCdecl( void* pInstance, Args ... args ) + { + using Fn = T(__cdecl*)( void*, decltype(args)... ); + return ( reinterpret_cast( VirtualFunction( pInstance ) ) )( pInstance, args... ); + } + #else + template < typename T, size_t Index, typename... Args > + constexpr T VirtualCall(void* pInstance, Args ... args) + { + using Fn = T(*)(void*, decltype(args)...); + return reinterpret_cast(VirtualFunction(pInstance))(pInstance, args...); + } + #endif + + private: + static std::vector PatternToBytes( const char* pattern ); + static std::uintptr_t FindPattern( const std::uint8_t* data, std::size_t size, const std::vector& pattern ); + std::vector GetXrefsTo(uintptr_t address, uintptr_t start, uintptr_t size); + }; + + inline std::unique_ptr Memory; +} diff --git a/l4d2/utilities/module_info/module_info.cpp b/l4d2/utilities/module_info/module_info.cpp new file mode 100644 index 0000000..d2e7ea0 --- /dev/null +++ b/l4d2/utilities/module_info/module_info.cpp @@ -0,0 +1,147 @@ +#include "../lib.h" + +Hikari::ModuleInfo_t::ModuleInfo_t(): _base_address( 0 ), _size( 0 ), _handle( nullptr ) +{ +} + +Hikari::ModuleInfo_t::ModuleInfo_t( const std::string& module_name ) +{ + GetModuleInfo( module_name ); +} + +#ifdef WINDOWS +Hikari::ModuleInfo_t::ModuleInfo_t( const HMODULE handle ) +{ + GetModuleInfo( handle ); +} +#endif + +Hikari::ModuleInfo_t::~ModuleInfo_t() +{ + this->_segments.clear(); + this->_name.clear(); + this->_base_address = this->_size = 0; +} + +void Hikari::ModuleInfo_t::GetModuleInfo( const std::string& module_name ) +{ + #ifdef WINDOWS + const auto handle = GetModuleHandleA( module_name.c_str() ); + if ( !handle ) + throw std::runtime_error( std::format( ( "Failed to get module handle for {}" ), module_name ) ); + + const auto dos_header = reinterpret_cast( handle ); + + if ( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) + throw std::runtime_error( std::format( ( "Invalid dos magic for {}" ), module_name ) ); + + const auto nt_header = reinterpret_cast( reinterpret_cast( handle ) + dos_header->e_lfanew ); + + if ( nt_header->Signature != IMAGE_NT_SIGNATURE ) + throw std::runtime_error( std::format( ( "Invalid nt signature for {}" ), module_name ) ); + + this->_handle = handle; + this->_name = module_name; + this->_base_address = reinterpret_cast( handle ); + this->_size = nt_header->OptionalHeader.SizeOfImage; + + auto section = IMAGE_FIRST_SECTION( nt_header ); + + for ( auto i = 0; i < nt_header->FileHeader.NumberOfSections; i++, section++ ) + { + const auto is_executable = ( section->Characteristics & IMAGE_SCN_MEM_EXECUTE ) != 0; + + if ( const auto is_readable = ( section->Characteristics & IMAGE_SCN_MEM_READ ) != 0; is_executable && is_readable ) + { + const auto start = reinterpret_cast( handle ) + section->VirtualAddress; + const auto size = ( std::min )( section->SizeOfRawData, section->Misc.VirtualSize ); + this->_segments.emplace_back( start, reinterpret_cast( start ), size ); + } + } + + Logger->Success( std::format( ( "{} | base_addr:{:#09X} | size:{:#09X} | _segments.size():{}" ), _name, _base_address, _size, _segments.size() ) ); + #else + // TODO: Linux support + #endif +} + +#ifdef WINDOWS +void Hikari::ModuleInfo_t::GetModuleInfo( HMODULE handle ) +{ + if ( !handle ) + throw std::runtime_error( std::format( ( "Invalid module handle" ) ) ); + + const auto dos_header = reinterpret_cast( handle ); + + if ( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) + throw std::runtime_error( std::format( ( "Invalid dos magic" ) ) ); + + const auto nt_header = reinterpret_cast( reinterpret_cast( handle ) + dos_header->e_lfanew ); + + if ( nt_header->Signature != IMAGE_NT_SIGNATURE ) + throw std::runtime_error( std::format( ( "Invalid nt signature" ) ) ); + + this->_handle = handle; + this->_name = ( "Hikari" ); + this->_base_address = reinterpret_cast( handle ); + this->_size = nt_header->OptionalHeader.SizeOfImage; + + auto section = IMAGE_FIRST_SECTION( nt_header ); + + for ( auto i = 0; i < nt_header->FileHeader.NumberOfSections; i++, section++ ) + { + if ( !strcmp( reinterpret_cast( section->Name ), ".text" ) ) + { + const auto start = reinterpret_cast( handle ) + section->VirtualAddress; + const auto size = ( std::min )( section->SizeOfRawData, section->Misc.VirtualSize ); + this->_segments.emplace_back( start, reinterpret_cast( start ), size, reinterpret_cast( section->Name ) ); + } + } + + Logger->Success( std::format( ( "{} | base_addr:{:#09X} | size:{:#09X} | _segments.size():{}" ), _name, _base_address, _size, _segments.size() ) ); + #else + // TODO: Linux support + #endif +} + +std::string Hikari::ModuleInfo_t::Name() +{ + return _name; +} + +std::vector& Hikari::ModuleInfo_t::Segments() +{ + return _segments; +} + +std::uintptr_t Hikari::ModuleInfo_t::BaseAddress() const +{ + return _base_address; +} + +std::size_t Hikari::ModuleInfo_t::Size() const +{ + return _size; +} + +void* Hikari::ModuleInfo_t::Handle() const +{ + return _handle; +} + +FARPROC Hikari::ModuleInfo_t::GetProc( std::string_view proc_name ) +{ + if ( !this->_handle ) + throw std::runtime_error( std::format( ( "Invalid module handle for {} when getting ProcAddress" ), this->_name ) ); + + #ifdef WINDOWS + if ( const auto address = GetProcAddress( this->_handle, proc_name.data() ); address ) + { + return address; + } + #else + // TOOD: Linux support + #endif + + return nullptr; +} diff --git a/l4d2/utilities/module_info/module_info.h b/l4d2/utilities/module_info/module_info.h new file mode 100644 index 0000000..2114b00 --- /dev/null +++ b/l4d2/utilities/module_info/module_info.h @@ -0,0 +1,53 @@ +#pragma once + +namespace Hikari +{ + struct ModuleInfo_t + { + struct Segment_t + { + std::uintptr_t address; + std::uint8_t* data; + std::size_t size; + const char* name; + }; + + ModuleInfo_t(); + + /** + * \brief Getting module info (base_address, size, readable & executable segments) by name + * \param module_name The module's name + */ + explicit ModuleInfo_t( const std::string& module_name ); + + #ifdef WINDOWS + /** + * \brief Getting module info (base_address, size, readable & executable segments) by handle + * \param handle The module handle + */ + explicit ModuleInfo_t( HMODULE handle ); + #endif + ~ModuleInfo_t(); + + private: + std::string _name; + std::vector _segments; + std::uintptr_t _base_address; + std::size_t _size; +#ifdef WINDOWS + HMODULE _handle; +#endif + + void GetModuleInfo( const std::string& module_name ); + #ifdef WINDOWS + void GetModuleInfo( HMODULE handle ); + #endif + public: + std::string Name(); + std::vector& Segments(); + std::uintptr_t BaseAddress() const; + std::size_t Size() const; + void* Handle() const; + FARPROC GetProc( std::string_view proc_name ); + }; +} diff --git a/l4d2/utilities/string/string.cpp b/l4d2/utilities/string/string.cpp new file mode 100644 index 0000000..42c644f --- /dev/null +++ b/l4d2/utilities/string/string.cpp @@ -0,0 +1,72 @@ +#include "../lib.h" +#include "string.h" + +#include + +namespace Hikari +{ + std::string String::ToUtf8( const std::wstring& str ) + { + if ( str.empty() ) + return std::string(); + + const int size = WideCharToMultiByte( CP_UTF8, 0, &str[ 0 ], str.size(), nullptr, 0, nullptr, nullptr ); + + std::string out( size, 0 ); + WideCharToMultiByte( CP_UTF8, 0, &str[ 0 ], str.size(), &out[ 0 ], size, nullptr, nullptr ); + + return out; + } + + std::wstring String::ToUnicode( const std::string& str ) + { + if ( str.empty() ) + return std::wstring(); + + const auto size = MultiByteToWideChar( CP_UTF8, 0, &str[ 0 ], str.size(), nullptr, 0 ); + + std::wstring out( size, 0 ); + MultiByteToWideChar( CP_UTF8, 0, &str[ 0 ], str.size(), &out[ 0 ], size ); + + return out; + } + + std::vector String::Split( const std::string& str, const std::string& delimiter ) + { + std::vector vec; + size_t last = 0; + size_t next; + + while ( ( next = str.find( delimiter, last ) ) != std::string::npos ) + { + if ( auto s = str.substr( last, next - last ); !s.empty() ) + vec.push_back( s ); + last = next + 1; + } + + if ( const auto s = str.substr( last ); !s.empty() ) + vec.push_back( s ); + + return vec; + } + + std::string_view String::Lowercase( std::string& str ) + { + std::ranges::transform( str.begin(), str.end(), str.begin(), []( const unsigned char c ) + { + return std::tolower( c ); + } ); + + return str; + } + + std::string_view String::Uppercase( std::string& str ) + { + std::ranges::transform( str.begin(), str.end(), str.begin(), []( const unsigned char c ) + { + return std::toupper( c ); + } ); + + return str; + } +} diff --git a/l4d2/utilities/string/string.h b/l4d2/utilities/string/string.h new file mode 100644 index 0000000..f505000 --- /dev/null +++ b/l4d2/utilities/string/string.h @@ -0,0 +1,10 @@ +#pragma once + +namespace Hikari::String +{ + std::string ToUtf8( const std::wstring& str ); + std::wstring ToUnicode( const std::string& str ); + std::vector Split( const std::string& str, const std::string& delimiter ); + std::string_view Lowercase( std::string& str ); + std::string_view Uppercase( std::string& str ); +}