hook api 异常问题

ZOthello 2008-07-25 01:20:51
经过一段时间的努力,终于把关于hook api的东西搞定了,可是出现了几个小问题,还不是很清楚,特发帖讨论,希望各位大侠多多关注。
下面是一段hook NtCreateProcess的代码,很挫,见笑了:
NTSTATUS FakedZwCreateProcess(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN HANDLE InheritFromProcessHandle,
IN BOOLEAN InheritHandles,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL,
IN HANDLE Unknown
)
{
NTSTATUS ntstatus;
int mark;
int i;
char aProcessName[MAXPROCNAMELEN];
char aPathName[MAXPROCNAME];
char data[MAXDATALEN];
DbgPrint("create process begin:\n");

RtlZeroMemory(aProcessName,MAXPROCNAMELEN);
RtlZeroMemory(aPathName,MAXPROCNAME);
RtlZeroMemory(data,MAXDATALEN);


GetProcess(aProcessName);//调用native api的进程名
if (strstr(aProcessName,"anti"))//本程序的跳过
{
ntstatus=RealZwCreateProcess(
ProcessHandle,
DesiredAccess,
ObjectAttributes,
InheritFromProcessHandle,
InheritHandles,
SectionHandle ,
DebugPort ,
ExceptionPort,
Unknown
);
return ntstatus;

}
GetFullProcessName(SectionHandle,aPathName);//得到路径名


DbgPrint("ZwCreateProcess is called by %s\n",aProcessName);
DbgPrint("The path is %s\n",aPathName);//调用程序的路径

sprintf(data,"%s##\tZwCreateProcess\t%s", aProcessName,aPathName);


mark=manage(data);//由他来进行输入缓冲,判断是否危险动作的处理。
if (mark==1)
{

ntstatus=RealZwCreateProcess(
ProcessHandle,
DesiredAccess,
ObjectAttributes,
InheritFromProcessHandle,
InheritHandles,
SectionHandle ,
DebugPort ,
ExceptionPort,
Unknown
);
DbgPrint("mark =1\n");
}
else if (mark==2)
{
DbgPrint("mark==2");
ntstatus= STATUS_ACCESS_DENIED;//用户选择了拒绝执行

}

return ntstatus;

}

我把出问题的代码已经用红色标出来了。
我把这个程序描述一下,它hook 了 几个操作注册表的函数,以及NtCreateProcess和NtWriteVirtualMemory。
程序运行时会蓝屏,出错代码是PAGED_FAULT_IN_NONPAGED_AREA ,然后查看crashdump发现是RtlZeroMemory的问题,删除这个RtlZeroMemory后,问题解决。不明白是为什么会这样。
但是我把hook注册表函数去掉后,光hook NtCreateProcess和NtWriteVirtualMemory就不会出问题,程序的代码一点都没变。
大家觉得可能是什么地方的问题,我是第一次写驱动,所以可能有些地方不太清楚,请各位赐教~~
...全文
425 52 打赏 收藏 转发到动态 举报
写回复
用AI写文章
52 条回复
切换为时间正序
请发表友善的回复…
发表回复
hxfjb 2008-07-29
  • 打赏
  • 举报
回复
to cnzdgs:
1 就算是将调用修改默认是__stdcall,作为程序员,还是要明确地声明为__stdcall,否则这样的错误以后你想找到都非常困难;
2 如果是c调用,那么现在没问题,不等于以后没问题。是否有问题这取决于调用ZwCreateProcess的函数是如何调整堆栈的。调用函数时,压栈先压参数,然后才是返回的eip,
而ret的时候,将返回地址出栈,赋给eip,此时栈中比预期多了**h个字节。
如果你的调用者都是使用基址来访问堆栈中的内容,那就没问题,即这样访问mov [ebp+***], ****。但如果
调用函数在ret之前使用[esp+***]的方式访问,那调用者访问的就不是预期的内容,出现什么问题很难说。

通常调用者都是使用ebp来访问栈的,但也存在不少使用esp来访问的,你不能要求调用者如何如何,因此。。。

ZOthello 2008-07-29
  • 打赏
  • 举报
回复
情况超出了控制,我先把帖结了,问题我再考虑下。有什么进展再发帖通知大家,一块讨论,多谢各位的关注,特别是cnzdgs。
linxren 2008-07-29
  • 打赏
  • 举报
回复
虚心学习...
wfh_178 2008-07-29
  • 打赏
  • 举报
回复
学习了
cnzdgs 2008-07-28
  • 打赏
  • 举报
回复
不必争了,做个程序看一下就清楚。大家各自做个驱动程序,定义一个函数,不指定调用约定,随便定义几个参数,编译好调试看看该函数最后返回是ret指令还是ret立即数指令。
cnzdgs 2008-07-28
  • 打赏
  • 举报
回复
默认调用约定是可以通过编译参数来指定的,DDK中提供的生成环境默认是/Gz参数编译,VC项目默认是/Gd,前者是__stdcall,后者是__cdecl,这个参数可以自己修改。
LZ用的生成环境就是默认__stdcall,不然只要一加载就会出问题,LZ在单独测试这部分功能时已经通过,所以可以确定调用约定没有问题。

内核服务都是要求运行在PASSIVE_LEVEL级别上的,可以使用分页内存。另外驱动程序中定义的全局数据,如无特别声明,都是在非分页池分配的,所以问题与内存是否分页没有关系,如果是在高IRQL使用了分页内存,应该是蓝屏显示Bug Check 0xA。
hxfjb 2008-07-28
  • 打赏
  • 举报
