zhihuzheye 发表于 2014-11-27 18:04:31

分析编译器对变长数组的实现

分析编译器对变长数组的实现

#include <stdio.h>

int main(void)
{
    int n;
    scanf("%d", &n);      // 实验而已,输入n时,请输入大于3的数字
    int ar;
    ar = 0xff00;
    printf("%d\n", ar);
    printf("%p", ar);

    return 0;
}

以上代码经codeblocks13.12编译后(release版),使用od载入后(请结合栈的变化图来分析)

CPU Disasm
Address   Hex dump          Command                                       Comments
00401C2C/$8D4C24 04   lea   ecx,                         ; ecx = argc变量的地址,c中的main(argc, argv, env),argc为第一个参数
00401C30|.83E4 F0       and   esp, FFFFFFF0                         ; dqword (16.-byte) stack alignment
00401C33|.FF71 FC       push    dword ptr ds:                  ; 返回地址再次压栈(如何获取函数返回后的地址?这就是方法)
00401C36|.55            push    ebp
00401C37|.89E5          mov   ebp, esp
00401C39|.53            push    ebx
00401C3A|.51            push    ecx                                 ; argc的地址压栈
00401C3B|.83EC 20       sub   esp, 20
00401C3E|.E8 ADFCFFFF   call    004018F0                              ; [cConsole.004018F0, 未分析出函数的目的
00401C43|.8D45 F4       lea   eax,
00401C46|.894424 04   mov   dword ptr ss:, eax             ; /<%d> => offset LOCAL.4
00401C4A|.C70424 243040 mov   dword ptr ss:, offset 00403024   ; |format => "%d"
00401C51|.E8 36FFFFFF   call    <jmp.&msvcrt.scanf>                   ; \MSVCRT.scanf
00401C56|.8B45 F4       mov   eax, dword ptr ss:
00401C59|.8D0485 120000 lea   eax,                      ; 准备为变长数组分配空间,有多0x12,但为啥是0x12?
00401C60|.83E0 F0       and   eax, FFFFFFF0                         ; 清空了低4位,即又保证了栈为16字节对齐
00401C63|.E8 C8FEFFFF   call    00401B30                              ; 检测变长数组的长度是否可能导致栈溢出, probes EAX bytes on stack
00401C68|.29C4          sub   esp, eax                              ; 为变长数组分配空间,在栈中分配的哦
00401C6A|.8D5C24 08   lea   ebx,                         ; 现开始,esp指向的就是变长数组的首元素的首地址,即esp指向了变长数组的第一个元素
00401C6E|.C74424 10 00F mov   dword ptr ss:, 0FF00          ; 给ar赋值0xff00
00401C76|.C74424 04 00F mov   dword ptr ss:, 0FF00         ; /<%d> => 65280. 即0xff00 == 65280
00401C7E|.C70424 273040 mov   dword ptr ss:, offset 00403027   ; |format => "%d\n"
00401C85|.E8 0AFFFFFF   call    <jmp.&msvcrt.printf>                  ; \MSVCRT.printf
00401C8A|.895C24 04   mov   dword ptr ss:, ebx             ; /<%p>
00401C8E|.C70424 2B3040 mov   dword ptr ss:, offset 0040302B   ; |format => "%p"
00401C95|.E8 FAFEFFFF   call    <jmp.&msvcrt.printf>                  ; \MSVCRT.printf
00401C9A|.31C0          xor   eax, eax
00401C9C|.8D65 F8       lea   esp,                         ; 令esp指向argc
00401C9F|.59            pop   ecx                                 ; argc的地址出栈
00401CA0|.5B            pop   ebx
00401CA1|.5D            pop   ebp
00401CA2|.8D61 FC       lea   esp,                         ; 令esp指向返回地址
00401CA5\.C3            retn

CPU Disasm
Address   Hex dump          Command                                       Comments
004018A0/> /53            push    ebx
004018A1|. |83EC 18       sub   esp, 18
004018A4|. |8B1D C41C4000 mov   ebx, dword ptr ds:
004018AA|. |83FB FF       cmp   ebx, -1
004018AD|. |74 24         je      short 004018D3
004018AF|> |85DB          test    ebx, ebx
004018B1|. |74 0F         jz      short 004018C2
004018B3|> |FF149D C41C40 /call    near dword ptr ds:
004018BA|. |83EB 01       |sub   ebx, 1
004018BD|. |8D76 00       |lea   esi,
004018C0|.^|75 F1         \jnz   short 004018B3
004018C2|> |C70424 701840 mov   dword ptr ss:, 00401870          ; Entry point
004018C9|. |E8 F2F9FFFF   call    004012C0
004018CE|. |83C4 18       add   esp, 18
004018D1|. |5B            pop   ebx
004018D2|. |C3            retn
004018D3|> |31DB          xor   ebx, ebx
004018D5|. |EB 02         jmp   short 004018D9
004018D7|> |89C3          /mov   ebx, eax
004018D9|> |8D43 01       |lea   eax,
004018DC|. |8B1485 C41C40 |mov   edx, dword ptr ds:
004018E3|. |85D2          |test    edx, edx
004018E5|.^|75 F0         \jnz   short 004018D7
004018E7|.^|EB C6         jmp   short 004018AF
004018E9||8DB426 000000 lea   esi,
004018F0|$ |8B0D 24504000 mov   ecx, dword ptr ds:            ; cConsole.004018F0(guessed void)
004018F6|. |85C9          test    ecx, ecx
004018F8|. |74 06         jz      short 00401900
004018FA|. |F3:C3         rep retn
004018FC||8D7426 00   lea   esi,
00401900|> |C705 24504000 mov   dword ptr ds:, 1
0040190A\.^\EB 94         jmp   short 004018A0

CPU Disasm
Address   Hex dump          Command                                       Comments
00401B30/$51            push    ecx
00401B31|.50            push    eax
00401B32|.3D 00100000   cmp   eax, 1000                           ; 0x1000==4096==4k,变长数组长度不可超过4k字节?非也!参考下面
00401B37|.8D4C24 0C   lea   ecx,                          ; 可是此子程序并没有实现什么功能啊
00401B3B|.72 15         jb      short 00401B52
00401B3D|>81E9 00100000 /sub   ecx, 1000
00401B43|.8309 00       |or      dword ptr ds:, 00000000         ; 看似没有实现什么功能,实际上ecx不断减去4k,如果eax太大,则会使得ds:进入操作系统数据区域,再进行写入数据时造成访问违例的异常!非常巧妙的技巧!
00401B46|.2D 00100000   |sub   eax, 1000
00401B4B|.3D 00100000   |cmp   eax, 1000
00401B50|.^ 77 EB         \ja      short 00401B3D
00401B52|>29C1          sub   ecx, eax
00401B54|.8309 00       or      dword ptr ds:, 00000000
00401B57|.58            pop   eax
00401B58|.59            pop   ecx
00401B59\.C3            retn
页: [1]
查看完整版本: 分析编译器对变长数组的实现