漏洞战争:堆溢出原理及调试技巧学习

堆溢出原理

调试代码:

#include <windows.h>
#include <stdio.h>
int main ( )
{
HANDLE hHeap;
char *heap;
char str[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
//The HeapCreate function creates a heap object that can be used by the calling process. The function reserves space in the virtual address space of the process and allocates physical storage for a specified initial portion of this block. 
//HEAP_GENERATE_EXCEPTIONS:Specifies that the system will raise an exception to indicate a function failure, such as an out-of-memory condition, instead of returning NULL. 
//0x1000 初始化的堆的大小
//0xffff 最大的大小
hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0x1000, 0xffff);
getchar();// 用于暂停程序,便于调试器加载
heap = HeapAlloc(hHeap, 0, 0x10);
printf("heap addr:0x%08x\n",heap);
strcpy(heap,str);// 导致堆溢出
HeapFree(hHeap, 0, heap);// 触发崩溃
HeapDestroy(hHeap);
return 0;
}

release版运行附加,之后崩溃

ecx被覆盖为0x41414141,再引用ecx导致崩溃

0:001> g
(d7c.c80): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=005405a8 ebx=00000000 ecx=41414141 edx=00540590 esi=00540588 edi=00540000
eip=76f23067 esp=0012fdf0 ebp=0012fed0 iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010283
ntdll!RtlpFreeHeap+0x4d6:
76f23067 8b19            mov     ebx,dword ptr [ecx]  ds:0023:41414141=????????
0:000> kb
ChildEBP RetAddr  Args to Child              
0012fed0 76f22d68 00540588 00540590 0012ff49 ntdll!RtlpFreeHeap+0x4d6
0012fef0 766ff1ac 00540000 00000000 00540590 ntdll!RtlFreeHeap+0x142
*** WARNING: Unable to verify checksum for C:\Users\giantbranch\Desktop\heapoverflow.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Users\giantbranch\Desktop\heapoverflow.exe
0012ff04 00401094 00540000 00000000 00540590 kernel32!HeapFree+0x14
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ff48 00401327 00000001 002e0ad8 002e0b20 heapoverflow+0x1094
0012ff88 76701174 7ffd7000 0012ffd4 76f2b3f5 heapoverflow+0x1327
0012ff94 76f2b3f5 7ffd7000 76f596f1 00000000 kernel32!BaseThreadInitThunk+0xe
0012ffd4 76f2b3c8 00401273 7ffd7000 00000000 ntdll!__RtlUserThreadStart+0x70
0012ffec 00000000 00401273 7ffd7000 00000000 ntdll!_RtlUserThreadStart+0x1b

这么简单的程序我们直接在复制地方下断吧

blob.png

继续

0:001> bp 00401084
0:001> g
Breakpoint 0 hit
eax=00000021 ebx=005f0590 ecx=00000008 edx=76f164f4 esi=0012ff28 edi=005f0590
eip=00401084 esp=0012ff10 ebp=005f0000 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
heapoverflow+0x1084:
00401084 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:000> dd edi
005f0590  005f00c4 005f00c4 00000000 00000000
005f05a0  0eb23090 000077a0 005f00c4 005f00c4
005f05b0  00000000 00000000 00000000 00000000
005f05c0  00000000 00000000 00000000 00000000
005f05d0  00000000 00000000 00000000 00000000
005f05e0  00000000 00000000 00000000 00000000
005f05f0  00000000 00000000 00000000 00000000
005f0600  00000000 00000000 00000000 00000000
0:000> dd esi
0012ff28  41414141 41414141 41414141 41414141
0012ff38  41414141 41414141 41414141 41414141
0012ff48  00407000 00401327 00000001 00610ad8
0012ff58  00610b20 00000000 00000000 7ffda000
0012ff68  00000000 00000000 0012ff5c 00000000
0012ff78  0012ffc4 00402c50 004060b8 00000000
0012ff88  0012ff94 76701174 7ffda000 0012ffd4
0012ff98  76f2b3f5 7ffda000 76c39055 00000000

我们看到我们申请的地址是0x005f0590,但是堆头是在前面的8个字节

而且我们也看到了下一个堆块的堆头005f05a0的位置:0eb23090 000077a0,它是一个空闲的堆块,下面我们就看到的了

我们看看它的堆头:45b331db 08007712

0:000> dd edi-8
005f0588  45b331db 08007712 005f00c4 005f00c4
005f0598  00000000 00000000 0eb23090 000077a0
005f05a8  005f00c4 005f00c4 00000000 00000000
005f05b8  00000000 00000000 00000000 00000000
005f05c8  00000000 00000000 00000000 00000000
005f05d8  00000000 00000000 00000000 00000000
005f05e8  00000000 00000000 00000000 00000000
005f05f8  00000000 00000000 00000000 00000000

我们用堆命令看看吧

-a就是可以查看很多信息,-p指定哪个地址的堆块吧

我们可以看到是busy,占用状态

0:000> !heap -p -a 005f0588
    address 005f0588 found in
    _HEAP @ 5f0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        005f0588 0003 0000  [00]   005f0590    00010 - (busy)

我们用!heap查看一下HeapCreate创建的堆,第5个就是我们程序创建的

0:000> !heap
Index   Address  Name      Debugging options enabled
  1:   002d0000                
  2:   00010000                
  3:   00020000                
  4:   00610000                
  5:   005f0000

继续

0:000> !heap -a 005f0000
Index   Address  Name      Debugging options enabled
  5:   005f0000 
    Segment at 005f0000 to 00600000 (00001000 bytes committed)
    Flags:                00001004
    ForceFlags:           00000004
    Granularity:          8 bytes
    Segment Reserve:      00100000
    Segment Commit:       00002000
    DeCommit Block Thres: 00000200
    DeCommit Total Thres: 00002000
    Total Free Size:      00000148
    Max. Allocation Size: 7ffdefff
    Lock Variable at:     005f0138
    Next TagIndex:        0000
    Maximum TagIndex:     0000
    Tag Entries:          00000000
    PsuedoTag Entries:    00000000
    Virtual Alloc List:   005f00a0
    Uncommitted ranges:   005f0090
            005f1000: 0000f000  (61440 bytes)
    FreeList[ 00 ] at 005f00c4: 005f05a8 . 005f05a8  
        005f05a0: 00018 . 00a40 [100] - free
    Segment00 at 005f0000:
        Flags:           00000000
        Base:            005f0000
        First Entry:     005f0588
        Last Entry:      00600000
        Total Pages:     00000010
        Total UnCommit:  0000000f
        Largest UnCommit:00000000
        UnCommitted Ranges: (1)
    Heap entries for Segment00 in Heap 005f0000
         address: psize . size  flags   state (requested size)
        005f0000: 00000 . 00588 [101] - busy (587)
        005f0588: 00588 . 00018 [101] - busy (10)    //数据将要复制到这里,会覆盖下面的空闲块
        005f05a0: 00018 . 00a40 [100]            //空闲堆
        005f0fe0: 00a40 . 00020 [111] - busy (1d)
        005f1000:      0000f000      - uncommitted bytes.

我们看到最后那里005f0588后面的那个确实是空闲堆

但看数据结构的时候,那个flag有点诡异

PreviousSize也是,总之这个结构的解析应该是有问题的

0:000> dt _HEAP_FREE_ENTRY 005f05a0
ntdll!_HEAP_FREE_ENTRY
   +0x000 Size             : 0x3090
   +0x002 Flags            : 0xb2 ''
   +0x003 SmallTagIndex    : 0xe ''
   +0x000 SubSegmentCode   : 0x0eb23090 Void
   +0x004 PreviousSize     : 0x77a0
   +0x006 SegmentOffset    : 0 ''
   +0x006 LFHFlags         : 0 ''
   +0x007 UnusedBytes      : 0 ''
   +0x000 FunctionIndex    : 0x3090
   +0x002 ContextValue     : 0xeb2
   +0x000 InterceptorValue : 0xeb23090
   +0x004 UnusedBytesLength : 0x77a0
   +0x006 EntryOffset      : 0 ''
   +0x007 ExtendedBlockSignature : 0 ''
   +0x000 Code1            : 0xeb23090
   +0x004 Code2            : 0x77a0
   +0x006 Code3            : 0 ''
   +0x007 Code4            : 0 ''
   +0x000 AgregateCode     : 0x000077a0`0eb23090
   +0x008 FreeList         : _LIST_ENTRY [ 0x5f00c4 - 0x5f00c4 ]

在看看空闲链表

0:000> dt _LIST_ENTRY 0x005f05a0+8
ntdll!_LIST_ENTRY
 [ 0x5f00c4 - 0x5f00c4 ]
   +0x000 Flink            : 0x005f00c4 _LIST_ENTRY [ 0x5f05a8 - 0x5f05a8 ]
   +0x004 Blink            : 0x005f00c4 _LIST_ENTRY [ 0x5f05a8 - 0x5f05a8 ]

我们单步看看覆盖后是怎么样的

可以看到整个堆头部的信息都被覆盖了

包括空闲链表的前后向指针

0:000> dt _HEAP_FREE_ENTRY 0x005f05a0
ntdll!_HEAP_FREE_ENTRY
   +0x000 Size             : 0x4141
   +0x002 Flags            : 0x41 'A'
   +0x003 SmallTagIndex    : 0x41 'A'
   +0x000 SubSegmentCode   : 0x41414141 Void
   +0x004 PreviousSize     : 0x4141
   +0x006 SegmentOffset    : 0x41 'A'
   +0x006 LFHFlags         : 0x41 'A'
   +0x007 UnusedBytes      : 0x41 'A'
   +0x000 FunctionIndex    : 0x4141
   +0x002 ContextValue     : 0x4141
   +0x000 InterceptorValue : 0x41414141
   +0x004 UnusedBytesLength : 0x4141
   +0x006 EntryOffset      : 0x41 'A'
   +0x007 ExtendedBlockSignature : 0x41 'A'
   +0x000 Code1            : 0x41414141
   +0x004 Code2            : 0x4141
   +0x006 Code3            : 0x41 'A'
   +0x007 Code4            : 0x41 'A'
   +0x000 AgregateCode     : 0x41414141`41414141
   +0x008 FreeList         : _LIST_ENTRY [ 0x41414141 - 0x41414141 ]

那么我么free掉0x10大小的堆,应该就会与后面的0x005f05a0的堆合并

就是要将0x005f05a0unlink下来,那就会引用0x41414141,造成异常

node->blink->flink = node->flink

node->flink->blink = node->blink

堆调试技巧

堆调试,可以通过gflags.exe设置,也可以通过命令

blob.png

可以在windbg

!gflag -i app.exe +htc +hpa -htg

或者命令行

gflags.exe -i app.exe +htc +hpa -htg

堆漏洞调试常用的是htc hpc,hfc和hpa

还是以上面的程序为例

堆尾检查

就是在堆尾加连续的两个0xabababab,如果被破坏就说明堆溢出了

但还要开启hpc或hfc

下面我们开启htc和hpc,如果说找不到符号应该你只是用绿色版的,没有用安装版

原来我这里默认开启了hfc,为了跟作者一样,我还是关掉吧,当然也可以不关有什么问题

0:000> !gflag +htc +hpc
Current NtGlobalFlag contents: 0x00000070
    htc - Enable heap tail checking
    hfc - Enable heap free checking
    hpc - Enable heap parameter checking

去掉-hfc

0:000> !gflag -hfc
New NtGlobalFlag contents: 0x00000050
    htc - Enable heap tail checking
    hpc - Enable heap parameter checking

go

0:000> g
HEAP[heapoverflow.exe]: Heap block at 004A0588 modified at 004A05A0 past requested size of 10
(e58.ec4): Break instruction exception - code 80000003 (first chance)
eax=004a0588 ebx=004a05a0 ecx=76ef07ed edx=0012fb15 esi=004a0588 edi=00000010
eip=76f94684 esp=0012fd5c ebp=0012fd5c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!RtlpBreakPointHeap+0x23:
76f94684 cc              int     3
0:000> kb
ChildEBP RetAddr  Args to Child              
0012fd5c 76f7d55f 004a0588 004a0000 004a0588 ntdll!RtlpBreakPointHeap+0x23
0012fd74 76f5fa18 00000000 004a0588 004a0588 ntdll!RtlpCheckBusyBlockTail+0x171
0012fd94 76f956df 004a0000 004a0588 76f1f98e ntdll!RtlpValidateHeapEntry+0x116
0012fddc 76f57aca 004a0000 50000067 004a0590 ntdll!RtlDebugFreeHeap+0x9a
0012fed0 76f22d68 004a0588 004a0590 0012ff49 ntdll!RtlpFreeHeap+0x5d
0012fef0 766ff1ac 004a0000 00000000 004a0590 ntdll!RtlFreeHeap+0x142
0012ff04 00401094 004a0000 00000000 004a0590 kernel32!HeapFree+0x14
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ff48 00401327 00000001 003a0b78 003a0bd0 image00400000+0x1094
0012ff88 76701174 7ffde000 0012ffd4 76f2b3f5 image00400000+0x1327
0012ff94 76f2b3f5 7ffde000 76472a71 00000000 kernel32!BaseThreadInitThunk+0xe
0012ffd4 76f2b3c8 00401273 7ffde000 00000000 ntdll!__RtlUserThreadStart+0x70
0012ffec 00000000 00401273 7ffde000 00000000 ntdll!_RtlUserThreadStart+0x1b

