**************************
*标题:Egg hunters                    
*作者:Peter Van  Eeckhoutte    
*翻译:ouchz                              
*时间:2010-09-14                     
**************************
师兄给的资料,看的时候顺便给翻译了。
第一次做还不太熟悉,特别是Egg Hunter与Egg,一直纠结要不要就写英文,后来想想写成鸡蛋猎人和找鸡蛋会利于中文传诵,于是就这样了。文章里应该还有错,大家发现了就提哈,我好及时更改


Peter van Eeckhoutte的博客
【知识不是一成不变,而是随时改变】
程序开发指导第八章: Win32 找鸡蛋
Pve 2010年1月9日,星期六
简介:
现在离复活节还早,应该是个谈谈怎么样找鸡蛋的好时候(所以当复活节兔子再带给你一个0 day 漏洞的时候你就能有所准备)。
在这部程序开发指导的前几部分,我们谈论了堆栈溢出,以及怎样利用它们执行任意代码。在我们之前构建的所有开发中,shellcode安放的位置或多或少是静态的或是可以利用寄存器来引用的(而不是硬编码的堆栈地址),兼顾稳定性和可靠性。
这个系列的某些部分中,我讲了几种跳到shellcode的方法,比如说用一个或多个跳床转进shellcode。我讲的这些实例中,堆栈中的可用内存空间都足够装下我们完整的shellcode。
要是可用缓冲区太短装不了所有的shellcode呢?有个叫做找鸡蛋(egg_hunting)的技巧可以帮我们解决问题。Egg hunting这种技巧可以被归为“分级shellcode”,它主要可以支持你用一小段特制的shellcode来找到你的实际的(更大的)shellcode(我们的‘鸡蛋‘),原理就是通过在内存中搜索我们的最终shellcode。换句话说,一段短代码先执行,然后再去寻找真正的shellcode并执行。
要有效使用这个技巧,以下是三个重要的条件。
1.  你必须能跳到(jmp,call,push/ret)并且执行“一些”shellcode。可用缓冲区空间可以相对较小,因为它只需要包含一些所谓的“鸡蛋猎人”。
2.  最终的shellcode必须在一个可用的存储空间中(堆、栈)
3.  你必须要用一个独特的记号标记最终的shellcode。最初的shellcode(短小的“鸡蛋猎人”)会遍历内存空间,寻找这个小标记。找到它后,用一个jmp或call 指令跳转到小标记后面的代码中执行。这就意味着你需要在鸡蛋猎人代码中定义这个标记,并且把标记写到实际shellcode的前面。
搜索内存是相当需要处理器资源的,并且会花一段时间。所以你使用鸡蛋猎人的时候,你会发现:
1.搜寻内存时,很快CPU就被沾满了。
2.执行shellcode前会先花上一段时间。(假如你有3GB随机存储器)
历史&基本方法
只有小部分手册上讲过这个主题:Skape前阵子写过相关的文章,你也可以在网上找到一些关于面向堆栈的egg hunting的信息。
Skape的文档确实是因特网上能找到的最好的关于egg hunting的资料。其中写了一些针对Linux和Windows的技巧与方法,并且很清楚地解释了egg hunting的工作原理,以及怎样安全地搜索内存。
在此我不重复egg hunting的技术细节,因为Skape的文章写得已经很细了,也清楚地说明了问题。我只用几个例子讲讲怎么在堆栈溢出中实现它们。
记住以下几点:
标记一定要特殊(通常你需要在鸡蛋猎人中定义四个字节的标签,然后在实际的shellcode前置标签,所以一共8个字节。
你需要测试哪种方法适合搜索某种特定的开发(在我的系统上,NtAccessCheakAndAuditAlarm看来是最优的)。
SEH(结构化异常处理)的方法需要60字节,IsBadReadPtr需啊哟37字节,NtDispalyString方法需要32字节。(最后一种方法只适用于NT架构,其他的在Windows 9x下也可以工作。)
鸡蛋猎人代码
正如上面谈到的,针对Windows开发,Skape概述了3种不同的找鸡蛋的技术。同样,我不会解释鸡蛋猎人的详细原理,我只提供实现鸡蛋猎人的代码。
选择合适的找鸡蛋技术主要基于下面的因素:
1.  运行鸡蛋猎人代码的可用空间。
2.  针对一个给定的开发,判断一种搜索方法是否适合你的电脑很简单测试下就知道了。
第一种鸡蛋猎人SHE注入
鸡蛋猎人长度:60字节,鸡蛋大小:8个字节

 
要使用这个鸡蛋猎人,你需要写成下面的机器码:

 
(w00t就是标签,你也可以写成”\x77\x30\x30\x74”)
注意:SHE注入法也许变得过时了,SafeSeh机制正成长为新的操作系统和服务包的标准。所以如果你需要在XP P3,Vista,Win7中使用鸡蛋猎人,你要么得绕过SafeSeh机制,要么就用其他的鸡蛋猎人技术(见下)。
第二种鸡蛋猎人使用IsBadReadPtr()函数
鸡蛋猎人长度:37字节,鸡蛋大小:8个字节
 

Egg Hunter的机器码如下:
 

第三种鸡蛋猎人使用NtDisplayString
鸡蛋猎人长度32字节,鸡蛋大小8字节
 

机器码如下:
 

或者在调试器中观察:
 

第四种鸡蛋猎人使用NtAccessCheckAndAuditAlarm()
这种方法和第三种很相似:
 

与使用NtDisplayString不同的是,这里用NtAccessCheckSNdAuditAlarm()函数(KiServicetable中偏移量为0x02)来防止访问冲突接管鸡蛋猎人的控制权。
(获取更多关于NtAccessCheck的信息:
http://undocumented.rawol.com/sbs-w2k-5-monitoring-native-api-calls.pdf
http://xosmos.net/txt/nativapi.html)
简单介绍NtDisplayString/NtAccessCheckAndAuditAlarm鸡蛋猎人的工作原理
这两种鸡蛋猎人使用相似的技术,只是使用不同的系统调用来检查是否有非法访问。(并逃过杀毒软件查杀)
NtDiaplayString()函数原型:
 

NtAccessCheckAndAuditAlarm()函数原型
  

参见:http://undocumented.ntinternals.net/
下面对汇编代码做分析:
or dx,0xfff;获取页尾地址
inc edx;计数器
push edx;
push byte +0x02;NtAccessCheckAndAuditAlarm的调用号
;或者用0x43调用NtDisPlayString()
pop eax  ;0x02或0x43装入eax,这样就能当做参数调用系统函数了
int 0x2e  ;告诉内核我想用之前的寄存器调用系统函数
cmp al,0x5    ;检查是否触发非法访问
    ;0xc0000005==ACCESS_VIOLATION 
pop edx    ;返回edx
je xxxx    ;74 EF   跳回开头dx  0xfff处
mov eax,0x50905090;标签(鸡蛋)
mov edi,edx;  edi用作指针
scasd    ;对比状态位
jnz xxxxxx  ;75EA跳回inc ebx,检查找到没
scasd    ;当找到鸡蛋后
jnz xxxxx  ;跳回inc edx
    ;仅当第一个鸡蛋被找到
jmp edi  ;edi指向shellcode的开头

感谢Shahin Ramezany