-
Постов
1 635 -
Зарегистрирован
-
Посещение
-
Победитель дней
55
Тип контента
Профили
Форумы
Загрузки
Блоги
Весь контент keng
-
/*---------------------------------------------------------------------------*/ Пустяки, я только рад помочь. Здорово, что всё получилось. Очень советую внимательно и вдумчиво разглядывать код примеров и описания в MSDN, потому что во всей этой каше из типов и макросов (HMODULE == DWORD == unsigned long) очень легко запутаться. Так же советую почитать (например, в Kernighan & Ritchie) про указатели, как они работают и что делают - наконец можно будет перестать (почти перестать) путаться в "*" и "&". Крайне полезная штука в нашем деле, рекомендую. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Я очень горячо предлагаю в функции OpenProcess использовать флаг "PROCESS_ALL_ACCESS", а потом, убедившись, что она возвращает валидный идентификатор процесса (не ноль), посмотреть коды ошибок вызова ReadProcessMemory и колько байт она читает. Если нисколько - то смотреть код ошибки, а затем подняться на уровень вверх - проверить, корректно ли срабатывает функция VirtualProtectEx. Без обид, но гадать на куске кода мы можем ещё очень долго, но с использованием отладчика и кодов ошибок дело может пойти куда быстрее. PS: Третий параметр функции ReadProcessMemory - это указатель на адрес переменной, куда будет прочитано значение адреса памяти. У тебя объявлена переменная типа DWORD, которая затем конвертируется в LPVOID (void*). Я могу ошибаться, но при этом переменная принимает значение "0", так как конвертируется значение переменной, а функции нужен её адрес. Попробуй просто написать "ReadProcessMemory(..., ..., &CPed, ..., ...);". /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Мгм. ReadProcessMemory(phandle,(void*)Base, (LPVOID)CPed, sizeof(Base), NULL); center = CPed + 0x14; Второй параметр у тебя приводится к void*, а требуется LPCVOID. Объяви: DWORD bRead = 0; И передай последним параметром (вместо NULL) указатель на него. Заодно заверни вызов RPM в отдельную переменную (BOOL result = ReadProcessMemory), при успешном вызове должна возвращать не ноль. Типа: DWORD bRead = 0; // Переменная, куда запишем количество прочитанных байт// Если RPM сработала с ошибкой (вернула ноль) или bRead == 0if ( !ReadProcessMemory( 0, 0, 0, 0, &bRead ) || !bRead ) {// Пробуем прочитать код последней ошибки DWORD err = GetLastError(); } else { // Иначе - всё хорошо и прочиталось } /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Немного дополню пункт №2 предыдущего поста. В той функции, что кончается на Ex, можно что-то делать в чужом адресном пространстве (не своего процесса), а в функции без этого окончания - только в пределах адресного пространства собственного процесса. Я говорю про виртуальное адресное пространство, само собой, а не про физическое, но это особого отношения к посту не имеет. Ещё есть функция [VirtualQueryEx], которая позволяет получить информацию о той или иной странице памяти - грубо говоря, проверить, не стоит ли уже на нужной нам странице памяти флаг записи. Все эти VirtualProtect и прочая чушь про защиту страниц памяти нужна затем, чтобы случайно не попытаться записать при помощи WriteProcessMemory в адрес, который находит на странице памяти, защищённой от записи. По идее, конечно, эту ошибку можно и обработать, но если есть обработчик подобных ошибок, то автор (скорее всего) уже в курсе, что такое защита памяти, как и зачем она устроена. Поэтому делается так: 0. Копируем в укромное место старую защиту со страницы. 1. Ставим странице разрешение на запись. 2. Записываем нужное значение. 3. Возвращаем старую защиту на место. Секция кода чаще всего разрешена для записи, а вот секция данных - далеко не всегда, поэтому бывает так, что очень нужно изменить значение, скажем, указателя, а никак - страница, где лежит его адрес, защищена от записи. Вот и. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Привет! У меня есть видео с использованием способа, похожего на способ A1t0r, поищи на канале youtube (ссылка в подписи). Хотелось бы заметить, что анализатор структур CE не всегда корректно обрабатывает поля структуры - он лишь предполагает, так что в некоторых случаях поле float может оказаться указателем, а DWORD - четырьмя отдельными байтами. Работа со структурами - долгая, муторная и очень утомительная, не безумно эффективная. Раньше всё это происходило в 16-ричном редакторе и при любом неверном действии случался вылет из ОС, так что все действия, адреса, смещения и указатели структур (а бывало, что и участки кода) записывались на бумагу. CE очень и очень сильно облегчает в этом плане жизнь. По поводу поиска ID могу добавить, что можно поискать инструкции сравнения (cmp, test и так далее)выше по коду от инструкции, которую требуется изменить, но хотя бы частичный разбор структуры часто оказывается куда полезнее. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ (LPTHREAD_START_ROUTINE) Hook вернёт адрес Hook, сконвертированный в LPTHREAD_START_ROUTINE, а (LPTHREAD_START_ROUTINE) Hook() - результат выполнения Hook(), сконверченный туда же. Если не уверен в результате того или иного выражения - вынеси его в отдельную переменную и проверь в отладчике. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ А можешь показать код, где ты вызываешь SetOptions()? /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Если инструкций много - то проверять, что они корректно работают, подбирать нужную. Долго, муторно, но что поделаешь. Второй вариант - указатели. А что за "старая система с оффсетами"? /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Привет! Меня смущают size=1 и buffer = new byte[6]. Ты читаешь DWORD - это 4 байта, так что size и buffer должны быть по 4 байта. Второй момент: baseadress = BitConverter.ToInt32(buffer, 0);[/font] baseadress += offset[i]; Переменную baseadress ты конвертируешь в Int, а offset - всё ещё массив byte, причём в 16-ричной системе счисления. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ - На форуме и в профиле (у меня) указано разное количество сообщений. - В профиле не работает вкладка "Репутация". - Как-то очень странно работает редактор сообщений, в частности - кнопки Enter и Backspace. Закономерность я до сих пор не улавливаю, но время от времени после нажатия Backspace курсор перескакивает на строчку выше и начинает вести себя как insert+delete одновременно. Трудно это описать, но баг есть и на домашнем компе и на рабочем - Windows\Linux соответственно. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ А зачем нужен сокет в чисто настольком приложении? По мне так достаточно EditBox и ReadProcessMemory \ WriteProcessMemory, но shared-секция удобнее - её один раз организовать и никаких дополнительных функций вызывать не придётся. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Привет! Между модулями PE файла можно сделать "общую" секцию памяти, которая будет видна из обоих (всех) модулей. Называется это shared memory section. Подробнее можно почитать [тут] или спросить у [Coder]'а, у него и видеоуроки на эту тему были. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ А что значит "откуда-то"? Ты волен задавать координаты и текст самостоятельно, переменными, например. А откуда при этом их брать - твоё дело. Не совсем понял, что ты имеешь ввиду. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Интересно! А в диспетчере задач процесс прям такой и висит - с "dll" на конце? Не "exe"? У меня есть подозрение, что ты не в том процессе ищешь. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Ну, по идее, логика там должна быть примерно следующая (в руках меч): 1. Нажали левую кнопку мыши 2. Начинаем анимацию замаха мечом. 3. Проверяем, что на некотором расстоянии от нас (заданном в свойствах меча) есть нечто, а не просто пустое место. 4. Если есть, то играем звук удара по соответствующему предмету. 5. Наносим предмету урон (пытаемся, как минимум). Вот тебе, собственно, надо в отладчике (или в долгом созерцании структуры меча) надо найти пункт три, а точнее - расстояние из этого пункта. Выглядеть это должно как-то так: MOV EBX,[ECX+0x456] ; В EBX кладём "длину меча", ECX - его структура CMP EBX,EDI ; Сравниваем длину меча с расстоянием до противника в EDI JNE 0x3475869 ; Если не равны - то переходим в другое место PUSH EBX ; Иначе - берём всякие аргументы PUSH EDX ; всякие аргументы CALL 0x7685757 ; и вызываем функцию нанесения урона /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Каким угодно, на самом деле. Скорее всего, оно будет ещё и не в пикселях каких-нибудь или метрах, а в игровой мере длины, скажем, в юнитах. PS: 1024-е сообщение на форуме! /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Привет! Скорее всего, это свойство самого оружия, т.е. искать надо в структуре, которая его описывает. Скажем, если у персонажа в руках лук, то для него задаётся количество стрел, скорострельность, дальность полёта стрелы и так далее. Для оружия ближнего боя должна задаваться "длина" оружия, то есть расстояние между тобой и противником, на котором это оружие противника достанет. Искать это можно несколькими способами: 1. Найти оружие, используя структуру самого персонажа. Скажем, найти здоровье, а дальше смотреть, какие инструкции пишут и читают это значение, обнаружится инструкция вроде этой: MOV EAX, [EBX+0x456] Здесь в EBX будет храниться указатель на структуру персонажа, а 0x456 - смещение в этой структуруре до его здоровья. Где-то в ней же может быть и структура, описывающая текущее выбранное персонажем оружие, а уже в ней можно и "длину" поискать. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Предположу, что что-то не так. Можешь показать цепочку указателей в таблице Cheat Engine и твой код на C++, который читает? Вряд ли в моём коде где-то ошибка - проверил на нескольких указателях. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Судя по твоему скриншоту, у тебя выполнилась функция CreateWindow (успешно, так как hwnd не равен нулю, что можно увидеть в нижней части окна), а дальше отладчик встал на строчке return 1. Чтобы посмотреть, попадает ли отладчик в функцию WndProc, просто поставь брейкпоинт в ней. Можно хоть в самом начале, после имени функции и аргументов, на строчке с открывающейся фигурной скобкой. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ По поводу пары вопросов: 1. По паттерну. 2. Подмена оригинальной d3d9.dll во всей системе. 3. Подмена d3d9.dll в папке с программой. 4. Загрузчик для программы. 5. Создание своего девайса в нужном процессе (или в своём) и поиск по этому адресу - в куче или прочими вариантами этого же способа. Если нашёл статический адрес в коде, то скорее всего этот код адаптирован под конкретную игру. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ 1. Да, например, так. Имя сам задаёшь - главное его же указать при регистрации класса окна. Скажем, класс "окно трейнера". А окон два или три. Значит ты один раз зарегистрировал нужный класс, а окон сделал три, у каждого при этом будет одна и та же оконная процедура. 2. Да, именно так. 3. CreateWindow мы по факту создаём после вызова RegisterClass, просто при этом проверяем, что RegisterClass вернула "1" (или "true"), то есть выполнилась верно. Примерно так: if ( RegisterClass) // Если зарегистрировали класс, то CreateWindow // создаём окно if ( RegisterClass == 1 ) CreateWindow if ( RegisterClass == true ) CreateWindow Второй и третий варианты эквивалентны первому. Это простая проверка, чтобы убедиться что класс зарегистрировался и всё идёт хорошо, так как если произойдёт ошибка, то окно создать не получится. По поводу отладки. Вот картинка:
- 31 ответ
-
- 2
-
-
/*---------------------------------------------------------------------------*/ 1. Функция возвращает не функцию, а её результат. Вызывая DefWindowProc, мы просим выдать нам результат "по умолчанию", мол, всё хорошо и сообщение было обработано корректно. И этот результат возвращаем при выходе из WndProc. Иначе в while(GetMessage()) у нас вернётся что-то не то и цикл прервётся, а следовательно и программа тоже завершится. 2. WndProc - функция, в которой происходит обработка сообщений окна. Окон может быть много и каждому нужна такая функция. А ещё можно разным окнам выдавать одну и ту же оконную процедуру, ага. 3. В RegisterClass мы не создаём окно, а, по сути, описываем его. Какое оно будет. А создаём окно этого типа (какой зарегистрировали) уже при помощи CreateWindow, сообщая ему этот самый тип (или класс). 4. Как её отладить - покажу чуть позже, с картинками. Остальное пока понятно? PS: Без английского на уровне хотя бы "плохо читаю и со словариком" будет тяжко, т.к. 99% документации - на английском, а я не бессмертен. Подучи хотя бы минимально, будет очень круто и полезно, правда. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Привет! Исходя из [документации], функция CreateDevice должна вернуть D3D_OK в случае успеха. Это если я тебя правильно понял. Если не совсем правильно и это уже готовый адрес d3ddev, что (в моём понимании) является волшебством и магией, то попробуй обратиться к какому-нибудь методу - если не получится, значит что-то пошло не так. /*---------------------------------------------------------------------------*/
-
/*---------------------------------------------------------------------------*/ Итак! Первое, что нужно сделать - это открыть Visual Studio и создать пустой C++ проект. Дальше в него нужно добавить пустой файл исходника и в нём написать нечто такое: #include <windows.h> int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow ) { return 0; } Это - точка входа в приложение. Прямо сейчас она у нас возвращает ноль и программа на этом завершается. Вот картинка, на всякий случай: Идём дальше! Где-то на просторах MSDN мы натыкаемся на функцию [CreateWindow], которая, судя по имени, должна делать окно. Вот её описание: HWND WINAPI CreateWindow( _In_opt_ LPCTSTR lpClassName, _In_opt_ LPCTSTR lpWindowName, _In_ DWORD dwStyle, _In_ int x, _In_ int y, _In_ int nWidth, _In_ int nHeight, _In_opt_ HWND hWndParent, _In_opt_ HMENU hMenu, _In_opt_ HINSTANCE hInstance, _In_opt_ LPVOID lpParam ); Знает, смотрим на возвращаемый тип. Тип этот - HWND (почитать подробнее про типы данных можно [тут]) - Handle Window - дескриптор (идентификатор) окна. Теперь смотрим на аргументы (in - надо сообщить его фукнции, in_opt - надо сообщить, но не обязательно): LPCTSTR lpClassName - имя какого-то класса (?) LPCTSTR lpWindowName - имя создаваемого окна. DWORD dwStyle - какой-то стиль (??) int x - координаты левого int y - верхнего угла окна int nWidth - ширина окна int nHeight - высота окна HWND hWndParent - дескриптор окна-родителя, в случае если одно окно рожает другое. HMENU hMenu - меню (???) HINSTANCE hInstance - ещё один идентификатор. На этот раз - нашей программы, приехал нам в аргументах в WinMain(). LPVOID lpParam - скучная штука, описывать её не буду. ? - структура, в которой хранится всякая нужная окну фигня, типа иконки, курсора мыши и ещё одной вещи, о которой я напишу в следующем посте. ?? - если коротко, то у окна бывают всякие разные стили. Плоское окно, окно с заголовком, окно с кнопками закрыть-открыть, видимое окно или нет и так далее. ??? - указатель на структуру, описывающую меню окна. Ползём дальше. Функция упрямо требует от нас какой-то класс, которого у нас пока нет. Следовательно, его надо как-то сделать. Идём в гугл и выясняется, что есть функция, которая класс делает (а точнее, регистрирует), и имя ей - [RegisterClass]. Ей надо скормить структуру, описывающую класс окна (всякие важные и полезные для него штуки) и она вернёт "ОК" или "НЕ ОК". Делаем: #include <windows.h> LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam ) { return DefWindowProc( hwnd, message, wparam, lparam ); } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow ) { WNDCLASS wc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH ); wc.hCursor = LoadCursor( 0, IDC_ARROW ); wc.hIcon = LoadIcon( 0, IDI_APPLICATION ); wc.hInstance = hInstance; wc.lpfnWndProc = WndProc; wc.lpszClassName = TEXT("Test Window"); wc.lpszMenuName = 0; wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; if (RegisterClass(&wc)) return 1; return 0; } Здесь wc - это структура, описывающая класс окна. Функции RegisterClass мы скармливаем указатель на неё, предварительно заполнив её данными, и сверяем результат с единицей. Если равен, то возвращаем из программы 1, иначе возвращаем 0. Ты наверняка заметил вот эту странную строчку: wc.lpfnWndProc = WndProc; И одноимённую функцию в самом верху. Это - функция окна, в ней будет содержаться вся логика его работы. Подробнее будет дальше. Наконец, создаём окно: if (RegisterClass(&wc)) { HWND hwnd = CreateWindow("Test Window", "Test", WS_VISIBLE | WS_CAPTION | WS_SYSMENU , 0, 0, 640, 480, 0, 0, hInstance, 0); return 1; } Утверждаем, что окно будет класса "Test Window", с именем "Test", видимое, с заголовком и кнопками закрыть-свернуть-развернуть, находиться в верхнем левом углу экрана (0:0) и иметь размеры 640х480 пикселей, без родителя и без меню. Запускаем, проверяем - и ничего не происходит! Отлаживаем код, видим, что hwnd (идентификатор нашего созданного окна) не равняется нулю, значит окно создаётся, видим, что отладчик заходит в функцию самого окна (WndProc), но после этого программа просто завершается. Как так? А легко. Мы забыли о том, как устроены и работают окна. У каждого окна есть процедура, которая что-то делает. Следовательно, ей нужно откуда-то брать данные, чтобы что-то делать. Эти данные называются сообщениями. Скажем, нажали кнопку мыши. Нажали кнопку "закрыть". Нажали клавишу на клавиатуре. И нам нужно каким-то образом научить наше окно принимать и обрабатывать эти сообщения, пока оно не получит сообщение о закрытии. В этом нам поможет функция [GetMessage], которая как раз этим и занимается. Пишем: if (RegisterClass(&wc)) { HWND hwnd = CreateWindow("Test Window", "Test", WS_VISIBLE | WS_CAPTION | WS_SYSMENU, 0, 0, 640, 480, 0, 0, hInstance, 0); MSG msg; while( GetMessage( &msg, 0, 0, 0 ) != 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return 1; } Что тут происходит? После создания окна мы объявили структуру MSG, которая представляет собой пустое сообщение. Дальше мы лезем в бесконечный цикл, в котором принимаем сообщения, а затем запихиваем их в оконную процедуру (WndProc). В оконной процедуре следует написать следующее: LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam ) { switch( message ) { case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc( hwnd, message, wparam, lparam ); } Всякий раз попадая в функцию WndProc, мы получаем в аргументе сообщение. А дальше мы смотрим, что это за сообщение, и реагируем. В частности, выше я описал действие на WM_DESTROY - это когда мы закрываем окно. В ответ на это мы говорим, что пора прощаться, а дальше программа выходит из цикла обработки сообщений и уже тогда завершается. Картинка: Ну как? Получается что-нибудь? Вопросы есть? /*---------------------------------------------------------------------------*/
- 31 ответ
-
- 2
-