diff --git a/README.md b/README.md index 642bfcb..7cea3c0 100644 --- a/README.md +++ b/README.md @@ -58,4 +58,6 @@ Simple external esp using gdi overlay to render esp boxes on top of cs2 highligh ## 💫 Credits * [UnnamedZ03](https://github.com/UnnamedZ03) for providing [offsets](https://www.unknowncheats.me/forum/3846642-post734.html) and guide with his [CS2-external-base](https://github.com/UnnamedZ03/CS2-external-base) +* [Bekston](https://github.com/Bekston) for his [contributions](https://github.com/IMXNOOBX/cs2-external-esp/pull/20) to the project and ideas +* [Apxaey](https://github.com/Apxaey) for releasing an easy way to implement [handle hijacking](https://github.com/Apxaey/Handle-Hijacking-Anti-Cheat-Bypass) * Unknowncheats comunity for their reseach! diff --git a/memory-external/classes/auto_updater.cpp b/memory-external/classes/auto_updater.cpp index f6b8577..5bc5402 100644 --- a/memory-external/classes/auto_updater.cpp +++ b/memory-external/classes/auto_updater.cpp @@ -2,7 +2,7 @@ namespace updater { - bool check_and_update(bool prompt_update) { + bool check_and_update(bool automatic_update) { json commit; if (!get_last_commit_date(commit)) { std::cout << "[updater] error getting last commit information from GitHub" << std::endl; @@ -30,30 +30,38 @@ namespace updater { // Check if the local file is older than the last GitHub commit if (lastModifiedClockTime < commitTimePoint) { std::cout << "[updater] Local file is older than the last GitHub commit." << std::endl; - if (prompt_update) { + + char response; + if (!automatic_update) { std::cout << "[updater] Do you want to download the latest offsets? (y/n): "; - char response; std::cin >> response; - if (response == 'Y' || response == 'y') { - if (download_file(raw_updated_offets.c_str(), "offsets.json")) { - std::cout << "[updater] Successfully downloaded latest offsets.json file\n" << std::endl; - return true; - } - else { - std::cout << "[updater] Error: Failed to download file, try downloading manually from " << raw_updated_offets << "\n" << std::endl; - } + } + + if (automatic_update || (response == 'Y' || response == 'y')) { + if (download_file(raw_updated_offets.c_str(), "offsets.json")) { + std::cout << "[updater] Successfully downloaded latest offsets.json file\n" << std::endl; + return true; + } + else { + std::cout << "[updater] Error: Failed to download file, try downloading manually from " << raw_updated_offets << "\n" << std::endl; } } + } else { std::cout << "[updater] Local file is up to date.\n" << std::endl; } } else { - std::cout << "[updater] Do you want to download the latest offsets? (y/n): "; + + char response; - std::cin >> response; - if (response == 'Y' || response == 'y') { + if (!automatic_update) { + std::cout << "[updater] Do you want to download the latest offsets? (y/n): "; + std::cin >> response; + } + + if (automatic_update || (response == 'Y' || response == 'y')) { if (download_file(raw_updated_offets.c_str(), "offsets.json")) { std::cout << "[updater] Successfully downloaded latest offsets.json file\n" << std::endl; return true; @@ -156,4 +164,62 @@ namespace updater { return true; } + + bool read() { + if (!updater::file_good(file_path)) { + save(); + return false; + } + + std::ifstream f(file_path); + + json data; + try { + data = json::parse(f); + } + catch (const std::exception& e) { + save(); + } + + if (data.empty()) + return false; + + if (data["dwLocalPlayer"].is_number()) + offsets::dwLocalPlayer = data["dwLocalPlayer"]; + if (data["dwEntityList"].is_number()) + offsets::dwEntityList = data["dwEntityList"]; + if (data["dwViewMatrix"].is_number()) + offsets::dwViewMatrix = data["dwViewMatrix"]; + + if (data["dwPawnHealth"].is_number()) + offsets::dwPawnHealth = data["dwPawnHealth"]; + if (data["dwPlayerPawn"].is_number()) + offsets::dwPlayerPawn = data["dwPlayerPawn"]; + if (data["dwSanitizedName"].is_number()) + offsets::dwSanitizedName = data["dwSanitizedName"]; + if (data["m_iTeamNum"].is_number()) + offsets::m_iTeamNum = data["m_iTeamNum"]; + if (data["m_vecOrigin"].is_number()) + offsets::m_vecOrigin = data["m_vecOrigin"]; + + return true; + } + + void save() { + json data; + + data["dwLocalPlayer"] = offsets::dwLocalPlayer; + data["dwEntityList"] = offsets::dwEntityList; + data["dwViewMatrix"] = offsets::dwViewMatrix; + + data["dwPawnHealth"] = offsets::dwPawnHealth; + data["dwPlayerPawn"] = offsets::dwPlayerPawn; + data["dwSanitizedName"] = offsets::dwSanitizedName; + data["m_iTeamNum"] = offsets::m_iTeamNum; + data["m_vecOrigin"] = offsets::m_vecOrigin; + + std::ofstream output(file_path); + output << std::setw(4) << data << std::endl; + output.close(); + } } \ No newline at end of file diff --git a/memory-external/classes/auto_updater.hpp b/memory-external/classes/auto_updater.hpp index 7c7af31..303c85b 100644 --- a/memory-external/classes/auto_updater.hpp +++ b/memory-external/classes/auto_updater.hpp @@ -15,12 +15,28 @@ namespace fs = std::filesystem; namespace updater { + const std::string file_path = "offsets.json"; bool check_and_update(bool prompt_update); bool get_last_commit_date(json& commit); bool download_file(const char* url, const char* localPath); bool file_good(const std::string& name); + extern bool read(); + extern void save(); + inline std::string github_repo_api = "https://api.github.com/repos/IMXNOOBX/cs2-external-esp/commits"; inline std::string raw_updated_offets = "https://github.com/IMXNOOBX/cs2-external-esp/raw/main/offsets/offsets.json"; + + namespace offsets { + inline std::ptrdiff_t dwLocalPlayer = 0x187AC28; + inline std::ptrdiff_t dwEntityList = 0x178D8C8; + inline std::ptrdiff_t dwViewMatrix = 0x187B710; + + inline std::ptrdiff_t dwPawnHealth = 0x808; + inline std::ptrdiff_t dwPlayerPawn = 0x7FC; + inline std::ptrdiff_t dwSanitizedName = 0x720; + inline std::ptrdiff_t m_iTeamNum = 0x3bf; + inline std::ptrdiff_t m_vecOrigin = 0x1204; + } } \ No newline at end of file diff --git a/memory-external/classes/config.cpp b/memory-external/classes/config.cpp index 970bc8c..0ebbc54 100644 --- a/memory-external/classes/config.cpp +++ b/memory-external/classes/config.cpp @@ -1,9 +1,6 @@ #include "config.hpp" namespace config { - using json = nlohmann::json; - const std::string file_path = "offsets.json"; - bool read() { if (!updater::file_good(file_path)) { save(); @@ -23,23 +20,12 @@ namespace config { if (data.empty()) return false; - if (data["dwLocalPlayer"].is_number()) - dwLocalPlayer = data["dwLocalPlayer"]; - if (data["dwEntityList"].is_number()) - dwEntityList = data["dwEntityList"]; - if (data["dwViewMatrix"].is_number()) - dwViewMatrix = data["dwViewMatrix"]; - - if (data["dwPawnHealth"].is_number()) - dwPawnHealth = data["dwPawnHealth"]; - if (data["dwPlayerPawn"].is_number()) - dwPlayerPawn = data["dwPlayerPawn"]; - if (data["dwSanitizedName"].is_number()) - dwSanitizedName = data["dwSanitizedName"]; - if (data["m_iTeamNum"].is_number()) - m_iTeamNum = data["m_iTeamNum"]; - if (data["m_vecOrigin"].is_number()) - m_vecOrigin = data["m_vecOrigin"]; + if (data["team_esp"].is_boolean()) + team_esp = data["team_esp"]; + if (data["automatic_update"].is_boolean()) + automatic_update = data["automatic_update"]; + if (data["render_distance"].is_number()) + render_distance = data["render_distance"]; return true; } @@ -47,16 +33,11 @@ namespace config { void save() { json data; - data["dwLocalPlayer"] = dwLocalPlayer; - data["dwEntityList"] = dwEntityList; - data["dwViewMatrix"] = dwViewMatrix; - - data["dwPawnHealth"] = dwPawnHealth; - data["dwPlayerPawn"] = dwPlayerPawn; - data["dwSanitizedName"] = dwSanitizedName; - data["m_iTeamNum"] = m_iTeamNum; - data["m_vecOrigin"] = m_vecOrigin; + data["team_esp"] = team_esp; + data["automatic_update"] = automatic_update; + data["render_distance"] = render_distance; + std::ofstream output(file_path); output << std::setw(4) << data << std::endl; output.close(); diff --git a/memory-external/classes/config.hpp b/memory-external/classes/config.hpp index 24d5b91..b087611 100644 --- a/memory-external/classes/config.hpp +++ b/memory-external/classes/config.hpp @@ -3,17 +3,15 @@ #include "json.hpp" #include "auto_updater.hpp" +using json = nlohmann::json; + namespace config { + const std::string file_path = "config.json"; + extern bool read(); extern void save(); - inline std::ptrdiff_t dwLocalPlayer = 0x17DB108; - inline std::ptrdiff_t dwEntityList = 0x178C888; - inline std::ptrdiff_t dwViewMatrix = 0x187A6E0; - - inline std::ptrdiff_t dwPawnHealth = 0x808; - inline std::ptrdiff_t dwPlayerPawn = 0x7FC; - inline std::ptrdiff_t dwSanitizedName = 0x720; - inline std::ptrdiff_t m_iTeamNum = 0x3bf; - inline std::ptrdiff_t m_vecOrigin = 0x1204; + inline bool automatic_update = false; + inline bool team_esp = false; + inline float render_distance = -1.f; } \ No newline at end of file diff --git a/memory-external/hacks/hack.hpp b/memory-external/hacks/hack.hpp index 472031e..e631284 100644 --- a/memory-external/hacks/hack.hpp +++ b/memory-external/hacks/hack.hpp @@ -1,6 +1,6 @@ #include #include "../memory/memory.hpp" -#include "classes/globals.hpp" +#include "../classes/globals.hpp" #include "../classes/render.hpp" #include "../classes/config.hpp" @@ -9,14 +9,14 @@ namespace hack { ProcessModule base_module; void loop() { - const uintptr_t localPlayer = process->read(base_module.base + config::dwLocalPlayer); + const uintptr_t localPlayer = process->read(base_module.base + updater::offsets::dwLocalPlayer); if (!localPlayer) return; - const int localTeam = process->read(localPlayer + config::m_iTeamNum); - const view_matrix_t view_matrix = process->read(base_module.base + config::dwViewMatrix); - const Vector3 localOrigin = process->read(localPlayer + config::m_vecOrigin); - const uintptr_t entity_list = process->read(base_module.base + config::dwEntityList); + const int localTeam = process->read(localPlayer + updater::offsets::m_iTeamNum); + const view_matrix_t view_matrix = process->read(base_module.base + updater::offsets::dwViewMatrix); + const Vector3 localOrigin = process->read(localPlayer + updater::offsets::m_vecOrigin); + const uintptr_t entity_list = process->read(base_module.base + updater::offsets::dwEntityList); int playerIndex = 1; uintptr_t list_entry; @@ -37,7 +37,7 @@ namespace hack { continue; } - const int playerHealth = process->read(player + config::dwPawnHealth); + const int playerHealth = process->read(player + updater::offsets::dwPawnHealth); if (playerHealth <= 0 || playerHealth > 100) { playerIndex++; continue; @@ -49,13 +49,13 @@ namespace hack { * If you really want you can exclude your own character from the check but * since you are in the same team as yourself it will be excluded anyway **/ - const int playerTeam = process->read(player + config::m_iTeamNum); - if (playerTeam == localTeam) { + const int playerTeam = process->read(player + updater::offsets::m_iTeamNum); + if (config::team_esp && (playerTeam == localTeam)) { playerIndex++; continue; } - const std::uint32_t playerPawn = process->read(player + config::dwPlayerPawn); + const std::uint32_t playerPawn = process->read(player + updater::offsets::dwPlayerPawn); const uintptr_t list_entry2 = process->read(entity_list + 0x8 * ((playerPawn & 0x7FFF) >> 9) + 16); if (!list_entry2) { @@ -69,8 +69,13 @@ namespace hack { continue; } + if (config::team_esp && (pCSPlayerPawn == localPlayer)) { + playerIndex++; + continue; + } + std::string playerName = "Invalid Name"; - const DWORD64 playerNameAddress = process->read(player + config::dwSanitizedName); + const DWORD64 playerNameAddress = process->read(player + updater::offsets::dwSanitizedName); if (playerNameAddress) { char buf[256]; @@ -78,9 +83,14 @@ namespace hack { playerName = std::string(buf); } - const Vector3 origin = process->read(pCSPlayerPawn + config::m_vecOrigin); + const Vector3 origin = process->read(pCSPlayerPawn + updater::offsets::m_vecOrigin); const Vector3 head = { origin.x, origin.y, origin.z + 75.f }; + if (config::render_distance != -1 && (localOrigin - origin).length2d() > config::render_distance) { + playerIndex++; + continue; + } + const Vector3 screenPos = origin.world_to_screen(view_matrix); const Vector3 screenHead = head.world_to_screen(view_matrix); @@ -91,10 +101,49 @@ namespace hack { const COLORREF boxColor = RGB(175, 75, 75); const COLORREF healthBarColor = RGB(255 - playerHealth, 55 + playerHealth * 2, 75); - render::DrawBorderBox(g::hdcBuffer, screenHead.x - width / 2, screenHead.y, width, height, boxColor); - render::DrawBorderBox(g::hdcBuffer, screenHead.x - (width / 2 + 5), screenHead.y + (height * (100 - playerHealth) / 100), 2, height - (height * (100 - playerHealth) / 100), healthBarColor); - render::RenderText(g::hdcBuffer, screenHead.x + (width / 2 + 5), screenHead.y, playerName.c_str(), RGB(75, 75, 175), 10); - render::RenderText(g::hdcBuffer, screenHead.x + (width / 2 + 5), screenHead.y + 10, (std::to_string(playerHealth) + "hp").c_str(), healthBarColor, 10); + render::DrawBorderBox( + g::hdcBuffer, + screenHead.x - width / 2, + screenHead.y, + width, + height, + (localTeam == playerTeam ? RGB(75, 175, 75) : RGB(175, 75, 75)) + ); + + render::DrawBorderBox( + g::hdcBuffer, + screenHead.x - (width / 2 + 5), + screenHead.y + (height * (100 - playerHealth) / 100), + 2, + height - (height * (100 - playerHealth) / 100), + RGB( + (255 - playerHealth), + (55 + playerHealth * 2), + 75 + ) + ); + + render::RenderText( + g::hdcBuffer, + screenHead.x + (width / 2 + 5), + screenHead.y, + playerName.c_str(), + RGB(75, 75, 175), + 10 + ); + + render::RenderText( + g::hdcBuffer, + screenHead.x + (width / 2 + 5), + screenHead.y + 10, + (std::to_string(playerHealth) + "hp").c_str(), + RGB( + (255 - playerHealth), + (55 + playerHealth * 2), + 75 + ), + 10 + ); } playerIndex++; } diff --git a/memory-external/main.cpp b/memory-external/main.cpp index 22e3509..82969fa 100644 --- a/memory-external/main.cpp +++ b/memory-external/main.cpp @@ -69,20 +69,26 @@ int main() { hack::process = std::make_shared(); - updater::check_and_update(true); + std::cout << "[config] Reading configuration." << std::endl; + if (config::read()) + std::cout << "[updater] Sucessfully read configuration file\n" << std::endl; + else + std::cout << "[updater] Error reading config file, reseting to the default state\n" << std::endl; + + updater::check_and_update(config::automatic_update); std::cout << "[cs2] Waiting for cs2.exe..." << std::endl; - while (!hack::process->AttachProcess("cs2.exe")) + while (!hack::process->AttachProcessHj("cs2.exe")) std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "[cs2] Attached to cs2.exe\n" << std::endl; - std::cout << "[config] Reading offsets from configuration." << std::endl; - if (config::read()) - std::cout << "[config] Sucessfully read offsets file\n" << std::endl; + std::cout << "[updater] Reading offsets from file offsets.json." << std::endl; + if (updater::read()) + std::cout << "[updater] Sucessfully read offsets file\n" << std::endl; else - std::cout << "[config] Error reading offsets file, reseting to the default state\n" << std::endl; + std::cout << "[updater] Error reading offsets file, reseting to the default state\n" << std::endl; std::cout << "[warn] If the esp doesnt work, consider updating offsets manually in the file offsets.json" << std::endl; @@ -128,12 +134,17 @@ int main() { ShowWindow(hWnd, TRUE); //SetActiveWindow(hack::process->hwnd_); + std::cout << "\n[settings] In Game keybinds:\n\t[F5] enable/disable Team ESP\n\t[F6] enable/disable automatic updates\n\t[fin] Unload esp.\n" << std::endl; + // Message loop MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { if (GetAsyncKeyState(VK_END) & 0x8000) break; + if (GetAsyncKeyState(VK_F5) & 0x8000) { config::team_esp = !config::team_esp; config::save(); Beep(700, 100); }; + if (GetAsyncKeyState(VK_F6) & 0x8000) { config::automatic_update = !config::automatic_update; config::save(); Beep(700, 100); } + TranslateMessage(&msg); DispatchMessage(&msg); diff --git a/memory-external/memory-external.vcxproj b/memory-external/memory-external.vcxproj index d0343dd..43f56aa 100644 --- a/memory-external/memory-external.vcxproj +++ b/memory-external/memory-external.vcxproj @@ -151,6 +151,7 @@ + diff --git a/memory-external/memory-external.vcxproj.filters b/memory-external/memory-external.vcxproj.filters index 702b1b1..8d1391e 100644 --- a/memory-external/memory-external.vcxproj.filters +++ b/memory-external/memory-external.vcxproj.filters @@ -53,5 +53,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/memory-external/memory/handle_hijack.hpp b/memory-external/memory/handle_hijack.hpp new file mode 100644 index 0000000..dbae527 --- /dev/null +++ b/memory-external/memory/handle_hijack.hpp @@ -0,0 +1,286 @@ +/* + Credits to: https://github.com/Apxaey/Handle-Hijacking-Anti-Cheat-Bypass for the source and public sharing! + + Its a little bit messy as i tried to make it asap. All comments below this are from the original creator! +*/ + +/* + This is a stand alone bypass made by Apxaey. Feel free to use this in your cheats but credit me for the bypass as i put alot of time into this. + If you have some brain cells you will be able to incorporate this into your cheats and remain undetected by user-mode anticheats. + Obviously standard cheat 'recommendations' still apply: + 1.) Use self-written or not signatured code + 2.) Dont write impossible values + 3.) If your going internal use a manual map injector + + If you follow the guidelines above and use this bypass you will be safe from usermode anticheats like VAC. + Obviously you can build and adapt upon my code to suit your needs. + If I was to make a cheat for myself i would put this bypass into something i call an 'external internal' cheat. + Whereby you make a cheat and inject into a legitimate program like discord and add a check to the this bypass to only hijack a handle from the process you inject into, giving the appearence that nothing is out of the ordinary + However you can implement this bypass into any form of cheat, its your decision. + If you need want some more info i recommend you watch my YT video on this bypass. + Anyways if you want to see more of my stuff feel free to join my discord server discord.gg/********. Here's my YT as well https://www.youtube.com/channel/UCPN6OOLxn1OaBP5jPThIiog. +*/ + +#include +#include +#include +#include + +// macros we use.Some can be found in wintrnl.h +#define SeDebugPriv 20 +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004) +#define NtCurrentProcess ( (HANDLE)(LONG_PTR) -1 ) +#define ProcessHandleType 0x7 +#define SystemHandleInformation 16 + +/* +STRUCTURES NEEDED FOR NTOPENPROCESS: +*/ +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWCH Buffer; +} UNICODE_STRING, * PUNICODE_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES; + +typedef struct _CLIENT_ID +{ + PVOID UniqueProcess; + PVOID UniqueThread; +} CLIENT_ID, * PCLIENT_ID; + +/* +STRUCTURES NEEDED FOR HANDLE INFORMATION: +*/ + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO +{ + ULONG ProcessId; + BYTE ObjectTypeNumber; + BYTE Flags; + USHORT Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; +} SYSTEM_HANDLE, * PSYSTEM_HANDLE; //i shortened it to SYSTEM_HANDLE for the sake of typing + +typedef struct _SYSTEM_HANDLE_INFORMATION +{ + ULONG HandleCount; + SYSTEM_HANDLE Handles[1]; +} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION; + +/* +FUNCTION PROTOTYPES: +*/ +typedef NTSTATUS(NTAPI* _NtDuplicateObject)( + HANDLE SourceProcessHandle, + HANDLE SourceHandle, + HANDLE TargetProcessHandle, + PHANDLE TargetHandle, + ACCESS_MASK DesiredAccess, + ULONG Attributes, + ULONG Options + ); + +typedef NTSTATUS(NTAPI* _RtlAdjustPrivilege)( + ULONG Privilege, + BOOLEAN Enable, + BOOLEAN CurrentThread, + PBOOLEAN Enabled + ); + +typedef NTSYSAPI NTSTATUS(NTAPI* _NtOpenProcess)( + PHANDLE ProcessHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + PCLIENT_ID ClientId + ); + +typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)( + ULONG SystemInformationClass, //your supposed to supply the whole class but microsoft kept the enum mostly empty so I just passed 16 instead for handle info. Thats why you get a warning in your code btw + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength + ); + +SYSTEM_HANDLE_INFORMATION* hInfo; //holds the handle information + +//the handles we will need to use later on + +namespace hj { + HANDLE procHandle = NULL; + HANDLE hProcess = NULL; + HANDLE HijackedHandle = NULL; + + // simple function i made that will just initialize our Object_Attributes structure as NtOpenProcess will fail otherwise + OBJECT_ATTRIBUTES InitObjectAttributes(PUNICODE_STRING name, ULONG attributes, HANDLE hRoot, PSECURITY_DESCRIPTOR security) + { + OBJECT_ATTRIBUTES object; + + object.Length = sizeof(OBJECT_ATTRIBUTES); + object.ObjectName = name; + object.Attributes = attributes; + object.RootDirectory = hRoot; + object.SecurityDescriptor = security; + + return object; + } + + bool IsHandleValid(HANDLE handle) // i made this to simply check if a handle is valid rather than repeating the if statments + { + if (handle && handle != INVALID_HANDLE_VALUE) + { + return true; + } + else + { + return false; + } + } + + HANDLE HijackExistingHandle(DWORD dwTargetProcessId) + { + HMODULE Ntdll = GetModuleHandleA("ntdll"); // get the base address of ntdll.dll + + //get the address of RtlAdjustPrivilege in ntdll.dll so we can grant our process the highest permission possible + _RtlAdjustPrivilege RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(Ntdll, "RtlAdjustPrivilege"); + + boolean OldPriv; //store the old privileges + + // Give our program SeDeugPrivileges whcih allows us to get a handle to every process, even the highest privileged SYSTEM level processes. + RtlAdjustPrivilege(SeDebugPriv, TRUE, FALSE, &OldPriv); + + //get the address of NtQuerySystemInformation in ntdll.dll so we can find all the open handles on our system + _NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(Ntdll, "NtQuerySystemInformation"); + + //get the address of NtDuplicateObject in ntdll.dll so we can duplicate an existing handle into our cheat, basically performing the hijacking + _NtDuplicateObject NtDuplicateObject = (_NtDuplicateObject)GetProcAddress(Ntdll, "NtDuplicateObject"); + + //get the address of NtOpenProcess in ntdll.dll so wecan create a Duplicate handle + _NtOpenProcess NtOpenProcess = (_NtOpenProcess)GetProcAddress(Ntdll, "NtOpenProcess"); + + + //initialize the Object Attributes structure, you can just set each member to NULL rather than create a function like i did + OBJECT_ATTRIBUTES Obj_Attribute = InitObjectAttributes(NULL, NULL, NULL, NULL); + + //clientID is a PDWORD or DWORD* of the process id to create a handle to + CLIENT_ID clientID = { 0 }; + + + //the size variable is the amount of bytes allocated to store all the open handles + DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION); + + //we allocate the memory to store all the handles on the heap rather than the stack becuase of the large amount of data + hInfo = (SYSTEM_HANDLE_INFORMATION*) new byte[size]; + + //zero the memory handle info + ZeroMemory(hInfo, size); + + //we use this for checking if the Native functions succeed + NTSTATUS NtRet = NULL; + + do + { + // delete the previously allocated memory on the heap because it wasn't large enough to store all the handles + delete[] hInfo; + + //increase the amount of memory allocated by 50% + size *= 1.5; + try + { + //set and allocate the larger size on the heap + hInfo = (PSYSTEM_HANDLE_INFORMATION) new byte[size]; + } + catch (std::bad_alloc) //catch a bad heap allocation. + { + procHandle ? CloseHandle(procHandle) : 0; + } + Sleep(1); //sleep for the cpu + + //we continue this loop until all the handles have been stored + } while ((NtRet = NtQuerySystemInformation(SystemHandleInformation, hInfo, size, NULL)) == STATUS_INFO_LENGTH_MISMATCH); + + //check if we got all the open handles on our system + if (!NT_SUCCESS(NtRet)) + { + procHandle ? CloseHandle(procHandle) : 0; + } + + + //loop through each handle on our system, and filter out handles that are invalid or cant be hijacked + for (unsigned int i = 0; i < hInfo->HandleCount; ++i) + { + //a variable to store the number of handles OUR cheat has open. + static DWORD NumOfOpenHandles; + + //get the amount of outgoing handles OUR cheat has open + GetProcessHandleCount(GetCurrentProcess(), &NumOfOpenHandles); + + //you can do a higher number if this is triggering false positives. Its just to make sure we dont fuck up and create thousands of handles + if (NumOfOpenHandles > 50) + { + procHandle ? CloseHandle(procHandle) : 0; + } + + //check if the current handle is valid, otherwise increment i and check the next handle + if (!IsHandleValid((HANDLE)hInfo->Handles[i].Handle)) + { + continue; + } + + //check the handle type is 0x7 meaning a process handle so we dont hijack a file handle for example + if (hInfo->Handles[i].ObjectTypeNumber != ProcessHandleType) + { + continue; + } + + + //set clientID to a pointer to the process with the handle to out target + clientID.UniqueProcess = (DWORD*)hInfo->Handles[i].ProcessId; + + //if procHandle is open, close it + procHandle ? CloseHandle(procHandle) : 0; + + //create a a handle with duplicate only permissions to the process with a handle to our target. NOT OUR TARGET. + NtRet = NtOpenProcess(&procHandle, PROCESS_DUP_HANDLE, &Obj_Attribute, &clientID); + if (!IsHandleValid(procHandle) || !NT_SUCCESS(NtRet)) //check is the funcions succeeded and check the handle is valid + { + continue; + } + + + //we duplicate the handle another process has to our target into our cheat with whatever permissions we want. I did all access. + NtRet = NtDuplicateObject(procHandle, (HANDLE)hInfo->Handles[i].Handle, NtCurrentProcess, &HijackedHandle, PROCESS_ALL_ACCESS, 0, 0); + if (!IsHandleValid(HijackedHandle) || !NT_SUCCESS(NtRet))//check is the funcions succeeded and check the handle is valid + { + + continue; + } + + //get the process id of the handle we duplicated and check its to our target + if (GetProcessId(HijackedHandle) != dwTargetProcessId) { + CloseHandle(HijackedHandle); + continue; + } + + + + hProcess = HijackedHandle; + + break; + } + + procHandle ? CloseHandle(procHandle) : 0; + + return hProcess; + } +} diff --git a/memory-external/memory/memory.cpp b/memory-external/memory/memory.cpp index 8adbd72..e42edbe 100644 --- a/memory-external/memory/memory.cpp +++ b/memory-external/memory/memory.cpp @@ -1,5 +1,6 @@ #include "memory.hpp" #include +#include "handle_hijack.hpp" uint32_t pProcess::FindProcessIdByProcessName(const char* ProcessName) { @@ -83,6 +84,41 @@ bool pProcess::AttachProcess(const char* ProcessName) return false; } +bool pProcess::AttachProcessHj(const char* ProcessName) +{ + this->pid_ = this->FindProcessIdByProcessName(ProcessName); + + if (pid_) + { + HMODULE modules[0xFF]; + MODULEINFO module_info; + DWORD _; + + + // Using Apxaey's handle hijack function to safely open a handle + handle_ = hj::HijackExistingHandle(pid_); + + if (!hj::IsHandleValid(handle_)) + { + std::cout << "[cheat] Handle Hijack failed, falling back to OpenProcess method." << std::endl; + return pProcess::AttachProcess(ProcessName); // Handle hijacking failed, so we fall back to the normal OpenProcess method + } + + EnumProcessModulesEx(this->handle_, modules, sizeof(modules), &_, LIST_MODULES_64BIT); + base_module_.base = (uintptr_t)modules[0]; + + GetModuleInformation(this->handle_, modules[0], &module_info, sizeof(module_info)); + base_module_.size = module_info.SizeOfImage; + + hwnd_ = this->GetWindowHandleFromProcessId(pid_); + + return true; + } + + return false; +} + + bool pProcess::AttachWindow(const char* WindowName) { this->pid_ = this->FindProcessIdByWindowName(WindowName); diff --git a/memory-external/memory/memory.hpp b/memory-external/memory/memory.hpp index 4dab227..12d703b 100644 --- a/memory-external/memory/memory.hpp +++ b/memory-external/memory/memory.hpp @@ -24,6 +24,7 @@ public: public: bool AttachProcess(const char* process_name); + bool AttachProcessHj(const char* process_name); bool AttachWindow(const char* window_name); bool UpdateHWND(); void Close();