Виртуальная машина на страже порядка GPcH Хакер, номер #077, стр. 077-106-6 asm mov ecx, strLen mov eax, strText dec eax mov bh, strByte @encrypt: xor byte ptr[eax+ecx],bh loop @encrypt end; Ассемблерная вставка была использована по двум причинам: для удобства (на ассемблере написать простенькую шифровку намного проще, чем на Delphi) и для того, чтобы ты лучше смог представить перевод скриптовых команд в понятный процессору машинный код. Наверное, не лишним будет рассмотреть данный код поподробнее. Начнем с регистров процессора eax, ebx и ecx. Они служат для хранения 4 байтных данных. Чтобы получить доступ к 2 младшим байтам например регистра ebx - нужно обращаться к регистру bx (соответственно для eax - ax, ecx - cx). Если нам нужно использовать только старший или младший байт 2 байтного регистра bx - юзаем регистры bh и bl соответственно. В нашем конкретном случае каждый байт строки ксорится с байтом, переданным во втором параметре скриптовой командой, потому для хранения этой команды мы используем однобайтный регистр bh. @encrypt - это метка. Собака перед именем метки ставится для того, чтобы указать компилятору что метка локальная, а не глобальная для всей процедуры нашего интерпретатора. Командам ассемблера передаются 2 операнда или значение и операнд. Команда MOV перемещает в регистр, являющийся первым операндом содержащиеся во втором операнде данные. Данные во втором операнде должны быть представлены либо регистром, либо числом, переданным напрямую. Главное, о чем надо помнить так это о том, что размер данных должен соответствовать или быть меньше размера регистра. Команда DEC имеет дело с одним операндом, который должен быть представлен исключительно в виде регистра. Эта команда уменьшает число, записанное в передаваемом регистре на 1. Последовательность encrypt и loop encrypt представляет собой цикл. Счетчик цикла записывается в регистр ecx. Команда xor byte ptr[eax+ecx],bh означает, что нужно отXORить byte по адресу в памяти равному eax+ecx с байтом, который содержится в регистре bh. Так как ecx содержит длину строчки, а eax - адрес этой строчки в памяти, то, учитывая автоматическое уменьшение ecx (так как этот регистр является счетчиком цикла), можно определить, что строчка шифруется с конца. Так оно и есть - процессору так проще. Мы, конечно, можем добавить пару лишних команд, чтобы шифровка производилась с начала строки, но зачем усложнять программу и тратить на это лишние такты работы процессора? Теперь, когда думаю, ты во всем алгоритме интерпретатора разобрался - добавь на форму Memo и кнопку. В Memo мы будем вводить команды движку, а при нажатии на кнопку они будут циклично выполняться движком интерпретатора. Собственно обработчик нажатия на кнопку будет выглядеть так: procedure TForm1.Button1Click(Sender: TObject); var i: integer; begin for i:=0 to Memo1.Lines.Count-1 do begin //вызываем интерпретатор EngineVM(Memo1.Lines.Strings[i]); end; end; В том исходнике, что лежит на диске в Memo уже введено пара команд, чтобы ты мог сразу протестировать движок. Если есть желание - добавь в него побольше команд, сделай возможность возвращения командами результата, введи макросы, переменные и кучу всего интересного. Это все значительно расширит возможности нашей программы. Также, ты можешь придумать другой формат команд, какой-нибудь сложный и непонятный, для того чтобы писать свои программы целиком на нем и чтобы никто, даже самый хитрый крэкер не разобрался бы в них. |