Всем салам, сегодня будем обходить любые клиентчеки со стороны сервера
Алгоритм обхода у нас такой:
Сохранить к себе чистые модули gta_sa.exe и samp, и при попытке чтения таковых, подменять их.
Все хуки буду ставить через MinHook.
Оффсет обработчика RPC ClientCheck на R3 - 11710; R4 - 0x11A40
В начало кода добавляем это:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Создадим enum для версий сампа:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Объявим переменные констант(как бы это глупо не звучало :D)
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Где нибудь ниже объявим все необходимые переменные:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Объявляем прототипы и трамплины для хуков:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Начнем с обхода захода с R2/R3/R4.
(Перейти к SAMP R1 можете тыкнув сюда)
Возможные варианты подмены чтения gta_sa.exe - хук на месте вызова, либо хук ReadMemory и проверка адреса возврата.
Тут я покажу оба.
В случае с хуком вызова будем хукать это место:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Из-за того что в MinHook нет Call хуков, я буду использовать naked hook. Сам код:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
А чтобы подменять чтение сампа, будем подменять HMODULE у сампа. Он используется только при краше и чтении памяти, так что можем безболезненно менять его.
А в случае с хуком ReadMemory это будет выглядеть так:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Перейдем к обходу для R1. Переход к точке входа
Здесь у нас принцип такой: подменять версию сампа при отправке RPC и на клиентчеки отсылать наш ответ.
Сначала добавьте samp.dll от любой версии которая вам нравится в ресурсы с типом DLL
также не забудьте заменить FAKEVER на версию, dll от которой вы берете
Объявим функцию хука отправки RPC для подмены версии сампа
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Объявим функцию получения чексуммы с куска памяти(Скопировал псевдокод с ida pro):
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Функция хука:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Ну и перейдем к точке входа:
Инициализируем MinHook и получаем все нужные адреса:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Определяем версию сампа:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Ну и конце копируем модуль гташки:
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
На этом наш обход завершен.
Плюсы этого обхода в том, что он скроет любое говно что вы поставите на свою гта: будь то клео, сампфункс, сайлент аим и любые другие читы.
Также если сервер попытается прочитать наш хук, то ничего он не узнает, ведь мы перекидываем его чтение на чистый модуль
Пасиба @HellsCoder за идею обхода, @DarkP1xel за моральную поддержку, а также Lot. за пинки :D
			
			Алгоритм обхода у нас такой:
Сохранить к себе чистые модули gta_sa.exe и samp, и при попытке чтения таковых, подменять их.
Все хуки буду ставить через MinHook.
Оффсет обработчика RPC ClientCheck на R3 - 11710; R4 - 0x11A40
В начало кода добавляем это:
			
				C++:
			
		
		
		#include <Windows.h>
#include <psapi.h>
#include <intrin.h>
#include "MinHook.h"
#pragma comment(lib, "libMinHook-x86-v141-mt.lib")
#pragma intrinsic(_ReturnAddress)
#define FAKEVER "0.3.7-R4"Создадим enum для версий сампа:
			
				C++:
			
		
		
		enum SampVersion {
    SAMP_UNKNOWN = -1,
    SAMP_0_3_7_R1 = 0,
    SAMP_0_3_7_R3_1,
    SAMP_0_3_7_R4,
};Объявим переменные констант(как бы это глупо не звучало :D)
			
				C++:
			
		
		
		DWORD READMEMFUNC;
DWORD SAMPHMODULE;
DWORD HOOKREADMEM;
DWORD HOOKEXITREADMEM;Где нибудь ниже объявим все необходимые переменные:
			
				C++:
			
		
		
		HMODULE hSAMPModule;
HMODULE hGTAModule;
DWORD dwSampModule;
DWORD dwGTAModule;
unsigned char* ClearSAMPModule = nullptr; // байты чистого сампа
unsigned char* ClearGTAModule = nullptr; // байты чистой гташкиОбъявляем прототипы и трамплины для хуков:
			
				C++:
			
		
		
		typedef bool(__fastcall* RakPeer_RPC)(void*, void*, int*, BitStream*, int, int, int, int, __int16, int, int, int, int, int);
