最近一直在学习汇编和逆向因为我汇编基础极差(只看过几天IBM汇编,正在对自己失去信心..)
所以为了继续学习给自己点动力,就试着在0基础的状态下做了 failwest 兄<【技术专题】软件漏洞分析入门 > 教程中给的一个实验ms06-040漏洞..
因为上面所说的情况导致对这个简单的函数NetpwPathCanonicalize的逆向困难重重..
总共花了整整10个小时左右.才完全弄明白到底这个函数做了什么..
嘿嘿这可是我的第一次逆向..期间的苦与乐自己最清楚..觉得对汇编和逆向有了更进一步的理解 嘿嘿
感谢: failwest 兄的 <【技术专题】软件漏洞分析入门 >
推荐跟我一样的新手看看哦:http://bbs.pediy.com/showthread.php?...id=phpforce_42
目的:希望所有在学习逆向或者汇编的同学遇到困难的时候一定不要放弃.因为等待你的就是你的梦.它离你很近很近..
用到的工具:IDA(静态反汇编工具),ollydbg(动态调试工具),和微软系统自带计算器(嘿16进制的运算我经常算混成10进制的)
以上废话可不看,目的在于抒发小菜激动的心情 下面进入正题..
代码:
;NetpwPathCanonicalize函数 .text:7517F7E2 ; =============== S U B R O U T I N E ======================================= .text:7517F7E2 .text:7517F7E2 ; Str 是第一个参数 一个指向unicode字符串的指针(用来生成路径的第二部分) .text:7517F7E2 ; lpWideCharStr 是第二个参数 用于接收格式化后的字符串 .text:7517F7E2 ; arg_8 是第三个参数 用来指明参数2所指定的buffer大小 .text:7517F7E2 ; Source 是第四个参数 一个指向unicode字符串的指针(用来生成路径的第一部分) .text:7517F7E2 ; arg_10 是第五个参数 一个无用指针 .text:7517F7E2 ; arg_14 标志位必须为0 .text:7517F7E2 ; Attributes: bp-based frame .text:7517F7E2 .text:7517F7E2 ; int __stdcall NetpwPathCanonicalize(wchar_t *lpWideCharStr, int, int, wchar_t *Source, int, int) .text:7517F7E2 public NetpwPathCanonicalize .text:7517F7E2 NetpwPathCanonicalize proc near ; CODE XREF: I_NetPathCanonicalize:loc_7517FB38p .text:7517F7E2 ; NetpwPathCompare+96p ... .text:7517F7E2 .text:7517F7E2 Str = dword ptr 8 .text:7517F7E2 lpWideCharStr = dword ptr 0Ch .text:7517F7E2 arg_8 = dword ptr 10h .text:7517F7E2 Source = dword ptr 14h .text:7517F7E2 arg_10 = dword ptr 18h .text:7517F7E2 arg_14 = dword ptr 1Ch .text:7517F7E2 .text:7517F7E2 push ebp .text:7517F7E3 mov ebp, esp .text:7517F7E5 push ebx .text:7517F7E6 mov ebx, [ebp+Source] .text:7517F7E9 push esi .text:7517F7EA push edi .text:7517F7EB xor edi, edi .text:7517F7ED cmp ebx, edi ; 判断4号参数是否等于0 .text:7517F7EF jz short loc_7517F7FA .text:7517F7F1 cmp [ebx], di ; 4号参数 字符串指针所指的地址不等于0 的话 .text:7517F7F4 jz short loc_7517F7FA .text:7517F7F6 xor esi, esi ; 4号参数 字符串指针所指的地址中第一个字符不等于0的话 .text:7517F7F8 jmp short loc_7517F7FD .text:7517F7FA ; --------------------------------------------------------------------------- .text:7517F7FA .text:7517F7FA loc_7517F7FA: ; CODE XREF: NetpwPathCanonicalize+Dj .text:7517F7FA ; NetpwPathCanonicalize+12j .text:7517F7FA push 1 ; 如果4号参数一个字符串指针等于0或者此指针指向的字符串等于0的话 .text:7517F7FC pop esi .text:7517F7FD .text:7517F7FD loc_7517F7FD: ; CODE XREF: NetpwPathCanonicalize+16j .text:7517F7FD mov eax, [ebp+arg_10] .text:7517F800 test [ebp+arg_14], 7FFFFFFEh .text:7517F807 mov eax, [eax] .text:7517F809 mov [ebp+Source], eax ; 将参数4里的指针改为 参数5里的指针 .text:7517F80C jz short loc_7517F813 .text:7517F80E push 57h .text:7517F810 pop eax .text:7517F811 jmp short loc_7517F869 .text:7517F813 ; --------------------------------------------------------------------------- .text:7517F813 .text:7517F813 loc_7517F813: ; CODE XREF: NetpwPathCanonicalize+2Aj .text:7517F813 cmp eax, edi ; 判断参数5里的指针是否等于0 .text:7517F815 jnz short loc_7517F828 .text:7517F817 lea eax, [ebp+Source] ; 如果参数5等于0 .text:7517F81A push edi ; int .text:7517F81B push eax ; int .text:7517F81C push [ebp+Str] ; lpWideCharStr .text:7517F81F call NetpwPathType ;对字符串做检查的函数 .text:7517F824 cmp eax, edi .text:7517F826 jnz short loc_7517F869 .text:7517F828 .text:7517F828 loc_7517F828: ; CODE XREF: NetpwPathCanonicalize+33j .text:7517F828 cmp esi, edi ; 如果参数5里的指针不等于0 .text:7517F828 ; 判断esi是否等于0 .text:7517F82A jnz short loc_7517F83B ; 存储路径的指针是否为0 .text:7517F82C lea eax, [ebp+arg_14] ; 如果esi等于0 .text:7517F82F push edi ; int .text:7517F830 push eax ; int .text:7517F831 push ebx ; lpWideCharStr .text:7517F832 call NetpwPathType ; 将edi=0,eax=0,和参数4 合成路径的第一部分入栈,调用函数 .text:7517F837 cmp eax, edi ; 判断路径的第一部分 参数4的长度不能大于103 .text:7517F839 jnz short loc_7517F869 ; eax 不等于 edi 跳转 退出函数 .text:7517F83B .text:7517F83B loc_7517F83B: ; CODE XREF: NetpwPathCanonicalize+48j .text:7517F83B cmp [ebp+arg_8], edi ; 如果esi不等于0 .text:7517F83B ; .text:7517F83E jnz short loc_7517F847 ; 不等于0 .text:7517F840 mov eax, 84Bh .text:7517F845 jmp short loc_7517F869 .text:7517F847 ; --------------------------------------------------------------------------- .text:7517F847 .text:7517F847 loc_7517F847: ; CODE XREF: NetpwPathCanonicalize+5Cj .text:7517F847 mov esi, [ebp+lpWideCharStr] ; 不等于0 .text:7517F84A push edi ; int 0 .text:7517F84B push [ebp+arg_8] ; int 存储路径buffer的大小 440 .text:7517F84E mov [esi], di .text:7517F851 push esi ; 接收合成后路径的地址 .text:7517F852 push [ebp+Str] ; Str 指向合成路径第二部分字符串的指针 .text:7517F855 push ebx ; Source 指向合成路径第一部分字符串的指针 .text:7517F856 call sub_7517FC68 ; CanonicalizePathName<--生成新路径的函数 漏洞就出现在此函数中 .text:7517F85B cmp eax, edi .text:7517F85D jnz short loc_7517F869 .text:7517F85F push edi ; int .text:7517F860 push [ebp+arg_10] ; int .text:7517F863 push esi ; lpWideCharStr .text:7517F864 call NetpwPathType ; 对字符串做检查的函数 .text:7517F869 .text:7517F869 loc_7517F869: ; CODE XREF: NetpwPathCanonicalize+2Fj .text:7517F869 ; NetpwPathCanonicalize+44j ... .text:7517F869 pop edi .text:7517F86A pop esi .text:7517F86B pop ebx .text:7517F86C pop ebp .text:7517F86D retn 18h .text:7517F86D NetpwPathCanonicalize endp ;CanonicalizePathName函数 .text:7517FC68 ; =============== S U B R O U T I N E ======================================= .text:7517FC68 ;CanonicalizePathName函数 .text:7517FC68 ; 生成新路径 .text:7517FC68 ; Source 参数1 合成路径第一段 .text:7517FC68 ; str 参数2 合成路径第二段 .text:7517FC68 ; arg_8 参数3 接收合成后路径的地址 .text:7517FC68 ; arg_c 参数4 存放合成后路径的buffer的大小 440 .text:7517FC68 ; arg_10 参数5 0 .text:7517FC68 ; Attributes: bp-based frame .text:7517FC68 .text:7517FC68 ; int __stdcall sub_7517FC68(wchar_t *Source, wchar_t *Str, int, int, int) .text:7517FC68 sub_7517FC68 proc near ; CODE XREF: NetpwPathCanonicalize+74p .text:7517FC68 .text:7517FC68 var_416 = word ptr -416h .text:7517FC68 Dest = word ptr -414h .text:7517FC68 Source = dword ptr 8 .text:7517FC68 Str = dword ptr 0Ch .text:7517FC68 arg_8 = dword ptr 10h .text:7517FC68 arg_C = dword ptr 14h .text:7517FC68 arg_10 = dword ptr 18h .text:7517FC68 .text:7517FC68 push ebp .text:7517FC69 mov ebp, esp .text:7517FC6B sub esp, 414h ; 抬高堆栈 414 字节 .text:7517FC71 push ebx .text:7517FC72 push esi .text:7517FC73 xor esi, esi ; esi 清0 .text:7517FC75 push edi .text:7517FC76 cmp [ebp+Source], esi ; 判断第一段字符串地址是否为0 .text:7517FC79 mov edi, ds:__imp_wcslen .text:7517FC7F mov ebx, 411h .text:7517FC84 jz short loc_7517FCED ; 当第一段字符串地址等于0时转移 .text:7517FC86 push [ebp+Source] ; Str 第一段字符串地址入栈 .text:7517FC89 call edi ; __imp_wcslen ; 判断长度并把长度存入eax寄存器中 .text:7517FC8B mov esi, eax ; 将第一段字符串的长度存入esi .text:7517FC8D pop ecx .text:7517FC8E test esi, esi .text:7517FC90 jz short loc_7517FCF4 ; 判断第一段字符串的长度为0的时候跳转 .text:7517FC92 cmp esi, ebx ; 判断1号串的长度大于等于411字节的时候跳转 .text:7517FC94 ja loc_7517FD3E ; 字符串大于等于限制长度的时候 .text:7517FC9A push [ebp+Source] ; Source 第一段字符串地址入栈 .text:7517FC9D lea eax, [ebp+Dest] ; 要存放第一段字符串的缓冲区的地址 414字节大小 .text:7517FCA3 push eax ; Dest .text:7517FCA4 call ds:__imp_wcscpy ; unicode复制函数 将参数2里的字符串拷贝到参数1所指向的缓冲区中 .text:7517FCAA mov ax, [ebp+esi*2+var_416] ; ax等于存放路径缓冲区中第一段字符串的最后一个字符 .text:7517FCB2 pop ecx .text:7517FCB3 cmp ax, 5Ch ; 将最后一个字符与5ch('\')比较 .text:7517FCB7 pop ecx .text:7517FCB8 jz short loc_7517FCD5 ; 相等跳转 .text:7517FCBA cmp ax, 2Fh ; 将最后一个字符与2fh('/')比较 .text:7517FCBE jz short loc_7517FCD5 ; 相等跳转 .text:7517FCC0 lea eax, [ebp+Dest] ; 获取存放路径的缓冲区地址 .text:7517FCC6 push offset asc_751717B8 ; 一个unicode的'\'字符数据地址入栈 .text:7517FCCB push eax ; Dest 放路径的缓冲区地址 入栈 .text:7517FCCC call ds:__imp_wcscat ; 将unicode字符'\'追加到第一段路径字符串结尾 .text:7517FCD2 pop ecx .text:7517FCD3 inc esi ; esi为路径第一部分的字符串长度加1 .text:7517FCD4 pop ecx .text:7517FCD5 .text:7517FCD5 loc_7517FCD5: ; CODE XREF: sub_7517FC68+50j .text:7517FCD5 ; sub_7517FC68+56j .text:7517FCD5 mov eax, [ebp+Str] ; .text:7517FCD5 ; eax等于参数2 合成路径的第二段的地址 .text:7517FCD8 mov ax, [eax] ; 获取合成路径第二段的第一个字 .text:7517FCDB cmp ax, 5Ch ; 与 '\'(5ch) 比较 .text:7517FCDF jz short loc_7517FCE7 ; 合成路径的第二段字符串的第一个字符如果是'\'(5c)的话就跳转 .text:7517FCE1 cmp ax, 2Fh ; 否则判断第二段字符串的第一个字符是不是'/'(2fh) .text:7517FCE5 jnz short loc_7517FCF4 ; 不是的话跳转 .text:7517FCE7 .text:7517FCE7 loc_7517FCE7: ; CODE XREF: sub_7517FC68+77j .text:7517FCE7 add [ebp+Str], 2 ; .text:7517FCE7 ; 如果第二段字符穿的第一个字符为'\'或'/'的话就在地址中去掉此字符的长度 .text:7517FCEB jmp short loc_7517FCF4 .text:7517FCED ; --------------------------------------------------------------------------- .text:7517FCED .text:7517FCED loc_7517FCED: ; CODE XREF: sub_7517FC68+1Cj .text:7517FCED mov [ebp+Dest], si .text:7517FCF4 .text:7517FCF4 loc_7517FCF4: ; CODE XREF: sub_7517FC68+28j .text:7517FCF4 ; sub_7517FC68+7Dj ... .text:7517FCF4 push [ebp+Str] ; Str .text:7517FCF4 ; 第二段字符串地址入栈 .text:7517FCF7 call edi ; __imp_wcslen ; 获取第二段字符串的长度 并保存到eax中 .text:7517FCF9 add eax, esi ; 将两段字符串的总长度保存到eax中 第一段长度为8 第二段长度为209 .text:7517FCFB pop ecx ; ecx 是第二段字符串的地址 .text:7517FCFC cmp eax, ebx ; 判断总长度是否大于等于411 漏洞出现原因(因为获取参数长度的时候一按unicode获取的就是字符个数,而做限制的时候应按411字节,但这个按411字符数所以存在漏洞) .text:7517FCFE ja short loc_7517FD3E ; 字符串大于等于限制长度的时候 .text:7517FD00 push [ebp+Str] ; Source 参数2 路径第二段地址入栈 .text:7517FD03 lea eax, [ebp+Dest] ; 获取存储 路径的buffer地址 大小414字节 .text:7517FD09 push eax ; Dest .text:7517FD0A call ds:__imp_wcscat ; 将路径第二段附加到 buffer 中的最后位置 (第一段字符串:8+'\':1+第二段字符串:209)=212=424字节 .text:7517FD10 pop ecx .text:7517FD11 lea eax, [ebp+Dest] .text:7517FD17 pop ecx .text:7517FD18 push eax .text:7517FD19 call sub_7518AE95 ; 将缓冲区中合成后的字符串里的'/'字符替换成'\' 然后返回 .text:7517FD1E lea eax, [ebp+Dest] .text:7517FD24 push eax ; Str 存放路径的buffer地址入栈 .text:7517FD25 call sub_7518AEB3 ; 没理解上去是干什么的.. .text:7517FD2A test eax, eax .text:7517FD2C jnz short loc_7517FD43 .text:7517FD2E lea eax, [ebp+Dest] .text:7517FD34 push eax .text:7517FD35 call sub_7518AFE2 .text:7517FD3A test eax, eax .text:7517FD3C jnz short loc_7517FD43 .text:7517FD3E .text:7517FD3E loc_7517FD3E: ; CODE XREF: sub_7517FC68+2Cj .text:7517FD3E ; sub_7517FC68+96j .text:7517FD3E push 7Bh ; 字符串大于等于限制长度的时候 .text:7517FD40 pop eax .text:7517FD41 jmp short loc_7517FD7A .text:7517FD43 ; --------------------------------------------------------------------------- .text:7517FD43 .text:7517FD43 loc_7517FD43: ; CODE XREF: sub_7517FC68+C4j .text:7517FD43 ; sub_7517FC68+D4j .text:7517FD43 lea eax, [ebp+Dest] .text:7517FD49 push eax ; Str .text:7517FD4A call edi ; __imp_wcslen .text:7517FD4C lea eax, [eax+eax+2] ; 获取总字节数 424字节 .text:7517FD50 pop ecx .text:7517FD51 cmp eax, [ebp+arg_C] ; 将总字节424与参数buffer的大小440比较 .text:7517FD54 jbe short loc_7517FD66 ; 小于等于转移 .text:7517FD56 mov ecx, [ebp+arg_10] .text:7517FD59 test ecx, ecx .text:7517FD5B jz short loc_7517FD5F .text:7517FD5D mov [ecx], eax .text:7517FD5F .text:7517FD5F loc_7517FD5F: ; CODE XREF: sub_7517FC68+F3j .text:7517FD5F mov eax, 84Bh .text:7517FD64 jmp short loc_7517FD7A .text:7517FD66 ; --------------------------------------------------------------------------- .text:7517FD66 .text:7517FD66 loc_7517FD66: ; CODE XREF: sub_7517FC68+ECj .text:7517FD66 lea eax, [ebp+Dest] .text:7517FD6C push eax ; Source .text:7517FD6D push [ebp+arg_8] ; Dest .text:7517FD70 call ds:__imp_wcscpy ; 将局部buffer中的合成后路径的字符串 拷贝到 参数中指定的buffer中 .text:7517FD76 pop ecx .text:7517FD77 xor eax, eax .text:7517FD79 pop ecx .text:7517FD7A .text:7517FD7A loc_7517FD7A: ; CODE XREF: sub_7517FC68+D9j .text:7517FD7A ; sub_7517FC68+FCj .text:7517FD7A pop edi .text:7517FD7B pop esi .text:7517FD7C pop ebx .text:7517FD7D leave .text:7517FD7E retn 14h ; 缓冲区的返回地址被覆盖为第二个字符串中第 408,409,410,411 个字符 .text:7517FD7E sub_7517FC68 endp
溢出前的堆栈
成功溢出后的堆栈 注意返回地址已经被改为71717171
对这个函数的逆向
暂时我只是了解了这个漏洞.. 下一步准备写一个简单的shellcode.. 增加一下汇编的编程能力
经历了这么长的时间发现有些体力不支..
可能有些语言组织的不好希望不要见怪..
