n l i t e d

:



Thread Links
next

Creating the Driver Project

📢 PUBLIC Page 1021:21/21 | edit | chip 2018-03-28 17:03:07
Tags: CryptDisk KernelMode

August 18 2017

Building drivers using the standard Win32 project template has a number of advantages over the "official" driver template:

  • Enabling the code browser lets me use auto-completion and automatic background compilation with automatic error highlighting.
  • I can compile individual files without having to rebuild the entire project.
  • Better control over the output directories.
  • It makes it easier to use WinDbg rather than the (broken) Visual Studio kernel debugger.
  • Avoids dealing with the (broken) target provisioning system.

Solution RtClick > Add > New Project. Choose Visual C++ > Windows > Win32 and Win32 Project.

Click "Application Settings" to expose the settings.

  • Select "Windows application"
  • Check "Empty Project"
  • Uncheck SDL

Click "Finish" to create the project.

I need to add a C++ file to enable the C++ properties. RtClick Driver > Source Files and add DrvEntry.cpp. Add just a comment line.

Open the configuration manager and make sure both x86 and x64 are enabled for the project.

Open the properties panel for DrvEntry.cpp Make sure All Configurations and All Platforms are selected. There should be as little variation as possible (ideally none!) between configurations and platforms. The .vcxproj file is a terrible place to manage configurations, use header files instead whenever possible.

Configuration Properties > General

  • Output Directory: $(SolutionDir)Out\Win$(PlatformName)$(Configuration)\
    This will put all the final output files in
    CryptDisk\Out\Winx64Debug
  • Intermediate Directory: $(ProjectDir)Out\Win$(PlatformName)$(Configuration)\
  • TargetName: CryptDriver
  • Target Extension: .sys
  • Whole Program Optimization: No Whole Program Optimization

C/C++

General

  • Additional Include Directories:
    $(ProjectDir);$(SolutionDir)Include;$(WDKPATH)\Include\km;$(WDKPATH)\Include\shared;$(VS12PATH)\VC\include
    WDKPATH is an environment variable that points to the WDK, which is typically found in
    C:\Program Files (x86)\Windows Kits\8.010
    This directory should include the file "SDKManifest.xml"
    VC12PATH is an environment variable that points to the Visual Studion installation. This contains the Common7, SDK, and VC directories.
  • Debug Information Format: Program Database (/Zi)
  • Common Language RunTime Support: No Common Language RunTime Support
  • Treat Warnings as Errors: Yes (/WX)
  • SDL checks: Leave blank
  • Multi-processor Compilation: Yes (/MP)

Optimization

  • Optimization: Full Optimization (/Ox)
    The project should always be built with full optimization, including debug builds. The optimizer will be disabled for individual files using #pragma only while I am debugging that file, then re-enabled before committing the changes. This is easy with two lines at the top of every file (just below the includes):
    //#pragma message(__FILE__": Optimizer disabled.") //#pragma optimize("",off)
    I uncomment both lines (never just one!) when I need to debug the file, step through the un-optimized code (because it is much easier to follow), fix the bug, re-comment both lines, build again and test, then commit the changes. Sticking to this practice will bring the debug and release builds very close to each other, avoiding last-minute surprises in the release builds.
  • Inline Function Expansion: Only _inline (/Ob1)
  • Enable Intrinsic Functions: Yes (/Oi)
  • Favor Size or Speed: Favor fast code (/Ot)
  • Omit Frame Pointers: No (/Oy-)
  • Whole Program Optimization: No

Preprocessor

  • Preprocessor Definitions: <different options>
    This field should contain as little information as possible, only the defines that are necessary to define a specific build version. Everything else goes into the WdkDefines.h file. Leave this for now, I will deal with it later.
  • Ignore Standard Include Paths: Yes (/X)
    We need to use only the APIs from the WDK.

Code Generation

  • Enable String Pooling: Yes (/GF)
  • Enable Minimal Rebuild: No (/Gm-)
  • Enable C++ Exceptions: No
    Windows does support C++ exceptions in the kernel. Whether to use try/except in the kernel is a religious argument. I prefer to use extensive error handling and let the kernel crash during debugging.
  • Basic Runtime Checks: Default
    Runtime checks and full optimization are mutually exclusive. I prefer to have the optimizer enabled during debug builds so that my debug code more closely resembles my release builds. Relying too heavily on runtime checks and never debugging optimized code is why so many projects end up shipping their debug builds.
  • Runtime Library: Multi-threaded (/MT)
    The driver won't actually link with the default libraries, but there is a lot of overlap.
  • Security Check: Disable Security Check (/GS-)
    Security checks and full optimization are mutually exclusive.
  • Enable Function-Level Linking: Yes (/Gy)
  • Enable Parallel Code Generation: Yes (/Qpar)

Language

  • Enable Run-Time Type Information: No (/GR-)

Trying to use run-time type information will result in link error like:
1>Device.obj : error LNK2001: unresolved external symbol "const type_info::`vftable'" (??_7type_info@@6B@)

