Watch & Learn

Debugwar Blog

Step in or Step over, this is a problem ...

Compiling windows drivers using WinDDK on Linux with wine

2024-08-31 @ UTC+0

Dear readers, please don’t be puzzled—because even I was a bit confused when I wrote this title about why I am doing such a seemingly unnecessary thing. As mentioned many times in this blog, my main operating system is Linux. Therefore, if some daily tasks can be accomplished in the native system, it saves the trouble of opening a virtual machine. I believe there are friends with similar needs, so I am sharing this process for everyone to critique (for my seemingly pointless behavior).

Preparing Demonstration Files

First, create a test project called XxDriver. This project has no actual functionality and only meets the minimum source code requirements for a driver. Its structure and content are as follows:

  1. >> tree  
  2. .  
  3. ├── driver.c  
  4. └── SOURCES  
  5.   
  6. 1 directory, 2 files  
  7. >> cat SOURCES   
  8. TARGETNAME  =   XxDriver  
  9. TARGETTYPE  =   DRIVER  
  10.   
  11. SOURCES     =   driver.c  
  12. >> cat driver.c   
  13. #include <ntifs.h>  
  14.   
  15. VOID Unload(IN PDRIVER_OBJECT DriverObject)  
  16. {  
  17.     DbgPrint("Unload\r\n");  
  18. }  
  19.   
  20. NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)  
  21. {  
  22.     DriverObject->DriverUnload = Unload;  
  23.     return STATUS_SUCCESS;  
  24. }  

Since our goal this time is to compile Windows drivers on Linux, we need the compilation toolchain to be as simple as possible. For this reason, we chose the last standalone version of WinDDK—version 7600.

The normal process for compiling Windows drivers using WinDDK is:

  1. Open the corresponding system’s compilation command line environment.
  2. Switch to the driver’s project directory (i.e., the directory created above).
  3. Execute the build command.

If there are no issues, WinDDK will compile the corresponding files based on the selected system and architecture.


Preparing the Compilation Environment on Linux

Next, we start setting up the environment for compiling drivers on Linux.

Installing Wine

First, install the Wine environment according to your distribution. This step should not be difficult.

Configuring the Wine Environment

Then, we need to create several “disks” to allow Wine to quickly find our deployed compilation environment and source code directory. Here, I map ${HOME} to the D drive, below is the configure window of winecfg command:


Finally, copy the WinDDK directory installed in the Windows environment to the disk directory we created above. According to the D drive configuration mentioned above, WinDDK is located in D:\Softwares\WinDDK, which corresponds to ${HOME}/Softwares/WinDDK. The source code of the XxDriver project created at the beginning of this article is located in D:\Codes\XxDriver, which corresponds to ${HOME}/Codes/XxDriver.

After configuring the Wine disks and copying WinDDK to the corresponding directory, we also need to copy two tools to ~/.wine/drive_c/windows. These tools are files used in setenv.bat (mentioned later):

  1. >> find ~/.wine/drive_c/windows/ -iname 'ulib.dll' -or -iname 'doskey.exe'  
  2. ~/.wine/drive_c/windows/system32/ulib.dll  
  3. ~/.wine/drive_c/windows/system32/doskey.exe  
  4. ~/.wine/drive_c/windows/syswow64/ulib.dll  
  5. ~/.wine/drive_c/windows/syswow64/doskey.exe  

上面几个文件分为32位和64位的程序,请从windows环境的对应目录拷贝。另外,根据笔者测试,似乎不拷贝也可以,但是编译环境会少几个命令的ALIAS ;)
These files are divided into 32-bit and 64-bit programs. Please copy them from the corresponding directories in the Windows environment. Additionally, based on my tests, it seems that is OK if not copying them, but the compilation environment will lack a few command aliases.

Starting the WinDDK Compilation Environment

Reviewing the steps for compiling drivers on Windows, we first need to start a build environment, which is opened through the following methods provided by WinDDK:


By checking the information of the above shortcuts, we can get the following four initialization commands for free or checked compilation environments under x86/x64 (we will not focus on the IA64 architecture as it is less commonly used):

  1. C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ chk x64 WIN7  
  2. C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ fre x64 WIN7  
  3. C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ chk x86 WIN7  
  4. C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ fre x86 WIN7  

