您的位置:首页 > Web前端 > React

根据ReactOS源代码分析windows蓝屏处理过程

2014-05-02 20:43 711 查看
在windows系统下面蓝屏是经常发生的事情,下面就来跟随reactOS系统的源代码看一下windows蓝屏的实现。引起蓝屏的函数实现如下面所示,这个字符串组成函数是不是和蓝屏打印出来的信息一样。而系统的关闭正是有这句引起的。至于整个输出函数也很简单,就是调用最后MACHVtbl结构体的成员函数实现。看到这里不禁对操作系统模块化有一个直观的理解。这也就是为什么可以用C++实现操作系统的原因。因为如果这里将整个MACHVtbl中的函数指针用虚函数实现也是类似的。只不过C++会加入一些不必要的东西,而这里在最底层的系统当中是显得多余的。至于这里用一个结构体来管理函数,原因也很简单,这样便于组成一张表——所有在MACHVtbl当中的函数,都可以通过MACHVtbl来管理。

VOID NTAPI KeBugCheckEx(
IN ULONG  BugCheckCode,
IN ULONG_PTR  BugCheckParameter1,
IN ULONG_PTR  BugCheckParameter2,
IN ULONG_PTR  BugCheckParameter3,
IN ULONG_PTR  BugCheckParameter4)
{
char Buffer[70];
sprintf(Buffer, "*** STOP: 0x%08lX (0x%08lX, 0x%08lX, 0x%08lX, 0x%08lX)",
BugCheckCode, BugCheckParameter1, BugCheckParameter2,
BugCheckParameter3, BugCheckParameter4);
UiMessageBoxCritical(Buffer);
assert(FALSE);
for (;;);
}
VOID UiMessageBoxCritical(IN PCSTR MessageText)
{
TuiPrintf(MessageText);
}

下面的难点是不定项参数的分析,由于所有的参数都放到了堆栈当中。所以不管多少参数都可以通过堆栈指针来实现,通过堆栈指针跳过第一个参数,然后找到后面的参数。

#define  _AUPBND                (sizeof (int) - 1)
#define  _ADNBND                (sizeof (int) - 1)
typedef char *va_list;			//将要获得的参数定义为char*类型
#define _Bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))//进行四字节对齐
#define va_arg(ap, T)           (*(T *)(((ap) += (_Bnd (T, _AUPBND))) - (_Bnd (T,_ADNBND))))
#define va_end(ap)              (void) 0//表明参数全部扫描结束
#define va_start(ap, A)         (void) ((ap) = (((char *) &(A)) + (_Bnd (A,_AUPBND))))//跳过第一个参数,这个参数在printf函数里面是format字符串

至于va_arg的解释,需要明确一点,由于压入堆栈的是不确定长度的参数,所以最左边的参数最先入栈,而堆栈的指针依次递减。所以这里分为两步来分析。首先将整个宏当中的符号简化:(*(T *)(((ap) += (对齐)) - (对齐)))。在这里看起来相当于没有加,实际不然,第一步加的时候有赋值的等号,这里使得ap下移,然后减去后面的对齐,将类型强制转化为T*,然后取值,一个宏完成两件事。至于类型T的实现,当然是根据format里面在%后面的类型而具体看待了。

int TuiPrintf(const char *Format, ...)
{
int i;
int Length;
va_list ap;
CHAR Buffer[512];
va_start(ap, Format);
Length = _vsnprintf(Buffer, sizeof(Buffer), Format, ap);
va_end(ap);
if (Length == -1) Length = sizeof(Buffer);

for (i = 0; i < Length; i++)
{
MachConsPutChar(Buffer[i]);
}

return Length;
}
VOID MachConsPutChar(int Ch)
{
MachVtbl.ConsPutChar(Ch);
}
根据MACHInit函数,我们可以知道MachVtbl结构体当中的PcConsPutChar函数指针指向PcConsPutChar函数,所以这里实际调用的是PcConsPutChar函数。
PcConsPutChar(int Ch)
{
REGS Regs;
if ('\n' == Ch)
{
PcConsPutChar('\r');
}
if ('\t' == Ch)
{
PcConsPutChar(' ');
PcConsPutChar(' ');
PcConsPutChar(' ');
PcConsPutChar(' ');
PcConsPutChar(' ');
PcConsPutChar(' ');
PcConsPutChar(' ');
PcConsPutChar(' ');
return;
}
Regs.b.ah = 0x0E;
Regs.b.al = Ch;
Regs.w.bx = 1;
Int386(0x10, &Regs, &Regs);
}
Int386_regsin:
.long 0
Int386_regsout:
.long 0
PUBLIC _Int386
_Int386:
mov eax, dword ptr [esp + 4]
mov dword ptr ds:[BSS_IntVector], eax
mov eax, dword ptr [esp + 8]
mov dword ptr [Int386_regsin], eax
mov eax, dword ptr [esp + 12]
mov dword ptr [Int386_regsout], eax//首先取出三个参数,其中中断号放到全局范围的变量当中,其中BSS用于存放实模式下的一些数据
push ds
push es
push fs
push gs
pusha                              //保存所有寄存器

mov esi, dword ptr [Int386_regsin] //将输入参数复制到edi所指向的地址
mov edi, BSS_RegisterSet
mov ecx, REGS_SIZE / 4
rep movsd

mov bx, FNID_Int386               //FNID_Int386标志实模式下面的Int386的编号,将这个编号放入到BX里面,由于模式转化是不会影响到寄存器的值的,所以可以这样传递数据

mov dword ptr [ContinueAddress], offset Int386_return      //continueAddress用于存放返回地址,然而SwitchToReal是不会有返回值的,因为这里实际上会改变系统的模式,所以会有一个跳转到保护模式的过程,然后将continueAddress通过某种方式写入到EIP当中就可以了
jmp SwitchToReal

Int386_return:

mov esi, BSS_RegisterSet
mov edi, dword ptr [Int386_regsout]
mov ecx, REGS_SIZE / 4
rep movsd                //将返回值写入到传进来的数据当中

popa
pop gs
pop fs
pop es
pop ds
ret
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