Персональный раздел проекта Персональный форум проекта В общем, сидел вчера и пытался реализовать свою давнюю идею использования x86-инструкций в качестве байт-кода. Получился, в общем, очень компактный текст - 160 строчек. Работает очень просто: Все операции, напрямую не обращающияся к памяти, остаются как есть. Остальные - модифицируются. Так: 1) Стек отсутствует. Т.е. ESP в шапке x86-кода сохраняется и дальше ESP работает как простой регистр; 2) Операции PUSH/POP официально не предусмотрены. Хотя транслятор их поддерживает: Извлекает из памяти реальный ESP, производит PUSH/POP, проверяет BOUND ESP и возвращает на место. Операций много, производительность страдает. Но, как уже сказал, операции не предусмотрены концепцией; 3) Инструкция LEA остаётся как есть. Она же не обращается к памяти, а возвращает вычисленное значение; 4) Инструкции CALL, JMP, Jcnd и LOOP работают иначе. Вместо переходов по смещению указывается уровень блочной вложенности (break 1-127 / continue 1-127). Так, можно выйти из текущего блока (break 1), из родительского блока текущего (break 2) и т.д. до 127 уровня. Отчего метки попросту отсутствуют и можно строить алгоритм произвольной блочной реализацией. CALL как такового нету, хотя были варианты, такие как Код (Text): CALL L4 ; Указатель на строку CALL L7 ; Вызов API ... L4: db '/dev/con',0 ; где в строке указывается путь к файлу/устройству L7: db 'SetPixel',0 но на деле оказалось непрактичным и уязвимым; 5) Доступа к памяти через указатели не предусмотрено и это запрещено. Вместо ячеек памяти предоставляются готовые структуры. Так, сегменты CS,DS,ES,FS,GS,SS - всего лишь указывают на одну из шести структур, что вполне достаточно. Транслятор же не включает префиксы сегментов в x86-код, но ориентируется по ним для коррекции адреса. Так Код (Text): MOV ES,L9 ; Указываем на имя структуры. Здесь - WAVEFORMATEX MOV EAX,ES:[3] ; Условный доступ к третьему члену структуры. Здесь - nSamplesPerSec MOV ECX,ES:[0] ; Эквивалентно MOV ECX,sizeof(WAVEFORMATEX) ... L9: db '#WAVEFORMATEX',0 на практике сложно реализуется, по-этому все сегменты - статические и инициализируются при компиляции скрипта. Тем самым, вместо 6 из бесконечного числа структур мы имеем только 6!; 6) Доступ к памяти без префикса - доступ к локальным переменным. Например: Код (Text): MOV EAX,[EDX+0x12345678] ; Эквивалентно EAX = EDX + local_variables[0x12] - local_variables[0x45] ^ 0x78 ... В теории всё более-менее складно. На практике - куча проблем. И проблемы не в самой сложности реализации. А в том, на сколько корректно использовать ссылки на переменные. Т.е. ясно, что ни у какой структуры не может быть 4 млрд. членов. Тогда как распорядиться с этим 32-битным адресом? Усечь количество членов до 256? Не мало ли? А если 65536? Не много ли? А остальные 16-24 бита для чего? Т.е. стандарт не выработан... В целом, всё более-менее скриптуется и работает с бешенной скоростью. Только вот спец-ассемблер пока не написал. И систему взаимодействия не придумал. Так-как скрипт не имеет официального права на какой-либо внешний вызов, вся такая скрипт-программа разбивается на отдельные замкнутые скрипты, где возможен только цикл (и то контроллируется подсчётом тактов процессора). Т.е. пока могу запустить один-другой скрипт. Но вот интегрировать их чем? Текстовым скриптом? Уже парсер нужен... В любом случае, идее лет так около пяти. А за реализацию взялся вчера. Может кто поделится опытом? Не в плане реализации. А в плане использования 32-битных адресов и т.д. Спасибо!
Прочитал по -диагонали ничего не понял, хотя кажется что-то интересное. Ты объясни что ты хочешь сделать, цель??? Обфускатор? Просто сжать код? Где это будет полезно?
Типо, да. Своеобразная VirtualMachine. Правда, подчёркиваю, своеобразная. Пока скрипт пишу пользуясь ассемблером HIEW-редактора. Главная цель: Проработать концепцию так, чтобы она обгоняла в скорости любые скрипты Flash и Java, а так же имела зачатки конструкций высокоуровневых языков. Так, чтобы в любом аудио плеере с DSP-фильтрами можно было использовать собственный скрипт на этом x86-скрипт языке. Скажем, под WinAMP написать DSP-фильтр крошечный с таким компилятором, загрузить его и потом только подставлять в его конфигурации подобные скрипты. Он будет их компилировать и потом передавать управление тому коду. Просто давно об этом думал. Чтобы фильтр свой в Visual C++ не нужно было писать каждый раз, а просто в дампе его набросать. Скажем, очень полезно для тех, кто не владеет VC или вообще не умеет программировать. Допустим, есть он-лайн сервис со скрипт-генератором на заказ пользователя. Тот вводит парочку-другую формул, а сценарий сервера компилирует это в x86-скрипт. Тем самым, получается одновременно и машинный код, и инкапсуляция без возможности подложить вирус! То же самое и с фильтрами VirtualDub. Там вообще можно не только видео фильтры писать такими скриптами, сохраняя предельную скорость, но и на лету их модифицировать. Т.е. такой же маленький фильтр с компилятором всегда следит за изменением указанного файла со скриптом. Как только мы скрипт вновь сохраняем, фильтр автоматически читает его, компилирует и передаёт ему управление. Если скрипт написан неверно, синтаксически некорректно, фильтр сам сообщит об этом прямо в кадре. Тем самым, идея ещё и в том, что можно находиться всегда в одном окне HIEW. Но при сохранении эффект будет сразу виден или слышен в любом плеере. На лету! Или анимированный Desktop. Как в WinAMP'е, закрасив рабочий стол OverLay-цветом, можно скриптом на лету проверять любые графические эффекты. В общем, спектр применений довольно широкий. Всё от фантазии зависит. Я сейчас перечислил лишь несколько их развлекательных направлений. Но умелыми руками можно будет писать всё, от Тетрисов до он-лайн игр или 3D-приложений. При этом, сохраняя практически 100% машинной производительности! Если идея будет удачной, такие скрипты можно писать не только x86-кодом, но и кодом других процессоров. Без всяких разных Java.
боюсь, что и тут вас опередили. например http://ipn.caerwyn.com/2009/01/lab-92-vxinferno.html не вчитывайтесь в особенности реализации. просто посмотрите на пример использования сандбокс либы
Сегодня долго возился с механизмом организации блочной структуры. Будь в основе ассемблер, было бы проще. Но здесь, для Break-выходов из блоков, необходимо знать размер блока и где он завершается. А это требует либо двух проходов, либо построения таблиц, где надо будет корректировать адреса все ветвлений внутри блока по его закрытию. Первое - требует памяти. Второе - сложно и требует ещё больше памяти. Решил поступить проще: При открытии блока записывать туда инструкцию выхода из него. Тем самым, все операции выхода из блока ссылаются на неё. А с закрытием блока просто в той инструкции корректируем адрес. И вот что получилось: Данный скрипт-код Код (Text): B9 10 00 00 00 |MOV ECX,16 EB 80 |BEGIN BA 10 00 00 00 | MOV EDX,16 EB 80 | BEGIN F7 C2 01 00 00 00| TEST EDX,1 75 80 | BEGIN NZ 03 C3 | ADD EAX,EBX EB 00 | END 4A | DEC EDX 75 00 | END NZ E2 00 |END LOOP транслируется в Код (Text): B9 10 00 00 00 | MOV ECX,16 EB 05 | JMP L1 E9 2A 00 00 00 | JMP Q1 BA 10 00 00 00 |L1: MOV EDX,16 EB 05 | JMP L2 E9 19 00 00 00 | JMP Q2 F7 C2 01 00 00 00|L2: TEST EDX,1 75 05 | JNZ L3 E9 02 00 00 00 | JMP Q3 03 C3 |L3: ADD EAX,EBX 4A |Q3: DEC EDX 75 02 | JNZ C2 EB 05 | JMP Q2 E9 E7 FF FF FF |C2: JMP L2 E2 02 |Q2: LOOP C1 EB 05 | JMP Q1 E9 D2 FF FF FF |C1: JMP L1 |Q1: и благополучно работает. Правда, выгладит безобразным лабиринтом. Но на данной стадии развития концепции это подойдёт.
Paguo_86PK все понятно, кроме одного - почему MOV EDX,16 разве это удобно? вы ж все равно транслируете. почему не r3 = 16 r3 += 2 r3:r0 *= r1 << r3 помойму, все нагляднее и короче ворд от дворда - r3w, дворды - r3bh и r3bl итд еще неплохо бы както симметризовать x86. чтоб все полностью интуитивно было
Хм. Я понял, Вы предлагаете переименовать регистры. Как я сказал, на данном этапе с работой в Hiew-редакторе, имена регистров трогать не следует. Для таких целей нужно писать уже собственный ассемблер и к нему - подобие Си--, на свой js-вариант которого я давал ссылку некогда... С именами регистров я вообще делал вариант: Код (Text): ; Здесь: ; EAH -> HIWORD(EAX) ; EAL -> LOWORD(EAX) or AX ; EDH -> HIWORD(EDX) ; EDL -> LOWORD(EDX) or DX ; В одной из тем я уже писал об этом. ; Тогда: ; Имеем запись x86-скрипта: 66 12 E2 ADC EAH,EDL ; Сложить старшую половину EAX с младшей EDX ; необходимо переобразовать в нечто подобное: XCHG ESP,[real_stack] ; Нам понадобится стек! PUSH WORD 0x0000 ; Первые 16-бит - 0 PUSH DX ; Старшие - DX ADC EAX,[ESP] ; Указанная операция LEA ESP,[ESP+4] ; Удаляем из стека. LEA не влияет на флаги! XCHG ESP,[real_stack] ; Восстанавливаем ESP А такие конструкции, как 66 12 .. ни один ассемблер/дизассемблер не переваривает. Сейчас же, когда с блочными механизмами практически разобрался, нужно обязательно разрабатывать именно свой ассемблер с перевариванием и таких регистров. Набор полный регистров получается таким: EAX(EAH:EAL(AH:AL)), ECX(ECH:ECL(CH:CL)), EDX(EDH:EDL(DHL)), EBX(EBH:EBL(BH:BL)), ESP, EBP, ESI, EDI. Так что переименовывать их будет не просто. Например: Код (Text): EAX -> ABC | EAH -> A | EAL -> BC | AH -> B | AL -> C ECX -> IJK | ECH -> I | ECL -> JK | CH -> J | CL -> K EDX -> XYZ | EDH -> X | EDL -> YZ | DH -> Y | DL -> Z EBX -> LMN | EBH -> L | EBL -> MN | BH -> M | BL -> N ESP -> S EBP -> P ESI -> Q EDI -> R вариант довольно кривой и не интуитивный с непривычки. Хотя скрипт писать без всяких Hi-Lo-половинок практичнее. Нужно лишь проработать как следует систему имён регистров. Буду рад содействию. А всякие r3w r2l - из ряда RISC-технологий. Тяжело запомнить в скрипте назначения проиндексованных регистров. Легче I(iterrator) - ECX, XY(position) - EBX, R(result) - EAX и т.д. Но всё это надо продумать хорошо и складно...
зачем вам какоето кривое С-- ? если уж вы сочиняете вариацию языка, ассемблера или нет - не важно, то освойте какой нибудь генератор компиляторов/интерпретаторов. увидите, что особой проблемы написать компилер или интерпрер для вашего языка нету. а зачем вам чтото запоминать? просто предусмотрите именование. а однородное называние однородных частей очень упрощает запоминание и работу. попробуйте сами. можно еще рассмотреть регистр как структуру из меньших частей, те r1 -> r1.h r1.l -> r1.l.h r1.l.l можно включить разные представления. например r1.w.l или r1.b.3 . итд. если уж фантазировать, то фантазировать. например, посмотрите до чего дофантазировался один товарищ насчет avr ассемблера http://www.algrom.net/russian.html
Да, хороши ссылочки! Бр-ррр... Жуть. ИМХО, не мой уровень. Хоть то, что я сейчас делаю, и может показаться запутанным и сложным, на самом деле весит уже всего 285 Си-строк. Немного поработать, появится поддержка и FPU, и MMX. Но меня сейчас волнует вопросы, с которых я и начал тему: Адресация данных и интеграция нескольких скриптов. Вообще в качестве примера, вы не поверите, я использовал концепцию паттернов трекерной музыки. Тем самым, так или иначе, у меня скрипт - паттерн. А значит нужно продумать механизм снятие результата работы одного скрипта и передачи этих данных в другой. Помните пассивный процессор? Какрас в нём я пытался проработать этот механизм. Кстати, бью транслятор и вдруг понимаю, что в начале любого блока могу организовать приостановку скрипт по временному условию, а потом продолжить работу с точки разрыва. А значит, несколько скриптов по-очереди могут работать независимо друг от друга продолжительный период! Ну, а толку то? Если нет концепции некоего скрипта-супервизора... Надо бы серъёзнее взяться за вопрос... Хотя, как я говорил выше, для DSP-фильтрации или видео-эффектов достаточно одного скрипта. С одной стороны, довольно одного скрипта и не нужно больше ничего ухищрять. А с другой, следовало бы предусмотреть и возможность строить сеть таких скриптов для решения более сложных задач. Короче, сам себя запутал! Поставлю пока такие сверх-задачи: 1) Объявление входных-выходных массивов, с чем я таки и бьюсь; 2) Перезагрузку имён регистров. Хотя, чушь, вьювер ещё не написан же...
Хм... Помнится, все смеялись над этой моей задумкой... А теперь, сама Intel! Ну, значит, я тут, как говорит Зелёный из "Тайны третьей планеты": Здесь мне делать нечего... Кстати, я забыл сказать, что проект данной темы - приложение к соседней. А если копать глубже, то первые шаткие шаги к реализации концепции моей OS... Как у меня часто это происходит, я одновременно развиваю несколько проектов и не могу сосредоточиться на чём-то одном. Так-что, сейчас мне необходимо остановиться и передохнуть. Нужно поработать именно в плане той OS, чтобы реализовать базовые механизмы именно в рамках текущего проекта... 1) Вместо драйверов - x86-скрипты. Клавиатура, звук, дисплей - всё в скриптах; 2) Оболочка (интерфейсы, обозреватели и т.д.) - всё x86-сценарии, как в Windows файлы .htt для папок; 3) Все исполняемые файлы (драйверы/приложения) - всегда каждый в своём .tar.gzip архиве. Т.е. специальных форматов файлов (как dll/lib/obj/so) в принципе нет. Большое приложение представляет большой .tar с кучей скриптов и сценариев, а так же ресурсов (подобия .res) во вложенных .tar: Звуковой поток, картинка, иконка - всё тот же .tar, но с файлами .raw (дамп чего угодно: видео/аудио) и .dec (декларация/описание ресурса как .ini или .conf) в текстовом виде с иерархией и ссылками на драйвер (например /dev/audio), чтобы OS сама не угадывала, а сразу передала на обработку нужному драйверу... Здесь x86-сценарий на уровень выше x86-скрипта. Как я уже говорил выше, несколько скриптов связять в один (сценарий) для меня сейчас проблема. Стандарт не разработан вообще. Ясно только одно. Сейчас я копаюсь с решением 1). До остальных пунктов очень далеко...
В общем, с теорией удалось более-менее разобраться. На данный момент исправно работает следующий пример: Код (Text): for(long ecx = 256; ecx; -- ecx) for(long edx = 256; edx; -- edx) SetPixel(hdc, edx, ecx, GetPixel(hdc, edx, ecx) + 0x030201); который представляется как: Код (Text): ; x86-Assembler ; x86-Script MOV DS,szGDI |INCLUDE DS,"/dev/gdi" JMP $-128 |BEGIN MOV ECX,256 | MOV ECX,256 JMP $-128 | BEGIN MOV EDX,256 | MOV EDX,256 JMP $-128 | BEGIN LOCK | LOCK MOV EAX,DS:[ECX][EDX][szGetPixel] | MOV EAX,DS.GetPixel(EDX,ECX) ADD EAX,0x00030201 | ADD EAX,0x00030201 LOCK | LOCK MOV DS:[ECX][EDX][szSetPixel],EAX | MOV DS.SetPixel(EDX,ECX),EAX DEC EDX | DEC EDX JNE $+0 | END NE LOOP $+0 | END LOOP JMP $+0 |END HLT |HLT szGDI DB '/dev/gdi',0 szGetPixel DB 'GetPixel',0 szSetPixel DB 'SetPixel',0 и компилируется в Код (Text): 00439B62 60 pushad 00439B63 9C pushfd 00439B64 89 25 98 9A 43 00 mov dword ptr [ScriptCode (00439a98)],esp 00439B6A BC A4 9A 43 00 mov esp,offset ScriptCode+0Ch (00439aa4) 00439B6F 9D popfd 00439B70 61 popad 00439B71 8B 25 B4 9A 43 00 mov esp,dword ptr [ScriptCode+1Ch (00439ab4)] 00439B77 FF 25 A0 9A 43 00 jmp dword ptr [ScriptCode+8 (00439aa0)] 00439B7D 89 25 C8 9A 43 00 mov dword ptr [ScriptCode+30h (00439ac8)],esp 00439B83 BC C8 9A 43 00 mov esp,offset ScriptCode+30h (00439ac8) 00439B88 60 pushad 00439B89 9C pushfd 00439B8A 8B 25 98 9A 43 00 mov esp,dword ptr [ScriptCode (00439a98)] 00439B90 FF 35 C8 9A 43 00 push dword ptr [ScriptCode+30h (00439ac8)] 00439B96 8F 05 B4 9A 43 00 pop dword ptr [ScriptCode+1Ch (00439ab4)] 00439B9C 9D popfd 00439B9D 61 popad 00439B9E C3 ret 00439B9F EB 05 jmp ScriptCode+10Eh (00439ba6) 00439BA1 E9 00 00 00 00 jmp ScriptCode+10Eh (00439ba6) 00439BA6 B9 00 01 00 00 mov ecx,100h 00439BAB EB 05 jmp ScriptCode+11Ah (00439bb2) 00439BAD E9 C6 00 00 00 jmp ScriptCode+1E0h (00439c78) 00439BB2 BA 00 01 00 00 mov edx,100h 00439BB7 EB 05 jmp ScriptCode+126h (00439bbe) 00439BB9 E9 B1 00 00 00 jmp ScriptCode+1D7h (00439c6f) 00439BBE 89 25 C8 9A 43 00 mov dword ptr [ScriptCode+30h (00439ac8)],esp 00439BC4 BC C8 9A 43 00 mov esp,offset ScriptCode+30h (00439ac8) 00439BC9 60 pushad 00439BCA 9C pushfd 00439BCB 8B 25 98 9A 43 00 mov esp,dword ptr [ScriptCode (00439a98)] 00439BD1 FF 35 C8 9A 43 00 push dword ptr [ScriptCode+30h (00439ac8)] 00439BD7 8F 05 B4 9A 43 00 pop dword ptr [ScriptCode+1Ch (00439ab4)] 00439BDD 9D popfd 00439BDE 61 popad 00439BDF FF 35 BC 9A 43 00 push dword ptr [ScriptCode+24h (00439abc)] 00439BE5 FF 35 C0 9A 43 00 push dword ptr [ScriptCode+28h (00439ac0)] 00439BEB FF 35 9C 9A 43 00 push dword ptr [ScriptCode+4 (00439a9c)] 00439BF1 E8 65 37 AE 77 call 77F1D35B 00439BF6 A3 C4 9A 43 00 mov [ScriptCode+2Ch (00439ac4)],eax 00439BFB 60 pushad 00439BFC 9C pushfd 00439BFD 89 25 98 9A 43 00 mov dword ptr [ScriptCode (00439a98)],esp 00439C03 BC A4 9A 43 00 mov esp,offset ScriptCode+0Ch (00439aa4) 00439C08 9D popfd 00439C09 61 popad 00439C0A 8B 25 B4 9A 43 00 mov esp,dword ptr [ScriptCode+1Ch (00439ab4)] 00439C10 05 01 02 03 90 add eax,90030201h 00439C15 89 25 C8 9A 43 00 mov dword ptr [ScriptCode+30h (00439ac8)],esp 00439C1B BC C8 9A 43 00 mov esp,offset ScriptCode+30h (00439ac8) 00439C20 60 pushad 00439C21 9C pushfd 00439C22 8B 25 98 9A 43 00 mov esp,dword ptr [ScriptCode (00439a98)] 00439C28 FF 35 C8 9A 43 00 push dword ptr [ScriptCode+30h (00439ac8)] 00439C2E 8F 05 B4 9A 43 00 pop dword ptr [ScriptCode+1Ch (00439ab4)] 00439C34 9D popfd 00439C35 61 popad 00439C36 FF 35 C4 9A 43 00 push dword ptr [ScriptCode+2Ch (00439ac4)] 00439C3C FF 35 BC 9A 43 00 push dword ptr [ScriptCode+24h (00439abc)] 00439C42 FF 35 C0 9A 43 00 push dword ptr [ScriptCode+28h (00439ac0)] 00439C48 FF 35 9C 9A 43 00 push dword ptr [ScriptCode+4 (00439a9c)] 00439C4E E8 8E 37 AE 77 call 77F1D3E1 00439C53 60 pushad 00439C54 9C pushfd 00439C55 89 25 98 9A 43 00 mov dword ptr [ScriptCode (00439a98)],esp 00439C5B BC A4 9A 43 00 mov esp,offset ScriptCode+0Ch (00439aa4) 00439C60 9D popfd 00439C61 61 popad 00439C62 8B 25 B4 9A 43 00 mov esp,dword ptr [ScriptCode+1Ch (00439ab4)] 00439C68 4A dec edx 00439C69 0F 85 4F FF FF FF jne ScriptCode+126h (00439bbe) 00439C6F E2 02 loop ScriptCode+1DBh (00439c73) 00439C71 EB 05 jmp ScriptCode+1E0h (00439c78) 00439C73 E9 3A FF FF FF jmp ScriptCode+11Ah (00439bb2) 00439C78 C7 05 A0 9A 43 00 00 mov dword ptr [ScriptCode+8 (00439aa0)],0 00439C82 E9 F6 FE FF FF jmp ScriptCode+0E5h (00439b7d) Здесь префикс LOCK указывает на непосредственно включение кода вызова функции в код скрипта. Иначе скрипт будет приостанавливаться для интерпретации вектора операции. Код при этом выглядит вот так: Код (Text): 00439BC0 C7 05 A0 9A 43 00 CF mov dword ptr [ScriptCode+8 (00439aa0)],offset ScriptCode+137h (00439bcf) 00439BCA E9 AE FF FF FF jmp ScriptCode+0E5h (00439b7d) 00439BCF 8B 84 11 .. .. .. .. mov eax,dword ptr [ecx+edx+...] 00439BD6 05 01 02 03 90 add eax,90030201h 00439BDB C7 05 A0 9A 43 00 EA mov dword ptr [ScriptCode+8 (00439aa0)],offset ScriptCode+152h (00439bea) 00439BE5 E9 93 FF FF FF jmp ScriptCode+0E5h (00439b7d) 00439BEA 89 84 11 .. .. .. .. mov dword ptr [ecx+edx+...],eax К тому же, почти разобрался с проблемой интеграции скриптов. На данный момент пока не реализовано, но уже решено, что другие скрипты будут подключаться декларацией '/scp/...' и работать как обычные функции. Со стороны посмотреть - ужасный код. Нужно будет копать в сторону CreateProcess и VDMCONTEXT, о которых я очень мало знаю на настоящий момент... [o]* * *[/o] Итак, продолжая углубляться в развитие x86-скриптов. Согласно идеалогии x86-скриптов, а именно, использования машинного x86-кода в качестве байт-кода псевдо-виртуальной машины, все вычислительные инструкции кодируются как есть. За исключением векторов памяти, которые переназначаются строго в соответствиях правил безопасности. А так же, работа с сегментными регистрами, о которых давно забыли в прикладном программировании многих операционных систем, также активно задействована. Так, рассмотрим следующий пример: Код (Text): JMP $-128 |BEGIN ; <- First-sub-script biginning <────────────┐ MOV DX,DS | MOV EDX,Z │ MOV BP,ES | MOV EBP,Y │ MOV BX,GS | MOV EBX,X │ MOV CX,CS | MOV ECX,N │ MOV SP,SS | MOV ESP,L │ MOV AX,FS | MOV EAX,M │ TEST AH,0xFF | TEST AH,0xFF ; if(simple) { │ JZ $-128 | BEGIN Z ; switch(AL) { │ CMP AL,0x88 | CMP AL,0x88 │ JZ $-128 | BEGIN Z ; case "MOV mem8,reg": │ ... | ... ; ... │ JMP $-3 | BREAK 3 ────┐ ; return; │ JMP $+0 | END │ ; break; │ CMP AL,0x8A | CMP AL,0x8A │ │ JZ $-128 | BEGIN Z │ ; case "MOV reg,mem8": │ ... | ... │ ; ... │ JMP $-3 | BREAK 3 ────┤ ; return; │ JMP $+0 | END │ ; break; │ JMP $-2 | BREAK 2 ────┐ │ ; } │ JMP $+0 | END │ │ ; } else │ CMP AL,0xAC | CMP AL,0xAC │ │ ; switch(AL) { │ JZ $+0 | BEGIN Z │ │ ; case "SHRD mem32,reg,n": │ ... | ... │ │ │ JMP $-2 | BREAK 2 ────┤ │ ; break; │ JMP $+0 | END │ │ ; } │ JMP $+0 |END <═════════╧─┘ ; <- End of first-sub-script │ JMP $-128 |BEGIN ; <- Second-sub-script biginning │ ... | ... │ JMP $+0 |END ; <- End of second-sub-script │ JMP $-128 |BEGIN ; <- Main script │ MOV ES,[L1] | INCLUDE ES,"/scp/:1" │ MOV EDX,0x20454458 | MOV EDX,0x20454458 │ MOV EBX,0x20454258 | MOV EBX,0x20454258 │ MOV EBP,0x20454250 | MOV EBP,0x20454250 │ SHRD ES:[EBP][EBX*4][0x12345678],EDX,23| SHRD ES:[EBP][EBX*4][0x12345678],EDX,23 ─────────────┘ JMP $+0 |END HLT |HLT ; <- Script end L1: DB '/scp/:1',0 Здесь видно, что обращение к памяти через вектор [EBX][EDX*4][0x12345678] вызывает под-скрипт текущего файла, а все операнды команды получают зеркала в виде сегментных регистров: Код (Text): ; EDX=0x20454458 ; EBX=0x20454258 ; EBP=0x20454250 ; Flags=11000111 0F AC 94 9D 78 56 34 12 17 |SHRD [EBP][EBX*4][0x12345678],EDX,23 -----------------------------> M [Y][X*M][L],Z,N -----------------------------> FS [ES][GS*FS][SS],DS,CS Virtual Y or ES=0x20454250: Base register Virtual N or CS=0x00000017: Instruction data Virtual L or SS=0x12345678: Label/Displace Virtual Z or DS=0x20454458: Source/Destination register Virtual M or FS=0xC7800FAC: Flags & Multiplicator (1,2,4,8) & Instruction code Virtual X or GS=0x20454258: Index register Значения реальных регистров заносятся напрямую в рабочую область вызываемого скрипта, а по-завершению - считываются обратно в регистры текущего скрипта. Тем самым, ни один из скриптов не может знать реально указанных регистров, но может изменять их. Исключения представляют инструкции работы со строкой, где регистры ESI, EDI и ECX (при REPcnd) также доступны по-умолчанию. Таким простым способом построено всё взаимодействие скриптов. У каждого скрипта имеется своя маленькая статическая память объёмом до 256 ячеек. Они доступны по векторам [EBP-128]..[EBP+127].
Итак. Можно подвести некоторые итоги моей работы. Вот текущая версия Windows-оболочки и он-лайн редактор dump'а с дизассемблером. Так же стало доступным: • Персональный раздел проекта • Персональный форум проекта На текущий момент разработаны некоторые правила структуры x86-скриптов: • В одном файле допускается до 256 под-скриптов; • Каждый под-скрипт начинается открытием блока с помощью BEGIN или HOOK; • BEGIN-открытый скрипт работает как функция и имеет свой порядковый индекс "/app.#n"; • HOOK-открытый скрипт служит для внедрения в системный процесс (окраска заголовка окна, события мыши и клавиатуры); • Главный скрипт описывается всегда в конце. Так, допустим, нам надо окрашивать поверхность окна (WM_ERASE) своим способом. Объявляем Hook на нужное событие и всё. Сам по-себе Hook не доступен другим скриптам. Но когда приложение начнёт закраску окна и обнаружит наличие Hook'а на это событие, то каждый пиксел окна будет окрашиваться цветом, возвращаемым в Hook-скрипте. На практике получаются очень сильные тормоза, из-за GetPixel/SetPixel. Однако, расчёт же на то, что концепция скриптов взята из идеологии самой операционной системы. А значит они должны внедряться прямо в системный код с доступом к видео памяти. Так что, пока как могу, занимаюсь реализацией.
Нет конца моей глупости, оказывается! На одном форуме заметили, что я изобрёл самый настоящий велосипед - Шейдеры, только под x86! Хм. А ведь раньше я и понять никак не мог, что за шейдеры такие в современных видео адаптерах? Получается, ещё в 1997, когда я сидел ещё за XT-совместимом в DOS 3.1, уже тогда задумывался о подобных технологиях? Странно. Действительно, почему в Windows нету Шейдеров? Чтобы, скажем, внешний интерфейс выглядил как в MacOS, нужно ставить всякие WinBlings и Astone. Тогда как можно было просто описать вид в скрипте. Хм. Замачиво, по-крайней мере, для меня. Вот тому-то я и занялся этими x86-скриптами. А ныне - x86-шейдерами.
Шейдеры - не моя идея. На одном из форуме меня поздравили с изобретением велосипеда: Шейдеров под Виндовс. Именно тогда я зашёл в википедию и подробно прочитал про Шейдеры. И действительно, сходство есть.