黑暗之魂奥斯卡剧情:深入理解ZwCreateFile

来源:百度文库 编辑:九乡新闻网 时间:2024/03/29 19:54:36
前言:

任何编码的东西必须充分的了解规则!

我试探性的搜索了下关于这个native api的相关的问题,很郁闷的发现,这些问题90%都是没有真正理解了这个函数导致的!

有的人连函数干嘛的都不知道就着急的写驱动,真是不可一世!所谓兼收并蓄为的是厚积薄发,其间如履薄冰。说的很贴切!

对此我一直支持achills师傅的思想!搞明白了最基本的和必须的东西,一切后续的东西迎刃而解!

这个文章我没有过多的写什么东西,因为很多东西都文档化了,只需要我们认真而且真正从本质上串一遍,完全的理解了它就可以了!而且有些东西也没有必 要再写了,比如HOOK SSDT inline hook等,现在缺的不是怎么实现一个代码的流程,缺的是两个东西:一个是思路,一个是准确性。

原型:

NTSYSAPI
NTSTATUS
NTAPI
ZwCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);

参数理解:
OUT-
FileHandle--------这是一个指向一个变量的指针,用来最后存放file object handle的
IoStatusBlock-----这个也是个指针变量,指向一个叫做IO_STATUS_BLOCK的结构体,最后函数返回的时候,这个结构体的成员 里面要填充一些值,具体的呢就是完成状态,请求操作的一些信息,最重要的一个成员就是Information成员,他显示了函数对文件的处理方式,他的值 可能是下面的几个:
FILE_SUPERSEDED(替代)
FILE_OPENED(打开)
FILE_CREATED(创建)
FILE_OVERWRITTEN(重写)
FILE_EXISTS(存在)
FILE_DOES_NOT_EXIST(文件不存在)

