Driver in Freebasic


Freebasic is a free, open-source Dialect of Basic. It can produce fast, small and native Executables and DLLs. With no need for explicit declaration(s) of any Win32 API Function(s) followed by full inline ASM support, it turned out to be more reliable, efficient and powerful than other Basic dialects that i had (have) seen so far. With a helpful community available voluntarily available 24x7 for your help, it was a perfect Basic Language for both beginners and experts alike. It soon proved to be more powerful than it actually seemed.
I found a Sample Kernel Mode Device Driver in the FBC\examples\windows\ddk\driver\. The Sample Device Driver was written by a Freebasic Member called VoodooAttack.

I wanted to give it a try. I started by taking a look at the Code in Freebasic Driver Sample FBC\examples\windows\ddk\driver\DRIVER.BAS. The Code is:

'' NT driver example, written by voodooattack
   
    #include once "win\ddk\winddk.bi"
   
    #undef fb_RtInit
   
    declare function DriverEntry stdcall alias "DriverEntry" (byval pDriverObject as PDRIVER_OBJECT, _
                                                      byval pRegistryPath as PUNICODE_STRING) as NTSTATUS
   
   
   
    static shared dev_name as wstring ptr = @wstr("\Device\FBExample")
    static shared dev_dos_name as wstring ptr = @wstr("\DosDevices\FBExample")
   
    declare sub fb_RtInit stdcall alias "fb_RtInit"()
   
    declare function FBDriver_UnsupportedFunction(byval as PDEVICE_OBJECT, byval as PIRP) as NTSTATUS
    declare function FBDriver_Create(byval as PDEVICE_OBJECT, byval as PIRP) as NTSTATUS
    declare function FBDriver_Close(byval as PDEVICE_OBJECT, byval as PIRP) as NTSTATUS
    declare function FBDriver_IoControl(byval as PDEVICE_OBJECT, byval as PIRP) as NTSTATUS
    declare function FBDriver_Read(byval as PDEVICE_OBJECT, byval as PIRP) as NTSTATUS
    declare function FBDriver_Write(byval as PDEVICE_OBJECT, byval as PIRP) as NTSTATUS
    declare sub FBDriver_Unload(byval as PDRIVER_OBJECT)
   
   
    function DriverEntry(byval pDriverObject as PDRIVER_OBJECT, _
                         byval pRegistryPath as PUNICODE_STRING) as NTSTATUS
       
        dim Status as NTSTATUS = STATUS_SUCCESS
        dim pDeviceObject as PDEVICE_OBJECT = NULL
       
        dim as UNICODE_STRING usDriverName, usDosDeviceName
        dim i as integer
       
        DbgPrint(@!"FBExample: DriverEntry Called \r\n")
       
        RtlInitUnicodeString(@usDriverName, dev_name)
        RtlInitUnicodeString(@usDosDeviceName, dev_dos_name)
   
        Status = IoCreateDevice(pDriverObject, _
                                0, _
                                @usDriverName, _
                                FILE_DEVICE_UNKNOWN, _
                                FILE_DEVICE_SECURE_OPEN, _
                                FALSE, _
                                @pDeviceObject)
       

        if (Status = STATUS_SUCCESS) then
           
            DbgPrint(@!"FBExample: Device created \r\n")
           
            for i = 0 to IRP_MJ_MAXIMUM_FUNCTION - 1
                pDriverObject->MajorFunction(i) = @FBDriver_UnsupportedFunction
            next
           
            with *pDriverObject
                .MajorFunction(IRP_MJ_CLOSE)  = @FBDriver_Close
                .MajorFunction(IRP_MJ_CREATE) = @FBDriver_Create
                .MajorFunction(IRP_MJ_DEVICE_CONTROL) = @FBDriver_IoControl
                .MajorFunction(IRP_MJ_READ) = @FBDriver_Read
                .MajorFunction(IRP_MJ_WRITE) = @FBDriver_Write
                .DriverUnload = @FBDriver_Unload
                .Flags or= DO_DIRECT_IO
                .Flags and= NOT (DO_DEVICE_INITIALIZING)
            end with
           
            IoCreateSymbolicLink(@usDosDeviceName, @usDriverName)
        else
            DbgPrint(@!"FBExample: Error creating device \r\n")
        end if
       
        return Status
       
    end function
   
    sub fb_RtInit()
        DbgPrint(@!"FBExample: fb_RtInit Called \r\n")
        return
    end sub
   
    declare function KeTickCount stdcall alias "KeTickCount" () as PLARGE_INTEGER
   
    function KeTickCount () as LARGE_INTEGER ptr
        static as LARGE_INTEGER c
        KeQueryTickCount(@c)
        return @c
    end function
   
    sub FBDriver_Unload(byval DriverObject as PDRIVER_OBJECT)
       
        dim usDosDeviceName as UNICODE_STRING
       
        DbgPrint(@!"FBExample: unloading.. \r\n")
       
        RtlInitUnicodeString(@usDosDeviceName, dev_dos_name)
       
        IoDeleteSymbolicLink(@usDosDeviceName)
        IoDeleteDevice(DriverObject->DeviceObject)
       
    end sub
   
    function FBDriver_UnsupportedFunction(byval DeviceObject as PDEVICE_OBJECT, byval Irp as PIRP) as NTSTATUS
        DbgPrint(@!"FBExample: Unsupported Function \r\n")
        return STATUS_NOT_SUPPORTED
    end function
   
    function FBDriver_Create(byval DeviceObject as PDEVICE_OBJECT, byval Irp as PIRP) as NTSTATUS
        DbgPrint(@!"FBExample: FBDriver_Create \r\n")
        return STATUS_SUCCESS
    end function

    function FBDriver_Close(byval DeviceObject as PDEVICE_OBJECT, byval Irp as PIRP) as NTSTATUS
        DbgPrint(@!"FBExample: FBDriver_Close \r\n")
        return STATUS_SUCCESS
    end function

    function FBDriver_IoControl(byval DeviceObject as PDEVICE_OBJECT, byval Irp as PIRP) as NTSTATUS
        DbgPrint(@!"FBExample: FBDriver_IoControl \r\n")
        return STATUS_SUCCESS
    end function

    function FBDriver_Read(byval DeviceObject as PDEVICE_OBJECT, byval Irp as PIRP) as NTSTATUS
        DbgPrint(@!"FBExample: FBDriver_Read \r\n")
        return STATUS_SUCCESS
    end function

    function FBDriver_Write(byval DeviceObject as PDEVICE_OBJECT, byval Irp as PIRP) as NTSTATUS
        DbgPrint(@!"FBExample: FBDriver_Write \r\n")
        return STATUS_SUCCESS
    end function

