Как загрузить kernel driver
Драйверы составляют значительный процент от общего кода, выполняемого в режиме ядра. Драйвер режима ядра, фактически, является компонентом операционной системы. Поэтому драйверы, которые являются надежными и безопасными, значительно влияют на общую надежность операционной системы. Чтобы создать надежный драйвер режима ядра, следуйте приведенным ниже рекомендациям.
Обеспечьте правильную защиту объектов устройств.
Доступ пользователей к драйверам и устройствам системы осуществляется с помощью дескрипторов безопасности, которые система назначает объектам устройств. Чаще всего система устанавливает параметры безопасности устройства при установке устройства. Дополнительные сведения см. в разделе Создание защищенных установок устройств. Иногда драйвер может играть часть в управлении доступом к своему устройству. Дополнительные сведения см. в разделе Защита объектов устройств.
Проверьте правильность объектов устройств.
Если драйвер создает несколько типов объектов устройств, он должен проверить, какой тип он получает в каждом IRP. Дополнительные сведения см. в разделе сбой при проверке объектов устройств.
Используйте функции "безопасного типа".
При управлении строками драйвер должен использовать функции с надежными строками, а не строковые функции, предоставляемые библиотеками среды выполнения языка C/C++. дополнительные сведения см. в разделе использование Сейф строковых функций.
Проверка дескрипторов объектов.
Драйверы, получающие дескрипторы объектов в качестве входных данных, должны проверять, являются ли дескрипторы допустимыми, доступны ли они и имеют ли они ожидаемый тип. Дополнительные сведения об использовании дескрипторов объектов см. в следующих разделах:
Поддержка многопроцессорности должным образом.
Никогда не следует считать, что драйвер будет работать только в однопроцессорных системах. Сведения о методах программирования, которые можно использовать, чтобы убедиться, что драйвер будет правильно функционировать в многопроцессорных системах, см. следующие разделы:
Правильно обработайте состояние драйвера.
Важно всегда проверять, находится ли драйвер в том состоянии, в котором он предполагается. Например, если драйвер получает IRP, то уже обслуживает IRP того же типа? Если драйвер не проверяет эту ситуацию, первый IRP может быть потерян. Дополнительные сведения см. в разделе сбой при проверке состояния драйвера.
Проверка входных значений IRP.
Это важно для проверки всех значений, связанных с IRP, таких как буферные адреса и длины, с точки зрения надежности и безопасности. Следующие разделы содержат сведения о проверке входных значений IRP:
Правильно обработайте стек ввода-вывода.
При передаче запросов IRP вниз по стеку драйверовважно, чтобы драйверы вызывали иоскипкуррентирпстакклокатион или иокопикуррентирпстакклокатионтонекст для настройки расположения стека ввода-вывода следующего драйвера. Не создавайте код, который непосредственно копирует одно расположение стека ввода-вывода в следующий.
Правильно обрабатывайте операции завершения IRP.
Драйвер не должен заполнять IRP со значением состояния STATUS_SUCCESS, если только он не поддерживает и не обрабатывает IRP. Сведения о правильных способах управления операциями завершения IRP см. в разделе Завершение IRP.
Правильно обрабатывайте операции отмены IRP.
Операции отмены могут быть сложными для правильного кода, так как они обычно выполняются асинхронно. Проблемы в коде, обрабатывающем операции отмены, могут пойти незамеченными в течение длительного времени, поскольку этот код обычно не выполняется часто в работающей системе.
Не забудьте прочитать и разобраться в информации, предоставляемой при отмене запросов IRP. Обратите особое внимание на синхронизацию отмены IRP и моменты, которые следует учитывать при отмене запросов IRP.
Одним из способов избежать проблем синхронизации, связанных с операциями отмены, является реализация очереди ненадежных запросов IRP. безнадежная очередь IRP — это управляемая драйвером очередь, которая появилась для Windows XP и более поздних версий операционных систем, но также обратно совместима с предыдущими версиями.
Правильно обрабатывайте операции очистки и закрытия IRP.
Убедитесь, что вы понимаете разницу между запросами IRP_MJ_CLEANUP и IRP_MJ_CLOSE . Запросы на очистку поступают после того, как приложение закрывает все дескрипторы объекта File, но иногда до завершения всех запросов ввода-вывода. Запросы на закрытие поступают после завершения всех запросов ввода-вывода для объекта File. Дополнительные сведения см. в следующих разделах:
Дополнительные сведения об правильной обработке запросов IRP см. в разделе Дополнительные ошибки при обработке запросов IRP.
Использование средства проверки драйверов
Средство проверки драйверов — это наиболее важный инструмент, который можно использовать для обеспечения надежности драйвера. Средство проверки драйверов может проверять различные распространенные проблемы с драйверами, в том числе некоторые из обсуждаемых в этом разделе. Однако использование средства проверки драйверов не заменяет осторожность, продуманное проектирование программного обеспечения.
Last Achievements
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.
Last Achievements
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
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?
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 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 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
the driver initalizes, but if i put fortnite exe as process it just tells me trough debugview that its looking for it but it wont actually find it..
tryed with mspaint.exe -> works
any idea why it doesnt work for fortnite?
Все мало-мальски серьезные защитные приложения, будь то файрволы или антивирусы, используют собственные модули режима ядра (ring 0), через которые работает большинство их функций: защита процессов от завершения, фильтры различных событий, получение актуальной информации о состоянии сетевого трафика и количестве процессов в системе. Если у программы есть такой драйвер, то пробовать скрываться от нее из режима пользователя (ring 3) бессмысленно. Так же бесполезно пытаться на нее как-то воздействовать. Решение — написать собственный драйвер. В этой статье я покажу, как это делается.
Процессорные архитектуры x86 и x64 имеют четыре кольца защиты, из которых в Windows по факту используются всего два — это ring 3 (режим пользователя) и ring 0 (режим ядра). Бытует мнение, что код режима ядра — самый привилегированный и «ниже» ничего нет. На самом деле архитектура x86/x64 позволяет опускаться еще ниже: это технология виртуализации (hypervisor mode), которая считается кольцом −1 (ring −1), и режим системного управления (System Management Mode, SMM), считающийся кольцом −2 (ring −2), которому доступна память режима ядра и гипервизора.
Итак, мы решили писать собственный драйвер. Начнем с выбора инструментария. Я советую использовать Microsoft Visual Studio, как наиболее user-friendly IDE. Также необходимо будет установить Windows SDK и Windows Driver Kit (WDK) для твоей версии ОС. Кроме того, я крайне рекомендую запастись такими утилитами, как DebugView (просмотр отладочного вывода), DriverView (позволяет получить список всех установленных драйверов) и KmdManager (удобный загрузчик драйверов).
Драйверы в Windows начиная с Vista могут быть как режима пользователя (User-Mode Driver Framework, UMDF), так и режима ядра (Kernel-Mode Driver Framework, KMDF). Более ранние драйверы Windows Driver Model (WDM) появились в Windows 98 и сейчас считаются устаревшими.
Драйверы UMDF имеют намного более ограниченные права, чем KMDF, однако они используются, например, для управления устройствами, подключенными по USB. Помимо ограничений, у них есть очевидные плюсы: их намного проще отлаживать, а ошибка в их написании не вызовет глобальный системный сбой и синий экран смерти. Такие драйверы имеют расширение dll.
Что до драйверов режима ядра (KMDF), то им дозволено куда больше, а расширение файлов, закрепленное за ними, — это sys. В этой статье мы научимся писать простые драйверы режима ядра, напишем драйвер для скрытия процессов методом DKOM (Direct Kernel Object Manipulation) и его загрузчик.
Создание драйвера KMDF
После того как ты создашь проект драйвера, Visual Studio автоматически настроит некоторые параметры. Проект будет компилироваться в бинарный файл в соответствии с тем, какая выбрана подсистема. Наш вариант — это NATIVE, подсистема низкого уровня, как раз для того, чтобы писать драйверы.
Точка входа в драйвер
Строго говоря, точка входа в драйвер может быть любой — мы можем сами ее определить, добавив к параметрам компоновки проекта -entry:[DriverEntry] , где [DriverEntry] — название функции, которую мы хотим сделать стартовой. Если в обычных приложениях основная функция обычно называется main, то в драйверах точку входа принято называть DriverEntry.
Выглядеть это будет так:
Давай пройдемся по параметрам, которые передаются DriverEntry . pDriverObject имеет тип PDRIVER_OBJECT , это значит, что это указатель на структуру DRIVER_OBJECT , которая содержит информацию о нашем драйвере. Мы можем менять некоторые поля этой структуры, тем самым меняя свойства драйвера. Второй параметр имеет тип PUNICODE_STRING , который означает указатель на строку типа UNICODE . Она, в свою очередь, указывает, где в системном реестре хранится информация о нашем драйвере.
WARNING
Любая ошибка в драйвере может вызвать общесистемный сбой и BSOD. Вероятна потеря данных и повреждение системы. Все эксперименты я рекомендую проводить в виртуальной машине.
Interrupt Request Level (IRQL)
IRQL — это своеобразный «приоритет» для драйверов. Чем выше IRQL, тем меньшее число других драйверов будут прерывать выполнение нашего кода. Существует несколько уровней IRQL: Passive, APC, Dispatch и DIRQL. Если открыть документацию MSDN по функциям WinAPI, то можно увидеть примечания, которые регламентируют уровень IRQL, который требуется для обращения к каждой функции. Чем выше этот уровень, тем меньше WinAPI нам доступно для использования. Первые три уровня IRQL используются для синхронизации программных частей ОС, уровень DIRQL считается аппаратным и самым высоким по сравнению с программными уровнями.
Пакеты запроса ввода-вывода (Input/Output Request Packet)
IRP — это запросы, которые поступают к драйверу. Именно при помощи IRP один драйвер может «попросить» сделать что-то другой драйвер либо получить запрос от программы, которая им управляет. IRP используются диспетчером ввода-вывода ОС. Чтобы научить программу воспринимать наши IRP, мы должны зарегистрировать функцию обратного вызова и настроить на нее массив указателей на функции. Код весьма прост:
А вот код функции-заглушки, которая всегда возвращает статусный код STATUS_SUCCESS . В этой функции мы обрабатываем запрос IRP.
Теперь любой запрос к нашему драйверу вызовет функцию-заглушку, которая всегда возвращает STATUS_SUCCESS . Но что, если нам нужно попросить драйвер сделать что-то конкретное, например вызвать определенную функцию? Для этого регистрируем управляющую процедуру:
Здесь мы объявили процедуру с именем IRP_MY_FUNC и ее кодом — 0x801 . Чтобы драйвер ее обработал, мы должны настроить на нее ссылку, создав таким образом дополнительную точку входа в драйвер:
После этого нам нужно получить указатель на стек IRP, который мы будем обрабатывать. Это делается при помощи функции IoGetCurrentIrpStackLocation , на вход которой подается указатель на пакет. Кроме этого, необходимо будет получить от диспетчера ввода-вывода размеры буферов ввода-вывода, чтобы иметь возможность передавать и получать данные от пользовательского приложения. Шаблонный код каркаса обработчика управляющей процедуры:
Продолжение доступно только участникам
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Читайте также: