CodeNet / Языки программирования / Ассемблер / "Вирусы", "Черви", "Драконы" и резиденты на службе прогресса.
"Вирусы", "Черви", "Драконы" и резиденты на службе прогресса.
Например: адрес п/п-ы прерывания N0 (реакция на ситуацию "деление на нуль") нахо- дится в таблице векторов прерываний по адресу 0000:0000--segment и 0000:0002--offset адрес п/п-ы прерывания N5 (печать экрана) находится в таблице векторов прерываний по адресу 0000:0014--segment и 0000:0016--offset Если мы замыслим заменить исходную стандартную п/п-му нашей собственной, мы должны: 1. Засунуть куда-то эту нашу п/п-му (и чтобы она там действительно была! И была готова обработать соотв. прер-е). 2. Изменить в таблице векторов адрес исходной стандартной п/п-ы на адрес нашей. Отметим, что пихать нашу п/п-му обработки прерыв-я в то-же место, где сидела исходная (иногда это физически возможно) лучше не надо, хотябы потому, что исходная нам ЕЩЕ ОЧЕНЬ МОЖЕТ ПРИГОДИТЬСЯ. ПОДРОБНО опишем механизм того, как PC реализует обработку прерываний: 1. PC прячет в стек регистр флагов. 2. PC прячет в стек содержимое регистра CS. 3. PC прячет в стек значение IP для следующей команды. 4. PC делает JMP Far :[] (джамп @@?? на п/п-у обработки прерывания). ПРИ ЭТОМ все операции 1-4 (PUSHF, PUSH CS,, JMP FAR ) содер- жатся неявно В ОДНОЙ КОМАНДЕ "INT" вызова прерывания (если прерыв-е реализу- ется программно). 5. Далее выполняется п/п-ма обработки прерывания. 6. П/п-ма обработки прерывания завершается специальной командой IRET, которая состоит из следующих действий: а) помещается из стека слово в регистр IP (и увелич. SP на 2) ---¬ б) помещается из стека слово в регистр CS (и увелич. SP на 2) +----¬ в) помещается из стека слово в регистр флагов (увелич. SP на 2)--- ¦ ¦ ¦ т.е. это- JMP Far обратно, в программу ¦ пользователя, на команду, следующую за той, ¦ которая осуществила вызвлв прерыв-я ¦ + восстановление флагов ====¬ ¦ г===>=====¬ AH INT 10h L===>===-.........¦ ......... PUSH CS ¦¦ г================¬ ¦ ¦L=====:[] ¦ ¦ L--T-- L---T- ¦ ¦ ¦ ¦ ¦ L-> путь выполнения @@?? ¦ ¦ ¦ слово по- L--- слово по адресу 0:40 адресу 0:42 Покажем, опираясь на описонный выше подробный механизм выполнения преры- ваний, как реализовать прер-е НЕ пользуясь классической командой INT. Печата- ем символ 'a' (см. пример 1): -----------------------------------------------------------¬ ¦ пример 1 ¦: L----------------------------------------------------------- TITLE Это - COM. программа N1 для демонстрации механизма вызова прерываний ASSUME CS:CodeSegment ;--------------------------------------------------------------------------- CodeSegment SEGMENT PARA ORG(100h) Start: MainProcedure PROC NEAR ; ; ; ;--запасаем в стеке информацию, необходимую для автоматического-¬ ; возврата из п/п-мы обработки прерыв-я ¦ ; ; ¦ PUSHF ; ¦ PUSH CS ; ¦ MOV BX,OFFSET after_int ;---¬ ¦ PUSH BX ;---------+-засунуть в стек IP точки возврата¦ ; ; из п/п-ы обработки прерывания ¦ ;---------------------------------------------------------------- ; MOV AH,0Eh ;входные MOV AL,61h ; параметры прерыв-я 10h ; ; XOR DX,DX ; DX = 0 напрямую, как Вы знаете, засунуть MOV DS,DX ; DS = 0 константу в регистр DS невозможно ; ; JMP dword ptr DS:[40h] ;длин. джамп по адресу, котор. находится ; ; в ячейках 0000:0040h и 0000:0042h after_int: ; RET ;закончить программу и вернуться в DOS ; ; ; MainProcedure ENDP ; CodeSegment ENDS END Start Пояснение: команда JMP dword ptr DS:[40h] (длинный JMP) осуществляет пе- реход по адресу, который хранится по адресу DS:[40h]. Это -- адрес п/п-мы об- работчика прерывания 10h. При этом в стек нужно уронить регистр флагов, ре- гистры CS и IP. Т.о. для того, чтобы команда IRET, завершающая обработчик прерывания 10h вернула управление нашей программе (на метку after_int), необ- ходимо сохранить в стеке определенную информацию (см. пример 1). -----------T------------------------------------------------------T------------ ¦ в случае, если кто-то не знаком с пакетом TASM, то: L------------------------------------------------------- А теперь -- создадим загрузочный модуль. Вы можете при помощи любого текстового редактора перетащить вышепред- ставленный текст исходника в текстовой файл с расширением .asm, например -- proba.asm А после этого сделать сначала .OBJ файл: tasm.exe /l proba.asm LT- L---- будет создан файл листинга proba.lst (там Вам покажут ошибки - если они есть) а потом - и .COM файл: tlink.exe /t proba.obj LT- L--- будет создан .COM файл Если Вы не знакомы с пакетом TASM и это - Ваш первый опыт, то можете по- ка-что пропустить (особо не задумываясь) всякие диррективы, предшествующие и последующие за текстом основной программы (TITLE ...; ASSUME ...; CodeSegment SEGMENT PARA; ORG(100h);MainProcedure ENDP; CodeSegment ENDS; END Start ). Поступайте с ними пока-что чисто механически. (На досуге можете почитать /1/,/2/,/7/) Итак, у нас есть готовый COM. модуль, который можно загрузить и выпол- нить. И он напечатает литеру 'a'. Замечательно! А вот тут пример того как решить ту же задачу не JMP Far-ом, а -- CALL Far-ом Разница в том, что в этом случае не нужно прятать в стек значения CS и IP для точки возврата из п/п-мы обработки прерывания (CALL Far оказался "ум- нее" - он делает это автоматически). -----------------------------------------------------------¬ ¦ пример 2 ¦: L----------------------------------------------------------- TITLE Это - COM. программа N2 для демонстрации механизма вызова прерываний ASSUME CS:CodeSegment ;--------------------------------------------------------------------------- CodeSegment SEGMENT PARA ORG(100h) Start: MainProcedure PROC NEAR ; ; ; XOR DX,DX ; DX = 0 MOV DS,DX ; DS = 0 ; PUSHF ;запасаем информацию, необходимую для автома- ; ;тического ыозврата из п/п-ы обработки прерыв-я ; MOV AH,0Eh ;входные MOV AL,61h ; параметры прерыв-я 10h ; ; CALL dword ptr DS:[40h] ;длинный вызов п/п-ы обработки прерыв-я 10h ; ;(по адресу 0:40h - адрес п/п-ы обработки) ; ;(два слова) ; RET ;закончить программу и вернуться в DOS ; ; ; MainProcedure ENDP ; CodeSegment ENDS END Start И последнее, что мы сделаем в этой главе, - научимся давать вызов преры- вания, не пользуясь INT-ом и не обращаясь каждый раз к таблице векторов. За- чем ? Это иногда полезно. Дело в том, что 'вирусы' и 'драконы' очень часто пользуются прерываниями и при этом должны оставаться необнаруживаемыми. А ес- ли прерывание дается командой INT, то охранные системы (всевозможные антиви- русные мониторы) тут же узнаЮт об этом и могут насторожиться. Если мы посто- янно пользуемся таблицей векторов, то где гарантия, что антивирусные мониторы межу делом не подложили в нее адрес СВОЕЙ п/п-мы, и, вместо того, чтобы полу- чить сервис п/п-мы-обработчика прерыв-я, мы попадаем в засаду. А если мы сох- раним в коде нашей программы адрес п/п-мы исходного обработчика прерыв-я (и в тот момент будем абсолютно уверены, что имеем дело не с 'оборотнем') то в дальнейшем сможем смело делать JMP Far или CALL Far без опасения быть обнару- женными. Вот готовая программа: (коментарии см. ниже) -----------------------------------------------------------¬ ¦ пример 3 ¦: L----------------------------------------------------------- TITLE Это - COM. программа N3 для демонстрации механизма вызова прерываний ASSUME CS:CodeSegment ;--------------------------------------------------------------------------- CodeSegment SEGMENT PARA ORG(100h) Start: MainProcedure PROC NEAR ; ; ; JMP over_data ; перепрыгнем через данные ; ; saved_int10: DD 0 ; данные (хранилище для адреса INT 10h ; ; -- 2 слова) ; over_data: XOR DX,DX ; DX = 0 MOV DS,DX ; DS = 0 ; ;--сохраняем в хранилище адрес исходн. п/п-мы INT 10h (2 слова)-¬ ; ; ¦ MOV AX,DS:[10h*4] ;мы указываем лишь порядко-¦ MOV word ptr CS:[saved_int10 ],AX ;вый номер прерывания ¦ MOV AX,DS:[10h*4+2] ; ¦ MOV word ptr CS:[saved_int10+2],AX ; ¦ ;---------------------------------------------------------------- ; ;--запасаем в стеке информацию, необходимую для автоматического-¬ ; возврата из п/п-мы обработки прерыв-я ¦ ; ; ¦ PUSHF ; ¦ PUSH CS ; ¦ MOV BX,OFFSET after_int ;---¬ засунуть в стек ¦ PUSH BX ;---+- IP точки возврата ¦ ;---------------------------------------------------------------- ; ; MOV AH,0Eh ;входные параметры MOV AL,61h ; прерыв-я 10h (печатать 'a') ; JMP dword ptr CS:[saved_int10] ;длин. джамп по адресу, котор. на- ; ; ходится теперь в хранилище after_int: ; ; saved_int10 ; RET ;закончить программу и вывалиться ; ; в DOS ; ; MainProcedure ENDP ; CodeSegment ENDS END Start Что здесь нового? Совсем немного. Во-первых мы перетащили адрес п/п-мы-обработчика прерывания 10h из таблицы векторов в тело нашей прогр-мы. Во-вторых мы теперь делаем JMP Far не на адрес в таблице векторов (dword ptr 00:[40h]), а на адрес, сохраненный в теле нашей прогр-мы (dword ptr CS:[saved_int10]). Есть одна громоздкость -- данные (saved_int10: DD 0) дол- жны определяться перед их использованием (MOV word ptr CS:[saved_int10 ],AX) и, следовательно, мы должны в начале через них перепрыгнуть. Этот бардак свя- зан с особенностями TASM-овской компиляции.