您的位置:首页 > 职场人生

NtCreatFile函数的参数传递分析 - zl21 - 程序员网志 - Powered By PHPWind.Net

2008-05-22 21:53 344 查看
导读:

写这篇分析的目的: m^mNa8$
最近在反汇编一个驱动程序时,看到CreateDispacth里面有这么一段操作: F{>Tg lX1f
;eax=IoGetCurrentIrpLocationStack(Irp) %N~5S5q''
mov eax, [ecx+8] ; eax->Parameters.Create.Option Ima~JKh
mov cx, [ecx+0Ch] ;eax->Parameters.Create.FileAttributes |/n~;0
mov edi, eax Lw @0g
shr eax, 18h ; 右移24位得到CreateOption里面的CreateDisposition参数 qz [ A#z
; 以下是C代码: #08ZSu7M(,
; ULONG disposition GGL%yUtkq
; dispostion=(irpSp->Parameters.Create.Option>>24)&0xff ~yL5 
mov esi, eax ?{X1/XM!-@
and edi, 0FFFFFFh ; edi=204022 h0X?Z0.
cmp esi, 4 _;- '&or{
…… ` b"LV>
qu7t%^
本来我是不知道该代码取的是 CreateDisposition 参数的,是论坛上的一个朋友告诉我的,但是我不知道 win 为什么会这么操作,本着打破沙锅的精神(神经:)),写了分析。 p8n_Bq OJ
首先来看看在这段代码以前 win 都做了什么: 7-k9Du}]2
. {]* qz
**!CreateDispacth zMsLn$tO:
nt!IopCallDriver `}",TZ0Q
nt!IopParseDevice m'Cp^s 
nt!ObpLookupObjectName `Y$D.;0 
nt!ObOpenObejctByName }FT/6-"gV
nt!IopCreateFile mb +d9sTu$
nt!IoCreateFile T7H~S8(
nt!NtCreateFile )Am:o
nt!KiSystemService dM|u3W e
nt!ZwCreateFile ~U^,V%b,u
…… q.9l&Ky8
在 nt!IopparseDevice 中: !mdEjLagf^
调用 nt!IoGetAttacheDevice ,获得设备栈最顶端的设备对象。调用 IoAllocateIrp 创建 IRP。调用 nt!ObCreateObject 创建文件对象。初始化这个文件对象。该文件对象的 +04 struct _DEVCIE_OBEJCT *DeviceObejct 赋值为通过传入参数找到的那个设备对象。调用 nt!IopCallDriver 将IRP发给设备栈的栈顶。(参考了jiurl的键盘驱动里的一段章节) zX|u7]O
(Mc[tuUZU
我不是要抄袭jiurl的大作,我只是想有一个基本的框架。 jWg1r9
3<^_ Q_#f]
回到原来的问题,那些参数是怎样传递的??为什么会这样取参数?? ,>;8YZMu
先来看看 IoAllocateIrp 函数的代码(请出至尊宝典 win2k 的源代码): koA$RwK
v/{hU"+$6
代码简单的出乎我的意料: Dy<=lt
IoAllocateIrp其实什么也不做,他只是一个公共接口函数。 CiuQx<z~v
PIRP kDXXel/
IoAllocateIrp( "JAK?8 ix
IN CCHAR StackSize, ~/eQ82
IN BOOLEAN ChargeQuota *C2w?0
) r%0H O7G
{ .,yH1zkf
return (pIoAllocateIrp(StackSize, ChargeQuota)); s%<P
} ETWN'9e
5MK vsEQ
extern PIO_ALLOCATE_IRP pIoAllocateIrp; R*R/C1(pJ
Cvh DqySi9
在win2k 源代码中,我找到了两个对 pIoAllocateIrp 的赋值,也就是说有两个 AllocateIrp 函数,他们分别是 PIRP IovAllocateIrp( IN CCHAR StackSize, IN BOOLEAN ChargeQuota) 和 PIRP IopAllocateIrpPrivate( IN CCHAR StackSize, IN BOOLEAN ChargeQuota),这两个函数的主要功能是检查参数和为 IRP 分配内存空间,最后都会调用同一个函数 IopInitializeIrp 来初始化 Irp ,至于在什么情况下调用 IovAllocateIrp 和 IopAllocateIrpPrivate 函数则不在我的讨论范围里面。 qnzz>(!R
>&U(q^I
而 IoInitializeIrp函数也同样出乎了我的意料 yFp+ c
#define IopInitializeIrp( Irp, PacketSize, StackSize ) { /  -rr{Tl0
RtlZeroMemory( (Irp), (PacketSize) ); / d7>} C 8
(Irp)->Type = (CSHORT) IO_TYPE_IRP; / }Q"b9HM">t
(Irp)->Size = (USHORT) ((PacketSize)); / {p({2>}
(Irp)->StackCount = (CCHAR) ((StackSize)); / 4Ndx8 uV
(Irp)->CurrentLocation = (CCHAR) ((StackSize) + 1); / n@?Mq>V>BT
(Irp)->ApcEnvironment = KeGetCurrentApcEnvironment(); / D l^/ IP
InitializeListHead (&(Irp)->ThreadListEntry); / L'XAKoP|
(Irp)->Tail.Overlay.CurrentStackLocation = / %C8%MK>
((PIO_STACK_LOCATION) ((UCHAR *) (Irp) + / 9zu ,"_
sizeof( IRP ) + / <8b} W^Kq
( (StackSize) * sizeof( IO_STACK_LOCATION )))); } pE ?Jy
) 6Ie w3k
这个函数只填充了部分参数,这些被填充了的参数只与IRP有关系,可是和我想要找的 NtCreateFile 所传入的那些参数则没有太大的关系。 0?POha
&I/J>V
看来我是走错了方向了,那么回过头让我从头跟踪一下这些参数的流向:  u4X{!"!
s[k^>0T3
NTSTATUS 5vyXDq_m~
NtCreateFile(OUT PHANDLE FileHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes, Jmf*10N
OUT PIO_STATUS_BLOCK IoStatusBlock,IN PLARGE_INTEGER AllocationSize OPTIONAL, hLL!+yFQ7
IN ULONG FileAttributes,IN ULONG ShareAccess,IN ULONG CreateDisposition,IN ULONG CreateOptions, ?^ZE`!qw
IN PVOID EaBuffer OPTIONAL,IN ULONG EaLength {aDY77w8
) 3._]l&F=
{ :9I}ZJyk
// RIvBA 3 lQ
// Simply invoke the common I/O file creation routine to do the work. ;1<`,{
// U&MUfMH_
m<Qn^<}B
PAGED_CODE(); CG o#^-5<X
4z[w?Vd#
return IoCreateFile( FileHandle,DesiredAccess,ObjectAttributes,IoStatusBlock,AllocationSize, A.@ /(_J
FileAttributes,ShareAccess,CreateDisposition,CreateOptions, +e:Pwka
EaBuffer,EaLength,CreateFileTypeNone,(PVOID)NULL,0 %ychWc k
); LEx9>/a
} Evo*f5dJ
在 NtCreateFile中,传入的参数原封不动的被传入 IoCreateFile 函数; ] qP?72>
在 IoCreateFile中,传入的参数被封装在一个_OPEN_PACKET的结构中: PH+K $
…… _MG+V#w/
//参数检查 5-}[?/ 9
…… 4- {sp d
openPacket.Type = IO_TYPE_OPEN_PACKET; {_2Vzu;|E
openPacket.Size = sizeof( OPEN_PACKET ); ;;<*#AZ P
openPacket.ParseCheck = 0L; p+@4qw`rS
openPacket.AllocationSize = initialAllocationSize; //NtCreateFile(……,IN PLARGE_INTEGER AllocationSize OPTIONAL,,……) J<E*zxP R
openPacket.CreateOptions = CreateOptions; //NtCreateFile(……,IN ULONG CreateOptions,……) # QJRp
openPacket.FileAttributes = (USHORT) FileAttributes; //NtCreateFile(……,IN ULONG FileAttributes,……) 08;.MD|L
openPacket.ShareAccess = (USHORT) ShareAccess; //NtCreateFile(……,IN ULONG ShareAccess,……) )],CmM]1
openPacket.Disposition = Disposition; //NtCreateFile(……,IN ULONG CreateDisposition,……) "%LH/g
openPacket.Override = FALSE; Lt9xUfH)
openPacket.QueryOnly = FALSE; H --Nk
openPacket.DeleteOnly = FALSE; h -v*F
openPacket.Options = Options; ]_HGmz^I
openPacket.RelatedFileObject = (PFILE_OBJECT) NULL; p95H^%pt
openPacket.CreateFileType = CreateFileType; ;.E=$"wI
openPacket.ExtraCreateParameters = ExtraCreateParameters; JoYVeW#
…… qLFu=Z
status = ObOpenObjectByName( ObjectAttributes,(POBJECT_TYPE) NULL,requestorMode, K'w'4
NULL,DesiredAccess,&openPacket,&handle e#%P,{
); K5]&^$8^
…… ULH#" rod0
在传入的参数当中,DesireAccess 和 ObjectAttributes 是调用 ObOpenObejctByName 函数所用到的参数,不在封装之列,而IoStatusBlock 参数则作为单独的结构做处理,这里需要注意的就是 openPacket.Eabuffer 、openPacket.EaLength 和 openPacket.AllocationSize,AllocationSize 参数需要调用 ARGUMENT_PRESENT 宏函数来检验该参数是否为空,然后调用 ProbeForRead 函数做进一步的检查,然后使 initialAllocationSize 指向 AllocationSize,将 initialAllocationSize 封装入 openPacket 中,而 Eabuffer 则同样是调用 ARGUMENT_PRESENT 、ProbeForRead 做参数有效性检查,如果该函数不为空检查通过,则调用 ExAllocatePoolWithQuotaTag 函数为其分配内存空间,然后调用 RtlCopyMemory 函数将Eabuffer 从用户空间拷贝到内核空间。而其余的四个参数 FileAttributes、ShareAccess、Disposition、CreatOption 被原封不动的封装入 OPEN_PACKET 结构中;  =e}Vg0YC
gRV5DN[F#
在 ObOpenObejctByName 函数中 openPacket 没有被改动过,调用 ObpLookupObjectName 函数; l--W]R
…… 3%(Z60k
Status = ObpLookupObjectName( ObjectCreateInfo.RootDirectory,&CapturedObjectName,ObjectCreateInfo.Attributes, Gu,kj:
ObjectType,AccessMode,ParseContext,ObjectCreateInfo.SecurityQos,NULL, %R9L2V 7,
AccessState,&DirectoryLocked,&ExistingObject F?F!nM:P
); UiqQ1I2
…… x!I9E(?#
在这里,只不过是由 ParseContext 指针指向了OPNE_PACKET结构 H: H d
同样在 ObpLookupObjectName 函数中 OPEN_PACKET 结构中的内容也没有被改动过,调用 IopParseDevice 函数; }; P8J9_
…… OA9J*~Ty
Status = IopParseDevice( RootDirectory,ObjectType,AccessState,AccessMode,Attributes, JJfLW?`F =
ObjectName,&RemainingName,ParseContext,SecurityQos,&Object /Vpo &QQ
); A/heOYy
<ae<JBIO-g
在 IopParseDevice 中首先是调用 IoAllocateIrp 函数来分配一个 IRP,这个在前面已经提过了,接下来便是填充 IRP 了。 9#1zy-r}
……  u*6_ X-
POPEN_PACKET op; =7=0t_E8
op=ParseContext; *p`}p"Q
…… bk|3Wl
irp = IopAllocateIrp( deviceObject->StackSize, TRUE ); 2N_q{5
…… @-(<SZ[$rV
irp->Tail.Overlay.Thread = PsGetCurrentThread(); OP.ZnI"$k
irp->RequestorMode = AccessMode; V=&3m9wS"
irp->Flags = IRP_CREATE_OPERATION | IRP_SYNCHRONOUS_API | IRP_DEFER_IO_COMPLETION; !sDPz4f
…… D[}9*v&l4
irpSp = IoGetNextIrpStackLocation( irp ); KD5_b6f
irpSp->Control = 0; P1<Ra 
if (op->CreateFileType == CreateFileTypeNone) { R6d|@
*&+@ 
irpSp->MajorFunction = IRP_MJ_CREATE; +%8EjnnR*
irpSp->Parameters.Create.EaLength = op->EaLength; 8R<zMzJ[V
irpSp->Flags = (UCHAR) op->Options; Vb_NX}U"o
if (!(Attributes & OBJ_CASE_INSENSITIVE)) { &Q.45?I
irpSp->Flags |= SL_CASE_SENSITIVE; 1YGktS(
} H)^eK7
PR 5<U
} else if (op->CreateFileType == CreateFileTypeNamedPipe) { 2aGjK2]
Q`S~G):
irpSp->MajorFunction = IRP_MJ_CREATE_NAMED_PIPE; %9 SG
irpSp->Parameters.CreatePipe.Parameters = op->ExtraCreateParameters; t7;l8W-Z
Ct >:CoT8
} else { u]#` :YP0Q
@Ql7.G6
irpSp->MajorFunction = IRP_MJ_CREATE_MAILSLOT; J 3 D+^
irpSp->Parameters.CreateMailslot.Parameters = op->ExtraCreateParameters; @GCG^_#L
} ]{<hUhQp>
irp->Overlay.AllocationSize = op->AllocationSize; [U84 o/QF"
irp->AssociatedIrp.SystemBuffer = op->EaBuffer; ~0p v-I
irpSp->Parameters.Create.Options = (op->Disposition << 24) | (op->CreateOptions & 0x00ffffff); @KCe 4e
irpSp->Parameters.Create.FileAttributes = op->FileAttributes; ~I ,MK
irpSp->Parameters.Create.ShareAccess = op->ShareAccess; 0*m6n`gB
irpSp->Parameters.Create.SecurityContext = &securityContext; D/V &y${
$Qz:v6cE
irp->UserIosb = &ioStatus; N( bz]F
irp->MdlAddress = (PMDL) NULL; p09 i$!
irp->PendingReturned = FALSE; *z1v;CxQ
irp->Cancel = FALSE; #RMU&<G
irp->UserEvent = (PKEVENT) NULL; ?Sv+6?;TD
irp->CancelRoutine = (PDRIVER_CANCEL) NULL; [aPAdj:~w*
irp->Tail.Overlay.AuxiliaryBuffer = (PVOID) NULL; .i7/?" 7P)
…… 4RI)^ 3p
V RrOEb.@
找到了,终于被我找到了,我们来看这一句: $tr6flv
irpSp->Parameters.Create.Options = (op->Disposition << 24) | (op->CreateOptions & 0x00ffffff); x_kq3gu
}aWHX T
我想了很长的时间都不解其意(哎,都怪我笨的出奇5555……) U@sOu /u
.3;ZBK% ,
首先我们来看看 CreateOption 参数的取值: Ba)dS|X
#define FILE_DIRECTORY_FILE 0x00000001 P9~/=;O/|
…… 7' raX
#define FILE_OPEN_BY_FILE_ID 0x00002000 zXjA5OI
#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 `%By@mE~E
iGYl};3TQ
再来看看 Disposition 参数的取值: ys 8(5Oo
…… /@mUuo
#define FILE_OPEN 0x00000001 sf}/[EtP
#define FILE_CREATE 0x00000002 9ttOAR1
…… {{P{9I1.B
8}V]h51
让我们做一个假设,假设Disposition 取值为0x00000002(FILE_CREATE),那么Disposition<<24 的值为 0x02000000,假设 CreateOption 的取值为0x00004000(FILE_OPEN_FOR_BACKUP_INTENT),那么CreateOptions & 0x00ffffff的值为 0x00004000,这样一来(Disposition << 24) | (CreateOptions & 0x00ffffff)的值就为0x02004000,那么irpSp->Parameters.Create.Options 就包含了两个参数,0-3位代表 CreateOption参数,6-7位代表CreateDisposition参数,至于4、5位的取值有没有其他含义,我就不得而知了,还请高人给予赐教。win 这样做的目的除了压缩结构空间外我没有想出太好的理由。 "Rhoibh
[s 4Q
接下来就是调用 IopCallDriver 函数了,至此,参数不再传递,代替的是传递 IRP,最后 IRP 会被传递到 CreateDispatch 函数中。 {i V<a #
z =Nb,B>E
以上全部来自于泄露的 windows2000 源代码,获此代码如获至宝,只是由于本人整天沉迷于玩乐之中不知刻苦耐劳苦心钻研,以至于代码用时方恨少,只希望前辈将《Windows Nt/2000 泄露源代码情景分析》尽早写出来,以供我等拜读。
时间仓促,只写了这么多,没有动手反汇编分析,总是觉得缺少点什么。
此篇为晚生之初作,本难登大雅之堂,只因一想作为笔记加深印象,二想请各位前辈指点一二(4、5位的取值有没有其他含义??),所以厚着脸皮贴到此处,请各位前辈不要嗤之以鼻,希望前辈们多多指点晚生,晚生在此拜谢。

本文转自
http://blog.zndev.com/blog.php?do=showone&uid=356&type=blog&itemid=323
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: