Дизассемблиры

Автор: Пользователь скрыл имя, 19 Октября 2011 в 15:00, реферат

Краткое описание

Дизассемблирование (От англ. disassemble - разбирать, демонтировать) – это процесс или способ получения исходного текста программы на ассемблере из программы в машинных кодах. Полезен при определении степени оптимальности транслятора и при генерации кодов собственной программы. Позволяет понять алгоритм или метод построения программ, у которых отсутствуют исходные тексты. Существуют специальные программы дизассемблеры, которые выполняют этот процесс.

Оглавление

Введение.
Структура природы человека.
Биологическое и социальное в человеке.
Роль биологических и географических факторов в формировании социальной жизни.
Социальная жизнь.

Файлы: 1 файл

kyrsovaya0002.doc

— 391.00 Кб (Скачать)

shl eax, 4

; Умножаем var_a на 16 

push eax

push offset aXXX ; "%x %x %x\n"

call _printf

add esp, 14h

; printf("%x %x %x\n", var_a * 16, var_a * 4 + 5, var_a * 13) 

retn 

main  endp 

  

     Этот  код, правда, все же не быстрее предыдущего, не оптимизированного, и укладывается в те же три такта, но в других случаях выигрыш может оказаться вполне ощутимым.

     Другие  компиляторы так же используют LEA для быстрого умножения чисел. Вот, к примеру, Borland поступает так:  

_main  proc near  ; DATA XREF: DATA:00407044 o

lea edx, [eax+eax*2]

; EDX = var_a*3 

mov ecx, eax

; Загружаем  в ECX неинициализированную регистровую  переменную var_a 

shl ecx, 2

; ECX = var_a * 4 

push ebp

; Сохраняем EBP 

add ecx, 5

; Добавляем  к var_a * 4 значение 5

; К сожалению Borland не использует LEA для сложения. 

lea edx, [eax+edx*4]

; EDX = var_a + (var_a *3) *4 = var_a * 13

; Здесь  Borland и MS единодушны. 

mov ebp, esp

; Открываем  кадр стека в середине функции.

; Выше находтся "потерянная" команда push EBP 

push edx

; Передаем printf произведение var_a * 13 

shl eax, 4

; Умножаем ((var_a *4) + 5) на 16

; Здесь  проискодит глюк компилятора, посчитавшего что: раз переменная var_a

; неинициализирована, то ее можно и не загружать… 

push ecx

push eax

push offset aXXX ; "%x %x %x\n"

call printf

add esp, 10h

xor eax, eax

pop ebp

retn 

_main  endp 

  

     Хотя "визуально" Borland генерирует не очень “красивый” код, его выполнение укладывается в те же три такта процессора. Другое дело WATCOM, показывающий удручающе отсталый результат на фоне двух предыдущих компиляторов:  

main  proc near

push ebx

; Сохраняем EBX в стеке 

mov eax, ebx

; Загружаем  в EAX значение неинициализированной  регистровой переменной var_a 

shl eax, 2

; EAX = var_a * 4 

sub eax, ebx

; EAX = var_a * 4 - var_a = var_a * 3

; Здесь WATCOM! Сначала умножает "с запасом", а потом лишнее отнимает! 

shl eax, 2

; EAX = var_a * 3 * 4 = var_a * 12 

add eax, ebx

; EAX = var_a * 12 + var_a = var_a * 13

; Четыре инструкции, в то время как

; Microsoft Visual C++ вполне обходится и двумя! 

push eax

; Передаем printf значение var_a * 13 

mov eax, ebx

; Загружаем  в EAX значение неинициализированной  регистровой переменной var_a 

shl eax, 2

; EAX = var_a * 4 

add eax, 5

; EAX = var_a * 4 + 5

; Пользоваться LEA WATCOM то же не умеет! 

push eax

; Передаем printf значение var_a * 4 + 5 

shl ebx, 4

; EBX = var_a * 16 

push ebx

; Передаем printf значение var_a * 16 

push offset aXXX ; "%x %x %x\n"

call printf_

add esp, 10h

; printf("%x %x %x\n",var_a * 16, var_a * 4 + 5, var_a*13) 

pop ebx 

retn 

main_  endp 
 

     В результате, код, сгенерированный компилятором WATCOM требует шести тактов, т.е. вдвое  больше, чем у конкурентов.  
 
 

§1.6 Комплексные операторы 

     Язык  Си\Си++ выгодно отличается от большинства  своих конкурентов поддержкой комплексных операторов: x= (где x - любой элементарный оператор), ++ и - -. Комплексные операторы семейства "a x= b" транслируются в "a = a x b" и они идентифицируются так же, как и элементарные операторы. Операторы "++" и "--": в префиксной форме они выражаются в тривиальные конструкции "a = a +1" и "a = a - 1" и не представляющие никакого интереса.

