Advertisement

Bochs源码分析 - 19:分析Bochs对于bound,into的实现

阅读量:

前言

在《x86/x64体系探索及编程》的ex10-7代码中,使用了int3,bound,into来加载CS寄存器。我们现在来尝试分析bochs对于这几条指令的实现。

into指令分析

关于into以及处理OF异常的代码如下,比较好理解。into指令只是判断eflag的of位,这位标志着是否存在溢出发生,如果存在溢出,则触发OF异常。

复制代码
 ;; 测试 INTO 指令

    
     mov eax, 0x80000000
    
     mov ebx, eax
    
     add eax, ebx                                        ; 产生溢出,OF标志置位
    
     into                                                ; 引发 #OF 异常
    
  
    
  
    
  
    
 ;---------------------------------------
    
 ; OF_handler(): #OF handler
    
 ;---------------------------------------
    
 OF_handler:
    
     jmp do_OF_handler
    
 omsg1   db '---> Now, enter #OF handler',10, 10,0        
    
  
    
 do_OF_handler:
    
     push ebx
    
     mov ebx, [esp + 12]             ; 读 eflags 值
    
     mov esi, omsg1
    
     call puts
    
     mov esi, ebx
    
     call dump_flags_value
    
     pop ebx
    
     iret
    
    
    
    

Intel手册中对于INTO指令的描述非常简单,只是注意这是一个trap类型,而不是fault类型,我们之后会看细节,看看两种类型的实际区别,这里简单来讲,trap类型执行完后不会再来执行原指令,而fault则会再来执行原指令,int主动调用的都为trap类型。

我们现在来用IDA逆向代码,确定其位置,然后用bochs定位,最后用visual studio看源码。

如下bochs对于into指令的模拟代码,内容如下,这部分其实一点也不难。其基本就是调用get_OF()来判断标志位,如果满足要求的话,触发0x4号中断(overflow)。

复制代码
 void BX_CPP_AttrRegparmN(1) BX_CPU_C::INTO(bxInstruction_c *i)

    
 {
    
   if (get_OF()) {
    
     BX_INSTR_FAR_BRANCH_ORIGIN();
    
  
    
     .....
    
     // interrupt is not RSP safe
    
     interrupt(4, BX_SOFTWARE_EXCEPTION, 0, 0);
    
  
    
     BX_INSTR_FAR_BRANCH(BX_CPU_ID, BX_INSTR_IS_INT,
    
                     FAR_BRANCH_PREV_CS, FAR_BRANCH_PREV_RIP,
    
                     BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value, RIP);
    
   }
    
  
    
   BX_NEXT_TRACE(i);
    
 }
    
    
    
    

bound指令分析

#BR异常应该含义是 bad range,越界引发的,该代码就是来触发#BR异常。

复制代码
 ;; 测试 bound 指令

    
     mov eax, 0x8000                                    ; 这个值将越界
    
     bound eax, [bound_rang]                            ; 引发 #BR 异常
    
  
    
 ;-------------------------------------
    
 ; BR_handler(): #BR handler
    
 ;-------------------------------------
    
 BR_handler:
    
     jmp do_BR_handler
    
 brmsg1        db 10, 10, '---> Now, enter #BR handler', 10, 0        
    
 do_BR_handler:        
    
     mov esi, brmsg1
    
     call puts
    
     mov eax, [bound_rang]                ; 修复错误
    
     iret
    
  
    
 bound_rang        dd        10000h              ; 给定的范围是 10000h 到 20000h
    
               dd        20000h
    
    
    
    

我们现在来使用IDA,逆向这部分代码,如下图所示。

其代码总体来说是比较简单的,来获取大小值,之后来进行判断,如果越界则调用 exception(BX_BR_EXCEPTION, 0) 函数来触发异常。

复制代码
 void BX_CPP_AttrRegparmN(1) BX_CPU_C::BOUND_GdMa(bxInstruction_c *i)

    
 {
    
   Bit32s op1_32 = BX_READ_32BIT_REG(i->dst());
    
  
    
   Bit32u eaddr = (Bit32u) BX_CPU_RESOLVE_ADDR_32(i);
    
  
    
   Bit32s bound_min = (Bit32s) read_virtual_dword_32(i->seg(), eaddr);
    
   Bit32s bound_max = (Bit32s) read_virtual_dword_32(i->seg(), (eaddr+4) & i->asize_mask());
    
  
    
   if (op1_32 < bound_min || op1_32 > bound_max) {
    
     BX_DEBUG(("%s: fails bounds test", i->getIaOpcodeNameShort()));
    
     exception(BX_BR_EXCEPTION, 0);
    
   }
    
  
    
   BX_NEXT_INSTR(i);
    
 }
    
    
    
    

