返回介绍

20.1 MSVC

发布于 2025-02-22 14:00:47 字数 6768 浏览 0 评论 0 收藏 0

MSVC2010 /Ox 选项编译:

Listing 20.1: Optimizing MSVC 2010: /Ox /GS- /MD

#!bash
__a$ = 8                                            ; size = 4
__b$ = 12                                           ; size = 4
_comp   PROC
        mov     eax, DWORD PTR __a$[esp-4]
        mov     ecx, DWORD PTR __b$[esp-4]
        mov     eax, DWORD PTR [eax]
        mov     ecx, DWORD PTR [ecx]
        cmp     eax, ecx
        jne     SHORT $LN4@comp
        xor     eax, eax
        ret     0
$LN4@comp:
        xor     edx, edx
        cmp     eax, ecx
        setge   dl
        lea     eax, DWORD PTR [edx+edx-1]
        ret     0
_comp ENDP

_numbers$ = -40                                     ; size = 40
_argc$ = 8                                          ; size = 4
_argv$ = 12                                         ; size = 4
_main   PROC
        sub     esp, 40                             ; 00000028H
        push    esi
        push    OFFSET _comp
        push    4
        lea     eax, DWORD PTR _numbers$[esp+52]
        push    10 ; 0000000aH
        push    eax
        mov     DWORD PTR _numbers$[esp+60], 1892   ; 00000764H
        mov     DWORD PTR _numbers$[esp+64], 45     ; 0000002dH
        mov     DWORD PTR _numbers$[esp+68], 200    ; 000000c8H
        mov     DWORD PTR _numbers$[esp+72], -98    ; ffffff9eH
        mov     DWORD PTR _numbers$[esp+76], 4087   ; 00000ff7H
        mov     DWORD PTR _numbers$[esp+80], 5
        mov     DWORD PTR _numbers$[esp+84], -12345 ; ffffcfc7H
        mov     DWORD PTR _numbers$[esp+88], 1087   ; 0000043fH
        mov     DWORD PTR _numbers$[esp+92], 88     ; 00000058H
        mov     DWORD PTR _numbers$[esp+96], -100000 ; fffe7960H
        call    _qsort
        add     esp, 16 ; 00000010H
...

第四个参数传递了一个地址标签_comp,指向了 comp() 函数。

我们来看 MSVCR80.DLL(包含 C 标准库函数的 MSVC DLL 模块)里该函数的内部调用:

Listing 20.2: MSVCR80.DLL

#!bash
.text:7816CBF0 ; void __cdecl qsort(void *, unsigned int, unsigned int, int (__cdecl *)(const void *, const void *))
.text:7816CBF0          public _qsort
.text:7816CBF0 _qsort   proc near
.text:7816CBF0
.text:7816CBF0 lo       = dword ptr -104h
.text:7816CBF0 hi       = dword ptr -100h
.text:7816CBF0 var_FC   = dword ptr -0FCh
.text:7816CBF0 stkptr   = dword ptr -0F8h
.text:7816CBF0 lostk    = dword ptr -0F4h
.text:7816CBF0 histk    = dword ptr -7Ch
.text:7816CBF0 base     = dword ptr 4
.text:7816CBF0 num      = dword ptr 8
.text:7816CBF0 width    = dword ptr 0Ch
.text:7816CBF0 comp     = dword ptr 10h
.text:7816CBF0
.text:7816CBF0 sub      esp, 100h
....
.text:7816CCE0 loc_7816CCE0:                ; CODE XREF: _qsort+B1
.text:7816CCE0          shr     eax, 1
.text:7816CCE2          imul    eax, ebp
.text:7816CCE5          add     eax, ebx
.text:7816CCE7          mov     edi, eax
.text:7816CCE9          push    edi
.text:7816CCEA          push    ebx
.text:7816CCEB          call    [esp+118h+comp]
.text:7816CCF2          add     esp, 8
.text:7816CCF5          test    eax, eax
.text:7816CCF7          jle     short loc_7816CD04

第四个参数 comp 传递函数指针,comp() 有两个参数,参数被检测后才执行。

这种使用函数指针的方式有一定的风险。第一种原因是如果你用 qsort() 调用了错误的函数指针,可能造成程序崩溃,并且这个错误很难被发现。

第二个原因是即使回调函数类型完全正确,使用错误的参数调用函数可能会导致更严重的问题。进程崩溃不是最大的问题,最大的问题是崩溃的原因—编译器很难发现这种潜在的问题。

20.1.1 MSVC + OllyDbg

我们在 OD 中加载我们的例子,并在 comp() 函数下断点。

我们可以看到第一次 comp() 调用时是如何比较的:fig.20.1.OD 代码窗口显示了比较的值。我们还可以看到 SP 指向的 RA 地址在 qsort() 函数空间里(实际上位于 MSVCR100.DLL)。