typedef unsigned char(__cdecl* ReadMemory)(int, unsigned __int16);
typedef void(__cdecl* ClientCheck)(RPCParameters*);
RakPeer_RPC fpRPC = NULL;
ReadMemory fpHkRead = NULL;
ClientCheck fpHkClientCheck = NULL;Начнем с обхода захода с R2/R3/R4.
(Перейти к SAMP R1 можете тыкнув сюда)
Возможные варианты подмены чтения gta_sa.exe - хук на месте вызова, либо хук ReadMemory и проверка адреса возврата.
Тут я покажу оба.
В случае с хуком вызова будем хукать это место:
			
				Код:
			
		
		
		.text:10011A3E 140 50                                push    eax
.text:10011A3F 144 E8 FC CC FF FF                    call    readMemory
			
				Вариант с naked хуком::
			
		
		
		// Объявляем функцию с параметром naked и укажем что она ничего не принимает и не возращает
__declspec(naked) void HK_ReadMemory(void) {
    static unsigned int address = 0; // Адрес откуда собирается читать самп
    __asm {
        pushad // Кидаем все регистры на стек, чтобы не затереть случайно лишнего
        mov address, eax // Вытаскиваем адрес с eax регистра
    }
    address += reinterpret_cast<DWORD>(ClearGTAModule) - dwGTAModule; // Меняем адрес на наш.
    static DWORD dwTmp = dwSampModule + READMEMFUNC;
    static DWORD retjmp = dwSampModule + HOOKEXITREADMEM;
    __asm {
        popad // тащим все регистры со стека обратно
        mov eax, address // перезаписываем регистр eax
        push eax // пушим его перед вызовом ReadMemory
        call dwTmp // Вызываем функцию ReadMemory
        jmp retjmp // Прыжок дальше
    }
}А в случае с хуком ReadMemory это будет выглядеть так:
			
				C++:
			
		
		
		unsigned char __cdecl HOOK_ReadMemory(unsigned int address, unsigned short readSize) {
    auto ret = reinterpret_cast<DWORD>(_ReturnAddress()); // Получаем адрес куда вернется функция после своего возврата
    if (ret >= 0x400000 && ret <= 0x856000 + 256) { // Если чтение будет производиться в модуле gta_sa.exe
        address += reinterpret_cast<DWORD>(ClearGTAModule) - dwGTAModule;
    }
    else { // Чтение идет в модуле samp.dll
        address += reinterpret_cast<DWORD>(ClearSAMPModule) - dwSAMPModule;
    }
    return fpHkReadMem(address, readSize);
}Перейдем к обходу для R1. Переход к точке входа
Здесь у нас принцип такой: подменять версию сампа при отправке RPC и на клиентчеки отсылать наш ответ.
Сначала добавьте samp.dll от любой версии которая вам нравится в ресурсы с типом DLL
также не забудьте заменить FAKEVER на версию, dll от которой вы берете
Объявим функцию хука отправки RPC для подмены версии сампа
			
				C++:
			
		
		
		bool __fastcall HOOK_RakPeer_RPC(void* dis, void* EDX, int* uniqueID, BitStream* parameters, int a4, int a5, int a6, int a7, __int16 a8, int a9, int a10, int a11, int a12, int a13) {
    if (*uniqueID == 25) {
        INT32 iVersion; UINT8 byteMod; UINT8 byteNicknameLen;
        UINT32 uiClientChallengeResponse;
        UINT8 byteAuthKeyLen;
        parameters->Read(iVersion);
        parameters->Read(byteMod);
        parameters->Read(byteNicknameLen);
        char* nickname = new char[byteNicknameLen + 1];
        nickname[byteNicknameLen] = 0;
        parameters->Read(nickname, byteNicknameLen);
        parameters->Read(uiClientChallengeResponse);
        parameters->Read(byteAuthKeyLen);
        char* authKey = new char[byteAuthKeyLen + 1];
        authKey[byteAuthKeyLen] = 0;
        parameters->Read(authKey, byteAuthKeyLen);
        parameters->SetWriteOffset(parameters->GetReadOffset());
        parameters->Write(static_cast<UINT8>(strlen(FAKEVER)));
        parameters->Write(FAKEVER, strlen(FAKEVER));
        delete[] authKey, nickname;
    }
    return fpRPC(dis, EDX, uniqueID, parameters, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13);
}Объявим функцию получения чексуммы с куска памяти(Скопировал псевдокод с ida pro):
			
				C++:
			
		
		
		unsigned char readMemory(int address, unsigned __int16 readSize)
{
    unsigned char result = 0;
    int i = 0;
    if (readSize)
    {
        do
            result ^= *(BYTE*)(i++ + address) & 0xCC;
        while (i != readSize);
    }
    return result;
}Функция хука:
			
				C++:
			
		
		
		void HandleRPCPacketFunc(RPCParameters* rpcParams) {
    BitStream bs(rpcParams->input, rpcParams->numberOfBitsOfData / 8, false);
    #pragma pack(push, 1)
    struct CCheck { // Для удобной работы с входящими параметрами
        unsigned __int8 requestType;
        unsigned __int32 arg;
        unsigned __int16 offset, readSize;
    };
    #pragma pack(pop)
    CCheck* data = reinterpret_cast<CCheck*>(rpcParams->input); // Определяем структуру
    // Мы отправляем ответ только на те типы, которые R1 не обрабатывает. Остальное отдаем ему.
    if (data->requestType != 0x45 && data->requestType != 5) fpHkClientCheck(rpcParams);
    if (data->readSize > 256u || data->readSize < 2u || data->offset > 256u) return; // Делаем те же проверки что и самп
    unsigned __int8 result = 0; // что будем отправлять серверу
    switch (data->requestType) {
    case 0x5:
        if (data->arg >= 0x400000 && data->arg <= 0x856E00) { // Делаем проверку на рамки как самп.
            result = readMemory(data->arg + data->offset - dwGTAModule + reinterpret_cast<DWORD>(ClearGTAModule), data->readSize);
        }
        break;
    case 0x45:
    {
        if (data->arg <= 0xC3500) { // Проверяем то что может читать.
            result = readMemory(data->arg + data->offset + reinterpret_cast<DWORD>(ClearSAMPModule), data->readSize);
        }
    }
        break;
    }
    // Создадим битстрим для отправки
    BitStream sendBS;
    // Записываем нужные данные.
    sendBS.Write(data->requestType);
    sendBS.Write(data->arg);
    sendBS.Write(result);
    int sendID = RPC_ClientCheck;
    #pragma pack(push, 1)
    struct CNetGameR1 { // Опять же мне так удобнее.
        char                junk[0x3C9];
        RakClientInterface* m_pRakClient;
    };
    #pragma pack(pop)
    RakClientInterface* pRak = (*reinterpret_cast<CNetGameR1**>(dwSampModule + 0x21A0F8))->m_pRakClient;
    // Отправляем результат чтения.
    pRak->RPC(&sendID, &sendBS, PacketPriority::HIGH_PRIORITY, PacketReliability::RELIABLE_ORDERED, 0u, false);
    return;
}Ну и перейдем к точке входа:
Инициализируем MinHook и получаем все нужные адреса:
			
				C++:
			
		
		
		DWORD oldProt;