Precompiled Headers

  • Precompiled Header: Not using Precompiled Headers
    Precompiled headers are a symptom of putting too much code in header files where doesn't belong.

Output Files

  • Program Database File Name: $(OutDir)$(TargetName).pdb
    The PDB should be located in the same directory as the driver binary so it can be easily found for debugging.

Browse Information

  • Enable Browse Information: Yes (/FR)

Advanced

  • Calling Convention: __stdcall (/Gz)
    The NT kernel uses stdcall by default. This can be left as __cdecl if desired.
  • Disable Specific Warnings: 4996;4200;4091
    • 4996: Allow strncpy()
    • 4200: Allow zero-length arrays.
    • 4091: 'typedef': ignored on left of 'type' when no variable is declared.
      This suppresses warnings generated in the 8.1 version of ntddscsi.h.
  • Forced Include File:
    $(ProjectDir)WdkDefines.h;$(SolutionDir)Include\SlnDefines.h
    I put all my WDK flags in WdkDefines.h. It is easier to manage a file than settings buried inside the project definition.

WDKDefines.h

Create a new header file named WDKDefines.h which will contain the defines used to build the driver. Since this is really more of a build configuration file than a source code file, I like to put it into the "Build" folder rather than "Source Files" or "Header Files".

WDKDefines.h: /*************************************************************************/ /** WinDefines.h: Compiler flags that must be defined before including **/ /** any other files. **/ /** (C)2013 nlited systems inc, Chip Doran **/ /*************************************************************************/ #ifndef __WINDEFINES_H__ #define __WINDEFINES_H__ 0x0101 // See ReadMe.txt for notes about the build environment. #ifdef _NTDDK_ #pragma error(__FILE__ " must be included BEFORE NtDDK.h !!") #endif //These are the WDK platform configuration flags. //Normally these are passed to the compiler as command //line flags (ie /DKERNEL). This creates a real mess //as critical information is buried in the makefile, //project specification (.vcxproj), or (worst of all!) //configuration setup scripts. To make things //even worse, all but three of these flags are common to //all platforms! //A much better solution is to put these defines in a //source file that can include comments (such as these). //The trick is to make sure this file is included before //any others (especially NtDDK.h). This is accomplished //by setting (for all configurations and platforms) the //project option: // C/C++ > Advanced > Force Include file = $(ProjectDir)WinDefines.h #define KERNEL 1 #define _KERNEL_MODE 1 //Required for Win10 DDK #define _WINDOWS 1 #ifdef DEBUG //#define DBGOUT 1 #endif //How thorough is IsBadPtr() ? //0: Check for NULL //1: Probe physical page #define ISBADPTR 1 //Calling conventions #define STD_CALL 1 #define CONDITIONAL_HANDLING #define DEVL 1 #define FPO 1 #define _IDWBUILD 1 //Windows target version: 8.1 (WINBLUE) #define _WIN32_WINNT 0x0603 //Win81 #define NTDDI_VERSION 0x06030000 //Win81 driver interface #define WIN32_LEAN_AND_MEAN 1 //Discard the frills and laces. #define NT_UP 1 #define NT_INST 0 //#define WIN32 100 #define _NT1X 100 #define WINNT 1 #endif //EOF: WINDEFINES.H

SlnDefines.h

Create a new header file named SlnDefines.h in the $(SolutionDir)Include directory. This will contains flags that are universal to all projects in the solution.

SlnDefines.h: /*************************************************************************/ /** SlnDefines.h: CryptDisk solution flags. **/ /** (C)2017 nlited systems, chip doran. **/ /*************************************************************************/ #pragma once #define __SLNDEFINES_H__ #include "Build.h" //Sets DEBUG flags #include "Platform.h" //Sets _X86_ or _AMD64_ //NOTE: DEBUG, _DEBUG, NDEBUG, and _NDEBUG will all be defined as 0 or 1. #ifndef UNICODE #define UNICODE //Unicode for everything. #endif #ifndef _UNICODE #define _UNICODE #endif /*************************************************************************/ /** Flags for kernel DbgPrintf() **/ /*************************************************************************/ #if _DEBUG /*** IMPORTANT *** IMPORTANT *** IMPORTANT *** IMPORTANT *** IMPORANT *** *** **************************************************************** *** *** !!!!!! DO NOT SHIP DRIVERS THAT PRINT TO ERROR !!!!!! *** *** !!!!!! This is a debug HACK to get around the unfortunate !!!!!! *** *** !!!!!! situation that every driver uses the same filter. !!!!!! *** *** !!!!!! This is why no one should ever ship a debug build. !!!!!! *** *** **************************************************************** *** *** IMPORTANT *** IMPORTANT *** IMPORTANT *** IMPORTANT *** IMPORANT ***/ // In DEBUG builds, print everything at ERROR level. #define PRINT_LEVEL 0 //DPFLTR_ERROR_LEVEL #define DO_BREAK 1 //Enable DbgBreakPoint() #else #define PRINT_LEVEL 3 //DPFLTR_INFO_LEVEL #define DO_BREAK 0 //Disable DbgBreakPoint() #endif #define PRINT_ID 77 //DPFLTR_IHVDRIVER_ID /************************************************************************* To see text from DbgPrint in WinDbg, I need to enable the filters. On the target machine, add the registry values: HKEY_LOCAL_MACHINE\ SYSTEM\ CurrentControlSet\ Control\ Session Manager\ Debug Print Filter\ DEFAULT = DWORD 0xFF IHVDRIVER = DWORD 0xFF Beware: Enabling anything other than ERROR unleashes a flood of text from every driver in the system. See the docs for DbgPrintEx() for more detail. *************************************************************************/ //EOF: SLNDEFINES.H

