Kernel-level hook for keyboard filtering (2)

Source: Internet
Author: User
Kernel-level hook for keyboard filtering (2)

If you do not want the keyboard filter driver or callback function to obtain the buttons first, it must be more underlying than the port driver.
In earlier versions, the QQ anti-account theft drive works like this: when a user wants to enter a password (for example, move the input focus to the password box), the user registers an interrupt service to take over the keyboard interruption, for example, if 0x93 is interrupted, then the key is not the key driver.
First, we will introduce hook keyboard interruptions.
1. Interrupt: IRQ and int
People who have learned the computer architecture know that hardware is often interrupted to notify the CPU of an event. For example, press the key. However, interruption does not have to be triggered by any hardware notifications. A single command can interrupt the CPU ". For example, write in a. c file:
_ Asm int 3
Such code is often used to manually set a breakpoint where the program is interrupted when executed. Int n (n is the interrupt number) can trigger software interruption (software interruption is also called an exception). The trigger essence is: the execution pause of the CPU and jump to the interrupt processing function, the interrupt handler function is saved in memory beforehand. At the same time, the first addresses of these functions are stored in a table called IDT (Interrupt Descriptor Table). Each interrupt number has an item in this table.
Once an int N is executed, the CPU goes to the IDT to find the nth item. There is an Interrupt Descriptor in which you can read the first address of a function, and then the CPU jumps to this first address for execution. Of course, after proper processing, the system generally returns to continue the execution of the preceding program. This is the process of interruption.
Real interruptions are generally called IRQ. What hardware does an IRQ come from? This is largely a rule. For example, irq1 must be a PS/2 keyboard, and only a few IRQ are left for your use. An IRQ generally requires an interrupt function, but IRQ does not have as many as 24 interrupt numbers. IRQ processing is also handled by the interrupt processing function, which requires a correspondence between the IRQ Number and the interrupt number. When such an IRQ occurs, the CPU knows where to jump.
After ioaipc appears, the corresponding relationship can be modified. In Windows, the PS/2 Keyboard buttons or release keys are generally int0x93, it is precisely because this relationship (irq1-> int 0x93) is set.
In this way, we have a simple solution to protect keyboard interruptions: Modify the address saved by INT 0x93 in IDT. If we change it to a function we write, the interruption must be intercepted first, and Other filtering layers will be removed.

2. How to modify IDT
Modifying IDT in an application is impossible due to permission issues, but it is completely feasible to do it in the kernel program. The memory address of the IDT is not fixed, but it can be obtained through a command sidt. The following code obtains the address of the Interrupt Descriptor Table.
Note that each core has its own idt on a multi-core CPU. Therefore, you should pay attention to getting IDT for each core. That is to say, the following code must be executed on every core.
// Since we must specify the number of bits in a domain, we define several
// Determine the number of characters in length to avoid compilation troubles in different environments.
Typedef unsigned char p2c_u8;
Typedef unsigned short p2c_2010;
Typedef unsigned long p2c_u32;

# Define p2c_makelong (low, high )\
(P2c_u32) (p2c_2010) (p2c_u32) (low) & 0 xFFFF) | (p2c_u32) (p2c_2010) (p2c_u32) (high) & 0 xFFFF) <16 ))

# Define p2c_low16_of_32 (data )\
(P2c_2010) (p2c_u32) data) & 0 xFFFF ))

# Define p2c_high16_of_32 (data )\
(P2c_2010) (p2c_u32) data)> 16 ))

// Obtain the following structure from the sidt command. Here we can get the start address of IDT.
# Pragma pack (push, 1)
Typedef struct p2c_idtr _{
P2c_16limit; // range
P2c_u32 base; // The base address (that is, the start address)
} P2c_idtr, * pp2c_idtr;
# Pragma pack (POP)