MH_Initialize();
MODULEINFO SAMPmoduleInfo;
hSAMPModule = GetModuleHandle(L"samp.dll");
hGTAModule = GetModuleHandle(L"gta_sa.exe");
dwSampModule = reinterpret_cast<DWORD>(hSAMPModule);
dwGTAModule = reinterpret_cast<DWORD>(hGTAModule);
if (hSAMPModule == NULL || hGTAModule == NULL) return FALSE; // Вдруг самп не загруженОпределяем версию сампа:
			
				C++:
			
		
		
		GetModuleInformation(GetCurrentProcess(), hSAMPModule, &SAMPmoduleInfo, sizeof(SAMPmoduleInfo)); // Получаем инфу о модуле
switch (reinterpret_cast<DWORD>(SAMPmoduleInfo.EntryPoint) - dwSampModule) {
    case 0x31DF13:    sampVer = SampVersion::SAMP_0_3_7_R1; break;
    case 0xCC4D0:    sampVer = SampVersion::SAMP_0_3_7_R3_1; break;
    case 0xCBCB0:    sampVer = SampVersion::SAMP_0_3_7_R4; break;
    default:        return FALSE; // Если версия неизвестная.
}
			
				C++:
			
		
		
		if (sampVer == SampVersion::SAMP_0_3_7_R1) {
    ClearSAMPModule = reinterpret_cast<unsigned char*>(LockResource(LoadResource(hModule, FindResourceW(hModule, MAKEINTRESOURCEW(IDR_DLL_FILE1), L"DLL_FILE")))); // Получаем DLL сампа из ресурсов
    MH_CreateAndEnableHook(dwSampModule + 0xEAF0, &HandleRPCPacketFunc, reinterpret_cast<LPVOID*>(&fpHkClientCheck)); // Ставим хук на обработчик RPC ClientCheck
    MH_CreateAndEnableHook(dwSampModule + 0x36C30, &HOOK_RakPeer_RPC, reinterpret_cast<LPVOID*>(&fpRPC)); // Ставим хук на отправку рпц
}
else {
    switch (sampVer) {
        case SampVersion::SAMP_0_3_7_R3_1:
            {
                HOOKREADMEM = 0x11A3F;
                HOOKEXITREADMEM = 0x11A44;
                READMEMFUNC = 0xE740;
                SAMPHMODULE = 0x26E880;
            }
            break;
        case SampVersion::SAMP_0_3_7_R4:
            {
                HOOKREADMEM = 0x11D6F;
                HOOKEXITREADMEM = 0x11D74;
                READMEMFUNC = 0xEA50;
                SAMPHMODULE = 0x26E9B0;
            }
            break;
    }
    ClearSAMPModule = new unsigned char[SAMPmoduleInfo.SizeOfImage]; // Выделяем массив
    // Копируем модуль samp.dll
    memcpy(ClearSAMPModule, reinterpret_cast<void*>(dwSampModule), 0xC3500 + 256); // пределы чеков сампа - C3500
    VirtualProtect(reinterpret_cast<void*>(dwSampModule + HOOKREADMEM - 1), 6, PAGE_EXECUTE_READWRITE, &oldProt);
    *reinterpret_cast<unsigned char*>(dwSampModule + HOOKREADMEM - 1) = 0x90; // Нопим байт чтобы листинг не сбивался
    //MH_CreateAndEnableHook(dwSampModule + HOOKREADMEM, &HK_ReadMemory, NULL); // Если у вас naked хук - расскоммментировать!
    // Строку ниже закомментировать, если у вас naked хук
    MH_CreateAndEnableHook(dwSampModule + 0xEA50, &sub_1000EA50, reinterpret_cast<LPVOID*>(&fpHkRead));
    VirtualProtect(reinterpret_cast<void*>(dwSampModule + HOOKREADMEM - 1), 6, oldProt, &oldProt);
 
    // Раскомментировать если у вас naked хук
    /*
    VirtualProtect(reinterpret_cast<void*>(dwSampModule + SAMPHMODULE), 4, PAGE_EXECUTE_READWRITE, &oldProt);
    *reinterpret_cast<HMODULE*>(dwSampModule + SAMPHMODULE) = reinterpret_cast<HMODULE>(ClearSAMPModule);
    VirtualProtect(reinterpret_cast<void*>(dwSampModule + SAMPHMODULE), 4, oldProt, &oldProt);
    */
}Ну и конце копируем модуль гташки:
			
				C++:
			
		
		
		MODULEINFO GTAmoduleInfo;
GetModuleInformation(GetCurrentProcess(), hGTAModule, >AmoduleInfo, sizeof(GTAmoduleInfo));
// копируем только то, что может прочитать сервер.
ClearGTAModule = new unsigned char[0x856000 + 256 - 0x400000];
memcpy(ClearGTAModule, reinterpret_cast<void*>(dwGTAModule), 0x856000 + 256 - 0x400000);На этом наш обход завершен.
Плюсы этого обхода в том, что он скроет любое говно что вы поставите на свою гта: будь то клео, сампфункс, сайлент аим и любые другие читы.
Также если сервер попытается прочитать наш хук, то ничего он не узнает, ведь мы перекидываем его чтение на чистый модуль
Пасиба @HellsCoder за идею обхода, @DarkP1xel за моральную поддержку, а также Lot. за пинки :D
			
				Последнее редактирование: 
				
		
	
										
										
											
	
										
									
								 
				
		



 
 
		 
 
		 
 
		 
 
		 
					
				 
						
					 
 
		
 
 
		 
 
		 
 
		 
 
		 
 
		