非常奇怪的段错误调用 WinUsb_GetOverlappedResult

2022-01-12 00:00:00 windows gcc segmentation-fault c++ stdcall

我有这个代码:

void GetResult(WINUSB_INTERFACE_HANDLE InterfaceHandle, LPOVERLAPPED lpOverlapped)
{
    DWORD numBytes = 0;
    WinUsb_GetOverlappedResult(
                InterfaceHandle,
                lpOverlapped,
                &numBytes,
                TRUE
                );
    return;

    uint8_t stack[64];
}

WinUsb_GetOverlappedResult 是一个 __stdcall 函数,声明如下:

WinUsb_GetOverlappedResult is a __stdcall function declared as follows:

  WINBOOL WINAPI WinUsb_GetOverlappedResult (WINUSB_INTERFACE_HANDLE InterfaceHandle, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, WINBOOL bWait);

使用 GCC 5.3.0 (MinGW) 在调试模式下编译一切正常.(我无法使用 VC++ 编译,因为我使用的是 GCC 扩展.)

Compiling in debug mode with GCC 5.3.0 (MinGW) it all works fine. (I can't compile with VC++ because I'm using GCC extensions.)

但是如果我把它改成 stack[80] 那么它会出现段错误!!

However if I change it to stack[80] then it segfaults!!

这是每种情况下的反汇编.64(不会崩溃):