Combining the environment we deployed on Linux, we can initialize the build environment on Linux and use the build command to compile the XxDriver example code created at the beginning of this article with the following command:

  1. >> wine cmd.exe /k D:/Softwares/WinDDK/7600.16385.1/bin/setenv.bat D:\\Softwares\\WinDDK\\7600.16385.1 chk x64 WIN7  
  2. 002c:fixme:winediag:loader_init wine-staging 9.9 is a testing version containing experimental patches.  
  3. 002c:fixme:winediag:loader_init Please mention your exact version when filing bug reports on winehq.org.  
  4. 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.  
  5. 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.  
  6. 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.  
  7. 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.  
  8. 0128:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  9. 0128:fixme:console:AddConsoleAliasW : (L"..", L"cd ..\\$*", L"cmd.exe") stub!  
  10. D:\Softwares\WinDDK\7600.16385.1>cd \  
  11.   
  12. D:\>cd Codes\XxDriver  
  13.   
  14. D:\Codes\XxDriver>build  
  15. BUILD: Compile and Link for AMD64  
  16. BUILD: Loading d:\softwares\winddk\7600.16385.1\build.dat...  
  17. BUILD: Computing Include file dependencies:  
  18. BUILD: Start time: Sat Aug 31 13:58:01 2024  
  19. BUILD: Examining d:\codes\xxdriver directory for files to compile.  
  20. BUILD: Saving d:\softwares\winddk\7600.16385.1\build.dat...  
  21. BUILD: Compiling and Linking d:\codes\xxdriver directory  
  22. 014c:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  23. 0154:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  24. 0154:err:winediag:gnutls_process_attach failed to load libgnutls, no support for encryption  
  25. 0154:err:winediag:process_attach failed to load libgnutls, no support for pfx import/export  
  26. Compiling - driver.c  
  27. 0154:fixme:msvcrt:__clean_type_info_names_internal (5008C9F0) stub  
  28. 0154:fixme:msvcrt:__clean_type_info_names_internal (7A4E3490) stub  
  29. 0154:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub  
  30. Linking Executable - objchk_win7_amd64\amd64\xxdriver.sys  
  31. 015c:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  32. 0164:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  33. 0164:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub  
  34. 0164:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub  
  35. 0164:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub  
  36. 015c:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub  
  37. 015c:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub  
  38. 015c:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub  
  39. 0134:fixme:file:NtSetInformationFile Unsupported class (19)  
  40. BUILD: Finish time: Sat Aug 31 13:58:02 2024  
  41. BUILD: Done  
  42.   
  43.     3 files compiled  
  44.     1 executable built  

OK, at this point, we can manually compile the driver on the Linux environment.

Compiling Using Makefile

However, typing so many commands every time we compile is still very annoying. Is there a simpler way?

The answer is yes—in Linux world, the common compilation method is to use the Makefile command. Naturally, we can also use this tool to save some time.

Edit the following content into the makefile.linux file:

  1. .PHONY: x64chk x86chk clean  
  2.   
  3. WINDDK_PATH := "D:\\\\Softwares\\\\WinDDK\\\\7600.16385.1"  
  4.   
  5. x64chk:  
  6.     wine cmd.exe /c compile.bat x64chk ${WINDDK_PATH} 2 > /dev/null  
  7.   
  8. x86chk:  
  9.     wine cmd.exe /c compile.bat x86chk ${WINDDK_PATH} 2 > /dev/null  
  10.   
  11. clean:  
  12.     rm -f buildchk_*_*.log  
  13.     rm -rf objchk_*_*/  

Note: The makefile name cannot be any of the following (case insensitive): makefile, makefile.inc, makefile.def, because the WinDDK build tool will check if these filenames exist in the current directory and then read their contents for parsing. Due to differences in syntax format with Linux, this may cause compilation errors.

