堆溢出原理
调试代码:
#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
这么简单的程序我们直接在复制地方下断吧
继续
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设置,也可以通过命令
可以在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 ................ ...... ...... ...... 还有很多
页堆
我们常常需要定位导致漏洞的代码或者函数,
而前面的堆尾检查只是检查堆被破坏后的情景
为此微软就引入页堆的概念,开启后会在堆块中增加不可访问的栅栏页,当覆盖到栅栏页就会触发异常
这次我们使用命令行开启页堆
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