14.Testujemy samomodyfikujący się program.


Postanowiłem sprawdzić jak będą działać programy modyfikujące swój kod.

W przypadku nowszych procesorów (Athlon,Duron) anomalii nie było, ale na starym 386SX-LT(Amd) to już nie zawsze program działał jak powinien. Nie wiem czy wszystkie 386 tak działają czy tylko mój więc macie okazję sprawdzić.


W katalogu R5 są pierwsze programy testujące zachowanie się procesora.Jest tam plik bat z komentarzami do odpalenia wszystkich programów.
Program wstawia sobie w środek RET więc powinna wykonać się tylko połowa programu (jeden pasek na ekranie a nie dwa)


Pierwsze wnioski są takie, że po zmianie jakiegoś kodu musi być:
rozkaz hlt,
lub jump (krótki wystarczy)
lub przynajmniej 10 bajtów programu.
Jak wykazały dalsze próby czasem może być mniej jak się przestawi inne wcześniejsze rozkazy.

W katalogu R6 dalsze próby, tu zmieniałem kolejność niektórych rozkazów. Najpierw dałem te działające u mnie a potem te niedziałające, ale warto odpalić je kilka razy bo niektóre raz działają dobrze a raz nie.

Całkiem ciekawe jest że wstawienie sprawdzania czy się kod zmienił w pamięci działa dziwnie -
jak widać w R5CMP.com procesor najwidoczniej porównuje dane w cache-u, gdyż wykonuje program po staremu.

mov dl,[r1] ; pobieramy RET
mov [r2],dl ; wstawiamy w miejsce mov
c:    cmp dl,[r2]  ;porównujemy czy napewno się zmieniło
      jne c
r2:   mov di,84    ; a program sobie dalej leci

(...)

r1: ret


Na koniec zrobiłem jeszcze sprawdzenie czy ten zmieniony bajt trafia do pamięci - okazuje się że tak. Programy te są w katalogu R8.

Używałem fasmlite z flatassembler.net oraz dissasamblera ndisasm z nasm.us Para tych programów będzie też bohaterem następnej części.

=============================================================================

15.Piętnastobajtowy rozkaz procesora w dosie (Real-Mode)

Programy na procesorach x86 mogą być maksymalnie 15 bajtowe - niestety nie było przykładu takiego programu więc musiałem sam napisać.

W katalogu opc15 jest max15.com; Odpalamy go w ms-dosie; Pokażą się 2 kolorowe znaczki na ekranie - to znaczy rozkaz ten że działa.

No nie jest tak pięknie, ale po kolei...

Program miał coś wrzucić na ekran jednym rozkazem. Na początek użyłem adresu B8000h bo czemu nie? No niestety program taki zachowywał się dziwnie bo

albo się wieszał albo działał. Po wieku próbach skojarzyłem, że po uruchomieniu fasmlite działają, a po disasm nie, także pod windows powodują zatrzymanie programu oraz pokazują błąd właśnie na tym rozkazie gdzie jest ten adres.
Problematyczny więc był 32bitowy adres - jeśli był większy niź 0000FFFF to były problemy, jak nie był to zawsze było OK.

Rzućcie okiem na wydruk z ndisasm:

00000000  6631DB            xor ebx,ebx
00000003  6631FF            xor edi,edi
00000006  8EE3              mov fs,bx
00000008  BB00B8            mov bx,0xb800
0000000B  8EC3              mov es,bx
0000000D  BB0300            mov bx,0x3
00000010  BF0000            mov di,0x0
00000013  F026676681449F04  lock add dword [dword es:edi+ebx*4+0x4],0x35353131
         -31313535
0000001F  F026676681849F04  lock add dword [dword es:edi+ebx*4+0x104],0x34343232     - tu OK bo adres 0000xxxx
         -01000032323434
0000002E  F064676681845F02  lock add dword [dword fs:edi+ebx*2+0xb8002],0x6b4b694f   - ten rozkaz powoduje błąd  (pod windowsami,pod dosem czasem działał)
         -800B004F694B6B
0000003D  C3                ret


