-
Постов
17 -
Зарегистрирован
-
Посещение
-
Победитель дней
2
Тип контента
Профили
Форумы
Загрузки
Блоги
Сообщения, опубликованные bortolomeo
-
-
Chucky, ты, похоже, сам даже не пытаешься взломать игру, а лишь выпрашиваешь скрипты у тех, кто это делает. И неясно, с какой именно целью ты это делаешь.
Всем остальным: Отныне и впредь раскрытые скрипты во избежание их "воровоства" публикуем исключительно в разделе, доступ к которому имеют только участники группы "Разработчики".
Так а чего, вроде писал же чел про трутницы скрипт. А если скилов не хватает то что уж тут поделать, для того и форум.
-
Спасибо большое! А "Рассудок" ты взломал?
функция 46CAA0
рассудок берется здесь:
mov eax, dword_72836C
push esi
mov esi, ecx
mov ecx, [eax+84h]
fld dword ptr [ecx+68h]здоровье лежит рядом, 4-мя байтами раньше. тип 32 бит флоат.
можно было ставить точку останова на доступ к строку "все впорядке" или "в здравом уме" и смотреть по стеку откуда оно вызывалось
-
Для этого тебе нужна среда разработки. Как минимум либо Flex Builder, либо Flash Catalyst.
вот никогда не получалось скомпилировать декомпилированные игры :-(
-
Я бы и сам бы проверил написав тестируемый код. Да вот интересно, кто как думает, а то обычно по таким вещам (связанные с ООП и дизассемблированием) только мне одному доводится заморачиваться.
а вот и код:
//Определение классов и структур
struct SomeStruct
{
int classData;
void structMethod();
};
class SomeClass
{
int structData;
public:
void classMethod();
};
void SomeStruct::structMethod()
{
}
void SomeClass::classMethod()
{
}
void StructFunction(SomeStruct* testStruct)
{
}
void ClassFunction(SomeClass* testObject)
{
};
//Дизассемблер
testStruct->structMethod();
0040168A 8B 4D F4 mov ecx,dword ptr [ebp-0Ch]
0040168D E8 4E FE FF FF call SomeStruct::structMethod (4014E0h)
testObject->classMethod();
00401692 8B 4D E8 mov ecx,dword ptr [ebp-18h]
00401695 E8 76 FE FF FF call SomeClass::classMethod (401510h)
StructFunction(testStruct);
0040169A 8B 45 F4 mov eax,dword ptr [ebp-0Ch]
0040169D 50 push eax
0040169E E8 9D FE FF FF call StructFunction (401540h)
004016A3 83 C4 04 add esp,4
ClassFunction(testObject);
004016A6 8B 45 E8 mov eax,dword ptr [ebp-18h]
004016A9 50 push eax
004016AA E8 C1 FE FF FF call ClassFunction (401570h)
004016AF 83 C4 04 add esp,4Как видим, экземпляры классов и структур не отличаются ни в дизассемблере, ни в исходнике. (не считая ключевого слова public в определении класса).
это в MSVS 2008 без оптимизации. в других компиляторах с другими параметрами может быть и по-другому
- 1
-
хм... интересно.
Далее считаю объект - объектом, а структуру структурой (не объекта). Объект - экземпляр класса. Пусть у рассматриваемого объекта нет таблиц виртуальных функций и нет таблиц виртуальных классов. Нет метаданных подсказывающих про классы и т.п.
Если дизассемблерный код функции работает с указателем на ecx это ещё не значит, что ecx указатель на объект (но я не уверен, хотя всем известно что компилятор C++ по ecx передаёт указатель на объект). Если у объекта есть конструктор, то и у структуры данных может быть своя функция похожая на конструктор... По поводу "boost" я к не в курсе что это за зверь.
Программист мог хорошо взяться за оптимизацию и отказаться где-то от ООП заменив объект простыми структурами.
Интересно узнать следующее.
1) Как будет передаваться указатель на объект в функцию член в дизассемблере.
2) Как будет передаваться указатель на обычную структуру в функцию не принадлежащую никакому классу.
3) Будут ли какие-то отличия
Я бы и сам бы проверил написав тестируемый код. Да вот интересно, кто как думает, а то обычно по таким вещам (связанные с ООП и дизассемблированием) только мне одному доводится заморачиваться.
1) Если вызывается что-нибудь типа
someObject->member();
//то это будет
move ecx, someObj
call member
//ну это если наследования нет и всего такого прочего2) А если что нибудь типа
someClass* someObject;
function(someObject);
//то это будет
push someObject
call functionМежду структурами и классами различий не будет, потому как если я ничего не путаю, то структура - это тот же класс, только у нее поля по умолчанию public.
-
1. Почему ты считаешь что это "объект", а не структура? Какие отличия объекта от структуры? (каверзный вопрос с прицелом на второй)
В основном, конечно по вызову. Регистр ECX используется в функции без инициализации. Инициируется перед вызовом функции. Сейчас еще посмотрел откуда берется эта структура/объект.
push 8F0h
call sub_6BAD9B
add esp, 8
mov [ebp+var_14], eaxФункция sub_6BAD9B вызывается не один раз из разных мест. В дереве вызовов функции sub_6BAD9B находится функция HeapAlloc. В функцию sub_6BAD9B каждый раз передаются разные числа (и я уверен что это размер).
Я очень верю что это какой-то свой оператор new.
Потом идет функция которая инициализирует ту память которую мы выделили. И я очень верю что это конструктор.
Еще человек использует у себя boost, и я очень не верю что он все пишет на структурах.
Но что это функция-член, я конечно подумал когда увидел что в нее передается указатель на что-то в регистре ECX. Как то не встречал чтобы параметры передавались таки образом если не класс
-
Этот трейнер мне заказал какой-то странный араб. Он очень жаловался на то, что уже месяц играет в казуальную игру, и ему нужно срочно ее пройти и успокоится.
Для тех кто в нее играл (а таких, наверное, довольно много), вкратце опишу что она собой представляет, и какая задача передо мной стояла. А представляет она из себя обычный казуальный Match3 с двумя экранами: карта Рима, на которой можно строить различные строения и собственно match3 игра. Из отличий от остальных казуальных игр - на match3 экране есть бонусы, они становятся доступными после постройки определенных строений. У каждого бонуса есть энергия. Она пополняется, если соединить фишки соответствующие этому бонусу. Кроме того есть режим турнира, в котором нужно очистить ировое поле за заданное време. Вот на этом то режиме араб и застрял.
Итак, тренер должен:
1) устанавливать заданное количество ресурсов,
2) автоматически пополнять энергию у бонусов,
3) останавливать время в режиме турнира.
С чего бы начать? С поиска какого-нибудь простого значения с помощью Art of Money. Идеально для поиска подходит количество ресурсов (неважно каких). Они скорее всего хранятся в двойном беззнаковом слове. Их легко изменить в игре, чтобы отсеять мусор. Благодаря повериям казуальщиков, это достаточно большое число, и мусора будет не слишком много.
Ищу и отсеиваю. Отсеиваю и ищу. После второй же итерации, остаются 3 ячейки памяти, в которых ничего не меняется.
Теперь в дело вступает OllyDbg. Устанвливаю точку останова на чтение всех трех найденных ячеек. На какой первой остановится, там и здорово. Подкидываю золотишка. Остановилось. Здорово. Смотрю на код.
А в коде происходит следующее: сначала считывают текущее количество золота(eax) из массива(edx) по заданному индексу (ecx). Затем прибавляют аргумент функции ([ebp+0xc]), и полученное значение записывают обратно. Смотрю откуда берется массив. Массив берется из [ecx+0x7F0]. Смотрим что такое ecx (кроме того что регистр, конечно :-)). А ecx у нас берется из локальной переменной (ebp-78), в которую он сохраняется в самом начеле функции. Значит функция - член класса. Ecx - указатель на this. Для лучшего понимая происходящего пишу псевдокод:
void Game::AddResources (DWORD increment, DWORD resourceIndex)
{
DWORD currentResource;
currentResource = this->resources[resourceIndex];
currentResource += increment;
this->resources[resourceIndex] = currentResource;
}Теперь нужно найти где же хранится указатель на объект. Открывают стек вызовов и поднимаюсь на уровень выше, в функцию откуда вызвали добавление ресурсов.
Тут небольшая последовательность присвоений.
mov edx, [0xA5ABB0]
mov eax, [edx]
mov [ebp-0xC4], eax
mov ecx, [ebp-0xC4]И получается отлично! Указатель на игру хранится в глобальной переменной, а значит я в любой момент времени могу его вычислить. Опять пишу псевдокод, что делать когда у меня будет указатель.
DWORD* GlobalPointer = (DWORD*) 0xA5ABB0;
BYTE* GamePointer = GlobalPointer[0];// это чтобы со смещением не путаться
DWORD* ResourcesPointer = GamePointer[0x7F0];
ResourcesPointer[resourcesId] = newResource;Добавляю поочередно ресурсы. Смотрю на изменение значений в массиве (дамп OllyDbg). Составляю табличку смещений массива:
int MONEY_OFFSET = 1; //Золото
int WOOD_OFFSET = 2; //Ресурсы
int int FOOD_OFFSET = 3; //Еда
int SCORE_OFFSET = 4; //ОчкиПопутно отмечаю, что рядом в дампе меняются и другие значения. После пары-тройки несложных экспериментов выясняю что:
int LIFES_OFFSET = 5; // жизни
int LEVEL_OFFSET = 6; // уровеньОбращаю внимание на ряд единичек и нолей. Они обязательно должны что-то означать. Создаю новый профиль в игре, начинаю заново. И точно, означают. Доступен или не доступен бонус. Значит где-то здесь есть и заряд бонусов. Есть. Такой же ряд байт, минимум 0 (разряжен), максимум 0xC4 (заряжен). Странный выбор максимума, кстати.
Пишу таблицу для бонусов.
int HAMMER = 0x0;
int BOMB = 0x1;
int FIREBALL = 0x2;
int HOURGLASS = 0x3;
int LIGHTNING = 0x4;
int MEGABOMB = 0x5;
int MIXER = 0x6;
int STAR = 0x7; //Всякие бонусы
int BONUSAMOUNT_OFFSET = 0x4C; //Смещение для заряда
int BONUSENABLED_OFFSET = 0x7A; //Смещение для индикатора доступностиС этим понятно. Перехожу в режим турнира. Смотрю на память в дампе, и нахожу бонусы для турнира.
int TOURNBONUSAMOUNT_OFFSET = 0x164; //Смещение для заряда в турнире
int TOURNBONUSENABLED_OFFSET = 0x192; //Смещение для индикатора доступности в турниреПоловина пути пройдено. Остается найти как остановить таймер в турнире, и спасти несчастного араба от игровой зависимости.
Ищу стандартные функции для получения времени, ставлю на них точку останова, и обнуляю возвращаемый результат. Добиваюсь того, что нашел нужную функцию, после которой игра полностью замирает. Двигаюсь после нее по коду, заменяя отдельные функции и условные переходы Nop-ами. Игра не единожды падает, но наконец-то я нахожу нужный участок кода.
Заменяю JNZ на JMP и получаю результат! Таймер стоит, побочных эффектов не обнаружено. Конечно, патчить код не так здорово как данные, но разбирать дальше что происходит нет ни желания, ни времени. И так ведь работает :-).
- 1
-
Герои меча и отладчика
Сначала небольшая предыстория. 7 лет назад, я вернулся из магазина с диском 4-х героев. Ощущения от них были двоякие: с одной стороны неплохие находки в плане геймплея, интересные кампании. С другой – непривычное графическое оформление, высокие (для моей машины в то время) системные требования, и, конечно же, ошибки. Их было много, но одна была особенно критичной.
Если зайти в своем замке в тюрьму, в которой содержатся герои, программа вылетала в систему. Это случилось когда я победил особо сильного соперника с раскачанным героем. Была надежда на то, что в тюрьме пытками и подкупом можно склонить его на свою сторону, ан нет. В общем диск полетел в стол, где был благополучно забыт.
Сегодня у выдалось свободное время. Страдая бездельем, я решил разобрать вещи в столе, и выкинуть ненужное. Наткнулся на забытый диск, и уборку пришлось отложить. Забыл о былых обидах, и погрузился в мир Аксеота. Почти прошел первый сценарий кампании, и надо же было опять влезть в тюрьму с чужим героем. Игра закрылась, а у меня появилось занятие.
За 7 лет я несколько поумнел, и узнал про существование отладчика. Открыв игру во всеми любимом отладчике OllyDbg, и повторив последовательность действий для выявления ошибки увидел следующую картину:
Увидел, что вызывается функция, которая возвращает указатель на какой-то объект. Функция возвращает 0. Далее программа пытается что-то читать, и вызывается исключение нарушения доступа к памяти.
Затем я решил просмотреть функцию, которая возвращает указатель. Это метод какого-то класса, куда передается строка. Класс – контейнер. В контейнере находятся объекты со строками. Объекты упорядочены по возрастанию строк. Если упрощенно, то в нем с помощью алгоритма бинарного поиска ищется объект, в котором присутствует заданная аргументом строка. Если элемент найден, то возвращается указатель на него, если нет - 0 (если я ни в чем не ошибся, конечно). Судя по строкам, которые ищут, то это картинки для GUI. Строка на которой все вылетает – «background».
На скриншоте отображена часть, которая возвращает значение из функции. Поставил breakpoint на инструкцию xor eax, eax чтобы посмотреть как часто функция возвращает нулевой указатель, и как этот указатель в дальнейшем обрабатывается. В результате я выяснил, что ноль возвращается довольно часто. Просмотрев перекрестные ссылки на эту функцию, обнаружил что чаще всего указатель проверяют. Например таким образом:
Места чтобы добавить проверку на ноль и оператор условного перехода не было. Делать DLL Injection ради этого не сильно хотелось. Поэтому попробовал сделать все тупо, быстро и опасно. Места, где идет обращение к этому указателя забить NOP-ами:
Получилось как-то не слишком изящно. Зато быстро и ненапряжно. Может и прокатить. Запустил игру и зашел в тюрьму. В систему не вылетает, исключений нет. Пленного героя показывает. А радости-то и никакой. Герой, как и подобает настоящему герою, оказался неподкупным. И тут я сделал то что надо было сделать с самого начала: поискал информацию про тюрьму в гугле. Обнаружилось 2 вещи. Во-первых, тюрьма не нужна и непонятно почему я вообще подумал что там можно подкупить вражеского героя. Во-вторых, есть патч, который исправляет эту ошибку. Зато было интересно, как надеюсь и вам при прочтении этой статьи.
- 2
Амнезия. Призрак прошлого
in Вопросы по созданию читов в одиночных играх
Опубликовано
ну вот, что-то мне говорит что если ты посмотришь дебаггером, то здесь
ecx, edi, eax будут указывать на один и тот же адрес, и будут являться указателями на объект (или если кому угодно структуру) игрока. Если там посмотреть там на память, то найдешь все что тебе надо.