可以看到第一行说HEAP[heapoverflow.exe]: Heap block at 004A0588 modified at 004A05A0 past requested size of 10

就是说004A0588的堆块修改了地址0x004A05A0超过了申请的0x10的大小

此外,我们还看到ntdll!RtlDebugFreeHeap,说明是调试状态下的堆,因为我们是直接打开的,哈哈

开了htc之后也看到了ntdll!RtlpCheckBusyBlockTail,检测占用态堆块的尾部

那我们看看尾部的数据究竟是不是0xabababab

0:000> bp 00401084
*** WARNING: Unable to verify checksum for image00400000
*** ERROR: Module load completed but symbols could not be loaded for image00400000
0:000> g
Breakpoint 0 hit
eax=00000021 ebx=003f0590 ecx=00000008 edx=76f164f4 esi=0012ff28 edi=003f0590
eip=00401084 esp=0012ff10 ebp=003f0000 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
image00400000+0x1084:
00401084 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:000> dd edi
003f0590  baadf00d baadf00d baadf00d baadf00d
003f05a0  abababab abababab 00000000 00000000
003f05b0  0a9620b4 0000e4f1 003f00c4 003f00c4
003f05c0  feeefeee feeefeee feeefeee feeefeee
003f05d0  feeefeee feeefeee feeefeee feeefeee
003f05e0  feeefeee feeefeee feeefeee feeefeee
003f05f0  feeefeee feeefeee feeefeee feeefeee
003f0600  feeefeee feeefeee feeefeee feeefeee
0:000> dd esi
0012ff28  41414141 41414141 41414141 41414141
0012ff38  41414141 41414141 41414141 41414141
0012ff48  00407000 00401327 00000001 00580b78
0012ff58  00580bd0 00000000 00000000 7ffde000
0012ff68  00000000 00000000 0012ff5c 00000000
0012ff78  0012ffc4 00402c50 004060b8 00000000
0012ff88  0012ff94 76701174 7ffde000 0012ffd4
0012ff98  76f2b3f5 7ffde000 77af288c 00000000

我们看到尾部确实有两个0xabababab

而且我们看到调试状态下下面的数据都是0xfeeefeee

我们再看看堆的信息

可以看到调试状态下,空闲的堆块是被0xfeeefeee填充的

0:000> !heap
NtGlobalFlag enables following debugging aids for new heaps:    tail checking
    validate parameters
Index   Address  Name      Debugging options enabled
  1:   00240000                 tail checking free checking validate parameters
  2:   00010000                 tail checking free checking validate parameters
  3:   00020000                 tail checking free checking validate parameters
  4:   00580000                 tail checking free checking validate parameters
  5:   003f0000                 tail checking free checking validate parameters
0:000> !heap -a 003f0000                 
Index   Address  Name      Debugging options enabled
  5:   003f0000 
    Segment at 003f0000 to 00400000 (00001000 bytes committed)
    Flags:                40001064
    ForceFlags:           40000064
    Granularity:          8 bytes
    Segment Reserve:      00100000
    Segment Commit:       00002000
    DeCommit Block Thres: 00000200
    DeCommit Total Thres: 00002000
    Total Free Size:      00000146
    Max. Allocation Size: 7ffdefff
    Lock Variable at:     003f0138
    Next TagIndex:        0000
    Maximum TagIndex:     0000
    Tag Entries:          00000000
    PsuedoTag Entries:    00000000
    Virtual Alloc List:   003f00a0
    Uncommitted ranges:   003f0090
            003f1000: 0000f000  (61440 bytes)
    FreeList[ 00 ] at 003f00c4: 003f05b8 . 003f05b8  
        003f05b0: 00028 . 00a30 [104] - free
    Segment00 at 003f0000:
        Flags:           00000000
        Base:            003f0000
        First Entry:     003f0588
        Last Entry:      00400000
        Total Pages:     00000010
        Total UnCommit:  0000000f
        Largest UnCommit:00000000
        UnCommitted Ranges: (1)
    Heap entries for Segment00 in Heap 003f0000
        003f0000: 00000 . 00588 [101] - busy (587)
        003f0588: 00588 . 00028 [107] - busy (10), tail fill
        003f05b0: 00028 . 00a30 [104] free fill
        003f0fe0: 00a30 . 00020 [111] - busy (1d)
        003f1000:      0000f000      - uncommitted bytes.
