Parents know best what their child can do and what not. Similarly, the programmer of a particular program knows better than others what actions the program should perform and which not. But not everything is as good as we would like. Malicious programs often interfere with the work of other programs. The consequences of exploiting vulnerabilities in network programs are not at all predictable. All this somehow violates information security not only in enterprises, but also on computers of ordinary users.
And how it would be nice if the program itself knew what actions it doesn’t need and turned off them just in case.
')
Antiviruses and other similar systems try to take on the role of defenders, but all protection comes down to a set of rules that are the same for many and do not take into account the specifics of the programs being protected. This approach sometimes leads to unpleasant situations. Here are some examples:
- Embedded malicious code in the browser - gets unhindered access to network functions.
- Embedded malicious code in the mail program - will freely get access to the mail database files, despite the fact that the antivirus will protect these files.
- Exploiting vulnerabilities in network programs - sometimes allows access to the system or elevation of rights, despite the fact that the user may have minimal rights.
But as mentioned above - the author of the program knows best what his program should do. So why not give him the opportunity to put restrictions on certain actions? And not for the whole program, but for specific streams.
Efficiency of the idea
Thanks to this, you can achieve:
- The processing and visualization flows could not work with the network.
- Network workflows could not launch another program or be implemented in another process.
- Malicious code embedded in the program, could not use its level of trust among antiviruses.
Speaking from a more realistic point, one could:
- The browser itself could prohibit the launch of third-party programs.
- The mail program could have access to the database files, but the code embedded in it could not.
- Any program could learn about its abnormal activity.
Features of the implementation and use of ideas
To implement the idea you need in the Windows kernel:
- Make edits in two system structures.
- Add one system call.
- Write code for 50-100 lines.
In kernel32.dll add one function to control the protection. Which also will not be very big.
These actions under the force of any programmer (and in one day). But the biggest difficulty is that Microsoft itself has to do it. That is why this protection mechanism remains in dreams.
Theoretical retreat
Since any restrictions in user mode (user mode) are easily bypassed or removed, it is necessary to implement all the protection in the kernel.
The basic principle of calling system functions in Windows can be represented as the following diagram:
After calling sysenter / syscall / int 0x2E or call dword ptr fs: [0xC0] (depending on the windows version), further control will go to the kernel, where the KiSystemService function looks at the table of system service addresses (SDT) and by the service number (transmitted in the eax register) ) finds the address of the desired function and then transfers control to it. In this way, the function is called.
The system uses two tables - one is responsible directly for the system and is processed by the kernel, and the second is responsible for the work of the GUI and is processed by the win32.sys driver. We are interested in the first table.
Thanks to this architecture, it is possible to put access restrictions on the call of any system function. To do this, simply insert a simple check into the KiSystemService function.
We have already found the place of verification, now it is necessary to find
a place for storing the access matrix.
In Windows operating systems, the kernel uses the KTHREAD structure to describe the flow, which is different for each thread. This is where the access matrix can be stored. But now it will not be a matrix, but a line list (since each stream will store information only about itself).
Everything is good, but there is also a network, work with which is organized through special drivers. But even here, the problem is solved in processing some IRP requests to the devices \\ Device \ Tcp, \\ Device \ Udp, \\ Device \ Raw, which will also allow you to transparently check the ability to access the network from certain streams.
How we will implement protection
The implementation of protection is seen as follows:
- In the structure of the description of each stream there is a prohibition table consisting of bits. In Windows 7, there are 360 ​​functions in the SDT, so an array of 512 bits (64 bytes) is enough above the roof.
- By default, all bits are cleared (i.e. there is no prohibition)
- There is also a 64 bit key field, which will be discussed later.
- There is also a 32/64 bit field that stores the address of the callback function.
- Total we have data on 76/80 bytes
- Another system call has been added to the kernel, which fills the structure according to the following rules:
- Bits can always be set.
- You can reset the bits only by specifying the security key.
- Special key can be obtained only once.
- Callback function can be set only once or unlimited number using the key.
- In kernel32.dll and ntdll.dll, an interface function is added to the system call organizing protection management.
Protection work progress:
- In KiSystemService or the network driver request IRP handler, we get a pointer to the PTHREAD structure
- Check the index boundaries (in the case of a number in the SDT)
- Check the bit is set or not. If set then return STATUS_ACCESS_DENIED
- If the bit is set and the callback function is specified, then call it. Thereby notifying the process that a thread has attempted to perform prohibited actions.
I would like to pay special attention to the protection installation function. She sees the prototype as:
DWORD QuerySystemSecurity (HANDLE hThread, DWORD Flag, QWORD * Key, VOID * Data);
- hThread is a thread descriptor for which protection is being installed. Or 0xFFFFFFFF in the case of the current thread.
- Flag - parameters transmitted or requested:
- Get the key.
- Set a ban on the function.
- Set a ban on the list of functions.
- Set a ban on a group of functions. - for example, working with files or processes.
- Set callback function.
- Remove the ban on the function.
- Inherit the protection table or not.
- Forbid to remove protection even if the key is set.
- Deny getting the key.
- Key - key address or NULL.
- Data - data depending on the Flag.
Also in QuerySystemSecurity there should be a table of correspondence to the number of the function to be prohibited and its number from the SDT. the latter changes from version to version.
Conclusion
Thanks to one simple function and a small modification of the kernel, you can get quite flexible and fast protection. The programmer himself will be able to forbid abnormal actions to his programs. That would be very useful in network programs and primarily in browsers.
Protection based on this approach would be able to more flexibly control the execution of third-party code in programs (not only malicious, but also plug-ins from other authors). If small developers would not be interested in such protection, then large developers could easily use it to enhance the protection of their programs against the interference of someone else's code.
Of course, such protection can be implemented not only by Microsoft itself, but also by third-party developers, but this will already be a crutch and will not work as effectively (due to the fact that you have to refuse to store data in PTHREAD) well, and will not have such a global scale.
But as always, these are only personal dreams about the existence of protection, which are unlikely to be realized by Microsoft.