Автор Тема: Вопросы по замене ассемблерных инструкций в asi.  (Прочитано 6057 раз)

Оффлайн graveman

  • Прохожий
  • *
  • Сообщений: 50
  • Репутация: +2/-0
  • Its cool! Man
    • Просмотр профиля
Всем привет. Как заменить вызов call на nop, чтобы определенная функция не вызывалась?
Как правильно поменять передаваемое в push целочисленное значение аргумента?

Оффлайн Sektor

  • Администратор
  • Постоялец
  • *****
  • Сообщений: 514
  • Репутация: +33/-0
    • Просмотр профиля
Цитировать
Как заменить вызов call на nop, чтобы определенная функция не вызывалась?


Приведите конкретный пример, какую бы вы хотели функцию затереть. На данный момент скажу, что обязательно надо затереть сами ее передаваемые параметры и затем саму функцию с адресом. При этом опять вы должны установить память на чтение/запись.

Цитировать
Как правильно поменять передаваемое в push целочисленное значение аргумента?

Это совсем не сложно. Для этого надо знать размер самого параметра, затем задать в VirtualProtect атрибут как чтение записать, записать и готово.

Метод точно такой же как в этих статьях:

Чтение и запись адресов GTA Vice City
Чтение и запись адресов GTA Vice City часть 2

Единственно это нужно будет проделать в командах push, обычно которые заталкивают параметры в стек, перед вызовом.

Оффлайн graveman

  • Прохожий
  • *
  • Сообщений: 50
  • Репутация: +2/-0
  • Its cool! Man
    • Просмотр профиля
Цитировать
Приведите конкретный пример, какую бы вы хотели функцию затереть.
Например, в функции "Render" хочу попробовать затереть вызов функции "sub_5C1710".

Оффлайн Sektor

  • Администратор
  • Постоялец
  • *****
  • Сообщений: 514
  • Репутация: +33/-0
    • Просмотр профиля
Значит поступаем следующим образом. Находим то место, где идет вызов данной процедуры, и затем я выделил то что нам надо будет затереть. Так как параметры не передаются, нам нужно затереть саму команду call и ее адрес:



Команда NOP - имеет значение опкода 0x90.

Дальше все просто:

Код: C++
  1. case DLL_PROCESS_ATTACH:
  2.                
  3.                 DWORD _old;
  4.                 VirtualProtect((LPVOID)0x04A6594, 5,  PAGE_READWRITE, &_old);
  5.  
  6.                 *(BYTE*)0x04A6594 = 0x90;  //E8
  7.                 *(BYTE*)0x04A6595 = 0x90;  //77
  8.                 *(BYTE*)0x04A6596 = 0x90;  //B1
  9.                 *(BYTE*)0x04A6597 = 0x90;  //11
  10.                 *(BYTE*)0x04A6598 = 0x90;  //00
  11.  
  12.                 VirtualProtect((LPVOID)0x04A6594, 5, _old, &_old);

Прописываем это в dll, т.е в ASI плагине. Уроки были про DLL_PROCESS_ATTACH. Этот код выполниться тогда когда dll будет загружена. Судя по коду, сперва мы меняем по нужному нам адресу, атрибут памяти на чтение/запись. Затем уже не посредственно вписываем наши NOP-ы, по нужным нам адресам. Это те самые адреса, что я выделил на картинки. После возвращаем данный участок памяти в исходное положение.

Оффлайн graveman

  • Прохожий
  • *
  • Сообщений: 50
  • Репутация: +2/-0
  • Its cool! Man
    • Просмотр профиля
Отлично, спасибо. Надо будет попробовать. А я задавал смещения *(patch+1), *(patch+2)...
Цитировать
Уроки были про DLL_PROCESS_ATTACH
Да, с написанием asi-плагина я освоился, благодаря твоим статьям. Я просто переименовываю расширение релизной библиотеки с "dll" на "asi" и копирую этот asi-плагин в папку "mss".

Sector, а как заменить "mov eax, dword_xxx" на "mov eax, int", то бишь засылку переменной засылкой явно заданным целым числом? То же 5 байт поменять, как приведено выше - первый на код операции, второй на число, а остальные на ноль?
« Последнее редактирование: Ноябрь 27, 2013, 12:58:41 pm от Sektor »