这个 exception(BX_BR_EXCEPTION, 0) 函数来触发异常,异常之后必须被对应的中断所响应,而在这里填写中断所相应的信息,其中重要部分如下所示,填写errocode之类的,之后调用interrupt(...)函数,这个函数我们已经留意很多次了,不过多叙述了。

复制代码
 // vector:     0..255: vector in IDT

    
 // error_code: if exception generates and error, push this error code
    
 void BX_CPU_C::exception(unsigned vector, Bit16u error_code)
    
 {
    
     ....
    
  if (vector < BX_CPU_HANDLED_EXCEPTIONS) {
    
      push_error = exceptions_info[vector].push_error;
    
      exception_class = exceptions_info[vector].exception_class;
    
      exception_type = exceptions_info[vector].exception_type;
    
   }
    
     ....
    
  interrupt(vector, BX_HARDWARE_EXCEPTION, push_error, error_code);
    
  
    
  longjmp(BX_CPU_THIS_PTR jmp_buf_env, 1); // go back to main decode loop 
    
     ....
    
 }
    
  
    
 struct BxExceptionInfo {
    
   unsigned exception_type;
    
   unsigned exception_class;
    
   bx_bool push_error;
    
 };
    
  
    
  
    
 struct BxExceptionInfo exceptions_info[BX_CPU_HANDLED_EXCEPTIONS] = {
    
   /* DE */ { BX_ET_CONTRIBUTORY, BX_EXCEPTION_CLASS_FAULT, 0 },
    
   /* DB */ { BX_ET_BENIGN,       BX_EXCEPTION_CLASS_FAULT, 0 },
    
   /* 02 */ { BX_ET_BENIGN,       BX_EXCEPTION_CLASS_FAULT, 0 }, // NMI
    
   /* BP */ { BX_ET_BENIGN,       BX_EXCEPTION_CLASS_TRAP,  0 },
    
 }
    
    
    
    

上述代码中一定要注意其返回方式,其不是用return返回的,而是使用一个longjmp(...)指令(这个longjmp指令记得在前面描述过,这里不过多叙述)。其返回到cpu_loop( )函数开头,代码如下:

复制代码
 void BX_CPU_C::cpu_loop(void)

    
 {
    
 #if BX_DEBUGGER
    
   BX_CPU_THIS_PTR break_point = 0;
    
   BX_CPU_THIS_PTR magic_break = 0;
    
   BX_CPU_THIS_PTR stop_reason = STOP_NO_REASON;
    
 #endif
    
  
    
   // If the exception() routine has encountered a nasty fault scenario,
    
   // the debugger may request that control is returned to it so that
    
   // the situation may be examined.
    
 #if BX_DEBUGGER
    
   if (bx_guard.interrupt_requested) return;
    
 #endif
    
  
    
   if (setjmp(BX_CPU_THIS_PTR jmp_buf_env)) { < --- 从这里出来
    
     // can get here only from exception function or VMEXIT
    
     BX_CPU_THIS_PTR icount++;
    
     BX_SYNC_TIME_IF_SINGLE_PROCESSOR(0);
    
   }
    
  
    
      // We get here either by a normal function call, or by a longjmp
    
      // back from an exception() call.  In either case, commit the
    
      // new EIP/ESP, and set up other environmental fields.  This code
    
      // mirrors similar code below, after the interrupt() call.
    
       BX_CPU_THIS_PTR prev_rip = RIP; // commit new EIP
    
       BX_CPU_THIS_PTR speculative_rsp = 0;
    
  
    
  
    
     ....
    
     ....
    
     while(1){
    
     for(;;){
    
     ...
    
     i1->execute(...) // 我们从这里进去执行指令的
    
     ...
    
     }
    
     }
    
 }
    
    
    
    

其为什么这么走现在还看不太出来,之后我们会有单独分析exception的环节,看代码注释,合理怀疑:如果遇到嵌套的fault,此时就需要bochs的调试器接管代码,因此需要预先跳出cpu循环。

总结

这篇文章就简单分析了这两个指令,该两个指令本身没什么难度。但是我们了解了exception(..)这个函数,异常都会走exception(..)中,然后通过longjmp(...)函数来跳转到cpu之外,之后随着我们深入分析,对其细节把握会更加深刻。

全部评论 (0)

还没有任何评论哟~