Как загрузить kernel driver

Обновлено: 04.07.2024

Last Achievements

Thank you for your answer. But now I have some other problems. There is a mistake here. What is the reason?

win10 20H2 19042.685

You probably need to update signatures / offsets, I suppose the original code is made for other version than 20H2 Last Achievements

Thank you for your answer. But now I have some other problems. There is a mistake here. What is the reason?

win10 20H2 19042.685

what mapper are you using?

Originally Posted by montroisiemecon You probably need to update signatures / offsets, I suppose the original code is made for other version than 20H2

This supports 1803-20h2, but the cleaning will fail if the driver name, and time stamp and not found and changed. Most likely OP was not using kdmapper, which would cause iqvw64e.sys to not be found.

and change the info accordingly.
make sure you also change the time stamps

Last edited by unmaiwae; 3rd January 2021 at 07:54 AM . Last Achievements

what mapper are you using?

This supports 1803-20h2, but the cleaning will fail if the driver name, and time stamp and not found and changed. Most likely OP was not using kdmapper, which would cause iqvw64e.sys to not be found.

and change the info accordingly.
make sure you also change the time stamps

kdmapper , and It can print MmUnloadedDriversInstr

Last Achievements Last Achievements Last Achievements

weird its cleaning correctly. give me a second to review the code.

Oh. easy fix. When writing this I guess I forgot the diffrent between NTSTATUS and a boolean.

Last edited by unmaiwae; 3rd January 2021 at 08:53 AM . Last Achievements Last Achievements


Last Achievements Last Achievements

Ill just cut strait to the chase.

Over the last week I have been detection testing and got really bored so I thought I would add something to my arsenal, something I will not use but could be useful to others.

This alone would be detected, but adding a simple thread hiding system is all that is needed. I have including a template for thread hiding, but I leave that up to the reader to do.

This is not my best work by far, since I did do it overnight; I would love some constructive criticism. Thanks

This drivers features includes:
- read
- write
- get base address
- get PID
- get peprocess
- clean mmu
- clean piddb
- io system

credits:
@xoe for mmu/piddb cleaning base (heavily modified)
@r1chy for his getting PEPROCESS by process name

Last Achievements Windows kernel driver tutorial (setup, coding, remarks)

Initially the post did not have any HTML file, is was wrote raw. I changed the style in order to improve the quality of the tutorial. If for any reason you want to preview the deprecated article, click the spoiler below

Recently I became able to write kernel drivers and found out that the bits of information required to successfully setup / write / debug / inspect are quite scattered around the internet. I'm making this tutorial with the scope to be self-sufficient and our newbies be able to start their kernel journey from a single place rather than looking over multiple guides at once. The tutorial will be split into 3 parts: setting up, coding itself, and Q&A. I will also provide solutions to common problems. The power of this post does not rely on the coding section, but setup and Q&A, as I will show tricks and pieces of code. Maybe is worth to look at the last section even if u're a senior in this field. This is my first tutorial made on this forum. I accept criticism, but please don't be rude

This part is the most variable. We are gonna try to make a debugging environment and get running some tools. U're free to skip this part if u want to use a different kind of setup than one I will mention in just a bit. The other 2 big parts of the tutorial are not dependent on this one

I will present and use the following: Win 10 x64, VS2019, VirtualBox

Developing a kernel is not a trivial task, and that's not only about itself being a new domain for u, but the dumb labor which takes time, as with every crash of ur program, the system may crash too, requiring a reboot. We don't want this to happen to our developing system. We should setup a secondary machine to carry out our driver execution. I prefer using a virtual machine as it is easy to work with (compared to alternatives such as another computer)

We need VirtualBox installed on the host machine and an iso with Win 10 x64 itself. Open VirtualBox and on the screen u should see a "New" button. This creates a new machine. By entering the advanced mode u shall see the following window:

Name whatever u want. As version select Win 10 x64 (or another coresponding to ur iso). "Memory size" is the amount of RAM we want to allocate. On the current host I have 8 GB, and my previous target was 2 GB. With this setup there shouldn't be any problem as the RAM won't get filled. The VM takes

1 GB, so it has 1 more for spare. After creating it, double click it. A prompt will ask u for the iso, as the VirtualBox cannot boot. Insert it and install Windows

