[原创]Windbg和IDA脚本辅助分析
本文尝试解决另个问题:内核漏洞快速定位漏洞触发点 && Windbg执行流在IDA中高亮标示
内核漏洞快速定位漏洞触发点:
拿到一个内核漏洞的POC,首先我们需要定位触发漏洞,提权的地方。直接看反汇编代码往往不容易看出来
内核漏洞常见的目标是提权,一般会替换本进程的token为System的token,这种办法简单粗暴,成功率极高,所以往往为各黑客所爱。
既然要提权token,那么必须要对token所在的地址有写入操作,本文的思路就是对token所在地址下写入断点,一但触发,便是来到替换token,也就是提权的地方,再栈回溯,就能找到触发漏洞的函数了。下面我们来演示一下,我写了个windbg脚本:
$$注意一:凡是寄存器操作需加上r
$$注意二:打印字符串用%ma
$$此脚本用到了一个硬编码token偏移 0xf8,请根据你的实际系统修正它。
r @$t0=@$proc;
r @$t1=@$proc;
.while(1)
{
.if ((poi(@$t1+0xb8)-0xb8) != @$t0 )
{
$$比较是否为test进程
.if ( poi(@$t1+0x16c)==0x74736574 )
{
.printf "进程名:%ma\t",@$t1+0x16c;
.printf "进程EPROCESS:%x\t",@$t1;
.printf "进程TOKEN:%x\n",poi(@$t1+0xf8);
.printf "请输入:\n";
.printf "ba w1 %x+f8 \".if (wo(%x+f8+2)!=0x%x){} .else{gc;}\"",@$t1,@$t1,wo(@$t1+0xf8+2);
}
r @$t1=poi(@$t1+0xb8)-0xb8;
}
.else
{
.break;
}
}
代码便不会解释,如有不明,烦请百度。我用CVE-2014-4113这个漏洞演示,把win32.exe复制到虚拟机改名为test.exe。然后

回到物理机,把前面的代码保存为js.txt,然后

执行提示的那条命令,结果如下

然后物理机g跑起来,虚拟机也g跑起来,稍等一会,断在这里,ring3地址

再看看此是test.exe进程token

这个token恰好是System的token,请看

找到了提权的地方后,就可以对test.exe反汇编代码进行进一步分析了。
Windbg执行流在IDA中高亮标示:
前面得到了提权的地方,但是往往漏洞触发条件很复杂,需要经过很多函数,我们需要逐步跟踪这个函数,理解漏洞的成功,而有些函数及其庞大,分支又多,我们需要确定走哪些分析导致漏洞触发。前面得到了关键点,打印此时的堆栈
ChildEBP RetAddr Args to Child WARNING: Stack unwind information not available. Following frames may be wrong. 99a80a24 96d094f3 fffffffb 000001ed 0192fac4 test+0x186d 99a80a64 96d095c5 fffffffb 000001ed 0192fac4 win32k!xxxSendMessageTimeout+0x1ac 99a80a8c 96d892fb fffffffb 000001ed 0192fac4 win32k!xxxSendMessage+0x28 99a80aec 96d88c1f 99a80b0c 00000000 0192fac4 win32k!xxxHandleMenuMessages+0x582 99a80b38 96d8f8f1 ffb89bb0 96e6f580 00000000 win32k!xxxMNLoop+0x2c6 99a80ba0 96d8f9dc 0000001c 00000000 ffffd8f0 win32k!xxxTrackPopupMenuEx+0x5cd 99a80c14 83e501ea 0009017d 00000000 ffffd8f0 win32k!NtUserTrackPopupMenuEx+0xc3 99a80c14 772570b4 0009017d 00000000 ffffd8f0 nt!KiFastCallEntry+0x12a 0192fad8 76b6483e 76b52243 0009017d 00000000 ntdll!KiFastSystemCallRet 0192fadc 76b52243 0009017d 00000000 ffffd8f0 USER32!NtUserTrackPopupMenuEx+0xc 0192fafc 013c1604 0009017d 00000000 ffffd8f0 USER32!TrackPopupMenu+0x1b 0192fb8c 75d93c45 00000000 0192fbd8 772737f5 test+0x1604 0192fb98 772737f5 00000000 76bae6a7 00000000 kernel32!BaseThreadInitThunk+0xe 0192fbd8 772737c8 013c1526 00000000 00000000 ntdll!__RtlUserThreadStart+0x70 0192fbf0 00000000 013c1526 00000000 00000000 ntdll!_RtlUserThreadStart+0x1b
PS:这是CVE-2015-1701,也是用前面的windbg脚本定位到关键点,上面是断下来后的堆栈。win32k!xxxSendMessageTimeout是最直接调用了提权函数,我们首先想到这这个函数下断,然后分析。但是win32k!xxxSendMessageTimeout频繁被系统调用,所以不好确定什么时候调用了提权函数。我选择在USER32!NtUserTrackPopupMenuEx下断。来到win32k!xxxSendMessageTimeout后
.logopen "C:\Users\Netfairy\Desktop\test.txt"
把指令流记录下来,然后一直按p,其实按一次p,接着按住回车键就可以。一会之后,windbg记录的指令流如下:

接着使用这个IDA脚本:
//
//IDC脚本,用于配合windbg动态调试得到的指令流,自动化用高亮绿色在IDA中标示出来,方便静态分析
//By www.netfairy.net
//
#include <idc.idc>
extern ida_image_base_addr,windbg_image_base_addr,offset; //全局变量
//主函数
static main(void)
{
auto i,temp;
auto FileName,FileLength,hFile,str,Target="66666666";
//
//获取输入Windbg和IDA中模块的基址
//
temp=AskStr("96c50000","Please input Windbg's Win32k.sys imageBase,For example:");
windbg_image_base_addr=xtol(temp);
temp=AskStr("bf800000","Please input IDA's Win32k.sys imageBase,For example:");
ida_image_base_addr=xtol(temp);
FileName=AskFile(0,"*.*","Please choose Windbg Instruction stream file"); //选择文件
hFile=fopen(FileName,"r+"); //读写方式打开文件
FileLength=filelength(hFile); //读取文件长度
//
//跳到指令流开头
//
for(i=0;i<FileLength;i++)
{
//往后读取八个字符
Target[0]=fgetc(hFile);
Target[1]=fgetc(hFile);
Target[2]=fgetc(hFile);
Target[3]=fgetc(hFile);
Target[4]=fgetc(hFile);
Target[5]=fgetc(hFile);
Target[6]=fgetc(hFile);
Target[7]=fgetc(hFile);
temp=xtol(Target); // 转换字符为十六进制地址
if( temp > windbg_image_base_addr && temp < 0xffffffff ) //判断地址范围
{
temp=temp-windbg_image_base_addr+ida_image_base_addr; //转换为IDA地址
str=fgetc(hFile); //因为Windbg得到的指令流有(地址)情况,需要排除它
fseek(hFile,-1,1);
if(str=='\x29') //0x29是 ')'的十六进制
{
}
else
{
Jump(temp); //首次跳转到指令流开头
fseek(hFile,0,0); //修正文件指针到文件开头
break;
}
}
else
{
fseek(hFile,-7,1);
}
}
//
//功能代码
//
for(i=0;i<FileLength;i++)
{
//往后读取八个字符
Target[0]=fgetc(hFile);
Target[1]=fgetc(hFile);
Target[2]=fgetc(hFile);
Target[3]=fgetc(hFile);
Target[4]=fgetc(hFile);
Target[5]=fgetc(hFile);
Target[6]=fgetc(hFile);
Target[7]=fgetc(hFile);
temp=xtol(Target); // 转换字符为十六进制地址
if( temp > windbg_image_base_addr && temp < 0xffffffff ) //判断地址范围
{
temp=temp-windbg_image_base_addr+ida_image_base_addr; //转换为IDA地址
str=fgetc(hFile); //因为Windbg得到的指令流有(地址)情况,需要排除它
fseek(hFile,-1,1);
if(str=='\x29') //0x29是 ')'的十六进制
{
}
else
{
SetColor(temp,1,0x98fb98); //染高亮绿色
}
}
else
{
fseek(hFile,-7,1);
}
}
}
根据提示输入相应参数,一会之后

图一

图二
下面就可以进行进一步的分析了,还是看IDA舒服!!!