Okazuje się że w RealMode nie można stosować adresów większych od FFFF bo procesor tego nie przyjmie, ale dlaczego po fasmlite działał (pod dosem)? Wyczytałem że jest coś takiego jak Unreal-mode wtedy można używać dowolny adres większy niż FFFF (nie opiszę co to dokładnie ten Unreal-mode bo sam tego nie rozumiem).Fasmlite widocznie z niego korzysta i dlatego po nim wszystko działało. Za to ndisasm psuł wszystko (ten wymaga cwsdpmi więc  wchodził w protected-mode).

Na początku chciałem zrobić program składający się tylko z 2 linijek: tego długiego rozkazu no i oczywiście RET. (Zapomniałem, że do do adresu trzeba
jeszcze dodać segment*16) więc napisałem program mini15.exe generujący kod programu z tak dobranym adresem aby zawsze wyszło b8000 (początek ekranu) i w lewym górnym rogu pojawiły jakieś się znaki. Po jego asemblacji mamy program z 16-bajtowy program. Oczywiście wymaga to zerowych wartości EAX i EBX aby coś wyświetlił oraz UnrealMode akurat bezpośrednio po użyciu fasmlite mamy co potrzeba. Dołożyłem zeruj.com, który zeruje 32 bitowe rejestry - można użyć w razie potrzeby;Wyższe słowa rejestrów nie zawsze zawierają zera (ms-dos jest tylko 16 bitowy ale nigdy nie wiadomo co inne programy tam zostawiają).Programem reg.com można sprawdzić co jest domyślnie w rejestrach po załadowaniu programu *.com - jest to przerobiony yourhelp.com ze strony fysnet.net.

W katalogu opc15 znajduje się:
- max15.com - (ten program zawiera "15-bajtowca" zawsze zadziała, pod win 9x też, no może poza win7)
- mini15.exe - ten program wygeneruje opc15.asm (16 bajtowy program działający na komputerze na którym został wygenerowany)
- po poleceniu fasmlite opc15.asm otrzymamy opc15.com
- reg.com pokazuje zawartość rejestrów
- zeruj.com przydaje się czasem przed odpaleniem opc15.com

Post's attachments

opc15.rar 63.01 kb, liczba pobrań: 1 (od 2021-07-31) 

R1.jpg 105.51 kb, nikt jeszcze nie pobierał tego pliku. 

R4.jpg 108 kb, nikt jeszcze nie pobierał tego pliku. 

Tylko zalogowani mogą pobierać załączniki.

2

Pierwsze wnioski są takie, że po zmianie jakiegoś kodu musi być:
rozkaz hlt,
lub jump (krótki wystarczy)
lub przynajmniej 10 bajtów programu.
Jak wykazały dalsze próby czasem może być mniej jak się przestawi inne wcześniejsze rozkazy.

Standard. To się nazywa pipeline. Instrukcje są pobierane i wykonywane z wyprzedzeniem - jmp czyści tę kolejkę, bo musi. Wyczyszczenie kolejki to konieczność jej załadowania od nowa i mnóstwo zmarnowanych taktów zegara. Dlatego nie poleca się tworzenia kodu samomodyfikującego się na współczesnych maszynach.
No a jak się kolejki nie wyczyści, wykona się instrukcja sprzed modyfikacji.

Co innego 6502.

A jak ktoś chce na nowym sprzęcie, Parallax Propeller. On też ma kolejkę ale płytszą i 2 nopy wystarczą. Ale ma też instrukcje manipulujące rozkazami już pobranymi bezpośrednio w kolejce.

Hans 2004 napisał/a:

lub jump (krótki wystarczy)

Do tego bym się nie przywiązywał. Nie pamiętam czy 386 ma branch prediction, ale z dużym prawdopodobieństwem ma jego najprostszą formę, więc to jak jump na to wpłynie zależy od tego jak predykcji sie uda skok przewidzieć.

Atari: FireBee, (Falcon030 CT60e SuperVidel SvEthlana CTPCI), TT, (520ST Pak030 Frak PuPla Panther), (520ST 4MB ST RAM 8MB TT RAM CosmosEx SC1435), (1040STFM UltraSatan SM124), (1040STE 4MB ST RAM 8MB TT RAM CosmosEx NetUSBee SM144 SC1224), 260ST, 520 ST+, (MEGA ST SM125), (65XE Rapidus U1MB VBXE SIDE2 SIO2PC), (Jaguar SkunkBoard), Lynx II, 2x Portfolio