Чего ж ты до этого момента терпел с вопросами, если не понял, как оно работает? Давай на примере посмотрим. Есть у тебя вот такой участок игрового кода: 0x100: nop 0x101: nop 0x102: nop 0x103: nop 0x104: nop 1 инструкция nop = опкод 0x90 = 1 байт памяти. Итого 5 байт. Нам нужно сделать так, чтобы игра, дойдя до адреса 0x100, выполнила наш код, длина которого больше 5 байт, а затем вернулась обратно. Как это сделать? Прежде всего, нужно куда-то записать код, который наш, чтобы игра могла его выполнить. Для этого нужно выделить участок памяти. Допустим, что длина нашего кода будет 10 байт. Дальше я буду писать в псевдокоде: allocated_mem = VIrtualAllocEx(10) После выполнения видим, что allocated_mem присвоилось значение 0x110. Это для примера. Итого получаем: 0x100: nop 0x101: nop 0x102: nop 0x103: nop 0x104: nop ... 0x110: <- отсюда лежит 10 байт выделенной нам памяти, в которую мы можем писать 0x111 0x112 0x113 0x114 0x115 0x116 0x117 0x118 0x119 Так, память выделили, теперь нужно научить игру переходить на нужный нам адрес. Для этого нам понадобится команда безусловного перехода - jmp. Она прыгает на относительный адрес, а не на абсолютный. Показываю, опять же, на примере: 0x10: my_code ... 0x15: jmp my_code Вместо "my_code" по адресу 0x15 подставится -5. То есть на пять байт выше относительно текущего адреса (15-5 = 10). Прыжки вниз по коду - то есть вперед - прибавляют нужное смещение от текущего адреса. Смотрим еще раз на код игры и выделенную память. Код игры - 0x100, выделенная память - 0x110. То есть нам нужно прыгнуть ниже по коду, а значит вместо игрового кода по адресу 0x100 записать: 0x100: jmp 10 То есть на 0x110, на 10 байт вперед. Итого управление передастся на наш код, но нам нужно будет вернуться обратно. Чтобы вернуться обратно, нам понадобится еще один jmp и адрес, куда прыгать. Опкод команды jmp занимает 1 байт, адрес - 4 байта, итого 5. Так что для нашего кода нам остается 5 байт, ибо всего у нас их 10. То есть в выделенной памяти по адресу 0x115 нам нужно записать "jmp адрес", где адресом будет игровой код, откуда мы прыгнули, +5 байт. Почему +5? Вот почему: Игровой код: 0x100: jmp 10 0x105: ... jmp 10 занимает 5 байт. Нам нужно продолжить выполнение с команды, идущей сразу после нее, то есть 0x105. Если прыгнем опять на 0x100 - получится, что прыгнули снова на jmp 10, в итоге получится рекурсия и игра зависнет. В общем, нам нужно сделать jmp с адреса 0x115 наверх, на 0x105. Итого: 0x115: jmp -10 У нас есть адрес игрового кода (game_addr), адрес, куда нужно прыгнуть (game_addr + 5), адрес нашей выделенной памяти (allocated_mem), ее размер (10 байт) и адрес, откуда прыгать (allocated_mem + 10 - 5). Считаем, получается, что адрес, куда нужно прыгнуть, это 0x105, адрес, откуда прыгать - 0x110+10 - 5 = 0x115, вычитаем, получаем 110 - 115 = -10. Что дает нам: 0x100: jmp 0x1100x105: <- сюда прыгнем обратно...0x110: <- сюда прыгнем из игрового кода0x111:0x112:0x113:0x114:0x115: jmp 0x105 <- отсюда прыгнем обратно И вот, у нас остается еще целых 5 байт для кода, который выполнит игра вместо своего оригинального. Берем WriteProcessMemory и записываем 5 байт по адресу нашей выделенной памяти, то есть 0x110. Как вернуть все на место? Скажем, когда человек нажал кнопку, чтобы функцию трейнера отключить. У нас есть адрес игрового кода, адрес нашей выделенной памяти, и оригинальный игровой код, который мы заблаговременно сохранили перед его изменением. Алгоритм простой: 0. По адресу 0x100 (игрового кода) используя WriteProcessMemory записываем оригинальный игровой код.1. Очищаем выделенную нами память через VirtualFreeEx. Вот как-то так и работает инъекция кода в Cheat Engine. Единственное, каждый вызов WriteProcessMemory лучше оборачивать в Virtual(Un)ProtectEx.