Author:[CISRG]KiSSinGGer
E-mail:kissingger@gmail.com
MSN:kyller_clemens@hotmail.com
题目有点搞......Anti-CallStack Check and Anti-Anti-CallStack Check...(;- -)
发现最近MJ0011的“基于CallStack的Anti-Rootkit HOOK检测思路”和gyzy的“基于栈指纹检测缓冲区溢出的一点思路”两篇文章有异曲同工之妙。
两者都通过检测CallStack中的返回地址来做文章。
最近在初步学习一些AntiRootkit技术,这两个不得不吸引我的眼球。
按照MJ0011大侠的逻辑,从Rootkit Detector的Hook点向上检测CallStack.
但是CallStack里面都是些DWORDs,怎么判断哪儿是参数,哪儿是返回地址呢?
我Goo了两把...普遍是用EBP回溯的方式.
考虑大部分的__stdcall的形式:
mov edi edi
push ebp
mov ebp esp
...
...
我们从dword ptr [EBP]里面可以获得上个call的EBP,dword ptr [EBP+4]里面获得需要检测的返回地址,然后EBP = dword ptr [EBP],继续找下去.找到栈基址为止.
每次得到的返回地址,判断一下它是否在一个合法的模块中.
但是,根据gyzy大侠的<编写绕过卡巴主动防御的Shellcode>一文启示,我们可以知道如下一种方式,可使这样的检测方式失效.
1.在合法的系统模块里(e.g. ntoskrnl.exe),找到一个'C3'(ret Opcode)字节,它的指针是K.
2.使用如下方式的Hook函数
HookedZwXxx(...)
{
//
// 一些参数处理操作
//
jmp __pushrealretaddr
__trickstage:
push Arg[N]
push Arg[N-1]
...
push Arg[0]
push K
jmp ZwXxx; //调用原始函数
__pushrealretaddr:
call __trickstage
realretaddr:
//
// 另一些结果处理操作
//
}
这样,在ZwXxx深处检查调用栈,dword ptr [EBP+4]是一个处于合法模块中的地址K.
我写了一个如下的ring3示例程序.
定义如下一些函数:
int __stdcall Call_C(int a, int b)
{
check_callstack();
return a+b;
}
int __stdcall Call_B(int a, int b)
{
return Call_C(a,b);
}
int __stdcall Call_A(int a, int b)
{
return Call_B(a,b);
}
调用次序是A->
E-mail:kissingger@gmail.com
MSN:kyller_clemens@hotmail.com
题目有点搞......Anti-CallStack Check and Anti-Anti-CallStack Check...(;- -)
发现最近MJ0011的“基于CallStack的Anti-Rootkit HOOK检测思路”和gyzy的“基于栈指纹检测缓冲区溢出的一点思路”两篇文章有异曲同工之妙。
两者都通过检测CallStack中的返回地址来做文章。
最近在初步学习一些AntiRootkit技术,这两个不得不吸引我的眼球。
按照MJ0011大侠的逻辑,从Rootkit Detector的Hook点向上检测CallStack.
但是CallStack里面都是些DWORDs,怎么判断哪儿是参数,哪儿是返回地址呢?
我Goo了两把...普遍是用EBP回溯的方式.
考虑大部分的__stdcall的形式:
mov edi edi
push ebp
mov ebp esp
...
...
我们从dword ptr [EBP]里面可以获得上个call的EBP,dword ptr [EBP+4]里面获得需要检测的返回地址,然后EBP = dword ptr [EBP],继续找下去.找到栈基址为止.
每次得到的返回地址,判断一下它是否在一个合法的模块中.
但是,根据gyzy大侠的<编写绕过卡巴主动防御的Shellcode>一文启示,我们可以知道如下一种方式,可使这样的检测方式失效.
1.在合法的系统模块里(e.g. ntoskrnl.exe),找到一个'C3'(ret Opcode)字节,它的指针是K.
2.使用如下方式的Hook函数
HookedZwXxx(...)
{
//
// 一些参数处理操作
//
jmp __pushrealretaddr
__trickstage:
push Arg[N]
push Arg[N-1]
...
push Arg[0]
push K
jmp ZwXxx; //调用原始函数
__pushrealretaddr:
call __trickstage
realretaddr:
//
// 另一些结果处理操作
//
}
这样,在ZwXxx深处检查调用栈,dword ptr [EBP+4]是一个处于合法模块中的地址K.
我写了一个如下的ring3示例程序.
定义如下一些函数:
int __stdcall Call_C(int a, int b)
{
check_callstack();
return a+b;
}
int __stdcall Call_B(int a, int b)
{
return Call_C(a,b);
}
int __stdcall Call_A(int a, int b)
{
return Call_B(a,b);
}
调用次序是A->