很久不写博客了,笔记大多记在电脑上在,以后整理好了再搬运上来吧。

今天记一下“进程内存管理器”这个小程序上遇到的一个问题——内核模式调用Nt*函数。

使用的是内核中的NtQueryVirtualMemory函数,先看一下WinDbg:

  kd> u NtQueryVirtualMemory
  ntdll!NtQueryVirtualMemory:
  00000000`76e414e0 4c8bd1 mov r10,rcx
  00000000`76e414e3 b820000000 mov eax,20h
  00000000`76e414e8 0f05 syscall
  00000000`76e414ea c3 ret
  00000000`76e414eb 0f1f440000 nop dword ptr [rax+rax]
  ntdll!ZwOpenThreadToken:
  00000000`76e414f0 4c8bd1 mov r10,rcx
  00000000`76e414f3 b821000000 mov eax,21h
  00000000`76e414f8 0f05 syscall
  kd> u nt!NtQueryVirtualMemory

  kd> u nt!ZwQueryVirtualMemory l20
  nt!ZwQueryVirtualMemory:
  fffff800`03eb75c0 488bc4 mov rax,rsp
  fffff800`03eb75c3 fa cli
  fffff800`03eb75c4 4883ec10 sub rsp,10h
  fffff800`03eb75c8 50 push rax
  fffff800`03eb75c9 9c pushfq
  fffff800`03eb75ca 6a10 push 10h
  fffff800`03eb75cc 488d053d2e0000 lea rax,[nt!KiServiceLinkage (fffff800`03eba410)]
  fffff800`03eb75d3 50 push rax
  fffff800`03eb75d4 b820000000 mov eax,20h
  fffff800`03eb75d9 e962650000 jmp nt!KiServiceInternal (fffff800`03ebdb40)
  fffff800`03eb75de 6690 xchg ax,ax

  nt!NtQueryVirtualMemory:
  fffff800`041979f0 48895c2410 mov qword ptr [rsp+10h],rbx
  fffff800`041979f5 4889742420 mov qword ptr [rsp+20h],rsi
  fffff800`041979fa 48894c2408 mov qword ptr [rsp+8],rcx
  fffff800`041979ff 57 push rdi
  fffff800`04197a00 4154 push r12
  fffff800`04197a02 4155 push r13
  fffff800`04197a04 4156 push r14
  fffff800`04197a06 4157 push r15

ntdll中的NtQueryVirtualMemory与ZwQueryVirtualMemory是同一个函数,也就是一个简单的包装函数,使用了syscall切入内核,调用了nt!ZwQueryVirtualMemory函数,0x20(NtQueryVirtualMemory的服务号)存入eax,然后在SSDT中查找相应的系统服务,然后调用NtQueryVirtualMemory。这个就不用多说了,写过内核的,人尽皆知。

  总而言之,Ntoskrnl导出的NtQueryVirtualMemory才是真正的执行函数。而直接使用内核中NtQueryVirtualMemory的好处在于,这样做的好处在于可以避免SSDTHook和InlineHook引起的错误调用,而且杀毒软件一般都会Hook掉SSDT的,这样直接调用也可以绕过监控。

下面先来具体看看NtQueryVirtualMemory函数(本文如未特殊说明,则所谈NtQueryVirtualMemory函数皆为ntoskrnl.exe中的NtQueryVirtualMemory函数):

 

 NTSTATUS NTAPI NtQueryVirtualMemory(  

          IN HANDLE                  ProcessHandle,                 //目标进程句柄  

          IN PVOID                  BaseAddress,               //目标内存地址  

          IN MEMORY_INFORMATION_CLASS  MemoryInformationClass,       //查询内存信息的类别  

          OUT PVOID                  Buffer,                      //用于存储获取到的内存信息的结构地址  

          IN ULONG                  Length,                      //Buffer的最大长度  

          OUT PULONG                  ResultLength OPTIONAL);        //存储该函数处理返回的信息的长度的ULONG的地址   

  )

  第三个参数类型MEMORY_INFORMATION_CLASS是一个枚举类型其定义如下:

  

  1. //MEMORY_INFORMATION_CLASS定义
  2. typedef enum _MEMORY_INFORMATION_CLASS
  3. {
  4. MemoryBasicInformation,          //内存基本信息
  5. MemoryWorkingSetInformation,       //工作集信息
  6. MemoryMappedFilenameInformation    //内存映射文件名信息
  7. } MEMORY_INFORMATION_CLASS;

  0x00:使用MemoryBasicInformation时,Buffer应当指向的结构为MEMORY_BASIC_INFORMATION,其定义如下:

typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
SIZE_T RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

  0x01:使用MemoryWorkingSetInformation时,Buffer应当指向的结构为MEMORY_WORKING_SET_INFORMATION,其定义如下:

typedef struct _MEMORY_WORKING_SET_INFORMATION {
ULONG SizeOfWorkingSet;
DWORD WsEntries[ANYSIZE_ARRAY];
} MEMORY_WORKING_SET_INFORMATION, *PMEMORY_WORKING_SET_INFORMATION;

  0x02:当使用MemoryMappedFilenameInformation  时,Buffer应当指向结构为MEMORY_MAPPED_FILE_NAME_INFORMATION,其定义如下:

#define _MAX_OBJECT_NAME 1024/sizeof(WCHAR)
typedef struct _MEMORY_MAPPED_FILE_NAME_INFORMATION {
UNICODE_STRING Name;
WCHAR Buffer[_MAX_OBJECT_NAME];
} MEMORY_MAPPED_FILE_NAME_INFORMATION, *PMEMORY_MAPPED_FILE_NAME_INFORMATION;

  在程序中我对的NtQueryVirtualMemory函数的使用方法是,定义一个NtQueryVirtualMemory函数结构的指针,在通过SSDT表来找到内核层的NtQueryVirtualMemory的地址,用定义好的指针指向这个地址,从而调用这个函数。这样做的原因在于Ring0层也无法直接调用内核中的NtQueryVirtualMemory了,这一点看雪中的这篇文章有过讨论,并给出了上述的解决方案:

  http://bbs.pediy.com/thread-65392.htm

  

typedef
NTSTATUS
(*pfnNtQueryVirtualMemory)(HANDLE ProcessHandle, PVOID BaseAddress,
MEMORY_INFORMATION_CLASS MemoryInformationClass,
PVOID MemoryInformation,
SIZE_T MemoryInformationLength,
PSIZE_T ReturnLength);

  这也就引出了我想要解决的问题——直接调用内核中Nt*系列函数之前,是需要将Previous Mode切换为KernelMode的!

  其原因可以参考这篇外文讲的很详细:

  http://www.osronline.com/article.cfm?article=257

 (文中摘录:) 

Previous Mode

Time to step back and figure out what all of this means. An important fact to know is that Kernel Mode components by default trust all other Kernel Mode components. Because system services are always processed in Kernel Mode, Windows keeps track of whether the request originated from User Mode or Kernel Mode to determine if the caller is to be implicitly trusted. The system uses the previous mode indicator to determine the mode from which a system service call came. When a call comes from User Mode, previous mode is set to User. When a system service processing routine needs to determine whether or not to implicitly trust its caller, it checks the value of previous mode. If previous mode is set to User, the system service processing routine knows the call came from User Mode and thus any parameters passed in to the function need to be validated before they can be used.

This is why the previous mode being set is really the most important part about what we have talked about so far. No matter what a User Mode application does, the system treats its system service request as a User request, coming from User Mode, and goes out of its way to validate the request. All buffers are subject to validation, all access checks are performed, and absolutely no part of the request is implicitly trusted. However, a Kernel Mode request is not as scrutinized and it is assumed that the passed in parameters are valid.

If a Kernel component calls the ZwXxx version of a native API, all is well. The previous mode is set to Kernel and the credentials of the Kernel are used. The system service processing routine that is called assumes that any parameters that are passed are valid, because the request came from a Kernel Mode component (and Kernel Mode components implicitly trust each other).

The NtXxxx version of the native system service is the name of the function itself. Thus, when a Kernel Mode component calls the NtXxxx version of the system service, whatever is presently set into previous mode is unchanged. Thus, it is quite possible that the Kernel component could be running on an arbitrary User stack, with the requestor mode set to User. The system service will not know any better, attempt to validate the request parameters, possibly using the credentials of the arbitrary User Mode thread, and thus possibly fail the request. Another problem here is that one step in the validation process for a User Mode request is that all passed in buffers have either ProbeForRead or ProbeForWrite executed on them, depending on the buffer’s usage. These routines raise exceptions if executed on Kernel Mode addresses. Therefore, if you pass in Kernel Mode buffers with your request mode set to User, your calls into the native API return STATUS_ACCESS_VIOLATION.

The moral of this bedtime story is that if you are in User Mode, use whatever variant you think makes your code look pretty. In Kernel Mode, use the ZwXxx routines and get your previous mode set properly, to Kernel Mode.

  

  简言之,调用NtQueryVirtualMemory中时,会检测当前调用来自用户态还是内核态 ,如果是来自内核态 ,不会检测参数, 而如果是来自用户态 ,就会做一系列的参数检测,  而内核组件可能运行在任意进程的上下文中 , 当它调用NtQueryVirtualMemory时 ,因为Previous Mode很可能是User Mode , 而我们的参数请求的内核态的地址 ,这时通常就会产STATUS_ACCESS_VIOLATION 。

  关于内核切换调用Nt系列函数的问题,看雪的这篇文章也有讨论,我们可以参考了解:

  http://bbs.pediy.com/thread-55142.htm

  

  

  

调用Nt函数内核模式切换问题的更多相关文章

  1. C++如何在r3静态调用NT函数

    原文最早发表于百度空间2010-02-22. 1.把ntapi.h.ntdll.lib放在一个目录,然后设置工具——选项——项目和解决方案——VC++目录——包含文件,把刚刚的目录设置在改包 含文件中 ...

  2. Zw函数与Nt函数的分别与联系

    Ring3中的NATIVE API,和Ring0的系统调用,都有同名的Zw和Nt系列函数,一度让初学者感到迷糊.N久前的我,也是相当的迷糊.现在就以ZwOpenProcess和NtOpenProces ...

  3. 50.Linux-分析ifconfig到内核的调用过程,实现内核启机自动设MAC地址(原)

    内核版本: Linux version 3.10.14 1.由于每次开发板开机的网卡eth0的物理地址都是随机的. 然后在网上找到可以通过命令行实现设置mac物理地址: ifconfig eth0 d ...

  4. (转)platform_driver_register,什么时候调用PROBE函数 注册后如何找到驱动匹配的设备

     platform_driver_register,什么时候调用PROBE函数 注册后如何找到驱动匹配的设备 2011-10-24 19:47:07 分类: LINUX   kernel_init中d ...

  5. electron/nodejs实现调用golang函数

    https://www.jianshu.com/p/a3be0d206d4c 思路 golang 支持编译成c shared library, 也就是系统中常见的.so(windows下是dll)后缀 ...

  6. LoadRunner如何调用外部函数

    LoadRunner如何调用外部函数 使用 VuGen 时,可以调用在外部 DLL 中定义的函数.通过从脚本调用外部函数,可以降低脚本的内存使用量以及总体运行时间.要调用外部函数,需要加载定义了该函数 ...

  7. C# 互操作性入门系列(二):使用平台调用调用Win32 函数

    好文章搬用工模式启动ing ..... { 文章中已经包含了原文链接 就不再次粘贴了 言明 改文章是一个系列,但只收录了2篇,原因是 够用了 } --------------------------- ...

  8. [转]C# 互操作性入门系列(二):使用平台调用调用Win32 函数

    传送门 C#互操作系列文章: C# 互操作性入门系列(一):C#中互操作性介绍 C# 互操作性入门系列(二):使用平台调用调用Win32 函数 C# 互操作性入门系列(三):平台调用中的数据封送处理 ...

  9. ng-repeat循环出来的部分调用同一个函数并且实现每个模块之间不能相互干扰

    使用场景:用ng-repeat几个部分,每个部分调用同一个函数,但是每个模块之间的功能不能相互干扰 问题:在用repeat实现.content块repeat的时候打算这样做:新建一个空的数组(nmbe ...

随机推荐

  1. How to create Excel file in C#

    http://csharp.net-informations.com/excel/csharp-create-excel.htm Before you create an Excel file in ...

  2. NS3 使用NS3工具PyViz

    官方文档 跑了一个样例(first.py): 由于 NetAnim 对我实在是有点不友好,在 PyViz 和 NetAnim 之间,我倾向前者.后者需要生成.xml文件,相比前者较为麻烦. 安装过程: ...

  3. 肿瘤基因组学数据库终结者:cBioPortal---转载

    转载自:http://blog.sciencenet.cn/blog-1509670-1000479.html 随着芯片和高通量测序技术的广泛应用,在肿瘤研究领域积累了越来越多的基因组学数据,特别是像 ...

  4. python 集合元素添加

    #A new empty set color_set = set() color_set.add("Red") print(color_set) #Add multiple ite ...

  5. mysql 开启远程访问

    # vi /etc/mysql/my.cnf修改 bind-address = 127.0.0.1  为  bind-address = 0.0.0.0 修改完成后重启mysql服务 # sudo / ...

  6. CASSANDRA How to import and export data

    https://docs.datastax.com/en/cql/3.1/cql/cql_reference/copy_r.html 感谢领导,感谢同事,与其自己百思不得其解,不如一个问题就搞定了. ...

  7. Win7下怎么设置让远程桌面连接记住密码下次登录不需再输入

    远程桌面连接功能想必大家都不会陌生吧,特别是使用VPS服务器的用户们经常会用到,为了服务器的安全每次都会把密码设置的很复制,但是这样也有一个麻烦,就是每次要桌面远程连接的时候都要输入这么复杂的密码,很 ...

  8. Python day20正则表达式和re方法

    元字符6个函数以及几个元字符1.'.'通配符2.'^'以什么开头3.'$'以什么结尾4.'*'紧挨着的字符0~∞次5.'+'紧挨着的字符1~∞次6.'?'紧挨的字符0次或1次7.'{}' {0,}== ...

  9. Selenium库的使用

    一.什么是Selenium selenium 是一套完整的web应用程序测试系统,包含了测试的录制(selenium IDE),编写及运行(Selenium Remote Control)和测试的并行 ...

  10. 原生js的博客

    原生js一里边还有很多二三等http://www.cnblogs.com/charling/p/3527561.html 选区器 http://wenku.baidu.com/link?url=zr2 ...