В [пятом уроке] (и в четвёртом тоже ^^) мы рассматривали такую технику, как Code-Injection, или, если буквально, "инъекция кода". Давайте остановимся на этой штуке ещё немного - больно уж часто она используется. Больше скажу - одна из самых распостранённых техник взлома. :)
Итак. Представим, что имеется у нас вот такой гипотетический код игры:
Адрес | Команда | Комментарий
---------------------------------------------------------------------------------
0000000: mov eax, 3 //помещаем в регистр eax число "3"
0000001: sub eax, 1 //вычитаем из еах единичку
0000002: cmp eax, 0 //сравниваем еах с нулём
0000003: je "exit" //если еах = 0, то выходим из игры
0000004: add eax, 2 //иначе прибавляем к еах двойку
0000005:
0000006:
0000007:
0000008:
0000009:
---------------------------------------------------------------------------------
Скажем, нашли мы нужный адрес в Cheat Engine, включили отладчик дабы посмотреть - а с чего это у нас заканчиваются жизни (которые, как вы поняли, в еах находятся), отладчик нам выплёвывает адрес 0х00000001.
Если мы хотим, чтобы жизни просто не убавлялись, мы заменяем команду "sub eax, 1" nop'ами - командами, которые ничего не делают. Почему я написал во множественном числе? Всё просто - одна команда "nop" занимает один байт места, а вот "sub eax, 1" места занимает чуток побольше. Точные значения нас сейчас не волнуют (их можно посмотреть в отладчике), скажем, что это четыре байта. Поэтому вместо:
sub eax, 1
Мы должны будем написать четыре команды "nop", по одной на каждый байт:
nop
nop
nop
nop
Если не соблюдать это правило (количество байт кода всегда неизменно) - получите синий экран и придётся делать заново.
Идём дальше. В чём суть техники Code-Injection? Мы берём какую-то команду (в данном примере - ту, что вычитает у нас жизни) и заменяем её на команду jmp - прыжок или переход. Там выполняем какой-то код, затем возвращаемся обратно - в код игры. Смотрите:
Адрес | Команда | Комментарий
---------------------------------------------------------------------------------
0000000: mov eax, 3 //помещаем в регистр eax число "3"
0000001: jmp [0000006] //переходим ("прыгаем") на адрес 0x0000006
0000002: cmp eax, 0 //сравниваем еах с нулём
0000003: je "exit" //если еах = 0, то выходим из игры
0000004: add eax, 2 //иначе прибавляем к еах двойку
0000005:
0000006: add eax, 10 //добавляем к еах десять
0000007: jmp [0000002] //прыгаем обратно - на адрес 0x0000002
0000008:
0000009:
---------------------------------------------------------------------------------
В результате этих действий мы заставили игру выполнить наш код (находящийся под адресам 0х0000006-0х0000007) а затем вернуться обратно и продолжить свой. Что нам это даёт? На самом деле - что угодно, всё зависит от фантазии и навыков программирования. Можно добавить жизней, можно патронов, можно всех врагов убить, можно любимой бабушке привет передать - в общем, чего душа пожелает, а дальше игра послушно вернётся к своим делам. :)
Почему делать нужно именно так? Потому что про количество байт и длину кода я писал не просто так - мы не можем уместить тысячу байт кода в размеры одной трёх-четырёхбайтовой команды - поэтому засовываем код в отдельный участок памяти, обращаемся к нему, а затем продолжаем исполнение игрового кода. Прелесть в том, что код всегда будет находиться в одном и том же месте - адреса меняться не будут. Такая вот загогулина. Использовать этот не слишком хитрый приём мы будем ещё очень много раз, так что понимание успеет придти. Если же нет - пишите, задавайте вопросы. :)
Итак. Представим, что имеется у нас вот такой гипотетический код игры:
Адрес | Команда | Комментарий
---------------------------------------------------------------------------------
0000000: mov eax, 3 //помещаем в регистр eax число "3"
0000001: sub eax, 1 //вычитаем из еах единичку
0000002: cmp eax, 0 //сравниваем еах с нулём
0000003: je "exit" //если еах = 0, то выходим из игры
0000004: add eax, 2 //иначе прибавляем к еах двойку
0000005:
0000006:
0000007:
0000008:
0000009:
---------------------------------------------------------------------------------
Скажем, нашли мы нужный адрес в Cheat Engine, включили отладчик дабы посмотреть - а с чего это у нас заканчиваются жизни (которые, как вы поняли, в еах находятся), отладчик нам выплёвывает адрес 0х00000001.
Если мы хотим, чтобы жизни просто не убавлялись, мы заменяем команду "sub eax, 1" nop'ами - командами, которые ничего не делают. Почему я написал во множественном числе? Всё просто - одна команда "nop" занимает один байт места, а вот "sub eax, 1" места занимает чуток побольше. Точные значения нас сейчас не волнуют (их можно посмотреть в отладчике), скажем, что это четыре байта. Поэтому вместо:
sub eax, 1
Мы должны будем написать четыре команды "nop", по одной на каждый байт:
nop
nop
nop
nop
Если не соблюдать это правило (количество байт кода всегда неизменно) - получите синий экран и придётся делать заново.
Идём дальше. В чём суть техники Code-Injection? Мы берём какую-то команду (в данном примере - ту, что вычитает у нас жизни) и заменяем её на команду jmp - прыжок или переход. Там выполняем какой-то код, затем возвращаемся обратно - в код игры. Смотрите:
Адрес | Команда | Комментарий
---------------------------------------------------------------------------------
0000000: mov eax, 3 //помещаем в регистр eax число "3"
0000001: jmp [0000006] //переходим ("прыгаем") на адрес 0x0000006
0000002: cmp eax, 0 //сравниваем еах с нулём
0000003: je "exit" //если еах = 0, то выходим из игры
0000004: add eax, 2 //иначе прибавляем к еах двойку
0000005:
0000006: add eax, 10 //добавляем к еах десять
0000007: jmp [0000002] //прыгаем обратно - на адрес 0x0000002
0000008:
0000009:
---------------------------------------------------------------------------------
В результате этих действий мы заставили игру выполнить наш код (находящийся под адресам 0х0000006-0х0000007) а затем вернуться обратно и продолжить свой. Что нам это даёт? На самом деле - что угодно, всё зависит от фантазии и навыков программирования. Можно добавить жизней, можно патронов, можно всех врагов убить, можно любимой бабушке привет передать - в общем, чего душа пожелает, а дальше игра послушно вернётся к своим делам. :)
Почему делать нужно именно так? Потому что про количество байт и длину кода я писал не просто так - мы не можем уместить тысячу байт кода в размеры одной трёх-четырёхбайтовой команды - поэтому засовываем код в отдельный участок памяти, обращаемся к нему, а затем продолжаем исполнение игрового кода. Прелесть в том, что код всегда будет находиться в одном и том же месте - адреса меняться не будут. Такая вот загогулина. Использовать этот не слишком хитрый приём мы будем ещё очень много раз, так что понимание успеет придти. Если же нет - пишите, задавайте вопросы. :)
Комментариев нет:
Отправить комментарий
Не люблю мат и низкий уровень грамотности. Чем конкретнее поставите свой вопрос и чем лучше он будет выглядеть - тем большая вероятность на мой ответ. :)