-
Постов
28 -
Зарегистрирован
-
Посещение
Тип контента
Профили
Форумы
Загрузки
Блоги
Сообщения, опубликованные Abraham08
-
-
11 час назад, partoftheworlD сказал:
Прочитай про недокументированные функции.(NtWow64QueryInformationProcess64
NtWow64ReadVirtualMemory64) Инфы по ним мало. Для чтения из игры x64 можно использовать NtWow64ReadVirtualMemory64 в 32 битном трейнере и окошко будет работать, придется функции руками импортировать из ntdll. И вот статейку глянь.Вот спасибо, знать бы о них раньше)
И, к слову, таки удалось запустить окно х64 проекта в в10, воспользовался этой шапкой:
Скрытый текстHINSTANCE hInstance2; LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); HWND hWnd; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { hInstance2 = hInstance; WNDCLASSEX wc; ZeroMemory(&wc, sizeof(WNDCLASSEX)); wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor(0, IDC_ARROW); wc.lpszClassName = "WindowClass"; RegisterClassEx(&wc); hWnd = CreateWindow("WindowClass", "DS2RANK", WS_SYSMENU | WS_MINIMIZEBOX, (GetSystemMetrics(SM_CXSCREEN) / 2) - 300, (GetSystemMetrics(SM_CYSCREEN) / 2) - 300, 600, 600, 0, 0, hInstance, 0); ShowWindow(hWnd, nCmdShow); MSG msg; while (1) { while (PeekMessage(&msg, 0, 0, 0, 1)) { TranslateMessage(&msg); DispatchMessage(&msg); } if (msg.message == WM_QUIT) break; } return (int)msg.wParam; }
В итоге так и не понял почему мой вариант не работал. Вроде и тут и там одно и то же - создается, заполняется и регистриуется класс окна, создается окно с учетом разрешения, запускается цикл сообщений. Но мой вариант на х64 в10 не работал, а этот работает. Мистика.
-
Вот полный код программы:
Скрытый текст//Main.cpp #include <Windows.h> #include <string> #include "resource.h" #include <TlHelp32.h> #include <fstream> #include "MemReader.h" using namespace std; HWND hwnd; HINSTANCE hInstance2; DWORD64 var; fstream Logs; LRESULT CALLBACK DS2ZERO(HWND, UINT, UINT, LONG); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int nCmdShow){ MSG Msg; WNDCLASSEX WndClass; char name[] = "ClassName"; hInstance2 = hInstance; WndClass.style = CS_HREDRAW | CS_VREDRAW; WndClass.lpfnWndProc = (WNDPROC)DS2ZERO; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = hInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); WndClass.lpszMenuName = NULL; WndClass.lpszClassName = name; WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.hIconSm = NULL; if (!RegisterClassEx(&WndClass)){ MessageBox(NULL, "Cant register class", "Error", MB_OK); exit(1); } HDC hDCScreen = GetDC(NULL); int Horres = GetDeviceCaps(hDCScreen, HORZRES); int Vertres = GetDeviceCaps(hDCScreen, VERTRES); ReleaseDC(NULL, hDCScreen); hwnd = CreateWindow(name, "DS2RANK", WS_OVERLAPPED | WS_SYSMENU, (Horres / 2) - 300, (Vertres / 2) - 300, 600, 600, NULL, NULL, hInstance, NULL); if (!hwnd){ MessageBox(NULL, to_string(GetLastError()).c_str(), "Error", MB_OK);// exit(1); } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&Msg, NULL, 0, 0)){ TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } LRESULT CALLBACK DS2ZERO(HWND hwnd, UINT Message, UINT wParam, LONG lParam){ static HANDLE background; static Trainer ReadRank; HDC hDC, hCompatibleDC; PAINTSTRUCT PaintStruct; RECT Rect, Rect2, Rect3; switch (Message){ case WM_CREATE: background = LoadBitmap(hInstance2, MAKEINTRESOURCE(IDB_BITMAP1)); //Фоновое изображение, загружаемое из RC файла return 0; case WM_PAINT: GetClientRect(hwnd, &Rect); hDC = BeginPaint(hwnd, &PaintStruct); hCompatibleDC = CreateCompatibleDC(hDC); SelectObject(hCompatibleDC, (HANDLE)background); BitBlt(hDC, 0, 0, Rect.right, Rect.bottom, hCompatibleDC, 0, 0, SRCCOPY); ReadRank.GetProcess(); ReadRank.GetModule(); ReadRank.OpenProc(); Rect2.left = 0; Rect2.right = 192; Rect2.top = 0; Rect2.bottom = 367; Rect3.left = 807; Rect3.right = 192; Rect3.top = 0; Rect3.bottom = 367; DrawText(hDC, to_string(ReadRank.GetBloodRank()).c_str(), -1, &Rect2, DT_CENTER|DT_SINGLELINE|DT_VCENTER); DrawText(hDC, to_string(ReadRank.GetBlueRank()).c_str(), -1, &Rect3, DT_CENTER | DT_SINGLELINE | DT_VCENTER); DeleteDC(hCompatibleDC); EndPaint(hwnd, &PaintStruct); return 0; case WM_DESTROY: DeleteObject(background); PostQuitMessage(1); return 0; case WM_KEYDOWN: if (wParam == 27) SendMessage(hwnd, WM_CLOSE, wParam, lParam); InvalidateRect(hwnd, NULL, false); return 0; case WM_LBUTTONDOWN: InvalidateRect(hwnd, NULL, false); return DefWindowProc(hwnd, Message, wParam, lParam); case WM_RBUTTONDOWN: InvalidateRect(hwnd, NULL, false); return DefWindowProc(hwnd, Message, wParam, lParam); case WM_ERASEBKGND: return 0; } return DefWindowProc(hwnd, Message, wParam, lParam); }
Скрытый текст//MemReader.cpp #pragma once #include <Windows.h> #include <TlHelp32.h> #include <cstring> #include "MemReader.h" #include <string> Trainer::Trainer(){ ProcessName = new char[16]; BloodRank = 0; BlueRank = 0; strcpy_s(ProcessName, 16, "Game.exe"); pHandle = 0; } Trainer::~Trainer(){ delete[] ProcessName; } DWORD Trainer::GetProcess(){ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 pInfo; pInfo.dwSize = sizeof(PROCESSENTRY32); if (Process32First(snapshot, &pInfo)){ do{ if (!strcmp(ProcessName, pInfo.szExeFile)){ pID = pInfo.th32ProcessID; CloseHandle(snapshot); return pInfo.th32ProcessID; } } while (Process32Next(snapshot, &pInfo)); } } MODULEENTRY32 Trainer::GetModule(){ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pID); MODULEENTRY32 mInfo; mInfo.dwSize = sizeof(MODULEENTRY32); Module32First(snapshot, &mInfo); AddressBase = (INT64)mInfo.modBaseAddr; //MessageBox(NULL, std::to_string(AddressBase).c_str(), "33", MB_OK); CloseHandle(snapshot); return mInfo; } HANDLE Trainer::OpenProc(){ return pHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pID); } short int Trainer::GetBloodRank(){ ReadProcessMemory(pHandle, (LPVOID)(AddressBase + 0x160B8D0), &AddressOffset, 8, NULL); ReadProcessMemory(pHandle, (LPVOID)(AddressOffset + 0xd0), &AddressOffset, 8, NULL); ReadProcessMemory(pHandle, (LPVOID)(AddressOffset + 0x490), &AddressOffset, 8, NULL); ReadProcessMemory(pHandle, (LPVOID)(AddressOffset + 0x1c8), &BloodRank, 2, NULL); return BloodRank; } short int Trainer::GetBlueRank(){ ReadProcessMemory(pHandle, (LPVOID)(AddressBase + 0x160B8D0), &AddressOffset, 8, NULL); ReadProcessMemory(pHandle, (LPVOID)(AddressOffset + 0xd0), &AddressOffset, 8, NULL); ReadProcessMemory(pHandle, (LPVOID)(AddressOffset + 0x490), &AddressOffset, 8, NULL); ReadProcessMemory(pHandle, (LPVOID)(AddressOffset + 0x1c6), &BlueRank, 2, NULL); return BlueRank; } DWORD64 Trainer::FindSignature(DWORD64 base, DWORD64 size, byte* sign, char* mask){ MEMORY_BASIC_INFORMATION mbi = { 0 }; DWORD64 offset = 0; while (offset < size){ VirtualQueryEx(pHandle, (LPCVOID)(base + offset), &mbi, sizeof(MEMORY_BASIC_INFORMATION)); if (mbi.State != MEM_FREE){ byte* buffer = new byte[mbi.RegionSize]; ReadProcessMemory(pHandle, mbi.BaseAddress, buffer, mbi.RegionSize, NULL); for (int i = 0; i < mbi.RegionSize; i++){ if (DataCompare(buffer + i, sign, mask)){ delete[] buffer; return (DWORD64)mbi.BaseAddress + i; } } delete[] buffer; } offset += mbi.RegionSize; //MessageBox(NULL, std::to_string(mbi.RegionSize).c_str(), "33", MB_OK); } return 0; } bool Trainer::DataCompare(byte* data, byte* sign, char* mask){ for (; *mask; data++, mask++, sign++){ if (*mask == 'x' && *data != *sign) return false; } return true; } void Trainer::SetAddressBase(DWORD64 addr){ AddressBase = addr; }
Скрытый текст//Memreader.h #pragma once #include <Windows.h> #include <TlHelp32.h> class Trainer{ public: Trainer(); ~Trainer(); DWORD GetProcess(); MODULEENTRY32 GetModule(); HANDLE OpenProc(); short int GetBloodRank(); short int GetBlueRank(); DWORD64 FindSignature(DWORD64 base, DWORD64 size, byte* sign, char* mask); bool DataCompare(byte* data, byte* sign, char* mask); void SetAddressBase(DWORD64); private: short int BloodRank; short int BlueRank; char* ProcessName; HWND hWndDS; DWORD pID; HANDLE pHandle; INT64 AddressBase; INT64 AddressOffset; };
Как уже говорил, проблема только на windows 10 и только в режиме x64 (в режиме x32 на w10 окно создается, но программа не работает т.к. функции принимают 4 байтовые значения)
Может нужно какие-то ещё параметры изменить в Project Properties? Или версия студии неподходит?
-
В 29.01.2016в15:35, partoftheworlD сказал:
А вы трейнеры для 64 битных игр делали под windows 10? Тут такое дело, чтобы функции ReadProcessMemory и подобные принимали в качестве параметров 8 байтовые значения (я имею в виду второй параметр, в который заносится адрес из которого нужно считать данные, а в 64 битной игре и адреса 64 битные), необходимо перевести проект в Visual Studio 2013 в 64 битный режим (Project-> Project Properties->Configuration Manager->Active Solution Platform->x64), но вот в чем беда, на Windows 7 при таком раскладе всё продолжает работать, а в Windows 10 не создается окно (функция CreateWindow возвращает 0), я даже без трейнера попытался его создать, банальный шаблон программы HelloWorld, где просто создается окно и более ничего, в 32 битной версии нормально всё, а в 64 окно не хочет создаваться (проблема только на Windows 10), не знаете в чём дело?
-
Нашел нужный адрес, по которому инструкция лежит, через сканер сигнатур. Но как теперь прочитать что в регистре лежит?
-
Ещё такой вопрос: у меня есть байтовый адрес инструкции (48 8B 58 38 48 85 DB 74 * F6), есть сама инструкция (mov rbx,[rax+38]), в регистре rax хранится нужный мне адрес (тот самый, который был получен вчера через MODULEENTRY32), как можно на с++ добраться до этого регистра и считать из него информацию? Дело в том, что вчерашний вариант работает исключительно на в7, этот же вариант будет работать и на в10.
-
Спасибо) Забыл размерность (mEntry.dwSize = sizeof(mEntry) ) указать, из-за этого адрес и возвращался нулевым)
-
А где именно в той структуре адрес лежит? Я сделал так
INT64 address; snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); pInfo.dwSize = sizeof(PROCESSENTRY32); if (Process32First(snapshot, &pInfo)){ while (Process32Next(snapshot, &pInfo)){ if (!strcmp(name, pInfo.szExeFile)){ HANDLE hSnap; hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pInfo.th32ProcessID); MODULEENTRY32 mod; if (Module32First(hSnap, &mod)) { } CloseHandle(hSnap); } } }
В mod оказывается экзешник игры (если верить mod.szExePath), но как адрес вытащить?
modBaseAddr мне 0 возвращает
-
У меня такая проблема - в СЕ самый нижний указатель (который должен быть статическим ) имеет вид "xxx.exe+160B8D0", так вот, сам xxx.exe - динамический и каждый раз меняется.
Как его можно занести в код на с++? Был бы он статическим, записал бы так:
ReadProcessMemory(phandle, (LPVOID)(0x13F8D0000 + 0x160B8D0), &address, 8, NULL); ReadProcessMemory(phandle, (LPVOID)(address + 0xd0), &address, 8, NULL); ReadProcessMemory(phandle, (LPVOID)(address + 0x490), &address, 8, NULL); ReadProcessMemory(phandle, (LPVOID)(address + 0x1c6), &BlueRank, 2, NULL);
Но увы, стоит перезрузить систему и уже не работает из-за измененного базового адреса (0x13F8D0000).
-
Не, все эти типы тоже были 4 байтовые. Потратил пол дня на поиск ответа, а оказалось всё предельно просто.. http://www.mpgh.net/forum/showthread.php?t=860166 вот тут у человека была такая же проблема. Нужно было всего лишь перейти в режим 64 битного приложения в VS, после этого и LPVOID, и LPCVOID стали 8 байтовые и спокойно приняли этот адрес.
-
В общем, хочу написать трейнер для 64битной игры (она использует 64 битные адреса, 0х7FFED6CF128, в этом духе), а функция ReadProcessMemory в качестве аргумента принимает LPVOID , который 4 байтовый. Т.е. передавая этой функции адрес, передается только ED6CF128, а остальная часть теряется и, соответственно, функция считывает мусор. Как передать ей полный адрес? С 32 битными процессами проблем нет, там всё считывается прекрасно.
-
И снова здравствуйте. Может кто-нибудь помочь c пониманием как работает нижеследующий скрипт (сразу скажу - он рабочий, просто хочу наконец понять как точно он работает) ? Часть мне понятна, сам его несколько раз восстанавливал ибо для игры выходили патчи и ломали его, менялись адреса, поменялась инструкция раз и тд, но непонятная мне часть продолжала работать исправно.
Итак, что делает этот скрипт? Он телепортирует всех монстров, которые в данный момент просчитываются игрой, к игроку. Работа здесь идет только с одной инструкцией:
"DAI.exe"+BCB4AA:
movaps xmm0,[rax+000001A0]Она отвечает за координаты как героя, так и монстров, в [rax+1A0] хранится адрес этой самой координаты (первой идет X, с шагом 4 идут Y и Z).
Вначале (то, что находится между //1) мы cравниваем что лежит в [rax+1A0] (координата Х) и [rax+1A8] (координата Z) с тем, что лежит в адресах этих координат, принадлежащих игроку, т.е. делаем фильтр, отсеивающий их изменение. Далее (//2) мы кладем координаты игрока в [moffset+4,8,C], сам [moffset] равен 1, что отвечает за расстояние, на котором должны появится монстры относительно игрока, а [mmindist] - дистанция, на которой монстры могут быть захвачены (т.е. если ввести 1 - монстры не захватятся вообще, а в 5000 захватываюстя со всей локации), таким образом монстры должны были бы телепортироваться к игроку. Вот эта часть мне ясна. Собственно, если так и оставить (заменив [moffset+*] на [rax+*]), то монстры действительно будут телепортироваться к игроку, но после отключения скрипта, сделав хоть 1 движения, они сразу же вернутсья туда, откуда их вытащили. И вот дальнейшая часть скрипта каким-то образом это исправляет, заставляя их остаться на месте.
С другими инструкциями, вроде бы, работы не ведется, потому мне не понятно как это происходит.
Особенно мне непонятно то, что находится между //3 (в дальнейшем есть еще несколько таких конструкций). Как я понимаю, это какой-то цикл, но как именно он работает так и не смог разобраться, может кто-то объяснить эту конструкцию? Могу сказать одно - если отключить эти циклы, то телепорта не будет происходить никакого.. а еще, перед последним патчем в mov rcx,2 (это счетчик, как я понимаю, т.е. сколько раз должен цикл выполниться), был равен 9. После патча такая конструкция стала работать неверно - монстры захватывались далеко не все, могли телепортироваться те, которые стояли в другой части карты, а стоящие в 2х шагах не захватывались. Когда я поменял 9 на 2 - все заработало снова, как прежде.
Извиняюсь за всю эту стену, но сам так и не разобрался во всем этом, может найдется кто-то, кому не лень будет немного пролить свет на все это?) Или, хотя бы, объяснить как работает цикл между //3.
[ENABLE]alloc(magnet,4096,"DAI.exe"+BCB4AA)label(returned)label(skipme)label(skipplayer)label(pcoord)label(mcoord)label(moffset)label(mcount)label(mmindist)label(mcalcdist)label(mconst)label(pangle)label(tests)registersymbol(mmindist)registersymbol(mcount)registersymbol(moffset)magnet:push raxpush rbxpush rdxpush rcxpush rdipush rsipush edx //1push rbxmov rbx,[DAI.exe+160B8D0]mov rbx,[rbx+98]mov rbx,[rbx+28]mov rbx,[rbx+0]mov rbx,[rbx+100]mov rbx,[rbx+10]mov rbx,[rbx+10]mov rbx,[rbx+1d0]mov rbx,[rbx+20]mov edx,[rbx+1a0]pop rbxcmp [rax+000001A0],edxpop edxjne testspush edxpush rbxmov rbx,[DAI.exe+160B8D0]mov rbx,[rbx+98]mov rbx,[rbx+28]mov rbx,[rbx+0]mov rbx,[rbx+100]mov rbx,[rbx+10]mov rbx,[rbx+10]mov rbx,[rbx+1d0]mov rbx,[rbx+20]mov edx,[rbx+1a8]pop rbxcmp [rax+000001A8],edxpop edxje skipplayer //1{let's just check this isn't the player}//mov ebx,[[[[[[[DAI.exe+FDB074]+74]+B8]+8]+14]+1B0]+10]//cmp ebx,eax//je skipplayer{copy player coords}tests:push edx //2push rbxmov rbx,[DAI.exe+160B8D0]mov rbx,[rbx+98]mov rbx,[rbx+28]mov rbx,[rbx+0]mov rbx,[rbx+100]mov rbx,[rbx+10]mov rbx,[rbx+10]mov rbx,[rbx+1d0]mov rbx,[rbx+20]mov edx,[rbx+1a0]pop rbxmov [moffset+4],edxpop edxpush edxpush rbxmov rbx,[DAI.exe+160B8D0]mov rbx,[rbx+98]mov rbx,[rbx+28]mov rbx,[rbx+0]mov rbx,[rbx+100]mov rbx,[rbx+10]mov rbx,[rbx+10]mov rbx,[rbx+1d0]mov rbx,[rbx+20]mov edx,[rbx+1a8]pop rbxmov [moffset+C],edxpop edxpush edxpush rbxmov rbx,[DAI.exe+160B8D0]mov rbx,[rbx+98]mov rbx,[rbx+28]mov rbx,[rbx+0]mov rbx,[rbx+100]mov rbx,[rbx+10]mov rbx,[rbx+10]mov rbx,[rbx+1d0]mov rbx,[rbx+20]mov edx,[rbx+1a4]pop rbxmov [moffset+8],edxpop edx//2mov [mmindist],(float)5000mov [moffset],(float)1//3add rbx,1A0mov rsi,rbxmov rdi,pcoordcldmov rcx,2rep movsb //3//jmp skipplayer{copy monster coords}add rax,1A0mov rsi,raxmov rdi,mcoordcldmov rcx,2rep movsbsub rax,1A0{calculate distance}movss xmm0,[pcoord]movss xmm1,[mcoord]subss xmm0,xmm1mulss xmm0,xmm0movss [mcalcdist],xmm0movss xmm0,[pcoord+4]movss xmm1,[mcoord+4]subss xmm0,xmm1mulss xmm0,xmm0movss [mcalcdist+4],xmm0movss xmm0,[pcoord+8]movss xmm1,[mcoord+8]subss xmm0,xmm1mulss xmm0,xmm0movss [mcalcdist+8],xmm0movss xmm0,[mcalcdist]movss xmm1,[mcalcdist+4]addss xmm0,xmm1movss xmm1,[mcalcdist+8]addss xmm0,xmm1movss [mcalcdist+c],xmm0fld dword ptr [mcalcdist+c]fsqrtfstp dword ptr [mcalcdist+c]{test distance}movss xmm0,[mcalcdist+c]movss xmm1,[mmindist]cmpss xmm0,xmm1,1movss [mcalcdist+C],xmm0cmp [mcalcdist+C],0je skipme{increment monster counter}mov rcx,[mcount]inc rcxmov [mcount],rcx{get orientation}//mov ebx,[[[[DAI.exe+FDB074]+74]+B4]+A8]// movss xmm0,[ebx+54]//movss xmm0,[ebx]//addss xmm0,[mconst+4]//mulss xmm0,[mconst]//movss [pangle],xmm0{calculate sine/cosine pair}fld dword ptr [pangle]fsincosfstp dword ptr [pangle+4]fstp dword ptr [pangle+8]{calculate offsets}movss xmm0,[pcoord]addss xmm0,[moffset+4]movss xmm1,[moffset]mulss xmm1,[pangle+8]subss xmm0,xmm1movss [mcoord],xmm0movss xmm0,[pcoord+4]addss xmm0,[moffset+8]movss [mcoord+4],xmm0movss xmm0,[pcoord+8]addss xmm0,[moffset+C]movss xmm1,[moffset]mulss xmm1,[pangle+4]subss xmm0,xmm1movss [mcoord+8],xmm0{assign new coords to "previous" vector}add rax,1A0mov rsi,mcoordmov rdi,raxcldmov rcx,crep movsb{assign new coords to "next" vector}add rax,20mov rsi,mcoordmov rdi,raxcldmov rcx,crep movsbskipme:pop rsipop rdipop rcxpop rdxpop rbxpop rax{old scope}movaps xmm0,[rax+000001A0]jmp returnedskipplayer:mov [mcount],0 {start counting from zero again}pop rsipop rdipop rcxpop rdxpop rbxpop raxmovaps xmm0,[rax+000001A0]jmp returnedpcoord:dq 0dq 0dq 0pangle:dq 0dq 0dq 0mcoord:dq 0dq 0dq 0mcount:dq 0mcalcdist:dq 0dq 0dq 0dq 0mmindist:dq (float)10moffset:dq (float)5dq (float)0dq (float)0dq (float)0mconst:dq (float)3.14159265dq (float)1dq (float)-1{blank out the call to fall timer test (I think?)}//"DAI.exe"+34139D://nop//nop//nop//nop//nop{hook magnet at per-critter position update code}"DAI.exe"+BCB4AA:jmp magnetnopnopreturned:[DISABLE]"DAI.exe"+BCB4AA:movaps xmm0,[rax+000001A0]unregistersymbol(mmindist)unregistersymbol(mcount)unregistersymbol(moffset)dealloc(magnet)
-
Для того, чтобы точно ответить на вопрос, нужны твой скрипт и информация на что срабатывает игровая инструкция, которую ты использовал в скрипте (на ход ГГ или всегда читает координаты, ну и т.д.).
Инструкция вообще не имеет отношения к координатам..
У меня есть структура заклинаний. Там можно изменить кол-во вылетающих снарядов, скорость, траекторию и даже дочерние части спелла, прикрепленные к заклинанию. Т.е. изменяя числа и id в этой структуре можно создавать свои уникальные заклинания. Так вот, я написал скрипт, который приделан к удару правой рукой (инструкция считывается пока нажата кнопка удара, успевает за 1 нажатие проработать раз 15-20), сразу после срабатывания этой инструкции я вписал свои mov [xxx],(float)xx или mov [xxx],#xx и так далее, где вместо [xxx] - адреса, в которые я хочу внести изменения (и которые не имеют отношения к выполняемой инструкции), а в (float)xx - новые значения. Так вот некоторые адреса, изменяемые таким образом, дают нужный результат, а другие нет, хотя если менять их вручную - все выдают нужный результат. Телепорт я привел только в качестве примера тк это один из вариантов, в которых такая подмена работала неверно. Вот и хочу понять где зарыта собака.. скрипт выкладывать нет смысла мне кажется - стандартный шаблон, сразу после оригинальной инструкции (повторюсь, не имеющей отношения к изменяемым адресам) эти строки с подменой значений в адресах. Или так вообще не стоит делать?
И еще, эти адреса, которые пытаюсь изменить таким способом - статические.
-
А может кто-нибудь ответить еще на 1 вопрос - почему некоторые адреса, изменяемые через скрипт, работают иначе, чем при ручном изменении? Простой пример: я нашел адрес с координатами героя (там их было штук 100 для каждой координаты, я выбрал нужный), если вручную, через таблицу, поменять значение, то происходит телепорт и все нормально. Но если в этот адрес занести нужное значение через скрипт (записью вида: mov [xxxxxxxx],(float)xx ), то происходит лишь иллюзия телепорта - герой перемещается на нужный участок, но при любом движении сразу же возвращаетя назад. Я обратил внимание, что при ручном изменении все оставшиеся 99 адресов также изменяются на то, что я ввел в первый адрес. А при изменении скриптом они остаются прежними, меняется только тот, в который я занес значение, что и заставляет, видимо, персонажа вернуться назад. Как с этим бороться, что сделать, чтобы при изменении адреса через скрипт эффект был бы аналогичен ручному изменению?
-
Да, так тоже работает, адрес максимальных жизней лежал в [rbx+170].
Но уже получилось и по другому сделать, запись вида:
cmp rbx,000007FFEFDE9A40je exitoriginalcode:cmp dword ptr [rbx+00000168],00exit:jmp returnhere
Оказалась неисправной, а вот так:
push raxmov rax,000007FFEFDE9A40cmp rbx,raxje exitoriginalcode:cmp dword ptr [rbx+00000168],00exit:pop raxjmp returnhere
заработало. Не понимаю в чем разница, какие еще сюрпризы принесет 64 разрядность.. причем если бы я напрямую сравнивал не с адресом жизней, а с каким-нибудь "0" - простым числом, то фильтр сработал бы сразу.
-
Возникла еще проблема. Захотел сделать для этой же игры бессмертие. Нашел нужную инструкцию - при ее отключении урон просто не засчитывается. Эта же инструкция работает также и с монстрами, соответственно все становятся бессмертными. Инструкция вида cmp dword ptr [rbx+00000168],00, в rbx+168 находится адрес жизней, если монстр бьет по мне - моих (который статический), если я бью по монстру - его. В других играх ставил простой фильтр, который сравнивал значение регистра с адресом жизней (с учетом смещения, естественно) и если адрес совпадал с геройским - скрипт перепрыгивал оригинальную инструкцию. Сейчас же у меня что-то не выходит, фильтр не срабатывает, в чем может быть проблема? К слову, недавно делал еще 1 скрипт для этой игры и там также была проблема с фильтром, определяющим адрес, фильтр заработал только с регистром, в котором находился "0" в нужном мне случае.
Адрес самих жизней - 7FFEFDE9BA8
UPD
Просидел полчаса, не понимал что не так, запостил здесь и сразу же пришла мысль сравнить rbx не с самим значением, а с rax, предварительно положив это же значение в него (rax) и все заработало.. теперь задаюсь другим вопросом - почему не получилось сравнить напрямую? В прошлых играх всегда напрямую сравнивал и работало.. точно также, как и с этими указателями - старая запись перестала работать в 64 битной игре..
[ENABLE]//code from here to '[DISABLE]' will be used to enable the cheatalloc(newmem,2048,"DAI.exe"+1671B5)label(returnhere)label(originalcode)label(exit)newmem: //this is allocated memory, you have read,write,execute access//place your code herecmp rbx,000007FFEFDE9A40je exitoriginalcode:cmp dword ptr [rbx+00000168],00exit:jmp returnhere"DAI.exe"+1671B5:jmp newmemnopnopreturnhere:[DISABLE]//code from here till the end of the code will be used to disable the cheatdealloc(newmem)"DAI.exe"+1671B5:cmp dword ptr [rbx+00000168],00//Alt: db 83 BB 68 01 00 00 00
-
Не, без указателя там нельзя было обойтись. Я rbx после внесения указателя в eax восстанавливал pop'ом.
А насчет вашей замены alloc на registersymbol.. в чем вообще разница между ними? Я обычно в alloc все заношу, как-то мне это более понятно - резервирую место в памяти с указанием размера и туда забрасываю значения.
-
Cпасибо, вашим методом указатели работают через скрипт) Только в том скрипте, который вы предложили еще все ebx на rbx поменять понадобилось - адреса то 64 битные, в ebx не умещались.
-
NullAlex: большой код принято заключать не только в тег кода, но и в спойлер.
[ENABLE]//code from here to '[DISABLE]' will be used to enable the cheatalloc(newmem,2048,"DAI.exe"+373B05)label(returnhere)label(originalcode)label(exit)alloc(Coord,64)alloc(offset,64)newmem: //this is allocated memory, you have read,write,execute access//place your code heremov [offset],(float)0.7push eaxmov eax,[[[[[[[[[[DAI.exe+160B8D0]+98]+28]+0]+100]+10]+10]+1d0]+20]+1a0]mov [Coord],eaxfld [Coord]fadd [offset]fstp [Coord]mov eax,[Coord]mov [rbx+10],eaxpop eaxpush eaxmov eax,[[[[[[[[[[DAI.exe+160B8D0]+98]+28]+0]+100]+10]+10]+1d0]+20]+1a4]mov [rbx+14],eaxpop eaxpush eaxmov eax,[[[[[[[[[[DAI.exe+160B8D0]+98]+28]+0]+100]+10]+10]+1d0]+20]+1a8]mov [rbx+18],eaxpop eaxoriginalcode:mov eax,[rbx+10]movss xmm1,[rbx+18]exit:jmp returnhere"DAI.exe"+373B05:jmp newmemnopnopnopreturnhere: [DISABLE]//code from here till the end of the code will be used to disable the cheatdealloc(newmem)dealloc(Coord)dealloc(offset)"DAI.exe"+373B05:mov eax,[rbx+10]movss xmm1,[rbx+18]
-
Как можно записать в скрипт указатель на нужный адрес? Раньше я использовал в одном из скриптов такую запись:
mov eax,[[[[[[[1201ed44]+b8]+8]+14]+1b0]+10]+120]
И все работало. Сейчас, в другой игре, которая использует 64 битные адреса, нужно сделать похожую запись, пишу вот так:
mov eax,[[[[[[[[[[DAI.exe+160B8D0]+98]+28]+0]+100]+10]+10]+1d0]+20]+1a0]
и компилятор ругается на эту запись. Указатель верный - в таблице все определяет и меняется через него правильно. Но мне нужно записать его в скрипт.. пока что мучаюсь, вручную указывая в скрипте в нескольких местах адрес, на который в конечном счете указывает этот указатель. Но каждый раз его менять при перезапуске игры - очень утомляет.
-
Выложи сюда скрипт, посмотрим - может ошибка в скрипте. Хотя это может быть и особенностью игры.
Особо для этого ничего не писал - есть адрес для оси Х со значением типа float (для остальных осей аналогично), если этот адрес заморозить и вставить свое значение, то происходит описанная мной ситуация (без заморозки там вообще небольшой рывок в сторону и на этом все). Ставил на этот адрес бряк на доступ, первой инструкцией является movq xmm0,[eax+08], которая считывается каждый кадр. [eax+08] - это адрес нашей координаты Х. В скрипте я просто перед меткой оригинального кода ставил свое
mov [eax+08],(float)***, где вместо звездочек любое выбранное заранее число. При запуске этого скрипта также происходит вышеописанная ситуация.
UPD В дебрях сети обнаружил таблицу с функцией телепорта для главного героя. Вместо скриптов или осей xyz там длиннющий массив байтов, изменяющийся при любом малейшем движении игрока. Если его сохранить в одном месте, перейти в другую часть локации и вставить сохраненное значение, то произойдет моментальный телепорт. Пока самому выйти на этот адрес не удалось, если пойму как он был найден, то, возможно, удастся и для монстров найти подобные и будет дело в шляпе)
Ребята сори за оффтоп.
garik66 ты знаешь как тема эта называлась или может ссылка на нее у тебя есть. Я ее никак не могу найти.
У меня тоже пока не получилось найти эту тему, если вдруг обнаружится - скинь пожалуйста ссылку сюда)
-
Такой скрипт написать можно, сам я лично не делал, но читал где-то у MasterGH - он делал выстрел в игре из другой части кода (извини - объяснить не смог, читал давно и пока уровня моего опыта и знаний маловато чтобы было понять как это сделал MasterGH). Но точно помню, что он использовал Call для вызова кода выстрела и потом Ret для возврата назад в свою инъекцию. Так что написать, то что описал, можно.
Это на этом форуме была статья/скрипт такой? Нужно будет поискать, с самого первого дня как познакомился с СЕ была мысль такое проворачивать.
Зачем тебе проход монстра сквозь стены. Тебе просто нужно вписывать каждому монстру координаты ГГ, а игра обычно сама подвигает движущийся объект. Грубо говоря, то что ты хочешь сделать это Blink-телепорт наоборот: не ГГ прыгает на чьи-то координаты (прицел, НПС), а наоборот монстр будет прыгать на координаты ГГ.
Посмотри, как пример у меня в скрипте Blink-телепорт: ГГ прыгает на координаты ближайшего НПС и тот двигается (в видео, которое ниже в сообщении это видно) Скрипт и видео здесь.
Так я выше писал, что нашел координаты как игрока, так и монстров, но при их искусственном изменении не происходит моментального телепорта как в других играх - персонаж начинает рывками без анимации бега/ходьбы двигаться к новым координатам и если между старой и новой точкой присутствует препятствие - он через нее не пройдет. Если Xipho прав, то на это дело влияет как-то система коллизий.
-
Не, там все правильно - когда игра запускает эту инструкцию и видит, что регистр edi равен указанному в сравнении, то она проверяет какое значение флага установлено. Если 0 (а оно 0 до тех пор, пока не пальнешь заклинанием), то флаг изменяется на 1 и дальше идет по коду (когда срабатывают другие инструкции, они также проверяют флаг и при значении "1" происходит выполнение моего кода, иначе все идет по стандарту), под конец нас снова перекидывают к старту первой инструкции, она вновь проверяет флаг, видит, что он равен 1 и переходит к "метке 2", в которой флаг меняется на 0, затем выполняется стандартный код выстрела заклинания и на этом все заканчивается. Меня интересует, вообще такие вещи можно делать? Первая инструкция срабатывает при выстреле заклинания, а вторая и остальные должны срабатывать при ударе по монстру, я же попытался добавить эффект удара при выстреле заклинания. Ошибка, мне кажется, в том, что так смешивать инструкции нельзя, а если и можно, то существуют какие-то правила, о которых мне неизвестно)
-
Спасибо, keng, но у меня проблема именно с тем, чтобы подвигать монстра (вернее подвигать так, что б он прошел сквозь стену), если бы это удалось - найти координаты и соответствующие инструкции для каждого монстра было бы несложно, а затем написать скрипт, что б в эти инструкции вписал нужные мне значения. Искать массив указателей, конечно, правильней, приму к сведению)
Такой еще вопрос, когда пишешь скрипт, можно ли делать так: при запуска некой инструкции, при проверке, например, флага или еще какого-нибудь фильтра, делать прыжок вообще в другую часть исполняемого кода, к инструкции, которая запускается при других условиях, предварительно изменив регистры на те, которые должны быть при правильном запуске этой инструкции, затем от нее пройти часть кода и, наконец, восстановив регистры назад, вернутся к тому месту, откуда был совершен прыжок или это заведомо фейл? Собственно, я уже написал такой скрипт:
[ENABLE]alloc(newmem,2048)alloc(newmem2,2048)alloc(newmem3,2048)label(returnhere)label(returnhere2)label(returnhere3)label(originalcode)label(exit)label(originalcode2)label(exit2)label(originalcode3)label(exit3)label(flag1)label(metka)label(metka2)label(metka3)registersymbol(flag1)newmem:cmp edi,1098732Cjne originalcodecmp [flag1],1je metka2mov [flag1],1jmp DarkSoulsII.exe+1C36B2//mov [102AD8AC],#150metka2:mov [flag1],0originalcode:mov al,[edi+2F]movsx edx,alexit:jmp returnhere2newmem2://mov [ebp+08],#100cmp [flag1],0je metkamov eax,103FB600mov ebx,1030BF00mov ecx,00000000mov edx,1259DC20mov esi,103FC010mov edi,104CE8F0mov esp,0018F068mov ebp,0018F070metka:push eaxmov eax,[ebp+08]imul eax,eax,#2mov [ebp+08],eaxpop eaxoriginalcode2:mov edx,[ebp+08]mov eax,[esi+000000FC]cmp [flag1],1je metka3exit2:jmp returnheremetka3:jmp DarkSoulsII.exe+1C36B5newmem3:originalcode3:mov [esi+000000FC],ecxcmp [flag1],0je exit3mov eax,00000001mov ebx,0018F7F8mov ecx,00000001mov edx,00000000mov esi,10380770mov edi,1098732Cmov esp,0018F790mov ebp,0018F7F0jmp DarkSoulsII.exe+3ED7D2exit3:jmp returnhere3flag1:db 00"DarkSoulsII.exe"+1C36F1:jmp newmem3nopreturnhere3:"DarkSoulsII.exe"+3ED7D2:jmp newmemnopreturnhere2:"DarkSoulsII.exe"+1C36B2:jmp newmem2nopnopnopnopreturnhere: [DISABLE]dealloc(newmem)dealloc(newmem2)dealloc(newmem3)unregistersymbol(flag1)"DarkSoulsII.exe"+3ED7D2:mov al,[edi+2F]movsx edx,al"DarkSoulsII.exe"+1C36B2:mov edx,[ebp+08]mov eax,[esi+000000FC]"DarkSoulsII.exe"+1C36F1:mov [esi+000000FC],ecx
При его запуске, пока флаг равен 0, все работает нормально, но если выстрелить определенным заклинанием (в этом случае регистр edi становится равным 1098732C), флаг переходит в состояние "1". По задумке, в конце флаг должен вернутся в состояние "0" и на этом все закончится, но у меня начинается бесконечный цикл, заклинания начинают стрелять как из пулемета, 50 снарядов в секунду, что через десяток секунд приводит к крашу.
-
Ну я же расписал ради чего мне это - заснять эпичное видео с битвой полусотни монстров против героя, вооруженного кучей кастомных заклинаний, это пве. Учитывая, что на вашем форуме в разделе таблиц есть тема по дарк соулсу второму, в которой лежит скрипт, увеличивающий урон по противникам и уменьшающий по герою, то решил, что запрета на обсуждение взлома данной игры нет, иначе не создавал бы тему.
Dll injection
in Низкоуровневое программирование
Опубликовано
Извиняюсь сразу за тупой вопрос ) В общем, как мне известно, чтобы внедрить свою дллку в игру, необходимо написать эту самую библиотеку, а затем создать отдельно приложение типа такого:
собственно, вопрос, а есть ли способ внедрить свою длл без стороннего приложения? Т.е. чтобы закинул библиотеку в папку с игрой и она сама подключилась?)