2010-11-22 24 views
3

我目前正在调查一个Windows崩溃转储,并在打开转储文件时,Visual Studio调试器向我显示“非法指令0xC000001D”。它示出该错误的代码位置示出了沿着以下示例的拆解:是否可以在Windows XP下用普通的C++程序覆盖代码段?

void g(int x) { 
00401E80 push  ebp 
00401E81 mov   ebp,esp 
    if(x > 20) { 
00401E83 cmp   dword ptr [x],14h 
00401E87 jle   g+14h (401E94h) 
     x *= 4; 
>00401E89 db   0fh // illegal instruction here 
00401E8A db   0fh 
00401E8B xadd  eax,esp 
00401E8E add   cl,byte ptr [ecx+9EB0845h] 
     x += 42; 
00401E94 mov   ecx,dword ptr [x] 
... 

我手动通过改写与在调试程序存储器窗口一些无效值的功能码创建在调试器上面的例子,但故障转储我调查显示同样的db 0fh条目,显然表明一条无效指令。该代码也类似于我的转储文件显示的内容,因为无效指令之前的指令都显得有效且与源代码匹配。

现在问题是是否有可能在所有的正常编译的C++程序 - 围绕不惹内存页面的访问限制 - (在Windows XP中的Visual C++ 2005)弄乱的代码段处理?

如果我尝试从代码中写入上述示例中的函数地址,我总是得到一个访问冲突,即代码段内存页似乎是写保护的。

{ 
    void* fnAddr = &g; // non-portable but OK in VC++ 
    unsigned int x = 0xDEADBEEF; 
    // Simulate memory corruption: Try to write something to the code segment: 
    memcpy((char*)fnAddr+4, &x, sizeof(x)); // generated 0xC0000005 Access Violation 
    g(42); // call messed up function - never get here 
} 

您是否知道实际上可能会无意中覆盖代码段中某些内容的任何情况?

我应该补充一点,真正的程序很复杂,有很多虚函数,一些成员函数指针等等,这个问题很遗憾不能重现,我们目前只有这个转储文件看起来很好除此以外。 - 尽管如此,转储文件在代码段中显示非法指令,我不会认为有可能弄乱代码段。

回答

3

不,包含代码的内存页面是写保护的。这种损坏只能发生在进程初始化时。但更可能的来源是软RAM错误。请您的客户运行一个RAM测试程序。考虑文件损坏是错误是可重复的。

+0

这些文件看起来不错。内存错误似乎不太可能,但我们会测试。也许转储文件没有向我展示我认为给我看的东西。 – 2010-11-23 07:41:44

0

是的,在部分代码段已被应用程序写入的情况下,例如在Java JIT中,Java字节码即时编译为本机代码。

+1

答案应该从“否”开始,因为问题是:*是否可以在通常编译的C++程序中使用 - 不会混淆内存页访问限制 - 将进程的代码段搞乱? * – 2010-11-23 08:04:07

0

默认.text部分有RX访问权限。但是,使用VirtualProtect您可以获得这些内存页面的写入限制。但是,看看这个..

我想这应该是代码:

code:00401000 55        push ebp 
code:00401001 89 E5        mov  ebp, esp 
code:00401003 81 7D 08 14 00 00+    cmp  dword ptr [ebp+8], 14h 
code:0040100A 7E 0B        jle  short loc_401017 
code:0040100A     ; --------------------------------------------------------------------------- 
code:0040100C 0F        db 0Fh // here should be x += 10.5; ?? 
code:0040100D 0F        db 0Fh 
code:0040100E 0F        db 0Fh 
code:0040100F     ; --------------------------------------------------------------------------- 
code:0040100F C1 E0 02       shl  eax, 2 ; //x *= 4 
code:00401012 
code:00401012     loc_401012: 
code:00401012 89 45 08       mov  [ebp+8], eax // save x 
code:00401015 EB 09        jmp  short near ptr unk_401020 
code:00401017     ; --------------------------------------------------------------------------- 
code:00401017 
code:00401017     loc_401017:        ; CODE XREF: code:0040100Aj 
code:00401017 8B 4D 08       mov  ecx, [ebp+8] 

如果我猜的操作码,那么失踪部分应增加10.5到EAX,没有的东西很有可能。你可以尝试的好奇心,使其

if (x > 20){ 
     x *= 4; 
     x += 40; 
}; 

你能还与它的ASM上市在一起后整个g(int)功能? (如果可能的话,在我的例子中使用二进制操作码值)。

+0

ruslik - 我在这里介绍的操作码是由我在调试器内存视图中覆盖内存创建的。在写这篇文章时,我没有访问原始转储列表,所以我用我的测试程序“手动”搞砸了内存。 – 2010-11-23 08:06:24

1

你的困难最可能的原因是你溢出了一个局部变量(比如说一个字符串)并覆盖了你的堆栈帧。子例程退出时,它会从堆栈中弹出返回地址,并开始执行它在其中找到的任何指令。这是通过缓冲区溢出攻击获得利用的机制。

+0

堆栈溢出无法修改代码段。该函数中有无效的操作码。 – ruslik 2010-11-22 22:31:13

相关问题