Here is the disassembly in each case. 64 (doesn't crash):

Dump of assembler code for function GetResult(void*, _OVERLAPPED*):
88    {
   0x00408523 <+0>:    push   %ebp
   0x00408524 <+1>:    mov    %esp,%ebp
   0x00408526 <+3>:    sub    $0x68,%esp

89        DWORD numBytes = 0;
   0x00408529 <+6>:    movl   $0x0,-0xc(%ebp)

90        WinUsb_GetOverlappedResult(
91                    InterfaceHandle,
92                    lpOverlapped,
93                    &numBytes,
94                    TRUE
95                    );
=> 0x00408530 <+13>:    movl   $0x1,0xc(%esp)
   0x00408538 <+21>:    lea    -0xc(%ebp),%eax
   0x0040853b <+24>:    mov    %eax,0x8(%esp)
   0x0040853f <+28>:    mov    0xc(%ebp),%eax
   0x00408542 <+31>:    mov    %eax,0x4(%esp)
   0x00408546 <+35>:    mov    0x8(%ebp),%eax
   0x00408549 <+38>:    mov    %eax,(%esp)
   0x0040854c <+41>:    call   0x409d58 <WinUsb_GetOverlappedResult@16>
   0x00408551 <+46>:    sub    $0x10,%esp

96        return;
   0x00408554 <+49>:    nop

97        
98        uint8_t stack[64];
99    }
   0x00408555 <+50>:    leave  
   0x00408556 <+51>:    ret  

还有 80(确实崩溃):

And 80 (does crash):

Dump of assembler code for function GetResult(void*, _OVERLAPPED*):
88    {
   0x00408523 <+0>:    push   %ebp
   0x00408524 <+1>:    mov    %esp,%ebp
   0x00408526 <+3>:    sub    $0x78,%esp

89        DWORD numBytes = 0;
   0x00408529 <+6>:    movl   $0x0,-0xc(%ebp)

90        WinUsb_GetOverlappedResult(
91                    InterfaceHandle,
92                    lpOverlapped,
93                    &numBytes,
94                    TRUE
95                    );
=> 0x00408530 <+13>:    movl   $0x1,0xc(%esp)
   0x00408538 <+21>:    lea    -0xc(%ebp),%eax
   0x0040853b <+24>:    mov    %eax,0x8(%esp)
   0x0040853f <+28>:    mov    0xc(%ebp),%eax
   0x00408542 <+31>:    mov    %eax,0x4(%esp)
   0x00408546 <+35>:    mov    0x8(%ebp),%eax
   0x00408549 <+38>:    mov    %eax,(%esp)
   0x0040854c <+41>:    call   0x409d58 <WinUsb_GetOverlappedResult@16>
   0x00408551 <+46>:    sub    $0x10,%esp

96        return;
   0x00408554 <+49>:    nop

97        
98        uint8_t stack[80];
99    }
   0x00408555 <+50>:    leave  
   0x00408556 <+51>:    ret    

__stdcall 的效果是添加 sub $0x10,%esp 这行我猜是取消 ret $0x10在函数中.

The effect of the __stdcall is to add the line sub $0x10,%esp which I guess is to cancel out ret $0x10 in the function.

无论如何,这些看起来非常相似,我不知道它为什么会崩溃.我什至不能 100% 确定它在哪里崩溃(GDB 相当无用),但它在 WinUsb 函数调用附近.

In any case these seem very similar and I have no idea why it is crashing. I'm not even 100% sure where it is crashing (GDB is rather unhelpful) but it is somewhere around WinUsb function call.

调试非常困难,因为如果我在设置了任何断点的情况下运行调试器,它就不会崩溃.我怀疑这可能与时间有关-我还可以通过一些额外的 Sleep(100) 来防止崩溃.一旦它似乎在 PerfIncrementULongLongLongCounterValue() 中崩溃,但谁知道...

It's quite hard to debug because if I run the debugger with any breakpoints set, it doesn't crash. I suspect it may be timing related - I can also prevent the crash with a few extra Sleep(100)s. Once it seemed to crash in PerfIncrementULongLongCounterValue() but who knows...

有人知道为什么会发生这种情况吗?

Does anyone have any clue why this might be happening?

编辑

WinUsb_GetOverlappedResult() 只是根据其程序集直接调用 GetOverlappedResult() ,所以我用它替换调用.现在你需要 stack[96] 来导致崩溃,但是当它发生时,它至少告诉我真正的崩溃在哪里(我认为)!

WinUsb_GetOverlappedResult() just calls straight through to GetOverlappedResult() according to its assembly, so I replace the call with that. Now you need stack[96] to cause the crash, but when it does it at least tells me where the real crash is (I think)!

这里是GetOverlappedResult()的反汇编.它在指示的地方崩溃,因为 ebp 为 0.

Here is the disassembly of GetOverlappedResult(). It crashes where indicated because ebp is 0.

0x76feaba0                   8b ff                 mov    %edi,%edi
0x76feaba2  <+0x0002>        55                    push   %ebp
0x76feaba3  <+0x0003>        8b ec                 mov    %esp,%ebp
0x76feaba5  <+0x0005>        83 ec 0c              sub    $0xc,%esp
0x76feaba8  <+0x0008>        a1 94 4b 09 77        mov    0x77094b94,%eax
0x76feabad  <+0x000d>        33 c5                 xor    %ebp,%eax
0x76feabaf  <+0x000f>        89 45 fc              mov    %eax,-0x4(%ebp)
0x76feabb2  <+0x0012>        83 7d 14 00           cmpl   $0x0,0x14(%ebp)
0x76feabb6  <+0x0016>        53                    push   %ebx
0x76feabb7  <+0x0017>        56                    push   %esi
0x76feabb8  <+0x0018>        57                    push   %edi
0x76feabb9  <+0x0019>        0f 84 b3 00 00 00     je     0x76feac72 <KERNELBASE!GetOverlappedResult+210>
0x76feabbf  <+0x001f>        83 cf ff              or     $0xffffffff,%edi
0x76feabc2  <+0x0022>        8b 5d 08              mov    0x8(%ebp),%ebx
0x76feabc5  <+0x0025>        83 cb 01              or     $0x1,%ebx
0x76feabc8  <+0x0028>        85 ff                 test   %edi,%edi
0x76feabca  <+0x002a>        0f 84 a9 00 00 00     je     0x76feac79 <KERNELBASE!GetOverlappedResult+217>
0x76feabd0  <+0x0030>        b8 01 00 00 00        mov    $0x1,%eax
0x76feabd5  <+0x0035>        c7 45 f4 01 00 00 00  movl   $0x1,-0xc(%ebp)
0x76feabdc  <+0x003c>        89 45 f8              mov    %eax,-0x8(%ebp)
0x76feabdf  <+0x003f>        84 d8                 test   %bl,%al
0x76feabe1  <+0x0041>        0f 84 5e f3 03 00     je     0x77029f45 <KERNELBASE!GetCurrentProcess+43221>
0x76feabe7  <+0x0047>        6a 00                 push   $0x0
0x76feabe9  <+0x0049>        68 dc 10 f2 76        push   $0x76f210dc
0x76feabee  <+0x004e>        50                    push   %eax
0x76feabef  <+0x004f>        68 ab ab ab ab        push   $0xabababab
0x76feabf4  <+0x0054>        ff 15 68 80 09 77     call   *0x77098068
0x76feabfa  <+0x005a>        8b f0                 mov    %eax,%esi
0x76feabfc  <+0x005c>        85 f6                 test   %esi,%esi
0x76feabfe  <+0x005e>        74 0e                 je     0x76feac0e <KERNELBASE!GetOverlappedResult+110>
0x76feac00  <+0x0060>        8d 45 f4              lea    -0xc(%ebp),%eax
0x76feac03  <+0x0063>        8b ce                 mov    %esi,%ecx
0x76feac05  <+0x0065>        50                    push   %eax
0x76feac06  <+0x0066>        ff 15 5c 8a 09 77     call   *0x77098a5c
0x76feac0c  <+0x006c>        ff d6                 call   *%esi
0x76feac0e  <+0x006e>        33 c0                 xor    %eax,%eax
0x76feac10  <+0x0070>        83 e3 fe              and    $0xfffffffe,%ebx
0x76feac13  <+0x0073>        89 45 f8              mov    %eax,-0x8(%ebp)
0x76feac16  <+0x0076>        39 45 f4              cmp    %eax,-0xc(%ebp)
0x76feac19  <+0x0079>        0f 85 26 f3 03 00     jne    0x77029f45 <KERNELBASE!GetCurrentProcess+43221>
0x76feac1f  <+0x007f>        8b 75 0c              mov    0xc(%ebp),%esi
0x76feac22  <+0x0082>        81 3e 03 01 00 00     cmpl   $0x103,(%esi)
0x76feac28  <+0x0088>        74 26                 je     0x76feac50 <KERNELBASE!GetOverlappedResult+176>

Crash:
0x76feac2a  <+0x008a>        8b 45 10              mov    0x10(%ebp),%eax

0x76feac2d  <+0x008d>        8b 4e 04              mov    0x4(%esi),%ecx
0x76feac30  <+0x0090>        89 08                 mov    %ecx,(%eax)
0x76feac32  <+0x0092>        8b 0e                 mov    (%esi),%ecx
0x76feac34  <+0x0094>        85 c9                 test   %ecx,%ecx
0x76feac36  <+0x0096>        78 31                 js     0x76feac69 <KERNELBASE!GetOverlappedResult+201>
0x76feac38  <+0x0098>        b8 01 00 00 00        mov    $0x1,%eax
0x76feac3d  <+0x009d>        8b 4d fc              mov    -0x4(%ebp),%ecx
0x76feac40  <+0x00a0>        5f                    pop    %edi
0x76feac41  <+0x00a1>        5e                    pop    %esi
0x76feac42  <+0x00a2>        33 cd                 xor    %ebp,%ecx
0x76feac44  <+0x00a4>        5b                    pop    %ebx
0x76feac45  <+0x00a5>        e8 0b f0 02 00        call   0x77019c55 <PerfIncrementULongLongCounterValue+197>
0x76feac4a  <+0x00aa>        8b e5                 mov    %ebp,%esp
0x76feac4c  <+0x00ac>        5d                    pop    %ebp
0x76feac4d  <+0x00ad>        c2 10 00              ret    $0x10
0x76feac50  <+0x00b0>        8b 46 10              mov    0x10(%esi),%eax
0x76feac53  <+0x00b3>        85 c0                 test   %eax,%eax
0x76feac55  <+0x00b5>        74 46                 je     0x76feac9d <KERNELBASE!GetOverlappedResult+253>
0x76feac57  <+0x00b7>        6a 00                 push   $0x0
0x76feac59  <+0x00b9>        57                    push   %edi
0x76feac5a  <+0x00ba>        50                    push   %eax
0x76feac5b  <+0x00bb>        e8 50 01 00 00        call   0x76feadb0 <WaitForSingleObjectEx>
0x76feac60  <+0x00c0>        85 c0                 test   %eax,%eax
0x76feac62  <+0x00c2>        74 c6                 je     0x76feac2a <KERNELBASE!GetOverlappedResult+138>
0x76feac64  <+0x00c4>        e9 fb f2 03 00        jmp    0x77029f64 <KERNELBASE!GetCurrentProcess+43252>
0x76feac69  <+0x00c9>        e8 d2 f1 ff ff        call   0x76fe9e40 <OpenThreadToken+64>
0x76feac6e  <+0x00ce>        33 c0                 xor    %eax,%eax
0x76feac70  <+0x00d0>        eb cb                 jmp    0x76feac3d <KERNELBASE!GetOverlappedResult+157>
0x76feac72  <+0x00d2>        33 ff                 xor    %edi,%edi
0x76feac74  <+0x00d4>        e9 49 ff ff ff        jmp    0x76feabc2 <KERNELBASE!GetOverlappedResult+34>
0x76feac79  <+0x00d9>        8b 75 0c              mov    0xc(%ebp),%esi
0x76feac7c  <+0x00dc>        81 3e 03 01 00 00     cmpl   $0x103,(%esi)
0x76feac82  <+0x00e2>        74 0a                 je     0x76feac8e <KERNELBASE!GetOverlappedResult+238>
0x76feac84  <+0x00e4>        33 c9                 xor    %ecx,%ecx
0x76feac86  <+0x00e6>        8d 45 f8              lea    -0x8(%ebp),%eax
0x76feac89  <+0x00e9>        f0 09 08              lock or %ecx,(%eax)
0x76feac8c  <+0x00ec>        eb 9c                 jmp    0x76feac2a <KERNELBASE!GetOverlappedResult+138>
0x76feac8e  <+0x00ee>        68 e4 03 00 00        push   $0x3e4
0x76feac93  <+0x00f3>        ff 15 c4 80 09 77     call   *0x770980c4
0x76feac99  <+0x00f9>        33 c0                 xor    %eax,%eax
0x76feac9b  <+0x00fb>        eb a0                 jmp    0x76feac3d <KERNELBASE!GetOverlappedResult+157>
0x76feac9d  <+0x00fd>        8b c3                 mov    %ebx,%eax
0x76feac9f  <+0x00ff>        eb b6                 jmp    0x76feac57 <KERNELBASE!GetOverlappedResult+183>
0x76feaca1  <+0x0101>        cc                    int3
0x76feaca2  <+0x0102>        cc                    int3
0x76feaca3  <+0x0103>        cc                    int3
0x76feaca4  <+0x0104>        cc                    int3
0x76feaca5  <+0x0105>        cc                    int3
0x76feaca6  <+0x0106>        cc                    int3
0x76feaca7  <+0x0107>        cc                    int3
0x76feaca8  <+0x0108>        cc                    int3
0x76feaca9  <+0x0109>        cc                    int3
0x76feacaa  <+0x010a>        cc                    int3
0x76feacab  <+0x010b>        cc                    int3
0x76feacac  <+0x010c>        cc                    int3
0x76feacad  <+0x010d>        cc                    int3
0x76feacae  <+0x010e>        cc                    int3
0x76feacaf  <+0x010f>        cc                    int3

推荐答案

我认为我想通了.或许.我改变的是我不再移动我的 OVERLAPPED 结构.我只能假设 WinUsb 保留了一个指向您在开始写入时传递的 OVERLAPPED 的指针.如果它移动了,那么大概事情就会破裂.

Well I think I figured this out. Maybe. The thing I changed is that I don't move my OVERLAPPED structure any more. I can only assume that WinUsb retains a pointer to the OVERLAPPED you pass when you start the write. If it moves then presumably things break.

这在我可以找到的任何地方都没有提到崩溃.

This isn't mentioned anywhere I can find int the documentation for OVERLAPPED but changing my code so that the OVERLAPPED is dynamically allocated once and never moved seems to stop the crashes.

不幸的是,我从未找到调试它的好方法.最好的方法是可逆调试器,但它们似乎不适用于 Windows.

Unfortunately I never found a good way to debug it. The best way would be a reversible debugger but they don't seem to exist for Windows.

相关文章