Перед рассмотрением этой части следует изучить две предыдущие.
Рассмотрим программу 7.5, в которой применена технология перекрытия кода, но с переходом вперед по коду программы.
Программа 7.5.В приведенной программе применяется строковый идентификатор $ с различными положительными приращениями. Такое идентичное необходимо для того, чтобы рассмотреть особенности переходов через регистры с разной разрядной сеткой.Код (ASM):
include win64a.inc .data .code WinMain proc sub rsp,28h; mov rbp,rsp mov ax,05EBh jmp $ + 5 ; перепрыгивает свои 2 байта и 3 байта dec r9 dec r9 ; 49:FFC0 inc r10 ; выполняем с этой команды mov r8,1 mov eax,06EB0000h jmp $ + 6 ; перепрыгивает свои 2 байта и следующие 4 dec cl ; FEC9 dec al ; FEC8 dec dl ; FECA; выполняем с этой команды nop mov rax,09EB000000000000h jmp $ + 9 ; перепрыгивает свои 2 байта и следующие 7 mov r15,1 ; 49:C7C7 0100 0000 nop ; выполняем с этой команды invoke RtlExitUserProcess,0 ; invoke ExitProcess,0 WinMain endp end
В первом интересующим нас блоке кодакоманда mov ax,05EBh ни на что не влияет.Код (ASM):
mov ax,05EBh jmp $ + 5 ; перепрыгивает свои 2 байта и 3 байта dec r9 dec r9 ; 49:FFC0 inc r10 ; выполняем с этой команды
Значение имеет команда jmp $ + 5, которая занимает в памяти 2 байта: EB03. По результатам выполнения этой команды происходит переход через свои 2 байта и 3 байта, расположенных за ней строки кода. Всего получилось смещение на 5 байтов. Для простоты рассмотрения применена команда dec r9 с кодом операции 49:FFC0, которая имеет размерность 3 байта. Но если есть логический смысл и задумка программиста, то можно указать другое смещение адреса и войти во внутрь этой команды и выполнить совсем другой код операции.
Следующие блоки кода со смещениями +6 и +9 имеют аналогичный смысл и эти блоки хорошо прокомментированы.
Рассмотрим применение положительных смещений в строковом идентификаторе $ (программа 7.6).
Программа 7.6.Программа состоит из двух частей, которые выполняют вычисление одного и того же целочисленного выражения: ab + c/d.Код (ASM):
include win64a.inc .data a1 db 1 b1 db 2 c1 db 4 d1 db 3 .code WinMain proc sub rsp,28h; mov rbp,rsp ; блок1 : ab + c/d mov al,a1 mul b1 mov cl,al mov al,c1 div d1 add al,cl nop ; блок2 : ab + c/d mov al,a1 ; 6 байтов: 8A05 D61F0000 jmp $+4 ; 2 байта: EB 02 nop ; nop ; mul b1 ; 6 байтов: F625 CD1F0000 jmp $+5 ; 2 байта: EB 03 dec dx ; 3 байта: 66:FFCA mov cl,al ; jmp $+9 ; 2 байта: EB 07 mov r15,1 ; 7 байтов: 49:C7C7 01000000 mov al,c1 ; jmp $+7 ; 2 байта: EB 05 mov esi,1234 ; 5 байтов: BE D2040000 div d1 ; add al,cl ; invoke RtlExitUserProcess,0 ; invoke ExitProcess,0 WinMain endp end
В блоке 1 кода отсутствуют какие-либо запутывания кода. А в блоке 2 вычисляется тоже самое выражение, но с применением строкового идентификатора для небольшого затруднения анализа. Для простоты анализа в блоке 2 отсутствуют вхождения во внутрь машинного кода команд, что является существенным недостатком этой программы. Но даже и такое применение затрудняет анализ этого кода.
Перед тем, как применить перекрытие кода рассмотрим обычную программу пересылки целочисленного массива из одного места памяти в другой при помощи команд SSE (программа 7.7).
Программа 7.7.Для затруднения анализа этой программы внесем в нее незначительные изменения путем внесения данных в секцию кода. Данные можно переносить только те, которые при выполнении программы не требуют записи (изменений) в секцию кода (в последующем будет рассмотрена такая возможность при помощи изменений атрибутов секции) (программа 7.8).Код (ASM):
include win64a.inc .data mas1 DD 20 dup(1,2,4,6) ; резервирование ячеек памяти для mas1 dd 16 dup(1) ; 80+16=96 len1 equ ($-mas1)/type mas1 mas2 DD len1 dup(0) ; резервирование ячеек памяти для mas2 ifmt db "… masm64",10, "…",0 titl1 db "Пересылка целых чисел, SSE",0 .code WinMain proc sub rsp,28h; mov rbp,rsp mov rcx,len1/4 ; количество блоков массива mas1 lea rsi,mas1 ; адрес начала массива mas1 lea rdi,mas2 ; адрес начала массива mas2 @@: movups xmm0,dword ptr [rsi] ; пересылка 4-x 32-разрядных целых чисел movups dword ptr [rdi],xmm0 add rsi,16; add rdi,16; loop @b invoke MessageBox,0,addr ifmt,addr titl1,MB_ICONINFORMATION invoke RtlExitUserProcess,0 ; invoke ExitProcess,0 WinMain endp end
Программа 7.8.Получим из этого файла ехе-файл. Откроем его в отладчике x64Dbg (рис. 7.8) и обратим внимание на строку кодаКод (ASM):
include win64a.inc .data mas1 DD 20 dup(1,2,4,6) ; резервирование ячеек памяти для mas1 dd 16 dup(1) ; 80+16=96 len1 equ ($-mas1)/type mas1 mas2 DD len1 dup(0) ; резервирование ячеек памяти для mas2 .code WinMain proc sub rsp,28h; mov rbp,rsp mov rcx,len1/4 ; количество блоков массива mas1 lea rsi,mas1 ; адрес начала массива mas1 lea rdi,mas2 ; адрес начала массива mas2 jmp m2 ifmt db "Проверка перекрытия кода на masm64",10, "Рысованый А.Н., каф. ВТП, НТУ ХПИ",0 m2: @@: movups xmm0,dword ptr [rsi] ; пересылка 4-x 32-разрядных целых чисел movups dword ptr [rdi],xmm0 add rsi,16; jmp m1 titl1 db "Пересылка целых чисел, SSE",0 m1: add rdi,16; loop @b invoke MessageBox,0,addr ifmt,addr titl1,MB_ICONINFORMATION invoke RtlExitUserProcess,0 ; invoke ExitProcess,0 WinMain endp end
00007FF7F4121070 EB 1B jmp rp l2-13.7FF7F412108D
В этой строке команда jmp обращается к адресу, который находится уже внутри другой команды:
Так отладчик отреагировал на команду jmp, которая размещена внутри другого цикла – тем самым применив собственное перекрытие кода.
00007FF7F412108B 45:0048 83 add byte ptr ds:[r8-7D],r9b
Принимаем решение на применение перекрытия кода на примере команды jmp, которая обрамляет последний блок данных. Эта команда размещена по адресу 7FF7F4121070h, а передает управление на адрес 7FF7F412108Dh. Получаем разницу между адресами 1Dh. Эту разницу будем применять при перекрытии кода.
Следующим шагом в изменении первоначальной программы является применение перекрытия кода. В учебных целях проще это делать на командах jmp потому, что на других командах применение перекрытия приводит к зацикливанию этой части программы (программа 7.9).
Программа 7.9.В программе для затруднения анализа, данные, которые в процессе выполнения не изменяются, размещены в секции кода и эта секция переименована управляющим словом segment в новое имя mySeg. Данные обрамлены командой jmp и соответствующей меткой.Код (ASM):
include win64a.inc .data mas1 DD 20 dup(1,2,4,6) ; резервирование ячеек памяти для mas1 dd 16 dup(1) ; 80+16=96 len1 equ ($-mas1)/type mas1 mas2 DD len1 dup(0) ; резервирование ячеек памяти для mas2 mySg1 segment READ WRITE EXECUTE alias ("mySeg") ;.code WinMain proc sub rsp,28h; mov rbp,rsp mov rcx,len1/4 ; количество блоков массива mas1 lea rsi,mas1 ; адрес начала массива mas1 lea rdi,mas2 ; адрес начала массива mas2 jmp m2 ifmt db "Проверка перекрытия кода на masm64",10, "Рысованый А.Н., каф. ВТП, НТУ ХПИ",0 m2: @@: movups xmm0,dword ptr [rsi] ; пересылка 4-x 32-разрядных целых чисел movups dword ptr [rdi],xmm0 add rsi,16; mov ax,1debh jmp $-2 ; jmp m1 titl1 db "Пересылка целых чисел, SSE",0 ; m1: add rdi,16; loop @b invoke MessageBox,0,addr ifmt,addr titl1,MB_ICONINFORMATION invoke RtlExitUserProcess,0 ; invoke ExitProcess,0 WinMain endp mySg1 ends end
Результат выполнения программы приведен на рис. 7.9.
Команда jmp заменена на строки кодаВ регистр AX заносится код операции jmp – значение 0EBh и смещение – 1Dh (рис. 7.10).Код (ASM):
mov ax,1debh jmp $-2
Непосредственный результат пересылки целочисленных данных можно посмотреть в дампе памяти отладчика x64Dbg.
Перекрытие кода с переходом вперед по коду программы. Часть 10.
Дата публикации 9 апр 2019
| Редактировалось 17 май 2019