The Command in FBC\examples\windows\ddk\driver\MAKE.BAT. It has(d) the following command:

@echo off         
set drvname=driver

fbc %drvname%.bas -c

link %drvname%.o /DRIVER /align:0x80 /FULLBUILD /base:0x10000 /release /osversion:5.1 /version:5.1 /OPT:ICF /OPT:REF /SECTION:INIT,d /MERGE:_PAGE=PAGE /MERGE:.data=PAGE /MERGE:.ctors=PAGE /MERGE:_INIT=INIT /MERGE:_TEXT=.text /subsystem:native,5.01 /entry:GsDriverEntry@8 bufferoverflowk.lib ntoskrnl.lib /OUT:%drvname%.sys

Just in case you don’t know Command Prompt, @echo off prevents the Command Prompt Commands from showing up in the Console Window. Set drvname sets the Driver name as driver(filename of the Sample Driver). Fbc is the compiler executable belonging to the Freebasic Package. –c compiler switch belongs to FBC.EXE which tells the Compiler to output an Object File(.o).
%drvname%.bas = driver.bas(Filename of the Freebasic Sample Driver)

Now, lets compile, with these commands first and you may get the error:
\inc\win\ddk\winddk.bi(3399) error 4: Duplicated definition in type EXECUTION_STATE As ULONG
Error during Compilation

Now, open \inc\win\ddk\winddk.bi and goto Line 3399 and modify type EXECUTION_STATE As ULONG to ‘ type EXECUTION_STATE As ULONG. This will make the line appear as a comment to the Freebasic Compiler.  Now, try the same command fbc %drvname%.bas –c and you will get driver.o in the Source Folder.
Now, the real process starts here.

The point worth noting here is that the Author of the Sample Driver (VoodooAttack) possibly was calling link (LINK.EXE - the linker which upon passing proper commands would produce an Executable accordingly from an Object File(s)). The Sample Driver was written possibly for Freebasic v0.17b or earlier. And, there is no link.exe now. The latest Freebasic Version available for use at the time of writing this article is Freebasic V0.23 which has ld.exe (\bin\win32\ld.exe). The Other point worth noting is that the current version of ld.exe has no compiler switch (command) called /driver.
Now, how could we build a Device Driver then. I went to the Freebasic forum and started a thread with a question about the LINK.EXE.  

The New command needed to produce a Kernel Mode Device Driver is as follows:
bin\win32\ld -h DRIVER.O -o DRIVER.SYS -shared  -e DriverEntry@8 --image-base 0x1000 --subsystem native -s -l ntoskrnl -L lib\win32

The bin\win32\ld is the path to the LD.EXE File. –h is the path to the Source (Object File), -o is the path to the desired output file, -e defines the Executable’s EntryPoint, --image-base defines the base Address of the Executable –subsystem is to be passed native (Drivers have no Subsystem(s)so pass native) –l  is the path to the Library (LIBPATH), -L is the path to the Directory holding the Library File.

Your Antivirus may complain about the Driver but, it is clean.
Avira AV falsely detects the Driver as a Rootkit


And, there you have it, a KERNEL MODE DEVICE DRIVER written entirely on Freebasic.

Found this post useful ? If so, Click +1 and recommend this site on Google

4 comments:

  1. Hi,

    I followed your example but i got the error message

    bin\win32\ld: warning: cannot find entry symbol DriverEntry@8; defaulting to 000
    02000

    Do u know how to solve this ?

    ReplyDelete
  2. If you found the answer Leo, please let me know.

    ReplyDelete
  3. I get the same warning, Im not sure if it has any effect or not on the entry symbol or not.

    ReplyDelete
  4. Seems that ld.exe of freebasic (probably old version) is buggy.
    Works well when also using MinGW ld.exe

    Sample batch file (where ^ means continuation of line according to John Savill
    of windowsitpro.com):

    path=d:\FreeBasic
    fbc.exe -lang fb -c X.bas

    path=d:\MinGW\bin
    ld.exe -o X.sys X.o -shared ^
    -e _DRIVERENTRY@8 --image-base 0x1000 ^
    --subsystem native -l ntoskrnl -L d:\MinGW\lib

    regards
    DurianKing

    ReplyDelete