From 3e2da1eedf90017026a456e23129382c9229faa3 Mon Sep 17 00:00:00 2001 From: Milxnor Date: Sun, 5 Mar 2023 01:03:51 -0500 Subject: [PATCH] a lottt of stuff --- Project Reboot 3.0/Array.h | 2 + Project Reboot 3.0/BitArray.h | 17 + Project Reboot 3.0/BuildingActor.cpp | 94 + Project Reboot 3.0/BuildingActor.h | 21 + Project Reboot 3.0/BuildingSMActor.cpp | 7 + Project Reboot 3.0/BuildingSMActor.h | 18 + Project Reboot 3.0/BuildingWeapons.cpp | 2 +- .../ContainerAllocationPolicies.h | 29 + Project Reboot 3.0/CurveTable.h | 15 + Project Reboot 3.0/DataTable.h | 17 + .../DataTableFunctionLibrary.cpp | 30 + Project Reboot 3.0/DataTableFunctionLibrary.h | 21 + Project Reboot 3.0/FortGameModeAthena.cpp | 42 +- Project Reboot 3.0/FortInventory.cpp | 2 +- Project Reboot 3.0/FortInventory.h | 2 +- Project Reboot 3.0/FortItem.cpp | 2 +- Project Reboot 3.0/FortItemDefinition.cpp | 2 +- Project Reboot 3.0/FortKismetLibrary.cpp | 18 + Project Reboot 3.0/FortKismetLibrary.h | 14 + Project Reboot 3.0/FortLootPackage.cpp | 0 Project Reboot 3.0/FortLootPackage.h | 0 Project Reboot 3.0/FortPlayerController.cpp | 44 + Project Reboot 3.0/FortPlayerController.h | 5 + .../FortResourceItemDefinition.h | 8 + Project Reboot 3.0/FortWeapon.cpp | 9 + Project Reboot 3.0/FortWeapon.h | 8 + .../FortWeaponMeleeItemDefinition.h | 14 + Project Reboot 3.0/GameplayAbilitySpec.h | 4 +- Project Reboot 3.0/GameplayTagContainer.h | 67 + Project Reboot 3.0/Map.h | 36 + Project Reboot 3.0/NameTypes.h | 2 + Project Reboot 3.0/Object.cpp | 5 +- Project Reboot 3.0/Object.h | 2 +- Project Reboot 3.0/Project Reboot 3.0.vcxproj | 20 + .../Project Reboot 3.0.vcxproj.filters | 82 +- Project Reboot 3.0/Set.h | 45 + Project Reboot 3.0/SparseArray.h | 52 + Project Reboot 3.0/Tuple.h | 2 + Project Reboot 3.0/addresses.cpp | 97 +- Project Reboot 3.0/addresses.h | 4 + Project Reboot 3.0/dllmain.cpp | 26 +- Project Reboot 3.0/finder.h | 80 +- Project Reboot 3.0/hooking.h | 12 +- vendor/memcury.h | 2272 +++++++++-------- 44 files changed, 2086 insertions(+), 1165 deletions(-) create mode 100644 Project Reboot 3.0/BitArray.h create mode 100644 Project Reboot 3.0/BuildingActor.cpp create mode 100644 Project Reboot 3.0/BuildingSMActor.cpp create mode 100644 Project Reboot 3.0/ContainerAllocationPolicies.h create mode 100644 Project Reboot 3.0/CurveTable.h create mode 100644 Project Reboot 3.0/DataTable.h create mode 100644 Project Reboot 3.0/DataTableFunctionLibrary.cpp create mode 100644 Project Reboot 3.0/DataTableFunctionLibrary.h create mode 100644 Project Reboot 3.0/FortKismetLibrary.cpp create mode 100644 Project Reboot 3.0/FortKismetLibrary.h create mode 100644 Project Reboot 3.0/FortLootPackage.cpp create mode 100644 Project Reboot 3.0/FortLootPackage.h create mode 100644 Project Reboot 3.0/FortResourceItemDefinition.h create mode 100644 Project Reboot 3.0/FortWeapon.cpp create mode 100644 Project Reboot 3.0/FortWeaponMeleeItemDefinition.h create mode 100644 Project Reboot 3.0/GameplayTagContainer.h create mode 100644 Project Reboot 3.0/Map.h create mode 100644 Project Reboot 3.0/Set.h create mode 100644 Project Reboot 3.0/SparseArray.h create mode 100644 Project Reboot 3.0/Tuple.h diff --git a/Project Reboot 3.0/Array.h b/Project Reboot 3.0/Array.h index b4c282d..f903570 100644 --- a/Project Reboot 3.0/Array.h +++ b/Project Reboot 3.0/Array.h @@ -114,6 +114,8 @@ public: bool Remove(const int Index) { + // return false; + if (Index < ArrayNum) { if (Index != ArrayNum - 1) diff --git a/Project Reboot 3.0/BitArray.h b/Project Reboot 3.0/BitArray.h new file mode 100644 index 0000000..8d44396 --- /dev/null +++ b/Project Reboot 3.0/BitArray.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ContainerAllocationPolicies.h" + +class TBitArray +{ +private: + template + friend class TSparseArray; + template + friend class TSet; + +private: + TInlineAllocator<4>::ForElementType Data; + int NumBits; + int MaxBits; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/BuildingActor.cpp b/Project Reboot 3.0/BuildingActor.cpp new file mode 100644 index 0000000..4cfb53f --- /dev/null +++ b/Project Reboot 3.0/BuildingActor.cpp @@ -0,0 +1,94 @@ +#include "BuildingActor.h" + +#include "FortWeapon.h" +#include "BuildingSMActor.h" +#include "FortPlayerControllerAthena.h" +#include "FortPawn.h" +#include "FortWeaponMeleeItemDefinition.h" +#include "CurveTable.h" +#include "DataTable.h" +#include "FortResourceItemDefinition.h" +#include "FortKismetLibrary.h" +#include "DataTableFunctionLibrary.h" + +void ABuildingActor::OnDamageServerHook(ABuildingActor* BuildingActor, float Damage, FGameplayTagContainer DamageTags, + FVector Momentum, /* FHitResult */ __int64 HitInfo, APlayerController* InstigatedBy, AActor* DamageCauser, + /* FGameplayEffectContextHandle */ __int64 EffectContext) +{ + LOG_INFO(LogDev, "Befor3e"); + + auto BuildingSMActor = Cast(BuildingActor); + auto PlayerController = Cast(InstigatedBy); + auto Pawn = PlayerController ? PlayerController->GetMyFortPawn() : nullptr; + auto Weapon = Cast(DamageCauser); + + if (!BuildingSMActor || !PlayerController || !Pawn || !Weapon) + return OnDamageServerOriginal(BuildingActor, Damage, DamageTags, Momentum, HitInfo, InstigatedBy, DamageCauser, EffectContext); + + auto WeaponData = Cast(Weapon->GetWeaponData()); + + if (!WeaponData) + return OnDamageServerOriginal(BuildingActor, Damage, DamageTags, Momentum, HitInfo, InstigatedBy, DamageCauser, EffectContext); + + auto ResourceCount = 0; + UFortResourceItemDefinition* ItemDef = UFortKismetLibrary::K2_GetResourceItemDefinition(BuildingSMActor->GetResourceType()); + + static auto BuildingResourceAmountOverrideOffset = BuildingSMActor->GetOffset("BuildingResourceAmountOverride"); + auto& BuildingResourceAmountOverride = BuildingSMActor->Get(BuildingResourceAmountOverrideOffset); + + if (BuildingResourceAmountOverride.RowName.IsValid()) + { + // auto AssetManager = Cast(GEngine->AssetManager); + // auto GameState = Cast(GetWorld()->GetGGetGameStateAthena); + UCurveTable* CurveTable = nullptr; // GameState->CurrentPlaylistInfo.BasePlaylist ? GameState->CurrentPlaylistInfo.BasePlaylist->ResourceRates.Get() : nullptr; + + LOG_INFO(LogDev, "Before1"); + + if (!CurveTable) + CurveTable = FindObject("/Game/Athena/Balance/DataTables/AthenaResourceRates.AthenaResourceRates"); + + { + // auto curveMap = ((UDataTable*)CurveTable)->GetRowMap(); + + LOG_INFO(LogDev, "Before {}", __int64(CurveTable)); + + float Out; + FString ContextString; + UDataTableFunctionLibrary::EvaluateCurveTableRow(CurveTable, BuildingResourceAmountOverride.RowName, 0, ContextString, nullptr, &Out); + + LOG_INFO(LogDev, "Out: {}", Out); + + auto DamageThatWillAffect = Damage; + + auto skid = Out / (BuildingSMActor->GetMaxHealth() / DamageThatWillAffect); + + ResourceCount = round(skid); // almost right + } + } + + if (!ItemDef || ResourceCount <= 0) + { + return OnDamageServerOriginal(BuildingActor, Damage, DamageTags, Momentum, HitInfo, InstigatedBy, DamageCauser, EffectContext); + // return OnDamageServer(BuildingActor, Damage, DamageTags, Momentum, HitInfo, InstigatedBy, DamageCauser, EffectContext); + } + + bool bIsWeakspot = Damage == 100.0f; + PlayerController->ClientReportDamagedResourceBuilding(BuildingSMActor, BuildingSMActor->GetResourceType(), ResourceCount, false, bIsWeakspot); + + if (ResourceCount > 0) + { + bool bShouldUpdate = false; + PlayerController->GetWorldInventory()->AddItem(ItemDef, &bShouldUpdate, ResourceCount); + + if (bShouldUpdate) + PlayerController->GetWorldInventory()->Update(); + } + + return OnDamageServerOriginal(BuildingActor, Damage, DamageTags, Momentum, HitInfo, InstigatedBy, DamageCauser, EffectContext); +} + +UClass* ABuildingActor::StaticClass() +{ + static auto Class = FindObject(L"/Script/FortniteGame.BuildingActor"); + return Class; +} \ No newline at end of file diff --git a/Project Reboot 3.0/BuildingActor.h b/Project Reboot 3.0/BuildingActor.h index fbde64e..d89b9cb 100644 --- a/Project Reboot 3.0/BuildingActor.h +++ b/Project Reboot 3.0/BuildingActor.h @@ -2,6 +2,9 @@ #include "Actor.h" #include "reboot.h" // we want to prevent this but im to lazy to make cpp file +#include "PlayerController.h" + +#include "GameplayTagContainer.h" class ABuildingActor : public AActor { @@ -18,4 +21,22 @@ public: static auto fn = FindObject("/Script/FortniteGame.BuildingActor.InitializeKismetSpawnedBuildingActor"); this->ProcessEvent(fn, &IBAParams); } + + float GetMaxHealth() + { + float MaxHealth = 0; + static auto fn = FindObject("/Script/FortniteGame.BuildingActor.GetMaxHealth"); + this->ProcessEvent(fn, &MaxHealth); + return MaxHealth; + } + + static inline void (*OnDamageServerOriginal)(ABuildingActor* BuildingActor, float Damage, FGameplayTagContainer DamageTags, + FVector Momentum, /* FHitResult */ __int64 HitInfo, APlayerController* InstigatedBy, AActor* DamageCauser, + /* FGameplayEffectContextHandle */ __int64 EffectContext); + + static void OnDamageServerHook(ABuildingActor* BuildingActor, float Damage, FGameplayTagContainer DamageTags, + FVector Momentum, /* FHitResult */ __int64 HitInfo, APlayerController* InstigatedBy, AActor* DamageCauser, + /* FGameplayEffectContextHandle */ __int64 EffectContext); + + static UClass* StaticClass(); }; \ No newline at end of file diff --git a/Project Reboot 3.0/BuildingSMActor.cpp b/Project Reboot 3.0/BuildingSMActor.cpp new file mode 100644 index 0000000..b2f45c9 --- /dev/null +++ b/Project Reboot 3.0/BuildingSMActor.cpp @@ -0,0 +1,7 @@ +#include "BuildingSMActor.h" + +UClass* ABuildingSMActor::StaticClass() +{ + static auto Class = FindObject(L"/Script/FortniteGame.BuildingSMActor"); + return Class; +} \ No newline at end of file diff --git a/Project Reboot 3.0/BuildingSMActor.h b/Project Reboot 3.0/BuildingSMActor.h index b591077..e19cfef 100644 --- a/Project Reboot 3.0/BuildingSMActor.h +++ b/Project Reboot 3.0/BuildingSMActor.h @@ -3,6 +3,16 @@ #include "BuildingActor.h" #include "PlayerState.h" +enum class EFortResourceType : uint8_t +{ + Wood = 0, + Stone = 1, + Metal = 2, + Permanite = 3, + None = 4, + EFortResourceType_MAX = 5 +}; + class ABuildingSMActor : public ABuildingActor { public: @@ -32,4 +42,12 @@ public: static auto CurrentBuildingLevelOffset = GetOffset("CurrentBuildingLevel"); return Get(CurrentBuildingLevelOffset); } + + EFortResourceType& GetResourceType() + { + static auto ResourceTypeOffset = GetOffset("ResourceType"); + return Get(ResourceTypeOffset); + } + + static UClass* StaticClass(); }; \ No newline at end of file diff --git a/Project Reboot 3.0/BuildingWeapons.cpp b/Project Reboot 3.0/BuildingWeapons.cpp index 99a5a82..48823b1 100644 --- a/Project Reboot 3.0/BuildingWeapons.cpp +++ b/Project Reboot 3.0/BuildingWeapons.cpp @@ -4,6 +4,6 @@ UClass* AFortWeap_EditingTool::StaticClass() { - static auto Class = FindObject("/Script/FortniteGame.FortWeap_EditingTool"); + static auto Class = FindObject(L"/Script/FortniteGame.FortWeap_EditingTool"); return Class; } \ No newline at end of file diff --git a/Project Reboot 3.0/ContainerAllocationPolicies.h b/Project Reboot 3.0/ContainerAllocationPolicies.h new file mode 100644 index 0000000..8bc5f96 --- /dev/null +++ b/Project Reboot 3.0/ContainerAllocationPolicies.h @@ -0,0 +1,29 @@ +#pragma once + +template +class TInlineAllocator +{ +private: + template + struct alignas(Alignment) TAlignedBytes + { + unsigned char Pad[Size]; + }; + + template + struct TTypeCompatibleBytes : public TAlignedBytes + { + }; + +public: + template + class ForElementType + { + friend class TBitArray; + + private: + TTypeCompatibleBytes InlineData[NumElements]; + + ElementType* SecondaryData; + }; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/CurveTable.h b/Project Reboot 3.0/CurveTable.h new file mode 100644 index 0000000..c264c4a --- /dev/null +++ b/Project Reboot 3.0/CurveTable.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Object.h" +#include "NameTypes.h" + +class UCurveTable : public UObject +{ +public: +}; + +struct FCurveTableRowHandle +{ + UCurveTable* CurveTable; + FName RowName; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/DataTable.h b/Project Reboot 3.0/DataTable.h new file mode 100644 index 0000000..b70aeec --- /dev/null +++ b/Project Reboot 3.0/DataTable.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Object.h" + +#include "Map.h" + +class UDataTable : public UObject +{ +public: + template + TMap GetRowMap() + { + static auto RowStructOffset = FindOffsetStruct("/Script/Engine.DataTable", "RowStruct"); + + return *(TMap*)(__int64(this) + (RowStructOffset + sizeof(UObject*))); // because after rowstruct is rowmap + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/DataTableFunctionLibrary.cpp b/Project Reboot 3.0/DataTableFunctionLibrary.cpp new file mode 100644 index 0000000..5c328e9 --- /dev/null +++ b/Project Reboot 3.0/DataTableFunctionLibrary.cpp @@ -0,0 +1,30 @@ +#include "DataTableFunctionLibrary.h" + +#include "reboot.h" + +void UDataTableFunctionLibrary::EvaluateCurveTableRow(UCurveTable* CurveTable, FName RowName, float InXY, + FString ContextString, EEvaluateCurveTableResult* OutResult, float* OutXY) +{ + static auto fn = FindObject("/Script/Engine.DataTableFunctionLibrary.EvaluateCurveTableRow"); + + float wtf{}; + EEvaluateCurveTableResult wtf1{}; + + struct { UCurveTable* CurveTable; FName RowName; float InXY; EEvaluateCurveTableResult OutResult; float OutXY; FString ContextString; } + UDataTableFunctionLibrary_EvaluateCurveTableRow_Params{CurveTable, RowName, InXY, wtf1, wtf, ContextString}; + + static auto DefaultClass = StaticClass(); + DefaultClass->ProcessEvent(fn, &UDataTableFunctionLibrary_EvaluateCurveTableRow_Params); + + if (OutResult) + *OutResult = wtf1; + + if (OutXY) + *OutXY = wtf; +} + +UClass* UDataTableFunctionLibrary::StaticClass() +{ + static auto Class = FindObject("/Script/Engine.DataTableFunctionLibrary"); + return Class; +} \ No newline at end of file diff --git a/Project Reboot 3.0/DataTableFunctionLibrary.h b/Project Reboot 3.0/DataTableFunctionLibrary.h new file mode 100644 index 0000000..e218ecd --- /dev/null +++ b/Project Reboot 3.0/DataTableFunctionLibrary.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Object.h" +#include "CurveTable.h" +#include "UnrealString.h" + +enum class EEvaluateCurveTableResult : uint8_t +{ + RowFound = 0, + RowNotFound = 1, + EEvaluateCurveTableResult_MAX = 2 +}; + +class UDataTableFunctionLibrary : public UObject +{ +public: + static void EvaluateCurveTableRow(UCurveTable* CurveTable, FName RowName, float InXY, + FString ContextString, EEvaluateCurveTableResult* OutResult, float* OutXY); + + static UClass* StaticClass(); +}; \ No newline at end of file diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index 5029bc7..f77b4bf 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -30,6 +30,7 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game else { GameState->Get("CurrentPlaylistData") = Playlist; + GameState->OnRep_CurrentPlaylistInfo(); } }; @@ -121,7 +122,8 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game GameState->OnRep_GamePhase(); } - auto Playlist = FindObject("/Game/Athena/Playlists/Playground/Playlist_Playground.Playlist_Playground"); // FindObject("/Game/Athena/Playlists/Playlist_DefaultSolo.Playlist_DefaultSolo"); + auto Playlist = FindObject("/Game/Athena/Playlists/Playlist_DefaultSolo.Playlist_DefaultSolo"); + // FindObject("/Game/Athena/Playlists/Playground/Playlist_Playground.Playlist_Playground"); SetPlaylist(Playlist); GameState->Get("WarmupCountdownEndTime") = TimeSeconds + Duration; @@ -196,34 +198,41 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena PlayerStateAthena->ProcessEvent(OnRep_bHasStartedPlayingFn); } - // if (false) + if (false) { - static auto GameplayAbilitySet = FindObject("/Game/Abilities/Player/Generic/Traits/DefaultPlayer/GAS_AthenaPlayer.GAS_AthenaPlayer"); + static auto GameplayAbilitySet = FindObject("/Game/Abilities/Player/Generic/Traits/DefaultPlayer/GAS_AthenaPlayer.GAS_AthenaPlayer") ? + FindObject("/Game/Abilities/Player/Generic/Traits/DefaultPlayer/GAS_AthenaPlayer.GAS_AthenaPlayer") : + FindObject("/Game/Abilities/Player/Generic/Traits/DefaultPlayer/GAS_DefaultPlayer.GAS_DefaultPlayer"); - static auto GameplayAbilitiesOffset = GameplayAbilitySet->GetOffset("GameplayAbilities"); - auto GameplayAbilities = GameplayAbilitySet->GetPtr>(GameplayAbilitiesOffset); - - for (int i = 0; i < GameplayAbilities->Num(); i++) + if (GameplayAbilitySet) { - UClass* AbilityClass = GameplayAbilities->At(i); + static auto GameplayAbilitiesOffset = GameplayAbilitySet->GetOffset("GameplayAbilities"); + auto GameplayAbilities = GameplayAbilitySet->GetPtr>(GameplayAbilitiesOffset); - // LOG_INFO(LogDev, "AbilityClass {}", __int64(AbilityClass)); + for (int i = 0; i < GameplayAbilities->Num(); i++) + { + UClass* AbilityClass = GameplayAbilities->At(i); - if (!AbilityClass) - continue; + LOG_INFO(LogDev, "AbilityClass {}", __int64(AbilityClass)); - // LOG_INFO(LogDev, "AbilityClass Name {}", AbilityClass->GetFullName()); + if (!AbilityClass) + continue; - auto DefaultAbility = AbilityClass->CreateDefaultObject(); + // LOG_INFO(LogDev, "AbilityClass Name {}", AbilityClass->GetFullName()); - // LOG_INFO(LogDev, "DefaultAbility {}", __int64(DefaultAbility)); - // LOG_INFO(LogDev, "DefaultAbility Name {}", DefaultAbility->GetFullName()); + // LOG_INFO(LogDev, "DefaultAbility {}", __int64(DefaultAbility)); + // LOG_INFO(LogDev, "DefaultAbility Name {}", DefaultAbility->GetFullName()); - PlayerStateAthena->GetAbilitySystemComponent()->GiveAbilityEasy(AbilityClass); + PlayerStateAthena->GetAbilitySystemComponent()->GiveAbilityEasy(AbilityClass); + } } } + static auto GameMemberInfoArrayOffset = GameState->GetOffset("GameMemberInfoArray", false); + // if (false) + // if (GameMemberInfoArrayOffset != 0) + if (Engine_Version >= 423) { struct FUniqueNetIdRepl { @@ -268,7 +277,6 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena static auto GameMemberInfoArray_MembersOffset = 0x0108; - static auto GameMemberInfoArrayOffset = GameState->GetOffset("GameMemberInfoArray"); auto GameMemberInfoArray = GameState->GetPtr(GameMemberInfoArrayOffset); ((TArray*)(__int64(GameMemberInfoArray) + GameMemberInfoArray_MembersOffset))->Add(*GameMemberInfo, GameMemberInfoStructSize); diff --git a/Project Reboot 3.0/FortInventory.cpp b/Project Reboot 3.0/FortInventory.cpp index acc508d..616f871 100644 --- a/Project Reboot 3.0/FortInventory.cpp +++ b/Project Reboot 3.0/FortInventory.cpp @@ -29,7 +29,7 @@ std::pair, std::vector> AFortInventory::AddI // NewItemInstance->GetItemEntry()->GetItemDefinition() = ItemDefinition; - static auto FortItemEntryStruct = FindObject("/Script/FortniteGame.FortItemEntry"); + static auto FortItemEntryStruct = FindObject(L"/Script/FortniteGame.FortItemEntry"); static auto FortItemEntrySize = *(int*)(__int64(FortItemEntryStruct) + Offsets::PropertiesSize); // LOG_INFO(LogDev, "FortItemEntryStruct {}", __int64(FortItemEntryStruct)); diff --git a/Project Reboot 3.0/FortInventory.h b/Project Reboot 3.0/FortInventory.h index 399c867..192ad60 100644 --- a/Project Reboot 3.0/FortInventory.h +++ b/Project Reboot 3.0/FortInventory.h @@ -53,7 +53,7 @@ public: ProcessEvent(HandleInventoryLocalUpdateFn); } - FORCENOINLINE void Update(bool bMarkArrayDirty = false) + FORCENOINLINE void Update(bool bMarkArrayDirty = true) { HandleInventoryLocalUpdate(); diff --git a/Project Reboot 3.0/FortItem.cpp b/Project Reboot 3.0/FortItem.cpp index 2578306..5f5fce1 100644 --- a/Project Reboot 3.0/FortItem.cpp +++ b/Project Reboot 3.0/FortItem.cpp @@ -2,6 +2,6 @@ void UFortItem::SetOwningControllerForTemporaryItem(UObject* Controller) { - static auto SOCFTIFn = FindObject("/Script/FortniteGame.FortItem.SetOwningControllerForTemporaryItem"); + static auto SOCFTIFn = FindObject(L"/Script/FortniteGame.FortItem.SetOwningControllerForTemporaryItem"); this->ProcessEvent(SOCFTIFn, &Controller); } \ No newline at end of file diff --git a/Project Reboot 3.0/FortItemDefinition.cpp b/Project Reboot 3.0/FortItemDefinition.cpp index f33d4a7..838e7ac 100644 --- a/Project Reboot 3.0/FortItemDefinition.cpp +++ b/Project Reboot 3.0/FortItemDefinition.cpp @@ -2,7 +2,7 @@ UFortItem* UFortItemDefinition::CreateTemporaryItemInstanceBP(int Count, int Level) { - static auto CreateTemporaryItemInstanceBPFunction = FindObject("/Script/FortniteGame.FortItemDefinition.CreateTemporaryItemInstanceBP"); + static auto CreateTemporaryItemInstanceBPFunction = FindObject(L"/Script/FortniteGame.FortItemDefinition.CreateTemporaryItemInstanceBP"); struct { int Count; int Level; UFortItem* ReturnValue; } CreateTemporaryItemInstanceBP_Params{ Count, 1 }; ProcessEvent(CreateTemporaryItemInstanceBPFunction, &CreateTemporaryItemInstanceBP_Params); diff --git a/Project Reboot 3.0/FortKismetLibrary.cpp b/Project Reboot 3.0/FortKismetLibrary.cpp new file mode 100644 index 0000000..6eb7837 --- /dev/null +++ b/Project Reboot 3.0/FortKismetLibrary.cpp @@ -0,0 +1,18 @@ +#include "FortKismetLibrary.h" + +UFortResourceItemDefinition* UFortKismetLibrary::K2_GetResourceItemDefinition(EFortResourceType ResourceType) +{ + static auto fn = FindObject(L"/Script/FortniteGame.FortKismetLibrary.K2_GetResourceItemDefinition"); + + struct { EFortResourceType type; UFortResourceItemDefinition* ret; } params{ResourceType}; + + static auto DefaultClass = StaticClass(); + DefaultClass->ProcessEvent(fn, ¶ms); + return params.ret; +} + +UClass* UFortKismetLibrary::StaticClass() +{ + static auto ptr = FindObject(L"/Script/FortniteGame.FortKismetLibrary"); + return ptr; +} \ No newline at end of file diff --git a/Project Reboot 3.0/FortKismetLibrary.h b/Project Reboot 3.0/FortKismetLibrary.h new file mode 100644 index 0000000..16f7bc4 --- /dev/null +++ b/Project Reboot 3.0/FortKismetLibrary.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Object.h" + +#include "FortResourceItemDefinition.h" +#include "BuildingSMActor.h" + +class UFortKismetLibrary : public UObject +{ +public: + static UFortResourceItemDefinition* K2_GetResourceItemDefinition(EFortResourceType ResourceType); + + static UClass* StaticClass(); +}; \ No newline at end of file diff --git a/Project Reboot 3.0/FortLootPackage.cpp b/Project Reboot 3.0/FortLootPackage.cpp new file mode 100644 index 0000000..e69de29 diff --git a/Project Reboot 3.0/FortLootPackage.h b/Project Reboot 3.0/FortLootPackage.h new file mode 100644 index 0000000..e69de29 diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index 2907dae..57c5370 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -8,6 +8,17 @@ #include "BuildingWeapons.h" #include "ActorComponent.h" +#include "FortPlayerStateAthena.h" + +void AFortPlayerController::ClientReportDamagedResourceBuilding(ABuildingSMActor* BuildingSMActor, EFortResourceType PotentialResourceType, int PotentialResourceCount, bool bDestroyed, bool bJustHitWeakspot) +{ + static auto fn = FindObject(L"/Script/FortniteGame.FortPlayerController.ClientReportDamagedResourceBuilding"); + + struct { ABuildingSMActor* BuildingSMActor; EFortResourceType PotentialResourceType; int PotentialResourceCount; bool bDestroyed; bool bJustHitWeakspot; } + AFortPlayerController_ClientReportDamagedResourceBuilding_Params{BuildingSMActor, PotentialResourceType, PotentialResourceCount, bDestroyed, bJustHitWeakspot}; + + this->ProcessEvent(fn, &AFortPlayerController_ClientReportDamagedResourceBuilding_Params); +} void AFortPlayerController::ServerAttemptAircraftJumpHook(AFortPlayerController* PC, FRotator ClientRotation) { @@ -96,6 +107,39 @@ void AFortPlayerController::ServerCreateBuildingActorHook(AFortPlayerController* BuildingActor->InitializeBuildingActor(PlayerController, BuildingActor, true); } +void AFortPlayerController::ServerPlayEmoteItemHook(AFortPlayerController* PlayerController, UObject* EmoteAsset) +{ + auto PlayerState = (AFortPlayerStateAthena*)PlayerController->GetPlayerState(); + auto Pawn = PlayerController->GetPawn(); + + if (!EmoteAsset || !PlayerState || !Pawn) + return; + + UObject* AbilityToUse = nullptr; + + if (!AbilityToUse) + { + static auto EmoteGameplayAbilityDefault = FindObject("/Game/Abilities/Emotes/GAB_Emote_Generic.Default__GAB_Emote_Generic_C"); + AbilityToUse = EmoteGameplayAbilityDefault; + } + + if (!AbilityToUse) + return; + + + int outHandle = 0; + + FGameplayAbilitySpecHandle Handle{}; + Handle.GenerateNewHandle(); + + FGameplayAbilitySpec* Spec = MakeNewSpec((UClass*)AbilityToUse, EmoteAsset, true); + + static unsigned int* (*GiveAbilityAndActivateOnce)(UAbilitySystemComponent * ASC, int* outHandle, __int64 Spec) + = decltype(GiveAbilityAndActivateOnce)(Addresses::GiveAbilityAndActivateOnce); + + GiveAbilityAndActivateOnce(PlayerState->GetAbilitySystemComponent(), &outHandle, __int64(Spec)); +} + void AFortPlayerController::ServerBeginEditingBuildingActorHook(AFortPlayerController* PlayerController, ABuildingSMActor* BuildingActorToEdit) { if (!BuildingActorToEdit || !BuildingActorToEdit->IsPlayerPlaced()) diff --git a/Project Reboot 3.0/FortPlayerController.h b/Project Reboot 3.0/FortPlayerController.h index b270e44..bf5833c 100644 --- a/Project Reboot 3.0/FortPlayerController.h +++ b/Project Reboot 3.0/FortPlayerController.h @@ -12,6 +12,8 @@ struct FCreateBuildingActorData { uint32_t BuildingClassHandle; FVector BuildLoc class AFortPlayerController : public APlayerController { public: + void ClientReportDamagedResourceBuilding(ABuildingSMActor* BuildingSMActor, EFortResourceType PotentialResourceType, int PotentialResourceCount, bool bDestroyed, bool bJustHitWeakspot); + AFortInventory*& GetWorldInventory() { static auto WorldInventoryOffset = GetOffset("WorldInventory"); @@ -24,6 +26,7 @@ public: return Get(MyFortPawnOffset); } + static UClass* StaticClass() { static auto Class = FindObject("/Script/FortniteGame.FortPlayerController"); @@ -47,6 +50,8 @@ public: static void ServerAttemptAircraftJumpHook(AFortPlayerController* PC, FRotator ClientRotation); static void ServerCreateBuildingActorHook(AFortPlayerController* PlayerController, FCreateBuildingActorData CreateBuildingData); + static void ServerPlayEmoteItemHook(AFortPlayerController* PlayerController, UObject* EmoteAsset); + static void ServerBeginEditingBuildingActorHook(AFortPlayerController* PlayerController, ABuildingSMActor* BuildingActorToEdit); static void ServerEditBuildingActorHook(AFortPlayerController* PlayerController, ABuildingSMActor* BuildingActorToEdit, UClass* NewBuildingClass, int RotationIterations, char bMirrored); static void ServerEndEditingBuildingActorHook(AFortPlayerController* PlayerController, ABuildingSMActor* BuildingActorToStopEditing); diff --git a/Project Reboot 3.0/FortResourceItemDefinition.h b/Project Reboot 3.0/FortResourceItemDefinition.h new file mode 100644 index 0000000..8ffa742 --- /dev/null +++ b/Project Reboot 3.0/FortResourceItemDefinition.h @@ -0,0 +1,8 @@ +#pragma once + +#include "FortWorldItemDefinition.h" + +class UFortResourceItemDefinition : public UFortWorldItemDefinition +{ +public: +}; \ No newline at end of file diff --git a/Project Reboot 3.0/FortWeapon.cpp b/Project Reboot 3.0/FortWeapon.cpp new file mode 100644 index 0000000..26c1efa --- /dev/null +++ b/Project Reboot 3.0/FortWeapon.cpp @@ -0,0 +1,9 @@ +#include "FortWeapon.h" + +#include "reboot.h" + +UClass* AFortWeapon::StaticClass() +{ + static auto Class = FindObject(L"/Script/FortniteGame.FortWeapon"); + return Class; +} \ No newline at end of file diff --git a/Project Reboot 3.0/FortWeapon.h b/Project Reboot 3.0/FortWeapon.h index be9ba7e..c43ca19 100644 --- a/Project Reboot 3.0/FortWeapon.h +++ b/Project Reboot 3.0/FortWeapon.h @@ -4,5 +4,13 @@ class AFortWeapon : public AActor { +public: + template + T* GetWeaponData() + { + static auto WeaponDataOffset = GetOffset("WeaponData"); + return Get(WeaponDataOffset); + } + static UClass* StaticClass(); }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortWeaponMeleeItemDefinition.h b/Project Reboot 3.0/FortWeaponMeleeItemDefinition.h new file mode 100644 index 0000000..a9567e5 --- /dev/null +++ b/Project Reboot 3.0/FortWeaponMeleeItemDefinition.h @@ -0,0 +1,14 @@ +#pragma once + +#include "FortWeaponItemDefinition.h" +#include "reboot.h" + +class UFortWeaponMeleeItemDefinition : public UFortWeaponItemDefinition +{ +public: + static UClass* StaticClass() + { + static auto Class = FindObject("/Script/FortniteGame.FortWeaponMeleeItemDefinition"); + return Class; + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/GameplayAbilitySpec.h b/Project Reboot 3.0/GameplayAbilitySpec.h index 9275680..154c431 100644 --- a/Project Reboot 3.0/GameplayAbilitySpec.h +++ b/Project Reboot 3.0/GameplayAbilitySpec.h @@ -37,7 +37,7 @@ struct FGameplayAbilitySpec : FFastArraySerializerItem } }; -static FGameplayAbilitySpec* MakeNewSpec(UClass* GameplayAbilityClass, UObject* SourceObject = nullptr) +static FGameplayAbilitySpec* MakeNewSpec(UClass* GameplayAbilityClass, UObject* SourceObject = nullptr, bool bAlreadyIsDefault = false) { auto NewSpec = Alloc(FGameplayAbilitySpec::GetStructSize()); @@ -55,7 +55,7 @@ static FGameplayAbilitySpec* MakeNewSpec(UClass* GameplayAbilityClass, UObject* ((FFastArraySerializerItem*)NewSpec)->ReplicationKey = -1; NewSpec->GetHandle().GenerateNewHandle(); - NewSpec->GetAbility() = GameplayAbilityClass->CreateDefaultObject(); + NewSpec->GetAbility() = bAlreadyIsDefault ? GameplayAbilityClass : GameplayAbilityClass->CreateDefaultObject(); *(int*)(__int64(NewSpec) + LevelOffset) = 0; *(int*)(__int64(NewSpec) + InputIDOffset) = -1; *(UObject**)(__int64(NewSpec) + SourceObjectOffset) = SourceObject; diff --git a/Project Reboot 3.0/GameplayTagContainer.h b/Project Reboot 3.0/GameplayTagContainer.h new file mode 100644 index 0000000..5a61fff --- /dev/null +++ b/Project Reboot 3.0/GameplayTagContainer.h @@ -0,0 +1,67 @@ +#pragma once + +#include "NameTypes.h" +#include "Array.h" + +struct FGameplayTag +{ + static const int npos = -1; // lol? + + FName TagName; +}; + +struct FGameplayTagContainer +{ + TArray GameplayTags; + TArray ParentTags; + + std::string ToStringSimple(bool bQuoted) + { + std::string RetString; + for (int i = 0; i < GameplayTags.Num(); ++i) + { + if (bQuoted) + { + RetString += ("\""); + } + RetString += GameplayTags.at(i).TagName.ToString(); + if (bQuoted) + { + RetString += ("\""); + } + + if (i < GameplayTags.Num() - 1) + { + RetString += (", "); + } + } + return RetString; + } + + int Find(const std::string& Str) + { + for (int i = 0; i < GameplayTags.Num(); i++) + { + if (GameplayTags.at(i).TagName.ToString() == Str) + return i; + } + + return FGameplayTag::npos; + } + + int Find(FGameplayTag& Tag) + { + return Find(Tag.TagName.ToString()); + } + + bool Contains(const std::string& Str) + { + return Find(Str) != FGameplayTag::npos; + } + + void Reset() + { + GameplayTags.Free(); + ParentTags.Free(); + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/Map.h b/Project Reboot 3.0/Map.h new file mode 100644 index 0000000..ce4fc98 --- /dev/null +++ b/Project Reboot 3.0/Map.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Set.h" + +// template +// using TPair = TTuple; + +template +class TPair +{ +public: + KeyType First; + ValueType Second; +}; + +template //, typename SetAllocator, typename KeyFuncs> +class TMapBase +{ +public: + typedef TPair ElementType; + + typedef TSet ElementSetType; + + ElementSetType Pairs; +}; + +template //, typename SetAllocator, typename KeyFuncs> +class TSortableMapBase : public TMapBase //, SetAllocator, KeyFuncs> +{ +}; + +template //,typename SetAllocator /*= FDefaultSetAllocator*/, typename KeyFuncs /*= TDefaultMapHashableKeyFuncs*/> +class TMap : public TSortableMapBase //, SetAllocator, KeyFuncs> +{ + +}; \ No newline at end of file diff --git a/Project Reboot 3.0/NameTypes.h b/Project Reboot 3.0/NameTypes.h index f5aaf08..1619816 100644 --- a/Project Reboot 3.0/NameTypes.h +++ b/Project Reboot 3.0/NameTypes.h @@ -13,4 +13,6 @@ struct FName uint32 Number; std::string ToString(); + + bool IsValid() { return ComparisonIndex.Value > 0; } }; \ No newline at end of file diff --git a/Project Reboot 3.0/Object.cpp b/Project Reboot 3.0/Object.cpp index 4407781..a540e85 100644 --- a/Project Reboot 3.0/Object.cpp +++ b/Project Reboot 3.0/Object.cpp @@ -5,7 +5,7 @@ #include "Class.h" #include "KismetSystemLibrary.h" -int UObject::GetOffset(const std::string& ChildName) +int UObject::GetOffset(const std::string& ChildName, bool bWarnIfNotFound) { auto getFNameOfProp = [](void* Property) -> FName* { @@ -47,7 +47,8 @@ int UObject::GetOffset(const std::string& ChildName) } } - LOG_WARN(LogFinder, "Unable to find0{}", ChildName); + if (bWarnIfNotFound) + LOG_WARN(LogFinder, "Unable to find0{}", ChildName); return 0; } diff --git a/Project Reboot 3.0/Object.h b/Project Reboot 3.0/Object.h index 02db50e..742a970 100644 --- a/Project Reboot 3.0/Object.h +++ b/Project Reboot 3.0/Object.h @@ -44,7 +44,7 @@ public: bool IsA(UClass* Other); - int GetOffset(const std::string& ChildName); + int GetOffset(const std::string& ChildName, bool bWarnIfNotFound = true); template T& Get(int Offset) { return *(T*)(__int64(this) + Offset); } diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index 84721b6..149b8f7 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -168,8 +168,11 @@ + + + @@ -178,8 +181,11 @@ + + + @@ -199,10 +205,15 @@ + + + + + @@ -213,19 +224,24 @@ + + + + + @@ -233,6 +249,7 @@ + @@ -246,8 +263,11 @@ + + + diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters index 1ee7b08..20772ba 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -83,6 +83,24 @@ Engine\Source\Runtime\Engine\Private\Components + + FortniteGame\Source\FortniteGame\Private\Items + + + FortniteGame\Source\FortniteGame\Private + + + FortniteGame\Source\FortniteGame\Private\Building + + + FortniteGame\Source\FortniteGame\Private\Weapons + + + FortniteGame\Source\FortniteGame\Private\Building + + + Engine\Source\Runtime\Engine\Private + @@ -234,10 +252,10 @@ Engine\Source\Runtime\Engine\Classes\Kismet - FortniteGame\Source\FortniteGame\Private\Building + FortniteGame\Source\FortniteGame\Public\Building - FortniteGame\Source\FortniteGame\Private\Building + FortniteGame\Source\FortniteGame\Public\Building Engine\Source\Runtime\Core\Public\Math @@ -254,6 +272,48 @@ Engine\Source\Runtime\Engine\Classes\Components + + FortniteGame\Source\FortniteGame\Public\Items + + + FortniteGame\Source\FortniteGame\Public + + + Engine\Source\Runtime\GameplayTags\Classes + + + FortniteGame\Source\FortniteGame\Public\Items + + + Engine\Source\Runtime\Engine\Classes\Engine + + + Engine\Source\Runtime\Engine\Classes\Engine + + + FortniteGame\Source\FortniteGame\Public\Items + + + Engine\Source\Runtime\Core\Public\Containers + + + Engine\Source\Runtime\Core\Public\Templates + + + Engine\Source\Runtime\Core\Public\Containers + + + Engine\Source\Runtime\Core\Public\Containers + + + Engine\Source\Runtime\Core\Public\Containers + + + Engine\Source\Runtime\Core\Public\Containers + + + Engine\Source\Runtime\Engine\Classes\Kismet + @@ -379,9 +439,6 @@ {a12cb364-3e34-454a-958f-b1fe54534353} - - {a6fd658c-6824-4186-b45e-2edf5d20eeae} - {6c0193a3-7b06-4298-99fe-a5a18be27d58} @@ -394,6 +451,21 @@ {eab3cd46-ced6-4e56-9fda-ed35ec9f9f64} + + {a6fd658c-6824-4186-b45e-2edf5d20eeae} + + + {1f8bc849-7da5-4917-a055-443ae102c233} + + + {3f2848b1-9e39-4c4b-b3bb-b4b5a8bb8360} + + + {247d4c62-23f7-4964-8879-5a0d65c44a73} + + + {31a7f342-8b7c-4594-a24d-c4dd5c9d230d} + diff --git a/Project Reboot 3.0/Set.h b/Project Reboot 3.0/Set.h new file mode 100644 index 0000000..e90d216 --- /dev/null +++ b/Project Reboot 3.0/Set.h @@ -0,0 +1,45 @@ +#pragma once + +#include "SparseArray.h" + +template +class TSetElement +{ +public: + ElementType Value; + mutable int32 HashNextId; + mutable int32 HashIndex; + + TSetElement(ElementType InValue, int32 InHashNextId, int32 InHashIndex) + : Value(InValue) + , HashNextId(InHashNextId) + , HashIndex(InHashIndex) + { + } + + FORCEINLINE TSetElement& operator=(const TSetElement& Other) + { + Value = Other.Value; + } + + FORCEINLINE bool operator==(const TSetElement& Other) const + { + return Value == Other.Value; + } + FORCEINLINE bool operator!=(const TSetElement& Other) const + { + return Value != Other.Value; + } +}; + +template //, typename KeyFuncs, typename Allocator> +class TSet +{ + typedef TSetElement ElementType; + typedef TSparseArrayElementOrListLink ArrayElementType; + + TSparseArray Elements; + + mutable TInlineAllocator<1>::ForElementType Hash; + mutable int32 HashSize; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/SparseArray.h b/Project Reboot 3.0/SparseArray.h new file mode 100644 index 0000000..4d6497e --- /dev/null +++ b/Project Reboot 3.0/SparseArray.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Array.h" +#include "BitArray.h" + +template +union TSparseArrayElementOrListLink +{ + TSparseArrayElementOrListLink(ElementType& InElement) + : ElementData(InElement) + { + } + TSparseArrayElementOrListLink(ElementType&& InElement) + : ElementData(InElement) + { + } + + TSparseArrayElementOrListLink(int32 InPrevFree, int32 InNextFree) + : PrevFreeIndex(InPrevFree) + , NextFreeIndex(InNextFree) + { + } + + TSparseArrayElementOrListLink operator=(const TSparseArrayElementOrListLink& Other) + { + return TSparseArrayElementOrListLink(Other.NextFreeIndex, Other.PrevFreeIndex); + } + + /** If the element is allocated, its value is stored here. */ + ElementType ElementData; + + struct + { + /** If the element isn't allocated, this is a link to the previous element in the array's free list. */ + int PrevFreeIndex; + + /** If the element isn't allocated, this is a link to the next element in the array's free list. */ + int NextFreeIndex; + }; +}; + +template +class TSparseArray +{ +public: + typedef TSparseArrayElementOrListLink FSparseArrayElement; + + TArray Data; + TBitArray AllocationFlags; + int32 FirstFreeIndex; + int32 NumFreeIndices; +}; \ No newline at end of file diff --git a/Project Reboot 3.0/Tuple.h b/Project Reboot 3.0/Tuple.h new file mode 100644 index 0000000..3f59c93 --- /dev/null +++ b/Project Reboot 3.0/Tuple.h @@ -0,0 +1,2 @@ +#pragma once + diff --git a/Project Reboot 3.0/addresses.cpp b/Project Reboot 3.0/addresses.cpp index 1057564..5385011 100644 --- a/Project Reboot 3.0/addresses.cpp +++ b/Project Reboot 3.0/addresses.cpp @@ -11,10 +11,13 @@ #include "AbilitySystemComponent.h" #include "finder.h" +#include + +#include "BuildingActor.h" void Addresses::SetupVersion() { - // if (false) + if (false) { Engine_Version = 423; Fortnite_Version = 10.40; @@ -32,6 +35,74 @@ void Addresses::SetupVersion() Fortnite_Version = 12.41; } + static FString(*GetEngineVersion)() = decltype(GetEngineVersion)(Memcury::Scanner::FindPattern("40 53 48 83 EC 20 48 8B D9 E8 ? ? ? ? 48 8B C8 41 B8 04 ? ? ? 48 8B D3").Get()); + + std::string FullVersion; + FString toFree; + + if (!GetEngineVersion) + { + auto VerStr = Memcury::Scanner::FindPattern("2B 2B 46 6F 72 74 6E 69 74 65 2B 52 65 6C 65 61 73 65 2D ? ? ? ?").Get(); + + // if (!VerStr) + + FullVersion = decltype(FullVersion.c_str())(VerStr); + Engine_Version = 500; + } + + else + { + toFree = GetEngineVersion(); + FullVersion = toFree.ToString(); + } + + std::string FNVer = FullVersion; + std::string EngineVer = FullVersion; + std::string CLStr; + int CL = 0; + + if (!FullVersion.contains("Live") && !FullVersion.contains(("Next")) && !FullVersion.contains(("Cert"))) + { + if (GetEngineVersion) + { + FNVer.erase(0, FNVer.find_last_of(("-"), FNVer.length() - 1) + 1); + EngineVer.erase(EngineVer.find_first_of(("-"), FNVer.length() - 1), 40); + + if (EngineVer.find_first_of(".") != EngineVer.find_last_of(".")) // this is for 4.21.0 and itll remove the .0 + EngineVer.erase(EngineVer.find_last_of((".")), 2); + + Engine_Version = std::stod(EngineVer) * 100; + } + + else + { + const std::regex base_regex(("-([0-9.]*)-")); + std::cmatch base_match; + + std::regex_search(FullVersion.c_str(), base_match, base_regex); + + FNVer = base_match[1]; + } + + Fortnite_Version = std::stod(FNVer); + + if (Fortnite_Version >= 16.00 && Fortnite_Version <= 18.40) + Engine_Version = 427; // 4.26.1; + } + + else + { + // TODO + // Engine_Version = FullVersion.contains(("Next")) ? 419 : 416; + CLStr = FullVersion.substr(FullVersion.find_first_of('-') + 1); + CLStr = CLStr.substr(0, CLStr.find_first_of('+')); + CL = std::stoi(CLStr); + Engine_Version = CL <= 3775276 ? 416 : 419; // std::stoi(FullVersion.substr(0, FullVersion.find_first_of('-'))); + Fortnite_Version = FullVersion.contains(("Next")) ? 2.4 : 1.8; + } + + // Fortnite_Season = std::floor(Fortnite_Version); + FFastArraySerializer::bNewSerializer = Fortnite_Version >= 8.30; } @@ -57,6 +128,8 @@ void Addresses::FindAll() Addresses::GiveAbility = FindGiveAbility(); Addresses::CantBuild = FindCantBuild(); Addresses::ReplaceBuildingActor = FindReplaceBuildingActor(); + Addresses::GiveAbilityAndActivateOnce = FindGiveAbilityAndActivateOnce(); + Addresses::OnDamageServer = FindOnDamageServer(); } void Addresses::Print() @@ -84,6 +157,8 @@ void Addresses::Print() LOG_INFO(LogDev, "GiveAbility: 0x{:x}", GiveAbility - Base); LOG_INFO(LogDev, "CantBuild: 0x{:x}", CantBuild - Base); LOG_INFO(LogDev, "ReplaceBuildingActor: 0x{:x}", ReplaceBuildingActor - Base); + LOG_INFO(LogDev, "GiveAbilityAndActivateOnce: 0x{:x}", GiveAbilityAndActivateOnce - Base); + LOG_INFO(LogDev, "OnDamageServer: 0x{:x}", OnDamageServer - Base); } void Offsets::FindAll() @@ -95,7 +170,7 @@ void Offsets::FindAll() if (Engine_Version == 420 || Engine_Version == 421) Offsets::Func = 0xB0; - else if (Engine_Version == 423 || Engine_Version == 424) + else if (Engine_Version >= 422 && Engine_Version <= 424) Offsets::Func = 0xC0; else if (Engine_Version == 425) Offsets::Func = 0xF0; @@ -146,7 +221,25 @@ void Addresses::Init() FMemory::Realloc = decltype(FMemory::Realloc)(Realloc); UAbilitySystemComponent::GiveAbilityOriginal = decltype(UAbilitySystemComponent::GiveAbilityOriginal)(GiveAbility); UAbilitySystemComponent::InternalTryActivateAbilityOriginal = decltype(UAbilitySystemComponent::InternalTryActivateAbilityOriginal)(InternalTryActivateAbility); + ABuildingActor::OnDamageServerOriginal = decltype(ABuildingActor::OnDamageServerOriginal)(OnDamageServer); // if (Engine_Version >= 421) ChunkedObjects = decltype(ChunkedObjects)(ObjectArray); // else UnchunkedObjects = decltype(UnchunkedObjects)(ObjectArray); +} + +std::vector Addresses::GetFunctionsToNull() +{ + std::vector toNull; + + if (Engine_Version == 420) + { + toNull.push_back(Memcury::Scanner::FindPattern("48 8B C4 57 48 81 EC ? ? ? ? 4C 8B 82 ? ? ? ? 48 8B F9 0F 29 70 E8 0F 29 78 D8").Get()); // Pawn Overlap + } + + if (Engine_Version == 421) + { + toNull.push_back(Memcury::Scanner::FindPattern("48 8B C4 48 89 58 08 48 89 70 10 57 48 81 EC ? ? ? ? 48 8B BA ? ? ? ? 48 8B DA 0F 29").Get()); // Pawn Overlap + } + + return toNull; } \ No newline at end of file diff --git a/Project Reboot 3.0/addresses.h b/Project Reboot 3.0/addresses.h index 2a247d5..22e9cda 100644 --- a/Project Reboot 3.0/addresses.h +++ b/Project Reboot 3.0/addresses.h @@ -30,11 +30,15 @@ namespace Addresses extern inline uint64 GiveAbility = 0; extern inline uint64 CantBuild = 0; extern inline uint64 ReplaceBuildingActor = 0; + extern inline uint64 GiveAbilityAndActivateOnce = 0; + extern inline uint64 OnDamageServer = 0; void SetupVersion(); // Finds Engine Version void FindAll(); void Print(); void Init(); + + std::vector GetFunctionsToNull(); } namespace Offsets diff --git a/Project Reboot 3.0/dllmain.cpp b/Project Reboot 3.0/dllmain.cpp index 349ade3..fb46ab3 100644 --- a/Project Reboot 3.0/dllmain.cpp +++ b/Project Reboot 3.0/dllmain.cpp @@ -9,6 +9,8 @@ #include "FortPlayerControllerAthena.h" #include "AbilitySystemComponent.h" +#include "Map.h" + enum ENetMode { NM_Standalone, @@ -56,12 +58,14 @@ DWORD WINAPI Main(LPVOID) static auto SwitchLevel = FindObject(L"/Script/Engine.PlayerController.SwitchLevel"); FString Level = Engine_Version < 424 ? L"Athena_Terrain" : Engine_Version >= 500 ? Engine_Version >= 501 ? L"Asteria_Terrain" : L"Artemis_Terrain" : L"Apollo_Terrain"; - // if (Hooking::MinHook::Hook((PVOID)Addresses::NoMCP, (PVOID)NoMCPHook, nullptr)) + if (Hooking::MinHook::Hook((PVOID)Addresses::NoMCP, (PVOID)NoMCPHook, nullptr)) { LOG_INFO(LogHook, "Hooking GetNetMode!"); Hooking::MinHook::Hook((PVOID)Addresses::GetNetMode, (PVOID)GetNetModeHook, nullptr); } + LOG_INFO(LogDev, "Size: 0x{:x}", sizeof(TMap)); + GetLocalPlayerController()->ProcessEvent(SwitchLevel, &Level); auto& LocalPlayers = GetLocalPlayers(); @@ -71,8 +75,16 @@ DWORD WINAPI Main(LPVOID) LocalPlayers.Remove(0); } + for (auto func : Addresses::GetFunctionsToNull()) + { + *(uint8_t*)func = 0xC3; + } + Hooking::MinHook::Hook(GameModeDefault, FindObject(L"/Script/Engine.GameMode.ReadyToStartMatch"), AFortGameModeAthena::Athena_ReadyToStartMatchHook, (PVOID*)&AFortGameModeAthena::Athena_ReadyToStartMatchOriginal, false); + + // return false; + Hooking::MinHook::Hook(GameModeDefault, FindObject(L"/Script/Engine.GameModeBase.SpawnDefaultPawnFor"), AGameModeBase::SpawnDefaultPawnForHook, nullptr, false); Hooking::MinHook::Hook(GameModeDefault, FindObject(L"/Script/Engine.GameModeBase.HandleStartingNewPlayer"), AFortGameModeAthena::Athena_HandleStartingNewPlayerHook, @@ -80,13 +92,15 @@ DWORD WINAPI Main(LPVOID) Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerExecuteInventoryItem"), AFortPlayerController::ServerExecuteInventoryItemHook, nullptr, false); + Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerPlayEmoteItem"), + AFortPlayerController::ServerPlayEmoteItemHook, nullptr, false); Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerCreateBuildingActor"), AFortPlayerController::ServerCreateBuildingActorHook, nullptr, false); - Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject("/Script/FortniteGame.FortPlayerController.ServerBeginEditingBuildingActor"), + Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerBeginEditingBuildingActor"), AFortPlayerController::ServerBeginEditingBuildingActorHook, nullptr, false); - Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject("/Script/FortniteGame.FortPlayerController.ServerEditBuildingActor"), + Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerEditBuildingActor"), AFortPlayerController::ServerEditBuildingActorHook, nullptr, false); - Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject("/Script/FortniteGame.FortPlayerController.ServerEndEditingBuildingActor"), + Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerController.ServerEndEditingBuildingActor"), AFortPlayerController::ServerEndEditingBuildingActorHook, nullptr, false); Hooking::MinHook::Hook(FortPlayerControllerAthenaDefault, FindObject(L"/Script/Engine.PlayerController.ServerAcknowledgePossession"), AFortPlayerControllerAthena::ServerAcknowledgePossessionHook, nullptr, false); @@ -108,8 +122,8 @@ DWORD WINAPI Main(LPVOID) Hooking::MinHook::Hook((PVOID)Addresses::KickPlayer, (PVOID)AGameSession::KickPlayerHook, (PVOID*)&AGameSession::KickPlayerOriginal); Hooking::MinHook::Hook((PVOID)Addresses::TickFlush, (PVOID)UNetDriver::TickFlushHook, (PVOID*)&UNetDriver::TickFlushOriginal); - - Hooking::MinHook::Hook((PVOID)Addresses::CollectGarbage, (PVOID)CollectGarbageHook, nullptr); + Hooking::MinHook::Hook((PVOID)Addresses::OnDamageServer, (PVOID)ABuildingActor::OnDamageServerHook, (PVOID*)&ABuildingActor::OnDamageServerOriginal); + // Hooking::MinHook::Hook((PVOID)Addresses::CollectGarbage, (PVOID)CollectGarbageHook, nullptr); Hooking::MinHook::Hook((PVOID)Addresses::PickTeam, (PVOID)AFortGameModeAthena::Athena_PickTeamHook, nullptr); srand(time(0)); diff --git a/Project Reboot 3.0/finder.h b/Project Reboot 3.0/finder.h index 6f9f45e..995e7a1 100644 --- a/Project Reboot 3.0/finder.h +++ b/Project Reboot 3.0/finder.h @@ -74,15 +74,37 @@ static inline uint64 FindKickPlayer() { // return Memcury::Scanner::FindPattern("48 89 5C 24 08 48 89 74 24 10 57 48 83 EC ? 49 8B F0 48 8B DA 48 85 D2").Get(); // 12.41 + uint64 Ret = 0; + auto Addr = Memcury::Scanner::FindStringRef(L"Validation Failure: %s. kicking %s", false); if (Addr.Get()) { - return FindBytes(Addr, { 0x40, 0x55 }, 2000, 0, true); + Ret = Addr.Get() ? FindBytes(Addr, { 0x40, 0x55 }, 1000, 0, true) : Ret; + + if (!Ret) + Ret = Addr.Get() ? FindBytes(Addr, { 0x40, 0x53 }, 2000, 0, true) : Ret; } - auto Addr2 = Memcury::Scanner::FindStringRef(L"KickPlayer %s Reason %s"); - return FindBytes(Addr, { 0x48, 0x89, 0x5C }, 2000, 0, true); + if (Ret) + return Ret; + + auto Addr2 = Memcury::Scanner::FindStringRef(L"Failed to kick player"); // L"KickPlayer %s Reason %s" + Ret = Addr2.Get() ? FindBytes(Addr2, { 0x48, 0x89, 0x5C }, 2000, 0, true) : Ret; // s12?? + // Ret = Addr2.Get() ? FindBytes(Addr2, { 0x48, 0x8B, 0xC4 }, 2000, 0, true) : Ret; + + if (Ret) + return Ret; + + /* auto Addr3 = Memcury::Scanner::FindStringRef(L"Game already ended."); + Ret = Addr3.Get() ? FindBytes(Addr3, { 0x48, 0x89, 0x5C }, 2000, 0, true) : Ret; + + if (Ret) + return Ret; */ + + Ret = Memcury::Scanner::FindPattern("40 53 41 56 48 81 EC ? ? ? ? 48 8B 01 48 8B DA 4C 8B F1 FF 90").Get(); + + return Ret; } static inline uint64 FindInitHost() @@ -126,15 +148,35 @@ static inline uint64 FindInitListen() return FindBytes(Addr, { 0x48, 0x89, 0x5C }, 2000, 0, true, 1); } +static inline uint64 FindOnDamageServer() +{ + auto Addr = FindFunctionCall(L"OnDamageServer", { 0x40, 0x55 }); + return Addr; +} + static inline uint64 FindNoMCP() { + if (Fortnite_Version == 4) + return Memcury::Scanner::FindPattern("E8 ? ? ? ? 83 A7 ? ? ? ? ? 83 E0 01").RelativeOffset(1).Get(); + + if (Engine_Version == 421 || Engine_Version == 422) + return Memcury::Scanner::FindPattern("E8 ? ? ? ? 84 C0 75 CE").RelativeOffset(1).Get(); + + if (Engine_Version == 423) + return Memcury::Scanner::FindPattern("E8 ? ? ? ? 84 C0 75 C0").RelativeOffset(1).Get(); + + if (Engine_Version == 425) + return Memcury::Scanner::FindPattern("E8 ? ? ? ? 84 C0 75 C1").RelativeOffset(1).Get(); + // return (uintptr_t)GetModuleHandleW(0) + 0x1791CF0; // 11.01 return 0; - return (uintptr_t)GetModuleHandleW(0) + 0x161d600; + // return (uintptr_t)GetModuleHandleW(0) + 0x161d600; // 10.40 } static inline uint64 FindCollectGarbage() { + return 0; + auto Addr = Memcury::Scanner::FindStringRef(L"STAT_CollectGarbageInternal"); return FindBytes(Addr, { 0x48, 0x89, 0x5C }, 2000, 0, true, 1); } @@ -158,7 +200,13 @@ static inline uint64 FindGetNetMode() static inline uint64 FindRealloc() { - auto Addr = Memcury::Scanner::FindStringRef(L"a.Budget.BudgetMs"); + auto Addr = Memcury::Scanner::FindStringRef(L"a.Budget.BudgetMs", false); + + if (!Addr.Get()) + { + return Memcury::Scanner::FindPattern("48 89 5C 24 08 48 89 74 24 10 57 48 83 EC ? 48 8B F1 41 8B D8 48 8B 0D ? ? ? ?").Get(); // 4.16-4.20 + } + auto BeginningFunction = Memcury::Scanner(FindBytes(Addr, { 0x40, 0x53 }, 1000, 0, true)); auto CallToFunc = Memcury::Scanner(FindBytes(BeginningFunction, { 0xE8 })); @@ -169,7 +217,11 @@ static inline uint64 FindRealloc() static inline uint64 FindPickTeam() { - auto Addr = Memcury::Scanner::FindStringRef(L"PickTeam for [%s] used beacon value [%d]"); + auto Addr = Memcury::Scanner::FindStringRef(L"PickTeam for [%s] used beacon value [%d]", false); + + if (!Addr.Get()) + Addr = Memcury::Scanner::FindStringRef(L"PickTeam for [%s] used beacon value [%s]"); + return FindBytes(Addr, { 0x40, 0x55 }, 1000, 0, true); } @@ -187,9 +239,21 @@ static inline uint64 FindGiveAbility() return realGiveAbility; } -static inline uint64 FindCantBuild() +static inline uint64 FindGiveAbilityAndActivateOnce() { - return Memcury::Scanner::FindPattern("48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 41 56 48 83 EC ? 49 8B E9 4D 8B F0").Get(); + auto Addr = Memcury::Scanner::FindStringRef(L"GiveAbilityAndActivateOnce called on ability %s on the client, not allowed!"); + + return FindBytes(Addr, { 0x48, 0x89, 0x5C }, 1000, 0, true); +} + +static inline uint64 FindCantBuild() +{ + auto add = Memcury::Scanner::FindPattern("48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 41 56 48 83 EC ? 49 8B E9 4D 8B F0", false).Get(); + + if (!add) + add = Memcury::Scanner::FindPattern("48 89 54 24 ? 55 56 41 56 48 83 EC 50").Get(); // 4.20 + + return add; auto CreateBuildingActorAddr = Memcury::Scanner(GetFunctionIdxOrPtr(FindObject("/Script/FortniteGame.FortAIController.CreateBuildingActor"))); auto LikeHuh = Memcury::Scanner(FindBytes(CreateBuildingActorAddr, { 0x40, 0x88 }, 3000)); diff --git a/Project Reboot 3.0/hooking.h b/Project Reboot 3.0/hooking.h index 5d58ecf..4da7abf 100644 --- a/Project Reboot 3.0/hooking.h +++ b/Project Reboot 3.0/hooking.h @@ -98,8 +98,8 @@ inline __int64 GetFunctionIdxOrPtr(UFunction* Function) const wchar_t* ValidateWCStr = ValidateWStr.c_str(); bool bHasValidateFunc = Memcury::Scanner::FindStringRef(ValidateWCStr, false).Get(); - // LOG_INFO(LogDev, "[{}] bHasValidateFunc: {}", Function->GetName(), bHasValidateFunc); - // LOG_INFO(LogDev, "NativeAddr: 0x{:x}", __int64(NativeAddr) - __int64(GetModuleHandleW(0))); + LOG_INFO(LogDev, "[{}] bHasValidateFunc: {}", Function->GetName(), bHasValidateFunc); + LOG_INFO(LogDev, "NativeAddr: 0x{:x}", __int64(NativeAddr) - __int64(GetModuleHandleW(0))); bool bFoundValidate = !bHasValidateFunc; @@ -139,7 +139,7 @@ inline __int64 GetFunctionIdxOrPtr(UFunction* Function) std::transform(wtf.begin(), wtf.end(), wtf.begin(), ::toupper); - // std::cout << "wtf: " << wtf << '\n'; + LOG_INFO(LogDev, "wtf: {}", wtf); return HexToDec(wtf); } @@ -163,17 +163,17 @@ inline __int64 GetFunctionIdxOrPtr(UFunction* Function) if (RetAddr) { - // LOG_INFO(LogDev, "RetAddr 0x{:x}", RetAddr - __int64(GetModuleHandleW(0))); + LOG_INFO(LogDev, "RetAddr 0x{:x}", RetAddr - __int64(GetModuleHandleW(0))); int i = 0; for (__int64 CurrentAddy = RetAddr; CurrentAddy != NativeAddr && i < 2000; CurrentAddy -= 1) // Find last call { - // LOG_INFO(LogDev, "[{}] 0x{:x}", i, *(uint8_t*)CurrentAddy); + LOG_INFO(LogDev, "[{}] 0x{:x}", i, *(uint8_t*)CurrentAddy); if (*(uint8_t*)CurrentAddy == 0xE8) { - // LOG_INFO(LogDev, "CurrentAddy 0x{:x}", CurrentAddy - __int64(GetModuleHandleW(0))); + LOG_INFO(LogDev, "CurrentAddy 0x{:x}", CurrentAddy - __int64(GetModuleHandleW(0))); functionAddy = (CurrentAddy + 1 + 4) + *(int*)(CurrentAddy + 1); break; } diff --git a/vendor/memcury.h b/vendor/memcury.h index 0e81168..8d14af2 100644 --- a/vendor/memcury.h +++ b/vendor/memcury.h @@ -1,639 +1,734 @@ -// https://github.com/kem0x/memcury + // https://github.com/kem0x/memcury -#pragma once + #pragma once -/* - Memcury is a single-header file library for memory manipulation in C++. + /* + Memcury is a single-header file library for memory manipulation in C++. - Containers: - -PE::Address: A pointer container. - -PE::Section: Portable executable section container for internal usage. + Containers: + -PE::Address: A pointer container. + -PE::Section: Portable executable section container for internal usage. - Modules: - -Scanner: - -Constructors: - -Default: Takes a pointer to start the scanning from. - -FindPattern: Finds a pattern in memory. - -FindStringRef: Finds a string reference in memory, supports all types of strings. - -Functions: - -SetTargetModule: Sets the target module for the scanner. - -ScanFor: Scans for a byte(s) near the current address. - -FindFunctionBoundary: Finds the boundary of a function near the current address. - -RelativeOffset: Gets the relative offset of the current address. - -AbsoluteOffset: Gets the absolute offset of the current address. - -GetAs: Gets the current address as a type. - -Get: Gets the current address as an int64. + Modules: + -Scanner: + -Constructors: + -Default: Takes a pointer to start the scanning from. + -FindPattern: Finds a pattern in memory. + -FindStringRef: Finds a string reference in memory, supports all types of strings. + -Functions: + -SetTargetModule: Sets the target module for the scanner. + -ScanFor: Scans for a byte(s) near the current address. + -FindFunctionBoundary: Finds the boundary of a function near the current address. + -RelativeOffset: Gets the relative offset of the current address. + -AbsoluteOffset: Gets the absolute offset of the current address. + -GetAs: Gets the current address as a type. + -Get: Gets the current address as an int64. - -TrampolineHook: - -Constructors: - -Default: Takes a pointer pointer to the target function and a pointer to the hook function. - -Functions: - -Commit: Commits the hook. - -Revert: Reverts the hook. - -Toggle: Toggles the hook on\off. + -TrampolineHook: + -Constructors: + -Default: Takes a pointer pointer to the target function and a pointer to the hook function. + -Functions: + -Commit: Commits the hook. + -Revert: Reverts the hook. + -Toggle: Toggles the hook on\off. - -VEHHook: - -Functions: - -Init: Initializes the VEH Hook system. - -AddHook: Adds a hook to the VEH Hook system. - -RemoveHook: Removes a hook from the VEH Hook system. -*/ + -VEHHook: + -Functions: + -Init: Initializes the VEH Hook system. + -AddHook: Adds a hook to the VEH Hook system. + -RemoveHook: Removes a hook from the VEH Hook system. + */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#pragma comment(lib, "Dbghelp.lib") + #include + #include + #include + #include + #include + #include + #include + #include + #include + #pragma comment(lib, "Dbghelp.lib") -#define MemcuryAssert(cond) \ - if (!(cond)) \ - { \ - MessageBoxA(nullptr, #cond, __FUNCTION__, MB_ICONERROR | MB_OK); \ - Memcury::Safety::FreezeCurrentThread(); \ - } + #define MemcuryAssert(cond) \ + if (!(cond)) \ + { \ + MessageBoxA(nullptr, #cond, __FUNCTION__, MB_ICONERROR | MB_OK); \ + Memcury::Safety::FreezeCurrentThread(); \ + } -#define MemcuryAssertM(cond, msg) \ - if (!(cond)) \ - { \ + #define MemcuryAssertM(cond, msg) \ + if (!(cond)) \ + { \ + MessageBoxA(nullptr, msg, __FUNCTION__, MB_ICONERROR | MB_OK); \ + } + + #define MemcuryThrow(msg) \ MessageBoxA(nullptr, msg, __FUNCTION__, MB_ICONERROR | MB_OK); \ - } + Memcury::Safety::FreezeCurrentThread(); -#define MemcuryThrow(msg) \ - MessageBoxA(nullptr, msg, __FUNCTION__, MB_ICONERROR | MB_OK); \ - Memcury::Safety::FreezeCurrentThread(); - -namespace Memcury -{ - extern "C" IMAGE_DOS_HEADER __ImageBase; - - inline auto GetCurrentModule() -> HMODULE + namespace Memcury { - return reinterpret_cast(&__ImageBase); - } + extern "C" IMAGE_DOS_HEADER __ImageBase; - namespace Util - { - template - constexpr static auto IsInRange(T value, T min, T max) -> bool + inline auto GetCurrentModule() -> HMODULE { - return value >= min && value < max; + return reinterpret_cast(&__ImageBase); } - constexpr auto StrHash(const char* str, int h = 0) -> unsigned int + namespace Util { - return !str[h] ? 5381 : (StrHash(str, h + 1) * 33) ^ str[h]; - } - - inline auto IsSamePage(void* A, void* B) -> bool - { - MEMORY_BASIC_INFORMATION InfoA; - if (!VirtualQuery(A, &InfoA, sizeof(InfoA))) + template + constexpr static auto IsInRange(T value, T min, T max) -> bool { - return true; + return value >= min && value < max; } - MEMORY_BASIC_INFORMATION InfoB; - if (!VirtualQuery(B, &InfoB, sizeof(InfoB))) + constexpr auto StrHash(const char* str, int h = 0) -> unsigned int { - return true; + return !str[h] ? 5381 : (StrHash(str, h + 1) * 33) ^ str[h]; } - return InfoA.BaseAddress == InfoB.BaseAddress; - } - - inline auto GetModuleStartAndEnd() -> std::pair - { - auto HModule = GetCurrentModule(); - auto NTHeaders = reinterpret_cast((uintptr_t)HModule + reinterpret_cast((uintptr_t)HModule)->e_lfanew); - - uintptr_t dllStart = (uintptr_t)HModule; - uintptr_t dllEnd = (uintptr_t)HModule + NTHeaders->OptionalHeader.SizeOfImage; - - return { dllStart, dllEnd }; - } - - inline auto CopyToClipboard(std::string str) - { - auto mem = GlobalAlloc(GMEM_FIXED, str.size() + 1); - memcpy(mem, str.c_str(), str.size() + 1); - - OpenClipboard(nullptr); - EmptyClipboard(); - SetClipboardData(CF_TEXT, mem); - CloseClipboard(); - - GlobalFree(mem); - } - } - - namespace Safety - { - enum class ExceptionMode - { - None, - CatchDllExceptionsOnly, - CatchAllExceptions - }; - - static auto FreezeCurrentThread() -> void - { - SuspendThread(GetCurrentThread()); - } - - static auto PrintStack(CONTEXT* ctx) -> void - { - STACKFRAME64 stack; - memset(&stack, 0, sizeof(STACKFRAME64)); - - auto process = GetCurrentProcess(); - auto thread = GetCurrentThread(); - - SymInitialize(process, NULL, TRUE); - - bool result; - DWORD64 displacement = 0; - - char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]{ 0 }; - char name[256]{ 0 }; - char module[256]{ 0 }; - - PSYMBOL_INFO symbolInfo = (PSYMBOL_INFO)buffer; - - for (ULONG frame = 0;; frame++) + inline auto IsSamePage(void* A, void* B) -> bool { - result = StackWalk64( - IMAGE_FILE_MACHINE_AMD64, - process, - thread, - &stack, - ctx, - NULL, - SymFunctionTableAccess64, - SymGetModuleBase64, - NULL); - - if (!result) - break; - - symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO); - symbolInfo->MaxNameLen = MAX_SYM_NAME; - SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbolInfo); - - HMODULE hModule = NULL; - lstrcpyA(module, ""); - GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const wchar_t*)(stack.AddrPC.Offset), &hModule); - - if (hModule != NULL) - GetModuleFileNameA(hModule, module, 256); - - printf("[%lu] Name: %s - Address: %p - Module: %s\n", frame, symbolInfo->Name, (void*)symbolInfo->Address, module); - } - } - - template - auto MemcuryGlobalHandler(EXCEPTION_POINTERS* ExceptionInfo) -> long - { - auto [dllStart, dllEnd] = Util::GetModuleStartAndEnd(); - - if constexpr (mode == ExceptionMode::CatchDllExceptionsOnly) - { - if (!Util::IsInRange(ExceptionInfo->ContextRecord->Rip, dllStart, dllEnd)) + MEMORY_BASIC_INFORMATION InfoA; + if (!VirtualQuery(A, &InfoA, sizeof(InfoA))) { - return EXCEPTION_CONTINUE_SEARCH; + return true; } + + MEMORY_BASIC_INFORMATION InfoB; + if (!VirtualQuery(B, &InfoB, sizeof(InfoB))) + { + return true; + } + + return InfoA.BaseAddress == InfoB.BaseAddress; } - auto message = std::format("Memcury caught an exception at [{:x}]\nPress Yes if you want the address to be copied to your clipboard", ExceptionInfo->ContextRecord->Rip); - if (MessageBoxA(nullptr, message.c_str(), "Error", MB_ICONERROR | MB_YESNO) == IDYES) + inline auto GetModuleStartAndEnd() -> std::pair { - std::string clip = std::format("{:x}", ExceptionInfo->ContextRecord->Rip); - Util::CopyToClipboard(clip); + auto HModule = GetCurrentModule(); + auto NTHeaders = reinterpret_cast((uintptr_t)HModule + reinterpret_cast((uintptr_t)HModule)->e_lfanew); + + uintptr_t dllStart = (uintptr_t)HModule; + uintptr_t dllEnd = (uintptr_t)HModule + NTHeaders->OptionalHeader.SizeOfImage; + + return { dllStart, dllEnd }; } - PrintStack(ExceptionInfo->ContextRecord); - - FreezeCurrentThread(); - - return EXCEPTION_EXECUTE_HANDLER; - } - - template - static auto SetExceptionMode() -> void - { - SetUnhandledExceptionFilter(MemcuryGlobalHandler); - } - } - - namespace Globals - { - constexpr const bool bLogging = true; - - static const char* moduleName = nullptr; - } - - namespace ASM - { - //@todo: this whole namespace needs a rework, should somehow make this more modern and less ugly. - enum MNEMONIC : uint8_t - { - JMP_REL8 = 0xEB, - JMP_REL32 = 0xE9, - JMP_EAX = 0xE0, - CALL = 0xE8, - LEA = 0x8D, - CDQ = 0x99, - CMOVL = 0x4C, - CMOVS = 0x48, - CMOVNS = 0x49, - NOP = 0x90, - INT3 = 0xCC, - RETN_REL8 = 0xC2, - RETN = 0xC3, - NONE = 0x00 - }; - - constexpr int SIZE_OF_JMP_RELATIVE_INSTRUCTION = 5; - constexpr int SIZE_OF_JMP_ABSLOUTE_INSTRUCTION = 13; - - constexpr auto MnemonicToString(MNEMONIC e) -> const char* - { - switch (e) + inline auto CopyToClipboard(std::string str) { - case JMP_REL8: - return "JMP_REL8"; - case JMP_REL32: - return "JMP_REL32"; - case JMP_EAX: - return "JMP_EAX"; - case CALL: - return "CALL"; - case LEA: - return "LEA"; - case CDQ: - return "CDQ"; - case CMOVL: - return "CMOVL"; - case CMOVS: - return "CMOVS"; - case CMOVNS: - return "CMOVNS"; - case NOP: - return "NOP"; - case INT3: - return "INT3"; - case RETN_REL8: - return "RETN_REL8"; - case RETN: - return "RETN"; - case NONE: - return "NONE"; - default: - return "UNKNOWN"; + auto mem = GlobalAlloc(GMEM_FIXED, str.size() + 1); + memcpy(mem, str.c_str(), str.size() + 1); + + OpenClipboard(nullptr); + EmptyClipboard(); + SetClipboardData(CF_TEXT, mem); + CloseClipboard(); + + GlobalFree(mem); } } - constexpr auto Mnemonic(const char* s) -> MNEMONIC + namespace Safety { - switch (Util::StrHash(s)) + enum class ExceptionMode { - case Util::StrHash("JMP_REL8"): - return JMP_REL8; - case Util::StrHash("JMP_REL32"): - return JMP_REL32; - case Util::StrHash("JMP_EAX"): - return JMP_EAX; - case Util::StrHash("CALL"): - return CALL; - case Util::StrHash("LEA"): - return LEA; - case Util::StrHash("CDQ"): - return CDQ; - case Util::StrHash("CMOVL"): - return CMOVL; - case Util::StrHash("CMOVS"): - return CMOVS; - case Util::StrHash("CMOVNS"): - return CMOVNS; - case Util::StrHash("NOP"): - return NOP; - case Util::StrHash("INT3"): - return INT3; - case Util::StrHash("RETN_REL8"): - return RETN_REL8; - case Util::StrHash("RETN"): - return RETN; - default: - return NONE; - } - } - - inline auto byteIsA(uint8_t byte, MNEMONIC opcode) -> bool - { - return byte == opcode; - } - - inline auto byteIsAscii(uint8_t byte) -> bool - { - static constexpr bool isAscii[0x100] = { - false, false, false, false, false, false, false, false, false, true, true, false, false, true, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false + None, + CatchDllExceptionsOnly, + CatchAllExceptions }; - return isAscii[byte]; - } - - inline bool isJump(uint8_t byte) - { - return byte >= 0x70 && byte <= 0x7F; - } - - static auto pattern2bytes(const char* pattern) -> std::vector - { - 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) + static auto FreezeCurrentThread() -> void { - if (*current == '?') + SuspendThread(GetCurrentThread()); + } + + static auto PrintStack(CONTEXT* ctx) -> void + { + STACKFRAME64 stack; + memset(&stack, 0, sizeof(STACKFRAME64)); + + auto process = GetCurrentProcess(); + auto thread = GetCurrentThread(); + + SymInitialize(process, NULL, TRUE); + + bool result; + DWORD64 displacement = 0; + + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]{ 0 }; + char name[256]{ 0 }; + char module[256]{ 0 }; + + PSYMBOL_INFO symbolInfo = (PSYMBOL_INFO)buffer; + + for (ULONG frame = 0;; frame++) { - ++current; - if (*current == '?') - ++current; - bytes.push_back(-1); - } - else - { - bytes.push_back(strtoul(current, ¤t, 16)); + result = StackWalk64( + IMAGE_FILE_MACHINE_AMD64, + process, + thread, + &stack, + ctx, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL); + + if (!result) + break; + + symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO); + symbolInfo->MaxNameLen = MAX_SYM_NAME; + SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbolInfo); + + HMODULE hModule = NULL; + lstrcpyA(module, ""); + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const wchar_t*)(stack.AddrPC.Offset), &hModule); + + if (hModule != NULL) + GetModuleFileNameA(hModule, module, 256); + + printf("[%lu] Name: %s - Address: %p - Module: %s\n", frame, symbolInfo->Name, (void*)symbolInfo->Address, module); } } - return bytes; - } - } - namespace PE - { - inline auto SetCurrentModule(const char* moduleName) -> void - { - Globals::moduleName = moduleName; + template + auto MemcuryGlobalHandler(EXCEPTION_POINTERS* ExceptionInfo) -> long + { + auto [dllStart, dllEnd] = Util::GetModuleStartAndEnd(); + + if constexpr (mode == ExceptionMode::CatchDllExceptionsOnly) + { + if (!Util::IsInRange(ExceptionInfo->ContextRecord->Rip, dllStart, dllEnd)) + { + return EXCEPTION_CONTINUE_SEARCH; + } + } + + auto message = std::format("Memcury caught an exception at [{:x}]\nPress Yes if you want the address to be copied to your clipboard", ExceptionInfo->ContextRecord->Rip); + if (MessageBoxA(nullptr, message.c_str(), "Error", MB_ICONERROR | MB_YESNO) == IDYES) + { + std::string clip = std::format("{:x}", ExceptionInfo->ContextRecord->Rip); + Util::CopyToClipboard(clip); + } + + PrintStack(ExceptionInfo->ContextRecord); + + FreezeCurrentThread(); + + return EXCEPTION_EXECUTE_HANDLER; + } + + template + static auto SetExceptionMode() -> void + { + SetUnhandledExceptionFilter(MemcuryGlobalHandler); + } } - inline auto GetModuleBase() -> uintptr_t + namespace Globals { - return reinterpret_cast(GetModuleHandleA(Globals::moduleName)); + constexpr const bool bLogging = true; + + static const char* moduleName = nullptr; } - inline auto GetDOSHeader() -> PIMAGE_DOS_HEADER + namespace ASM { - return reinterpret_cast(GetModuleBase()); + //@todo: this whole namespace needs a rework, should somehow make this more modern and less ugly. + enum MNEMONIC : uint8_t + { + JMP_REL8 = 0xEB, + JMP_REL32 = 0xE9, + JMP_EAX = 0xE0, + CALL = 0xE8, + LEA = 0x8D, + CDQ = 0x99, + CMOVL = 0x4C, + CMOVS = 0x48, + CMOVNS = 0x49, + NOP = 0x90, + INT3 = 0xCC, + RETN_REL8 = 0xC2, + RETN = 0xC3, + NONE = 0x00 + }; + + constexpr int SIZE_OF_JMP_RELATIVE_INSTRUCTION = 5; + constexpr int SIZE_OF_JMP_ABSLOUTE_INSTRUCTION = 13; + + constexpr auto MnemonicToString(MNEMONIC e) -> const char* + { + switch (e) + { + case JMP_REL8: + return "JMP_REL8"; + case JMP_REL32: + return "JMP_REL32"; + case JMP_EAX: + return "JMP_EAX"; + case CALL: + return "CALL"; + case LEA: + return "LEA"; + case CDQ: + return "CDQ"; + case CMOVL: + return "CMOVL"; + case CMOVS: + return "CMOVS"; + case CMOVNS: + return "CMOVNS"; + case NOP: + return "NOP"; + case INT3: + return "INT3"; + case RETN_REL8: + return "RETN_REL8"; + case RETN: + return "RETN"; + case NONE: + return "NONE"; + default: + return "UNKNOWN"; + } + } + + constexpr auto Mnemonic(const char* s) -> MNEMONIC + { + switch (Util::StrHash(s)) + { + case Util::StrHash("JMP_REL8"): + return JMP_REL8; + case Util::StrHash("JMP_REL32"): + return JMP_REL32; + case Util::StrHash("JMP_EAX"): + return JMP_EAX; + case Util::StrHash("CALL"): + return CALL; + case Util::StrHash("LEA"): + return LEA; + case Util::StrHash("CDQ"): + return CDQ; + case Util::StrHash("CMOVL"): + return CMOVL; + case Util::StrHash("CMOVS"): + return CMOVS; + case Util::StrHash("CMOVNS"): + return CMOVNS; + case Util::StrHash("NOP"): + return NOP; + case Util::StrHash("INT3"): + return INT3; + case Util::StrHash("RETN_REL8"): + return RETN_REL8; + case Util::StrHash("RETN"): + return RETN; + default: + return NONE; + } + } + + inline auto byteIsA(uint8_t byte, MNEMONIC opcode) -> bool + { + return byte == opcode; + } + + inline auto byteIsAscii(uint8_t byte) -> bool + { + static constexpr bool isAscii[0x100] = { + false, false, false, false, false, false, false, false, false, true, true, false, false, true, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false + }; + + return isAscii[byte]; + } + + inline bool isJump(uint8_t byte) + { + return byte >= 0x70 && byte <= 0x7F; + } + + static auto pattern2bytes(const char* pattern) -> std::vector + { + 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; + } } - inline auto GetNTHeaders() -> PIMAGE_NT_HEADERS + namespace PE { - return reinterpret_cast(GetModuleBase() + GetDOSHeader()->e_lfanew); + inline auto SetCurrentModule(const char* moduleName) -> void + { + Globals::moduleName = moduleName; + } + + inline auto GetModuleBase() -> uintptr_t + { + return reinterpret_cast(GetModuleHandleA(Globals::moduleName)); + } + + inline auto GetDOSHeader() -> PIMAGE_DOS_HEADER + { + return reinterpret_cast(GetModuleBase()); + } + + inline auto GetNTHeaders() -> PIMAGE_NT_HEADERS + { + return reinterpret_cast(GetModuleBase() + GetDOSHeader()->e_lfanew); + } + + class Address + { + uintptr_t _address; + + public: + Address() + { + _address = 0; + } + + Address(uintptr_t address) + : _address(address) + { + } + + Address(void* address) + : _address(reinterpret_cast(address)) + { + } + + auto operator=(uintptr_t address) -> Address + { + _address = address; + return *this; + } + + auto operator=(void* address) -> Address + { + _address = reinterpret_cast(address); + return *this; + } + + auto operator+(uintptr_t offset) -> Address + { + return Address(_address + offset); + } + + bool operator>(uintptr_t offset) + { + return _address > offset; + } + + bool operator>(Address address) + { + return _address > address._address; + } + + bool operator<(uintptr_t offset) + { + return _address < offset; + } + + bool operator<(Address address) + { + return _address < address._address; + } + + bool operator>=(uintptr_t offset) + { + return _address >= offset; + } + + bool operator>=(Address address) + { + return _address >= address._address; + } + + bool operator<=(uintptr_t offset) + { + return _address <= offset; + } + + bool operator<=(Address address) + { + return _address <= address._address; + } + + bool operator==(uintptr_t offset) + { + return _address == offset; + } + + bool operator==(Address address) + { + return _address == address._address; + } + + bool operator!=(uintptr_t offset) + { + return _address != offset; + } + + bool operator!=(Address address) + { + return _address != address._address; + } + + auto RelativeOffset(uint32_t offset) -> Address + { + _address = ((_address + offset + 4) + *(int32_t*)(_address + offset)); + return *this; + } + + auto AbsoluteOffset(uint32_t offset) -> Address + { + _address = _address + offset; + return *this; + } + + auto Jump() -> Address + { + if (ASM::isJump(*reinterpret_cast(_address))) + { + UINT8 toSkip = *reinterpret_cast(_address + 1); + _address = _address + 2 + toSkip; + } + + return *this; + } + + auto Get() -> uintptr_t + { + return _address; + } + + template + auto GetAs() -> T + { + return reinterpret_cast(_address); + } + + auto IsValid() -> bool + { + return _address != 0; + } + }; + + class Section + { + public: + std::string sectionName; + IMAGE_SECTION_HEADER rawSection; + + static auto GetAllSections() -> std::vector
+ { + std::vector
sections; + + auto sectionsSize = GetNTHeaders()->FileHeader.NumberOfSections; + auto section = IMAGE_FIRST_SECTION(GetNTHeaders()); + + for (WORD i = 0; i < sectionsSize; i++, section++) + { + auto secName = std::string((char*)section->Name); + + sections.push_back({ secName, *section }); + } + + return sections; + } + + static auto GetSection(std::string sectionName) -> Section + { + for (auto& section : GetAllSections()) + { + if (section.sectionName == sectionName) + { + return section; + } + } + + MemcuryThrow("Section not found"); + return Section{}; + } + + auto GetSectionSize() -> uint32_t + { + return rawSection.Misc.VirtualSize; + } + + auto GetSectionStart() -> Address + { + return Address(GetModuleBase() + rawSection.VirtualAddress); + } + + auto GetSectionEnd() -> Address + { + return Address(GetSectionStart() + GetSectionSize()); + } + + auto isInSection(Address address) -> bool + { + return address >= GetSectionStart() && address < GetSectionEnd(); + } + }; } - class Address + class Scanner { - uintptr_t _address; + PE::Address _address; public: - Address() - { - _address = 0; - } - - Address(uintptr_t address) + Scanner(PE::Address address) : _address(address) { } - Address(void* address) - : _address(reinterpret_cast(address)) + static auto SetTargetModule(const char* moduleName) -> void { + PE::SetCurrentModule(moduleName); } - auto operator=(uintptr_t address) -> Address + static auto FindPatternEx(HANDLE handle, const char* pattern, const char* mask, uint64_t begin, uint64_t end) -> Scanner { - _address = address; - return *this; - } - - auto operator=(void* address) -> Address - { - _address = reinterpret_cast(address); - return *this; - } - - auto operator+(uintptr_t offset) -> Address - { - return Address(_address + offset); - } - - bool operator>(uintptr_t offset) - { - return _address > offset; - } - - bool operator>(Address address) - { - return _address > address._address; - } - - bool operator<(uintptr_t offset) - { - return _address < offset; - } - - bool operator<(Address address) - { - return _address < address._address; - } - - bool operator>=(uintptr_t offset) - { - return _address >= offset; - } - - bool operator>=(Address address) - { - return _address >= address._address; - } - - bool operator<=(uintptr_t offset) - { - return _address <= offset; - } - - bool operator<=(Address address) - { - return _address <= address._address; - } - - bool operator==(uintptr_t offset) - { - return _address == offset; - } - - bool operator==(Address address) - { - return _address == address._address; - } - - bool operator!=(uintptr_t offset) - { - return _address != offset; - } - - bool operator!=(Address address) - { - return _address != address._address; - } - - auto RelativeOffset(uint32_t offset) -> Address - { - _address = ((_address + offset + 4) + *(int32_t*)(_address + offset)); - return *this; - } - - auto AbsoluteOffset(uint32_t offset) -> Address - { - _address = _address + offset; - return *this; - } - - auto Jump() -> Address - { - if (ASM::isJump(*reinterpret_cast(_address))) + auto scan = [](const char* pattern, const char* mask, char* begin, unsigned int size) -> char* { - UINT8 toSkip = *reinterpret_cast(_address + 1); - _address = _address + 2 + toSkip; - } - - return *this; - } - - auto Get() -> uintptr_t - { - return _address; - } - - template - auto GetAs() -> T - { - return reinterpret_cast(_address); - } - - auto IsValid() -> bool - { - return _address != 0; - } - }; - - class Section - { - public: - std::string sectionName; - IMAGE_SECTION_HEADER rawSection; - - static auto GetAllSections() -> std::vector
- { - std::vector
sections; - - auto sectionsSize = GetNTHeaders()->FileHeader.NumberOfSections; - auto section = IMAGE_FIRST_SECTION(GetNTHeaders()); - - for (WORD i = 0; i < sectionsSize; i++, section++) - { - auto secName = std::string((char*)section->Name); - - sections.push_back({ secName, *section }); - } - - return sections; - } - - static auto GetSection(std::string sectionName) -> Section - { - for (auto& section : GetAllSections()) - { - if (section.sectionName == sectionName) + size_t patternLen = strlen(mask); + for (unsigned int i = 0; i < size - patternLen; i++) { - return section; + bool found = true; + for (unsigned int j = 0; j < patternLen; j++) + { + if (mask[j] != '?' && pattern[j] != *(begin + i + j)) + { + found = false; + break; + } + } + + if (found) + return (begin + i); + } + return nullptr; + }; + + uint64_t match = NULL; + SIZE_T bytesRead; + char* buffer = nullptr; + MEMORY_BASIC_INFORMATION mbi = { 0 }; + + uint64_t curr = begin; + + for (uint64_t curr = begin; curr < end; curr += mbi.RegionSize) + { + if (!VirtualQueryEx(handle, (void*)curr, &mbi, sizeof(mbi))) + continue; + + if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS) + continue; + + buffer = new char[mbi.RegionSize]; + + if (ReadProcessMemory(handle, mbi.BaseAddress, buffer, mbi.RegionSize, &bytesRead)) + { + char* internalAddr = scan(pattern, mask, buffer, (unsigned int)bytesRead); + + if (internalAddr != nullptr) + { + match = curr + (uint64_t)(internalAddr - buffer); + break; + } } } + delete[] buffer; - MemcuryThrow("Section not found"); - return Section{}; + MemcuryAssertM(match != 0, "FindPatternEx return nullptr"); + + return Scanner(match); } - auto GetSectionSize() -> uint32_t + static auto FindPatternEx(HANDLE handle, const char* sig) -> Scanner { - return rawSection.Misc.VirtualSize; + char pattern[100]; + char mask[100]; + + char lastChar = ' '; + unsigned int j = 0; + + for (unsigned int i = 0; i < strlen(sig); i++) + { + if ((sig[i] == '?' || sig[i] == '*') && (lastChar != '?' && lastChar != '*')) + { + pattern[j] = mask[j] = '?'; + j++; + } + + else if (isspace(lastChar)) + { + pattern[j] = lastChar = (char)strtol(&sig[i], 0, 16); + mask[j] = 'x'; + j++; + } + lastChar = sig[i]; + } + pattern[j] = mask[j] = '\0'; + + auto module = (uint64_t)GetModuleHandle(nullptr); + + return FindPatternEx(handle, pattern, mask, module, module + Memcury::PE::GetNTHeaders()->OptionalHeader.SizeOfImage); } - auto GetSectionStart() -> Address + static auto FindPattern(const char* signature, bool bWarnIfNotFound = true) -> Scanner { - return Address(GetModuleBase() + rawSection.VirtualAddress); - } + PE::Address add{ nullptr }; - auto GetSectionEnd() -> Address - { - return Address(GetSectionStart() + GetSectionSize()); - } + const auto sizeOfImage = PE::GetNTHeaders()->OptionalHeader.SizeOfImage; + auto patternBytes = ASM::pattern2bytes(signature); + const auto scanBytes = reinterpret_cast(PE::GetModuleBase()); - auto isInSection(Address address) -> bool - { - return address >= GetSectionStart() && address < GetSectionEnd(); - } - }; - } + const auto s = patternBytes.size(); + const auto d = patternBytes.data(); - class Scanner - { - PE::Address _address; - - public: - Scanner(PE::Address address) - : _address(address) - { - } - - static auto SetTargetModule(const char* moduleName) -> void - { - PE::SetCurrentModule(moduleName); - } - - static auto FindPatternEx(HANDLE handle, const char* pattern, const char* mask, uint64_t begin, uint64_t end) -> Scanner - { - auto scan = [](const char* pattern, const char* mask, char* begin, unsigned int size) -> char* - { - size_t patternLen = strlen(mask); - for (unsigned int i = 0; i < size - patternLen; i++) + for (auto i = 0ul; i < sizeOfImage - s; ++i) { bool found = true; - for (unsigned int j = 0; j < patternLen; j++) + for (auto j = 0ul; j < s; ++j) { - if (mask[j] != '?' && pattern[j] != *(begin + i + j)) + if (scanBytes[i + j] != d[j] && d[j] != -1) { found = false; break; @@ -641,661 +736,616 @@ namespace Memcury } if (found) - return (begin + i); - } - return nullptr; - }; - - uint64_t match = NULL; - SIZE_T bytesRead; - char* buffer = nullptr; - MEMORY_BASIC_INFORMATION mbi = { 0 }; - - uint64_t curr = begin; - - for (uint64_t curr = begin; curr < end; curr += mbi.RegionSize) - { - if (!VirtualQueryEx(handle, (void*)curr, &mbi, sizeof(mbi))) - continue; - - if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS) - continue; - - buffer = new char[mbi.RegionSize]; - - if (ReadProcessMemory(handle, mbi.BaseAddress, buffer, mbi.RegionSize, &bytesRead)) - { - char* internalAddr = scan(pattern, mask, buffer, (unsigned int)bytesRead); - - if (internalAddr != nullptr) { - match = curr + (uint64_t)(internalAddr - buffer); - break; - } - } - } - delete[] buffer; - - MemcuryAssertM(match != 0, "FindPatternEx return nullptr"); - - return Scanner(match); - } - - static auto FindPatternEx(HANDLE handle, const char* sig) -> Scanner - { - char pattern[100]; - char mask[100]; - - char lastChar = ' '; - unsigned int j = 0; - - for (unsigned int i = 0; i < strlen(sig); i++) - { - if ((sig[i] == '?' || sig[i] == '*') && (lastChar != '?' && lastChar != '*')) - { - pattern[j] = mask[j] = '?'; - j++; - } - - else if (isspace(lastChar)) - { - pattern[j] = lastChar = (char)strtol(&sig[i], 0, 16); - mask[j] = 'x'; - j++; - } - lastChar = sig[i]; - } - pattern[j] = mask[j] = '\0'; - - auto module = (uint64_t)GetModuleHandle(nullptr); - - return FindPatternEx(handle, pattern, mask, module, module + Memcury::PE::GetNTHeaders()->OptionalHeader.SizeOfImage); - } - - static auto FindPattern(const char* signature) -> Scanner - { - PE::Address add{ nullptr }; - - const auto sizeOfImage = PE::GetNTHeaders()->OptionalHeader.SizeOfImage; - auto patternBytes = ASM::pattern2bytes(signature); - const auto scanBytes = reinterpret_cast(PE::GetModuleBase()); - - const auto s = patternBytes.size(); - const auto d = patternBytes.data(); - - for (auto i = 0ul; i < sizeOfImage - s; ++i) - { - bool found = true; - for (auto j = 0ul; j < s; ++j) - { - if (scanBytes[i + j] != d[j] && d[j] != -1) - { - found = false; + add = reinterpret_cast(&scanBytes[i]); break; } } - if (found) + // MemcuryAssertM(add != 0, "FindPattern return nullptr"); + + if (bWarnIfNotFound) { - add = reinterpret_cast(&scanBytes[i]); - break; + if (add == 0) + MessageBoxA(0, ("FindPattern " + std::string(signature) + " null").c_str(), "Memcury", MB_ICONERROR); } + + return Scanner(add); } - // MemcuryAssertM(add != 0, "FindPattern return nullptr"); - - if (add == 0) - MessageBoxA(0, ("FindPattern " + std::string(signature) + " null").c_str(), "Memcury", MB_ICONERROR); - - return Scanner(add); - } - - // Supports wide and normal strings both std and pointers - template - static auto FindStringRef(T string, bool bWarnIfNotFound = true) -> Scanner - { - PE::Address add{ nullptr }; - - constexpr auto bIsWide = std::is_same::value; - constexpr auto bIsChar = std::is_same::value; - - constexpr auto bIsPtr = bIsWide || bIsChar; - - auto textSection = PE::Section::GetSection(".text"); - auto rdataSection = PE::Section::GetSection(".rdata"); - - const auto scanBytes = reinterpret_cast(textSection.GetSectionStart().Get()); - - // scan only text section - for (DWORD i = 0x0; i < textSection.GetSectionSize(); i++) + static auto FindPointerRef(void* Pointer) -> Scanner // credit ender { - if ((scanBytes[i] == ASM::CMOVL || scanBytes[i] == ASM::CMOVS) && scanBytes[i + 1] == ASM::LEA) + PE::Address add{ nullptr }; + + auto textSection = PE::Section::GetSection(".text"); + + const auto scanBytes = reinterpret_cast(textSection.GetSectionStart().Get()); + + // scan only text section + for (DWORD i = 0x0; i < textSection.GetSectionSize(); i++) { - auto stringAdd = PE::Address(&scanBytes[i]).RelativeOffset(3); - - // Check if the string is in the .rdata section - if (rdataSection.isInSection(stringAdd)) + if ((scanBytes[i] == ASM::CMOVL || scanBytes[i] == ASM::CMOVS) && (scanBytes[i + 1] == ASM::LEA || scanBytes[i + 1] == 0x8B)) { - auto strBytes = stringAdd.GetAs(); - - // Check if the first char is printable - if (ASM::byteIsAscii(strBytes[0])) + if (PE::Address(&scanBytes[i]).RelativeOffset(3).GetAs() == Pointer) { - if constexpr (!bIsPtr) + add = PE::Address(&scanBytes[i]); + } + } + } + + if (add == 0) + { + MessageBoxA(0, "FindPointerRef return nullptr", "Memcury", MB_OK); + } + + return Scanner(add); + } + + // Supports wide and normal strings both std and pointers + template + static auto FindStringRef(T string, bool bWarnIfNotFound = true, int useRefNum = 0) -> Scanner + { + PE::Address add{ nullptr }; + + constexpr auto bIsWide = std::is_same::value; + constexpr auto bIsChar = std::is_same::value; + + constexpr auto bIsPtr = bIsWide || bIsChar; + + auto textSection = PE::Section::GetSection(".text"); + auto rdataSection = PE::Section::GetSection(".rdata"); + + const auto scanBytes = reinterpret_cast(textSection.GetSectionStart().Get()); + + int aa = 0; + + // scan only text section + for (DWORD i = 0x0; i < textSection.GetSectionSize(); i++) + { + if ((scanBytes[i] == ASM::CMOVL || scanBytes[i] == ASM::CMOVS) && scanBytes[i + 1] == ASM::LEA) + { + auto stringAdd = PE::Address(&scanBytes[i]).RelativeOffset(3); + + // Check if the string is in the .rdata section + if (rdataSection.isInSection(stringAdd)) + { + auto strBytes = stringAdd.GetAs(); + + // Check if the first char is printable + if (ASM::byteIsAscii(strBytes[0])) { - typedef T::value_type char_type; - - auto lea = stringAdd.GetAs(); - - T leaT(lea); - - if (leaT == string) + if constexpr (!bIsPtr) { - add = PE::Address(&scanBytes[i]); - } - } - else - { - auto lea = stringAdd.GetAs(); + typedef T::value_type char_type; - if constexpr (bIsWide) - { - if (wcscmp(string, lea) == 0) + auto lea = stringAdd.GetAs(); + + T leaT(lea); + + if (leaT == string) { add = PE::Address(&scanBytes[i]); + + if (++aa > useRefNum) + break; } } else { - if (strcmp(string, lea) == 0) + auto lea = stringAdd.GetAs(); + + if constexpr (bIsWide) { - add = PE::Address(&scanBytes[i]); + if (wcscmp(string, lea) == 0) + { + add = PE::Address(&scanBytes[i]); + + if (++aa > useRefNum) + break; + } + } + else + { + if (strcmp(string, lea) == 0) + { + add = PE::Address(&scanBytes[i]); + + if (++aa > useRefNum) + break; + } } } } } } } - } - // MemcuryAssertM(add != 0, "FindStringRef return nullptr"); + // MemcuryAssertM(add != 0, "FindStringRef return nullptr"); - if (bWarnIfNotFound) - { - if (add == 0) + if (bWarnIfNotFound) { - if constexpr (bIsWide) + if (add == 0) { - auto aaa = (L"FindStringRef " + std::wstring(string)); - MessageBoxA(0, std::string(aaa.begin(), aaa.end()).c_str(), "Memcury", MB_ICONERROR); - } - else - { - MessageBoxA(0, ("FindStringRef " + std::string(string)).c_str(), "Memcury", MB_ICONERROR); + if constexpr (bIsWide) + { + auto aaa = (L"FindStringRef " + std::wstring(string)); + MessageBoxA(0, std::string(aaa.begin(), aaa.end()).c_str(), "Memcury", MB_ICONERROR); + } + else + { + MessageBoxA(0, ("FindStringRef " + std::string(string)).c_str(), "Memcury", MB_ICONERROR); + } } } + + return Scanner(add); } - return Scanner(add); - } - - auto Jump() -> Scanner - { - _address.Jump(); - return *this; - } - - inline auto ScanFor(std::vector opcodesToFind, bool forward = true, int toSkip = 0) -> Scanner - { - const auto scanBytes = _address.GetAs(); - - for (auto i = (forward ? 1 : -1); forward ? (i < 2048) : (i > -2048); forward ? i++ : i--) + auto Jump() -> Scanner { - bool found = true; + _address.Jump(); + return *this; + } - for (int k = 0; k < opcodesToFind.size() && found; k++) + inline auto ScanFor(std::vector opcodesToFind, bool forward = true, int toSkip = 0) -> Scanner + { + const auto scanBytes = _address.GetAs(); + + for (auto i = (forward ? 1 : -1); forward ? (i < 2048) : (i > -2048); forward ? i++ : i--) { - auto& currentOpcode = opcodesToFind[k]; + bool found = true; - if (currentOpcode == -1) - continue; - - // std::cout << std::format("[{} {}] 0x{:x}\n", i, k, currentOpcode); - - found = currentOpcode == scanBytes[i + k]; - } - - if (found) - { - _address = &scanBytes[i]; - if (toSkip != 0) + for (int k = 0; k < opcodesToFind.size() && found; k++) { - return ScanFor(opcodesToFind, forward, toSkip - 1); + auto& currentOpcode = opcodesToFind[k]; + + if (currentOpcode == -1) + continue; + + // std::cout << std::format("[{} {}] 0x{:x}\n", i, k, currentOpcode); + + found = currentOpcode == scanBytes[i + k]; } - break; + if (found) + { + _address = &scanBytes[i]; + if (toSkip != 0) + { + return ScanFor(opcodesToFind, forward, toSkip - 1); + } + + break; + } } + + return *this; } - return *this; - } - - auto FindFunctionBoundary(bool forward = false) -> Scanner - { - const auto scanBytes = _address.GetAs(); - - for (auto i = (forward ? 1 : -1); forward ? (i < 2048) : (i > -2048); forward ? i++ : i--) + auto FindFunctionBoundary(bool forward = false) -> Scanner { - if ( // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_REL8) || - // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_REL32) || - // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_EAX) || - ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::RETN_REL8) || ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::RETN) || ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::INT3)) + const auto scanBytes = _address.GetAs(); + + for (auto i = (forward ? 1 : -1); forward ? (i < 2048) : (i > -2048); forward ? i++ : i--) { - _address = (uintptr_t)&scanBytes[i + 1]; - break; + if ( // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_REL8) || + // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_REL32) || + // ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::JMP_EAX) || + ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::RETN_REL8) || ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::RETN) || ASM::byteIsA(scanBytes[i], ASM::MNEMONIC::INT3)) + { + _address = (uintptr_t)&scanBytes[i + 1]; + break; + } } + + return *this; } - return *this; - } - - auto RelativeOffset(uint32_t offset) -> Scanner - { - _address.RelativeOffset(offset); - - return *this; - } - - auto AbsoluteOffset(uint32_t offset) -> Scanner - { - _address.AbsoluteOffset(offset); - - return *this; - } - - template - auto GetAs() -> T - { - return _address.GetAs(); - } - - auto Get() -> uintptr_t - { - return _address.Get(); - } - - auto IsValid() -> bool - { - return _address.IsValid(); - } - }; - - /* Bad don't use it tbh... */ - class TrampolineHook - { - void** originalFunctionPtr; - PE::Address originalFunction; - PE::Address hookFunction; - PE::Address allocatedPage; - std::vector restore; - - void PointToCodeIfNot(PE::Address& ptr) - { - auto bytes = ptr.GetAs(); - - if (ASM::byteIsA(bytes[0], ASM::MNEMONIC::JMP_REL32)) + auto RelativeOffset(uint32_t offset) -> Scanner { - ptr = bytes + 5 + *(int32_t*)&bytes[1]; + _address.RelativeOffset(offset); + + return *this; } - } - void* AllocatePageNearAddress(void* targetAddr) - { - SYSTEM_INFO sysInfo; - GetSystemInfo(&sysInfo); - const uint64_t PAGE_SIZE = sysInfo.dwPageSize; - - uint64_t startAddr = (uint64_t(targetAddr) & ~(PAGE_SIZE - 1)); // round down to nearest page boundary - uint64_t minAddr = fmin(startAddr - 0x7FFFFF00, (uint64_t)sysInfo.lpMinimumApplicationAddress); - uint64_t maxAddr = fmax(startAddr + 0x7FFFFF00, (uint64_t)sysInfo.lpMaximumApplicationAddress); - - uint64_t startPage = (startAddr - (startAddr % PAGE_SIZE)); - - for (uint64_t pageOffset = 1; pageOffset; pageOffset++) + auto AbsoluteOffset(uint32_t offset) -> Scanner { - uint64_t byteOffset = pageOffset * PAGE_SIZE; - uint64_t highAddr = startPage + byteOffset; - uint64_t lowAddr = (startPage > byteOffset) ? startPage - byteOffset : 0; + _address.AbsoluteOffset(offset); - bool needsExit = highAddr > maxAddr && lowAddr < minAddr; + return *this; + } - if (highAddr < maxAddr) + template + auto GetAs() -> T + { + return _address.GetAs(); + } + + auto Get() -> uintptr_t + { + return _address.Get(); + } + + auto IsValid() -> bool + { + return _address.IsValid(); + } + }; + + /* Bad don't use it tbh... */ + class TrampolineHook + { + void** originalFunctionPtr; + PE::Address originalFunction; + PE::Address hookFunction; + PE::Address allocatedPage; + std::vector restore; + + void PointToCodeIfNot(PE::Address& ptr) + { + auto bytes = ptr.GetAs(); + + if (ASM::byteIsA(bytes[0], ASM::MNEMONIC::JMP_REL32)) { - void* outAddr = VirtualAlloc((void*)highAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (outAddr) - return outAddr; - } - - if (lowAddr > minAddr) - { - void* outAddr = VirtualAlloc((void*)lowAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (outAddr != nullptr) - return outAddr; - } - - if (needsExit) - { - break; + ptr = bytes + 5 + *(int32_t*)&bytes[1]; } } - return nullptr; - } + void* AllocatePageNearAddress(void* targetAddr) + { + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + const uint64_t PAGE_SIZE = sysInfo.dwPageSize; - void WriteAbsoluteJump(void* jumpLocation, void* destination) - { - uint8_t absJumpInstructions[] = { - ASM::Mnemonic("CMOVNS"), 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, addr - 0x41, 0xFF, 0xE2 // jmp r10 + uint64_t startAddr = (uint64_t(targetAddr) & ~(PAGE_SIZE - 1)); // round down to nearest page boundary + uint64_t minAddr = fmin(startAddr - 0x7FFFFF00, (uint64_t)sysInfo.lpMinimumApplicationAddress); + uint64_t maxAddr = fmax(startAddr + 0x7FFFFF00, (uint64_t)sysInfo.lpMaximumApplicationAddress); + + uint64_t startPage = (startAddr - (startAddr % PAGE_SIZE)); + + for (uint64_t pageOffset = 1; pageOffset; pageOffset++) + { + uint64_t byteOffset = pageOffset * PAGE_SIZE; + uint64_t highAddr = startPage + byteOffset; + uint64_t lowAddr = (startPage > byteOffset) ? startPage - byteOffset : 0; + + bool needsExit = highAddr > maxAddr && lowAddr < minAddr; + + if (highAddr < maxAddr) + { + void* outAddr = VirtualAlloc((void*)highAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (outAddr) + return outAddr; + } + + if (lowAddr > minAddr) + { + void* outAddr = VirtualAlloc((void*)lowAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (outAddr != nullptr) + return outAddr; + } + + if (needsExit) + { + break; + } + } + + return nullptr; + } + + void WriteAbsoluteJump(void* jumpLocation, void* destination) + { + uint8_t absJumpInstructions[] = { + ASM::Mnemonic("CMOVNS"), 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, addr + 0x41, 0xFF, 0xE2 // jmp r10 + }; + + auto destination64 = (uint64_t)destination; + memcpy(&absJumpInstructions[2], &destination64, sizeof(destination64)); + memcpy(jumpLocation, absJumpInstructions, sizeof(absJumpInstructions)); + } + + uintptr_t PrepareRestore() + { + /* + This is not a correct way to do it at all, since not all functions sub from the stack + This needs so much more tests, but it works for now. + */ + + Scanner scanner(originalFunction); + scanner.ScanFor({ 0x48, 0x83, 0xEC }); // sub rsp + + auto restoreSize = scanner.Get() - originalFunction.Get(); + + MemcuryAssert(restoreSize > 0 && restoreSize < 0x100); + + restore.reserve(restoreSize); + for (auto i = 0; i < restoreSize; i++) + { + restore.push_back(originalFunction.GetAs()[i]); + } + + return restoreSize; + } + + void WriteRestore() + { + auto restorePtr = allocatedPage + ASM::SIZE_OF_JMP_ABSLOUTE_INSTRUCTION + 2; + + memcpy(restorePtr.GetAs(), restore.data(), restore.size()); + + *originalFunctionPtr = restorePtr.GetAs(); + + // Write a jump back to where the execution should resume + restorePtr.AbsoluteOffset((uint32_t)restore.size()); + + auto contuineExecution = originalFunction + restore.size(); + + WriteAbsoluteJump(restorePtr.GetAs(), contuineExecution.GetAs()); + } + + auto PrepareJMPInstruction(uint64_t dst) + { + uint8_t bytes[5] = { ASM::Mnemonic("JMP_REL32"), 0x0, 0x0, 0x0, 0x0 }; + + const uint64_t relAddr = dst - (originalFunction.Get() + ASM::SIZE_OF_JMP_RELATIVE_INSTRUCTION); + memcpy(bytes + 1, &relAddr, 4); + + return std::move(bytes); + } + + bool IsHooked() + { + return originalFunction.GetAs()[0] == ASM::Mnemonic("JMP_REL32"); + } + + public: + TrampolineHook(void** originalFunction, void* hookFunction) + { + this->originalFunctionPtr = originalFunction; + + this->originalFunction = *originalFunction; + this->hookFunction = hookFunction; + + PointToCodeIfNot(this->originalFunction); + PointToCodeIfNot(this->hookFunction); }; - auto destination64 = (uint64_t)destination; - memcpy(&absJumpInstructions[2], &destination64, sizeof(destination64)); - memcpy(jumpLocation, absJumpInstructions, sizeof(absJumpInstructions)); - } - - uintptr_t PrepareRestore() - { - /* - This is not a correct way to do it at all, since not all functions sub from the stack - This needs so much more tests, but it works for now. - */ - - Scanner scanner(originalFunction); - scanner.ScanFor({ 0x48, 0x83, 0xEC }); // sub rsp - - auto restoreSize = scanner.Get() - originalFunction.Get(); - - MemcuryAssert(restoreSize > 0 && restoreSize < 0x100); - - restore.reserve(restoreSize); - for (auto i = 0; i < restoreSize; i++) + bool Commit() { - restore.push_back(originalFunction.GetAs()[i]); + auto fnStart = originalFunction.GetAs(); + + auto restoreSize = PrepareRestore(); + + if (!allocatedPage.IsValid()) + { + allocatedPage = AllocatePageNearAddress(fnStart); + } + + memset(allocatedPage.GetAs(), ASM::MNEMONIC::INT3, 0x1000); + + WriteAbsoluteJump(allocatedPage.GetAs(), hookFunction.GetAs()); + + DWORD oldProtect; + VirtualProtect(fnStart, 1024, PAGE_EXECUTE_READWRITE, &oldProtect); + + auto jmpInstruction = PrepareJMPInstruction(allocatedPage.Get()); + + WriteRestore(); + + memset(fnStart, ASM::MNEMONIC::INT3, restoreSize); + memcpy(fnStart, jmpInstruction, ASM::SIZE_OF_JMP_RELATIVE_INSTRUCTION); + + return true; } - return restoreSize; - } - - void WriteRestore() - { - auto restorePtr = allocatedPage + ASM::SIZE_OF_JMP_ABSLOUTE_INSTRUCTION + 2; - - memcpy(restorePtr.GetAs(), restore.data(), restore.size()); - - *originalFunctionPtr = restorePtr.GetAs(); - - // Write a jump back to where the execution should resume - restorePtr.AbsoluteOffset((uint32_t)restore.size()); - - auto contuineExecution = originalFunction + restore.size(); - - WriteAbsoluteJump(restorePtr.GetAs(), contuineExecution.GetAs()); - } - - auto PrepareJMPInstruction(uint64_t dst) - { - uint8_t bytes[5] = { ASM::Mnemonic("JMP_REL32"), 0x0, 0x0, 0x0, 0x0 }; - - const uint64_t relAddr = dst - (originalFunction.Get() + ASM::SIZE_OF_JMP_RELATIVE_INSTRUCTION); - memcpy(bytes + 1, &relAddr, 4); - - return std::move(bytes); - } - - bool IsHooked() - { - return originalFunction.GetAs()[0] == ASM::Mnemonic("JMP_REL32"); - } - - public: - TrampolineHook(void** originalFunction, void* hookFunction) - { - this->originalFunctionPtr = originalFunction; - - this->originalFunction = *originalFunction; - this->hookFunction = hookFunction; - - PointToCodeIfNot(this->originalFunction); - PointToCodeIfNot(this->hookFunction); - }; - - bool Commit() - { - auto fnStart = originalFunction.GetAs(); - - auto restoreSize = PrepareRestore(); - - if (!allocatedPage.IsValid()) + bool Revert() { - allocatedPage = AllocatePageNearAddress(fnStart); + auto fnStart = originalFunction.GetAs(); + + DWORD oldProtect; + VirtualProtect(fnStart, 1024, PAGE_EXECUTE_READWRITE, &oldProtect); + + memcpy(fnStart, restore.data(), restore.size()); + + *originalFunctionPtr = originalFunction.GetAs(); + + // VirtualFree(allocatedPage.GetAs(), 0x1000, MEM_RELEASE); + + return true; } - memset(allocatedPage.GetAs(), ASM::MNEMONIC::INT3, 0x1000); - - WriteAbsoluteJump(allocatedPage.GetAs(), hookFunction.GetAs()); - - DWORD oldProtect; - VirtualProtect(fnStart, 1024, PAGE_EXECUTE_READWRITE, &oldProtect); - - auto jmpInstruction = PrepareJMPInstruction(allocatedPage.Get()); - - WriteRestore(); - - memset(fnStart, ASM::MNEMONIC::INT3, restoreSize); - memcpy(fnStart, jmpInstruction, ASM::SIZE_OF_JMP_RELATIVE_INSTRUCTION); - - return true; - } - - bool Revert() - { - auto fnStart = originalFunction.GetAs(); - - DWORD oldProtect; - VirtualProtect(fnStart, 1024, PAGE_EXECUTE_READWRITE, &oldProtect); - - memcpy(fnStart, restore.data(), restore.size()); - - *originalFunctionPtr = originalFunction.GetAs(); - - // VirtualFree(allocatedPage.GetAs(), 0x1000, MEM_RELEASE); - - return true; - } - - auto Toggle() - { - if (IsHooked()) - Revert(); - else - Commit(); - - return IsHooked(); - } - }; - - namespace VEHHook - { - struct HOOK_INFO - { - void* Original; - void* Detour; - - HOOK_INFO(void* Original, void* Detour) - : Original(Original) - , Detour(Detour) + auto Toggle() { + if (IsHooked()) + Revert(); + else + Commit(); + + return IsHooked(); } }; - inline std::vector Hooks; - inline std::vector HookProtections; - inline HANDLE ExceptionHandler; - - inline long Handler(EXCEPTION_POINTERS* Exception) + namespace VEHHook { - if (Exception->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) + struct HOOK_INFO { - auto Itr = std::find_if(Hooks.begin(), Hooks.end(), [Rip = Exception->ContextRecord->Rip](const HOOK_INFO& Hook) - { return Hook.Original == (void*)Rip; }); - if (Itr != Hooks.end()) + void* Original; + void* Detour; + + HOOK_INFO(void* Original, void* Detour) + : Original(Original) + , Detour(Detour) { - Exception->ContextRecord->Rip = (uintptr_t)Itr->Detour; + } + }; + + inline std::vector Hooks; + inline std::vector HookProtections; + inline HANDLE ExceptionHandler; + + inline long Handler(EXCEPTION_POINTERS* Exception) + { + if (Exception->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) + { + auto Itr = std::find_if(Hooks.begin(), Hooks.end(), [Rip = Exception->ContextRecord->Rip](const HOOK_INFO& Hook) + { return Hook.Original == (void*)Rip; }); + if (Itr != Hooks.end()) + { + Exception->ContextRecord->Rip = (uintptr_t)Itr->Detour; + } + + Exception->ContextRecord->EFlags |= 0x100; // SINGLE_STEP_FLAG + + return EXCEPTION_CONTINUE_EXECUTION; + } + else if (Exception->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) + { + // TODO: find a way to only vp the function that about to get executed + for (auto& Hook : Hooks) + { + DWORD dwOldProtect; + VirtualProtect(Hook.Original, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &dwOldProtect); + } + + return EXCEPTION_CONTINUE_EXECUTION; } - Exception->ContextRecord->EFlags |= 0x100; // SINGLE_STEP_FLAG - - return EXCEPTION_CONTINUE_EXECUTION; + return EXCEPTION_CONTINUE_SEARCH; } - else if (Exception->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) + + inline bool Init() { - // TODO: find a way to only vp the function that about to get executed - for (auto& Hook : Hooks) + if (ExceptionHandler == nullptr) { - DWORD dwOldProtect; - VirtualProtect(Hook.Original, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &dwOldProtect); + ExceptionHandler = AddVectoredExceptionHandler(true, (PVECTORED_EXCEPTION_HANDLER)Handler); + } + return ExceptionHandler != nullptr; + } + + inline bool AddHook(void* Target, void* Detour) + { + if (ExceptionHandler == nullptr) + { + return false; } - return EXCEPTION_CONTINUE_EXECUTION; + if (Util::IsSamePage(Target, Detour)) + { + return false; + } + + if (!VirtualProtect(Target, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &HookProtections.emplace_back())) + { + HookProtections.pop_back(); + return false; + } + + Hooks.emplace_back(Target, Detour); + return true; } - return EXCEPTION_CONTINUE_SEARCH; - } - - inline bool Init() - { - if (ExceptionHandler == nullptr) + inline bool RemoveHook(void* Original) { - ExceptionHandler = AddVectoredExceptionHandler(true, (PVECTORED_EXCEPTION_HANDLER)Handler); - } - return ExceptionHandler != nullptr; - } + auto Itr = std::find_if(Hooks.begin(), Hooks.end(), [Original](const HOOK_INFO& Hook) + { return Hook.Original == Original; }); + + if (Itr == Hooks.end()) + { + return false; + } + + const auto ProtItr = HookProtections.begin() + std::distance(Hooks.begin(), Itr); + Hooks.erase(Itr); + + DWORD dwOldProtect; + bool Ret = VirtualProtect(Original, 1, *ProtItr, &dwOldProtect); + HookProtections.erase(ProtItr); - inline bool AddHook(void* Target, void* Detour) - { - if (ExceptionHandler == nullptr) - { return false; } - - if (Util::IsSamePage(Target, Detour)) - { - return false; - } - - if (!VirtualProtect(Target, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &HookProtections.emplace_back())) - { - HookProtections.pop_back(); - return false; - } - - Hooks.emplace_back(Target, Detour); - return true; - } - - inline bool RemoveHook(void* Original) - { - auto Itr = std::find_if(Hooks.begin(), Hooks.end(), [Original](const HOOK_INFO& Hook) - { return Hook.Original == Original; }); - - if (Itr == Hooks.end()) - { - return false; - } - - const auto ProtItr = HookProtections.begin() + std::distance(Hooks.begin(), Itr); - Hooks.erase(Itr); - - DWORD dwOldProtect; - bool Ret = VirtualProtect(Original, 1, *ProtItr, &dwOldProtect); - HookProtections.erase(ProtItr); - - return false; } } -} -inline int HexToDec(std::string hexValue) -{ - int len = hexValue.size(); - // Initializing base value to 1, i.e 16 ^ 0 - int base = 1; - int dec_value = 0; - // extracting characters as digits from last - // character - for (int i = len - 1; i >= 0; i--) { - if (hexValue[i] >= '0' && hexValue[i] <= '9') { - dec_value += (int(hexValue[i]) - 48) * base; - - - // incrementing base by power - base = base * 16; - } - // if character lies in 'A' - 'F' , converting - // it to integral 10 - 15 by subtracting 55 - // from ASCII value - else if (hexValue[i] >= 'A' && hexValue[i] <= 'F') { - dec_value += (int(hexValue[i]) - 55) * base; - - - // incrementing base by power - base = base * 16; - } - } - return dec_value; -} - -inline std::string GetBytes(uintptr_t Address, int count = 10) { - std::string Bytes; - - for (int i = 0; i < count; i++) { - auto byte = *(uint8_t*)(Address + i) & 0xff; - auto Byte = (byte == 0) ? "? " : std::format("{:x} ", byte); - - if (Byte.length() == 2 && byte != 0) // 2 because of the space - Byte = "0" + Byte; - - Bytes += Byte; - } - - std::transform(Bytes.begin(), Bytes.end(), Bytes.begin(), ::toupper); - - return Bytes; -} - -inline bool IsBadReadPtr(void* p) -{ - MEMORY_BASIC_INFORMATION mbi = { 0 }; - if (::VirtualQuery(p, &mbi, sizeof(mbi))) + inline int HexToDec(std::string hexValue) { - DWORD mask = (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY); - bool b = !(mbi.Protect & mask); - // check the page is not a guard page - if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true; + int len = hexValue.size(); + // Initializing base value to 1, i.e 16 ^ 0 + int base = 1; + int dec_value = 0; + // extracting characters as digits from last + // character + for (int i = len - 1; i >= 0; i--) { + if (hexValue[i] >= '0' && hexValue[i] <= '9') { + dec_value += (int(hexValue[i]) - 48) * base; - return b; + + // incrementing base by power + base = base * 16; + } + // if character lies in 'A' - 'F' , converting + // it to integral 10 - 15 by subtracting 55 + // from ASCII value + else if (hexValue[i] >= 'A' && hexValue[i] <= 'F') { + dec_value += (int(hexValue[i]) - 55) * base; + + + // incrementing base by power + base = base * 16; + } + } + return dec_value; } - return true; -} -inline void VirtualSwap(void** VTable, int Idx, void* NewFunc) -{ - DWORD dwProtection; - VirtualProtect(VTable, (Idx + 8), PAGE_EXECUTE_READWRITE, &dwProtection); + inline std::string GetBytes(uintptr_t Address, int count = 10) { + std::string Bytes; - VTable[Idx] = NewFunc; + for (int i = 0; i < count; i++) { + auto byte = *(uint8_t*)(Address + i) & 0xff; + auto Byte = (byte == 0) ? "? " : std::format("{:x} ", byte); - DWORD dwTemp; - VirtualProtect(VTable, (Idx + 8), dwProtection, &dwTemp); -} \ No newline at end of file + if (Byte.length() == 2 && byte != 0) // 2 because of the space + Byte = "0" + Byte; + + Bytes += Byte; + } + + std::transform(Bytes.begin(), Bytes.end(), Bytes.begin(), ::toupper); + + return Bytes; + } + + inline bool IsBadReadPtr(void* p) + { + MEMORY_BASIC_INFORMATION mbi = { 0 }; + if (::VirtualQuery(p, &mbi, sizeof(mbi))) + { + DWORD mask = (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY); + bool b = !(mbi.Protect & mask); + // check the page is not a guard page + if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true; + + return b; + } + return true; + } + + inline void VirtualSwap(void** VTable, int Idx, void* NewFunc) + { + DWORD dwProtection; + VirtualProtect(VTable, (Idx + 8), PAGE_EXECUTE_READWRITE, &dwProtection); + + VTable[Idx] = NewFunc; + + DWORD dwTemp; + VirtualProtect(VTable, (Idx + 8), dwProtection, &dwTemp); + } + + // Finds a string ref, then goes searches xref of the function that it's in and returns that address. + inline uintptr_t FindFunctionCall(const wchar_t* Name, const std::vector& Bytes = { 0x48, 0x89, 0x5C }, int skip = 0) // credit ender & me + { + auto FunctionPtr = Memcury::Scanner::FindStringRef(Name, true, skip).ScanFor({ 0x48, 0x8D, 0x0D }).RelativeOffset(3).GetAs(); + + return Memcury::Scanner::FindPointerRef(FunctionPtr).ScanFor(Bytes, false).Get(); + } \ No newline at end of file