Добрый день!
Есть игра, в которой я написал бота (твиновода). Он работает хорошо, за исключением вылетов.
Вылеты не привязаны к действиям персонажей. Бот может проработать 5 минут, а может несколько часов. В отладчике я поймал момент вылета, там по стеку вызовов вообще не моя функция и не мой id потока. Судя по всему вылетает сама игра из за внедрения кода. Но на античит я не думаю, потому что я создавал свой сервер с чистой версией клиента - тоже самое.
Сейчас мой код устроен так: я выделяю память в процессе, записываю туда мой код, запускаю поток и жду его завершения.
Для завершения потока я пробовал использовать сначала RET, затем нашел функцию ExitThread в процессе. Разницы никакой - и так и так вылетает одинаково.
Еще заметил нюанс, что на более слабом ПК вылеты происходят чаще.
У меня вот предположение, что может быть проблема решится, если я не буду создавать свой поток, а буду использовать игровой. То есть, использовать JMP на адрес моего кода, затем возвращаться обратно. Только я не понимаю как это реализовать. Какой поток надо найти чтобы так сработало? Я не понимаю, помогите!
Вот код C++:
bool ProcessReader::sendPacket(size_t size, void* packetData) {
if (GameSession == 0) {
std::cout << u8"GameSession не установлен.\n";
return false;
}
DWORD processId = pid;
if (processId == 0) {
std::cout << u8"Не удалось найти процесс ElementClient.exe." << std::endl;
return false;
}
LPVOID remotePacketData = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!remotePacketData) {
std::cout << u8"Не удалось выделить память в удалённом процессе." << std::endl;
return false;
}
SIZE_T bytesWritten;
if (!WriteProcessMemory(hProcess, remotePacketData, packetData, size, &bytesWritten) || bytesWritten != size) {
std::cout << u8"Не удалось записать данные пакета в удалённый процесс." << std::endl;
VirtualFreeEx(hProcess, remotePacketData, 0, MEM_RELEASE);
return false;
}
DWORD sendPacketAddress = baseAddress + SENDPACKET;
DWORD remoteKernel32Base = GetRemoteModuleBaseAddress(processId, L"ntdll.dll");
if (remoteKernel32Base == 0) {
VirtualFreeEx(hProcess, remotePacketData, 0, MEM_RELEASE);
return false;
}
DWORD remoteExitThreadAddr = remoteKernel32Base + RTLEXITUSERTHREAD;
if (remoteExitThreadAddr == 0) {
std::cout << u8"Не удалось вычислить адрес ExitThread в удалённом процессе." << std::endl;
VirtualFreeEx(hProcess, remotePacketData, 0, MEM_RELEASE);
return false;
}
// Код-стаб для вызова функции игровой, который я записываю в память.
BYTE codeStub[] = {
0xB9, 0, 0, 0, 0, // mov ecx, GameSession ; ECX = GameSession
0x68, 0, 0, 0, 0, // push size ; Параметр size
0x68, 0, 0, 0, 0, // push remotePacketData ; Параметр packetData
0xB8, 0, 0, 0, 0, // mov eax, SENDPACKET_ADDRESS
0xFF, 0xD0, // call eax
0x83, 0xC4, 0x08, // add esp, 8 ; Очистка стека
0x6A, 0x00, // push 0
0xB8, 0, 0, 0, 0, // mov eax, ExitThread_ADDRESS
0xFF, 0xD0 // call eax
};
// Заполняю параметры в код-стабе
*(DWORD*)(codeStub + 1) = (DWORD)GameSession; // ECX = GameSession
*(DWORD*)(codeStub + 6) = (DWORD)size; // Параметр size
*(DWORD*)(codeStub + 11) = (DWORD)remotePacketData; // Параметр packetData
*(DWORD*)(codeStub + 16) = sendPacketAddress; // Адрес функции SENDPACKET
*(DWORD*)(codeStub + 28) = remoteExitThreadAddr; // Адрес функции ExitThread
LPVOID remoteCode = VirtualAllocEx(hProcess, NULL, sizeof(codeStub), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!remoteCode) {
std::cout << u8"Не удалось выделить память для код-стаба в удалённом процессе." << std::endl;
VirtualFreeEx(hProcess, remotePacketData, 0, MEM_RELEASE);
return false;
}
if (!WriteProcessMemory(hProcess, remoteCode, codeStub, sizeof(codeStub), &bytesWritten) || bytesWritten != sizeof(codeStub)) {
std::cout << u8"Не удалось записать код-стаб в удалённый процесс." << std::endl;
VirtualFreeEx(hProcess, remotePacketData, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, remoteCode, 0, MEM_RELEASE);
return false;
}
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteCode, NULL, 0, NULL);
if (!hThread) {
std::cout << u8"Не удалось создать удалённый поток." << std::endl;
VirtualFreeEx(hProcess, remotePacketData, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, remoteCode, 0, MEM_RELEASE);
return false;
}
WaitForSingleObject(hThread, INFINITE);
VirtualFreeEx(hProcess, remotePacketData, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, remoteCode, 0, MEM_RELEASE);
CloseHandle(hThread);
return true;
}