Оффлайн Sektor

  • Администратор
  • Постоялец
  • *****
  • Сообщений: 514
  • Репутация: +33/-0
    • Просмотр профиля
Цитировать
Отлично, спасибо. Надо будет попробовать. А я задавал смещения *(patch+1), *(patch+2)...
Можно и так.  Так как значения одинаковые 0x90, то можно вообще распределить таким образом

Код: C++
  1. memset((PVOID)0x04A6594, 0x90, 5);

Это будет тогда выглядеть более компактно, в одну строчку.

Цитировать
Да, с написанием asi-плагина я освоился, благодаря твоим статьям. Я просто переименовываю расширение релизной библиотеки с "dll" на "asi" и копирую этот asi-плагин в папку "mss".

Да по сути одна и та же DLL, переименовать расширение это проще простого. На данный момент я приболел да еще и дел полно, как только выздоровлю и будет свободное время, так напишу продолжение пошаговых статей.

Цитировать
"mov eax, dword_xxx" на "mov eax, int"
Типы на уровне ассемблерных команд, особой роли не играют , за исключением float. Если говорить о разных размерах, то обычными правками не обойтись. Для этого уже нужно переписывать саму функцию и затем заменять, путем как инжектом. Приводите конкретный пример, что и где именно вы хотите заменить с адресами. Мне так лучше будет видно.

Оффлайн graveman

  • Прохожий
  • *
  • Сообщений: 50
  • Репутация: +2/-0
  • Its cool! Man
    • Просмотр профиля
Да, приведу код, если не удастся решить, просто исходник дома.
Цитировать
Если говорить о разных размерах, то обычными правками не обойтись. Для этого уже нужно переписывать саму функцию и затем заменять, путем как инжектом.
Я вот про инжект не понял, зачем записывается "function - (address + 5)", а не просто "function" и что означает "5"?
Цитировать
На данный момент я приболел
Выздоравливай.
« Последнее редактирование: Ноябрь 26, 2013, 01:12:11 pm от graveman »

Оффлайн Sektor

  • Администратор
  • Постоялец
  • *****
  • Сообщений: 514
  • Репутация: +33/-0
    • Просмотр профиля
Цитировать
Я вот про инжект не понял, зачем записывается "function - (address + 5)", а не просто "function" и что означает "5"?
Об, этом я расскажу как освобожусь, более в деталях и подробно.

Оффлайн graveman

  • Прохожий
  • *
  • Сообщений: 50
  • Репутация: +2/-0
  • Its cool! Man
    • Просмотр профиля
Пытался выводить в лог значения некоторых переменных через SetTimer(...Update) в теле Update(). Похоже, что идет вызов из определенного места кода, так как переменные должны быть явно больше нуля (при выводе большое отрицательное число), так как из использования их в качестве аргумента в функции RW ясно, что это счетчики вершин.
Вопрос - нельзя ли как-то применить gta_log или вызвать свой Log в теле экзешника?
Я пробовал обрывать цикл в одной из функций обнулением счетчика в for{} - в игре при этом становились полупрозрачными некоторые стены и детали автомобилей (лонжероны, капот).


Такой вопрос. Правильно ли я начал менять код. Есть проверка переменной на нуль через cmp. Я смотрю там 7 байт. В то время как test eax, eax занимает всего 2 байта. При том эта переменная потом все равно заносится в eax. Что если заменить cmp на засылку переменной в eax с последующим test eax, eax,  jz      short loc_5C2935 переписать на адрес сразу за test eax, eax. Останется 5 свободных байт, достаточных для вызова call или jump near. Вообщем, картинка для пояснения:

Можно ли на освободившиеся 5 байт прописать call для своей функции из dll?
Короче дома попробую.


Sector, я кстати нашел ответ на вопрос:
Я вот про инжект не понял, зачем записывается "function - (address + 5)", а не просто "function" и что означает "5"?


