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. centos7 ftp 拒绝连接

    2021-09-03 1. 问题描述 刚才在重新搭建 ftp 服务器时,发现 ftp 拒绝连接,想起来我还没启动 vsftpd 服务,尝试启动却无法启动 vsftpd 服务 2. 解决方法 使用命令 ...

  2. 整理之BroadcaseReceiver

    广播的分类 有序广播:按接收器优先级从高到低接受消息,一次只能有一个接收器处理消息.中途可以被截断. 无序广播:所有接收器同时接受消息并处理,无法拦截. 本地广播:只能在本应用内传播的无需广播.上面两 ...

  3. Photoshop 批量修改图像大小

  4. vue 引用高德地图

    vue 引用地图之傻瓜式教程(复制粘贴即可用) npm 下载 npm install vue-amap --save main.js 代码 import AMap from 'vue-amap'; V ...

  5. 20210803 noip29

    考场 第一次在 hz 考试.害怕会困,但其实还好 看完题感觉不太难,估计有人 AK. T3 比较套路,没办法枚举黑点就从 LCA 处考虑,在一个点变成黑点时计算其他点和它的 LCA 的贡献,暴力跳父亲 ...

  6. rtl8188eu 驱动移植

    测试平台 宿主机平台:Ubuntu 16.04.6 目标机:iMX6ULL 目标机内核:Linux 4.1.15 rtl8188eu 驱动移植 在网上下载Linux版的驱动源码: wifi驱动的实现有 ...

  7. 植入式Web前端开发方法

    上一篇,我讲述了植入式Web前端开发的基本情况,本篇就来探究其开发方法.以下假定CMS只能植入前端代码,并且需求规模是任意大小的. 代码形式 HTML代码是直接植入的毫无疑问,但除非植入的代码非常简短 ...

  8. 导出excel、word、csv文件方法汇总

    http://www.woaic.com/2012/06/64 excel文件主要是输出html代码.以xls的文本格式保存文件. 生成excel格式的代码: /// <summary> ...

  9. uni-app开发基本知识点

    uni-app: 开始:必须要有一个根view结点. 外部文件引用方式的变化: js要require进来,变成了对象. <script> var util = require('../.. ...

  10. 关于当前PHP脚本运行时系统信息相关函数

    我们的 PHP 在执行的时候,其实可以获取到非常多的当前系统相关的信息.就像很多开源的 CMS 一般会在安装的时候来检测一些环境信息一样,这些信息都是可以方便地动态获取的. 脚本文件运行时的系统用户相 ...