【问题标题】:Interrupt handler chaining in real mode实模式下的中断处理程序链接
【发布时间】:2020-09-06 11:06:09
【问题描述】:

我正在尝试挂钩 BIOS Int 13h 以向其中添加我的自定义功能并劫持一些现有功能。 Old Int 13h 向量存储在全局变量中。 当调用中断处理程序时,DS 被设置为与调用者的原始数据段不匹配的某个值。因此访问调用者的全局变量变得很头疼。

链接中断处理程序的最佳做法是什么?

Hook 是这样安装的:

#ifdef __cplusplus
#  define INTARGS ...
#else
#  define INTARGS unsigned bp, unsigned di, unsigned si,\
                  unsigned ds, unsigned es, unsigned dx,\
                  unsigned cx, unsigned bx, unsigned ax
#endif

void interrupt (far *hackInt13h)(INTARGS) = NULL;
void interrupt (far *biosInt13h)(INTARGS) = (void interrupt (far *)(INTARGS))0xDEADBEEF;

void main(void)
{
  struct REGPACK reg;

  biosInt13h = getvect(0x13);
  hackInt13h = int13h;

  setvect(0x13, hackInt13h);

  // Calling CAFE
  reg.r_ax = 0xCAFE;
  intr(0x13, &reg);
  printf("Cafe returned: 0x%04x\n", reg.r_ax);

  // Resetting FDD just to check interrupt handler chaining
  reg.r_ax = 0;
  reg.r_dx = 0;
  intr(0x13, &reg);
  printf("CF=%i\n", reg.r_flags & 0x01);

  setvect(0x13, biosInt13h);
}

int 13h 钩子代码:

    P286
    .MODEL TINY

_Data   SEGMENT PUBLIC 'DATA'
    EXTRN _biosInt13h:FAR
_Data   ENDS


_Text   SEGMENT PUBLIC 'CODE'
    PUBLIC _int13h
_int13h PROC FAR
    pusha
    cmp AX,0CAFEh
    jnz chain
    popa
    mov AX, 0BEEFh
    iret
chain:
    popa
    call    far ptr [_biosInt13h]   ; <-- at this moment DS points to outer space
                                    ;     and _biosInt13h is not valid
_int13h ENDP
_Text   ENDS
    END

如果重要的话,我正在使用 Borland C++

【问题讨论】:

  • 通常的解决方案是将变量移动到代码段中以便您可以使用call far ptr [cs:_biosInt13h] 或根据需要设置段,检索指针,将其放在堆栈上,恢复段,然后执行retf 跳转到 BIOS 中断处理程序。请注意,代码将按原样崩溃,因为除了返回地址之外,中断处理程序还会弹出标志。
  • 如果您使用的是微型模型,您可以在远调用中使用 cs 段覆盖。但是,您要么需要将其更改为远跳,要么在远调用之前添加pushf
  • 如果您没有返回到您的处理程序并且正在尾部调用旧的处理程序,那么您可以使用 jmp far ptr [cs:_biosInt13h] 。然后iret 将直接由旧的中断处理程序完成。
  • 只有当您在微型模型中执行此操作并最终构建 COM 程序时,CS 段覆盖才会起作用,否则您将必须显式加载 DS 寄存器,其中包含特定于您正在使用的模型的段.
  • @MichaelPetch 您只需将变量放在与访问它的中断函数相同的段中。

标签: c assembly interrupt-handling tasm real-mode


【解决方案1】:

谢谢大家,我找到了解决办法!

我错过的第一件事是将变量移动到代码段并明确指定它。

第二个是使用被黑(压入堆栈)返回地址和retf,而不是call,它在堆栈上添加真正的返回地址。

无需明确pushf,因为在int 之后标志已经在堆栈上。无论是在我的处理程序中还是在链式处理程序中,都会在iret 上弹出标志。

    P286
    .MODEL TINY

_Text   SEGMENT PUBLIC 'CODE'
    EXTRN _biosInt13h:FAR ; This should be in CODE 'cause CS is only segreg reliable
    PUBLIC _int13h
_int13h PROC FAR
    pusha
    cmp AX, 0CAFEh
    jnz chain
    popa
    mov AX, 0BEEFh
    iret
chain:
    popa
    push    word ptr cs:[_biosInt13h + 2]   ; Pushing chained handler SEG on stack
    push    word ptr cs:[_biosInt13h]       ; Pushing chained handler OFFSET on stack
    retf                        ; ...actually this is JMP FAR to address on stack
_int13h ENDP
_Text   ENDS
    END

【讨论】:

  • 为什么是push push retf。你不能做一个间接的远跳。试图记住 TASM 的语法。也许jmp dword ptr cs:[_biosInt13h]
  • 你可以只写jmp [_biosInt13h]而不是RETF的东西,但是你需要将_biosInt13h正确声明为EXTERN _biosInt13h:DWORD并且你需要告诉汇编器它不能假设DS和ES点到与_TEXTASSUME DS:NOTHING, ES:NOTHING 相同的段。
  • @MichaelPetch 也可以。 push push retf 的灵感来自 MS-DOS 5.0 引导记录的反汇编。
  • @RossRidge 远指针的大小与 DWORD 相同,但使用适当的类型而不是相同长度的类型似乎更好。所以我更喜欢jmp far ptr 而不是jmp dword ptr
  • 不,远指针的适当类型是 DWORD.. FAR 是您可以直接跳转到的远标签的适当类型。 DWORD 是您将间接跳转到的远指针的适当类型。为了清楚起见,我是说您不应该使用jmp far ptr,因为那将是直接跳转,如果您正确声明_biosInt13h,则不需要使用jmp dword ptr。 DWORD 和 FAR 的大小相同,但在与跳转和调用指令一起使用时不可互换。 DWORD 是您需要用来获得间接跳转的类型。
猜你喜欢
  • 1970-01-01
  • 2013-09-23
  • 2014-08-19
  • 2011-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多