III. Берём скальпель.
Открываем наш файл (target.exe) в Olly Debugger:

У нас есть 2 варианта – так как места в секции кода почти всегда мало (в смысле
RawSize – размер в исполняемом файле), а размер в памяти
VirtualSize >>
RawSize, то в конце кода у нас есть достаточно большой нулевой блок. Мы можем поступить одним из двух способов – записать свой код поверх этих нулей, потом сделать дамп из памяти и получить то, что хотим, а можем заранее создать свою секцию и писать в неё. Что касается имеющейся секции данных, то там, как правило, место всегда есть, но если создавать свою секцию кода, то свою секцию данных создать – уже не проблема. А можно сделать её одновременно и секцией данных, разрешив писать в неё… Ну, в общем, всё по порядку.
IV. Подготовка к операции
Сейчас я пойду по второму пути (в первый раз, когда я делал такое, я шёл по первому, и… в общем, это было очень муторно. А если вы захотите всё-таки когда-то делать так (например, кажется, что код влезет в
RawSize, а он не влезает…), то вот вам мой совет: единственная сработавшая у меня связка – это OllyDump без восстановления импорта + ImpRec). Поэтому закрываем Олю и открываем наш файл в LordPE или PETools:
И идём в раздел “
Sections”:
Нажимаем правую кнопку на какой-нибудь секции, выбираем “
add section header”:
В конце списка появляется новая секция. Щёлкакем на ней правой кнопкой, выбираем “
edit section header”:
1) В поле
Name вводим какое-нибудь название секции, например “
.code”;
2) Поля
VirtualAddress и
RawOffset не трогаем – это адреса секции в памяти и в файле соответственно, вычисляются как
VirtualAddress = ((VirtualAddress(предыдущая секция) + VirtualSize(предыдущая секция) – 1) div VirtualAlign) + 1) * VirtualAlign;
(Перевожу на русский – наименьшее число, делящееся на VirtualAlign и большее последнего адреса предыдущей секции =) )
Аналогично считается и RawOffset, только вместо VirtualAlign стоит FileAlign.
Их LordPE считает автоматом, и менять их не надо! Кстати, VirtualAlign и FileAlign – тоже поля заголовка PE, должны быть степенями двойки, притом VirtualAlign >= 1000h; FileAlign >= 200h. Подробнее это рассмотрено в [1].
3)
VirtualSize и
RawSize.
А вот это нам и надо!
RawSize – размер секции в файле, а
VirtualSize – в памяти. Поставим RawSize = 1000 (ну уж 4кб нам под код хватит), а VirtualSize = 4000 (то есть 16кб). «Зачем нам такой большой VirtualSize?»,- спросите вы,- «Ведь если код в файле занимает 4кб, то зачем под него выделять целых 16?» Просто я хочу писать сюда не только код, но и данные, чтобы не создавать новую секцию, хоть это и не трудно. А помните, что мы хотели сделать? Да-да, загрузить http://ya.ru. А сколько он там весит? Не помню, но в 16кб, думаю, влезет…
4) А теперь редактируем флаги секции. Нажимаем на кнопочку рядом с полем Flags:
То, что нам нужно, уже отмечено – проверяем:
a) Что-то в секции можно выполнять
b) Из неё можно читать
c) В неё можно писать
d) В ней есть код
e) В ней есть инициализированные данные
f) В ней есть неинициализированные данные
А если что-то здесь лишнее, то это не смертельно.
Всё, жмём «ОК», потом, запомнив
VirtualOffset и
RawOffset нашей секции (
60000h и
59E00h), закрываем окошко с секциями. Зачем запоминать VirtualOffset? Всё дело в том, что есть в PE-заголовке такое поле, как
BaseOfCode. В нём сейчас записан VirtualOffset (всё, надоело, дальше вместо VirtualOffset буду говорить просто
RVA) секции «CODE», и Olly, которым мы будем пользоваться дальше, не будет
анализировать (потом об этом) нашу секцию, считая её чем-то странным и не понимая, как туда попала программа. А ориентируется он как раз на
BaseOfCode. Значит, пишем в
BaseOfCode запомненный
RVA. А RawOffset… В общем, скоро поймёте. Теперь смотрим на важный параметр
EntryPoint (Если не ясно, что это, то вам в [1]), запоминаем его значение (4CA98), и пишем туда (RVA нашей секции + 5). Почему +5, станет ясно потом. Теперь заголовок выглядит как-то вот так:
Всё, жмём Save, жмём “OK”, и пытаемся запустить файл. А он не запускается… И правильно! Секцию-то мы объявили, а записать – не записали! Открываем
target.exe во
FlexHex и идём в самый конец файла. Теперь ищем то место, где будет новая секция. А она начинается с запомненного нами RawOffset (назовём его
Raw =
59E00). Если последний существующий байт файла имеет адрес, отличный от (Raw-1), то забиваем нулями всё место от конца файла до этого числа (Щёлкаем после последнего байта в файле, далее
Edit->Insert Zero Block; Block Size = Raw - адрес последнего байта - 1. Калькулятор вам в помощь =)). Но нам этого делать не надо – у нас и так всё хорошо.
Теперь щёлкаем на пустой квадратик по адресу
Raw, далее
Edit->Insert Zero Block;
Block Size = размер нашей секции,
RawSize, то есть
1000h.
“ОК”, “Ctrl-S”, “Exit” =)
Всё, секция готова, пытаемся запустить файл… Ошибка…

Но уже «хорошая»! То есть наша программа запускается! Ура-ура! Это валидный PE-файл!