Ассемблерные головоломки Крис Касперски ака мыщъх Хакер, номер #081, стр. 081-112-4 00000000: B4 09 MOV AH,009 00000002: BA 08 01 MOV DX,00108 00000005: CD 21 INT 021h 00000007: C3 RETN 00000008: 48 45 4C 4C-4F 2C "HELLO, 0000000E: 57 4F 52 4C 44 21-24 WORLD!$" Практически все символы этой программы нечитабельны, то есть не могут быть напрямую введены с клавиатуры. Здесь придется хитрить. Начнем с инструкции "MOV AH, 09h", заносящей в регистр AH код сервисной функции, ответственный за телетайпный вывод. Заглянув в Матрицу, мы с огорчением наблюдаем, что все команды пересылки регистров MOV/LEA имеют опкод превышающий 7Fh, то есть вылезающий за американскую часть кодировки ASCII. Ладно, не дают нам MOV'а и не надо! Используем математические операции! В нашем распоряжении есть INC reg16/DEC reg16, SUB и XOR. Не такой уж и богатый выбор! Поскольку, начальное значение регистра AX равно 0000h, для достижения задуманного, нам достаточно вычесть из него значение F700h, что равносильно сложением с 900h. В машинном представлении это будет выглядеть приблизительно так: [подготовка регистра AH в работе (предварительный вариант)] 00000000: 2D 00 F7 sub ax,0F700 Опс! Сразу два байта вылетают в штрафбат. Это 00h и F7h. Черт! Как же быть? Надо подумать… А что если вычислить значение не все сразу, а по частям? Короче говоря, нужно разложить F700h на ряд слагаемых, каждое из которых находилось бы в заданном интервале. Точнее даже не интервале, а каждый байт, входящий в слово, удовлетворял бы условию 80h > x > 1Fh. Чем не головоломка? Любители математики легко найдут строгое решение, а всем остальным придется довольствоваться методом перебора. Вот, например, если от F700h шесть раз отнять по 292Ah, останется всего 4, которые можно накрутить обычным DEC AX (впрочем, в данном случае "крутить" совершенно необязательно, поскольку при AH == 9, значение регистра AL игнорируется). В общем, наш аналог MOV AX, 9 будет выглядеть так: [подготовка регистра AH в работе (окончательный вариант)] 00000000: 2D 2A 29 sub ax,0292A 00000003: 2D 2A 29 sub ax,0292A 00000006: 2D 2A 29 sub ax,0292A 00000009: 2D 2A 29 sub ax,0292A 0000000C: 2D 2A 29 sub ax,0292A 0000000F: 2D 2A 29 sub ax,0292A 00000012: 48 dec ax 00000013: 48 dec ax 00000014: 48 dec ax 00000015: 48 dec ax А в текстовом виде: "-*)-*)-*)-*)-*)-*)HHHH". Для проверки работоспособности программы, запустим ее под отладчиком. [проверка работоспособности фрагменты программы под отладчиком] Смотрим и… ура! Получилось! Регистр AH послушно обратился в 09h и ни одного ASCII символа при этом не пострадало. Впрочем, это не единственный, и к тому же не самый короткий вариант. Можно, например, подтянуть регистр AL к 09h (в этом нам помогут команды INC AX), а затем переслать AL в AH. Стоп! Ведь команд пересылки у нас нет! Ни MOV, ни XCHG не работают! Однако в нашем распоряжении есть стек! А стек это могучая вещь! Команда PUSH reg16 забрасывает 16-разрядный регистр на верхушку, а POP reg16 стаскивает его оттуда. Команд для работы с 8-разярдными регистрами нет, а это, значит, что AL и AH мы никак не обменяем, во всяком случае если действовать в лобовую. Нет, тут нужен совсем другой подход! Что такое машинное слово? Совокупность двух байт, так? Причем, младший байт лежит по меньшему адресу, а за ним следует старший. |