Windows内核开发-9-32位和64位的区别

32位的应用程序可以完美再64位的电脑上运行,而32位的内核驱动无法再64位的电脑上运行,或者64位的驱动无法在32位的应用程序上运行。这是为什么呢。

原因是在x64的Windows操作系统上,模拟了x86操作系统的操作,并且引入了一个WOW64子系统,将x86和x64完美进行兼容。

WOW64子系统

x86能在x64上运行全靠这个东西。全名叫做Windows On Windows,英文名感觉是在套娃,其实它的意思就是在Windows64上运行Windows32。

这个系统由Wow64.dll,Wow64Win.dll,Wow64Cpu.dll三个dll实现,具体怎么实现的不用考虑。

Wow64子系统可以完美实现x86和x64之间的转换。

转换流程: 当一个32位Application发起系统调用时,WOW64会拦截下来,将其转换为64位的类型(包括指针范围,数据类型范围等等),然后再把系统调用请求提交给内核。这整个拦截-转换的流程被称为"thunking"。

WOW64有两个重要的模块,一个是系统文件重定向(File System Redirector),一个是注册表重定向(Registry Redirector)。

系统文件重定向(File System Redirector)

Windows64位OS包含了两个System32文件,一个是System32另一个是SysWow64。默认情况下的安装路径%Windows%\System32和%Windows%\SysWow64。

System32这个文件里面保存了系统需要的一些二进制文件,System32里面存放的是x64的系统二进制文件,SysWow64里面存放的是x86里的文件。不要被这个什么system32迷惑成了它就是32位的系统文件了。

一般情况下32位的只能加载32位的系统dll,64只能加载64的。因为是64位的操作系统,所以肯定默认是加载64的dll,但是32位怎么办,为了解决这个问题WOW64就构成了文件系统重定向模块,把32的系统dll放到了SysWow64里面,然后把System32这个文件夹给他重定向指到了SysWow64文件夹里了。

这里我们写一个代码来验证一下:

void TestFileRedirector()
{
HANDLE hFile = CreateFileA("C:\\Windows\\System32\\test.txt", GENERIC_READ | GENERIC_WRITE, 0x00000000, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
cout << "创建文件失败" << endl;
}
else
{
cout << "创建文件成功,文件名为test.txt" << endl;
}
CloseHandle(hFile);
}

假如说这个test文件是在SysWow64文件夹下面创建的,那么说明我们前面的讲述没问题,确实是重定向到了SysWow64里面。这里我们要用管理员启动Visual Studio才行,因为这个文件夹是系统文件夹,需要管理员权限。

下面是我的验证结果:

在x86和x64运行后分别是在System32和SysWow64新建了文件,足以说明结论了。

关闭系统文件重定向

文件重定向固然不错,但是肯定有时候我们会不得不关闭它。

这里就会用到两个API:

BOOL Wow64DisableWow64FsRedirection(
[out] PVOID *OldValue
);//关闭重定向,将原来的值保存到输入参数oldvalue里面
BOOL Wow64RevertWow64FsRedirection(
PVOID OlValue
);//通过参数olvalue来恢复重定向

这里我们在修改一下我们的代码,让他x86的程序不要重定向到x64文件里面:

void TestFileRedirector()
{
PVOID OldValue;
auto ret = Wow64DisableWow64FsRedirection(&OldValue);
if (ret == 0)
{
cout << "调用关闭重定向的函数失败,请检查" << endl;
return;
}
else
{
cout << "调用重定向的函数成功,已经取消文件系统重定向" << endl;
}

HANDLE hFile = CreateFileA("C:\\Windows\\System32\\test.txt", GENERIC_READ | GENERIC_WRITE, 0x00000000, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
cout << "创建文件失败" << endl;
return;
}
else
{
cout << "创建文件成功,文件名为test.txt" << endl;
}
CloseHandle(hFile);
Wow64RevertWow64FsRedirection(OldValue);
}

这样再创建就是到System32系统文件夹里面了。

有一部分文件是不会被重定向的:

%Windows%System32\catrrot
%Windows%System32\catrrot2
%Windows%System32\drivers\etc
%Windows%System32\logfiles
%Windows%System32\spool

注册表重定向(Registry Redirector)

和系统文件重定向(File System Redirecotr)比较类似,但是功能更为复杂。除了重定向还有注册表反射功能(Registry Reflection 该功能暂时用不到)。

和File System Redirector比较类似的是,win32访问HKEY_LOCAL_MACHINE\SOFTWARE会被重定向到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node下面。同样写一下代码测试一下:

void TestRegRedirector()
{
HKEY hKey = NULL;
RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Test"),
0, NULL, 0, KEY_READ, NULL, &hKey,NULL);
if (hKey != NULL)
{
cout << "创建注册表成功" <<endl;
RegCloseKey(hKey);
}
else
{
cout << "创建注册表失败" << endl;
return;
}
}

用32位来运行,看他添加到哪里:

当然也肯定有关闭的办法。

DIY注册表重定向

在创建注册表的API上加一个宏定义就可完美解决这个问题了:

    RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Test"),0, NULL, 0, KEY_READ | KEY_WOW64_32KEY, NULL, &hKey,NULL);

这里的KEY_WOW64_32KEY就是32位,KEY_WOW64_64KEY就可以锁定为64位了。
void TestRegRedirector()
{
HKEY hKey = NULL;
RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Test"),
0, NULL, 0, KEY_READ | KEY_WOW64_32KEY, NULL, &hKey,NULL);
if (hKey != NULL)
{
cout << "创建注册表成功" <<endl;
RegCloseKey(hKey);
}
else
{
cout << "创建注册表失败" << endl;
return;
}
}

64位系统的升级技术

64对比32提供了很多新技术,比如之前的32位被很多程序很多公司,进行挂钩啊各种功能导致很不安全很麻烦。

PatchGuard

所以升级了一个PatchGuard技术,这个机制就是系统会定期检查内部的关键位置是否被篡改,一旦被篡改就会蓝屏。

比如一些论坛常见的SSDT(系统描述表),GDT(全局描述表),IDT(中断描述表)等等。但是其实也是可以绕过的。正所谓道高一尺魔高一丈就是这个意思,没有绝对的安全。

x64的编译、安装、运行

编译很简单,vs换成x64就行了。

安装的话著需要考虑32位exe安装驱动的时候不会把他放到64位驱动system32这个文件夹下就行了,这个用关闭File System redirecotr就行。

运行:x64的驱动必须得有签名才行,变相提高了安全吧,不过我们自己测试就把测试机变成测试模式就好了。

编程差异

x86和x64编程还是有少许区别的。

加入汇编

32位和64比较麻烦的就是不能直接内联汇编了,就比如说下面这段代码:

#include<ntddk.h>


extern "C"
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);
__asm
{
int 3
}

return STATUS_SUCCESS;
}

一个很简单的内核驱动代码,用x64编译就不行,而x86没问题:

也就是说不运行直接内联汇编了,只能用汇编asm写好了,然后作为函数的形式放进去了。

条件编译

WDK设置了宏来帮助条件编译,比如针对操作系统平台有: _M_AMD64, _M_IX64等等,对于位数也有 _WIN64和 _WIN32 ,比如说:

#ifdef _WIN32   //32位情况

#else //不是32位情况

#endif

调整数据结构

当一个32位的exe通过DeviceIoControl的方式和64位驱动进行交互的时候,如果结构体里有指针是不会进行thunking技术调整的,所以这里就会涉及到一些问题了,比如指针位数的不兼容,以及比如int这种位数也是不兼容的,还有对齐的一些很多问题。所以最好是采用限制好了的数据结构体。

比如说结构体定义成这样

struct DRIVER_DATA
{
void* POINTER_32 test;
UNICODE_STRING32 testUnicode_string;
};

直接把长度一次到位限制了。

小结:

x64和x86的区别还是蛮多的,如果要从头到尾设计一个驱动的话是必须得思考这个问题的。

Windows内核开发-9-32位和64位的区别的更多相关文章

  1. linux内核学习之三:linux中的"32位"与"64位"

    在通用PC领域,不论是windows还是linux界,我们都会经常听到"32位"与"64位"的说法,类似的还有"x86"与"x86 ...

  2. 【扫盲】】32位和64位Windows的区别

    用户购买windows安装盘或者重新安装操作系统的时候,通常会遇到这个问题,就是不知道该如何选择使用32位操作系统和64位操作系统,有人说64位系统速度快,其实理论上确实是这样,不过具体还要根据你的个 ...

  3. 【原创】在Windows系统中使用VC9、VC11编译32位、64位PHP及其扩展

    项目中需要使用runkit模块实现AOP,但是团队成员的开发环境都是Windows,而runkit模块官方没有提供Windows环境下的dll扩展,只能自己编译. 下面是编译过程的分类总结.(操作系统 ...

  4. zz Windows 10安装教程:硬盘安装Win10 系统步骤(适合32位和64位)

    Windows 10安装教程:硬盘安装Win10 系统步骤(适合32位和64位) Posted on 2015年01月28日 by 虾虾 22 Comments   最新的Windows 10 MSD ...

  5. 查看 Java Web 开发环境软件是 32 位还是 64 位

    这里 Java Web 的开发环境指的是:Java + Tomcat + Eclipse 查看 Java 的版本 java -version 结果: JDK 版本位 1.8.0\_221 而且是 64 ...

  6. 如何判断你的windows系统是32位还是64位?

    [学习笔记] 如 何判断你的windows系统是32位还是64位? java -version时,如果没有64就是32位的.eclipse.ini中如果没有64,就是32位的.但是我们的ini文件里面 ...

  7. linux中的"32位"与"64位"

    linux内核学习之三:linux中的"32位"与"64位" 在通用PC领域,不论是windows还是linux界,我们都会经常听到"32位" ...

  8. Windows内核开发-5-(2)-内核模式调试

    Windows内核开发-5-(2)-内核模式调试 普通用户模式的调试,采取的是给进程添加一个线程来挂起断点,作为一个调试器的线程在进程中使用.照这样来类推,对操作系统调试相当于添加一个进程来限制操作系 ...

  9. Windows内核开发-6-内核机制 Kernel Mechanisms

    Windows内核开发-6-内核机制 Kernel Mechanisms 一部分Windows的内核机制对于驱动开发很有帮助,还有一部分对于内核理解和调试也很有帮助. Interrupt Reques ...

随机推荐

  1. Linux centos7 -bash: pstree: 未找到命令

    2021-08-12 1. 命令简介pstree命令将所有行程以树状图显示,树状图将会以 pid (如果有指定) 或是以 init 这个基本行程为根 (root),如果有指定使用者 id,则树状图会只 ...

  2. Git使用教程七——Git实用技能

    Git实用技能 1.图形管理工具 Github for Desktop Source tree 老牌的GitGUl管理工具了,也号称是最好用的Git GUI工具.功能丰富,基本操作和高 级操作都非常流 ...

  3. DNS重新绑定攻击

    来自微信外挂的安全风险 DNS重新绑定攻击 DDNS 动态域名设置

  4. Docker详解(一)——

    Docker详解 https://www.cnblogs.com/antLaddie/p/14276726.html

  5. Redis集群的搭建及与SpringBoot的整合

    1.概述 之前聊了Redis的哨兵模式,哨兵模式解决了读的并发问题,也解决了Master节点单点的问题. 但随着系统越来越庞大,缓存的数据越来越多,服务器的内存容量又成了问题,需要水平扩容,此时哨兵模 ...

  6. 动态路由——OSPF

    目录: 一. OSPF路由协议概述     1,OSPF协议    2,内部网关协议和外部网关协议   3,OSPF的工作过程 二.OSPF基本概念   1,OSPF区域   2,区域ID   3,R ...

  7. JNDI注入基础

    JNDI注入基础 一.简介 JNDI(The Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API,命名服务 ...

  8. 一文搞懂Python Unittest测试方法执行顺序

    大家好~我是米洛! 欢迎关注我的公众号测试开发坑货,一起交流!点赞收藏关注,不迷路. Unittest unittest大家应该都不陌生.它作为一款博主在5-6年前最常用的单元测试框架,现在正被pyt ...

  9. Java中HashCode()和equals()的作用

    引言 我们知道Java中的集合(Collection)大致可以分为两类,一类是List,再有一类是Set. 前者集合内的元素是有序的,元素可以重复:后者元素无序,但元素不可重复. 这里就引出一个问题: ...

  10. VMware ESXi 7.0 U2 SLIC 2.6 & Unlocker 集成 Intel NUC 网卡、USB 网卡和 NVMe 驱动

    ESXi 7 U2 标准版镜像集成 NUC 网卡.USB 网卡 和 NVMe 驱动. 请访问原文链接:https://sysin.org/blog/vmware-esxi-7-u2-nuc-usb-n ...