รหัสเครื่อง 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
rundown
ส่วนที่ใช้งานอยู่จะถูกทำซ้ำเพื่อให้ไม่มีการสัมผัสกับรังสีหนึ่งครั้ง เราเลือกเวอร์ชั่นที่สมบูรณ์โดยการข้าม การกระโดดแต่ละครั้งเป็นการกระโดดสั้น ๆ และมีความยาวเพียงสองไบต์ซึ่งไบต์ที่สองคือการกระจัด (เช่นระยะทางที่จะกระโดดด้วยเครื่องหมายกำหนดทิศทาง)
เราสามารถแบ่งรหัสออกเป็นสี่ส่วนซึ่งอาจได้รับการฉายรังสี: กระโดด 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);
}