一个DNS报文格式如下:
"/xEB/x0B" //报文ID,可以随意设置,但在这个漏洞里是别有用途的,后面会说到
"/x80/x00" //报文FLAG,15位置1表示这是一个答复报文
"/x00/x01" //问题数量
"/x00/x01" //答复数量
"/xXX/xXX" //授权资源记录数,在这里不重要,随便设置
"/xXX/xXX" //格外信息资源记录数,在这里不重要,随便设置
以上部分为DNS报文头
"/xXX/xXX/x..." //域名,格式为每个分段域名长度+域名内容,比如
www.buaa.edu.cn就是
/x03/x77/x77/x77/x04/x62/x75/x61/x61/x03/x65/x64/x75/x02/x63/x6e/x00
w w w b u a a e d u c n
/x00表示到了末尾。处理的时候会把那长度记录数换成0x2e,就是".",就完成了处理。
剩下的就是在该进程地址空间内分配虚拟地址,锁定,并拷贝SHELLCODE过去,依次调用API为:ZwOpenProcess(这里要注意,如果没改变CR3的话这个调用会导致蓝屏,因为地址空间不符)->ZwAllocateVirtualMemory->ZwLockVirtualMemory->ZwWriteVirtualMemory,为了通用性我用mov eax, API NUMBER; int 2e这样的底层接口来调用API。在调用ZwWriteVirtualMemory之前我们得先修改该线程下次要执行的EIP,它是保存在KTRAP_FRAME+0x68处,把它修改为我们分配的地址。KTRAP_FRAME在线程堆栈底InitialStack-x29c的地方,ETHREAD+0x128直接指向该地址。记得将原来的EIP保存在我们的用户态SHELLCODE中,类似push 0x12345678; ret这样的格式,代码就会返回12345678的地址,所以在内存中就是/x68/x78/x56/x34/x12/xc3,将原来的返回地址覆盖那个12345678就行了,在执行完我们的功能代码后线程会恢复正常执行。