Winodws x64系统服务调用的那头4个参数

本文探讨了Windows x64系统下服务调用的过程,特别是NtDeviceIoControlFile函数的参数传递。在x64系统中,前4个参数分别存储在rcx、rdx、r8和r9寄存器中,而非堆栈。当从用户态切换到内核态时,这些寄存器的值被保存在_KTRAP_FRAME结构中。在调试和逆向工程中,理解这一机制对于跟踪和分析系统服务调用至关重要。
摘要由CSDN通过智能技术生成

起因

        前几天有朋友遇到一个问题来问我。他有一个Windows 7 x64下的minidump文件(http://pan.baidu.com/s/1dD524hz),经过初步分析,知道系统崩溃最初是因用户态调用了NtDeviceIoControlFile造成的,KeBugCheckEx调用已经位于多重函数调用的深处,问如何找到最初NtDeviceIoControlFile的参数,尤其是头4个参数。经过一番努力,我总算是把问题解决了,其中得到一些领悟,想趁热记录,也想趁这个机会将内核服务调用的基本技术点梳理一下。

        在开始针对问题开始讨论之前,我们先要补习一下Windows x64下函数调用的基本知识。

 

x64函数调用堆栈

        x64下,不再使用x86下以“pushebp/mov ebp, esp”为特征的堆栈帧格式。取而代之的是如下图所示格局:


        函数调用的头4个参数虽然在堆栈中预留了位置,但参数值并不真的存储在那里。实际上从第1个参数到第4个参数是依次是使用rcx、rdx、r8、r9(浮点数情况下依次对应:xmm0、xmm1、xmm2、xmm3)。几乎(注意是:几乎)所有函数在头几条指令范围内就通过sub rsp, xxh的形式准备好了本函数用到的堆栈空间,之后直到函数返回,此函数不再进行改变rsp的任何操作。也就是说函数的堆栈是“一步到位”的。这样一步到位的好处是避免堆栈指针的反复调整,从而有效提高代码执行效率。其副作用是:头4个参数不在堆栈上,不利于追踪;没有特殊的ebp堆栈基址(rbp不再使用),堆栈回溯较难。在函数调用时,rdi、rsi、rbx、rbx、rbp、r12、r13、r14、r15是所谓的非容失性(nonvolatile)寄存器。也就是说,在函数调用过程中,被调用函数必须保证这些寄存器在函数返回时和进入函数时是一样的。

        某处代码在调用某个函数时,会将头4个参数分别存入相应寄存器,如果超过4个参数,会在位于rsp+20h的地方存入第5个参数。随后call指令会将返回地址压入堆栈,造成rsp减8。此时第1个参数对应位置是rsp+8,第4个参数对应位置是rsp+20h。随后,一般的函数会一次性sub rsp, xxh,其中xxh不同函数有所不同。通过IDA Pro进行观察会发现,函数内部代码对局部变量和参数的访问,都翻译成[rsp+xxh+yyh]和形式,其中yyh是数据相对于函数第1条指令将要执行而尚未执行时rsp的指针位置(当然返回地址位置是rsp+0)。

        补习完基础知识,我们就可以开始研究问题了。当然在开始之前,要启动我们的Windbg,装入dump文件,下载并装入相应符号。

 

第5个及以后的参数

        首先NtDeviceIoControlFile的原型是:

NTSTATUS WINAPI NtDeviceIoControlFile(
  _In_  HANDLE                    FileHandle,
  _In_  HANDLE                    Event,
  _In_  PIO_APC_ROUTINE           ApcRoutine,
  _In_  PVOID                     ApcContext,
  _Out_ PIO_STATUS_BLOCK           IoStatusBlock,
  _In_  ULONG                     IoControlCode,
  _In_  PVOID                     InputBuffer,
  _In_  ULONG                     InputBufferLength,
  _Out_ PVOID                      OutputBuffer,
  _In_  ULONG                     OutputBufferLength
);

  毫无疑问,第5个及以后的参数存储在堆栈中。我们使用kv来观察堆栈:

1:kd> kv
Child-SP       RetAddr        : Args to Child                                      : Call Site
fffff880`0253ff58 fffff800`03f577ab : 00000000`0000001e ffffffff`c0000005 fffff880`0254001000000000`00000000 : nt!KeBugCheckEx
fffff880`0253ff60 fffff800`03f16118 : 00000000`00000001 00000000`00000000 fffffa80`03306b30fffff880`02540920 : nt!KipFatalFilter+0x1b
fffff880`0253ffa0 fffff800`03eee89c : 00000000`00000000 fffffa80`03ee0da0 fffff8a0`00262450fffff8a0`00262140 : nt! ?? ::FNODOBFM::`string'+0x83d
fffff880`0253ffe0 fffff800`03eee31d : fffff800`0400f30c fffff880`02541610 00000000`00000000fffff800`03e50000 : nt!_C_specific_handler+0x8c
fffff880`02540050 fffff800`03eed0f5 : fffff800`0400f30c fffff880`025400c8 fffff880`02540f38fffff800`03e50000 : nt!RtlpExecuteHandlerForException+0xd
fffff880`02540080 fffff800`03efe081 : fffff880`02540f38 fffff880`02540790 fffff880`0000000000000000`00000000 : nt!RtlDispatchException+0x415
fffff880`02540760 fffff800`03ec20c2 : fffff880`02540f38 fffffa80`02884010 fffff880`02540fe000000000`00000010 : nt!KiDispatchException+0x135
fffff880`02540e00 fffff800`03ec0c3a : 00000000`00000001 00000000`00000018 00000000`00000000ffff
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值