0:000> dt _HEAP_ENTRY 003f0588
ntdll!_HEAP_ENTRY
   +0x000 Size             : 0x21f7
   +0x002 Flags            : 0x95 ''
   +0x003 SmallTagIndex    : 0x4b 'K'
   +0x000 SubSegmentCode   : 0x4b9521f7 Void
   +0x004 PreviousSize     : 0xe445
   +0x006 SegmentOffset    : 0 ''
   +0x006 LFHFlags         : 0 ''
   +0x007 UnusedBytes      : 0x18 ''
   +0x000 FunctionIndex    : 0x21f7
   +0x002 ContextValue     : 0x4b95
   +0x000 InterceptorValue : 0x4b9521f7
   +0x004 UnusedBytesLength : 0xe445
   +0x006 EntryOffset      : 0 ''
   +0x007 ExtendedBlockSignature : 0x18 ''
   +0x000 Code1            : 0x4b9521f7
   +0x004 Code2            : 0xe445
   +0x006 Code3            : 0 ''
   +0x007 Code4            : 0x18 ''
   +0x000 AgregateCode     : 0x1800e445`4b9521f7
0:000> dt _HEAP_FREE_ENTRY  003f05b0
ntdll!_HEAP_FREE_ENTRY
   +0x000 Size             : 0x20b4
   +0x002 Flags            : 0x96 ''
   +0x003 SmallTagIndex    : 0xa ''
   +0x000 SubSegmentCode   : 0x0a9620b4 Void
   +0x004 PreviousSize     : 0xe4f1
   +0x006 SegmentOffset    : 0 ''
   +0x006 LFHFlags         : 0 ''
   +0x007 UnusedBytes      : 0 ''
   +0x000 FunctionIndex    : 0x20b4
   +0x002 ContextValue     : 0xa96
   +0x000 InterceptorValue : 0xa9620b4
   +0x004 UnusedBytesLength : 0xe4f1
   +0x006 EntryOffset      : 0 ''
   +0x007 ExtendedBlockSignature : 0 ''
   +0x000 Code1            : 0xa9620b4
   +0x004 Code2            : 0xe4f1
   +0x006 Code3            : 0 ''
   +0x007 Code4            : 0 ''
   +0x000 AgregateCode     : 0xe4f1`0a9620b4
   +0x008 FreeList         : _LIST_ENTRY [ 0x3f00c4 - 0x3f00c4 ]
0:000> dc 003f05b0 l100
003f05b0  0a9620b4 0000e4f1 003f00c4 003f00c4  . ........?...?.
003f05c0  feeefeee feeefeee feeefeee feeefeee  ................
003f05d0  feeefeee feeefeee feeefeee feeefeee  ................
003f05e0  feeefeee feeefeee feeefeee feeefeee  ................
003f05f0  feeefeee feeefeee feeefeee feeefeee  ................
003f0600  feeefeee feeefeee feeefeee feeefeee  ................
003f0610  feeefeee feeefeee feeefeee feeefeee  ................
003f0620  feeefeee feeefeee feeefeee feeefeee  ................
003f0630  feeefeee feeefeee feeefeee feeefeee  ................
003f0640  feeefeee feeefeee feeefeee feeefeee  ................
003f0650  feeefeee feeefeee feeefeee feeefeee  ................
003f0660  feeefeee feeefeee feeefeee feeefeee  ................
003f0670  feeefeee feeefeee feeefeee feeefeee  ................
......   
......
......
还有很多

页堆

我们常常需要定位导致漏洞的代码或者函数,

而前面的堆尾检查只是检查堆被破坏后的情景

为此微软就引入页堆的概念,开启后会在堆块中增加不可访问的栅栏页,当覆盖到栅栏页就会触发异常

这次我们使用命令行开启页堆

blob.png

windbg加载,用命令看看是不是

