zer0m0n & ReactOS Build Environment

Posted on Wed 14 September 2016 in zer0m0n by Jurriaan Bremer

Many of you will know zer0m0n, a kernel driver developed for Cuckoo Sandbox by Nicolas Correia, Adrien Chevalier, and Cyril Moreau. In particular, zer0m0n has been developed to improve the analysis capabilities of Cuckoo as well as to further hide its presence.

After almost three years of part-time development by the French guys, the time has come for the Cuckoo team to mainstream its integration and usage. However, as most if not all Cuckoo developers (and users) run Linux-based operating systems, it is preferable that we find a way to develop 32-bit and 64-bit Windows kernel drivers on Linux-based systems.

After reaching out to Alex Ionescu, the Windows kernel guru himself, I was informed that ReactOS has an entire ReactOS Build Environment (aka RosBE from now on). This was all pretty easy to get started with and I had built my own ReactOS kernel within the hour, or so. Unfortunately though, based on the limited resources available on this topic, currently it’s not possible to build a 64-bit ReactOS kernel on non-Windows based systems. Naturally this needs to be investigated, as ReactOS provides everything else that you will be needing for building Windows kernel drivers (API definitions, header files, etc) without the use of MSVC. (It is preferable not having to copy over all of the structure and function definitions for your own little project when ReactOS already did all of the hard work on that front).

Setting up the initial RosBE

For completeness sake - so that we can refer potential contributors of zer0m0n to this "setup" guide - we will be going through the build environment setup here. This guide has been written with Ubuntu 14.04 in mind, provided that’s also ReactOS’ goto operating system for development.

First of all, fetch RosBE-Unix-2.1.2.tar.bz2 from SourceForge (am I right?).. https://sourceforge.net/projects/reactos/files/RosBE-Unix/

Install some required packages, unpack the RosBE archive, and setup the build environment.

# Could be this list is incomplete.
$ sudo apt-get install texinfo bison flex mingw-w64 libz-dev

# Unpack archive.
$ tar xfj RosBE-Unix-2.1.2.tar.bz2

# Setup a local RosBE, not a global one.
# Type "yes" in the prompt and wait a bit.
# This is going to warm up your apartment.
$ cd RosBE-Unix-2.1.2
$ ./RosBE-Builder.sh ~/rosbe

We now have the RosBE ready to be used. In fact, we’re just a couple commands away from building the entire ReactOS kernel (and related files).

Building ReactOS

Building ReactOS consists of starting the RosBE session, cloning the ReactOS Git repository, and building it through the ninja command.

# Enter a RosBE session.
$ ~/rosbe/RosBE.sh

# Clone the repository.
$ git clone https://github.com/reactos/reactos

# Prepare ReactOS build.
$ cd reactos/reactos
$ ./configure.sh

# Build ReactOS (this will also heat your apartment).
$ cd output-MinGW-i386/reactos
$ ninja

We can remove the ReactOS build by running the clean command from the correct directory.

$ cd ../..
$ clean

Now onto the real goal of this blogpost, modifying ReactOS in such a way that we can use it to compile 64-bit Windows kernel drivers on our Ubuntu system.

What’s in a compilation?

Before building zer0m0n or anything custom, we’re first going to take a look under the hood when compiling a relatively simple ReactOS-related driver. For no particular reason we settled with the usbd driver. The usbd driver consists of the actual implementation file as well as a resource file that defines its exported functions.

This is where it gets a little bit hairy. So, rather than delving into CMake internals (of which I’m not an expert), I settled with using the following strace command to find all invocations of mingw32 (GCC for Windows, so to say). Note that ninja usbd only builds the usbd driver rather than everything.

# Follow children, show long strings, limit to execve(2), be quiet.
$ strace -f -s 999999 -e execve -qq ninja usbd

Copy and pasting the executed commands we get something like the following, except that this snippet omits lots and lots of compiler switches, macro definitions, and what have you.

