Implemented #20 into hack.hpp now featuring a better entity loop and fixing known bugs while cleaning the code.

Added simple config system to enable team esp, automatic updates, and render distance.

Also moved the offsets to the updater namespace and read/writte methods to the offsets.json

Implemented Handle Hijacking method requested by #19 to improve the security. Credits to Apxaey on github for the source
This commit is contained in:
IMXNOOBX
2023-10-03 17:52:46 +02:00
parent 53a65d22c2
commit 509d2b5f2d
12 changed files with 524 additions and 74 deletions
+2
View File
@@ -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!
+80 -14
View File
@@ -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();
}
}
+16
View File
@@ -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;
}
}
+9 -28
View File
@@ -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,15 +33,10 @@ namespace config {
void save() {
json data;
data["dwLocalPlayer"] = dwLocalPlayer;
data["dwEntityList"] = dwEntityList;
data["dwViewMatrix"] = dwViewMatrix;
data["team_esp"] = team_esp;
data["automatic_update"] = automatic_update;
data["render_distance"] = render_distance;
data["dwPawnHealth"] = dwPawnHealth;
data["dwPlayerPawn"] = dwPlayerPawn;
data["dwSanitizedName"] = dwSanitizedName;
data["m_iTeamNum"] = m_iTeamNum;
data["m_vecOrigin"] = m_vecOrigin;
std::ofstream output(file_path);
output << std::setw(4) << data << std::endl;
+7 -9
View File
@@ -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;
}
+65 -16
View File
@@ -1,6 +1,6 @@
#include <thread>
#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<uintptr_t>(base_module.base + config::dwLocalPlayer);
const uintptr_t localPlayer = process->read<uintptr_t>(base_module.base + updater::offsets::dwLocalPlayer);
if (!localPlayer)
return;
const int localTeam = process->read<int>(localPlayer + config::m_iTeamNum);
const view_matrix_t view_matrix = process->read<view_matrix_t>(base_module.base + config::dwViewMatrix);
const Vector3 localOrigin = process->read<Vector3>(localPlayer + config::m_vecOrigin);
const uintptr_t entity_list = process->read<uintptr_t>(base_module.base + config::dwEntityList);
const int localTeam = process->read<int>(localPlayer + updater::offsets::m_iTeamNum);
const view_matrix_t view_matrix = process->read<view_matrix_t>(base_module.base + updater::offsets::dwViewMatrix);
const Vector3 localOrigin = process->read<Vector3>(localPlayer + updater::offsets::m_vecOrigin);
const uintptr_t entity_list = process->read<uintptr_t>(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<int>(player + config::dwPawnHealth);
const int playerHealth = process->read<int>(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<int>(player + config::m_iTeamNum);
if (playerTeam == localTeam) {
const int playerTeam = process->read<int>(player + updater::offsets::m_iTeamNum);
if (config::team_esp && (playerTeam == localTeam)) {
playerIndex++;
continue;
}
const std::uint32_t playerPawn = process->read<std::uint32_t>(player + config::dwPlayerPawn);
const std::uint32_t playerPawn = process->read<std::uint32_t>(player + updater::offsets::dwPlayerPawn);
const uintptr_t list_entry2 = process->read<uintptr_t>(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<DWORD64>(player + config::dwSanitizedName);
const DWORD64 playerNameAddress = process->read<DWORD64>(player + updater::offsets::dwSanitizedName);
if (playerNameAddress) {
char buf[256];
@@ -78,9 +83,14 @@ namespace hack {
playerName = std::string(buf);
}
const Vector3 origin = process->read<Vector3>(pCSPlayerPawn + config::m_vecOrigin);
const Vector3 origin = process->read<Vector3>(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++;
}
+17 -6
View File
@@ -69,20 +69,26 @@ int main() {
hack::process = std::make_shared<pProcess>();
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);
+1
View File
@@ -151,6 +151,7 @@
<ClInclude Include="classes\render.hpp" />
<ClInclude Include="classes\vector.hpp" />
<ClInclude Include="hacks\hack.hpp" />
<ClInclude Include="memory\handle_hijack.hpp" />
<ClInclude Include="memory\memory.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
@@ -53,5 +53,8 @@
<ClInclude Include="classes\auto_updater.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="memory\handle_hijack.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
+286
View File
@@ -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 <Windows.h>
#include <iostream>
#include <TlHelp32.h>
#include <string>
// 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;
}
}
+36
View File
@@ -1,5 +1,6 @@
#include "memory.hpp"
#include <tlhelp32.h>
#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);
+1
View File
@@ -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();