From the above makefile.linux file,it can be seen that the key lies in the compile.bat file. Here is the content of this file:

  1. set COMPILE_DIRECTORY=%CD%  
  2.   
  3. if "%1" == "x64chk" (  
  4.     call %~2\bin\setenv.bat %~2 chk x64 WIN7  
  5. else if "%1" == "x86chk" (  
  6.     call %~2\bin\setenv.bat %~2 chk x86 WIN7  
  7. )  
  8.   
  9. cd %COMPILE_DIRECTORY%  
  10.   
  11. build /g  

It’s actually not complicated. It just uses a batch script to automatically call setenv.bat to initialize the compilation environment and then use this environment to build.

After editing the above makefile file, you can compile the driver file on Linux using the following command. The -f parameter is a parameter of the makefile command, meaning to read the makefile content from the specified filename. x64chk is a marker defined in the .PHONY parameter in the above makefile content:

  1. >> make -f makefile.linux x64chk  
  2. wine cmd.exe /c compile.bat x64chk "D:\\\\Softwares\\\\WinDDK\\\\7600.16385.1" 2 > /dev/null  
  3. 0284:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  4. 0284:fixme:console:AddConsoleAliasW : (L"..", L"cd ..\\$*", L"cmd.exe") stub!  
  5. 0284:fixme:console:AddConsoleAliasW : (L"...", L"cd ..\\..\\$*", L"cmd.exe") stub!  
  6. 0284:fixme:console:AddConsoleAliasW : (L"....", L"cd ..\\..\\..\\$*", L"cmd.exe") stub!  
  7. 0284:fixme:console:AddConsoleAliasW : (L"BLD", L"build -cfeg $*", L"cmd.exe") stub!  
  8. 0284:fixme:console:AddConsoleAliasW : (L"BZZ", L"build -ZPg $*", L"cmd.exe") stub!  
  9. 0284:fixme:console:AddConsoleAliasW : (L"BZ", L"build -ZPg $*", L"cmd.exe") stub!  
  10. 0284:fixme:console:AddConsoleAliasW : (L"BCZ", L"build -cZMg $*", L"cmd.exe") stub!  
  11. 0284:fixme:console:AddConsoleAliasW : (L"USE", L"net use $*", L"cmd.exe") stub!  
  12. 0284:fixme:console:AddConsoleAliasW : (L"NUSE", L"net use $* /d", L"cmd.exe") stub!  
  13. 0284:fixme:console:AddConsoleAliasW : (L"QF", L"format a: /q /u /v:\"\"", L"cmd.exe") stub!  
  14. 0284:fixme:console:AddConsoleAliasW : (L"VIEW", L"net view \\\\$*", L"cmd.exe") stub!  
  15. 0284:fixme:console:AddConsoleAliasW : (L"WINDIR", L"cd %windir% $T %homedrive%", L"cmd.exe") stub!  
  16. 0284:fixme:console:AddConsoleAliasW : (L"LINK32", L"link $*", L"cmd.exe") stub!  
  17. 0284:fixme:console:AddConsoleAliasW : (L"COFF", L"link $*", L"cmd.exe") stub!  
  18. 0284:fixme:console:AddConsoleAliasW : (L"up", L"if \"$1\"==\"\" (cd ..) else (for /L %i in (1,1,$1) do cd ..)", L"cmd.exe") stub!  
  19. BUILD: Compile and Link for AMD64  
  20. BUILD: Loading d:\\softwares\\winddk\\7600.16385.1\build.dat...  
  21. BUILD: Computing Include file dependencies:  
  22. BUILD: Start time: Sat Aug 31 14:37:22 2024  
  23. BUILD: Examining d:\codes\xxdriver directory for files to compile.  
  24. BUILD: Saving d:\\softwares\\winddk\\7600.16385.1\build.dat...  
  25. BUILD: Compiling and Linking d:\codes\xxdriver directory  
  26. 0568:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  27. 0574:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  28. 0574:err:winediag:gnutls_process_attach failed to load libgnutls, no support for encryption  
  29. 0574:err:winediag:process_attach failed to load libgnutls, no support for pfx import/export  
  30. Compiling - driver.c  
  31. 0574:fixme:msvcrt:__clean_type_info_names_internal (5008C9F0) stub  
  32. 0574:fixme:msvcrt:__clean_type_info_names_internal (7A4E3490) stub  
  33. 0574:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub  
  34. Linking Executable - objchk_win7_amd64\amd64\xxdriver.sys  
  35. 0570:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  36. 0598:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!  
  37. 0598:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub  
  38. 0598:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub  
  39. 0598:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub  
  40. 0570:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub  
  41. 0570:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub  
  42. 0570:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub  
  43. 0330:fixme:file:NtSetInformationFile Unsupported class (19)  
  44. BUILD: Finish time: Sat Aug 31 14:37:23 2024  
  45. BUILD: Done  
  46.   
  47.     3 files compiled  
  48.     1 executable built  

At this point, we can save some more time ;)

Compiling Using VS Code

Although using the command line make command to compile on Linux is a common method, if we can compile directly in an IDE, it would better match everyone’s daily development habits. Next, I will introduce how to use the VS Code integrated development environment to compile drivers.

First, of course, you need to install VS Code.

Then search and install the following two plugins in the marketplace:

  1. C/C++: A support plugin for the C language, providing features such as syntax highlighting and auto-completion.
  2. Makefile Tools: A support plugin for the Makefile compilation system.

After installing the above plugins, open the project directory created at the beginning of this article. The Makefile Tools plugin will automatically load upon recognizing that the project supports makefile compilation.

Since our makefile name is not a common name, we need to manually specify the path of the makefile. Then, after specifying the build target as the target to be compiled by makefile.linux, click the button in the red box in the image below to compile the project:


Here is the compilation result:


The end

In this article, we used Wine and WinDDK to build an environment for compiling Windows drivers on Linux. Finally, we simplified the compilation process using Makefile and ended up with a non-mainstream environment for compiling Windows drivers on Linux using VS Code.

Catalog
Preparing Demonstration Files
Preparing the Compilation Environment on Linux
Installing Wine
Configuring the Wine Environment
Starting the WinDDK Compilation Environment
Compiling Using Makefile
Compiling Using VS Code
The end

CopyRight (c) 2020 - 2025 Debugwar.com

Designed by Hacksign