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
流れ落ちる
アクティブ部分は複製されているため、常に放射線の影響を受けない部分があります。ジャンプを使用して健全なバージョンを選択します。各ジャンプは短いジャンプであるため、2バイトの長さしかありません。2番目のバイトは変位(つまり、方向を決定する符号付きのジャンプまでの距離)です。
コードを照射可能な4つの部分に分割できます。ジャンプ1、コード1、ジャンプ2、およびコード2。アイデアは、クリーンなコード部分が常に使用されるようにすることです。コード部分の1つが照射された場合、もう1つを選択する必要がありますが、ジャンプの1つが照射された場合、両方のコード部分がきれいになるため、どちらを選択してもかまいません。
ジャンプ部分が2つある理由は、最初の部分のジャンプを飛び越えて検出するためです。最初のコード部分が照射されると、マークから1バイト離れて到着することを意味します。そのような失敗した着陸がコード2を選択し、適切な着陸がコード1を選択することを確認すると、黄金色になります。
両方のジャンプについて、変位バイトを複製し、各ジャンプ部分を3バイト長にします。これにより、最後の2バイトのいずれかを照射しても、ジャンプが有効になります。最後の2バイトは完全に異なる命令を形成するため、最初のバイトの照射はジャンプの発生をまったく停止します。
最初のジャンプをしてください:
EB 28 28 jmp +0x28 / db 0x28
いずれかの0x28
バイトが削除されても、同じ場所にジャンプします。場合は0xEB
、バイトが削除され、我々は代わりになってしまいます
28 28 sub [bx + si], ch
これはMS-DOS上の良性の命令であり(他のフレーバーは同意しない場合があります)、コード1に進みます。損傷はジャンプ1であったため、クリーンでなければなりません。
ジャンプが行われた場合、2回目のジャンプで着陸します。
EB D7 D7 jmp -0x29 / db 0xd7
このバイトシーケンスに問題がなく、マークに到達した場合、コード1はクリーンであり、この命令はその部分にジャンプして戻ります。複製されたディスプレイスメントバイトは、これらのディスプレイスメントバイトのいずれかが破損した場合でも、これを保証します。(損傷したコード1または1のジャンプのために)1バイトを着陸するか、その0xEB
バイトが損傷したバイトである場合、残りの2つのバイトもここで問題ありません。
D7 D7 xlatb / xlatb
いずれにせよ、これら2つの命令を実行することになった場合、ジャンプ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);
}