Compile

I should now be able to compile the empty DrvEntry.cpp file.

DriverEntry

Add some actual driver code:

DriverEntry: /*************************************************************************/ /** DrvEntry.c: Windows kernel driver entry point. **/ /** (C)2017 nlited systems inc, Chip Doran **/ /*************************************************************************/ #include "Globals.h" #pragma message(__FILE__": Optimizer disabled.") #pragma optimize("",off) DRIVER_INITIALIZE DriverEntry; static void DriverNtUnload(DRIVER_OBJECT *pDriver); NTSTATUS DriverEntry(DRIVER_OBJECT *pDriver, UNICODE_STRING *RegPath) { int Err= 0; //NOTE: By default, only error messages are printed by Windows. DbgPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_ERROR_LEVEL,"CryptDriver has arrived! [%llX]\r\n",DriverEntry); if(KD_DEBUGGER_ENABLED && !KD_DEBUGGER_NOT_PRESENT) DbgBreakPoint(); pDriver->DriverUnload= DriverNtUnload; return(STATUS_SUCCESS); } static void DriverNtUnload(DRIVER_OBJECT *pDriver) { DbgPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_ERROR_LEVEL,"CryptDriver has left the building.\r\n"); } //EOF: DRVENTRY.C

This does nothing but print a "Hello, world!" message and set the unload function. I am hijacking the IHV filter driver's device ID to make sure my message is printed without requiring any registry changes.

Globals.h: /*************************************************************************/ /** Globals.h: Global declarations for the CryptDisk kernel driver. **/ /** (C)2017 nlited systems inc, Chip Doran **/ /*************************************************************************/ #pragma once #define __WINK_H__ #include <NtDDK.h> #include <WinDef.h> #include "StdTypes.h"

There was a time when I was opposed to embedded #include's inside header files, but I have changed my mind with the recent versions of Visual Studio. Embedding the includes allows VS to compile on the fly while editting the header files, so it can flag undefined names and other errors. This is very handy.

If I try to compile now, I get an error
"No Target Architecture"
This is because the WDK needs more specific architecture names than the standard Windows applications. I need to go back and set the flags for each build variation separately.

UPDATE: A better solution is to move the defines to Build.h and Platform.h, then use the $(Configuration) and $(PlatformName) build macros to select which versions are used. This doesn't become apparent until very late in project lifecycle management. Versioning

  • Debug/Win32: _X86_;_DEBUG
  • Release/Win32: _X86_;NDEBUG
  • Debug/x64: _X64_;_AMD64_;_DEBUG
  • Release/x64: _X64_;_AMD64_;NDEBUG

I should now be able to successfully compile the DrvEntry.cpp file.

Linking

Open the Driver project's property panel and expand the Linker item.

General

  • Output File: $(OutDir)$(TargetName)$(TargetExt)
  • Enable Incremental Linking: No (/INCREMENTAL:NO)
  • Additional Library Directories: $(WDKPATH)\lib\win8\km\x64
  • Prevent Dll Binding: No (/ALLOWBIND:NO)
  • Treat Linker Warning as Errors: YES (/WX)

Note that the "Additional Library Directories" parameter is specific to the build and needs to be set to different values for the x86 and x64 platform.

Input

  • Additional Dependencies: ntoskrnl.lib;hal.lib;BufferOverflowK.lib
  • Ignore All Default Libraries: Yes (/NODEFAULTLIB)

Manifest File

  • Generate Manifest: No (/MANIFEST:NO)

Debugging

  • Generate Debug Info: true
  • Generate Program Database File: $(OutDir)$(TargetName).pdb
    This should match the setting from C/C++ > Output Files
  • Generate Map File: Yes (/MAP)
  • Map File Name: $(OutDir)$(TargetName).map
  • Map Exports: Yes (/MAPINFO:EXPORTS)

System

  • SubSystem: Native (/SUBSYSTEM:NATIVE)
  • Driver: Driver (/Driver)

Advanced

  • Entry Point: DriverEntry

I should now be able to build the Driver project.

The driver can't do anything yet, I don't even have a way to load it. Drivers need a lot of support...



close comments Comments are closed.

Comments are moderated. Anonymous comments are not visible to other users until approved. The content of comments remains the intellectual property of the poster. Comments may be removed or reused (but not modified) by this site at any time without notice.

  1. [] ok delete


Page rendered by tikope in 307.022ms