// The following function reads a p2c_idtr structure using the sidt command and returns the IDT address.
Void * p2cgetidt ()
{
P2c_idtr idtr;
// The location where the Assembly reads the IDT.
_ ASM sidt idtr
Return (void *) idtr. base;
}
After obtaining the IDT address, the memory space is an array. Each element has the following structure:
# Pragma pack (push, 1)
Typedef struct p2c_idt_entry _{
P2c_2010offset_low;
P2c_2010selector;
P2c_u8 reserved;
P2c_u8 type: 4;
P2c_u8 always0: 1;
P2c_u8 DPL: 2;
P2c_u8 present: 1;
P2c_2010offset_high;
} P2c_idtentry, * pp2c_idtentry;
# Pragma pack (POP)
Some people may not get used to writing a structure with a single colon after such a member variable. Fields with colons are called bit fields. This is a field where the member width is even smaller than one byte and only 1 ~ 7 digits. The number after the colon indicates the number of digits. For example, type has four digits and always has one digit.
The redirection address of the interrupted service is actually a 32-bit virtual address. However, this address is stored separately. The 16-bit high is saved in offset_high, and the 16-bit low is saved in offset_low.
There is no interrupt number here. It should be the interrupt number, which is the index in this table. Therefore, the structure of item 0x93 is a concern of readers.

3. Replace the jump address in IDT
You can write a function to replace the interrupt service address, but note the writing of this function. The interruption does not occur when a call is redirected. Therefore, you cannot use ret to return. Generally, the iret command should be used to return the interrupt. However, to avoid more problems, we should jump to the original interrupt processing function entry after processing, so it is better to replace it with our return. In this case, we need a pure assembly code that does not include the function framework generated by the C compiler. Readers can write Data Directly Using ASM assembly, but I have used C language Embedded Assembly here. Note that you can use _ declspec (naked) to generate a bare function. The following function is an example:
Void * g_p2c_old = NULL;

_ Declspec (naked) p2cinterruptproc ()
{
_ ASM
{
Pushad // save all General registers
Pushfd // Save the flag register
Call p2cuserfilter // call a function of our own. This function will implement
// Some of our own functions
Popfd // recovery flag register
Popad // restores General registers
JMP g_p2c_old // jump to the original interrupted service program
}
}
There is nothing in the bare function, so local variables cannot be used, and only embedded assembly can be used for implementation. However, most readers are still accustomed to using C language, so we can use assembly to call a C function. C functions may change the register content, which may be unexpected by the true interrupt processing function. Therefore, the registers are saved and restored before and after the call.
The following code directly replaces 0x93 in IDT for service interruption, including obtaining and replacing IDT addresses. Note that the code can only run on a single-core, 32-bit operating system. If multiple cores exist, sidt can only obtain the current CPU core IDT. Note: this function can be replaced or restored.
// This function modifies item x93 In the IDT table to p2cinterruptproc.
// Save the modification to g_p2c_old.
Void p2chookint93 (Boolean hook_or_unhook)
{
Pp2c_idtentry idt_addr = (pp2c_idtentry) p2cgetidt ();
Idt_addr + = 0x93;
Kdprint ("P2c: the current address = % x. \ r \ n ",
(Void *) p2c_makelong (idt_addr-> offset_low, idt_addr-> offset_high )));
If (hook_or_unhook)
{
Kdprint ("P2c: try to hook interrupt. \ r \ n "));
// If g_p2c_old is null, hook
G_p2c_old = (void *) p2c_makelong (idt_addr-> offset_low, idt_addr-> offset_high );
Idt_addr-> offset_low = p2c_low16_of_32 (p2cinterruptproc );
Idt_addr-> offset_high = p2c_high16_of_32 (p2cinterruptproc );
}
Else
{
Kdprint ("P2c: Try to recovery interrupt. \ r \ n "));
// If g_p2c_old is not null, cancel the hook.
Idt_addr-> offset_low = p2c_low16_of_32 (g_p2c_old );
Idt_addr-> offset_high = p2c_high16_of_32 (g_p2c_old );
}
Kdprint ("P2c: the current address = % x. \ r \ n ",
(Void *) p2c_makelong (idt_addr-> offset_low, idt_addr-> offset_high )));
}
Use ioapic to relocate interrupt handling functions
1. What is ioapic?
Ioapic is a new interrupt controller that can be used for multiple core CPUs, so or should be understood as a new programmable hardware.
Ioapic is used to determine the CPU core to which IRQ is sent and the form in which it is sent when an IRQ occurs. Ioapic can be programmed. Therefore, you can program or send the hardware interrupt request (irq1) of the PS/2 keyboard to a CPU core, let an interrupt number in the core IDT correspond to the Interrupt Processing Service.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.