如何在将自身缝合到自己的尾部的程序中,无限地环绕在 64KB 代码段中? [英] How to wraparound in the 64KB code segment in a program that stitches itself to its own tail, ad infinitum?

查看:25
本文介绍了如何在将自身缝合到自己的尾部的程序中,无限地环绕在 64KB 代码段中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果指令的顺序执行通过了偏移量 65535,那么 8086 将从同一代码段中的偏移量 0 处获取下一个指令字节.

If the sequential execution of instructions passes offset 65535, then the 8086 will fetch the next instruction byte from offset 0 in the same code segment.

下一个 .COM 程序使用这个事实并不断地将其整个代码(总共 32 个字节)拼接到自己的尾部,环绕在 64KB 代码段中.你可以称之为二元奎因.

Next .COM program uses this fact and continually stitches its entire code (32 bytes in total) to its own tail, wrapping around in the 64KB code segment. You could call this a binary quine.

    ORG 256            ; .COM programs start with CS=DS=ES=SS

Begin:
    mov  ax, cs        ; 2 Providing an exterior stack
    add  ax, 4096      ; 3
    mov  ss, ax        ; 2
    mov  sp, 256       ; 3
    cli                ; 1
    call Next          ; 3 This gets encoded with a relative offset
Next:
    pop  bx            ; 1  -> BX is current address of Next
    sub  bx, 14        ; 3  -> BX is current address of Begin
More:
    mov  al, [bx]      ; 2
    mov  [bx+32], al   ; 3
    inc  bx
    test bx, 31        ; 4
    jnz  More          ; 2
    nop                ; 1
    nop                ; 1
    nop                ; 1

为了 callpop 指令的好处,程序会在代码段外部建立一个小堆栈.我不认为 cli 真的有必要,因为我们确实有一个堆栈.
一旦我们计算了 32 字节程序的当前起始地址,我们就将其复制到内存中高 32 字节的位置.所有 BX 指针算法都会环绕.
然后我们在新编写的代码中失败.

For the benefit of the call and pop instructions, will the program set up a small stack exterior to the code segment. I don't think the cli is really necessary because we do have a stack.
Once we have calculated the address of the current start of our 32-byte program, we copy it 32 bytes higher in memory. All the BX pointer arithmetic will wraparound.
We then fall through in the newly written code.

如果指令的顺序执行超过了偏移量 65535,则 80386 将触发异常 13.

If the sequential execution of instructions passes offset 65535, then the 80386 will trigger exception 13.

假设我包含了异常处理程序的必要设置,是否只执行远跳转到此代码段的开头(新编写的代码等待的地方)就足够了吗?这样的解决方案在 80386 CPU 之后仍然有效吗?

Assuming that I include the necessary setup for an exception handler, would it be enough to just execute a far jump to the start of this code segment (where the newly written code sits waiting)? And would such a solution remain valid on post 80386 CPU's?

相关:是否有可能制作一个永远自己写的汇编程序?

推荐答案

在 16 位模式(真实或保护)下,IP 寄存器将环绕 64KiB 没有任何错误,当然没有指令越过 64KiB 边界(例如,位于 0xffff 处的两字节指令).

In 16-bit mode (real or protect), the IP register will wrap around 64KiB without any fault, granted that no instruction crosses the 64KiB boundary (e.g. a two bytes instruction placed at 0xffff).

交叉指令会在 80386+ 上出错,不确定在以前的模型上会发生什么(读取线性地址空间中的下一个字节?从 0 读取下一个字节?).

A crossing instruction will fault on an 80386+, not sure what will happen on previous models (read the next byte in the linear address space? read the next byte from 0?).

请注意,这是有效的,因为段限制与 IP 寄存器限制"相同.
在 16 位保护模式下,您可以设置小于 64KiB 的段限制,在这种情况下,执行到最后会出错.
简而言之(并且形象地),CPU 确保它需要的所有字节都在段限制内,然后在没有溢出检测的情况下递增程序计数器.

Note that this works because the segment limit is the same as the IP register "limit".
In 16-bit protected mode you can set a segment limit less than 64KiB, in that case, the execution will fault when reaching the end.
In short (and figuratively), the CPU makes sure all the bytes it needs are within the segment limit and then will increment the program counter without overflow detection.

所以你的程序应该可以运行.

So your program should work.

将其称为 quine 可能有点牵强,因为它正在读取自己的机器代码,这是作弊(就像阅读源代码文件是针对高级语言一样).

It's probably a bit of a stretch to call it a quine because it's reading its own machine code and that's cheating (just like reading the source code file is for high-level languages).

我还没有测试过它,但它是程序某种复制"的一个非常小的例子.本身可能是:

I haven't tested it, but a very minimal example of a program "kind of replicating" itself could be:

 ;Setup (assuming ES=CS)
 mov al, 0abh       ;This encodes stosb
 mov di, _next      ;Where to start writing the instruction stream

 stosb              ;Let's roll

_next: 

这也不是 quine,因为只有 stosb 被复制.
制作 quine 很困难,存储必须是编码小于存储数据大小的指令,否则我们要写入的字节总是比写入的字节多.

This is also not a quine because only the stosb is replicated.
Making a quine is hard, the stores must be instructions whose encoding is less than the size of the data stored or we will always have more bytes to write than those written.

这篇关于如何在将自身缝合到自己的尾部的程序中,无限地环绕在 64KB 代码段中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