【问题标题】:Assembly call interrupt based on register value基于寄存器值的汇编调用中断
【发布时间】:2020-02-25 15:36:23
【问题描述】:

我想在 NASM 中有一个调用不是硬编码中断而是 int 的中断。在寄存器中。 举个例子:

mov al, 0x10
int al    ; you can't do this for some reason

因此,如果我将 0x10 存储在寄存器 al 中,那么我可以根据该寄存器中的内容调用中断。

有什么办法可以做到吗?

【问题讨论】:

    标签: assembly x86 interrupt


    【解决方案1】:

    有什么办法可以做到吗?

    在没有自修改代码的 16 位“实模式”中:

    大多数 DOS 的 C 编译器都提供了一个库函数,允许执行 int al 的等效操作。

    这通过以下方式起作用:

    在实模式下,int 指令等于 pushf 后跟 call 到中断处理程序。

    然而,“远”call 只不过是将下一条指令的“远”地址(csip)压入堆栈并执行跳转。 (“near”调用仅推送ip。)retf 将从堆栈中弹出ipcs 并跳转到该地址。

    中断处理程序的地址存储在地址0:(4*n)。

    所以要进入一个中断,首先执行如下代码:

      pushf
      push cs       # Leave this line out if "call helper" is a "far" call
      call helper
    

    当进入函数helper时,栈是这样的:

    Address (IP) of the instruction after "call helper"
    Segment (CS) of the program
    Flags
    ...
    

    这三个元素在int 指令之后位于堆栈中。

    程序helper 如下所示。

    helper:
        # Calculate BX = 4*AX
        # (This can be done with less instructions on a modern CPU)
      mov bl,al
      mov bh,0
      add bx,bx
      add bx,bx
        # Set DS to 0
      xor ax,ax
      mov ds,ax
        # Push the segment part of the interrupt handler address
        # to the stack
      push word [bx+4]
        # Push the offset part
      push word [bx]
        # Load all registers with the desired values
      # TODO: Ensure all registers have the correct values
        # Enter the interrupt
      retf
    

    retf 之前,堆栈将如下所示:

    Address (IP) of the interrupt routine
    Segment (CS) of the interrupt routine
    Address (IP) of the instruction after "call helper"
    Segment (CS) of the program
    Flags
    ...
    

    retf 指令的行为方式与前两个字已被“远”call 指令压入一样:它将从堆栈中删除前两个字并跳转到这些字描述的地址两个词 - 这意味着:进入中断处理程序。

    在中断处理程序结束时,最后3个字将从堆栈中弹出,并在call helper之后的指令处继续执行。

    在带有自修改代码的 16 位“实模式”中:

    这很简单:

        # AL contains the interrupt number
        # Modify the "int" instruction, so "int 42" becomes
        # "int 31" if the value of AL is 31.
      mov cs:[theIntInstruction+1], al
        # Load all registers with the desired values
      # TODO: Ensure all registers have the correct values
        # Now perform the "int" instruction
    theIntInstruction:
      int 42
    

    自修改代码可能会产生负面影响。这意味着可能会出现问题...

    在(16 位或 32 位)“保护模式”下:

    根据内存保护设置,您有机会写入“可执行”内存。在这种情况下,您可能会使用自修改代码。

    如果您无法使用自修改代码,则无法执行 int al 的等效操作,除非您想执行以下操作:

    performInt0:
        int 0
        ret
    performInt1:
        int 1
        ret
    performInt2:
        int 2
        ret
    ...
    performInt255:
        int 255
        ret
    

    ...然后对所需标签执行call

    这当然总是可能的。

    【讨论】:

    • 辅助函数中,计算bx和设置ds后,为什么不做jmp far [bx]
    • @prl 当然,如果中断服务程序不需要dsesbx 具有某些值,jmp far [bx] 也是可能的。我发布的变体允许所有寄存器具有任何值 - 使用 iret 而不是 retf 甚至可以加载具有所需值的标志。
    • 马丁,当然,谢谢。我完全忽略了代码中的 cmets。
    【解决方案2】:

    一般情况下没有好的/简单的选项,不要这样做。你需要自我修改代码,或者每个选项的跳转表,或者条件链如果只有几个可能的值,则分支。

    (但是,如果您只需要它在实模式下工作,请参阅 Martin 的回答;使用 pushfcall far [ivt_entry] 模拟 int

    如果你想创建一个包装函数,不要;使它成为一个宏,以便它可以与中断号作为常量内联。或者为您要使用的每个中断号制作单独的包装器。

    【讨论】:

    • 谢谢!情况有点复杂,所以我想我会尝试自修改代码。
    • 其实你知道我应该怎么做一个int自修改代码吗?
    • 检查int的编码。第一个字节是操作码,第二个是中断号。覆盖它以更改您调用的中断。
    • pushf ; call far [idt-entry] 不是一个很好/简单的选择吗? (类似于马丁的回答。)
    • @prl:我没想过模拟int,因为这只能在我通常不使用的实模式下实现。
    猜你喜欢
    • 1970-01-01
    • 2013-07-26
    • 1970-01-01
    • 2021-03-25
    • 2019-12-26
    • 1970-01-01
    • 1970-01-01
    • 2010-10-21
    • 2013-10-21
    相关资源
    最近更新 更多