$ rm -rf drivers/usb/usbd/CMakeFiles/* drivers/usb/usbd/usbd.sys
$ mkdir -p drivers/usb/usbd/CMakeFiles/usbd.dir/

$ i686-w64-mingw32-gcc [ ... ] \
    -o drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.c.obj \
    -c ../../drivers/usb/usbd/usbd.c

$ i686-w64-mingw32-windres -O coff [ ... ] \
    /home/jbr/reactos/reactos/drivers/usb/usbd/usbd.rc \

$ i686-w64-mingw32-gcc -E -xc-header [ ... ] \

$ i686-w64-mingw32-gcc [ ... ] -shared \
    -o drivers/usb/usbd/usbd.sys \
    drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.c.obj [ ... ]

The point is the fact that, from a first sight, we should be able to trivially modify these commands into building a 64-bit usbd.sys file. This only brings a couple of minor issues which we’ll delve into right now.

64-bit Compilation

Note that before running any of the next steps, you must have built some of the related components with 32-bit as target (simply running ninja will literally compile over 9000 things rather than just a couple hundred for the following command).

# From the output-MingW32-i386/reactos directory.
$ ninja ntoskrnl hal usbd xdk

Modifying the commands to represent 64-bit arguments requires only a couple of changes to the commands that we had obtained earlier.

  • Change of the i686 command prefixes into x86_64.
  • Removal of the -march=pentium, -mtune=i686, and -mpreferred-stack-boundary=3 arguments.
  • Removal of the -D__i386__, -Di386, -D_X86_, -D_M_IX86, and -D_USE_32BIT_TIME_T macro definitions.
  • Rewriting the entry function name parameter -Wl,-entry,_DriverEntry@8 to -Wl,-entry,DriverEntry.

Having these changes applied to our bash script, we can now try to build the 64-bit usbd.sys driver. We will run into exactly two other issues.

  • Incompatible libntoskrnl.a and libhal.a files.
  • A couple minor compilation issues.

To be precise, we will be getting symbol not defined and undefined reference errors.

Cannot export USBD_CalculateUsbBandwidth@12: symbol not defined
Cannot export USBD_CreateConfigurationRequest@8: symbol not defined
../../drivers/usb/usbd/usbd.c:77: undefined reference to `__imp_ExAllocatePoolWithTag'
../../drivers/usb/usbd/usbd.c:86: undefined reference to `__imp_ExFreePool'

These errors occur due to the definition of 32-bit .def files. We already changed one of such instance by renaming _DriverEntry@8 to DriverEntry and we will be doing so for the libhal.a, libntoskrnl.a, and usbd.sys related files as well. In particular, when compiling 32-bit applications, the @8 part defines the amount of stack space that is required for the arguments of this function - four bytes per argument. This is part of the __stdcall calling convention. When compiling for 64-bit, however, there’s only one calling convention, and these declarations are not needed for this calling convention - it is therefore a problem for the compiler if you do explicitly specify the @8 etc.

Simply put, we will be stripping everything from the last @ token onwards for the following files.

  • hal/halx86/libhal_implib.def
  • ntoskrnl/libntoskrnl_implib.def
  • drivers/usb/usbd/usbd.def

As an example, we change the libntoskrnl_implib.def file as follows.

; File generated automatically, do not edit!

NAME ntoskrnl.exe

- CcCanIWrite@16
- CcCopyRead@24
- CcCopyWrite@20
+ CcCanIWrite
+ CcCopyRead
+ CcCopyWrite
[ ... ]

After doing so it is required that we create new libhal.a and libntoskrnl.a files, this may be done using the following commands.

# From the output-MingW32-i386/reactos directory.
$ x86_64-w64-mingw32-dlltool --def ntoskrnl/libntoskrnl_implib.def --kill-at --output-lib=ntoskrnl/libntoskrnl.a
$ x86_64-w64-mingw32-dlltool --def hal/halx86/libhal_implib.def --kill-at --output-lib=hal/halx86/libhal.a

We are also required to do a couple of minor header file modification, the patch for that looks as follows (surely there are better workarounds).

diff --git a/reactos/sdk/include/crt/_mingw.h b/reactos/sdk/include/crt/_mingw.h
index 203ac56..e72b1d0 100644
--- a/reactos/sdk/include/crt/_mingw.h
+++ b/reactos/sdk/include/crt/_mingw.h
@@ -168,7 +168,9 @@ allow GCC to optimize away some EH unwind code, at least in DW2 case.  */
#define __int32 int
#define __int64 long long
#ifdef _WIN64
    typedef int __int128 __attribute__ ((mode (TI)));