Глава2. Идентификация SWITCH - CASE - BREAK

 

     Для улучшения читабельности программ в язык Си был введен оператор множественного выбора – switch. В Паскале с той же самой задачей справляется оператор CASE.

     Легко показать, что switch эквивалентен конструкции "IF (a == x1) THEN оператор1 ELSE IF (a == x2) THEN оператор2 IF (a == x2) THEN оператор2 IF (a == x2) THEN оператор2 ELSE …. оператор по умолчанию". Если изобразить это ветвление в виде логического дерева, то образуется характерная "косичка", прозванная так за сходство с завитой в косу прядью волос – см. рис. 1

     Казалось  бы, идентифицировать switch никакого труда не составит, – даже не строя дерева, невозможно не обратить внимания на длинную цепочку гнезд, проверяющих истинность условия равенства некоторой переменной с серией непосредственных значений (сравнения переменной с другой переменной switch не допускает).  

 

Рисунок 1 Трансляция оператора switch в общем случае 

     Однако  в реальной жизни все происходит совсем не так. Компиляторы (даже не оптимизирующие) транслируют switch в настоящий "мясной рулет", содержащий всевозможные операции отношений. Вот что из этого выйдет, откомпилировав приведенный выше пример компилятором Microsoft Visual C++:  

main  proc near  ; CODE XREF: start+AF p 

var_tmp  = dword ptr -8

var_a  = dword ptr –4 

push ebp

mov ebp, esp

; Открываем кадр стека 

sub esp, 8

; Резервируем  место для локальных переменных 

mov eax, [ebp+var_a]

; Загружаем  в EAX значение переменной var_a 

mov [ebp+var_tmp], eax

; Обратите  внимание – switch создает собственную  временную переменную!

; Даже  если значение сравниваемой переменной  в каком-то ответвлении CASE

; будет  изменено, это не повлияет на результат выборов!

; В дальнейшем  во избежании путаницы, следует условно называть

; переменную var_tmp переменной var_a 

cmp [ebp+var_tmp], 2

; Сравниваем  значение переменной var_a с двойкой

; В исходном коде CASE начинался с нуля, а заканчивался 0x666 

jg short loc_401026

; Переход,  если var_a > 2

; Обратите  на этот момент особое внимание  – ведь в исходном тексте  такой

; операции  отношения не было!

; Причем, этот переход не ведет к  вызову функции printf, т.е. этот  фрагмент

; кода  получен не прямой трансляцией некой ветки case, а как-то иначе! 

cmp [ebp+var_tmp], 2

; Сравниваем  значение var_a с двойкой

; Очевидный  "прокол" компилятора (обратите внимание никакие флаги не менялись!) 

jz short loc_40104F

; Переход к вызову printf("a == 2"), если var_a == 2

; Этот код явно получен трансляцией ветки CASE 2: printf("a == 2") 

cmp [ebp+var_tmp], 0

; Сравниваем var_a с нулем 

jz short loc_401031

; Переход к вызову printf("a == 0"), если var_a == 0

; Этот  код получен трансляцией ветки  CASE 0: printf("a == 0") 

cmp [ebp+var_tmp], 1

; Сравниваем var_a с единицей 

jz short loc_401040

; Переход к вызову printf("a == 1"), если var_a == 1

; Этот  код получен трансляцией ветки  CASE 1: printf("a == 1") 

jmp short loc_40106D

; Переход к вызову printf("Default")

; Этот  код получен трансляцией ветки  Default: printf("a == 0") 

loc_401026:    ; CODE XREF: main+10 j

; Эта  ветка получает управление, если var_a > 2

cmp [ebp+var_tmp], 666h

; Сравниваем var_a со значением 0x666 

jz short loc_40105E

; Переход к вызову printf("a == 666h"), если var_a == 0x666

; Этот  код получен трансляцией ветки  CASE 0x666: printf("a == 666h") 

jmp short loc_40106D

; Переход к вызову printf("Default")

; Этот  код получен трансляцией ветки  Default: printf("a == 0") 

loc_401031:    ; CODE XREF: main+1C j

; // printf("A == 0")

push offset aA0 ; "A == 0"

call _printf

add esp, 4

jmp short loc_40107A

; ^^^^^^^^^^^^^^^^^^^^^^ - а вот это оператор break, выносящий  управление

; за  пределы switch – если бы его  не было, то начали бы выполняться все

; остальные  ветки CASE, не зависимо от того, к какому значению var_a они

; принадлежат! 

loc_401040:    ; CODE XREF: main+22 j

; // printf("A == 1")

push offset aA1 ; "A == 1"

call _printf

add esp, 4

jmp short loc_40107A

; ^ break 

loc_40104F:    ; CODE XREF: main+16 j

; // printf("A == 2")

push offset aA2 ; "A == 2"

call _printf

add esp, 4

jmp short loc_40107A

; ^ break 

loc_40105E:    ; CODE XREF: main+2D j

; // printf("A == 666h")

push offset aA666h ; "A == 666h"

call _printf

Информация о работе Дизассемблиры