在软件调试一书的一个示例程序ProtSeg.exe,是为了证明应用程序代码不可以直接修改段寄存器。但是在我测试的时候发生了奇怪的问题。表现如下:
第一次测试时间:在win7下使用vs2012编译,执行后崩溃在了 ntdll.dll中的lock btr dword ptr [eax],0 语句上;当时我查看了gdt表,发现对应的表项为
0018 00000000 ffffffff Code RE Ac 3 Bg Pg P Nl 00000cfb
当时我想可能是因为程序在写代码段的数据,所以发生了段保护异常。
于是我在程序中模拟了上述语句
__asm { mov bx,25 mov ds,bx
mov esi,ebp mov [esi-10h],eax }
也同样出现异常。
第二次测试,环境同上
int p=18; __asm { mov bx,25 mov ds,bx mov esi,ebp mov ds:[p],eax }
程序内竟然未出现异常,依旧是lock btr dword ptr [eax],0 异常。
第三次测试,环境为win7,vs2010
int p; __asm { mov dx,cs mov ds,dx mov ds:[p],0
}
程序内竟然无任何异常,但却崩在了mov dword ptr [mainret (1197154h)],eax 上,而这在前一个环境中是没有异常的。
我思考以后产生了以下问题:
看来出现异常的原因可能不是段描述符的属性保护问题,原因有二:
一、第一次测试我还看到在lock btr dword ptr [eax],0之前还执行了 mov dword ptr [mainret],eax 语句也无异常,这与第二次测试相似。
二、在第二个环境中,ds是否显式使用程序内都无问题,问题却出现在第一个中没有问题的位置上。
总之,感觉问题出现的很没有规律,看似与寻址方式有关又不全然如此。
只好来请教张老师,修改段寄存器出错的原因到底是什么,为什么感觉实验中出现的问题前后矛盾?
正文中似乎没有提到这个例子吧?
在这个小例子的开头,有简单的注释:
/*---------------------------------------------------------------------ProtSeg.cpp - Utility to watch protection of segment register. Software Debugging by Raymond Zhang, All rights reserved. ---------------------------------------------------------------------*/
意思是“用于观察段寄存器保护的小工具”,并没有说“应用程序不可以改段寄存器”哦^^
事实上,是可以改段寄存器,但是一定要提供有效的源操作数,也就是CPU保护段寄存器,防止被改为无效的值或者0(0是允许的)。
也就是IA手册上说的:
If the destination operand is a segment register (DS, ES, FS, GS, or SS), the sourceoperand must be a valid segment selector.
小例子中试图将DS改为25(0n19),这是不可以的,因为这不是一个有效的段选择子。
0:000> .formats 0n25Evaluate expression: Hex: 00000019 Decimal: 25 Octal: 00000000031 Binary: 00000000 00000000 00000000 00011001
参考《软件调试》P53,低两位是RPL,用户态的应该为0b11,也就是3,内核态的应该为0。
如果拿个有效的段寄存器内容看看
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b
0:000> .formats 2bEvaluate expression: Hex: 0000002b Decimal: 43 Octal: 00000000053 Binary: 00000000 00000000 00000000 00101011
RPL是3
你看的很仔细,网站示例的描述的确有点不准确,纠正了:
应用程序加载无效段寄存器导致异常
为附录A加了个勘误,多谢