如何在 NASM 中测试和调试 x86 程序集异常?

逆向工程 部件 调试 x86 例外 机器码
2021-06-22 18:30:27

我已经在英特尔手册中看到了所有的异常“ID”,刚刚遇到了 Sandpile的异常指南,它实际上显示了一些十六进制代码。例如,“除以零误差”:

00h         #DE

问题:

  1. 这些ID有什么用,只是为了在手册中使用以方便参考?
  2. 这些十六进制代码(或机器代码)在运行代码中出现在哪里?
  3. 你如何在你的代码中测试这些?
  4. 你一般是怎么处理的?

什么是演示它们的用法的例子,本质上,就像这个除以零错误?我正在寻找可以与 NASM 一起运行的东西,以及如何在可能的情况下调试它的一般方法。

从我对表格的阅读来看,有两种“类型”:rIPB/CAB是“良性”错误,意味着它不会导致“双重错误”,即错误处理程序中的错误。AC是“贡献”错误,它可能导致双重错误(错误处理程序中的错误)。有 3 种类型rIP(这代表什么?):陷阱、故障和中止。除以零是一种fault rIP类型,那么如何生成此错误,以及如何处理它,例如 x86 汇编代码?

2个回答

这些是中断向量,如 int1....intn

这是生成并处理 int0 (#DE) 、int1 (#DB) 、int3 (#BP) 和 int6 (#UD) 的示例 c 代码

#include <stdio.h>
#include <windows.h>
unsigned int Exec_Search(unsigned int code) {
    printf("unknown Exception %x\n",code);
    return EXCEPTION_CONTINUE_SEARCH;
}
unsigned int  ExceHandler(unsigned int code ){
    printf("caught %x\n" , code);
    return EXCEPTION_EXECUTE_HANDLER;
}
int filter(unsigned int code, struct _EXCEPTION_POINTERS *){
    if( code == EXCEPTION_BREAKPOINT ) { 
        return ExceHandler(code); 
    }else if( code == EXCEPTION_ACCESS_VIOLATION  ) { 
        return ExceHandler(code); 
    }else if( code == EXCEPTION_ILLEGAL_INSTRUCTION  ) { 
        return ExceHandler(code); 
    }else { 
        return Exec_Search(code); 
    }    
}
void testint0(void){
    printf("Testing int 0\n");
    __try {
        __asm {
            int 0
        }
    } __except(filter(GetExceptionCode(), GetExceptionInformation())) {
        printf("Handler for #DE %x\n",GetExceptionCode() );
    }
}

void testint1(void){
    printf("Testing int 1\n");
    __try {
        __asm {
            int 1
        }
    } __except(filter(GetExceptionCode(), GetExceptionInformation())) {
        printf("Handler for #DB %x\n",GetExceptionCode() );
    }
}
void testint3(void){
    printf("Testing int 3\n");
    __try {
        __asm {
            int 3
        }
    } __except(filter(GetExceptionCode(), GetExceptionInformation())) {
        printf("Handler for #BP %x\n",GetExceptionCode() );
    }
}

void testint6(void){
    printf("Testing int 6\n");
    __try {
        __asm {
            ud2
        }
    } __except(filter(GetExceptionCode(), GetExceptionInformation())) {
        printf("Handler for #UD %x\n",GetExceptionCode() );
    }
}
int main (void) {    
    testint1(); 
    testint3();
    testint0();
    testint6();    
    printf("normal program continuation\n");
} 

编译并执行

:\>cl /Zi /W4 /analyze /Od /EHsc /nologo inthash.cpp /link /release
inthash.cpp

:\>inthash.exe
Testing int 1
caught c0000005
Handler for #DB c0000005
Testing int 3
caught 80000003
Handler for #BP 80000003
Testing int 0
caught c0000005
Handler for #DE c0000005
Testing int 6
caught c000001d
Handler for #UD c000001d
normal program continuation

为了生成和处理divide_by_zero,
可以添加这样的测试用例

void testdiv0(void){
    printf("Testing integer divided by 0\n");
    __try {
        __asm {
            mov edx,0 // remainder
            mov eax,0 // dividend
            mov ecx,0 // divisor
            div ecx   // doing 0/0
        }
    } __except(filter(GetExceptionCode(), GetExceptionInformation())) {
        printf("Handler for #DE %x\n",GetExceptionCode() );
    }
}

添加像这样的过滤器子句

else if( code == EXCEPTION_INT_DIVIDE_BY_ZERO  ) { 
        return ExceHandler(code); 

并在 main 中执行它

testdiv0();

导致

Testing integer divided by 0
caught c0000094
Handler for #DE c0000094
normal program continuation

CPU 异常被报告给 OS 故障处理程序,然后它可以终止程序或通过 OS 特定机制通知它。在 Windows 上这是结构化异常处理 (SEH),在类 Unix 系统上通常是信号。查看您的操作系统文档以获取更多信息。

如果你想学习如何自己处理原始异常,你基本上需要编写一个操作系统。Osdev wiki 可能是一个开始,否则请参阅处理器手册(系统编程卷)。