回复
to _zhoujianhei:
别拿驱动吓唬人啊,好像人家都没写过似的,我一直喜欢使用VC的来编译链接驱动。
hxfjb 2008-07-28
  • 打赏
  • 举报
回复
To zhoujianhei:
是的,就是在VC的IDE环境中,其实仍然是利用ddk来编译的,但默认仍然不是__stdcall,而是c调用。

To skysolo:
你既然是hook了这个函数,那么当调用这个函数的时候是这样做的:
push 参数N
...
push 参数1
call 函数地址
//这里不调整堆栈,而是你的函数调整

而你的函数不是__stdcall,你反汇编你的函数后,会发现最后一行代码是:retn,而非retn 24h。
因此当执行retn的时候:将esp中的DWORD出栈,写入eip,然后流程走到eip继续执行。堆栈少调整了24h。
你的函数自然不会有问题,但是调用你的这个hook函数的函数可就倒霉了,因为你把栈搞乱了。

zhoujianhei 2008-07-28
  • 打赏
  • 举报
回复
[Quote=引用 41 楼 hxfjb 的回复:]
内核的函数正因为是__stdcall,所以你的 函数要遵从人家的格式,否则你的hook函数就把人家的栈给搞坏了。
VC的默认调用不是__stdcall,而是C调用。而C调用中,函数是不负责调整栈的,由调用者调整。

这个都不懂,你写的hook函数肯定错误。
[/Quote]

不管是不是__stdcall调用,但没听过VC可以编译驱动的,只是借助VC的IDE环境。

如果HOOK中用到了全局变量如:DefaultValue,那么要在初始化时在非分页池中分配,否则可能被换出内存。
ZOthello 2008-07-28
  • 打赏
  • 举报
回复
跑题了啊,跑题了。
我觉得不是分页池或非分页池的问题,IRQL在DISPATCH_LEVEL下的可以访问分页池,这应该不会产生一个错误吧,况且RegMon都是这样写的~~
cnzdgs 2008-07-27
  • 打赏
  • 举报
回复
照你描述的情况,很像是fullname[MAXPATHLEN]的长度不够,你把MAXPATHLEN改大一些再试试,先试2K,不行再试4K、8K。
ZOthello 2008-07-27
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 cnzdgs 的回复:]
照你描述的情况,很像是fullname[MAXPATHLEN]的长度不够,你把MAXPATHLEN改大一些再试试,先试2K,不行再试4K、8K。
[/Quote]
试过了,还是会蓝。我觉得不是长度不够的问题,可能是这个有问题:if( lpszSubKeyVal ) {
RtlUnicodeStringToAnsiString( &keyname, lpszSubKeyVal, TRUE );
if( keyname.Buffer[0] ) {
strcat( tmpname, "\\" );
strncatZ( tmpname, keyname.Buffer, MAXPATHLEN - 1 - strlen(tmpname) );
}
RtlFreeAnsiString( &keyname );
}
我再看看
最奇怪的是单独hook 注册表函数就没问题了~~~
这个如何解释哩?
ZOthello 2008-07-27
  • 打赏
  • 举报
回复
[Quote=引用 41 楼 hxfjb 的回复:]
内核的函数正因为是__stdcall,所以你的 函数要遵从人家的格式,否则你的hook函数就把人家的栈给搞坏了。
VC的默认调用不是__stdcall,而是C调用。而C调用中,函数是不负责调整栈的,由调用者调整。

这个都不懂,你写的hook函数肯定错误。
[/Quote]
如果调用格式错误的话,那么就不可能运行。我的程序可以运行,但是有问题。
我的函数哪里把栈搞坏了,麻烦指出来。
hxfjb 2008-07-27
  • 打赏
  • 举报
回复
内核的函数正因为是__stdcall,所以你的 函数要遵从人家的格式,否则你的hook函数就把人家的栈给搞坏了。
VC的默认调用不是__stdcall,而是C调用。而C调用中,函数是不负责调整栈的,由调用者调整。

这个都不懂,你写的hook函数肯定错误。
ZOthello 2008-07-27
  • 打赏
  • 举报
回复
这个还没有试过,那我再试试。
晕,调试个程序真麻烦~~
cnzdgs 2008-07-27
  • 打赏
  • 举报
回复
这个函数一般不会失败。代码中有if( lpszSubKeyVal )判断,所以不会有NULL的情况。

另外,你试过做成两个驱动都加载的情况了吗?
ZOthello 2008-07-27
  • 打赏
  • 举报
回复
我有个问题想问一下RtlUnicodeStringToAnsiString( &keyname, lpszSubKeyVal, TRUE ); 这个函数在什么情况下运行会失败?第二个参数的buffer为NULL会不会导致失败?
ZOthello 2008-07-27
  • 打赏
  • 举报
回复
恩,就是改变MAXPATHLEN定义的。
好郁闷啊,这问题太奇怪了~~
cnzdgs 2008-07-27
  • 打赏
  • 举报
回复
驱动程序编译时是默认__stdcall的,否则你的程序就算没有合到一起,一加载也会立即出问题。

32楼的这段代码看不出问题,你可以把这段代码注释掉试试。另外,你是改变MAXPATHLEN的定义来试的吗?因为GetFullName函数中也使用了MAXPATHLEN。
ZOthello 2008-07-27
  • 打赏
  • 举报
回复
难道内核中的函数不是默认的_stdcall吗?它也是windows下的函数啊,就不用加_stdcall了。这个不是问题的关键吧~~
加载更多回复(32)

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