# endif
# define __ptr32
# define __ptr64
diff --git a/reactos/sdk/include/crt/mingw32/intrin_x86.h b/reactos/sdk/include/crt/mingw32/intrin_x86.h
index e325785..d575465 100644
--- a/reactos/sdk/include/crt/mingw32/intrin_x86.h
+++ b/reactos/sdk/include/crt/mingw32/intrin_x86.h
@@ -739,6 +739,7 @@ __INTRIN_INLINE void __movsd(unsigned long * Destination, const unsigned long *

#ifdef __x86_64__
__INTRIN_INLINE void __movsq(unsigned long * Destination, const unsigned long * Source, size_t Count)
    __asm__ __volatile__
@@ -748,6 +749,7 @@ __INTRIN_INLINE void __movsq(unsigned long * Destination, const unsigned long *
        "[Destination]" (Destination), "[Source]" (Source), "[Count]" (Count)

#if defined(__x86_64__)
@@ -832,10 +834,12 @@ __INTRIN_INLINE void __addgsword(unsigned long Offset, unsigned short Data)
    __asm__ __volatile__("addw %w[Data], %%gs:%a[Offset]" : : [Offset] "ir" (Offset), [Data] "ir" (Data) : "memory");

__INTRIN_INLINE void __addgsdword(unsigned long Offset, unsigned int Data)
    __asm__ __volatile__("addl %k[Data], %%gs:%a[Offset]" : : [Offset] "ir" (Offset), [Data] "ir" (Data) : "memory");

__INTRIN_INLINE void __addgsqword(unsigned long Offset, unsigned long long Data)

All of this leaves us with the following bash script that contains commands to fully compile the 64-bit Windows usbd.sys kernel driver.


rm -rf ./drivers/usb/usbd/CMakeFiles/* drivers/usb/usbd/usbd.sys ntoskrnl/libntoskrnl.a hal/halx86/libhal.a
mkdir -p drivers/usb/usbd/CMakeFiles/usbd.dir/

x86_64-w64-mingw32-dlltool --def ntoskrnl/libntoskrnl_implib.def --kill-at --output-lib=ntoskrnl/libntoskrnl.a
x86_64-w64-mingw32-dlltool --def hal/halx86/libhal_implib.def --kill-at --output-lib=hal/halx86/libhal.a
x86_64-w64-mingw32-gcc -DDBG=1 -DKDBG=1 -DUSE_COMPILER_EXCEPTIONS -DWINVER=0x502 -D_SEH_ENABLE_TRACE -D_SETUPAPI_VER=0x502 -D_USE_PSEH3=1 -D_WIN32_IE=0x600 -D_WIN32_WINDOWS=0x502 -D_WIN32_WINNT=0x502 -D__REACTOS__ -D_inline=__inline -Dusbd_EXPORTS -Wa,--compress-debug-sections -pipe -fms-extensions -fno-strict-aliasing -nostdinc -mstackrealign -Wold-style-declaration -Wdeclaration-after-statement -fdebug-prefix-map="/home/jbr/reactos/reactos"=ReactOS -gdwarf-2 -gstrict-dwarf -femit-struct-debug-detailed=none -feliminate-unused-debug-symbols -Werror -Wall -Wpointer-arith -Wno-char-subscripts -Wno-multichar -Wno-unused-value -Wno-maybe-uninitialized -O1 -fno-optimize-sibling-calls -fno-omit-frame-pointer -fno-set-stack-executable -Winvalid-pch -Werror=invalid-pch -Idrivers/usb/usbd -I../../drivers/usb/usbd -I../../sdk/include -I../../sdk/include/psdk -I../../sdk/include/dxsdk -Isdk/include -Isdk/include/psdk -Isdk/include/dxsdk -Isdk/include/ddk -Isdk/include/reactos -I../../sdk/include/crt -I../../sdk/include/ddk -I../../sdk/include/ndk -I../../sdk/include/reactos -I../../sdk/include/reactos/libs -MMD -MT drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.c.obj -MF drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.c.obj.d -o drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.c.obj -c ../../drivers/usb/usbd/usbd.c
x86_64-w64-mingw32-windres  -O coff -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/drivers/usb/usbd -I/home/jbr/reactos/reactos/drivers/usb/usbd -I/home/jbr/reactos/reactos/sdk/include -I/home/jbr/reactos/reactos/sdk/include/psdk -I/home/jbr/reactos/reactos/sdk/include/dxsdk -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include/psdk -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include/dxsdk -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include/ddk -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include/reactos -I/home/jbr/reactos/reactos/sdk/include/crt -I/home/jbr/reactos/reactos/sdk/include/ddk -I/home/jbr/reactos/reactos/sdk/include/ndk -I/home/jbr/reactos/reactos/sdk/include/reactos -I/home/jbr/reactos/reactos/sdk/include/reactos/libs --preprocessor "x86_64-w64-mingw32-gcc -E -xc-header -MMD -MF drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.rc.res.d -MT drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.rc.res"  -DRC_INVOKED -D__WIN32__=1 -D__FLAT__=1 -DDBG=1 -DKDBG=1 -DUSE_COMPILER_EXCEPTIONS -DWINVER=0x502 -D_SEH_ENABLE_TRACE -D_SETUPAPI_VER=0x502 -D_USE_PSEH3=1 -D_WIN32_IE=0x600 -D_WIN32_WINDOWS=0x502 -D_WIN32_WINNT=0x502 -D__REACTOS__ -D_inline=__inline -Dusbd_EXPORTS /home/jbr/reactos/reactos/drivers/usb/usbd/usbd.rc drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.rc.res
x86_64-w64-mingw32-gcc -E -xc-header -MMD -MF drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.rc.res.d -MT drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.rc.res -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/drivers/usb/usbd -I/home/jbr/reactos/reactos/drivers/usb/usbd -I/home/jbr/reactos/reactos/sdk/include -I/home/jbr/reactos/reactos/sdk/include/psdk -I/home/jbr/reactos/reactos/sdk/include/dxsdk -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include/psdk -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include/dxsdk -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include/ddk -I/home/jbr/reactos/reactos/output-MinGW-i386/reactos/sdk/include/reactos -I/home/jbr/reactos/reactos/sdk/include/crt -I/home/jbr/reactos/reactos/sdk/include/ddk -I/home/jbr/reactos/reactos/sdk/include/ndk -I/home/jbr/reactos/reactos/sdk/include/reactos -I/home/jbr/reactos/reactos/sdk/include/reactos/libs -DRC_INVOKED -D__WIN32__=1 -D__FLAT__=1 -DDBG=1 -DKDBG=1 -DUSE_COMPILER_EXCEPTIONS -DWINVER=0x502 -D_SEH_ENABLE_TRACE -D_SETUPAPI_VER=0x502 -D_USE_PSEH3=1 -D_WIN32_IE=0x600 -D_WIN32_WINDOWS=0x502 -D_WIN32_WINNT=0x502 -D__REACTOS__ -D_inline=__inline -Dusbd_EXPORTS /home/jbr/reactos/reactos/drivers/usb/usbd/usbd.rc
x86_64-w64-mingw32-gcc -pipe -fms-extensions -fno-strict-aliasing -nostdinc -mstackrealign -Wold-style-declaration -Wdeclaration-after-statement -fdebug-prefix-map="/home/jbr/reactos/reactos"=ReactOS -gdwarf-2 -gstrict-dwarf -femit-struct-debug-detailed=none -feliminate-unused-debug-symbols -Werror -Wall -Wpointer-arith -Wno-char-subscripts -Wno-multichar -Wno-unused-value -Wno-maybe-uninitialized -O1 -fno-optimize-sibling-calls -fno-omit-frame-pointer -mpreferred-stack-boundary=3 -fno-set-stack-executable  -nostdlib -Wl,--enable-auto-image-base,--disable-auto-import -Wl,--disable-stdcall-fixup   -Wl,--subsystem,native -Wl,-entry,DriverEntry -Wl,--image-base,0x00010000 -Wl,--exclude-all-symbols,-file-alignment=0x1000,-section-alignment=0x1000  drivers/usb/usbd/usbd.def -shared -o drivers/usb/usbd/usbd.sys drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.c.obj drivers/usb/usbd/CMakeFiles/usbd.dir/usbd.rc.res  ntoskrnl/libntoskrnl.a hal/halx86/libhal.a -lgcc

Now as you can imagine this is only part of the story. I won’t be putting too much effort into making a proper 64-bit RosBE for Ubuntu/Debian-based platforms (unfortunately), but I’m sure that with some grepping and additional configuration files one should be able to get quite far to merging these steps in a proper way into the RosBE system.

For the remainder of this blogpost and the compilation of arbitrary 64-bit Windows kernel drivers we’re going to need two "checkouts" of the same output-MinGW-i386 directory. Please obtain these as follows.

  • Create one checkout (i.e., with the ./configure.sh command).
  • Apply all of the changes discussed above.
  • Rename this checkout to output-MinGW-x86_64.
  • Create a new checkout.
  • Run just ninja ntoskrnl hal usbd xdk in this second checkout.

Compiling zer0m0n

Getting back to the goal of this blogpost, we’re now going to take a look at compiling 32-bit and 64-bit zer0m0n.sys Windows kernel drivers. Naturally switching from one compiler to another requires some code changes, especially since GCC is a stricter than MSVC and thus raises more warnings and errors.

In this blogpost we’ll only be focusing on actually building the driver, but we’d just like to point out that MinGW doesn’t support SEH constructs for 64-bit targets as-is and as such, any try/catch blocks will have to be rewritten using, e.g., MmProbeAndLockPages until we find a more appropriate solution such as verifying thrown exceptions ourselves.

Actually, there’s not much to building the 32-bit and 64-bit kernel driver once all of the work has been done already, of course ;-) Following is a working Makefile for building a somewhat modified zer0m0n - in the sense that all the GCC warnings and errors have been resolved and that the SEH usage has been replaced by MmProbeAndLockPages logic.

The Makefile has a couple of dependencies, but other than that works out of the box.

  • The .def files are located in the defs/ directory and for each target two variants will be required, i.e., dummy-x86.def and dummy-x64.def.
  • Two checkouts of the ReactOS source are required, as per the previous chapter of this blogpost. By default the Makefile will assume these checkouts remain in the ~/reactos/ directory, but one may decide to modify the Makefile, provide the $REACTOS environment variable, or simply create a symbolic link at ~/reactos.

And that’s all. Happy Windows kernel hacking from Ubuntu/Debian. Stay tuned for more releases and blogposts!

# Only set if it does not already exist as environment variable.
REACTOS ?= ~/reactos

REACTOS86 = $(REACTOS)/output-MinGW-i386/reactos
REACTOS64 = $(REACTOS)/output-MinGW-x86_64/reactos

SRC = $(wildcard *.c)
DEFS86 = $(wildcard defs/*-x86.def)
DEFS64 = $(wildcard defs/*-x64.def)
LIBS86 = $(DEFS86:defs/%.def=obj/%.a)
LIBS64 = $(DEFS64:defs/%.def=obj/%.a)
ZER0M0NSYS = bin/zer0m0n-x86.sys bin/zer0m0n-x64.sys

OBJ86 = $(SRC:%.c=obj/%-x86.obj)
OBJ64 = $(SRC:%.c=obj/%-x64.obj)
HEADERS = $(wildcard *.h)

    -D_SETUPAPI_VER=0x601 -D_WIN32_IE=0x601 -D_WIN32_WINDOWS=0x601 \
    -D_WIN32_WINNT=0x601 -D__REACTOS__ -D_inline=__inline \
    -Wa,--compress-debug-sections -pipe -fms-extensions -fno-strict-aliasing \
    -nostdinc -mstackrealign -Wold-style-declaration \
    -Wdeclaration-after-statement -gdwarf-2 -gstrict-dwarf \
    -femit-struct-debug-detailed=none -feliminate-unused-debug-symbols \
    -Werror -Wall -Wpointer-arith -Wno-char-subscripts -Wno-multichar \
    -Wno-unused-value -Wno-maybe-uninitialized -O1 \
    -fno-optimize-sibling-calls -fno-omit-frame-pointer \
    -fno-set-stack-executable -Winvalid-pch -Werror=invalid-pch \
    -DNTDDI_VERSION=NTDDI_WIN7 -std=c99 -pie

ifdef DEBUG

CFLAGS86 = -march=pentium -mtune=i686 -mpreferred-stack-boundary=3

    -nostdlib -Wl,--enable-auto-image-base,--disable-auto-import \
    -Wl,--disable-stdcall-fixup -Wl,--subsystem,native \
    -Wl,--image-base,0x00010000 -shared -pie -fPIE \

    -I $(REACTOS86)/drivers/usb/usbd -I $(REACTOS)/drivers/usb/usbd \
    -I $(REACTOS)/sdk/include -I $(REACTOS)/sdk/include/psdk \
    -I $(REACTOS)/sdk/include/dxsdk -I $(REACTOS86)/sdk/include \
    -I $(REACTOS86)/sdk/include/psdk -I $(REACTOS86)/sdk/include/dxsdk \
    -I $(REACTOS86)/sdk/include/ddk -I $(REACTOS86)/sdk/include/reactos \
    -I $(REACTOS)/sdk/include/crt -I $(REACTOS)/sdk/include/ddk \
    -I $(REACTOS)/sdk/include/ndk -I $(REACTOS)/sdk/include/reactos \
    -I $(REACTOS)/sdk/include/reactos/libs

    -I $(REACTOS64)/drivers/usb/usbd -I $(REACTOS)/drivers/usb/usbd \
    -I $(REACTOS)/sdk/include -I $(REACTOS)/sdk/include/psdk \
    -I $(REACTOS)/sdk/include/dxsdk -I $(REACTOS64)/sdk/include \
    -I $(REACTOS64)/sdk/include/psdk -I $(REACTOS64)/sdk/include/dxsdk \
    -I $(REACTOS64)/sdk/include/ddk -I $(REACTOS64)/sdk/include/reactos \
    -I $(REACTOS)/sdk/include/crt -I $(REACTOS)/sdk/include/ddk \
    -I $(REACTOS)/sdk/include/ndk -I $(REACTOS)/sdk/include/reactos \
    -I $(REACTOS)/sdk/include/reactos/libs

all: $(ZER0M0NSYS)

obj/%-x86.a: defs/%-x86.def
    i686-w64-mingw32-dlltool --def $< --kill-at --output-lib=$@

obj/%-x64.a: defs/%-x64.def
    x86_64-w64-mingw32-dlltool --def $< --kill-at --output-lib=$@

obj/%-x86.obj: %.c $(HEADERS) Makefile
    i686-w64-mingw32-gcc $(CFLAGS) $(CFLAGS86) $(INCROSX86) -c -o $@ $<

obj/%-x64.obj: %.c $(HEADERS) Makefile
    x86_64-w64-mingw32-gcc $(CFLAGS) $(CFLAGS64) $(INCROSX64) -c -o $@ $<

bin/zer0m0n-x86.sys: $(OBJ86) $(LIBS86)
    i686-w64-mingw32-gcc $(CFLAGS) $(CFLAGS86) $(LDFLAGS) \
        -Wl,-entry,_DriverEntry@8 -o $@ $^ -static -nostdlib

bin/zer0m0n-x64.sys: $(OBJ64) $(LIBS64)
    x86_64-w64-mingw32-gcc $(CFLAGS) $(CFLAGS64) $(LDFLAGS) \
        -Wl,-entry,DriverEntry -o $@ $^ -static -nostdlib

    rm -rf $(LIBS86) $(LIBS64) $(OBJ86) $(OBJ64) $(ZER0M0NSYS)