Источник: http://habrahabr.ru/post/90377/
Цитата из него:
Цитировать
Итак, теперь мы разобрались, как происходит вызов функции и готовы к ее перехвату. Осталась одна деталь – а как же собственно сделать перехват?

Для этого, все что нам нужно – это поставить в начало функции инструкцию jmp с адресом, который будет указывать на начало нашей функции. Однако, не все так просто. Дело в том, что инструкция jmp, которая бы принимала абсолютный адрес нашей функции, размером 5 байт просто не существует. Единственный jump, который работает с абсолютными адресами – это jump far, который занимает 6 байт.

Поэтому, мы будем использовать jump near, который принимает относительный адрес (то есть разность между адресом точки назначения и следующей за jump near инструкцией). По факту, для вычисления параметра операции jump near, надо из адреса точки назначения вычесть адрес исходной точки и прибавить 5 байт (именно столько эта инструкция занимает).


PS: можно статью по замене функций как бы дополнить этим.
« Последнее редактирование: Ноябрь 27, 2013, 01:00:19 pm от Sektor »

Оффлайн Sektor

  • Администратор
  • Постоялец
  • *****
  • Сообщений: 514
  • Репутация: +33/-0
    • Просмотр профиля
По поводу инжекст функции, я думаю лучше отдельно потом статью написать. Потому-что многим и так тяжело все читать и понимать.
gta_log, есть такой плагин, я его прикрепил сюда, вместе с исходником. Автор его Listener.


Цитировать
Такой вопрос. Правильно ли я начал менять код.

Скриншота не вижу.

Оффлайн xanser

  • Главный Модератор
  • Постоялец
  • *****
  • Сообщений: 522
  • Репутация: +59/-0
  • Есть такая профессия - на работе сидеть
    • Просмотр профиля
Ууу, зря я сюда заглянул, это из другой сказки  :-\

Оффлайн graveman

  • Прохожий
  • *
  • Сообщений: 50
  • Репутация: +2/-0
  • Its cool! Man
    • Просмотр профиля
Вот скрин, походу у меня в кэше. Кстати как картинки постить?

Оффлайн Sektor

  • Администратор
  • Постоялец
  • *****
  • Сообщений: 514
  • Репутация: +33/-0
    • Просмотр профиля
Цитировать
Можно ли на освободившиеся 5 байт прописать call для своей функции из dll?

Можно, но для этого нужно еще учитывать, используются параметры или нет. Тогда просто call - 1 байт, 4 байта относительный адрес, не примой, но функции. Вообще скажу прямо, что такими методами не рекомендую заниматься, такими правками. Они не эффективны и в дальнейшем не подлежат для хороших модификаций. Рекомендую только переписывать все функции в сорцы, и делать потом инжекты. Об этом я тоже хочу в будущем статьи написать, методики переписывания...  Объясню почему, одними ассемблерными правками, не обойтись, второе логика всего будет утеряна, когда все лучше разрабатывать на C++. В результате если работать таким методом, то к хорошему в будущем это не приведет. Надо просто брать переписывать функции, изначально можно объявлять прототипы по адресам.
« Последнее редактирование: Ноябрь 27, 2013, 03:00:23 pm от Sektor »

Оффлайн graveman

  • Прохожий
  • *
  • Сообщений: 50
  • Репутация: +2/-0
  • Its cool! Man
    • Просмотр профиля
Я это делаю для изучения переменных и не найденных еще функций. Не для мода. Та переменная засветилась во многих функциях рендера и связана с количеством каких-то вершин.

Оффлайн Sektor

  • Администратор
  • Постоялец
  • *****
  • Сообщений: 514
  • Репутация: +33/-0
    • Просмотр профиля
Цитировать
Я это делаю для изучения переменных и не найденных еще функций. Не для мода. Та переменная засветилась во многих функциях рендера и связана с количеством каких-то вершин.

Эту глобальную не проинициализированную переменную можно найти во всех местах где она используется, нажав на нее кнопку "X". Изначально надо цепляться за то, откуда идет первая ей запись. Вроде бы равна к нулю. Возможно это какое-то состояние, пока что и сам точно не знаю. В рендере еще не копался, начал с самого простого.