0:000> !gflag
Current NtGlobalFlag contents: 0x02000000
    hpa - Place heap allocations at ends of pages

那go吧,

可以看到直接就看到触发触发异常的指令了

0:000> g
(ab4.fdc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000021 ebx=01755ff0 ecx=00000004 edx=76f164f4 esi=0012ff38 edi=01756000
eip=00401084 esp=0012ff10 ebp=01750000 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
*** WARNING: Unable to verify checksum for image00400000
*** ERROR: Module load completed but symbols could not be loaded for image00400000
image00400000+0x1084:
00401084 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:000> dd esi
0012ff38  41414141 41414141 41414141 41414141
0012ff48  00407000 00401327 00000001 01429fb0
0012ff58  0142bf70 00000000 00000000 7ffdf000
0012ff68  00000000 00000000 0012ff5c 00000000
0012ff78  0012ffc4 00402c50 004060b8 00000000
0012ff88  0012ff94 76701174 7ffdf000 0012ffd4
0012ff98  76f2b3f5 7ffdf000 775da14d 00000000
0012ffa8  00000000 7ffdf000 00000000 00000000
0:000> dd edi
01756000  ???????? ???????? ???????? ????????
01756010  ???????? ???????? ???????? ????????
01756020  ???????? ???????? ???????? ????????
01756030  ???????? ???????? ???????? ????????
01756040  ???????? ???????? ???????? ????????
01756050  ???????? ???????? ???????? ????????
01756060  ???????? ???????? ???????? ????????
01756070  ???????? ???????? ???????? ????????

我们看到,当我们复制第0x11个41的的时候产生的异常,(复制是未完成的)

而且edi的地址我们也是不能访问的

可以看到属性也是私有属性

0:000> !address edi
Usage:                  PageHeap
Allocation Base:        01750000
Base Address:           01756000
End Address:            01850000
Region Size:            000fa000
Type:                   00020000MEM_PRIVATE
State:                  00002000MEM_RESERVE
Protect:                00000000
More info:              !heap -p 0x1750000
More info:              !heap -p -a 0x1756000

直接定位到了导致溢出的地方,这对我们分析漏洞是很好的

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ff48 00401327 00000001 01429fb0 0142bf70 image00400000+0x1084
0012ff88 76701174 7ffdf000 0012ffd4 76f2b3f5 image00400000+0x1327
0012ff94 76f2b3f5 7ffdf000 775da14d 00000000 kernel32!BaseThreadInitThunk+0xe
0012ffd4 76f2b3c8 00401273 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x70
0012ffec 00000000 00401273 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b

我们看看上一层的函数,两个都是可以的,b就是before的意思

0:000> ub image00400000+0x1327
image00400000+0x1301:
00401301 e847120000      call    image00400000+0x254d (0040254d)
00401306 e8af0e0000      call    image00400000+0x21ba (004021ba)
0040130b a150994000      mov     eax,dword ptr [image00400000+0x9950 (00409950)]
00401310 a354994000      mov     dword ptr [image00400000+0x9954 (00409954)],eax
00401315 50              push    eax
00401316 ff3548994000    push    dword ptr [image00400000+0x9948 (00409948)]
0040131c ff3544994000    push    dword ptr [image00400000+0x9944 (00409944)]
00401322 e8d9fcffff      call    image00400000+0x1000 (00401000)
0:000> ub 00401327 
image00400000+0x1301:
00401301 e847120000      call    image00400000+0x254d (0040254d)
00401306 e8af0e0000      call    image00400000+0x21ba (004021ba)
0040130b a150994000      mov     eax,dword ptr [image00400000+0x9950 (00409950)]
00401310 a354994000      mov     dword ptr [image00400000+0x9954 (00409954)],eax
00401315 50              push    eax
00401316 ff3548994000    push    dword ptr [image00400000+0x9948 (00409948)]
0040131c ff3544994000    push    dword ptr [image00400000+0x9944 (00409944)]
00401322 e8d9fcffff      call    image00400000+0x1000 (00401000)

可以看到是调用00401000时导致的异常

我们看看这个函数,f就是function的意思,表示查看整个函数

0:000> uf 401000
image00400000+0x1000:
00401000 83ec24          sub     esp,24h
00401003 b908000000      mov     ecx,8
00401008 53              push    ebx
00401009 55              push    ebp
0040100a 56              push    esi
0040100b 57              push    edi
0040100c be44704000      mov     esi,offset image00400000+0x7044 (00407044)
00401011 8d7c2410        lea     edi,[esp+10h]
00401015 f3a5            rep movs dword ptr es:[edi],dword ptr [esi] ;初始化局部变量的值为0x20个A
00401017 68ffff0000      push    0FFFFh
0040101c 6800100000      push    1000h;堆块大小
00401021 6a04            push    4
00401023 a4              movs    byte ptr es:[edi],byte ptr [esi]
00401024 ff150c604000    call    dword ptr [image00400000+0x600c (0040600c)] ;HeapCreate创建0x1000大小的堆块
0040102a 8be8            mov     ebp,eax;堆块的起始地址先放在ebp 
0040102c a16c704000      mov     eax,dword ptr [image00400000+0x706c (0040706c)]
00401031 48              dec     eax
00401032 a36c704000      mov     dword ptr [image00400000+0x706c (0040706c)],eax
00401037 7808            js      image00400000+0x1041 (00401041)
image00400000+0x1039:
00401039 ff0568704000    inc     dword ptr [image00400000+0x7068 (00407068)]
0040103f eb0d            jmp     image00400000+0x104e (0040104e)
;上面这几条指令不知道是干啥的,看ida也看不出啥,好像是填充缓冲区的
 if ( --stru_407068._cnt < 0 )
    _filbuf(&stru_407068);
  else
    ++stru_407068._ptr;
image00400000+0x1041:
00401041 6868704000      push    offset image00400000+0x7068 (00407068)
00401046 e896000000      call    image00400000+0x10e1 (004010e1) ;getchar函数
0040104b 83c404          add     esp,4
image00400000+0x104e:
0040104e 6a10            push    10h
00401050 6a00            push    0
00401052 55              push    ebp
00401053 ff1508604000    call    dword ptr [image00400000+0x6008 (00406008)]
;申请0x10大小的内存
00401059 8bd8            mov     ebx,eax;将申请到的地址赋值到ebx
0040105b 53              push    ebx
0040105c 6830704000      push    offset image00400000+0x7030 (00407030)
00401061 e84a000000      call    image00400000+0x10b0 (004010b0)
;调用printf格式化输出
00401066 8d7c2418        lea     edi,[esp+18h] ;获取0x20个A在栈上的地址 
0040106a 83c9ff          or      ecx,0FFFFFFFFh
0040106d 33c0            xor     eax,eax ;0
0040106f 83c408          add     esp,8
00401072 f2ae            repne scas byte ptr es:[edi] ;比较看看eax(字符串结束符)在edi的哪个位置
00401074 f7d1            not     ecx
;这是典型的计算字符串长度的汇编代码,由于一开始已经设置ecx为-1(x0ffffffff),edi+1,ecx-1,所以最后not一下算出字符串的真正长度了(比如not -5 == 4)
00401076 2bf9            sub     edi,ecx ;减去字符串长度恢复edi指向0x20个A的首地址
00401078 53              push    ebx
00401079 8bc1            mov     eax,ecx ;保存长度
0040107b 8bf7            mov     esi,edi ;将0x20个A的首地址给esi
0040107d 8bfb            mov     edi,ebx ;ebx就是上面申请内存返回的地址,作为目的地址
0040107f 6a00            push    0
00401081 c1e902          shr     ecx,2 
00401084 f3a5            rep movs dword ptr es:[edi],dword ptr [esi] ;strcpy
00401086 8bc8            mov     ecx,eax
00401088 55              push    ebp
00401089 83e103          and     ecx,3
0040108c f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
0040108e ff1504604000    call    dword ptr [image00400000+0x6004 (00406004)] ;HeapFree
00401094 55              push    ebp
00401095 ff1500604000    call    dword ptr [image00400000+0x6000 (00406000)] ;HeapDestroy
0040109b 5f              pop     edi
0040109c 5e              pop     esi
0040109d 5d              pop     ebp
0040109e 33c0            xor     eax,eax
004010a0 5b              pop     ebx
004010a1 83c424          add     esp,24h
004010a4 c3              ret

打赏作者
喜欢本博客,打赏让博客永久运行,多少你说了算

您的支持将鼓励我们继续创作!

[微信] 扫描二维码打赏

[支付宝] 扫描二维码打赏

发表评论

电子邮件地址不会被公开。 必填项已用*标注