再看下这个IO_STATUS_BLOCK的具体结构:
typedef struct _IO_STATUS_BLOCK {
union {
    NTSTATUS Status;
    PVOID Pointer;
} DUMMYUNIONNAME;

ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

通过上述的两个输出的参数我们可以看到,这个ZwCreateFile函数就是返回创建好的文件对象的句柄,然后返回一个期间处理的方式。

IN-
DesiredAccess----这个参数指定一个访问权限,大概有以下的权限:
FILE_ANY_ACCESS 0x0000 // any type
FILE_READ_ACCESS 0x0001 // file & pipe
FILE_READ_DATA 0x0001 // file & pipe
FILE_LIST_DIRECTORY 0x0001 // directory
FILE_WRITE_ACCESS 0x0002 // file & pipe
FILE_WRITE_DATA 0x0002 // file & pipe
FILE_ADD_FILE 0x0002 // directory
FILE_APPEND_DATA 0x0004 // file
FILE_ADD_SUBDIRECTORY 0x0004 // directory
FILE_CREATE_PIPE_INSTANCE 0x0004 // named pipe
FILE_READ_EA 0x0008 // file & directory
FILE_WRITE_EA 0x0010 // file & directory
FILE_EXECUTE 0x0020 // file
FILE_TRAVERSE 0x0020 // directory
FILE_DELETE_CHILD 0x0040 // directory
FILE_READ_ATTRIBUTES 0x0080 // all types
FILE_WRITE_ATTRIBUTES 0x0100 // all types
FILE_ALL_ACCESS // All of the preceding +
STANDARD_RIGHTS_ALL
最后一个权限最大
这里面要注意的是范围问题,有的值只适合目录,有的只适合管道,有的只适合命名管道,有的同时适用,想下这个地方可以做什么文章

ObjectAttributes---指向下面这个结构的一个变量,就是来表明文件对象的属性的。
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;       /* type SECURITY_DESCRIPTOR */
PVOID SecurityQualityOfService; /* type SECURITY_QUALITY_OF_SERVICE */
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
这个结构是针对很多对象的,不光是针对文件对象的,所以OBJ_PERMANENT(永久), OBJ_EXCLUSIVE(互斥),
and OBJ_OPENLINK这三个实际上对于文件对象来说始终是无效的,为什么?
不能设置那三个,那可以设置些什么呢?呼呼


AllocationSize---这是个可选的参数,他是指定初始化文件需要的内存字节数的,所以可向而知,他只有在真正的涉及到文件创建的时候才有意义,也就是说创建,重写,替换这些操作的时候。指向一个LARGE_INTEGER:
typedef union _LARGE_INTEGER {
    _ANONYMOUS_STRUCT struct
    {
        ULONG LowPart;
        LONG HighPart;
    } DUMMYSTRUCTNAME;
    struct
    {
        ULONG LowPart;
        LONG HighPart;
    } u;
#endif //MIDL_PASS
    LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;
这个结构体在内核结构里面很常见,呼呼,,支持到64位,看情况设置。不过我至今没有明白QuadPart这个是干嘛的!

FileAttributes---这个参数指定文件的属性,刚才是文件对象的属性。可以是以下的:
FILE_ATTRIBUTE_READONLY
FILE_ATTRIBUTE_HIDDEN
FILE_ATTRIBUTE_SYSTEM
FILE_ATTRIBUTE_DIRECTORY
FILE_ATTRIBUTE_ARCHIVE
FILE_ATTRIBUTE_NORMAL
FILE_ATTRIBUTE_TEMPORARY
FILE_ATTRIBUTE_SPARSE_FILE
FILE_ATTRIBUTE_REPARSE_POINT
FILE_ATTRIBUTE_COMPRESSED
FILE_ATTRIBUTE_OFFLINE
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
FILE_ATTRIBUTE_ENCRYPTED

ShareAccess---指定共享的权限,以下三种的组合:
FILE_SHARE_READ
FILE_SHARE_WRITE
FILE_SHARE_DELETE

CreateDisposition---这个参数指定要对文件干嘛,呼呼,可以是下面的值:
FILE_SUPERSEDE
FILE_OPEN
FILE_CREATE
FILE_OPEN_IF
FILE_OVERWRITE
FILE_OVERWRITE_IF


CreateOptions---这个参数指定创建或者打开文件的时候做的一些事情,可以是以下的组合:
FILE_DIRECTORY_FILE
FILE_WRITE_THROUGH
FILE_SEQUENTIAL_ONLY
FILE_NO_INTERMEDIATE_BUFFERING
FILE_SYNCHRONOUS_IO_ALERT
FILE_SYNCHRONOUS_IO_NONALERT
FILE_NON_DIRECTORY_FILE
FILE_CREATE_TREE_CONNECTION
FILE_COMPLETE_IF_OPLOCKED
FILE_NO_EA_KNOWLEDGE
FILE_OPEN_FOR_RECOVERY
FILE_RANDOM_ACCESS
FILE_DELETE_ON_CLOSE
FILE_OPEN_BY_FILE_ID
FILE_OPEN_FOR_BACKUP_INTENT
FILE_NO_COMPRESSION
FILE_RESERVE_OPFILTER
FILE_OPEN_REPARSE_POINT
FILE_OPEN_NO_RECALL
FILE_OPEN_FOR_FREE_SPACE_QUERY
这个我不太清楚,概念有点模糊

EaBuffer---这个是个可选的参数,用来存放一些扩展的属性
EaLength---存放扩展属性的字节大小
有个疑问,这个扩展属性什么时候用呢?(TDI里面常用这个)

更详细的参数的理解参考
http://xiaomaier.bokee.com/3439967.html

返回值理解:
如果成功,返回STATUS_SUCCESS
如果失败,返回
STATUS_ACCESS_DENIED,
STATUS_OBJECT_NAME_NOT_FOUND, STATUS_OBJECT_NAME_COLLISION,
STATUS_OBJECT_NAME_INVALID, STATUS_SHARING_VIOLATION, STATUS_NOT_A_DIRECTORY, or
STATUS_FILE_IS_A_DIRECTORY.

说明:
1.与这个native api相关的r3api是CreateFile
2.DDK里面对这个函数有详细的说明


扩展:
1.与CreateFile这个api的异同。
同:
(1)功能基本相同,都是可以打开或者创建一个文件对象,包括文件,磁盘,卷,管道,串口,油槽等
(2)都是返回一个文件句柄
异:
(1)在vista及以上的版本,CreateFile做了扩展,加上了事务性文件系统
HANDLE WINAPI CreateFileTransacted(
__in          LPCTSTR lpFileName,
__in          DWORD dwDesiredAccess,
__in          DWORD dwShareMode,
__in_opt      LPSECURITY_ATTRIBUTES lpSecurityAttributes,
__in          DWORD dwCreationDisposition,
__in          DWORD dwFlagsAndAttributes,
__in_opt      HANDLE hTemplateFile,
__in          HANDLE hTransaction,
__in_opt      PUSHORT pusMiniVersion,
PVOID pExtendedParameter
);

但是ZwCreateFile还是那样
(2)HANDLE WINAPI CreateFile(
__in          LPCTSTR lpFileName,
__in          DWORD dwDesiredAccess,
__in          DWORD dwShareMode,//////////////////////
__in          LPSECURITY_ATTRIBUTES lpSecurityAttributes,
__in          DWORD dwCreationDisposition,
__in          DWORD dwFlagsAndAttributes,
__in          HANDLE hTemplateFile
);
NTSYSAPI
NTSTATUS
NTAPI
ZwCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,////////////
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,///////////
IN ULONG CreateDisposition,//////////////////////////
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);

2.内核里与ZwCreateFile功能相似的还有一个函数IoCreateFile
NTSTATUS IoCreateFile
(   
OUT PHANDLE FileHandle,   
IN ACCESS_MASK DesiredAccess,   
IN POBJECT_ATTRIBUTES ObjectAttributes,   
OUT PIO_STATUS_BLOCK IoStatusBlock,   
IN PLARGE_INTEGER AllocationSize OPTIONAL,   
IN ULONG FileAttributes,   
IN ULONG ShareAccess,   
IN ULONG Disposition,   
IN ULONG CreateOptions,   
IN PVOID EaBuffer OPTIONAL,    I
N ULONG EaLength,   
IN CREATE_FILE_TYPE CreateFileType,   
IN PVOID ExtraCreateParameters OPTIONAL,   
IN ULONG Options   
) ;
windbg看下,其实ZwCreateFile内部调用了IoCreateFile

至于这两个函数的区别可以参考combojiang的http://hi.baidu.com/combojiang/blog/item/cd6269de55ce235eccbf1adb.html

IoCreateFile内部还调用了一个allmul函数,这个函数是用来做int64的移位的,具体的参考
http://hi.baidu.com/combojiang/blog/item/29411c5c6841ae45fbf2c088.html

据说IoCreateFile函数更优良一些,IceSword就调用了这个函数来打开ntoskrnl。exe的

3.hook ZwCreateFile是很简单的,,hook SSDT就可以了!考虑个问题:
ZwCreateFile是创建文件的,那么如果我们hook之后比如函数变成了my_ZwCreateFile(),这个时候我们还想调用ZwCreateFile创建文件的话,就会出错,还想再创建文件,怎么办?
这个得做个线程模式的切换。
详细的可以参考http://www.cnblogs.com/jokerfox/archive/2009/04/14/1435819.html

4.ZwCreateFile是运行在PASSIVE_LEVEL上的。那么比如要调用这个函数的程序必须运行在DISPATCH_LEVEL呢?怎么调用?
http://xuyingpin.blogbus.com/logs/10845127.html
http://xuyingpin.blogbus.com/logs/11152569.html


最简单的使用方法:
BOOL testCreateFile(IN PUNICODE_STRING filename)
{
    HANDLE hFile=NULL;
    NTSTATUS status;
    IO_STATUS_BLOCK isb;
    OBJECT_ATTRIBUTES oa;
   
         InitializeObjectAttributes(&oa,filename,OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,NULL,NULL);
   
    status=ZwCreateFile(&hFile,GENERIC_ALL,&oa,&isb,NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,FILE_CREATE,FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,NULL,0);
   
    if(NT_SUCCESS(status))
    {
    status=STATUS_SUCCESS;
    }
    else
    {
    status=isb.Status;
    }
   
    DbgPrint("hFile=%08X",hFile);
   
    if(hFile)
    {
    ZwClose(hFile);
    }
    return status;

hook ZwCreateFile的思路:
1.hook SSDT
NTSTATUS NewZwCreateFile(OUT PHANDLE FileHandle,
                         IN ACCESS_MASK DesiredAccess,
                         IN POBJECT_ATTRIBUTES ObjectAttributes,
                         OUT PIO_STATUS_BLOCK IoStatusBlock,
                         IN PLARGE_INTEGER AllocationSize OPTIONAL,
                         IN ULONG FileAttributes,
                         IN ULONG ShareAccess,
                         IN ULONG CreateDisposition,
                         IN ULONG CreateOptions,
                         IN PVOID EaBuffer OPTIONAL,
                         IN ULONG EaLength) {
    NTSTATUS ntS = (NTSTATUS) NULL;
   
    DbgPrint("ZwCreateFile called\n");
   
   
    ntS = ((ZWCREATEFILE)(OldZwCreateFile)) (
        FileHandle,
        DesiredAccess,
        ObjectAttributes,
        IoStatusBlock,
        AllocationSize,
        FileAttributes,
        ShareAccess,
        CreateDisposition,
        CreateOptions,
        EaBuffer,
        EaLength);
    return(ntS);
}
http://dev.csdn.net/article/36/36751.shtm
http://www.zeroplace.cn/article.asp?id=138

2.通过调试寄存器hook
http://www.xfocus.net/articles/200709/950.html

3.inline hook
http://hackbase.com/tech/2009-05-11/52690.html系列


总结:
我们站在一个稍微高的地方来看一些东西

任何用户态或核心态的函数CreateFile或者内核态的ZwCreateFile等都是为了获得handle,为进一步的访问做准备。而在获得 handle的过程中,调用者需要提供DesiredAccess和ShareAccess等选项。如果文件已经被另一个调用者以排他方式打开,这时候的 访问就会失败。

那么我们可以这么想一个问题,如果我们精通一种验证机制,或者说构造一种验证机制的话,那么访问不需要这么复杂就可以实现,这就是邪恶的绕过技术!那么针对我刚才说的话,具体的可以参考这么几种技术:

搜索句柄表,关闭句柄
DKOM
区域映射
I/O

我还不清楚这里面的研究空间有多大,反正我见好多人都是怎么xx了,怎么绕过什么了,怎么投机了一下过了什么保护了,其实从个人需求来看,确实是一个突破,若从全局来看,根本是牵一发不动全身的!