
各位看官,请不要疑惑——因为就算笔者在打出这个标题的时候都有点困惑我为什么要做这么多此一举的事情。本博客曾多次提到,笔者的主力系统为Linux,因此有一些日常工作如果可以在原生系统下实现,那么就省去了开虚拟机的麻烦。我相信还是有和我有相同需求的朋友的,因此把这次折腾的过程发出来,供大家批判(我这种没事找抽的行为)。
准备演示用文件
首先新建一个测试用的工程XxDriver,该工程没有任何实际功能,仅为一个驱动的最小源代码需求,其结构与内容如下:
- >> tree
- .
- ├── driver.c
- └── SOURCES
- 1 directory, 2 files
- >> cat SOURCES
- TARGETNAME = XxDriver
- TARGETTYPE = DRIVER
- SOURCES = driver.c
- >> cat driver.c
- #include <ntifs.h>
- VOID Unload(IN PDRIVER_OBJECT DriverObject)
- {
- DbgPrint("Unload\r\n");
- }
- NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
- {
- DriverObject->DriverUnload = Unload;
- return STATUS_SUCCESS;
- }
由于我们本次的目标是在Linux下编译Windows的驱动程序,因此我们需要编译工具链尽量的简单,由于这个原因,我们选择的编译工具为最后一个单独发行的WinDDK版本——即7600。
正常使用WinDDK编译Windows驱动的过程是:
- 打开对应系统的编译命令行环境
- 切换到驱动的工程目录(即上述建立的目录)
- 执行build命令
如果没有问题的话,WinDDK会根据选定的系统及架构编译对应的文件:

准备Linux环境下的编译环境
接下来,我们开始折腾Linux下编译驱动的环境。
安装wine
首先安装wine环境,根据你自己的发行版本安装即可,这一步应该不存在难度。
配置wine环境
然后,我们需要创建几个“磁盘”,其目的是为了让wine能快速的找到我们部署的编译环境和源码目录,这里笔者将${HOME}映射为D盘,下图为wincfg命令的配置界面:

最后,将Windows环境中安装的WinDDK目录拷贝到我们上面创建的磁盘目录中,按照上述D盘的配置,WinDDK位于D:\Softwares\WinDDK即${HOME}/Softwares/WinDDK目录下,而本文一开始创建的XxDriver工程的源代码则位于D:\Codes\XxDriver,即${HOME}/Codes/XxDriver下。
在配置完wine的磁盘,并将WinDDK拷贝到对应目录后,我们还需要拷贝2个工具到~/.wine/drive_c/windows下,这两个工具是setenv.bat(下文中会提到)中要用到的文件:
- >> find ~/.wine/drive_c/windows/ -iname 'ulib.dll' -or -iname 'doskey.exe'
- ~/.wine/drive_c/windows/system32/ulib.dll
- ~/.wine/drive_c/windows/system32/doskey.exe
- ~/.wine/drive_c/windows/syswow64/ulib.dll
- ~/.wine/drive_c/windows/syswow64/doskey.exe
上面几个文件分为32位和64位的程序,请从windows环境的对应目录拷贝。另外,根据笔者测试,似乎不拷贝也可以,但是编译环境会少几个命令的ALIAS ;)
启动WinDDK编译环境
回顾Windows下编译驱动的步骤,我们需要首先启动一个build环境,而build环境是通过WinDDK提供的如下方式打开的:

通过查看上述几个快捷方式的信息,我们可以得到如下4个x86/x64下的free或check编译环境初始化命令(IA64架构我们就不关注了,使用的较少):
- C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ chk x64 WIN7
- C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ fre x64 WIN7
- C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ chk x86 WIN7
- C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ fre x86 WIN7
那么结合上述我们在Linux部署的环境可知,我们可以通过如下命令初始化Linux下的build环境并使用build命令编译本文一开始的XxDriver实例代码:
- >> wine cmd.exe /k D:/Softwares/WinDDK/7600.16385.1/bin/setenv.bat D:\\Softwares\\WinDDK\\7600.16385.1 chk x64 WIN7
- 002c:fixme:winediag:loader_init wine-staging 9.9 is a testing version containing experimental patches.
- 002c:fixme:winediag:loader_init Please mention your exact version when filing bug reports on winehq.org.
- 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.
- 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.
- 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.
- 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.
- 0128:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0128:fixme:console:AddConsoleAliasW : (L"..", L"cd ..\\$*", L"cmd.exe") stub!
- D:\Softwares\WinDDK\7600.16385.1>cd \
- D:\>cd Codes\XxDriver
- D:\Codes\XxDriver>build
- BUILD: Compile and Link for AMD64
- BUILD: Loading d:\softwares\winddk\7600.16385.1\build.dat...
- BUILD: Computing Include file dependencies:
- BUILD: Start time: Sat Aug 31 13:58:01 2024
- BUILD: Examining d:\codes\xxdriver directory for files to compile.
- BUILD: Saving d:\softwares\winddk\7600.16385.1\build.dat...
- BUILD: Compiling and Linking d:\codes\xxdriver directory
- 014c:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0154:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0154:err:winediag:gnutls_process_attach failed to load libgnutls, no support for encryption
- 0154:err:winediag:process_attach failed to load libgnutls, no support for pfx import/export
- Compiling - driver.c
- 0154:fixme:msvcrt:__clean_type_info_names_internal (5008C9F0) stub
- 0154:fixme:msvcrt:__clean_type_info_names_internal (7A4E3490) stub
- 0154:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- Linking Executable - objchk_win7_amd64\amd64\xxdriver.sys
- 015c:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0164:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0164:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub
- 0164:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub
- 0164:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- 015c:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub
- 015c:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub
- 015c:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- 0134:fixme:file:NtSetInformationFile Unsupported class (19)
- BUILD: Finish time: Sat Aug 31 13:58:02 2024
- BUILD: Done
- 3 files compiled
- 1 executable built
OK,到此我们已经可以在Linux环境下手工编译驱动程序了~
使用Makefile文件编译
然而每次编译都敲这么多命令依然让人非常不爽,有没有简单一点的方法呢?
答案是肯定的——在Linux下,常见的编译方式为使用Makefile命令,我们自然也可以借助这个工具来偷点懒,做到少敲点命令的目标。
编辑如下内容到makefile.linux文件:
- .PHONY: x64chk x86chk clean
- WINDDK_PATH := "D:\\\\Softwares\\\\WinDDK\\\\7600.16385.1"
- x64chk:
- wine cmd.exe /c compile.bat x64chk ${WINDDK_PATH} 2 > /dev/null
- x86chk:
- wine cmd.exe /c compile.bat x86chk ${WINDDK_PATH} 2 > /dev/null
- clean:
- rm -f buildchk_*_*.log
- rm -rf objchk_*_*/
注意:makefile的文件名,不能为以下几个(不区分大小写):makefile、makefile.inc、makefile.def,因为WinDDK的build工具会检查当前目录是否有这几个文件名,然后读取其内容解析。由于其语法格式和linux下的有差异,因此可能导致编译报错。
从上述makefile.linux可知,玄机尽在compile.bat文件中,下面是该文件真容:
- set COMPILE_DIRECTORY=%CD%
- if "%1" == "x64chk" (
- call %~2\bin\setenv.bat %~2 chk x64 WIN7
- ) else if "%1" == "x86chk" (
- call %~2\bin\setenv.bat %~2 chk x86 WIN7
- )
- cd %COMPILE_DIRECTORY%
- build /g
其实没什么复杂的,只是借助batch脚本,自动调用setenv.bat来初始化编译环境,然后使用该环境进行build。
在编辑好上述makefile文件后,即可在linux下使用如下命令编译驱动文件。-f参数为makefile命令的参数,其意义为从指定文件名的文件中读取makefile内容, x64chk为上述makefile内容中.PHONY参数中定义的标记:
- >> make -f makefile.linux x64chk
- wine cmd.exe /c compile.bat x64chk "D:\\\\Softwares\\\\WinDDK\\\\7600.16385.1" 2 > /dev/null
- 0284:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0284:fixme:console:AddConsoleAliasW : (L"..", L"cd ..\\$*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"...", L"cd ..\\..\\$*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"....", L"cd ..\\..\\..\\$*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"BLD", L"build -cfeg $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"BZZ", L"build -ZPg $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"BZ", L"build -ZPg $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"BCZ", L"build -cZMg $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"USE", L"net use $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"NUSE", L"net use $* /d", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"QF", L"format a: /q /u /v:\"\"", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"VIEW", L"net view \\\\$*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"WINDIR", L"cd %windir% $T %homedrive%", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"LINK32", L"link $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"COFF", L"link $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"up", L"if \"$1\"==\"\" (cd ..) else (for /L %i in (1,1,$1) do cd ..)", L"cmd.exe") stub!
- BUILD: Compile and Link for AMD64
- BUILD: Loading d:\\softwares\\winddk\\7600.16385.1\build.dat...
- BUILD: Computing Include file dependencies:
- BUILD: Start time: Sat Aug 31 14:37:22 2024
- BUILD: Examining d:\codes\xxdriver directory for files to compile.
- BUILD: Saving d:\\softwares\\winddk\\7600.16385.1\build.dat...
- BUILD: Compiling and Linking d:\codes\xxdriver directory
- 0568:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0574:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0574:err:winediag:gnutls_process_attach failed to load libgnutls, no support for encryption
- 0574:err:winediag:process_attach failed to load libgnutls, no support for pfx import/export
- Compiling - driver.c
- 0574:fixme:msvcrt:__clean_type_info_names_internal (5008C9F0) stub
- 0574:fixme:msvcrt:__clean_type_info_names_internal (7A4E3490) stub
- 0574:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- Linking Executable - objchk_win7_amd64\amd64\xxdriver.sys
- 0570:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0598:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0598:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub
- 0598:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub
- 0598:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- 0570:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub
- 0570:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub
- 0570:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- 0330:fixme:file:NtSetInformationFile Unsupported class (19)
- BUILD: Finish time: Sat Aug 31 14:37:23 2024
- BUILD: Done
- 3 files compiled
- 1 executable built
到此为止,我们又可以偷一点懒了~
使用VS Code编译
虽然Linux下使用命令行make命令编译是一个常见方式,但是如果能在IDE中直接编译则更符合大家日常开发的习惯。接下来笔者介绍如何使用VS Code集成开发环境编译驱动。
首先,当然是要安装VS Code。
然后在市场中搜索并安装如下两个插件:
- C/C++: C语言的支持插件,例如语法高亮、自动补全等功能。
- Makefile Tools: Makefile编译系统的支持插件。
安装完成上述插件之后,打开本文一开始的工程目录,Makefile Tools插件在识别到该工程支持makefile编译后便会自动加载。
由于我们的makefile名称并不是常见名,因此要手动指定一下Makfile的路径。然后,指定好Build target为makeifle.linux希望编译的target之后,点击下图中红框中按钮即可编译工程:

下面是编译结果:

总结
本文借用wine和WinDDK构建了一套可以在Linux下编译Windows驱动的环境。最后通过Makefile简化了编译过程,最终得到了一套在Linux下的VS Code编译Windows驱动的非主流环境。