Доброго времени суток всем читателям.
В этой статье я попытаюсь наиболее понятным языком рассказать об основах редактирования клиента на примере удаления котов из него. Результат тут.
С чего начать? Такой вопрос возникает всегда. Давайте рассуждать. Нужно удалить кота из клиента. Как? Как кот вообще попадает в клиент? Когда мы подходим к коту (или к персонажу) на достаточное расстояние (рассматривать только ситуации, когда мы изначально не видели кота), он появляется на экране, следовательно сервер присылает пакет с информацией об игроке и клиент прорисовывает его. Как клиент отличает кота от игрока? Информация в пакете, правильно. С этим разобрались, проверим потом.
Пакет попал в клиент, дальше клиент его должен обработать и нарисовать нам кота (или игрока обычного). Значит нам нужно найти место в памяти, где клиент проверяет, пришёл пакет с котом или игроком, и немножко поменять это место.
План есть, приступим!
Начнём с определения флага "кота" в пакете. Тут первая проблема - кто не умеет пользоваться пандорой, тот вряд ли быстро разберется в этом. Поэтому тут я сразу выложу готовый результат:
Пакет 0x04 контейнера 0х00 Нет кота
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
Порядок байт обратный в маске, поэтому "00 10 00 00" == 0x00001000 или 0x1000 просто. Следовательно клиент должен как-то проверять маску. Поищем же.
При получении и обработке пакета, идёт хоть одно обращение к базовому адресу клиента (ведь клиент должен записать полученного игрока куда нужно). Запускаем клиент в два окна, заходим за двух персов, улетаем куда-нибудь в пустынное место (чтобы не было других игроков) и расходимся так, чтобы с первого перса не было видно второго перса. Открываем СЕ, подключаемся к первому процессу пв. Жмём
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
ПКМ по нему - Find Out What accesses this address и ждём. Сразу начнут
[Ссылки могут видеть только зарегистрированные пользователи. ]
появляться сотни адресов и клиент начнёт виснуть. Нужно дождаться, пока адреса перестанут появляться, при этом можно побегать в клиенте за первого, понажимать что-нибудь, но при этом поблизости никого не должно быть и никто не должен появиться. Когда адреса перестанут появляться, выделите
[Ссылки могут видеть только зарегистрированные пользователи. ]
последний адрес в этом списке, подойдите вторым окном к первому так, чтобы появиться в зоне видимости. В списке адресов тут же появится
[Ссылки могут видеть только зарегистрированные пользователи. ]
несколько новых адресов (или не несколько, смотря сколько ждали). Сразу жмите Stop. Лично у меня появилось около сотни лишних, но нас интересуют только те, что вызвались первыми и по одному разу (скрин выше). Скопируйте эти адреса в блокнот (на случай случайного закрытия окошка).
Итак, у нас есть адреса функций, который вызвались при появлении другого игрока. И еще мы знаем, что в одной из них должна быть проверка флага. Если кликнуть по адресу в окошке - Show disassembler, мы перейдём к самой функции. Мы знаем, что где-то должна идти проверка маски. Маска - 4 байта. Ассемблер любит сравнивать командами test или cmp. В нём вместо переменных регистры:
eax
ebx
ecx и другие.
Это 4-х байтовые регистры. Почитать можно [Ссылки могут видеть только зарегистрированные пользователи. ]. Видим примерно следующее:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Это означает следующее. Если процессор засунет нашу маску в регистр eax, то получится следующее:
eax = 00 00 10 00
ax = 10 00
ah = 10
al = 00
где ah, al - "четвертинки регистра", а ax - половинка. Для ebx аналогично, только не "а", а "b". Следовательно процессору выгоднее всего при сравнении маски пользоваться сравнением ah (bh или другие) с 10. После сравнения обычно идёт условный прыжок JE (Jump if Equal). А перед сравнением должно идти:
mov #, * (# - регистр eax, ebx, ecx или другой, а * - какой-либо адрес)
Следовательно мы ищем среди этих функций конструкцию:
Код:
mov eax, *адрес*
test ah, 10
je #адрес#
Повторюсь, вместо eax может быть любой другой регистр. Помимо этой конструкции, там должно быть много
Код:
test **, **
je **
так как проверка маски идёт не один раз.
Мне повезло, эта функция была пятой по счёту (чуть пролистать еще вниз) и выглядела вот так
[Ссылки могут видеть только зарегистрированные пользователи. ]
Небольшое отступление в ассемблер:
mov - команда копирования. Есть несколько видов:
1) mov *, #
Копирует из регистра # в регистр *.
Пример: mov eax, ebx - делает eax равным ebx
2) mov *, [#]
Читает значение по адресу # и копирует его в *
Пример: mov eax, [ebx]
3) mov[*], #
Вместо того, что лежит по адресу * ставит туда #
Пример: mov [eax], ebx
Итак, мы видим много раз повторяющуюся конструкцию mov eax, [ebp+16] (16 - это в [Ссылки могут видеть только зарегистрированные пользователи. ]'e, dec - 22). А теперь вернемся к нашим пакетам. Как видно
[Ссылки могут видеть только зарегистрированные пользователи. ]
, ровно на 22-м месте стоит маска. Значит мы на верном пути! Записываем в блокнот адрес начала функции (у меня
[Ссылки могут видеть только зарегистрированные пользователи. ]
Мало кто до сюда доживает. Но ведь здесь происходит самое интересное!
Нам известна функция, известно, что пакет (а точнее его кусок с информацией об одном персонаже) хранится по адресу ebp. Я предлагаю модифицировать код так, чтобы "у" координата обнулялась, если персонаж == кот. Приступим! (первое окно можно закрыть, бегающего твина лучше оставить)
Чтобы писать свой код в клиенте, нам нужно место. В бой вступает CFF Explorer. Запускаем, открываем наш ехешник, жмём слева Section Headers и видим следующее
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
пкм по пустому месту, Add Section (Empty space), размер 1000 - хватит, ок. Внизу появилась
[Ссылки могут видеть только зарегистрированные пользователи. ]
еще одна строчка (я назвал её .zhyk - начинаться должно с точки). Этот
[Ссылки могут видеть только зарегистрированные пользователи. ]
адрес нам понадобится. Сохраняем под новым именем. Итак, место для наших каляк-маляк у нас есть.
Теперь открываем Olly Debugger (далее оля), открываем
[Ссылки могут видеть только зарегистрированные пользователи. ]
в нём новый ехе'шник, переходим по адресу функции (Ctrl-G), найденному в конце 2-й части. У меня 00453D40. Если олька ругается так
[Ссылки могут видеть только зарегистрированные пользователи. ]
, просто допишите пару нулей перед адресом. Теперь нужно реализовать следующую схему:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Поэтому выбираем какое-нибудь место в функции (после push'ов всяких), я выбрал это
[Ссылки могут видеть только зарегистрированные пользователи. ]
. Переписываем в блокнот все эти инструкции (дабл-клик, копипаст). Вместо первой инструкции пишем
Код:
JMP 00C75000
где С75000 = адрес, который выписали из CFF Explorer'а + 0x400000 (почему именно 0х400000? это адрес образа файла при копировании его в память)
[Ссылки могут видеть только зарегистрированные пользователи. ]
адрес (нам ведь сюда нужно вернуться).
Теперь переходим на нашу память. Жмём Ctrl-G и тот адрес, на который мы прописали джамп. Ммм.. красота
[Ссылки могут видеть только зарегистрированные пользователи. ]
!
Пишем следующий код (построчно подряд, получится так
[Ссылки могут видеть только зарегистрированные пользователи. ]
)
Код:
PUSH EAX
MOV EAX,DWORD PTR SS:[EBP+16]
TEST AH,10
JE SHORT *наш адрес (который CFF+400000) плюс 10* (у меня получилось JE SHORT 00C75010)
MOV DWORD PTR SS:[EBP+8],0
POP EAX
LEA EDI,DWORD PTR SS:[EBP+4]
MOV DWORD PTR DS:[ESI+6E8],EAX
JMP 00453D5B
Эти строчки нужны для того, чтобы не потерять регистр eax и смысл начального кода. Так как мы его в дальнейшем будем использовать, то мы его сохраняем (командой push, в стеке), дальше используем его для своих делишек, потом вытаскиваем обратно. Две строчки после pop eax - это те строки, которые мы потеряли, когда прописали в основном коде jmp.
Этот джамп возвращает нас на тот адрес, который мы записали ранее (этот
[Ссылки могут видеть только зарегистрированные пользователи. ]
).
Рассмотрим оставшиеся 4 строчки отдельно.
Код:
MOV EAX,DWORD PTR SS:[EBP+16]
кратко mov eax, [ebp+16] - записываем в регистр (кому удобнее - переменную) eax значение по адресу ebp+16 (наша маска)
Код:
TEST AH,10
см. ниже
Код:
JE SHORT 00C75010 (мой адрес)
проверка на "кота" и если "не кот", то перепрыгнуть на команду pop eax(конкретнее)
если в test'е выше байты не совпали (ah AND 10 == 0), тогда происходит джамп на адрес команды pop eax
Код:
MOV DWORD PTR SS:[EBP+8],0
кратко mov [ebp+8], 0 - если предыдущий джамп не произошёл, то есть перс оказался котом, тогда мы устанавливаем y=0 (или z, везде по разному называется, хотя на самом деле у китайцев высота это у) Если еще конкретнее
[Ссылки могут видеть только зарегистрированные пользователи. ]
Теперь переходим обратно по адресу начальной функции (где писали jmp), выделяем наш код, подсвеченный красным, пкм, Copy to executable -> All modifications - так
[Ссылки могут видеть только зарегистрированные пользователи. ]
. Далее Copy all. В появившемся окошке пкм - Save file
[Ссылки могут видеть только зарегистрированные пользователи. ]
Планировалась короткая статья, но получилось что-то длинное
Давно, когда я был новичком, я перечитывал статьи TBX1n'а, Dinmaite'а, FreePvP, dwa83 и многих других десятки раз и каждый раз вычитывал что-то новое. А теперь я сам владею тем, чем могу поделиться с людьми в своих статьях. Надеюсь, что донёс до тебя, читатель, хоть какие-нибудь полезные мысли. Информация - это наиважнейший ресурс.
кратко mov eax, [ebp+16] - записываем в регистр (кому удобнее - переменную) eax значение по адресу ebp+16 (наша маска)
А что, если я тебе скажу, что эта команда записывает в аккумулятор четвёртый по счёту элемент стека(ну если от ebp отсчитывать)?
Цитата:
MOV DWORD PTR SS:[EBP+8],0
А эта пишет во второй элемент стека 0
________________
Принимаются пожертвования любых размеров в фонд поддержки начинающих программистов Для просмотра ссылок или изображений в подписях, у Вас должно быть не менее 10 сообщение(ий). Сейчас у Вас 0 сообщение(ий).
Я допустил грубую ошибку. Я забыл, что ebp - указатель на стек. push eax помещает в стек значение eax, следовательно ebp -= 4, и дальнейшее обнуление [ebp+8] обнуляет по факту [ebp+4] (если считать относительно начального ebp). Странно, что эта ошибка не всплыла (обнулялась координата x). Буду с пк - поправлю статью.
апдейт: А флаг вообще из левого места тогда читается. Странно, что это работает. Либо я спросонья запутался и всё в порядке. Обязательно проверю.
Паника отменяется. Push eax суёт eax в "другой" стек, за адрес которого отвечает esp, а не ebp. Почитав гугл, не особо понял разницу esp и ebp как стековых указателей.
________________
Больше не занимаюсь читами, ушёл в серверную часть. Новый ник - int 3.
P.S. Но я всё такой же добрый модератор раздела PW.
Последний раз редактировалось Smertig; 05.03.2014 в 20:53.
ESP отвечает за адрес вершины стека; вручную (умышленно, разработчиком взламываемого ПО) модифицируется редко, но часто команды sub esp и add esp встречаются в прологах/эпилогах функций.
EBP же - Base Pointer, часто содержит в себе указатели на что-нибудь, но лично я ни разу не видел особой специфики работы с этим регистром (совсем необязательно он содержит указатель на стек, лично я вообще его иногда использую как те же eax/edx/ecx).
UPD:
Кстати, добавлю про перехват управления в коде, в частности про 0xE9.
Если хотим пропатчить клиент с целью перехода с адреса X в адрес Y, то в адрес X записывается байт 0xE9 (jmp), а в следующие 4 байта, т.е. по адресу памяти X+1 запишется число Y-(X+5). Т.е. адрес назначения минус текущий адрес плюс размер команды jmp с операндами (5 байт).
Это я к тому, что можно и без дополнительных программ вычислить этот адрес, даже не проверяя через Olly.
P.S. Думал на днях запилить гайд по перехвату управления. Но даже не знаю, стоит ли игра свеч, ибо прошлые мои гайды особого успеха не поимели (пишу громоздко и максимально подробно). Интересно кому-нибудь?
________________
Принимаю реквесты на статьи, программы. Всё будет запилено в лучшем виде :3
Последний раз редактировалось BritishColonist; 07.03.2014 в 00:27.
ESP отвечает за адрес вершины стека; вручную (умышленно, разработчиком взламываемого ПО) модифицируется редко, но часто команды sub esp и add esp встречаются в прологах/эпилогах функций.
EBP же - Base Pointer, часто содержит в себе указатели на что-нибудь, но лично я ни разу не видел особой специфики работы с этим регистром (совсем необязательно он содержит указатель на стек, лично я вообще его иногда использую как те же eax/edx/ecx).
Вот за это спасибо.
Цитата:
UPD:
Кстати, добавлю про перехват управления в коде, в частности про 0xE9.
Если хотим пропатчить клиент с целью перехода с адреса X в адрес Y, то в адрес X записывается байт 0xE9 (jmp), а в следующие 4 байта, т.е. по адресу памяти X+1 запишется число Y-(X+5). Т.е. адрес назначения минус текущий адрес плюс размер команды jmp с операндами (5 байт).
Это я к тому, что можно и без дополнительных программ вычислить этот адрес, даже не проверяя через Olly.
Проще говоря - относительная адресация. Она очень удобна, когда джамп локальный и при перемещении куска кода ничего не нужно исправлять.
Цитата:
P.S. Думал на днях запилить гайд по перехвату управления. Но даже не знаю, стоит ли игра свеч, ибо прошлые мои гайды особого успеха не поимели (пишу громоздко и максимально подробно). Интересно кому-нибудь?
Гайды имели успех, и этим пользовался, а этидве темы вдохновили на написание своего яп. Пили гайд, обязательно
________________
Больше не занимаюсь читами, ушёл в серверную часть. Новый ник - int 3.
P.S. Но я всё такой же добрый модератор раздела PW.
Последний раз редактировалось Smertig; 07.03.2014 в 01:42.
EBP же - Base Pointer, часто содержит в себе указатели на что-нибудь, но лично я ни разу не видел особой специфики работы с этим регистром
Там должен быть указатель на начало сегмента стека, используется "по назначению" в случае необходимости работы с элементом стека, не вынимая все предыдущие, что мы, кстати, тут и видим
________________
Принимаются пожертвования любых размеров в фонд поддержки начинающих программистов Для просмотра ссылок или изображений в подписях, у Вас должно быть не менее 10 сообщение(ий). Сейчас у Вас 0 сообщение(ий).