Гость Mephisto Опубликовано 1 ноября, 2016 Поделиться Опубликовано 1 ноября, 2016 Приветствую. У меня к вам такой вопрос: предположим, есть некая инструкция в игре, к примеру, mov [rax+18], rbx и в регистре rax хранится значение, которое мне необходимо получить в своей программе, каким путём это можно сделать? Пытался найти примеры, но во всех используются ассемблерные вставки, которые х64 проект vc++ не поддерживает. Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 2 ноября, 2016 Поделиться Опубликовано 2 ноября, 2016 Предположим, что в этой же игре ты делаешь Code Cave, в котором, предположим, получаешь нужный тебе регистр в нужный момент времени. И даже предположим, что в этом CodeCave ты можешь сохранить содержимое нужного регистра в выделенной памяти, которую затем прочитать из C++. Более подробно рассказать не могу, так как задача обрисована весьма и весьма неясно. Ссылка на комментарий Поделиться на другие сайты Поделиться
ШАРИК Опубликовано 2 ноября, 2016 Поделиться Опубликовано 2 ноября, 2016 setbreakpoint делаешь ? ^.^ Ссылка на комментарий Поделиться на другие сайты Поделиться
partoftheworlD Опубликовано 2 ноября, 2016 Поделиться Опубликовано 2 ноября, 2016 (изменено) В x64 ассемблерных вставок нет, но можно прикрутить файл(ml64 /c твойасмфайл после компиляции передать *.obj в линкер) с процедурой которая будет получать нужный тебе регистр. А потом использовать extern "C" __int64 имяпроцедуры();//(Имя процедуры из асмфайла) или Встроенные объекты компилятора и __getReg Изменено 2 ноября, 2016 пользователем partoftheworlD 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
elvis66666 Опубликовано 3 ноября, 2016 Поделиться Опубликовано 3 ноября, 2016 мммм, интересно, т.е. я могу написать так: Скрытый текст in_obj file: void PlayerStruct(int64 &Player) { _asm{ ... mov Player,eax ... } } in_64x Project: extern "C" _void PlayerStruct(int64); int64 Player; PlayerStruct(Player); К сожалению пока нету под рукой VS проверить) винда ставится, пишу с планшета. Ссылка на комментарий Поделиться на другие сайты Поделиться
gmz Опубликовано 3 ноября, 2016 Поделиться Опубликовано 3 ноября, 2016 на mingw64 -masm=intel DWORD_PTR temp; asm ( "rdtsc\n" "mov %0,rax" :"=m"(temp) ); wprintf(L"%d\n",temp); Ссылка на комментарий Поделиться на другие сайты Поделиться
elvis66666 Опубликовано 4 ноября, 2016 Поделиться Опубликовано 4 ноября, 2016 да, я тоже компилятор от интела использую под VSx64. Просто интересный вариант выше. Ссылка на комментарий Поделиться на другие сайты Поделиться
gmz Опубликовано 4 ноября, 2016 Поделиться Опубликовано 4 ноября, 2016 44 минуты назад, elvis66666 сказал: да, я тоже компилятор от интела использую под VSx64. Просто интересный вариант выше. нее интел не годится. слишком большой % мусора. а тот вариант выше, там ведь надо кодить в .asm а потом сгенерить .obj ? Ссылка на комментарий Поделиться на другие сайты Поделиться
elvis66666 Опубликовано 4 ноября, 2016 Поделиться Опубликовано 4 ноября, 2016 2 часа назад, gmz сказал: нее интел не годится. слишком большой % мусора. а тот вариант выше, там ведь надо кодить в .asm а потом сгенерить .obj ? да) я особо не заметил мусора от интела, да и проект большим уж точно не будет, чтобы заметить хоть какие-то просадки. За-то сидишь и пишешь себе инлайн асмом в VS и не надо ничего руками прописывать, преобразовывать =) Ссылка на комментарий Поделиться на другие сайты Поделиться
Гость Mephisto Опубликовано 8 ноября, 2016 Поделиться Опубликовано 8 ноября, 2016 Угу, с этим разобрался - как подключить и работать с отдельным асм файлом в студии (кстати, сделал это сразу в ней, т.е. просто создал асм файл и указал в его свойствах, что этот файл типа "масм", вручную компилировать и подключать в линкере не потребовалось). Но основной вопрос всё ещё в силе: как получить данные из регистра при срабатывании определённой инструкции. Можете по шагам описать, что для этого необходимо? Или лучше, возможно у кого завалялся где-нибудь листинг, в котором выполняется такая задача, чтобы по нему можно было разобраться. Ссылка на комментарий Поделиться на другие сайты Поделиться
partoftheworlD Опубликовано 8 ноября, 2016 Поделиться Опубликовано 8 ноября, 2016 (изменено) 2 часа назад, Mephisto сказал: как получить данные из регистра при срабатывании определённой инструкции Я думаю в твоем случае как и в Mid Hook function, тебе нужен трамплин который будет делать прыжок на твою процедуру(asm которая будет передавать в буфер адрес регистра, типа mov Buffer, rcx) и возвращать обратно в код игры. Пример могу показать, только он для x32, но думаю логика будет понятна. Вместо вставки будет процедура только. Скрытый текст __declspec(naked) void EndlessAmmo() { __asm { mov AmmoRegister , esi jmp [AmmoJMPBack] } } void InitializeHooks() { DWORD exampleAmmo = FindPattern("server.dll","\xE8\x00\x00\x00\x00\x33\xD2\x66\x89\x50\x02\x89\xBE\x00\x00\x00\x00\x8B\x06\x8B\x90\x00\x00\x00\x00\x8B\xBE\x00\x00\x00\x00","x????xxxxxxxx????xxxx????xx????"); exampleAmmo += 11; //Смещение от сигнатуры на 11 байт для выхода на нужную инструкцию AmmoJMPBack = exampleAmmo + 0x6; // количество байт заменяемых инструкций PlaceJMP((BYTE*)exampleAmmo, (DWORD)EndlesseAmmo, 0x6); // трамплин } DWORD WINAPI OverwriteValues() { for (;;Sleep(150)) { if (GetAsyncKeyState(VK_MENU)) { DWORD idkptr = AmmoRegister + 0x1414; *(int*)idkptr += 1; } } return 0; } BOOL WINAPI DllMain( HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: InitializeHooks(); CreateThread(0,0,(LPTHREAD_START_ROUTINE)OverwriteValues,0,0,0); break; } return true; } Изменено 8 ноября, 2016 пользователем partoftheworlD Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 9 ноября, 2016 Поделиться Опубликовано 9 ноября, 2016 Я вообще не понимаю, вокруг чего сыр-бор? Я ж в первом же ответе написал алгоритм получения значения регистра из игры. Все предложеннные варианты позволяют прочитать регистр из СВОЕГО приложения (просматривал по диагонали), тогда как топикстартер говорил о стороннем приложении. Ссылка на комментарий Поделиться на другие сайты Поделиться
Гость Mephisto Опубликовано 9 ноября, 2016 Поделиться Опубликовано 9 ноября, 2016 (изменено) Хм, кажется начинает доходить, спасибо. И вот ещё вопрос возник: я выделил память с помощью VirtualAllocEx, там прописываю код, затем мне необходимо вернуться назад к точке прыжка. Рассчитываю байты для прыжка по формуле "0 - ( Откуда - Куда) - размер моего кода". Но когда эти байты подставляю к прыжку, адрес получается неверным. Т.е. вместо, 13F83104B получается 3F83104B, единичка теряется. А если добавить флаг MEM_TOP_DOWN, то на выходе получится 8003F83104B, т.е. вместо единички будет 800. Похоже это из-за того, что память выделяется не там, где нужно. В свою очередь когда я рассчитываю байты для прыжка от игровой инструкции к выделенной памяти, то к нужному адресу наоборот прибавляется единичка: память выделяется по адресу 07960000, а игра прыгает к 107960000. Как это можно исправить? Полагаю, нужно как-то указать VirtualAllocEx в каком регионе выделять память. Изменено 9 ноября, 2016 пользователем Mephisto Ссылка на комментарий Поделиться на другие сайты Поделиться
Dino Опубликовано 9 ноября, 2016 Поделиться Опубликовано 9 ноября, 2016 19 час назад, Mephisto сказал: Можете по шагам описать, что для этого необходимо? Или лучше, возможно у кого завалялся где-нибудь листинг, в котором выполняется такая задача, чтобы по нему можно было разобраться. Для начала читаешь о межпроцессорном взаимодействие, затем когда определишься какой метод будешь использовать можешь писать хук. Задача хука - прочитать содержимое регистра и отправить его твоей программе. Задача твоей программы - получить и обработать посылку. Естественно все это должно происходить синхронно. Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 10 ноября, 2016 Поделиться Опубликовано 10 ноября, 2016 VirtualAllocEx всегда выделит память дальше, чем находится код, с которого прыгаешь. Поэтому для прыжка на выделенную память используешь Адрес кейва - Адрес оригинальной инструкции и при записи этого числа не забываешь байты перевернуть (в памяти они хранятся в перевернутом виде). Для прыжка из выделеннной памяти обратно считаешь (Адрес кейва + Размер кейва до инструкции прыжка обратно) - (Адрес оригинальной инструкции + 5 байт). Ссылка на комментарий Поделиться на другие сайты Поделиться
Гость Mephisto Опубликовано 10 ноября, 2016 Поделиться Опубликовано 10 ноября, 2016 Спасибо) Удалось, наконец, сделать что хотел - и заменил часть кода своим, и регистры прочитал в выделенную память. Правда по этой формуле " (Адрес кейва + Размер кейва до инструкции прыжка обратно) - (Адрес оригинальной инструкции + 5 байт) " всё равно выходит абракадабра (и после переворота байт тоже). Тут ведь дело в чем - по формуле, которую использовал изначально, получалось верно число, просто оно 8-байтовое, тогда как jmp после себя забирает лишь 4 байта для определения адреса и потому единичку теряет, ведь она не умещается в эти 4 байта. Видимо приставка добавляется в зависимости от точки прыжка. Я так посмотрел, когда СЕ выделяет память под скрипты, он выделяет её близ самой игры, а не отсылает так далеко, как VirtualAllocEx. Чтобы эта функция выделяла память рядом с игрой, написал вот такое: Скрытый текст UINT64 Trainer::Alloc(int size) { MEMORY_BASIC_INFORMATION mbi; UINT64 FreeMem = 0; UINT64 offset = 0; while (true) { VirtualQueryEx(pHandle, (LPCVOID)(AddressBase - offset), &mbi, sizeof(MEMORY_BASIC_INFORMATION)); if (mbi.State == MEM_FREE && mbi.RegionSize >= size) { FreeMem = AddressBase - offset; break; } offset += mbi.RegionSize; } return (UINT64)VirtualAllocEx(pHandle, (LPVOID)FreeMem, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); } Теперь, как мне и нужно, к началу адреса добавляется единичка и адрес для прыжка получается верным. Ссылка на комментарий Поделиться на другие сайты Поделиться
gmz Опубликовано 10 ноября, 2016 Поделиться Опубликовано 10 ноября, 2016 В 09.11.2016в05:31, Xipho сказал: тогда как топикстартер говорил о стороннем приложении. предполагалось что автор не страдает херней и кодит длл В 09.11.2016в12:37, Mephisto сказал: В свою очередь когда я рассчитываю байты для прыжка от игровой инструкции к выделенной памяти, то к нужному адресу наоборот прибавляется единичка: память выделяется по адресу 07960000, а игра прыгает к 107960000. в х64 над другой метод. например я использую: 00007FF7755E005B | FF 15 00 00 00 00 | call qword [00007FF7755E0061] 00007FF7755E0061 | D1 15 00 00 01 00 00 00 <- туда пишу адрес а в самой хук функции добавляю add qword[rsp],8 или можно jmp после call поставить на +8 5 часов назад, Xipho сказал: VirtualAllocEx всегда выделит память дальше, чем находится код, с которого прыгаешь. Поэтому для прыжка на выделенную память используешь Адрес кейва - Адрес оригинальной инструкции и при записи этого числа не забываешь байты перевернуть (в памяти они хранятся в перевернутом виде). Для прыжка из выделеннной памяти обратно считаешь (Адрес кейва + Размер кейва до инструкции прыжка обратно) - (Адрес оригинальной инструкции + 5 байт) дк х64 Ссылка на комментарий Поделиться на другие сайты Поделиться
Dino Опубликовано 10 ноября, 2016 Поделиться Опубликовано 10 ноября, 2016 код хука нужно писать в самой dll, чтобы не было проблем с релоками и импортами, так что предлагаю отказаться от VirtualAllocEx Ссылка на комментарий Поделиться на другие сайты Поделиться
Dino Опубликовано 10 ноября, 2016 Поделиться Опубликовано 10 ноября, 2016 (изменено) Вот на примере игры сапер Win7 x64, при нажатии на кнопку "О программе" она будет отправлять через пайп содержимое регистра RAX в твою программу Main.cpp (код твоей программы) Скрытый текст void Pipe() { LPCSTR lpszPipeName = "\\\\.\\pipe\\GetReg"; BOOL fConnected; HANDLE hNamedPipe; DWORD cbRead = 0; DWORD64 reg; TCHAR strout[MAX_PATH]; DWORD size = sizeof(DWORD64); while (1) { hNamedPipe = CreateNamedPipe( lpszPipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, size, size, NMPWAIT_USE_DEFAULT_WAIT, NULL); if (hNamedPipe != INVALID_HANDLE_VALUE) { fConnected = ConnectNamedPipe(hNamedPipe, NULL); if (fConnected) { while (1) { if (ReadFile(hNamedPipe, ®, size, &cbRead, NULL)) { wsprintf(strout, "RAX = %p", reg); MessageBox(0, strout, 0, 0); break; } Sleep(100); } DisconnectNamedPipe(hNamedPipe); } CloseHandle(hNamedPipe); } } } int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Pipe, 0, 0, 0); //... тут твои действия } Dllmain.cpp(код твоей Dll, которая должна быть загружена в игру) Скрытый текст #include "stdafx.h" #include <stdlib.h> typedef PVOID(*GetContext)(); extern "C" void HookedGetReg(); extern "C" PVOID Pipe(DWORD64 reg); GetContext tGetReg = nullptr, mGetReg = nullptr; PVOID Pipe(DWORD64 reg) { LPCSTR lpszPipeName = "\\\\.\\pipe\\GetReg"; HANDLE hNamedPipe; DWORD cbWrite; DWORD size; if (WaitNamedPipe(lpszPipeName, NMPWAIT_USE_DEFAULT_WAIT)) { hNamedPipe = CreateFile( lpszPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hNamedPipe != INVALID_HANDLE_VALUE) { size = sizeof(DWORD64); while (1) { if (WriteFile(hNamedPipe, ®, size, &cbWrite, NULL)) break; Sleep(100); } CloseHandle(hNamedPipe); } } return tGetReg(); } void SetHook() { BYTE opcodejmp[15] = "\xFF\x25\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; DWORD dwold, len; len = 15; // длина инструкций, оригинальные байты которые заменяем прыжком на наш код (не менее 14) mGetReg = (GetContext)((PBYTE)GetModuleHandleA("minesweeper.exe") + 0x351E0); // Инструкция которую хукаем *(DWORD64*)&opcodejmp[6] = (DWORD64)((PBYTE)mGetReg + len); tGetReg = (GetContext)malloc(14 + len);// выделяем память для трамплина VirtualProtect(tGetReg, 14 + len, PAGE_EXECUTE_READWRITE, &dwold); memcpy(tGetReg, mGetReg, len); memcpy((LPVOID)((PBYTE)tGetReg + len), opcodejmp, 14); *(DWORD64*)&opcodejmp[6] = (DWORD64)&HookedGetReg; VirtualProtect(mGetReg, 14, PAGE_EXECUTE_READWRITE, &dwold); memcpy(mGetReg, (LPCVOID)opcodejmp, 14); VirtualProtect(mGetReg, 14, dwold, &dwold); } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: SetHook(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } Asm файл(так же должен быть включен в проект с Dll !) Скрытый текст EXTRN Pipe : proc .CODE HookedGetReg PROC push rax push rcx push rdx push rdi push r8 push r9 sub rsp, 28h mov RCX,RAX call Pipe add rsp, 28h pop r9 pop r8 pop rdi pop rdx pop rcx pop rax ret HookedGetReg ENDP END Код для загрузки Dll в стороннее приложение можешь найти где угодно, поэтому не вижу смысла его здесь приводить. Изменено 10 ноября, 2016 пользователем Xipho Куски кода принято оборачивать спойлером, дабы не создавать "простыней". Ссылка на комментарий Поделиться на другие сайты Поделиться
gmz Опубликовано 10 ноября, 2016 Поделиться Опубликовано 10 ноября, 2016 2 часа назад, Dino сказал: Asm файл(так же должен быть включен в проект с Dll !) надо сохр RAX RCX RDX R8 R9 R10 R11 если он поставит хук, а там они активно юзаются - call Pipe напихает мусора и будет эпик крешш Ссылка на комментарий Поделиться на другие сайты Поделиться
Dino Опубликовано 10 ноября, 2016 Поделиться Опубликовано 10 ноября, 2016 (изменено) 1 час назад, gmz сказал: надо сохр RAX RCX RDX R8 R9 R10 R11 если он поставит хук, а там они активно юзаются - call Pipe напихает мусора и будет эпик крешш ага ты прав.. А все из за того что я трамплин не из того места вызываю Вот исправленный вариант: Dllmain.cpp(код твоей Dll, которая должна быть загружена в игру) Скрытый текст #include <windows.h> typedef PVOID(*GetContext)(); extern "C" void HookedGetReg(); extern "C" void Pipe(DWORD64 reg); extern "C" DWORD64 tGetReg[10] = { 0x00000000 }; //выделим побольше памяти для трамплина GetContext mGetReg = nullptr; void Pipe(DWORD64 reg) { LPCSTR lpszPipeName = "\\\\.\\pipe\\GetReg"; HANDLE hNamedPipe; DWORD cbWrite = 0; DWORD size; if (WaitNamedPipe(lpszPipeName, NMPWAIT_USE_DEFAULT_WAIT)) { hNamedPipe = CreateFile( lpszPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hNamedPipe != INVALID_HANDLE_VALUE) { size = sizeof(DWORD64); while (1) { if (WriteFile(hNamedPipe, ®, size, &cbWrite, NULL)) break; Sleep(100); } CloseHandle(hNamedPipe); } } } void SetHook() { BYTE opcodejmp[15] = "\xFF\x25\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; DWORD dwold, len; len = 15; // длина инструкций, оригинальные байты которые заменяем прыжком на наш код (не менее 14) mGetReg = (GetContext)((PBYTE)GetModuleHandleA("minesweeper.exe") + 0x351E0); // Инструкция которую хукаем *(DWORD64*)&opcodejmp[6] = (DWORD64)((PBYTE)mGetReg + len); VirtualProtect((LPVOID)(tGetReg), 14 + len, PAGE_EXECUTE_READWRITE, &dwold); memcpy((LPVOID)tGetReg, mGetReg, len); memcpy((LPVOID)((PBYTE)tGetReg + len), opcodejmp, 14); *(DWORD64*)&opcodejmp[6] = (DWORD64)&HookedGetReg; VirtualProtect(mGetReg, 14, PAGE_EXECUTE_READWRITE, &dwold); memcpy(mGetReg, (LPCVOID)opcodejmp, 14); VirtualProtect(mGetReg, 14, dwold, &dwold); } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CreateThread(0,0,(LPTHREAD_START_ROUTINE)SetHook,0,0,0); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } Asm файл(так же должен быть включен в проект с Dll !) Скрытый текст EXTRN Pipe : proc EXTRN tGetReg : proc .CODE HookedGetReg PROC push rax push rbx push rcx push rdx push rdi push rsi push r8 push r9 push r10 push r11 push r12 sub rsp, 30h mov RCX,RAX call Pipe ; Важно! Когда функция сделает что-то вроде <sub rsp, 48h> верхушка стека должна быть кратна 16, в противном случае краш! add rsp, 30h pop r12 pop r11 pop r10 pop r9 pop r8 pop rsi pop rdi pop rdx pop rcx pop rbx pop rax jmp tGetReg ; выполняем оригинальный код HookedGetReg ENDP END Изменено 10 ноября, 2016 пользователем Dino 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
gmz Опубликовано 10 ноября, 2016 Поделиться Опубликовано 10 ноября, 2016 51 минуту назад, Dino сказал: Asm файл(так же должен быть включен в проект с Dll !) что то их слишком много, можно обойтись списком выше, но даже так ты rbp забыл Ссылка на комментарий Поделиться на другие сайты Поделиться
Dino Опубликовано 10 ноября, 2016 Поделиться Опубликовано 10 ноября, 2016 16 минуту назад, gmz сказал: что то их слишком много, можно обойтись списком выше, но даже так ты rbp забыл да кому он нужен хахах Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения