Трудовая дисциплина Крис Касперски aka мыщъх Хакер, номер #088, стр. 088-114-5 Метку start можно, в принципе, и не убирать, а вот с s0 придется покончить. Ну не поддерживает встроенный ассемблер директивы "db", что тут поделать?! Приходится объявлять строковую константу средствами самого Си. Она может быть размещена как в стеке (как локальная переменная), так и в секции данных (как глобальная переменная). В действительности строки всегда размещаются в секции данных, а стек заносится лишь их копия, а копия - это оверхид, то есть накладные расходы и прочий маст-дай. Некоторые оптимизирующие компиляторы, в том числе и vc, могут загонять строковые переменные в стек при помощи инструкций PUSH XXYYZZ, и тогда в секции данных их уже не оказывается. Если переменная объявлена как глобальная, то ключевое слово offset сохраняет свою силу, и компилятор не матерится. С локальными переменными все сложнее. Конструкция «push offset s0» в этом случае разворачивается компилятором в «push offset [ebp+x]», что совершенно бессмысленно, с точки зрения синтаксиса! Но убирать offset нельзя, поскольку «push [ebp+x]» заталкивает в стек отнюдь не указатель на s0, а… значения первых четырех байт, то есть ведет себя, как «*((DWORD*)s0)». Правильный вариант выглядит так: «lea eax,s0/push eax» (разумеется, вместо eax можно использовать любой другой регистр общего назначения). Еще один нюанс. Конструкция «call MessageBoxA» выполняется совсем не так, как задумывалось, поскольку вместо MessageBoxA коварный компилятор подставляет отнюдь не адрес самой MessageBoxA, а указатель на двойное слово, хранящее адрес MessageBoxA! Следовательно, чтобы программа не развалилась и не умерла, необходимо использовать префикс ds, и тогда вызывающий код будет выглядеть так: «call ds:MessageBoxA» Обобщив все вышесказанное, мы получаем следующую программу. Даже две! С объявлением строки как глобальной и локальной переменной: [программа hello_global.c, с "глобализацией" ассемблерных переменных] #include <windows.h> // глобальная переменная, выводимая на экран char s0[]="hello,wordl\n"; main() { __asm { push 0 push 0 push offset s0 push 0 call ds:MessageBoxA ; добавляем ds: } } Компилируем: cl.exe hello_global.c USER32.lib где USER32.lib – имя библиотеки для MessageBoxA, и запускаем. Получаем симпатичное диалоговое окно. Вариант с локальной переменной компилируется и запускается точно так же, как и предыдущий: [программа hello_local.c с "локализацией" ассемблерных переменных] #include <windows.h> main() { char s0[]="hello,wordl\n"; __asm { push 0 push 0 lea eax, s0 push eax push 0 call ds:MessageBoxA } } Ассемблерные вставки, бесспорно, удобная вещь, но все-таки имеющая кое-какие ограничения. В частности, встроенный ассемблер не поддерживает никаких макросредств, и если в транслируемой программе присутствует множество макросов, то без помощи MASM'а (или его конкурентов) здесь уже не обойтись. |