Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
| Помогли мы вам |
Обращаю твое внимание на одну деталь: с текущей статьи я перехожу на Visual Studio 2019. Последняя версия датируется 17 сентября и имеет номер 16.7.5. Чтобы избежать возможных несостыковок, советую тебе тоже обновить «Студию».
Структуры очень популярны среди программистов. Позволяя объединить под одной крышей родственные данные, они делают листинг программы более наглядным и упрощают его понимание. Соответственно, идентификация структур при дизассемблировании облегчает анализ кода. К великому сожалению исследователей, структуры как таковые существуют только в исходном тексте программы и практически полностью «перемалываются» при ее компиляции, становясь неотличимыми от обычных, никак не связанных друг с другом переменных.
Рассмотрим пример, демонстрирующий уничтожение структур на стадии компиляции:
#include <stdio.h>#include <string.h>struct zzz{ char s0[16]; int a; float f;};void func(struct zzz y)// Понятное дело, передачи структуры РїРѕ значению лучше избегать,// РЅРѕ здесь это сделано умышленно для демонстрации скрытого создания// локальной переменной{ printf("%s %x %fn", &y.s0[0], y.a, y.f);}int main(){ struct zzz y; strcpy_s(&y.s0[0], 14, "Hello,Sailor!"); // Для копирования строки y.a = 0x666;// используется безопасная версия функции y.f = (float)6.6;// Чтобы подавить возражение компилятора, func(y);// указываем целевой тип}Результат компиляции этого кода с помощью Visual Studio 2019 для платформы x64 должен выглядеть так:
main proc near; Члены структуры неотличимы РѕС‚ обычных локальных переменныхvar_48 = xmmword ptr -48hvar_38 = qword ptr -38hDst = byte ptr -28hvar_18 = qword ptr -18hvar_10 = qword ptr -10h sub rsp, 68h mov rax, cs:__security_cookie xor rax, rsp mov [rsp+68h+var_10], rax ; Подготовка параметров для вызова функции lea r8, Src; "Hello,Sailor!" mov edx, 0Eh; SizeInBytes lea rcx, [rsp+68h+Dst] ; Dst ; Вызов функции для копирования строки РёР· сегмента данных РІ локальную ; переменную call cs:__imp_strcpy_sСледующая команда копирует одно вещественное число, находящееся в младших 32 битах источника, — константу __real@40d33333 (смотрим, чему она равна при объявлении в секции rdаta: , в формате float она будет равна 6.6) в младшие 32 бита приемника — 128-битного регистра XMM1. Напомню, восемь регистров XMM0 были добавлены в расширение SSE и поэтому впервые появились в процессоре Pentium III.
movss xmm1, cs:__real@40d33333 ; Помещаем указатель РЅР° строку РІ регистр RDX lea rdx, [rsp+68h+var_48]Далее с использованием инструкции MOVUPS из расширения SSE копируются невыровненные куски по 16 бит. Таким образом, за раз копируются сразу восемь символов Unicode. Однако количество символов в строке вполне может быть не кратно восьми, поэтому используется именно эта инструкция — все остальные инструкции из расширения SSE оперируют с переменными, выровненными по 16-битным границам памяти. В ином случае они вызывают исключение.
; В регистр RCX помещаем форматную строку для функции printf
lea
rcx, _Format
; "%s %x %fn"
; Помещаем двойное слово (значение 0x666) в переменную типа DWORD
mov
dword ptr [rsp+68h+var_18], 666h ; --1Следующая команда копирует строго двойное слово из памяти в регистр (у нас это XMM3). Значение, сохраненное в копируемой области памяти: 6., выровнено по границе 16 бит и на самом деле равно 6.6. В случае копирования из памяти в регистр (подобно нашему примеру) обнуляется старшее двойное слово источника.
movsd xmm3, cs:__real@401a666660000000 ; Помещаем значение 0x666 РІ 32-битный регистр mov r8d, 666h ; Р?Р· переменной (СЃРј. метку --1) копируем РґРІРѕР№РЅРѕРµ слово РІ регистр movsd xmm2, [rsp+68h+var_18]Далее учетверенное слово (64 бит) копируется из регистра XMM3 расширения SSE в регистр общего назначения R9, добавленный вместе с расширением x86-64. Ведь AMD64, по сути, представляет собой такое же расширение процессорной архитектуры x86, как и SSE.
movq r9, xmm3Инструкция shufps посредством битовой маски комбинирует и переставляет данные в 32-битных компонентах XMM-регистра. Таким образом, если представить 0E1h в бинарном виде, получим 11100001b. В соответствии с этой маской происходит трансформация всех четырех 32-битных частей регистра XMM2.
shufps xmm2, xmm2, 0E1h ; Копирование нижней 32-битной части источника РІ приемник movss xmm2, xmm1 ; Копирует 128 Р±РёС‚ РёР· регистра РІ переменную movaps [rsp+68h+var_48], xmm0 ; Р’ соответствии СЃ маской перемешивает содержимое регистра (СЃРј. выше) shufps xmm2, xmm2, 0E1h ; Две следующие инструкции помещают значение регистра РІ переменные, ; находящиеся РІ памяти movsd [rsp+68h+var_18], xmm2 movsd [rsp+68h+var_38], xmm2 ; Р’СЃРµ параметры находятся РЅР° СЃРІРѕРёС… местах, вызываем функцию printf call printf xor eax, eax mov rcx, [rsp+68h+var_10] xor rcx, rsp; StackCookie call __security_check_cookie add rsp, 68h retnmain endpКомпилятор сгенерировал довольно витиеватый код со множеством команд из расширения SSE. При этом он встроил функцию func прямо в main!
А теперь заменим структуру последовательным объявлением тех же самых переменных и рассмотрим пример, демонстрирующий сходство структур с обычными локальными переменными.
int main(){ char s0[16]; int a; float f; strcpy_s(&s0[0], 14, "Hello,Sailor!"); a = 0x666; f = (float)6.6; printf("%s %x %fn", &s0[0], a, f);}И сравним результат компиляции с предыдущим:
Dst
= byte ptr -28hvar_18 = qword ptr -18h; Есть различие! Компилятор избавился от ненужных для выполнения переменных,; однако от этого не становится понятнее, принадлежат переменные структуре или нет
sub
rsp, 48h
mov
rax, cs:__security_cookie
xor
rax, rsp
mov
[rsp+48h+var_18], rax
; Готовим параметры
lea
r8, Src; "Hello,Sailor!"
mov
edx, 0Eh; SizeInBytes
lea
rcx, [rsp+48h+Dst] ; Dst
; Вызываем функцию копирования строки
call
cs:__imp_strcpy_s
; В XMM3 помещается значение 6.599999904632568 (подробно мы говорили,
; когда разбирали предыдущий листинг)
movsd
xmm3, cs:__real@401a666660000000
; Последующие инструкции продолжают готовить параметры для функции
lea
rdx, [rsp+48h+Dst]
movq
r9, xmm3
; В регистр RCX помещаем форматную строку для функции printf
lea
rcx, _Format
; "%s %x %fn"
; Помещаем значение 0x666 в младшие 32 бита регистра R8
mov
r8d, 666h
; Вызов функции printf
call
printf
xor
eax, eax
mov
rcx, [rsp+48h+var_18]
xor
rcx, rsp; StackCookie
call
__security_check_cookie
add
rsp, 48h
retnmain
endpБез вызова дополнительных функций и передачи параметров дизассемблерный листинг заметно сократился. Остальной код остался идентичным предыдущему листингу.
|
|
|