Общие сведения
При внедрении как внешнего кода, так и тем более дополнительных функций в этом коде, сначала необходимо выяснить размер секции кода и количество свободных в ней байт. Даже для обычных учебных программ свободного места в секции для внедрения функций может уже и не хватать.
Внедрение новых API-функций в чужой код не очень сильно отличается от внедрения команд. При внедрении API-функций необходимо не только знать количество и назначение входящих в них параметров, но и как эти параметры отображаются в окне отладчика. Для этих целей в зависимости от задачи, проще всего, написать отдельную программу с использованием необходимых API-функций. Затем откомпилировать такую программу, а потом открыть ее ехе-файл в отладчике и проанализировать их использование. После чего можно приступать и к внедрению своей программы в чужой код. В учебных целях в качестве чужой программы тоже будет применяться написанная нами программа.
При использовании отладчика x64Dbg нет необходимости вызывать, затем анализировать таблицу импорта адресов внедряемых функций, и вставлять эти адреса вместо названий функций, что сильно упрощает алгоритм внедрения.
Внедрение кода с добавлением API-функций
Предположим, что существует программа, написанная в среде masm64 с использованием команд SSE, которая вычисляет математическое уравнение. В эту программу необходимо внедрить свое сообщение функцией MessageBox без изменения функционала. Внедрение кода с изменением функционала было выполнено в предыдущей главе.
Рассмотрим основную программу (программа 5.1), в которую внедрим функцию MessageBox с соответствующим сообщением.
Программа 5.1. Числа {a,b,c,d} заданные массивом и имеют размерность Real8. Вычислить уравнение d/b sqrt(a) + a
Результатом выполнения программы является окно сообщения.Код (ASM):
title Рысованый А.Н. ; masm64. Числа {a,b,c,d} заданные массивом ; и имеют размерность Real8. Вычислить уравнение d/b sqrt(a) + a include win64a.inc ; подключаемые библиотеки .data mas1 real8 16.,2.,4.,16. ; a, b, c, d tit1 db "masm64. Результат вычисления уравнения на SSE2",0 buf1 dq ?,0 ; буфер вывода сообщения ifmt db "masm64. Массив a, b, c, d := 16., 2., 4., 16.",10,10, "Уравнение d/b sqrt(a) + a. Результат: %d ",10,10, "Автор: Рысованый А.Н., каф. ВТП, фак. КИТ, НТУ ХПИ",10, 9,"Сайт: http://blogs.kpi.kharkov.ua/v2/asm/",0 .code ; уравнение d/b sqrt(a) + a WinMain proc sub rsp,28h; cтек: 28h=32d+8; 8 - возврат mov rbp,rsp movsd xmm1,mas1[0] ; xmm1 - a - переслать двойное слово movsd xmm2,mas1[8] ; xmm2 - b movsd xmm3,mas1[16] ; xmm3 - c movsd xmm4,mas1[24] ; xmm3 - d divsd xmm4,xmm2 ; d/b sqrtsd xmm5,xmm1 ; sqrt(a) mulsd xmm4,xmm5 ; d/b x sqrt(a) addsd xmm4,xmm1 ; d/b x sqrt(a) + a cvttsd2si eax,xmm4 ; movsxd r15,eax invoke wsprintf,addr buf1,addr ifmt,r15 invoke MessageBox,0,addr buf1,addr tit1,MB_ICONINFORMATION invoke RtlExitUserProcess,0 WinMain endp end
Определим объем в байтах свободного места в секции кода. Для этой цели воспользуемся программой-анализатором LotdPE. Выполним последовательность действий:
Определим объем в байтах свободного места в секции кода. Для этой цели воспользуемся программой-анализатором LotdPE. Выполним последовательность действий:
- в программе нажать на клавишу меню PE Editor и выбрать исследуемый ехе-файл;
- в появившемся окне нажать клавишу меню Sections;
- выбрать секцию, где расположен текст программы (это секция text), нажать правую клавишу мышки и выбрать пункт меню hex edit section;
- в появившемся окне memory buffer выбранная секция выделится темным цветом. В конце секции свободные ячейки будут заполнены нулями. Их необходимо просто посчитать.
Для подсчета количества байтов свободных ячеек памяти в секции кода необходимо из начального адреса следующей секции вычесть первую свободную ячейку после окончания кода:
600h – 477h = 188h = 393 байтов.
Для внедрения функции с ее параметрами необходимо точно знать, как эти параметры отображаются синтаксически в отладчике и применять их точно так же. В основной программе уже присутствует функция MessageBox. Поэтому, писать в качестве примера отдельную программу с этой функцией уже нет необходимости: будем использовать параметры этой функции в ехе-файле, анализировать и изменять их под собственные адреса ячеек памяти.
Открываем ехе-файл этой программы в отладчике x64Dbg. При анализе кода в отладчике x64Dbg сначала необходимо продумать свои действия: где удобнее поставить новую функцию MessageBox и в какой последовательности она должна вывести окно сообщения.
Внешний вид основной программы представлен на рис.
Проанализируем программу в отладчике.
В нашем самом простом случае необходимо осуществить внедрение функции с ее параметрами в существующий код. Для этого надо в программе освободить место. Внедрить новую функцию MessageBox можно в начале программы, в середине или уже в самом конце. Алгоритм внедрения при выборе любого места не поменяется. Поменяется только последовательность вывода сообщений программой.
Выбираем для внедрения кода начало программы. Первые две команды перемещать нельзя, т.к они подготавливают программу к выполнению – выравнивают стек (при использовании среды masm64 от Mikl___). Значит – меняем третью строчку. Необходимо перенести эту команду из третьей строчки на свободное место после окончания программы. Последней строкой программы является функция RtlExitUserProcess.
Затем на освободившееся место ставим команду jmp с адресом перехода на адрес расположения перенесенной команды.
После – вставляем параметры функции MessageBox и вызов (команда call) этой функции.
Последней командой после вызова функции MessageBox снова будет команда jmp, но с адресом возврата на нетронутую строчку кода после вставленной первой команды jmp в начале программы.
И так, команда jmp с адресом перехода имеет размерность 2 байта.
Перенесем третью строчку кода и тем самым освободим место под команду jmp. В третьей строчке программы находится команда, которая занимает 8 байт памяти:
Поэтому, перенесем эту команду в конец файла, а на ее место – команду jmp с адресом, который соответствует новому месту расположения после окончания программы. А оставшиеся байты заполним автоматически командами NOP.
00007FF7100F1007 F2:0F100D F11F0000 movsd xmm1,qword ptr ds:[7FF7100F3000]
После команды jmp вставляем функцию MessageBox, скопировав эту функцию в нашей же программе со старыми значениями параметров (теми же адресами ячеек):А позже уже заменим адреса на те, которые соответствуют новому месту расположения других параметров.Код (ASM):
xor ecx,ecx lea rdx,qword ptr ds:[7FF75E51304F] lea r8,qword ptr ds:[7FF75E513020] mov r9d,40 call qword ptr ds:[<&MessageBoxA>]
Для продолжения этой программы поставим команду возврата на не измененную строчку кода, которая расположена после вставленной в начале программы команды jmp.
Последней вставкой по свободным адресам расположим текстовую информацию для функции MessageBox. И эти адреса поставим в новой функции MessageBox.
Итак, приступаем к внедрению. Рассмотрим последовательность внедрения.
- Освобождаем место для команды jmp. Для этого ставим курсор мышки на строку с первой командой программы, которую будем переносить на место, которое будет располагаться после конца программы:
Копируем строку кода. Для этого нажимаем правую клавишу мышки и выбираем Копировать / Дизассемблерный код.Код (ASM):
movsd xmm1,qword ptr ds:[7FF75E513000]- Переносим скопированную строку кода в конец кода. Для этого ставим курсор на первую позицию после самой последней команды программы. Нажимаем дважды на левую клавишу мышки и вставляем (Ctrl + V) из буфера команду
Код (ASM):
movsd xmm1,qword ptr ds:[7FF75E513000]
В этом окне показано, что скопированную команду вставлено в конец программы, но в начале кода (третья строка) эта команда еще не удалена.
Не забываем после каждого действия сохранять внесенные изменения с заменой имени файла на другое. Для этого выбираем пиктограмму с названием Исправления, а далее новое имя и расширение ехе.- Теперь необходимо заменить в начале программы команду, которую в предыдущем пункте уже переместили в конец файла, на команду jmp.
Для этого сначала копируем адрес перемещенной команды. Для этого ставим курсор в начало программы на команду, которую уже скопировали, но не заменили. Нажимаем правую клавишу мышки и выбираем Копировать / Адрес. После копирования адреса подводим курсор мышки в начало программы к команде, которую необходимо выделить, а затем и заменить. Дважды нажимаем на левую клавишу мышки.
Рис. 5.5. Окно с выделением первой команды, которую скопировали
В всплывающем окне пишем команду jmp, а затем Ctrl + V для вставки адреса из буфера (рис.).
А затем кнопку OK. Произошла замена команды, а лишние ячейки заполнились пустыми командами nop (рис. ).
- Теперь вставляем параметры и вызываем саму функцию MessageBox.
Необходимо последовательно после первой перенесенной команды вставить строки из нашей же программы:Для этого ставим курсор мышки на первый параметр функции MessageBox. Это строка кода:Код (ASM):
xor ecx,ecx lea rdx, ds:[0x00007FF7100F304F] lea r8, ds:[0x00007FF7100F3020] mov r9d,40 call qword ptr ds:[<&MessageBoxA>]Нажимаем правую клавишу мышки и выбираем Копировать / Дизассемблерный код, но быстрее и, как покажут дальнейшие действия, правильнее, если использовать сочетание клавиш Ctrl + C.Код (ASM):
xor ecx,ecx
Подводим курсор мышки в конец файла на свободную строку. Нажимаем дважды левую клавишу мышки и нажимаем Ctrl + V для вставки содержимого из буфера. После нажатия кнопки OK новая команда вставится на новое место (рис. ).
Таким же образом переносятся оставшиеся строки параметров функции MessageBox. А вот, чтобы скопировать команду вызова самой функции, необходимо дважды нажать на этой строке левой клавише мышки и нажать только Ctrl + C. При этом в всплывающем окне в строке отображения должно отображаться не название MessageBox, а адрес ячейки памяти, где располагается эта функция.
После такой вставки адрес нахождения функции поменяется на название этой функции.
Во вставленных строках параметров функции стоят старые адреса, которые необходимо заменить на новые. Но для того, чтобы не было фрагментации кода, с этими действиями необходимо пока повременить – надо еще вставить команду возврата jmp, а потом уже заменить адреса на реальные.- Вставляем команду возврата в конце вставленного кода с адресом возврата на начало оставшегося кода программы.
Для этого сначала подводим курсор к командеи копируем адрес ее расположения: Копировать / Адрес.Код (ASM):
movsd xmm2,qword ptr ds:[7FF60F393008]
После чего подводим курсор к следующей свободной в конце файла строке, нажимаем дважды левую клавишу мышки. Пишем jmp, вставляем пропуск и нажимаем комбинацию Ctrl + V (рис. ).
- Меняем адреса параметров функции MessageBox.
В этой функции в качестве параметров выступают текстовые строки с названиями титула упрощенного окна и строки вывода сообщения. Если указать для каждого из параметров один адрес, то эти строки будут совпадать. В регистре rdx передается сообщение, а название титула упрощенного окна – через регистр r8.
Напишем слово «Инфицировано» и в эти регистры передадим для простоты один и тот же адрес. Скопируем адрес свободной строки и заменим эти адреса в регистрах rdx и r8. Для этого подводим курсор мышки к свободной строке, нажимаем правую клавишу и выбираем Копировать / Адрес.
После занесения в буфер адреса последовательно вызываем строкии меняем из буфера адрес на адрес свободной строки, куда будем писать сообщение (рис. ).Код (ASM):
lea rdx, … lea r8, …
- Вставляем в ячейки памяти текст сообщения.
Для этого сначала подводим курсор к любой из строк кода, где записан адрес начала сообщения. Выбираем Перейти к дампу / Константа. После чего в дампе памяти отображается содержимое этих ячеек. Подводим курсор к содержимому первого байта, нажимаем левую клавишу мышки и не отпуская ее выбираем столько ячеек, сколько букв содержится в слове Инфицировано. Нажимаем правую клавишу мышки и выбираем Двоичные операции /Редактировать. Вводим сообщение (рис. ).
- Делаем последнее сохранение изменений с заменой имени файла на другое. Для этого выбираем пиктограмму с названием Исправления, а далее новое имя и расширение ехе.
- Запускаем ехе-файл. После запуска последовательно выводятся 2 сообщения (рис. ).
Процесс внедрения завершен успешно.
Внедрение кода в ехе-файл в конец секции c добавлением API-функций. Часть 6
Дата публикации 5 апр 2019
| Редактировалось 17 май 2019