按 F8 直到函数返回到 qsort() 函数:fig20.2.这里比较函数被调用。

第二次调用 comp()—当前比较的值不相同:fig203。

enter image description here

Figure 20.1: OllyDbg: first call of comp()

enter image description here

Figure 20.2: OllyDbg: the code in qsort() right a_er comp() call

enter image description here

Figure 20.3: OllyDbg: second call of comp()

20.1.2 MSVC + tracer

我们来看成对比较,来对 10 个数字进行排序:1892, 45, 200, -98, 4087, 5, -12345, 1087, 88,-100000.

我们找到 comp() 函数中的 CMP 指令地址,并在其地址 0x0040100C 上设置断点。

#!bash
tracer.exe -l:17_1.exe bpx=17_1.exe!0x0040100C

断点中断是的寄存器地址:

#!bash
PID=4336|New process 17_1.exe
(0) 17_1.exe!0x40100c
EAX=0x00000764 EBX=0x0051f7c8 ECX=0x00000005 EDX=0x00000000
ESI=0x0051f7d8 EDI=0x0051f7b4 EBP=0x0051f794 ESP=0x0051f67c
EIP=0x0028100c
FLAGS=IF
(0) 17_1.exe!0x40100c
EAX=0x00000005 EBX=0x0051f7c8 ECX=0xfffe7960 EDX=0x00000000
ESI=0x0051f7d8 EDI=0x0051f7b4 EBP=0x0051f794 ESP=0x0051f67c
EIP=0x0028100c
FLAGS=PF ZF IF
(0) 17_1.exe!0x40100c
EAX=0x00000764 EBX=0x0051f7c8 ECX=0x00000005 EDX=0x00000000
ESI=0x0051f7d8 EDI=0x0051f7b4 EBP=0x0051f794 ESP=0x0051f67c
EIP=0x0028100c
FLAGS=CF PF ZF IF
...

过滤 EAX 和 ECX 得到:

EAX=0x00000764 ECX=0x00000005
EAX=0x00000005 ECX=0xfffe7960
EAX=0x00000764 ECX=0x00000005
EAX=0x0000002d ECX=0x00000005
EAX=0x00000058 ECX=0x00000005
EAX=0x0000043f ECX=0x00000005
EAX=0xffffcfc7 ECX=0x00000005
EAX=0x000000c8 ECX=0x00000005
EAX=0xffffff9e ECX=0x00000005
EAX=0x00000ff7 ECX=0x00000005
EAX=0x00000ff7 ECX=0x00000005
EAX=0xffffff9e ECX=0x00000005
EAX=0xffffff9e ECX=0x00000005
EAX=0xffffcfc7 ECX=0xfffe7960
EAX=0x00000005 ECX=0xffffcfc7
EAX=0xffffff9e ECX=0x00000005
EAX=0xffffcfc7 ECX=0xfffe7960
EAX=0xffffff9e ECX=0xffffcfc7
EAX=0xffffcfc7 ECX=0xfffe7960
EAX=0x000000c8 ECX=0x00000ff7
EAX=0x0000002d ECX=0x00000ff7
EAX=0x0000043f ECX=0x00000ff7
EAX=0x00000058 ECX=0x00000ff7
EAX=0x00000764 ECX=0x00000ff7
EAX=0x000000c8 ECX=0x00000764
EAX=0x0000002d ECX=0x00000764
EAX=0x0000043f ECX=0x00000764
EAX=0x00000058 ECX=0x00000764
EAX=0x000000c8 ECX=0x00000058
EAX=0x0000002d ECX=0x000000c8
EAX=0x0000043f ECX=0x000000c8
EAX=0x000000c8 ECX=0x00000058
EAX=0x0000002d ECX=0x000000c8
EAX=0x0000002d ECX=0x00000058

有 34 对。因此快速排序算法对 10 个数字排序需要 34 此对比操作。

20.1.3 MSVC + tracer (code coverage)

我们使用跟踪特性收集寄存器的值并在 IDA 中查看。

跟踪 comp() 函数所有指令:

tracer.exe -l:17_1.exe bpf=17_1.exe!0x00401000,trace:cc

IDA 加载.idc 脚本:fig20.4。

IDA 给出了函数名字(PtFuncCompare)—IDA 认为该函数指针被传递给 qsort()。

可以看到 a 和 b 指向数组不同的位置,并且相差 4-32bit 的字节数。

0x401010 和 0x401012 之间的指令从没有被执行:事实上 comp() 从来不返回 0,因为没有相等的元素。

enter image description here

Figure 20.4: tracer and IDA. N.B.: some values are cutted at right

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文