TOP10 ошибок защитников программ

         

Защита в ассемблерных вставках


Хорошо продуманная защита не нуждается в ассемблере и уж тем более в ассемблерных вставках, выдающих защитный код с головой. Хотите знать почему? Если в функции отсутствуют ассемблерные вставки, оптимизирующие компиляторы выбрасывают стандартный пролог, адресуя локальные переменные и аргументы непосредственно через регистр ESP, но как только в теле функции появится хоть одна ассемблерная вставка, интеллекта компилятора для "сквозной" адресации через ESP уже оказывается недостаточно и он возвращается к стандартному прологу.

Проведем простой эксперимент. Возьмем следующую программу и откомпилируем ее компилятором Microsoft Visual C++ с максимальным режимом оптимизации (ключ /Ox).

main()

{

       int a,b=0;

       for (a=0;a<10;a++) b+=a*2;

       printf("%x\n",b);

}

Листинг 2 исходный код функции без ассемблерных вставок

Дизассемблерный листинг при этом будет выглядеть так:

.text:00000000 _main       proc near

.text:00000000                    xor    ecx, ecx

.text:00000002                    xor    eax, eax



.text:00000004

.text:00000004 loc_4:                                  ; CODE XREF: _main+Cvj

.text:00000004                    add    ecx, eax

.text:00000006                    add    eax, 2

.text:00000009                    cmp    eax, 14h

.text:0000000C                    jl     short loc_4

.text:0000000E                    push   ecx

.text:0000000F                    push   offset $SG398

.text:00000014                    call   _printf

.text:00000019                    add    esp, 8

.text:0000001C                    retn

.text:0000001C _main       endp

Листинг 3 дизассемблерный листинг функции без ассемблерных вставок (стандартный пролог выброшен компилятором)

Как мы видим, ничего похожего на пролог тут нет! Но стоит нам добавить хотя бы простейшую ассемблерную вставку типа: __asm {mov a,eax } и перекомпилировать программу, как все полетит кувырком!

.text:00000000 _main       proc near


.text:00000000
.text:00000000 var_4       = dword ptr -4
.text:00000000
.text:00000000                    push   ebp
.text:00000001                    mov    ebp, esp
.text:00000003                    push   ecx
.text:00000004                    xor    ecx, ecx
.text:00000006                    xor    eax, eax
.text:00000008
.text:00000008 loc_8:                                  ; CODE XREF: _main+10vj
.text:00000008                    add    ecx, eax
.text:0000000A                    add    eax, 2
.text:0000000D                    cmp    eax, 14h
.text:00000010                    jl     short loc_8
.text:00000012                    mov    [ebp+var_4], eax
.text:00000015                    push   ecx
.text:00000016                    push   offset $SG398
.text:0000001B                    call   _printf
.text:00000020                    add    esp, 8
.text:00000023                    mov    esp, ebp
.text:00000025                    pop    ebp
.text:00000026                    retn
.text:00000026 _main       endp
Листинг 4  дизассемблерный листинг той же самой функции с мелкой ассемблерной вставкой (стандартный пролог выделен полужирным шрифтом)
Вот он, стандартный пролог, легко обнаруживаемый контекстным поиском! Поэтому, либо вообще не используйте никакого ассемблера в своих программах, либо пишите на чистом ассемблере с последующей трансляцией в obj, либо предваряйте ассемблерные функции спецификатором "naked", в этом случае Microsoft Visual C++ ни пролога, ни эпилога вставлять не будет.

Содержание раздела