8086 машинного коду (MS-DOS .COM), 83 байти
Працює в DOSBox або улюбленому обчислювальному двигуні на пару. Рядок для опромінення задається як аргумент командного рядка.
Двійковий:
00000000 : EB 28 28 8A 0E 80 00 49 BD 83 00 B4 02 51 8A 0E : .((....I.....Q..
00000010 : 80 00 BE 82 00 AC 39 EE 74 04 88 C2 CD 21 E2 F5 : ......9.t....!..
00000020 : 59 45 B2 0A CD 21 E2 E5 C3 90 EB D7 D7 8A 0E 80 : YE...!..........
00000030 : 00 49 BD 83 00 B4 02 51 8A 0E 80 00 BE 82 00 AC : .I.....Q........
00000040 : 39 EE 74 04 88 C2 CD 21 E2 F5 59 45 B2 0A CD 21 : 9.t....!..YE...!
00000050 : E2 E5 C3 : ...
Читає:
cpu 8086
org 0x100
jmp part2
db 0x28
part1:
mov cl, [0x80]
dec cx
mov bp, 0x83
mov ah, 0x02
.l:
push cx
mov cl, [0x80]
mov si, 0x82
.k:
lodsb
cmp si, bp
je .skip
mov dl, al
int 0x21
.skip:
loop .k
pop cx
inc bp
mov dl, 10
int 0x21
loop .l
ret
nop
part2:
jmp part1
db 0xd7
mov cl, [0x80]
dec cx
mov bp, 0x83
mov ah, 0x02
.l:
push cx
mov cl, [0x80]
mov si, 0x82
.k:
lodsb
cmp si, bp
je .skip
mov dl, al
int 0x21
.skip:
loop .k
pop cx
inc bp
mov dl, 10
int 0x21
loop .l
ret
Спуститися
Активна частина дублюється так, що завжди є одна, не торкнута радіацією. Ми вибираємо здорову версію за допомогою стрибків. Кожен стрибок - це короткий стрибок, і так це лише два байти, де другий байт - це переміщення (тобто відстань до стрибка, зі знаком, що визначає напрям).
Ми можемо розділити код на чотири частини, які можна опромінити: стрибок 1, код 1, стрибок 2 та код 2. Ідея полягає у тому, щоб завжди використовувати чисту кодову частину. Якщо одна з частин коду опромінена, інша повинна бути обрана, але якщо один із стрибків опромінений, обидві частини коду будуть чистими, тому не має значення, яку з них обрано.
Причиною наявності двох частин стрибка є виявлення опромінення в першій частині, перестрибуючи його. Якщо перша частина коду опромінена, це означає, що ми вийдемо на один байт від позначки. Якщо ми переконаємося, що такий збір посадки вибирає код 2, а правильна посадка вибирає код 1, ми золото.
Для обох стрибків ми дублюємо байт зміщення, роблячи кожну частину стрибка 3 байтами. Це гарантує, що опромінення в одному з двох останніх байтів все-таки зробить стрибок дійсним. Опромінення в першому байті перешкоджає переходу стрибка взагалі, оскільки останні два байти формуватимуть зовсім іншу інструкцію.
Зробіть перший стрибок:
EB 28 28 jmp +0x28 / db 0x28
Якщо будь-який з 0x28
байтів буде видалений, він все одно перескочить на те саме місце. Якщо 0xEB
байт буде видалено, ми замість цього закінчимо
28 28 sub [bx + si], ch
що є доброякісною інструкцією щодо MS-DOS (інші аромати можуть не погоджуватися), і тоді ми переходимо до коду 1, який повинен бути чистим, оскільки шкода була в стрибку 1.
Якщо стрибок зроблений, ми приземляємось при другому стрибку:
EB D7 D7 jmp -0x29 / db 0xd7
Якщо ця послідовність байтів є недоторканою, і ми приземляємось прямо на позначку, це означає, що код 1 був чистим, і ця інструкція переходить до тієї частини. Дублюваний байт переміщення гарантує це, навіть якщо це був один із цих байтів переміщення, які були пошкоджені. Якщо ми або висадимо один байт (через пошкоджений код 1 або стрибок 1), або 0xEB
байт пошкоджений, два рейтингових байта також тут будуть доброякісними:
D7 D7 xlatb / xlatb
Незалежно від випадку, якщо ми закінчимо виконання цих двох інструкцій, ми знаємо, що або стрибок 1, код 1, або стрибок 2 були опромінені, що робить прохід до коду 2 безпечним.
Тестування
Наступна програма була використана для автоматичного створення всіх версій файлу .COM. Він також створює BAT-файл, який можна запустити в цільовому середовищі, який запускає кожне опромінене бінарне, і передає їхні виводи на окремі текстові файли. Порівнювати вихідні файли для перевірки досить просто, але DOSBox не має fc
, тому його не додавали до файлу BAT.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
FILE *fin, *fout, *fbat;
int fsize;
char *data;
if (!(fin = fopen(argv[1], "rb")))
{
fprintf(stderr, "Could not open input file \"%s\".\n", argv[1]);
exit(1);
}
if (!(fbat = fopen("tester.bat", "w")))
{
fprintf(stderr, "Could not create BAT test file.\n");
exit(2);
}
fseek(fin, 0L, SEEK_END);
fsize = ftell(fin);
fseek(fin, 0L, SEEK_SET);
if (!(data = malloc(fsize)))
{
fprintf(stderr, "Could not allocate memory.\n");
exit(3);
}
fread(data, 1, fsize, fin);
fprintf(fbat, "@echo off\n");
for (int i = 0; i < fsize; i++)
{
char fname[512];
sprintf(fname, "%03d.com", i);
fprintf(fbat, "%s Hello, world! > %03d.txt\n", fname, i);
fout = fopen(fname, "wb");
fwrite(data, 1, i, fout);
fwrite(data + i + 1, 1, fsize - i - 1, fout);
fclose(fout);
}
free(data);
fclose(fin);
fclose(fbat);
}