8086 машинный код (MS-DOS .COM), 83 байта
Запускается в DOSBox или в вашем любимом паровом вычислительном движке. Строка для облучения задается в качестве аргумента командной строки.
Binary:
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);
}