While our VM installs we can setup tools that are gonna be used on our host machine. As I already mentioned, we will use VS2019. Probably u already have it configured to user-mode C++. Open "Visual Studio Installer", click "Modify", go to "Individual Components" and make sure to have checked "Windows 10 SDK (10.0.19041.0)", "Windows Driver Kit" and "x64/x86 Spectre-mitigated libs (v14.26)". If u just selected them, click install button on the bottom right

Target machine setup

After VS2019 has installed we can continue to configure our vm. The connection I will make will be via Serial, not Network. Pop up an admin cmd (in vm) and type the following:

Now we need to setup a registry key for "DbgPrint()" to work (I will explain later in code section). Go to "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter", create a dword named "DEFAULT" and set it to "0x8" (make sure hex is checked). On the host machine go to a path similar to "C:\Program Files (x86)\Windows Kits\10\Remote\x64", copy "WDK Test Target Setup x64-x64_en-us.msi" to the target machine and install it

While we are at it, download and install the redistributables. 2015, 2017 and 2019 are all the same. Go for 2019. We also need a loader for our driver. I will go with OSRLOADER. Download and install it

After all installs are done shutdown the machine. Select it in VirtualBox and go to Settings. Make sure that all settings in "Serial Ports" are the same like in the image below:

U should also under "Network" make sure the connection is "Bridged" and not "NAT". It is not a must, but a plus. Boot up the machine. It is ready now

Our host machine is ready too, except the driver project shall be made. When choosing the type of the project, match it with the one selected in the image:

After creating, what I do is lowering the warning level. This should be used mainly for testing, and when u plan porting the driver for real usage, turn it back to 4. The option could be found in the project properties:

If u can't see all options from the left bar, make sure the "Platform" is not "arm", so select "x64", "x86", or both, but not all. When we are gonna debug the driver, we go to "Debug" > "Attach to Process". It should look like mine:

"New Computer" won't appear first time. We need to add it, which is the vm we just made earlier. Go to "Find" > "Add New Device", put any display name, and as network name use the ip of the vm. U can get it by using "ipconfig" command inside cmd inside vm. Select "Manually configure. " and click "Next". Now configure the settings like in the image:

The project is done. Everytime u wanna debug the driver, u're gonna use the "Attach" button after u selected the newly created device. It will work as follow:
- type code
- build the project (not debug / run)
- copy to vm (OSRLOADER doesn't like it in a shared folder)
- attach to device (after attach u should press "Break all" in VS (the pause button), wait, and then "Continue"; it's not a must, but until u know why it shall be done, just do it, otherwise problems might appear)
- load driver

In order to drag and drop, have shared folders, etc, u must not forget to install VirtualBox "user additions". After that, settings such as sharing a folder between both machines shall work

Like in any other program, we should have a main function that will run at the beginning. For drivers we use "DriverEntry":

"DbgPrint()" is used to display stuff into debug console (the one that appears after u attach to the vm). We must have the registry set to work (another methods exists, but I find this one easiest). "RtlInitUnicodeString()" just initializes our "UNICODE_STRING"s. "IoCreateDevice()" is used for I/O. "IoCreateSymbolicLink()" shall be used to register the symbols made earlier

These lines tells the driver what to do in case of specific events. All the 4 values from the right side of "=" are function that we are gonna define in just a moment:

For now these 2 are useful for debugging. We will see messages in the console

"unload()" is called when we stop the driver. There we could deallocate resources and cleanup. We are cleaning now the symbols we registered

"io()" is called everytime we get something from the userland. Example: request code and input data. I will show were they are inserted when we reach the code from user. This is how we can retrieve them:

Using them we know what the caller wanted the driver to do, such as reading something from memory. "input" is a "void*", so from userland u can pass anything

Sometimes we want to respond back to the user. In this case we use:

These 2 shall be before "IoCompleteRequest(irp, IO_NO_INCREMENT);". Don't forget to use this one too:

That's pretty much all for the driver. Put a breakpoint inside "io()". We will test it later

We create now another project, a normal one. Before sending requests we shall open a handle to the driver:

"DriverName" shall be the same with the one u put inside the driver code when u created the symbol. We can now make a request to the handle using:

The "code" u shall pass in came from the "CTL_CODE" macro, like:

The raw code is "0x899" in this case. MSDN recommends to use above "0x800"

"in" is the input of the request. It can be anything, or "nullptr". "out" is where the response from driver (if it exists) shall be stored. It can be "nullptr" too. Notice that even if we pass addresses, they are only used for raw memory copy. If u want to "pass by pointer", then "&in" shall be of type double pointer, and "sizeof()" would return 8 bytes. Same for "out"

It's not bad to also put this at the end of the code:

Now build it as "Release" and send it to vm. Same with the driver, but as "Debug". Attach to vm, load the driver, notice the messages, and execute the userland code. It should trigger the breakpoint we put before in the driver code

Last Achievements

Important:
To start off, this thread are for people who are just trying to learn more about ring 0 bypasses. i will provide basic information about drivers and how they exactly work. after reading this thread, you should get a better idea on how ring 0 bypass works and how to make a simple driver. For people who already are good in kernel, then feel free to correct me, any way you like. i am just a person who had a chance to play around with drivers and develop my own Anti Cheat to test and see how normal Anti Cheats works. one more thing, without daax, i dont think i will where i am at. He really taught me alot of things which will be useful.

Make sure to leave a +rep if this helped!

What Are Drivers?
To start off, one major difference between Drivers and normal programs is that Drivers cannot display any types of overlay or user interfaces. To have a overlay, you will need to make your own overlay in usermode and communicate with the driver . Drivers also cannot be loaded if the user isnt admin and if the driver doesnt have a cert ( test mode is the only way or disable patch guard ). The other thing about drivers is that, if it crashes, thats it, your PC will crash with it. Drivers also ran in Kernel mode rather then usermode like regular programs, giving access to kernel APIs. Drivers also have full access to the system, meaning you can do pretty much anything with no-one really stopping you. Anti Viruses does have measures against Rottkits ( viruses in the form of drivers ), Windows also have implemented a feature called DSE which i will talk about later on. Cool features that Drivers can do is hiding processes from pretty much everything, Drivers also can monitor callbacks such as creating Handles and so on. Driver also have full access to pretty much everything

How Does Anti Cheats Use Their Drivers?
Anti Cheats uses ObRegisterCallback which basically allows the Anti Cheat to monitor any Handle creations, they also have two actions against this, Pre (before) and Post (after). Normally what they do is strip handle permissions to their game in their Pre function, stripping handles is as easy as these lines:

These lines of code will replace every Handle created ( not including lsass, csrss, the game itself and the current process ) permissions by PROCESS_QUERY_LIMITED_INFORMATION and SYNCHRONIZE. This will not allow any programs to RPM and WPM unless they abuse lsass or other white-listed programs. This function also strips handle permissions even before they are really created, therefor being named PreCallback. In their PostCallback, they might most likely log what happened and so on.

Bypass Against ObRegisterCallbacks?
There are afew ways to basically bypass this, things like unregistering the callback. You do have to think about afew things before doing this. Anti Cheat can check if their callback is unreigstered by just registering their callback again, two things can happen if they do that, one is that they get a STATUS_FLT_INSTANCE_ALTITUDE_COLLISION, meaning that a collision would indicate that the callback was still in place. However the other thing that can happen is returning success when registering again, this means that the callback was unregistered. Anti Cheat can do somethings about this. The other way to bypass would be just abusing the programs they white-listed. Programs such as lsass, csrss and others. One note to take is that lsass may not always be free to use, in my time of making an Anti Cheat to better learn, i have noticed that you can strip handle permissions from lsass without BSOD. Meaning, dont always thing lsass is a easy bypass. The other downside to this is that, the Anti Cheat can monitor the white-listed programs, Anti Cheats such as Battleye monitors their white-listed programs, which makes it harder to really use this bypass.

Coding A Driver?
Well, everyone who is willing to put in effort and such are able to make a Driver. Its not as hard to make a simple Driver. Advanced Drivers do require alot of experience, but the only way to learn is to test and try. Failure will always catch up and in those times, dont give up. I almost gave up afew times when making my own Drivers, its not easy but if you are willing to put in the effort and such, anything is possible. The only reason why i am here is because of people such as daax, wlan and such helping me out. Other then that, lets start!


What You Need:
MicroSoft Visual Studio
Windows Driver Kit
C++ Knowledge

Driver's Main:
Drivers also has a Main function like every other programs, but its slightly different i would say. Driver's Main Function is called "DriverEntry", this function will be basically your entry point of your driver.

Identifying The Driver:
Drivers also need a SymbolName and a Name. This can easily done by afew lines of code. By using IoCreateDevice and IoCreateSymbolicLink.

This would Name the driver "Driver1" and give a Symbol Name of "Driver1".

Unload Driver:
You will need a function for Unloading. Without This function, the Driver will have no way to unload, which would be quite annoying. We will be using two simple fucntions which just deletes the Driver's Symbol Name and The Driver Itself.

Now once this is done, you will have to tell the Driver what the Unload function is.

This would be basically called when unloading the driver.

Communication With Usermode Program:
To start off, we will need a Create Call and a Close Call Functions, these functions will not do much for now, but its a good practice to always do it.

Now after that is done, we need to somewhat tell the driver these are our close and create functions.

Alrite, now moving on to the main function to communicate with the driver and back.

Lets make a function for it and lets name it "IoControl". This will control all inputs and outputs. Lets make it empty for now and tell the Driver that this is our Input and Output Control functions.

Telling the Driver

Alrite once this is done, lets move on to filling in the IOControl. We will be basically using a Code for each communication. Lets say, you want the Driver to write some part of memory, then you can use a code which will be sent to the Driver and the Driver would know what you want. So if i set the Write Request code to 0x0814, then in the usermode i will have to use that code if i wanted the Driver to write. Same for Read and so on.

Firstly lets define the codes

One more note, you can change the code if you like. I will just use 0x812 and 0x813 for this example.

Alrite once that is done, lets store the received code in a variable for the Driver to check the request and take actions.

This will store the Code received from usermode.

Lets setup the Requestions And Actions, this is quite easy, just setting up if and else statements.

Once this is setup, Lets move on to the Usermode program to setup their communication.

To start off, lets get a handle to the Driver.

Other thing you can do is checking if the Handle is valid or not and if its NULL then doing something about it.

You also need to Define the request codes in the usermode program, this is also quite easy.

Once done, you can pretty much send requests and so on. The way you can send information from usermode is by using a function called DeviceIoControl

Its simple as this

You can make afew other request such as Process ID, where you can send the driver the game's process ID as there is not documented way of getting a game's processID using the name. Unless you loop through the entire Process list and find the name you are looking for.

Debugging:
This is one of the worst moments i went through with this, just because i dont really have a spare PC to use for testing. Nevertheless there are ways to debug with only one PC.

Alrite, lets start off by saying that when you are trying to see whats wrong with your Driver or why is it causing BSOD, the minidump file is important. A minidump is created everytime you get a BSOD. It is usually located under C:\Windows\Minidump. You might ask, well how do i open these files. You will need a program called WinDbg. This program allows you to debug Drivers and view the minidumps. This can be helpful as you will know where the problem is. You are able to view the problem in detail online. The common problem people have are PAGE FAULT IN NON PAGE AREA and CRITICAL_STRUCTURE_CORRUPTION. CRITICAL_STRUCTURE_CORRUPTION is caused by Patch Guard most of the time and PAGE FAULT IN NON PAGE AREA is when you fuck up your Unload.

Other ways to debug your Driver is by using DbgPrintEx or DbgPrint. This together with DebugView can help you out. This will not work if you Driver just gives you BSOD, you will need to use the method above, this method is for people who dont really understand what is happening inside their Driver. You can use this method to view what your variables are storing or what some of the function returns.

The last method you can use is downloading a VMWare or a virtual machine. You can setup your virtual machine, by downloading softwares like VMWare VMBox and other few softwares. From there, you can use
WinDbg and setup a debugger there.

Other Functions:
This is just Extra functions that you might want to add or use. Functions such as shutting down programs in usermode and so on.

Signing Your Driver & How It Works

Introduction:
To start off, why sign your driver? Well, this is quite simple, the reason why people sign their Drivers is mainly to just allow them to normally load their Driver instead of going to Test Mode, disabling DSE or abusing other Drivers. Signing a Driver will also add your Driver to the "Trusted" list. This would basically would make Windows trust your Driver by letting it load normally, plus this will somewhat ensure your customers or such that they are dealing with trusted people.

Cost:
Signing your Driver isn't cheap or even free to start off. It would cost afew hundreds of dollars per year. This plus with other downsides. Paying the price is usually the most reason why people would just not bother signing their Driver and once an Anti Cheat finds that you are making a Cheat using that certificate, then oh boi, you would usually be fucked.

Digital Certificate:
How signing works is that, Microsoft or whoever you bought it from, will send you a "Certificate". This "Certificate" is fully digital, i have seen companies shipping hardware tokens, which are just basically things that hold your digital certificate and a code ( Private key ).

Signing The Driver:
The way you sign the Driver is extremely easy, all you need to use is the developer console for visual studio and basically using one simple command line, called signtool.

Open up your developer console and basically type this line of code. As simple as that.

Well, let me explain what this exactly is. Firstly, the digitalcertificate.cer is the cross certificate that you got, it would be different. Secondly the "sha1" basically tells signtool to find the correct certificate and not use the wrong one, this would be different for everyone. Thirdly, sacdriver.sys, is just the Driver you would like to sign.

You are also allowed to give a short description of your Driver, for me i just added "Sagaan's Anti-Cheat".

How many times can you sign your Driver?

- Any many times as you like

How to get a certificate?

- Search online, there are websites which provides services to this, make sure to read everything two times as sometime it might be not what you are looking for

How to get the developer console?

- Search Developer Command Prompt for VS 2017 or something similar in your taskbar

- Really depends on what you are planning to use the driver for. if its for cheats, then no, if not then most likely.

Useful Sources To Look At!


Wanted To Test Your Cheats?

Well, you can, i will somewhat share my anti cheat which is extremely simple. All it does it strip handles to CSGO. Use this to see exactly how ObRegisterCallback works.



Ядро — это та часть операционной системы, работа которой полностью скрыта от пользователя, т. к. пользователь с ним не работает напрямую: пользователь работает с программами. Но, тем не менее, без ядра невозможна работа ни одной программы, т.е. они без ядра бесполезны. Этот механизм чем-то напоминает отношения официанта и клиента: работа хорошего официанта должна быть практически незаметна для клиента, но без официанта клиент не сможет передать заказ повару, и этот заказ не будет доставлен.
В Linux ядро монолитное, т.е. все его драйвера и подсистемы работают в своем адресном пространстве, отделенном от пользовательского. Сам термин «монолит» говорит о том, что в ядре сконцентрировано всё, и, по логике, ничего не может в него добавляться или удаляться. В случае с ядром Linux — это правда лишь отчасти: ядро Linux может работать в таком режиме, однако, в подавляющем большинстве сборок возможна модификация части кода ядра без его перекомпиляции, и даже без его выгрузки. Это достигается путем загрузки и выгрузки некоторых частей ядра, которые называются модулями. Чаще всего в процессе работы необходимо подключать модули драйверов устройств, поддержки криптографических алгоритмов, сетевых средств, и, чтобы уметь это правильно делать, нужно разбираться в строении ядра и уметь правильно работать с его модулями. Об этом и пойдет речь в этой статье.

В современных ядрах при подключении оборудования модули подключаются автоматически, а это событие обрабатывается демоном udev, который создает соответствующий файл устройства в каталоге "/dev". Все это выполняется в том случае, если соответствующий модуль корректно установлен в дерево модулей. В случае с файловыми системами ситуация та же: при попытке монтирования файловой системы ядро подгружает необходимый модуль автоматически, и выполняет монтирование.
Если необходимость в модуле не на столько очевидна, ядро его не загружает самостоятельно. Например, для поддержки функции шифрования на loop устройстве нужно вручную подгрузить модуль «cryptoloop», а для непосредственного шифрования — модуль алгоритма шифрования, например «blowfish».

Поиск необходимого модуля

Модули хранятся в каталоге "/lib/modules/<версия ядра>" в виде файлов с расширением «ko». Для получения списка всех модулей из дерева можно выполнить команду поиска всех файлов с расширением «ko» в каталоге с модулями текущего ядра:

find /lib/modules/`uname -r` -name ‘*.ko’

Полученный список даст некоторое представление о доступных модулях, их назначении и именах. Например, путь «kernel/drivers/net/wireless/rt2x00/rt73usb.ko» явно указывает на то, что этот модуль — драйвер устройства беспроводной связи на базе чипа rt73. Более детальную информацию о модуле можно получить при помощи команды modinfo:

Загрузка и выгрузка модулей

Загрузить модуль в ядро можно при помощи двух команд: «insmod» и «modprobe», отличающихся друг от друга возможностью просчета и удовлетворения зависимостей. Команда «insmod» загружает конкретный файл с расширением «ko», при этом, если модуль зависит от других модулей, еще не загруженных в ядро, команда выдаст ошибку, и не загрузит модуль. Команда «modprobe» работает только с деревом модулей, и возможна загрузка только оттуда по имени модуля, а не по имени файла. Отсюда следует область применения этих команд: при помощи «insmod» подгружается файл модуля из произвольного места файловой системы (например, пользователь скомпилировал модули и перед переносом в дерево ядра решил проверить его работоспособность), а «modprobe» — для подгрузки уже готовых модулей, включенных в дерево модулей текущей версии ядра. Например, для загрузки модуля ядра «rt73usb» из дерева ядра, включая все зависимости, и отключив аппаратное шифрование, нужно выполнить команду:

После загрузки модуля можно проверить его наличие в списке загруженных в ядро модулей при помощи команды «lsmod»:

Module Size Used by
rt73usb 17305 0
crc_itu_t 999 1 rt73usb
rt2x00usb 5749 1 rt73usb
rt2x00lib 19484 2 rt73usb,rt2x00usb

Из вывода команды ясно, что модуль подгружен, а так же в своей работе использует другие модули.
Чтобы его выгрузить, можно воспользоваться командой «rmmod» или той же командой «modprobe» с ключем "-r". В качестве параметра обоим командам нужно передать только имя модуля. Если модуль не используется, то он будет выгружен, а если используется — будет выдана ошибка, и придется выгружать все модули, которые от него зависят:

Для автоматической загрузки модулей в разных дистрибутивах предусмотрены разные механизмы. Я не буду вдаваться здесь в подробности, они для каждого дистрибутива свои, но один метод загрузки всегда действенен и удобен: при помощи стартовых скриптов. В тех же RedHat системах можно записать команды загрузки модуля прямо в "/etc/rc.d/rc.local" со всеми опциями.
Файлы конфигурация модулей находится в каталоге "/etc/modprobe.d/" и имеют расширение «conf». В этих файлах преимущественно перечисляются альтернативные имена модулей, их параметры, применяемые при их загрузке, а так же черные списки, запрещенные для загрузки. Например, чтобы вышеупомянутый модуль сразу загружался с опцией «nohwcrypt=1» нужно создать файл, в котором записать строку:

options rt73usb nohwcrypt=1

Черный список модулей хранится преимущественно в файле "/etc/modules.d/blacklist.conf" в формате «blacklist <имя модуля>». Используется эта функция для запрета загрузки глючных или конфликтных модулей.

Сборка модуля и добавление его в дерево

Иногда нужного драйвера в ядре нет, поэтому приходится его компилировать вручную. Это так же тот случай, если дополнительное ПО требует добавление своего модуля в ядро, типа vmware, virtualbox или пакет поддержки карт Nvidia. Сам процесс компиляции не отличается от процесса сборки программы, но определенные требования все же есть.
Во первых, нужен компилятор. Обычно установка «gcc» устанавливает все, что нужно для сборки модуля. Если чего-то не хватает — программа сборки об этом скажет, и нужно будет доустановить недостающие пакеты.
Во вторых, нужны заголовочные файлы ядра. Дело в том, что модули ядра всегда собираются вместе с ядром, используя его заголовочные файлы, т.к. любое отклонение и несоответствие версий модуля и загруженного ядра ведет к невозможности загрузить этот модуль в ядро.
Если система работает на базе ядра дистрибутива, то нужно установить пакеты с заголовочными файлами ядра. В большинстве дистрибутивов это пакеты «kernel-headers» и/или «kernel-devel». Для сборки модулей этого будет достаточно. Если ядро собиралось вручную, то эти пакеты не нужны: достаточно сделать символическую ссылку "/usr/src/linux", ссылающуюся на дерево сконфигурированных исходных кодов текущего ядра.
После компиляции модуля на выходе будет получен один или несколько файлов с расширением «ko». Можно попробовать их загрузить при помощи команды «insmod» и протестировать их работу.
Если модули загрузились и работают (или лень вручную подгружать зависимости), нужно их скопировать в дерево модулей текущего ядра, после чего обязательно обновить зависимости модулей командой «depmod». Она пройдется рекурсивно по дереву модулей и запишет все зависимости в файл «modules.dep», который, в последствие, будет анализироваться командой «modprobe». Теперь модули готовы к загрузке командой modprobe и могут загружаться по имени со всеми зависимостями.
Стоит отметить, что при обновлении ядра этот модуль работать не будет. Нужны будут новые заголовочные файлы и потребуется заново пересобрать модуль.

Ошибка «Invalid argument» может говорить о чем угодно, саму ошибку ядро на консоль написать не может, только при помощи функции «printk» записать в системный лог. Посмотрев логи можно уже узнать в чем ошибка:

В этом примере выведена только последняя строка с ошибкой, чтобы не загромаждать статью. Модуль может написать и несколько строк, поэтому лучше выводить полный лог, или хотя бы последние строк десять.
Ошибку уже легко найти: значение «2» неприемлемо для параметра «nohwcrypt». После исправления, модуль корректно загрузится в ядро.

Из всего сказанного можно сделать один вывод: ядро Linux играет по своим правилам и занимается серьезными вещами. Тем не менее — это всего лишь программа, оно, по сути, не сильно отличается от других обычных программ. Понимание того, что ядро не так уж страшно, как кажется, может стать первым шагом к пониманию внутреннего устройства системы и, как результат, поможет быстро и эффективно решать задачи, с которыми сталкивается любой администратор Linux в повседневной работе.


Данная статья нацелена на тех, кто только недавно начал разрабатывать kernel-драйвера под ОС Windows. В 100-ый раз видишь ненавистную надпись IRQL_NOT_LESS_OR_EQUAL и этот грустный смайлик? Тогда прошу пройти под кат.

Одной из основных ошибок, которую я и сам совершал, является жонглирование IRQL так, как душе угодно, и неполное понимание внутреннего устройства работы приоритетов потоков в ядре Windows.

К примеру, у вас есть кусок кода, который генерирует какое-либо событие по PID-процесса.

Внутри данного кода, используются разделяемые данные, синхронизация которых, обеспечивается спинлоком. Также нам нужно получить имя процесса, чтобы залогировать событие.

Уже увидели ошибку в данном фрагменте?

PsLookupProcessByProcessId() – требует соблюдения условия: IRQL <= APC_LEVEL.

Так что, подобный код будет часто выдавать BSOD с кодом ошибки IRQL_NOT_LESS_OR_EQUAL.


И вот дальше, начинается самое интересное. Первое решение, которое придёт в голову новичкам, будет менять уровень IRQL перед вызовом данной функции так, чтобы условие соблюдалось.

То есть, переписать код — вот так:

Вот теперь то, всё работает достаточно стабильно. Но, на самом деле это не так. Данный код только хорошо маскирует проблему, снижая шансы её проявления до минимума, но в 1 из 1000 случаев, она всё же всплывёт, а вы будет рвать на себе волосы, пытаясь понять в чём же ошибка.

И тут нужно вспомнить одно из правил написания драйверов, а именно:
«Понижать IRQL можно только в том случае, если вы его собственноручно повышали, и только до его предыдущего значения!»

Если какой-либо код вызвал вашу функцию на IRQL = APC_LEVEL, то вы не имеете права опустить его ниже данного уровня. Вы можете поднять IRQL до DISPATCH_LEVEL, потом опустить обратно до APC_LEVEL, но не ниже.

Таким образом, более приемлемым вариантом кода, будет:

А вспомогательные функции по типу SetIrql() из 2-го примера, в принципе не являются адекватными с точки зрения интерфейса, т.к. при проектировании отдельных методов в вашем драйвере, важно продумывать ограничения накладываемые на предусловия вызова вашей функции.

Для описания данных предусловий, удобно использовать аннотации SAL, их список вы можете посмотреть тут:

Также Microsoft предоставляет небольшой whitepaper(в самом низу статьи) по управлению приоритетами потоков в ядре, и более подробно рассказывает некоторые тонкости по работе с ними:

Если же, вам всё-таки нужно каким-либо образом вызвать какое-либо Api, требующее более низких значений IRQL, то одним из вариантов решения данной проблемы могут стать WorkItem’ы. Но о них, я расскажу уже в другой статье.

Читайте также: